(uri, request, response)
| 248 | }); |
| 249 | |
| 250 | function serveFile(uri, request, response) { |
| 251 | var file = decodeURIComponent(new URL(uri || request.url, 'http://localhost').pathname); |
| 252 | |
| 253 | var rootPath = path.resolve(options.path); |
| 254 | var filePath = path.resolve(rootPath + file); |
| 255 | |
| 256 | // since we call filesystem directly so we need to verify that the |
| 257 | // url doesn't go outside the serve path |
| 258 | var relative = path.relative(rootPath, filePath); |
| 259 | if (relative !== '' && (relative.startsWith('..') || path.isAbsolute(relative))) { |
| 260 | response.writeHead(403, { 'Content-Type': 'text/html' }); |
| 261 | return response.end('403 Forbidden'); |
| 262 | } |
| 263 | |
| 264 | fs.stat(filePath, function (err, stats) { |
| 265 | if (!err && stats.isDirectory()) { |
| 266 | if (options.ftp) { |
| 267 | // In ftp mode, try index.html first, fall back to directory listing |
| 268 | var indexPath = path.join(filePath, 'index.html'); |
| 269 | fs.access(indexPath, fs.constants.R_OK, function (err) { |
| 270 | if (!err) { |
| 271 | return serveStaticFile(indexPath, file + '/index.html', request, response); |
| 272 | } |
| 273 | return serveDirectoryListing(filePath, file, response); |
| 274 | }); |
| 275 | return; |
| 276 | } |
| 277 | // Not ftp mode: serve homepage |
| 278 | if (file === '/' || file === '') { |
| 279 | file = options.homepage; |
| 280 | request.wantHomepage = true; |
| 281 | filePath = path.resolve(options.path + file); |
| 282 | } |
| 283 | } else if (file === '/' || file === '') { |
| 284 | file = options.homepage; |
| 285 | request.wantHomepage = true; |
| 286 | filePath = path.resolve(options.path + file); |
| 287 | } |
| 288 | serveStaticFile(filePath, file, request, response); |
| 289 | }); |
| 290 | } |
| 291 | |
| 292 | function formatFileSize(bytes) { |
| 293 | if (bytes < 1024) return bytes + 'B'; |
no test coverage detected
searching dependent graphs…