summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-28 03:28:27 +0000
committerrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-28 03:28:27 +0000
commit3ee0489049bb17df3ec74213dbcc8e74d927d7c0 (patch)
tree475fbaafb8ecd58b4eaf5272b002752f62a7d4a3
parente2150328d75a1ae7a7684a7e5fda90842886812d (diff)
downloadchromium_src-3ee0489049bb17df3ec74213dbcc8e74d927d7c0.zip
chromium_src-3ee0489049bb17df3ec74213dbcc8e74d927d7c0.tar.gz
chromium_src-3ee0489049bb17df3ec74213dbcc8e74d927d7c0.tar.bz2
Add a new class SpdyProxyClientSocket which implements ClientSocket
by sending a CONNECT request via a SPDY SYN_STREAM frame to a SPDY proxy, and then reading/writing data to/from SPDY Data frames. BUG=29625 TEST=none Review URL: http://codereview.chromium.org/3432009 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@60747 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--net/http/http_proxy_client_socket.cc33
-rw-r--r--net/http/http_proxy_utils.cc39
-rw-r--r--net/http/http_proxy_utils.h28
-rw-r--r--net/net.gyp5
-rw-r--r--net/spdy/spdy_framer.h10
-rw-r--r--net/spdy/spdy_http_utils.cc2
-rw-r--r--net/spdy/spdy_proxy_client_socket.cc437
-rw-r--r--net/spdy/spdy_proxy_client_socket.h183
-rw-r--r--net/spdy/spdy_proxy_client_socket_unittest.cc894
-rw-r--r--net/spdy/spdy_session.h10
-rw-r--r--net/spdy/spdy_stream.cc7
-rw-r--r--net/spdy/spdy_stream.h7
12 files changed, 1620 insertions, 35 deletions
diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc
index ccc5d5d..ddefe8e 100644
--- a/net/http/http_proxy_client_socket.cc
+++ b/net/http/http_proxy_client_socket.cc
@@ -14,42 +14,13 @@
#include "net/base/net_util.h"
#include "net/http/http_net_log_params.h"
#include "net/http/http_network_session.h"
+#include "net/http/http_proxy_utils.h"
#include "net/http/http_request_info.h"
#include "net/http/http_stream_parser.h"
#include "net/socket/client_socket_handle.h"
namespace net {
-namespace {
-
-// The HTTP CONNECT method for establishing a tunnel connection is documented
-// in draft-luotonen-web-proxy-tunneling-01.txt and RFC 2817, Sections 5.2 and
-// 5.3.
-void BuildTunnelRequest(const HttpRequestInfo* request_info,
- const HttpRequestHeaders& authorization_headers,
- const HostPortPair& endpoint,
- std::string* request_line,
- HttpRequestHeaders* request_headers) {
- // RFC 2616 Section 9 says the Host request-header field MUST accompany all
- // HTTP/1.1 requests. Add "Proxy-Connection: keep-alive" for compat with
- // HTTP/1.0 proxies such as Squid (required for NTLM authentication).
- *request_line = base::StringPrintf(
- "CONNECT %s HTTP/1.1\r\n", endpoint.ToString().c_str());
- request_headers->SetHeader(HttpRequestHeaders::kHost,
- GetHostAndOptionalPort(request_info->url));
- request_headers->SetHeader(HttpRequestHeaders::kProxyConnection,
- "keep-alive");
-
- std::string user_agent;
- if (request_info->extra_headers.GetHeader(HttpRequestHeaders::kUserAgent,
- &user_agent))
- request_headers->SetHeader(HttpRequestHeaders::kUserAgent, user_agent);
-
- request_headers->MergeFrom(authorization_headers);
-}
-
-} // namespace
-
HttpProxyClientSocket::HttpProxyClientSocket(
ClientSocketHandle* transport_socket,
const GURL& request_url,
@@ -358,7 +329,7 @@ int HttpProxyClientSocket::DoSendRequest() {
auth_->AddAuthorizationHeader(&authorization_headers);
std::string request_line;
HttpRequestHeaders request_headers;
- BuildTunnelRequest(&request_, authorization_headers, endpoint_,
+ BuildTunnelRequest(request_, authorization_headers, endpoint_,
&request_line, &request_headers);
if (net_log_.IsLoggingAll()) {
net_log_.AddEvent(
diff --git a/net/http/http_proxy_utils.cc b/net/http/http_proxy_utils.cc
new file mode 100644
index 0000000..3e4fb8b
--- /dev/null
+++ b/net/http/http_proxy_utils.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http/http_proxy_utils.h"
+
+#include "base/string_util.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/net_util.h"
+#include "net/http/http_request_info.h"
+
+namespace net {
+
+void BuildTunnelRequest(
+ const HttpRequestInfo& request_info,
+ const HttpRequestHeaders& auth_headers,
+ const HostPortPair& endpoint,
+ std::string* request_line,
+ HttpRequestHeaders* request_headers) {
+ // RFC 2616 Section 9 says the Host request-header field MUST accompany all
+ // HTTP/1.1 requests. Add "Proxy-Connection: keep-alive" for compat with
+ // HTTP/1.0 proxies such as Squid (required for NTLM authentication).
+ *request_line = StringPrintf(
+ "CONNECT %s HTTP/1.1\r\n", endpoint.ToString().c_str());
+ request_headers->SetHeader(HttpRequestHeaders::kHost,
+ GetHostAndOptionalPort(request_info.url));
+ request_headers->SetHeader(HttpRequestHeaders::kProxyConnection,
+ "keep-alive");
+
+ std::string user_agent;
+ if (request_info.extra_headers.GetHeader(HttpRequestHeaders::kUserAgent,
+ &user_agent))
+ request_headers->SetHeader(HttpRequestHeaders::kUserAgent, user_agent);
+
+ request_headers->MergeFrom(auth_headers);
+}
+
+} // namespace net
diff --git a/net/http/http_proxy_utils.h b/net/http/http_proxy_utils.h
new file mode 100644
index 0000000..9d18fc8
--- /dev/null
+++ b/net/http/http_proxy_utils.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2010 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_HTTP_HTTP_PROXY_UTILS_H_
+#define NET_HTTP_HTTP_PROXY_UTILS_H_
+#pragma once
+
+#include <string>
+
+namespace net {
+
+struct HttpRequestInfo;
+class HttpRequestHeaders;
+class HostPortPair;
+
+// The HTTP CONNECT method for establishing a tunnel connection is documented
+// in draft-luotonen-web-proxy-tunneling-01.txt and RFC 2817, Sections 5.2 and
+// 5.3.
+void BuildTunnelRequest(const HttpRequestInfo& request_info,
+ const HttpRequestHeaders& auth_headers,
+ const HostPortPair& endpoint,
+ std::string* request_line,
+ HttpRequestHeaders* request_headers);
+
+} // namespace net
+
+#endif // NET_HTTP_HTTP_PROXY_UTILS_H_
diff --git a/net/net.gyp b/net/net.gyp
index 06105fa..b93ce86 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -453,6 +453,8 @@
'http/http_proxy_client_socket.h',
'http/http_proxy_client_socket_pool.cc',
'http/http_proxy_client_socket_pool.h',
+ 'http/http_proxy_utils.cc',
+ 'http/http_proxy_utils.h',
'http/http_util.cc',
'http/http_util_icu.cc',
'http/http_util.h',
@@ -570,6 +572,8 @@
'spdy/spdy_io_buffer.cc',
'spdy/spdy_io_buffer.h',
'spdy/spdy_protocol.h',
+ 'spdy/spdy_proxy_client_socket.cc',
+ 'spdy/spdy_proxy_client_socket.h',
'spdy/spdy_session.cc',
'spdy/spdy_session.h',
'spdy/spdy_session_pool.cc',
@@ -845,6 +849,7 @@
'spdy/spdy_http_stream_unittest.cc',
'spdy/spdy_network_transaction_unittest.cc',
'spdy/spdy_protocol_test.cc',
+ 'spdy/spdy_proxy_client_socket_unittest.cc',
'spdy/spdy_session_unittest.cc',
'spdy/spdy_stream_unittest.cc',
'spdy/spdy_test_util.cc',
diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h
index e6d0e15..00ddac0 100644
--- a/net/spdy/spdy_framer.h
+++ b/net/spdy/spdy_framer.h
@@ -26,10 +26,11 @@ typedef struct z_stream_s z_stream; // Forward declaration for zlib.
namespace net {
class HttpNetworkLayer;
class HttpNetworkTransactionTest;
+class SpdyHttpStreamTest;
class SpdyNetworkTransactionTest;
+class SpdyProxyClientSocketTest;
class SpdySessionTest;
class SpdyStreamTest;
-class SpdyHttpStreamTest;
}
namespace spdy {
@@ -248,11 +249,12 @@ class SpdyFramer {
protected:
FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, DataCompression);
FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, UnclosedStreamDataCompressors);
- friend class net::SpdyNetworkTransactionTest;
- friend class net::HttpNetworkTransactionTest;
friend class net::HttpNetworkLayer; // This is temporary for the server.
- friend class net::SpdySessionTest;
+ friend class net::HttpNetworkTransactionTest;
friend class net::SpdyHttpStreamTest;
+ friend class net::SpdyNetworkTransactionTest;
+ friend class net::SpdyProxyClientSocketTest;
+ friend class net::SpdySessionTest;
friend class net::SpdyStreamTest;
friend class test::TestSpdyVisitor;
friend void test::FramerSetEnableCompressionHelper(SpdyFramer* framer,
diff --git a/net/spdy/spdy_http_utils.cc b/net/spdy/spdy_http_utils.cc
index d2fdd8fa..b2ef551 100644
--- a/net/spdy/spdy_http_utils.cc
+++ b/net/spdy/spdy_http_utils.cc
@@ -4,6 +4,8 @@
#include "net/spdy/spdy_http_utils.h"
+#include <string>
+
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/time.h"
diff --git a/net/spdy/spdy_proxy_client_socket.cc b/net/spdy/spdy_proxy_client_socket.cc
new file mode 100644
index 0000000..e282d76
--- /dev/null
+++ b/net/spdy/spdy_proxy_client_socket.cc
@@ -0,0 +1,437 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/spdy/spdy_proxy_client_socket.h"
+
+#include <algorithm> // min
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/auth.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_util.h"
+#include "net/http/http_auth_cache.h"
+#include "net/http/http_auth_handler_factory.h"
+#include "net/http/http_net_log_params.h"
+#include "net/http/http_proxy_utils.h"
+#include "net/spdy/spdy_http_utils.h"
+
+namespace net {
+
+SpdyProxyClientSocket::SpdyProxyClientSocket(
+ SpdyStream* spdy_stream,
+ const std::string& user_agent,
+ const HostPortPair& endpoint,
+ const GURL& url,
+ const HostPortPair& proxy_server,
+ HttpAuthCache* auth_cache,
+ HttpAuthHandlerFactory* auth_handler_factory)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(
+ io_callback_(this, &SpdyProxyClientSocket::OnIOComplete)),
+ next_state_(STATE_NONE),
+ spdy_stream_(spdy_stream),
+ read_callback_(NULL),
+ write_callback_(NULL),
+ endpoint_(endpoint),
+ auth_(
+ new HttpAuthController(HttpAuth::AUTH_PROXY,
+ GURL("http://" + proxy_server.ToString()),
+ auth_cache,
+ auth_handler_factory)),
+ user_buffer_(NULL),
+ write_buffer_len_(0),
+ write_bytes_outstanding_(0),
+ eof_has_been_read_(false),
+ net_log_(spdy_stream->net_log()) {
+ request_.method = "CONNECT";
+ request_.url = url;
+ if (!user_agent.empty())
+ request_.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent,
+ user_agent);
+ spdy_stream_->SetDelegate(this);
+ was_ever_used_ = spdy_stream_->WasEverUsed();
+}
+
+SpdyProxyClientSocket::~SpdyProxyClientSocket() {
+ Disconnect();
+}
+
+// Sends a SYN_STREAM frame to the proxy with a CONNECT request
+// for the specified endpoint. Waits for the server to send back
+// a SYN_REPLY frame. OK will be returned if the status is 200.
+// ERR_TUNNEL_CONNECTION_FAILED will be returned for any other status.
+// In any of these cases, Read() may be called to retrieve the HTTP
+// response body. Any other return values should be considered fatal.
+// TODO(rch): handle 407 proxy auth requested correctly, perhaps
+// by creating a new stream for the subsequent request.
+// TODO(rch): create a more appropriate error code to disambiguate
+// the HTTPS Proxy tunnel failure from an HTTP Proxy tunnel failure.
+int SpdyProxyClientSocket::Connect(CompletionCallback* callback) {
+ DCHECK(!read_callback_);
+ if (next_state_ == STATE_DONE)
+ return OK;
+
+ DCHECK_EQ(STATE_NONE, next_state_);
+ next_state_ = STATE_GENERATE_AUTH_TOKEN;
+
+ int rv = DoLoop(OK);
+ if (rv == ERR_IO_PENDING)
+ read_callback_ = callback;
+ return rv;
+}
+
+void SpdyProxyClientSocket::Disconnect() {
+ next_state_ = STATE_NONE;
+ if (spdy_stream_)
+ // This will cause OnClose to be invoked, which takes care of
+ // cleaning up all the internal state.
+ spdy_stream_->Cancel();
+}
+
+bool SpdyProxyClientSocket::IsConnected() const {
+ return next_state_ == STATE_DONE && spdy_stream_ != NULL &&
+ !spdy_stream_->closed();
+}
+
+bool SpdyProxyClientSocket::IsConnectedAndIdle() const {
+ return IsConnected() && !spdy_stream_->is_idle();
+}
+
+void SpdyProxyClientSocket::SetSubresourceSpeculation() {
+ // TODO(rch): what should this implementation be?
+}
+
+void SpdyProxyClientSocket::SetOmniboxSpeculation() {
+ // TODO(rch): what should this implementation be?
+}
+
+bool SpdyProxyClientSocket::WasEverUsed() const {
+ return was_ever_used_ || (spdy_stream_ && spdy_stream_->WasEverUsed());
+}
+
+int SpdyProxyClientSocket::Read(IOBuffer* buf, int buf_len,
+ CompletionCallback* callback) {
+ DCHECK(!read_callback_);
+ DCHECK(!user_buffer_);
+
+ if (!spdy_stream_) {
+ if (eof_has_been_read_)
+ return ERR_CONNECTION_CLOSED;
+ eof_has_been_read_ = true;
+ return 0;
+ }
+
+ DCHECK(next_state_ == STATE_DONE);
+ DCHECK(buf);
+ user_buffer_ = new DrainableIOBuffer(buf, buf_len);
+ int result = PopulateUserReadBuffer();
+ if (result == 0) {
+ DCHECK(callback);
+ read_callback_ = callback;
+ return ERR_IO_PENDING;
+ }
+ user_buffer_ = NULL;
+ return result;
+}
+
+int SpdyProxyClientSocket::PopulateUserReadBuffer() {
+ if (!user_buffer_)
+ return ERR_IO_PENDING;
+
+ while (!read_buffer_.empty() && user_buffer_->BytesRemaining() > 0) {
+ scoped_refptr<DrainableIOBuffer> data = read_buffer_.front();
+ const int bytes_to_copy = std::min(user_buffer_->BytesRemaining(),
+ data->size());
+ memcpy(user_buffer_->data(), data->data(), bytes_to_copy);
+ user_buffer_->DidConsume(bytes_to_copy);
+ if (bytes_to_copy == data->size()) {
+ // Consumed all data from this buffer
+ read_buffer_.pop_front();
+ } else {
+ data->DidConsume(bytes_to_copy);
+ }
+ }
+
+ return user_buffer_->BytesConsumed();
+}
+
+int SpdyProxyClientSocket::Write(IOBuffer* buf, int buf_len,
+ CompletionCallback* callback) {
+ DCHECK(!write_callback_);
+ if (!spdy_stream_)
+ return ERR_CONNECTION_CLOSED;
+
+ write_bytes_outstanding_= buf_len;
+ if (buf_len <= kMaxSpdyFrameChunkSize) {
+ int rv = spdy_stream_->WriteStreamData(buf, buf_len, spdy::DATA_FLAG_NONE);
+ if (rv == ERR_IO_PENDING) {
+ write_callback_ = callback;
+ write_buffer_len_ = buf_len;
+ }
+ return rv;
+ }
+
+ // Since a SPDY Data frame can only include kMaxSpdyFrameChunkSize bytes
+ // we need to send multiple data frames
+ for (int i = 0; i < buf_len; i += kMaxSpdyFrameChunkSize) {
+ int len = std::min(kMaxSpdyFrameChunkSize, buf_len - i);
+ scoped_refptr<DrainableIOBuffer> iobuf(new DrainableIOBuffer(buf, i + len));
+ iobuf->SetOffset(i);
+ int rv = spdy_stream_->WriteStreamData(iobuf, len, spdy::DATA_FLAG_NONE);
+ if (rv > 0) {
+ write_bytes_outstanding_ -= rv;
+ } else if (rv != ERR_IO_PENDING) {
+ return rv;
+ }
+ }
+ if (write_bytes_outstanding_ > 0) {
+ write_callback_ = callback;
+ write_buffer_len_ = buf_len;
+ return ERR_IO_PENDING;
+ } else {
+ return buf_len;
+ }
+}
+
+bool SpdyProxyClientSocket::SetReceiveBufferSize(int32 size) {
+ // Since this ClientSocket sits on top of a shared SpdySession, it
+ // is not safe for callers to set change this underlying socket.
+ return false;
+}
+
+bool SpdyProxyClientSocket::SetSendBufferSize(int32 size) {
+ // Since this ClientSocket sits on top of a shared SpdySession, it
+ // is not safe for callers to set change this underlying socket.
+ return false;
+}
+
+int SpdyProxyClientSocket::GetPeerAddress(AddressList* address) const {
+ if (!IsConnected())
+ return ERR_UNEXPECTED;
+ return spdy_stream_->GetPeerAddress(address);
+}
+
+void SpdyProxyClientSocket::OnIOComplete(int result) {
+ DCHECK_NE(STATE_NONE, next_state_);
+ int rv = DoLoop(result);
+ if (rv != ERR_IO_PENDING) {
+ CompletionCallback* c = read_callback_;
+ read_callback_ = NULL;
+ c->Run(rv);
+ }
+}
+
+int SpdyProxyClientSocket::DoLoop(int last_io_result) {
+ DCHECK_NE(next_state_, STATE_NONE);
+ int rv = last_io_result;
+ do {
+ State state = next_state_;
+ next_state_ = STATE_NONE;
+ switch (state) {
+ case STATE_GENERATE_AUTH_TOKEN:
+ DCHECK_EQ(OK, rv);
+ rv = DoGenerateAuthToken();
+ break;
+ case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
+ rv = DoGenerateAuthTokenComplete(rv);
+ break;
+ case STATE_SEND_REQUEST:
+ DCHECK_EQ(OK, rv);
+ net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST,
+ NULL);
+ rv = DoSendRequest();
+ break;
+ case STATE_SEND_REQUEST_COMPLETE:
+ rv = DoSendRequestComplete(rv);
+ net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST,
+ NULL);
+ break;
+ case STATE_READ_REPLY_COMPLETE:
+ rv = DoReadReplyComplete(rv);
+ net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS,
+ NULL);
+ break;
+ default:
+ NOTREACHED() << "bad state";
+ rv = ERR_UNEXPECTED;
+ break;
+ }
+ } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE &&
+ next_state_ != STATE_DONE);
+ return rv;
+}
+
+int SpdyProxyClientSocket::DoGenerateAuthToken() {
+ next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
+ return auth_->MaybeGenerateAuthToken(&request_, &io_callback_, net_log_);
+}
+
+int SpdyProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
+ DCHECK_NE(ERR_IO_PENDING, result);
+ if (result == OK)
+ next_state_ = STATE_SEND_REQUEST;
+ return result;
+}
+
+int SpdyProxyClientSocket::DoSendRequest() {
+ next_state_ = STATE_SEND_REQUEST_COMPLETE;
+
+ // Add Proxy-Authentication header if necessary.
+ HttpRequestHeaders authorization_headers;
+ if (auth_->HaveAuth()) {
+ auth_->AddAuthorizationHeader(&authorization_headers);
+ }
+
+ std::string request_line;
+ HttpRequestHeaders request_headers;
+ BuildTunnelRequest(request_, authorization_headers, endpoint_, &request_line,
+ &request_headers);
+ if (net_log_.IsLoggingAll()) {
+ net_log_.AddEvent(
+ NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
+ new NetLogHttpRequestParameter(
+ request_line, request_headers));
+ }
+
+ request_.extra_headers.MergeFrom(request_headers);
+ linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock());
+ CreateSpdyHeadersFromHttpRequest(request_, headers.get(), true);
+ // Reset the URL to be the endpoint of the connection
+ (*headers)["url"] = endpoint_.ToString();
+ headers->erase("scheme");
+ spdy_stream_->set_spdy_headers(headers);
+
+ return spdy_stream_->SendRequest(true);
+}
+
+int SpdyProxyClientSocket::DoSendRequestComplete(int result) {
+ if (result < 0)
+ return result;
+
+ // Wait for SYN_REPLY frame from the server
+ next_state_ = STATE_READ_REPLY_COMPLETE;
+ return ERR_IO_PENDING;
+}
+
+int SpdyProxyClientSocket::DoReadReplyComplete(int result) {
+ // We enter this method directly from DoSendRequestComplete, since
+ // we are notified by a callback when the SYN_REPLY frame arrives
+
+ if (result < 0)
+ return result;
+
+ next_state_ = STATE_DONE;
+ // Require the "HTTP/1.x" status line for SSL CONNECT.
+ if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0))
+ return ERR_TUNNEL_CONNECTION_FAILED;
+
+ if (net_log_.IsLoggingAll()) {
+ net_log_.AddEvent(
+ NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
+ new NetLogHttpResponseParameter(response_.headers));
+ }
+
+ if (response_.headers->response_code() == 200)
+ return OK;
+ else
+ return ERR_TUNNEL_CONNECTION_FAILED;
+}
+
+// SpdyStream::Delegate methods:
+// Called when SYN frame has been sent.
+// Returns true if no more data to be sent after SYN frame.
+bool SpdyProxyClientSocket::OnSendHeadersComplete(int status) {
+ DCHECK_EQ(next_state_, STATE_SEND_REQUEST_COMPLETE);
+
+ OnIOComplete(status);
+
+ // We return true here so that we send |spdy_stream_| into
+ // STATE_OPEN (ala WebSockets).
+ return true;
+}
+
+int SpdyProxyClientSocket::OnSendBody() {
+ // Because we use |spdy_stream_| via STATE_OPEN (ala WebSockets)
+ // OnSendBody() should never be called.
+ NOTREACHED();
+ return ERR_UNEXPECTED;
+}
+
+bool SpdyProxyClientSocket::OnSendBodyComplete(int status) {
+ // Because we use |spdy_stream_| via STATE_OPEN (ala WebSockets)
+ // OnSendBodyComplete() should never be called.
+ NOTREACHED();
+ return false;
+}
+
+int SpdyProxyClientSocket::OnResponseReceived(
+ const spdy::SpdyHeaderBlock& response,
+ base::Time response_time,
+ int status) {
+ // Save the response
+ SpdyHeadersToHttpResponse(response, &response_);
+
+ DCHECK_EQ(next_state_, STATE_READ_REPLY_COMPLETE);
+
+ OnIOComplete(status);
+
+ return OK;
+}
+
+// Called when data is received.
+void SpdyProxyClientSocket::OnDataReceived(const char* data, int length) {
+ if (length > 0) {
+ // Save the received data.
+ scoped_refptr<IOBuffer> io_buffer = new IOBuffer(length);
+ memcpy(io_buffer->data(), data, length);
+ read_buffer_.push_back(new DrainableIOBuffer(io_buffer, length));
+ }
+
+ if (read_callback_) {
+ int rv = PopulateUserReadBuffer();
+ CompletionCallback* c = read_callback_;
+ read_callback_ = NULL;
+ user_buffer_ = NULL;
+ c->Run(rv);
+ }
+}
+
+void SpdyProxyClientSocket::OnDataSent(int length) {
+ DCHECK(write_callback_);
+
+ write_bytes_outstanding_ -= length;
+
+ DCHECK_GE(write_bytes_outstanding_, 0);
+
+ if (write_bytes_outstanding_ == 0) {
+ int rv = write_buffer_len_;
+ write_buffer_len_ = 0;
+ write_bytes_outstanding_ = 0;
+ CompletionCallback* c = write_callback_;
+ write_callback_ = NULL;
+ c->Run(rv);
+ }
+}
+
+void SpdyProxyClientSocket::OnClose(int status) {
+ DCHECK(spdy_stream_);
+ // If we're in the middle of connecting, we need to make sure
+ // we invoke the connect callback.
+ CompletionCallback* connect_callback = NULL;
+ if (next_state_ != STATE_NONE && next_state_ != STATE_DONE) {
+ DCHECK(read_callback_);
+ connect_callback = read_callback_;
+ }
+ was_ever_used_ = spdy_stream_->WasEverUsed();
+ spdy_stream_ = NULL;
+ read_callback_ = NULL;
+ write_callback_ = NULL;
+ user_buffer_ = NULL;
+ read_buffer_.empty();
+ if (connect_callback)
+ connect_callback->Run(status);
+}
+
+} // namespace net
diff --git a/net/spdy/spdy_proxy_client_socket.h b/net/spdy/spdy_proxy_client_socket.h
new file mode 100644
index 0000000..30062b0
--- /dev/null
+++ b/net/spdy/spdy_proxy_client_socket.h
@@ -0,0 +1,183 @@
+// Copyright (c) 2010 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_PROXY_CLIENT_SOCKET_H_
+#define NET_SPDY_SPDY_PROXY_CLIENT_SOCKET_H_
+#pragma once
+
+#include <string>
+#include <list>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "net/base/completion_callback.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/net_log.h"
+#include "net/http/http_auth_controller.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_request_info.h"
+#include "net/http/http_response_info.h"
+#include "net/socket/client_socket.h"
+#include "net/spdy/spdy_protocol.h"
+#include "net/spdy/spdy_session.h"
+#include "net/spdy/spdy_stream.h"
+
+
+class GURL;
+
+namespace net {
+
+class AddressList;
+class ClientSocketHandle;
+class HttpStream;
+class IOBuffer;
+class SpdySession;
+class SpdyStream;
+
+class SpdyProxyClientSocket : public ClientSocket, public SpdyStream::Delegate {
+ public:
+ // Create a socket on top of the |spdy_stream| by sending a SYN_STREAM
+ // CONNECT frame for |endpoint|. After the SYN_REPLY is received,
+ // any data read/written to the socket will be transferred in data
+ // frames.
+ SpdyProxyClientSocket(SpdyStream* spdy_stream,
+ const std::string& user_agent,
+ const HostPortPair& endpoint,
+ const GURL& url,
+ const HostPortPair& proxy_server,
+ HttpAuthCache* auth_cache,
+ HttpAuthHandlerFactory* auth_handler_factory);
+
+
+ // On destruction Disconnect() is called.
+ virtual ~SpdyProxyClientSocket();
+
+ const scoped_refptr<HttpAuthController>& auth_controller() {
+ return auth_;
+ }
+
+ const HttpResponseInfo* GetConnectResponseInfo() const {
+ return response_.headers ? &response_ : NULL;
+ }
+
+ // ClientSocket methods:
+
+ virtual int Connect(CompletionCallback* callback);
+ virtual void Disconnect();
+ virtual bool IsConnected() const;
+ virtual bool IsConnectedAndIdle() const;
+ virtual const BoundNetLog& NetLog() const { return net_log_; }
+ virtual void SetSubresourceSpeculation();
+ virtual void SetOmniboxSpeculation();
+ virtual bool WasEverUsed() const;
+
+ // Socket methods:
+
+ virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback);
+ virtual int Write(IOBuffer* buf, int buf_len, CompletionCallback* callback);
+
+ virtual bool SetReceiveBufferSize(int32 size);
+ virtual bool SetSendBufferSize(int32 size);
+
+ virtual int GetPeerAddress(AddressList* address) const;
+
+ // SpdyStream::Delegate methods:
+
+ // Called when SYN frame has been sent.
+ // Returns true if no more data to be sent after SYN frame.
+ virtual bool OnSendHeadersComplete(int status);
+
+ // Called when stream is ready to send data.
+ // Returns network error code. OK when it successfully sent data.
+ virtual int OnSendBody();
+
+ // 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);
+
+ // Called when SYN_STREAM or SYN_REPLY received. |status| indicates network
+ // error. Returns network error code.
+ virtual int OnResponseReceived(const spdy::SpdyHeaderBlock& response,
+ base::Time response_time,
+ int status);
+
+ // Called when data is received.
+ virtual void OnDataReceived(const char* data, int length);
+
+ // Called when data is sent.
+ virtual void OnDataSent(int length);
+
+ // Called when SpdyStream is closed.
+ virtual void OnClose(int status);
+
+ private:
+ enum State {
+ STATE_NONE,
+ STATE_GENERATE_AUTH_TOKEN,
+ STATE_GENERATE_AUTH_TOKEN_COMPLETE,
+ STATE_SEND_REQUEST,
+ STATE_SEND_REQUEST_COMPLETE,
+ STATE_READ_REPLY_COMPLETE,
+ STATE_DONE,
+ };
+
+ void OnIOComplete(int result);
+
+ int DoLoop(int last_io_result);
+ int DoGenerateAuthToken();
+ int DoGenerateAuthTokenComplete(int result);
+ int DoSendRequest();
+ int DoSendRequestComplete(int result);
+ int DoReadReplyComplete(int result);
+
+ // Populates |user_buffer_| with as much read data as possible
+ // and returns the number of bytes read.
+ int PopulateUserReadBuffer();
+
+ CompletionCallbackImpl<SpdyProxyClientSocket> io_callback_;
+ State next_state_;
+
+ // Pointer to the SPDY Stream that this sits on top of.
+ scoped_refptr<SpdyStream> spdy_stream_;
+
+ // Stores the callback to the layer above, called on completing Read() or
+ // Connect().
+ CompletionCallback* read_callback_;
+ // Stores the callback to the layer above, called on completing Write().
+ CompletionCallback* write_callback_;
+
+ // CONNECT request and response.
+ HttpRequestInfo request_;
+ HttpResponseInfo response_;
+
+ // The hostname and port of the endpoint. This is not necessarily the one
+ // specified by the URL, due to Alternate-Protocol or fixed testing ports.
+ const HostPortPair endpoint_;
+ scoped_refptr<HttpAuthController> auth_;
+
+ // We buffer the response body as it arrives asynchronously from the stream.
+ std::list<scoped_refptr<DrainableIOBuffer> > read_buffer_;
+
+ // User provided buffer for the Read() response.
+ scoped_refptr<DrainableIOBuffer> user_buffer_;
+
+ // User specified number of bytes to be written.
+ int write_buffer_len_;
+ // Number of bytes written which have not been confirmed
+ int write_bytes_outstanding_;
+
+ // True if read has ever returned zero for eof.
+ bool eof_has_been_read_;
+ // True if the transport socket has ever sent data.
+ bool was_ever_used_;
+
+ const BoundNetLog net_log_;
+
+ DISALLOW_COPY_AND_ASSIGN(SpdyProxyClientSocket);
+};
+
+} // namespace net
+
+#endif // NET_SPDY_SPDY_PROXY_CLIENT_SOCKET_H_
diff --git a/net/spdy/spdy_proxy_client_socket_unittest.cc b/net/spdy/spdy_proxy_client_socket_unittest.cc
new file mode 100644
index 0000000..edde8a5
--- /dev/null
+++ b/net/spdy/spdy_proxy_client_socket_unittest.cc
@@ -0,0 +1,894 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/spdy/spdy_proxy_client_socket.h"
+
+#include "base/utf_string_conversions.h"
+#include "net/base/address_list.h"
+#include "net/base/net_log.h"
+#include "net/base/net_log_unittest.h"
+#include "net/base/mock_host_resolver.h"
+#include "net/base/test_completion_callback.h"
+#include "net/base/winsock_init.h"
+#include "net/http/http_response_info.h"
+#include "net/http/http_response_headers.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/tcp_client_socket.h"
+#include "net/socket/socket_test_util.h"
+#include "net/spdy/spdy_protocol.h"
+#include "net/spdy/spdy_session_pool.h"
+#include "net/spdy/spdy_test_util.h"
+#include "testing/platform_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+static const char kUrl[] = "https://www.google.com/";
+static const char kOriginHost[] = "www.google.com";
+static const int kOriginPort = 443;
+static const char kOriginHostPort[] = "www.google.com:443";
+static const char kProxyUrl[] = "http://myproxy:6121/";
+static const char kProxyHost[] = "myproxy";
+static const int kProxyPort = 6121;
+static const char kUserAgent[] = "Mozilla/1.0";
+
+static const int kStreamId = 1;
+
+static const char kMsg1[] = "\0hello!\xff";
+static const int kLen1 = 8;
+static const char kMsg2[] = "\012345678\0";
+static const int kLen2 = 10;
+static const char kMsg3[] = "bye!";
+static const int kLen3 = 4;
+static const char kMsg33[] = "bye!bye!";
+static const int kLen33 = kLen3 + kLen3;
+
+} // anonymous namespace
+
+namespace net {
+
+class SpdyProxyClientSocketTest : public PlatformTest {
+ public:
+ SpdyProxyClientSocketTest();
+
+ virtual void TearDown();
+
+ protected:
+ void Initialize(MockRead* reads, size_t reads_count, MockWrite* writes,
+ size_t writes_count);
+ spdy::SpdyFrame* ConstructConnectRequestFrame();
+ spdy::SpdyFrame* ConstructConnectAuthRequestFrame();
+ spdy::SpdyFrame* ConstructConnectReplyFrame();
+ spdy::SpdyFrame* ConstructConnectAuthReplyFrame();
+ spdy::SpdyFrame* ConstructConnectErrorReplyFrame();
+ spdy::SpdyFrame* ConstructBodyFrame(const char* data, int length);
+ scoped_refptr<IOBufferWithSize> CreateBuffer(const char* data, int size);
+ void AssertConnectSucceeds();
+ void AssertConnectionEstablished();
+ void AssertSyncReadEquals(const char* data, int len);
+ void AssertAsyncReadEquals(const char* data, int len);
+ void AssertAsyncWriteSucceeds(const char* data, int len);
+ void AssertAsyncWriteWithReadsSucceeds(const char* data, int len,
+ int num_reads);
+
+ void AddAuthToCache() {
+ const string16 kFoo(ASCIIToUTF16("foo"));
+ const string16 kBar(ASCIIToUTF16("bar"));
+ session_->auth_cache()->Add(GURL(kProxyUrl), "MyRealm1", "Basic",
+ "Basic realm=MyRealm1", kFoo, kBar, "/");
+ }
+
+ scoped_ptr<SpdyProxyClientSocket> sock_;
+ TestCompletionCallback callback_;
+
+ private:
+ scoped_refptr<HttpNetworkSession> session_;
+ scoped_refptr<OrderedSocketData> data_;
+ SpdySessionDependencies session_deps_;
+ MockConnect connect_data_;
+ scoped_refptr<SpdySession> spdy_session_;
+ scoped_refptr<SpdyStream> spdy_stream_;
+ spdy::SpdyFramer framer_;
+
+ std::string user_agent_;
+ GURL url_;
+ HostPortPair proxy_host_port_;
+ HostPortPair endpoint_host_port_pair_;
+ ProxyServer proxy_;
+ HostPortProxyPair endpoint_host_port_proxy_pair_;
+ scoped_refptr<TCPSocketParams> tcp_params_;
+
+ DISALLOW_COPY_AND_ASSIGN(SpdyProxyClientSocketTest);
+};
+
+SpdyProxyClientSocketTest::SpdyProxyClientSocketTest()
+ : sock_(NULL),
+ callback_(),
+ session_(NULL),
+ data_(NULL),
+ session_deps_(),
+ connect_data_(false, OK),
+ spdy_session_(NULL),
+ spdy_stream_(NULL),
+ framer_(),
+ user_agent_(kUserAgent),
+ url_(kUrl),
+ proxy_host_port_(kProxyHost, kProxyPort),
+ endpoint_host_port_pair_(kOriginHost, kOriginPort),
+ proxy_(ProxyServer::SCHEME_HTTPS, proxy_host_port_),
+ endpoint_host_port_proxy_pair_(endpoint_host_port_pair_, proxy_),
+ tcp_params_(new TCPSocketParams(proxy_host_port_, LOWEST, url_, false)) {
+}
+
+void SpdyProxyClientSocketTest::TearDown() {
+ if (session_ != NULL)
+ session_->spdy_session_pool()->CloseAllSessions();
+
+ spdy::SpdyFramer::set_enable_compression_default(true);
+ // Empty the current queue.
+ MessageLoop::current()->RunAllPending();
+ PlatformTest::TearDown();
+}
+
+void SpdyProxyClientSocketTest::Initialize(MockRead* reads,
+ size_t reads_count,
+ MockWrite* writes,
+ size_t writes_count) {
+ data_ = new OrderedSocketData(reads, reads_count, writes, writes_count);
+ data_->set_connect_data(connect_data_);
+
+ session_deps_.socket_factory->AddSocketDataProvider(data_.get());
+ session_deps_.host_resolver->set_synchronous_mode(true);
+
+ session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
+ SpdySession::SetSSLMode(false);
+ spdy::SpdyFramer::set_enable_compression_default(false);
+
+ // Creates a new spdy session
+ spdy_session_ =
+ session_->spdy_session_pool()->Get(endpoint_host_port_proxy_pair_,
+ session_->mutable_spdy_settings(),
+ BoundNetLog());
+
+ // Perform the TCP connect
+ scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle);
+ EXPECT_EQ(OK,
+ connection->Init(endpoint_host_port_pair_.ToString(), tcp_params_,
+ LOWEST, NULL, session_->tcp_socket_pool(),
+ BoundNetLog()));
+ spdy_session_->InitializeWithSocket(connection.release(), false, OK);
+
+ // Create the SPDY Stream
+ ASSERT_EQ(
+ OK,
+ spdy_session_->CreateStream(url_, LOWEST, &spdy_stream_, BoundNetLog(),
+ NULL));
+
+ // Create the SpdyProxyClientSocket
+ sock_.reset(
+ new SpdyProxyClientSocket(spdy_stream_, user_agent_,
+ endpoint_host_port_pair_, url_,
+ proxy_host_port_, session_->auth_cache(),
+ session_->http_auth_handler_factory()));
+}
+
+scoped_refptr<IOBufferWithSize> SpdyProxyClientSocketTest::CreateBuffer(
+ const char* data, int size) {
+ scoped_refptr<IOBufferWithSize> buf(new IOBufferWithSize(size));
+ memcpy(buf->data(), data, size);
+ return buf;
+}
+
+void SpdyProxyClientSocketTest::AssertConnectSucceeds() {
+ ASSERT_EQ(ERR_IO_PENDING, sock_->Connect(&callback_));
+ ASSERT_EQ(OK, callback_.WaitForResult());
+}
+
+void SpdyProxyClientSocketTest::AssertConnectionEstablished() {
+ const HttpResponseInfo* response = sock_->GetConnectResponseInfo();
+ ASSERT_TRUE(response != NULL);
+ ASSERT_EQ(200, response->headers->response_code());
+ ASSERT_EQ("Connection Established", response->headers->GetStatusText());
+}
+
+void SpdyProxyClientSocketTest::AssertSyncReadEquals(const char* data,
+ int len) {
+ scoped_refptr<IOBuffer> buf(new IOBuffer(len));
+ ASSERT_EQ(len, sock_->Read(buf, len, NULL));
+ ASSERT_EQ(std::string(data, len), std::string(buf->data(), len));
+ ASSERT_TRUE(sock_->IsConnected());
+}
+
+void SpdyProxyClientSocketTest::AssertAsyncReadEquals(const char* data,
+ int len) {
+ // Issue the read, which will be completed asynchronously
+ scoped_refptr<IOBuffer> buf(new IOBuffer(len));
+ ASSERT_EQ(ERR_IO_PENDING, sock_->Read(buf, len, &callback_));
+ EXPECT_TRUE(sock_->IsConnected());
+
+ // Dummy write to un-block the read
+ AssertAsyncWriteSucceeds(kMsg2, kLen2);
+ EXPECT_TRUE(sock_->IsConnected());
+
+ // Now the read will return
+ EXPECT_EQ(len, callback_.WaitForResult());
+ ASSERT_EQ(std::string(data, len), std::string(buf->data(), len));
+}
+
+void SpdyProxyClientSocketTest::AssertAsyncWriteSucceeds(const char* data,
+ int len) {
+ AssertAsyncWriteWithReadsSucceeds(data, len, 0);
+}
+
+void SpdyProxyClientSocketTest::AssertAsyncWriteWithReadsSucceeds(
+ const char* data, int len, int num_reads) {
+ scoped_refptr<IOBufferWithSize> buf(CreateBuffer(data, len));
+
+ TestCompletionCallback callback;
+ EXPECT_EQ(ERR_IO_PENDING, sock_->Write(buf, buf->size(), &callback));
+
+ // Dummy reads to un-block the writes
+ for (int i = 0; i < num_reads; i++) {
+ AssertSyncReadEquals(kMsg2, kLen2);
+ }
+
+ callback.WaitForResult();
+}
+
+// Constructs a standard SPDY SYN_STREAM frame for a CONNECT request.
+spdy::SpdyFrame* SpdyProxyClientSocketTest::ConstructConnectRequestFrame() {
+ const SpdyHeaderInfo kSynStartHeader = {
+ spdy::SYN_STREAM,
+ kStreamId,
+ 0,
+ net::ConvertRequestPriorityToSpdyPriority(LOWEST),
+ spdy::CONTROL_FLAG_NONE,
+ false,
+ spdy::INVALID,
+ NULL,
+ 0,
+ spdy::DATA_FLAG_NONE
+ };
+ const char* const kConnectHeaders[] = {
+ "method", "CONNECT",
+ "url", kOriginHostPort,
+ "host", kOriginHost,
+ "user-agent", kUserAgent,
+ "version", "HTTP/1.1",
+ "proxy-connection", "keep-alive",
+ };
+ return ConstructSpdyPacket(
+ kSynStartHeader, NULL, 0, kConnectHeaders, arraysize(kConnectHeaders)/2);
+}
+
+// Constructs a SPDY SYN_STREAM frame for a CONNECT request which includes
+// Proxy-Authorization headers.
+spdy::SpdyFrame* SpdyProxyClientSocketTest::ConstructConnectAuthRequestFrame() {
+ const SpdyHeaderInfo kSynStartHeader = {
+ spdy::SYN_STREAM,
+ kStreamId,
+ 0,
+ net::ConvertRequestPriorityToSpdyPriority(LOWEST),
+ spdy::CONTROL_FLAG_NONE,
+ false,
+ spdy::INVALID,
+ NULL,
+ 0,
+ spdy::DATA_FLAG_NONE
+ };
+ const char* const kConnectHeaders[] = {
+ "method", "CONNECT",
+ "url", kOriginHostPort,
+ "host", kOriginHost,
+ "user-agent", kUserAgent,
+ "version", "HTTP/1.1",
+ "proxy-authorization", "Basic Zm9vOmJhcg==",
+ "proxy-connection", "keep-alive",
+ };
+ return ConstructSpdyPacket(
+ kSynStartHeader, NULL, 0, kConnectHeaders, arraysize(kConnectHeaders)/2);
+}
+
+// Constructs a standard SPDY SYN_REPLY frame to match the SPDY CONNECT.
+spdy::SpdyFrame* SpdyProxyClientSocketTest::ConstructConnectReplyFrame() {
+ const char* const kStandardReplyHeaders[] = {
+ "status", "200 Connection Established",
+ "version", "HTTP/1.1"
+ };
+ return ConstructSpdyControlFrame(NULL,
+ 0,
+ false,
+ kStreamId,
+ LOWEST,
+ spdy::SYN_REPLY,
+ spdy::CONTROL_FLAG_NONE,
+ kStandardReplyHeaders,
+ arraysize(kStandardReplyHeaders));
+}
+
+// Constructs a standard SPDY SYN_REPLY frame to match the SPDY CONNECT.
+spdy::SpdyFrame* SpdyProxyClientSocketTest::ConstructConnectAuthReplyFrame() {
+ const char* const kStandardReplyHeaders[] = {
+ "status", "407 Proxy Authentication Required",
+ "version", "HTTP/1.1",
+ "proxy-authenticate", "Basic realm=\"MyRealm1\"",
+ };
+
+ return ConstructSpdyControlFrame(NULL,
+ 0,
+ false,
+ kStreamId,
+ LOWEST,
+ spdy::SYN_REPLY,
+ spdy::CONTROL_FLAG_NONE,
+ kStandardReplyHeaders,
+ arraysize(kStandardReplyHeaders));
+}
+
+// Constructs a SPDY SYN_REPLY frame with an HTTP 500 error.
+spdy::SpdyFrame* SpdyProxyClientSocketTest::ConstructConnectErrorReplyFrame() {
+ const char* const kStandardReplyHeaders[] = {
+ "status", "500 Internal Server Error",
+ "version", "HTTP/1.1",
+ };
+
+ return ConstructSpdyControlFrame(NULL,
+ 0,
+ false,
+ kStreamId,
+ LOWEST,
+ spdy::SYN_REPLY,
+ spdy::CONTROL_FLAG_NONE,
+ kStandardReplyHeaders,
+ arraysize(kStandardReplyHeaders));
+}
+
+spdy::SpdyFrame* SpdyProxyClientSocketTest::ConstructBodyFrame(const char* data,
+ int length) {
+ return framer_.CreateDataFrame(kStreamId, data, length, spdy::DATA_FLAG_NONE);
+}
+
+// ----------- Connect
+
+TEST_F(SpdyProxyClientSocketTest, ConnectSendsCorrectRequest) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 0),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ MockRead reads[] = {
+ CreateMockRead(*resp, 1),
+ MockRead(true, 0, 0), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ ASSERT_FALSE(sock_->IsConnected());
+
+ AssertConnectSucceeds();
+
+ AssertConnectionEstablished();
+}
+
+TEST_F(SpdyProxyClientSocketTest, ConnectWithAuthRequested) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 0),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectAuthReplyFrame());
+ MockRead reads[] = {
+ CreateMockRead(*resp, 1),
+ MockRead(true, 0, 0), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ EXPECT_EQ(ERR_IO_PENDING, sock_->Connect(&callback_));
+ EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, callback_.WaitForResult());
+
+ const HttpResponseInfo* response = sock_->GetConnectResponseInfo();
+ ASSERT_TRUE(response != NULL);
+ ASSERT_EQ(407, response->headers->response_code());
+ ASSERT_EQ("Proxy Authentication Required",
+ response->headers->GetStatusText());
+}
+
+TEST_F(SpdyProxyClientSocketTest, ConnectWithAuthCredentials) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectAuthRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 0),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ MockRead reads[] = {
+ CreateMockRead(*resp, 1),
+ MockRead(true, 0, 0), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+ AddAuthToCache();
+
+ EXPECT_EQ(ERR_IO_PENDING, sock_->Connect(&callback_));
+ EXPECT_EQ(OK, callback_.WaitForResult());
+
+ AssertConnectionEstablished();
+}
+
+TEST_F(SpdyProxyClientSocketTest, ConnectFails) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 0),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ MockRead reads[] = {
+ MockRead(true, 0, 0), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ ASSERT_FALSE(sock_->IsConnected());
+
+ ASSERT_EQ(ERR_IO_PENDING, sock_->Connect(&callback_));
+ ASSERT_EQ(ERR_CONNECTION_CLOSED, callback_.WaitForResult());
+
+ ASSERT_FALSE(sock_->IsConnected());
+}
+
+// ----------- WasEverUsed
+
+TEST_F(SpdyProxyClientSocketTest, WasEverUsedReturnsCorrectValues) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 0),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ MockRead reads[] = {
+ CreateMockRead(*resp, 1),
+ MockRead(true, 0, 0), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ EXPECT_FALSE(sock_->WasEverUsed());
+ AssertConnectSucceeds();
+ EXPECT_TRUE(sock_->WasEverUsed());
+ sock_->Disconnect();
+ EXPECT_TRUE(sock_->WasEverUsed());
+}
+
+// ----------- GetPeerAddress
+
+TEST_F(SpdyProxyClientSocketTest, GetPeerAddressReturnsCorrectValues) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 1),
+ MockWrite(true, 0, 3), // EOF
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ MockRead reads[] = {
+ CreateMockRead(*resp, 2),
+ MockRead(false, 0, 4), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ net::AddressList addr;
+ EXPECT_EQ(ERR_UNEXPECTED, sock_->GetPeerAddress(&addr));
+ AssertConnectSucceeds();
+ EXPECT_TRUE(sock_->IsConnected());
+ EXPECT_EQ(OK, sock_->GetPeerAddress(&addr));
+ sock_->Disconnect();
+ EXPECT_EQ(ERR_UNEXPECTED, sock_->GetPeerAddress(&addr));
+}
+
+// ----------- Write
+
+TEST_F(SpdyProxyClientSocketTest, WriteSendsDataInDataFrame) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ scoped_ptr<spdy::SpdyFrame> msg1(ConstructBodyFrame(kMsg1, kLen1));
+ scoped_ptr<spdy::SpdyFrame> msg2(ConstructBodyFrame(kMsg2, kLen2));
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 0),
+ CreateMockWrite(*msg1, 3),
+ CreateMockWrite(*msg2, 4),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ MockRead reads[] = {
+ CreateMockRead(*resp, 2),
+ MockRead(true, 0, 6), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ AssertConnectSucceeds();
+
+ AssertAsyncWriteSucceeds(kMsg1, kLen1);
+ AssertAsyncWriteSucceeds(kMsg2, kLen2);
+}
+
+// ----------- Read
+
+TEST_F(SpdyProxyClientSocketTest, ReadReadsDataInDataFrame) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 1),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ scoped_ptr<spdy::SpdyFrame> msg1(ConstructBodyFrame(kMsg1, kLen1));
+ scoped_ptr<spdy::SpdyFrame> msg2(ConstructBodyFrame(kMsg2, kLen2));
+ MockRead reads[] = {
+ CreateMockRead(*resp, 2),
+ CreateMockRead(*msg1, 3),
+ CreateMockRead(*msg2, 4),
+ MockRead(true, 0, 6), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ AssertConnectSucceeds();
+
+ AssertSyncReadEquals(kMsg1, kLen1);
+ AssertSyncReadEquals(kMsg2, kLen2);
+}
+
+TEST_F(SpdyProxyClientSocketTest, ReadAuthResponseBody) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 1),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectAuthReplyFrame());
+ scoped_ptr<spdy::SpdyFrame> msg1(ConstructBodyFrame(kMsg1, kLen1));
+ scoped_ptr<spdy::SpdyFrame> msg2(ConstructBodyFrame(kMsg2, kLen2));
+ MockRead reads[] = {
+ CreateMockRead(*resp, 2),
+ CreateMockRead(*msg1, 3),
+ CreateMockRead(*msg2, 4),
+ MockRead(true, 0, 6), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ EXPECT_EQ(ERR_IO_PENDING, sock_->Connect(&callback_));
+ EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, callback_.WaitForResult());
+ // EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED, callback_.WaitForResult());
+
+ AssertSyncReadEquals(kMsg1, kLen1);
+ AssertSyncReadEquals(kMsg2, kLen2);
+}
+
+TEST_F(SpdyProxyClientSocketTest, ReadErrorResponseBody) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 1),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectErrorReplyFrame());
+ scoped_ptr<spdy::SpdyFrame> msg1(ConstructBodyFrame(kMsg1, kLen1));
+ scoped_ptr<spdy::SpdyFrame> msg2(ConstructBodyFrame(kMsg2, kLen2));
+ MockRead reads[] = {
+ CreateMockRead(*resp, 2),
+ CreateMockRead(*msg1, 3),
+ CreateMockRead(*msg2, 4),
+ MockRead(true, 0, 6), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ EXPECT_EQ(ERR_IO_PENDING, sock_->Connect(&callback_));
+ EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, callback_.WaitForResult());
+
+ AssertSyncReadEquals(kMsg1, kLen1);
+ AssertSyncReadEquals(kMsg2, kLen2);
+}
+
+// ----------- Reads and Writes
+
+TEST_F(SpdyProxyClientSocketTest, AsyncReadAroundWrite) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ scoped_ptr<spdy::SpdyFrame> msg2(ConstructBodyFrame(kMsg2, kLen2));
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 1),
+ CreateMockWrite(*msg2, 4), // write to un-cork the read
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ scoped_ptr<spdy::SpdyFrame> msg1(ConstructBodyFrame(kMsg1, kLen1));
+ scoped_ptr<spdy::SpdyFrame> msg3(ConstructBodyFrame(kMsg3, kLen3));
+ MockRead reads[] = {
+ CreateMockRead(*resp, 2),
+ CreateMockRead(*msg1, 3), // sync read
+ CreateMockRead(*msg3, 5, true), // async read
+ MockRead(true, 0, 9), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ AssertConnectSucceeds();
+
+ AssertSyncReadEquals(kMsg1, kLen1);
+
+ AssertAsyncReadEquals(kMsg3, kLen3);
+}
+
+TEST_F(SpdyProxyClientSocketTest, AsyncWriteAroundRead) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ scoped_ptr<spdy::SpdyFrame> msg1(ConstructBodyFrame(kMsg1, kLen1));
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 1),
+ CreateMockWrite(*msg1, 4),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ scoped_ptr<spdy::SpdyFrame> msg2(ConstructBodyFrame(kMsg2, kLen2));
+ MockRead reads[] = {
+ CreateMockRead(*resp, 2),
+ CreateMockRead(*msg2, 3), // sync read (which will un-cork the write)
+ MockRead(true, 0, 5), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ AssertConnectSucceeds();
+
+ AssertAsyncWriteWithReadsSucceeds(kMsg1, kLen1, 1);
+}
+
+TEST_F(SpdyProxyClientSocketTest, Mixed) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ scoped_ptr<spdy::SpdyFrame> msg2(ConstructBodyFrame(kMsg2, kLen2));
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 1),
+ CreateMockWrite(*msg2, 4), // write to un-cork the read
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ scoped_ptr<spdy::SpdyFrame> msg1(ConstructBodyFrame(kMsg1, kLen1));
+ scoped_ptr<spdy::SpdyFrame> msg3(ConstructBodyFrame(kMsg3, kLen3));
+ MockRead reads[] = {
+ CreateMockRead(*resp, 2),
+ CreateMockRead(*msg1, 3), // sync read
+ CreateMockRead(*msg3, 5, true), // async read
+ MockRead(true, 0, 9), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ AssertConnectSucceeds();
+
+ AssertSyncReadEquals(kMsg1, kLen1);
+ AssertAsyncReadEquals(kMsg3, kLen3);
+}
+
+TEST_F(SpdyProxyClientSocketTest, MultipleShortReads) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 1),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ scoped_ptr<spdy::SpdyFrame> msg1(ConstructBodyFrame(kMsg1, kLen1));
+ scoped_ptr<spdy::SpdyFrame> msg3(ConstructBodyFrame(kMsg3, kLen3));
+ MockRead reads[] = {
+ CreateMockRead(*resp, 2),
+ CreateMockRead(*msg1, 3), // sync read
+ CreateMockRead(*msg3, 4), // sync read
+ CreateMockRead(*msg3, 5), // sync read
+ MockRead(true, 0, 8), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ AssertConnectSucceeds();
+
+ AssertSyncReadEquals(kMsg1, kLen1);
+ // The payload from two data frames, each with kMsg3 will be combined
+ // together into a single read().
+ AssertSyncReadEquals(kMsg33, kLen33);
+}
+
+TEST_F(SpdyProxyClientSocketTest, MultipleShortReadsThenMoreRead) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 1),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ scoped_ptr<spdy::SpdyFrame> msg1(ConstructBodyFrame(kMsg1, kLen1));
+ scoped_ptr<spdy::SpdyFrame> msg3(ConstructBodyFrame(kMsg3, kLen3));
+ scoped_ptr<spdy::SpdyFrame> msg2(ConstructBodyFrame(kMsg2, kLen2));
+ MockRead reads[] = {
+ CreateMockRead(*resp, 2),
+ CreateMockRead(*msg1, 3), // sync read
+ CreateMockRead(*msg3, 4), // sync read
+ CreateMockRead(*msg3, 5), // sync read
+ CreateMockRead(*msg2, 5), // sync read
+ MockRead(true, 0, 8), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ AssertConnectSucceeds();
+
+ AssertSyncReadEquals(kMsg1, kLen1);
+ // The payload from two data frames, each with kMsg3 will be combined
+ // together into a single read().
+ AssertSyncReadEquals(kMsg33, kLen33);
+ AssertSyncReadEquals(kMsg2, kLen2);
+}
+
+
+TEST_F(SpdyProxyClientSocketTest, LargeSplitRead) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 1),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ scoped_ptr<spdy::SpdyFrame> msg1(ConstructBodyFrame(kMsg1, kLen1));
+ scoped_ptr<spdy::SpdyFrame> msg33(ConstructBodyFrame(kMsg33, kLen33));
+ scoped_ptr<spdy::SpdyFrame> msg2(ConstructBodyFrame(kMsg2, kLen2));
+ MockRead reads[] = {
+ CreateMockRead(*resp, 2),
+ CreateMockRead(*msg1, 3),
+ CreateMockRead(*msg33, 4),
+ MockRead(true, 0, 8), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ AssertConnectSucceeds();
+
+ AssertSyncReadEquals(kMsg1, kLen1);
+ // The payload from the single large data frame will be read across
+ // two different reads.
+ AssertSyncReadEquals(kMsg3, kLen3);
+ AssertSyncReadEquals(kMsg3, kLen3);
+}
+
+TEST_F(SpdyProxyClientSocketTest, WriteLargeDataSplits) {
+ std::string chunk_data(kMaxSpdyFrameChunkSize, 'x');
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ scoped_ptr<spdy::SpdyFrame> chunk(ConstructBodyFrame(chunk_data.data(),
+ chunk_data.length()));
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 1),
+ CreateMockWrite(*chunk, 3, false),
+ CreateMockWrite(*chunk, 4, false),
+ CreateMockWrite(*chunk, 5, false)
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ MockRead reads[] = {
+ CreateMockRead(*resp, 2),
+ MockRead(true, 0, 7), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ AssertConnectSucceeds();
+
+ std::string big_data(kMaxSpdyFrameChunkSize * 3, 'x');
+ AssertAsyncWriteSucceeds(big_data.data(), big_data.length());
+}
+
+// ----------- Reading/Writing on Closed socket
+
+// Reading from an already closed socket should return 0
+TEST_F(SpdyProxyClientSocketTest, ReadOnClosedSocketReturnsZero) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 1),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ MockRead reads[] = {
+ CreateMockRead(*resp, 2),
+ MockRead(true, 0, 4), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ AssertConnectSucceeds();
+
+ sock_->Disconnect();
+
+ ASSERT_EQ(0, sock_->Read(NULL, 1, NULL));
+ ASSERT_EQ(ERR_CONNECTION_CLOSED, sock_->Read(NULL, 1, NULL));
+ ASSERT_EQ(ERR_CONNECTION_CLOSED, sock_->Read(NULL, 1, NULL));
+}
+
+// Calling Write() on a closed socket is an error
+TEST_F(SpdyProxyClientSocketTest, WriteOnClosedStream) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 1),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ scoped_ptr<spdy::SpdyFrame> msg1(ConstructBodyFrame(kMsg1, kLen1));
+ MockRead reads[] = {
+ CreateMockRead(*resp, 2),
+ MockRead(false, 0, 3), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ AssertConnectSucceeds();
+
+ scoped_refptr<IOBufferWithSize> buf(CreateBuffer(kMsg1, kLen1));
+ EXPECT_EQ(ERR_CONNECTION_CLOSED, sock_->Write(buf, buf->size(), NULL));
+}
+
+// ----------- Pending read/write when closed
+
+// If the socket is closed with a pending Write(), the callback
+// should not be called.
+TEST_F(SpdyProxyClientSocketTest, DisconnectWithWritePending) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 1),
+ MockWrite(true, 0, 3), // EOF
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ MockRead reads[] = {
+ CreateMockRead(*resp, 2),
+ MockRead(false, 0, 4), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ AssertConnectSucceeds();
+
+ EXPECT_TRUE(sock_->IsConnected());
+
+ scoped_refptr<IOBufferWithSize> buf(CreateBuffer(kMsg1, kLen1));
+ EXPECT_EQ(ERR_IO_PENDING, sock_->Write(buf, buf->size(), &callback_));
+
+ sock_->Disconnect();
+
+ EXPECT_FALSE(sock_->IsConnected());
+ EXPECT_FALSE(callback_.have_result());
+}
+
+// If the socket is closed with a pending Read(), the callback
+// should not be called.
+TEST_F(SpdyProxyClientSocketTest, DisconnectWithReadPending) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 1),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ MockRead reads[] = {
+ CreateMockRead(*resp, 2),
+ MockRead(true, 0, 4), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ AssertConnectSucceeds();
+
+ EXPECT_TRUE(sock_->IsConnected());
+
+ scoped_refptr<IOBuffer> buf(new IOBuffer(kLen1));
+ ASSERT_EQ(ERR_IO_PENDING, sock_->Read(buf, kLen1, &callback_));
+
+ sock_->Disconnect();
+
+ EXPECT_FALSE(sock_->IsConnected());
+ EXPECT_FALSE(callback_.have_result());
+}
+
+} // namespace net
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index 2894471..49de547 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -158,6 +158,12 @@ class SpdySession : public base::RefCounted<SpdySession>,
return frames_received_ > 0;
}
+ // Returns true if the underlying transport socket ever had any reads or
+ // writes.
+ bool WasEverUsed() const {
+ return connection_->socket()->WasEverUsed();
+ }
+
void set_in_session_pool(bool val) { in_session_pool_ = val; }
// Access to the number of active and pending streams. These are primarily
@@ -169,6 +175,10 @@ class SpdySession : public base::RefCounted<SpdySession>,
const BoundNetLog& net_log() const { return net_log_; }
+ int GetPeerAddress(AddressList* address) const {
+ return connection_->socket()->GetPeerAddress(address);
+ }
+
private:
friend class base::RefCounted<SpdySession>;
FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, GetActivePushStream);
diff --git a/net/spdy/spdy_stream.cc b/net/spdy/spdy_stream.cc
index 88b009d..102d318 100644
--- a/net/spdy/spdy_stream.cc
+++ b/net/spdy/spdy_stream.cc
@@ -205,6 +205,13 @@ void SpdyStream::DecreaseRecvWindowSize(int delta_window_size) {
session_->ResetStream(stream_id_, spdy::FLOW_CONTROL_ERROR);
}
+int SpdyStream::GetPeerAddress(AddressList* address) const {
+ return session_->GetPeerAddress(address);
+}
+
+bool SpdyStream::WasEverUsed() const {
+ return session_->WasEverUsed();
+}
base::Time SpdyStream::GetRequestTime() const {
return request_time_;
diff --git a/net/spdy/spdy_stream.h b/net/spdy/spdy_stream.h
index 1730af5..4e30a60 100644
--- a/net/spdy/spdy_stream.h
+++ b/net/spdy/spdy_stream.h
@@ -21,6 +21,7 @@
namespace net {
+class AddressList;
class SpdySession;
class SSLCertRequestInfo;
class SSLInfo;
@@ -129,6 +130,12 @@ class SpdyStream : public base::RefCounted<SpdyStream> {
// Decreases |send_window_size_| by the given number of bytes.
void DecreaseSendWindowSize(int delta_window_size);
+ int GetPeerAddress(AddressList* 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);