st 1.2.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.travis.yml +11 -2
- package/README.md +28 -22
- package/bin/server.js +75 -66
- package/package.json +12 -10
- package/st.js +505 -449
- package/test/basic.js +133 -0
- package/test/cli/basic-test.js +32 -0
- package/test/cli/common.js +108 -0
- package/test/cli/host-test.js +92 -0
- package/test/common.js +66 -0
- package/test/cors.js +19 -0
- package/test/dot-common.js +44 -0
- package/test/dot-false.js +19 -0
- package/test/dot-true.js +20 -0
- package/test/explicit-cache-control.js +12 -0
- package/test/fd-limit.js +10 -0
- package/test/fixtures/.dotted-dir/.index.html +11 -0
- package/test/fixtures/.dotted-dir/index.html +11 -0
- package/test/fixtures/index.html +11 -0
- package/test/fixtures/space in filename.txt +1 -0
- package/test/gzip-after-no-gzip.js +38 -0
- package/test/middleware.js +17 -0
- package/test/multi-mount.js +251 -0
- package/test/no-cache.js +19 -0
- package/test/no-content-maxage.js +10 -0
- package/test/no-cors.js +14 -0
- package/test/no-fd-cache.js +8 -0
- package/test/no-gzip-accepted-no-cache.js +32 -0
- package/test/no-gzip-accepted.js +31 -0
- package/test/no-gzip.js +18 -0
- package/test/parent-path.js +8 -0
- package/test/passthrough.js +75 -0
- package/test/preset-cache-control.js +65 -0
package/test/basic.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
const zlib = require('zlib')
|
|
2
|
+
const { test } = require('tap')
|
|
3
|
+
const { req, stExpect, opts, mount } = require('./common')
|
|
4
|
+
|
|
5
|
+
module.exports.req = req
|
|
6
|
+
module.exports.stExpect = stExpect
|
|
7
|
+
module.exports.mount = mount
|
|
8
|
+
|
|
9
|
+
let stEtag
|
|
10
|
+
|
|
11
|
+
test('simple request', (t) => {
|
|
12
|
+
req('/test/st.js', (er, res, body) => {
|
|
13
|
+
t.error(er)
|
|
14
|
+
t.equal(res.statusCode, 200)
|
|
15
|
+
t.ok(/\/javascript$/.test(res.headers['content-type']))
|
|
16
|
+
t.ok(res.headers.etag)
|
|
17
|
+
stEtag = res.headers.etag
|
|
18
|
+
t.equal(body.toString(), stExpect)
|
|
19
|
+
t.end()
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('304 request', (t) => {
|
|
24
|
+
req('/test/st.js', { 'if-none-match': stEtag }, (er, res, body) => {
|
|
25
|
+
t.equal(res.statusCode, 304)
|
|
26
|
+
t.equal(body.length, 0)
|
|
27
|
+
t.end()
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
if (opts.gzip !== false) {
|
|
32
|
+
test('gzip', (t) => {
|
|
33
|
+
req('/test/st.js', { 'accept-encoding': 'gzip' },
|
|
34
|
+
(er, res, body) => {
|
|
35
|
+
t.equal(res.statusCode, 200)
|
|
36
|
+
t.equal(res.headers['content-encoding'], 'gzip')
|
|
37
|
+
zlib.gunzip(body, (er, body) => {
|
|
38
|
+
if (er) {
|
|
39
|
+
throw er
|
|
40
|
+
}
|
|
41
|
+
t.equal(body.toString(), stExpect)
|
|
42
|
+
t.end()
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
test('multiball!', (t) => {
|
|
49
|
+
let n = 6
|
|
50
|
+
req('/test/st.js', then)
|
|
51
|
+
req('/test/README.md', then)
|
|
52
|
+
req('/test/LICENSE', then)
|
|
53
|
+
req('/test/package.json', then)
|
|
54
|
+
req('/test/favicon.ico', then)
|
|
55
|
+
req('/test/bin/server.js', then)
|
|
56
|
+
|
|
57
|
+
function then (er, res) {
|
|
58
|
+
if (er) {
|
|
59
|
+
throw er
|
|
60
|
+
}
|
|
61
|
+
t.equal(res.statusCode, 200)
|
|
62
|
+
|
|
63
|
+
// give them all time to close, then go again.
|
|
64
|
+
if (--n === 0) {
|
|
65
|
+
setTimeout(() => {
|
|
66
|
+
n = 6
|
|
67
|
+
req('/test/st.js', then2)
|
|
68
|
+
req('/test/README.md', then2)
|
|
69
|
+
req('/test/LICENSE', then2)
|
|
70
|
+
req('/test/package.json', then2)
|
|
71
|
+
req('/test/favicon.ico', then2)
|
|
72
|
+
req('/test/bin/server.js', then2)
|
|
73
|
+
}, 200)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function then2 (er, res) {
|
|
78
|
+
if (er) {
|
|
79
|
+
throw er
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
t.equal(res.statusCode, 200)
|
|
83
|
+
|
|
84
|
+
if (opts.cache === false) {
|
|
85
|
+
t.equal(res.headers['cache-control'], 'no-cache')
|
|
86
|
+
} else if (opts.cache && opts.cache.content && opts.cache.content.maxAge === false) {
|
|
87
|
+
t.ok(res.headers['cache-control'] === undefined)
|
|
88
|
+
} else if (opts.cache && opts.cache.content && opts.cache.content.cacheControl) {
|
|
89
|
+
t.equal(res.headers['cache-control'], opts.cache.content.cacheControl)
|
|
90
|
+
} else {
|
|
91
|
+
t.equal(res.headers['cache-control'], 'public, max-age=600')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (--n === 0) {
|
|
95
|
+
t.end()
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
test('space in filename', (t) => {
|
|
101
|
+
req('/test/test/fixtures/space in filename.txt', (er, res, body) => {
|
|
102
|
+
t.equal(res.statusCode, 200)
|
|
103
|
+
t.ok(body)
|
|
104
|
+
t.end()
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
test('malformed URL', (t) => {
|
|
109
|
+
req('/test%2E%git', (er, res) => {
|
|
110
|
+
t.equal(res.statusCode, 404)
|
|
111
|
+
t.end()
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
test('shenanigans', (t) => {
|
|
116
|
+
req('/%2e%2E/%2e%2E/%2e%2E/%2e%2E/%2e%2E/%2e%2E/etc/passwd', (er, res) => {
|
|
117
|
+
if (er) {
|
|
118
|
+
throw er
|
|
119
|
+
}
|
|
120
|
+
t.equal(res.statusCode, 403)
|
|
121
|
+
t.end()
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
test('shenanigans2', (t) => {
|
|
126
|
+
req('/test//foo/%2e%2E', (er, res) => {
|
|
127
|
+
if (er) {
|
|
128
|
+
throw er
|
|
129
|
+
}
|
|
130
|
+
t.equal(res.statusCode, 403)
|
|
131
|
+
t.end()
|
|
132
|
+
})
|
|
133
|
+
})
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const { test } = require('tap')
|
|
2
|
+
const { serve, stExpect } = require('./common')
|
|
3
|
+
|
|
4
|
+
test('Basic cli operation', (t) => {
|
|
5
|
+
serve([], (req) => {
|
|
6
|
+
req('/st.js', (er, res, body) => {
|
|
7
|
+
t.ifError(er) &&
|
|
8
|
+
t.equal(res.statusCode, 200) &&
|
|
9
|
+
t.equal(body.toString(), stExpect)
|
|
10
|
+
})
|
|
11
|
+
}, (er, stdout, stderr) => {
|
|
12
|
+
t.ifError(er)
|
|
13
|
+
t.match(stdout, /^listening at http:\/\/(\[::\]|0\.0\.0\.0):[0-9]+\n$/)
|
|
14
|
+
t.equal(stderr, '')
|
|
15
|
+
t.end()
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test('Listening on localhost only', (t) => {
|
|
20
|
+
serve(['--localhost'], (req) => {
|
|
21
|
+
req('/st.js', (er, res, body) => {
|
|
22
|
+
t.ifError(er) &&
|
|
23
|
+
t.equal(res.statusCode, 200) &&
|
|
24
|
+
t.equal(body.toString(), stExpect)
|
|
25
|
+
})
|
|
26
|
+
}, (er, stdout, stderr) => {
|
|
27
|
+
t.ifError(er)
|
|
28
|
+
t.match(stdout, /^listening at http:\/\/localhost:[0-9]+\n$/)
|
|
29
|
+
t.equal(stderr, '')
|
|
30
|
+
t.end()
|
|
31
|
+
})
|
|
32
|
+
})
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const fs = require('fs')
|
|
5
|
+
const request = require('request')
|
|
6
|
+
const childProcess = require('child_process')
|
|
7
|
+
const bl = require('bl')
|
|
8
|
+
|
|
9
|
+
const port = process.env.PORT || 1337
|
|
10
|
+
|
|
11
|
+
const stExpect = fs.readFileSync(require.resolve('../../st.js'), 'utf8')
|
|
12
|
+
|
|
13
|
+
// Run server with given command line arguments,
|
|
14
|
+
// then allow cbRequests to schedule a bunch of requests,
|
|
15
|
+
// finally call cbDone.
|
|
16
|
+
// cbRequests gets the req function as an argument.
|
|
17
|
+
|
|
18
|
+
function serve (args, cbRequests, cbDone) {
|
|
19
|
+
args = [require.resolve('../../bin/server.js')].concat(args || [])
|
|
20
|
+
const server = childProcess.spawn(process.execPath, args, {
|
|
21
|
+
cwd: path.dirname(path.dirname(__dirname)),
|
|
22
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
23
|
+
env: { LANG: 'C', LC_ALL: 'C' }
|
|
24
|
+
})
|
|
25
|
+
const stdout = bl()
|
|
26
|
+
const stderr = bl()
|
|
27
|
+
server.stdout.pipe(stdout)
|
|
28
|
+
server.stderr.pipe(stderr)
|
|
29
|
+
let thingsToDo = 4 // cbRequests, exit, stdout, stderr
|
|
30
|
+
let code = null
|
|
31
|
+
let signal = null
|
|
32
|
+
let cbReqEr = null
|
|
33
|
+
let outputSeen = false
|
|
34
|
+
server.once('error', (er) => {
|
|
35
|
+
thingsToDo = -10 // only call cbDone once
|
|
36
|
+
cbDone(er)
|
|
37
|
+
})
|
|
38
|
+
server.once('exit', (c, s) => {
|
|
39
|
+
code = c
|
|
40
|
+
signal = s
|
|
41
|
+
if (!outputSeen) {
|
|
42
|
+
outputSeen = true
|
|
43
|
+
--thingsToDo
|
|
44
|
+
}
|
|
45
|
+
then()
|
|
46
|
+
})
|
|
47
|
+
stdout.once('finish', then)
|
|
48
|
+
stderr.once('finish', then)
|
|
49
|
+
server.stdout.once('data', () => {
|
|
50
|
+
if (outputSeen) return
|
|
51
|
+
outputSeen = true
|
|
52
|
+
try {
|
|
53
|
+
cbRequests(req)
|
|
54
|
+
} catch (er) {
|
|
55
|
+
cbReqEr = er
|
|
56
|
+
} finally {
|
|
57
|
+
then()
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
function then () {
|
|
62
|
+
--thingsToDo
|
|
63
|
+
if (thingsToDo === 3) { // all requests done, one way or another
|
|
64
|
+
server.kill()
|
|
65
|
+
} else if (thingsToDo === 0) {
|
|
66
|
+
let er = null
|
|
67
|
+
if (cbReqEr) {
|
|
68
|
+
er = cbReqEr
|
|
69
|
+
} else if (signal !== null && signal !== 'SIGTERM') {
|
|
70
|
+
er = Error('Terminated by signal ' + signal)
|
|
71
|
+
} else if (code !== null && code !== 0) {
|
|
72
|
+
er = Error('Exited with code ' + code)
|
|
73
|
+
}
|
|
74
|
+
const o = stdout.toString(); const e = stderr.toString()
|
|
75
|
+
if (er) {
|
|
76
|
+
console.info(o)
|
|
77
|
+
console.error(e)
|
|
78
|
+
}
|
|
79
|
+
cbDone(er, o, e)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function req (url, headers, cb) {
|
|
84
|
+
if (typeof headers === 'function') {
|
|
85
|
+
cb = headers
|
|
86
|
+
headers = {}
|
|
87
|
+
}
|
|
88
|
+
if (!/:\/\//.test(url)) {
|
|
89
|
+
url = 'http://localhost:' + port + url
|
|
90
|
+
}
|
|
91
|
+
++thingsToDo
|
|
92
|
+
request({
|
|
93
|
+
encoding: null,
|
|
94
|
+
url: url,
|
|
95
|
+
headers: headers
|
|
96
|
+
}, (...args) => {
|
|
97
|
+
try {
|
|
98
|
+
cb.apply(null, args)
|
|
99
|
+
} finally {
|
|
100
|
+
then()
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports.port = port
|
|
107
|
+
module.exports.stExpect = stExpect
|
|
108
|
+
module.exports.serve = serve
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const os = require('os')
|
|
2
|
+
let { test, fail, comment } = require('tap')
|
|
3
|
+
const { serve } = require('./common')
|
|
4
|
+
const port = 1338
|
|
5
|
+
|
|
6
|
+
const otherAddress = (() => {
|
|
7
|
+
const ifaces = os.networkInterfaces()
|
|
8
|
+
for (const iface in ifaces) {
|
|
9
|
+
const addrs = ifaces[iface]
|
|
10
|
+
for (let i = 0; i < addrs.length; ++i) {
|
|
11
|
+
const addr = addrs[i].address
|
|
12
|
+
if (/^127\./.test(addr) || /^::1$/.test(addr)) { // loopback device
|
|
13
|
+
continue
|
|
14
|
+
}
|
|
15
|
+
if (/^fe80:/.test(addr)) { // link-local address
|
|
16
|
+
continue
|
|
17
|
+
}
|
|
18
|
+
return addr
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return null
|
|
22
|
+
})()
|
|
23
|
+
if (!otherAddress) {
|
|
24
|
+
fail('No non-loopback network address found', { skip: true })
|
|
25
|
+
test = () => {}
|
|
26
|
+
} else {
|
|
27
|
+
comment('Using ' + otherAddress + ' as non-localhost address')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function addr2url (addr, path) {
|
|
31
|
+
if (/:/.test(addr)) {
|
|
32
|
+
addr = '[' + addr + ']'
|
|
33
|
+
}
|
|
34
|
+
addr = 'http://' + addr + ':' + port
|
|
35
|
+
if (path) {
|
|
36
|
+
addr += path
|
|
37
|
+
}
|
|
38
|
+
return addr
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function testServer (name, args, addr, canConnect, cannotConnect) {
|
|
42
|
+
test(name, (t) => {
|
|
43
|
+
serve(args.concat(['--port', port]), (req) => {
|
|
44
|
+
canConnect.forEach(checkConnections(t, req, true))
|
|
45
|
+
cannotConnect.forEach(checkConnections(t, req, false))
|
|
46
|
+
}, (err, stdout, stderr) => {
|
|
47
|
+
t.ifError(err)
|
|
48
|
+
t.equal(stderr, '')
|
|
49
|
+
if (addr) {
|
|
50
|
+
t.equal(stdout, 'listening at ' + addr2url(addr) + '\n')
|
|
51
|
+
}
|
|
52
|
+
t.end()
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function checkConnections (t, req, canConnect) {
|
|
58
|
+
return (addr) => {
|
|
59
|
+
const url = addr2url(addr, '/st.js')
|
|
60
|
+
req(url, (er, res, body) => {
|
|
61
|
+
if (canConnect) {
|
|
62
|
+
t.ifError(er, url) && t.equal(res.statusCode, 200, url)
|
|
63
|
+
} else {
|
|
64
|
+
t.ok(er, url)
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
testServer(
|
|
71
|
+
'Listening on all ports by default',
|
|
72
|
+
[], null,
|
|
73
|
+
['127.0.0.1', 'localhost', otherAddress], []
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
testServer(
|
|
77
|
+
'Restricted to localhost',
|
|
78
|
+
['--localhost'], 'localhost',
|
|
79
|
+
['127.0.0.1', 'localhost'], [otherAddress]
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
testServer(
|
|
83
|
+
'Restricted to non-local host',
|
|
84
|
+
['--host', otherAddress], otherAddress,
|
|
85
|
+
[otherAddress], ['127.0.0.1']
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
testServer(
|
|
89
|
+
'Restricted to IPv4',
|
|
90
|
+
['--host', '127.0.0.1'], '127.0.0.1',
|
|
91
|
+
['127.0.0.1'], ['::1']
|
|
92
|
+
)
|
package/test/common.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const http = require('http')
|
|
4
|
+
const request = require('request')
|
|
5
|
+
const { test, tearDown } = require('tap')
|
|
6
|
+
|
|
7
|
+
const st = require('../st.js')
|
|
8
|
+
|
|
9
|
+
let address
|
|
10
|
+
let server
|
|
11
|
+
|
|
12
|
+
const opts = Object.assign({
|
|
13
|
+
autoindex: true,
|
|
14
|
+
path: path.dirname(__dirname),
|
|
15
|
+
url: '/test'
|
|
16
|
+
}, global.options || {})
|
|
17
|
+
|
|
18
|
+
const stExpect = fs.readFileSync(require.resolve('../st.js'), 'utf8')
|
|
19
|
+
const mount = st(opts)
|
|
20
|
+
|
|
21
|
+
function req (url, headers, cb) {
|
|
22
|
+
if (typeof headers === 'function') {
|
|
23
|
+
cb = headers
|
|
24
|
+
headers = {}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let host = address.address
|
|
28
|
+
if (address.family === 'IPv6') {
|
|
29
|
+
host = `[${host}]`
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
request({
|
|
33
|
+
encoding: null,
|
|
34
|
+
url: `http://${host}:${address.port}${url}`,
|
|
35
|
+
headers: headers,
|
|
36
|
+
followRedirect: false
|
|
37
|
+
}, cb)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
test('setup', (t) => {
|
|
41
|
+
server = http.createServer((req, res) => {
|
|
42
|
+
try {
|
|
43
|
+
if (!mount(req, res)) {
|
|
44
|
+
res.statusCode = 404
|
|
45
|
+
return res.end(`Not a match: ${req.url}`)
|
|
46
|
+
}
|
|
47
|
+
} catch (e) {
|
|
48
|
+
res.statusCode = 500
|
|
49
|
+
console.error(e)
|
|
50
|
+
return res.end(`Internal error: ${e.message}`)
|
|
51
|
+
}
|
|
52
|
+
}).listen(0, '127.0.0.1', () => {
|
|
53
|
+
t.pass('listening')
|
|
54
|
+
address = server.address()
|
|
55
|
+
t.end()
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
tearDown(() => {
|
|
60
|
+
server.close()
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
module.exports.mount = mount
|
|
64
|
+
module.exports.req = req
|
|
65
|
+
module.exports.stExpect = stExpect
|
|
66
|
+
module.exports.opts = opts
|
package/test/cors.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
global.options = {
|
|
2
|
+
cors: true
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
const { test } = require('tap')
|
|
6
|
+
const { req } = require('./common')
|
|
7
|
+
|
|
8
|
+
test('CORS headers', (t) => {
|
|
9
|
+
req('/test/st.js', (er, res) => {
|
|
10
|
+
t.equal(res.headers['access-control-allow-origin'], '*')
|
|
11
|
+
const headers = res.headers['access-control-allow-headers']
|
|
12
|
+
t.ok(/Origin/.test(headers))
|
|
13
|
+
t.ok(/X-Requested-With/.test(headers))
|
|
14
|
+
t.ok(/Content-Type/.test(headers))
|
|
15
|
+
t.ok(/Accept/.test(headers))
|
|
16
|
+
t.ok(/Range/.test(headers))
|
|
17
|
+
t.end()
|
|
18
|
+
})
|
|
19
|
+
})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const http = require('http')
|
|
3
|
+
const request = require('request')
|
|
4
|
+
const { test, tearDown } = require('tap')
|
|
5
|
+
|
|
6
|
+
const st = require('../st.js')
|
|
7
|
+
|
|
8
|
+
let address
|
|
9
|
+
let server
|
|
10
|
+
|
|
11
|
+
const opts = {
|
|
12
|
+
dot: global.dot,
|
|
13
|
+
path: path.join(__dirname, 'fixtures', '.dotted-dir')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const mount = st(opts)
|
|
17
|
+
|
|
18
|
+
const req = (url, cb) => {
|
|
19
|
+
let host = address.address
|
|
20
|
+
if (address.family === 'IPv6') {
|
|
21
|
+
host = `[${host}]`
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
request({ url: `http://${host}:${address.port}${url}` }, cb)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
test('setup', (t) => {
|
|
28
|
+
server = http.createServer((req, res) => {
|
|
29
|
+
if (!mount(req, res)) {
|
|
30
|
+
res.statusCode = 404
|
|
31
|
+
return res.end(`Not a match: ${req.url}`)
|
|
32
|
+
}
|
|
33
|
+
}).listen(0, '127.0.0.1', () => {
|
|
34
|
+
t.pass('listening')
|
|
35
|
+
address = server.address()
|
|
36
|
+
t.end()
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
tearDown(() => {
|
|
41
|
+
server.close()
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
module.exports.req = req
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
global.dot = false
|
|
2
|
+
|
|
3
|
+
const { test } = require('tap')
|
|
4
|
+
const { req } = require('./dot-common')
|
|
5
|
+
|
|
6
|
+
// failing per https://github.com/isaacs/st/issues/67
|
|
7
|
+
test('non-dotted file', (t) => {
|
|
8
|
+
req('/index.html', (er, res, body) => {
|
|
9
|
+
t.equal(res.statusCode, 200)
|
|
10
|
+
t.end()
|
|
11
|
+
})
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test('dotted file', (t) => {
|
|
15
|
+
req('/.index.html', (er, res, body) => {
|
|
16
|
+
t.equal(res.statusCode, 403)
|
|
17
|
+
t.end()
|
|
18
|
+
})
|
|
19
|
+
})
|
package/test/dot-true.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
global.dot = true
|
|
2
|
+
|
|
3
|
+
const { test } = require('tap')
|
|
4
|
+
const { req } = require('./dot-common')
|
|
5
|
+
|
|
6
|
+
test('non-dotted file', (t) => {
|
|
7
|
+
req('/index.html', (er, res, body) => {
|
|
8
|
+
t.error(er)
|
|
9
|
+
t.equal(res.statusCode, 200)
|
|
10
|
+
t.end()
|
|
11
|
+
})
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test('dotted file', (t) => {
|
|
15
|
+
req('/.index.html', (er, res, body) => {
|
|
16
|
+
t.error(er)
|
|
17
|
+
t.equal(res.statusCode, 200)
|
|
18
|
+
t.end()
|
|
19
|
+
})
|
|
20
|
+
})
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
global.options = {
|
|
2
|
+
cache: {
|
|
3
|
+
content: {
|
|
4
|
+
// testing an arbitrary string can be passed through to override
|
|
5
|
+
// the header value, YES the misspellings are intentional
|
|
6
|
+
cacheControl: 'pubic, marx-aged=-100'
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// otherwise just the same as basic.
|
|
12
|
+
require('./basic.js')
|
package/test/fd-limit.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
<head><title>Index of {url}</title></head>
|
|
3
|
+
<body bgcolor="white">
|
|
4
|
+
<h1>Index of {url}</h1><hr><pre><a href="../">../</a>
|
|
5
|
+
<!-- dirs first -->
|
|
6
|
+
<!-- for each show this:
|
|
7
|
+
<a href="{filename}">{filename}</a> {modified date} {size}
|
|
8
|
+
-->
|
|
9
|
+
<!-- line up the fields nicely -->
|
|
10
|
+
</pre><hr></body>
|
|
11
|
+
</html>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
<head><title>Index of {url}</title></head>
|
|
3
|
+
<body bgcolor="white">
|
|
4
|
+
<h1>Index of {url}</h1><hr><pre><a href="../">../</a>
|
|
5
|
+
<!-- dirs first -->
|
|
6
|
+
<!-- for each show this:
|
|
7
|
+
<a href="{filename}">{filename}</a> {modified date} {size}
|
|
8
|
+
-->
|
|
9
|
+
<!-- line up the fields nicely -->
|
|
10
|
+
</pre><hr></body>
|
|
11
|
+
</html>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
<head><title>Index of {url}</title></head>
|
|
3
|
+
<body bgcolor="white">
|
|
4
|
+
<h1>Index of {url}</h1><hr><pre><a href="../">../</a>
|
|
5
|
+
<!-- dirs first -->
|
|
6
|
+
<!-- for each show this:
|
|
7
|
+
<a href="{filename}">{filename}</a> {modified date} {size}
|
|
8
|
+
-->
|
|
9
|
+
<!-- line up the fields nicely -->
|
|
10
|
+
</pre><hr></body>
|
|
11
|
+
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
space in filename
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
global.options = {
|
|
2
|
+
cachedHeader: true // inspect to see if something is served from cache
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
const zlib = require('zlib')
|
|
6
|
+
const { req, stExpect } = require('./common.js')
|
|
7
|
+
const { test } = require('tap')
|
|
8
|
+
|
|
9
|
+
test('does not gzip first response', (t) => {
|
|
10
|
+
req('/test/st.js', { 'accept-encoding': 'none' }, (er, res, body) => {
|
|
11
|
+
t.equal(res.statusCode, 200)
|
|
12
|
+
t.notOk(res.headers['content-encoding'])
|
|
13
|
+
t.notOk(res.headers['x-from-cache'])
|
|
14
|
+
t.equal(body.toString(), stExpect)
|
|
15
|
+
t.end()
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test('gzips second response', (t) => {
|
|
20
|
+
req('/test/st.js', { 'accept-encoding': 'gzip' }, (er, res, body) => {
|
|
21
|
+
t.error(er, 'no error')
|
|
22
|
+
|
|
23
|
+
t.equal(res.statusCode, 200)
|
|
24
|
+
t.equal(res.headers['content-encoding'], 'gzip')
|
|
25
|
+
t.equal(res.headers['x-from-cache'], 'true')
|
|
26
|
+
|
|
27
|
+
t.ok(body, 'returned a body')
|
|
28
|
+
t.notEqual(body.toString(), stExpect, 'gzipped string')
|
|
29
|
+
|
|
30
|
+
zlib.gunzip(body, (er, body) => {
|
|
31
|
+
if (er) {
|
|
32
|
+
throw er
|
|
33
|
+
}
|
|
34
|
+
t.equal(body.toString(), stExpect)
|
|
35
|
+
t.end()
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const st = require('../st.js')
|
|
2
|
+
const { test } = require('tap')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
|
|
5
|
+
const opts = Object.assign({
|
|
6
|
+
autoindex: true,
|
|
7
|
+
path: path.dirname(__dirname),
|
|
8
|
+
url: '/test'
|
|
9
|
+
}, global.options || {})
|
|
10
|
+
|
|
11
|
+
const mount = st(opts)
|
|
12
|
+
|
|
13
|
+
test('call next() if asset not found', (t) => {
|
|
14
|
+
const req = { url: '/does-not-exist?a=b' }
|
|
15
|
+
t.plan(1)
|
|
16
|
+
mount(req, req, () => t.equal(req.url, '/does-not-exist?a=b'))
|
|
17
|
+
})
|