diff options
Diffstat (limited to 'net/spdy/spdy_framer.cc')
-rw-r--r-- | net/spdy/spdy_framer.cc | 924 |
1 files changed, 464 insertions, 460 deletions
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc index ed21610..9a3203b 100644 --- a/net/spdy/spdy_framer.cc +++ b/net/spdy/spdy_framer.cc @@ -19,8 +19,39 @@ #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; + +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; @@ -71,92 +102,6 @@ SpdyFramer::~SpdyFramer() { delete [] current_frame_buffer_; } -void SpdyFramer::Reset() { - state_ = SPDY_RESET; - error_code_ = SPDY_NO_ERROR; - remaining_payload_ = 0; - remaining_control_payload_ = 0; - current_frame_len_ = 0; - if (current_frame_capacity_ != kControlFrameBufferInitialSize) { - delete [] current_frame_buffer_; - current_frame_buffer_ = 0; - current_frame_capacity_ = 0; - ExpandControlFrameBuffer(kControlFrameBufferInitialSize); - } -} - -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"; - } - return "UNKNOWN_STATE"; -} - -size_t SpdyFramer::BytesSafeToRead() const { - switch (state_) { - case SPDY_ERROR: - case SPDY_DONE: - case SPDY_AUTO_RESET: - case SPDY_RESET: - return 0; - case SPDY_READING_COMMON_HEADER: - DCHECK_LT(current_frame_len_, SpdyFrame::size()); - return SpdyFrame::size() - current_frame_len_; - case SPDY_INTERPRET_CONTROL_FRAME_COMMON_HEADER: - return 0; - case SPDY_CONTROL_FRAME_PAYLOAD: - case SPDY_IGNORE_REMAINING_PAYLOAD: - case SPDY_FORWARD_STREAM_FRAME: - return remaining_payload_; - } - // We should never get to here. - return 0; -} - -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"; -} - size_t SpdyFramer::ProcessInput(const char* data, size_t len) { DCHECK(visitor_); DCHECK(data); @@ -211,236 +156,18 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) { return original_len - len; } -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_; - size_t bytes_to_append = std::min(bytes_desired, len); - char* header_buffer = current_frame_buffer_; - memcpy(&header_buffer[current_frame_len_], data, bytes_to_append); - current_frame_len_ += bytes_to_append; - data += bytes_to_append; - len -= bytes_to_append; - // 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_payload_ = current_frame.length(); - - // This is just a sanity check for help debugging early frame errors. - if (remaining_payload_ > 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; - case NOOP: - // NOOP. Swallow it. - 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; - default: - LOG(WARNING) << "Valid spdy control frame with unknown 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 amount_to_consume = std::min(remaining_control_payload_, len); - memcpy(¤t_frame_buffer_[current_frame_len_], data, - amount_to_consume); - current_frame_len_ += amount_to_consume; - data += amount_to_consume; - len -= amount_to_consume; - remaining_control_payload_ -= amount_to_consume; - remaining_payload_ -= amount_to_consume; - 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_payload_) { - size_t amount_to_forward = std::min(remaining_payload_, 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_payload_ -= 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_payload_ && - 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); +void SpdyFramer::Reset() { + state_ = SPDY_RESET; + error_code_ = SPDY_NO_ERROR; + remaining_payload_ = 0; + remaining_control_payload_ = 0; + current_frame_len_ = 0; + if (current_frame_capacity_ != kControlFrameBufferInitialSize) { + delete [] current_frame_buffer_; + current_frame_buffer_ = 0; + current_frame_capacity_ = 0; + ExpandControlFrameBuffer(kControlFrameBufferInitialSize); } - return original_len - len; -} - -void SpdyFramer::ExpandControlFrameBuffer(size_t size) { - size_t alloc_size = size + SpdyFrame::size(); - DCHECK_LT(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; } bool SpdyFramer::ParseHeaderBlock(const SpdyFrame* frame, @@ -508,26 +235,6 @@ bool SpdyFramer::ParseHeaderBlock(const SpdyFrame* frame, 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, SpdyHeaderBlock* headers) { @@ -727,6 +434,26 @@ 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) { @@ -759,32 +486,318 @@ 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; +SpdyFrame* SpdyFramer::CompressFrame(const SpdyFrame& frame) { + if (frame.is_control_frame()) { + return CompressControlFrame( + reinterpret_cast<const SpdyControlFrame&>(frame)); + } + return CompressDataFrame(reinterpret_cast<const SpdyDataFrame&>(frame)); +} -// 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::DecompressFrame(const SpdyFrame& frame) { + if (frame.is_control_frame()) { + return DecompressControlFrame( + reinterpret_cast<const SpdyControlFrame&>(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 + // and SYN_REPLY. + // TODO(mbelshe): Reconcile this with the spec when the spec is + // explicit about which frames compress and which do not. + if (frame.is_control_frame()) { + const SpdyControlFrame& control_frame = + reinterpret_cast<const SpdyControlFrame&>(frame); + return control_frame.type() == SYN_STREAM || + control_frame.type() == SYN_REPLY; + } + + const SpdyDataFrame& data_frame = + reinterpret_cast<const SpdyDataFrame&>(frame); + 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"; + } + 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"; +} -static uLong dictionary_id = 0; +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_; + size_t bytes_to_append = std::min(bytes_desired, len); + char* header_buffer = current_frame_buffer_; + memcpy(&header_buffer[current_frame_len_], data, bytes_to_append); + current_frame_len_ += bytes_to_append; + data += bytes_to_append; + len -= bytes_to_append; + // 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_payload_ = current_frame.length(); + + // This is just a sanity check for help debugging early frame errors. + if (remaining_payload_ > 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; + case NOOP: + // NOOP. Swallow it. + 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; + default: + LOG(WARNING) << "Valid spdy control frame with unknown 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 amount_to_consume = std::min(remaining_control_payload_, len); + memcpy(¤t_frame_buffer_[current_frame_len_], data, + amount_to_consume); + current_frame_len_ += amount_to_consume; + data += amount_to_consume; + len -= amount_to_consume; + remaining_control_payload_ -= amount_to_consume; + remaining_payload_ -= amount_to_consume; + 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_payload_) { + size_t amount_to_forward = std::min(remaining_payload_, 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_payload_ -= 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_payload_ && + 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()) @@ -873,74 +886,6 @@ 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; -} - -SpdyFrame* SpdyFramer::CompressFrame(const SpdyFrame& frame) { - if (frame.is_control_frame()) { - return CompressControlFrame( - reinterpret_cast<const SpdyControlFrame&>(frame)); - } - return CompressDataFrame(reinterpret_cast<const SpdyDataFrame&>(frame)); -} - -SpdyFrame* SpdyFramer::DecompressFrame(const SpdyFrame& frame) { - if (frame.is_control_frame()) { - return DecompressControlFrame( - reinterpret_cast<const SpdyControlFrame&>(frame)); - } - return DecompressDataFrame(reinterpret_cast<const SpdyDataFrame&>(frame)); -} - SpdyControlFrame* SpdyFramer::CompressControlFrame( const SpdyControlFrame& frame) { z_stream* compressor = GetHeaderCompressor(); @@ -950,6 +895,14 @@ SpdyControlFrame* SpdyFramer::CompressControlFrame( CompressFrameWithZStream(frame, compressor)); } +SpdyDataFrame* SpdyFramer::CompressDataFrame(const SpdyDataFrame& frame) { + z_stream* compressor = GetStreamCompressor(frame.stream_id()); + if (!compressor) + return NULL; + return reinterpret_cast<SpdyDataFrame*>( + CompressFrameWithZStream(frame, compressor)); +} + SpdyControlFrame* SpdyFramer::DecompressControlFrame( const SpdyControlFrame& frame) { z_stream* decompressor = GetHeaderDecompressor(); @@ -959,14 +912,6 @@ SpdyControlFrame* SpdyFramer::DecompressControlFrame( DecompressFrameWithZStream(frame, decompressor)); } -SpdyDataFrame* SpdyFramer::CompressDataFrame(const SpdyDataFrame& frame) { - z_stream* compressor = GetStreamCompressor(frame.stream_id()); - if (!compressor) - return NULL; - return reinterpret_cast<SpdyDataFrame*>( - CompressFrameWithZStream(frame, compressor)); -} - SpdyDataFrame* SpdyFramer::DecompressDataFrame(const SpdyDataFrame& frame) { z_stream* decompressor = GetStreamDecompressor(frame.stream_id()); if (!decompressor) @@ -1145,37 +1090,96 @@ 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; -} - -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 - // and SYN_REPLY. - // TODO(mbelshe): Reconcile this with the spec when the spec is - // explicit about which frames compress and which do not. - if (frame.is_control_frame()) { - const SpdyControlFrame& control_frame = - reinterpret_cast<const SpdyControlFrame&>(frame); - return control_frame.type() == SYN_STREAM || - control_frame.type() == SYN_REPLY; +size_t SpdyFramer::BytesSafeToRead() const { + switch (state_) { + case SPDY_ERROR: + case SPDY_DONE: + case SPDY_AUTO_RESET: + case SPDY_RESET: + return 0; + case SPDY_READING_COMMON_HEADER: + DCHECK_LT(current_frame_len_, SpdyFrame::size()); + return SpdyFrame::size() - current_frame_len_; + case SPDY_INTERPRET_CONTROL_FRAME_COMMON_HEADER: + return 0; + case SPDY_CONTROL_FRAME_PAYLOAD: + case SPDY_IGNORE_REMAINING_PAYLOAD: + case SPDY_FORWARD_STREAM_FRAME: + return remaining_payload_; } + // We should never get to here. + return 0; +} - const SpdyDataFrame& data_frame = - reinterpret_cast<const SpdyDataFrame&>(frame); - return (data_frame.flags() & DATA_FLAG_COMPRESSED) != 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_LT(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; } } // namespace spdy |