// Copyright 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_headers_stream.h" #include "base/strings/stringprintf.h" #include "net/quic/quic_session.h" using base::StringPiece; using std::string; namespace net { namespace { const QuicStreamId kInvalidStreamId = 0; } // namespace // A SpdyFramer visitor which passed SYN_STREAM and SYN_REPLY frames to // the QuicDataStream, and closes the connection if any unexpected frames // are received. class QuicHeadersStream::SpdyFramerVisitor : public SpdyFramerVisitorInterface, public SpdyFramerDebugVisitorInterface { public: SpdyFramerVisitor(SpdyMajorVersion spdy_version, QuicHeadersStream* stream) : spdy_version_(spdy_version), stream_(stream) {} // SpdyFramerVisitorInterface implementation void OnSynStream(SpdyStreamId stream_id, SpdyStreamId associated_stream_id, SpdyPriority priority, bool fin, bool unidirectional) override { CloseConnection("SPDY SYN_STREAM frame received."); } void OnSynReply(SpdyStreamId stream_id, bool fin) override { CloseConnection("SPDY SYN_REPLY frame received."); } bool OnControlFrameHeaderData(SpdyStreamId stream_id, const char* header_data, size_t len) override { if (!stream_->IsConnected()) { return false; } stream_->OnControlFrameHeaderData(stream_id, header_data, len); return true; } void OnStreamFrameData(SpdyStreamId stream_id, const char* data, size_t len, bool fin) override { if (fin && len == 0) { // The framer invokes OnStreamFrameData with zero-length data and // fin = true after processing a SYN_STREAM or SYN_REPLY frame // that had the fin bit set. return; } CloseConnection("SPDY DATA frame received."); } void OnStreamPadding(SpdyStreamId stream_id, size_t len) override { CloseConnection("SPDY frame padding received."); } void OnError(SpdyFramer* framer) override { CloseConnection(base::StringPrintf( "SPDY framing error: %s", SpdyFramer::ErrorCodeToString(framer->error_code()))); } void OnDataFrameHeader(SpdyStreamId stream_id, size_t length, bool fin) override { CloseConnection("SPDY DATA frame received."); } void OnRstStream(SpdyStreamId stream_id, SpdyRstStreamStatus status) override { CloseConnection("SPDY RST_STREAM frame received."); } void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) override { CloseConnection("SPDY SETTINGS frame received."); } void OnSettingsAck() override { CloseConnection("SPDY SETTINGS frame received."); } void OnSettingsEnd() override { CloseConnection("SPDY SETTINGS frame received."); } void OnPing(SpdyPingId unique_id, bool is_ack) override { CloseConnection("SPDY PING frame received."); } void OnGoAway(SpdyStreamId last_accepted_stream_id, SpdyGoAwayStatus status) override { CloseConnection("SPDY GOAWAY frame received."); } void OnHeaders(SpdyStreamId stream_id, bool has_priority, SpdyPriority priority, bool fin, bool end) override { if (!stream_->IsConnected()) { return; } if (has_priority) { stream_->OnSynStream(stream_id, priority, fin); } else { stream_->OnSynReply(stream_id, fin); } } void OnWindowUpdate(SpdyStreamId stream_id, uint32 delta_window_size) override { CloseConnection("SPDY WINDOW_UPDATE frame received."); } void OnPushPromise(SpdyStreamId stream_id, SpdyStreamId promised_stream_id, bool end) override { CloseConnection("SPDY PUSH_PROMISE frame received."); } void OnContinuation(SpdyStreamId stream_id, bool end) override { } bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) override { CloseConnection("Unknown frame type received."); return false; } // SpdyFramerDebugVisitorInterface implementation void OnSendCompressedFrame(SpdyStreamId stream_id, SpdyFrameType type, size_t payload_len, size_t frame_len) override {} void OnReceiveCompressedFrame(SpdyStreamId stream_id, SpdyFrameType type, size_t frame_len) override { if (stream_->IsConnected()) { stream_->OnCompressedFrameSize(frame_len); } } private: void CloseConnection(const string& details) { if (stream_->IsConnected()) { stream_->CloseConnectionWithDetails( QUIC_INVALID_HEADERS_STREAM_DATA, details); } } private: SpdyMajorVersion spdy_version_; QuicHeadersStream* stream_; DISALLOW_COPY_AND_ASSIGN(SpdyFramerVisitor); }; QuicHeadersStream::QuicHeadersStream(QuicSession* session) : ReliableQuicStream(kHeadersStreamId, session), stream_id_(kInvalidStreamId), fin_(false), frame_len_(0), spdy_framer_(SPDY4), spdy_framer_visitor_(new SpdyFramerVisitor(SPDY4, this)) { spdy_framer_.set_visitor(spdy_framer_visitor_.get()); spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get()); // The headers stream is exempt from connection level flow control. DisableConnectionFlowControlForThisStream(); } QuicHeadersStream::~QuicHeadersStream() {} size_t QuicHeadersStream::WriteHeaders( QuicStreamId stream_id, const SpdyHeaderBlock& headers, bool fin, QuicPriority priority, QuicAckNotifier::DelegateInterface* ack_notifier_delegate) { SpdyHeadersIR headers_frame(stream_id); headers_frame.set_name_value_block(headers); headers_frame.set_fin(fin); if (session()->perspective() == Perspective::IS_CLIENT) { headers_frame.set_has_priority(true); headers_frame.set_priority(priority); } scoped_ptr frame( spdy_framer_.SerializeFrame(headers_frame)); WriteOrBufferData(StringPiece(frame->data(), frame->size()), false, ack_notifier_delegate); return frame->size(); } uint32 QuicHeadersStream::ProcessRawData(const char* data, uint32 data_len) { return spdy_framer_.ProcessInput(data, data_len); } QuicPriority QuicHeadersStream::EffectivePriority() const { return 0; } void QuicHeadersStream::OnSynStream(SpdyStreamId stream_id, SpdyPriority priority, bool fin) { if (session()->perspective() == Perspective::IS_CLIENT) { CloseConnectionWithDetails( QUIC_INVALID_HEADERS_STREAM_DATA, "SPDY SYN_STREAM frame received at the client"); return; } DCHECK_EQ(kInvalidStreamId, stream_id_); stream_id_ = stream_id; fin_ = fin; session()->OnStreamHeadersPriority(stream_id, priority); } void QuicHeadersStream::OnSynReply(SpdyStreamId stream_id, bool fin) { if (session()->perspective() == Perspective::IS_SERVER) { CloseConnectionWithDetails( QUIC_INVALID_HEADERS_STREAM_DATA, "SPDY SYN_REPLY frame received at the server"); return; } DCHECK_EQ(kInvalidStreamId, stream_id_); stream_id_ = stream_id; fin_ = fin; } void QuicHeadersStream::OnControlFrameHeaderData(SpdyStreamId stream_id, const char* header_data, size_t len) { DCHECK_EQ(stream_id_, stream_id); if (len == 0) { DCHECK_NE(0u, stream_id_); DCHECK_NE(0u, frame_len_); session()->OnStreamHeadersComplete(stream_id_, fin_, frame_len_); // Reset state for the next frame. stream_id_ = kInvalidStreamId; fin_ = false; frame_len_ = 0; } else { session()->OnStreamHeaders(stream_id_, StringPiece(header_data, len)); } } void QuicHeadersStream::OnCompressedFrameSize(size_t frame_len) { frame_len_ += frame_len; } bool QuicHeadersStream::IsConnected() { return session()->connection()->connected(); } } // namespace net