...

Source file src/net/http/http_test.go

Documentation: net/http

		 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  // Tests of internal functions and things with no better homes.
		 6  
		 7  package http
		 8  
		 9  import (
		10  	"bytes"
		11  	"internal/testenv"
		12  	"io/fs"
		13  	"net/url"
		14  	"os"
		15  	"os/exec"
		16  	"reflect"
		17  	"regexp"
		18  	"strings"
		19  	"testing"
		20  )
		21  
		22  func TestForeachHeaderElement(t *testing.T) {
		23  	tests := []struct {
		24  		in	 string
		25  		want []string
		26  	}{
		27  		{"Foo", []string{"Foo"}},
		28  		{" Foo", []string{"Foo"}},
		29  		{"Foo ", []string{"Foo"}},
		30  		{" Foo ", []string{"Foo"}},
		31  
		32  		{"foo", []string{"foo"}},
		33  		{"anY-cAsE", []string{"anY-cAsE"}},
		34  
		35  		{"", nil},
		36  		{",,,,	,	,,	 ,,, ,", nil},
		37  
		38  		{" Foo,Bar, Baz,lower,,Quux ", []string{"Foo", "Bar", "Baz", "lower", "Quux"}},
		39  	}
		40  	for _, tt := range tests {
		41  		var got []string
		42  		foreachHeaderElement(tt.in, func(v string) {
		43  			got = append(got, v)
		44  		})
		45  		if !reflect.DeepEqual(got, tt.want) {
		46  			t.Errorf("foreachHeaderElement(%q) = %q; want %q", tt.in, got, tt.want)
		47  		}
		48  	}
		49  }
		50  
		51  func TestCleanHost(t *testing.T) {
		52  	tests := []struct {
		53  		in, want string
		54  	}{
		55  		{"www.google.com", "www.google.com"},
		56  		{"www.google.com foo", "www.google.com"},
		57  		{"www.google.com/foo", "www.google.com"},
		58  		{" first character is a space", ""},
		59  		{"[1::6]:8080", "[1::6]:8080"},
		60  
		61  		// Punycode:
		62  		{"гофер.рф/foo", "xn--c1ae0ajs.xn--p1ai"},
		63  		{"bücher.de", "xn--bcher-kva.de"},
		64  		{"bücher.de:8080", "xn--bcher-kva.de:8080"},
		65  		// Verify we convert to lowercase before punycode:
		66  		{"BÜCHER.de", "xn--bcher-kva.de"},
		67  		{"BÜCHER.de:8080", "xn--bcher-kva.de:8080"},
		68  		// Verify we normalize to NFC before punycode:
		69  		{"gophér.nfc", "xn--gophr-esa.nfc"},						// NFC input; no work needed
		70  		{"goph\u0065\u0301r.nfd", "xn--gophr-esa.nfd"}, // NFD input
		71  	}
		72  	for _, tt := range tests {
		73  		got := cleanHost(tt.in)
		74  		if tt.want != got {
		75  			t.Errorf("cleanHost(%q) = %q, want %q", tt.in, got, tt.want)
		76  		}
		77  	}
		78  }
		79  
		80  // Test that cmd/go doesn't link in the HTTP server.
		81  //
		82  // This catches accidental dependencies between the HTTP transport and
		83  // server code.
		84  func TestCmdGoNoHTTPServer(t *testing.T) {
		85  	t.Parallel()
		86  	goBin := testenv.GoToolPath(t)
		87  	out, err := exec.Command(goBin, "tool", "nm", goBin).CombinedOutput()
		88  	if err != nil {
		89  		t.Fatalf("go tool nm: %v: %s", err, out)
		90  	}
		91  	wantSym := map[string]bool{
		92  		// Verify these exist: (sanity checking this test)
		93  		"net/http.(*Client).do":					 true,
		94  		"net/http.(*Transport).RoundTrip": true,
		95  
		96  		// Verify these don't exist:
		97  		"net/http.http2Server":					 false,
		98  		"net/http.(*Server).Serve":			 false,
		99  		"net/http.(*ServeMux).ServeHTTP": false,
	 100  		"net/http.DefaultServeMux":			 false,
	 101  	}
	 102  	for sym, want := range wantSym {
	 103  		got := bytes.Contains(out, []byte(sym))
	 104  		if !want && got {
	 105  			t.Errorf("cmd/go unexpectedly links in HTTP server code; found symbol %q in cmd/go", sym)
	 106  		}
	 107  		if want && !got {
	 108  			t.Errorf("expected to find symbol %q in cmd/go; not found", sym)
	 109  		}
	 110  	}
	 111  }
	 112  
	 113  // Tests that the nethttpomithttp2 build tag doesn't rot too much,
	 114  // even if there's not a regular builder on it.
	 115  func TestOmitHTTP2(t *testing.T) {
	 116  	if testing.Short() {
	 117  		t.Skip("skipping in short mode")
	 118  	}
	 119  	t.Parallel()
	 120  	goTool := testenv.GoToolPath(t)
	 121  	out, err := exec.Command(goTool, "test", "-short", "-tags=nethttpomithttp2", "net/http").CombinedOutput()
	 122  	if err != nil {
	 123  		t.Fatalf("go test -short failed: %v, %s", err, out)
	 124  	}
	 125  }
	 126  
	 127  // Tests that the nethttpomithttp2 build tag at least type checks
	 128  // in short mode.
	 129  // The TestOmitHTTP2 test above actually runs tests (in long mode).
	 130  func TestOmitHTTP2Vet(t *testing.T) {
	 131  	t.Parallel()
	 132  	goTool := testenv.GoToolPath(t)
	 133  	out, err := exec.Command(goTool, "vet", "-tags=nethttpomithttp2", "net/http").CombinedOutput()
	 134  	if err != nil {
	 135  		t.Fatalf("go vet failed: %v, %s", err, out)
	 136  	}
	 137  }
	 138  
	 139  var valuesCount int
	 140  
	 141  func BenchmarkCopyValues(b *testing.B) {
	 142  	b.ReportAllocs()
	 143  	src := url.Values{
	 144  		"a": {"1", "2", "3", "4", "5"},
	 145  		"b": {"2", "2", "3", "4", "5"},
	 146  		"c": {"3", "2", "3", "4", "5"},
	 147  		"d": {"4", "2", "3", "4", "5"},
	 148  		"e": {"1", "1", "2", "3", "4", "5", "6", "7", "abcdef", "l", "a", "b", "c", "d", "z"},
	 149  		"j": {"1", "2"},
	 150  		"m": nil,
	 151  	}
	 152  	for i := 0; i < b.N; i++ {
	 153  		dst := url.Values{"a": {"b"}, "b": {"2"}, "c": {"3"}, "d": {"4"}, "j": nil, "m": {"x"}}
	 154  		copyValues(dst, src)
	 155  		if valuesCount = len(dst["a"]); valuesCount != 6 {
	 156  			b.Fatalf(`%d items in dst["a"] but expected 6`, valuesCount)
	 157  		}
	 158  	}
	 159  	if valuesCount == 0 {
	 160  		b.Fatal("Benchmark wasn't run")
	 161  	}
	 162  }
	 163  
	 164  var forbiddenStringsFunctions = map[string]bool{
	 165  	// Functions that use Unicode-aware case folding.
	 166  	"EqualFold":			true,
	 167  	"Title":					true,
	 168  	"ToLower":				true,
	 169  	"ToLowerSpecial": true,
	 170  	"ToTitle":				true,
	 171  	"ToTitleSpecial": true,
	 172  	"ToUpper":				true,
	 173  	"ToUpperSpecial": true,
	 174  
	 175  	// Functions that use Unicode-aware spaces.
	 176  	"Fields":		true,
	 177  	"TrimSpace": true,
	 178  }
	 179  
	 180  // TestNoUnicodeStrings checks that nothing in net/http uses the Unicode-aware
	 181  // strings and bytes package functions. HTTP is mostly ASCII based, and doing
	 182  // Unicode-aware case folding or space stripping can introduce vulnerabilities.
	 183  func TestNoUnicodeStrings(t *testing.T) {
	 184  	if !testenv.HasSrc() {
	 185  		t.Skip("source code not available")
	 186  	}
	 187  
	 188  	re := regexp.MustCompile(`(strings|bytes).([A-Za-z]+)`)
	 189  	if err := fs.WalkDir(os.DirFS("."), ".", func(path string, d fs.DirEntry, err error) error {
	 190  		if err != nil {
	 191  			t.Fatal(err)
	 192  		}
	 193  
	 194  		if path == "internal/ascii" {
	 195  			return fs.SkipDir
	 196  		}
	 197  		if !strings.HasSuffix(path, ".go") ||
	 198  			strings.HasSuffix(path, "_test.go") ||
	 199  			path == "h2_bundle.go" || d.IsDir() {
	 200  			return nil
	 201  		}
	 202  
	 203  		contents, err := os.ReadFile(path)
	 204  		if err != nil {
	 205  			t.Fatal(err)
	 206  		}
	 207  		for lineNum, line := range strings.Split(string(contents), "\n") {
	 208  			for _, match := range re.FindAllStringSubmatch(line, -1) {
	 209  				if !forbiddenStringsFunctions[match[2]] {
	 210  					continue
	 211  				}
	 212  				t.Errorf("disallowed call to %s at %s:%d", match[0], path, lineNum+1)
	 213  			}
	 214  		}
	 215  
	 216  		return nil
	 217  	}); err != nil {
	 218  		t.Fatal(err)
	 219  	}
	 220  }
	 221  

View as plain text