Shunting the request/response cycle through custom handlers
Router
Preliminaries
This note describes how the server is configured to route dynamic resource requests.
The server can handle both static and dynamic resource requests.
A static resource request is defined as a request to receive, save, or delete the contents of a file of unvarying content. By contrast, a dynamic request is defined as a request to receive, save, or delete data of varying content, which is not serialized in a single file.
Sometimes static files are easy to recognize: images, audio, video, fonts, etc. are all examples. Other times static files are known to be static only to the webmaster: HTML documents, CSS style sheets, and JavaScript scripts outwardly appear to be static, but in fact may be generated on-the-fly by the server.
Some types of resource requests can't be fulfilled with static files. HTTP POST
requests, for example, must be handled in an application specific way. HTTP GET
requests that retrieve values from a database, are another example. These are common examples of dynamic requests.
When a static request is made to the server, standard HTTP protocol rules apply. The request's resource path and method determine what to process and how to process it: the resource path points to a file, and the GET
, PUT
or DELETE
method is applied in a well-regulated way.
When a dynamic request is made to the server, HTTP protocol guidelines still dictate the rules-of-the-road, but no code exists on the server to actually carry out the request. This is where the router fits in.
The router shunts dynamic requests to plugin modules.
Normally, incoming requests work their way through a sequence of request handlers which check the validity of the request, then work their way through a sequence of response handlers to format the payload and its metadata into HTTP compliant responses. When a dynamic request is received, the router intercepts the processing cycle midway — between the request handlers and response handlers — and shunts the processing to the configured plugin handler.
The plugin handler is provided with pointers to the data structures that contain the request headers, payload, and response headers. What it does with them and how it does it, is application specific.
Configuration
The server's plugins
section contains a router
subsection which comprises a collection of entries, where each entry has a path-pattern, a list of applicable methods, and a plugin name.
Unlike most other configuration sections, all defined path patterns are scanned, in top-to-bottom order, allowing multiple plugins to chain their handling of requests. Refer to the separate note regarding Path Patterns for a description of how patterns are matched.
The list of applicable methods is a comma-separated list of HTTP methods which will be honored by the router.
The plugin name is a reference to a user-defined plugin name. Read more about declaring and configuring plugins in the separate note about Plugins.
During processing, the entries in the router section are scanned from top to bottom, and each matching entry triggers the use of the declared plugin. If the incoming request's path and method do not match any resource-pattern/method combination, the router returns control to the normal request/response cycle and the request is handled as a static file.
Placement
The router
configuration section may appear in either the server/plugins
section or a host/plugins
section: merging is not supported. If values are placed in the host/plugins
section they will be used in their entirety; if not, the values in the server/plugins
section, if any, will be used as a fallback.
Information Headers
When a resource-pattern/method matches a request, but the plugin module is not reachable at its declared location, a rw-router-missing-module
information header is added to the response and it returns with status code 500
.
EBNF
SP | ::= | U+20 |
CR | ::= | U+0D |
ASTERISK | ::= | U+2A |
HYPHEN | ::= | U+2D |
QUESTION-MARK | ::= | U+3F |
SOLIDUS | ::= | U+2F |
GRAVE-ACCENT | ::= | U+60 |
LEFT-CURLY-BRACKET | ::= | U+7B |
RIGHT-CURLY-BRACKET | ::= | U+7D |
file-system-chars | ::= | (ALPHA | DIGIT | †)* |
wildcards | ::= | ASTERISK | QUESTION-MARK |
path-pattern | ::= | (SOLIDUS | file-system-chars | wildcards)* |
delimited-path-pattern | ::= | GRAVE-ACCENT path-pattern GRAVE-ACCENT |
method-name | ::= | 'HEAD' | 'GET' | 'PUT' | 'DELETE' | 'POST' | 'PATCH' | 'OPTIONS' | 'TRACE' |
methods-attribute | ::= | ASTERISK 'methods' EQUALS-SIGN (method-name COMMA)* |
plugin-name | ::= | (ALPHA | DIGIT | HYPHEN)* |
plugin-attribute | ::= | ASTERISK 'plugin' EQUALS-SIGN plugin-name CR |
router-entry | ::= | delimited-path-pattern SP methods-attribute SP plugin-attribute CR |
router-section | ::= | 'router' SP LEFT-CURLY-BRACKET CR router-entry* RIGHT-CURLY-BRACKET CR |
† Legal file system characters vary by platform
Cookbook
Example 1: Routing GET requests to built-in Blue plugin
server {
plugins {
router {
`*.blue` *methods=GET,HEAD *plugin=rwserve-blue
}
}
}
Example 2: Routing GET requests to built-in Counters plugin
server {
modules {
counters on
}
plugins {
router {
`/rwserve/counters/*` *methods=GET *plugin=rwserve-counters
}
}
}
Example 3: Routing POST requests to builtin RBAC-Auth plugin
server {
modules {
rbac on
}
plugins {
router {
`/login-logout/*` *methods=POST *plugin=rwserve-rbac-auth
}
}
}
Example 4: Routing POST requests to builtin policy report handler
server {
modules {
cross-origin on
policies on
}
request {
cross-origin {
`/notifications` *origin="domain.tld" *headers=content-type *methods=POST
}
}
plugins {
router {
`/notifications` *methods=POST *plugin=rwserve-policy-reports
}
}
}
Example 5: Routing PATCH requests to custom plugin
NOTE: The custom-patch-handler
is exemplary only.
server {
plugins {
router {
`/api/*` *methods=PATCH *plugin=custom-patch-handler
}
}
}
Review
Key points to remember:
- The request/response cycle can be interrupted by the router and shunted to plugin modules for handling.
- Plugin handlers are called in top-to-bottom order, allowing multiple plugins to chain their handling of requests.