| Index: net/http/http_network_transaction_unittest.cc | 
| diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc | 
| index f7614fffd8e1cd7bc355a05dc34985cc677c1820..909c8ec2cc8e505b56432d015b64ce226af57038 100644 | 
| --- a/net/http/http_network_transaction_unittest.cc | 
| +++ b/net/http/http_network_transaction_unittest.cc | 
| @@ -12,6 +12,7 @@ | 
| #include "base/file_path.h" | 
| #include "base/file_util.h" | 
| #include "base/scoped_ptr.h" | 
| +#include "base/time.h" | 
| #include "base/utf_string_conversions.h" | 
| #include "net/base/auth.h" | 
| #include "net/base/capturing_net_log.h" | 
| @@ -8463,4 +8464,250 @@ TEST_F(HttpNetworkTransactionTest, Proxy_ClientAuthCertCache_Fail) { | 
| } | 
| } | 
|  | 
| +// Tests that the client certificate used to authenticate with an HTTPS proxy | 
| +// is different than the client certificate used to authenticate to an SSL | 
| +// endpoint, and that the HTTPS proxy certificate will not be sent to the SSL | 
| +// endpoint. | 
| +TEST_F(HttpNetworkTransactionTest, HTTPSProxyAuthAndSSLClientAuth) { | 
| +  SessionDependencies session_deps( | 
| +      ProxyService::CreateFixed("https://proxy:70")); | 
| +  CapturingBoundNetLog log(CapturingNetLog::kUnbounded); | 
| +  session_deps.net_log = log.bound().net_log(); | 
| + | 
| +  // Certificate request from the HTTPS proxy. | 
| +  scoped_refptr<SSLCertRequestInfo> proxy_cert_request( | 
| +      new SSLCertRequestInfo()); | 
| +  proxy_cert_request->host_and_port = "proxy:70"; | 
| + | 
| +  // The client certificate to use when authenticating with the proxy. | 
| +  base::Time start_date = base::Time::Now(); | 
| +  base::Time expiration_date = start_date + base::TimeDelta::FromDays(1); | 
| +  scoped_refptr<X509Certificate> proxy_cert( | 
| +      new X509Certificate("proxy", "me", start_date, expiration_date)); | 
| + | 
| +  // Certificate request from the SSL endpoint. | 
| +  scoped_refptr<SSLCertRequestInfo> endpoint_cert_request( | 
| +    new net::SSLCertRequestInfo()); | 
| +  endpoint_cert_request->host_and_port = "www.example.com:443"; | 
| + | 
| +  // The client certificate to use when authenticating with the SSL endpoint. | 
| +  scoped_refptr<X509Certificate> endpoint_cert( | 
| +      new X509Certificate("endpoint", "you", start_date, expiration_date)); | 
| + | 
| +  // ssl_data1 is the initial connection to the HTTPS proxy, which is | 
| +  // requesting a client certificate. data1 is needed to initialize the | 
| +  // underlying transport socket, but no data is exchanged due to the SSL | 
| +  // handshake failure. | 
| +  SSLSocketDataProvider ssl_data1(true /* async */, | 
| +                                  net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED); | 
| +  ssl_data1.cert_request_info = proxy_cert_request.get(); | 
| +  session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data1); | 
| + | 
| +  StaticSocketDataProvider data1(NULL, 0, NULL, 0); | 
| +  session_deps.socket_factory.AddSocketDataProvider(&data1); | 
| + | 
| +  // ssl_data2 is the re-connection to the HTTPS proxy, now with a valid | 
| +  // client certificate. data2 contains the data exchanged with the proxy to | 
| +  // set up the tunnel, and then the following reads/writes contain the data | 
| +  // exchanged with the endpoint, which there should be none, due to the | 
| +  // endpoint SSL handshake failure. | 
| +  SSLSocketDataProvider ssl_data2(true /* async */, net::OK); | 
| +  session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data2); | 
| + | 
| +  MockWrite data_writes2[] = { | 
| +    MockWrite("CONNECT www.example.com:443 HTTP/1.1\r\n" | 
| +              "Host: www.example.com\r\n" | 
| +              "Proxy-Connection: keep-alive\r\n\r\n"), | 
| +  }; | 
| +  MockRead data_reads2[] = { | 
| +    MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"), | 
| +  }; | 
| + | 
| +  StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2), | 
| +                                 data_writes2, arraysize(data_writes2)); | 
| +  session_deps.socket_factory.AddSocketDataProvider(&data2); | 
| + | 
| +  // ssl_data3 is the initial connection to the SSL endpoint, which is also | 
| +  // requesting a client certificate. There is no data3, because ssl_data3's | 
| +  // client socket handle comes from the proxy, which is using data2. | 
| +  SSLSocketDataProvider ssl_data3(true /* async */, | 
| +                                  net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED); | 
| +  ssl_data3.cert_request_info = endpoint_cert_request.get(); | 
| +  session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data3); | 
| + | 
| +  // ssl_data4 is the reconnection to the HTTPS proxy, after the connection | 
| +  // to the SSL endpoint has failed due to requiring a client certificate. | 
| +  // data4 contains the data exchanged with the proxy to set up the tunnel, | 
| +  // and then the subsequent reads/writes contains the data exchanged with | 
| +  // the endpoint. | 
| +  SSLSocketDataProvider ssl_data4(true /* async */, net::OK); | 
| +  session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data4); | 
| + | 
| +  MockWrite data_writes4[] = { | 
| +    MockWrite("CONNECT www.example.com:443 HTTP/1.1\r\n" | 
| +              "Host: www.example.com\r\n" | 
| +              "Proxy-Connection: keep-alive\r\n\r\n"), | 
| + | 
| +    MockWrite("GET / HTTP/1.1\r\n" | 
| +              "Host: www.example.com\r\n" | 
| +              "Connection: keep-alive\r\n\r\n"), | 
| +  }; | 
| +  MockRead data_reads4[] = { | 
| +    MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"), | 
| + | 
| +    MockRead("HTTP/1.1 200 OK\r\n\r\n"), | 
| +    MockRead("hello, world"), | 
| +    MockRead(false, OK), | 
| +  }; | 
| +  StaticSocketDataProvider data4(data_reads4, arraysize(data_reads4), | 
| +                                 data_writes4, arraysize(data_writes4)); | 
| +  session_deps.socket_factory.AddSocketDataProvider(&data4); | 
| + | 
| +  // ssl_data5 is the SSL handshake with the endpoint, after the proxy has | 
| +  // been negotiated. There is no data5, because ssl_data5's client socket | 
| +  // handle comes from the proxy, which is using data4. | 
| +  SSLSocketDataProvider ssl_data5(true /* async */, net::OK); | 
| +  session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data5); | 
| + | 
| +  net::HttpRequestInfo request; | 
| +  request.url = GURL("https://www.example.com/"); | 
| +  request.method = "GET"; | 
| +  request.load_flags = net::LOAD_NORMAL; | 
| + | 
| +  scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); | 
| +  scoped_ptr<HttpNetworkTransaction> trans( | 
| +      new HttpNetworkTransaction(session)); | 
| + | 
| +  // Make the initial connection to the HTTPS proxy. | 
| +  TestCompletionCallback callback; | 
| +  int rv = trans->Start(&request, &callback, net::BoundNetLog()); | 
| +  ASSERT_EQ(net::ERR_IO_PENDING, rv); | 
| +  // Finish the SSL handshake to the HTTPS proxy (ssl_data1). | 
| +  rv = callback.WaitForResult(); | 
| +  ASSERT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED, rv); | 
| + | 
| +  // Make sure that the certificate request matches that from ssl_data1. | 
| +  const HttpResponseInfo* response = trans->GetResponseInfo(); | 
| +  ASSERT_TRUE(response); | 
| +  ASSERT_TRUE(response->cert_request_info); | 
| +  ASSERT_EQ("proxy:70", response->cert_request_info->host_and_port); | 
| + | 
| +  // Restart the connection to the HTTPS proxy, now with a client | 
| +  // certificate (ssl_data2). | 
| +  rv = trans->RestartWithCertificate(proxy_cert, &callback); | 
| +  ASSERT_EQ(net::ERR_IO_PENDING, rv); | 
| + | 
| +  // Make sure that SSL client auth cache is only updated for the proxy and | 
| +  // not for the endpoint. | 
| +  scoped_refptr<X509Certificate> cached_cert; | 
| +  ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup("proxy:70", | 
| +                                                       &cached_cert)); | 
| +  ASSERT_EQ(proxy_cert, cached_cert); | 
| +  ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("www.example.com:443", | 
| +                                                        &cached_cert)); | 
| + | 
| +  // Utility class to interject ourselves into the handshake. |callback|, | 
| +  // the TestCompletionCallback, can only wait until the | 
| +  // HttpNetworkTransaction callback is invoked. However, on an SSL failure, | 
| +  // the MockSSLClientSocket will be destroyed by the ClientSocketHandle, | 
| +  // meaning the parameters that were passed in it's construction will no | 
| +  // longer be available. This is just a hack-job substitute to demonstrate | 
| +  // the problem without having to shave the Mock*Factory yak to allow us to | 
| +  // break after creating a socket, and DOES NOT represent code I'd ever | 
| +  // check in. | 
| +  class WaitForSSLCreationObserver : public MessageLoop::TaskObserver { | 
| +   public: | 
| +    WaitForSSLCreationObserver(SessionDependencies* session_deps, | 
| +                               size_t expected_ssl_sockets) | 
| +        : session_deps_(session_deps), | 
| +          expected_ssl_sockets_(expected_ssl_sockets) { | 
| +    } | 
| + | 
| +    // TaskObserver | 
| +    virtual void WillProcessTask(const Task* task) OVERRIDE {} | 
| +    virtual void DidProcessTask(const Task* task) OVERRIDE { | 
| +      if (session_deps_->socket_factory.ssl_client_sockets().size() >= | 
| +          expected_ssl_sockets_) { | 
| +        MessageLoop::current()->QuitNow(); | 
| +      } | 
| +    } | 
| +   private: | 
| +    SessionDependencies* session_deps_; | 
| +    size_t expected_ssl_sockets_; | 
| +  }; | 
| + | 
| +  // Finish the SSL handshake with the HTTPS proxy and begin | 
| +  // the SSL handshake with the endpoint (ssl_data3). | 
| +  WaitForSSLCreationObserver wait_for_endpoint(&session_deps, 3); | 
| +  MessageLoop::current()->AddTaskObserver(&wait_for_endpoint); | 
| +  MessageLoop::current()->Run(); | 
| +  MessageLoop::current()->RemoveTaskObserver(&wait_for_endpoint); | 
| + | 
| +  // Make sure that the SSL handshake to the endpoint is NOT set to send the | 
| +  // client certificate used for the proxy. | 
| +  MockSSLClientSocket* endpoint_socket = | 
| +      session_deps.socket_factory.GetMockSSLClientSocket(2); | 
| +  ASSERT_TRUE(endpoint_socket); | 
| +  ASSERT_EQ("www.example.com:443", | 
| +            endpoint_socket->GetHostAndPort().ToString()); | 
| +  ASSERT_FALSE(endpoint_socket->GetSSLConfig().send_client_cert); | 
| +  ASSERT_FALSE(endpoint_socket->GetSSLConfig().client_cert); | 
| + | 
| +  rv = callback.WaitForResult(); | 
| +  ASSERT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED, rv); | 
| + | 
| +  // Make sure that the certificate request matches that from ssl_data3. | 
| +  response = trans->GetResponseInfo(); | 
| +  ASSERT_TRUE(response); | 
| +  ASSERT_TRUE(response->cert_request_info); | 
| +  ASSERT_EQ("www.example.com:443", | 
| +            response->cert_request_info->host_and_port); | 
| + | 
| +  // Restart the SSL handshake with the endpoint. This will cause ssl_data4 | 
| +  // to be consumed, because |trans| has to first re-establish a connection | 
| +  // with the proxy. | 
| +  rv = trans->RestartWithCertificate(endpoint_cert, &callback); | 
| +  ASSERT_EQ(net::ERR_IO_PENDING, rv); | 
| + | 
| +  // Ensure that the SSL socket for the HTTPS proxy is still configured to | 
| +  // use the proxy client certificate, and NOT the client certificate for | 
| +  // the endpoint. | 
| +  MockSSLClientSocket* proxy_socket = | 
| +      session_deps.socket_factory.GetMockSSLClientSocket(3); | 
| +  ASSERT_TRUE(proxy_socket); | 
| +  ASSERT_EQ("proxy:70", proxy_socket->GetHostAndPort().ToString()); | 
| +  ASSERT_TRUE(proxy_socket->GetSSLConfig().send_client_cert); | 
| +  ASSERT_EQ(proxy_cert, proxy_socket->GetSSLConfig().client_cert); | 
| + | 
| +  // Finish the SSL handshake with HTTPS proxy and start the SSL handshake | 
| +  // with the endpoint. This will consume ssl_data5. | 
| +  WaitForSSLCreationObserver wait_for_endpoint_again(&session_deps, 5); | 
| +  MessageLoop::current()->AddTaskObserver(&wait_for_endpoint_again); | 
| +  MessageLoop::current()->Run(); | 
| +  MessageLoop::current()->RemoveTaskObserver(&wait_for_endpoint_again); | 
| + | 
| +  // Ensure that the SSL socket for the SSL endpoint is configured to use | 
| +  // the endpoint client certificate, and NOT the client certificate for the | 
| +  // HTTPS proxy. | 
| +  endpoint_socket = session_deps.socket_factory.GetMockSSLClientSocket(4); | 
| +  ASSERT_TRUE(endpoint_socket); | 
| +  ASSERT_EQ("www.example.com:443", | 
| +            endpoint_socket->GetHostAndPort().ToString()); | 
| +  ASSERT_TRUE(endpoint_socket->GetSSLConfig().send_client_cert); | 
| +  ASSERT_EQ(endpoint_cert, endpoint_socket->GetSSLConfig().client_cert); | 
| + | 
| +  rv = callback.WaitForResult(); | 
| +  ASSERT_EQ(net::OK, rv); | 
| + | 
| +  // Make sure that the request was consumed from data4. | 
| +  response = trans->GetResponseInfo(); | 
| +  ASSERT_TRUE(response); | 
| +  EXPECT_EQ(200, response->headers->response_code()); | 
| +  EXPECT_EQ(HttpVersion(1, 1), response->headers->GetHttpVersion()); | 
| +  std::string response_data; | 
| +  rv = ReadTransaction(trans.get(), &response_data); | 
| +  EXPECT_EQ(net::OK, rv); | 
| +  EXPECT_EQ("hello, world", response_data); | 
| +} | 
| + | 
| }  // namespace net | 
|  |