| // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'package:http2/src/hpack/hpack.dart'; |
| import 'package:test/test.dart'; |
| |
| void main() { |
| group('hpack', () { |
| group('hpack-spec-decoder', () { |
| test('C.3 request without huffman encoding', () { |
| var context = HPackContext(); |
| List<Header> headers; |
| |
| // First request |
| headers = context.decoder.decode([ |
| 0x82, |
| 0x86, |
| 0x84, |
| 0x41, |
| 0x0f, |
| 0x77, |
| 0x77, |
| 0x77, |
| 0x2e, |
| 0x65, |
| 0x78, |
| 0x61, |
| 0x6d, |
| 0x70, |
| 0x6c, |
| 0x65, |
| 0x2e, |
| 0x63, |
| 0x6f, |
| 0x6d, |
| ]); |
| expect(headers, hasLength(4)); |
| expect(headers[0], isHeader(':method', 'GET')); |
| expect(headers[1], isHeader(':scheme', 'http')); |
| expect(headers[2], isHeader(':path', '/')); |
| expect(headers[3], isHeader(':authority', 'www.example.com')); |
| |
| // Second request |
| headers = context.decoder.decode([ |
| 0x82, |
| 0x86, |
| 0x84, |
| 0xbe, |
| 0x58, |
| 0x08, |
| 0x6e, |
| 0x6f, |
| 0x2d, |
| 0x63, |
| 0x61, |
| 0x63, |
| 0x68, |
| 0x65, |
| ]); |
| expect(headers, hasLength(5)); |
| expect(headers[0], isHeader(':method', 'GET')); |
| expect(headers[1], isHeader(':scheme', 'http')); |
| expect(headers[2], isHeader(':path', '/')); |
| expect(headers[3], isHeader(':authority', 'www.example.com')); |
| expect(headers[4], isHeader('cache-control', 'no-cache')); |
| |
| // Third request |
| headers = context.decoder.decode([ |
| 0x82, |
| 0x87, |
| 0x85, |
| 0xbf, |
| 0x40, |
| 0x0a, |
| 0x63, |
| 0x75, |
| 0x73, |
| 0x74, |
| 0x6f, |
| 0x6d, |
| 0x2d, |
| 0x6b, |
| 0x65, |
| 0x79, |
| 0x0c, |
| 0x63, |
| 0x75, |
| 0x73, |
| 0x74, |
| 0x6f, |
| 0x6d, |
| 0x2d, |
| 0x76, |
| 0x61, |
| 0x6c, |
| 0x75, |
| 0x65, |
| ]); |
| expect(headers, hasLength(5)); |
| expect(headers[0], isHeader(':method', 'GET')); |
| expect(headers[1], isHeader(':scheme', 'https')); |
| expect(headers[2], isHeader(':path', '/index.html')); |
| expect(headers[3], isHeader(':authority', 'www.example.com')); |
| expect(headers[4], isHeader('custom-key', 'custom-value')); |
| }); |
| |
| test('C.4 request with huffman encoding', () { |
| var context = HPackContext(); |
| List<Header> headers; |
| |
| // First request |
| headers = context.decoder.decode([ |
| 0x82, |
| 0x86, |
| 0x84, |
| 0x41, |
| 0x8c, |
| 0xf1, |
| 0xe3, |
| 0xc2, |
| 0xe5, |
| 0xf2, |
| 0x3a, |
| 0x6b, |
| 0xa0, |
| 0xab, |
| 0x90, |
| 0xf4, |
| 0xff, |
| ]); |
| expect(headers, hasLength(4)); |
| expect(headers[0], isHeader(':method', 'GET')); |
| expect(headers[1], isHeader(':scheme', 'http')); |
| expect(headers[2], isHeader(':path', '/')); |
| expect(headers[3], isHeader(':authority', 'www.example.com')); |
| |
| // Second request |
| headers = context.decoder.decode([ |
| 0x82, |
| 0x86, |
| 0x84, |
| 0xbe, |
| 0x58, |
| 0x86, |
| 0xa8, |
| 0xeb, |
| 0x10, |
| 0x64, |
| 0x9c, |
| 0xbf, |
| ]); |
| expect(headers, hasLength(5)); |
| expect(headers[0], isHeader(':method', 'GET')); |
| expect(headers[1], isHeader(':scheme', 'http')); |
| expect(headers[2], isHeader(':path', '/')); |
| expect(headers[3], isHeader(':authority', 'www.example.com')); |
| expect(headers[4], isHeader('cache-control', 'no-cache')); |
| |
| // Third request |
| headers = context.decoder.decode([ |
| 0x82, |
| 0x87, |
| 0x85, |
| 0xbf, |
| 0x40, |
| 0x88, |
| 0x25, |
| 0xa8, |
| 0x49, |
| 0xe9, |
| 0x5b, |
| 0xa9, |
| 0x7d, |
| 0x7f, |
| 0x89, |
| 0x25, |
| 0xa8, |
| 0x49, |
| 0xe9, |
| 0x5b, |
| 0xb8, |
| 0xe8, |
| 0xb4, |
| 0xbf, |
| ]); |
| expect(headers, hasLength(5)); |
| expect(headers[0], isHeader(':method', 'GET')); |
| expect(headers[1], isHeader(':scheme', 'https')); |
| expect(headers[2], isHeader(':path', '/index.html')); |
| expect(headers[3], isHeader(':authority', 'www.example.com')); |
| expect(headers[4], isHeader('custom-key', 'custom-value')); |
| }); |
| |
| test('C.5 response without huffman encoding', () { |
| var context = HPackContext(); |
| List<Header> headers; |
| |
| // First response |
| headers = context.decoder.decode([ |
| 0x48, |
| 0x03, |
| 0x33, |
| 0x30, |
| 0x32, |
| 0x58, |
| 0x07, |
| 0x70, |
| 0x72, |
| 0x69, |
| 0x76, |
| 0x61, |
| 0x74, |
| 0x65, |
| 0x61, |
| 0x1d, |
| 0x4d, |
| 0x6f, |
| 0x6e, |
| 0x2c, |
| 0x20, |
| 0x32, |
| 0x31, |
| 0x20, |
| 0x4f, |
| 0x63, |
| 0x74, |
| 0x20, |
| 0x32, |
| 0x30, |
| 0x31, |
| 0x33, |
| 0x20, |
| 0x32, |
| 0x30, |
| 0x3a, |
| 0x31, |
| 0x33, |
| 0x3a, |
| 0x32, |
| 0x31, |
| 0x20, |
| 0x47, |
| 0x4d, |
| 0x54, |
| 0x6e, |
| 0x17, |
| 0x68, |
| 0x74, |
| 0x74, |
| 0x70, |
| 0x73, |
| 0x3a, |
| 0x2f, |
| 0x2f, |
| 0x77, |
| 0x77, |
| 0x77, |
| 0x2e, |
| 0x65, |
| 0x78, |
| 0x61, |
| 0x6d, |
| 0x70, |
| 0x6c, |
| 0x65, |
| 0x2e, |
| 0x63, |
| 0x6f, |
| 0x6d, |
| ]); |
| expect(headers, hasLength(4)); |
| expect(headers[0], isHeader(':status', '302')); |
| expect(headers[1], isHeader('cache-control', 'private')); |
| expect(headers[2], isHeader('date', 'Mon, 21 Oct 2013 20:13:21 GMT')); |
| expect(headers[3], isHeader('location', 'https://www.example.com')); |
| |
| // Second response |
| headers = context.decoder.decode([ |
| 0x48, |
| 0x03, |
| 0x33, |
| 0x30, |
| 0x37, |
| 0xc1, |
| 0xc0, |
| 0xbf, |
| ]); |
| expect(headers, hasLength(4)); |
| expect(headers[0], isHeader(':status', '307')); |
| expect(headers[1], isHeader('cache-control', 'private')); |
| expect(headers[2], isHeader('date', 'Mon, 21 Oct 2013 20:13:21 GMT')); |
| expect(headers[3], isHeader('location', 'https://www.example.com')); |
| |
| // Third response |
| headers = context.decoder.decode([ |
| 0x88, |
| 0xc1, |
| 0x61, |
| 0x1d, |
| 0x4d, |
| 0x6f, |
| 0x6e, |
| 0x2c, |
| 0x20, |
| 0x32, |
| 0x31, |
| 0x20, |
| 0x4f, |
| 0x63, |
| 0x74, |
| 0x20, |
| 0x32, |
| 0x30, |
| 0x31, |
| 0x33, |
| 0x20, |
| 0x32, |
| 0x30, |
| 0x3a, |
| 0x31, |
| 0x33, |
| 0x3a, |
| 0x32, |
| 0x32, |
| 0x20, |
| 0x47, |
| 0x4d, |
| 0x54, |
| 0xc0, |
| 0x5a, |
| 0x04, |
| 0x67, |
| 0x7a, |
| 0x69, |
| 0x70, |
| 0x77, |
| 0x38, |
| 0x66, |
| 0x6f, |
| 0x6f, |
| 0x3d, |
| 0x41, |
| 0x53, |
| 0x44, |
| 0x4a, |
| 0x4b, |
| 0x48, |
| 0x51, |
| 0x4b, |
| 0x42, |
| 0x5a, |
| 0x58, |
| 0x4f, |
| 0x51, |
| 0x57, |
| 0x45, |
| 0x4f, |
| 0x50, |
| 0x49, |
| 0x55, |
| 0x41, |
| 0x58, |
| 0x51, |
| 0x57, |
| 0x45, |
| 0x4f, |
| 0x49, |
| 0x55, |
| 0x3b, |
| 0x20, |
| 0x6d, |
| 0x61, |
| 0x78, |
| 0x2d, |
| 0x61, |
| 0x67, |
| 0x65, |
| 0x3d, |
| 0x33, |
| 0x36, |
| 0x30, |
| 0x30, |
| 0x3b, |
| 0x20, |
| 0x76, |
| 0x65, |
| 0x72, |
| 0x73, |
| 0x69, |
| 0x6f, |
| 0x6e, |
| 0x3d, |
| 0x31, |
| ]); |
| expect(headers, hasLength(6)); |
| expect(headers[0], isHeader(':status', '200')); |
| expect(headers[1], isHeader('cache-control', 'private')); |
| expect(headers[2], isHeader('date', 'Mon, 21 Oct 2013 20:13:22 GMT')); |
| expect(headers[3], isHeader('location', 'https://www.example.com')); |
| expect(headers[4], isHeader('content-encoding', 'gzip')); |
| expect( |
| headers[5], |
| isHeader( |
| 'set-cookie', |
| 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1', |
| ), |
| ); |
| }); |
| |
| test('C.6 response with huffman encoding', () { |
| var context = HPackContext(); |
| List<Header> headers; |
| |
| // First response |
| headers = context.decoder.decode([ |
| 0x48, |
| 0x82, |
| 0x64, |
| 0x02, |
| 0x58, |
| 0x85, |
| 0xae, |
| 0xc3, |
| 0x77, |
| 0x1a, |
| 0x4b, |
| 0x61, |
| 0x96, |
| 0xd0, |
| 0x7a, |
| 0xbe, |
| 0x94, |
| 0x10, |
| 0x54, |
| 0xd4, |
| 0x44, |
| 0xa8, |
| 0x20, |
| 0x05, |
| 0x95, |
| 0x04, |
| 0x0b, |
| 0x81, |
| 0x66, |
| 0xe0, |
| 0x82, |
| 0xa6, |
| 0x2d, |
| 0x1b, |
| 0xff, |
| 0x6e, |
| 0x91, |
| 0x9d, |
| 0x29, |
| 0xad, |
| 0x17, |
| 0x18, |
| 0x63, |
| 0xc7, |
| 0x8f, |
| 0x0b, |
| 0x97, |
| 0xc8, |
| 0xe9, |
| 0xae, |
| 0x82, |
| 0xae, |
| 0x43, |
| 0xd3, |
| ]); |
| expect(headers, hasLength(4)); |
| expect(headers[0], isHeader(':status', '302')); |
| expect(headers[1], isHeader('cache-control', 'private')); |
| expect(headers[2], isHeader('date', 'Mon, 21 Oct 2013 20:13:21 GMT')); |
| expect(headers[3], isHeader('location', 'https://www.example.com')); |
| |
| // Second response |
| headers = context.decoder.decode([ |
| 0x48, |
| 0x83, |
| 0x64, |
| 0x0e, |
| 0xff, |
| 0xc1, |
| 0xc0, |
| 0xbf, |
| ]); |
| expect(headers, hasLength(4)); |
| expect(headers[0], isHeader(':status', '307')); |
| expect(headers[1], isHeader('cache-control', 'private')); |
| expect(headers[2], isHeader('date', 'Mon, 21 Oct 2013 20:13:21 GMT')); |
| expect(headers[3], isHeader('location', 'https://www.example.com')); |
| |
| // Third response |
| headers = context.decoder.decode([ |
| 0x88, |
| 0xc1, |
| 0x61, |
| 0x96, |
| 0xd0, |
| 0x7a, |
| 0xbe, |
| 0x94, |
| 0x10, |
| 0x54, |
| 0xd4, |
| 0x44, |
| 0xa8, |
| 0x20, |
| 0x05, |
| 0x95, |
| 0x04, |
| 0x0b, |
| 0x81, |
| 0x66, |
| 0xe0, |
| 0x84, |
| 0xa6, |
| 0x2d, |
| 0x1b, |
| 0xff, |
| 0xc0, |
| 0x5a, |
| 0x83, |
| 0x9b, |
| 0xd9, |
| 0xab, |
| 0x77, |
| 0xad, |
| 0x94, |
| 0xe7, |
| 0x82, |
| 0x1d, |
| 0xd7, |
| 0xf2, |
| 0xe6, |
| 0xc7, |
| 0xb3, |
| 0x35, |
| 0xdf, |
| 0xdf, |
| 0xcd, |
| 0x5b, |
| 0x39, |
| 0x60, |
| 0xd5, |
| 0xaf, |
| 0x27, |
| 0x08, |
| 0x7f, |
| 0x36, |
| 0x72, |
| 0xc1, |
| 0xab, |
| 0x27, |
| 0x0f, |
| 0xb5, |
| 0x29, |
| 0x1f, |
| 0x95, |
| 0x87, |
| 0x31, |
| 0x60, |
| 0x65, |
| 0xc0, |
| 0x03, |
| 0xed, |
| 0x4e, |
| 0xe5, |
| 0xb1, |
| 0x06, |
| 0x3d, |
| 0x50, |
| 0x07, |
| ]); |
| expect(headers, hasLength(6)); |
| expect(headers[0], isHeader(':status', '200')); |
| expect(headers[1], isHeader('cache-control', 'private')); |
| expect(headers[2], isHeader('date', 'Mon, 21 Oct 2013 20:13:22 GMT')); |
| expect(headers[3], isHeader('location', 'https://www.example.com')); |
| expect(headers[4], isHeader('content-encoding', 'gzip')); |
| expect( |
| headers[5], |
| isHeader( |
| 'set-cookie', |
| 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1', |
| ), |
| ); |
| }); |
| }); |
| |
| group('negative-decoder-tests', () { |
| test('invalid-integer-encoding', () { |
| var context = HPackContext(); |
| expect( |
| () => context.decoder.decode([1 << 6, 0xff]), |
| throwsA(isHPackDecodingException), |
| ); |
| }); |
| |
| test('index-out-of-table-size', () { |
| var context = HPackContext(); |
| expect( |
| () => context.decoder.decode([0x7f]), |
| throwsA(isHPackDecodingException), |
| ); |
| }); |
| |
| test('invalid-update-dynamic-table-size', () { |
| var context = HPackContext(); |
| expect( |
| () => context.decoder.decode([0x3f]), |
| throwsA(isHPackDecodingException), |
| ); |
| }); |
| |
| test('update-dynamic-table-size-too-high', () { |
| var context = HPackContext(); |
| // Tries to set dynamic table to 4097 (max is 4096 by default) |
| var bytes = TestHelper.newInteger(0x20, 5, 4097); |
| expect( |
| () => context.decoder.decode(bytes), |
| throwsA(isHPackDecodingException), |
| ); |
| }); |
| }); |
| |
| group('custom decoder tests', () { |
| const char0 = 0x30; |
| const char1 = 0x31; |
| const char2 = 0x31; |
| const char3 = 0x31; |
| const charA = 0x61; |
| const charB = 0x62; |
| const charC = 0x63; |
| const charD = 0x64; |
| |
| test('update-dynamic-table-size-too-high', () { |
| var context = HPackContext(); |
| // Sets dynamic table to 4096 |
| expect( |
| context.decoder.decode(TestHelper.newInteger(0x20, 5, 4096)), |
| <void>[], |
| ); |
| }); |
| |
| test('dynamic table entry', () { |
| List<Header> headers; |
| var context = HPackContext(); |
| |
| var buffer = <int>[]; |
| buffer.addAll(TestHelper.insertIntoDynamicTable(2048, char0, charA)); |
| buffer.addAll(TestHelper.insertIntoDynamicTable(2048, char1, charB)); |
| buffer.addAll(TestHelper.dynamicTableLookup(0)); |
| buffer.addAll(TestHelper.dynamicTableLookup(1)); |
| buffer.addAll(TestHelper.dynamicTableLookup(0)); |
| buffer.addAll(TestHelper.dynamicTableLookup(1)); |
| buffer.addAll(TestHelper.insertIntoDynamicTable(1024, char2, charC)); |
| buffer.addAll(TestHelper.insertIntoDynamicTable(1024, char3, charD)); |
| buffer.addAll(TestHelper.dynamicTableLookup(0)); |
| buffer.addAll(TestHelper.dynamicTableLookup(1)); |
| buffer.addAll(TestHelper.dynamicTableLookup(2)); |
| |
| headers = context.decoder.decode(buffer); |
| expect(headers, hasLength(11)); |
| TestHelper.expectHeader(headers[0], 2048, char0, charA); |
| TestHelper.expectHeader(headers[1], 2048, char1, charB); |
| |
| TestHelper.expectHeader(headers[2], 2048, char1, charB); |
| TestHelper.expectHeader(headers[3], 2048, char0, charA); |
| TestHelper.expectHeader(headers[4], 2048, char1, charB); |
| TestHelper.expectHeader(headers[5], 2048, char0, charA); |
| |
| TestHelper.expectHeader(headers[6], 1024, char2, charC); |
| TestHelper.expectHeader(headers[7], 1024, char3, charD); |
| |
| TestHelper.expectHeader(headers[8], 1024, char1, charD); |
| TestHelper.expectHeader(headers[9], 1024, char0, charC); |
| TestHelper.expectHeader(headers[10], 2048, char1, charB); |
| |
| // We're reducing now the size by 1 byte, which should evict the last |
| // entry. |
| headers = context.decoder.decode( |
| TestHelper.setDynamicTableSize(4096 - 1), |
| ); |
| expect(headers, hasLength(0)); |
| |
| headers = context.decoder.decode(TestHelper.dynamicTableLookup(0)); |
| expect(headers, hasLength(1)); |
| TestHelper.expectHeader(headers[0], 1024, char1, charD); |
| |
| headers = context.decoder.decode(TestHelper.dynamicTableLookup(1)); |
| expect(headers, hasLength(1)); |
| TestHelper.expectHeader(headers[0], 1024, char0, charC); |
| |
| // Since we reduce the size by 1 byte, the last entry must be gone now. |
| expect( |
| () => context.decoder.decode(TestHelper.dynamicTableLookup(2)), |
| throwsA(isHPackDecodingException), |
| ); |
| }); |
| }); |
| |
| group('encoder-tests', () { |
| test('simple-encoding', () { |
| var context = HPackContext(); |
| var headers = [Header.ascii('key', 'value')]; |
| expect(context.encoder.encode(headers), [ |
| 0x00, |
| 0x03, |
| 0x6b, |
| 0x65, |
| 0x79, |
| 0x05, |
| 0x76, |
| 0x61, |
| 0x6c, |
| 0x75, |
| 0x65, |
| ]); |
| }); |
| |
| test('simple-encoding-long-value', () { |
| var context = HPackContext(); |
| var headers = [ |
| Header([0x42], List.filled(300, 0x84)), |
| ]; |
| |
| expect( |
| context.decoder.decode(context.encoder.encode(headers)).first, |
| equalsHeader(headers.first), |
| ); |
| |
| expect(context.encoder.encode(headers), [ |
| // Literal Header Field with Incremental Indexing - Indexed Name |
| 0x00, |
| |
| // Key: Length |
| 0x01, |
| |
| // Key: Bytes |
| 0x42, |
| |
| // Value: (first 7 bits + rest) |
| 0x7f, 0xad, 0x01, |
| |
| // Value: Bytes |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, |
| ]); |
| }); |
| }); |
| }); |
| } |
| |
| class TestHelper { |
| static List<int> setDynamicTableSize(int newSize) { |
| return TestHelper.newInteger(0x20, 5, newSize); |
| } |
| |
| static List<int> newInteger(int currentByte, int prefixBits, int value) { |
| assert((currentByte & ((1 << prefixBits) - 1)) == 0); |
| var buffer = <int>[]; |
| if (value < ((1 << prefixBits) - 1)) { |
| currentByte |= value; |
| buffer.add(currentByte); |
| } else { |
| // Length encodeded. |
| currentByte |= (1 << prefixBits) - 1; |
| value -= (1 << prefixBits) - 1; |
| buffer.add(currentByte); |
| var done = false; |
| while (!done) { |
| currentByte = value & 0x7f; |
| value = value >> 7; |
| done = value == 0; |
| if (!done) currentByte |= 0x80; |
| buffer.add(currentByte); |
| } |
| } |
| return buffer; |
| } |
| |
| static List<int> insertIntoDynamicTable(int n, int nameChar, int valueChar) { |
| // NOTE: size(header) = 32 + header.name.length + header.value.length. |
| |
| var buffer = <int>[]; |
| |
| // Literal indexed (will be put into dynamic table) |
| buffer.addAll([0x40]); |
| |
| var name = [nameChar]; |
| buffer.addAll(newInteger(0, 7, name.length)); |
| buffer.addAll(name); |
| |
| var value = List.filled(n - 32 - name.length, valueChar); |
| buffer.addAll(newInteger(0, 7, value.length)); |
| buffer.addAll(value); |
| |
| return buffer; |
| } |
| |
| static List<int> dynamicTableLookup(int index) { |
| // There are 62 entries in the static table. |
| return newInteger(0x80, 7, 62 + index); |
| } |
| |
| static void expectHeader(Header h, int len, int nameChar, int valueChar) { |
| var data = h.value; |
| expect(data, hasLength(len - 32 - 1)); |
| for (var i = 0; i < data.length; i++) { |
| expect(data[i], valueChar); |
| } |
| } |
| } |
| |
| /// A matcher for HuffmannDecodingExceptions. |
| const Matcher isHPackDecodingException = TypeMatcher<HPackDecodingException>(); |
| |
| class _HeaderMatcher extends Matcher { |
| final Header header; |
| |
| _HeaderMatcher(this.header); |
| |
| @override |
| Description describe(Description description) => description.add('Header'); |
| |
| @override |
| bool matches(Object? item, Map matchState) { |
| return item is Header && |
| _compareLists(item.name, header.name) && |
| _compareLists(item.value, header.value); |
| } |
| |
| bool _compareLists(List<int> a, List<int> b) { |
| if (a.length != b.length) return false; |
| for (var i = 0; i < a.length; i++) { |
| if (a[i] != b[i]) return false; |
| } |
| return true; |
| } |
| } |
| |
| Matcher isHeader(String name, String value) => |
| _HeaderMatcher(Header.ascii(name, value)); |
| |
| Matcher equalsHeader(Header header) => _HeaderMatcher(header); |