Carol Sente Will Not Run Again

sente

ptaoussanis / sente Goto Github PK

ane.6K 50.0 179.0 1.35 MB

Realtime web comms for Clojure/Script

Home Page: https://www.taoensso.com

License: Eclipse Public License 1.0

Clojure 100.00%
clojure clojurescript epl taoensso websockets http-kit ajax async edn transit immutant

sente'due south Introduction

Taoensso open-source

CHANGELOG | API | current Intermission Version:

[com.taoensso/sente                                          "1.16.2"                    ]                                          ;                      See CHANGELOG for details                  

See here if y'all're interested in helping support my open up-source work, thanks! - Peter Taoussanis

Sente: realtime web comms for Clojure/Script

Or: We don't need no Socket.IO

Or: core.async + Ajax + WebSockets = The Shiznizzle

Sente is a modest client+server library that makes it easy to build reliable, high-performance realtime web applications with Clojure + ClojureScript.

Hero

Sen-te (先手) is a Japanese Go term used to describe a play with such an overwhelming follow-up that information technology demands an immediate response, leaving its player with the initiative.

(I'd besides recommend checking out James Henderson's Chord and Kevin Lynagh's jetty7-websockets-async equally possible alternatives!)

Features

  • Bidirectional a/sync comms over both WebSockets and Ajax (auto-fallback)
  • It simply works: automobile keep-alives, buffering, protocol selection, reconnects
  • Efficient design incl. transparent effect batching for low-bandwidth apply, even over Ajax
  • Send arbitrary Clojure vals over edn or Transit (JSON, MessagePack, etc.)
  • Tiny API: brand-aqueduct-socket! and you're expert to go
  • Automatic, sensible support for users connected with multiple clients and/or devices simultaneously
  • Realtime info on which users are connected over which protocols (v0.10.0+)
  • Flexible model: apply it anywhere you'd employ WebSockets/Ajax/Socket.IO, etc.
  • Standard Ring security model: auth as you like, HTTPS when bachelor, CSRF back up, etc.
  • Fully documented, with examples
  • Small codebase: ~ane.5k lines for the unabridged client+server implementation
  • Supported servers: http-kit, Immutant v2+, nginx-clojure, node.js, Aleph, ring-jetty9-adapter

Capabilities

Protocol customer>server customer>server + ack/answer server>user push
WebSockets ✓ (native) ✓ (emulated) ✓ (native)
Ajax ✓ (emulated) ✓ (native) ✓ (emulated)

So you tin ignore the underlying protocol and deal direct with Sente's unified API. It'due south simple, and exposes the all-time of both WebSockets (bidirectionality + performance) and Ajax (optional evented ack/respond model).

Getting started

Note that there'south as well a diverseness of total example projects available

Add the necessary dependency to your project:

Leiningen: [com.taoensso/sente                                          "i.xvi.2"                    ]                                          ;                      or                    deps.edn:   com.taoensso/sente {:mvn/version                                          "1.16.2"                    }

On the server (Clojure) side

Showtime make certain that you lot're using one of the supported web servers (PRs for additional server adapters welcome!).

Somewhere in your web app's lawmaking you'll already have a routing mechanism in place for handling Ring requests by request URL. If you're using Compojure for case, you'll have something that looks like this:

(defroutes                    my-app                    (Go                                          "/"                                        req (my-landing-pg-handler                    req))   (Mail                                          "/submit-form"                                        req (my-form-submit-handler                    req)))

For Sente, we're going to add 2 new URLs and setup their handlers:

(ns                    my-server-side-routing-ns                                          ;                      .clj                    (:require                                          ;; <other stuff>                    [taoensso.sente                    :every bit                    sente]                                          ;                      <--- Add this                    [ring.middleware.anti-forgery                    :refer                    [wrap-anti-forgery]]                                          ;                      <--- Recommended                                          ;; Uncomment a web-server adapter --->                                          ;; [taoensso.sente.server-adapters.http-kit      :refer (go-sch-adapter)]                                          ;; [taoensso.sente.server-adapters.immutant      :refer (go-sch-adapter)]                                          ;; [taoensso.sente.server-adapters.nginx-clojure :refer (get-sch-adapter)]                                          ;; [taoensso.sente.server-adapters.aleph         :refer (become-sch-adapter)]                    ))                                          ;;; Add this: --->                    (let                    [{:keys                    [ch-recv ship-fn continued-uids               ajax-mail-fn ajax-go-or-ws-handshake-fn]}       (sente/make-aqueduct-socket!                    (become-sch-adapter) {})]    (def                    band-ajax-post                    ajax-mail-fn)   (def                    ring-ajax-get-or-ws-handshake                    ajax-get-or-ws-handshake-fn)   (def                    ch-chsk                    ch-recv)                                          ;                      ChannelSocket'southward receive channel                    (def                    chsk-send!                    send-fn)                                          ;                      ChannelSocket's ship API fn                    (def                    connected-uids                    connected-uids)                                          ;                      Watchable, read-only cantlet                    )  (defroutes                    my-app-routes                                          ;; <other stuff>                                          ;;; Add these 2 entries: --->                    (GET                                          "/chsk"                                        req (band-ajax-go-or-ws-handshake                    req))   (POST                                          "/chsk"                                        req (ring-ajax-mail                    req))   )  (def                    my-app                    (->                    my-app-routes                                          ;; Add together necessary Ring middleware:                    ring.middleware.keyword-params/wrap-keyword-params       band.middleware.params/wrap-params       ring.middleware.anti-forgery/wrap-anti-forgery       ring.middleware.session/wrap-session))

The ring-ajax-post and band-ajax-get-or-ws-handshake fns will automatically handle Ring Become and POST requests to our channel socket URL ("/chsk"). Together these have care of the messy details of establishing + maintaining WebSocket or long-polling requests.

Add together a CSRF token somewhere in your HTML:

                    (let [csrf-token (forcefulness ring.middleware.anti-forgery/*anti-forgery-token*)]   [:div#sente-csrf-token {:data-csrf-token csrf-token}])                                      

On the client (ClojureScript) side

Yous'll setup something similar on the customer side:

(ns                    my-client-side-ns                                          ;                      .cljs                    (:require-macros                    [cljs.core.async.macros                    :as                    asyncm                    :refer                    (go                    get-loop)])   (:require                                          ;; <other stuff>                    [cljs.core.async                    :equally                    async                    :refer                    (<!                    >! put! chan)]    [taoensso.sente                    :as                    sente                    :refer                    (cb-success?)]                                          ;                      <--- Add this                    ))                                          ;;; Add this: --->                    (def                    ?csrf-token                    (when-let                    [el (.getElementById                    js/certificate                                          "sente-csrf-token"                    )]     (.getAttribute                    el                                          "data-csrf-token"                    )))  (permit                    [{:keys                    [chsk ch-recv transport-fn country]}       (sente/brand-channel-socket-client!                                          "/chsk"                                                              ;                      Notation the same path as before                    ?csrf-token        {:type                    :automobile                                          ;                      e/o #{:car :ajax :ws}                    })]    (def                    chsk                    chsk)   (def                    ch-chsk                    ch-recv)                                          ;                      ChannelSocket's receive aqueduct                    (def                    chsk-transport!                    transport-fn)                                          ;                      ChannelSocket's send API fn                    (def                    chsk-land                    state)                                          ;                      Watchable, read-simply atom                    )

Now what?

The customer will automatically initiate a WebSocket or repeating long-polling connectedness to your server. Client<->server events are now ready to transmit over the ch-chsk channel.

Terminal step: yous'll want to claw your ain event handlers upwardly to this aqueduct. Delight see one of the example projects for details.

Client-side API

  • ch-recv is a core.async channel that'll receive event-msgs
  • chsk-send! is a (fn [event & [?timeout-ms ?cb-fn]]) for standard client>server req>resp calls

Server-side API

  • ch-recv is a cadre.async channel that'll receive consequence-msgs
  • chsk-transport! is a (fn [user-id event]) for async server>user Button calls

===============

Term Form
event [<ev-id> <?ev-data>], e.g. [:my-app/some-req {:data "data"}]
server event-msg {:keys [upshot id ?information send-fn ?reply-fn uid ring-req client-id]}
customer event-msg {:keys [issue id ?data ship-fn]}
<ev-id> A namespaced keyword like :my-app/some-req
<?ev-data> An optional arbitrary edn value like {:data "information"}
:ring-req Ring map for Ajax request or WebSocket's initial handshake asking
:?answer-fn Present only when customer requested a reply

Summary

  • So clients can use chsk-ship! to send eventsouth to the server and optionally asking a reply with timeout
  • The server can likewise apply chsk-send! to send effectsouthward to all the clients (browser tabs, devices, etc.) of a item continued user by his/her user-id
  • The server tin can besides use an event-msg's ?reply-fn to respond to a particular client event using an arbitrary edn value

It'south worth noting that the server>user button (chsk-send! <user-id> <event>) takes a mandatory user-id argument. See the FAQ later on for more than info.

Ajax/Sente comparison: client>server

(jayq/ajax                                          ;                      Using the jayq wrapper around jQuery                    {:blazon                    :mail                    :url                                          "/some-url-on-server/"                                        :information                    {:proper name                                          "Rich Hickey"                                        :type                                          "Awesome"                    }                    :timeout                    8000                    :success                    (fn                    [content text-condition xhr]              (do-something!                    content))                    :fault                    (fn                    [xhr text-condition] (error-handler!))})  (chsk-transport!                                          ;                      Using Sente                    [:some/request-id                    {:name                                          "Rich Hickey"                                        :blazon                                          "Awesome"                    }]                                          ;                      Consequence                    8000                                          ;                      Timeout                                          ;; Optional callback:                    (fn                    [reply]                                          ;                      Reply is arbitrary Clojure data                    (if                    (sente/cb-success?                    reply)                                          ;                      Checks for :chsk/closed, :chsk/timeout, :chsk/error                    (do-something!                    reply)       (fault-handler!))))

Some important differences to note:

  • The Ajax request is irksome to initialize, and bulky (HTTP overhead)
  • The Sente asking is pre-initialized (usu. WebSocket), and lean (edn/Transit protocol)

Ajax/Sente comparison: server>user push

  • Ajax would require clumsy long-polling setup, and wouldn't hands support users connected with multiple clients simultaneously
  • Sente: (chsk-ship! "destination-user-id" [:some/alert-id <arb-clj-data-payload>])

Channel socket client land

Each time the channel socket customer's state changes, a client-side :chsk/country consequence will burn down that you tin watch for and handle like any other outcome.

The event form is [:chsk/land [<quondam-state-map> <new-state-map>]] with the following possible state map keys:

Key Value
:type e/o #{:auto :ws :ajax}
:open? Truthy iff chsk appears to be open up (continued) now
:ever-opened? Truthy iff chsk handshake has ever completed successfully
:first-open? Truthy iff chsk but completed get-go successful handshake
:uid User id provided past server on handshake, or nil
:csrf-token CSRF token provided by server on handshake, or nil
:handshake-information Arb user information provided by server on handshake
:final-ws-fault ?{:udt _ :ev <WebSocket-on-fault-event>}
:final-ws-shut ?{:udt _ :ev <WebSocket-on-close-event> :clean? _ :lawmaking _ :reason _}
:terminal-close ?{:udt _ :reason _}, with reason east/o #{nil :requested-disconnect :requested-reconnect :downgrading-ws-to-ajax :unexpected}

Example projects

Link Description
Official example Official Sente reference example, ever upward-to-date
@laforge49/sente-boot Instance using Sente v1.eleven.0, Boot (also works with Windows)
@laforge49/sente-kick-reagent Example using Sente v1.11.0, Boot, and Reagent
@tiensonqin/lymchat Example conversation app using React Native
@danielsz/system-websockets Client-side UI, login and wiring of components
@timothypratley/snakelake Multiplayer ophidian game with screencast walkthrough
@theasp/sente-nodejs-example Ref. example adapted for Node.js servers (Express, Domestic dog Fort), too as a node.js client
@ebellani/rug Web+mobile interface for a remmitance application
@danielsz/sente-organization Ref example adapted for @danielsz/arrangement
@danielsz/sente-kick Ref example adapted for boot
@seancorfield/om-sente ??
@tfoldi/data15-blackjack Multiplayer blackjack game with documented source code
@davidvujic/sente-with-reagent-and-re-frame Example code that combines Sente with Reagent and re-frame in a unmarried folio awarding
Your link here? PR's welcome!

FAQ

What is the user-id provided to the server>user push fn?

At that place's now as well a full user-id, client-id summary up here

For the server to push events, we demand a destination. Traditionally we might push button to a customer (e.g. browser tab). Just with modernistic rich web applications and the increasing utilize of multiple simultaneous devices (tablets, mobiles, etc.) - the value of a client push is diminishing. Y'all'll ofttimes see applications (even by Google) struggling to deal with these cases.

Sente offers an out-the-box solution by pulling the concept of identity 1 level higher and dealing with unique users rather than clients. What constitutes a user is entirely at the discretion of each application:

  • Each user-id may have aught or more continued clients at any given time
  • Each user-id may survive across clients (browser tabs, devices), and sessions

To give a user an identity, either gear up the user'due south :uid Band session central OR supply a :user-id-fn (takes request, returns an identity string) to the make-channel-socket! constructor.

If you want a elementary per-session identity, generate a random uuid. If you want an identity that persists beyond sessions, endeavour employ something with semantic meaning that you may already have similar a database-generated user-id, a login email address, a secure URL fragment, etc.

Note that user-ids are used just for server>user push. customer>server requests don't have a user-id.

Every bit of Sente v0.13.0+ it's also possible to send events to :sente/all-users-without-uid.

How practice I integrate Sente with my usual login/auth procedure?

This is trivially easy as of Sente v0.13.0+. Please meet one of the case projects for details.

Will Sente work with Reactjs/Reagent/Om/Pedestel/etc.?

Sure! Sente'southward just a customer<->server comms mechanism so it'll work with whatever view/rendering approach you'd like.

I have a strong preference for Reagent myself, so would recommend checking that out beginning if you're still evaluating options.

What if I need to apply JSON, XML, raw strings, etc.?

As of v1, Sente uses an extensible customer<->server serialization mechanism. It uses edn by default since this usu. gives proficient functioning and doesn't require whatever external dependencies. The reference example project shows how you can plug in an culling de/serializer. In item, annotation that Sente ships with a Transit de/serializer that allows transmission or smart (automated) per-payload format selection.

How do I add together custom Transit read and write handlers?

To add custom handlers to the TransitPacker, pass them in equally writer-opts and reader-opts when creating a TransitPacker. These arguments are the same as the opts map you would pass directly to transit/writer. The code sample below shows how yous would exercise this to add a write handler to catechumen Joda-Time DateTime objects to Transit time objects.

(ns                    my-ns.app                    (:require                    [cognitect.transit                    :equally                    transit]             [taoensso.sente.packers.transit                    :as                    sente-transit])   (:import                    [org.joda.time DateTime ReadableInstant]))                                          ;; From https://increasinglyfunctional.com/2014/09/02/custom-transit-writers-clojure-joda-time.html                    (def                    joda-time-writer                    (transit/write-handler                    (constantly                                          "m"                    )     (fn                    [v] (->                    ^ReadableInstant v .getMillis))     (fn                    [five] (->                    ^ReadableInstant v .getMillis .toString))))  (def                    packer                    (sente-transit/->TransitPacker                    :json                    {:handlers                    {DateTime joda-fourth dimension-writer}} {}))

How exercise I route customer/server events?

However you lot like! If you don't take many events, a simple cond will probably do. Otherwise a multimethod dispatching against result ids works well (this is the approach taken in the reference example project).

Security: is there HTTPS back up?

Yup, information technology's automatic for both Ajax and WebSockets. If the page serving your JavaScript (ClojureScript) is running HTTPS, your Sente channel sockets will run over HTTPS and/or the WebSocket equivalent (WSS).

Security: CSRF protection?

This is important. Sente has support, and utilise is strongly recommended. You'll demand to utilize middleware like ring-anti-forgery or ring-defaults to generate and check CSRF codes. The band-ajax-post handler should be covered (i.e. protected).

Please see i of the example projects for a fully-baked case.

Pageload: How do I know when Sente is ready customer-side?

You'll want to listen on the receive channel for a [:chsk/state [_ {:outset-open? true}]] result. That'due south the point that the socket's been established.

How can server-side channel socket events alter a user's session?

Update: @danielsz has kindly provided a detailed case here.

Think that server-side issue-msgs are of the form {:ring-req _ :event _ :?reply-fn _}, so each server-side effect is accompanied past the relevant[*] Ring request.

For WebSocket events this is the initial Ring HTTP handshake request, for Ajax events it's just the Ring HTTP Ajax request.

The Ring request's :session central is an immutable value, so how do you modify a session in response to an upshot? You won't exist doing this often, simply it can be handy (e.g. for login/logout forms).

You've got two choices:

  1. Write any changes direct to your Band SessionStore (i.east. the mutable land that'south actually backing your sessions). Y'all'll need the relevant user'due south session key, which you can find under your Ring request's :cookies key. This is flexible, just requires that you lot know how+where your session data is existence stored.

  2. Just use regular HTTP Ajax requests for stuff that needs to modify sessions (like login/logout), since these will automatically go through the usual Ring session middleware and permit you modify a session with a simple {:status 200 :session <new-session>} response. This is the strategy the reference example takes.

Lifecycle management (component management/shutdown, etc.)

Using something like @stuartsierra/component or @palletops/leaven?

Almost of Sente's state is held internally to each channel socket (the map returned from client/server calls to brand-channel-socket!). The absence of global land makes things like testing, and running multiple concurrent connections easy. It likewise makes integration with your component management piece of cake.

The simply thing you may[1] desire to practice on component shutdown is stop any router loops that y'all've created to acceleration events to handlers. The customer/server side start-chsk-router! fns both return a (fn end []) that y'all can call to exercise this.

[ane] The cost of not doing this is actually negligible (a single parked go thread).

There's too a couple lifecycle libraries that include Sente components:

  1. @danielsz/organisation for apply with @stuartsierra/component
  2. @palletops/bakery for use with @palletops/leaven

How to debug/benchmark Sente at the protocol level?

@arichiardi has kindly provided notes on some of Sente's current implementation details here.

Whatsoever other questions?

If I've missed something hither, feel complimentary to open a GitHub upshot or pop me an email!

Contacting me / contributions

Please use the project's GitHub problems folio for all questions, ideas, etc. Pull requests welcome. Meet the projection's GitHub contributors page for a list of contributors.

Otherwise, you can reach me at Taoensso.com. Happy hacking!

- Peter Taoussanis

License

Distributed under the EPL v1.0 (same every bit Clojure).
Copyright © 2014-2020 Peter Taoussanis.

sente's People

sente's Bug

Add a simple, fully-working example equally lawmaking

Will try practice this in the adjacent 24-hour interval or two

how to broadcast to all connected clients?

Socket.io allows broadcast to every client, or to every client except the sender. I'm using Sente to broadcast race results in realtime to every connected browser - this feels difficult in the electric current API.

I think what I want is socket.io style channels in addition to the per user channels. I tin can faux this by using the "uid" as a aqueduct ID for now. Simply wanted to gather thoughts.

server>user push sends malformed events

I'm using 0.nine version of sente. When i do:

(chsk-transport! uid [:event/my-event some-data])

I'm getting "Malformed events" on the client and the following information in chrome dev tools WS-frames:
[[:event/my-event some-data]]

just other frames contain {:chsk/clj [:normal-issue/effect-name information]}

Do i demand to wrap my events in {:chsk/clj ... } too to push them to client?

Add unit tests

Whatsoever assistance would be much appreciated!

Farther develop example project (login, who'southward online, etc.)

Session: writable from within Sente'due south router loop?

So I know that I can read session properties inside Sente's router loop, but tin I write to it as well?

For example, how would I become about writing fn-that-writes-and-persists-a-session-property?

(defn                  event-msg-handler                  [{:every bit                  ev-msg                  :keys                  [ring-req event ?reply-fn]} _]   (let                  [session (:session                  ring-req)         uid     (:uid                  session)         [id information                  :equally                  ev] event]      (println                                      "Result: %s"                                    ev)     (match                  [id information]            [:myapp/event-10                  _] (fn-that-writes-and-persists-a-session-property                  session)                  :else                  (do                  (println                                      "Unmatched event: %s"                                    ev)         (when-not                  (:dummy-answer-fn?                  (meta                  ?reply-fn))           (?reply-fn                  {:umatched-issue-as-echoed-from-from-server                  ev}))))))

Basic auth interoperability

I'd need a manner to authenticate Sente workflow with basic auth (I'm using Friend). I tin't think of whatsoever clean way to do it, other than maybe digging into the handshake process, I guess.

Possible workaround would be just use class hallmark.

issues in continued-uids logic?

I call up there's a issues in the connected UID logic.

If a user opens upward multiple tabs to the same folio (creating multiple websocket connections with the same UID), when whatever one of the tabs closes, that UID is removed from the connected-uids atom; if you lot're using connected-uids to broadcast to some user prepare, the other clients will stop receiving messages.

Internally, sente probably needs to reference count the number of connections.

Not sure what to do with the reference example

How are we supposed to run the case? Is information technology supposed to spawn up a website which we tin can so visit?

Running the example projection fails

A freshly cloned repository fails the post-obit way:

                  ~/Downloads/git/sente/instance-project $ lein start-dev Reflection warning, cljx/cadre.clj:73:24 - reference to field getPath on java.lang.Object tin't exist resolved. Reflection warning, cljx/core.clj:73:24 - reference to field getPath tin can't exist resolved. Reflection warning, cljs/closure.clj:142:26 - call to method setDefineToIntegerLiteral on com.google.javascript.jscomp.CompilerOptions tin't be resolved (no such method). Reflection warning, cljs/closure.clj:142:26 - telephone call to method setDefineToIntegerLiteral on com.google.javascript.jscomp.CompilerOptions tin't be resolved (no such method). Reflection warning, cljs/closure.clj:164:17 - phone call to java.util.aught.ZipFile ctor tin't be resolved. Reflection alarm, cljs/closure.clj:165:18 - reference to field getName can't be resolved. Reflection alarm, cljs/closure.clj:229:41 - reference to field getFile can't exist resolved. Reflection warning, cljs/closure.clj:1018:70 - call to method findResources on java.lang.ClassLoader tin't exist resolved (no such method). Reflection warning, cljs/repl.clj:96:28 - call to method getBytes can't be resolved (target class is unknown). Reflection warning, cljs/repl.clj:178:22 - call to java.io.File ctor tin't be resolved. Reflection alarm, cljs/repl.clj:180:40 - reference to field getAbsolutePath on java.lang.Object can't be resolved. Reflection warning, cljs/repl.clj:180:40 - reference to field getAbsolutePath can't exist resolved. Reflection warning, cemerick/austin.clj:50:3 - call to method cease can't be resolved (target course is unknown). Reflection warning, cemerick/austin.clj:72:iii - reference to field getAddress can't be resolved. Reflection alarm, cemerick/austin.clj:72:three - reference to field getPort can't be resolved. Reflection alert, cemerick/austin.clj:77:14 - call to method getBytes tin can't exist resolved (target form is unknown). Reflection alert, cemerick/austin.clj:82:28 - call to method write on coffee.io.OutputStream can't exist resolved (argument types: unknown). Reflection warning, cemerick/austin.clj:168:47 - reference to field getRequestHeaders tin't be resolved. Reflection warning, cemerick/austin.clj:191:21 - call to method endsWith tin't be resolved (target course is unknown). Reflection alert, cemerick/austin.clj:429:23 - telephone call to method exec on java.lang.Runtime can't exist resolved (argument types: unknown). Reflection warning, cemerick/austin.clj:439:five - reference to field destroy on java.lang.Object tin can't be resolved. Reflection warning, cljs/repl/rhino.clj:36:five - telephone call to method evaluateReader on java.lang.Object can't be resolved (no such method). Reflection alert, cljs/repl/rhino.clj:32:5 - telephone call to method evaluateString on java.lang.Object tin can't be resolved (no such method). Reflection warning, cljs/repl/rhino.clj:42:52 - reference to field toString can't exist resolved. Reflection warning, cljs/repl/rhino.clj:42:67 - reference to field getStackTrace tin can't be resolved. Reflection warning, cljs/repl/rhinoceros.clj:45:iii - reference to field getScriptStackTrace tin't be resolved. Reflection warning, cljs/repl/rhino.clj:50:three - reference to field toString can't be resolved. Reflection alert, cljs/repl/rhinoceros.clj:91:38 - reference to field toString tin't be resolved. Reflection alert, cljs/repl/rhinoceros.clj:105:5 - phone call to static method putProperty on org.mozilla.javascript.ScriptableObject can't be resolved (statement types: unknown, coffee.lang.Cord, coffee.lang.Object). Reflection warning, cemerick/piggieback.clj:54:3 - phone call to static method putProperty on org.mozilla.javascript.ScriptableObject can't be resolved (argument types: unknown, java.lang.String, java.lang.Object). Reflection warning, alex_and_georges/debug_repl.clj:101:eight - reference to field getCause can't be resolved. Reflection alert, alex_and_georges/debug_repl.clj:102:viii - reference to field getCause can't exist resolved. Reflection warning, dynapath/defaults.clj:13:52 - reference to field getURLs can't be resolved. Reflection warning, dynapath/defaults.clj:27:28 - call to method addURL can't exist resolved (target class is unknown). Reflection warning, dynapath/util.clj:30:22 - reference to field getParent can't exist resolved. Reflection warning, alembic/still.clj:33:5 - reference to field close on java.lang.Object tin can't be resolved. Reflection warning, com/georgejahad/difform.clj:47:25 - reference to field operation can't be resolved. Reflection warning, com/georgejahad/difform.clj:48:44 - reference to field text can't be resolved. Reflection warning, com/georgejahad/difform.clj:48:37 - reference to field trim can't exist resolved. Exception in thread "main" java.lang.ExceptionInInitializerError, compiling:(/private/var/folders/j2/zvt92c1s39d_0kdmhrhbgtkm0000gn/T/grade-init7898637281734394767.clj:1:142)     at clojure.lang.Compiler.load(Compiler.java:7142)     at clojure.lang.Compiler.loadFile(Compiler.java:7086)     at clojure.principal$load_script.invoke(master.clj:274)     at clojure.main$init_opt.invoke(master.clj:279)     at clojure.main$initialize.invoke(principal.clj:307)     at clojure.main$null_opt.invoke(master.clj:342)     at clojure.chief$chief.doInvoke(main.clj:420)     at clojure.lang.RestFn.invoke(RestFn.java:421)     at clojure.lang.Var.invoke(Var.coffee:383)     at clojure.lang.AFn.applyToHelper(AFn.java:156)     at clojure.lang.Var.applyTo(Var.java:700)     at clojure.main.primary(main.java:37) Caused by: java.lang.ExceptionInInitializerError     at java.lang.Form.forName0(Native Method)     at java.lang.Class.forName(Course.java:270)     at clojure.lang.RT.loadClassForName(RT.java:2093)     at clojure.lang.RT.load(RT.coffee:430)     at clojure.lang.RT.load(RT.coffee:411)     at clojure.cadre$load$fn__5066.invoke(core.clj:5641)     at clojure.core$load.doInvoke(cadre.clj:5640)     at clojure.lang.RestFn.invoke(RestFn.java:408)     at clojure.core$load_one.invoke(core.clj:5446)     at clojure.cadre$load_lib$fn__5015.invoke(core.clj:5486)     at clojure.core$load_lib.doInvoke(cadre.clj:5485)     at clojure.lang.RestFn.applyTo(RestFn.coffee:142)     at clojure.core$apply.invoke(core.clj:626)     at clojure.core$load_libs.doInvoke(core.clj:5524)     at clojure.lang.RestFn.applyTo(RestFn.java:137)     at clojure.core$use.invoke(core.clj:626)     at clojure.core$crave.doInvoke(core.clj:5607)     at clojure.lang.RestFn.invoke(RestFn.java:408)     at clj_ns_browser.sdoc$loading__4920__auto__.invoke(sdoc.clj:9)     at clj_ns_browser.sdoc__init.load(Unknown Source)     at clj_ns_browser.sdoc__init.<clinit>(Unknown Source)     at java.lang.Class.forName0(Native Method)     at java.lang.Grade.forName(Class.java:270)     at clojure.lang.RT.loadClassForName(RT.java:2093)     at clojure.lang.RT.load(RT.java:430)     at clojure.lang.RT.load(RT.java:411)     at clojure.core$load$fn__5066.invoke(core.clj:5641)     at clojure.cadre$load.doInvoke(cadre.clj:5640)     at clojure.lang.RestFn.invoke(RestFn.java:408)     at clojure.core$load_one.invoke(core.clj:5446)     at clojure.core$load_lib$fn__5015.invoke(core.clj:5486)     at clojure.core$load_lib.doInvoke(core.clj:5485)     at clojure.lang.RestFn.applyTo(RestFn.coffee:142)     at clojure.core$use.invoke(core.clj:626)     at clojure.core$load_libs.doInvoke(cadre.clj:5524)     at clojure.lang.RestFn.applyTo(RestFn.java:137)     at clojure.core$utilise.invoke(cadre.clj:626)     at clojure.core$require.doInvoke(core.clj:5607)     at clojure.lang.RestFn.invoke(RestFn.java:512)     at user$eval4932.invoke(form-init7898637281734394767.clj:1)     at clojure.lang.Compiler.eval(Compiler.java:6703)     at clojure.lang.Compiler.eval(Compiler.java:6692)     at clojure.lang.Compiler.load(Compiler.java:7130)     ... 11 more than Caused by: java.lang.ExceptionInInitializerError, compiling:(clj_info/doc2map.clj:one:ane)     at clojure.lang.Compiler.load(Compiler.java:7142)     at clojure.lang.RT.loadResourceScript(RT.java:370)     at clojure.lang.RT.loadResourceScript(RT.coffee:361)     at clojure.lang.RT.load(RT.java:440)     at clojure.lang.RT.load(RT.coffee:411)     at clojure.core$load$fn__5066.invoke(core.clj:5641)     at clojure.cadre$load.doInvoke(cadre.clj:5640)     at clojure.lang.RestFn.invoke(RestFn.java:408)     at clojure.cadre$load_one.invoke(core.clj:5446)     at clojure.core$load_lib$fn__5015.invoke(core.clj:5486)     at clojure.core$load_lib.doInvoke(cadre.clj:5485)     at clojure.lang.RestFn.applyTo(RestFn.java:142)     at clojure.core$employ.invoke(core.clj:626)     at clojure.core$load_libs.doInvoke(cadre.clj:5524)     at clojure.lang.RestFn.applyTo(RestFn.java:137)     at clojure.cadre$utilize.invoke(core.clj:626)     at clojure.core$require.doInvoke(cadre.clj:5607)     at clojure.lang.RestFn.invoke(RestFn.java:2793)     at clj_ns_browser.browser$loading__4920__auto__.invoke(browser.clj:9)     at clj_ns_browser.browser__init.load(Unknown Source)     at clj_ns_browser.browser__init.<clinit>(Unknown Source)     ... 54 more Caused by: java.lang.ExceptionInInitializerError     at hiccup.core__init.load(Unknown Source)     at hiccup.core__init.<clinit>(Unknown Source)     at java.lang.Course.forName0(Native Method)     at coffee.lang.Class.forName(Course.java:270)     at clojure.lang.RT.loadClassForName(RT.coffee:2093)     at clojure.lang.RT.load(RT.java:430)     at clojure.lang.RT.load(RT.java:411)     at clojure.core$load$fn__5066.invoke(core.clj:5641)     at clojure.core$load.doInvoke(core.clj:5640)     at clojure.lang.RestFn.invoke(RestFn.java:408)     at clojure.core$load_one.invoke(core.clj:5446)     at clojure.core$load_lib$fn__5015.invoke(core.clj:5486)     at clojure.cadre$load_lib.doInvoke(core.clj:5485)     at clojure.lang.RestFn.applyTo(RestFn.java:142)     at clojure.core$apply.invoke(core.clj:626)     at clojure.core$load_libs.doInvoke(core.clj:5524)     at clojure.lang.RestFn.applyTo(RestFn.java:137)     at clojure.cadre$use.invoke(core.clj:626)     at clojure.core$require.doInvoke(cadre.clj:5607)     at clojure.lang.RestFn.invoke(RestFn.java:512)     at clj_info.doc2map$eval5991$loading__4958__auto____5992.invoke(doc2map.clj:9)     at clj_info.doc2map$eval5991.invoke(doc2map.clj:nine)     at clojure.lang.Compiler.eval(Compiler.java:6703)     at clojure.lang.Compiler.eval(Compiler.java:6692)     at clojure.lang.Compiler.load(Compiler.java:7130)     ... 74 more Acquired by: java.lang.IllegalStateException: *html-way* already refers to: #'hiccup.util/*html-fashion* in namespace: hiccup.compiler     at clojure.lang.Namespace.warnOrFailOnReplace(Namespace.coffee:88)     at clojure.lang.Namespace.intern(Namespace.java:72)     at clojure.lang.Var.intern(Var.java:158)     at clojure.lang.RT.var(RT.java:341)     at hiccup.cadre$html.<clinit>(cadre.clj:7)     ... 99 more than Mistake encountered performing chore 'repl' with profile(southward): 'default,dev' Subprocess failed                                  

I get pretty much the same stacktrace with lein build-once besides. Appears to be a problem with cljx, and then maybe this is a wrong place to submit information technology. I haven't used cljx before and I'm pretty new to CLJS in general.

Clarification for use of vars

I've been scratching my head as to why the chsk routes take vars instead of functions. Would you care to explain? Cheers.

(defroutes                  my-app                                      ;; <other stuff>                                      ;;; Add these two entries: --->                  (Go                                      "/chsk"                                    req (#'ring-ajax-get-or-ws-handshake                  req))                                      ;                    Note the #'                  (Mail                                      "/chsk"                                    req (#'ring-ajax-mail service                  req))                                      ;                    ''                  )

Sente in advanced compilation style

When compiled in advanced fashion, Sente doesn't fifty-fifty tries to establish WebSocket connection or long-polling ajax call. It falls back to ajax Mail service's.

Should i tweak something in cljsbuild configuration?

Sente Trips Up When Used In A Browser-Repl

Using a browser-connected repl, on the client side, when sente tries to establish a chsk channel, I experience a broken channel.

I become effectually this past including a custom chsk-url-fn. The tricky thing, I think, is that the browser-repl uses a different port (i default browser-repl is 9000, ii. austin's browser-repl port changes each session), than the running http-kit server (8090). So when brand-aqueduct-socket! calls chsk-url-fn (which is the default-chsk-url-fn), (encore/get-window-location) is returning the URL with the browser-repl'due south port. I'm not certain how you'd go around that if y'all're using a browser-repl, vs simply running in a plain browser runtime. Seems to come up downward to the behaviour of (.-location js/window) (in encore/become-window-location). Ie, in my browser-repl, invoking (.-location js/window) gives me "http://172.28.128.5:33283/347/repl/start?...".

                  cljs.user> (.-location js/window)                                                                                                                                                                                                      #<http://172.28.128.5:33283/347/repl/start?xpc=%7B%22cn%22%3A%222qJDPo7hur%22%2C%22tp%22%3Anull%2C%22osh%22%3Anull%2C%22ppu%22%3A%22http%3A%2F%2F172.28.128.5%3A8090%2Frobots.txt%22%2C%22lpu%22%3A%22http%3A%2F%2F172.28.128.5%3A33\  283%2Frobots.txt%22%7D>                                  

In that location's more context in this Google Group thread.

Rough getting started experience

I've been trying to wrap my head effectually your library, but I'm unable to get something useful going.

Here are some problems I ran into:

  • Some examples in the Readme are inconsistent with the current version (such equally the send-fn function taking ii arguments, non i)
  • At that place'south no full example showing how to communicate betwixt the server and client. When your docs say "what now? You're good to get!" I'chiliad thinking "how?".
  • When creating a socket, I go a bunch of bearding functions, which leads to errors like "Wrong number of args (ane) passed to: sente/make-channel-socket!/fn--14849", which aren't really helpful.
  • The Readme case that desctructures the map of functions gives them all different names, which makes debugging more cumbersome, since I now have to i) look upward what the part was really chosen, 2) wait up how to use that office.

It would exist easier to become started with sente if in that location was one fully contained re-create-pastable case in the Readme that got me a customer/server talking to each other.

All in all I was very excited about sente upon reading its pitch, but having tried to use it I am frustrated and experience stupid. I hope you can brand anything useful out of this feedback.

Optimization: minified client/server metadata format for production mode

:chsk/cb-uuid -> :c1, etc.

Non a huge savings, but every byte tin count on mobile devices.

Relax :uid requirement to Office[Request, UID]

It would be great if we could specify a :uid extraction function, rather than requiring that specific primal in the the session. I'd be happy to code this up if you lot like the feature. Thoughts?

Sente and good (functional) programming practice

Sente is an crawly library. As such, information technology volition be used in production-grade applications. Information technology is therefore important to provide guidelines regarding the integration of said library.

Common challenges are:

  • Brand it play along a setup and teardown strategy to attain fast development cycles.
  • Avoid tiptop-level def forms with runtime state. This leads to imperative code, and is decumbent to circadian dependencies.

The instance projection, while extremely useful to introduce the features of Sente, should be complemented with guidelines aiming to accost those challenges specifically.

Stuart Sierra has been advocating a workflow that works well and we may want to prefer its principles.

Two primal points from the README:

Large applications often consist of many stateful processes which must be started and stopped in a particular order. The component model makes those relationships explicit and declarative, instead of implicit in imperative code.

And:

Instead of having mutable land (atoms, refs, etc.) scattered throughout different namespaces, all the stateful parts of an awarding can exist gathered together.

I would like to open for word my implementation of a Sente component. It is working well for me, but I might take overlooked things or, worse, inadvertently introduced new problems.

(ns                  front end-cease.framework.components.channel-sockets                  (:require                  [com.stuartsierra.component                  :equally                  component]             [taoensso.sente                  :every bit                  sente]             [front-end.webapp.handler                  :refer                  [event-msg-handler]]))   (defrecord                  ChannelSockets                  [band-ajax-post ring-ajax-become-or-ws-handshake ch-chsk chsk-ship! chsk-router]                  component/Lifecycle                  (first                  [component]     (let                  [{:keys                  [ch-recv ship-fn ajax-post-fn ajax-go-or-ws-handshake-fn]}       (sente/make-channel-socket!                  {})]       (assoc                  component                  :ring-ajax-post                  ajax-post-fn                  :ring-ajax-get-or-ws-handshake                  ajax-go-or-ws-handshake-fn                  :ch-chsk                  ch-recv                  :chsk-send!                  send-fn                  :chsk-router                  (sente/starting time-chsk-router-loop!                  upshot-msg-handler ch-recv))))   (stop                  [component]     component))  (defn                  new-channel-sockets                  []   (map->ChannelSockets                  {}))

My development system looks like this:

(defn                  dev-system                  []   (component/system-map                  :sente                  (new-channel-sockets)                  :web                  (new-web-server                  (Integer.                  (env                  :http-port)) (env                  :trace-headers))))

One would start the system like this:

(def                  system) (change-var-root                  #'system (fn                  [_] (component/kickoff                  (dev-organisation))))

As a event, all the relevant sentefunctions are in the system map, allowing us to call chsk-send!, for example, from whatever namespace.

((:chsk-transport!                  (:sente                  (system-map))) (:uid                  session) [:myapp/account                  {:status                                      "OK"                  }])

Following this, the compojure routes expect like this;

                  (GET                                      "/chsk"                                    req ((:band-ajax-go-or-ws-handshake                  (:sente                  (system-map))) req))   (POST                                      "/chsk"                                    req ((:band-ajax-mail                  (:sente                  (organisation-map))) req)))

Thoughts?

Teething troubles

Apologies, probably user error. I cannot become a simple setup working. I've been through the docs for setting up server and client side and cannot get a (chsk-send! ...) from the client working.

(ns                  bom.cadre                  (:require-macros                  [cljs.cadre.friction match.macros                  :refer                  (match)]    [cljs.core.async.macros                  :as                  asyncm                  :refer                  (go                  go-loop)])   (:require                  [cljs.cadre.match]    [cljs.core.async                  :as                  async                  :refer                  (<!                  >! put! chan)]    [taoensso.sente                  :equally                  sente                  :refer                  (cb-success?)]    ))  (let                  [{:keys                  [chsk ch-recv send-fn]}       (sente/make-aqueduct-socket!                                      "/chsk"                                    {} {:blazon                  :car})]   (def                  chsk                  chsk)   (def                  ch-chsk                  ch-recv)   (def                  chsk-ship!                  transport-fn))  (chsk-send!                  [:chsk/ping                  {:data                                      "data"                  }]                  8000                  (fn                  [reply]     (if                  (cb-success?                  answer)       (.log                  js/console                                      "Success"                  )       (.log                  js/console                                      "Failure"                  ))))

results in:
image

[Perf] Buffer client>server events

This is far less important than buffering for server>user events (which we now have), but information technology'd still be a big efficiency win - esp. for Ajax + for bandwidth minimization on mobiles.

It'due south fifty-fifty possible to offer this for events with callbacks if we're prepared to let actual timeouts to vary by [0,<max-buffer-window-size>] ms.

UPDATE: Have started some piece of work for this on the dev co-operative. Shouldn't be hard to implement, just short on time atm.

event handler and blocking APIs

Hi

My server side outcome handler is doing some blocking API calls and thus making the router loop block other requests.

Yous run into any potential issues with wrapping the handler in another get block inside the router loop? Seems to work fine but haven't tested thoroughly.

                  @@ -871,11 +871,13 @@            (let [[five p] (async/alts! [ch ctrl-ch])]              (if (identical? p ctrl-ch) ::terminate                (let [event-msg v] -                (try -                  (timbre/tracef "Event-msg: %s" event-msg) -                  (do (event-msg-handler effect-msg ch) nil) -                  (take hold of Throwable t -                    (timbre/errorf t "Chsk-router-loop treatment error: %south" event-msg)))))) +                (go  +                  (try +                    (timbre/tracef "Consequence-msg: %s" upshot-msg) +                    (practice (event-msg-handler issue-msg ch) nil) +                    (catch Throwable t +                      (timbre/errorf t "Chsk-router-loop treatment mistake: %s" outcome-msg)))) +                null)))                                  

Sending params on socket connect

Is there a way to send params from the customer when calling make-aqueduct-socket! or chsk-reconnect!? This gets passed to the server'southward user-id-fn to set the user-id. Information technology's not feasible to store the uid in the session in my instance, because I want to support multiple users across unlike browser windows, and the session persists beyond all windows.

Unable to run example due to cljx

I tin't get the example to run. I tried installing emacs in order to attempt to one thousand-10 cider into information technology... no joy.

Sente initialization and Safari Password manager

Thanks for a wonderful library. I'm very new to ClojureScript/Javascript earth and your library makes things much easier.

Writing a simple single page application I've got stuck with strange upshot with Safari browser on Bone 10. My application works as follows - I take a login folio, which redirects to application, if login was successful. When application starts it tries to setup ws/ajax connection to server exactly the same way as shown in sente example project. When I utilize Chrome/Firefox to access application, everything works as intended. But Safari shows me a popup window offering to save username/password, and application is loaded in groundwork. But if application gets loaded in background, it looks similar consequence-treatment loop is not started, though I see on server side, thats connection is established and ws-pings are coming.

Is at that place a way to figure out why it happens?

Thanks,
Eugene

Csrf protection in 0.14.1

Readme.md
"Server-side: you lot'll need to use middleware like ring-anti-forgery to generate and bank check CSRF codes. The ring-ajax-mail service handler should be covered (i.east. protected).
Client-side: yous'll demand to laissez passer the folio's csrf code to the make-channel-socket! constructor."

Does the customer side function even so hold, it seems to be delivered from the server on handshake?

Assertion failed on `receive-buffered-evs!` with ClojureScript 0.0-2261

Clarification

Running the example projection and clicking the chsk-ship! (with respond) push button throws a validation error on taoensso.sente/receive-buffered-evs!.

Steps to reproduce

  • Update the dependencies in example-project/projection.clj for clojurescript to version 0.0-2261 and for cljsbuild to 1.0.3.
  • Follow the instructions on my_app.cljx to build and run the case application
  • Open the console of your browser
  • Click on the button chsk-send! (with reply)

NB I know the higher up steps could sound insulting to the project maintainer but they're meant for everybody else. :)

Notes

I've tried to dig further into the problem but I'm unable to use the checkouts characteristic of Leinengen with the instance project, and then the whole build wheel would be also long for me. I never used this characteristic earlier, then maybe I'm doing it wrong.

Add per-request-tunable smart upshot batching (5-high throughput optimization)

Allow client-side `:has-uid?` opt to be toggled after chsk creation

Error bulletin from crosspagechannel.js

I'm just getting started with this library and so please bear with me if I'm doing something completely wrong.

Following the README instruction I can see handshake event in the browser panel, forth preceded by CSRF warning, which is ok at this phase. Only even before that I see this error message:

                  TypeError: 'undefined' is not an object (evaluating 'peerUri.setParameterValue')                                  

Logon/logoff instance

Hi there!

It's non completely articulate to me how to best implement logon/logoff functionality with Sente. I've implemented something, and it works. But it seems somewhat artificial.

So could y'all please provide us with your recommendation on how to implement logon -
I guess via former-school async AJAX resp-req, and so plant websocket via sente/starting time-chsk-router-loop! ?

And then while you're at information technology, how to logoff (invalidate session, close socket etc.)?

Thanks for all your peachy work! :)

Best,
Henrik

Verify that new Ajax impl. is working

Just swapped out the Ajax mechanism to avoid an external dependency. Since I'chiliad running a unlike version in prod, oasis't confirmed yet that this public release behaves every bit expected under Ajax mode.

Optimization: compress edn payloads

Should exist easy to automatically+transparently compress edn strings that are long enough to do good. This'll assistance cutting down on information transfer (esp. useful for mobile clients, etc.)

Server-side compression could be TTL-memoized to go acceptable circulate performance even for very large payloads.

Q: How to close all open sockets / connections?

I'chiliad working on migrating some lifecycle lawmaking, where when a server shuts downwardly I shutdown all open up sockets from the server side. Is there some message I could send through the channel that would strength a disconnection, or is there some other mode to exercise this through the electric current sente API?

Thanks Peter!

repl-friendly development workflow

Hi, thanks for making sente.

Could y'all recommend a workflow that works across REPL re-evaluations? My current solution is wrapping the initialization in a defonce:

(defonce                  sente-init                  (do                  (let                  [{:keys                  [ch-recv ship-fn ajax-postal service-fn ajax-become-or-ws-handshake-fn                     connected-uids]}             (sente/make-aqueduct-socket!                  {})]         (def                  band-ajax-post                  ajax-post-fn)         (def                  ring-ajax-get-or-ws-handshake                  ajax-get-or-ws-handshake-fn)         (def                  ch-chsk                  ch-recv)                                      ;                    ChannelSocket'south receive channel                  (def                  chsk-ship!                  send-fn)                                      ;                    ChannelSocket's send API fn                  (def                  connected-uids                  continued-uids)                                      ;                    Watchable, read-only atom                  )))

which prevents re-initialization everytime I reload the namespace. Then I apply a var instead of the function name for my consequence handler.

(defonce                  chsk-router                  (sente/starting time-chsk-router-loop!                  #'event-msg-handler ch-chsk))

If this good? I could create a pull asking on the example projection if that would help.

Stack trace in the console (possibly related with continued uid update)

Running Sente 0.12.0-SNAPSHOT, I am seeing this on the console when I reload my app in the browser. It has no adverse issue that I tin can run across, merely this stack trace. I thought you might desire to know.

Wed Apr                  30                  07:12:15                  IDT                  2014                  [worker-four] Mistake - on close handler java.lang.ClassCastException: clojure.lang.PersistentArrayMap cannot be cast to clojure.lang.IPersistentSet     at clojure.core$disj.invoke(cadre.clj:1449)     at taoensso.sente$make_channel_socket_BANG_$upd_connected_uid_BANG___17812.invoke(sente.clj:261)     at taoensso.sente$make_channel_socket_BANG_$fn__18022$fn__18051.invoke(sente.clj:402)     at org.httpkit.server.AsyncChannel.onClose(AsyncChannel.java:188)     at org.httpkit.server.RingHandler$ane.run(RingHandler.java:217)     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)     at java.util.concurrent.FutureTask.run(FutureTask.java:262)     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)     at java.lang.Thread.run(Thread.java:744)

Improve docs

Add support for other Clojure-side web servers (i.e. likewise http-kit)

This shouldn't be too hard (at that place's only a modest surface area that actually interacts with http-kit) simply will require potential servers to expose a sensible API that we can utilize.

Don't have a lot of time to await into this myself atm, and then help would be welcome:

  • What server(due south) should nosotros target? This might be a useful starting point?
  • What API does each expose?
  • Suggested abstraction to encapsulate whatever API differences?

Otherwise whatever other thoughts/suggestions very welcome! Feel free to ping me with any questions :-)

Cnt dcphr chsk

I'm trying to follow the Sente tutorial and can't make much sense of the needlessly brief function names. ch-recv is like shooting fish in a barrel to guess, chsk and ch-chsk are not. Consider using
more descriptive names and documenting what said functions practise.

Why does sente restrict ws communication to same-domain?

I was hoping to utilise sente to provide spider web-socket access to a server from arbitrary external domains, not obviously an unreasonable utilise instance (chat server for example). Could there be a workaround?

Add step-by-pace tutorial, clean up docs (esp. w/respect user ids)

Another example for Light Table users?

Hullo, thanks for making sente.

What do you think nearly providing another example projection without cljx?
It is considering I am using the Calorie-free Table for coding and information technology doesn't handle cljx project nicely..

If you like this idea, please check my sample lawmaking .
This is a port of an ancient app for socket.io (maybe) and has clj and cljx codes separately.

I am thinking Information technology would exist groovy if this tin help LT users to endeavour sente!

my project

Replacing cadre.async

I'm investigating Sente but don't want to use core.async (it is non especially efficient for my workload). Instead, I'd similar to use or integrate Meltdown.

Any thoughts on if this should be possible/easy, and if I should contribute this to Sente
vs Meltdown itself?

Avoiding (too) early usage of channels.

I have clojurescript lawmaking that runs at pageload time. It might try to utilize a aqueduct as well early.
The console says:

                  Chsk send against airtight chsk.                                  

How do I avoid that? How do I cheque when we're all set and gear up to become?

Outcome loop on client side: optional arguments?

Hi Peter,

I'm initializing Sente's client-side event loop in an Om component, and I would like to pass additional arguments to the event handler.

And then I rewrite the upshot loop like and then:

(defn                  outcome-loop                                      "Handle inbound events."                                    [owner]   (go-loop                  []      (let                  [[op arg] (<!                  ch-chsk)]       (consequence-handler                  op arg owner)       (recur))))

This seems to work merely fine, but I was a bit worried that I might miss something, compared to what you wrote with starting time-chsk-router-loop! which seems to do more.

(defn                  start-chsk-router-loop!                  [upshot-handler ch]   (let                  [ctrl-ch (chan)]     (go-loop                  []       (allow                  [[v p] (async/alts!                  [ch ctrl-ch])]         (if                  (identical?                  p ctrl-ch)                  ::end                  (let                  [[id information                  :as                  result] five]                                      ;; Provide ch to handler to allow event injection back into loop:                  (outcome-handler                  event ch)                                      ;                    Permit errors to throw                  (recur)))))     (fn                  stop! [] (async/shut!                  ctrl-ch))))

Is the issue-loop I wrote satisfactory or would it be improve to enhance the start-chsk-router-loop! function with optional arguments?

Thank you!

ring-anti-forgery primal change

For band-anti-forgery 1.0.0, I plan on changing the session key from:

to a namespaced keyword:

:ring.middleware.anti-forgery/anti-forgery-token

H12 Timeouts on Heroku with Sente

Hey @ptaoussanis,

I recently upgraded to Sente 0.14.1 (from 0.12.0), and, suspiciously, started seeing a bunch of H12 - Application Timeouts on my Heroku app.

I've also noticed a number of Idle Connection warnings likewise.

Could something take changed between those versions wrt the default continue-alive settings? Also, is there some way to tune these settings? I believe that Heroku has a thirty 2d timeout on connections, and keep-alives need to be sent within 55 seconds. I'm not certain if these errors were coming from websocket connections or long-polling connections.

Anyhow, this is out of my wheelhouse, simply it's certainly something that merely started after a recent batch of upgrades. Would love your advice! Permit me know if I can go you any further info.

Decomplect -- run into jetty9-websockets-async

At the hazard of sounding rude, I desire to signal out to y'all the reasons why I will not be using this library. You're a really sharp guy, and rather than keep this feedback to myself, sharing it makes it more likely that we will agree on implementation in the futurity so we can both benefit. Nosotros want the same things, and then why not talk about them?

Beginning, you lot've mandated shipping edn. What if my frontend team writes JS and we want to send JSON? What if we're high-throughput and are actually aircraft BSON? Why can't I pick my serialization mechanism?

Where are my go-loops? Yous first go-loops on channels as in ch-pull-ajax-hx-chs, but yous don't hand them back to me. What if I am trying to test my code and want to verify that the processing has stopped after I have closed my aqueduct?

ajax-post-fn and ajax-go-or-ws-handshake-fn don't vest in the same place. These are different scenarios and should be treated as such.

Why can't I specify my own error treatment? Why are you logging for me? I should be able to encounter successes/failures on channels and be able to make the decision myself.

Why is the cljs customer related to the server at all? Reconnect logic in javascript is different than reconnect logic in java and is only relevant on the client side.

You are mandating hallmark past using ring sessions. What if I desire to generate random urls and apply those? In fact, this is what the most contempo application I'grand working on needs to do.

You explicit recommend placing the resulting channels and functions equally top level vars. This is terrible for testing -- where are your tests!

The overarching theme is that you are doing way too many things and making them not-configurable. Contrast this with my library:
https://github.com/ToBeReplaced/jetty9-websockets-async

It's less than a quarter of the size of your code base, even so is entirely configurable in all of the above ways. Moreover, I simply return a javax.http.servlet.HttpServlet. This means that my libarary will work with whatever java server with servlets. Jetty/Tomcat/any. 100% test coverage. There's still a fiddling bit of piece of work to exercise to open up a few more than places (ex. passing the client disconnect message), only the place to do that is clear and won't disrupt anyone by design.

Equally a final comment, yous demand to get your dependencies under control -- Why on globe is sente bringing in a redis client? I would not be able to bring this in to a product surroundings. For reference, lein deps :tree prints out:

                  WARNING!!! version ranges institute for: [com.keminglabs/cljx "0.3.two"] -> [org.clojars.trptcolin/sjacket "0.1.0.3"] -> [org.clojure/clojure "[1.3.0,)"] Consider using [com.keminglabs/cljx "0.3.two" :exclusions [org.clojure/clojure]]. [com.keminglabs/cljx "0.iii.2"] -> [org.clojars.trptcolin/sjacket "0.1.0.3"] -> [net.cgrand/regex "1.one.0"] -> [org.clojure/clojure "[1.2.0,)"] Consider using [com.keminglabs/cljx "0.3.2" :exclusions [org.clojure/clojure]]. [com.keminglabs/cljx "0.3.two"] -> [org.clojars.trptcolin/sjacket "0.1.0.3"] -> [net.cgrand/parsley "0.9.1"] -> [org.clojure/clojure "[1.ii.0,)"] Consider using [com.keminglabs/cljx "0.3.ii" :exclusions [org.clojure/clojure]]. [com.keminglabs/cljx "0.3.2"] -> [org.clojars.trptcolin/sjacket "0.1.0.3"] -> [net.cgrand/parsley "0.9.ane"] -> [net.cgrand/regex "one.i.0"] -> [org.clojure/clojure "[1.two.0,)"] Consider using [com.keminglabs/cljx "0.iii.two" :exclusions [org.clojure/clojure]]. [com.taoensso/timbre "3.i.0"] -> [com.taoensso/encore "0.8.0"] -> [com.keminglabs/cljx "0.three.ii"] -> [org.clojars.trptcolin/sjacket "0.1.0.3"] -> [org.clojure/clojure "[1.3.0,)"] Consider using [com.taoensso/timbre "3.1.0" :exclusions [org.clojure/clojure]]. [com.taoensso/timbre "three.1.0"] -> [com.taoensso/encore "0.8.0"] -> [com.keminglabs/cljx "0.3.two"] -> [org.clojars.trptcolin/sjacket "0.1.0.3"] -> [net.cgrand/regex "1.1.0"] -> [org.clojure/clojure "[ane.two.0,)"] Consider using [com.taoensso/timbre "3.1.0" :exclusions [org.clojure/clojure]]. [com.taoensso/timbre "three.1.0"] -> [com.taoensso/encore "0.8.0"] -> [com.keminglabs/cljx "0.3.two"] -> [org.clojars.trptcolin/sjacket "0.1.0.3"] -> [net.cgrand/parsley "0.9.1"] -> [org.clojure/clojure "[1.2.0,)"] Consider using [com.taoensso/timbre "3.1.0" :exclusions [org.clojure/clojure]]. [com.taoensso/timbre "three.1.0"] -> [com.taoensso/encore "0.viii.0"] -> [com.keminglabs/cljx "0.iii.two"] -> [org.clojars.trptcolin/sjacket "0.one.0.iii"] -> [net.cgrand/parsley "0.ix.one"] -> [net.cgrand/regex "1.ane.0"] -> [org.clojure/clojure "[i.2.0,)"] Consider using [com.taoensso/timbre "3.1.0" :exclusions [org.clojure/clojure]].  Mayhap confusing dependencies establish: [com.taoensso/timbre "three.ane.0"] -> [com.draines/postal "1.11.ane"] -> [commons-codec "ane.7"]  overrides [com.taoensso/timbre "3.one.0"] -> [com.taoensso/carmine "two.four.6"] -> [commons-codec "1.9"]  Consider using these exclusions: [com.taoensso/timbre "3.1.0" :exclusions [eatables-codec]]   [clojure-consummate "0.2.iii" :exclusions [[org.clojure/clojure]]]  [com.cemerick/austin "0.1.4"]    [com.cemerick/piggieback "0.1.three"]  [com.cemerick/clojurescript.test "0.2.2"]  [com.keminglabs/cljx "0.3.2"]    [org.clojars.trptcolin/sjacket "0.1.0.3"]      [net.cgrand/parsley "0.9.1"]      [net.cgrand/regex "1.1.0"]    [org.clojure/core.lucifer "0.ii.0"]    [watchtower "0.1.ane"]  [com.taoensso/encore "0.ix.2"]  [com.taoensso/timbre "3.1.0"]    [com.draines/postal "one.xi.1"]      [commons-codec "i.7"]      [javax.mail/mail service "1.four.4" :exclusions [[javax.activation/activation]]]    [com.taoensso/carmine "ii.4.6"]      [commons-pool "1.6"]      [org.clojure/tools.macro "0.1.five"]    [com.taoensso/nippy "ii.5.ii"]      [org.iq80.snappy/snappy "0.3"]      [org.tukaani/xz "1.4"]    [io.aviso/pretty "0.1.eight"]    [org.clojure/data.fressian "0.2.0"]      [org.fressian/fressian "0.6.3"]    [org.clojure/tools.logging "0.2.6"]    [org.xerial.snappy/snappy-java "1.1.i-M1"]  [expectations "1.4.56"]    [erajure "0.0.3"]      [org.mockito/mockito-all "1.8.0"]    [junit "four.8.1"]  [http-kit "two.one.17"]  [org.clojure/clojure "ane.vi.0-beta1"]  [org.clojure/clojurescript "0.0-2173"]    [com.google.javascript/closure-compiler "v20131014"]      [args4j "2.0.16"]      [com.google.lawmaking.findbugs/jsr305 "i.three.9"]      [com.google.guava/guava "15.0"]      [com.google.protobuf/protobuf-java "ii.4.ane"]      [org.json/json "20090211"]    [org.clojure/data.json "0.2.3"]    [org.clojure/google-closure-library "0.0-20130212-95c19e7f0f5f"]      [org.clojure/google-closure-library-third-party "0.0-20130212-95c19e7f0f5f"]    [org.mozilla/rhino "1.7R4"]  [org.clojure/core.async "0.1.278.0-76b25b-alpha"]  [org.clojure/tools.nrepl "0.two.3" :exclusions [[org.clojure/clojure]]]  [org.clojure/tools.reader "0.eight.3"]  [reiddraper/simple-bank check "0.5.6"]                                  

What exercise the chsk/uidport-close and chsk/uidport-open up events mean?

Hi,

I'm really very happy with Sente, thanks. Release 0.13.0 made a huge departure to my awarding. I upgraded to 0.14.1 today, everything still works so no worries there :-)

This, I suppose, is a documentation issue...

I come across yous've added two events: chsk/uidport-close and chsk/uidport-open up. I put a couple of handlers in place for them and was thinking that they might be handy to clean up some resources when the connectedness was closed. Information technology'southward non working out the way I'd expected. If I re-draw the page in the browser I'll get an chsk/uidport-close followed by a chsk/uidport-open up with the same session-id. It's nearly as though it's dropping then re-establishing the connection. If this is a reconnection and so I definitely don't want to clean up after the close.

What practise these events actually mean at Sente's level and at the application level? When should they exist used? When should they non be used? Your thoughts on this would be profoundly appreciated.

Add optional support for JSON instead of edn

Edn can occasionally be too slow for some applications. In other cases apps may demand to interface with pre-existing JSON services, etc.

Edn's a sensible default, merely information technology'd be nice to offer optional JSON chsks.

Pull-requests welcome!

Login/logout once again

Hi,

I have Sente working with my application. Things are looking very good. Well done!

I've been trying to get my head wrapped around the login thing. The basics are very straight forward, but you might want to move some of the information in event 17 into the docs.

I think the way things work at present is that when you establish a chsk connection you take to know the uid that will be associated with it. Then y'all've established a 1-ane relation betwixt session and chsk connexion.

I desire to do something a piffling different than this. I'k edifice an application where I'd like to open a chsk channel to an anonymous user. In that location's a fair flake of stuff an anonymous user is allowed to practise. At some point, I'd like the user to exist able to sign in, so they tin exercise more stuff. Sometime later the user can sign out, but still exist able to deed as an anonymous user.

I mode that I tin think of to exercise this is to create a new channel whenever the user state changes. Piece of cake enough, I tin do this past re-loading the page on sign in/off. But this is a unmarried page app. If I do this I'll be effectively starting the application from it's initial state on sign in/off.

Another mode would be to have sign in/off trigger disconnect and reconnect. I'm a picayune leary of this. Is this a 'reliable' option in Sente? Or is Sente depending on a reload to clean stuff upwards?

Another fashion is to layer my ain channel managemet stuff on top of Sente's. In this case every customer would have a unique 'user' id (client id actually) that persisted beyond sign in and out. The new user direction layer would practice essentially what you're doing already, mapping some 'higher level' user id to the 'lower level' user/customer id. As I write this I'thousand starting to lean towards this style of doing it.

This awarding is brand new, I tin can do pretty much annihilation I want with Sente and user management.

I hope that made sense.

Any suggestions you lot might have would be profoundly appreciated.

Graceful shutdown

When I restart http-kit in my evolution process after having called `sente/brand-aqueduct-socket!, I see an fault on STDOUT.

ri                  Mar                  28                  22                  :                  44                  :                  12                  IDT                  2014                  [nREPL-worker-                  5]                  ERROR                  -                  increase                  :queue-size                  if                  this                  happens often                  java.util.concurrent.RejectedExecutionException                  :                  Job                  java.util.concurrent.FutureTask                  @122c00b3                  rejected from                  java.util.concurrent.ThreadPoolExecutor                  @6e1f117[Terminated, puddle size                  =                  0, agile threads                  =                  0, queued tasks                  =                  0, completed tasks                  =                  7]

How should I go about gracefully shutting down all channel sockets with sente?

Svelte shutdown functionality volition also be handy when componentizing sente or integrating it in Stuart Sierra's reloaded workflow.

Carol Sente Will Not Run Again

Source: https://githubhelp.com/ptaoussanis/sente

0 Response to "Carol Sente Will Not Run Again"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel