|  | // Copyright (c) 2012 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/spdy/buffered_spdy_framer.h" | 
|  |  | 
|  | #include "base/logging.h" | 
|  |  | 
|  | namespace net { | 
|  |  | 
|  | SpdyMajorVersion NextProtoToSpdyMajorVersion(NextProto next_proto) { | 
|  | switch (next_proto) { | 
|  | case kProtoDeprecatedSPDY2: | 
|  | return SPDY2; | 
|  | case kProtoSPDY3: | 
|  | case kProtoSPDY31: | 
|  | return SPDY3; | 
|  | case kProtoHTTP2_14: | 
|  | case kProtoHTTP2: | 
|  | return HTTP2; | 
|  | case kProtoUnknown: | 
|  | case kProtoHTTP11: | 
|  | case kProtoQUIC1SPDY3: | 
|  | break; | 
|  | } | 
|  | NOTREACHED(); | 
|  | return SPDY2; | 
|  | } | 
|  |  | 
|  | BufferedSpdyFramer::BufferedSpdyFramer(SpdyMajorVersion version, | 
|  | bool enable_compression) | 
|  | : spdy_framer_(version), | 
|  | visitor_(NULL), | 
|  | header_buffer_used_(0), | 
|  | header_buffer_valid_(false), | 
|  | header_stream_id_(SpdyFramer::kInvalidStream), | 
|  | frames_received_(0) { | 
|  | spdy_framer_.set_enable_compression(enable_compression); | 
|  | memset(header_buffer_, 0, sizeof(header_buffer_)); | 
|  | } | 
|  |  | 
|  | BufferedSpdyFramer::~BufferedSpdyFramer() { | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::set_visitor( | 
|  | BufferedSpdyFramerVisitorInterface* visitor) { | 
|  | visitor_ = visitor; | 
|  | spdy_framer_.set_visitor(this); | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::set_debug_visitor( | 
|  | SpdyFramerDebugVisitorInterface* debug_visitor) { | 
|  | spdy_framer_.set_debug_visitor(debug_visitor); | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::OnError(SpdyFramer* spdy_framer) { | 
|  | DCHECK(spdy_framer); | 
|  | visitor_->OnError(spdy_framer->error_code()); | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::OnSynStream(SpdyStreamId stream_id, | 
|  | SpdyStreamId associated_stream_id, | 
|  | SpdyPriority priority, | 
|  | bool fin, | 
|  | bool unidirectional) { | 
|  | frames_received_++; | 
|  | DCHECK(!control_frame_fields_.get()); | 
|  | control_frame_fields_.reset(new ControlFrameFields()); | 
|  | control_frame_fields_->type = SYN_STREAM; | 
|  | control_frame_fields_->stream_id = stream_id; | 
|  | control_frame_fields_->associated_stream_id = associated_stream_id; | 
|  | control_frame_fields_->priority = priority; | 
|  | control_frame_fields_->fin = fin; | 
|  | control_frame_fields_->unidirectional = unidirectional; | 
|  |  | 
|  | InitHeaderStreaming(stream_id); | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::OnHeaders(SpdyStreamId stream_id, | 
|  | bool has_priority, | 
|  | SpdyPriority priority, | 
|  | SpdyStreamId parent_stream_id, | 
|  | bool exclusive, | 
|  | bool fin, | 
|  | bool end) { | 
|  | frames_received_++; | 
|  | DCHECK(!control_frame_fields_.get()); | 
|  | control_frame_fields_.reset(new ControlFrameFields()); | 
|  | control_frame_fields_->type = HEADERS; | 
|  | control_frame_fields_->stream_id = stream_id; | 
|  | control_frame_fields_->has_priority = has_priority; | 
|  | if (control_frame_fields_->has_priority) { | 
|  | control_frame_fields_->priority = priority; | 
|  | control_frame_fields_->parent_stream_id = parent_stream_id; | 
|  | control_frame_fields_->exclusive = exclusive; | 
|  | } | 
|  | control_frame_fields_->fin = fin; | 
|  |  | 
|  | InitHeaderStreaming(stream_id); | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::OnSynReply(SpdyStreamId stream_id, | 
|  | bool fin) { | 
|  | frames_received_++; | 
|  | DCHECK(!control_frame_fields_.get()); | 
|  | control_frame_fields_.reset(new ControlFrameFields()); | 
|  | control_frame_fields_->type = SYN_REPLY; | 
|  | control_frame_fields_->stream_id = stream_id; | 
|  | control_frame_fields_->fin = fin; | 
|  |  | 
|  | InitHeaderStreaming(stream_id); | 
|  | } | 
|  |  | 
|  | bool BufferedSpdyFramer::OnControlFrameHeaderData(SpdyStreamId stream_id, | 
|  | const char* header_data, | 
|  | size_t len) { | 
|  | CHECK_EQ(header_stream_id_, stream_id); | 
|  |  | 
|  | if (len == 0) { | 
|  | // Indicates end-of-header-block. | 
|  | CHECK(header_buffer_valid_); | 
|  |  | 
|  | SpdyHeaderBlock headers; | 
|  | size_t parsed_len = spdy_framer_.ParseHeaderBlockInBuffer( | 
|  | header_buffer_, header_buffer_used_, &headers); | 
|  | // TODO(rch): this really should be checking parsed_len != len, | 
|  | // but a bunch of tests fail.  Need to figure out why. | 
|  | if (parsed_len == 0) { | 
|  | visitor_->OnStreamError( | 
|  | stream_id, "Could not parse Spdy Control Frame Header."); | 
|  | return false; | 
|  | } | 
|  | DCHECK(control_frame_fields_.get()); | 
|  | switch (control_frame_fields_->type) { | 
|  | case SYN_STREAM: | 
|  | visitor_->OnSynStream(control_frame_fields_->stream_id, | 
|  | control_frame_fields_->associated_stream_id, | 
|  | control_frame_fields_->priority, | 
|  | control_frame_fields_->fin, | 
|  | control_frame_fields_->unidirectional, | 
|  | headers); | 
|  | break; | 
|  | case SYN_REPLY: | 
|  | visitor_->OnSynReply(control_frame_fields_->stream_id, | 
|  | control_frame_fields_->fin, | 
|  | headers); | 
|  | break; | 
|  | case HEADERS: | 
|  | visitor_->OnHeaders(control_frame_fields_->stream_id, | 
|  | control_frame_fields_->has_priority, | 
|  | control_frame_fields_->priority, | 
|  | control_frame_fields_->parent_stream_id, | 
|  | control_frame_fields_->exclusive, | 
|  | control_frame_fields_->fin, headers); | 
|  | break; | 
|  | case PUSH_PROMISE: | 
|  | DCHECK_LT(SPDY3, protocol_version()); | 
|  | visitor_->OnPushPromise(control_frame_fields_->stream_id, | 
|  | control_frame_fields_->promised_stream_id, | 
|  | headers); | 
|  | break; | 
|  | default: | 
|  | DCHECK(false) << "Unexpect control frame type: " | 
|  | << control_frame_fields_->type; | 
|  | break; | 
|  | } | 
|  | control_frame_fields_.reset(NULL); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | const size_t available = kHeaderBufferSize - header_buffer_used_; | 
|  | if (len > available) { | 
|  | header_buffer_valid_ = false; | 
|  | visitor_->OnStreamError( | 
|  | stream_id, "Received more data than the allocated size."); | 
|  | return false; | 
|  | } | 
|  | memcpy(header_buffer_ + header_buffer_used_, header_data, len); | 
|  | header_buffer_used_ += len; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::OnDataFrameHeader(SpdyStreamId stream_id, | 
|  | size_t length, | 
|  | bool fin) { | 
|  | frames_received_++; | 
|  | header_stream_id_ = stream_id; | 
|  | visitor_->OnDataFrameHeader(stream_id, length, fin); | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::OnStreamFrameData(SpdyStreamId stream_id, | 
|  | const char* data, | 
|  | size_t len, | 
|  | bool fin) { | 
|  | visitor_->OnStreamFrameData(stream_id, data, len, fin); | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::OnStreamPadding(SpdyStreamId stream_id, size_t len) { | 
|  | visitor_->OnStreamPadding(stream_id, len); | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::OnSettings(bool clear_persisted) { | 
|  | visitor_->OnSettings(clear_persisted); | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::OnSetting(SpdySettingsIds id, | 
|  | uint8 flags, | 
|  | uint32 value) { | 
|  | visitor_->OnSetting(id, flags, value); | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::OnSettingsAck() { | 
|  | visitor_->OnSettingsAck(); | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::OnSettingsEnd() { | 
|  | visitor_->OnSettingsEnd(); | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::OnPing(SpdyPingId unique_id, bool is_ack) { | 
|  | visitor_->OnPing(unique_id, is_ack); | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::OnRstStream(SpdyStreamId stream_id, | 
|  | SpdyRstStreamStatus status) { | 
|  | visitor_->OnRstStream(stream_id, status); | 
|  | } | 
|  | void BufferedSpdyFramer::OnGoAway(SpdyStreamId last_accepted_stream_id, | 
|  | SpdyGoAwayStatus status) { | 
|  | visitor_->OnGoAway(last_accepted_stream_id, status); | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::OnWindowUpdate(SpdyStreamId stream_id, | 
|  | int delta_window_size) { | 
|  | visitor_->OnWindowUpdate(stream_id, delta_window_size); | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::OnPushPromise(SpdyStreamId stream_id, | 
|  | SpdyStreamId promised_stream_id, | 
|  | bool end) { | 
|  | DCHECK_LT(SPDY3, protocol_version()); | 
|  | frames_received_++; | 
|  | DCHECK(!control_frame_fields_.get()); | 
|  | control_frame_fields_.reset(new ControlFrameFields()); | 
|  | control_frame_fields_->type = PUSH_PROMISE; | 
|  | control_frame_fields_->stream_id = stream_id; | 
|  | control_frame_fields_->promised_stream_id = promised_stream_id; | 
|  |  | 
|  | InitHeaderStreaming(stream_id); | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::OnContinuation(SpdyStreamId stream_id, bool end) { | 
|  | } | 
|  |  | 
|  | bool BufferedSpdyFramer::OnUnknownFrame(SpdyStreamId stream_id, | 
|  | int frame_type) { | 
|  | return visitor_->OnUnknownFrame(stream_id, frame_type); | 
|  | } | 
|  |  | 
|  | SpdyMajorVersion BufferedSpdyFramer::protocol_version() { | 
|  | return spdy_framer_.protocol_version(); | 
|  | } | 
|  |  | 
|  | size_t BufferedSpdyFramer::ProcessInput(const char* data, size_t len) { | 
|  | return spdy_framer_.ProcessInput(data, len); | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::Reset() { | 
|  | spdy_framer_.Reset(); | 
|  | } | 
|  |  | 
|  | SpdyFramer::SpdyError BufferedSpdyFramer::error_code() const { | 
|  | return spdy_framer_.error_code(); | 
|  | } | 
|  |  | 
|  | SpdyFramer::SpdyState BufferedSpdyFramer::state() const { | 
|  | return spdy_framer_.state(); | 
|  | } | 
|  |  | 
|  | bool BufferedSpdyFramer::MessageFullyRead() { | 
|  | return state() == SpdyFramer::SPDY_FRAME_COMPLETE; | 
|  | } | 
|  |  | 
|  | bool BufferedSpdyFramer::HasError() { | 
|  | return spdy_framer_.HasError(); | 
|  | } | 
|  |  | 
|  | // TODO(jgraettinger): Eliminate uses of this method (prefer | 
|  | // SpdySynStreamIR). | 
|  | SpdyFrame* BufferedSpdyFramer::CreateSynStream( | 
|  | SpdyStreamId stream_id, | 
|  | SpdyStreamId associated_stream_id, | 
|  | SpdyPriority priority, | 
|  | SpdyControlFlags flags, | 
|  | const SpdyHeaderBlock* headers) { | 
|  | SpdySynStreamIR syn_stream(stream_id); | 
|  | syn_stream.set_associated_to_stream_id(associated_stream_id); | 
|  | syn_stream.set_priority(priority); | 
|  | syn_stream.set_fin((flags & CONTROL_FLAG_FIN) != 0); | 
|  | syn_stream.set_unidirectional((flags & CONTROL_FLAG_UNIDIRECTIONAL) != 0); | 
|  | // TODO(hkhalil): Avoid copy here. | 
|  | syn_stream.set_header_block(*headers); | 
|  | return spdy_framer_.SerializeSynStream(syn_stream); | 
|  | } | 
|  |  | 
|  | // TODO(jgraettinger): Eliminate uses of this method (prefer | 
|  | // SpdySynReplyIR). | 
|  | SpdyFrame* BufferedSpdyFramer::CreateSynReply( | 
|  | SpdyStreamId stream_id, | 
|  | SpdyControlFlags flags, | 
|  | const SpdyHeaderBlock* headers) { | 
|  | SpdySynReplyIR syn_reply(stream_id); | 
|  | syn_reply.set_fin(flags & CONTROL_FLAG_FIN); | 
|  | // TODO(hkhalil): Avoid copy here. | 
|  | syn_reply.set_header_block(*headers); | 
|  | return spdy_framer_.SerializeSynReply(syn_reply); | 
|  | } | 
|  |  | 
|  | // TODO(jgraettinger): Eliminate uses of this method (prefer | 
|  | // SpdyRstStreamIR). | 
|  | SpdyFrame* BufferedSpdyFramer::CreateRstStream( | 
|  | SpdyStreamId stream_id, | 
|  | SpdyRstStreamStatus status) const { | 
|  | // RST_STREAM payloads are not part of any SPDY spec. | 
|  | // SpdyFramer will accept them, but don't create them. | 
|  | SpdyRstStreamIR rst_ir(stream_id, status, ""); | 
|  | return spdy_framer_.SerializeRstStream(rst_ir); | 
|  | } | 
|  |  | 
|  | // TODO(jgraettinger): Eliminate uses of this method (prefer | 
|  | // SpdySettingsIR). | 
|  | SpdyFrame* BufferedSpdyFramer::CreateSettings( | 
|  | const SettingsMap& values) const { | 
|  | SpdySettingsIR settings_ir; | 
|  | for (SettingsMap::const_iterator it = values.begin(); | 
|  | it != values.end(); | 
|  | ++it) { | 
|  | settings_ir.AddSetting( | 
|  | it->first, | 
|  | (it->second.first & SETTINGS_FLAG_PLEASE_PERSIST) != 0, | 
|  | (it->second.first & SETTINGS_FLAG_PERSISTED) != 0, | 
|  | it->second.second); | 
|  | } | 
|  | return spdy_framer_.SerializeSettings(settings_ir); | 
|  | } | 
|  |  | 
|  | // TODO(jgraettinger): Eliminate uses of this method (prefer SpdyPingIR). | 
|  | SpdyFrame* BufferedSpdyFramer::CreatePingFrame(SpdyPingId unique_id, | 
|  | bool is_ack) const { | 
|  | SpdyPingIR ping_ir(unique_id); | 
|  | ping_ir.set_is_ack(is_ack); | 
|  | return spdy_framer_.SerializePing(ping_ir); | 
|  | } | 
|  |  | 
|  | // TODO(jgraettinger): Eliminate uses of this method (prefer SpdyGoAwayIR). | 
|  | SpdyFrame* BufferedSpdyFramer::CreateGoAway( | 
|  | SpdyStreamId last_accepted_stream_id, | 
|  | SpdyGoAwayStatus status) const { | 
|  | SpdyGoAwayIR go_ir(last_accepted_stream_id, status, ""); | 
|  | return spdy_framer_.SerializeGoAway(go_ir); | 
|  | } | 
|  |  | 
|  | // TODO(jgraettinger): Eliminate uses of this method (prefer SpdyHeadersIR). | 
|  | SpdyFrame* BufferedSpdyFramer::CreateHeaders( | 
|  | SpdyStreamId stream_id, | 
|  | SpdyControlFlags flags, | 
|  | SpdyPriority priority, | 
|  | const SpdyHeaderBlock* headers) { | 
|  | SpdyHeadersIR headers_ir(stream_id); | 
|  | headers_ir.set_fin((flags & CONTROL_FLAG_FIN) != 0); | 
|  | if (flags & HEADERS_FLAG_PRIORITY) { | 
|  | headers_ir.set_has_priority(true); | 
|  | headers_ir.set_priority(priority); | 
|  | } | 
|  | headers_ir.set_header_block(*headers); | 
|  | return spdy_framer_.SerializeHeaders(headers_ir); | 
|  | } | 
|  |  | 
|  | // TODO(jgraettinger): Eliminate uses of this method (prefer | 
|  | // SpdyWindowUpdateIR). | 
|  | SpdyFrame* BufferedSpdyFramer::CreateWindowUpdate( | 
|  | SpdyStreamId stream_id, | 
|  | uint32 delta_window_size) const { | 
|  | SpdyWindowUpdateIR update_ir(stream_id, delta_window_size); | 
|  | return spdy_framer_.SerializeWindowUpdate(update_ir); | 
|  | } | 
|  |  | 
|  | // TODO(jgraettinger): Eliminate uses of this method (prefer SpdyDataIR). | 
|  | SpdyFrame* BufferedSpdyFramer::CreateDataFrame(SpdyStreamId stream_id, | 
|  | const char* data, | 
|  | uint32 len, | 
|  | SpdyDataFlags flags) { | 
|  | SpdyDataIR data_ir(stream_id, | 
|  | base::StringPiece(data, len)); | 
|  | data_ir.set_fin((flags & DATA_FLAG_FIN) != 0); | 
|  | return spdy_framer_.SerializeData(data_ir); | 
|  | } | 
|  |  | 
|  | // TODO(jgraettinger): Eliminate uses of this method (prefer SpdyPushPromiseIR). | 
|  | SpdyFrame* BufferedSpdyFramer::CreatePushPromise( | 
|  | SpdyStreamId stream_id, | 
|  | SpdyStreamId promised_stream_id, | 
|  | const SpdyHeaderBlock* headers) { | 
|  | SpdyPushPromiseIR push_promise_ir(stream_id, promised_stream_id); | 
|  | push_promise_ir.set_header_block(*headers); | 
|  | return spdy_framer_.SerializePushPromise(push_promise_ir); | 
|  | } | 
|  |  | 
|  | SpdyPriority BufferedSpdyFramer::GetHighestPriority() const { | 
|  | return spdy_framer_.GetHighestPriority(); | 
|  | } | 
|  |  | 
|  | void BufferedSpdyFramer::InitHeaderStreaming(SpdyStreamId stream_id) { | 
|  | memset(header_buffer_, 0, kHeaderBufferSize); | 
|  | header_buffer_used_ = 0; | 
|  | header_buffer_valid_ = true; | 
|  | header_stream_id_ = stream_id; | 
|  | DCHECK_NE(header_stream_id_, SpdyFramer::kInvalidStream); | 
|  | } | 
|  |  | 
|  | }  // namespace net |