github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/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 "github.com/hxx258456/ccgo/gmhttp"
    23  
    24  	"github.com/hxx258456/ccgo/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  }