on master help sign in
Finished embedded cleanup
commitbe8df49f7b56337b3fd14ecc7e62d8529502e307 authorMichael Snoyman <michael@snoyman.com> dateTue 22 May 2012, 07:47 (14 years ago) parent10f0201
5 files changed+100−68
wai-app-static/Backend/Embedded.hs−65 wai-app-static/Network/Wai/Application/Static.hs+2 wai-app-static/WaiAppStatic/Storage/Embedded.hs+92 wai-app-static/embedded-sample.hs+5−3 wai-app-static/wai-app-static.cabal+1
wai-app-static/Backend/Embedded.hsdeleted−65
@@ -1,65 +0,0 @@
1−module Backend.Embedded where
2−
3−type Embedded = Map.Map FilePath EmbeddedEntry
4−
5−data EmbeddedEntry = EEFile S8.ByteString | EEFolder Embedded
6−
7−embeddedLookup :: Embedded -> Pieces -> IO FileLookup
8−embeddedLookup root pieces =
9− return $ elookup "<root>" (map F.fromText pieces) root
10− where
11− elookup :: FilePath -> [FilePath] -> Embedded -> FileLookup
12− elookup p [] x = Just $ Left $ Folder (unFilePath p) $ map toEntry $ Map.toList x
13− elookup p [""] x = elookup p [] x
14− elookup _ (p:ps) x =
15− case Map.lookup p x of
16− Nothing -> Nothing
17− Just (EEFile f) ->
18− case ps of
19− [] -> Just $ Right $ bsToFile p f
20− _ -> Nothing
21− Just (EEFolder y) -> elookup p ps y
22−
23−toEntry :: (FilePath, EmbeddedEntry) -> Either Folder File
24−toEntry = error "toEntry"
25−{-
26−toEntry (name, EEFolder{}) = Left $ Folder name []
27−toEntry (name, EEFile bs) = Right $ File
28− { fileGetSize = S8.length bs
29− , fileToResponse = \s h -> W.ResponseBuilder s h $ fromByteString bs
30− , fileName = name
31− , fileGetHash = return $ Just $ runHash bs
32− , fileGetModified = Nothing
33− }
34−-}
35−
36−toEmbedded :: [(Prelude.FilePath, S8.ByteString)] -> Embedded
37−toEmbedded fps =
38− go texts
39− where
40− texts = map (\(x, y) -> (filter (not . T.null . unFilePath) $ toPieces x, y)) fps
41− toPieces "" = []
42− toPieces x =
43− let (y, z) = break (== '/') x
44− in toFilePath y : toPieces (drop 1 z)
45− go :: [([FilePath], S8.ByteString)] -> Embedded
46− go orig =
47− Map.fromList $ map (second go') hoisted
48− where
49− next = map (\(x, y) -> (head x, (tail x, y))) orig
50− grouped :: [[(FilePath, ([FilePath], S8.ByteString))]]
51− grouped = groupBy ((==) `on` fst) $ sortBy (comparing fst) next
52− hoisted :: [(FilePath, [([FilePath], S8.ByteString)])]
53− hoisted = map (fst . head &&& map snd) grouped
54− go' :: [([FilePath], S8.ByteString)] -> EmbeddedEntry
55− go' [([], content)] = EEFile content
56− go' x = EEFolder $ go $ filter (\y -> not $ null $ fst y) x
57−
58−bsToFile :: FilePath -> S8.ByteString -> File
59−bsToFile name bs = File
60− { fileGetSize = S8.length bs
61− , fileToResponse = \s h -> W.ResponseBuilder s h $ fromByteString bs
62− , fileName = name
63− , fileGetHash = return $ Just $ runHash bs
64− , fileGetModified = Nothing
65− }
wai-app-static/Network/Wai/Application/Static.hs+2
@@ -10,6 +10,7 @@ module Network.Wai.Application.Static
1010 , defaultWebAppSettings
1111 , webAppSettingsWithLookup
1212 , defaultFileServerSettings
13+ , embeddedSettings
1314 -- ** Settings
1415 , StaticSettings
1516 , ssLookupFile
@@ -45,6 +46,7 @@ import Data.Monoid (First (First, getFirst), mconcat)
4546import WaiAppStatic.Types
4647import Util
4748import WaiAppStatic.Storage.Filesystem
49+import WaiAppStatic.Storage.Embedded
4850
4951data StaticResponse =
5052 -- | Just the etag hash or Nothing for no etag hash
wai-app-static/WaiAppStatic/Storage/Embedded.hsnew file+92
@@ -0,0 +1,92 @@
1+-- | Lookup files stored in memory instead of from the filesystem.
2+module WaiAppStatic.Storage.Embedded
3+ ( -- * Settings
4+ embeddedSettings
5+ ) where
6+
7+import WaiAppStatic.Types
8+import Data.ByteString (ByteString)
9+import Control.Arrow ((&&&), second)
10+import Data.List
11+import Blaze.ByteString.Builder (fromByteString)
12+import qualified Network.Wai as W
13+import qualified Data.Map as Map
14+import Data.Function (on)
15+import qualified Data.Text as T
16+import Data.Ord
17+import qualified Data.ByteString as S
18+import qualified Crypto.Hash.MD5 as MD5
19+import qualified Data.ByteString.Base64 as B64
20+import WaiAppStatic.Storage.Filesystem (defaultFileServerSettings)
21+
22+-- | Serve the list of path/content pairs directly from memory.
23+embeddedSettings :: [(Prelude.FilePath, ByteString)] -> StaticSettings
24+embeddedSettings files = (defaultFileServerSettings $ error "unused")
25+ { ssLookupFile = embeddedLookup $ toEmbedded files
26+ }
27+
28+type Embedded = Map.Map Piece EmbeddedEntry
29+
30+data EmbeddedEntry = EEFile ByteString | EEFolder Embedded
31+
32+embeddedLookup :: Embedded -> Pieces -> IO LookupResult
33+embeddedLookup root pieces =
34+ return $ elookup pieces root
35+ where
36+ elookup :: Pieces -> Embedded -> LookupResult
37+ elookup [] x = LRFolder $ Folder $ map toEntry $ Map.toList x
38+ elookup [p] x | T.null (fromPiece p) = elookup [] x
39+ elookup (p:ps) x =
40+ case Map.lookup p x of
41+ Nothing -> LRNotFound
42+ Just (EEFile f) ->
43+ case ps of
44+ [] -> LRFile $ bsToFile p f
45+ _ -> LRNotFound
46+ Just (EEFolder y) -> elookup ps y
47+
48+toEntry :: (Piece, EmbeddedEntry) -> Either FolderName File
49+toEntry (name, EEFolder{}) = Left name
50+toEntry (name, EEFile bs) = Right File
51+ { fileGetSize = S.length bs
52+ , fileToResponse = \s h -> W.ResponseBuilder s h $ fromByteString bs
53+ , fileName = name
54+ , fileGetHash = return $ Just $ runHash bs
55+ , fileGetModified = Nothing
56+ }
57+
58+toEmbedded :: [(Prelude.FilePath, ByteString)] -> Embedded
59+toEmbedded fps =
60+ go texts
61+ where
62+ texts = map (\(x, y) -> (filter (not . T.null . fromPiece) $ toPieces' x, y)) fps
63+ toPieces' "" = []
64+ toPieces' x =
65+ let (y, z) = break (== '/') x
66+ in unsafeToPiece (T.pack y) : toPieces' (drop 1 z)
67+
68+ go :: [(Pieces, ByteString)] -> Embedded
69+ go orig =
70+ Map.fromList $ map (second go') hoisted
71+ where
72+ next = map (\(x, y) -> (head x, (tail x, y))) orig
73+ grouped :: [[(Piece, ([Piece], ByteString))]]
74+ grouped = groupBy ((==) `on` fst) $ sortBy (comparing fst) next
75+ hoisted :: [(Piece, [([Piece], ByteString)])]
76+ hoisted = map (fst . head &&& map snd) grouped
77+
78+ go' :: [(Pieces, ByteString)] -> EmbeddedEntry
79+ go' [([], content)] = EEFile content
80+ go' x = EEFolder $ go $ filter (\y -> not $ null $ fst y) x
81+
82+bsToFile :: Piece -> ByteString -> File
83+bsToFile name bs = File
84+ { fileGetSize = S.length bs
85+ , fileToResponse = \s h -> W.ResponseBuilder s h $ fromByteString bs
86+ , fileName = name
87+ , fileGetHash = return $ Just $ runHash bs
88+ , fileGetModified = Nothing
89+ }
90+
91+runHash :: ByteString -> ByteString
92+runHash = B64.encode . MD5.hash
wai-app-static/embedded-sample.hs+5−3
@@ -1,11 +1,13 @@
11{-# LANGUAGE TemplateHaskell #-}
2+{-# LANGUAGE OverloadedStrings #-}
23import Network.Wai.Application.Static
34import Network.Wai.Handler.Warp (run)
45import Data.FileEmbed
6+import WaiAppStatic.Types
7+import WaiAppStatic.Storage.Embedded
58
69main :: IO ()
7−main = run 3000 $ staticApp defaultFileServerSettings
8− { ssFolder = embeddedLookup $ toEmbedded $(embedDir ".")
9− , ssIndices = []
10+main = run 3000 $ staticApp (embeddedSettings $(embedDir "test"))
11+ { ssIndices = []
1012 , ssMaxAge = NoMaxAge
1113 }
wai-app-static/wai-app-static.cabal+1
@@ -48,6 +48,7 @@ library
4848
4949 exposed-modules: Network.Wai.Application.Static
5050 WaiAppStatic.Storage.Filesystem
51+ WaiAppStatic.Storage.Embedded
5152 WaiAppStatic.Listing
5253 WaiAppStatic.Mime
5354 WaiAppStatic.Types