Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  websocket.test.js

  Sprache: JAVA
 

/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^ws$" }] */

'use strict';

const assert = require('assert');
const crypto = require('crypto');
const https = require('https');
const http = require('http');
const path = require('path');
const net = require('net');
const tls = require('tls');
const os = require('os');
const fs = require('fs');
const { URL } = require('url');

const Sender = require('../lib/sender');
const WebSocket = require('..');
const {
  CloseEvent,
  ErrorEvent,
  Event,
  MessageEvent
} = require('../lib/event-target');
const { EMPTY_BUFFER, GUID, kListener, NOOP } = require('../lib/constants');

class CustomAgent extends http.Agent {
  addRequest() {}
}

describe('WebSocket', () => {
  describe('#ctor', () => {
    it('throws an error when using an invalid url', () => {
      assert.throws(
        () => new WebSocket('foo'),
        /^SyntaxError: Invalid URL: foo$/
      );

      assert.throws(
        () => new WebSocket('https://websocket-echo.com'),
        /^SyntaxError: The URL's protocol must be one of "ws:", "wss:", or "ws\+unix:"$/
      );

      assert.throws(
        () => new WebSocket('ws+unix:'),
        /^SyntaxError: The URL's pathname is empty$/
      );

      assert.throws(
        () => new WebSocket('wss://websocket-echo.com#foo'),
        /^SyntaxError: The URL contains a fragment identifier$/
      );
    });

    it('throws an error if a subprotocol is invalid or duplicated', () => {
      for (const subprotocol of [null'''a,b', ['a''a']]) {
        assert.throws(
          () => new WebSocket('ws://localhost', subprotocol),
          /^SyntaxError: An invalid or duplicated subprotocol was specified$/
        );
      }
    });

    it('accepts `url.URL` objects as url', (done) => {
      const agent = new CustomAgent();

      agent.addRequest = (req, opts) => {
        assert.strictEqual(opts.host, '::1');
        assert.strictEqual(req.path, '/');
        done();
      };

      const ws = new WebSocket(new URL('ws://[::1]'), { agent });
    });

    describe('options', () => {
      it('accepts the `options` object as 3rd argument', () => {
        const agent = new CustomAgent();
        let count = 0;
        let ws;

        agent.addRequest = (req) => {
          assert.strictEqual(
            req.getHeader('sec-websocket-protocol'),
            undefined
          );
          count++;
        };

        ws = new WebSocket('ws://localhost', undefined, { agent });
        ws = new WebSocket('ws://localhost', [], { agent });

        assert.strictEqual(count, 2);
      });

      it('accepts the `maxPayload` option', (done) => {
        const maxPayload = 20480;
        const wss = new WebSocket.Server(
          {
            perMessageDeflate: true,
            port: 0
          },
          () => {
            const ws = new WebSocket(`ws://localhost:${wss.address().port}`, {
              perMessageDeflate: true,
              maxPayload
            });

            ws.on('open', () => {
              assert.strictEqual(ws._receiver._maxPayload, maxPayload);
              assert.strictEqual(
                ws._receiver._extensions['permessage-deflate']._maxPayload,
                maxPayload
              );
              wss.close(done);
            });
          }
        );

        wss.on('connection', (ws) => {
          ws.close();
        });
      });

      it('throws an error when using an invalid `protocolVersion`', () => {
        const options = { agent: new CustomAgent(), protocolVersion: 1000 };

        assert.throws(
          () => new WebSocket('ws://localhost', options),
          /^RangeError: Unsupported protocol version: 1000 \(supported versions: 8, 13\)$/
        );
      });

      it('honors the `generateMask` option', (done) => {
        const data = Buffer.from('foo');
        const wss = new WebSocket.Server({ port: 0 }, () => {
          const ws = new WebSocket(`ws://localhost:${wss.address().port}`, {
            generateMask() {}
          });

          ws.on('open', () => {
            ws.send(data);
          });

          ws.on('close', (code, reason) => {
            assert.strictEqual(code, 1005);
            assert.deepStrictEqual(reason, EMPTY_BUFFER);

            wss.close(done);
          });
        });

        wss.on('connection', (ws) => {
          const chunks = [];

          ws._socket.prependListener('data', (chunk) => {
            chunks.push(chunk);
          });

          ws.on('message', (message) => {
            assert.deepStrictEqual(message, data);
            assert.deepStrictEqual(
              Buffer.concat(chunks).slice(2, 6),
              Buffer.alloc(4)
            );

            ws.close();
          });
        });
      });
    });
  });

  describe('Constants', () => {
    const readyStates = {
      CONNECTING: 0,
      OPEN: 1,
      CLOSING: 2,
      CLOSED: 3
    };

    Object.keys(readyStates).forEach((state) => {
      describe(`\`${state}\``, () => {
        it('is enumerable property of class', () => {
          const descriptor = Object.getOwnPropertyDescriptor(WebSocket, state);

          assert.deepStrictEqual(descriptor, {
            configurable: false,
            enumerable: true,
            value: readyStates[state],
            writable: false
          });
        });

        it('is enumerable property of prototype', () => {
          const descriptor = Object.getOwnPropertyDescriptor(
            WebSocket.prototype,
            state
          );

          assert.deepStrictEqual(descriptor, {
            configurable: false,
            enumerable: true,
            value: readyStates[state],
            writable: false
          });
        });
      });
    });
  });

  describe('Attributes', () => {
    describe('`binaryType`', () => {
      it('is enumerable and configurable', () => {
        const descriptor = Object.getOwnPropertyDescriptor(
          WebSocket.prototype,
          'binaryType'
        );

        assert.strictEqual(descriptor.configurable, true);
        assert.strictEqual(descriptor.enumerable, true);
        assert.ok(descriptor.get !== undefined);
        assert.ok(descriptor.set !== undefined);
      });

      it("defaults to 'nodebuffer'", () => {
        const ws = new WebSocket('ws://localhost', {
          agent: new CustomAgent()
        });

        assert.strictEqual(ws.binaryType, 'nodebuffer');
      });

      it("can be changed to 'arraybuffer' or 'fragments'", () => {
        const ws = new WebSocket('ws://localhost', {
          agent: new CustomAgent()
        });

        ws.binaryType = 'arraybuffer';
        assert.strictEqual(ws.binaryType, 'arraybuffer');

        ws.binaryType = 'foo';
        assert.strictEqual(ws.binaryType, 'arraybuffer');

        ws.binaryType = 'fragments';
        assert.strictEqual(ws.binaryType, 'fragments');

        ws.binaryType = '';
        assert.strictEqual(ws.binaryType, 'fragments');

        ws.binaryType = 'nodebuffer';
        assert.strictEqual(ws.binaryType, 'nodebuffer');
      });
    });

    describe('`bufferedAmount`', () => {
      it('is enumerable and configurable', () => {
        const descriptor = Object.getOwnPropertyDescriptor(
          WebSocket.prototype,
          'bufferedAmount'
        );

        assert.strictEqual(descriptor.configurable, true);
        assert.strictEqual(descriptor.enumerable, true);
        assert.ok(descriptor.get !== undefined);
        assert.ok(descriptor.set === undefined);
      });

      it('defaults to zero', () => {
        const ws = new WebSocket('ws://localhost', {
          agent: new CustomAgent()
        });

        assert.strictEqual(ws.bufferedAmount, 0);
      });

      it('defaults to zero upon "open"', (done) => {
        const wss = new WebSocket.Server({ port: 0 }, () => {
          const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

          ws.onopen = () => {
            assert.strictEqual(ws.bufferedAmount, 0);
            wss.close(done);
          };
        });

        wss.on('connection', (ws) => {
          ws.close();
        });
      });

      it('takes into account the data in the sender queue', (done) => {
        const wss = new WebSocket.Server(
          {
            perMessageDeflate: true,
            port: 0
          },
          () => {
            const ws = new WebSocket(`ws://localhost:${wss.address().port}`, {
              perMessageDeflate: { threshold: 0 }
            });

            ws.on('open', () => {
              ws.send('foo');

              assert.strictEqual(ws.bufferedAmount, 3);

              ws.send('bar', (err) => {
                assert.ifError(err);
                assert.strictEqual(ws.bufferedAmount, 0);
                wss.close(done);
              });

              assert.strictEqual(ws.bufferedAmount, 6);
            });
          }
        );

        wss.on('connection', (ws) => {
          ws.close();
        });
      });

      it('takes into account the data in the socket queue', (done) => {
        const wss = new WebSocket.Server({ port: 0 }, () => {
          const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
        });

        wss.on('connection', (ws) => {
          const data = Buffer.alloc(1024, 61);

          while (ws.bufferedAmount === 0) {
            ws.send(data);
          }

          assert.ok(ws.bufferedAmount > 0);
          assert.strictEqual(
            ws.bufferedAmount,
            ws._socket._writableState.length
          );

          ws.on('close', () => wss.close(done));
          ws.close();
        });
      });
    });

    describe('`extensions`', () => {
      it('is enumerable and configurable', () => {
        const descriptor = Object.getOwnPropertyDescriptor(
          WebSocket.prototype,
          'bufferedAmount'
        );

        assert.strictEqual(descriptor.configurable, true);
        assert.strictEqual(descriptor.enumerable, true);
        assert.ok(descriptor.get !== undefined);
        assert.ok(descriptor.set === undefined);
      });

      it('exposes the negotiated extensions names (1/2)', (done) => {
        const wss = new WebSocket.Server({ port: 0 }, () => {
          const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

          assert.strictEqual(ws.extensions, '');

          ws.on('open', () => {
            assert.strictEqual(ws.extensions, '');
            ws.on('close', () => wss.close(done));
          });
        });

        wss.on('connection', (ws) => {
          assert.strictEqual(ws.extensions, '');
          ws.close();
        });
      });

      it('exposes the negotiated extensions names (2/2)', (done) => {
        const wss = new WebSocket.Server(
          {
            perMessageDeflate: true,
            port: 0
          },
          () => {
            const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

            assert.strictEqual(ws.extensions, '');

            ws.on('open', () => {
              assert.strictEqual(ws.extensions, 'permessage-deflate');
              ws.on('close', () => wss.close(done));
            });
          }
        );

        wss.on('connection', (ws) => {
          assert.strictEqual(ws.extensions, 'permessage-deflate');
          ws.close();
        });
      });
    });

    describe('`isPaused`', () => {
      it('is enumerable and configurable', () => {
        const descriptor = Object.getOwnPropertyDescriptor(
          WebSocket.prototype,
          'isPaused'
        );

        assert.strictEqual(descriptor.configurable, true);
        assert.strictEqual(descriptor.enumerable, true);
        assert.ok(descriptor.get !== undefined);
        assert.ok(descriptor.set === undefined);
      });

      it('indicates whether the websocket is paused', (done) => {
        const wss = new WebSocket.Server({ port: 0 }, () => {
          const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

          ws.on('open', () => {
            ws.pause();
            assert.ok(ws.isPaused);

            ws.resume();
            assert.ok(!ws.isPaused);

            ws.close();
            wss.close(done);
          });

          assert.ok(!ws.isPaused);
        });
      });
    });

    describe('`protocol`', () => {
      it('is enumerable and configurable', () => {
        const descriptor = Object.getOwnPropertyDescriptor(
          WebSocket.prototype,
          'protocol'
        );

        assert.strictEqual(descriptor.configurable, true);
        assert.strictEqual(descriptor.enumerable, true);
        assert.ok(descriptor.get !== undefined);
        assert.ok(descriptor.set === undefined);
      });

      it('exposes the subprotocol selected by the server', (done) => {
        const wss = new WebSocket.Server({ port: 0 }, () => {
          const port = wss.address().port;
          const ws = new WebSocket(`ws://localhost:${port}`, 'foo');

          assert.strictEqual(ws.extensions, '');

          ws.on('open', () => {
            assert.strictEqual(ws.protocol, 'foo');
            ws.on('close', () => wss.close(done));
          });
        });

        wss.on('connection', (ws) => {
          assert.strictEqual(ws.protocol, 'foo');
          ws.close();
        });
      });
    });

    describe('`readyState`', () => {
      it('is enumerable and configurable', () => {
        const descriptor = Object.getOwnPropertyDescriptor(
          WebSocket.prototype,
          'readyState'
        );

        assert.strictEqual(descriptor.configurable, true);
        assert.strictEqual(descriptor.enumerable, true);
        assert.ok(descriptor.get !== undefined);
        assert.ok(descriptor.set === undefined);
      });

      it('defaults to `CONNECTING`', () => {
        const ws = new WebSocket('ws://localhost', {
          agent: new CustomAgent()
        });

        assert.strictEqual(ws.readyState, WebSocket.CONNECTING);
      });

      it('is set to `OPEN` once connection is established', (done) => {
        const wss = new WebSocket.Server({ port: 0 }, () => {
          const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

          ws.on('open', () => {
            assert.strictEqual(ws.readyState, WebSocket.OPEN);
            ws.close();
          });

          ws.on('close', () => wss.close(done));
        });
      });

      it('is set to `CLOSED` once connection is closed', (done) => {
        const wss = new WebSocket.Server({ port: 0 }, () => {
          const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

          ws.on('close', () => {
            assert.strictEqual(ws.readyState, WebSocket.CLOSED);
            wss.close(done);
          });

          ws.on('open', () => ws.close(1001));
        });
      });

      it('is set to `CLOSED` once connection is terminated', (done) => {
        const wss = new WebSocket.Server({ port: 0 }, () => {
          const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

          ws.on('close', () => {
            assert.strictEqual(ws.readyState, WebSocket.CLOSED);
            wss.close(done);
          });

          ws.on('open', () => ws.terminate());
        });
      });
    });

    describe('`url`', () => {
      it('is enumerable and configurable', () => {
        const descriptor = Object.getOwnPropertyDescriptor(
          WebSocket.prototype,
          'url'
        );

        assert.strictEqual(descriptor.configurable, true);
        assert.strictEqual(descriptor.enumerable, true);
        assert.ok(descriptor.get !== undefined);
        assert.ok(descriptor.set === undefined);
      });

      it('exposes the server url', () => {
        const url = 'ws://localhost';
        const ws = new WebSocket(url, { agent: new CustomAgent() });

        assert.strictEqual(ws.url, url);
      });
    });
  });

  describe('Events', () => {
    it("emits an 'error' event if an error occurs", (done) => {
      let clientCloseEventEmitted = false;
      let serverClientCloseEventEmitted = false;

      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('error', (err) => {
          assert.ok(err instanceof RangeError);
          assert.strictEqual(err.code, 'WS_ERR_INVALID_OPCODE');
          assert.strictEqual(
            err.message,
            'Invalid WebSocket frame: invalid opcode 5'
          );

          ws.on('close', (code, reason) => {
            assert.strictEqual(code, 1006);
            assert.strictEqual(reason, EMPTY_BUFFER);

            clientCloseEventEmitted = true;
            if (serverClientCloseEventEmitted) wss.close(done);
          });
        });
      });

      wss.on('connection', (ws) => {
        ws.on('close', (code, reason) => {
          assert.strictEqual(code, 1002);
          assert.deepStrictEqual(reason, EMPTY_BUFFER);

          serverClientCloseEventEmitted = true;
          if (clientCloseEventEmitted) wss.close(done);
        });

        ws._socket.write(Buffer.from([0x85, 0x00]));
      });
    });

    it('does not re-emit `net.Socket` errors', (done) => {
      const codes = ['EPIPE''ECONNABORTED''ECANCELED''ECONNRESET'];
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => {
          ws._socket.on('error', (err) => {
            assert.ok(err instanceof Error);
            assert.ok(codes.includes(err.code), `Unexpected code: ${err.code}`);
            ws.on('close', (code, message) => {
              assert.strictEqual(code, 1006);
              assert.strictEqual(message, EMPTY_BUFFER);
              wss.close(done);
            });
          });

          for (const client of wss.clients) client.terminate();
          ws.send('foo');
          ws.send('bar');
        });
      });
    });

    it("emits an 'upgrade' event", (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
        ws.on('upgrade', (res) => {
          assert.ok(res instanceof http.IncomingMessage);
          wss.close(done);
        });
      });

      wss.on('connection', (ws) => {
        ws.close();
      });
    });

    it("emits a 'ping' event", (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
        ws.on('ping', () => wss.close(done));
      });

      wss.on('connection', (ws) => {
        ws.ping();
        ws.close();
      });
    });

    it("emits a 'pong' event", (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
        ws.on('pong', () => wss.close(done));
      });

      wss.on('connection', (ws) => {
        ws.pong();
        ws.close();
      });
    });

    it("emits a 'redirect' event", (done) => {
      const server = http.createServer();
      const wss = new WebSocket.Server({ noServer: true, path: '/foo' });

      server.once('upgrade', (req, socket) => {
        socket.end('HTTP/1.1 302 Found\r\nLocation: /foo\r\n\r\n');
        server.once('upgrade', (req, socket, head) => {
          wss.handleUpgrade(req, socket, head, (ws) => {
            ws.close();
          });
        });
      });

      server.listen(() => {
        const port = server.address().port;
        const ws = new WebSocket(`ws://localhost:${port}`, {
          followRedirects: true
        });

        ws.on('redirect', (url, req) => {
          assert.strictEqual(ws._redirects, 1);
          assert.strictEqual(url, `ws://localhost:${port}/foo`);
          assert.ok(req instanceof http.ClientRequest);

          ws.on('close', (code) => {
            assert.strictEqual(code, 1005);
            server.close(done);
          });
        });
      });
    });
  });

  describe('Connection establishing', () => {
    const server = http.createServer();

    beforeEach((done) => server.listen(0, done));
    afterEach((done) => server.close(done));

    it('fails if the Upgrade header field value is not "websocket"', (done) => {
      server.once('upgrade', (req, socket) => {
        socket.on('end', socket.end);
        socket.write(
          'HTTP/1.1 101 Switching Protocols\r\n' +
            'Connection: Upgrade\r\n' +
            'Upgrade: foo\r\n' +
            '\r\n'
        );
      });

      const ws = new WebSocket(`ws://localhost:${server.address().port}`);

      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(err.message, 'Invalid Upgrade header');
        done();
      });
    });

    it('fails if the Sec-WebSocket-Accept header is invalid', (done) => {
      server.once('upgrade', (req, socket) => {
        socket.on('end', socket.end);
        socket.write(
          'HTTP/1.1 101 Switching Protocols\r\n' +
            'Upgrade: websocket\r\n' +
            'Connection: Upgrade\r\n' +
            'Sec-WebSocket-Accept: CxYS6+NgJSBG74mdgLvGscRvpns=\r\n' +
            '\r\n'
        );
      });

      const ws = new WebSocket(`ws://localhost:${server.address().port}`);

      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(err.message, 'Invalid Sec-WebSocket-Accept header');
        done();
      });
    });

    it('close event is raised when server closes connection', (done) => {
      server.once('upgrade', (req, socket) => {
        const key = crypto
          .createHash('sha1')
          .update(req.headers['sec-websocket-key'] + GUID)
          .digest('base64');

        socket.end(
          'HTTP/1.1 101 Switching Protocols\r\n' +
            'Upgrade: websocket\r\n' +
            'Connection: Upgrade\r\n' +
            `Sec-WebSocket-Accept: ${key}\r\n` +
            '\r\n'
        );
      });

      const ws = new WebSocket(`ws://localhost:${server.address().port}`);

      ws.on('close', (code, reason) => {
        assert.strictEqual(code, 1006);
        assert.strictEqual(reason, EMPTY_BUFFER);
        done();
      });
    });

    it('error is emitted if server aborts connection', (done) => {
      server.once('upgrade', (req, socket) => {
        socket.end(
          `HTTP/1.1 401 ${http.STATUS_CODES[401]}\r\n` +
            'Connection: close\r\n' +
            'Content-type: text/html\r\n' +
            `Content-Length: ${http.STATUS_CODES[401].length}\r\n` +
            '\r\n'
        );
      });

      const ws = new WebSocket(`ws://localhost:${server.address().port}`);

      ws.on('open', () => done(new Error("Unexpected 'open' event")));
      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(err.message, 'Unexpected server response: 401');
        done();
      });
    });

    it('unexpected response can be read when sent by server', (done) => {
      server.once('upgrade', (req, socket) => {
        socket.end(
          `HTTP/1.1 401 ${http.STATUS_CODES[401]}\r\n` +
            'Connection: close\r\n' +
            'Content-type: text/html\r\n' +
            'Content-Length: 3\r\n' +
            '\r\n' +
            'foo'
        );
      });

      const ws = new WebSocket(`ws://localhost:${server.address().port}`);

      ws.on('open', () => done(new Error("Unexpected 'open' event")));
      ws.on('error', () => done(new Error("Unexpected 'error' event")));
      ws.on('unexpected-response', (req, res) => {
        assert.strictEqual(res.statusCode, 401);

        let data = '';

        res.on('data', (v) => {
          data += v;
        });

        res.on('end', () => {
          assert.strictEqual(data, 'foo');
          done();
        });
      });
    });

    it('request can be aborted when unexpected response is sent by server', (done) => {
      server.once('upgrade', (req, socket) => {
        socket.end(
          `HTTP/1.1 401 ${http.STATUS_CODES[401]}\r\n` +
            'Connection: close\r\n' +
            'Content-type: text/html\r\n' +
            'Content-Length: 3\r\n' +
            '\r\n' +
            'foo'
        );
      });

      const ws = new WebSocket(`ws://localhost:${server.address().port}`);

      ws.on('open', () => done(new Error("Unexpected 'open' event")));
      ws.on('error', () => done(new Error("Unexpected 'error' event")));
      ws.on('unexpected-response', (req, res) => {
        assert.strictEqual(res.statusCode, 401);

        res.on('end', done);
        req.abort();
      });
    });

    it('fails if the opening handshake timeout expires', (done) => {
      server.once('upgrade', (req, socket) => socket.on('end', socket.end));

      const port = server.address().port;
      const ws = new WebSocket(`ws://localhost:${port}`, {
        handshakeTimeout: 100
      });

      ws.on('open', () => done(new Error("Unexpected 'open' event")));
      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(err.message, 'Opening handshake has timed out');
        done();
      });
    });

    it('fails if an unexpected Sec-WebSocket-Extensions header is received', (done) => {
      server.once('upgrade', (req, socket) => {
        const key = crypto
          .createHash('sha1')
          .update(req.headers['sec-websocket-key'] + GUID)
          .digest('base64');

        socket.end(
          'HTTP/1.1 101 Switching Protocols\r\n' +
            'Upgrade: websocket\r\n' +
            'Connection: Upgrade\r\n' +
            `Sec-WebSocket-Accept: ${key}\r\n` +
            'Sec-WebSocket-Extensions: foo\r\n' +
            '\r\n'
        );
      });

      const ws = new WebSocket(`ws://localhost:${server.address().port}`, {
        perMessageDeflate: false
      });

      ws.on('open', () => done(new Error("Unexpected 'open' event")));
      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(
          err.message,
          'Server sent a Sec-WebSocket-Extensions header but no extension ' +
            'was requested'
        );
        ws.on('close', () => done());
      });
    });

    it('fails if the Sec-WebSocket-Extensions header is invalid (1/2)', (done) => {
      server.once('upgrade', (req, socket) => {
        const key = crypto
          .createHash('sha1')
          .update(req.headers['sec-websocket-key'] + GUID)
          .digest('base64');

        socket.end(
          'HTTP/1.1 101 Switching Protocols\r\n' +
            'Upgrade: websocket\r\n' +
            'Connection: Upgrade\r\n' +
            `Sec-WebSocket-Accept: ${key}\r\n` +
            'Sec-WebSocket-Extensions: foo;=\r\n' +
            '\r\n'
        );
      });

      const ws = new WebSocket(`ws://localhost:${server.address().port}`);

      ws.on('open', () => done(new Error("Unexpected 'open' event")));
      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(
          err.message,
          'Invalid Sec-WebSocket-Extensions header'
        );
        ws.on('close', () => done());
      });
    });

    it('fails if the Sec-WebSocket-Extensions header is invalid (2/2)', (done) => {
      server.once('upgrade', (req, socket) => {
        const key = crypto
          .createHash('sha1')
          .update(req.headers['sec-websocket-key'] + GUID)
          .digest('base64');

        socket.end(
          'HTTP/1.1 101 Switching Protocols\r\n' +
            'Upgrade: websocket\r\n' +
            'Connection: Upgrade\r\n' +
            `Sec-WebSocket-Accept: ${key}\r\n` +
            'Sec-WebSocket-Extensions: ' +
            'permessage-deflate; client_max_window_bits=7\r\n' +
            '\r\n'
        );
      });

      const ws = new WebSocket(`ws://localhost:${server.address().port}`);

      ws.on('open', () => done(new Error("Unexpected 'open' event")));
      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(
          err.message,
          'Invalid Sec-WebSocket-Extensions header'
        );
        ws.on('close', () => done());
      });
    });

    it('fails if an unexpected extension is received (1/2)', (done) => {
      server.once('upgrade', (req, socket) => {
        const key = crypto
          .createHash('sha1')
          .update(req.headers['sec-websocket-key'] + GUID)
          .digest('base64');

        socket.end(
          'HTTP/1.1 101 Switching Protocols\r\n' +
            'Upgrade: websocket\r\n' +
            'Connection: Upgrade\r\n' +
            `Sec-WebSocket-Accept: ${key}\r\n` +
            'Sec-WebSocket-Extensions: foo\r\n' +
            '\r\n'
        );
      });

      const ws = new WebSocket(`ws://localhost:${server.address().port}`);

      ws.on('open', () => done(new Error("Unexpected 'open' event")));
      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(
          err.message,
          'Server indicated an extension that was not requested'
        );
        ws.on('close', () => done());
      });
    });

    it('fails if an unexpected extension is received (2/2)', (done) => {
      server.once('upgrade', (req, socket) => {
        const key = crypto
          .createHash('sha1')
          .update(req.headers['sec-websocket-key'] + GUID)
          .digest('base64');

        socket.end(
          'HTTP/1.1 101 Switching Protocols\r\n' +
            'Upgrade: websocket\r\n' +
            'Connection: Upgrade\r\n' +
            `Sec-WebSocket-Accept: ${key}\r\n` +
            'Sec-WebSocket-Extensions: permessage-deflate,foo\r\n' +
            '\r\n'
        );
      });

      const ws = new WebSocket(`ws://localhost:${server.address().port}`);

      ws.on('open', () => done(new Error("Unexpected 'open' event")));
      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(
          err.message,
          'Server indicated an extension that was not requested'
        );
        ws.on('close', () => done());
      });
    });

    it('fails if server sends a subprotocol when none was requested', (done) => {
      const wss = new WebSocket.Server({ server });

      wss.on('headers', (headers) => {
        headers.push('Sec-WebSocket-Protocol: foo');
      });

      const ws = new WebSocket(`ws://localhost:${server.address().port}`);

      ws.on('open', () => done(new Error("Unexpected 'open' event")));
      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(
          err.message,
          'Server sent a subprotocol but none was requested'
        );
        ws.on('close', () => wss.close(done));
      });
    });

    it('fails if server sends an invalid subprotocol (1/2)', (done) => {
      const wss = new WebSocket.Server({
        handleProtocols: () => 'baz',
        server
      });

      const ws = new WebSocket(`ws://localhost:${server.address().port}`, [
        'foo',
        'bar'
      ]);

      ws.on('open', () => done(new Error("Unexpected 'open' event")));
      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(err.message, 'Server sent an invalid subprotocol');
        ws.on('close', () => wss.close(done));
      });
    });

    it('fails if server sends an invalid subprotocol (2/2)', (done) => {
      server.once('upgrade', (req, socket) => {
        const key = crypto
          .createHash('sha1')
          .update(req.headers['sec-websocket-key'] + GUID)
          .digest('base64');

        socket.end(
          'HTTP/1.1 101 Switching Protocols\r\n' +
            'Upgrade: websocket\r\n' +
            'Connection: Upgrade\r\n' +
            `Sec-WebSocket-Accept: ${key}\r\n` +
            'Sec-WebSocket-Protocol:\r\n' +
            '\r\n'
        );
      });

      const ws = new WebSocket(`ws://localhost:${server.address().port}`, [
        'foo',
        'bar'
      ]);

      ws.on('open', () => done(new Error("Unexpected 'open' event")));
      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(err.message, 'Server sent an invalid subprotocol');
        ws.on('close', () => done());
      });
    });

    it('fails if server sends no subprotocol', (done) => {
      const wss = new WebSocket.Server({
        handleProtocols() {},
        server
      });

      const ws = new WebSocket(`ws://localhost:${server.address().port}`, [
        'foo',
        'bar'
      ]);

      ws.on('open', () => done(new Error("Unexpected 'open' event")));
      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(err.message, 'Server sent no subprotocol');
        ws.on('close', () => wss.close(done));
      });
    });

    it('does not follow redirects by default', (done) => {
      server.once('upgrade', (req, socket) => {
        socket.end(
          'HTTP/1.1 301 Moved Permanently\r\n' +
            'Location: ws://localhost:8080\r\n' +
            '\r\n'
        );
      });

      const ws = new WebSocket(`ws://localhost:${server.address().port}`);

      ws.on('open', () => done(new Error("Unexpected 'open' event")));
      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(err.message, 'Unexpected server response: 301');
        assert.strictEqual(ws._redirects, 0);
        ws.on('close', () => done());
      });
    });

    it('honors the `followRedirects` option', (done) => {
      const wss = new WebSocket.Server({ noServer: true, path: '/foo' });

      server.once('upgrade', (req, socket) => {
        socket.end('HTTP/1.1 302 Found\r\nLocation: /foo\r\n\r\n');
        server.once('upgrade', (req, socket, head) => {
          wss.handleUpgrade(req, socket, head, NOOP);
        });
      });

      const port = server.address().port;
      const ws = new WebSocket(`ws://localhost:${port}`, {
        followRedirects: true
      });

      ws.on('open', () => {
        assert.strictEqual(ws.url, `ws://localhost:${port}/foo`);
        assert.strictEqual(ws._redirects, 1);
        ws.on('close', () => done());
        ws.close();
      });
    });

    it('honors the `maxRedirects` option', (done) => {
      const onUpgrade = (req, socket) => {
        socket.end('HTTP/1.1 302 Found\r\nLocation: /\r\n\r\n');
      };

      server.on('upgrade', onUpgrade);

      const ws = new WebSocket(`ws://localhost:${server.address().port}`, {
        followRedirects: true,
        maxRedirects: 1
      });

      ws.on('open', () => done(new Error("Unexpected 'open' event")));
      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(err.message, 'Maximum redirects exceeded');
        assert.strictEqual(ws._redirects, 2);

        server.removeListener('upgrade', onUpgrade);
        ws.on('close', () => done());
      });
    });

    it('emits an error if the redirect URL is invalid (1/2)', (done) => {
      server.once('upgrade', (req, socket) => {
        socket.end('HTTP/1.1 302 Found\r\nLocation: ws://\r\n\r\n');
      });

      const ws = new WebSocket(`ws://localhost:${server.address().port}`, {
        followRedirects: true
      });

      ws.on('open', () => done(new Error("Unexpected 'open' event")));
      ws.on('error', (err) => {
        assert.ok(err instanceof SyntaxError);
        assert.strictEqual(err.message, 'Invalid URL: ws://');
        assert.strictEqual(ws._redirects, 1);

        ws.on('close', () => done());
      });
    });

    it('emits an error if the redirect URL is invalid (2/2)', (done) => {
      server.once('upgrade', (req, socket) => {
        socket.end('HTTP/1.1 302 Found\r\nLocation: http://localhost\r\n\r\n');
      });

      const ws = new WebSocket(`ws://localhost:${server.address().port}`, {
        followRedirects: true
      });

      ws.on('open', () => done(new Error("Unexpected 'open' event")));
      ws.on('error', (err) => {
        assert.ok(err instanceof SyntaxError);
        assert.strictEqual(
          err.message,
          'The URL\'s protocol must be one of "ws:""wss:", or "ws+unix:"'
        );
        assert.strictEqual(ws._redirects, 1);

        ws.on('close', () => done());
      });
    });

    it('uses the first url userinfo when following redirects', (done) => {
      const wss = new WebSocket.Server({ noServer: true, path: '/foo' });
      const authorization = 'Basic Zm9vOmJhcg==';

      server.once('upgrade', (req, socket) => {
        socket.end(
          'HTTP/1.1 302 Found\r\n' +
            `Location: ws://baz:qux@localhost:${port}/foo\r\n\r\n`
        );
        server.once('upgrade', (req, socket, head) => {
          wss.handleUpgrade(req, socket, head, (ws, req) => {
            assert.strictEqual(req.headers.authorization, authorization);
            ws.close();
          });
        });
      });

      const port = server.address().port;
      const ws = new WebSocket(`ws://foo:bar@localhost:${port}`, {
        followRedirects: true
      });

      assert.strictEqual(ws._req.getHeader('Authorization'), authorization);

      ws.on('close', (code) => {
        assert.strictEqual(code, 1005);
        assert.strictEqual(ws.url, `ws://baz:qux@localhost:${port}/foo`);
        assert.strictEqual(ws._redirects, 1);

        wss.close(done);
      });
    });

    describe('When moving away from a secure context', () => {
      function proxy(httpServer, httpsServer) {
        const server = net.createServer({ allowHalfOpen: true });

        server.on('connection', (socket) => {
          socket.on('readable'function read() {
            socket.removeListener('readable', read);

            const buf = socket.read(1);
            const target = buf[0] === 22 ? httpsServer : httpServer;

            socket.unshift(buf);
            target.emit('connection', socket);
          });
        });

        return server;
      }

      describe("If there is no 'redirect' event listener", () => {
        it('drops the `auth` option', (done) => {
          const httpServer = http.createServer();
          const httpsServer = https.createServer({
            cert: fs.readFileSync('test/fixtures/certificate.pem'),
            key: fs.readFileSync('test/fixtures/key.pem')
          });
          const server = proxy(httpServer, httpsServer);

          server.listen(() => {
            const port = server.address().port;

            httpsServer.on('upgrade', (req, socket) => {
              socket.on('error', NOOP);
              socket.end(
                'HTTP/1.1 302 Found\r\n' +
                  `Location: ws://localhost:${port}/\r\n\r\n`
              );
            });

            const wss = new WebSocket.Server({ server: httpServer });

            wss.on('connection', (ws, req) => {
              assert.strictEqual(req.headers.authorization, undefined);
              ws.close();
            });

            const ws = new WebSocket(`wss://localhost:${port}`, {
              auth: 'foo:bar',
              followRedirects: true,
              rejectUnauthorized: false
            });

            assert.strictEqual(
              ws._req.getHeader('Authorization'),
              'Basic Zm9vOmJhcg=='
            );

            ws.on('close', (code) => {
              assert.strictEqual(code, 1005);
              assert.strictEqual(ws.url, `ws://localhost:${port}/`);
              assert.strictEqual(ws._redirects, 1);

              server.close(done);
            });
          });
        });

        it('drops the Authorization and Cookie headers', (done) => {
          const httpServer = http.createServer();
          const httpsServer = https.createServer({
            cert: fs.readFileSync('test/fixtures/certificate.pem'),
            key: fs.readFileSync('test/fixtures/key.pem')
          });
          const server = proxy(httpServer, httpsServer);

          server.listen(() => {
            const port = server.address().port;

            httpsServer.on('upgrade', (req, socket) => {
              socket.on('error', NOOP);
              socket.end(
                'HTTP/1.1 302 Found\r\n' +
                  `Location: ws://localhost:${port}/\r\n\r\n`
              );
            });

            const headers = {
              authorization: 'Basic Zm9vOmJhcg==',
              cookie: 'foo=bar',
              host: 'foo'
            };

            const wss = new WebSocket.Server({ server: httpServer });

            wss.on('connection', (ws, req) => {
              assert.strictEqual(req.headers.authorization, undefined);
              assert.strictEqual(req.headers.cookie, undefined);
              assert.strictEqual(req.headers.host, headers.host);

              ws.close();
            });

            const ws = new WebSocket(`wss://localhost:${port}`, {
              followRedirects: true,
              headers,
              rejectUnauthorized: false
            });

            const firstRequest = ws._req;

            assert.strictEqual(
              firstRequest.getHeader('Authorization'),
              headers.authorization
            );
            assert.strictEqual(
              firstRequest.getHeader('Cookie'),
              headers.cookie
            );
            assert.strictEqual(firstRequest.getHeader('Host'), headers.host);

            ws.on('close', (code) => {
              assert.strictEqual(code, 1005);
              assert.strictEqual(ws.url, `ws://localhost:${port}/`);
              assert.strictEqual(ws._redirects, 1);

              server.close(done);
            });
          });
        });
      });

      describe("If there is at least one 'redirect' event listener", () => {
        it('does not drop any headers by default', (done) => {
          const httpServer = http.createServer();
          const httpsServer = https.createServer({
            cert: fs.readFileSync('test/fixtures/certificate.pem'),
            key: fs.readFileSync('test/fixtures/key.pem')
          });
          const server = proxy(httpServer, httpsServer);

          server.listen(() => {
            const port = server.address().port;

            httpsServer.on('upgrade', (req, socket) => {
              socket.on('error', NOOP);
              socket.end(
                'HTTP/1.1 302 Found\r\n' +
                  `Location: ws://localhost:${port}/\r\n\r\n`
              );
            });

            const headers = {
              authorization: 'Basic Zm9vOmJhcg==',
              cookie: 'foo=bar',
              host: 'foo'
            };

            const wss = new WebSocket.Server({ server: httpServer });

            wss.on('connection', (ws, req) => {
              assert.strictEqual(
                req.headers.authorization,
                headers.authorization
              );
              assert.strictEqual(req.headers.cookie, headers.cookie);
              assert.strictEqual(req.headers.host, headers.host);

              ws.close();
            });

            const ws = new WebSocket(`wss://localhost:${port}`, {
              followRedirects: true,
              headers,
              rejectUnauthorized: false
            });

            const firstRequest = ws._req;

            assert.strictEqual(
              firstRequest.getHeader('Authorization'),
              headers.authorization
            );
            assert.strictEqual(
              firstRequest.getHeader('Cookie'),
              headers.cookie
            );
            assert.strictEqual(firstRequest.getHeader('Host'), headers.host);

            ws.on('redirect', (url, req) => {
              assert.strictEqual(ws._redirects, 1);
              assert.strictEqual(url, `ws://localhost:${port}/`);
              assert.notStrictEqual(firstRequest, req);
              assert.strictEqual(
                req.getHeader('Authorization'),
                headers.authorization
              );
              assert.strictEqual(req.getHeader('Cookie'), headers.cookie);
              assert.strictEqual(req.getHeader('Host'), headers.host);

              ws.on('close', (code) => {
                assert.strictEqual(code, 1005);
                server.close(done);
              });
            });
          });
        });
      });
    });

    describe('When the redirect host is different', () => {
      describe("If there is no 'redirect' event listener", () => {
        it('drops the `auth` option', (done) => {
          const wss = new WebSocket.Server({ port: 0 }, () => {
            const port = wss.address().port;

            server.once('upgrade', (req, socket) => {
              socket.end(
                'HTTP/1.1 302 Found\r\n' +
                  `Location: ws://localhost:${port}/\r\n\r\n`
              );
            });

            const ws = new WebSocket(
              `ws://localhost:${server.address().port}`,
              {
                auth: 'foo:bar',
                followRedirects: true
              }
            );

            assert.strictEqual(
              ws._req.getHeader('Authorization'),
              'Basic Zm9vOmJhcg=='
            );

            ws.on('close', (code) => {
              assert.strictEqual(code, 1005);
              assert.strictEqual(ws.url, `ws://localhost:${port}/`);
              assert.strictEqual(ws._redirects, 1);

              wss.close(done);
            });
          });

          wss.on('connection', (ws, req) => {
            assert.strictEqual(req.headers.authorization, undefined);
            ws.close();
          });
        });

        it('drops the Authorization, Cookie and Host headers (1/4)', (done) => {
          // Test the `ws:` to `ws:` case.

          const wss = new WebSocket.Server({ port: 0 }, () => {
            const port = wss.address().port;

            server.once('upgrade', (req, socket) => {
              socket.end(
                'HTTP/1.1 302 Found\r\n' +
                  `Location: ws://localhost:${port}/\r\n\r\n`
              );
            });

            const headers = {
              authorization: 'Basic Zm9vOmJhcg==',
              cookie: 'foo=bar',
              host: 'foo'
            };

            const ws = new WebSocket(
              `ws://localhost:${server.address().port}`,
              { followRedirects: true, headers }
            );

            const firstRequest = ws._req;

            assert.strictEqual(
              firstRequest.getHeader('Authorization'),
              headers.authorization
            );
            assert.strictEqual(
              firstRequest.getHeader('Cookie'),
              headers.cookie
            );
            assert.strictEqual(firstRequest.getHeader('Host'), headers.host);

            ws.on('close', (code) => {
              assert.strictEqual(code, 1005);
              assert.strictEqual(ws.url, `ws://localhost:${port}/`);
              assert.strictEqual(ws._redirects, 1);

              wss.close(done);
            });
          });

          wss.on('connection', (ws, req) => {
            assert.strictEqual(req.headers.authorization, undefined);
            assert.strictEqual(req.headers.cookie, undefined);
            assert.strictEqual(
              req.headers.host,
              `localhost:${wss.address().port}`
            );

            ws.close();
          });
        });

        it('drops the Authorization, Cookie and Host headers (2/4)'function (done) {
          if (process.platform === 'win32'return this.skip();

          // Test the `ws:` to `ws+unix:` case.

          const socketPath = path.join(
            os.tmpdir(),
            `ws.${crypto.randomBytes(16).toString('hex')}.sock`
          );

          server.once('upgrade', (req, socket) => {
            socket.end(
              `HTTP/1.1 302 Found\r\nLocation: ws+unix://${socketPath}\r\n\r\n`
            );
          });

          const redirectedServer = http.createServer();
          const wss = new WebSocket.Server({ server: redirectedServer });

          wss.on('connection', (ws, req) => {
            assert.strictEqual(req.headers.authorization, undefined);
            assert.strictEqual(req.headers.cookie, undefined);
            assert.strictEqual(req.headers.host, 'localhost');

            ws.close();
          });

          redirectedServer.listen(socketPath, () => {
            const headers = {
              authorization: 'Basic Zm9vOmJhcg==',
              cookie: 'foo=bar',
              host: 'foo'
            };

            const ws = new WebSocket(
              `ws://localhost:${server.address().port}`,
              { followRedirects: true, headers }
            );

            const firstRequest = ws._req;

            assert.strictEqual(
              firstRequest.getHeader('Authorization'),
              headers.authorization
            );
            assert.strictEqual(
              firstRequest.getHeader('Cookie'),
              headers.cookie
            );
            assert.strictEqual(firstRequest.getHeader('Host'), headers.host);

            ws.on('close', (code) => {
              assert.strictEqual(code, 1005);
              assert.strictEqual(ws.url, `ws+unix://${socketPath}`);
              assert.strictEqual(ws._redirects, 1);

              redirectedServer.close(done);
            });
          });
        });

        it('drops the Authorization, Cookie and Host headers (3/4)'function (done) {
          if (process.platform === 'win32'return this.skip();

          // Test the `ws+unix:` to `ws+unix:` case.

          const redirectingServerSocketPath = path.join(
            os.tmpdir(),
            `ws.${crypto.randomBytes(16).toString('hex')}.sock`
          );
          const redirectedServerSocketPath = path.join(
            os.tmpdir(),
            `ws.${crypto.randomBytes(16).toString('hex')}.sock`
          );

          const redirectingServer = http.createServer();

          redirectingServer.on('upgrade', (req, socket) => {
            socket.end(
              'HTTP/1.1 302 Found\r\n' +
                `Location: ws+unix://${redirectedServerSocketPath}\r\n\r\n`
            );
          });

          const redirectedServer = http.createServer();
          const wss = new WebSocket.Server({ server: redirectedServer });

          wss.on('connection', (ws, req) => {
            assert.strictEqual(req.headers.authorization, undefined);
            assert.strictEqual(req.headers.cookie, undefined);
            assert.strictEqual(req.headers.host, 'localhost');

            ws.close();
          });

          redirectingServer.listen(redirectingServerSocketPath, listening);
          redirectedServer.listen(redirectedServerSocketPath, listening);

          let callCount = 0;

          function listening() {
            if (++callCount !== 2) return;

            const headers = {
              authorization: 'Basic Zm9vOmJhcg==',
              cookie: 'foo=bar',
              host: 'foo'
            };

            const ws = new WebSocket(
              `ws+unix://${redirectingServerSocketPath}`,
              { followRedirects: true, headers }
            );

            const firstRequest = ws._req;

            assert.strictEqual(
              firstRequest.getHeader('Authorization'),
              headers.authorization
            );
            assert.strictEqual(
              firstRequest.getHeader('Cookie'),
              headers.cookie
            );
            assert.strictEqual(firstRequest.getHeader('Host'), headers.host);

            ws.on('close', (code) => {
              assert.strictEqual(code, 1005);
              assert.strictEqual(
                ws.url,
                `ws+unix://${redirectedServerSocketPath}`
              );
              assert.strictEqual(ws._redirects, 1);

              redirectingServer.close();
              redirectedServer.close(done);
            });
          }
        });

        it('drops the Authorization, Cookie and Host headers (4/4)'function (done) {
          if (process.platform === 'win32'return this.skip();

          // Test the `ws+unix:` to `ws:` case.

          const redirectingServer = http.createServer();
          const redirectedServer = http.createServer();
          const wss = new WebSocket.Server({ server: redirectedServer });

          wss.on('connection', (ws, req) => {
            assert.strictEqual(req.headers.authorization, undefined);
            assert.strictEqual(req.headers.cookie, undefined);
            assert.strictEqual(
              req.headers.host,
              `localhost:${redirectedServer.address().port}`
            );

            ws.close();
          });

          const socketPath = path.join(
            os.tmpdir(),
            `ws.${crypto.randomBytes(16).toString('hex')}.sock`
          );

          redirectingServer.listen(socketPath, listening);
          redirectedServer.listen(0, listening);

          let callCount = 0;

          function listening() {
            if (++callCount !== 2) return;

            const port = redirectedServer.address().port;

            redirectingServer.on('upgrade', (req, socket) => {
              socket.end(
                `HTTP/1.1 302 Found\r\nLocation: ws://localhost:${port}\r\n\r\n`
              );
            });

            const headers = {
              authorization: 'Basic Zm9vOmJhcg==',
              cookie: 'foo=bar',
              host: 'foo'
            };

            const ws = new WebSocket(`ws+unix://${socketPath}`, {
              followRedirects: true,
              headers
            });

            const firstRequest = ws._req;

            assert.strictEqual(
              firstRequest.getHeader('Authorization'),
              headers.authorization
            );
            assert.strictEqual(
              firstRequest.getHeader('Cookie'),
              headers.cookie
            );
            assert.strictEqual(firstRequest.getHeader('Host'), headers.host);

            ws.on('close', (code) => {
              assert.strictEqual(code, 1005);
              assert.strictEqual(ws.url, `ws://localhost:${port}/`);
              assert.strictEqual(ws._redirects, 1);

              redirectingServer.close();
              redirectedServer.close(done);
            });
          }
        });
      });

      describe("If there is at least one 'redirect' event listener", () => {
        it('does not drop any headers by default', (done) => {
          const headers = {
            authorization: 'Basic Zm9vOmJhcg==',
            cookie: 'foo=bar',
            host: 'foo'
          };

          const wss = new WebSocket.Server({ port: 0 }, () => {
            const port = wss.address().port;

            server.once('upgrade', (req, socket) => {
              socket.end(
                'HTTP/1.1 302 Found\r\n' +
                  `Location: ws://localhost:${port}/\r\n\r\n`
              );
            });

            const ws = new WebSocket(
              `ws://localhost:${server.address().port}`,
              { followRedirects: true, headers }
            );

            const firstRequest = ws._req;

            assert.strictEqual(
              firstRequest.getHeader('Authorization'),
              headers.authorization
            );
            assert.strictEqual(
              firstRequest.getHeader('Cookie'),
              headers.cookie
            );
            assert.strictEqual(firstRequest.getHeader('Host'), headers.host);

            ws.on('redirect', (url, req) => {
              assert.strictEqual(ws._redirects, 1);
              assert.strictEqual(url, `ws://localhost:${port}/`);
              assert.notStrictEqual(firstRequest, req);
              assert.strictEqual(
                req.getHeader('Authorization'),
                headers.authorization
              );
              assert.strictEqual(req.getHeader('Cookie'), headers.cookie);
              assert.strictEqual(req.getHeader('Host'), headers.host);

              ws.on('close', (code) => {
                assert.strictEqual(code, 1005);
                wss.close(done);
              });
            });
          });

          wss.on('connection', (ws, req) => {
            assert.strictEqual(
              req.headers.authorization,
              headers.authorization
            );
            assert.strictEqual(req.headers.cookie, headers.cookie);
            assert.strictEqual(req.headers.host, headers.host);
            ws.close();
          });
        });
      });
    });

    describe("In a listener of the 'redirect' event", () => {
      it('allows to abort the request without swallowing errors', (done) => {
        server.once('upgrade', (req, socket) => {
          socket.end('HTTP/1.1 302 Found\r\nLocation: /foo\r\n\r\n');
        });

        const port = server.address().port;
        const ws = new WebSocket(`ws://localhost:${port}`, {
          followRedirects: true
        });

        ws.on('redirect', (url, req) => {
          assert.strictEqual(ws._redirects, 1);
          assert.strictEqual(url, `ws://localhost:${port}/foo`);

          req.on('socket', () => {
            req.abort();
          });

          ws.on('error', (err) => {
            assert.ok(err instanceof Error);
            assert.strictEqual(err.message, 'socket hang up');

            ws.on('close', (code) => {
              assert.strictEqual(code, 1006);
              done();
            });
          });
        });
      });

      it('allows to remove headers', (done) => {
        const wss = new WebSocket.Server({ port: 0 }, () => {
          const port = wss.address().port;

          server.once('upgrade', (req, socket) => {
            socket.end(
              'HTTP/1.1 302 Found\r\n' +
                `Location: ws://localhost:${port}/\r\n\r\n`
            );
          });

          const headers = {
            authorization: 'Basic Zm9vOmJhcg==',
            cookie: 'foo=bar'
          };

          const ws = new WebSocket(`ws://localhost:${server.address().port}`, {
            followRedirects: true,
            headers
          });

          ws.on('redirect', (url, req) => {
            assert.strictEqual(ws._redirects, 1);
            assert.strictEqual(url, `ws://localhost:${port}/`);
            assert.strictEqual(
              req.getHeader('Authorization'),
              headers.authorization
            );
            assert.strictEqual(req.getHeader('Cookie'), headers.cookie);

            req.removeHeader('authorization');
            req.removeHeader('cookie');

            ws.on('close', (code) => {
              assert.strictEqual(code, 1005);
              wss.close(done);
            });
          });
        });

        wss.on('connection', (ws, req) => {
          assert.strictEqual(req.headers.authorization, undefined);
          assert.strictEqual(req.headers.cookie, undefined);
          ws.close();
        });
      });
    });
  });

  describe('Connection with query string', () => {
    it('connects when pathname is not null', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const port = wss.address().port;
        const ws = new WebSocket(`ws://localhost:${port}/?token=qwerty`);

        ws.on('open', () => {
          wss.close(done);
        });
      });

      wss.on('connection', (ws) => {
        ws.close();
      });
    });

    it('connects when pathname is null', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const port = wss.address().port;
        const ws = new WebSocket(`ws://localhost:${port}?token=qwerty`);

        ws.on('open', () => {
          wss.close(done);
        });
      });

      wss.on('connection', (ws) => {
        ws.close();
      });
    });
  });

  describe('#pause', () => {
    it('does nothing if `readyState` is `CONNECTING` or `CLOSED`', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        assert.strictEqual(ws.readyState, WebSocket.CONNECTING);
        assert.ok(!ws.isPaused);

        ws.pause();
        assert.ok(!ws.isPaused);

        ws.on('open', () => {
          ws.on('close', () => {
            assert.strictEqual(ws.readyState, WebSocket.CLOSED);

            ws.pause();
            assert.ok(!ws.isPaused);

            wss.close(done);
          });

          ws.close();
        });
      });
    });

    it('pauses the socket', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
      });

      wss.on('connection', (ws) => {
        assert.ok(!ws.isPaused);
        assert.ok(!ws._socket.isPaused());

        ws.pause();
        assert.ok(ws.isPaused);
        assert.ok(ws._socket.isPaused());

        ws.terminate();
        wss.close(done);
      });
    });
  });

  describe('#ping', () => {
    it('throws an error if `readyState` is `CONNECTING`', () => {
      const ws = new WebSocket('ws://localhost', {
        lookup() {}
      });

      assert.throws(
        () => ws.ping(),
        /^Error: WebSocket is not open: readyState 0 \(CONNECTING\)$/
      );

      assert.throws(
        () => ws.ping(NOOP),
        /^Error: WebSocket is not open: readyState 0 \(CONNECTING\)$/
      );
    });

    it('increases `bufferedAmount` if `readyState` is 2 or 3', (done) => {
      const ws = new WebSocket('ws://localhost', {
        lookup() {}
      });

      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(
          err.message,
          'WebSocket was closed before the connection was established'
        );

        assert.strictEqual(ws.readyState, WebSocket.CLOSING);
        assert.strictEqual(ws.bufferedAmount, 0);

        ws.ping('hi');
        assert.strictEqual(ws.bufferedAmount, 2);

        ws.ping();
        assert.strictEqual(ws.bufferedAmount, 2);

        ws.on('close', () => {
          assert.strictEqual(ws.readyState, WebSocket.CLOSED);

          ws.ping('hi');
          assert.strictEqual(ws.bufferedAmount, 4);

          ws.ping();
          assert.strictEqual(ws.bufferedAmount, 4);

          done();
        });
      });

      ws.close();
    });

    it('calls the callback w/ an error if `readyState` is 2 or 3', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
      });

      wss.on('connection', (ws) => {
        ws.close();

        assert.strictEqual(ws.bufferedAmount, 0);

        ws.ping('hi', (err) => {
          assert.ok(err instanceof Error);
          assert.strictEqual(
            err.message,
            'WebSocket is not open: readyState 2 (CLOSING)'
          );
          assert.strictEqual(ws.bufferedAmount, 2);

          ws.on('close', () => {
            ws.ping((err) => {
              assert.ok(err instanceof Error);
              assert.strictEqual(
                err.message,
                'WebSocket is not open: readyState 3 (CLOSED)'
              );
              assert.strictEqual(ws.bufferedAmount, 2);

              wss.close(done);
            });
          });
        });
      });
    });

    it('can send a ping with no data', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => {
          ws.ping(() => {
            ws.ping();
            ws.close();
          });
        });
      });

      wss.on('connection', (ws) => {
        let pings = 0;
        ws.on('ping', (data) => {
          assert.ok(Buffer.isBuffer(data));
          assert.strictEqual(data.length, 0);
          if (++pings === 2) wss.close(done);
        });
      });
    });

    it('can send a ping with data', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => {
          ws.ping('hi', () => {
            ws.ping('hi'true);
            ws.close();
          });
        });
      });

      wss.on('connection', (ws) => {
        let pings = 0;
        ws.on('ping', (message) => {
          assert.strictEqual(message.toString(), 'hi');
          if (++pings === 2) wss.close(done);
        });
      });
    });

    it('can send numbers as ping payload', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => {
          ws.ping(0);
          ws.close();
        });
      });

      wss.on('connection', (ws) => {
        ws.on('ping', (message) => {
          assert.strictEqual(message.toString(), '0');
          wss.close(done);
        });
      });
    });

    it('throws an error if the data size is greater than 125 bytes', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => {
          assert.throws(
            () => ws.ping(Buffer.alloc(126)),
            /^RangeError: The data size must not be greater than 125 bytes$/
          );

          wss.close(done);
        });
      });

      wss.on('connection', (ws) => {
        ws.close();
      });
    });
  });

  describe('#pong', () => {
    it('throws an error if `readyState` is `CONNECTING`', () => {
      const ws = new WebSocket('ws://localhost', {
        lookup() {}
      });

      assert.throws(
        () => ws.pong(),
        /^Error: WebSocket is not open: readyState 0 \(CONNECTING\)$/
      );

      assert.throws(
        () => ws.pong(NOOP),
        /^Error: WebSocket is not open: readyState 0 \(CONNECTING\)$/
      );
    });

    it('increases `bufferedAmount` if `readyState` is 2 or 3', (done) => {
      const ws = new WebSocket('ws://localhost', {
        lookup() {}
      });

      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(
          err.message,
          'WebSocket was closed before the connection was established'
        );

        assert.strictEqual(ws.readyState, WebSocket.CLOSING);
        assert.strictEqual(ws.bufferedAmount, 0);

        ws.pong('hi');
        assert.strictEqual(ws.bufferedAmount, 2);

        ws.pong();
        assert.strictEqual(ws.bufferedAmount, 2);

        ws.on('close', () => {
          assert.strictEqual(ws.readyState, WebSocket.CLOSED);

          ws.pong('hi');
          assert.strictEqual(ws.bufferedAmount, 4);

          ws.pong();
          assert.strictEqual(ws.bufferedAmount, 4);

          done();
        });
      });

      ws.close();
    });

    it('calls the callback w/ an error if `readyState` is 2 or 3', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
      });

      wss.on('connection', (ws) => {
        ws.close();

        assert.strictEqual(ws.bufferedAmount, 0);

        ws.pong('hi', (err) => {
          assert.ok(err instanceof Error);
          assert.strictEqual(
            err.message,
            'WebSocket is not open: readyState 2 (CLOSING)'
          );
          assert.strictEqual(ws.bufferedAmount, 2);

          ws.on('close', () => {
            ws.pong((err) => {
              assert.ok(err instanceof Error);
              assert.strictEqual(
                err.message,
                'WebSocket is not open: readyState 3 (CLOSED)'
              );
              assert.strictEqual(ws.bufferedAmount, 2);

              wss.close(done);
            });
          });
        });
      });
    });

    it('can send a pong with no data', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => {
          ws.pong(() => {
            ws.pong();
            ws.close();
          });
        });
      });

      wss.on('connection', (ws) => {
        let pongs = 0;
        ws.on('pong', (data) => {
          assert.ok(Buffer.isBuffer(data));
          assert.strictEqual(data.length, 0);
          if (++pongs === 2) wss.close(done);
        });
      });
    });

    it('can send a pong with data', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => {
          ws.pong('hi', () => {
            ws.pong('hi'true);
            ws.close();
          });
        });
      });

      wss.on('connection', (ws) => {
        let pongs = 0;
        ws.on('pong', (message) => {
          assert.strictEqual(message.toString(), 'hi');
          if (++pongs === 2) wss.close(done);
        });
      });
    });

    it('can send numbers as pong payload', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => {
          ws.pong(0);
          ws.close();
        });
      });

      wss.on('connection', (ws) => {
        ws.on('pong', (message) => {
          assert.strictEqual(message.toString(), '0');
          wss.close(done);
        });
      });
    });

    it('throws an error if the data size is greater than 125 bytes', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => {
          assert.throws(
            () => ws.pong(Buffer.alloc(126)),
            /^RangeError: The data size must not be greater than 125 bytes$/
          );

          wss.close(done);
        });
      });

      wss.on('connection', (ws) => {
        ws.close();
      });
    });
  });

  describe('#resume', () => {
    it('does nothing if `readyState` is `CONNECTING` or `CLOSED`', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        assert.strictEqual(ws.readyState, WebSocket.CONNECTING);
        assert.ok(!ws.isPaused);

        // Verify that no exception is thrown.
        ws.resume();

        ws.on('open', () => {
          ws.pause();
          assert.ok(ws.isPaused);

          ws.on('close', () => {
            assert.strictEqual(ws.readyState, WebSocket.CLOSED);

            ws.resume();
            assert.ok(ws.isPaused);

            wss.close(done);
          });

          ws.terminate();
        });
      });
    });

    it('resumes the socket', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
      });

      wss.on('connection', (ws) => {
        assert.ok(!ws.isPaused);
        assert.ok(!ws._socket.isPaused());

        ws.pause();
        assert.ok(ws.isPaused);
        assert.ok(ws._socket.isPaused());

        ws.resume();
        assert.ok(!ws.isPaused);
        assert.ok(!ws._socket.isPaused());

        ws.close();
        wss.close(done);
      });
    });
  });

  describe('#send', () => {
    it('throws an error if `readyState` is `CONNECTING`', () => {
      const ws = new WebSocket('ws://localhost', {
        lookup() {}
      });

      assert.throws(
        () => ws.send('hi'),
        /^Error: WebSocket is not open: readyState 0 \(CONNECTING\)$/
      );

      assert.throws(
        () => ws.send('hi', NOOP),
        /^Error: WebSocket is not open: readyState 0 \(CONNECTING\)$/
      );
    });

    it('increases `bufferedAmount` if `readyState` is 2 or 3', (done) => {
      const ws = new WebSocket('ws://localhost', {
        lookup() {}
      });

      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(
          err.message,
          'WebSocket was closed before the connection was established'
        );

        assert.strictEqual(ws.readyState, WebSocket.CLOSING);
        assert.strictEqual(ws.bufferedAmount, 0);

        ws.send('hi');
        assert.strictEqual(ws.bufferedAmount, 2);

        ws.send();
        assert.strictEqual(ws.bufferedAmount, 2);

        ws.on('close', () => {
          assert.strictEqual(ws.readyState, WebSocket.CLOSED);

          ws.send('hi');
          assert.strictEqual(ws.bufferedAmount, 4);

          ws.send();
          assert.strictEqual(ws.bufferedAmount, 4);

          done();
        });
      });

      ws.close();
    });

    it('calls the callback w/ an error if `readyState` is 2 or 3', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
      });

      wss.on('connection', (ws) => {
        ws.close();

        assert.strictEqual(ws.bufferedAmount, 0);

        ws.send('hi', (err) => {
          assert.ok(err instanceof Error);
          assert.strictEqual(
            err.message,
            'WebSocket is not open: readyState 2 (CLOSING)'
          );
          assert.strictEqual(ws.bufferedAmount, 2);

          ws.on('close', () => {
            ws.send('hi', (err) => {
              assert.ok(err instanceof Error);
              assert.strictEqual(
                err.message,
                'WebSocket is not open: readyState 3 (CLOSED)'
              );
              assert.strictEqual(ws.bufferedAmount, 4);

              wss.close(done);
            });
          });
        });
      });
    });

    it('can send a big binary message', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const array = new Float32Array(5 * 1024 * 1024);

        for (let i = 0; i < array.length; i++) {
          array[i] = i / 5;
        }

        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => ws.send(array));
        ws.on('message', (msg, isBinary) => {
          assert.deepStrictEqual(msg, Buffer.from(array.buffer));
          assert.ok(isBinary);
          wss.close(done);
        });
      });

      wss.on('connection', (ws) => {
        ws.on('message', (msg, isBinary) => {
          assert.ok(isBinary);
          ws.send(msg);
          ws.close();
        });
      });
    });

    it('can send text data', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => ws.send('hi'));
        ws.on('message', (message, isBinary) => {
          assert.deepStrictEqual(message, Buffer.from('hi'));
          assert.ok(!isBinary);
          wss.close(done);
        });
      });

      wss.on('connection', (ws) => {
        ws.on('message', (msg, isBinary) => {
          ws.send(msg, { binary: isBinary });
          ws.close();
        });
      });
    });

    it('does not override the `fin` option', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => {
          ws.send('fragment', { fin: false });
          ws.send('fragment', { fin: true });
          ws.close();
        });
      });

      wss.on('connection', (ws) => {
        ws.on('message', (msg, isBinary) => {
          assert.deepStrictEqual(msg, Buffer.from('fragmentfragment'));
          assert.ok(!isBinary);
          wss.close(done);
        });
      });
    });

    it('sends numbers as strings', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => {
          ws.send(0);
          ws.close();
        });
      });

      wss.on('connection', (ws) => {
        ws.on('message', (msg, isBinary) => {
          assert.deepStrictEqual(msg, Buffer.from('0'));
          assert.ok(!isBinary);
          wss.close(done);
        });
      });
    });

    it('can send a `TypedArray`', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const array = new Float32Array(6);

        for (let i = 0; i < array.length; ++i) {
          array[i] = i / 2;
        }

        const partial = array.subarray(2, 5);
        const buf = Buffer.from(
          partial.buffer,
          partial.byteOffset,
          partial.byteLength
        );

        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => {
          ws.send(partial);
          ws.close();
        });

        ws.on('message', (message, isBinary) => {
          assert.deepStrictEqual(message, buf);
          assert.ok(isBinary);
          wss.close(done);
        });
      });

      wss.on('connection', (ws) => {
        ws.on('message', (msg, isBinary) => {
          assert.ok(isBinary);
          ws.send(msg);
        });
      });
    });

    it('can send an `ArrayBuffer`', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const array = new Float32Array(5);

        for (let i = 0; i < array.length; ++i) {
          array[i] = i / 2;
        }

        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => {
          ws.send(array.buffer);
          ws.close();
        });

        ws.onmessage = (event) => {
          assert.ok(event.data.equals(Buffer.from(array.buffer)));
          wss.close(done);
        };
      });

      wss.on('connection', (ws) => {
        ws.on('message', (msg, isBinary) => {
          assert.ok(isBinary);
          ws.send(msg);
        });
      });
    });

    it('can send a `Buffer`', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const buf = Buffer.from('foobar');
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => {
          ws.send(buf);
          ws.close();
        });

        ws.onmessage = (event) => {
          assert.deepStrictEqual(event.data, buf);
          wss.close(done);
        };
      });

      wss.on('connection', (ws) => {
        ws.on('message', (msg, isBinary) => {
          assert.ok(isBinary);
          ws.send(msg);
        });
      });
    });

    it('calls the callback when data is written out', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => {
          ws.send('hi', (err) => {
            assert.ifError(err);
            wss.close(done);
          });
        });
      });

      wss.on('connection', (ws) => {
        ws.close();
      });
    });

    it('works when the `data` argument is falsy', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => {
          ws.send();
          ws.close();
        });
      });

      wss.on('connection', (ws) => {
        ws.on('message', (message, isBinary) => {
          assert.strictEqual(message, EMPTY_BUFFER);
          assert.ok(isBinary);
          wss.close(done);
        });
      });
    });

    it('honors the `mask` option', (done) => {
      let clientCloseEventEmitted = false;
      let serverClientCloseEventEmitted = false;

      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => ws.send('hi', { mask: false }));
        ws.on('close', (code, reason) => {
          assert.strictEqual(code, 1002);
          assert.deepStrictEqual(reason, EMPTY_BUFFER);

          clientCloseEventEmitted = true;
          if (serverClientCloseEventEmitted) wss.close(done);
        });
      });

      wss.on('connection', (ws) => {
        const chunks = [];

        ws._socket.prependListener('data', (chunk) => {
          chunks.push(chunk);
        });

        ws.on('error', (err) => {
          assert.ok(err instanceof RangeError);
          assert.strictEqual(
            err.message,
            'Invalid WebSocket frame: MASK must be set'
          );
          assert.ok(
            Buffer.concat(chunks).slice(0, 2).equals(Buffer.from('8102''hex'))
          );

          ws.on('close', (code, reason) => {
            assert.strictEqual(code, 1006);
            assert.strictEqual(reason, EMPTY_BUFFER);

            serverClientCloseEventEmitted = true;
            if (clientCloseEventEmitted) wss.close(done);
          });
        });
      });
    });
  });

  describe('#close', () => {
    it('closes the connection if called while connecting (1/3)', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => done(new Error("Unexpected 'open' event")));
        ws.on('error', (err) => {
          assert.ok(err instanceof Error);
          assert.strictEqual(
            err.message,
            'WebSocket was closed before the connection was established'
          );
          ws.on('close', () => wss.close(done));
        });
        ws.close(1001);
      });
    });

    it('closes the connection if called while connecting (2/3)', (done) => {
      const wss = new WebSocket.Server(
        {
          verifyClient: (info, cb) => setTimeout(cb, 300, true),
          port: 0
        },
        () => {
          const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

          ws.on('open', () => done(new Error("Unexpected 'open' event")));
          ws.on('error', (err) => {
            assert.ok(err instanceof Error);
            assert.strictEqual(
              err.message,
              'WebSocket was closed before the connection was established'
            );
            ws.on('close', () => wss.close(done));
          });
          setTimeout(() => ws.close(1001), 150);
        }
      );
    });

    it('closes the connection if called while connecting (3/3)', (done) => {
      const server = http.createServer();

      server.listen(0, () => {
        const ws = new WebSocket(`ws://localhost:${server.address().port}`);

        ws.on('open', () => done(new Error("Unexpected 'open' event")));
        ws.on('error', (err) => {
          assert.ok(err instanceof Error);
          assert.strictEqual(
            err.message,
            'WebSocket was closed before the connection was established'
          );
          ws.on('close', () => {
            server.close(done);
          });
        });

        ws.on('unexpected-response', (req, res) => {
          assert.strictEqual(res.statusCode, 502);

          const chunks = [];

          res.on('data', (chunk) => {
            chunks.push(chunk);
          });

          res.on('end', () => {
            assert.strictEqual(Buffer.concat(chunks).toString(), 'foo');
            ws.close();
          });
        });
      });

      server.on('upgrade', (req, socket) => {
        socket.on('end', socket.end);

        socket.write(
          `HTTP/1.1 502 ${http.STATUS_CODES[502]}\r\n` +
            'Connection: keep-alive\r\n' +
            'Content-type: text/html\r\n' +
            'Content-Length: 3\r\n' +
            '\r\n' +
            'foo'
        );
      });
    });

    it('can be called from an error listener while connecting', (done) => {
      const ws = new WebSocket('ws://localhost:1337');

      ws.on('open', () => done(new Error("Unexpected 'open' event")));
      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(err.code, 'ECONNREFUSED');
        ws.close();
        ws.on('close', () => done());
      });
    }).timeout(4000);

    it("can be called from a listener of the 'redirect' event", (done) => {
      const server = http.createServer();

      server.once('upgrade', (req, socket) => {
        socket.end('HTTP/1.1 302 Found\r\nLocation: /foo\r\n\r\n');
      });

      server.listen(() => {
        const port = server.address().port;
        const ws = new WebSocket(`ws://localhost:${port}`, {
          followRedirects: true
        });

        ws.on('open', () => {
          done(new Error("Unexpected 'open' event"));
        });

        ws.on('error', (err) => {
          assert.ok(err instanceof Error);
          assert.strictEqual(
            err.message,
            'WebSocket was closed before the connection was established'
          );

          ws.on('close', (code) => {
            assert.strictEqual(code, 1006);
            server.close(done);
          });
        });

        ws.on('redirect', () => {
          ws.close();
        });
      });
    });

    it("can be called from a listener of the 'upgrade' event", (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => done(new Error("Unexpected 'open' event")));
        ws.on('error', (err) => {
          assert.ok(err instanceof Error);
          assert.strictEqual(
            err.message,
            'WebSocket was closed before the connection was established'
          );
          ws.on('close', () => wss.close(done));
        });
        ws.on('upgrade', () => ws.close());
      });
    });

    it('sends the close status code only when necessary', (done) => {
      let sent;
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => {
          ws._socket.once('data', (data) => {
            sent = data;
          });
        });
      });

      wss.on('connection', (ws) => {
        ws._socket.once('data', (received) => {
          assert.deepStrictEqual(
            received.slice(0, 2),
            Buffer.from([0x88, 0x80])
          );
          assert.deepStrictEqual(sent, Buffer.from([0x88, 0x00]));

          ws.on('close', (code, reason) => {
            assert.strictEqual(code, 1005);
            assert.strictEqual(reason, EMPTY_BUFFER);
            wss.close(done);
          });
        });
        ws.close();
      });
    });

    it('works when close reason is not specified', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => ws.close(1000));
      });

      wss.on('connection', (ws) => {
        ws.on('close', (code, message) => {
          assert.strictEqual(code, 1000);
          assert.deepStrictEqual(message, EMPTY_BUFFER);
          wss.close(done);
        });
      });
    });

    it('works when close reason is specified', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => ws.close(1000, 'some reason'));
      });

      wss.on('connection', (ws) => {
        ws.on('close', (code, message) => {
          assert.strictEqual(code, 1000);
          assert.deepStrictEqual(message, Buffer.from('some reason'));
          wss.close(done);
        });
      });
    });

    it('permits all buffered data to be delivered', (done) => {
      const wss = new WebSocket.Server(
        {
          perMessageDeflate: { threshold: 0 },
          port: 0
        },
        () => {
          const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
          const messages = [];

          ws.on('message', (message, isBinary) => {
            assert.ok(!isBinary);
            messages.push(message.toString());
          });
          ws.on('close', (code) => {
            assert.strictEqual(code, 1005);
            assert.deepStrictEqual(messages, ['foo''bar''baz']);
            wss.close(done);
          });
        }
      );

      wss.on('connection', (ws) => {
        const callback = (err) => assert.ifError(err);

        ws.send('foo', callback);
        ws.send('bar', callback);
        ws.send('baz', callback);
        ws.close();
        ws.close();
      });
    });

    it('allows close code 1013', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('close', (code) => {
          assert.strictEqual(code, 1013);
          wss.close(done);
        });
      });

      wss.on('connection', (ws) => ws.close(1013));
    });

    it('allows close code 1014', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('close', (code) => {
          assert.strictEqual(code, 1014);
          wss.close(done);
        });
      });

      wss.on('connection', (ws) => ws.close(1014));
    });

    it('does nothing if `readyState` is `CLOSED`', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('close', (code) => {
          assert.strictEqual(code, 1005);
          assert.strictEqual(ws.readyState, WebSocket.CLOSED);
          ws.close();
          wss.close(done);
        });
      });

      wss.on('connection', (ws) => ws.close());
    });

    it('sets a timer for the closing handshake to complete', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('close', (code, reason) => {
          assert.strictEqual(code, 1000);
          assert.deepStrictEqual(reason, Buffer.from('some reason'));
          wss.close(done);
        });

        ws.on('open', () => {
          let callbackCalled = false;

          assert.strictEqual(ws._closeTimer, null);

          ws.send('foo', () => {
            callbackCalled = true;
          });

          ws.close(1000, 'some reason');

          //
          // Check that the close timer is set even if the `Sender.close()`
          // callback is not called.
          //
          assert.strictEqual(callbackCalled, false);
          assert.strictEqual(ws._closeTimer._idleTimeout, 30000);
        });
      });
    });
  });

  describe('#terminate', () => {
    it('closes the connection if called while connecting (1/2)', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => done(new Error("Unexpected 'open' event")));
        ws.on('error', (err) => {
          assert.ok(err instanceof Error);
          assert.strictEqual(
            err.message,
            'WebSocket was closed before the connection was established'
          );
          ws.on('close', () => wss.close(done));
        });
        ws.terminate();
      });
    });

    it('closes the connection if called while connecting (2/2)', (done) => {
      const wss = new WebSocket.Server(
        {
          verifyClient: (info, cb) => setTimeout(cb, 300, true),
          port: 0
        },
        () => {
          const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

          ws.on('open', () => done(new Error("Unexpected 'open' event")));
          ws.on('error', (err) => {
            assert.ok(err instanceof Error);
            assert.strictEqual(
              err.message,
              'WebSocket was closed before the connection was established'
            );
            ws.on('close', () => wss.close(done));
          });
          setTimeout(() => ws.terminate(), 150);
        }
      );
    });

    it('can be called from an error listener while connecting', (done) => {
      const ws = new WebSocket('ws://localhost:1337');

      ws.on('open', () => done(new Error("Unexpected 'open' event")));
      ws.on('error', (err) => {
        assert.ok(err instanceof Error);
        assert.strictEqual(err.code, 'ECONNREFUSED');
        ws.terminate();
        ws.on('close', () => done());
      });
    }).timeout(4000);

    it("can be called from a listener of the 'redirect' event", (done) => {
      const server = http.createServer();

      server.once('upgrade', (req, socket) => {
        socket.end('HTTP/1.1 302 Found\r\nLocation: /foo\r\n\r\n');
      });

      server.listen(() => {
        const port = server.address().port;
        const ws = new WebSocket(`ws://localhost:${port}`, {
          followRedirects: true
        });

        ws.on('open', () => {
          done(new Error("Unexpected 'open' event"));
        });

        ws.on('error', (err) => {
          assert.ok(err instanceof Error);
          assert.strictEqual(
            err.message,
            'WebSocket was closed before the connection was established'
          );

          ws.on('close', (code) => {
            assert.strictEqual(code, 1006);
            server.close(done);
          });
        });

        ws.on('redirect', () => {
          ws.terminate();
        });
      });
    });

    it("can be called from a listener of the 'upgrade' event", (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('open', () => done(new Error("Unexpected 'open' event")));
        ws.on('error', (err) => {
          assert.ok(err instanceof Error);
          assert.strictEqual(
            err.message,
            'WebSocket was closed before the connection was established'
          );
          ws.on('close', () => wss.close(done));
        });
        ws.on('upgrade', () => ws.terminate());
      });
    });

    it('does nothing if `readyState` is `CLOSED`', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('close', (code) => {
          assert.strictEqual(code, 1006);
          assert.strictEqual(ws.readyState, WebSocket.CLOSED);
          ws.terminate();
          wss.close(done);
        });
      });

      wss.on('connection', (ws) => ws.terminate());
    });
  });

  describe('WHATWG API emulation', () => {
    it('supports the `on{close,error,message,open}` attributes', () => {
      for (const property of ['onclose''onerror''onmessage''onopen']) {
        const descriptor = Object.getOwnPropertyDescriptor(
          WebSocket.prototype,
          property
        );

        assert.strictEqual(descriptor.configurable, true);
        assert.strictEqual(descriptor.enumerable, true);
        assert.ok(descriptor.get !== undefined);
        assert.ok(descriptor.set !== undefined);
      }

      const ws = new WebSocket('ws://localhost', { agent: new CustomAgent() });

      assert.strictEqual(ws.onmessage, null);
      assert.strictEqual(ws.onclose, null);
      assert.strictEqual(ws.onerror, null);
      assert.strictEqual(ws.onopen, null);

      ws.onmessage = NOOP;
      ws.onerror = NOOP;
      ws.onclose = NOOP;
      ws.onopen = NOOP;

      assert.strictEqual(ws.onmessage, NOOP);
      assert.strictEqual(ws.onclose, NOOP);
      assert.strictEqual(ws.onerror, NOOP);
      assert.strictEqual(ws.onopen, NOOP);

      ws.onmessage = 'foo';

      assert.strictEqual(ws.onmessage, null);
      assert.strictEqual(ws.listenerCount('message'), 0);
    });

    it('works like the `EventEmitter` interface', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.onmessage = (messageEvent) => {
          assert.strictEqual(messageEvent.data, 'foo');
          ws.onclose = (closeEvent) => {
            assert.strictEqual(closeEvent.wasClean, true);
            assert.strictEqual(closeEvent.code, 1005);
            assert.strictEqual(closeEvent.reason, '');
            wss.close(done);
          };
          ws.close();
        };

        ws.onopen = () => ws.send('foo');
      });

      wss.on('connection', (ws) => {
        ws.on('message', (msg, isBinary) => {
          ws.send(msg, { binary: isBinary });
        });
      });
    });

    it("doesn't return listeners added with `on`", () => {
      const ws = new WebSocket('ws://localhost', { agent: new CustomAgent() });

      ws.on('open', NOOP);

      assert.deepStrictEqual(ws.listeners('open'), [NOOP]);
      assert.strictEqual(ws.onopen, null);
    });

    it("doesn't remove listeners added with `on`", () => {
      const ws = new WebSocket('ws://localhost', { agent: new CustomAgent() });

      ws.on('close', NOOP);
      ws.onclose = NOOP;

      let listeners = ws.listeners('close');

      assert.strictEqual(listeners.length, 2);
      assert.strictEqual(listeners[0], NOOP);
      assert.strictEqual(listeners[1][kListener], NOOP);

      ws.onclose = NOOP;

      listeners = ws.listeners('close');

      assert.strictEqual(listeners.length, 2);
      assert.strictEqual(listeners[0], NOOP);
      assert.strictEqual(listeners[1][kListener], NOOP);
    });

    it('supports the `addEventListener` method', () => {
      const events = [];
      const ws = new WebSocket('ws://localhost', { agent: new CustomAgent() });

      ws.addEventListener('foo', () => {});
      assert.strictEqual(ws.listenerCount('foo'), 0);

      ws.addEventListener('open', () => {
        events.push('open');
        assert.strictEqual(ws.listenerCount('open'), 1);
      });

      assert.strictEqual(ws.listenerCount('open'), 1);

      ws.addEventListener(
        'message',
        () => {
          events.push('message');
          assert.strictEqual(ws.listenerCount('message'), 0);
        },
        { once: true }
      );

      assert.strictEqual(ws.listenerCount('message'), 1);

      ws.emit('open');
      ws.emit('message', EMPTY_BUFFER, false);

      assert.deepStrictEqual(events, ['open''message']);
    });

    it("doesn't return listeners added with `addEventListener`", () => {
      const ws = new WebSocket('ws://localhost', { agent: new CustomAgent() });

      ws.addEventListener('open', NOOP);

      const listeners = ws.listeners('open');

      assert.strictEqual(listeners.length, 1);
      assert.strictEqual(listeners[0][kListener], NOOP);

      assert.strictEqual(ws.onopen, null);
    });

    it("doesn't remove listeners added with `addEventListener`", () => {
      const ws = new WebSocket('ws://localhost', { agent: new CustomAgent() });

      ws.addEventListener('close', NOOP);
      ws.onclose = NOOP;

      let listeners = ws.listeners('close');

      assert.strictEqual(listeners.length, 2);
      assert.strictEqual(listeners[0][kListener], NOOP);
      assert.strictEqual(listeners[1][kListener], NOOP);

      ws.onclose = NOOP;

      listeners = ws.listeners('close');

      assert.strictEqual(listeners.length, 2);
      assert.strictEqual(listeners[0][kListener], NOOP);
      assert.strictEqual(listeners[1][kListener], NOOP);
    });

    it('supports the `removeEventListener` method', () => {
      const ws = new WebSocket('ws://localhost', { agent: new CustomAgent() });

      ws.addEventListener('message', NOOP);
      ws.addEventListener('open', NOOP);

      assert.strictEqual(ws.listeners('message')[0][kListener], NOOP);
      assert.strictEqual(ws.listeners('open')[0][kListener], NOOP);

      ws.removeEventListener('message', () => {});

      assert.strictEqual(ws.listeners('message')[0][kListener], NOOP);

      ws.removeEventListener('message', NOOP);
      ws.removeEventListener('open', NOOP);

      assert.strictEqual(ws.listenerCount('message'), 0);
      assert.strictEqual(ws.listenerCount('open'), 0);

      ws.addEventListener('message', NOOP, { once: true });
      ws.addEventListener('open', NOOP, { once: true });

      assert.strictEqual(ws.listeners('message')[0][kListener], NOOP);
      assert.strictEqual(ws.listeners('open')[0][kListener], NOOP);

      ws.removeEventListener('message', () => {});

      assert.strictEqual(ws.listeners('message')[0][kListener], NOOP);

      ws.removeEventListener('message', NOOP);
      ws.removeEventListener('open', NOOP);

      assert.strictEqual(ws.listenerCount('message'), 0);
      assert.strictEqual(ws.listenerCount('open'), 0);

      // Multiple listeners.
      ws.addEventListener('message', NOOP);
      ws.addEventListener('message', NOOP);

      assert.strictEqual(ws.listeners('message')[0][kListener], NOOP);
      assert.strictEqual(ws.listeners('message')[1][kListener], NOOP);

      ws.removeEventListener('message', NOOP);

      assert.strictEqual(ws.listeners('message')[0][kListener], NOOP);

      ws.removeEventListener('message', NOOP);

      assert.strictEqual(ws.listenerCount('message'), 0);

      // Listeners not added with `websocket.addEventListener()`.
      ws.on('message', NOOP);

      assert.deepStrictEqual(ws.listeners('message'), [NOOP]);

      ws.removeEventListener('message', NOOP);

      assert.deepStrictEqual(ws.listeners('message'), [NOOP]);

      ws.onclose = NOOP;

      assert.strictEqual(ws.listeners('close')[0][kListener], NOOP);

      ws.removeEventListener('close', NOOP);

      assert.strictEqual(ws.listeners('close')[0][kListener], NOOP);
    });

    it('wraps text data in a `MessageEvent`', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.addEventListener('open', () => {
          ws.send('hi');
          ws.close();
        });

        ws.addEventListener('message', (event) => {
          assert.ok(event instanceof MessageEvent);
          assert.strictEqual(event.data, 'hi');
          wss.close(done);
        });
      });

      wss.on('connection', (ws) => {
        ws.on('message', (msg, isBinary) => {
          ws.send(msg, { binary: isBinary });
        });
      });
    });

    it('receives a `CloseEvent` when server closes (1000)', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.addEventListener('close', (event) => {
          assert.ok(event instanceof CloseEvent);
          assert.ok(event.wasClean);
          assert.strictEqual(event.reason, '');
          assert.strictEqual(event.code, 1000);
          wss.close(done);
        });
      });

      wss.on('connection', (ws) => ws.close(1000));
    });

    it('receives a `CloseEvent` when server closes (4000)', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.addEventListener('close', (event) => {
          assert.ok(event instanceof CloseEvent);
          assert.ok(event.wasClean);
          assert.strictEqual(event.reason, 'some daft reason');
          assert.strictEqual(event.code, 4000);
          wss.close(done);
        });
      });

      wss.on('connection', (ws) => ws.close(4000, 'some daft reason'));
    });

    it('sets `target` and `type` on events', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const err = new Error('forced');
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.addEventListener('open', (event) => {
          assert.ok(event instanceof Event);
          assert.strictEqual(event.type, 'open');
          assert.strictEqual(event.target, ws);
        });
        ws.addEventListener('message', (event) => {
          assert.ok(event instanceof MessageEvent);
          assert.strictEqual(event.type, 'message');
          assert.strictEqual(event.target, ws);
          ws.close();
        });
        ws.addEventListener('close', (event) => {
          assert.ok(event instanceof CloseEvent);
          assert.strictEqual(event.type, 'close');
          assert.strictEqual(event.target, ws);
          ws.emit('error', err);
        });
        ws.addEventListener('error', (event) => {
          assert.ok(event instanceof ErrorEvent);
          assert.strictEqual(event.message, 'forced');
          assert.strictEqual(event.type, 'error');
          assert.strictEqual(event.target, ws);
          assert.strictEqual(event.error, err);

          wss.close(done);
        });
      });

      wss.on('connection', (client) => client.send('hi'));
    });

    it('passes binary data as a Node.js `Buffer` by default', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.onmessage = (evt) => {
          assert.ok(Buffer.isBuffer(evt.data));
          wss.close(done);
        };
      });

      wss.on('connection', (ws) => {
        ws.send(new Uint8Array(4096));
        ws.close();
      });
    });

    it('ignores `binaryType` for text messages', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.binaryType = 'arraybuffer';

        ws.onmessage = (evt) => {
          assert.strictEqual(evt.data, 'foo');
          wss.close(done);
        };
      });

      wss.on('connection', (ws) => {
        ws.send('foo');
        ws.close();
      });
    });

    it('allows to update `binaryType` on the fly', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        function testType(binaryType, next) {
          const buf = Buffer.from(binaryType);
          ws.binaryType = binaryType;

          ws.onmessage = (evt) => {
            if (binaryType === 'nodebuffer') {
              assert.ok(Buffer.isBuffer(evt.data));
              assert.ok(evt.data.equals(buf));
            } else if (binaryType === 'arraybuffer') {
              assert.ok(evt.data instanceof ArrayBuffer);
              assert.ok(Buffer.from(evt.data).equals(buf));
            } else if (binaryType === 'fragments') {
              assert.deepStrictEqual(evt.data, [buf]);
            }
            next();
          };

          ws.send(buf);
        }

        ws.onopen = () => {
          testType('nodebuffer', () => {
            testType('arraybuffer', () => {
              testType('fragments', () => {
                ws.close();
                wss.close(done);
              });
            });
          });
        };
      });

      wss.on('connection', (ws) => {
        ws.on('message', (msg, isBinary) => {
          assert.ok(isBinary);
          ws.send(msg);
        });
      });
    });
  });

  describe('SSL', () => {
    it('connects to secure websocket server', (done) => {
      const server = https.createServer({
        cert: fs.readFileSync('test/fixtures/certificate.pem'),
        key: fs.readFileSync('test/fixtures/key.pem')
      });
      const wss = new WebSocket.Server({ server });

      wss.on('connection', () => {
        server.close(done);
      });

      server.listen(0, () => {
        const ws = new WebSocket(`wss://127.0.0.1:${server.address().port}`, {
          rejectUnauthorized: false
        });

        ws.on('open', ws.close);
      });
    });

    it('connects to secure websocket server with client side certificate', (done) => {
      const server = https.createServer({
        cert: fs.readFileSync('test/fixtures/certificate.pem'),
        ca: [fs.readFileSync('test/fixtures/ca-certificate.pem')],
        key: fs.readFileSync('test/fixtures/key.pem'),
        requestCert: true
      });

      const wss = new WebSocket.Server({ noServer: true });

      server.on('upgrade', (request, socket, head) => {
        assert.ok(socket.authorized);

        wss.handleUpgrade(request, socket, head, (ws) => {
          ws.on('close', (code) => {
            assert.strictEqual(code, 1005);
            server.close(done);
          });
        });
      });

      server.listen(0, () => {
        const ws = new WebSocket(`wss://localhost:${server.address().port}`, {
          cert: fs.readFileSync('test/fixtures/client-certificate.pem'),
          key: fs.readFileSync('test/fixtures/client-key.pem'),
          rejectUnauthorized: false
        });

        ws.on('open', ws.close);
      });
    });

    it('cannot connect to secure websocket server via ws://', (done) => {
      const server = https.createServer({
        cert: fs.readFileSync('test/fixtures/certificate.pem'),
        key: fs.readFileSync('test/fixtures/key.pem')
      });
      const wss = new WebSocket.Server({ server });

      server.listen(0, () => {
        const ws = new WebSocket(`ws://localhost:${server.address().port}`, {
          rejectUnauthorized: false
        });

        ws.on('error', () => {
          server.close(done);
          wss.close();
        });
      });
    });

    it('can send and receive text data', (done) => {
      const server = https.createServer({
        cert: fs.readFileSync('test/fixtures/certificate.pem'),
        key: fs.readFileSync('test/fixtures/key.pem')
      });
      const wss = new WebSocket.Server({ server });

      wss.on('connection', (ws) => {
        ws.on('message', (message, isBinary) => {
          assert.deepStrictEqual(message, Buffer.from('foobar'));
          assert.ok(!isBinary);
          server.close(done);
        });
      });

      server.listen(0, () => {
        const ws = new WebSocket(`wss://localhost:${server.address().port}`, {
          rejectUnauthorized: false
        });

        ws.on('open', () => {
          ws.send('foobar');
          ws.close();
        });
      });
    });

    it('can send a big binary message', (done) => {
      const buf = crypto.randomBytes(5 * 1024 * 1024);
      const server = https.createServer({
        cert: fs.readFileSync('test/fixtures/certificate.pem'),
        key: fs.readFileSync('test/fixtures/key.pem')
      });
      const wss = new WebSocket.Server({ server });

      wss.on('connection', (ws) => {
        ws.on('message', (message, isBinary) => {
          assert.ok(isBinary);
          ws.send(message);
          ws.close();
        });
      });

      server.listen(0, () => {
        const ws = new WebSocket(`wss://localhost:${server.address().port}`, {
          rejectUnauthorized: false
        });

        ws.on('open', () => ws.send(buf));
        ws.on('message', (message, isBinary) => {
          assert.deepStrictEqual(message, buf);
          assert.ok(isBinary);

          server.close(done);
        });
      });
    }).timeout(4000);

    it('allows to disable sending the SNI extension', (done) => {
      const original = tls.connect;

      tls.connect = (options) => {
        assert.strictEqual(options.servername, '');
        tls.connect = original;
        done();
      };

      const ws = new WebSocket('wss://127.0.0.1', { servername: '' });
    });

    it("works around a double 'error' event bug in Node.js"function (done) {
      //
      // The `minVersion` and `maxVersion` options are not supported in
      // Node.js < 10.16.0.
      //
      if (process.versions.modules < 64) return this.skip();

      //
      // The `'error'` event can be emitted multiple times by the
      // `http.ClientRequest` object in Node.js < 13. This test reproduces the
      // issue in Node.js 12.
      //
      const server = https.createServer({
        cert: fs.readFileSync('test/fixtures/certificate.pem'),
        key: fs.readFileSync('test/fixtures/key.pem'),
        minVersion: 'TLSv1.2'
      });
      const wss = new WebSocket.Server({ server });

      server.listen(0, () => {
        const ws = new WebSocket(`wss://localhost:${server.address().port}`, {
          maxVersion: 'TLSv1.1',
          rejectUnauthorized: false
        });

        ws.on('error', (err) => {
          assert.ok(err instanceof Error);
          server.close(done);
          wss.close();
        });
      });
    });
  });

  describe('Request headers', () => {
    it('adds the authorization header if the url has userinfo', (done) => {
      const agent = new CustomAgent();
      const userinfo = 'test:testpass';

      agent.addRequest = (req) => {
        assert.strictEqual(
          req.getHeader('authorization'),
          `Basic ${Buffer.from(userinfo).toString('base64')}`
        );
        done();
      };

      const ws = new WebSocket(`ws://${userinfo}@localhost`, { agent });
    });

    it('honors the `auth` option', (done) => {
      const agent = new CustomAgent();
      const auth = 'user:pass';

      agent.addRequest = (req) => {
        assert.strictEqual(
          req.getHeader('authorization'),
          `Basic ${Buffer.from(auth).toString('base64')}`
        );
        done();
      };

      const ws = new WebSocket('ws://localhost', { agent, auth });
    });

    it('favors the url userinfo over the `auth` option', (done) => {
      const agent = new CustomAgent();
      const auth = 'foo:bar';
      const userinfo = 'baz:qux';

      agent.addRequest = (req) => {
        assert.strictEqual(
          req.getHeader('authorization'),
          `Basic ${Buffer.from(userinfo).toString('base64')}`
        );
        done();
      };

      const ws = new WebSocket(`ws://${userinfo}@localhost`, { agent, auth });
    });

    it('adds custom headers', (done) => {
      const agent = new CustomAgent();

      agent.addRequest = (req) => {
        assert.strictEqual(req.getHeader('cookie'), 'foo=bar');
        done();
      };

      const ws = new WebSocket('ws://localhost', {
        headers: { Cookie: 'foo=bar' },
        agent
      });
    });

    it('excludes default ports from host header', () => {
      const options = { lookup() {} };
      const variants = [
        ['wss://localhost:8443', 'localhost:8443'],
        ['wss://localhost:443', 'localhost'],
        ['ws://localhost:88', 'localhost:88'],
        ['ws://localhost:80', 'localhost']
      ];

      for (const [url, host] of variants) {
        const ws = new WebSocket(url, options);
        assert.strictEqual(ws._req.getHeader('host'), host);
      }
    });

    it("doesn't add the origin header by default", (done) => {
      const agent = new CustomAgent();

      agent.addRequest = (req) => {
        assert.strictEqual(req.getHeader('origin'), undefined);
        done();
      };

      const ws = new WebSocket('ws://localhost', { agent });
    });

    it('honors the `origin` option (1/2)', (done) => {
      const agent = new CustomAgent();

      agent.addRequest = (req) => {
        assert.strictEqual(req.getHeader('origin'), 'https://example.com:8000');
        done();
      };

      const ws = new WebSocket('ws://localhost', {
        origin: 'https://example.com:8000',
        agent
      });
    });

    it('honors the `origin` option (2/2)', (done) => {
      const agent = new CustomAgent();

      agent.addRequest = (req) => {
        assert.strictEqual(
          req.getHeader('sec-websocket-origin'),
          'https://example.com:8000'
        );
        done();
      };

      const ws = new WebSocket('ws://localhost', {
        origin: 'https://example.com:8000',
        protocolVersion: 8,
        agent
      });
    });
  });

  describe('permessage-deflate', () => {
    it('is enabled by default', (done) => {
      const agent = new CustomAgent();

      agent.addRequest = (req) => {
        assert.strictEqual(
          req.getHeader('sec-websocket-extensions'),
          'permessage-deflate; client_max_window_bits'
        );
        done();
      };

      const ws = new WebSocket('ws://localhost', { agent });
    });

    it('can be disabled', (done) => {
      const agent = new CustomAgent();

      agent.addRequest = (req) => {
        assert.strictEqual(
          req.getHeader('sec-websocket-extensions'),
          undefined
        );
        done();
      };

      const ws = new WebSocket('ws://localhost', {
        perMessageDeflate: false,
        agent
      });
    });

    it('can send extension parameters', (done) => {
      const agent = new CustomAgent();

      const value =
        'permessage-deflate; server_no_context_takeover;' +
        ' client_no_context_takeover; server_max_window_bits=10;' +
        ' client_max_window_bits';

      agent.addRequest = (req) => {
        assert.strictEqual(req.getHeader('sec-websocket-extensions'), value);
        done();
      };

      const ws = new WebSocket('ws://localhost', {
        perMessageDeflate: {
          clientNoContextTakeover: true,
          serverNoContextTakeover: true,
          clientMaxWindowBits: true,
          serverMaxWindowBits: 10
        },
        agent
      });
    });

    it('consumes all received data when connection is closed (1/2)', (done) => {
      const wss = new WebSocket.Server(
        {
          perMessageDeflate: { threshold: 0 },
          port: 0
        },
        () => {
          const messages = [];
          const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

          ws.on('open', () => {
            ws._socket.on('close', () => {
              assert.strictEqual(ws._receiver._state, 5);
            });
          });

          ws.on('message', (message, isBinary) => {
            assert.ok(!isBinary);
            messages.push(message.toString());
          });

          ws.on('close', (code) => {
            assert.strictEqual(code, 1006);
            assert.deepStrictEqual(messages, ['foo''bar''baz''qux']);
            wss.close(done);
          });
        }
      );

      wss.on('connection', (ws) => {
        ws.send('foo');
        ws.send('bar');
        ws.send('baz');
        ws.send('qux', () => ws._socket.end());
      });
    });

    it('consumes all received data when connection is closed (2/2)', (done) => {
      const wss = new WebSocket.Server(
        {
          perMessageDeflate: true,
          port: 0
        },
        () => {
          const messageLengths = [];
          const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

          ws.on('open', () => {
            ws._socket.prependListener('close', () => {
              assert.strictEqual(ws._receiver._state, 5);
              assert.strictEqual(ws._socket._readableState.length, 3);
            });

            const push = ws._socket.push;

            // Override `ws._socket.push()` to know exactly when data is
            // received and call `ws.terminate()` immediately after that without
            // relying on a timer.
            ws._socket.push = (data) => {
              ws._socket.push = push;
              ws._socket.push(data);
              ws.terminate();
            };

            const payload1 = Buffer.alloc(15 * 1024);
            const payload2 = Buffer.alloc(1);

            const opts = {
              fin: true,
              opcode: 0x02,
              mask: false,
              readOnly: false
            };

            const list = [
              ...Sender.frame(payload1, { rsv1: false, ...opts }),
              ...Sender.frame(payload2, { rsv1: true, ...opts })
            ];

            for (let i = 0; i < 399; i++) {
              list.push(list[list.length - 2], list[list.length - 1]);
            }

            // This hack is used because there is no guarantee that more than
            // 16 KiB will be sent as a single TCP packet.
            push.call(ws._socket, Buffer.concat(list));

            wss.clients
              .values()
              .next()
              .value.send(payload2, { compress: false });
          });

          ws.on('message', (message, isBinary) => {
            assert.ok(isBinary);
            messageLengths.push(message.length);
          });

          ws.on('close', (code) => {
            assert.strictEqual(code, 1006);
            assert.strictEqual(messageLengths.length, 402);
            assert.strictEqual(messageLengths[0], 15360);
            assert.strictEqual(messageLengths[messageLengths.length - 1], 1);
            wss.close(done);
          });
        }
      );
    });

    it('handles a close frame received while compressing data', (done) => {
      const wss = new WebSocket.Server(
        {
          perMessageDeflate: true,
          port: 0
        },
        () => {
          const ws = new WebSocket(`ws://localhost:${wss.address().port}`, {
            perMessageDeflate: { threshold: 0 }
          });

          ws.on('open', () => {
            ws._receiver.on('conclude', () => {
              assert.ok(ws._sender._deflating);
            });

            ws.send('foo');
            ws.send('bar');
            ws.send('baz');
            ws.send('qux');
          });
        }
      );

      wss.on('connection', (ws) => {
        const messages = [];

        ws.on('message', (message, isBinary) => {
          assert.ok(!isBinary);
          messages.push(message.toString());
        });

        ws.on('close', (code, reason) => {
          assert.deepStrictEqual(messages, ['foo''bar''baz''qux']);
          assert.strictEqual(code, 1000);
          assert.deepStrictEqual(reason, EMPTY_BUFFER);
          wss.close(done);
        });

        ws.close(1000);
      });
    });

    describe('#close', () => {
      it('can be used while data is being decompressed', (done) => {
        const wss = new WebSocket.Server(
          {
            perMessageDeflate: true,
            port: 0
          },
          () => {
            const messages = [];
            const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

            ws.on('open', () => {
              ws._socket.on('end', () => {
                assert.strictEqual(ws._receiver._state, 5);
              });
            });

            ws.on('message', (message, isBinary) => {
              assert.ok(!isBinary);

              if (messages.push(message.toString()) > 1) return;

              ws.close(1000);
            });

            ws.on('close', (code, reason) => {
              assert.deepStrictEqual(messages, ['''''''']);
              assert.strictEqual(code, 1000);
              assert.deepStrictEqual(reason, EMPTY_BUFFER);
              wss.close(done);
            });
          }
        );

        wss.on('connection', (ws) => {
          const buf = Buffer.from('c10100c10100c10100c10100''hex');
          ws._socket.write(buf);
        });
      });
    });

    describe('#send', () => {
      it('can send text data', (done) => {
        const wss = new WebSocket.Server(
          {
            perMessageDeflate: { threshold: 0 },
            port: 0
          },
          () => {
            const ws = new WebSocket(`ws://localhost:${wss.address().port}`, {
              perMessageDeflate: { threshold: 0 }
            });

            ws.on('open', () => {
              ws.send('hi', { compress: true });
              ws.close();
            });

            ws.on('message', (message, isBinary) => {
              assert.deepStrictEqual(message, Buffer.from('hi'));
              assert.ok(!isBinary);
              wss.close(done);
            });
          }
        );

        wss.on('connection', (ws) => {
          ws.on('message', (message, isBinary) => {
            ws.send(message, { binary: isBinary, compress: true });
          });
        });
      });

      it('can send a `TypedArray`', (done) => {
        const array = new Float32Array(5);

        for (let i = 0; i < array.length; i++) {
          array[i] = i / 2;
        }

        const wss = new WebSocket.Server(
          {
            perMessageDeflate: { threshold: 0 },
            port: 0
          },
          () => {
            const ws = new WebSocket(`ws://localhost:${wss.address().port}`, {
              perMessageDeflate: { threshold: 0 }
            });

            ws.on('open', () => {
              ws.send(array, { compress: true });
              ws.close();
            });

            ws.on('message', (message, isBinary) => {
              assert.deepStrictEqual(message, Buffer.from(array.buffer));
              assert.ok(isBinary);
              wss.close(done);
            });
          }
        );

        wss.on('connection', (ws) => {
          ws.on('message', (message, isBinary) => {
            assert.ok(isBinary);
            ws.send(message, { compress: true });
          });
        });
      });

      it('can send an `ArrayBuffer`', (done) => {
        const array = new Float32Array(5);

        for (let i = 0; i < array.length; i++) {
          array[i] = i / 2;
        }

        const wss = new WebSocket.Server(
          {
            perMessageDeflate: { threshold: 0 },
            port: 0
          },
          () => {
            const ws = new WebSocket(`ws://localhost:${wss.address().port}`, {
              perMessageDeflate: { threshold: 0 }
            });

            ws.on('open', () => {
              ws.send(array.buffer, { compress: true });
              ws.close();
            });

            ws.on('message', (message, isBinary) => {
              assert.deepStrictEqual(message, Buffer.from(array.buffer));
              assert.ok(isBinary);
              wss.close(done);
            });
          }
        );

        wss.on('connection', (ws) => {
          ws.on('message', (message, isBinary) => {
            assert.ok(isBinary);
            ws.send(message, { compress: true });
          });
        });
      });

      it('ignores the `compress` option if the extension is disabled', (done) => {
        const wss = new WebSocket.Server({ port: 0 }, () => {
          const ws = new WebSocket(`ws://localhost:${wss.address().port}`, {
            perMessageDeflate: false
          });

          ws.on('open', () => {
            ws.send('hi', { compress: true });
            ws.close();
          });

          ws.on('message', (message, isBinary) => {
            assert.deepStrictEqual(message, Buffer.from('hi'));
            assert.ok(!isBinary);
            wss.close(done);
          });
        });

        wss.on('connection', (ws) => {
          ws.on('message', (message, isBinary) => {
            ws.send(message, { binary: isBinary, compress: true });
          });
        });
      });

      it('calls the callback if the socket is closed prematurely', (done) => {
        const called = [];
        const wss = new WebSocket.Server(
          { perMessageDeflate: true, port: 0 },
          () => {
            const ws = new WebSocket(`ws://localhost:${wss.address().port}`, {
              perMessageDeflate: { threshold: 0 }
            });

            ws.on('open', () => {
              ws.send('foo');
              ws.send('bar', (err) => {
                called.push(1);

                assert.strictEqual(ws.readyState, WebSocket.CLOSING);
                assert.ok(err instanceof Error);
                assert.strictEqual(
                  err.message,
                  'The socket was closed while data was being compressed'
                );
              });
              ws.send('baz');
              ws.send('qux', (err) => {
                called.push(2);

                assert.strictEqual(ws.readyState, WebSocket.CLOSING);
                assert.ok(err instanceof Error);
                assert.strictEqual(
                  err.message,
                  'The socket was closed while data was being compressed'
                );
              });
            });
          }
        );

        wss.on('connection', (ws) => {
          ws.on('close', () => {
            assert.deepStrictEqual(called, [1, 2]);
            wss.close(done);
          });

          ws._socket.end();
        });
      });
    });

    describe('#terminate', () => {
      it('can be used while data is being compressed', (done) => {
        const wss = new WebSocket.Server(
          {
            perMessageDeflate: { threshold: 0 },
            port: 0
          },
          () => {
            const ws = new WebSocket(`ws://localhost:${wss.address().port}`, {
              perMessageDeflate: { threshold: 0 }
            });

            ws.on('open', () => {
              ws.send('hi', (err) => {
                assert.strictEqual(ws.readyState, WebSocket.CLOSING);
                assert.ok(err instanceof Error);
                assert.strictEqual(
                  err.message,
                  'The socket was closed while data was being compressed'
                );

                ws.on('close', () => {
                  wss.close(done);
                });
              });
              ws.terminate();
            });
          }
        );
      });

      it('can be used while data is being decompressed', (done) => {
        const wss = new WebSocket.Server(
          {
            perMessageDeflate: true,
            port: 0
          },
          () => {
            const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
            const messages = [];

            ws.on('message', (message, isBinary) => {
              assert.ok(!isBinary);

              if (messages.push(message.toString()) > 1) return;

              process.nextTick(() => {
                assert.strictEqual(ws._receiver._state, 5);
                ws.terminate();
              });
            });

            ws.on('close', (code, reason) => {
              assert.deepStrictEqual(messages, ['''''''']);
              assert.strictEqual(code, 1006);
              assert.strictEqual(reason, EMPTY_BUFFER);
              wss.close(done);
            });
          }
        );

        wss.on('connection', (ws) => {
          const buf = Buffer.from('c10100c10100c10100c10100''hex');
          ws._socket.write(buf);
        });
      });
    });
  });

  describe('Connection close', () => {
    it('closes cleanly after simultaneous errors (1/2)', (done) => {
      let clientCloseEventEmitted = false;
      let serverClientCloseEventEmitted = false;

      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('error', (err) => {
          assert.ok(err instanceof RangeError);
          assert.strictEqual(err.code, 'WS_ERR_INVALID_OPCODE');
          assert.strictEqual(
            err.message,
            'Invalid WebSocket frame: invalid opcode 5'
          );

          ws.on('close', (code, reason) => {
            assert.strictEqual(code, 1006);
            assert.strictEqual(reason, EMPTY_BUFFER);

            clientCloseEventEmitted = true;
            if (serverClientCloseEventEmitted) wss.close(done);
          });
        });

        ws.on('open', () => {
          // Write an invalid frame in both directions to trigger simultaneous
          // failure.
          const chunk = Buffer.from([0x85, 0x00]);

          wss.clients.values().next().value._socket.write(chunk);
          ws._socket.write(chunk);
        });
      });

      wss.on('connection', (ws) => {
        ws.on('error', (err) => {
          assert.ok(err instanceof RangeError);
          assert.strictEqual(err.code, 'WS_ERR_INVALID_OPCODE');
          assert.strictEqual(
            err.message,
            'Invalid WebSocket frame: invalid opcode 5'
          );

          ws.on('close', (code, reason) => {
            assert.strictEqual(code, 1006);
            assert.strictEqual(reason, EMPTY_BUFFER);

            serverClientCloseEventEmitted = true;
            if (clientCloseEventEmitted) wss.close(done);
          });
        });
      });
    });

    it('closes cleanly after simultaneous errors (2/2)', (done) => {
      let clientCloseEventEmitted = false;
      let serverClientCloseEventEmitted = false;

      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);

        ws.on('error', (err) => {
          assert.ok(err instanceof RangeError);
          assert.strictEqual(err.code, 'WS_ERR_INVALID_OPCODE');
          assert.strictEqual(
            err.message,
            'Invalid WebSocket frame: invalid opcode 5'
          );

          ws.on('close', (code, reason) => {
            assert.strictEqual(code, 1006);
            assert.strictEqual(reason, EMPTY_BUFFER);

            clientCloseEventEmitted = true;
            if (serverClientCloseEventEmitted) wss.close(done);
          });
        });

        ws.on('open', () => {
          // Write an invalid frame in both directions and change the
          // `readyState` to `WebSocket.CLOSING`.
          const chunk = Buffer.from([0x85, 0x00]);
          const serverWs = wss.clients.values().next().value;

          serverWs._socket.write(chunk);
          serverWs.close();

          ws._socket.write(chunk);
          ws.close();
        });
      });

      wss.on('connection', (ws) => {
        ws.on('error', (err) => {
          assert.ok(err instanceof RangeError);
          assert.strictEqual(err.code, 'WS_ERR_INVALID_OPCODE');
          assert.strictEqual(
            err.message,
            'Invalid WebSocket frame: invalid opcode 5'
          );

          ws.on('close', (code, reason) => {
            assert.strictEqual(code, 1006);
            assert.strictEqual(reason, EMPTY_BUFFER);

            serverClientCloseEventEmitted = true;
            if (clientCloseEventEmitted) wss.close(done);
          });
        });
      });
    });

    it('resumes the socket when an error occurs', (done) => {
      const maxPayload = 16 * 1024;
      const wss = new WebSocket.Server({ maxPayload, port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
      });

      wss.on('connection', (ws) => {
        const list = [
          ...Sender.frame(Buffer.alloc(maxPayload + 1), {
            fin: true,
            opcode: 0x02,
            mask: true,
            readOnly: false
          })
        ];

        ws.on('error', (err) => {
          assert.ok(err instanceof RangeError);
          assert.strictEqual(err.code, 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH');
          assert.strictEqual(err.message, 'Max payload size exceeded');

          ws.on('close', (code, reason) => {
            assert.strictEqual(code, 1006);
            assert.strictEqual(reason, EMPTY_BUFFER);
            wss.close(done);
          });
        });

        ws._socket.push(Buffer.concat(list));
      });
    });

    it('resumes the socket when the close frame is received', (done) => {
      const wss = new WebSocket.Server({ port: 0 }, () => {
        const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
      });

      wss.on('connection', (ws) => {
        const opts = { fin: true, mask: true, readOnly: false };
        const list = [
          ...Sender.frame(Buffer.alloc(16 * 1024), { opcode: 0x02, ...opts }),
          ...Sender.frame(EMPTY_BUFFER, { opcode: 0x08, ...opts })
        ];

        ws.on('close', (code, reason) => {
          assert.strictEqual(code, 1005);
          assert.strictEqual(reason, EMPTY_BUFFER);
          wss.close(done);
        });

        ws._socket.push(Buffer.concat(list));
      });
    });
  });
});

Messung V0.5 in Prozent
C=88 H=92 G=89

¤ Dauer der Verarbeitung: 0.81 Sekunden  (vorverarbeitet am  2026-04-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge