Skip to content

Commit ddc199d

Browse files
rosejnptaoussanis
authored andcommitted
[#398] Enable binary support for custom un/packers (@rosejn, @ptaoussanis)
This commit changes an implementation detail of the packed message format [1] to enable non-string IPacker implementations. See new `*write-legacy-pack-format?*` var for more info. [1] Stop automatic "+"/"-" prefixing of packed output. This was originally done as a micro-optimization to keep packed output as short as possible for string packaged message formats. The optimization really doesn't make much difference, so isn't worth the loss in flexibility.
1 parent 4fee910 commit ddc199d

File tree

2 files changed

+96
-28
lines changed

2 files changed

+96
-28
lines changed

src/taoensso/sente.cljc

+91-27
Original file line numberDiff line numberDiff line change
@@ -201,41 +201,98 @@
201201
;;;; Packing
202202
;; * Client<->server payloads are arbitrary Clojure vals (cb replies or events).
203203
;; * Payloads are packed for client<->server transit.
204-
;; * Packing includes ->str encoding, and may incl. wrapping to carry cb info.
205-
206-
(defn- unpack "prefixed-pstr->[clj ?cb-uuid]"
207-
[packer prefixed-pstr]
208-
(have? string? prefixed-pstr)
209-
(let [wrapped? (enc/str-starts-with? prefixed-pstr "+")
210-
pstr (subs prefixed-pstr 1)
211-
clj
204+
205+
(defn- parse-packed
206+
"Returns [<packed> <?format>]. Used to support some minimal backwards
207+
compatibility between v2 `pack` and v1 `unpack` formats."
208+
;; TODO Remove this in a future ~breaking release
209+
[packed]
210+
(if (string? packed)
211+
(cond
212+
(enc/str-starts-with? packed "+") [(subs packed 1) :v1/wrapped]
213+
(enc/str-starts-with? packed "-") [(subs packed 1) :v1/unwrapped]
214+
:else [ packed :v2/unwrapped])
215+
packed))
216+
217+
(comment (parse-packed "+[[\"foo\"] \"uuid\"]"))
218+
219+
(defn- unpack "packed->[clj ?cb-uuid]"
220+
[packer packed]
221+
(let [[packed ?format] (parse-packed packed)
222+
unpacked #_[clj ?cb-uuid]
212223
(try
213-
(interfaces/unpack packer pstr)
224+
(interfaces/unpack packer packed)
214225
(catch #?(:clj Throwable :cljs :default) t
215-
(debugf "Bad package: %s (%s)" pstr t)
216-
[:chsk/bad-package pstr]))
226+
(debugf "Bad package: %s (%s)" packed t)
227+
[:chsk/bad-package packed]))
228+
229+
[clj ?cb-uuid]
230+
(case ?format
231+
:v1/wrapped unpacked
232+
:v1/unwrapped [unpacked nil]
233+
:v2/unwrapped unpacked)
217234

218-
[clj ?cb-uuid] (if wrapped? clj [clj nil])
219235
?cb-uuid (if (= 0 ?cb-uuid) :ajax-cb ?cb-uuid)]
220236

221-
(tracef "Unpacking: %s -> %s" prefixed-pstr [clj ?cb-uuid])
222237
[clj ?cb-uuid]))
223238

224-
(defn- pack "clj->prefixed-pstr"
225-
([packer clj]
226-
(let [;; "-" prefix => Unwrapped (has no callback)
227-
pstr (str "-" (interfaces/pack packer clj))]
228-
(tracef "Packing (unwrapped): %s -> %s" clj pstr)
229-
pstr))
239+
(def ^:dynamic *write-legacy-pack-format?*
240+
"Advanced option, most users can ignore this var. Only necessary
241+
for those that want to use Sente < v1.18 with a non-standard
242+
IPacker that deals with non-string payloads.
243+
244+
Details:
245+
246+
Sente uses a private message format as an implementation detail
247+
for client<->server comms.
248+
249+
As part of [#398], this format is being updated to support
250+
non-string (e.g. binary) payloads.
251+
252+
Unfortunately updating the format is non-trivial because:
253+
254+
1. Both the client & server need to support the same format.
255+
2. Clients are often served as cached cl/js.
256+
257+
To help ease migration, the new pack format is being rolled out
258+
in 2 stages:
259+
260+
Sente <= v1.16: reads v1 format only
261+
writes v1 format only
262+
263+
Sente v1.17: reads v1 and v2 formats
264+
writes v1 and v2 formats (v1 default) <- Currently here
265+
266+
Sente v1.18: reads v1 and v2 formats
267+
writes v1 and v2 formats (v2 default)
230268
269+
Sente >= v1.19: reads v2 format only
270+
writes v2 format only
271+
272+
This var controls which format to use for writing.
273+
Override default with `alter-var-root` or `binding`."
274+
275+
;; TODO -> false for Sente v1.18, remove for Sente >= v1.19
276+
true)
277+
278+
(defn- pack "[clj ?cb-uuid]->packed"
279+
([packer clj ] (pack packer clj nil))
231280
([packer clj ?cb-uuid]
232-
(let [;;; Keep wrapping as light as possible:
233-
?cb-uuid (if (= ?cb-uuid :ajax-cb) 0 ?cb-uuid)
234-
wrapped-clj (if ?cb-uuid [clj ?cb-uuid] [clj])
235-
;; "+" prefix => Wrapped (has callback)
236-
pstr (str "+" (interfaces/pack packer wrapped-clj))]
237-
(tracef "Packing (wrapped): %s -> %s" wrapped-clj pstr)
238-
pstr)))
281+
(let [?cb-uuid (if (= ?cb-uuid :ajax-cb) 0 ?cb-uuid)
282+
packed
283+
(interfaces/pack packer
284+
(if-some [cb-uuid ?cb-uuid]
285+
[clj cb-uuid]
286+
[clj ]))]
287+
288+
(if *write-legacy-pack-format?*
289+
(str "+" (have string? packed))
290+
(do packed)))))
291+
292+
(comment
293+
(unpack default-edn-packer
294+
(binding [*write-legacy-pack-format?* true]
295+
(pack default-edn-packer [:foo]))))
239296

240297
(deftype EdnPacker []
241298
interfaces/IPacker
@@ -1083,7 +1140,10 @@
10831140

10841141
#?(:cljs
10851142
(defn- create-js-client-websocket!
1086-
[{:as opts :keys [onerror-fn onmessage-fn onclose-fn uri-str headers]}]
1143+
[{:as opts
1144+
:keys [onerror-fn onmessage-fn onclose-fn binary-type
1145+
uri-str headers]}]
1146+
10871147
(when-let [WebSocket
10881148
(or
10891149
(enc/oget goog/global "WebSocket")
@@ -1096,6 +1156,10 @@
10961156
(aset "onmessage" onmessage-fn) ; Nb receives both push & cb evs!
10971157
;; Fires repeatedly (on each connection attempt) while server is down:
10981158
(aset "onclose" onclose-fn))
1159+
1160+
(when-let [bt binary-type] ; "arraybuffer" or "blob" (default)
1161+
(aset socket "binaryType" bt))
1162+
10991163
socket))))
11001164

11011165
(defn- create-websocket! [{:as opts :keys [onerror-fn onmessage-fn onclose-fn uri-str headers]}]

src/taoensso/sente/interfaces.cljc

+5-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@
4343

4444
(defprotocol IPacker
4545
"Extension pt. for client<->server comms data un/packers:
46-
arbitrary Clojure data <-> serialized strings."
46+
arbitrary Clojure data <-> serialized payloads.
47+
48+
NB if dealing with non-string payloads, see also
49+
`taoensso.sente/*write-legacy-pack-format?*`."
50+
4751
(pack [_ x])
4852
(unpack [_ x]))

0 commit comments

Comments
 (0)