Index: net/spdy/spdy_framer_test.cc |
diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc |
index de8e86189096434987c2cd9901a86d9725aa6445..f5bbe0a2c6ebdd94b7de1fe3bbdb7103804a3d3d 100644 |
--- a/net/spdy/spdy_framer_test.cc |
+++ b/net/spdy/spdy_framer_test.cc |
@@ -222,6 +222,8 @@ class SpdyFramerTestUtil { |
class TestSpdyVisitor : public SpdyFramerVisitorInterface, |
public SpdyFramerDebugVisitorInterface { |
public: |
+ // This is larger than our max frame size because header blocks that |
+ // are too long can spill over into CONTINUATION frames. |
static const size_t kDefaultHeaderBufferSize = 16 * 1024 * 1024; |
explicit TestSpdyVisitor(SpdyMajorVersion version) |
@@ -231,6 +233,7 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, |
syn_frame_count_(0), |
syn_reply_frame_count_(0), |
headers_frame_count_(0), |
+ push_promise_frame_count_(0), |
goaway_count_(0), |
setting_count_(0), |
settings_ack_sent_(0), |
@@ -261,13 +264,13 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, |
virtual void OnError(SpdyFramer* f) OVERRIDE { |
LOG(INFO) << "SpdyFramer Error: " |
<< SpdyFramer::ErrorCodeToString(f->error_code()); |
- error_count_++; |
+ ++error_count_; |
} |
virtual void OnDataFrameHeader(SpdyStreamId stream_id, |
size_t length, |
bool fin) OVERRIDE { |
- data_frame_count_++; |
+ ++data_frame_count_; |
header_stream_id_ = stream_id; |
} |
@@ -321,24 +324,24 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, |
SpdyPriority priority, |
bool fin, |
bool unidirectional) OVERRIDE { |
- syn_frame_count_++; |
+ ++syn_frame_count_; |
InitHeaderStreaming(SYN_STREAM, stream_id); |
if (fin) { |
- fin_flag_count_++; |
+ ++fin_flag_count_; |
} |
} |
virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE { |
- syn_reply_frame_count_++; |
+ ++syn_reply_frame_count_; |
InitHeaderStreaming(SYN_REPLY, stream_id); |
if (fin) { |
- fin_flag_count_++; |
+ ++fin_flag_count_; |
} |
} |
virtual void OnRstStream(SpdyStreamId stream_id, |
SpdyRstStreamStatus status) OVERRIDE { |
- fin_frame_count_++; |
+ ++fin_frame_count_; |
} |
virtual bool OnRstStreamFrameData(const char* rst_stream_data, |
@@ -352,17 +355,17 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, |
virtual void OnSetting(SpdySettingsIds id, |
uint8 flags, |
uint32 value) OVERRIDE { |
- setting_count_++; |
+ ++setting_count_; |
} |
virtual void OnSettingsAck() OVERRIDE { |
DCHECK_GE(4, framer_.protocol_version()); |
- settings_ack_received_++; |
+ ++settings_ack_received_; |
} |
virtual void OnSettingsEnd() OVERRIDE { |
if (framer_.protocol_version() < 4) { return; } |
- settings_ack_sent_++; |
+ ++settings_ack_sent_; |
} |
virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE { |
@@ -371,14 +374,14 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, |
virtual void OnGoAway(SpdyStreamId last_accepted_stream_id, |
SpdyGoAwayStatus status) OVERRIDE { |
- goaway_count_++; |
+ ++goaway_count_; |
} |
virtual void OnHeaders(SpdyStreamId stream_id, bool fin, bool end) OVERRIDE { |
- headers_frame_count_++; |
+ ++headers_frame_count_; |
InitHeaderStreaming(HEADERS, stream_id); |
if (fin) { |
- fin_flag_count_++; |
+ ++fin_flag_count_; |
} |
} |
@@ -391,13 +394,14 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, |
virtual void OnPushPromise(SpdyStreamId stream_id, |
SpdyStreamId promised_stream_id, |
bool end) OVERRIDE { |
+ ++push_promise_frame_count_; |
InitHeaderStreaming(PUSH_PROMISE, stream_id); |
last_push_promise_stream_ = stream_id; |
last_push_promise_promised_stream_ = promised_stream_id; |
} |
virtual void OnContinuation(SpdyStreamId stream_id, bool end) OVERRIDE { |
- continuation_count_++; |
+ ++continuation_count_; |
} |
virtual void OnSendCompressedFrame(SpdyStreamId stream_id, |
@@ -464,6 +468,7 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, |
int syn_frame_count_; |
int syn_reply_frame_count_; |
int headers_frame_count_; |
+ int push_promise_frame_count_; |
int goaway_count_; |
int setting_count_; |
int settings_ack_sent_; |
@@ -759,14 +764,14 @@ TEST_P(SpdyFramerTest, DuplicateHeader) { |
} |
SpdyFramer framer(spdy_version_); |
// Frame builder with plentiful buffer size. |
- SpdyFrameBuilder frame(1024); |
+ SpdyFrameBuilder frame(1024, spdy_version_); |
if (spdy_version_ < 4) { |
frame.WriteControlFrameHeader(framer, SYN_STREAM, CONTROL_FLAG_NONE); |
frame.WriteUInt32(3); // stream_id |
frame.WriteUInt32(0); // associated stream id |
frame.WriteUInt16(0); // Priority. |
} else { |
- frame.WriteFramePrefix(framer, HEADERS, HEADERS_FLAG_PRIORITY, 3); |
+ frame.BeginNewFrame(framer, HEADERS, HEADERS_FLAG_PRIORITY, 3); |
frame.WriteUInt32(framer.GetHighestPriority()); |
} |
@@ -800,17 +805,17 @@ TEST_P(SpdyFramerTest, DuplicateHeader) { |
TEST_P(SpdyFramerTest, MultiValueHeader) { |
SpdyFramer framer(spdy_version_); |
// Frame builder with plentiful buffer size. |
- SpdyFrameBuilder frame(1024); |
+ SpdyFrameBuilder frame(1024, spdy_version_); |
if (spdy_version_ < 4) { |
frame.WriteControlFrameHeader(framer, SYN_STREAM, CONTROL_FLAG_NONE); |
frame.WriteUInt32(3); // stream_id |
frame.WriteUInt32(0); // associated stream id |
frame.WriteUInt16(0); // Priority. |
} else { |
- frame.WriteFramePrefix(framer, |
- HEADERS, |
- HEADERS_FLAG_PRIORITY | HEADERS_FLAG_END_HEADERS, |
- 3); |
+ frame.BeginNewFrame(framer, |
+ HEADERS, |
+ HEADERS_FLAG_PRIORITY | HEADERS_FLAG_END_HEADERS, |
+ 3); |
frame.WriteUInt32(framer.GetHighestPriority()); |
} |
@@ -3063,6 +3068,63 @@ TEST_P(SpdyFramerTest, ControlFrameTooLarge) { |
EXPECT_EQ(0u, visitor.header_buffer_length_); |
} |
+TEST_P(SpdyFramerTest, TooLargeHeadersFrameUsesContinuation) { |
+ if (spdy_version_ < net::SPDY4) { |
+ return; |
+ } |
+ SpdyFramer framer(spdy_version_); |
+ framer.set_enable_compression(false); |
+ SpdyHeadersIR headers(1); |
+ |
+ // Exact payload length will change with HPACK, but this should be long |
+ // enough to cause an overflow. |
+ const size_t kBigValueSize = framer.GetControlFrameBufferMaxSize(); |
+ string big_value(kBigValueSize, 'x'); |
+ headers.SetHeader("aa", big_value.c_str()); |
+ scoped_ptr<SpdyFrame> control_frame(framer.SerializeHeaders(headers)); |
+ EXPECT_TRUE(control_frame.get() != NULL); |
+ EXPECT_GT(control_frame->size(), framer.GetControlFrameBufferMaxSize()); |
+ |
+ TestSpdyVisitor visitor(spdy_version_); |
+ visitor.SimulateInFramer( |
+ reinterpret_cast<unsigned char*>(control_frame->data()), |
+ control_frame->size()); |
+ EXPECT_TRUE(visitor.header_buffer_valid_); |
+ EXPECT_EQ(0, visitor.error_count_); |
+ EXPECT_EQ(1, visitor.headers_frame_count_); |
+ EXPECT_EQ(1, visitor.continuation_count_); |
+ EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_); |
+} |
+ |
+TEST_P(SpdyFramerTest, TooLargePushPromiseFrameUsesContinuation) { |
+ if (spdy_version_ < net::SPDY4) { |
+ return; |
+ } |
+ SpdyFramer framer(spdy_version_); |
+ framer.set_enable_compression(false); |
+ SpdyPushPromiseIR push_promise(1, 2); |
+ |
+ // Exact payload length will change with HPACK, but this should be long |
+ // enough to cause an overflow. |
+ const size_t kBigValueSize = framer.GetControlFrameBufferMaxSize(); |
+ string big_value(kBigValueSize, 'x'); |
+ push_promise.SetHeader("aa", big_value.c_str()); |
+ scoped_ptr<SpdyFrame> control_frame( |
+ framer.SerializePushPromise(push_promise)); |
+ EXPECT_TRUE(control_frame.get() != NULL); |
+ EXPECT_GT(control_frame->size(), framer.GetControlFrameBufferMaxSize()); |
+ |
+ TestSpdyVisitor visitor(spdy_version_); |
+ visitor.SimulateInFramer( |
+ reinterpret_cast<unsigned char*>(control_frame->data()), |
+ control_frame->size()); |
+ EXPECT_TRUE(visitor.header_buffer_valid_); |
+ EXPECT_EQ(0, visitor.error_count_); |
+ EXPECT_EQ(1, visitor.push_promise_frame_count_); |
+ EXPECT_EQ(1, visitor.continuation_count_); |
+ EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_); |
+} |
+ |
// Check that the framer stops delivering header data chunks once the visitor |
// declares it doesn't want any more. This is important to guard against |
// "zip bomb" types of attacks. |