UNPKG

21.6 kBMarkdownView Raw
1---
2title: Kettle Request Handlers and the kettle.app grade
3layout: default
4category: Kettle
5---
6
7A [`kettle.server'](Servers.md) comprises one or more `kettle.app` units, each of which comprises an independently mountable application unit. Within a [`kettle.app`](#kettle.app), each
8type of request handled by the application is defined using a [`kettle.request`](#how-to-implement-a-request-handler) component.
9
10<a id="kettle.app"></a>
11
12## Registering and implementing a request handler
13
14Request handlers are registered in the `requestHandlers` section of the options of a `kettle.app` – see the [sample app](ConfigsAndApplications.md#a-simple-kettle-application) for positioning of this component in the
15containment structure. This consists of a free hash of `handlerName` strings to `handlerRecord` structures.
16
17###Structure of the `requestHandlers` option of a `kettle.app`
18```javascript
19{
20<handlerName> : <handlerRecord>,
21<handlerName> : <handlerRecord>,
22...
23}
24```
25
26Note that the `handlerName`s are simply free strings and have no function other than to uniquely name the handler in the context of its app. These strings exist to allow easy alignment when
27multiple apps are merged together from different sources to produce combined apps.
28
29### Structure of the `handlerRecord` structure
30
31<table>
32 <thead>
33 <tr>
34 <th colspan="3">Members of an <code>handlerRecord</code> entry within the <code>requestHandlers</code> block of a <code>kettle.app</code> component</th>
35 </tr>
36 <tr>
37 <th>Member</th>
38 <th>Type</th>
39 <th>Description</th>
40 </tr>
41 </thead>
42 <tbody>
43 <tr>
44 <td><code>type</code></td>
45 <td><code>String</code></td>
46 <td>The name of a request handling grade, which must be descended from <code>kettle.request</code>. If you supply the <code>method</code> field, your grade must be descended from <code>kettle.request.http</code>.</td>
47 </tr>
48 <tr>
49 <td><code>route</code></td>
50 <td><code>String</code></td>
51 <td>An express-compatible <a href="http://expressjs.com/guide/routing.html">routing</a> string, expressing the range of HTTP paths to be handled by this handler, together with any named parameters and query parameters
52 that should be captured. The exact syntax for route matching is documented more precisely at <a href="https://github.com/pillarjs/path-to-regexp">pillarjs</a>.</td>
53 </tr>
54 <tr>
55 <td><code>gradeNames</code> (optional)</td>
56 <td><code>String/Array of String</code></td>
57 <td>One or more grade names which will be mixed in to the constructed handler when it is constructed.</td>
58 </tr>
59 <tr>
60 <td><code>prefix</code> (optional)</td>
61 <td><code>String</code></td>
62 <td>A routing prefix to be prepended to this handler's <code>route</code>. The prefix plus the route expression must match the incoming request in order for this handler to be activated –
63 but if it is, it will only see the portion of the URL matched by <code>route</code> in the member <code>request.req.url</code>. The entire incoming URL will remain visible in <code>request.req.originalUrl</code> –
64 this is the same behaviour as express.js <a href="http://expressjs.com/api.html#app.use">routing system</a>. It is primarily useful when using <a href="#thing">static middleware</a> which will compare the
65 <code>req.url</code> value with the filesystem path relative to its mount point.
66 </tr>
67 <tr>
68 <td><code>method</code> (optional)</td>
69 <td><code>String</code> value – one of the valid <a href="https://github.com/nodejs/node/blob/master/deps/http_parser/http_parser.h#L88">HTTP methods</a> supported by node.js, expressed in lower case, or else a comma-separated
70 sequence of such values.
71 </td>
72 <td>The HTTP request type(s) which this handler will match. <code>method</code> is omitted in the
73 case that the request handling grade is not descended from <code>kettle.request.http</code> – the only currently supported requests of that type are WebSockets requests descended from <code>kettle.request.ws</code>.
74 </tr>
75 </tbody>
76</table>
77
78### How to implement a request handler
79
80A handler for a particular request must have a [grade](http://docs.fluidproject.org/infusion/development/ComponentGrades.html) registered with Infusion whose name
81matches the `type` field in the `handlerRecord` structure just described. The parent grades of this grade must be consistent with the the request you expect to handle –
82descended from `kettle.request.http` in the case of an HTTP request, or `kettle.request.ws` in the case of a WebSockets request. In addition, the grade must define
83(at minimum) an [invoker](http://docs.fluidproject.org/infusion/development/Invokers.html) named `handleRequest`. This invoker will be called by Kettle when your route
84is matched, and be supplied a single argument holding the ***request object***, an object whose grade is your request handler's grade, which the framework has
85constructed to handle the request.
86
87We duplicate the definitions from the [sample application](ConfigsAndApplications.md#a-simple-kettle-application) in order to show a minimal request handler grade and request handler function:
88
89```javascript
90fluid.defaults("examples.simpleConfig.handler", {
91 gradeNames: "kettle.request.http",
92 invokers: {
93 handleRequest: "examples.simpleConfig.handleRequest"
94 }
95});
96
97examples.simpleConfig.handleRequest = function (request) {
98 request.events.onSuccess.fire({
99 message: "GET request received on path /handlerPath"
100 });
101};
102```
103
104In the next section we will talk more about request (handler) objects, the members you can expect on them, and how to use them.
105
106<a id="kettle.request"></a>
107
108## Request components
109
110A ***request component*** is constructed by Kettle when it has determined the correct [handler record](#registering-and-implementing-a-request-handler) which matches
111the incoming request. This request component will be usefully populated with material drawn from the request and node.js initial process of handling it. It also contains
112various elements supplied by Kettle in order to support you in handling the request. You can add any further material that you like to the request object by adding
113entries to its grade definition, of any of the types supported by Infusion's [component configuration options](http://docs.fluidproject.org/infusion/development/ComponentConfigurationOptions.html).
114Here we will document the standard members that are placed there by Kettle for the two standard request types which are supported, `kettle.request.http` and `kettle.request.ws`. These
115are derived from a common grade `kettle.request` which defines several common members and workflow elements.
116
117### Members defined by the Kettle framework at top-level on an HTTP request component
118
119The following table describes the members defined on `kettle.request.http`. Where these are not listed as "only for `kettle.request.http`" they are defined in the
120base grade `kettle.request` and so are also available for WebSockets components of type `kettle.request.ws`.
121
122<table>
123 <thead>
124 <tr>
125 <th colspan="3">Members defined by default at top-level on an HTTP request component of type <code>kettle.request.http</code></th>
126 </tr>
127 <tr>
128 <th>Member</th>
129 <th>Type</th>
130 <th>Description</th>
131 </tr>
132 </thead>
133 <tbody>
134 <tr>
135 <td><code>req</code></td>
136 <td><a href="https://nodejs.org/api/http.html#http_http_incomingmessage"><code>http.IncomingMessage</code></a></td>
137 <td>The request object produced by node.js – this is the value which is commonly referred to as <a href="http://expressjs.com/4x/api.html#req"><code>req</code></a> in the standard express <a href="http://expressjs.com/guide/using-middleware.html">middleware pattern</a></td>
138 </tr>
139 <tr>
140 <td><code>res</code> (only for <code>kettle.request.http</code>)</td>
141 <td><a href="https://nodejs.org/api/http.html#http_class_http_serverresponse"><code>http.ServerResponse</code></a></td>
142 <td>The response object produced by node.js – this is the value which is commonly referred to as <a href="http://expressjs.com/4x/api.html#res"><code>res</code></a> in the standard express <a href="http://expressjs.com/4x/api.html#req">middleware pattern</a></td>
143 </tr>
144 <tr>
145 <td><code>events.onSuccess</code> (only for <code>kettle.request.http</code>)</td>
146 <td><a href="http://docs.fluidproject.org/infusion/development/InfusionEventSystem.html"><code>Event</code></a></td>
147 <td>A standard <a href="http://docs.fluidproject.org/infusion/development/InfusionEventSystem.html">Infusion Event</a> which should be fired if the request is to produce a response successfully. The event argument will produce the response body – if it is of type <code>Object</code>, it will be JSON-encoded.</td>
148 </tr>
149 <tr>
150 <td><code>events.onError</code></td>
151 <td><a href="http://docs.fluidproject.org/infusion/development/InfusionEventSystem.html"><code>Event</code></a></td>
152 <td>A standard <a href="http://docs.fluidproject.org/infusion/development/InfusionEventSystem.html">Infusion Event</a> which should be fired if the request is to send an error response.
153 For a request of type <code>kettle.request.http</code>, the argument to the event must be an <code>Object</code> with at least
154 a field <code>message</code> of type <code>String</code> holding the error message to be returned to the client. The argument can also include a member <code>statusCode</code> of type <code>Number</code> holding the HTTP status code to accompany the error –
155 if this is not supplied, it will default to 500.
156 </td>
157 </tr>
158 <tr>
159 <td><code>handlerPromise</code> (only for <code>kettle.request.http</code>)</td>
160 <td><code>Promise</code></td>
161 <td>This promise is a proxy for the two events <code>onSuccess</code> and <code>onError</code>, packaged as a <a href="https://www.promisejs.org/">Promise</a>. This promise exposes methods <code>resolve</code> and <code>reject</code>
162 which forward their arguments to <code>onSuccess</code> and <code>onError</code> respectively. In addition, the promise exposes a <code>then</code> method which accepts two callbacks which can be used to listen to these event firings
163 respectively. Note that this promise is not compliant with any particular specification for promises, including ES6, A+, etc. – in the language of those specifications, it is simply a <code>thenable</code> which also includes
164 the standard resolution methods <code>resolve</code> and <code>reject</code>. Implementation at <a href="https://github.com/fluid-project/infusion/blob/master/src/framework/core/js/FluidPromises.js#L21">FluidPromises.js</a>.
165 </td>
166 </tr>
167 </tbody>
168</table>
169
170Note that, conversely with the `req` property of the Kettle request component, the Kettle request component itself will be marked onto the node.js request object so that it can easily
171be retrieved from standard middleware, etc. – it will be available as `req.fluidRequest` where `req` is the
172request object described in the table above. More details follow on middleware in the section [working with middleware](Middleware.md#working-with-middleware).
173
174<a id="kettle.request.ws"></a>
175
176### WebSockets request components of type `kettle.request.ws`
177
178WebSockets communications in a Kettle application are mediated by the [ws](https://github.com/websockets/ws) WebSockets library – you should get familiar with the documentation for
179that library if you intend to use this functionality significantly. It is also worth spending some time familiarising yourself with at least some of the `ws` implementation code since there are several
180aspects not properly covered by the documentation.
181
182The request component for a WebSockets request, derived from the grade `kettle.request.ws`, includes the members in the above table which are not marked as
183`kettle.request.http` only via `kettle.request`, as well as several more members described in the following table:
184
185<table>
186 <thead>
187 <tr>
188 <th colspan="3">Members defined by default at top-level on a WebSockets request component of type <code>kettle.request.ws</code></th>
189 </tr>
190 <tr>
191 <th>Member</th>
192 <th>Type</th>
193 <th>Description</th>
194 </tr>
195 </thead>
196 <tbody>
197 <tr>
198 <td><code>events.onBindWs</code></td>
199 <td><a href="http://docs.fluidproject.org/infusion/development/InfusionEventSystem.html"><code>Event</code></a></td>
200 <td>A standard <a href="http://docs.fluidproject.org/infusion/development/InfusionEventSystem.html">Infusion Event</a> which is fired by the framework when the original HTTP connection has completed the
201 <a href="https://en.wikipedia.org/wiki/WebSocket#Protocol_handshake">handshake and upgrade sequence</a> and the <a href="https://github.com/websockets/ws/blob/master/doc/ws.md#class-wswebsocket"><code>ws.WebSocket</code></a>
202 object has been allocated. Any listener registered to this event will receive two arguments - firstly, the <code>kettle.request.ws</code> component itself, and secondly
203 the <a href="https://github.com/websockets/ws/blob/master/doc/ws.md#class-wswebsocket"><code>ws.WebSocket</code></a> object.
204 </td>
205 </tr>
206 <tr>
207 <td><code>ws</code></td>
208 <td><a href="https://github.com/websockets/ws/blob/master/doc/ws.md#class-wswebsocket"><code>ws.WebSocket</code></a></td>
209 <td>The <code>ws.WebSocket</code> advertised by the <a href="https://github.com/websockets/ws"><code>ws</code></a> WebSockets library as allocated to handle one end of an established WebSockets connection. This will be of the variety
210 referred to in the <code>ws</code> docs as "a WebSocket constructed by a Server". This member will only be present after the <code>onBindWs</code> event described in the previous row has fired.</td>
211 </tr>
212 <tr>
213 <td><code>sendMessage</code></td>
214 <td><code>Function(message: Any)</code></a></td>
215 <td>The <code>sendMessage</code> method is used to sent a WebSocket message to the client. By default, the <code>sendMessageJSON</code> options described in the table below is set to <code>true</code> and so <code>sendMessage</code> will
216 accept a JavaScript object which will be encoded as JSON. </td>
217 </tr>
218 <tr>
219 <td><code>sendTypedMessage</code></td>
220 <td><code>Function(type: String, payload: Object)</code></a></td>
221 <td>Operates a simple "typed message" system which will fire a payload to <code>sendMessage</code> consisting of <code>{type: type, payload: payload}</code>. This method is only useful together with the
222 <code>sendMessageJSON: true</code> option (its default value).</td>
223 </tr>
224 <tr>
225 <td><code>events.onReceiveMessage</code></td>
226 <td><a href="http://docs.fluidproject.org/infusion/development/InfusionEventSystem.html"><code>Event</code></a></td>
227 <td>A standard <a href="http://docs.fluidproject.org/infusion/development/InfusionEventSystem.html">Infusion Event</a> which is fired by the framework when a message is received from the client
228 at the other end of the WebSockets connection. The arguments to the event are <code>(that, message)</code> where <code>that</code> represents this request component itself, and <code>message</code> represnts
229 the message sent by the client. If the <code>receiveMessageJSON</code> option is set to <code>true</code> for this component (the default), the message will have been decoded as JSON.</td>
230 </tr>
231 <tr>
232 <td><code>events.onSendMessage</code></td>
233 <td><a href="http://docs.fluidproject.org/infusion/development/InfusionEventSystem.html"><code>Event</code></a></td>
234 <td>A standard <a href="http://docs.fluidproject.org/infusion/development/InfusionEventSystem.html">Infusion Event</a> which operates the workflow started by the <code>sendMessage</code> method.
235 This is a <a href="./DataSources.md#transforming-promise-chains">transforming promise chain</a> event for which each listener has
236 the chance to transform the payload before it is sent to the next. More information is available for the
237 <a href="http://docs.fluidproject.org/infusion/development/PromisesAPI.html#fluid-promise-firetransformevent-event-payload-options-"><code>fireTransformEvent</code></a> function from Infusion's Promises API</td>
238 </tr>
239 <tr>
240 <td><code>events.onError</code></td>
241 <td><a href="http://docs.fluidproject.org/infusion/development/InfusionEventSystem.html"><code>Event</code></a></td>
242 <td>A standard <a href="http://docs.fluidproject.org/infusion/development/InfusionEventSystem.html">Infusion Event</a> which should be fired if the request is to send an error response.
243 This event has the same name as the one fired by a <code>kettle.request.http</code> but the behaviour and semantic is different. Rather than sending an HTTP error response, the framework instead
244 emits a WebSockets event of type <code>error</code>. Because of this, the <code>statusCode</code> field of the event argument should not be used. However, it is recommended that the event payload
245 still includes a field <code>message</code> of type <code>String</code> holding the error message to be returned to the client, as well as a boolean member <code>isError</code> with the value <code>true</code>.</td>
246 </tr>
247 </tbody>
248</table>
249
250A `kettle.request.ws` accepts the following configurable options at top level:
251
252<table>
253 <thead>
254 <tr>
255 <th colspan="3">Supported configurable options for a <code>kettle.request.ws</code></th>
256 </tr>
257 <tr>
258 <th>Option</th>
259 <th>Type</th>
260 <th>Description</th>
261 </tr>
262 </thead>
263 <tbody>
264 <tr>
265 <td><code>sendMessageJSON</code></td>
266 <td><code>Boolean</code> (default: <code>true</code>)</td>
267 <td>If this is set to <code>true</code>, the argument supplied to the component's <code>sendMessage</code> method will be encoded as JSON. Otherwise the argument will be sent to <code>websocket.send</code> as is.</td>
268 </tr>
269 <tr>
270 <td><code>receiveMessageJSON</code></td>
271 <td><code>Boolean</code> (default: <code>true</code>)</td>
272 <td>If this is set to <code>true</code>, the argument received by listeners to the component's <code>onReceiveMessage</code> event will be encoded as JSON. Otherwise the value will be transmitted as from the WebSocket's <code>message</code> event unchanged.</td>
273 </tr>
274 </tbody>
275</table>
276
277### Ending a WebSockets conversation
278
279The currently recommended scheme for terminating a WebSockets conversation managed by a `kettle.request.ws` component is to call the standard [`close`](https://github.com/websockets/ws/blob/master/doc/ws.md#websocketclosecode-data) method on the top-level
280`ws` member. This accepts two optional arguments - a [WebSockets status code](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) and a description message. After processing the connection termination
281sequence, the request component will be automatically destroyed by the framework.
282
283### Example mini-application hosting a WebSockets endpoint
284
285The following example shows a minimal Kettle application which hosts a single WebSockets request handler named `webSocketsHandler` at the path `/webSocketsPath`. This handler
286simply logs any messages received to the console.
287
288```javascript
289
290fluid.defaults("examples.webSocketsConfig", {
291 gradeNames: "fluid.component",
292 components: {
293 server: {
294 type: "kettle.server",
295 options: {
296 port: 8081,
297 components: {
298 app: {
299 type: "kettle.app",
300 options: {
301 requestHandlers: {
302 webSocketsHandler: {
303 "type": "examples.webSocketsConfig.handler",
304 "route": "/webSocketsPath"
305 }
306 }
307 }
308 }
309 }
310 }
311 }
312 }
313});
314
315fluid.defaults("examples.webSocketsConfig.handler", {
316 gradeNames: "kettle.request.ws",
317 listeners: {
318 onReceiveMessage: "examples.webSocketsConfig.receiveMessage"
319 }
320});
321
322examples.webSocketsConfig.receiveMessage = function (request, message) {
323 console.log("Received WebSockets message " + JSON.stringify(message, null, 2));
324};
325
326// Construct the server using the above config
327examples.webSocketsConfig();
328```
\No newline at end of file