-
-
Notifications
You must be signed in to change notification settings - Fork 565
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
Shouldn't body be closed by decoder ? #3329
Comments
Interesting question :) The Go http server should take care of closing the body but that will happen after the handler returns. Can you point me to where the requirement for the body to be closed before the context can be cancelled comes from? |
I am not sure why it is like that, but if we don't close the body, then we don't always get notified of the client cancelling on his side. It is not always the case, if just merely testing by directly connecting on the service with curl, it does work as expected (no need to close the body), but when we are in actual deployment behind a routing proxy (traefik in our case), not only we don't get notified, but even worst there seem to be leak of connections. |
Thanks for the explanation, one more question: does implementing a custom decoder that closes the body help? This would be a good test to validate the approach (might also be a better workaround than using |
Thanks for the suggestion, bizarrely I tried and didn't manage to fix the issue with a custom decoder. Maybe there's something I didn't completely understand, the custom decoder is actually a custom decoder factory, it returns a decoder for a given request, I don't fully understand why it is done like that, does it mean that the Decode method could be called more than once for a given decoder ? If so I should not close the body at the end of Decode (in general), because this could break subsequent calls. Here is what I tried : type BodyCloser struct {
request *http.Request
lower goahttp.Decoder
}
func (d *BodyCloser) Decode(v any) error {
err := d.lower.Decode(v)
d.request.Body.Close()
return err
}
func NewBodyCloserRequestDecoder(r *http.Request) goahttp.Decoder {
return &BodyCloser{r, goahttp.RequestDecoder(r)}
} To complete the explanation, this is actually this issue we hit : golang/go#23262 It is a pretty old issue, while there's a working solution in closing the body, the issue is still open, so I'm not sure what it means, maybe the golang team consider this is not sufficiently documented ? Anyway, what they are saying in that issue is that if you don't read the body, then you should close it. |
Hmm the above code looks good to me. The reason the decoder is a factory and not a specific decoder is so that the code can inspect the request |
I see that makes sense. Regarding fixing by using a custom decoder, it does not work, because there's nothing to decode in the body. That's what triggers the bug in the first place : if the body is not read, you need to close it (or you don't receive cancel), that's what is described in golang/go#23262. But the code generated by goa does not call the decoder in this case, so I can't implement the fix there. |
I verified, if I add an optional request parameter in the body in my design, then generated code use the decoder, which will read the body, but in this case the bug is not triggered, even without the custom decoder, because the body was read. So maybe what goa should do (the generated code I guess) is when it does not use the decoder, close the body instead. |
That makes sense, would you care putting together a PR? Adding a |
I would be happy to propose something. But I don't know what ... It turns out that when using nginx as proxy, closing the body does not work. I need to make sure the body is read. For example by adding some dummy parameter, so generated code will call the decoder, or even again using SkipRequestBodyEncodeDecode and calling So, I guess for now I will stick to this workaround, and since I can also workaround #3328 , I am not blocked. (note : reading just partially is not enough, you need to read up to EOF, in my case there was two bytes in the body, reading just one byte did not fix, but reading 100 (which returned only 2 + EOF) did. The two bytes were '{' and '}' ) |
OK I thought the problem was that Goa wasn't closing the request Body when it is empty. The code I linked above is where the code checks whether the decoder should be called and I was suggesting adding a |
Well, I also thought that, but after digging deeper, it is not clear, since it does not resolve the issue when nginx is used as reverse proxy. What seems to fix the issue is to make sure you consume the body fully. Normally the body should be almost empty (for example in our case it contains an empty json dict {} ). I'm a bit resilient to propose this workaround generically because it's not as straightforward as just closing the body. Basically, this issue on golang side golang/go#23262 is still open. I am not sure what it means, do they plan to change the behaviour one day ? Or improve the doc ? |
Thank you, that makes sense. It does look like the need for reading the request body before a request can be closed is a hard limitation - good to know! Closing this issue since there isn't anything Goa can do to change this behavior. |
I want to implement a cancellable post method. However for context.Done() to be notified, the body has to be previously closed, at least in some cases.
When an endpoint method is called, just after the decoder was called, unless SkipRequestBodyEncodeDecode is used, the body can't be accessed anymore, it would thus make sense to call
Close()
on it before calling the endpoint method.Currently, to implement a cancellable method, I need to use SkipRequestBodyEncodeDecode and explicitly close the body (which I can now access, even though I didn't need it). But this is incompatible with using authentification (I opened another issue regarding this), and seems like a workaround rather than a clean solution, since I didn't need the body. Beside, not closing the body seems to lead to some resource leaks in some cases (might be related to golang/go#43966), so closing it in all cases seems to be safer.
EDIT: To clarify, this issue happens when the body of the request is not read (no parameters were declared there so goa doesn't bother decoding the body). If the body is read, everything works normally without needing to close the body.
The text was updated successfully, but these errors were encountered: