wzp
2021-05-13 7d694a9113118daec5be7ac224dab46a3b20f106
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
'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;