tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 });