OpenFlax API 1.0 Draft

Please note that this document is a draft. Until such time as it becomes 'final', the API is subject to change wildly. After such time as it becomes 'final', OpenFlax is obliged to provide a compatibility layer for OpenFlax API 1.0.

Handlers

To add functionality to an OpenFlax web server, a handler must be written. A handler is a callback module which conforms to the openflax.handler behaviour. Writing a handler is similar to writing a CGI or an ESI.

In order for the functionality of a handler to actually be used by the web server, the handler must be given its own section in a configuration file, and a dispatch rule response must point to the handler.

By convention, the handler's module name follows the pattern openflax.mod.*, although this is not a hard and fast rule. The handler module can be placed anywhere in the Erlang search path.

Types

conf()

A conf() represents a configuration dictionary. Such a dictionary is typically drawn primarily from a configuration file, although it is important to remember that it may also contain settings drawn from various sources, including:

As a dictionary, a conf() contains a set of settings, mapping each settings' key to its value. (These terms are described in greater depth in the Configuration File Format documentation.)

Handlers communicate with OpenFlax and with each other system by reading and writing settings in a conf(). Settings within the conf() can be accessed and manipulated with the functions exported by the openflax.conf module.

Each function in a handler accepts at least a conf() among its arguments, and is expected to return a conf(). Often (although not always) it can simply return the same conf() as was passed to it.

The conf() that is passed to each function is generally (although not always) the conf() that was returned from the previous function that was called.

Initially, this conf() is generally constructed by reading the configuration files specified on the command line. Note however, that while there may be many configuration files and sections within configuration files, there is only one conf() that forms the basis for conf()s passed to and fro within OpenFlax; settings in this conf() override (or accumulate into) other settings when multiple conf()s, such as from two different sections, are merged.

response()

A response() is an abstract representation of a desired HTTP response from the web server. A response() may be specified directly in the cfg_openflax_dispatch setting in the configuration (with the exception of streams, which cannot be specified in dispatch rules) and/or returned by a handler.

  response()    = error()
                | relocate()
                | serve()
                | stream()
                | handler()

  error()       = not_found
                | not_implemented
                | method_not_allowed

  relocate()    = {moved_temporarily, location()}
                | {moved_permanently, location()}
                | {permanent_new_domain, domain()}
                | {temporary_new_domain, domain()}

  serve()       = {content, data()} 
  data()        = [byte()] | binary()
  byte()        = 0 <= integer() <= 255

  stream()      = {stream, pid(), size()}
  size()        = integer() | unknown

  handler()     = module_name()
  module_name() = atom()
  

A standard HTTP error response can be indicated by any of the atoms not_found, not_implemented, method_not_allowed.

An HTTP redirection response can be indicated by {moved_permanently, location()} or {moved_temporarily, location()}, where location() is a string (URI or URL). A redirection to an analogous URI at another domain name can be indicated with {permanent_new_domain, domain()} or {temporary_new_domain, domain()} or where domain() is a string (domain name, eg "foo.bar.baz").

An indication to use a (different) handler can be given by naming the module. Note that when this is passed back from an already-executing handler, the new handler will not have an opportunity to read its own conf stuff from the config file.

An indication to serve some content to the user agent can be given with {content, data()}. data() is a string or a binary which is sent to the user agent verbatim. HTTP headers should be present in the conf() which accompanies this response; if important headers such as res_content_type are not present, reasonable defaults will be assumed.

In addition, content can be served to the user by streaming it from the handler to OpenFlax. This is recommended for content which is too large to be efficiently passed all at once in a {content, data()} response.

Streaming content is indicated with the response {stream, pid(), size()}. This implies that the handler has already spawned a streaming process to stream the content, and that the pid() is the pid of that process. If the size of the content is known, it should be stated (in octets) as size(); if it is not known, the atom unknown should be passed instead. The response will be altered accordingly, cancelling the possibilities for a cacheable response or a persistent connection.

The streaming process should follow the following protocol:

The streaming process should wait for the message {pid(), stream_open} before it begins. The pid() in this message is the pid of the receiving process internal to the OpenFlax web serving logic, and which should be the destination of all subsequent messages from the streaming process.

The streaming process should then send the receiving process a series of messages of the form {self(), stream_data, data()}. After each message, it should wait for a message {pid(), stream_acknowledge, data} before proceeding to send the next message, to keep the stream in synch. data() is as described above in {content, data()}, except that it need not be the entire content of the response, naturally. self() is the pid of the streaming process and pid() should be the pid of the receiving process.

When the streaming process is finished, it should send {self(), stream_close} to the receiving process. It may then wait for a response of the form {pid(), stream_acknowledge, close}, at which point both processes know that they are done, and must stop communicating. The streaming process may then exit.

For an example of streaming with a known size, see openflax.mod.file. For an example of streaming with an unknown size, see openflax.mod.watch.

Behaviour

The openflax.handler behaviour requires that the handler implement the following functions.