| // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/quic/quic_spdy_decompressor.h" |
| |
| #include <algorithm> |
| |
| #include "base/logging.h" |
| |
| using base::StringPiece; |
| using std::min; |
| |
| namespace net { |
| |
| class SpdyFramerVisitor : public SpdyFramerVisitorInterface { |
| public: |
| explicit SpdyFramerVisitor(QuicSpdyDecompressor::Visitor* visitor) |
| : visitor_(visitor), |
| error_(false) { |
| } |
| |
| virtual void OnError(SpdyFramer* framer) OVERRIDE { |
| error_ = true; |
| } |
| virtual void OnDataFrameHeader(SpdyStreamId stream_id, |
| size_t length, |
| bool fin) OVERRIDE {} |
| virtual void OnStreamFrameData(SpdyStreamId stream_id, |
| const char* data, |
| size_t len, |
| bool fin) OVERRIDE {} |
| virtual bool OnControlFrameHeaderData(SpdyStreamId stream_id, |
| const char* header_data, |
| size_t len) OVERRIDE; |
| virtual void OnSynStream(SpdyStreamId stream_id, |
| SpdyStreamId associated_stream_id, |
| SpdyPriority priority, |
| uint8 credential_slot, |
| bool fin, |
| bool unidirectional) OVERRIDE {} |
| virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE {} |
| virtual void OnRstStream(SpdyStreamId stream_id, |
| SpdyRstStreamStatus status) OVERRIDE {} |
| virtual void OnSetting(SpdySettingsIds id, |
| uint8 flags, |
| uint32 value) OVERRIDE {} |
| virtual void OnPing(uint32 unique_id) OVERRIDE {} |
| virtual void OnGoAway(SpdyStreamId last_accepted_stream_id, |
| SpdyGoAwayStatus status) OVERRIDE {} |
| virtual void OnHeaders(SpdyStreamId stream_id, bool fin) OVERRIDE {} |
| virtual void OnWindowUpdate(SpdyStreamId stream_id, |
| uint32 delta_window_size) OVERRIDE {} |
| virtual bool OnCredentialFrameData(const char* credential_data, |
| size_t len) OVERRIDE { |
| return false; |
| } |
| virtual void OnPushPromise(SpdyStreamId stream_id, |
| SpdyStreamId promised_stream_id) OVERRIDE {} |
| void set_visitor(QuicSpdyDecompressor::Visitor* visitor) { |
| DCHECK(visitor); |
| visitor_ = visitor; |
| } |
| |
| private: |
| QuicSpdyDecompressor::Visitor* visitor_; |
| bool error_; |
| }; |
| |
| bool SpdyFramerVisitor::OnControlFrameHeaderData(SpdyStreamId stream_id, |
| const char* header_data, |
| size_t len) { |
| DCHECK(visitor_); |
| return visitor_->OnDecompressedData(StringPiece(header_data, len)); |
| } |
| |
| QuicSpdyDecompressor::QuicSpdyDecompressor() |
| : spdy_framer_(SPDY3), |
| spdy_visitor_(new SpdyFramerVisitor(NULL)), |
| current_header_id_(1), |
| has_current_compressed_size_(false), |
| current_compressed_size_(0), |
| compressed_bytes_consumed_(0) { |
| spdy_framer_.set_visitor(spdy_visitor_.get()); |
| } |
| |
| QuicSpdyDecompressor::~QuicSpdyDecompressor() { |
| } |
| |
| size_t QuicSpdyDecompressor::DecompressData(StringPiece data, |
| Visitor* visitor) { |
| spdy_visitor_->set_visitor(visitor); |
| size_t bytes_consumed = 0; |
| |
| if (!has_current_compressed_size_) { |
| const size_t kCompressedBufferSizeSize = sizeof(uint32); |
| DCHECK_GT(kCompressedBufferSizeSize, compressed_size_buffer_.length()); |
| size_t missing_size = |
| kCompressedBufferSizeSize - compressed_size_buffer_.length(); |
| if (data.length() < missing_size) { |
| data.AppendToString(&compressed_size_buffer_); |
| return data.length(); |
| } |
| bytes_consumed += missing_size; |
| data.substr(0, missing_size).AppendToString(&compressed_size_buffer_); |
| DCHECK_EQ(kCompressedBufferSizeSize, compressed_size_buffer_.length()); |
| memcpy(¤t_compressed_size_, compressed_size_buffer_.data(), |
| kCompressedBufferSizeSize); |
| compressed_size_buffer_.clear(); |
| has_current_compressed_size_ = true; |
| data = data.substr(missing_size); |
| compressed_bytes_consumed_ = 0; |
| } |
| |
| size_t bytes_to_consume = |
| min(current_compressed_size_ - compressed_bytes_consumed_, |
| static_cast<uint32>(data.length())); |
| if (bytes_to_consume > 0) { |
| if (!spdy_framer_.IncrementallyDecompressControlFrameHeaderData( |
| current_header_id_, data.data(), bytes_to_consume)) { |
| visitor->OnDecompressionError(); |
| return bytes_consumed; |
| } |
| compressed_bytes_consumed_ += bytes_to_consume; |
| bytes_consumed += bytes_to_consume; |
| } |
| if (current_compressed_size_ - compressed_bytes_consumed_ == 0) { |
| ResetForNextHeaders(); |
| } |
| return bytes_consumed; |
| } |
| |
| void QuicSpdyDecompressor::ResetForNextHeaders() { |
| has_current_compressed_size_ = false; |
| current_compressed_size_ = 0; |
| compressed_bytes_consumed_ = 0; |
| ++current_header_id_; |
| } |
| |
| } // namespace net |