HTTP

Contents

HTTP

The XP framework lets developer build web based applications based on the following capabilities:

HTTP Controller

JavaScript http controllers simply export functions matching the desired HTTP Method it implements. As such, any controller must explicitly declare one or more "exports" in order to handle requests: get, post, delete are examples of such methods.

A controller can also export a special function all which will handle any HTTP method, unless there is a more specific handler available.

The appropriate function will automatically be invoked for every request sent to the controller.

Example usage
// Handles a GET request
exports.get = function(req) {}

// Handles a POST request
exports.post = function(req) {}

// Handles all requests, other than GET or POST which are handled by the functions above
exports.all = function(req) {}

A handler function receives a parameter with an HTTP Request object, and returns an HTTP Response object.

exports.get = function(request) {

  if (request.mode === 'edit') {
    // do something...
  }

  var name = request.params.name;
  log.info('Name = %s', name);

  return {
    body: 'Hello ' + name,
    contentType: 'text/plain'
  };

};

HTTP Filter

Filters enable you to step into the request pipeline, and execute code at at both request and response of the execution pipeline, possibly intercepting the request directly. Similar to an HTTP Controller, standalone filters are triggered based on an export.

As such, JavaScript filters must be exported as filter.

Minimal filter timing the subsequent request
exports.filter = function (req, next) {
    var before = new Date().getTime();
    var response = next(req);  // next(req) hands over the request to the engine pipeline and returns the response
    var after = new Date().getTime();
    log.info((after - before) + 'ms');
    return response;
};
Filter manipulating the request
exports.filter = function (req, next) {
  req.requestLogging = true; // Manipulate request
  log.info("Request:" + JSON.stringify(req, null, 2));
  var response = next(req); // Continue request pipeline
  response.responseLogging = true; // Manipulate response
  log.info("Response:" + JSON.stringify(response, null, 2));
  return response;
};
Filter intercepting the request
exports.filter = function (req, next) {

    if (req.headers['X-Auth-Token'] !== 'letMeIn') {
        // intercept request pipeline
        return {
            status: 403
        }
    }

    req.headers['Authenticated'] = true;
    return next(req);
};

Filters may currently be wired into the execution pipeline in the following ways:

HTTP Request

The following object is passed along with every HTTP request. The object is similar to many traditional request objects, except for two special properties: mode and branch. These properties are specific to the XP Portal, automatically indicating the contextual branch and rendering mode.

The request object represents the HTTP request and current context for the controller.

{
  "method": "GET", (1)
  "scheme": "http", (2)
  "host": "enonic.com", (3)
  "port": "80", (4)
  "path": "/my/page", (5)
  "url": "http://enonic.com/my/page?debug=true", (6)
  "remoteAddress": "10.0.0.1", (7)
  "mode": "edit", (8)
  "branch": "master", (9)
  "body": null (10)
  "params": { (11)
    "debug": "true"
  },
  "headers": { (12)
    "Language": "en",
    "Cookies": "mycookie=123; other=abc;"
  },
  "cookies": { (13)
    "mycookie": "123",
    "other": "abc"
  }
}
1 HTTP method of the request
2 Scheme used to make this request i.e. "http" / "https"
3 Host name of the server to which the request was sent.
4 Port of the server to which the request was sent.
5 Path of the request
6 URL of the request.
7 IP address of the client that sent the request. If the X-Forwarded-For [1] header is set, its value will override the client IP.
8 Rendering mode (used in site context) one of: inline, edit, preview, live.
9 Contextual repository branch (used in site context), one of: draft, master.
10 Optional string value
11 Name/value pairs of the query/form parameters from the request.
12 Name/value pairs of the HTTP request headers.
13 Name/value pairs of the HTTP request cookies.

HTTP Response

The response object is the value returned by an HTTP controller - as a response to an :ref:`http_request`.

