koa-classic-server 1.2.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/BENCHMARKS.md +317 -0
- package/CREATE_RELEASE.sh +53 -0
- package/EXAMPLES_INDEX_OPTION.md +395 -0
- package/INDEX_OPTION_PRIORITY.md +527 -0
- package/OPTIMIZATION_HTTP_CACHING.md +687 -0
- package/PERFORMANCE_ANALYSIS.md +839 -0
- package/PERFORMANCE_COMPARISON.md +388 -0
- package/README.md +21 -5
- package/__tests__/index-option.test.js +447 -0
- package/__tests__/performance.test.js +301 -0
- package/benchmark-results-baseline-v1.2.0.txt +354 -0
- package/benchmark-results-optimized-v2.0.0.txt +354 -0
- package/benchmark.js +239 -0
- package/demo-regex-index.js +140 -0
- package/index.cjs +201 -51
- package/jest.config.js +18 -0
- package/package.json +9 -4
- package/publish-to-npm.sh +65 -0
- package/scripts/setup-benchmark.js +178 -0
- package/test-regex-quick.js +158 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Setup script for performance benchmarks
|
|
5
|
+
* Creates test files and directories for realistic performance testing
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
const BENCHMARK_DIR = path.join(__dirname, '../benchmark-data');
|
|
12
|
+
|
|
13
|
+
console.log('π§ Setting up benchmark environment...\n');
|
|
14
|
+
|
|
15
|
+
// Clean up old benchmark data
|
|
16
|
+
if (fs.existsSync(BENCHMARK_DIR)) {
|
|
17
|
+
console.log('Cleaning old benchmark data...');
|
|
18
|
+
fs.rmSync(BENCHMARK_DIR, { recursive: true, force: true });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Create benchmark directory
|
|
22
|
+
fs.mkdirSync(BENCHMARK_DIR, { recursive: true });
|
|
23
|
+
|
|
24
|
+
// Create small files (1KB each)
|
|
25
|
+
console.log('Creating small files (1KB each)...');
|
|
26
|
+
const smallDir = path.join(BENCHMARK_DIR, 'small-files');
|
|
27
|
+
fs.mkdirSync(smallDir);
|
|
28
|
+
for (let i = 1; i <= 100; i++) {
|
|
29
|
+
const content = 'X'.repeat(1024); // 1KB
|
|
30
|
+
fs.writeFileSync(path.join(smallDir, `file-${i}.txt`), content);
|
|
31
|
+
}
|
|
32
|
+
console.log(`β Created 100 small files (1KB each) = 100KB total`);
|
|
33
|
+
|
|
34
|
+
// Create medium files (100KB each)
|
|
35
|
+
console.log('Creating medium files (100KB each)...');
|
|
36
|
+
const mediumDir = path.join(BENCHMARK_DIR, 'medium-files');
|
|
37
|
+
fs.mkdirSync(mediumDir);
|
|
38
|
+
for (let i = 1; i <= 50; i++) {
|
|
39
|
+
const content = 'X'.repeat(100 * 1024); // 100KB
|
|
40
|
+
fs.writeFileSync(path.join(mediumDir, `file-${i}.txt`), content);
|
|
41
|
+
}
|
|
42
|
+
console.log(`β Created 50 medium files (100KB each) = 5MB total`);
|
|
43
|
+
|
|
44
|
+
// Create large files (1MB each)
|
|
45
|
+
console.log('Creating large files (1MB each)...');
|
|
46
|
+
const largeDir = path.join(BENCHMARK_DIR, 'large-files');
|
|
47
|
+
fs.mkdirSync(largeDir);
|
|
48
|
+
for (let i = 1; i <= 10; i++) {
|
|
49
|
+
const content = 'X'.repeat(1024 * 1024); // 1MB
|
|
50
|
+
fs.writeFileSync(path.join(largeDir, `file-${i}.txt`), content);
|
|
51
|
+
}
|
|
52
|
+
console.log(`β Created 10 large files (1MB each) = 10MB total`);
|
|
53
|
+
|
|
54
|
+
// Create directory with many files for listing test
|
|
55
|
+
console.log('Creating large directory (1000 files)...');
|
|
56
|
+
const largeDirListing = path.join(BENCHMARK_DIR, 'large-directory');
|
|
57
|
+
fs.mkdirSync(largeDirListing);
|
|
58
|
+
for (let i = 1; i <= 1000; i++) {
|
|
59
|
+
const content = `File number ${i}\n`;
|
|
60
|
+
fs.writeFileSync(path.join(largeDirListing, `item-${String(i).padStart(4, '0')}.txt`), content);
|
|
61
|
+
}
|
|
62
|
+
console.log(`β Created directory with 1000 files`);
|
|
63
|
+
|
|
64
|
+
// Create directory with very many files (10,000) for stress test
|
|
65
|
+
console.log('Creating very large directory (10,000 files) - this may take a while...');
|
|
66
|
+
const veryLargeDirListing = path.join(BENCHMARK_DIR, 'very-large-directory');
|
|
67
|
+
fs.mkdirSync(veryLargeDirListing);
|
|
68
|
+
for (let i = 1; i <= 10000; i++) {
|
|
69
|
+
const content = `File number ${i}\n`;
|
|
70
|
+
fs.writeFileSync(path.join(veryLargeDirListing, `item-${String(i).padStart(5, '0')}.txt`), content);
|
|
71
|
+
if (i % 1000 === 0) {
|
|
72
|
+
process.stdout.write(` Progress: ${i}/10000 files created...\r`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
console.log(`β Created directory with 10,000 files `);
|
|
76
|
+
|
|
77
|
+
// Create HTML file for caching test
|
|
78
|
+
console.log('Creating HTML files for caching test...');
|
|
79
|
+
const htmlContent = `<!DOCTYPE html>
|
|
80
|
+
<html>
|
|
81
|
+
<head>
|
|
82
|
+
<meta charset="UTF-8">
|
|
83
|
+
<title>Benchmark Test Page</title>
|
|
84
|
+
<style>
|
|
85
|
+
body { font-family: Arial, sans-serif; margin: 40px; }
|
|
86
|
+
h1 { color: #333; }
|
|
87
|
+
p { line-height: 1.6; }
|
|
88
|
+
</style>
|
|
89
|
+
</head>
|
|
90
|
+
<body>
|
|
91
|
+
<h1>Benchmark Test Page</h1>
|
|
92
|
+
<p>${'Lorem ipsum dolor sit amet. '.repeat(100)}</p>
|
|
93
|
+
</body>
|
|
94
|
+
</html>`;
|
|
95
|
+
fs.writeFileSync(path.join(BENCHMARK_DIR, 'test.html'), htmlContent);
|
|
96
|
+
console.log(`β Created HTML file for caching test`);
|
|
97
|
+
|
|
98
|
+
// Create CSS file
|
|
99
|
+
const cssContent = `
|
|
100
|
+
body {
|
|
101
|
+
margin: 0;
|
|
102
|
+
padding: 20px;
|
|
103
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
104
|
+
}
|
|
105
|
+
${'h1 { color: #333; }\n'.repeat(50)}
|
|
106
|
+
`.trim();
|
|
107
|
+
fs.writeFileSync(path.join(BENCHMARK_DIR, 'style.css'), cssContent);
|
|
108
|
+
console.log(`β Created CSS file`);
|
|
109
|
+
|
|
110
|
+
// Create JS file
|
|
111
|
+
const jsContent = `
|
|
112
|
+
function benchmark() {
|
|
113
|
+
console.log('Benchmark test');
|
|
114
|
+
${'console.log("test");\n'.repeat(100)}
|
|
115
|
+
}
|
|
116
|
+
`.trim();
|
|
117
|
+
fs.writeFileSync(path.join(BENCHMARK_DIR, 'script.js'), jsContent);
|
|
118
|
+
console.log(`β Created JS file`);
|
|
119
|
+
|
|
120
|
+
// Create nested directory structure
|
|
121
|
+
console.log('Creating nested directory structure...');
|
|
122
|
+
const nestedBase = path.join(BENCHMARK_DIR, 'nested');
|
|
123
|
+
fs.mkdirSync(nestedBase);
|
|
124
|
+
for (let depth = 1; depth <= 5; depth++) {
|
|
125
|
+
const dirPath = path.join(nestedBase, ...Array(depth).fill('level'));
|
|
126
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
127
|
+
for (let i = 1; i <= 10; i++) {
|
|
128
|
+
fs.writeFileSync(
|
|
129
|
+
path.join(dirPath, `file-depth${depth}-${i}.txt`),
|
|
130
|
+
`Depth ${depth}, File ${i}\n`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
console.log(`β Created nested directory structure (5 levels deep)`);
|
|
135
|
+
|
|
136
|
+
// Create .gitignore for benchmark-data
|
|
137
|
+
const gitignorePath = path.join(BENCHMARK_DIR, '.gitignore');
|
|
138
|
+
fs.writeFileSync(gitignorePath, '*\n');
|
|
139
|
+
console.log(`β Created .gitignore to exclude benchmark data from git`);
|
|
140
|
+
|
|
141
|
+
// Summary
|
|
142
|
+
console.log('\nβ
Benchmark environment setup complete!\n');
|
|
143
|
+
console.log('Directory structure:');
|
|
144
|
+
console.log(' benchmark-data/');
|
|
145
|
+
console.log(' βββ small-files/ (100 files Γ 1KB = 100KB)');
|
|
146
|
+
console.log(' βββ medium-files/ (50 files Γ 100KB = 5MB)');
|
|
147
|
+
console.log(' βββ large-files/ (10 files Γ 1MB = 10MB)');
|
|
148
|
+
console.log(' βββ large-directory/ (1,000 files for listing test)');
|
|
149
|
+
console.log(' βββ very-large-directory/ (10,000 files for stress test)');
|
|
150
|
+
console.log(' βββ nested/ (5 levels deep, 10 files each)');
|
|
151
|
+
console.log(' βββ test.html (HTML for caching test)');
|
|
152
|
+
console.log(' βββ style.css (CSS file)');
|
|
153
|
+
console.log(' βββ script.js (JS file)');
|
|
154
|
+
|
|
155
|
+
const stats = getDirSize(BENCHMARK_DIR);
|
|
156
|
+
console.log(`\nTotal size: ${(stats.size / 1024 / 1024).toFixed(2)} MB`);
|
|
157
|
+
console.log(`Total files: ${stats.files}`);
|
|
158
|
+
|
|
159
|
+
function getDirSize(dir) {
|
|
160
|
+
let totalSize = 0;
|
|
161
|
+
let totalFiles = 0;
|
|
162
|
+
|
|
163
|
+
function traverse(currentPath) {
|
|
164
|
+
const items = fs.readdirSync(currentPath, { withFileTypes: true });
|
|
165
|
+
for (const item of items) {
|
|
166
|
+
const fullPath = path.join(currentPath, item.name);
|
|
167
|
+
if (item.isDirectory()) {
|
|
168
|
+
traverse(fullPath);
|
|
169
|
+
} else {
|
|
170
|
+
totalSize += fs.statSync(fullPath).size;
|
|
171
|
+
totalFiles++;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
traverse(dir);
|
|
177
|
+
return { size: totalSize, files: totalFiles };
|
|
178
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
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
|
+
}
|