| 23 | // Create a local HTTP server that serves files of specified sizes |
| 24 | // No rate limiting - relies on VM's built-in network rate limiting |
| 25 | function createTestServer(port) { |
| 26 | return new Promise((resolve, reject) => { |
| 27 | const server = http.createServer((req, res) => { |
| 28 | // Parse URL to get requested size: /bytes/<size> |
| 29 | const match = req.url.match(/^\/bytes\/(\d+)$/); |
| 30 | if (!match) { |
| 31 | res.writeHead(404); |
| 32 | res.end('Not Found'); |
| 33 | return; |
| 34 | } |
| 35 | |
| 36 | const size = parseInt(match[1], 10); |
| 37 | console.log(` [Server] Serving ${(size / 1024 / 1024).toFixed(2)}MB`); |
| 38 | |
| 39 | res.writeHead(200, { |
| 40 | 'Content-Type': 'application/octet-stream', |
| 41 | 'Content-Length': size, |
| 42 | 'Connection': 'close' |
| 43 | }); |
| 44 | |
| 45 | // Stream data in chunks, but as fast as TCP allows (no artificial rate limiting) |
| 46 | const CHUNK_SIZE = 64 * 1024; // 64KB chunks |
| 47 | let remaining = size; |
| 48 | |
| 49 | function sendChunk() { |
| 50 | while (remaining > 0) { |
| 51 | const toWrite = Math.min(CHUNK_SIZE, remaining); |
| 52 | const chunk = crypto.randomBytes(toWrite); |
| 53 | remaining -= toWrite; |
| 54 | |
| 55 | if (!res.write(chunk)) { |
| 56 | // TCP backpressure - wait for drain |
| 57 | res.once('drain', sendChunk); |
| 58 | return; |
| 59 | } |
| 60 | } |
| 61 | res.end(); |
| 62 | console.log(` [Server] Finished sending ${size} bytes`); |
| 63 | } |
| 64 | |
| 65 | sendChunk(); |
| 66 | }); |
| 67 | |
| 68 | server.listen(port, '127.0.0.1', () => { |
| 69 | console.log(` [Server] Test server listening on http://127.0.0.1:${port}`); |
| 70 | resolve(server); |
| 71 | }); |
| 72 | |
| 73 | server.on('error', reject); |
| 74 | }); |
| 75 | } |
| 76 | |
| 77 | async function test() { |
| 78 | console.log("=== Very Large File Download Tests ===\n"); |