Network.HTTP.Lucu.Resource
Contents
Description
This is the Resource Monad; monadic actions to define a behavior
of resource. The Rsrc Monad is a kind of IO Monad thus it
implements MonadIO class, and it is a state machine as well.
Request Processing Flow:
- A client issues an HTTP request.
- If the URI of it matches to any resource, the corresponding
RsrcMonad starts running on a newly spawned thread. - The
RsrcMonad looks at request headers, find (or not find) an entity, receive the request body (if any), send response headers, and then send a response body. This process will be discussed later. - The
RsrcMonad and its thread stops running. The client may or may not be sending us the next request at this point.
Rsrc Monad takes the following states. The initial state is
Examining Request and the final state is Done.
- Examining Request
- In this state, a
Rsrclooks at the request header fields and thinks about the corresponding entity for it. If there is a suitable entity, theRsrctells the system an entity tag and its last modification time (foundEntity). If it found no entity, it tells the system so (foundNoEntity). In case it is impossible to decide the existence of entity, which is a typical case for POST requests,Rsrcdoes nothing in this state. - Receiving Body
- A
Rsrcasks the system to receive a request body from the client. Before actually reading from the socket, the system sends "100 Continue" to the client if need be. When aRsrctransits to the next state without receiving all or part of a request body, the system automatically discards it. - Deciding Header
- A
Rsrcmakes a decision of response status code and header fields. When it transits to the next state, the system validates and completes the header fields and then sends them to the client. - Sending Body
- In this state, a
Rsrcasks the system to write some response body to the socket. When it transits to the next state without writing any response body, the system automatically completes it depending on the status code. (To be exact, such completion only occurs when theRsrctransits to this state without even declaring the "Content-Type" header field. See:setContentType) - Done
- Everything is over. A
Rsrccan do nothing for the HTTP interaction anymore.
Note that the state transition is one-way: for instance, it is an
error to try to read a request body after writing some
response. This limitation is for efficiency. We don't want to read
the entire request before starting Rsrc, nor we don't want to
postpone writing the entire response till the end of Rsrc
computation.
- data Resource = Resource {}
- data Rsrc a
- data FormData = FormData {
- fdFileName :: !(Maybe Text)
- fdMIMEType :: !MIMEType
- fdContent :: !ByteString
- getConfig :: Rsrc Config
- getRemoteAddr :: Rsrc SockAddr
- getRemoteAddr' :: Rsrc HostName
- getRemoteHost :: Rsrc (Maybe HostName)
- getRemoteCertificate :: Rsrc (Maybe X509)
- getRequest :: Rsrc Request
- getMethod :: Rsrc Method
- getRequestURI :: Rsrc URI
- getRequestVersion :: Rsrc HttpVersion
- getResourcePath :: Rsrc Path
- getPathInfo :: Rsrc [ByteString]
- getQueryForm :: Rsrc [(ByteString, FormData)]
- getHeader :: CIAscii -> Rsrc (Maybe Ascii)
- getAccept :: Rsrc [MIMEType]
- getAcceptEncoding :: Rsrc [(CIAscii, Maybe Double)]
- isEncodingAcceptable :: CIAscii -> Rsrc Bool
- getContentType :: Rsrc (Maybe MIMEType)
- getAuthorization :: Rsrc (Maybe AuthCredential)
- foundEntity :: ETag -> UTCTime -> Rsrc ()
- foundETag :: ETag -> Rsrc ()
- foundTimeStamp :: UTCTime -> Rsrc ()
- foundNoEntity :: Maybe Text -> Rsrc ()
- foundNoEntity' :: Rsrc ()
- getChunk :: Int -> Rsrc ByteString
- getChunks :: Maybe Int -> Rsrc ByteString
- getForm :: Maybe Int -> Rsrc [(ByteString, FormData)]
- setStatus :: StatusCode sc => sc -> Rsrc ()
- redirect :: StatusCode sc => sc -> URI -> Rsrc ()
- setContentType :: MIMEType -> Rsrc ()
- setContentEncoding :: [CIAscii] -> Rsrc ()
- setWWWAuthenticate :: AuthChallenge -> Rsrc ()
- setLocation :: URI -> Rsrc ()
- setHeader :: CIAscii -> Ascii -> Rsrc ()
- deleteHeader :: CIAscii -> Rsrc ()
- putChunk :: ByteString -> Rsrc ()
- putChunks :: ByteString -> Rsrc ()
- putBuilder :: Builder -> Rsrc ()
Types
Constructors
| Resource | |
Fields
| |
The resource monad. This monad implements MonadIO so it can do
any IO actions.
FormData represents a form value and possibly an uploaded file
name.
Constructors
| FormData | |
Fields
| |
Getting request header
These functions can be called regardless of the current state,
and they don't change the state of Rsrc.
getRemoteAddr :: Rsrc SockAddrSource
Get the SockAddr of the remote host.
getRemoteAddr' :: Rsrc HostNameSource
Get the string representation of the address of remote host. If
you want a SockAddr instead of HostName, use getRemoteAddr.
getRemoteHost :: Rsrc (Maybe HostName)Source
Resolve an address to the remote host.
getRemoteCertificate :: Rsrc (Maybe X509)Source
getRequest :: Rsrc RequestSource
Return the Request value representing the request header. You
usually don't need to call this function directly.
getRequestURI :: Rsrc URISource
Get the URI of the request.
getRequestVersion :: Rsrc HttpVersionSource
Get the HTTP version of the request.
getResourcePath :: Rsrc PathSource
Get the path of this Rsrc (to be exact, Resource) in the
corresponding Network.HTTP.Lucu.ResourceTree. The result of this
action is the exact path in the tree even when the Resource is
Network.HTTP.Lucu.greedy.
Example:
main ::IO() main = let tree ::Network.HTTP.Lucu.ResourceTreetree =fromList[ ([foo],Network.HTTP.Lucu.greedyresFoo) ] inNetwork.withSocketsDo.Network.HTTP.Lucu.runHttpddefaultConfig$Network.HTTP.Lucu.resourceMaptree resFoo ::ResourceresFoo =singleton(GET, do requestURI <-getRequestURIresourcePath <-getResourcePathpathInfo <-getPathInfo--Network.URI.uriPathrequestURI=="foobar/baz" -- resourcePath == [foo] -- pathInfo == [bar, baz] ... )
getPathInfo :: Rsrc [ByteString]Source
This is an analogy of CGI PATH_INFO. getPathInfo always returns
[] if the corresponding Resource is not greedy. See
getResourcePath.
Note that the returned path components are URI-decoded.
getQueryForm :: Rsrc [(ByteString, FormData)]Source
Assume the query part of request URI as
application/x-www-form-urlencoded, and parse it into pairs of
(name, formData). This function doesn't read the request
body.
getHeader :: CIAscii -> Rsrc (Maybe Ascii)Source
returns the value of the request header field
getHeader namename. Comparison of header name is case-insensitive. Note that
this function is not intended to be used so frequently: there
should be functions like getContentType for every common headers.
getAccept :: Rsrc [MIMEType]Source
Return the list of MIMEType enumerated on the value of request
header "Accept", or [] if absent.
getAcceptEncoding :: Rsrc [(CIAscii, Maybe Double)]Source
Return the list of (contentCoding, qvalue) enumerated on the
value of request header "Accept-Encoding". The list is sorted in
descending order by qvalue.
isEncodingAcceptable :: CIAscii -> Rsrc BoolSource
Return True iff a given content-coding is acceptable by the
client.
getContentType :: Rsrc (Maybe MIMEType)Source
Return the value of request header "Content-Type" as MIMEType.
getAuthorization :: Rsrc (Maybe AuthCredential)Source
Return the value of request header "Authorization" as
AuthCredential.
Finding an entity
These functions can be called only in the Examining Request
state. They make the Rsrc transit to the Receiving Body
state.
foundEntity :: ETag -> UTCTime -> Rsrc ()Source
Tell the system that the Rsrc found an entity for the request
URI. If this is a GET or HEAD request, a found entity means a datum
to be replied. If this is a PUT or DELETE request, it means a datum
which was stored for the URI until now. For POST requests it raises
an error.
foundEntity performs "If-Match" test or "If-None-Match" test
whenever possible, and if those tests fail, it immediately aborts
with status "412 Precondition Failed" or "304 Not Modified"
depending on the situation.
If the request method is either GET or HEAD, foundEntity
automatically puts "ETag" and "Last-Modified" headers into the
response.
foundETag :: ETag -> Rsrc ()Source
Tell the system that the Rsrc found an entity for the request
URI. The only difference from foundEntity is that foundETag
doesn't (nor can't) put "Last-Modified" header into the response.
Using this function is discouraged. You should use foundEntity
whenever possible.
foundTimeStamp :: UTCTime -> Rsrc ()Source
Tell the system that the Rsrc found an entity for the
request URI. The only difference from foundEntity is that
foundTimeStamp performs "If-Modified-Since" test or
"If-Unmodified-Since" test instead of "If-Match" test or
"If-None-Match" test. Be aware that any tests based on a last
modification time are unsafe because it is possible to mess up such
tests by modifying the entity twice in a second.
Using this function is discouraged. You should use foundEntity
whenever possible.
foundNoEntity :: Maybe Text -> Rsrc ()Source
tells the system that the foundNoEntity mStrRsrc found no
entity for the request URI. mStr is an optional error message to
be replied to the client.
If the request method is PUT, foundNoEntity performs "If-Match"
test and when that fails it aborts with status "412 Precondition
Failed". If the request method is GET, HEAD, POST or DELETE,
foundNoEntity always aborts with status "404 Not Found".
foundNoEntity' :: Rsrc ()Source
foundNoEntity' is the same as .
foundNoEntity Nothing
Receiving a request body
These functions make the Rsrc transit to the /Receiving
Body/ state.
getChunk :: Int -> Rsrc ByteStringSource
getChunks :: Maybe Int -> Rsrc ByteStringSource
attemts to read the entire request body up to
getChunks limitlimit bytes, and then make the Rsrc transit to the /Deciding
Header/ state. When the actual size of the body is larger than
limit bytes, getChunks immediately aborts with status "413
Request Entity Too Large". When the request has no body, it
returns an empty string.
When the limit is Nothing, getChunks uses the default
limitation value (cnfMaxEntityLength) instead.
getChunks returns a lazy ByteString but it's not really
lazy: reading from the socket just happens at the computation of
getChunks, not at the evaluation of the ByteString.
getForm :: Maybe Int -> Rsrc [(ByteString, FormData)]Source
attempts to read the request body with
getForm limitgetChunks and parse it as application/x-www-form-urlencoded or
multipart/form-data. If the request header "Content-Type" is
neither of them, getForm aborts with status "415 Unsupported
Media Type". If the request has no "Content-Type", it aborts
with "400 Bad Request".
Note that there are currently a few limitations on parsing
multipart/form-data. See: parseMultipartFormData
Declaring response status and header fields
These functions can be called at any time before transiting to the Sending Body state, but they themselves never causes any state transitions.
setStatus :: StatusCode sc => sc -> Rsrc ()Source
Declare the response status code. If you don't call this function, the status code will be defaulted to "200 OK".
redirect :: StatusCode sc => sc -> URI -> Rsrc ()Source
declares the response status as redirect code uricode and
"Location" header field as uri. The code must satisfy
isRedirection or it raises an error.
setContentType :: MIMEType -> Rsrc ()Source
declares the response header
"Content-Type" as setContentType mTypemType. Declaring "Content-Type" is
mandatory for sending a response body.
setContentEncoding :: [CIAscii] -> Rsrc ()Source
declares the response header
"Content-Encoding" as setContentEncoding codingscodings.
setWWWAuthenticate :: AuthChallenge -> Rsrc ()Source
declares the response header
"WWW-Authenticate" as setWWWAuthenticate challengechallenge.
Less frequently used functions
setLocation :: URI -> Rsrc ()Source
declares the response header "Location" as
setLocation uriuri. You usually don't need to call this function directly.
setHeader :: CIAscii -> Ascii -> Rsrc ()Source
declares the value of the response header
setHeader name valuename as value. Note that this function is not intended to be
used so frequently: there should be specialised functions like
Network.HTTP.Lucu.setContentType for every common headers.
Some important headers (especially "Content-Length" and "Transfer-Encoding") may be silently dropped or overwritten by the system not to corrupt the interaction with client at the viewpoint of HTTP protocol layer. For instance, if we are keeping the connection alive, without this manipulation it will be a catastrophe when we send a header "Content-Length: 10" and actually send a body of 20 bytes long to the remote peer. In this case the client shall only accept the first 10 bytes of response body and thinks that the residual 10 bytes is a part of the header of the next response.
deleteHeader :: CIAscii -> Rsrc ()Source
deletes a response header deleteHeader namename if
any. This function is not intended to be used so frequently.
Sending a response body
These functions make the Rsrc transit to the Sending Body
state.
putChunk :: ByteString -> Rsrc ()Source
Write a chunk in ByteString to the response body. You
must first declare the response header "Content-Type" before
applying this function. See setContentType.
putChunks :: ByteString -> Rsrc ()Source
Write a chunk in lazy ByteString to the response body. It
can be safely applied to an infinitely long ByteString.
Note that you must first declare the response header
"Content-Type" before applying this function. See
setContentType.
putBuilder :: Builder -> Rsrc ()Source
Run a Builder to construct a chunk, and write it to the response
body. It can be safely applied to a Builder producing an
infinitely long stream of octets.
Note that you must first declare the response header
"Content-Type" before applying this function. See
Network.HTTP.Lucu.setContentType.