Lucu-0.1: HTTP Daemonic Library




Lucu is an HTTP daemonic library. It can be embedded in any Haskell program and runs in an independent thread.


Full support of HTTP\1.1/
Lucu supports request pipelining, chunked I/O, ETag comparison and "100 Continue".
Lucu doesn't fork/exec to handle requests like CGI. It just spawns a new thread. Inter-process communication is done with STM.

Lucu is not a replacement for Apache. It is intended to be used to create an efficient web-based application without messing around FastCGI. It is also intended to be run behind a reverse-proxy so it doesn't have the following (otherwise essential) facilities:

Lucu doesn't log any requests from any clients.
Client Filtering
Lucu always accepts any clients. No IP filter is implemented.
SSL Support
Lucu can handle only HTTP.
Bandwidth Limitting
Lucu doesn't limit bandwidth it consumes.
Protection Against Wicked Clients
Lucu is fragile against wicked clients. No attacker should be able to cause a buffer-overflow but can possibly DoS it.


Entry Point

runHttpd :: Config -> ResTree -> [FallbackHandler] -> IO ()Source

This is the entry point of Lucu httpd. It listens to a socket and waits for clients. Computation of runHttpd never stops by itself so the only way to stop it is to raise an exception in the thread computing it.

Note that runHttpd automatically makes SIGPIPE be ignored by computing installHandler sigPIPE Ignore Nothing. This can hardly cause a problem but it may do.


 module Main where
 import Network.HTTP.Lucu
 main :: IO ()
 main = let config    = defaultConfig
            resources = mkResTree [ ([], helloWorld) ]
          runHttpd config resourcees []

 helloWorld :: ResourceDef
 helloWorld = ResourceDef {
                resUsesNativeThread = False
              , resIsGreedy         = False
              , resGet
                  = Just $ do setContentType $ read "text/plain"
                              output "Hello, world!"
              , resHead   = Nothing
              , resPost   = Nothing
              , resPut    = Nothing
              , resDelete = Nothing


Resource Tree

data ResourceDef Source

ResourceDef is basically a set of Resource monads for each HTTP methods.




resUsesNativeThread :: !Bool

Whether to run a Resource on a native thread (spawned by forkOS) or to run it on a user thread (spanwed by forkIO). Generally you don't need to set this field to True.

resIsGreedy :: !Bool

Whether to be greedy or not.

Say a client is trying to access /aaa/bbb/ccc. If there is a greedy resource at /aaa/bbb, it is always chosen even if there is another resource at /aaa/bbb/ccc. If the resource at /aaa/bbb is not greedy, it is just ignored. Greedy resources are like CGI scripts.

resGet :: !(Maybe (Resource ()))

A Resource to be run when a GET request comes for the resource path. If resGet is Nothing, the system responds "405 Method Not Allowed" for GET requests.

It also runs for HEAD request if the resHead is Nothing. In this case output and such like don't actually write a response body.

resHead :: !(Maybe (Resource ()))

A Resource to be run when a HEAD request comes for the resource path. If resHead is Nothing, the system runs resGet instead. If resGet is also Nothing, the system responds "405 Method Not Allowed" for HEAD requests.

resPost :: !(Maybe (Resource ()))

A Resource to be run when a POST request comes for the resource path. If resPost is Nothing, the system responds "405 Method Not Allowed" for POST requests.

resPut :: !(Maybe (Resource ()))

A Resource to be run when a PUT request comes for the resource path. If resPut is Nothing, the system responds "405 Method Not Allowed" for PUT requests.

resDelete :: !(Maybe (Resource ()))

A Resource to be run when a DELETE request comes for the resource path. If resDelete is Nothing, the system responds "405 Method Not Allowed" for DELETE requests.

data ResTree Source

ResTree is an opaque structure which is a map from resource path to ResourceDef.

mkResTree :: [([String], ResourceDef)] -> ResTreeSource

mkResTree converts a list of (path, def) to a ResTree e.g.

   mkResTree [ ([]        , Network.HTTP.Lucu.StaticFile.staticFile "/usr/include/stdio.h" ) -- /
             , (["unistd"], Network.HTTP.Lucu.StaticFile.staticFile "/usr/include/unistd.h") -- /unistd

Resource Monad

Things to be used in the Resource monad

Status Code


abort :: MonadIO m => StatusCode -> [(String, String)] -> Maybe String -> m aSource

Computation of abort status headers msg aborts the Network.HTTP.Lucu.Resource.Resource monad with given status, additional response headers, and optional message string.

What this really does is to just throw a special DynException. The exception will be caught by the Lucu.

  1. If the Network.HTTP.Lucu.Resource.Resource is in the /Deciding Header/ or any precedent states, it is possible to use the status and such like as a HTTP response to be sent to the client.
  2. Otherwise the HTTP response can't be modified anymore so the only possible thing the system can do is to dump it to the stderr. See cnfDumpTooLateAbortionToStderr.

Note that the status code doesn't have to be an error code so you can use this action for redirection as well as error reporting e.g.

 abort MovedPermanently
       [("Location", "")]
       (Just "It has been moved to")

abortPurely :: StatusCode -> [(String, String)] -> Maybe String -> aSource

This is similar to abort but computes it with unsafePerformIO.

abortA :: ArrowIO a => a (StatusCode, ([(String, String)], Maybe String)) cSource

Computation of abortA -< (status, (headers, msg)) just computes abort in an ArrowIO.


data ETag Source

An entity tag is made of a weakness flag and a opaque string.




etagIsWeak :: !Bool

The weakness flag. Weak tags looks like W/"blahblah" and strong tags are like "blahblah".

etagToken :: !String

An opaque string. Only characters from 0x20 (sp) to 0x7e (~) are allowed.


strongETag :: String -> ETagSource

This is equivalent to ETag False. If you want to generate an ETag from a file, try using Network.HTTP.Lucu.StaticFile.generateETagFromFile.

weakETag :: String -> ETagSource

This is equivalent to ETag True.


data MIMEType Source

MIMEType "major" "minor" [("name", "value")] represents "major/minor; name=value".




mtMajor :: !String
mtMinor :: !String
mtParams :: ![(String, String)]


data AuthChallenge Source

Authorization challenge to be sent to client with "WWW-Authenticate" header. See Network.HTTP.Lucu.Resource.setWWWAuthenticate.

data AuthCredential Source

Authorization credential to be sent by client with "Authorization" header. See Network.HTTP.Lucu.Resource.getAuthorization.


Static file handling