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

Dream.serve exit whole program when sending long string to closed connection #378

Open
panglesd opened this issue Jan 10, 2025 · 1 comment

Comments

@panglesd
Copy link

Hello, and thanks for your work on this great package!

I have a strange (and critical!) issue. When using Dream.serve, if I send a long string to a closed connection, the program simply exits, without any error.

Here is a minimal reproduction:

  1. Create a server with Dream.serve, which waits 3 seconds before answering a long string of as:
open Lwt.Syntax

let () =
  let f =
    let+ () =
      Dream.serve
      @@ Dream.logger (fun _ ->
             Logs.app (fun m -> m "Starting waiting");
             let* () = Lwt_unix.sleep 3.0 in
             Logs.app (fun m -> m "Finished waiting");
             let x = String.init 1015765 (fun _ -> 'a') in
             Dream.respond x)
    in
    print_endline "Finished"
  in
  Lwt_main.run f
  1. Start the program. Connect to localhost:8080 with a web browser, but close the tab before the 3 seconds elapses

  2. Have a look at the log, and cry 😢:

$ dune exec ./middleware.exe
10.01.25 10:36:56.511    dream.logger  INFO REQ 1 GET / ::1:48826 fd 6 Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/127.0
10.01.25 10:36:56.511                       REQ 1 Starting waiting
10.01.25 10:36:59.515                       REQ 1 Finished waiting
10.01.25 10:36:59.528    dream.logger  INFO REQ 1 200 in 3017101 μs
$ # back to prompt. "Finished" was not even written to output... Error result is 0.

Some more comments:

  • Any length of answer above 1015765 result in this behavior,

  • Any length below works as expected

  • The same program but with Dream.run works!

    open Lwt.Syntax
    
    let () =
      Dream.run
      @@ Dream.logger (fun _ ->
             Logs.app (fun m -> m "Starting waiting");
             let* () = Lwt_unix.sleep 3.0 in
             Logs.app (fun m -> m "Finished waiting");
             let x = String.init 1015765 (* 1061514 *) (fun _ -> 'a') in
             Dream.respond x)

I am puzzled...

@panglesd
Copy link
Author

panglesd commented Jan 10, 2025

Apparently, that is because of an unhandled "broken pipe" signal. I investigated the difference between Dream.serve and Dream.run, and it seems this line (taken from here) solves the problem:

let () = if Sys.unix then Sys.(set_signal sigpipe Signal_ignore) in

It makes sense, but probably should be added to Dream.serve. Or, have a mention in the doc.

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

No branches or pull requests

1 participant