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  }