koa-classic-server 3.0.0-alpha.0 → 3.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/CLAUDE.md +101 -0
- package/README.md +550 -635
- package/__tests__/benchmark-results-v3.0.0.txt +372 -0
- package/__tests__/benchmark.js +1 -1
- package/__tests__/compression.test.js +17 -3
- package/__tests__/customTest/serversToLoad.util.js +4 -4
- package/__tests__/demo-regex-index.js +4 -4
- package/__tests__/directory-sorting-links.test.js +1 -1
- package/__tests__/dt-unknown.test.js +19 -19
- package/__tests__/ejs.test.js +1 -1
- package/__tests__/hidden-option.test.js +48 -63
- package/__tests__/hideExtension.test.js +70 -13
- package/__tests__/index.test.js +6 -6
- package/__tests__/listing.test.js +437 -0
- package/__tests__/logger.test.js +232 -0
- package/__tests__/range.test.js +2 -2
- package/__tests__/security-headers.test.js +20 -8
- package/__tests__/security.test.js +5 -5
- package/__tests__/server-cache.test.js +178 -7
- package/__tests__/symlink.test.js +10 -10
- package/__tests__/template-timeout.test.js +321 -0
- package/docs/CHANGELOG.md +209 -4
- package/docs/CODE_REVIEW.md +2 -0
- package/docs/DOCUMENTATION.md +259 -32
- package/docs/EXAMPLES_INDEX_OPTION.md +1 -1
- package/docs/FLOW_DIAGRAM.md +2 -0
- package/docs/INDEX_OPTION_PRIORITY.md +2 -2
- package/docs/OPTIMIZATION_HTTP_CACHING.md +2 -0
- package/docs/security_improvement_for_V3.md +421 -0
- package/docs/template-engine/TEMPLATE_ENGINE_GUIDE.md +5 -5
- package/docs/template-engine/esempi-incrementali.js +1 -1
- package/index.cjs +551 -178
- package/package.json +6 -1
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
const supertest = require('supertest');
|
|
2
|
+
const crypto = require('crypto');
|
|
2
3
|
const koaClassicServer = require('../index.cjs');
|
|
3
4
|
const Koa = require('koa');
|
|
4
5
|
const path = require('path');
|
|
5
6
|
|
|
6
7
|
const root = path.join(__dirname, 'publicWwwTest');
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
// Compute the expected CSP hash from the actual inline CSS in the response, so
|
|
10
|
+
// CSS edits to the listing template do not require updating a hardcoded hash here.
|
|
11
|
+
async function expectedListingCsp(server, requestPath = '/') {
|
|
12
|
+
const res = await supertest(server).get(requestPath);
|
|
13
|
+
const m = res.text.match(/<style>([\s\S]*?)<\/style>/);
|
|
14
|
+
if (!m) throw new Error('No <style> block in listing HTML — cannot compute expected CSP');
|
|
15
|
+
const cssHash = 'sha256-' + crypto.createHash('sha256').update(m[1], 'utf8').digest('base64');
|
|
16
|
+
return `default-src 'none'; style-src '${cssHash}'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'`;
|
|
17
|
+
}
|
|
18
|
+
|
|
9
19
|
const NOT_FOUND_CSP = "default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'";
|
|
10
20
|
|
|
11
21
|
const COMMON_HEADERS = {
|
|
@@ -17,7 +27,7 @@ const COMMON_HEADERS = {
|
|
|
17
27
|
|
|
18
28
|
function createApp(opts = {}) {
|
|
19
29
|
const app = new Koa();
|
|
20
|
-
app.use(koaClassicServer(root, {
|
|
30
|
+
app.use(koaClassicServer(root, { dirListing: { enabled: true }, ...opts }));
|
|
21
31
|
return app.listen();
|
|
22
32
|
}
|
|
23
33
|
|
|
@@ -30,7 +40,7 @@ describe('Security headers — directory listing page', () => {
|
|
|
30
40
|
|
|
31
41
|
test('Content-Security-Policy uses hash-based style-src', async () => {
|
|
32
42
|
const res = await supertest(server).get('/');
|
|
33
|
-
expect(res.headers['content-security-policy']).toBe(
|
|
43
|
+
expect(res.headers['content-security-policy']).toBe(await expectedListingCsp(server));
|
|
34
44
|
});
|
|
35
45
|
|
|
36
46
|
test('X-Content-Type-Options: nosniff', async () => {
|
|
@@ -62,8 +72,10 @@ describe('Security headers — directory listing page', () => {
|
|
|
62
72
|
|
|
63
73
|
test('CSP style-src hash in listing matches actual inline CSS', async () => {
|
|
64
74
|
const res = await supertest(server).get('/');
|
|
65
|
-
|
|
66
|
-
expect(
|
|
75
|
+
const styleMatch = res.text.match(/<style>([\s\S]*?)<\/style>/);
|
|
76
|
+
expect(styleMatch).toBeTruthy();
|
|
77
|
+
const expectedHash = 'sha256-' + crypto.createHash('sha256').update(styleMatch[1], 'utf8').digest('base64');
|
|
78
|
+
expect(res.headers['content-security-policy']).toContain(expectedHash);
|
|
67
79
|
});
|
|
68
80
|
});
|
|
69
81
|
|
|
@@ -93,11 +105,11 @@ describe('Security headers — 404 Not Found page', () => {
|
|
|
93
105
|
});
|
|
94
106
|
});
|
|
95
107
|
|
|
96
|
-
// ─── Directory listing disabled (
|
|
108
|
+
// ─── Directory listing disabled (dirListing: { enabled: false }) ─────────────────────
|
|
97
109
|
|
|
98
110
|
describe('Security headers — 404 when directory listing disabled', () => {
|
|
99
111
|
let server;
|
|
100
|
-
beforeAll(() => { server = createApp({
|
|
112
|
+
beforeAll(() => { server = createApp({ dirListing: { enabled: false } }); });
|
|
101
113
|
afterAll(() => server.close());
|
|
102
114
|
|
|
103
115
|
test('Security headers present when directory listing disabled', async () => {
|
|
@@ -145,7 +157,7 @@ describe('Security headers — subdirectory listing page', () => {
|
|
|
145
157
|
test('Listing of subdirectory also has security headers', async () => {
|
|
146
158
|
const res = await supertest(server).get('/cartella/');
|
|
147
159
|
expect(res.status).toBe(200);
|
|
148
|
-
expect(res.headers['content-security-policy']).toBe(
|
|
160
|
+
expect(res.headers['content-security-policy']).toBe(await expectedListingCsp(server, '/cartella/'));
|
|
149
161
|
for (const [header, value] of Object.entries(COMMON_HEADERS)) {
|
|
150
162
|
expect(res.headers[header]).toBe(value);
|
|
151
163
|
}
|
|
@@ -22,7 +22,7 @@ describe('Security Tests - Path Traversal', () => {
|
|
|
22
22
|
beforeAll(() => {
|
|
23
23
|
app = new Koa();
|
|
24
24
|
app.use(koaClassicServer(rootDir, {
|
|
25
|
-
|
|
25
|
+
dirListing: { enabled: true }
|
|
26
26
|
}));
|
|
27
27
|
server = app.listen();
|
|
28
28
|
});
|
|
@@ -79,7 +79,7 @@ describe('Bug Tests - Status Code 404', () => {
|
|
|
79
79
|
beforeAll(() => {
|
|
80
80
|
app = new Koa();
|
|
81
81
|
app.use(koaClassicServer(rootDir, {
|
|
82
|
-
|
|
82
|
+
dirListing: { enabled: true }
|
|
83
83
|
}));
|
|
84
84
|
server = app.listen();
|
|
85
85
|
});
|
|
@@ -90,10 +90,10 @@ describe('Bug Tests - Status Code 404', () => {
|
|
|
90
90
|
expect(res.text).toContain('Not Found');
|
|
91
91
|
});
|
|
92
92
|
|
|
93
|
-
test('FIXED: Directory with
|
|
93
|
+
test('FIXED: Directory with dirListing.enabled=false returns 404', async () => {
|
|
94
94
|
const app2 = new Koa();
|
|
95
95
|
app2.use(koaClassicServer(rootDir, {
|
|
96
|
-
|
|
96
|
+
dirListing: { enabled: false }
|
|
97
97
|
}));
|
|
98
98
|
const server2 = app2.listen();
|
|
99
99
|
|
|
@@ -235,7 +235,7 @@ describe('Bug Tests - Directory Read Errors', () => {
|
|
|
235
235
|
const tempDir = path.join(rootDir, 'temp-perm-test-dir');
|
|
236
236
|
if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir);
|
|
237
237
|
|
|
238
|
-
app.use(koaClassicServer(rootDir, {
|
|
238
|
+
app.use(koaClassicServer(rootDir, { dirListing: { enabled: true } }));
|
|
239
239
|
const server = app.listen();
|
|
240
240
|
|
|
241
241
|
try {
|
|
@@ -28,7 +28,7 @@ const fixturesDir = path.join(__dirname, 'server-cache-fixtures');
|
|
|
28
28
|
|
|
29
29
|
function createApp(opts = {}) {
|
|
30
30
|
const app = new Koa();
|
|
31
|
-
app.use(koaClassicServer(fixturesDir, {
|
|
31
|
+
app.use(koaClassicServer(fixturesDir, { dirListing: { enabled: false }, ...opts }));
|
|
32
32
|
return app.listen();
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -124,7 +124,7 @@ describe('serverCache.rawFile — cache invalidation on file change', () => {
|
|
|
124
124
|
|
|
125
125
|
const app = new Koa();
|
|
126
126
|
app.use(koaClassicServer(tmpDir, {
|
|
127
|
-
|
|
127
|
+
dirListing: { enabled: false },
|
|
128
128
|
serverCache: { rawFile: { enabled: true } }
|
|
129
129
|
}));
|
|
130
130
|
server = app.listen();
|
|
@@ -156,6 +156,177 @@ describe('serverCache.rawFile — cache invalidation on file change', () => {
|
|
|
156
156
|
});
|
|
157
157
|
});
|
|
158
158
|
|
|
159
|
+
// ─── maxAge: factory validation ───────────────────────────────────────────────
|
|
160
|
+
|
|
161
|
+
describe('serverCache.*.maxAge — factory validation', () => {
|
|
162
|
+
test('rejects negative rawFile.maxAge', () => {
|
|
163
|
+
expect(() => koaClassicServer(fixturesDir, {
|
|
164
|
+
serverCache: { rawFile: { enabled: true, maxAge: -1 } }
|
|
165
|
+
})).toThrow(/rawFile\.maxAge must be a finite number/);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test('rejects NaN rawFile.maxAge', () => {
|
|
169
|
+
expect(() => koaClassicServer(fixturesDir, {
|
|
170
|
+
serverCache: { rawFile: { enabled: true, maxAge: NaN } }
|
|
171
|
+
})).toThrow(/rawFile\.maxAge must be a finite number/);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test('rejects Infinity rawFile.maxAge', () => {
|
|
175
|
+
expect(() => koaClassicServer(fixturesDir, {
|
|
176
|
+
serverCache: { rawFile: { enabled: true, maxAge: Infinity } }
|
|
177
|
+
})).toThrow(/rawFile\.maxAge must be a finite number/);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test('rejects string rawFile.maxAge', () => {
|
|
181
|
+
expect(() => koaClassicServer(fixturesDir, {
|
|
182
|
+
serverCache: { rawFile: { enabled: true, maxAge: '5000' } }
|
|
183
|
+
})).toThrow(/rawFile\.maxAge must be a finite number/);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test('rejects negative compressedFile.maxAge', () => {
|
|
187
|
+
expect(() => koaClassicServer(fixturesDir, {
|
|
188
|
+
serverCache: { compressedFile: { maxAge: -1 } }
|
|
189
|
+
})).toThrow(/compressedFile\.maxAge must be a finite number/);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test('accepts 0 (disabled) and positive integers', () => {
|
|
193
|
+
expect(() => koaClassicServer(fixturesDir, {
|
|
194
|
+
serverCache: { rawFile: { enabled: true, maxAge: 0 } }
|
|
195
|
+
})).not.toThrow();
|
|
196
|
+
expect(() => koaClassicServer(fixturesDir, {
|
|
197
|
+
serverCache: { rawFile: { enabled: true, maxAge: 1000 } }
|
|
198
|
+
})).not.toThrow();
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// ─── maxAge: rawFile staleness ────────────────────────────────────────────────
|
|
203
|
+
|
|
204
|
+
describe('serverCache.rawFile.maxAge — time-based staleness', () => {
|
|
205
|
+
// Verify maxAge triggers a fresh disk read even when mtime+size are unchanged.
|
|
206
|
+
// Simulates the NFS attribute-cache scenario by manually restoring mtime after
|
|
207
|
+
// editing the file, so the mtime-based invariant alone cannot detect the change.
|
|
208
|
+
let tmpDir;
|
|
209
|
+
let filePath;
|
|
210
|
+
let server;
|
|
211
|
+
|
|
212
|
+
beforeAll(() => {
|
|
213
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'kcs-maxage-raw-'));
|
|
214
|
+
filePath = path.join(tmpDir, 'data.txt');
|
|
215
|
+
fs.writeFileSync(filePath, 'version-A');
|
|
216
|
+
|
|
217
|
+
const app = new Koa();
|
|
218
|
+
app.use(koaClassicServer(tmpDir, {
|
|
219
|
+
dirListing: { enabled: false },
|
|
220
|
+
serverCache: { rawFile: { enabled: true, maxAge: 100 } }
|
|
221
|
+
}));
|
|
222
|
+
server = app.listen();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
afterAll(() => {
|
|
226
|
+
server.close();
|
|
227
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test('within maxAge: cache is served (no disk re-read)', async () => {
|
|
231
|
+
const first = await supertest(server).get('/data.txt').set('Accept-Encoding', 'identity');
|
|
232
|
+
expect(first.text).toBe('version-A');
|
|
233
|
+
|
|
234
|
+
// Replace content but freeze mtime to its original value (simulates NFS lying about mtime).
|
|
235
|
+
// Length must stay identical so the size check doesn't invalidate independently of maxAge.
|
|
236
|
+
const originalStat = fs.statSync(filePath);
|
|
237
|
+
fs.writeFileSync(filePath, 'version-B'); // same 9 bytes as 'version-A'
|
|
238
|
+
fs.utimesSync(filePath, originalStat.atime, originalStat.mtime);
|
|
239
|
+
|
|
240
|
+
// Within maxAge → stale check passes → still serves cached A
|
|
241
|
+
const second = await supertest(server).get('/data.txt').set('Accept-Encoding', 'identity');
|
|
242
|
+
expect(second.text).toBe('version-A');
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test('after maxAge: cache is refreshed (disk re-read) even when mtime+size unchanged', async () => {
|
|
246
|
+
await new Promise(r => setTimeout(r, 120)); // exceeds 100 ms maxAge
|
|
247
|
+
|
|
248
|
+
const third = await supertest(server).get('/data.txt').set('Accept-Encoding', 'identity');
|
|
249
|
+
expect(third.text).toBe('version-B');
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
describe('serverCache.rawFile.maxAge — disabled (0) preserves previous behaviour', () => {
|
|
254
|
+
let tmpDir;
|
|
255
|
+
let server;
|
|
256
|
+
|
|
257
|
+
beforeAll(() => {
|
|
258
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'kcs-maxage-off-'));
|
|
259
|
+
fs.writeFileSync(path.join(tmpDir, 'stable.txt'), 'unchanged');
|
|
260
|
+
|
|
261
|
+
const app = new Koa();
|
|
262
|
+
app.use(koaClassicServer(tmpDir, {
|
|
263
|
+
dirListing: { enabled: false },
|
|
264
|
+
serverCache: { rawFile: { enabled: true, maxAge: 0 } }
|
|
265
|
+
}));
|
|
266
|
+
server = app.listen();
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
afterAll(() => {
|
|
270
|
+
server.close();
|
|
271
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test('cache served indefinitely while mtime+size unchanged', async () => {
|
|
275
|
+
await supertest(server).get('/stable.txt').set('Accept-Encoding', 'identity');
|
|
276
|
+
await new Promise(r => setTimeout(r, 150));
|
|
277
|
+
const res = await supertest(server).get('/stable.txt').set('Accept-Encoding', 'identity');
|
|
278
|
+
expect(res.status).toBe(200);
|
|
279
|
+
expect(res.text).toBe('unchanged');
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// ─── maxAge: compressedFile staleness ─────────────────────────────────────────
|
|
284
|
+
|
|
285
|
+
describe('serverCache.compressedFile.maxAge — time-based staleness', () => {
|
|
286
|
+
let tmpDir;
|
|
287
|
+
let filePath;
|
|
288
|
+
let server;
|
|
289
|
+
|
|
290
|
+
beforeAll(() => {
|
|
291
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'kcs-maxage-cmp-'));
|
|
292
|
+
filePath = path.join(tmpDir, 'data.css');
|
|
293
|
+
fs.writeFileSync(filePath, 'a'.repeat(2048)); // above minFileSize=1024 to ensure compression
|
|
294
|
+
|
|
295
|
+
const app = new Koa();
|
|
296
|
+
app.use(koaClassicServer(tmpDir, {
|
|
297
|
+
dirListing: { enabled: false },
|
|
298
|
+
serverCache: { compressedFile: { maxAge: 100 } }
|
|
299
|
+
}));
|
|
300
|
+
server = app.listen();
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
afterAll(() => {
|
|
304
|
+
server.close();
|
|
305
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
test('after maxAge, compressed cache is rebuilt from updated content', async () => {
|
|
309
|
+
const first = await supertest(server).get('/data.css').set('Accept-Encoding', 'gzip');
|
|
310
|
+
expect(first.status).toBe(200);
|
|
311
|
+
expect(first.headers['content-encoding']).toBe('gzip');
|
|
312
|
+
expect(first.text).toBe('a'.repeat(2048));
|
|
313
|
+
|
|
314
|
+
// Replace content with same length, freeze mtime to simulate NFS staleness.
|
|
315
|
+
const originalStat = fs.statSync(filePath);
|
|
316
|
+
fs.writeFileSync(filePath, 'b'.repeat(2048));
|
|
317
|
+
fs.utimesSync(filePath, originalStat.atime, originalStat.mtime);
|
|
318
|
+
|
|
319
|
+
// Within maxAge → cached gzip of A still served.
|
|
320
|
+
const second = await supertest(server).get('/data.css').set('Accept-Encoding', 'gzip');
|
|
321
|
+
expect(second.text).toBe('a'.repeat(2048));
|
|
322
|
+
|
|
323
|
+
// After maxAge → cache refreshed → gzip of B served.
|
|
324
|
+
await new Promise(r => setTimeout(r, 120));
|
|
325
|
+
const third = await supertest(server).get('/data.css').set('Accept-Encoding', 'gzip');
|
|
326
|
+
expect(third.text).toBe('b'.repeat(2048));
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
|
|
159
330
|
// ─── rawFile + Range requests ─────────────────────────────────────────────────
|
|
160
331
|
|
|
161
332
|
describe('serverCache.rawFile — Range request served from buffer', () => {
|
|
@@ -194,7 +365,7 @@ describe('serverCache.rawFile + compression — rawFile feeds compressedFile', (
|
|
|
194
365
|
let server;
|
|
195
366
|
beforeAll(() => {
|
|
196
367
|
server = createApp({
|
|
197
|
-
compression: {
|
|
368
|
+
compression: { minFileSize: false }, // compress small.txt too
|
|
198
369
|
serverCache: { rawFile: { enabled: true } }
|
|
199
370
|
});
|
|
200
371
|
});
|
|
@@ -276,7 +447,7 @@ describe('serverCache.rawFile — LFU eviction when maxSize exceeded', () => {
|
|
|
276
447
|
|
|
277
448
|
const app = new Koa();
|
|
278
449
|
app.use(koaClassicServer(tmpDir, {
|
|
279
|
-
|
|
450
|
+
dirListing: { enabled: false },
|
|
280
451
|
serverCache: {
|
|
281
452
|
rawFile: {
|
|
282
453
|
enabled: true,
|
|
@@ -321,7 +492,7 @@ describe('serverCache.rawFile — warnInterval throttles warnings', () => {
|
|
|
321
492
|
|
|
322
493
|
const app = new Koa();
|
|
323
494
|
app.use(koaClassicServer(tmpDir, {
|
|
324
|
-
|
|
495
|
+
dirListing: { enabled: false },
|
|
325
496
|
serverCache: {
|
|
326
497
|
rawFile: {
|
|
327
498
|
enabled: true,
|
|
@@ -360,7 +531,7 @@ describe('serverCache.rawFile — buffer passed as 4th param to render function'
|
|
|
360
531
|
|
|
361
532
|
const app = new Koa();
|
|
362
533
|
app.use(koaClassicServer(tmpDir, {
|
|
363
|
-
|
|
534
|
+
dirListing: { enabled: false },
|
|
364
535
|
serverCache: { rawFile: { enabled: true } },
|
|
365
536
|
template: {
|
|
366
537
|
ext: ['tmpl'],
|
|
@@ -404,7 +575,7 @@ describe('serverCache.rawFile — buffer passed as 4th param to render function'
|
|
|
404
575
|
let bufferWhenDisabled;
|
|
405
576
|
const appNoCache = new Koa();
|
|
406
577
|
appNoCache.use(koaClassicServer(tmpDir, {
|
|
407
|
-
|
|
578
|
+
dirListing: { enabled: false },
|
|
408
579
|
// rawFile.enabled: false by default
|
|
409
580
|
template: {
|
|
410
581
|
ext: ['tmpl'],
|
|
@@ -124,7 +124,7 @@ describeIfSymlinks('koa-classic-server - symlink support', () => {
|
|
|
124
124
|
const app = new Koa();
|
|
125
125
|
app.use(koaClassicServer(tmpDir, {
|
|
126
126
|
index: ['index.html'],
|
|
127
|
-
|
|
127
|
+
dirListing: { enabled: true }
|
|
128
128
|
}));
|
|
129
129
|
server = app.listen();
|
|
130
130
|
request = supertest(server);
|
|
@@ -150,7 +150,7 @@ describeIfSymlinks('koa-classic-server - symlink support', () => {
|
|
|
150
150
|
const app = new Koa();
|
|
151
151
|
app.use(koaClassicServer(tmpDir, {
|
|
152
152
|
index: ['index.ejs'],
|
|
153
|
-
|
|
153
|
+
dirListing: { enabled: true }
|
|
154
154
|
}));
|
|
155
155
|
server = app.listen();
|
|
156
156
|
request = supertest(server);
|
|
@@ -177,7 +177,7 @@ describeIfSymlinks('koa-classic-server - symlink support', () => {
|
|
|
177
177
|
const app = new Koa();
|
|
178
178
|
app.use(koaClassicServer(tmpDir, {
|
|
179
179
|
index: [],
|
|
180
|
-
|
|
180
|
+
dirListing: { enabled: true }
|
|
181
181
|
}));
|
|
182
182
|
server = app.listen();
|
|
183
183
|
request = supertest(server);
|
|
@@ -210,7 +210,7 @@ describeIfSymlinks('koa-classic-server - symlink support', () => {
|
|
|
210
210
|
const app = new Koa();
|
|
211
211
|
app.use(koaClassicServer(tmpDir, {
|
|
212
212
|
index: ['index.ejs'],
|
|
213
|
-
|
|
213
|
+
dirListing: { enabled: true },
|
|
214
214
|
template: {
|
|
215
215
|
ext: ['ejs'],
|
|
216
216
|
render: async (ctx, next, filePath) => {
|
|
@@ -244,7 +244,7 @@ describeIfSymlinks('koa-classic-server - symlink support', () => {
|
|
|
244
244
|
const app = new Koa();
|
|
245
245
|
app.use(koaClassicServer(tmpDir, {
|
|
246
246
|
index: [],
|
|
247
|
-
|
|
247
|
+
dirListing: { enabled: true }
|
|
248
248
|
}));
|
|
249
249
|
server = app.listen();
|
|
250
250
|
request = supertest(server);
|
|
@@ -275,7 +275,7 @@ describeIfSymlinks('koa-classic-server - symlink support', () => {
|
|
|
275
275
|
const app = new Koa();
|
|
276
276
|
app.use(koaClassicServer(tmpDir, {
|
|
277
277
|
index: [],
|
|
278
|
-
|
|
278
|
+
dirListing: { enabled: true }
|
|
279
279
|
}));
|
|
280
280
|
server = app.listen();
|
|
281
281
|
request = supertest(server);
|
|
@@ -301,7 +301,7 @@ describeIfSymlinks('koa-classic-server - symlink support', () => {
|
|
|
301
301
|
const app = new Koa();
|
|
302
302
|
app.use(koaClassicServer(tmpDir, {
|
|
303
303
|
index: [],
|
|
304
|
-
|
|
304
|
+
dirListing: { enabled: true }
|
|
305
305
|
}));
|
|
306
306
|
server = app.listen();
|
|
307
307
|
request = supertest(server);
|
|
@@ -330,7 +330,7 @@ describeIfSymlinks('koa-classic-server - symlink support', () => {
|
|
|
330
330
|
const app = new Koa();
|
|
331
331
|
app.use(koaClassicServer(tmpDir, {
|
|
332
332
|
index: ['index.html'],
|
|
333
|
-
|
|
333
|
+
dirListing: { enabled: true }
|
|
334
334
|
}));
|
|
335
335
|
server = app.listen();
|
|
336
336
|
request = supertest(server);
|
|
@@ -356,7 +356,7 @@ describeIfSymlinks('koa-classic-server - symlink support', () => {
|
|
|
356
356
|
const app = new Koa();
|
|
357
357
|
app.use(koaClassicServer(tmpDir, {
|
|
358
358
|
index: [],
|
|
359
|
-
|
|
359
|
+
dirListing: { enabled: true }
|
|
360
360
|
}));
|
|
361
361
|
server = app.listen();
|
|
362
362
|
request = supertest(server);
|
|
@@ -422,7 +422,7 @@ describeIfSymlinks('koa-classic-server - symlink support', () => {
|
|
|
422
422
|
const app = new Koa();
|
|
423
423
|
app.use(koaClassicServer(tmpDir, {
|
|
424
424
|
index: [/index\.[eE][jJ][sS]/],
|
|
425
|
-
|
|
425
|
+
dirListing: { enabled: true }
|
|
426
426
|
}));
|
|
427
427
|
server = app.listen();
|
|
428
428
|
request = supertest(server);
|