The Eiffel Web Server Gateway Interface

Status

This document is in draft status and is subject to change. The reader is forwarned! :-)

The current contents is about to undergo substantial changes in the coming days.

Last modification: Monday, August 1, 2011 (Paul Cohen)

Preface

This document specifies a proposed standard interface between web servers and Eiffel web applications or frameworks, to promote web application portability across a variety of web servers.

Note: This document is based on  PEP 333 -- Python Web Server Gateway Interface v1.0 but adapted to needs and properties of Eiffel.

Rationale and Goals

The most important goal of EWSGI is to enable an amazingly simple and wonderful programmer experience to the Eiffel developer who wants to write Eiffel server applications for the web!

Background

Other programming laguages like Java boast a wide variety of web application frameworks. Java's "servlet" API makes it possible for applications written with any Java web application framework to run in any web server that supports the servlet API. Python also boasts many frameworks like Zope, Quixote, Webware, SkunkWeb, PSO, and Twisted Web -- to name just a few. This wide variety of choices was a problem for new Python users, because there was no standard protocol or convention for how webservers were to interact with Python applications and frameworks and this meant that the frameworks only worked with some webservers and not with others. To resolve this the  PEP 333 -- Python Web Server Gateway Interface v1.0 was written and subsequently implemented in many webserver environments and in Python frameworks.

