Skip to content
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

Fix multiple header writes for connect unary on error #726

Merged
merged 2 commits into from
Apr 17, 2024

Conversation

emcfarlane
Copy link
Contributor

On error a connect unary handler will call Close(err) to send the final error. For non-nil errors this will always attempt to encode the header status by calling WriteHeader. If the body has already been written this triggers the following log via http.Server:

http: superfluous response.WriteHeader call from connectrpc.com/connect.(*connectUnaryHandlerConn).Close (protocol_connect.go:743)

The common case for this superfluous log is when a message send is interrupted, due to a context cancel or other write error, and the error is then attempting to re-encode the headers and body.

This PR now moves the wroteBody check to a wroteHeader check on the unmarshaller. When any payload is sent, including a nil field for header only, we now stop attempting to encode any following errors, as it would be superfluous.

Copy link
Member

@jhump jhump left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

if err := conn.Receive(&msg2); err == nil {
return nil, NewError(CodeUnimplemented, fmt.Errorf("unary %s has multiple messages", what))
} else if err != nil && !errors.Is(err, io.EOF) {
if err := conn.Receive(&msg2); !errors.Is(err, io.EOF) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated to the change but the error check err != nil was superfluous, so simplified to the happy path first.

@@ -757,7 +755,7 @@ func (hc *connectUnaryHandlerConn) getHTTPMethod() string {
return hc.request.Method
}

func (hc *connectUnaryHandlerConn) writeResponseHeader(err error) {
func (hc *connectUnaryHandlerConn) mergeResponseHeader(err error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to mergeResponseHeaders as this doesn't trigger a write.

@emcfarlane emcfarlane marked this pull request as ready for review April 17, 2024 19:52
// If the handler received a GET request and the resource hasn't changed,
// return a 304.
if len(hc.peer.Query) > 0 && IsNotModifiedError(err) {
hc.responseWriter.WriteHeader(http.StatusNotModified)
return hc.request.Body.Close()
}
}
if err == nil {
if err == nil || hc.marshaler.wroteHeader {
Copy link
Contributor Author

@emcfarlane emcfarlane Apr 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could error here to report to the Close that the error wasn't encoded. I've left for now, as a best effort solution.

Copy link
Member

@akshayjshah akshayjshah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TY for the quick fix!

@emcfarlane emcfarlane merged commit fd9d60a into main Apr 17, 2024
13 checks passed
@emcfarlane emcfarlane deleted the ed/fixMultiHeaderWrite branch April 17, 2024 20:18
@jhump jhump mentioned this pull request Apr 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants