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