Procházet zdrojové kódy

http2/hpack: track the beginning of a header block

dynamic table size updates must occur at the beginning of the first
header block. The original fix, golang/go#25023, guaranteed it was at
the beginning of the very first block. The Close method implicitly
marked the end of the current header. We now document the Close behavior
and can track when we are at the beginning of the first block.

Updates golang/go#29187

Change-Id: I83ec39546527cb17d7de8a88ec417a46443d2baa
Reviewed-on: https://go-review.googlesource.com/c/153978
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Michael Fraenkel před 7 roky
rodič
revize
891ebc4b82
2 změnil soubory, kde provedl 20 přidání a 4 odebrání
  1. 9 1
      http2/hpack/hpack.go
  2. 11 3
      http2/hpack/hpack_test.go

+ 9 - 1
http2/hpack/hpack.go

@@ -92,6 +92,8 @@ type Decoder struct {
 	// saveBuf is previous data passed to Write which we weren't able
 	// saveBuf is previous data passed to Write which we weren't able
 	// to fully parse before. Unlike buf, we own this data.
 	// to fully parse before. Unlike buf, we own this data.
 	saveBuf bytes.Buffer
 	saveBuf bytes.Buffer
+
+	firstField bool // processing the first field of the header block
 }
 }
 
 
 // NewDecoder returns a new decoder with the provided maximum dynamic
 // NewDecoder returns a new decoder with the provided maximum dynamic
@@ -101,6 +103,7 @@ func NewDecoder(maxDynamicTableSize uint32, emitFunc func(f HeaderField)) *Decod
 	d := &Decoder{
 	d := &Decoder{
 		emit:        emitFunc,
 		emit:        emitFunc,
 		emitEnabled: true,
 		emitEnabled: true,
+		firstField:  true,
 	}
 	}
 	d.dynTab.table.init()
 	d.dynTab.table.init()
 	d.dynTab.allowedMaxSize = maxDynamicTableSize
 	d.dynTab.allowedMaxSize = maxDynamicTableSize
@@ -226,11 +229,15 @@ func (d *Decoder) DecodeFull(p []byte) ([]HeaderField, error) {
 	return hf, nil
 	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 {
 func (d *Decoder) Close() error {
 	if d.saveBuf.Len() > 0 {
 	if d.saveBuf.Len() > 0 {
 		d.saveBuf.Reset()
 		d.saveBuf.Reset()
 		return DecodingError{errors.New("truncated headers")}
 		return DecodingError{errors.New("truncated headers")}
 	}
 	}
+	d.firstField = true
 	return nil
 	return nil
 }
 }
 
 
@@ -266,6 +273,7 @@ func (d *Decoder) Write(p []byte) (n int, err error) {
 			d.saveBuf.Write(d.buf)
 			d.saveBuf.Write(d.buf)
 			return len(p), nil
 			return len(p), nil
 		}
 		}
+		d.firstField = false
 		if err != nil {
 		if err != nil {
 			break
 			break
 		}
 		}
@@ -391,7 +399,7 @@ func (d *Decoder) callEmit(hf HeaderField) error {
 func (d *Decoder) parseDynamicTableSizeUpdate() error {
 func (d *Decoder) parseDynamicTableSizeUpdate() error {
 	// RFC 7541, sec 4.2: This dynamic table size update MUST occur at the
 	// RFC 7541, sec 4.2: This dynamic table size update MUST occur at the
 	// beginning of the first header block following the change to the dynamic table size.
 	// beginning of the first header block following the change to the dynamic table size.
-	if d.dynTab.size > 0 {
+	if !d.firstField && d.dynTab.size > 0 {
 		return DecodingError{errors.New("dynamic table size update MUST occur at the beginning of a header block")}
 		return DecodingError{errors.New("dynamic table size update MUST occur at the beginning of a header block")}
 	}
 	}
 
 

+ 11 - 3
http2/hpack/hpack_test.go

@@ -748,14 +748,22 @@ func TestDynamicSizeUpdate(t *testing.T) {
 	enc.SetMaxDynamicTableSize(255)
 	enc.SetMaxDynamicTableSize(255)
 	enc.WriteField(HeaderField{Name: "foo", Value: "bar"})
 	enc.WriteField(HeaderField{Name: "foo", Value: "bar"})
 
 
-	d := NewDecoder(4096, nil)
-	_, err := d.DecodeFull(buf.Bytes())
+	d := NewDecoder(4096, func(_ HeaderField) {})
+	_, err := d.Write(buf.Bytes())
+	if err != nil {
+		t.Fatalf("unexpected error: got = %v", err)
+	}
+
+	d.Close()
+
+	// Start a new header
+	_, err = d.Write(buf.Bytes())
 	if err != nil {
 	if err != nil {
 		t.Fatalf("unexpected error: got = %v", err)
 		t.Fatalf("unexpected error: got = %v", err)
 	}
 	}
 
 
 	// must fail since the dynamic table update must be at the beginning
 	// must fail since the dynamic table update must be at the beginning
-	_, err = d.DecodeFull(buf.Bytes())
+	_, err = d.Write(buf.Bytes())
 	if err == nil {
 	if err == nil {
 		t.Fatalf("dynamic table size update not at the beginning of a header block")
 		t.Fatalf("dynamic table size update not at the beginning of a header block")
 	}
 	}