diff options
author | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-04 00:01:57 +0000 |
---|---|---|
committer | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-04 00:01:57 +0000 |
commit | 8ac2a02fd8f0e4355d70ae6c51d2c1c51f92b64a (patch) | |
tree | ab3f7ed18f31dedd18e9ea86cb77e400923c325b /net/spdy/spdy_framer.cc | |
parent | 03101106c90fb094efa760200521bcc6dbf7586e (diff) | |
download | chromium_src-8ac2a02fd8f0e4355d70ae6c51d2c1c51f92b64a.zip chromium_src-8ac2a02fd8f0e4355d70ae6c51d2c1c51f92b64a.tar.gz chromium_src-8ac2a02fd8f0e4355d70ae6c51d2c1c51f92b64a.tar.bz2 |
Merge spdy_framer changes from server.
R=willchan
TEST=network unit tests
Review URL: http://codereview.chromium.org/8437098
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@108585 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/spdy/spdy_framer.cc')
-rw-r--r-- | net/spdy/spdy_framer.cc | 1522 |
1 files changed, 755 insertions, 767 deletions
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc index 963ae43..29a1565 100644 --- a/net/spdy/spdy_framer.cc +++ b/net/spdy/spdy_framer.cc @@ -20,75 +20,25 @@ #include "third_party/zlib/zlib.h" #endif -namespace { - -// The following compression setting are based on Brian Olson's analysis. See -// https://groups.google.com/group/spdy-dev/browse_thread/thread/dfaf498542fac792 -// for more details. -const int kCompressorLevel = 9; -const int kCompressorWindowSizeInBits = 11; -const int kCompressorMemLevel = 1; - -// Adler ID for the SPDY header compressor dictionary. -uLong dictionary_id = 0; - -} // namespace - namespace spdy { -// This is just a hacked dictionary to use for shrinking HTTP-like headers. -// TODO(mbelshe): Use a scientific methodology for computing the dictionary. -const char SpdyFramer::kDictionary[] = - "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-" - "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi" - "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser" - "-agent10010120020120220320420520630030130230330430530630740040140240340440" - "5406407408409410411412413414415416417500501502503504505accept-rangesageeta" - "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic" - "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran" - "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati" - "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo" - "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe" - "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic" - "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1" - ".1statusversionurl"; -const int SpdyFramer::kDictionarySize = arraysize(kDictionary); - -// By default is compression on or off. -bool SpdyFramer::compression_default_ = true; -int SpdyFramer::spdy_version_ = kSpdyProtocolVersion; - -// The initial size of the control frame buffer; this is used internally -// as we parse through control frames. (It is exposed here for unit test -// purposes.) -size_t SpdyFramer::kControlFrameBufferInitialSize = 32 * 1024; - -// The maximum size of the control frame buffer that we support. -// TODO(mbelshe): We should make this stream-based so there are no limits. -size_t SpdyFramer::kControlFrameBufferMaxSize = 64 * 1024; - -const SpdyStreamId SpdyFramer::kInvalidStream = -1; -const size_t SpdyFramer::kHeaderDataChunkMaxSize = 1024; - -#ifdef DEBUG_SPDY_STATE_CHANGES -#define CHANGE_STATE(newstate) \ -{ \ - do { \ - LOG(INFO) << "Changing state from: " \ - << StateToString(state_) \ - << " to " << StateToString(newstate) << "\n"; \ - state_ = newstate; \ - } while (false); \ +// Compute the id of our dictionary so that we know we're using the +// right one when asked for it. +uLong CalculateDictionaryId() { + uLong initial_value = adler32(0L, Z_NULL, 0); + return adler32(initial_value, + reinterpret_cast<const Bytef*>(SpdyFramer::kDictionary), + SpdyFramer::kDictionarySize); } -#else -#define CHANGE_STATE(newstate) (state_ = newstate) -#endif + +// Adler ID for the SPDY header compressor dictionary. +const uLong kDictionaryId = CalculateDictionaryId(); int DecompressHeaderBlockInZStream(z_stream* decompressor) { int rv = inflate(decompressor, Z_SYNC_FLUSH); if (rv == Z_NEED_DICT) { // Need to try again with the right dictionary. - if (decompressor->adler == dictionary_id) { + if (decompressor->adler == kDictionaryId) { rv = inflateSetDictionary(decompressor, (const Bytef*)SpdyFramer::kDictionary, SpdyFramer::kDictionarySize); @@ -136,6 +86,37 @@ FlagsAndLength CreateFlagsAndLength(SpdyControlFlags flags, size_t length) { return flags_length; } +// By default is compression on or off. +bool SpdyFramer::compression_default_ = true; + +// The initial size of the control frame buffer; this is used internally +// as we parse through control frames. (It is exposed here for unit test +// purposes.) +size_t SpdyFramer::kControlFrameBufferInitialSize = 32 * 1024; + +// The maximum size of the control frame buffer that we support. +// TODO(mbelshe): We should make this stream-based so there are no limits. +size_t SpdyFramer::kControlFrameBufferMaxSize = 64 * 1024; + +int SpdyFramer::spdy_version_ = kSpdyProtocolVersion; + +const SpdyStreamId SpdyFramer::kInvalidStream = -1; +const size_t SpdyFramer::kHeaderDataChunkMaxSize = 1024; + +#ifdef DEBUG_SPDY_STATE_CHANGES +#define CHANGE_STATE(newstate) \ +{ \ + do { \ + LOG(INFO) << "Changing state from: " \ + << StateToString(state_) \ + << " to " << StateToString(newstate) << "\n"; \ + state_ = newstate; \ + } while (false); \ +} +#else +#define CHANGE_STATE(newstate) (state_ = newstate) +#endif + SpdyFramer::SpdyFramer() : state_(SPDY_RESET), error_code_(SPDY_NO_ERROR), @@ -147,7 +128,8 @@ SpdyFramer::SpdyFramer() current_frame_capacity_(0), validate_control_frame_sizes_(true), enable_compression_(compression_default_), - visitor_(NULL) { + visitor_(NULL), + display_protocol_("SPDY") { } SpdyFramer::~SpdyFramer() { @@ -161,6 +143,81 @@ SpdyFramer::~SpdyFramer() { delete [] current_frame_buffer_; } +void SpdyFramer::Reset() { + state_ = SPDY_RESET; + error_code_ = SPDY_NO_ERROR; + remaining_data_ = 0; + remaining_control_payload_ = 0; + remaining_control_header_ = 0; + current_frame_len_ = 0; + // TODO(hkhalil): Remove once initial_size == kControlFrameBufferInitialSize. + size_t initial_size = kControlFrameBufferInitialSize; + if (!enable_compression_) { + initial_size = kUncompressedControlFrameBufferInitialSize; + } + if (current_frame_capacity_ != initial_size) { + delete [] current_frame_buffer_; + current_frame_buffer_ = 0; + current_frame_capacity_ = 0; + ExpandControlFrameBuffer(initial_size); + } +} + +const char* SpdyFramer::StateToString(int state) { + switch (state) { + case SPDY_ERROR: + return "ERROR"; + case SPDY_DONE: + return "DONE"; + case SPDY_AUTO_RESET: + return "AUTO_RESET"; + case SPDY_RESET: + return "RESET"; + case SPDY_READING_COMMON_HEADER: + return "READING_COMMON_HEADER"; + case SPDY_INTERPRET_CONTROL_FRAME_COMMON_HEADER: + return "INTERPRET_CONTROL_FRAME_COMMON_HEADER"; + case SPDY_CONTROL_FRAME_PAYLOAD: + return "CONTROL_FRAME_PAYLOAD"; + case SPDY_IGNORE_REMAINING_PAYLOAD: + return "IGNORE_REMAINING_PAYLOAD"; + case SPDY_FORWARD_STREAM_FRAME: + return "FORWARD_STREAM_FRAME"; + case SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK: + return "SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK"; + case SPDY_CONTROL_FRAME_HEADER_BLOCK: + return "SPDY_CONTROL_FRAME_HEADER_BLOCK"; + } + return "UNKNOWN_STATE"; +} + +void SpdyFramer::set_error(SpdyError error) { + DCHECK(visitor_); + error_code_ = error; + CHANGE_STATE(SPDY_ERROR); + visitor_->OnError(this); +} + +const char* SpdyFramer::ErrorCodeToString(int error_code) { + switch (error_code) { + case SPDY_NO_ERROR: + return "NO_ERROR"; + case SPDY_INVALID_CONTROL_FRAME: + return "INVALID_CONTROL_FRAME"; + case SPDY_CONTROL_PAYLOAD_TOO_LARGE: + return "CONTROL_PAYLOAD_TOO_LARGE"; + case SPDY_ZLIB_INIT_FAILURE: + return "ZLIB_INIT_FAILURE"; + case SPDY_UNSUPPORTED_VERSION: + return "UNSUPPORTED_VERSION"; + case SPDY_DECOMPRESS_FAILURE: + return "DECOMPRESS_FAILURE"; + case SPDY_COMPRESS_FAILURE: + return "COMPRESS_FAILURE"; + } + return "UNKNOWN_ERROR"; +} + const char* SpdyFramer::StatusCodeToString(int status_code) { switch (status_code) { case INVALID: @@ -289,84 +346,142 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) { return original_len - len; } -void SpdyFramer::Reset() { - state_ = SPDY_RESET; - error_code_ = SPDY_NO_ERROR; - remaining_data_ = 0; - remaining_control_payload_ = 0; - remaining_control_header_ = 0; - current_frame_len_ = 0; - if (current_frame_capacity_ != kControlFrameBufferInitialSize) { - delete [] current_frame_buffer_; - current_frame_buffer_ = 0; - current_frame_capacity_ = 0; - ExpandControlFrameBuffer(kControlFrameBufferInitialSize); - } +size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { + // This should only be called when we're in the SPDY_READING_COMMON_HEADER + // state. + DCHECK_EQ(state_, SPDY_READING_COMMON_HEADER); + + size_t original_len = len; + SpdyFrame current_frame(current_frame_buffer_, false); + + do { + if (current_frame_len_ < SpdyFrame::size()) { + size_t bytes_desired = SpdyFrame::size() - current_frame_len_; + UpdateCurrentFrameBuffer(&data, &len, bytes_desired); + // Check for an empty data frame. + if (current_frame_len_ == SpdyFrame::size() && + !current_frame.is_control_frame() && + current_frame.length() == 0) { + if (current_frame.flags() & CONTROL_FLAG_FIN) { + SpdyDataFrame data_frame(current_frame_buffer_, false); + visitor_->OnStreamFrameData(data_frame.stream_id(), NULL, 0); + } + CHANGE_STATE(SPDY_AUTO_RESET); + } + break; + } + remaining_data_ = current_frame.length(); + + // This is just a sanity check for help debugging early frame errors. + if (remaining_data_ > 1000000u) { + LOG(WARNING) << + "Unexpectedly large frame. Spdy session is likely corrupt."; + } + + // if we're here, then we have the common header all received. + if (!current_frame.is_control_frame()) + CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME); + else + CHANGE_STATE(SPDY_INTERPRET_CONTROL_FRAME_COMMON_HEADER); + } while (false); + + return original_len - len; } -bool SpdyFramer::ParseHeaderBlock(const SpdyFrame* frame, - SpdyHeaderBlock* block) { - SpdyControlFrame control_frame(frame->data(), false); - uint32 type = control_frame.type(); - if (type != SYN_STREAM && type != SYN_REPLY && type != HEADERS) - return false; +void SpdyFramer::ProcessControlFrameHeader() { + DCHECK_EQ(SPDY_NO_ERROR, error_code_); + DCHECK_LE(SpdyFrame::size(), current_frame_len_); + SpdyControlFrame current_control_frame(current_frame_buffer_, false); - // Find the header data within the control frame. - scoped_ptr<SpdyFrame> decompressed_frame(DecompressFrame(*frame)); - if (!decompressed_frame.get()) - return false; + // We check version before we check validity: version can never be 'invalid', + // it can only be unsupported. + if (current_control_frame.version() != spdy_version_) { + set_error(SPDY_UNSUPPORTED_VERSION); + return; + } - const char *header_data = NULL; - int header_length = 0; + // Next up, check to see if we have valid data. This should be after version + // checking (otherwise if the the type were out of bounds due to a version + // upgrade we would misclassify the error) and before checking the type + // (type can definitely be out of bounds) + if (!current_control_frame.AppearsToBeAValidControlFrame()) { + set_error(SPDY_INVALID_CONTROL_FRAME); + return; + } - switch (type) { - case SYN_STREAM: - { - SpdySynStreamControlFrame syn_frame(decompressed_frame->data(), false); - header_data = syn_frame.header_block(); - header_length = syn_frame.header_block_len(); - } - break; - case SYN_REPLY: - { - SpdySynReplyControlFrame syn_frame(decompressed_frame->data(), false); - header_data = syn_frame.header_block(); - header_length = syn_frame.header_block_len(); - } - break; - case HEADERS: - { - SpdyHeadersControlFrame header_frame(decompressed_frame->data(), false); - header_data = header_frame.header_block(); - header_length = header_frame.header_block_len(); - } - break; + if (current_control_frame.type() == NOOP) { + DLOG(INFO) << "NOOP control frame found. Ignoring."; + CHANGE_STATE(SPDY_AUTO_RESET); + return; } - SpdyFrameBuilder builder(header_data, header_length); - void* iter = NULL; - uint16 num_headers; - if (builder.ReadUInt16(&iter, &num_headers)) { - int index; - for (index = 0; index < num_headers; ++index) { - std::string name; - std::string value; - if (!builder.ReadString(&iter, &name)) + if (validate_control_frame_sizes_) { + // Do some sanity checking on the control frame sizes. + switch (current_control_frame.type()) { + case SYN_STREAM: + if (current_control_frame.length() < + SpdySynStreamControlFrame::size() - SpdyControlFrame::size()) + set_error(SPDY_INVALID_CONTROL_FRAME); break; - if (!builder.ReadString(&iter, &value)) + case SYN_REPLY: + if (current_control_frame.length() < + SpdySynReplyControlFrame::size() - SpdyControlFrame::size()) + set_error(SPDY_INVALID_CONTROL_FRAME); + break; + case RST_STREAM: + if (current_control_frame.length() != + SpdyRstStreamControlFrame::size() - SpdyFrame::size()) + set_error(SPDY_INVALID_CONTROL_FRAME); + break; + case SETTINGS: + if (current_control_frame.length() < + SpdySettingsControlFrame::size() - SpdyControlFrame::size()) + set_error(SPDY_INVALID_CONTROL_FRAME); + break; + case GOAWAY: + if (current_control_frame.length() != + SpdyGoAwayControlFrame::size() - SpdyFrame::size()) + set_error(SPDY_INVALID_CONTROL_FRAME); + break; + case HEADERS: + if (current_control_frame.length() < + SpdyHeadersControlFrame::size() - SpdyControlFrame::size()) + set_error(SPDY_INVALID_CONTROL_FRAME); + break; + case WINDOW_UPDATE: + if (current_control_frame.length() != + SpdyWindowUpdateControlFrame::size() - SpdyControlFrame::size()) + set_error(SPDY_INVALID_CONTROL_FRAME); + break; + case PING: + if (current_control_frame.length() != + SpdyPingControlFrame::size() - SpdyControlFrame::size()) + set_error(SPDY_INVALID_CONTROL_FRAME); + break; + default: + LOG(WARNING) << "Valid " << display_protocol_ + << " control frame with unhandled type: " + << current_control_frame.type(); + DCHECK(false); + set_error(SPDY_INVALID_CONTROL_FRAME); break; - if (!name.size() || !value.size()) - return false; - if (block->find(name) == block->end()) { - (*block)[name] = value; - } else { - return false; - } } - return index == num_headers && - iter == header_data + header_length; } - return false; + + // We only support version 1 of this protocol. + if (current_control_frame.version() != spdy_version_) { + set_error(SPDY_UNSUPPORTED_VERSION); + return; + } + + remaining_control_payload_ = current_control_frame.length(); + if (remaining_control_payload_ > kControlFrameBufferMaxSize) { + set_error(SPDY_CONTROL_PAYLOAD_TOO_LARGE); + return; + } + + ExpandControlFrameBuffer(remaining_control_payload_); + CHANGE_STATE(SPDY_CONTROL_FRAME_PAYLOAD); } size_t SpdyFramer::UpdateCurrentFrameBuffer(const char** data, size_t* len, @@ -419,7 +534,7 @@ size_t SpdyFramer::NewProcessControlFrameHeaderBlock(const char* data, DCHECK_GT(process_bytes, 0u); if (enable_compression_) { - processed_successfully = IncrementallyDecompressControlFrameHeaderData( + processed_successfully = NewIncrementallyDecompressControlFrameHeaderData( &control_frame, data, process_bytes); } else { processed_successfully = IncrementallyDeliverControlFrameHeaderData( @@ -503,6 +618,107 @@ size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data, return original_data_len - data_len; } +size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) { + size_t original_len = len; + do { + 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_) + break; + } + SpdyControlFrame control_frame(current_frame_buffer_, false); + visitor_->OnControl(&control_frame); + + // If this is a FIN, tell the caller. + if (control_frame.type() == SYN_REPLY && + control_frame.flags() & CONTROL_FLAG_FIN) { + visitor_->OnStreamFrameData(reinterpret_cast<SpdySynReplyControlFrame*>( + &control_frame)->stream_id(), + NULL, 0); + } + + CHANGE_STATE(SPDY_IGNORE_REMAINING_PAYLOAD); + } while (false); + return original_len - len; +} + +size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) { + size_t original_len = len; + + SpdyDataFrame current_data_frame(current_frame_buffer_, false); + 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); + } + } + data += amount_to_forward; + len -= amount_to_forward; + remaining_data_ -= 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_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); + } + return original_len - len; +} + +void SpdyFramer::ExpandControlFrameBuffer(size_t size) { + size_t alloc_size = size + SpdyFrame::size(); + DCHECK_LE(alloc_size, kControlFrameBufferMaxSize); + if (alloc_size <= current_frame_capacity_) + return; + char* new_buffer = new char[alloc_size]; + memcpy(new_buffer, current_frame_buffer_, current_frame_len_); + delete [] current_frame_buffer_; + current_frame_capacity_ = alloc_size; + current_frame_buffer_ = new_buffer; +} + /* static */ bool SpdyFramer::ParseHeaderBlockInBuffer(const char* header_data, size_t header_length, @@ -529,39 +745,115 @@ bool SpdyFramer::ParseHeaderBlockInBuffer(const char* header_data, return false; } +bool SpdyFramer::ParseHeaderBlock(const SpdyFrame* frame, + SpdyHeaderBlock* block) { + SpdyControlFrame control_frame(frame->data(), false); + uint32 type = control_frame.type(); + if (type != SYN_STREAM && type != SYN_REPLY && type != HEADERS) + return false; + + // Find the header data within the control frame. + scoped_ptr<SpdyFrame> decompressed_frame(DecompressFrame(*frame)); + if (!decompressed_frame.get()) + return false; + + const char *header_data = NULL; + int header_length = 0; + + switch (type) { + case SYN_STREAM: + { + SpdySynStreamControlFrame syn_frame(decompressed_frame->data(), false); + header_data = syn_frame.header_block(); + header_length = syn_frame.header_block_len(); + } + break; + case SYN_REPLY: + { + SpdySynReplyControlFrame syn_frame(decompressed_frame->data(), false); + header_data = syn_frame.header_block(); + header_length = syn_frame.header_block_len(); + } + break; + case HEADERS: + { + SpdyHeadersControlFrame header_frame(decompressed_frame->data(), false); + header_data = header_frame.header_block(); + header_length = header_frame.header_block_len(); + } + break; + } + + SpdyFrameBuilder builder(header_data, header_length); + void* iter = NULL; + uint16 num_headers; + if (builder.ReadUInt16(&iter, &num_headers)) { + int index; + for (index = 0; index < num_headers; ++index) { + std::string name; + std::string value; + if (!builder.ReadString(&iter, &name)) + break; + if (!builder.ReadString(&iter, &value)) + break; + if (!name.size() || !value.size()) + return false; + if (block->find(name) == block->end()) { + (*block)[name] = value; + } else { + return false; + } + } + return index == num_headers && + iter == header_data + header_length; + } + return false; +} + +/* static */ +bool SpdyFramer::ParseSettings(const SpdySettingsControlFrame* frame, + SpdySettings* settings) { + DCHECK_EQ(frame->type(), SETTINGS); + DCHECK(settings); + + SpdyFrameBuilder parser(frame->header_block(), frame->header_block_len()); + void* iter = NULL; + for (size_t index = 0; index < frame->num_entries(); ++index) { + uint32 id; + uint32 value; + if (!parser.ReadUInt32(&iter, &id)) + return false; + if (!parser.ReadUInt32(&iter, &value)) + return false; + settings->insert(settings->end(), std::make_pair(id, value)); + } + return true; +} + SpdySynStreamControlFrame* SpdyFramer::CreateSynStream( SpdyStreamId stream_id, SpdyStreamId associated_stream_id, int priority, SpdyControlFlags flags, bool compressed, const SpdyHeaderBlock* headers) { - SpdyFrameBuilder frame; - DCHECK_GT(stream_id, static_cast<SpdyStreamId>(0)); DCHECK_EQ(0u, stream_id & ~kStreamIdMask); DCHECK_EQ(0u, associated_stream_id & ~kStreamIdMask); + // Find our length. + size_t expected_frame_size = SpdySynStreamControlFrame::size() + + GetSerializedLength(headers); + + // Create our FlagsAndLength. + FlagsAndLength flags_length = CreateFlagsAndLength( + flags, + expected_frame_size - SpdyFrame::size()); + + SpdyFrameBuilder frame(expected_frame_size); frame.WriteUInt16(kControlFlagMask | spdy_version_); frame.WriteUInt16(SYN_STREAM); - frame.WriteUInt32(0); // Placeholder for the length and flags + frame.WriteBytes(&flags_length, sizeof(flags_length)); frame.WriteUInt32(stream_id); frame.WriteUInt32(associated_stream_id); frame.WriteUInt16(ntohs(priority) << 6); // Priority. - - frame.WriteUInt16(headers->size()); // Number of headers. - SpdyHeaderBlock::const_iterator it; - for (it = headers->begin(); it != headers->end(); ++it) { - bool wrote_header; - wrote_header = frame.WriteString(it->first); - wrote_header &= frame.WriteString(it->second); - DCHECK(wrote_header); - } - - // Write the length and flags. - size_t length = frame.length() - SpdyFrame::size(); - DCHECK_EQ(0u, length & ~static_cast<size_t>(kLengthMask)); - FlagsAndLength flags_length; - flags_length.length_ = htonl(static_cast<uint32>(length)); - DCHECK_EQ(0, flags & ~kControlFlagsMask); - flags_length.flags_[0] = flags; - frame.WriteBytesToOffset(4, &flags_length, sizeof(flags_length)); + WriteHeaderBlock(&frame, headers); scoped_ptr<SpdySynStreamControlFrame> syn_frame( reinterpret_cast<SpdySynStreamControlFrame*>(frame.take())); @@ -577,31 +869,22 @@ SpdySynReplyControlFrame* SpdyFramer::CreateSynReply(SpdyStreamId stream_id, DCHECK_GT(stream_id, 0u); DCHECK_EQ(0u, stream_id & ~kStreamIdMask); - SpdyFrameBuilder frame; + // Find our length. + size_t expected_frame_size = SpdySynReplyControlFrame::size() + + GetSerializedLength(headers); + // Create our FlagsAndLength. + FlagsAndLength flags_length = CreateFlagsAndLength( + flags, + expected_frame_size - SpdyFrame::size()); + + SpdyFrameBuilder frame(expected_frame_size); frame.WriteUInt16(kControlFlagMask | spdy_version_); frame.WriteUInt16(SYN_REPLY); - frame.WriteUInt32(0); // Placeholder for the length and flags. + frame.WriteBytes(&flags_length, sizeof(flags_length)); frame.WriteUInt32(stream_id); frame.WriteUInt16(0); // Unused - - frame.WriteUInt16(headers->size()); // Number of headers. - SpdyHeaderBlock::const_iterator it; - for (it = headers->begin(); it != headers->end(); ++it) { - bool wrote_header; - wrote_header = frame.WriteString(it->first); - wrote_header &= frame.WriteString(it->second); - DCHECK(wrote_header); - } - - // Write the length and flags. - size_t length = frame.length() - SpdyFrame::size(); - DCHECK_EQ(0u, length & ~static_cast<size_t>(kLengthMask)); - FlagsAndLength flags_length; - flags_length.length_ = htonl(static_cast<uint32>(length)); - DCHECK_EQ(0, flags & ~kControlFlagsMask); - flags_length.flags_[0] = flags; - frame.WriteBytesToOffset(4, &flags_length, sizeof(flags_length)); + WriteHeaderBlock(&frame, headers); scoped_ptr<SpdySynReplyControlFrame> reply_frame( reinterpret_cast<SpdySynReplyControlFrame*>(frame.take())); @@ -620,7 +903,7 @@ SpdyRstStreamControlFrame* SpdyFramer::CreateRstStream(SpdyStreamId stream_id, DCHECK_NE(status, INVALID); DCHECK_LT(status, NUM_STATUS_CODES); - SpdyFrameBuilder frame; + SpdyFrameBuilder frame(SpdyRstStreamControlFrame::size()); frame.WriteUInt16(kControlFlagMask | spdy_version_); frame.WriteUInt16(RST_STREAM); frame.WriteUInt32(8); @@ -632,7 +915,7 @@ SpdyRstStreamControlFrame* SpdyFramer::CreateRstStream(SpdyStreamId stream_id, /* static */ SpdySettingsControlFrame* SpdyFramer::CreateSettings( const SpdySettings& values) { - SpdyFrameBuilder frame; + SpdyFrameBuilder frame(SpdySettingsControlFrame::size() + 8 * values.size()); frame.WriteUInt16(kControlFlagMask | spdy_version_); frame.WriteUInt16(SETTINGS); size_t settings_size = SpdySettingsControlFrame::size() - SpdyFrame::size() + @@ -650,7 +933,7 @@ SpdySettingsControlFrame* SpdyFramer::CreateSettings( /* static */ SpdyNoOpControlFrame* SpdyFramer::CreateNopFrame() { - SpdyFrameBuilder frame; + SpdyFrameBuilder frame(SpdyNoOpControlFrame::size()); frame.WriteUInt16(kControlFlagMask | spdy_version_); frame.WriteUInt16(NOOP); frame.WriteUInt32(0); @@ -659,8 +942,8 @@ SpdyNoOpControlFrame* SpdyFramer::CreateNopFrame() { /* static */ SpdyPingControlFrame* SpdyFramer::CreatePingFrame(uint32 unique_id) { - SpdyFrameBuilder frame; - frame.WriteUInt16(kControlFlagMask | kSpdyProtocolVersion); + SpdyFrameBuilder frame(SpdyPingControlFrame::size()); + frame.WriteUInt16(kControlFlagMask | spdy_version_); frame.WriteUInt16(PING); size_t ping_size = SpdyPingControlFrame::size() - SpdyFrame::size(); frame.WriteUInt32(ping_size); @@ -673,7 +956,7 @@ SpdyGoAwayControlFrame* SpdyFramer::CreateGoAway( SpdyStreamId last_accepted_stream_id) { DCHECK_EQ(0u, last_accepted_stream_id & ~kStreamIdMask); - SpdyFrameBuilder frame; + SpdyFrameBuilder frame(SpdyGoAwayControlFrame::size()); frame.WriteUInt16(kControlFlagMask | spdy_version_); frame.WriteUInt16(GOAWAY); size_t go_away_size = SpdyGoAwayControlFrame::size() - SpdyFrame::size(); @@ -688,30 +971,23 @@ SpdyHeadersControlFrame* SpdyFramer::CreateHeaders(SpdyStreamId stream_id, DCHECK_GT(stream_id, 0u); DCHECK_EQ(0u, stream_id & ~kStreamIdMask); - SpdyFrameBuilder frame; - frame.WriteUInt16(kControlFlagMask | kSpdyProtocolVersion); + // Find our length. + size_t expected_frame_size = SpdyHeadersControlFrame::size() + + GetSerializedLength(headers); + + // Create our FlagsAndLength. + FlagsAndLength flags_length = CreateFlagsAndLength( + flags, + expected_frame_size - SpdyFrame::size()); + + SpdyFrameBuilder frame(expected_frame_size); + frame.WriteUInt16(kControlFlagMask | spdy_version_); frame.WriteUInt16(HEADERS); - frame.WriteUInt32(0); // Placeholder for the length and flags. + frame.WriteBytes(&flags_length, sizeof(flags_length)); frame.WriteUInt32(stream_id); frame.WriteUInt16(0); // Unused - - frame.WriteUInt16(headers->size()); // Number of headers. - SpdyHeaderBlock::const_iterator it; - for (it = headers->begin(); it != headers->end(); ++it) { - bool wrote_header; - wrote_header = frame.WriteString(it->first); - wrote_header &= frame.WriteString(it->second); - DCHECK(wrote_header); - } - - // Write the length and flags. - size_t length = frame.length() - SpdyFrame::size(); - DCHECK_EQ(0u, length & ~static_cast<size_t>(kLengthMask)); - FlagsAndLength flags_length; - flags_length.length_ = htonl(static_cast<uint32>(length)); - DCHECK_EQ(0, flags & ~kControlFlagsMask); - flags_length.flags_[0] = flags; - frame.WriteBytesToOffset(4, &flags_length, sizeof(flags_length)); + WriteHeaderBlock(&frame, headers); + DCHECK_EQ(static_cast<size_t>(frame.length()), expected_frame_size); scoped_ptr<SpdyHeadersControlFrame> headers_frame( reinterpret_cast<SpdyHeadersControlFrame*>(frame.take())); @@ -731,7 +1007,7 @@ SpdyWindowUpdateControlFrame* SpdyFramer::CreateWindowUpdate( DCHECK_GT(delta_window_size, 0u); DCHECK_LE(delta_window_size, spdy::kSpdyStreamMaximumWindowSize); - SpdyFrameBuilder frame; + SpdyFrameBuilder frame(SpdyWindowUpdateControlFrame::size()); frame.WriteUInt16(kControlFlagMask | spdy_version_); frame.WriteUInt16(WINDOW_UPDATE); size_t window_update_size = SpdyWindowUpdateControlFrame::size() - @@ -742,33 +1018,13 @@ SpdyWindowUpdateControlFrame* SpdyFramer::CreateWindowUpdate( return reinterpret_cast<SpdyWindowUpdateControlFrame*>(frame.take()); } -/* static */ -bool SpdyFramer::ParseSettings(const SpdySettingsControlFrame* frame, - SpdySettings* settings) { - DCHECK_EQ(frame->type(), SETTINGS); - DCHECK(settings); - - SpdyFrameBuilder parser(frame->header_block(), frame->header_block_len()); - void* iter = NULL; - for (size_t index = 0; index < frame->num_entries(); ++index) { - uint32 id; - uint32 value; - if (!parser.ReadUInt32(&iter, &id)) - return false; - if (!parser.ReadUInt32(&iter, &value)) - return false; - settings->insert(settings->end(), std::make_pair(id, value)); - } - return true; -} - SpdyDataFrame* SpdyFramer::CreateDataFrame(SpdyStreamId stream_id, const char* data, uint32 len, SpdyDataFlags flags) { - SpdyFrameBuilder frame; - DCHECK_GT(stream_id, 0u); DCHECK_EQ(0u, stream_id & ~kStreamIdMask); + + SpdyFrameBuilder frame(SpdyDataFrame::size() + len); frame.WriteUInt32(stream_id); DCHECK_EQ(0u, len & ~static_cast<size_t>(kLengthMask)); @@ -794,6 +1050,31 @@ SpdyDataFrame* SpdyFramer::CreateDataFrame(SpdyStreamId stream_id, return rv; } +// The following compression setting are based on Brian Olson's analysis. See +// https://groups.google.com/group/spdy-dev/browse_thread/thread/dfaf498542fac792 +// for more details. +static const int kCompressorLevel = 9; +static const int kCompressorWindowSizeInBits = 11; +static const int kCompressorMemLevel = 1; + +// This is just a hacked dictionary to use for shrinking HTTP-like headers. +// TODO(mbelshe): Use a scientific methodology for computing the dictionary. +const char SpdyFramer::kDictionary[] = + "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-" + "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi" + "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser" + "-agent10010120020120220320420520630030130230330430530630740040140240340440" + "5406407408409410411412413414415416417500501502503504505accept-rangesageeta" + "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic" + "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran" + "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati" + "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo" + "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe" + "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic" + "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1" + ".1statusversionurl"; +const int SpdyFramer::kDictionarySize = arraysize(kDictionary); + SpdyFrame* SpdyFramer::CompressFrame(const SpdyFrame& frame) { if (frame.is_control_frame()) { return CompressControlFrame( @@ -810,13 +1091,6 @@ SpdyFrame* SpdyFramer::DecompressFrame(const SpdyFrame& frame) { return DecompressDataFrame(reinterpret_cast<const SpdyDataFrame&>(frame)); } -SpdyFrame* SpdyFramer::DuplicateFrame(const SpdyFrame& frame) { - int size = SpdyFrame::size() + frame.length(); - SpdyFrame* new_frame = new SpdyFrame(size); - memcpy(new_frame->data(), frame.data(), size); - return new_frame; -} - bool SpdyFramer::IsCompressible(const SpdyFrame& frame) const { // The important frames to compress are those which contain large // amounts of compressible data - namely the headers in the SYN_STREAM @@ -835,280 +1109,6 @@ bool SpdyFramer::IsCompressible(const SpdyFrame& frame) const { return (data_frame.flags() & DATA_FLAG_COMPRESSED) != 0; } -const char* SpdyFramer::StateToString(int state) { - switch (state) { - case SPDY_ERROR: - return "ERROR"; - case SPDY_DONE: - return "DONE"; - case SPDY_AUTO_RESET: - return "AUTO_RESET"; - case SPDY_RESET: - return "RESET"; - case SPDY_READING_COMMON_HEADER: - return "READING_COMMON_HEADER"; - case SPDY_INTERPRET_CONTROL_FRAME_COMMON_HEADER: - return "INTERPRET_CONTROL_FRAME_COMMON_HEADER"; - case SPDY_CONTROL_FRAME_PAYLOAD: - return "CONTROL_FRAME_PAYLOAD"; - case SPDY_IGNORE_REMAINING_PAYLOAD: - return "IGNORE_REMAINING_PAYLOAD"; - case SPDY_FORWARD_STREAM_FRAME: - return "FORWARD_STREAM_FRAME"; - case SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK: - return "SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK"; - case SPDY_CONTROL_FRAME_HEADER_BLOCK: - return "SPDY_CONTROL_FRAME_HEADER_BLOCK"; - } - return "UNKNOWN_STATE"; -} - -const char* SpdyFramer::ErrorCodeToString(int error_code) { - switch (error_code) { - case SPDY_NO_ERROR: - return "NO_ERROR"; - case SPDY_INVALID_CONTROL_FRAME: - return "INVALID_CONTROL_FRAME"; - case SPDY_CONTROL_PAYLOAD_TOO_LARGE: - return "CONTROL_PAYLOAD_TOO_LARGE"; - case SPDY_ZLIB_INIT_FAILURE: - return "ZLIB_INIT_FAILURE"; - case SPDY_UNSUPPORTED_VERSION: - return "UNSUPPORTED_VERSION"; - case SPDY_DECOMPRESS_FAILURE: - return "DECOMPRESS_FAILURE"; - case SPDY_COMPRESS_FAILURE: - return "COMPRESS_FAILURE"; - } - return "UNKNOWN_ERROR"; -} - -void SpdyFramer::set_enable_compression(bool value) { - enable_compression_ = value; -} - -void SpdyFramer::set_enable_compression_default(bool value) { - compression_default_ = value; -} - -size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { - // This should only be called when we're in the SPDY_READING_COMMON_HEADER - // state. - DCHECK_EQ(state_, SPDY_READING_COMMON_HEADER); - - size_t original_len = len; - SpdyFrame current_frame(current_frame_buffer_, false); - - do { - if (current_frame_len_ < SpdyFrame::size()) { - size_t bytes_desired = SpdyFrame::size() - current_frame_len_; - UpdateCurrentFrameBuffer(&data, &len, bytes_desired); - // Check for an empty data frame. - if (current_frame_len_ == SpdyFrame::size() && - !current_frame.is_control_frame() && - current_frame.length() == 0) { - if (current_frame.flags() & CONTROL_FLAG_FIN) { - SpdyDataFrame data_frame(current_frame_buffer_, false); - visitor_->OnStreamFrameData(data_frame.stream_id(), NULL, 0); - } - CHANGE_STATE(SPDY_AUTO_RESET); - } - break; - } - remaining_data_ = current_frame.length(); - - // This is just a sanity check for help debugging early frame errors. - if (remaining_data_ > 1000000u) { - LOG(WARNING) << - "Unexpectedly large frame. Spdy session is likely corrupt."; - } - - // if we're here, then we have the common header all received. - if (!current_frame.is_control_frame()) - CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME); - else - CHANGE_STATE(SPDY_INTERPRET_CONTROL_FRAME_COMMON_HEADER); - } while (false); - - return original_len - len; -} - -void SpdyFramer::ProcessControlFrameHeader() { - DCHECK_EQ(SPDY_NO_ERROR, error_code_); - DCHECK_LE(SpdyFrame::size(), current_frame_len_); - SpdyControlFrame current_control_frame(current_frame_buffer_, false); - - // We check version before we check validity: version can never be 'invalid', - // it can only be unsupported. - if (current_control_frame.version() != spdy_version_) { - set_error(SPDY_UNSUPPORTED_VERSION); - return; - } - - // Next up, check to see if we have valid data. This should be after version - // checking (otherwise if the the type were out of bounds due to a version - // upgrade we would misclassify the error) and before checking the type - // (type can definitely be out of bounds) - if (!current_control_frame.AppearsToBeAValidControlFrame()) { - set_error(SPDY_INVALID_CONTROL_FRAME); - return; - } - - // Do some sanity checking on the control frame sizes. - switch (current_control_frame.type()) { - case SYN_STREAM: - if (current_control_frame.length() < - SpdySynStreamControlFrame::size() - SpdyControlFrame::size()) - set_error(SPDY_INVALID_CONTROL_FRAME); - break; - case SYN_REPLY: - if (current_control_frame.length() < - SpdySynReplyControlFrame::size() - SpdyControlFrame::size()) - set_error(SPDY_INVALID_CONTROL_FRAME); - break; - case RST_STREAM: - if (current_control_frame.length() != - SpdyRstStreamControlFrame::size() - SpdyFrame::size()) - set_error(SPDY_INVALID_CONTROL_FRAME); - break; - case SETTINGS: - if (current_control_frame.length() < - SpdySettingsControlFrame::size() - SpdyControlFrame::size()) - set_error(SPDY_INVALID_CONTROL_FRAME); - break; - // TODO(hkhalil): Remove NOOP. - case NOOP: - // NOOP. Swallow it. - DLOG(INFO) << "Attempted frame size validation for NOOP. Resetting."; - CHANGE_STATE(SPDY_AUTO_RESET); - return; - case GOAWAY: - if (current_control_frame.length() != - SpdyGoAwayControlFrame::size() - SpdyFrame::size()) - set_error(SPDY_INVALID_CONTROL_FRAME); - break; - case HEADERS: - if (current_control_frame.length() < - SpdyHeadersControlFrame::size() - SpdyControlFrame::size()) - set_error(SPDY_INVALID_CONTROL_FRAME); - break; - case WINDOW_UPDATE: - if (current_control_frame.length() != - SpdyWindowUpdateControlFrame::size() - SpdyControlFrame::size()) - set_error(SPDY_INVALID_CONTROL_FRAME); - break; - case PING: - if (current_control_frame.length() != - SpdyPingControlFrame::size() - SpdyControlFrame::size()) - set_error(SPDY_INVALID_CONTROL_FRAME); - break; - default: - LOG(WARNING) << "Valid spdy control frame with unhandled type: " - << current_control_frame.type(); - DCHECK(false); - set_error(SPDY_INVALID_CONTROL_FRAME); - break; - } - - remaining_control_payload_ = current_control_frame.length(); - if (remaining_control_payload_ > kControlFrameBufferMaxSize) { - set_error(SPDY_CONTROL_PAYLOAD_TOO_LARGE); - return; - } - - ExpandControlFrameBuffer(remaining_control_payload_); - CHANGE_STATE(SPDY_CONTROL_FRAME_PAYLOAD); -} - -size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) { - size_t original_len = len; - do { - 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_) - break; - } - SpdyControlFrame control_frame(current_frame_buffer_, false); - visitor_->OnControl(&control_frame); - - // If this is a FIN, tell the caller. - if (control_frame.type() == SYN_REPLY && - control_frame.flags() & CONTROL_FLAG_FIN) { - visitor_->OnStreamFrameData(reinterpret_cast<SpdySynReplyControlFrame*>( - &control_frame)->stream_id(), - NULL, 0); - } - - CHANGE_STATE(SPDY_IGNORE_REMAINING_PAYLOAD); - } while (false); - return original_len - len; -} - -size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) { - size_t original_len = len; - - SpdyDataFrame current_data_frame(current_frame_buffer_, false); - 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); - } - } - data += amount_to_forward; - len -= amount_to_forward; - remaining_data_ -= 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_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); - } - return original_len - len; -} - z_stream* SpdyFramer::GetHeaderCompressor() { if (header_compressor_.get()) return header_compressor_.get(); // Already initialized. @@ -1141,15 +1141,6 @@ z_stream* SpdyFramer::GetHeaderDecompressor() { header_decompressor_.reset(new z_stream); memset(header_decompressor_.get(), 0, sizeof(z_stream)); - // Compute the id of our dictionary so that we know we're using the - // right one when asked for it. - if (dictionary_id == 0) { - dictionary_id = adler32(0L, Z_NULL, 0); - dictionary_id = adler32(dictionary_id, - reinterpret_cast<const Bytef*>(kDictionary), - kDictionarySize); - } - int success = inflateInit(header_decompressor_.get()); if (success != Z_OK) { LOG(WARNING) << "inflateInit failure: " << success; @@ -1196,6 +1187,58 @@ z_stream* SpdyFramer::GetStreamDecompressor(SpdyStreamId stream_id) { return stream_decompressors_[stream_id] = decompressor.release(); } +bool SpdyFramer::GetFrameBoundaries(const SpdyFrame& frame, + int* payload_length, + int* header_length, + const char** payload) const { + size_t frame_size; + if (frame.is_control_frame()) { + const SpdyControlFrame& control_frame = + reinterpret_cast<const SpdyControlFrame&>(frame); + switch (control_frame.type()) { + case SYN_STREAM: + { + const SpdySynStreamControlFrame& syn_frame = + reinterpret_cast<const SpdySynStreamControlFrame&>(frame); + frame_size = SpdySynStreamControlFrame::size(); + *payload_length = syn_frame.header_block_len(); + *header_length = frame_size; + *payload = frame.data() + *header_length; + } + break; + case SYN_REPLY: + { + const SpdySynReplyControlFrame& syn_frame = + reinterpret_cast<const SpdySynReplyControlFrame&>(frame); + frame_size = SpdySynReplyControlFrame::size(); + *payload_length = syn_frame.header_block_len(); + *header_length = frame_size; + *payload = frame.data() + *header_length; + } + break; + case HEADERS: + { + const SpdyHeadersControlFrame& headers_frame = + reinterpret_cast<const SpdyHeadersControlFrame&>(frame); + frame_size = SpdyHeadersControlFrame::size(); + *payload_length = headers_frame.header_block_len(); + *header_length = frame_size; + *payload = frame.data() + *header_length; + } + break; + default: + // TODO(mbelshe): set an error? + return false; // We can't compress this frame! + } + } else { + frame_size = SpdyFrame::size(); + *header_length = frame_size; + *payload_length = frame.length(); + *payload = frame.data() + SpdyFrame::size(); + } + return true; +} + SpdyControlFrame* SpdyFramer::CompressControlFrame( const SpdyControlFrame& frame) { z_stream* compressor = GetHeaderCompressor(); @@ -1222,194 +1265,6 @@ SpdyControlFrame* SpdyFramer::DecompressControlFrame( DecompressFrameWithZStream(frame, decompressor)); } -// Incrementally decompress the control frame's header block, feeding the -// result to the visitor in chunks. Continue this until the visitor -// indicates that it cannot process any more data, or (more commonly) we -// run out of data to deliver. -bool SpdyFramer::IncrementallyDecompressControlFrameHeaderData( - const SpdyControlFrame* control_frame) { - z_stream* decomp = GetHeaderDecompressor(); - int payload_length; - int header_length; - const char* payload; - bool read_successfully = true; - bool more = true; - char buffer[kHeaderDataChunkMaxSize]; - - if (!GetFrameBoundaries( - *control_frame, &payload_length, &header_length, &payload)) { - DLOG(ERROR) << "Control frame of type " - << SpdyFramer::ControlTypeToString(control_frame->type()) - <<" doesn't have headers"; - return false; - } - decomp->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(payload)); - decomp->avail_in = payload_length; - const SpdyStreamId stream_id = GetControlFrameStreamId(control_frame); - DCHECK_LT(0u, stream_id); - while (more && read_successfully) { - decomp->next_out = reinterpret_cast<Bytef*>(buffer); - decomp->avail_out = arraysize(buffer); - int rv = DecompressHeaderBlockInZStream(decomp); - if (rv != Z_OK) { - set_error(SPDY_DECOMPRESS_FAILURE); - DLOG(WARNING) << "inflate failure: " << rv; - more = read_successfully = false; - } else { - DCHECK_GT(arraysize(buffer), decomp->avail_out); - size_t len = arraysize(buffer) - decomp->avail_out; - read_successfully = visitor_->OnControlFrameHeaderData(stream_id, buffer, - len); - if (!read_successfully) { - // Assume that the problem was the header block was too large for the - // visitor. - set_error(SPDY_CONTROL_PAYLOAD_TOO_LARGE); - } - more = decomp->avail_in > 0; - } - } - return read_successfully; -} - -// Incrementally decompress the control frame's header block, feeding the -// result to the visitor in chunks. Continue this until the visitor -// indicates that it cannot process any more data, or (more commonly) we -// run out of data to deliver. -bool SpdyFramer::IncrementallyDecompressControlFrameHeaderData( - const SpdyControlFrame* control_frame, - const char* data, - size_t len) { - // Get a decompressor or set error. - z_stream* decomp = GetHeaderDecompressor(); - if (decomp == NULL) { - LOG(DFATAL) << "Couldn't get decompressor for handling compressed headers."; - set_error(SPDY_DECOMPRESS_FAILURE); - return false; - } - - bool processed_successfully = true; - char buffer[kHeaderDataChunkMaxSize]; - - decomp->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data)); - decomp->avail_in = len; - const SpdyStreamId stream_id = GetControlFrameStreamId(control_frame); - DCHECK_LT(0u, stream_id); - while (decomp->avail_in > 0 && processed_successfully) { - decomp->next_out = reinterpret_cast<Bytef*>(buffer); - decomp->avail_out = arraysize(buffer); - int rv = DecompressHeaderBlockInZStream(decomp); - if (rv != Z_OK) { - set_error(SPDY_DECOMPRESS_FAILURE); - DLOG(WARNING) << "inflate failure: " << rv; - processed_successfully = false; - } else { - size_t decompressed_len = arraysize(buffer) - decomp->avail_out; - if (decompressed_len > 0) { - processed_successfully = visitor_->OnControlFrameHeaderData( - stream_id, buffer, decompressed_len); - } - if (!processed_successfully) { - // Assume that the problem was the header block was too large for the - // visitor. - set_error(SPDY_CONTROL_PAYLOAD_TOO_LARGE); - } - } - } - return processed_successfully; -} - -bool SpdyFramer::IncrementallyDeliverControlFrameHeaderData( - const SpdyControlFrame* control_frame, const char* data, size_t len) { - bool read_successfully = true; - const SpdyStreamId stream_id = GetControlFrameStreamId(control_frame); - DCHECK_LT(0u, stream_id); - while (read_successfully && len > 0) { - size_t bytes_to_deliver = std::min(len, kHeaderDataChunkMaxSize); - read_successfully = visitor_->OnControlFrameHeaderData(stream_id, data, - bytes_to_deliver); - data += bytes_to_deliver; - len -= bytes_to_deliver; - if (!read_successfully) { - // Assume that the problem was the header block was too large for the - // visitor. - set_error(SPDY_CONTROL_PAYLOAD_TOO_LARGE); - } - } - return read_successfully; -} - -size_t SpdyFramer::GetMinimumControlFrameSize(SpdyControlType type) { - switch (type) { - case SYN_STREAM: - return SpdySynStreamControlFrame::size(); - case SYN_REPLY: - return SpdySynReplyControlFrame::size(); - case RST_STREAM: - return SpdyRstStreamControlFrame::size(); - case SETTINGS: - return SpdySettingsControlFrame::size(); - case NOOP: - return SpdyNoOpControlFrame::size(); - case PING: - return SpdyPingControlFrame::size(); - case GOAWAY: - return SpdyGoAwayControlFrame::size(); - case HEADERS: - return SpdyHeadersControlFrame::size(); - case WINDOW_UPDATE: - return SpdyWindowUpdateControlFrame::size(); - case NUM_CONTROL_FRAME_TYPES: - break; - } - LOG(ERROR) << "Unknown SPDY control frame type " << type; - return 0x7FFFFFFF; // Max signed 32bit int -} - -/* static */ -SpdyStreamId SpdyFramer::GetControlFrameStreamId( - const SpdyControlFrame* control_frame) { - SpdyStreamId stream_id = kInvalidStream; - if (control_frame != NULL) { - switch (control_frame->type()) { - case SYN_STREAM: - stream_id = reinterpret_cast<const SpdySynStreamControlFrame*>( - control_frame)->stream_id(); - break; - case SYN_REPLY: - stream_id = reinterpret_cast<const SpdySynReplyControlFrame*>( - control_frame)->stream_id(); - break; - case HEADERS: - stream_id = reinterpret_cast<const SpdyHeadersControlFrame*>( - control_frame)->stream_id(); - break; - case RST_STREAM: - stream_id = reinterpret_cast<const SpdyRstStreamControlFrame*>( - control_frame)->stream_id(); - break; - case WINDOW_UPDATE: - stream_id = reinterpret_cast<const SpdyWindowUpdateControlFrame*>( - control_frame)->stream_id(); - break; - // All of the following types are not part of a particular stream. - // They all fall through to the invalid control frame type case. - // (The default case isn't used so that the compile will break if a new - // control frame type is added but not included here.) - case SETTINGS: - case NOOP: - case PING: - case GOAWAY: - case NUM_CONTROL_FRAME_TYPES: // makes compiler happy - break; - } - } - return stream_id; -} - -void SpdyFramer::set_validate_control_frame_sizes(bool value) { - validate_control_frame_sizes_ = value; -} - SpdyDataFrame* SpdyFramer::DecompressDataFrame(const SpdyDataFrame& frame) { z_stream* decompressor = GetStreamDecompressor(frame.stream_id()); if (!decompressor) @@ -1523,9 +1378,10 @@ SpdyFrame* SpdyFramer::DecompressFrameWithZStream(const SpdyFrame& frame, int rv = inflate(decompressor, Z_SYNC_FLUSH); if (rv == Z_NEED_DICT) { // Need to try again with the right dictionary. - if (decompressor->adler == dictionary_id) { - rv = inflateSetDictionary(decompressor, (const Bytef*)kDictionary, - kDictionarySize); + if (decompressor->adler == kDictionaryId) { + rv = inflateSetDictionary(decompressor, + (const Bytef*)SpdyFramer::kDictionary, + SpdyFramer::kDictionarySize); if (rv == Z_OK) rv = inflate(decompressor, Z_SYNC_FLUSH); } @@ -1558,6 +1414,122 @@ SpdyFrame* SpdyFramer::DecompressFrameWithZStream(const SpdyFrame& frame, return new_frame.release(); } +// Incrementally decompress the control frame's header block, feeding the +// result to the visitor in chunks. Continue this until the visitor +// indicates that it cannot process any more data, or (more commonly) we +// run out of data to deliver. +bool SpdyFramer::IncrementallyDecompressControlFrameHeaderData( + const SpdyControlFrame* control_frame) { + z_stream* decomp = GetHeaderDecompressor(); + int payload_length; + int header_length; + const char* payload; + bool read_successfully = true; + bool more = true; + char buffer[kHeaderDataChunkMaxSize]; + + if (!GetFrameBoundaries( + *control_frame, &payload_length, &header_length, &payload)) { + DLOG(ERROR) << "Control frame of type " + << SpdyFramer::ControlTypeToString(control_frame->type()) + <<" doesn't have headers"; + return false; + } + decomp->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(payload)); + decomp->avail_in = payload_length; + const SpdyStreamId stream_id = GetControlFrameStreamId(control_frame); + DCHECK_LT(0u, stream_id); + while (more && read_successfully) { + decomp->next_out = reinterpret_cast<Bytef*>(buffer); + decomp->avail_out = arraysize(buffer); + int rv = DecompressHeaderBlockInZStream(decomp); + if (rv != Z_OK) { + set_error(SPDY_DECOMPRESS_FAILURE); + DLOG(WARNING) << "inflate failure: " << rv; + more = read_successfully = false; + } else { + DCHECK_GT(arraysize(buffer), decomp->avail_out); + size_t len = arraysize(buffer) - decomp->avail_out; + read_successfully = visitor_->OnControlFrameHeaderData(stream_id, buffer, + len); + if (!read_successfully) { + // Assume that the problem was the header block was too large for the + // visitor. + set_error(SPDY_CONTROL_PAYLOAD_TOO_LARGE); + } + more = decomp->avail_in > 0; + } + } + return read_successfully; +} + +// Incrementally decompress the control frame's header block, feeding the +// result to the visitor in chunks. Continue this until the visitor +// indicates that it cannot process any more data, or (more commonly) we +// run out of data to deliver. +bool SpdyFramer::NewIncrementallyDecompressControlFrameHeaderData( + const SpdyControlFrame* control_frame, + const char* data, + size_t len) { + // Get a decompressor or set error. + z_stream* decomp = GetHeaderDecompressor(); + if (decomp == NULL) { + LOG(DFATAL) << "Couldn't get decompressor for handling compressed headers."; + set_error(SPDY_DECOMPRESS_FAILURE); + return false; + } + + bool processed_successfully = true; + char buffer[kHeaderDataChunkMaxSize]; + + decomp->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data)); + decomp->avail_in = len; + const SpdyStreamId stream_id = GetControlFrameStreamId(control_frame); + DCHECK_LT(0u, stream_id); + while (decomp->avail_in > 0 && processed_successfully) { + decomp->next_out = reinterpret_cast<Bytef*>(buffer); + decomp->avail_out = arraysize(buffer); + int rv = DecompressHeaderBlockInZStream(decomp); + if (rv != Z_OK) { + set_error(SPDY_DECOMPRESS_FAILURE); + DLOG(WARNING) << "inflate failure: " << rv; + processed_successfully = false; + } else { + size_t decompressed_len = arraysize(buffer) - decomp->avail_out; + if (decompressed_len > 0) { + processed_successfully = visitor_->OnControlFrameHeaderData( + stream_id, buffer, decompressed_len); + } + if (!processed_successfully) { + // Assume that the problem was the header block was too large for the + // visitor. + set_error(SPDY_CONTROL_PAYLOAD_TOO_LARGE); + } + } + } + return processed_successfully; +} + +bool SpdyFramer::IncrementallyDeliverControlFrameHeaderData( + const SpdyControlFrame* control_frame, const char* data, size_t len) { + bool read_successfully = true; + const SpdyStreamId stream_id = GetControlFrameStreamId(control_frame); + DCHECK_LT(0u, stream_id); + while (read_successfully && len > 0) { + size_t bytes_to_deliver = std::min(len, kHeaderDataChunkMaxSize); + read_successfully = visitor_->OnControlFrameHeaderData(stream_id, data, + bytes_to_deliver); + data += bytes_to_deliver; + len -= bytes_to_deliver; + if (!read_successfully) { + // Assume that the problem was the header block was too large for the + // visitor. + set_error(SPDY_CONTROL_PAYLOAD_TOO_LARGE); + } + } + return read_successfully; +} + void SpdyFramer::CleanupCompressorForStream(SpdyStreamId id) { CompressorMap::iterator it = stream_compressors_.find(id); if (it != stream_compressors_.end()) { @@ -1600,6 +1572,81 @@ void SpdyFramer::CleanupStreamCompressorsAndDecompressors() { stream_decompressors_.clear(); } +SpdyFrame* SpdyFramer::DuplicateFrame(const SpdyFrame& frame) { + int size = SpdyFrame::size() + frame.length(); + SpdyFrame* new_frame = new SpdyFrame(size); + memcpy(new_frame->data(), frame.data(), size); + return new_frame; +} + +size_t SpdyFramer::GetMinimumControlFrameSize(SpdyControlType type) { + switch (type) { + case SYN_STREAM: + return SpdySynStreamControlFrame::size(); + case SYN_REPLY: + return SpdySynReplyControlFrame::size(); + case RST_STREAM: + return SpdyRstStreamControlFrame::size(); + case SETTINGS: + return SpdySettingsControlFrame::size(); + case NOOP: + return SpdyNoOpControlFrame::size(); + case PING: + return SpdyPingControlFrame::size(); + case GOAWAY: + return SpdyGoAwayControlFrame::size(); + case HEADERS: + return SpdyHeadersControlFrame::size(); + case WINDOW_UPDATE: + return SpdyWindowUpdateControlFrame::size(); + case NUM_CONTROL_FRAME_TYPES: + break; + } + LOG(ERROR) << "Unknown control frame type " << type; + return 0x7FFFFFFF; // Max signed 32bit int +} + +/* static */ +SpdyStreamId SpdyFramer::GetControlFrameStreamId( + const SpdyControlFrame* control_frame) { + SpdyStreamId stream_id = kInvalidStream; + if (control_frame != NULL) { + switch (control_frame->type()) { + case SYN_STREAM: + stream_id = reinterpret_cast<const SpdySynStreamControlFrame*>( + control_frame)->stream_id(); + break; + case SYN_REPLY: + stream_id = reinterpret_cast<const SpdySynReplyControlFrame*>( + control_frame)->stream_id(); + break; + case HEADERS: + stream_id = reinterpret_cast<const SpdyHeadersControlFrame*>( + control_frame)->stream_id(); + break; + case RST_STREAM: + stream_id = reinterpret_cast<const SpdyRstStreamControlFrame*>( + control_frame)->stream_id(); + break; + case WINDOW_UPDATE: + stream_id = reinterpret_cast<const SpdyWindowUpdateControlFrame*>( + control_frame)->stream_id(); + break; + // All of the following types are not part of a particular stream. + // They all fall through to the invalid control frame type case. + // (The default case isn't used so that the compile will break if a new + // control frame type is added but not included here.) + case SETTINGS: + case NOOP: + case PING: + case GOAWAY: + case NUM_CONTROL_FRAME_TYPES: // makes compiler happy + break; + } + } + return stream_id; +} + size_t SpdyFramer::BytesSafeToRead() const { switch (state_) { case SPDY_ERROR: @@ -1626,75 +1673,16 @@ size_t SpdyFramer::BytesSafeToRead() const { return 0; } -void SpdyFramer::set_error(SpdyError error) { - DCHECK(visitor_); - error_code_ = error; - CHANGE_STATE(SPDY_ERROR); - visitor_->OnError(this); +void SpdyFramer::set_enable_compression(bool value) { + enable_compression_ = value; } -void SpdyFramer::ExpandControlFrameBuffer(size_t size) { - size_t alloc_size = size + SpdyFrame::size(); - DCHECK_LE(alloc_size, kControlFrameBufferMaxSize); - if (alloc_size <= current_frame_capacity_) - return; - char* new_buffer = new char[alloc_size]; - memcpy(new_buffer, current_frame_buffer_, current_frame_len_); - delete [] current_frame_buffer_; - current_frame_capacity_ = alloc_size; - current_frame_buffer_ = new_buffer; +void SpdyFramer::set_enable_compression_default(bool value) { + compression_default_ = value; } -bool SpdyFramer::GetFrameBoundaries(const SpdyFrame& frame, - int* payload_length, - int* header_length, - const char** payload) const { - size_t frame_size; - if (frame.is_control_frame()) { - const SpdyControlFrame& control_frame = - reinterpret_cast<const SpdyControlFrame&>(frame); - switch (control_frame.type()) { - case SYN_STREAM: - { - const SpdySynStreamControlFrame& syn_frame = - reinterpret_cast<const SpdySynStreamControlFrame&>(frame); - frame_size = SpdySynStreamControlFrame::size(); - *payload_length = syn_frame.header_block_len(); - *header_length = frame_size; - *payload = frame.data() + *header_length; - } - break; - case SYN_REPLY: - { - const SpdySynReplyControlFrame& syn_frame = - reinterpret_cast<const SpdySynReplyControlFrame&>(frame); - frame_size = SpdySynReplyControlFrame::size(); - *payload_length = syn_frame.header_block_len(); - *header_length = frame_size; - *payload = frame.data() + *header_length; - } - break; - case HEADERS: - { - const SpdyHeadersControlFrame& headers_frame = - reinterpret_cast<const SpdyHeadersControlFrame&>(frame); - frame_size = SpdyHeadersControlFrame::size(); - *payload_length = headers_frame.header_block_len(); - *header_length = frame_size; - *payload = frame.data() + *header_length; - } - break; - default: - // TODO(mbelshe): set an error? - return false; // We can't compress this frame! - } - } else { - frame_size = SpdyFrame::size(); - *header_length = frame_size; - *payload_length = frame.length(); - *payload = frame.data() + SpdyFrame::size(); - } - return true; +void SpdyFramer::set_validate_control_frame_sizes(bool value) { + validate_control_frame_sizes_ = value; } } // namespace spdy |