]> gitweb @ CieloNegro.org - Lucu.git/blobdiff - Network/HTTP/Lucu/Resource/Tree.hs
Doc fix, optimization, and more.
[Lucu.git] / Network / HTTP / Lucu / Resource / Tree.hs
index 4ed161fafc39c41ed0ed324100042430b674f8a2..149fa9d92d3b5101a27f5ba6c334133a3921fe34 100644 (file)
@@ -14,7 +14,7 @@ module Network.HTTP.Lucu.Resource.Tree
 import           Control.Concurrent
 import           Control.Concurrent.STM
 import           Control.Exception
-import           Control.Monad.Reader
+import           Control.Monad
 import           Data.Dynamic
 import           Data.List
 import qualified Data.Map as M
@@ -22,7 +22,6 @@ import           Data.Map (Map)
 import           Data.Maybe
 import           Network.HTTP.Lucu.Abortion
 import           Network.HTTP.Lucu.Config
-import           Network.HTTP.Lucu.MIMEType
 import           Network.HTTP.Lucu.Request
 import           Network.HTTP.Lucu.Resource
 import           Network.HTTP.Lucu.Response
@@ -42,10 +41,12 @@ import           Prelude hiding (catch)
 -- | 'ResourceDef' is basically a set of
 -- 'Network.HTTP.Lucu.Resource.Resource' monads for each HTTP methods.
 data ResourceDef = ResourceDef {
-    -- | Whether to run a 'Network.HTTP.Lucu.Resource.Resource' on a
-    -- native thread (spawned using @forkOS@) or to run it on a user
-    -- thread (spanwed using @forkIO@). Generally you don't
-      resUsesNativeThread :: Bool
+    -- |Whether to run a 'Network.HTTP.Lucu.Resource.Resource' on a
+    -- native thread (spawned by 'Control.Concurrent.forkOS') or to
+    -- run it on a user thread (spanwed by
+    -- 'Control.Concurrent.forkIO'). Generally you don't need to set
+    -- this field to 'Prelude.True'.
+      resUsesNativeThread :: !Bool
     -- | Whether to be greedy or not.
     -- 
     -- Say a client is trying to access \/aaa\/bbb\/ccc. If there is a
@@ -53,7 +54,7 @@ data ResourceDef = ResourceDef {
     -- there is another resource at \/aaa\/bbb\/ccc. If the resource
     -- at \/aaa\/bbb is not greedy, it is just ignored. Greedy
     -- resource is like a CGI script.
-    , resIsGreedy         :: Bool
+    , resIsGreedy         :: !Bool
     -- | A 'Network.HTTP.Lucu.Resource.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
@@ -62,35 +63,35 @@ data ResourceDef = ResourceDef {
     -- It also runs for HEAD request if the 'resHead' is Nothing. In
     -- this case 'Network.HTTP.Lucu.Resource.output' and such like
     -- don't actually write a response body.
-    , resGet              :: Maybe (Resource ())
+    , resGet              :: !(Maybe (Resource ()))
     -- | A 'Network.HTTP.Lucu.Resource.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.
-    , resHead             :: Maybe (Resource ())
+    , resHead             :: !(Maybe (Resource ()))
     -- | A 'Network.HTTP.Lucu.Resource.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.
-    , resPost             :: Maybe (Resource ())
+    , resPost             :: !(Maybe (Resource ()))
     -- | A 'Network.HTTP.Lucu.Resource.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.
-    , resPut              :: Maybe (Resource ())
+    , resPut              :: !(Maybe (Resource ()))
     -- | A 'Network.HTTP.Lucu.Resource.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.
-    , resDelete           :: Maybe (Resource ())
+    , resDelete           :: !(Maybe (Resource ()))
     }
 
 -- | 'ResTree' is an opaque structure which is a map from resource
 -- path to 'ResourceDef'.
 type ResTree    = ResNode -- root だから Map ではない
 type ResSubtree = Map String ResNode
-data ResNode    = ResNode (Maybe ResourceDef) ResSubtree
+data ResNode    = ResNode !(Maybe ResourceDef) !ResSubtree
 
 -- | 'mkResTree' converts a list of @(path, def)@ to a 'ResTree' e.g.
 --
@@ -100,7 +101,7 @@ data ResNode    = ResNode (Maybe ResourceDef) ResSubtree
 --             ]
 -- @
 mkResTree :: [ ([String], ResourceDef) ] -> ResTree
-mkResTree list = processRoot list
+mkResTree list = list `seq` processRoot list
     where
       processRoot :: [ ([String], ResourceDef) ] -> ResTree
       processRoot list
@@ -167,27 +168,30 @@ findResource (ResNode rootDefM subtree) uri
 
 runResource :: ResourceDef -> Interaction -> IO ThreadId
 runResource def itr
-    = fork
-      $ catch ( runReaderT ( do fromMaybe notAllowed rsrc 
-                                driftTo Done
-                           ) itr
-              )
-      $ \ exc -> processException exc
+    = def `seq` itr `seq`
+      fork
+      $! catch ( runRes ( do req <- getRequest
+                             fromMaybe notAllowed $ rsrc req
+                             driftTo Done
+                        ) itr
+               )
+             $ \ exc -> processException exc
     where
       fork :: IO () -> IO ThreadId
       fork = if (resUsesNativeThread def)
              then forkOS
              else forkIO
       
-      rsrc :: Maybe (Resource ())
-      rsrc = case reqMethod $ fromJust $ itrRequest itr of
-               GET    -> resGet def
-               HEAD   -> case resHead def of
-                           Just r  -> Just r
-                           Nothing -> resGet def
-               POST   -> resPost def
-               PUT    -> resPut def
-               DELETE -> resDelete def
+      rsrc :: Request -> Maybe (Resource ())
+      rsrc req
+          = case reqMethod req of
+              GET    -> resGet def
+              HEAD   -> case resHead def of
+                          Just r  -> Just r
+                          Nothing -> resGet def
+              POST   -> resPost def
+              PUT    -> resPut def
+              DELETE -> resDelete def
 
       notAllowed :: Resource ()
       notAllowed = do setStatus MethodNotAllowed
@@ -218,14 +222,14 @@ runResource def itr
                                                          $ Just $ show exc
                            _                 -> Abortion InternalServerError [] $ Just $ show exc
                    conf = itrConfig itr
-                   reqM = itrRequest itr
                -- まだ DecidingHeader 以前の状態だったら、この途中終了
                -- を應答に反映させる餘地がある。さうでなければ stderr
                -- にでも吐くしか無い。
                state <- atomically $ readItr itr itrState id
+               reqM  <- atomically $ readItr itr itrRequest id
                res   <- atomically $ readItr itr itrResponse id
                if state <= DecidingHeader then
-                   flip runReaderT itr
+                   flip runRes itr
                       $ do setStatus $ aboStatus abo
                            -- FIXME: 同じ名前で複數の値があった時は、こ
                            -- れではまずいと思ふ。
@@ -235,7 +239,7 @@ runResource def itr
                    when (cnfDumpTooLateAbortionToStderr $ itrConfig itr)
                             $ hPutStrLn stderr $ show abo
 
-               flip runReaderT itr $ driftTo Done
+               flip runRes itr $ driftTo Done
 
       formatIOE :: IOError -> String
       formatIOE ioE = if isUserError ioE then