Provides a common interface for WebSockets and HTTP streaming.


(as-channel request & options)

Converts the current ring request in to an asynchronous channel.

The type of channel created depends on the request - if the request is a Websocket upgrade request (the :websocket? key is true), a Websocket channel will be created. Otherwise, an HTTP stream channel is created. You interact with both channel types using the other functions in this namespace, and through callbacks in options.

The callbacks common to both channel types are:

  • :on-open - (fn [ch] ...) - called when the channel is available for sending. Will only be invoked once.
  • :on-error - (fn [ch throwable] ...) - Called for any error that occurs in relation to the channel. If the error requires the channel to be closed, :on-close will also be invoked. To handle send! errors separately, provide it a completion callback.
  • :on-close - (fn [ch {:keys [code reason]}] ...) - called for any close, including a call to close, but will only be invoked once. ch will already be closed by the time this is invoked.

code and reason will be the numeric closure code and text reason, respectively, if the channel is a WebSocket (see Both will be nil for HTTP streams.

If the channel is a Websocket, the following callback is also used:

  • :on-message - (fn [ch message] ...) - Called for each message from the client. message will be a String or byte[]

You can also specify a :timeout option, that will cause a Websocket to be closed if idle more than the timeout, or an HTTP stream to be closed if open more than the timeout. This means that once opened, an HTTP stream will be closed after :timeout elapses, regardless of activity. It defaults to 0 (no timeout), and is in milliseconds.

When the ring handler is called during a WebSocket upgrade request, any changes to the session in the response map are applied, and any headers from the response map are included in the upgrade response.

Returns a ring response map, at least the :body of which must be returned in the response map from the calling ring handler.


(close ch)

Gracefully close the channel.

This will trigger the :on-close callback if one is registered with as-channel.


(open? ch)

Is the channel open?


(originating-request ch)

Returns the request map for the request that initiated the channel.


(send! ch message & options)

Send a message to the channel, asynchronously.

message can either be a String, File, InputStream, ISeq, byte[], or map. If it is a String, it will be encoded to the character set of the response for HTTP streams, and as UTF-8 for WebSockets. Files and InputStreams will be sent as up to 16k chunks (each chunk being a byte[] message for WebSockets). Each item in an ISeq will pass through send!, and can be any of the valid message types.

If message is a map, its :body entry must be one of the other valid message types, and its :status and :headers entries will be used to override the status or headers returned from the handler that called as-channel for HTTP streams. A map is only a valid message on the first send to an HTTP stream channel - an exception is thrown if it is passed on a subsequent send or passed to a WebSocket channel.

The following options are supported [default]:

  • :close? - if true, the channel will be closed when the send completes. Setting this to true on the first send to an HTTP stream channel will cause it to behave like a standard HTTP response, and not chunk the response. [false]
  • :on-success - (fn [] ...) - called when the send attempt has completed successfully. If this callback throws an exception, it will be reported to the as-channel :on-error callback [nil]
  • :on-error - (fn [throwable] ...) - Called when an error occurs on the send. If the error requires the channel to be closed, the as-channel :on-close callback will also be invoked. If this callback throws an exception, it will be reported to the as-channel :on-error callback [#(throw %)]

Returns nil if the channel is closed when the send is initiated, true otherwise. If the channel is already closed, :on-success won’t be invoked.