diff options
Diffstat (limited to 'net/spdy/spdy_framer.cc')
-rw-r--r-- | net/spdy/spdy_framer.cc | 586 |
1 files changed, 544 insertions, 42 deletions
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc index e178165..878b199 100644 --- a/net/spdy/spdy_framer.cc +++ b/net/spdy/spdy_framer.cc @@ -31,6 +31,7 @@ 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 @@ -68,6 +69,9 @@ size_t SpdyFramer::kControlFrameBufferInitialSize = 8 * 1024; // TODO(mbelshe): We should make this stream-based so there are no limits. size_t SpdyFramer::kControlFrameBufferMaxSize = 16 * 1024; +const SpdyStreamId SpdyFramer::kInvalidStream = -1; +const size_t SpdyFramer::kHeaderDataChunkMaxSize = 1024; + #ifdef DEBUG_SPDY_STATE_CHANGES #define CHANGE_STATE(newstate) \ { \ @@ -82,14 +86,68 @@ size_t SpdyFramer::kControlFrameBufferMaxSize = 16 * 1024; #define CHANGE_STATE(newstate) (state_ = newstate) #endif +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) { + rv = inflateSetDictionary(decompressor, + (const Bytef*)SpdyFramer::kDictionary, + SpdyFramer::kDictionarySize); + if (rv == Z_OK) + rv = inflate(decompressor, Z_SYNC_FLUSH); + } + } + return rv; +} + +// Retrieve serialized length of SpdyHeaderBlock. +size_t GetSerializedLength(const SpdyHeaderBlock* headers) { + size_t total_length = SpdyControlFrame::kNumNameValuePairsSize; + SpdyHeaderBlock::const_iterator it; + for (it = headers->begin(); it != headers->end(); ++it) { + // We add space for the length of the name and the length of the value as + // well as the length of the name and the length of the value. + total_length += SpdyControlFrame::kLengthOfNameSize + + it->first.size() + + SpdyControlFrame::kLengthOfValueSize + + it->second.size(); + } + return total_length; +} + +// Serializes a SpdyHeaderBlock. +void WriteHeaderBlock(SpdyFrameBuilder* frame, const SpdyHeaderBlock* headers) { + 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); + } +} + +// Creates a FlagsAndLength. +FlagsAndLength CreateFlagsAndLength(SpdyControlFlags flags, size_t length) { + 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; + return flags_length; +} + SpdyFramer::SpdyFramer() : state_(SPDY_RESET), error_code_(SPDY_NO_ERROR), - remaining_payload_(0), + remaining_data_(0), remaining_control_payload_(0), + remaining_control_header_(0), current_frame_buffer_(NULL), current_frame_len_(0), current_frame_capacity_(0), + validate_control_frame_sizes_(true), enable_compression_(compression_default_), visitor_(NULL) { } @@ -105,6 +163,54 @@ SpdyFramer::~SpdyFramer() { delete [] current_frame_buffer_; } +const char* SpdyFramer::StatusCodeToString(int status_code) { + switch (status_code) { + case INVALID: + return "INVALID"; + case PROTOCOL_ERROR: + return "PROTOCOL_ERROR"; + case INVALID_STREAM: + return "INVALID_STREAM"; + case REFUSED_STREAM: + return "REFUSED_STREAM"; + case UNSUPPORTED_VERSION: + return "UNSUPPORTED_VERSION"; + case CANCEL: + return "CANCEL"; + case INTERNAL_ERROR: + return "INTERNAL_ERROR"; + case FLOW_CONTROL_ERROR: + return "FLOW_CONTROL_ERROR"; + } + return "UNKNOWN_STATUS"; +} + +const char* SpdyFramer::ControlTypeToString(SpdyControlType type) { + switch (type) { + case SYN_STREAM: + return "SYN_STREAM"; + case SYN_REPLY: + return "SYN_REPLY"; + case RST_STREAM: + return "RST_STREAM"; + case SETTINGS: + return "SETTINGS"; + case NOOP: + return "NOOP"; + case PING: + return "PING"; + case GOAWAY: + return "GOAWAY"; + case HEADERS: + return "HEADERS"; + case WINDOW_UPDATE: + return "WINDOW_UPDATE"; + case NUM_CONTROL_FRAME_TYPES: + break; + } + return "UNKNOWN_CONTROL_TYPE"; +} + size_t SpdyFramer::ProcessInput(const char* data, size_t len) { DCHECK(visitor_); DCHECK(data); @@ -132,10 +238,36 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) { // Arguably, this case is not necessary, as no bytes are consumed here. // I felt it was a nice partitioning, however (which probably indicates // that it should be refactored into its own function!) + // TODO(hkhalil): Remove -- while loop above prevents proper handling of + // zero-length control frames. case SPDY_INTERPRET_CONTROL_FRAME_COMMON_HEADER: ProcessControlFrameHeader(); continue; + case SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK: { + // Control frames that contain header blocks (SYN_STREAM, SYN_REPLY, + // HEADERS) take a different path through the state machine - they + // will go: + // 1. SPDY_INTERPRET_CONTROL_FRAME_COMMON HEADER + // 2. SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK + // 3. SPDY_CONTROL_FRAME_HEADER_BLOCK + // + // All other control frames will use the alternate route: + // 1. SPDY_INTERPRET_CONTROL_FRAME_COMMON_HEADER + // 2. SPDY_CONTROL_FRAME_PAYLOAD + int bytes_read = ProcessControlFrameBeforeHeaderBlock(data, len); + len -= bytes_read; + data += bytes_read; + continue; + } + + case SPDY_CONTROL_FRAME_HEADER_BLOCK: { + int bytes_read = ProcessControlFrameHeaderBlock(data, len); + len -= bytes_read; + data += bytes_read; + continue; + } + case SPDY_CONTROL_FRAME_PAYLOAD: { size_t bytes_read = ProcessControlFramePayload(data, len); len -= bytes_read; @@ -162,8 +294,9 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) { void SpdyFramer::Reset() { state_ = SPDY_RESET; error_code_ = SPDY_NO_ERROR; - remaining_payload_ = 0; + remaining_data_ = 0; remaining_control_payload_ = 0; + remaining_control_header_ = 0; current_frame_len_ = 0; if (current_frame_capacity_ != kControlFrameBufferInitialSize) { delete [] current_frame_buffer_; @@ -238,9 +371,169 @@ bool SpdyFramer::ParseHeaderBlock(const SpdyFrame* frame, return false; } +size_t SpdyFramer::UpdateCurrentFrameBuffer(const char** data, size_t* len, + size_t max_bytes) { + size_t bytes_to_read = std::min(*len, max_bytes); + DCHECK_GE(current_frame_capacity_, current_frame_len_ + bytes_to_read); + memcpy(¤t_frame_buffer_[current_frame_len_], *data, bytes_to_read); + current_frame_len_ += bytes_to_read; + *data += bytes_to_read; + *len -= bytes_to_read; + return bytes_to_read; +} + +size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, + size_t len) { + DCHECK_EQ(SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK, state_); + DCHECK_GT(remaining_control_header_, 0u); + size_t original_len = len; + + if (remaining_control_header_) { + size_t bytes_read = UpdateCurrentFrameBuffer(&data, &len, + remaining_control_header_); + remaining_control_header_ -= bytes_read; + if (remaining_control_header_ == 0) { + SpdyControlFrame control_frame(current_frame_buffer_, false); + DCHECK(control_frame.type() == SYN_STREAM || + control_frame.type() == SYN_REPLY || + control_frame.type() == HEADERS); + visitor_->OnControl(&control_frame); + + CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK); + } + } + return original_len - len; +} + +// Does not buffer the control payload. Instead, either passes directly to the +// visitor or decompresses and then passes directly to the visitor, via +// IncrementallyDeliverControlFrameHeaderData() or +// IncrementallyDecompressControlFrameHeaderData() respectively. +size_t SpdyFramer::NewProcessControlFrameHeaderBlock(const char* data, + size_t data_len) { + DCHECK_EQ(SPDY_CONTROL_FRAME_HEADER_BLOCK, state_); + SpdyControlFrame control_frame(current_frame_buffer_, false); + bool processed_successfully = true; + DCHECK(control_frame.type() == SYN_STREAM || + control_frame.type() == SYN_REPLY || + control_frame.type() == HEADERS); + size_t process_bytes = std::min(data_len, remaining_control_payload_); + DCHECK_GT(process_bytes, 0u); + + if (enable_compression_) { + processed_successfully = IncrementallyDecompressControlFrameHeaderData( + &control_frame, data, process_bytes); + } else { + processed_successfully = IncrementallyDeliverControlFrameHeaderData( + &control_frame, data, process_bytes); + } + remaining_control_payload_ -= process_bytes; + + // Handle the case that there is no futher data in this frame. + if (remaining_control_payload_ == 0 && processed_successfully) { + // The complete header block has been delivered. We send a zero-length + // OnControlFrameHeaderData() to indicate this. + visitor_->OnControlFrameHeaderData( + GetControlFrameStreamId(&control_frame), NULL, 0); + + // If this is a FIN, tell the caller. + if (control_frame.flags() & CONTROL_FLAG_FIN) { + visitor_->OnStreamFrameData(GetControlFrameStreamId(&control_frame), + NULL, 0); + } + + CHANGE_STATE(SPDY_RESET); + } + + // Handle error. + if (!processed_successfully) { + return data_len; + } + + // Return amount processed. + return process_bytes; +} + +size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data, + size_t data_len) { + DCHECK_EQ(SPDY_CONTROL_FRAME_HEADER_BLOCK, state_); + size_t original_data_len = data_len; + SpdyControlFrame control_frame(current_frame_buffer_, false); + bool read_successfully = true; + DCHECK(control_frame.type() == SYN_STREAM || + control_frame.type() == SYN_REPLY || + control_frame.type() == HEADERS); + + if (enable_compression_) { + // Note that the header block is held in the frame's payload, and is not + // part of the frame's headers. + if (remaining_control_payload_ > 0) { + size_t bytes_read = UpdateCurrentFrameBuffer( + &data, + &data_len, + remaining_control_payload_); + remaining_control_payload_ -= bytes_read; + if (remaining_control_payload_ == 0) { + read_successfully = IncrementallyDecompressControlFrameHeaderData( + &control_frame); + } + } + } else { + size_t bytes_to_send = std::min(data_len, remaining_control_payload_); + DCHECK_GT(bytes_to_send, 0u); + read_successfully = IncrementallyDeliverControlFrameHeaderData( + &control_frame, data, bytes_to_send); + data_len -= bytes_to_send; + remaining_control_payload_ -= bytes_to_send; + } + if (remaining_control_payload_ == 0 && read_successfully) { + // The complete header block has been delivered. + visitor_->OnControlFrameHeaderData(GetControlFrameStreamId(&control_frame), + NULL, 0); + + // If this is a FIN, tell the caller. + if (control_frame.flags() & CONTROL_FLAG_FIN) { + visitor_->OnStreamFrameData(GetControlFrameStreamId(&control_frame), + NULL, 0); + } + + CHANGE_STATE(SPDY_RESET); + } + if (!read_successfully) { + return original_data_len; + } + return original_data_len - data_len; +} + +/* static */ +bool SpdyFramer::ParseHeaderBlockInBuffer(const char* header_data, + size_t header_length, + SpdyHeaderBlock* block) { + SpdyFrameBuilder builder(header_data, header_length); + void* iter = NULL; + uint16 num_headers; + if (builder.ReadUInt16(&iter, &num_headers)) { + for (int index = 0; index < num_headers; ++index) { + std::string name; + std::string value; + if (!builder.ReadString(&iter, &name)) + return false; + if (!builder.ReadString(&iter, &value)) + return false; + if (block->find(name) == block->end()) { + (*block)[name] = value; + } else { + return false; + } + } + return true; + } + return false; +} + SpdySynStreamControlFrame* SpdyFramer::CreateSynStream( SpdyStreamId stream_id, SpdyStreamId associated_stream_id, int priority, - SpdyControlFlags flags, bool compressed, SpdyHeaderBlock* headers) { + SpdyControlFlags flags, bool compressed, const SpdyHeaderBlock* headers) { SpdyFrameBuilder frame; DCHECK_GT(stream_id, static_cast<SpdyStreamId>(0)); @@ -255,7 +548,7 @@ SpdySynStreamControlFrame* SpdyFramer::CreateSynStream( frame.WriteUInt16(ntohs(priority) << 6); // Priority. frame.WriteUInt16(headers->size()); // Number of headers. - SpdyHeaderBlock::iterator it; + SpdyHeaderBlock::const_iterator it; for (it = headers->begin(); it != headers->end(); ++it) { bool wrote_header; wrote_header = frame.WriteString(it->first); @@ -272,16 +565,17 @@ SpdySynStreamControlFrame* SpdyFramer::CreateSynStream( flags_length.flags_[0] = flags; frame.WriteBytesToOffset(4, &flags_length, sizeof(flags_length)); - scoped_ptr<SpdyFrame> syn_frame(frame.take()); + scoped_ptr<SpdySynStreamControlFrame> syn_frame( + reinterpret_cast<SpdySynStreamControlFrame*>(frame.take())); if (compressed) { return reinterpret_cast<SpdySynStreamControlFrame*>( - CompressFrame(*syn_frame.get())); + CompressControlFrame(*syn_frame.get())); } - return reinterpret_cast<SpdySynStreamControlFrame*>(syn_frame.release()); + return syn_frame.release(); } SpdySynReplyControlFrame* SpdyFramer::CreateSynReply(SpdyStreamId stream_id, - SpdyControlFlags flags, bool compressed, SpdyHeaderBlock* headers) { + SpdyControlFlags flags, bool compressed, const SpdyHeaderBlock* headers) { DCHECK_GT(stream_id, 0u); DCHECK_EQ(0u, stream_id & ~kStreamIdMask); @@ -294,7 +588,7 @@ SpdySynReplyControlFrame* SpdyFramer::CreateSynReply(SpdyStreamId stream_id, frame.WriteUInt16(0); // Unused frame.WriteUInt16(headers->size()); // Number of headers. - SpdyHeaderBlock::iterator it; + SpdyHeaderBlock::const_iterator it; for (it = headers->begin(); it != headers->end(); ++it) { bool wrote_header; wrote_header = frame.WriteString(it->first); @@ -311,12 +605,13 @@ SpdySynReplyControlFrame* SpdyFramer::CreateSynReply(SpdyStreamId stream_id, flags_length.flags_[0] = flags; frame.WriteBytesToOffset(4, &flags_length, sizeof(flags_length)); - scoped_ptr<SpdyFrame> reply_frame(frame.take()); + scoped_ptr<SpdySynReplyControlFrame> reply_frame( + reinterpret_cast<SpdySynReplyControlFrame*>(frame.take())); if (compressed) { return reinterpret_cast<SpdySynReplyControlFrame*>( - CompressFrame(*reply_frame.get())); + CompressControlFrame(*reply_frame.get())); } - return reinterpret_cast<SpdySynReplyControlFrame*>(reply_frame.release()); + return reply_frame.release(); } /* static */ @@ -356,12 +651,23 @@ SpdySettingsControlFrame* SpdyFramer::CreateSettings( } /* static */ -SpdyControlFrame* SpdyFramer::CreateNopFrame() { +SpdyNoOpControlFrame* SpdyFramer::CreateNopFrame() { SpdyFrameBuilder frame; frame.WriteUInt16(kControlFlagMask | spdy_version_); frame.WriteUInt16(NOOP); frame.WriteUInt32(0); - return reinterpret_cast<SpdyControlFrame*>(frame.take()); + return reinterpret_cast<SpdyNoOpControlFrame*>(frame.take()); +} + +/* static */ +SpdyPingControlFrame* SpdyFramer::CreatePingFrame(uint32 unique_id) { + SpdyFrameBuilder frame; + frame.WriteUInt16(kControlFlagMask | kSpdyProtocolVersion); + frame.WriteUInt16(PING); + size_t ping_size = SpdyPingControlFrame::size() - SpdyFrame::size(); + frame.WriteUInt32(ping_size); + frame.WriteUInt32(unique_id); + return reinterpret_cast<SpdyPingControlFrame*>(frame.take()); } /* static */ @@ -379,7 +685,7 @@ SpdyGoAwayControlFrame* SpdyFramer::CreateGoAway( } SpdyHeadersControlFrame* SpdyFramer::CreateHeaders(SpdyStreamId stream_id, - SpdyControlFlags flags, bool compressed, SpdyHeaderBlock* headers) { + SpdyControlFlags flags, bool compressed, const SpdyHeaderBlock* headers) { // Basically the same as CreateSynReply(). DCHECK_GT(stream_id, 0u); DCHECK_EQ(0u, stream_id & ~kStreamIdMask); @@ -392,7 +698,7 @@ SpdyHeadersControlFrame* SpdyFramer::CreateHeaders(SpdyStreamId stream_id, frame.WriteUInt16(0); // Unused frame.WriteUInt16(headers->size()); // Number of headers. - SpdyHeaderBlock::iterator it; + SpdyHeaderBlock::const_iterator it; for (it = headers->begin(); it != headers->end(); ++it) { bool wrote_header; wrote_header = frame.WriteString(it->first); @@ -409,12 +715,13 @@ SpdyHeadersControlFrame* SpdyFramer::CreateHeaders(SpdyStreamId stream_id, flags_length.flags_[0] = flags; frame.WriteBytesToOffset(4, &flags_length, sizeof(flags_length)); - scoped_ptr<SpdyFrame> headers_frame(frame.take()); + scoped_ptr<SpdyHeadersControlFrame> headers_frame( + reinterpret_cast<SpdyHeadersControlFrame*>(frame.take())); if (compressed) { return reinterpret_cast<SpdyHeadersControlFrame*>( - CompressFrame(*headers_frame.get())); + CompressControlFrame(*headers_frame.get())); } - return reinterpret_cast<SpdyHeadersControlFrame*>(headers_frame.release()); + return headers_frame.release(); } /* static */ @@ -550,6 +857,10 @@ const char* SpdyFramer::StateToString(int state) { 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"; } @@ -593,12 +904,7 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { 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; + UpdateCurrentFrameBuffer(&data, &len, bytes_desired); // Check for an empty data frame. if (current_frame_len_ == SpdyFrame::size() && !current_frame.is_control_frame() && @@ -611,10 +917,10 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) { } break; } - remaining_payload_ = current_frame.length(); + remaining_data_ = current_frame.length(); // This is just a sanity check for help debugging early frame errors. - if (remaining_payload_ > 1000000u) { + if (remaining_data_ > 1000000u) { LOG(WARNING) << "Unexpectedly large frame. Spdy session is likely corrupt."; } @@ -672,8 +978,10 @@ void SpdyFramer::ProcessControlFrameHeader() { 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: @@ -691,8 +999,13 @@ void SpdyFramer::ProcessControlFrameHeader() { 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 unknown type: " + LOG(WARNING) << "Valid spdy control frame with unhandled type: " << current_control_frame.type(); DCHECK(false); set_error(SPDY_INVALID_CONTROL_FRAME); @@ -713,14 +1026,10 @@ 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; + size_t bytes_read = UpdateCurrentFrameBuffer(&data, &len, + remaining_control_payload_); + remaining_control_payload_ -= bytes_read; + remaining_data_ -= bytes_read; if (remaining_control_payload_) break; } @@ -744,8 +1053,8 @@ 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 (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 = @@ -787,11 +1096,11 @@ size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) { } data += amount_to_forward; len -= amount_to_forward; - remaining_payload_ -= 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_payload_ && + 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()); @@ -915,6 +1224,194 @@ 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) @@ -1121,10 +1618,15 @@ size_t SpdyFramer::BytesSafeToRead() const { return SpdyFrame::size() - current_frame_len_; case SPDY_INTERPRET_CONTROL_FRAME_COMMON_HEADER: return 0; + // TODO(rtenneti): Add support for SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK + // and SPDY_CONTROL_FRAME_HEADER_BLOCK. + case SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK: + case SPDY_CONTROL_FRAME_HEADER_BLOCK: + return 0; case SPDY_CONTROL_FRAME_PAYLOAD: case SPDY_IGNORE_REMAINING_PAYLOAD: case SPDY_FORWARD_STREAM_FRAME: - return remaining_payload_; + return remaining_data_; } // We should never get to here. return 0; @@ -1139,7 +1641,7 @@ void SpdyFramer::set_error(SpdyError error) { void SpdyFramer::ExpandControlFrameBuffer(size_t size) { size_t alloc_size = size + SpdyFrame::size(); - DCHECK_LT(alloc_size, kControlFrameBufferMaxSize); + DCHECK_LE(alloc_size, kControlFrameBufferMaxSize); if (alloc_size <= current_frame_capacity_) return; char* new_buffer = new char[alloc_size]; |