Skip to content

Commit

Permalink
Merge pull request #5 from marten-seemann/decode-full
Browse files Browse the repository at this point in the history
implement DecodeFull for the decoder
  • Loading branch information
marten-seemann authored Mar 10, 2019
2 parents 416d651 + 680a677 commit f8170ed
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 0 deletions.
33 changes: 33 additions & 0 deletions decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"errors"
"fmt"
"sync"

"golang.org/x/net/http2/hpack"
)
Expand Down Expand Up @@ -35,6 +36,8 @@ 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
Expand Down Expand Up @@ -63,6 +66,13 @@ func (d *Decoder) Write(p []byte) (int, error) {
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 {
Expand All @@ -83,6 +93,29 @@ func (d *Decoder) Write(p []byte) (int, error) {
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.
Expand Down
48 changes: 48 additions & 0 deletions decoder_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package qpack

import (
"bytes"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"golang.org/x/net/http2/hpack"
Expand Down Expand Up @@ -175,4 +177,50 @@ var _ = Describe("Decoder", func() {
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())
})
})
})

0 comments on commit f8170ed

Please sign in to comment.