Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not shutdown the connection for HTTP/1.1 persistent connections #226

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

edwintorok
Copy link

I was trying to use a persistent HTTP/1.1 connection, but I kept getting "unexpected eof" errors, and strace was showing that shutdown got called on the client socket. I haven't found a way to make this work without patching httpaf, but perhaps I missed something.

The following program is then able to use persistent connections when talking to nginx on localhost:8000:

open Lwt.Syntax
open Httpaf
open Httpaf_lwt_unix

let repeat = 100000

let config =
  let open Httpaf.Config in
  {default with response_body_buffer_size= 16384}

let rec perform n =
  let socket = Lwt_unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
  let* () =
    Lwt_unix.connect socket (Unix.ADDR_INET (Unix.inet_addr_loopback, 8000))
  in
  let headers = Httpaf.Headers.of_list [("host", "127.0.0.1")] in
  let req = Request.create ~headers `GET "/" in
  let rec loop n =
    let finished, notify_finished = Lwt.wait () in
    let error_handler error =
      Printexc.print_backtrace stderr ;
      let exn =
        match error with
        | `Malformed_response err ->
            Failure ("Malformed response: " ^ err)
        | `Invalid_response_body_length r ->
            Failure
              (Format.asprintf "Invalid response length: %a" Response.pp_hum r)
        | `Exn exn ->
            exn
      in
      Printexc.to_string exn |> prerr_endline ;
      raise exn
    in
    let keepalive = ref true in
    let response_handler response body =
      keepalive := Response.persistent_connection response ;
      (* Format.eprintf "Keepalive: %b, Response: %a@." (!keepalive) Response.pp_hum response ; *)
      let on_eof () =
        Body.Reader.close body ;
        Lwt.wakeup notify_finished ()
      in
      let rec on_read bs ~off ~len =
        Body.Reader.schedule_read body ~on_read ~on_eof
      in
      Body.Reader.schedule_read body ~on_read ~on_eof
    in
    let request_body =
      Client.request ~config ~error_handler ~response_handler socket req
    in
    Body.Writer.flush request_body ignore ;
    let* () = finished in
    if n < repeat then if !keepalive then loop (n + 1) else perform n
    else
      let* () = Lwt_unix.close socket in
      Lwt.return n
  in
  loop n

let main =
  let+ lst = List.init 6 (fun _ -> 0) |> Lwt_list.map_p perform in
  List.fold_left ( + ) 0 lst

let () =
  let t0 = Unix.gettimeofday () in
  let n = Lwt_main.run main in
  let t1 = Unix.gettimeofday () in
  Printf.printf "%d req: %g req/s\n" n (float n /. (t1 -. t0))

@edwintorok
Copy link
Author

Note: the client still needs to be careful not to accidentally close the request body writer, because that'd prevent further requests from being sent (whereas closing the reader is fine), perhaps similar treatment is needed for the writer?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant