inert
Version:
Static file and directory handlers plugin for hapi.js
274 lines (223 loc) • 11.6 kB
Markdown
# inert
Static file and directory handlers plugin for hapi.js.
[](http://travis-ci.org/hapijs/inert)
Lead Maintainer - [Gil Pedersen](https://github.com/kanongil)
**inert** provides new [handler](https://github.com/hapijs/hapi/blob/master/API.md#serverhandlername-method)
methods for serving static files and directories, as well as adding a `h.file()` method to the
[toolkit](https://github.com/hapijs/hapi/blob/master/API.md#response-toolkit), which can respond with
file based resources.
#### Features
* Files are served with cache friendly `last-modified` and `etag` headers.
* Generated file listings and custom indexes.
* Precompressed file support for `content-encoding: gzip` responses.
* File attachment support using `content-disposition` header.
## Index
- [Examples](#examples)
- [Static file server](#static-file-server)
- [Serving a single file](#serving-a-single-file)
- [Customized file response](#customized-file-response)
- [Usage](#usage)
- [Server options](#server-options)
- [`h.file(path, [options])`](#hfilepath-options)
- [The `file` handler](#the-file-handler)
- [The `directory` handler](#the-directory-handler)
- [Errors](#errors)
## Examples
**inert** enables a number of common use cases for serving static assets.
### Static file server
The following creates a basic static file server that can be used to serve html content from the
`public` directory on port 3000:
```js
const Path = require('path');
const Hapi = require('hapi');
const Inert = require('inert');
const server = new Hapi.Server({
port: 3000,
routes: {
files: {
relativeTo: Path.join(__dirname, 'public')
}
}
});
const provision = async () => {
await server.register(Inert);
server.route({
method: 'GET',
path: '/{param*}',
handler: {
directory: {
path: '.',
redirectToSlash: true,
index: true,
}
}
});
await server.start();
console.log('Server running at:', server.info.uri);
};
provision();
```
### Serving a single file
You can serve specific files using the `file` handler:
```js
server.route({
method: 'GET',
path: '/{path*}',
handler: {
file: 'page.html'
}
});
```
### Customized file response
If you need more control, the `h.file()` method is available to use inside handlers:
```js
server.route({
method: 'GET',
path: '/file',
handler(request, h) {
let path = 'plain.txt';
if (request.headers['x-magic'] === 'sekret') {
path = 'awesome.png';
}
return h.file(path).vary('x-magic');
}
});
server.ext('onPreResponse', (request, h) => {
const response = request.response;
if (response.isBoom &&
response.output.statusCode === 404) {
return h.file('404.html').code(404);
}
return h.continue;
});
```
## Usage
After registration, this plugin adds a new method to the `toolkit` object and exposes the `'file'`
and `'directory'` route handlers.
Note that **inert** uses the custom `'file'` `variety` to signal that the response is a static
file generated by this plugin.
### Server options
**inert** handles the following server plugin options on `plugins.inert`:
- `etagsCacheMaxSize` - sets the maximum number of file etag hash values stored in the
etags cache. Defaults to `10000`.
### `h.file(path, [options])`
Transmits a file from the file system. The 'Content-Type' header defaults to the matching mime
type based on filename extension.:
- `path` - the file path.
- `options` - optional settings:
- `confine` - serve file relative to this directory and returns `403 Forbidden` if the
`path` resolves outside the `confine` directory.
Defaults to `true` which uses the `relativeTo` route option as the `confine`.
Set to `false` to disable this security feature.
- `filename` - an optional filename to specify if sending a 'Content-Disposition' header,
defaults to the basename of `path`
- `mode` - specifies whether to include the 'Content-Disposition' header with the response.
Available values:
- `false` - header is not included. This is the default value.
- `'attachment'`
- `'inline'`
- `lookupCompressed` - if `true`, looks for for a pre-compressed version of the file with
the same filename with an extension, depending on the accepted encoding.
Defaults to `false`.
- `lookupMap` - an `object` which maps content encoding to expected file name extension.
Defaults to `{ gzip: '.gz' }`.
- `etagMethod` - specifies the method used to calculate the `ETag` header response.
Available values:
- `'hash'` - SHA1 sum of the file contents, suitable for distributed deployments.
Default value.
- `'simple'` - Hex encoded size and modification date, suitable when files are stored
on a single server.
- `false` - Disable ETag computation.
- `start` - offset in file to reading from, defaults to 0.
- `end` - offset in file to stop reading from. If not set, will read to end of file.
Returns a standard [response](https://github.com/hapijs/hapi/blob/master/API.md#response-object) object.
The [response flow control rules](https://github.com/hapijs/hapi/blob/master/API.md#flow-control) **do not** apply.
### The `file` handler
Generates a static file endpoint for serving a single file. `file` can be set to:
- a relative or absolute file path string (relative paths are resolved based on the
route [`files`](https://github.com/hapijs/hapi/blob/master/API.md#route.config.files)
configuration).
- a function with the signature `function(request)` which returns the relative or absolute
file path.
- an object with one or more of the following options:
- `path` - a path string or function as described above (required).
- `confine` - serve file relative to this directory and returns `403 Forbidden` if the
`path` resolves outside the `confine` directory.
Defaults to `true` which uses the `relativeTo` route option as the `confine`.
Set to `false` to disable this security feature.
- `filename` - an optional filename to specify if sending a 'Content-Disposition'
header, defaults to the basename of `path`
- `mode` - specifies whether to include the 'Content-Disposition' header with the
response. Available values:
- `false` - header is not included. This is the default value.
- `'attachment'`
- `'inline'`
- `lookupCompressed` - if `true`, looks for for a pre-compressed version of the file with
the same filename with an extension, depending on the accepted encoding.
Defaults to `false`.
- `lookupMap` - an `object` which maps content encoding to expected file name extension.
Defaults to `{ gzip: '.gz' }`.
- `etagMethod` - specifies the method used to calculate the `ETag` header response.
Available values:
- `'hash'` - SHA1 sum of the file contents, suitable for distributed deployments.
Default value.
- `'simple'` - Hex encoded size and modification date, suitable when files are stored
on a single server.
- `false` - Disable ETag computation.
- `start` - offset in file to reading from, defaults to 0.
- `end` - offset in file to stop reading from. If not set, will read to end of file.
### The `directory` handler
Generates a directory endpoint for serving static content from a directory.
Routes using the directory handler must include a path parameter at the end of the path
string (e.g. `/path/to/somewhere/{param}` where the parameter name does not matter). The
path parameter can use any of the parameter options (e.g. `{param}` for one level files
only, `{param?}` for one level files or the directory root, `{param*}` for any level, or
`{param*3}` for a specific level). If additional path parameters are present, they are
ignored for the purpose of selecting the file system resource. The directory handler is an
object with the following options:
- `path` - (required) the directory root path (relative paths are resolved based on the
route [`files`](https://github.com/hapijs/hapi/blob/master/API.md#route.config.files)
configuration). Value can be:
- a single path string used as the prefix for any resources requested by appending the
request path parameter to the provided string.
- an array of path strings. Each path will be attempted in order until a match is
found (by following the same process as the single path string).
- a function with the signature `function(request)` which returns the path string or
an array of path strings. If the function returns an error, the error is passed back
to the client in the response.
- `index` - optional boolean|string|string[], determines if an index file will be served
if found in the folder when requesting a directory. The given string or strings specify
the name(s) of the index file to look for. If `true`, looks for 'index.html'. Any falsy
value disables index file lookup. Defaults to `true`.
- `listing` - optional boolean, determines if directory listing is generated when a
directory is requested without an index document.
Defaults to `false`.
- `showHidden` - optional boolean, determines if hidden files will be shown and served.
Defaults to `false`.
- `redirectToSlash` - optional boolean, determines if requests for a directory without a
trailing slash are redirected to the same path with the missing slash. Useful for
ensuring relative links inside the response are resolved correctly. Disabled when the
server config `router.stripTrailingSlash` is `true. `Defaults to `false`.
- `lookupCompressed` - optional boolean, instructs the file processor to look for the same
filename with an extension, depending on the accepted encoding, for a pre-compressed
version of the file to serve. Defaults to `false`.
- `lookupMap` - an `object` which maps content encoding to expected file name extension.
Defaults to `{ gzip: '.gz' }`.
- `etagMethod` - specifies the method used to calculate the `ETag` header response.
Available values:
- `'hash'` - SHA1 sum of the file contents, suitable for distributed deployments.
Default value.
- `'simple'` - Hex encoded size and modification date, suitable when files are stored
on a single server.
- `false` - Disable ETag computation.
- `defaultExtension` - optional string, appended to file requests if the requested file is
not found. Defaults to no extension.
### Errors
Any file access errors are signalled using appropriate [Boom](https://github.com/hapijs/boom)
errors. These are `Boom.notFound()` for missing or hidden files, and `Boom.forbidden()` for
files that exist, but can't otherwise be accessed.
The error can contain an `err.data.path` property, which is the path that the error failed for.
This property *does not always exist* if the response was generated without a file system lookup,
and for the directory handler it will be the last tested non-index path.
If an unexpected configuration or processing errors occurs, `Boom.internal()` and `'system'`
errors can also be thrown.