The Darlean Web Gateways Service makes it possible for actors to handle incoming HTTP requests. It allows applications to provide scalable, redundant web services like API’s and static web sites and to expose the actors that implement the business logic to the outside world.
Introduction
With the Darlean Web Gateways service enabled, Darlean acts as a gateway for HTTP requests. A configurable mapping determines which actors are invoked for which URL’s.
Actors have full access to the request headers, query parameters, cookies, and of course the request body. The request body is either passed in one chunk (for sufficiently small requests), or the actor can repeatedly ask for more chunks.
Actors can set all response headers (including cookies) and return a response body. The response body is either passed in one chunk (for sufficiently small responses) or it can be streamed in multiple chunks to the web gateway.
Convenience classes are available for parsing and validation of JSON requests, returning JSON responses, returning static files, et cetera.
Architecture
The below figure shows a typical web gateways architecture.
On the left, you have a client that makes web requests. This can be a user that visits a website via the browser, or the web site or an external application that makes HTTP/HTTPS API requests. These requests are usually sent to a load balancer or reverse proxy that is provided by the infrastructure provider (like AWS Load Balancing or Azure Load Balancing).
At the moment, the Darlean Web Gateways service only supports HTTP, so the load balancer must be configured to properly handle HTTPS.
The load balancer routes an HTTP request to one of the Darlean Web Gateways, typically in a round-robin fashion. The Web Gateways service runs on one or more Darlean runtime applications, and listens on a configurable port number for incoming HTTP connections.
For smaller applications, the runtime application can be combined with the customer application. The downside is a tighter coupling between gateway and application logic, which is in general less secure than having separate applications for this.
The web gateways are configured with a mapping from URL path to the actor type or the actor that should handle the request and the name of the action that should be invoked to handle the request.
The actors handle the request, and convert the results back into a response, which is returned to the Web Gateway that sends it back to the client (via the load balancer).
Service actors
It is good practice to decouple business logic (usually in the form of application actors) from the HTTP interpretation logic. The figure below illustrates the recommended practice of shielding the applications actors from the outside world. It is based on the principles of clean architecture by having two actors that surround the application actors: an application service actor and a gateway service actor.
A gateway service actor is an actor that usually has type multiplar (which means that multiple instances can exist within the cluster) which means that it is scalable. It receives an HTTP request, parses and validates the request and performs any necessary authorization. A gateway service actor should focus on the protocol (that is, interpreting the HTTP request). It should not contain any business logic. It should parse and validate HTTP requests and convert them into the (data transfer) objects understood by the business logic, and it should invoke the business logic, but it should not implement the business logic.
On the very right of the figure, we have application actors, which are usually regular (singular typed) actors that perform (implement) the actual business logic. Conform the principles of clean architecture, they should have no direct relationship with or knowledge of the HTTP layer. They should just focus on the business logic.
As glue between a gateway service actor and one or more application actors, an application service actor could be defined. Like the gateway service actor, this typically is a multiplar, but then with focus on the business logic (not on the protocol). It acts as an orchestrator around the individual application actors. This removes dependencies from the gateway service actor on the application actors that implement the business logic, which allows developers to refactor the application actors (e.g., split them up or combine or rename them) without impacting the gateway service actor (which should be unaware of business logic details).
This layered approach provides nice decoupling between the responsiblities of (a) HTTP request handling (including authorization), (b) business logic orchestration/encapsulation and (c) the actual business logic implementation.
Configuration
An example configuration file is shown here:
{
runtimeApps: ['server01', 'server02', 'server03'],
runtime: {
webGateways: {
gateways: [
{
id: 'default',
port: 8080,
actorType: 'WebApiService',
handlers: [
{ path: '/ask/*/', actionName: 'ask' },
{ path: '/teach', actionName: 'teach' },
{ path: '/*', actionName: 'file' }
]
}
]
}
}
}
The example is taken from our tutorial in which we make an oracle that is capable of learning new facts and answering questions about previously learned facts.
In this configuration file, we have defined one gateway that listens on port 8080. By default (when a handler does not specify otherwise), the web gateway invokes action methods on a WebApiService
actor type.
The handlers
specify what should happen for a certain path. When the path matches with /ask/*/
, the ask
action method is invoked on the WebApiService
actor. Similar for /teach
, but this holds for the exact match (no wildcard present).
For all other paths ('/*
), the file
action method is invoked on the WebApiService
actor type. As can be seen in the implementation, a StaticFileHandler class is used to serve static files (in this case, the HTML, JS and CSS files that make up the web application). When no file is found, a 404 error is returned.
Implementing a handler
A handler is nothing more than an action method on an actor with the same name as specified in the configuration.
Action methods must have the following signature:
public async ask(req: IWebGatewayRequest): Promise<IWebGatewayResponse> { ... }
The request object
As argument, they receive a IWebGatewayRequest. That is an object with the following properties:
body
– Optional buffer that contains the raw request body.cookies
– Optional array of raw cookie strings (one per cookie)headers
– Optional dictionary of header fieldshostname
– ???method
– The HTTP method verb (GET
,POST
, et cetera)path
– String that contains the pathplaceholders
– Dictionary with the values at the place of the wildcard characters in the path filter expression. The first match is named'*'
, the second'**'
, and so on – unless explicit placeholder names are configured.port
– The port number for the requestprotocol
– The protocol for the requestsearchParams
– Dictionary with the query search parametersurl
– The full? urlusername
– The username part of the url.
TODO: Describe the mechanism of reading chunks
The response object
As a response, they return an IWebGatewayResponse. That is an object with the following properties:
body
– Buffer that contains the raw response bodycookies
– Dictionary of cookiesheaders
– Dictionary of response headersstatusCode
– Response status codestatusMessage
– Response status message.
TODO: Describe the mechanism of sending chunks