github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/net/http/server_test.go (about) 1 // Copyright 2018 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 // Server unit tests 6 7 package http 8 9 import ( 10 "fmt" 11 "net/url" 12 "regexp" 13 "testing" 14 "time" 15 ) 16 17 func TestServerTLSHandshakeTimeout(t *testing.T) { 18 tests := []struct { 19 s *Server 20 want time.Duration 21 }{ 22 { 23 s: &Server{}, 24 want: 0, 25 }, 26 { 27 s: &Server{ 28 ReadTimeout: -1, 29 }, 30 want: 0, 31 }, 32 { 33 s: &Server{ 34 ReadTimeout: 5 * time.Second, 35 }, 36 want: 5 * time.Second, 37 }, 38 { 39 s: &Server{ 40 ReadTimeout: 5 * time.Second, 41 WriteTimeout: -1, 42 }, 43 want: 5 * time.Second, 44 }, 45 { 46 s: &Server{ 47 ReadTimeout: 5 * time.Second, 48 WriteTimeout: 4 * time.Second, 49 }, 50 want: 4 * time.Second, 51 }, 52 { 53 s: &Server{ 54 ReadTimeout: 5 * time.Second, 55 ReadHeaderTimeout: 2 * time.Second, 56 WriteTimeout: 4 * time.Second, 57 }, 58 want: 2 * time.Second, 59 }, 60 } 61 for i, tt := range tests { 62 got := tt.s.tlsHandshakeTimeout() 63 if got != tt.want { 64 t.Errorf("%d. got %v; want %v", i, got, tt.want) 65 } 66 } 67 } 68 69 type handler struct{ i int } 70 71 func (handler) ServeHTTP(ResponseWriter, *Request) {} 72 73 func TestFindHandler(t *testing.T) { 74 mux := NewServeMux() 75 for _, ph := range []struct { 76 pat string 77 h Handler 78 }{ 79 {"/", &handler{1}}, 80 {"/foo/", &handler{2}}, 81 {"/foo", &handler{3}}, 82 {"/bar/", &handler{4}}, 83 {"//foo", &handler{5}}, 84 } { 85 mux.Handle(ph.pat, ph.h) 86 } 87 88 for _, test := range []struct { 89 method string 90 path string 91 wantHandler string 92 }{ 93 {"GET", "/", "&http.handler{i:1}"}, 94 {"GET", "//", `&http.redirectHandler{url:"/", code:301}`}, 95 {"GET", "/foo/../bar/./..//baz", `&http.redirectHandler{url:"/baz", code:301}`}, 96 {"GET", "/foo", "&http.handler{i:3}"}, 97 {"GET", "/foo/x", "&http.handler{i:2}"}, 98 {"GET", "/bar/x", "&http.handler{i:4}"}, 99 {"GET", "/bar", `&http.redirectHandler{url:"/bar/", code:301}`}, 100 {"CONNECT", "/", "&http.handler{i:1}"}, 101 {"CONNECT", "//", "&http.handler{i:1}"}, 102 {"CONNECT", "//foo", "&http.handler{i:5}"}, 103 {"CONNECT", "/foo/../bar/./..//baz", "&http.handler{i:2}"}, 104 {"CONNECT", "/foo", "&http.handler{i:3}"}, 105 {"CONNECT", "/foo/x", "&http.handler{i:2}"}, 106 {"CONNECT", "/bar/x", "&http.handler{i:4}"}, 107 {"CONNECT", "/bar", `&http.redirectHandler{url:"/bar/", code:301}`}, 108 } { 109 var r Request 110 r.Method = test.method 111 r.Host = "example.com" 112 r.URL = &url.URL{Path: test.path} 113 gotH, _, _, _ := mux.findHandler(&r) 114 got := fmt.Sprintf("%#v", gotH) 115 if got != test.wantHandler { 116 t.Errorf("%s %q: got %q, want %q", test.method, test.path, got, test.wantHandler) 117 } 118 } 119 } 120 121 func TestEmptyServeMux(t *testing.T) { 122 // Verify that a ServeMux with nothing registered 123 // doesn't panic. 124 mux := NewServeMux() 125 var r Request 126 r.Method = "GET" 127 r.Host = "example.com" 128 r.URL = &url.URL{Path: "/"} 129 _, p := mux.Handler(&r) 130 if p != "" { 131 t.Errorf(`got %q, want ""`, p) 132 } 133 } 134 135 func TestRegisterErr(t *testing.T) { 136 mux := NewServeMux() 137 h := &handler{} 138 mux.Handle("/a", h) 139 140 for _, test := range []struct { 141 pattern string 142 handler Handler 143 wantRegexp string 144 }{ 145 {"", h, "invalid pattern"}, 146 {"/", nil, "nil handler"}, 147 {"/", HandlerFunc(nil), "nil handler"}, 148 {"/{x", h, `parsing "/\{x": at offset 1: bad wildcard segment`}, 149 {"/a", h, `conflicts with pattern.* \(registered at .*/server_test.go:\d+`}, 150 } { 151 t.Run(fmt.Sprintf("%s:%#v", test.pattern, test.handler), func(t *testing.T) { 152 err := mux.registerErr(test.pattern, test.handler) 153 if err == nil { 154 t.Fatal("got nil error") 155 } 156 re := regexp.MustCompile(test.wantRegexp) 157 if g := err.Error(); !re.MatchString(g) { 158 t.Errorf("\ngot %q\nwant string matching %q", g, test.wantRegexp) 159 } 160 }) 161 } 162 } 163 164 func TestExactMatch(t *testing.T) { 165 for _, test := range []struct { 166 pattern string 167 path string 168 want bool 169 }{ 170 {"", "/a", false}, 171 {"/", "/a", false}, 172 {"/a", "/a", true}, 173 {"/a/{x...}", "/a/b", false}, 174 {"/a/{x}", "/a/b", true}, 175 {"/a/b/", "/a/b/", true}, 176 {"/a/b/{$}", "/a/b/", true}, 177 {"/a/", "/a/b/", false}, 178 } { 179 var n *routingNode 180 if test.pattern != "" { 181 pat := mustParsePattern(t, test.pattern) 182 n = &routingNode{pattern: pat} 183 } 184 got := exactMatch(n, test.path) 185 if got != test.want { 186 t.Errorf("%q, %s: got %t, want %t", test.pattern, test.path, got, test.want) 187 } 188 } 189 } 190 191 func TestEscapedPathsAndPatterns(t *testing.T) { 192 matches := []struct { 193 pattern string 194 paths []string // paths that match the pattern 195 paths121 []string // paths that matched the pattern in Go 1.21. 196 }{ 197 { 198 "/a", // this pattern matches a path that unescapes to "/a" 199 []string{"/a", "/%61"}, 200 []string{"/a", "/%61"}, 201 }, 202 { 203 "/%62", // patterns are unescaped by segment; matches paths that unescape to "/b" 204 []string{"/b", "/%62"}, 205 []string{"/%2562"}, // In 1.21, patterns were not unescaped but paths were. 206 }, 207 { 208 "/%7B/%7D", // the only way to write a pattern that matches '{' or '}' 209 []string{"/{/}", "/%7b/}", "/{/%7d", "/%7B/%7D"}, 210 []string{"/%257B/%257D"}, // In 1.21, patterns were not unescaped. 211 }, 212 { 213 "/%x", // patterns that do not unescape are left unchanged 214 []string{"/%25x"}, 215 []string{"/%25x"}, 216 }, 217 } 218 219 run := func(t *testing.T, test121 bool) { 220 defer func(u bool) { use121 = u }(use121) 221 use121 = test121 222 223 mux := NewServeMux() 224 for _, m := range matches { 225 mux.HandleFunc(m.pattern, func(w ResponseWriter, r *Request) {}) 226 } 227 228 for _, m := range matches { 229 paths := m.paths 230 if use121 { 231 paths = m.paths121 232 } 233 for _, p := range paths { 234 u, err := url.ParseRequestURI(p) 235 if err != nil { 236 t.Fatal(err) 237 } 238 req := &Request{ 239 URL: u, 240 } 241 _, gotPattern := mux.Handler(req) 242 if g, w := gotPattern, m.pattern; g != w { 243 t.Errorf("%s: pattern: got %q, want %q", p, g, w) 244 } 245 } 246 } 247 } 248 249 t.Run("latest", func(t *testing.T) { run(t, false) }) 250 t.Run("1.21", func(t *testing.T) { run(t, true) }) 251 } 252 253 func TestCleanPath(t *testing.T) { 254 for _, test := range []struct { 255 in, want string 256 }{ 257 {"//", "/"}, 258 {"/x", "/x"}, 259 {"//x", "/x"}, 260 {"x//", "/x/"}, 261 {"a//b/////c", "/a/b/c"}, 262 {"/foo/../bar/./..//baz", "/baz"}, 263 } { 264 got := cleanPath(test.in) 265 if got != test.want { 266 t.Errorf("%s: got %q, want %q", test.in, got, test.want) 267 } 268 } 269 } 270 271 func BenchmarkServerMatch(b *testing.B) { 272 fn := func(w ResponseWriter, r *Request) { 273 fmt.Fprintf(w, "OK") 274 } 275 mux := NewServeMux() 276 mux.HandleFunc("/", fn) 277 mux.HandleFunc("/index", fn) 278 mux.HandleFunc("/home", fn) 279 mux.HandleFunc("/about", fn) 280 mux.HandleFunc("/contact", fn) 281 mux.HandleFunc("/robots.txt", fn) 282 mux.HandleFunc("/products/", fn) 283 mux.HandleFunc("/products/1", fn) 284 mux.HandleFunc("/products/2", fn) 285 mux.HandleFunc("/products/3", fn) 286 mux.HandleFunc("/products/3/image.jpg", fn) 287 mux.HandleFunc("/admin", fn) 288 mux.HandleFunc("/admin/products/", fn) 289 mux.HandleFunc("/admin/products/create", fn) 290 mux.HandleFunc("/admin/products/update", fn) 291 mux.HandleFunc("/admin/products/delete", fn) 292 293 paths := []string{"/", "/notfound", "/admin/", "/admin/foo", "/contact", "/products", 294 "/products/", "/products/3/image.jpg"} 295 b.StartTimer() 296 for i := 0; i < b.N; i++ { 297 r, err := NewRequest("GET", "http://example.com/"+paths[i%len(paths)], nil) 298 if err != nil { 299 b.Fatal(err) 300 } 301 if h, p, _, _ := mux.findHandler(r); h != nil && p == "" { 302 b.Error("impossible") 303 } 304 } 305 b.StopTimer() 306 }