1 | ---
|
2 | title: Kettle Request Handlers and the kettle.app grade
|
3 | layout: default
|
4 | category: Kettle
|
5 | ---
|
6 |
|
7 | A [`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
|
8 | type 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 |
|
14 | Request 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
|
15 | containment 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 |
|
26 | Note 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
|
27 | multiple 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 |
|
80 | A handler for a particular request must have a [grade](http://docs.fluidproject.org/infusion/development/ComponentGrades.html) registered with Infusion whose name
|
81 | matches 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 –
|
82 | descended 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
|
84 | is 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
|
85 | constructed to handle the request.
|
86 |
|
87 | We 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
|
90 | fluid.defaults("examples.simpleConfig.handler", {
|
91 | gradeNames: "kettle.request.http",
|
92 | invokers: {
|
93 | handleRequest: "examples.simpleConfig.handleRequest"
|
94 | }
|
95 | });
|
96 |
|
97 | examples.simpleConfig.handleRequest = function (request) {
|
98 | request.events.onSuccess.fire({
|
99 | message: "GET request received on path /handlerPath"
|
100 | });
|
101 | };
|
102 | ```
|
103 |
|
104 | In 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 |
|
110 | A ***request component*** is constructed by Kettle when it has determined the correct [handler record](#registering-and-implementing-a-request-handler) which matches
|
111 | the 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
|
112 | various 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
|
113 | entries to its grade definition, of any of the types supported by Infusion's [component configuration options](http://docs.fluidproject.org/infusion/development/ComponentConfigurationOptions.html).
|
114 | Here 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
|
115 | are 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 |
|
119 | The 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
|
120 | base 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 |
|
170 | Note 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
|
171 | be retrieved from standard middleware, etc. – it will be available as `req.fluidRequest` where `req` is the
|
172 | request 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 |
|
178 | WebSockets 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
|
179 | that 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
|
180 | aspects not properly covered by the documentation.
|
181 |
|
182 | The 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 |
|
250 | A `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 |
|
279 | The 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
|
281 | sequence, the request component will be automatically destroyed by the framework.
|
282 |
|
283 | ### Example mini-application hosting a WebSockets endpoint
|
284 |
|
285 | The following example shows a minimal Kettle application which hosts a single WebSockets request handler named `webSocketsHandler` at the path `/webSocketsPath`. This handler
|
286 | simply logs any messages received to the console.
|
287 |
|
288 | ```javascript
|
289 |
|
290 | fluid.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 |
|
315 | fluid.defaults("examples.webSocketsConfig.handler", {
|
316 | gradeNames: "kettle.request.ws",
|
317 | listeners: {
|
318 | onReceiveMessage: "examples.webSocketsConfig.receiveMessage"
|
319 | }
|
320 | });
|
321 |
|
322 | examples.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
|
327 | examples.webSocketsConfig();
|
328 | ``` |
\ | No newline at end of file |