Enable partial delivery of Push Promise Frame's push id.
gfe-relnote: v99 only, not protected.
PiperOrigin-RevId: 262578050
Change-Id: I738b7d5be49dfa07eeb5201eca21a6e144d5a3ca
diff --git a/quic/core/http/http_decoder.cc b/quic/core/http/http_decoder.cc
index 227a839..3a13300 100644
--- a/quic/core/http/http_decoder.cc
+++ b/quic/core/http/http_decoder.cc
@@ -23,6 +23,8 @@
remaining_frame_length_(0),
current_type_field_length_(0),
remaining_type_field_length_(0),
+ current_push_id_length_(0),
+ remaining_push_id_length_(0),
error_(QUIC_NO_ERROR),
error_detail_("") {
DCHECK(visitor_);
@@ -214,25 +216,57 @@
break;
}
case static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE): {
+ PushId push_id;
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)) {
- // TODO(b/124216424): Use HTTP_MALFORMED_FRAME.
- RaiseError(QUIC_INVALID_FRAME_DATA, "Unable to read push_id");
+ // A new Push Promise frame just arrived.
+ DCHECK_EQ(0u, current_push_id_length_);
+ current_push_id_length_ = reader->PeekVarInt62Length();
+ if (current_push_id_length_ > remaining_frame_length_) {
+ RaiseError(QUIC_INVALID_FRAME_DATA, "PUSH_PROMISE frame malformed.");
return false;
}
- remaining_frame_length_ -= bytes_remaining - reader->BytesRemaining();
- if (!visitor_->OnPushPromiseFrameStart(
- push_id,
- current_length_field_length_ + current_type_field_length_,
- push_id_length)) {
- continue_processing = false;
+ if (current_push_id_length_ > reader->BytesRemaining()) {
+ // Not all bytes of push id is present yet, buffer push id.
+ DCHECK_EQ(0u, remaining_push_id_length_);
+ remaining_push_id_length_ = current_push_id_length_;
+ BufferPushId(reader);
break;
}
+ bool success = reader->ReadVarInt62(&push_id);
+ DCHECK(success);
+ remaining_frame_length_ -= current_push_id_length_;
+ if (!visitor_->OnPushPromiseFrameStart(
+ push_id,
+ current_length_field_length_ + current_type_field_length_,
+ current_push_id_length_)) {
+ continue_processing = false;
+ current_push_id_length_ = 0;
+ break;
+ }
+ current_push_id_length_ = 0;
+ } else if (remaining_push_id_length_ > 0) {
+ // Waiting for more bytes on push id.
+ BufferPushId(reader);
+ if (remaining_push_id_length_ != 0) {
+ break;
+ }
+ QuicDataReader push_id_reader(push_id_buffer_.data(),
+ current_push_id_length_);
+
+ bool success = push_id_reader.ReadVarInt62(&push_id);
+ DCHECK(success);
+ if (!visitor_->OnPushPromiseFrameStart(
+ push_id,
+ current_length_field_length_ + current_type_field_length_,
+ current_push_id_length_)) {
+ continue_processing = false;
+ current_push_id_length_ = 0;
+ break;
+ }
+ current_push_id_length_ = 0;
}
+
+ // Read Push Promise headers.
DCHECK_LT(remaining_frame_length_, current_frame_length_);
QuicByteCount bytes_to_read = std::min<QuicByteCount>(
remaining_frame_length_, reader->BytesRemaining());
@@ -252,7 +286,6 @@
break;
}
case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID): {
- // TODO(rch): Handle partial delivery.
BufferFramePayload(reader);
break;
}
@@ -306,7 +339,6 @@
break;
}
case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH): {
- // TODO(rch): Handle partial delivery.
CancelPushFrame frame;
QuicDataReader reader(buffer_.data(), current_frame_length_);
if (!reader.ReadVarInt62(&frame.push_id)) {
@@ -336,7 +368,6 @@
break;
}
case static_cast<uint64_t>(HttpFrameType::GOAWAY): {
- // TODO(bnc): Handle partial delivery.
QuicDataReader reader(buffer_.data(), current_frame_length_);
GoAwayFrame frame;
static_assert(!std::is_same<decltype(frame.stream_id), uint64_t>::value,
@@ -359,7 +390,6 @@
break;
}
case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID): {
- // TODO(bnc): Handle partial delivery.
QuicDataReader reader(buffer_.data(), current_frame_length_);
MaxPushIdFrame frame;
if (!reader.ReadVarInt62(&frame.push_id)) {
@@ -376,7 +406,6 @@
break;
}
case static_cast<uint64_t>(HttpFrameType::DUPLICATE_PUSH): {
- // TODO(bnc): Handle partial delivery.
QuicDataReader reader(buffer_.data(), current_frame_length_);
DuplicatePushFrame frame;
if (!reader.ReadVarInt62(&frame.push_id)) {
@@ -433,9 +462,6 @@
}
void HttpDecoder::BufferFrameLength(QuicDataReader* reader) {
- if (current_length_field_length_ == remaining_length_field_length_) {
- length_buffer_.fill(0);
- }
QuicByteCount bytes_to_read = std::min<QuicByteCount>(
remaining_length_field_length_, reader->BytesRemaining());
bool success =
@@ -447,9 +473,6 @@
}
void HttpDecoder::BufferFrameType(QuicDataReader* reader) {
- if (current_type_field_length_ == remaining_type_field_length_) {
- type_buffer_.fill(0);
- }
QuicByteCount bytes_to_read = std::min<QuicByteCount>(
remaining_type_field_length_, reader->BytesRemaining());
bool success =
@@ -460,6 +483,19 @@
remaining_type_field_length_ -= bytes_to_read;
}
+void HttpDecoder::BufferPushId(QuicDataReader* reader) {
+ DCHECK_LE(remaining_push_id_length_, current_frame_length_);
+ QuicByteCount bytes_to_read = std::min<QuicByteCount>(
+ reader->BytesRemaining(), remaining_push_id_length_);
+ bool success =
+ reader->ReadBytes(push_id_buffer_.data() + current_push_id_length_ -
+ remaining_push_id_length_,
+ bytes_to_read);
+ DCHECK(success);
+ remaining_push_id_length_ -= bytes_to_read;
+ remaining_frame_length_ -= bytes_to_read;
+}
+
void HttpDecoder::RaiseError(QuicErrorCode error, std::string error_detail) {
state_ = STATE_ERROR;
error_ = error;
diff --git a/quic/core/http/http_decoder.h b/quic/core/http/http_decoder.h
index 21561b9..30ebadc 100644
--- a/quic/core/http/http_decoder.h
+++ b/quic/core/http/http_decoder.h
@@ -5,8 +5,11 @@
#ifndef QUICHE_QUIC_CORE_HTTP_HTTP_DECODER_H_
#define QUICHE_QUIC_CORE_HTTP_HTTP_DECODER_H_
+#include <cstdint>
+
#include "net/third_party/quiche/src/quic/core/http/http_frames.h"
#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
@@ -169,6 +172,10 @@
// Buffers any remaining frame type field from |reader| into |type_buffer_|.
void BufferFrameType(QuicDataReader* reader);
+ // Buffers at most |remaining_push_id_length_| from |reader| to
+ // |push_id_buffer_|.
+ void BufferPushId(QuicDataReader* reader);
+
// Sets |error_| and |error_detail_| accordingly.
void RaiseError(QuicErrorCode error, std::string error_detail);
@@ -199,6 +206,10 @@
QuicByteCount current_type_field_length_;
// Remaining length that's needed for the frame's type field.
QuicByteCount remaining_type_field_length_;
+ // Length of PUSH_PROMISE frame's push id.
+ QuicByteCount current_push_id_length_;
+ // Remaining length that's needed for PUSH_PROMISE frame's push id field.
+ QuicByteCount remaining_push_id_length_;
// Last error.
QuicErrorCode error_;
// The issue which caused |error_|
@@ -209,6 +220,8 @@
std::array<char, sizeof(uint64_t)> length_buffer_;
// Remaining unparsed type field data.
std::array<char, sizeof(uint64_t)> type_buffer_;
+ // Remaining unparsed push id data.
+ std::array<char, sizeof(uint64_t)> push_id_buffer_;
};
} // namespace quic
diff --git a/quic/core/http/http_decoder_test.cc b/quic/core/http/http_decoder_test.cc
index cbadf3b..12d96e8 100644
--- a/quic/core/http/http_decoder_test.cc
+++ b/quic/core/http/http_decoder_test.cc
@@ -202,19 +202,27 @@
TEST_F(HttpDecoderTest, PushPromiseFrame) {
InSequence s;
- std::string input =
- "\x05" // type (PUSH_PROMISE)
- "\x08" // length
- "\x01" // Push Id
- "Headers"; // Header Block
+ std::string input(
+ "\x05" // type (PUSH PROMISE)
+ "\x0f" // length
+ "\xC0"
+ "\x00"
+ "\x00"
+ "\x00"
+ "\x00"
+ "\x00"
+ "\x01"
+ "\x01" // push id 257.
+ "Headers",
+ 17);
// Visitor pauses processing.
- EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, 2, 1))
+ EXPECT_CALL(visitor_, OnPushPromiseFrameStart(257, 2, 8))
.WillOnce(Return(false));
QuicStringPiece remaining_input(input);
QuicByteCount processed_bytes =
ProcessInputWithGarbageAppended(remaining_input);
- EXPECT_EQ(3u, processed_bytes);
+ EXPECT_EQ(10u, processed_bytes);
remaining_input = remaining_input.substr(processed_bytes);
EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("Headers")))
@@ -228,7 +236,7 @@
EXPECT_EQ("", decoder_.error_detail());
// Process the full frame.
- EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, 2, 1));
+ EXPECT_CALL(visitor_, OnPushPromiseFrameStart(257, 2, 8));
EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("Headers")));
EXPECT_CALL(visitor_, OnPushPromiseFrameEnd());
EXPECT_EQ(input.size(), ProcessInput(input));
@@ -236,7 +244,7 @@
EXPECT_EQ("", decoder_.error_detail());
// Process the frame incrementally.
- EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1, 2, 1));
+ EXPECT_CALL(visitor_, OnPushPromiseFrameStart(257, 2, 8));
EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("H")));
EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("e")));
EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("a")));
@@ -248,6 +256,15 @@
ProcessInputCharByChar(input);
EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
EXPECT_EQ("", decoder_.error_detail());
+
+ // Process push id incrementally and append headers with last byte of push id.
+ EXPECT_CALL(visitor_, OnPushPromiseFrameStart(257, 2, 8));
+ EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("Headers")));
+ EXPECT_CALL(visitor_, OnPushPromiseFrameEnd());
+ ProcessInputCharByChar(input.substr(0, 9));
+ EXPECT_EQ(8u, ProcessInput(input.substr(9)));
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
}
TEST_F(HttpDecoderTest, MaxPushId) {
@@ -845,7 +862,7 @@
{"\x05" // type (PUSH_PROMISE)
"\x01" // length
"\x40", // first byte of two-byte varint push id
- "Unable to read push_id"},
+ "PUSH_PROMISE frame malformed."},
{"\x0D" // type (MAX_PUSH_ID)
"\x01" // length
"\x40", // first byte of two-byte varint push id