tor-browser

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

websocket-server.test.js (36110B)


      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 fs = require('fs');
     12 const os = require('os');
     13 
     14 const Sender = require('../lib/sender');
     15 const WebSocket = require('..');
     16 const { NOOP } = require('../lib/constants');
     17 
     18 describe('WebSocketServer', () => {
     19  describe('#ctor', () => {
     20    it('throws an error if no option object is passed', () => {
     21      assert.throws(
     22        () => new WebSocket.Server(),
     23        new RegExp(
     24          '^TypeError: One and only one of the "port", "server", or ' +
     25            '"noServer" options must be specified$'
     26        )
     27      );
     28    });
     29 
     30    describe('options', () => {
     31      it('throws an error if required options are not specified', () => {
     32        assert.throws(
     33          () => new WebSocket.Server({}),
     34          new RegExp(
     35            '^TypeError: One and only one of the "port", "server", or ' +
     36              '"noServer" options must be specified$'
     37          )
     38        );
     39      });
     40 
     41      it('throws an error if mutually exclusive options are specified', () => {
     42        const server = http.createServer();
     43        const variants = [
     44          { port: 0, noServer: true, server },
     45          { port: 0, noServer: true },
     46          { port: 0, server },
     47          { noServer: true, server }
     48        ];
     49 
     50        for (const options of variants) {
     51          assert.throws(
     52            () => new WebSocket.Server(options),
     53            new RegExp(
     54              '^TypeError: One and only one of the "port", "server", or ' +
     55                '"noServer" options must be specified$'
     56            )
     57          );
     58        }
     59      });
     60 
     61      it('exposes options passed to constructor', (done) => {
     62        const wss = new WebSocket.Server({ port: 0 }, () => {
     63          assert.strictEqual(wss.options.port, 0);
     64          wss.close(done);
     65        });
     66      });
     67 
     68      it('accepts the `maxPayload` option', (done) => {
     69        const maxPayload = 20480;
     70        const wss = new WebSocket.Server(
     71          {
     72            perMessageDeflate: true,
     73            maxPayload,
     74            port: 0
     75          },
     76          () => {
     77            const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
     78 
     79            ws.on('open', ws.close);
     80          }
     81        );
     82 
     83        wss.on('connection', (ws) => {
     84          assert.strictEqual(ws._receiver._maxPayload, maxPayload);
     85          assert.strictEqual(
     86            ws._receiver._extensions['permessage-deflate']._maxPayload,
     87            maxPayload
     88          );
     89          wss.close(done);
     90        });
     91      });
     92 
     93      it('honors the `WebSocket` option', (done) => {
     94        class CustomWebSocket extends WebSocket.WebSocket {
     95          get foo() {
     96            return 'foo';
     97          }
     98        }
     99 
    100        const wss = new WebSocket.Server(
    101          {
    102            port: 0,
    103            WebSocket: CustomWebSocket
    104          },
    105          () => {
    106            const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
    107 
    108            ws.on('open', ws.close);
    109          }
    110        );
    111 
    112        wss.on('connection', (ws) => {
    113          assert.ok(ws instanceof CustomWebSocket);
    114          assert.strictEqual(ws.foo, 'foo');
    115          wss.close(done);
    116        });
    117      });
    118    });
    119 
    120    it('emits an error if http server bind fails', (done) => {
    121      const wss1 = new WebSocket.Server({ port: 0 }, () => {
    122        const wss2 = new WebSocket.Server({
    123          port: wss1.address().port
    124        });
    125 
    126        wss2.on('error', () => wss1.close(done));
    127      });
    128    });
    129 
    130    it('starts a server on a given port', (done) => {
    131      const port = 1337;
    132      const wss = new WebSocket.Server({ port }, () => {
    133        const ws = new WebSocket(`ws://localhost:${port}`);
    134 
    135        ws.on('open', ws.close);
    136      });
    137 
    138      wss.on('connection', () => wss.close(done));
    139    });
    140 
    141    it('binds the server on any IPv6 address when available', (done) => {
    142      const wss = new WebSocket.Server({ port: 0 }, () => {
    143        assert.strictEqual(wss._server.address().address, '::');
    144        wss.close(done);
    145      });
    146    });
    147 
    148    it('uses a precreated http server', (done) => {
    149      const server = http.createServer();
    150 
    151      server.listen(0, () => {
    152        const wss = new WebSocket.Server({ server });
    153 
    154        wss.on('connection', () => {
    155          server.close(done);
    156        });
    157 
    158        const ws = new WebSocket(`ws://localhost:${server.address().port}`);
    159 
    160        ws.on('open', ws.close);
    161      });
    162    });
    163 
    164    it('426s for non-Upgrade requests', (done) => {
    165      const wss = new WebSocket.Server({ port: 0 }, () => {
    166        http.get(`http://localhost:${wss.address().port}`, (res) => {
    167          let body = '';
    168 
    169          assert.strictEqual(res.statusCode, 426);
    170          res.on('data', (chunk) => {
    171            body += chunk;
    172          });
    173          res.on('end', () => {
    174            assert.strictEqual(body, http.STATUS_CODES[426]);
    175            wss.close(done);
    176          });
    177        });
    178      });
    179    });
    180 
    181    it('uses a precreated http server listening on unix socket', function (done) {
    182      //
    183      // Skip this test on Windows. The URL parser:
    184      //
    185      // - Throws an error if the named pipe uses backward slashes.
    186      // - Incorrectly parses the path if the named pipe uses forward slashes.
    187      //
    188      if (process.platform === 'win32') return this.skip();
    189 
    190      const server = http.createServer();
    191      const sockPath = path.join(
    192        os.tmpdir(),
    193        `ws.${crypto.randomBytes(16).toString('hex')}.sock`
    194      );
    195 
    196      server.listen(sockPath, () => {
    197        const wss = new WebSocket.Server({ server });
    198 
    199        wss.on('connection', (ws, req) => {
    200          if (wss.clients.size === 1) {
    201            assert.strictEqual(req.url, '/foo?bar=bar');
    202          } else {
    203            assert.strictEqual(req.url, '/');
    204 
    205            for (const client of wss.clients) {
    206              client.close();
    207            }
    208 
    209            server.close(done);
    210          }
    211        });
    212 
    213        const ws = new WebSocket(`ws+unix://${sockPath}:/foo?bar=bar`);
    214        ws.on('open', () => new WebSocket(`ws+unix://${sockPath}`));
    215      });
    216    });
    217  });
    218 
    219  describe('#address', () => {
    220    it('returns the address of the server', (done) => {
    221      const wss = new WebSocket.Server({ port: 0 }, () => {
    222        const addr = wss.address();
    223 
    224        assert.deepStrictEqual(addr, wss._server.address());
    225        wss.close(done);
    226      });
    227    });
    228 
    229    it('throws an error when operating in "noServer" mode', () => {
    230      const wss = new WebSocket.Server({ noServer: true });
    231 
    232      assert.throws(() => {
    233        wss.address();
    234      }, /^Error: The server is operating in "noServer" mode$/);
    235    });
    236 
    237    it('returns `null` if called after close', (done) => {
    238      const wss = new WebSocket.Server({ port: 0 }, () => {
    239        wss.close(() => {
    240          assert.strictEqual(wss.address(), null);
    241          done();
    242        });
    243      });
    244    });
    245  });
    246 
    247  describe('#close', () => {
    248    it('does not throw if called multiple times', (done) => {
    249      const wss = new WebSocket.Server({ port: 0 }, () => {
    250        wss.on('close', done);
    251 
    252        wss.close();
    253        wss.close();
    254        wss.close();
    255      });
    256    });
    257 
    258    it("doesn't close a precreated server", (done) => {
    259      const server = http.createServer();
    260      const realClose = server.close;
    261 
    262      server.close = () => {
    263        done(new Error('Must not close pre-created server'));
    264      };
    265 
    266      const wss = new WebSocket.Server({ server });
    267 
    268      wss.on('connection', () => {
    269        wss.close();
    270        server.close = realClose;
    271        server.close(done);
    272      });
    273 
    274      server.listen(0, () => {
    275        const ws = new WebSocket(`ws://localhost:${server.address().port}`);
    276 
    277        ws.on('open', ws.close);
    278      });
    279    });
    280 
    281    it('invokes the callback in noServer mode', (done) => {
    282      const wss = new WebSocket.Server({ noServer: true });
    283 
    284      wss.close(done);
    285    });
    286 
    287    it('cleans event handlers on precreated server', (done) => {
    288      const server = http.createServer();
    289      const wss = new WebSocket.Server({ server });
    290 
    291      server.listen(0, () => {
    292        wss.close(() => {
    293          assert.strictEqual(server.listenerCount('listening'), 0);
    294          assert.strictEqual(server.listenerCount('upgrade'), 0);
    295          assert.strictEqual(server.listenerCount('error'), 0);
    296 
    297          server.close(done);
    298        });
    299      });
    300    });
    301 
    302    it("emits the 'close' event after the server closes", (done) => {
    303      let serverCloseEventEmitted = false;
    304 
    305      const wss = new WebSocket.Server({ port: 0 }, () => {
    306        net.createConnection({ port: wss.address().port });
    307      });
    308 
    309      wss._server.on('connection', (socket) => {
    310        wss.close();
    311 
    312        //
    313        // The server is closing. Ensure this does not emit a `'close'`
    314        // event before the server is actually closed.
    315        //
    316        wss.close();
    317 
    318        process.nextTick(() => {
    319          socket.end();
    320        });
    321      });
    322 
    323      wss._server.on('close', () => {
    324        serverCloseEventEmitted = true;
    325      });
    326 
    327      wss.on('close', () => {
    328        assert.ok(serverCloseEventEmitted);
    329        done();
    330      });
    331    });
    332 
    333    it("emits the 'close' event if client tracking is disabled", (done) => {
    334      const wss = new WebSocket.Server({
    335        noServer: true,
    336        clientTracking: false
    337      });
    338 
    339      wss.on('close', done);
    340      wss.close();
    341    });
    342 
    343    it('calls the callback if the server is already closed', (done) => {
    344      const wss = new WebSocket.Server({ port: 0 }, () => {
    345        wss.close(() => {
    346          assert.strictEqual(wss._state, 2);
    347 
    348          wss.close((err) => {
    349            assert.ok(err instanceof Error);
    350            assert.strictEqual(err.message, 'The server is not running');
    351            done();
    352          });
    353        });
    354      });
    355    });
    356 
    357    it("emits the 'close' event if the server is already closed", (done) => {
    358      const wss = new WebSocket.Server({ port: 0 }, () => {
    359        wss.close(() => {
    360          assert.strictEqual(wss._state, 2);
    361 
    362          wss.on('close', done);
    363          wss.close();
    364        });
    365      });
    366    });
    367  });
    368 
    369  describe('#clients', () => {
    370    it('returns a list of connected clients', (done) => {
    371      const wss = new WebSocket.Server({ port: 0 }, () => {
    372        assert.strictEqual(wss.clients.size, 0);
    373 
    374        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
    375 
    376        ws.on('open', ws.close);
    377      });
    378 
    379      wss.on('connection', () => {
    380        assert.strictEqual(wss.clients.size, 1);
    381        wss.close(done);
    382      });
    383    });
    384 
    385    it('can be disabled', (done) => {
    386      const wss = new WebSocket.Server(
    387        { port: 0, clientTracking: false },
    388        () => {
    389          assert.strictEqual(wss.clients, undefined);
    390          const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
    391 
    392          ws.on('open', () => ws.close());
    393        }
    394      );
    395 
    396      wss.on('connection', (ws) => {
    397        assert.strictEqual(wss.clients, undefined);
    398        ws.on('close', () => wss.close(done));
    399      });
    400    });
    401 
    402    it('is updated when client terminates the connection', (done) => {
    403      const wss = new WebSocket.Server({ port: 0 }, () => {
    404        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
    405 
    406        ws.on('open', () => ws.terminate());
    407      });
    408 
    409      wss.on('connection', (ws) => {
    410        ws.on('close', () => {
    411          assert.strictEqual(wss.clients.size, 0);
    412          wss.close(done);
    413        });
    414      });
    415    });
    416 
    417    it('is updated when client closes the connection', (done) => {
    418      const wss = new WebSocket.Server({ port: 0 }, () => {
    419        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
    420 
    421        ws.on('open', () => ws.close());
    422      });
    423 
    424      wss.on('connection', (ws) => {
    425        ws.on('close', () => {
    426          assert.strictEqual(wss.clients.size, 0);
    427          wss.close(done);
    428        });
    429      });
    430    });
    431  });
    432 
    433  describe('#shouldHandle', () => {
    434    it('returns true when the path matches', () => {
    435      const wss = new WebSocket.Server({ noServer: true, path: '/foo' });
    436 
    437      assert.strictEqual(wss.shouldHandle({ url: '/foo' }), true);
    438      assert.strictEqual(wss.shouldHandle({ url: '/foo?bar=baz' }), true);
    439    });
    440 
    441    it("returns false when the path doesn't match", () => {
    442      const wss = new WebSocket.Server({ noServer: true, path: '/foo' });
    443 
    444      assert.strictEqual(wss.shouldHandle({ url: '/bar' }), false);
    445    });
    446  });
    447 
    448  describe('#handleUpgrade', () => {
    449    it('can be used for a pre-existing server', (done) => {
    450      const server = http.createServer();
    451 
    452      server.listen(0, () => {
    453        const wss = new WebSocket.Server({ noServer: true });
    454 
    455        server.on('upgrade', (req, socket, head) => {
    456          wss.handleUpgrade(req, socket, head, (ws) => {
    457            ws.send('hello');
    458            ws.close();
    459          });
    460        });
    461 
    462        const ws = new WebSocket(`ws://localhost:${server.address().port}`);
    463 
    464        ws.on('message', (message, isBinary) => {
    465          assert.deepStrictEqual(message, Buffer.from('hello'));
    466          assert.ok(!isBinary);
    467          server.close(done);
    468        });
    469      });
    470    });
    471 
    472    it("closes the connection when path doesn't match", (done) => {
    473      const wss = new WebSocket.Server({ port: 0, path: '/ws' }, () => {
    474        const req = http.get({
    475          port: wss.address().port,
    476          headers: {
    477            Connection: 'Upgrade',
    478            Upgrade: 'websocket',
    479            'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
    480            'Sec-WebSocket-Version': 13
    481          }
    482        });
    483 
    484        req.on('response', (res) => {
    485          assert.strictEqual(res.statusCode, 400);
    486          wss.close(done);
    487        });
    488      });
    489    });
    490 
    491    it('closes the connection when protocol version is Hixie-76', (done) => {
    492      const wss = new WebSocket.Server({ port: 0 }, () => {
    493        const req = http.get({
    494          port: wss.address().port,
    495          headers: {
    496            Connection: 'Upgrade',
    497            Upgrade: 'WebSocket',
    498            'Sec-WebSocket-Key1': '4 @1  46546xW%0l 1 5',
    499            'Sec-WebSocket-Key2': '12998 5 Y3 1  .P00',
    500            'Sec-WebSocket-Protocol': 'sample'
    501          }
    502        });
    503 
    504        req.on('response', (res) => {
    505          assert.strictEqual(res.statusCode, 400);
    506 
    507          const chunks = [];
    508 
    509          res.on('data', (chunk) => {
    510            chunks.push(chunk);
    511          });
    512 
    513          res.on('end', () => {
    514            assert.strictEqual(
    515              Buffer.concat(chunks).toString(),
    516              'Missing or invalid Sec-WebSocket-Key header'
    517            );
    518            wss.close(done);
    519          });
    520        });
    521      });
    522    });
    523  });
    524 
    525  describe('#completeUpgrade', () => {
    526    it('throws an error if called twice with the same socket', (done) => {
    527      const server = http.createServer();
    528 
    529      server.listen(0, () => {
    530        const wss = new WebSocket.Server({ noServer: true });
    531 
    532        server.on('upgrade', (req, socket, head) => {
    533          wss.handleUpgrade(req, socket, head, (ws) => {
    534            ws.close();
    535          });
    536          assert.throws(
    537            () => wss.handleUpgrade(req, socket, head, NOOP),
    538            (err) => {
    539              assert.ok(err instanceof Error);
    540              assert.strictEqual(
    541                err.message,
    542                'server.handleUpgrade() was called more than once with the ' +
    543                  'same socket, possibly due to a misconfiguration'
    544              );
    545              return true;
    546            }
    547          );
    548        });
    549 
    550        const ws = new WebSocket(`ws://localhost:${server.address().port}`);
    551 
    552        ws.on('open', () => {
    553          ws.on('close', () => {
    554            server.close(done);
    555          });
    556        });
    557      });
    558    });
    559  });
    560 
    561  describe('Connection establishing', () => {
    562    it('fails if the HTTP method is not GET', (done) => {
    563      const wss = new WebSocket.Server({ port: 0 }, () => {
    564        const req = http.request({
    565          method: 'POST',
    566          port: wss.address().port,
    567          headers: {
    568            Connection: 'Upgrade',
    569            Upgrade: 'websocket'
    570          }
    571        });
    572 
    573        req.on('response', (res) => {
    574          assert.strictEqual(res.statusCode, 405);
    575 
    576          const chunks = [];
    577 
    578          res.on('data', (chunk) => {
    579            chunks.push(chunk);
    580          });
    581 
    582          res.on('end', () => {
    583            assert.strictEqual(
    584              Buffer.concat(chunks).toString(),
    585              'Invalid HTTP method'
    586            );
    587            wss.close(done);
    588          });
    589        });
    590 
    591        req.end();
    592      });
    593 
    594      wss.on('connection', () => {
    595        done(new Error("Unexpected 'connection' event"));
    596      });
    597    });
    598 
    599    it('fails if the Upgrade header field value is not "websocket"', (done) => {
    600      const wss = new WebSocket.Server({ port: 0 }, () => {
    601        const req = http.get({
    602          port: wss.address().port,
    603          headers: {
    604            Connection: 'Upgrade',
    605            Upgrade: 'foo'
    606          }
    607        });
    608 
    609        req.on('response', (res) => {
    610          assert.strictEqual(res.statusCode, 400);
    611 
    612          const chunks = [];
    613 
    614          res.on('data', (chunk) => {
    615            chunks.push(chunk);
    616          });
    617 
    618          res.on('end', () => {
    619            assert.strictEqual(
    620              Buffer.concat(chunks).toString(),
    621              'Invalid Upgrade header'
    622            );
    623            wss.close(done);
    624          });
    625        });
    626      });
    627 
    628      wss.on('connection', () => {
    629        done(new Error("Unexpected 'connection' event"));
    630      });
    631    });
    632 
    633    it('fails if the Sec-WebSocket-Key header is invalid (1/2)', (done) => {
    634      const wss = new WebSocket.Server({ port: 0 }, () => {
    635        const req = http.get({
    636          port: wss.address().port,
    637          headers: {
    638            Connection: 'Upgrade',
    639            Upgrade: 'websocket'
    640          }
    641        });
    642 
    643        req.on('response', (res) => {
    644          assert.strictEqual(res.statusCode, 400);
    645 
    646          const chunks = [];
    647 
    648          res.on('data', (chunk) => {
    649            chunks.push(chunk);
    650          });
    651 
    652          res.on('end', () => {
    653            assert.strictEqual(
    654              Buffer.concat(chunks).toString(),
    655              'Missing or invalid Sec-WebSocket-Key header'
    656            );
    657            wss.close(done);
    658          });
    659        });
    660      });
    661 
    662      wss.on('connection', () => {
    663        done(new Error("Unexpected 'connection' event"));
    664      });
    665    });
    666 
    667    it('fails if the Sec-WebSocket-Key header is invalid (2/2)', (done) => {
    668      const wss = new WebSocket.Server({ port: 0 }, () => {
    669        const req = http.get({
    670          port: wss.address().port,
    671          headers: {
    672            Connection: 'Upgrade',
    673            Upgrade: 'websocket',
    674            'Sec-WebSocket-Key': 'P5l8BJcZwRc='
    675          }
    676        });
    677 
    678        req.on('response', (res) => {
    679          assert.strictEqual(res.statusCode, 400);
    680 
    681          const chunks = [];
    682 
    683          res.on('data', (chunk) => {
    684            chunks.push(chunk);
    685          });
    686 
    687          res.on('end', () => {
    688            assert.strictEqual(
    689              Buffer.concat(chunks).toString(),
    690              'Missing or invalid Sec-WebSocket-Key header'
    691            );
    692            wss.close(done);
    693          });
    694        });
    695      });
    696 
    697      wss.on('connection', () => {
    698        done(new Error("Unexpected 'connection' event"));
    699      });
    700    });
    701 
    702    it('fails if the Sec-WebSocket-Version header is invalid (1/2)', (done) => {
    703      const wss = new WebSocket.Server({ port: 0 }, () => {
    704        const req = http.get({
    705          port: wss.address().port,
    706          headers: {
    707            Connection: 'Upgrade',
    708            Upgrade: 'websocket',
    709            'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ=='
    710          }
    711        });
    712 
    713        req.on('response', (res) => {
    714          assert.strictEqual(res.statusCode, 400);
    715 
    716          const chunks = [];
    717 
    718          res.on('data', (chunk) => {
    719            chunks.push(chunk);
    720          });
    721 
    722          res.on('end', () => {
    723            assert.strictEqual(
    724              Buffer.concat(chunks).toString(),
    725              'Missing or invalid Sec-WebSocket-Version header'
    726            );
    727            wss.close(done);
    728          });
    729        });
    730      });
    731 
    732      wss.on('connection', () => {
    733        done(new Error("Unexpected 'connection' event"));
    734      });
    735    });
    736 
    737    it('fails if the Sec-WebSocket-Version header is invalid (2/2)', (done) => {
    738      const wss = new WebSocket.Server({ port: 0 }, () => {
    739        const req = http.get({
    740          port: wss.address().port,
    741          headers: {
    742            Connection: 'Upgrade',
    743            Upgrade: 'websocket',
    744            'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
    745            'Sec-WebSocket-Version': 12
    746          }
    747        });
    748 
    749        req.on('response', (res) => {
    750          assert.strictEqual(res.statusCode, 400);
    751 
    752          const chunks = [];
    753 
    754          res.on('data', (chunk) => {
    755            chunks.push(chunk);
    756          });
    757 
    758          res.on('end', () => {
    759            assert.strictEqual(
    760              Buffer.concat(chunks).toString(),
    761              'Missing or invalid Sec-WebSocket-Version header'
    762            );
    763            wss.close(done);
    764          });
    765        });
    766      });
    767 
    768      wss.on('connection', () => {
    769        done(new Error("Unexpected 'connection' event"));
    770      });
    771    });
    772 
    773    it('fails is the Sec-WebSocket-Protocol header is invalid', (done) => {
    774      const wss = new WebSocket.Server({ port: 0 }, () => {
    775        const req = http.get({
    776          port: wss.address().port,
    777          headers: {
    778            Connection: 'Upgrade',
    779            Upgrade: 'websocket',
    780            'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
    781            'Sec-WebSocket-Version': 13,
    782            'Sec-WebSocket-Protocol': 'foo;bar'
    783          }
    784        });
    785 
    786        req.on('response', (res) => {
    787          assert.strictEqual(res.statusCode, 400);
    788 
    789          const chunks = [];
    790 
    791          res.on('data', (chunk) => {
    792            chunks.push(chunk);
    793          });
    794 
    795          res.on('end', () => {
    796            assert.strictEqual(
    797              Buffer.concat(chunks).toString(),
    798              'Invalid Sec-WebSocket-Protocol header'
    799            );
    800            wss.close(done);
    801          });
    802        });
    803      });
    804 
    805      wss.on('connection', () => {
    806        done(new Error("Unexpected 'connection' event"));
    807      });
    808    });
    809 
    810    it('fails if the Sec-WebSocket-Extensions header is invalid', (done) => {
    811      const wss = new WebSocket.Server(
    812        {
    813          perMessageDeflate: true,
    814          port: 0
    815        },
    816        () => {
    817          const req = http.get({
    818            port: wss.address().port,
    819            headers: {
    820              Connection: 'Upgrade',
    821              Upgrade: 'websocket',
    822              'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
    823              'Sec-WebSocket-Version': 13,
    824              'Sec-WebSocket-Extensions':
    825                'permessage-deflate; server_max_window_bits=foo'
    826            }
    827          });
    828 
    829          req.on('response', (res) => {
    830            assert.strictEqual(res.statusCode, 400);
    831 
    832            const chunks = [];
    833 
    834            res.on('data', (chunk) => {
    835              chunks.push(chunk);
    836            });
    837 
    838            res.on('end', () => {
    839              assert.strictEqual(
    840                Buffer.concat(chunks).toString(),
    841                'Invalid or unacceptable Sec-WebSocket-Extensions header'
    842              );
    843              wss.close(done);
    844            });
    845          });
    846        }
    847      );
    848 
    849      wss.on('connection', () => {
    850        done(new Error("Unexpected 'connection' event"));
    851      });
    852    });
    853 
    854    it("emits the 'wsClientError' event", (done) => {
    855      const wss = new WebSocket.Server({ port: 0 }, () => {
    856        const req = http.request({
    857          method: 'POST',
    858          port: wss.address().port,
    859          headers: {
    860            Connection: 'Upgrade',
    861            Upgrade: 'websocket'
    862          }
    863        });
    864 
    865        req.on('response', (res) => {
    866          assert.strictEqual(res.statusCode, 400);
    867          wss.close(done);
    868        });
    869 
    870        req.end();
    871      });
    872 
    873      wss.on('wsClientError', (err, socket, request) => {
    874        assert.ok(err instanceof Error);
    875        assert.strictEqual(err.message, 'Invalid HTTP method');
    876 
    877        assert.ok(request instanceof http.IncomingMessage);
    878        assert.strictEqual(request.method, 'POST');
    879 
    880        socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
    881      });
    882 
    883      wss.on('connection', () => {
    884        done(new Error("Unexpected 'connection' event"));
    885      });
    886    });
    887 
    888    it('fails if the WebSocket server is closing or closed', (done) => {
    889      const server = http.createServer();
    890      const wss = new WebSocket.Server({ noServer: true });
    891 
    892      server.on('upgrade', (req, socket, head) => {
    893        wss.close();
    894        wss.handleUpgrade(req, socket, head, () => {
    895          done(new Error('Unexpected callback invocation'));
    896        });
    897      });
    898 
    899      server.listen(0, () => {
    900        const ws = new WebSocket(`ws://localhost:${server.address().port}`);
    901 
    902        ws.on('unexpected-response', (req, res) => {
    903          assert.strictEqual(res.statusCode, 503);
    904          res.resume();
    905          server.close(done);
    906        });
    907      });
    908    });
    909 
    910    it('handles unsupported extensions', (done) => {
    911      const wss = new WebSocket.Server(
    912        {
    913          perMessageDeflate: true,
    914          port: 0
    915        },
    916        () => {
    917          const req = http.get({
    918            port: wss.address().port,
    919            headers: {
    920              Connection: 'Upgrade',
    921              Upgrade: 'websocket',
    922              'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
    923              'Sec-WebSocket-Version': 13,
    924              'Sec-WebSocket-Extensions': 'foo; bar'
    925            }
    926          });
    927 
    928          req.on('upgrade', (res, socket, head) => {
    929            if (head.length) socket.unshift(head);
    930 
    931            socket.once('data', (chunk) => {
    932              assert.strictEqual(chunk[0], 0x88);
    933              socket.destroy();
    934              wss.close(done);
    935            });
    936          });
    937        }
    938      );
    939 
    940      wss.on('connection', (ws) => {
    941        assert.strictEqual(ws.extensions, '');
    942        ws.close();
    943      });
    944    });
    945 
    946    describe('`verifyClient`', () => {
    947      it('can reject client synchronously', (done) => {
    948        const wss = new WebSocket.Server(
    949          {
    950            verifyClient: () => false,
    951            port: 0
    952          },
    953          () => {
    954            const req = http.get({
    955              port: wss.address().port,
    956              headers: {
    957                Connection: 'Upgrade',
    958                Upgrade: 'websocket',
    959                'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
    960                'Sec-WebSocket-Version': 8
    961              }
    962            });
    963 
    964            req.on('response', (res) => {
    965              assert.strictEqual(res.statusCode, 401);
    966              wss.close(done);
    967            });
    968          }
    969        );
    970 
    971        wss.on('connection', () => {
    972          done(new Error("Unexpected 'connection' event"));
    973        });
    974      });
    975 
    976      it('can accept client synchronously', (done) => {
    977        const server = https.createServer({
    978          cert: fs.readFileSync('test/fixtures/certificate.pem'),
    979          key: fs.readFileSync('test/fixtures/key.pem')
    980        });
    981 
    982        const wss = new WebSocket.Server({
    983          verifyClient: (info) => {
    984            assert.strictEqual(info.origin, 'https://example.com');
    985            assert.strictEqual(info.req.headers.foo, 'bar');
    986            assert.ok(info.secure, true);
    987            return true;
    988          },
    989          server
    990        });
    991 
    992        wss.on('connection', () => {
    993          server.close(done);
    994        });
    995 
    996        server.listen(0, () => {
    997          const ws = new WebSocket(`wss://localhost:${server.address().port}`, {
    998            headers: { Origin: 'https://example.com', foo: 'bar' },
    999            rejectUnauthorized: false
   1000          });
   1001 
   1002          ws.on('open', ws.close);
   1003        });
   1004      });
   1005 
   1006      it('can accept client asynchronously', (done) => {
   1007        const wss = new WebSocket.Server(
   1008          {
   1009            verifyClient: (o, cb) => process.nextTick(cb, true),
   1010            port: 0
   1011          },
   1012          () => {
   1013            const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
   1014 
   1015            ws.on('open', ws.close);
   1016          }
   1017        );
   1018 
   1019        wss.on('connection', () => wss.close(done));
   1020      });
   1021 
   1022      it('can reject client asynchronously', (done) => {
   1023        const wss = new WebSocket.Server(
   1024          {
   1025            verifyClient: (info, cb) => process.nextTick(cb, false),
   1026            port: 0
   1027          },
   1028          () => {
   1029            const req = http.get({
   1030              port: wss.address().port,
   1031              headers: {
   1032                Connection: 'Upgrade',
   1033                Upgrade: 'websocket',
   1034                'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
   1035                'Sec-WebSocket-Version': 8
   1036              }
   1037            });
   1038 
   1039            req.on('response', (res) => {
   1040              assert.strictEqual(res.statusCode, 401);
   1041              wss.close(done);
   1042            });
   1043          }
   1044        );
   1045 
   1046        wss.on('connection', () => {
   1047          done(new Error("Unexpected 'connection' event"));
   1048        });
   1049      });
   1050 
   1051      it('can reject client asynchronously w/ status code', (done) => {
   1052        const wss = new WebSocket.Server(
   1053          {
   1054            verifyClient: (info, cb) => process.nextTick(cb, false, 404),
   1055            port: 0
   1056          },
   1057          () => {
   1058            const req = http.get({
   1059              port: wss.address().port,
   1060              headers: {
   1061                Connection: 'Upgrade',
   1062                Upgrade: 'websocket',
   1063                'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
   1064                'Sec-WebSocket-Version': 8
   1065              }
   1066            });
   1067 
   1068            req.on('response', (res) => {
   1069              assert.strictEqual(res.statusCode, 404);
   1070              wss.close(done);
   1071            });
   1072          }
   1073        );
   1074 
   1075        wss.on('connection', () => {
   1076          done(new Error("Unexpected 'connection' event"));
   1077        });
   1078      });
   1079 
   1080      it('can reject client asynchronously w/ custom headers', (done) => {
   1081        const wss = new WebSocket.Server(
   1082          {
   1083            verifyClient: (info, cb) => {
   1084              process.nextTick(cb, false, 503, '', { 'Retry-After': 120 });
   1085            },
   1086            port: 0
   1087          },
   1088          () => {
   1089            const req = http.get({
   1090              port: wss.address().port,
   1091              headers: {
   1092                Connection: 'Upgrade',
   1093                Upgrade: 'websocket',
   1094                'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
   1095                'Sec-WebSocket-Version': 8
   1096              }
   1097            });
   1098 
   1099            req.on('response', (res) => {
   1100              assert.strictEqual(res.statusCode, 503);
   1101              assert.strictEqual(res.headers['retry-after'], '120');
   1102              wss.close(done);
   1103            });
   1104          }
   1105        );
   1106 
   1107        wss.on('connection', () => {
   1108          done(new Error("Unexpected 'connection' event"));
   1109        });
   1110      });
   1111    });
   1112 
   1113    it("doesn't emit the 'connection' event if socket is closed prematurely", (done) => {
   1114      const server = http.createServer();
   1115 
   1116      server.listen(0, () => {
   1117        const wss = new WebSocket.Server({
   1118          verifyClient: ({ req: { socket } }, cb) => {
   1119            assert.strictEqual(socket.readable, true);
   1120            assert.strictEqual(socket.writable, true);
   1121 
   1122            socket.on('end', () => {
   1123              assert.strictEqual(socket.readable, false);
   1124              assert.strictEqual(socket.writable, true);
   1125              cb(true);
   1126            });
   1127          },
   1128          server
   1129        });
   1130 
   1131        wss.on('connection', () => {
   1132          done(new Error("Unexpected 'connection' event"));
   1133        });
   1134 
   1135        const socket = net.connect(
   1136          {
   1137            port: server.address().port,
   1138            allowHalfOpen: true
   1139          },
   1140          () => {
   1141            socket.end(
   1142              [
   1143                'GET / HTTP/1.1',
   1144                'Host: localhost',
   1145                'Upgrade: websocket',
   1146                'Connection: Upgrade',
   1147                'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==',
   1148                'Sec-WebSocket-Version: 13',
   1149                '\r\n'
   1150              ].join('\r\n')
   1151            );
   1152          }
   1153        );
   1154 
   1155        socket.on('end', () => {
   1156          wss.close();
   1157          server.close(done);
   1158        });
   1159      });
   1160    });
   1161 
   1162    it('handles data passed along with the upgrade request', (done) => {
   1163      const wss = new WebSocket.Server({ port: 0 }, () => {
   1164        const req = http.request({
   1165          port: wss.address().port,
   1166          headers: {
   1167            Connection: 'Upgrade',
   1168            Upgrade: 'websocket',
   1169            'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
   1170            'Sec-WebSocket-Version': 13
   1171          }
   1172        });
   1173 
   1174        const list = Sender.frame(Buffer.from('Hello'), {
   1175          fin: true,
   1176          rsv1: false,
   1177          opcode: 0x01,
   1178          mask: true,
   1179          readOnly: false
   1180        });
   1181 
   1182        req.write(Buffer.concat(list));
   1183        req.end();
   1184      });
   1185 
   1186      wss.on('connection', (ws) => {
   1187        ws.on('message', (data, isBinary) => {
   1188          assert.deepStrictEqual(data, Buffer.from('Hello'));
   1189          assert.ok(!isBinary);
   1190          wss.close(done);
   1191        });
   1192      });
   1193    });
   1194 
   1195    describe('`handleProtocols`', () => {
   1196      it('allows to select a subprotocol', (done) => {
   1197        const handleProtocols = (protocols, request) => {
   1198          assert.ok(request instanceof http.IncomingMessage);
   1199          assert.strictEqual(request.url, '/');
   1200          return Array.from(protocols).pop();
   1201        };
   1202        const wss = new WebSocket.Server({ handleProtocols, port: 0 }, () => {
   1203          const ws = new WebSocket(`ws://localhost:${wss.address().port}`, [
   1204            'foo',
   1205            'bar'
   1206          ]);
   1207 
   1208          ws.on('open', () => {
   1209            assert.strictEqual(ws.protocol, 'bar');
   1210            wss.close(done);
   1211          });
   1212        });
   1213 
   1214        wss.on('connection', (ws) => {
   1215          ws.close();
   1216        });
   1217      });
   1218    });
   1219 
   1220    it("emits the 'headers' event", (done) => {
   1221      const wss = new WebSocket.Server({ port: 0 }, () => {
   1222        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
   1223 
   1224        ws.on('open', ws.close);
   1225      });
   1226 
   1227      wss.on('headers', (headers, request) => {
   1228        assert.deepStrictEqual(headers.slice(0, 3), [
   1229          'HTTP/1.1 101 Switching Protocols',
   1230          'Upgrade: websocket',
   1231          'Connection: Upgrade'
   1232        ]);
   1233        assert.ok(request instanceof http.IncomingMessage);
   1234        assert.strictEqual(request.url, '/');
   1235 
   1236        wss.on('connection', () => wss.close(done));
   1237      });
   1238    });
   1239  });
   1240 
   1241  describe('permessage-deflate', () => {
   1242    it('is disabled by default', (done) => {
   1243      const wss = new WebSocket.Server({ port: 0 }, () => {
   1244        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
   1245 
   1246        ws.on('open', ws.close);
   1247      });
   1248 
   1249      wss.on('connection', (ws, req) => {
   1250        assert.strictEqual(
   1251          req.headers['sec-websocket-extensions'],
   1252          'permessage-deflate; client_max_window_bits'
   1253        );
   1254        assert.strictEqual(ws.extensions, '');
   1255        wss.close(done);
   1256      });
   1257    });
   1258 
   1259    it('uses configuration options', (done) => {
   1260      const wss = new WebSocket.Server(
   1261        {
   1262          perMessageDeflate: { clientMaxWindowBits: 8 },
   1263          port: 0
   1264        },
   1265        () => {
   1266          const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
   1267 
   1268          ws.on('upgrade', (res) => {
   1269            assert.strictEqual(
   1270              res.headers['sec-websocket-extensions'],
   1271              'permessage-deflate; client_max_window_bits=8'
   1272            );
   1273 
   1274            wss.close(done);
   1275          });
   1276        }
   1277      );
   1278 
   1279      wss.on('connection', (ws) => {
   1280        ws.close();
   1281      });
   1282    });
   1283  });
   1284 });