pax_global_header00006660000000000000000000000064150511433760014517gustar00rootroot0000000000000052 comment=0e7d6d49155507870e3ff43368d1a7788301545c golang-github-marten-seemann-qpack-0.5.1/000077500000000000000000000000001505114337600202565ustar00rootroot00000000000000golang-github-marten-seemann-qpack-0.5.1/.codecov.yml000066400000000000000000000001471505114337600225030ustar00rootroot00000000000000coverage: round: nearest status: project: default: threshold: 1 patch: false golang-github-marten-seemann-qpack-0.5.1/.github/000077500000000000000000000000001505114337600216165ustar00rootroot00000000000000golang-github-marten-seemann-qpack-0.5.1/.github/FUNDING.yml000066400000000000000000000014641505114337600234400ustar00rootroot00000000000000# These are supported funding model platforms github: [marten-seemann] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] golang-github-marten-seemann-qpack-0.5.1/.github/dependabot.yml000066400000000000000000000001661505114337600244510ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" golang-github-marten-seemann-qpack-0.5.1/.github/workflows/000077500000000000000000000000001505114337600236535ustar00rootroot00000000000000golang-github-marten-seemann-qpack-0.5.1/.github/workflows/golangci-lint.yml000066400000000000000000000010421505114337600271220ustar00rootroot00000000000000name: golangci-lint on: push: tags: - v* branches: - master pull_request: jobs: golangci: name: lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: '1.23.x' - name: golangci-lint uses: golangci/golangci-lint-action@v6 with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. version: v1.60.3 golang-github-marten-seemann-qpack-0.5.1/.github/workflows/test.yml000066400000000000000000000025241505114337600253600ustar00rootroot00000000000000on: [ push, pull_request ] jobs: unit: strategy: matrix: go: [ "1.22.x", "1.23.x" ] runs-on: ubuntu-latest name: Unit tests (Go ${{ matrix.go }}) steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - run: go version - name: Run tests run: go test -v -cover -race -shuffle=on . - name: Run tests (32 bit) env: GOARCH: 386 run: go test -v -cover -coverprofile=coverage.txt -shuffle=on . - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: files: coverage.txt env_vars: GO=${{ matrix.go }} token: ${{ secrets.CODECOV_TOKEN }} integration: strategy: matrix: go: [ "1.22.x", "1.23.x" ] runs-on: ubuntu-latest name: Integration tests (Go ${{ matrix.go }}) steps: - uses: actions/checkout@v4 with: submodules: 'recursive' - uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - run: go version - name: Run interop tests run: go test -v ./integrationtests/interop/ - name: Run integration tests run: | for i in {1..25}; do go test -v -race -shuffle=on ./integrationtests/self done golang-github-marten-seemann-qpack-0.5.1/.gitignore000066400000000000000000000001561505114337600222500ustar00rootroot00000000000000fuzzing/*.zip fuzzing/coverprofile fuzzing/crashers fuzzing/sonarprofile fuzzing/suppressions fuzzing/corpus/ golang-github-marten-seemann-qpack-0.5.1/.gitmodules000066400000000000000000000001761505114337600224370ustar00rootroot00000000000000[submodule "integrationtests/interop/qifs"] path = integrationtests/interop/qifs url = https://github.com/qpackers/qifs.git golang-github-marten-seemann-qpack-0.5.1/.golangci.yml000066400000000000000000000005771505114337600226530ustar00rootroot00000000000000run: linters-settings: linters: disable-all: true enable: - asciicheck - copyloopvar - exhaustive - goconst - gofmt # redundant, since gofmt *should* be a no-op after gofumpt - gofumpt - goimports - gosimple - govet - ineffassign - misspell - prealloc - staticcheck - stylecheck - unconvert - unparam - unused golang-github-marten-seemann-qpack-0.5.1/LICENSE.md000066400000000000000000000020361505114337600216630ustar00rootroot00000000000000Copyright 2019 Marten Seemann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-marten-seemann-qpack-0.5.1/README.md000066400000000000000000000024551505114337600215430ustar00rootroot00000000000000# QPACK [![PkgGoDev](https://pkg.go.dev/badge/github.com/quic-go/qpack)](https://pkg.go.dev/github.com/quic-go/qpack) [![Code Coverage](https://img.shields.io/codecov/c/github/quic-go/qpack/master.svg?style=flat-square)](https://codecov.io/gh/quic-go/qpack) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/quic-go.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:quic-go) This is a minimal QPACK ([RFC 9204](https://datatracker.ietf.org/doc/html/rfc9204)) implementation in Go. It is minimal in the sense that it doesn't use the dynamic table at all, but just the static table and (Huffman encoded) string literals. Wherever possible, it reuses code from the [HPACK implementation in the Go standard library](https://github.com/golang/net/tree/master/http2/hpack). It is interoperable with other QPACK implementations (both encoders and decoders), however it won't achieve a high compression efficiency. If you're interested in dynamic table support, please comment on [the issue](https://github.com/quic-go/qpack/issues/33). ## Running the Interop Tests Install the [QPACK interop files](https://github.com/qpackers/qifs/) by running ```bash git submodule update --init --recursive ``` Then run the tests: ```bash go test -v ./integrationtests/interop/ ``` golang-github-marten-seemann-qpack-0.5.1/decoder.go000066400000000000000000000144311505114337600222150ustar00rootroot00000000000000package qpack import ( "bytes" "errors" "fmt" "sync" "golang.org/x/net/http2/hpack" ) // A decodingError is something the spec defines as a decoding error. type decodingError struct { err error } func (de decodingError) Error() string { return fmt.Sprintf("decoding error: %v", de.err) } // An invalidIndexError is returned when an encoder references a table // entry before the static table or after the end of the dynamic table. type invalidIndexError int func (e invalidIndexError) Error() string { return fmt.Sprintf("invalid indexed representation index %d", int(e)) } var errNoDynamicTable = decodingError{errors.New("no dynamic table")} // errNeedMore is an internal sentinel error value that means the // buffer is truncated and we need to read more data before we can // continue parsing. var errNeedMore = errors.New("need more data") // A Decoder is the decoding context for incremental processing of // header blocks. type Decoder struct { mutex sync.Mutex emitFunc func(f HeaderField) readRequiredInsertCount bool readDeltaBase bool // buf is the unparsed buffer. It's only written to // saveBuf if it was truncated in the middle of a header // block. Because it's usually not owned, we can only // process it under Write. buf []byte // not owned; only valid during Write // saveBuf is previous data passed to Write which we weren't able // to fully parse before. Unlike buf, we own this data. saveBuf bytes.Buffer } // NewDecoder returns a new decoder // The emitFunc will be called for each valid field parsed, // in the same goroutine as calls to Write, before Write returns. func NewDecoder(emitFunc func(f HeaderField)) *Decoder { return &Decoder{emitFunc: emitFunc} } func (d *Decoder) Write(p []byte) (int, error) { if len(p) == 0 { return 0, nil } d.mutex.Lock() n, err := d.writeLocked(p) d.mutex.Unlock() return n, err } func (d *Decoder) writeLocked(p []byte) (int, error) { // Only copy the data if we have to. Optimistically assume // that p will contain a complete header block. if d.saveBuf.Len() == 0 { d.buf = p } else { d.saveBuf.Write(p) d.buf = d.saveBuf.Bytes() d.saveBuf.Reset() } if err := d.decode(); err != nil { if err != errNeedMore { return 0, err } // TODO: limit the size of the buffer d.saveBuf.Write(d.buf) } return len(p), nil } // DecodeFull decodes an entire block. func (d *Decoder) DecodeFull(p []byte) ([]HeaderField, error) { if len(p) == 0 { return []HeaderField{}, nil } d.mutex.Lock() defer d.mutex.Unlock() saveFunc := d.emitFunc defer func() { d.emitFunc = saveFunc }() var hf []HeaderField d.emitFunc = func(f HeaderField) { hf = append(hf, f) } if _, err := d.writeLocked(p); err != nil { return nil, err } if err := d.Close(); err != nil { return nil, err } return hf, nil } // Close declares that the decoding is complete and resets the Decoder // to be reused again for a new header block. If there is any remaining // data in the decoder's buffer, Close returns an error. func (d *Decoder) Close() error { if d.saveBuf.Len() > 0 { d.saveBuf.Reset() return decodingError{errors.New("truncated headers")} } d.readRequiredInsertCount = false d.readDeltaBase = false return nil } func (d *Decoder) decode() error { if !d.readRequiredInsertCount { requiredInsertCount, rest, err := readVarInt(8, d.buf) if err != nil { return err } d.readRequiredInsertCount = true if requiredInsertCount != 0 { return decodingError{errors.New("expected Required Insert Count to be zero")} } d.buf = rest } if !d.readDeltaBase { base, rest, err := readVarInt(7, d.buf) if err != nil { return err } d.readDeltaBase = true if base != 0 { return decodingError{errors.New("expected Base to be zero")} } d.buf = rest } if len(d.buf) == 0 { return errNeedMore } for len(d.buf) > 0 { b := d.buf[0] var err error switch { case b&0x80 > 0: // 1xxxxxxx err = d.parseIndexedHeaderField() case b&0xc0 == 0x40: // 01xxxxxx err = d.parseLiteralHeaderField() case b&0xe0 == 0x20: // 001xxxxx err = d.parseLiteralHeaderFieldWithoutNameReference() default: err = fmt.Errorf("unexpected type byte: %#x", b) } if err != nil { return err } } return nil } func (d *Decoder) parseIndexedHeaderField() error { buf := d.buf if buf[0]&0x40 == 0 { return errNoDynamicTable } index, buf, err := readVarInt(6, buf) if err != nil { return err } hf, ok := d.at(index) if !ok { return decodingError{invalidIndexError(index)} } d.emitFunc(hf) d.buf = buf return nil } func (d *Decoder) parseLiteralHeaderField() error { buf := d.buf if buf[0]&0x10 == 0 { return errNoDynamicTable } // We don't need to check the value of the N-bit here. // It's only relevant when re-encoding header fields, // and determines whether the header field can be added to the dynamic table. // Since we don't support the dynamic table, we can ignore it. index, buf, err := readVarInt(4, buf) if err != nil { return err } hf, ok := d.at(index) if !ok { return decodingError{invalidIndexError(index)} } if len(buf) == 0 { return errNeedMore } usesHuffman := buf[0]&0x80 > 0 val, buf, err := d.readString(buf, 7, usesHuffman) if err != nil { return err } hf.Value = val d.emitFunc(hf) d.buf = buf return nil } func (d *Decoder) parseLiteralHeaderFieldWithoutNameReference() error { buf := d.buf usesHuffmanForName := buf[0]&0x8 > 0 name, buf, err := d.readString(buf, 3, usesHuffmanForName) if err != nil { return err } if len(buf) == 0 { return errNeedMore } usesHuffmanForVal := buf[0]&0x80 > 0 val, buf, err := d.readString(buf, 7, usesHuffmanForVal) if err != nil { return err } d.emitFunc(HeaderField{Name: name, Value: val}) d.buf = buf return nil } func (d *Decoder) readString(buf []byte, n uint8, usesHuffman bool) (string, []byte, error) { l, buf, err := readVarInt(n, buf) if err != nil { return "", nil, err } if uint64(len(buf)) < l { return "", nil, errNeedMore } var val string if usesHuffman { var err error val, err = hpack.HuffmanDecodeToString(buf[:l]) if err != nil { return "", nil, err } } else { val = string(buf[:l]) } buf = buf[l:] return val, buf, nil } func (d *Decoder) at(i uint64) (hf HeaderField, ok bool) { if i >= uint64(len(staticTableEntries)) { return } return staticTableEntries[i], true } golang-github-marten-seemann-qpack-0.5.1/decoder_test.go000066400000000000000000000150721505114337600232560ustar00rootroot00000000000000package qpack import ( "bytes" "testing" "golang.org/x/net/http2/hpack" "github.com/stretchr/testify/require" ) type recordingDecoder struct { *Decoder headerFields []HeaderField } func newRecordingDecoder() *recordingDecoder { decoder := &recordingDecoder{} decoder.Decoder = NewDecoder(func(hf HeaderField) { decoder.headerFields = append(decoder.headerFields, hf) }) return decoder } func (decoder *recordingDecoder) Fields() []HeaderField { return decoder.headerFields } func insertPrefix(data []byte) []byte { prefix := appendVarInt(nil, 8, 0) prefix = appendVarInt(prefix, 7, 0) return append(prefix, data...) } func TestDecoderRejectsInvalidInputs(t *testing.T) { tests := []struct { name string input []byte expected string }{ { name: "non-zero required insert count", // we don't support dynamic table updates input: append(appendVarInt(nil, 8, 1), appendVarInt(nil, 7, 0)...), expected: "decoding error: expected Required Insert Count to be zero", }, { name: "non-zero delta base", // we don't support dynamic table updates input: append(appendVarInt(nil, 8, 0), appendVarInt(nil, 7, 1)...), expected: "decoding error: expected Base to be zero", }, { name: "unknown type byte", input: insertPrefix([]byte{0x10}), expected: "unexpected type byte: 0x10", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := NewDecoder(nil).Write(tt.input) require.EqualError(t, err, tt.expected) }) } } func doPartialWrites(t *testing.T, decoder *recordingDecoder, data []byte) { t.Helper() for i := 0; i < len(data)-1; i++ { n, err := decoder.Write([]byte{data[i]}) require.NoError(t, err) require.Equal(t, 1, n) require.Empty(t, decoder.Fields()) } n, err := decoder.Write([]byte{data[len(data)-1]}) require.NoError(t, err) require.Equal(t, 1, n) require.Len(t, decoder.Fields(), 1) } func TestDecoderIndexedHeaderFields(t *testing.T) { decoder := newRecordingDecoder() data := appendVarInt(nil, 6, 20) data[0] ^= 0x80 | 0x40 doPartialWrites(t, decoder, insertPrefix(data)) require.Len(t, decoder.Fields(), 1) require.Equal(t, staticTableEntries[20], decoder.Fields()[0]) } func TestDecoderInvalidIndexedHeaderFields(t *testing.T) { tests := []struct { name string input []byte expected string }{ { name: "errors when a non-existent static table entry is referenced", input: func() []byte { data := appendVarInt(nil, 6, 10000) data[0] ^= 0x80 | 0x40 return insertPrefix(data) }(), expected: "decoding error: invalid indexed representation index 10000", }, { name: "rejects an indexed header field that references the dynamic table", input: func() []byte { data := appendVarInt(nil, 6, 20) data[0] ^= 0x80 // don't set the static flag (0x40) return insertPrefix(data) }(), expected: errNoDynamicTable.Error(), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { decoder := newRecordingDecoder() _, err := decoder.Write(tt.input) require.EqualError(t, err, tt.expected) require.Empty(t, decoder.Fields()) }) } } func TestDecoderLiteralHeaderFieldWithNameReference(t *testing.T) { t.Run("without the N-bit", func(t *testing.T) { testDecoderLiteralHeaderFieldWithNameReference(t, false) }) t.Run("with the N-bit", func(t *testing.T) { testDecoderLiteralHeaderFieldWithNameReference(t, true) }) } func testDecoderLiteralHeaderFieldWithNameReference(t *testing.T, n bool) { decoder := newRecordingDecoder() data := appendVarInt(nil, 4, 49) data[0] ^= 0x40 | 0x10 if n { data[0] |= 0x20 } data = appendVarInt(data, 7, 6) data = append(data, []byte("foobar")...) doPartialWrites(t, decoder, insertPrefix(data)) require.Len(t, decoder.Fields(), 1) require.Equal(t, "content-type", decoder.Fields()[0].Name) require.Equal(t, "foobar", decoder.Fields()[0].Value) } func TestDecoderLiteralHeaderFieldWithNameReferenceAndHuffmanEncoding(t *testing.T) { decoder := newRecordingDecoder() data := appendVarInt(nil, 4, 49) data[0] ^= 0x40 | 0x10 data2 := appendVarInt(nil, 7, hpack.HuffmanEncodeLength("foobar")) data2[0] ^= 0x80 data = hpack.AppendHuffmanString(append(data, data2...), "foobar") doPartialWrites(t, decoder, insertPrefix(data)) require.Len(t, decoder.Fields(), 1) require.Equal(t, "content-type", decoder.Fields()[0].Name) require.Equal(t, "foobar", decoder.Fields()[0].Value) } func TestDecoderLiteralHeaderFieldWithNameReferenceToTheDynamicTable(t *testing.T) { decoder := newRecordingDecoder() data := appendVarInt(nil, 4, 49) data[0] ^= 0x40 // don't set the static flag (0x10) data = appendVarInt(data, 7, 6) data = append(data, []byte("foobar")...) _, err := decoder.Write(insertPrefix(data)) require.ErrorIs(t, err, errNoDynamicTable) } func TestDecoderLiteralHeaderFieldWithoutNameReference(t *testing.T) { decoder := newRecordingDecoder() data := appendVarInt(nil, 3, 3) data[0] ^= 0x20 data = append(data, []byte("foo")...) data2 := appendVarInt(nil, 7, 3) data2 = append(data2, []byte("bar")...) data = append(data, data2...) doPartialWrites(t, decoder, insertPrefix(data)) require.Len(t, decoder.Fields(), 1) require.Equal(t, "foo", decoder.Fields()[0].Name) require.Equal(t, "bar", decoder.Fields()[0].Value) } func TestDecodeFull(t *testing.T) { // decode nothing data, err := NewDecoder(nil).DecodeFull([]byte{}) require.NoError(t, err) require.Empty(t, data) // decode a few entries buf := &bytes.Buffer{} enc := NewEncoder(buf) require.NoError(t, enc.WriteField(HeaderField{Name: "foo", Value: "bar"})) require.NoError(t, enc.WriteField(HeaderField{Name: "lorem", Value: "ipsum"})) data, err = NewDecoder(nil).DecodeFull(buf.Bytes()) require.NoError(t, err) require.Equal(t, []HeaderField{ {Name: "foo", Value: "bar"}, {Name: "lorem", Value: "ipsum"}, }, data) } func TestDecodeFullIncompleteData(t *testing.T) { buf := &bytes.Buffer{} enc := NewEncoder(buf) require.NoError(t, enc.WriteField(HeaderField{Name: "foo", Value: "bar"})) _, err := NewDecoder(nil).DecodeFull(buf.Bytes()[:buf.Len()-2]) require.EqualError(t, err, "decoding error: truncated headers") } func TestDecodeFullRestoresEmitFunc(t *testing.T) { var emitFuncCalled bool emitFunc := func(HeaderField) { emitFuncCalled = true } decoder := NewDecoder(emitFunc) buf := &bytes.Buffer{} enc := NewEncoder(buf) require.NoError(t, enc.WriteField(HeaderField{Name: "foo", Value: "bar"})) _, err := decoder.DecodeFull(buf.Bytes()) require.NoError(t, err) require.False(t, emitFuncCalled) _, err = decoder.Write(buf.Bytes()) require.NoError(t, err) require.True(t, emitFuncCalled) } golang-github-marten-seemann-qpack-0.5.1/encoder.go000066400000000000000000000051021505114337600222220ustar00rootroot00000000000000package qpack import ( "io" "golang.org/x/net/http2/hpack" ) // An Encoder performs QPACK encoding. type Encoder struct { wrotePrefix bool w io.Writer buf []byte } // NewEncoder returns a new Encoder which performs QPACK encoding. An // encoded data is written to w. func NewEncoder(w io.Writer) *Encoder { return &Encoder{w: w} } // WriteField encodes f into a single Write to e's underlying Writer. // This function may also produce bytes for the Header Block Prefix // if necessary. If produced, it is done before encoding f. func (e *Encoder) WriteField(f HeaderField) error { // write the Header Block Prefix if !e.wrotePrefix { e.buf = appendVarInt(e.buf, 8, 0) e.buf = appendVarInt(e.buf, 7, 0) e.wrotePrefix = true } idxAndVals, nameFound := encoderMap[f.Name] if nameFound { if idxAndVals.values == nil { if len(f.Value) == 0 { e.writeIndexedField(idxAndVals.idx) } else { e.writeLiteralFieldWithNameReference(&f, idxAndVals.idx) } } else { valIdx, valueFound := idxAndVals.values[f.Value] if valueFound { e.writeIndexedField(valIdx) } else { e.writeLiteralFieldWithNameReference(&f, idxAndVals.idx) } } } else { e.writeLiteralFieldWithoutNameReference(f) } _, err := e.w.Write(e.buf) e.buf = e.buf[:0] return err } // Close declares that the encoding is complete and resets the Encoder // to be reused again for a new header block. func (e *Encoder) Close() error { e.wrotePrefix = false return nil } func (e *Encoder) writeLiteralFieldWithoutNameReference(f HeaderField) { offset := len(e.buf) e.buf = appendVarInt(e.buf, 3, hpack.HuffmanEncodeLength(f.Name)) e.buf[offset] ^= 0x20 ^ 0x8 e.buf = hpack.AppendHuffmanString(e.buf, f.Name) offset = len(e.buf) e.buf = appendVarInt(e.buf, 7, hpack.HuffmanEncodeLength(f.Value)) e.buf[offset] ^= 0x80 e.buf = hpack.AppendHuffmanString(e.buf, f.Value) } // Encodes a header field whose name is present in one of the tables. func (e *Encoder) writeLiteralFieldWithNameReference(f *HeaderField, id uint8) { offset := len(e.buf) e.buf = appendVarInt(e.buf, 4, uint64(id)) // Set the 01NTxxxx pattern, forcing N to 0 and T to 1 e.buf[offset] ^= 0x50 offset = len(e.buf) e.buf = appendVarInt(e.buf, 7, hpack.HuffmanEncodeLength(f.Value)) e.buf[offset] ^= 0x80 e.buf = hpack.AppendHuffmanString(e.buf, f.Value) } // Encodes an indexed field, meaning it's entirely defined in one of the tables. func (e *Encoder) writeIndexedField(id uint8) { offset := len(e.buf) e.buf = appendVarInt(e.buf, 6, uint64(id)) // Set the 1Txxxxxx pattern, forcing T to 1 e.buf[offset] ^= 0xc0 } golang-github-marten-seemann-qpack-0.5.1/encoder_test.go000066400000000000000000000130201505114337600232570ustar00rootroot00000000000000package qpack import ( "bytes" "io" "testing" "golang.org/x/net/http2/hpack" "github.com/stretchr/testify/require" ) // errWriter wraps bytes.Buffer and optionally fails on every write // useful for testing misbehaving writers type errWriter struct { bytes.Buffer fail bool } func (ew *errWriter) Write(b []byte) (int, error) { if ew.fail { return 0, io.ErrClosedPipe } return ew.Buffer.Write(b) } func readPrefix(t *testing.T, data []byte) (rest []byte, requiredInsertCount uint64, deltaBase uint64) { var err error requiredInsertCount, rest, err = readVarInt(8, data) require.NoError(t, err) deltaBase, rest, err = readVarInt(7, rest) require.NoError(t, err) return } func checkHeaderField(t *testing.T, data []byte, hf HeaderField) []byte { require.Equal(t, uint8(0x20), data[0]&(0x80^0x40^0x20)) // 001xxxxx require.NotZero(t, data[0]&0x8) // Huffman encoding nameLen, data, err := readVarInt(3, data) require.NoError(t, err) l := hpack.HuffmanEncodeLength(hf.Name) require.Equal(t, l, nameLen) decodedName, err := hpack.HuffmanDecodeToString(data[:l]) require.NoError(t, err) require.Equal(t, hf.Name, decodedName) valueLen, data, err := readVarInt(7, data[l:]) require.NoError(t, err) l = hpack.HuffmanEncodeLength(hf.Value) require.Equal(t, l, valueLen) decodedValue, err := hpack.HuffmanDecodeToString(data[:l]) require.NoError(t, err) require.Equal(t, hf.Value, decodedValue) return data[l:] } // Reads one indexed field line representation from data and verifies it matches hf. // Returns the leftover bytes from data. func checkIndexedHeaderField(t *testing.T, data []byte, hf HeaderField) []byte { require.Equal(t, uint8(1), data[0]>>7) // 1Txxxxxx index, data, err := readVarInt(6, data) require.NoError(t, err) require.Equal(t, hf, staticTableEntries[index]) return data } func checkHeaderFieldWithNameRef(t *testing.T, data []byte, hf HeaderField) []byte { // read name reference require.Equal(t, uint8(1), data[0]>>6) // 01NTxxxx index, data, err := readVarInt(4, data) require.NoError(t, err) require.Equal(t, hf.Name, staticTableEntries[index].Name) // read literal value valueLen, data, err := readVarInt(7, data) require.NoError(t, err) l := hpack.HuffmanEncodeLength(hf.Value) require.Equal(t, l, valueLen) decodedValue, err := hpack.HuffmanDecodeToString(data[:l]) require.NoError(t, err) require.Equal(t, hf.Value, decodedValue) return data[l:] } func TestEncoderEncodesSingleField(t *testing.T) { output := &errWriter{} encoder := NewEncoder(output) hf := HeaderField{Name: "foobar", Value: "lorem ipsum"} require.NoError(t, encoder.WriteField(hf)) data, requiredInsertCount, deltaBase := readPrefix(t, output.Bytes()) require.Zero(t, requiredInsertCount) require.Zero(t, deltaBase) data = checkHeaderField(t, data, hf) require.Empty(t, data) } func TestEncoderFailsToEncodeWhenWriterErrs(t *testing.T) { output := &errWriter{fail: true} encoder := NewEncoder(output) hf := HeaderField{Name: "foobar", Value: "lorem ipsum"} err := encoder.WriteField(hf) require.EqualError(t, err, "io: read/write on closed pipe") } func TestEncoderEncodesMultipleFields(t *testing.T) { output := &errWriter{} encoder := NewEncoder(output) hf1 := HeaderField{Name: "foobar", Value: "lorem ipsum"} hf2 := HeaderField{Name: "raboof", Value: "dolor sit amet"} require.NoError(t, encoder.WriteField(hf1)) require.NoError(t, encoder.WriteField(hf2)) data, requiredInsertCount, deltaBase := readPrefix(t, output.Bytes()) require.Zero(t, requiredInsertCount) require.Zero(t, deltaBase) data = checkHeaderField(t, data, hf1) data = checkHeaderField(t, data, hf2) require.Empty(t, data) } func TestEncoderEncodesAllFieldsOfStaticTable(t *testing.T) { output := &errWriter{} encoder := NewEncoder(output) for _, hf := range staticTableEntries { require.NoError(t, encoder.WriteField(hf)) } data, requiredInsertCount, deltaBase := readPrefix(t, output.Bytes()) require.Zero(t, requiredInsertCount) require.Zero(t, deltaBase) for _, hf := range staticTableEntries { data = checkIndexedHeaderField(t, data, hf) } require.Empty(t, data) } func TestEncodeFieldsWithNameReferenceInStaticTable(t *testing.T) { output := &errWriter{} encoder := NewEncoder(output) hf1 := HeaderField{Name: ":status", Value: "666"} hf2 := HeaderField{Name: "server", Value: "lorem ipsum"} hf3 := HeaderField{Name: ":method", Value: ""} require.NoError(t, encoder.WriteField(hf1)) require.NoError(t, encoder.WriteField(hf2)) require.NoError(t, encoder.WriteField(hf3)) data, requiredInsertCount, deltaBase := readPrefix(t, output.Bytes()) require.Zero(t, requiredInsertCount) require.Zero(t, deltaBase) data = checkHeaderFieldWithNameRef(t, data, hf1) data = checkHeaderFieldWithNameRef(t, data, hf2) data = checkHeaderFieldWithNameRef(t, data, hf3) require.Empty(t, data) } func TestEncodeMultipleRequests(t *testing.T) { output := &errWriter{} encoder := NewEncoder(output) hf1 := HeaderField{Name: "foobar", Value: "lorem ipsum"} require.NoError(t, encoder.WriteField(hf1)) data, requiredInsertCount, deltaBase := readPrefix(t, output.Bytes()) require.Zero(t, requiredInsertCount) require.Zero(t, deltaBase) require.Empty(t, checkHeaderField(t, data, hf1)) output.Reset() require.NoError(t, encoder.Close()) hf2 := HeaderField{Name: "raboof", Value: "dolor sit amet"} require.NoError(t, encoder.WriteField(hf2)) data, requiredInsertCount, deltaBase = readPrefix(t, output.Bytes()) require.Zero(t, requiredInsertCount) require.Zero(t, deltaBase) require.Empty(t, checkHeaderField(t, data, hf2)) } golang-github-marten-seemann-qpack-0.5.1/example/000077500000000000000000000000001505114337600217115ustar00rootroot00000000000000golang-github-marten-seemann-qpack-0.5.1/example/fb-req-hq.out.0.0.0000066400000000000000000004457241505114337600246760ustar00rootroot00000000000000QbXE랶;Ǫ2kg[$oPBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])a LE^X%KTb,"[c(`DC{ŃvJ1lعPBF>W2IR?QbXE랶;ǫX?XĽuM])a LE^X%KTb,"[c(`DC{ŃvJ1lع_PfSj?)zSq<5Ay"L]uQEfU"Vz&|"zl9g03|(p[35B2pFpj*Z;%xSrWϐh9=!zX',{PGEE`QVH5v aQX^2 E+5QY->Q"Up&jHDDiP򌅌O_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢQ`}Ƅk R I䶂Q_գ(Al șuZ}|EQIm%Y)k\>{l3`ז'k>Vx&=jm%2pF:s|Ynmsy~+?=Ȟ۷쵢^'(,*p}Va <3+ Z|}Ef+ e$Vjm]~"[:P򌅌O_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUmyOQbXE랶;Ǯ,X~Lz^_?PBF>W2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay"p(T6.=6lr{k^FoE+fƼB_5k6|# _Dhaw?9g>V,fd{6Rg?ԞSOxZG{/TyQX^OQEd^8w`0E->Q"U2 E+5QGlDTI-@}ƝgP򌅌O_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUmyOGQ`}vv% b3(MSP_Ħ%abJj[?/A5MBKgb%B~z_)Qώ/@ 2뮴I;@?Ej d&Dˮ(P򌅌O_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUmyOQ`}vv% b3(MSP P[>z_)Qώ/@ 2뮴I;@?Ej d&Dˮ(P򌅌O_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUmyO Q`}vv% b3(MSPICԒLv|w1|L]uOԵAߊ+P[ 2&]u֟|_D8P򌅌O_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUmyO PBF>W2IR?QbXE랶;ǮX}ZT54])cǏ 23=,_PfSj?)zSq<5Ay{l3`ז'k>Vx&=jm%2pF:s|Ynmsy~+?=Ȟ۷쵢^'7QFXU x݂xgV DTV@O/HE ںE+5Q&uP򌅌O_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUmyO QbXE랶;ǮXñW̴ݺPBF>W2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?QbXE랶;ǥu zruM])a LE^X%KTb,"[cP>aІ9h[O~"_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUmyOT414Q`}P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUmyOT m=Q`}P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUmyOUczƉ3B*493̞]gIJC_UH6ˆ7=haU>1z t|-cn?[5pͺMٔYEY ù${N{ϭ/VwI%.a[T M;Q`RPŵ , 92$Ǫ)̄PUm|I]}VX>Y,I?R?Ͱn-$Vg?1:jr؃@O/7@7~"L]uQEfU"V?E|f̓6O{ykq6hl׈Zbkf_/akm>{?> ovy89_|G |zZ(z;م2x+ =U/($ûY 0Ͽ(/xi"_o(Y8eu$VjLl"4?D9P򌅌O_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUmyOUczƉ3B*493̞]gIJC_UH6ˆ7=haU>1z t|-cn?[5pͺMٔYEY ù${N{ϭ/VwI%.a[Q`RP2`U۪VUD%YD!&=QNd$zTBUUvĕ~~k5e??5ē-PC< Fm}ro-1 jt w(Al șuZ}|EQIm%Y)k\>{l3`ז'k>Vx&=jm%2pF:s|Ynmsy~+?=Ȟ۷쵢^'\LEe?EY ׎/7a}Ea{O/HEd DTQ-Q"U` aqY!ȘP򌅌O_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUmyOUH6ˆ7=haU>1z t|-cn?[5pͺMٔYEY ù${N{ϭ &N.apQbXE랶;ǣ1C%Bc3lur!PBF>W2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyaІ4g."PBF>W2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyaІ=Wbw.D#PBF>W2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUmyOUH6ˆ7=haU>1z t|-cn?[5pͺMٔYEY ù${N{ϭ &N.apT515$Q`usP򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUmyOUH6ˆ7=haT605%Q`usP򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUmyOUH6ˆ7=haT556&QbXE랶;ǯlXŖc>׮PBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])a LE^X%KTb,"[cX}á zȶ8ҹ'Q`Ԏb-ICR]SP򌅌O_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,jJUnM(6/6?!_UAm~qxeOg|z/UXGܯ?ι,Ub''0v]xrәdzT,mXKUxs+Bƫe0[^uFі8]y}T]qd?cz)WUhUvf iǯW~K>8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUmyOUH6ˆ7=ha(QbXE랶;Ǥc 6YνuMPBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,)QbXE랶;Ǡ1csguMPBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,*QbXE랶;ǣ1cp秸czPBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,+QbXE랶;ǮXǷ;oPBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,,QbXE랶;ǪX*"ӦTPBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,-QbXE랶;ǩر]w1yuzPBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,.QbXE랶;ǨرHx#%<׮PBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,/QbXE랶;ǫ?#{+TPBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])a LE^X%KTb,"[c}á IO^#cB?0Qc X V' <_yD <o:q]ut1TJ<arY#Z In_~PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,1Qc X !32_!qeי68y]ER+<2BF7dNi׈BJOu?PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,2Qc X V'2> :㎶D"hӭ8au f*\7t`a]e}K+|7 a/PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,3Qc X V&[`AuBqmƚ}Bӏ4q 2nM~x]@)]eHmҳr7PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,4Qc X vx,V&[`L! dOЄ.2@'uER+<4d qG)FI\q9`n~?PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,5Qc X V' 8Cimqǜv'/'qAKo%QRl$'ZH/p/PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,6Qc X !AM:mb @mЅbd"M[m}_s _3(J((yH #n7>XPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,7Qc X V' dODȟ Nr&[t@N8,'"~xp C jV n@xclEy||_PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,8Qc X !堺"`eB˂lN;}:i[U/ҹc~G\ WdqWi+ϊ6XPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,9Qc X vy+yD\}6lAr&<-^eeȪ_s+QA>B~B:YzI%fV+, z4 PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,: Qc eO  A.,N_U/ҹMH'@7#jW26r"PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,;Qc X V'2^}DB ,O4O>O/םnKoۏ˱#ϲ쎸̒Zq7Z, }2PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,<Qc X V&[`@ubu>@"m 6>qTJ< G _|p.@ %t!9`n4PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,=Qc X V'4B܈.e t/48ۏ2"~x$jQH8ܑ0uawPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,>Qc X V&[`ByBQp/2!N~X{])cǏ 23=,?Qc X !32e2" b n"BlO}6󢊥W7yeHЉ^+e ceBp YPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,@Qc X V&[`Lx1my^umȚh-y2U/ҹ)Hș+ vBԑF@#ur7ePARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,AQc X V&[`L` o!m -'̄-:Ob~x$[ucmfX #_,  PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,BQc X V&[`MDŽblӢ|Av 'm6^ @U/ҹt# 䕠ш 9`n>PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,CQc X vy+- &Ț \q8/6_tBe~x$V `m]iHd/)[7X^?PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,DQc X V&[`Bm}mh!q]n&ul W7yvQ61n0$y2螕r/j PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,EQbXE랶;ǬXA:3qW2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])a LE^X%KTb,"[cX}á Oupӫ6kFQbXE랶;ǬX̀Fm/]SPBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])a LE^X%KTb,"[cј}á Elڶ:GQbXE랶;ǨرϢoPBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])a LE^X%KTb,"[c}á IO^#cB?HQbXE랶;ǩX}á ލ1~0PBF>W2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUmyOUH6ˆ7=haToJQbXE랶;ǮX|nտwxJPBF>W2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hԽ?9UczƉ3B*3/ǟilsf(om?}NF1z t|-cn?[5pͺMyI+d?g <6r{~ iz-&-]ῑ:)ۚ\oe=c+_T ONQ`RP_u >_qd=I/UDE9Q VaTBڮUʈHFBD%YV*!H4OdyQ Q^UʈT"[0D$[4BLz+j2Unw6)D%YWnD$2u5S*D%YjyQ Wn[wE[wn«wnTBU۪yV*[wnTBELT$Ǫ$U[wnUʷyV*!**yV*[wnTBAUnUʷyV*[*[wnUʷyQ 1P겪[wnUʷyQ VaUʷyV*[wnnUʷyV*[*[wnUʷyQ ;B z4yV*[wnTBUUnUʷyV*[@ϺԏWzB)#lO6[wnUʷyV*!*+ʷyV*[wnTBAP"UʷyV*[wf[wnUʷyV*! :}2M[wnUʷyV*!*+ʷyV*[wnTBv ATFV*[wnUʈJ UʷyV*[wȄ؝|OmUʷyV*[w[wnUʷyV*!!#1{nUʷyV*[0Wo[wvTBUTBUWdAՖjOԵAl1GkLNܶ ǫ2 *߈"e]iC?E'vfHp_`̓^Zwog3|(p[35YZ'Con-{p;qN=Wx?q)ԞSOxZG{/TyQX^tA&"2kf0>e$VjZ|}Ef(M(Y0ӬEL P򌅌O_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hԽ?9UczƉ3B*3/ǟilsf(om?}NF1z t|-cn?[5pͺMyI+d?g <6r{~ iz-&-]ῑ:)ۚ\oe=c+_OQbXE랶;ǯLP>aІ W[\GPBF>W2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?QbXE랶;ǭ P>aІ4g."_PfSj?)zSq<5AyW2IR?QbXE랶;ǩء}á ԉleȄ_PfSj?)zSq<5AyaІ?u홗,PBF>W2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyaІ) m{{\GPBF>W2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?QbXE랶;ǭLP>aІ=Wbw.D#_PfSj?)zSq<5AyaІW1ɱr!PBF>W2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?QbXE랶;ǯlX4GmNb)ez])a LE^X%KTb,"[cP>aІVć'D#_PfSj?)zSq<5AyW2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])a LE^X%KTb,"[cצ(`DCǫխD#_QbXE랶;ǨX7ɺO;TPBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])a LE^X%KTb,"[c&(`DCpD#`Qc X V؜'`Bd<y> Dθi]e U/ҹ_rYyI^}B"6+7 ϷPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,avQ`usP򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hԽ?9U>1z t|-cn?[5pͺMyI+d?g <6r{~ iz-&-]ῑ:)ۚ\oe=c+_T556bQbXE랶;ǫX7|GɃTPBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])a LE^X%KTb,"[cX}á pӋ{"D#cQc X,Kv@b0}qlBe[TJ<$mo- n#с&9`n}PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,d Qa LHk=xLU~搶@/`@EBԒ@]>ˎg=0GgR؈,k㭄kG1`-QpP/ʇBL6V |,d*_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,eQbXE랶;ǫX}á 7ۋPzPBF>W2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyaІ/6N<PBF>W2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyaІ=n/_]"B?PBF>W2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])a LE^X%KTb,"[c&(`DCpD#iQbXE랶;ǮXƃ~~o^PBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])a LE^X%KTb,"[c(`DCn\GjQc X,V<dBA[l!!yӂt.<1TJ<+.6iِ+b, }ךPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,kQc X,V<]hBv @d/pAL:ӎ[p1TJ<#nK(cVt da偸o] PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,lQc X,V<[M &Zqǚm:ib[yq*\灶H8#@̯FfG^~Xp^t?PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,mQc X a<ao6Ӯ~X{])cǏ 23=,nQc X !姀 n!}iȢ M8O_uǜ R+ӲFr7PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,oQc X,@1X@! qy8Zn!2-Ȫ_s h"f1H6ܖIF_Gr7uPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,pQc X V&M@݈/2&BӁiDqt8W7ym ]#9nJ̒Weyd7~X PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,qQc Ya[`MOq d 4M:m4_s3$f@B]x+Fd}<̿, σPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,rQc X MȚhOC\d 8댁:'E/ δ.~x$) 7(}}Zx23$9`n]zPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,sQc X vxL MȚh,3@ l i2& <ζێ 7Ko7tn+R7/JoKr7aPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,tQc X MȚl- @e46LN8N@!W7yyFEhB}G ێB>͍p},PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,uQc X,Vȼ&dBA[l!!yӂt.<1TJ< ǃ,2m%~7N6@ c, {ۻPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,vQc X,Vȼ&]hBv @d/pAL:ӎ[p1TJ< J2I%GAA@Qpކ;PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,wQc X,Vȼ&[M &Zqǚm:ib[yq*\ҸdHHYcIi偸_&p=PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,xQc X,Vȼ&&}@ &q Ȝp4 ZBTJ< aiY ܲFF7C(6X4PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,yQc X,Vȼ&N@ "iy}>t U/ҹJY]|$y@@9F,mؾXOPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,zQc X,Vȼ&]q.i <ED8^ R+<>F 'J쏐o?ὅgPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,{Qc X, vD"bE04'q؂}mu-B\eW7yyD +'Y#?z7pPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,| Qc X, vD"bE0uD_u8L,ER+<:Hn4r IZ`PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,}Qc X,Vȼ&N@ AqmuU/ҹI RFG|A|챴D|7 qPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,~Qc X,Vȼ&i A[euux@me@_s ILFB\/,n|7 05PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,QbXE랶;ǯ,Xa(uMPBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])a LE^X%KTb,"[c&(`DCpD#Q`RPŵ4JMTcSa%GRKaPV?"e]i%v@P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hԽ?9U>1z t|-cn?[5pͺMyI+d?g <6r{~ iz-&-]ῑ:)ۚ\oe=c+_T438QbXE랶;d_b T\71Tй%yFDPBF>W2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X W41[yq!<Ai -Q>_| u]quǢ~x@p#$c|B#9`nPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X V^\i u pO6ۯZu؂Mm돾뮴~x \P/ƊMFdbpu8PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X ..iwbLM&~ DțON6/8ER+<4WJ2^ln l~X_PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X .iwbL.Ap !2؞ O:Yl(_s27c uh2$#qX롻}PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X .iwbL.<" Ap _m[}Yl/o2W7y#Һ϶ 뎕C<29`nPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,rQ`RPi_>؛y8¡=L!I|h=CԒODE9*!WnD$[d!Un«w'E[*D*JIQJTBUUn-b&=QJR*yQ Q^UʈEHn«Unl:n[UnUʈ]"UʷyQ VaUʷyV*!*[wnUʷyV*!"٦*cm*UʷyV*[wf[wnUʷyV*!Ga yV*[wnTBTWnUʷyV*[fITIYUnUʷyV*[0[wnUʷyQ yV*[wnTBTWnUʷyV*[Ă=QE[wnUʷyV*!**yV*[wnTB dijG+=! ' UʷyV*[w[wnUʷyV*! ATFV*[wnUʈJ UʷyV*[wȄ؝|OmUʷyV*[w[wnUʷyV*![QZXT Ǫ#HwnUʷyV*D%YV*[wnUʈBdBlN't @yV*[wnUʈJUʷyV*[wؘm=yV*[wnTBU@nUʈJ}V*!WoEyWnD$[d!Un«w-UV*!*+ʷyQ qdRwf[[*D"MAjyQ Vav$P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hԽ?9T438qQ`RP]}>m^`aP?$S_@_!I|c"LzIUn-QyQ VaUʈR q"TBTWn%$ȥV*!**yQ 1P)dڌ[*D"MAjyQ VaU۪yQ 6 MTʷyQ VaڭTBU۪yV*D.ƑV*[0[wvUʷyV*[wl 1V*[wnUʈJ UʷyV*[wU[wnUʷyV*!*+ʷyV*[wnTBELT$Ǫ$yV*[wnTBUUnUʷyV*[[wnUʷyV*!*+ʷyV*[wnTBAbAP"UʷyV*[wf[wnUʷyV*!24#y瞐r HͅV*[wnUʈJUʷyV*[wT Ǫ#HwnUʷyV*D%YV*[wnUʈBdBlN't @yV*[wnUʈJUʷyV*[wݭ,H*cUʷyV*[wn«wnUʷyV*D!2!6'_[i8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hԽ?9T438rQ`RPy 1 %BRK?cBOg@]wmUʈJ TBh3nw)'FE*yQ VaUʈHiE)K$VdD%EyV*!lR #UʈJ UʈIdjUʈJnUʷyV*!v4yV*D%YV*[UnUʷyV*[fITH=yV*[wnTBUUnUʷyV*[[wnUʷyQ Q^UʷyV*[wn-b&=Q'eUʷyV*[wn«wnUʷyV*D *[wnUʷyQ Q^UʷyV*[wnv  DinUʷyV*[0[wnUʷyQu#τ<(#RF؞l*yV*[wnTBTWnUʷyV*[=QE[wnUʷyV*!**yV*[wnTB"bu>eʷyV*[wnTBTWnUʷyV*[mFibAP"UʷyV*[wf[wnUʷyV*! :}2M[wnUʷyV*!*+ʷyV*[wnTBBGbb% *[wnUʷyQ VaUʷyV*!*[]]wmUʈJ TBR: WU[*D*JIQJTBUUnwnw6)D%YUTB P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hԽ?9T438qQ`RPa6`aP?$S_@_!I|c"LzIUn-QyQ VaUʈR q"TBTWn%$ȥV*!**yQ 1P)dڌ[*D"MAjyQ VaU۪yQ 6 MTʷyQ VaڭTBU۪yV*D.ƑV*[0[wvUʷyV*[wl 1V*[wnUʈJ UʷyV*[wU[wnUʷyV*!*+ʷyV*[wnTBELT$Ǫ$yV*[wnTBUUnUʷyV*[[wnUʷyV*!*+ʷyV*[wnTBAbAP"UʷyV*[wf[wnUʷyV*!24#y瞐r HͅV*[wnUʈJUʷyV*[wT Ǫ#HwnUʷyV*D%YV*[wnUʈBdBlN't @yV*[wnUʈJUʷyV*[wݭ,H*cUʷyV*[wn«wnUʷyV*D!2!6'_[i8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hԽ?9T438rQ`RP O _e Mv1cRK?A 522$Ǫ)̄_Q juV*!"% wf[ ReʷyV*[wnTBTWnUʷyV*[KlAUʷyV*[wn]wnTBUyQ }Wo+ʻuV*!"% wf[YltyQ Q^UʈT"[0D-*D%EyV*!lR #UʈJ%v@P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hԽ?9T439rQ`RPd 8̲'yS]B zCԒO{l!M| %1s!' TBڮUʈHFBD%YV*!H4OdyQ Q^UʈT"[0D$[4BLz+j2Unw6)D%YWnD$2u5S*D%YjyQ Wn[wE[wn«wnTBU۪yV*[wnTBELT$Ǫ$U[wnUʷyV*!**yV*[wnTBAUnUʷyV*[*[wnUʷyQ 1P겪[wnUʷyQ VaUʷyV*[wnnUʷyV*[*[wnUʷyQ ;B z4yV*[wnTBUUnUʷyV*[@ϺԏWzB)#lO6[wnUʷyV*!*+ʷyV*[wnTBAP"UʷyV*[wf[wnUʷyV*! :}2M[wnUʷyV*!*+ʷyV*[wnTBv ATFV*[wnUʈJ UʷyV*[wȄ؝|OmUʷyV*[w[wnUʷyV*!!#1{nUʷyV*[0Wo[wvTBUUʈHFBD%YV*!V[)TBTWn%$ȥV*!**yQ n;ʷyQ Q^UʈEHn**!|I]P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hԽ?9T439Q`usP򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hԽ?9T569QbXE랶;ǤcQ/]SPBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])a LE^X%KTb,"[cИ}á u˷zZW")Q`RPŵ4JMTcSa%GRKaPV?"e]i%v@P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hԽ?9T439)Q`RPŵ4JMTcSa%GRKaPV?"e]i%v@P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hԽ?9T439)Q`RPŵ4JMTcSa%GRKaPV?"e]i%v@P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hԽ?9T439Q`}P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hԽ?9T "wMQ`}P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜUczƉ3B*3~p ;v[يg+N\9ʏU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ql{:I4~坷ۓx+|w,T .Q`RPZi϶&p1 %vQ VaTBiD%EyQ ڌ,J i,jJD!&=QNd$zTBUUUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0JJ??5YbI!m#u~6hiփU7Z|}YYV@dL>"p(T6.=6l{輬w,|:,~pG3$EwC'{zxxvgS N`0t8}fq= G3=oޞrY]Ϭ8/ ?.Vz($ûY 0Ͽ(/xi"_o(Y8eu$VjLl"4?D9P򌅌O_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜUczƉ3B*3~p ;v[يg+N\9ʏU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ql{:I4~坷ۓx+|w,PBF>W2IR?QbXE랶;ǭ P>aІ4g."_PfSj?)zSq<5AyW2IR?QbXE랶;ǩء}á ԉleȄ_PfSj?)zSq<5AyW2IR?QbXE랶;ǭ,P>aІ?u홗,_PfSj?)zSq<5AyW2IR?QbXE랶;ǤC%Bl$TE\G_PfSj?)zSq<5AyW2IR?QbXE랶;Ǫ}á qgзe\G_PfSj?)zSq<5AyW2IR?QbXE랶;ǭLP>aІ=Wbw.D#_PfSj?)zSq<5Ay~X{])cǏ 23=,tQ`usP򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ql{:I4~坷ۓx+|w,T569Qc X,V< ֟n 6ϴZn!2y֟}݊W7y(R4۠6 GRHVyp.?PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,PBF>W2IR?QbXE랶;ǫP>aІ/6N<_PfSj?)zSq<5AyW2IR?QbXE랶;Ǭ,P>aІ=n/_]"B?_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ql{:I4~坷ۓx+|w,T569QbXE랶;ǭXsg?|UzPBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])a LE^X%KTb,"[cP>aІ D6FB?Qc X,V<`u柈 D_hBbp i6Оeq_s^H()d-F, 9PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X M.y n!}iȢ M8O_uǜ R+~X{])cǏ 23=,Qc X vxLM.yl.ӑmZuێN@U/ҹbmq2 >c+ j6^fB9>XpPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X !姀Y}iȂ@aqq}t @_s@&e$czGd!8dx2偸_iPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,V@ ֟n 6ϴZn!2y֟}݊W7ym]fWutL2yd y|7 |7PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X V&MƂ'ǜt!Dbm\ii}DR+< 9_uq#XZ|WPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X V&MiD[m]h&Z"y m >TJ<#$eqxA(6|2r/>PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X V. lbx4.Bm:&'\qTJ<P6WY ΀#yם~YC#%2>9`n0PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X vy+@&L" o:YhQ2@lO4@ :_s emHG$rF#bu|7 ZmPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X V.eb h'd@}DAm㏸ qTJ<$q7pL˯^lr7CtOPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,Vȼ& ֟n 6ϴZn!2y֟}݊W7yt!mA,nm 6Gr7ٽPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,Vȼ&`u柈 D_hBbp i6Оeq_s pmI(rW M_9`nܾPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,Vȼ&Y]j Z]| M5-48}^t/<(_s7[t2FYC#}fHq, \PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,Vȼ&2& @o}j&8!:l.[W7y2>m=+% \Qۭ#10|7 oпPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,X evD"bE0&Zyy_im:4M>mך~*\ +r\f8R%e9`nnPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,Vȼ&- -ۮ^uM6'N۠W7y(YEA6m=)JؙnYl, Z7PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,Vȼ&]t/5@e4Aiu&|NW7yc|iI$b2 !9`nPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,Vȼ&} m>iYn&q4 R+~X{])cǏ 23=,Qc X,Vȼ&NAb}ND/Xw PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,Vȼ&˭=@y B"ie4!2'lU/ҹ`q$#z2돺R H9Z7 }PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X .iwbL.O=\D'uj'ux@p@>R+<2!,6V#d+'pPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X .iwbL.M tBtM<mtNKom@mv@b8n?(]o偸^}`PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X .iwbL.Ɯn d"i 2/eZyER+~X{])cǏ 23=,Qc X .iwbL@i B&[qheȜnی'^ KoP cόoO8R>˱]y5r7 ׿PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X .iwbL _m܈.8iY bi8ӯ}]W7ymD@(d\me>Gs偸_PPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Q`RP}m}`aP?$S / i$S_@_!I|c"LzIUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0}Q^U۪yQ (U[0D*e#uUʈJTBuD%YV*!myV*!*+ʷyQS`iTBU]D/+P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜT439Q`RPt<,ۢ`aP?$S / i$S_@_!I|c"LzIUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0}Q^U۪yQ (U[0D*e#uUʈJTBuD%YV*!myV*!*+ʷyQS`iTBU]D/+P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜT439Q`RP|!ye Mv1cRK?RK?A 522$Ǫ)̄_Q juV*!"% wf[YltyQ Q^UʈT"[0D-*D%EyV*!lR #UʈJ]wmUʈJ TBR: WU[*D*JIQJTBUUnwnw6)D%YUTB P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜT439Q`RP/L&55T ǩd=I/kd=I/4z_)ȓ2{=D/[ld*TBUUnejD%EyV*!RRN:Un«w㼫w[_)qH4V*!*?TWvTBEJ2V*!**yQ H5]Unw)'FE*yQ VaUʈ[qUʈJTB/8FwfWoQ J?P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜT439Q`RP]}>m^`aP?$S / i$S_@_!I|c"LzIUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0}Q^U۪yQ (U[0D*e#uUʈJTBuD%YV*!myV*!*+ʷyQS`iTBU]D/+P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜT439Q`RP | D¡=L!I|v_@_!I|h=CԒODE9*!WnD$[d!Un«w-UV*!*+ʷyQ qdRwf[[*D"MAjyQ VavUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0_WdP򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜT439Q`}P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜToKQ`}P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hOY÷9AUczƉ3B*3|JƝDoXsf(oz9s`*>U>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>:.LLe ٫ɼLU滎?T .Q`RP]<>/6CԒL uQ ʨ0!4ҢmFLVUD%YQ 4Hܵ% Jȓ2{=*!**_U۪yQ (U[0D*e#uUʈJTBuD%YV*!myV*!*+ʷyQS`iTBU]D%]%v@Y`f$KTO6p_\|vA}b z->ڬ+P[ 2&]u֟|_D8RzaIVhtAZq6X^Vr; B||v?O[x^d#~pm";!=Wpi<]3k'0w|dvro>w8ӏU{ɞ uL~V{ͣUToO9{gO+ =UQFXU x݂xgV DTV@O/HE ںE+5Q&uP򌅌O_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hOY÷9AUczƉ3B*3|JƝDoXsf(oz9s`*>U>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>:.LLe ٫ɼLU滎?PBF>W2IR?QbXE랶;ǯLP>aІ W[\G_PfSj?)zSq<5AyW2IR?QbXE랶;ǭ P>aІ4g."_PfSj?)zSq<5AyW2IR?QbXE랶;ǩء}á ԉleȄ_PfSj?)zSq<5AyW2IR?QbXE랶;ǭ,P>aІ?u홗,_PfSj?)zSq<5AyW2IR?QbXE랶;ǤC%Bl$TE\G_PfSj?)zSq<5AyW2IR?QbXE랶;Ǫ}á qgзe\G_PfSj?)zSq<5AyW2IR?QbXE랶;ǯ(`DC߯S8.D#_PfSj?)zSq<5AyW2IR?QbXE랶;ǭLP>aІ=Wbw.D#_PfSj?)zSq<5AyW2IR?QbXE랶;ǧXthJ7])a LE^X%KTb,"[cצ(`DCǫխD#_PfSj?)zSq<5Ay~X{])cǏ 23=,rQ`usP򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hOY÷9AU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>:.LLe ٫ɼLU滎?T569PBF>W2IR?QbXE랶;ǫX}á 7ۋPz_PfSj?)zSq<5AyW2IR?QbXE랶;ǫP>aІ/6N<_PfSj?)zSq<5AyW2IR?QbXE랶;Ǭ,P>aІ=n/_]"B?_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hOY÷9AU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>:.LLe ٫ɼLU滎?T569Qc X,V<&M M> e='iNR+< @Jlap-?+<cn@, ?PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X M..y[il.˱~X{])cǏ 23=,Qc X,Vȼ&&M M> e='iNR+<>9@ٕH6^7r7nPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X .iwbL-r ζ'Мf!>'e&z*\#u~8$^e]m^偸o_~/PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X W41[yqA[l  iZqb N6O!4`}_s܃dn2n7#ךvXXpPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,{Q`RP h B'`aP?$S / i$S_@_!I|c"LzIUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0}Q^U۪yQ (U[0D*e#uUʈJTBuD%YV*!myV*!*+ʷyQS`iTBU]D/+P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hOY÷9AU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>:.LLe ٫ɼLU滎?T439{Q`RPud,̲¡=L!I|v_@_!I|h=CԒODE9*!WnD$[d!Un«w-UV*!*+ʷyQ qdRwf[[*D"MAjyQ VavUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0_WdP򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hOY÷9AU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>:.LLe ٫ɼLU滎?T439{Q`RP]}>m^`aP?$S / i$S_@_!I|c"LzIUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0}Q^U۪yQ (U[0D*e#uUʈJTBuD%YV*!myV*!*+ʷyQS`iTBU]D/+P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hOY÷9AU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>:.LLe ٫ɼLU滎?T439|Q`RP}m}`aP?$S / i$S_@_!I|c"LzIUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0}Q^U۪yQ (U[0D*e#uUʈJTBuD%YV*!myV*!*+ʷyQS`iTBU]D/+P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hOY÷9AU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>:.LLe ٫ɼLU滎?T439{Q`RP/L&55T ǩd=I/kd=I/4z_)ȓ2{=D/[ld*TBUUnejD%EyV*!RRN:Un«w㼫w[_)qH4V*!*?TWvTBEJ2V*!**yQ H5]Unw)'FE*yQ VaUʈ[qUʈJTB/8FwfWoQ J?P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hOY÷9AU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>:.LLe ٫ɼLU滎?T439{Q`RP|!ye Mv1cRK?RK?A 522$Ǫ)̄_Q juV*!"% wf[YltyQ Q^UʈT"[0D-*D%EyV*!lR #UʈJ]wmUʈJ TBR: WU[*D*JIQJTBUUnwnw6)D%YUTB P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hOY÷9AU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>:.LLe ٫ɼLU滎?T439oQ`}P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hOY÷9AU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>:.LLe ٫ɼLU滎?TwKQ`}P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hhw9EUczƉ3B*3~st<™9{KKV=NU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ӧl{pyM kڬ+P[ 2&]u֟|_D8RzaIVhtAZq6X^Vr; B||v?O[x^d#~pm";!=Wpi<]3k'0w|dvro>w8ӏU{ɞ uL~V{ͣUToO9{gO+ =UQFXU x݂xgV DTV@O/HE ںE+5Q&uP򌅌O_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hhw9EUczƉ3B*3~st<™9{KKV=NU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ӧl{pyM kW2IR?QbXE랶;ǯLP>aІ W[\G_PfSj?)zSq<5AyW2IR?QbXE랶;ǭ P>aІ4g."_PfSj?)zSq<5AyW2IR?QbXE랶;ǩء}á ԉleȄ_PfSj?)zSq<5AyW2IR?QbXE랶;ǭ,P>aІ?u홗,_PfSj?)zSq<5AyW2IR?QbXE랶;ǤC%Bl$TE\G_PfSj?)zSq<5AyW2IR?QbXE랶;Ǫ}á qgзe\G_PfSj?)zSq<5AyW2IR?QbXE랶;ǭLP>aІ=Wbw.D#_PfSj?)zSq<5AyW2IR?QbXE랶;ǯ(`DC߯S8.D#_PfSj?)zSq<5Ay~X{])cǏ 23=,rQ`usP򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hhw9EU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ӧl{pyM kW2IR?QbXE랶;ǫX}á 7ۋPz_PfSj?)zSq<5AyW2IR?QbXE랶;ǫP>aІ/6N<_PfSj?)zSq<5AyW2IR?QbXE랶;Ǭ,P>aІ=n/_]"B?_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hhw9EU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ӧl{pyM k~X{])cǏ 23=,Qc X,V<',^]mDux DŽ&ZuOER+<2Gڐ uDoG)H^, ?{PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,V<ȄϻB\tBe|-,'qb~x#$rHVemq.@r78\PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,Vȼ&ЙD!mYmp@yER+<<]6Rm,o$as偸_m!gPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,Vȼ&',^]mDux DŽ&ZuOER+<:xb$no σ{pPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,Vȼ&ȄϻB\tBe|-,'qb~x$R7DjA46-;r70PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,Vȼ& 2@-8M!u8O_s $:VK"m@7%y̿, ZoPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,Vȼ& r A@} !6 2u R+<2GQ+RG8^]9`nbPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,Vȼ&/< "eZyǂ"u4&mU/ҹ2nV nH۱ph-wPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,Vȼ&ӌ<--YuYe>NۑTJ<#r@IJF@9#27 fFp.;PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,Vȼ&ˢO8D,&B@iZuيW7y$ePOF7r7 σ{ ;PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,Vȼ&A}m4d:i֜iAKoP<6rWY]+&Y偸_zAPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,"]1["Șm2 DmL&@pOTJ<]yD 4n =+e^ r7?PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X !\ 4n5Ƙm,.2/ !>۱TJ<:H@F϶aO# 8_(r/аPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,{Q`RPiqu¡=L!I|v_@_!I|h=CԒODE9*!WnD$[d!Un«w-UV*!*+ʷyQ qdRwf[[*D"MAjyQ VavUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0_WdP򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hhw9EU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ӧl{pyM k8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hhw9EU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ӧl{pyM k8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hhw9EU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ӧl{pyM k8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hhw9EU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ӧl{pyM k؛y8¡=L!I|v_@_!I|h=CԒODE9*!WnD$[d!Un«w-UV*!*+ʷyQ qdRwf[[*D"MAjyQ VavUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0_WdP򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hhw9EU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ӧl{pyM k8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hhw9EU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ӧl{pyM k8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hhw9ETgLQ`}P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜUczƉ3B*4r`sKs@{玝MQgU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ŕts!<5vبȧ^R泝54T .Q`RP!e_Ay1 %vQ VaTBiD%EyQ ڌ,J i,jJD!&=QNd$zTBUUUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0JJ??5YbI!m#u~6hiփU7Z|}YYV@dL>"p(T6.=6l{輬w,|:,~pG3$EwC'{zxxvgS N`0t8}fq= G3=oޞrY]Ϭ8/ ?.Vz($ûY 0Ͽ(/xi"_o(Y8eu$VjLl"4?D9P򌅌O_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜUczƉ3B*4r`sKs@{玝MQgU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ŕts!<5vبȧ^R泝54PBF>W2IR?QbXE랶;ǭ P>aІ4g."_PfSj?)zSq<5AyW2IR?QbXE랶;ǩء}á ԉleȄ_PfSj?)zSq<5AyW2IR?QbXE랶;ǭ,P>aІ?u홗,_PfSj?)zSq<5AyW2IR?QbXE랶;ǤC%Bl$TE\G_PfSj?)zSq<5AyW2IR?QbXE랶;Ǫ}á qgзe\G_PfSj?)zSq<5AyW2IR?QbXE랶;ǭLP>aІ=Wbw.D#_PfSj?)zSq<5Ay~X{])cǏ 23=,sQ`usP򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ŕts!<5vبȧ^R泝54T568PBF>W2IR?QbXE랶;ǫP>aІ/6N<_PfSj?)zSq<5AyW2IR?QbXE랶;Ǭ,P>aІ=n/_]"B?_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ŕts!<5vبȧ^R泝54T568PBF>W2IR?QbXE랶;ǭXsg?|Uz])a LE^X%KTb,"[cP>aІ D6FB?_PfSj?)zSq<5AyXo]sPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Q`Ԏb~^cJԬeiWPBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Qc X,Vȼ&d-}loiךi_sRFWYfP|",r~X{])cǏ 23=,Qc X  w]ƘƼeDB m\^y6O }<W7yz@3) )}+e 8o偸_vPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=, {Q`RPh"x. 1 %YM| %BRK?cBOg@]wmUʈJ TBR: WU[*D*JIQJTBUUnwnw6)D%YUUʈHFBD%YV*!V[)TBTWn%$ȥV*!**yQ n;ʷyQ Q^UʈEHn**!|I]P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ŕts!<5vبȧ^R泝54T439!}Q`RPt<,ۢ`aP?$S / i$S_@_!I|c"LzIUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0}Q^U۪yQ (U[0D*e#uUʈJTBuD%YV*!myV*!*+ʷyQS`iTBU]D/+P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ŕts!<5vبȧ^R泝54T439"|Q`RP|!ye Mv1cRK?RK?A 522$Ǫ)̄_Q juV*!"% wf[YltyQ Q^UʈT"[0D-*D%EyV*!lR #UʈJ]wmUʈJ TBR: WU[*D*JIQJTBUUnwnw6)D%YUTB P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ŕts!<5vبȧ^R泝54T439#|Q`RP/L&55T ǩd=I/kd=I/4z_)ȓ2{=D/[ld*TBUUnejD%EyV*!RRN:Un«w㼫w[_)qH4V*!*?TWvTBEJ2V*!**yQ H5]Unw)'FE*yQ VaUʈ[qUʈJTB/8FwfWoQ J?P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ŕts!<5vبȧ^R泝54T439$|Q`RP]}>m^`aP?$S / i$S_@_!I|c"LzIUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0}Q^U۪yQ (U[0D*e#uUʈJTBuD%YV*!myV*!*+ʷyQS`iTBU]D/+P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ŕts!<5vبȧ^R泝54T439%|Q`RPi_>؛y8¡=L!I|v_@_!I|h=CԒODE9*!WnD$[d!Un«w-UV*!*+ʷyQ qdRwf[[*D"MAjyQ VavUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0_WdP򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ŕts!<5vبȧ^R泝54T439&pQ`}P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hۜU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?>Ŕts!<5vبȧ^R泝54T'NQ`}P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hK9yUczƉ3B*6sadB3Ӟc~1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?0iñ·fGIqȧ^R泝54T .(Q`RPì-?$STB!**M4*![QQ VaTB %"-IB2$Ǫ)̄JJ vTBEJ2V*!**yQ H5]Unw)'FE*yQ VaUʈ[qUʈJTB/8FwfWoQ WoI]}VX>Y,I?R?Ͱn-1:jr؃@O/7@7~"L]uQEfU"V?E|f̓/x=Ń_6.b5[H|doU/O|.q=Ͻ]4^g=aShUfg-^+YG=>"Uq?QEd^8w`0E->Q"U2 E+5QGlDTI-@}Ɲg("`P򌅌O_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hK9yUczƉ3B*6sadB3Ӟc~1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?0iñ·fGIqȧ^R泝54)PBF>W2IR?QbXE랶;ǯLP>aІ W[\G_PfSj?)zSq<5AyW2IR?QbXE랶;ǭ P>aІ4g."_PfSj?)zSq<5AyW2IR?QbXE랶;ǩء}á ԉleȄ_PfSj?)zSq<5AyW2IR?QbXE랶;ǭ,P>aІ?u홗,_PfSj?)zSq<5AyW2IR?QbXE랶;ǤC%Bl$TE\G_PfSj?)zSq<5AyW2IR?QbXE랶;Ǫ}á qgзe\G_PfSj?)zSq<5AyW2IR?QbXE랶;ǯ(`DC߯S8.D#_PfSj?)zSq<5AyW2IR?QbXE랶;ǭLP>aІ=Wbw.D#_PfSj?)zSq<5AyW2IR?QbXE랶;ǧXthJ7])a LE^X%KTb,"[cצ(`DCǫխD#_PfSj?)zSq<5Ay~X{])cǏ 23=,3PBF>W2IR?QbXE랶;ǫX}á 7ۋPz_PfSj?)zSq<5AyW2IR?QbXE랶;ǫP>aІ/6N<_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hK9yU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?0iñ·fGIqȧ^R泝54T5686PBF>W2IR?QbXE랶;Ǭ,P>aІ=n/_]"B?_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hK9yU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?0iñ·fGIqȧ^R泝54T5688Qc X .iwbL Bj Mu}D@ud 6]tN_sc$i)c mI%e$, znPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,9Qc X .iwbL yۈ.8uy}B/8mAKo'xA9G+Z|$9`n޻PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,:|Q`RP | D¡=L!I|v_@_!I|h=CԒODE9*!WnD$[d!Un«w-UV*!*+ʷyQ qdRwf[[*D"MAjyQ VavUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0_WdP򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hK9yU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?0iñ·fGIqȧ^R泝54T439;|Q`RPˢ/!  1 %YM| %BRK?cBOg@]wmUʈJ TBR: WU[*D*JIQJTBUUnwnw6)D%YUUʈHFBD%YV*!V[)TBTWn%$ȥV*!**yQ n;ʷyQ Q^UʈEHn**!|I]P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hK9yU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?0iñ·fGIqȧ^R泝54T439<}Q`RP]}>m^`aP?$S / i$S_@_!I|c"LzIUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0}Q^U۪yQ (U[0D*e#uUʈJTBuD%YV*!myV*!*+ʷyQS`iTBU]D/+P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hK9yU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?0iñ·fGIqȧ^R泝54T439=|Q`RPmB o@¡=L!I|v_@_!I|h=CԒODE9*!WnD$[d!Un«w-UV*!*+ʷyQ qdRwf[[*D"MAjyQ VavUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0_WdP򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hK9yU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?0iñ·fGIqȧ^R泝54T439>~Q`RP}m}`aP?$S / i$S_@_!I|c"LzIUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0}Q^U۪yQ (U[0D*e#uUʈJTBuD%YV*!myV*!*+ʷyQS`iTBU]D/+P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hK9yU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?0iñ·fGIqȧ^R泝54T439?~Q`RPt<,ۢ`aP?$S / i$S_@_!I|c"LzIUn-QyQ VaUʈUGAw[II82)UʈJ TBێTBTWn| 5[0}Q^U۪yQ (U[0D*e#uUʈJTBuD%YV*!myV*!*+ʷyQS`iTBU]D/+P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hK9yU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ?0iñ·fGIqȧ^R泝54T439@Q`}P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hK9yTAQ`}Ƅk R I䶂 g[;$+P[ 2&]u֟|_D8RzaIVhtAZq6X^Vr; B||v?O[x^d#~pm";!=Wpi<]3k'0w|dvro>w8ӏU{ɞ uL~V{ͣUToO9{gO+ v(~"A;_Uo "_o(Yi"6[W_Ef$ >NP򌅌O_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hK9yBQbXE랶;ǭP>aІ el~%r!PBF>W2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hý9yUczƂï{4_iU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ5ѲZ6o'kToEQcMT%v@Y`f$KTO6p_\|vA}b z->ڬ+P[ 2&]u֟|_D8RzaIVhtAZq6X^Vr; B||v?O[x^d#~pm";!=Wpi<]3k'0w|dvro>w8ӏU{ɞ uL~V{ͣUToO9{gO+ =U|Ee?EY ׎/7a}Ea{O/HEd DTQ-Q"U` aqY!ȘP򌅌O_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hý9yUczƂï{4_iU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ5ѲZ6o'kFXQa%> B՛h^cKWPd,g?Uz_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,UnM(6/6?!_UAm~qxeOg|z/UXGܯ?ι,Ub''0v]xrәdzT,mXKUxs+Bƫe0[^uFі8]y}T]qd?cz)WUhUvf iǯW~K>8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hý9yU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ5ѲZ6o'kGQc X,"\ -q"lO/6BqMKo#"xGIDPK27 8PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,HQbXE랶;Ǫر4̅oPBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])a LE^X%KTb,"[c&(`DCpD#IQbXE랶;ǬlXǭ=9uMPBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])a LE^X%KTb,"[cP>aІ9h[O~"JPARKRVaWyc$/QSQc X,\M1X&mr -inD!l lqTJ< .I+7q6hlr7ڼ])cǏ 23=,_PfSj?)zSq<5Ay~X{])cǏ 23=,LQc X,W  :bhDe۱l L@'N<δKo+e[hy[ e2? ]/PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,MQc X,V&M2݈ m>ЁN Ȫ_s[e|O<݌# h/Q  @PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,NQc X,V&M^ bl<'_q^f'p  ̂*\瀭F`J,:- x, yPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,OQc X,V&M~X{])cǏ 23=,PQc X,V&MOよ @O.26ZiBЛϳKo$Ɲ(#2󒁷 ~XtPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,QQc X,V&M2\qo8}>m 2&Yy~x#yݑ Prm2>X7PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,RQc X, q4MȚi@' @2֟}Ȟ} M2Л_r*\瀣,J!J'+qceBGr/ PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,SQc X,M ``/d1lBd,!dO8ApO 6TJ<+FFJFAFcIc9`nvPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,TQc X,@b4"h -8@~ -́MyMӭ4N^W7yIB1>܁N mVV"2I>Xp~X{])cǏ 23=,UQc X,"\ .&bhD~X{])cǏ 23=,VQc X,V&MN"\tO B'u@M:U/ҹJ` [m J _zA7'p-PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,WQc X,V&Mք'@ o4!4ӑ:ۭ8m}W7y2"my_ +  u偸_{ `PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,XQc X,V&MBf&,Bum2_txTJ<2G FHqy_, {йPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,YQc X, q4MȚ M 2κˎ4eby_s ZC$l@62W1Vdg偸_h PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,Z Qc X,M ``.@ 2؄ =^ 6KoۃbmLY϶61I >XoӟPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,[Qc X,V&M q?@u>uȄ؝D`eER+<7,JͶ9%$nH+o:7 PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,\Qc X,V&M]oD m]@/ -_s0A#rI%hF'偸oBAPARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,]Qc X,V&Mb]|.~X{])cǏ 23=,^PBF>W2IR?QbXE랶;ǨرHӗOr-B_])cǏ 23=,_PfSj?)zSq<5AyW2IR?QbXE랶;Ǧi n])cǏ 23=,_PfSj?)zSq<5Ay~X{])cǏ 23=,dPARKRVaWyc$/QSQc X,V<Az -<}yiǸO/2_sNIV唲 DW9`ne])cǏ 23=,_PfSj?)zSq<5AyW2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])a LE^X%KTb,"[c&(`DCpD#gPBF>W2IR?QbXE랶;ǭX.䳽W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{iQa%> B՛h^cK.P2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,jQa%> B՛h^cKP4Gr_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,kQc X,-큊@m8lM'r! `@[eۊW7yQF1HG9 m2qo_, PARKRVaWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,lQbXE랶;ǥ}á VKW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay B՛h^cKP!ꨤJ򌅌EO_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,nQbXE랶;ǭX>qۧoPBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{oQbXE랶;ǩV"7PBF>W2IR?_9-KpZ@_PfSj?)zSq<5Ay~X{pNQaJOE% WdP򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hý9yU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ5ѲZ6o'kT982qQ`JYLj`aP?x$.2۠k㱤Ɵi6:u8{=EԾm_|A t WdP򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hý9yU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ5ѲZ6o'kT441rQ`JYLj`aP?x$ 2w㱤Ɵi6:u8{=EԾm_|A t WdP򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hý9yU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ5ѲZ6o'kT441sQbXE랶;g=nb T\716eoPBF>W2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5AyW2IR?_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hý9yU>1z t|-cn?[5pͺMyI62wyo4t9bt=,0y4C,)ɫ5ѲZ6o'kTwzQbqȅjLqpNZqD}yR7PARKRWyc$/QS_9-KpZ@_PfSj?)zSq<5Ay~X{])cǏ 23=,{Q`}P򌅌O_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hý9yT |GPŒbIƕ rQ>bh$㪢q.O/v$1a|HD$ptjA }9 [:[~(XRw?HL]uslI@ 2뮴BLNI&9i$qj?_HXD])cǏ 23=,_PfSj?)zSq<5Aybh$㪢q.O/v$1a|HD$ptjA }9 [:[~(XRw?HL]uslI@ 2뮴BLNI&9i$qj?_HXDPŒbIƕ r_K)cǏ 23=?_9-KpZ@_PfSj?)zSq<5Ay8s/Ͼ4.qvbwQ'N<}gg?g>"c#ߢUlJUH6ˆ7=hý9y~1PŒbIƕ rQ(bh$㪢q.O/v$1a|HD$pK<J _\|WKaQ?R'xi șuZ}|w1|ȭ4.O/$$j"o 1:@='V%dI])cǏ 23=,_PfSj?)zSq<5Ay8=blKrim/{t=&x_>]q?S.m|oVqc&21Ϟ (T ?golang-github-marten-seemann-qpack-0.5.1/example/main.go000066400000000000000000000020571505114337600231700ustar00rootroot00000000000000package main import ( "encoding/binary" "errors" "fmt" "io" "os" "github.com/quic-go/qpack" ) func main() { file, err := os.Open("example/fb-req-hq.out.0.0.0") if err != nil { panic(err) } dec := qpack.NewDecoder(emitFunc) for { in, err := decodeInput(file) if err != nil { panic(err) } fmt.Printf("\nRequest on stream %d:\n", in.streamID) dec.Write(in.data) } } func emitFunc(hf qpack.HeaderField) { fmt.Printf("%#v\n", hf) } type input struct { streamID uint64 data []byte } func decodeInput(r io.Reader) (*input, error) { prefix := make([]byte, 12) if _, err := io.ReadFull(r, prefix); err != nil { return nil, errors.New("insufficient data for prefix") } streamID := binary.BigEndian.Uint64(prefix[:8]) length := binary.BigEndian.Uint32(prefix[8:12]) if length > (1 << 15) { return nil, errors.New("input too long") } data := make([]byte, int(length)) if _, err := io.ReadFull(r, data); err != nil { return nil, errors.New("incomplete data") } return &input{ streamID: streamID, data: data, }, nil } golang-github-marten-seemann-qpack-0.5.1/fuzzing/000077500000000000000000000000001505114337600217525ustar00rootroot00000000000000golang-github-marten-seemann-qpack-0.5.1/fuzzing/fuzz.go000066400000000000000000000025721505114337600233050ustar00rootroot00000000000000package qpack import ( "bytes" "fmt" "reflect" "github.com/quic-go/qpack" ) func Fuzz(data []byte) int { if len(data) < 1 { return 0 } chunkLen := int(data[0]) + 1 data = data[1:] fields, err := qpack.NewDecoder(nil).DecodeFull(data) if err != nil { return 0 } if len(fields) == 0 { return 0 } var writtenFields []qpack.HeaderField decoder := qpack.NewDecoder(func(hf qpack.HeaderField) { writtenFields = append(writtenFields, hf) }) for len(data) > 0 { var chunk []byte if chunkLen <= len(data) { chunk = data[:chunkLen] data = data[chunkLen:] } else { chunk = data data = nil } n, err := decoder.Write(chunk) if err != nil { return 0 } if n != len(chunk) { panic("len error") } } if !reflect.DeepEqual(fields, writtenFields) { fmt.Printf("%#v vs %#v", fields, writtenFields) panic("Write() and DecodeFull() produced different results") } buf := &bytes.Buffer{} encoder := qpack.NewEncoder(buf) for _, hf := range fields { if err := encoder.WriteField(hf); err != nil { panic(err) } } if err := encoder.Close(); err != nil { panic(err) } encodedFields, err := qpack.NewDecoder(nil).DecodeFull(buf.Bytes()) if err != nil { fmt.Printf("Fields: %#v\n", fields) panic(err) } if !reflect.DeepEqual(fields, encodedFields) { fmt.Printf("%#v vs %#v", fields, encodedFields) panic("unequal") } return 0 } golang-github-marten-seemann-qpack-0.5.1/go.mod000066400000000000000000000004071505114337600213650ustar00rootroot00000000000000module github.com/quic-go/qpack go 1.22 require ( github.com/stretchr/testify v1.9.0 golang.org/x/net v0.28.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) golang-github-marten-seemann-qpack-0.5.1/go.sum000066400000000000000000000020121505114337600214040ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-github-marten-seemann-qpack-0.5.1/header_field.go000066400000000000000000000007351505114337600232050ustar00rootroot00000000000000package qpack // A HeaderField is a name-value pair. Both the name and value are // treated as opaque sequences of octets. type HeaderField struct { Name string Value string } // IsPseudo reports whether the header field is an HTTP3 pseudo header. // That is, it reports whether it starts with a colon. // It is not otherwise guaranteed to be a valid pseudo header field, // though. func (hf HeaderField) IsPseudo() bool { return len(hf.Name) != 0 && hf.Name[0] == ':' } golang-github-marten-seemann-qpack-0.5.1/header_field_test.go000066400000000000000000000010321505114337600242330ustar00rootroot00000000000000package qpack import ( "testing" "github.com/stretchr/testify/require" ) func TestHeaderFieldIsPseudo(t *testing.T) { t.Run("Pseudo headers", func(t *testing.T) { require.True(t, (HeaderField{Name: ":status"}).IsPseudo()) require.True(t, (HeaderField{Name: ":authority"}).IsPseudo()) require.True(t, (HeaderField{Name: ":foobar"}).IsPseudo()) }) t.Run("Non-pseudo headers", func(t *testing.T) { require.False(t, (HeaderField{Name: "status"}).IsPseudo()) require.False(t, (HeaderField{Name: "foobar"}).IsPseudo()) }) } golang-github-marten-seemann-qpack-0.5.1/integrationtests/000077500000000000000000000000001505114337600236645ustar00rootroot00000000000000golang-github-marten-seemann-qpack-0.5.1/integrationtests/interop/000077500000000000000000000000001505114337600253445ustar00rootroot00000000000000golang-github-marten-seemann-qpack-0.5.1/integrationtests/interop/interop_test.go000066400000000000000000000073071505114337600304210ustar00rootroot00000000000000package interop import ( "bufio" "encoding/binary" "fmt" "io" "log" "os" "path" "path/filepath" "runtime" "strings" "testing" "github.com/quic-go/qpack" "github.com/stretchr/testify/require" ) type request struct { headers []qpack.HeaderField } type qif struct { requests []request } var qifs map[string]qif func init() { qifs = make(map[string]qif) readQIFs() } func readQIFs() { qifDir := currentDir() + "/qifs/qifs" if err := filepath.Walk(qifDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } _, filename := filepath.Split(path) name := filename[:len(filename)-len(filepath.Ext(filename))] file, err := os.Open(path) if err != nil { return err } defer file.Close() requests := parseRequests(file) qifs[name] = qif{requests: requests} return nil }); err != nil { log.Fatal(err) } } func parseRequests(r io.Reader) []request { lr := bufio.NewReader(r) var reqs []request for { headers, done := parseRequest(lr) if done { break } reqs = append(reqs, request{headers}) } return reqs } func parseRequest(lr *bufio.Reader) (headers []qpack.HeaderField, done bool) { for { line, isPrefix, err := lr.ReadLine() if err == io.EOF { return headers, true } if err != nil { return nil, true } if isPrefix { return nil, true } if len(line) == 0 { break } split := strings.Split(string(line), "\t") if len(split) != 1 && len(split) != 2 { return nil, true } name := split[0] var val string if len(split) == 2 { val = split[1] } headers = append(headers, qpack.HeaderField{Name: name, Value: val}) } return headers, false } func currentDir() string { _, filename, _, ok := runtime.Caller(0) if !ok { panic("Failed to get current frame") } return path.Dir(filename) } func findFiles() []string { var files []string encodedDir := currentDir() + "/qifs/encoded/qpack-06/" filepath.Walk(encodedDir, func(path string, info os.FileInfo, err error) error { if info.IsDir() { return nil } _, file := filepath.Split(path) split := strings.Split(file, ".") tableSize := split[len(split)-3] if tableSize == "0" { files = append(files, path) } return nil }) return files } func parseInput(r io.Reader) (uint64, []byte) { prefix := make([]byte, 12) if _, err := io.ReadFull(r, prefix); err != nil { return 0, nil } streamID := binary.BigEndian.Uint64(prefix[:8]) length := binary.BigEndian.Uint32(prefix[8:12]) if length > 1<<15 { return 0, nil } data := make([]byte, int(length)) if _, err := io.ReadFull(r, data); err != nil { return 0, nil } return streamID, data } func TestInteropDecodingEncodedFiles(t *testing.T) { filenames := findFiles() for _, path := range filenames { fpath, filename := filepath.Split(path) prettyPath := path[len(filepath.Dir(filepath.Dir(filepath.Dir(fpath))))+1:] t.Run(fmt.Sprintf("Decoding_%s", prettyPath), func(t *testing.T) { qif, ok := qifs[strings.Split(filename, ".")[0]] require.True(t, ok) file, err := os.Open(path) require.NoError(t, err) defer file.Close() var numRequests, numHeaderFields int require.NotEmpty(t, qif.requests) var headers []qpack.HeaderField decoder := qpack.NewDecoder(func(hf qpack.HeaderField) { headers = append(headers, hf) }) for _, req := range qif.requests { _, data := parseInput(file) require.NotNil(t, data) _, err = decoder.Write(data) require.NoError(t, err) require.Equal(t, req.headers, headers) numRequests++ numHeaderFields += len(headers) headers = nil decoder.Close() } t.Logf("Decoded %d requests containing %d header fields.", len(qif.requests), numHeaderFields) }) } } golang-github-marten-seemann-qpack-0.5.1/integrationtests/self/000077500000000000000000000000001505114337600246155ustar00rootroot00000000000000golang-github-marten-seemann-qpack-0.5.1/integrationtests/self/integration_test.go000066400000000000000000000137161505114337600305360ustar00rootroot00000000000000package self import ( "bytes" "math/rand/v2" "testing" _ "unsafe" // for go:linkname "github.com/quic-go/qpack" "github.com/stretchr/testify/require" ) var staticTable []qpack.HeaderField //go:linkname getStaticTable github.com/quic-go/qpack.getStaticTable func getStaticTable() []qpack.HeaderField func init() { staticTable = getStaticTable() } func randomString(l int) string { const charset = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" s := make([]byte, l) for i := range s { s[i] = charset[rand.IntN(len(charset))] } return string(s) } func getEncoder() (*qpack.Encoder, *bytes.Buffer) { output := &bytes.Buffer{} return qpack.NewEncoder(output), output } func TestEncodingAndDecodingSingleHeaderField(t *testing.T) { hf := qpack.HeaderField{ Name: randomString(15), Value: randomString(15), } encoder, output := getEncoder() require.NoError(t, encoder.WriteField(hf)) headerFields, err := qpack.NewDecoder(nil).DecodeFull(output.Bytes()) require.NoError(t, err) require.Equal(t, []qpack.HeaderField{hf}, headerFields) } func TestEncodingAndDecodingMultipleHeaderFields(t *testing.T) { hfs := []qpack.HeaderField{ {Name: "foo", Value: "bar"}, {Name: "lorem", Value: "ipsum"}, {Name: randomString(15), Value: randomString(20)}, } encoder, output := getEncoder() for _, hf := range hfs { require.NoError(t, encoder.WriteField(hf)) } headerFields, err := qpack.NewDecoder(nil).DecodeFull(output.Bytes()) require.NoError(t, err) require.Equal(t, hfs, headerFields) } func TestEncodingAndDecodingMultipleRequests(t *testing.T) { hfs1 := []qpack.HeaderField{{Name: "foo", Value: "bar"}} hfs2 := []qpack.HeaderField{ {Name: "lorem", Value: "ipsum"}, {Name: randomString(15), Value: randomString(20)}, } encoder, output := getEncoder() for _, hf := range hfs1 { require.NoError(t, encoder.WriteField(hf)) } req1 := append([]byte{}, output.Bytes()...) output.Reset() for _, hf := range hfs2 { require.NoError(t, encoder.WriteField(hf)) } req2 := append([]byte{}, output.Bytes()...) var headerFields []qpack.HeaderField decoder := qpack.NewDecoder(func(hf qpack.HeaderField) { headerFields = append(headerFields, hf) }) _, err := decoder.Write(req1) require.NoError(t, err) require.Equal(t, hfs1, headerFields) headerFields = nil _, err = decoder.Write(req2) require.NoError(t, err) require.Equal(t, hfs2, headerFields) } // replace one character by a random character at a random position func replaceRandomCharacter(s string) string { pos := rand.IntN(len(s)) new := s[:pos] for { if c := randomString(1); c != string(s[pos]) { new += c break } } new += s[pos+1:] return new } func check(t *testing.T, encoded []byte, hf qpack.HeaderField) { t.Helper() headerFields, err := qpack.NewDecoder(nil).DecodeFull(encoded) require.NoError(t, err) require.Len(t, headerFields, 1) require.Equal(t, hf, headerFields[0]) } func TestUsingStaticTableForFieldNamesWithoutValues(t *testing.T) { var hf qpack.HeaderField for { if entry := staticTable[rand.IntN(len(staticTable))]; len(entry.Value) == 0 { hf = qpack.HeaderField{Name: entry.Name} break } } encoder, output := getEncoder() require.NoError(t, encoder.WriteField(hf)) encodedLen := output.Len() check(t, output.Bytes(), hf) encoder, output = getEncoder() oldName := hf.Name hf.Name = replaceRandomCharacter(hf.Name) require.NoError(t, encoder.WriteField(hf)) t.Logf("Encoding field name:\n\t%s: %d bytes\n\t%s: %d bytes\n", oldName, encodedLen, hf.Name, output.Len()) require.Greater(t, output.Len(), encodedLen) } func TestUsingStaticTableForFieldNamesWithCustomValues(t *testing.T) { var hf qpack.HeaderField for { if entry := staticTable[rand.IntN(len(staticTable))]; len(entry.Value) == 0 { hf = qpack.HeaderField{ Name: entry.Name, Value: randomString(5), } break } } encoder, output := getEncoder() require.NoError(t, encoder.WriteField(hf)) encodedLen := output.Len() check(t, output.Bytes(), hf) encoder, output = getEncoder() oldName := hf.Name hf.Name = replaceRandomCharacter(hf.Name) require.NoError(t, encoder.WriteField(hf)) t.Logf("Encoding field name:\n\t%s: %d bytes\n\t%s: %d bytes", oldName, encodedLen, hf.Name, output.Len()) require.Greater(t, output.Len(), encodedLen) } func TestStaticTableForFieldNamesWithValues(t *testing.T) { var hf qpack.HeaderField for { // Only use values with at least 2 characters. // This makes sure that Huffman encoding doesn't compress them as much as encoding it using the static table would. if entry := staticTable[rand.IntN(len(staticTable))]; len(entry.Value) > 1 { hf = qpack.HeaderField{ Name: entry.Name, Value: randomString(20), } break } } encoder, output := getEncoder() require.NoError(t, encoder.WriteField(hf)) encodedLen := output.Len() check(t, output.Bytes(), hf) encoder, output = getEncoder() oldName := hf.Name hf.Name = replaceRandomCharacter(hf.Name) require.NoError(t, encoder.WriteField(hf)) t.Logf("Encoding field name:\n\t%s: %d bytes\n\t%s: %d bytes", oldName, encodedLen, hf.Name, output.Len()) require.Greater(t, output.Len(), encodedLen) } func TestStaticTableForFieldValues(t *testing.T) { var hf qpack.HeaderField for { // Only use values with at least 2 characters. // This makes sure that Huffman encoding doesn't compress them as much as encoding it using the static table would. if entry := staticTable[rand.IntN(len(staticTable))]; len(entry.Value) > 1 { hf = qpack.HeaderField{ Name: entry.Name, Value: entry.Value, } break } } encoder, output := getEncoder() require.NoError(t, encoder.WriteField(hf)) encodedLen := output.Len() check(t, output.Bytes(), hf) encoder, output = getEncoder() oldValue := hf.Value hf.Value = replaceRandomCharacter(hf.Value) require.NoError(t, encoder.WriteField(hf)) t.Logf( "Encoding field value:\n\t%s: %s -> %d bytes\n\t%s: %s -> %d bytes", hf.Name, oldValue, encodedLen, hf.Name, hf.Value, output.Len(), ) require.Greater(t, output.Len(), encodedLen) } golang-github-marten-seemann-qpack-0.5.1/static_table.go000066400000000000000000000216341505114337600232510ustar00rootroot00000000000000package qpack var staticTableEntries = [...]HeaderField{ {Name: ":authority"}, {Name: ":path", Value: "/"}, {Name: "age", Value: "0"}, {Name: "content-disposition"}, {Name: "content-length", Value: "0"}, {Name: "cookie"}, {Name: "date"}, {Name: "etag"}, {Name: "if-modified-since"}, {Name: "if-none-match"}, {Name: "last-modified"}, {Name: "link"}, {Name: "location"}, {Name: "referer"}, {Name: "set-cookie"}, {Name: ":method", Value: "CONNECT"}, {Name: ":method", Value: "DELETE"}, {Name: ":method", Value: "GET"}, {Name: ":method", Value: "HEAD"}, {Name: ":method", Value: "OPTIONS"}, {Name: ":method", Value: "POST"}, {Name: ":method", Value: "PUT"}, {Name: ":scheme", Value: "http"}, {Name: ":scheme", Value: "https"}, {Name: ":status", Value: "103"}, {Name: ":status", Value: "200"}, {Name: ":status", Value: "304"}, {Name: ":status", Value: "404"}, {Name: ":status", Value: "503"}, {Name: "accept", Value: "*/*"}, {Name: "accept", Value: "application/dns-message"}, {Name: "accept-encoding", Value: "gzip, deflate, br"}, {Name: "accept-ranges", Value: "bytes"}, {Name: "access-control-allow-headers", Value: "cache-control"}, {Name: "access-control-allow-headers", Value: "content-type"}, {Name: "access-control-allow-origin", Value: "*"}, {Name: "cache-control", Value: "max-age=0"}, {Name: "cache-control", Value: "max-age=2592000"}, {Name: "cache-control", Value: "max-age=604800"}, {Name: "cache-control", Value: "no-cache"}, {Name: "cache-control", Value: "no-store"}, {Name: "cache-control", Value: "public, max-age=31536000"}, {Name: "content-encoding", Value: "br"}, {Name: "content-encoding", Value: "gzip"}, {Name: "content-type", Value: "application/dns-message"}, {Name: "content-type", Value: "application/javascript"}, {Name: "content-type", Value: "application/json"}, {Name: "content-type", Value: "application/x-www-form-urlencoded"}, {Name: "content-type", Value: "image/gif"}, {Name: "content-type", Value: "image/jpeg"}, {Name: "content-type", Value: "image/png"}, {Name: "content-type", Value: "text/css"}, {Name: "content-type", Value: "text/html; charset=utf-8"}, {Name: "content-type", Value: "text/plain"}, {Name: "content-type", Value: "text/plain;charset=utf-8"}, {Name: "range", Value: "bytes=0-"}, {Name: "strict-transport-security", Value: "max-age=31536000"}, {Name: "strict-transport-security", Value: "max-age=31536000; includesubdomains"}, {Name: "strict-transport-security", Value: "max-age=31536000; includesubdomains; preload"}, {Name: "vary", Value: "accept-encoding"}, {Name: "vary", Value: "origin"}, {Name: "x-content-type-options", Value: "nosniff"}, {Name: "x-xss-protection", Value: "1; mode=block"}, {Name: ":status", Value: "100"}, {Name: ":status", Value: "204"}, {Name: ":status", Value: "206"}, {Name: ":status", Value: "302"}, {Name: ":status", Value: "400"}, {Name: ":status", Value: "403"}, {Name: ":status", Value: "421"}, {Name: ":status", Value: "425"}, {Name: ":status", Value: "500"}, {Name: "accept-language"}, {Name: "access-control-allow-credentials", Value: "FALSE"}, {Name: "access-control-allow-credentials", Value: "TRUE"}, {Name: "access-control-allow-headers", Value: "*"}, {Name: "access-control-allow-methods", Value: "get"}, {Name: "access-control-allow-methods", Value: "get, post, options"}, {Name: "access-control-allow-methods", Value: "options"}, {Name: "access-control-expose-headers", Value: "content-length"}, {Name: "access-control-request-headers", Value: "content-type"}, {Name: "access-control-request-method", Value: "get"}, {Name: "access-control-request-method", Value: "post"}, {Name: "alt-svc", Value: "clear"}, {Name: "authorization"}, {Name: "content-security-policy", Value: "script-src 'none'; object-src 'none'; base-uri 'none'"}, {Name: "early-data", Value: "1"}, {Name: "expect-ct"}, {Name: "forwarded"}, {Name: "if-range"}, {Name: "origin"}, {Name: "purpose", Value: "prefetch"}, {Name: "server"}, {Name: "timing-allow-origin", Value: "*"}, {Name: "upgrade-insecure-requests", Value: "1"}, {Name: "user-agent"}, {Name: "x-forwarded-for"}, {Name: "x-frame-options", Value: "deny"}, {Name: "x-frame-options", Value: "sameorigin"}, } // Only needed for tests. // use go:linkname to retrieve the static table. // //nolint:deadcode,unused func getStaticTable() []HeaderField { return staticTableEntries[:] } type indexAndValues struct { idx uint8 values map[string]uint8 } // A map of the header names from the static table to their index in the table. // This is used by the encoder to quickly find if a header is in the static table // and what value should be used to encode it. // There's a second level of mapping for the headers that have some predefined // values in the static table. var encoderMap = map[string]indexAndValues{ ":authority": {0, nil}, ":path": {1, map[string]uint8{"/": 1}}, "age": {2, map[string]uint8{"0": 2}}, "content-disposition": {3, nil}, "content-length": {4, map[string]uint8{"0": 4}}, "cookie": {5, nil}, "date": {6, nil}, "etag": {7, nil}, "if-modified-since": {8, nil}, "if-none-match": {9, nil}, "last-modified": {10, nil}, "link": {11, nil}, "location": {12, nil}, "referer": {13, nil}, "set-cookie": {14, nil}, ":method": {15, map[string]uint8{ "CONNECT": 15, "DELETE": 16, "GET": 17, "HEAD": 18, "OPTIONS": 19, "POST": 20, "PUT": 21, }}, ":scheme": {22, map[string]uint8{ "http": 22, "https": 23, }}, ":status": {24, map[string]uint8{ "103": 24, "200": 25, "304": 26, "404": 27, "503": 28, "100": 63, "204": 64, "206": 65, "302": 66, "400": 67, "403": 68, "421": 69, "425": 70, "500": 71, }}, "accept": {29, map[string]uint8{ "*/*": 29, "application/dns-message": 30, }}, "accept-encoding": {31, map[string]uint8{"gzip, deflate, br": 31}}, "accept-ranges": {32, map[string]uint8{"bytes": 32}}, "access-control-allow-headers": {33, map[string]uint8{ "cache-control": 33, "content-type": 34, "*": 75, }}, "access-control-allow-origin": {35, map[string]uint8{"*": 35}}, "cache-control": {36, map[string]uint8{ "max-age=0": 36, "max-age=2592000": 37, "max-age=604800": 38, "no-cache": 39, "no-store": 40, "public, max-age=31536000": 41, }}, "content-encoding": {42, map[string]uint8{ "br": 42, "gzip": 43, }}, "content-type": {44, map[string]uint8{ "application/dns-message": 44, "application/javascript": 45, "application/json": 46, "application/x-www-form-urlencoded": 47, "image/gif": 48, "image/jpeg": 49, "image/png": 50, "text/css": 51, "text/html; charset=utf-8": 52, "text/plain": 53, "text/plain;charset=utf-8": 54, }}, "range": {55, map[string]uint8{"bytes=0-": 55}}, "strict-transport-security": {56, map[string]uint8{ "max-age=31536000": 56, "max-age=31536000; includesubdomains": 57, "max-age=31536000; includesubdomains; preload": 58, }}, "vary": {59, map[string]uint8{ "accept-encoding": 59, "origin": 60, }}, "x-content-type-options": {61, map[string]uint8{"nosniff": 61}}, "x-xss-protection": {62, map[string]uint8{"1; mode=block": 62}}, // ":status" is duplicated and takes index 63 to 71 "accept-language": {72, nil}, "access-control-allow-credentials": {73, map[string]uint8{ "FALSE": 73, "TRUE": 74, }}, // "access-control-allow-headers" is duplicated and takes index 75 "access-control-allow-methods": {76, map[string]uint8{ "get": 76, "get, post, options": 77, "options": 78, }}, "access-control-expose-headers": {79, map[string]uint8{"content-length": 79}}, "access-control-request-headers": {80, map[string]uint8{"content-type": 80}}, "access-control-request-method": {81, map[string]uint8{ "get": 81, "post": 82, }}, "alt-svc": {83, map[string]uint8{"clear": 83}}, "authorization": {84, nil}, "content-security-policy": {85, map[string]uint8{ "script-src 'none'; object-src 'none'; base-uri 'none'": 85, }}, "early-data": {86, map[string]uint8{"1": 86}}, "expect-ct": {87, nil}, "forwarded": {88, nil}, "if-range": {89, nil}, "origin": {90, nil}, "purpose": {91, map[string]uint8{"prefetch": 91}}, "server": {92, nil}, "timing-allow-origin": {93, map[string]uint8{"*": 93}}, "upgrade-insecure-requests": {94, map[string]uint8{"1": 94}}, "user-agent": {95, nil}, "x-forwarded-for": {96, nil}, "x-frame-options": {97, map[string]uint8{ "deny": 97, "sameorigin": 98, }}, } golang-github-marten-seemann-qpack-0.5.1/static_table_test.go000066400000000000000000000014731505114337600243070ustar00rootroot00000000000000package qpack import ( "testing" "github.com/stretchr/testify/require" ) func TestEncoderMapHasValueForEveryStaticTableEntry(t *testing.T) { for idx, hf := range staticTableEntries { if len(hf.Value) == 0 { require.Equal(t, uint8(idx), encoderMap[hf.Name].idx) } else { require.Equal(t, uint8(idx), encoderMap[hf.Name].values[hf.Value]) } } } func TestStaticTableasValueForEveryEncoderMapEntry(t *testing.T) { for name, indexAndVal := range encoderMap { if len(indexAndVal.values) == 0 { id := indexAndVal.idx require.Equal(t, name, staticTableEntries[id].Name) require.Empty(t, staticTableEntries[id].Value) } else { for value, id := range indexAndVal.values { require.Equal(t, name, staticTableEntries[id].Name) require.Equal(t, value, staticTableEntries[id].Value) } } } } golang-github-marten-seemann-qpack-0.5.1/varint.go000066400000000000000000000030651505114337600221140ustar00rootroot00000000000000package qpack // copied from the Go standard library HPACK implementation import "errors" var errVarintOverflow = errors.New("varint integer overflow") // appendVarInt appends i, as encoded in variable integer form using n // bit prefix, to dst and returns the extended buffer. // // See // http://http2.github.io/http2-spec/compression.html#integer.representation func appendVarInt(dst []byte, n byte, i uint64) []byte { k := uint64((1 << n) - 1) if i < k { return append(dst, byte(i)) } dst = append(dst, byte(k)) i -= k for ; i >= 128; i >>= 7 { dst = append(dst, byte(0x80|(i&0x7f))) } return append(dst, byte(i)) } // readVarInt reads an unsigned variable length integer off the // beginning of p. n is the parameter as described in // http://http2.github.io/http2-spec/compression.html#rfc.section.5.1. // // n must always be between 1 and 8. // // The returned remain buffer is either a smaller suffix of p, or err != nil. // The error is errNeedMore if p doesn't contain a complete integer. func readVarInt(n byte, p []byte) (i uint64, remain []byte, err error) { if n < 1 || n > 8 { panic("bad n") } if len(p) == 0 { return 0, p, errNeedMore } i = uint64(p[0]) if n < 8 { i &= (1 << uint64(n)) - 1 } if i < (1< 0 { b := p[0] p = p[1:] i += uint64(b&127) << m if b&128 == 0 { return i, p, nil } m += 7 if m >= 63 { // TODO: proper overflow check. making this up. return 0, origP, errVarintOverflow } } return 0, origP, errNeedMore }