Safe Haskell | None |
---|---|
Language | Haskell2010 |
- class (Monad m, MonadIO m, MonadBaseControl IO m, MonadPlus m, Functor m, Applicative m, Alternative m) => MonadSnap m where
- data SnapResult a
- type EscapeHttpHandler = ((Int -> Int) -> IO ()) -> InputStream ByteString -> OutputStream Builder -> IO ()
- data EscapeSnap
- data Zero
- newtype Snap a = Snap {}
- data SnapState = SnapState {
- _snapRequest :: Request
- _snapResponse :: Response
- _snapLogError :: ByteString -> IO ()
- _snapModifyTimeout :: (Int -> Int) -> IO ()
- runRequestBody :: MonadSnap m => (InputStream ByteString -> IO a) -> m a
- readRequestBody :: MonadSnap m => Word64 -> m ByteString
- transformRequestBody :: (InputStream ByteString -> IO (InputStream ByteString)) -> Snap ()
- finishWith :: MonadSnap m => Response -> m a
- catchFinishWith :: Snap a -> Snap (Either Response a)
- pass :: MonadSnap m => m a
- method :: MonadSnap m => Method -> m a -> m a
- methods :: MonadSnap m => [Method] -> m a -> m a
- updateContextPath :: Int -> Request -> Request
- pathWith :: MonadSnap m => (ByteString -> ByteString -> Bool) -> ByteString -> m a -> m a
- dir :: MonadSnap m => ByteString -> m a -> m a
- path :: MonadSnap m => ByteString -> m a -> m a
- pathArg :: (Readable a, MonadSnap m) => (a -> m b) -> m b
- ifTop :: MonadSnap m => m a -> m a
- sget :: Snap SnapState
- smodify :: (SnapState -> SnapState) -> Snap ()
- getRequest :: MonadSnap m => m Request
- getResponse :: MonadSnap m => m Response
- getsRequest :: MonadSnap m => (Request -> a) -> m a
- getsResponse :: MonadSnap m => (Response -> a) -> m a
- putRequest :: MonadSnap m => Request -> m ()
- putResponse :: MonadSnap m => Response -> m ()
- modifyRequest :: MonadSnap m => (Request -> Request) -> m ()
- modifyResponse :: MonadSnap m => (Response -> Response) -> m ()
- redirect :: MonadSnap m => ByteString -> m a
- redirect' :: MonadSnap m => ByteString -> Int -> m a
- logError :: MonadSnap m => ByteString -> m ()
- addToOutput :: MonadSnap m => (OutputStream Builder -> IO (OutputStream Builder)) -> m ()
- writeBuilder :: MonadSnap m => Builder -> m ()
- writeBS :: MonadSnap m => ByteString -> m ()
- writeLBS :: MonadSnap m => ByteString -> m ()
- writeText :: MonadSnap m => Text -> m ()
- writeLazyText :: MonadSnap m => Text -> m ()
- sendFile :: MonadSnap m => FilePath -> m ()
- sendFilePartial :: MonadSnap m => FilePath -> (Word64, Word64) -> m ()
- localRequest :: MonadSnap m => (Request -> Request) -> m a -> m a
- withRequest :: MonadSnap m => (Request -> m a) -> m a
- withResponse :: MonadSnap m => (Response -> m a) -> m a
- ipHeaderFilter :: MonadSnap m => m ()
- ipHeaderFilter' :: MonadSnap m => CI ByteString -> m ()
- bracketSnap :: IO a -> (a -> IO b) -> (a -> Snap c) -> Snap c
- data NoHandlerException = NoHandlerException String
- terminateConnection :: (Exception e, MonadSnap m) => e -> m a
- escapeHttp :: MonadSnap m => EscapeHttpHandler -> m ()
- runSnap :: Snap a -> (ByteString -> IO ()) -> ((Int -> Int) -> IO ()) -> Request -> IO (Request, Response)
- fixupResponse :: Request -> Response -> IO Response
- evalSnap :: Snap a -> (ByteString -> IO ()) -> ((Int -> Int) -> IO ()) -> Request -> IO a
- getParamFrom :: MonadSnap m => (ByteString -> Request -> Maybe [ByteString]) -> ByteString -> m (Maybe ByteString)
- getParam :: MonadSnap m => ByteString -> m (Maybe ByteString)
- getPostParam :: MonadSnap m => ByteString -> m (Maybe ByteString)
- getQueryParam :: MonadSnap m => ByteString -> m (Maybe ByteString)
- getParams :: MonadSnap m => m Params
- getPostParams :: MonadSnap m => m Params
- getQueryParams :: MonadSnap m => m Params
- getCookie :: MonadSnap m => ByteString -> m (Maybe Cookie)
- readCookie :: (MonadSnap m, Readable a) => ByteString -> m a
- expireCookie :: MonadSnap m => Cookie -> m ()
- setTimeout :: MonadSnap m => Int -> m ()
- extendTimeout :: MonadSnap m => Int -> m ()
- modifyTimeout :: MonadSnap m => (Int -> Int) -> m ()
- getTimeoutModifier :: MonadSnap m => m ((Int -> Int) -> IO ())
- module Snap.Internal.Http.Types
Documentation
class (Monad m, MonadIO m, MonadBaseControl IO m, MonadPlus m, Functor m, Applicative m, Alternative m) => MonadSnap m where Source #
type EscapeHttpHandler Source #
= ((Int -> Int) -> IO ()) | timeout modifier |
-> InputStream ByteString | socket read end |
-> OutputStream Builder | socket write end |
-> IO () |
Type of external handler passed to escapeHttp
.
data EscapeSnap Source #
Used internally to implement escapeHttp
.
Snap
is the Monad
that user web handlers run in. Snap
gives you:
Stateful access to fetch or modify an HTTP
Request
.printRqContextPath :: Snap () printRqContextPath =
writeBS
.rqContextPath
=<<getRequest
Stateful access to fetch or modify an HTTP
Response
.printRspStatusReason :: Snap () printRspStatusReason =
writeBS
.rspStatusReason
=<<getResponse
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 bConvenience functions (
writeBS
,writeLBS
,writeText
,writeLazyText
,addToOutput
) for queueing output to be written to theResponse
, or for streaming to the response using io-streams:example :: (
OutputStream
Builder
-> IO (OutputStream
Builder
)) -> Snap () example streamProc = dowriteBS
"I'm a strict bytestring"writeLBS
"I'm a lazy bytestring"writeText
"I'm strict text"addToOutput
streamProcEarly termination: if you call
finishWith
:a :: Snap () a = do
modifyResponse
$setResponseStatus
500 "Internal Server Error"writeBS
"500 error" r <-getResponse
finishWith
rthen any subsequent processing will be skipped and the supplied
Response
value will be returned fromrunSnap
as-is.Access to the
IO
monad through aMonadIO
instance:a :: Snap () a =
liftIO
fireTheMissilesThe ability to set or extend a timeout which will kill the handler thread after
N
seconds of inactivity (the default is 20 seconds):a :: Snap () a =
setTimeout
30Throw and catch exceptions using a
MonadBaseControl
instance:import Control.Exception.Lifted (
SomeException
,throwIO
,catch
) foo :: Snap () foo = bar `catch` (e::SomeException
) -> baz where bar =throwIO
FooExceptionLog a message to the error log:
foo :: Snap () foo =
logError
"grumble."
You may notice that most of the type signatures in this module contain a
(
typeclass constraint. MonadSnap
m) => ...MonadSnap
is a typeclass
which, in essence, says "you can get back to the Snap
monad from
here". Using MonadSnap
you can extend the Snap
monad with additional
functionality and still have access to most of the Snap
functions without
writing lift
everywhere. Instances are already
provided for most of the common monad transformers
(ReaderT
, WriterT
,
StateT
, etc.).
SnapState | |
|
runRequestBody :: MonadSnap m => (InputStream ByteString -> IO a) -> m a Source #
Pass the request body stream to a consuming procedure, returning the result.
If the consuming procedure you pass in here throws an exception, Snap will
attempt to clear the rest of the unread request body (using
skipToEof
) before rethrowing the
exception. If you used terminateConnection
, however, Snap will give up and
immediately close the socket.
To prevent slowloris attacks, the connection will be also terminated if the input socket produces data too slowly (500 bytes per second is the default limit).
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.ByteString.Char8 as B8 ghci> import qualified Data.ByteString.Lazy as L ghci> import Data.Char (toUpper) ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> import qualified System.IO.Streams as Streams ghci> let r = T.put "/foo" "text/plain" "some text" ghci> :{ ghci| let f s = do u <- Streams.map (B8.map toUpper) s ghci| l <- Streams.toList u ghci| return $ L.fromChunks l ghci| :} ghci> T.runHandler r (runRequestBody
f >>=writeLBS
) HTTP/1.1 200 OK server: Snap/test date: Thu, 07 Aug 2014 20:48:40 GMT SOME TEXT
:: MonadSnap m | |
=> Word64 | size of the largest request body we're willing
to accept. If a request body longer than this is
received, a |
-> m ByteString |
Returns the request body as a lazy bytestring. /Note that the request is not actually provided lazily!/
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> let r = T.put "/foo" "text/plain" "some text" ghci> T.runHandler r (readRequestBody
2048 >>=writeLBS
) HTTP/1.1 200 OK server: Snap/test date: Thu, 07 Aug 2014 20:08:44 GMT some text
Since: 0.6
:: (InputStream ByteString -> IO (InputStream ByteString)) | the |
-> Snap () |
Normally Snap is careful to ensure that the request body is fully
consumed after your web handler runs, but before the Response
body
is streamed out the socket. If you want to transform the request body into
some output in O(1) space, you should use this function.
Take care: in order for this to work, the HTTP client must be written with input-to-output streaming in mind.
Note that upon calling this function, response processing finishes early as
if you called finishWith
. Make sure you set any content types, headers,
cookies, etc. before you call this function.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.ByteString.Char8 as B8 ghci> import Data.Char (toUpper) ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> import qualified System.IO.Streams as Streams ghci> let r = T.put "/foo" "text/plain" "some text" ghci> let f = Streams.map (B8.map toUpper) ghci> T.runHandler r (transformRequestBody
f >>readRequestBody
2048 >>=writeLBS
) HTTP/1.1 200 OK server: Snap/test date: Thu, 07 Aug 2014 20:30:15 GMT SOME TEXT
finishWith :: MonadSnap m => Response -> m a Source #
Short-circuits a Snap
monad action early, storing the given
Response
value in its state.
IMPORTANT: Be vary careful when using this with things like a DB library's
withTransaction
function or any other kind of setup/teardown block, as it
can prevent the cleanup from being called and result in resource leaks.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> import Control.Applicative ghci> let r = T.get "/" M.empty ghci> T.runHandler r ((ifTop
$writeBS
"TOP") <|>finishWith
emptyResponse
) HTTP/1.1 200 OK server: Snap/test date: Thu, 07 Aug 2014 16:58:57 GMT TOP ghci> let r' = T.get "/foo/bar" M.empty ghci> T.runHandler r' ((ifTop
$writeBS
"TOP") <|>finishWith
emptyResponse
) HTTP/1.1 200 OK server: Snap/test date: Thu, 07 Aug 2014 17:50:50 GMT
catchFinishWith :: Snap a -> Snap (Either Response a) Source #
Capture the flow of control in case a handler calls finishWith
.
WARNING: in the event of a call to transformRequestBody
it is possible
to violate HTTP protocol safety when using this function. If you call
catchFinishWith
it is suggested that you do not modify the body of the
Response
which was passed to the finishWith
call.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.ByteString.Char8 as B8 ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> import Control.Applicative ghci> let r = T.get "/foo/bar" M.empty ghci> let h = (ifTop
$writeBS
"TOP") <|>finishWith
emptyResponse
ghci> T.runHandler r (catchFinishWith
h >>=writeBS
. B8.pack . show) HTTP/1.1 200 OK server: Snap/test date: Thu, 07 Aug 2014 18:35:42 GMT Left HTTP/1.1 200 OK
pass :: MonadSnap m => m a 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.
Example:
ghci> :set -XOverloadedStrings
ghci> import qualified Data.Map as M
ghci> import qualified Snap.Test as T
ghci> let r = T.get "/foo/bar" M.empty
ghci> T.runHandler r pass
HTTP/1.1 404 Not Found
server: Snap/test
date: Thu, 07 Aug 2014 13:35:42 GMT
<!DOCTYPE html>
<html>
<head>
<title>Not found</title>
</head>
<body>
<code>No handler accepted "/foo/bar"/code
</body></html>
method :: MonadSnap m => Method -> m a -> m a Source #
Runs a Snap
monad action only if the request's HTTP method matches
the given method.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> let r = T.get "/foo/bar" M.empty ghci> T.runHandler r (method
GET
$writeBS
"OK") HTTP/1.1 200 OK server: Snap/test date: Thu, 07 Aug 2014 13:38:48 GMT OK ghci> T.runHandler r (method
POST
$writeBS
"OK") HTTP/1.1 404 Not Found ...
methods :: MonadSnap m => [Method] -> m a -> m a Source #
Runs a Snap
monad action only if the request's HTTP method matches
one of the given methods.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> let r = T.get "/foo/bar" M.empty ghci> T.runHandler r (methods
[GET
,POST
] $writeBS
"OK") HTTP/1.1 200 OK server: Snap/test date: Thu, 07 Aug 2014 13:38:48 GMT OK ghci> T.runHandler r (methods
[POST
] $writeBS
"OK") HTTP/1.1 404 Not Found ...
pathWith :: MonadSnap m => (ByteString -> ByteString -> Bool) -> ByteString -> m a -> m a Source #
:: MonadSnap m | |
=> ByteString | path component to match |
-> m a | handler to run |
-> m 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
.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> let r = T.get "/foo/bar" M.empty ghci> T.runHandler r (dir
"foo" $writeBS
"OK") HTTP/1.1 200 OK server: Snap/test date: Thu, 07 Aug 2014 14:52:24 GMT OK ghci> T.runHandler r (dir
"baz" $writeBS
"OK") HTTP/1.1 404 Not Found ...
:: MonadSnap m | |
=> ByteString | path to match against |
-> m a | handler to run |
-> m 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.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> T.runHandler (T.get "/foo" M.empty) (path
"foo" $writeBS
"bar") HTTP/1.1 200 OK server: Snap/test date: Thu, 07 Aug 2014 14:15:42 GMT bar ghci> T.runHandler (T.get "/foo" M.empty) (path
"bar" $writeBS
"baz") HTTP/1.1 404 Not Found ...
pathArg :: (Readable a, MonadSnap m) => (a -> m b) -> m b Source #
Runs a Snap
monad action only when the first path component is
successfully parsed as the argument to the supplied handler function.
Note that the path segment is url-decoded prior to being passed to fromBS
;
this is new as of snap-core 0.10.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> let r = T.get "/11/foo/bar" M.empty ghci> let f = (\i -> if i == 11 thenwriteBS
"11" elsewriteBS
"???") ghci> T.runHandler r (pathArg
f) HTTP/1.1 200 OK server: Snap/test date: Thu, 07 Aug 2014 14:27:10 GMT 11 ghci> let r' = T.get "/foo/11/bar" M.empty ghci> T.runHandler r' (pathArg
f) HTTP/1.1 404 Not Found ...
ifTop :: MonadSnap m => m a -> m a Source #
Runs a Snap
monad action only when rqPathInfo
is empty.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> let r = T.get "/" M.empty ghci> T.runHandler r (ifTop
$writeBS
OK) HTTP/1.1 200 OK server: Snap/test date: Thu, 07 Aug 2014 14:56:39 GMT OK ghci> let r' = T.get "/foo" M.empty ghci> T.runHandler r' (ifTop
$writeBS
"OK") HTTP/1.1 404 Not Found ...
getRequest :: MonadSnap m => m Request Source #
Grabs the Request
object out of the Snap
monad.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> let r = T.get "/foo/bar" M.empty ghci> T.runHandler r (writeBS
.rqURI
=<<getRequest
) HTTP/1.1 200 OK server: Snap/test date: Sat, 02 Aug 2014 07:51:54 GMT /foo/bar
getResponse :: MonadSnap m => m Response Source #
Grabs the Response
object out of the Snap
monad.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> let r = T.get "/foo/bar" M.empty ghci> T.runHandler r (writeBS
.rspStatusReason
=<<getResponse
) HTTP/1.1 200 OK server: Snap/test date: Sat, 02 Aug 2014 15:06:00 GMT OK
getsRequest :: MonadSnap m => (Request -> a) -> m a Source #
Grabs something out of the Request
object, using the given projection
function. See gets
.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> let r = T.get "/foo/bar" M.empty ghci> T.runHandler r (writeBS
=<<getsRequest
rqURI
) HTTP/1.1 200 OK server: Snap/test date: Sat, 02 Aug 2014 07:51:54 GMT /foo/bar
getsResponse :: MonadSnap m => (Response -> a) -> m a Source #
Grabs something out of the Response
object, using the given projection
function. See gets
.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> let r = T.get "/foo/bar" M.empty ghci> T.runHandler r (writeBS
=<<getsResponse
rspStatusReason
) HTTP/1.1 200 OK server: Snap/test date: Wed, 06 Aug 2014 13:35:45 GMT OK
putRequest :: MonadSnap m => Request -> m () Source #
Puts a new Request
object into the Snap
monad.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> :{ ghci| let hndlr = do rq <- T.buildRequest (T.get "/bar/foo" M.empty) ghci|putRequest
rq ghci| uri' <-getsRequest
rqURI
ghci|writeBS
uri' ghci| :} ghci> T.runHandler (T.get "/foo/bar" M.empty) hndlr HTTP/1.1 200 OK server: Snap/test date: Wed, 06 Aug 2014 15:13:46 GMT /bar/foo
putResponse :: MonadSnap m => Response -> m () Source #
Puts a new Response
object into the Snap
monad.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> let rsp =setResponseCode
404emptyResponse
ghci> let req = T.get "/foo/bar" M.empty ghci> T.runHandler req (putResponse
rsp) HTTP/1.1 404 Not Found server: Snap/test date: Wed, 06 Aug 2014 13:59:58 GMT
modifyRequest :: MonadSnap m => (Request -> Request) -> m () Source #
Modifies the Request
object stored in a Snap
monad.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> let r = T.get "/foo/bar" M.empty ghci> r' <- T.buildRequest $ T.get "/bar/foo" M.empty ghci> T.runHandler r (modifyRequest
(const r') >>getsRequest
rqURI
>>=writeBS
) HTTP/1.1 200 OK server: Snap/test date: Wed, 06 Aug 2014 15:24:25 GMT /bar/foo
modifyResponse :: MonadSnap m => (Response -> Response) -> m () Source #
Modifes the Response
object stored in a Snap
monad.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> let r = T.get "/foo/bar" M.empty ghci> T.runHandler r (modifyResponse
$setResponseCode
404) HTTP/1.1 404 Not Found server: Snap/test date: Wed, 06 Aug 2014 15:27:11 GMT
redirect :: MonadSnap m => ByteString -> m a Source #
Performs a redirect by setting the Location
header to the given target
URL/path and the status code to 302 in the Response
object stored in a
Snap
monad. Note that the target URL is not validated in any way.
Consider using redirect'
instead, which allows you to choose the correct
status code.
Example:
ghci> :set -XOverloadedStrings
ghci> import qualified Data.Map as M
ghci> import qualified Snap.Test as T
ghci> let r = T.get "/foo/bar" M.empty
ghci> T.runHandler r (redirect
"http://snapframework.com")
HTTP/1.1 302 Found
content-length: 0
location: http://snapframework.com
server: Snap/test
date: Thu, 07 Aug 2014 08:52:11 GMT
Content-Length: 0
redirect' :: MonadSnap m => ByteString -> Int -> m a Source #
Performs a redirect by setting the Location
header to the given target
URL/path and the status code (should be one of 301, 302, 303 or 307) in the
Response
object stored in a Snap
monad. Note that the target URL is not
validated in any way.
Example:
ghci> :set -XOverloadedStrings
ghci> import qualified Data.Map as M
ghci> import qualified Snap.Test as T
ghci> let r = T.get "/foo/bar" M.empty
ghci> T.runHandler r (redirect'
"http://snapframework.com" 301)
HTTP/1.1 307 Temporary Redirect
content-length: 0
location: http://snapframework.com
server: Snap/test
date: Thu, 07 Aug 2014 08:55:51 GMT
Content-Length: 0
logError :: MonadSnap m => ByteString -> m () Source #
Log an error message in the Snap
monad.
Example:
ghci> import qualified Data.ByteString.Char8 as B8 ghci>runSnap
(logError
"fatal error!") (error
. B8.unpack) undefined undefined *** Exception: fatal error!
:: MonadSnap m | |
=> (OutputStream Builder -> IO (OutputStream Builder)) | output to add |
-> m () |
Run the given stream procedure, adding its output to the Response
stored
in the Snap
monad state.
Example:
ghci> :set -XOverloadedStrings
ghci> import qualified Data.Map as M
ghci> import qualified Snap.Test as T
ghci> import qualified Data.ByteString.Builder as B
ghci> import qualified System.IO.Streams as Streams
ghci> let r = T.get "/foo/bar" M.empty
ghci> :{
ghci| let f str = do {
ghci| Streams.write (Just $ B.byteString "Hello, streams world") str;
ghci| return str }
ghci| :}
ghci> T.runHandler r (addToOutput
f)
HTTP/1.1 200 OK
server: Snap/test
date: Wed, 06 Aug 2014 17:55:47 GMT
Hello, streams world
writeBuilder :: MonadSnap m => Builder -> m () Source #
Adds the given Builder
to the body of the Response
stored in the
| Snap
monad state.
Example:
ghci> :set -XOverloadedStrings
ghci> import qualified Data.Map as M
ghci> import qualified Snap.Test as T
ghci> import qualified Data.ByteString.Builder as B
ghci> let r = T.get "/foo/bar" M.empty
ghci> T.runHandler r (writeBuilder
$ B.byteString "Hello, world")
HTTP/1.1 200 OK
server: Snap/test
date: Wed, 06 Aug 2014 17:33:33 GMT
Hello, world
writeBS :: MonadSnap m => ByteString -> m () 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.
Example:
ghci> :set -XOverloadedStrings
ghci> import qualified Data.Map as M
ghci> import qualified Snap.Test as T
ghci> let r = T.get "/foo/bar" M.empty
ghci> T.runHandler r (writeBS
"Hello, bytestring world")
HTTP/1.1 200 OK
server: Snap/test
date: Wed, 06 Aug 2014 17:34:27 GMT
Hello, bytestring world
writeLBS :: MonadSnap m => ByteString -> m () 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.
Example:
ghci> :set -XOverloadedStrings
ghci> import qualified Data.Map as M
ghci> import qualified Snap.Test as T
ghci> let r = T.get "/foo/bar" M.empty
ghci> T.runHandler r (writeLBS
"Hello, lazy bytestring world")
HTTP/1.1 200 OK
server: Snap/test
date: Wed, 06 Aug 2014 17:35:15 GMT
Hello, lazy bytestring world
writeText :: MonadSnap m => Text -> m () 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.
Example:
ghci> :set -XOverloadedStrings
ghci> import qualified Data.Map as M
ghci> import qualified Snap.Test as T
ghci> let r = T.get "/foo/bar" M.empty
ghci> T.runHandler r (writeText
"Hello, text world")
HTTP/1.1 200 OK
server: Snap/test
date: Wed, 06 Aug 2014 17:36:38 GMT
Hello, text world
writeLazyText :: MonadSnap m => Text -> m () 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.
Example:
ghci> :set -XOverloadedStrings
ghci> import qualified Data.Map as M
ghci> import qualified Snap.Test as T
ghci> let r = T.get "/foo/bar" M.empty
ghci> T.runHandler r (writeLazyText
"Hello, lazy text world")
HTTP/1.1 200 OK
server: Snap/test
date: Wed, 06 Aug 2014 17:37:41 GMT
Hello, lazy text world
sendFile :: MonadSnap m => FilePath -> m () 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()
.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci>writeFile
"/tmp/snap-file" "Hello, sendFile world" ghci> let r = T.get "/foo/bar" M.empty ghci> T.runHandler r (sendFile
"/tmp/snap-file") HTTP/1.1 200 OK content-length: 21 server: Snap/test date: Wed, 06 Aug 2014 17:45:10 GMT Content-Length: 21 Hello, sendFile world
sendFilePartial :: MonadSnap m => FilePath -> (Word64, Word64) -> m () Source #
Sets the output to be the contents of the specified file, within the given (start,end) range.
Calling sendFilePartial
will overwrite any output queued to be sent in
the Response
. If the response body is not modified after the call to
sendFilePartial
, 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()
.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci>writeFile
"/tmp/snap-file" "Hello, sendFilePartial world" ghci> let r = T.get "/foo/bar" M.empty ghci> T.runHandler r (sendFilePartial
"/tmp/snap-file" (7, 28)) HTTP/1.1 200 OK content-length: 21 server: Snap/test date: Wed, 06 Aug 2014 17:47:20 GMT Content-Length: 21 sendFilePartial world
localRequest :: MonadSnap m => (Request -> Request) -> m a -> m a Source #
Runs a Snap
action with a locally-modified Request
state
object. The Request
object in the Snap monad state after the call
to localRequest will be unchanged.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> let r = T.get "/foo/bar" M.empty ghci> r' <- T.buildRequest $ T.get "/bar/foo" M.empty ghci> let printRqURI =getsRequest
rqURI
>>=writeBS
>>writeBS
"\n" ghci> T.runHandler r (printRqURI >>localRequest
(const r') printRqURI) HTTP/1.1 200 OK server: Snap/test date: Wed, 06 Aug 2014 15:34:12 GMT /foo/bar /bar/foo
withRequest :: MonadSnap m => (Request -> m a) -> m a Source #
Fetches the Request
from state and hands it to the given action.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> import Control.Monad.IO.Class ghci> let r = T.get "/foo/bar" M.empty ghci> let h =withRequest
(\rq ->liftIO
(T.requestToString rq) >>=writeBS
) ghci> T.runHandler r h HTTP/1.1 200 OK server: Snap/test date: Wed, 06 Aug 2014 15:44:24 GMT GET /foo/bar HTTP/1.1 host: localhost
withResponse :: MonadSnap m => (Response -> m a) -> m a Source #
Fetches the Response
from state and hands it to the given action.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> let r = T.get "/foo/bar" M.empty ghci> T.runHandler r (withResponse
$writeBS
.rspStatusReason
) HTTP/1.1 200 OK server: Snap/test date: Wed, 06 Aug 2014 15:48:45 GMT OK
ipHeaderFilter :: MonadSnap m => m () 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' :: MonadSnap m => CI ByteString -> m () 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.
bracketSnap :: IO a -> (a -> IO b) -> (a -> Snap c) -> Snap c Source #
This function brackets a Snap action in resource acquisition and
release. This is provided because MonadCatchIO's bracket
function
doesn't work properly in the case of a short-circuit return from
the action being bracketed.
In order to prevent confusion regarding the effects of the aquisition and release actions on the Snap state, this function doesn't accept Snap actions for the acquire or release actions.
This function will run the release action in all cases where the acquire action succeeded. This includes the following behaviors from the bracketed Snap action.
- Normal completion
- Short-circuit completion, either from calling
fail
orfinishWith
- An exception being thrown.
Example:
ghci> :set -XOverloadedStrings
ghci> import qualified Data.Map as M
ghci> import qualified Snap.Test as T
ghci> let br = bracketSnap
(putStrLn "before") (const $ putStrLn "after")
ghci> T.runHandler (T.get "/" M.empty) (br $ const $ writeBS "OK")
before
after
HTTP/1.1 200 OK
server: Snap/test
date: Thu, 07 Aug 2014 18:41:50 GMT
OK
data NoHandlerException Source #
This exception is thrown if the handler you supply to runSnap
fails.
terminateConnection :: (Exception e, MonadSnap m) => e -> m a Source #
Terminate the HTTP session with the given exception.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> import qualified Control.Exception as E ghci> let r = T.get "/foo/bar" M.empty ghci> T.runHandler r (terminateConnection $ E.AssertionFailed "Assertion failed!") *** Exception: <terminated: Assertion failed!>
escapeHttp :: MonadSnap m => EscapeHttpHandler -> m () Source #
Terminate the HTTP session and hand control to some external handler, escaping all further HTTP traffic.
The external handler takes three arguments: a function to modify the thread's timeout, and a read and a write ends to the socket.
:: Snap a | Action to run. |
-> (ByteString -> IO ()) | Error logging action. |
-> ((Int -> Int) -> IO ()) | Timeout action. |
-> Request | HTTP request. |
-> IO (Request, Response) |
Runs a Snap
monad action.
This function is mostly intended for library writers; instead of invoking
runSnap
directly, use httpServe
or
runHandler
(for testing).
fixupResponse :: Request -> Response -> IO Response Source #
Post-process a finalized HTTP response:
- fixup content-length header
- properly handle 204/304 responses
- if request was HEAD, remove response body
Note that we do NOT deal with transfer-encoding: chunked or "connection: close" here.
getParamFrom :: MonadSnap m => (ByteString -> Request -> Maybe [ByteString]) -> ByteString -> m (Maybe ByteString) Source #
:: MonadSnap m | |
=> ByteString | parameter name to look up |
-> m (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
" "
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> import qualified Data.ByteString.Char8 as B8 ghci> let r = T.get "/foo/bar" $ M.fromList [("foo", ["bar"])] ghci> T.runHandler r (getParam
"foo" >>=writeBS
. B8.pack . show) HTTP/1.1 200 OK server: Snap/test date: Mon, 11 Aug 2014 12:57:20 GMT Just "bar"
:: MonadSnap m | |
=> ByteString | parameter name to look up |
-> m (Maybe ByteString) |
See rqPostParam
. Looks up a value for the given named parameter in the
POST form parameters mapping in Request
. If more than one value was
entered for the given parameter name, getPostParam
gloms the values
together with:
.intercalate
" "
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> import qualified Data.ByteString.Char8 as B8 ghci> let r = T.postUrlEncoded "/foo/bar" $ M.fromList [("foo", ["bar"])] ghci> T.runHandler r (getPostParam
"foo" >>=writeBS
. B8.pack . show) HTTP/1.1 200 OK server: Snap/test date: Mon, 11 Aug 2014 13:01:04 GMT Just "bar"
:: MonadSnap m | |
=> ByteString | parameter name to look up |
-> m (Maybe ByteString) |
See rqQueryParam
. Looks up a value for the given named parameter in the
query string parameters mapping in Request
. If more than one value was
entered for the given parameter name, getQueryParam
gloms the values
together with
.intercalate
" "
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> import qualified Data.ByteString.Char8 as B8 ghci> let r = T.postUrlEncoded "/foo/bar" M.empty >> T.setQueryStringRaw "foo=bar&foo=baz" ghci> T.runHandler r (getQueryParam
"foo" >>=writeBS
. B8.pack . show) HTTP/1.1 200 OK server: Snap/test date: Mon, 11 Aug 2014 13:06:50 GMT Just "bar baz"
getParams :: MonadSnap m => m Params Source #
See rqParams
. Convenience function to return Params
from the
Request
inside of a MonadSnap
instance.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> import qualified Data.ByteString.Char8 as B8 ghci> let r = T.get "/foo/bar" $ M.fromList [("foo", ["bar"])] ghci> T.runHandler r (getParams
>>=writeBS
. B8.pack . show) HTTP/1.1 200 OK server: Snap/test date: Mon, 11 Aug 2014 13:02:54 GMT fromList [("foo",["bar"])]
getPostParams :: MonadSnap m => m Params Source #
See rqParams
. Convenience function to return Params
from the
Request
inside of a MonadSnap
instance.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> import qualified Data.ByteString.Char8 as B8 ghci> let r = T.postUrlEncoded "/foo/bar" $ M.fromList [("foo", ["bar"])] ghci> T.runHandler r (getPostParams
>>=writeBS
. B8.pack . show) HTTP/1.1 200 OK server: Snap/test date: Mon, 11 Aug 2014 13:04:34 GMT fromList [("foo",["bar"])]
getQueryParams :: MonadSnap m => m Params Source #
See rqParams
. Convenience function to return Params
from the
Request
inside of a MonadSnap
instance.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> import qualified Data.ByteString.Char8 as B8 ghci> let r = T.postUrlEncoded "/foo/bar" M.empty >> T.setQueryStringRaw "foo=bar&foo=baz" ghci> T.runHandler r (getQueryParams
>>=writeBS
. B8.pack . show) HTTP/1.1 200 OK server: Snap/test date: Mon, 11 Aug 2014 13:10:17 GMT fromList [("foo",["bar","baz"])]
getCookie :: MonadSnap m => ByteString -> m (Maybe Cookie) Source #
Gets the HTTP Cookie
with the specified name.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> import qualified Data.ByteString.Char8 as B8 ghci> let cookie =Cookie
"name" "value" Nothing Nothing Nothing False False ghci> let r = T.get "/foo/bar" M.empty >> T.addCookies [cookie] ghci> T.runHandler r (getCookie
"name" >>=writeBS
. B8.pack . show) HTTP/1.1 200 OK server: Snap/test date: Thu, 07 Aug 2014 12:16:58 GMT Just (Cookie {cookieName = "name", cookieValue = "value", ...})
readCookie :: (MonadSnap m, Readable a) => ByteString -> m a Source #
Gets the HTTP Cookie
with the specified name and decodes it. If the
decoding fails, the handler calls pass.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> let cookie =Cookie
"name" "value" Nothing Nothing Nothing False False ghci> let r = T.get "/foo/bar" M.empty >> T.addCookies [cookie] ghci> T.runHandler r (readCookie
"name" >>=writeBS
) HTTP/1.1 200 OK server: Snap/test date: Thu, 07 Aug 2014 12:20:09 GMT value
expireCookie :: MonadSnap m => Cookie -> m () Source #
Expire given Cookie
in client's browser.
Example:
ghci> :set -XOverloadedStrings ghci> import qualified Data.Map as M ghci> import qualified Snap.Test as T ghci> let r = T.get "/foo/bar" M.empty ghci> let cookie = Cookie "name" "" Nothing (Just "/subsite") Nothing True False ghci> T.runHandler r (expireCookie
cookie) HTTP/1.1 200 OK set-cookie: name=; path=/subsite; expires=Sat, 24 Dec 1994 06:28:16 GMT; Secure server: Snap/test date: Thu, 07 Aug 2014 12:21:27 GMT ghci> let cookie = Cookie "name" "value" Nothing Nothing Nothing False False ghci> let r2 = T.get "/foo/bar" M.empty >> T.addCookies [cookie] ghci> T.runHandler r (getCookie
"name" >>= maybe (return ())expireCookie
) HTTP/1.1 200 OK set-cookie: name=; expires=Sat, 24 Dec 1994 06:28:16 GMT server: Snap/test
setTimeout :: MonadSnap m => Int -> m () Source #
Causes the handler thread to be killed n
seconds from now.
extendTimeout :: MonadSnap m => Int -> m () Source #
Causes the handler thread to be killed at least n
seconds from now.
modifyTimeout :: MonadSnap m => (Int -> Int) -> m () Source #
Modifies the amount of time remaining before the request times out.
getTimeoutModifier :: MonadSnap m => m ((Int -> Int) -> IO ()) Source #
Returns an IO
action which you can use to modify the timeout value.
module Snap.Internal.Http.Types