st 1.1.0 → 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/test/basic.js CHANGED
@@ -1,20 +1,16 @@
1
- var zlib = require('zlib')
2
- var test = require('tap').test
3
- var common = require('./common')
1
+ const zlib = require('zlib')
2
+ const { test } = require('tap')
3
+ const { req, stExpect, opts, mount } = require('./common')
4
4
 
5
- var req = common.req
6
- var stExpect = common.stExpect
7
- var opts = common.opts
5
+ module.exports.req = req
6
+ module.exports.stExpect = stExpect
7
+ module.exports.mount = mount
8
8
 
9
- var stEtag
9
+ let stEtag
10
10
 
11
- module.exports.mount = common.mount
12
- module.exports.req = common.req
13
- module.exports.stExpect = common.stExpect
14
-
15
-
16
- test('simple request', function (t) {
17
- req('/test/st.js', function (er, res, body) {
11
+ test('simple request', (t) => {
12
+ req('/test/st.js', (er, res, body) => {
13
+ t.error(er)
18
14
  t.equal(res.statusCode, 200)
19
15
  t.ok(/\/javascript$/.test(res.headers['content-type']))
20
16
  t.ok(res.headers.etag)
@@ -24,34 +20,33 @@ test('simple request', function (t) {
24
20
  })
25
21
  })
26
22
 
27
-
28
- test('304 request', function (t) {
29
- req('/test/st.js', {'if-none-match':stEtag}, function (er, res, body) {
23
+ test('304 request', (t) => {
24
+ req('/test/st.js', { 'if-none-match': stEtag }, (er, res, body) => {
30
25
  t.equal(res.statusCode, 304)
31
26
  t.equal(body.length, 0)
32
27
  t.end()
33
28
  })
34
29
  })
35
30
 
36
-
37
31
  if (opts.gzip !== false) {
38
- test('gzip', function (t) {
39
- req('/test/st.js', {'accept-encoding':'gzip'},
40
- function (er, res, body) {
41
- t.equal(res.statusCode, 200)
42
- t.equal(res.headers['content-encoding'], 'gzip')
43
- zlib.gunzip(body, function (er, body) {
44
- if (er) throw er;
45
- t.equal(body.toString(), stExpect)
46
- t.end()
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
+ })
47
44
  })
48
- })
49
45
  })
50
46
  }
51
47
 
52
-
53
- test('multiball!', function (t) {
54
- var n = 6
48
+ test('multiball!', (t) => {
49
+ let n = 6
55
50
  req('/test/st.js', then)
56
51
  req('/test/README.md', then)
57
52
  req('/test/LICENSE', then)
@@ -60,13 +55,14 @@ test('multiball!', function (t) {
60
55
  req('/test/bin/server.js', then)
61
56
 
62
57
  function then (er, res) {
63
- if (er)
58
+ if (er) {
64
59
  throw er
60
+ }
65
61
  t.equal(res.statusCode, 200)
66
62
 
67
63
  // give them all time to close, then go again.
68
- if (--n === 0)
69
- setTimeout(function () {
64
+ if (--n === 0) {
65
+ setTimeout(() => {
70
66
  n = 6
71
67
  req('/test/st.js', then2)
72
68
  req('/test/README.md', then2)
@@ -75,50 +71,62 @@ test('multiball!', function (t) {
75
71
  req('/test/favicon.ico', then2)
76
72
  req('/test/bin/server.js', then2)
77
73
  }, 200)
74
+ }
78
75
  }
79
76
 
80
77
  function then2 (er, res) {
81
- if (er)
78
+ if (er) {
82
79
  throw er
80
+ }
83
81
 
84
82
  t.equal(res.statusCode, 200)
85
83
 
86
- if (opts.cache === false)
84
+ if (opts.cache === false) {
87
85
  t.equal(res.headers['cache-control'], 'no-cache')
88
- else if (opts.cache && opts.cache.content && opts.cache.content.maxAge === false)
86
+ } else if (opts.cache && opts.cache.content && opts.cache.content.maxAge === false) {
89
87
  t.ok(res.headers['cache-control'] === undefined)
90
- else if (opts.cache && opts.cache.content && opts.cache.content.cacheControl)
88
+ } else if (opts.cache && opts.cache.content && opts.cache.content.cacheControl) {
91
89
  t.equal(res.headers['cache-control'], opts.cache.content.cacheControl)
92
- else
90
+ } else {
93
91
  t.equal(res.headers['cache-control'], 'public, max-age=600')
92
+ }
94
93
 
95
- if (--n === 0)
94
+ if (--n === 0) {
96
95
  t.end()
96
+ }
97
97
  }
98
98
  })
99
99
 
100
-
101
- test('space in filename', function (t) {
102
- req('/test/test/fixtures/space in filename.txt', function (er, res, body) {
100
+ test('space in filename', (t) => {
101
+ req('/test/test/fixtures/space in filename.txt', (er, res, body) => {
103
102
  t.equal(res.statusCode, 200)
104
103
  t.ok(body)
105
104
  t.end()
106
105
  })
107
106
  })
108
107
 
109
-
110
- test('malformed URL', function (t) {
111
- req('/test%2E%git', function (er, res) {
108
+ test('malformed URL', (t) => {
109
+ req('/test%2E%git', (er, res) => {
112
110
  t.equal(res.statusCode, 404)
113
111
  t.end()
114
112
  })
115
113
  })
116
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
+ })
117
124
 
118
- test('shenanigans', function(t) {
119
- req('/%2e%2E/%2e%2E/%2e%2E/%2e%2E/%2e%2E/%2e%2E/etc/passwd', function(er, res) {
120
- if (er)
125
+ test('shenanigans2', (t) => {
126
+ req('/test//foo/%2e%2E', (er, res) => {
127
+ if (er) {
121
128
  throw er
129
+ }
122
130
  t.equal(res.statusCode, 403)
123
131
  t.end()
124
132
  })
@@ -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 CHANGED
@@ -1,54 +1,66 @@
1
- var path = require('path')
2
- var fs = require('fs')
3
- var http = require('http')
4
- var util = require('util')
5
- var request = require('request')
6
- var tap = require('tap')
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')
7
6
 
8
- var st = require('../st.js')
7
+ const st = require('../st.js')
9
8
 
10
- var port = process.env.PORT || 1337
11
- var test = tap.test
9
+ let address
10
+ let server
12
11
 
13
- var server
14
-
15
- var opts = util._extend({
12
+ const opts = Object.assign({
16
13
  autoindex: true,
17
14
  path: path.dirname(__dirname),
18
15
  url: '/test'
19
16
  }, global.options || {})
20
17
 
21
- var stExpect = fs.readFileSync(require.resolve('../st.js'), 'utf8')
22
- var mount = st(opts)
23
-
18
+ const stExpect = fs.readFileSync(require.resolve('../st.js'), 'utf8')
19
+ const mount = st(opts)
24
20
 
25
21
  function req (url, headers, cb) {
26
- if (typeof headers === 'function') cb = headers, headers = {}
27
- request({ encoding: null,
28
- url: 'http://localhost:' + port + url,
29
- headers: headers }, cb)
30
- }
22
+ if (typeof headers === 'function') {
23
+ cb = headers
24
+ headers = {}
25
+ }
31
26
 
27
+ let host = address.address
28
+ if (address.family === 'IPv6') {
29
+ host = `[${host}]`
30
+ }
32
31
 
33
- test('setup', function (t) {
34
- server = http.createServer(function (req, res) {
35
- if (!mount(req, res)) {
36
- res.statusCode = 404
37
- return res.end('Not a match: ' + req.url)
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}`)
38
51
  }
39
- }).listen(port, function () {
52
+ }).listen(0, '127.0.0.1', () => {
40
53
  t.pass('listening')
54
+ address = server.address()
41
55
  t.end()
42
56
  })
43
57
  })
44
58
 
45
-
46
- tap.tearDown(function() {
59
+ tearDown(() => {
47
60
  server.close()
48
61
  })
49
62
 
50
-
51
63
  module.exports.mount = mount
52
64
  module.exports.req = req
53
65
  module.exports.stExpect = stExpect
54
- module.exports.opts = opts
66
+ module.exports.opts = opts
package/test/cors.js CHANGED
@@ -2,20 +2,18 @@ global.options = {
2
2
  cors: true
3
3
  }
4
4
 
5
- var common = require('./common')
5
+ const { test } = require('tap')
6
+ const { req } = require('./common')
6
7
 
7
- var test = require('tap').test
8
- var req = common.req
9
-
10
- test('CORS headers', function (t) {
11
- req('/test/st.js', function (er, res) {
12
- t.equal(res.headers['access-control-allow-origin'], '*');
13
- var headers = res.headers['access-control-allow-headers']
14
- t.ok(/Origin/.test(headers));
15
- t.ok(/X-Requested-With/.test(headers));
16
- t.ok(/Content-Type/.test(headers));
17
- t.ok(/Accept/.test(headers));
18
- t.ok(/Range/.test(headers));
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))
19
17
  t.end()
20
18
  })
21
19
  })
@@ -1,38 +1,44 @@
1
- var path = require('path')
2
- var http = require('http')
3
- var request = require('request')
4
- var tap = require('tap')
1
+ const path = require('path')
2
+ const http = require('http')
3
+ const request = require('request')
4
+ const { test, tearDown } = require('tap')
5
5
 
6
- var st = require('../st.js')
6
+ const st = require('../st.js')
7
7
 
8
- var port = process.env.PORT || 1337
9
- var test = exports.test = tap.test
8
+ let address
9
+ let server
10
10
 
11
- var server
12
-
13
- var opts = {
11
+ const opts = {
14
12
  dot: global.dot,
15
- path: path.join(__dirname, "fixtures", ".dotted-dir"),
13
+ path: path.join(__dirname, 'fixtures', '.dotted-dir')
16
14
  }
17
15
 
18
- var mount = st(opts)
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
+ }
19
23
 
20
- exports.req = function req (url, cb) {
21
- request({ url: 'http://localhost:' + port + url }, cb)
24
+ request({ url: `http://${host}:${address.port}${url}` }, cb)
22
25
  }
23
26
 
24
- test('setup', function (t) {
25
- server = http.createServer(function (req, res) {
27
+ test('setup', (t) => {
28
+ server = http.createServer((req, res) => {
26
29
  if (!mount(req, res)) {
27
30
  res.statusCode = 404
28
- return res.end('Not a match: ' + req.url)
31
+ return res.end(`Not a match: ${req.url}`)
29
32
  }
30
- }).listen(port, function () {
33
+ }).listen(0, '127.0.0.1', () => {
31
34
  t.pass('listening')
35
+ address = server.address()
32
36
  t.end()
33
37
  })
34
38
  })
35
39
 
36
- tap.tearDown(function() {
40
+ tearDown(() => {
37
41
  server.close()
38
42
  })
43
+
44
+ module.exports.req = req
package/test/dot-false.js CHANGED
@@ -1,20 +1,18 @@
1
1
  global.dot = false
2
2
 
3
- var common = require('./dot-common')
4
-
5
- var req = common.req
6
- var test = common.test
3
+ const { test } = require('tap')
4
+ const { req } = require('./dot-common')
7
5
 
8
6
  // failing per https://github.com/isaacs/st/issues/67
9
- test('non-dotted file', function (t) {
10
- req('/index.html', function (er, res, body) {
7
+ test('non-dotted file', (t) => {
8
+ req('/index.html', (er, res, body) => {
11
9
  t.equal(res.statusCode, 200)
12
10
  t.end()
13
11
  })
14
12
  })
15
13
 
16
- test('dotted file', function (t) {
17
- req('/.index.html', function (er, res, body) {
14
+ test('dotted file', (t) => {
15
+ req('/.index.html', (er, res, body) => {
18
16
  t.equal(res.statusCode, 403)
19
17
  t.end()
20
18
  })
package/test/dot-true.js CHANGED
@@ -1,19 +1,19 @@
1
1
  global.dot = true
2
2
 
3
- var common = require('./dot-common')
3
+ const { test } = require('tap')
4
+ const { req } = require('./dot-common')
4
5
 
5
- var req = common.req
6
- var test = common.test
7
-
8
- test('non-dotted file', function (t) {
9
- req('/index.html', function (er, res, body) {
6
+ test('non-dotted file', (t) => {
7
+ req('/index.html', (er, res, body) => {
8
+ t.error(er)
10
9
  t.equal(res.statusCode, 200)
11
10
  t.end()
12
11
  })
13
12
  })
14
13
 
15
- test('dotted file', function (t) {
16
- req('/.index.html', function (er, res, body) {
14
+ test('dotted file', (t) => {
15
+ req('/.index.html', (er, res, body) => {
16
+ t.error(er)
17
17
  t.equal(res.statusCode, 200)
18
18
  t.end()
19
19
  })