on master help sign in
Merge pull request #460 from soenkehahn/master

wai-app-static: honor ssIndices when used with defaultWebAppSettings

commit1bb6c53c71ae11c1808c40cd0af4c82b89b3cc25 authorMichael Snoyman <michael@snoyman.com> dateMon 9 Nov 2015, 18:37 (11 years ago) parentc8ff1f4f088f32
6 files changed+80−51
.travis.yml+1−1 wai-app-static/Network/Wai/Application/Static.hs+33−44 wai-app-static/test/EmbeddedTestEntries.hs+5 wai-app-static/test/WaiAppEmbeddedTest.hs+12−5 wai-app-static/test/WaiAppStaticTest.hs+27−1 wai-app-static/wai-app-static.cabal+2
.travis.yml+1−1
@@ -23,7 +23,7 @@ install:
2323 - travis_retry cabal update
2424 - cabal unpack wai-logger
2525 - mv wai-logger-* wai-logger
26− - cabal install hspec doctest HTTP cabal-meta cabal-src
26+ - cabal install hspec doctest HTTP temporary cabal-meta cabal-src
2727 - cabal-meta install --force-reinstalls
2828
2929script: mega-sdist --test
wai-app-static/Network/Wai/Application/Static.hs+33−44
@@ -40,9 +40,7 @@ import Data.FileEmbed (embedFile)
4040import Data.Text (Text)
4141import qualified Data.Text as T
4242
43−import Data.Either (rights)
4443import Network.HTTP.Date (parseHTTPDate, epochTimeToHTTPDate, formatHTTPDate)
45−import Data.Monoid (First (First, getFirst), mconcat)
4644
4745import WaiAppStatic.Types
4846import Util
@@ -74,38 +72,20 @@ filterButLast f (x:xs)
7472
7573-- | Serve an appropriate response for a folder request.
7674serveFolder :: StaticSettings -> Pieces -> W.Request -> Folder -> IO StaticResponse
77−serveFolder ss@StaticSettings {..} pieces req folder@Folder {..} =
78− -- first check if there is an index file in this folder
79− case getFirst $ mconcat $ map (findIndex $ rights folderContents) ssIndices of
80− Just index ->
81− let pieces' = setLast pieces index in
82− case () of
83− () | ssRedirectToIndex -> return $ Redirect pieces' Nothing
84− | Just path <- addTrailingSlash req, ssAddTrailingSlash ->
85− return $ RawRedirect path
86− | otherwise ->
87− -- start the checking process over, with a new set
88− checkPieces ss pieces' req
89− Nothing ->
90− case ssListing of
91− Just _ | Just path <- addTrailingSlash req, ssAddTrailingSlash ->
92− return $ RawRedirect path
93− Just listing -> do
94− -- directory listings turned on, display it
95− builder <- listing pieces folder
96− return $ WaiResponse $ W.responseBuilder H.status200
97− [ ("Content-Type", "text/html; charset=utf-8")
98− ] builder
99− Nothing -> return $ WaiResponse $ W.responseLBS H.status403
100− [ ("Content-Type", "text/plain")
101− ] "Directory listings disabled"
75+serveFolder StaticSettings {..} pieces req folder@Folder {..} =
76+ case ssListing of
77+ Just _ | Just path <- addTrailingSlash req, ssAddTrailingSlash ->
78+ return $ RawRedirect path
79+ Just listing -> do
80+ -- directory listings turned on, display it
81+ builder <- listing pieces folder
82+ return $ WaiResponse $ W.responseBuilder H.status200
83+ [ ("Content-Type", "text/html; charset=utf-8")
84+ ] builder
85+ Nothing -> return $ WaiResponse $ W.responseLBS H.status403
86+ [ ("Content-Type", "text/plain")
87+ ] "Directory listings disabled"
10288 where
103− setLast :: Pieces -> Piece -> Pieces
104− setLast [] x = [x]
105− setLast [t] x
106− | fromPiece t == "" = [x]
107− setLast (a:b) x = a : setLast b x
108−
10989 addTrailingSlash :: W.Request -> Maybe ByteString
11090 addTrailingSlash req
11191 | S8.null rp = Just "/"
@@ -114,16 +94,6 @@ serveFolder ss@StaticSettings {..} pieces req folder@Folder {..} =
11494 where
11595 rp = W.rawPathInfo req
11696
117− noTrailingSlash :: Pieces -> Bool
118− noTrailingSlash [] = False
119− noTrailingSlash [x] = fromPiece x /= ""
120− noTrailingSlash (_:xs) = noTrailingSlash xs
121−
122− findIndex :: [File] -> Piece -> First Piece
123− findIndex files index
124− | index `elem` map fileName files = First $ Just index
125− | otherwise = First Nothing
126−
12797checkPieces :: StaticSettings
12898 -> Pieces -- ^ parsed request
12999 -> W.Request
@@ -134,11 +104,30 @@ checkPieces _ pieces _ | any (T.null . fromPiece) $ safeInit pieces =
134104 return $ Redirect (filterButLast (not . T.null . fromPiece) pieces) Nothing
135105
136106checkPieces ss@StaticSettings {..} pieces req = do
137− res <- ssLookupFile pieces
107+ res <- lookupResult
138108 case res of
139109 LRNotFound -> return NotFound
140110 LRFile file -> serveFile ss req file
141111 LRFolder folder -> serveFolder ss pieces req folder
112+ where
113+ lookupResult :: IO LookupResult
114+ lookupResult = do
115+ nonIndexResult <- ssLookupFile pieces
116+ case nonIndexResult of
117+ LRFile{} -> return nonIndexResult
118+ _ -> do
119+ indexResult <- lookupIndices (map (\ index -> pieces ++ [index]) ssIndices)
120+ return $ case indexResult of
121+ LRNotFound -> nonIndexResult
122+ _ -> indexResult
123+
124+ lookupIndices :: [Pieces] -> IO LookupResult
125+ lookupIndices (x : xs) = do
126+ res <- ssLookupFile x
127+ case res of
128+ LRNotFound -> lookupIndices xs
129+ _ -> return res
130+ lookupIndices [] = return LRNotFound
142131
143132serveFile :: StaticSettings -> W.Request -> File -> IO StaticResponse
144133serveFile StaticSettings {..} req file
wai-app-static/test/EmbeddedTestEntries.hs+5
@@ -42,4 +42,9 @@ mkEntries = return
4242 , EmbeddableEntry "e6.txt"
4343 "text/plain"
4444 (Right [| return ("" :: T.Text, body 1000 'W') |] )
45+
46+ -- An index file
47+ , EmbeddableEntry "index.html"
48+ "text/html"
49+ (Right [| return ("" :: T.Text, "index file") |] )
4550 ]
wai-app-static/test/WaiAppEmbeddedTest.hs+12−5
@@ -8,16 +8,15 @@ import Network.Wai.Application.Static (staticApp)
88import Network.Wai.Test
99import Test.Hspec
1010import WaiAppStatic.Storage.Embedded
11−
12−embApp :: Application
13−embApp = staticApp $(mkSettings mkEntries)
11+import WaiAppStatic.Types
1412
1513defRequest :: Request
1614defRequest = defaultRequest
1715
1816embSpec :: Spec
1917embSpec = do
20− let embed = flip runSession embApp
18+ let embedSettings settings = flip runSession (staticApp settings)
19+ let embed = embedSettings $(mkSettings mkEntries)
2120 describe "embedded, compressed entry" $ do
2221 it "served correctly" $ embed $ do
2322 req <- request (setRawPathInfo defRequest "e1.txt")
@@ -33,6 +32,15 @@ embSpec = do
3332 { requestHeaders = [("If-None-Match", "Etag 1")] }
3433 assertStatus 304 req
3534
35+ it "ssIndices works" $ do
36+ let testSettings = $(mkSettings mkEntries){
37+ ssIndices = [unsafeToPiece "index.html"]
38+ }
39+ embedSettings testSettings $ do
40+ req <- request defRequest
41+ assertStatus 200 req
42+ assertBody "index file" req
43+
3644 describe "embedded, uncompressed entry" $ do
3745 it "too short" $ embed $ do
3846 req <- request (setRawPathInfo defRequest "e2.txt")
@@ -75,4 +83,3 @@ embSpec = do
7583 assertNoHeader "Content-Encoding" req
7684 assertNoHeader "ETag" req
7785 assertBody (body 1000 'W') req
78−
wai-app-static/test/WaiAppStaticTest.hs+27−1
@@ -2,11 +2,14 @@
22module WaiAppStaticTest (spec) where
33
44import Network.Wai.Application.Static
5+import WaiAppStatic.Types
56
67import Test.Hspec
78import qualified Data.ByteString.Char8 as S8
89-- import qualified Data.ByteString.Lazy.Char8 as L8
910import System.PosixCompat.Files (getFileStatus, modificationTime)
11+import System.FilePath
12+import System.IO.Temp
1013
1114import Network.HTTP.Date
1215import Network.HTTP.Types (status500)
@@ -25,7 +28,8 @@ defRequest = defaultRequest
2528spec :: Spec
2629spec = do
2730 let webApp = flip runSession $ staticApp $ defaultWebAppSettings "test"
28− let fileServerApp = flip runSession $ staticApp (defaultFileServerSettings "test")
31+ let fileServerAppWithSettings settings = flip runSession $ staticApp settings
32+ let fileServerApp = fileServerAppWithSettings (defaultFileServerSettings "test")
2933 { ssAddTrailingSlash = True
3034 }
3135
@@ -145,3 +149,25 @@ spec = do
145149 req <- request (setRawPathInfo defRequest "/subPath")
146150 assertStatus 301 req
147151 assertHeader "Location" "/subPath/" req
152+
153+ context "with defaultWebAppSettings" $ do
154+ it "ssIndices works" $ do
155+ withSystemTempDirectory "wai-app-static-test" $ \ dir -> do
156+ writeFile (dir </> "index.html") "foo"
157+ let testSettings = (defaultWebAppSettings dir) {
158+ ssIndices = [unsafeToPiece "index.html"]
159+ }
160+ fileServerAppWithSettings testSettings $ do
161+ resp <- request (setRawPathInfo defRequest "/")
162+ assertStatus 200 resp
163+ assertBody "foo" resp
164+
165+ context "with defaultFileServerSettings" $ do
166+ it "prefers ssIndices over ssListing" $ do
167+ withSystemTempDirectory "wai-app-static-test" $ \ dir -> do
168+ writeFile (dir </> "index.html") "foo"
169+ let testSettings = defaultFileServerSettings dir
170+ fileServerAppWithSettings testSettings $ do
171+ resp <- request (setRawPathInfo defRequest "/")
172+ assertStatus 200 resp
173+ assertBody "foo" resp
wai-app-static/wai-app-static.cabal+2
@@ -102,6 +102,8 @@ test-suite runtests
102102 , transformers
103103 , mime-types
104104 , zlib
105+ , filepath
106+ , temporary
105107 -- , containers
106108 ghc-options: -Wall
107109