-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
net/http/httputil: ReverseProxy no longer uses io.ReaderFrom/WriterTo #21814
Comments
I suggest either reverting to calling |
Looking at the CL that introduced this, it seems that the intent was to log only read errors: https://go-review.googlesource.com/c/go/+/30692/5/src/net/http/httputil/reverseproxy.go#264. That is why io.CopyBuffer was copied in. Whatever solution we use here should probably retain that property. |
Change https://golang.org/cl/65690 mentions this issue: |
I'm not sure how to resolve this without breaking #16659 or making errors spammy, which @bradfitz objected to. The issue: when WriteTo/ReadFrom returns an error, it's impossible to know if the error came from reading from src or writing to dst. That detail is hidden inside the WriteTo/ReadFrom implementation. Options are:
My inclination is to go with option 1. I don't believe that WriteTo or ReadFrom can use sendfile because resp.Body is never a simple file descriptor (not even when the incoming response is HTTP/1 without chunked encoding). At best WriteTo/ReadFrom might provide slightly more optimal buffering. I would want to see evidence of improved performance from WriteTo/ReadFrom over Read/Write before actually moving forward. |
@tombergan Its more or less how we ended up with the current implementation. Since the http package doesn't provide any optimized ReadFrom/WriteTo, I would be ok with option 3. |
Actually, net/http's ResponseWriter implements ReaderFrom, so I think option 3 would break #16659. To clarify: the WaitinfForInfo state is waiting for some justification that WriteTo/ReadFrom provide an actual (measurable) performance gain. |
Timed out in state WaitingForInfo. Closing. (I am just a bot, though. Please speak up if this is a mistake or you have the requested information.) |
Now that we have splice being used it's unfortunate that it's not used in ReverseProxy. I vote for using io.CopyBuffer here so there isn't a need to consistantly keep copying functionality over and any future improvements in the io package will benefit the ReverseProxy. We currently log over 16,000 "http: proxy error: context canceled" errors per hour but I haven't seen any of the "read error" logs. Now that we have ErrorHandler if a user wants to filter out specific spammy errors (like the one above) they can do so in their own ErrorHandler. @fraenkel, what are your thoughts? |
@fastest963 I understand that you have never seen the issue but the reason why the code was changed was because we did. It was far too common to have downstream clients disappear and have no knowledge in the proxy as to who was at fault. Do you have any data that shows using splice provides value? Which is what @tombergan was asking for above. |
@fraenkel I was saying that if you added your own ErrorHandler you'd be able to capture both writing and reading errors and handle them however you like. You can ignore write errors and log reader errors. So it sounds like 2 is the best option since by default it will log all errors but you can handle it however you like if you want. |
@fastest963 That is the problem, you cannot distinguish between read and write errors, its all errors. In #21814 (comment), the point was to ignore write errors. If I could have done that without splitting CopyBuffer apart I would have. |
@fraenkel I would argue that you might want to care about some write errors. I don't know the history of why you only care about read errors but you could do something like:
What I'm proposing (#2 in @tombergan's comment) is that using Additionally, I have not tested splice yet but the fact that ReverseProxy has its own "gimped" copy of |
I did add a benchmark to see what the improvement might be. Here are the results based on fixed size responses of 100, 1000 and 8000 bytes. But as expected there is no way to detect read errors.
|
@fraenkel thanks for the benchmarks those definitely are useful for context. What do you mean by:
It has to be an error type, right? Can you print out the type? Is it a *net.OpError? When you say:
What was the change you made? Did you take the error and call getErrorHandler if there was an error from |
@fastest963 You are correct, I forgot to call logf. But here is the actual error that comes back from io.CopyBuffer, &errors.errorString{s:"unexpected EOF"}. Is that a read or write error? |
@fraenkel looks like that's an io.ErrUnexpectedEOF which I would assume is from a read. The docs:
Though I'm not sure how you got that considering:
If the error is not |
Digging a bit deeper. Splice will never be used in reverseproxy. Splice requires a TCP conn on both sides which never happens. At best you see ReadFrom or WriteTo come into play which is what is happening. Again this is part of the issue, there are 3 possible paths, all which provide different results. |
Before go 1.8
httputil.ReverseProxy
would useio.CopyBuffer
which would useio.ReaderFrom
orio.WriterTo
if possible to avoid allocation. In fixing #16659 it removed usingio.CopyBuffer
and implements its own without the optimisations ofio.CopyBuffer
to add an extra log.Please answer these questions before submitting your issue. Thanks!
What did you do?
https://play.golang.org/p/4GjT-hwI3t
What did you expect to see?
WriteTo
What did you see instead?
Read
System details
The text was updated successfully, but these errors were encountered: