diff options
author | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-20 07:21:45 +0000 |
---|---|---|
committer | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-20 07:21:45 +0000 |
commit | 60cec34f510c08f8dcdf69bcde7369e06244befe (patch) | |
tree | 84bbda2f4327b1dbcc666acd2b945eb960ac0768 /net | |
parent | cfa1b164c10d44e62a00f4fc920999550d5d07dc (diff) | |
download | chromium_src-60cec34f510c08f8dcdf69bcde7369e06244befe.zip chromium_src-60cec34f510c08f8dcdf69bcde7369e06244befe.tar.gz chromium_src-60cec34f510c08f8dcdf69bcde7369e06244befe.tar.bz2 |
Merge changes to SpdyFramer from server code (#83):
* Remove SPDY data frame compression and decompression code which is not part of
any spec.
Send RST_STREAM when processing data frames with invalid flags from clients.
Send GOAWAY when processing data frames with invalid flags from backends.
Review URL: http://codereview.chromium.org/10129001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@133158 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/spdy/spdy_framer.cc | 146 | ||||
-rw-r--r-- | net/spdy/spdy_framer.h | 15 | ||||
-rw-r--r-- | net/spdy/spdy_framer_test.cc | 94 | ||||
-rw-r--r-- | net/spdy/spdy_protocol.h | 2 | ||||
-rw-r--r-- | net/tools/flip_server/spdy_interface.cc | 6 | ||||
-rw-r--r-- | net/tools/flip_server/spdy_interface.h | 6 |
6 files changed, 83 insertions, 186 deletions
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc index 1a72f35..001b652 100644 --- a/net/spdy/spdy_framer.cc +++ b/net/spdy/spdy_framer.cc @@ -139,7 +139,6 @@ SpdyFramer::~SpdyFramer() { if (header_decompressor_.get()) { inflateEnd(header_decompressor_.get()); } - CleanupStreamCompressorsAndDecompressors(); } void SpdyFramer::Reset() { @@ -205,6 +204,8 @@ const char* SpdyFramer::ErrorCodeToString(int error_code) { return "DECOMPRESS_FAILURE"; case SPDY_COMPRESS_FAILURE: return "COMPRESS_FAILURE"; + case SPDY_INVALID_DATA_FRAME_FLAGS: + return "SPDY_INVALID_DATA_FRAME_FLAGS"; } return "UNKNOWN_ERROR"; } @@ -374,17 +375,8 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { } if (current_frame_len_ < SpdyFrame::kHeaderSize) { + // TODO(rch): remove this empty block // Do nothing. - } else if (current_frame_len_ == SpdyFrame::kHeaderSize && - !current_frame.is_control_frame() && - current_frame.length() == 0) { - // Empty data frame. - SpdyDataFrame data_frame(current_frame_buffer_.get(), false); - visitor_->OnDataFrameHeader(&data_frame); - if (current_frame.flags() & DATA_FLAG_FIN) { - visitor_->OnStreamFrameData(data_frame.stream_id(), NULL, 0); - } - CHANGE_STATE(SPDY_AUTO_RESET); } else { remaining_data_ = current_frame.length(); @@ -406,7 +398,16 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { if (!current_frame.is_control_frame()) { SpdyDataFrame data_frame(current_frame_buffer_.get(), false); visitor_->OnDataFrameHeader(&data_frame); - CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME); + + if (current_frame.length() > 0) { + CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME); + } else { + // Empty data frame. + if (current_frame.flags() & DATA_FLAG_FIN) { + visitor_->OnStreamFrameData(data_frame.stream_id(), NULL, 0); + } + CHANGE_STATE(SPDY_AUTO_RESET); + } } else { ProcessControlFrameHeader(); } @@ -859,42 +860,10 @@ size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) { if (remaining_data_) { size_t amount_to_forward = std::min(remaining_data_, len); if (amount_to_forward && state_ != SPDY_IGNORE_REMAINING_PAYLOAD) { - if (current_data_frame.flags() & DATA_FLAG_COMPRESSED) { - z_stream* decompressor = - GetStreamDecompressor(current_data_frame.stream_id()); - if (!decompressor) - return 0; - - size_t decompressed_max_size = amount_to_forward * 100; - scoped_array<char> decompressed(new char[decompressed_max_size]); - decompressor->next_in = reinterpret_cast<Bytef*>( - const_cast<char*>(data)); - decompressor->avail_in = amount_to_forward; - decompressor->next_out = - reinterpret_cast<Bytef*>(decompressed.get()); - decompressor->avail_out = decompressed_max_size; - - int rv = inflate(decompressor, Z_SYNC_FLUSH); - if (rv != Z_OK) { - LOG(WARNING) << "inflate failure: " << rv; - set_error(SPDY_DECOMPRESS_FAILURE); - return 0; - } - size_t decompressed_size = decompressed_max_size - - decompressor->avail_out; - - // Only inform the visitor if there is data. - if (decompressed_size) - visitor_->OnStreamFrameData(current_data_frame.stream_id(), - decompressed.get(), - decompressed_size); - amount_to_forward -= decompressor->avail_in; - } else { - // The data frame was not compressed. // Only inform the visitor if there is data. - if (amount_to_forward) - visitor_->OnStreamFrameData(current_data_frame.stream_id(), - data, amount_to_forward); + if (amount_to_forward) { + visitor_->OnStreamFrameData(current_data_frame.stream_id(), + data, amount_to_forward); } } data += amount_to_forward; @@ -906,7 +875,6 @@ size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) { if (!remaining_data_ && current_data_frame.flags() & DATA_FLAG_FIN) { visitor_->OnStreamFrameData(current_data_frame.stream_id(), NULL, 0); - CleanupDecompressorForStream(current_data_frame.stream_id()); } } else { CHANGE_STATE(SPDY_AUTO_RESET); @@ -1249,19 +1217,7 @@ SpdyDataFrame* SpdyFramer::CreateDataFrame(SpdyStreamId stream_id, SpdyFrameBuilder frame(stream_id, flags, frame_size); frame.WriteBytes(data, len); DCHECK_EQ(static_cast<size_t>(frame.length()), frame_size); - scoped_ptr<SpdyFrame> data_frame(frame.take()); - SpdyDataFrame* rv; - if (flags & DATA_FLAG_COMPRESSED) { - LOG(DFATAL) << "DATA_FLAG_COMPRESSED invalid for " << display_protocol_ - << "."; - } - rv = reinterpret_cast<SpdyDataFrame*>(data_frame.release()); - - if (flags & DATA_FLAG_FIN) { - CleanupCompressorForStream(stream_id); - } - - return rv; + return reinterpret_cast<SpdyDataFrame*>(frame.take()); } // The following compression setting are based on Brian Olson's analysis. See @@ -1340,22 +1296,6 @@ z_stream* SpdyFramer::GetHeaderDecompressor() { return header_decompressor_.get(); } -z_stream* SpdyFramer::GetStreamDecompressor(SpdyStreamId stream_id) { - CompressorMap::iterator it = stream_decompressors_.find(stream_id); - if (it != stream_decompressors_.end()) - return it->second; // Already initialized. - - scoped_ptr<z_stream> decompressor(new z_stream); - memset(decompressor.get(), 0, sizeof(z_stream)); - - int success = inflateInit(decompressor.get()); - if (success != Z_OK) { - LOG(WARNING) << "inflateInit failure: " << success; - return NULL; - } - return stream_decompressors_[stream_id] = decompressor.release(); -} - bool SpdyFramer::GetFrameBoundaries(const SpdyFrame& frame, int* payload_length, int* header_length, @@ -1457,16 +1397,6 @@ SpdyControlFrame* SpdyFramer::CompressControlFrame( header_length; compressor->avail_out = compressed_max_size; - // Data packets have a 'compressed' flag. - // TODO(hkhalil): Remove post code-yellow. It's impossible to execute this - // branch given that SpdyControlFrame::is_control_frame always returns true. - DCHECK(new_frame->is_control_frame()); - if (!new_frame->is_control_frame()) { - SpdyDataFrame* data_frame = - reinterpret_cast<SpdyDataFrame*>(new_frame.get()); - data_frame->set_flags(data_frame->flags() | DATA_FLAG_COMPRESSED); - } - // Make sure that all the data we pass to zlib is defined. // This way, all Valgrind reports on the compressed data are zlib's fault. (void)VALGRIND_CHECK_MEM_IS_DEFINED(compressor->next_in, @@ -1587,48 +1517,6 @@ bool SpdyFramer::IncrementallyDeliverControlFrameHeaderData( return read_successfully; } -void SpdyFramer::CleanupCompressorForStream(SpdyStreamId id) { - CompressorMap::iterator it = stream_compressors_.find(id); - if (it != stream_compressors_.end()) { - z_stream* compressor = it->second; - deflateEnd(compressor); - delete compressor; - stream_compressors_.erase(it); - } -} - -void SpdyFramer::CleanupDecompressorForStream(SpdyStreamId id) { - CompressorMap::iterator it = stream_decompressors_.find(id); - if (it != stream_decompressors_.end()) { - z_stream* decompressor = it->second; - inflateEnd(decompressor); - delete decompressor; - stream_decompressors_.erase(it); - } -} - -void SpdyFramer::CleanupStreamCompressorsAndDecompressors() { - CompressorMap::iterator it; - - it = stream_compressors_.begin(); - while (it != stream_compressors_.end()) { - z_stream* compressor = it->second; - deflateEnd(compressor); - delete compressor; - ++it; - } - stream_compressors_.clear(); - - it = stream_decompressors_.begin(); - while (it != stream_decompressors_.end()) { - z_stream* decompressor = it->second; - inflateEnd(decompressor); - delete decompressor; - ++it; - } - stream_decompressors_.clear(); -} - SpdyFrame* SpdyFramer::DuplicateFrame(const SpdyFrame& frame) { int size = SpdyFrame::kHeaderSize + frame.length(); SpdyFrame* new_frame = new SpdyFrame(size); diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h index cf2937c..5a31c48 100644 --- a/net/spdy/spdy_framer.h +++ b/net/spdy/spdy_framer.h @@ -216,6 +216,7 @@ class NET_EXPORT_PRIVATE SpdyFramer { SPDY_DECOMPRESS_FAILURE, // There was an error decompressing. SPDY_COMPRESS_FAILURE, // There was an error compressing. SPDY_CREDENTIAL_FRAME_CORRUPT, // CREDENTIAL frame could not be parsed. + SPDY_INVALID_DATA_FRAME_FLAGS, // Data frame has invalid flags. LAST_ERROR, // Must be the last entry in the enum. }; @@ -356,7 +357,6 @@ class NET_EXPORT_PRIVATE SpdyFramer { // |data| is the data to be included in the frame. // |len| is the length of the data // |flags| is the flags to use with the data. - // To create a compressed frame, enable DATA_FLAG_COMPRESSED. // To mark this frame as the last data frame, enable DATA_FLAG_FIN. SpdyDataFrame* CreateDataFrame(SpdyStreamId stream_id, const char* data, uint32 len, SpdyDataFlags flags); @@ -447,8 +447,6 @@ class NET_EXPORT_PRIVATE SpdyFramer { friend class test::TestSpdyVisitor; private: - typedef std::map<SpdyStreamId, z_stream*> CompressorMap; - // Internal breakouts from ProcessInput. Each returns the number of bytes // consumed from the data. size_t ProcessCommonHeader(const char* data, size_t len); @@ -466,13 +464,9 @@ class NET_EXPORT_PRIVATE SpdyFramer { // Get (and lazily initialize) the ZLib state. z_stream* GetHeaderCompressor(); z_stream* GetHeaderDecompressor(); - z_stream* GetStreamDecompressor(SpdyStreamId id); // Compression helpers SpdyControlFrame* CompressControlFrame(const SpdyControlFrame& frame); - void CleanupCompressorForStream(SpdyStreamId id); - void CleanupDecompressorForStream(SpdyStreamId id); - void CleanupStreamCompressorsAndDecompressors(); // Deliver the given control frame's compressed headers block to the visitor // in decompressed form, in chunks. Returns true if the visitor has @@ -513,9 +507,6 @@ class NET_EXPORT_PRIVATE SpdyFramer { bool GetFrameBoundaries(const SpdyFrame& frame, int* payload_length, int* header_length, const char** payload) const; - int num_stream_compressors() const { return stream_compressors_.size(); } - int num_stream_decompressors() const { return stream_decompressors_.size(); } - // The size of the control frame buffer. // Since this is only used for control frame headers, the maximum control // frame header size (SYN_STREAM) is sufficient; all remaining control @@ -554,10 +545,6 @@ class NET_EXPORT_PRIVATE SpdyFramer { scoped_ptr<z_stream> header_compressor_; scoped_ptr<z_stream> header_decompressor_; - // Per-stream data compressors. - CompressorMap stream_compressors_; - CompressorMap stream_decompressors_; - SpdyFramerVisitorInterface* visitor_; std::string display_protocol_; diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc index 738eacf..b0be6b4 100644 --- a/net/spdy/spdy_framer_test.cc +++ b/net/spdy/spdy_framer_test.cc @@ -9,14 +9,33 @@ #include "net/spdy/spdy_framer.h" #include "net/spdy/spdy_protocol.h" #include "net/spdy/spdy_frame_builder.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/platform_test.h" +using testing::_; + namespace net { namespace test { static const size_t kMaxDecompressedSize = 1024; +class MockVisitor : public SpdyFramerVisitorInterface { + public: + MOCK_METHOD1(OnError, void(SpdyFramer* framer)); + MOCK_METHOD1(OnControl, void(const SpdyControlFrame* frame)); + MOCK_METHOD3(OnControlFrameHeaderData, bool(SpdyStreamId stream_id, + const char* header_data, + size_t len)); + MOCK_METHOD2(OnCredentialFrameData, bool(const char* header_data, + size_t len)); + MOCK_METHOD1(OnDataFrameHeader, void(const SpdyDataFrame* frame)); + MOCK_METHOD3(OnStreamFrameData, void(SpdyStreamId stream_id, + const char* data, + size_t len)); + MOCK_METHOD3(OnSetting, void(SpdySettingsIds id, uint8 flags, uint32 value)); +}; + class SpdyFramerTestUtil { public: // Decompress a single frame using the decompression context held by @@ -43,7 +62,7 @@ class SpdyFramerTestUtil { class DecompressionVisitor : public SpdyFramerVisitorInterface { public: DecompressionVisitor() - : buffer_(NULL), size_(0), finished_(false), allow_data_frames_(false) { + : buffer_(NULL), size_(0), finished_(false) { } virtual void OnControl(const SpdyControlFrame* frame) { @@ -98,11 +117,7 @@ class SpdyFramerTestUtil { virtual void OnError(SpdyFramer* framer) { LOG(FATAL); } virtual void OnDataFrameHeader(const SpdyDataFrame* frame) { - // For most tests, this class does not expect to see OnDataFrameHeader - // calls. Individual tests can override this if they need to. - if (!allow_data_frames_) { - LOG(FATAL) << "Unexpected data frame header"; - } + LOG(FATAL) << "Unexpected data frame header"; } virtual void OnStreamFrameData(SpdyStreamId stream_id, const char* data, @@ -122,13 +137,11 @@ class SpdyFramerTestUtil { CHECK(finished_); return size_; } - void set_allow_data_frames(bool allow) { allow_data_frames_ = allow; } private: scoped_array<char> buffer_; size_t size_; bool finished_; - bool allow_data_frames_; DISALLOW_COPY_AND_ASSIGN(DecompressionVisitor); }; @@ -1169,12 +1182,6 @@ TEST_P(SpdyFramerTest, HeaderCompression) { EXPECT_EQ(kValue1, decompressed_headers[kHeader1]); EXPECT_EQ(kValue2, decompressed_headers[kHeader2]); EXPECT_EQ(kValue3, decompressed_headers[kHeader3]); - - // We didn't have data streams, so we shouldn't have (de)compressors. - EXPECT_EQ(0, send_framer.num_stream_compressors()); - EXPECT_EQ(0, send_framer.num_stream_decompressors()); - EXPECT_EQ(0, recv_framer.num_stream_compressors()); - EXPECT_EQ(0, recv_framer.num_stream_decompressors()); } // Verify we don't leak when we leave streams unclosed @@ -1227,12 +1234,6 @@ TEST_P(SpdyFramerTest, UnclosedStreamDataCompressors) { EXPECT_EQ(0, visitor.fin_flag_count_); EXPECT_EQ(1, visitor.zero_length_data_frame_count_); EXPECT_EQ(1, visitor.data_frame_count_); - - // We closed the streams, so all compressors should be down. - EXPECT_EQ(0, visitor.framer_.num_stream_compressors()); - EXPECT_EQ(0, visitor.framer_.num_stream_decompressors()); - EXPECT_EQ(0, send_framer.num_stream_compressors()); - EXPECT_EQ(0, send_framer.num_stream_decompressors()); } // Verify we can decompress the stream even if handed over to the @@ -1296,12 +1297,6 @@ TEST_P(SpdyFramerTest, UnclosedStreamDataCompressorsOneByteAtATime) { EXPECT_EQ(0, visitor.fin_flag_count_); EXPECT_EQ(1, visitor.zero_length_data_frame_count_); EXPECT_EQ(1, visitor.data_frame_count_); - - // We closed the streams, so all compressors should be down. - EXPECT_EQ(0, visitor.framer_.num_stream_compressors()); - EXPECT_EQ(0, visitor.framer_.num_stream_decompressors()); - EXPECT_EQ(0, send_framer.num_stream_compressors()); - EXPECT_EQ(0, send_framer.num_stream_decompressors()); } TEST_P(SpdyFramerTest, WindowUpdateFrame) { @@ -1332,8 +1327,9 @@ TEST_P(SpdyFramerTest, CreateDataFrame) { 'h', 'e', 'l', 'l', 'o' }; + const char bytes[] = "hello"; scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( - 1, "hello", 5, DATA_FLAG_NONE)); + 1, bytes, arraysize(bytes) - 1, DATA_FLAG_NONE)); CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); } @@ -2492,6 +2488,9 @@ TEST_P(SpdyFramerTest, ErrorCodeToStringTest) { EXPECT_STREQ("COMPRESS_FAILURE", SpdyFramer::ErrorCodeToString( SpdyFramer::SPDY_COMPRESS_FAILURE)); + EXPECT_STREQ("SPDY_INVALID_DATA_FRAME_FLAGS", + SpdyFramer::ErrorCodeToString( + SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS)); EXPECT_STREQ("UNKNOWN_ERROR", SpdyFramer::ErrorCodeToString(SpdyFramer::LAST_ERROR)); } @@ -2584,19 +2583,56 @@ TEST_P(SpdyFramerTest, GetMinimumControlFrameSizeTest) { } TEST_P(SpdyFramerTest, CatchProbableHttpResponse) { - SpdyFramerTestUtil::DecompressionVisitor visitor; - visitor.set_allow_data_frames(true); { + testing::StrictMock<test::MockVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); + + // This won't cause an error at the framer level. It will cause + // flag validation errors at the Visitor::OnDataFrameHeader level. + EXPECT_CALL(visitor, OnDataFrameHeader(_)); framer.ProcessInput("HTTP/1.1", 8); EXPECT_TRUE(framer.probable_http_response()); + EXPECT_EQ(SpdyFramer::SPDY_FORWARD_STREAM_FRAME, framer.state()); } { + testing::StrictMock<test::MockVisitor> visitor; SpdyFramer framer(spdy_version_); framer.set_visitor(&visitor); + + // This won't cause an error at the framer level. It will cause + // flag validation errors at the Visitor::OnDataFrameHeader level. + EXPECT_CALL(visitor, OnDataFrameHeader(_)); framer.ProcessInput("HTTP/1.0", 8); EXPECT_TRUE(framer.probable_http_response()); + EXPECT_EQ(SpdyFramer::SPDY_FORWARD_STREAM_FRAME, framer.state()); + } +} + +TEST_P(SpdyFramerTest, DataFrameFlags) { + for (int flags = 0; flags < 256; ++flags) { + SCOPED_TRACE(testing::Message() << "Flags " << flags); + + testing::StrictMock<test::MockVisitor> visitor; + SpdyFramer framer(spdy_version_); + framer.set_visitor(&visitor); + + scoped_ptr<SpdyFrame> frame( + framer.CreateDataFrame(1, "hello", 5, DATA_FLAG_NONE)); + frame->set_flags(flags); + + // Flags are just passed along since they need to be validated at + // a higher protocol layer. + EXPECT_CALL(visitor, OnDataFrameHeader(_)); + EXPECT_CALL(visitor, OnStreamFrameData(_, _, 5)); + if (flags & DATA_FLAG_FIN) { + EXPECT_CALL(visitor, OnStreamFrameData(_, _, 0)); + } + + size_t frame_size = frame->length() + SpdyFrame::kHeaderSize; + framer.ProcessInput(frame->data(), frame_size); + EXPECT_EQ(SpdyFramer::SPDY_FORWARD_STREAM_FRAME, framer.state()); + EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.error_code()); } } diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h index 989a4ed..3ea3d14 100644 --- a/net/spdy/spdy_protocol.h +++ b/net/spdy/spdy_protocol.h @@ -374,8 +374,6 @@ enum SpdyControlType { enum SpdyDataFlags { DATA_FLAG_NONE = 0, DATA_FLAG_FIN = 1, - // TODO(hkhalil): Remove. - DATA_FLAG_COMPRESSED = 2 }; // Flags on control packets diff --git a/net/tools/flip_server/spdy_interface.cc b/net/tools/flip_server/spdy_interface.cc index 969ea450..d4ed198 100644 --- a/net/tools/flip_server/spdy_interface.cc +++ b/net/tools/flip_server/spdy_interface.cc @@ -16,8 +16,6 @@ namespace net { // static -bool SpdySM::disable_data_compression_ = true; -// static std::string SpdySM::forward_ip_header_; class SpdyFrameDataFrame : public DataFrame { @@ -451,10 +449,6 @@ size_t SpdySM::SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers) { void SpdySM::SendDataFrameImpl(uint32 stream_id, const char* data, int64 len, SpdyDataFlags flags, bool compress) { - // Force compression off if disabled via command line. - if (disable_data_compression()) - flags = static_cast<SpdyDataFlags>(flags & ~DATA_FLAG_COMPRESSED); - // TODO(mbelshe): We can't compress here - before going into the // priority queue. Compression needs to be done // with late binding. diff --git a/net/tools/flip_server/spdy_interface.h b/net/tools/flip_server/spdy_interface.h index a53d577..ba02061 100644 --- a/net/tools/flip_server/spdy_interface.h +++ b/net/tools/flip_server/spdy_interface.h @@ -45,11 +45,6 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, std::string remote_ip, bool use_ssl) OVERRIDE; - static bool disable_data_compression() { return disable_data_compression_; } - static void set_disable_data_compression(bool value) { - disable_data_compression_ = value; - } - private: virtual void set_is_request() OVERRIDE {} SMInterface* NewConnectionInterface(); @@ -157,7 +152,6 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, StreamToSmif stream_to_smif_; bool close_on_error_; - static bool disable_data_compression_; static std::string forward_ip_header_; }; |