github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/pkg/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 "io/ioutil" 11 "net/http" 12 "net/http/httptest" 13 "net/url" 14 "strings" 15 "testing" 16 "time" 17 ) 18 19 func TestReverseProxy(t *testing.T) { 20 const backendResponse = "I am the backend" 21 const backendStatus = 404 22 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 23 if len(r.TransferEncoding) > 0 { 24 t.Errorf("backend got unexpected TransferEncoding: %v", r.TransferEncoding) 25 } 26 if r.Header.Get("X-Forwarded-For") == "" { 27 t.Errorf("didn't get X-Forwarded-For header") 28 } 29 if c := r.Header.Get("Connection"); c != "" { 30 t.Errorf("handler got Connection header value %q", c) 31 } 32 if c := r.Header.Get("Upgrade"); c != "" { 33 t.Errorf("handler got Upgrade header value %q", c) 34 } 35 if g, e := r.Host, "some-name"; g != e { 36 t.Errorf("backend got Host header %q, want %q", g, e) 37 } 38 w.Header().Set("X-Foo", "bar") 39 http.SetCookie(w, &http.Cookie{Name: "flavor", Value: "chocolateChip"}) 40 w.WriteHeader(backendStatus) 41 w.Write([]byte(backendResponse)) 42 })) 43 defer backend.Close() 44 backendURL, err := url.Parse(backend.URL) 45 if err != nil { 46 t.Fatal(err) 47 } 48 proxyHandler := NewSingleHostReverseProxy(backendURL) 49 frontend := httptest.NewServer(proxyHandler) 50 defer frontend.Close() 51 52 getReq, _ := http.NewRequest("GET", frontend.URL, nil) 53 getReq.Host = "some-name" 54 getReq.Header.Set("Connection", "close") 55 getReq.Header.Set("Upgrade", "foo") 56 getReq.Close = true 57 res, err := http.DefaultClient.Do(getReq) 58 if err != nil { 59 t.Fatalf("Get: %v", err) 60 } 61 if g, e := res.StatusCode, backendStatus; g != e { 62 t.Errorf("got res.StatusCode %d; expected %d", g, e) 63 } 64 if g, e := res.Header.Get("X-Foo"), "bar"; g != e { 65 t.Errorf("got X-Foo %q; expected %q", g, e) 66 } 67 if g, e := len(res.Header["Set-Cookie"]), 1; g != e { 68 t.Fatalf("got %d SetCookies, want %d", g, e) 69 } 70 if cookie := res.Cookies()[0]; cookie.Name != "flavor" { 71 t.Errorf("unexpected cookie %q", cookie.Name) 72 } 73 bodyBytes, _ := ioutil.ReadAll(res.Body) 74 if g, e := string(bodyBytes), backendResponse; g != e { 75 t.Errorf("got body %q; expected %q", g, e) 76 } 77 } 78 79 func TestXForwardedFor(t *testing.T) { 80 const prevForwardedFor = "client ip" 81 const backendResponse = "I am the backend" 82 const backendStatus = 404 83 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 84 if r.Header.Get("X-Forwarded-For") == "" { 85 t.Errorf("didn't get X-Forwarded-For header") 86 } 87 if !strings.Contains(r.Header.Get("X-Forwarded-For"), prevForwardedFor) { 88 t.Errorf("X-Forwarded-For didn't contain prior data") 89 } 90 w.WriteHeader(backendStatus) 91 w.Write([]byte(backendResponse)) 92 })) 93 defer backend.Close() 94 backendURL, err := url.Parse(backend.URL) 95 if err != nil { 96 t.Fatal(err) 97 } 98 proxyHandler := NewSingleHostReverseProxy(backendURL) 99 frontend := httptest.NewServer(proxyHandler) 100 defer frontend.Close() 101 102 getReq, _ := http.NewRequest("GET", frontend.URL, nil) 103 getReq.Host = "some-name" 104 getReq.Header.Set("Connection", "close") 105 getReq.Header.Set("X-Forwarded-For", prevForwardedFor) 106 getReq.Close = true 107 res, err := http.DefaultClient.Do(getReq) 108 if err != nil { 109 t.Fatalf("Get: %v", err) 110 } 111 if g, e := res.StatusCode, backendStatus; g != e { 112 t.Errorf("got res.StatusCode %d; expected %d", g, e) 113 } 114 bodyBytes, _ := ioutil.ReadAll(res.Body) 115 if g, e := string(bodyBytes), backendResponse; g != e { 116 t.Errorf("got body %q; expected %q", g, e) 117 } 118 } 119 120 var proxyQueryTests = []struct { 121 baseSuffix string // suffix to add to backend URL 122 reqSuffix string // suffix to add to frontend's request URL 123 want string // what backend should see for final request URL (without ?) 124 }{ 125 {"", "", ""}, 126 {"?sta=tic", "?us=er", "sta=tic&us=er"}, 127 {"", "?us=er", "us=er"}, 128 {"?sta=tic", "", "sta=tic"}, 129 } 130 131 func TestReverseProxyQuery(t *testing.T) { 132 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 133 w.Header().Set("X-Got-Query", r.URL.RawQuery) 134 w.Write([]byte("hi")) 135 })) 136 defer backend.Close() 137 138 for i, tt := range proxyQueryTests { 139 backendURL, err := url.Parse(backend.URL + tt.baseSuffix) 140 if err != nil { 141 t.Fatal(err) 142 } 143 frontend := httptest.NewServer(NewSingleHostReverseProxy(backendURL)) 144 req, _ := http.NewRequest("GET", frontend.URL+tt.reqSuffix, nil) 145 req.Close = true 146 res, err := http.DefaultClient.Do(req) 147 if err != nil { 148 t.Fatalf("%d. Get: %v", i, err) 149 } 150 if g, e := res.Header.Get("X-Got-Query"), tt.want; g != e { 151 t.Errorf("%d. got query %q; expected %q", i, g, e) 152 } 153 res.Body.Close() 154 frontend.Close() 155 } 156 } 157 158 func TestReverseProxyFlushInterval(t *testing.T) { 159 const expected = "hi" 160 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 161 w.Write([]byte(expected)) 162 })) 163 defer backend.Close() 164 165 backendURL, err := url.Parse(backend.URL) 166 if err != nil { 167 t.Fatal(err) 168 } 169 170 proxyHandler := NewSingleHostReverseProxy(backendURL) 171 proxyHandler.FlushInterval = time.Microsecond 172 173 done := make(chan bool) 174 onExitFlushLoop = func() { done <- true } 175 defer func() { onExitFlushLoop = nil }() 176 177 frontend := httptest.NewServer(proxyHandler) 178 defer frontend.Close() 179 180 req, _ := http.NewRequest("GET", frontend.URL, nil) 181 req.Close = true 182 res, err := http.DefaultClient.Do(req) 183 if err != nil { 184 t.Fatalf("Get: %v", err) 185 } 186 defer res.Body.Close() 187 if bodyBytes, _ := ioutil.ReadAll(res.Body); string(bodyBytes) != expected { 188 t.Errorf("got body %q; expected %q", bodyBytes, expected) 189 } 190 191 select { 192 case <-done: 193 // OK 194 case <-time.After(5 * time.Second): 195 t.Error("maxLatencyWriter flushLoop() never exited") 196 } 197 }