Skip to content

Latest commit

 

History

History
418 lines (380 loc) · 22.2 KB

RequestHandlersAndApps.md

File metadata and controls

418 lines (380 loc) · 22.2 KB
title layout category
Kettle Request Handlers and the kettle.app grade
default
Kettle

Kettle Request Handlers and the kettle.app grade

A kettle.server comprises one or more kettle.app units, each of which comprises an independently mountable application unit. Within a kettle.app, each type of request handled by the application is defined using a kettle.request component.

Registering and implementing a request handler

Request handlers are registered in the requestHandlers section of the options of a kettle.app – see the sample app for positioning of this component in the containment structure. This consists of a free hash of handlerName strings to handlerRecord structures.

Structure of the requestHandlers option of a kettle.app

{
<handlerName> : <handlerRecord>,
<handlerName> : <handlerRecord>,
...
}

Note that the handlerNames 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 multiple apps are merged together from different sources to produce combined apps.

Structure of the handlerRecord structure

Members of an handlerRecord entry within the requestHandlers block of a kettle.app component
Member Type Description
type String The name of a request handling grade, which must be descended from kettle.request. If you supply the method field, your grade must be descended from kettle.request.http.
route String An express-compatible routing string, expressing the range of HTTP paths to be handled by this handler, together with any named parameters and query parameters that should be captured. The exact syntax for route matching is documented more precisely at pillarjs.
gradeNames (optional) String/Array of String One or more grade names which will be mixed in to the constructed handler when it is constructed.
prefix (optional) String A routing prefix to be prepended to this handler's route. The prefix plus the route expression must match the incoming request in order for this handler to be activated – but if it is, it will only see the portion of the URL matched by route in the member request.req.url. The entire incoming URL will remain visible in request.req.originalUrl – this is the same behaviour as express.js routing system. It is primarily useful when using static middleware which will compare the req.url value with the filesystem path relative to its mount point.
method (optional) String value – one of the valid HTTP methods supported by node.js, expressed in lower case, or else a comma-separated sequence of such values. The HTTP request type(s) which this handler will match. method is omitted in the case that the request handling grade is not descended from kettle.request.http – the only currently supported requests of that type are WebSockets requests descended from kettle.request.ws.

How to implement a request handler

A handler for a particular request must have a grade registered with Infusion whose name 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 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 (at minimum) an invoker named handleRequest. This invoker will be called by Kettle when your route 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 constructed to handle the request.

We duplicate the definitions from the sample application in order to show a minimal request handler grade and request handler function:

fluid.defaults("examples.simpleConfig.handler", {
    gradeNames: "kettle.request.http",
    invokers: {
        handleRequest: "examples.simpleConfig.handleRequest"
    }
});

examples.simpleConfig.handleRequest = function (request) {
    request.events.onSuccess.fire({
        message: "GET request received on path /handlerPath"
    });
};

In the next section we will talk more about request (handler) objects, the members you can expect on them, and how to use them.

Request components

A request component is constructed by Kettle when it has determined the correct handler record which matches 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 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 entries to its grade definition, of any of the types supported by Infusion's component configuration options. 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 are derived from a common grade kettle.request which defines several common members and workflow elements.

Members defined by the Kettle framework at top-level on an HTTP request component

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 base grade kettle.request and so are also available for WebSockets components of type kettle.request.ws.

Members defined by default at top-level on an HTTP request component of type kettle.request.http
Member Type Description
req http.IncomingMessage The request object produced by node.js – this is the value which is commonly referred to as req in the standard express middleware pattern
res (only for kettle.request.http) http.ServerResponse The response object produced by node.js – this is the value which is commonly referred to as res in the standard express middleware pattern
events.onSuccess (only for kettle.request.http) Event A standard Infusion Event 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 Object, it will be JSON-encoded.
events.onError Event A standard Infusion Event which should be fired if the request is to send an error response. For a request of type kettle.request.http, the argument to the event must be an Object with at least a field message of type String holding the error message to be returned to the client. The argument can also include a member statusCode of type Number holding the HTTP status code to accompany the error if this is not supplied, it will default to 500.
handlerPromise (only for kettle.request.http) Promise This promise is a proxy for the two events onSuccess and onError, packaged as a Promise. This promise exposes methods resolve and reject which forward their arguments to onSuccess and onError respectively. In addition, the promise exposes a then method which accepts two callbacks which can be used to listen to these event firings 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 thenable which also includes the standard resolution methods resolve and reject. Implementation at FluidPromises.js.

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 be retrieved from standard middleware, etc. – it will be available as req.fluidRequest where req is the request object described in the table above. More details follow on middleware in the section working with middleware.

