wai-0.2.0: Web Application Interface.




This module defines a generic web application interface. It is a common protocol between web servers and web applications.

The overriding design principles here are performance and generality . To address performance, this library is built on Source for the request body and Enumerator for the response bodies. The advantages of this approach over lazy IO have been debated elsewhere.

Nonetheless, many people find these data structures difficult to work with. For that reason, this library includes the Network.Wai.Enumerator module to provide more familiar abstractions, including lazy IO.

Generality is achieved by removing many variables commonly found in similar projects that are not universal to all servers. The goal is that the Request object contains only data which is meaningful in all circumstances.

A final note: please remember when using this package that, while your application may compile without a hitch against many different servers, there are other considerations to be taken when moving to a new backend. For example, if you transfer from a CGI application to a FastCGI one, you might suddenly find you have a memory leak. Conversely, a FastCGI application would be well served to preload all templates from disk when first starting; this would kill the performance of a CGI application.


Data types

Request method

type Method = ByteStringSource

HTTP request method. Since the HTTP protocol allows arbitrary request methods, we leave this open as a ByteString. Please note the request methods are case-sensitive.

HTTP protocol versions

type HttpVersion = ByteStringSource

Version of HTTP protocol used in current request. The value given here should be everything following the "HTTP/" line in a request. In other words, HTTP/1.1 -> "1.1", HTTP/1.0 -> "1.0".

Case-insensitive byte strings

data CIByteString Source

A case insensitive bytestring, where the Eq and Ord instances do comparisons based on the lower-cased version of this string. For efficiency, this datatype contains both the original and lower-case version of the string; this means there is no need to lower-case the bytestring for every comparison.

Please note that this datatype has an IsString instance, which can allow for very concise code when using the OverloadedStrings language extension.

mkCIByteString :: ByteString -> CIByteStringSource

Convert a regular bytestring to a case-insensitive bytestring.

Request header names

type RequestHeader = CIByteStringSource

Headers sent from the client to the server. Note that this is a case-insensitive string, as the HTTP spec specifies.

Response header names

type ResponseHeader = CIByteStringSource

Headers sent from the server to the client. Note that this is a case-insensitive string, as the HTTP spec specifies.

Response status code

data Status Source

HTTP status code; a combination of the integral code and a status message. Equality is determined solely on the basis of the integral code.




status301 :: StatusSource

Moved Permanently

status303 :: StatusSource

See Other

status400 :: StatusSource

Bad Request

status401 :: StatusSource


status403 :: StatusSource


status404 :: StatusSource

Not Found

status405 :: StatusSource

Method Not Allowed

status500 :: StatusSource

Internal Server Error

Response body

data ResponseBody Source

The response body returned to the server from the application. We provide three separate constructors as optimizations:

  • ResponseEnumerator is the most general type, allowing constant-memory production of a response, even in the presence of interleaved I/O actions.
  • ResponseFile serves a static file from the filesystem. Many servers use a sendfile system call to optimize this type of serving, making this a huge performance gain.
  • ResponseLBS. Often times, we wish to return a response that includes no interleaved I/O. In this case, we can use Haskell's natural laziness to our advantage, and represent the response as a lazy bytestring.


newtype Source Source

This is a source for ByteStrings. It is a function (wrapped in a newtype) that will return Nothing if the data has been completely consumed, or return the next ByteString from the source along with a new Source to continue reading from.

Be certain not to reuse a Source! It might work fine with some implementations of Source, while causing bugs with others.

This datatype is used by WAI to represent a request body. We choose this over an enumerator in that it gives the application power over control flow. This not only makes it easier to use in many situations, but also allows implementation of some features such as a backtracking parser which doesn't read the entire body into memory.




newtype Enumerator Source

An enumerator is a data producer. It takes two arguments: a function to enumerate over (the iteratee) and an accumulating parameter. As the enumerator produces output, it calls the iteratee, thereby avoiding the need to allocate large amounts of memory for storing the entire piece of data.

Normally in Haskell, we can achieve the same results with laziness. For example, an inifinite list does not require inifinite memory storage; we simply get away with thunks. However, when operating in the IO monad, we do not have this luxury. There are other approaches, such as lazy I/O. If you would like to program in this manner, please see Network.Wai.Enumerator, in particular toLBS.

That said, let's address the details of this particular enumerator implementation. You'll notice that the iteratee is a function that takes two arguments and returns an Either value. The second argument is simply the piece of data generated by the enumerator. The Either value at the end is a means to alert the enumerator whether to continue or not. If it returns Left, then the enumeration should cease. If it returns Right, it should continue.

The accumulating parameter (a) has meaning only to the iteratee; the enumerator simply passes it around. The enumerator itself also returns an Either value; a Right means the enumerator ran to completion, while a Left indicates early termination was requested by the iteratee.

Enumerators are not required to be resumable. That is to say, the Enumerator may only be called once. While this requirement puts a bit of a strain on the caller in some situations, it saves a large amount of complication- and thus performance- on the producer.

In WAI, an Enumerator is used to represent the response body. We have specifically chosen one of the simplest representations of an enumerator to avoid coding complication and performance overhead.




runEnumerator :: forall a. (a -> ByteString -> IO (Either a a)) -> a -> IO (Either a a)

WAI interface

data Request Source

Information on the request sent by the client. This abstracts away the details of the underlying implementation.




requestMethod :: Method
httpVersion :: HttpVersion
pathInfo :: ByteString

Extra path information sent by the client. The meaning varies slightly depending on backend; in a standalone server setting, this is most likely all information after the domain name. In a CGI application, this would be the information following the path to the CGI executable itself.

queryString :: ByteString

If no query string was specified, this should be empty.

serverName :: ByteString
serverPort :: Int
requestHeaders :: [(RequestHeader, ByteString)]

Was this request made over an SSL connection?

isSecure :: Bool
requestBody :: Source

Log the given line in some method; how this is accomplished is server-dependant.

errorHandler :: String -> IO ()
remoteHost :: ByteString

The client's host information.

data Response Source




status :: Status
responseHeaders :: [(ResponseHeader, ByteString)]
responseBody :: ResponseBody

A common optimization is to use the sendfile system call when sending files from the disk. This datatype facilitates this optimization; if Left is returned, the server will send the file from the disk by whatever means it wishes. If Right, it will call the Enumerator.

type Middleware = Application -> ApplicationSource

Middleware is a component that sits between the server and application. It can do such tasks as GZIP encoding or response caching. What follows is the general definition of middleware, though a middleware author should feel free to modify this.

As an example of an alternate type for middleware, suppose you write a function to load up session information. The session information is simply a string map [(String, String)]. A logical type signatures for this middleware might be:

 loadSession :: ([(String, String)] -> Application) -> Application

Here, instead of taking a standard Application as its first argument, the middleware takes a function which consumes the session information as well.