Write push promise on request streams.
This CL migrates push promise from headers stream to request streams, and HTTP/3 push promise format is used in v99.
gfe-relnote: v99 only, not used in prod.
PiperOrigin-RevId: 260185991
Change-Id: I676cb4ebb71f72b321934c91d77ce4911f1d7225
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index 0f6a7b1..cbfb84d 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -3067,12 +3067,19 @@
QUIC_DVLOG(1) << "send request for /push_example";
EXPECT_EQ(kBody, client_->SendSynchronousRequest(
"https://example.com/push_example"));
- QuicHeadersStream* headers_stream = QuicSpdySessionPeer::GetHeadersStream(
- client_->client()->client_session());
- QuicStreamSequencer* sequencer = QuicStreamPeer::sequencer(headers_stream);
- // Headers stream's sequencer buffer shouldn't be released because server push
- // hasn't finished yet.
- EXPECT_TRUE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer));
+ QuicStreamSequencer* sequencer;
+ if (!VersionUsesQpack(client_->client()
+ ->client_session()
+ ->connection()
+ ->transport_version())) {
+ QuicHeadersStream* headers_stream = QuicSpdySessionPeer::GetHeadersStream(
+ client_->client()->client_session());
+ sequencer = QuicStreamPeer::sequencer(headers_stream);
+ // Headers stream's sequencer buffer shouldn't be released because server
+ // push hasn't finished yet.
+ EXPECT_TRUE(
+ QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer));
+ }
for (const std::string& url : push_urls) {
QUIC_DVLOG(1) << "send request for pushed stream on url " << url;
@@ -3082,7 +3089,13 @@
QUIC_DVLOG(1) << "response body " << response_body;
EXPECT_EQ(expected_body, response_body);
}
- EXPECT_FALSE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer));
+ if (!VersionUsesQpack(client_->client()
+ ->client_session()
+ ->connection()
+ ->transport_version())) {
+ EXPECT_FALSE(
+ QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer));
+ }
}
TEST_P(EndToEndTestServerPush, ServerPushUnderLimit) {
diff --git a/quic/core/http/http_decoder.cc b/quic/core/http/http_decoder.cc
index f8de083..d424c04 100644
--- a/quic/core/http/http_decoder.cc
+++ b/quic/core/http/http_decoder.cc
@@ -219,6 +219,7 @@
if (current_frame_length_ == remaining_frame_length_) {
QuicByteCount bytes_remaining = reader->BytesRemaining();
PushId push_id;
+ QuicByteCount push_id_length = reader->PeekVarInt62Length();
// TODO(rch): Handle partial delivery of this field.
if (!reader->ReadVarInt62(&push_id)) {
RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id");
@@ -226,9 +227,11 @@
}
remaining_frame_length_ -= bytes_remaining - reader->BytesRemaining();
if (!visitor_->OnPushPromiseFrameStart(
- push_id, Http3FrameLengths(current_length_field_length_ +
- current_type_field_length_,
- current_frame_length_))) {
+ push_id,
+ Http3FrameLengths(
+ current_length_field_length_ + current_type_field_length_,
+ current_frame_length_),
+ push_id_length)) {
continue_processing = false;
break;
}
diff --git a/quic/core/http/http_decoder.h b/quic/core/http/http_decoder.h
index 80ba385..723251b 100644
--- a/quic/core/http/http_decoder.h
+++ b/quic/core/http/http_decoder.h
@@ -101,7 +101,8 @@
// Called when a PUSH_PROMISE frame has been received for |push_id|.
virtual bool OnPushPromiseFrameStart(PushId push_id,
- Http3FrameLengths frame_length) = 0;
+ Http3FrameLengths frame_length,
+ QuicByteCount push_id_length) = 0;
// Called when part of the payload of a PUSH_PROMISE frame has been read.
// May be called multiple times for a single frame. |payload| is guaranteed
// to be non-empty.
diff --git a/quic/core/http/http_decoder_test.cc b/quic/core/http/http_decoder_test.cc
index 635268b..be47ee4 100644
--- a/quic/core/http/http_decoder_test.cc
+++ b/quic/core/http/http_decoder_test.cc
@@ -51,8 +51,10 @@
MOCK_METHOD1(OnHeadersFramePayload, bool(QuicStringPiece payload));
MOCK_METHOD0(OnHeadersFrameEnd, bool());
- MOCK_METHOD2(OnPushPromiseFrameStart,
- bool(PushId push_id, Http3FrameLengths frame_lengths));
+ MOCK_METHOD3(OnPushPromiseFrameStart,
+ bool(PushId push_id,
+ Http3FrameLengths frame_lengths,
+ QuicByteCount push_id_length));
MOCK_METHOD1(OnPushPromiseFramePayload, bool(QuicStringPiece payload));
MOCK_METHOD0(OnPushPromiseFrameEnd, bool());
@@ -78,7 +80,7 @@
ON_CALL(visitor_, OnHeadersFrameStart(_)).WillByDefault(Return(true));
ON_CALL(visitor_, OnHeadersFramePayload(_)).WillByDefault(Return(true));
ON_CALL(visitor_, OnHeadersFrameEnd()).WillByDefault(Return(true));
- ON_CALL(visitor_, OnPushPromiseFrameStart(_, _))
+ ON_CALL(visitor_, OnPushPromiseFrameStart(_, _, _))
.WillByDefault(Return(true));
ON_CALL(visitor_, OnPushPromiseFramePayload(_)).WillByDefault(Return(true));
ON_CALL(visitor_, OnPushPromiseFrameEnd()).WillByDefault(Return(true));
@@ -209,7 +211,7 @@
"Headers"; // Header Block
// Visitor pauses processing.
- EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, Http3FrameLengths(2, 8)))
+ EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, Http3FrameLengths(2, 8), 1))
.WillOnce(Return(false));
QuicStringPiece remaining_input(input);
QuicByteCount processed_bytes =
@@ -228,7 +230,7 @@
EXPECT_EQ("", decoder_.error_detail());
// Process the full frame.
- EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, Http3FrameLengths(2, 8)));
+ EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, Http3FrameLengths(2, 8), 1));
EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("Headers")));
EXPECT_CALL(visitor_, OnPushPromiseFrameEnd());
EXPECT_EQ(input.size(), ProcessInput(input));
@@ -236,7 +238,7 @@
EXPECT_EQ("", decoder_.error_detail());
// Process the frame incrementally.
- EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, Http3FrameLengths(2, 8)));
+ EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, Http3FrameLengths(2, 8), 1));
EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("H")));
EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("e")));
EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("a")));
@@ -717,7 +719,7 @@
"\x01"; // Push Id
// Visitor pauses processing.
- EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, Http3FrameLengths(2, 1)))
+ EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, Http3FrameLengths(2, 1), 1))
.WillOnce(Return(false));
EXPECT_EQ(input.size(), ProcessInputWithGarbageAppended(input));
@@ -727,14 +729,14 @@
EXPECT_EQ("", decoder_.error_detail());
// Process the full frame.
- EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, Http3FrameLengths(2, 1)));
+ EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, Http3FrameLengths(2, 1), 1));
EXPECT_CALL(visitor_, OnPushPromiseFrameEnd());
EXPECT_EQ(input.size(), ProcessInput(input));
EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
EXPECT_EQ("", decoder_.error_detail());
// Process the frame incrementally.
- EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, Http3FrameLengths(2, 1)));
+ EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, Http3FrameLengths(2, 1), 1));
EXPECT_CALL(visitor_, OnPushPromiseFrameEnd());
ProcessInputCharByChar(input);
EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
diff --git a/quic/core/http/quic_headers_stream_test.cc b/quic/core/http/quic_headers_stream_test.cc
index 89338a8..e3e03dd 100644
--- a/quic/core/http/quic_headers_stream_test.cc
+++ b/quic/core/http/quic_headers_stream_test.cc
@@ -166,6 +166,9 @@
std::vector<TestParams> params;
ParsedQuicVersionVector all_supported_versions = AllSupportedVersions();
for (size_t i = 0; i < all_supported_versions.size(); ++i) {
+ if (VersionUsesQpack(all_supported_versions[i].transport_version)) {
+ continue;
+ }
for (Perspective p : {Perspective::IS_SERVER, Perspective::IS_CLIENT}) {
params.emplace_back(all_supported_versions[i], p);
}
@@ -378,10 +381,6 @@
}
TEST_P(QuicHeadersStreamTest, WriteHeaders) {
- if (VersionUsesQpack(transport_version())) {
- return;
- }
-
for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
stream_id += next_stream_id_) {
for (bool fin : {false, true}) {
@@ -398,9 +397,6 @@
}
TEST_P(QuicHeadersStreamTest, WritePushPromises) {
- if (GetParam().version.DoesNotHaveHeadersStream()) {
- return;
- }
for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
stream_id += next_stream_id_) {
QuicStreamId promised_stream_id = NextPromisedStreamId();
@@ -436,10 +432,6 @@
}
TEST_P(QuicHeadersStreamTest, ProcessRawData) {
- if (VersionUsesQpack(transport_version())) {
- return;
- }
-
for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
stream_id += next_stream_id_) {
for (bool fin : {false, true}) {
@@ -473,9 +465,6 @@
}
TEST_P(QuicHeadersStreamTest, ProcessPushPromise) {
- if (GetParam().version.DoesNotHaveHeadersStream()) {
- return;
- }
if (perspective() == Perspective::IS_SERVER) {
return;
}
@@ -514,9 +503,6 @@
}
TEST_P(QuicHeadersStreamTest, ProcessPriorityFrame) {
- if (GetParam().version.DoesNotHaveHeadersStream()) {
- return;
- }
QuicStreamId parent_stream_id = 0;
for (SpdyPriority priority = 0; priority < 7; ++priority) {
for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
@@ -571,10 +557,6 @@
}
TEST_P(QuicHeadersStreamTest, ProcessLargeRawData) {
- if (VersionUsesQpack(transport_version())) {
- return;
- }
-
// We want to create a frame that is more than the SPDY Framer's max control
// frame size, which is 16K, but less than the HPACK decoders max decode
// buffer size, which is 32K.
@@ -749,10 +731,6 @@
}
TEST_P(QuicHeadersStreamTest, HpackDecoderDebugVisitor) {
- if (VersionUsesQpack(transport_version())) {
- return;
- }
-
auto hpack_decoder_visitor =
QuicMakeUnique<StrictMock<MockQuicHpackDebugVisitor>>();
{
@@ -806,10 +784,6 @@
}
TEST_P(QuicHeadersStreamTest, HpackEncoderDebugVisitor) {
- if (VersionUsesQpack(transport_version())) {
- return;
- }
-
auto hpack_encoder_visitor =
QuicMakeUnique<StrictMock<MockQuicHpackDebugVisitor>>();
if (perspective() == Perspective::IS_SERVER) {
diff --git a/quic/core/http/quic_receive_control_stream.cc b/quic/core/http/quic_receive_control_stream.cc
index 922dc8c..d870d21 100644
--- a/quic/core/http/quic_receive_control_stream.cc
+++ b/quic/core/http/quic_receive_control_stream.cc
@@ -106,7 +106,8 @@
}
bool OnPushPromiseFrameStart(PushId /*push_id*/,
- Http3FrameLengths /*frame_length*/) override {
+ Http3FrameLengths /*frame_length*/,
+ QuicByteCount /*push_id_length*/) override {
CloseConnectionOnWrongFrame("Push Promise");
return false;
}
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index 50332ee..b57ad72 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -520,15 +520,27 @@
return;
}
- SpdyPushPromiseIR push_promise(original_stream_id, promised_stream_id,
- std::move(headers));
- // PUSH_PROMISE must not be the last frame sent out, at least followed by
- // response headers.
- push_promise.set_fin(false);
+ if (!VersionHasStreamType(connection()->transport_version())) {
+ SpdyPushPromiseIR push_promise(original_stream_id, promised_stream_id,
+ std::move(headers));
+ // PUSH_PROMISE must not be the last frame sent out, at least followed by
+ // response headers.
+ push_promise.set_fin(false);
- SpdySerializedFrame frame(spdy_framer_.SerializeFrame(push_promise));
- headers_stream()->WriteOrBufferData(
- QuicStringPiece(frame.data(), frame.size()), false, nullptr);
+ SpdySerializedFrame frame(spdy_framer_.SerializeFrame(push_promise));
+ headers_stream()->WriteOrBufferData(
+ QuicStringPiece(frame.data(), frame.size()), false, nullptr);
+ return;
+ }
+
+ // Encode header list.
+ std::string encoded_headers =
+ qpack_encoder_->EncodeHeaderList(original_stream_id, &headers);
+ PushPromiseFrame frame;
+ frame.push_id = promised_stream_id;
+ frame.headers = encoded_headers;
+ QuicSpdyStream* stream = GetSpdyDataStream(original_stream_id);
+ stream->WritePushPromise(frame);
}
void QuicSpdySession::SendMaxHeaderListSize(size_t value) {
@@ -693,6 +705,11 @@
void QuicSpdySession::OnHeaderList(const QuicHeaderList& header_list) {
QUIC_DVLOG(1) << "Received header list for stream " << stream_id_ << ": "
<< header_list.DebugString();
+ // This code path is only executed for push promise in IETF QUIC.
+ if (VersionUsesQpack(connection()->transport_version())) {
+ DCHECK(promised_stream_id_ !=
+ QuicUtils::GetInvalidStreamId(connection()->transport_version()));
+ }
if (promised_stream_id_ ==
QuicUtils::GetInvalidStreamId(connection()->transport_version())) {
OnStreamHeaderList(stream_id_, fin_, frame_len_, header_list);
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h
index 1ce8166..8d8bbda 100644
--- a/quic/core/http/quic_spdy_session.h
+++ b/quic/core/http/quic_spdy_session.h
@@ -175,6 +175,18 @@
// Returns true if the session has active request streams.
bool HasActiveRequestStreams() const;
+ // Called when the size of the compressed frame payload is available.
+ void OnCompressedFrameSize(size_t frame_len);
+
+ // Called when a PUSH_PROMISE frame has been received.
+ void OnPushPromise(spdy::SpdyStreamId stream_id,
+ spdy::SpdyStreamId promised_stream_id);
+
+ // Called when the complete list of headers is available.
+ void OnHeaderList(const QuicHeaderList& header_list);
+
+ QuicStreamId promised_stream_id() const { return promised_stream_id_; }
+
// Initialze HTTP/3 unidirectional streams if |unidirectional| is true and
// those streams are not initialized yet.
void OnCanCreateNewOutgoingStream(bool unidirectional) override;
@@ -251,20 +263,10 @@
const spdy::SpdyStreamPrecedence& precedence,
bool fin);
- // Called when a PUSH_PROMISE frame has been received.
- void OnPushPromise(spdy::SpdyStreamId stream_id,
- spdy::SpdyStreamId promised_stream_id);
-
// Called when a PRIORITY frame has been received.
void OnPriority(spdy::SpdyStreamId stream_id,
const spdy::SpdyStreamPrecedence& precedence);
- // Called when the complete list of headers is available.
- void OnHeaderList(const QuicHeaderList& header_list);
-
- // Called when the size of the compressed frame payload is available.
- void OnCompressedFrameSize(size_t frame_len);
-
// Initializes HTTP/3 unidirectional streams if not yet initialzed.
void MaybeInitializeHttp3UnidirectionalStreams();
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc
index ca1345d..c9fa852 100644
--- a/quic/core/http/quic_spdy_stream.cc
+++ b/quic/core/http/quic_spdy_stream.cc
@@ -124,23 +124,32 @@
return stream_->OnHeadersFrameEnd();
}
- bool OnPushPromiseFrameStart(PushId /*push_id*/,
- Http3FrameLengths /*frame_length*/) override {
- // TODO(b/137554973): Consume frame header.
- CloseConnectionOnWrongFrame("Push Promise");
- return false;
+ bool OnPushPromiseFrameStart(PushId push_id,
+ Http3FrameLengths frame_length,
+ QuicByteCount push_id_length) override {
+ if (!VersionHasStreamType(stream_->transport_version())) {
+ CloseConnectionOnWrongFrame("Push Promise");
+ return false;
+ }
+ return stream_->OnPushPromiseFrameStart(push_id, frame_length,
+ push_id_length);
}
bool OnPushPromiseFramePayload(QuicStringPiece payload) override {
- // TODO(b/137554973): Consume frame payload.
DCHECK(!payload.empty());
- CloseConnectionOnWrongFrame("Push Promise");
- return false;
+ if (!VersionUsesQpack(stream_->transport_version())) {
+ CloseConnectionOnWrongFrame("Push Promise");
+ return false;
+ }
+ return stream_->OnPushPromiseFramePayload(payload);
}
bool OnPushPromiseFrameEnd() override {
- CloseConnectionOnWrongFrame("Push Promise");
- return false;
+ if (!VersionUsesQpack(stream_->transport_version())) {
+ CloseConnectionOnWrongFrame("Push Promise");
+ return false;
+ }
+ return stream_->OnPushPromiseFrameEnd();
}
bool OnUnknownFrameStart(uint64_t /* frame_type */,
@@ -342,6 +351,35 @@
return bytes_written;
}
+void QuicSpdyStream::WritePushPromise(const PushPromiseFrame& frame) {
+ DCHECK(VersionUsesQpack(transport_version()));
+ std::unique_ptr<char[]> push_promise_frame_with_id;
+ const size_t push_promise_frame_length =
+ encoder_.SerializePushPromiseFrameWithOnlyPushId(
+ frame, &push_promise_frame_with_id);
+
+ unacked_frame_headers_offsets_.Add(send_buffer().stream_offset(),
+ send_buffer().stream_offset() +
+ push_promise_frame_length +
+ frame.headers.length());
+
+ // Write Push Promise frame header and push id.
+ QUIC_DLOG(INFO) << "Stream " << id()
+ << " is writing Push Promise frame header of length "
+ << push_promise_frame_length << " , with promised id "
+ << frame.push_id;
+ WriteOrBufferData(QuicStringPiece(push_promise_frame_with_id.get(),
+ push_promise_frame_length),
+ /* fin = */ false, /* ack_listener = */ nullptr);
+
+ // Write response headers.
+ QUIC_DLOG(INFO) << "Stream " << id()
+ << " is writing Push Promise request header of length "
+ << frame.headers.length();
+ WriteOrBufferData(frame.headers, /* fin = */ false,
+ /* ack_listener = */ nullptr);
+}
+
QuicConsumedData QuicSpdyStream::WritevBody(const struct iovec* iov,
int count,
bool fin) {
@@ -898,11 +936,48 @@
return !sequencer()->IsClosed() && !reading_stopped();
}
+bool QuicSpdyStream::OnPushPromiseFrameStart(PushId push_id,
+ Http3FrameLengths frame_length,
+ QuicByteCount push_id_length) {
+ DCHECK(VersionHasStreamType(transport_version()));
+ DCHECK(!qpack_decoded_headers_accumulator_);
+
+ // TODO(renjietang): Check max push id and handle errors.
+ spdy_session_->OnPushPromise(id(), push_id);
+ sequencer()->MarkConsumed(
+ body_buffer_.OnNonBody(frame_length.header_length + push_id_length));
+
+ qpack_decoded_headers_accumulator_ =
+ QuicMakeUnique<QpackDecodedHeadersAccumulator>(
+ id(), spdy_session_->qpack_decoder(), this,
+ spdy_session_->max_inbound_header_list_size());
+
+ return true;
+}
+
+bool QuicSpdyStream::OnPushPromiseFramePayload(QuicStringPiece payload) {
+ spdy_session_->OnCompressedFrameSize(payload.length());
+ return OnHeadersFramePayload(payload);
+}
+
+bool QuicSpdyStream::OnPushPromiseFrameEnd() {
+ DCHECK(VersionUsesQpack(transport_version()));
+
+ OnHeadersFrameEnd();
+ return !sequencer()->IsClosed() && !reading_stopped();
+}
+
void QuicSpdyStream::ProcessDecodedHeaders(const QuicHeaderList& headers) {
- const QuicByteCount frame_length = headers_decompressed_
- ? trailers_payload_length_
- : headers_payload_length_;
- OnStreamHeaderList(/* fin = */ false, frame_length, headers);
+ if (spdy_session_->promised_stream_id() ==
+ QuicUtils::GetInvalidStreamId(
+ session()->connection()->transport_version())) {
+ const QuicByteCount frame_length = headers_decompressed_
+ ? trailers_payload_length_
+ : headers_payload_length_;
+ OnStreamHeaderList(/* fin = */ false, frame_length, headers);
+ } else {
+ spdy_session_->OnHeaderList(headers);
+ }
qpack_decoded_headers_accumulator_.reset();
}
diff --git a/quic/core/http/quic_spdy_stream.h b/quic/core/http/quic_spdy_stream.h
index bb111a9..b2e9c84 100644
--- a/quic/core/http/quic_spdy_stream.h
+++ b/quic/core/http/quic_spdy_stream.h
@@ -126,6 +126,9 @@
spdy::SpdyHeaderBlock trailer_block,
QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+ // Serializes |frame| and writes the encoded push promise data.
+ void WritePushPromise(const PushPromiseFrame& frame);
+
// Override to report newly acked bytes via ack_listener_.
bool OnStreamFrameAcked(QuicStreamOffset offset,
QuicByteCount data_length,
@@ -255,6 +258,11 @@
bool OnHeadersFrameStart(Http3FrameLengths frame_length);
bool OnHeadersFramePayload(QuicStringPiece payload);
bool OnHeadersFrameEnd();
+ bool OnPushPromiseFrameStart(PushId push_id,
+ Http3FrameLengths frame_length,
+ QuicByteCount push_id_length);
+ bool OnPushPromiseFramePayload(QuicStringPiece payload);
+ bool OnPushPromiseFrameEnd();
// Called internally when headers are decoded.
void ProcessDecodedHeaders(const QuicHeaderList& headers);
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index 5b2ca81..f641dce 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -2149,25 +2149,28 @@
ElementsAre(Pair("custom-key", "custom-value")));
}
-TEST_P(QuicSpdyStreamTest, PushPromiseOnDataStreamShouldClose) {
+TEST_P(QuicSpdyStreamTest, PushPromiseOnDataStream) {
Initialize(kShouldProcessData);
if (!HasFrameHeader()) {
return;
}
+
+ // QPACK encoded single header field "foo: bar".
+ std::string headers = QuicTextUtils::HexDecode("00002a94e703626172");
+
PushPromiseFrame push_promise;
push_promise.push_id = 0x01;
- push_promise.headers = "Headers";
+ push_promise.headers = headers;
std::unique_ptr<char[]> buffer;
HttpEncoder encoder;
uint64_t length =
encoder.SerializePushPromiseFrameWithOnlyPushId(push_promise, &buffer);
- QuicStreamFrame frame(stream_->id(), false, 0, buffer.get(), length);
- // TODO(lassey): Check for HTTP_WRONG_STREAM error code.
- EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_DECODER_ERROR, _, _));
- stream_->OnStreamHeadersPriority(
- spdy::SpdyStreamPrecedence(kV3HighestPriority));
- ProcessHeaders(false, headers_);
- stream_->ConsumeHeaderList();
+ std::string data = std::string(buffer.get(), length) + headers;
+ QuicStreamFrame frame(stream_->id(), false, 0, data);
+
+ EXPECT_CALL(*session_,
+ OnPromiseHeaderList(stream_->id(), push_promise.push_id,
+ headers.length(), _));
stream_->OnStreamFrame(frame);
}