The availability and widespread use of such an API in web servers for Eiffel, whether those servers are written in Eiffel (e.g. Nino), embed an Eiffel module in Apache (e.g. mod_ewsgi), or invoke an Eiffel application via a gateway protocol (e.g. CGI, FastCGI, etc.), would separate choice of framework from choice of web server, freeing users to choose a pairing that suits them, while freeing server developers as well as developers of higher level web application framworks and tools, like AJC (Alex's Eiffel2JavaScript), to focus on their preferred area of specialization.

This document, therefore, proposes a simple and universal interface between web servers and Eiffel web applications and frameworks: the Eiffel Web Server Gateway Interface (EWSGI).

Simplicity and extendability

But the mere existence of a EWSGI spec does nothing to address the existing state of servers and frameworks for Eiffel web applications. Server and framework authors and maintainers must actually implement EWSGI for there to be any effect. However, since no existing servers or frameworks support EWSGI, there is little immediate reward for an author who implements EWSGI support. Thus, EWSGI must be easy to implement, so that an author's initial investment in the interface can be reasonably low.

Thus, simplicity of implementation on both the server and framework sides of the interface is absolutely critical to the utility of the EWSGI interface, and is therefore the principal criterion for any design decisions. Note, however, that simplicity of implementation for a framework author is not the same thing as ease of use for a web application author. EWSGI presents an absolutely "no frills" interface to the framework author, because bells and whistles like response handlers and cookie handling would just get in the way of existing frameworks' handling of these issues. Again, the goal of EWSGI is to facilitate easy interconnection of existing servers and applications or frameworks, not to create a new web framework.

It is expected that once there are working EWSGI implementations, people will start writing or porting existing Eiffel web frameworks to EWSGI. Such web frameworks may provide facilities like chained handlers, uri-based handlers or integration with AJC (Alex's Eiffel2JavaScript). Such high level framework features are not part of EWSGI.

Abstractions

An important goal of EWSGI in providing a simple and universal interface between web servers and Eiffel web applications, is to have the right level of abstractions and to hide web server and gateway protocol specific details from the Eiffel developer. One consequence of this is that EWSGI does not provide direct access to the web server, gateway or input and output streams. EWSGI presents the user with the notion of requests and responses and an environment very much along the lines of a CGI/Fast-CGI environment.

Some words on web server gateways

The  HTTP specification introduces the concept of a gateway, which is a server that acts as an intermediary between incoming HTTP requests and a backend server system that talks some other protocol. The two most common language independent standards for gateway communication are CGI and Fast-CGI. Both are based on the gateway server providing the following information for each HTTP request:

  • An environment which is set of key/value pairs with information about the request.
  • An input stream for reading the incoming HTTP entity body.
  • An output stream for writing the outgoing HTTP entity body of the HTTP response.

In CGI this is done via process environment variables and the standard process file descriptors stdin and stdout. In Fast-CGI sockets are used to send this information to a running backend server process.

Specific web servers provide other ways for integrating the backend server more tightly either as a shared library or by actually compiling the web server together with the backend server. For example Apache provides the possibility of writing Apache modules that are "plugged in" to the Apache architecture.

Specification overview

Gateways and EWSGI implementations

EWSGI specifies a uniform interface for writing backend server applications in Eiffel that can be deployed with different web (gateway) servers using different gateway protocols and integration technologies. EWSGI hides the specific details of different web servers, gateway protocols and integration technologies and provides a uniform interface to the Eiffel developer. There can be many different implementations of EWSGI, but all EWSGI compliant implementations should allow an Eiffel developer to move his code between implementations and recompile without modifications.

The web (gateway) server is considered a client of the EWSGI implementation. The EWSGI implementation in its turn acts as a client to the end user (developers) EWSGI application. In order modularize the handling of different web servers and gateway protocols EWSGI implementations should implement different so called EWSGI connectors for each web server or gateway protocol. The EWSGI implementation can be seen as an controller of communication between the web (gateway) server and the EWSGI application.

As a client, the web (gateway) server must pass information about HTTP requests to the EWSGI implementation and provide a way for the EWSGI implementation to pass back information about HTTP responses. EWSGI, in turn, as a client to the EWSGI application must provide information about the request in and be able to get information from the EWSGI application about its response.

All information about the HTTP request is packaged by the EWSGI implementation in an EWSGI_REQUEST object and all information about the HTTP response is packaged in an EWSGI_RESPONSE object. These are provided to the EWSGI application via a single feature in the class EWSGI_APPLICATION:

    response (request: EWSGI_REQUEST): EWSGI_RESPONSE is
            -- The response to the given 'request'.
       deferred
       ensure
           Result.status_is_set
           Result.ready_to_transmit
       end

It is the responsability of the ESWGI implementation to:

  1. Create and initialize a EWSGI_REQUEST object that contains features for accessing the environment (CGI-style dictionary of values) and for reading the message body of request. How these EWSGI_REQUEST objects are created and initialized is handled by EWSGI connectors which are specific for a given gateway protocol or integration technology.
  2. Pass EWSGI_REQUEST objects to the EWSGI application via the above 'response' feature.
  3. On return from the 'response' call, use the returned EWSGI_RESPONSE object to return a HTTP response message to the web server. The web server in turn is responsible for sending the HTTP response message to the original client that sent the HTTP request!
  4. Hide all connector details from the EWSGI application.

It is the responsablity of EWSGI applications to:

  1. Write a subclass of EWSGI_APPLCIATION and implement the 'response' feature. In this feature a EWSGI_RESPONSE object must be created and it must have its status set and be marked as ready to transmit before it is returned.
  2. In those cases where large message bodies will be created the developer may not want to use the default buffered message body available in EWSGI_RESPONSE. The developer must then subclass EWSGI_RESPONSE and reimplement the 'read_block' feature and decide how large blocks should be returned to the EWSGI implementation.

Some comments on output handling

Buffered and block-based output

The first thing to realize about framework-based web applications is:

a) Generally speaking, applications will achieve the best throughput by buffering their (modestly-sized) output and sending it all at once. This is a common approach in existing web frameworks in other languages. The output is buffered in a STRING or similar object, then transmitted all at once, along with the response headers.

b) For large files, however, or for specialized uses of HTTP streaming an application may need to provide output in smaller blocks (e.g. to avoid loading a large file into memory). It's also sometimes the case that part of a response may be time-consuming to produce, but it would be useful to send ahead the portion of the response that precedes it. The ouput therefore can not be buffered but must be handled some other way. Se below for how this is solved.

Application versus framework driven output handling

Initial EWSGI design drafts have assumed that the application must be given a handle to the ouput stream by EWSGI and that the application should be responsable for writing the entire response to this output stream. This approach can be referred to as "application driven output handling".

The main problem with "application driven output handling" is that it puts the responsibility of formatting the output (HTTP messages) in the hands of the application developer. Futhermore there are differences in how different gateway servers expect the gateway applications to return output. For example, in non-NPH CGI applications the output must not contain a HTTP start line, but should instead use the HTTP header "Status" to return the HTTP status code. Such minor differences in gateway implementations are exactly what EWSGI intends to free the developer from having to know about and resolve. The most important goal of EWSGI is to make it easy to write web applications in Eiffel!

