wai-app-static 0.3.1.1 → 0.3.3
raw patch · 2 files changed
+64/−35 lines, 2 filesdep ~hspecdep ~timePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: hspec, time
API changes (from Hackage documentation)
+ Network.Wai.Application.Static: type ETagLookup = FilePath -> IO (Maybe ByteString)
+ Network.Wai.Application.Static: webAppSettingsWithLookup :: FilePath -> ETagLookup -> StaticSettings
- Network.Wai.Application.Static: File :: Int -> (Status -> ResponseHeaders -> Response) -> FilePath -> Maybe (IO ByteString) -> Maybe EpochTime -> File
+ Network.Wai.Application.Static: File :: Int -> (Status -> ResponseHeaders -> Response) -> FilePath -> IO (Maybe ByteString) -> Maybe EpochTime -> File
- Network.Wai.Application.Static: fileGetHash :: File -> Maybe (IO ByteString)
+ Network.Wai.Application.Static: fileGetHash :: File -> IO (Maybe ByteString)
- Network.Wai.Application.Static: fileSystemLookupHash :: (FilePath -> Maybe (IO ByteString)) -> FilePath -> Pieces -> IO FileLookup
+ Network.Wai.Application.Static: fileSystemLookupHash :: ETagLookup -> FilePath -> Pieces -> IO FileLookup
Files
- Network/Wai/Application/Static.hs +61/−32
- wai-app-static.cabal +3/−3
Network/Wai/Application/Static.hs view
@@ -6,6 +6,7 @@ staticApp -- ** Settings , defaultWebAppSettings+ , webAppSettingsWithLookup , defaultFileServerSettings , StaticSettings , ssFolder@@ -47,6 +48,7 @@ , toFilePath , fromFilePath , MaxAge (..)+ , ETagLookup ) where import Prelude hiding (FilePath)@@ -229,10 +231,12 @@ nullFilePath :: FilePath -> Bool nullFilePath = T.null . unFilePath +{- stripTrailingSlash :: FilePath -> FilePath stripTrailingSlash fp@(FilePath t) | T.null t || T.last t /= '/' = fp | otherwise = FilePath $ T.init t+ -} type Pieces = [FilePath] @@ -304,31 +308,32 @@ handleCache file = if not useHash then lastModifiedCache file else do- let mGetHash = fileGetHash file let etagParam = lookup "etag" queryString - case (etagParam, mGetHash) of- (Just mEtag, Just getHash) -> do- hash <- getHash- if isJust mEtag && hash == fromJust mEtag- then return $ FileResponse file $ ("ETag", hash):cacheControl- else return $ Redirect pieces (Just hash)- -- a file used to have an etag parameter, but no longer does- (Just _, Nothing) -> return $ Redirect pieces Nothing+ case etagParam of+ Nothing -> do -- no query parameter. Set appropriate ETag headers+ mHash <- fileGetHash file+ case mHash of+ Nothing -> lastModifiedCache file+ Just hash ->+ case lookup "if-none-match" headers of+ Just lastHash ->+ if hash == lastHash+ then return NotModified+ else return $ FileResponse file $ [("ETag", hash)]+ Nothing -> return $ FileResponse file $ [("ETag", hash)] - _ -> - case (lookup "if-none-match" headers, mGetHash) of- -- etag- (mLastHash, Just getHash) -> do- hash <- getHash- case mLastHash of- Just lastHash ->- if hash == lastHash- then return NotModified- else return $ FileResponse file $ [("ETag", hash)]- Nothing -> return $ FileResponse file $ [("ETag", hash)]- (_, Nothing) -> lastModifiedCache file+ Just mEtag -> do+ mHash <- fileGetHash file+ case mHash of+ -- a file used to have an etag parameter, but no longer does+ Nothing -> return $ Redirect pieces Nothing+ Just hash ->+ if isJust mEtag && hash == fromJust mEtag+ then return $ FileResponse file $ ("ETag", hash):cacheControl+ else return $ Redirect pieces (Just hash) + lastModifiedCache file = case (lookup "if-modified-since" headers >>= parseHTTPDate, fileGetModified file) of (mLastSent, Just modified) -> do@@ -375,12 +380,12 @@ { fileGetSize :: Int , fileToResponse :: H.Status -> H.ResponseHeaders -> W.Response , fileName :: FilePath- , fileGetHash :: Maybe (IO ByteString)+ , fileGetHash :: IO (Maybe ByteString) , fileGetModified :: Maybe EpochTime } data StaticSettings = StaticSettings- { ssFolder :: Pieces -> IO FileLookup+ { ssFolder :: Pieces -> IO FileLookup -- TODO: not a folder, so rename , ssMkRedirect :: Pieces -> ByteString -> ByteString , ssGetMimeType :: File -> IO MimeType , ssListing :: Maybe Listing@@ -400,9 +405,14 @@ where relDir = TE.encodeUtf8 (relativeDirFromPieces pieces) +webAppSettingsWithLookup :: FilePath -> ETagLookup -> StaticSettings+webAppSettingsWithLookup dir etagLookup =+ defaultWebAppSettings { ssFolder = webAppLookup etagLookup dir}++ defaultWebAppSettings :: StaticSettings defaultWebAppSettings = StaticSettings- { ssFolder = fileSystemLookup "static"+ { ssFolder = webAppLookup hashFileIfExists "static" , ssMkRedirect = defaultMkRedirect , ssGetMimeType = return . defaultMimeTypeByExt . fileName , ssMaxAge = MaxAgeForever@@ -422,7 +432,7 @@ , ssUseHash = False } -fileHelper :: (FilePath -> Maybe (IO ByteString)) -- ^ hash function+fileHelper :: ETagLookup -> FilePath -> FilePath -> IO File fileHelper hashFunc fp name = do fs <- getFileStatus $ fromFilePath fp@@ -434,17 +444,36 @@ , fileGetModified = Just $ modificationTime fs } -defaultFileSystemHash :: FilePath -> Maybe (IO ByteString)-defaultFileSystemHash fp = Just $ do- -- FIXME replace lazy IO with enumerators- -- FIXME let's use a dictionary to cache these values?+type ETagLookup = (FilePath -> IO (Maybe ByteString))++webAppLookup :: ETagLookup -> FilePath -> Pieces -> IO FileLookup+webAppLookup cachedLookupHash prefix pieces = do+ file <- fileHelper cachedLookupHash fp (last pieces)+ return $ Just $ Right $ file+ where+ fp = pathFromPieces prefix pieces++defaultFileSystemHash :: ETagLookup+defaultFileSystemHash fp = fmap Just $ hashFile fp++-- FIXME replace lazy IO with enumerators+-- FIXME let's use a dictionary to cache these values?+hashFile :: FilePath -> IO ByteString+hashFile fp = do l <- L.readFile $ fromFilePath fp return $ runHashL l +hashFileIfExists :: ETagLookup+hashFileIfExists fp = do+ fe <- doesFileExist $ fromFilePath fp+ if fe+ then return Nothing+ else defaultFileSystemHash fp+ fileSystemLookup :: FilePath -> Pieces -> IO FileLookup fileSystemLookup = fileSystemLookupHash defaultFileSystemHash -fileSystemLookupHash :: (FilePath -> Maybe (IO ByteString)) -- ^ hash function+fileSystemLookupHash :: ETagLookup -> FilePath -> Pieces -> IO FileLookup fileSystemLookupHash hashFunc prefix pieces = do let fp = pathFromPieces prefix pieces@@ -494,7 +523,7 @@ { fileGetSize = S8.length bs , fileToResponse = \s h -> W.ResponseBuilder s h $ fromByteString bs , fileName = name- , fileGetHash = Just $ return $ runHash bs+ , fileGetHash = return $ Just $ runHash bs , fileGetModified = Nothing } @@ -525,7 +554,7 @@ { fileGetSize = S8.length bs , fileToResponse = \s h -> W.ResponseBuilder s h $ fromByteString bs , fileName = name- , fileGetHash = Just $ return $ runHash bs+ , fileGetHash = return $ Just $ runHash bs , fileGetModified = Nothing }
wai-app-static.cabal view
@@ -1,5 +1,5 @@ name: wai-app-static-version: 0.3.1.1+version: 0.3.3 license: BSD3 license-file: LICENSE author: Michael Snoyman <michael@snoyman.com>@@ -48,10 +48,10 @@ type: exitcode-stdio-1.0 build-depends: base >= 4 && < 5- , hspec >= 0.6+ , hspec >= 0.6.1 && < 0.7 , HUnit , unix-compat >= 0.2 && < 0.3- , time >= 1.1.4 && < 1.3+ , time >= 1.1.4 && < 1.4 , old-locale >= 1.0.0.2 && < 1.1 , http-date , Cabal