// 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. #ifndef NET_SPDY_SPDY_STREAM_H_ #define NET_SPDY_SPDY_STREAM_H_ #include #include #include #include "base/basictypes.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" #include "base/memory/weak_ptr.h" #include "googleurl/src/gurl.h" #include "net/base/bandwidth_metrics.h" #include "net/base/io_buffer.h" #include "net/base/net_export.h" #include "net/base/net_log.h" #include "net/base/request_priority.h" #include "net/socket/ssl_client_socket.h" #include "net/spdy/spdy_framer.h" #include "net/spdy/spdy_header_block.h" #include "net/spdy/spdy_protocol.h" #include "net/spdy/spdy_session.h" #include "net/ssl/server_bound_cert_service.h" #include "net/ssl/ssl_client_cert_type.h" namespace net { class AddressList; class IPEndPoint; class SSLCertRequestInfo; class SSLInfo; // Returned by some SpdyStream::Delegate functions to indicate whether // there's more data to send. enum SpdySendStatus { MORE_DATA_TO_SEND, NO_MORE_DATA_TO_SEND }; // The SpdyStream is used by the SpdySession to represent each stream known // on the SpdySession. This class provides interfaces for SpdySession to use. // Streams can be created either by the client or by the server. When they // are initiated by the client, both the SpdySession and client object (such as // a SpdyNetworkTransaction) will maintain a reference to the stream. When // initiated by the server, only the SpdySession will maintain any reference, // until such a time as a client object requests a stream for the path. class NET_EXPORT_PRIVATE SpdyStream : public base::RefCounted { public: // Delegate handles protocol specific behavior of spdy stream. class NET_EXPORT_PRIVATE Delegate { public: Delegate() {} // Called when SYN frame has been sent. // Returns true if no more data to be sent after SYN frame. virtual SpdySendStatus OnSendHeadersComplete() = 0; // Called when stream is ready to send data. // Returns network error code. OK when it successfully sent data. // ERR_IO_PENDING when performing operation asynchronously. virtual int OnSendBody() = 0; // Called when body data has been sent. |bytes_sent| is the number // of bytes that has been sent (may be zero). Must return whether // there's more body data to send. virtual SpdySendStatus OnSendBodyComplete(size_t bytes_sent) = 0; // Called when the SYN_STREAM, SYN_REPLY, or HEADERS frames are received. // Normal streams will receive a SYN_REPLY and optional HEADERS frames. // Pushed streams will receive a SYN_STREAM and optional HEADERS frames. // Because a stream may have a SYN_* frame and multiple HEADERS frames, // this callback may be called multiple times. // |status| indicates network error. Returns network error code. virtual int OnResponseReceived(const SpdyHeaderBlock& response, base::Time response_time, int status) = 0; // Called when a HEADERS frame is sent. virtual void OnHeadersSent() = 0; // Called when data is received. |buffer| may be NULL, which // signals EOF. Must return OK if the data was received // successfully, or a network error code otherwise. virtual int OnDataReceived(scoped_ptr buffer) = 0; // Called when data is sent. virtual void OnDataSent(size_t bytes_sent) = 0; // Called when SpdyStream is closed. No other delegate functions // will be called after this is called, and the delegate must not // access the stream after this is called. virtual void OnClose(int status) = 0; protected: virtual ~Delegate() {} private: DISALLOW_COPY_AND_ASSIGN(Delegate); }; // SpdyStream constructor SpdyStream(SpdySession* session, const std::string& path, RequestPriority priority, int32 initial_send_window_size, int32 initial_recv_window_size, bool pushed, const BoundNetLog& net_log); // Set new |delegate|. |delegate| must not be NULL. // If it already received SYN_REPLY or data, OnResponseReceived() or // OnDataReceived() will be called. void SetDelegate(Delegate* delegate); Delegate* GetDelegate() { return delegate_; } // Detach the delegate from the stream, which must not yet be // closed, and cancel it. void DetachDelegate(); // Is this stream a pushed stream from the server. bool pushed() const { return pushed_; } SpdyStreamId stream_id() const { return stream_id_; } void set_stream_id(SpdyStreamId stream_id) { stream_id_ = stream_id; } bool response_received() const { return response_received_; } void set_response_received() { response_received_ = true; } // For pushed streams, we track a path to identify them. const std::string& path() const { return path_; } RequestPriority priority() const { return priority_; } int32 send_window_size() const { return send_window_size_; } int32 recv_window_size() const { return recv_window_size_; } bool send_stalled_by_flow_control() { return send_stalled_by_flow_control_; } void set_send_stalled_by_flow_control(bool stalled) { send_stalled_by_flow_control_ = stalled; } // Called by the session to adjust this stream's send window size by // |delta_window_size|, which is the difference between the // SETTINGS_INITIAL_WINDOW_SIZE in the most recent SETTINGS frame // and the previous initial send window size, possibly unstalling // this stream. Although |delta_window_size| may cause this stream's // send window size to go negative, it must not cause it to wrap // around in either direction. Does nothing if the stream is already // closed. // // If stream flow control is turned off, this must not be called. void AdjustSendWindowSize(int32 delta_window_size); // Called when bytes are consumed from a SpdyBuffer for a DATA frame // that is to be written or is being written. Increases the send // window size accordingly if some or all of the SpdyBuffer is being // discarded. // // If stream flow control is turned off, this must not be called. void OnWriteBufferConsumed(size_t frame_payload_size, size_t consume_size, SpdyBuffer::ConsumeSource consume_source); // Called by the session to increase this stream's send window size // by |delta_window_size| (which must be at least 1) from a received // WINDOW_UPDATE frame or from a dropped DATA frame that was // intended to be sent, possibly unstalling this stream. If // |delta_window_size| would cause this stream's send window size to // overflow, calls into the session to reset this stream. Does // nothing if the stream is already closed. // // If stream flow control is turned off, this must not be called. void IncreaseSendWindowSize(int32 delta_window_size); // If stream flow control is turned on, called by the session to // decrease this stream's send window size by |delta_window_size|, // which must be at least 0 and at most kMaxSpdyFrameChunkSize. // |delta_window_size| must not cause this stream's send window size // to go negative. Does nothing if the stream is already closed. // // If stream flow control is turned off, this must not be called. void DecreaseSendWindowSize(int32 delta_window_size); // Called when bytes are consumed by the delegate from a SpdyBuffer // containing received data. Increases the receive window size // accordingly. // // If stream flow control is turned off, this must not be called. void OnReadBufferConsumed(size_t consume_size, SpdyBuffer::ConsumeSource consume_source); // Called by OnReadBufferConsume to increase this stream's receive // window size by |delta_window_size|, which must be at least 1 and // must not cause this stream's receive window size to overflow, // possibly also sending a WINDOW_UPDATE frame. Does nothing if the // stream is not active. // // If stream flow control is turned off, this must not be called. void IncreaseRecvWindowSize(int32 delta_window_size); // Called by OnDataReceived (which is in turn called by the session) // to decrease this stream's receive window size by // |delta_window_size|, which must be at least 1 and must not cause // this stream's receive window size to go negative. // // If stream flow control is turned off or the stream is not active, // this must not be called. void DecreaseRecvWindowSize(int32 delta_window_size); int GetPeerAddress(IPEndPoint* address) const; int GetLocalAddress(IPEndPoint* address) const; // Returns true if the underlying transport socket ever had any reads or // writes. bool WasEverUsed() const; const BoundNetLog& net_log() const { return net_log_; } const SpdyHeaderBlock& spdy_headers() const; void set_spdy_headers(scoped_ptr headers); base::Time GetRequestTime() const; void SetRequestTime(base::Time t); // Called by the SpdySession when a response (e.g. a SYN_STREAM or SYN_REPLY) // has been received for this stream. Returns a status code. int OnResponseReceived(const SpdyHeaderBlock& response); // Called by the SpdySession when late-bound headers are received for a // stream. Returns a status code. int OnHeaders(const SpdyHeaderBlock& headers); // Called by the SpdySession when response data has been received for this // stream. This callback may be called multiple times as data arrives // from the network, and will never be called prior to OnResponseReceived. // // |buffer| contains the data received, or NULL if the stream is // being closed. The stream must copy any data from this // buffer before returning from this callback. // // |length| is the number of bytes received (at most 2^24 - 1) or 0 if // the stream is being closed. void OnDataReceived(scoped_ptr buffer); // Called by the SpdySession when a frame has been successfully and // completely written. |frame_size| is the total size of the frame // in bytes, including framing overhead. void OnFrameWriteComplete(SpdyFrameType frame_type, size_t frame_size); // Called by the SpdySession when the request is finished. This callback // will always be called at the end of the request and signals to the // stream that the stream has no more network events. No further callbacks // to the stream will be made after this call. // |status| is an error code or OK. void OnClose(int status); // Called by the SpdySession to log stream related errors. void LogStreamError(int status, const std::string& description); // If this stream is active, reset it, and close it otherwise. In // either case the stream is deleted. void Cancel(); // Close this stream without sending a RST_STREAM and delete // it. void Close(); // Returns whether or not this stream is closed. Note that the only // time a stream is closed and not deleted is in its delegate's // OnClose() method. bool closed() const { return io_state_ == STATE_DONE; } // TODO(satorux): This is only for testing. We should be able to remove // this once crbug.com/113107 is addressed. bool body_sent() const { return io_state_ > STATE_SEND_BODY_COMPLETE; } // Interface for Spdy[Http|WebSocket]Stream to use. // Sends the request. // For non push stream, it will send SYN_STREAM frame. int SendRequest(bool has_upload_data); // Queues a HEADERS frame to be sent. void QueueHeaders(scoped_ptr headers); // Queues a DATA frame to be sent. May not queue all the data that // is given (or even any of it) depending on flow control. void QueueStreamData(IOBuffer* data, int length, SpdyDataFlags flags); // Fills SSL info in |ssl_info| and returns true when SSL is in use. bool GetSSLInfo(SSLInfo* ssl_info, bool* was_npn_negotiated, NextProto* protocol_negotiated); // Fills SSL Certificate Request info |cert_request_info| and returns // true when SSL is in use. bool GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info); // If the stream is stalled on sending data, but the session is not // stalled on sending data and |send_window_size_| is positive, then // set |send_stalled_by_flow_control_| to false and unstall the data // sending. Called by the session or by the stream itself. Must be // called only when the stream is still open. void PossiblyResumeIfSendStalled(); // Must be used only by the SpdySession. base::WeakPtr GetWeakPtr(); bool is_idle() const { return io_state_ == STATE_OPEN || io_state_ == STATE_DONE; } int response_status() const { return response_status_; } // Returns true if the URL for this stream is known. bool HasUrl() const; // Get the URL associated with this stream. Only valid when has_url() is // true. GURL GetUrl() const; int GetProtocolVersion() const; private: class SynStreamBufferProducer; class HeaderBufferProducer; enum State { STATE_NONE, STATE_GET_DOMAIN_BOUND_CERT, STATE_GET_DOMAIN_BOUND_CERT_COMPLETE, STATE_SEND_DOMAIN_BOUND_CERT, STATE_SEND_DOMAIN_BOUND_CERT_COMPLETE, STATE_SEND_HEADERS, STATE_SEND_HEADERS_COMPLETE, STATE_SEND_BODY, STATE_SEND_BODY_COMPLETE, STATE_WAITING_FOR_RESPONSE, STATE_OPEN, STATE_DONE }; friend class base::RefCounted; virtual ~SpdyStream(); void OnGetDomainBoundCertComplete(int result); // Try to make progress sending/receiving the request/response. int DoLoop(int result); // The implementations of each state of the state machine. int DoGetDomainBoundCert(); int DoGetDomainBoundCertComplete(int result); int DoSendDomainBoundCert(); int DoSendDomainBoundCertComplete(); int DoSendHeaders(); int DoSendHeadersComplete(); int DoSendBody(); int DoSendBodyComplete(); int DoReadHeaders(); int DoReadHeadersComplete(int result); int DoOpen(); // Update the histograms. Can safely be called repeatedly, but should only // be called after the stream has completed. void UpdateHistograms(); // When a server pushed stream is first created, this function is posted on // the MessageLoop to replay all the data that the server has already sent. void PushedStreamReplayData(); // Produces the SYN_STREAM frame for the stream. The stream must // already be activated. scoped_ptr ProduceSynStreamFrame(); // Produce the initial HEADER frame for the stream with the given // block. The stream must already be activated. scoped_ptr ProduceHeaderFrame( scoped_ptr header_block); base::WeakPtrFactory weak_ptr_factory_; // There is a small period of time between when a server pushed stream is // first created, and the pushed data is replayed. Any data received during // this time should continue to be buffered. bool continue_buffering_data_; SpdyStreamId stream_id_; const std::string path_; const RequestPriority priority_; size_t slot_; // Flow control variables. bool send_stalled_by_flow_control_; int32 send_window_size_; int32 recv_window_size_; int32 unacked_recv_window_bytes_; const bool pushed_; ScopedBandwidthMetrics metrics_; bool response_received_; scoped_refptr session_; // The transaction should own the delegate. SpdyStream::Delegate* delegate_; // The request to send. scoped_ptr request_; // The time at which the request was made that resulted in this response. // For cached responses, this time could be "far" in the past. base::Time request_time_; scoped_ptr response_; base::Time response_time_; State io_state_; // Since we buffer the response, we also buffer the response status. // Not valid until the stream is closed. int response_status_; bool has_upload_data_; BoundNetLog net_log_; base::TimeTicks send_time_; base::TimeTicks recv_first_byte_time_; base::TimeTicks recv_last_byte_time_; // Number of data bytes that have been sent/received on this stream, not // including frame overhead. Note that this does not count headers. int send_bytes_; int recv_bytes_; // Data received before delegate is attached. ScopedVector pending_buffers_; SSLClientCertType domain_bound_cert_type_; std::string domain_bound_private_key_; std::string domain_bound_cert_; ServerBoundCertService::RequestHandle domain_bound_cert_request_handle_; // When OnFrameWriteComplete() is called, these variables are set. SpdyFrameType just_completed_frame_type_; size_t just_completed_frame_size_; DISALLOW_COPY_AND_ASSIGN(SpdyStream); }; } // namespace net #endif // NET_SPDY_SPDY_STREAM_H_