// Copyright (c) 2011 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_
#pragma once
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/memory/linked_ptr.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/upload_data.h"
#include "net/base/net_log.h"
#include "net/spdy/spdy_framer.h"
#include "net/spdy/spdy_protocol.h"
namespace net {
class AddressList;
class IPEndPoint;
class SpdySession;
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 SpdyStream
: public base::RefCounted<SpdyStream>,
public ChunkCallback {
public:
// Delegate handles protocol specific behavior of spdy stream.
class 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.
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 spdy::SpdyHeaderBlock& response,
base::Time response_time,
int status) = 0;
// Called when data is received.
virtual void 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;
// Sets the callback to be invoked when a new chunk is available to upload.
virtual void set_chunk_callback(ChunkCallback* callback) = 0;
protected:
friend class base::RefCounted<Delegate>;
virtual ~Delegate() {}
private:
DISALLOW_COPY_AND_ASSIGN(Delegate);
};
// SpdyStream constructor
SpdyStream(SpdySession* session,
spdy::SpdyStreamId stream_id,
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_; }
spdy::SpdyStreamId stream_id() const { return stream_id_; }
void set_stream_id(spdy::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; }
int priority() const { return priority_; }
void set_priority(int priority) { priority_ = priority; }
int send_window_size() const { return send_window_size_; }
void set_send_window_size(int window_size) {
send_window_size_ = window_size;
}
int recv_window_size() const { return recv_window_size_; }
void set_recv_window_size(int window_size) {
recv_window_size_ = window_size;
}
void set_stalled_by_flow_control(bool stalled) {
stalled_by_flow_control_ = stalled;
}
// 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(int delta_window_size);
// Decreases |send_window_size_| by the given number of bytes.
void DecreaseSendWindowSize(int delta_window_size);
int GetPeerAddress(AddressList* 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(int 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(int delta_window_size);
const BoundNetLog& net_log() const { return net_log_; }
const linked_ptr<spdy::SpdyHeaderBlock>& spdy_headers() const;
void set_spdy_headers(const linked_ptr<spdy::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 spdy::SpdyHeaderBlock& response);
// Called by the SpdySession when late-bound headers are received for a
// stream. Returns a status code.
int OnHeaders(const spdy::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);
void Cancel();
bool cancelled() const { return cancelled_; }
bool closed() const { return io_state_ == STATE_DONE; }
// 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 DATA frame.
int WriteStreamData(IOBuffer* data, int length,
spdy::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);
// 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;
// ChunkCallback methods.
virtual void OnChunkAvailable();
private:
enum State {
STATE_NONE,
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();
// Try to make progress sending/receiving the request/response.
int DoLoop(int result);
// The implementations of each state of the state machine.
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();
// 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_;
spdy::SpdyStreamId stream_id_;
std::string path_;
int priority_;
// Flow control variables.
bool stalled_by_flow_control_;
int send_window_size_;
int recv_window_size_;
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.
linked_ptr<spdy::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_;
linked_ptr<spdy::SpdyHeaderBlock> 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 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_;
DISALLOW_COPY_AND_ASSIGN(SpdyStream);
};
} // namespace net
#endif // NET_SPDY_SPDY_STREAM_H_