1 module Network.HTTP.Lucu.StaticFile
13 import Control.Monad.Trans
14 import qualified Data.ByteString.Lazy.Char8 as B
15 import Data.ByteString.Lazy.Char8 (ByteString)
16 import Network.HTTP.Lucu.Abortion
17 import Network.HTTP.Lucu.Config
18 import Network.HTTP.Lucu.ETag
19 import Network.HTTP.Lucu.MIMEType.Guess
20 import Network.HTTP.Lucu.Resource
21 import Network.HTTP.Lucu.Resource.Tree
22 import Network.HTTP.Lucu.Response
23 import Network.HTTP.Lucu.Utils
24 import System.Directory
25 import System.Posix.Files
29 staticFile :: FilePath -> ResourceDef
32 resUsesNativeThread = False
34 , resGet = Just $ handleStaticFile path
42 handleStaticFile :: FilePath -> Resource ()
44 = do isFile <- liftIO $ doesFileExist path
46 -- 存在はした。讀めるかどうかは知らない。
47 do readable <- liftIO $ fileAccess path True False False
50 $ abort Forbidden [] Nothing
53 tag <- liftIO $ generateETagFromFile path
54 lastMod <- liftIO $ getModificationTime path
55 foundEntity tag lastMod
59 case guessTypeByFileName (cnfExtToMIMEType conf) path of
61 Just mime -> setContentType mime
64 (liftIO $ B.readFile path) >>= outputBS
66 do isDir <- liftIO $ doesDirectoryExist path
68 abort Forbidden [] Nothing
73 -- |Computation @'generateETagFromFile' fpath@ generates a strong
74 -- entity tag from a file. The file doesn't necessarily have to be a
75 -- regular file; it may be a FIFO or a device file. The tag is made of
76 -- inode ID, size and modification time.
78 -- Note that the tag is not strictly strong because the file could be
79 -- modified twice at a second without changing inode ID or size, but
80 -- it's not really possible to generate a strict strong ETag from a
81 -- file since we don't want to simply grab the entire file and use it
82 -- as an ETag. It is indeed possible to hash it with SHA-1 or MD5 to
83 -- increase strictness, but it's too inefficient if the file is really
84 -- large (say, 1 TiB).
85 generateETagFromFile :: FilePath -> IO ETag
86 generateETagFromFile path
87 = do stat <- getFileStatus path
88 let inode = fromEnum $ fileID stat
89 size = fromEnum $ fileSize stat
90 lastmod = fromEnum $ modificationTime stat
91 return $ strongETag $ printf "%x-%x-%x" inode size lastmod
94 staticDir :: FilePath -> ResourceDef
97 resUsesNativeThread = False
99 , resGet = Just $ handleStaticDir path
103 , resDelete = Nothing
107 handleStaticDir :: FilePath -> Resource ()
108 handleStaticDir basePath
109 = do extraPath <- getPathInfo
110 securityCheck extraPath
111 let path = basePath ++ "/" ++ joinWith "/" extraPath
113 handleStaticFile path
115 securityCheck :: Monad m => [String] -> m ()
116 securityCheck pathElems
117 = when (any (== "..") pathElems) $ fail ("security error: "
118 ++ joinWith "/" pathElems)