summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-25 09:03:56 +0000
committerukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-25 09:03:56 +0000
commitaac499059df9f32e6b3ee691c17a4b6e6be3415b (patch)
tree89a29343a08d586bbe89034b317ebd4bfa881766 /net
parentd380411308f56505d4e83a923942a5fb840d0d69 (diff)
downloadchromium_src-aac499059df9f32e6b3ee691c17a4b6e6be3415b.zip
chromium_src-aac499059df9f32e6b3ee691c17a4b6e6be3415b.tar.gz
chromium_src-aac499059df9f32e6b3ee691c17a4b6e6be3415b.tar.bz2
secure proxy support in websocket
BUG=83950 TEST=net_unittest --gtest_filter=SocketStreamTest.* pass Review URL: http://codereview.chromium.org/7468025 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@93860 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/socket_stream/socket_stream.cc134
-rw-r--r--net/socket_stream/socket_stream.h5
-rw-r--r--net/socket_stream/socket_stream_metrics.h1
-rw-r--r--net/socket_stream/socket_stream_unittest.cc105
4 files changed, 201 insertions, 44 deletions
diff --git a/net/socket_stream/socket_stream.cc b/net/socket_stream/socket_stream.cc
index 74eabec..57af475 100644
--- a/net/socket_stream/socket_stream.cc
+++ b/net/socket_stream/socket_stream.cc
@@ -304,6 +304,49 @@ void SocketStream::Finish(int result) {
Release();
}
+int SocketStream::DidEstablishSSL(int result) {
+ if (IsCertificateError(result)) {
+ if (socket_->IsConnectedAndIdle()) {
+ result = HandleCertificateError(result);
+ } else {
+ // SSLClientSocket for Mac will report socket is not connected,
+ // if it returns cert verification error. It didn't perform
+ // SSLHandshake yet.
+ // So, we should restart establishing connection with the
+ // certificate in allowed bad certificates in |ssl_config_|.
+ // See also net/http/http_network_transaction.cc
+ // HandleCertificateError() and RestartIgnoringLastError().
+ SSLClientSocket* ssl_socket =
+ reinterpret_cast<SSLClientSocket*>(socket_.get());
+ SSLInfo ssl_info;
+ ssl_socket->GetSSLInfo(&ssl_info);
+ if (ssl_info.cert == NULL ||
+ ssl_config_.IsAllowedBadCert(ssl_info.cert, NULL)) {
+ // If we already have the certificate in the set of allowed bad
+ // certificates, we did try it and failed again, so we should not
+ // retry again: the connection should fail at last.
+ next_state_ = STATE_CLOSE;
+ return result;
+ }
+ // Add the bad certificate to the set of allowed certificates in the
+ // SSL config object.
+ SSLConfig::CertAndStatus bad_cert;
+ if (!ssl_info.cert->GetDEREncoded(&bad_cert.der_cert)) {
+ next_state_ = STATE_CLOSE;
+ return result;
+ }
+ bad_cert.cert_status = ssl_info.cert_status;
+ ssl_config_.allowed_bad_certs.push_back(bad_cert);
+ // Restart connection ignoring the bad certificate.
+ socket_->Disconnect();
+ socket_.reset();
+ next_state_ = STATE_TCP_CONNECT;
+ return OK;
+ }
+ }
+ return result;
+}
+
int SocketStream::DidEstablishConnection() {
if (!socket_.get() || !socket_->IsConnected()) {
next_state_ = STATE_CLOSE;
@@ -440,6 +483,13 @@ void SocketStream::DoLoop(int result) {
case STATE_SOCKS_CONNECT_COMPLETE:
result = DoSOCKSConnectComplete(result);
break;
+ case STATE_SECURE_PROXY_CONNECT:
+ DCHECK_EQ(OK, result);
+ result = DoSecureProxyConnect();
+ break;
+ case STATE_SECURE_PROXY_CONNECT_COMPLETE:
+ result = DoSecureProxyConnectComplete(result);
+ break;
case STATE_SSL_CONNECT:
DCHECK_EQ(OK, result);
result = DoSSLConnect();
@@ -616,11 +666,14 @@ int SocketStream::DoTcpConnectComplete(int result) {
return result;
}
- if (proxy_mode_ == kTunnelProxy)
- next_state_ = STATE_WRITE_TUNNEL_HEADERS;
- else if (proxy_mode_ == kSOCKSProxy)
+ if (proxy_mode_ == kTunnelProxy) {
+ if (proxy_info_.is_https())
+ next_state_ = STATE_SECURE_PROXY_CONNECT;
+ else
+ next_state_ = STATE_WRITE_TUNNEL_HEADERS;
+ } else if (proxy_mode_ == kSOCKSProxy) {
next_state_ = STATE_SOCKS_CONNECT;
- else if (is_secure()) {
+ } else if (is_secure()) {
next_state_ = STATE_SSL_CONNECT;
} else {
result = DidEstablishConnection();
@@ -850,6 +903,35 @@ int SocketStream::DoSOCKSConnectComplete(int result) {
return result;
}
+int SocketStream::DoSecureProxyConnect() {
+ DCHECK(factory_);
+ SSLClientSocketContext ssl_context;
+ ssl_context.cert_verifier = cert_verifier_;
+ ssl_context.origin_bound_cert_service = origin_bound_cert_service_;
+ // TODO(agl): look into plumbing SSLHostInfo here.
+ socket_.reset(factory_->CreateSSLClientSocket(
+ socket_.release(),
+ proxy_info_.proxy_server().host_port_pair(),
+ ssl_config_,
+ NULL /* ssl_host_info */,
+ ssl_context));
+ next_state_ = STATE_SECURE_PROXY_CONNECT_COMPLETE;
+ metrics_->OnCountConnectionType(SocketStreamMetrics::SECURE_PROXY_CONNECTION);
+ return socket_->Connect(&io_callback_);
+}
+
+int SocketStream::DoSecureProxyConnectComplete(int result) {
+ DCHECK_EQ(STATE_NONE, next_state_);
+ result = DidEstablishSSL(result);
+ if (next_state_ != STATE_NONE)
+ return result;
+ if (result == OK)
+ next_state_ = STATE_WRITE_TUNNEL_HEADERS;
+ else
+ next_state_ = STATE_CLOSE;
+ return result;
+}
+
int SocketStream::DoSSLConnect() {
DCHECK(factory_);
SSLClientSocketContext ssl_context;
@@ -867,46 +949,10 @@ int SocketStream::DoSSLConnect() {
}
int SocketStream::DoSSLConnectComplete(int result) {
- if (IsCertificateError(result)) {
- if (socket_->IsConnectedAndIdle()) {
- result = HandleCertificateError(result);
- } else {
- // SSLClientSocket for Mac will report socket is not connected,
- // if it returns cert verification error. It didn't perform
- // SSLHandshake yet.
- // So, we should restart establishing connection with the
- // certificate in allowed bad certificates in |ssl_config_|.
- // See also net/http/http_network_transaction.cc
- // HandleCertificateError() and RestartIgnoringLastError().
- SSLClientSocket* ssl_socket =
- reinterpret_cast<SSLClientSocket*>(socket_.get());
- SSLInfo ssl_info;
- ssl_socket->GetSSLInfo(&ssl_info);
- if (ssl_info.cert == NULL ||
- ssl_config_.IsAllowedBadCert(ssl_info.cert, NULL)) {
- // If we already have the certificate in the set of allowed bad
- // certificates, we did try it and failed again, so we should not
- // retry again: the connection should fail at last.
- next_state_ = STATE_CLOSE;
- return result;
- }
- // Add the bad certificate to the set of allowed certificates in the
- // SSL config object.
- SSLConfig::CertAndStatus bad_cert;
- if (!ssl_info.cert->GetDEREncoded(&bad_cert.der_cert)) {
- next_state_ = STATE_CLOSE;
- return result;
- }
- bad_cert.cert_status = ssl_info.cert_status;
- ssl_config_.allowed_bad_certs.push_back(bad_cert);
- // Restart connection ignoring the bad certificate.
- socket_->Disconnect();
- socket_.reset();
- next_state_ = STATE_TCP_CONNECT;
- return OK;
- }
- }
-
+ DCHECK_EQ(STATE_NONE, next_state_);
+ result = DidEstablishSSL(result);
+ if (next_state_ != STATE_NONE)
+ return result;
// TODO(toyoshim): Upgrade to SPDY through TLS NPN extension if possible.
// If we use HTTPS and this is the first connection to the SPDY server,
// we should take care of TLS NPN extension here.
diff --git a/net/socket_stream/socket_stream.h b/net/socket_stream/socket_stream.h
index 19331f4..5e5b339 100644
--- a/net/socket_stream/socket_stream.h
+++ b/net/socket_stream/socket_stream.h
@@ -228,6 +228,8 @@ class NET_API SocketStream : public base::RefCountedThreadSafe<SocketStream> {
STATE_READ_TUNNEL_HEADERS_COMPLETE,
STATE_SOCKS_CONNECT,
STATE_SOCKS_CONNECT_COMPLETE,
+ STATE_SECURE_PROXY_CONNECT,
+ STATE_SECURE_PROXY_CONNECT_COMPLETE,
STATE_SSL_CONNECT,
STATE_SSL_CONNECT_COMPLETE,
STATE_READ_WRITE,
@@ -255,6 +257,7 @@ class NET_API SocketStream : public base::RefCountedThreadSafe<SocketStream> {
// notifications will be sent to delegate.
void Finish(int result);
+ int DidEstablishSSL(int result);
int DidEstablishConnection();
int DidReceiveData(int result);
int DidSendData(int result);
@@ -279,6 +282,8 @@ class NET_API SocketStream : public base::RefCountedThreadSafe<SocketStream> {
int DoReadTunnelHeadersComplete(int result);
int DoSOCKSConnect();
int DoSOCKSConnectComplete(int result);
+ int DoSecureProxyConnect();
+ int DoSecureProxyConnectComplete(int result);
int DoSSLConnect();
int DoSSLConnectComplete(int result);
int DoReadWrite(int result);
diff --git a/net/socket_stream/socket_stream_metrics.h b/net/socket_stream/socket_stream_metrics.h
index 83666fe..e69ba25 100644
--- a/net/socket_stream/socket_stream_metrics.h
+++ b/net/socket_stream/socket_stream_metrics.h
@@ -32,6 +32,7 @@ class NET_TEST SocketStreamMetrics {
TUNNEL_CONNECTION,
SOCKS_CONNECTION,
SSL_CONNECTION,
+ SECURE_PROXY_CONNECTION,
NUM_CONNECTION_TYPES,
};
diff --git a/net/socket_stream/socket_stream_unittest.cc b/net/socket_stream/socket_stream_unittest.cc
index 090ecff..80bff3c 100644
--- a/net/socket_stream/socket_stream_unittest.cc
+++ b/net/socket_stream/socket_stream_unittest.cc
@@ -535,4 +535,109 @@ TEST_F(SocketStreamTest, SwitchAfterPending) {
EXPECT_EQ(net::ERR_PROTOCOL_SWITCHED, events[1].error_code);
}
+// Test a connection though a secure proxy.
+TEST_F(SocketStreamTest, SecureProxyConnectError) {
+ MockClientSocketFactory mock_socket_factory;
+ MockWrite data_writes[] = {
+ MockWrite("CONNECT example.com:80 HTTP/1.1\r\n"
+ "Host: example.com\r\n"
+ "Proxy-Connection: keep-alive\r\n\r\n")
+ };
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.1 200 Connection Established\r\n"),
+ MockRead("Proxy-agent: Apache/2.2.8\r\n"),
+ MockRead("\r\n"),
+ // SocketStream::DoClose is run asynchronously. Socket can be read after
+ // "\r\n". We have to give ERR_IO_PENDING to SocketStream then to indicate
+ // server doesn't close the connection.
+ MockRead(true, ERR_IO_PENDING)
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+ data_writes, arraysize(data_writes));
+ mock_socket_factory.AddSocketDataProvider(&data);
+ SSLSocketDataProvider ssl(false, ERR_SSL_PROTOCOL_ERROR);
+ mock_socket_factory.AddSSLSocketDataProvider(&ssl);
+
+ TestCompletionCallback callback;
+
+ scoped_ptr<SocketStreamEventRecorder> delegate(
+ new SocketStreamEventRecorder(&callback));
+ delegate->SetOnConnected(base::Bind(&SocketStreamEventRecorder::DoClose,
+ base::Unretained(delegate.get())));
+
+ scoped_refptr<SocketStream> socket_stream(
+ new SocketStream(GURL("ws://example.com/demo"), delegate.get()));
+
+ socket_stream->set_context(new TestURLRequestContext("https://myproxy:70"));
+ MockHostResolver host_resolver;
+ socket_stream->SetHostResolver(&host_resolver);
+ socket_stream->SetClientSocketFactory(&mock_socket_factory);
+
+ socket_stream->Connect();
+
+ callback.WaitForResult();
+
+ const std::vector<SocketStreamEvent>& events = delegate->GetSeenEvents();
+ ASSERT_EQ(3U, events.size());
+
+ EXPECT_EQ(SocketStreamEvent::EVENT_START_OPEN_CONNECTION,
+ events[0].event_type);
+ EXPECT_EQ(SocketStreamEvent::EVENT_ERROR, events[1].event_type);
+ EXPECT_EQ(net::ERR_SSL_PROTOCOL_ERROR, events[1].error_code);
+ EXPECT_EQ(SocketStreamEvent::EVENT_CLOSE, events[2].event_type);
+}
+
+// Test a connection though a secure proxy.
+TEST_F(SocketStreamTest, SecureProxyConnect) {
+ MockClientSocketFactory mock_socket_factory;
+ MockWrite data_writes[] = {
+ MockWrite("CONNECT example.com:80 HTTP/1.1\r\n"
+ "Host: example.com\r\n"
+ "Proxy-Connection: keep-alive\r\n\r\n")
+ };
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.1 200 Connection Established\r\n"),
+ MockRead("Proxy-agent: Apache/2.2.8\r\n"),
+ MockRead("\r\n"),
+ // SocketStream::DoClose is run asynchronously. Socket can be read after
+ // "\r\n". We have to give ERR_IO_PENDING to SocketStream then to indicate
+ // server doesn't close the connection.
+ MockRead(true, ERR_IO_PENDING)
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+ data_writes, arraysize(data_writes));
+ mock_socket_factory.AddSocketDataProvider(&data);
+ SSLSocketDataProvider ssl(false, OK);
+ mock_socket_factory.AddSSLSocketDataProvider(&ssl);
+
+ TestCompletionCallback callback;
+
+ scoped_ptr<SocketStreamEventRecorder> delegate(
+ new SocketStreamEventRecorder(&callback));
+ delegate->SetOnConnected(base::Bind(&SocketStreamEventRecorder::DoClose,
+ base::Unretained(delegate.get())));
+
+ scoped_refptr<SocketStream> socket_stream(
+ new SocketStream(GURL("ws://example.com/demo"), delegate.get()));
+
+ socket_stream->set_context(new TestURLRequestContext("https://myproxy:70"));
+ MockHostResolver host_resolver;
+ socket_stream->SetHostResolver(&host_resolver);
+ socket_stream->SetClientSocketFactory(&mock_socket_factory);
+
+ socket_stream->Connect();
+
+ callback.WaitForResult();
+
+ const std::vector<SocketStreamEvent>& events = delegate->GetSeenEvents();
+ ASSERT_EQ(4U, events.size());
+
+ EXPECT_EQ(SocketStreamEvent::EVENT_START_OPEN_CONNECTION,
+ events[0].event_type);
+ EXPECT_EQ(SocketStreamEvent::EVENT_CONNECTED, events[1].event_type);
+ EXPECT_EQ(SocketStreamEvent::EVENT_ERROR, events[2].event_type);
+ EXPECT_EQ(net::ERR_ABORTED, events[2].error_code);
+ EXPECT_EQ(SocketStreamEvent::EVENT_CLOSE, events[3].event_type);
+}
+
} // namespace net