-
-
Notifications
You must be signed in to change notification settings - Fork 193
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Server adapters for express and dogfort on node.js
- Loading branch information
Showing
3 changed files
with
169 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
(ns taoensso.sente.server-adapters.dogfort | ||
"Sente on node.js with dogfort (https://github.com/whamtet/dogfort)" | ||
{:author "Matthew Molloy <whamtet@gmail.com>"} | ||
(:require | ||
[taoensso.sente.server-adapters.generic-node :as generic-node])) | ||
|
||
;; Dogfort doesn't need anything special, use use the generic-node-ws | ||
;; adapter. | ||
(def dogfort-adapter generic-node/generic-node-adapter) | ||
(def sente-web-server-adapter dogfort-adapter) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
(ns taoensso.sente.server-adapters.express | ||
"Sente on node.js with express" | ||
{:author "Andrew Phillips <theasp@gmail.com>"} | ||
(:require | ||
[taoensso.sente :as sente] | ||
[taoensso.sente.server-adapters.generic-node :as generic-node] | ||
[taoensso.timbre :as timbre :refer-macros (tracef debugf infof warnf errorf)])) | ||
|
||
;; This adapter works differently that the others as sente is | ||
;; expecting ring requests but express uses http.IncomingMessage. | ||
;; While most of this adapter could be used for similar | ||
;; implementations there will be assumptions here that the following | ||
;; express middleware, or equivalents, ae in place: | ||
;; - cookie-parser | ||
;; - body-parser | ||
;; - csurf | ||
;; - express-session | ||
;; - express-ws | ||
|
||
;; See example-project for an implementation, as it's a bit | ||
;; different than something built on ring. | ||
|
||
(defn- make-ring-req [req res ring-req] | ||
"Emulate req as used by the ring library by processing the " | ||
(let [fake-params (merge (js->clj (.-params req) :keywordize-keys true) | ||
(js->clj (.-body req) :keywordize-keys true) | ||
(js->clj (.-query req) :keywordize-keys true) | ||
(:query ring-req)) | ||
ring-req (assoc ring-req | ||
:response res | ||
:body req | ||
:params fake-params)] | ||
(tracef "Emulated ring request: %s" ring-req) | ||
ring-req)) | ||
|
||
|
||
(defn wrap-ring-req [ring-fn req res ring-req] | ||
"Run a function that takes a ring request by converting a | ||
req, res, and fake ring-req map" | ||
(let [ring-req (make-ring-req req res ring-req)] | ||
(ring-fn ring-req))) | ||
|
||
(defn csrf-token [ring-req] | ||
"Get a valid token from csurf" | ||
(.csrfToken (:body ring-req))) | ||
|
||
(def default-options {:csrf-token-fn csrf-token}) | ||
|
||
(defn make-express-adapter | ||
"Provide a custom make-channel-socket-server! that wraps calls with | ||
wrap-ring-req as the functions from the real | ||
make-channel-socket-server! require ring-reqs. As a bonus you don't | ||
need to specify the adapter." | ||
[options] | ||
(tracef "Making express adapter") | ||
(let [ch (sente/make-channel-socket-server! | ||
(generic-node/GenericNodeServerChanAdapter.) | ||
(merge default-options options))] | ||
|
||
(assoc ch | ||
:ajax-get-or-ws-handshake-fn | ||
(fn [req res & [next ring-req]] | ||
(wrap-ring-req (:ajax-get-or-ws-handshake-fn ch) req res ring-req)) | ||
|
||
:ajax-post-fn | ||
(fn [req res & [next ring-req]] | ||
(wrap-ring-req (:ajax-post-fn ch) req res ring-req))))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
(ns taoensso.sente.server-adapters.generic-node | ||
"Sente on node.js using ws and http libraries" | ||
{:author "Andrew Phillips <theasp@gmail.com> & Matthew Molloy <whamtet@gmail.com>"} | ||
(:require | ||
[taoensso.sente.interfaces :as i] | ||
[taoensso.timbre :as timbre :refer-macros (tracef debugf infof warnf errorf)])) | ||
|
||
(defn- is-ws-open? [ws] | ||
(= (.-readyState ws) | ||
(.-OPEN ws))) | ||
|
||
(deftype GenericNodeWsAdapter [callbacks-map ws] | ||
i/IServerChan | ||
(-sch-send! [this msg close-after-send?] | ||
(let [pre-open? (is-ws-open? ws)] | ||
(try | ||
(.send ws msg) | ||
(catch :default e)) | ||
(when close-after-send? | ||
(.close ws)) | ||
pre-open?)) | ||
|
||
(sch-open? [server-ch] | ||
(is-ws-open? ws)) | ||
|
||
(sch-close! [server-ch] | ||
(let [pre-open? (is-ws-open? ws)] | ||
(.close ws) | ||
pre-open?))) | ||
|
||
(defn- make-ws-chan | ||
[{:keys [on-open on-close on-msg] :as callbacks-map} ws] | ||
(tracef "Making websocket adapter") | ||
(let [chan (new GenericNodeWsAdapter callbacks-map ws)] | ||
(on-open chan) | ||
(.on ws "message" | ||
(fn [data flags] | ||
(on-msg chan data))) | ||
(.onclose ws | ||
(fn [code message] | ||
(on-close chan code))))) | ||
|
||
(deftype GenericNodeAjaxAdapter [response-open? response] | ||
i/IServerChan | ||
(-sch-send! [this msg close-after-send?] | ||
(let [pre-open? @response-open?] | ||
(if close-after-send? | ||
(do | ||
(reset! response-open? false) | ||
(.end response msg)) | ||
(try | ||
(.write response msg) | ||
(catch :default e))) | ||
pre-open?)) | ||
|
||
(sch-open? [this] | ||
@response-open?) | ||
|
||
(sch-close! [this] | ||
(if @response-open? | ||
(.end response)) | ||
(reset! response-open? false))) | ||
|
||
(defn- make-ajax-chan | ||
[{:keys [on-open on-close on-msg] :as callbacks-map} response body] | ||
(tracef "Making ajax adapter") | ||
(let [response-open? (atom true) | ||
chan (new GenericNodeAjaxAdapter response-open? response)] | ||
(on-open chan) | ||
(.on body "data" | ||
(fn [data] | ||
(on-msg chan data))) | ||
;; TODO: If this is bad, bad, what should we do? | ||
#_(.on response ;bad, bad! | ||
"finish" | ||
#(on-close chan))) | ||
;; For AJAX connections, if we reply blank then the route | ||
;; matcher will fail. dogfort will send a 404 response and | ||
;; close the connection. to keep it open we just send this | ||
;; instead of a ring response. This should have no | ||
;; detrimental effect on other servers. | ||
{:keep-alive true}) | ||
|
||
(deftype GenericNodeServerChanAdapter [] | ||
i/IServerChanAdapter | ||
(ring-req->server-ch-resp [server-ch-adapter ring-req callbacks-map] | ||
(let [{:keys [websocket response body]} ring-req] | ||
(if websocket | ||
(make-ws-chan callbacks-map websocket) | ||
(make-ajax-chan callbacks-map response body))))) | ||
|
||
(def generic-node-adapter (GenericNodeServerChanAdapter.)) |