happstack-server-7.1.7.1: Web related tools and services.

Safe Haskell | None

Happstack.Server.FileServe.BuildingBlocks

Contents

- High-Level
  - Serving files from a directory
  - Serving a single file
- Low-Level
- Content-Type / Mime-Type
- Directory Browsing
- Other

Description

Build your own file serving functions

If the functions in Happstack.Server.FileServe do not quite do you want you can roll your own by reusing pieces from this module.

You will likely want to start by copying the source for a function like, `serveDirectory` and then modifying it to suit your needs.

Synopsis

- fileServe :: (WebMonad Response m, ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) => [FilePath] -> FilePath -> m Response
- fileServe' :: (WebMonad Response m, ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) => (String -> FilePath -> m Response) -> (FilePath -> m String) -> (FilePath -> m Response) -> FilePath -> m Response
- fileServeLazy :: (WebMonad Response m, ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) => [FilePath] -> FilePath -> m Response
- fileServeStrict :: (WebMonad Response m, ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) => [FilePath] -> FilePath -> m Response
- data Browsing
  - = EnableBrowsing
  - | DisableBrowsing
- serveDirectory :: (WebMonad Response m, ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) => Browsing -> [FilePath] -> FilePath -> m Response
- serveDirectory' :: (WebMonad Response m, ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) => Browsing -> [FilePath] -> (FilePath -> m String) -> FilePath -> m Response
- serveFile :: (ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) => (FilePath -> m String) -> FilePath -> m Response
- serveFileFrom :: (ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) => FilePath -> (FilePath -> m String) -> FilePath -> m Response
- serveFileUsing :: (ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) => (String -> FilePath -> m Response) -> (FilePath -> m String) -> FilePath -> m Response
- sendFileResponse :: String -> FilePath -> Maybe (UTCTime, Request) -> Integer -> Integer -> Response
- lazyByteStringResponse :: String -> ByteString -> Maybe (UTCTime, Request) -> Integer -> Integer -> Response
- strictByteStringResponse :: String -> ByteString -> Maybe (UTCTime, Request) -> Integer -> Integer -> Response
- filePathSendFile :: (ServerMonad m, MonadIO m) => String -> FilePath -> m Response
- filePathLazy :: (ServerMonad m, MonadIO m) => String -> FilePath -> m Response
- filePathStrict :: (ServerMonad m, MonadIO m) => String -> FilePath -> m Response
- type MimeMap = Map String String
- mimeTypes :: MimeMap
- asContentType :: Monad m => String -> FilePath -> m String
- guessContentType :: MimeMap -> FilePath -> Maybe String
- guessContentTypeM :: Monad m => MimeMap -> FilePath -> m String
- data EntryKind
  - = File
  - | Directory
  - | UnknownKind
- browseIndex :: (ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m, ToMessage b) => (FilePath -> [FilePath] -> m b) -> (String -> FilePath -> m Response) -> (FilePath -> m String) -> [String] -> FilePath -> m Response
- renderDirectoryContents :: MonadIO m => FilePath -> [FilePath] -> m Html
- renderDirectoryContentsTable :: [(FilePath, Maybe UTCTime, Maybe Integer, EntryKind)] -> Html
- blockDotFiles :: (Request -> IO Response) -> Request -> IO Response
- defaultIxFiles :: [FilePath]
- combineSafe :: FilePath -> FilePath -> Maybe FilePath
- isSafePath :: [FilePath] -> Bool
- tryIndex :: (ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) => (String -> FilePath -> m Response) -> (FilePath -> m String) -> [String] -> FilePath -> m Response
- doIndex :: (ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) => [FilePath] -> MimeMap -> FilePath -> m Response
- doIndex' :: (ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) => (String -> FilePath -> m Response) -> (FilePath -> m String) -> [String] -> FilePath -> m Response
- doIndexLazy :: (ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) => [String] -> MimeMap -> FilePath -> m Response
- doIndexStrict :: (ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) => [String] -> MimeMap -> FilePath -> m Response
- fileNotFound :: (Monad m, FilterMonad Response m) => FilePath -> m Response
- isDot :: String -> Bool

# High-Level

## Serving files from a directory

fileServe

Arguments

:: (WebMonad Response m, ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) |
=> [FilePath] | index file names, in case the requested path is a directory
-> FilePath | file/directory to serve
-> m Response |

