To manage several aspects of a SSE connection
(see the SSE design notes) the SDK provides a
write profile
mechanism. It lets you control:
An example may be the quickest way to get started.
Let's say we want to have a ring SSE handler using gzip compression with a temporary write buffer strategy. We can create a write profile to do this.
(require
'[starfederation.datastar.clojure.adapter.common :as ac])
(def my-write-profile
;; We specify a function that will wrap the output stream
;; used for the SSE connection
{ac/wrap-output-stream (fn [os] (-> os ac/->gzip-os ac/->os-writer))
;; We specify which writing function to use on the output stream
;; Since we just use an OutputStreamWriter in the wrap function above
;; we go for the temp buffer writing function helper
ac/write! (ac/->write-with-temp-buffer!)
;; We also provide a content encoding header for the HTTP response
;; this way it is automatically added
ac/content-encoding ac/gzip-content-encoding})
When using the ->sse-response
function we can do:
(require
'[starfederation.datastar.clojure.api :as d*]
'[starfederation.datastar.clojure.adapter.ring :refer [->sse-response on-open]])
(defn handler [req]
(->sse-response req
{ac/write-profile my-write-profile ;; note the use of the write profile here
on-open
(fn [sse]
(d*/with-open-sse sse
(d*/patch-elements! sse "some big element")))}))
This response will have the right Content-Encoding
header and will compress
SSE event with gzip.
If we want to control the buffer sizes used by our output stream we can write another profile:
(def my-specific-write-profile
{ac/wrap-output-stream
(fn [os] (-> os
(ac/->gzip-os 1024) ;; setting the gzip os buffer size
ac/->os-writer))
ac/write! (ac/->write-with-temp-buffer! 16384);; initial size of the StringBuilder
ac/content-encoding ac/gzip-content-encoding})
This also allows for using other compression algorithms as long as they work
like java's java.util.zip.GZIPOutputStream
. We could also implement a buffer
pooling of some kind by providing a custom ac/write!
function.
The SDK tries to provide sensible defaults. There are several write profiles provided:
profile | compression | buffering strategy | write! helper |
---|---|---|---|
basic-profile | no | temporary StringBuilder | ->write-with-temp-buffer! |
buffered-writer-profile | no | permanent BufferedWriter | write-to-buffered-writer! |
gzip-profile | gzip | temporary StringBuilder | ->write-with-temp-buffer! |
gzip-buffered-writer-profile | gzip | permanent BufferedWriter | write-to-buffered-writer! |
If you don't specify a profile in the ->sse-response
function call, the basic
profile is used by default.
With ->write-with-temp-buffer!
, the underlying StringBuilder
default size
is modeled after java's BufferedWriter
, that is 8192 bytes.
The rest of the buffer sizes are java's defaults.
Note that we have specific helper used for the ac/write!
value, depending on
the buffering strategy.
Each adapter specific namespace aliases the write profile option key and the profiles provided by the SDK.
Http-kit doesn't use an OutputStream
as its IO primitive. This means that as
soon as you want to wrap an OutputStream
the Http-kit adapter will create and
hold onto a ByteArrayOutputStream
for your ac/wrap-output-stream
function
to wrap. It will take the bytes from this OutputStream
to send.
This has an impact on allocated memory and so this behavior is used for the buffered-writer-profile, the gzip-profile and the gzip-buffered-writer-profile.
The basic-profile just concatenates SEE events text in a String builder and sends
the text. It doesn't allocate any ByteArrayOutputStream
.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
Ctrl+k | Jump to recent docs |
← | Move to previous article |
→ | Move to next article |
Ctrl+/ | Jump to the search field |