websocket.test.js (135685B)
1 /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^ws$" }] */ 2 3 'use strict'; 4 5 const assert = require('assert'); 6 const crypto = require('crypto'); 7 const https = require('https'); 8 const http = require('http'); 9 const path = require('path'); 10 const net = require('net'); 11 const tls = require('tls'); 12 const os = require('os'); 13 const fs = require('fs'); 14 const { URL } = require('url'); 15 16 const Sender = require('../lib/sender'); 17 const WebSocket = require('..'); 18 const { 19 CloseEvent, 20 ErrorEvent, 21 Event, 22 MessageEvent 23 } = require('../lib/event-target'); 24 const { EMPTY_BUFFER, GUID, kListener, NOOP } = require('../lib/constants'); 25 26 class CustomAgent extends http.Agent { 27 addRequest() {} 28 } 29 30 describe('WebSocket', () => { 31 describe('#ctor', () => { 32 it('throws an error when using an invalid url', () => { 33 assert.throws( 34 () => new WebSocket('foo'), 35 /^SyntaxError: Invalid URL: foo$/ 36 ); 37 38 assert.throws( 39 () => new WebSocket('https://websocket-echo.com'), 40 /^SyntaxError: The URL's protocol must be one of "ws:", "wss:", or "ws\+unix:"$/ 41 ); 42 43 assert.throws( 44 () => new WebSocket('ws+unix:'), 45 /^SyntaxError: The URL's pathname is empty$/ 46 ); 47 48 assert.throws( 49 () => new WebSocket('wss://websocket-echo.com#foo'), 50 /^SyntaxError: The URL contains a fragment identifier$/ 51 ); 52 }); 53 54 it('throws an error if a subprotocol is invalid or duplicated', () => { 55 for (const subprotocol of [null, '', 'a,b', ['a', 'a']]) { 56 assert.throws( 57 () => new WebSocket('ws://localhost', subprotocol), 58 /^SyntaxError: An invalid or duplicated subprotocol was specified$/ 59 ); 60 } 61 }); 62 63 it('accepts `url.URL` objects as url', (done) => { 64 const agent = new CustomAgent(); 65 66 agent.addRequest = (req, opts) => { 67 assert.strictEqual(opts.host, '::1'); 68 assert.strictEqual(req.path, '/'); 69 done(); 70 }; 71 72 const ws = new WebSocket(new URL('ws://[::1]'), { agent }); 73 }); 74 75 describe('options', () => { 76 it('accepts the `options` object as 3rd argument', () => { 77 const agent = new CustomAgent(); 78 let count = 0; 79 let ws; 80 81 agent.addRequest = (req) => { 82 assert.strictEqual( 83 req.getHeader('sec-websocket-protocol'), 84 undefined 85 ); 86 count++; 87 }; 88 89 ws = new WebSocket('ws://localhost', undefined, { agent }); 90 ws = new WebSocket('ws://localhost', [], { agent }); 91 92 assert.strictEqual(count, 2); 93 }); 94 95 it('accepts the `maxPayload` option', (done) => { 96 const maxPayload = 20480; 97 const wss = new WebSocket.Server( 98 { 99 perMessageDeflate: true, 100 port: 0 101 }, 102 () => { 103 const ws = new WebSocket(`ws://localhost:${wss.address().port}`, { 104 perMessageDeflate: true, 105 maxPayload 106 }); 107 108 ws.on('open', () => { 109 assert.strictEqual(ws._receiver._maxPayload, maxPayload); 110 assert.strictEqual( 111 ws._receiver._extensions['permessage-deflate']._maxPayload, 112 maxPayload 113 ); 114 wss.close(done); 115 }); 116 } 117 ); 118 119 wss.on('connection', (ws) => { 120 ws.close(); 121 }); 122 }); 123 124 it('throws an error when using an invalid `protocolVersion`', () => { 125 const options = { agent: new CustomAgent(), protocolVersion: 1000 }; 126 127 assert.throws( 128 () => new WebSocket('ws://localhost', options), 129 /^RangeError: Unsupported protocol version: 1000 \(supported versions: 8, 13\)$/ 130 ); 131 }); 132 133 it('honors the `generateMask` option', (done) => { 134 const data = Buffer.from('foo'); 135 const wss = new WebSocket.Server({ port: 0 }, () => { 136 const ws = new WebSocket(`ws://localhost:${wss.address().port}`, { 137 generateMask() {} 138 }); 139 140 ws.on('open', () => { 141 ws.send(data); 142 }); 143 144 ws.on('close', (code, reason) => { 145 assert.strictEqual(code, 1005); 146 assert.deepStrictEqual(reason, EMPTY_BUFFER); 147 148 wss.close(done); 149 }); 150 }); 151 152 wss.on('connection', (ws) => { 153 const chunks = []; 154 155 ws._socket.prependListener('data', (chunk) => { 156 chunks.push(chunk); 157 }); 158 159 ws.on('message', (message) => { 160 assert.deepStrictEqual(message, data); 161 assert.deepStrictEqual( 162 Buffer.concat(chunks).slice(2, 6), 163 Buffer.alloc(4) 164 ); 165 166 ws.close(); 167 }); 168 }); 169 }); 170 }); 171 }); 172 173 describe('Constants', () => { 174 const readyStates = { 175 CONNECTING: 0, 176 OPEN: 1, 177 CLOSING: 2, 178 CLOSED: 3 179 }; 180 181 Object.keys(readyStates).forEach((state) => { 182 describe(`\`${state}\``, () => { 183 it('is enumerable property of class', () => { 184 const descriptor = Object.getOwnPropertyDescriptor(WebSocket, state); 185 186 assert.deepStrictEqual(descriptor, { 187 configurable: false, 188 enumerable: true, 189 value: readyStates[state], 190 writable: false 191 }); 192 }); 193 194 it('is enumerable property of prototype', () => { 195 const descriptor = Object.getOwnPropertyDescriptor( 196 WebSocket.prototype, 197 state 198 ); 199 200 assert.deepStrictEqual(descriptor, { 201 configurable: false, 202 enumerable: true, 203 value: readyStates[state], 204 writable: false 205 }); 206 }); 207 }); 208 }); 209 }); 210 211 describe('Attributes', () => { 212 describe('`binaryType`', () => { 213 it('is enumerable and configurable', () => { 214 const descriptor = Object.getOwnPropertyDescriptor( 215 WebSocket.prototype, 216 'binaryType' 217 ); 218 219 assert.strictEqual(descriptor.configurable, true); 220 assert.strictEqual(descriptor.enumerable, true); 221 assert.ok(descriptor.get !== undefined); 222 assert.ok(descriptor.set !== undefined); 223 }); 224 225 it("defaults to 'nodebuffer'", () => { 226 const ws = new WebSocket('ws://localhost', { 227 agent: new CustomAgent() 228 }); 229 230 assert.strictEqual(ws.binaryType, 'nodebuffer'); 231 }); 232 233 it("can be changed to 'arraybuffer' or 'fragments'", () => { 234 const ws = new WebSocket('ws://localhost', { 235 agent: new CustomAgent() 236 }); 237 238 ws.binaryType = 'arraybuffer'; 239 assert.strictEqual(ws.binaryType, 'arraybuffer'); 240 241 ws.binaryType = 'foo'; 242 assert.strictEqual(ws.binaryType, 'arraybuffer'); 243 244 ws.binaryType = 'fragments'; 245 assert.strictEqual(ws.binaryType, 'fragments'); 246 247 ws.binaryType = ''; 248 assert.strictEqual(ws.binaryType, 'fragments'); 249 250 ws.binaryType = 'nodebuffer'; 251 assert.strictEqual(ws.binaryType, 'nodebuffer'); 252 }); 253 }); 254 255 describe('`bufferedAmount`', () => { 256 it('is enumerable and configurable', () => { 257 const descriptor = Object.getOwnPropertyDescriptor( 258 WebSocket.prototype, 259 'bufferedAmount' 260 ); 261 262 assert.strictEqual(descriptor.configurable, true); 263 assert.strictEqual(descriptor.enumerable, true); 264 assert.ok(descriptor.get !== undefined); 265 assert.ok(descriptor.set === undefined); 266 }); 267 268 it('defaults to zero', () => { 269 const ws = new WebSocket('ws://localhost', { 270 agent: new CustomAgent() 271 }); 272 273 assert.strictEqual(ws.bufferedAmount, 0); 274 }); 275 276 it('defaults to zero upon "open"', (done) => { 277 const wss = new WebSocket.Server({ port: 0 }, () => { 278 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 279 280 ws.onopen = () => { 281 assert.strictEqual(ws.bufferedAmount, 0); 282 wss.close(done); 283 }; 284 }); 285 286 wss.on('connection', (ws) => { 287 ws.close(); 288 }); 289 }); 290 291 it('takes into account the data in the sender queue', (done) => { 292 const wss = new WebSocket.Server( 293 { 294 perMessageDeflate: true, 295 port: 0 296 }, 297 () => { 298 const ws = new WebSocket(`ws://localhost:${wss.address().port}`, { 299 perMessageDeflate: { threshold: 0 } 300 }); 301 302 ws.on('open', () => { 303 ws.send('foo'); 304 305 assert.strictEqual(ws.bufferedAmount, 3); 306 307 ws.send('bar', (err) => { 308 assert.ifError(err); 309 assert.strictEqual(ws.bufferedAmount, 0); 310 wss.close(done); 311 }); 312 313 assert.strictEqual(ws.bufferedAmount, 6); 314 }); 315 } 316 ); 317 318 wss.on('connection', (ws) => { 319 ws.close(); 320 }); 321 }); 322 323 it('takes into account the data in the socket queue', (done) => { 324 const wss = new WebSocket.Server({ port: 0 }, () => { 325 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 326 }); 327 328 wss.on('connection', (ws) => { 329 const data = Buffer.alloc(1024, 61); 330 331 while (ws.bufferedAmount === 0) { 332 ws.send(data); 333 } 334 335 assert.ok(ws.bufferedAmount > 0); 336 assert.strictEqual( 337 ws.bufferedAmount, 338 ws._socket._writableState.length 339 ); 340 341 ws.on('close', () => wss.close(done)); 342 ws.close(); 343 }); 344 }); 345 }); 346 347 describe('`extensions`', () => { 348 it('is enumerable and configurable', () => { 349 const descriptor = Object.getOwnPropertyDescriptor( 350 WebSocket.prototype, 351 'bufferedAmount' 352 ); 353 354 assert.strictEqual(descriptor.configurable, true); 355 assert.strictEqual(descriptor.enumerable, true); 356 assert.ok(descriptor.get !== undefined); 357 assert.ok(descriptor.set === undefined); 358 }); 359 360 it('exposes the negotiated extensions names (1/2)', (done) => { 361 const wss = new WebSocket.Server({ port: 0 }, () => { 362 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 363 364 assert.strictEqual(ws.extensions, ''); 365 366 ws.on('open', () => { 367 assert.strictEqual(ws.extensions, ''); 368 ws.on('close', () => wss.close(done)); 369 }); 370 }); 371 372 wss.on('connection', (ws) => { 373 assert.strictEqual(ws.extensions, ''); 374 ws.close(); 375 }); 376 }); 377 378 it('exposes the negotiated extensions names (2/2)', (done) => { 379 const wss = new WebSocket.Server( 380 { 381 perMessageDeflate: true, 382 port: 0 383 }, 384 () => { 385 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 386 387 assert.strictEqual(ws.extensions, ''); 388 389 ws.on('open', () => { 390 assert.strictEqual(ws.extensions, 'permessage-deflate'); 391 ws.on('close', () => wss.close(done)); 392 }); 393 } 394 ); 395 396 wss.on('connection', (ws) => { 397 assert.strictEqual(ws.extensions, 'permessage-deflate'); 398 ws.close(); 399 }); 400 }); 401 }); 402 403 describe('`isPaused`', () => { 404 it('is enumerable and configurable', () => { 405 const descriptor = Object.getOwnPropertyDescriptor( 406 WebSocket.prototype, 407 'isPaused' 408 ); 409 410 assert.strictEqual(descriptor.configurable, true); 411 assert.strictEqual(descriptor.enumerable, true); 412 assert.ok(descriptor.get !== undefined); 413 assert.ok(descriptor.set === undefined); 414 }); 415 416 it('indicates whether the websocket is paused', (done) => { 417 const wss = new WebSocket.Server({ port: 0 }, () => { 418 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 419 420 ws.on('open', () => { 421 ws.pause(); 422 assert.ok(ws.isPaused); 423 424 ws.resume(); 425 assert.ok(!ws.isPaused); 426 427 ws.close(); 428 wss.close(done); 429 }); 430 431 assert.ok(!ws.isPaused); 432 }); 433 }); 434 }); 435 436 describe('`protocol`', () => { 437 it('is enumerable and configurable', () => { 438 const descriptor = Object.getOwnPropertyDescriptor( 439 WebSocket.prototype, 440 'protocol' 441 ); 442 443 assert.strictEqual(descriptor.configurable, true); 444 assert.strictEqual(descriptor.enumerable, true); 445 assert.ok(descriptor.get !== undefined); 446 assert.ok(descriptor.set === undefined); 447 }); 448 449 it('exposes the subprotocol selected by the server', (done) => { 450 const wss = new WebSocket.Server({ port: 0 }, () => { 451 const port = wss.address().port; 452 const ws = new WebSocket(`ws://localhost:${port}`, 'foo'); 453 454 assert.strictEqual(ws.extensions, ''); 455 456 ws.on('open', () => { 457 assert.strictEqual(ws.protocol, 'foo'); 458 ws.on('close', () => wss.close(done)); 459 }); 460 }); 461 462 wss.on('connection', (ws) => { 463 assert.strictEqual(ws.protocol, 'foo'); 464 ws.close(); 465 }); 466 }); 467 }); 468 469 describe('`readyState`', () => { 470 it('is enumerable and configurable', () => { 471 const descriptor = Object.getOwnPropertyDescriptor( 472 WebSocket.prototype, 473 'readyState' 474 ); 475 476 assert.strictEqual(descriptor.configurable, true); 477 assert.strictEqual(descriptor.enumerable, true); 478 assert.ok(descriptor.get !== undefined); 479 assert.ok(descriptor.set === undefined); 480 }); 481 482 it('defaults to `CONNECTING`', () => { 483 const ws = new WebSocket('ws://localhost', { 484 agent: new CustomAgent() 485 }); 486 487 assert.strictEqual(ws.readyState, WebSocket.CONNECTING); 488 }); 489 490 it('is set to `OPEN` once connection is established', (done) => { 491 const wss = new WebSocket.Server({ port: 0 }, () => { 492 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 493 494 ws.on('open', () => { 495 assert.strictEqual(ws.readyState, WebSocket.OPEN); 496 ws.close(); 497 }); 498 499 ws.on('close', () => wss.close(done)); 500 }); 501 }); 502 503 it('is set to `CLOSED` once connection is closed', (done) => { 504 const wss = new WebSocket.Server({ port: 0 }, () => { 505 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 506 507 ws.on('close', () => { 508 assert.strictEqual(ws.readyState, WebSocket.CLOSED); 509 wss.close(done); 510 }); 511 512 ws.on('open', () => ws.close(1001)); 513 }); 514 }); 515 516 it('is set to `CLOSED` once connection is terminated', (done) => { 517 const wss = new WebSocket.Server({ port: 0 }, () => { 518 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 519 520 ws.on('close', () => { 521 assert.strictEqual(ws.readyState, WebSocket.CLOSED); 522 wss.close(done); 523 }); 524 525 ws.on('open', () => ws.terminate()); 526 }); 527 }); 528 }); 529 530 describe('`url`', () => { 531 it('is enumerable and configurable', () => { 532 const descriptor = Object.getOwnPropertyDescriptor( 533 WebSocket.prototype, 534 'url' 535 ); 536 537 assert.strictEqual(descriptor.configurable, true); 538 assert.strictEqual(descriptor.enumerable, true); 539 assert.ok(descriptor.get !== undefined); 540 assert.ok(descriptor.set === undefined); 541 }); 542 543 it('exposes the server url', () => { 544 const url = 'ws://localhost'; 545 const ws = new WebSocket(url, { agent: new CustomAgent() }); 546 547 assert.strictEqual(ws.url, url); 548 }); 549 }); 550 }); 551 552 describe('Events', () => { 553 it("emits an 'error' event if an error occurs", (done) => { 554 let clientCloseEventEmitted = false; 555 let serverClientCloseEventEmitted = false; 556 557 const wss = new WebSocket.Server({ port: 0 }, () => { 558 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 559 560 ws.on('error', (err) => { 561 assert.ok(err instanceof RangeError); 562 assert.strictEqual(err.code, 'WS_ERR_INVALID_OPCODE'); 563 assert.strictEqual( 564 err.message, 565 'Invalid WebSocket frame: invalid opcode 5' 566 ); 567 568 ws.on('close', (code, reason) => { 569 assert.strictEqual(code, 1006); 570 assert.strictEqual(reason, EMPTY_BUFFER); 571 572 clientCloseEventEmitted = true; 573 if (serverClientCloseEventEmitted) wss.close(done); 574 }); 575 }); 576 }); 577 578 wss.on('connection', (ws) => { 579 ws.on('close', (code, reason) => { 580 assert.strictEqual(code, 1002); 581 assert.deepStrictEqual(reason, EMPTY_BUFFER); 582 583 serverClientCloseEventEmitted = true; 584 if (clientCloseEventEmitted) wss.close(done); 585 }); 586 587 ws._socket.write(Buffer.from([0x85, 0x00])); 588 }); 589 }); 590 591 it('does not re-emit `net.Socket` errors', (done) => { 592 const codes = ['EPIPE', 'ECONNABORTED', 'ECANCELED', 'ECONNRESET']; 593 const wss = new WebSocket.Server({ port: 0 }, () => { 594 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 595 596 ws.on('open', () => { 597 ws._socket.on('error', (err) => { 598 assert.ok(err instanceof Error); 599 assert.ok(codes.includes(err.code), `Unexpected code: ${err.code}`); 600 ws.on('close', (code, message) => { 601 assert.strictEqual(code, 1006); 602 assert.strictEqual(message, EMPTY_BUFFER); 603 wss.close(done); 604 }); 605 }); 606 607 for (const client of wss.clients) client.terminate(); 608 ws.send('foo'); 609 ws.send('bar'); 610 }); 611 }); 612 }); 613 614 it("emits an 'upgrade' event", (done) => { 615 const wss = new WebSocket.Server({ port: 0 }, () => { 616 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 617 ws.on('upgrade', (res) => { 618 assert.ok(res instanceof http.IncomingMessage); 619 wss.close(done); 620 }); 621 }); 622 623 wss.on('connection', (ws) => { 624 ws.close(); 625 }); 626 }); 627 628 it("emits a 'ping' event", (done) => { 629 const wss = new WebSocket.Server({ port: 0 }, () => { 630 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 631 ws.on('ping', () => wss.close(done)); 632 }); 633 634 wss.on('connection', (ws) => { 635 ws.ping(); 636 ws.close(); 637 }); 638 }); 639 640 it("emits a 'pong' event", (done) => { 641 const wss = new WebSocket.Server({ port: 0 }, () => { 642 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 643 ws.on('pong', () => wss.close(done)); 644 }); 645 646 wss.on('connection', (ws) => { 647 ws.pong(); 648 ws.close(); 649 }); 650 }); 651 652 it("emits a 'redirect' event", (done) => { 653 const server = http.createServer(); 654 const wss = new WebSocket.Server({ noServer: true, path: '/foo' }); 655 656 server.once('upgrade', (req, socket) => { 657 socket.end('HTTP/1.1 302 Found\r\nLocation: /foo\r\n\r\n'); 658 server.once('upgrade', (req, socket, head) => { 659 wss.handleUpgrade(req, socket, head, (ws) => { 660 ws.close(); 661 }); 662 }); 663 }); 664 665 server.listen(() => { 666 const port = server.address().port; 667 const ws = new WebSocket(`ws://localhost:${port}`, { 668 followRedirects: true 669 }); 670 671 ws.on('redirect', (url, req) => { 672 assert.strictEqual(ws._redirects, 1); 673 assert.strictEqual(url, `ws://localhost:${port}/foo`); 674 assert.ok(req instanceof http.ClientRequest); 675 676 ws.on('close', (code) => { 677 assert.strictEqual(code, 1005); 678 server.close(done); 679 }); 680 }); 681 }); 682 }); 683 }); 684 685 describe('Connection establishing', () => { 686 const server = http.createServer(); 687 688 beforeEach((done) => server.listen(0, done)); 689 afterEach((done) => server.close(done)); 690 691 it('fails if the Upgrade header field value is not "websocket"', (done) => { 692 server.once('upgrade', (req, socket) => { 693 socket.on('end', socket.end); 694 socket.write( 695 'HTTP/1.1 101 Switching Protocols\r\n' + 696 'Connection: Upgrade\r\n' + 697 'Upgrade: foo\r\n' + 698 '\r\n' 699 ); 700 }); 701 702 const ws = new WebSocket(`ws://localhost:${server.address().port}`); 703 704 ws.on('error', (err) => { 705 assert.ok(err instanceof Error); 706 assert.strictEqual(err.message, 'Invalid Upgrade header'); 707 done(); 708 }); 709 }); 710 711 it('fails if the Sec-WebSocket-Accept header is invalid', (done) => { 712 server.once('upgrade', (req, socket) => { 713 socket.on('end', socket.end); 714 socket.write( 715 'HTTP/1.1 101 Switching Protocols\r\n' + 716 'Upgrade: websocket\r\n' + 717 'Connection: Upgrade\r\n' + 718 'Sec-WebSocket-Accept: CxYS6+NgJSBG74mdgLvGscRvpns=\r\n' + 719 '\r\n' 720 ); 721 }); 722 723 const ws = new WebSocket(`ws://localhost:${server.address().port}`); 724 725 ws.on('error', (err) => { 726 assert.ok(err instanceof Error); 727 assert.strictEqual(err.message, 'Invalid Sec-WebSocket-Accept header'); 728 done(); 729 }); 730 }); 731 732 it('close event is raised when server closes connection', (done) => { 733 server.once('upgrade', (req, socket) => { 734 const key = crypto 735 .createHash('sha1') 736 .update(req.headers['sec-websocket-key'] + GUID) 737 .digest('base64'); 738 739 socket.end( 740 'HTTP/1.1 101 Switching Protocols\r\n' + 741 'Upgrade: websocket\r\n' + 742 'Connection: Upgrade\r\n' + 743 `Sec-WebSocket-Accept: ${key}\r\n` + 744 '\r\n' 745 ); 746 }); 747 748 const ws = new WebSocket(`ws://localhost:${server.address().port}`); 749 750 ws.on('close', (code, reason) => { 751 assert.strictEqual(code, 1006); 752 assert.strictEqual(reason, EMPTY_BUFFER); 753 done(); 754 }); 755 }); 756 757 it('error is emitted if server aborts connection', (done) => { 758 server.once('upgrade', (req, socket) => { 759 socket.end( 760 `HTTP/1.1 401 ${http.STATUS_CODES[401]}\r\n` + 761 'Connection: close\r\n' + 762 'Content-type: text/html\r\n' + 763 `Content-Length: ${http.STATUS_CODES[401].length}\r\n` + 764 '\r\n' 765 ); 766 }); 767 768 const ws = new WebSocket(`ws://localhost:${server.address().port}`); 769 770 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 771 ws.on('error', (err) => { 772 assert.ok(err instanceof Error); 773 assert.strictEqual(err.message, 'Unexpected server response: 401'); 774 done(); 775 }); 776 }); 777 778 it('unexpected response can be read when sent by server', (done) => { 779 server.once('upgrade', (req, socket) => { 780 socket.end( 781 `HTTP/1.1 401 ${http.STATUS_CODES[401]}\r\n` + 782 'Connection: close\r\n' + 783 'Content-type: text/html\r\n' + 784 'Content-Length: 3\r\n' + 785 '\r\n' + 786 'foo' 787 ); 788 }); 789 790 const ws = new WebSocket(`ws://localhost:${server.address().port}`); 791 792 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 793 ws.on('error', () => done(new Error("Unexpected 'error' event"))); 794 ws.on('unexpected-response', (req, res) => { 795 assert.strictEqual(res.statusCode, 401); 796 797 let data = ''; 798 799 res.on('data', (v) => { 800 data += v; 801 }); 802 803 res.on('end', () => { 804 assert.strictEqual(data, 'foo'); 805 done(); 806 }); 807 }); 808 }); 809 810 it('request can be aborted when unexpected response is sent by server', (done) => { 811 server.once('upgrade', (req, socket) => { 812 socket.end( 813 `HTTP/1.1 401 ${http.STATUS_CODES[401]}\r\n` + 814 'Connection: close\r\n' + 815 'Content-type: text/html\r\n' + 816 'Content-Length: 3\r\n' + 817 '\r\n' + 818 'foo' 819 ); 820 }); 821 822 const ws = new WebSocket(`ws://localhost:${server.address().port}`); 823 824 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 825 ws.on('error', () => done(new Error("Unexpected 'error' event"))); 826 ws.on('unexpected-response', (req, res) => { 827 assert.strictEqual(res.statusCode, 401); 828 829 res.on('end', done); 830 req.abort(); 831 }); 832 }); 833 834 it('fails if the opening handshake timeout expires', (done) => { 835 server.once('upgrade', (req, socket) => socket.on('end', socket.end)); 836 837 const port = server.address().port; 838 const ws = new WebSocket(`ws://localhost:${port}`, { 839 handshakeTimeout: 100 840 }); 841 842 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 843 ws.on('error', (err) => { 844 assert.ok(err instanceof Error); 845 assert.strictEqual(err.message, 'Opening handshake has timed out'); 846 done(); 847 }); 848 }); 849 850 it('fails if an unexpected Sec-WebSocket-Extensions header is received', (done) => { 851 server.once('upgrade', (req, socket) => { 852 const key = crypto 853 .createHash('sha1') 854 .update(req.headers['sec-websocket-key'] + GUID) 855 .digest('base64'); 856 857 socket.end( 858 'HTTP/1.1 101 Switching Protocols\r\n' + 859 'Upgrade: websocket\r\n' + 860 'Connection: Upgrade\r\n' + 861 `Sec-WebSocket-Accept: ${key}\r\n` + 862 'Sec-WebSocket-Extensions: foo\r\n' + 863 '\r\n' 864 ); 865 }); 866 867 const ws = new WebSocket(`ws://localhost:${server.address().port}`, { 868 perMessageDeflate: false 869 }); 870 871 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 872 ws.on('error', (err) => { 873 assert.ok(err instanceof Error); 874 assert.strictEqual( 875 err.message, 876 'Server sent a Sec-WebSocket-Extensions header but no extension ' + 877 'was requested' 878 ); 879 ws.on('close', () => done()); 880 }); 881 }); 882 883 it('fails if the Sec-WebSocket-Extensions header is invalid (1/2)', (done) => { 884 server.once('upgrade', (req, socket) => { 885 const key = crypto 886 .createHash('sha1') 887 .update(req.headers['sec-websocket-key'] + GUID) 888 .digest('base64'); 889 890 socket.end( 891 'HTTP/1.1 101 Switching Protocols\r\n' + 892 'Upgrade: websocket\r\n' + 893 'Connection: Upgrade\r\n' + 894 `Sec-WebSocket-Accept: ${key}\r\n` + 895 'Sec-WebSocket-Extensions: foo;=\r\n' + 896 '\r\n' 897 ); 898 }); 899 900 const ws = new WebSocket(`ws://localhost:${server.address().port}`); 901 902 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 903 ws.on('error', (err) => { 904 assert.ok(err instanceof Error); 905 assert.strictEqual( 906 err.message, 907 'Invalid Sec-WebSocket-Extensions header' 908 ); 909 ws.on('close', () => done()); 910 }); 911 }); 912 913 it('fails if the Sec-WebSocket-Extensions header is invalid (2/2)', (done) => { 914 server.once('upgrade', (req, socket) => { 915 const key = crypto 916 .createHash('sha1') 917 .update(req.headers['sec-websocket-key'] + GUID) 918 .digest('base64'); 919 920 socket.end( 921 'HTTP/1.1 101 Switching Protocols\r\n' + 922 'Upgrade: websocket\r\n' + 923 'Connection: Upgrade\r\n' + 924 `Sec-WebSocket-Accept: ${key}\r\n` + 925 'Sec-WebSocket-Extensions: ' + 926 'permessage-deflate; client_max_window_bits=7\r\n' + 927 '\r\n' 928 ); 929 }); 930 931 const ws = new WebSocket(`ws://localhost:${server.address().port}`); 932 933 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 934 ws.on('error', (err) => { 935 assert.ok(err instanceof Error); 936 assert.strictEqual( 937 err.message, 938 'Invalid Sec-WebSocket-Extensions header' 939 ); 940 ws.on('close', () => done()); 941 }); 942 }); 943 944 it('fails if an unexpected extension is received (1/2)', (done) => { 945 server.once('upgrade', (req, socket) => { 946 const key = crypto 947 .createHash('sha1') 948 .update(req.headers['sec-websocket-key'] + GUID) 949 .digest('base64'); 950 951 socket.end( 952 'HTTP/1.1 101 Switching Protocols\r\n' + 953 'Upgrade: websocket\r\n' + 954 'Connection: Upgrade\r\n' + 955 `Sec-WebSocket-Accept: ${key}\r\n` + 956 'Sec-WebSocket-Extensions: foo\r\n' + 957 '\r\n' 958 ); 959 }); 960 961 const ws = new WebSocket(`ws://localhost:${server.address().port}`); 962 963 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 964 ws.on('error', (err) => { 965 assert.ok(err instanceof Error); 966 assert.strictEqual( 967 err.message, 968 'Server indicated an extension that was not requested' 969 ); 970 ws.on('close', () => done()); 971 }); 972 }); 973 974 it('fails if an unexpected extension is received (2/2)', (done) => { 975 server.once('upgrade', (req, socket) => { 976 const key = crypto 977 .createHash('sha1') 978 .update(req.headers['sec-websocket-key'] + GUID) 979 .digest('base64'); 980 981 socket.end( 982 'HTTP/1.1 101 Switching Protocols\r\n' + 983 'Upgrade: websocket\r\n' + 984 'Connection: Upgrade\r\n' + 985 `Sec-WebSocket-Accept: ${key}\r\n` + 986 'Sec-WebSocket-Extensions: permessage-deflate,foo\r\n' + 987 '\r\n' 988 ); 989 }); 990 991 const ws = new WebSocket(`ws://localhost:${server.address().port}`); 992 993 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 994 ws.on('error', (err) => { 995 assert.ok(err instanceof Error); 996 assert.strictEqual( 997 err.message, 998 'Server indicated an extension that was not requested' 999 ); 1000 ws.on('close', () => done()); 1001 }); 1002 }); 1003 1004 it('fails if server sends a subprotocol when none was requested', (done) => { 1005 const wss = new WebSocket.Server({ server }); 1006 1007 wss.on('headers', (headers) => { 1008 headers.push('Sec-WebSocket-Protocol: foo'); 1009 }); 1010 1011 const ws = new WebSocket(`ws://localhost:${server.address().port}`); 1012 1013 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 1014 ws.on('error', (err) => { 1015 assert.ok(err instanceof Error); 1016 assert.strictEqual( 1017 err.message, 1018 'Server sent a subprotocol but none was requested' 1019 ); 1020 ws.on('close', () => wss.close(done)); 1021 }); 1022 }); 1023 1024 it('fails if server sends an invalid subprotocol (1/2)', (done) => { 1025 const wss = new WebSocket.Server({ 1026 handleProtocols: () => 'baz', 1027 server 1028 }); 1029 1030 const ws = new WebSocket(`ws://localhost:${server.address().port}`, [ 1031 'foo', 1032 'bar' 1033 ]); 1034 1035 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 1036 ws.on('error', (err) => { 1037 assert.ok(err instanceof Error); 1038 assert.strictEqual(err.message, 'Server sent an invalid subprotocol'); 1039 ws.on('close', () => wss.close(done)); 1040 }); 1041 }); 1042 1043 it('fails if server sends an invalid subprotocol (2/2)', (done) => { 1044 server.once('upgrade', (req, socket) => { 1045 const key = crypto 1046 .createHash('sha1') 1047 .update(req.headers['sec-websocket-key'] + GUID) 1048 .digest('base64'); 1049 1050 socket.end( 1051 'HTTP/1.1 101 Switching Protocols\r\n' + 1052 'Upgrade: websocket\r\n' + 1053 'Connection: Upgrade\r\n' + 1054 `Sec-WebSocket-Accept: ${key}\r\n` + 1055 'Sec-WebSocket-Protocol:\r\n' + 1056 '\r\n' 1057 ); 1058 }); 1059 1060 const ws = new WebSocket(`ws://localhost:${server.address().port}`, [ 1061 'foo', 1062 'bar' 1063 ]); 1064 1065 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 1066 ws.on('error', (err) => { 1067 assert.ok(err instanceof Error); 1068 assert.strictEqual(err.message, 'Server sent an invalid subprotocol'); 1069 ws.on('close', () => done()); 1070 }); 1071 }); 1072 1073 it('fails if server sends no subprotocol', (done) => { 1074 const wss = new WebSocket.Server({ 1075 handleProtocols() {}, 1076 server 1077 }); 1078 1079 const ws = new WebSocket(`ws://localhost:${server.address().port}`, [ 1080 'foo', 1081 'bar' 1082 ]); 1083 1084 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 1085 ws.on('error', (err) => { 1086 assert.ok(err instanceof Error); 1087 assert.strictEqual(err.message, 'Server sent no subprotocol'); 1088 ws.on('close', () => wss.close(done)); 1089 }); 1090 }); 1091 1092 it('does not follow redirects by default', (done) => { 1093 server.once('upgrade', (req, socket) => { 1094 socket.end( 1095 'HTTP/1.1 301 Moved Permanently\r\n' + 1096 'Location: ws://localhost:8080\r\n' + 1097 '\r\n' 1098 ); 1099 }); 1100 1101 const ws = new WebSocket(`ws://localhost:${server.address().port}`); 1102 1103 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 1104 ws.on('error', (err) => { 1105 assert.ok(err instanceof Error); 1106 assert.strictEqual(err.message, 'Unexpected server response: 301'); 1107 assert.strictEqual(ws._redirects, 0); 1108 ws.on('close', () => done()); 1109 }); 1110 }); 1111 1112 it('honors the `followRedirects` option', (done) => { 1113 const wss = new WebSocket.Server({ noServer: true, path: '/foo' }); 1114 1115 server.once('upgrade', (req, socket) => { 1116 socket.end('HTTP/1.1 302 Found\r\nLocation: /foo\r\n\r\n'); 1117 server.once('upgrade', (req, socket, head) => { 1118 wss.handleUpgrade(req, socket, head, NOOP); 1119 }); 1120 }); 1121 1122 const port = server.address().port; 1123 const ws = new WebSocket(`ws://localhost:${port}`, { 1124 followRedirects: true 1125 }); 1126 1127 ws.on('open', () => { 1128 assert.strictEqual(ws.url, `ws://localhost:${port}/foo`); 1129 assert.strictEqual(ws._redirects, 1); 1130 ws.on('close', () => done()); 1131 ws.close(); 1132 }); 1133 }); 1134 1135 it('honors the `maxRedirects` option', (done) => { 1136 const onUpgrade = (req, socket) => { 1137 socket.end('HTTP/1.1 302 Found\r\nLocation: /\r\n\r\n'); 1138 }; 1139 1140 server.on('upgrade', onUpgrade); 1141 1142 const ws = new WebSocket(`ws://localhost:${server.address().port}`, { 1143 followRedirects: true, 1144 maxRedirects: 1 1145 }); 1146 1147 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 1148 ws.on('error', (err) => { 1149 assert.ok(err instanceof Error); 1150 assert.strictEqual(err.message, 'Maximum redirects exceeded'); 1151 assert.strictEqual(ws._redirects, 2); 1152 1153 server.removeListener('upgrade', onUpgrade); 1154 ws.on('close', () => done()); 1155 }); 1156 }); 1157 1158 it('emits an error if the redirect URL is invalid (1/2)', (done) => { 1159 server.once('upgrade', (req, socket) => { 1160 socket.end('HTTP/1.1 302 Found\r\nLocation: ws://\r\n\r\n'); 1161 }); 1162 1163 const ws = new WebSocket(`ws://localhost:${server.address().port}`, { 1164 followRedirects: true 1165 }); 1166 1167 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 1168 ws.on('error', (err) => { 1169 assert.ok(err instanceof SyntaxError); 1170 assert.strictEqual(err.message, 'Invalid URL: ws://'); 1171 assert.strictEqual(ws._redirects, 1); 1172 1173 ws.on('close', () => done()); 1174 }); 1175 }); 1176 1177 it('emits an error if the redirect URL is invalid (2/2)', (done) => { 1178 server.once('upgrade', (req, socket) => { 1179 socket.end('HTTP/1.1 302 Found\r\nLocation: http://localhost\r\n\r\n'); 1180 }); 1181 1182 const ws = new WebSocket(`ws://localhost:${server.address().port}`, { 1183 followRedirects: true 1184 }); 1185 1186 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 1187 ws.on('error', (err) => { 1188 assert.ok(err instanceof SyntaxError); 1189 assert.strictEqual( 1190 err.message, 1191 'The URL\'s protocol must be one of "ws:", "wss:", or "ws+unix:"' 1192 ); 1193 assert.strictEqual(ws._redirects, 1); 1194 1195 ws.on('close', () => done()); 1196 }); 1197 }); 1198 1199 it('uses the first url userinfo when following redirects', (done) => { 1200 const wss = new WebSocket.Server({ noServer: true, path: '/foo' }); 1201 const authorization = 'Basic Zm9vOmJhcg=='; 1202 1203 server.once('upgrade', (req, socket) => { 1204 socket.end( 1205 'HTTP/1.1 302 Found\r\n' + 1206 `Location: ws://baz:qux@localhost:${port}/foo\r\n\r\n` 1207 ); 1208 server.once('upgrade', (req, socket, head) => { 1209 wss.handleUpgrade(req, socket, head, (ws, req) => { 1210 assert.strictEqual(req.headers.authorization, authorization); 1211 ws.close(); 1212 }); 1213 }); 1214 }); 1215 1216 const port = server.address().port; 1217 const ws = new WebSocket(`ws://foo:bar@localhost:${port}`, { 1218 followRedirects: true 1219 }); 1220 1221 assert.strictEqual(ws._req.getHeader('Authorization'), authorization); 1222 1223 ws.on('close', (code) => { 1224 assert.strictEqual(code, 1005); 1225 assert.strictEqual(ws.url, `ws://baz:qux@localhost:${port}/foo`); 1226 assert.strictEqual(ws._redirects, 1); 1227 1228 wss.close(done); 1229 }); 1230 }); 1231 1232 describe('When moving away from a secure context', () => { 1233 function proxy(httpServer, httpsServer) { 1234 const server = net.createServer({ allowHalfOpen: true }); 1235 1236 server.on('connection', (socket) => { 1237 socket.on('readable', function read() { 1238 socket.removeListener('readable', read); 1239 1240 const buf = socket.read(1); 1241 const target = buf[0] === 22 ? httpsServer : httpServer; 1242 1243 socket.unshift(buf); 1244 target.emit('connection', socket); 1245 }); 1246 }); 1247 1248 return server; 1249 } 1250 1251 describe("If there is no 'redirect' event listener", () => { 1252 it('drops the `auth` option', (done) => { 1253 const httpServer = http.createServer(); 1254 const httpsServer = https.createServer({ 1255 cert: fs.readFileSync('test/fixtures/certificate.pem'), 1256 key: fs.readFileSync('test/fixtures/key.pem') 1257 }); 1258 const server = proxy(httpServer, httpsServer); 1259 1260 server.listen(() => { 1261 const port = server.address().port; 1262 1263 httpsServer.on('upgrade', (req, socket) => { 1264 socket.on('error', NOOP); 1265 socket.end( 1266 'HTTP/1.1 302 Found\r\n' + 1267 `Location: ws://localhost:${port}/\r\n\r\n` 1268 ); 1269 }); 1270 1271 const wss = new WebSocket.Server({ server: httpServer }); 1272 1273 wss.on('connection', (ws, req) => { 1274 assert.strictEqual(req.headers.authorization, undefined); 1275 ws.close(); 1276 }); 1277 1278 const ws = new WebSocket(`wss://localhost:${port}`, { 1279 auth: 'foo:bar', 1280 followRedirects: true, 1281 rejectUnauthorized: false 1282 }); 1283 1284 assert.strictEqual( 1285 ws._req.getHeader('Authorization'), 1286 'Basic Zm9vOmJhcg==' 1287 ); 1288 1289 ws.on('close', (code) => { 1290 assert.strictEqual(code, 1005); 1291 assert.strictEqual(ws.url, `ws://localhost:${port}/`); 1292 assert.strictEqual(ws._redirects, 1); 1293 1294 server.close(done); 1295 }); 1296 }); 1297 }); 1298 1299 it('drops the Authorization and Cookie headers', (done) => { 1300 const httpServer = http.createServer(); 1301 const httpsServer = https.createServer({ 1302 cert: fs.readFileSync('test/fixtures/certificate.pem'), 1303 key: fs.readFileSync('test/fixtures/key.pem') 1304 }); 1305 const server = proxy(httpServer, httpsServer); 1306 1307 server.listen(() => { 1308 const port = server.address().port; 1309 1310 httpsServer.on('upgrade', (req, socket) => { 1311 socket.on('error', NOOP); 1312 socket.end( 1313 'HTTP/1.1 302 Found\r\n' + 1314 `Location: ws://localhost:${port}/\r\n\r\n` 1315 ); 1316 }); 1317 1318 const headers = { 1319 authorization: 'Basic Zm9vOmJhcg==', 1320 cookie: 'foo=bar', 1321 host: 'foo' 1322 }; 1323 1324 const wss = new WebSocket.Server({ server: httpServer }); 1325 1326 wss.on('connection', (ws, req) => { 1327 assert.strictEqual(req.headers.authorization, undefined); 1328 assert.strictEqual(req.headers.cookie, undefined); 1329 assert.strictEqual(req.headers.host, headers.host); 1330 1331 ws.close(); 1332 }); 1333 1334 const ws = new WebSocket(`wss://localhost:${port}`, { 1335 followRedirects: true, 1336 headers, 1337 rejectUnauthorized: false 1338 }); 1339 1340 const firstRequest = ws._req; 1341 1342 assert.strictEqual( 1343 firstRequest.getHeader('Authorization'), 1344 headers.authorization 1345 ); 1346 assert.strictEqual( 1347 firstRequest.getHeader('Cookie'), 1348 headers.cookie 1349 ); 1350 assert.strictEqual(firstRequest.getHeader('Host'), headers.host); 1351 1352 ws.on('close', (code) => { 1353 assert.strictEqual(code, 1005); 1354 assert.strictEqual(ws.url, `ws://localhost:${port}/`); 1355 assert.strictEqual(ws._redirects, 1); 1356 1357 server.close(done); 1358 }); 1359 }); 1360 }); 1361 }); 1362 1363 describe("If there is at least one 'redirect' event listener", () => { 1364 it('does not drop any headers by default', (done) => { 1365 const httpServer = http.createServer(); 1366 const httpsServer = https.createServer({ 1367 cert: fs.readFileSync('test/fixtures/certificate.pem'), 1368 key: fs.readFileSync('test/fixtures/key.pem') 1369 }); 1370 const server = proxy(httpServer, httpsServer); 1371 1372 server.listen(() => { 1373 const port = server.address().port; 1374 1375 httpsServer.on('upgrade', (req, socket) => { 1376 socket.on('error', NOOP); 1377 socket.end( 1378 'HTTP/1.1 302 Found\r\n' + 1379 `Location: ws://localhost:${port}/\r\n\r\n` 1380 ); 1381 }); 1382 1383 const headers = { 1384 authorization: 'Basic Zm9vOmJhcg==', 1385 cookie: 'foo=bar', 1386 host: 'foo' 1387 }; 1388 1389 const wss = new WebSocket.Server({ server: httpServer }); 1390 1391 wss.on('connection', (ws, req) => { 1392 assert.strictEqual( 1393 req.headers.authorization, 1394 headers.authorization 1395 ); 1396 assert.strictEqual(req.headers.cookie, headers.cookie); 1397 assert.strictEqual(req.headers.host, headers.host); 1398 1399 ws.close(); 1400 }); 1401 1402 const ws = new WebSocket(`wss://localhost:${port}`, { 1403 followRedirects: true, 1404 headers, 1405 rejectUnauthorized: false 1406 }); 1407 1408 const firstRequest = ws._req; 1409 1410 assert.strictEqual( 1411 firstRequest.getHeader('Authorization'), 1412 headers.authorization 1413 ); 1414 assert.strictEqual( 1415 firstRequest.getHeader('Cookie'), 1416 headers.cookie 1417 ); 1418 assert.strictEqual(firstRequest.getHeader('Host'), headers.host); 1419 1420 ws.on('redirect', (url, req) => { 1421 assert.strictEqual(ws._redirects, 1); 1422 assert.strictEqual(url, `ws://localhost:${port}/`); 1423 assert.notStrictEqual(firstRequest, req); 1424 assert.strictEqual( 1425 req.getHeader('Authorization'), 1426 headers.authorization 1427 ); 1428 assert.strictEqual(req.getHeader('Cookie'), headers.cookie); 1429 assert.strictEqual(req.getHeader('Host'), headers.host); 1430 1431 ws.on('close', (code) => { 1432 assert.strictEqual(code, 1005); 1433 server.close(done); 1434 }); 1435 }); 1436 }); 1437 }); 1438 }); 1439 }); 1440 1441 describe('When the redirect host is different', () => { 1442 describe("If there is no 'redirect' event listener", () => { 1443 it('drops the `auth` option', (done) => { 1444 const wss = new WebSocket.Server({ port: 0 }, () => { 1445 const port = wss.address().port; 1446 1447 server.once('upgrade', (req, socket) => { 1448 socket.end( 1449 'HTTP/1.1 302 Found\r\n' + 1450 `Location: ws://localhost:${port}/\r\n\r\n` 1451 ); 1452 }); 1453 1454 const ws = new WebSocket( 1455 `ws://localhost:${server.address().port}`, 1456 { 1457 auth: 'foo:bar', 1458 followRedirects: true 1459 } 1460 ); 1461 1462 assert.strictEqual( 1463 ws._req.getHeader('Authorization'), 1464 'Basic Zm9vOmJhcg==' 1465 ); 1466 1467 ws.on('close', (code) => { 1468 assert.strictEqual(code, 1005); 1469 assert.strictEqual(ws.url, `ws://localhost:${port}/`); 1470 assert.strictEqual(ws._redirects, 1); 1471 1472 wss.close(done); 1473 }); 1474 }); 1475 1476 wss.on('connection', (ws, req) => { 1477 assert.strictEqual(req.headers.authorization, undefined); 1478 ws.close(); 1479 }); 1480 }); 1481 1482 it('drops the Authorization, Cookie and Host headers (1/4)', (done) => { 1483 // Test the `ws:` to `ws:` case. 1484 1485 const wss = new WebSocket.Server({ port: 0 }, () => { 1486 const port = wss.address().port; 1487 1488 server.once('upgrade', (req, socket) => { 1489 socket.end( 1490 'HTTP/1.1 302 Found\r\n' + 1491 `Location: ws://localhost:${port}/\r\n\r\n` 1492 ); 1493 }); 1494 1495 const headers = { 1496 authorization: 'Basic Zm9vOmJhcg==', 1497 cookie: 'foo=bar', 1498 host: 'foo' 1499 }; 1500 1501 const ws = new WebSocket( 1502 `ws://localhost:${server.address().port}`, 1503 { followRedirects: true, headers } 1504 ); 1505 1506 const firstRequest = ws._req; 1507 1508 assert.strictEqual( 1509 firstRequest.getHeader('Authorization'), 1510 headers.authorization 1511 ); 1512 assert.strictEqual( 1513 firstRequest.getHeader('Cookie'), 1514 headers.cookie 1515 ); 1516 assert.strictEqual(firstRequest.getHeader('Host'), headers.host); 1517 1518 ws.on('close', (code) => { 1519 assert.strictEqual(code, 1005); 1520 assert.strictEqual(ws.url, `ws://localhost:${port}/`); 1521 assert.strictEqual(ws._redirects, 1); 1522 1523 wss.close(done); 1524 }); 1525 }); 1526 1527 wss.on('connection', (ws, req) => { 1528 assert.strictEqual(req.headers.authorization, undefined); 1529 assert.strictEqual(req.headers.cookie, undefined); 1530 assert.strictEqual( 1531 req.headers.host, 1532 `localhost:${wss.address().port}` 1533 ); 1534 1535 ws.close(); 1536 }); 1537 }); 1538 1539 it('drops the Authorization, Cookie and Host headers (2/4)', function (done) { 1540 if (process.platform === 'win32') return this.skip(); 1541 1542 // Test the `ws:` to `ws+unix:` case. 1543 1544 const socketPath = path.join( 1545 os.tmpdir(), 1546 `ws.${crypto.randomBytes(16).toString('hex')}.sock` 1547 ); 1548 1549 server.once('upgrade', (req, socket) => { 1550 socket.end( 1551 `HTTP/1.1 302 Found\r\nLocation: ws+unix://${socketPath}\r\n\r\n` 1552 ); 1553 }); 1554 1555 const redirectedServer = http.createServer(); 1556 const wss = new WebSocket.Server({ server: redirectedServer }); 1557 1558 wss.on('connection', (ws, req) => { 1559 assert.strictEqual(req.headers.authorization, undefined); 1560 assert.strictEqual(req.headers.cookie, undefined); 1561 assert.strictEqual(req.headers.host, 'localhost'); 1562 1563 ws.close(); 1564 }); 1565 1566 redirectedServer.listen(socketPath, () => { 1567 const headers = { 1568 authorization: 'Basic Zm9vOmJhcg==', 1569 cookie: 'foo=bar', 1570 host: 'foo' 1571 }; 1572 1573 const ws = new WebSocket( 1574 `ws://localhost:${server.address().port}`, 1575 { followRedirects: true, headers } 1576 ); 1577 1578 const firstRequest = ws._req; 1579 1580 assert.strictEqual( 1581 firstRequest.getHeader('Authorization'), 1582 headers.authorization 1583 ); 1584 assert.strictEqual( 1585 firstRequest.getHeader('Cookie'), 1586 headers.cookie 1587 ); 1588 assert.strictEqual(firstRequest.getHeader('Host'), headers.host); 1589 1590 ws.on('close', (code) => { 1591 assert.strictEqual(code, 1005); 1592 assert.strictEqual(ws.url, `ws+unix://${socketPath}`); 1593 assert.strictEqual(ws._redirects, 1); 1594 1595 redirectedServer.close(done); 1596 }); 1597 }); 1598 }); 1599 1600 it('drops the Authorization, Cookie and Host headers (3/4)', function (done) { 1601 if (process.platform === 'win32') return this.skip(); 1602 1603 // Test the `ws+unix:` to `ws+unix:` case. 1604 1605 const redirectingServerSocketPath = path.join( 1606 os.tmpdir(), 1607 `ws.${crypto.randomBytes(16).toString('hex')}.sock` 1608 ); 1609 const redirectedServerSocketPath = path.join( 1610 os.tmpdir(), 1611 `ws.${crypto.randomBytes(16).toString('hex')}.sock` 1612 ); 1613 1614 const redirectingServer = http.createServer(); 1615 1616 redirectingServer.on('upgrade', (req, socket) => { 1617 socket.end( 1618 'HTTP/1.1 302 Found\r\n' + 1619 `Location: ws+unix://${redirectedServerSocketPath}\r\n\r\n` 1620 ); 1621 }); 1622 1623 const redirectedServer = http.createServer(); 1624 const wss = new WebSocket.Server({ server: redirectedServer }); 1625 1626 wss.on('connection', (ws, req) => { 1627 assert.strictEqual(req.headers.authorization, undefined); 1628 assert.strictEqual(req.headers.cookie, undefined); 1629 assert.strictEqual(req.headers.host, 'localhost'); 1630 1631 ws.close(); 1632 }); 1633 1634 redirectingServer.listen(redirectingServerSocketPath, listening); 1635 redirectedServer.listen(redirectedServerSocketPath, listening); 1636 1637 let callCount = 0; 1638 1639 function listening() { 1640 if (++callCount !== 2) return; 1641 1642 const headers = { 1643 authorization: 'Basic Zm9vOmJhcg==', 1644 cookie: 'foo=bar', 1645 host: 'foo' 1646 }; 1647 1648 const ws = new WebSocket( 1649 `ws+unix://${redirectingServerSocketPath}`, 1650 { followRedirects: true, headers } 1651 ); 1652 1653 const firstRequest = ws._req; 1654 1655 assert.strictEqual( 1656 firstRequest.getHeader('Authorization'), 1657 headers.authorization 1658 ); 1659 assert.strictEqual( 1660 firstRequest.getHeader('Cookie'), 1661 headers.cookie 1662 ); 1663 assert.strictEqual(firstRequest.getHeader('Host'), headers.host); 1664 1665 ws.on('close', (code) => { 1666 assert.strictEqual(code, 1005); 1667 assert.strictEqual( 1668 ws.url, 1669 `ws+unix://${redirectedServerSocketPath}` 1670 ); 1671 assert.strictEqual(ws._redirects, 1); 1672 1673 redirectingServer.close(); 1674 redirectedServer.close(done); 1675 }); 1676 } 1677 }); 1678 1679 it('drops the Authorization, Cookie and Host headers (4/4)', function (done) { 1680 if (process.platform === 'win32') return this.skip(); 1681 1682 // Test the `ws+unix:` to `ws:` case. 1683 1684 const redirectingServer = http.createServer(); 1685 const redirectedServer = http.createServer(); 1686 const wss = new WebSocket.Server({ server: redirectedServer }); 1687 1688 wss.on('connection', (ws, req) => { 1689 assert.strictEqual(req.headers.authorization, undefined); 1690 assert.strictEqual(req.headers.cookie, undefined); 1691 assert.strictEqual( 1692 req.headers.host, 1693 `localhost:${redirectedServer.address().port}` 1694 ); 1695 1696 ws.close(); 1697 }); 1698 1699 const socketPath = path.join( 1700 os.tmpdir(), 1701 `ws.${crypto.randomBytes(16).toString('hex')}.sock` 1702 ); 1703 1704 redirectingServer.listen(socketPath, listening); 1705 redirectedServer.listen(0, listening); 1706 1707 let callCount = 0; 1708 1709 function listening() { 1710 if (++callCount !== 2) return; 1711 1712 const port = redirectedServer.address().port; 1713 1714 redirectingServer.on('upgrade', (req, socket) => { 1715 socket.end( 1716 `HTTP/1.1 302 Found\r\nLocation: ws://localhost:${port}\r\n\r\n` 1717 ); 1718 }); 1719 1720 const headers = { 1721 authorization: 'Basic Zm9vOmJhcg==', 1722 cookie: 'foo=bar', 1723 host: 'foo' 1724 }; 1725 1726 const ws = new WebSocket(`ws+unix://${socketPath}`, { 1727 followRedirects: true, 1728 headers 1729 }); 1730 1731 const firstRequest = ws._req; 1732 1733 assert.strictEqual( 1734 firstRequest.getHeader('Authorization'), 1735 headers.authorization 1736 ); 1737 assert.strictEqual( 1738 firstRequest.getHeader('Cookie'), 1739 headers.cookie 1740 ); 1741 assert.strictEqual(firstRequest.getHeader('Host'), headers.host); 1742 1743 ws.on('close', (code) => { 1744 assert.strictEqual(code, 1005); 1745 assert.strictEqual(ws.url, `ws://localhost:${port}/`); 1746 assert.strictEqual(ws._redirects, 1); 1747 1748 redirectingServer.close(); 1749 redirectedServer.close(done); 1750 }); 1751 } 1752 }); 1753 }); 1754 1755 describe("If there is at least one 'redirect' event listener", () => { 1756 it('does not drop any headers by default', (done) => { 1757 const headers = { 1758 authorization: 'Basic Zm9vOmJhcg==', 1759 cookie: 'foo=bar', 1760 host: 'foo' 1761 }; 1762 1763 const wss = new WebSocket.Server({ port: 0 }, () => { 1764 const port = wss.address().port; 1765 1766 server.once('upgrade', (req, socket) => { 1767 socket.end( 1768 'HTTP/1.1 302 Found\r\n' + 1769 `Location: ws://localhost:${port}/\r\n\r\n` 1770 ); 1771 }); 1772 1773 const ws = new WebSocket( 1774 `ws://localhost:${server.address().port}`, 1775 { followRedirects: true, headers } 1776 ); 1777 1778 const firstRequest = ws._req; 1779 1780 assert.strictEqual( 1781 firstRequest.getHeader('Authorization'), 1782 headers.authorization 1783 ); 1784 assert.strictEqual( 1785 firstRequest.getHeader('Cookie'), 1786 headers.cookie 1787 ); 1788 assert.strictEqual(firstRequest.getHeader('Host'), headers.host); 1789 1790 ws.on('redirect', (url, req) => { 1791 assert.strictEqual(ws._redirects, 1); 1792 assert.strictEqual(url, `ws://localhost:${port}/`); 1793 assert.notStrictEqual(firstRequest, req); 1794 assert.strictEqual( 1795 req.getHeader('Authorization'), 1796 headers.authorization 1797 ); 1798 assert.strictEqual(req.getHeader('Cookie'), headers.cookie); 1799 assert.strictEqual(req.getHeader('Host'), headers.host); 1800 1801 ws.on('close', (code) => { 1802 assert.strictEqual(code, 1005); 1803 wss.close(done); 1804 }); 1805 }); 1806 }); 1807 1808 wss.on('connection', (ws, req) => { 1809 assert.strictEqual( 1810 req.headers.authorization, 1811 headers.authorization 1812 ); 1813 assert.strictEqual(req.headers.cookie, headers.cookie); 1814 assert.strictEqual(req.headers.host, headers.host); 1815 ws.close(); 1816 }); 1817 }); 1818 }); 1819 }); 1820 1821 describe("In a listener of the 'redirect' event", () => { 1822 it('allows to abort the request without swallowing errors', (done) => { 1823 server.once('upgrade', (req, socket) => { 1824 socket.end('HTTP/1.1 302 Found\r\nLocation: /foo\r\n\r\n'); 1825 }); 1826 1827 const port = server.address().port; 1828 const ws = new WebSocket(`ws://localhost:${port}`, { 1829 followRedirects: true 1830 }); 1831 1832 ws.on('redirect', (url, req) => { 1833 assert.strictEqual(ws._redirects, 1); 1834 assert.strictEqual(url, `ws://localhost:${port}/foo`); 1835 1836 req.on('socket', () => { 1837 req.abort(); 1838 }); 1839 1840 ws.on('error', (err) => { 1841 assert.ok(err instanceof Error); 1842 assert.strictEqual(err.message, 'socket hang up'); 1843 1844 ws.on('close', (code) => { 1845 assert.strictEqual(code, 1006); 1846 done(); 1847 }); 1848 }); 1849 }); 1850 }); 1851 1852 it('allows to remove headers', (done) => { 1853 const wss = new WebSocket.Server({ port: 0 }, () => { 1854 const port = wss.address().port; 1855 1856 server.once('upgrade', (req, socket) => { 1857 socket.end( 1858 'HTTP/1.1 302 Found\r\n' + 1859 `Location: ws://localhost:${port}/\r\n\r\n` 1860 ); 1861 }); 1862 1863 const headers = { 1864 authorization: 'Basic Zm9vOmJhcg==', 1865 cookie: 'foo=bar' 1866 }; 1867 1868 const ws = new WebSocket(`ws://localhost:${server.address().port}`, { 1869 followRedirects: true, 1870 headers 1871 }); 1872 1873 ws.on('redirect', (url, req) => { 1874 assert.strictEqual(ws._redirects, 1); 1875 assert.strictEqual(url, `ws://localhost:${port}/`); 1876 assert.strictEqual( 1877 req.getHeader('Authorization'), 1878 headers.authorization 1879 ); 1880 assert.strictEqual(req.getHeader('Cookie'), headers.cookie); 1881 1882 req.removeHeader('authorization'); 1883 req.removeHeader('cookie'); 1884 1885 ws.on('close', (code) => { 1886 assert.strictEqual(code, 1005); 1887 wss.close(done); 1888 }); 1889 }); 1890 }); 1891 1892 wss.on('connection', (ws, req) => { 1893 assert.strictEqual(req.headers.authorization, undefined); 1894 assert.strictEqual(req.headers.cookie, undefined); 1895 ws.close(); 1896 }); 1897 }); 1898 }); 1899 }); 1900 1901 describe('Connection with query string', () => { 1902 it('connects when pathname is not null', (done) => { 1903 const wss = new WebSocket.Server({ port: 0 }, () => { 1904 const port = wss.address().port; 1905 const ws = new WebSocket(`ws://localhost:${port}/?token=qwerty`); 1906 1907 ws.on('open', () => { 1908 wss.close(done); 1909 }); 1910 }); 1911 1912 wss.on('connection', (ws) => { 1913 ws.close(); 1914 }); 1915 }); 1916 1917 it('connects when pathname is null', (done) => { 1918 const wss = new WebSocket.Server({ port: 0 }, () => { 1919 const port = wss.address().port; 1920 const ws = new WebSocket(`ws://localhost:${port}?token=qwerty`); 1921 1922 ws.on('open', () => { 1923 wss.close(done); 1924 }); 1925 }); 1926 1927 wss.on('connection', (ws) => { 1928 ws.close(); 1929 }); 1930 }); 1931 }); 1932 1933 describe('#pause', () => { 1934 it('does nothing if `readyState` is `CONNECTING` or `CLOSED`', (done) => { 1935 const wss = new WebSocket.Server({ port: 0 }, () => { 1936 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 1937 1938 assert.strictEqual(ws.readyState, WebSocket.CONNECTING); 1939 assert.ok(!ws.isPaused); 1940 1941 ws.pause(); 1942 assert.ok(!ws.isPaused); 1943 1944 ws.on('open', () => { 1945 ws.on('close', () => { 1946 assert.strictEqual(ws.readyState, WebSocket.CLOSED); 1947 1948 ws.pause(); 1949 assert.ok(!ws.isPaused); 1950 1951 wss.close(done); 1952 }); 1953 1954 ws.close(); 1955 }); 1956 }); 1957 }); 1958 1959 it('pauses the socket', (done) => { 1960 const wss = new WebSocket.Server({ port: 0 }, () => { 1961 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 1962 }); 1963 1964 wss.on('connection', (ws) => { 1965 assert.ok(!ws.isPaused); 1966 assert.ok(!ws._socket.isPaused()); 1967 1968 ws.pause(); 1969 assert.ok(ws.isPaused); 1970 assert.ok(ws._socket.isPaused()); 1971 1972 ws.terminate(); 1973 wss.close(done); 1974 }); 1975 }); 1976 }); 1977 1978 describe('#ping', () => { 1979 it('throws an error if `readyState` is `CONNECTING`', () => { 1980 const ws = new WebSocket('ws://localhost', { 1981 lookup() {} 1982 }); 1983 1984 assert.throws( 1985 () => ws.ping(), 1986 /^Error: WebSocket is not open: readyState 0 \(CONNECTING\)$/ 1987 ); 1988 1989 assert.throws( 1990 () => ws.ping(NOOP), 1991 /^Error: WebSocket is not open: readyState 0 \(CONNECTING\)$/ 1992 ); 1993 }); 1994 1995 it('increases `bufferedAmount` if `readyState` is 2 or 3', (done) => { 1996 const ws = new WebSocket('ws://localhost', { 1997 lookup() {} 1998 }); 1999 2000 ws.on('error', (err) => { 2001 assert.ok(err instanceof Error); 2002 assert.strictEqual( 2003 err.message, 2004 'WebSocket was closed before the connection was established' 2005 ); 2006 2007 assert.strictEqual(ws.readyState, WebSocket.CLOSING); 2008 assert.strictEqual(ws.bufferedAmount, 0); 2009 2010 ws.ping('hi'); 2011 assert.strictEqual(ws.bufferedAmount, 2); 2012 2013 ws.ping(); 2014 assert.strictEqual(ws.bufferedAmount, 2); 2015 2016 ws.on('close', () => { 2017 assert.strictEqual(ws.readyState, WebSocket.CLOSED); 2018 2019 ws.ping('hi'); 2020 assert.strictEqual(ws.bufferedAmount, 4); 2021 2022 ws.ping(); 2023 assert.strictEqual(ws.bufferedAmount, 4); 2024 2025 done(); 2026 }); 2027 }); 2028 2029 ws.close(); 2030 }); 2031 2032 it('calls the callback w/ an error if `readyState` is 2 or 3', (done) => { 2033 const wss = new WebSocket.Server({ port: 0 }, () => { 2034 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2035 }); 2036 2037 wss.on('connection', (ws) => { 2038 ws.close(); 2039 2040 assert.strictEqual(ws.bufferedAmount, 0); 2041 2042 ws.ping('hi', (err) => { 2043 assert.ok(err instanceof Error); 2044 assert.strictEqual( 2045 err.message, 2046 'WebSocket is not open: readyState 2 (CLOSING)' 2047 ); 2048 assert.strictEqual(ws.bufferedAmount, 2); 2049 2050 ws.on('close', () => { 2051 ws.ping((err) => { 2052 assert.ok(err instanceof Error); 2053 assert.strictEqual( 2054 err.message, 2055 'WebSocket is not open: readyState 3 (CLOSED)' 2056 ); 2057 assert.strictEqual(ws.bufferedAmount, 2); 2058 2059 wss.close(done); 2060 }); 2061 }); 2062 }); 2063 }); 2064 }); 2065 2066 it('can send a ping with no data', (done) => { 2067 const wss = new WebSocket.Server({ port: 0 }, () => { 2068 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2069 2070 ws.on('open', () => { 2071 ws.ping(() => { 2072 ws.ping(); 2073 ws.close(); 2074 }); 2075 }); 2076 }); 2077 2078 wss.on('connection', (ws) => { 2079 let pings = 0; 2080 ws.on('ping', (data) => { 2081 assert.ok(Buffer.isBuffer(data)); 2082 assert.strictEqual(data.length, 0); 2083 if (++pings === 2) wss.close(done); 2084 }); 2085 }); 2086 }); 2087 2088 it('can send a ping with data', (done) => { 2089 const wss = new WebSocket.Server({ port: 0 }, () => { 2090 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2091 2092 ws.on('open', () => { 2093 ws.ping('hi', () => { 2094 ws.ping('hi', true); 2095 ws.close(); 2096 }); 2097 }); 2098 }); 2099 2100 wss.on('connection', (ws) => { 2101 let pings = 0; 2102 ws.on('ping', (message) => { 2103 assert.strictEqual(message.toString(), 'hi'); 2104 if (++pings === 2) wss.close(done); 2105 }); 2106 }); 2107 }); 2108 2109 it('can send numbers as ping payload', (done) => { 2110 const wss = new WebSocket.Server({ port: 0 }, () => { 2111 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2112 2113 ws.on('open', () => { 2114 ws.ping(0); 2115 ws.close(); 2116 }); 2117 }); 2118 2119 wss.on('connection', (ws) => { 2120 ws.on('ping', (message) => { 2121 assert.strictEqual(message.toString(), '0'); 2122 wss.close(done); 2123 }); 2124 }); 2125 }); 2126 2127 it('throws an error if the data size is greater than 125 bytes', (done) => { 2128 const wss = new WebSocket.Server({ port: 0 }, () => { 2129 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2130 2131 ws.on('open', () => { 2132 assert.throws( 2133 () => ws.ping(Buffer.alloc(126)), 2134 /^RangeError: The data size must not be greater than 125 bytes$/ 2135 ); 2136 2137 wss.close(done); 2138 }); 2139 }); 2140 2141 wss.on('connection', (ws) => { 2142 ws.close(); 2143 }); 2144 }); 2145 }); 2146 2147 describe('#pong', () => { 2148 it('throws an error if `readyState` is `CONNECTING`', () => { 2149 const ws = new WebSocket('ws://localhost', { 2150 lookup() {} 2151 }); 2152 2153 assert.throws( 2154 () => ws.pong(), 2155 /^Error: WebSocket is not open: readyState 0 \(CONNECTING\)$/ 2156 ); 2157 2158 assert.throws( 2159 () => ws.pong(NOOP), 2160 /^Error: WebSocket is not open: readyState 0 \(CONNECTING\)$/ 2161 ); 2162 }); 2163 2164 it('increases `bufferedAmount` if `readyState` is 2 or 3', (done) => { 2165 const ws = new WebSocket('ws://localhost', { 2166 lookup() {} 2167 }); 2168 2169 ws.on('error', (err) => { 2170 assert.ok(err instanceof Error); 2171 assert.strictEqual( 2172 err.message, 2173 'WebSocket was closed before the connection was established' 2174 ); 2175 2176 assert.strictEqual(ws.readyState, WebSocket.CLOSING); 2177 assert.strictEqual(ws.bufferedAmount, 0); 2178 2179 ws.pong('hi'); 2180 assert.strictEqual(ws.bufferedAmount, 2); 2181 2182 ws.pong(); 2183 assert.strictEqual(ws.bufferedAmount, 2); 2184 2185 ws.on('close', () => { 2186 assert.strictEqual(ws.readyState, WebSocket.CLOSED); 2187 2188 ws.pong('hi'); 2189 assert.strictEqual(ws.bufferedAmount, 4); 2190 2191 ws.pong(); 2192 assert.strictEqual(ws.bufferedAmount, 4); 2193 2194 done(); 2195 }); 2196 }); 2197 2198 ws.close(); 2199 }); 2200 2201 it('calls the callback w/ an error if `readyState` is 2 or 3', (done) => { 2202 const wss = new WebSocket.Server({ port: 0 }, () => { 2203 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2204 }); 2205 2206 wss.on('connection', (ws) => { 2207 ws.close(); 2208 2209 assert.strictEqual(ws.bufferedAmount, 0); 2210 2211 ws.pong('hi', (err) => { 2212 assert.ok(err instanceof Error); 2213 assert.strictEqual( 2214 err.message, 2215 'WebSocket is not open: readyState 2 (CLOSING)' 2216 ); 2217 assert.strictEqual(ws.bufferedAmount, 2); 2218 2219 ws.on('close', () => { 2220 ws.pong((err) => { 2221 assert.ok(err instanceof Error); 2222 assert.strictEqual( 2223 err.message, 2224 'WebSocket is not open: readyState 3 (CLOSED)' 2225 ); 2226 assert.strictEqual(ws.bufferedAmount, 2); 2227 2228 wss.close(done); 2229 }); 2230 }); 2231 }); 2232 }); 2233 }); 2234 2235 it('can send a pong with no data', (done) => { 2236 const wss = new WebSocket.Server({ port: 0 }, () => { 2237 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2238 2239 ws.on('open', () => { 2240 ws.pong(() => { 2241 ws.pong(); 2242 ws.close(); 2243 }); 2244 }); 2245 }); 2246 2247 wss.on('connection', (ws) => { 2248 let pongs = 0; 2249 ws.on('pong', (data) => { 2250 assert.ok(Buffer.isBuffer(data)); 2251 assert.strictEqual(data.length, 0); 2252 if (++pongs === 2) wss.close(done); 2253 }); 2254 }); 2255 }); 2256 2257 it('can send a pong with data', (done) => { 2258 const wss = new WebSocket.Server({ port: 0 }, () => { 2259 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2260 2261 ws.on('open', () => { 2262 ws.pong('hi', () => { 2263 ws.pong('hi', true); 2264 ws.close(); 2265 }); 2266 }); 2267 }); 2268 2269 wss.on('connection', (ws) => { 2270 let pongs = 0; 2271 ws.on('pong', (message) => { 2272 assert.strictEqual(message.toString(), 'hi'); 2273 if (++pongs === 2) wss.close(done); 2274 }); 2275 }); 2276 }); 2277 2278 it('can send numbers as pong payload', (done) => { 2279 const wss = new WebSocket.Server({ port: 0 }, () => { 2280 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2281 2282 ws.on('open', () => { 2283 ws.pong(0); 2284 ws.close(); 2285 }); 2286 }); 2287 2288 wss.on('connection', (ws) => { 2289 ws.on('pong', (message) => { 2290 assert.strictEqual(message.toString(), '0'); 2291 wss.close(done); 2292 }); 2293 }); 2294 }); 2295 2296 it('throws an error if the data size is greater than 125 bytes', (done) => { 2297 const wss = new WebSocket.Server({ port: 0 }, () => { 2298 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2299 2300 ws.on('open', () => { 2301 assert.throws( 2302 () => ws.pong(Buffer.alloc(126)), 2303 /^RangeError: The data size must not be greater than 125 bytes$/ 2304 ); 2305 2306 wss.close(done); 2307 }); 2308 }); 2309 2310 wss.on('connection', (ws) => { 2311 ws.close(); 2312 }); 2313 }); 2314 }); 2315 2316 describe('#resume', () => { 2317 it('does nothing if `readyState` is `CONNECTING` or `CLOSED`', (done) => { 2318 const wss = new WebSocket.Server({ port: 0 }, () => { 2319 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2320 2321 assert.strictEqual(ws.readyState, WebSocket.CONNECTING); 2322 assert.ok(!ws.isPaused); 2323 2324 // Verify that no exception is thrown. 2325 ws.resume(); 2326 2327 ws.on('open', () => { 2328 ws.pause(); 2329 assert.ok(ws.isPaused); 2330 2331 ws.on('close', () => { 2332 assert.strictEqual(ws.readyState, WebSocket.CLOSED); 2333 2334 ws.resume(); 2335 assert.ok(ws.isPaused); 2336 2337 wss.close(done); 2338 }); 2339 2340 ws.terminate(); 2341 }); 2342 }); 2343 }); 2344 2345 it('resumes the socket', (done) => { 2346 const wss = new WebSocket.Server({ port: 0 }, () => { 2347 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2348 }); 2349 2350 wss.on('connection', (ws) => { 2351 assert.ok(!ws.isPaused); 2352 assert.ok(!ws._socket.isPaused()); 2353 2354 ws.pause(); 2355 assert.ok(ws.isPaused); 2356 assert.ok(ws._socket.isPaused()); 2357 2358 ws.resume(); 2359 assert.ok(!ws.isPaused); 2360 assert.ok(!ws._socket.isPaused()); 2361 2362 ws.close(); 2363 wss.close(done); 2364 }); 2365 }); 2366 }); 2367 2368 describe('#send', () => { 2369 it('throws an error if `readyState` is `CONNECTING`', () => { 2370 const ws = new WebSocket('ws://localhost', { 2371 lookup() {} 2372 }); 2373 2374 assert.throws( 2375 () => ws.send('hi'), 2376 /^Error: WebSocket is not open: readyState 0 \(CONNECTING\)$/ 2377 ); 2378 2379 assert.throws( 2380 () => ws.send('hi', NOOP), 2381 /^Error: WebSocket is not open: readyState 0 \(CONNECTING\)$/ 2382 ); 2383 }); 2384 2385 it('increases `bufferedAmount` if `readyState` is 2 or 3', (done) => { 2386 const ws = new WebSocket('ws://localhost', { 2387 lookup() {} 2388 }); 2389 2390 ws.on('error', (err) => { 2391 assert.ok(err instanceof Error); 2392 assert.strictEqual( 2393 err.message, 2394 'WebSocket was closed before the connection was established' 2395 ); 2396 2397 assert.strictEqual(ws.readyState, WebSocket.CLOSING); 2398 assert.strictEqual(ws.bufferedAmount, 0); 2399 2400 ws.send('hi'); 2401 assert.strictEqual(ws.bufferedAmount, 2); 2402 2403 ws.send(); 2404 assert.strictEqual(ws.bufferedAmount, 2); 2405 2406 ws.on('close', () => { 2407 assert.strictEqual(ws.readyState, WebSocket.CLOSED); 2408 2409 ws.send('hi'); 2410 assert.strictEqual(ws.bufferedAmount, 4); 2411 2412 ws.send(); 2413 assert.strictEqual(ws.bufferedAmount, 4); 2414 2415 done(); 2416 }); 2417 }); 2418 2419 ws.close(); 2420 }); 2421 2422 it('calls the callback w/ an error if `readyState` is 2 or 3', (done) => { 2423 const wss = new WebSocket.Server({ port: 0 }, () => { 2424 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2425 }); 2426 2427 wss.on('connection', (ws) => { 2428 ws.close(); 2429 2430 assert.strictEqual(ws.bufferedAmount, 0); 2431 2432 ws.send('hi', (err) => { 2433 assert.ok(err instanceof Error); 2434 assert.strictEqual( 2435 err.message, 2436 'WebSocket is not open: readyState 2 (CLOSING)' 2437 ); 2438 assert.strictEqual(ws.bufferedAmount, 2); 2439 2440 ws.on('close', () => { 2441 ws.send('hi', (err) => { 2442 assert.ok(err instanceof Error); 2443 assert.strictEqual( 2444 err.message, 2445 'WebSocket is not open: readyState 3 (CLOSED)' 2446 ); 2447 assert.strictEqual(ws.bufferedAmount, 4); 2448 2449 wss.close(done); 2450 }); 2451 }); 2452 }); 2453 }); 2454 }); 2455 2456 it('can send a big binary message', (done) => { 2457 const wss = new WebSocket.Server({ port: 0 }, () => { 2458 const array = new Float32Array(5 * 1024 * 1024); 2459 2460 for (let i = 0; i < array.length; i++) { 2461 array[i] = i / 5; 2462 } 2463 2464 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2465 2466 ws.on('open', () => ws.send(array)); 2467 ws.on('message', (msg, isBinary) => { 2468 assert.deepStrictEqual(msg, Buffer.from(array.buffer)); 2469 assert.ok(isBinary); 2470 wss.close(done); 2471 }); 2472 }); 2473 2474 wss.on('connection', (ws) => { 2475 ws.on('message', (msg, isBinary) => { 2476 assert.ok(isBinary); 2477 ws.send(msg); 2478 ws.close(); 2479 }); 2480 }); 2481 }); 2482 2483 it('can send text data', (done) => { 2484 const wss = new WebSocket.Server({ port: 0 }, () => { 2485 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2486 2487 ws.on('open', () => ws.send('hi')); 2488 ws.on('message', (message, isBinary) => { 2489 assert.deepStrictEqual(message, Buffer.from('hi')); 2490 assert.ok(!isBinary); 2491 wss.close(done); 2492 }); 2493 }); 2494 2495 wss.on('connection', (ws) => { 2496 ws.on('message', (msg, isBinary) => { 2497 ws.send(msg, { binary: isBinary }); 2498 ws.close(); 2499 }); 2500 }); 2501 }); 2502 2503 it('does not override the `fin` option', (done) => { 2504 const wss = new WebSocket.Server({ port: 0 }, () => { 2505 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2506 2507 ws.on('open', () => { 2508 ws.send('fragment', { fin: false }); 2509 ws.send('fragment', { fin: true }); 2510 ws.close(); 2511 }); 2512 }); 2513 2514 wss.on('connection', (ws) => { 2515 ws.on('message', (msg, isBinary) => { 2516 assert.deepStrictEqual(msg, Buffer.from('fragmentfragment')); 2517 assert.ok(!isBinary); 2518 wss.close(done); 2519 }); 2520 }); 2521 }); 2522 2523 it('sends numbers as strings', (done) => { 2524 const wss = new WebSocket.Server({ port: 0 }, () => { 2525 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2526 2527 ws.on('open', () => { 2528 ws.send(0); 2529 ws.close(); 2530 }); 2531 }); 2532 2533 wss.on('connection', (ws) => { 2534 ws.on('message', (msg, isBinary) => { 2535 assert.deepStrictEqual(msg, Buffer.from('0')); 2536 assert.ok(!isBinary); 2537 wss.close(done); 2538 }); 2539 }); 2540 }); 2541 2542 it('can send a `TypedArray`', (done) => { 2543 const wss = new WebSocket.Server({ port: 0 }, () => { 2544 const array = new Float32Array(6); 2545 2546 for (let i = 0; i < array.length; ++i) { 2547 array[i] = i / 2; 2548 } 2549 2550 const partial = array.subarray(2, 5); 2551 const buf = Buffer.from( 2552 partial.buffer, 2553 partial.byteOffset, 2554 partial.byteLength 2555 ); 2556 2557 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2558 2559 ws.on('open', () => { 2560 ws.send(partial); 2561 ws.close(); 2562 }); 2563 2564 ws.on('message', (message, isBinary) => { 2565 assert.deepStrictEqual(message, buf); 2566 assert.ok(isBinary); 2567 wss.close(done); 2568 }); 2569 }); 2570 2571 wss.on('connection', (ws) => { 2572 ws.on('message', (msg, isBinary) => { 2573 assert.ok(isBinary); 2574 ws.send(msg); 2575 }); 2576 }); 2577 }); 2578 2579 it('can send an `ArrayBuffer`', (done) => { 2580 const wss = new WebSocket.Server({ port: 0 }, () => { 2581 const array = new Float32Array(5); 2582 2583 for (let i = 0; i < array.length; ++i) { 2584 array[i] = i / 2; 2585 } 2586 2587 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2588 2589 ws.on('open', () => { 2590 ws.send(array.buffer); 2591 ws.close(); 2592 }); 2593 2594 ws.onmessage = (event) => { 2595 assert.ok(event.data.equals(Buffer.from(array.buffer))); 2596 wss.close(done); 2597 }; 2598 }); 2599 2600 wss.on('connection', (ws) => { 2601 ws.on('message', (msg, isBinary) => { 2602 assert.ok(isBinary); 2603 ws.send(msg); 2604 }); 2605 }); 2606 }); 2607 2608 it('can send a `Buffer`', (done) => { 2609 const wss = new WebSocket.Server({ port: 0 }, () => { 2610 const buf = Buffer.from('foobar'); 2611 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2612 2613 ws.on('open', () => { 2614 ws.send(buf); 2615 ws.close(); 2616 }); 2617 2618 ws.onmessage = (event) => { 2619 assert.deepStrictEqual(event.data, buf); 2620 wss.close(done); 2621 }; 2622 }); 2623 2624 wss.on('connection', (ws) => { 2625 ws.on('message', (msg, isBinary) => { 2626 assert.ok(isBinary); 2627 ws.send(msg); 2628 }); 2629 }); 2630 }); 2631 2632 it('calls the callback when data is written out', (done) => { 2633 const wss = new WebSocket.Server({ port: 0 }, () => { 2634 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2635 2636 ws.on('open', () => { 2637 ws.send('hi', (err) => { 2638 assert.ifError(err); 2639 wss.close(done); 2640 }); 2641 }); 2642 }); 2643 2644 wss.on('connection', (ws) => { 2645 ws.close(); 2646 }); 2647 }); 2648 2649 it('works when the `data` argument is falsy', (done) => { 2650 const wss = new WebSocket.Server({ port: 0 }, () => { 2651 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2652 2653 ws.on('open', () => { 2654 ws.send(); 2655 ws.close(); 2656 }); 2657 }); 2658 2659 wss.on('connection', (ws) => { 2660 ws.on('message', (message, isBinary) => { 2661 assert.strictEqual(message, EMPTY_BUFFER); 2662 assert.ok(isBinary); 2663 wss.close(done); 2664 }); 2665 }); 2666 }); 2667 2668 it('honors the `mask` option', (done) => { 2669 let clientCloseEventEmitted = false; 2670 let serverClientCloseEventEmitted = false; 2671 2672 const wss = new WebSocket.Server({ port: 0 }, () => { 2673 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2674 2675 ws.on('open', () => ws.send('hi', { mask: false })); 2676 ws.on('close', (code, reason) => { 2677 assert.strictEqual(code, 1002); 2678 assert.deepStrictEqual(reason, EMPTY_BUFFER); 2679 2680 clientCloseEventEmitted = true; 2681 if (serverClientCloseEventEmitted) wss.close(done); 2682 }); 2683 }); 2684 2685 wss.on('connection', (ws) => { 2686 const chunks = []; 2687 2688 ws._socket.prependListener('data', (chunk) => { 2689 chunks.push(chunk); 2690 }); 2691 2692 ws.on('error', (err) => { 2693 assert.ok(err instanceof RangeError); 2694 assert.strictEqual( 2695 err.message, 2696 'Invalid WebSocket frame: MASK must be set' 2697 ); 2698 assert.ok( 2699 Buffer.concat(chunks).slice(0, 2).equals(Buffer.from('8102', 'hex')) 2700 ); 2701 2702 ws.on('close', (code, reason) => { 2703 assert.strictEqual(code, 1006); 2704 assert.strictEqual(reason, EMPTY_BUFFER); 2705 2706 serverClientCloseEventEmitted = true; 2707 if (clientCloseEventEmitted) wss.close(done); 2708 }); 2709 }); 2710 }); 2711 }); 2712 }); 2713 2714 describe('#close', () => { 2715 it('closes the connection if called while connecting (1/3)', (done) => { 2716 const wss = new WebSocket.Server({ port: 0 }, () => { 2717 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2718 2719 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 2720 ws.on('error', (err) => { 2721 assert.ok(err instanceof Error); 2722 assert.strictEqual( 2723 err.message, 2724 'WebSocket was closed before the connection was established' 2725 ); 2726 ws.on('close', () => wss.close(done)); 2727 }); 2728 ws.close(1001); 2729 }); 2730 }); 2731 2732 it('closes the connection if called while connecting (2/3)', (done) => { 2733 const wss = new WebSocket.Server( 2734 { 2735 verifyClient: (info, cb) => setTimeout(cb, 300, true), 2736 port: 0 2737 }, 2738 () => { 2739 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2740 2741 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 2742 ws.on('error', (err) => { 2743 assert.ok(err instanceof Error); 2744 assert.strictEqual( 2745 err.message, 2746 'WebSocket was closed before the connection was established' 2747 ); 2748 ws.on('close', () => wss.close(done)); 2749 }); 2750 setTimeout(() => ws.close(1001), 150); 2751 } 2752 ); 2753 }); 2754 2755 it('closes the connection if called while connecting (3/3)', (done) => { 2756 const server = http.createServer(); 2757 2758 server.listen(0, () => { 2759 const ws = new WebSocket(`ws://localhost:${server.address().port}`); 2760 2761 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 2762 ws.on('error', (err) => { 2763 assert.ok(err instanceof Error); 2764 assert.strictEqual( 2765 err.message, 2766 'WebSocket was closed before the connection was established' 2767 ); 2768 ws.on('close', () => { 2769 server.close(done); 2770 }); 2771 }); 2772 2773 ws.on('unexpected-response', (req, res) => { 2774 assert.strictEqual(res.statusCode, 502); 2775 2776 const chunks = []; 2777 2778 res.on('data', (chunk) => { 2779 chunks.push(chunk); 2780 }); 2781 2782 res.on('end', () => { 2783 assert.strictEqual(Buffer.concat(chunks).toString(), 'foo'); 2784 ws.close(); 2785 }); 2786 }); 2787 }); 2788 2789 server.on('upgrade', (req, socket) => { 2790 socket.on('end', socket.end); 2791 2792 socket.write( 2793 `HTTP/1.1 502 ${http.STATUS_CODES[502]}\r\n` + 2794 'Connection: keep-alive\r\n' + 2795 'Content-type: text/html\r\n' + 2796 'Content-Length: 3\r\n' + 2797 '\r\n' + 2798 'foo' 2799 ); 2800 }); 2801 }); 2802 2803 it('can be called from an error listener while connecting', (done) => { 2804 const ws = new WebSocket('ws://localhost:1337'); 2805 2806 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 2807 ws.on('error', (err) => { 2808 assert.ok(err instanceof Error); 2809 assert.strictEqual(err.code, 'ECONNREFUSED'); 2810 ws.close(); 2811 ws.on('close', () => done()); 2812 }); 2813 }).timeout(4000); 2814 2815 it("can be called from a listener of the 'redirect' event", (done) => { 2816 const server = http.createServer(); 2817 2818 server.once('upgrade', (req, socket) => { 2819 socket.end('HTTP/1.1 302 Found\r\nLocation: /foo\r\n\r\n'); 2820 }); 2821 2822 server.listen(() => { 2823 const port = server.address().port; 2824 const ws = new WebSocket(`ws://localhost:${port}`, { 2825 followRedirects: true 2826 }); 2827 2828 ws.on('open', () => { 2829 done(new Error("Unexpected 'open' event")); 2830 }); 2831 2832 ws.on('error', (err) => { 2833 assert.ok(err instanceof Error); 2834 assert.strictEqual( 2835 err.message, 2836 'WebSocket was closed before the connection was established' 2837 ); 2838 2839 ws.on('close', (code) => { 2840 assert.strictEqual(code, 1006); 2841 server.close(done); 2842 }); 2843 }); 2844 2845 ws.on('redirect', () => { 2846 ws.close(); 2847 }); 2848 }); 2849 }); 2850 2851 it("can be called from a listener of the 'upgrade' event", (done) => { 2852 const wss = new WebSocket.Server({ port: 0 }, () => { 2853 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2854 2855 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 2856 ws.on('error', (err) => { 2857 assert.ok(err instanceof Error); 2858 assert.strictEqual( 2859 err.message, 2860 'WebSocket was closed before the connection was established' 2861 ); 2862 ws.on('close', () => wss.close(done)); 2863 }); 2864 ws.on('upgrade', () => ws.close()); 2865 }); 2866 }); 2867 2868 it('sends the close status code only when necessary', (done) => { 2869 let sent; 2870 const wss = new WebSocket.Server({ port: 0 }, () => { 2871 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2872 2873 ws.on('open', () => { 2874 ws._socket.once('data', (data) => { 2875 sent = data; 2876 }); 2877 }); 2878 }); 2879 2880 wss.on('connection', (ws) => { 2881 ws._socket.once('data', (received) => { 2882 assert.deepStrictEqual( 2883 received.slice(0, 2), 2884 Buffer.from([0x88, 0x80]) 2885 ); 2886 assert.deepStrictEqual(sent, Buffer.from([0x88, 0x00])); 2887 2888 ws.on('close', (code, reason) => { 2889 assert.strictEqual(code, 1005); 2890 assert.strictEqual(reason, EMPTY_BUFFER); 2891 wss.close(done); 2892 }); 2893 }); 2894 ws.close(); 2895 }); 2896 }); 2897 2898 it('works when close reason is not specified', (done) => { 2899 const wss = new WebSocket.Server({ port: 0 }, () => { 2900 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2901 2902 ws.on('open', () => ws.close(1000)); 2903 }); 2904 2905 wss.on('connection', (ws) => { 2906 ws.on('close', (code, message) => { 2907 assert.strictEqual(code, 1000); 2908 assert.deepStrictEqual(message, EMPTY_BUFFER); 2909 wss.close(done); 2910 }); 2911 }); 2912 }); 2913 2914 it('works when close reason is specified', (done) => { 2915 const wss = new WebSocket.Server({ port: 0 }, () => { 2916 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2917 2918 ws.on('open', () => ws.close(1000, 'some reason')); 2919 }); 2920 2921 wss.on('connection', (ws) => { 2922 ws.on('close', (code, message) => { 2923 assert.strictEqual(code, 1000); 2924 assert.deepStrictEqual(message, Buffer.from('some reason')); 2925 wss.close(done); 2926 }); 2927 }); 2928 }); 2929 2930 it('permits all buffered data to be delivered', (done) => { 2931 const wss = new WebSocket.Server( 2932 { 2933 perMessageDeflate: { threshold: 0 }, 2934 port: 0 2935 }, 2936 () => { 2937 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2938 const messages = []; 2939 2940 ws.on('message', (message, isBinary) => { 2941 assert.ok(!isBinary); 2942 messages.push(message.toString()); 2943 }); 2944 ws.on('close', (code) => { 2945 assert.strictEqual(code, 1005); 2946 assert.deepStrictEqual(messages, ['foo', 'bar', 'baz']); 2947 wss.close(done); 2948 }); 2949 } 2950 ); 2951 2952 wss.on('connection', (ws) => { 2953 const callback = (err) => assert.ifError(err); 2954 2955 ws.send('foo', callback); 2956 ws.send('bar', callback); 2957 ws.send('baz', callback); 2958 ws.close(); 2959 ws.close(); 2960 }); 2961 }); 2962 2963 it('allows close code 1013', (done) => { 2964 const wss = new WebSocket.Server({ port: 0 }, () => { 2965 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2966 2967 ws.on('close', (code) => { 2968 assert.strictEqual(code, 1013); 2969 wss.close(done); 2970 }); 2971 }); 2972 2973 wss.on('connection', (ws) => ws.close(1013)); 2974 }); 2975 2976 it('allows close code 1014', (done) => { 2977 const wss = new WebSocket.Server({ port: 0 }, () => { 2978 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2979 2980 ws.on('close', (code) => { 2981 assert.strictEqual(code, 1014); 2982 wss.close(done); 2983 }); 2984 }); 2985 2986 wss.on('connection', (ws) => ws.close(1014)); 2987 }); 2988 2989 it('does nothing if `readyState` is `CLOSED`', (done) => { 2990 const wss = new WebSocket.Server({ port: 0 }, () => { 2991 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 2992 2993 ws.on('close', (code) => { 2994 assert.strictEqual(code, 1005); 2995 assert.strictEqual(ws.readyState, WebSocket.CLOSED); 2996 ws.close(); 2997 wss.close(done); 2998 }); 2999 }); 3000 3001 wss.on('connection', (ws) => ws.close()); 3002 }); 3003 3004 it('sets a timer for the closing handshake to complete', (done) => { 3005 const wss = new WebSocket.Server({ port: 0 }, () => { 3006 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 3007 3008 ws.on('close', (code, reason) => { 3009 assert.strictEqual(code, 1000); 3010 assert.deepStrictEqual(reason, Buffer.from('some reason')); 3011 wss.close(done); 3012 }); 3013 3014 ws.on('open', () => { 3015 let callbackCalled = false; 3016 3017 assert.strictEqual(ws._closeTimer, null); 3018 3019 ws.send('foo', () => { 3020 callbackCalled = true; 3021 }); 3022 3023 ws.close(1000, 'some reason'); 3024 3025 // 3026 // Check that the close timer is set even if the `Sender.close()` 3027 // callback is not called. 3028 // 3029 assert.strictEqual(callbackCalled, false); 3030 assert.strictEqual(ws._closeTimer._idleTimeout, 30000); 3031 }); 3032 }); 3033 }); 3034 }); 3035 3036 describe('#terminate', () => { 3037 it('closes the connection if called while connecting (1/2)', (done) => { 3038 const wss = new WebSocket.Server({ port: 0 }, () => { 3039 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 3040 3041 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 3042 ws.on('error', (err) => { 3043 assert.ok(err instanceof Error); 3044 assert.strictEqual( 3045 err.message, 3046 'WebSocket was closed before the connection was established' 3047 ); 3048 ws.on('close', () => wss.close(done)); 3049 }); 3050 ws.terminate(); 3051 }); 3052 }); 3053 3054 it('closes the connection if called while connecting (2/2)', (done) => { 3055 const wss = new WebSocket.Server( 3056 { 3057 verifyClient: (info, cb) => setTimeout(cb, 300, true), 3058 port: 0 3059 }, 3060 () => { 3061 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 3062 3063 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 3064 ws.on('error', (err) => { 3065 assert.ok(err instanceof Error); 3066 assert.strictEqual( 3067 err.message, 3068 'WebSocket was closed before the connection was established' 3069 ); 3070 ws.on('close', () => wss.close(done)); 3071 }); 3072 setTimeout(() => ws.terminate(), 150); 3073 } 3074 ); 3075 }); 3076 3077 it('can be called from an error listener while connecting', (done) => { 3078 const ws = new WebSocket('ws://localhost:1337'); 3079 3080 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 3081 ws.on('error', (err) => { 3082 assert.ok(err instanceof Error); 3083 assert.strictEqual(err.code, 'ECONNREFUSED'); 3084 ws.terminate(); 3085 ws.on('close', () => done()); 3086 }); 3087 }).timeout(4000); 3088 3089 it("can be called from a listener of the 'redirect' event", (done) => { 3090 const server = http.createServer(); 3091 3092 server.once('upgrade', (req, socket) => { 3093 socket.end('HTTP/1.1 302 Found\r\nLocation: /foo\r\n\r\n'); 3094 }); 3095 3096 server.listen(() => { 3097 const port = server.address().port; 3098 const ws = new WebSocket(`ws://localhost:${port}`, { 3099 followRedirects: true 3100 }); 3101 3102 ws.on('open', () => { 3103 done(new Error("Unexpected 'open' event")); 3104 }); 3105 3106 ws.on('error', (err) => { 3107 assert.ok(err instanceof Error); 3108 assert.strictEqual( 3109 err.message, 3110 'WebSocket was closed before the connection was established' 3111 ); 3112 3113 ws.on('close', (code) => { 3114 assert.strictEqual(code, 1006); 3115 server.close(done); 3116 }); 3117 }); 3118 3119 ws.on('redirect', () => { 3120 ws.terminate(); 3121 }); 3122 }); 3123 }); 3124 3125 it("can be called from a listener of the 'upgrade' event", (done) => { 3126 const wss = new WebSocket.Server({ port: 0 }, () => { 3127 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 3128 3129 ws.on('open', () => done(new Error("Unexpected 'open' event"))); 3130 ws.on('error', (err) => { 3131 assert.ok(err instanceof Error); 3132 assert.strictEqual( 3133 err.message, 3134 'WebSocket was closed before the connection was established' 3135 ); 3136 ws.on('close', () => wss.close(done)); 3137 }); 3138 ws.on('upgrade', () => ws.terminate()); 3139 }); 3140 }); 3141 3142 it('does nothing if `readyState` is `CLOSED`', (done) => { 3143 const wss = new WebSocket.Server({ port: 0 }, () => { 3144 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 3145 3146 ws.on('close', (code) => { 3147 assert.strictEqual(code, 1006); 3148 assert.strictEqual(ws.readyState, WebSocket.CLOSED); 3149 ws.terminate(); 3150 wss.close(done); 3151 }); 3152 }); 3153 3154 wss.on('connection', (ws) => ws.terminate()); 3155 }); 3156 }); 3157 3158 describe('WHATWG API emulation', () => { 3159 it('supports the `on{close,error,message,open}` attributes', () => { 3160 for (const property of ['onclose', 'onerror', 'onmessage', 'onopen']) { 3161 const descriptor = Object.getOwnPropertyDescriptor( 3162 WebSocket.prototype, 3163 property 3164 ); 3165 3166 assert.strictEqual(descriptor.configurable, true); 3167 assert.strictEqual(descriptor.enumerable, true); 3168 assert.ok(descriptor.get !== undefined); 3169 assert.ok(descriptor.set !== undefined); 3170 } 3171 3172 const ws = new WebSocket('ws://localhost', { agent: new CustomAgent() }); 3173 3174 assert.strictEqual(ws.onmessage, null); 3175 assert.strictEqual(ws.onclose, null); 3176 assert.strictEqual(ws.onerror, null); 3177 assert.strictEqual(ws.onopen, null); 3178 3179 ws.onmessage = NOOP; 3180 ws.onerror = NOOP; 3181 ws.onclose = NOOP; 3182 ws.onopen = NOOP; 3183 3184 assert.strictEqual(ws.onmessage, NOOP); 3185 assert.strictEqual(ws.onclose, NOOP); 3186 assert.strictEqual(ws.onerror, NOOP); 3187 assert.strictEqual(ws.onopen, NOOP); 3188 3189 ws.onmessage = 'foo'; 3190 3191 assert.strictEqual(ws.onmessage, null); 3192 assert.strictEqual(ws.listenerCount('message'), 0); 3193 }); 3194 3195 it('works like the `EventEmitter` interface', (done) => { 3196 const wss = new WebSocket.Server({ port: 0 }, () => { 3197 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 3198 3199 ws.onmessage = (messageEvent) => { 3200 assert.strictEqual(messageEvent.data, 'foo'); 3201 ws.onclose = (closeEvent) => { 3202 assert.strictEqual(closeEvent.wasClean, true); 3203 assert.strictEqual(closeEvent.code, 1005); 3204 assert.strictEqual(closeEvent.reason, ''); 3205 wss.close(done); 3206 }; 3207 ws.close(); 3208 }; 3209 3210 ws.onopen = () => ws.send('foo'); 3211 }); 3212 3213 wss.on('connection', (ws) => { 3214 ws.on('message', (msg, isBinary) => { 3215 ws.send(msg, { binary: isBinary }); 3216 }); 3217 }); 3218 }); 3219 3220 it("doesn't return listeners added with `on`", () => { 3221 const ws = new WebSocket('ws://localhost', { agent: new CustomAgent() }); 3222 3223 ws.on('open', NOOP); 3224 3225 assert.deepStrictEqual(ws.listeners('open'), [NOOP]); 3226 assert.strictEqual(ws.onopen, null); 3227 }); 3228 3229 it("doesn't remove listeners added with `on`", () => { 3230 const ws = new WebSocket('ws://localhost', { agent: new CustomAgent() }); 3231 3232 ws.on('close', NOOP); 3233 ws.onclose = NOOP; 3234 3235 let listeners = ws.listeners('close'); 3236 3237 assert.strictEqual(listeners.length, 2); 3238 assert.strictEqual(listeners[0], NOOP); 3239 assert.strictEqual(listeners[1][kListener], NOOP); 3240 3241 ws.onclose = NOOP; 3242 3243 listeners = ws.listeners('close'); 3244 3245 assert.strictEqual(listeners.length, 2); 3246 assert.strictEqual(listeners[0], NOOP); 3247 assert.strictEqual(listeners[1][kListener], NOOP); 3248 }); 3249 3250 it('supports the `addEventListener` method', () => { 3251 const events = []; 3252 const ws = new WebSocket('ws://localhost', { agent: new CustomAgent() }); 3253 3254 ws.addEventListener('foo', () => {}); 3255 assert.strictEqual(ws.listenerCount('foo'), 0); 3256 3257 ws.addEventListener('open', () => { 3258 events.push('open'); 3259 assert.strictEqual(ws.listenerCount('open'), 1); 3260 }); 3261 3262 assert.strictEqual(ws.listenerCount('open'), 1); 3263 3264 ws.addEventListener( 3265 'message', 3266 () => { 3267 events.push('message'); 3268 assert.strictEqual(ws.listenerCount('message'), 0); 3269 }, 3270 { once: true } 3271 ); 3272 3273 assert.strictEqual(ws.listenerCount('message'), 1); 3274 3275 ws.emit('open'); 3276 ws.emit('message', EMPTY_BUFFER, false); 3277 3278 assert.deepStrictEqual(events, ['open', 'message']); 3279 }); 3280 3281 it("doesn't return listeners added with `addEventListener`", () => { 3282 const ws = new WebSocket('ws://localhost', { agent: new CustomAgent() }); 3283 3284 ws.addEventListener('open', NOOP); 3285 3286 const listeners = ws.listeners('open'); 3287 3288 assert.strictEqual(listeners.length, 1); 3289 assert.strictEqual(listeners[0][kListener], NOOP); 3290 3291 assert.strictEqual(ws.onopen, null); 3292 }); 3293 3294 it("doesn't remove listeners added with `addEventListener`", () => { 3295 const ws = new WebSocket('ws://localhost', { agent: new CustomAgent() }); 3296 3297 ws.addEventListener('close', NOOP); 3298 ws.onclose = NOOP; 3299 3300 let listeners = ws.listeners('close'); 3301 3302 assert.strictEqual(listeners.length, 2); 3303 assert.strictEqual(listeners[0][kListener], NOOP); 3304 assert.strictEqual(listeners[1][kListener], NOOP); 3305 3306 ws.onclose = NOOP; 3307 3308 listeners = ws.listeners('close'); 3309 3310 assert.strictEqual(listeners.length, 2); 3311 assert.strictEqual(listeners[0][kListener], NOOP); 3312 assert.strictEqual(listeners[1][kListener], NOOP); 3313 }); 3314 3315 it('supports the `removeEventListener` method', () => { 3316 const ws = new WebSocket('ws://localhost', { agent: new CustomAgent() }); 3317 3318 ws.addEventListener('message', NOOP); 3319 ws.addEventListener('open', NOOP); 3320 3321 assert.strictEqual(ws.listeners('message')[0][kListener], NOOP); 3322 assert.strictEqual(ws.listeners('open')[0][kListener], NOOP); 3323 3324 ws.removeEventListener('message', () => {}); 3325 3326 assert.strictEqual(ws.listeners('message')[0][kListener], NOOP); 3327 3328 ws.removeEventListener('message', NOOP); 3329 ws.removeEventListener('open', NOOP); 3330 3331 assert.strictEqual(ws.listenerCount('message'), 0); 3332 assert.strictEqual(ws.listenerCount('open'), 0); 3333 3334 ws.addEventListener('message', NOOP, { once: true }); 3335 ws.addEventListener('open', NOOP, { once: true }); 3336 3337 assert.strictEqual(ws.listeners('message')[0][kListener], NOOP); 3338 assert.strictEqual(ws.listeners('open')[0][kListener], NOOP); 3339 3340 ws.removeEventListener('message', () => {}); 3341 3342 assert.strictEqual(ws.listeners('message')[0][kListener], NOOP); 3343 3344 ws.removeEventListener('message', NOOP); 3345 ws.removeEventListener('open', NOOP); 3346 3347 assert.strictEqual(ws.listenerCount('message'), 0); 3348 assert.strictEqual(ws.listenerCount('open'), 0); 3349 3350 // Multiple listeners. 3351 ws.addEventListener('message', NOOP); 3352 ws.addEventListener('message', NOOP); 3353 3354 assert.strictEqual(ws.listeners('message')[0][kListener], NOOP); 3355 assert.strictEqual(ws.listeners('message')[1][kListener], NOOP); 3356 3357 ws.removeEventListener('message', NOOP); 3358 3359 assert.strictEqual(ws.listeners('message')[0][kListener], NOOP); 3360 3361 ws.removeEventListener('message', NOOP); 3362 3363 assert.strictEqual(ws.listenerCount('message'), 0); 3364 3365 // Listeners not added with `websocket.addEventListener()`. 3366 ws.on('message', NOOP); 3367 3368 assert.deepStrictEqual(ws.listeners('message'), [NOOP]); 3369 3370 ws.removeEventListener('message', NOOP); 3371 3372 assert.deepStrictEqual(ws.listeners('message'), [NOOP]); 3373 3374 ws.onclose = NOOP; 3375 3376 assert.strictEqual(ws.listeners('close')[0][kListener], NOOP); 3377 3378 ws.removeEventListener('close', NOOP); 3379 3380 assert.strictEqual(ws.listeners('close')[0][kListener], NOOP); 3381 }); 3382 3383 it('wraps text data in a `MessageEvent`', (done) => { 3384 const wss = new WebSocket.Server({ port: 0 }, () => { 3385 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 3386 3387 ws.addEventListener('open', () => { 3388 ws.send('hi'); 3389 ws.close(); 3390 }); 3391 3392 ws.addEventListener('message', (event) => { 3393 assert.ok(event instanceof MessageEvent); 3394 assert.strictEqual(event.data, 'hi'); 3395 wss.close(done); 3396 }); 3397 }); 3398 3399 wss.on('connection', (ws) => { 3400 ws.on('message', (msg, isBinary) => { 3401 ws.send(msg, { binary: isBinary }); 3402 }); 3403 }); 3404 }); 3405 3406 it('receives a `CloseEvent` when server closes (1000)', (done) => { 3407 const wss = new WebSocket.Server({ port: 0 }, () => { 3408 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 3409 3410 ws.addEventListener('close', (event) => { 3411 assert.ok(event instanceof CloseEvent); 3412 assert.ok(event.wasClean); 3413 assert.strictEqual(event.reason, ''); 3414 assert.strictEqual(event.code, 1000); 3415 wss.close(done); 3416 }); 3417 }); 3418 3419 wss.on('connection', (ws) => ws.close(1000)); 3420 }); 3421 3422 it('receives a `CloseEvent` when server closes (4000)', (done) => { 3423 const wss = new WebSocket.Server({ port: 0 }, () => { 3424 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 3425 3426 ws.addEventListener('close', (event) => { 3427 assert.ok(event instanceof CloseEvent); 3428 assert.ok(event.wasClean); 3429 assert.strictEqual(event.reason, 'some daft reason'); 3430 assert.strictEqual(event.code, 4000); 3431 wss.close(done); 3432 }); 3433 }); 3434 3435 wss.on('connection', (ws) => ws.close(4000, 'some daft reason')); 3436 }); 3437 3438 it('sets `target` and `type` on events', (done) => { 3439 const wss = new WebSocket.Server({ port: 0 }, () => { 3440 const err = new Error('forced'); 3441 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 3442 3443 ws.addEventListener('open', (event) => { 3444 assert.ok(event instanceof Event); 3445 assert.strictEqual(event.type, 'open'); 3446 assert.strictEqual(event.target, ws); 3447 }); 3448 ws.addEventListener('message', (event) => { 3449 assert.ok(event instanceof MessageEvent); 3450 assert.strictEqual(event.type, 'message'); 3451 assert.strictEqual(event.target, ws); 3452 ws.close(); 3453 }); 3454 ws.addEventListener('close', (event) => { 3455 assert.ok(event instanceof CloseEvent); 3456 assert.strictEqual(event.type, 'close'); 3457 assert.strictEqual(event.target, ws); 3458 ws.emit('error', err); 3459 }); 3460 ws.addEventListener('error', (event) => { 3461 assert.ok(event instanceof ErrorEvent); 3462 assert.strictEqual(event.message, 'forced'); 3463 assert.strictEqual(event.type, 'error'); 3464 assert.strictEqual(event.target, ws); 3465 assert.strictEqual(event.error, err); 3466 3467 wss.close(done); 3468 }); 3469 }); 3470 3471 wss.on('connection', (client) => client.send('hi')); 3472 }); 3473 3474 it('passes binary data as a Node.js `Buffer` by default', (done) => { 3475 const wss = new WebSocket.Server({ port: 0 }, () => { 3476 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 3477 3478 ws.onmessage = (evt) => { 3479 assert.ok(Buffer.isBuffer(evt.data)); 3480 wss.close(done); 3481 }; 3482 }); 3483 3484 wss.on('connection', (ws) => { 3485 ws.send(new Uint8Array(4096)); 3486 ws.close(); 3487 }); 3488 }); 3489 3490 it('ignores `binaryType` for text messages', (done) => { 3491 const wss = new WebSocket.Server({ port: 0 }, () => { 3492 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 3493 3494 ws.binaryType = 'arraybuffer'; 3495 3496 ws.onmessage = (evt) => { 3497 assert.strictEqual(evt.data, 'foo'); 3498 wss.close(done); 3499 }; 3500 }); 3501 3502 wss.on('connection', (ws) => { 3503 ws.send('foo'); 3504 ws.close(); 3505 }); 3506 }); 3507 3508 it('allows to update `binaryType` on the fly', (done) => { 3509 const wss = new WebSocket.Server({ port: 0 }, () => { 3510 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 3511 3512 function testType(binaryType, next) { 3513 const buf = Buffer.from(binaryType); 3514 ws.binaryType = binaryType; 3515 3516 ws.onmessage = (evt) => { 3517 if (binaryType === 'nodebuffer') { 3518 assert.ok(Buffer.isBuffer(evt.data)); 3519 assert.ok(evt.data.equals(buf)); 3520 } else if (binaryType === 'arraybuffer') { 3521 assert.ok(evt.data instanceof ArrayBuffer); 3522 assert.ok(Buffer.from(evt.data).equals(buf)); 3523 } else if (binaryType === 'fragments') { 3524 assert.deepStrictEqual(evt.data, [buf]); 3525 } 3526 next(); 3527 }; 3528 3529 ws.send(buf); 3530 } 3531 3532 ws.onopen = () => { 3533 testType('nodebuffer', () => { 3534 testType('arraybuffer', () => { 3535 testType('fragments', () => { 3536 ws.close(); 3537 wss.close(done); 3538 }); 3539 }); 3540 }); 3541 }; 3542 }); 3543 3544 wss.on('connection', (ws) => { 3545 ws.on('message', (msg, isBinary) => { 3546 assert.ok(isBinary); 3547 ws.send(msg); 3548 }); 3549 }); 3550 }); 3551 }); 3552 3553 describe('SSL', () => { 3554 it('connects to secure websocket server', (done) => { 3555 const server = https.createServer({ 3556 cert: fs.readFileSync('test/fixtures/certificate.pem'), 3557 key: fs.readFileSync('test/fixtures/key.pem') 3558 }); 3559 const wss = new WebSocket.Server({ server }); 3560 3561 wss.on('connection', () => { 3562 server.close(done); 3563 }); 3564 3565 server.listen(0, () => { 3566 const ws = new WebSocket(`wss://127.0.0.1:${server.address().port}`, { 3567 rejectUnauthorized: false 3568 }); 3569 3570 ws.on('open', ws.close); 3571 }); 3572 }); 3573 3574 it('connects to secure websocket server with client side certificate', (done) => { 3575 const server = https.createServer({ 3576 cert: fs.readFileSync('test/fixtures/certificate.pem'), 3577 ca: [fs.readFileSync('test/fixtures/ca-certificate.pem')], 3578 key: fs.readFileSync('test/fixtures/key.pem'), 3579 requestCert: true 3580 }); 3581 3582 const wss = new WebSocket.Server({ noServer: true }); 3583 3584 server.on('upgrade', (request, socket, head) => { 3585 assert.ok(socket.authorized); 3586 3587 wss.handleUpgrade(request, socket, head, (ws) => { 3588 ws.on('close', (code) => { 3589 assert.strictEqual(code, 1005); 3590 server.close(done); 3591 }); 3592 }); 3593 }); 3594 3595 server.listen(0, () => { 3596 const ws = new WebSocket(`wss://localhost:${server.address().port}`, { 3597 cert: fs.readFileSync('test/fixtures/client-certificate.pem'), 3598 key: fs.readFileSync('test/fixtures/client-key.pem'), 3599 rejectUnauthorized: false 3600 }); 3601 3602 ws.on('open', ws.close); 3603 }); 3604 }); 3605 3606 it('cannot connect to secure websocket server via ws://', (done) => { 3607 const server = https.createServer({ 3608 cert: fs.readFileSync('test/fixtures/certificate.pem'), 3609 key: fs.readFileSync('test/fixtures/key.pem') 3610 }); 3611 const wss = new WebSocket.Server({ server }); 3612 3613 server.listen(0, () => { 3614 const ws = new WebSocket(`ws://localhost:${server.address().port}`, { 3615 rejectUnauthorized: false 3616 }); 3617 3618 ws.on('error', () => { 3619 server.close(done); 3620 wss.close(); 3621 }); 3622 }); 3623 }); 3624 3625 it('can send and receive text data', (done) => { 3626 const server = https.createServer({ 3627 cert: fs.readFileSync('test/fixtures/certificate.pem'), 3628 key: fs.readFileSync('test/fixtures/key.pem') 3629 }); 3630 const wss = new WebSocket.Server({ server }); 3631 3632 wss.on('connection', (ws) => { 3633 ws.on('message', (message, isBinary) => { 3634 assert.deepStrictEqual(message, Buffer.from('foobar')); 3635 assert.ok(!isBinary); 3636 server.close(done); 3637 }); 3638 }); 3639 3640 server.listen(0, () => { 3641 const ws = new WebSocket(`wss://localhost:${server.address().port}`, { 3642 rejectUnauthorized: false 3643 }); 3644 3645 ws.on('open', () => { 3646 ws.send('foobar'); 3647 ws.close(); 3648 }); 3649 }); 3650 }); 3651 3652 it('can send a big binary message', (done) => { 3653 const buf = crypto.randomBytes(5 * 1024 * 1024); 3654 const server = https.createServer({ 3655 cert: fs.readFileSync('test/fixtures/certificate.pem'), 3656 key: fs.readFileSync('test/fixtures/key.pem') 3657 }); 3658 const wss = new WebSocket.Server({ server }); 3659 3660 wss.on('connection', (ws) => { 3661 ws.on('message', (message, isBinary) => { 3662 assert.ok(isBinary); 3663 ws.send(message); 3664 ws.close(); 3665 }); 3666 }); 3667 3668 server.listen(0, () => { 3669 const ws = new WebSocket(`wss://localhost:${server.address().port}`, { 3670 rejectUnauthorized: false 3671 }); 3672 3673 ws.on('open', () => ws.send(buf)); 3674 ws.on('message', (message, isBinary) => { 3675 assert.deepStrictEqual(message, buf); 3676 assert.ok(isBinary); 3677 3678 server.close(done); 3679 }); 3680 }); 3681 }).timeout(4000); 3682 3683 it('allows to disable sending the SNI extension', (done) => { 3684 const original = tls.connect; 3685 3686 tls.connect = (options) => { 3687 assert.strictEqual(options.servername, ''); 3688 tls.connect = original; 3689 done(); 3690 }; 3691 3692 const ws = new WebSocket('wss://127.0.0.1', { servername: '' }); 3693 }); 3694 3695 it("works around a double 'error' event bug in Node.js", function (done) { 3696 // 3697 // The `minVersion` and `maxVersion` options are not supported in 3698 // Node.js < 10.16.0. 3699 // 3700 if (process.versions.modules < 64) return this.skip(); 3701 3702 // 3703 // The `'error'` event can be emitted multiple times by the 3704 // `http.ClientRequest` object in Node.js < 13. This test reproduces the 3705 // issue in Node.js 12. 3706 // 3707 const server = https.createServer({ 3708 cert: fs.readFileSync('test/fixtures/certificate.pem'), 3709 key: fs.readFileSync('test/fixtures/key.pem'), 3710 minVersion: 'TLSv1.2' 3711 }); 3712 const wss = new WebSocket.Server({ server }); 3713 3714 server.listen(0, () => { 3715 const ws = new WebSocket(`wss://localhost:${server.address().port}`, { 3716 maxVersion: 'TLSv1.1', 3717 rejectUnauthorized: false 3718 }); 3719 3720 ws.on('error', (err) => { 3721 assert.ok(err instanceof Error); 3722 server.close(done); 3723 wss.close(); 3724 }); 3725 }); 3726 }); 3727 }); 3728 3729 describe('Request headers', () => { 3730 it('adds the authorization header if the url has userinfo', (done) => { 3731 const agent = new CustomAgent(); 3732 const userinfo = 'test:testpass'; 3733 3734 agent.addRequest = (req) => { 3735 assert.strictEqual( 3736 req.getHeader('authorization'), 3737 `Basic ${Buffer.from(userinfo).toString('base64')}` 3738 ); 3739 done(); 3740 }; 3741 3742 const ws = new WebSocket(`ws://${userinfo}@localhost`, { agent }); 3743 }); 3744 3745 it('honors the `auth` option', (done) => { 3746 const agent = new CustomAgent(); 3747 const auth = 'user:pass'; 3748 3749 agent.addRequest = (req) => { 3750 assert.strictEqual( 3751 req.getHeader('authorization'), 3752 `Basic ${Buffer.from(auth).toString('base64')}` 3753 ); 3754 done(); 3755 }; 3756 3757 const ws = new WebSocket('ws://localhost', { agent, auth }); 3758 }); 3759 3760 it('favors the url userinfo over the `auth` option', (done) => { 3761 const agent = new CustomAgent(); 3762 const auth = 'foo:bar'; 3763 const userinfo = 'baz:qux'; 3764 3765 agent.addRequest = (req) => { 3766 assert.strictEqual( 3767 req.getHeader('authorization'), 3768 `Basic ${Buffer.from(userinfo).toString('base64')}` 3769 ); 3770 done(); 3771 }; 3772 3773 const ws = new WebSocket(`ws://${userinfo}@localhost`, { agent, auth }); 3774 }); 3775 3776 it('adds custom headers', (done) => { 3777 const agent = new CustomAgent(); 3778 3779 agent.addRequest = (req) => { 3780 assert.strictEqual(req.getHeader('cookie'), 'foo=bar'); 3781 done(); 3782 }; 3783 3784 const ws = new WebSocket('ws://localhost', { 3785 headers: { Cookie: 'foo=bar' }, 3786 agent 3787 }); 3788 }); 3789 3790 it('excludes default ports from host header', () => { 3791 const options = { lookup() {} }; 3792 const variants = [ 3793 ['wss://localhost:8443', 'localhost:8443'], 3794 ['wss://localhost:443', 'localhost'], 3795 ['ws://localhost:88', 'localhost:88'], 3796 ['ws://localhost:80', 'localhost'] 3797 ]; 3798 3799 for (const [url, host] of variants) { 3800 const ws = new WebSocket(url, options); 3801 assert.strictEqual(ws._req.getHeader('host'), host); 3802 } 3803 }); 3804 3805 it("doesn't add the origin header by default", (done) => { 3806 const agent = new CustomAgent(); 3807 3808 agent.addRequest = (req) => { 3809 assert.strictEqual(req.getHeader('origin'), undefined); 3810 done(); 3811 }; 3812 3813 const ws = new WebSocket('ws://localhost', { agent }); 3814 }); 3815 3816 it('honors the `origin` option (1/2)', (done) => { 3817 const agent = new CustomAgent(); 3818 3819 agent.addRequest = (req) => { 3820 assert.strictEqual(req.getHeader('origin'), 'https://example.com:8000'); 3821 done(); 3822 }; 3823 3824 const ws = new WebSocket('ws://localhost', { 3825 origin: 'https://example.com:8000', 3826 agent 3827 }); 3828 }); 3829 3830 it('honors the `origin` option (2/2)', (done) => { 3831 const agent = new CustomAgent(); 3832 3833 agent.addRequest = (req) => { 3834 assert.strictEqual( 3835 req.getHeader('sec-websocket-origin'), 3836 'https://example.com:8000' 3837 ); 3838 done(); 3839 }; 3840 3841 const ws = new WebSocket('ws://localhost', { 3842 origin: 'https://example.com:8000', 3843 protocolVersion: 8, 3844 agent 3845 }); 3846 }); 3847 }); 3848 3849 describe('permessage-deflate', () => { 3850 it('is enabled by default', (done) => { 3851 const agent = new CustomAgent(); 3852 3853 agent.addRequest = (req) => { 3854 assert.strictEqual( 3855 req.getHeader('sec-websocket-extensions'), 3856 'permessage-deflate; client_max_window_bits' 3857 ); 3858 done(); 3859 }; 3860 3861 const ws = new WebSocket('ws://localhost', { agent }); 3862 }); 3863 3864 it('can be disabled', (done) => { 3865 const agent = new CustomAgent(); 3866 3867 agent.addRequest = (req) => { 3868 assert.strictEqual( 3869 req.getHeader('sec-websocket-extensions'), 3870 undefined 3871 ); 3872 done(); 3873 }; 3874 3875 const ws = new WebSocket('ws://localhost', { 3876 perMessageDeflate: false, 3877 agent 3878 }); 3879 }); 3880 3881 it('can send extension parameters', (done) => { 3882 const agent = new CustomAgent(); 3883 3884 const value = 3885 'permessage-deflate; server_no_context_takeover;' + 3886 ' client_no_context_takeover; server_max_window_bits=10;' + 3887 ' client_max_window_bits'; 3888 3889 agent.addRequest = (req) => { 3890 assert.strictEqual(req.getHeader('sec-websocket-extensions'), value); 3891 done(); 3892 }; 3893 3894 const ws = new WebSocket('ws://localhost', { 3895 perMessageDeflate: { 3896 clientNoContextTakeover: true, 3897 serverNoContextTakeover: true, 3898 clientMaxWindowBits: true, 3899 serverMaxWindowBits: 10 3900 }, 3901 agent 3902 }); 3903 }); 3904 3905 it('consumes all received data when connection is closed (1/2)', (done) => { 3906 const wss = new WebSocket.Server( 3907 { 3908 perMessageDeflate: { threshold: 0 }, 3909 port: 0 3910 }, 3911 () => { 3912 const messages = []; 3913 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 3914 3915 ws.on('open', () => { 3916 ws._socket.on('close', () => { 3917 assert.strictEqual(ws._receiver._state, 5); 3918 }); 3919 }); 3920 3921 ws.on('message', (message, isBinary) => { 3922 assert.ok(!isBinary); 3923 messages.push(message.toString()); 3924 }); 3925 3926 ws.on('close', (code) => { 3927 assert.strictEqual(code, 1006); 3928 assert.deepStrictEqual(messages, ['foo', 'bar', 'baz', 'qux']); 3929 wss.close(done); 3930 }); 3931 } 3932 ); 3933 3934 wss.on('connection', (ws) => { 3935 ws.send('foo'); 3936 ws.send('bar'); 3937 ws.send('baz'); 3938 ws.send('qux', () => ws._socket.end()); 3939 }); 3940 }); 3941 3942 it('consumes all received data when connection is closed (2/2)', (done) => { 3943 const wss = new WebSocket.Server( 3944 { 3945 perMessageDeflate: true, 3946 port: 0 3947 }, 3948 () => { 3949 const messageLengths = []; 3950 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 3951 3952 ws.on('open', () => { 3953 ws._socket.prependListener('close', () => { 3954 assert.strictEqual(ws._receiver._state, 5); 3955 assert.strictEqual(ws._socket._readableState.length, 3); 3956 }); 3957 3958 const push = ws._socket.push; 3959 3960 // Override `ws._socket.push()` to know exactly when data is 3961 // received and call `ws.terminate()` immediately after that without 3962 // relying on a timer. 3963 ws._socket.push = (data) => { 3964 ws._socket.push = push; 3965 ws._socket.push(data); 3966 ws.terminate(); 3967 }; 3968 3969 const payload1 = Buffer.alloc(15 * 1024); 3970 const payload2 = Buffer.alloc(1); 3971 3972 const opts = { 3973 fin: true, 3974 opcode: 0x02, 3975 mask: false, 3976 readOnly: false 3977 }; 3978 3979 const list = [ 3980 ...Sender.frame(payload1, { rsv1: false, ...opts }), 3981 ...Sender.frame(payload2, { rsv1: true, ...opts }) 3982 ]; 3983 3984 for (let i = 0; i < 399; i++) { 3985 list.push(list[list.length - 2], list[list.length - 1]); 3986 } 3987 3988 // This hack is used because there is no guarantee that more than 3989 // 16 KiB will be sent as a single TCP packet. 3990 push.call(ws._socket, Buffer.concat(list)); 3991 3992 wss.clients 3993 .values() 3994 .next() 3995 .value.send(payload2, { compress: false }); 3996 }); 3997 3998 ws.on('message', (message, isBinary) => { 3999 assert.ok(isBinary); 4000 messageLengths.push(message.length); 4001 }); 4002 4003 ws.on('close', (code) => { 4004 assert.strictEqual(code, 1006); 4005 assert.strictEqual(messageLengths.length, 402); 4006 assert.strictEqual(messageLengths[0], 15360); 4007 assert.strictEqual(messageLengths[messageLengths.length - 1], 1); 4008 wss.close(done); 4009 }); 4010 } 4011 ); 4012 }); 4013 4014 it('handles a close frame received while compressing data', (done) => { 4015 const wss = new WebSocket.Server( 4016 { 4017 perMessageDeflate: true, 4018 port: 0 4019 }, 4020 () => { 4021 const ws = new WebSocket(`ws://localhost:${wss.address().port}`, { 4022 perMessageDeflate: { threshold: 0 } 4023 }); 4024 4025 ws.on('open', () => { 4026 ws._receiver.on('conclude', () => { 4027 assert.ok(ws._sender._deflating); 4028 }); 4029 4030 ws.send('foo'); 4031 ws.send('bar'); 4032 ws.send('baz'); 4033 ws.send('qux'); 4034 }); 4035 } 4036 ); 4037 4038 wss.on('connection', (ws) => { 4039 const messages = []; 4040 4041 ws.on('message', (message, isBinary) => { 4042 assert.ok(!isBinary); 4043 messages.push(message.toString()); 4044 }); 4045 4046 ws.on('close', (code, reason) => { 4047 assert.deepStrictEqual(messages, ['foo', 'bar', 'baz', 'qux']); 4048 assert.strictEqual(code, 1000); 4049 assert.deepStrictEqual(reason, EMPTY_BUFFER); 4050 wss.close(done); 4051 }); 4052 4053 ws.close(1000); 4054 }); 4055 }); 4056 4057 describe('#close', () => { 4058 it('can be used while data is being decompressed', (done) => { 4059 const wss = new WebSocket.Server( 4060 { 4061 perMessageDeflate: true, 4062 port: 0 4063 }, 4064 () => { 4065 const messages = []; 4066 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 4067 4068 ws.on('open', () => { 4069 ws._socket.on('end', () => { 4070 assert.strictEqual(ws._receiver._state, 5); 4071 }); 4072 }); 4073 4074 ws.on('message', (message, isBinary) => { 4075 assert.ok(!isBinary); 4076 4077 if (messages.push(message.toString()) > 1) return; 4078 4079 ws.close(1000); 4080 }); 4081 4082 ws.on('close', (code, reason) => { 4083 assert.deepStrictEqual(messages, ['', '', '', '']); 4084 assert.strictEqual(code, 1000); 4085 assert.deepStrictEqual(reason, EMPTY_BUFFER); 4086 wss.close(done); 4087 }); 4088 } 4089 ); 4090 4091 wss.on('connection', (ws) => { 4092 const buf = Buffer.from('c10100c10100c10100c10100', 'hex'); 4093 ws._socket.write(buf); 4094 }); 4095 }); 4096 }); 4097 4098 describe('#send', () => { 4099 it('can send text data', (done) => { 4100 const wss = new WebSocket.Server( 4101 { 4102 perMessageDeflate: { threshold: 0 }, 4103 port: 0 4104 }, 4105 () => { 4106 const ws = new WebSocket(`ws://localhost:${wss.address().port}`, { 4107 perMessageDeflate: { threshold: 0 } 4108 }); 4109 4110 ws.on('open', () => { 4111 ws.send('hi', { compress: true }); 4112 ws.close(); 4113 }); 4114 4115 ws.on('message', (message, isBinary) => { 4116 assert.deepStrictEqual(message, Buffer.from('hi')); 4117 assert.ok(!isBinary); 4118 wss.close(done); 4119 }); 4120 } 4121 ); 4122 4123 wss.on('connection', (ws) => { 4124 ws.on('message', (message, isBinary) => { 4125 ws.send(message, { binary: isBinary, compress: true }); 4126 }); 4127 }); 4128 }); 4129 4130 it('can send a `TypedArray`', (done) => { 4131 const array = new Float32Array(5); 4132 4133 for (let i = 0; i < array.length; i++) { 4134 array[i] = i / 2; 4135 } 4136 4137 const wss = new WebSocket.Server( 4138 { 4139 perMessageDeflate: { threshold: 0 }, 4140 port: 0 4141 }, 4142 () => { 4143 const ws = new WebSocket(`ws://localhost:${wss.address().port}`, { 4144 perMessageDeflate: { threshold: 0 } 4145 }); 4146 4147 ws.on('open', () => { 4148 ws.send(array, { compress: true }); 4149 ws.close(); 4150 }); 4151 4152 ws.on('message', (message, isBinary) => { 4153 assert.deepStrictEqual(message, Buffer.from(array.buffer)); 4154 assert.ok(isBinary); 4155 wss.close(done); 4156 }); 4157 } 4158 ); 4159 4160 wss.on('connection', (ws) => { 4161 ws.on('message', (message, isBinary) => { 4162 assert.ok(isBinary); 4163 ws.send(message, { compress: true }); 4164 }); 4165 }); 4166 }); 4167 4168 it('can send an `ArrayBuffer`', (done) => { 4169 const array = new Float32Array(5); 4170 4171 for (let i = 0; i < array.length; i++) { 4172 array[i] = i / 2; 4173 } 4174 4175 const wss = new WebSocket.Server( 4176 { 4177 perMessageDeflate: { threshold: 0 }, 4178 port: 0 4179 }, 4180 () => { 4181 const ws = new WebSocket(`ws://localhost:${wss.address().port}`, { 4182 perMessageDeflate: { threshold: 0 } 4183 }); 4184 4185 ws.on('open', () => { 4186 ws.send(array.buffer, { compress: true }); 4187 ws.close(); 4188 }); 4189 4190 ws.on('message', (message, isBinary) => { 4191 assert.deepStrictEqual(message, Buffer.from(array.buffer)); 4192 assert.ok(isBinary); 4193 wss.close(done); 4194 }); 4195 } 4196 ); 4197 4198 wss.on('connection', (ws) => { 4199 ws.on('message', (message, isBinary) => { 4200 assert.ok(isBinary); 4201 ws.send(message, { compress: true }); 4202 }); 4203 }); 4204 }); 4205 4206 it('ignores the `compress` option if the extension is disabled', (done) => { 4207 const wss = new WebSocket.Server({ port: 0 }, () => { 4208 const ws = new WebSocket(`ws://localhost:${wss.address().port}`, { 4209 perMessageDeflate: false 4210 }); 4211 4212 ws.on('open', () => { 4213 ws.send('hi', { compress: true }); 4214 ws.close(); 4215 }); 4216 4217 ws.on('message', (message, isBinary) => { 4218 assert.deepStrictEqual(message, Buffer.from('hi')); 4219 assert.ok(!isBinary); 4220 wss.close(done); 4221 }); 4222 }); 4223 4224 wss.on('connection', (ws) => { 4225 ws.on('message', (message, isBinary) => { 4226 ws.send(message, { binary: isBinary, compress: true }); 4227 }); 4228 }); 4229 }); 4230 4231 it('calls the callback if the socket is closed prematurely', (done) => { 4232 const called = []; 4233 const wss = new WebSocket.Server( 4234 { perMessageDeflate: true, port: 0 }, 4235 () => { 4236 const ws = new WebSocket(`ws://localhost:${wss.address().port}`, { 4237 perMessageDeflate: { threshold: 0 } 4238 }); 4239 4240 ws.on('open', () => { 4241 ws.send('foo'); 4242 ws.send('bar', (err) => { 4243 called.push(1); 4244 4245 assert.strictEqual(ws.readyState, WebSocket.CLOSING); 4246 assert.ok(err instanceof Error); 4247 assert.strictEqual( 4248 err.message, 4249 'The socket was closed while data was being compressed' 4250 ); 4251 }); 4252 ws.send('baz'); 4253 ws.send('qux', (err) => { 4254 called.push(2); 4255 4256 assert.strictEqual(ws.readyState, WebSocket.CLOSING); 4257 assert.ok(err instanceof Error); 4258 assert.strictEqual( 4259 err.message, 4260 'The socket was closed while data was being compressed' 4261 ); 4262 }); 4263 }); 4264 } 4265 ); 4266 4267 wss.on('connection', (ws) => { 4268 ws.on('close', () => { 4269 assert.deepStrictEqual(called, [1, 2]); 4270 wss.close(done); 4271 }); 4272 4273 ws._socket.end(); 4274 }); 4275 }); 4276 }); 4277 4278 describe('#terminate', () => { 4279 it('can be used while data is being compressed', (done) => { 4280 const wss = new WebSocket.Server( 4281 { 4282 perMessageDeflate: { threshold: 0 }, 4283 port: 0 4284 }, 4285 () => { 4286 const ws = new WebSocket(`ws://localhost:${wss.address().port}`, { 4287 perMessageDeflate: { threshold: 0 } 4288 }); 4289 4290 ws.on('open', () => { 4291 ws.send('hi', (err) => { 4292 assert.strictEqual(ws.readyState, WebSocket.CLOSING); 4293 assert.ok(err instanceof Error); 4294 assert.strictEqual( 4295 err.message, 4296 'The socket was closed while data was being compressed' 4297 ); 4298 4299 ws.on('close', () => { 4300 wss.close(done); 4301 }); 4302 }); 4303 ws.terminate(); 4304 }); 4305 } 4306 ); 4307 }); 4308 4309 it('can be used while data is being decompressed', (done) => { 4310 const wss = new WebSocket.Server( 4311 { 4312 perMessageDeflate: true, 4313 port: 0 4314 }, 4315 () => { 4316 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 4317 const messages = []; 4318 4319 ws.on('message', (message, isBinary) => { 4320 assert.ok(!isBinary); 4321 4322 if (messages.push(message.toString()) > 1) return; 4323 4324 process.nextTick(() => { 4325 assert.strictEqual(ws._receiver._state, 5); 4326 ws.terminate(); 4327 }); 4328 }); 4329 4330 ws.on('close', (code, reason) => { 4331 assert.deepStrictEqual(messages, ['', '', '', '']); 4332 assert.strictEqual(code, 1006); 4333 assert.strictEqual(reason, EMPTY_BUFFER); 4334 wss.close(done); 4335 }); 4336 } 4337 ); 4338 4339 wss.on('connection', (ws) => { 4340 const buf = Buffer.from('c10100c10100c10100c10100', 'hex'); 4341 ws._socket.write(buf); 4342 }); 4343 }); 4344 }); 4345 }); 4346 4347 describe('Connection close', () => { 4348 it('closes cleanly after simultaneous errors (1/2)', (done) => { 4349 let clientCloseEventEmitted = false; 4350 let serverClientCloseEventEmitted = false; 4351 4352 const wss = new WebSocket.Server({ port: 0 }, () => { 4353 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 4354 4355 ws.on('error', (err) => { 4356 assert.ok(err instanceof RangeError); 4357 assert.strictEqual(err.code, 'WS_ERR_INVALID_OPCODE'); 4358 assert.strictEqual( 4359 err.message, 4360 'Invalid WebSocket frame: invalid opcode 5' 4361 ); 4362 4363 ws.on('close', (code, reason) => { 4364 assert.strictEqual(code, 1006); 4365 assert.strictEqual(reason, EMPTY_BUFFER); 4366 4367 clientCloseEventEmitted = true; 4368 if (serverClientCloseEventEmitted) wss.close(done); 4369 }); 4370 }); 4371 4372 ws.on('open', () => { 4373 // Write an invalid frame in both directions to trigger simultaneous 4374 // failure. 4375 const chunk = Buffer.from([0x85, 0x00]); 4376 4377 wss.clients.values().next().value._socket.write(chunk); 4378 ws._socket.write(chunk); 4379 }); 4380 }); 4381 4382 wss.on('connection', (ws) => { 4383 ws.on('error', (err) => { 4384 assert.ok(err instanceof RangeError); 4385 assert.strictEqual(err.code, 'WS_ERR_INVALID_OPCODE'); 4386 assert.strictEqual( 4387 err.message, 4388 'Invalid WebSocket frame: invalid opcode 5' 4389 ); 4390 4391 ws.on('close', (code, reason) => { 4392 assert.strictEqual(code, 1006); 4393 assert.strictEqual(reason, EMPTY_BUFFER); 4394 4395 serverClientCloseEventEmitted = true; 4396 if (clientCloseEventEmitted) wss.close(done); 4397 }); 4398 }); 4399 }); 4400 }); 4401 4402 it('closes cleanly after simultaneous errors (2/2)', (done) => { 4403 let clientCloseEventEmitted = false; 4404 let serverClientCloseEventEmitted = false; 4405 4406 const wss = new WebSocket.Server({ port: 0 }, () => { 4407 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 4408 4409 ws.on('error', (err) => { 4410 assert.ok(err instanceof RangeError); 4411 assert.strictEqual(err.code, 'WS_ERR_INVALID_OPCODE'); 4412 assert.strictEqual( 4413 err.message, 4414 'Invalid WebSocket frame: invalid opcode 5' 4415 ); 4416 4417 ws.on('close', (code, reason) => { 4418 assert.strictEqual(code, 1006); 4419 assert.strictEqual(reason, EMPTY_BUFFER); 4420 4421 clientCloseEventEmitted = true; 4422 if (serverClientCloseEventEmitted) wss.close(done); 4423 }); 4424 }); 4425 4426 ws.on('open', () => { 4427 // Write an invalid frame in both directions and change the 4428 // `readyState` to `WebSocket.CLOSING`. 4429 const chunk = Buffer.from([0x85, 0x00]); 4430 const serverWs = wss.clients.values().next().value; 4431 4432 serverWs._socket.write(chunk); 4433 serverWs.close(); 4434 4435 ws._socket.write(chunk); 4436 ws.close(); 4437 }); 4438 }); 4439 4440 wss.on('connection', (ws) => { 4441 ws.on('error', (err) => { 4442 assert.ok(err instanceof RangeError); 4443 assert.strictEqual(err.code, 'WS_ERR_INVALID_OPCODE'); 4444 assert.strictEqual( 4445 err.message, 4446 'Invalid WebSocket frame: invalid opcode 5' 4447 ); 4448 4449 ws.on('close', (code, reason) => { 4450 assert.strictEqual(code, 1006); 4451 assert.strictEqual(reason, EMPTY_BUFFER); 4452 4453 serverClientCloseEventEmitted = true; 4454 if (clientCloseEventEmitted) wss.close(done); 4455 }); 4456 }); 4457 }); 4458 }); 4459 4460 it('resumes the socket when an error occurs', (done) => { 4461 const maxPayload = 16 * 1024; 4462 const wss = new WebSocket.Server({ maxPayload, port: 0 }, () => { 4463 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 4464 }); 4465 4466 wss.on('connection', (ws) => { 4467 const list = [ 4468 ...Sender.frame(Buffer.alloc(maxPayload + 1), { 4469 fin: true, 4470 opcode: 0x02, 4471 mask: true, 4472 readOnly: false 4473 }) 4474 ]; 4475 4476 ws.on('error', (err) => { 4477 assert.ok(err instanceof RangeError); 4478 assert.strictEqual(err.code, 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'); 4479 assert.strictEqual(err.message, 'Max payload size exceeded'); 4480 4481 ws.on('close', (code, reason) => { 4482 assert.strictEqual(code, 1006); 4483 assert.strictEqual(reason, EMPTY_BUFFER); 4484 wss.close(done); 4485 }); 4486 }); 4487 4488 ws._socket.push(Buffer.concat(list)); 4489 }); 4490 }); 4491 4492 it('resumes the socket when the close frame is received', (done) => { 4493 const wss = new WebSocket.Server({ port: 0 }, () => { 4494 const ws = new WebSocket(`ws://localhost:${wss.address().port}`); 4495 }); 4496 4497 wss.on('connection', (ws) => { 4498 const opts = { fin: true, mask: true, readOnly: false }; 4499 const list = [ 4500 ...Sender.frame(Buffer.alloc(16 * 1024), { opcode: 0x02, ...opts }), 4501 ...Sender.frame(EMPTY_BUFFER, { opcode: 0x08, ...opts }) 4502 ]; 4503 4504 ws.on('close', (code, reason) => { 4505 assert.strictEqual(code, 1005); 4506 assert.strictEqual(reason, EMPTY_BUFFER); 4507 wss.close(done); 4508 }); 4509 4510 ws._socket.push(Buffer.concat(list)); 4511 }); 4512 }); 4513 }); 4514 });