diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index d253f505..4e4ab500 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -54,6 +54,10 @@ library Servant.Server.Internal.Router Servant.Server.Internal.RoutingApplication Servant.Server.Internal.ServantErr + Servant.Server.StaticFiles + + -- deprecated + exposed-modules: Servant.Utils.StaticFiles -- Bundled with GHC: Lower bound to not force re-installs @@ -133,12 +137,12 @@ test-suite spec Servant.Server.Internal.ContextSpec Servant.Server.Internal.RoutingApplicationSpec Servant.Server.RouterSpec + Servant.Server.StaticFilesSpec Servant.Server.StreamingSpec Servant.Server.UsingContextSpec Servant.Server.UsingContextSpec.TestCombinators Servant.HoistSpec Servant.ServerSpec - Servant.Utils.StaticFilesSpec -- Dependencies inherited from the library. No need to specify bounds. build-depends: diff --git a/servant-server/src/Servant/Server/StaticFiles.hs b/servant-server/src/Servant/Server/StaticFiles.hs new file mode 100644 index 00000000..588f792d --- /dev/null +++ b/servant-server/src/Servant/Server/StaticFiles.hs @@ -0,0 +1,92 @@ +{-# LANGUAGE CPP #-} +-- | This module defines server-side handlers that lets you serve static files. +-- +-- 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.Server.StaticFiles + ( serveDirectoryWebApp + , serveDirectoryWebAppLookup + , serveDirectoryFileServer + , serveDirectoryEmbedded + , serveDirectoryWith + , -- * Deprecated + serveDirectory + ) where + +import Data.ByteString + (ByteString) +import Network.Wai.Application.Static +import Servant.API.Raw + (Raw) +import Servant.Server + (ServerT, Tagged (..)) +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. +-- +-- @ +-- type MyApi = "static" :> Raw +-- +-- server :: Server MyApi +-- server = serveDirectoryWebApp "\/var\/www" +-- @ +-- +-- would capture any request to @\/static\/\@ and look for +-- @\@ under @\/var\/www@. +-- +-- It will do its best to guess the MIME type for that file, based on the extension, +-- and send an appropriate /Content-Type/ header if possible. +-- +-- 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 'serveDirectoryWebApp' +-- handler in the last position, because /servant/ will try to match the handlers +-- in order. +-- +-- Corresponds to the `defaultWebAppSettings` `StaticSettings` value. +serveDirectoryWebApp :: FilePath -> ServerT Raw m +serveDirectoryWebApp = serveDirectoryWith . defaultWebAppSettings . fixPath + +-- | Same as 'serveDirectoryWebApp', but uses `defaultFileServerSettings`. +serveDirectoryFileServer :: FilePath -> ServerT Raw m +serveDirectoryFileServer = serveDirectoryWith . defaultFileServerSettings . fixPath + +-- | Same as 'serveDirectoryWebApp', but uses 'webAppSettingsWithLookup'. +serveDirectoryWebAppLookup :: ETagLookup -> FilePath -> ServerT Raw m +serveDirectoryWebAppLookup etag = + serveDirectoryWith . flip webAppSettingsWithLookup etag . fixPath + +-- | Uses 'embeddedSettings'. +serveDirectoryEmbedded :: [(FilePath, ByteString)] -> ServerT Raw m +serveDirectoryEmbedded files = serveDirectoryWith (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 -> ServerT Raw m +serveDirectoryWith = Tagged . 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 -> ServerT Raw m +serveDirectory = serveDirectoryFileServer +{-# DEPRECATED serveDirectory "Use serveDirectoryFileServer instead" #-} + +fixPath :: FilePath -> FilePath +fixPath = +#if MIN_VERSION_wai_app_static(3,1,0) + addTrailingPathSeparator +#else + decodeString . addTrailingPathSeparator +#endif diff --git a/servant-server/src/Servant/Utils/StaticFiles.hs b/servant-server/src/Servant/Utils/StaticFiles.hs index 3e12c9c5..a51728af 100644 --- a/servant-server/src/Servant/Utils/StaticFiles.hs +++ b/servant-server/src/Servant/Utils/StaticFiles.hs @@ -1,86 +1,6 @@ -{-# LANGUAGE CPP #-} --- | This module defines server-side handlers that lets you serve static files. --- --- 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 - , -- * Deprecated - serveDirectory - ) where + {-# DEPRECATED "Use Servant.ServerStaticFiles." #-} + ( module Servant.Server.StaticFiles ) + where -import Data.ByteString (ByteString) -import Network.Wai.Application.Static -import Servant.API.Raw (Raw) -import Servant.Server (ServerT, Tagged (..)) -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. --- --- @ --- type MyApi = "static" :> Raw --- --- server :: Server MyApi --- server = serveDirectoryWebApp "\/var\/www" --- @ --- --- would capture any request to @\/static\/\@ and look for --- @\@ under @\/var\/www@. --- --- It will do its best to guess the MIME type for that file, based on the extension, --- and send an appropriate /Content-Type/ header if possible. --- --- 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 'serveDirectoryWebApp' --- handler in the last position, because /servant/ will try to match the handlers --- in order. --- --- Corresponds to the `defaultWebAppSettings` `StaticSettings` value. -serveDirectoryWebApp :: FilePath -> ServerT Raw m -serveDirectoryWebApp = serveDirectoryWith . defaultWebAppSettings . fixPath - --- | Same as 'serveDirectoryWebApp', but uses `defaultFileServerSettings`. -serveDirectoryFileServer :: FilePath -> ServerT Raw m -serveDirectoryFileServer = serveDirectoryWith . defaultFileServerSettings . fixPath - --- | Same as 'serveDirectoryWebApp', but uses 'webAppSettingsWithLookup'. -serveDirectoryWebAppLookup :: ETagLookup -> FilePath -> ServerT Raw m -serveDirectoryWebAppLookup etag = - serveDirectoryWith . flip webAppSettingsWithLookup etag . fixPath - --- | Uses 'embeddedSettings'. -serveDirectoryEmbedded :: [(FilePath, ByteString)] -> ServerT Raw m -serveDirectoryEmbedded files = serveDirectoryWith (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 -> ServerT Raw m -serveDirectoryWith = Tagged . 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 -> ServerT Raw m -serveDirectory = serveDirectoryFileServer -{-# DEPRECATED serveDirectory "Use serveDirectoryFileServer instead" #-} - -fixPath :: FilePath -> FilePath -fixPath = -#if MIN_VERSION_wai_app_static(3,1,0) - addTrailingPathSeparator -#else - decodeString . addTrailingPathSeparator -#endif +import Servant.Server.StaticFiles diff --git a/servant-server/test/Servant/Utils/StaticFilesSpec.hs b/servant-server/test/Servant/Server/StaticFilesSpec.hs similarity index 58% rename from servant-server/test/Servant/Utils/StaticFilesSpec.hs rename to servant-server/test/Servant/Server/StaticFilesSpec.hs index b3c43d31..1f7b31ba 100644 --- a/servant-server/test/Servant/Utils/StaticFilesSpec.hs +++ b/servant-server/test/Servant/Server/StaticFilesSpec.hs @@ -3,22 +3,31 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeOperators #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -module Servant.Utils.StaticFilesSpec where +module Servant.Server.StaticFilesSpec where -import Control.Exception (bracket) -import Data.Proxy (Proxy (Proxy)) -import Network.Wai (Application) -import System.Directory (createDirectory, - getCurrentDirectory, - setCurrentDirectory) -import System.IO.Temp (withSystemTempDirectory) -import Test.Hspec (Spec, around_, describe, it) -import Test.Hspec.Wai (get, shouldRespondWith, with) +import Control.Exception + (bracket) +import Data.Proxy + (Proxy (Proxy)) +import Network.Wai + (Application) +import System.Directory + (createDirectory, getCurrentDirectory, setCurrentDirectory) +import System.IO.Temp + (withSystemTempDirectory) +import Test.Hspec + (Spec, around_, describe, it) +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 (serveDirectoryFileServer) +import Servant.API + ((:<|>) ((:<|>)), (:>), Capture, Get, JSON, Raw) +import Servant.Server + (Server, serve) +import Servant.Server.StaticFiles + (serveDirectoryFileServer) +import Servant.ServerSpec + (Person (Person)) type Api = "dummy_api" :> Capture "person_name" String :> Get '[JSON] Person