- newtype HttpRoute m = HttpRoute (HttpReq -> Maybe (Iter ByteString m (HttpResp m)))
- runHttpRoute :: Monad m => HttpRoute m -> HttpReq -> Iter ByteString m (HttpResp m)
- addHeader :: Monad m => ByteString -> HttpRoute m -> HttpRoute m
- routeConst :: Monad m => HttpResp m -> HttpRoute m
- routeFn :: (HttpReq -> Iter ByteString m (HttpResp m)) -> HttpRoute m
- routeReq :: (HttpReq -> HttpRoute m) -> HttpRoute m
- routeMethod :: String -> HttpRoute m -> HttpRoute m
- routeHost :: String -> HttpRoute m -> HttpRoute m
- routeTop :: HttpRoute m -> HttpRoute m
- type HttpMap m = [(String, HttpRoute m)]
- routeMap :: Monad m => HttpMap m -> HttpRoute m
- routeMap' :: HttpMap m -> HttpRoute m
- routeName :: String -> HttpRoute m -> HttpRoute m
- routePath :: String -> HttpRoute m -> HttpRoute m
- routeVar :: HttpRoute m -> HttpRoute m
- mimeTypesI :: Monad m => String -> Iter ByteString m (String -> ByteString)
- dirRedir :: Monad m => FilePath -> FilePath -> HttpRoute m
- routeFileSys :: MonadIO m => (String -> ByteString) -> (FilePath -> HttpRoute m) -> FilePath -> HttpRoute m
- data FileSystemCalls h m = FileSystemCalls {
- fs_stat :: !(FilePath -> Iter ByteString m FileStatus)
- fs_open :: !(FilePath -> Iter ByteString m h)
- fs_close :: !(h -> Iter ByteString m ())
- fs_fstat :: !(h -> Iter ByteString m FileStatus)
- fs_enum :: !(h -> Iter ByteString m (Onum ByteString m (IterR ByteString m ())))
- routeGenFileSys :: Monad m => FileSystemCalls h m -> (String -> ByteString) -> (FilePath -> HttpRoute m) -> FilePath -> HttpRoute m
Documentation
Simple HTTP request routing structure for inumHttpServer
. This
is a wrapper around a function on HttpReq
structures. If the
function accepts the HttpReq
, it returns Just
a response
action. Otherwise it returns Nothing
.
HttpRoute
is a Monoid
, and hence can be concatenated with
mappend
or mconcat
. For example, you can say something like:
simpleServer :: Iter L.ByteString IO () -- Output to web browser -> Onum L.ByteString IO () -- Input from web browser -> IO () simpleServer iter enum = enum |$ inumHttpServer server .| iter where htdocs = "/var/www/htdocs" server = ioHttpServer $ runHttpRoute routing routing = mconcat [ routeTop $ routeConst $ resp301 "/start.html" , routeName "apps" $ routeMap apps , routeFileSys mimeMap "index.html" htdocs ] apps = [ ("app1", routeFn app1) , ("app2", routeFn app2) ] app1 :: (Monad m) => HttpReq -> Iter L.ByteString m (HttpResp m) app1 = ...
The above function will redirect requests for /
to the URL
/start.html
using an HTTP 301 (Moved Permanently) response. Any
request for a path under apps
will be redirected to the
functions app1
, app2
, etc. Finally, any other file name will
be served out of the file system under the "/var/www/htdocs"
directory. (This example assumes mimeMap
has been constructed as
discussed for mimeTypesI
.)
runHttpRoute :: Monad m => HttpRoute m -> HttpReq -> Iter ByteString m (HttpResp m)Source
addHeader :: Monad m => ByteString -> HttpRoute m -> HttpRoute mSource
Prepend a header field to the response produced by an HttpRoute
if that HttpRoute
is successful. For example, to let clients
cache static data for an hour, you might use:
addHeader (pack
"Cache-control: max-age=3600") $routeFileSys
mime (dirRedir
"index.html") "/var/www/htdocs"
routeConst :: Monad m => HttpResp m -> HttpRoute mSource
Route all requests to a constant response action that does not
depend on the request. This route always succeeds, so anything
mappend
ed will never be used.
routeFn :: (HttpReq -> Iter ByteString m (HttpResp m)) -> HttpRoute mSource
Route all requests to a particular function. This route always
succeeds, so anything mappend
ed will never be used.
routeReq :: (HttpReq -> HttpRoute m) -> HttpRoute mSource
Select a route based on some arbitrary function of the request.
For most purposes, the existing predicates (routeName
,
routePath
, etc.) should be fine, but occationally you might want
to define a custom predicate. For example, to reject methods other
then "GET" or "POST" at the top of your route, you could say:
myRoute =mconcat
[ rejectBadMethod , otherRoute1 , ... ] ... rejectBadMethod ::HttpRoute
m rejectBadMethod = routeReq $ req -> casereqMethod
req of s | s ==pack
"GET" || s ==pack
"PUT" ->mempty
{- reject route, falling through to rest of myRoute -} _ ->routeConst
$resp405
req {- reject request -}
:: String | String method should match |
-> HttpRoute m | Target route to take if method matches |
-> HttpRoute m |
Route based on the method (GET, POST, HEAD, etc.) in a request.
:: String | String to compare against host (must be lower-case) |
-> HttpRoute m | Target route to follow if host matches |
-> HttpRoute m |
Route requests whose "Host:" header matches a particular string.
routeMap :: Monad m => HttpMap m -> HttpRoute mSource
routeMap
builds an efficient map out of a list of
(directory_name,
pairs. It matches all requests and
returns a 404 error if there is a request for a name not present in
the map.
HttpRoute
)
routeMap' :: HttpMap m -> HttpRoute mSource
routeMap'
is like routeMap
, but only matches names that exist
in the map. Thus, multiple routeMap'
results can be combined
with mappend
. By contrast, combining routeMap
results with
mappend
is useless--the first one will match all requests (and
return a 404 error for names that do not appear in the map).
routeName :: String -> HttpRoute m -> HttpRoute mSource
Routes a specific directory name, like routeMap
for a singleton
map.
routePath :: String -> HttpRoute m -> HttpRoute mSource
Routes a specific path, like routeName
, except that the path
can include several directories.
routeVar :: HttpRoute m -> HttpRoute mSource
Matches any directory name, but additionally pushes it onto the
front of the reqPathParams
list in the HttpReq
structure. This
allows the name to serve as a variable argument to the eventual
handling function.
mimeTypesI :: Monad m => String -> Iter ByteString m (String -> ByteString)Source
Parses mime.types
file data. Returns a function mapping file
suffixes to mime types. The argument is a default mime type for
suffixes to do not match any in the mime.types data. (Reasonable
defaults might be "text/html"
, "text/plain"
, or, more
pedantically but less usefully, "application/octet-stream"
.)
Since this likely doesn't change, it is convenient just to define it once in your program, for instance with something like:
mimeMap :: String -> S8.ByteString mimeMap = unsafePerformIO $ do path <- findMimeTypes ["mime.types" , "/etc/mime.types" , "/var/www/conf/mime.types"] enumFile path |$ mimeTypesI "application/octet-stream" where findMimeTypes (h:t) = do exist <- fileExist h if exist then return h else findMimeTypes t findMimeTypes [] = return "mime.types" -- cause error
dirRedir :: Monad m => FilePath -> FilePath -> HttpRoute mSource
dirRedir indexFileName
redirects requests to the URL formed by
appending "/" ++ indexFileName
to the requested URL.
:: MonadIO m | |
=> (String -> ByteString) | Map of file suffixes to mime types (see |
-> (FilePath -> HttpRoute m) | Handler to invoke when the URL maps to a directory in the file system. Reasonable options include: |
-> FilePath | Pathname of directory to serve from file system |
-> HttpRoute m |
Route a request to a directory tree in the file system. It gets the Content-Length from the target file's attributes (after opening the file). Thus, overwriting files on an active server could cause problems, while renaming new files into place should be safe.
data FileSystemCalls h m Source
An abstract representation of file system calls returning an
opaque handle type h
in an Iter
parameterized by an arbitrary
Monad
m
. This representation allows one to use
routeGenFileSys
in a monad that is not an instance of MonadIO
.
FileSystemCalls | |
|
routeGenFileSys :: Monad m => FileSystemCalls h m -> (String -> ByteString) -> (FilePath -> HttpRoute m) -> FilePath -> HttpRoute mSource
A generalized version of routeFileSys
that takes a
FileSystemCalls
object and can therefore work outside of the
MonadIO
monad. Other than the FileSystemCalls
object, the
arguments and their meaning are identical to routeFileSys
.