WebSockets request components of type kettle.request.ws

WebSockets communications in a Kettle application are mediated by the ws WebSockets library – you should get familiar with the documentation for 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 aspects not properly covered by the documentation.

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 kettle.request.http only via kettle.request, as well as several more members described in the following table:

Members defined by default at top-level on a WebSockets request component of type kettle.request.ws
Member Type Description
events.onBindWs Event A standard Infusion Event which is fired by the framework when the original HTTP connection has completed the handshake and upgrade sequence and the ws.WebSocket object has been allocated. Any listener registered to this event will receive two arguments - firstly, the kettle.request.ws component itself, and secondly the ws.WebSocket object.
ws ws.WebSocket The ws.WebSocket advertised by the ws WebSockets library as allocated to handle one end of an established WebSockets connection. This will be of the variety referred to in the ws docs as "a WebSocket constructed by a Server". This member will only be present after the onBindWs event described in the previous row has fired.
sendMessage Function(message: Any) The sendMessage method is used to sent a WebSocket message to the client. By default, the sendMessageJSON options described in the table below is set to true and so sendMessage will accept a JavaScript object which will be encoded as JSON.
sendTypedMessage Function(type: String, payload: Object) Operates a simple "typed message" system which will fire a payload to sendMessage consisting of {type: type, payload: payload}. This method is only useful together with the sendMessageJSON: true option (its default value).
events.onReceiveMessage Event A standard Infusion Event which is fired by the framework when a message is received from the client at the other end of the WebSockets connection. The arguments to the event are (that, message) where that represents this request component itself, and message represnts the message sent by the client. If the receiveMessageJSON option is set to true for this component (the default), the message will have been decoded as JSON.
events.onSendMessage Event A standard Infusion Event which operates the workflow started by the sendMessage method. This is a transforming promise chain event for which each listener has the chance to transform the payload before it is sent to the next. More information is available for the fireTransformEvent function from Infusion's Promises API
events.onError Event A standard Infusion Event which should be fired if the request is to send an error response. This event has the same name as the one fired by a kettle.request.http but the behaviour and semantic is different. Rather than sending an HTTP error response, the framework instead emits a WebSockets event of type error. Because of this, the statusCode field of the event argument should not be used. However, it is recommended that the event payload still includes a field message of type String holding the error message to be returned to the client, as well as a boolean member isError with the value true.

A kettle.request.ws accepts the following configurable options at top level:

Supported configurable options for a kettle.request.ws
Option Type Description
sendMessageJSON Boolean (default: true) If this is set to true, the argument supplied to the component's sendMessage method will be encoded as JSON. Otherwise the argument will be sent to websocket.send as is.
receiveMessageJSON Boolean (default: true) If this is set to true, the argument received by listeners to the component's onReceiveMessage event will be encoded as JSON. Otherwise the value will be transmitted as from the WebSocket's message event unchanged.

Ending a WebSockets conversation

The currently recommended scheme for terminating a WebSockets conversation managed by a kettle.request.ws component is to call the standard close method on the top-level ws member. This accepts two optional arguments - a WebSockets status code and a description message. After processing the connection termination sequence, the request component will be automatically destroyed by the framework.

Example mini-application hosting a WebSockets endpoint

The following example shows a minimal Kettle application which hosts a single WebSockets request handler named webSocketsHandler at the path /webSocketsPath. This handler simply logs any messages received to the console.

fluid.defaults("examples.webSocketsConfig", {
    gradeNames: "fluid.component",
    components: {
        server: {
            type: "kettle.server",
            options: {
                gradeNames: ["kettle.server.ws"],
                port: 8081,
                components: {
                    app: {
                        type: "kettle.app",
                        options: {
                            requestHandlers: {
                                webSocketsHandler: {
                                    "type": "examples.webSocketsConfig.handler",
                                    "route": "/webSocketsPath"
                                }
                            }
                        }
                    }
                }
            }
        }
    }
});

fluid.defaults("examples.webSocketsConfig.handler", {
    gradeNames: "kettle.request.ws",
    listeners: {
        onReceiveMessage: "examples.webSocketsConfig.receiveMessage"
    }
});

examples.webSocketsConfig.receiveMessage = function (request, message) {
    console.log("Received WebSockets message " + JSON.stringify(message, null, 2));
};

// Construct the server using the above config
examples.webSocketsConfig();