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 aba2828919941d86b684290b57785fc5e65d5e6f..1aadd34558292a5dfe51ebd729fff3bbdfee15a8 100644 |
--- a/net/http/http_network_transaction_unittest.cc |
+++ b/net/http/http_network_transaction_unittest.cc |
@@ -11971,6 +11971,172 @@ TEST_P(AltSvcCertificateVerificationTest, NewConnectionInvalid) { |
Run(false, false); |
} |
+// Alternative service requires HTTP/2 (or SPDY), but HTTP/1.1 is negotiated |
+// with the alternative server. That connection should not be used. |
+TEST_P(HttpNetworkTransactionTest, AlternativeServiceNotOnHttp11) { |
+ HostPortPair origin("origin.example.org", 443); |
+ HostPortPair alternative("alternative.example.org", 443); |
+ |
+ // Negotiate HTTP/1.1 with alternative.example.org. |
+ SSLSocketDataProvider ssl(ASYNC, OK); |
+ ssl.SetNextProto(kProtoHTTP11); |
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); |
+ |
+ // No data should be read from the alternative, because HTTP/1.1 is |
+ // negotiated. |
+ StaticSocketDataProvider data; |
+ session_deps_.socket_factory->AddSocketDataProvider(&data); |
+ |
+ // This test documents that an alternate Job should not be used if HTTP/1.1 is |
+ // negotiated. In order to test this, a failed connection to the origin is |
+ // mocked. This way the request relies on the alternate Job. |
+ StaticSocketDataProvider data_refused; |
+ data_refused.set_connect_data(MockConnect(ASYNC, ERR_CONNECTION_REFUSED)); |
+ session_deps_.socket_factory->AddSocketDataProvider(&data_refused); |
+ |
+ // Set up alternative service for origin. |
+ session_deps_.use_alternate_protocols = true; |
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); |
+ base::WeakPtr<HttpServerProperties> http_server_properties = |
+ session->http_server_properties(); |
+ AlternativeService alternative_service( |
+ AlternateProtocolFromNextProto(GetParam()), alternative); |
+ http_server_properties->SetAlternativeService(origin, alternative_service, |
+ 1.0); |
+ |
+ scoped_ptr<HttpTransaction> trans( |
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); |
+ HttpRequestInfo request; |
+ request.method = "GET"; |
+ request.url = GURL("https://origin.example.org:443"); |
+ request.load_flags = 0; |
+ TestCompletionCallback callback; |
+ |
+ // HTTP/2 (or SPDY) is required for alternative service, if HTTP/1.1 is |
+ // negotiated, the alternate Job should fail with ERR_NPN_NEGOTIATION_FAILED. |
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog()); |
+ EXPECT_EQ(ERR_NPN_NEGOTIATION_FAILED, callback.GetResult(rv)); |
+} |
+ |
+// Alternative service requires HTTP/2 (or SPDY), but there is already a |
+// HTTP/1.1 socket open to the alternative server. That socket should not be |
+// used. |
+TEST_P(HttpNetworkTransactionTest, AlternativeServiceShouldNotPoolToHttp11) { |
+ HostPortPair origin("origin.example.org", 443); |
+ HostPortPair alternative("alternative.example.org", 443); |
+ std::string origin_url = "https://origin.example.org:443"; |
+ std::string alternative_url = "https://alternative.example.org:443"; |
+ |
+ // Negotiate HTTP/1.1 with alternative.example.org. |
+ SSLSocketDataProvider ssl(ASYNC, OK); |
+ ssl.SetNextProto(kProtoHTTP11); |
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); |
+ |
+ // HTTP/1.1 data for |request1| and |request2|. |
+ MockWrite http_writes[] = { |
+ MockWrite( |
+ "GET / HTTP/1.1\r\n" |
+ "Host: alternative.example.org\r\n" |
+ "Connection: keep-alive\r\n\r\n"), |
+ MockWrite( |
+ "GET / HTTP/1.1\r\n" |
+ "Host: alternative.example.org\r\n" |
+ "Connection: keep-alive\r\n\r\n"), |
+ }; |
+ |
+ MockRead http_reads[] = { |
+ MockRead( |
+ "HTTP/1.1 200 OK\r\n" |
+ "Content-Type: text/html; charset=iso-8859-1\r\n" |
+ "Content-Length: 40\r\n\r\n" |
+ "first HTTP/1.1 response from alternative"), |
+ MockRead( |
+ "HTTP/1.1 200 OK\r\n" |
+ "Content-Type: text/html; charset=iso-8859-1\r\n" |
+ "Content-Length: 41\r\n\r\n" |
+ "second HTTP/1.1 response from alternative"), |
+ }; |
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), |
+ http_writes, arraysize(http_writes)); |
+ session_deps_.socket_factory->AddSocketDataProvider(&http_data); |
+ |
+ // This test documents that an alternate Job should not pool to an already |
+ // existing HTTP/1.1 connection. In order to test this, a failed connection |
+ // to the origin is mocked. This way |request2| relies on the alternate Job. |
+ StaticSocketDataProvider data_refused; |
+ data_refused.set_connect_data(MockConnect(ASYNC, ERR_CONNECTION_REFUSED)); |
+ session_deps_.socket_factory->AddSocketDataProvider(&data_refused); |
+ |
+ // Set up alternative service for origin. |
+ session_deps_.use_alternate_protocols = true; |
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); |
+ base::WeakPtr<HttpServerProperties> http_server_properties = |
+ session->http_server_properties(); |
+ AlternativeService alternative_service( |
+ AlternateProtocolFromNextProto(GetParam()), alternative); |
+ http_server_properties->SetAlternativeService(origin, alternative_service, |
+ 1.0); |
+ |
+ // First transaction to alternative to open an HTTP/1.1 socket. |
+ scoped_ptr<HttpTransaction> trans1( |
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); |
+ HttpRequestInfo request1; |
+ request1.method = "GET"; |
+ request1.url = GURL(alternative_url); |
+ request1.load_flags = 0; |
+ TestCompletionCallback callback1; |
+ |
+ int rv = trans1->Start(&request1, callback1.callback(), BoundNetLog()); |
+ EXPECT_EQ(OK, callback1.GetResult(rv)); |
+ const HttpResponseInfo* response1 = trans1->GetResponseInfo(); |
+ ASSERT_TRUE(response1); |
+ ASSERT_TRUE(response1->headers.get()); |
+ EXPECT_EQ("HTTP/1.1 200 OK", response1->headers->GetStatusLine()); |
+ EXPECT_TRUE(response1->was_npn_negotiated); |
+ EXPECT_FALSE(response1->was_fetched_via_spdy); |
+ std::string response_data1; |
+ ASSERT_EQ(OK, ReadTransaction(trans1.get(), &response_data1)); |
+ EXPECT_EQ("first HTTP/1.1 response from alternative", response_data1); |
+ |
+ // Request for origin.example.org, which has an alternative service. This |
+ // will start two Jobs: the alternative looks for connections to pool to, |
+ // finds one which is HTTP/1.1, and should ignore it, and should not try to |
+ // open other connections to alternative server. The Job to origin fails, so |
+ // this request fails. |
+ scoped_ptr<HttpTransaction> trans2( |
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); |
+ HttpRequestInfo request2; |
+ request2.method = "GET"; |
+ request2.url = GURL(origin_url); |
+ request2.load_flags = 0; |
+ TestCompletionCallback callback2; |
+ |
+ rv = trans2->Start(&request2, callback2.callback(), BoundNetLog()); |
+ EXPECT_EQ(ERR_CONNECTION_REFUSED, callback2.GetResult(rv)); |
+ |
+ // Another transaction to alternative. This is to test that the HTTP/1.1 |
+ // socket is still open and in the pool. |
+ scoped_ptr<HttpTransaction> trans3( |
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); |
+ HttpRequestInfo request3; |
+ request3.method = "GET"; |
+ request3.url = GURL(alternative_url); |
+ request3.load_flags = 0; |
+ TestCompletionCallback callback3; |
+ |
+ rv = trans3->Start(&request3, callback3.callback(), BoundNetLog()); |
+ EXPECT_EQ(OK, callback3.GetResult(rv)); |
+ const HttpResponseInfo* response3 = trans3->GetResponseInfo(); |
+ ASSERT_TRUE(response3); |
+ ASSERT_TRUE(response3->headers.get()); |
+ EXPECT_EQ("HTTP/1.1 200 OK", response3->headers->GetStatusLine()); |
+ EXPECT_TRUE(response3->was_npn_negotiated); |
+ EXPECT_FALSE(response3->was_fetched_via_spdy); |
+ std::string response_data3; |
+ ASSERT_EQ(OK, ReadTransaction(trans3.get(), &response_data3)); |
+ EXPECT_EQ("second HTTP/1.1 response from alternative", response_data3); |
+} |
+ |
TEST_P(HttpNetworkTransactionTest, DoNotUseSpdySessionForHttpOverTunnel) { |
const std::string https_url = "https://www.example.org:8080/"; |
const std::string http_url = "http://www.example.org:8080/"; |