initial commit
This commit is contained in:
commit
ada511f5d2
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
.DS_Store
|
||||||
|
/target
|
||||||
|
/classes
|
||||||
|
/checkouts
|
||||||
|
pom.xml
|
||||||
|
pom.xml.asc
|
||||||
|
*.jar
|
||||||
|
*.class
|
||||||
|
/.lein-*
|
||||||
|
/.nrepl-port
|
||||||
|
.settings/
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Gered King
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
170
README.md
Normal file
170
README.md
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
# clj-browserchannel-messaging
|
||||||
|
|
||||||
|
Helper utilities and Ring middleware for using [BrowserChannel](http://thegeez.net/2012/04/03/why_browserchannel.html)
|
||||||
|
as a real-time client-server messaging protocol in your Clojure/ClojureScript web apps.
|
||||||
|
|
||||||
|
**Note: This library is currently "beta status." As such some of this setup may be a bit overly complex
|
||||||
|
and the documentation a bit rough.**
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
None of the current versions of Clojure BrowserChannel libraries we need (including this library at the moment)
|
||||||
|
are available on Clojars.
|
||||||
|
|
||||||
|
You will need to install **clj-browserchannel-server** and **clj-browserchannel-jetty-adapter** manually via
|
||||||
|
`lein install`. This library depends on the versions of these libraries
|
||||||
|
[located here](https://github.com/gered/clj-browserchannel) currently.
|
||||||
|
|
||||||
|
Then you will need to locally install this library via `lein install` as well.
|
||||||
|
|
||||||
|
### `project.clj`
|
||||||
|
|
||||||
|
Add these to your dependencies.
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
[net.thegeez/clj-browserchannel-jetty-adapter "0.0.5"]
|
||||||
|
[clj-browserchannel-messaging "0.0.1"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Message Format
|
||||||
|
|
||||||
|
This library wraps messages sent/received by client and server in a lightly structured format:
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
{:topic <<keyword describing the contents of the message>>
|
||||||
|
:body <<any clojure data structure or value>>}
|
||||||
|
```
|
||||||
|
|
||||||
|
The topic is kind of like a message type or category. Similar types of messages communicating the same types of
|
||||||
|
information should share the same message topic.
|
||||||
|
|
||||||
|
## Server-side Setup
|
||||||
|
|
||||||
|
### Jetty Async
|
||||||
|
|
||||||
|
Both clj-browserchannel and this library _require_ use of an async HTTP server. The
|
||||||
|
**clj-browserchannel-jetty-adapter** library you installed previously contains the Jetty Async adapter that can be
|
||||||
|
used by **clj-browserchannel-server**.
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
(ns yourapp
|
||||||
|
(:gen-class)
|
||||||
|
(:require [net.thegeez.jetty-async-adapter :refer [run-jetty-async]]))
|
||||||
|
|
||||||
|
(defn -main [& args]
|
||||||
|
(run-jetty-async handler {:port 8080 :join? false}))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ring Middleware
|
||||||
|
|
||||||
|
You need to add the `clj-browserchannel-messaging.server/wrap-browserchannel` middleware to your Ring app handler.
|
||||||
|
|
||||||
|
Note that currently, this library does not play nice with Ring's `wrap-anti-forgery` middleware. If you are using
|
||||||
|
lib-noir or ring-defaults, then this middleware is enabled by default when using the `site-defaults` settings
|
||||||
|
from ring-defaults. You will need to disable this by setting, e.g.:
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
(assoc-in site-defaults [:security :anti-forgery] false)
|
||||||
|
```
|
||||||
|
|
||||||
|
Otherwise you will get 403 access denied responses when sending BrowserChannel messages from client to server. This
|
||||||
|
will be addressed properly in this library in a future release.
|
||||||
|
|
||||||
|
See the doc comments for `clj-browserchannel-messaging.server/wrap-browserchannel` for more detailed descriptions
|
||||||
|
of the options you can pass to this middleware. Example usage:
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
(wrap-browserchannel
|
||||||
|
{:on-open (fn [browserchannel-session-id request]
|
||||||
|
(println "browserchannel session opened with client:" browserchannel-session-id))
|
||||||
|
:on-close (fn [browserchannel-session-id request reason]
|
||||||
|
(println "browserchannel session closed for client:" browserchannel-session-id ", reason:" reason))
|
||||||
|
:on-receive (fn [browserchannel-session-id request message]
|
||||||
|
(println "received message from client" browserchannel-session-id ":" message))})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sending and Receiving Messages
|
||||||
|
|
||||||
|
The `:on-receive` handler passed to `wrap-browserchannel` will be invoked when any message is received from any
|
||||||
|
BrowserChannel client. It's basically your main event handler.
|
||||||
|
|
||||||
|
However, you can also use `clj-browserchannel-messaging.server/message-handler` anywhere in your application code
|
||||||
|
to listen for specific types of messages and provide a separate handler function to run when they are received.
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
(message-handler
|
||||||
|
:foobar
|
||||||
|
(fn [msg]
|
||||||
|
(println "received :foobar message:" msg)))
|
||||||
|
```
|
||||||
|
|
||||||
|
To send a message to a client, you must have the "BrowserChannel session id" associated with that client. This is
|
||||||
|
first generated and passed to the `:on-open` event and becomes invalid after `:on-close`. All messages received
|
||||||
|
from the client will automatically have the BrowserChannel session id of the sending client included in the message
|
||||||
|
under the `:browserchannel-session-id` key.
|
||||||
|
|
||||||
|
Use the `clj-browserchannel-messaging.server/send` function to send a message to a client. If the message could not
|
||||||
|
be sent for any reason, a nil value is returned.
|
||||||
|
|
||||||
|
#### BrowserChannel Sessions
|
||||||
|
|
||||||
|
Just a quick note about BrowserChannel Sessions. They are essentially tied to the length of time that a user has
|
||||||
|
a single page of the web app open in their browser, so obviously it goes without saying that BrowserChannel should be
|
||||||
|
used by Single Page Apps or other applications that keep the user on a single page for a lengthy time and have heavy
|
||||||
|
client-side scripting driving the UI.
|
||||||
|
|
||||||
|
When the page is first loaded, the BrowserChannel setup occurs and a session id is generated (`:on-open`). The user
|
||||||
|
then continues using the app in their browser and if they leave the page or close their browser, the BrowserChannel
|
||||||
|
connection and session is closed (`:on-close`). If the user refreshes the page with their browser, the existing
|
||||||
|
session is closed and a new one is created when the page reloads.
|
||||||
|
|
||||||
|
Also, BrowserChannel sessions can expire after a period of inactivity (the `:on-close` reason will be "Timed out").
|
||||||
|
|
||||||
|
## Client-side Setup
|
||||||
|
|
||||||
|
### Page Load
|
||||||
|
|
||||||
|
When the page loads, in your ClojureScript code you should call `clj-browserchannel-messaging.client/init!`. This
|
||||||
|
function takes some options, the most important of which are callbacks (similar in idea to the callbacks you specify
|
||||||
|
to the `wrap-browserchannel` middleware on the server-side).
|
||||||
|
|
||||||
|
See the doc comments for `clj-browserchannel-messaging.client/init!` for more detailed descriptions of the options
|
||||||
|
available. Example usage:
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
(init!
|
||||||
|
{:on-open (fn []
|
||||||
|
(println "on-open"))
|
||||||
|
:on-send (fn [msg]
|
||||||
|
(println "sending" msg))
|
||||||
|
:on-receive (fn [msg]
|
||||||
|
(println "receiving" msg))
|
||||||
|
:on-close (fn [pending undelivered]
|
||||||
|
(println "closed" pending undelivered))
|
||||||
|
:on-error (fn [error]
|
||||||
|
(println "error:" error))})
|
||||||
|
```
|
||||||
|
|
||||||
|
On the client-side, you'll probably care most about `:on-receive` and possibly `:on-close` and `:on-error` to help
|
||||||
|
gracefully deal with connection loss / server timeouts.
|
||||||
|
|
||||||
|
### Sending and Receiving Messages
|
||||||
|
|
||||||
|
Note that, unlike on the server, the client does not deal with any "BrowserChannel session ids." That is because
|
||||||
|
it only sends and receives messages to/from the server, not directly to other clients.
|
||||||
|
|
||||||
|
Like on the server, the `:on-receive` handler will be invoked when any message is received from the server.
|
||||||
|
|
||||||
|
You can also use `clj-browserchannel-messaging.client/message-handler` which works in exactly the same manner as the
|
||||||
|
server-side version mentioned above.
|
||||||
|
|
||||||
|
To send a message to the server, use the `clj-browserchannel-messaging.client/send` function. If the message could
|
||||||
|
not be sent for any reason, a nil value is returned.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Copyright © 2014 Gered King
|
||||||
|
|
||||||
|
Distributed under the the MIT License (the same as clj-browserchannel). See LICENSE for more details.
|
13
project.clj
Normal file
13
project.clj
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
(defproject clj-browserchannel-messaging "0.0.1"
|
||||||
|
:description "Tools for quickly using BrowserChannel for bi-directional client-server messaging."
|
||||||
|
:url "https://github.com/gered/clj-browserchannel-messaging"
|
||||||
|
:license {:name "MIT License"
|
||||||
|
:url "http://opensource.org/licenses/MIT"}
|
||||||
|
|
||||||
|
:dependencies [[org.clojure/clojure "1.6.0"]
|
||||||
|
[org.clojure/clojurescript "0.0-2371" :scope "provided"]
|
||||||
|
[org.clojure/core.async "0.1.346.0-17112a-alpha"]
|
||||||
|
[net.thegeez/clj-browserchannel-server "0.0.9"]]
|
||||||
|
|
||||||
|
:source-paths ["src/clj"]
|
||||||
|
:resource-paths ["src/cljs"])
|
114
src/clj/clj_browserchannel_messaging/server.clj
Normal file
114
src/clj/clj_browserchannel_messaging/server.clj
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
(ns clj-browserchannel-messaging.server
|
||||||
|
(:refer-clojure :exclude [send])
|
||||||
|
(:require [clojure.edn :as edn]
|
||||||
|
[clojure.core.async :refer [chan pub sub <! put! go-loop]]
|
||||||
|
[net.thegeez.browserchannel :as browserchannel]))
|
||||||
|
|
||||||
|
(defonce incoming-messages (chan))
|
||||||
|
(defonce incoming-messages-pub (pub incoming-messages :topic))
|
||||||
|
|
||||||
|
(defn encode-message
|
||||||
|
"encodes a message made up of a topic and body into a format that can be sent
|
||||||
|
via browserchannel to a client. topic should be a keyword, while body can be
|
||||||
|
anything. returns nil if the message could not be encoded."
|
||||||
|
[topic body]
|
||||||
|
(if-let [topic (name topic)]
|
||||||
|
{"topic" topic
|
||||||
|
"body" (pr-str body)}))
|
||||||
|
|
||||||
|
(defn decode-message
|
||||||
|
"decodes a message received via browserchannel into a map composed of a
|
||||||
|
topic and body. returns nil if the message could not be decoded."
|
||||||
|
[msg]
|
||||||
|
(let [topic (get msg "topic")
|
||||||
|
body (get msg "body")]
|
||||||
|
(if topic
|
||||||
|
{:topic (keyword topic)
|
||||||
|
:body (edn/read-string body)})))
|
||||||
|
|
||||||
|
(defn send
|
||||||
|
"sends a browserchannel message to a client identified by the given
|
||||||
|
browserchannel session id. topic should be a keyword, while body can be
|
||||||
|
anything. returns nil if the message was not sent."
|
||||||
|
[browserchannel-session-id topic body]
|
||||||
|
(if-let [encoded (encode-message topic body)]
|
||||||
|
(browserchannel/send-map browserchannel-session-id encoded)))
|
||||||
|
|
||||||
|
(defn message-handler
|
||||||
|
"listens for incoming browserchannel messages with the specified topic.
|
||||||
|
executes the passed handler function when any are received. handler should
|
||||||
|
be a function which accepts the received decoded message. the decoded
|
||||||
|
message will contain the browserchannel session id of the client that
|
||||||
|
sent the message under :browserchannel-session-id.
|
||||||
|
note that the handler is executed asynchronously."
|
||||||
|
[topic handler]
|
||||||
|
(let [incoming-topic-messages (chan)]
|
||||||
|
(sub incoming-messages-pub topic incoming-topic-messages)
|
||||||
|
(go-loop []
|
||||||
|
(when-let [msg (<! incoming-topic-messages)]
|
||||||
|
(handler msg)
|
||||||
|
(recur)))))
|
||||||
|
|
||||||
|
(defn- handle-session [browserchannel-session-id req {:keys [on-open on-close on-receive]}]
|
||||||
|
(if on-open (on-open browserchannel-session-id req))
|
||||||
|
(browserchannel/add-listener
|
||||||
|
browserchannel-session-id
|
||||||
|
:close
|
||||||
|
(fn [request reason]
|
||||||
|
(if on-close (on-close browserchannel-session-id request reason))))
|
||||||
|
(browserchannel/add-listener
|
||||||
|
browserchannel-session-id
|
||||||
|
:map
|
||||||
|
(fn [request m]
|
||||||
|
(if-let [decoded (decode-message m)]
|
||||||
|
(let [msg (assoc decoded :browserchannel-session-id browserchannel-session-id)]
|
||||||
|
(if on-receive (on-receive browserchannel-session-id request msg))
|
||||||
|
(put! incoming-messages msg))))))
|
||||||
|
|
||||||
|
(defn wrap-browserchannel
|
||||||
|
"Middleware to handle server-side browserchannel session and message
|
||||||
|
processing.
|
||||||
|
|
||||||
|
You can specify the same set of options that
|
||||||
|
net.thegeez.browserchannel/wrap-browserchannel accepts, except for
|
||||||
|
:on-session (which will be overridden even if you do try to pass it).
|
||||||
|
See net.thegeez.browserchannel/default-options for more info.
|
||||||
|
|
||||||
|
Note that if :base is not specified, the default is '/browserchannel'
|
||||||
|
(this differs from net.thegeez.browserchannel/wrap-browserchannel).
|
||||||
|
|
||||||
|
In addition, you can pass event handler functions. Note that the return
|
||||||
|
value for all of these handlers is not used.
|
||||||
|
|
||||||
|
:on-open
|
||||||
|
Occurs when a new browserchannel session has been established. Receives 2
|
||||||
|
arguments: the browserchannel session id and the request map (for the
|
||||||
|
request that resulted in the browserchannel session being established) as
|
||||||
|
arguments.
|
||||||
|
|
||||||
|
:on-receive
|
||||||
|
Occurs when a new message is received from a client. Receives 3 arguments:
|
||||||
|
the browsercannel session id, the request map (for the client request that
|
||||||
|
the message was sent with), and the actual decoded message as arguments.
|
||||||
|
the browserchannel session id of the client that sent the message is
|
||||||
|
automatically added to the message under :browserchannel-session-id.
|
||||||
|
|
||||||
|
:on-close
|
||||||
|
Occurs when the browserchannel session is closed. Receives 3 arguments: the
|
||||||
|
browserchannel session id, the request map (for the request sent by the
|
||||||
|
client causing the session to be closed, if any), and a string containing
|
||||||
|
the reason for the session close. Note that, this may or may not be
|
||||||
|
initiated directly by the client. The request argument will be nil if the
|
||||||
|
session is being closed as part of some server-side operation (e.g.
|
||||||
|
browserchannel session timeout)."
|
||||||
|
[handler & [opts]]
|
||||||
|
(-> handler
|
||||||
|
(browserchannel/wrap-browserchannel
|
||||||
|
(assoc
|
||||||
|
opts
|
||||||
|
:base (or (:base opts) "/browserchannel")
|
||||||
|
:on-session
|
||||||
|
(fn [browserchannel-session-id request]
|
||||||
|
(handle-session
|
||||||
|
browserchannel-session-id request
|
||||||
|
(select-keys opts [:on-open :on-close :on-receive])))))))
|
155
src/cljs/clj_browserchannel_messaging/client.cljs
Normal file
155
src/cljs/clj_browserchannel_messaging/client.cljs
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
(ns clj-browserchannel-messaging.client
|
||||||
|
(:require-macros [cljs.core.async.macros :refer [go-loop]])
|
||||||
|
(:require
|
||||||
|
[cljs.reader :as reader]
|
||||||
|
[cljs.core.async :refer [pub sub chan <! put!]]
|
||||||
|
goog.net.BrowserChannel
|
||||||
|
[goog.events :as events]))
|
||||||
|
|
||||||
|
(defonce browser-channel (goog.net.BrowserChannel.))
|
||||||
|
|
||||||
|
(defonce incoming-messages (chan))
|
||||||
|
(defonce incoming-messages-pub (pub incoming-messages :topic))
|
||||||
|
(defonce outgoing-messages (chan))
|
||||||
|
|
||||||
|
(defn encode-message
|
||||||
|
"encodes a message composed of a topic and body into a format that can be
|
||||||
|
sent via browserchannel. topic should be a keyword while body can be
|
||||||
|
anything. returns nil if the message could not be encoded."
|
||||||
|
[{:keys [topic body] :as msg}]
|
||||||
|
(if-let [topic (name topic)]
|
||||||
|
(clj->js {"topic" topic
|
||||||
|
"body" (pr-str body)})))
|
||||||
|
|
||||||
|
(defn decode-message
|
||||||
|
"decodes a message received via browserchannel into a map composed of a
|
||||||
|
topic and body. returns nil if the message could not be decoded."
|
||||||
|
[msg]
|
||||||
|
(let [msg (js->clj msg)
|
||||||
|
topic (keyword (get msg "topic"))
|
||||||
|
body (get msg "body")]
|
||||||
|
(if topic
|
||||||
|
{:topic topic
|
||||||
|
:body (reader/read-string body)})))
|
||||||
|
|
||||||
|
(defn send
|
||||||
|
"sends a browserchannel message to the server asynchronously."
|
||||||
|
[topic body]
|
||||||
|
(put! outgoing-messages {:topic topic :body body}))
|
||||||
|
|
||||||
|
(defn message-handler
|
||||||
|
"listens for incoming browserchannel messages with the specified topic.
|
||||||
|
executes the passed handler function when any are received. handler should
|
||||||
|
be a function which accepts the received decoded message.
|
||||||
|
note that the handler is executed asynchronously"
|
||||||
|
[topic handler]
|
||||||
|
(let [incoming-topic-messages (chan)]
|
||||||
|
(sub incoming-messages-pub topic incoming-topic-messages)
|
||||||
|
(go-loop []
|
||||||
|
(when-let [msg (<! incoming-topic-messages)]
|
||||||
|
(handler msg)
|
||||||
|
(recur)))))
|
||||||
|
|
||||||
|
(defn- handle-outgoing [channel on-send]
|
||||||
|
(go-loop []
|
||||||
|
(when-let [msg (<! outgoing-messages)]
|
||||||
|
(when-let [encoded (encode-message msg)]
|
||||||
|
(if on-send (on-send msg))
|
||||||
|
(.sendMap channel encoded))
|
||||||
|
(recur))))
|
||||||
|
|
||||||
|
(defn- handle-incoming [channel msg on-receive]
|
||||||
|
(when-let [decoded (decode-message msg)]
|
||||||
|
(if on-receive (on-receive decoded))
|
||||||
|
(put! incoming-messages decoded)))
|
||||||
|
|
||||||
|
; see: http://docs.closure-library.googlecode.com/git/local_closure_goog_net_browserchannel.js.source.html#line521
|
||||||
|
(def bch-error-enum-to-keyword
|
||||||
|
{0 :ok
|
||||||
|
2 :request-failed
|
||||||
|
4 :logged-out
|
||||||
|
5 :no-data
|
||||||
|
6 :unknown-session-id
|
||||||
|
7 :stop
|
||||||
|
8 :network
|
||||||
|
9 :blocked
|
||||||
|
10 :bad-data
|
||||||
|
11 :bad-response
|
||||||
|
12 :active-x-blocked})
|
||||||
|
|
||||||
|
(defn- bch-error-enum->keyword [error-code]
|
||||||
|
(or (get bch-error-enum-to-keyword error-code)
|
||||||
|
:unknown))
|
||||||
|
|
||||||
|
(defn- handler [{:keys [on-open on-send on-receive on-close on-error]}]
|
||||||
|
(let [h (goog.net.BrowserChannel.Handler.)]
|
||||||
|
(set! (.-channelOpened h)
|
||||||
|
(fn [channel]
|
||||||
|
(if on-open (on-open))
|
||||||
|
(handle-outgoing channel on-send)))
|
||||||
|
(set! (.-channelHandleArray h)
|
||||||
|
(fn [channel msg]
|
||||||
|
(handle-incoming channel msg on-receive)))
|
||||||
|
(set! (.-channelClosed h)
|
||||||
|
(fn [channel pending undelivered]
|
||||||
|
(if on-close (on-close pending undelivered))))
|
||||||
|
(set! (.-channelError h)
|
||||||
|
(fn [channel error]
|
||||||
|
(if on-error (on-error (bch-error-enum->keyword error)))))
|
||||||
|
h))
|
||||||
|
|
||||||
|
(defn- set-debug-logger! [level]
|
||||||
|
(if-let [logger (-> browser-channel .getChannelDebug .getLogger)]
|
||||||
|
(.setLevel logger level)))
|
||||||
|
|
||||||
|
(defn init!
|
||||||
|
"sets up browserchannel for use, creating a handler with the specified
|
||||||
|
properties. this function should be called once on page load.
|
||||||
|
|
||||||
|
properties:
|
||||||
|
|
||||||
|
:base - the base URL on which the server's browserchannel routes are
|
||||||
|
located at. default is '/browserchannel'
|
||||||
|
|
||||||
|
callbacks:
|
||||||
|
|
||||||
|
:on-open
|
||||||
|
occurs when a browserchannel session with the server is established
|
||||||
|
|
||||||
|
:on-close
|
||||||
|
occurs when the browserchannel session is closed (e.g. terminated by the
|
||||||
|
server due to error, timeout, etc).
|
||||||
|
receives 2 arguments: array of pending messages that may or may not
|
||||||
|
have been sent to the server, and an array of undelivered messages that
|
||||||
|
have definitely not been delivered to the server. note that these
|
||||||
|
arguments will both be javascript arrays containing
|
||||||
|
goog.net.BrowserChannel.QueuedMap objects.
|
||||||
|
|
||||||
|
:on-error
|
||||||
|
occurs when an error occurred on the browserchannel. receives 1 argument:
|
||||||
|
a keyword indicating the type of error
|
||||||
|
|
||||||
|
:on-send
|
||||||
|
raised whenever a message is sent via the send function. receives 1
|
||||||
|
argument: the message that is to be sent. this is probably only useful for
|
||||||
|
debugging/logging purposes. note that this event is only raised for messages
|
||||||
|
which can be encoded by encode-message
|
||||||
|
|
||||||
|
:on-receive
|
||||||
|
occurs whenever a browserchannel message is received from the server.
|
||||||
|
receives 1 argument: the message that was received. note that this event is
|
||||||
|
only raised for messages which can be decoded by decode-message. also note
|
||||||
|
that this event is raised for all messages received, regardless of any
|
||||||
|
listeners created via message-handler."
|
||||||
|
[& [{:keys [base] :as opts}]]
|
||||||
|
(let [base (or base "/browserchannel")]
|
||||||
|
(events/listen
|
||||||
|
js/window "unload"
|
||||||
|
(fn []
|
||||||
|
(.disconnect browser-channel)
|
||||||
|
(events/removeAll)))
|
||||||
|
(set-debug-logger! goog.debug.Logger.Level.OFF)
|
||||||
|
(.setHandler browser-channel (handler opts))
|
||||||
|
(.connect browser-channel
|
||||||
|
(str base "/test")
|
||||||
|
(str base "/bind"))))
|
Reference in a new issue