Module to generate the context which is passed on requests from a Podium Layout server to a Podium Podlet server.
This module is intended for internal use in Podium and is not a module an end user would use directly. End users will typically interact with this module through higher level modules such as the @podium/layout module.
$ npm install @podium/context
Generate a context which can be passed on to an http request to a Podlet:
import { HttpIncoming } from '@podium/utils';
import Context from '@podium/context';
import http from 'http';
// Set up a context with the name 'myLayout'
const context = new Context({ name: 'myLayout' });
const server = http.createServer(async (req, res) => {
// Create a HttpIncoming object
const incoming = new HttpIncoming(req, res);
// Run context parsers on the request
const incom = await context.process(incoming);
// Serialize the context into an object that can be
// passed on as HTTP headers on each HTTP request
const headers = Context.serialize({}, incom.context);
[ ... snip ...]
});
server.listen(8080);
The Podium Context is used to provide key information from a Layout server to a Podlet server. This is done by a defined set of HTTP headers which is applied to all requests from a Layout server to a Podlet server.
This module handles generating wanted key information in the Layout server and seralizing it into HTTP headers which are passed on to requests to the Podlet servers where the HTTP headers are once again parsed back into a key / value object with the key information.
There are three parts in this module:
- Parsers
- Middleware to run parsers
- Serializing / deserializing
Each part works as follow:
Parsers operate on inbound requests to a layout server. Each parser is handed an HttpIncoming object for each request. Upon execution a parser builds a value which will be applied as part of the context and then appended to all requests made to podlet servers.
This module comes with a set of built in parsers which will always be applied.
It's also possible to write custom parsers and append them to the process of constructing the context.
There is a .process()
method which takes an HttpIncoming object and then
runs it through all registered parsers in parallel.
The result of each parser is stored in an object which is set on the .context
property of the HttpIncoming object. This object is "HTTP header like" and can
be serialized into headers on an HTTP request to a Podlet.
These are static
methods used to serialize and deserialize the "HTTP header
like" object from HttpIncoming.context
into HTTP headers on the HTTP request
to a Podlet and then back into a object on HttpIncoming.context
in the Podlet
server.
Creates a new Podium context instance.
import Context from '@podium/context';
const context = new Context({ name: 'myName' });
The constructor takes the following arguments:
option | default | type | required | details |
---|---|---|---|---|
name | null |
string |
true |
|
debug | null |
object |
false |
See parser options |
locale | null |
object |
false |
See parser options |
deviceType | null |
object |
false |
See parser options |
mountOrigin | null |
object |
false |
See parser options |
mountPathname | null |
object |
false |
See parser options |
publicPathname | null |
object |
false |
See parser options |
A name as a String
to identify the instance. This should be a logical and
human readable name related to the Layout this instance is appended too. This
name is passed on to the Podlet servers as part of the
Requested By context.
The name value must be in camelCase.
Example
const context = new Context({
name: 'myLayout';
});
Config object passed on to the debug parser. See the parser docs.
Config object passed on to the locale parser. See the parser docs.
Config object passed on to the device type parser. See the parser docs.
Config object passed on to the mount origin parser. See the parser docs.
Config object passed on to the mount pathname parser. See the parser docs.
Config object passed on to the public pathname parser. See the parser docs.
The Context instance has the following API:
Register a Parser for a value that should be appended to the Context.
This method takes the following arguments:
option | default | type | required | details |
---|---|---|---|---|
name | null |
string |
true |
Unique name of the parser. Used as the key for the parser's value in the context |
parser | null |
object |
true |
The Parser to be registered |
Example:
import { HttpIncoming } from '@podium/utils';
import Context from '@podium/context';
import Parser from 'my-custom-parser';
import http from 'http';
// Set up a context and register the custom parser
const context = new Context({ name: 'myLayout' });
context.register('myStuff', new Parser('someConfig'));
const server = http.createServer(async (req, res) => {
const incoming = new HttpIncoming(req, res);
const incom = await context.process(incoming);
// incom.context will now hold the following object:
// {
// 'podium-debug': 'false',
// 'podium-locale': 'no-NO'
// 'podium-my-stuff': 'value from custom parser'
// }
});
server.listen(8080);
Metod for processing a incoming http request. It runs all parsers in parallel
and append the result of each parser to HttpIncoming.context
.
This will execute all built in parsers as well as all externally registered
(through the .register()
method) parsers.
Returns a Promise which will resolve with the passed in HttpIncoming
object.
An instance of a HttpIncoming class.
The Context constructor has the following static API:
Takes an "HTTP header like" object produced by .process()
(the
HttpIncoming.context
object) and serializes it into an HTTP header object
which can be applied to HTTP requests sent to podlets.
The object stored at HttpIncoming.context
is "HTTP header-ish" because the
value of each key can be either a String
or a Function
. If a key holds a
Function
the serializer will call the function with the podletName
argument.
The method takes the following arguments:
option | default | type | required | details |
---|---|---|---|---|
headers | null |
object |
true |
An existing HTTP header object or empty object the context should be merged into |
context | null |
object |
true |
The object produced by .middleware() and stored at res.locals.podium.context
|
podletName | null |
string |
false |
The name of the podlet the context should be applied to |
Example: layout sends context with a request to a podlet
const server = http.createServer(async (req, res) => {
const incoming = new HttpIncoming(req, res);
const incom = await context.process(incoming);
const headers = Context.serialize({}, incom.context, 'somePodlet');
request({
headers: headers,
method: 'GET',
url: 'http://some.podlet.finn.no/',
}).pipe(res);
});
Connect compatible middleware which will parse HTTP headers on inbound requests
and turn Podium context headers into a context object stored at
res.locals.podium.context
.
Example: podlet receives request from a layout server
app.use(Context.deserialize());
app.get('/', (req, res) => {
res.status(200).json(res.locals.podium.context);
});
This module comes with a set of default parsers which will be applied when
.process()
is run.
Each of these parsers can be configured through the constructor's options object by passing an options object for the specific parser (see constructor options).
Example of passing options to the built in debug
parser:
import Context from '@podium/context';
const context = new Context({
name: 'myName',
debug: {
enabled: true,
},
});
The following parsers are applied by default:
Context header: podium-requested-by
Each layout must have a given name to make it more easily human identifiable.
This name value is then passed on from the layout to any podlets in the
podium-requested-by
context header which is generated by running this parser.
The parser takes a string (required) as the first argument to the constructor.
Example:
import Context from '@podium/context';
const context = new Context({
name: 'myName',
});
Context header: podium-debug
Indicates to podlets when the layout server is in debug mode.
This parser takes an optional config object with the following properties:
option | default | type | required | details |
---|---|---|---|---|
enable | false |
boolean |
false |
Indicates whether layout is in debug mode or not |
This config object is passed on to the debug
argument on the context object
constructor.
Context header: podium-locale
Locale of the requesting browser. When executed by .process()
, this
parser will look for a locale property at HttpIncoming.params.locale
.
If found, this value will be used. If not found, the default locale will
be used.
const context = new Context({
name: 'myName',
});
const app = express();
app.get(await (req, res) => {
const incoming = new HttpIncoming(req, res, {
locale: 'nb-NO',
});
const incom = await context.process(incoming);
[ ... snip ...]
});
This parser takes an optional config object with the following properties:
option | default | type | required | details |
---|---|---|---|---|
locale | en-US |
string |
false |
A bcp47 compliant locale String |
This config object is passed on to the locale
property on the config object
in the constructor.
Context header: podium-device-type
A guess at the device type of the requesting browser. Guessing is done by UA detection and is not guaranteed to be accurate.
The output value will be one of the following strings:
-
desktop
: The device requesting the podlet is probably a desktop computer or something with a large screen. This is the default if we're not able to determine anything more detailed. -
tablet
: The device is probably a tablet of some sort, or a device with a smaller screen than a desktop. -
mobile
: The device is probably a phone of some sort, or a device with a smaller screen than a tablet.
This module will internally cache its result and the matching UA string in an LRU cache for faster lookup.
This parser takes an optional config object with the following properties:
option | default | type | required | details |
---|---|---|---|---|
cacheSize | 10000 |
number |
false |
Number of UA Strings to keep in the LRU cache |
This config object is passed on to the deviceType
property on the config
object in the constructor.
Context header: podium-mount-origin
URL origin of the inbound request to the layout server. The parser will try to parse this value from inbound requests to the layout server. It is also possible to override the value using config.
The value is a WHATWG URL compatible origin (illustrated overview).
This parser takes an optional config object with the following properties:
option | default | type | required | details |
---|---|---|---|---|
origin | null | string |
false |
Origin string that, if present, will override the origin found on request |
Example:
import Context from '@podium/context';
const context = new Context({
name: 'myName',
mountOrigin: {
origin: 'https://example.org/',
},
});
Context header: podium-mount-pathname
URL pathname specifying where a layout is mounted in an HTTP server.
The value is a WHATWG URL compatible pathname (illustrated overview).
This parser takes an optional config object with the following properties:
option | default | type | required | details |
---|---|---|---|---|
pathname | '/' | string |
false |
Pathname specifying where a Layout is mounted in an HTTP server |
Example:
import Context from '@podium/context';
const context = new Context({
name: 'myName',
mountPathname: {
pathname: '/my/path/name',
},
});
Context header: podium-public-pathname
URL pathname indicating where a layout server has mounted a proxy to proxy public traffic to podlets.
The full public pathname is built up joining pathname
and prefix
where
pathname
is the pathname to where the proxy is mounted into the HTTP server
and prefix
is a namespace isolating the proxy from other routes defined under
the same pathname.
Often pathname
will be the same value as mount pathname.
The value is a WHATWG URL compatible pathname (illustrated overview).
This parser takes an optional config object with the following properties:
option | default | type | required | details |
---|---|---|---|---|
pathname | '/' | string |
false |
Pathname where a Proxy is mounted in a HTTP server |
prefix | 'podium-resource' | string |
false |
Namespace used to isolate the proxy from other routes under the same pathname |
Example:
import Context from '@podium/context';
const context = new Context({
name: 'myName',
publicPathname: {
pathname: '/my/custom/proxy',
prefix: 'proxy',
},
});
Copyright (c) 2019 FINN.no
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.