{
  "status": 200, (1)
  "body": "Hello World", (2)
  "contentType": "text/plain", (3)
  "headers": {  (4)
      "key": "value"
  },
  "cookies": {},  (5)
  "redirect": "/another/page",  (6)
  "postProcess": true,  (7)
  "pageContributions": {},  (8)
  "applyFilters": true  (9)
}
1 HTTP response status code (default is 200).
2 HTTP message body of the response that can either be a string or a JavaScript object.
3 MIME type of the body (defaults to text/plain; charset=utf-8).
4 Name/value pairs with the HTTP headers to be added to the response.
5 HTTP cookies to be added to the response. Will be described in a later section.
6 URI to redirect to. If specified, the value will be set in the "Location" header and the status will be set to 303.
7 Site engine only: If enabled the response body from a page render is processed to find and render any component tags found. (default is true). Set to false to skip post processing of tags.
8 Site engine only: Use to contribute html to the resulting response markup. See page contributions for more information.
9 Site engine only: If enabled, any defined response processors in the pipeline will be executed.

HTTP Cookies

There are two ways that Http Cookie values can be set in responses (see examples).

Here’s an example of how the cookies are set:

return {
    status: 200,
    body: "Hello World",
    cookies: {
        "plain": "value", (1)
        "complex": { (2)
            value: "value", (3)
            path: "/valid/path", (4)
            domain: "enonic.com", (5)
            comment: "Some cookie comments", (6)
            maxAge: 2000, (7)
            secure: false, (8)
            httpOnly: false (9)
        }
    }
};
1 If the value is a string then the cookie is created using default settings.
2 If the value is an object, it will try to apply the settings.
3 Value (required) The value to store in the cookie. This example will create a cookie looking like this complex: value.
4 The paths on the site where this cookie should be available from (and all containing paths). Defaults to empty
5 Add additional sites that should be able to read the cookie. Defaults to empty (Only the server that creates the cookie can read it.)
6 A comment describing the cookie. Default to `null.
7 Number of seconds before the browser is allowed to delete the cookie. Defaults to -1 (The cookie will live until the browser is shut down.)
8 Control if the cookie should only be accepted to be created and read over https and similar secure protocols. Defaults to false
9 Control if the cookie is available for scripts or not. If true, only the serverside code can read the cookie. Defaults to false (Also client-side scripts can read the cookie.)

HTTP Websockets

Websocket support allows a service to act as a websocket channel that you can connect to from a web-browser.

A get method must be implemented to handle initialization of the websocket.

// Create a websocket if websocket request.
exports.get = function (req) {

  if (!req.webSocket) {
    return {
      status: 404
    };
  }

  return {
    webSocket: {
      data: {
        user: "test"
      },
      subProtocols: ["text"]
    }
  };
};

A websocket event handler named webSocketEvent is required. It will be called for every websocket event from a client. See example below.

// Listen to a websocket event
exports.webSocketEvent = function (event) {

  if (event.type == 'open') {
    // Do something on open
  }

  if (event.type == 'message') {
    // Do something on message recieved
  }

  if (event.type == 'close') {
    // Do something on close
  }

};

Below is an example of a simple chat using the Websocket JS api for sending messages back and adding/removing clients in groups. Adding to groups allows for multicast message sending.

// Lib that contains websocket functions.
var webSocketLib = require('/lib/xp/websocket');

// Listen to a websocket event
exports.webSocketEvent = function (event) {

  if (event.type == 'open') {
    // Send message back to client
    webSocketLib.send(event.session.id, 'Welcome to our chat');

    // Add client into a group
    webSocketLib.addToGroup('chat', event.session.id);
  }

  if (event.type == 'message') {
    // Propegate message to group
    webSocketLib.sendToGroup('chat', event.message);
  }

  if (event.type == 'close') {
    // Remove client from a group
    webSocketLib.removeFromGroup('chat', event.session.id);
  }

};

Error handler

TODO: Work in progress

Contents