...

Source file src/golang.org/x/net/http2/http2_test.go

Documentation: golang.org/x/net/http2

     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  	"net/http"
    12  	"os"
    13  	"path/filepath"
    14  	"regexp"
    15  	"strings"
    16  	"testing"
    17  	"time"
    18  
    19  	"golang.org/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  type puppetCommand struct {
    85  	fn   func(w http.ResponseWriter, r *http.Request)
    86  	done chan<- bool
    87  }
    88  
    89  type handlerPuppet struct {
    90  	ch chan puppetCommand
    91  }
    92  
    93  func newHandlerPuppet() *handlerPuppet {
    94  	return &handlerPuppet{
    95  		ch: make(chan puppetCommand),
    96  	}
    97  }
    98  
    99  func (p *handlerPuppet) act(w http.ResponseWriter, r *http.Request) {
   100  	for cmd := range p.ch {
   101  		cmd.fn(w, r)
   102  		cmd.done <- true
   103  	}
   104  }
   105  
   106  func (p *handlerPuppet) done() { close(p.ch) }
   107  func (p *handlerPuppet) do(fn func(http.ResponseWriter, *http.Request)) {
   108  	done := make(chan bool)
   109  	p.ch <- puppetCommand{fn, done}
   110  	<-done
   111  }
   112  
   113  func cleanDate(res *http.Response) {
   114  	if d := res.Header["Date"]; len(d) == 1 {
   115  		d[0] = "XXX"
   116  	}
   117  }
   118  
   119  func TestSorterPoolAllocs(t *testing.T) {
   120  	ss := []string{"a", "b", "c"}
   121  	h := http.Header{
   122  		"a": nil,
   123  		"b": nil,
   124  		"c": nil,
   125  	}
   126  	sorter := new(sorter)
   127  
   128  	if allocs := testing.AllocsPerRun(100, func() {
   129  		sorter.SortStrings(ss)
   130  	}); allocs >= 1 {
   131  		t.Logf("SortStrings allocs = %v; want <1", allocs)
   132  	}
   133  
   134  	if allocs := testing.AllocsPerRun(5, func() {
   135  		if len(sorter.Keys(h)) != 3 {
   136  			t.Fatal("wrong result")
   137  		}
   138  	}); allocs > 0 {
   139  		t.Logf("Keys allocs = %v; want <1", allocs)
   140  	}
   141  }
   142  
   143  // waitCondition reports whether fn eventually returned true,
   144  // checking immediately and then every checkEvery amount,
   145  // until waitFor has elapsed, at which point it returns false.
   146  func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool {
   147  	deadline := time.Now().Add(waitFor)
   148  	for time.Now().Before(deadline) {
   149  		if fn() {
   150  			return true
   151  		}
   152  		time.Sleep(checkEvery)
   153  	}
   154  	return false
   155  }
   156  
   157  // waitErrCondition is like waitCondition but with errors instead of bools.
   158  func waitErrCondition(waitFor, checkEvery time.Duration, fn func() error) error {
   159  	deadline := time.Now().Add(waitFor)
   160  	var err error
   161  	for time.Now().Before(deadline) {
   162  		if err = fn(); err == nil {
   163  			return nil
   164  		}
   165  		time.Sleep(checkEvery)
   166  	}
   167  	return err
   168  }
   169  
   170  func equalError(a, b error) bool {
   171  	if a == nil {
   172  		return b == nil
   173  	}
   174  	if b == nil {
   175  		return a == nil
   176  	}
   177  	return a.Error() == b.Error()
   178  }
   179  
   180  // Tests that http2.Server.IdleTimeout is initialized from
   181  // http.Server.{Idle,Read}Timeout. http.Server.IdleTimeout was
   182  // added in Go 1.8.
   183  func TestConfigureServerIdleTimeout_Go18(t *testing.T) {
   184  	const timeout = 5 * time.Second
   185  	const notThisOne = 1 * time.Second
   186  
   187  	// With a zero http2.Server, verify that it copies IdleTimeout:
   188  	{
   189  		s1 := &http.Server{
   190  			IdleTimeout: timeout,
   191  			ReadTimeout: notThisOne,
   192  		}
   193  		s2 := &Server{}
   194  		if err := ConfigureServer(s1, s2); err != nil {
   195  			t.Fatal(err)
   196  		}
   197  		if s2.IdleTimeout != timeout {
   198  			t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout)
   199  		}
   200  	}
   201  
   202  	// And that it falls back to ReadTimeout:
   203  	{
   204  		s1 := &http.Server{
   205  			ReadTimeout: timeout,
   206  		}
   207  		s2 := &Server{}
   208  		if err := ConfigureServer(s1, s2); err != nil {
   209  			t.Fatal(err)
   210  		}
   211  		if s2.IdleTimeout != timeout {
   212  			t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout)
   213  		}
   214  	}
   215  
   216  	// Verify that s1's IdleTimeout doesn't overwrite an existing setting:
   217  	{
   218  		s1 := &http.Server{
   219  			IdleTimeout: notThisOne,
   220  		}
   221  		s2 := &Server{
   222  			IdleTimeout: timeout,
   223  		}
   224  		if err := ConfigureServer(s1, s2); err != nil {
   225  			t.Fatal(err)
   226  		}
   227  		if s2.IdleTimeout != timeout {
   228  			t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout)
   229  		}
   230  	}
   231  }
   232  
   233  var forbiddenStringsFunctions = map[string]bool{
   234  	// Functions that use Unicode-aware case folding.
   235  	"EqualFold":      true,
   236  	"Title":          true,
   237  	"ToLower":        true,
   238  	"ToLowerSpecial": true,
   239  	"ToTitle":        true,
   240  	"ToTitleSpecial": true,
   241  	"ToUpper":        true,
   242  	"ToUpperSpecial": true,
   243  
   244  	// Functions that use Unicode-aware spaces.
   245  	"Fields":    true,
   246  	"TrimSpace": true,
   247  }
   248  
   249  // TestNoUnicodeStrings checks that nothing in net/http uses the Unicode-aware
   250  // strings and bytes package functions. HTTP is mostly ASCII based, and doing
   251  // Unicode-aware case folding or space stripping can introduce vulnerabilities.
   252  func TestNoUnicodeStrings(t *testing.T) {
   253  	re := regexp.MustCompile(`(strings|bytes).([A-Za-z]+)`)
   254  	if err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
   255  		if err != nil {
   256  			t.Fatal(err)
   257  		}
   258  
   259  		if path == "h2i" || path == "h2c" {
   260  			return filepath.SkipDir
   261  		}
   262  		if !strings.HasSuffix(path, ".go") ||
   263  			strings.HasSuffix(path, "_test.go") ||
   264  			path == "ascii.go" || info.IsDir() {
   265  			return nil
   266  		}
   267  
   268  		contents, err := os.ReadFile(path)
   269  		if err != nil {
   270  			t.Fatal(err)
   271  		}
   272  		for lineNum, line := range strings.Split(string(contents), "\n") {
   273  			for _, match := range re.FindAllStringSubmatch(line, -1) {
   274  				if !forbiddenStringsFunctions[match[2]] {
   275  					continue
   276  				}
   277  				t.Errorf("disallowed call to %s at %s:%d", match[0], path, lineNum+1)
   278  			}
   279  		}
   280  
   281  		return nil
   282  	}); err != nil {
   283  		t.Fatal(err)
   284  	}
   285  }
   286  
   287  // must returns v if err is nil, or panics otherwise.
   288  func must[T any](v T, err error) T {
   289  	if err != nil {
   290  		panic(err)
   291  	}
   292  	return v
   293  }
   294  

View as plain text