tor-browser

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

permessage-deflate.test.js (21871B)


      1 'use strict';
      2 
      3 const assert = require('assert');
      4 
      5 const PerMessageDeflate = require('../lib/permessage-deflate');
      6 const extension = require('../lib/extension');
      7 
      8 describe('PerMessageDeflate', () => {
      9  describe('#offer', () => {
     10    it('creates an offer', () => {
     11      const perMessageDeflate = new PerMessageDeflate();
     12 
     13      assert.deepStrictEqual(perMessageDeflate.offer(), {
     14        client_max_window_bits: true
     15      });
     16    });
     17 
     18    it('uses the configuration options', () => {
     19      const perMessageDeflate = new PerMessageDeflate({
     20        serverNoContextTakeover: true,
     21        clientNoContextTakeover: true,
     22        serverMaxWindowBits: 10,
     23        clientMaxWindowBits: 11
     24      });
     25 
     26      assert.deepStrictEqual(perMessageDeflate.offer(), {
     27        server_no_context_takeover: true,
     28        client_no_context_takeover: true,
     29        server_max_window_bits: 10,
     30        client_max_window_bits: 11
     31      });
     32    });
     33  });
     34 
     35  describe('#accept', () => {
     36    it('throws an error if a parameter has multiple values', () => {
     37      const perMessageDeflate = new PerMessageDeflate();
     38      const extensions = extension.parse(
     39        'permessage-deflate; server_no_context_takeover; server_no_context_takeover'
     40      );
     41 
     42      assert.throws(
     43        () => perMessageDeflate.accept(extensions['permessage-deflate']),
     44        /^Error: Parameter "server_no_context_takeover" must have only a single value$/
     45      );
     46    });
     47 
     48    it('throws an error if a parameter has an invalid name', () => {
     49      const perMessageDeflate = new PerMessageDeflate();
     50      const extensions = extension.parse('permessage-deflate;foo');
     51 
     52      assert.throws(
     53        () => perMessageDeflate.accept(extensions['permessage-deflate']),
     54        /^Error: Unknown parameter "foo"$/
     55      );
     56    });
     57 
     58    it('throws an error if client_no_context_takeover has a value', () => {
     59      const perMessageDeflate = new PerMessageDeflate();
     60      const extensions = extension.parse(
     61        'permessage-deflate; client_no_context_takeover=10'
     62      );
     63 
     64      assert.throws(
     65        () => perMessageDeflate.accept(extensions['permessage-deflate']),
     66        /^TypeError: Invalid value for parameter "client_no_context_takeover": 10$/
     67      );
     68    });
     69 
     70    it('throws an error if server_no_context_takeover has a value', () => {
     71      const perMessageDeflate = new PerMessageDeflate();
     72      const extensions = extension.parse(
     73        'permessage-deflate; server_no_context_takeover=10'
     74      );
     75 
     76      assert.throws(
     77        () => perMessageDeflate.accept(extensions['permessage-deflate']),
     78        /^TypeError: Invalid value for parameter "server_no_context_takeover": 10$/
     79      );
     80    });
     81 
     82    it('throws an error if server_max_window_bits has an invalid value', () => {
     83      const perMessageDeflate = new PerMessageDeflate();
     84 
     85      let extensions = extension.parse(
     86        'permessage-deflate; server_max_window_bits=7'
     87      );
     88      assert.throws(
     89        () => perMessageDeflate.accept(extensions['permessage-deflate']),
     90        /^TypeError: Invalid value for parameter "server_max_window_bits": 7$/
     91      );
     92 
     93      extensions = extension.parse(
     94        'permessage-deflate; server_max_window_bits'
     95      );
     96      assert.throws(
     97        () => perMessageDeflate.accept(extensions['permessage-deflate']),
     98        /^TypeError: Invalid value for parameter "server_max_window_bits": true$/
     99      );
    100    });
    101 
    102    describe('As server', () => {
    103      it('accepts an offer with no parameters', () => {
    104        const perMessageDeflate = new PerMessageDeflate({}, true);
    105 
    106        assert.deepStrictEqual(perMessageDeflate.accept([{}]), {});
    107      });
    108 
    109      it('accepts an offer with parameters', () => {
    110        const perMessageDeflate = new PerMessageDeflate({}, true);
    111        const extensions = extension.parse(
    112          'permessage-deflate; server_no_context_takeover; ' +
    113            'client_no_context_takeover; server_max_window_bits=10; ' +
    114            'client_max_window_bits=11'
    115        );
    116 
    117        assert.deepStrictEqual(
    118          perMessageDeflate.accept(extensions['permessage-deflate']),
    119          {
    120            server_no_context_takeover: true,
    121            client_no_context_takeover: true,
    122            server_max_window_bits: 10,
    123            client_max_window_bits: 11,
    124            __proto__: null
    125          }
    126        );
    127      });
    128 
    129      it('prefers the configuration options', () => {
    130        const perMessageDeflate = new PerMessageDeflate(
    131          {
    132            serverNoContextTakeover: true,
    133            clientNoContextTakeover: true,
    134            serverMaxWindowBits: 12,
    135            clientMaxWindowBits: 11
    136          },
    137          true
    138        );
    139        const extensions = extension.parse(
    140          'permessage-deflate; server_max_window_bits=14; client_max_window_bits=13'
    141        );
    142 
    143        assert.deepStrictEqual(
    144          perMessageDeflate.accept(extensions['permessage-deflate']),
    145          {
    146            server_no_context_takeover: true,
    147            client_no_context_takeover: true,
    148            server_max_window_bits: 12,
    149            client_max_window_bits: 11,
    150            __proto__: null
    151          }
    152        );
    153      });
    154 
    155      it('accepts the first supported offer', () => {
    156        const perMessageDeflate = new PerMessageDeflate(
    157          { serverMaxWindowBits: 11 },
    158          true
    159        );
    160        const extensions = extension.parse(
    161          'permessage-deflate; server_max_window_bits=10, permessage-deflate'
    162        );
    163 
    164        assert.deepStrictEqual(
    165          perMessageDeflate.accept(extensions['permessage-deflate']),
    166          {
    167            server_max_window_bits: 11,
    168            __proto__: null
    169          }
    170        );
    171      });
    172 
    173      it('throws an error if server_no_context_takeover is unsupported', () => {
    174        const perMessageDeflate = new PerMessageDeflate(
    175          { serverNoContextTakeover: false },
    176          true
    177        );
    178        const extensions = extension.parse(
    179          'permessage-deflate; server_no_context_takeover'
    180        );
    181 
    182        assert.throws(
    183          () => perMessageDeflate.accept(extensions['permessage-deflate']),
    184          /^Error: None of the extension offers can be accepted$/
    185        );
    186      });
    187 
    188      it('throws an error if server_max_window_bits is unsupported', () => {
    189        const perMessageDeflate = new PerMessageDeflate(
    190          { serverMaxWindowBits: false },
    191          true
    192        );
    193        const extensions = extension.parse(
    194          'permessage-deflate; server_max_window_bits=10'
    195        );
    196 
    197        assert.throws(
    198          () => perMessageDeflate.accept(extensions['permessage-deflate']),
    199          /^Error: None of the extension offers can be accepted$/
    200        );
    201      });
    202 
    203      it('throws an error if server_max_window_bits is less than configuration', () => {
    204        const perMessageDeflate = new PerMessageDeflate(
    205          { serverMaxWindowBits: 11 },
    206          true
    207        );
    208        const extensions = extension.parse(
    209          'permessage-deflate; server_max_window_bits=10'
    210        );
    211 
    212        assert.throws(
    213          () => perMessageDeflate.accept(extensions['permessage-deflate']),
    214          /^Error: None of the extension offers can be accepted$/
    215        );
    216      });
    217 
    218      it('throws an error if client_max_window_bits is unsupported on client', () => {
    219        const perMessageDeflate = new PerMessageDeflate(
    220          { clientMaxWindowBits: 10 },
    221          true
    222        );
    223        const extensions = extension.parse('permessage-deflate');
    224 
    225        assert.throws(
    226          () => perMessageDeflate.accept(extensions['permessage-deflate']),
    227          /^Error: None of the extension offers can be accepted$/
    228        );
    229      });
    230 
    231      it('throws an error if client_max_window_bits has an invalid value', () => {
    232        const perMessageDeflate = new PerMessageDeflate({}, true);
    233 
    234        const extensions = extension.parse(
    235          'permessage-deflate; client_max_window_bits=16'
    236        );
    237        assert.throws(
    238          () => perMessageDeflate.accept(extensions['permessage-deflate']),
    239          /^TypeError: Invalid value for parameter "client_max_window_bits": 16$/
    240        );
    241      });
    242    });
    243 
    244    describe('As client', () => {
    245      it('accepts a response with no parameters', () => {
    246        const perMessageDeflate = new PerMessageDeflate({});
    247 
    248        assert.deepStrictEqual(perMessageDeflate.accept([{}]), {});
    249      });
    250 
    251      it('accepts a response with parameters', () => {
    252        const perMessageDeflate = new PerMessageDeflate({});
    253        const extensions = extension.parse(
    254          'permessage-deflate; server_no_context_takeover; ' +
    255            'client_no_context_takeover; server_max_window_bits=10; ' +
    256            'client_max_window_bits=11'
    257        );
    258 
    259        assert.deepStrictEqual(
    260          perMessageDeflate.accept(extensions['permessage-deflate']),
    261          {
    262            server_no_context_takeover: true,
    263            client_no_context_takeover: true,
    264            server_max_window_bits: 10,
    265            client_max_window_bits: 11,
    266            __proto__: null
    267          }
    268        );
    269      });
    270 
    271      it('throws an error if client_no_context_takeover is unsupported', () => {
    272        const perMessageDeflate = new PerMessageDeflate({
    273          clientNoContextTakeover: false
    274        });
    275        const extensions = extension.parse(
    276          'permessage-deflate; client_no_context_takeover'
    277        );
    278 
    279        assert.throws(
    280          () => perMessageDeflate.accept(extensions['permessage-deflate']),
    281          /^Error: Unexpected parameter "client_no_context_takeover"$/
    282        );
    283      });
    284 
    285      it('throws an error if client_max_window_bits is unsupported', () => {
    286        const perMessageDeflate = new PerMessageDeflate({
    287          clientMaxWindowBits: false
    288        });
    289        const extensions = extension.parse(
    290          'permessage-deflate; client_max_window_bits=10'
    291        );
    292 
    293        assert.throws(
    294          () => perMessageDeflate.accept(extensions['permessage-deflate']),
    295          /^Error: Unexpected or invalid parameter "client_max_window_bits"$/
    296        );
    297      });
    298 
    299      it('throws an error if client_max_window_bits is greater than configuration', () => {
    300        const perMessageDeflate = new PerMessageDeflate({
    301          clientMaxWindowBits: 10
    302        });
    303        const extensions = extension.parse(
    304          'permessage-deflate; client_max_window_bits=11'
    305        );
    306 
    307        assert.throws(
    308          () => perMessageDeflate.accept(extensions['permessage-deflate']),
    309          /^Error: Unexpected or invalid parameter "client_max_window_bits"$/
    310        );
    311      });
    312 
    313      it('throws an error if client_max_window_bits has an invalid value', () => {
    314        const perMessageDeflate = new PerMessageDeflate();
    315 
    316        let extensions = extension.parse(
    317          'permessage-deflate; client_max_window_bits=16'
    318        );
    319        assert.throws(
    320          () => perMessageDeflate.accept(extensions['permessage-deflate']),
    321          /^TypeError: Invalid value for parameter "client_max_window_bits": 16$/
    322        );
    323 
    324        extensions = extension.parse(
    325          'permessage-deflate; client_max_window_bits'
    326        );
    327        assert.throws(
    328          () => perMessageDeflate.accept(extensions['permessage-deflate']),
    329          /^TypeError: Invalid value for parameter "client_max_window_bits": true$/
    330        );
    331      });
    332 
    333      it('uses the config value if client_max_window_bits is not specified', () => {
    334        const perMessageDeflate = new PerMessageDeflate({
    335          clientMaxWindowBits: 10
    336        });
    337 
    338        assert.deepStrictEqual(perMessageDeflate.accept([{}]), {
    339          client_max_window_bits: 10
    340        });
    341      });
    342    });
    343  });
    344 
    345  describe('#compress and #decompress', () => {
    346    it('works with unfragmented messages', (done) => {
    347      const perMessageDeflate = new PerMessageDeflate();
    348      const buf = Buffer.from([1, 2, 3]);
    349 
    350      perMessageDeflate.accept([{}]);
    351      perMessageDeflate.compress(buf, true, (err, data) => {
    352        if (err) return done(err);
    353 
    354        perMessageDeflate.decompress(data, true, (err, data) => {
    355          if (err) return done(err);
    356 
    357          assert.ok(data.equals(buf));
    358          done();
    359        });
    360      });
    361    });
    362 
    363    it('works with fragmented messages', (done) => {
    364      const perMessageDeflate = new PerMessageDeflate();
    365      const buf = Buffer.from([1, 2, 3, 4]);
    366 
    367      perMessageDeflate.accept([{}]);
    368 
    369      perMessageDeflate.compress(buf.slice(0, 2), false, (err, compressed1) => {
    370        if (err) return done(err);
    371 
    372        perMessageDeflate.compress(buf.slice(2), true, (err, compressed2) => {
    373          if (err) return done(err);
    374 
    375          perMessageDeflate.decompress(compressed1, false, (err, data1) => {
    376            if (err) return done(err);
    377 
    378            perMessageDeflate.decompress(compressed2, true, (err, data2) => {
    379              if (err) return done(err);
    380 
    381              assert.ok(Buffer.concat([data1, data2]).equals(buf));
    382              done();
    383            });
    384          });
    385        });
    386      });
    387    });
    388 
    389    it('works with the negotiated parameters', (done) => {
    390      const perMessageDeflate = new PerMessageDeflate({
    391        memLevel: 5,
    392        level: 9
    393      });
    394      const extensions = extension.parse(
    395        'permessage-deflate; server_no_context_takeover; ' +
    396          'client_no_context_takeover; server_max_window_bits=10; ' +
    397          'client_max_window_bits=11'
    398      );
    399      const buf = Buffer.from("Some compressible data, it's compressible.");
    400 
    401      perMessageDeflate.accept(extensions['permessage-deflate']);
    402 
    403      perMessageDeflate.compress(buf, true, (err, data) => {
    404        if (err) return done(err);
    405 
    406        perMessageDeflate.decompress(data, true, (err, data) => {
    407          if (err) return done(err);
    408 
    409          assert.ok(data.equals(buf));
    410          done();
    411        });
    412      });
    413    });
    414 
    415    it('honors the `level` option', (done) => {
    416      const lev0 = new PerMessageDeflate({
    417        zlibDeflateOptions: { level: 0 }
    418      });
    419      const lev9 = new PerMessageDeflate({
    420        zlibDeflateOptions: { level: 9 }
    421      });
    422      const extensionStr =
    423        'permessage-deflate; server_no_context_takeover; ' +
    424        'client_no_context_takeover; server_max_window_bits=10; ' +
    425        'client_max_window_bits=11';
    426      const buf = Buffer.from("Some compressible data, it's compressible.");
    427 
    428      lev0.accept(extension.parse(extensionStr)['permessage-deflate']);
    429      lev9.accept(extension.parse(extensionStr)['permessage-deflate']);
    430 
    431      lev0.compress(buf, true, (err, compressed1) => {
    432        if (err) return done(err);
    433 
    434        lev0.decompress(compressed1, true, (err, decompressed1) => {
    435          if (err) return done(err);
    436 
    437          lev9.compress(buf, true, (err, compressed2) => {
    438            if (err) return done(err);
    439 
    440            lev9.decompress(compressed2, true, (err, decompressed2) => {
    441              if (err) return done(err);
    442 
    443              // Level 0 compression actually adds a few bytes due to headers.
    444              assert.ok(compressed1.length > buf.length);
    445              // Level 9 should not, of course.
    446              assert.ok(compressed2.length < buf.length);
    447              // Ensure they both decompress back properly.
    448              assert.ok(decompressed1.equals(buf));
    449              assert.ok(decompressed2.equals(buf));
    450              done();
    451            });
    452          });
    453        });
    454      });
    455    });
    456 
    457    it('honors the `zlib{Deflate,Inflate}Options` option', (done) => {
    458      const lev0 = new PerMessageDeflate({
    459        zlibDeflateOptions: {
    460          level: 0,
    461          chunkSize: 256
    462        },
    463        zlibInflateOptions: {
    464          chunkSize: 2048
    465        }
    466      });
    467      const lev9 = new PerMessageDeflate({
    468        zlibDeflateOptions: {
    469          level: 9,
    470          chunkSize: 128
    471        },
    472        zlibInflateOptions: {
    473          chunkSize: 1024
    474        }
    475      });
    476 
    477      // Note no context takeover so we can get a hold of the raw streams after
    478      // we do the dance.
    479      const extensionStr =
    480        'permessage-deflate; server_max_window_bits=10; ' +
    481        'client_max_window_bits=11';
    482      const buf = Buffer.from("Some compressible data, it's compressible.");
    483 
    484      lev0.accept(extension.parse(extensionStr)['permessage-deflate']);
    485      lev9.accept(extension.parse(extensionStr)['permessage-deflate']);
    486 
    487      lev0.compress(buf, true, (err, compressed1) => {
    488        if (err) return done(err);
    489 
    490        lev0.decompress(compressed1, true, (err, decompressed1) => {
    491          if (err) return done(err);
    492 
    493          lev9.compress(buf, true, (err, compressed2) => {
    494            if (err) return done(err);
    495 
    496            lev9.decompress(compressed2, true, (err, decompressed2) => {
    497              if (err) return done(err);
    498              // Level 0 compression actually adds a few bytes due to headers.
    499              assert.ok(compressed1.length > buf.length);
    500              // Level 9 should not, of course.
    501              assert.ok(compressed2.length < buf.length);
    502              // Ensure they both decompress back properly.
    503              assert.ok(decompressed1.equals(buf));
    504              assert.ok(decompressed2.equals(buf));
    505 
    506              // Assert options were set.
    507              assert.ok(lev0._deflate._level === 0);
    508              assert.ok(lev9._deflate._level === 9);
    509              assert.ok(lev0._deflate._chunkSize === 256);
    510              assert.ok(lev9._deflate._chunkSize === 128);
    511              assert.ok(lev0._inflate._chunkSize === 2048);
    512              assert.ok(lev9._inflate._chunkSize === 1024);
    513              done();
    514            });
    515          });
    516        });
    517      });
    518    });
    519 
    520    it("doesn't use contex takeover if not allowed", (done) => {
    521      const perMessageDeflate = new PerMessageDeflate({}, true);
    522      const extensions = extension.parse(
    523        'permessage-deflate;server_no_context_takeover'
    524      );
    525      const buf = Buffer.from('foofoo');
    526 
    527      perMessageDeflate.accept(extensions['permessage-deflate']);
    528 
    529      perMessageDeflate.compress(buf, true, (err, compressed1) => {
    530        if (err) return done(err);
    531 
    532        perMessageDeflate.decompress(compressed1, true, (err, data) => {
    533          if (err) return done(err);
    534 
    535          assert.ok(data.equals(buf));
    536          perMessageDeflate.compress(data, true, (err, compressed2) => {
    537            if (err) return done(err);
    538 
    539            assert.strictEqual(compressed2.length, compressed1.length);
    540            perMessageDeflate.decompress(compressed2, true, (err, data) => {
    541              if (err) return done(err);
    542 
    543              assert.ok(data.equals(buf));
    544              done();
    545            });
    546          });
    547        });
    548      });
    549    });
    550 
    551    it('uses contex takeover if allowed', (done) => {
    552      const perMessageDeflate = new PerMessageDeflate({}, true);
    553      const extensions = extension.parse('permessage-deflate');
    554      const buf = Buffer.from('foofoo');
    555 
    556      perMessageDeflate.accept(extensions['permessage-deflate']);
    557 
    558      perMessageDeflate.compress(buf, true, (err, compressed1) => {
    559        if (err) return done(err);
    560 
    561        perMessageDeflate.decompress(compressed1, true, (err, data) => {
    562          if (err) return done(err);
    563 
    564          assert.ok(data.equals(buf));
    565          perMessageDeflate.compress(data, true, (err, compressed2) => {
    566            if (err) return done(err);
    567 
    568            assert.ok(compressed2.length < compressed1.length);
    569            perMessageDeflate.decompress(compressed2, true, (err, data) => {
    570              if (err) return done(err);
    571 
    572              assert.ok(data.equals(buf));
    573              done();
    574            });
    575          });
    576        });
    577      });
    578    });
    579 
    580    it('calls the callback when an error occurs (inflate)', (done) => {
    581      const perMessageDeflate = new PerMessageDeflate();
    582      const data = Buffer.from('something invalid');
    583 
    584      perMessageDeflate.accept([{}]);
    585      perMessageDeflate.decompress(data, true, (err) => {
    586        assert.ok(err instanceof Error);
    587        assert.strictEqual(err.code, 'Z_DATA_ERROR');
    588        assert.strictEqual(err.errno, -3);
    589        done();
    590      });
    591    });
    592 
    593    it("doesn't call the callback twice when `maxPayload` is exceeded", (done) => {
    594      const perMessageDeflate = new PerMessageDeflate({}, false, 25);
    595      const buf = Buffer.from('A'.repeat(50));
    596 
    597      perMessageDeflate.accept([{}]);
    598      perMessageDeflate.compress(buf, true, (err, data) => {
    599        if (err) return done(err);
    600 
    601        perMessageDeflate.decompress(data, true, (err) => {
    602          assert.ok(err instanceof RangeError);
    603          assert.strictEqual(err.message, 'Max payload size exceeded');
    604          done();
    605        });
    606      });
    607    });
    608 
    609    it('calls the callback if the deflate stream is closed prematurely', (done) => {
    610      const perMessageDeflate = new PerMessageDeflate();
    611      const buf = Buffer.from('A'.repeat(50));
    612 
    613      perMessageDeflate.accept([{}]);
    614      perMessageDeflate.compress(buf, true, (err) => {
    615        assert.ok(err instanceof Error);
    616        assert.strictEqual(
    617          err.message,
    618          'The deflate stream was closed while data was being processed'
    619        );
    620        done();
    621      });
    622 
    623      process.nextTick(() => perMessageDeflate.cleanup());
    624    });
    625 
    626    it('recreates the inflate stream if it ends', (done) => {
    627      const perMessageDeflate = new PerMessageDeflate();
    628      const extensions = extension.parse(
    629        'permessage-deflate; client_no_context_takeover; ' +
    630          'server_no_context_takeover'
    631      );
    632      const buf = Buffer.from('33343236313533b7000000', 'hex');
    633      const expected = Buffer.from('12345678');
    634 
    635      perMessageDeflate.accept(extensions['permessage-deflate']);
    636 
    637      perMessageDeflate.decompress(buf, true, (err, data) => {
    638        assert.ok(data.equals(expected));
    639 
    640        perMessageDeflate.decompress(buf, true, (err, data) => {
    641          assert.ok(data.equals(expected));
    642          done();
    643        });
    644      });
    645    });
    646  });
    647 });