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, generality and type
safety. 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.
Unlike other approaches, this package declares many data types to assist in type safety. This feels more inline with the general Haskell spirit.
A final note: please remember when using this package that, while your application my 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 Method
- methodFromBS :: ByteString -> Method
- methodToBS :: Method -> ByteString
- data UrlScheme
- data HttpVersion
- = Http09
- | Http10
- | Http11
- | HttpVersion ByteString
- httpVersionFromBS :: ByteString -> HttpVersion
- httpVersionToBS :: HttpVersion -> ByteString
- data RequestHeader
- = Accept
- | AcceptCharset
- | AcceptEncoding
- | AcceptLanguage
- | Authorization
- | Cookie
- | ReqContentLength
- | ReqContentType
- | Host
- | Referer
- | RequestHeader ByteString
- requestHeaderFromBS :: ByteString -> RequestHeader
- requestHeaderToBS :: RequestHeader -> ByteString
- data ResponseHeader
- responseHeaderFromBS :: ByteString -> ResponseHeader
- responseHeaderToBS :: ResponseHeader -> ByteString
- data Status
- statusCode :: Status -> Int
- statusMessage :: Status -> ByteString
- newtype Source = Source {
- runSource :: IO (Maybe (ByteString, Source))
- newtype Enumerator = Enumerator {
- runEnumerator :: forall a. (a -> ByteString -> IO (Either a a)) -> a -> IO (Either a a)
- data Request = Request {}
- data Response = Response {}
- type Application = Request -> IO Response
- type Middleware = Application -> Application
Data types
For the data types below, you should only use the Show
and Read
instances for debugging purposes. Each datatype (excepting UrlScheme
) has
associated functions for converting to and from strict ByteString
s;
these are approrpiate for generating content.
HTTP request method. This data type is extensible via the Method
constructor. Request methods are case-sensitive, and comparison is achieved
by converting to a ByteString
via methodToBS
.
methodToBS :: Method -> ByteStringSource
URL scheme (http versus https)
HTTP protocol versions
data HttpVersion Source
Version of HTTP protocol used in current request. This data type is
extensible via the HttpVersion constructor. Comparison is achieved by
converting to a ByteString
via httpVersionToBS
.
httpVersionFromBS :: ByteString -> HttpVersionSource
This function takes the information after "HTTP/". For example:
httpVersionFromBS
(pack
"1.0") ==Http10
httpVersionToBS :: HttpVersion -> ByteStringSource
Returns the version number, for example:
unpack
(httpVersionToBS
Http10
) == "1.0"
Request header names
data RequestHeader Source
Headers sent from the client to the server. Clearly, this is not a complete list of all possible headers, but rather a selection of common ones. If other headers are required, they can be created with the RequestHeader constructor.
The naming rules are simple: removing any hyphens from the actual name, and
if there is a naming conflict with a ResponseHeader
, prefix with Req.
Equality determined by conversion via requestHeaderToBS
. Remember, headers
are case sensitive.
Response header names
data ResponseHeader Source
Headers sent from the server to the client. Clearly, this is not a complete list of all possible headers, but rather a selection of common ones. If other headers are required, they can be created with the ResponseHeader constructor.
if there is a naming conflict with a ResponseHeader
, prefix with Req.
Equality determined by conversion via responseHeaderToBS
. Remember,
headers are case sensitive.
Response status code
This attempts to provide the most common HTTP status codes, not all of them. Use the Status constructor when you want to create a status code not provided.
The Eq
instance tests equality based only on the numeric status code
value. See statusCode
.
statusCode :: Status -> IntSource
Source
This is a source for ByteString
s. 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.
Enumerator
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.
Enumerator
s 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.
Enumerator | |
|
WAI interface
Information on the request sent by the client. This abstracts away the details of the underlying implementation.
Request | |
|
Response | |
|
type Application = Request -> IO ResponseSource
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.