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.
@@ -0,0 +1,251 @@
1
+ const st = require('../st.js')
2
+ const test = require('tap').test
3
+ const port = process.env.PORT || 1337
4
+ const path = require('path')
5
+ const request = require('request')
6
+ const assert = require('assert')
7
+ const fs = require('fs')
8
+ const http = require('http')
9
+
10
+ let middlewareServer
11
+ let server
12
+
13
+ // mount the dirname on the /test url
14
+ const mount1 = st({
15
+ autoindex: true,
16
+ path: path.dirname(__dirname),
17
+ url: '/test',
18
+ cache: {
19
+ fd: {
20
+ max: 3
21
+ }
22
+ }
23
+ })
24
+
25
+ // mount the test dir on the /blerg url
26
+ const mount2 = st({
27
+ autoindex: true,
28
+ path: __dirname,
29
+ url: '/blerg',
30
+ cache: {
31
+ fd: {
32
+ max: 3
33
+ }
34
+ }
35
+ })
36
+
37
+ function req (url, headers, cb) {
38
+ if (typeof headers === 'function') {
39
+ cb = headers
40
+ headers = {}
41
+ }
42
+
43
+ let reqs = 0
44
+ let errState = null
45
+ let prev = null
46
+
47
+ request({
48
+ encoding: null,
49
+ url: `http://localhost:${port}${url}`,
50
+ headers: headers,
51
+ agentOptions: { maxSockets: 50 }
52
+ }, next)
53
+ request({
54
+ encoding: null,
55
+ url: `http://localhost:${(port + 1)}${url}`,
56
+ headers: headers,
57
+ agentOptions: { maxSockets: 50 }
58
+ }, next)
59
+
60
+ function next (er, res, body) {
61
+ if (errState) {
62
+ return
63
+ }
64
+ if (er) {
65
+ errState = er
66
+ return cb(er, res, body)
67
+ }
68
+ if (++reqs === 2) {
69
+ assert.strictEqual(res.statusCode, prev.res.statusCode)
70
+ // compare dates, they should be approximately the same
71
+ assert(Math.abs(new Date(res.headers.date).getTime() -
72
+ new Date(res.headers.date).getTime()) < 1000)
73
+ // compare the headers minus the 'date' value
74
+ res.headers.date = prev.res.headers.date = null
75
+ assert.deepStrictEqual(res.headers, prev.res.headers)
76
+ assert.strictEqual(`${body}`, `${prev.body}`)
77
+ return cb(er, res, body)
78
+ }
79
+ prev = { res: res, body: body }
80
+ }
81
+ }
82
+
83
+ test('setup middleware server', function (t) {
84
+ // using the middleware approach
85
+ middlewareServer = http.createServer(function (req, res) {
86
+ mount1(req, res, function () {
87
+ mount2(req, res, function () {
88
+ res.statusCode = 404
89
+ return res.end(`Not a match: ${req.url}`)
90
+ })
91
+ })
92
+ })
93
+ middlewareServer.listen(port, '127.0.0.1', function () {
94
+ t.pass('listening')
95
+ t.end()
96
+ })
97
+ })
98
+
99
+ test('setup regular server', function (t) {
100
+ server = http.createServer(function (req, res) {
101
+ if (!mount1(req, res) && !mount2(req, res)) {
102
+ res.statusCode = 404
103
+ return res.end(`Not a match: ${req.url}`)
104
+ }
105
+ })
106
+ server.listen(port + 1, '127.0.0.1', function () {
107
+ t.pass('listening')
108
+ t.end()
109
+ })
110
+ })
111
+
112
+ let stEtag
113
+ const stExpect = fs.readFileSync(require.resolve('../st.js')).toString()
114
+
115
+ test('/test/st.js', function (t) {
116
+ req('/test/st.js', function (er, res, body) {
117
+ t.equal(res.statusCode, 200)
118
+ t.ok(res.headers.etag)
119
+ stEtag = res.headers.etag
120
+ t.equal(body.toString(), stExpect)
121
+ t.end()
122
+ })
123
+ })
124
+
125
+ test('/test/st.js 304', function (t) {
126
+ req('/test/st.js', { 'if-none-match': stEtag }, function (er, res, body) {
127
+ t.equal(res.statusCode, 304)
128
+ t.equal(body.length, 0)
129
+ t.end()
130
+ })
131
+ })
132
+
133
+ let mmEtag
134
+ const mmExpect = fs.readFileSync(__filename, 'utf8')
135
+ test('/blerg/multi-mount.js', function (t) {
136
+ req('/blerg/multi-mount.js', function (er, res, body) {
137
+ t.equal(res.statusCode, 200)
138
+ t.ok(res.headers.etag)
139
+ mmEtag = res.headers.etag
140
+ t.equal(body.toString(), mmExpect)
141
+ t.end()
142
+ })
143
+ })
144
+
145
+ test('/test/test/multi-mount.js', function (t) {
146
+ req('/test/test/multi-mount.js', function (er, res, body) {
147
+ t.equal(res.statusCode, 200)
148
+ t.equal(mmEtag, res.headers.etag)
149
+ t.equal(body.toString(), mmExpect)
150
+ t.end()
151
+ })
152
+ })
153
+
154
+ let rmEtag
155
+ const rmExpect = fs.readFileSync(path.resolve(__dirname, '../README.md'), 'utf8')
156
+ let pjEtag
157
+ const pjExpect = fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf8')
158
+ let idEtag
159
+ const idExpect = fs.readFileSync(path.join(__dirname, '/fixtures/index.html'), 'utf8')
160
+ test('just get a few more etags', function (t) {
161
+ let n = 3
162
+
163
+ req('/test/package.json', function (er, res, body) {
164
+ if (er) {
165
+ throw er
166
+ }
167
+ t.equal(body.toString(), pjExpect)
168
+ pjEtag = res.headers.etag
169
+ if (--n === 0) {
170
+ t.end()
171
+ }
172
+ })
173
+
174
+ req('/test/README.md', function (er, res, body) {
175
+ if (er) {
176
+ throw er
177
+ }
178
+ t.equal(body.toString(), rmExpect)
179
+ rmEtag = res.headers.etag
180
+ if (--n === 0) {
181
+ t.end()
182
+ }
183
+ })
184
+
185
+ req('/test/test/fixtures/index.html', function (er, res, body) {
186
+ if (er) {
187
+ throw er
188
+ }
189
+ t.equal(body.toString(), idExpect)
190
+ idEtag = res.headers.etag
191
+ if (--n === 0) {
192
+ t.end()
193
+ }
194
+ })
195
+ })
196
+
197
+ test('many parallel requests', function (t) {
198
+ const n = 50
199
+ const reqs =
200
+ [['/test/test/multi-mount.js', mmEtag, mmExpect],
201
+ ['/blerg/multi-mount.js', mmEtag, mmExpect],
202
+ ['/test/st.js', stEtag, stExpect],
203
+ ['/test/README.md', rmEtag, rmExpect],
204
+ ['/test/package.json', pjEtag, pjExpect],
205
+ ['/test/test/fixtures/index.html', idEtag, idExpect],
206
+ ['/blerg/fixtures/index.html', idEtag, idExpect]]
207
+
208
+ let total = n * reqs.length
209
+
210
+ for (let i = 0; i < n; i++) {
211
+ reqs.forEach(function (r) {
212
+ req(r[0], next(r))
213
+ })
214
+ }
215
+
216
+ function next (r) {
217
+ return (er, res, body) => {
218
+ if (er) {
219
+ console.error('problem with', r[0])
220
+ throw er
221
+ }
222
+
223
+ t.pass(r[0])
224
+ t.ok(res)
225
+ t.ok(res.headers)
226
+ t.equal(res.headers.etag, r[1])
227
+ t.equal(body.toString(), r[2].toString())
228
+
229
+ if (--total === 0) {
230
+ process.nextTick(() => {
231
+ t.ok(require('fd')._totalOpenFds <= 6) // max of 3 fds per mount
232
+ t.end()
233
+ })
234
+ }
235
+ }
236
+ }
237
+ })
238
+
239
+ test('shutdown regular server', (t) => {
240
+ server.close(() => {
241
+ t.pass('closed')
242
+ t.end()
243
+ })
244
+ })
245
+
246
+ test('shutdown middleware server', (t) => {
247
+ middlewareServer.close(() => {
248
+ t.pass('closed')
249
+ t.end()
250
+ })
251
+ })
@@ -0,0 +1,19 @@
1
+ // turn off ALL caching.
2
+ global.options = {
3
+ cache: false
4
+ }
5
+
6
+ // otherwise just the same as basic.
7
+ const { mount } = require('./basic.js')
8
+ const { test } = require('tap')
9
+
10
+ // additional tests to ensure that it's actually not caching.
11
+
12
+ test('all caches should be empty', (t) => {
13
+ t.same(mount._this.cache.fd._cache.dump(), [])
14
+ t.same(mount._this.cache.stat._cache.dump(), [])
15
+ t.same(mount._this.cache.index._cache.dump(), [])
16
+ t.same(mount._this.cache.readdir._cache.dump(), [])
17
+ t.same(mount._this.cache.content._cache.dump(), [])
18
+ t.end()
19
+ })
@@ -0,0 +1,10 @@
1
+ global.options = {
2
+ cache: {
3
+ content: {
4
+ maxAge: false
5
+ }
6
+ }
7
+ }
8
+
9
+ // otherwise just the same as basic.
10
+ require('./basic.js')
@@ -0,0 +1,14 @@
1
+ global.options = {
2
+ cors: false
3
+ }
4
+
5
+ const { req } = require('./common')
6
+ const test = require('tap').test
7
+
8
+ test('without CORS headers', function (t) {
9
+ req('/test/st.js', function (er, res) {
10
+ t.error(er)
11
+ t.notOk(res.headers['access-control-allow-origin'])
12
+ t.end()
13
+ })
14
+ })
@@ -0,0 +1,8 @@
1
+ global.options = {
2
+ cache: {
3
+ fd: false
4
+ }
5
+ }
6
+
7
+ // otherwise just the same as basic.
8
+ require('./basic.js')
@@ -0,0 +1,32 @@
1
+ global.options = {
2
+ cache: false // cache invokes a separate path
3
+ }
4
+
5
+ const fs = require('fs')
6
+ const path = require('path')
7
+ const crypto = require('crypto')
8
+ const rimraf = require('rimraf')
9
+ const { test } = require('tap')
10
+ const { req } = require('./common.js')
11
+ const testFileName = 'no-gzip-accepted-no-cache.testfile'
12
+ const testFile = path.join(__dirname, '../', testFileName)
13
+
14
+ const rndData = crypto.randomBytes(1024 * 128).toString('hex') // significantly larger than highWaterMark
15
+
16
+ test('does not gzip the response', (t) => {
17
+ t.on('end', () => {
18
+ rimraf(testFile, () => {})
19
+ })
20
+
21
+ fs.writeFile(testFile, rndData, (err) => {
22
+ t.error(err)
23
+
24
+ req(`/test/${testFileName}`, { 'accept-encoding': 'none' }, (er, res, body) => {
25
+ t.error(er)
26
+ t.equal(res.statusCode, 200)
27
+ t.notOk(res.headers['content-encoding'])
28
+ t.equal(body.toString(), rndData)
29
+ t.end()
30
+ })
31
+ })
32
+ })
@@ -0,0 +1,31 @@
1
+ global.options = {
2
+ }
3
+
4
+ const fs = require('fs')
5
+ const path = require('path')
6
+ const crypto = require('crypto')
7
+ const rimraf = require('rimraf')
8
+ const { test } = require('tap')
9
+ const { req } = require('./common.js')
10
+ const testFileName = 'no-gzip-accepted.testfile'
11
+ const testFile = path.join(__dirname, '../', testFileName)
12
+
13
+ const rndData = crypto.randomBytes(1024 * 128).toString('hex') // significantly larger than highWaterMark
14
+
15
+ test('does not gzip the response', (t) => {
16
+ t.on('end', () => {
17
+ rimraf(testFile, () => {})
18
+ })
19
+
20
+ fs.writeFile(testFile, rndData, (err) => {
21
+ t.error(err)
22
+
23
+ req('/test/' + testFileName, { 'accept-encoding': 'none' }, (er, res, body) => {
24
+ t.error(er)
25
+ t.equal(res.statusCode, 200)
26
+ t.notOk(res.headers['content-encoding'])
27
+ t.equal(body.toString(), rndData)
28
+ t.end()
29
+ })
30
+ })
31
+ })
@@ -0,0 +1,18 @@
1
+ // turn off gzip compression
2
+ global.options = {
3
+ gzip: false
4
+ }
5
+
6
+ const { test } = require('tap')
7
+ const { req, stExpect } = require('./basic.js')
8
+
9
+ // additional test to ensure that it's actually not gzipping
10
+
11
+ test('does not gzip the response', (t) => {
12
+ req('/test/st.js', { 'accept-encoding': 'gzip' }, (er, res, body) => {
13
+ t.equal(res.statusCode, 200)
14
+ t.notOk(res.headers['content-encoding'])
15
+ t.equal(body.toString(), stExpect)
16
+ t.end()
17
+ })
18
+ })
@@ -0,0 +1,8 @@
1
+ const path = require('path')
2
+
3
+ global.options = {
4
+ path: path.dirname(__filename) + '/..'
5
+ }
6
+
7
+ // otherwise just the same as basic.
8
+ require('./basic.js')
@@ -0,0 +1,75 @@
1
+ const { test } = require('tap')
2
+ const path = require('path')
3
+ const st = require('../st.js')
4
+
5
+ const opts = Object.assign({
6
+ index: false,
7
+ path: path.resolve(__dirname, './fixtures'),
8
+ url: '/',
9
+ passthrough: true
10
+ }, global.options || {})
11
+
12
+ const mount = st(opts)
13
+
14
+ test('call next() if passthrough is set', (t) => {
15
+ const req = { method: 'GET', url: '/doesnotexist.txt', headers: {} }
16
+ const res = {
17
+ error: () => t.end(),
18
+ setHeader: () => {},
19
+ end: () => {}
20
+ }
21
+ t.plan(2)
22
+ mount(req, res, () => {
23
+ t.ok(true, 'next called with nonexistant file')
24
+ req.url = '/'
25
+ mount(req, res, () => {
26
+ t.ok(true, 'next called without indexing')
27
+ t.end()
28
+ })
29
+ })
30
+ })
31
+
32
+ const opts2 = Object.assign({
33
+ autoindex: true,
34
+ path: path.resolve(__dirname, './fixtures'),
35
+ url: '/'
36
+ }, global.options || {})
37
+ const mount2 = st(opts2)
38
+
39
+ test('return error if passthrough is not set', (t) => {
40
+ const req = { method: 'GET', url: '/doesnotexist.txt', headers: {} }
41
+ const res = {
42
+ setHeader: () => {},
43
+ error: () => {
44
+ t.ok(true, 'error used')
45
+ t.end()
46
+ },
47
+ end: () => {}
48
+ }
49
+ t.plan(1)
50
+ mount2(req, res, () => {
51
+ t.end()
52
+ })
53
+ })
54
+
55
+ test('does not set headers if passthrough is set', (t) => {
56
+ const req = { method: 'GET', url: '/doesnotexist.txt', headers: {} }
57
+ const res = {
58
+ error: () => t.end(),
59
+ _headers: [],
60
+ setHeader: (header) => {
61
+ res._headers.push(header)
62
+ },
63
+ end: () => {}
64
+ }
65
+ t.plan(2)
66
+ mount(req, res, () => {
67
+ t.notOk(res._headers.length, 'headers are not set on a non-existant file')
68
+ req.url = '/'
69
+
70
+ mount(req, res, () => {
71
+ t.notOk(res._headers.length, 'headers are not set with no index')
72
+ t.end()
73
+ })
74
+ })
75
+ })
@@ -0,0 +1,65 @@
1
+ const st = require('../st.js')
2
+ const { test, tearDown } = require('tap')
3
+ const path = require('path')
4
+ const http = require('http')
5
+ const request = require('request')
6
+ const port = process.env.PORT || 1337
7
+
8
+ const opts = Object.assign({
9
+ index: false,
10
+ path: path.dirname(__dirname),
11
+ url: '/test'
12
+ }, global.options || {})
13
+
14
+ const mount = st(opts)
15
+ let server
16
+ let cacheControl = null
17
+
18
+ function req (url, headers, cb) {
19
+ if (typeof headers === 'function') {
20
+ cb = headers
21
+ headers = {}
22
+ }
23
+ request({
24
+ encoding: null,
25
+ url: 'http://localhost:' + port + url,
26
+ headers: headers
27
+ }, cb)
28
+ }
29
+
30
+ test('setup', (t) => {
31
+ server = http.createServer((req, res) => {
32
+ if (cacheControl) {
33
+ res.setHeader('cache-control', cacheControl)
34
+ }
35
+ if (!mount(req, res)) {
36
+ res.statusCode = 404
37
+ return res.end('Not a match: ' + req.url)
38
+ }
39
+ }).listen(port, () => {
40
+ t.pass('listening')
41
+ t.end()
42
+ })
43
+ })
44
+
45
+ tearDown(() => {
46
+ server.close()
47
+ })
48
+
49
+ test('simple request', (t) => {
50
+ cacheControl = null
51
+ req('/test/st.js', (er, res, body) => {
52
+ t.error(er)
53
+ t.equal(res.headers['cache-control'], 'public, max-age=600')
54
+ t.end()
55
+ })
56
+ })
57
+
58
+ test('pre-set cache-control', (t) => {
59
+ cacheControl = 'I\'m so excited, and I just can\'t hide it'
60
+ req('/test/st.js', (er, res, body) => {
61
+ t.error(er)
62
+ t.equal(res.headers['cache-control'], cacheControl)
63
+ t.end()
64
+ })
65
+ })