github.com/zebozhuang/go@v0.0.0-20200207033046-f8a98f6f5c5d/src/net/http/httputil/reverseproxy_test.go (about) 1 // Copyright 2011 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 // Reverse proxy tests. 6 7 package httputil 8 9 import ( 10 "bufio" 11 "bytes" 12 "errors" 13 "fmt" 14 "io" 15 "io/ioutil" 16 "log" 17 "net/http" 18 "net/http/httptest" 19 "net/url" 20 "reflect" 21 "strconv" 22 "strings" 23 "sync" 24 "testing" 25 "time" 26 ) 27 28 const fakeHopHeader = "X-Fake-Hop-Header-For-Test" 29 30 func init() { 31 hopHeaders = append(hopHeaders, fakeHopHeader) 32 } 33 34 func TestReverseProxy(t *testing.T) { 35 const backendResponse = "I am the backend" 36 const backendStatus = 404 37 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 38 if r.Method == "GET" && r.FormValue("mode") == "hangup" { 39 c, _, _ := w.(http.Hijacker).Hijack() 40 c.Close() 41 return 42 } 43 if len(r.TransferEncoding) > 0 { 44 t.Errorf("backend got unexpected TransferEncoding: %v", r.TransferEncoding) 45 } 46 if r.Header.Get("X-Forwarded-For") == "" { 47 t.Errorf("didn't get X-Forwarded-For header") 48 } 49 if c := r.Header.Get("Connection"); c != "" { 50 t.Errorf("handler got Connection header value %q", c) 51 } 52 if c := r.Header.Get("Upgrade"); c != "" { 53 t.Errorf("handler got Upgrade header value %q", c) 54 } 55 if c := r.Header.Get("Proxy-Connection"); c != "" { 56 t.Errorf("handler got Proxy-Connection header value %q", c) 57 } 58 if g, e := r.Host, "some-name"; g != e { 59 t.Errorf("backend got Host header %q, want %q", g, e) 60 } 61 w.Header().Set("Trailers", "not a special header field name") 62 w.Header().Set("Trailer", "X-Trailer") 63 w.Header().Set("X-Foo", "bar") 64 w.Header().Set("Upgrade", "foo") 65 w.Header().Set(fakeHopHeader, "foo") 66 w.Header().Add("X-Multi-Value", "foo") 67 w.Header().Add("X-Multi-Value", "bar") 68 http.SetCookie(w, &http.Cookie{Name: "flavor", Value: "chocolateChip"}) 69 w.WriteHeader(backendStatus) 70 w.Write([]byte(backendResponse)) 71 w.Header().Set("X-Trailer", "trailer_value") 72 w.Header().Set(http.TrailerPrefix+"X-Unannounced-Trailer", "unannounced_trailer_value") 73 })) 74 defer backend.Close() 75 backendURL, err := url.Parse(backend.URL) 76 if err != nil { 77 t.Fatal(err) 78 } 79 proxyHandler := NewSingleHostReverseProxy(backendURL) 80 proxyHandler.ErrorLog = log.New(ioutil.Discard, "", 0) // quiet for tests 81 frontend := httptest.NewServer(proxyHandler) 82 defer frontend.Close() 83 frontendClient := frontend.Client() 84 85 getReq, _ := http.NewRequest("GET", frontend.URL, nil) 86 getReq.Host = "some-name" 87 getReq.Header.Set("Connection", "close") 88 getReq.Header.Set("Proxy-Connection", "should be deleted") 89 getReq.Header.Set("Upgrade", "foo") 90 getReq.Close = true 91 res, err := frontendClient.Do(getReq) 92 if err != nil { 93 t.Fatalf("Get: %v", err) 94 } 95 if g, e := res.StatusCode, backendStatus; g != e { 96 t.Errorf("got res.StatusCode %d; expected %d", g, e) 97 } 98 if g, e := res.Header.Get("X-Foo"), "bar"; g != e { 99 t.Errorf("got X-Foo %q; expected %q", g, e) 100 } 101 if c := res.Header.Get(fakeHopHeader); c != "" { 102 t.Errorf("got %s header value %q", fakeHopHeader, c) 103 } 104 if g, e := res.Header.Get("Trailers"), "not a special header field name"; g != e { 105 t.Errorf("header Trailers = %q; want %q", g, e) 106 } 107 if g, e := len(res.Header["X-Multi-Value"]), 2; g != e { 108 t.Errorf("got %d X-Multi-Value header values; expected %d", g, e) 109 } 110 if g, e := len(res.Header["Set-Cookie"]), 1; g != e { 111 t.Fatalf("got %d SetCookies, want %d", g, e) 112 } 113 if g, e := res.Trailer, (http.Header{"X-Trailer": nil}); !reflect.DeepEqual(g, e) { 114 t.Errorf("before reading body, Trailer = %#v; want %#v", g, e) 115 } 116 if cookie := res.Cookies()[0]; cookie.Name != "flavor" { 117 t.Errorf("unexpected cookie %q", cookie.Name) 118 } 119 bodyBytes, _ := ioutil.ReadAll(res.Body) 120 if g, e := string(bodyBytes), backendResponse; g != e { 121 t.Errorf("got body %q; expected %q", g, e) 122 } 123 if g, e := res.Trailer.Get("X-Trailer"), "trailer_value"; g != e { 124 t.Errorf("Trailer(X-Trailer) = %q ; want %q", g, e) 125 } 126 if g, e := res.Trailer.Get("X-Unannounced-Trailer"), "unannounced_trailer_value"; g != e { 127 t.Errorf("Trailer(X-Unannounced-Trailer) = %q ; want %q", g, e) 128 } 129 130 // Test that a backend failing to be reached or one which doesn't return 131 // a response results in a StatusBadGateway. 132 getReq, _ = http.NewRequest("GET", frontend.URL+"/?mode=hangup", nil) 133 getReq.Close = true 134 res, err = frontendClient.Do(getReq) 135 if err != nil { 136 t.Fatal(err) 137 } 138 res.Body.Close() 139 if res.StatusCode != http.StatusBadGateway { 140 t.Errorf("request to bad proxy = %v; want 502 StatusBadGateway", res.Status) 141 } 142 143 } 144 145 // Issue 16875: remove any proxied headers mentioned in the "Connection" 146 // header value. 147 func TestReverseProxyStripHeadersPresentInConnection(t *testing.T) { 148 const fakeConnectionToken = "X-Fake-Connection-Token" 149 const backendResponse = "I am the backend" 150 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 151 if c := r.Header.Get(fakeConnectionToken); c != "" { 152 t.Errorf("handler got header %q = %q; want empty", fakeConnectionToken, c) 153 } 154 if c := r.Header.Get("Upgrade"); c != "" { 155 t.Errorf("handler got header %q = %q; want empty", "Upgrade", c) 156 } 157 w.Header().Set("Connection", "Upgrade, "+fakeConnectionToken) 158 w.Header().Set("Upgrade", "should be deleted") 159 w.Header().Set(fakeConnectionToken, "should be deleted") 160 io.WriteString(w, backendResponse) 161 })) 162 defer backend.Close() 163 backendURL, err := url.Parse(backend.URL) 164 if err != nil { 165 t.Fatal(err) 166 } 167 proxyHandler := NewSingleHostReverseProxy(backendURL) 168 frontend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 169 proxyHandler.ServeHTTP(w, r) 170 if c := r.Header.Get("Upgrade"); c != "original value" { 171 t.Errorf("handler modified header %q = %q; want %q", "Upgrade", c, "original value") 172 } 173 })) 174 defer frontend.Close() 175 176 getReq, _ := http.NewRequest("GET", frontend.URL, nil) 177 getReq.Header.Set("Connection", "Upgrade, "+fakeConnectionToken) 178 getReq.Header.Set("Upgrade", "original value") 179 getReq.Header.Set(fakeConnectionToken, "should be deleted") 180 res, err := frontend.Client().Do(getReq) 181 if err != nil { 182 t.Fatalf("Get: %v", err) 183 } 184 defer res.Body.Close() 185 bodyBytes, err := ioutil.ReadAll(res.Body) 186 if err != nil { 187 t.Fatalf("reading body: %v", err) 188 } 189 if got, want := string(bodyBytes), backendResponse; got != want { 190 t.Errorf("got body %q; want %q", got, want) 191 } 192 if c := res.Header.Get("Upgrade"); c != "" { 193 t.Errorf("handler got header %q = %q; want empty", "Upgrade", c) 194 } 195 if c := res.Header.Get(fakeConnectionToken); c != "" { 196 t.Errorf("handler got header %q = %q; want empty", fakeConnectionToken, c) 197 } 198 } 199 200 func TestXForwardedFor(t *testing.T) { 201 const prevForwardedFor = "client ip" 202 const backendResponse = "I am the backend" 203 const backendStatus = 404 204 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 205 if r.Header.Get("X-Forwarded-For") == "" { 206 t.Errorf("didn't get X-Forwarded-For header") 207 } 208 if !strings.Contains(r.Header.Get("X-Forwarded-For"), prevForwardedFor) { 209 t.Errorf("X-Forwarded-For didn't contain prior data") 210 } 211 w.WriteHeader(backendStatus) 212 w.Write([]byte(backendResponse)) 213 })) 214 defer backend.Close() 215 backendURL, err := url.Parse(backend.URL) 216 if err != nil { 217 t.Fatal(err) 218 } 219 proxyHandler := NewSingleHostReverseProxy(backendURL) 220 frontend := httptest.NewServer(proxyHandler) 221 defer frontend.Close() 222 223 getReq, _ := http.NewRequest("GET", frontend.URL, nil) 224 getReq.Host = "some-name" 225 getReq.Header.Set("Connection", "close") 226 getReq.Header.Set("X-Forwarded-For", prevForwardedFor) 227 getReq.Close = true 228 res, err := frontend.Client().Do(getReq) 229 if err != nil { 230 t.Fatalf("Get: %v", err) 231 } 232 if g, e := res.StatusCode, backendStatus; g != e { 233 t.Errorf("got res.StatusCode %d; expected %d", g, e) 234 } 235 bodyBytes, _ := ioutil.ReadAll(res.Body) 236 if g, e := string(bodyBytes), backendResponse; g != e { 237 t.Errorf("got body %q; expected %q", g, e) 238 } 239 } 240 241 var proxyQueryTests = []struct { 242 baseSuffix string // suffix to add to backend URL 243 reqSuffix string // suffix to add to frontend's request URL 244 want string // what backend should see for final request URL (without ?) 245 }{ 246 {"", "", ""}, 247 {"?sta=tic", "?us=er", "sta=tic&us=er"}, 248 {"", "?us=er", "us=er"}, 249 {"?sta=tic", "", "sta=tic"}, 250 } 251 252 func TestReverseProxyQuery(t *testing.T) { 253 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 254 w.Header().Set("X-Got-Query", r.URL.RawQuery) 255 w.Write([]byte("hi")) 256 })) 257 defer backend.Close() 258 259 for i, tt := range proxyQueryTests { 260 backendURL, err := url.Parse(backend.URL + tt.baseSuffix) 261 if err != nil { 262 t.Fatal(err) 263 } 264 frontend := httptest.NewServer(NewSingleHostReverseProxy(backendURL)) 265 req, _ := http.NewRequest("GET", frontend.URL+tt.reqSuffix, nil) 266 req.Close = true 267 res, err := frontend.Client().Do(req) 268 if err != nil { 269 t.Fatalf("%d. Get: %v", i, err) 270 } 271 if g, e := res.Header.Get("X-Got-Query"), tt.want; g != e { 272 t.Errorf("%d. got query %q; expected %q", i, g, e) 273 } 274 res.Body.Close() 275 frontend.Close() 276 } 277 } 278 279 func TestReverseProxyFlushInterval(t *testing.T) { 280 const expected = "hi" 281 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 282 w.Write([]byte(expected)) 283 })) 284 defer backend.Close() 285 286 backendURL, err := url.Parse(backend.URL) 287 if err != nil { 288 t.Fatal(err) 289 } 290 291 proxyHandler := NewSingleHostReverseProxy(backendURL) 292 proxyHandler.FlushInterval = time.Microsecond 293 294 done := make(chan bool) 295 onExitFlushLoop = func() { done <- true } 296 defer func() { onExitFlushLoop = nil }() 297 298 frontend := httptest.NewServer(proxyHandler) 299 defer frontend.Close() 300 301 req, _ := http.NewRequest("GET", frontend.URL, nil) 302 req.Close = true 303 res, err := frontend.Client().Do(req) 304 if err != nil { 305 t.Fatalf("Get: %v", err) 306 } 307 defer res.Body.Close() 308 if bodyBytes, _ := ioutil.ReadAll(res.Body); string(bodyBytes) != expected { 309 t.Errorf("got body %q; expected %q", bodyBytes, expected) 310 } 311 312 select { 313 case <-done: 314 // OK 315 case <-time.After(5 * time.Second): 316 t.Error("maxLatencyWriter flushLoop() never exited") 317 } 318 } 319 320 func TestReverseProxyCancelation(t *testing.T) { 321 const backendResponse = "I am the backend" 322 323 reqInFlight := make(chan struct{}) 324 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 325 close(reqInFlight) // cause the client to cancel its request 326 327 select { 328 case <-time.After(10 * time.Second): 329 // Note: this should only happen in broken implementations, and the 330 // closenotify case should be instantaneous. 331 t.Error("Handler never saw CloseNotify") 332 return 333 case <-w.(http.CloseNotifier).CloseNotify(): 334 } 335 336 w.WriteHeader(http.StatusOK) 337 w.Write([]byte(backendResponse)) 338 })) 339 340 defer backend.Close() 341 342 backend.Config.ErrorLog = log.New(ioutil.Discard, "", 0) 343 344 backendURL, err := url.Parse(backend.URL) 345 if err != nil { 346 t.Fatal(err) 347 } 348 349 proxyHandler := NewSingleHostReverseProxy(backendURL) 350 351 // Discards errors of the form: 352 // http: proxy error: read tcp 127.0.0.1:44643: use of closed network connection 353 proxyHandler.ErrorLog = log.New(ioutil.Discard, "", 0) 354 355 frontend := httptest.NewServer(proxyHandler) 356 defer frontend.Close() 357 frontendClient := frontend.Client() 358 359 getReq, _ := http.NewRequest("GET", frontend.URL, nil) 360 go func() { 361 <-reqInFlight 362 frontendClient.Transport.(*http.Transport).CancelRequest(getReq) 363 }() 364 res, err := frontendClient.Do(getReq) 365 if res != nil { 366 t.Errorf("got response %v; want nil", res.Status) 367 } 368 if err == nil { 369 // This should be an error like: 370 // Get http://127.0.0.1:58079: read tcp 127.0.0.1:58079: 371 // use of closed network connection 372 t.Error("Server.Client().Do() returned nil error; want non-nil error") 373 } 374 } 375 376 func req(t *testing.T, v string) *http.Request { 377 req, err := http.ReadRequest(bufio.NewReader(strings.NewReader(v))) 378 if err != nil { 379 t.Fatal(err) 380 } 381 return req 382 } 383 384 // Issue 12344 385 func TestNilBody(t *testing.T) { 386 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 387 w.Write([]byte("hi")) 388 })) 389 defer backend.Close() 390 391 frontend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { 392 backURL, _ := url.Parse(backend.URL) 393 rp := NewSingleHostReverseProxy(backURL) 394 r := req(t, "GET / HTTP/1.0\r\n\r\n") 395 r.Body = nil // this accidentally worked in Go 1.4 and below, so keep it working 396 rp.ServeHTTP(w, r) 397 })) 398 defer frontend.Close() 399 400 res, err := http.Get(frontend.URL) 401 if err != nil { 402 t.Fatal(err) 403 } 404 defer res.Body.Close() 405 slurp, err := ioutil.ReadAll(res.Body) 406 if err != nil { 407 t.Fatal(err) 408 } 409 if string(slurp) != "hi" { 410 t.Errorf("Got %q; want %q", slurp, "hi") 411 } 412 } 413 414 // Issue 15524 415 func TestUserAgentHeader(t *testing.T) { 416 const explicitUA = "explicit UA" 417 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 418 if r.URL.Path == "/noua" { 419 if c := r.Header.Get("User-Agent"); c != "" { 420 t.Errorf("handler got non-empty User-Agent header %q", c) 421 } 422 return 423 } 424 if c := r.Header.Get("User-Agent"); c != explicitUA { 425 t.Errorf("handler got unexpected User-Agent header %q", c) 426 } 427 })) 428 defer backend.Close() 429 backendURL, err := url.Parse(backend.URL) 430 if err != nil { 431 t.Fatal(err) 432 } 433 proxyHandler := NewSingleHostReverseProxy(backendURL) 434 proxyHandler.ErrorLog = log.New(ioutil.Discard, "", 0) // quiet for tests 435 frontend := httptest.NewServer(proxyHandler) 436 defer frontend.Close() 437 frontendClient := frontend.Client() 438 439 getReq, _ := http.NewRequest("GET", frontend.URL, nil) 440 getReq.Header.Set("User-Agent", explicitUA) 441 getReq.Close = true 442 res, err := frontendClient.Do(getReq) 443 if err != nil { 444 t.Fatalf("Get: %v", err) 445 } 446 res.Body.Close() 447 448 getReq, _ = http.NewRequest("GET", frontend.URL+"/noua", nil) 449 getReq.Header.Set("User-Agent", "") 450 getReq.Close = true 451 res, err = frontendClient.Do(getReq) 452 if err != nil { 453 t.Fatalf("Get: %v", err) 454 } 455 res.Body.Close() 456 } 457 458 type bufferPool struct { 459 get func() []byte 460 put func([]byte) 461 } 462 463 func (bp bufferPool) Get() []byte { return bp.get() } 464 func (bp bufferPool) Put(v []byte) { bp.put(v) } 465 466 func TestReverseProxyGetPutBuffer(t *testing.T) { 467 const msg = "hi" 468 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 469 io.WriteString(w, msg) 470 })) 471 defer backend.Close() 472 473 backendURL, err := url.Parse(backend.URL) 474 if err != nil { 475 t.Fatal(err) 476 } 477 478 var ( 479 mu sync.Mutex 480 log []string 481 ) 482 addLog := func(event string) { 483 mu.Lock() 484 defer mu.Unlock() 485 log = append(log, event) 486 } 487 rp := NewSingleHostReverseProxy(backendURL) 488 const size = 1234 489 rp.BufferPool = bufferPool{ 490 get: func() []byte { 491 addLog("getBuf") 492 return make([]byte, size) 493 }, 494 put: func(p []byte) { 495 addLog("putBuf-" + strconv.Itoa(len(p))) 496 }, 497 } 498 frontend := httptest.NewServer(rp) 499 defer frontend.Close() 500 501 req, _ := http.NewRequest("GET", frontend.URL, nil) 502 req.Close = true 503 res, err := frontend.Client().Do(req) 504 if err != nil { 505 t.Fatalf("Get: %v", err) 506 } 507 slurp, err := ioutil.ReadAll(res.Body) 508 res.Body.Close() 509 if err != nil { 510 t.Fatalf("reading body: %v", err) 511 } 512 if string(slurp) != msg { 513 t.Errorf("msg = %q; want %q", slurp, msg) 514 } 515 wantLog := []string{"getBuf", "putBuf-" + strconv.Itoa(size)} 516 mu.Lock() 517 defer mu.Unlock() 518 if !reflect.DeepEqual(log, wantLog) { 519 t.Errorf("Log events = %q; want %q", log, wantLog) 520 } 521 } 522 523 func TestReverseProxy_Post(t *testing.T) { 524 const backendResponse = "I am the backend" 525 const backendStatus = 200 526 var requestBody = bytes.Repeat([]byte("a"), 1<<20) 527 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 528 slurp, err := ioutil.ReadAll(r.Body) 529 if err != nil { 530 t.Errorf("Backend body read = %v", err) 531 } 532 if len(slurp) != len(requestBody) { 533 t.Errorf("Backend read %d request body bytes; want %d", len(slurp), len(requestBody)) 534 } 535 if !bytes.Equal(slurp, requestBody) { 536 t.Error("Backend read wrong request body.") // 1MB; omitting details 537 } 538 w.Write([]byte(backendResponse)) 539 })) 540 defer backend.Close() 541 backendURL, err := url.Parse(backend.URL) 542 if err != nil { 543 t.Fatal(err) 544 } 545 proxyHandler := NewSingleHostReverseProxy(backendURL) 546 frontend := httptest.NewServer(proxyHandler) 547 defer frontend.Close() 548 549 postReq, _ := http.NewRequest("POST", frontend.URL, bytes.NewReader(requestBody)) 550 res, err := frontend.Client().Do(postReq) 551 if err != nil { 552 t.Fatalf("Do: %v", err) 553 } 554 if g, e := res.StatusCode, backendStatus; g != e { 555 t.Errorf("got res.StatusCode %d; expected %d", g, e) 556 } 557 bodyBytes, _ := ioutil.ReadAll(res.Body) 558 if g, e := string(bodyBytes), backendResponse; g != e { 559 t.Errorf("got body %q; expected %q", g, e) 560 } 561 } 562 563 type RoundTripperFunc func(*http.Request) (*http.Response, error) 564 565 func (fn RoundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) { 566 return fn(req) 567 } 568 569 // Issue 16036: send a Request with a nil Body when possible 570 func TestReverseProxy_NilBody(t *testing.T) { 571 backendURL, _ := url.Parse("http://fake.tld/") 572 proxyHandler := NewSingleHostReverseProxy(backendURL) 573 proxyHandler.ErrorLog = log.New(ioutil.Discard, "", 0) // quiet for tests 574 proxyHandler.Transport = RoundTripperFunc(func(req *http.Request) (*http.Response, error) { 575 if req.Body != nil { 576 t.Error("Body != nil; want a nil Body") 577 } 578 return nil, errors.New("done testing the interesting part; so force a 502 Gateway error") 579 }) 580 frontend := httptest.NewServer(proxyHandler) 581 defer frontend.Close() 582 583 res, err := frontend.Client().Get(frontend.URL) 584 if err != nil { 585 t.Fatal(err) 586 } 587 defer res.Body.Close() 588 if res.StatusCode != 502 { 589 t.Errorf("status code = %v; want 502 (Gateway Error)", res.Status) 590 } 591 } 592 593 // Issue 14237. Test ModifyResponse and that an error from it 594 // causes the proxy to return StatusBadGateway, or StatusOK otherwise. 595 func TestReverseProxyModifyResponse(t *testing.T) { 596 backendServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 597 w.Header().Add("X-Hit-Mod", fmt.Sprintf("%v", r.URL.Path == "/mod")) 598 })) 599 defer backendServer.Close() 600 601 rpURL, _ := url.Parse(backendServer.URL) 602 rproxy := NewSingleHostReverseProxy(rpURL) 603 rproxy.ErrorLog = log.New(ioutil.Discard, "", 0) // quiet for tests 604 rproxy.ModifyResponse = func(resp *http.Response) error { 605 if resp.Header.Get("X-Hit-Mod") != "true" { 606 return fmt.Errorf("tried to by-pass proxy") 607 } 608 return nil 609 } 610 611 frontendProxy := httptest.NewServer(rproxy) 612 defer frontendProxy.Close() 613 614 tests := []struct { 615 url string 616 wantCode int 617 }{ 618 {frontendProxy.URL + "/mod", http.StatusOK}, 619 {frontendProxy.URL + "/schedule", http.StatusBadGateway}, 620 } 621 622 for i, tt := range tests { 623 resp, err := http.Get(tt.url) 624 if err != nil { 625 t.Fatalf("failed to reach proxy: %v", err) 626 } 627 if g, e := resp.StatusCode, tt.wantCode; g != e { 628 t.Errorf("#%d: got res.StatusCode %d; expected %d", i, g, e) 629 } 630 resp.Body.Close() 631 } 632 } 633 634 // Issue 16659: log errors from short read 635 func TestReverseProxy_CopyBuffer(t *testing.T) { 636 backendServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 637 out := "this call was relayed by the reverse proxy" 638 // Coerce a wrong content length to induce io.UnexpectedEOF 639 w.Header().Set("Content-Length", fmt.Sprintf("%d", len(out)*2)) 640 fmt.Fprintln(w, out) 641 })) 642 defer backendServer.Close() 643 644 rpURL, err := url.Parse(backendServer.URL) 645 if err != nil { 646 t.Fatal(err) 647 } 648 649 var proxyLog bytes.Buffer 650 rproxy := NewSingleHostReverseProxy(rpURL) 651 rproxy.ErrorLog = log.New(&proxyLog, "", log.Lshortfile) 652 frontendProxy := httptest.NewServer(rproxy) 653 defer frontendProxy.Close() 654 655 resp, err := http.Get(frontendProxy.URL) 656 if err != nil { 657 t.Fatalf("failed to reach proxy: %v", err) 658 } 659 defer resp.Body.Close() 660 661 if _, err := ioutil.ReadAll(resp.Body); err == nil { 662 t.Fatalf("want non-nil error") 663 } 664 expected := []string{ 665 "EOF", 666 "read", 667 } 668 for _, phrase := range expected { 669 if !bytes.Contains(proxyLog.Bytes(), []byte(phrase)) { 670 t.Errorf("expected log to contain phrase %q", phrase) 671 } 672 } 673 } 674 675 type staticTransport struct { 676 res *http.Response 677 } 678 679 func (t *staticTransport) RoundTrip(r *http.Request) (*http.Response, error) { 680 return t.res, nil 681 } 682 683 func BenchmarkServeHTTP(b *testing.B) { 684 res := &http.Response{ 685 StatusCode: 200, 686 Body: ioutil.NopCloser(strings.NewReader("")), 687 } 688 proxy := &ReverseProxy{ 689 Director: func(*http.Request) {}, 690 Transport: &staticTransport{res}, 691 } 692 693 w := httptest.NewRecorder() 694 r := httptest.NewRequest("GET", "/", nil) 695 696 b.ReportAllocs() 697 for i := 0; i < b.N; i++ { 698 proxy.ServeHTTP(w, r) 699 } 700 } 701 702 func TestServeHTTPDeepCopy(t *testing.T) { 703 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 704 w.Write([]byte("Hello Gopher!")) 705 })) 706 defer backend.Close() 707 backendURL, err := url.Parse(backend.URL) 708 if err != nil { 709 t.Fatal(err) 710 } 711 712 type result struct { 713 before, after string 714 } 715 716 resultChan := make(chan result, 1) 717 proxyHandler := NewSingleHostReverseProxy(backendURL) 718 frontend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 719 before := r.URL.String() 720 proxyHandler.ServeHTTP(w, r) 721 after := r.URL.String() 722 resultChan <- result{before: before, after: after} 723 })) 724 defer frontend.Close() 725 726 want := result{before: "/", after: "/"} 727 728 res, err := frontend.Client().Get(frontend.URL) 729 if err != nil { 730 t.Fatalf("Do: %v", err) 731 } 732 res.Body.Close() 733 734 got := <-resultChan 735 if got != want { 736 t.Errorf("got = %+v; want = %+v", got, want) 737 } 738 } 739 740 // Issue 18327: verify we always do a deep copy of the Request.Header map 741 // before any mutations. 742 func TestClonesRequestHeaders(t *testing.T) { 743 req, _ := http.NewRequest("GET", "http://foo.tld/", nil) 744 req.RemoteAddr = "1.2.3.4:56789" 745 rp := &ReverseProxy{ 746 Director: func(req *http.Request) { 747 req.Header.Set("From-Director", "1") 748 }, 749 Transport: roundTripperFunc(func(req *http.Request) (*http.Response, error) { 750 if v := req.Header.Get("From-Director"); v != "1" { 751 t.Errorf("From-Directory value = %q; want 1", v) 752 } 753 return nil, io.EOF 754 }), 755 } 756 rp.ServeHTTP(httptest.NewRecorder(), req) 757 758 if req.Header.Get("From-Director") == "1" { 759 t.Error("Director header mutation modified caller's request") 760 } 761 if req.Header.Get("X-Forwarded-For") != "" { 762 t.Error("X-Forward-For header mutation modified caller's request") 763 } 764 765 } 766 767 type roundTripperFunc func(req *http.Request) (*http.Response, error) 768 769 func (fn roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) { 770 return fn(req) 771 }