Slow http server for huge PUT commands

Hello,
I write a proxy on go, I open server and make ListenAndServe with a handler.
Then I upload a big PUT command and I have 2 problems there:

  1. My handler function get called only several seconds after I send the PUT command.
  2. When I send the request to other Url, it takes much more time than if I would send this command to this Url in the first place.

Server:

http.HandleFunc("/", requestHandlerPipe)
http.ListenAndServe(":8080", nil)

func requestHandlerPipe(rw http.ResponseWriter, request *http.Request) {
    // it takes several seconds only to get here
}

Second problem inside the handler:

new_request, _ := http.NewRequest(method, url_new, request.Body)
new_request.Header = request.Header
response, err := c.Do(new_request)   // this line can take 20 seconds while sending and getting response directly takes 12 second

//copy response to ResponseWriter - this part seems pretty fast but ugly.
//copyHeaders(reqnew.Header, ans.Header) this didnt work for some reason so I did it manually
//if this part also can be implemented in a better way, I would like to know this way

	 for hkey, values := range ans.Header {
 		for _, value := range values {
 			rw.Header().Add(hkey, value)
 		}
 	}
 	rw.WriteHeader(ans.StatusCode)
	defer ans.Body.Close()
	nr, err := io.Copy(rw, ans.Body)

This is a problem only when I work on fast network. If I use slow network my proxy don’t add any latency.

Any help will be appreciated.

P.S. I tried fasthttp, the second problem disappeared there but the first problem was even worse because it tries to read the whole body of the request.

Thank you

Hey @etyk, have you tried using either of these?
httputil.ReverseProxy, e.g. https://play.golang.org/p/YqeE7vzPDo
or
httputil.NewSingleHostReverseProxy, e.g. https://play.golang.org/p/ZCiiu3rkwT

Hi Benjamin,
I am not using those because I need to make manipulations on both the request headers and the response (actually every request will be translated into several other requests, one of them will contain the full body of the original request). Currently I build a simple proxy only to test the performance.
If I understand correctly those solutions won’t give me enough freedom to do what I need.

By the way, sending small objects < 1K I don’t have any penalties, but starting at ~100K latency increases and continue increase the bigger my PUT command is.

Thank you, and any help will be appreciated.

This is really interesting. How big is your PUT? Also, are you sending this over the network or localhost?

I start to feel latency at 100K (the bigger input the bigger latency relatively to “not using my proxy”)

I send PUT in localhost, then redirect it over the network and return the response back.
I compare the performance to sending over the network directly.

I run the program through IntelliJ (Run no Debug mode) - if this is relevant somehow.
Sending using Python Request library.

Any help will be very appreciated.

Hey again @etyk,

Not exactly sure what you are actually trying to do or if this even useful, but here’s how you can modify the response while using an httputil.ReveseProxy object: https://play.golang.org/p/Y5QIgRhfTX.

I’m also not sure if the latency your seeing is even related to your code or possibly something else but you may as well at least see if some latency is reduced by using an httputil.ReverseProxy object and compare your results.

I’m not sure what you do to the request, but you’re buffering the response in it’s entirety before responding to the client. If the response is large that may be relevant.

You probably only need the final io.Copy.

Also in general you’ll be adding latency when the request goes A -> B -> C and back rather than directly A -> C. That can obviously affect the performance.

1 Like

Hey @calmh, I actually think the ReadAll body slurp is because he mentioned that he’s actually trying to modify the response whilst proxying it.

Hi, here was my mistake, I already use only io.Copy, I uncommented out ReadAll + Write by mistake.
I will Edit the question now.

Hi,
I tried this solution as well, but it is still too slow.
Using ReverseProxy as is: 7 seconds
Using ReverseProxy that I give it Transport (that uses Default transport): 11 seconds
Direct call without “me” in the middle: 1 second

By the way, this is a problem only if my network is fast.
If I use slow network - my proxy don’t add any latency to the process.

Hmm that’s interesting. I guess since you haven’t really shown any of your code where your actual processing is happening and since I’m far from a pro at Go I probably can’t comment too much on it.

I do wonder though if your latency in fast network speeds is due to the fact that when you are running on a slow network, you are limited to how many concurrent connections are available at one time to process simultaneously so therefore you can process the uploads with no issues, but with the faster networking speeds you have many more uploads being received simultaneously and maybe the latency is actually the upload processing taking a long time and not the actual proxying.

Not too sure otherwise with the information I’ve seen up to this point. Sorry I couldn’t really help ya.

Yes, this is my theory too. Very fast upload that my proxy is not able to stream out in proper speed.

About the code, I have a version with ReverseProxy that is still 6 times slower than the direct and the whole code is:

func main()  {
	proxy := &httputil.ReverseProxy{
		Director:  director,
	}

	server := http.Server{
		Addr:    ":8080",
		Handler: proxy,
	}
	server.ListenAndServe()
}

func director(r *http.Request){
	url_new := UrlToCleversafeServer + r.URL.Path
	r.URL, _ = url.Parse(url_new)
}

this is the whole code. It does no manipulations on the response or the request, only redirecting it to other server.
And it is still too slow.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.