In WSGI (Python) the application returns an iterator (often a generator-iterator) that produces the output in a block-by-block fashion. These blocks may be broken to coincide with mulitpart boundaries (for "server push"), or just before time-consuming tasks (such as reading another block of an on-disk file). This approach is made easy by Pythons support for generator-iterators (with the yield construct). The WSGI approach can be referred to as "framework driven output handling". It has the advantage of freeing the developer from having to know about how to format the output (HTTP messages). It also enables better exception handling for applications using buffered output.

In EWSGI "framework driven output handling" can be achieved by providing the class EWSGI_RESPONSE with features for a) setting the "buffered output" and b) for retrieving the output. For the cases where the developer wants to use "block-based output", for example if the application will be generating very large entity bodies, the class EWSGI_RESPONSE can be subclassed and the developer can then implement his/her own block-based output version of the feature for retrieving the output. It is the EWSGI implementation that is responsable for querying the EWSGI_RESPONSE objects for their output and for then writing it to the appropriate output stream. The two main advantages with "framework driven output handling" over "application driven output handling" are:

  1. Since the framework has full control over the output stream it can guarantee that output is compliant with the HTTP specification and the gateway protocol currently being used. This is something it can't if it gives the application free access to the output stream. (Eg: the application developer may inadvertently intermix writing of HTTP headers and entity body contents to the output if he/she has access to the output stream and the developer will have to know if Apache CGI is being used in order to know if a HTTP start line should be written to the output.)
  2. Since the framework has full control over the output stream and waits for the application to return its response before writing anything to the output stream, it can catch exceptions in buffered output applications before any output actually has been written to the output stream and the web server. It can then output an appropriate server gateway error HTTP response.

Conclusions for EWSGI

So to summarize the important conclusions for EWSGI. It must:

a) Enable EWSGI applications to return buffered output. This will cover a majority of use cases and provide good throughput. Eg: as a STRING.

b) Enable EWSGI applications to return block-based output for those cases where the response body will be too large to keep in memory.

c) Specifically NOT provide the EWSGI application with direct access to the output stream in order to ensure creation of proper HTTP messages and good exception handling for applications using buffered output.

Writing an EWSGI application

Here we show what the end developer needs to do to make his/her application work as an EWSGI application.

With buffered output

Here is the Hello World example. The developer subclasses EWSGI_APPLICATION and implements the 'response' feature.

class APPLICATION

inherit
    EWSGI_APPLICATION -- THIS IS PART OF THE EWSGI SPECIFICATION!

creation
    make -- THIS IS PART OF THE EWSGI SPECIFICATION!

features

    response (request: EWSGI_REQUEST): EWSGI_RESPONSE is -- THIS IS PART OF THE EWSGI SPECIFICATION!
            -- The response to the given 'request'.
       do
            create Result.make
            Result.set_status ("200")
            Result.set_header ("Content-Type", "text/html; charset=utf-8")
            Result.set_message_body ("<html><body>Hello World</body></html>")
       end

end

With block-based output

The developer subclasses EWSGI_APPLICATION and implements the 'response' feature and subclasses EWSGI_RESPONSE and reimplements the 'make' and 'read_block' features.

class HELLO_STREAMING_WORLD

inherit
    EWSGI_APPLICATION
    
creation
    make
    
feature {NONE}-- Basic operations

    response (request: EWSGI_REQUEST): EWSGI_RESPONSE
        do
            create {HELLO_WORLD_RESPONSE} Result.make
            Result.set_status ("200")
            Result.set_header ("Content-Type", "text/html; charset=utf-8")
        end
    
end

And then the response class:

class HELLO_WORLD_RESPONSE

inherit
    EWSGI_RESPONSE
        redefine
            make,
            read_block
        end
    
creation
    make
    
feature {NONE} -- Initialization

  make is
        do
            precursor
            set_ready_to_transmit
            current_hello := 0
        end
    
feature {NONE} -- Entity body

    read_block
        -- Reads a block of 100000 lines of "Hello World". 
        local
            i: INTEGER
        do
            if current_hello >= 100000 then
                end_of_blocks := True
            else
                if current_hello = 0 then
                    current_block := "<html><body>%N"
                end
                from
                    i := 0
                until
                    i = 10000
                loop
                    current_block.append ("Hello World%N")
                    i := i + 1
                end
                current_hello := current_hello + i
                if current_hello = 100000 then
                    current_block.append ("</body></html>")
                end
            end
        end

    current_hello: INTEGER
      
