Skip to content

Commit f961f9f

Browse files
committed
Add draft 1.5 SPEC for Ring 1.11-alpha1
1 parent 7ce01f8 commit f961f9f

File tree

1 file changed

+291
-0
lines changed

1 file changed

+291
-0
lines changed

SPEC-alpha.md

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
# Ring Spec (1.5-DRAFT)
2+
3+
Ring is an abstraction layer for building HTTP server applications in
4+
Clojure.
5+
6+
The specification is divided into two parts; a synchronous API, and an
7+
asynchronous API. The synchronous API is simpler, but the asynchronous
8+
API can be more performant.
9+
10+
11+
## 1. Synchronous API
12+
13+
Ring is defined in terms of handlers, middleware, adapters, request
14+
maps and response maps, each of which are described below.
15+
16+
## 1.1. Handlers
17+
18+
Ring handlers constitute the core logic of the web application.
19+
Handlers are implemented as Clojure functions.
20+
21+
A synchronous handler takes 1 argument, a request map, and returns a
22+
response map.
23+
24+
```clojure
25+
(fn [request] response)
26+
```
27+
28+
### 1.2. Middleware
29+
30+
Ring middlware augment the functionality of handlers. Middleware is
31+
implemented as higher-order functions that take one or more handlers
32+
and configuration options as arguments and return a new handler with
33+
the desired additional behavior.
34+
35+
### 1.3. Adapters
36+
37+
Ring adapters are side-effectful functions that take a handler and a
38+
map of options as arguments, and when invoked start a HTTP server.
39+
40+
```clojure
41+
(run-adapter handler options)
42+
```
43+
44+
Once invoked, adapters will receive HTTP requests, parse them to
45+
construct a request map, and then invoke their handler with this
46+
request map as an argument. Once the handler response with a response
47+
map, the adapter will use it to construct and send an HTTP response to
48+
the client.
49+
50+
### 1.4. Request Maps
51+
52+
A Ring request map represents a HTTP request, and contains the
53+
following keys. Any key not marked as **required** may be omitted. Keys
54+
marked as **deprecated** are there only for backward compatibility with
55+
earlier versions of the specification.
56+
57+
| Key | Type | Required | Deprecated |
58+
| ------------------- | ---------------------------------- | -------- | ---------- |
59+
|`:body` |`java.io.InputStream` | | |
60+
|`:character-encoding`|`String` | | Yes |
61+
|`:content-length` |`String` | | Yes |
62+
|`:content-type` |`String` | | Yes |
63+
|`:headers` |`{String String}` | Yes | |
64+
|`:protocol` |`String` | Yes | |
65+
|`:query-string` |`String` | | |
66+
|`:remote-addr` |`String` | Yes | |
67+
|`:request-method` |`Keyword` | Yes | |
68+
|`:scheme` |`Keyword` | Yes | |
69+
|`:server-name` |`String` | Yes | |
70+
|`:server-port` |`Integer` | Yes | |
71+
|`:ssl-client-cert` |`java.security.cert.X509Certificate`| | |
72+
|`:uri` |`String` | Yes | |
73+
74+
#### :body
75+
76+
An `InputStream` for the request body, if one is present.
77+
78+
#### :character-encoding [DEPRECATED]
79+
80+
Equivalent to the character encoding specified in the `Content-Type`
81+
header. Deprecated key.
82+
83+
#### :content-length [DEPRECATED]
84+
85+
Equivalent to the `Content-Length` header, converted to an integer.
86+
Deprecated key.
87+
88+
#### :content-type [DEPRECATED]
89+
90+
Equivalent to the media type in the `Content-Type` header. Deprecated
91+
key.
92+
93+
#### :headers
94+
95+
A Clojure map of lowercased header name strings to corresponding
96+
header value strings.
97+
98+
Where there are multiple headers with the same name, the adapter must
99+
concatenate the values into a single string, using the ASCII `,`
100+
character as a delimiter.
101+
102+
The exception to this is the `cookie` header, which should instead use
103+
the ASCII `;` character as a delimiter.
104+
105+
#### :protocol
106+
107+
The protocol the request was made with, e.g. "HTTP/1.1".
108+
109+
#### :query-string
110+
111+
The query segment of the URI in the HTTP request. This includes
112+
everything after the `?` character, but excludes the `?` itself.
113+
114+
#### :remote-addr
115+
116+
The IP address of the client or the last proxy that sent the request.
117+
118+
#### :request-method
119+
120+
The HTTP request method. Must be a lowercase keyword corresponding to
121+
a HTTP request method, such as `:get` or `:post`.
122+
123+
#### :scheme
124+
125+
The transport protocol denoted in the scheme of the request URL. Must be
126+
either: `:http`, `:https`, `:ws` or `:wss`.
127+
128+
#### :server-name
129+
130+
The resolved server name, or the server IP address, as a string.
131+
132+
#### :server-port
133+
134+
The port on which the request is being handled.
135+
136+
#### :ssl-client-cert
137+
138+
The SSL client certificate, if supplied.
139+
140+
#### :uri
141+
142+
The absolute path of the URI in the HTTP request. Must start with a `/`.
143+
144+
### 1.5. Response Maps
145+
146+
A Ring response map represents a HTTP response, and contains the
147+
following keys. Any key not marked as **required** may be omitted.
148+
149+
| Key | Type | Required |
150+
| -------- | ------------------------------------------ | -------- |
151+
|`:body` |`ring.core.protocols/StreamableResponseBody`| |
152+
|`:headers`|`{String String}` or `{String [String]}` | Yes |
153+
|`:status` |`Integer` | Yes |
154+
155+
#### :body
156+
157+
A representation of the request body that must satisfy the
158+
`ring.core.protocols/StreamableResponseBody` protocol.
159+
160+
```clojure
161+
(defprotocol StreamableResponseBody
162+
(write-body-to-stream [body response output-stream]))
163+
```
164+
165+
#### :headers
166+
167+
A Clojure map of lowercased header name strings to either a string or
168+
a vector of strings that correspond to the header value or values.
169+
170+
#### :status
171+
172+
The HTTP status code. Must be greater than or equal to 100, and less
173+
than or equal to 599.
174+
175+
176+
## 2. Asynchronous API
177+
178+
The asynchronous API builds upon the synchronous API. The differences
179+
between the two APIs are described below.
180+
181+
### 2.1. Handlers
182+
183+
An asynchronous handler takes 3 arguments: a request map, a callback
184+
function for sending a response and a callback function for raising an
185+
exception. The response callback takes a response map as its
186+
argument. The exception callback takes an exception as its
187+
argument. The return value of the function is ignored.
188+
189+
```clojure
190+
(fn [request respond raise]
191+
(respond response))
192+
```
193+
194+
```clojure
195+
(fn [request respond raise]
196+
(raise exception))
197+
```
198+
199+
A handler function may simultaneously support synchronous and
200+
asynchronous behavior by accepting both arities.
201+
202+
```clojure
203+
(fn
204+
([request]
205+
response)
206+
([request respond raise]
207+
(respond response)))
208+
```
209+
210+
### 2.2. Adapters
211+
212+
An adapter may support synchronous handlers, or asynchronous handlers,
213+
or both. If it supports both, it should have an option to specify
214+
which one to use at the time it is invoked.
215+
216+
For example:
217+
218+
```clojure
219+
(run-adapter handler {:async? true})
220+
```
221+
222+
## 3. Websockets
223+
224+
A HTTP request can be promoted into a websocket by means of an
225+
"upgrade" header.
226+
227+
In this situation, a Ring handler may choose to respond with a
228+
websocket response instead of a HTTP response.
229+
230+
### 3.1. Websocket Responses
231+
232+
A websocket response is a map that has the `:ring.websocket/listener`
233+
key, which maps to a websocket listener, described in section 3.2.
234+
235+
```clojure
236+
(fn [request]
237+
#:ring.websocket{:listener websocket-listener})
238+
```
239+
240+
A websocket response may be returned from a synchronous listener, or
241+
via the response callback of an asynchronous listener.
242+
243+
```clojure
244+
(fn [request respond raise]
245+
(respond #:ring.websocket{:listener websocket-listener}))
246+
```
247+
248+
### 3.2. Websocket Listeners
249+
250+
A websocket listener must satisfy the `ring.websocket/Listener`
251+
protocol:
252+
253+
```clojure
254+
(defprotocol Listener
255+
(on-open [listener socket])
256+
(on-message [listener socket message])
257+
(on-pong [listener socket data])
258+
(on-error [listener socket throwable])
259+
(on-close [listener socket code reason]))
260+
```
261+
262+
The arguments are described as follows:
263+
264+
* `socket` - described in section 3.3.
265+
* `message` - a `String` or `java.nio.ByteBuffer` containing a message
266+
* `data` - a `java.nio.ByteBuffer` containing pong data
267+
* `throwable` - an error inheriting from `java.lang.Throwable`
268+
* `code` - an integer from 1000 to 4999
269+
* `reason` - a string describing the reason for closing the socket
270+
271+
### 3.3. Websocket Sockets
272+
273+
A socket must satisfy the `ring.websocket/Socket` protocol:
274+
275+
```clojure
276+
(defprotocol Socket
277+
(-send [socket message])
278+
(-ping [socket data])
279+
(-pong [socket data])
280+
(-close [socket status reason]))
281+
```
282+
283+
The types of the arguments are the same as those described for the
284+
`Listener` protocol.
285+
286+
It *may* optionally satisfy the `ring.websocket/AsyncSocket` protocol:
287+
288+
```clojure
289+
(defprotocol AsyncSocket
290+
(-send-async [socket message callback]))
291+
```

0 commit comments

Comments
 (0)