summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-13 22:37:16 +0000
committerrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-13 22:37:16 +0000
commitd9da5fe263e748c75d85816ee6bae46b2b9de9c9 (patch)
treef6dbf611550dc1118fcff2cfe9429ca721a5c1bf /net
parenta691b8874c225cf530396661264a77159db38780 (diff)
downloadchromium_src-d9da5fe263e748c75d85816ee6bae46b2b9de9c9.zip
chromium_src-d9da5fe263e748c75d85816ee6bae46b2b9de9c9.tar.gz
chromium_src-d9da5fe263e748c75d85816ee6bae46b2b9de9c9.tar.bz2
Integrate the SpdyProxyClientSocket into the HttpStreamRequest
to support fetching HTTPS URLS over a SPDY Proxy. BUG=29625 TEST=HttpNetworkTransactionTest.HttpsProxySpdyConnect Review URL: http://codereview.chromium.org/3417010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@62468 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/base/net_error_list.h5
-rw-r--r--net/http/http_network_transaction_unittest.cc212
-rw-r--r--net/http/http_proxy_client_socket_pool.cc114
-rw-r--r--net/http/http_proxy_client_socket_pool.h21
-rw-r--r--net/http/http_proxy_client_socket_pool_unittest.cc416
-rw-r--r--net/http/http_stream_request.cc18
-rw-r--r--net/http/http_stream_request.h2
-rw-r--r--net/socket/socket.h8
-rw-r--r--net/socket/socket_test_util.cc97
-rw-r--r--net/socket/socket_test_util.h61
-rw-r--r--net/socket/ssl_client_socket_pool_unittest.cc2
-rw-r--r--net/spdy/spdy_framer.h2
-rw-r--r--net/spdy/spdy_proxy_client_socket.cc79
-rw-r--r--net/spdy/spdy_proxy_client_socket.h5
-rw-r--r--net/spdy/spdy_proxy_client_socket_unittest.cc130
-rw-r--r--net/spdy/spdy_test_util.cc61
-rw-r--r--net/spdy/spdy_test_util.h18
17 files changed, 913 insertions, 338 deletions
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h
index c1c24b9..018cd77 100644
--- a/net/base/net_error_list.h
+++ b/net/base/net_error_list.h
@@ -174,7 +174,7 @@ NET_ERROR(SSL_UNSAFE_NEGOTIATION, -128)
// The SSL server attempted to use a weak ephemeral Diffie-Hellman key.
NET_ERROR(SSL_WEAK_SERVER_EPHEMERAL_DH_KEY, -129)
-// Could not create a TCP connection to the proxy server. An error occurred
+// Could not create a connection to the proxy server. An error occurred
// either in resolving its name, or in connecting a socket to it.
// Note that this does NOT include failures during the actual "CONNECT" method
// of an HTTP proxy.
@@ -202,6 +202,9 @@ NET_ERROR(SSL_CLIENT_AUTH_PRIVATE_KEY_ACCESS_DENIED, -134)
// The SSL client certificate has no private key.
NET_ERROR(SSL_CLIENT_AUTH_CERT_NO_PRIVATE_KEY, -135)
+// The certificate presented by the HTTPS Proxy was invalid.
+NET_ERROR(PROXY_CERTIFICATE_INVALID, -136)
+
// Certificate error codes
//
// The values of certificate error codes must be consecutive.
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 508cc28..e37eea6 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -1829,6 +1829,218 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyGet) {
EXPECT_EQ(net::kUploadData, response_data);
}
+// Test a SPDY CONNECT through an HTTPS Proxy to an HTTPS (non-SPDY) Server.
+TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyConnectHttps) {
+ // Configure against https proxy server "proxy:70".
+ SessionDependencies session_deps(CreateFixedProxyService("https://proxy:70"));
+ CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+ session_deps.net_log = log.bound().net_log();
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("https://www.google.com/");
+ request.load_flags = 0;
+
+ // CONNECT to www.google.com:443 via SPDY
+ scoped_ptr<spdy::SpdyFrame> connect(ConstructSpdyConnect(NULL, 0, 1));
+ // fetch https://www.google.com/ via HTTP
+
+ const char get[] = "GET / HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n\r\n";
+ scoped_ptr<spdy::SpdyFrame> wrapped_get(
+ ConstructSpdyBodyFrame(1, get, strlen(get), false));
+ MockWrite spdy_writes[] = {
+ CreateMockWrite(*connect, 1),
+ CreateMockWrite(*wrapped_get, 3)
+ };
+
+ scoped_ptr<spdy::SpdyFrame> conn_resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+ const char resp[] = "HTTP/1.1 200 OK\r\n"
+ "Content-Length: 10\r\n\r\n";
+
+ scoped_ptr<spdy::SpdyFrame> wrapped_get_resp(
+ ConstructSpdyBodyFrame(1, resp, strlen(resp), false));
+ scoped_ptr<spdy::SpdyFrame> wrapped_body(
+ ConstructSpdyBodyFrame(1, "1234567890", 10, false));
+ MockRead spdy_reads[] = {
+ CreateMockRead(*conn_resp, 2, true),
+ CreateMockRead(*wrapped_get_resp, 4, true),
+ CreateMockRead(*wrapped_body, 5, true),
+ CreateMockRead(*wrapped_body, 6, true),
+ MockRead(true, 0, 7),
+ };
+
+ scoped_refptr<OrderedSocketData> spdy_data(
+ new OrderedSocketData(
+ spdy_reads, arraysize(spdy_reads),
+ spdy_writes, arraysize(spdy_writes)));
+ session_deps.socket_factory.AddSocketDataProvider(spdy_data);
+
+ SSLSocketDataProvider ssl(true, OK);
+ ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
+ ssl.next_proto = "spdy/2";
+ ssl.was_npn_negotiated = true;
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
+ SSLSocketDataProvider ssl2(true, OK);
+ ssl2.was_npn_negotiated = false;
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl2);
+
+ TestCompletionCallback callback1;
+
+ int rv = trans->Start(&request, &callback1, log.bound());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+ ASSERT_TRUE(response->headers != NULL);
+ EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+ std::string response_data;
+ ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+ EXPECT_EQ("1234567890", response_data);
+}
+
+// Test a SPDY CONNECT through an HTTPS Proxy to a SPDY server.
+TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyConnectSpdy) {
+ // Configure against https proxy server "proxy:70".
+ SessionDependencies session_deps(CreateFixedProxyService("https://proxy:70"));
+ CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+ session_deps.net_log = log.bound().net_log();
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("https://www.google.com/");
+ request.load_flags = 0;
+
+ // CONNECT to www.google.com:443 via SPDY
+ scoped_ptr<spdy::SpdyFrame> connect(ConstructSpdyConnect(NULL, 0, 1));
+ // fetch https://www.google.com/ via SPDY
+ const char* const kMyUrl = "https://www.google.com/";
+ scoped_ptr<spdy::SpdyFrame> get(ConstructSpdyGet(kMyUrl, false, 1, LOWEST));
+ scoped_ptr<spdy::SpdyFrame> wrapped_get(ConstructWrappedSpdyFrame(get, 1));
+ MockWrite spdy_writes[] = {
+ CreateMockWrite(*connect, 1),
+ CreateMockWrite(*wrapped_get, 3)
+ };
+
+ scoped_ptr<spdy::SpdyFrame> conn_resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+ scoped_ptr<spdy::SpdyFrame> get_resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+ scoped_ptr<spdy::SpdyFrame> wrapped_get_resp(
+ ConstructWrappedSpdyFrame(get_resp, 1));
+ scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+ scoped_ptr<spdy::SpdyFrame> wrapped_body(ConstructWrappedSpdyFrame(body, 1));
+ MockRead spdy_reads[] = {
+ CreateMockRead(*conn_resp, 2, true),
+ CreateMockRead(*wrapped_get_resp, 4, true),
+ CreateMockRead(*wrapped_body, 5, true),
+ MockRead(true, 0, 1),
+ };
+
+ scoped_refptr<OrderedSocketData> spdy_data(
+ new OrderedSocketData(
+ spdy_reads, arraysize(spdy_reads),
+ spdy_writes, arraysize(spdy_writes)));
+ session_deps.socket_factory.AddSocketDataProvider(spdy_data);
+
+ SSLSocketDataProvider ssl(true, OK);
+ ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
+ ssl.next_proto = "spdy/2";
+ ssl.was_npn_negotiated = true;
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
+ SSLSocketDataProvider ssl2(true, OK);
+ ssl2.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
+ ssl2.next_proto = "spdy/2";
+ ssl2.was_npn_negotiated = true;
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl2);
+
+ TestCompletionCallback callback1;
+
+ int rv = trans->Start(&request, &callback1, log.bound());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+ ASSERT_TRUE(response->headers != NULL);
+ EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+ std::string response_data;
+ ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+ EXPECT_EQ(net::kUploadData, response_data);
+}
+
+// Test a SPDY CONNECT failure through an HTTPS Proxy.
+TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyConnectFailure) {
+ // Configure against https proxy server "proxy:70".
+ SessionDependencies session_deps(CreateFixedProxyService("https://proxy:70"));
+ CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+ session_deps.net_log = log.bound().net_log();
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("https://www.google.com/");
+ request.load_flags = 0;
+
+ // CONNECT to www.google.com:443 via SPDY
+ scoped_ptr<spdy::SpdyFrame> connect(ConstructSpdyConnect(NULL, 0, 1));
+ scoped_ptr<spdy::SpdyFrame> get(ConstructSpdyRstStream(1, spdy::CANCEL));
+
+ MockWrite spdy_writes[] = {
+ CreateMockWrite(*connect, 1),
+ CreateMockWrite(*get, 3),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdySynReplyError(1));
+ scoped_ptr<spdy::SpdyFrame> data(ConstructSpdyBodyFrame(1, true));
+ MockRead spdy_reads[] = {
+ CreateMockRead(*resp, 2, true),
+ MockRead(true, 0, 4),
+ };
+
+ scoped_refptr<OrderedSocketData> spdy_data(
+ new OrderedSocketData(
+ spdy_reads, arraysize(spdy_reads),
+ spdy_writes, arraysize(spdy_writes)));
+ session_deps.socket_factory.AddSocketDataProvider(spdy_data);
+
+ SSLSocketDataProvider ssl(true, OK);
+ ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
+ ssl.next_proto = "spdy/2";
+ ssl.was_npn_negotiated = true;
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
+ SSLSocketDataProvider ssl2(true, OK);
+ ssl2.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
+ ssl2.next_proto = "spdy/2";
+ ssl2.was_npn_negotiated = true;
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl2);
+
+ TestCompletionCallback callback1;
+
+ int rv = trans->Start(&request, &callback1, log.bound());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response == NULL);
+}
+
// Test the challenge-response-retry sequence through an HTTPS Proxy
TEST_F(HttpNetworkTransactionTest, HttpsProxyAuthRetry) {
// Configure against https proxy server "proxy:70".
diff --git a/net/http/http_proxy_client_socket_pool.cc b/net/http/http_proxy_client_socket_pool.cc
index 4a5495b..a5bbb53 100644
--- a/net/http/http_proxy_client_socket_pool.cc
+++ b/net/http/http_proxy_client_socket_pool.cc
@@ -19,6 +19,11 @@
#include "net/socket/ssl_client_socket.h"
#include "net/socket/ssl_client_socket_pool.h"
#include "net/socket/tcp_client_socket_pool.h"
+#include "net/spdy/spdy_proxy_client_socket.h"
+#include "net/spdy/spdy_session.h"
+#include "net/spdy/spdy_session_pool.h"
+#include "net/spdy/spdy_settings_storage.h"
+#include "net/spdy/spdy_stream.h"
namespace net {
@@ -30,9 +35,13 @@ HttpProxySocketParams::HttpProxySocketParams(
HostPortPair endpoint,
HttpAuthCache* http_auth_cache,
HttpAuthHandlerFactory* http_auth_handler_factory,
+ SpdySessionPool* spdy_session_pool,
+ SpdySettingsStorage* spdy_settings,
bool tunnel)
: tcp_params_(tcp_params),
ssl_params_(ssl_params),
+ spdy_session_pool_(spdy_session_pool),
+ spdy_settings_(spdy_settings),
request_url_(request_url),
user_agent_(user_agent),
endpoint_(endpoint),
@@ -87,6 +96,8 @@ LoadState HttpProxyConnectJob::GetLoadState() const {
return transport_socket_handle_->GetLoadState();
case STATE_HTTP_PROXY_CONNECT:
case STATE_HTTP_PROXY_CONNECT_COMPLETE:
+ case STATE_SPDY_PROXY_CREATE_STREAM:
+ case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE:
return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL;
default:
NOTREACHED();
@@ -137,6 +148,13 @@ int HttpProxyConnectJob::DoLoop(int result) {
case STATE_HTTP_PROXY_CONNECT_COMPLETE:
rv = DoHttpProxyConnectComplete(rv);
break;
+ case STATE_SPDY_PROXY_CREATE_STREAM:
+ DCHECK_EQ(OK, rv);
+ rv = DoSpdyProxyCreateStream();
+ break;
+ case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE:
+ rv = DoSpdyProxyCreateStreamComplete(rv);
+ break;
default:
NOTREACHED() << "bad state";
rv = ERR_FAILED;
@@ -165,11 +183,21 @@ int HttpProxyConnectJob::DoTCPConnectComplete(int result) {
// longer to timeout than it should.
ResetTimer(base::TimeDelta::FromSeconds(
kHttpProxyConnectJobTimeoutInSeconds));
+
next_state_ = STATE_HTTP_PROXY_CONNECT;
return result;
}
int HttpProxyConnectJob::DoSSLConnect() {
+ if (params_->tunnel()) {
+ HostPortProxyPair pair(params_->destination().host_port_pair(),
+ ProxyServer::Direct());
+ if (params_->spdy_session_pool()->HasSession(pair)) {
+ using_spdy_ = true;
+ next_state_ = STATE_SPDY_PROXY_CREATE_STREAM;
+ return OK;
+ }
+ }
next_state_ = STATE_SSL_CONNECT_COMPLETE;
transport_socket_handle_.reset(new ClientSocketHandle());
return transport_socket_handle_->Init(
@@ -179,15 +207,21 @@ int HttpProxyConnectJob::DoSSLConnect() {
}
int HttpProxyConnectJob::DoSSLConnectComplete(int result) {
- if (IsCertificateError(result) &&
- params_->ssl_params()->load_flags() & LOAD_IGNORE_ALL_CERT_ERRORS)
- result = OK;
+ // TODO(rch): enable support for client auth to the proxy
+ if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED)
+ return ERR_PROXY_AUTH_UNSUPPORTED;
+ if (IsCertificateError(result)) {
+ if (params_->ssl_params()->load_flags() & LOAD_IGNORE_ALL_CERT_ERRORS)
+ result = OK;
+ else
+ // TODO(rch): allow the user to deal with proxy cert errors in the
+ // same way as server cert errors.
+ return ERR_PROXY_CERTIFICATE_INVALID;
+ }
if (result < 0) {
- // TODO(eroman): return ERR_PROXY_CONNECTION_FAILED if failed with the
- // TCP connection.
if (transport_socket_handle_->socket())
transport_socket_handle_->socket()->Disconnect();
- return result;
+ return ERR_PROXY_CONNECTION_FAILED;
}
SSLClientSocket* ssl =
@@ -199,10 +233,67 @@ int HttpProxyConnectJob::DoSSLConnectComplete(int result) {
// longer to timeout than it should.
ResetTimer(base::TimeDelta::FromSeconds(
kHttpProxyConnectJobTimeoutInSeconds));
- next_state_ = STATE_HTTP_PROXY_CONNECT;
+ // TODO(rch): If we ever decide to implement a "trusted" SPDY proxy
+ // (one that we speak SPDY over SSL to, but to which we send HTTPS
+ // request directly instead of through CONNECT tunnels, then we
+ // need to add a predicate to this if statement so we fall through
+ // to the else case. (HttpProxyClientSocket currently acts as
+ // a "trusted" SPDY proxy).
+ if (using_spdy_ && params_->tunnel())
+ next_state_ = STATE_SPDY_PROXY_CREATE_STREAM;
+ else
+ next_state_ = STATE_HTTP_PROXY_CONNECT;
return result;
}
+int HttpProxyConnectJob::DoSpdyProxyCreateStream() {
+ DCHECK(using_spdy_);
+ DCHECK(params_->tunnel());
+
+ HostPortProxyPair pair(params_->destination().host_port_pair(),
+ ProxyServer::Direct());
+ SpdySessionPool* spdy_pool = params_->spdy_session_pool();
+ scoped_refptr<SpdySession> spdy_session;
+ // It's possible that a session to the proxy has recently been created
+ if (spdy_pool->HasSession(pair)) {
+ if (transport_socket_handle_->socket())
+ transport_socket_handle_->socket()->Disconnect();
+ transport_socket_handle_->Reset();
+ spdy_session = spdy_pool->Get(pair, params_->spdy_settings(), net_log());
+ } else {
+ // Create a session direct to the proxy itself
+ int rv = spdy_pool->GetSpdySessionFromSocket(
+ pair, params_->spdy_settings(), transport_socket_handle_.release(),
+ net_log(), OK, &spdy_session, /*using_ssl_*/ true);
+ if (rv < 0) {
+ if (transport_socket_handle_->socket())
+ transport_socket_handle_->socket()->Disconnect();
+ return rv;
+ }
+ }
+
+ next_state_ = STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE;
+ return spdy_session->CreateStream(params_->request_url(),
+ params_->destination().priority(),
+ &spdy_stream_, net_log(), &callback_);
+}
+
+int HttpProxyConnectJob::DoSpdyProxyCreateStreamComplete(int result) {
+ if (result < 0)
+ return result;
+
+ next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
+ transport_socket_.reset(
+ new SpdyProxyClientSocket(spdy_stream_,
+ params_->user_agent(),
+ params_->endpoint(),
+ params_->request_url(),
+ params_->destination().host_port_pair(),
+ params_->http_auth_cache(),
+ params_->http_auth_handler_factory()));
+ return transport_socket_->Connect(&callback_);
+}
+
int HttpProxyConnectJob::DoHttpProxyConnect() {
next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
const HostResolver::RequestInfo& tcp_destination = params_->destination();
@@ -219,14 +310,7 @@ int HttpProxyConnectJob::DoHttpProxyConnect() {
params_->http_auth_handler_factory(),
params_->tunnel(),
using_spdy_));
- int result = transport_socket_->Connect(&callback_);
-
- // Clear the circular reference to HttpNetworkSession (|params_| reference
- // HttpNetworkSession, which reference HttpProxyClientSocketPool, which
- // references |this|) here because it is safe to do so now but not at other
- // points. This may cancel this ConnectJob.
- params_ = NULL;
- return result;
+ return transport_socket_->Connect(&callback_);
}
int HttpProxyConnectJob::DoHttpProxyConnectComplete(int result) {
diff --git a/net/http/http_proxy_client_socket_pool.h b/net/http/http_proxy_client_socket_pool.h
index f95a054..a08a573 100644
--- a/net/http/http_proxy_client_socket_pool.h
+++ b/net/http/http_proxy_client_socket_pool.h
@@ -25,6 +25,9 @@ class HttpAuthCache;
class HttpAuthHandlerFactory;
class SSLClientSocketPool;
class SSLSocketParams;
+class SpdySessionPool;
+class SpdySettingsStorage;
+class SpdyStream;
class TCPClientSocketPool;
class TCPSocketParams;
@@ -41,6 +44,8 @@ class HttpProxySocketParams : public base::RefCounted<HttpProxySocketParams> {
HostPortPair endpoint,
HttpAuthCache* http_auth_cache,
HttpAuthHandlerFactory* http_auth_handler_factory,
+ SpdySessionPool* spdy_session_pool,
+ SpdySettingsStorage* spdy_settings,
bool tunnel);
const scoped_refptr<TCPSocketParams>& tcp_params() const {
@@ -56,6 +61,12 @@ class HttpProxySocketParams : public base::RefCounted<HttpProxySocketParams> {
HttpAuthHandlerFactory* http_auth_handler_factory() const {
return http_auth_handler_factory_;
}
+ SpdySessionPool* spdy_session_pool() {
+ return spdy_session_pool_;
+ }
+ SpdySettingsStorage* spdy_settings() {
+ return spdy_settings_;
+ }
const HostResolver::RequestInfo& destination() const;
bool tunnel() const { return tunnel_; }
@@ -65,6 +76,8 @@ class HttpProxySocketParams : public base::RefCounted<HttpProxySocketParams> {
const scoped_refptr<TCPSocketParams> tcp_params_;
const scoped_refptr<SSLSocketParams> ssl_params_;
+ SpdySessionPool* spdy_session_pool_;
+ SpdySettingsStorage* spdy_settings_;
const GURL request_url_;
const std::string user_agent_;
const HostPortPair endpoint_;
@@ -100,6 +113,9 @@ class HttpProxyConnectJob : public ConnectJob {
STATE_SSL_CONNECT_COMPLETE,
STATE_HTTP_PROXY_CONNECT,
STATE_HTTP_PROXY_CONNECT_COMPLETE,
+ STATE_SPDY_PROXY_CREATE_STREAM,
+ STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE,
+ STATE_SPDY_PROXY_CONNECT_COMPLETE,
STATE_NONE,
};
@@ -127,6 +143,9 @@ class HttpProxyConnectJob : public ConnectJob {
int DoHttpProxyConnect();
int DoHttpProxyConnectComplete(int result);
+ int DoSpdyProxyCreateStream();
+ int DoSpdyProxyCreateStreamComplete(int result);
+
scoped_refptr<HttpProxySocketParams> params_;
TCPClientSocketPool* const tcp_pool_;
SSLClientSocketPool* const ssl_pool_;
@@ -138,6 +157,8 @@ class HttpProxyConnectJob : public ConnectJob {
scoped_ptr<ClientSocket> transport_socket_;
bool using_spdy_;
+ scoped_refptr<SpdyStream> spdy_stream_;
+
DISALLOW_COPY_AND_ASSIGN(HttpProxyConnectJob);
};
diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc
index 4fe8873..71485e9 100644
--- a/net/http/http_proxy_client_socket_pool_unittest.cc
+++ b/net/http/http_proxy_client_socket_pool_unittest.cc
@@ -19,7 +19,9 @@
#include "net/socket/client_socket_handle.h"
#include "net/socket/client_socket_pool_histograms.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/gtest/include/gtest/gtest.h"
namespace net {
@@ -28,34 +30,48 @@ namespace {
const int kMaxSockets = 32;
const int kMaxSocketsPerGroup = 6;
+const char * const kAuthHeaders[] = {
+ "proxy-authorization", "Basic Zm9vOmJhcg=="
+};
+const int kAuthHeadersSize = arraysize(kAuthHeaders) / 2;
enum HttpProxyType {
HTTP,
- HTTPS
+ HTTPS,
+ SPDY
};
typedef ::testing::TestWithParam<HttpProxyType> TestWithHttpParam;
+} // namespace
+
class HttpProxyClientSocketPoolTest : public TestWithHttpParam {
protected:
HttpProxyClientSocketPoolTest()
: ssl_config_(),
ignored_tcp_socket_params_(new TCPSocketParams(
- HostPortPair("proxy", 80), MEDIUM, GURL(), false)),
+ HostPortPair("proxy", 80), LOWEST, GURL(), false)),
ignored_ssl_socket_params_(new SSLSocketParams(
ignored_tcp_socket_params_, NULL, NULL, ProxyServer::SCHEME_DIRECT,
- "host", ssl_config_, 0, false, false)),
+ "www.google.com", ssl_config_, 0, false, false)),
tcp_histograms_("MockTCP"),
tcp_socket_pool_(
kMaxSockets, kMaxSocketsPerGroup,
&tcp_histograms_,
- &tcp_client_socket_factory_),
+ &socket_factory_),
ssl_histograms_("MockSSL"),
+ ssl_config_service_(new SSLConfigServiceDefaults),
+ host_resolver_(new MockHostResolver),
ssl_socket_pool_(kMaxSockets, kMaxSocketsPerGroup,
&ssl_histograms_,
- &tcp_client_socket_factory_,
- &tcp_socket_pool_),
- host_resolver_(new MockHostResolver),
+ host_resolver_.get(),
+ NULL /* dnsrr_resolver */,
+ &socket_factory_,
+ &tcp_socket_pool_,
+ NULL,
+ NULL,
+ ssl_config_service_.get(),
+ BoundNetLog().net_log()),
http_auth_handler_factory_(
HttpAuthHandlerFactory::CreateDefault(host_resolver_.get())),
session_(new HttpNetworkSession(host_resolver_.get(),
@@ -68,6 +84,8 @@ class HttpProxyClientSocketPoolTest : public TestWithHttpParam {
NULL,
NULL)),
http_proxy_histograms_("HttpProxyUnitTest"),
+ ssl_data_(NULL),
+ data_(NULL),
pool_(kMaxSockets, kMaxSocketsPerGroup,
&http_proxy_histograms_,
NULL,
@@ -87,7 +105,7 @@ class HttpProxyClientSocketPoolTest : public TestWithHttpParam {
}
scoped_refptr<TCPSocketParams> GetTcpParams() {
- if (GetParam() == HTTPS)
+ if (GetParam() != HTTP)
return scoped_refptr<TCPSocketParams>();
return ignored_tcp_socket_params_;
}
@@ -105,11 +123,13 @@ class HttpProxyClientSocketPoolTest : public TestWithHttpParam {
new HttpProxySocketParams(
GetTcpParams(),
GetSslParams(),
- GURL("http://host/"),
+ GURL(tunnel ? "https://www.google.com/" : "http://www.google.com"),
"",
- HostPortPair("host", 80),
+ HostPortPair("www.google.com", tunnel ? 443 : 80),
session_->auth_cache(),
session_->http_auth_handler_factory(),
+ session_->spdy_session_pool(),
+ session_->mutable_spdy_settings(),
tunnel));
}
@@ -121,227 +141,363 @@ class HttpProxyClientSocketPoolTest : public TestWithHttpParam {
return GetParams(false);
}
+ DeterministicMockClientSocketFactory& socket_factory() {
+ return socket_factory_;
+ }
+
+ void Initialize(bool async, MockRead* reads, size_t reads_count,
+ MockWrite* writes, size_t writes_count,
+ MockRead* spdy_reads, size_t spdy_reads_count,
+ MockWrite* spdy_writes, size_t spdy_writes_count) {
+ if (GetParam() == SPDY)
+ data_ = new DeterministicSocketData(spdy_reads, spdy_reads_count,
+ spdy_writes, spdy_writes_count);
+ else
+ data_ = new DeterministicSocketData(reads, reads_count, writes,
+ writes_count);
+
+ data_->set_connect_data(MockConnect(async, 0));
+ data_->StopAfter(2); // Request / Response
+
+ socket_factory_.AddSocketDataProvider(data_.get());
+
+ if (GetParam() != HTTP) {
+ ssl_data_.reset(new SSLSocketDataProvider(async, OK));
+ if (GetParam() == SPDY) {
+ InitializeSpdySsl();
+ }
+ socket_factory_.AddSSLSocketDataProvider(ssl_data_.get());
+ }
+ }
+
+ void InitializeSpdySsl() {
+ spdy::SpdyFramer::set_enable_compression_default(false);
+ ssl_data_->next_proto_status = SSLClientSocket::kNextProtoNegotiated;
+ ssl_data_->next_proto = "spdy/2";
+ ssl_data_->was_npn_negotiated = true;
+ }
+
+ private:
SSLConfig ssl_config_;
scoped_refptr<TCPSocketParams> ignored_tcp_socket_params_;
scoped_refptr<SSLSocketParams> ignored_ssl_socket_params_;
ClientSocketPoolHistograms tcp_histograms_;
- MockClientSocketFactory tcp_client_socket_factory_;
+ DeterministicMockClientSocketFactory socket_factory_;
MockTCPClientSocketPool tcp_socket_pool_;
ClientSocketPoolHistograms ssl_histograms_;
- MockSSLClientSocketPool ssl_socket_pool_;
-
- MockClientSocketFactory socket_factory_;
+ scoped_refptr<SSLConfigService> ssl_config_service_;
scoped_ptr<HostResolver> host_resolver_;
+ SSLClientSocketPool ssl_socket_pool_;
+
scoped_ptr<HttpAuthHandlerFactory> http_auth_handler_factory_;
scoped_refptr<HttpNetworkSession> session_;
ClientSocketPoolHistograms http_proxy_histograms_;
+
+ protected:
+ scoped_ptr<SSLSocketDataProvider> ssl_data_;
+ scoped_refptr<DeterministicSocketData> data_;
HttpProxyClientSocketPool pool_;
+ ClientSocketHandle handle_;
+ TestCompletionCallback callback_;
};
//-----------------------------------------------------------------------------
-// All tests are run with three different connection types: SPDY after NPN
-// negotiation, SPDY without SSL, and SPDY with SSL.
+// All tests are run with three different proxy types: HTTP, HTTPS (non-SPDY)
+// and SPDY.
INSTANTIATE_TEST_CASE_P(HttpProxyClientSocketPoolTests,
HttpProxyClientSocketPoolTest,
- ::testing::Values(HTTP, HTTPS));
+ ::testing::Values(HTTP, HTTPS, SPDY));
TEST_P(HttpProxyClientSocketPoolTest, NoTunnel) {
- StaticSocketDataProvider data;
- data.set_connect_data(MockConnect(false, 0));
- tcp_client_socket_factory_.AddSocketDataProvider(&data);
+ Initialize(false, NULL, 0, NULL, 0, NULL, 0, NULL, 0);
- ClientSocketHandle handle;
- int rv = handle.Init("a", GetNoTunnelParams(), LOW, NULL, &pool_,
+ int rv = handle_.Init("a", GetNoTunnelParams(), LOW, NULL, &pool_,
BoundNetLog());
EXPECT_EQ(OK, rv);
- EXPECT_TRUE(handle.is_initialized());
- EXPECT_TRUE(handle.socket());
+ EXPECT_TRUE(handle_.is_initialized());
+ ASSERT_TRUE(handle_.socket());
HttpProxyClientSocket* tunnel_socket =
- static_cast<HttpProxyClientSocket*>(handle.socket());
+ static_cast<HttpProxyClientSocket*>(handle_.socket());
EXPECT_TRUE(tunnel_socket->IsConnected());
}
TEST_P(HttpProxyClientSocketPoolTest, NeedAuth) {
MockWrite writes[] = {
- MockWrite("CONNECT host:80 HTTP/1.1\r\n"
- "Host: host\r\n"
- "Proxy-Connection: keep-alive\r\n\r\n"),
+ MockWrite(true, 0, "CONNECT www.google.com:443 HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Proxy-Connection: keep-alive\r\n\r\n"),
};
MockRead reads[] = {
- // No credentials.
- MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"),
- MockRead("Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
- MockRead("Content-Length: 10\r\n\r\n"),
- MockRead("0123456789"),
+ // No credentials.
+ MockRead(true, 1, "HTTP/1.1 407 Proxy Authentication Required\r\n"),
+ MockRead(true, 2, "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
+ MockRead(true, 3, "Content-Length: 10\r\n\r\n"),
+ MockRead(true, 4, "0123456789"),
+ };
+ scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyConnect(NULL, 0, 1));
+ scoped_ptr<spdy::SpdyFrame> rst(ConstructSpdyRstStream(1, spdy::CANCEL));
+ MockWrite spdy_writes[] = {
+ CreateMockWrite(*req, 0, true),
+ CreateMockWrite(*rst, 2, true),
+ };
+ scoped_ptr<spdy::SpdyFrame> resp(
+ ConstructSpdySynReplyError("407 Proxy Authentication Required", 1));
+ MockRead spdy_reads[] = {
+ CreateMockWrite(*resp, 1, true),
+ MockRead(true, 0, 3)
};
- StaticSocketDataProvider data(reads, arraysize(reads), writes,
- arraysize(writes));
- tcp_client_socket_factory_.AddSocketDataProvider(&data);
+ Initialize(false, reads, arraysize(reads), writes, arraysize(writes),
+ spdy_reads, arraysize(spdy_reads), spdy_writes,
+ arraysize(spdy_writes));
- ClientSocketHandle handle;
- TestCompletionCallback callback;
- int rv = handle.Init("a", GetTunnelParams(), LOW, &callback, &pool_,
+ data_->StopAfter(4);
+ int rv = handle_.Init("a", GetTunnelParams(), LOW, &callback_, &pool_,
BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_FALSE(handle.is_initialized());
- EXPECT_FALSE(handle.socket());
-
- EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED, callback.WaitForResult());
- EXPECT_TRUE(handle.is_initialized());
- EXPECT_TRUE(handle.socket());
- HttpProxyClientSocket* tunnel_socket =
- static_cast<HttpProxyClientSocket*>(handle.socket());
- EXPECT_FALSE(tunnel_socket->IsConnected());
+ EXPECT_FALSE(handle_.is_initialized());
+ EXPECT_FALSE(handle_.socket());
+
+ data_->RunFor(4);
+ rv = callback_.WaitForResult();
+ if (GetParam() != SPDY) {
+ EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED, rv);
+ EXPECT_TRUE(handle_.is_initialized());
+ ASSERT_TRUE(handle_.socket());
+ HttpProxyClientSocket* tunnel_socket =
+ static_cast<HttpProxyClientSocket*>(handle_.socket());
+ EXPECT_FALSE(tunnel_socket->IsConnected());
+ EXPECT_FALSE(tunnel_socket->using_spdy());
+ } else {
+ // Proxy auth is not really implemented for SPDY yet
+ EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, rv);
+ EXPECT_FALSE(handle_.is_initialized());
+ EXPECT_FALSE(handle_.socket());
+ }
}
TEST_P(HttpProxyClientSocketPoolTest, HaveAuth) {
+ // It's pretty much impossible to make the SPDY case becave synchronously
+ // so we skip this test for SPDY
+ if (GetParam() == SPDY)
+ return;
MockWrite writes[] = {
- MockWrite(false,
- "CONNECT host:80 HTTP/1.1\r\n"
- "Host: host\r\n"
- "Proxy-Connection: keep-alive\r\n"
- "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
+ MockWrite(false, 0,
+ "CONNECT www.google.com:443 HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Proxy-Connection: keep-alive\r\n"
+ "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
};
MockRead reads[] = {
- MockRead(false, "HTTP/1.1 200 Connection Established\r\n\r\n"),
+ MockRead(false, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"),
};
- StaticSocketDataProvider data(reads, arraysize(reads), writes,
- arraysize(writes));
- data.set_connect_data(MockConnect(false, 0));
- tcp_client_socket_factory_.AddSocketDataProvider(&data);
+ Initialize(false, reads, arraysize(reads), writes, arraysize(writes), NULL, 0,
+ NULL, 0);
AddAuthToCache();
- ClientSocketHandle handle;
- TestCompletionCallback callback;
- int rv = handle.Init("a", GetTunnelParams(), LOW, &callback, &pool_,
+ int rv = handle_.Init("a", GetTunnelParams(), LOW, &callback_, &pool_,
BoundNetLog());
EXPECT_EQ(OK, rv);
- EXPECT_TRUE(handle.is_initialized());
- EXPECT_TRUE(handle.socket());
+ EXPECT_TRUE(handle_.is_initialized());
+ ASSERT_TRUE(handle_.socket());
HttpProxyClientSocket* tunnel_socket =
- static_cast<HttpProxyClientSocket*>(handle.socket());
+ static_cast<HttpProxyClientSocket*>(handle_.socket());
EXPECT_TRUE(tunnel_socket->IsConnected());
}
TEST_P(HttpProxyClientSocketPoolTest, AsyncHaveAuth) {
MockWrite writes[] = {
- MockWrite("CONNECT host:80 HTTP/1.1\r\n"
- "Host: host\r\n"
- "Proxy-Connection: keep-alive\r\n"
- "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
+ MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Proxy-Connection: keep-alive\r\n"
+ "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
};
MockRead reads[] = {
- MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"),
+ MockRead(false, "HTTP/1.1 200 Connection Established\r\n\r\n"),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyConnect(kAuthHeaders,
+ kAuthHeadersSize, 1));
+ MockWrite spdy_writes[] = {
+ CreateMockWrite(*req, 0, true)
+ };
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+ MockRead spdy_reads[] = {
+ CreateMockRead(*resp, 1, true),
+ MockRead(true, 0, 2)
};
- StaticSocketDataProvider data(reads, arraysize(reads), writes,
- arraysize(writes));
- tcp_client_socket_factory_.AddSocketDataProvider(&data);
+ Initialize(false, reads, arraysize(reads), writes, arraysize(writes),
+ spdy_reads, arraysize(spdy_reads), spdy_writes,
+ arraysize(spdy_writes));
AddAuthToCache();
- ClientSocketHandle handle;
- TestCompletionCallback callback;
- int rv = handle.Init("a", GetTunnelParams(), LOW, &callback, &pool_,
+ int rv = handle_.Init("a", GetTunnelParams(), LOW, &callback_, &pool_,
BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_FALSE(handle.is_initialized());
- EXPECT_FALSE(handle.socket());
+ EXPECT_FALSE(handle_.is_initialized());
+ EXPECT_FALSE(handle_.socket());
- EXPECT_EQ(OK, callback.WaitForResult());
- EXPECT_TRUE(handle.is_initialized());
- EXPECT_TRUE(handle.socket());
+ data_->RunFor(2);
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ EXPECT_TRUE(handle_.is_initialized());
+ ASSERT_TRUE(handle_.socket());
HttpProxyClientSocket* tunnel_socket =
- static_cast<HttpProxyClientSocket*>(handle.socket());
+ static_cast<HttpProxyClientSocket*>(handle_.socket());
EXPECT_TRUE(tunnel_socket->IsConnected());
}
TEST_P(HttpProxyClientSocketPoolTest, TCPError) {
- StaticSocketDataProvider data;
- data.set_connect_data(MockConnect(true, ERR_CONNECTION_CLOSED));
+ if (GetParam() == SPDY) return;
+ data_ = new DeterministicSocketData(NULL, 0, NULL, 0);
+ data_->set_connect_data(MockConnect(true, ERR_CONNECTION_CLOSED));
- tcp_client_socket_factory_.AddSocketDataProvider(&data);
+ socket_factory().AddSocketDataProvider(data_.get());
- ClientSocketHandle handle;
- TestCompletionCallback callback;
- int rv = handle.Init("a", GetTunnelParams(), LOW, &callback, &pool_,
+ int rv = handle_.Init("a", GetTunnelParams(), LOW, &callback_, &pool_,
BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_FALSE(handle.is_initialized());
- EXPECT_FALSE(handle.socket());
+ EXPECT_FALSE(handle_.is_initialized());
+ EXPECT_FALSE(handle_.socket());
- if (GetParam() == HTTP)
- EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED, callback.WaitForResult());
- else
- EXPECT_EQ(ERR_CONNECTION_CLOSED, callback.WaitForResult());
+ EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED, callback_.WaitForResult());
- EXPECT_FALSE(handle.is_initialized());
- EXPECT_FALSE(handle.socket());
+ EXPECT_FALSE(handle_.is_initialized());
+ EXPECT_FALSE(handle_.socket());
+}
+
+TEST_P(HttpProxyClientSocketPoolTest, SSLError) {
+ if (GetParam() == HTTP) return;
+ data_ = new DeterministicSocketData(NULL, 0, NULL, 0);
+ data_->set_connect_data(MockConnect(true, OK));
+ socket_factory().AddSocketDataProvider(data_.get());
+
+ ssl_data_.reset(new SSLSocketDataProvider(true,
+ ERR_CERT_AUTHORITY_INVALID));
+ if (GetParam() == SPDY) {
+ InitializeSpdySsl();
+ }
+ socket_factory().AddSSLSocketDataProvider(ssl_data_.get());
+
+ int rv = handle_.Init("a", GetTunnelParams(), LOW, &callback_, &pool_,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_FALSE(handle_.is_initialized());
+ EXPECT_FALSE(handle_.socket());
+
+ EXPECT_EQ(ERR_PROXY_CERTIFICATE_INVALID, callback_.WaitForResult());
+
+ EXPECT_FALSE(handle_.is_initialized());
+ EXPECT_FALSE(handle_.socket());
+}
+
+TEST_P(HttpProxyClientSocketPoolTest, SslClientAuth) {
+ if (GetParam() == HTTP) return;
+ data_ = new DeterministicSocketData(NULL, 0, NULL, 0);
+ data_->set_connect_data(MockConnect(true, OK));
+ socket_factory().AddSocketDataProvider(data_.get());
+
+ ssl_data_.reset(new SSLSocketDataProvider(true,
+ ERR_SSL_CLIENT_AUTH_CERT_NEEDED));
+ if (GetParam() == SPDY) {
+ InitializeSpdySsl();
+ }
+ socket_factory().AddSSLSocketDataProvider(ssl_data_.get());
+
+ int rv = handle_.Init("a", GetTunnelParams(), LOW, &callback_, &pool_,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_FALSE(handle_.is_initialized());
+ EXPECT_FALSE(handle_.socket());
+
+ EXPECT_EQ(ERR_PROXY_AUTH_UNSUPPORTED, callback_.WaitForResult());
+
+ EXPECT_FALSE(handle_.is_initialized());
+ EXPECT_FALSE(handle_.socket());
}
TEST_P(HttpProxyClientSocketPoolTest, TunnelUnexpectedClose) {
MockWrite writes[] = {
- MockWrite("CONNECT host:80 HTTP/1.1\r\n"
- "Host: host\r\n"
- "Proxy-Connection: keep-alive\r\n"
- "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
+ MockWrite(true, 0,
+ "CONNECT www.google.com:443 HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Proxy-Connection: keep-alive\r\n"
+ "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
};
MockRead reads[] = {
- MockRead("HTTP/1.1 200 Conn"),
- MockRead(true, ERR_CONNECTION_CLOSED),
+ MockRead(true, 1, "HTTP/1.1 200 Conn"),
+ MockRead(true, ERR_CONNECTION_CLOSED, 2),
+ };
+ scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyConnect(kAuthHeaders,
+ kAuthHeadersSize, 1));
+ MockWrite spdy_writes[] = {
+ CreateMockWrite(*req, 0, true)
+ };
+ MockRead spdy_reads[] = {
+ MockRead(true, ERR_CONNECTION_CLOSED, 1),
};
- StaticSocketDataProvider data(reads, arraysize(reads), writes,
- arraysize(writes));
- tcp_client_socket_factory_.AddSocketDataProvider(&data);
+ Initialize(false, reads, arraysize(reads), writes, arraysize(writes),
+ spdy_reads, arraysize(spdy_reads), spdy_writes,
+ arraysize(spdy_writes));
AddAuthToCache();
- ClientSocketHandle handle;
- TestCompletionCallback callback;
- int rv = handle.Init("a", GetTunnelParams(), LOW, &callback, &pool_,
+ int rv = handle_.Init("a", GetTunnelParams(), LOW, &callback_, &pool_,
BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_FALSE(handle.is_initialized());
- EXPECT_FALSE(handle.socket());
+ EXPECT_FALSE(handle_.is_initialized());
+ EXPECT_FALSE(handle_.socket());
- EXPECT_EQ(ERR_CONNECTION_CLOSED, callback.WaitForResult());
- EXPECT_FALSE(handle.is_initialized());
- EXPECT_FALSE(handle.socket());
+ data_->RunFor(3);
+ EXPECT_EQ(ERR_CONNECTION_CLOSED, callback_.WaitForResult());
+ EXPECT_FALSE(handle_.is_initialized());
+ EXPECT_FALSE(handle_.socket());
}
TEST_P(HttpProxyClientSocketPoolTest, TunnelSetupError) {
MockWrite writes[] = {
- MockWrite("CONNECT host:80 HTTP/1.1\r\n"
- "Host: host\r\n"
- "Proxy-Connection: keep-alive\r\n"
- "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
+ MockWrite(true, 0,
+ "CONNECT www.google.com:443 HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Proxy-Connection: keep-alive\r\n"
+ "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
};
MockRead reads[] = {
- MockRead("HTTP/1.1 304 Not Modified\r\n\r\n"),
+ MockRead(true, 1, "HTTP/1.1 304 Not Modified\r\n\r\n"),
+ };
+ scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyConnect(kAuthHeaders,
+ kAuthHeadersSize, 1));
+ scoped_ptr<spdy::SpdyFrame> rst(ConstructSpdyRstStream(1, spdy::CANCEL));
+ MockWrite spdy_writes[] = {
+ CreateMockWrite(*req, 0, true),
+ CreateMockWrite(*rst, 2, true),
+ };
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdySynReplyError(1));
+ MockRead spdy_reads[] = {
+ CreateMockRead(*resp, 1, true),
+ MockRead(true, 0, 3),
};
- StaticSocketDataProvider data(reads, arraysize(reads), writes,
- arraysize(writes));
- tcp_client_socket_factory_.AddSocketDataProvider(&data);
+ Initialize(false, reads, arraysize(reads), writes, arraysize(writes),
+ spdy_reads, arraysize(spdy_reads), spdy_writes,
+ arraysize(spdy_writes));
AddAuthToCache();
- ClientSocketHandle handle;
- TestCompletionCallback callback;
- int rv = handle.Init("a", GetTunnelParams(), LOW, &callback, &pool_,
+ int rv = handle_.Init("a", GetTunnelParams(), LOW, &callback_, &pool_,
BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_FALSE(handle.is_initialized());
- EXPECT_FALSE(handle.socket());
+ EXPECT_FALSE(handle_.is_initialized());
+ EXPECT_FALSE(handle_.socket());
+
+ data_->RunFor(2);
- EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, callback.WaitForResult());
- EXPECT_FALSE(handle.is_initialized());
- EXPECT_FALSE(handle.socket());
+ EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, callback_.WaitForResult());
+ EXPECT_FALSE(handle_.is_initialized());
+ EXPECT_FALSE(handle_.socket());
}
// It would be nice to also test the timeouts in HttpProxyClientSocketPool.
-} // namespace
-
} // namespace net
diff --git a/net/http/http_stream_request.cc b/net/http/http_stream_request.cc
index 35832c7..f60a86c 100644
--- a/net/http/http_stream_request.cc
+++ b/net/http/http_stream_request.cc
@@ -460,9 +460,9 @@ int HttpStreamRequest::DoInitConnection() {
}
// Check next if we have a spdy session for this proxy. If so, then go
// straight to using that.
- if (proxy_info()->is_https()) {
+ if (IsHttpsProxyAndHttpUrl()) {
HostPortProxyPair proxy(proxy_info()->proxy_server().host_port_pair(),
- proxy_info()->proxy_server());
+ ProxyServer::Direct());
if (session_->spdy_session_pool()->HasSession(proxy)) {
using_spdy_ = true;
next_state_ = STATE_CREATE_STREAM;
@@ -537,6 +537,8 @@ int HttpStreamRequest::DoInitConnection() {
endpoint_,
session_->auth_cache(),
session_->http_auth_handler_factory(),
+ session_->spdy_session_pool(),
+ session_->mutable_spdy_settings(),
using_ssl_);
} else {
DCHECK(proxy_info()->is_socks());
@@ -725,10 +727,11 @@ int HttpStreamRequest::DoCreateStream() {
// connection, or it might be a SPDY session through an HTTP or HTTPS proxy.
spdy_session =
spdy_pool->Get(pair, session_->mutable_spdy_settings(), net_log_);
- } else if (proxy_info()->is_https()) {
+ } else if (IsHttpsProxyAndHttpUrl()) {
// If we don't have a direct SPDY session, and we're using an HTTPS
// proxy, then we might have a SPDY session to the proxy
- pair = HostPortProxyPair(proxy_server.host_port_pair(), proxy_server);
+ pair = HostPortProxyPair(proxy_server.host_port_pair(),
+ ProxyServer::Direct());
if (spdy_pool->HasSession(pair)) {
spdy_session =
spdy_pool->Get(pair, session_->mutable_spdy_settings(), net_log_);
@@ -751,7 +754,8 @@ int HttpStreamRequest::DoCreateStream() {
if (spdy_session->IsClosed())
return ERR_CONNECTION_CLOSED;
- stream_.reset(new SpdyHttpStream(spdy_session, direct));
+ bool useRelativeUrl = direct || request_info().url.SchemeIs("https");
+ stream_.reset(new SpdyHttpStream(spdy_session, useRelativeUrl));
return OK;
}
@@ -798,6 +802,10 @@ void HttpStreamRequest::SetSocketMotivation() {
// TODO(mbelshe): Add other motivations (like EARLY_LOAD_MOTIVATED).
}
+bool HttpStreamRequest::IsHttpsProxyAndHttpUrl() {
+ return proxy_info()->is_https() && request_info().url.SchemeIs("http");
+}
+
// Returns a newly create SSLSocketParams, and sets several
// fields of ssl_config_.
scoped_refptr<SSLSocketParams> HttpStreamRequest::GenerateSslParams(
diff --git a/net/http/http_stream_request.h b/net/http/http_stream_request.h
index 5a3ccb7..cad0e59 100644
--- a/net/http/http_stream_request.h
+++ b/net/http/http_stream_request.h
@@ -112,6 +112,8 @@ class HttpStreamRequest : public StreamFactory::StreamRequestJob {
// Set the motivation for this request onto the underlying socket.
void SetSocketMotivation();
+ bool IsHttpsProxyAndHttpUrl();
+
// Returns a newly create SSLSocketParams, and sets several
// fields of ssl_config_.
scoped_refptr<SSLSocketParams> GenerateSslParams(
diff --git a/net/socket/socket.h b/net/socket/socket.h
index c3465ea..da17ce4 100644
--- a/net/socket/socket.h
+++ b/net/socket/socket.h
@@ -26,8 +26,8 @@ class Socket {
// case the result will be passed to the callback when available. If the
// operation is not completed immediately, the socket acquires a reference to
// the provided buffer until the callback is invoked or the socket is
- // destroyed. If the socket is closed before the read completes, the callback
- // will not be invoked.
+ // closed. If the socket is Disconnected before the read completes, the
+ // callback will not be invoked.
virtual int Read(IOBuffer* buf, int buf_len,
CompletionCallback* callback) = 0;
@@ -40,9 +40,9 @@ class Socket {
// case the result will be passed to the callback when available. If the
// operation is not completed immediately, the socket acquires a reference to
// the provided buffer until the callback is invoked or the socket is
- // destroyed. Implementations of this method should not modify the contents
+ // closed. Implementations of this method should not modify the contents
// of the actual buffer that is written to the socket. If the socket is
- // closed before the write completes, the callback will not be invoked.
+ // Disconnected before the write completes, the callback will not be invoked.
virtual int Write(IOBuffer* buf, int buf_len,
CompletionCallback* callback) = 0;
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index cba4b1c..414d395 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -127,7 +127,6 @@ void MockClientSocket::GetSSLInfo(net::SSLInfo* ssl_info) {
void MockClientSocket::GetSSLCertRequestInfo(
net::SSLCertRequestInfo* cert_request_info) {
- NOTREACHED();
}
SSLClientSocket::NextProtoStatus
@@ -1235,100 +1234,4 @@ const char kSOCKS5OkResponse[] =
{ 0x05, 0x00, 0x00, 0x01, 127, 0, 0, 1, 0x00, 0x50 };
const int kSOCKS5OkResponseLength = arraysize(kSOCKS5OkResponse);
-MockSSLClientSocketPool::MockSSLClientSocketPool(
- int max_sockets,
- int max_sockets_per_group,
- ClientSocketPoolHistograms* histograms,
- ClientSocketFactory* socket_factory,
- TCPClientSocketPool* tcp_pool)
- : SSLClientSocketPool(max_sockets, max_sockets_per_group, histograms,
- NULL, NULL, socket_factory,
- tcp_pool,
- NULL, NULL, NULL, NULL),
- client_socket_factory_(socket_factory),
- release_count_(0),
- cancel_count_(0) {
-}
-
-int MockSSLClientSocketPool::RequestSocket(const std::string& group_name,
- const void* socket_params,
- RequestPriority priority,
- ClientSocketHandle* handle,
- CompletionCallback* callback,
- const BoundNetLog& net_log) {
- ClientSocket* socket = client_socket_factory_->CreateTCPClientSocket(
- AddressList(), net_log.net_log(), net::NetLog::Source());
- MockConnectJob* job = new MockConnectJob(socket, handle, callback);
- job_list_.push_back(job);
- handle->set_pool_id(1);
- return job->Connect();
-}
-
-void MockSSLClientSocketPool::CancelRequest(const std::string& group_name,
- ClientSocketHandle* handle) {
- std::vector<MockConnectJob*>::iterator i;
- for (i = job_list_.begin(); i != job_list_.end(); ++i) {
- if ((*i)->CancelHandle(handle)) {
- cancel_count_++;
- break;
- }
- }
-}
-
-void MockSSLClientSocketPool::ReleaseSocket(const std::string& group_name,
- ClientSocket* socket, int id) {
- EXPECT_EQ(1, id);
- release_count_++;
- delete socket;
-}
-
-MockSSLClientSocketPool::~MockSSLClientSocketPool() {}
-
-MockSSLClientSocketPool::MockConnectJob::MockConnectJob(
- ClientSocket* socket,
- ClientSocketHandle* handle,
- CompletionCallback* callback)
- : socket_(socket),
- handle_(handle),
- user_callback_(callback),
- ALLOW_THIS_IN_INITIALIZER_LIST(
- connect_callback_(this, &MockConnectJob::OnConnect)) {
-}
-
-int MockSSLClientSocketPool::MockConnectJob::Connect() {
- int rv = socket_->Connect(&connect_callback_);
- if (rv == OK) {
- user_callback_ = NULL;
- OnConnect(OK);
- }
- return rv;
-}
-
-bool MockSSLClientSocketPool::MockConnectJob::CancelHandle(
- const ClientSocketHandle* handle) {
- if (handle != handle_)
- return false;
- socket_.reset();
- handle_ = NULL;
- user_callback_ = NULL;
- return true;
-}
-
-void MockSSLClientSocketPool::MockConnectJob::OnConnect(int rv) {
- if (!socket_.get())
- return;
- if (rv == OK) {
- handle_->set_socket(socket_.release());
- } else {
- socket_.reset();
- }
-
- handle_ = NULL;
-
- if (user_callback_) {
- CompletionCallback* callback = user_callback_;
- user_callback_ = NULL;
- callback->Run(rv);
- }
-}
} // namespace net
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index 7293c05..eb54b84 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -92,6 +92,11 @@ struct MockRead {
result(0), data(data), data_len(data_len), sequence_number(0),
time_stamp(base::Time::Now()) { }
+ // Read success (inferred data length) with sequence information.
+ MockRead(bool async, int seq, const char* data) : async(async),
+ result(0), data(data), data_len(strlen(data)), sequence_number(seq),
+ time_stamp(base::Time::Now()) { }
+
// Read success with sequence information.
MockRead(bool async, const char* data, int data_len, int seq) : async(async),
result(0), data(data), data_len(data_len), sequence_number(seq),
@@ -923,62 +928,6 @@ extern const int kSOCKS5OkRequestLength;
extern const char kSOCKS5OkResponse[];
extern const int kSOCKS5OkResponseLength;
-class MockSSLClientSocketPool : public SSLClientSocketPool {
- public:
- class MockConnectJob {
- public:
- MockConnectJob(ClientSocket* socket, ClientSocketHandle* handle,
- CompletionCallback* callback);
-
- int Connect();
- bool CancelHandle(const ClientSocketHandle* handle);
-
- private:
- void OnConnect(int rv);
-
- scoped_ptr<ClientSocket> socket_;
- ClientSocketHandle* handle_;
- CompletionCallback* user_callback_;
- CompletionCallbackImpl<MockConnectJob> connect_callback_;
-
- DISALLOW_COPY_AND_ASSIGN(MockConnectJob);
- };
-
- MockSSLClientSocketPool(
- int max_sockets,
- int max_sockets_per_group,
- ClientSocketPoolHistograms* histograms,
- ClientSocketFactory* socket_factory,
- TCPClientSocketPool* tcp_pool);
-
- virtual ~MockSSLClientSocketPool();
-
- int release_count() const { return release_count_; }
- int cancel_count() const { return cancel_count_; }
-
- // SSLClientSocketPool methods.
- virtual int RequestSocket(const std::string& group_name,
- const void* socket_params,
- RequestPriority priority,
- ClientSocketHandle* handle,
- CompletionCallback* callback,
- const BoundNetLog& net_log);
-
- virtual void CancelRequest(const std::string& group_name,
- ClientSocketHandle* handle);
- virtual void ReleaseSocket(const std::string& group_name,
- ClientSocket* socket, int id);
-
- private:
- ClientSocketFactory* client_socket_factory_;
- int release_count_;
- int cancel_count_;
- ScopedVector<MockConnectJob> job_list_;
-
- DISALLOW_COPY_AND_ASSIGN(MockSSLClientSocketPool);
-};
-
-
} // namespace net
#endif // NET_SOCKET_SOCKET_TEST_UTIL_H_
diff --git a/net/socket/ssl_client_socket_pool_unittest.cc b/net/socket/ssl_client_socket_pool_unittest.cc
index 2cc564bf..755bd44 100644
--- a/net/socket/ssl_client_socket_pool_unittest.cc
+++ b/net/socket/ssl_client_socket_pool_unittest.cc
@@ -71,6 +71,8 @@ class SSLClientSocketPoolTest : public testing::Test {
HostPortPair("host", 80),
session_->auth_cache(),
session_->http_auth_handler_factory(),
+ session_->spdy_session_pool(),
+ session_->mutable_spdy_settings(),
true)),
http_proxy_histograms_("MockHttpProxy"),
http_proxy_socket_pool_(
diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h
index 00ddac0..9b290cd 100644
--- a/net/spdy/spdy_framer.h
+++ b/net/spdy/spdy_framer.h
@@ -24,6 +24,7 @@
typedef struct z_stream_s z_stream; // Forward declaration for zlib.
namespace net {
+class HttpProxyClientSocketPoolTest;
class HttpNetworkLayer;
class HttpNetworkTransactionTest;
class SpdyHttpStreamTest;
@@ -251,6 +252,7 @@ class SpdyFramer {
FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, UnclosedStreamDataCompressors);
friend class net::HttpNetworkLayer; // This is temporary for the server.
friend class net::HttpNetworkTransactionTest;
+ friend class net::HttpProxyClientSocketPoolTest;
friend class net::SpdyHttpStreamTest;
friend class net::SpdyNetworkTransactionTest;
friend class net::SpdyProxyClientSocketTest;
diff --git a/net/spdy/spdy_proxy_client_socket.cc b/net/spdy/spdy_proxy_client_socket.cc
index 588af81..d36b6306 100644
--- a/net/spdy/spdy_proxy_client_socket.cc
+++ b/net/spdy/spdy_proxy_client_socket.cc
@@ -30,7 +30,7 @@ SpdyProxyClientSocket::SpdyProxyClientSocket(
HttpAuthHandlerFactory* auth_handler_factory)
: ALLOW_THIS_IN_INITIALIZER_LIST(
io_callback_(this, &SpdyProxyClientSocket::OnIOComplete)),
- next_state_(STATE_NONE),
+ next_state_(STATE_DISCONNECTED),
spdy_stream_(spdy_stream),
read_callback_(NULL),
write_callback_(NULL),
@@ -70,10 +70,10 @@ SpdyProxyClientSocket::~SpdyProxyClientSocket() {
// the HTTPS Proxy tunnel failure from an HTTP Proxy tunnel failure.
int SpdyProxyClientSocket::Connect(CompletionCallback* callback) {
DCHECK(!read_callback_);
- if (next_state_ == STATE_DONE)
+ if (next_state_ == STATE_OPEN)
return OK;
- DCHECK_EQ(STATE_NONE, next_state_);
+ DCHECK_EQ(STATE_DISCONNECTED, next_state_);
next_state_ = STATE_GENERATE_AUTH_TOKEN;
int rv = DoLoop(OK);
@@ -83,7 +83,16 @@ int SpdyProxyClientSocket::Connect(CompletionCallback* callback) {
}
void SpdyProxyClientSocket::Disconnect() {
- next_state_ = STATE_NONE;
+ read_buffer_.clear();
+ user_buffer_ = NULL;
+ read_callback_ = NULL;
+
+ write_buffer_len_ = 0;
+ write_bytes_outstanding_ = 0;
+ write_callback_ = NULL;
+
+ next_state_ = STATE_DISCONNECTED;
+
if (spdy_stream_)
// This will cause OnClose to be invoked, which takes care of
// cleaning up all the internal state.
@@ -91,8 +100,7 @@ void SpdyProxyClientSocket::Disconnect() {
}
bool SpdyProxyClientSocket::IsConnected() const {
- return next_state_ == STATE_DONE && spdy_stream_ != NULL &&
- !spdy_stream_->closed();
+ return next_state_ == STATE_OPEN || next_state_ == STATE_CLOSED;
}
bool SpdyProxyClientSocket::IsConnectedAndIdle() const {
@@ -116,14 +124,17 @@ int SpdyProxyClientSocket::Read(IOBuffer* buf, int buf_len,
DCHECK(!read_callback_);
DCHECK(!user_buffer_);
- if (!spdy_stream_) {
+ if (next_state_ == STATE_DISCONNECTED)
+ return ERR_SOCKET_NOT_CONNECTED;
+
+ if (!spdy_stream_ && read_buffer_.empty()) {
if (eof_has_been_read_)
return ERR_CONNECTION_CLOSED;
eof_has_been_read_ = true;
return 0;
}
- DCHECK(next_state_ == STATE_DONE);
+ DCHECK(next_state_ == STATE_OPEN || next_state_ == STATE_CLOSED);
DCHECK(buf);
user_buffer_ = new DrainableIOBuffer(buf, buf_len);
int result = PopulateUserReadBuffer();
@@ -146,7 +157,7 @@ int SpdyProxyClientSocket::PopulateUserReadBuffer() {
data->BytesRemaining());
memcpy(user_buffer_->data(), data->data(), bytes_to_copy);
user_buffer_->DidConsume(bytes_to_copy);
- if (data->BytesRemaining() == 0) {
+ if (data->BytesRemaining() == bytes_to_copy) {
// Consumed all data from this buffer
read_buffer_.pop_front();
} else {
@@ -160,6 +171,9 @@ int SpdyProxyClientSocket::PopulateUserReadBuffer() {
int SpdyProxyClientSocket::Write(IOBuffer* buf, int buf_len,
CompletionCallback* callback) {
DCHECK(!write_callback_);
+ if (next_state_ == STATE_DISCONNECTED)
+ return ERR_SOCKET_NOT_CONNECTED;
+
if (!spdy_stream_)
return ERR_CONNECTION_CLOSED;
@@ -214,7 +228,7 @@ int SpdyProxyClientSocket::GetPeerAddress(AddressList* address) const {
}
void SpdyProxyClientSocket::OnIOComplete(int result) {
- DCHECK_NE(STATE_NONE, next_state_);
+ DCHECK_NE(STATE_DISCONNECTED, next_state_);
int rv = DoLoop(result);
if (rv != ERR_IO_PENDING) {
CompletionCallback* c = read_callback_;
@@ -224,11 +238,11 @@ void SpdyProxyClientSocket::OnIOComplete(int result) {
}
int SpdyProxyClientSocket::DoLoop(int last_io_result) {
- DCHECK_NE(next_state_, STATE_NONE);
+ DCHECK_NE(next_state_, STATE_DISCONNECTED);
int rv = last_io_result;
do {
State state = next_state_;
- next_state_ = STATE_NONE;
+ next_state_ = STATE_DISCONNECTED;
switch (state) {
case STATE_GENERATE_AUTH_TOKEN:
DCHECK_EQ(OK, rv);
@@ -258,8 +272,8 @@ int SpdyProxyClientSocket::DoLoop(int last_io_result) {
rv = ERR_UNEXPECTED;
break;
}
- } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE &&
- next_state_ != STATE_DONE);
+ } while (rv != ERR_IO_PENDING && next_state_ != STATE_DISCONNECTED &&
+ next_state_ != STATE_OPEN);
return rv;
}
@@ -322,11 +336,11 @@ int SpdyProxyClientSocket::DoReadReplyComplete(int result) {
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;
+ next_state_ = STATE_OPEN;
if (net_log_.IsLoggingAll()) {
net_log_.AddEvent(
NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
@@ -417,21 +431,34 @@ void SpdyProxyClientSocket::OnDataSent(int length) {
void SpdyProxyClientSocket::OnClose(int status) {
DCHECK(spdy_stream_);
+ was_ever_used_ = spdy_stream_->WasEverUsed();
+ spdy_stream_ = NULL;
+
+ bool connecting = next_state_ != STATE_DISCONNECTED &&
+ next_state_ < STATE_OPEN;
+ if (next_state_ == STATE_OPEN)
+ next_state_ = STATE_CLOSED;
+ else
+ next_state_ = STATE_DISCONNECTED;
+
+ CompletionCallback* write_callback = write_callback_;
+ write_callback_ = NULL;
+ write_buffer_len_ = 0;
+ write_bytes_outstanding_ = 0;
+
// 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) {
+ if (connecting) {
DCHECK(read_callback_);
- connect_callback = read_callback_;
+ CompletionCallback* read_callback = read_callback_;
+ read_callback_ = NULL;
+ read_callback->Run(status);
+ } else if (read_callback_) {
+ // If we have a read_callback, the we need to make sure we call it back
+ OnDataReceived(NULL, 0);
}
- 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);
+ if (write_callback)
+ write_callback->Run(ERR_CONNECTION_CLOSED);
}
} // namespace net
diff --git a/net/spdy/spdy_proxy_client_socket.h b/net/spdy/spdy_proxy_client_socket.h
index 30062b0..b993b5e 100644
--- a/net/spdy/spdy_proxy_client_socket.h
+++ b/net/spdy/spdy_proxy_client_socket.h
@@ -114,13 +114,14 @@ class SpdyProxyClientSocket : public ClientSocket, public SpdyStream::Delegate {
private:
enum State {
- STATE_NONE,
+ STATE_DISCONNECTED,
STATE_GENERATE_AUTH_TOKEN,
STATE_GENERATE_AUTH_TOKEN_COMPLETE,
STATE_SEND_REQUEST,
STATE_SEND_REQUEST_COMPLETE,
STATE_READ_REPLY_COMPLETE,
- STATE_DONE,
+ STATE_OPEN,
+ STATE_CLOSED
};
void OnIOComplete(int result);
diff --git a/net/spdy/spdy_proxy_client_socket_unittest.cc b/net/spdy/spdy_proxy_client_socket_unittest.cc
index c3df858..9639036 100644
--- a/net/spdy/spdy_proxy_client_socket_unittest.cc
+++ b/net/spdy/spdy_proxy_client_socket_unittest.cc
@@ -942,10 +942,85 @@ TEST_F(SpdyProxyClientSocketTest, ReadOnClosedSocketReturnsZero) {
AssertConnectSucceeds();
+ Run(1);
+
+ 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));
+}
+
+// Read pending when socket is closed should return 0
+TEST_F(SpdyProxyClientSocketTest, PendingReadOnCloseReturnsZero) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 0, false),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ MockRead reads[] = {
+ CreateMockRead(*resp, 1, true),
+ MockRead(true, 0, 2), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ AssertConnectSucceeds();
+
+ AssertReadStarts(kMsg1, kLen1);
+
+ Run(1);
+
+ ASSERT_EQ(0, read_callback_.WaitForResult());
+}
+
+// Reading from a disconnected socket is an error
+TEST_F(SpdyProxyClientSocketTest, ReadOnDisconnectSocketReturnsNotConnected) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 0, false),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ MockRead reads[] = {
+ CreateMockRead(*resp, 1, true),
+ MockRead(true, 0, 2), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ AssertConnectSucceeds();
+
sock_->Disconnect();
+ ASSERT_EQ(ERR_SOCKET_NOT_CONNECTED, sock_->Read(NULL, 1, NULL));
+}
+
+// Reading buffered data from an already closed socket should return
+// buffered data, then 0.
+TEST_F(SpdyProxyClientSocketTest, ReadOnClosedSocketReturnsBufferedData) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 0, false),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ scoped_ptr<spdy::SpdyFrame> msg1(ConstructBodyFrame(kMsg1, kLen1));
+ MockRead reads[] = {
+ CreateMockRead(*resp, 1, true),
+ CreateMockRead(*msg1, 2, true),
+ MockRead(true, 0, 3), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ AssertConnectSucceeds();
+
+ Run(2);
+
+ AssertSyncReadEquals(kMsg1, kLen1);
ASSERT_EQ(0, sock_->Read(NULL, 1, NULL));
ASSERT_EQ(ERR_CONNECTION_CLOSED, sock_->Read(NULL, 1, NULL));
+ // Verify that read *still* returns ERR_CONNECTION_CLOSED
ASSERT_EQ(ERR_CONNECTION_CLOSED, sock_->Read(NULL, 1, NULL));
}
@@ -972,9 +1047,60 @@ TEST_F(SpdyProxyClientSocketTest, WriteOnClosedStream) {
EXPECT_EQ(ERR_CONNECTION_CLOSED, sock_->Write(buf, buf->size(), NULL));
}
-// ----------- Pending read/write when closed
+// Calling Write() on a disconnected socket is an error
+TEST_F(SpdyProxyClientSocketTest, WriteOnDisconnectedSocket) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 0, false),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ scoped_ptr<spdy::SpdyFrame> msg1(ConstructBodyFrame(kMsg1, kLen1));
+ MockRead reads[] = {
+ CreateMockRead(*resp, 1, true),
+ MockRead(true, 0, 2), // EOF
+ };
+
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ AssertConnectSucceeds();
+
+ sock_->Disconnect();
+
+ scoped_refptr<IOBufferWithSize> buf(CreateBuffer(kMsg1, kLen1));
+ EXPECT_EQ(ERR_SOCKET_NOT_CONNECTED, sock_->Write(buf, buf->size(), NULL));
+}
// If the socket is closed with a pending Write(), the callback
+// should be called with ERR_CONNECTION_CLOSED.
+TEST_F(SpdyProxyClientSocketTest, WritePendingOnClose) {
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
+ MockWrite writes[] = {
+ CreateMockWrite(*conn, 0, false),
+ MockWrite(true, ERR_IO_PENDING, 2),
+ };
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructConnectReplyFrame());
+ MockRead reads[] = {
+ CreateMockRead(*resp, 1, true),
+ MockRead(true, 0, 3), // 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(), &write_callback_));
+
+ Run(1);
+
+ EXPECT_EQ(ERR_CONNECTION_CLOSED, write_callback_.WaitForResult());
+}
+
+// If the socket is Disconnected with a pending Write(), the callback
// should not be called.
TEST_F(SpdyProxyClientSocketTest, DisconnectWithWritePending) {
scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
@@ -1004,7 +1130,7 @@ TEST_F(SpdyProxyClientSocketTest, DisconnectWithWritePending) {
EXPECT_FALSE(write_callback_.have_result());
}
-// If the socket is closed with a pending Read(), the callback
+// If the socket is Disconnected with a pending Read(), the callback
// should not be called.
TEST_F(SpdyProxyClientSocketTest, DisconnectWithReadPending) {
scoped_ptr<spdy::SpdyFrame> conn(ConstructConnectRequestFrame());
diff --git a/net/spdy/spdy_test_util.cc b/net/spdy/spdy_test_util.cc
index 1727717..3dda1edd 100644
--- a/net/spdy/spdy_test_util.cc
+++ b/net/spdy/spdy_test_util.cc
@@ -407,6 +407,28 @@ spdy::SpdyFrame* ConstructSpdyGet(const char* const extra_headers[],
arraysize(kStandardGetHeaders));
}
+// Constructs a standard SPDY SYN_STREAM frame for a CONNECT request.
+spdy::SpdyFrame* ConstructSpdyConnect(const char* const extra_headers[],
+ int extra_header_count,
+ int stream_id) {
+ const char* const kConnectHeaders[] = {
+ "method", "CONNECT",
+ "url", "www.google.com:443",
+ "host", "www.google.com",
+ "version", "HTTP/1.1",
+ "proxy-connection", "keep-alive",
+ };
+ return ConstructSpdyControlFrame(extra_headers,
+ extra_header_count,
+ /*compressed*/ false,
+ stream_id,
+ LOWEST,
+ spdy::SYN_STREAM,
+ spdy::CONTROL_FLAG_NONE,
+ kConnectHeaders,
+ arraysize(kConnectHeaders));
+}
+
// Constructs a standard SPDY push SYN packet.
// |extra_headers| are the extra header-value pairs, which typically
// will vary the most between calls.
@@ -524,6 +546,36 @@ spdy::SpdyFrame* ConstructSpdyGetSynReplyRedirect(int stream_id) {
arraysize(kStandardGetHeaders));
}
+// Constructs a standard SPDY SYN_REPLY packet with an Internal Server
+// Error status code.
+// Returns a SpdyFrame.
+spdy::SpdyFrame* ConstructSpdySynReplyError(int stream_id) {
+ return ConstructSpdySynReplyError("500 Internal Server Error", 1);
+}
+
+// Constructs a standard SPDY SYN_REPLY packet with the specified status code.
+// Returns a SpdyFrame.
+spdy::SpdyFrame* ConstructSpdySynReplyError(const char* const status,
+ int stream_id) {
+ static const char* const kStandardGetHeaders[] = {
+ "hello",
+ "bye",
+ "status",
+ status,
+ "version",
+ "HTTP/1.1"
+ };
+ return ConstructSpdyControlFrame(NULL,
+ 0,
+ false,
+ stream_id,
+ LOWEST,
+ spdy::SYN_REPLY,
+ spdy::CONTROL_FLAG_NONE,
+ kStandardGetHeaders,
+ arraysize(kStandardGetHeaders));
+}
+
// Constructs a standard SPDY SYN_REPLY packet to match the SPDY GET.
// |extra_headers| are the extra header-value pairs, which typically
// will vary the most between calls.
@@ -629,6 +681,15 @@ spdy::SpdyFrame* ConstructSpdyBodyFrame(int stream_id, const char* data,
stream_id, data, len, fin ? spdy::DATA_FLAG_FIN : spdy::DATA_FLAG_NONE);
}
+// Wraps |frame| in the payload of a data frame in stream |stream_id|.
+spdy::SpdyFrame* ConstructWrappedSpdyFrame(
+ const scoped_ptr<spdy::SpdyFrame>& frame,
+ int stream_id) {
+ return ConstructSpdyBodyFrame(stream_id, frame->data(),
+ frame->length() + spdy::SpdyFrame::size(),
+ false);
+}
+
// Construct an expected SPDY reply string.
// |extra_headers| are the extra header-value pairs, which typically
// will vary the most between calls.
diff --git a/net/spdy/spdy_test_util.h b/net/spdy/spdy_test_util.h
index 6304c74..8c3a1e5 100644
--- a/net/spdy/spdy_test_util.h
+++ b/net/spdy/spdy_test_util.h
@@ -212,6 +212,11 @@ spdy::SpdyFrame* ConstructSpdyGet(const char* const extra_headers[],
RequestPriority request_priority,
bool direct);
+// Constructs a standard SPDY SYN_STREAM frame for a CONNECT request.
+spdy::SpdyFrame* ConstructSpdyConnect(const char* const extra_headers[],
+ int extra_header_count,
+ int stream_id);
+
// Constructs a standard SPDY push SYN packet.
// |extra_headers| are the extra header-value pairs, which typically
// will vary the most between calls.
@@ -248,6 +253,15 @@ spdy::SpdyFrame* ConstructSpdyGetSynReply(const char* const extra_headers[],
// Returns a SpdyFrame.
spdy::SpdyFrame* ConstructSpdyGetSynReplyRedirect(int stream_id);
+// Constructs a standard SPDY SYN_REPLY packet with an Internal Server
+// Error status code.
+// Returns a SpdyFrame.
+spdy::SpdyFrame* ConstructSpdySynReplyError(int stream_id);
+
+// Constructs a standard SPDY SYN_REPLY packet with the specified status code.
+// Returns a SpdyFrame.
+spdy::SpdyFrame* ConstructSpdySynReplyError(const char* const status,
+ int stream_id);
// Constructs a standard SPDY POST SYN packet.
// |extra_headers| are the extra header-value pairs, which typically
// will vary the most between calls.
@@ -271,6 +285,10 @@ spdy::SpdyFrame* ConstructSpdyBodyFrame(int stream_id,
spdy::SpdyFrame* ConstructSpdyBodyFrame(int stream_id, const char* data,
uint32 len, bool fin);
+// Wraps |frame| in the payload of a data frame in stream |stream_id|.
+spdy::SpdyFrame* ConstructWrappedSpdyFrame(
+ const scoped_ptr<spdy::SpdyFrame>& frame, int stream_id);
+
// Create an async MockWrite from the given SpdyFrame.
MockWrite CreateMockWrite(const spdy::SpdyFrame& req);