| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent ef9a6cd commit 23468fd
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3379,6 +3379,9 @@ class Http2SecureServer extends TLSServer { | |||
| 3379 | 3379 | this.headersTimeout = 60_000; // Minimum between 60 seconds or requestTimeout | |
| 3380 | 3380 | this.requestTimeout = 300_000; // 5 minutes | |
| 3381 | 3381 | this.connectionsCheckingInterval = 30_000; // 30 seconds | |
| 3382 | + this.shouldUpgradeCallback = function() { | ||
| 3383 | + return this.listenerCount('upgrade') > 0; | ||
| 3384 | + }; | ||
| 3382 | 3385 | this.on('listening', setupConnectionsTracking); | |
| 3383 | 3386 | } | |
| 3384 | 3387 | if (typeof requestListener === 'function') | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,108 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + const common = require('./index'); | ||
| 3 | + if (!common.hasCrypto) | ||
| 4 | + common.skip('missing crypto'); | ||
| 5 | + const http = require('http'); | ||
| 6 | + const crypto = require('crypto'); | ||
| 7 | + | ||
| 8 | + class WebSocketServer { | ||
| 9 | + constructor({ | ||
| 10 | + port = 0, | ||
| 11 | + server, | ||
| 12 | + }) { | ||
| 13 | + this.port = port; | ||
| 14 | + this.server = server || http.createServer(); | ||
| 15 | + this.clients = new Set(); | ||
| 16 | + | ||
| 17 | + this.server.on('upgrade', this.handleUpgrade.bind(this)); | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + start() { | ||
| 21 | + return new Promise((resolve) => { | ||
| 22 | + this.server.listen(this.port, () => { | ||
| 23 | + this.port = this.server.address().port; | ||
| 24 | + resolve(); | ||
| 25 | + }); | ||
| 26 | + }).catch((err) => { | ||
| 27 | + console.error('Failed to start WebSocket server:', err); | ||
| 28 | + }); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + handleUpgrade(req, socket, head) { | ||
| 32 | + const key = req.headers['sec-websocket-key']; | ||
| 33 | + const acceptKey = this.generateAcceptValue(key); | ||
| 34 | + const responseHeaders = [ | ||
| 35 | + 'HTTP/1.1 101 Switching Protocols', | ||
| 36 | + 'Upgrade: websocket', | ||
| 37 | + 'Connection: Upgrade', | ||
| 38 | + `Sec-WebSocket-Accept: ${acceptKey}`, | ||
| 39 | + ]; | ||
| 40 | + | ||
| 41 | + socket.write(responseHeaders.join('\r\n') + '\r\n\r\n'); | ||
| 42 | + this.clients.add(socket); | ||
| 43 | + | ||
| 44 | + socket.on('data', (buffer) => { | ||
| 45 | + const opcode = buffer[0] & 0x0f; | ||
| 46 | + | ||
| 47 | + if (opcode === 0x8) { | ||
| 48 | + // Send a minimal close frame in response: | ||
| 49 | + socket.write(Buffer.from([0x88, 0x00])); | ||
| 50 | + socket.end(); | ||
| 51 | + this.clients.delete(socket); | ||
| 52 | + return; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + socket.write(this.encodeMessage('Hello from server!')); | ||
| 56 | + }); | ||
| 57 | + | ||
| 58 | + socket.on('close', () => { | ||
| 59 | + this.clients.delete(socket); | ||
| 60 | + }); | ||
| 61 | + | ||
| 62 | + socket.on('error', (err) => { | ||
| 63 | + console.error('Socket error:', err); | ||
| 64 | + this.clients.delete(socket); | ||
| 65 | + }); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + generateAcceptValue(secWebSocketKey) { | ||
| 69 | + return crypto | ||
| 70 | + .createHash('sha1') | ||
| 71 | + .update(secWebSocketKey + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', 'binary') | ||
| 72 | + .digest('base64'); | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + decodeMessage(buffer) { | ||
| 76 | + const secondByte = buffer[1]; | ||
| 77 | + const length = secondByte & 127; | ||
| 78 | + const maskStart = 2; | ||
| 79 | + const dataStart = maskStart + 4; | ||
| 80 | + const masks = buffer.slice(maskStart, dataStart); | ||
| 81 | + const data = buffer.slice(dataStart, dataStart + length); | ||
| 82 | + const result = Buffer.alloc(length); | ||
| 83 | + | ||
| 84 | + for (let i = 0; i < length; i++) { | ||
| 85 | + result[i] = data[i] ^ masks[i % 4]; | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + return result.toString(); | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + encodeMessage(message) { | ||
| 92 | + const msgBuffer = Buffer.from(message); | ||
| 93 | + const length = msgBuffer.length; | ||
| 94 | + const frame = [0x81]; | ||
| 95 | + | ||
| 96 | + if (length < 126) { | ||
| 97 | + frame.push(length); | ||
| 98 | + } else if (length < 65536) { | ||
| 99 | + frame.push(126, (length >> 8) & 0xff, length & 0xff); | ||
| 100 | + } else { | ||
| 101 | + throw new Error('Message too long'); | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + return Buffer.concat([Buffer.from(frame), msgBuffer]); | ||
| 105 | + } | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + module.exports = WebSocketServer; | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,39 @@ | |||
| 1 | + // Flags: --expose-internals | ||
| 2 | + 'use strict'; | ||
| 3 | + | ||
| 4 | + const common = require('../common'); | ||
| 5 | + const fixtures = require('../common/fixtures'); | ||
| 6 | + if (!common.hasCrypto) common.skip('missing crypto'); | ||
| 7 | + | ||
| 8 | + const http2 = require('http2'); | ||
| 9 | + | ||
| 10 | + const undici = require('internal/deps/undici/undici'); | ||
| 11 | + const WebSocketServer = require('../common/websocket-server'); | ||
| 12 | + | ||
| 13 | + (async function main() { | ||
| 14 | + const server = http2.createSecureServer({ | ||
| 15 | + key: fixtures.readKey('agent1-key.pem'), | ||
| 16 | + cert: fixtures.readKey('agent1-cert.pem'), | ||
| 17 | + allowHTTP1: true, | ||
| 18 | + }); | ||
| 19 | + | ||
| 20 | + server.on('request', common.mustNotCall()); | ||
| 21 | + new WebSocketServer({ server }); // Handles websocket 'upgrade' events | ||
| 22 | + | ||
| 23 | + await new Promise((resolve) => server.listen(0, resolve)); | ||
| 24 | + | ||
| 25 | + await new Promise((resolve, reject) => { | ||
| 26 | + const ws = new WebSocket(`wss://localhost:${server.address().port}`, { | ||
| 27 | + dispatcher: new undici.EnvHttpProxyAgent({ | ||
| 28 | + connect: { rejectUnauthorized: false } | ||
| 29 | + }) | ||
| 30 | + }); | ||
| 31 | + ws.addEventListener('open', common.mustCall(() => { | ||
| 32 | + ws.close(); | ||
| 33 | + resolve(); | ||
| 34 | + })); | ||
| 35 | + ws.addEventListener('error', reject); | ||
| 36 | + }); | ||
| 37 | + | ||
| 38 | + server.close(); | ||
| 39 | + })().then(common.mustCall()); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments