Ring is an abstraction layer for building HTTP server applications in Clojure.
The specification is divided into two parts; a synchronous API, and an asynchronous API. The synchronous API is simpler, but the asynchronous API can be more performant.
Ring is defined in terms of handlers, middleware, adapters, request maps and response maps, each of which are described below.
Ring handlers constitute the core logic of the web application. Handlers are implemented as Clojure functions.
A synchronous handler takes 1 argument, a request map, and returns a response map.
(fn [request] response)
Ring middlware augment the functionality of handlers. Middleware is implemented as higher-order functions that take one or more handlers and configuration options as arguments and return a new handler with the desired additional behavior.
Ring adapters are side-effectful functions that take a handler and a map of options as arguments, and when invoked start a HTTP server.
(run-adapter handler options)
Once invoked, adapters will receive HTTP requests, parse them to construct a request map, and then invoke their handler with this request map as an argument. Once the handler response with a response map, the adapter will use it to construct and send an HTTP response to the client.
A Ring request map represents a HTTP request, and contains the following keys. Any key not marked as required may be omitted. Keys marked as deprecated are there only for backward compatibility with earlier versions of the specification.
Key | Type | Required | Deprecated |
---|---|---|---|
:body |
java.io.InputStream |
||
:character-encoding |
String |
Yes | |
:content-length |
String |
Yes | |
:content-type |
String |
Yes | |
:headers |
{String String} |
Yes | |
:protocol |
String |
Yes | |
:query-string |
String |
||
:remote-addr |
String |
Yes | |
:request-method |
Keyword |
Yes | |
:scheme |
Keyword |
Yes | |
:server-name |
String |
Yes | |
:server-port |
Integer |
Yes | |
:ssl-client-cert |
java.security.cert.X509Certificate |
||
:uri |
String |
Yes |
An InputStream
for the request body, if one is present.
Equivalent to the character encoding specified in the Content-Type
header. Deprecated key.
Equivalent to the Content-Length
header, converted to an integer.
Deprecated key.
Equivalent to the media type in the Content-Type
header. Deprecated
key.
A Clojure map of lowercased header name strings to corresponding header value strings.
Where there are multiple headers with the same name, the adapter must
concatenate the values into a single string, using the ASCII ,
character as a delimiter.
The exception to this is the cookie
header, which should instead use
the ASCII ;
character as a delimiter.
The protocol the request was made with, e.g. "HTTP/1.1".
The query segment of the URI in the HTTP request. This includes
everything after the ?
character, but excludes the ?
itself.
The IP address of the client or the last proxy that sent the request.
The HTTP request method. Must be a lowercase keyword corresponding to
a HTTP request method, such as :get
or :post
.
The transport protocol denoted in the scheme of the request URL. Must be
either: :http
, :https
, :ws
or :wss
.
The resolved server name, or the server IP address, as a string.
The port on which the request is being handled.
The SSL client certificate, if supplied.
The absolute path of the URI in the HTTP request. Must start with a /
.
A Ring response map represents a HTTP response, and contains the following keys. Any key not marked as required may be omitted.
Key | Type | Required |
---|---|---|
:body |
ring.core.protocols/StreamableResponseBody |
|
:headers |
{String String} or {String [String]} |
Yes |
:status |
Integer |
Yes |
A representation of the request body that must satisfy the
ring.core.protocols/StreamableResponseBody
protocol.
(defprotocol StreamableResponseBody
(write-body-to-stream [body response output-stream]))
A Clojure map of header name strings to either a string or a vector of strings that correspond to the header value or values.
The HTTP status code. Must be greater than or equal to 100, and less than or equal to 599.
The asynchronous API builds upon the synchronous API. The differences between the two APIs are described below.
An asynchronous handler takes 3 arguments: a request map, a callback function for sending a response and a callback function for raising an exception. The response callback takes a response map as its argument. The exception callback takes an exception as its argument. The return value of the function is ignored.
(fn [request respond raise]
(respond response))
(fn [request respond raise]
(raise exception))
A handler function may simultaneously support synchronous and asynchronous behavior by accepting both arities.
(fn
([request]
response)
([request respond raise]
(respond response)))
An adapter may support synchronous handlers, or asynchronous handlers, or both. If it supports both, it should have an option to specify which one to use at the time it is invoked.
For example:
(run-adapter handler {:async? true})
A HTTP request can be promoted into a websocket by means of an ""upgrade" header.
In this situation, a Ring handler may choose to respond with a websocket response instead of a HTTP response.
A websocket response is a map that represents a WebSocket, and may be returned from a handler in place of a response map.
(fn [request]
#:ring.websocket{:listener websocket-listener})
It may also be used from an asynchronous handler.
(fn [request respond raise]
(respond #:ring.websocket{:listener websocket-listener}))
A websocket response contains the following keys. Any key not marked as required may be omitted.
Key | Type | Required |
---|---|---|
:ring.websocket/listener |
ring.websocket/Listener |
Yes |
:ring.websocket/protocol |
String |
An event listener that satisfies the ring.websocket.protocols/Listener
protocol, as described in section 3.2.
An optional websocket subprotocol. Must be one of the values listed in
the Sec-Websocket-Protocol
header on the request.
A websocket listener must satisfy the
ring.websocket.protocols/Listener
protocol:
(defprotocol Listener
(on-open [listener socket])
(on-message [listener socket message])
(on-pong [listener socket data])
(on-error [listener socket throwable])
(on-close [listener socket code reason]))
It may optionally satisfy the ring.websocket.protocols/PingListener
protocol:
(defprotocol PingListener
(on-ping [listener socket data]))
If the PingListener
protocol is not satisifed, the adapter must
default to respond to each ping message with a corresponding pong
message that has the same data.
Called once when the websocket is successfully opened. Supplies a
socket
argument that satisfies ring.websocket.protools/Socket
,
described in section 3.3.
Called when a text or binary message frame is received from the client.
The message
argument must be a java.lang.CharSequence
or a
java.nio.ByteBuffer
depending on whether the message is text or binary.
Called when a "ping" frame is received from the client. The data
argument is a java.nio.ByteBuffer
that contains optional client
session data.
If the user implements this method, they are responsible for sending the return "pong" that the websocket protocol expects.
Called when a "pong" frame is received from the client. The data
argument is a java.nio.ByteBuffer
that contains optional client
session data.
Called when an error occurs. This may cause the websocket to be closed.
The throwable
argument is a java.lang.Throwable
that was generated
by the error.
Called once when the websocket is closed, either via a valid close
frame or by an abnormal disconnect of the underlying TCP connection.
Guaranteed to be called if and only if on-open
was called, so may be
used for finalizing/cleanup logic. Takes an integer code
and a string
reason
as arguments.
A socket must satisfy the ring.websocket.protocols/Socket
protocol:
(defprotocol Socket
(-open? [socket])
(-send [socket message])
(-ping [socket data])
(-pong [socket data])
(-close [socket code reason]))
It may optionally satisfy the ring.websocket.protocols/AsyncSocket
protocol:
(defprotocol AsyncSocket
(-send-async [socket message succeed fail]))
Returns a truthy or falsey value denoting whether the socket is currently connected to the client.
Sends a websocket message frame that may be a java.lang.CharSequence
(for text), or a java.nio.ByteBuffer
(for binary).
As above, but does not block and requires two callback functions:
succeed
is called with zero arguments when the send succeedsfail
is called with a singlejava.lang.Throwable
argument when the send fails
Sends a websocket ping frame with a java.nio.ByteBuffer
of session
data (which may be empty).
Sends an unsolicited pong frame with a java.nio.ByteBuffer
of session
data (which may be empty).
Closes the websocket with the supplied integer code and reason string.