This module contains the core type definitions, class instances, and functions
for HTTP as well as the Snap
monad, which is used for web handlers.
- data Snap a
- runSnap :: Snap a -> (ByteString -> IO ()) -> Request -> Iteratee IO (Request, Response)
- data NoHandlerException = NoHandlerException
- finishWith :: Response -> Snap ()
- pass :: Snap a
- method :: Method -> Snap a -> Snap a
- path :: ByteString -> Snap a -> Snap a
- dir :: ByteString -> Snap a -> Snap a
- ifTop :: Snap a -> Snap a
- route :: [(ByteString, Snap a)] -> Snap a
- routeLocal :: [(ByteString, Snap a)] -> Snap a
- getRequest :: Snap Request
- getResponse :: Snap Response
- putRequest :: Request -> Snap ()
- putResponse :: Response -> Snap ()
- modifyRequest :: (Request -> Request) -> Snap ()
- modifyResponse :: (Response -> Response) -> Snap ()
- localRequest :: (Request -> Request) -> Snap a -> Snap a
- withRequest :: (Request -> Snap a) -> Snap a
- withResponse :: (Response -> Snap a) -> Snap a
- logError :: ByteString -> Snap ()
- runRequestBody :: Iteratee IO a -> Snap a
- getRequestBody :: Snap ByteString
- unsafeDetachRequestBody :: Snap (Enumerator a)
- data Request
- data Response
- type Headers = Map CIByteString [ByteString]
- class HasHeaders a where
- updateHeaders :: (Headers -> Headers) -> a -> a
- headers :: a -> Headers
- type Params = Map ByteString [ByteString]
- data Method
- data Cookie = Cookie {
- cookieName :: !ByteString
- cookieValue :: !ByteString
- cookieExpires :: !(Maybe UTCTime)
- cookieDomain :: !(Maybe ByteString)
- cookiePath :: !(Maybe ByteString)
- type HttpVersion = (Int, Int)
- addHeader :: HasHeaders a => CIByteString -> ByteString -> a -> a
- setHeader :: HasHeaders a => CIByteString -> ByteString -> a -> a
- getHeader :: HasHeaders a => CIByteString -> a -> Maybe ByteString
- ipHeaderFilter :: Snap ()
- ipHeaderFilter' :: CIByteString -> Snap ()
- rqServerName :: Request -> ByteString
- rqServerPort :: Request -> Int
- rqRemoteAddr :: Request -> ByteString
- rqRemotePort :: Request -> Int
- rqLocalAddr :: Request -> ByteString
- rqLocalHostname :: Request -> ByteString
- rqIsSecure :: Request -> Bool
- rqContentLength :: Request -> Maybe Int
- rqMethod :: Request -> Method
- rqVersion :: Request -> HttpVersion
- rqCookies :: Request -> [Cookie]
- rqPathInfo :: Request -> ByteString
- rqContextPath :: Request -> ByteString
- rqURI :: Request -> ByteString
- rqQueryString :: Request -> ByteString
- rqParams :: Request -> Params
- rqParam :: ByteString -> Request -> Maybe [ByteString]
- getParam :: ByteString -> Snap (Maybe ByteString)
- rqModifyParams :: (Params -> Params) -> Request -> Request
- rqSetParam :: ByteString -> [ByteString] -> Request -> Request
- emptyResponse :: Response
- setResponseStatus :: Int -> ByteString -> Response -> Response
- rspStatus :: Response -> Int
- rspStatusReason :: Response -> ByteString
- setContentType :: ByteString -> Response -> Response
- addCookie :: Cookie -> Response -> Response
- setContentLength :: Int64 -> Response -> Response
- clearContentLength :: Response -> Response
- setResponseBody :: (forall a. Enumerator a) -> Response -> Response
- modifyResponseBody :: (forall a. Enumerator a -> Enumerator a) -> Response -> Response
- addToOutput :: (forall a. Enumerator a) -> Snap ()
- writeBS :: ByteString -> Snap ()
- writeLazyText :: Text -> Snap ()
- writeText :: Text -> Snap ()
- writeLBS :: ByteString -> Snap ()
- sendFile :: FilePath -> Snap ()
- type Enumerator a = Enumerator IO a
- formatHttpTime :: CTime -> IO ByteString
- parseHttpTime :: ByteString -> IO CTime
- urlEncode :: ByteString -> ByteString
- urlDecode :: ByteString -> Maybe ByteString
The Snap Monad
Snap
is the Monad
that user web handlers run in. Snap
gives you:
- stateful access to fetch or modify an HTTP
Request
- stateful access to fetch or modify an HTTP
Response
- failure /
Alternative
/MonadPlus
semantics: aSnap
handler can choose not to handle a given request, usingempty
or its synonympass
, and you can try alternative handlers with the<|>
operator:
a :: Snap String a = pass b :: Snap String b = return "foo" c :: Snap String c = a <|> b -- try running a, if it fails then try b
- convenience functions (
writeBS
,writeLBS
,writeText
,writeLazyText
,addToOutput
) for writing output to theResponse
:
a :: (forall a . Enumerator a) -> Snap () a someEnumerator = do writeBS "I'm a strict bytestring" writeLBS "I'm a lazy bytestring" addToOutput someEnumerator
- early termination: if you call
finishWith
:
a :: Snap () a = do modifyResponse $ setResponseStatus 500 "Internal Server Error" writeBS "500 error" r <- getResponse finishWith r
then any subsequent processing will be skipped and supplied Response
value
will be returned from runSnap
as-is.
a :: Snap () a = liftIO fireTheMissiles
runSnap :: Snap a -> (ByteString -> IO ()) -> Request -> Iteratee IO (Request, Response)Source
Runs a Snap
monad action in the 'Iteratee IO' monad.
data NoHandlerException Source
This exception is thrown if the handler you supply to runSnap
fails.
Functions for control flow and early termination
finishWith :: Response -> Snap ()Source
Fails out of a Snap
monad action. This is used to indicate
that you choose not to handle the given request within the given
handler.
Routing
method :: Method -> Snap a -> Snap aSource
Runs a Snap
monad action only if the request's HTTP method matches
the given method.
:: ByteString | path to match against |
-> Snap a | handler to run |
-> Snap a |
Runs a Snap
monad action only for requests where rqPathInfo
is exactly
equal to the given string. If the path matches, locally sets rqContextPath
to the old value of rqPathInfo
, sets rqPathInfo
="", and runs the given
handler.
:: ByteString | path component to match |
-> Snap a | handler to run |
-> Snap a |
Runs a Snap
monad action only when the rqPathInfo
of the request
starts with the given path. For example,
dir "foo" handler
Will fail if rqPathInfo
is not "/foo
" or "/foo/...
", and will
add "foo/"
to the handler's local rqContextPath
.
route :: [(ByteString, Snap a)] -> Snap aSource
A web handler which, given a mapping from URL entry points to web handlers, efficiently routes requests to the correct handler.
The URL entry points are given as relative paths, for example:
route [ ("foo/bar/quux", fooBarQuux) ]
If the URI of the incoming request is
/foo/bar/quux
or
/foo/bar/quux/...anything...
then the request will be routed to "fooBarQuux
", with rqContextPath
set to "/foo/bar/quux/
" and rqPathInfo
set to
"...anything...
".
A path component within an URL entry point beginning with a colon (":
")
is treated as a variable capture; the corresponding path component within
the request URI will be entered into the rqParams
parameters mapping with
the given name. For instance, if the routes were:
route [ ("foo/:bar/baz", fooBazHandler) ]
Then a request for "/foo/saskatchewan/baz
" would be routed to
fooBazHandler
with a mapping for:
"bar" => "saskatchewan"
in its parameters table.
Longer paths are matched first, and specific routes are matched before captures. That is, if given routes:
[ ("a", h1), ("a/b", h2), ("a/:x", h3) ]
a request for "/a/b
" will go to h2
, "/a/s
" for any s will go
to h3
, and "/a
" will go to h1
.
The following example matches "/article
" to an article index,
"/login
" to a login, and "/article/...
" to an article renderer.
route [ ("article", renderIndex) , ("article/:id", renderArticle) , ("login", method POST doLogin) ]
routeLocal :: [(ByteString, Snap a)] -> Snap aSource
The routeLocal
function is the same as route'
, except it doesn't change
the request's context path. This is useful if you want to route to a
particular handler but you want that handler to receive the rqPathInfo
as
it is.
Access to state
withRequest :: (Request -> Snap a) -> Snap aSource
Fetches the Request
from state and hands it to the given action.
withResponse :: (Response -> Snap a) -> Snap aSource
Fetches the Response
from state and hands it to the given action.
Logging
Grabbing request bodies
runRequestBody :: Iteratee IO a -> Snap aSource
Sends the request body through an iteratee (data consumer) and returns the result.
getRequestBody :: Snap ByteStringSource
Returns the request body as a bytestring.
unsafeDetachRequestBody :: Snap (Enumerator a)Source
Detaches the request body's Enumerator
from the Request
and
returns it. You would want to use this if you needed to send the
HTTP request body (transformed or otherwise) through to the output
in O(1) space. (Examples: transcoding, "echo", etc)
Normally Snap is careful to ensure that the request body is fully
consumed after your web handler runs; this function is marked
"unsafe" because it breaks this guarantee and leaves the
responsibility up to you. If you don't fully consume the
Enumerator
you get here, the next HTTP request in the pipeline
(if any) will misparse. Be careful with exception handlers.
HTTP Datatypes and Functions
Contains all of the information about an incoming HTTP request.
type Headers = Map CIByteString [ByteString]Source
A type alias for a case-insensitive key-value mapping.
class HasHeaders a whereSource
A typeclass for datatypes which contain HTTP headers.
updateHeaders :: (Headers -> Headers) -> a -> aSource
Modify the datatype's headers.
Retrieve the headers from a datatype that has headers.
type Params = Map ByteString [ByteString]Source
A type alias for the HTTP parameters mapping. Each parameter
key maps to a list of ByteString values; if a parameter is specified
multiple times (e.g.: "GET /foo?param=bar1¶m=bar2
"), looking up
"param
" in the mapping will give you ["bar1", "bar2"]
.
Enumerates the HTTP method values (see http://tools.ietf.org/html/rfc2068.html#section-5.1.1).
A datatype representing an HTTP cookie.
Cookie | |
|
type HttpVersion = (Int, Int)Source
Headers
addHeader :: HasHeaders a => CIByteString -> ByteString -> a -> aSource
Adds a header key-value-pair to the HasHeaders
datatype. If a header with
the same name already exists, the new value is appended to the headers list.
setHeader :: HasHeaders a => CIByteString -> ByteString -> a -> aSource
Sets a header key-value-pair in a HasHeaders
datatype. If a header with
the same name already exists, it is overwritten with the new value.
getHeader :: HasHeaders a => CIByteString -> a -> Maybe ByteStringSource
Gets a header value out of a HasHeaders
datatype. If many headers came
in with the same name, they will be catenated together.
ipHeaderFilter :: Snap ()Source
Modifies the Request
in the state to set the rqRemoteAddr
field to the value in the X-Forwarded-For header. If the header is
not present, this action has no effect.
This action should be used only when working behind a reverse http proxy that sets the X-Forwarded-For header. This is the only way to ensure the value in the X-Forwarded-For header can be trusted.
This is provided as a filter so actions that require the remote address can get it in a uniform manner. It has specifically limited functionality to ensure that its transformation can be trusted, when used correctly.
ipHeaderFilter' :: CIByteString -> Snap ()Source
Modifies the Request
in the state to set the rqRemoteAddr
field to the value from the header specified. If the header
specified is not present, this action has no effect.
This action should be used only when working behind a reverse http proxy that sets the header being looked at. This is the only way to ensure the value in the header can be trusted.
This is provided as a filter so actions that require the remote address can get it in a uniform manner. It has specifically limited functionality to ensure that its transformation can be trusted, when used correctly.
Requests
rqServerName :: Request -> ByteStringSource
The server name of the request, as it came in from the request's
Host:
header.
rqServerPort :: Request -> IntSource
Returns the port number the HTTP server is listening on.
rqRemoteAddr :: Request -> ByteStringSource
The remote IP address.
rqRemotePort :: Request -> IntSource
The remote TCP port number.
rqLocalAddr :: Request -> ByteStringSource
The local IP address for this request.
rqLocalHostname :: Request -> ByteStringSource
Returns the HTTP server's idea of its local hostname.
rqIsSecure :: Request -> BoolSource
Returns True
if this is an HTTPS
session (currently always
False
).
rqContentLength :: Request -> Maybe IntSource
Returns the Content-Length
of the HTTP request body.
rqVersion :: Request -> HttpVersionSource
Returns the HTTP version used by the client.
rqCookies :: Request -> [Cookie]Source
Returns a list of the cookies that came in from the HTTP request headers.
rqPathInfo :: Request -> ByteStringSource
Handlers can (will be; --ed) be hung on a URI
"entry point";
this is called the "context path". If a handler is hung on the
context path "/foo/"
, and you request "/foo/bar"
, the value
of rqPathInfo
will be "bar"
.
rqContextPath :: Request -> ByteStringSource
The "context path" of the request; catenating rqContextPath
, and
rqPathInfo
should get you back to the original rqURI
. The
rqContextPath
always begins and ends with a slash ("/"
)
character, and represents the path (relative to your
component/snaplet) you took to get to your handler.
rqURI :: Request -> ByteStringSource
Returns the URI
requested by the client.
rqQueryString :: Request -> ByteStringSource
Returns the HTTP query string for this Request
.
:: ByteString | parameter name to look up |
-> Request | HTTP request |
-> Maybe [ByteString] |
Looks up the value(s) for the given named parameter. Parameters initially
come from the request's query string and any decoded POST body (if the
request's Content-Type
is application/x-www-form-urlencoded
). Parameter
values can be modified within handlers using rqModifyParams.
:: ByteString | parameter name to look up |
-> Snap (Maybe ByteString) |
See rqParam
. Looks up a value for the given named parameter in the
Request
. If more than one value was entered for the given parameter name,
getParam
gloms the values together with:
intercalate
" "
rqModifyParams :: (Params -> Params) -> Request -> RequestSource
Modifies the parameters mapping (which is a Map ByteString ByteString
) in
a Request
using the given function.
:: ByteString | parameter name |
-> [ByteString] | parameter values |
-> Request | request |
-> Request |
Writes a key-value pair to the parameters mapping within the given request.
Responses
emptyResponse :: ResponseSource
An empty Response
.
:: Int | HTTP response integer code |
-> ByteString | HTTP response explanation |
-> Response | Response to be modified |
-> Response |
Sets the HTTP response status.
rspStatusReason :: Response -> ByteStringSource
Returns the HTTP status explanation string.
setContentType :: ByteString -> Response -> ResponseSource
Sets the Content-Type
in the Response
headers.
setContentLength :: Int64 -> Response -> ResponseSource
A note here: if you want to set the Content-Length
for the response,
Snap forces you to do it with this function rather than by setting it in the
headers; the Content-Length
in the headers will be ignored.
The reason for this is that Snap needs to look up the value of
Content-Length
for each request, and looking the string value up in the
headers and parsing the number out of the text will be too expensive.
If you don't set a content length in your response, HTTP keep-alive will be
disabled for HTTP/1.0 clients, forcing a Connection: close
. For HTTP/1.1
clients, Snap will switch to the chunked transfer encoding if
Content-Length
is not specified.
clearContentLength :: Response -> ResponseSource
Removes any Content-Length
set in the Response
.
Response I/O
:: (forall a. Enumerator a) | new response body enumerator |
-> Response | response to modify |
-> Response |
Sets an HTTP response body to the given Enumerator
value.
modifyResponseBody :: (forall a. Enumerator a -> Enumerator a) -> Response -> ResponseSource
Modifies a response body.
:: (forall a. Enumerator a) | output to add |
-> Snap () |
writeBS :: ByteString -> Snap ()Source
Adds the given strict ByteString
to the body of the Response
stored in
the Snap
monad state.
Warning: This function is intentionally non-strict. If any pure
exceptions are raised by the expression creating the ByteString
,
the exception won't actually be raised within the Snap handler.
writeLazyText :: Text -> Snap ()Source
Adds the given lazy Text
to the body of the Response
stored in the
Snap
monad state.
Warning: This function is intentionally non-strict. If any pure
exceptions are raised by the expression creating the ByteString
,
the exception won't actually be raised within the Snap handler.
writeText :: Text -> Snap ()Source
Adds the given strict Text
to the body of the Response
stored in the
Snap
monad state.
Warning: This function is intentionally non-strict. If any pure
exceptions are raised by the expression creating the ByteString
,
the exception won't actually be raised within the Snap handler.
writeLBS :: ByteString -> Snap ()Source
Adds the given lazy ByteString
to the body of the Response
stored in
the Snap
monad state.
Warning: This function is intentionally non-strict. If any pure
exceptions are raised by the expression creating the ByteString
,
the exception won't actually be raised within the Snap handler.
sendFile :: FilePath -> Snap ()Source
Sets the output to be the contents of the specified file.
Calling sendFile
will overwrite any output queued to be sent in the
Response
. If the response body is not modified after the call to
sendFile
, Snap will use the efficient sendfile()
system call on
platforms that support it.
If the response body is modified (using modifyResponseBody
), the file will
be read using mmap()
.
Iteratee
type Enumerator a = Enumerator IO aSource
HTTP utilities
formatHttpTime :: CTime -> IO ByteStringSource
Converts a CTime
into an HTTP timestamp.
parseHttpTime :: ByteString -> IO CTimeSource
Converts an HTTP timestamp into a CTime
.
urlEncode :: ByteString -> ByteStringSource
URL-escapes a string (see http://tools.ietf.org/html/rfc2396.html#section-2.4)
urlDecode :: ByteString -> Maybe ByteStringSource
Decodes an URL-escaped string (see http://tools.ietf.org/html/rfc2396.html#section-2.4)