1 | 'use strict'
|
2 |
|
3 | const fs = require('fs')
|
4 | const mime = require('../detect/mime')
|
5 | const path = require('path')
|
6 | const util = require('util')
|
7 |
|
8 | const defaultMimeType = mime.getType('bin')
|
9 | const statAsync = util.promisify(fs.stat)
|
10 | const readdirAsync = util.promisify(fs.readdir)
|
11 |
|
12 | function sendFile (response, filePath, stat) {
|
13 | return new Promise((resolve, reject) => {
|
14 | response.writeHead(200, {
|
15 | 'Content-Type': mime.getType(path.extname(filePath)) || defaultMimeType,
|
16 | 'Content-Length': stat.size
|
17 | })
|
18 | fs.createReadStream(filePath)
|
19 | .on('error', reject)
|
20 | .on('end', resolve)
|
21 | .pipe(response)
|
22 | })
|
23 | }
|
24 |
|
25 | function sendIndex (response, folderPath) {
|
26 | const indexPath = path.join(folderPath, 'index.html')
|
27 | return statAsync(indexPath)
|
28 | .then(stat => sendFile(response, indexPath, stat))
|
29 | }
|
30 |
|
31 | async function checkCaseSensitivePath (filePath) {
|
32 | const folderPath = path.dirname(filePath)
|
33 | if (folderPath && folderPath !== filePath) {
|
34 | const name = path.basename(filePath)
|
35 | const names = await readdirAsync(folderPath)
|
36 | if (!names.includes(name)) {
|
37 | throw new Error('Not found')
|
38 | }
|
39 | return checkCaseSensitivePath(folderPath)
|
40 | }
|
41 | }
|
42 |
|
43 | module.exports = {
|
44 | schema: {},
|
45 | redirect: ({ request, mapping, redirect, response }) => {
|
46 | if (request.method !== 'GET') {
|
47 | return Promise.resolve(405)
|
48 | }
|
49 | let filePath = /([^?#]+)/.exec(unescape(redirect))[1]
|
50 | if (!path.isAbsolute(filePath)) {
|
51 | filePath = path.join(mapping.cwd, filePath)
|
52 | }
|
53 | const directoryAccess = !!filePath.match(/(\\|\/)$/)
|
54 | if (directoryAccess) {
|
55 | filePath = filePath.substring(0, filePath.length - 1)
|
56 | }
|
57 | return statAsync(filePath)
|
58 | .then(async stat => {
|
59 | if (mapping['case-sensitive']) {
|
60 | await checkCaseSensitivePath(filePath)
|
61 | }
|
62 | const isDirectory = stat.isDirectory()
|
63 | if (isDirectory ^ directoryAccess) {
|
64 | return 404
|
65 | }
|
66 | if (isDirectory) {
|
67 | return sendIndex(response, filePath)
|
68 | }
|
69 | return sendFile(response, filePath, stat)
|
70 | })
|
71 | .catch(() => 404)
|
72 | }
|
73 | }
|