/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^ws$" }] */
'use strict' ;
const assert = require(
'assert' );
const crypto = require(
'crypto' );
const https = require(
'https' );
const http = require(
'http' );
const path = require(
'path' );
const net = require(
'net' );
const fs = require(
'fs' );
const os = require(
'os' );
const Sender = require(
'../lib/sender' );
const WebSocket = require(
'..' );
const { NOOP } = require(
'../lib/constants' );
describe(
'WebSocketServer' , () => {
describe(
'#ctor' , () => {
it(
'throws an error if no option object is passed' , () => {
assert .
throws (
() =>
new WebSocket.Server(),
new RegExp(
'^TypeError: One and only one of the "port", "server", or ' +
'"noServer" options must be specified$'
)
);
});
describe(
'options' , () => {
it(
'throws an error if required options are not specified' , () => {
assert .
throws (
() =>
new WebSocket.Server({}),
new RegExp(
'^TypeError: One and only one of the "port", "server", or ' +
'"noServer" options must be specified$'
)
);
});
it(
'throws an error if mutually exclusive options are specified' , () => {
const server = http.createServer();
const variants = [
{ port:
0 , noServer:
true , server },
{ port:
0 , noServer:
true },
{ port:
0 , server },
{ noServer:
true , server }
];
for (
const options of variants) {
assert .
throws (
() =>
new WebSocket.Server(options),
new RegExp(
'^TypeError: One and only one of the "port", "server", or ' +
'"noServer" options must be specified$'
)
);
}
});
it(
'exposes options passed to constructor' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
assert .strictEqual(wss.options.port,
0 );
wss.close(done);
});
});
it(
'accepts the `maxPayload` option' , (done) => {
const maxPayload =
20480 ;
const wss =
new WebSocket.Server(
{
perMessageDeflate:
true ,
maxPayload,
port:
0
},
() => {
const ws =
new WebSocket(`ws:
//localhost:${wss.address().port}`);
ws.on(
'open' , ws.close);
}
);
wss.on(
'connection' , (ws) => {
assert .strictEqual(ws._receiver._maxPayload, maxPayload);
assert .strictEqual(
ws._receiver._extensions[
'permessage-deflate' ]._maxPayload,
maxPayload
);
wss.close(done);
});
});
it(
'honors the `WebSocket` option' , (done) => {
class CustomWebSocket
extends WebSocket.WebSocket {
get foo() {
return 'foo' ;
}
}
const wss =
new WebSocket.Server(
{
port:
0 ,
WebSocket: CustomWebSocket
},
() => {
const ws =
new WebSocket(`ws:
//localhost:${wss.address().port}`);
ws.on(
'open' , ws.close);
}
);
wss.on(
'connection' , (ws) => {
assert .ok(ws
instanceof CustomWebSocket);
assert .strictEqual(ws.foo,
'foo' );
wss.close(done);
});
});
});
it(
'emits an error if http server bind fails' , (done) => {
const wss1 =
new WebSocket.Server({ port:
0 }, () => {
const wss2 =
new WebSocket.Server({
port: wss1.address().port
});
wss2.on(
'error' , () => wss1.close(done));
});
});
it(
'starts a server on a given port' , (done) => {
const port =
1337 ;
const wss =
new WebSocket.Server({ port }, () => {
const ws =
new WebSocket(`ws:
//localhost:${port}`);
ws.on(
'open' , ws.close);
});
wss.on(
'connection' , () => wss.close(done));
});
it(
'binds the server on any IPv6 address when available' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
assert .strictEqual(wss._server.address().address,
'::' );
wss.close(done);
});
});
it(
'uses a precreated http server' , (done) => {
const server = http.createServer();
server.listen(
0 , () => {
const wss =
new WebSocket.Server({ server });
wss.on(
'connection' , () => {
server.close(done);
});
const ws =
new WebSocket(`ws:
//localhost:${server.address().port}`);
ws.on(
'open' , ws.close);
});
});
it(
'426s for non-Upgrade requests' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
http.get(`http:
//localhost:${wss.address().port}`, (res) => {
let body =
'' ;
assert .strictEqual(res.statusCode,
426 );
res.on(
'data' , (chunk) => {
body += chunk;
});
res.on(
'end' , () => {
assert .strictEqual(body, http.STATUS_CODES[
426 ]);
wss.close(done);
});
});
});
});
it(
'uses a precreated http server listening on unix socket' ,
function (done) {
//
// Skip this test on Windows. The URL parser:
//
// - Throws an error if the named pipe uses backward slashes.
// - Incorrectly parses the path if the named pipe uses forward slashes.
//
if (process.platform ===
'win32' )
return this .skip();
const server = http.createServer();
const sockPath = path.join(
os.tmpdir(),
`ws.${crypto.randomBytes(
16 ).toString(
'hex' )}.sock`
);
server.listen(sockPath, () => {
const wss =
new WebSocket.Server({ server });
wss.on(
'connection' , (ws, req) => {
if (wss.clients.size ===
1 ) {
assert .strictEqual(req.url,
'/foo?bar=bar' );
}
else {
assert .strictEqual(req.url,
'/' );
for (
const client of wss.clients) {
client.close();
}
server.close(done);
}
});
const ws =
new WebSocket(`ws+unix:
//${sockPath}:/foo?bar=bar`);
ws.on(
'open' , () =>
new WebSocket(`ws+unix:
//${sockPath}`));
});
});
});
describe(
'#address' , () => {
it(
'returns the address of the server' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
const addr = wss.address();
assert .deepStrictEqual(addr, wss._server.address());
wss.close(done);
});
});
it(
'throws an error when operating in "noServer" mode' , () => {
const wss =
new WebSocket.Server({ noServer:
true });
assert .
throws (() => {
wss.address();
}, /^Error: The server is operating in
"noServer" mode$/);
});
it(
'returns `null` if called after close' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
wss.close(() => {
assert .strictEqual(wss.address(),
null );
done();
});
});
});
});
describe(
'#close' , () => {
it(
'does not throw if called multiple times' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
wss.on(
'close' , done);
wss.close();
wss.close();
wss.close();
});
});
it(
"doesn't close a precreated server" , (done) => {
const server = http.createServer();
const realClose = server.close;
server.close = () => {
done(
new Error(
'Must not close pre-created server' ));
};
const wss =
new WebSocket.Server({ server });
wss.on(
'connection' , () => {
wss.close();
server.close = realClose;
server.close(done);
});
server.listen(
0 , () => {
const ws =
new WebSocket(`ws:
//localhost:${server.address().port}`);
ws.on(
'open' , ws.close);
});
});
it(
'invokes the callback in noServer mode' , (done) => {
const wss =
new WebSocket.Server({ noServer:
true });
wss.close(done);
});
it(
'cleans event handlers on precreated server' , (done) => {
const server = http.createServer();
const wss =
new WebSocket.Server({ server });
server.listen(
0 , () => {
wss.close(() => {
assert .strictEqual(server.listenerCount(
'listening' ),
0 );
assert .strictEqual(server.listenerCount(
'upgrade' ),
0 );
assert .strictEqual(server.listenerCount(
'error' ),
0 );
server.close(done);
});
});
});
it(
"emits the 'close' event after the server closes" , (done) => {
let serverCloseEventEmitted =
false ;
const wss =
new WebSocket.Server({ port:
0 }, () => {
net.createConnection({ port: wss.address().port });
});
wss._server.on(
'connection' , (socket) => {
wss.close();
//
// The server is closing. Ensure this does not emit a `'close'`
// event before the server is actually closed.
//
wss.close();
process.nextTick(() => {
socket.end();
});
});
wss._server.on(
'close' , () => {
serverCloseEventEmitted =
true ;
});
wss.on(
'close' , () => {
assert .ok(serverCloseEventEmitted);
done();
});
});
it(
"emits the 'close' event if client tracking is disabled" , (done) => {
const wss =
new WebSocket.Server({
noServer:
true ,
clientTracking:
false
});
wss.on(
'close' , done);
wss.close();
});
it(
'calls the callback if the server is already closed' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
wss.close(() => {
assert .strictEqual(wss._state,
2 );
wss.close((err) => {
assert .ok(err
instanceof Error);
assert .strictEqual(err.message,
'The server is not running' );
done();
});
});
});
});
it(
"emits the 'close' event if the server is already closed" , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
wss.close(() => {
assert .strictEqual(wss._state,
2 );
wss.on(
'close' , done);
wss.close();
});
});
});
});
describe(
'#clients' , () => {
it(
'returns a list of connected clients' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
assert .strictEqual(wss.clients.size,
0 );
const ws =
new WebSocket(`ws:
//localhost:${wss.address().port}`);
ws.on(
'open' , ws.close);
});
wss.on(
'connection' , () => {
assert .strictEqual(wss.clients.size,
1 );
wss.close(done);
});
});
it(
'can be disabled' , (done) => {
const wss =
new WebSocket.Server(
{ port:
0 , clientTracking:
false },
() => {
assert .strictEqual(wss.clients, undefined);
const ws =
new WebSocket(`ws:
//localhost:${wss.address().port}`);
ws.on(
'open' , () => ws.close());
}
);
wss.on(
'connection' , (ws) => {
assert .strictEqual(wss.clients, undefined);
ws.on(
'close' , () => wss.close(done));
});
});
it(
'is updated when client terminates the connection' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
const ws =
new WebSocket(`ws:
//localhost:${wss.address().port}`);
ws.on(
'open' , () => ws.terminate());
});
wss.on(
'connection' , (ws) => {
ws.on(
'close' , () => {
assert .strictEqual(wss.clients.size,
0 );
wss.close(done);
});
});
});
it(
'is updated when client closes the connection' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
const ws =
new WebSocket(`ws:
//localhost:${wss.address().port}`);
ws.on(
'open' , () => ws.close());
});
wss.on(
'connection' , (ws) => {
ws.on(
'close' , () => {
assert .strictEqual(wss.clients.size,
0 );
wss.close(done);
});
});
});
});
describe(
'#shouldHandle' , () => {
it(
'returns true when the path matches' , () => {
const wss =
new WebSocket.Server({ noServer:
true , path:
'/foo' });
assert .strictEqual(wss.shouldHandle({ url:
'/foo' }),
true );
assert .strictEqual(wss.shouldHandle({ url:
'/foo?bar=baz' }),
true );
});
it(
"returns false when the path doesn't match" , () => {
const wss =
new WebSocket.Server({ noServer:
true , path:
'/foo' });
assert .strictEqual(wss.shouldHandle({ url:
'/bar' }),
false );
});
});
describe(
'#handleUpgrade' , () => {
it(
'can be used for a pre-existing server' , (done) => {
const server = http.createServer();
server.listen(
0 , () => {
const wss =
new WebSocket.Server({ noServer:
true });
server.on(
'upgrade' , (req, socket, head) => {
wss.handleUpgrade(req, socket, head, (ws) => {
ws.send(
'hello' );
ws.close();
});
});
const ws =
new WebSocket(`ws:
//localhost:${server.address().port}`);
ws.on(
'message' , (message, isBinary) => {
assert .deepStrictEqual(message, Buffer.from(
'hello' ));
assert .ok(!isBinary);
server.close(done);
});
});
});
it(
"closes the connection when path doesn't match" , (done) => {
const wss =
new WebSocket.Server({ port:
0 , path:
'/ws' }, () => {
const req = http.get({
port: wss.address().port,
headers: {
Connection:
'Upgrade' ,
Upgrade:
'websocket' ,
'Sec-WebSocket-Key' :
'dGhlIHNhbXBsZSBub25jZQ==' ,
'Sec-WebSocket-Version' :
13
}
});
req.on(
'response' , (res) => {
assert .strictEqual(res.statusCode,
400 );
wss.close(done);
});
});
});
it(
'closes the connection when protocol version is Hixie-76' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
const req = http.get({
port: wss.address().port,
headers: {
Connection:
'Upgrade' ,
Upgrade:
'WebSocket' ,
'Sec-WebSocket-Key1' :
'4 @1 46546xW%0l 1 5' ,
'Sec-WebSocket-Key2' :
'12998 5 Y3 1 .P00' ,
'Sec-WebSocket-Protocol' :
'sample'
}
});
req.on(
'response' , (res) => {
assert .strictEqual(res.statusCode,
400 );
const chunks = [];
res.on(
'data' , (chunk) => {
chunks.push(chunk);
});
res.on(
'end' , () => {
assert .strictEqual(
Buffer.concat(chunks).toString(),
'Missing or invalid Sec-WebSocket-Key header'
);
wss.close(done);
});
});
});
});
});
describe(
'#completeUpgrade' , () => {
it(
'throws an error if called twice with the same socket' , (done) => {
const server = http.createServer();
server.listen(
0 , () => {
const wss =
new WebSocket.Server({ noServer:
true });
server.on(
'upgrade' , (req, socket, head) => {
wss.handleUpgrade(req, socket, head, (ws) => {
ws.close();
});
assert .
throws (
() => wss.handleUpgrade(req, socket, head, NOOP),
(err) => {
assert .ok(err
instanceof Error);
assert .strictEqual(
err.message,
'server.handleUpgrade() was called more than once with the ' +
'same socket, possibly due to a misconfiguration'
);
return true ;
}
);
});
const ws =
new WebSocket(`ws:
//localhost:${server.address().port}`);
ws.on(
'open' , () => {
ws.on(
'close' , () => {
server.close(done);
});
});
});
});
});
describe(
'Connection establishing' , () => {
it(
'fails if the HTTP method is not GET' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
const req = http.request({
method:
'POST' ,
port: wss.address().port,
headers: {
Connection:
'Upgrade' ,
Upgrade:
'websocket'
}
});
req.on(
'response' , (res) => {
assert .strictEqual(res.statusCode,
405 );
const chunks = [];
res.on(
'data' , (chunk) => {
chunks.push(chunk);
});
res.on(
'end' , () => {
assert .strictEqual(
Buffer.concat(chunks).toString(),
'Invalid HTTP method'
);
wss.close(done);
});
});
req.end();
});
wss.on(
'connection' , () => {
done(
new Error(
"Unexpected 'connection' event" ));
});
});
it(
'fails if the Upgrade header field value is not "websocket"' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
const req = http.get({
port: wss.address().port,
headers: {
Connection:
'Upgrade' ,
Upgrade:
'foo'
}
});
req.on(
'response' , (res) => {
assert .strictEqual(res.statusCode,
400 );
const chunks = [];
res.on(
'data' , (chunk) => {
chunks.push(chunk);
});
res.on(
'end' , () => {
assert .strictEqual(
Buffer.concat(chunks).toString(),
'Invalid Upgrade header'
);
wss.close(done);
});
});
});
wss.on(
'connection' , () => {
done(
new Error(
"Unexpected 'connection' event" ));
});
});
it(
'fails if the Sec-WebSocket-Key header is invalid (1/2)' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
const req = http.get({
port: wss.address().port,
headers: {
Connection:
'Upgrade' ,
Upgrade:
'websocket'
}
});
req.on(
'response' , (res) => {
assert .strictEqual(res.statusCode,
400 );
const chunks = [];
res.on(
'data' , (chunk) => {
chunks.push(chunk);
});
res.on(
'end' , () => {
assert .strictEqual(
Buffer.concat(chunks).toString(),
'Missing or invalid Sec-WebSocket-Key header'
);
wss.close(done);
});
});
});
wss.on(
'connection' , () => {
done(
new Error(
"Unexpected 'connection' event" ));
});
});
it(
'fails if the Sec-WebSocket-Key header is invalid (2/2)' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
const req = http.get({
port: wss.address().port,
headers: {
Connection:
'Upgrade' ,
Upgrade:
'websocket' ,
'Sec-WebSocket-Key' :
'P5l8BJcZwRc='
}
});
req.on(
'response' , (res) => {
assert .strictEqual(res.statusCode,
400 );
const chunks = [];
res.on(
'data' , (chunk) => {
chunks.push(chunk);
});
res.on(
'end' , () => {
assert .strictEqual(
Buffer.concat(chunks).toString(),
'Missing or invalid Sec-WebSocket-Key header'
);
wss.close(done);
});
});
});
wss.on(
'connection' , () => {
done(
new Error(
"Unexpected 'connection' event" ));
});
});
it(
'fails if the Sec-WebSocket-Version header is invalid (1/2)' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
const req = http.get({
port: wss.address().port,
headers: {
Connection:
'Upgrade' ,
Upgrade:
'websocket' ,
'Sec-WebSocket-Key' :
'dGhlIHNhbXBsZSBub25jZQ=='
}
});
req.on(
'response' , (res) => {
assert .strictEqual(res.statusCode,
400 );
const chunks = [];
res.on(
'data' , (chunk) => {
chunks.push(chunk);
});
res.on(
'end' , () => {
assert .strictEqual(
Buffer.concat(chunks).toString(),
'Missing or invalid Sec-WebSocket-Version header'
);
wss.close(done);
});
});
});
wss.on(
'connection' , () => {
done(
new Error(
"Unexpected 'connection' event" ));
});
});
it(
'fails if the Sec-WebSocket-Version header is invalid (2/2)' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
const req = http.get({
port: wss.address().port,
headers: {
Connection:
'Upgrade' ,
Upgrade:
'websocket' ,
'Sec-WebSocket-Key' :
'dGhlIHNhbXBsZSBub25jZQ==' ,
'Sec-WebSocket-Version' :
12
}
});
req.on(
'response' , (res) => {
assert .strictEqual(res.statusCode,
400 );
const chunks = [];
res.on(
'data' , (chunk) => {
chunks.push(chunk);
});
res.on(
'end' , () => {
assert .strictEqual(
Buffer.concat(chunks).toString(),
'Missing or invalid Sec-WebSocket-Version header'
);
wss.close(done);
});
});
});
wss.on(
'connection' , () => {
done(
new Error(
"Unexpected 'connection' event" ));
});
});
it(
'fails is the Sec-WebSocket-Protocol header is invalid' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
const req = http.get({
port: wss.address().port,
headers: {
Connection:
'Upgrade' ,
Upgrade:
'websocket' ,
'Sec-WebSocket-Key' :
'dGhlIHNhbXBsZSBub25jZQ==' ,
'Sec-WebSocket-Version' :
13 ,
'Sec-WebSocket-Protocol' :
'foo;bar'
}
});
req.on(
'response' , (res) => {
assert .strictEqual(res.statusCode,
400 );
const chunks = [];
res.on(
'data' , (chunk) => {
chunks.push(chunk);
});
res.on(
'end' , () => {
assert .strictEqual(
Buffer.concat(chunks).toString(),
'Invalid Sec-WebSocket-Protocol header'
);
wss.close(done);
});
});
});
wss.on(
'connection' , () => {
done(
new Error(
"Unexpected 'connection' event" ));
});
});
it(
'fails if the Sec-WebSocket-Extensions header is invalid' , (done) => {
const wss =
new WebSocket.Server(
{
perMessageDeflate:
true ,
port:
0
},
() => {
const req = http.get({
port: wss.address().port,
headers: {
Connection:
'Upgrade' ,
Upgrade:
'websocket' ,
'Sec-WebSocket-Key' :
'dGhlIHNhbXBsZSBub25jZQ==' ,
'Sec-WebSocket-Version' :
13 ,
'Sec-WebSocket-Extensions' :
'permessage-deflate; server_max_window_bits=foo'
}
});
req.on(
'response' , (res) => {
assert .strictEqual(res.statusCode,
400 );
const chunks = [];
res.on(
'data' , (chunk) => {
chunks.push(chunk);
});
res.on(
'end' , () => {
assert .strictEqual(
Buffer.concat(chunks).toString(),
'Invalid or unacceptable Sec-WebSocket-Extensions header'
);
wss.close(done);
});
});
}
);
wss.on(
'connection' , () => {
done(
new Error(
"Unexpected 'connection' event" ));
});
});
it(
"emits the 'wsClientError' event" , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
const req = http.request({
method:
'POST' ,
port: wss.address().port,
headers: {
Connection:
'Upgrade' ,
Upgrade:
'websocket'
}
});
req.on(
'response' , (res) => {
assert .strictEqual(res.statusCode,
400 );
wss.close(done);
});
req.end();
});
wss.on(
'wsClientError' , (err, socket, request) => {
assert .ok(err
instanceof Error);
assert .strictEqual(err.message,
'Invalid HTTP method' );
assert .ok(request
instanceof http.IncomingMessage);
assert .strictEqual(request.method,
'POST' );
socket.end(
'HTTP/1.1 400 Bad Request\r\n\r\n' );
});
wss.on(
'connection' , () => {
done(
new Error(
"Unexpected 'connection' event" ));
});
});
it(
'fails if the WebSocket server is closing or closed' , (done) => {
const server = http.createServer();
const wss =
new WebSocket.Server({ noServer:
true });
server.on(
'upgrade' , (req, socket, head) => {
wss.close();
wss.handleUpgrade(req, socket, head, () => {
done(
new Error(
'Unexpected callback invocation' ));
});
});
server.listen(
0 , () => {
const ws =
new WebSocket(`ws:
//localhost:${server.address().port}`);
ws.on(
'unexpected-response' , (req, res) => {
assert .strictEqual(res.statusCode,
503 );
res.resume();
server.close(done);
});
});
});
it(
'handles unsupported extensions' , (done) => {
const wss =
new WebSocket.Server(
{
perMessageDeflate:
true ,
port:
0
},
() => {
const req = http.get({
port: wss.address().port,
headers: {
Connection:
'Upgrade' ,
Upgrade:
'websocket' ,
'Sec-WebSocket-Key' :
'dGhlIHNhbXBsZSBub25jZQ==' ,
'Sec-WebSocket-Version' :
13 ,
'Sec-WebSocket-Extensions' :
'foo; bar'
}
});
req.on(
'upgrade' , (res, socket, head) => {
if (head.length) socket.unshift(head);
socket.once(
'data' , (chunk) => {
assert .strictEqual(chunk[
0 ],
0 x88);
socket.destroy();
wss.close(done);
});
});
}
);
wss.on(
'connection' , (ws) => {
assert .strictEqual(ws.extensions,
'' );
ws.close();
});
});
describe(
'`verifyClient`' , () => {
it(
'can reject client synchronously' , (done) => {
const wss =
new WebSocket.Server(
{
verifyClient: () =>
false ,
port:
0
},
() => {
const req = http.get({
port: wss.address().port,
headers: {
Connection:
'Upgrade' ,
Upgrade:
'websocket' ,
'Sec-WebSocket-Key' :
'dGhlIHNhbXBsZSBub25jZQ==' ,
'Sec-WebSocket-Version' :
8
}
});
req.on(
'response' , (res) => {
assert .strictEqual(res.statusCode,
401 );
wss.close(done);
});
}
);
wss.on(
'connection' , () => {
done(
new Error(
"Unexpected 'connection' event" ));
});
});
it(
'can accept client synchronously' , (done) => {
const server = https.createServer({
cert: fs.readFileSync(
'test/fixtures/certificate.pem' ),
key: fs.readFileSync(
'test/fixtures/key.pem' )
});
const wss =
new WebSocket.Server({
verifyClient: (info) => {
assert .strictEqual(info.origin,
'https://example.com ');
assert .strictEqual(info.req.headers.foo,
'bar' );
assert .ok(info.secure,
true );
return true ;
},
server
});
wss.on(
'connection' , () => {
server.close(done);
});
server.listen(
0 , () => {
const ws =
new WebSocket(`wss:
//localhost:${server.address().port}`, {
headers: { Origin:
'https://example.com ', foo: 'bar' },
rejectUnauthorized:
false
});
ws.on(
'open' , ws.close);
});
});
it(
'can accept client asynchronously' , (done) => {
const wss =
new WebSocket.Server(
{
verifyClient: (o, cb) => process.nextTick(cb,
true ),
port:
0
},
() => {
const ws =
new WebSocket(`ws:
//localhost:${wss.address().port}`);
ws.on(
'open' , ws.close);
}
);
wss.on(
'connection' , () => wss.close(done));
});
it(
'can reject client asynchronously' , (done) => {
const wss =
new WebSocket.Server(
{
verifyClient: (info, cb) => process.nextTick(cb,
false ),
port:
0
},
() => {
const req = http.get({
port: wss.address().port,
headers: {
Connection:
'Upgrade' ,
Upgrade:
'websocket' ,
'Sec-WebSocket-Key' :
'dGhlIHNhbXBsZSBub25jZQ==' ,
'Sec-WebSocket-Version' :
8
}
});
req.on(
'response' , (res) => {
assert .strictEqual(res.statusCode,
401 );
wss.close(done);
});
}
);
wss.on(
'connection' , () => {
done(
new Error(
"Unexpected 'connection' event" ));
});
});
it(
'can reject client asynchronously w/ status code' , (done) => {
const wss =
new WebSocket.Server(
{
verifyClient: (info, cb) => process.nextTick(cb,
false ,
404 ),
port:
0
},
() => {
const req = http.get({
port: wss.address().port,
headers: {
Connection:
'Upgrade' ,
Upgrade:
'websocket' ,
'Sec-WebSocket-Key' :
'dGhlIHNhbXBsZSBub25jZQ==' ,
'Sec-WebSocket-Version' :
8
}
});
req.on(
'response' , (res) => {
assert .strictEqual(res.statusCode,
404 );
wss.close(done);
});
}
);
wss.on(
'connection' , () => {
done(
new Error(
"Unexpected 'connection' event" ));
});
});
it(
'can reject client asynchronously w/ custom headers' , (done) => {
const wss =
new WebSocket.Server(
{
verifyClient: (info, cb) => {
process.nextTick(cb,
false ,
503 ,
'' , {
'Retry-After' :
120 });
},
port:
0
},
() => {
const req = http.get({
port: wss.address().port,
headers: {
Connection:
'Upgrade' ,
Upgrade:
'websocket' ,
'Sec-WebSocket-Key' :
'dGhlIHNhbXBsZSBub25jZQ==' ,
'Sec-WebSocket-Version' :
8
}
});
req.on(
'response' , (res) => {
assert .strictEqual(res.statusCode,
503 );
assert .strictEqual(res.headers[
'retry-after' ],
'120' );
wss.close(done);
});
}
);
wss.on(
'connection' , () => {
done(
new Error(
"Unexpected 'connection' event" ));
});
});
});
it(
"doesn't emit the 'connection' event if socket is closed prematurely" , (done) => {
const server = http.createServer();
server.listen(
0 , () => {
const wss =
new WebSocket.Server({
verifyClient: ({ req: { socket } }, cb) => {
assert .strictEqual(socket.readable,
true );
assert .strictEqual(socket.writable,
true );
socket.on(
'end' , () => {
assert .strictEqual(socket.readable,
false );
assert .strictEqual(socket.writable,
true );
cb(
true );
});
},
server
});
wss.on(
'connection' , () => {
done(
new Error(
"Unexpected 'connection' event" ));
});
const socket = net.connect(
{
port: server.address().port,
allowHalfOpen:
true
},
() => {
socket.end(
[
'GET / HTTP/1.1' ,
'Host: localhost' ,
'Upgrade: websocket' ,
'Connection: Upgrade' ,
'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' ,
'Sec-WebSocket-Version: 13' ,
'\r\n'
].join(
'\r\n' )
);
}
);
socket.on(
'end' , () => {
wss.close();
server.close(done);
});
});
});
it(
'handles data passed along with the upgrade request' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
const req = http.request({
port: wss.address().port,
headers: {
Connection:
'Upgrade' ,
Upgrade:
'websocket' ,
'Sec-WebSocket-Key' :
'dGhlIHNhbXBsZSBub25jZQ==' ,
'Sec-WebSocket-Version' :
13
}
});
const list = Sender.frame(Buffer.from(
'Hello' ), {
fin:
true ,
rsv1:
false ,
opcode:
0 x01,
mask:
true ,
readOnly:
false
});
req.write(Buffer.concat(list));
req.end();
});
wss.on(
'connection' , (ws) => {
ws.on(
'message' , (data, isBinary) => {
assert .deepStrictEqual(data, Buffer.from(
'Hello' ));
assert .ok(!isBinary);
wss.close(done);
});
});
});
describe(
'`handleProtocols`' , () => {
it(
'allows to select a subprotocol' , (done) => {
const handleProtocols = (protocols, request) => {
assert .ok(request
instanceof http.IncomingMessage);
assert .strictEqual(request.url,
'/' );
return Array.from(protocols).pop();
};
const wss =
new WebSocket.Server({ handleProtocols, port:
0 }, () => {
const ws =
new WebSocket(`ws:
//localhost:${wss.address().port}`, [
'foo' ,
'bar'
]);
ws.on(
'open' , () => {
assert .strictEqual(ws.protocol,
'bar' );
wss.close(done);
});
});
wss.on(
'connection' , (ws) => {
ws.close();
});
});
});
it(
"emits the 'headers' event" , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
const ws =
new WebSocket(`ws:
//localhost:${wss.address().port}`);
ws.on(
'open' , ws.close);
});
wss.on(
'headers' , (headers, request) => {
assert .deepStrictEqual(headers.slice(
0 ,
3 ), [
'HTTP/1.1 101 Switching Protocols' ,
'Upgrade: websocket' ,
'Connection: Upgrade'
]);
assert .ok(request
instanceof http.IncomingMessage);
assert .strictEqual(request.url,
'/' );
wss.on(
'connection' , () => wss.close(done));
});
});
});
describe(
'permessage-deflate' , () => {
it(
'is disabled by default' , (done) => {
const wss =
new WebSocket.Server({ port:
0 }, () => {
const ws =
new WebSocket(`ws:
//localhost:${wss.address().port}`);
ws.on(
'open' , ws.close);
});
wss.on(
'connection' , (ws, req) => {
assert .strictEqual(
req.headers[
'sec-websocket-extensions' ],
'permessage-deflate; client_max_window_bits'
);
assert .strictEqual(ws.extensions,
'' );
wss.close(done);
});
});
it(
'uses configuration options' , (done) => {
const wss =
new WebSocket.Server(
{
perMessageDeflate: { clientMaxWindowBits:
8 },
port:
0
},
() => {
const ws =
new WebSocket(`ws:
//localhost:${wss.address().port}`);
ws.on(
'upgrade' , (res) => {
assert .strictEqual(
res.headers[
'sec-websocket-extensions' ],
'permessage-deflate; client_max_window_bits=8'
);
wss.close(done);
});
}
);
wss.on(
'connection' , (ws) => {
ws.close();
});
});
});
});
Messung V0.5 in Prozent C=95 H=96 G=95
¤ Dauer der Verarbeitung: 0.24 Sekunden
(vorverarbeitet am 2026-06-04)
¤
*© Formatika GbR, Deutschland