The foswiki svn repository will become read-only on Friday 8/8. Developers should register for a http://github.com/ account for commit access to foswiki.

Foswiki Stand Alone

Introduction

There are many ways to run web applications, including CGI, mod_perl (for Perl apps), and FastCGI. Each of them has its strengths and weaknesses, and interacts in different ways with the application.

Foswiki Standalone is a code architecture design that makes Foswiki independent of execution technology: It has an abstraction layer to manage retrieving the server request information, sending the response back to the web server, and interacting with the web server, regardless of the specific execution environment mechanism that is being used.

Execution Environments for Web Applications

CGI - Common Gateway Interface

The Common Gateway Interface protocol is simple to use (both when developing applications and installing/configuring them with the web server) as it is independent of programming language, web server architecture and operating system, making CGI apps very portable. It's also very mature and stable, and is broadly available with many web hosting services. However, it has serious performance issues: For each request, the web server forks a new process and loads the application; the application reads its configuration, perform its initialization tasks, answers the request and terminates; and finally, the web server sends the response back to the client. For applications written in interpreted languages, such as Foswiki, written in Perl, there is one more step: read, parse and pre-compile/interpret the code, before the web application begins. All this overhead is resource-intensive, which can lead to poor performance, particular during periods of high access.

Web server API

Some web servers have an API that makes it possible for application developers to extend the web server itself to run web applications within the web server process. This approach leads to much better performance than CGI, since there is no overhead in forking a new process. In addition, the integrated web app can interact with phases of web server request processing other than response generation, which cannot be done with a separate web app process: for example, applications can handle URI translation, authentication, or authorization phases.

Web apps using the web server API approach have limitations that limit their portability. The API is not the same from one web server to another, and it can be different even with different versions of the same web server. Thus a web app using a web server API can be tightly coupled to that web server and its request processing architecture. Also, the app must be written (or interact) with the language bindings provided by the web server API. Another problem is that a single buggy application can compromise the entire web server, since they run within the same memory space. (mod_perl is the Perl interface to the Apache web server API.)

FastCGI

FastCGI was developed to address some issues when choosing between CGI and a web server API: Applications are separate processes from web server, as with the CGI approach, but they are persistent, rather than being created for each request. There is no overhead in forking a new process for each request, a buggy application doesn't compromise the whole web server, applications are independent of the web server and its internals, and there is no restriction on the programming language used. FastCGI also provides a way to permit applications to interact with the web server in phases other than response generation: authentication/authorization and response filtering. Because of its benefits, FastCGI is broadly available at hosting services, and many web servers support it.

Note that although each of these technologies have different interfaces to web applications, their underlying intent is the same: feed an application with request information, and return a response generated by the app to the client.

Abstraction Classes for Interactions with the Web Server

CGI makes request info available through environment variables and standard input and the application is expected to generate the response and send it through its standard output. mod_perl applications take request info and send response through web server API. FastCGI applications receives request info from a socket — environment variable values followed by request data — and sends the response through the same socket.

With the Foswiki architecture, there are two classes, Foswiki::Request and Foswiki::Response, that hold request info and the generated response, no matter what underlying technology is used. A runtime engine populates the information in a Foswiki::Request object, and after Foswiki has filled in a Foswiki::Response object, sends the information from the response object to the web server. There is a specific runtime engine for each supported execution environment mechanism that deals with the specific protocol or API required for that execution environment.

  • The interface of Foswiki::Request is compatible with CGI.pm with regards to methods to retrieve request information, for historical reasons. It is a singleton, available from $session->{request} or Foswiki::Func::getCgiQuery.
  • Foswiki::Response is also a singleton, available from $session->{response}.
  • A third class, Foswiki::LoginManager::Session, is used in place of CGI::Session to make sessions available under execution technologies other than CGI.

Runtime Engines

Runtime Engines are the glue between the execution technology and Foswiki, so they are the start and end points of Foswiki execution. Engines are in charge of filling the Foswiki::Request object and sending data from Foswiki::Response, using the specific way of the corresponding execution technology.

Each engine must extend the base class called Foswiki::Engine, which is composed of many small methods, grouped into two phases:
  • The prepare phase incrementally fills the Foswiki::Request object.
  • The finalize phase sends data from Foswiki::Response and performs any needed cleanup.

Prepare phase

The Prepare phase is composed of:
prepareConnection
should fill method, remote_address, server_port and secure fields.
prepareQueryParameters
should fill param field with data from query string unless method is POST.
prepareHeaders
should fill headers and remote_user fields and maybe update remote_address (in the case of a X-Forwarded-For header)
prepareCookies
should fill cookies fields. Foswiki::Engine base class provides a good default. Engines probably won't implement this method.
preparePath
should fill action, path_info and uri fields.
prepareBody
should perform any initialization tasks related to body processing.
prepareBodyParameters
should fill param field with data from HTTP request body (data sent with POST), except uploads.
prepareUploads
should fill uploads field with Foswiki::Request::Upload objects, tied to uploaded files.

Finalize phase

