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.
5 // HTTP reverse proxy handler
20 // ReverseProxy is an HTTP Handler that takes an incoming request and
21 // sends it to another server, proxying the response back to the
23 type ReverseProxy struct {
24 // Director must be a function which modifies
25 // the request into a new request to be sent
26 // using Transport. Its response is then copied
27 // back to the original client unmodified.
28 Director func(*http.Request)
30 // The transport used to perform proxy requests.
31 // If nil, http.DefaultTransport is used.
32 Transport http.RoundTripper
34 // FlushInterval specifies the flush interval, in
35 // nanoseconds, to flush to the client while
36 // coping the response body.
37 // If zero, no periodic flushing is done.
41 func singleJoiningSlash(a, b string) string {
42 aslash := strings.HasSuffix(a, "/")
43 bslash := strings.HasPrefix(b, "/")
45 case aslash && bslash:
47 case !aslash && !bslash:
53 // NewSingleHostReverseProxy returns a new ReverseProxy that rewrites
54 // URLs to the scheme, host, and base path provided in target. If the
55 // target's path is "/base" and the incoming request was for "/dir",
56 // the target request will be for /base/dir.
57 func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy {
58 director := func(req *http.Request) {
59 req.URL.Scheme = target.Scheme
60 req.URL.Host = target.Host
61 req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
62 if q := req.URL.RawQuery; q != "" {
63 req.URL.RawPath = req.URL.Path + "?" + q
65 req.URL.RawPath = req.URL.Path
67 req.URL.RawQuery = target.RawQuery
69 return &ReverseProxy{Director: director}
72 func copyHeader(dst, src http.Header) {
73 for k, vv := range src {
74 for _, v := range vv {
80 func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
81 transport := p.Transport
83 transport = http.DefaultTransport
86 outreq := new(http.Request)
87 *outreq = *req // includes shallow copies of maps, but okay
90 outreq.Proto = "HTTP/1.1"
95 // Remove the connection header to the backend. We want a
96 // persistent connection, regardless of what the client sent
97 // to us. This is modifying the same underlying map from req
98 // (shallow copied above) so we only copy it if necessary.
99 if outreq.Header.Get("Connection") != "" {
100 outreq.Header = make(http.Header)
101 copyHeader(outreq.Header, req.Header)
102 outreq.Header.Del("Connection")
105 if clientIp, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
106 outreq.Header.Set("X-Forwarded-For", clientIp)
109 res, err := transport.RoundTrip(outreq)
111 log.Printf("http: proxy error: %v", err)
112 rw.WriteHeader(http.StatusInternalServerError)
116 copyHeader(rw.Header(), res.Header)
118 rw.WriteHeader(res.StatusCode)
121 var dst io.Writer = rw
122 if p.FlushInterval != 0 {
123 if wf, ok := rw.(writeFlusher); ok {
124 dst = &maxLatencyWriter{dst: wf, latency: p.FlushInterval}
127 io.Copy(dst, res.Body)
131 type writeFlusher interface {
136 type maxLatencyWriter struct {
138 latency int64 // nanos
140 lk sync.Mutex // protects init of done, as well Write + Flush
144 func (m *maxLatencyWriter) Write(p []byte) (n int, err error) {
148 m.done = make(chan bool)
151 n, err = m.dst.Write(p)
158 func (m *maxLatencyWriter) flushLoop() {
159 t := time.NewTicker(m.latency)