20
// onExitFlushLoop is a callback set by tests to detect the state of the
21
// flushLoop() goroutine.
22
var onExitFlushLoop func()
20
24
// ReverseProxy is an HTTP Handler that takes an incoming request and
21
25
// sends it to another server, proxying the response back to the
84
// Hop-by-hop headers. These are removed when sent to the backend.
85
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
86
var hopHeaders = []string{
90
"Proxy-Authorization",
91
"Te", // canonicalized version of "TE"
80
97
func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
81
98
transport := p.Transport
82
99
if transport == nil {
92
109
outreq.ProtoMinor = 1
93
110
outreq.Close = false
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")
112
// Remove hop-by-hop headers to the backend. Especially
113
// important is "Connection" because we want a persistent
114
// connection, regardless of what the client sent to us. This
115
// is modifying the same underlying map from req (shallow
116
// copied above) so we only copy it if necessary.
117
copiedHeaders := false
118
for _, h := range hopHeaders {
119
if outreq.Header.Get(h) != "" {
121
outreq.Header = make(http.Header)
122
copyHeader(outreq.Header, req.Header)
105
if clientIp, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
106
outreq.Header.Set("X-Forwarded-For", clientIp)
129
if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
130
// If we aren't the first proxy retain prior
131
// X-Forwarded-For information as a comma+space
132
// separated list and fold multiple headers into one.
133
if prior, ok := outreq.Header["X-Forwarded-For"]; ok {
134
clientIP = strings.Join(prior, ", ") + ", " + clientIP
136
outreq.Header.Set("X-Forwarded-For", clientIP)
109
139
res, err := transport.RoundTrip(outreq)
112
142
rw.WriteHeader(http.StatusInternalServerError)
145
defer res.Body.Close()
116
147
copyHeader(rw.Header(), res.Header)
118
149
rw.WriteHeader(res.StatusCode)
150
p.copyResponse(rw, res.Body)
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}
153
func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) {
154
if p.FlushInterval != 0 {
155
if wf, ok := dst.(writeFlusher); ok {
156
mlw := &maxLatencyWriter{
158
latency: p.FlushInterval,
159
done: make(chan bool),
127
io.Copy(dst, res.Body)
131
170
type writeFlusher interface {
138
177
latency time.Duration
140
lk sync.Mutex // protects init of done, as well Write + Flush
179
lk sync.Mutex // protects Write + Flush
144
func (m *maxLatencyWriter) Write(p []byte) (n int, err error) {
183
func (m *maxLatencyWriter) Write(p []byte) (int, error) {
146
185
defer m.lk.Unlock()
148
m.done = make(chan bool)
151
n, err = m.dst.Write(p)
186
return m.dst.Write(p)
158
189
func (m *maxLatencyWriter) flushLoop() {