Deprecated: use serveDirectory instead.

Serve files from a directory and its subdirectories using `sendFile`.

Usage:

     fileServe ["index.html"] "path/to/files/on/disk"

`fileServe` does not support directory browsing. See `serveDirectory`

DEPRECATED: use `serveDirectory` instead.

Note:

The list of index files `["index.html"]` is only used to determine what file to show if the user requests a directory. You *do not* need to explicitly list all the files you want to serve.

fileServe'

Arguments

:: (WebMonad Response m, ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) |
=> (String -> FilePath -> m Response) | function which takes a content-type and filepath and generates a response (typically `filePathSendFile`, `filePathLazy`, or `filePathStrict`)
-> (FilePath -> m String) | function which returns the mime-type for FilePath -> [FilePath] -- ^ index file names, in case the requested path is a directory
-> (FilePath -> m Response) |
-> FilePath | file/directory to serve
-> m Response |

Serve files from a directory and its subdirectories (parameterizable version)

Parameterize this function to create functions like, `fileServe`, `fileServeLazy`, and `fileServeStrict`

You supply:

1. a low-level function which takes a content-type and `FilePath` and generates a Response
2. a function which determines the content-type from the `FilePath`
3. a list of all the default index files

NOTE: unlike fileServe, there are no index files by default. See `defaultIxFiles`.

fileServeLazy

Arguments

:: (WebMonad Response m, ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) |
=> [FilePath] | index file names, in case the requested path is a directory
-> FilePath | file/directory to serve
-> m Response |

Serve files from a directory and its subdirectories (lazy ByteString version).

WARNING: May leak file handles. You should probably use `fileServe` instead.

fileServeStrict

Arguments

:: (WebMonad Response m, ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) |
=> [FilePath] | index file names, in case the next argument is a directory
-> FilePath | file/directory to serve
-> m Response |

Serve files from a directory and its subdirectories (strict ByteString version).

WARNING: the entire file will be read into RAM before being served. You should probably use `fileServe` instead.

data Browsing

see `serveDirectory`

Constructors

EnableBrowsing |
DisableBrowsing |

Instances

Enum Browsing |
Eq Browsing |
Data Browsing |
Ord Browsing |
Read Browsing |
Show Browsing |
Typeable Browsing |

serveDirectory

Arguments

:: (WebMonad Response m, ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) |
=> Browsing | allow directory browsing
-> [FilePath] | index file names, in case the requested path is a directory
-> FilePath | file/directory to serve
-> m Response |

Serve files and directories from a directory and its subdirectories using `sendFile`.

Usage:

     serveDirectory EnableBrowsing ["index.html"] "path/to/files/on/disk"

If the requested path does not match a file or directory on the disk, then `serveDirectory` calls `mzero`.

If the requested path is a file then the file is served normally.

If the requested path is a directory, then the result depends on what the first two arguments to the function are.

The first argument controls whether directory browsing is enabled.

The second argument is a list of index files (such as index.html).

When a directory is requested, `serveDirectory` will first try to find one of the index files (in the order they are listed). If that fails, it will show a directory listing if `EnableBrowsing` is set, otherwise it will return `forbidden "Directory index forbidden"`.

Here is an explicit list of all the possible outcomes when the argument is a (valid) directory:

``DisableBrowsing`, empty index file list`

This will always return, forbidden "Directory index forbidden"

``DisableBrowsing`, non-empty index file list`

1. If an index file is found it will be shown.
2. Otherwise returns, forbidden "Directory index forbidden"

``EnableBrowsing`, empty index file list`

Always shows a directory index.

``EnableBrowsing`, non-empty index file list`

1. If an index file is found it will be shown
2. Otherwise shows a directory index

see also: `defaultIxFiles`, `serveFile`

serveDirectory'

Arguments

:: (WebMonad Response m, ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) |
=> Browsing | allow directory browsing
-> [FilePath] | index file names, in case the requested path is a directory
-> (FilePath -> m String) | function which returns the mime-type for FilePath
-> FilePath | file/directory to serve
-> m Response |

like `serveDirectory` but with custom mimeTypes

## Serving a single file

serveFile

Arguments

:: (ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) |
=> (FilePath -> m String) | function for determining content-type of file. Typically `asContentType` or `guessContentTypeM`
-> FilePath | path to the file to serve
-> m Response |

