glide up
This commit is contained in:
		
							
								
								
									
										29
									
								
								vendor/golang.org/x/net/http2/hpack/encode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										29
									
								
								vendor/golang.org/x/net/http2/hpack/encode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -39,14 +39,13 @@ func NewEncoder(w io.Writer) *Encoder {
 | 
			
		||||
		tableSizeUpdate: false,
 | 
			
		||||
		w:               w,
 | 
			
		||||
	}
 | 
			
		||||
	e.dynTab.table.init()
 | 
			
		||||
	e.dynTab.setMaxSize(initialHeaderTableSize)
 | 
			
		||||
	return e
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteField encodes f into a single Write to e's underlying Writer.
 | 
			
		||||
// This function may also produce bytes for "Header Table Size Update"
 | 
			
		||||
// if necessary. If produced, it is done before encoding f.
 | 
			
		||||
// if necessary.  If produced, it is done before encoding f.
 | 
			
		||||
func (e *Encoder) WriteField(f HeaderField) error {
 | 
			
		||||
	e.buf = e.buf[:0]
 | 
			
		||||
 | 
			
		||||
@@ -89,17 +88,29 @@ func (e *Encoder) WriteField(f HeaderField) error {
 | 
			
		||||
// only name matches, i points to that index and nameValueMatch
 | 
			
		||||
// becomes false.
 | 
			
		||||
func (e *Encoder) searchTable(f HeaderField) (i uint64, nameValueMatch bool) {
 | 
			
		||||
	i, nameValueMatch = staticTable.search(f)
 | 
			
		||||
	if nameValueMatch {
 | 
			
		||||
		return i, true
 | 
			
		||||
	for idx, hf := range staticTable {
 | 
			
		||||
		if !constantTimeStringCompare(hf.Name, f.Name) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if i == 0 {
 | 
			
		||||
			i = uint64(idx + 1)
 | 
			
		||||
		}
 | 
			
		||||
		if f.Sensitive {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if !constantTimeStringCompare(hf.Value, f.Value) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		i = uint64(idx + 1)
 | 
			
		||||
		nameValueMatch = true
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	j, nameValueMatch := e.dynTab.table.search(f)
 | 
			
		||||
	j, nameValueMatch := e.dynTab.search(f)
 | 
			
		||||
	if nameValueMatch || (i == 0 && j != 0) {
 | 
			
		||||
		return j + uint64(staticTable.len()), nameValueMatch
 | 
			
		||||
		i = j + uint64(len(staticTable))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return i, false
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetMaxDynamicTableSize changes the dynamic header table size to v.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										70
									
								
								vendor/golang.org/x/net/http2/hpack/encode_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										70
									
								
								vendor/golang.org/x/net/http2/hpack/encode_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -7,8 +7,6 @@ package hpack
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math/rand"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
@@ -103,20 +101,17 @@ func TestEncoderSearchTable(t *testing.T) {
 | 
			
		||||
		wantMatch bool
 | 
			
		||||
	}{
 | 
			
		||||
		// Name and Value match
 | 
			
		||||
		{pair("foo", "bar"), uint64(staticTable.len()) + 3, true},
 | 
			
		||||
		{pair("blake", "miz"), uint64(staticTable.len()) + 2, true},
 | 
			
		||||
		{pair("foo", "bar"), uint64(len(staticTable) + 3), true},
 | 
			
		||||
		{pair("blake", "miz"), uint64(len(staticTable) + 2), true},
 | 
			
		||||
		{pair(":method", "GET"), 2, true},
 | 
			
		||||
 | 
			
		||||
		// Only name match because Sensitive == true. This is allowed to match
 | 
			
		||||
		// any ":method" entry. The current implementation uses the last entry
 | 
			
		||||
		// added in newStaticTable.
 | 
			
		||||
		{HeaderField{":method", "GET", true}, 3, false},
 | 
			
		||||
		// Only name match because Sensitive == true
 | 
			
		||||
		{HeaderField{":method", "GET", true}, 2, false},
 | 
			
		||||
 | 
			
		||||
		// Only Name matches
 | 
			
		||||
		{pair("foo", "..."), uint64(staticTable.len()) + 3, false},
 | 
			
		||||
		{pair("blake", "..."), uint64(staticTable.len()) + 2, false},
 | 
			
		||||
		// As before, this is allowed to match any ":method" entry.
 | 
			
		||||
		{pair(":method", "..."), 3, false},
 | 
			
		||||
		{pair("foo", "..."), uint64(len(staticTable) + 3), false},
 | 
			
		||||
		{pair("blake", "..."), uint64(len(staticTable) + 2), false},
 | 
			
		||||
		{pair(":method", "..."), 2, false},
 | 
			
		||||
 | 
			
		||||
		// None match
 | 
			
		||||
		{pair("foo-", "bar"), 0, false},
 | 
			
		||||
@@ -333,54 +328,3 @@ func TestEncoderSetMaxDynamicTableSizeLimit(t *testing.T) {
 | 
			
		||||
func removeSpace(s string) string {
 | 
			
		||||
	return strings.Replace(s, " ", "", -1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func BenchmarkEncoderSearchTable(b *testing.B) {
 | 
			
		||||
	e := NewEncoder(nil)
 | 
			
		||||
 | 
			
		||||
	// A sample of possible header fields.
 | 
			
		||||
	// This is not based on any actual data from HTTP/2 traces.
 | 
			
		||||
	var possible []HeaderField
 | 
			
		||||
	for _, f := range staticTable.ents {
 | 
			
		||||
		if f.Value == "" {
 | 
			
		||||
			possible = append(possible, f)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		// Generate 5 random values, except for cookie and set-cookie,
 | 
			
		||||
		// which we know can have many values in practice.
 | 
			
		||||
		num := 5
 | 
			
		||||
		if f.Name == "cookie" || f.Name == "set-cookie" {
 | 
			
		||||
			num = 25
 | 
			
		||||
		}
 | 
			
		||||
		for i := 0; i < num; i++ {
 | 
			
		||||
			f.Value = fmt.Sprintf("%s-%d", f.Name, i)
 | 
			
		||||
			possible = append(possible, f)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for k := 0; k < 10; k++ {
 | 
			
		||||
		f := HeaderField{
 | 
			
		||||
			Name:      fmt.Sprintf("x-header-%d", k),
 | 
			
		||||
			Sensitive: rand.Int()%2 == 0,
 | 
			
		||||
		}
 | 
			
		||||
		for i := 0; i < 5; i++ {
 | 
			
		||||
			f.Value = fmt.Sprintf("%s-%d", f.Name, i)
 | 
			
		||||
			possible = append(possible, f)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Add a random sample to the dynamic table. This very loosely simulates
 | 
			
		||||
	// a history of 100 requests with 20 header fields per request.
 | 
			
		||||
	for r := 0; r < 100*20; r++ {
 | 
			
		||||
		f := possible[rand.Int31n(int32(len(possible)))]
 | 
			
		||||
		// Skip if this is in the staticTable verbatim.
 | 
			
		||||
		if _, has := staticTable.search(f); !has {
 | 
			
		||||
			e.dynTab.add(f)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b.ResetTimer()
 | 
			
		||||
	for n := 0; n < b.N; n++ {
 | 
			
		||||
		for _, f := range possible {
 | 
			
		||||
			e.searchTable(f)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										104
									
								
								vendor/golang.org/x/net/http2/hpack/hpack.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										104
									
								
								vendor/golang.org/x/net/http2/hpack/hpack.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -61,7 +61,7 @@ func (hf HeaderField) String() string {
 | 
			
		||||
func (hf HeaderField) Size() uint32 {
 | 
			
		||||
	// http://http2.github.io/http2-spec/compression.html#rfc.section.4.1
 | 
			
		||||
	// "The size of the dynamic table is the sum of the size of
 | 
			
		||||
	// its entries. The size of an entry is the sum of its name's
 | 
			
		||||
	// its entries.  The size of an entry is the sum of its name's
 | 
			
		||||
	// length in octets (as defined in Section 5.2), its value's
 | 
			
		||||
	// length in octets (see Section 5.2), plus 32.  The size of
 | 
			
		||||
	// an entry is calculated using the length of the name and
 | 
			
		||||
@@ -102,7 +102,6 @@ func NewDecoder(maxDynamicTableSize uint32, emitFunc func(f HeaderField)) *Decod
 | 
			
		||||
		emit:        emitFunc,
 | 
			
		||||
		emitEnabled: true,
 | 
			
		||||
	}
 | 
			
		||||
	d.dynTab.table.init()
 | 
			
		||||
	d.dynTab.allowedMaxSize = maxDynamicTableSize
 | 
			
		||||
	d.dynTab.setMaxSize(maxDynamicTableSize)
 | 
			
		||||
	return d
 | 
			
		||||
@@ -155,9 +154,12 @@ func (d *Decoder) SetAllowedMaxDynamicTableSize(v uint32) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type dynamicTable struct {
 | 
			
		||||
	// ents is the FIFO described at
 | 
			
		||||
	// http://http2.github.io/http2-spec/compression.html#rfc.section.2.3.2
 | 
			
		||||
	table          headerFieldTable
 | 
			
		||||
	size           uint32 // in bytes
 | 
			
		||||
	// The newest (low index) is append at the end, and items are
 | 
			
		||||
	// evicted from the front.
 | 
			
		||||
	ents           []HeaderField
 | 
			
		||||
	size           uint32
 | 
			
		||||
	maxSize        uint32 // current maxSize
 | 
			
		||||
	allowedMaxSize uint32 // maxSize may go up to this, inclusive
 | 
			
		||||
}
 | 
			
		||||
@@ -167,45 +169,95 @@ func (dt *dynamicTable) setMaxSize(v uint32) {
 | 
			
		||||
	dt.evict()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: change dynamicTable to be a struct with a slice and a size int field,
 | 
			
		||||
// per http://http2.github.io/http2-spec/compression.html#rfc.section.4.1:
 | 
			
		||||
//
 | 
			
		||||
//
 | 
			
		||||
// Then make add increment the size. maybe the max size should move from Decoder to
 | 
			
		||||
// dynamicTable and add should return an ok bool if there was enough space.
 | 
			
		||||
//
 | 
			
		||||
// Later we'll need a remove operation on dynamicTable.
 | 
			
		||||
 | 
			
		||||
func (dt *dynamicTable) add(f HeaderField) {
 | 
			
		||||
	dt.table.addEntry(f)
 | 
			
		||||
	dt.ents = append(dt.ents, f)
 | 
			
		||||
	dt.size += f.Size()
 | 
			
		||||
	dt.evict()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// If we're too big, evict old stuff.
 | 
			
		||||
// If we're too big, evict old stuff (front of the slice)
 | 
			
		||||
func (dt *dynamicTable) evict() {
 | 
			
		||||
	var n int
 | 
			
		||||
	for dt.size > dt.maxSize && n < dt.table.len() {
 | 
			
		||||
		dt.size -= dt.table.ents[n].Size()
 | 
			
		||||
		n++
 | 
			
		||||
	base := dt.ents // keep base pointer of slice
 | 
			
		||||
	for dt.size > dt.maxSize {
 | 
			
		||||
		dt.size -= dt.ents[0].Size()
 | 
			
		||||
		dt.ents = dt.ents[1:]
 | 
			
		||||
	}
 | 
			
		||||
	dt.table.evictOldest(n)
 | 
			
		||||
 | 
			
		||||
	// Shift slice contents down if we evicted things.
 | 
			
		||||
	if len(dt.ents) != len(base) {
 | 
			
		||||
		copy(base, dt.ents)
 | 
			
		||||
		dt.ents = base[:len(dt.ents)]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// constantTimeStringCompare compares string a and b in a constant
 | 
			
		||||
// time manner.
 | 
			
		||||
func constantTimeStringCompare(a, b string) bool {
 | 
			
		||||
	if len(a) != len(b) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c := byte(0)
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(a); i++ {
 | 
			
		||||
		c |= a[i] ^ b[i]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c == 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Search searches f in the table. The return value i is 0 if there is
 | 
			
		||||
// no name match. If there is name match or name/value match, i is the
 | 
			
		||||
// index of that entry (1-based). If both name and value match,
 | 
			
		||||
// nameValueMatch becomes true.
 | 
			
		||||
func (dt *dynamicTable) search(f HeaderField) (i uint64, nameValueMatch bool) {
 | 
			
		||||
	l := len(dt.ents)
 | 
			
		||||
	for j := l - 1; j >= 0; j-- {
 | 
			
		||||
		ent := dt.ents[j]
 | 
			
		||||
		if !constantTimeStringCompare(ent.Name, f.Name) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if i == 0 {
 | 
			
		||||
			i = uint64(l - j)
 | 
			
		||||
		}
 | 
			
		||||
		if f.Sensitive {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if !constantTimeStringCompare(ent.Value, f.Value) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		i = uint64(l - j)
 | 
			
		||||
		nameValueMatch = true
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *Decoder) maxTableIndex() int {
 | 
			
		||||
	// This should never overflow. RFC 7540 Section 6.5.2 limits the size of
 | 
			
		||||
	// the dynamic table to 2^32 bytes, where each entry will occupy more than
 | 
			
		||||
	// one byte. Further, the staticTable has a fixed, small length.
 | 
			
		||||
	return d.dynTab.table.len() + staticTable.len()
 | 
			
		||||
	return len(d.dynTab.ents) + len(staticTable)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *Decoder) at(i uint64) (hf HeaderField, ok bool) {
 | 
			
		||||
	// See Section 2.3.3.
 | 
			
		||||
	if i == 0 {
 | 
			
		||||
	if i < 1 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if i <= uint64(staticTable.len()) {
 | 
			
		||||
		return staticTable.ents[i-1], true
 | 
			
		||||
	}
 | 
			
		||||
	if i > uint64(d.maxTableIndex()) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	// In the dynamic table, newer entries have lower indices.
 | 
			
		||||
	// However, dt.ents[0] is the oldest entry. Hence, dt.ents is
 | 
			
		||||
	// the reversed dynamic table.
 | 
			
		||||
	dt := d.dynTab.table
 | 
			
		||||
	return dt.ents[dt.len()-(int(i)-staticTable.len())], true
 | 
			
		||||
	if i <= uint64(len(staticTable)) {
 | 
			
		||||
		return staticTable[i-1], true
 | 
			
		||||
	}
 | 
			
		||||
	dents := d.dynTab.ents
 | 
			
		||||
	return dents[len(dents)-(int(i)-len(staticTable))], true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Decode decodes an entire block.
 | 
			
		||||
@@ -255,7 +307,7 @@ func (d *Decoder) Write(p []byte) (n int, err error) {
 | 
			
		||||
		err = d.parseHeaderFieldRepr()
 | 
			
		||||
		if err == errNeedMore {
 | 
			
		||||
			// Extra paranoia, making sure saveBuf won't
 | 
			
		||||
			// get too large. All the varint and string
 | 
			
		||||
			// get too large.  All the varint and string
 | 
			
		||||
			// reading code earlier should already catch
 | 
			
		||||
			// overlong things and return ErrStringLength,
 | 
			
		||||
			// but keep this as a last resort.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										146
									
								
								vendor/golang.org/x/net/http2/hpack/hpack_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										146
									
								
								vendor/golang.org/x/net/http2/hpack/hpack_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -5,16 +5,117 @@
 | 
			
		||||
package hpack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math/rand"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestStaticTable(t *testing.T) {
 | 
			
		||||
	fromSpec := `
 | 
			
		||||
          +-------+-----------------------------+---------------+
 | 
			
		||||
          | 1     | :authority                  |               |
 | 
			
		||||
          | 2     | :method                     | GET           |
 | 
			
		||||
          | 3     | :method                     | POST          |
 | 
			
		||||
          | 4     | :path                       | /             |
 | 
			
		||||
          | 5     | :path                       | /index.html   |
 | 
			
		||||
          | 6     | :scheme                     | http          |
 | 
			
		||||
          | 7     | :scheme                     | https         |
 | 
			
		||||
          | 8     | :status                     | 200           |
 | 
			
		||||
          | 9     | :status                     | 204           |
 | 
			
		||||
          | 10    | :status                     | 206           |
 | 
			
		||||
          | 11    | :status                     | 304           |
 | 
			
		||||
          | 12    | :status                     | 400           |
 | 
			
		||||
          | 13    | :status                     | 404           |
 | 
			
		||||
          | 14    | :status                     | 500           |
 | 
			
		||||
          | 15    | accept-charset              |               |
 | 
			
		||||
          | 16    | accept-encoding             | gzip, deflate |
 | 
			
		||||
          | 17    | accept-language             |               |
 | 
			
		||||
          | 18    | accept-ranges               |               |
 | 
			
		||||
          | 19    | accept                      |               |
 | 
			
		||||
          | 20    | access-control-allow-origin |               |
 | 
			
		||||
          | 21    | age                         |               |
 | 
			
		||||
          | 22    | allow                       |               |
 | 
			
		||||
          | 23    | authorization               |               |
 | 
			
		||||
          | 24    | cache-control               |               |
 | 
			
		||||
          | 25    | content-disposition         |               |
 | 
			
		||||
          | 26    | content-encoding            |               |
 | 
			
		||||
          | 27    | content-language            |               |
 | 
			
		||||
          | 28    | content-length              |               |
 | 
			
		||||
          | 29    | content-location            |               |
 | 
			
		||||
          | 30    | content-range               |               |
 | 
			
		||||
          | 31    | content-type                |               |
 | 
			
		||||
          | 32    | cookie                      |               |
 | 
			
		||||
          | 33    | date                        |               |
 | 
			
		||||
          | 34    | etag                        |               |
 | 
			
		||||
          | 35    | expect                      |               |
 | 
			
		||||
          | 36    | expires                     |               |
 | 
			
		||||
          | 37    | from                        |               |
 | 
			
		||||
          | 38    | host                        |               |
 | 
			
		||||
          | 39    | if-match                    |               |
 | 
			
		||||
          | 40    | if-modified-since           |               |
 | 
			
		||||
          | 41    | if-none-match               |               |
 | 
			
		||||
          | 42    | if-range                    |               |
 | 
			
		||||
          | 43    | if-unmodified-since         |               |
 | 
			
		||||
          | 44    | last-modified               |               |
 | 
			
		||||
          | 45    | link                        |               |
 | 
			
		||||
          | 46    | location                    |               |
 | 
			
		||||
          | 47    | max-forwards                |               |
 | 
			
		||||
          | 48    | proxy-authenticate          |               |
 | 
			
		||||
          | 49    | proxy-authorization         |               |
 | 
			
		||||
          | 50    | range                       |               |
 | 
			
		||||
          | 51    | referer                     |               |
 | 
			
		||||
          | 52    | refresh                     |               |
 | 
			
		||||
          | 53    | retry-after                 |               |
 | 
			
		||||
          | 54    | server                      |               |
 | 
			
		||||
          | 55    | set-cookie                  |               |
 | 
			
		||||
          | 56    | strict-transport-security   |               |
 | 
			
		||||
          | 57    | transfer-encoding           |               |
 | 
			
		||||
          | 58    | user-agent                  |               |
 | 
			
		||||
          | 59    | vary                        |               |
 | 
			
		||||
          | 60    | via                         |               |
 | 
			
		||||
          | 61    | www-authenticate            |               |
 | 
			
		||||
          +-------+-----------------------------+---------------+
 | 
			
		||||
`
 | 
			
		||||
	bs := bufio.NewScanner(strings.NewReader(fromSpec))
 | 
			
		||||
	re := regexp.MustCompile(`\| (\d+)\s+\| (\S+)\s*\| (\S(.*\S)?)?\s+\|`)
 | 
			
		||||
	for bs.Scan() {
 | 
			
		||||
		l := bs.Text()
 | 
			
		||||
		if !strings.Contains(l, "|") {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		m := re.FindStringSubmatch(l)
 | 
			
		||||
		if m == nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		i, err := strconv.Atoi(m[1])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("Bogus integer on line %q", l)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if i < 1 || i > len(staticTable) {
 | 
			
		||||
			t.Errorf("Bogus index %d on line %q", i, l)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if got, want := staticTable[i-1].Name, m[2]; got != want {
 | 
			
		||||
			t.Errorf("header index %d name = %q; want %q", i, got, want)
 | 
			
		||||
		}
 | 
			
		||||
		if got, want := staticTable[i-1].Value, m[3]; got != want {
 | 
			
		||||
			t.Errorf("header index %d value = %q; want %q", i, got, want)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err := bs.Err(); err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *Decoder) mustAt(idx int) HeaderField {
 | 
			
		||||
	if hf, ok := d.at(uint64(idx)); !ok {
 | 
			
		||||
		panic(fmt.Sprintf("bogus index %d", idx))
 | 
			
		||||
@@ -31,10 +132,10 @@ func TestDynamicTableAt(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
	d.dynTab.add(pair("foo", "bar"))
 | 
			
		||||
	d.dynTab.add(pair("blake", "miz"))
 | 
			
		||||
	if got, want := at(staticTable.len()+1), (pair("blake", "miz")); got != want {
 | 
			
		||||
	if got, want := at(len(staticTable)+1), (pair("blake", "miz")); got != want {
 | 
			
		||||
		t.Errorf("at(dyn 1) = %v; want %v", got, want)
 | 
			
		||||
	}
 | 
			
		||||
	if got, want := at(staticTable.len()+2), (pair("foo", "bar")); got != want {
 | 
			
		||||
	if got, want := at(len(staticTable)+2), (pair("foo", "bar")); got != want {
 | 
			
		||||
		t.Errorf("at(dyn 2) = %v; want %v", got, want)
 | 
			
		||||
	}
 | 
			
		||||
	if got, want := at(3), (pair(":method", "POST")); got != want {
 | 
			
		||||
@@ -42,6 +143,41 @@ func TestDynamicTableAt(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestDynamicTableSearch(t *testing.T) {
 | 
			
		||||
	dt := dynamicTable{}
 | 
			
		||||
	dt.setMaxSize(4096)
 | 
			
		||||
 | 
			
		||||
	dt.add(pair("foo", "bar"))
 | 
			
		||||
	dt.add(pair("blake", "miz"))
 | 
			
		||||
	dt.add(pair(":method", "GET"))
 | 
			
		||||
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		hf        HeaderField
 | 
			
		||||
		wantI     uint64
 | 
			
		||||
		wantMatch bool
 | 
			
		||||
	}{
 | 
			
		||||
		// Name and Value match
 | 
			
		||||
		{pair("foo", "bar"), 3, true},
 | 
			
		||||
		{pair(":method", "GET"), 1, true},
 | 
			
		||||
 | 
			
		||||
		// Only name match because of Sensitive == true
 | 
			
		||||
		{HeaderField{"blake", "miz", true}, 2, false},
 | 
			
		||||
 | 
			
		||||
		// Only Name matches
 | 
			
		||||
		{pair("foo", "..."), 3, false},
 | 
			
		||||
		{pair("blake", "..."), 2, false},
 | 
			
		||||
		{pair(":method", "..."), 1, false},
 | 
			
		||||
 | 
			
		||||
		// None match
 | 
			
		||||
		{pair("foo-", "bar"), 0, false},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		if gotI, gotMatch := dt.search(tt.hf); gotI != tt.wantI || gotMatch != tt.wantMatch {
 | 
			
		||||
			t.Errorf("d.search(%+v) = %v, %v; want %v, %v", tt.hf, gotI, gotMatch, tt.wantI, tt.wantMatch)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestDynamicTableSizeEvict(t *testing.T) {
 | 
			
		||||
	d := NewDecoder(4096, nil)
 | 
			
		||||
	if want := uint32(0); d.dynTab.size != want {
 | 
			
		||||
@@ -60,7 +196,7 @@ func TestDynamicTableSizeEvict(t *testing.T) {
 | 
			
		||||
	if want := uint32(6 + 32); d.dynTab.size != want {
 | 
			
		||||
		t.Fatalf("after setMaxSize, size = %d; want %d", d.dynTab.size, want)
 | 
			
		||||
	}
 | 
			
		||||
	if got, want := d.mustAt(staticTable.len()+1), (pair("foo", "bar")); got != want {
 | 
			
		||||
	if got, want := d.mustAt(len(staticTable)+1), (pair("foo", "bar")); got != want {
 | 
			
		||||
		t.Errorf("at(dyn 1) = %v; want %v", got, want)
 | 
			
		||||
	}
 | 
			
		||||
	add(pair("long", strings.Repeat("x", 500)))
 | 
			
		||||
@@ -119,9 +255,9 @@ func TestDecoderDecode(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dt *dynamicTable) reverseCopy() (hf []HeaderField) {
 | 
			
		||||
	hf = make([]HeaderField, len(dt.table.ents))
 | 
			
		||||
	hf = make([]HeaderField, len(dt.ents))
 | 
			
		||||
	for i := range hf {
 | 
			
		||||
		hf[i] = dt.table.ents[len(dt.table.ents)-1-i]
 | 
			
		||||
		hf[i] = dt.ents[len(dt.ents)-1-i]
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										250
									
								
								vendor/golang.org/x/net/http2/hpack/tables.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										250
									
								
								vendor/golang.org/x/net/http2/hpack/tables.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -4,199 +4,73 @@
 | 
			
		||||
 | 
			
		||||
package hpack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// headerFieldTable implements a list of HeaderFields.
 | 
			
		||||
// This is used to implement the static and dynamic tables.
 | 
			
		||||
type headerFieldTable struct {
 | 
			
		||||
	// For static tables, entries are never evicted.
 | 
			
		||||
	//
 | 
			
		||||
	// For dynamic tables, entries are evicted from ents[0] and added to the end.
 | 
			
		||||
	// Each entry has a unique id that starts at one and increments for each
 | 
			
		||||
	// entry that is added. This unique id is stable across evictions, meaning
 | 
			
		||||
	// it can be used as a pointer to a specific entry. As in hpack, unique ids
 | 
			
		||||
	// are 1-based. The unique id for ents[k] is k + evictCount + 1.
 | 
			
		||||
	//
 | 
			
		||||
	// Zero is not a valid unique id.
 | 
			
		||||
	//
 | 
			
		||||
	// evictCount should not overflow in any remotely practical situation. In
 | 
			
		||||
	// practice, we will have one dynamic table per HTTP/2 connection. If we
 | 
			
		||||
	// assume a very powerful server that handles 1M QPS per connection and each
 | 
			
		||||
	// request adds (then evicts) 100 entries from the table, it would still take
 | 
			
		||||
	// 2M years for evictCount to overflow.
 | 
			
		||||
	ents       []HeaderField
 | 
			
		||||
	evictCount uint64
 | 
			
		||||
 | 
			
		||||
	// byName maps a HeaderField name to the unique id of the newest entry with
 | 
			
		||||
	// the same name. See above for a definition of "unique id".
 | 
			
		||||
	byName map[string]uint64
 | 
			
		||||
 | 
			
		||||
	// byNameValue maps a HeaderField name/value pair to the unique id of the newest
 | 
			
		||||
	// entry with the same name and value. See above for a definition of "unique id".
 | 
			
		||||
	byNameValue map[pairNameValue]uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type pairNameValue struct {
 | 
			
		||||
	name, value string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *headerFieldTable) init() {
 | 
			
		||||
	t.byName = make(map[string]uint64)
 | 
			
		||||
	t.byNameValue = make(map[pairNameValue]uint64)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// len reports the number of entries in the table.
 | 
			
		||||
func (t *headerFieldTable) len() int {
 | 
			
		||||
	return len(t.ents)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// addEntry adds a new entry.
 | 
			
		||||
func (t *headerFieldTable) addEntry(f HeaderField) {
 | 
			
		||||
	id := uint64(t.len()) + t.evictCount + 1
 | 
			
		||||
	t.byName[f.Name] = id
 | 
			
		||||
	t.byNameValue[pairNameValue{f.Name, f.Value}] = id
 | 
			
		||||
	t.ents = append(t.ents, f)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// evictOldest evicts the n oldest entries in the table.
 | 
			
		||||
func (t *headerFieldTable) evictOldest(n int) {
 | 
			
		||||
	if n > t.len() {
 | 
			
		||||
		panic(fmt.Sprintf("evictOldest(%v) on table with %v entries", n, t.len()))
 | 
			
		||||
	}
 | 
			
		||||
	for k := 0; k < n; k++ {
 | 
			
		||||
		f := t.ents[k]
 | 
			
		||||
		id := t.evictCount + uint64(k) + 1
 | 
			
		||||
		if t.byName[f.Name] == id {
 | 
			
		||||
			delete(t.byName, f.Name)
 | 
			
		||||
		}
 | 
			
		||||
		if p := (pairNameValue{f.Name, f.Value}); t.byNameValue[p] == id {
 | 
			
		||||
			delete(t.byNameValue, p)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	copy(t.ents, t.ents[n:])
 | 
			
		||||
	for k := t.len() - n; k < t.len(); k++ {
 | 
			
		||||
		t.ents[k] = HeaderField{} // so strings can be garbage collected
 | 
			
		||||
	}
 | 
			
		||||
	t.ents = t.ents[:t.len()-n]
 | 
			
		||||
	if t.evictCount+uint64(n) < t.evictCount {
 | 
			
		||||
		panic("evictCount overflow")
 | 
			
		||||
	}
 | 
			
		||||
	t.evictCount += uint64(n)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// search finds f in the table. If there is no match, i is 0.
 | 
			
		||||
// If both name and value match, i is the matched index and nameValueMatch
 | 
			
		||||
// becomes true. If only name matches, i points to that index and
 | 
			
		||||
// nameValueMatch becomes false.
 | 
			
		||||
//
 | 
			
		||||
// The returned index is a 1-based HPACK index. For dynamic tables, HPACK says
 | 
			
		||||
// that index 1 should be the newest entry, but t.ents[0] is the oldest entry,
 | 
			
		||||
// meaning t.ents is reversed for dynamic tables. Hence, when t is a dynamic
 | 
			
		||||
// table, the return value i actually refers to the entry t.ents[t.len()-i].
 | 
			
		||||
//
 | 
			
		||||
// All tables are assumed to be a dynamic tables except for the global
 | 
			
		||||
// staticTable pointer.
 | 
			
		||||
//
 | 
			
		||||
// See Section 2.3.3.
 | 
			
		||||
func (t *headerFieldTable) search(f HeaderField) (i uint64, nameValueMatch bool) {
 | 
			
		||||
	if !f.Sensitive {
 | 
			
		||||
		if id := t.byNameValue[pairNameValue{f.Name, f.Value}]; id != 0 {
 | 
			
		||||
			return t.idToIndex(id), true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if id := t.byName[f.Name]; id != 0 {
 | 
			
		||||
		return t.idToIndex(id), false
 | 
			
		||||
	}
 | 
			
		||||
	return 0, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// idToIndex converts a unique id to an HPACK index.
 | 
			
		||||
// See Section 2.3.3.
 | 
			
		||||
func (t *headerFieldTable) idToIndex(id uint64) uint64 {
 | 
			
		||||
	if id <= t.evictCount {
 | 
			
		||||
		panic(fmt.Sprintf("id (%v) <= evictCount (%v)", id, t.evictCount))
 | 
			
		||||
	}
 | 
			
		||||
	k := id - t.evictCount - 1 // convert id to an index t.ents[k]
 | 
			
		||||
	if t != staticTable {
 | 
			
		||||
		return uint64(t.len()) - k // dynamic table
 | 
			
		||||
	}
 | 
			
		||||
	return k + 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func pair(name, value string) HeaderField {
 | 
			
		||||
	return HeaderField{Name: name, Value: value}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07#appendix-B
 | 
			
		||||
var staticTable = newStaticTable()
 | 
			
		||||
 | 
			
		||||
func newStaticTable() *headerFieldTable {
 | 
			
		||||
	t := &headerFieldTable{}
 | 
			
		||||
	t.init()
 | 
			
		||||
	t.addEntry(pair(":authority", ""))
 | 
			
		||||
	t.addEntry(pair(":method", "GET"))
 | 
			
		||||
	t.addEntry(pair(":method", "POST"))
 | 
			
		||||
	t.addEntry(pair(":path", "/"))
 | 
			
		||||
	t.addEntry(pair(":path", "/index.html"))
 | 
			
		||||
	t.addEntry(pair(":scheme", "http"))
 | 
			
		||||
	t.addEntry(pair(":scheme", "https"))
 | 
			
		||||
	t.addEntry(pair(":status", "200"))
 | 
			
		||||
	t.addEntry(pair(":status", "204"))
 | 
			
		||||
	t.addEntry(pair(":status", "206"))
 | 
			
		||||
	t.addEntry(pair(":status", "304"))
 | 
			
		||||
	t.addEntry(pair(":status", "400"))
 | 
			
		||||
	t.addEntry(pair(":status", "404"))
 | 
			
		||||
	t.addEntry(pair(":status", "500"))
 | 
			
		||||
	t.addEntry(pair("accept-charset", ""))
 | 
			
		||||
	t.addEntry(pair("accept-encoding", "gzip, deflate"))
 | 
			
		||||
	t.addEntry(pair("accept-language", ""))
 | 
			
		||||
	t.addEntry(pair("accept-ranges", ""))
 | 
			
		||||
	t.addEntry(pair("accept", ""))
 | 
			
		||||
	t.addEntry(pair("access-control-allow-origin", ""))
 | 
			
		||||
	t.addEntry(pair("age", ""))
 | 
			
		||||
	t.addEntry(pair("allow", ""))
 | 
			
		||||
	t.addEntry(pair("authorization", ""))
 | 
			
		||||
	t.addEntry(pair("cache-control", ""))
 | 
			
		||||
	t.addEntry(pair("content-disposition", ""))
 | 
			
		||||
	t.addEntry(pair("content-encoding", ""))
 | 
			
		||||
	t.addEntry(pair("content-language", ""))
 | 
			
		||||
	t.addEntry(pair("content-length", ""))
 | 
			
		||||
	t.addEntry(pair("content-location", ""))
 | 
			
		||||
	t.addEntry(pair("content-range", ""))
 | 
			
		||||
	t.addEntry(pair("content-type", ""))
 | 
			
		||||
	t.addEntry(pair("cookie", ""))
 | 
			
		||||
	t.addEntry(pair("date", ""))
 | 
			
		||||
	t.addEntry(pair("etag", ""))
 | 
			
		||||
	t.addEntry(pair("expect", ""))
 | 
			
		||||
	t.addEntry(pair("expires", ""))
 | 
			
		||||
	t.addEntry(pair("from", ""))
 | 
			
		||||
	t.addEntry(pair("host", ""))
 | 
			
		||||
	t.addEntry(pair("if-match", ""))
 | 
			
		||||
	t.addEntry(pair("if-modified-since", ""))
 | 
			
		||||
	t.addEntry(pair("if-none-match", ""))
 | 
			
		||||
	t.addEntry(pair("if-range", ""))
 | 
			
		||||
	t.addEntry(pair("if-unmodified-since", ""))
 | 
			
		||||
	t.addEntry(pair("last-modified", ""))
 | 
			
		||||
	t.addEntry(pair("link", ""))
 | 
			
		||||
	t.addEntry(pair("location", ""))
 | 
			
		||||
	t.addEntry(pair("max-forwards", ""))
 | 
			
		||||
	t.addEntry(pair("proxy-authenticate", ""))
 | 
			
		||||
	t.addEntry(pair("proxy-authorization", ""))
 | 
			
		||||
	t.addEntry(pair("range", ""))
 | 
			
		||||
	t.addEntry(pair("referer", ""))
 | 
			
		||||
	t.addEntry(pair("refresh", ""))
 | 
			
		||||
	t.addEntry(pair("retry-after", ""))
 | 
			
		||||
	t.addEntry(pair("server", ""))
 | 
			
		||||
	t.addEntry(pair("set-cookie", ""))
 | 
			
		||||
	t.addEntry(pair("strict-transport-security", ""))
 | 
			
		||||
	t.addEntry(pair("transfer-encoding", ""))
 | 
			
		||||
	t.addEntry(pair("user-agent", ""))
 | 
			
		||||
	t.addEntry(pair("vary", ""))
 | 
			
		||||
	t.addEntry(pair("via", ""))
 | 
			
		||||
	t.addEntry(pair("www-authenticate", ""))
 | 
			
		||||
	return t
 | 
			
		||||
var staticTable = [...]HeaderField{
 | 
			
		||||
	pair(":authority", ""), // index 1 (1-based)
 | 
			
		||||
	pair(":method", "GET"),
 | 
			
		||||
	pair(":method", "POST"),
 | 
			
		||||
	pair(":path", "/"),
 | 
			
		||||
	pair(":path", "/index.html"),
 | 
			
		||||
	pair(":scheme", "http"),
 | 
			
		||||
	pair(":scheme", "https"),
 | 
			
		||||
	pair(":status", "200"),
 | 
			
		||||
	pair(":status", "204"),
 | 
			
		||||
	pair(":status", "206"),
 | 
			
		||||
	pair(":status", "304"),
 | 
			
		||||
	pair(":status", "400"),
 | 
			
		||||
	pair(":status", "404"),
 | 
			
		||||
	pair(":status", "500"),
 | 
			
		||||
	pair("accept-charset", ""),
 | 
			
		||||
	pair("accept-encoding", "gzip, deflate"),
 | 
			
		||||
	pair("accept-language", ""),
 | 
			
		||||
	pair("accept-ranges", ""),
 | 
			
		||||
	pair("accept", ""),
 | 
			
		||||
	pair("access-control-allow-origin", ""),
 | 
			
		||||
	pair("age", ""),
 | 
			
		||||
	pair("allow", ""),
 | 
			
		||||
	pair("authorization", ""),
 | 
			
		||||
	pair("cache-control", ""),
 | 
			
		||||
	pair("content-disposition", ""),
 | 
			
		||||
	pair("content-encoding", ""),
 | 
			
		||||
	pair("content-language", ""),
 | 
			
		||||
	pair("content-length", ""),
 | 
			
		||||
	pair("content-location", ""),
 | 
			
		||||
	pair("content-range", ""),
 | 
			
		||||
	pair("content-type", ""),
 | 
			
		||||
	pair("cookie", ""),
 | 
			
		||||
	pair("date", ""),
 | 
			
		||||
	pair("etag", ""),
 | 
			
		||||
	pair("expect", ""),
 | 
			
		||||
	pair("expires", ""),
 | 
			
		||||
	pair("from", ""),
 | 
			
		||||
	pair("host", ""),
 | 
			
		||||
	pair("if-match", ""),
 | 
			
		||||
	pair("if-modified-since", ""),
 | 
			
		||||
	pair("if-none-match", ""),
 | 
			
		||||
	pair("if-range", ""),
 | 
			
		||||
	pair("if-unmodified-since", ""),
 | 
			
		||||
	pair("last-modified", ""),
 | 
			
		||||
	pair("link", ""),
 | 
			
		||||
	pair("location", ""),
 | 
			
		||||
	pair("max-forwards", ""),
 | 
			
		||||
	pair("proxy-authenticate", ""),
 | 
			
		||||
	pair("proxy-authorization", ""),
 | 
			
		||||
	pair("range", ""),
 | 
			
		||||
	pair("referer", ""),
 | 
			
		||||
	pair("refresh", ""),
 | 
			
		||||
	pair("retry-after", ""),
 | 
			
		||||
	pair("server", ""),
 | 
			
		||||
	pair("set-cookie", ""),
 | 
			
		||||
	pair("strict-transport-security", ""),
 | 
			
		||||
	pair("transfer-encoding", ""),
 | 
			
		||||
	pair("user-agent", ""),
 | 
			
		||||
	pair("vary", ""),
 | 
			
		||||
	pair("via", ""),
 | 
			
		||||
	pair("www-authenticate", ""),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var huffmanCodes = [256]uint32{
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										214
									
								
								vendor/golang.org/x/net/http2/hpack/tables_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										214
									
								
								vendor/golang.org/x/net/http2/hpack/tables_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,214 +0,0 @@
 | 
			
		||||
// Copyright 2017 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package hpack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestHeaderFieldTable(t *testing.T) {
 | 
			
		||||
	table := &headerFieldTable{}
 | 
			
		||||
	table.init()
 | 
			
		||||
	table.addEntry(pair("key1", "value1-1"))
 | 
			
		||||
	table.addEntry(pair("key2", "value2-1"))
 | 
			
		||||
	table.addEntry(pair("key1", "value1-2"))
 | 
			
		||||
	table.addEntry(pair("key3", "value3-1"))
 | 
			
		||||
	table.addEntry(pair("key4", "value4-1"))
 | 
			
		||||
	table.addEntry(pair("key2", "value2-2"))
 | 
			
		||||
 | 
			
		||||
	// Tests will be run twice: once before evicting anything, and
 | 
			
		||||
	// again after evicting the three oldest entries.
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		f                 HeaderField
 | 
			
		||||
		beforeWantStaticI uint64
 | 
			
		||||
		beforeWantMatch   bool
 | 
			
		||||
		afterWantStaticI  uint64
 | 
			
		||||
		afterWantMatch    bool
 | 
			
		||||
	}{
 | 
			
		||||
		{HeaderField{"key1", "value1-1", false}, 1, true, 0, false},
 | 
			
		||||
		{HeaderField{"key1", "value1-2", false}, 3, true, 0, false},
 | 
			
		||||
		{HeaderField{"key1", "value1-3", false}, 3, false, 0, false},
 | 
			
		||||
		{HeaderField{"key2", "value2-1", false}, 2, true, 3, false},
 | 
			
		||||
		{HeaderField{"key2", "value2-2", false}, 6, true, 3, true},
 | 
			
		||||
		{HeaderField{"key2", "value2-3", false}, 6, false, 3, false},
 | 
			
		||||
		{HeaderField{"key4", "value4-1", false}, 5, true, 2, true},
 | 
			
		||||
		// Name match only, because sensitive.
 | 
			
		||||
		{HeaderField{"key4", "value4-1", true}, 5, false, 2, false},
 | 
			
		||||
		// Key not found.
 | 
			
		||||
		{HeaderField{"key5", "value5-x", false}, 0, false, 0, false},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	staticToDynamic := func(i uint64) uint64 {
 | 
			
		||||
		if i == 0 {
 | 
			
		||||
			return 0
 | 
			
		||||
		}
 | 
			
		||||
		return uint64(table.len()) - i + 1 // dynamic is the reversed table
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	searchStatic := func(f HeaderField) (uint64, bool) {
 | 
			
		||||
		old := staticTable
 | 
			
		||||
		staticTable = table
 | 
			
		||||
		defer func() { staticTable = old }()
 | 
			
		||||
		return staticTable.search(f)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	searchDynamic := func(f HeaderField) (uint64, bool) {
 | 
			
		||||
		return table.search(f)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		gotI, gotMatch := searchStatic(test.f)
 | 
			
		||||
		if wantI, wantMatch := test.beforeWantStaticI, test.beforeWantMatch; gotI != wantI || gotMatch != wantMatch {
 | 
			
		||||
			t.Errorf("before evictions: searchStatic(%+v)=%v,%v want %v,%v", test.f, gotI, gotMatch, wantI, wantMatch)
 | 
			
		||||
		}
 | 
			
		||||
		gotI, gotMatch = searchDynamic(test.f)
 | 
			
		||||
		wantDynamicI := staticToDynamic(test.beforeWantStaticI)
 | 
			
		||||
		if wantI, wantMatch := wantDynamicI, test.beforeWantMatch; gotI != wantI || gotMatch != wantMatch {
 | 
			
		||||
			t.Errorf("before evictions: searchDynamic(%+v)=%v,%v want %v,%v", test.f, gotI, gotMatch, wantI, wantMatch)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	table.evictOldest(3)
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		gotI, gotMatch := searchStatic(test.f)
 | 
			
		||||
		if wantI, wantMatch := test.afterWantStaticI, test.afterWantMatch; gotI != wantI || gotMatch != wantMatch {
 | 
			
		||||
			t.Errorf("after evictions: searchStatic(%+v)=%v,%v want %v,%v", test.f, gotI, gotMatch, wantI, wantMatch)
 | 
			
		||||
		}
 | 
			
		||||
		gotI, gotMatch = searchDynamic(test.f)
 | 
			
		||||
		wantDynamicI := staticToDynamic(test.afterWantStaticI)
 | 
			
		||||
		if wantI, wantMatch := wantDynamicI, test.afterWantMatch; gotI != wantI || gotMatch != wantMatch {
 | 
			
		||||
			t.Errorf("after evictions: searchDynamic(%+v)=%v,%v want %v,%v", test.f, gotI, gotMatch, wantI, wantMatch)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestHeaderFieldTable_LookupMapEviction(t *testing.T) {
 | 
			
		||||
	table := &headerFieldTable{}
 | 
			
		||||
	table.init()
 | 
			
		||||
	table.addEntry(pair("key1", "value1-1"))
 | 
			
		||||
	table.addEntry(pair("key2", "value2-1"))
 | 
			
		||||
	table.addEntry(pair("key1", "value1-2"))
 | 
			
		||||
	table.addEntry(pair("key3", "value3-1"))
 | 
			
		||||
	table.addEntry(pair("key4", "value4-1"))
 | 
			
		||||
	table.addEntry(pair("key2", "value2-2"))
 | 
			
		||||
 | 
			
		||||
	// evict all pairs
 | 
			
		||||
	table.evictOldest(table.len())
 | 
			
		||||
 | 
			
		||||
	if l := table.len(); l > 0 {
 | 
			
		||||
		t.Errorf("table.len() = %d, want 0", l)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if l := len(table.byName); l > 0 {
 | 
			
		||||
		t.Errorf("len(table.byName) = %d, want 0", l)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if l := len(table.byNameValue); l > 0 {
 | 
			
		||||
		t.Errorf("len(table.byNameValue) = %d, want 0", l)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestStaticTable(t *testing.T) {
 | 
			
		||||
	fromSpec := `
 | 
			
		||||
          +-------+-----------------------------+---------------+
 | 
			
		||||
          | 1     | :authority                  |               |
 | 
			
		||||
          | 2     | :method                     | GET           |
 | 
			
		||||
          | 3     | :method                     | POST          |
 | 
			
		||||
          | 4     | :path                       | /             |
 | 
			
		||||
          | 5     | :path                       | /index.html   |
 | 
			
		||||
          | 6     | :scheme                     | http          |
 | 
			
		||||
          | 7     | :scheme                     | https         |
 | 
			
		||||
          | 8     | :status                     | 200           |
 | 
			
		||||
          | 9     | :status                     | 204           |
 | 
			
		||||
          | 10    | :status                     | 206           |
 | 
			
		||||
          | 11    | :status                     | 304           |
 | 
			
		||||
          | 12    | :status                     | 400           |
 | 
			
		||||
          | 13    | :status                     | 404           |
 | 
			
		||||
          | 14    | :status                     | 500           |
 | 
			
		||||
          | 15    | accept-charset              |               |
 | 
			
		||||
          | 16    | accept-encoding             | gzip, deflate |
 | 
			
		||||
          | 17    | accept-language             |               |
 | 
			
		||||
          | 18    | accept-ranges               |               |
 | 
			
		||||
          | 19    | accept                      |               |
 | 
			
		||||
          | 20    | access-control-allow-origin |               |
 | 
			
		||||
          | 21    | age                         |               |
 | 
			
		||||
          | 22    | allow                       |               |
 | 
			
		||||
          | 23    | authorization               |               |
 | 
			
		||||
          | 24    | cache-control               |               |
 | 
			
		||||
          | 25    | content-disposition         |               |
 | 
			
		||||
          | 26    | content-encoding            |               |
 | 
			
		||||
          | 27    | content-language            |               |
 | 
			
		||||
          | 28    | content-length              |               |
 | 
			
		||||
          | 29    | content-location            |               |
 | 
			
		||||
          | 30    | content-range               |               |
 | 
			
		||||
          | 31    | content-type                |               |
 | 
			
		||||
          | 32    | cookie                      |               |
 | 
			
		||||
          | 33    | date                        |               |
 | 
			
		||||
          | 34    | etag                        |               |
 | 
			
		||||
          | 35    | expect                      |               |
 | 
			
		||||
          | 36    | expires                     |               |
 | 
			
		||||
          | 37    | from                        |               |
 | 
			
		||||
          | 38    | host                        |               |
 | 
			
		||||
          | 39    | if-match                    |               |
 | 
			
		||||
          | 40    | if-modified-since           |               |
 | 
			
		||||
          | 41    | if-none-match               |               |
 | 
			
		||||
          | 42    | if-range                    |               |
 | 
			
		||||
          | 43    | if-unmodified-since         |               |
 | 
			
		||||
          | 44    | last-modified               |               |
 | 
			
		||||
          | 45    | link                        |               |
 | 
			
		||||
          | 46    | location                    |               |
 | 
			
		||||
          | 47    | max-forwards                |               |
 | 
			
		||||
          | 48    | proxy-authenticate          |               |
 | 
			
		||||
          | 49    | proxy-authorization         |               |
 | 
			
		||||
          | 50    | range                       |               |
 | 
			
		||||
          | 51    | referer                     |               |
 | 
			
		||||
          | 52    | refresh                     |               |
 | 
			
		||||
          | 53    | retry-after                 |               |
 | 
			
		||||
          | 54    | server                      |               |
 | 
			
		||||
          | 55    | set-cookie                  |               |
 | 
			
		||||
          | 56    | strict-transport-security   |               |
 | 
			
		||||
          | 57    | transfer-encoding           |               |
 | 
			
		||||
          | 58    | user-agent                  |               |
 | 
			
		||||
          | 59    | vary                        |               |
 | 
			
		||||
          | 60    | via                         |               |
 | 
			
		||||
          | 61    | www-authenticate            |               |
 | 
			
		||||
          +-------+-----------------------------+---------------+
 | 
			
		||||
`
 | 
			
		||||
	bs := bufio.NewScanner(strings.NewReader(fromSpec))
 | 
			
		||||
	re := regexp.MustCompile(`\| (\d+)\s+\| (\S+)\s*\| (\S(.*\S)?)?\s+\|`)
 | 
			
		||||
	for bs.Scan() {
 | 
			
		||||
		l := bs.Text()
 | 
			
		||||
		if !strings.Contains(l, "|") {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		m := re.FindStringSubmatch(l)
 | 
			
		||||
		if m == nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		i, err := strconv.Atoi(m[1])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("Bogus integer on line %q", l)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if i < 1 || i > staticTable.len() {
 | 
			
		||||
			t.Errorf("Bogus index %d on line %q", i, l)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if got, want := staticTable.ents[i-1].Name, m[2]; got != want {
 | 
			
		||||
			t.Errorf("header index %d name = %q; want %q", i, got, want)
 | 
			
		||||
		}
 | 
			
		||||
		if got, want := staticTable.ents[i-1].Value, m[3]; got != want {
 | 
			
		||||
			t.Errorf("header index %d value = %q; want %q", i, got, want)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err := bs.Err(); err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user