]> gitweb @ CieloNegro.org - Lucu.git/commitdiff
staticDir
authorpho <pho@cielonegro.org>
Thu, 12 Apr 2007 16:40:42 +0000 (01:40 +0900)
committerpho <pho@cielonegro.org>
Thu, 12 Apr 2007 16:40:42 +0000 (01:40 +0900)
darcs-hash:20070412164042-62b54-1f3cfc81356c7d9d53b5b25b77c8539789857843.gz

Network/HTTP/Lucu/Interaction.hs
Network/HTTP/Lucu/MIMEType/Guess.hs
Network/HTTP/Lucu/Postprocess.hs
Network/HTTP/Lucu/RequestReader.hs
Network/HTTP/Lucu/Resource.hs
Network/HTTP/Lucu/Resource/Tree.hs
Network/HTTP/Lucu/StaticFile.hs
Network/HTTP/Lucu/Utils.hs
examples/HelloWorld.hs

index 5f28c558c9b53f3c53d79196fd576b54da373ce4..68c6c0e919d6431a4ccc8b8524c1bcefab0ee014 100644 (file)
@@ -24,11 +24,16 @@ import           Network.HTTP.Lucu.Request
 import           Network.HTTP.Lucu.Response
 
 data Interaction = Interaction {
-      itrConfig      :: Config
-    , itrRemoteHost  :: HostName
-    , itrRequest     :: Maybe Request
-    , itrResponse    :: TVar (Maybe Response)
-
+      itrConfig       :: Config
+    , itrRemoteHost   :: HostName
+    , itrResourcePath :: Maybe [String]
+    , itrRequest      :: Maybe Request
+    , itrResponse     :: TVar (Maybe Response)
+
+    -- FIXME: この三つは本來 TVar であるべきでないので、唯の Bool にす
+    -- るに越した事は無いが、それは重要でない。そんな golf で自分の貴重
+    -- な時間を /dev/null に突っ込むのは、他にしたい事が何も無くなって
+    -- からにすべき。
     , itrRequestHasBody    :: TVar Bool
     , itrRequestIsChunked  :: TVar Bool
     , itrExpectedContinue  :: TVar Bool
@@ -99,10 +104,11 @@ newInteraction conf host req
          wroteHeader   <- newTVarIO False
 
          return $ Interaction {
-                      itrConfig     = conf
-                    , itrRemoteHost = host
-                    , itrRequest    = req
-                    , itrResponse   = responce
+                      itrConfig       = conf
+                    , itrRemoteHost   = host
+                    , itrResourcePath = Nothing
+                    , itrRequest      = req
+                    , itrResponse     = responce
 
                     , itrRequestHasBody    = requestHasBody
                     , itrRequestIsChunked  = requestIsChunked
index 7fe58206b5dcb86cd15538c4906f2f86ed0a09d8..12f19e0684748eb7bccfa229e670fb80f9b91e57 100644 (file)
@@ -25,7 +25,7 @@ type ExtMap = Map String MIMEType
 
 guessTypeByFileName :: ExtMap -> FilePath -> Maybe MIMEType
 guessTypeByFileName extMap fpath
-    = let ext = head $ reverse $ splitBy (== '.') fpath
+    = let ext = last $ splitBy (== '.') fpath
       in
         M.lookup ext extMap >>= return
 
index 260bbcc6bdffb3d42efaad0a2560f88c3d719878..80fc7225a4de9bca49f2891189e8123e8e5a5128 100644 (file)
@@ -106,6 +106,10 @@ postprocess itr
 
                updateRes itr $ deleteHeader "Content-Length"
 
+               cType <- readHeader itr "Content-Type"
+               when (cType == Nothing)
+                        $ updateRes itr $ setHeader "Content-Type" "application/octet-stream"
+
                if canHaveBody then
                    do teM <- readHeader itr "Transfer-Encoding"
                       if reqVer == HttpVersion 1 1 then
@@ -130,10 +134,6 @@ postprocess itr
                                                $ Just ("Transfer-Encoding must be `identity' because "
                                                        ++ "this is an HTTP/1.0 request: "
                                                        ++ te)
-
-                      cType <- readHeader itr "Content-Type"
-                      when (cType == Nothing)
-                               $ updateRes itr $ setHeader "Content-Type" "application/octet-stream"
                  else
                    -- body 関連のヘッダを削除。但し HEAD なら Content-* は殘す
                    do updateRes itr $ deleteHeader "Transfer-Encoding"
index 00d3b03ea5251a3b1fe12ae5c2dccb5a7caa339b..9b54ca58e92ac648a20bc6dbcd07b4b0dbd7f949 100644 (file)
@@ -85,8 +85,8 @@ requestReader cnf tree h host tQueue
                                Nothing -- Resource が無かった
                                    -> acceptRequestForNonexistentResource itr input
 
-                               Just rsrcDef -- あった
-                                   -> acceptRequestForExistentResource itr input rsrcDef
+                               Just (rsrcPath, rsrcDef) -- あった
+                                   -> acceptRequestForExistentResource itr input rsrcPath rsrcDef
                action
 
       acceptSemanticallyInvalidRequest :: Interaction -> ByteString -> STM (IO ())
@@ -111,9 +111,10 @@ requestReader cnf tree h host tQueue
                enqueue itr
                return $ acceptRequest input
 
-      acceptRequestForExistentResource :: Interaction -> ByteString -> ResourceDef -> STM (IO ())
-      acceptRequestForExistentResource itr input rsrcDef
-          = do requestHasBody <- readItr itr itrRequestHasBody id
+      acceptRequestForExistentResource :: Interaction -> ByteString -> [String] -> ResourceDef -> STM (IO ())
+      acceptRequestForExistentResource oldItr input rsrcPath rsrcDef
+          = do let itr = oldItr { itrResourcePath = Just rsrcPath }
+               requestHasBody <- readItr itr itrRequestHasBody id
                enqueue itr
                return $ do runResource rsrcDef itr
                            if requestHasBody then
index 28ce4628901a8380a3e48e578657b143cf778a0d..7b1b26a0dd08ea9423f3f895395d0e6a45c2c145 100644 (file)
@@ -1,10 +1,15 @@
 module Network.HTTP.Lucu.Resource
     ( Resource
 
-    , getConfig -- Resource Config
-    , getMethod -- Resource Method
-    , getHeader -- String -> Resource (Maybe String)
-    , getAccept -- Resource [MIMEType]
+    , getConfig       -- Resource Config
+    , getRequest      -- Resource Request
+    , getMethod       -- Resource Method
+    , getRequestURI   -- Resource URI
+    , getResourcePath -- Resource [String]
+    , getPathInfo     -- Resource [String]
+
+    , getHeader   -- String -> Resource (Maybe String)
+    , getAccept   -- Resource [MIMEType]
     , getContentType -- Resource (Maybe MIMEType)
 
     , foundEntity    -- ETag -> ClockTime -> Resource ()
@@ -67,9 +72,37 @@ getConfig = do itr <- ask
                return $ itrConfig itr
 
 
+getRequest :: Resource Request
+getRequest = do itr <- ask
+                return $ fromJust $ itrRequest itr
+
+
 getMethod :: Resource Method
-getMethod = do itr <- ask
-               return $ reqMethod $ fromJust $ itrRequest itr
+getMethod = do req <- getRequest
+               return $ reqMethod req
+
+
+getRequestURI :: Resource URI
+getRequestURI = do req <- getRequest
+                   return $ reqURI req
+
+
+getResourcePath :: Resource [String]
+getResourcePath = do itr <- ask
+                     return $ fromJust $ itrResourcePath itr
+
+
+getPathInfo :: Resource [String]
+getPathInfo = do rsrcPath <- getResourcePath
+                 reqURI   <- getRequestURI
+                 let reqPathStr = uriPath reqURI
+                     reqPath    = [x | x <- splitBy (== '/') reqPathStr, x /= ""]
+                 -- rsrcPath と reqPath の共通する先頭部分を reqPath か
+                 -- ら全部取り除くと、それは PATH_INFO のやうなものにな
+                 -- る。rsrcPath は全部一致してゐるに決まってゐる(でな
+                 -- ければこの Resource が撰ばれた筈が無い)ので、
+                 -- rsrcPath の長さの分だけ削除すれば良い。
+                 return $ drop (length rsrcPath) reqPath
 
 
 getHeader :: String -> Resource (Maybe String)
@@ -414,18 +447,22 @@ outputChunk = outputChunkBS . B.pack
 outputChunkBS :: ByteString -> Resource ()
 outputChunkBS str
     = do driftTo DecidingBody
-         unless (B.null str)
-                    $ do itr <- ask
+         itr <- ask
+         
+         let limit = cnfMaxOutputChunkLength $ itrConfig itr
+         when (limit <= 0)
+                  $ fail ("cnfMaxOutputChunkLength must be positive: "
+                          ++ show limit)
 
-                         let limit = cnfMaxOutputChunkLength $ itrConfig itr
-                         when (limit <= 0)
-                                  $ fail ("cnfMaxOutputChunkLength must be positive: "
-                                          ++ show limit)
+         discardBody <- liftIO $ atomically $
+                        readItr itr itrWillDiscardBody id
 
-                         sendChunks str limit
+         unless (discardBody)
+                    $ sendChunks str limit
 
-                         liftIO $ atomically $
-                                writeItr itr itrBodyIsNull False
+         unless (B.null str)
+                    $ liftIO $ atomically $
+                      writeItr itr itrBodyIsNull False
     where
       sendChunks :: ByteString -> Int -> Resource ()
       sendChunks str limit
index 28a94a4791d08235a1ff98abe2e8adf91a78c8eb..6fc49d477891adfd16b80f37466f21d6d865d6f3 100644 (file)
@@ -4,7 +4,7 @@ module Network.HTTP.Lucu.Resource.Tree
     , ResTree
     , mkResTree    -- [ ([String], ResourceDef) ] -> ResTree
 
-    , findResource -- ResTree -> URI -> Maybe ResourceDef
+    , findResource -- ResTree -> URI -> Maybe ([String], ResourceDef)
     , runResource  -- ResourceDef -> Interaction -> IO ThreadId
     )
     where
@@ -20,6 +20,7 @@ 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
@@ -86,30 +87,33 @@ mkResTree list = processRoot list
               subtree
 
 
-findResource :: ResTree -> URI -> Maybe ResourceDef
+findResource :: ResTree -> URI -> Maybe ([String], ResourceDef)
 findResource (ResNode rootDefM subtree) uri
     = let pathStr = uriPath uri
           path    = [x | x <- splitBy (== '/') pathStr, x /= ""]
       in
         if null path then
-            rootDefM
+            do def <- rootDefM
+               return (path, def)
         else
-            walkTree subtree path
+            walkTree subtree path []
     where
-      walkTree :: ResSubtree -> [String] -> Maybe ResourceDef
+      walkTree :: ResSubtree -> [String] -> [String] -> Maybe ([String], ResourceDef)
 
-      walkTree subtree (name:[])
+      walkTree subtree (name:[]) soFar
           = case M.lookup name subtree of
               Nothing               -> Nothing
-              Just (ResNode defM _) -> defM
+              Just (ResNode defM _) -> do def <- defM
+                                          return (soFar ++ [name], def)
 
-      walkTree subtree (x:xs)
+      walkTree subtree (x:xs) soFar
           = case M.lookup x subtree of
               Nothing                      -> Nothing
               Just (ResNode defM children) -> case defM of
                                                 Just (ResourceDef { resIsGreedy = True })
-                                                    -> defM
-                                                _   -> walkTree children xs
+                                                    -> do def <- defM
+                                                          return (soFar ++ [x], def)
+                                                _   -> walkTree children xs (soFar ++ [x])
 
 
 runResource :: ResourceDef -> Interaction -> IO ThreadId
@@ -177,7 +181,7 @@ runResource def itr
                            -- FIXME: 同じ名前で複數の値があった時は、こ
                            -- れではまずいと思ふ。
                            mapM_ (\ (name, value) -> setHeader name value) $ aboHeaders abo
-                           setHeader "Content-Type" "application/xhtml+xml"
+                           setContentType ("application" +/+ "xhtml+xml")
                            output $ abortPage conf reqM resM abo
                  else
                    hPutStrLn stderr $ show abo
index 89b783281b12bbfdf526fabcdc545c856e68ecec..e5443409d2f65b0e1639622aae1d334eceff96db 100644 (file)
@@ -1,6 +1,9 @@
 module Network.HTTP.Lucu.StaticFile
     ( staticFile       -- FilePath -> ResourceDef
     , handleStaticFile -- FilePath -> Resource ()
+
+    , staticDir       -- FilePath -> ResourceDef
+    , handleStaticDir -- FilePath -> Resource ()
     )
     where
 
@@ -15,6 +18,7 @@ import           Network.HTTP.Lucu.MIMEType.Guess
 import           Network.HTTP.Lucu.Resource
 import           Network.HTTP.Lucu.Resource.Tree
 import           Network.HTTP.Lucu.Response
+import           Network.HTTP.Lucu.Utils
 import           System.Directory
 import           System.Posix.Files
 import           Text.Printf
@@ -35,8 +39,8 @@ staticFile path
 
 handleStaticFile :: FilePath -> Resource ()
 handleStaticFile path
-    = do exist <- liftIO $ fileExist path
-         if exist then
+    = do isFile <- liftIO $ doesFileExist path
+         if isFile then
              -- 存在はした。讀めるかどうかは知らない。
              do readable <- liftIO $ fileAccess path True False False
                 unless readable
@@ -57,7 +61,11 @@ handleStaticFile path
                 -- 實際にファイルを讀んで送る
                 (liftIO $ B.readFile path) >>= outputBS
            else
-             foundNoEntity Nothing
+             do isDir <- liftIO $ doesDirectoryExist path
+                if isDir then
+                    abort Forbidden [] Nothing
+                  else
+                    foundNoEntity Nothing
 
 
 -- inode-size-lastmod
@@ -68,3 +76,24 @@ generateETagFromFile path
              size    = fromEnum $ fileSize stat
              lastmod = fromEnum $ modificationTime stat
          return $ strongETag $ printf "%x-%x-%x" inode size lastmod
+
+
+staticDir :: FilePath -> ResourceDef
+staticDir path
+    = ResourceDef {
+        resUsesNativeThread = False
+      , resIsGreedy         = True
+      , resGet              = Just $ handleStaticDir path
+      , resHead             = Nothing
+      , resPost             = Nothing
+      , resPut              = Nothing
+      , resDelete           = Nothing
+      }
+
+
+handleStaticDir :: FilePath -> Resource ()
+handleStaticDir basePath
+    = do extraPath <- getPathInfo
+         let path = basePath ++ "/" ++ joinWith "/" extraPath
+
+         handleStaticFile path
index 58da6f50c2612ac1e64e4d8366e17dfa21712f50..5dc1584683b5b23082cc3ae63cbd57ad293fe2e2 100644 (file)
@@ -13,6 +13,7 @@ import Data.Char
 import Data.List
 import Foreign
 import Foreign.C
+import Network.URI
 
 
 splitBy :: (a -> Bool) -> [a] -> [[a]]
index a2c6d50c02795a8959fb1c54c2b5a32d100ff95e..43e21d2b5e039c89b58e0e973072c02041310dab 100644 (file)
@@ -20,8 +20,15 @@ main :: IO ()
 main = let config    = defaultConfig { cnfServerPort = PortNumber 9999 }
            resources = mkResTree [ ( []
                                    , helloWorld )
-                                 , ( ["compilers"]
-                                   , staticFile "/etc/compilers" )
+
+                                 , ( ["index.html"]
+                                   , staticFile "/Users/admin/Sites/index.html" )
+
+                                 , ( ["urandom"]
+                                   , staticFile "/dev/urandom" )
+
+                                 , ( ["inc"]
+                                   , staticDir "/usr/include" )
                                  ]
        in
          do installHandler sigPIPE Ignore Nothing