koa-classic-server 3.0.0 → 3.1.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.
Files changed (105) hide show
  1. package/README.md +44 -120
  2. package/index.cjs +236 -21
  3. package/package.json +10 -5
  4. package/.github/workflows/npm-publish.yml +0 -98
  5. package/CLAUDE.md +0 -101
  6. package/__tests__/benchmark-results-baseline-v1.2.0.txt +0 -354
  7. package/__tests__/benchmark-results-optimized-v2.0.0.txt +0 -354
  8. package/__tests__/benchmark-results-v3.0.0.txt +0 -372
  9. package/__tests__/benchmark.js +0 -239
  10. package/__tests__/caching-headers.test.js +0 -556
  11. package/__tests__/compression-fixtures/data.json +0 -1
  12. package/__tests__/compression-fixtures/large.txt +0 -1
  13. package/__tests__/compression-fixtures/small.txt +0 -1
  14. package/__tests__/compression.test.js +0 -284
  15. package/__tests__/customTest/README.md +0 -6
  16. package/__tests__/customTest/loadConfig.util.js +0 -41
  17. package/__tests__/customTest/serversToLoad.util.js +0 -93
  18. package/__tests__/demo-regex-index.js +0 -140
  19. package/__tests__/deprecation-warnings.test.js +0 -105
  20. package/__tests__/directory-sorting-links.test.js +0 -135
  21. package/__tests__/dt-unknown.test.js +0 -635
  22. package/__tests__/ejs.test.js +0 -299
  23. package/__tests__/hidden-fixtures/.dot-dir/inside.txt +0 -1
  24. package/__tests__/hidden-fixtures/.env +0 -2
  25. package/__tests__/hidden-fixtures/.well-known/acme-challenge.txt +0 -1
  26. package/__tests__/hidden-fixtures/config/secrets/password.txt +0 -1
  27. package/__tests__/hidden-fixtures/data.key +0 -1
  28. package/__tests__/hidden-fixtures/file.secret +0 -1
  29. package/__tests__/hidden-fixtures/index.html +0 -1
  30. package/__tests__/hidden-fixtures/normal.txt +0 -1
  31. package/__tests__/hidden-fixtures/subdir/.env +0 -1
  32. package/__tests__/hidden-fixtures/subdir/regular.txt +0 -1
  33. package/__tests__/hidden-option.test.js +0 -407
  34. package/__tests__/hideExtension.test.js +0 -607
  35. package/__tests__/index-option.test.js +0 -449
  36. package/__tests__/index.test.js +0 -345
  37. package/__tests__/listing.test.js +0 -437
  38. package/__tests__/logger.test.js +0 -232
  39. package/__tests__/performance.test.js +0 -370
  40. package/__tests__/publicWwwTest/cartella/sottocartella/ciao.html +0 -11
  41. package/__tests__/publicWwwTest/cartella/sottocartella/provaEjs/testEjs.ejs +0 -11
  42. package/__tests__/publicWwwTest/cartella vuota con spazi nel nome/file con spazio nel nome .txt +0 -1
  43. package/__tests__/publicWwwTest/cartella vuota con spazi nel nome /file con spazio nel nome .txt +0 -1
  44. package/__tests__/publicWwwTest/ejs-templates/complex.ejs +0 -56
  45. package/__tests__/publicWwwTest/ejs-templates/index.ejs +0 -30
  46. package/__tests__/publicWwwTest/ejs-templates/simple.ejs +0 -13
  47. package/__tests__/publicWwwTest/ejs-templates/with-conditional.ejs +0 -28
  48. package/__tests__/publicWwwTest/ejs-templates/with-escaping.ejs +0 -26
  49. package/__tests__/publicWwwTest/ejs-templates/with-loop.ejs +0 -16
  50. package/__tests__/publicWwwTest/hideext-test/about/index.html +0 -1
  51. package/__tests__/publicWwwTest/hideext-test/about.EJS +0 -1
  52. package/__tests__/publicWwwTest/hideext-test/about.ejs +0 -2
  53. package/__tests__/publicWwwTest/hideext-test/blog/articolo.ejs +0 -2
  54. package/__tests__/publicWwwTest/hideext-test/conflict-test/pagina +0 -1
  55. package/__tests__/publicWwwTest/hideext-test/conflict-test/pagina.ejs +0 -1
  56. package/__tests__/publicWwwTest/hideext-test/file.ejs.bak +0 -1
  57. package/__tests__/publicWwwTest/hideext-test/index.ejs +0 -2
  58. package/__tests__/publicWwwTest/hideext-test/photo.txt +0 -1
  59. package/__tests__/publicWwwTest/hideext-test/sezione/index.ejs +0 -1
  60. package/__tests__/publicWwwTest/hideext-test/style.css +0 -1
  61. package/__tests__/publicWwwTest/ile_vuoto.txt +0 -0
  62. package/__tests__/publicWwwTest/index.html +0 -11
  63. package/__tests__/publicWwwTest/prova file .txt +0 -2
  64. package/__tests__/publicWwwTest/semplicetxt.txt +0 -1
  65. package/__tests__/publicWwwTest/test-page.html +0 -1
  66. package/__tests__/publicWwwTest/test.txt +0 -1
  67. package/__tests__/range-fixtures/sample.txt +0 -1
  68. package/__tests__/range.test.js +0 -223
  69. package/__tests__/security-headers.test.js +0 -165
  70. package/__tests__/security.test.js +0 -322
  71. package/__tests__/server-cache-fixtures/large.txt +0 -1
  72. package/__tests__/server-cache-fixtures/small.txt +0 -1
  73. package/__tests__/server-cache.test.js +0 -594
  74. package/__tests__/setup-benchmark.js +0 -178
  75. package/__tests__/symlink.test.js +0 -441
  76. package/__tests__/template-timeout.test.js +0 -321
  77. package/__tests__/test-regex-quick.js +0 -158
  78. package/__tests__/useOriginalUrl.test.js +0 -213
  79. package/docs/ACTION_PLAN.md +0 -293
  80. package/docs/BENCHMARKS.md +0 -317
  81. package/docs/CHANGELOG.md +0 -880
  82. package/docs/CODE_REVIEW.md +0 -300
  83. package/docs/DEBUG_REPORT.md +0 -593
  84. package/docs/DOCUMENTATION.md +0 -1864
  85. package/docs/EXAMPLES_INDEX_OPTION.md +0 -395
  86. package/docs/FLOW_DIAGRAM.md +0 -954
  87. package/docs/INDEX_OPTION_PRIORITY.md +0 -527
  88. package/docs/OPTIMIZATION_HTTP_CACHING.md +0 -689
  89. package/docs/OPTIMIZATION_ROADMAP_for_V3.md +0 -864
  90. package/docs/PERFORMANCE_ANALYSIS.md +0 -839
  91. package/docs/PERFORMANCE_COMPARISON.md +0 -388
  92. package/docs/noteExports.md +0 -148
  93. package/docs/security_improvement_for_V3.md +0 -421
  94. package/docs/template-engine/TEMPLATE_ENGINE_GUIDE.md +0 -1734
  95. package/docs/template-engine/esempi-incrementali.js +0 -192
  96. package/docs/template-engine/examples/esempio1-nessun-dato.ejs +0 -12
  97. package/docs/template-engine/examples/esempio2-una-variabile.ejs +0 -11
  98. package/docs/template-engine/examples/esempio3-piu-variabili.ejs +0 -15
  99. package/docs/template-engine/examples/esempio4-condizionale.ejs +0 -18
  100. package/docs/template-engine/examples/esempio5-loop.ejs +0 -18
  101. package/docs/template-engine/examples/index-esempi.html +0 -181
  102. package/docs/template-engine/examples/index.html +0 -40
  103. package/docs/template-engine/examples/test.ejs +0 -64
  104. package/eslint.config.mjs +0 -17
  105. package/jest.config.js +0 -18
