golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/http2/http2_test.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package http2 6 7 import ( 8 "bytes" 9 "flag" 10 "fmt" 11 "io/ioutil" 12 "net/http" 13 "os" 14 "path/filepath" 15 "regexp" 16 "strings" 17 "testing" 18 "time" 19 20 "golang.org/x/net/http2/hpack" 21 ) 22 23 var knownFailing = flag.Bool("known_failing", false, "Run known-failing tests.") 24 25 func condSkipFailingTest(t *testing.T) { 26 if !*knownFailing { 27 t.Skip("Skipping known-failing test without --known_failing") 28 } 29 } 30 31 func init() { 32 inTests = true 33 DebugGoroutines = true 34 flag.BoolVar(&VerboseLogs, "verboseh2", VerboseLogs, "Verbose HTTP/2 debug logging") 35 } 36 37 func TestSettingString(t *testing.T) { 38 tests := []struct { 39 s Setting 40 want string 41 }{ 42 {Setting{SettingMaxFrameSize, 123}, "[MAX_FRAME_SIZE = 123]"}, 43 {Setting{1<<16 - 1, 123}, "[UNKNOWN_SETTING_65535 = 123]"}, 44 } 45 for i, tt := range tests { 46 got := fmt.Sprint(tt.s) 47 if got != tt.want { 48 t.Errorf("%d. for %#v, string = %q; want %q", i, tt.s, got, tt.want) 49 } 50 } 51 } 52 53 type twriter struct { 54 t testing.TB 55 st *serverTester // optional 56 } 57 58 func (w twriter) Write(p []byte) (n int, err error) { 59 if w.st != nil { 60 ps := string(p) 61 for _, phrase := range w.st.logFilter { 62 if strings.Contains(ps, phrase) { 63 return len(p), nil // no logging 64 } 65 } 66 } 67 w.t.Logf("%s", p) 68 return len(p), nil 69 } 70 71 // like encodeHeader, but don't add implicit pseudo headers. 72 func encodeHeaderNoImplicit(t *testing.T, headers ...string) []byte { 73 var buf bytes.Buffer 74 enc := hpack.NewEncoder(&buf) 75 for len(headers) > 0 { 76 k, v := headers[0], headers[1] 77 headers = headers[2:] 78 if err := enc.WriteField(hpack.HeaderField{Name: k, Value: v}); err != nil { 79 t.Fatalf("HPACK encoding error for %q/%q: %v", k, v, err) 80 } 81 } 82 return buf.Bytes() 83 } 84 85 type puppetCommand struct { 86 fn func(w http.ResponseWriter, r *http.Request) 87 done chan<- bool 88 } 89 90 type handlerPuppet struct { 91 ch chan puppetCommand 92 } 93 94 func newHandlerPuppet() *handlerPuppet { 95 return &handlerPuppet{ 96 ch: make(chan puppetCommand), 97 } 98 } 99 100 func (p *handlerPuppet) act(w http.ResponseWriter, r *http.Request) { 101 for cmd := range p.ch { 102 cmd.fn(w, r) 103 cmd.done <- true 104 } 105 } 106 107 func (p *handlerPuppet) done() { close(p.ch) } 108 func (p *handlerPuppet) do(fn func(http.ResponseWriter, *http.Request)) { 109 done := make(chan bool) 110 p.ch <- puppetCommand{fn, done} 111 <-done 112 } 113 114 func cleanDate(res *http.Response) { 115 if d := res.Header["Date"]; len(d) == 1 { 116 d[0] = "XXX" 117 } 118 } 119 120 func TestSorterPoolAllocs(t *testing.T) { 121 ss := []string{"a", "b", "c"} 122 h := http.Header{ 123 "a": nil, 124 "b": nil, 125 "c": nil, 126 } 127 sorter := new(sorter) 128 129 if allocs := testing.AllocsPerRun(100, func() { 130 sorter.SortStrings(ss) 131 }); allocs >= 1 { 132 t.Logf("SortStrings allocs = %v; want <1", allocs) 133 } 134 135 if allocs := testing.AllocsPerRun(5, func() { 136 if len(sorter.Keys(h)) != 3 { 137 t.Fatal("wrong result") 138 } 139 }); allocs > 0 { 140 t.Logf("Keys allocs = %v; want <1", allocs) 141 } 142 } 143 144 // waitCondition reports whether fn eventually returned true, 145 // checking immediately and then every checkEvery amount, 146 // until waitFor has elapsed, at which point it returns false. 147 func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool { 148 deadline := time.Now().Add(waitFor) 149 for time.Now().Before(deadline) { 150 if fn() { 151 return true 152 } 153 time.Sleep(checkEvery) 154 } 155 return false 156 } 157 158 // waitErrCondition is like waitCondition but with errors instead of bools. 159 func waitErrCondition(waitFor, checkEvery time.Duration, fn func() error) error { 160 deadline := time.Now().Add(waitFor) 161 var err error 162 for time.Now().Before(deadline) { 163 if err = fn(); err == nil { 164 return nil 165 } 166 time.Sleep(checkEvery) 167 } 168 return err 169 } 170 171 func equalError(a, b error) bool { 172 if a == nil { 173 return b == nil 174 } 175 if b == nil { 176 return a == nil 177 } 178 return a.Error() == b.Error() 179 } 180 181 // Tests that http2.Server.IdleTimeout is initialized from 182 // http.Server.{Idle,Read}Timeout. http.Server.IdleTimeout was 183 // added in Go 1.8. 184 func TestConfigureServerIdleTimeout_Go18(t *testing.T) { 185 const timeout = 5 * time.Second 186 const notThisOne = 1 * time.Second 187 188 // With a zero http2.Server, verify that it copies IdleTimeout: 189 { 190 s1 := &http.Server{ 191 IdleTimeout: timeout, 192 ReadTimeout: notThisOne, 193 } 194 s2 := &Server{} 195 if err := ConfigureServer(s1, s2); err != nil { 196 t.Fatal(err) 197 } 198 if s2.IdleTimeout != timeout { 199 t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout) 200 } 201 } 202 203 // And that it falls back to ReadTimeout: 204 { 205 s1 := &http.Server{ 206 ReadTimeout: timeout, 207 } 208 s2 := &Server{} 209 if err := ConfigureServer(s1, s2); err != nil { 210 t.Fatal(err) 211 } 212 if s2.IdleTimeout != timeout { 213 t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout) 214 } 215 } 216 217 // Verify that s1's IdleTimeout doesn't overwrite an existing setting: 218 { 219 s1 := &http.Server{ 220 IdleTimeout: notThisOne, 221 } 222 s2 := &Server{ 223 IdleTimeout: timeout, 224 } 225 if err := ConfigureServer(s1, s2); err != nil { 226 t.Fatal(err) 227 } 228 if s2.IdleTimeout != timeout { 229 t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout) 230 } 231 } 232 } 233 234 var forbiddenStringsFunctions = map[string]bool{ 235 // Functions that use Unicode-aware case folding. 236 "EqualFold": true, 237 "Title": true, 238 "ToLower": true, 239 "ToLowerSpecial": true, 240 "ToTitle": true, 241 "ToTitleSpecial": true, 242 "ToUpper": true, 243 "ToUpperSpecial": true, 244 245 // Functions that use Unicode-aware spaces. 246 "Fields": true, 247 "TrimSpace": true, 248 } 249 250 // TestNoUnicodeStrings checks that nothing in net/http uses the Unicode-aware 251 // strings and bytes package functions. HTTP is mostly ASCII based, and doing 252 // Unicode-aware case folding or space stripping can introduce vulnerabilities. 253 func TestNoUnicodeStrings(t *testing.T) { 254 re := regexp.MustCompile(`(strings|bytes).([A-Za-z]+)`) 255 if err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error { 256 if err != nil { 257 t.Fatal(err) 258 } 259 260 if path == "h2i" || path == "h2c" { 261 return filepath.SkipDir 262 } 263 if !strings.HasSuffix(path, ".go") || 264 strings.HasSuffix(path, "_test.go") || 265 path == "ascii.go" || info.IsDir() { 266 return nil 267 } 268 269 contents, err := ioutil.ReadFile(path) 270 if err != nil { 271 t.Fatal(err) 272 } 273 for lineNum, line := range strings.Split(string(contents), "\n") { 274 for _, match := range re.FindAllStringSubmatch(line, -1) { 275 if !forbiddenStringsFunctions[match[2]] { 276 continue 277 } 278 t.Errorf("disallowed call to %s at %s:%d", match[0], path, lineNum+1) 279 } 280 } 281 282 return nil 283 }); err != nil { 284 t.Fatal(err) 285 } 286 }