From d50574e6a2bad090b3cc3277e7f05b98964e9693 Mon Sep 17 00:00:00 2001 From: Jim Crossley Date: Tue, 10 Feb 2015 10:54:48 -0500 Subject: [PATCH] Minor doc tweaks --- docs/guides/web.md | 50 +++++++++++++++++++----------------- web/src/immutant/web/sse.clj | 8 +++--- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/docs/guides/web.md b/docs/guides/web.md index 38e1b510..8a3bf251 100644 --- a/docs/guides/web.md +++ b/docs/guides/web.md @@ -40,9 +40,8 @@ middleware: aggregates some middleware handy during development. The [[immutant.web.async]] namespace enables the creation of -[WebSockets], [HTTP streams], and [Server-Sent Events] -streams. Features specific to Server-Sent Events are provided in the -[[immutant.web.sse]] namespace. +[WebSockets] and [HTTP streams]. And support for [Server-Sent Events] +is provided by [[immutant.web.sse]]. The [[immutant.web.undertow]] namespace exposes tuning options for Undertow, the ability to open additional listeners, and flexible SSL @@ -273,11 +272,11 @@ them within a single threaded call. ## Asynchrony -[HTTP streams], [WebSockets], and [Server-Sent Events] streams are -created with the [[immutant.web.async/as-channel]] function, which -should be called from your Ring handler, as it takes a request map and -some callbacks and returns a valid response map. Its polymorphic -design enables graceful degradation from bidirectional WebSockets to +[WebSockets], [HTTP streams], and [Server-Sent Events] are all enabled +by the [[immutant.web.async/as-channel]] function, which should be +called from your Ring handler, as it takes a request map and some +callbacks and returns a valid response map. Its polymorphic design +enables graceful degradation from bidirectional WebSockets to unidirectional chunked responses, e.g. streams. In either case, data is sent from the server using [[immutant.web.async/send!]]. @@ -335,10 +334,10 @@ supported, `:on-message`, for bidirectional communication. {:on-message (fn [ch msg] (async/send! ch (.toUpperCase msg)))}) -(defn my-ws [request] +(defn app [request] (async/as-channel request callbacks)) -(run my-ws) +(run app) ``` You can identify a WebSocket upgrade request by the presence of @@ -349,7 +348,7 @@ as well as WebSockets. ```clojure (defn app [request] (if (:websocket? request) - (my-ws request) + (async/as-channel request callbacks) (-> request (get-in [:params "msg"]) .toUpperCase @@ -367,24 +366,28 @@ encapsulates the check for the upgrade request: (wrap-websocket callbacks))) ``` -Using `wrap-websocket` means giving up the request closure in your -Ring handler, but the upgrade request is always available to your -callbacks via [[immutant.web.async/originating-request]]. +But using `wrap-websocket` means losing the `request` closure in your +Ring handler, representing the original WebSocket upgrade request from +the client. You can still access it, however, with +[[immutant.web.async/originating-request]]. Note the `:path` argument to [[immutant.web/run]] applies to both the Ring handler and the WebSocket, distinguished only by the request -protocol, e.g. `http://host.com/foo` vs `ws://host.com/foo`. +protocol. Given a `:path` of "/foo", for example, you'd have both +`http://your.host.com/foo` and `ws://your.host.com/foo`. -### Server-Sent Events +### Server-Sent Events (SSE) [Server-Sent Events] are a stream of specially-formatted chunked responses with a `Content-Type` header of `text/event-stream`. The [[immutant.web.sse]] namespace provides its own `send!` and `as-channel` functions that are composed from their -[[immutant.web.async]] counterparts. *Events* are represented as -either strings (for *data* fields), collections (for multi-line *data* -fields), or maps, which are expected to contain at least one of the -following keys: `:event`, `:data`, `:id`, and `:retry`. +[[immutant.web.async]] counterparts. *Events* are polymorphic: any +`Object` other than a `Collection` or `Map` is considered a simple +data field that will be string-ified, prefixed with "data:", and +suffixed with "\n". A `Collection` represents a multi-line data field. +And a `Map` is expected to contain at least one of the following keys: +`:event`, `:data`, `:id`, and `:retry`. Let's modify the HTTP streaming example to use SSE: @@ -394,10 +397,10 @@ Let's modify the HTTP streaming example to use SSE: (defn app [request] (sse/as-channel request {:on-open (fn [stream] - (dotimes [msg 10] - (sse/send! stream msg) + (dotimes [e 10] + (sse/send! stream e) (Thread/sleep 1000)) - (sse/send! stream {:event "close"}))})) + (sse/send! stream {:event "close", :data "bye!"}))})) (run app) ``` @@ -417,6 +420,7 @@ data: 8 data: 9 event: close +data: bye! ``` diff --git a/web/src/immutant/web/sse.clj b/web/src/immutant/web/sse.clj index ed9a8f78..7f65ec1d 100644 --- a/web/src/immutant/web/sse.clj +++ b/web/src/immutant/web/sse.clj @@ -44,10 +44,10 @@ `event` can be one of: - * a map with one or more of the following keys: :event, :data, :id, and :retry, - where the :data entry can be an object or collection - * an object that will be treated as data (sent as `(str \"data:\" the-object)`) - * a collecton of either of the above, which will be sent as events in order" + * a Map, with one or more of the following keys: :event, :data, :id, and :retry, + where the :data entry can be an Object or Collection + * an Object, treated as a simple data field (sent as `(str \"data:\" the-object \"\\n\")`) + * a Collecton, treated as a multi-line data field" ([ch event] (send! ch event nil)) ([ch event options]