blob: 5621564ee7954351a4b9a7349b80bc41c7b22e35 [file] [log] [blame]
// 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 <list>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_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/base/server_bound_cert_service.h"
#include "net/base/ssl_client_cert_type.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"
namespace net {
class AddressList;
class IPEndPoint;
class SSLCertRequestInfo;
class SSLInfo;
// 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<SpdyStream> {
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 bool OnSendHeadersComplete(int status) = 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 data has been sent. |status| indicates network error
// or number of bytes that has been sent. On return, |eof| is set to true
// if no more data is available to send in the request body.
// Returns network error code. OK when it successfully sent data.
virtual int OnSendBodyComplete(int status, bool* eof) = 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.
// Returns network error code. OK when it successfully receives data.
virtual int OnDataReceived(const char* data, int length) = 0;
// Called when data is sent.
virtual void OnDataSent(int length) = 0;
// Called when SpdyStream is closed.
virtual void OnClose(int status) = 0;
protected:
virtual ~Delegate() {}
private:
DISALLOW_COPY_AND_ASSIGN(Delegate);
};
// Indicates pending frame type.
enum FrameType {
TYPE_HEADERS,
TYPE_DATA
};
// Structure to contains pending frame information.
typedef struct {
FrameType type;
union {
SpdyHeaderBlock* header_block;
SpdyDataFrame* data_frame;
};
} PendingFrame;
// SpdyStream constructor
SpdyStream(SpdySession* session,
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 delegate from the stream. It will cancel the stream if it was not
// cancelled yet. It is safe to call multiple times.
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_; }
void set_path(const std::string& path) { path_ = path; }
RequestPriority priority() const { return priority_; }
void set_priority(RequestPriority priority) { priority_ = priority; }
int32 send_window_size() const { return send_window_size_; }
void set_send_window_size(int32 window_size) {
send_window_size_ = window_size;
}
int32 recv_window_size() const { return recv_window_size_; }
void set_recv_window_size(int32 window_size) {
recv_window_size_ = window_size;
}
// Set session_'s initial_recv_window_size. Used by unittests.
void set_initial_recv_window_size(int32 window_size);
bool stalled_by_flow_control() { return stalled_by_flow_control_; }
void set_stalled_by_flow_control(bool stalled) {
stalled_by_flow_control_ = stalled;
}
// Adjusts the |send_window_size_| by |delta_window_size|. |delta_window_size|
// is the difference between the SETTINGS_INITIAL_WINDOW_SIZE in SETTINGS
// frame and the previous initial_send_window_size.
void AdjustSendWindowSize(int32 delta_window_size);
// Increases |send_window_size_| with delta extracted from a WINDOW_UPDATE
// frame; sends a RST_STREAM if delta overflows |send_window_size_| and
// removes the stream from the session.
void IncreaseSendWindowSize(int32 delta_window_size);
// Decreases |send_window_size_| by the given number of bytes.
void DecreaseSendWindowSize(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;
// Increases |recv_window_size_| by the given number of bytes, also sends
// a WINDOW_UPDATE frame.
void IncreaseRecvWindowSize(int32 delta_window_size);
// Decreases |recv_window_size_| by the given number of bytes, called
// whenever data is read. May also send a RST_STREAM and remove the
// stream from the session if the resultant |recv_window_size_| is
// negative, since that would be a flow control violation.
void DecreaseRecvWindowSize(int32 delta_window_size);
const BoundNetLog& net_log() const { return net_log_; }
const SpdyHeaderBlock& spdy_headers() const;
void set_spdy_headers(scoped_ptr<SpdyHeaderBlock> 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. The stream must copy any data
// from this buffer before returning from this callback.
// |length| is the number of bytes received or an error.
// A zero-length count does not indicate end-of-stream.
void OnDataReceived(const char* buffer, int bytes);
// Called by the SpdySession when a write has completed. This callback
// will be called multiple times for each write which completes. Writes
// include the SYN_STREAM write and also DATA frame writes.
// |result| is the number of bytes written or a net error code.
void OnWriteComplete(int bytes);
// 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);
void Cancel();
void Close();
bool cancelled() const { return cancelled_; }
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);
// Sends a HEADERS frame. SpdyStream owns |headers| and will release it after
// the HEADERS frame is actually sent.
int WriteHeaders(SpdyHeaderBlock* headers);
// Sends DATA frame.
int WriteStreamData(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);
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 SpdyStreamIOBufferProducer;
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<SpdyStream>;
virtual ~SpdyStream();
// If the stream is stalled and if |send_window_size_| is positive, then set
// |stalled_by_flow_control_| to false and unstall the stream.
void PossiblyResumeIfStalled();
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 result);
int DoSendHeaders();
int DoSendHeadersComplete(int result);
int DoSendBody();
int DoSendBodyComplete(int result);
int DoReadHeaders();
int DoReadHeadersComplete(int result);
int DoOpen(int result);
// 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();
// Informs the SpdySession that this stream has a write available.
void SetHasWriteAvailable();
// Returns a newly created SPDY frame owned by the called that contains
// the next frame to be sent by this frame. May return NULL if this
// stream has become stalled on flow control.
SpdyFrame* ProduceNextFrame();
// 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_;
std::string path_;
RequestPriority priority_;
size_t slot_;
// Flow control variables.
bool 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<SpdySession> session_;
// The transaction should own the delegate.
SpdyStream::Delegate* delegate_;
// The request to send.
scoped_ptr<SpdyHeaderBlock> 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<SpdyHeaderBlock> response_;
base::Time response_time_;
// An in order list of pending frame data that are going to be sent. HEADERS
// frames are queued as SpdyHeaderBlock structures because these must be
// compressed just before sending. Data frames are queued as SpdyDataFrame.
std::list<PendingFrame> pending_frames_;
// An in order list of sending frame types. It will be used to know which type
// of frame is sent and which callback should be invoked in OnOpen().
std::list<FrameType> waiting_completions_;
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 cancelled_;
bool has_upload_data_;
BoundNetLog net_log_;
base::TimeTicks send_time_;
base::TimeTicks recv_first_byte_time_;
base::TimeTicks recv_last_byte_time_;
int send_bytes_;
int recv_bytes_;
// Data received before delegate is attached.
std::vector<scoped_refptr<IOBufferWithSize> > 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_;
DISALLOW_COPY_AND_ASSIGN(SpdyStream);
};
} // namespace net
#endif // NET_SPDY_SPDY_STREAM_H_