forked from nodejs/node
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwebsocket-server.js
More file actions
105 lines (87 loc) Β· 2.55 KB
/
websocket-server.js
File metadata and controls
105 lines (87 loc) Β· 2.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
'use strict';
const common = require('./index');
if (!common.hasCrypto)
common.skip('missing crypto');
const http = require('http');
const crypto = require('crypto');
class WebSocketServer {
constructor({
port = 0,
}) {
this.port = port;
this.server = http.createServer();
this.clients = new Set();
this.server.on('upgrade', this.handleUpgrade.bind(this));
}
start() {
return new Promise((resolve) => {
this.server.listen(this.port, () => {
this.port = this.server.address().port;
resolve();
});
}).catch((err) => {
console.error('Failed to start WebSocket server:', err);
});
}
handleUpgrade(req, socket, head) {
const key = req.headers['sec-websocket-key'];
const acceptKey = this.generateAcceptValue(key);
const responseHeaders = [
'HTTP/1.1 101 Switching Protocols',
'Upgrade: websocket',
'Connection: Upgrade',
`Sec-WebSocket-Accept: ${acceptKey}`,
];
socket.write(responseHeaders.join('\r\n') + '\r\n\r\n');
this.clients.add(socket);
socket.on('data', (buffer) => {
const opcode = buffer[0] & 0x0f;
if (opcode === 0x8) {
socket.end();
this.clients.delete(socket);
return;
}
socket.write(this.encodeMessage('Hello from server!'));
});
socket.on('close', () => {
this.clients.delete(socket);
});
socket.on('error', (err) => {
console.error('Socket error:', err);
this.clients.delete(socket);
});
}
generateAcceptValue(secWebSocketKey) {
return crypto
.createHash('sha1')
.update(secWebSocketKey + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', 'binary')
.digest('base64');
}
decodeMessage(buffer) {
const secondByte = buffer[1];
const length = secondByte & 127;
const maskStart = 2;
const dataStart = maskStart + 4;
const masks = buffer.slice(maskStart, dataStart);
const data = buffer.slice(dataStart, dataStart + length);
const result = Buffer.alloc(length);
for (let i = 0; i < length; i++) {
result[i] = data[i] ^ masks[i % 4];
}
return result.toString();
}
encodeMessage(message) {
const msgBuffer = Buffer.from(message);
const length = msgBuffer.length;
const frame = [0x81];
if (length < 126) {
frame.push(length);
} else if (length < 65536) {
frame.push(126, (length >> 8) & 0xff, length & 0xff);
} else {
throw new Error('Message too long');
}
return Buffer.concat([Buffer.from(frame), msgBuffer]);
}
}
module.exports = WebSocketServer;