end

A reference EWSGI application implementation

This specification tries to avoid specifying detailed implementation requirements. However, here is a possible implementation of the EWSGI_APPLICATION feature responsible for invoking the 'response' feature.

    process (environment: HASH_TABLE [STRING, STRING]; input: EWSGI_INPUT_STREAM; output: EWSGI_OUTPUT_STREAM) is
            -- Process new request.
        local
            rq: EWSGI_REQUEST
            rs: EWSGI_RESPONSE
            s: STRING
        do
            create rq.make (environment, input)
            rs := response (rq)
            if rs.ready_to_transmit then
                output.write (rs.headers)
                from
                    rs.read_block
                    output.write (rs.last_block)
                    output.flush
                until
                    rs.end_of_blocks
                loop
                    rs.read_block
                    output.write (rs.last_block)
                    output.flush
                end
            else
                -- Report internal server error. 
                -- Response not ready to transmit!
                -- Implementor of EWSGI_APPLICATION has not done his job!
                create rs.make
                rs.set_status ("500")
                rs.set_header ("Content-Type", "text/plain")
                rs.set_message_body ("Incomplete server implementation: Response not ready to transmit.%NTell the programmer to finish his/her job!")
                output.write (rs.headers)
                rs.read_block
                output.write (rs.last_block)
                output.flush
            end
        end

Note: The writing of headers and the message body to the output stream should be put into specific EWSGI connector classes, since the exact format of output depends on the specific gateway protocol being used.

Specification Details (Normative)

An EWSGI implementation must provide the following 3 classes:

  • EWSGI_APPLICATION.
  • EWSGI_REQUEST.
  • EWSGI_RESPONSE.

These classes are all that is needed to be known by an EWSGI application developer. The classes must have the interfaces as specified below.

Furthermore an EWSGI implementation must provide (internally) at least one gateway connector for deploying the EWSGI application. An EWSGI implementation need not support all of CGI, Fast-CGI, SCGI and mod_ewsgi, but if it does provide support for more than one mechanism it should be possible for the EWSGI application developer to easily choose which one to use. It can be done dynamically by providing the EWSGI application with features like 'use_cgi' and 'use_fastcgi', or via classes with the same name but with different implementations and located in different clusters. Eg:

.../cgi/ewsgi_application.e
.../fcgi/ewsgi_application.e
.../scgi/ewsgi_application.e
.../mod_ewsgi/ewsgi_application.e

EWSGI classes

These are the classes all EWSGI implementations must provide.

EWSGI_APPLICATION

class EWSGI_APPLICATION  

features

    make is
            -- Initialize the EWSGI implementation.
        do
        end

    response (request: EWSGI_REQUEST): EWSGI_RESPONSE is
            -- The response to the given 'request'.
       deferred
       ensure
           Result.status_is_set
           Result.ready_to_transmit
       end

end

EWSGI_REQUEST

class EWSGI_REQUEST

features

    read_message_body
        -- Read entire message body.
        ensure
            end_of_message_body

    read_block
        -- Read message body block.

    block_size: INTEGER
        -- Current block size.

    default_block_size: INTEGER is 65536
        -- The default block size.

    set_block_size (size: INTEGER)
        -- Set 'block_size' to 'size'.
        ensure
            block_size = size

    last_block: STRING
        -- The last block (or message body) read.

    end_of_message_body: BOOLEAN
        -- Has the end of the message body been reached?

    environment: HASH_TABLE [STRING, STRING]
        -- Environment values. Contains HTTP headers, CGI variables, extension variables and EWSGI specific variables.
 
end

EWSGI_RESPONSE

class EWSGI_RESPONSE
    
creation
    make
    
feature {NONE} -- Initialization

    make
        -- Create new response object
  
feature {EWSGI_APPLICATION} -- Response status

    ready_to_transmit: BOOLEAN
        -- Is this response ready to be transmitted?

    set_ready_to_transmit is
        -- Set response to ready to transmit.
        ensure
            ready_to_transmit
 
feature {EWSGI_APPLICATION} -- Message start line and status

    status: STRING
        -- HTTP status code
          
    set_status (s: STRING)
        -- Set 'status_code'.
        ensure
            status = s
      
    start_line: STRING
        -- HTTP message start-line

