internal/http3: send appropriate error codes in STOP_SENDING frames Now that the quic package supports setting an error code for STOP_SENDING frames, update the HTTP/3 layer accordingly. For golang/go#70914 Change-Id: I0f439c685e4fda883dddeb92fae78ddc6a6a6964 Reviewed-on: https://go-review.googlesource.com/c/net/+/825364 LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-by: David Chase <drchase@google.com>
diff --git a/internal/http3/body.go b/internal/http3/body.go index c80bc75..ed27a83 100644 --- a/internal/http3/body.go +++ b/internal/http3/body.go
@@ -225,7 +225,7 @@ func (r *bodyReader) Close() error { // Unlike the HTTP/1 and HTTP/2 body readers (at the time of this comment being written), // calling Close concurrently with Read will interrupt the read. - r.st.CloseRead() + r.st.CloseRead(uint64(errH3NoError)) // Make sure that any data that has already been written to bodyReader // cannot be read after it has been closed. r.mu.Lock()
diff --git a/internal/http3/conn.go b/internal/http3/conn.go index a90464e..02776d6 100644 --- a/internal/http3/conn.go +++ b/internal/http3/conn.go
@@ -74,12 +74,10 @@ // "Recipients of unknown stream types MUST either abort reading // of the stream or discard incoming data without further processing." // https://www.rfc-editor.org/rfc/rfc9114.html#section-6.2-7 - // - // We should send the H3_STREAM_CREATION_ERROR error code, - // but the quic package currently doesn't allow setting error codes - // for STOP_SENDING frames. - // TODO: Should CloseRead take an error code? - err = nil + err = &streamError{ + code: errH3StreamCreationError, + message: "unknown stream type", + } } if err == io.EOF { err = &connectionError{ @@ -99,13 +97,13 @@ case *connectionError: h.abort(err) case nil: - st.CloseRead() + st.CloseRead(uint64(errH3NoError)) st.CloseWrite() case *streamError: - st.CloseRead() + st.CloseRead(uint64(err.code)) st.Reset(uint64(err.code)) default: - st.CloseRead() + st.CloseRead(uint64(errH3InternalError)) st.Reset(uint64(errH3InternalError)) } }
diff --git a/internal/http3/conn_test.go b/internal/http3/conn_test.go index 91eb404..9cc7305 100644 --- a/internal/http3/conn_test.go +++ b/internal/http3/conn_test.go
@@ -5,8 +5,11 @@ package http3 import ( + "errors" "testing" "testing/synctest" + + "golang.org/x/net/quic" ) // Tests which apply to both client and server connections. @@ -31,8 +34,9 @@ // The endpoint should send a STOP_SENDING for this stream, // but it should not close the connection. synctest.Wait() - if _, err := st.Write([]byte("hello")); err == nil { - t.Fatalf("write to send-only stream with an unknown type succeeded; want error") + wantErr := quic.StreamError(errH3StreamCreationError) + if _, err := st.Write([]byte("hello")); !errors.Is(err, wantErr) { + t.Fatalf("write to send-only stream with an unknown type returned error %v; want %v", err, wantErr) } tc.wantNotClosed("after receiving unknown unidirectional stream type") })
diff --git a/internal/http3/roundtrip.go b/internal/http3/roundtrip.go index 3fd80dc..36d72fb 100644 --- a/internal/http3/roundtrip.go +++ b/internal/http3/roundtrip.go
@@ -53,10 +53,10 @@ case *connectionError: rt.cc.abort(e) case *streamError: - rt.st.CloseRead() + rt.st.CloseRead(uint64(e.code)) rt.st.Reset(uint64(e.code)) default: - rt.st.CloseRead() + rt.st.CloseRead(uint64(errH3NoError)) rt.st.Reset(uint64(errH3NoError)) } }) @@ -285,11 +285,7 @@ // code, and the client MUST NOT discard the response. func reqBodyIgnored(err error) bool { if streamErr, ok := errors.AsType[quic.StreamError](err); ok { - // TODO: the H3_NO_ERROR should arrive in a QUIC STOP_SENDING frame. - // However, the quic package currently only sends code 0 in the - // STOP_SENDING frame due to its API limitation. - // For now, accept 0, in addition to H3_NO_ERROR. - return streamErr == 0 || http3Error(streamErr) == errH3NoError + return http3Error(streamErr) == errH3NoError } return false }
diff --git a/internal/http3/roundtrip_test.go b/internal/http3/roundtrip_test.go index 54dd10f..f5f9a9e 100644 --- a/internal/http3/roundtrip_test.go +++ b/internal/http3/roundtrip_test.go
@@ -413,7 +413,7 @@ // Server stops reading the request because it has enough // information already to construct its response. - st.CloseRead() + st.CloseRead(uint64(errH3NoError)) st.writeHeaders(http.Header{ ":status": {"200"}, })
diff --git a/internal/http3/stream.go b/internal/http3/stream.go index 3529983..b2e7eac 100644 --- a/internal/http3/stream.go +++ b/internal/http3/stream.go
@@ -82,9 +82,9 @@ return st.stream.Close() } -func (st *stream) CloseRead() { +func (st *stream) CloseRead(code uint64) { st.readDeadline.stop() - st.stream.CloseRead() + st.stream.StopSending(code) } func (st *stream) CloseWrite() {