| // Copyright 2012 The Chromium Authors |
| // 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 <algorithm> |
| #include <utility> |
| |
| #include "base/check.h" |
| #include "base/strings/string_util.h" |
| #include "base/trace_event/memory_usage_estimator.h" |
| |
| namespace net { |
| |
| namespace { |
| |
| // GOAWAY frame debug data is only buffered up to this many bytes. |
| size_t kGoAwayDebugDataMaxSize = 1024; |
| |
| } // namespace |
| |
| BufferedSpdyFramer::BufferedSpdyFramer(uint32_t max_header_list_size, |
| const NetLogWithSource& net_log, |
| TimeFunc time_func) |
| : spdy_framer_(spdy::SpdyFramer::ENABLE_COMPRESSION), |
| max_header_list_size_(max_header_list_size), |
| net_log_(net_log), |
| time_func_(time_func) { |
| // Do not bother decoding response header payload above the limit. |
| deframer_.GetHpackDecoder()->set_max_decode_buffer_size_bytes( |
| max_header_list_size_); |
| } |
| |
| BufferedSpdyFramer::~BufferedSpdyFramer() = default; |
| |
| void BufferedSpdyFramer::set_visitor( |
| BufferedSpdyFramerVisitorInterface* visitor) { |
| visitor_ = visitor; |
| deframer_.set_visitor(this); |
| } |
| |
| void BufferedSpdyFramer::set_debug_visitor( |
| spdy::SpdyFramerDebugVisitorInterface* debug_visitor) { |
| spdy_framer_.set_debug_visitor(debug_visitor); |
| deframer_.set_debug_visitor(debug_visitor); |
| } |
| |
| void BufferedSpdyFramer::OnError( |
| http2::Http2DecoderAdapter::SpdyFramerError spdy_framer_error, |
| std::string /*detailed_error*/) { |
| visitor_->OnError(spdy_framer_error); |
| } |
| |
| void BufferedSpdyFramer::OnHeaders(spdy::SpdyStreamId stream_id, |
| size_t payload_length, |
| bool has_priority, |
| int weight, |
| spdy::SpdyStreamId parent_stream_id, |
| bool exclusive, |
| bool fin, |
| bool end) { |
| frames_received_++; |
| DCHECK(!control_frame_fields_.get()); |
| control_frame_fields_ = std::make_unique<ControlFrameFields>(); |
| control_frame_fields_->type = spdy::SpdyFrameType::HEADERS; |
| control_frame_fields_->stream_id = stream_id; |
| control_frame_fields_->has_priority = has_priority; |
| if (control_frame_fields_->has_priority) { |
| control_frame_fields_->weight = weight; |
| control_frame_fields_->parent_stream_id = parent_stream_id; |
| control_frame_fields_->exclusive = exclusive; |
| } |
| control_frame_fields_->fin = fin; |
| control_frame_fields_->recv_first_byte_time = time_func_(); |
| } |
| |
| void BufferedSpdyFramer::OnDataFrameHeader(spdy::SpdyStreamId stream_id, |
| size_t length, |
| bool fin) { |
| frames_received_++; |
| visitor_->OnDataFrameHeader(stream_id, length, fin); |
| } |
| |
| void BufferedSpdyFramer::OnStreamFrameData(spdy::SpdyStreamId stream_id, |
| const char* data, |
| size_t len) { |
| visitor_->OnStreamFrameData(stream_id, data, len); |
| } |
| |
| void BufferedSpdyFramer::OnStreamEnd(spdy::SpdyStreamId stream_id) { |
| visitor_->OnStreamEnd(stream_id); |
| } |
| |
| void BufferedSpdyFramer::OnStreamPadLength(spdy::SpdyStreamId stream_id, |
| size_t value) { |
| // Deliver the stream pad length byte for flow control handling. |
| visitor_->OnStreamPadding(stream_id, 1); |
| } |
| |
| void BufferedSpdyFramer::OnStreamPadding(spdy::SpdyStreamId stream_id, |
| size_t len) { |
| visitor_->OnStreamPadding(stream_id, len); |
| } |
| |
| spdy::SpdyHeadersHandlerInterface* BufferedSpdyFramer::OnHeaderFrameStart( |
| spdy::SpdyStreamId stream_id) { |
| coalescer_ = |
| std::make_unique<HeaderCoalescer>(max_header_list_size_, net_log_); |
| return coalescer_.get(); |
| } |
| |
| void BufferedSpdyFramer::OnHeaderFrameEnd(spdy::SpdyStreamId stream_id) { |
| if (coalescer_->error_seen()) { |
| visitor_->OnStreamError(stream_id, |
| "Could not parse Spdy Control Frame Header."); |
| control_frame_fields_.reset(); |
| return; |
| } |
| DCHECK(control_frame_fields_.get()); |
| switch (control_frame_fields_->type) { |
| case spdy::SpdyFrameType::HEADERS: |
| visitor_->OnHeaders( |
| control_frame_fields_->stream_id, control_frame_fields_->has_priority, |
| control_frame_fields_->weight, |
| control_frame_fields_->parent_stream_id, |
| control_frame_fields_->exclusive, control_frame_fields_->fin, |
| coalescer_->release_headers(), |
| control_frame_fields_->recv_first_byte_time); |
| break; |
| case spdy::SpdyFrameType::PUSH_PROMISE: |
| visitor_->OnPushPromise(control_frame_fields_->stream_id, |
| control_frame_fields_->promised_stream_id, |
| coalescer_->release_headers()); |
| break; |
| default: |
| DCHECK(false) << "Unexpect control frame type: " |
| << control_frame_fields_->type; |
| break; |
| } |
| control_frame_fields_.reset(nullptr); |
| } |
| |
| void BufferedSpdyFramer::OnSettings() { |
| visitor_->OnSettings(); |
| } |
| |
| void BufferedSpdyFramer::OnSetting(spdy::SpdySettingsId id, uint32_t value) { |
| visitor_->OnSetting(id, value); |
| } |
| |
| void BufferedSpdyFramer::OnSettingsAck() { |
| visitor_->OnSettingsAck(); |
| } |
| |
| void BufferedSpdyFramer::OnSettingsEnd() { |
| visitor_->OnSettingsEnd(); |
| } |
| |
| void BufferedSpdyFramer::OnPing(spdy::SpdyPingId unique_id, bool is_ack) { |
| visitor_->OnPing(unique_id, is_ack); |
| } |
| |
| void BufferedSpdyFramer::OnRstStream(spdy::SpdyStreamId stream_id, |
| spdy::SpdyErrorCode error_code) { |
| visitor_->OnRstStream(stream_id, error_code); |
| } |
| void BufferedSpdyFramer::OnGoAway(spdy::SpdyStreamId last_accepted_stream_id, |
| spdy::SpdyErrorCode error_code) { |
| DCHECK(!goaway_fields_); |
| goaway_fields_ = std::make_unique<GoAwayFields>(); |
| goaway_fields_->last_accepted_stream_id = last_accepted_stream_id; |
| goaway_fields_->error_code = error_code; |
| } |
| |
| bool BufferedSpdyFramer::OnGoAwayFrameData(const char* goaway_data, |
| size_t len) { |
| if (len > 0) { |
| if (goaway_fields_->debug_data.size() < kGoAwayDebugDataMaxSize) { |
| goaway_fields_->debug_data.append( |
| goaway_data, std::min(len, kGoAwayDebugDataMaxSize - |
| goaway_fields_->debug_data.size())); |
| } |
| return true; |
| } |
| visitor_->OnGoAway(goaway_fields_->last_accepted_stream_id, |
| goaway_fields_->error_code, goaway_fields_->debug_data); |
| goaway_fields_.reset(); |
| return true; |
| } |
| |
| void BufferedSpdyFramer::OnWindowUpdate(spdy::SpdyStreamId stream_id, |
| int delta_window_size) { |
| visitor_->OnWindowUpdate(stream_id, delta_window_size); |
| } |
| |
| void BufferedSpdyFramer::OnPushPromise(spdy::SpdyStreamId stream_id, |
| spdy::SpdyStreamId promised_stream_id, |
| bool end) { |
| frames_received_++; |
| DCHECK(!control_frame_fields_.get()); |
| control_frame_fields_ = std::make_unique<ControlFrameFields>(); |
| control_frame_fields_->type = spdy::SpdyFrameType::PUSH_PROMISE; |
| control_frame_fields_->stream_id = stream_id; |
| control_frame_fields_->promised_stream_id = promised_stream_id; |
| control_frame_fields_->recv_first_byte_time = time_func_(); |
| } |
| |
| void BufferedSpdyFramer::OnAltSvc( |
| spdy::SpdyStreamId stream_id, |
| absl::string_view origin, |
| const spdy::SpdyAltSvcWireFormat::AlternativeServiceVector& altsvc_vector) { |
| visitor_->OnAltSvc(stream_id, origin, altsvc_vector); |
| } |
| |
| void BufferedSpdyFramer::OnContinuation(spdy::SpdyStreamId stream_id, |
| size_t payload_length, |
| bool end) {} |
| |
| bool BufferedSpdyFramer::OnUnknownFrame(spdy::SpdyStreamId stream_id, |
| uint8_t frame_type) { |
| return visitor_->OnUnknownFrame(stream_id, frame_type); |
| } |
| |
| size_t BufferedSpdyFramer::ProcessInput(const char* data, size_t len) { |
| return deframer_.ProcessInput(data, len); |
| } |
| |
| void BufferedSpdyFramer::UpdateHeaderDecoderTableSize(uint32_t value) { |
| deframer_.GetHpackDecoder()->ApplyHeaderTableSizeSetting(value); |
| } |
| |
| http2::Http2DecoderAdapter::SpdyFramerError |
| BufferedSpdyFramer::spdy_framer_error() const { |
| return deframer_.spdy_framer_error(); |
| } |
| |
| http2::Http2DecoderAdapter::SpdyState BufferedSpdyFramer::state() const { |
| return deframer_.state(); |
| } |
| |
| bool BufferedSpdyFramer::MessageFullyRead() { |
| return state() == http2::Http2DecoderAdapter::SPDY_FRAME_COMPLETE; |
| } |
| |
| bool BufferedSpdyFramer::HasError() { |
| return deframer_.HasError(); |
| } |
| |
| // TODO(jgraettinger): Eliminate uses of this method (prefer |
| // spdy::SpdyRstStreamIR). |
| std::unique_ptr<spdy::SpdySerializedFrame> BufferedSpdyFramer::CreateRstStream( |
| spdy::SpdyStreamId stream_id, |
| spdy::SpdyErrorCode error_code) const { |
| spdy::SpdyRstStreamIR rst_ir(stream_id, error_code); |
| return std::make_unique<spdy::SpdySerializedFrame>( |
| spdy_framer_.SerializeRstStream(rst_ir)); |
| } |
| |
| // TODO(jgraettinger): Eliminate uses of this method (prefer |
| // spdy::SpdySettingsIR). |
| std::unique_ptr<spdy::SpdySerializedFrame> BufferedSpdyFramer::CreateSettings( |
| const spdy::SettingsMap& values) const { |
| spdy::SpdySettingsIR settings_ir; |
| for (const auto& it : values) { |
| settings_ir.AddSetting(it.first, it.second); |
| } |
| return std::make_unique<spdy::SpdySerializedFrame>( |
| spdy_framer_.SerializeSettings(settings_ir)); |
| } |
| |
| // TODO(jgraettinger): Eliminate uses of this method (prefer spdy::SpdyPingIR). |
| std::unique_ptr<spdy::SpdySerializedFrame> BufferedSpdyFramer::CreatePingFrame( |
| spdy::SpdyPingId unique_id, |
| bool is_ack) const { |
| spdy::SpdyPingIR ping_ir(unique_id); |
| ping_ir.set_is_ack(is_ack); |
| return std::make_unique<spdy::SpdySerializedFrame>( |
| spdy_framer_.SerializePing(ping_ir)); |
| } |
| |
| // TODO(jgraettinger): Eliminate uses of this method (prefer |
| // spdy::SpdyWindowUpdateIR). |
| std::unique_ptr<spdy::SpdySerializedFrame> |
| BufferedSpdyFramer::CreateWindowUpdate(spdy::SpdyStreamId stream_id, |
| uint32_t delta_window_size) const { |
| spdy::SpdyWindowUpdateIR update_ir(stream_id, delta_window_size); |
| return std::make_unique<spdy::SpdySerializedFrame>( |
| spdy_framer_.SerializeWindowUpdate(update_ir)); |
| } |
| |
| // TODO(jgraettinger): Eliminate uses of this method (prefer spdy::SpdyDataIR). |
| std::unique_ptr<spdy::SpdySerializedFrame> BufferedSpdyFramer::CreateDataFrame( |
| spdy::SpdyStreamId stream_id, |
| const char* data, |
| uint32_t len, |
| spdy::SpdyDataFlags flags) { |
| spdy::SpdyDataIR data_ir(stream_id, std::string_view(data, len)); |
| data_ir.set_fin((flags & spdy::DATA_FLAG_FIN) != 0); |
| return std::make_unique<spdy::SpdySerializedFrame>( |
| spdy_framer_.SerializeData(data_ir)); |
| } |
| |
| // TODO(jgraettinger): Eliminate uses of this method (prefer |
| // spdy::SpdyPriorityIR). |
| std::unique_ptr<spdy::SpdySerializedFrame> BufferedSpdyFramer::CreatePriority( |
| spdy::SpdyStreamId stream_id, |
| spdy::SpdyStreamId dependency_id, |
| int weight, |
| bool exclusive) const { |
| spdy::SpdyPriorityIR priority_ir(stream_id, dependency_id, weight, exclusive); |
| return std::make_unique<spdy::SpdySerializedFrame>( |
| spdy_framer_.SerializePriority(priority_ir)); |
| } |
| |
| void BufferedSpdyFramer::UpdateHeaderEncoderTableSize(uint32_t value) { |
| spdy_framer_.UpdateHeaderEncoderTableSize(value); |
| } |
| |
| uint32_t BufferedSpdyFramer::header_encoder_table_size() const { |
| return spdy_framer_.header_encoder_table_size(); |
| } |
| |
| BufferedSpdyFramer::ControlFrameFields::ControlFrameFields() = default; |
| |
| } // namespace net |