diff --git a/clj-browserchannel-server/src/net/thegeez/browserchannel.clj b/clj-browserchannel-server/src/net/thegeez/browserchannel.clj index 59c0106..cae0cc3 100644 --- a/clj-browserchannel-server/src/net/thegeez/browserchannel.clj +++ b/clj-browserchannel-server/src/net/thegeez/browserchannel.clj @@ -50,9 +50,8 @@ (.schedule scheduler f secs TimeUnit/SECONDS)) ;; json responses are sent as "size-of-response\njson-response" -(defn size-json-str [data] - (let [json (json/json-str data) - size (alength (.getBytes json "UTF-8"))] +(defn size-json-str [json] + (let [size (alength (.getBytes json "UTF-8"))] (str size "\n" json))) ;; make sure the root URI for channels starts with a / for route matching @@ -195,7 +194,7 @@ (when (seq domain) (async-adapter/write-chunk respond (str "\n")))) (write [this data] - (async-adapter/write-chunk respond (str "\n")) + (async-adapter/write-chunk respond (str "\n")) (when-not write-padding-sent (async-adapter/write-chunk respond ie-stream-padding) (set! write-padding-sent true))) @@ -225,7 +224,7 @@ ;; the client acknowledges received arrays when creating a new backwardchannel (acknowledge-arrays [this array-ids]) - (queue-array [this array]) + (queue-string [this string]) ;; heartbeat is a timer to send noop over the backward channel (clear-heartbeat [this]) @@ -338,7 +337,7 @@ (let [session-agent *agent*] (schedule (fn [] (send-off session-agent #(-> % - (queue-array ["noop"]) + (queue-string "[\"noop\"]") flush-buffer))) (:heartbeat-interval details)))))) (clear-session-timeout [this] @@ -355,10 +354,10 @@ (schedule (fn [] (send-off session-agent close "Timed out")) (:session-timeout-interval details)))))) - (queue-array [this array] + (queue-string [this string] (let [next-array-id (inc last-sent-array-id)] (-> this - (update-in [:array-buffer] conj [next-array-id array]) + (update-in [:array-buffer] conj [next-array-id string]) (assoc :last-sent-array-id next-array-id)))) (acknowledge-arrays [this array-id] (update-in this [:to-confirm-array-ids] @@ -371,10 +370,15 @@ this ;; nothing to do when there's no connection (if-let [buffer (seq array-buffer)] (try - ;; write throws exception when the connection is closed - (write (:respond back-channel) buffer) + ;; buffer contains [[1 json-str] ...] can't use + ;; json-str which will double escape the json + (let [data (str "[" + (str/join "," (map (fn [[n d]] (str "[" n "," d "]")) buffer)) + "]")] + ;; write throws exception when the connection is closed + (write (:respond back-channel) data)) ;; size is an approximation - (let [this (let [size (reduce + 0 (map count (map json/json-str buffer)))] + (let [this (let [size (reduce + 0 (map count (map second buffer)))] (-> this (assoc :array-buffer clojure.lang.PersistentQueue/EMPTY) (assoc :last-sent-array-id (first (last buffer))) @@ -456,23 +460,24 @@ (let [has-back-channel (if (:back-channel session) 1 0) last-sent-array-id (:last-sent-array-id session) ;; the sum of all the data that is still to be send - ;; technically the size is padded by "[","," and "]" dividers inserted by - ;; the encoding but this number is only used for estimation on - ;; the client side. outstanding-bytes (let [buffer (:array-buffer session)] (if (empty? buffer) 0 - (reduce + 0 (map count (map json/json-str buffer)))))] + (reduce + 0 (map count (map second buffer)))))] [has-back-channel last-sent-array-id outstanding-bytes])) ;; convience function to send data to a session -;; the data will be queued until there is a backchannel to send it over -(defn send-map [session-id map] +;; the data will be queued until there is a backchannel to send it +;; over +(defn send-string [session-id string] (when-let [session-agent (get @sessions session-id)] (send-off session-agent #(-> % - (queue-array map) + (queue-string string) flush-buffer)))) +(defn send-map [session-id map] + (send-string session-id (json/json-str map))) + ;; wrap the respond function from :reactor with the proper ;; responsewrapper for either IE or other clients (defn wrap-continuation-writers [handler options] @@ -545,7 +550,7 @@ {:status 200 :headers (assoc (:headers options) "Content-Type" "application/javascript") :body - (size-json-str [[0,["c", session-id, host-prefix, 8]]])}) + (size-json-str (json/json-str [[0,["c", session-id, host-prefix, 8]]]))}) ;; For existing sessions: ;; Forward sent data by client to listeners ;; reply with @@ -558,7 +563,7 @@ (let [status (session-status @session-agent)] {:status 200 :headers (:headers options) - :body (size-json-str status)}))))) + :body (size-json-str (json/json-str status))}))))) ;; GET req server->client is a backwardchannel opened by client (defn handle-backward-channel [req session-agent options]