github.com/aloncn/graphics-go@v0.0.1/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 "io" 13 "io/ioutil" 14 "log" 15 "net/http" 16 "net/http/httptest" 17 "net/url" 18 "reflect" 19 "strconv" 20 "strings" 21 "sync" 22 "testing" 23 "time" 24 ) 25 26 const fakeHopHeader = "X-Fake-Hop-Header-For-Test" 27 28 func init() { 29 hopHeaders = append(hopHeaders, fakeHopHeader) 30 } 31 32 func TestReverseProxy(t *testing.T) { 33 const backendResponse = "I am the backend" 34 const backendStatus = 404 35 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 36 if len(r.TransferEncoding) > 0 { 37 t.Errorf("backend got unexpected TransferEncoding: %v", r.TransferEncoding) 38 } 39 if r.Header.Get("X-Forwarded-For") == "" { 40 t.Errorf("didn't get X-Forwarded-For header") 41 } 42 if c := r.Header.Get("Connection"); c != "" { 43 t.Errorf("handler got Connection header value %q", c) 44 } 45 if c := r.Header.Get("Upgrade"); c != "" { 46 t.Errorf("handler got Upgrade header value %q", c) 47 } 48 if c := r.Header.Get("Proxy-Connection"); c != "" { 49 t.Errorf("handler got Proxy-Connection header value %q", c) 50 } 51 if g, e := r.Host, "some-name"; g != e { 52 t.Errorf("backend got Host header %q, want %q", g, e) 53 } 54 w.Header().Set("Trailers", "not a special header field name") 55 w.Header().Set("Trailer", "X-Trailer") 56 w.Header().Set("X-Foo", "bar") 57 w.Header().Set("Upgrade", "foo") 58 w.Header().Set(fakeHopHeader, "foo") 59 w.Header().Add("X-Multi-Value", "foo") 60 w.Header().Add("X-Multi-Value", "bar") 61 http.SetCookie(w, &http.Cookie{Name: "flavor", Value: "chocolateChip"}) 62 w.WriteHeader(backendStatus) 63 w.Write([]byte(backendResponse)) 64 w.Header().Set("X-Trailer", "trailer_value") 65 })) 66 defer backend.Close() 67 backendURL, err := url.Parse(backend.URL) 68 if err != nil { 69 t.Fatal(err) 70 } 71 proxyHandler := NewSingleHostReverseProxy(backendURL) 72 frontend := httptest.NewServer(proxyHandler) 73 defer frontend.Close() 74 75 getReq, _ := http.NewRequest("GET", frontend.URL, nil) 76 getReq.Host = "some-name" 77 getReq.Header.Set("Connection", "close") 78 getReq.Header.Set("Proxy-Connection", "should be deleted") 79 getReq.Header.Set("Upgrade", "foo") 80 getReq.Close = true 81 res, err := http.DefaultClient.Do(getReq) 82 if err != nil { 83 t.Fatalf("Get: %v", err) 84 } 85 if g, e := res.StatusCode, backendStatus; g != e { 86 t.Errorf("got res.StatusCode %d; expected %d", g, e) 87 } 88 if g, e := res.Header.Get("X-Foo"), "bar"; g != e { 89 t.Errorf("got X-Foo %q; expected %q", g, e) 90 } 91 if c := res.Header.Get(fakeHopHeader); c != "" { 92 t.Errorf("got %s header value %q", fakeHopHeader, c) 93 } 94 if g, e := res.Header.Get("Trailers"), "not a special header field name"; g != e { 95 t.Errorf("header Trailers = %q; want %q", g, e) 96 } 97 if g, e := len(res.Header["X-Multi-Value"]), 2; g != e { 98 t.Errorf("got %d X-Multi-Value header values; expected %d", g, e) 99 } 100 if g, e := len(res.Header["Set-Cookie"]), 1; g != e { 101 t.Fatalf("got %d SetCookies, want %d", g, e) 102 } 103 if g, e := res.Trailer, (http.Header{"X-Trailer": nil}); !reflect.DeepEqual(g, e) { 104 t.Errorf("before reading body, Trailer = %#v; want %#v", g, e) 105 } 106 if cookie := res.Cookies()[0]; cookie.Name != "flavor" { 107 t.Errorf("unexpected cookie %q", cookie.Name) 108 } 109 bodyBytes, _ := ioutil.ReadAll(res.Body) 110 if g, e := string(bodyBytes), backendResponse; g != e { 111 t.Errorf("got body %q; expected %q", g, e) 112 } 113 if g, e := res.Trailer.Get("X-Trailer"), "trailer_value"; g != e { 114 t.Errorf("Trailer(X-Trailer) = %q ; want %q", g, e) 115 } 116 } 117 118 func TestXForwardedFor(t *testing.T) { 119 const prevForwardedFor = "client ip" 120 const backendResponse = "I am the backend" 121 const backendStatus = 404 122 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 123 if r.Header.Get("X-Forwarded-For") == "" { 124 t.Errorf("didn't get X-Forwarded-For header") 125 } 126 if !strings.Contains(r.Header.Get("X-Forwarded-For"), prevForwardedFor) { 127 t.Errorf("X-Forwarded-For didn't contain prior data") 128 } 129 w.WriteHeader(backendStatus) 130 w.Write([]byte(backendResponse)) 131 })) 132 defer backend.Close() 133 backendURL, err := url.Parse(backend.URL) 134 if err != nil { 135 t.Fatal(err) 136 } 137 proxyHandler := NewSingleHostReverseProxy(backendURL) 138 frontend := httptest.NewServer(proxyHandler) 139 defer frontend.Close() 140 141 getReq, _ := http.NewRequest("GET", frontend.URL, nil) 142 getReq.Host = "some-name" 143 getReq.Header.Set("Connection", "close") 144 getReq.Header.Set("X-Forwarded-For", prevForwardedFor) 145 getReq.Close = true 146 res, err := http.DefaultClient.Do(getReq) 147 if err != nil { 148 t.Fatalf("Get: %v", err) 149 } 150 if g, e := res.StatusCode, backendStatus; g != e { 151 t.Errorf("got res.StatusCode %d; expected %d", g, e) 152 } 153 bodyBytes, _ := ioutil.ReadAll(res.Body) 154 if g, e := string(bodyBytes), backendResponse; g != e { 155 t.Errorf("got body %q; expected %q", g, e) 156 } 157 } 158 159 var proxyQueryTests = []struct { 160 baseSuffix string // suffix to add to backend URL 161 reqSuffix string // suffix to add to frontend's request URL 162 want string // what backend should see for final request URL (without ?) 163 }{ 164 {"", "", ""}, 165 {"?sta=tic", "?us=er", "sta=tic&us=er"}, 166 {"", "?us=er", "us=er"}, 167 {"?sta=tic", "", "sta=tic"}, 168 } 169 170 func TestReverseProxyQuery(t *testing.T) { 171 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 172 w.Header().Set("X-Got-Query", r.URL.RawQuery) 173 w.Write([]byte("hi")) 174 })) 175 defer backend.Close() 176 177 for i, tt := range proxyQueryTests { 178 backendURL, err := url.Parse(backend.URL + tt.baseSuffix) 179 if err != nil { 180 t.Fatal(err) 181 } 182 frontend := httptest.NewServer(NewSingleHostReverseProxy(backendURL)) 183 req, _ := http.NewRequest("GET", frontend.URL+tt.reqSuffix, nil) 184 req.Close = true 185 res, err := http.DefaultClient.Do(req) 186 if err != nil { 187 t.Fatalf("%d. Get: %v", i, err) 188 } 189 if g, e := res.Header.Get("X-Got-Query"), tt.want; g != e { 190 t.Errorf("%d. got query %q; expected %q", i, g, e) 191 } 192 res.Body.Close() 193 frontend.Close() 194 } 195 } 196 197 func TestReverseProxyFlushInterval(t *testing.T) { 198 const expected = "hi" 199 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 200 w.Write([]byte(expected)) 201 })) 202 defer backend.Close() 203 204 backendURL, err := url.Parse(backend.URL) 205 if err != nil { 206 t.Fatal(err) 207 } 208 209 proxyHandler := NewSingleHostReverseProxy(backendURL) 210 proxyHandler.FlushInterval = time.Microsecond 211 212 done := make(chan bool) 213 onExitFlushLoop = func() { done <- true } 214 defer func() { onExitFlushLoop = nil }() 215 216 frontend := httptest.NewServer(proxyHandler) 217 defer frontend.Close() 218 219 req, _ := http.NewRequest("GET", frontend.URL, nil) 220 req.Close = true 221 res, err := http.DefaultClient.Do(req) 222 if err != nil { 223 t.Fatalf("Get: %v", err) 224 } 225 defer res.Body.Close() 226 if bodyBytes, _ := ioutil.ReadAll(res.Body); string(bodyBytes) != expected { 227 t.Errorf("got body %q; expected %q", bodyBytes, expected) 228 } 229 230 select { 231 case <-done: 232 // OK 233 case <-time.After(5 * time.Second): 234 t.Error("maxLatencyWriter flushLoop() never exited") 235 } 236 } 237 238 func TestReverseProxyCancelation(t *testing.T) { 239 const backendResponse = "I am the backend" 240 241 reqInFlight := make(chan struct{}) 242 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 243 close(reqInFlight) 244 245 select { 246 case <-time.After(10 * time.Second): 247 // Note: this should only happen in broken implementations, and the 248 // closenotify case should be instantaneous. 249 t.Log("Failed to close backend connection") 250 t.Fail() 251 case <-w.(http.CloseNotifier).CloseNotify(): 252 } 253 254 w.WriteHeader(http.StatusOK) 255 w.Write([]byte(backendResponse)) 256 })) 257 258 defer backend.Close() 259 260 backend.Config.ErrorLog = log.New(ioutil.Discard, "", 0) 261 262 backendURL, err := url.Parse(backend.URL) 263 if err != nil { 264 t.Fatal(err) 265 } 266 267 proxyHandler := NewSingleHostReverseProxy(backendURL) 268 269 // Discards errors of the form: 270 // http: proxy error: read tcp 127.0.0.1:44643: use of closed network connection 271 proxyHandler.ErrorLog = log.New(ioutil.Discard, "", 0) 272 273 frontend := httptest.NewServer(proxyHandler) 274 defer frontend.Close() 275 276 getReq, _ := http.NewRequest("GET", frontend.URL, nil) 277 go func() { 278 <-reqInFlight 279 http.DefaultTransport.(*http.Transport).CancelRequest(getReq) 280 }() 281 res, err := http.DefaultClient.Do(getReq) 282 if res != nil { 283 t.Fatal("Non-nil response") 284 } 285 if err == nil { 286 // This should be an error like: 287 // Get http://127.0.0.1:58079: read tcp 127.0.0.1:58079: 288 // use of closed network connection 289 t.Fatal("DefaultClient.Do() returned nil error") 290 } 291 } 292 293 func req(t *testing.T, v string) *http.Request { 294 req, err := http.ReadRequest(bufio.NewReader(strings.NewReader(v))) 295 if err != nil { 296 t.Fatal(err) 297 } 298 return req 299 } 300 301 // Issue 12344 302 func TestNilBody(t *testing.T) { 303 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 304 w.Write([]byte("hi")) 305 })) 306 defer backend.Close() 307 308 frontend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { 309 backURL, _ := url.Parse(backend.URL) 310 rp := NewSingleHostReverseProxy(backURL) 311 r := req(t, "GET / HTTP/1.0\r\n\r\n") 312 r.Body = nil // this accidentally worked in Go 1.4 and below, so keep it working 313 rp.ServeHTTP(w, r) 314 })) 315 defer frontend.Close() 316 317 res, err := http.Get(frontend.URL) 318 if err != nil { 319 t.Fatal(err) 320 } 321 defer res.Body.Close() 322 slurp, err := ioutil.ReadAll(res.Body) 323 if err != nil { 324 t.Fatal(err) 325 } 326 if string(slurp) != "hi" { 327 t.Errorf("Got %q; want %q", slurp, "hi") 328 } 329 } 330 331 type bufferPool struct { 332 get func() []byte 333 put func([]byte) 334 } 335 336 func (bp bufferPool) Get() []byte { return bp.get() } 337 func (bp bufferPool) Put(v []byte) { bp.put(v) } 338 339 func TestReverseProxyGetPutBuffer(t *testing.T) { 340 const msg = "hi" 341 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 342 io.WriteString(w, msg) 343 })) 344 defer backend.Close() 345 346 backendURL, err := url.Parse(backend.URL) 347 if err != nil { 348 t.Fatal(err) 349 } 350 351 var ( 352 mu sync.Mutex 353 log []string 354 ) 355 addLog := func(event string) { 356 mu.Lock() 357 defer mu.Unlock() 358 log = append(log, event) 359 } 360 rp := NewSingleHostReverseProxy(backendURL) 361 const size = 1234 362 rp.BufferPool = bufferPool{ 363 get: func() []byte { 364 addLog("getBuf") 365 return make([]byte, size) 366 }, 367 put: func(p []byte) { 368 addLog("putBuf-" + strconv.Itoa(len(p))) 369 }, 370 } 371 frontend := httptest.NewServer(rp) 372 defer frontend.Close() 373 374 req, _ := http.NewRequest("GET", frontend.URL, nil) 375 req.Close = true 376 res, err := http.DefaultClient.Do(req) 377 if err != nil { 378 t.Fatalf("Get: %v", err) 379 } 380 slurp, err := ioutil.ReadAll(res.Body) 381 res.Body.Close() 382 if err != nil { 383 t.Fatalf("reading body: %v", err) 384 } 385 if string(slurp) != msg { 386 t.Errorf("msg = %q; want %q", slurp, msg) 387 } 388 wantLog := []string{"getBuf", "putBuf-" + strconv.Itoa(size)} 389 mu.Lock() 390 defer mu.Unlock() 391 if !reflect.DeepEqual(log, wantLog) { 392 t.Errorf("Log events = %q; want %q", log, wantLog) 393 } 394 } 395 396 func TestReverseProxy_Post(t *testing.T) { 397 const backendResponse = "I am the backend" 398 const backendStatus = 200 399 var requestBody = bytes.Repeat([]byte("a"), 1<<20) 400 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 401 slurp, err := ioutil.ReadAll(r.Body) 402 if err != nil { 403 t.Errorf("Backend body read = %v", err) 404 } 405 if len(slurp) != len(requestBody) { 406 t.Errorf("Backend read %d request body bytes; want %d", len(slurp), len(requestBody)) 407 } 408 if !bytes.Equal(slurp, requestBody) { 409 t.Error("Backend read wrong request body.") // 1MB; omitting details 410 } 411 w.Write([]byte(backendResponse)) 412 })) 413 defer backend.Close() 414 backendURL, err := url.Parse(backend.URL) 415 if err != nil { 416 t.Fatal(err) 417 } 418 proxyHandler := NewSingleHostReverseProxy(backendURL) 419 frontend := httptest.NewServer(proxyHandler) 420 defer frontend.Close() 421 422 postReq, _ := http.NewRequest("POST", frontend.URL, bytes.NewReader(requestBody)) 423 res, err := http.DefaultClient.Do(postReq) 424 if err != nil { 425 t.Fatalf("Do: %v", err) 426 } 427 if g, e := res.StatusCode, backendStatus; g != e { 428 t.Errorf("got res.StatusCode %d; expected %d", g, e) 429 } 430 bodyBytes, _ := ioutil.ReadAll(res.Body) 431 if g, e := string(bodyBytes), backendResponse; g != e { 432 t.Errorf("got body %q; expected %q", g, e) 433 } 434 }