func TestReverseProxyQuery(t *testing.T) { backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-Got-Query", r.URL.RawQuery) w.Write([]byte("hi")) })) defer backend.Close() for i, tt := range proxyQueryTests { backendURL, err := url.Parse(backend.URL + tt.baseSuffix) if err != nil { t.Fatal(err) } frontend := httptest.NewServer(NewSingleHostReverseProxy(backendURL)) req, _ := http.NewRequest("GET", frontend.URL+tt.reqSuffix, nil) req.Close = true res, err := http.DefaultClient.Do(req) if err != nil { t.Fatalf("%d. Get: %v", i, err) } if g, e := res.Header.Get("X-Got-Query"), tt.want; g != e { t.Errorf("%d. got query %q; expected %q", i, g, e) } res.Body.Close() frontend.Close() } }
func TestInternalRedirect(t *testing.T) { check(t) baseHandler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { fmt.Fprintf(rw, "basepath=%s\n", req.URL.Path) fmt.Fprintf(rw, "remoteaddr=%s\n", req.RemoteAddr) }) h := &Handler{ Path: "testdata/test.cgi", Root: "/test.cgi", PathLocationHandler: baseHandler, } expectedMap := map[string]string{ "basepath": "/foo", "remoteaddr": "1.2.3.4", } runCgiTest(t, h, "GET /test.cgi?loc=/foo HTTP/1.0\nHost: example.com\n\n", expectedMap) }
func TestServer(t *testing.T) { ts := NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello")) })) defer ts.Close() res, err := http.Get(ts.URL) if err != nil { t.Fatal(err) } got, err := ioutil.ReadAll(res.Body) if err != nil { t.Fatal(err) } if string(got) != "hello" { t.Errorf("got %q, want hello", string(got)) } }
func ExampleServer() { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello, client") })) defer ts.Close() res, err := http.Get(ts.URL) if err != nil { log.Fatal(err) } greeting, err := ioutil.ReadAll(res.Body) res.Body.Close() if err != nil { log.Fatal(err) } fmt.Printf("%s", greeting) // Output: Hello, client }
func main() { flag.Parse() // The counter is published as a variable directly. ctr := new(Counter) expvar.Publish("counter", ctr) http.Handle("/counter", ctr) http.Handle("/", http.HandlerFunc(Logger)) http.Handle("/go/", http.StripPrefix("/go/", http.FileServer(http.Dir(*webroot)))) http.Handle("/chan", ChanCreate()) http.HandleFunc("/flags", FlagServer) http.HandleFunc("/args", ArgServer) http.HandleFunc("/go/hello", HelloServer) http.HandleFunc("/date", DateServer) err := http.ListenAndServe(":12345", nil) if err != nil { log.Panicln("ListenAndServe:", err) } }
func TestXForwardedFor(t *testing.T) { const prevForwardedFor = "client ip" const backendResponse = "I am the backend" const backendStatus = 404 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("X-Forwarded-For") == "" { t.Errorf("didn't get X-Forwarded-For header") } if !strings.Contains(r.Header.Get("X-Forwarded-For"), prevForwardedFor) { t.Errorf("X-Forwarded-For didn't contain prior data") } w.WriteHeader(backendStatus) w.Write([]byte(backendResponse)) })) defer backend.Close() backendURL, err := url.Parse(backend.URL) if err != nil { t.Fatal(err) } proxyHandler := NewSingleHostReverseProxy(backendURL) frontend := httptest.NewServer(proxyHandler) defer frontend.Close() getReq, _ := http.NewRequest("GET", frontend.URL, nil) getReq.Host = "some-name" getReq.Header.Set("Connection", "close") getReq.Header.Set("X-Forwarded-For", prevForwardedFor) getReq.Close = true res, err := http.DefaultClient.Do(getReq) if err != nil { t.Fatalf("Get: %v", err) } if g, e := res.StatusCode, backendStatus; g != e { t.Errorf("got res.StatusCode %d; expected %d", g, e) } bodyBytes, _ := ioutil.ReadAll(res.Body) if g, e := string(bodyBytes), backendResponse; g != e { t.Errorf("got body %q; expected %q", g, e) } }
func TestReverseProxyFlushInterval(t *testing.T) { const expected = "hi" backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(expected)) })) defer backend.Close() backendURL, err := url.Parse(backend.URL) if err != nil { t.Fatal(err) } proxyHandler := NewSingleHostReverseProxy(backendURL) proxyHandler.FlushInterval = time.Microsecond done := make(chan bool) onExitFlushLoop = func() { done <- true } defer func() { onExitFlushLoop = nil }() frontend := httptest.NewServer(proxyHandler) defer frontend.Close() req, _ := http.NewRequest("GET", frontend.URL, nil) req.Close = true res, err := http.DefaultClient.Do(req) if err != nil { t.Fatalf("Get: %v", err) } defer res.Body.Close() if bodyBytes, _ := ioutil.ReadAll(res.Body); string(bodyBytes) != expected { t.Errorf("got body %q; expected %q", bodyBytes, expected) } select { case <-done: // OK case <-time.After(5 * time.Second): t.Error("maxLatencyWriter flushLoop() never exited") } }
// Note: not actually a test. func TestBeChildCGIProcess(t *testing.T) { if os.Getenv("REQUEST_METHOD") == "" { // Not in a CGI environment; skipping test. return } switch os.Getenv("REQUEST_URI") { case "/immediate-disconnect": os.Exit(0) case "/no-content-type": fmt.Printf("Content-Length: 6\n\nHello\n") os.Exit(0) case "/empty-headers": fmt.Printf("\nHello") os.Exit(0) } Serve(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.Header().Set("X-Test-Header", "X-Test-Value") req.ParseForm() if req.FormValue("no-body") == "1" { return } if req.FormValue("write-forever") == "1" { io.Copy(rw, neverEnding('a')) for { time.Sleep(5 * time.Second) // hang forever, until killed } } fmt.Fprintf(rw, "test=Hello CGI-in-CGI\n") for k, vv := range req.Form { for _, v := range vv { fmt.Fprintf(rw, "param-%s=%s\n", k, v) } } for _, kv := range os.Environ() { fmt.Fprintf(rw, "env-%s\n", kv) } })) os.Exit(0) }
func init() { http.Handle("/debug/pprof/", http.HandlerFunc(Index)) http.Handle("/debug/pprof/cmdline", http.HandlerFunc(Cmdline)) http.Handle("/debug/pprof/profile", http.HandlerFunc(Profile)) http.Handle("/debug/pprof/symbol", http.HandlerFunc(Symbol)) }
func main() { verbose := flag.Bool("v", true, "should every proxy request be logged to stdout") http_addr := flag.String("httpaddr", ":3129", "proxy http listen address") https_addr := flag.String("httpsaddr", ":3128", "proxy https listen address") flag.Parse() proxy := goproxy.NewProxyHttpServer() proxy.Verbose = *verbose if proxy.Verbose { log.Printf("Server starting up! - configured to listen on http interface %s and https interface %s", *http_addr, *https_addr) } proxy.NonproxyHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { if req.Host == "" { fmt.Fprintln(w, "Cannot handle requests without Host header, e.g., HTTP 1.0") return } req.URL.Scheme = "http" req.URL.Host = req.Host proxy.ServeHTTP(w, req) }) proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*$"))). HandleConnect(goproxy.AlwaysMitm) proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*:80$"))). HijackConnect(func(req *http.Request, client net.Conn, ctx *goproxy.ProxyCtx) { defer func() { if e := recover(); e != nil { ctx.Logf("error connecting to remote: %v", e) client.Write([]byte("HTTP/1.1 500 Cannot reach destination\r\n\r\n")) } client.Close() }() clientBuf := bufio.NewReadWriter(bufio.NewReader(client), bufio.NewWriter(client)) remote, err := connectDial(proxy, "tcp", req.URL.Host) orPanic(err) remoteBuf := bufio.NewReadWriter(bufio.NewReader(remote), bufio.NewWriter(remote)) for { req, err := http.ReadRequest(clientBuf.Reader) orPanic(err) orPanic(req.Write(remoteBuf)) orPanic(remoteBuf.Flush()) resp, err := http.ReadResponse(remoteBuf.Reader, req) orPanic(err) orPanic(resp.Write(clientBuf.Writer)) orPanic(clientBuf.Flush()) } }) go func() { log.Fatalln(http.ListenAndServe(*http_addr, proxy)) }() // listen to the TLS ClientHello but make it a CONNECT request instead ln, err := net.Listen("tcp", *https_addr) if err != nil { log.Fatalf("Error listening for https connections - %v", err) } for { c, err := ln.Accept() if err != nil { log.Printf("Error accepting new connection - %v", err) continue } go func(c net.Conn) { tlsConn, err := vhost.TLS(c) if err != nil { log.Printf("Error accepting new connection - %v", err) } if tlsConn.Host() == "" { log.Printf("Cannot support non-SNI enabled clients") return } connectReq := &http.Request{ Method: "CONNECT", URL: &url.URL{ Opaque: tlsConn.Host(), Host: net.JoinHostPort(tlsConn.Host(), "443"), }, Host: tlsConn.Host(), Header: make(http.Header), } resp := dumbResponseWriter{tlsConn} proxy.ServeHTTP(resp, connectReq) }(c) } }
func TestRecorder(t *testing.T) { type checkFunc func(*ResponseRecorder) error check := func(fns ...checkFunc) []checkFunc { return fns } hasStatus := func(wantCode int) checkFunc { return func(rec *ResponseRecorder) error { if rec.Code != wantCode { return fmt.Errorf("Status = %d; want %d", rec.Code, wantCode) } return nil } } hasContents := func(want string) checkFunc { return func(rec *ResponseRecorder) error { if rec.Body.String() != want { return fmt.Errorf("wrote = %q; want %q", rec.Body.String(), want) } return nil } } hasFlush := func(want bool) checkFunc { return func(rec *ResponseRecorder) error { if rec.Flushed != want { return fmt.Errorf("Flushed = %v; want %v", rec.Flushed, want) } return nil } } tests := []struct { name string h func(w http.ResponseWriter, r *http.Request) checks []checkFunc }{ { "200 default", func(w http.ResponseWriter, r *http.Request) {}, check(hasStatus(200), hasContents("")), }, { "first code only", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(201) w.WriteHeader(202) w.Write([]byte("hi")) }, check(hasStatus(201), hasContents("hi")), }, { "write sends 200", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hi first")) w.WriteHeader(201) w.WriteHeader(202) }, check(hasStatus(200), hasContents("hi first"), hasFlush(false)), }, { "flush", func(w http.ResponseWriter, r *http.Request) { w.(http.Flusher).Flush() // also sends a 200 w.WriteHeader(201) }, check(hasStatus(200), hasFlush(true)), }, } r, _ := http.NewRequest("GET", "http://foo.com/", nil) for _, tt := range tests { h := http.HandlerFunc(tt.h) rec := NewRecorder() h.ServeHTTP(rec, r) for _, check := range tt.checks { if err := check(rec); err != nil { t.Errorf("%s: %v", tt.name, err) } } } }
func TestReverseProxy(t *testing.T) { const backendResponse = "I am the backend" const backendStatus = 404 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if len(r.TransferEncoding) > 0 { t.Errorf("backend got unexpected TransferEncoding: %v", r.TransferEncoding) } if r.Header.Get("X-Forwarded-For") == "" { t.Errorf("didn't get X-Forwarded-For header") } if c := r.Header.Get("Connection"); c != "" { t.Errorf("handler got Connection header value %q", c) } if c := r.Header.Get("Upgrade"); c != "" { t.Errorf("handler got Upgrade header value %q", c) } if g, e := r.Host, "some-name"; g != e { t.Errorf("backend got Host header %q, want %q", g, e) } w.Header().Set("X-Foo", "bar") w.Header().Set("Upgrade", "foo") w.Header().Set(fakeHopHeader, "foo") w.Header().Add("X-Multi-Value", "foo") w.Header().Add("X-Multi-Value", "bar") http.SetCookie(w, &http.Cookie{Name: "flavor", Value: "chocolateChip"}) w.WriteHeader(backendStatus) w.Write([]byte(backendResponse)) })) defer backend.Close() backendURL, err := url.Parse(backend.URL) if err != nil { t.Fatal(err) } proxyHandler := NewSingleHostReverseProxy(backendURL) frontend := httptest.NewServer(proxyHandler) defer frontend.Close() getReq, _ := http.NewRequest("GET", frontend.URL, nil) getReq.Host = "some-name" getReq.Header.Set("Connection", "close") getReq.Header.Set("Upgrade", "foo") getReq.Close = true res, err := http.DefaultClient.Do(getReq) if err != nil { t.Fatalf("Get: %v", err) } if g, e := res.StatusCode, backendStatus; g != e { t.Errorf("got res.StatusCode %d; expected %d", g, e) } if g, e := res.Header.Get("X-Foo"), "bar"; g != e { t.Errorf("got X-Foo %q; expected %q", g, e) } if c := res.Header.Get(fakeHopHeader); c != "" { t.Errorf("got %s header value %q", fakeHopHeader, c) } if g, e := len(res.Header["X-Multi-Value"]), 2; g != e { t.Errorf("got %d X-Multi-Value header values; expected %d", g, e) } if g, e := len(res.Header["Set-Cookie"]), 1; g != e { t.Fatalf("got %d SetCookies, want %d", g, e) } if cookie := res.Cookies()[0]; cookie.Name != "flavor" { t.Errorf("unexpected cookie %q", cookie.Name) } bodyBytes, _ := ioutil.ReadAll(res.Body) if g, e := string(bodyBytes), backendResponse; g != e { t.Errorf("got body %q; expected %q", g, e) } }