|
Network.HTTP.Lucu.Resource |
|
|
|
|
Description |
This is the Resource Monad; monadic actions to define the behavior
of each resources. The Resource Monad is a kind of IO
Monad thus it implements Control.Monad.Trans.MonadIO class. It is
also a state machine.
Request Processing Flow:
1. A client issues an HTTP request.
2. If the URI of it matches to any resource, the corresponding
Resource Monad starts running on a newly spawned thread.
3. The Resource Monad looks at the request header, find (or not
find) an entity, receive the request body (if any), decide the
response header, and decide the response body. This process
will be discussed later.
4. The Resource Monad and its thread stops running. The client
may or may not be sending us the next request at this point.
Resource Monad takes the following states. The initial state is
Examining Request and the final state is Done.
- Examining Request
- In this state, a Resource looks at the
request header and thinks about an entity for it. If there is a
suitable entity, the Resource tells 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, Resource does nothing in this state.
- Getting Body
- A Resource asks the system to receive a
request body from client. Before actually reading from the
socket, the system sends "100 Continue" to the client if need
be. When a Resource transits to the next state without
receiving all or part of request body, the system still reads it
and just throws it away.
- Deciding Header
- A Resource makes a decision of status code
and response header. When it transits to the next state, the
system checks the validness of response header and then write
them to the socket.
- Deciding Body
- In this state, a Resource asks the system to
write some response body to the socket. When it transits to the
next state without writing any response body, the system
completes it depending on the status code.
- Done
- Everything is over. A Resource can 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 Resource, nor we don't want to
postpone writing the entire response till the end of Resource
computation.
|
|
Synopsis |
|
|
|
|
Monad
|
|
|
The Resource monad. This monad implements
Control.Monad.Trans.MonadIO so it can do any IO
actions.
| Instances | |
|
|
Actions
|
|
Getting request header
|
|
These actions can be computed regardless of the current state,
and they don't change the state.
|
|
|
Get the Config value which is used for
the httpd.
|
|
|
Get the SockAddr of the remote host. If you want
a string representation instead of SockAddr, use
getRemoteAddr'.
|
|
|
Get the string representation of the address of remote host. If
you want a SockAddr instead of String,
use getRemoteAddr.
|
|
|
Get the Request value which represents
the request header. In general you don't have to use this action.
|
|
|
Get the Method value of the request.
|
|
|
Get the URI of the request.
|
|
|
Get the HTTP version of the request.
|
|
|
Get the path of this Resource (to be exact,
Network.HTTP.Lucu.Resource.Tree.ResourceDef) in the
Network.HTTP.Lucu.Resource.Tree.ResTree. The result of this
action is the exact path in the tree even if the
Network.HTTP.Lucu.Resource.Tree.ResourceDef is greedy.
Example:
main = let tree = mkResTree [ (["foo"], resFoo) ]
in runHttpd defaultConfig tree
resFoo = ResourceDef {
resIsGreedy = True
, resGet = Just $ do requestURI <- getRequestURI
resourcePath <- getResourcePath
pathInfo <- getPathInfo
-- uriPath requestURI == "/foo/bar/baz"
-- resourcePath == ["foo"]
-- pathInfo == ["bar", "baz"]
...
, ...
}
|
|
|
This is an analogy of CGI PATH_INFO. Its result is always [] if
the Network.HTTP.Lucu.Resource.Tree.ResourceDef is not
greedy. See getResourcePath.
|
|
|
Assume the query part of request URI as
application/x-www-form-urlencoded, and parse it. This action
doesn't parse the request body. See inputForm.
|
|
|
Get a value of given request header. Comparison of header name is
case-insensitive. Note that this action is not intended to be used
so frequently: there should be actions like getContentType for
every common headers.
|
|
|
Get a list of MIMEType enumerated on
header "Accept".
|
|
|
Get a list of (contentCoding, qvalue) enumerated on header
"Accept-Encoding". The list is sorted in descending order by
qvalue.
|
|
|
Check whether a given content-coding is acceptable.
|
|
|
Get the header "Content-Type" as
MIMEType.
|
|
|
Get the header "Authorization" as
AuthCredential.
|
|
Finding an entity
|
|
These actions can be computed only in the Examining Request
state. After the computation, the Resource transits to
Getting Body state.
|
|
|
Tell the system that the Resource 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 up to now. It is an error to
compute foundEntity if this is a POST request.
Computation of foundEntity performs "If-Match" test or
"If-None-Match" test if possible. When those tests fail, the
computation of Resource immediately aborts with status "412
Precondition Failed" or "304 Not Modified" depending on the
situation.
If this is a GET or HEAD request, foundEntity automatically puts
"ETag" and "Last-Modified" headers into the response.
|
|
|
Tell the system that the Resource found an entity for the
request URI. The only difference from foundEntity is that
foundETag doesn't (and can't) put "Last-Modified" header into
the response.
This action is not preferred. You should use foundEntity whenever
possible.
|
|
|
Tell the system that the Resource 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 last
modification time are unsafe because it is possible to mess up such
tests by modifying the entity twice in a second.
This action is not preferred. You should use foundEntity whenever
possible.
|
|
|
Computation of foundNoEntity mStr tells the system that the
Resource found no entity for the request URI. mStr is an
optional error message to be replied to the client.
If this is a PUT request, foundNoEntity performs "If-Match"
test and aborts with status "412 Precondition Failed" when it
failed. If this is a GET, HEAD, POST or DELETE request,
foundNoEntity always aborts with status "404 Not Found".
|
|
Getting a request body
|
|
Computation of these actions changes the state to /Getting
Body/.
|
|
|
Computation of input limit attempts to read the request body
up to limit bytes, and then make the Resource transit to
Deciding Header state. When the actual size of body is larger
than limit bytes, computation of Resource immediately aborts
with status "413 Request Entity Too Large". When the request has
no body, input returns an empty string.
limit may be less than or equal to zero. In this case, the
default limitation value
(cnfMaxEntityLength) is used. See
defaultLimit.
Note that inputLBS is more efficient than input so you should
use it whenever possible.
|
|
|
Computation of inputChunk limit attempts to read a part of
request body up to limit bytes. You can read any large request by
repeating computation of this action. When you've read all the
request body, inputChunk returns an empty string and then make
the Resource transit to Deciding Header state.
limit may be less than or equal to zero. In this case, the
default limitation value
(cnfMaxEntityLength) is used. See
defaultLimit.
Note that inputChunkLBS is more efficient than inputChunk so you
should use it whenever possible.
|
|
|
This is mostly the same as input but is more
efficient. inputLBS returns a Data.ByteString.Lazy.ByteString
but it's not really lazy: reading from the socket just happens at
the computation of inputLBS, not at the evaluation of the
Data.ByteString.Lazy.ByteString. The same goes for
inputChunkLBS.
|
|
|
This is mostly the same as inputChunk but is more
efficient. See inputLBS.
|
|
|
Computation of inputForm limit attempts to read the request
body with input and parse it as
application/x-www-form-urlencoded or multipart/form-data. If the
request header "Content-Type" is neither of them, inputForm
makes Resource abort with status "415 Unsupported Media
Type". If the request has no "Content-Type", it aborts with
"400 Bad Request".
|
|
|
This is just a constant -1. It's better to say input
defaultLimit than to say input (-1) but these are exactly
the same.
|
|
Setting response headers
|
|
Computation of these actions changes the state to /Deciding
Header/.
|
|
|
Set the response status code. If you omit to compute this action,
the status code will be defaulted to "200 OK".
|
|
|
Set a value of given resource header. Comparison of header name
is case-insensitive. Note that this action is not intended to be
used so frequently: there should be actions like 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 process it causes a catastrophe
to send a header "Content-Length: 10" and actually send a body of
20 bytes long. 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 header of the next response.
|
|
|
Computation of redirect code uri sets the response status to
code and "Location" header to uri. The code must satisfy
isRedirection or it causes an error.
|
|
|
Computation of setContentType mType sets the response header
"Content-Type" to mType.
|
|
|
Computation of setLocation uri sets the response header
"Location" to uri.
|
|
|
Computation of setContentEncoding codings sets the response
header "Content-Encoding" to codings.
|
|
|
Computation of setWWWAuthenticate challenge sets the response
header "WWW-Authenticate" to challenge.
|
|
Writing a response body
|
|
Computation of these actions changes the state to /Deciding
Body/.
|
|
|
Computation of output str writes str as a response body,
and then make the Resource transit to Done state. It is safe to
apply output to an infinite string, such as a lazy stream of
/dev/random.
Note that outputLBS is more efficient than output so you should
use it whenever possible.
|
|
|
Computation of outputChunk str writes str as a part of
response body. You can compute this action multiple times to write
a body little at a time. It is safe to apply outputChunk to an
infinite string.
Note that outputChunkLBS is more efficient than outputChunk so
you should use it whenever possible.
|
|
|
This is mostly the same as output but is more efficient.
|
|
|
This is mostly the same as outputChunk but is more efficient.
|
|
Produced by Haddock version 2.4.2 |