on master help sign in
implementing file info cache.

This is not used internally yet. Apps can access it via getFileInfo.

commit999b17d4e65c9b0d52aa2951c9d8e1e3112ff7e1 authorKazu Yamamoto <kazu@iij.ad.jp> dateMon 30 Nov 2015, 16:07 (11 years ago) parent0569be7
5 files changed+124−17
warp/Network/Wai/Handler/Warp.hs+11−1 warp/Network/Wai/Handler/Warp/Date.hs+18−14 warp/Network/Wai/Handler/Warp/FileInfoCache.hs+86 warp/Network/Wai/Handler/Warp/Request.hs+6 warp/warp.cabal+3−2
warp/Network/Wai/Handler/Warp.hs+11−1
@@ -95,6 +95,8 @@ module Network.Wai.Handler.Warp (
9595 , InvalidRequest (..)
9696 -- * Utilities
9797 , pauseTimeout
98+ , FileInfo(..)
99+ , getFileInfo
98100 -- * Internal
99101 -- | The following APIs will be removed in Warp 3.2.0. Please use ones exported from Network.Wai.Handler.Warp.Internal.
100102
@@ -136,7 +138,7 @@ module Network.Wai.Handler.Warp (
136138 , module Network.Wai.Handler.Warp.Timeout
137139 ) where
138140
139−import Control.Exception (SomeException)
141+import Control.Exception (SomeException, throwIO)
140142import Data.ByteString (ByteString)
141143import Data.Maybe (fromMaybe)
142144import Data.Streaming.Network (HostPreference)
@@ -145,6 +147,7 @@ import Network.Socket (SockAddr)
145147import Network.Wai (Request, Response, vault)
146148import Network.Wai.Handler.Warp.Buffer
147149import Network.Wai.Handler.Warp.Date
150+import Network.Wai.Handler.Warp.FileInfoCache
148151import Network.Wai.Handler.Warp.FdCache
149152import Network.Wai.Handler.Warp.Header
150153import Network.Wai.Handler.Warp.Request
@@ -371,3 +374,10 @@ setHTTP2Disabled y = y { settingsHTTP2Enabled = False }
371374-- Since 3.0.10
372375pauseTimeout :: Request -> IO ()
373376pauseTimeout = fromMaybe (return ()) . Vault.lookup pauseTimeoutKey . vault
377+
378+-- | Getting file information from the inside cache.
379+-- If file information is not available, an IO error is thrown.
380+--
381+-- Since 3.1.10
382+getFileInfo :: Request -> FilePath -> IO FileInfo
383+getFileInfo = fromMaybe (\_ -> throwIO (userError "getFileInfo")) . Vault.lookup getFileInfoKey . vault
warp/Network/Wai/Handler/Warp/Date.hs+18−14
@@ -5,6 +5,9 @@ module Network.Wai.Handler.Warp.Date (
55 , getDate
66 , DateCache
77 , GMTDate
8+#if WINDOWS
9+ , uToH
10+#endif
811 ) where
912
1013#if __GLASGOW_HASKELL__ < 709
@@ -12,16 +15,13 @@ import Control.Applicative
1215#endif
1316import Control.AutoUpdate (defaultUpdateSettings, updateAction, mkAutoUpdate)
1417import Data.ByteString.Char8
18+import Network.HTTP.Date
1519
1620#if WINDOWS
17−import Data.Time (formatTime, getCurrentTime)
18−# if MIN_VERSION_time(1,5,0)
19−import Data.Time (defaultTimeLocale)
20−# else
21−import System.Locale (defaultTimeLocale)
22−# endif
21+import Data.Time (UTCTime, formatTime, getCurrentTime)
22+import Data.Time.Clock.POSIX (posixSecondsToUTCTime, utcTimeToPOSIXSeconds)
23+import Foreign.C.Types (CTime(..))
2324#else
24−import Network.HTTP.Date
2525import System.Posix (epochTime)
2626#endif
2727
@@ -36,17 +36,21 @@ withDateCache :: (DateCache -> IO a) -> IO a
3636withDateCache action = initialize >>= action
3737
3838initialize :: IO DateCache
39−initialize = mkAutoUpdate defaultUpdateSettings { updateAction = getCurrentGMTDate }
39+initialize = mkAutoUpdate defaultUpdateSettings {
40+ updateAction = formatHTTPDate <$> getCurrentHTTPDate
41+ }
4042
41−-- | Getting 'GMTDate' based on 'DateCache'.
43+-- | Getting current 'GMTDate' based on 'DateCache'.
4244getDate :: DateCache -> IO GMTDate
4345getDate = id
4446
45−getCurrentGMTDate :: IO GMTDate
4647#ifdef WINDOWS
47−getCurrentGMTDate = formatDate <$> getCurrentTime
48− where
49− formatDate = pack . formatTime defaultTimeLocale "%a, %d %b %Y %H:%M:%S GMT"
48+uToH :: UTCTime -> HTTPDate
49+uToH = epochTimeToHTTPDate . CTime . truncate . utcTimeToPOSIXSeconds
50+
51+getCurrentHTTPDate :: IO HTTPDate
52+getCurrentHTTPDate = uToH <$> getCurrentTime
5053#else
51−getCurrentGMTDate = formatHTTPDate . epochTimeToHTTPDate <$> epochTime
54+getCurrentHTTPDate :: IO HTTPDate
55+getCurrentHTTPDate = epochTimeToHTTPDate <$> epochTime
5256#endif
warp/Network/Wai/Handler/Warp/FileInfoCache.hsnew file+86
@@ -0,0 +1,86 @@
1+{-# LANGUAGE RecordWildCards, CPP #-}
2+
3+module Network.Wai.Handler.Warp.FileInfoCache (
4+ -- * Types
5+ FileInfo(..)
6+ -- * Starter
7+ , fileCacheInit
8+ ) where
9+
10+import Control.Exception as E
11+import Control.Reaper
12+import Data.ByteString (ByteString)
13+import Data.HashMap.Strict (HashMap)
14+import qualified Data.HashMap.Strict as M
15+import Network.HTTP.Date
16+import System.PosixCompat.Files
17+
18+#if WINDOWS
19+import Data.Time (UTCTime)
20+import Network.Wai.Handler.Warp.Date (uToH)
21+#endif
22+
23+data FileInfo = FileInfo {
24+ fileInfoName :: !FilePath
25+ , fileInfoSize :: !Integer
26+ , fileInfoTime :: !HTTPDate -- ^ Modification time
27+ , fileInfoDate :: !ByteString -- ^ Modification time in the GMT format
28+ } deriving (Eq, Show)
29+
30+data Entry = Negative | Positive FileInfo
31+type Cache = HashMap FilePath Entry
32+type FileCache = Reaper Cache (FilePath,Entry)
33+
34+fileInfo :: FileCache -> FilePath -> IO FileInfo
35+fileInfo reaper@Reaper{..} path = do
36+ cache <- reaperRead
37+ case M.lookup path cache of
38+ Just Negative -> throwIO (userError "fileInfo")
39+ Just (Positive x) -> return x
40+ Nothing -> register reaper path `E.onException` negative reaper path
41+
42+register :: FileCache -> FilePath -> IO FileInfo
43+register reaper path = do
44+ fs <- getFileStatus path -- file access
45+ let regular = not (isDirectory fs)
46+ readable = fileMode fs `intersectFileModes` ownerReadMode /= 0
47+ if regular && readable then
48+ positive reaper fs path
49+ else
50+ throwIO (userError "go to negative")
51+
52+positive :: FileCache -> FileStatus -> FilePath -> IO FileInfo
53+positive Reaper{..} fs path = do
54+ reaperAdd (path,entry)
55+ return info
56+ where
57+ info = FileInfo {
58+ fileInfoName = path
59+ , fileInfoSize = size fs
60+ , fileInfoTime = time
61+ , fileInfoDate = formatHTTPDate time
62+ }
63+ size = fromIntegral . fileSize
64+ time = epochTimeToHTTPDate (modificationTime fs)
65+ entry = Positive info
66+
67+negative :: FileCache -> FilePath -> IO FileInfo
68+negative Reaper{..} path = do
69+ reaperAdd (path,Negative)
70+ throwIO (userError "fileInfo")
71+
72+----------------------------------------------------------------
73+
74+fileCacheInit :: IO (FilePath -> IO FileInfo)
75+fileCacheInit = mkReaper settings >>= return . fileInfo
76+ where
77+ settings = defaultReaperSettings {
78+ reaperAction = override
79+ , reaperDelay = 10000000 -- 10 seconds -- hard-coding: fixme
80+ , reaperCons = uncurry M.insert
81+ , reaperNull = M.null
82+ , reaperEmpty = M.empty
83+ }
84+
85+override :: Cache -> IO (Cache -> Cache)
86+override _ = return $ const M.empty
warp/Network/Wai/Handler/Warp/Request.hs+6
@@ -7,6 +7,7 @@ module Network.Wai.Handler.Warp.Request (
77 recvRequest
88 , headerLines
99 , pauseTimeoutKey
10+ , getFileInfoKey
1011 ) where
1112
1213import qualified Control.Concurrent as Conc (yield)
@@ -21,6 +22,7 @@ import qualified Network.HTTP.Types as H
2122import Network.Socket (SockAddr)
2223import Network.Wai
2324import Network.Wai.Handler.Warp.Conduit
25+import Network.Wai.Handler.Warp.FileInfoCache
2426import Network.Wai.Handler.Warp.Header
2527import Network.Wai.Handler.Warp.ReadInt
2628import Network.Wai.Handler.Warp.RequestHeader
@@ -278,3 +280,7 @@ checkCR bs pos = if pos > 0 && 13 == S.index bs p then p else pos -- 13 is CR
278280pauseTimeoutKey :: Vault.Key (IO ())
279281pauseTimeoutKey = unsafePerformIO Vault.newKey
280282{-# NOINLINE pauseTimeoutKey #-}
283+
284+getFileInfoKey :: Vault.Key (FilePath -> IO FileInfo)
285+getFileInfoKey = unsafePerformIO Vault.newKey
286+{-# NOINLINE getFileInfoKey #-}
warp/warp.cabal+3−2
@@ -53,6 +53,7 @@ Library
5353 , stm >= 2.3
5454 , word8
5555 , hashable
56+ , unordered-containers
5657 if flag(network-bytestring)
5758 Build-Depends: network >= 2.2.1.5 && < 2.2.3
5859 , network-bytestring >= 0.1.3 && < 0.1.4
@@ -65,6 +66,7 @@ Library
6566 Network.Wai.Handler.Warp.Counter
6667 Network.Wai.Handler.Warp.Date
6768 Network.Wai.Handler.Warp.FdCache
69+ Network.Wai.Handler.Warp.FileInfoCache
6870 Network.Wai.Handler.Warp.HTTP2
6971 Network.Wai.Handler.Warp.HTTP2.EncodeFrame
7072 Network.Wai.Handler.Warp.HTTP2.HPACK
@@ -99,7 +101,6 @@ Library
99101 if os(windows)
100102 Cpp-Options: -DWINDOWS
101103 Build-Depends: time
102− , old-locale
103104 else
104105 Build-Depends: unix
105106 , http-date
@@ -152,7 +153,6 @@ Test-Suite spec
152153 , QuickCheck
153154 , hspec >= 1.3
154155 , time
155− , old-locale
156156 , text
157157 , streaming-commons >= 0.1.10
158158 , async
@@ -164,6 +164,7 @@ Test-Suite spec
164164 , http2 >= 1.3
165165 , word8
166166 , hashable
167+ , unordered-containers
167168
168169 if (os(linux) || os(freebsd) || os(darwin)) && flag(allow-sendfilefd)
169170 Cpp-Options: -DSENDFILEFD