Go http, send incoming http.request to an other server using client.Do

You need to copy the values you want into a new request. Since this is very similar to what a reverse proxy does, you may want to look at what "net/http/httputil" does for ReverseProxy.

Create a new request, and copy only the parts of the request you want to send to the next server. You will also need to read and buffer the request body if you intend to use it both places:

func handler(w http.ResponseWriter, req *http.Request) {
    // we need to buffer the body if we want to read it here and send it
    // in the request. 
    body, err := ioutil.ReadAll(req.Body)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    // you can reassign the body if you need to parse it as multipart
    req.Body = ioutil.NopCloser(bytes.NewReader(body))

    // create a new url from the raw RequestURI sent by the client
    url := fmt.Sprintf("%s://%s%s", proxyScheme, proxyHost, req.RequestURI)

    proxyReq, err := http.NewRequest(req.Method, url, bytes.NewReader(body))

    // We may want to filter some headers, otherwise we could just use a shallow copy
    // proxyReq.Header = req.Header
    proxyReq.Header = make(http.Header)
    for h, val := range req.Header {
        proxyReq.Header[h] = val
    }

    resp, err := httpClient.Do(proxyReq)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadGateway)
        return
    }
    defer resp.Body.Close()

    // legacy code
}

In my experience, the easiest way to achieve this was to simply create a new request and copy all request attributes that you need into the new request object:

func(rw http.ResponseWriter, req *http.Request) {
    url := req.URL
    url.Host = "v2ofdoom.local:8081"

    proxyReq, err := http.NewRequest(req.Method, url.String(), req.Body)
    if err != nil {
        // handle error
    }

    proxyReq.Header.Set("Host", req.Host)
    proxyReq.Header.Set("X-Forwarded-For", req.RemoteAddr)

    for header, values := range req.Header {
        for _, value := range values {
            proxyReq.Header.Add(header, value)
        }
    }

    client := &http.Client{}
    proxyRes, err := client.Do(proxyReq)

    // and so on...

This approach has the benefit of not modifying the original request object (maybe your handler function or any middleware functions that are living in your stack still need the original object?).

Tags:

Go