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 5c9f0a29641ebba3235287e2fcfdd91760226689..82011903c54a0d4e0ecd97c4157e25c224f8db6b 100644 |
--- a/net/http/http_network_transaction_unittest.cc |
+++ b/net/http/http_network_transaction_unittest.cc |
@@ -8407,4 +8407,104 @@ TEST_F(HttpNetworkTransactionTest, ClientAuthCertCache_Direct_FalseStart) { |
&client_cert)); |
} |
+// Ensure that a client certificate is removed from the SSL client auth |
+// cache when: |
+// 1) An HTTPS proxy is involved. |
+// 3) The HTTPS proxy requests a client certificate. |
+// 4) The client supplies an invalid/unacceptable certificate for the |
+// proxy. |
+// The test is repeated twice, first for connecting to an HTTPS endpoint, |
+// then for connecting to an HTTP endpoint. |
+TEST_F(HttpNetworkTransactionTest, ClientAuthCertCache_Proxy_Fail) { |
+ SessionDependencies session_deps( |
+ ProxyService::CreateFixed("https://proxy:70")); |
+ CapturingBoundNetLog log(CapturingNetLog::kUnbounded); |
+ session_deps.net_log = log.bound().net_log(); |
+ |
+ scoped_refptr<SSLCertRequestInfo> cert_request(new SSLCertRequestInfo()); |
+ cert_request->host_and_port = "proxy:70"; |
+ |
+ // See ClientAuthCertCache_Direct_NoFalseStart for the explanation of |
+ // [ssl_]data[1-3]. Rather than represending the endpoint |
+ // (www.example.com:443), they represent failures with the HTTPS proxy |
+ // (proxy:70). |
+ SSLSocketDataProvider ssl_data1(true /* async */, |
+ net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED); |
+ ssl_data1.cert_request_info = cert_request.get(); |
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data1); |
+ net::StaticSocketDataProvider data1(NULL, 0, NULL, 0); |
+ session_deps.socket_factory.AddSocketDataProvider(&data1); |
+ |
+ SSLSocketDataProvider ssl_data2(true /* async */, |
+ net::ERR_SSL_PROTOCOL_ERROR); |
+ ssl_data2.cert_request_info = cert_request.get(); |
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data2); |
+ net::StaticSocketDataProvider data2(NULL, 0, NULL, 0); |
+ session_deps.socket_factory.AddSocketDataProvider(&data2); |
+ |
+ SSLSocketDataProvider ssl_data3(true /* async */, |
+ net::ERR_SSL_PROTOCOL_ERROR); |
+ ssl_data3.cert_request_info = cert_request.get(); |
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data3); |
+ net::StaticSocketDataProvider data3(NULL, 0, NULL, 0); |
+ session_deps.socket_factory.AddSocketDataProvider(&data3); |
+ |
+ net::HttpRequestInfo requests[2]; |
+ requests[0].url = GURL("https://www.example.com/"); |
+ requests[0].method = "GET"; |
+ requests[0].load_flags = net::LOAD_NORMAL; |
+ |
+ requests[1].url = GURL("http://www.example.com/"); |
+ requests[1].method = "GET"; |
+ requests[1].load_flags = net::LOAD_NORMAL; |
+ |
+ for (size_t i = 0; i < arraysize(requests); ++i) { |
+ session_deps.socket_factory.ResetNextMockIndexes(); |
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); |
+ scoped_ptr<HttpNetworkTransaction> trans( |
+ new HttpNetworkTransaction(session)); |
+ |
+ // Begin the SSL handshake with the proxy. |
+ TestCompletionCallback callback; |
+ int rv = trans->Start(&requests[i], &callback, net::BoundNetLog()); |
+ ASSERT_EQ(net::ERR_IO_PENDING, rv); |
+ |
+ // Complete the SSL handshake, which should abort due to requiring a |
+ // client certificate. |
+ rv = callback.WaitForResult(); |
+ ASSERT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED, rv); |
+ |
+ // Indicate that no certificate should be supplied. From the perspective |
+ // of SSLClientCertCache, NULL is just as meaningful as a real |
+ // certificate, so this is the same as supply a |
+ // legitimate-but-unacceptable certificate. |
+ rv = trans->RestartWithCertificate(NULL, &callback); |
+ ASSERT_EQ(net::ERR_IO_PENDING, rv); |
+ |
+ // Ensure the certificate was added to the client auth cache before |
+ // allowing the connection to continue restarting. |
+ scoped_refptr<X509Certificate> client_cert; |
+ ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup("proxy:70", |
+ &client_cert)); |
+ ASSERT_EQ(NULL, client_cert.get()); |
+ // Ensure the certificate was NOT cached for the endpoint. This only |
+ // applies to HTTPS requests, but is fine to check for HTTP requests. |
+ ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("www.example.com:443", |
+ &client_cert)); |
+ |
+ // Restart the handshake. This will consume ssl_data2, which fails, and |
+ // then consume ssl_data3, which should also fail. The result code is |
+ // checked against what ssl_data3 should return. |
+ rv = callback.WaitForResult(); |
+ ASSERT_EQ(net::ERR_PROXY_CONNECTION_FAILED, rv); |
+ |
+ // Now that the new handshake has failed, ensure that the client |
+ // certificate was removed from the client auth cache. |
+ ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("proxy:70", |
+ &client_cert)); |
+ ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("www.example.com:443", |
+ &client_cert)); |
+ } |
+} |
+ |
} // namespace net |