141 lines
5.2 KiB
JavaScript
141 lines
5.2 KiB
JavaScript
'use strict';
|
|
var assert = require('assert-plus');
|
|
var errors = require('restify-errors');
|
|
var path = require('path');
|
|
var send = require('send');
|
|
var shallowCopy = require('./utils/shallowCopy');
|
|
|
|
///--- Globals
|
|
var MethodNotAllowedError = errors.MethodNotAllowedError;
|
|
var NotAuthorizedError = errors.NotAuthorizedError;
|
|
var ResourceNotFoundError = errors.ResourceNotFoundError;
|
|
|
|
/**
|
|
* Serves static files, with API similar to expressjs
|
|
*
|
|
* @public
|
|
* @function serveStaticFiles
|
|
* @param {String} directory - the directory to serve files from
|
|
* @param {Object} opts - an options object, which is optional
|
|
* @param {Number} [opts.maxAge=0] - specify max age in millisecs
|
|
* @param {Boolean} [opts.etag=true] - enable/disable etag, default = true
|
|
* @param {Function} [opts.setHeaders] - set custom headers for the Files
|
|
* (synchronously), The function is called as `fn(res, path, stat)`,
|
|
* where the arguments are:
|
|
* `res` the response object
|
|
* `path` the file path that is being sent
|
|
* `stat` the stat object of the file that is being sent
|
|
* @throws {MethodNotAllowedError}
|
|
* @throws {NotAuthorizedError}
|
|
* @throws {ResourceNotFoundError}
|
|
* @returns {Function} Handler
|
|
* @example
|
|
* <caption>
|
|
* The serveStaticFiles plugin allows you to map a GET route to a
|
|
* directory on the disk
|
|
* </caption>
|
|
* server.get('/public/*', // don't forget the `/*`
|
|
* restify.plugins.serveStaticFiles('./documentation/v1')
|
|
* );
|
|
* @example
|
|
* <caption>
|
|
* The GET `route` and `directory` combination will serve a file
|
|
* located in `./documentation/v1/index.html` when you attempt to hit
|
|
* `http://localhost:8080/public/index.html`
|
|
*
|
|
* The plugin uses [send](https://github.com/pillarjs/send) under the hood
|
|
* which is also used by `expressjs` to serve static files. Most of the options
|
|
* that work with `send` will work with this plugin.
|
|
*
|
|
* The default file the plugin looks for is `index.html`
|
|
* </caption>
|
|
* server.get('/public/*',
|
|
* restify.plugins.serveStaticFiles('./documentation/v1', {
|
|
* maxAge: 3600000, // this is in millisecs
|
|
* etag: false,
|
|
* setHeaders: function setCustomHeaders(response, requestedPath, stat) {
|
|
* response.setHeader('restify-plugin-x', 'awesome');
|
|
* }
|
|
* })
|
|
* );
|
|
*/
|
|
function serveStaticFiles(directory, opts) {
|
|
// make a copy of the options that will be passed to send
|
|
var optionsPlugin = shallowCopy(opts || {});
|
|
var optionsSend = shallowCopy(opts || {});
|
|
// lets assert some options
|
|
assert.object(optionsSend, 'options');
|
|
assert.object(optionsPlugin, 'options');
|
|
assert.string(directory, 'directory');
|
|
|
|
// `send` library relies on `root` to specify the root folder
|
|
// to look for files
|
|
optionsSend.root = path.resolve(directory);
|
|
// `setHeaders` is only understood by our plugin
|
|
if (optionsSend.setHeaders) {
|
|
delete optionsSend.setHeaders;
|
|
}
|
|
|
|
return function handleServeStaticFiles(req, res, next) {
|
|
// Check to make sure that this was either a GET or a HEAD request
|
|
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
|
return next(new MethodNotAllowedError('%s', req.method));
|
|
}
|
|
// we expect the params to have `*`:
|
|
// This allows the router to accept any file path
|
|
var requestedFile = req.params['*'] || 'index.html';
|
|
// This is used only for sending back correct error message text
|
|
var requestedFullPath = req.url;
|
|
// Rely on `send` library to create a stream
|
|
var stream = send(req, requestedFile, optionsSend);
|
|
|
|
// Lets handle the various events being emitted by send module
|
|
|
|
// stream has ended, must call `next()`
|
|
stream.on('end', function handleEnd() {
|
|
return next();
|
|
});
|
|
|
|
// when `send` encounters any `error`, we have the opportunity
|
|
// to handle the errors here
|
|
stream.on('error', function handleError(err) {
|
|
var respondWithError = null;
|
|
// When file does not exist
|
|
if (err.statusCode === 404) {
|
|
respondWithError = new ResourceNotFoundError(requestedFullPath);
|
|
} else {
|
|
// or action is forbidden (like requesting a directory)
|
|
respondWithError = new NotAuthorizedError(requestedFullPath);
|
|
}
|
|
return next(respondWithError);
|
|
});
|
|
|
|
// If the request was for directory and that directory did not
|
|
// have index.html, this will be called
|
|
stream.on('directory', function handleDirectoryRequest() {
|
|
next(new NotAuthorizedError('%s', requestedFullPath));
|
|
return;
|
|
});
|
|
|
|
// stream is about to send headers, and custom headers must be
|
|
// set now
|
|
stream.on('headers', function handleCustomHeaders(
|
|
response,
|
|
requestedPath,
|
|
stat
|
|
) {
|
|
if (
|
|
optionsPlugin.setHeaders &&
|
|
typeof optionsPlugin.setHeaders === 'function'
|
|
) {
|
|
optionsPlugin.setHeaders(response, requestedPath, stat);
|
|
}
|
|
});
|
|
|
|
// pipe the stream into response
|
|
return stream.pipe(res);
|
|
};
|
|
}
|
|
|
|
module.exports = serveStaticFiles;
|