| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 | // 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 hpackimport (	"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)	}}
 |