Index: net/spdy/spdy_framer.cc |
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc |
index ed21610524b481a5b96615471de335133553d70f..9a3203b0ee6b6b36cdb9495293c65e39fcd64be8 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,274 +156,56 @@ 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::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); |
+ } |
} |
-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); |
+bool SpdyFramer::ParseHeaderBlock(const SpdyFrame* frame, |
+ SpdyHeaderBlock* block) { |
+ SpdyControlFrame control_frame(frame->data(), false); |
+ uint32 type = control_frame.type(); |
+ if (type != SYN_STREAM && type != SYN_REPLY && type != HEADERS) |
+ return false; |
- // 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; |
- } |
+ // Find the header data within the control frame. |
+ scoped_ptr<SpdyFrame> decompressed_frame(DecompressFrame(*frame)); |
+ if (!decompressed_frame.get()) |
+ return false; |
- // 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; |
- } |
+ const char *header_data = NULL; |
+ int header_length = 0; |
- // Do some sanity checking on the control frame sizes. |
- switch (current_control_frame.type()) { |
+ switch (type) { |
case SYN_STREAM: |
- if (current_control_frame.length() < |
- SpdySynStreamControlFrame::size() - SpdyControlFrame::size()) |
- set_error(SPDY_INVALID_CONTROL_FRAME); |
+ { |
+ SpdySynStreamControlFrame syn_frame(decompressed_frame->data(), false); |
+ header_data = syn_frame.header_block(); |
+ header_length = syn_frame.header_block_len(); |
+ } |
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); |
+ { |
+ SpdySynReplyControlFrame syn_frame(decompressed_frame->data(), false); |
+ header_data = syn_frame.header_block(); |
+ header_length = syn_frame.header_block_len(); |
+ } |
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; |
-} |
- |
-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, |
- SpdyHeaderBlock* block) { |
- SpdyControlFrame control_frame(frame->data(), false); |
- uint32 type = control_frame.type(); |
- if (type != SYN_STREAM && type != SYN_REPLY && type != HEADERS) |
- return false; |
- |
- // Find the header data within the control frame. |
- scoped_ptr<SpdyFrame> decompressed_frame(DecompressFrame(*frame)); |
- if (!decompressed_frame.get()) |
- return false; |
- |
- const char *header_data = NULL; |
- int header_length = 0; |
- |
- switch (type) { |
- case SYN_STREAM: |
- { |
- SpdySynStreamControlFrame syn_frame(decompressed_frame->data(), false); |
- header_data = syn_frame.header_block(); |
- header_length = syn_frame.header_block_len(); |
- } |
- break; |
- case SYN_REPLY: |
- { |
- SpdySynReplyControlFrame syn_frame(decompressed_frame->data(), false); |
- header_data = syn_frame.header_block(); |
- header_length = syn_frame.header_block_len(); |
- } |
- break; |
- case HEADERS: |
- { |
- SpdyHeadersControlFrame header_frame(decompressed_frame->data(), false); |
- header_data = header_frame.header_block(); |
- header_length = header_frame.header_block_len(); |
- } |
+ { |
+ SpdyHeadersControlFrame header_frame(decompressed_frame->data(), false); |
+ header_data = header_frame.header_block(); |
+ header_length = header_frame.header_block_len(); |
+ } |
break; |
} |
@@ -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) { |
@@ -743,49 +470,335 @@ SpdyDataFrame* SpdyFramer::CreateDataFrame(SpdyStreamId stream_id, |
flags_length.flags_[0] = flags; |
frame.WriteBytes(&flags_length, sizeof(flags_length)); |
- frame.WriteBytes(data, len); |
- scoped_ptr<SpdyFrame> data_frame(frame.take()); |
- SpdyDataFrame* rv; |
- if (flags & DATA_FLAG_COMPRESSED) { |
- rv = reinterpret_cast<SpdyDataFrame*>(CompressFrame(*data_frame.get())); |
- } else { |
- rv = reinterpret_cast<SpdyDataFrame*>(data_frame.release()); |
- } |
+ frame.WriteBytes(data, len); |
+ scoped_ptr<SpdyFrame> data_frame(frame.take()); |
+ SpdyDataFrame* rv; |
+ if (flags & DATA_FLAG_COMPRESSED) { |
+ rv = reinterpret_cast<SpdyDataFrame*>(CompressFrame(*data_frame.get())); |
+ } else { |
+ rv = reinterpret_cast<SpdyDataFrame*>(data_frame.release()); |
+ } |
+ |
+ if (flags & DATA_FLAG_FIN) { |
+ CleanupCompressorForStream(stream_id); |
+ } |
+ |
+ return rv; |
+} |
+ |
+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)); |
+} |
+ |
+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"; |
+} |
+ |
+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 (flags & DATA_FLAG_FIN) { |
- CleanupCompressorForStream(stream_id); |
+ // 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 rv; |
+ return original_len - len; |
} |
-// The following compression setting are based on Brian Olson's analysis. See |
-// https://groups.google.com/group/spdy-dev/browse_thread/thread/dfaf498542fac792 |
-// for more details. |
-static const int kCompressorLevel = 9; |
-static const int kCompressorWindowSizeInBits = 11; |
-static const int kCompressorMemLevel = 1; |
- |
-// This is just a hacked dictionary to use for shrinking HTTP-like headers. |
-// TODO(mbelshe): Use a scientific methodology for computing the dictionary. |
-const char SpdyFramer::kDictionary[] = |
- "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-" |
- "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi" |
- "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser" |
- "-agent10010120020120220320420520630030130230330430530630740040140240340440" |
- "5406407408409410411412413414415416417500501502503504505accept-rangesageeta" |
- "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic" |
- "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran" |
- "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati" |
- "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo" |
- "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe" |
- "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic" |
- "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1" |
- ".1statusversionurl"; |
-const int SpdyFramer::kDictionarySize = arraysize(kDictionary); |
- |
-static uLong dictionary_id = 0; |
- |
z_stream* SpdyFramer::GetHeaderCompressor() { |
if (header_compressor_.get()) |
return header_compressor_.get(); // Already initialized. |
@@ -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 |