This module contains functions for writing webservers. These servers process requests in a state monad pipeline and several useful actions are provided herein.
See examples/test.hs
for an example of how to use this module.
- type WebMonad = StateT WebState IO
- data WebState = WebState {}
- getRequest :: WebMonad Request
- getPayload :: WebMonad (Maybe Source)
- getPOST :: Int -> WebMonad (Map ByteString ByteString)
- getGET :: WebMonad (Map ByteString ByteString)
- getReply :: WebMonad Reply
- setReply :: Int -> WebMonad ()
- setHeader :: (Headers -> Headers) -> WebMonad ()
- setCookie :: Cookie -> WebMonad ()
- errorPage :: String -> WebMonad ()
- handleConditionalRequest :: WebMonad ()
- handleHandleToSource :: WebMonad ()
- handleRangeRequests :: WebMonad ()
- handleDecoration :: WebMonad ()
- handleFromFilesystem :: FilePath -> WebMonad ()
- serveHTTP :: Int -> WebMonad () -> IO ()
- serveHTTPS :: Int -> FilePath -> FilePath -> WebMonad () -> IO ()
- data DispatchMatch
- dispatchOnURL :: [(DispatchMatch, WebMonad ())] -> WebMonad ()
The processing monad
Processing a request involves running a number of actions in a StateT monad
where the state for that monad is this record. This contains both a
Source
and a Handle element. Often something will fill in the Handle
and expect later processing to convert it to a Source. Somehow, you have
to end up with a Source, however.
WebState | |
|
getRequest :: WebMonad RequestSource
Return the request
getPayload :: WebMonad (Maybe Source)Source
Return the client's request payload (if any)
:: Int | max number of bytes to read |
-> WebMonad (Map ByteString ByteString) |
Get the arguments to a POST request
getGET :: WebMonad (Map ByteString ByteString)Source
Get the arguments to a GET request
setReply :: Int -> WebMonad ()Source
Set the current reply to be a reply with the given status code, the default message for that status code, an empty body and an empty set of headers.
setHeader :: (Headers -> Headers) -> WebMonad ()Source
Set a header in the current reply. Because of the way records work, you use this function like this:
setHeader $ \h -> h { httpSomeHeader = Just value }
WebMonad actions
handleConditionalRequest :: WebMonad ()Source
This handles the If-*Matches and If-*Modified conditional headers. It takes its information from the Last-Modified and ETag headers of the current reply. Note that, for the purposes of ETag matching, a reply without an ETag header is considered not to exist from the point of view of, say, If-Matches: *.
handleHandleToSource :: WebMonad ()Source
If the current state includes a Handle, this turns it into a Source
handleRangeRequests :: WebMonad ()Source
This handles Range requests and also translates from Handles to Sources. If the WebMonad has a Handle at this point, then we can construct sources from any subrange of the file. (We also assume that Content-Length is correctly set.)
See RFC 2616, section 14.35
handleDecoration :: WebMonad ()Source
At the moment, this just adds the header Server: Network.MiniHTTP
This is a very simple handler which deals with requests by returning the requested file from the filesystem. It sets a Handle in the state and sets the Content-Type, Content-Length and Last-Modified headers
Running the server
Start an IPv4 HTTP server
:: Int | the port number to listen on |
-> FilePath | path to public key (certificate) |
-> FilePath | path to private key |
-> WebMonad () | the processing action |
-> IO () |
Start an IPv4 HTTPS server. Plese remember to have wrapped your main
function in OpenSSL.withOpenSSL
otherwise you'll probably crash the
process.
:: [(DispatchMatch, WebMonad ())] | the list of URL prefixes (with |
-> WebMonad () |
This is an, optional, helper function which you might find useful. The serving fuctions both expect a WebMonad action which is called to process each request. In general you have to write that and dispatch based on the client's request.
This might save you some work: it tries each of the elements in the list in turn. As soon as one matches it runs the given action to process the request.