{-# LANGUAGE DeriveDataTypeable, RecordWildCards #-} -- | Command line version of wai-app-static, used for the warp-static server. module WaiAppStatic.CmdLine ( runCommandLine , Args (..) ) where import Network.Wai (Middleware) import Network.Wai.Application.Static (staticApp, defaultFileServerSettings) import Network.Wai.Handler.Warp ( runSettings, defaultSettings, setHost, setPort ) import Options.Applicative import Text.Printf (printf) import System.Directory (canonicalizePath) import Control.Monad (unless) import Network.Wai.Middleware.RequestLogger (logStdout) import Network.Wai.Middleware.Gzip import qualified Data.Map as Map import qualified Data.ByteString.Char8 as S8 import Control.Arrow ((***)) import Data.Text (pack) import Data.String (fromString) import Network.Mime (defaultMimeMap, mimeByExt, defaultMimeType) import WaiAppStatic.Types (ssIndices, toPiece, ssGetMimeType, fileName, fromPiece) import Data.Maybe (mapMaybe) import Control.Arrow (second) data Args = Args { docroot :: FilePath , index :: [FilePath] , port :: Int , noindex :: Bool , quiet :: Bool , verbose :: Bool , mime :: [(String, String)] , host :: String } #if MIN_VERSION_optparse_applicative(0, 10, 0) option' = option auto #else option' = option #endif args :: Parser Args args = Args <$> strOption ( long "docroot" <> short 'd' <> metavar "DOCROOT" <> value "." <> help "directory containing files to serve") <*> (defIndex <$> many (strOption ( long "index" <> short 'i' <> metavar "INDEX" <> help "index files to serve when a directory is required" ))) <*> option' ( long "port" <> short 'p' <> metavar "PORT" <> value 3000) <*> switch ( long "noindex" <> short 'n') <*> switch ( long "quiet" <> short 'q') <*> switch ( long "verbose" <> short 'v') <*> many (toPair <$> strOption ( long "mime" <> short 'm' <> metavar "MIME" <> help "extra file extension/mime type mappings")) <*> strOption ( long "host" <> short 'h' <> metavar "HOST" <> value "*" <> help "interface to bind to, special values: *, *4, *6") where toPair = second (drop 1) . break (== '=') defIndex [] = ["index.html", "index.htm"] defIndex x = x -- | Run with the given middleware and parsing options from the command line. -- -- Since 2.0.1 runCommandLine :: (Args -> Middleware) -> IO () runCommandLine middleware = do args@Args {..} <- execParser $ info (helper <*> args) fullDesc let mime' = map (pack *** S8.pack) mime let mimeMap = Map.fromList mime' `Map.union` defaultMimeMap docroot' <- canonicalizePath docroot unless quiet $ printf "Serving directory %s on port %d with %s index files.\n" docroot' port (if noindex then "no" else show index) let middle = gzip def { gzipFiles = GzipCompress } . (if verbose then logStdout else id) . (middleware args) runSettings ( setPort port $ setHost (fromString host) defaultSettings ) $ middle $ staticApp (defaultFileServerSettings $ fromString docroot) { ssIndices = if noindex then [] else mapMaybe (toPiece . pack) index , ssGetMimeType = return . mimeByExt mimeMap defaultMimeType . fromPiece . fileName }