feature {EWSGI_APPLICATION} -- Message headers

    headers: STRING
        -- HTTP message headers including trailing empty line.
      
    headers_table: HASH_TABLE [LIST [STRING], STRING]
        -- Hash table of HTTP headers
    
    values (name: STRING): LIST [STRING]
        -- All values for the HTTP header 'name'.
        require
            headers_table.has (name)

    set_header (name, value: STRING)
        -- Set the HTTP header 'name' to the given 'value'.
        ensure
            headers_table.has (name) and values.count = 1 and values.has_item (value)
            
    append_header (name, value: STRING)
       -- Append the HTTP header 'name' with the given 'value'.
        ensure
            headers_table.has (name) and values.count >= 1 values.has_item (value)

feature {EWSGI_APPLICATION} -- Message body
        
    read_block
        -- Read a message body block.
        ensure
            not is_buffered implies last_block.count <= max_block_size
        end

    last_block: STRING
        -- Last message body block that has been read.

    is_buffered: BOOLEAN
        -- Is the entire entity body buffered in memory (STRING)?

    end_of_blocks: BOOLEAN
        -- Has the last of the entity body blocks been read?
  
    set_message_body (s: STRING)
        -- Set the message body to 's'. Use this for when you want a memory
        -- buffered response.
        ensure
            is_buffered
            ready_to_transmit
            last_block.is_equal (s)
        end
  
    max_block_size: INTEGER
        -- Maximum block size returned by message body if not buffered

    set_max_block_size (block_size: INTEGER)
        -- Set 'max_block_size'.
        ensure
            max_block_size = block_size
        end

end

EWSGI request environment (To be rewritten)

The class EWSGI_RESQUEST has a feature 'environment' with the following signature:

environment: HASH_TABLE [STRING, STRING]
        -- Environment values. Contains HTTP headers, CGI variables, extension variables, and EWSGI specific variables.

It contains HTTP headers, CGI variables and extension variables as defined by the  The Common Gateway Interface (CGI) Version 1.1. Among the extension variables are some EWSGI specific variables. The keys in 'environment' specified below are categorized as belonging to one of "CGI", "CGI HTTP", "CGI Extension" and "EWSGI".

