Service an incoming HTTP connection on conn by sending a request out to the world through dst. If the URL in the request does not contain a scheme, use the specified scheme for the proxied request.
(dst http.RoundTripper, conn net.Conn, outgoingScheme string)
| 183 | // Service an incoming HTTP connection on conn by sending a request out to the world through dst. |
| 184 | // If the URL in the request does not contain a scheme, use the specified scheme for the proxied request. |
| 185 | func proxyHTTPScheme(dst http.RoundTripper, conn net.Conn, outgoingScheme string) { |
| 186 | defer handlePanic() |
| 187 | defer conn.Close() |
| 188 | |
| 189 | verbosef("intercepted a connection to %v", conn.LocalAddr()) |
| 190 | |
| 191 | // wrap the connection with a byte counter |
| 192 | counts := countBytesConn{Conn: conn} |
| 193 | conn = &counts |
| 194 | |
| 195 | // read the HTTP request (TODO: support HTTP/2 using golang.org/x/net/http2) |
| 196 | req, err := http.ReadRequest(bufio.NewReader(conn)) |
| 197 | if err != nil { |
| 198 | errorf("error reading http request over tls server conn: %v, aborting", err) |
| 199 | return |
| 200 | } |
| 201 | defer req.Body.Close() |
| 202 | |
| 203 | verbosef("decoded an HTTP request for %v sent to %v", req.URL, conn.LocalAddr()) |
| 204 | |
| 205 | // the request may contain a relative URL but we need an absolute URL for call to RoundTrip |
| 206 | if req.URL.Host == "" { |
| 207 | req.URL.Host = req.Host |
| 208 | if req.URL.Host == "" { |
| 209 | req.URL.Host = conn.LocalAddr().String() |
| 210 | } |
| 211 | } |
| 212 | if req.URL.Scheme == "" { |
| 213 | req.URL.Scheme = outgoingScheme |
| 214 | } |
| 215 | |
| 216 | // add the IP to which we intercepted packets as a context variable |
| 217 | req = req.WithContext(context.WithValue(req.Context(), dialToContextKey, conn.LocalAddr().String())) |
| 218 | |
| 219 | // capture the request body into memory for inspection later |
| 220 | var reqbody bytes.Buffer |
| 221 | req.Body = TeeReadCloser(req.Body, &reqbody) |
| 222 | |
| 223 | // it seems that harlog assumes that request.GetBody will be non-nil whenever request.Body is non-nil |
| 224 | req.GetBody = func() (io.ReadCloser, error) { return req.Body, nil } |
| 225 | |
| 226 | // do roundtrip to the actual server in the world -- we use RoundTrip here because |
| 227 | // we do not want to follow redirects or accumulate our own cookies |
| 228 | resp, err := dst.RoundTrip(req) |
| 229 | if err != nil { |
| 230 | // error here means the server hostname could not be resolved, or a TCP connection could not be made, |
| 231 | // or TLS could not be negotiated, or something like that |
| 232 | errbody := []byte(err.Error()) |
| 233 | resp = &http.Response{ |
| 234 | Proto: "HTTP/1.1", |
| 235 | ProtoMajor: 1, |
| 236 | ProtoMinor: 1, |
| 237 | Status: http.StatusText(http.StatusBadGateway), |
| 238 | StatusCode: http.StatusBadGateway, |
| 239 | Header: make(http.Header), |
| 240 | ContentLength: int64(len(errbody)), |
| 241 | Body: io.NopCloser(bytes.NewReader(errbody)), |
| 242 | } |
no test coverage detected