diff --git a/example/README.md b/example/README.md index 6853e51..2369d11 100644 --- a/example/README.md +++ b/example/README.md @@ -77,8 +77,8 @@ if something is missing! - [**`w-query`**](w-query#files)  —  reading URL query parameters. - [**`w-server-sent-events`**](w-server-sent-events#files)  —  - the server side of JavaScript - [`EventSource`](https://developer.mozilla.org/en-US/docs/Web/API/EventSource). + [`EventSource`](https://developer.mozilla.org/en-US/docs/Web/API/EventSource), + an older alternative to WebSockets. - [**`w-site-prefix`**](w-site-prefix#files)  —  a Web app running not at `/`. - [**`w-template-stream`**](w-template-stream#files)  —  writing diff --git a/example/w-server-sent-events/README.md b/example/w-server-sent-events/README.md index 546e64f..d9dab5e 100644 --- a/example/w-server-sent-events/README.md +++ b/example/w-server-sent-events/README.md @@ -2,11 +2,55 @@
-**Next steps:** +In [server-sent +events](https://developer.mozilla.org/en-US/docs/Web/API/EventSource), a client +sends a request to a server, to which the server responds with header +`Content-Type: text/event-stream`, and gradually streams events. + +This example sets up a message-generating loop, `message_loop`, to simulate +messages sent by other clients: + +```ocaml +let rec message_loop () = + let%lwt () = Lwt_unix.sleep (Random.float 2.) in + + incr last_message; + let message = string_of_int !last_message in + Dream.log "Generated message %s" message; + + server_state := message::!server_state; + !notify (); + + message_loop () +```
-[Up to the example index](../#readme) +When a client connects to the example's server-sent events endpoint at +[http://localhost:8080/push](http://localhost:8080/push), the server first sends +any messages that have already accumulated, and then gradually +[streams](https://aantron.github.io/dream/#streaming) more messages as they are +created. + +You can see this in action either by visiting the endpoint directly, or as +interpreted by the page at [http://localhost:8080](http://localhost:8080), which +uses the browser +[`EventSource`](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) +interface to server-sent events. + +
+ +**See also:** + +- [**`k-websocket`**](../k-websocket#files) for WebSockets, which largely + supersede server-sent events. +- [**`w-template-stream`**](../w-template-stream#files) for another example of + “real-time” streaming with + [`Dream.flush`](https://aantron.github.io/dream/#val-flush). + +
+ +[Up to the example index](../#examples) diff --git a/example/w-server-sent-events/server_sent_events.eml.ml b/example/w-server-sent-events/server_sent_events.eml.ml index 9230f4e..91c5e8b 100644 --- a/example/w-server-sent-events/server_sent_events.eml.ml +++ b/example/w-server-sent-events/server_sent_events.eml.ml @@ -28,13 +28,40 @@ let last_message = let rec message_loop () = let%lwt () = Lwt_unix.sleep (Random.float 2.) in + incr last_message; let message = string_of_int !last_message in Dream.log "Generated message %s" message; + server_state := message::!server_state; !notify (); + message_loop () +let rec forward_messages response = + let%lwt messages = + match !server_state with + | [] -> + let on_message, notify_message = Lwt.wait () in + notify := Lwt.wakeup_later notify_message; + let%lwt () = on_message in + notify := ignore; + Lwt.return !server_state + | messages -> + Lwt.return messages + in + + server_state := []; + + messages + |> List.rev + |> List.map (Printf.sprintf "data: %s\n\n") + |> String.concat "" + |> fun text -> + let%lwt () = Dream.write text response in + let%lwt () = Dream.flush response in + forward_messages response + let () = Lwt.async message_loop; @@ -45,28 +72,6 @@ let () = Dream.get "/" (fun _ -> Dream.respond home); Dream.get "/push" (fun _ -> - let rec forward_messages response = - let%lwt messages = - match !server_state with - | [] -> - let on_message, notify_message = Lwt.wait () in - notify := Lwt.wakeup_later notify_message; - let%lwt () = on_message in - notify := ignore; - Lwt.return !server_state - | messages -> - Lwt.return messages - in - server_state := []; - messages - |> List.rev - |> List.map (Printf.sprintf "data: %s\n\n") - |> String.concat "" - |> fun text -> - let%lwt () = Dream.write text response in - let%lwt () = Dream.flush response in - forward_messages response - in Dream.stream ~headers:["Content-Type", "text/event-stream"] forward_messages);