github.com/Andyfoo/golang/x/net@v0.0.0-20190901054642-57c1bf301704/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 "net/http" 13 "os/exec" 14 "strconv" 15 "strings" 16 "testing" 17 "time" 18 19 "github.com/Andyfoo/golang/x/net/http2/hpack" 20 ) 21 22 var knownFailing = flag.Bool("known_failing", false, "Run known-failing tests.") 23 24 func condSkipFailingTest(t *testing.T) { 25 if !*knownFailing { 26 t.Skip("Skipping known-failing test without --known_failing") 27 } 28 } 29 30 func init() { 31 inTests = true 32 DebugGoroutines = true 33 flag.BoolVar(&VerboseLogs, "verboseh2", VerboseLogs, "Verbose HTTP/2 debug logging") 34 } 35 36 func TestSettingString(t *testing.T) { 37 tests := []struct { 38 s Setting 39 want string 40 }{ 41 {Setting{SettingMaxFrameSize, 123}, "[MAX_FRAME_SIZE = 123]"}, 42 {Setting{1<<16 - 1, 123}, "[UNKNOWN_SETTING_65535 = 123]"}, 43 } 44 for i, tt := range tests { 45 got := fmt.Sprint(tt.s) 46 if got != tt.want { 47 t.Errorf("%d. for %#v, string = %q; want %q", i, tt.s, got, tt.want) 48 } 49 } 50 } 51 52 type twriter struct { 53 t testing.TB 54 st *serverTester // optional 55 } 56 57 func (w twriter) Write(p []byte) (n int, err error) { 58 if w.st != nil { 59 ps := string(p) 60 for _, phrase := range w.st.logFilter { 61 if strings.Contains(ps, phrase) { 62 return len(p), nil // no logging 63 } 64 } 65 } 66 w.t.Logf("%s", p) 67 return len(p), nil 68 } 69 70 // like encodeHeader, but don't add implicit pseudo headers. 71 func encodeHeaderNoImplicit(t *testing.T, headers ...string) []byte { 72 var buf bytes.Buffer 73 enc := hpack.NewEncoder(&buf) 74 for len(headers) > 0 { 75 k, v := headers[0], headers[1] 76 headers = headers[2:] 77 if err := enc.WriteField(hpack.HeaderField{Name: k, Value: v}); err != nil { 78 t.Fatalf("HPACK encoding error for %q/%q: %v", k, v, err) 79 } 80 } 81 return buf.Bytes() 82 } 83 84 // Verify that curl has http2. 85 func requireCurl(t *testing.T) { 86 out, err := dockerLogs(curl(t, "--version")) 87 if err != nil { 88 t.Skipf("failed to determine curl features; skipping test") 89 } 90 if !strings.Contains(string(out), "HTTP2") { 91 t.Skip("curl doesn't support HTTP2; skipping test") 92 } 93 } 94 95 func curl(t *testing.T, args ...string) (container string) { 96 out, err := exec.Command("docker", append([]string{"run", "-d", "--net=host", "gohttp2/curl"}, args...)...).Output() 97 if err != nil { 98 t.Skipf("Failed to run curl in docker: %v, %s", err, out) 99 } 100 return strings.TrimSpace(string(out)) 101 } 102 103 // Verify that h2load exists. 104 func requireH2load(t *testing.T) { 105 out, err := dockerLogs(h2load(t, "--version")) 106 if err != nil { 107 t.Skipf("failed to probe h2load; skipping test: %s", out) 108 } 109 if !strings.Contains(string(out), "h2load nghttp2/") { 110 t.Skipf("h2load not present; skipping test. (Output=%q)", out) 111 } 112 } 113 114 func h2load(t *testing.T, args ...string) (container string) { 115 out, err := exec.Command("docker", append([]string{"run", "-d", "--net=host", "--entrypoint=/usr/local/bin/h2load", "gohttp2/curl"}, args...)...).Output() 116 if err != nil { 117 t.Skipf("Failed to run h2load in docker: %v, %s", err, out) 118 } 119 return strings.TrimSpace(string(out)) 120 } 121 122 type puppetCommand struct { 123 fn func(w http.ResponseWriter, r *http.Request) 124 done chan<- bool 125 } 126 127 type handlerPuppet struct { 128 ch chan puppetCommand 129 } 130 131 func newHandlerPuppet() *handlerPuppet { 132 return &handlerPuppet{ 133 ch: make(chan puppetCommand), 134 } 135 } 136 137 func (p *handlerPuppet) act(w http.ResponseWriter, r *http.Request) { 138 for cmd := range p.ch { 139 cmd.fn(w, r) 140 cmd.done <- true 141 } 142 } 143 144 func (p *handlerPuppet) done() { close(p.ch) } 145 func (p *handlerPuppet) do(fn func(http.ResponseWriter, *http.Request)) { 146 done := make(chan bool) 147 p.ch <- puppetCommand{fn, done} 148 <-done 149 } 150 func dockerLogs(container string) ([]byte, error) { 151 out, err := exec.Command("docker", "wait", container).CombinedOutput() 152 if err != nil { 153 return out, err 154 } 155 exitStatus, err := strconv.Atoi(strings.TrimSpace(string(out))) 156 if err != nil { 157 return out, errors.New("unexpected exit status from docker wait") 158 } 159 out, err = exec.Command("docker", "logs", container).CombinedOutput() 160 exec.Command("docker", "rm", container).Run() 161 if err == nil && exitStatus != 0 { 162 err = fmt.Errorf("exit status %d: %s", exitStatus, out) 163 } 164 return out, err 165 } 166 167 func kill(container string) { 168 exec.Command("docker", "kill", container).Run() 169 exec.Command("docker", "rm", container).Run() 170 } 171 172 func cleanDate(res *http.Response) { 173 if d := res.Header["Date"]; len(d) == 1 { 174 d[0] = "XXX" 175 } 176 } 177 178 func TestSorterPoolAllocs(t *testing.T) { 179 ss := []string{"a", "b", "c"} 180 h := http.Header{ 181 "a": nil, 182 "b": nil, 183 "c": nil, 184 } 185 sorter := new(sorter) 186 187 if allocs := testing.AllocsPerRun(100, func() { 188 sorter.SortStrings(ss) 189 }); allocs >= 1 { 190 t.Logf("SortStrings allocs = %v; want <1", allocs) 191 } 192 193 if allocs := testing.AllocsPerRun(5, func() { 194 if len(sorter.Keys(h)) != 3 { 195 t.Fatal("wrong result") 196 } 197 }); allocs > 0 { 198 t.Logf("Keys allocs = %v; want <1", allocs) 199 } 200 } 201 202 // waitCondition reports whether fn eventually returned true, 203 // checking immediately and then every checkEvery amount, 204 // until waitFor has elapsed, at which point it returns false. 205 func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool { 206 deadline := time.Now().Add(waitFor) 207 for time.Now().Before(deadline) { 208 if fn() { 209 return true 210 } 211 time.Sleep(checkEvery) 212 } 213 return false 214 } 215 216 // waitErrCondition is like waitCondition but with errors instead of bools. 217 func waitErrCondition(waitFor, checkEvery time.Duration, fn func() error) error { 218 deadline := time.Now().Add(waitFor) 219 var err error 220 for time.Now().Before(deadline) { 221 if err = fn(); err == nil { 222 return nil 223 } 224 time.Sleep(checkEvery) 225 } 226 return err 227 } 228 229 func equalError(a, b error) bool { 230 if a == nil { 231 return b == nil 232 } 233 if b == nil { 234 return a == nil 235 } 236 return a.Error() == b.Error() 237 } 238 239 // Tests that http2.Server.IdleTimeout is initialized from 240 // http.Server.{Idle,Read}Timeout. http.Server.IdleTimeout was 241 // added in Go 1.8. 242 func TestConfigureServerIdleTimeout_Go18(t *testing.T) { 243 const timeout = 5 * time.Second 244 const notThisOne = 1 * time.Second 245 246 // With a zero http2.Server, verify that it copies IdleTimeout: 247 { 248 s1 := &http.Server{ 249 IdleTimeout: timeout, 250 ReadTimeout: notThisOne, 251 } 252 s2 := &Server{} 253 if err := ConfigureServer(s1, s2); err != nil { 254 t.Fatal(err) 255 } 256 if s2.IdleTimeout != timeout { 257 t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout) 258 } 259 } 260 261 // And that it falls back to ReadTimeout: 262 { 263 s1 := &http.Server{ 264 ReadTimeout: timeout, 265 } 266 s2 := &Server{} 267 if err := ConfigureServer(s1, s2); err != nil { 268 t.Fatal(err) 269 } 270 if s2.IdleTimeout != timeout { 271 t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout) 272 } 273 } 274 275 // Verify that s1's IdleTimeout doesn't overwrite an existing setting: 276 { 277 s1 := &http.Server{ 278 IdleTimeout: notThisOne, 279 } 280 s2 := &Server{ 281 IdleTimeout: timeout, 282 } 283 if err := ConfigureServer(s1, s2); err != nil { 284 t.Fatal(err) 285 } 286 if s2.IdleTimeout != timeout { 287 t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout) 288 } 289 } 290 }