diff options
author | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-02 01:51:01 +0000 |
---|---|---|
committer | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-02 01:51:01 +0000 |
commit | 96f0e9e2b49168ef28539e7381780891a8cc9c09 (patch) | |
tree | a3958547e7eb385d208619c46afdb057bfebaddc | |
parent | 2224d50902cc923cc0fe2dcaf23916984f1942e7 (diff) | |
download | chromium_src-96f0e9e2b49168ef28539e7381780891a8cc9c09.zip chromium_src-96f0e9e2b49168ef28539e7381780891a8cc9c09.tar.gz chromium_src-96f0e9e2b49168ef28539e7381780891a8cc9c09.tar.bz2 |
Support SPDY 4 common header framing.
This lands server change 43180125.
Also add comments regarding the maximum frame size.
Also update test data for compressed frames.
Review URL: https://chromiumcodereview.appspot.com/12378031
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@185653 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/spdy/spdy_frame_builder.cc | 74 | ||||
-rw-r--r-- | net/spdy/spdy_frame_builder_test.cc | 3 | ||||
-rw-r--r-- | net/spdy/spdy_framer.cc | 272 | ||||
-rw-r--r-- | net/spdy/spdy_framer.h | 23 | ||||
-rw-r--r-- | net/spdy/spdy_framer_test.cc | 1041 | ||||
-rw-r--r-- | net/spdy/spdy_test_utils.cc | 12 |
6 files changed, 1069 insertions, 356 deletions
diff --git a/net/spdy/spdy_frame_builder.cc b/net/spdy/spdy_frame_builder.cc index 957e777..64ecc30 100644 --- a/net/spdy/spdy_frame_builder.cc +++ b/net/spdy/spdy_frame_builder.cc @@ -60,11 +60,19 @@ bool SpdyFrameBuilder::Seek(size_t length) { bool SpdyFrameBuilder::WriteControlFrameHeader(const SpdyFramer& framer, SpdyControlType type, uint8 flags) { - FlagsAndLength flags_length = CreateFlagsAndLength( - flags, capacity_ - framer.GetControlFrameHeaderSize()); - bool success = WriteUInt16(kControlFlagMask | framer.protocol_version()); - success &= WriteUInt16(type); - success &= WriteBytes(&flags_length, sizeof(flags_length)); + bool success = true; + if (framer.protocol_version() < 4) { + FlagsAndLength flags_length = CreateFlagsAndLength( + flags, capacity_ - framer.GetControlFrameHeaderSize()); + success &= WriteUInt16(kControlFlagMask | framer.protocol_version()); + success &= WriteUInt16(type); + success &= WriteBytes(&flags_length, sizeof(flags_length)); + } else { + DCHECK_GT(1u<<16, capacity_); // Make sure length fits in 2B. + success &= WriteUInt16(capacity_); + success &= WriteUInt8(type); + success &= WriteUInt8(flags); + } DCHECK_EQ(framer.GetControlFrameHeaderSize(), length()); return success; } @@ -73,14 +81,23 @@ bool SpdyFrameBuilder::WriteDataFrameHeader(const SpdyFramer& framer, SpdyStreamId stream_id, SpdyDataFlags flags) { DCHECK_EQ(0u, stream_id & ~kStreamIdMask); - bool success = WriteUInt32(stream_id); - size_t length_field = capacity_ - framer.GetDataFrameMinimumSize(); - DCHECK_EQ(0u, length_field & ~static_cast<size_t>(kLengthMask)); - FlagsAndLength flags_length; - flags_length.length_ = htonl(length_field); - DCHECK_EQ(0, flags & ~kDataFlagsMask); - flags_length.flags_[0] = flags; - success &= WriteBytes(&flags_length, sizeof(flags_length)); + bool success = true; + if (framer.protocol_version() < 4) { + success &= WriteUInt32(stream_id); + size_t length_field = capacity_ - framer.GetDataFrameMinimumSize(); + DCHECK_EQ(0u, length_field & ~static_cast<size_t>(kLengthMask)); + FlagsAndLength flags_length; + flags_length.length_ = htonl(length_field); + DCHECK_EQ(0, flags & ~kDataFlagsMask); + flags_length.flags_[0] = flags; + success &= WriteBytes(&flags_length, sizeof(flags_length)); + } else { + DCHECK_GT(1u<<16, capacity_); // Make sure length fits in 2B. + success &= WriteUInt16(capacity_); + success &= WriteUInt8(0); + success &= WriteUInt8(flags); + success &= WriteUInt32(stream_id); + } DCHECK_EQ(framer.GetDataFrameMinimumSize(), length()); return success; } @@ -117,20 +134,33 @@ bool SpdyFrameBuilder::WriteBytes(const void* data, uint32 data_len) { } bool SpdyFrameBuilder::RewriteLength(const SpdyFramer& framer) { - return OverwriteLength(framer, length_ - framer.GetControlFrameHeaderSize()); + if (framer.protocol_version() < 4) { + return OverwriteLength(framer, + length_ - framer.GetControlFrameHeaderSize()); + } else { + return OverwriteLength(framer, length_); + } } bool SpdyFrameBuilder::OverwriteLength(const SpdyFramer& framer, size_t length) { - FlagsAndLength flags_length = CreateFlagsAndLength( - 0, // We're not writing over the flags value anyway. - length); - - // Write into the correct location by temporarily faking the offset. + bool success = false; const size_t old_length = length_; - length_ = 5; // Offset at which the length field occurs. - bool success = WriteBytes(reinterpret_cast<char*>(&flags_length) + 1, - sizeof(flags_length) - 1); + + if (framer.protocol_version() < 4) { + FlagsAndLength flags_length = CreateFlagsAndLength( + 0, // We're not writing over the flags value anyway. + length); + + // Write into the correct location by temporarily faking the offset. + length_ = 5; // Offset at which the length field occurs. + success = WriteBytes(reinterpret_cast<char*>(&flags_length) + 1, + sizeof(flags_length) - 1); + } else { + length_ = 0; + success = WriteUInt16(length); + } + length_ = old_length; return success; } diff --git a/net/spdy/spdy_frame_builder_test.cc b/net/spdy/spdy_frame_builder_test.cc index d14f697..eeea2b6 100644 --- a/net/spdy/spdy_frame_builder_test.cc +++ b/net/spdy/spdy_frame_builder_test.cc @@ -26,6 +26,7 @@ TEST(SpdyFrameBuilderTestVersionAgnostic, GetWritableBuffer) { enum SpdyFrameBuilderTestTypes { SPDY2 = 2, SPDY3 = 3, + SPDY4 = 4, }; class SpdyFrameBuilderTest @@ -42,7 +43,7 @@ class SpdyFrameBuilderTest // All tests are run with two different SPDY versions: SPDY/2 and SPDY/3. INSTANTIATE_TEST_CASE_P(SpdyFrameBuilderTests, SpdyFrameBuilderTest, - ::testing::Values(SPDY2, SPDY3)); + ::testing::Values(SPDY2, SPDY3, SPDY4)); TEST_P(SpdyFrameBuilderTest, RewriteLength) { // Create an empty SETTINGS frame both via framer and manually via builder. diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc index 7d4fd2c..1d83d32 100644 --- a/net/spdy/spdy_framer.cc +++ b/net/spdy/spdy_framer.cc @@ -52,7 +52,7 @@ const uint8 kNoFlags = 0; } // namespace const int SpdyFramer::kMinSpdyVersion = 2; -const int SpdyFramer::kMaxSpdyVersion = 3; +const int SpdyFramer::kMaxSpdyVersion = 4; const SpdyStreamId SpdyFramer::kInvalidStream = -1; const size_t SpdyFramer::kHeaderDataChunkMaxSize = 1024; // The size of the control frame buffer. Must be >= the minimum size of the @@ -143,8 +143,7 @@ void SpdyFramer::Reset() { state_ = SPDY_RESET; previous_state_ = SPDY_RESET; error_code_ = SPDY_NO_ERROR; - remaining_data_ = 0; - remaining_control_payload_ = 0; + remaining_data_length_ = 0; remaining_control_header_ = 0; current_frame_buffer_length_ = 0; current_frame_type_ = NUM_CONTROL_FRAME_TYPES; @@ -161,11 +160,17 @@ size_t SpdyFramer::GetDataFrameMinimumSize() const { return 8; } +// Size, in bytes, of the control frame header. size_t SpdyFramer::GetControlFrameHeaderSize() const { - // Size, in bytes, of the control frame header. Future versions of SPDY - // will likely vary this, so we allow for the flexibility of a function call - // for this value as opposed to a constant. - return 8; + switch (protocol_version()) { + case 2: + case 3: + return 8; + case 4: + return 4; + } + LOG(DFATAL) << "Unhandled SPDY version."; + return 0; } size_t SpdyFramer::GetSynStreamMinimumSize() const { @@ -459,8 +464,7 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) { bottom: DCHECK(len == 0 || state_ == SPDY_ERROR); if (current_frame_buffer_length_ == 0 && - remaining_data_ == 0 && - remaining_control_payload_ == 0 && + remaining_data_length_ == 0 && remaining_control_header_ == 0) { DCHECK(state_ == SPDY_RESET || state_ == SPDY_ERROR) << "State: " << StateToString(state_); @@ -484,80 +488,133 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { } if (current_frame_buffer_length_ < GetControlFrameHeaderSize()) { - // TODO(rch): remove this empty block - // Do nothing. - } else { - SpdyFrameReader reader(current_frame_buffer_.get(), - current_frame_buffer_length_); + // Not enough information to do anything meaningful. + return original_len - len; + } + + // Using a scoped_ptr here since we may need to create a new SpdyFrameReader + // when processing DATA frames below. + scoped_ptr<SpdyFrameReader> reader( + new SpdyFrameReader(current_frame_buffer_.get(), + current_frame_buffer_length_)); - uint16 version = 0; - bool successful_read = reader.ReadUInt16(&version); + uint16 version = 0; + bool is_control_frame = false; + + if (protocol_version() < 4) { + bool successful_read = reader->ReadUInt16(&version); DCHECK(successful_read); - bool control_bit = (version & kControlFlagMask) != 0; + is_control_frame = (version & kControlFlagMask) != 0; version &= ~kControlFlagMask; // Only valid for control frames. - if (control_bit) { + if (is_control_frame) { uint16 frame_type_field; - successful_read = reader.ReadUInt16(&frame_type_field); + successful_read = reader->ReadUInt16(&frame_type_field); // We check for validity below in ProcessControlFrameHeader(). current_frame_type_ = static_cast<SpdyControlType>(frame_type_field); } else { - reader.Rewind(); - successful_read = reader.ReadUInt31(¤t_frame_stream_id_); + reader->Rewind(); + successful_read = reader->ReadUInt31(¤t_frame_stream_id_); } DCHECK(successful_read); - successful_read = reader.ReadUInt8(¤t_frame_flags_); + successful_read = reader->ReadUInt8(¤t_frame_flags_); DCHECK(successful_read); - successful_read = reader.ReadUInt24(¤t_frame_length_); + uint32 length_field = 0; + successful_read = reader->ReadUInt24(&length_field); DCHECK(successful_read); - remaining_data_ = current_frame_length_; - - // This is just a sanity check for help debugging early frame errors. - if (remaining_data_ > 1000000u) { - // The strncmp for 5 is safe because we only hit this point if we - // have GetControlFrameHeaderSize() (8) bytes - if (!syn_frame_processed_ && - strncmp(current_frame_buffer_.get(), "HTTP/", 5) == 0) { - LOG(WARNING) << "Unexpected HTTP response to " << display_protocol_ - << " request"; - probable_http_response_ = true; - } else { - LOG(WARNING) << "Unexpectedly large frame. " << display_protocol_ - << " session is likely corrupt."; + remaining_data_length_ = length_field; + current_frame_length_ = remaining_data_length_ + reader->GetBytesConsumed(); + } else { + // TODO(hkhalil): Avoid re-reading fields as possible for DATA frames? + version = protocol_version(); + uint16 length_field = 0; + bool successful_read = reader->ReadUInt16(&length_field); + DCHECK(successful_read); + current_frame_length_ = length_field; + + uint8 frame_type_field = 0; + successful_read = reader->ReadUInt8(&frame_type_field); + DCHECK(successful_read); + if (frame_type_field != 0) { + is_control_frame = true; + // We check for validity below in ProcessControlFrameHeader(). + current_frame_type_ = static_cast<SpdyControlType>(frame_type_field); + } + + successful_read = reader->ReadUInt8(¤t_frame_flags_); + DCHECK(successful_read); + + if (!is_control_frame) { + // We may not have the entirety of the DATA frame header. + if (current_frame_buffer_length_ < GetDataFrameMinimumSize()) { + size_t bytes_desired = + GetDataFrameMinimumSize() - current_frame_buffer_length_; + UpdateCurrentFrameBuffer(&data, &len, bytes_desired); + if (current_frame_buffer_length_ < GetDataFrameMinimumSize()) { + return original_len - len; + } } + // Construct a new SpdyFrameReader aware of the new frame length. + reader.reset(new SpdyFrameReader(current_frame_buffer_.get(), + current_frame_buffer_length_)); + reader->Seek(GetControlFrameHeaderSize()); + successful_read = reader->ReadUInt31(¤t_frame_stream_id_); + DCHECK(successful_read); } + remaining_data_length_ = current_frame_length_ - reader->GetBytesConsumed(); + } + DCHECK_EQ(is_control_frame ? GetControlFrameHeaderSize() + : GetDataFrameMinimumSize(), + reader->GetBytesConsumed()); + DCHECK_EQ(current_frame_length_, + remaining_data_length_ + reader->GetBytesConsumed()); + + // This is just a sanity check for help debugging early frame errors. + if (remaining_data_length_ > 1000000u) { + // The strncmp for 5 is safe because we only hit this point if we + // have kMinCommonHeader (8) bytes + if (!syn_frame_processed_ && + strncmp(current_frame_buffer_.get(), "HTTP/", 5) == 0) { + LOG(WARNING) << "Unexpected HTTP response to " << display_protocol_ + << " request"; + probable_http_response_ = true; + } else { + LOG(WARNING) << "Unexpectedly large frame. " << display_protocol_ + << " session is likely corrupt."; + } + } - // if we're here, then we have the common header all received. - if (!control_bit) { - if (current_frame_flags_ & ~DATA_FLAG_FIN) { - set_error(SPDY_INVALID_DATA_FRAME_FLAGS); + // if we're here, then we have the common header all received. + if (!is_control_frame) { + if (current_frame_flags_ & ~DATA_FLAG_FIN) { + set_error(SPDY_INVALID_DATA_FRAME_FLAGS); + } else { + visitor_->OnDataFrameHeader(current_frame_stream_id_, + remaining_data_length_, + current_frame_flags_ & DATA_FLAG_FIN); + if (remaining_data_length_ > 0) { + CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME); } else { - visitor_->OnDataFrameHeader(current_frame_stream_id_, - current_frame_length_, - current_frame_flags_ & DATA_FLAG_FIN); - if (current_frame_length_ > 0) { - CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME); - } else { - // Empty data frame. - if (current_frame_flags_ & DATA_FLAG_FIN) { - visitor_->OnStreamFrameData(current_frame_stream_id_, - NULL, 0, DATA_FLAG_FIN); - } - CHANGE_STATE(SPDY_AUTO_RESET); + // Empty data frame. + if (current_frame_flags_ & DATA_FLAG_FIN) { + visitor_->OnStreamFrameData(current_frame_stream_id_, + NULL, 0, DATA_FLAG_FIN); } + CHANGE_STATE(SPDY_AUTO_RESET); } - } else if (version != spdy_version_) { - // We check version before we check validity: version can never be - // 'invalid', it can only be unsupported. - DLOG(INFO) << "Unsupported SPDY version " << version - << " (expected " << spdy_version_ << ")"; - set_error(SPDY_UNSUPPORTED_VERSION); - } else { - ProcessControlFrameHeader(); } + } else if (version != spdy_version_) { + // We check version before we check validity: version can never be + // 'invalid', it can only be unsupported. + DLOG(INFO) << "Unsupported SPDY version " << version + << " (expected " << spdy_version_ << ")"; + set_error(SPDY_UNSUPPORTED_VERSION); + } else { + ProcessControlFrameHeader(); } + return original_len - len; } @@ -581,8 +638,7 @@ void SpdyFramer::ProcessControlFrameHeader() { // Do some sanity checking on the control frame sizes and flags. switch (current_frame_type_) { case SYN_STREAM: - if (current_frame_length_ < - GetSynStreamMinimumSize() - GetControlFrameHeaderSize()) { + if (current_frame_length_ < GetSynStreamMinimumSize()) { set_error(SPDY_INVALID_CONTROL_FRAME); } else if (current_frame_flags_ & ~(CONTROL_FLAG_FIN | CONTROL_FLAG_UNIDIRECTIONAL)) { @@ -590,16 +646,14 @@ void SpdyFramer::ProcessControlFrameHeader() { } break; case SYN_REPLY: - if (current_frame_length_ < - GetSynReplyMinimumSize() - GetControlFrameHeaderSize()) { + if (current_frame_length_ < GetSynReplyMinimumSize()) { set_error(SPDY_INVALID_CONTROL_FRAME); } else if (current_frame_flags_ & ~CONTROL_FLAG_FIN) { set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); } break; case RST_STREAM: - if (current_frame_length_ != - GetRstStreamSize() - GetControlFrameHeaderSize()) { + if (current_frame_length_ != GetRstStreamSize()) { set_error(SPDY_INVALID_CONTROL_FRAME); } else if (current_frame_flags_ != 0) { set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); @@ -608,9 +662,8 @@ void SpdyFramer::ProcessControlFrameHeader() { case SETTINGS: // Make sure that we have an integral number of 8-byte key/value pairs, // plus a 4-byte length field. - if (current_frame_length_ < - GetSettingsMinimumSize() - GetControlFrameHeaderSize() || - current_frame_length_ % 8 != 4) { + if (current_frame_length_ < GetSettingsMinimumSize() || + (current_frame_length_ - GetControlFrameHeaderSize()) % 8 != 4) { DLOG(WARNING) << "Invalid length for SETTINGS frame: " << current_frame_length_; set_error(SPDY_INVALID_CONTROL_FRAME); @@ -621,8 +674,7 @@ void SpdyFramer::ProcessControlFrameHeader() { break; case GOAWAY: { - if (current_frame_length_ != - GetGoAwaySize() - GetControlFrameHeaderSize()) { + if (current_frame_length_ != GetGoAwaySize()) { set_error(SPDY_INVALID_CONTROL_FRAME); } else if (current_frame_flags_ != 0) { set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); @@ -630,32 +682,28 @@ void SpdyFramer::ProcessControlFrameHeader() { break; } case HEADERS: - if (current_frame_length_ < - GetHeadersMinimumSize() - GetControlFrameHeaderSize()) { + if (current_frame_length_ < GetHeadersMinimumSize()) { set_error(SPDY_INVALID_CONTROL_FRAME); } else if (current_frame_flags_ & ~CONTROL_FLAG_FIN) { set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); } break; case WINDOW_UPDATE: - if (current_frame_length_ != - GetWindowUpdateSize() - GetControlFrameHeaderSize()) { + if (current_frame_length_ != GetWindowUpdateSize()) { set_error(SPDY_INVALID_CONTROL_FRAME); } else if (current_frame_flags_ != 0) { set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); } break; case PING: - if (current_frame_length_ != - GetPingSize() - GetControlFrameHeaderSize()) { + if (current_frame_length_ != GetPingSize()) { set_error(SPDY_INVALID_CONTROL_FRAME); } else if (current_frame_flags_ != 0) { set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); } break; case CREDENTIAL: - if (current_frame_length_ < - GetCredentialMinimumSize() - GetControlFrameHeaderSize()) { + if (current_frame_length_ < GetCredentialMinimumSize()) { set_error(SPDY_INVALID_CONTROL_FRAME); } else if (current_frame_flags_ != 0) { set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS); @@ -678,12 +726,9 @@ void SpdyFramer::ProcessControlFrameHeader() { return; } - remaining_control_payload_ = current_frame_length_; - const size_t total_frame_size = - remaining_control_payload_ + GetControlFrameHeaderSize(); - if (total_frame_size > GetControlFrameBufferMaxSize()) { + if (current_frame_length_ > GetControlFrameBufferMaxSize()) { DLOG(WARNING) << "Received control frame with way too big of a payload: " - << total_frame_size; + << current_frame_length_; set_error(SPDY_CONTROL_PAYLOAD_TOO_LARGE); return; } @@ -716,7 +761,7 @@ void SpdyFramer::ProcessControlFrameHeader() { } if ((frame_size_without_variable_data == -1) && - (total_frame_size > kControlFrameBufferSize)) { + (current_frame_length_ > kControlFrameBufferSize)) { // We should already be in an error state. Double-check. DCHECK_EQ(SPDY_ERROR, state_); if (state_ != SPDY_ERROR) { @@ -726,6 +771,7 @@ void SpdyFramer::ProcessControlFrameHeader() { } return; } + if (frame_size_without_variable_data > 0) { // We have a control frame with a header block. We need to parse the // remainder of the control frame's header before we can parse the header @@ -734,8 +780,6 @@ void SpdyFramer::ProcessControlFrameHeader() { static_cast<int32>(current_frame_buffer_length_)); remaining_control_header_ = frame_size_without_variable_data - current_frame_buffer_length_; - remaining_control_payload_ += GetControlFrameHeaderSize() - - frame_size_without_variable_data; CHANGE_STATE(SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK); return; } @@ -972,6 +1016,7 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, size_t bytes_read = UpdateCurrentFrameBuffer(&data, &len, remaining_control_header_); remaining_control_header_ -= bytes_read; + remaining_data_length_ -= bytes_read; } if (remaining_control_header_ == 0) { @@ -1065,7 +1110,7 @@ size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data, current_frame_type_ != HEADERS) { LOG(DFATAL) << "Unhandled frame type in ProcessControlFrameHeaderBlock."; } - size_t process_bytes = std::min(data_len, remaining_control_payload_); + size_t process_bytes = std::min(data_len, remaining_data_length_); if (process_bytes > 0) { if (enable_compression_) { processed_successfully = IncrementallyDecompressControlFrameHeaderData( @@ -1075,12 +1120,11 @@ size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data, current_frame_stream_id_, data, process_bytes); } - remaining_control_payload_ -= process_bytes; - remaining_data_ -= process_bytes; + remaining_data_length_ -= process_bytes; } // Handle the case that there is no futher data in this frame. - if (remaining_control_payload_ == 0 && processed_successfully) { + if (remaining_data_length_ == 0 && processed_successfully) { // The complete header block has been delivered. We send a zero-length // OnControlFrameHeaderData() to indicate this. visitor_->OnControlFrameHeaderData(current_frame_stream_id_, NULL, 0); @@ -1107,7 +1151,7 @@ size_t SpdyFramer::ProcessSettingsFramePayload(const char* data, size_t data_len) { DCHECK_EQ(SPDY_SETTINGS_FRAME_PAYLOAD, state_); DCHECK_EQ(SETTINGS, current_frame_type_); - size_t unprocessed_bytes = std::min(data_len, remaining_control_payload_); + size_t unprocessed_bytes = std::min(data_len, remaining_data_length_); size_t processed_bytes = 0; // Loop over our incoming data. @@ -1148,8 +1192,8 @@ size_t SpdyFramer::ProcessSettingsFramePayload(const char* data, } // Check if we're done handling this SETTINGS frame. - remaining_control_payload_ -= processed_bytes; - if (remaining_control_payload_ == 0) { + remaining_data_length_ -= processed_bytes; + if (remaining_data_length_ == 0) { CHANGE_STATE(SPDY_AUTO_RESET); } @@ -1207,12 +1251,11 @@ bool SpdyFramer::ProcessSetting(const char* data) { size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) { size_t original_len = len; - if (remaining_control_payload_) { - size_t bytes_read = UpdateCurrentFrameBuffer(&data, &len, - remaining_control_payload_); - remaining_control_payload_ -= bytes_read; - remaining_data_ -= bytes_read; - if (remaining_control_payload_ == 0) { + if (remaining_data_length_) { + size_t bytes_read = + UpdateCurrentFrameBuffer(&data, &len, remaining_data_length_); + remaining_data_length_ -= bytes_read; + if (remaining_data_length_ == 0) { SpdyFrameReader reader(current_frame_buffer_.get(), current_frame_buffer_length_); reader.Seek(GetControlFrameHeaderSize()); // Skip frame header. @@ -1234,8 +1277,8 @@ size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) { successful_read = reader.ReadUInt31(&delta_window_size); DCHECK(successful_read); DCHECK(reader.IsDoneReading()); - visitor_->OnWindowUpdate( - current_frame_stream_id_, delta_window_size); + visitor_->OnWindowUpdate(current_frame_stream_id_, + delta_window_size); } break; case RST_STREAM: { @@ -1291,14 +1334,11 @@ size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) { size_t SpdyFramer::ProcessCredentialFramePayload(const char* data, size_t len) { if (len > 0) { - // Process only up to the end of this CREDENTIAL frame. - len = std::min(len, remaining_control_payload_); bool processed_succesfully = visitor_->OnCredentialFrameData(data, len); - remaining_control_payload_ -= len; - remaining_data_ -= len; + remaining_data_length_ -= len; if (!processed_succesfully) { set_error(SPDY_CREDENTIAL_FRAME_CORRUPT); - } else if (remaining_control_payload_ == 0) { + } else if (remaining_data_length_ == 0) { visitor_->OnCredentialFrameData(NULL, 0); CHANGE_STATE(SPDY_AUTO_RESET); } @@ -1309,8 +1349,8 @@ size_t SpdyFramer::ProcessCredentialFramePayload(const char* data, size_t len) { size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) { size_t original_len = len; - if (remaining_data_ > 0) { - size_t amount_to_forward = std::min(remaining_data_, len); + if (remaining_data_length_ > 0) { + size_t amount_to_forward = std::min(remaining_data_length_, len); if (amount_to_forward && state_ != SPDY_IGNORE_REMAINING_PAYLOAD) { // Only inform the visitor if there is data. if (amount_to_forward) { @@ -1320,17 +1360,17 @@ size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) { } data += amount_to_forward; len -= amount_to_forward; - remaining_data_ -= amount_to_forward; + remaining_data_length_ -= amount_to_forward; // If the FIN flag is set, and there is no more data in this data // frame, inform the visitor of EOF via a 0-length data frame. - if (!remaining_data_ && current_frame_flags_ & DATA_FLAG_FIN) { + if (!remaining_data_length_ && current_frame_flags_ & DATA_FLAG_FIN) { visitor_->OnStreamFrameData( current_frame_stream_id_, NULL, 0, DATA_FLAG_FIN); } } - if (remaining_data_ == 0) { + if (remaining_data_length_ == 0) { CHANGE_STATE(SPDY_AUTO_RESET); } return original_len - len; @@ -1746,7 +1786,11 @@ SpdySerializedFrame* SpdyFramer::SerializeDataFrameHeader( SpdyFrameBuilder builder(kSize); builder.WriteDataFrameHeader(*this, data.stream_id(), flags); - builder.OverwriteLength(*this, data.data().length()); + if (protocol_version() < 4) { + builder.OverwriteLength(*this, data.data().length()); + } else { + builder.OverwriteLength(*this, data.data().length() + kSize); + } DCHECK_EQ(kSize, builder.length()); return builder.take(); } diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h index 2772cab..c691d73 100644 --- a/net/spdy/spdy_framer.h +++ b/net/spdy/spdy_framer.h @@ -516,6 +516,8 @@ class NET_EXPORT_PRIVATE SpdyFramer { FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, ReadLargeSettingsFrame); FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, ReadLargeSettingsFrameInSmallChunks); + FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, ControlFrameAtMaxSizeLimit); + FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, ControlFrameTooLarge); friend class net::HttpNetworkLayer; // This is temporary for the server. friend class net::HttpNetworkTransactionTest; friend class net::HttpProxyClientSocketPoolTest; @@ -597,7 +599,18 @@ class NET_EXPORT_PRIVATE SpdyFramer { // layer. We chose the framing layer, but this can be changed (or removed) // if necessary later down the line. size_t GetControlFrameBufferMaxSize() const { - return (spdy_version_ == 2) ? 64 * 1024 : 16 * 1024 * 1024; + // The theoretical maximum for SPDY3 and earlier is (2^24 - 1) + + // 8, since the length field does not count the size of the + // header. + if (spdy_version_ == kSpdyVersion2) { + return 64 * 1024; + } + if (spdy_version_ == kSpdyVersion3) { + return 16 * 1024 * 1024; + } + // The theoretical maximum for SPDY4 is 2^16 - 1, as the length + // field does count the size of the header. + return 16 * 1024; } // The size of the control frame buffer. @@ -609,11 +622,7 @@ class NET_EXPORT_PRIVATE SpdyFramer { SpdyState state_; SpdyState previous_state_; SpdyError error_code_; - size_t remaining_data_; - - // The number of bytes remaining to read from the current control frame's - // payload. - size_t remaining_control_payload_; + size_t remaining_data_length_; // The number of bytes remaining to read from the current control frame's // headers. Note that header data blocks (for control types that have them) @@ -631,7 +640,7 @@ class NET_EXPORT_PRIVATE SpdyFramer { // The flags field of the frame currently being read. uint8 current_frame_flags_; - // The length field of the frame currently being read. + // The total length of the frame currently being read, including frame header. uint32 current_frame_length_; // The stream ID field of the frame currently being read, if applicable. diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc index 91d56ae..9a9f956 100644 --- a/net/spdy/spdy_framer_test.cc +++ b/net/spdy/spdy_framer_test.cc @@ -78,17 +78,22 @@ class SpdyFramerTestUtil { SpdyFramer* framer, const SpdyFrameType& frame) { DecompressionVisitor visitor(framer->protocol_version()); framer->set_visitor(&visitor); - size_t input_size = frame.size(); - CHECK_EQ(input_size, framer->ProcessInput(frame.data(), input_size)); + CHECK_EQ(frame.size(), framer->ProcessInput(frame.data(), frame.size())); CHECK_EQ(SpdyFramer::SPDY_RESET, framer->state()); framer->set_visitor(NULL); char* buffer = visitor.ReleaseBuffer(); CHECK(buffer != NULL); SpdyFrame* decompressed_frame = new SpdyFrame(buffer, visitor.size(), true); - SetFrameLength(decompressed_frame, - visitor.size() - framer->GetControlFrameHeaderSize(), - framer->protocol_version()); + if (framer->protocol_version() == 4) { + SetFrameLength(decompressed_frame, + visitor.size(), + framer->protocol_version()); + } else { + SetFrameLength(decompressed_frame, + visitor.size() - framer->GetControlFrameHeaderSize(), + framer->protocol_version()); + } return decompressed_frame; } @@ -267,6 +272,8 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, headers_frame_count_(0), goaway_count_(0), setting_count_(0), + last_window_update_stream_(0), + last_window_update_delta_(0), data_bytes_(0), fin_frame_count_(0), fin_flag_count_(0), @@ -471,10 +478,6 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface, header_buffer_.reset(new char[header_buffer_size]); } - size_t control_frame_buffer_max_size() const { - return framer_.GetControlFrameBufferMaxSize(); - } - static size_t header_data_chunk_max_size() { return SpdyFramer::kHeaderDataChunkMaxSize; } @@ -541,6 +544,7 @@ namespace net { enum SpdyFramerTestTypes { SPDY2 = 2, SPDY3 = 3, + SPDY4 = 4, }; class SpdyFramerTest @@ -611,6 +615,8 @@ class SpdyFramerTest } bool IsSpdy2() { return spdy_version_ == SPDY2; } + bool IsSpdy3() { return spdy_version_ == SPDY3; } + bool IsSpdy4() { return spdy_version_ == SPDY4; } // Version of SPDY protocol to be used. unsigned char spdy_version_; @@ -619,7 +625,7 @@ class SpdyFramerTest // All tests are run with two different SPDY versions: SPDY/2 and SPDY/3. INSTANTIATE_TEST_CASE_P(SpdyFramerTests, SpdyFramerTest, - ::testing::Values(SPDY2, SPDY3)); + ::testing::Values(SPDY2, SPDY3, SPDY4)); // Test that we can encode and decode a SpdyHeaderBlock in serialized form. TEST_P(SpdyFramerTest, HeaderBlockInBuffer) { @@ -720,7 +726,7 @@ TEST_P(SpdyFramerTest, CreateCredential) { { const char kDescription[] = "CREDENTIAL frame"; - const unsigned char kFrameData[] = { + const unsigned char kV3FrameData[] = { // Also applies for V2. 0x80, spdy_version_, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x33, 0x00, 0x03, 0x00, 0x00, @@ -732,7 +738,23 @@ TEST_P(SpdyFramerTest, CreateCredential) { 0x0C, 'a', 'n', 'o', 't', 'h', 'e', 'r', ' ', 'c', 'e', 'r', - 't', 0x00, 0x00, 0x00, + 't', 0x00, 0x00, 0x00, + 0x0A, 'f', 'i', 'n', + 'a', 'l', ' ', 'c', + 'e', 'r', 't', + }; + const unsigned char kV4FrameData[] = { + 0x00, 0x37, 0x0A, 0x00, + 0x00, 0x03, 0x00, 0x00, + 0x00, 0x05, 'p', 'r', + 'o', 'o', 'f', 0x00, + 0x00, 0x00, 0x06, 'a', + ' ', 'c', 'e', 'r', + 't', 0x00, 0x00, 0x00, + 0x0C, 'a', 'n', 'o', + 't', 'h', 'e', 'r', + ' ', 'c', 'e', 'r', + 't', 0x00, 0x00, 0x00, 0x0A, 'f', 'i', 'n', 'a', 'l', ' ', 'c', 'e', 'r', 't', @@ -744,7 +766,11 @@ TEST_P(SpdyFramerTest, CreateCredential) { credential.certs.push_back("another cert"); credential.certs.push_back("final cert"); scoped_ptr<SpdyFrame> frame(framer.CreateCredentialFrame(credential)); - CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + if (IsSpdy4()) { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } else { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } } } @@ -752,7 +778,7 @@ TEST_P(SpdyFramerTest, ParseCredentialFrameData) { SpdyFramer framer(spdy_version_); { - unsigned char kFrameData[] = { + const unsigned char kV3FrameData[] = { // Also applies for V2. 0x80, spdy_version_, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x33, 0x00, 0x03, 0x00, 0x00, @@ -769,13 +795,37 @@ TEST_P(SpdyFramerTest, ParseCredentialFrameData) { 'a', 'l', ' ', 'c', 'e', 'r', 't', }; - SpdyFrame frame( - reinterpret_cast<char*>(kFrameData), arraysize(kFrameData), false); + const unsigned char kV4FrameData[] = { + 0x00, 0x37, 0x0A, 0x00, + 0x00, 0x03, 0x00, 0x00, + 0x00, 0x05, 'p', 'r', + 'o', 'o', 'f', 0x00, + 0x00, 0x00, 0x06, 'a', + ' ', 'c', 'e', 'r', + 't', 0x00, 0x00, 0x00, + 0x0C, 'a', 'n', 'o', + 't', 'h', 'e', 'r', + ' ', 'c', 'e', 'r', + 't', 0x00, 0x00, 0x00, + 0x0A, 'f', 'i', 'n', + 'a', 'l', ' ', 'c', + 'e', 'r', 't', + }; + SpdyCredential credential; - EXPECT_TRUE(SpdyFramer::ParseCredentialData( - frame.data() + framer.GetControlFrameHeaderSize(), - frame.size() - framer.GetControlFrameHeaderSize(), - &credential)); + if (IsSpdy4()) { + EXPECT_TRUE(SpdyFramer::ParseCredentialData( + reinterpret_cast<const char*>(kV4FrameData) + + framer.GetControlFrameHeaderSize(), + arraysize(kV4FrameData) - framer.GetControlFrameHeaderSize(), + &credential)); + } else { + EXPECT_TRUE(SpdyFramer::ParseCredentialData( + reinterpret_cast<const char*>(kV3FrameData) + + framer.GetControlFrameHeaderSize(), + arraysize(kV3FrameData) - framer.GetControlFrameHeaderSize(), + &credential)); + } EXPECT_EQ(3u, credential.slot); EXPECT_EQ("proof", credential.proof); EXPECT_EQ("a cert", credential.certs.front()); @@ -1085,11 +1135,66 @@ TEST_P(SpdyFramerTest, Basic) { 0x00, 0x00, 0x00, 0x00, }; + const unsigned char kV4Input[] = { + 0x00, 0x1e, 0x01, 0x00, // SYN_STREAM #1 + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, + 0x00, 0x02, 'h', 'h', + 0x00, 0x00, 0x00, 0x02, + 'v', 'v', + + 0x00, 0x24, 0x08, 0x00, // HEADERS on Stream #1 + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x02, + 'h', '2', 0x00, 0x00, + 0x00, 0x02, 'v', '2', + 0x00, 0x00, 0x00, 0x02, + 'h', '3', 0x00, 0x00, + 0x00, 0x02, 'v', '3', + + 0x00, 0x14, 0x00, 0x00, // DATA on Stream #1 + 0x00, 0x00, 0x00, 0x01, + 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xbe, 0xef, + + 0x00, 0x12, 0x01, 0x00, // SYN Stream #3 + 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + + 0x00, 0x10, 0x00, 0x00, // DATA on Stream #3 + 0x00, 0x00, 0x00, 0x03, + 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xbe, 0xef, + + 0x00, 0x0c, 0x00, 0x00, // DATA on Stream #1 + 0x00, 0x00, 0x00, 0x01, + 0xde, 0xad, 0xbe, 0xef, + + 0x00, 0x0c, 0x03, 0x00, // RST_STREAM on Stream #1 + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x08, 0x00, 0x00, // DATA on Stream #3 + 0x00, 0x00, 0x00, 0x03, + + 0x00, 0x0c, 0x03, 0x00, // RST_STREAM on Stream #3 + 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, + }; + TestSpdyVisitor visitor(spdy_version_); if (IsSpdy2()) { visitor.SimulateInFramer(kV2Input, sizeof(kV2Input)); - } else { + } else if (IsSpdy3()) { visitor.SimulateInFramer(kV3Input, sizeof(kV3Input)); + } else { + visitor.SimulateInFramer(kV4Input, sizeof(kV4Input)); } EXPECT_EQ(0, visitor.error_count_); @@ -1140,14 +1245,14 @@ TEST_P(SpdyFramerTest, FinOnDataFrame) { 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 'h', 'h', 0x00, 0x00, 0x00, 0x02, - 'v', 'v', + 'v', 'v', 0x80, spdy_version_, 0x00, 0x02, // SYN REPLY Stream #1 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, - 'a', 'a', 0x00, 0x00, + 'a', 'a', 0x00, 0x00, 0x00, 0x02, 'b', 'b', 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1 @@ -1160,12 +1265,41 @@ TEST_P(SpdyFramerTest, FinOnDataFrame) { 0x01, 0x00, 0x00, 0x04, 0xde, 0xad, 0xbe, 0xef, }; + const unsigned char kV4Input[] = { + 0x00, 0x1e, 0x01, 0x00, // SYN_STREAM #1 + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, + 0x00, 0x02, 'h', 'h', + 0x00, 0x00, 0x00, 0x02, + 'v', 'v', + + 0x00, 0x18, 0x02, 0x00, // SYN REPLY Stream #1 + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x02, + 'a', 'a', 0x00, 0x00, + 0x00, 0x02, 'b', 'b', + + 0x00, 0x14, 0x00, 0x00, // DATA on Stream #1 + 0x00, 0x00, 0x00, 0x01, + 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xbe, 0xef, + + 0x00, 0x0c, 0x00, 0x01, // DATA on Stream #1, with FIN + 0x00, 0x00, 0x00, 0x01, + 0xde, 0xad, 0xbe, 0xef, + }; TestSpdyVisitor visitor(spdy_version_); if (IsSpdy2()) { visitor.SimulateInFramer(kV2Input, sizeof(kV2Input)); - } else { + } else if (IsSpdy3()) { visitor.SimulateInFramer(kV3Input, sizeof(kV3Input)); + } else { + visitor.SimulateInFramer(kV4Input, sizeof(kV4Input)); } EXPECT_EQ(0, visitor.error_count_); @@ -1216,12 +1350,31 @@ TEST_P(SpdyFramerTest, FinOnSynReplyFrame) { 'a', 'a', 0x00, 0x00, 0x00, 0x02, 'b', 'b', }; + const unsigned char kV4Input[] = { + 0x00, 0x1e, 0x01, 0x00, // SYN_STREAM #1 + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, + 0x00, 0x02, 'h', 'h', + 0x00, 0x00, 0x00, 0x02, + 'v', 'v', + + 0x00, 0x18, 0x02, 0x01, // SYN_REPLY #1, with FIN + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x02, + 'a', 'a', 0x00, 0x00, + 0x00, 0x02, 'b', 'b', + }; TestSpdyVisitor visitor(spdy_version_); if (IsSpdy2()) { visitor.SimulateInFramer(kV2Input, sizeof(kV2Input)); - } else { + } else if (IsSpdy3()) { visitor.SimulateInFramer(kV3Input, sizeof(kV3Input)); + } else { + visitor.SimulateInFramer(kV4Input, sizeof(kV4Input)); } EXPECT_EQ(0, visitor.error_count_); @@ -1418,19 +1571,26 @@ TEST_P(SpdyFramerTest, UnclosedStreamDataCompressorsOneByteAtATime) { TEST_P(SpdyFramerTest, WindowUpdateFrame) { SpdyFramer framer(spdy_version_); - scoped_ptr<SpdyFrame> window_update_frame( - framer.CreateWindowUpdate(1, 0x12345678)); + scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(1, 0x12345678)); const char kDescription[] = "WINDOW_UPDATE frame, stream 1, delta 0x12345678"; - const unsigned char expected_data_frame[] = { - 0x80, spdy_version_, 0x00, 0x09, - 0x00, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x01, - 0x12, 0x34, 0x56, 0x78 + const unsigned char kV3FrameData[] = { // Also applies for V2. + 0x80, spdy_version_, 0x00, 0x09, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x01, + 0x12, 0x34, 0x56, 0x78 + }; + const unsigned char kV4FrameData[] = { + 0x00, 0x0c, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x12, 0x34, 0x56, 0x78 }; - CompareFrame(kDescription, *window_update_frame, expected_data_frame, - arraysize(expected_data_frame)); + if (IsSpdy4()) { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } else { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } } TEST_P(SpdyFramerTest, CreateDataFrame) { @@ -1438,17 +1598,29 @@ TEST_P(SpdyFramerTest, CreateDataFrame) { { const char kDescription[] = "'hello' data frame, no FIN"; - const unsigned char kFrameData[] = { + const unsigned char kV3FrameData[] = { // Also applies for V2. 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, - 'h', 'e', 'l', 'l', + 'h', 'e', 'l', 'l', + 'o' + }; + const unsigned char kV4FrameData[] = { + 0x00, 0x0d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + 'h', 'e', 'l', 'l', 'o' }; const char bytes[] = "hello"; scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( 1, bytes, strlen(bytes), DATA_FLAG_NONE)); - CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + if (IsSpdy4()) { + CompareFrame( + kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } else { + CompareFrame( + kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } SpdyDataIR data_ir(1); data_ir.SetDataShallow(base::StringPiece(bytes, strlen(bytes))); @@ -1457,60 +1629,107 @@ TEST_P(SpdyFramerTest, CreateDataFrame) { kDescription, reinterpret_cast<const unsigned char*>(frame->data()), framer.GetDataFrameMinimumSize(), - kFrameData, + IsSpdy4() ? kV4FrameData : kV3FrameData, framer.GetDataFrameMinimumSize()); } { const char kDescription[] = "Data frame with negative data byte, no FIN"; - const unsigned char kFrameData[] = { + const unsigned char kV3FrameData[] = { // Also applies for V2. 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0xff }; + const unsigned char kV4FrameData[] = { + 0x00, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0xff + }; scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( 1, "\xff", 1, DATA_FLAG_NONE)); - CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + if (IsSpdy4()) { + CompareFrame( + kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } else { + CompareFrame( + kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } } { const char kDescription[] = "'hello' data frame, with FIN"; - const unsigned char kFrameData[] = { + const unsigned char kV3FrameData[] = { // Also applies for V2. 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x05, 'h', 'e', 'l', 'l', 'o' }; + const unsigned char kV4FrameData[] = { + 0x00, 0x0d, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, + 'h', 'e', 'l', 'l', + 'o' + }; scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( 1, "hello", 5, DATA_FLAG_FIN)); - CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + if (IsSpdy4()) { + CompareFrame( + kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } else { + CompareFrame( + kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } } { const char kDescription[] = "Empty data frame"; - const unsigned char kFrameData[] = { + const unsigned char kV3FrameData[] = { // Also applies for V2. 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, }; + const unsigned char kV4FrameData[] = { + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + }; scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( 1, "", 0, DATA_FLAG_NONE)); - CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + if (IsSpdy4()) { + CompareFrame( + kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } else { + CompareFrame( + kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } } { const char kDescription[] = "Data frame with max stream ID"; - const unsigned char kFrameData[] = { + const unsigned char kV3FrameData[] = { // Also applies for V2. 0x7f, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x05, - 'h', 'e', 'l', 'l', + 'h', 'e', 'l', 'l', + 'o' + }; + const unsigned char kV4FrameData[] = { + 0x00, 0x0d, 0x00, 0x01, + 0x7f, 0xff, 0xff, 0xff, + 'h', 'e', 'l', 'l', 'o' }; scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( 0x7fffffff, "hello", 5, DATA_FLAG_FIN)); - CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + if (IsSpdy4()) { + CompareFrame( + kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } else { + CompareFrame( + kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } } - { + if (!IsSpdy4()) { + // This test does not apply to SPDY 4 because the max frame size is smaller + // than 4MB. const char kDescription[] = "Large data frame"; const int kDataSize = 4 * 1024 * 1024; // 4 MB const string kData(kDataSize, 'A'); @@ -1571,6 +1790,20 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) { 0x00, 0x00, 0x03, 'b', 'a', 'r' }; + const unsigned char kV4FrameData[] = { + 0x00, 0x2e, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + kPri, kCre, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, + 0x00, 0x03, 'b', 'a', + 'r', 0x00, 0x00, 0x00, + 0x03, 'f', 'o', 'o', + 0x00, 0x00, 0x00, 0x03, + 'f', 'o', 'o', 0x00, + 0x00, 0x00, 0x03, 'b', + 'a', 'r' + }; scoped_ptr<SpdyFrame> frame( framer.CreateSynStream(1, // stream id 0, // associated stream id @@ -1579,10 +1812,13 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) { CONTROL_FLAG_NONE, false, // compress &headers)); - CompareFrame(kDescription, - *frame, - IsSpdy2() ? kV2FrameData : kV3FrameData, - IsSpdy2() ? arraysize(kV2FrameData) : arraysize(kV3FrameData)); + if (IsSpdy2()) { + CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData)); + } else if (IsSpdy3()) { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } else { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } } { @@ -1620,6 +1856,19 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) { 0x00, 0x00, 0x00, 0x03, 'b', 'a', 'r' }; + const unsigned char kV4FrameData[] = { + 0x00, 0x2b, 0x01, 0x01, + 0x7f, 0xff, 0xff, 0xff, + 0x7f, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 'f', 'o', + 'o', 0x00, 0x00, 0x00, + 0x03, 'f', 'o', 'o', + 0x00, 0x00, 0x00, 0x03, + 'b', 'a', 'r' + }; scoped_ptr<SpdyFrame> frame( framer.CreateSynStream(0x7fffffff, // stream id 0x7fffffff, // associated stream id @@ -1628,10 +1877,13 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) { CONTROL_FLAG_FIN, false, // compress &headers)); - CompareFrame(kDescription, - *frame, - IsSpdy2() ? kV2FrameData : kV3FrameData, - IsSpdy2() ? arraysize(kV2FrameData) : arraysize(kV3FrameData)); + if (IsSpdy2()) { + CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData)); + } else if (IsSpdy3()) { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } else { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } } { @@ -1670,6 +1922,19 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) { 'f', 'o', 'o', 0x00, 0x00, 0x00, 0x00 }; + const unsigned char kV4FrameData[] = { + 0x00, 0x2b, 0x01, 0x01, + 0x7f, 0xff, 0xff, 0xff, + 0x7f, 0xff, 0xff, 0xff, + kPri, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, + 0x00, 0x03, 'b', 'a', + 'r', 0x00, 0x00, 0x00, + 0x03, 'f', 'o', 'o', + 0x00, 0x00, 0x00, 0x03, + 'f', 'o', 'o', 0x00, + 0x00, 0x00, 0x00 + }; scoped_ptr<SpdyFrame> frame( framer.CreateSynStream(0x7fffffff, // stream id 0x7fffffff, // associated stream id @@ -1678,10 +1943,13 @@ TEST_P(SpdyFramerTest, CreateSynStreamUncompressed) { CONTROL_FLAG_FIN, false, // compress &headers)); - CompareFrame(kDescription, - *frame, - IsSpdy2() ? kV2FrameData : kV3FrameData, - IsSpdy2() ? arraysize(kV2FrameData) : arraysize(kV3FrameData)); + if (IsSpdy2()) { + CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData)); + } else if (IsSpdy3()) { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } else { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } } } @@ -1737,6 +2005,23 @@ TEST_P(SpdyFramerTest, CreateSynStreamCompressed) { 0x80, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, }; + const unsigned char kV4FrameData[] = { + 0x00, 0x3b, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x38, 0xea, + 0xe3, 0xc6, 0xa7, 0xc2, + 0x02, 0xe5, 0x0e, 0x50, + 0xc2, 0x4b, 0x4a, 0x04, + 0xe5, 0x0b, 0x66, 0x80, + 0x00, 0x4a, 0xcb, 0xcf, + 0x07, 0x08, 0x20, 0x10, + 0x95, 0x96, 0x9f, 0x0f, + 0xa2, 0x00, 0x02, 0x28, + 0x29, 0xb1, 0x08, 0x20, + 0x80, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, + }; scoped_ptr<SpdyFrame> frame( framer.CreateSynStream(1, // stream id 0, // associated stream id @@ -1745,10 +2030,13 @@ TEST_P(SpdyFramerTest, CreateSynStreamCompressed) { CONTROL_FLAG_NONE, true, // compress &headers)); - CompareFrame(kDescription, - *frame, - IsSpdy2() ? kV2FrameData : kV3FrameData, - IsSpdy2() ? arraysize(kV2FrameData) : arraysize(kV3FrameData)); + if (IsSpdy2()) { + CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData)); + } else if (IsSpdy3()) { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } else { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } } } #endif // !defined(USE_SYSTEM_ZLIB) @@ -1788,12 +2076,27 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) { 'o', 0x00, 0x00, 0x00, 0x03, 'b', 'a', 'r' }; + const unsigned char kV4FrameData[] = { + 0x00, 0x28, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x03, + 'b', 'a', 'r', 0x00, + 0x00, 0x00, 0x03, 'f', + 'o', 'o', 0x00, 0x00, + 0x00, 0x03, 'f', 'o', + 'o', 0x00, 0x00, 0x00, + 0x03, 'b', 'a', 'r' + }; scoped_ptr<SpdyFrame> frame(framer.CreateSynReply( 1, CONTROL_FLAG_NONE, false, &headers)); - CompareFrame(kDescription, - *frame, - IsSpdy2() ? kV2FrameData : kV3FrameData, - IsSpdy2() ? arraysize(kV2FrameData) : arraysize(kV3FrameData)); + if (IsSpdy2()) { + CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData)); + } else if (IsSpdy3()) { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } else { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } } { @@ -1828,12 +2131,27 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) { 0x00, 0x03, 'b', 'a', 'r' }; + const unsigned char kV4FrameData[] = { + 0x00, 0x25, 0x02, 0x01, + 0x7f, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, + 'f', 'o', 'o', 0x00, + 0x00, 0x00, 0x03, 'f', + 'o', 'o', 0x00, 0x00, + 0x00, 0x03, 'b', 'a', + 'r' + }; scoped_ptr<SpdyFrame> frame(framer.CreateSynReply( 0x7fffffff, CONTROL_FLAG_FIN, false, &headers)); - CompareFrame(kDescription, - *frame, - IsSpdy2() ? kV2FrameData : kV3FrameData, - IsSpdy2() ? arraysize(kV2FrameData) : arraysize(kV3FrameData)); + if (IsSpdy2()) { + CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData)); + } else if (IsSpdy3()) { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } else { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } } { @@ -1868,12 +2186,27 @@ TEST_P(SpdyFramerTest, CreateSynReplyUncompressed) { 'o', 0x00, 0x00, 0x00, 0x00 }; + const unsigned char kV4FrameData[] = { + 0x00, 0x25, 0x02, 0x01, + 0x7f, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x03, + 'b', 'a', 'r', 0x00, + 0x00, 0x00, 0x03, 'f', + 'o', 'o', 0x00, 0x00, + 0x00, 0x03, 'f', 'o', + 'o', 0x00, 0x00, 0x00, + 0x00 + }; scoped_ptr<SpdyFrame> frame(framer.CreateSynReply( 0x7fffffff, CONTROL_FLAG_FIN, false, &headers)); - CompareFrame(kDescription, - *frame, - IsSpdy2() ? kV2FrameData : kV3FrameData, - IsSpdy2() ? arraysize(kV2FrameData) : arraysize(kV3FrameData)); + if (IsSpdy2()) { + CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData)); + } else if (IsSpdy3()) { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } else { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } } } @@ -1925,12 +2258,31 @@ TEST_P(SpdyFramerTest, CreateSynReplyCompressed) { 0x00, 0x00, 0x00, 0xff, 0xff, }; + const unsigned char kV4FrameData[] = { + 0x00, 0x35, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x38, 0xea, 0xe3, 0xc6, + 0xa7, 0xc2, 0x02, 0xe5, + 0x0e, 0x50, 0xc2, 0x4b, + 0x4a, 0x04, 0xe5, 0x0b, + 0x66, 0x80, 0x00, 0x4a, + 0xcb, 0xcf, 0x07, 0x08, + 0x20, 0x10, 0x95, 0x96, + 0x9f, 0x0f, 0xa2, 0x00, + 0x02, 0x28, 0x29, 0xb1, + 0x08, 0x20, 0x80, 0x00, + 0x00, 0x00, 0x00, 0xff, + 0xff, + }; scoped_ptr<SpdyFrame> frame(framer.CreateSynReply( 1, CONTROL_FLAG_NONE, true, &headers)); - CompareFrame(kDescription, - *frame, - IsSpdy2() ? kV2FrameData : kV3FrameData, - IsSpdy2() ? arraysize(kV2FrameData) : arraysize(kV3FrameData)); + if (IsSpdy2()) { + CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData)); + } else if (IsSpdy3()) { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } else { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } } } #endif // !defined(USE_SYSTEM_ZLIB) @@ -1940,41 +2292,68 @@ TEST_P(SpdyFramerTest, CreateRstStream) { { const char kDescription[] = "RST_STREAM frame"; - const unsigned char kFrameData[] = { + const unsigned char kV3FrameData[] = { // Also applies for V2. 0x80, spdy_version_, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, }; + const unsigned char kV4FrameData[] = { + 0x00, 0x0c, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, + }; scoped_ptr<SpdyFrame> frame( framer.CreateRstStream(1, RST_STREAM_PROTOCOL_ERROR)); - CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + if (IsSpdy4()) { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } else { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } } { const char kDescription[] = "RST_STREAM frame with max stream ID"; - const unsigned char kFrameData[] = { + const unsigned char kV3FrameData[] = { // Also applies for V2. 0x80, spdy_version_, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x7f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, }; + const unsigned char kV4FrameData[] = { + 0x00, 0x0c, 0x03, 0x00, + 0x7f, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, + }; scoped_ptr<SpdyFrame> frame(framer.CreateRstStream( 0x7FFFFFFF, RST_STREAM_PROTOCOL_ERROR)); - CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + if (IsSpdy4()) { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } else { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } } { const char kDescription[] = "RST_STREAM frame with max status code"; - const unsigned char kFrameData[] = { + const unsigned char kV3FrameData[] = { // Also applies for V2. 0x80, spdy_version_, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x7f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x06, }; + const unsigned char kV4FrameData[] = { + 0x00, 0x0c, 0x03, 0x00, + 0x7f, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x06, + }; scoped_ptr<SpdyFrame> frame(framer.CreateRstStream( 0x7FFFFFFF, RST_STREAM_INTERNAL_ERROR)); - CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + if (IsSpdy4()) { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } else { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } } } @@ -1994,27 +2373,35 @@ TEST_P(SpdyFramerTest, CreateSettings) { EXPECT_EQ(kFlags, settings[kId].first); EXPECT_EQ(kValue, settings[kId].second); - const unsigned char kFrameDatav2[] = { + const unsigned char kV2FrameData[] = { 0x80, spdy_version_, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x04, 0x03, 0x02, 0x01, 0x0a, 0x0b, 0x0c, 0x0d, }; - - const unsigned char kFrameDatav3[] = { + const unsigned char kV3FrameData[] = { 0x80, spdy_version_, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x0c, 0x0d, }; + const unsigned char kV4FrameData[] = { + 0x00, 0x10, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x01, 0x02, 0x03, 0x04, + 0x0a, 0x0b, 0x0c, 0x0d, + }; scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings)); - CompareFrame(kDescription, - *frame, - IsSpdy2() ? kFrameDatav2 : kFrameDatav3, - arraysize(kFrameDatav3)); // Size is unchanged among versions. + if (IsSpdy2()) { + CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData)); + } else if (IsSpdy3()) { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } else { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } } { @@ -2030,7 +2417,7 @@ TEST_P(SpdyFramerTest, CreateSettings) { AddSpdySettingFromWireFormat( &settings, 0x03000003, 0xff000004); // 4th Setting - const unsigned char kFrameData[] = { + const unsigned char kV3FrameData[] = { // Also applies for V2. 0x80, spdy_version_, 0x00, 0x04, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x04, @@ -2043,11 +2430,24 @@ TEST_P(SpdyFramerTest, CreateSettings) { 0x03, 0x00, 0x00, 0x03, // 4th Setting 0xff, 0x00, 0x00, 0x04, }; + const unsigned char kV4FrameData[] = { + 0x00, 0x28, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, // 1st Setting + 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, // 2nd Setting + 0x00, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x02, // 3rd Setting + 0x00, 0x00, 0x00, 0x03, + 0x03, 0x00, 0x00, 0x03, // 4th Setting + 0xff, 0x00, 0x00, 0x04, + }; scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings)); - CompareFrame(kDescription, - *frame, - kFrameData, - arraysize(kFrameData)); + if (IsSpdy4()) { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } else { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } } { @@ -2055,13 +2455,21 @@ TEST_P(SpdyFramerTest, CreateSettings) { SettingsMap settings; - const unsigned char kFrameData[] = { + const unsigned char kV3FrameData[] = { // Also applies for V2. 0x80, spdy_version_, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, }; + const unsigned char kV4FrameData[] = { + 0x00, 0x08, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings)); - CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + if (IsSpdy4()) { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } else { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } } } @@ -2070,13 +2478,21 @@ TEST_P(SpdyFramerTest, CreatePingFrame) { { const char kDescription[] = "PING frame"; - const unsigned char kFrameData[] = { - 0x80, spdy_version_, 0x00, 0x06, - 0x00, 0x00, 0x00, 0x04, - 0x12, 0x34, 0x56, 0x78, + const unsigned char kV3FrameData[] = { // Also applies for V2. + 0x80, spdy_version_, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x04, + 0x12, 0x34, 0x56, 0x78, + }; + const unsigned char kV4FrameData[] = { + 0x00, 0x08, 0x06, 0x00, + 0x12, 0x34, 0x56, 0x78, }; scoped_ptr<SpdyFrame> frame(framer.CreatePingFrame(0x12345678u)); - CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + if (IsSpdy4()) { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } else { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } } } @@ -2096,11 +2512,19 @@ TEST_P(SpdyFramerTest, CreateGoAway) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; + const unsigned char kV4FrameData[] = { + 0x00, 0x0c, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(0, GOAWAY_OK)); - CompareFrame(kDescription, - *frame, - IsSpdy2() ? kV2FrameData : kV3FrameData, - IsSpdy2() ? arraysize(kV2FrameData) : arraysize(kV3FrameData)); + if (IsSpdy2()) { + CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData)); + } else if (IsSpdy3()) { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } else { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } } { @@ -2116,12 +2540,20 @@ TEST_P(SpdyFramerTest, CreateGoAway) { 0x7f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x02, }; + const unsigned char kV4FrameData[] = { + 0x00, 0x0c, 0x07, 0x00, + 0x7f, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x02, + }; scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(0x7FFFFFFF, GOAWAY_INTERNAL_ERROR)); - CompareFrame(kDescription, - *frame, - IsSpdy2() ? kV2FrameData : kV3FrameData, - IsSpdy2() ? arraysize(kV2FrameData) : arraysize(kV3FrameData)); + if (IsSpdy2()) { + CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData)); + } else if (IsSpdy3()) { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } else { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } } } @@ -2160,12 +2592,27 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) { 'o', 0x00, 0x00, 0x00, 0x03, 'b', 'a', 'r' }; + const unsigned char kV4FrameData[] = { + 0x00, 0x28, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x03, + 'b', 'a', 'r', 0x00, + 0x00, 0x00, 0x03, 'f', + 'o', 'o', 0x00, 0x00, + 0x00, 0x03, 'f', 'o', + 'o', 0x00, 0x00, 0x00, + 0x03, 'b', 'a', 'r' + }; scoped_ptr<SpdyFrame> frame(framer.CreateHeaders( 1, CONTROL_FLAG_NONE, false, &headers)); - CompareFrame(kDescription, - *frame, - IsSpdy2() ? kV2FrameData : kV3FrameData, - IsSpdy2() ? arraysize(kV2FrameData) : arraysize(kV3FrameData)); + if (IsSpdy2()) { + CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData)); + } else if (IsSpdy3()) { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } else { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } } { @@ -2200,12 +2647,27 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) { 0x00, 0x03, 'b', 'a', 'r' }; + const unsigned char kV4FrameData[] = { + 0x00, 0x25, 0x08, 0x01, + 0x7f, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, + 'f', 'o', 'o', 0x00, + 0x00, 0x00, 0x03, 'f', + 'o', 'o', 0x00, 0x00, + 0x00, 0x03, 'b', 'a', + 'r' + }; scoped_ptr<SpdyFrame> frame(framer.CreateHeaders( 0x7fffffff, CONTROL_FLAG_FIN, false, &headers)); - CompareFrame(kDescription, - *frame, - IsSpdy2() ? kV2FrameData : kV3FrameData, - IsSpdy2() ? arraysize(kV2FrameData) : arraysize(kV3FrameData)); + if (IsSpdy2()) { + CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData)); + } else if (IsSpdy3()) { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } else { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } } { @@ -2240,12 +2702,27 @@ TEST_P(SpdyFramerTest, CreateHeadersUncompressed) { 'o', 0x00, 0x00, 0x00, 0x00 }; + const unsigned char kV4FrameData[] = { + 0x00, 0x25, 0x08, 0x01, + 0x7f, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x03, + 'b', 'a', 'r', 0x00, + 0x00, 0x00, 0x03, 'f', + 'o', 'o', 0x00, 0x00, + 0x00, 0x03, 'f', 'o', + 'o', 0x00, 0x00, 0x00, + 0x00 + }; scoped_ptr<SpdyFrame> frame(framer.CreateHeaders( 0x7fffffff, CONTROL_FLAG_FIN, false, &headers)); - CompareFrame(kDescription, - *frame, - IsSpdy2() ? kV2FrameData : kV3FrameData, - IsSpdy2() ? arraysize(kV2FrameData) : arraysize(kV3FrameData)); + if (IsSpdy2()) { + CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData)); + } else if (IsSpdy3()) { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } else { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } } } @@ -2297,12 +2774,31 @@ TEST_P(SpdyFramerTest, CreateHeadersCompressed) { 0x00, 0x00, 0x00, 0xff, 0xff, }; + const unsigned char kV4FrameData[] = { + 0x00, 0x35, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x38, 0xea, 0xe3, 0xc6, + 0xa7, 0xc2, 0x02, 0xe5, + 0x0e, 0x50, 0xc2, 0x4b, + 0x4a, 0x04, 0xe5, 0x0b, + 0x66, 0x80, 0x00, 0x4a, + 0xcb, 0xcf, 0x07, 0x08, + 0x20, 0x10, 0x95, 0x96, + 0x9f, 0x0f, 0xa2, 0x00, + 0x02, 0x28, 0x29, 0xb1, + 0x08, 0x20, 0x80, 0x00, + 0x00, 0x00, 0x00, 0xff, + 0xff + }; scoped_ptr<SpdyFrame> frame(framer.CreateHeaders( 1, CONTROL_FLAG_NONE, true, &headers)); - CompareFrame(kDescription, - *frame, - IsSpdy2() ? kV2FrameData : kV3FrameData, - IsSpdy2() ? arraysize(kV2FrameData) : arraysize(kV3FrameData)); + if (IsSpdy2()) { + CompareFrame(kDescription, *frame, kV2FrameData, arraysize(kV2FrameData)); + } else if (IsSpdy3()) { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } else { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } } } #endif // !defined(USE_SYSTEM_ZLIB) @@ -2312,39 +2808,66 @@ TEST_P(SpdyFramerTest, CreateWindowUpdate) { { const char kDescription[] = "WINDOW_UPDATE frame"; - const unsigned char kFrameData[] = { + const unsigned char kV3FrameData[] = { // Also applies for V2. 0x80, spdy_version_, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, }; + const unsigned char kV4FrameData[] = { + 0x00, 0x0c, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, + }; scoped_ptr<SpdyFrame> frame( framer.CreateWindowUpdate(1, 1)); - CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + if (IsSpdy4()) { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } else { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } } { const char kDescription[] = "WINDOW_UPDATE frame with max stream ID"; - const unsigned char kFrameData[] = { + const unsigned char kV3FrameData[] = { // Also applies for V2. 0x80, spdy_version_, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x7f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, }; + const unsigned char kV4FrameData[] = { + 0x00, 0x0c, 0x09, 0x00, + 0x7f, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, + }; scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(0x7FFFFFFF, 1)); - CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + if (IsSpdy4()) { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } else { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } } { const char kDescription[] = "WINDOW_UPDATE frame with max window delta"; - const unsigned char kFrameData[] = { + const unsigned char kV3FrameData[] = { // Also applies for V2. 0x80, spdy_version_, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x7f, 0xff, 0xff, 0xff, }; + const unsigned char kV4FrameData[] = { + 0x00, 0x0c, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x7f, 0xff, 0xff, 0xff, + }; scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(1, 0x7FFFFFFF)); - CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + if (IsSpdy4()) { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } else { + CompareFrame(kDescription, *frame, kV3FrameData, arraysize(kV3FrameData)); + } } } @@ -2446,25 +2969,12 @@ TEST_P(SpdyFramerTest, ReadCompressedHeadersHeaderBlockWithHalfClose) { } TEST_P(SpdyFramerTest, ControlFrameAtMaxSizeLimit) { - SpdyHeaderBlock headers; - // Size a header value to just fit inside the control frame buffer: - // SPDY 2 SPDY 3 - // SYN_STREAM header: 18 bytes 18 bytes - // Serialized header block: - // # headers 2 bytes (uint16) 4 bytes (uint32) - // name length 2 bytes (uint16) 4 bytes (uint32) - // name text ("aa") 2 bytes 2 bytes - // value length 2 bytes (uint16) 4 bytes (uint32) - // --- --- - // 26 bytes 32 bytes - const size_t overhead = IsSpdy2() ? 26 : 32; - TestSpdyVisitor visitor(spdy_version_); - const size_t big_value_size = - visitor.control_frame_buffer_max_size() - overhead; - std::string big_value(big_value_size, 'x'); - headers["aa"] = big_value.c_str(); + // First find the size of the header value in order to just reach the control + // frame max size. SpdyFramer framer(spdy_version_); framer.set_enable_compression(false); + SpdyHeaderBlock headers; + headers["aa"] = ""; scoped_ptr<SpdyFrame> control_frame( framer.CreateSynStream(1, // stream_id 0, // associated_stream_id @@ -2473,7 +2983,24 @@ TEST_P(SpdyFramerTest, ControlFrameAtMaxSizeLimit) { CONTROL_FLAG_NONE, false, // compress &headers)); + const size_t kBigValueSize = + framer.GetControlFrameBufferMaxSize() - control_frame->size(); + + // Create a frame at exatly that size. + string big_value(kBigValueSize, 'x'); + headers["aa"] = big_value.c_str(); + control_frame.reset( + framer.CreateSynStream(1, // stream_id + 0, // associated_stream_id + 1, // priority + 0, // credential_slot + CONTROL_FLAG_NONE, + false, // compress + &headers)); EXPECT_TRUE(control_frame.get() != NULL); + EXPECT_EQ(framer.GetControlFrameBufferMaxSize(), control_frame->size()); + + TestSpdyVisitor visitor(spdy_version_); visitor.SimulateInFramer( reinterpret_cast<unsigned char*>(control_frame->data()), control_frame->size()); @@ -2482,21 +3009,16 @@ TEST_P(SpdyFramerTest, ControlFrameAtMaxSizeLimit) { EXPECT_EQ(1, visitor.syn_frame_count_); EXPECT_EQ(1, visitor.zero_length_control_frame_header_data_count_); EXPECT_EQ(0, visitor.zero_length_data_frame_count_); - EXPECT_LT(big_value_size, visitor.header_buffer_length_); + EXPECT_LT(kBigValueSize, visitor.header_buffer_length_); } TEST_P(SpdyFramerTest, ControlFrameTooLarge) { - SpdyHeaderBlock headers; - // See size calculation for test above. This is one byte larger, which - // should exceed the control frame buffer capacity by that one byte. - const size_t overhead = IsSpdy2() ? 25 : 31; - TestSpdyVisitor visitor(spdy_version_); - const size_t kBigValueSize = - visitor.control_frame_buffer_max_size() - overhead; - std::string big_value(kBigValueSize, 'x'); - headers["aa"] = big_value.c_str(); + // First find the size of the header value in order to just reach the control + // frame max size. SpdyFramer framer(spdy_version_); framer.set_enable_compression(false); + SpdyHeaderBlock headers; + headers["aa"] = ""; scoped_ptr<SpdyFrame> control_frame( framer.CreateSynStream(1, // stream_id 0, // associated_stream_id @@ -2505,7 +3027,25 @@ TEST_P(SpdyFramerTest, ControlFrameTooLarge) { CONTROL_FLAG_NONE, false, // compress &headers)); + const size_t kBigValueSize = + framer.GetControlFrameBufferMaxSize() - control_frame->size() + 1; + + // Create a frame at exatly that size. + string big_value(kBigValueSize, 'x'); + headers["aa"] = big_value.c_str(); + control_frame.reset( + framer.CreateSynStream(1, // stream_id + 0, // associated_stream_id + 1, // priority + 0, // credential_slot + CONTROL_FLAG_NONE, + false, // compress + &headers)); EXPECT_TRUE(control_frame.get() != NULL); + EXPECT_EQ(framer.GetControlFrameBufferMaxSize() + 1, + control_frame->size()); + + TestSpdyVisitor visitor(spdy_version_); visitor.SimulateInFramer( reinterpret_cast<unsigned char*>(control_frame->data()), control_frame->size()); @@ -2525,8 +3065,8 @@ TEST_P(SpdyFramerTest, ControlFrameMuchTooLarge) { const size_t kHeaderBufferChunks = 4; const size_t kHeaderBufferSize = TestSpdyVisitor::header_data_chunk_max_size() * kHeaderBufferChunks; - const size_t big_value_size = kHeaderBufferSize * 2; - std::string big_value(big_value_size, 'x'); + const size_t kBigValueSize = kHeaderBufferSize * 2; + string big_value(kBigValueSize, 'x'); headers["aa"] = big_value.c_str(); SpdyFramer framer(spdy_version_); scoped_ptr<SpdyFrame> control_frame( @@ -2593,28 +3133,28 @@ TEST_P(SpdyFramerTest, DecompressCorruptHeaderBlock) { TEST_P(SpdyFramerTest, ControlFrameSizesAreValidated) { // Create a GoAway frame that has a few extra bytes at the end. // We create enough overhead to overflow the framer's control frame buffer. - ASSERT_GE(254u, SpdyFramer::kControlFrameBufferSize); + ASSERT_GE(250u, SpdyFramer::kControlFrameBufferSize); const unsigned char length = 1 + SpdyFramer::kControlFrameBufferSize; - const unsigned char kV2FrameData[] = { + const unsigned char kV3FrameData[] = { // Also applies for V2. 0x80, spdy_version_, 0x00, 0x07, 0x00, 0x00, 0x00, length, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, }; - const unsigned char kV3FrameData[] = { - 0x80, spdy_version_, 0x00, 0x07, - 0x00, 0x00, 0x00, length, + const unsigned char kV4FrameData[] = { + 0x00, static_cast<uint8>(length + 4), 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; SpdyFramer framer(spdy_version_); const size_t pad_length = length + framer.GetControlFrameHeaderSize() - - (IsSpdy2() ? sizeof(kV2FrameData) : sizeof(kV3FrameData)); + (IsSpdy4() ? sizeof(kV4FrameData) : sizeof(kV3FrameData)); string pad('A', pad_length); TestSpdyVisitor visitor(spdy_version_); - if (IsSpdy2()) { - visitor.SimulateInFramer(kV2FrameData, sizeof(kV2FrameData)); + if (IsSpdy4()) { + visitor.SimulateInFramer(kV4FrameData, sizeof(kV4FrameData)); } else { visitor.SimulateInFramer(kV3FrameData, sizeof(kV3FrameData)); } @@ -2716,7 +3256,6 @@ TEST_P(SpdyFramerTest, ReadDuplicateSettings) { 0x03, 0x00, 0x00, 0x00, // 3rd (unprocessed) Setting 0x00, 0x00, 0x00, 0x03, }; - const unsigned char kV3FrameData[] = { 0x80, spdy_version_, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1C, @@ -2728,13 +3267,25 @@ TEST_P(SpdyFramerTest, ReadDuplicateSettings) { 0x00, 0x00, 0x00, 0x03, // 3rd (unprocessed) Setting 0x00, 0x00, 0x00, 0x03, }; + const unsigned char kV4FrameData[] = { + 0x00, 0x20, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x01, // 1st Setting + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, // 2nd (duplicate) Setting + 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x03, // 3rd (unprocessed) Setting + 0x00, 0x00, 0x00, 0x03, + }; TestSpdyVisitor visitor(spdy_version_); visitor.use_compression_ = false; if (IsSpdy2()) { visitor.SimulateInFramer(kV2FrameData, sizeof(kV2FrameData)); - } else { + } else if (IsSpdy3()) { visitor.SimulateInFramer(kV3FrameData, sizeof(kV3FrameData)); + } else { + visitor.SimulateInFramer(kV4FrameData, sizeof(kV4FrameData)); } EXPECT_EQ(1, visitor.error_count_); EXPECT_EQ(1, visitor.setting_count_); @@ -2755,7 +3306,6 @@ TEST_P(SpdyFramerTest, ReadOutOfOrderSettings) { 0x03, 0x00, 0x00, 0x00, // 3rd (unprocessed) Setting 0x00, 0x00, 0x00, 0x03, }; - const unsigned char kV3FrameData[] = { 0x80, spdy_version_, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1C, @@ -2767,13 +3317,25 @@ TEST_P(SpdyFramerTest, ReadOutOfOrderSettings) { 0x00, 0x00, 0x01, 0x03, // 3rd (unprocessed) Setting 0x00, 0x00, 0x00, 0x03, }; + const unsigned char kV4FrameData[] = { + 0x00, 0x20, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x02, // 1st Setting + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, // 2nd (out of order) Setting + 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x01, 0x03, // 3rd (unprocessed) Setting + 0x00, 0x00, 0x00, 0x03, + }; TestSpdyVisitor visitor(spdy_version_); visitor.use_compression_ = false; if (IsSpdy2()) { visitor.SimulateInFramer(kV2FrameData, sizeof(kV2FrameData)); - } else { + } else if (IsSpdy3()) { visitor.SimulateInFramer(kV3FrameData, sizeof(kV3FrameData)); + } else { + visitor.SimulateInFramer(kV4FrameData, sizeof(kV4FrameData)); } EXPECT_EQ(1, visitor.error_count_); EXPECT_EQ(1, visitor.setting_count_); @@ -2924,6 +3486,10 @@ TEST_P(SpdyFramerTest, ReadGarbage) { } TEST_P(SpdyFramerTest, ReadGarbageWithValidVersion) { + if (IsSpdy4()) { + // Not valid for SPDY 4 since there is no version field. + return; + } SpdyFramer framer(spdy_version_); const unsigned char kFrameData[] = { 0x80, spdy_version_, 0xff, 0xff, @@ -2937,20 +3503,30 @@ TEST_P(SpdyFramerTest, ReadGarbageWithValidVersion) { TEST_P(SpdyFramerTest, SizesTest) { SpdyFramer framer(spdy_version_); - EXPECT_EQ(8u, framer.GetControlFrameHeaderSize()); - EXPECT_EQ(18u, framer.GetSynStreamMinimumSize()); - EXPECT_EQ(IsSpdy2() ? 14u : 12u, framer.GetSynReplyMinimumSize()); - EXPECT_EQ(16u, framer.GetRstStreamSize()); - EXPECT_EQ(8u, framer.GetControlFrameHeaderSize()); - EXPECT_EQ(18u, framer.GetSynStreamMinimumSize()); - EXPECT_EQ(IsSpdy2() ? 14u : 12u, framer.GetSynReplyMinimumSize()); - EXPECT_EQ(16u, framer.GetRstStreamSize()); - EXPECT_EQ(12u, framer.GetSettingsMinimumSize()); - EXPECT_EQ(12u, framer.GetPingSize()); - EXPECT_EQ(IsSpdy2() ? 12u : 16u, framer.GetGoAwaySize()); - EXPECT_EQ(IsSpdy2() ? 14u : 12u, framer.GetHeadersMinimumSize()); - EXPECT_EQ(16u, framer.GetWindowUpdateSize()); - EXPECT_EQ(10u, framer.GetCredentialMinimumSize()); + EXPECT_EQ(8u, framer.GetDataFrameMinimumSize()); + if (IsSpdy4()) { + EXPECT_EQ(4u, framer.GetControlFrameHeaderSize()); + EXPECT_EQ(14u, framer.GetSynStreamMinimumSize()); + EXPECT_EQ(8u, framer.GetSynReplyMinimumSize()); + EXPECT_EQ(12u, framer.GetRstStreamSize()); + EXPECT_EQ(8u, framer.GetSettingsMinimumSize()); + EXPECT_EQ(8u, framer.GetPingSize()); + EXPECT_EQ(12u, framer.GetGoAwaySize()); + EXPECT_EQ(8u, framer.GetHeadersMinimumSize()); + EXPECT_EQ(12u, framer.GetWindowUpdateSize()); + EXPECT_EQ(6u, framer.GetCredentialMinimumSize()); + } else { + EXPECT_EQ(8u, framer.GetControlFrameHeaderSize()); + EXPECT_EQ(18u, framer.GetSynStreamMinimumSize()); + EXPECT_EQ(IsSpdy2() ? 14u : 12u, framer.GetSynReplyMinimumSize()); + EXPECT_EQ(16u, framer.GetRstStreamSize()); + EXPECT_EQ(12u, framer.GetSettingsMinimumSize()); + EXPECT_EQ(12u, framer.GetPingSize()); + EXPECT_EQ(IsSpdy2() ? 12u : 16u, framer.GetGoAwaySize()); + EXPECT_EQ(IsSpdy2() ? 14u : 12u, framer.GetHeadersMinimumSize()); + EXPECT_EQ(16u, framer.GetWindowUpdateSize()); + EXPECT_EQ(10u, framer.GetCredentialMinimumSize()); + } } TEST_P(SpdyFramerTest, StateToStringTest) { @@ -3067,6 +3643,10 @@ TEST_P(SpdyFramerTest, ControlTypeToStringTest) { } TEST_P(SpdyFramerTest, CatchProbableHttpResponse) { + if (IsSpdy4()) { + // TODO(hkhalil): catch probable HTTP response in SPDY 4? + return; + } { testing::StrictMock<test::MockVisitor> visitor; SpdyFramer framer(spdy_version_); @@ -3129,7 +3709,7 @@ TEST_P(SpdyFramerTest, SynStreamFrameFlags) { for (int flags = 0; flags < 256; ++flags) { SCOPED_TRACE(testing::Message() << "Flags " << flags); - testing::StrictMock<net::test::MockVisitor> visitor; + testing::StrictMock<test::MockVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); @@ -3169,7 +3749,7 @@ TEST_P(SpdyFramerTest, SynReplyFrameFlags) { for (int flags = 0; flags < 256; ++flags) { SCOPED_TRACE(testing::Message() << "Flags " << flags); - testing::StrictMock<net::test::MockVisitor> visitor; + testing::StrictMock<test::MockVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); @@ -3206,7 +3786,7 @@ TEST_P(SpdyFramerTest, RstStreamFrameFlags) { for (int flags = 0; flags < 256; ++flags) { SCOPED_TRACE(testing::Message() << "Flags " << flags); - testing::StrictMock<net::test::MockVisitor> visitor; + testing::StrictMock<test::MockVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); @@ -3235,7 +3815,7 @@ TEST_P(SpdyFramerTest, SettingsFrameFlags) { for (int flags = 0; flags < 256; ++flags) { SCOPED_TRACE(testing::Message() << "Flags " << flags); - testing::StrictMock<net::test::MockVisitor> visitor; + testing::StrictMock<test::MockVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); @@ -3268,7 +3848,7 @@ TEST_P(SpdyFramerTest, GoawayFrameFlags) { for (int flags = 0; flags < 256; ++flags) { SCOPED_TRACE(testing::Message() << "Flags " << flags); - testing::StrictMock<net::test::MockVisitor> visitor; + testing::StrictMock<test::MockVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); @@ -3297,7 +3877,7 @@ TEST_P(SpdyFramerTest, HeadersFrameFlags) { for (int flags = 0; flags < 256; ++flags) { SCOPED_TRACE(testing::Message() << "Flags " << flags); - testing::StrictMock<net::test::MockVisitor> visitor; + testing::StrictMock<test::MockVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); @@ -3334,7 +3914,7 @@ TEST_P(SpdyFramerTest, PingFrameFlags) { for (int flags = 0; flags < 256; ++flags) { SCOPED_TRACE(testing::Message() << "Flags " << flags); - testing::StrictMock<net::test::MockVisitor> visitor; + testing::StrictMock<test::MockVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); @@ -3363,7 +3943,7 @@ TEST_P(SpdyFramerTest, WindowUpdateFrameFlags) { for (int flags = 0; flags < 256; ++flags) { SCOPED_TRACE(testing::Message() << "Flags " << flags); - testing::StrictMock<net::test::MockVisitor> visitor; + testing::StrictMock<test::MockVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); @@ -3392,7 +3972,7 @@ TEST_P(SpdyFramerTest, CredentialFrameFlags) { for (int flags = 0; flags < 256; ++flags) { SCOPED_TRACE(testing::Message() << "Flags " << flags); - testing::StrictMock<net::test::MockVisitor> visitor; + testing::StrictMock<test::MockVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); @@ -3431,10 +4011,17 @@ TEST_P(SpdyFramerTest, EmptySynStream) { frame(framer.CreateSynStream(1, 0, 1, 0, CONTROL_FLAG_NONE, true, &headers)); // Adjust size to remove the name/value block. - SetFrameLength( - frame.get(), - framer.GetSynStreamMinimumSize() - framer.GetControlFrameHeaderSize(), - spdy_version_); + if (IsSpdy4()) { + SetFrameLength( + frame.get(), + framer.GetSynStreamMinimumSize(), + spdy_version_); + } else { + SetFrameLength( + frame.get(), + framer.GetSynStreamMinimumSize() - framer.GetControlFrameHeaderSize(), + spdy_version_); + } EXPECT_CALL(visitor, OnSynStream(1, 0, 1, 0, false, false)); EXPECT_CALL(visitor, OnControlFrameHeaderData(1, NULL, 0)); @@ -3460,33 +4047,55 @@ TEST_P(SpdyFramerTest, SettingsFlagsAndId) { TEST_P(SpdyFramerTest, RstStreamStatusBounds) { DCHECK_GE(0xff, RST_STREAM_NUM_STATUS_CODES); - const unsigned char kRstStreamInvalid[] = { + const unsigned char kV3RstStreamInvalid[] = { 0x80, spdy_version_, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, RST_STREAM_INVALID }; + const unsigned char kV4RstStreamInvalid[] = { + 0x00, 0x0c, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, RST_STREAM_INVALID + }; - const unsigned char kRstStreamNumStatusCodes[] = { + const unsigned char kV3RstStreamNumStatusCodes[] = { 0x80, spdy_version_, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, RST_STREAM_NUM_STATUS_CODES }; + const unsigned char kV4RstStreamNumStatusCodes[] = { + 0x00, 0x0c, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, RST_STREAM_NUM_STATUS_CODES + }; - testing::StrictMock<net::test::MockVisitor> visitor; + testing::StrictMock<test::MockVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INVALID)); - framer.ProcessInput(reinterpret_cast<const char*>(kRstStreamInvalid), - arraysize(kRstStreamInvalid)); + if (IsSpdy4()) { + framer.ProcessInput(reinterpret_cast<const char*>(kV4RstStreamInvalid), + arraysize(kV4RstStreamInvalid)); + } else { + framer.ProcessInput(reinterpret_cast<const char*>(kV3RstStreamInvalid), + arraysize(kV3RstStreamInvalid)); + } EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state()); EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()); EXPECT_CALL(visitor, OnRstStream(1, RST_STREAM_INVALID)); - framer.ProcessInput(reinterpret_cast<const char*>(kRstStreamNumStatusCodes), - arraysize(kRstStreamNumStatusCodes)); + if (IsSpdy4()) { + framer.ProcessInput( + reinterpret_cast<const char*>(kV4RstStreamNumStatusCodes), + arraysize(kV4RstStreamNumStatusCodes)); + } else { + framer.ProcessInput( + reinterpret_cast<const char*>(kV3RstStreamNumStatusCodes), + arraysize(kV3RstStreamNumStatusCodes)); + } EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state()); EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()); } @@ -3504,8 +4113,13 @@ TEST_P(SpdyFramerTest, GoAwayStreamIdBounds) { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, }; + const unsigned char kV4FrameData[] = { + 0x00, 0x0c, 0x07, 0x00, + 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, + }; - testing::StrictMock<net::test::MockVisitor> visitor; + testing::StrictMock<test::MockVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); @@ -3513,9 +4127,12 @@ TEST_P(SpdyFramerTest, GoAwayStreamIdBounds) { if (IsSpdy2()) { framer.ProcessInput(reinterpret_cast<const char*>(kV2FrameData), arraysize(kV2FrameData)); - } else { + } else if (IsSpdy3()) { framer.ProcessInput(reinterpret_cast<const char*>(kV3FrameData), arraysize(kV3FrameData)); + } else { + framer.ProcessInput(reinterpret_cast<const char*>(kV4FrameData), + arraysize(kV4FrameData)); } EXPECT_EQ(SpdyFramer::SPDY_RESET, framer.state()); EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()); diff --git a/net/spdy/spdy_test_utils.cc b/net/spdy/spdy_test_utils.cc index 69a7f86..9a7401b 100644 --- a/net/spdy/spdy_test_utils.cc +++ b/net/spdy/spdy_test_utils.cc @@ -88,6 +88,9 @@ void SetFrameFlags(SpdyFrame* frame, uint8 flags, int spdy_version) { case 3: frame->data()[4] = flags; break; + case 4: + frame->data()[3] = flags; + break; default: LOG(FATAL) << "Unsupported SPDY version."; } @@ -105,6 +108,15 @@ void SetFrameLength(SpdyFrame* frame, size_t length, int spdy_version) { memcpy(frame->data() + 5, reinterpret_cast<char*>(&wire_length) + 1, 3); } break; + case 4: + CHECK_GT(1u<<16, length); + { + int32 wire_length = htons(static_cast<uint16>(length)); + memcpy(frame->data(), + reinterpret_cast<char*>(&wire_length), + sizeof(uint16)); + } + break; default: LOG(FATAL) << "Unsupported SPDY version."; } |