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

server: respond correctly to client headers with END_STREAM flag set #3803

Merged
merged 13 commits into from
Aug 21, 2020
Merged

server: respond correctly to client headers with END_STREAM flag set #3803

merged 13 commits into from
Aug 21, 2020

Conversation

GarrettGutierrez1
Copy link
Contributor

@GarrettGutierrez1 GarrettGutierrez1 commented Aug 10, 2020

Added logic in transport layer for appropriately handling an end stream flag set in a stream's initial http2 header frame, as well as for handling data frames on the same stream after such a frame. Added a test for testing this logic.

Fixes #2867

@GarrettGutierrez1 GarrettGutierrez1 added this to the 1.32 Release milestone Aug 10, 2020
internal/transport/http2_server.go Outdated Show resolved Hide resolved
test/servertester.go Outdated Show resolved Hide resolved
test/end2end_test.go Show resolved Hide resolved
test/end2end_test.go Show resolved Hide resolved
break
}
}
if err := stream.SendMsg(nil); err == nil {
Copy link
Member

Choose a reason for hiding this comment

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

Let's test the actual error we expect to get from the stream here instead of "any error is fine".

I have a feeling it will be an RPC status error now, but maybe that's not right (#3575). We can just expect the wrong thing for now and leave a comment that it will need to be updated when #3575 is fixed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added an error check here. The error is an RPC status error Internal.

Comment on lines 4689 to 4691
if err != nil {
break
}
Copy link
Member

Choose a reason for hiding this comment

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

What is err when this happens? Maybe we should change that? I'm guessing this is a race between Recv returning before/after the illegal data frame is written.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The err for this when logged reads as rpc error: code = Canceled desc = context canceled. Sometimes Recv returns an err and sometimes the loop breaks because of err == io.EOF. That does sound like a race. Most of the time Recv returns the canceled desc err.

Comment on lines 4708 to 4709
st.wantRSTStream(http2.ErrCodeStreamClosed)
<-sentMessage
Copy link
Member

Choose a reason for hiding this comment

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

Without this synchronization you wouldn't see the RST_STREAM at all sometimes? That sounds wrong. The server should still send a RST_STREAM even after the stream is terminated on the server's side.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Without the synchronization the handler would sometimes not encounter an error. I think this was because the goroutine for the test would finish before the goroutine for the handler would encounter an error condition.

@@ -4646,13 +4646,19 @@ func (s) TestClientInitialHeaderEndStream(t *testing.T) {
}

func testClientInitialHeaderEndStream(t *testing.T, e env) {
// To ensure RST_STREAM is sent for illegal data write and not normal stream close.
frameCheckingDone := make(chan bool, 1)
Copy link
Member

Choose a reason for hiding this comment

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

The typical way to implement this pattern is to make it a: chan struct{} (with no buffer) and close the channel when you want to indicate the thing happened.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Migrated to the chan struct{} pattern.

t.Error(errUnexpectedData)
return errUnexpectedData
t.Error("unexpected data received in func server method")
return nil
Copy link
Member

Choose a reason for hiding this comment

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

This is unnecessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed the return nil in the if block

errUnexpectedData := errors.New("unexpected data received in func server method")
t.Error(errUnexpectedData)
return errUnexpectedData
t.Error("unexpected data received in func server method")
Copy link
Member

Choose a reason for hiding this comment

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

Print the data? It might help with debugging if it ever happens.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a print of the data.

defer func() {
handlerDone <- true
}()
<-frameCheckingDone
Copy link
Member

Choose a reason for hiding this comment

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

Comment here to explain why we need to block until the server has sent the RST_STREAM. And in the test below, too

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a comment.

Comment on lines +4704 to +4707
if err == io.EOF {
break
}
if err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

Is this call not reliably failing now that we know it has sent the RST_STREAM?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's correct. It always returns with err != nil on the first call but sometimes it is io.EOF and sometimes it is a cancelled error.

Copy link
Member

@dfawley dfawley left a comment

Choose a reason for hiding this comment

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

Looks good, just a couple minor things.

Comment on lines 4655 to 4657
defer func() {
handlerDone <- true
close(handlerDone)
}()
Copy link
Member

Choose a reason for hiding this comment

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

Just defer close(handlerDone) now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Made this change.

}()
// Block on serverTester receiving RST_STREAM. This ensures server has closed stream before stream.Recv().
Copy link
Member

Choose a reason for hiding this comment

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

Please make sure to wrap comments at 80 columns for readability.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wrapped comments

te := newTest(t, e)
ts := &funcServer{streamingInputCall: func(stream testpb.TestService_StreamingInputCallServer) error {
defer func() {
handlerDone <- true
close(handlerDone)
Copy link
Member

Choose a reason for hiding this comment

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

Same

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Made this change.

@GarrettGutierrez1 GarrettGutierrez1 merged commit b9bc8e7 into grpc:master Aug 21, 2020
@dfawley dfawley changed the title End stream flag bugfix server: respond correctly to client headers with END_STREAM flag set Aug 21, 2020
@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 25, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Server does not respect END_STREAM flag in client HEADERS
2 participants