packages feed

Lucu 0.4 → 0.4.1

raw patch · 23 files changed

+282/−229 lines, 23 filesdep +filepath

Dependencies added: filepath

Files

ImplantFile.hs view
@@ -78,11 +78,11 @@ main = withOpenSSL $        do (opts, sources, errors) <- return . getOpt Permute options =<< getArgs -          when (not $ null errors)+          unless (null errors)                    $ do mapM_ putStr errors                         exitWith $ ExitFailure 1 -          when (any (\ x -> x == OptHelp) opts)+          when (any (== OptHelp) opts)                    $ do printUsage                         exitWith ExitSuccess @@ -106,7 +106,7 @@          output   <- openOutput opts          eTag     <- getETag opts input -         let compParams  = defaultCompressParams { compressLevel = BestCompression }+         let compParams  = defaultCompressParams { compressLevel = bestCompression }              gzippedData = compressWith compParams input              originalLen = L.length input              gzippedLen  = L.length gzippedData@@ -371,12 +371,12 @@                                             _            -> False) opts           -- モジュール名をピリオドで分割した時の最後の項目の先頭文字を           -- 小文字にしたものを使ふ。-          defaultSymName = mkDefault modName-          mkDefault      = headToLower . getLastComp-          headToLower    = \ str -> case str of-                                      []     -> error "module name must not be empty"-                                      (x:xs) -> toLower x : xs-          getLastComp    = reverse . fst . break (== '.') . reverse+          defaultSymName  = mkDefault modName+          mkDefault       = headToLower . getLastComp+          headToLower str = case str of+                              []     -> error "module name must not be empty"+                              (x:xs) -> toLower x : xs+          getLastComp     = reverse . fst . break (== '.') . reverse       in         case symNameOpts of           []                      -> return defaultSymName@@ -400,8 +400,8 @@  getLastModified :: FilePath -> IO UTCTime getLastModified "-"   = getCurrentTime-getLastModified fpath = getFileStatus fpath-                        >>= return . posixSecondsToUTCTime . fromRational . toRational . modificationTime+getLastModified fpath = fmap (posixSecondsToUTCTime . fromRational . toRational . modificationTime)+                        $ getFileStatus fpath   getETag :: [CmdOpt] -> Lazy.ByteString -> IO String@@ -411,25 +411,26 @@                                       _         -> False) opts       in         case eTagOpts of-          []               -> getDigestByName "SHA1" >>= return . mkETagFromInput . fromJust+          []               -> fmap (mkETagFromInput . fromJust) (getDigestByName "SHA1")           (OptETag str):[] -> return str           _                -> error "too many --etag options."     where       mkETagFromInput :: Digest -> String-      mkETagFromInput sha1 = "SHA-1:" ++ (toHex $ digestLBS sha1 input)+      mkETagFromInput sha1 = "SHA-1:" ++ toHex (digestLBS sha1 input) -      toHex :: [Char] -> String-      toHex []     = ""-      toHex (x:xs) = hexByte (fromEnum x) ++ toHex xs+      toHex :: String -> String+      toHex = foldr ((++) . hexByte . fromEnum) ""        hexByte :: Int -> String       hexByte n-          = hex4bit ((n `shiftR` 4) .&. 0x0F) : hex4bit (n .&. 0x0F) : []+          = [ hex4bit ((n `shiftR` 4) .&. 0x0F)+            , hex4bit ( n             .&. 0x0F)+            ]        hex4bit :: Int -> Char       hex4bit n-          | n < 10    = (chr $ ord '0' + n     )-          | n < 16    = (chr $ ord 'a' + n - 10)+          | n < 10    = chr $ ord '0' + n+          | n < 16    = chr $ ord 'a' + n - 10           | otherwise = undefined  
Lucu.cabal view
@@ -8,7 +8,7 @@         messing around FastCGI. It is also intended to be run behind a         reverse-proxy so it doesn't have some facilities like logging,         client filtering or such like.-Version: 0.4+Version: 0.4.1 License: PublicDomain License-File: COPYING Author: PHO <pho at cielonegro dot org>@@ -44,8 +44,9 @@ Library     Build-Depends:         HsOpenSSL, base >= 4 && < 5, bytestring, containers, dataenc,-        directory, haskell-src, hxt, mtl, network, stm, time, unix,-        zlib+        filepath, directory, haskell-src, hxt, mtl, network, stm,+        time, unix, zlib+     Exposed-Modules:         Network.HTTP.Lucu         Network.HTTP.Lucu.Abortion@@ -66,6 +67,7 @@         Network.HTTP.Lucu.Response         Network.HTTP.Lucu.StaticFile         Network.HTTP.Lucu.Utils+     Other-Modules:         Network.HTTP.Lucu.Chunk         Network.HTTP.Lucu.ContentCoding@@ -79,8 +81,10 @@         Network.HTTP.Lucu.Preprocess         Network.HTTP.Lucu.RequestReader         Network.HTTP.Lucu.ResponseWriter+     Extensions:         BangPatterns, DeriveDataTypeable, ScopedTypeVariables, UnboxedTuples+     ghc-options:         -Wall         -funbox-strict-fields@@ -90,9 +94,12 @@         Buildable: True     else         Buildable: False+     Main-Is: ImplantFile.hs+     Extensions:         BangPatterns, ScopedTypeVariables, UnboxedTuples+     ghc-options:         -Wall         -funbox-strict-fields
NEWS view
@@ -1,3 +1,24 @@+Changes from 0.4 to 0.4.1+-------------------------+* Network.HTTP.Lucu.Resource: (Thanks: Voker57)++    - getPathInfo now un-escapes the resulting path info. This may+      break backward compatibility in very confusing way, if your code+      relies on the previous implementation. Sorry for any+      inconvenience.++* Network.HTTP.Lucu.Resource.Tree: (Thanks: Voker57)++    - Fix: mkResTree wasn't working correctly for a resource path+           [""], which should be treated as same as [] the root.++    - Fix: Greedy resources on the root of resource tree wasn't really+           greedy.++* Network.HTTP.Lucu.Resource.Tree:++    - New constant: emptyResource+ Changes from 0.3.3 to 0.4 ------------------------- * Network.HTTP.Lucu.Resource: (Thanks: Voker57)
Network/HTTP/Lucu.hs view
@@ -43,6 +43,7 @@        -- * Resource Tree     , ResourceDef(..)+    , emptyResource     , ResTree     , mkResTree 
Network/HTTP/Lucu/Abortion.hs view
@@ -109,7 +109,6 @@         Nothing             -> let res'  = res { resStatus = aboStatus abo }                    res'' = foldl (.) id [setHeader name value-                                             | (name, value) <- fromHeaders $ aboHeaders abo]-                           $ res'+                                             | (name, value) <- fromHeaders $ aboHeaders abo] res'                in                  getDefaultPage conf reqM res''
Network/HTTP/Lucu/ContentCoding.hs view
@@ -7,6 +7,7 @@     where  import           Data.Char+import           Data.Ord import           Data.Maybe import           Network.HTTP.Lucu.Parser import           Network.HTTP.Lucu.Parser.Http@@ -43,4 +44,5 @@  orderAcceptEncodings :: (String, Maybe Double) -> (String, Maybe Double) -> Ordering orderAcceptEncodings (_, q1) (_, q2)-    = fromMaybe 0 q1 `compare` fromMaybe 0 q2+    = comparing (fromMaybe 0) q1 q2+
Network/HTTP/Lucu/DefaultPage.hs view
@@ -29,9 +29,8 @@   getDefaultPage :: Config -> Maybe Request -> Response -> String-getDefaultPage conf req res-    = conf `seq` req `seq` res `seq`-      let msgA = getMsg req res+getDefaultPage !conf !req !res+    = let msgA = getMsg req res       in         unsafePerformIO $         do [xmlStr] <- runX ( mkDefaultPage conf (resStatus res) msgA@@ -42,10 +41,9 @@   writeDefaultPage :: Interaction -> STM ()-writeDefaultPage itr-    = itr `seq`-      -- Content-Type が正しくなければ補完できない。-      do res <- readItr itr itrResponse id+writeDefaultPage !itr+    -- Content-Type が正しくなければ補完できない。+    = do res <- readItr itr itrResponse id          when (getHeader (C8.pack "Content-Type") res == Just defaultPageContentType)                   $ do reqM <- readItr itr itrRequest id @@ -57,9 +55,8 @@   mkDefaultPage :: (ArrowXml a) => Config -> StatusCode -> a b XmlTree -> a b XmlTree-mkDefaultPage conf status msgA-    = conf `seq` status `seq` msgA `seq`-      let (# sCode, sMsg #) = statusCode status+mkDefaultPage !conf !status !msgA+    = let (# sCode, sMsg #) = statusCode status           sig               = C8.unpack (cnfServerSoftware conf)                               ++ " at "                               ++ C8.unpack (cnfServerHost conf)@@ -85,9 +82,8 @@ {-# SPECIALIZE mkDefaultPage :: Config -> StatusCode -> IOSArrow b XmlTree -> IOSArrow b XmlTree #-}  getMsg :: (ArrowXml a) => Maybe Request -> Response -> a b XmlTree-getMsg req res-    = req `seq` res `seq`-      case resStatus res of+getMsg !req !res+    = case resStatus res of         -- 1xx は body を持たない         -- 2xx の body は補完しない 
Network/HTTP/Lucu/Format.hs view
@@ -24,7 +24,7 @@     where       fmt' :: Int -> String       fmt' m-          | m < base  = (intToChar upperCase m) : []+          | m < base  = [intToChar upperCase m]           | otherwise = (intToChar upperCase $! m `mod` base) : fmt' (m `div` base)  @@ -40,50 +40,54 @@ fmtDec2 :: Int -> String fmtDec2 n     | n < 0 || n >= 100 = fmtInt 10 undefined 2 '0' False n -- fallback-    | n < 10            =   '0'-                          : intToChar undefined n-                          : []-    | otherwise         =   intToChar undefined (n `div` 10)-                          : intToChar undefined (n `mod` 10)-                          : []+    | n < 10            = [ '0'+                          , intToChar undefined n+                          ]+    | otherwise         = [ intToChar undefined (n `div` 10)+                          , intToChar undefined (n `mod` 10)+                          ]   fmtDec3 :: Int -> String fmtDec3 n     | n < 0 || n >= 1000 = fmtInt 10 undefined 3 '0' False n -- fallback-    | n < 10             = '0' : '0'-                           : intToChar undefined n-                           : []-    | n < 100            = '0'-                           : intToChar undefined ((n `div` 10) `mod` 10)-                           : intToChar undefined ( n           `mod` 10)-                           : []-    | otherwise          =   intToChar undefined ((n `div` 100) `mod` 10)-                           : intToChar undefined ((n `div`  10) `mod` 10)-                           : intToChar undefined ( n            `mod` 10)-                           : []+    | n < 10             = [ '0'+                           , '0'+                           , intToChar undefined n+                           ]+    | n < 100            = [ '0'+                           , intToChar undefined ((n `div` 10) `mod` 10)+                           , intToChar undefined ( n           `mod` 10)+                           ]+    | otherwise          = [ intToChar undefined ((n `div` 100) `mod` 10)+                           , intToChar undefined ((n `div`  10) `mod` 10)+                           , intToChar undefined ( n            `mod` 10)+                           ]   fmtDec4 :: Int -> String fmtDec4 n     | n < 0 || n >= 10000 = fmtInt 10 undefined 4 '0' False n -- fallback-    | n < 10              =   '0' : '0' : '0'-                            : intToChar undefined n-                            : []-    | n < 100             =   '0' : '0'-                            : intToChar undefined ((n `div` 10) `mod` 10)-                            : intToChar undefined ( n           `mod` 10)-                            : []-    | n < 1000            =   '0'-                            : intToChar undefined ((n `div` 100) `mod` 10)-                            : intToChar undefined ((n `div`  10) `mod` 10)-                            : intToChar undefined ( n            `mod` 10)-                            : []-    | otherwise           =   intToChar undefined ((n `div` 1000) `mod` 10)-                            : intToChar undefined ((n `div`  100) `mod` 10)-                            : intToChar undefined ((n `div`   10) `mod` 10)-                            : intToChar undefined ( n             `mod` 10)-                            : []+    | n < 10              = [ '0'+                            , '0'+                            , '0'+                            , intToChar undefined n+                            ]+    | n < 100             = [ '0'+                            , '0'+                            , intToChar undefined ((n `div` 10) `mod` 10)+                            , intToChar undefined ( n           `mod` 10)+                            ]+    | n < 1000            = [ '0'+                            , intToChar undefined ((n `div` 100) `mod` 10)+                            , intToChar undefined ((n `div`  10) `mod` 10)+                            , intToChar undefined ( n            `mod` 10)+                            ]+    | otherwise           = [ intToChar undefined ((n `div` 1000) `mod` 10)+                            , intToChar undefined ((n `div`  100) `mod` 10)+                            , intToChar undefined ((n `div`   10) `mod` 10)+                            , intToChar undefined ( n             `mod` 10)+                            ]   fmtHex :: Bool -> Int -> Int -> String
Network/HTTP/Lucu/Headers.hs view
@@ -21,6 +21,7 @@ import           Data.List import           Data.Map (Map) import qualified Data.Map as M+import           Data.Ord import           Data.Word import           Foreign.ForeignPtr import           Foreign.Ptr@@ -76,7 +77,7 @@     | otherwise         = do c1 <- peek p1              c2 <- peek p2-             case toLower (w2c c1) `compare` toLower (w2c c2) of+             case comparing (toLower . w2c) c1 c2 of                EQ -> noCaseCmp' (p1 `plusPtr` 1) (l1 - 1) (p2 `plusPtr` 1) (l2 - 1)                x  -> return x @@ -194,7 +195,7 @@       normalize :: String -> String       normalize = trimBody . trim isWhiteSpace -      trimBody = foldr (++) []+      trimBody = concat                  . map (\ s -> if head s == ' ' then                                    " "                                else
Network/HTTP/Lucu/Interaction.hs view
@@ -84,8 +84,8 @@  newInteraction :: Config -> SockAddr -> Maybe X509 -> Maybe Request -> IO Interaction newInteraction !conf !addr !cert !req-    = do request  <- newTVarIO $ req-         responce <- newTVarIO $ Response {+    = do request  <- newTVarIO req+         responce <- newTVarIO Response {                        resVersion = HttpVersion 1 1                      , resStatus  = Ok                      , resHeaders = toHeaders [(C8.pack "Content-Type", defaultPageContentType)]@@ -115,7 +115,7 @@          wroteContinue <- newTVarIO False          wroteHeader   <- newTVarIO False -         return $ Interaction {+         return Interaction {                       itrConfig       = conf                     , itrRemoteAddr   = addr                     , itrRemoteCert   = cert@@ -150,33 +150,28 @@   writeItr :: Interaction -> (Interaction -> TVar a) -> a -> STM ()-writeItr itr accessor value-    = itr `seq` accessor `seq` value `seq`-      writeTVar (accessor itr) value+writeItr !itr !accessor !value+    = writeTVar (accessor itr) value   readItr :: Interaction -> (Interaction -> TVar a) -> (a -> b) -> STM b-readItr itr accessor reader-    = itr `seq` accessor `seq` reader `seq`-      readTVar (accessor itr) >>= return . reader+readItr !itr !accessor !reader+    = fmap reader $ readTVar (accessor itr)   readItrF :: Functor f => Interaction -> (Interaction -> TVar (f a)) -> (a -> b) -> STM (f b)-readItrF itr accessor reader-    = itr `seq` accessor `seq` reader `seq`-      readItr itr accessor (fmap reader)+readItrF !itr !accessor !reader+    = readItr itr accessor (fmap reader) {-# SPECIALIZE readItrF :: Interaction -> (Interaction -> TVar (Maybe a)) -> (a -> b) -> STM (Maybe b) #-}   updateItr :: Interaction -> (Interaction -> TVar a) -> (a -> a) -> STM ()-updateItr itr accessor updator-    = itr `seq` accessor `seq` updator `seq`-      do old <- readItr itr accessor id+updateItr !itr !accessor !updator+    = do old <- readItr itr accessor id          writeItr itr accessor (updator old)   updateItrF :: Functor f => Interaction -> (Interaction -> TVar (f a)) -> (a -> a) -> STM ()-updateItrF itr accessor updator-    = itr `seq` accessor `seq` updator `seq`-      updateItr itr accessor (fmap updator)+updateItrF !itr !accessor !updator+    = updateItr itr accessor (fmap updator) {-# SPECIALIZE updateItrF :: Interaction -> (Interaction -> TVar (Maybe a)) -> (a -> a) -> STM () #-}
Network/HTTP/Lucu/Parser.hs view
@@ -93,6 +93,9 @@     return !x = Parser $! return $! Success x     fail _    = Parser $! return $! IllegalInput +instance Functor Parser where+    fmap f p = p >>= return . f+ -- |@'failP'@ is just a synonym for @'Prelude.fail' -- 'Prelude.undefined'@. failP :: Parser a
Network/HTTP/Lucu/Parser/Http.hs view
@@ -87,7 +87,7 @@  -- |'text' accepts one character which doesn't satisfy 'isCtl'. text :: Parser Char-text = satisfy (\ c -> not (isCtl c))+text = satisfy (not . isCtl)  -- |'separator' accepts one character which satisfies 'isSeparator'. separator :: Parser Char
Network/HTTP/Lucu/Postprocess.hs view
@@ -56,13 +56,12 @@ -}  postprocess :: Interaction -> STM ()-postprocess itr-    = itr `seq`-      do reqM <- readItr itr itrRequest id+postprocess !itr+    = do reqM <- readItr itr itrRequest id          res  <- readItr itr itrResponse id          let sc = resStatus res -         when (not $ any (\ p -> p sc) [isSuccessful, isRedirection, isError])+         unless (any (\ p -> p sc) [isSuccessful, isRedirection, isError])                   $ abortSTM InternalServerError []                         $ Just ("The status code is not good for a final status: "                                 ++ show sc)@@ -119,10 +118,8 @@                conn <- readHeader (C8.pack "Connection")                case conn of                  Nothing    -> return ()-                 Just value -> if value `noCaseEq` C8.pack "close" then-                                   writeItr itr itrWillClose True-                               else-                                   return ()+                 Just value -> when (value `noCaseEq` C8.pack "close")+                                   $ writeItr itr itrWillClose True                 willClose <- readItr itr itrWillClose id                when willClose@@ -132,20 +129,17 @@                         $ writeTVar (itrWillDiscardBody itr) True        readHeader :: Strict.ByteString -> STM (Maybe Strict.ByteString)-      readHeader name-          = name `seq`-            readItr itr itrResponse $ getHeader name+      readHeader !name+          = readItr itr itrResponse $ getHeader name        updateRes :: (Response -> Response) -> STM ()-      updateRes updator -          = updator `seq`-            updateItr itr itrResponse updator+      updateRes !updator +          = updateItr itr itrResponse updator   completeUnconditionalHeaders :: Config -> Response -> IO Response-completeUnconditionalHeaders conf res-    = conf `seq` res `seq`-      return res >>= compServer >>= compDate >>= return+completeUnconditionalHeaders !conf !res+    = compServer res >>= compDate       where         compServer res'             = case getHeader (C8.pack "Server") res' of@@ -177,7 +171,6 @@     where       mostlyEq :: UTCTime -> UTCTime -> Bool       mostlyEq a b-          = if utctDay a == utctDay b then-                fromEnum (utctDayTime a) == fromEnum (utctDayTime b)-            else-                False+          = (utctDay a == utctDay b)+            &&+            (fromEnum (utctDayTime a) == fromEnum (utctDayTime b))
Network/HTTP/Lucu/Preprocess.hs view
@@ -100,7 +100,7 @@                          portStr                               = case port of                                   Just 80 -> Just ""-                                  Just n  -> Just $ ":" ++ show n+                                  Just n  -> Just $ ':' : show n                                   Nothing -> Nothing                      case portStr of                        Just str -> updateAuthority host (C8.pack str)@@ -110,10 +110,10 @@                        -- いと思ふ。stderr?                        Nothing  -> setStatus InternalServerError               else-                  do case getHeader (C8.pack "Host") req of-                       Just str -> let (host, portStr) = parseHost str-                                   in updateAuthority host portStr-                       Nothing  -> setStatus BadRequest+                  case getHeader (C8.pack "Host") req of+                    Just str -> let (host, portStr) = parseHost str+                                in updateAuthority host portStr+                    Nothing  -> setStatus BadRequest         parseHost :: Strict.ByteString -> (Strict.ByteString, Strict.ByteString)@@ -148,13 +148,11 @@                 case getHeader (C8.pack "Transfer-Encoding") req of                  Nothing    -> return ()-                 Just value -> if value `noCaseEq` C8.pack "identity" then-                                   return ()-                               else-                                   if value `noCaseEq` C8.pack "chunked" then-                                       writeItr itr itrRequestIsChunked True-                                   else-                                       setStatus NotImplemented+                 Just value -> unless (value `noCaseEq` C8.pack "identity")+                                   $ if value `noCaseEq` C8.pack "chunked" then+                                         writeItr itr itrRequestIsChunked True+                                     else+                                         setStatus NotImplemented                 case getHeader (C8.pack "Content-Length") req of                  Nothing    -> return ()@@ -167,7 +165,5 @@                 case getHeader (C8.pack "Connection") req of                  Nothing    -> return ()-                 Just value -> if value `noCaseEq` C8.pack "close" then-                                   writeItr itr itrWillClose True-                               else-                                   return ()+                 Just value -> when (value `noCaseEq` C8.pack "close")+                                   $ writeItr itr itrWillClose True
Network/HTTP/Lucu/Request.hs view
@@ -67,19 +67,19 @@   methodP :: Parser Method-methodP = (let methods = [ ("OPTIONS", OPTIONS)-                         , ("GET"    , GET    )-                         , ("HEAD"   , HEAD   )-                         , ("POST"   , POST   )-                         , ("PUT"    , PUT    )-                         , ("DELETE" , DELETE )-                         , ("TRACE"  , TRACE  )-                         , ("CONNECT", CONNECT)-                         ]-           in foldl (<|>) failP $ map (\ (str, mth)-                                           -> string str >> return mth) methods)+methodP = ( let methods = [ ("OPTIONS", OPTIONS)+                          , ("GET"    , GET    )+                          , ("HEAD"   , HEAD   )+                          , ("POST"   , POST   )+                          , ("PUT"    , PUT    )+                          , ("DELETE" , DELETE )+                          , ("TRACE"  , TRACE  )+                          , ("CONNECT", CONNECT)+                          ]+            in choice $ map (\ (str, mth)+                                 -> string str >> return mth) methods )           <|>-          token >>= return . ExtensionMethod+          fmap ExtensionMethod token   uriP :: Parser URI
Network/HTTP/Lucu/Resource.hs view
@@ -60,7 +60,7 @@  module Network.HTTP.Lucu.Resource     (-    -- * Monad+    -- * Types       Resource     , FormData(..)     , runRes -- private@@ -285,14 +285,15 @@                      return $! fromJust $! itrResourcePath itr  --- |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'.+-- |This is an analogy of CGI PATH_INFO. The result is+-- URI-unescaped. It is always @[]@ if the+-- 'Network.HTTP.Lucu.Resource.Tree.ResourceDef' is not greedy. See+-- 'getResourcePath'. getPathInfo :: Resource [String] getPathInfo = do rsrcPath <- getResourcePath                  uri      <- getRequestURI                  let reqPathStr = uriPath uri-                     reqPath    = [x | x <- splitBy (== '/') reqPathStr, x /= ""]+                     reqPath    = [unEscapeString x | x <- splitBy (== '/') reqPathStr, x /= ""]                  -- rsrcPath と reqPath の共通する先頭部分を reqPath か                  -- ら全部取り除くと、それは PATH_INFO のやうなものにな                  -- る。rsrcPath は全部一致してゐるに決まってゐる(でな
Network/HTTP/Lucu/Resource/Tree.hs view
@@ -3,6 +3,8 @@ -- | Repository of the resources in httpd. module Network.HTTP.Lucu.Resource.Tree     ( ResourceDef(..)+    , emptyResource+     , ResTree     , FallbackHandler @@ -13,6 +15,7 @@     )     where +import           Control.Arrow import           Control.Concurrent import           Control.Concurrent.STM import           Control.Exception@@ -98,11 +101,37 @@     , resDelete           :: !(Maybe (Resource ()))     } +-- |'emptyResource' is a resource definition with no actual+-- handlers. You can construct a 'ResourceDef' by selectively+-- overriding 'emptyResource'. It is defined as follows:+--+-- @+--   emptyResource = ResourceDef {+--                     resUsesNativeThread = False+--                   , resIsGreedy         = False+--                   , resGet              = Nothing+--                   , resHead             = Nothing+--                   , resPost             = Nothing+--                   , resPut              = Nothing+--                   , resDelete           = Nothing+--                   }+-- @+emptyResource :: ResourceDef+emptyResource = ResourceDef {+                  resUsesNativeThread = False+                , resIsGreedy         = False+                , resGet              = Nothing+                , resHead             = Nothing+                , resPost             = Nothing+                , resPut              = Nothing+                , resDelete           = Nothing+                }+ -- |'ResTree' is an opaque structure which is a map from resource path -- to 'ResourceDef'. newtype ResTree = 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. --@@ -112,18 +141,22 @@ --             ] -- @ mkResTree :: [ ([String], ResourceDef) ] -> ResTree-mkResTree xs = xs `seq` processRoot xs+mkResTree = processRoot . map (first canonicalisePath)     where+      canonicalisePath :: [String] -> [String]+      canonicalisePath = filter (/= "")+       processRoot :: [ ([String], ResourceDef) ] -> ResTree       processRoot list           = let (roots, nonRoots) = partition (\ (path, _) -> path == []) list                 children = processNonRoot nonRoots             in               if null roots then-                  -- "/" にリソースが定義されない。"/foo" とかにはあるかも。+                  -- The root has no resources. Maybe there's one at+                  -- somewhere like "/foo".                   ResTree (ResNode Nothing children)               else-                  -- "/" がある。+                  -- There is a root resource.                   let (_, def) = last roots                   in                      ResTree (ResNode (Just def) children)@@ -136,27 +169,31 @@                 node name  = let defs = [def | (path, def) <- list, path == [name]]                              in                                if null defs then-                                   -- この位置にリソースが定義されない。-                                   -- もっと下にはあるかも。+                                   -- No resources are defined+                                   -- here. Maybe there's one at+                                   -- somewhere below this node.                                    ResNode Nothing children                                else-                                   -- この位置にリソースがある。+                                   -- There is a resource here.                                    ResNode (Just $ last defs) children                 children   = processNonRoot [(path, def)-                                                 | (_:path, def) <- list, not (null path)]+                                                 | (_:path, def) <- list]             in               subtree   findResource :: ResTree -> [FallbackHandler] -> URI -> IO (Maybe ([String], ResourceDef)) findResource (ResTree (ResNode rootDefM subtree)) fbs uri-    = do let pathStr     = uriPath uri-             path        = [x | x <- splitBy (== '/') pathStr, x /= ""]-             foundInTree = if null path then-                               do def <- rootDefM-                                  return (path, def)-                           else-                               walkTree subtree path []+    = do let pathStr        = uriPath uri+             path           = [unEscapeString x | x <- splitBy (== '/') pathStr, x /= ""]+             haveGreedyRoot = case rootDefM of+                                Just def -> resIsGreedy def+                                Nothing  -> False+             foundInTree    = if haveGreedyRoot || null path then+                                  do def <- rootDefM+                                     return ([], def)+                              else+                                  walkTree subtree path []          if isJust foundInTree then              return foundInTree            else@@ -199,10 +236,10 @@                              driftTo Done                         ) itr                )-             $ \ exc -> processException exc+               processException     where       fork :: IO () -> IO ThreadId-      fork = if (resUsesNativeThread def)+      fork = if resUsesNativeThread def              then forkOS              else forkIO       @@ -223,12 +260,12 @@                       setHeader (C8.pack "Allow") (C8.pack $ joinWith ", " allowedMethods)        allowedMethods :: [String]-      allowedMethods = nub $ foldr (++) [] [ methods resGet    ["GET"]-                                           , methods resHead   ["GET", "HEAD"]-                                           , methods resPost   ["POST"]-                                           , methods resPut    ["PUT"]-                                           , methods resDelete ["DELETE"]-                                           ]+      allowedMethods = nub $ concat [ methods resGet    ["GET"]+                                    , methods resHead   ["GET", "HEAD"]+                                    , methods resPost   ["POST"]+                                    , methods resPut    ["PUT"]+                                    , methods resDelete ["DELETE"]+                                    ]        methods :: (ResourceDef -> Maybe a) -> [String] -> [String]       methods f xs = case f def of@@ -253,7 +290,7 @@                if state <= DecidingHeader then                    flip runRes itr                       $ do setStatus $ aboStatus abo-                           mapM_ (\ (name, value) -> setHeader name value) $ fromHeaders $ aboHeaders abo+                           mapM_ (uncurry setHeader) $ fromHeaders $ aboHeaders abo                            output $ abortPage conf reqM res abo                  else                    when (cnfDumpTooLateAbortionToStderr $ itrConfig itr)
Network/HTTP/Lucu/ResponseWriter.hs view
@@ -35,30 +35,28 @@       awaitSomethingToWrite :: IO ()       awaitSomethingToWrite            = {-# SCC "awaitSomethingToWrite" #-}-            do action-                   <- atomically $!-                      -- キューが空でなくなるまで待つ-                      do queue <- readTVar tQueue-                         -- GettingBody 状態にあり、Continue が期待され-                         -- てゐて、それがまだ送信前なのであれば、-                         -- Continue を送信する。-                         case S.viewr queue of-                           EmptyR   -> retry-                           _ :> itr -> do state <- readItr itr itrState id+            join $!+                 atomically $!+                 -- キューが空でなくなるまで待つ+                 do queue <- readTVar tQueue+                    -- GettingBody 状態にあり、Continue が期待されてゐ+                    -- て、それがまだ送信前なのであれば、Continue を送+                    -- 信する。+                    case S.viewr queue of+                      EmptyR   -> retry+                      _ :> itr -> do state <- readItr itr itrState id -                                          if state == GettingBody then-                                              writeContinueIfNecessary itr-                                            else-                                              if state >= DecidingBody then-                                                  writeHeaderOrBodyIfNecessary itr-                                              else-                                                  retry-               action+                                     if state == GettingBody then+                                         writeContinueIfNecessary itr+                                       else+                                         if state >= DecidingBody then+                                             writeHeaderOrBodyIfNecessary itr+                                         else+                                             retry        writeContinueIfNecessary :: Interaction -> STM (IO ())-      writeContinueIfNecessary itr+      writeContinueIfNecessary !itr           = {-# SCC "writeContinueIfNecessary" #-}-            itr `seq`             do expectedContinue <- readItr itr itrExpectedContinue id                if expectedContinue then                    do wroteContinue <- readItr itr itrWroteContinue id@@ -75,13 +73,12 @@                    retry        writeHeaderOrBodyIfNecessary :: Interaction -> STM (IO ())-      writeHeaderOrBodyIfNecessary itr+      writeHeaderOrBodyIfNecessary !itr           -- DecidingBody 以降の状態にあり、まだヘッダを出力する前であ           -- れば、ヘッダを出力する。ヘッダ出力後であり、bodyToSend が           -- 空でなければ、それを出力する。空である時は、もし状態が           -- Done であれば後処理をする。           = {-# SCC "writeHeaderOrBodyIfNecessary" #-}-            itr `seq`             do wroteHeader <- readItr itr itrWroteHeader id                                if not wroteHeader then@@ -100,9 +97,8 @@                           return $! writeBodyChunk itr        writeContinue :: Interaction -> IO ()-      writeContinue itr+      writeContinue !itr           = {-# SCC "writeContinue" #-}-            itr `seq`             do let cont = Response {                             resVersion = HttpVersion 1 1                           , resStatus  = Continue@@ -115,9 +111,8 @@                awaitSomethingToWrite        writeHeader :: Interaction -> IO ()-      writeHeader itr+      writeHeader !itr           = {-# SCC "writeHeader" #-}-            itr `seq`             do res <- atomically $! do writeItr itr itrWroteHeader True                                        readItr itr itrResponse id                hPutResponse h res@@ -125,9 +120,8 @@                awaitSomethingToWrite              writeBodyChunk :: Interaction -> IO ()-      writeBodyChunk itr+      writeBodyChunk !itr           = {-# SCC "writeBodyChunk" #-}-            itr `seq`             do willDiscardBody <- atomically $! readItr itr itrWillDiscardBody id                willChunkBody   <- atomically $! readItr itr itrWillChunkBody   id                chunk           <- atomically $! do chunk <- readItr itr itrBodyToSend id@@ -145,18 +139,16 @@                awaitSomethingToWrite        finishBodyChunk :: Interaction -> IO ()-      finishBodyChunk itr+      finishBodyChunk !itr           = {-# SCC "finishBodyChunk" #-}-            itr `seq`             do willDiscardBody <- atomically $! readItr itr itrWillDiscardBody id                willChunkBody   <- atomically $! readItr itr itrWillChunkBody   id                when (not willDiscardBody && willChunkBody)                         $ hPutLBS h (C8.pack "0\r\n\r\n") >> hFlush h        finalize :: Interaction -> IO ()-      finalize itr+      finalize !itr           = {-# SCC "finalize" #-}-            itr `seq`             do finishBodyChunk itr                willClose <- atomically $!                             do queue <- readTVar tQueue
Network/HTTP/Lucu/StaticFile.hs view
@@ -23,6 +23,7 @@ import           Network.HTTP.Lucu.Resource.Tree import           Network.HTTP.Lucu.Response import           Network.HTTP.Lucu.Utils+import           System.FilePath.Posix import           System.Posix.Files  @@ -64,7 +65,7 @@                            $ abort Forbidden [] Nothing                        -- 讀める                        tag     <- liftIO $ generateETagFromFile path-                       lastMod <- return $ posixSecondsToUTCTime $ fromRational $ toRational $ modificationTime stat+                       let lastMod = posixSecondsToUTCTime $ fromRational $ toRational $ modificationTime stat                        foundEntity tag lastMod                         -- MIME Type を推定@@ -74,7 +75,7 @@                          Just mime -> setContentType mime                         -- 實際にファイルを讀んで送る-                       (liftIO $ B.readFile path) >>= outputLBS+                       liftIO (B.readFile path) >>= outputLBS                   else                     abort Forbidden [] Nothing            else@@ -134,16 +135,15 @@ -- 'Network.HTTP.Lucu.Resource.Tree.ResTree', you had better use -- 'staticDir' instead of this. handleStaticDir :: FilePath -> Resource ()-handleStaticDir basePath-    = basePath `seq`-      do extraPath <- getPathInfo+handleStaticDir !basePath+    = do extraPath <- getPathInfo          securityCheck extraPath-         let path = basePath ++ "/" ++ joinWith "/" extraPath+         let path = basePath </> joinPath extraPath           handleStaticFile path     where       securityCheck :: Monad m => [String] -> m ()-      securityCheck pathElems-          = pathElems `seq`-            when (any (== "..") pathElems) $ fail ("security error: "+      securityCheck !pathElems+          = when (any (== "..") pathElems) $ fail ("security error: "                                                    ++ joinWith "/" pathElems)+-- TODO: implement directory listing.
Network/HTTP/Lucu/Utils.hs view
@@ -19,7 +19,7 @@ splitBy :: (a -> Bool) -> [a] -> [[a]] splitBy isSep src     = case break isSep src-      of (last , []       ) -> last  : []+      of (last , []       ) -> [last]          (first, _sep:rest) -> first : splitBy isSep rest  -- |> joinWith ":" ["ab", "c", "def"]
examples/HelloWorld.hs view
@@ -23,22 +23,17 @@  helloWorld :: ResourceDef helloWorld-    = ResourceDef {-        resUsesNativeThread = False-      , resIsGreedy         = False-      , resGet+    = emptyResource {+        resGet           = Just $ do --time <- liftIO $ getClockTime                       --foundEntity (strongETag "abcde") time                       setContentType $ read "text/hello"                       outputChunk "Hello, "                       outputChunk "World!\n"-      , resHead   = Nothing       , resPost           = Just $ do str1 <- inputChunk 3                       str2 <- inputChunk 3                       str3 <- inputChunk 3                       setContentType $ read "text/hello"                       output ("[" ++ str1 ++ " - " ++ str2 ++ "#" ++ str3 ++ "]")-      , resPut    = Nothing-      , resDelete = Nothing       }
examples/Makefile view
@@ -1,15 +1,23 @@-build: MiseRafturai.hs SmallFile.hs SSL.hs-	ghc --make HelloWorld -threaded -O3 -fwarn-unused-imports-	ghc --make Implanted -threaded -O3 -fwarn-unused-imports-	ghc --make ImplantedSmall -threaded -O3 -fwarn-unused-imports-	ghc --make Multipart -threaded -O3 -fwarn-unused-imports-	ghc --make SSL -threaded -O3 -fwarn-unused-imports+TARGETS = \+	HelloWorld \+	MiseRafturai \+	Implanted \+	ImplantedSmall \+	Multipart \+	SSL \+	StaticDir \+	$(NULL) +build: $(TARGETS)++%: %.hs+	ghc --make $@ -threaded -O3 -fwarn-unused-imports+ run: build 	./HelloWorld  clean:-	rm -f HelloWorld Implanted MiseRafturai.hs ImplantedSmall SmallFile.hs Multipart SSL *.hi *.o+	rm -f $(TARGETS) *.hi *.o  MiseRafturai.hs: mise-rafturai.html 	lucu-implant-file -m MiseRafturai -o $@ $<
examples/SSL.hs view
@@ -1,5 +1,6 @@+{-# LANGUAGE PackageImports #-} import           Control.Monad-import           Control.Monad.Trans+import "mtl"     Control.Monad.Trans import           Data.Time.Clock import           Network import           Network.HTTP.Lucu