root / HServer / 00.Server / 00.Program / node_modules / serve-static / index.js
이력 | 보기 | 이력해설 | 다운로드 (4.46 KB)
1 |
/*!
|
---|---|
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 |
} |