newProxy builds the reverse proxy. The upstream URL carries the canonical host that requests are forwarded to and that responses are rewritten from, while dialAddr is the address actually dialled, so the gateway keeps working once the canonical host is blackholed.
(gatewayHost string, upstream *url.URL, dialAddr string, rec *recorder)
| 147 | // while dialAddr is the address actually dialled, so the gateway keeps working |
| 148 | // once the canonical host is blackholed. |
| 149 | func newProxy(gatewayHost string, upstream *url.URL, dialAddr string, rec *recorder) *httputil.ReverseProxy { |
| 150 | transport := &http.Transport{ |
| 151 | DialContext: func(ctx context.Context, network, _ string) (net.Conn, error) { |
| 152 | var dialer net.Dialer |
| 153 | return dialer.DialContext(ctx, network, dialAddr) |
| 154 | }, |
| 155 | TLSClientConfig: &tls.Config{ |
| 156 | ServerName: upstream.Host, |
| 157 | MinVersion: tls.VersionTLS12, |
| 158 | }, |
| 159 | ForceAttemptHTTP2: true, |
| 160 | } |
| 161 | |
| 162 | return &httputil.ReverseProxy{ |
| 163 | Transport: transport, |
| 164 | Rewrite: func(pr *httputil.ProxyRequest) { |
| 165 | pr.Out.URL.Scheme = upstream.Scheme |
| 166 | pr.Out.URL.Host = upstream.Host |
| 167 | pr.Out.Host = upstream.Host |
| 168 | // The response has to be readable for rewriting, and asking for an |
| 169 | // identity encoding is cheaper than decompressing it again. |
| 170 | pr.Out.Header.Set("Accept-Encoding", "identity") |
| 171 | }, |
| 172 | ModifyResponse: func(resp *http.Response) error { |
| 173 | if entry, ok := resp.Request.Context().Value(recordKey{}).(*record); ok { |
| 174 | entry.Status = resp.StatusCode |
| 175 | } |
| 176 | return rewriteResponse(resp, upstream.Host, gatewayHost) |
| 177 | }, |
| 178 | ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) { |
| 179 | if entry, ok := r.Context().Value(recordKey{}).(*record); ok { |
| 180 | entry.Status = http.StatusBadGateway |
| 181 | entry.Error = err.Error() |
| 182 | } |
| 183 | rec.Logf("upstream error for %s %s: %v", r.Method, r.URL.RequestURI(), err) |
| 184 | w.WriteHeader(http.StatusBadGateway) |
| 185 | fmt.Fprintf(w, "gateway: upstream error: %v\n", err) |
| 186 | }, |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | // rewriteResponse replaces the upstream host with the gateway host everywhere |
| 191 | // it appears, so that clients following URLs the API handed them stay on the |