summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorwillchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-18 19:05:18 +0000
committerwillchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-18 19:05:18 +0000
commit78bf2aa62eb081644cf3b76bb36e95de1a730658 (patch)
tree26b891501661a39aee029106d6ace10620b81aa1 /net
parent00f71870f67dcb264c0eabe3060fd06a3c9259af (diff)
downloadchromium_src-78bf2aa62eb081644cf3b76bb36e95de1a730658.zip
chromium_src-78bf2aa62eb081644cf3b76bb36e95de1a730658.tar.gz
chromium_src-78bf2aa62eb081644cf3b76bb36e95de1a730658.tar.bz2
Revert 50215 because of crashes - Refactor SpdyStream to get HTTP specific out of the interface and members.
Add SpdyStream::Delegate interface and SpdyHttpStream implements SpdyStream::Delegate. SpdyHeaderBlock<>HTTP request/response conversion functions moved from spdy_session.cc to spdy_http_stream.cc. All interface between SpdySession and SpdyStream uses SpdyHeaderBlock instead of HttpRequestInfo,HttpResponseInfo. BUG=42320,46925 TEST=none Review URL: http://codereview.chromium.org/2667002 TBR=ukai@chromium.org Review URL: http://codereview.chromium.org/2827015 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@50268 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/http/http_network_transaction.cc38
-rw-r--r--net/http/http_network_transaction.h2
-rw-r--r--net/spdy/spdy_http_stream.cc273
-rw-r--r--net/spdy/spdy_http_stream.h58
-rw-r--r--net/spdy/spdy_http_stream_unittest.cc11
-rw-r--r--net/spdy/spdy_network_transaction.cc22
-rw-r--r--net/spdy/spdy_session.cc253
-rw-r--r--net/spdy/spdy_session.h49
-rw-r--r--net/spdy/spdy_session_unittest.cc14
-rw-r--r--net/spdy/spdy_stream.cc161
-rw-r--r--net/spdy/spdy_stream.h137
11 files changed, 427 insertions, 591 deletions
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index d670024..ccc956b 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -507,10 +507,10 @@ int HttpNetworkTransaction::Read(IOBuffer* buf, int buf_len,
// Are we using SPDY or HTTP?
if (using_spdy_) {
DCHECK(!http_stream_.get());
- DCHECK(spdy_http_stream_->GetResponseInfo()->headers);
+ DCHECK(spdy_stream_->GetResponseInfo()->headers);
next_state = STATE_SPDY_READ_BODY;
} else {
- DCHECK(!spdy_http_stream_.get());
+ DCHECK(!spdy_stream_.get());
next_state = STATE_READ_BODY;
if (!connection_->is_initialized())
@@ -593,8 +593,8 @@ HttpNetworkTransaction::~HttpNetworkTransaction() {
if (pac_request_)
session_->proxy_service()->CancelPacRequest(pac_request_);
- if (spdy_http_stream_.get())
- spdy_http_stream_->Cancel();
+ if (spdy_stream_.get())
+ spdy_stream_->Cancel();
}
void HttpNetworkTransaction::DoCallback(int rv) {
@@ -1498,7 +1498,7 @@ int HttpNetworkTransaction::DoDrainBodyForAuthRestartComplete(int result) {
int HttpNetworkTransaction::DoSpdySendRequest() {
next_state_ = STATE_SPDY_SEND_REQUEST_COMPLETE;
- CHECK(!spdy_http_stream_.get());
+ CHECK(!spdy_stream_.get());
// First we get a SPDY session. Theoretically, we've just negotiated one, but
// if one already exists, then screw it, use the existing one! Otherwise,
@@ -1529,25 +1529,9 @@ int HttpNetworkTransaction::DoSpdySendRequest() {
return error_code;
}
headers_valid_ = false;
- scoped_refptr<SpdyStream> spdy_stream;
- if (request_->method == "GET")
- spdy_stream = spdy_session->GetPushStream(request_->url, net_log_);
- if (spdy_stream.get()) {
- DCHECK(spdy_stream->pushed());
- CHECK(spdy_stream->GetDelegate() == NULL);
- spdy_http_stream_ = new SpdyHttpStream(spdy_stream);
- spdy_http_stream_->InitializeRequest(*request_, base::Time::Now(), NULL);
- } else {
- spdy_stream = spdy_session->CreateStream(request_->url,
- request_->priority,
- net_log_);
- DCHECK(!spdy_stream->pushed());
- CHECK(spdy_stream->GetDelegate() == NULL);
- spdy_http_stream_ = new SpdyHttpStream(spdy_stream);
- spdy_http_stream_->InitializeRequest(
- *request_, base::Time::Now(), upload_data);
- }
- return spdy_http_stream_->SendRequest(&response_, &io_callback_);
+ spdy_stream_ = spdy_session->GetOrCreateStream(
+ *request_, upload_data, net_log_);
+ return spdy_stream_->SendRequest(upload_data, &response_, &io_callback_);
}
int HttpNetworkTransaction::DoSpdySendRequestComplete(int result) {
@@ -1560,7 +1544,7 @@ int HttpNetworkTransaction::DoSpdySendRequestComplete(int result) {
int HttpNetworkTransaction::DoSpdyReadHeaders() {
next_state_ = STATE_SPDY_READ_HEADERS_COMPLETE;
- return spdy_http_stream_->ReadResponseHeaders(&io_callback_);
+ return spdy_stream_->ReadResponseHeaders(&io_callback_);
}
int HttpNetworkTransaction::DoSpdyReadHeadersComplete(int result) {
@@ -1576,7 +1560,7 @@ int HttpNetworkTransaction::DoSpdyReadHeadersComplete(int result) {
int HttpNetworkTransaction::DoSpdyReadBody() {
next_state_ = STATE_SPDY_READ_BODY_COMPLETE;
- return spdy_http_stream_->ReadResponseBody(
+ return spdy_stream_->ReadResponseBody(
read_buf_, read_buf_len_, &io_callback_);
}
@@ -1585,7 +1569,7 @@ int HttpNetworkTransaction::DoSpdyReadBodyComplete(int result) {
read_buf_len_ = 0;
if (result <= 0)
- spdy_http_stream_ = NULL;
+ spdy_stream_ = NULL;
return result;
}
diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h
index e481052..147c5e2 100644
--- a/net/http/http_network_transaction.h
+++ b/net/http/http_network_transaction.h
@@ -338,7 +338,7 @@ class HttpNetworkTransaction : public HttpTransaction {
scoped_ptr<ClientSocketHandle> connection_;
scoped_ptr<HttpStream> http_stream_;
- scoped_refptr<SpdyHttpStream> spdy_http_stream_;
+ scoped_refptr<SpdyHttpStream> spdy_stream_;
bool reused_socket_;
// True if we've validated the headers that the stream parser has returned.
diff --git a/net/spdy/spdy_http_stream.cc b/net/spdy/spdy_http_stream.cc
index 1a9e28f..a8979d5 100644
--- a/net/spdy/spdy_http_stream.cc
+++ b/net/spdy/spdy_http_stream.cc
@@ -8,186 +8,39 @@
#include "base/logging.h"
#include "base/message_loop.h"
-#include "base/string_util.h"
-#include "net/base/load_flags.h"
#include "net/http/http_request_info.h"
#include "net/http/http_response_info.h"
#include "net/spdy/spdy_session.h"
-namespace {
-
-// Convert a SpdyHeaderBlock into an HttpResponseInfo.
-// |headers| input parameter with the SpdyHeaderBlock.
-// |info| output parameter for the HttpResponseInfo.
-// Returns true if successfully converted. False if there was a failure
-// or if the SpdyHeaderBlock was invalid.
-bool SpdyHeadersToHttpResponse(const spdy::SpdyHeaderBlock& headers,
- net::HttpResponseInfo* response) {
- std::string version;
- std::string status;
-
- // The "status" and "version" headers are required.
- spdy::SpdyHeaderBlock::const_iterator it;
- it = headers.find("status");
- if (it == headers.end()) {
- LOG(ERROR) << "SpdyHeaderBlock without status header.";
- return false;
- }
- status = it->second;
-
- // Grab the version. If not provided by the server,
- it = headers.find("version");
- if (it == headers.end()) {
- LOG(ERROR) << "SpdyHeaderBlock without version header.";
- return false;
- }
- version = it->second;
-
- response->response_time = base::Time::Now();
-
- std::string raw_headers(version);
- raw_headers.push_back(' ');
- raw_headers.append(status);
- raw_headers.push_back('\0');
- for (it = headers.begin(); it != headers.end(); ++it) {
- // For each value, if the server sends a NUL-separated
- // list of values, we separate that back out into
- // individual headers for each value in the list.
- // e.g.
- // Set-Cookie "foo\0bar"
- // becomes
- // Set-Cookie: foo\0
- // Set-Cookie: bar\0
- std::string value = it->second;
- size_t start = 0;
- size_t end = 0;
- do {
- end = value.find('\0', start);
- std::string tval;
- if (end != value.npos)
- tval = value.substr(start, (end - start));
- else
- tval = value.substr(start);
- raw_headers.append(it->first);
- raw_headers.push_back(':');
- raw_headers.append(tval);
- raw_headers.push_back('\0');
- start = end + 1;
- } while (end != value.npos);
- }
-
- response->headers = new net::HttpResponseHeaders(raw_headers);
- response->was_fetched_via_spdy = true;
- return true;
-}
-
-// Create a SpdyHeaderBlock for a Spdy SYN_STREAM Frame from
-// a HttpRequestInfo block.
-void CreateSpdyHeadersFromHttpRequest(
- const net::HttpRequestInfo& info, spdy::SpdyHeaderBlock* headers) {
- // TODO(willchan): It's not really necessary to convert from
- // HttpRequestHeaders to spdy::SpdyHeaderBlock.
-
- static const char kHttpProtocolVersion[] = "HTTP/1.1";
-
- net::HttpRequestHeaders::Iterator it(info.extra_headers);
-
- while (it.GetNext()) {
- std::string name = StringToLowerASCII(it.name());
- if (headers->find(name) == headers->end()) {
- (*headers)[name] = it.value();
- } else {
- std::string new_value = (*headers)[name];
- new_value.append(1, '\0'); // +=() doesn't append 0's
- new_value += it.value();
- (*headers)[name] = new_value;
- }
- }
-
- // TODO(mbelshe): Add Proxy headers here. (See http_network_transaction.cc)
- // TODO(mbelshe): Add authentication headers here.
-
- (*headers)["method"] = info.method;
- (*headers)["url"] = info.url.spec();
- (*headers)["version"] = kHttpProtocolVersion;
- if (!info.referrer.is_empty())
- (*headers)["referer"] = info.referrer.spec();
-
- // Honor load flags that impact proxy caches.
- if (info.load_flags & net::LOAD_BYPASS_CACHE) {
- (*headers)["pragma"] = "no-cache";
- (*headers)["cache-control"] = "no-cache";
- } else if (info.load_flags & net::LOAD_VALIDATE_CACHE) {
- (*headers)["cache-control"] = "max-age=0";
- }
-}
-
-} // anonymous namespace
-
namespace net {
-SpdyHttpStream::SpdyHttpStream(const scoped_refptr<SpdyStream>& stream)
- : stream_(stream),
- response_info_(NULL),
+SpdyHttpStream::SpdyHttpStream(
+ SpdySession* session, spdy::SpdyStreamId stream_id, bool pushed)
+ : SpdyStream(session, stream_id, pushed),
download_finished_(false),
user_callback_(NULL),
user_buffer_len_(0),
buffered_read_callback_pending_(false),
- more_read_data_pending_(false) {
- CHECK(stream_.get());
- stream_->SetDelegate(this);
-}
+ more_read_data_pending_(false) {}
SpdyHttpStream::~SpdyHttpStream() {
- stream_->DetachDelegate();
-}
-
-void SpdyHttpStream::InitializeRequest(
- const HttpRequestInfo& request_info,
- base::Time request_time,
- UploadDataStream* upload_data) {
- request_info_ = request_info;
- linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock);
- CreateSpdyHeadersFromHttpRequest(request_info_, headers.get());
- stream_->set_spdy_headers(headers);
-
- stream_->SetRequestTime(request_time);
- // This should only get called in the case of a request occuring
- // during server push that has already begun but hasn't finished,
- // so we set the response's request time to be the actual one
- if (response_info_)
- response_info_->request_time = request_time;
-
- CHECK(!request_body_stream_.get());
- if (upload_data) {
- if (upload_data->size())
- request_body_stream_.reset(upload_data);
- else
- delete upload_data;
- }
-}
-
-const HttpResponseInfo* SpdyHttpStream::GetResponseInfo() const {
- return response_info_;
+ DLOG(INFO) << "Deleting SpdyHttpStream for stream " << stream_id();
}
uint64 SpdyHttpStream::GetUploadProgress() const {
- if (!request_body_stream_.get())
+ if (!request_body_stream())
return 0;
- return request_body_stream_->position();
+ return request_body_stream()->position();
}
int SpdyHttpStream::ReadResponseHeaders(CompletionCallback* callback) {
- DCHECK(stream_->is_idle());
+ DCHECK(is_idle());
// Note: The SpdyStream may have already received the response headers, so
// this call may complete synchronously.
CHECK(callback);
- if (stream_->response_complete())
- return stream_->response_status();
-
- int result = stream_->DoReadResponseHeaders();
+ int result = DoReadResponseHeaders();
if (result == ERR_IO_PENDING) {
CHECK(!user_callback_);
user_callback_ = callback;
@@ -197,10 +50,11 @@ int SpdyHttpStream::ReadResponseHeaders(CompletionCallback* callback) {
int SpdyHttpStream::ReadResponseBody(
IOBuffer* buf, int buf_len, CompletionCallback* callback) {
+ DCHECK(is_idle());
CHECK(buf);
CHECK(buf_len);
CHECK(callback);
- DCHECK(stream_->is_idle());
+ CHECK(!cancelled());
// If we have data buffered, complete the IO immediately.
if (!response_body_.empty()) {
@@ -223,8 +77,8 @@ int SpdyHttpStream::ReadResponseBody(
bytes_read += bytes_to_copy;
}
return bytes_read;
- } else if (stream_->response_complete()) {
- return stream_->response_status();
+ } else if (response_complete()) {
+ return response_status();
}
CHECK(!user_callback_);
@@ -237,38 +91,14 @@ int SpdyHttpStream::ReadResponseBody(
return ERR_IO_PENDING;
}
-int SpdyHttpStream::SendRequest(HttpResponseInfo* response,
+int SpdyHttpStream::SendRequest(UploadDataStream* upload_data,
+ HttpResponseInfo* response,
CompletionCallback* callback) {
CHECK(callback);
- CHECK(!stream_->cancelled());
+ CHECK(!cancelled());
CHECK(response);
- if (stream_->response_complete()) {
- if (stream_->response_status() == OK)
- return ERR_FAILED;
- else
- return stream_->response_status();
- }
-
- // SendRequest can be called in two cases.
- //
- // a) A client initiated request. In this case, |response_info_| should be
- // NULL to start with.
- // b) A client request which matches a response that the server has already
- // pushed. In this case, the value of |*push_response_info_| is copied
- // over to the new response object |*response|. |push_response_info_| is
- // deleted, and |response_info_| is reset |response|.
- if (push_response_info_.get()) {
- *response = *push_response_info_;
- push_response_info_.reset();
- response_info_ = NULL;
- }
-
- DCHECK_EQ(static_cast<HttpResponseInfo*>(NULL), response_info_);
- response_info_ = response;
-
- bool has_upload_data = request_body_stream_.get() != NULL;
- int result = stream_->DoSendRequest(has_upload_data);
+ int result = DoSendRequest(upload_data, response);
if (result == ERR_IO_PENDING) {
CHECK(!user_callback_);
user_callback_ = callback;
@@ -278,58 +108,22 @@ int SpdyHttpStream::SendRequest(HttpResponseInfo* response,
void SpdyHttpStream::Cancel() {
user_callback_ = NULL;
- stream_->Cancel();
-}
-
-bool SpdyHttpStream::OnSendHeadersComplete(int status) {
- return request_body_stream_.get() == NULL;
-}
-
-int SpdyHttpStream::OnSendBody() {
- CHECK(request_body_stream_.get());
- int buf_len = static_cast<int>(request_body_stream_->buf_len());
- if (!buf_len)
- return OK;
- return stream_->WriteStreamData(request_body_stream_->buf(), buf_len);
-}
-
-bool SpdyHttpStream::OnSendBodyComplete(int status) {
- CHECK(request_body_stream_.get());
- request_body_stream_->DidConsume(status);
- return request_body_stream_->eof();
+ DoCancel();
}
-int SpdyHttpStream::OnResponseReceived(const spdy::SpdyHeaderBlock& response,
- base::Time response_time,
- int status) {
- if (!response_info_) {
- DCHECK(stream_->pushed());
- push_response_info_.reset(new HttpResponseInfo);
- response_info_ = push_response_info_.get();
- }
-
- if (!SpdyHeadersToHttpResponse(response, response_info_)) {
- status = ERR_INVALID_RESPONSE;
- } else {
- stream_->GetSSLInfo(&response_info_->ssl_info,
- &response_info_->was_npn_negotiated);
- response_info_->request_time = stream_->GetRequestTime();
- response_info_->vary_data.Init(request_info_, *response_info_->headers);
- // TODO(ahendrickson): This is recorded after the entire SYN_STREAM control
- // frame has been received and processed. Move to framer?
- response_info_->response_time = response_time;
- }
-
+int SpdyHttpStream::OnResponseReceived(const HttpResponseInfo& response) {
+ int rv = DoOnResponseReceived(response);
if (user_callback_)
- DoCallback(status);
- return status;
+ DoCallback(rv);
+ return rv;
}
-void SpdyHttpStream::OnDataReceived(const char* data, int length) {
+bool SpdyHttpStream::OnDataReceived(const char* data, int length) {
+ bool result = DoOnDataReceived(data, length);
// Note that data may be received for a SpdyStream prior to the user calling
// ReadResponseBody(), therefore user_buffer_ may be NULL. This may often
// happen for server initiated streams.
- if (length > 0 && !stream_->response_complete()) {
+ if (result && !response_complete()) {
// Save the received data.
IOBufferWithSize* io_buffer = new IOBufferWithSize(length);
memcpy(io_buffer->data(), data, length);
@@ -341,13 +135,22 @@ void SpdyHttpStream::OnDataReceived(const char* data, int length) {
ScheduleBufferedReadCallback();
}
}
+ return result;
+}
+
+void SpdyHttpStream::OnWriteComplete(int status) {
+ DoOnWriteComplete(status);
}
void SpdyHttpStream::OnClose(int status) {
- if (status == net::OK)
+ if (status == net::OK) {
+ download_finished_ = true;
+ set_response_complete(true);
+
// We need to complete any pending buffered read now.
DoBufferedReadCallback();
- stream_->DetachDelegate();
+ }
+ DoOnClose(status);
if (user_callback_)
DoCallback(status);
}
@@ -371,7 +174,7 @@ void SpdyHttpStream::ScheduleBufferedReadCallback() {
// the caller. Returns true if we should wait, false otherwise.
bool SpdyHttpStream::ShouldWaitForMoreBufferedData() const {
// If the response is complete, there is no point in waiting.
- if (stream_->response_complete())
+ if (response_complete())
return false;
int bytes_buffered = 0;
@@ -389,7 +192,7 @@ void SpdyHttpStream::DoBufferedReadCallback() {
// If the transaction is cancelled or errored out, we don't need to complete
// the read.
- if (stream_->response_status() != OK || stream_->cancelled())
+ if (response_status() != OK || cancelled())
return;
// When more_read_data_pending_ is true, it means that more data has
diff --git a/net/spdy/spdy_http_stream.h b/net/spdy/spdy_http_stream.h
index b83c670..11c00e0 100644
--- a/net/spdy/spdy_http_stream.h
+++ b/net/spdy/spdy_http_stream.h
@@ -11,7 +11,6 @@
#include "base/ref_counted.h"
#include "net/base/completion_callback.h"
#include "net/base/net_log.h"
-#include "net/http/http_request_info.h"
#include "net/spdy/spdy_protocol.h"
#include "net/spdy/spdy_stream.h"
@@ -24,29 +23,21 @@ class UploadData;
class UploadDataStream;
// The SpdyHttpStream is a HTTP-specific type of stream known to a SpdySession.
-class SpdyHttpStream : public SpdyStream::Delegate,
- public base::RefCounted<SpdyHttpStream> {
+class SpdyHttpStream : public SpdyStream {
public:
// SpdyHttpStream constructor
- explicit SpdyHttpStream(const scoped_refptr<SpdyStream>& stream);
-
- SpdyStream* stream() { return stream_.get(); }
-
- // Initialize request. Must be called before calling SendRequest().
- // SpdyHttpStream takes ownership of |upload_data|. |upload_data| may be NULL.
- void InitializeRequest(const HttpRequestInfo& request_info,
- base::Time request_time,
- UploadDataStream* upload_data);
-
- const HttpResponseInfo* GetResponseInfo() const;
+ SpdyHttpStream(
+ SpdySession* session, spdy::SpdyStreamId stream_id, bool pushed);
// ===================================================
// Interface for [Http|Spdy]NetworkTransaction to use.
- // Sends the request.
- // |callback| is used when this completes asynchronously.
- // The actual SYN_STREAM packet will be sent if the stream is non-pushed.
- int SendRequest(HttpResponseInfo* response,
+ // Sends the request. If |upload_data| is non-NULL, sends that in the request
+ // body. |callback| is used when this completes asynchronously. Note that
+ // the actual SYN_STREAM packet will have already been sent by this point.
+ // Also note that SpdyStream takes ownership of |upload_data|.
+ int SendRequest(UploadDataStream* upload_data,
+ HttpResponseInfo* response,
CompletionCallback* callback);
// Reads the response headers. Returns a net error code.
@@ -64,25 +55,24 @@ class SpdyHttpStream : public SpdyStream::Delegate,
uint64 GetUploadProgress() const;
// ===================================================
- // SpdyStream::Delegate.
-
- virtual bool OnSendHeadersComplete(int status);
- virtual int OnSendBody();
- virtual bool OnSendBodyComplete(int status);
+ // Interface for SpdySession to use.
// Called by the SpdySession when a response (e.g. a SYN_REPLY) has been
// received for this stream.
// SpdyHttpSession calls back |callback| set by SendRequest or
// ReadResponseHeaders.
- virtual int OnResponseReceived(const spdy::SpdyHeaderBlock& response,
- base::Time response_time,
- int status);
+ virtual int OnResponseReceived(const HttpResponseInfo& response);
// 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.
// SpdyHttpSession schedule to call back |callback| set by ReadResponseBody.
- virtual void OnDataReceived(const char* buffer, int bytes);
+ virtual bool 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.
+ virtual void OnWriteComplete(int status);
// 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
@@ -103,20 +93,6 @@ class SpdyHttpStream : public SpdyStream::Delegate,
void DoBufferedReadCallback();
bool ShouldWaitForMoreBufferedData() const;
- const scoped_refptr<SpdyStream> stream_;
-
- // The request to send.
- HttpRequestInfo request_info_;
-
- scoped_ptr<UploadDataStream> request_body_stream_;
-
- // |response_info_| is the HTTP response data object which is filled in
- // when a SYN_REPLY comes in for the stream.
- // It is not owned by this stream object, or point to |push_response_info_|.
- HttpResponseInfo* response_info_;
-
- scoped_ptr<HttpResponseInfo> push_response_info_;
-
bool download_finished_;
// We buffer the response body as it arrives asynchronously from the stream.
diff --git a/net/spdy/spdy_http_stream_unittest.cc b/net/spdy/spdy_http_stream_unittest.cc
index f6d5021..5d20aa9a 100644
--- a/net/spdy/spdy_http_stream_unittest.cc
+++ b/net/spdy/spdy_http_stream_unittest.cc
@@ -4,10 +4,8 @@
#include "net/spdy/spdy_http_stream.h"
#include "base/ref_counted.h"
-#include "base/time.h"
#include "net/base/mock_host_resolver.h"
#include "net/base/net_errors.h"
-#include "net/base/net_log.h"
#include "net/base/ssl_config_service.h"
#include "net/base/ssl_config_service_defaults.h"
#include "net/base/test_completion_callback.h"
@@ -117,13 +115,10 @@ TEST_F(SpdyHttpStreamTest, SendRequest) {
TestCompletionCallback callback;
HttpResponseInfo response;
- scoped_refptr<SpdyStream> stream(
- session->CreateStream(request.url, HIGHEST, BoundNetLog()));
-
- scoped_refptr<SpdyHttpStream> http_stream(new SpdyHttpStream(stream.get()));
- http_stream->InitializeRequest(request, base::Time::Now(), NULL);
+ scoped_refptr<SpdyHttpStream> stream(new SpdyHttpStream(session, 1, false));
+ stream->SetRequestInfo(request);
EXPECT_EQ(ERR_IO_PENDING,
- http_stream->SendRequest(&response, &callback));
+ stream->SendRequest(NULL, &response, &callback));
// Need to manually remove the spdy session since normally it gets removed on
// socket close/error, but we aren't communicating over a socket here.
diff --git a/net/spdy/spdy_network_transaction.cc b/net/spdy/spdy_network_transaction.cc
index bc358b6..e79d889 100644
--- a/net/spdy/spdy_network_transaction.cc
+++ b/net/spdy/spdy_network_transaction.cc
@@ -256,26 +256,10 @@ int SpdyNetworkTransaction::DoSendRequest() {
if (!upload_data)
return error_code;
}
- scoped_refptr<SpdyStream> spdy_stream;
- if (request_->method == "GET")
- spdy_stream = spdy_->GetPushStream(request_->url, net_log_);
- if (spdy_stream.get()) {
- DCHECK(spdy_stream->pushed());
- CHECK(spdy_stream->GetDelegate() == NULL);
- stream_ = new SpdyHttpStream(spdy_stream);
- stream_->InitializeRequest(*request_, base::Time::Now(), NULL);
- // "vary" field?
- } else {
- spdy_stream = spdy_->CreateStream(request_->url,
- request_->priority,
- net_log_);
- DCHECK(!spdy_stream->pushed());
- CHECK(spdy_stream->GetDelegate() == NULL);
- stream_ = new SpdyHttpStream(spdy_stream);
- stream_->InitializeRequest(*request_, base::Time::Now(), upload_data);
- }
+ stream_ = spdy_->GetOrCreateStream(*request_, upload_data, net_log_);
+ // Release the reference to |spdy_| since we don't need it anymore.
spdy_ = NULL;
- return stream_->SendRequest(&response_, &io_callback_);
+ return stream_->SendRequest(upload_data, &response_, &io_callback_);
}
int SpdyNetworkTransaction::DoSendRequestComplete(int result) {
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index e670fd3..9e474b4 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -19,10 +19,14 @@
#include "net/base/net_log.h"
#include "net/base/net_util.h"
#include "net/http/http_network_session.h"
+#include "net/http/http_request_info.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_response_info.h"
#include "net/socket/client_socket.h"
#include "net/socket/client_socket_factory.h"
#include "net/socket/ssl_client_socket.h"
#include "net/spdy/spdy_frame_builder.h"
+#include "net/spdy/spdy_http_stream.h"
#include "net/spdy/spdy_protocol.h"
#include "net/spdy/spdy_settings_storage.h"
#include "net/spdy/spdy_stream.h"
@@ -62,6 +66,112 @@ const int kReadBufferSize = 2 * 1024;
const int kReadBufferSize = 8 * 1024;
#endif
+// Convert a SpdyHeaderBlock into an HttpResponseInfo.
+// |headers| input parameter with the SpdyHeaderBlock.
+// |info| output parameter for the HttpResponseInfo.
+// Returns true if successfully converted. False if there was a failure
+// or if the SpdyHeaderBlock was invalid.
+bool SpdyHeadersToHttpResponse(const spdy::SpdyHeaderBlock& headers,
+ HttpResponseInfo* response) {
+ std::string version;
+ std::string status;
+
+ // The "status" and "version" headers are required.
+ spdy::SpdyHeaderBlock::const_iterator it;
+ it = headers.find("status");
+ if (it == headers.end()) {
+ LOG(ERROR) << "SpdyHeaderBlock without status header.";
+ return false;
+ }
+ status = it->second;
+
+ // Grab the version. If not provided by the server,
+ it = headers.find("version");
+ if (it == headers.end()) {
+ LOG(ERROR) << "SpdyHeaderBlock without version header.";
+ return false;
+ }
+ version = it->second;
+
+ response->response_time = base::Time::Now();
+
+ std::string raw_headers(version);
+ raw_headers.push_back(' ');
+ raw_headers.append(status);
+ raw_headers.push_back('\0');
+ for (it = headers.begin(); it != headers.end(); ++it) {
+ // For each value, if the server sends a NUL-separated
+ // list of values, we separate that back out into
+ // individual headers for each value in the list.
+ // e.g.
+ // Set-Cookie "foo\0bar"
+ // becomes
+ // Set-Cookie: foo\0
+ // Set-Cookie: bar\0
+ std::string value = it->second;
+ size_t start = 0;
+ size_t end = 0;
+ do {
+ end = value.find('\0', start);
+ std::string tval;
+ if (end != value.npos)
+ tval = value.substr(start, (end - start));
+ else
+ tval = value.substr(start);
+ raw_headers.append(it->first);
+ raw_headers.push_back(':');
+ raw_headers.append(tval);
+ raw_headers.push_back('\0');
+ start = end + 1;
+ } while (end != value.npos);
+ }
+
+ response->headers = new HttpResponseHeaders(raw_headers);
+ response->was_fetched_via_spdy = true;
+ return true;
+}
+
+// Create a SpdyHeaderBlock for a Spdy SYN_STREAM Frame from
+// a HttpRequestInfo block.
+void CreateSpdyHeadersFromHttpRequest(
+ const HttpRequestInfo& info, spdy::SpdyHeaderBlock* headers) {
+ // TODO(willchan): It's not really necessary to convert from
+ // HttpRequestHeaders to spdy::SpdyHeaderBlock.
+
+ static const char kHttpProtocolVersion[] = "HTTP/1.1";
+
+ HttpRequestHeaders::Iterator it(info.extra_headers);
+
+ while (it.GetNext()) {
+ std::string name = StringToLowerASCII(it.name());
+ if (headers->find(name) == headers->end()) {
+ (*headers)[name] = it.value();
+ } else {
+ std::string new_value = (*headers)[name];
+ new_value.append(1, '\0'); // +=() doesn't append 0's
+ new_value += it.value();
+ (*headers)[name] = new_value;
+ }
+ }
+
+ // TODO(mbelshe): Add Proxy headers here. (See http_network_transaction.cc)
+ // TODO(mbelshe): Add authentication headers here.
+
+ (*headers)["method"] = info.method;
+ (*headers)["url"] = info.url.spec();
+ (*headers)["version"] = kHttpProtocolVersion;
+ if (!info.referrer.is_empty())
+ (*headers)["referer"] = info.referrer.spec();
+
+ // Honor load flags that impact proxy caches.
+ if (info.load_flags & LOAD_BYPASS_CACHE) {
+ (*headers)["pragma"] = "no-cache";
+ (*headers)["cache-control"] = "no-cache";
+ } else if (info.load_flags & LOAD_VALIDATE_CACHE) {
+ (*headers)["cache-control"] = "max-age=0";
+ }
+}
+
void AdjustSocketBufferSizes(ClientSocket* socket) {
// Adjust socket buffer sizes.
// SPDY uses one socket, and we want a really big buffer.
@@ -233,20 +343,42 @@ net::Error SpdySession::Connect(const std::string& group_name,
return static_cast<net::Error>(rv);
}
-scoped_refptr<SpdyStream> SpdySession::GetPushStream(
- const GURL& url,
+scoped_refptr<SpdyHttpStream> SpdySession::GetOrCreateStream(
+ const HttpRequestInfo& request,
+ const UploadDataStream* upload_data,
const BoundNetLog& stream_net_log) {
+ const GURL& url = request.url;
const std::string& path = url.PathForRequest();
- scoped_refptr<SpdyStream> stream = GetActivePushStream(path);
- if (stream) {
- DCHECK(streams_pushed_and_claimed_count_ < streams_pushed_count_);
- streams_pushed_and_claimed_count_++;
- return stream;
+ scoped_refptr<SpdyHttpStream> stream;
+
+ // Check if we have a push stream for this path.
+ if (request.method == "GET") {
+ // Only HTTP will push a stream.
+ scoped_refptr<SpdyHttpStream> stream = GetPushStream(path);
+ if (stream) {
+ DCHECK(streams_pushed_and_claimed_count_ < streams_pushed_count_);
+ // Update the request time
+ stream->SetRequestTime(base::Time::Now());
+ // Change the request info, updating the response's request time too
+ stream->SetRequestInfo(request);
+ const HttpResponseInfo* response = stream->GetResponseInfo();
+ if (response && response->headers->HasHeader("vary")) {
+ // TODO(ahendrickson) -- What is the right thing to do if the server
+ // pushes data with a vary field?
+ void* iter = NULL;
+ std::string value;
+ response->headers->EnumerateHeader(&iter, "vary", &value);
+ LOG(ERROR) << "SpdyStream: "
+ << "Received pushed stream ID " << stream->stream_id()
+ << "with vary field value '" << value << "'";
+ }
+ streams_pushed_and_claimed_count_++;
+ return stream;
+ }
}
// Check if we have a pending push stream for this url.
- // Note that we shouldn't have a pushed stream for non-GET method.
PendingStreamMap::iterator it;
it = pending_streams_.find(path);
if (it != pending_streams_.end()) {
@@ -255,73 +387,63 @@ scoped_refptr<SpdyStream> SpdySession::GetPushStream(
// Server will assign a stream id when the push stream arrives. Use 0 for
// now.
net_log_.AddEvent(NetLog::TYPE_SPDY_STREAM_ADOPTED_PUSH_STREAM, NULL);
- stream = new SpdyStream(this, 0, true);
+ stream = new SpdyHttpStream(this, 0, true);
+ stream->SetRequestInfo(request);
stream->set_path(path);
stream->set_net_log(stream_net_log);
it->second = stream;
return stream;
}
- return NULL;
-}
-
-const scoped_refptr<SpdyStream>& SpdySession::CreateStream(
- const GURL& url,
- RequestPriority priority,
- const BoundNetLog& stream_net_log) {
- const std::string& path = url.PathForRequest();
const spdy::SpdyStreamId stream_id = GetNewStreamId();
- scoped_refptr<SpdyStream> stream(new SpdyStream(this, stream_id, false));
-
- stream->set_priority(priority);
+ // If we still don't have a stream, activate one now.
+ stream = new SpdyHttpStream(this, stream_id, false);
+ stream->SetRequestInfo(request);
+ stream->set_priority(request.priority);
stream->set_path(path);
stream->set_net_log(stream_net_log);
ActivateStream(stream);
UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyPriorityCount",
- static_cast<int>(priority), 0, 10, 11);
+ static_cast<int>(request.priority), 0, 10, 11);
LOG(INFO) << "SpdyStream: Creating stream " << stream_id << " for " << url;
+
// TODO(mbelshe): Optimize memory allocations
- DCHECK(priority >= SPDY_PRIORITY_HIGHEST &&
- priority <= SPDY_PRIORITY_LOWEST);
+ DCHECK(request.priority >= SPDY_PRIORITY_HIGHEST &&
+ request.priority <= SPDY_PRIORITY_LOWEST);
- DCHECK_EQ(active_streams_[stream_id].get(), stream.get());
- return active_streams_[stream_id];
-}
+ // Convert from HttpRequestHeaders to Spdy Headers.
+ linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock);
+ CreateSpdyHeadersFromHttpRequest(request, headers.get());
-int SpdySession::WriteSynStream(
- spdy::SpdyStreamId stream_id,
- RequestPriority priority,
- spdy::SpdyControlFlags flags,
- const linked_ptr<spdy::SpdyHeaderBlock>& headers) {
- // Find our stream
- if (!IsStreamActive(stream_id))
- return ERR_INVALID_SPDY_STREAM;
- const scoped_refptr<SpdyStream>& stream = active_streams_[stream_id];
- CHECK_EQ(stream->stream_id(), stream_id);
+ spdy::SpdyControlFlags flags = spdy::CONTROL_FLAG_NONE;
+ if (!request.upload_data || !upload_data->size())
+ flags = spdy::CONTROL_FLAG_FIN;
+ // Create a SYN_STREAM packet and add to the output queue.
scoped_ptr<spdy::SpdySynStreamControlFrame> syn_frame(
- spdy_framer_.CreateSynStream(stream_id, 0, priority, flags, false,
+ spdy_framer_.CreateSynStream(stream_id, 0, request.priority, flags, false,
headers.get()));
- QueueFrame(syn_frame.get(), priority, stream);
+ QueueFrame(syn_frame.get(), request.priority, stream);
static StatsCounter spdy_requests("spdy.requests");
spdy_requests.Increment();
+
+ LOG(INFO) << "FETCHING: " << request.url.spec();
streams_initiated_count_++;
LOG(INFO) << "SPDY SYN_STREAM HEADERS ----------------------------------";
DumpSpdyHeaders(*headers);
- const BoundNetLog& log = stream->net_log();
- if (log.HasListener()) {
- log.AddEvent(
+ if (stream_net_log.HasListener()) {
+ stream_net_log.AddEvent(
NetLog::TYPE_SPDY_STREAM_SYN_STREAM,
new NetLogSpdySynParameter(headers, flags, stream_id));
}
- return ERR_IO_PENDING;
+ return stream;
}
int SpdySession::WriteStreamData(spdy::SpdyStreamId stream_id,
@@ -510,6 +632,7 @@ void SpdySession::OnWriteComplete(int result) {
// We only notify the stream when we've fully written the pending frame.
if (!in_flight_write_.buffer()->BytesRemaining()) {
+ scoped_refptr<SpdyStream> stream = in_flight_write_.stream();
if (stream) {
// Report the number of bytes written to the caller, but exclude the
// frame size overhead. NOTE: if this frame was compressed the
@@ -746,7 +869,7 @@ void SpdySession::DeleteStream(spdy::SpdyStreamId id, int status) {
// Remove the stream from pushed_streams_ and active_streams_.
ActivePushedStreamList::iterator it;
for (it = pushed_streams_.begin(); it != pushed_streams_.end(); ++it) {
- scoped_refptr<SpdyStream> curr = *it;
+ scoped_refptr<SpdyHttpStream> curr = *it;
if (id == curr->stream_id()) {
pushed_streams_.erase(it);
break;
@@ -772,13 +895,13 @@ void SpdySession::RemoveFromPool() {
}
}
-scoped_refptr<SpdyStream> SpdySession::GetActivePushStream(
+scoped_refptr<SpdyHttpStream> SpdySession::GetPushStream(
const std::string& path) {
static StatsCounter used_push_streams("spdy.claimed_push_streams");
LOG(INFO) << "Looking for push stream: " << path;
- scoped_refptr<SpdyStream> stream;
+ scoped_refptr<SpdyHttpStream> stream;
// We just walk a linear list here.
ActivePushedStreamList::iterator it;
@@ -796,15 +919,12 @@ scoped_refptr<SpdyStream> SpdySession::GetActivePushStream(
return NULL;
}
-bool SpdySession::GetSSLInfo(SSLInfo* ssl_info, bool* was_npn_negotiated) {
+void SpdySession::GetSSLInfo(SSLInfo* ssl_info) {
if (is_secure_) {
SSLClientSocket* ssl_socket =
reinterpret_cast<SSLClientSocket*>(connection_->socket());
ssl_socket->GetSSLInfo(ssl_info);
- *was_npn_negotiated = ssl_socket->wasNpnNegotiated();
- return true;
}
- return false;
}
void SpdySession::OnError(spdy::SpdyFramer* framer) {
@@ -829,9 +949,29 @@ void SpdySession::OnStreamFrameData(spdy::SpdyStreamId stream_id,
bool SpdySession::Respond(const spdy::SpdyHeaderBlock& headers,
const scoped_refptr<SpdyStream> stream) {
+ // TODO(mbelshe): For now we convert from our nice hash map back
+ // to a string of headers; this is because the HttpResponseInfo
+ // is a bit rigid for its http (non-spdy) design.
+ HttpResponseInfo response;
+ // TODO(ahendrickson): This is recorded after the entire SYN_STREAM control
+ // frame has been received and processed. Move to framer?
+ response.response_time = base::Time::Now();
int rv = OK;
- rv = stream->OnResponseReceived(headers);
+ if (SpdyHeadersToHttpResponse(headers, &response)) {
+ GetSSLInfo(&response.ssl_info);
+ response.request_time = stream->GetRequestTime();
+ response.vary_data.Init(*stream->GetRequestInfo(), *response.headers);
+ if (is_secure_) {
+ SSLClientSocket* ssl_socket =
+ reinterpret_cast<SSLClientSocket*>(connection_->socket());
+ response.was_npn_negotiated = ssl_socket->wasNpnNegotiated();
+ }
+ rv = stream->OnResponseReceived(response);
+ } else {
+ rv = ERR_INVALID_RESPONSE;
+ }
+
if (rv < 0) {
DCHECK_NE(rv, ERR_IO_PENDING);
const spdy::SpdyStreamId stream_id = stream->stream_id();
@@ -878,7 +1018,7 @@ void SpdySession::OnSyn(const spdy::SpdySynStreamControlFrame& frame,
}
// Only HTTP push a stream.
- scoped_refptr<SpdyStream> stream;
+ scoped_refptr<SpdyHttpStream> stream;
// Check if we already have a delegate awaiting this stream.
PendingStreamMap::iterator it;
@@ -901,7 +1041,7 @@ void SpdySession::OnSyn(const spdy::SpdySynStreamControlFrame& frame,
stream_id));
}
} else {
- stream = new SpdyStream(this, stream_id, true);
+ stream = new SpdyHttpStream(this, stream_id, true);
if (net_log_.HasListener()) {
net_log_.AddEvent(
@@ -910,6 +1050,15 @@ void SpdySession::OnSyn(const spdy::SpdySynStreamControlFrame& frame,
headers, static_cast<spdy::SpdyControlFlags>(frame.flags()),
stream_id));
}
+
+ // A new HttpResponseInfo object needs to be generated so the call to
+ // OnResponseReceived below has something to fill in.
+ // When a SpdyNetworkTransaction is created for this resource, the
+ // response_info is copied over and this version is destroyed.
+ //
+ // TODO(cbentzel): Minimize allocations and copies of HttpResponseInfo
+ // object. Should it just be part of SpdyStream?
+ stream->SetPushResponse(new HttpResponseInfo());
}
pushed_streams_.push_back(stream);
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index 01c649a..4d70d22 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -31,8 +31,11 @@
namespace net {
+class SpdyHttpStream;
class SpdyStream;
class HttpNetworkSession;
+struct HttpRequestInfo;
+class HttpResponseInfo;
class BoundNetLog;
class SSLInfo;
@@ -56,32 +59,20 @@ class SpdySession : public base::RefCounted<SpdySession>,
const TCPSocketParams& destination,
RequestPriority priority);
- // Get a pushed stream for a given |url|.
- // If the server initiates a stream, it might already exist for a given path.
- // The server might also not have initiated the stream yet, but indicated it
- // will via X-Associated-Content.
- // Returns existing stream or NULL.
- scoped_refptr<SpdyStream> GetPushStream(
- const GURL& url,
- const BoundNetLog& stream_net_log);
-
- // Create a new stream for a given |url|.
- // Returns the new stream. Never returns NULL.
- const scoped_refptr<SpdyStream>& CreateStream(
- const GURL& url,
- RequestPriority priority,
+ // Get a stream for a given |request|. In the typical case, this will involve
+ // the creation of a new stream (and will send the SYN frame). If the server
+ // initiates a stream, it might already exist for a given path. The server
+ // might also not have initiated the stream yet, but indicated it will via
+ // X-Associated-Content.
+ // Returns the new or existing stream. Never returns NULL.
+ scoped_refptr<SpdyHttpStream> GetOrCreateStream(
+ const HttpRequestInfo& request,
+ const UploadDataStream* upload_data,
const BoundNetLog& stream_net_log);
// Used by SpdySessionPool to initialize with a pre-existing SSL socket.
void InitializeWithSSLSocket(ClientSocketHandle* connection);
- // Send the SYN frame for |stream_id|.
- int WriteSynStream(
- spdy::SpdyStreamId stream_id,
- RequestPriority priority,
- spdy::SpdyControlFlags flags,
- const linked_ptr<spdy::SpdyHeaderBlock>& headers);
-
// Write a data frame to the stream.
// Used to create and queue a data frame for the given stream.
int WriteStreamData(spdy::SpdyStreamId stream_id, net::IOBuffer* data,
@@ -100,16 +91,13 @@ class SpdySession : public base::RefCounted<SpdySession>,
// Closes all streams. Used as part of shutdown.
void CloseAllStreams(net::Error status);
- // Fills SSL info in |ssl_info| and returns true when SSL is in use.
- bool GetSSLInfo(SSLInfo* ssl_info, bool* was_npn_negotiated);
-
// Enable or disable SSL.
static void SetSSLMode(bool enable) { use_ssl_ = enable; }
static bool SSLMode() { return use_ssl_; }
private:
friend class base::RefCounted<SpdySession>;
- FRIEND_TEST(SpdySessionTest, GetActivePushStream);
+ FRIEND_TEST(SpdySessionTest, GetPushStream);
enum State {
IDLE,
@@ -120,8 +108,9 @@ class SpdySession : public base::RefCounted<SpdySession>,
typedef std::map<int, scoped_refptr<SpdyStream> > ActiveStreamMap;
// Only HTTP push a stream.
- typedef std::list<scoped_refptr<SpdyStream> > ActivePushedStreamList;
- typedef std::map<std::string, scoped_refptr<SpdyStream> > PendingStreamMap;
+ typedef std::list<scoped_refptr<SpdyHttpStream> > ActivePushedStreamList;
+ typedef std::map<std::string, scoped_refptr<SpdyHttpStream> >
+ PendingStreamMap;
typedef std::priority_queue<SpdyIOBuffer> OutputQueue;
virtual ~SpdySession();
@@ -184,13 +173,15 @@ class SpdySession : public base::RefCounted<SpdySession>,
// Check if we have a pending pushed-stream for this url
// Returns the stream if found (and returns it from the pending
// list), returns NULL otherwise.
- scoped_refptr<SpdyStream> GetActivePushStream(const std::string& url);
+ scoped_refptr<SpdyHttpStream> GetPushStream(const std::string& url);
- // Calls OnResponseReceived().
+ // Creates an HttpResponseInfo instance, and calls OnResponseReceived().
// Returns true if successful.
bool Respond(const spdy::SpdyHeaderBlock& headers,
const scoped_refptr<SpdyStream> stream);
+ void GetSSLInfo(SSLInfo* ssl_info);
+
void RecordHistograms();
// Callbacks for the Spdy session.
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index 67dcb94..1cefb49 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -179,7 +179,7 @@ static const uint8 kPush[] = {
} // namespace
-TEST_F(SpdySessionTest, GetActivePushStream) {
+TEST_F(SpdySessionTest, GetPushStream) {
SpdySessionTest::TurnOffCompression();
SessionDependencies session_deps;
@@ -216,9 +216,9 @@ TEST_F(SpdySessionTest, GetActivePushStream) {
// No push streams should exist in the beginning.
std::string test_push_path = "/foo.js";
- scoped_refptr<SpdyStream> first_stream = session->GetActivePushStream(
+ scoped_refptr<SpdyHttpStream> first_stream = session->GetPushStream(
test_push_path);
- EXPECT_EQ(static_cast<SpdyStream*>(NULL), first_stream.get());
+ EXPECT_EQ(static_cast<SpdyHttpStream*>(NULL), first_stream.get());
// Read in the data which contains a server-issued SYN_STREAM.
TCPSocketParams tcp_params(test_host_port_pair, MEDIUM, GURL(), false);
@@ -227,14 +227,14 @@ TEST_F(SpdySessionTest, GetActivePushStream) {
MessageLoop::current()->RunAllPending();
// An unpushed path should not work.
- scoped_refptr<SpdyStream> unpushed_stream = session->GetActivePushStream(
+ scoped_refptr<SpdyHttpStream> unpushed_stream = session->GetPushStream(
"/unpushed_path");
- EXPECT_EQ(static_cast<SpdyStream*>(NULL), unpushed_stream.get());
+ EXPECT_EQ(static_cast<SpdyHttpStream*>(NULL), unpushed_stream.get());
// The pushed path should be found.
- scoped_refptr<SpdyStream> second_stream = session->GetActivePushStream(
+ scoped_refptr<SpdyHttpStream> second_stream = session->GetPushStream(
test_push_path);
- ASSERT_NE(static_cast<SpdyStream*>(NULL), second_stream.get());
+ ASSERT_NE(static_cast<SpdyHttpStream*>(NULL), second_stream.get());
EXPECT_EQ(test_push_path, second_stream->path());
EXPECT_EQ(2U, second_stream->stream_id());
EXPECT_EQ(0, second_stream->priority());
diff --git a/net/spdy/spdy_stream.cc b/net/spdy/spdy_stream.cc
index 3fbde08..baff2b9 100644
--- a/net/spdy/spdy_stream.cc
+++ b/net/spdy/spdy_stream.cc
@@ -7,6 +7,8 @@
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/singleton.h"
+#include "net/http/http_request_info.h"
+#include "net/http/http_response_info.h"
#include "net/spdy/spdy_session.h"
namespace net {
@@ -18,9 +20,8 @@ SpdyStream::SpdyStream(
pushed_(pushed),
metrics_(Singleton<BandwidthMetrics>::get()),
session_(session),
- delegate_(NULL),
request_time_(base::Time::Now()),
- response_(new spdy::SpdyHeaderBlock),
+ response_(NULL),
response_complete_(false),
io_state_(STATE_NONE),
response_status_(OK),
@@ -38,36 +39,23 @@ SpdyStream::~SpdyStream() {
DCHECK(response_complete_);
}
-void SpdyStream::SetDelegate(Delegate* delegate) {
- CHECK(delegate);
- delegate_ = delegate;
-
- if (!response_->empty()) {
- // The stream already got response.
- delegate_->OnResponseReceived(*response_, response_time_, OK);
- }
-
- std::vector<scoped_refptr<IOBufferWithSize> > buffers;
- buffers.swap(pending_buffers_);
- for (size_t i = 0; i < buffers.size(); ++i) {
- if (delegate_)
- delegate_->OnDataReceived(buffers[i]->data(), buffers[i]->size());
- }
+const HttpResponseInfo* SpdyStream::GetResponseInfo() const {
+ return response_;
}
-void SpdyStream::DetachDelegate() {
- delegate_ = NULL;
- if (!cancelled())
- Cancel();
+void SpdyStream::SetPushResponse(HttpResponseInfo* response_info) {
+ DCHECK(!response_);
+ DCHECK(!push_response_.get());
+ push_response_.reset(response_info);
+ response_ = response_info;
}
-const linked_ptr<spdy::SpdyHeaderBlock>& SpdyStream::spdy_headers() const {
- return request_;
+const HttpRequestInfo* SpdyStream::GetRequestInfo() const {
+ return &request_;
}
-void SpdyStream::set_spdy_headers(
- const linked_ptr<spdy::SpdyHeaderBlock>& headers) {
- request_ = headers;
+void SpdyStream::SetRequestInfo(const HttpRequestInfo& request) {
+ request_ = request;
}
base::Time SpdyStream::GetRequestTime() const {
@@ -76,20 +64,25 @@ base::Time SpdyStream::GetRequestTime() const {
void SpdyStream::SetRequestTime(base::Time t) {
request_time_ = t;
+
+ // This should only get called in the case of a request occuring
+ // during server push that has already begun but hasn't finished,
+ // so we set the response's request time to be the actual one
+ if (response_)
+ response_->request_time = request_time_;
}
-int SpdyStream::OnResponseReceived(const spdy::SpdyHeaderBlock& response) {
+int SpdyStream::DoOnResponseReceived(const HttpResponseInfo& response) {
int rv = OK;
- LOG(INFO) << "OnResponseReceived";
metrics_.StartStream();
- DCHECK(response_->empty());
- *response_ = response; // TODO(ukai): avoid copy.
- DCHECK(!response_->empty());
+ CHECK(!response_->headers);
+
+ *response_ = response; // TODO(mbelshe): avoid copy.
+ DCHECK(response_->headers);
recv_first_byte_time_ = base::TimeTicks::Now();
- response_time_ = base::Time::Now();
if (io_state_ == STATE_NONE) {
CHECK(pushed_);
@@ -108,15 +101,11 @@ int SpdyStream::OnResponseReceived(const spdy::SpdyHeaderBlock& response) {
}
rv = DoLoop(rv);
- if (delegate_)
- rv = delegate_->OnResponseReceived(*response_, response_time_, rv);
- // if delegate_ is not yet attached, we'll return response when delegate
- // gets attached to the stream.
return rv;
}
-void SpdyStream::OnDataReceived(const char* data, int length) {
+bool SpdyStream::DoOnDataReceived(const char* data, int length) {
DCHECK_GE(length, 0);
LOG(INFO) << "SpdyStream: Data (" << length << " bytes) received for "
<< stream_id_;
@@ -126,9 +115,9 @@ void SpdyStream::OnDataReceived(const char* data, int length) {
// If we don't have a response, then the SYN_REPLY did not come through.
// We cannot pass data up to the caller unless the reply headers have been
// received.
- if (response_->empty()) {
+ if (!response_->headers) {
session_->CloseStream(stream_id_, ERR_SYN_REPLY_NOT_RECEIVED);
- return;
+ return false;
}
// A zero-length read means that the stream is being closed.
@@ -137,7 +126,7 @@ void SpdyStream::OnDataReceived(const char* data, int length) {
scoped_refptr<SpdyStream> self(this);
session_->CloseStream(stream_id_, net::OK);
UpdateHistograms();
- return;
+ return true;
}
// Track our bandwidth.
@@ -145,19 +134,10 @@ void SpdyStream::OnDataReceived(const char* data, int length) {
recv_bytes_ += length;
recv_last_byte_time_ = base::TimeTicks::Now();
- if (!delegate_) {
- // It should be valid for this to happen in the server push case.
- // We'll return received data when delegate gets attached to the stream.
- IOBufferWithSize* buf = new IOBufferWithSize(length);
- memcpy(buf->data(), data, length);
- pending_buffers_.push_back(buf);
- return;
- }
-
- delegate_->OnDataReceived(data, length);
+ return true;
}
-void SpdyStream::OnWriteComplete(int status) {
+void SpdyStream::DoOnWriteComplete(int status) {
// TODO(mbelshe): Check for cancellation here. If we're cancelled, we
// should discontinue the DoLoop.
@@ -171,35 +151,45 @@ void SpdyStream::OnWriteComplete(int status) {
DoLoop(status);
}
-void SpdyStream::OnClose(int status) {
+void SpdyStream::DoOnClose(int status) {
response_complete_ = true;
response_status_ = status;
stream_id_ = 0;
- Delegate* delegate = delegate_;
- delegate_ = NULL;
- if (delegate)
- delegate->OnClose(status);
}
-void SpdyStream::Cancel() {
+void SpdyStream::DoCancel() {
cancelled_ = true;
session_->CloseStream(stream_id_, ERR_ABORTED);
}
-int SpdyStream::DoSendRequest(bool has_upload_data) {
+int SpdyStream::DoSendRequest(UploadDataStream* upload_data,
+ HttpResponseInfo* response) {
CHECK(!cancelled_);
+ CHECK(response);
+
+ // SendRequest can be called in two cases.
+ //
+ // a) A client initiated request. In this case, response_ should be NULL
+ // to start with.
+ // b) A client request which matches a response that the server has already
+ // pushed. In this case, the value of |*push_response_| is copied over to
+ // the new response object |*response|. |push_response_| is cleared
+ // and |*push_response_| is deleted, and |response_| is reset to
+ // |response|.
+ if (push_response_.get()) {
+ *response = *push_response_;
+ push_response_.reset(NULL);
+ response_ = NULL;
+ }
+
+ DCHECK_EQ(static_cast<HttpResponseInfo*>(NULL), response_);
+ response_ = response;
- if (!pushed_) {
- spdy::SpdyControlFlags flags = spdy::CONTROL_FLAG_NONE;
- if (!has_upload_data)
- flags = spdy::CONTROL_FLAG_FIN;
-
- CHECK(request_.get());
- int result = session_->WriteSynStream(
- stream_id_, static_cast<RequestPriority>(priority_), flags,
- request_);
- if (result != ERR_IO_PENDING)
- return result;
+ if (upload_data) {
+ if (upload_data->size())
+ request_body_stream_.reset(upload_data);
+ else
+ delete upload_data;
}
send_time_ = base::TimeTicks::Now();
@@ -208,7 +198,7 @@ int SpdyStream::DoSendRequest(bool has_upload_data) {
if (!pushed_)
io_state_ = STATE_SEND_HEADERS;
else {
- if (!response_->empty()) {
+ if (response_->headers) {
io_state_ = STATE_READ_BODY;
} else {
io_state_ = STATE_READ_HEADERS;
@@ -222,7 +212,7 @@ int SpdyStream::DoReadResponseHeaders() {
CHECK(!cancelled_);
// The SYN_REPLY has already been received.
- if (!response_->empty())
+ if (response_->headers)
return OK;
io_state_ = STATE_READ_HEADERS;
@@ -230,14 +220,6 @@ int SpdyStream::DoReadResponseHeaders() {
return ERR_IO_PENDING;
}
-int SpdyStream::WriteStreamData(IOBuffer* data, int length) {
- return session_->WriteStreamData(stream_id_, data, length);
-}
-
-bool SpdyStream::GetSSLInfo(SSLInfo* ssl_info, bool* was_npn_negotiated) {
- return session_->GetSSLInfo(ssl_info, was_npn_negotiated);
-}
-
int SpdyStream::DoLoop(int result) {
do {
State state = io_state_;
@@ -313,11 +295,8 @@ int SpdyStream::DoSendHeadersComplete(int result) {
CHECK_GT(result, 0);
- if (!delegate_)
- return ERR_UNEXPECTED;
-
// There is no body, skip that state.
- if (delegate_->OnSendHeadersComplete(result)) {
+ if (!request_body_stream_.get()) {
io_state_ = STATE_READ_HEADERS;
return OK;
}
@@ -335,9 +314,12 @@ int SpdyStream::DoSendBody() {
// the number of bytes in the frame that were written, only consume the
// data portion, of course.
io_state_ = STATE_SEND_BODY_COMPLETE;
- if (!delegate_)
- return ERR_UNEXPECTED;
- return delegate_->OnSendBody();
+ int buf_len = static_cast<int>(request_body_stream_->buf_len());
+ if (!buf_len)
+ return OK;
+ return session_->WriteStreamData(stream_id_,
+ request_body_stream_->buf(),
+ buf_len);
}
int SpdyStream::DoSendBodyComplete(int result) {
@@ -346,10 +328,9 @@ int SpdyStream::DoSendBodyComplete(int result) {
CHECK_NE(result, 0);
- if (!delegate_)
- return ERR_UNEXPECTED;
+ request_body_stream_->DidConsume(result);
- if (!delegate_->OnSendBodyComplete(result))
+ if (!request_body_stream_->eof())
io_state_ = STATE_SEND_BODY;
else
io_state_ = STATE_READ_HEADERS;
@@ -359,7 +340,7 @@ int SpdyStream::DoSendBodyComplete(int result) {
int SpdyStream::DoReadHeaders() {
io_state_ = STATE_READ_HEADERS_COMPLETE;
- return !response_->empty() ? OK : ERR_IO_PENDING;
+ return response_->headers ? OK : ERR_IO_PENDING;
}
int SpdyStream::DoReadHeadersComplete(int result) {
diff --git a/net/spdy/spdy_stream.h b/net/spdy/spdy_stream.h
index 919bca8..1792cf1 100644
--- a/net/spdy/spdy_stream.h
+++ b/net/spdy/spdy_stream.h
@@ -6,25 +6,25 @@
#define NET_SPDY_SPDY_STREAM_H_
#include <string>
-#include <vector>
#include "base/basictypes.h"
-#include "base/linked_ptr.h"
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
#include "net/base/bandwidth_metrics.h"
#include "net/base/io_buffer.h"
#include "net/base/net_log.h"
-#include "net/spdy/spdy_framer.h"
+#include "net/http/http_request_info.h"
#include "net/spdy/spdy_protocol.h"
namespace net {
+class HttpResponseInfo;
class SpdySession;
-class SSLInfo;
+class UploadDataStream;
// The SpdyStream is used by the SpdySession to represent each stream known
-// on the SpdySession. This class provides interfaces for SpdySession to use.
+// on the SpdySession. This class provides interfaces for SpdySession to use
+// and base implementations for the interfaces.
// 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
@@ -32,58 +32,9 @@ class SSLInfo;
// until such a time as a client object requests a stream for the path.
class SpdyStream : public base::RefCounted<SpdyStream> {
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 has been sent.
- // Returns true if no more data to be sent.
- virtual bool OnSendBodyComplete(int status) = 0;
-
- // Called when SYN_REPLY received. |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.
- // Returns true if data is successfully processed.
- virtual void OnDataReceived(const char* data, int length) = 0;
-
- // Called when SpdyStream is closed.
- virtual void OnClose(int status) = 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);
- // 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_; }
@@ -100,8 +51,9 @@ class SpdyStream : public base::RefCounted<SpdyStream> {
const BoundNetLog& net_log() const { return net_log_; }
void set_net_log(const BoundNetLog& log) { net_log_ = log; }
- const linked_ptr<spdy::SpdyHeaderBlock>& spdy_headers() const;
- void set_spdy_headers(const linked_ptr<spdy::SpdyHeaderBlock>& headers);
+ const HttpResponseInfo* GetResponseInfo() const;
+ const HttpRequestInfo* GetRequestInfo() const;
+ void SetRequestInfo(const HttpRequestInfo& request);
base::Time GetRequestTime() const;
void SetRequestTime(base::Time t);
@@ -109,7 +61,7 @@ class SpdyStream : public base::RefCounted<SpdyStream> {
// received for this stream. |path| is the path of the URL for a server
// initiated stream, otherwise is empty.
// Returns a status code.
- int OnResponseReceived(const spdy::SpdyHeaderBlock& response);
+ virtual int OnResponseReceived(const HttpResponseInfo& response) = 0;
// Called by the SpdySession when response data has been received for this
// stream. This callback may be called multiple times as data arrives
@@ -118,43 +70,61 @@ class SpdyStream : public base::RefCounted<SpdyStream> {
// 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);
+ // Returns true on success and false on error.
+ virtual bool OnDataReceived(const char* buffer, int bytes) = 0;
// 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 status);
+ virtual void OnWriteComplete(int status) = 0;
// 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);
+ virtual void OnClose(int status) = 0;
- void Cancel();
+ virtual void Cancel() = 0;
bool cancelled() const { return cancelled_; }
- // Interface for Spdy[Http|WebSocket]Stream to use.
+ void SetPushResponse(HttpResponseInfo* response_info);
+
+ protected:
+ friend class base::RefCounted<SpdyStream>;
+ virtual ~SpdyStream();
+
+ int DoOnResponseReceived(const HttpResponseInfo& response);
+ bool DoOnDataReceived(const char* buffer,int bytes);
+ void DoOnWriteComplete(int status);
+ void DoOnClose(int status);
- // Sends the request.
- // For non push stream, it will send SYN_STREAM frame.
- int DoSendRequest(bool has_upload_data);
+ void DoCancel();
+
+ // Sends the request. If |upload_data| is non-NULL, sends that in the
+ // request body. Note that the actual SYN_STREAM packet will have already
+ // been sent by this point.
+ // Note that SpdyStream takes ownership of |upload_data|.
+ // TODO(ukai): move out HTTP-specific thing to SpdyHttpStream.
+ int DoSendRequest(UploadDataStream* upload_data,
+ HttpResponseInfo* response_info);
// Reads response headers. If the SpdyStream have already received
// the response headers, return OK and response headers filled in
- // |response| given in SendRequest.
- // Otherwise, return ERR_IO_PENDING and OnResponseReceived() will be called.
+ // |response_info| given in SendRequest.
+ // Otherwise, return ERR_IO_PENDING.
int DoReadResponseHeaders();
- // Sends DATA frame.
- int WriteStreamData(IOBuffer* data, int length);
-
- bool GetSSLInfo(SSLInfo* ssl_info, bool* was_npn_negotiated);
+ const UploadDataStream* request_body_stream() const {
+ return request_body_stream_.get();
+ }
bool is_idle() const { return io_state_ == STATE_NONE; }
bool response_complete() const { return response_complete_; }
+ void set_response_complete(bool response_complete) {
+ response_complete_ = response_complete;
+ }
int response_status() const { return response_status_; }
private:
@@ -171,9 +141,6 @@ class SpdyStream : public base::RefCounted<SpdyStream> {
STATE_DONE
};
- friend class base::RefCounted<SpdyStream>;
- virtual ~SpdyStream();
-
// Try to make progress sending/receiving the request/response.
int DoLoop(int result);
@@ -199,18 +166,26 @@ class SpdyStream : public base::RefCounted<SpdyStream> {
scoped_refptr<SpdySession> session_;
- // The transaction should own the delegate.
- SpdyStream::Delegate* delegate_;
-
// The request to send.
- linked_ptr<spdy::SpdyHeaderBlock> request_;
+ HttpRequestInfo 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_;
+ // The push_response_ is the HTTP response data which is part of
+ // a server-initiated SYN_STREAM. If a client request comes in
+ // which matches the push stream, the data in push_response_ will
+ // be copied over to the response_ object owned by the caller
+ // of the request.
+ scoped_ptr<HttpResponseInfo> push_response_;
+
+ // response_ is the HTTP response data object which is filled in
+ // when a SYN_REPLY comes in for the stream. It is not owned by this
+ // stream object.
+ HttpResponseInfo* response_;
+
+ scoped_ptr<UploadDataStream> request_body_stream_;
bool response_complete_; // TODO(mbelshe): fold this into the io_state.
State io_state_;
@@ -229,8 +204,6 @@ class SpdyStream : public base::RefCounted<SpdyStream> {
int send_bytes_;
int recv_bytes_;
bool histograms_recorded_;
- // Data received before delegate is attached.
- std::vector<scoped_refptr<IOBufferWithSize> > pending_buffers_;
DISALLOW_COPY_AND_ASSIGN(SpdyStream);
};