ServeHTTPContext serves HTTP with a context
( ctx context.Context, rw http.ResponseWriter, req *http.Request, )
| 118 | |
| 119 | // ServeHTTPContext serves HTTP with a context |
| 120 | func (p *ReverseProxy) ServeHTTPContext( |
| 121 | ctx context.Context, rw http.ResponseWriter, req *http.Request, |
| 122 | ) { |
| 123 | log := termlog.FromContext(ctx) |
| 124 | transport := p.Transport |
| 125 | if transport == nil { |
| 126 | transport = http.DefaultTransport |
| 127 | } |
| 128 | |
| 129 | outreq := new(http.Request) |
| 130 | *outreq = *req // includes shallow copies of maps, but okay |
| 131 | |
| 132 | p.Director(outreq) |
| 133 | outreq.Proto = "HTTP/1.1" |
| 134 | outreq.ProtoMajor = 1 |
| 135 | outreq.ProtoMinor = 1 |
| 136 | outreq.Close = false |
| 137 | |
| 138 | // Remove hop-by-hop headers to the backend. Especially |
| 139 | // important is "Connection" because we want a persistent |
| 140 | // connection, regardless of what the client sent to us. This |
| 141 | // is modifying the same underlying map from req (shallow |
| 142 | // copied above) so we only copy it if necessary. |
| 143 | copiedHeaders := false |
| 144 | for _, h := range hopHeaders { |
| 145 | if outreq.Header.Get(h) != "" { |
| 146 | if !copiedHeaders { |
| 147 | outreq.Header = make(http.Header) |
| 148 | copyHeader(outreq.Header, req.Header) |
| 149 | copiedHeaders = true |
| 150 | } |
| 151 | outreq.Header.Del(h) |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { |
| 156 | // If we aren't the first proxy retain prior |
| 157 | // X-Forwarded-For information as a comma+space |
| 158 | // separated list and fold multiple headers into one. |
| 159 | if prior, ok := outreq.Header["X-Forwarded-For"]; ok { |
| 160 | clientIP = strings.Join(prior, ", ") + ", " + clientIP |
| 161 | } |
| 162 | outreq.Header.Set("X-Forwarded-For", clientIP) |
| 163 | } |
| 164 | |
| 165 | res, err := transport.RoundTrip(outreq) |
| 166 | if err != nil { |
| 167 | log.Shout("reverse proxy error: %v", err) |
| 168 | rw.WriteHeader(http.StatusInternalServerError) |
| 169 | return |
| 170 | } |
| 171 | defer res.Body.Close() |
| 172 | if req.ContentLength > 0 { |
| 173 | log.Say(fmt.Sprintf("%s uploaded", humanize.Bytes(uint64(req.ContentLength)))) |
| 174 | } |
| 175 | |
| 176 | inject, err := p.Inject.Sniff(res.Body, res.Header.Get("Content-Type")) |
| 177 | if err != nil { |
no test coverage detected