UNPKG

inert

Version:

Static file and directory handlers plugin for hapi.js

274 lines (223 loc) 11.6 kB
# inert Static file and directory handlers plugin for hapi.js. [![Build Status](https://secure.travis-ci.org/hapijs/inert.svg)](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.