After the Prepare phase, all needed information to process the request is available and Foswiki::UI::handleRequest is called. It returns a Foswiki::Response object used in the Finalize phase:
finalizeUploads
should delete any temporary files created in preparation phase.
finalizeHeaders
should send to web server the response headers. Engines should call SUPER::finalizeHeaders, since the base implementation takes care of some details, like cookies and HEAD handle.
finalizeBody
should send the body field of Foswiki::Response object to the web server. This method calls write() as needed, so engines should redefine that method instead of this one.
prepareWrite
this method is called by finalizeBody before the first write attempt, so engines have the opportunity to perform any tasks required to begin sending response body. It's all right if no tasks are needed.
write
it receives a buffer as parameter and should send it to the web server. Engines must implement this method.

Currently Supported Environments

Historical Background

Foswiki is based on a product designed to run as a CGI script. To maintain compatibility with the legacy software base, the Foswiki::Request interface is CGI.pm compliant with regards to retrieving request information. Various assumptions are made when running as a CGI script:
  • There is one process per request.
    • No problem in leaking memory, since all memory is returned to the operating system after process is finished.
    • No problem holding request-specific data in global variables.
    • No problem at all in using global variables.
    • No problem in "forgetting" an open file handle.
    • The impact of rare crashes is limited.
  • CGI object accessible via $session->{cgiQuery} can be used to generate HTML.

The introduction of the Foswiki Standalone Architecture was the first step in improving Foswiki's integration with execution environments other than CGI. Abstracting away the execution environment however breaks the previous assumptions:
  • $session->{cgiQuery} is no longer intended to be a CGI object and should not be used to generate HTML.
    • To reduce the impact on legacy code, Foswiki::Request currently subclasses from the CGI module. This should not be relied upon to be true in the future.
  • Use of global variables (particularly an issue with non-core extensions) can lead to unexpected behavior with persistent engines.
  • Memory leaks or failure to release other resources such as open file handles can lead to DoS and server crashes.
  • If there is a crash, all subsequent requests sent to the same persistent process will not be handled, requiring manual intervention.

Developers must keep these new assumptions in mind. In particular, care must be taken that all resources are released when they are no longer needed — for memory, this means eliminating all references to it so it can be garbage collected.

This architecture is not finished yet: though stable, it is still a work in progress.

Note to Extension Developers

Since environments other than CGI are supported, scripts under the bin/ directory are not necessarily start points. Foswiki uses $Foswiki::cfg{SwitchBoard} to determine what method implements each action. Examples of actions are: view, edit, save, etc.

ALERT! Foswiki 1.0.5 changed the format of SwitchBoard entries to make it self-documenting and easier to extend. The original format is still supported, but deprecated (use it only to make your extension usable in Foswiki 1.0.0-1.0.4 and 1.0.5 and newer).
Examples of old $Foswiki::cfg{SwitchBoard} entries are:
$Foswiki::cfg{SwitchBoard}{view} = [ 'Foswiki::UI::View', 'view', { view => 1 } ];
$Foswiki::cfg{SwitchBoard}{edit} = [ 'Foswiki::UI::Edit', 'edit', { edit => 1 } ];
$Foswiki::cfg{SwitchBoard}{save} = [ 'Foswiki::UI::Save', 'save', { save => 1 } ];

Each action has:
  • A name, that is used as key in $Foswiki::cfg{SwitchBoard} hash
  • An array reference to three values:
    1. The module that implements the action.
    2. The function in the module to be called.
    3. A hashref to be used as initial context.

Examples of the new format:

$Foswiki::cfg{SwitchBoard}{view} = {
    package => 'Foswiki::UI::View',
    function => 'view',
    context => {view => 1},
};
$Foswiki::cfg{SwitchBoard}{edit} = {
    package => 'Foswiki::UI::Edit',
    function => 'edit',
    context => { edit => 1 },
};
$Foswiki::cfg{SwitchBoard}{save} = {
    package => 'Foswiki::UI::Save',
    function => 'save',
    context => { save => 1 },
    allow => { POST => 1 },
};

Each entry is a hashref, instead of an arrayref. Fields are:
  • package - perl package that contains the method for this request
  • function - name of the function in package
  • context - hash of context vars to define (See note below)
  • allow - hash of HTTP methods to allow (all others are denied)
  • deny - hash of HTTP methods that are denied (all others are allowed)
    • ALERT! deny is not tested if allow is defined

The method is called with an initialized $session object as parameter.

If your extension provides an action, you should add an entry to $Foswiki::cfg{SwitchBoard} (see SpecifyingConfigurationItemsForExtensions). The provided bin/ script should look like other core scripts and adjust $ENV{FOSWIKI_ACTION} accordingly.

Setting Context

Contexts are defined in IfStatements#Context_identifiers. If your extension replaces or adds an equivalent script to the standard scripts provided with Foswiki, it should set the appropriate context in its switchboard entry. Example: A compare script that replaces the rdiff script should set the diff context so that skins and plugins know that they are running in an equivalent script. A genpdf script that provides a equivalent of the view script should set the view context.

Topic revision: r19 - 15 Jan 2010, PhilJ
 
The copyright of the content on this website is held by the contributing authors, except where stated elsewhere. see CopyrightStatement. Creative Commons License