summaryrefslogtreecommitdiffstats
path: root/net/spdy/spdy_framer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'net/spdy/spdy_framer.cc')
-rw-r--r--net/spdy/spdy_framer.cc924
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(&current_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(&current_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