~ubuntu-branches/ubuntu/vivid/golang/vivid

« back to all changes in this revision

Viewing changes to src/pkg/net/http/httputil/reverseproxy.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-08-20 14:06:23 UTC
  • mfrom: (14.1.23 saucy-proposed)
  • Revision ID: package-import@ubuntu.com-20130820140623-b414jfxi3m0qkmrq
Tags: 2:1.1.2-2ubuntu1
* Merge from Debian unstable (LP: #1211749, #1202027). Remaining changes:
  - 016-armhf-elf-header.patch: Use correct ELF header for armhf binaries.
  - d/control,control.cross: Update Breaks/Replaces for Ubuntu
    versions to ensure smooth upgrades, regenerate control file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
        "time"
18
18
)
19
19
 
 
20
// onExitFlushLoop is a callback set by tests to detect the state of the
 
21
// flushLoop() goroutine.
 
22
var onExitFlushLoop func()
 
23
 
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
22
26
// client.
77
81
        }
78
82
}
79
83
 
 
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{
 
87
        "Connection",
 
88
        "Keep-Alive",
 
89
        "Proxy-Authenticate",
 
90
        "Proxy-Authorization",
 
91
        "Te", // canonicalized version of "TE"
 
92
        "Trailers",
 
93
        "Transfer-Encoding",
 
94
        "Upgrade",
 
95
}
 
96
 
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
94
111
 
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) != "" {
 
120
                        if !copiedHeaders {
 
121
                                outreq.Header = make(http.Header)
 
122
                                copyHeader(outreq.Header, req.Header)
 
123
                                copiedHeaders = true
 
124
                        }
 
125
                        outreq.Header.Del(h)
 
126
                }
103
127
        }
104
128
 
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
 
135
                }
 
136
                outreq.Header.Set("X-Forwarded-For", clientIP)
107
137
        }
108
138
 
109
139
        res, err := transport.RoundTrip(outreq)
112
142
                rw.WriteHeader(http.StatusInternalServerError)
113
143
                return
114
144
        }
 
145
        defer res.Body.Close()
115
146
 
116
147
        copyHeader(rw.Header(), res.Header)
117
148
 
118
149
        rw.WriteHeader(res.StatusCode)
 
150
        p.copyResponse(rw, res.Body)
 
151
}
119
152
 
120
 
        if res.Body != nil {
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{
 
157
                                dst:     wf,
 
158
                                latency: p.FlushInterval,
 
159
                                done:    make(chan bool),
125
160
                        }
 
161
                        go mlw.flushLoop()
 
162
                        defer mlw.stop()
 
163
                        dst = mlw
126
164
                }
127
 
                io.Copy(dst, res.Body)
128
165
        }
 
166
 
 
167
        io.Copy(dst, src)
129
168
}
130
169
 
131
170
type writeFlusher interface {
137
176
        dst     writeFlusher
138
177
        latency time.Duration
139
178
 
140
 
        lk   sync.Mutex // protects init of done, as well Write + Flush
 
179
        lk   sync.Mutex // protects Write + Flush
141
180
        done chan bool
142
181
}
143
182
 
144
 
func (m *maxLatencyWriter) Write(p []byte) (n int, err error) {
 
183
func (m *maxLatencyWriter) Write(p []byte) (int, error) {
145
184
        m.lk.Lock()
146
185
        defer m.lk.Unlock()
147
 
        if m.done == nil {
148
 
                m.done = make(chan bool)
149
 
                go m.flushLoop()
150
 
        }
151
 
        n, err = m.dst.Write(p)
152
 
        if err != nil {
153
 
                m.done <- true
154
 
        }
155
 
        return
 
186
        return m.dst.Write(p)
156
187
}
157
188
 
158
189
func (m *maxLatencyWriter) flushLoop() {
160
191
        defer t.Stop()
161
192
        for {
162
193
                select {
 
194
                case <-m.done:
 
195
                        if onExitFlushLoop != nil {
 
196
                                onExitFlushLoop()
 
197
                        }
 
198
                        return
163
199
                case <-t.C:
164
200
                        m.lk.Lock()
165
201
                        m.dst.Flush()
166
202
                        m.lk.Unlock()
167
 
                case <-m.done:
168
 
                        return
169
203
                }
170
204
        }
171
 
        panic("unreached")
172
205
}
 
206
 
 
207
func (m *maxLatencyWriter) stop() { m.done <- true }