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 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)
- }
- }
|