pax_global_header00006660000000000000000000000064144604316300014513gustar00rootroot0000000000000052 comment=af2f70da91bd58cd341dd223e8eace8b0793a5bf golang-github-marten-seemann-qpack-0.4.0/000077500000000000000000000000001446043163000202505ustar00rootroot00000000000000golang-github-marten-seemann-qpack-0.4.0/.codecov.yml000066400000000000000000000001471446043163000224750ustar00rootroot00000000000000coverage: round: nearest status: project: default: threshold: 1 patch: false golang-github-marten-seemann-qpack-0.4.0/.github/000077500000000000000000000000001446043163000216105ustar00rootroot00000000000000golang-github-marten-seemann-qpack-0.4.0/.github/workflows/000077500000000000000000000000001446043163000236455ustar00rootroot00000000000000golang-github-marten-seemann-qpack-0.4.0/.github/workflows/golangci-lint.yml000066400000000000000000000010421446043163000271140ustar00rootroot00000000000000name: golangci-lint on: push: tags: - v* branches: - master pull_request: jobs: golangci: name: lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: go-version: '1.19.x' - name: golangci-lint uses: golangci/golangci-lint-action@v3 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.49.0 golang-github-marten-seemann-qpack-0.4.0/.github/workflows/test.yml000066400000000000000000000032441446043163000253520ustar00rootroot00000000000000on: [ push, pull_request ] jobs: unit: strategy: matrix: go: [ "1.18.x", "1.19.x" ] runs-on: ubuntu-latest name: Unit tests (Go ${{ matrix.go }}) steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: go-version: ${{ matrix.go }} - run: go version - name: Install dependencies run: go build - name: Run tests run: go run github.com/onsi/ginkgo/v2/ginkgo -r -v -cover -randomize-all -randomize-suites -trace -skip-package integrationtests - name: Run tests (32 bit) env: GOARCH: 386 run: go run github.com/onsi/ginkgo/v2/ginkgo -r -v -cover -coverprofile coverage.txt -output-dir . -randomize-all -randomize-suites -trace -skip-package integrationtests - name: Run tests with race detector run: go run github.com/onsi/ginkgo/v2/ginkgo -r -v -race -randomize-all -randomize-suites -trace -skip-package integrationtests - name: Upload coverage to Codecov uses: codecov/codecov-action@v1 with: file: coverage.txt env_vars: GO=${{ matrix.go }} integration: strategy: matrix: go: [ "1.18.x", "1.19.x" ] runs-on: ubuntu-latest name: Integration tests (Go ${{ matrix.go }}) steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: go-version: ${{ matrix.go }} - run: go version - name: Install dependencies run: go build - name: Run tests run: | for i in {1..25}; do go run github.com/onsi/ginkgo/v2/ginkgo -race -v -randomize-all -trace integrationtests/self; done golang-github-marten-seemann-qpack-0.4.0/.gitignore000066400000000000000000000001561446043163000222420ustar00rootroot00000000000000fuzzing/*.zip fuzzing/coverprofile fuzzing/crashers fuzzing/sonarprofile fuzzing/suppressions fuzzing/corpus/ golang-github-marten-seemann-qpack-0.4.0/.gitmodules000066400000000000000000000001761446043163000224310ustar00rootroot00000000000000[submodule "integrationtests/interop/qifs"] path = integrationtests/interop/qifs url = https://github.com/qpackers/qifs.git golang-github-marten-seemann-qpack-0.4.0/.golangci.yml000066400000000000000000000006771446043163000226460ustar00rootroot00000000000000run: linters-settings: linters: disable-all: true enable: - asciicheck - deadcode - exhaustive - exportloopref - goconst - gofmt # redundant, since gofmt *should* be a no-op after gofumpt - gofumpt - goimports - gosimple - ineffassign - misspell - prealloc - scopelint - staticcheck - stylecheck - structcheck - unconvert - unparam - unused - varcheck - vet golang-github-marten-seemann-qpack-0.4.0/LICENSE.md000066400000000000000000000020361446043163000216550ustar00rootroot00000000000000Copyright 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.4.0/README.md000066400000000000000000000020661446043163000215330ustar00rootroot00000000000000# QPACK [![Godoc Reference](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/marten-seemann/qpack) [![Code Coverage](https://img.shields.io/codecov/c/github/marten-seemann/qpack/master.svg?style=flat-square)](https://codecov.io/gh/marten-seemann/qpack) 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 should be able to interoperate with other QPACK implemetations (both encoders and decoders), however it won't achieve a high compression efficiency. ## 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 ginkgo -r integrationtests ``` golang-github-marten-seemann-qpack-0.4.0/decoder.go000066400000000000000000000140561446043163000222120ustar00rootroot00000000000000package 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]&0x20 > 0 || buf[0]&0x10 == 0 { return errNoDynamicTable } 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.4.0/decoder_test.go000066400000000000000000000157341446043163000232550ustar00rootroot00000000000000package qpack import ( "bytes" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "golang.org/x/net/http2/hpack" ) var _ = Describe("Decoder", func() { var ( decoder *Decoder headerFields []HeaderField ) BeforeEach(func() { headerFields = nil decoder = NewDecoder(func(hf HeaderField) { headerFields = append(headerFields, hf) }) }) insertPrefix := func(data []byte) []byte { prefix := appendVarInt(nil, 8, 0) prefix = appendVarInt(prefix, 7, 0) return append(prefix, data...) } doPartialWrites := func(data []byte) { for i := 0; i < len(data)-1; i++ { n, err := decoder.Write([]byte{data[i]}) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(1)) Expect(headerFields).To(BeEmpty()) } n, err := decoder.Write([]byte{data[len(data)-1]}) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(1)) Expect(headerFields).To(HaveLen(1)) } It("rejects a non-zero Required Insert Count", func() { prefix := appendVarInt(nil, 8, 1) prefix = appendVarInt(prefix, 7, 0) _, err := decoder.Write(prefix) Expect(err).To(MatchError("decoding error: expected Required Insert Count to be zero")) }) It("rejects a non-zero Delta Base", func() { prefix := appendVarInt(nil, 8, 0) prefix = appendVarInt(prefix, 7, 1) _, err := decoder.Write(prefix) Expect(err).To(MatchError("decoding error: expected Base to be zero")) }) It("rejects unknown type bytes", func() { _, err := decoder.Write(insertPrefix([]byte{0x10})) Expect(err).To(MatchError("unexpected type byte: 0x10")) }) Context("indexed header field", func() { It("parses an indexed header field", func() { data := appendVarInt(nil, 6, 20) data[0] ^= 0x80 | 0x40 _, err := decoder.Write(insertPrefix(data)) Expect(err).ToNot(HaveOccurred()) Expect(headerFields).To(HaveLen(1)) Expect(headerFields[0]).To(Equal(staticTableEntries[20])) }) It("rejects an indexed header field that references the dynamic table", func() { data := appendVarInt(nil, 6, 20) data[0] ^= 0x80 // don't set the static flag (0x40) _, err := decoder.Write(insertPrefix(data)) Expect(err).To(MatchError(errNoDynamicTable)) }) It("errors when a non-existent static table entry is referenced", func() { data := appendVarInt(nil, 6, 10000) data[0] ^= 0x80 | 0x40 _, err := decoder.Write(insertPrefix(data)) Expect(err).To(MatchError("decoding error: invalid indexed representation index 10000")) Expect(headerFields).To(BeEmpty()) }) It("handles partial writes", func() { data := appendVarInt(nil, 6, 20) data[0] ^= 0x80 | 0x40 data = insertPrefix(data) doPartialWrites(data) }) }) Context("header field with name reference", func() { It("parses a literal header field with name reference", func() { data := appendVarInt(nil, 4, 49) data[0] ^= 0x40 | 0x10 data = appendVarInt(data, 7, 6) data = append(data, []byte("foobar")...) _, err := decoder.Write(insertPrefix(data)) Expect(err).ToNot(HaveOccurred()) Expect(headerFields).To(HaveLen(1)) Expect(headerFields[0].Name).To(Equal("content-type")) Expect(headerFields[0].Value).To(Equal("foobar")) }) It("parses a literal header field with name reference, with Huffman encoding", func() { 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") _, err := decoder.Write(insertPrefix(data)) Expect(err).ToNot(HaveOccurred()) Expect(headerFields).To(HaveLen(1)) Expect(headerFields[0].Name).To(Equal("content-type")) Expect(headerFields[0].Value).To(Equal("foobar")) }) It("rejects a literal header field with name reference that references the dynamic table", func() { 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)) Expect(err).To(MatchError(errNoDynamicTable)) }) It("handles partial writes", func() { data := appendVarInt(nil, 4, 49) data[0] ^= 0x40 | 0x10 data = appendVarInt(data, 7, 6) data = append(data, []byte("foobar")...) data = insertPrefix(data) doPartialWrites(data) }) It("handles partial writes, when using Huffman encoding", func() { 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") data = insertPrefix(data) doPartialWrites(data) }) }) Context("header field without name reference", func() { It("parses a literal header field without name reference", func() { 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...) _, err := decoder.Write(insertPrefix(data)) Expect(err).ToNot(HaveOccurred()) Expect(headerFields).To(HaveLen(1)) Expect(headerFields[0].Name).To(Equal("foo")) Expect(headerFields[0].Value).To(Equal("bar")) }) It("handles partial writes", func() { 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...) data = insertPrefix(data) doPartialWrites(data) }) }) Context("using DecodeFull", func() { It("decodes nothing", func() { data, err := NewDecoder(nil).DecodeFull([]byte{}) Expect(err).ToNot(HaveOccurred()) Expect(data).To(BeEmpty()) }) It("decodes multiple entries", func() { buf := &bytes.Buffer{} enc := NewEncoder(buf) Expect(enc.WriteField(HeaderField{Name: "foo", Value: "bar"})).To(Succeed()) Expect(enc.WriteField(HeaderField{Name: "lorem", Value: "ipsum"})).To(Succeed()) data, err := NewDecoder(nil).DecodeFull(buf.Bytes()) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]HeaderField{ {Name: "foo", Value: "bar"}, {Name: "lorem", Value: "ipsum"}, })) }) It("returns an error if the data is incomplete", func() { buf := &bytes.Buffer{} enc := NewEncoder(buf) Expect(enc.WriteField(HeaderField{Name: "foo", Value: "bar"})).To(Succeed()) _, err := NewDecoder(nil).DecodeFull(buf.Bytes()[:buf.Len()-2]) Expect(err).To(MatchError("decoding error: truncated headers")) }) It("restores the emitFunc afterwards", func() { var emitFuncCalled bool emitFunc := func(HeaderField) { emitFuncCalled = true } decoder := NewDecoder(emitFunc) buf := &bytes.Buffer{} enc := NewEncoder(buf) Expect(enc.WriteField(HeaderField{Name: "foo", Value: "bar"})).To(Succeed()) _, err := decoder.DecodeFull(buf.Bytes()) Expect(err).ToNot(HaveOccurred()) Expect(emitFuncCalled).To(BeFalse()) _, err = decoder.Write(buf.Bytes()) Expect(err).ToNot(HaveOccurred()) Expect(emitFuncCalled).To(BeTrue()) }) }) }) golang-github-marten-seemann-qpack-0.4.0/encoder.go000066400000000000000000000051021446043163000222140ustar00rootroot00000000000000package 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.4.0/encoder_test.go000066400000000000000000000126231446043163000232610ustar00rootroot00000000000000package qpack import ( "bytes" "io" "golang.org/x/net/http2/hpack" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) // 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) } var _ = Describe("Encoder", func() { var ( encoder *Encoder output *errWriter ) BeforeEach(func() { output = &errWriter{} encoder = NewEncoder(output) }) readPrefix := func(data []byte) (rest []byte, requiredInsertCount uint64, deltaBase uint64) { var err error requiredInsertCount, rest, err = readVarInt(8, data) Expect(err).ToNot(HaveOccurred()) deltaBase, rest, err = readVarInt(7, rest) Expect(err).ToNot(HaveOccurred()) return } checkHeaderField := func(data []byte, hf HeaderField) []byte { Expect(data[0] & (0x80 ^ 0x40 ^ 0x20)).To(Equal(uint8(0x20))) // 001xxxxx Expect(data[0] & 0x8).ToNot(BeZero()) // Huffman encoding nameLen, data, err := readVarInt(3, data) Expect(err).ToNot(HaveOccurred()) l := hpack.HuffmanEncodeLength(hf.Name) Expect(nameLen).To(BeEquivalentTo(l)) Expect(hpack.HuffmanDecodeToString(data[:l])).To(Equal(hf.Name)) valueLen, data, err := readVarInt(7, data[l:]) Expect(err).ToNot(HaveOccurred()) l = hpack.HuffmanEncodeLength(hf.Value) Expect(valueLen).To(BeEquivalentTo(l)) Expect(hpack.HuffmanDecodeToString(data[:l])).To(Equal(hf.Value)) return data[l:] } // Reads one indexed field line representation from data and verifies it matches hf. // Returns the leftover bytes from data. checkIndexedHeaderField := func(data []byte, hf HeaderField) []byte { Expect(data[0] >> 7).To(Equal(uint8(1))) // 1Txxxxxx index, data, err := readVarInt(6, data) Expect(err).ToNot(HaveOccurred()) Expect(staticTableEntries[index]).To(Equal(hf)) return data } checkHeaderFieldWithNameRef := func(data []byte, hf HeaderField) []byte { // read name reference Expect(data[0] >> 6).To(Equal(uint8(1))) // 01NTxxxx index, data, err := readVarInt(4, data) Expect(err).ToNot(HaveOccurred()) Expect(staticTableEntries[index].Name).To(Equal(hf.Name)) // read literal value valueLen, data, err := readVarInt(7, data) Expect(err).ToNot(HaveOccurred()) l := hpack.HuffmanEncodeLength(hf.Value) Expect(valueLen).To(BeEquivalentTo(l)) Expect(hpack.HuffmanDecodeToString(data[:l])).To(Equal(hf.Value)) return data[l:] } It("encodes a single field", func() { hf := HeaderField{Name: "foobar", Value: "lorem ipsum"} Expect(encoder.WriteField(hf)).To(Succeed()) data, requiredInsertCount, deltaBase := readPrefix(output.Bytes()) Expect(requiredInsertCount).To(BeZero()) Expect(deltaBase).To(BeZero()) data = checkHeaderField(data, hf) Expect(data).To(BeEmpty()) }) It("encodes fails to encode when writer errs", func() { hf := HeaderField{Name: "foobar", Value: "lorem ipsum"} output.fail = true Expect(encoder.WriteField(hf)).To(MatchError("io: read/write on closed pipe")) }) It("encodes multiple fields", func() { hf1 := HeaderField{Name: "foobar", Value: "lorem ipsum"} hf2 := HeaderField{Name: "raboof", Value: "dolor sit amet"} Expect(encoder.WriteField(hf1)).To(Succeed()) Expect(encoder.WriteField(hf2)).To(Succeed()) data, requiredInsertCount, deltaBase := readPrefix(output.Bytes()) Expect(requiredInsertCount).To(BeZero()) Expect(deltaBase).To(BeZero()) data = checkHeaderField(data, hf1) data = checkHeaderField(data, hf2) Expect(data).To(BeEmpty()) }) It("encodes all the fields of the static table", func() { for _, hf := range staticTableEntries { Expect(encoder.WriteField(hf)).To(Succeed()) } data, requiredInsertCount, deltaBase := readPrefix(output.Bytes()) Expect(requiredInsertCount).To(BeZero()) Expect(deltaBase).To(BeZero()) for _, hf := range staticTableEntries { data = checkIndexedHeaderField(data, hf) } Expect(data).To(BeEmpty()) }) It("encodes fields with name reference in the static table", func() { hf1 := HeaderField{Name: ":status", Value: "666"} hf2 := HeaderField{Name: "server", Value: "lorem ipsum"} hf3 := HeaderField{Name: ":method", Value: ""} Expect(encoder.WriteField(hf1)).To(Succeed()) Expect(encoder.WriteField(hf2)).To(Succeed()) Expect(encoder.WriteField(hf3)).To(Succeed()) data, requiredInsertCount, deltaBase := readPrefix(output.Bytes()) Expect(requiredInsertCount).To(BeZero()) Expect(deltaBase).To(BeZero()) data = checkHeaderFieldWithNameRef(data, hf1) data = checkHeaderFieldWithNameRef(data, hf2) data = checkHeaderFieldWithNameRef(data, hf3) Expect(data).To(BeEmpty()) }) It("encodes multiple requests", func() { hf1 := HeaderField{Name: "foobar", Value: "lorem ipsum"} Expect(encoder.WriteField(hf1)).To(Succeed()) data, requiredInsertCount, deltaBase := readPrefix(output.Bytes()) Expect(requiredInsertCount).To(BeZero()) Expect(deltaBase).To(BeZero()) data = checkHeaderField(data, hf1) Expect(data).To(BeEmpty()) output.Reset() Expect(encoder.Close()) hf2 := HeaderField{Name: "raboof", Value: "dolor sit amet"} Expect(encoder.WriteField(hf2)).To(Succeed()) data, requiredInsertCount, deltaBase = readPrefix(output.Bytes()) Expect(requiredInsertCount).To(BeZero()) Expect(deltaBase).To(BeZero()) data = checkHeaderField(data, hf2) Expect(data).To(BeEmpty()) }) }) golang-github-marten-seemann-qpack-0.4.0/example/000077500000000000000000000000001446043163000217035ustar00rootroot00000000000000golang-github-marten-seemann-qpack-0.4.0/example/fb-req-hq.out.0.0.0000066400000000000000000004457241446043163000246700ustar00rootroot00000000000000QbXE랶;Ǫ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.4.0/example/main.go000066400000000000000000000020571446043163000231620ustar00rootroot00000000000000package 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.4.0/fuzzing/000077500000000000000000000000001446043163000217445ustar00rootroot00000000000000golang-github-marten-seemann-qpack-0.4.0/fuzzing/fuzz.go000066400000000000000000000025721446043163000232770ustar00rootroot00000000000000package 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.4.0/go.mod000066400000000000000000000010611446043163000213540ustar00rootroot00000000000000module github.com/quic-go/qpack go 1.18 require ( github.com/onsi/ginkgo/v2 v2.2.0 github.com/onsi/gomega v1.20.1 golang.org/x/net v0.0.0-20220722155237-a158d28d115b ) require ( github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/google/go-cmp v0.5.8 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.12 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) golang-github-marten-seemann-qpack-0.4.0/go.sum000066400000000000000000000070211446043163000214030ustar00rootroot00000000000000github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.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/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI= github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= 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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= 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.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 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.4.0/header_field.go000066400000000000000000000007351446043163000231770ustar00rootroot00000000000000package 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.4.0/header_field_test.go000066400000000000000000000007641446043163000242400ustar00rootroot00000000000000package qpack import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Header Field", func() { It("says if it is pseudo", func() { Expect((HeaderField{Name: ":status"}).IsPseudo()).To(BeTrue()) Expect((HeaderField{Name: ":authority"}).IsPseudo()).To(BeTrue()) Expect((HeaderField{Name: ":foobar"}).IsPseudo()).To(BeTrue()) Expect((HeaderField{Name: "status"}).IsPseudo()).To(BeFalse()) Expect((HeaderField{Name: "foobar"}).IsPseudo()).To(BeFalse()) }) }) golang-github-marten-seemann-qpack-0.4.0/integrationtests/000077500000000000000000000000001446043163000236565ustar00rootroot00000000000000golang-github-marten-seemann-qpack-0.4.0/integrationtests/interop/000077500000000000000000000000001446043163000253365ustar00rootroot00000000000000golang-github-marten-seemann-qpack-0.4.0/integrationtests/interop/interop_suite_test.go000066400000000000000000000035711446043163000316230ustar00rootroot00000000000000package interop import ( "bufio" "io" "os" "path/filepath" "strings" "testing" "github.com/quic-go/qpack" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) func TestInterop(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Interop Suite") } type request struct { headers []qpack.HeaderField } type qif struct { requests []request } var qifs map[string]qif func readQIFs() { qifDir := currentDir() + "/qifs/qifs" Expect(qifDir).To(BeADirectory()) 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) ext := filepath.Ext(filename) name := filename[:len(filename)-len(ext)] file, err := os.Open(path) Expect(err).ToNot(HaveOccurred()) requests := parseRequests(file) qifs[name] = qif{requests: requests} return nil }) } 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 } // Done means that we reached the end of the file // The headers returned with this call will be empty and should be ignored. func parseRequest(lr *bufio.Reader) ([]qpack.HeaderField, bool /* done reading */) { var headers []qpack.HeaderField for { line, isPrefix, err := lr.ReadLine() if err == io.EOF { return headers, true } Expect(err).ToNot(HaveOccurred()) Expect(isPrefix).To(BeFalse()) if len(line) == 0 { break } split := strings.Split(string(line), "\t") Expect(split).To(Or(HaveLen(1), HaveLen(2))) 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 } var _ = BeforeSuite(func() { qifs = make(map[string]qif) readQIFs() }) golang-github-marten-seemann-qpack-0.4.0/integrationtests/interop/interop_test.go000066400000000000000000000045241446043163000304110ustar00rootroot00000000000000package interop import ( "encoding/binary" "fmt" "io" "os" "path" "path/filepath" "runtime" "strings" "github.com/quic-go/qpack" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) func currentDir() string { _, filename, _, ok := runtime.Caller(0) if !ok { panic("Failed to get current frame") } return path.Dir(filename) } var _ = Describe("Interop", func() { // find all encoded files with a dynamic table size of 0 findFiles := func() []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 } parseInput := func(r io.Reader) (uint64 /* stream ID */, []byte) { prefix := make([]byte, 12) _, err := io.ReadFull(r, prefix) Expect(err).ToNot(HaveOccurred()) streamID := binary.BigEndian.Uint64(prefix[:8]) length := binary.BigEndian.Uint32(prefix[8:12]) if length > (1 << 15) { // DoS prevention Fail("input too long") } data := make([]byte, int(length)) _, err = io.ReadFull(r, data) Expect(err).ToNot(HaveOccurred()) return streamID, data } filenames := findFiles() for i := range filenames { path := filenames[i] fpath, filename := filepath.Split(path) prettyPath := path[len(filepath.Dir(filepath.Dir(filepath.Dir(fpath))))+1:] It(fmt.Sprintf("using %s", prettyPath), func() { qif, ok := qifs[strings.Split(filename, ".")[0]] Expect(ok).To(BeTrue()) file, err := os.Open(path) var headers []qpack.HeaderField decoder := qpack.NewDecoder(func(hf qpack.HeaderField) { headers = append(headers, hf) }) var numRequests, numHeaderFields int Expect(qif.requests).ToNot(BeEmpty()) for _, req := range qif.requests { Expect(err).ToNot(HaveOccurred()) _, data := parseInput(file) _, err = decoder.Write(data) Expect(err).ToNot(HaveOccurred()) Expect(headers).To(Equal(req.headers)) numRequests++ numHeaderFields += len(headers) headers = nil decoder.Close() } fmt.Fprintf(GinkgoWriter, "Decoded %d requests containing %d header fields.\n", len(qif.requests), numHeaderFields) }) } }) golang-github-marten-seemann-qpack-0.4.0/integrationtests/self/000077500000000000000000000000001446043163000246075ustar00rootroot00000000000000golang-github-marten-seemann-qpack-0.4.0/integrationtests/self/integration_test.go000066400000000000000000000147471446043163000305350ustar00rootroot00000000000000package self import ( "bytes" "fmt" "math/rand" "github.com/quic-go/qpack" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Self Tests", func() { getEncoder := func() (*qpack.Encoder, *bytes.Buffer) { output := &bytes.Buffer{} return qpack.NewEncoder(output), output } randomString := func(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) } It("encodes and decodes a single header field", func() { hf := qpack.HeaderField{ Name: randomString(15), Value: randomString(15), } encoder, output := getEncoder() Expect(encoder.WriteField(hf)).To(Succeed()) headerFields, err := qpack.NewDecoder(nil).DecodeFull(output.Bytes()) Expect(err).ToNot(HaveOccurred()) Expect(headerFields).To(Equal([]qpack.HeaderField{hf})) }) It("encodes and decodes multiple header fields", func() { hfs := []qpack.HeaderField{ {Name: "foo", Value: "bar"}, {Name: "lorem", Value: "ipsum"}, {Name: randomString(15), Value: randomString(20)}, } encoder, output := getEncoder() for _, hf := range hfs { Expect(encoder.WriteField(hf)).To(Succeed()) } headerFields, err := qpack.NewDecoder(nil).DecodeFull(output.Bytes()) Expect(err).ToNot(HaveOccurred()) Expect(headerFields).To(Equal(hfs)) }) It("encodes and decodes multiple requests", func() { 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 { Expect(encoder.WriteField(hf)).To(Succeed()) } req1 := append([]byte{}, output.Bytes()...) output.Reset() for _, hf := range hfs2 { Expect(encoder.WriteField(hf)).To(Succeed()) } req2 := append([]byte{}, output.Bytes()...) var headerFields []qpack.HeaderField decoder := qpack.NewDecoder(func(hf qpack.HeaderField) { headerFields = append(headerFields, hf) }) _, err := decoder.Write(req1) Expect(err).ToNot(HaveOccurred()) Expect(headerFields).To(Equal(hfs1)) headerFields = nil _, err = decoder.Write(req2) Expect(err).ToNot(HaveOccurred()) Expect(headerFields).To(Equal(hfs2)) }) // replace one character by a random character at a random position replaceRandomCharacter := func(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 } check := func(encoded []byte, hf qpack.HeaderField) { headerFields, err := qpack.NewDecoder(nil).DecodeFull(encoded) ExpectWithOffset(1, err).ToNot(HaveOccurred()) ExpectWithOffset(1, headerFields).To(HaveLen(1)) ExpectWithOffset(1, headerFields[0]).To(Equal(hf)) } // use an entry with a value, for example "set-cookie" It("uses the static table for field names, for fields without values", func() { 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() Expect(encoder.WriteField(hf)).To(Succeed()) encodedLen := output.Len() check(output.Bytes(), hf) encoder, output = getEncoder() oldName := hf.Name hf.Name = replaceRandomCharacter(hf.Name) Expect(encoder.WriteField(hf)).To(Succeed()) fmt.Fprintf(GinkgoWriter, "Encoding field name:\n\t%s: %d bytes\n\t%s: %d bytes\n", oldName, encodedLen, hf.Name, output.Len()) Expect(output.Len()).To(BeNumerically(">", encodedLen)) }) // use an entry with a value, for example "set-cookie", // but now use a custom value It("uses the static table for field names, for fields without values", func() { 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() Expect(encoder.WriteField(hf)).To(Succeed()) encodedLen := output.Len() check(output.Bytes(), hf) encoder, output = getEncoder() oldName := hf.Name hf.Name = replaceRandomCharacter(hf.Name) Expect(encoder.WriteField(hf)).To(Succeed()) fmt.Fprintf(GinkgoWriter, "Encoding field name:\n\t%s: %d bytes\n\t%s: %d bytes\n", oldName, encodedLen, hf.Name, output.Len()) Expect(output.Len()).To(BeNumerically(">", encodedLen)) }) // use an entry with a value, for example // cache-control -> Value: "max-age=0" // but encode a different value // cache-control -> xyz It("uses the static table for field names, for fields with values", func() { var hf qpack.HeaderField for { // Only use values with at least 2 characters. // This makes sure that Huffman enocding 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() Expect(encoder.WriteField(hf)).To(Succeed()) encodedLen := output.Len() check(output.Bytes(), hf) encoder, output = getEncoder() oldName := hf.Name hf.Name = replaceRandomCharacter(hf.Name) Expect(encoder.WriteField(hf)).To(Succeed()) fmt.Fprintf(GinkgoWriter, "Encoding field name:\n\t%s: %d bytes\n\t%s: %d bytes\n", oldName, encodedLen, hf.Name, output.Len()) Expect(output.Len()).To(BeNumerically(">", encodedLen)) }) It("uses the static table for field values", func() { var hf qpack.HeaderField for { // Only use values with at least 2 characters. // This makes sure that Huffman enocding 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() Expect(encoder.WriteField(hf)).To(Succeed()) encodedLen := output.Len() check(output.Bytes(), hf) encoder, output = getEncoder() oldValue := hf.Value hf.Value = replaceRandomCharacter(hf.Value) Expect(encoder.WriteField(hf)).To(Succeed()) fmt.Fprintf(GinkgoWriter, "Encoding field value:\n\t%s: %s -> %d bytes\n\t%s: %s -> %d bytes\n", hf.Name, oldValue, encodedLen, hf.Name, hf.Value, output.Len(), ) Expect(output.Len()).To(BeNumerically(">", encodedLen)) }) }) golang-github-marten-seemann-qpack-0.4.0/integrationtests/self/self_suite_test.go000066400000000000000000000007631446043163000303450ustar00rootroot00000000000000package self import ( "math/rand" "testing" _ "unsafe" "github.com/quic-go/qpack" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) func TestSelf(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Self Suite") } var _ = BeforeSuite(func() { rand.Seed(GinkgoRandomSeed()) }) var staticTable []qpack.HeaderField //go:linkname getStaticTable github.com/quic-go/qpack.getStaticTable func getStaticTable() []qpack.HeaderField func init() { staticTable = getStaticTable() } golang-github-marten-seemann-qpack-0.4.0/qpack_suite_test.go000066400000000000000000000002771446043163000241540ustar00rootroot00000000000000package qpack_test import ( "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) func TestQpack(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "QPACK Suite") } golang-github-marten-seemann-qpack-0.4.0/static_table.go000066400000000000000000000216341446043163000232430ustar00rootroot00000000000000package 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.4.0/static_table_test.go000066400000000000000000000016631446043163000243020ustar00rootroot00000000000000package qpack import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("StaticTable", func() { It("verifies that encoderMap has a value for every staticTableEntries entry", func() { for idx, hf := range staticTableEntries { if len(hf.Value) == 0 { Expect(encoderMap[hf.Name].idx).To(Equal(uint8(idx))) } else { Expect(encoderMap[hf.Name].values[hf.Value]).To(Equal(uint8(idx))) } } }) It("verifies that staticTableEntries has a value for every encoderMap entry", func() { for name, indexAndVal := range encoderMap { if len(indexAndVal.values) == 0 { id := indexAndVal.idx Expect(staticTableEntries[id].Name).To(Equal(name)) Expect(staticTableEntries[id].Value).To(BeEmpty()) } else { for value, id := range indexAndVal.values { Expect(staticTableEntries[id].Name).To(Equal(name)) Expect(staticTableEntries[id].Value).To(Equal(value)) } } } }) }) golang-github-marten-seemann-qpack-0.4.0/tools.go000066400000000000000000000001151446043163000217340ustar00rootroot00000000000000//go:build tools package qpack import _ "github.com/onsi/ginkgo/v2/ginkgo" golang-github-marten-seemann-qpack-0.4.0/varint.go000066400000000000000000000030651446043163000221060ustar00rootroot00000000000000package 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 }