From 8efabed5c6abd06c9d6c98ad206e66f217ffc125 Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Mon, 9 Jan 2017 00:37:37 +0100 Subject: [PATCH 1/5] Revamp static file serving module. Instead of only exposing 'serveDirectory', which picks a specific static file serving strategy (file server settings), we now expose 4 different variants each corresponding to an variant of StaticSettings in wai-app-static. In addition to these, we expose a more flexible 'serveDirectoryWith' function which allows the user to specify some arbitrary StaticSettings, if the 4 existing variants do not cover a user's needs. --- doc/tutorial/Javascript.lhs | 2 +- doc/tutorial/Server.lhs | 10 +++- .../src/Servant/Utils/StaticFiles.hs | 60 ++++++++++++++----- 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/doc/tutorial/Javascript.lhs b/doc/tutorial/Javascript.lhs index 2d9ab6bd..07691372 100644 --- a/doc/tutorial/Javascript.lhs +++ b/doc/tutorial/Javascript.lhs @@ -136,7 +136,7 @@ server = randomPoint server' :: Server API' server' = server - :<|> serveDirectory "static" + :<|> serveDirectoryWebApp "static" app :: Application app = serve api' server' diff --git a/doc/tutorial/Server.lhs b/doc/tutorial/Server.lhs index b4014263..cec9d059 100644 --- a/doc/tutorial/Server.lhs +++ b/doc/tutorial/Server.lhs @@ -787,10 +787,10 @@ directory serving WAI application, namely: ``` haskell ignore -- exported by Servant and Servant.Server -serveDirectory :: FilePath -> Server Raw +serveDirectoryWebApp :: FilePath -> Server Raw ``` -`serveDirectory`'s argument must be a path to a valid directory. +`serveDirectoryWebApp`'s argument must be a path to a valid directory. Here's an example API that will serve some static files: @@ -807,7 +807,7 @@ staticAPI = Proxy ``` haskell server7 :: Server StaticAPI -server7 = serveDirectory "static-files" +server7 = serveDirectoryWebApp "static-files" app3 :: Application app3 = serve staticAPI server7 @@ -821,6 +821,10 @@ In other words: If a client requests `/static/foo.txt`, the server will look for `./static-files/foo.txt`. If that file exists it'll succeed and serve the file. If it doesn't exist, the handler will fail with a `404` status code. +`serveDirectoryWebApp` uses some standard settings that fit the use case of +serving static files for most web apps. You can find out about the other +options in the documentation of the `Servant.Utils.StaticFiles` module. + ## Nested APIs Let's see how you can define APIs in a modular way, while avoiding repetition. diff --git a/servant-server/src/Servant/Utils/StaticFiles.hs b/servant-server/src/Servant/Utils/StaticFiles.hs index 08d01ada..30485c74 100644 --- a/servant-server/src/Servant/Utils/StaticFiles.hs +++ b/servant-server/src/Servant/Utils/StaticFiles.hs @@ -1,20 +1,27 @@ {-# LANGUAGE CPP #-} --- | This module defines a sever-side handler that lets you serve static files. +-- | This module defines server-side handlers that lets you serve static files. -- --- - 'serveDirectory' lets you serve anything that lives under a particular --- directory on your filesystem. -module Servant.Utils.StaticFiles ( - serveDirectory, - ) where +-- The most common needs for a web application are covered by +-- 'serveDirectoryWebApp`, but the other variants allow you to use +-- different `StaticSettings` and 'serveDirectoryWith' even allows you +-- to specify arbitrary 'StaticSettings' to be used for serving static files. +module Servant.Utils.StaticFiles + ( serveDirectoryWebApp + , serveDirectoryWebAppLookup + , serveDirectoryFileServer + , serveDirectoryEmbedded + , serveDirectoryWith + ) where -import Network.Wai.Application.Static (defaultFileServerSettings, - staticApp) +import Data.ByteString (ByteString) +import Network.Wai.Application.Static import Servant.API.Raw (Raw) import Servant.Server (Server) import System.FilePath (addTrailingPathSeparator) #if !MIN_VERSION_wai_app_static(3,1,0) import Filesystem.Path.CurrentOS (decodeString) #endif +import WaiAppStatic.Storage.Filesystem (ETagLookup) -- | Serve anything under the specified directory as a 'Raw' endpoint. -- @@ -22,7 +29,7 @@ import Filesystem.Path.CurrentOS (decodeString) -- type MyApi = "static" :> Raw -- -- server :: Server MyApi --- server = serveDirectory "\/var\/www" +-- server = serveDirectoryWebApp "\/var\/www" -- @ -- -- would capture any request to @\/static\/\@ and look for @@ -33,13 +40,38 @@ import Filesystem.Path.CurrentOS (decodeString) -- -- If your goal is to serve HTML, CSS and Javascript files that use the rest of the API -- as a webapp backend, you will most likely not want the static files to be hidden --- behind a /\/static\// prefix. In that case, remember to put the 'serveDirectory' +-- behind a /\/static\// prefix. In that case, remember to put the 'serveDirectoryWebApp' -- handler in the last position, because /servant/ will try to match the handlers -- in order. -serveDirectory :: FilePath -> Server Raw -serveDirectory = +-- +-- Corresponds to the `defaultWebAppSettings` `StaticSettings` value. +serveDirectoryWebApp :: FilePath -> Server Raw +serveDirectoryWebApp = staticApp . defaultWebAppSettings . fixPath + +-- | Same as 'serveDirectoryWebApp', but uses `defaultFileServerSettings`. +serveDirectoryFileServer :: FilePath -> Server Raw +serveDirectoryFileServer = staticApp . defaultFileServerSettings . fixPath + +-- | Same as 'serveDirectoryWebApp', but uses 'webAppSettingsWithLookup'. +serveDirectoryWebAppLookup :: ETagLookup -> FilePath -> Server Raw +serveDirectoryWebAppLookup etag = + staticApp . flip webAppSettingsWithLookup etag . fixPath + +-- | Uses 'embeddedSettings'. +serveDirectoryEmbedded :: [(FilePath, ByteString)] -> Server Raw +serveDirectoryEmbedded files = staticApp (embeddedSettings files) + +-- | Alias for 'staticApp'. Lets you serve a directory +-- with arbitrary 'StaticSettings'. Useful when you want +-- particular settings not covered by the four other +-- variants. This is the most flexible method. +serveDirectoryWith :: StaticSettings -> Server Raw +serveDirectoryWith = staticApp + +fixPath :: FilePath -> FilePath +fixPath = #if MIN_VERSION_wai_app_static(3,1,0) - staticApp . defaultFileServerSettings . addTrailingPathSeparator + addTrailingPathSeparator #else - staticApp . defaultFileServerSettings . decodeString . addTrailingPathSeparator + decodeString . addTrailingPathSeparator #endif From 0a3b2272debe4e6c09c5d5eb5a7c3937ee275c19 Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Mon, 9 Jan 2017 01:02:32 +0100 Subject: [PATCH 2/5] fix static file serving test --- servant-server/src/Servant/Utils/StaticFiles.hs | 2 ++ servant-server/test/Servant/Utils/StaticFilesSpec.hs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/servant-server/src/Servant/Utils/StaticFiles.hs b/servant-server/src/Servant/Utils/StaticFiles.hs index 30485c74..ec1630e6 100644 --- a/servant-server/src/Servant/Utils/StaticFiles.hs +++ b/servant-server/src/Servant/Utils/StaticFiles.hs @@ -49,6 +49,8 @@ serveDirectoryWebApp :: FilePath -> Server Raw serveDirectoryWebApp = staticApp . defaultWebAppSettings . fixPath -- | Same as 'serveDirectoryWebApp', but uses `defaultFileServerSettings`. +-- +-- This used to be called 'serveDirectory' in @servant < 0.10@. serveDirectoryFileServer :: FilePath -> Server Raw serveDirectoryFileServer = staticApp . defaultFileServerSettings . fixPath diff --git a/servant-server/test/Servant/Utils/StaticFilesSpec.hs b/servant-server/test/Servant/Utils/StaticFilesSpec.hs index 94c63f18..b3c43d31 100644 --- a/servant-server/test/Servant/Utils/StaticFilesSpec.hs +++ b/servant-server/test/Servant/Utils/StaticFilesSpec.hs @@ -18,7 +18,7 @@ import Test.Hspec.Wai (get, shouldRespondWith, with) import Servant.API ((:<|>) ((:<|>)), Capture, Get, Raw, (:>), JSON) import Servant.Server (Server, serve) import Servant.ServerSpec (Person (Person)) -import Servant.Utils.StaticFiles (serveDirectory) +import Servant.Utils.StaticFiles (serveDirectoryFileServer) type Api = "dummy_api" :> Capture "person_name" String :> Get '[JSON] Person @@ -34,7 +34,7 @@ app = serve api server server :: Server Api server = (\ name_ -> return (Person name_ 42)) - :<|> serveDirectory "static" + :<|> serveDirectoryFileServer "static" withStaticFiles :: IO () -> IO () withStaticFiles action = withSystemTempDirectory "servant-test" $ \ tmpDir -> From c655c6e4746c7a1317a03423f9076c1637fec398 Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Mon, 9 Jan 2017 01:20:57 +0100 Subject: [PATCH 3/5] tutorial fix --- doc/tutorial/Javascript.lhs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial/Javascript.lhs b/doc/tutorial/Javascript.lhs index 07691372..6a7aa6bb 100644 --- a/doc/tutorial/Javascript.lhs +++ b/doc/tutorial/Javascript.lhs @@ -136,7 +136,7 @@ server = randomPoint server' :: Server API' server' = server - :<|> serveDirectoryWebApp "static" + :<|> serveDirectoryFileServer "static" app :: Application app = serve api' server' From 6d35f3d88f0c43d3ca426e7493e7d5af69df66b9 Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Mon, 9 Jan 2017 11:02:02 +0100 Subject: [PATCH 4/5] reintroduce serveDirectory (to give some time to users to change their code), but deprecate it --- servant-server/src/Servant/Utils/StaticFiles.hs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/servant-server/src/Servant/Utils/StaticFiles.hs b/servant-server/src/Servant/Utils/StaticFiles.hs index ec1630e6..4db1ed6b 100644 --- a/servant-server/src/Servant/Utils/StaticFiles.hs +++ b/servant-server/src/Servant/Utils/StaticFiles.hs @@ -11,6 +11,8 @@ module Servant.Utils.StaticFiles , serveDirectoryFileServer , serveDirectoryEmbedded , serveDirectoryWith + , -- * Deprecated + serveDirectory ) where import Data.ByteString (ByteString) @@ -49,8 +51,6 @@ serveDirectoryWebApp :: FilePath -> Server Raw serveDirectoryWebApp = staticApp . defaultWebAppSettings . fixPath -- | Same as 'serveDirectoryWebApp', but uses `defaultFileServerSettings`. --- --- This used to be called 'serveDirectory' in @servant < 0.10@. serveDirectoryFileServer :: FilePath -> Server Raw serveDirectoryFileServer = staticApp . defaultFileServerSettings . fixPath @@ -70,6 +70,13 @@ serveDirectoryEmbedded files = staticApp (embeddedSettings files) serveDirectoryWith :: StaticSettings -> Server Raw serveDirectoryWith = staticApp +-- | Same as 'serveDirectoryFileServer'. It used to be the only +-- file serving function in servant pre-0.10 and will be kept +-- around for a few versions, but is deprecated. +serveDirectory :: FilePath -> Server Raw +serveDirectory = serveDirectoryFileServer +{-# DEPRECATED serveDirectory "Use serveDirectoryFileServer instead" #-} + fixPath :: FilePath -> FilePath fixPath = #if MIN_VERSION_wai_app_static(3,1,0) From 57445ac1c9da0704f29160b381b9a45f14e34135 Mon Sep 17 00:00:00 2001 From: Alp Mestanogullari Date: Mon, 9 Jan 2017 11:04:08 +0100 Subject: [PATCH 5/5] changelog entry --- servant-server/CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/servant-server/CHANGELOG.md b/servant-server/CHANGELOG.md index 411b4af4..a3815bdb 100644 --- a/servant-server/CHANGELOG.md +++ b/servant-server/CHANGELOG.md @@ -4,9 +4,14 @@ * Add `err422` Unprocessable Entity ([#646](https://github.com/haskell-servant/servant/pull/646)) -* `Handler` is not abstract datatype. Migration hint: change `throwE` to `throwError`. +* `Handler` is now an abstract datatype. Migration hint: change `throwE` to `throwError`. ([#641](https://github.com/haskell-servant/servant/issues/641)) +* Deprecate `serveDirectory` and introduce `serveDirectoryFileServer`, + `serveDirectoryWebApp`, `serveDirectoryWebAppLookup`, `serveDirectoryEmbedded` + and `serveDirectoryWith` which offer 4 default options and a more flexible + one for serving static files. + 0.7.1 ------