How to copy http request's body to a new http request

Hello,

I receive a http request and want to copy its body to a new http request to send to a third party.

Here is my code, is there any other way more efficient?

Best regards

// create the outgoing http request body
httpRequestBody := &bytes.Buffer{}

// copy the incoming request body to the outgoing request body
_, err = io.Copy(httpRequestBody, ctx.Request.Body)
if err != nil {...}
defer ctx.Request.Body.Close()

// create the outgoing http request
httpRequest, err := http.NewRequestWithContext(context.Background(), http.MethodPut, thirdPartyAPIsURL, httpRequestBody)
if err != nil {...}

// send the out going http request
httpResponse, err := http.DefaultClient.Do(httpRequest)
if err != nil {...}
defer httpResponse.Body.Close()

If you receive a http request it may be a http response? In that case I do like this (depending on the desired format):

// read body into to map
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
if err != nil {
	fmt.Println(err.Error())
	return
}
fmt.Println(result)

Sorry for the less explanation, I receive a http request from the client, using its body, I create a new http request and send it to a third party api. I am redirecting the incoming request’s body to the third party.

I updated the code and add more explanation.

When I get a request from the client (GET, POST etc) , I read the body in about the same way.

// Read the JSON data from the request body
body, err := io.ReadAll(r.Body)
if err != nil {
	http.Error(w, "Error reading request body", http.StatusBadRequest)
	return
}

Thank you.

After analysis, I understand that the current method for handling memory was not ideal. Switching to httputil.ReverseProxy resulted in a substantial improvement, decreasing memory usage from gigabytes to kilobytes.

Here is the improved version:

func upload(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	...
	targetURL, err := url.Parse(target)
	if err != nil {...}

	proxy := newProxy(targetURL)
	proxy.ServeHTTP(w, r)
}

func newProxy(target *url.URL) *httputil.ReverseProxy {
	return &httputil.ReverseProxy{
		Rewrite: func(r *httputil.ProxyRequest) {
			r.Out.URL = target
			r.Out.Host = target.Host
		},
	}
}

Here are the benchmark results:

First version:

2023/12/19 17:15:54 INFO starting redirect service address=:8082
2023/12/19 17:16:09 INFO max memory stats alloc="3.8 GB" total_alloc="4.3 GB" sys="4.4 GB" num_gc=11
2023/12/19 17:16:24 INFO max memory stats alloc="5.4 GB" total_alloc="6.4 GB" sys="6.0 GB" num_gc=11
2023/12/19 17:16:27 INFO max memory stats alloc="6.4 GB" total_alloc="8.6 GB" sys="6.8 GB" num_gc=13
2023/12/19 17:16:36 INFO max memory stats alloc="3.8 GB" total_alloc="11 GB" sys="6.8 GB" num_gc=14
2023/12/19 17:16:38 INFO max memory stats alloc="3.8 GB" total_alloc="13 GB" sys="6.8 GB" num_gc=15

Improved version:

2023/12/19 17:16:01 INFO starting proxy service address=:8083
2023/12/19 17:17:21 INFO max memory stats alloc="267 kB" total_alloc="267 kB" sys="8.3 MB" num_gc=0
2023/12/19 17:17:23 INFO max memory stats alloc="362 kB" total_alloc="362 kB" sys="8.3 MB" num_gc=0
2023/12/19 17:17:24 INFO max memory stats alloc="451 kB" total_alloc="451 kB" sys="8.3 MB" num_gc=0
2023/12/19 17:17:46 INFO max memory stats alloc="530 kB" total_alloc="530 kB" sys="8.3 MB" num_gc=0
2023/12/19 17:17:47 INFO max memory stats alloc="610 kB" total_alloc="610 kB" sys="8.3 MB" num_gc=0
2 Likes