@@ -1,321 +0,0 @@
1
- const Koa = require('koa');
2
- const koaClassicServer = require('../index.cjs');
3
- const supertest = require('supertest');
4
- const path = require('path');
5
-
6
- const ROOT = path.join(__dirname, 'publicWwwTest');
7
- const TEMPLATE_URL = '/ejs-templates/simple.ejs';
8
-
9
- function buildServer(renderFn, opts = {}) {
10
- const app = new Koa();
11
- app.silent = true;
12
- app.use(koaClassicServer(ROOT, {
13
- method: ['GET'],
14
- hidden: { dotFiles: { default: 'hidden' }, dotDirs: { default: 'visible' } },
15
- template: {
16
- ext: ['ejs'],
17
- render: renderFn,
18
- ...(opts.renderTimeout !== undefined ? { renderTimeout: opts.renderTimeout } : {})
19
- }
20
- }));
21
- const server = app.listen();
22
- return { server, request: supertest(server) };
23
- }
24
-
25
- // Sleep that does not keep the event loop alive — used to simulate
26
- // non-cooperative long-running work without leaving orphan timers behind.
27
- function unrefSleep(ms) {
28
- return new Promise(resolve => {
29
- const t = setTimeout(resolve, ms);
30
- if (typeof t.unref === 'function') t.unref();
31
- });
32
- }
33
-
34
- describe('template.renderTimeout', () => {
35
- let consoleErrorSpy;
36
-
37
- beforeEach(() => {
38
- consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
39
- });
40
-
41
- afterEach(() => {
42
- consoleErrorSpy.mockRestore();
43
- });
44
-
45
- describe('Factory validation', () => {
46
- test('rejects negative renderTimeout', () => {
47
- expect(() => koaClassicServer(ROOT, {
48
- template: { ext: ['ejs'], render: () => {}, renderTimeout: -1 }
49
- })).toThrow(/renderTimeout must be a finite number/);
50
- });
51
-
52
- test('rejects non-number renderTimeout', () => {
53
- expect(() => koaClassicServer(ROOT, {
54
- template: { ext: ['ejs'], render: () => {}, renderTimeout: '5000' }
55
- })).toThrow(/renderTimeout must be a finite number/);
56
- });
57
-
58
- test('rejects NaN renderTimeout', () => {
59
- expect(() => koaClassicServer(ROOT, {
60
- template: { ext: ['ejs'], render: () => {}, renderTimeout: NaN }
61
- })).toThrow(/renderTimeout must be a finite number/);
62
- });
63
-
64
- test('rejects Infinity renderTimeout', () => {
65
- expect(() => koaClassicServer(ROOT, {
66
- template: { ext: ['ejs'], render: () => {}, renderTimeout: Infinity }
67
- })).toThrow(/renderTimeout must be a finite number/);
68
- });
69
-
70
- test('accepts 0 (disabled)', () => {
71
- expect(() => koaClassicServer(ROOT, {
72
- template: { ext: ['ejs'], render: () => {}, renderTimeout: 0 }
73
- })).not.toThrow();
74
- });
75
-
76
- test('accepts positive integer', () => {
77
- expect(() => koaClassicServer(ROOT, {
78
- template: { ext: ['ejs'], render: () => {}, renderTimeout: 1000 }
79
- })).not.toThrow();
80
- });
81
-
82
- test('defaults to 30000 when undefined', () => {
83
- const opts = { template: { ext: ['ejs'], render: () => {} } };
84
- koaClassicServer(ROOT, opts);
85
- expect(opts.template.renderTimeout).toBe(30000);
86
- });
87
- });
88
-
89
- describe('Successful render within timeout', () => {
90
- let env;
91
- afterEach(() => env && env.server.close());
92
-
93
- test('returns 200 when render completes before timeout', async () => {
94
- env = buildServer(async (ctx) => {
95
- await new Promise(r => setTimeout(r, 20));
96
- ctx.type = 'text/html';
97
- ctx.body = '<p>ok</p>';
98
- }, { renderTimeout: 500 });
99
-
100
- const res = await env.request.get(TEMPLATE_URL);
101
- expect(res.status).toBe(200);
102
- expect(res.text).toContain('ok');
103
- });
104
- });
105
-
106
- describe('Timeout behaviour', () => {
107
- let env;
108
- afterEach(() => env && env.server.close());
109
-
110
- test('returns 504 when render exceeds timeout', async () => {
111
- env = buildServer(async () => {
112
- await unrefSleep(5000);
113
- }, { renderTimeout: 100 });
114
-
115
- const res = await env.request.get(TEMPLATE_URL);
116
- expect(res.status).toBe(504);
117
- expect(res.text).toContain('Gateway Timeout');
118
- expect(res.text).toContain('took too long to render');
119
- });
120
-
121
- test('504 response carries security headers', async () => {
122
- env = buildServer(async () => {
123
- await unrefSleep(5000);
124
- }, { renderTimeout: 50 });
125
-
126
- const res = await env.request.get(TEMPLATE_URL);
127
- expect(res.status).toBe(504);
128
- expect(res.headers['content-security-policy']).toMatch(/default-src 'none'/);
129
- expect(res.headers['x-content-type-options']).toBe('nosniff');
130
- expect(res.headers['x-frame-options']).toBe('DENY');
131
- });
132
-
133
- test('renderTimeout: 0 disables the timer (render is allowed to run long)', async () => {
134
- env = buildServer(async (ctx) => {
135
- await new Promise(r => setTimeout(r, 200));
136
- ctx.type = 'text/html';
137
- ctx.body = '<p>slow but ok</p>';
138
- }, { renderTimeout: 0 });
139
-
140
- const res = await env.request.get(TEMPLATE_URL);
141
- expect(res.status).toBe(200);
142
- expect(res.text).toContain('slow but ok');
143
- });
144
- });
145
-
146
- describe('Render argument contract', () => {
147
- let env;
148
- afterEach(() => env && env.server.close());
149
-
150
- test('render is called with (ctx, next, filePath, rawBuffer, signal) in that order', async () => {
151
- let received;
152
- env = buildServer(async (...args) => {
153
- received = args;
154
- args[0].body = 'ok';
155
- }, { renderTimeout: 1000 });
156
-
157
- await env.request.get(TEMPLATE_URL);
158
-
159
- expect(received).toHaveLength(5);
160
- // ctx: Koa context — must expose req/res/state
161
- expect(received[0]).toBeDefined();
162
- expect(received[0].req).toBeDefined();
163
- expect(received[0].res).toBeDefined();
164
- expect(received[0].state).toBeDefined();
165
- // next: function (downstream middleware)
166
- expect(typeof received[1]).toBe('function');
167
- // filePath: absolute path to the requested file
168
- expect(typeof received[2]).toBe('string');
169
- expect(path.isAbsolute(received[2])).toBe(true);
170
- expect(received[2]).toMatch(/simple\.ejs$/);
171
- // rawBuffer: Buffer or null depending on serverCache.rawFile state
172
- expect(received[3] === null || Buffer.isBuffer(received[3])).toBe(true);
173
- // signal: AbortSignal
174
- expect(received[4]).toBeInstanceOf(AbortSignal);
175
- });
176
- });
177
-
178
- describe('AbortSignal contract', () => {
179
- let env;
180
- afterEach(() => env && env.server.close());
181
-
182
- test('passes an AbortSignal as 5th argument', async () => {
183
- let receivedSignal;
184
- env = buildServer(async (ctx, _next, _path, _buf, signal) => {
185
- receivedSignal = signal;
186
- ctx.body = 'ok';
187
- }, { renderTimeout: 1000 });
188
-
189
- await env.request.get(TEMPLATE_URL);
190
- expect(receivedSignal).toBeInstanceOf(AbortSignal);
191
- expect(receivedSignal.aborted).toBe(false);
192
- });
193
-
194
- test('signal is NOT aborted after a successful render completes', async () => {
195
- let signalAtEnd;
196
- let signalRef;
197
- env = buildServer(async (ctx, _next, _path, _buf, signal) => {
198
- signalRef = signal;
199
- await new Promise(r => setTimeout(r, 20));
200
- signalAtEnd = signal.aborted;
201
- ctx.type = 'text/html';
202
- ctx.body = '<p>done</p>';
203
- }, { renderTimeout: 1000 });
204
-
205
- const res = await env.request.get(TEMPLATE_URL);
206
- expect(res.status).toBe(200);
207
- // Signal must not be aborted at the end of a successful render
208
- expect(signalAtEnd).toBe(false);
209
- // ...and must remain non-aborted after the handler finishes
210
- // (i.e. our cleanup must not abort it as a side-effect)
211
- await new Promise(r => setTimeout(r, 50));
212
- expect(signalRef.aborted).toBe(false);
213
- });
214
-
215
- test('signal aborts when render times out', async () => {
216
- let signalAtTimeout = null;
217
- env = buildServer(async (ctx, _next, _path, _buf, signal) => {
218
- await unrefSleep(300);
219
- signalAtTimeout = signal.aborted;
220
- }, { renderTimeout: 50 });
221
-
222
- const res = await env.request.get(TEMPLATE_URL);
223
- expect(res.status).toBe(504);
224
- await new Promise(r => setTimeout(r, 400));
225
- expect(signalAtTimeout).toBe(true);
226
- });
227
-
228
- test('cooperative render that honours signal terminates early', async () => {
229
- const renderDuration = jest.fn();
230
- env = buildServer(async (ctx, _next, _path, _buf, signal) => {
231
- const start = Date.now();
232
- try {
233
- await new Promise((resolve, reject) => {
234
- const t = setTimeout(resolve, 5000);
235
- signal.addEventListener('abort', () => {
236
- clearTimeout(t);
237
- reject(new Error('aborted'));
238
- });
239
- });
240
- } finally {
241
- renderDuration(Date.now() - start);
242
- }
243
- }, { renderTimeout: 50 });
244
-
245
- const res = await env.request.get(TEMPLATE_URL);
246
- expect(res.status).toBe(504);
247
- await new Promise(r => setTimeout(r, 200));
248
- const elapsed = renderDuration.mock.calls[0]?.[0] ?? 9999;
249
- expect(elapsed).toBeLessThan(1000);
250
- });
251
- });
252
-
253
- describe('Error handling integrity', () => {
254
- let env;
255
- afterEach(() => env && env.server.close());
256
-
257
- test('late rejection after timeout does not crash the process', async () => {
258
- const unhandledHandler = jest.fn();
259
- process.on('unhandledRejection', unhandledHandler);
260
-
261
- env = buildServer(async () => {
262
- await new Promise(r => setTimeout(r, 100));
263
- throw new Error('late failure');
264
- }, { renderTimeout: 30 });
265
-
266
- const res = await env.request.get(TEMPLATE_URL);
267
- expect(res.status).toBe(504);
268
-
269
- await new Promise(r => setTimeout(r, 250));
270
- process.off('unhandledRejection', unhandledHandler);
271
- expect(unhandledHandler).not.toHaveBeenCalled();
272
- });
273
-
274
- test('synchronous render returning rejected promise still produces 500', async () => {
275
- env = buildServer(() => Promise.reject(new Error('boom')), { renderTimeout: 1000 });
276
-
277
- const res = await env.request.get(TEMPLATE_URL);
278
- expect(res.status).toBe(500);
279
- expect(res.text).toContain('Internal Server Error');
280
- });
281
-
282
- test('synchronous throw in render produces 500', async () => {
283
- env = buildServer(() => { throw new Error('sync boom'); }, { renderTimeout: 1000 });
284
-
285
- const res = await env.request.get(TEMPLATE_URL);
286
- expect(res.status).toBe(500);
287
- expect(res.text).toContain('Internal Server Error');
288
- });
289
- });
290
-
291
- describe('Client disconnect', () => {
292
- let env;
293
- afterEach(() => env && env.server.close());
294
-
295
- test('signal aborts when client closes the connection before render finishes', async () => {
296
- let abortObserved = false;
297
- env = buildServer(async (ctx, _next, _path, _buf, signal) => {
298
- signal.addEventListener('abort', () => { abortObserved = true; });
299
- await unrefSleep(1000);
300
- ctx.body = 'late';
301
- }, { renderTimeout: 0 });
302
-
303
- const addr = env.server.address();
304
- const http = require('http');
305
- await new Promise((resolve) => {
306
- const req = http.request({
307
- host: addr.address === '::' ? '127.0.0.1' : addr.address,
308
- port: addr.port,
309
- path: TEMPLATE_URL,
310
- method: 'GET'
311
- }, () => {});
312
- req.on('error', () => {});
313
- req.end();
314
- setTimeout(() => { req.destroy(); resolve(); }, 50);
315
- });
316
-
317
- await new Promise(r => setTimeout(r, 100));
318
- expect(abortObserved).toBe(true);
319
- });
320
- });
321
- });
@@ -1,158 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Quick test: RegExp index option
5
- */
6
-
7
- const Koa = require('koa');
8
- const koaClassicServer = require('./index.cjs');
9
- const fs = require('fs');
10
- const path = require('path');
11
- const http = require('http');
12
-
13
- // Crea directory di test
14
- const testDir = path.join(__dirname, 'test-regex-temp');
15
- if (fs.existsSync(testDir)) {
16
- fs.readdirSync(testDir).forEach(file => {
17
- fs.unlinkSync(path.join(testDir, file));
18
- });
19
- fs.rmdirSync(testDir);
20
- }
21
- fs.mkdirSync(testDir);
22
-
23
- // Test case 1: Case-insensitive matching
24
- console.log('\n📋 TEST 1: Case-insensitive /index\\.html/i\n');
25
- console.log('File creati:');
26
- console.log(' - INDEX.HTML (maiuscolo)');
27
- console.log(' - other.txt\n');
28
-
29
- fs.writeFileSync(path.join(testDir, 'INDEX.HTML'), '<h1>INDEX.HTML (MAIUSCOLO)</h1>');
30
- fs.writeFileSync(path.join(testDir, 'other.txt'), 'altro file');
31
-
32
- const app1 = new Koa();
33
- app1.use(koaClassicServer(testDir, {
34
- index: [/index\.html/i] // ← REGEXP CASE-INSENSITIVE!
35
- }));
36
-
37
- const server1 = app1.listen(9001);
38
-
39
- // Test la richiesta
40
- setTimeout(() => {
41
- http.get('http://localhost:9001/', (res) => {
42
- let data = '';
43
- res.on('data', chunk => data += chunk);
44
- res.on('end', () => {
45
- if (data.includes('INDEX.HTML (MAIUSCOLO)')) {
46
- console.log('✅ SUCCESS: Il server ha trovato INDEX.HTML usando /index\\.html/i');
47
- console.log(' Contenuto ricevuto: ' + data.trim());
48
- } else {
49
- console.log('❌ FAIL: Pattern non ha matchato');
50
- console.log(' Contenuto ricevuto: ' + data);
51
- }
52
-
53
- server1.close();
54
-
55
- // Test case 2
56
- runTest2();
57
- });
58
- });
59
- }, 100);
60
-
61
- function runTest2() {
62
- // Pulisci directory
63
- fs.readdirSync(testDir).forEach(file => {
64
- fs.unlinkSync(path.join(testDir, file));
65
- });
66
-
67
- console.log('\n📋 TEST 2: Multi-extension /index\\.(html|htm)/i\n');
68
- console.log('File creati:');
69
- console.log(' - Index.HTM (mixed case, estensione .htm)');
70
- console.log(' - other.html\n');
71
-
72
- fs.writeFileSync(path.join(testDir, 'Index.HTM'), '<h1>Index.HTM (mixed case)</h1>');
73
- fs.writeFileSync(path.join(testDir, 'other.html'), '<h1>altro</h1>');
74
-
75
- const app2 = new Koa();
76
- app2.use(koaClassicServer(testDir, {
77
- index: [/index\.(html|htm)/i] // ← REGEXP con (html|htm)!
78
- }));
79
-
80
- const server2 = app2.listen(9002);
81
-
82
- setTimeout(() => {
83
- http.get('http://localhost:9002/', (res) => {
84
- let data = '';
85
- res.on('data', chunk => data += chunk);
86
- res.on('end', () => {
87
- if (data.includes('Index.HTM (mixed case)')) {
88
- console.log('✅ SUCCESS: Il server ha trovato Index.HTM usando /index\\.(html|htm)/i');
89
- console.log(' Contenuto ricevuto: ' + data.trim());
90
- } else {
91
- console.log('❌ FAIL: Pattern non ha matchato');
92
- console.log(' Contenuto ricevuto: ' + data);
93
- }
94
-
95
- server2.close();
96
-
97
- // Test case 3
98
- runTest3();
99
- });
100
- });
101
- }, 100);
102
- }
103
-
104
- function runTest3() {
105
- // Pulisci directory
106
- fs.readdirSync(testDir).forEach(file => {
107
- fs.unlinkSync(path.join(testDir, file));
108
- });
109
-
110
- console.log('\n📋 TEST 3: Array con priorità [/index\\.html/i, /default\\.html/i]\n');
111
- console.log('File creati:');
112
- console.log(' - DEFAULT.HTML (maiuscolo)');
113
- console.log(' - other.txt\n');
114
- console.log('Nota: index.html NON esiste, dovrebbe trovare default.html\n');
115
-
116
- fs.writeFileSync(path.join(testDir, 'DEFAULT.HTML'), '<h1>DEFAULT.HTML (fallback)</h1>');
117
- fs.writeFileSync(path.join(testDir, 'other.txt'), 'altro');
118
-
119
- const app3 = new Koa();
120
- app3.use(koaClassicServer(testDir, {
121
- index: [
122
- /index\.html/i, // Prima cerca index.html (NON esiste)
123
- /default\.html/i // Poi cerca default.html (ESISTE!)
124
- ]
125
- }));
126
-
127
- const server3 = app3.listen(9003);
128
-
129
- setTimeout(() => {
130
- http.get('http://localhost:9003/', (res) => {
131
- let data = '';
132
- res.on('data', chunk => data += chunk);
133
- res.on('end', () => {
134
- if (data.includes('DEFAULT.HTML (fallback)')) {
135
- console.log('✅ SUCCESS: Il server ha fatto fallback a DEFAULT.HTML');
136
- console.log(' Contenuto ricevuto: ' + data.trim());
137
- } else {
138
- console.log('❌ FAIL: Fallback non ha funzionato');
139
- console.log(' Contenuto ricevuto: ' + data);
140
- }
141
-
142
- server3.close();
143
-
144
- // Cleanup finale
145
- cleanup();
146
- });
147
- });
148
- }, 100);
149
- }
150
-
151
- function cleanup() {
152
- console.log('\n🧹 Pulizia...\n');
153
- fs.readdirSync(testDir).forEach(file => {
154
- fs.unlinkSync(path.join(testDir, file));
155
- });
156
- fs.rmdirSync(testDir);
157
- console.log('✅ Tutti i test completati!\n');
158
- }
@@ -1,213 +0,0 @@
1
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2
- //
3
- // TEST FOR useOriginalUrl OPTION
4
- // This test verifies that the useOriginalUrl option works correctly with URL rewriting middleware
5
- //
6
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
7
-
8
- const supertest = require('supertest');
9
- const koaClassicServer = require('../index.cjs');
10
- const Koa = require('koa');
11
- const fs = require('fs');
12
- const path = require('path');
13
-
14
- const rootDir = path.join(__dirname, 'publicWwwTest');
15
-
16
- // Create a simple test file if it doesn't exist
17
- const testFilePath = path.join(rootDir, 'test-page.html');
18
- const testFileContent = '<!DOCTYPE html><html><head><title>Test Page</title></head><body><h1>Test Page</h1></body></html>';
19
-
20
- beforeAll(() => {
21
- // Ensure test directory exists
22
- if (!fs.existsSync(rootDir)) {
23
- fs.mkdirSync(rootDir, { recursive: true });
24
- }
25
-
26
- // Create test file
27
- if (!fs.existsSync(testFilePath)) {
28
- fs.writeFileSync(testFilePath, testFileContent, 'utf-8');
29
- }
30
- });
31
-
32
- describe('useOriginalUrl option tests', () => {
33
-
34
- describe('Default behavior (useOriginalUrl: true)', () => {
35
- let app;
36
- let server;
37
-
38
- beforeAll(() => {
39
- app = new Koa();
40
-
41
- // i18n middleware that rewrites URLs
42
- app.use(async (ctx, next) => {
43
- if (ctx.path.match(/^\/it\//)) {
44
- // Rewrite /it/page.html to /page.html
45
- ctx.url = ctx.path.replace(/^\/it/, '');
46
- }
47
- await next();
48
- });
49
-
50
- // Serve files with default useOriginalUrl: true
51
- app.use(koaClassicServer(rootDir, {
52
- useOriginalUrl: true // Default behavior
53
- }));
54
-
55
- server = app.listen();
56
- });
57
-
58
- afterAll(() => {
59
- server.close();
60
- });
61
-
62
- test('should use ctx.originalUrl (original request path)', async () => {
63
- // Request /it/test-page.html
64
- // ctx.originalUrl = /it/test-page.html (unchanged)
65
- // ctx.url = /test-page.html (rewritten by middleware)
66
- // With useOriginalUrl: true, server looks for /it/test-page.html (which doesn't exist)
67
- const response = await supertest(server).get('/it/test-page.html');
68
-
69
- // Should return 404 because /it/test-page.html doesn't exist
70
- expect(response.status).toBe(404);
71
- });
72
-
73
- test('should serve file without rewriting', async () => {
74
- // Request /test-page.html directly (no rewriting)
75
- const response = await supertest(server).get('/test-page.html');
76
-
77
- // Should return 200 and the file content
78
- expect(response.status).toBe(200);
79
- expect(response.text).toContain('Test Page');
80
- });
81
- });
82
-
83
- describe('URL rewriting support (useOriginalUrl: false)', () => {
84
- let app;
85
- let server;
86
-
87
- beforeAll(() => {
88
- app = new Koa();
89
-
90
- // i18n middleware that rewrites URLs
91
- app.use(async (ctx, next) => {
92
- if (ctx.path.match(/^\/it\//)) {
93
- // Rewrite /it/page.html to /page.html
94
- ctx.url = ctx.path.replace(/^\/it/, '');
95
- }
96
- await next();
97
- });
98
-
99
- // Serve files with useOriginalUrl: false to use rewritten URL
100
- app.use(koaClassicServer(rootDir, {
101
- useOriginalUrl: false // Use ctx.url (rewritten)
102
- }));
103
-
104
- server = app.listen();
105
- });
106
-
107
- afterAll(() => {
108
- server.close();
109
- });
110
-
111
- test('should use ctx.url (rewritten path)', async () => {
112
- // Request /it/test-page.html
113
- // ctx.originalUrl = /it/test-page.html (unchanged)
114
- // ctx.url = /test-page.html (rewritten by middleware)
115
- // With useOriginalUrl: false, server looks for /test-page.html (which exists)
116
- const response = await supertest(server).get('/it/test-page.html');
117
-
118
- // Should return 200 and the file content
119
- expect(response.status).toBe(200);
120
- expect(response.text).toContain('Test Page');
121
- });
122
-
123
- test('should still serve file without rewriting', async () => {
124
- // Request /test-page.html directly (no rewriting)
125
- const response = await supertest(server).get('/test-page.html');
126
-
127
- // Should return 200 and the file content
128
- expect(response.status).toBe(200);
129
- expect(response.text).toContain('Test Page');
130
- });
131
- });
132
-
133
- describe('Complex i18n routing scenario', () => {
134
- let app;
135
- let server;
136
-
137
- beforeAll(() => {
138
- app = new Koa();
139
-
140
- // More complex i18n middleware
141
- app.use(async (ctx, next) => {
142
- const langPattern = /^\/(it|fr|de|es)\//;
143
- const match = ctx.path.match(langPattern);
144
- if (match) {
145
- // Store language in state
146
- ctx.state.lang = match[1];
147
- // Strip language prefix
148
- ctx.url = ctx.path.replace(langPattern, '/');
149
- }
150
- await next();
151
- });
152
-
153
- app.use(koaClassicServer(rootDir, {
154
- useOriginalUrl: false
155
- }));
156
-
157
- server = app.listen();
158
- });
159
-
160
- afterAll(() => {
161
- server.close();
162
- });
163
-
164
- test('should work with Italian locale (/it/)', async () => {
165
- const response = await supertest(server).get('/it/test-page.html');
166
- expect(response.status).toBe(200);
167
- expect(response.text).toContain('Test Page');
168
- });
169
-
170
- test('should work with French locale (/fr/)', async () => {
171
- const response = await supertest(server).get('/fr/test-page.html');
172
- expect(response.status).toBe(200);
173
- expect(response.text).toContain('Test Page');
174
- });
175
-
176
- test('should work with German locale (/de/)', async () => {
177
- const response = await supertest(server).get('/de/test-page.html');
178
- expect(response.status).toBe(200);
179
- expect(response.text).toContain('Test Page');
180
- });
181
-
182
- test('should work with Spanish locale (/es/)', async () => {
183
- const response = await supertest(server).get('/es/test-page.html');
184
- expect(response.status).toBe(200);
185
- expect(response.text).toContain('Test Page');
186
- });
187
- });
188
-
189
- describe('Backward compatibility', () => {
190
- let app;
191
- let server;
192
-
193
- beforeAll(() => {
194
- app = new Koa();
195
-
196
- // No URL rewriting middleware
197
- // Default useOriginalUrl (should be true)
198
- app.use(koaClassicServer(rootDir));
199
-
200
- server = app.listen();
201
- });
202
-
203
- afterAll(() => {
204
- server.close();
205
- });
206
-
207
- test('should work with default options (backward compatible)', async () => {
208
- const response = await supertest(server).get('/test-page.html');
209
- expect(response.status).toBe(200);
210
- expect(response.text).toContain('Test Page');
211
- });
212
- });
213
- });