Do not pretend receiving FIN when receiving trailers in QUIC v99. In Google QUIC, trailing HEADERS must have FIN bit. In IETF QUIC, HTTP/3 frames do not have flags, so trailing HEADERS will never have FIN. This CL makes QuicSpdyStream stop faking a FIN bit. In non-v99 QUIC, |fin| must be true at the end of OnTrailingHeadersComplete(), otherwise the connection would have been closed in line 568. In v99 QUIC, |fin| is false in production, when OnStreamHeaderList() is called from OnHeadersFrameEnd(). However, a large number of tests call OnStreamHeaderList() with fin = true, so this needs to be supported for the time being. See linked bug. This is prework for https://crbug.com/978733. I do not want QuicSpdyStream to close the connection when trailers arrive, so that it can signal an error if other frames are received afterwards. Also, according to https://quicwg.org/base-drafts/draft-ietf-quic-http.html#request-response, PUSH_PROMISE frames can arrive after trailers, so the stream really should remain open. This CL also fixes descriptions of some QuicSpdyStream methods related to FIN flag. gfe-relnote: n/a, QUIC v99-only change. PiperOrigin-RevId: 255681867 Change-Id: I33928191e35d601514a0be5b18b8aa5ff389c831
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc index f02eaeb..e3d9357 100644 --- a/quic/core/http/quic_spdy_stream.cc +++ b/quic/core/http/quic_spdy_stream.cc
@@ -589,12 +589,13 @@ return; } trailers_decompressed_ = true; - const QuicStreamOffset offset = - VersionUsesQpack(spdy_session_->connection()->transport_version()) - ? flow_controller()->highest_received_byte_offset() - : final_byte_offset; - OnStreamFrame( - QuicStreamFrame(id(), /* fin = */ true, offset, QuicStringPiece())); + if (fin) { + const QuicStreamOffset offset = + VersionUsesQpack(transport_version()) + ? flow_controller()->highest_received_byte_offset() + : final_byte_offset; + OnStreamFrame(QuicStreamFrame(id(), fin, offset, QuicStringPiece())); + } } void QuicSpdyStream::OnPriorityFrame(SpdyPriority priority) {
diff --git a/quic/core/http/quic_spdy_stream.h b/quic/core/http/quic_spdy_stream.h index b6b46fe..1a5c1b6 100644 --- a/quic/core/http/quic_spdy_stream.h +++ b/quic/core/http/quic_spdy_stream.h
@@ -168,7 +168,8 @@ static bool ParseHeaderStatusCode(const spdy::SpdyHeaderBlock& header, int* status_code); - // Returns true when all data has been read from the peer, including the fin. + // Returns true when all data from the peer has been read and consumed, + // including the fin. bool IsDoneReading() const; bool HasBytesToRead() const; @@ -191,8 +192,8 @@ // Returns true if headers have been fully read and consumed. bool FinishedReadingHeaders() const; - // Returns true if trailers have been fully read and consumed, or FIN has - // been received and there are no trailers. + // Returns true if FIN has been received and either trailers have been fully + // read and consumed or there are no trailers. bool FinishedReadingTrailers() const; // Called when owning session is getting deleted to avoid subsequent
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc index 2cb4bce..bab9228 100644 --- a/quic/core/http/quic_spdy_stream_test.cc +++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -1752,7 +1752,7 @@ EXPECT_TRUE(stream_->trailers_decompressed()); EXPECT_EQ(trailers_block, stream_->received_trailers()); - EXPECT_FALSE(stream_->IsDoneReading()); + EXPECT_TRUE(stream_->HasBytesToRead()); // Consume data. char buffer[2048]; @@ -1762,7 +1762,7 @@ size_t bytes_read = stream_->Readv(&vec, 1); EXPECT_EQ(kDataFramePayload, QuicStringPiece(buffer, bytes_read)); - EXPECT_TRUE(stream_->IsDoneReading()); + EXPECT_FALSE(stream_->HasBytesToRead()); } // The test stream will receive a stream frame containing malformed headers and