gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/net/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 "errors" 10 "flag" 11 "fmt" 12 "io/ioutil" 13 "os" 14 "os/exec" 15 "path/filepath" 16 "regexp" 17 "strconv" 18 "strings" 19 "testing" 20 "time" 21 22 http "gitee.com/ks-custle/core-gm/gmhttp" 23 24 "gitee.com/ks-custle/core-gm/net/http2/hpack" 25 ) 26 27 var knownFailing = flag.Bool("known_failing", false, "Run known-failing tests.") 28 29 func condSkipFailingTest(t *testing.T) { 30 if !*knownFailing { 31 t.Skip("Skipping known-failing test without --known_failing") 32 } 33 } 34 35 func init() { 36 inTests = true 37 DebugGoroutines = true 38 flag.BoolVar(&VerboseLogs, "verboseh2", VerboseLogs, "Verbose HTTP/2 debug logging") 39 } 40 41 func TestSettingString(t *testing.T) { 42 tests := []struct { 43 s Setting 44 want string 45 }{ 46 {Setting{SettingMaxFrameSize, 123}, "[MAX_FRAME_SIZE = 123]"}, 47 {Setting{1<<16 - 1, 123}, "[UNKNOWN_SETTING_65535 = 123]"}, 48 } 49 for i, tt := range tests { 50 got := fmt.Sprint(tt.s) 51 if got != tt.want { 52 t.Errorf("%d. for %#v, string = %q; want %q", i, tt.s, got, tt.want) 53 } 54 } 55 } 56 57 type twriter struct { 58 t testing.TB 59 st *serverTester // optional 60 } 61 62 func (w twriter) Write(p []byte) (n int, err error) { 63 if w.st != nil { 64 ps := string(p) 65 for _, phrase := range w.st.logFilter { 66 if strings.Contains(ps, phrase) { 67 return len(p), nil // no logging 68 } 69 } 70 } 71 w.t.Logf("%s", p) 72 return len(p), nil 73 } 74 75 // like encodeHeader, but don't add implicit pseudo headers. 76 func encodeHeaderNoImplicit(t *testing.T, headers ...string) []byte { 77 var buf bytes.Buffer 78 enc := hpack.NewEncoder(&buf) 79 for len(headers) > 0 { 80 k, v := headers[0], headers[1] 81 headers = headers[2:] 82 if err := enc.WriteField(hpack.HeaderField{Name: k, Value: v}); err != nil { 83 t.Fatalf("HPACK encoding error for %q/%q: %v", k, v, err) 84 } 85 } 86 return buf.Bytes() 87 } 88 89 // Verify that curl has http2. 90 func requireCurl(t *testing.T) { 91 out, err := dockerLogs(curl(t, "--version")) 92 if err != nil { 93 t.Skipf("failed to determine curl features; skipping test") 94 } 95 if !strings.Contains(string(out), "HTTP2") { 96 t.Skip("curl doesn't support HTTP2; skipping test") 97 } 98 } 99 100 func curl(t *testing.T, args ...string) (container string) { 101 out, err := exec.Command("docker", append([]string{"run", "-d", "--net=host", "gohttp2/curl"}, args...)...).Output() 102 if err != nil { 103 t.Skipf("Failed to run curl in docker: %v, %s", err, out) 104 } 105 return strings.TrimSpace(string(out)) 106 } 107 108 // Verify that h2load exists. 109 func requireH2load(t *testing.T) { 110 out, err := dockerLogs(h2load(t, "--version")) 111 if err != nil { 112 t.Skipf("failed to probe h2load; skipping test: %s", out) 113 } 114 if !strings.Contains(string(out), "h2load nghttp2/") { 115 t.Skipf("h2load not present; skipping test. (Output=%q)", out) 116 } 117 } 118 119 func h2load(t *testing.T, args ...string) (container string) { 120 out, err := exec.Command("docker", append([]string{"run", "-d", "--net=host", "--entrypoint=/usr/local/bin/h2load", "gohttp2/curl"}, args...)...).Output() 121 if err != nil { 122 t.Skipf("Failed to run h2load in docker: %v, %s", err, out) 123 } 124 return strings.TrimSpace(string(out)) 125 } 126 127 type puppetCommand struct { 128 fn func(w http.ResponseWriter, r *http.Request) 129 done chan<- bool 130 } 131 132 type handlerPuppet struct { 133 ch chan puppetCommand 134 } 135 136 func newHandlerPuppet() *handlerPuppet { 137 return &handlerPuppet{ 138 ch: make(chan puppetCommand), 139 } 140 } 141 142 func (p *handlerPuppet) act(w http.ResponseWriter, r *http.Request) { 143 for cmd := range p.ch { 144 cmd.fn(w, r) 145 cmd.done <- true 146 } 147 } 148 149 func (p *handlerPuppet) done() { close(p.ch) } 150 func (p *handlerPuppet) do(fn func(http.ResponseWriter, *http.Request)) { 151 done := make(chan bool) 152 p.ch <- puppetCommand{fn, done} 153 <-done 154 } 155 func dockerLogs(container string) ([]byte, error) { 156 out, err := exec.Command("docker", "wait", container).CombinedOutput() 157 if err != nil { 158 return out, err 159 } 160 exitStatus, err := strconv.Atoi(strings.TrimSpace(string(out))) 161 if err != nil { 162 return out, errors.New("unexpected exit status from docker wait") 163 } 164 out, err = exec.Command("docker", "logs", container).CombinedOutput() 165 exec.Command("docker", "rm", container).Run() 166 if err == nil && exitStatus != 0 { 167 err = fmt.Errorf("exit status %d: %s", exitStatus, out) 168 } 169 return out, err 170 } 171 172 func kill(container string) { 173 exec.Command("docker", "kill", container).Run() 174 exec.Command("docker", "rm", container).Run() 175 } 176 177 func cleanDate(res *http.Response) { 178 if d := res.Header["Date"]; len(d) == 1 { 179 d[0] = "XXX" 180 } 181 } 182 183 func TestSorterPoolAllocs(t *testing.T) { 184 ss := []string{"a", "b", "c"} 185 h := http.Header{ 186 "a": nil, 187 "b": nil, 188 "c": nil, 189 } 190 sorter := new(sorter) 191 192 if allocs := testing.AllocsPerRun(100, func() { 193 sorter.SortStrings(ss) 194 }); allocs >= 1 { 195 t.Logf("SortStrings allocs = %v; want <1", allocs) 196 } 197 198 if allocs := testing.AllocsPerRun(5, func() { 199 if len(sorter.Keys(h)) != 3 { 200 t.Fatal("wrong result") 201 } 202 }); allocs > 0 { 203 t.Logf("Keys allocs = %v; want <1", allocs) 204 } 205 } 206 207 // waitCondition reports whether fn eventually returned true, 208 // checking immediately and then every checkEvery amount, 209 // until waitFor has elapsed, at which point it returns false. 210 func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool { 211 deadline := time.Now().Add(waitFor) 212 for time.Now().Before(deadline) { 213 if fn() { 214 return true 215 } 216 time.Sleep(checkEvery) 217 } 218 return false 219 } 220 221 // waitErrCondition is like waitCondition but with errors instead of bools. 222 func waitErrCondition(waitFor, checkEvery time.Duration, fn func() error) error { 223 deadline := time.Now().Add(waitFor) 224 var err error 225 for time.Now().Before(deadline) { 226 if err = fn(); err == nil { 227 return nil 228 } 229 time.Sleep(checkEvery) 230 } 231 return err 232 } 233 234 func equalError(a, b error) bool { 235 if a == nil { 236 return b == nil 237 } 238 if b == nil { 239 return a == nil 240 } 241 return a.Error() == b.Error() 242 } 243 244 // Tests that http2.Server.IdleTimeout is initialized from 245 // http.Server.{Idle,Read}Timeout. http.Server.IdleTimeout was 246 // added in Go 1.8. 247 func TestConfigureServerIdleTimeout_Go18(t *testing.T) { 248 const timeout = 5 * time.Second 249 const notThisOne = 1 * time.Second 250 251 // With a zero http2.Server, verify that it copies IdleTimeout: 252 { 253 s1 := &http.Server{ 254 IdleTimeout: timeout, 255 ReadTimeout: notThisOne, 256 } 257 s2 := &Server{} 258 if err := ConfigureServer(s1, s2); err != nil { 259 t.Fatal(err) 260 } 261 if s2.IdleTimeout != timeout { 262 t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout) 263 } 264 } 265 266 // And that it falls back to ReadTimeout: 267 { 268 s1 := &http.Server{ 269 ReadTimeout: timeout, 270 } 271 s2 := &Server{} 272 if err := ConfigureServer(s1, s2); err != nil { 273 t.Fatal(err) 274 } 275 if s2.IdleTimeout != timeout { 276 t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout) 277 } 278 } 279 280 // Verify that s1's IdleTimeout doesn't overwrite an existing setting: 281 { 282 s1 := &http.Server{ 283 IdleTimeout: notThisOne, 284 } 285 s2 := &Server{ 286 IdleTimeout: timeout, 287 } 288 if err := ConfigureServer(s1, s2); err != nil { 289 t.Fatal(err) 290 } 291 if s2.IdleTimeout != timeout { 292 t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout) 293 } 294 } 295 } 296 297 var forbiddenStringsFunctions = map[string]bool{ 298 // Functions that use Unicode-aware case folding. 299 "EqualFold": true, 300 "Title": true, 301 "ToLower": true, 302 "ToLowerSpecial": true, 303 "ToTitle": true, 304 "ToTitleSpecial": true, 305 "ToUpper": true, 306 "ToUpperSpecial": true, 307 308 // Functions that use Unicode-aware spaces. 309 "Fields": true, 310 "TrimSpace": true, 311 } 312 313 // TestNoUnicodeStrings checks that nothing in net/http uses the Unicode-aware 314 // strings and bytes package functions. HTTP is mostly ASCII based, and doing 315 // Unicode-aware case folding or space stripping can introduce vulnerabilities. 316 func TestNoUnicodeStrings(t *testing.T) { 317 re := regexp.MustCompile(`(strings|bytes).([A-Za-z]+)`) 318 if err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error { 319 if err != nil { 320 t.Fatal(err) 321 } 322 323 if path == "h2i" || path == "h2c" { 324 return filepath.SkipDir 325 } 326 if !strings.HasSuffix(path, ".go") || 327 strings.HasSuffix(path, "_test.go") || 328 path == "ascii.go" || info.IsDir() { 329 return nil 330 } 331 332 contents, err := ioutil.ReadFile(path) 333 if err != nil { 334 t.Fatal(err) 335 } 336 for lineNum, line := range strings.Split(string(contents), "\n") { 337 for _, match := range re.FindAllStringSubmatch(line, -1) { 338 if !forbiddenStringsFunctions[match[2]] { 339 continue 340 } 341 t.Errorf("disallowed call to %s at %s:%d", match[0], path, lineNum+1) 342 } 343 } 344 345 return nil 346 }); err != nil { 347 t.Fatal(err) 348 } 349 }