Serve a single, specified file. The name of the file being served is specified explicity. It is not derived automatically from the `Request` url.

example 1:

Serve as a specific content-type:

     serveFile (asContentType "image/jpeg") "/srv/data/image.jpg"

example 2:

Serve guessing the content-type from the extension:

     serveFile (guessContentTypeM mimeTypes) "/srv/data/image.jpg"

If the specified path does not exist or is not a file, this function will return `mzero`.

WARNING: No security checks are performed.

NOTE: alias for `serveFileUsing` `filePathSendFile`

serveFileFrom

Arguments

:: (ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) |
=> FilePath | directory wherein served files must be contained
-> (FilePath -> m String) | function for determining content-type of file. Typically `asContentType` or `guessContentTypeM`
-> FilePath | path to the file to serve
-> m Response |

Like `serveFile`, but uses `combineSafe` to prevent directory traversal attacks when the path to the file is supplied by the user.

serveFileUsing

Arguments

:: (ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) |
=> (String -> FilePath -> m Response) | typically `filePathSendFile`, `filePathLazy`, or `filePathStrict`
-> (FilePath -> m String) | function for determining content-type of file. Typically `asContentType` or `guessContentTypeM`
-> FilePath | path to the file to serve
-> m Response |

Serve a single, specified file. The name of the file being served is specified explicity. It is not derived automatically from the `Request` url.

example 1:

Serve using sendfile() and the specified content-type

     serveFileUsing filePathSendFile (asContentType "image/jpeg") "/srv/data/image.jpg"

example 2:

Serve using a lazy ByteString and the guess the content-type from the extension

     serveFileUsing filePathLazy (guessContentTypeM mimeTypes) "/srv/data/image.jpg"

WARNING: No security checks are performed.

# Low-Level

sendFileResponse

Arguments