Key Category Description
auth_type CGI "Basic", "Digest" or some other token.
request_method CGI The HTTP request method, such as "GET" or "POST".
script_name: STRING CGI The initial portion of the request URL's "path" that corresponds to the application object, so that the application knows its virtual "location". This may be an empty string, if the application corresponds to the "root" of the server.
path_info CGI The remainder of the request URL's "path", designating the virtual "location" of the request's target within the application. This may be an empty string, if the request URL targets the application root and does not have a trailing slash.
query_string CGI The portion of the request URL that follows the "?", if any.
content_type CGI The contents of any Content-Type fields in the HTTP request. May be empty.
content_length CGI The contents of any Content-Length fields in the HTTP request. May be empty.
server_name, server_port CGI When combined with script_name and path_info, these variables can be used to complete the URL. Note, however, that HTTP_HOST, if present, should be used in preference to server_name for reconstructing the request URL. See the URL Reconstruction section below for more detail. server_name and server_port can never be empty strings.
server_protocol CGI The version of the protocol the client used to send the request. Typically this will be something like "HTTP/1.0" or "HTTP/1.1" and may be used by the application to determine how to treat any HTTP request headers. (This variable should probably be called request_protocol, since it denotes the protocol used in the request, and is not necessarily the protocol that will be used in the server's response. However, for compatibility with CGI we have to keep the existing name.)
http_* CGI HTTP Variables corresponding to the client-supplied HTTP request headers (i.e., variables whose names begin with "HTTP_"). The presence or absence of these variables should correspond with the presence or absence of the appropriate HTTP header in the request.
ewsgi_version EWSGI Version of EWSGI that the EWSGI implementation implements. Eg. "1.0".
ewsgi_implementation EWSGI Name and version of the EWSGI implementation. Eg. "Eiffel Web Framework 1.0".
ewsgi_connector EWSGI The connector being used. Eg. "Fast-CGI".
eiffel_compiler EWSGI Name and version of the Eiffel compiler used. Eg. "ISE EiffelStudio version 6.8.8.6627 GPL Edition - unix".

EWSGI exception handling (To be rewritten)

An EWSGI implementation must catch all internal exceptions in its root creation feature and package them as a HTTP response to the gateway server. One of the following HTTP status codes must be used (as applicable) in the response:

  1. 500 (Internal Server Error).
  2. 501 (Not Implemented).
  3. 502 (Bad Gateway).
  4. 503 (Service Unavailable).
  5. 504 (Gateway Time-out).
  6. 505 (HTTP Version not supported).

The EWSGI implementation may return the Eiffel stacktrace as well as the EWSGI specific environment variables (see above) in the entity body of the HTTP response, in order to facilitate debugging and error reporting. The EWSGI implementation could provide features for controlling what is returned in the entity body on exceptions. Eg:

-- From EWSGI_APPLICATION

   show_exception_stack_trace  
       -- Show full Eiffel stack trace in the entity body 
       -- of the HTTP response when exceptions occur.

   show_environment
       -- Show full environment contents in the entity body 
       -- of the HTTP response when exceptions occur.

Then the developer can write:

class APPLICATION

inherit
    EWSGI_APPLICATION -- THIS IS PART OF THE EWSGI SPECIFICATION!

creation
    make -- THIS IS PART OF THE EWSGI SPECIFICATION!

features

    response (request: EWSGI_REQUEST): EWSGI_RESPONSE is -- THIS IS PART OF THE EWSGI SPECIFICATION!
            -- The response to the given 'request'.
       do
            create Result.make
            if debug then
                show_exception_stack_trace  
                show_environment
            end
            if request.env @ "path_info" = "/hello" then
                Result.set_status (Http_ok)
                Result.set_header (”Content-Type”, ”text/html; charset=utf-8”)
		Result.Set_message_body (”<html><body>Hello World</body></html>”)
            else
                Result.set_status (Http_not_found)
                Result.Set_message_body ("")
            end
       end

    debug: BOOLEAN is True
       -- Or read from application specific configuration file!

end

Proof-of-concept and reference implementation (To be rewritten)

We propose a proof-of-concept or reference implementation be done with:

  • CGI
  • FCGI
  • Nino. A simple Web server written in Eiffel by Javier Velilla.
  • mod_ewsgi. Apache module on Linux and Windows using Wamie.

The reference implementation should enable a user to write an EWSGI "Hello World" application:

  1. Using just 1 Eiffel class of ~10 lines of code.
  2. And easily switch deployment method (CGI, FCGI, Nino or mod_ewsgi).

A reference implementation is being worked on by Jocelyn Fiat (Eiffel Software), Javier Velilla (independent), Daniel Rodríguez (Seibo) and Paul Cohen (Seibo). The reference implementation consists of

  1. EWSGI core library:  Eiffel-Web-Framework.
  2. eJSON library:  eJSON.
  3. Nino Eiffel Web server:  Nino.
  4. mod_ewsgi module. Based on Wamie.
  5. SOS. Simple Object Storage - a simple Eiffel persistance mechanism.

The reference implementation aims to:

  1. Be a working EWSGI implementation.
  2. Provide connectors for CGI, Fast-CGI, SCGI, Nino and mod_ewsgi. Allow the user to chose which connector to use (dynamically and statically)!
  3. Provide packaged higher level framework features for Eiffel developers, such as:
    • Chained request handlers.
    • URL-based request handlers.
    • Simple Eiffel persistance support.
    • Eiffel/JSON serialization.

For an Eiffel system to run it needs to be compiled to a executable program or shared library (.so/.dll). For supporting CGI and Fast-CGI all that is needed is an executable program, for mod_ewsgi we need to compile the Eiffel system to a shared library.

Interesting ideas for Eiffel web frameworks based on EWSGI

Idea 1: Chained request handlers

An EWSGI implementation, or a third party Eiffel library, may provide a more elaborate framework. For example support for registering chained request handlers:

class APPLICATION

inherit
    CHAINED_HANDLERS_APPLICATION -- THIS IS NOT PART OF THE EWSGI SPECIFICATION! But uses the EWSGI_APPLICATION class!

creation
    make

features

    setup is
        do
            add_handler (agent authentication_handler) -- This is the first handler in the chain
            add_handler (agent access_control_handler) -- This is the second handler in the chain
            add_handler (agent response_handler) -- This is the third and last handler in the chain. 
                                                 -- The ESWGI_RESPONSE object modified by this handler 
                                                 -- is then used by the framework to create the actual
                                                 -- HTTP response.
        end

    authentication_handler (request: EWSGI_REQUEST): EWSGI_RESPONSE is
            -- Check authentication
        do
        end

    authorization_handler (request: EWSGI_REQUEST): EWSGI_RESPONSE is
            -- Check authorization
        do
        end

    response_handler (request: EWSGI_REQUEST): EWSGI_RESPONSE is
            -- Build response
        do
        end

end

Idea 2: URL-based handlers

An EWSGI implementation, or a third party Eiffel library, may provide a more elaborate framework. For example support for URL-based request handlers:

class APPLICATION

inherit
    URL_BASED_HANDLERS_APPLICATION -- THIS IS NOT PART OF THE EWSGI SPECIFICATION! But uses the EWSGI_APPLICATION class!

creation
    make

features

    setup is
        do
            register_handler ("/", agent root_handler)
            register_handler ("/items", agent items_handler)
            register_handler ("/collections", agent collections_handler)
        end

    root_handler (request: EWSGI_REQUEST: EWSGI_RESPONSE is
            -- Handler for URL "/".
        do
        end

    items_handler (request: EWSGI_REQUEST): EWSGI_RESPONSE is
            -- Handler for URL "/items".
        do
        end

    collections_handler (request: EWSGI_REQUEST): EWSGI_RESPONSE is
            -- Handler for URL "/collections".
        do
        end

end

A even more ambitious framework could combine the two patterns of chanied request handlers and URL-based handlers!

Unresolved issues

1: How should the EWSGI application entry point be specified?

Proposals:

  1. process (request_context: REQUEST_CONTEXT). Environment, input and output streams provided by REQUEST_CONTEXT.
  2. process (environment: ENVIRONMENT; input: INPUT_STREAM; output: OUTPUT_STREAM).
  3. Similar to current implementation from EWF: process (request: REQUEST; response: RESPONSE). Environment and input stream provided by REQUEST, output stream provided by RESPONSE.
  4. Current suggested approach: response (request: REQUEST): RESPONSE. Environment provided by REQUEST.

2: How should dynamic and static choice of gateway mechanism (CGI, Fast-CGI etc) be implemented?

We want make it easy for the end user (developer) to chose. At the same time we want to avoid building in to many dependencies, eg. libfcgi!

3: Class prefix naming

What prefix should be used for EWSGI classes?

Proposals:

  1. EWSGI_. Heavy :-) but consistent with this specification!
  2. GW_. Lighter :-) but not immediately obvious that it is the implementation of this specification!

