Web Server

The Webserver-project aims to build a robust and fast HTTP server which supports easy administration and rapid development of stunning web applications. This article gives a brief introduction to the concepts behind that server and shows how simple examples can be implemented using it’s service interfaces. The underlying framework makes heavy use of the Content Framework also published on this site.

Concepts

Content Tree

Configuration Tree

Dispatching

The core of the Webserver runtime is build around some simple interfaces. Every request that is processed correctly (ie. without throwing any exception) is handled by an implementation of IWebContext1:
public interface IWebContext extends IExtension { void process(IWebRequest request, IWebResponse response) throws WebException; }
This implementation is called by a request-dispatcher, an artifact which knows about finding the correct context for a specific request and which controls the complete lifecycle of a request. The framework provide a generic dispatcher which is covering all important use-cases but application developers are free to implement their own dispatcher whenever they need specific tayloring. The request-dispatcher does not need to be implemented as a seperate class, instead it may be useful to implement a single method. The following applies to that generic dispatcher.

Note: It is important to understand that all artifacts involved in dispatching a request are strictly compositable. Every step in dispatching may be executed for each node, to which the current request could be applied. Particularly every request forms a path within the content-tree in which (commonly) all steps are repeated for each node along that path.

Finding the Right Context

The job of finding the right context to serve a given request is delegated to one or more instances of IRequestMatcher:
public interface IRequestMatcher extends IExtension { boolean dispatch(IWebRequest request, IWebResponse response) throws WebException; boolean matches(IWebRequest request); INode getTargetContext(IWebRequest request); String getDefaultUrl(); }
First of all the request-matcher bound to the root-node of the content-tree given to the webserver is asked for dispatching the current request. That matcher now determines whether any child-node is applicable, in which case it is given chance to serve the request. Any matcher that finds that it could serve the current request but has no web-context attached may give the control back to its parent or raise an exception.

Transforming Requests and Responses

Prior to dispatching the current request, any pending transformations (either request- or response-transformations) are applied to the request. Pending transformations are always attached to the same node as the current matcher. Transformations are defined by either IRequestTransformation:
public interface IRequestTransformation { IWebRequest transform(IWebRequest request) throws WebException; }
or (analogous) by IResponseTransformation:
public interface IResponseTransformation { IWebResponse transform(IWebResponse response) throws WebException; }
Transformation of requests or responses is done primarily by delegation. Each transformation may return a specific class implementing the request-/response-interfaces which simply passes all method calls not affected by that transformation to the request/response which was handed over to the transform-method while altering those method-calls which are affected by the transformation.

Authentication

Between transformation and further dispatching an ISecurityHandler is given chance to intercept the request:
public interface ISecurityHandler extends IExtension { void checkAccess(IWebRequest request) throws WebException; }
The checkAccess() method should throw an exception in case it does not allow this request to be further dispatched.

Examples

The following examples should clarify the important parts of request-handling and provide you with some starting point for your own development with the Webserver framework.

Default Dispatcher

The default dispatcher is implemented as method of each request-matcher (see interface spec above) and is found in the abstract base-class RequestMatcher. It is defined as follows:
try { request = applyRequestTransformations(request, getNode()); checkAccess(request, getNode()); response = applyResponseTransformations(response, getNode()); if (dispatchNext(request, response, getNode())) { return true; } else { return processSelf(request, response, getNode()); } } catch (WebException e) { handleException(request, response, e, getNode()); return true; }
As you can see this method definition stricly follows the procedure described above. If a specific request-matcher has to redefine one or more of these steps, it could rely on the given methods or redefine them as needed.

Static-Content Context

This web-context publishes a static string. The fields as well as getters and setters are omitted here:
public class StaticContentContext extends WebContext { public void process(IWebRequest request, IWebResponse response) throws WebException { response.status(OK) .header(CONTENT_TYPE, contentType) .content(toChannelBuffer(content.getBytes())); } }
Whenever the process method returns it is most important that one of the status() methods have been called to set the status of the response to a reasonable value. Nearly every other value may be guessed appropriately: The protocol for example is initially set to the protocol of the request.

Scripting Context


1 As said in the abstract, this framework uses the Content Framework and therefor most of the interfaces are extending IExtension so they can be managed by the Content Framework.