root / HServer / 00.Server / 00.Program / node_modules / serve-static / index.js
이력 | 보기 | 이력해설 | 다운로드 (4.46 KB)
1 | 39 | HKM | /*!
|
---|---|---|---|
2 | * serve-static
|
||
3 | * Copyright(c) 2010 Sencha Inc.
|
||
4 | * Copyright(c) 2011 TJ Holowaychuk
|
||
5 | * Copyright(c) 2014-2016 Douglas Christopher Wilson
|
||
6 | * MIT Licensed
|
||
7 | */
|
||
8 | |||
9 | 'use strict'
|
||
10 | |||
11 | /**
|
||
12 | * Module dependencies.
|
||
13 | * @private
|
||
14 | */
|
||
15 | |||
16 | var encodeUrl = require('encodeurl') |
||
17 | var escapeHtml = require('escape-html') |
||
18 | var parseUrl = require('parseurl') |
||
19 | var resolve = require('path').resolve |
||
20 | var send = require('send') |
||
21 | var url = require('url') |
||
22 | |||
23 | /**
|
||
24 | * Module exports.
|
||
25 | * @public
|
||
26 | */
|
||
27 | |||
28 | module.exports = serveStatic |
||
29 | module.exports.mime = send.mime |
||
30 | |||
31 | /**
|
||
32 | * @param {string} root
|
||
33 | * @param {object} [options]
|
||
34 | * @return {function}
|
||
35 | * @public
|
||
36 | */
|
||
37 | |||
38 | function serveStatic (root, options) { |
||
39 | if (!root) {
|
||
40 | throw new TypeError('root path required') |
||
41 | } |
||
42 | |||
43 | if (typeof root !== 'string') { |
||
44 | throw new TypeError('root path must be a string') |
||
45 | } |
||
46 | |||
47 | // copy options object
|
||
48 | var opts = Object.create(options || null) |
||
49 | |||
50 | // fall-though
|
||
51 | var fallthrough = opts.fallthrough !== false |
||
52 | |||
53 | // default redirect
|
||
54 | var redirect = opts.redirect !== false |
||
55 | |||
56 | // headers listener
|
||
57 | var setHeaders = opts.setHeaders
|
||
58 | |||
59 | if (setHeaders && typeof setHeaders !== 'function') { |
||
60 | throw new TypeError('option setHeaders must be function') |
||
61 | } |
||
62 | |||
63 | // setup options for send
|
||
64 | opts.maxage = opts.maxage || opts.maxAge || 0
|
||
65 | opts.root = resolve(root) |
||
66 | |||
67 | // construct directory listener
|
||
68 | var onDirectory = redirect
|
||
69 | ? createRedirectDirectoryListener() |
||
70 | : createNotFoundDirectoryListener() |
||
71 | |||
72 | return function serveStatic (req, res, next) { |
||
73 | if (req.method !== 'GET' && req.method !== 'HEAD') { |
||
74 | if (fallthrough) {
|
||
75 | return next()
|
||
76 | } |
||
77 | |||
78 | // method not allowed
|
||
79 | res.statusCode = 405
|
||
80 | res.setHeader('Allow', 'GET, HEAD') |
||
81 | res.setHeader('Content-Length', '0') |
||
82 | res.end() |
||
83 | return
|
||
84 | } |
||
85 | |||
86 | var forwardError = !fallthrough
|
||
87 | var originalUrl = parseUrl.original(req)
|
||
88 | var path = parseUrl(req).pathname
|
||
89 | |||
90 | // make sure redirect occurs at mount
|
||
91 | if (path === '/' && originalUrl.pathname.substr(-1) !== '/') { |
||
92 | path = ''
|
||
93 | } |
||
94 | |||
95 | // create send stream
|
||
96 | var stream = send(req, path, opts)
|
||
97 | |||
98 | // add directory handler
|
||
99 | stream.on('directory', onDirectory)
|
||
100 | |||
101 | // add headers listener
|
||
102 | if (setHeaders) {
|
||
103 | stream.on('headers', setHeaders)
|
||
104 | } |
||
105 | |||
106 | // add file listener for fallthrough
|
||
107 | if (fallthrough) {
|
||
108 | stream.on('file', function onFile () { |
||
109 | // once file is determined, always forward error
|
||
110 | forwardError = true
|
||
111 | }) |
||
112 | } |
||
113 | |||
114 | // forward errors
|
||
115 | stream.on('error', function error (err) { |
||
116 | if (forwardError || !(err.statusCode < 500)) { |
||
117 | next(err) |
||
118 | return
|
||
119 | } |
||
120 | |||
121 | next() |
||
122 | }) |
||
123 | |||
124 | // pipe
|
||
125 | stream.pipe(res) |
||
126 | } |
||
127 | } |
||
128 | |||
129 | /**
|
||
130 | * Collapse all leading slashes into a single slash
|
||
131 | * @private
|
||
132 | */
|
||
133 | function collapseLeadingSlashes (str) { |
||
134 | for (var i = 0; i < str.length; i++) { |
||
135 | if (str.charCodeAt(i) !== 0x2f /* / */) { |
||
136 | break
|
||
137 | } |
||
138 | } |
||
139 | |||
140 | return i > 1 |
||
141 | ? '/' + str.substr(i)
|
||
142 | : str |
||
143 | } |
||
144 | |||
145 | /**
|
||
146 | * Create a minimal HTML document.
|
||
147 | *
|
||
148 | * @param {string} title
|
||
149 | * @param {string} body
|
||
150 | * @private
|
||
151 | */
|
||
152 | |||
153 | function createHtmlDocument (title, body) { |
||
154 | return '<!DOCTYPE html>\n' + |
||
155 | '<html lang="en">\n' +
|
||
156 | '<head>\n' +
|
||
157 | '<meta charset="utf-8">\n' +
|
||
158 | '<title>' + title + '</title>\n' + |
||
159 | '</head>\n' +
|
||
160 | '<body>\n' +
|
||
161 | '<pre>' + body + '</pre>\n' + |
||
162 | '</body>\n' +
|
||
163 | '</html>\n'
|
||
164 | } |
||
165 | |||
166 | /**
|
||
167 | * Create a directory listener that just 404s.
|
||
168 | * @private
|
||
169 | */
|
||
170 | |||
171 | function createNotFoundDirectoryListener () { |
||
172 | return function notFound () { |
||
173 | this.error(404) |
||
174 | } |
||
175 | } |
||
176 | |||
177 | /**
|
||
178 | * Create a directory listener that performs a redirect.
|
||
179 | * @private
|
||
180 | */
|
||
181 | |||
182 | function createRedirectDirectoryListener () { |
||
183 | return function redirect (res) { |
||
184 | if (this.hasTrailingSlash()) { |
||
185 | this.error(404) |
||
186 | return
|
||
187 | } |
||
188 | |||
189 | // get original URL
|
||
190 | var originalUrl = parseUrl.original(this.req) |
||
191 | |||
192 | // append trailing slash
|
||
193 | originalUrl.path = null
|
||
194 | originalUrl.pathname = collapseLeadingSlashes(originalUrl.pathname + '/')
|
||
195 | |||
196 | // reformat the URL
|
||
197 | var loc = encodeUrl(url.format(originalUrl))
|
||
198 | var doc = createHtmlDocument('Redirecting', 'Redirecting to <a href="' + escapeHtml(loc) + '">' + |
||
199 | escapeHtml(loc) + '</a>')
|
||
200 | |||
201 | // send redirect response
|
||
202 | res.statusCode = 301
|
||
203 | res.setHeader('Content-Type', 'text/html; charset=UTF-8') |
||
204 | res.setHeader('Content-Length', Buffer.byteLength(doc))
|
||
205 | res.setHeader('Content-Security-Policy', "default-src 'self'") |
||
206 | res.setHeader('X-Content-Type-Options', 'nosniff') |
||
207 | res.setHeader('Location', loc)
|
||
208 | res.end(doc) |
||
209 | } |
||
210 | } |