Skip to content

Commit

Permalink
Merge pull request #132 from jarrodyellets/master
Browse files Browse the repository at this point in the history
Move Readme to API
  • Loading branch information
Marsup committed Sep 26, 2019
2 parents e1de8fe + 60bb2e8 commit f70b849
Show file tree
Hide file tree
Showing 2 changed files with 255 additions and 268 deletions.
254 changes: 254 additions & 0 deletions API.md
@@ -0,0 +1,254 @@

**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.
* Pre-compressed file support for `content-encoding: gzip` responses.
* File attachment support using `content-disposition` header.

## 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/hapi');
const Inert = require('@hapi/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.

0 comments on commit f70b849

Please sign in to comment.