:: String | content-type string
-> FilePath | file path for content to send
-> Maybe (UTCTime, Request) | mod-time for the handle (MUST NOT be later than server's time of message origination), incoming request (used to check for if-modified-since header)
-> Integer | offset into Handle
-> Integer | number of bytes to send
-> Response |

Use sendFile to send the contents of a Handle

lazyByteStringResponse

Arguments

:: String | content-type string (e.g. `"text/plain; charset=utf-8"`)
-> ByteString | lazy bytestring content to send
-> Maybe (UTCTime, Request) | mod-time for the bytestring, incoming request (used to check for if-modified-since header)
-> Integer | offset into the bytestring
-> Integer | number of bytes to send (offset + count must be less than or equal to the length of the bytestring)
-> Response |

Send the contents of a Lazy ByteString

strictByteStringResponse

Arguments

:: String | content-type string (e.g. `"text/plain; charset=utf-8"`)
-> ByteString | lazy bytestring content to send
-> Maybe (UTCTime, Request) | mod-time for the bytestring, incoming request (used to check for if-modified-since header)
-> Integer | offset into the bytestring
-> Integer | number of bytes to send (offset + count must be less than or equal to the length of the bytestring)
-> Response |

Send the contents of a Lazy ByteString

filePathSendFile

Arguments

:: (ServerMonad m, MonadIO m) |
=> String | content-type string
-> FilePath | path to file on disk
-> m Response |

Send the specified file with the specified mime-type using sendFile()

NOTE: assumes file exists and is readable by the server. See `serveFileUsing`.

WARNING: No security checks are performed.

filePathLazy

Arguments

:: (ServerMonad m, MonadIO m) |
=> String | content-type string
-> FilePath | path to file on disk
-> m Response |

Send the specified file with the specified mime-type using lazy ByteStrings

NOTE: assumes file exists and is readable by the server. See `serveFileUsing`.

WARNING: No security checks are performed.

filePathStrict

Arguments

:: (ServerMonad m, MonadIO m) |
=> String | content-type string
-> FilePath | path to file on disk
-> m Response |

Send the specified file with the specified mime-type using strict ByteStrings

NOTE: assumes file exists and is readable by the server. See `serveFileUsing`.

WARNING: No security checks are performed.

# Content-Type / Mime-Type

type MimeMap = Map String String

a `Map` from file extensions to content-types

example:

     myMimeMap :: MimeMap
     myMimeMap = Map.fromList [("gz","application/x-gzip"), ... ]

see also: `mimeTypes`

mimeTypes :: MimeMap

Ready collection of common mime types. Except for the first two entries, the mappings come from an Ubuntu 8.04 /etc/mime.types file.

asContentType

Arguments

:: Monad m |
=> String | the content-type to return
-> FilePath -> m String |

returns a specific content type, completely ignoring the `FilePath` argument.

Use this with `serveFile` if you want to explicitly specify the content-type.

see also: `guessContentTypeM`, `serveFile`

guessContentType :: MimeMap -> FilePath -> Maybe String

try to guess the content-type of a file based on its extension

see also: `guessContentTypeM`

guessContentTypeM :: Monad m => MimeMap -> FilePath -> m String

try to guess the content-type of a file based on its extension

defaults to application/octet-stream if no match was found.

Useful as an argument to `serveFile`

see also: `guessContentType`, `serveFile`

# Directory Browsing

data EntryKind

Constructors

File |
Directory |
UnknownKind |

Instances

Enum EntryKind |
Eq EntryKind |
Data EntryKind |
Ord EntryKind |
Read EntryKind |
Show EntryKind |
Typeable EntryKind |

browseIndex :: (ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m, ToMessage b) => (FilePath -> [FilePath] -> m b) -> (String -> FilePath -> m Response) -> (FilePath -> m String) -> [String] -> FilePath -> m Response

renderDirectoryContents

Arguments

:: MonadIO m |
=> FilePath | path to directory on disk
-> [FilePath] | list of entries in that path
-> m Html |

a function to generate an HTML page showing the contents of a directory on the disk

see also: `browseIndex`, `renderDirectoryContentsTable`

renderDirectoryContentsTable

Arguments

:: [(FilePath, Maybe UTCTime, Maybe Integer, EntryKind)] | list of files+meta data, see `getMetaData`
-> Html |

a function to generate an HTML table showing the contents of a directory on the disk

This function generates most of the content of the `renderDirectoryContents` page. If you want to style the page differently, or add google analytics code, etc, you can just create a new page template to wrap around this HTML.

see also: `getMetaData`, `renderDirectoryContents`

# Other

blockDotFiles :: (Request -> IO Response) -> Request -> IO Response

Prevents files of the form '.foo' or 'bar/.foo' from being served

defaultIxFiles :: [FilePath]

a list of common index files. Specifically: `index.html`, `index.xml`, `index.gif`

Typically used as an argument to `serveDiretory`.

combineSafe :: FilePath -> FilePath -> Maybe FilePath

Combine two `FilePath`s, ensuring that the resulting path leads to a file within the first `FilePath`.

    >>> combineSafe "/var/uploads/" "etc/passwd"
    Just "/var/uploads/etc/passwd"
    >>> combineSafe "/var/uploads/" "/etc/passwd"
    Nothing
    >>> combineSafe "/var/uploads/" "../../etc/passwd"
    Nothing
    >>> combineSafe "/var/uploads/" "../uploads/home/../etc/passwd"
    Just "/var/uploads/etc/passwd"

isSafePath :: [FilePath] -> Bool

tryIndex

Arguments

:: (ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) |
=> (String -> FilePath -> m Response) | usually `filePathSendFile`
-> (FilePath -> m String) | function to calculate mime type, usually `guessContentTypeM`
-> [String] | list of index files. See also `defaultIxFiles`
-> FilePath | directory to search in
-> m Response |

try to find an index file, calls mzero on failure

doIndex

Arguments

:: (ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) |
=> [FilePath] | list of possible index files (e.g., `index.html`)
-> MimeMap | see also `mimeTypes`
-> FilePath | directory on disk to search for index files
-> m Response |

attempt to serve index files

doIndex' :: (ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) => (String -> FilePath -> m Response) -> (FilePath -> m String) -> [String] -> FilePath -> m Response

doIndexLazy :: (ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) => [String] -> MimeMap -> FilePath -> m Response

doIndexStrict :: (ServerMonad m, FilterMonad Response m, MonadIO m, MonadPlus m) => [String] -> MimeMap -> FilePath -> m Response

fileNotFound :: (Monad m, FilterMonad Response m) => FilePath -> m Response

return a simple File not found 404 page.

isDot :: String -> Bool

Returns True if the given String either starts with a . or is of the form foo/.bar, e.g. the typical *nix convention for hidden files.