1module WaiAppStatic.Types
2    ( -- * Pieces
3      Piece
4    , toPiece
5    , fromPiece
6    , unsafeToPiece
7    , Pieces
8    , toPieces
9      -- * Caching
10    , MaxAge (..)
11      -- * File\/folder serving
12    , FolderName
13    , Folder (..)
14    , File (..)
15    , LookupResult (..)
16    , Listing
17      -- * Settings
18    , StaticSettings (..)
19    ) where
20
21import Data.Text (Text)
22import qualified Network.HTTP.Types as H
23import qualified Network.Wai as W
24import Data.ByteString (ByteString)
25import System.Posix.Types (EpochTime)
26import qualified Data.Text as T
27import Data.ByteString.Builder (Builder)
28import Network.Mime (MimeType)
29
30-- | An individual component of a path, or of a filepath.
31--
32-- This is the core type used by wai-app-static for doing lookups. It provides
33-- a smart constructor to avoid the possibility of constructing unsafe path
34-- segments (though @unsafeToPiece@ can get around that as necessary).
35--
36-- Individual file lookup backends must know how to convert from a @Piece@ to
37-- their storage system.
38newtype Piece = Piece { fromPiece :: Text }
39    deriving (Show, Eq, Ord)
40
41-- | Smart constructor for a @Piece@. Won\'t allow unsafe components, such as
42-- pieces beginning with a period or containing a slash. This /will/, however,
43-- allow null pieces.
44toPiece :: Text -> Maybe Piece
45toPiece t
46    | T.null t = Just $ Piece t
47    | T.head t == '.' = Nothing
48    | T.any (== '/') t = Nothing
49    | otherwise = Just $ Piece t
50
51-- | Construct a @Piece@ without input validation.
52unsafeToPiece :: Text -> Piece
53unsafeToPiece = Piece
54
55-- | Call @toPiece@ on a list.
56--
57-- > toPieces = mapM toPiece
58toPieces :: [Text] -> Maybe Pieces
59toPieces = mapM toPiece
60
61-- | Request coming from a user. Corresponds to @pathInfo@.
62--
63-- The root path is the empty list.
64type Pieces = [Piece]
65
66-- | Values for the max-age component of the cache-control response header.
67data MaxAge = NoMaxAge -- ^ no cache-control set
68            | MaxAgeSeconds Int -- ^ set to the given number of seconds
69            | MaxAgeForever -- ^ essentially infinite caching; in reality, probably one year
70
71-- | Just the name of a folder.
72type FolderName = Piece
73
74-- | Represent contents of a single folder, which can be itself either a file
75-- or a folder.
76data Folder = Folder
77    { folderContents :: [Either FolderName File]
78    }
79
80-- | Information on an individual file.
81data File = File
82    { -- | Size of file in bytes
83      fileGetSize :: Integer
84      -- | How to construct a WAI response for this file. Some files are stored
85      -- on the filesystem and can use @ResponseFile@, while others are stored
86      -- in memory and should use @ResponseBuilder@.
87    , fileToResponse :: H.Status -> H.ResponseHeaders -> W.Response
88      -- | Last component of the filename.
89    , fileName :: Piece
90      -- | Calculate a hash of the contents of this file, such as for etag.
91    , fileGetHash :: IO (Maybe ByteString)
92      -- | Last modified time, used for both display in listings and if-modified-since.
93    , fileGetModified :: Maybe EpochTime
94    }
95
96-- | Result of looking up a file in some storage backend.
97--
98-- The lookup is either a file or folder, or does not exist.
99data LookupResult = LRFile File
100                  | LRFolder Folder
101                  | LRNotFound
102
103-- | How to construct a directory listing page for the given request path and
104-- the resulting folder.
105type Listing = Pieces -> Folder -> IO Builder
106
107-- | All of the settings available to users for tweaking wai-app-static.
108--
109-- Note that you should use the settings type approach for modifying values.
110-- See <http://www.yesodweb.com/book/settings-types> for more information.
111data StaticSettings = StaticSettings
112    {
113      -- | Lookup a single file or folder. This is how you can control storage
114      -- backend (filesystem, embedded, etc) and where to lookup.
115      ssLookupFile :: Pieces -> IO LookupResult
116
117      -- | Determine the mime type of the given file. Note that this function
118      -- lives in @IO@ in case you want to perform more complicated mimetype
119      -- analysis, such as via the @file@ utility.
120    , ssGetMimeType :: File -> IO MimeType
121
122      -- | Ordered list of filenames to be used for indices. If the user
123      -- requests a folder, and a file with the given name is found in that
124      -- folder, that file is served. This supercedes any directory listing.
125    , ssIndices :: [Piece]
126
127      -- | How to perform a directory listing. Optional. Will be used when the
128      -- user requested a folder.
129    , ssListing :: Maybe Listing
130
131      -- | Value to provide for max age in the cache-control.
132    , ssMaxAge :: MaxAge
133
134      -- | Given a requested path and a new destination, construct a string
135      -- that will go there. Default implementation will use relative paths.
136    , ssMkRedirect :: Pieces -> ByteString -> ByteString
137
138      -- | If @True@, send a redirect to the user when a folder is requested
139      -- and an index page should be displayed. When @False@, display the
140      -- content immediately.
141    , ssRedirectToIndex :: Bool
142
143      -- | Prefer usage of etag caching to last-modified caching.
144    , ssUseHash :: Bool
145
146      -- | Force a trailing slash at the end of directories
147    , ssAddTrailingSlash :: Bool
148
149      -- | Optional `W.Application` to be used in case of 404 errors
150      --
151      -- Since 3.1.3
152    , ss404Handler :: Maybe W.Application
153    }
154