I (Paul Cohen) think it's important that the classes implementing EWSGI have their own prefix and any extra framework classes provided have a different prefix. It should be clear to the Eiffel developer which classes belong to the EWSGI implementation and which classes are extras.

4: Features for URL-encoded variables?

Should EWSGI_REQUEST have features for URL-encoded variables, and if so which?

5: Features for URL-based and POST/form-based queries?

Should EWSGI_REQUEST have features for URL-based and POST/form-based queries, and if so which?

6: Should all environment variables be in one HASH_TABLE?

Should all environment variables be in one HASH_TABLE? As in the feature 'environment' in EWSGI_REQUEST or should we have separate features? For example like:

    cgi_meta_variables: HASH_TABLE [STRING, STRING]

    cgi_extension_variables: HASH_TABLE [STRING, STRING]

    http_headers: HASH_TABLE [STRING, STRING]

    ewsgi_variables: HASH_TABLE [STRING, STRING]

Acknowledgements

  • Paul Cohen (Author)
  • Daniel Rodríguez (Major contributor)
  • Javier Velilla (Major contributor)
  • Jocelyn Fiat (Major contributor)

References

  1.  Hypertext Transfer Protocol -- HTTP/1.1 (IETF RFC 2616)
  2.  The Common Gateway Interface (CGI) Version 1.1 (IETF RFC 3875).
  3.  FastCGI Specification 1.0
  4.  SCGI: A Simple Common Gateway Interface alternative.
  5.  Python Web Server Gateway Interface v1.0.
  6.  The Apache Modules Book: Application Development with Apache.