-
Notifications
You must be signed in to change notification settings - Fork 4.4k
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
server: respond correctly to client headers with END_STREAM flag set #3803
Conversation
break | ||
} | ||
} | ||
if err := stream.SendMsg(nil); err == nil { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
.
test/end2end_test.go
Outdated
if err != nil { | ||
break | ||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
test/end2end_test.go
Outdated
st.wantRSTStream(http2.ErrCodeStreamClosed) | ||
<-sentMessage |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
test/end2end_test.go
Outdated
@@ -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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
test/end2end_test.go
Outdated
t.Error(errUnexpectedData) | ||
return errUnexpectedData | ||
t.Error("unexpected data received in func server method") | ||
return nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unnecessary.
There was a problem hiding this comment.
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
test/end2end_test.go
Outdated
errUnexpectedData := errors.New("unexpected data received in func server method") | ||
t.Error(errUnexpectedData) | ||
return errUnexpectedData | ||
t.Error("unexpected data received in func server method") |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a comment.
if err == io.EOF { | ||
break | ||
} | ||
if err != nil { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this 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.
test/end2end_test.go
Outdated
defer func() { | ||
handlerDone <- true | ||
close(handlerDone) | ||
}() |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made this change.
test/end2end_test.go
Outdated
}() | ||
// Block on serverTester receiving RST_STREAM. This ensures server has closed stream before stream.Recv(). |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrapped comments
test/end2end_test.go
Outdated
te := newTest(t, e) | ||
ts := &funcServer{streamingInputCall: func(stream testpb.TestService_StreamingInputCallServer) error { | ||
defer func() { | ||
handlerDone <- true | ||
close(handlerDone) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made this change.
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