From c958faacab648d49cca246c247414ded926b3d8c Mon Sep 17 00:00:00 2001 From: bnc Date: Fri, 31 Jul 2015 11:14:52 -0700 Subject: [PATCH] Process Alt-Svc headers. Process Alt-Svc headers in HttpNetworkTransaction::DoReadHeadersComplete(). BUG=392576 Review URL: https://codereview.chromium.org/1254423004 Cr-Commit-Position: refs/heads/master@{#341368} --- net/http/http_network_transaction.cc | 22 +- net/http/http_network_transaction_unittest.cc | 270 ++++++++++++++++++++--- net/http/http_server_properties.cc | 1 + net/http/http_server_properties.h | 1 + net/http/http_stream_factory.cc | 56 ++++- net/http/http_stream_factory.h | 8 + net/quic/quic_network_transaction_unittest.cc | 306 +++++++++++++++++++++++++- 7 files changed, 614 insertions(+), 50 deletions(-) diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 6563c6ac508e..ac7dac576ec8 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -67,10 +67,20 @@ namespace net { namespace { -void ProcessAlternateProtocol( - HttpNetworkSession* session, - const HttpResponseHeaders& headers, - const HostPortPair& http_host_port_pair) { +void ProcessAlternativeServices(HttpNetworkSession* session, + const HttpResponseHeaders& headers, + const HostPortPair& http_host_port_pair) { + if (headers.HasHeader(kAlternativeServiceHeader)) { + std::string alternative_service_str; + headers.GetNormalizedHeader(kAlternativeServiceHeader, + &alternative_service_str); + session->http_stream_factory()->ProcessAlternativeService( + session->http_server_properties(), alternative_service_str, + http_host_port_pair, *session); + // If there is an "Alt-Svc" header, then ignore "Alternate-Protocol". + return; + } + if (!headers.HasHeader(kAlternateProtocolHeader)) return; @@ -1071,8 +1081,8 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) { return OK; } - ProcessAlternateProtocol(session_, *response_.headers.get(), - HostPortPair::FromURL(request_->url)); + ProcessAlternativeServices(session_, *response_.headers.get(), + HostPortPair::FromURL(request_->url)); int rv = HandleAuthChallenge(); if (rv != OK) diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index e7a6a3b70e2c..e4a638280994 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -288,10 +288,14 @@ class HttpNetworkTransactionTest AlternateProtocolToString(AlternateProtocolFromNextProto(GetParam())); } - // This is the expected return from a current server advertising SPDY. + std::string GetAlternativeServiceHttpHeader() { + return std::string("Alt-Svc: ") + GetAlternateProtocolFromParam() + + "=\"www.example.com:443\"\r\n"; + } + std::string GetAlternateProtocolHttpHeader() { return std::string("Alternate-Protocol: 443:") + - GetAlternateProtocolFromParam() + "\r\n\r\n"; + GetAlternateProtocolFromParam() + "\r\n"; } // Either |write_failure| specifies a write failure or |read_failure| @@ -8721,6 +8725,132 @@ TEST_P(HttpNetworkTransactionTest, ChangeAuthRealms) { EXPECT_TRUE(response->auth_challenge.get() == NULL); } +TEST_P(HttpNetworkTransactionTest, HonorAlternativeServiceHeader) { + session_deps_.next_protos = SpdyNextProtos(); + session_deps_.use_alternate_protocols = true; + + std::string alternative_service_http_header = + GetAlternativeServiceHttpHeader(); + + MockRead data_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead(alternative_service_http_header.c_str()), + MockRead("\r\n"), + MockRead("hello world"), + MockRead(SYNCHRONOUS, OK), + }; + + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.example.org/"); + request.load_flags = 0; + + StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0); + + session_deps_.socket_factory->AddSocketDataProvider(&data); + + TestCompletionCallback callback; + + scoped_refptr session(CreateSession(&session_deps_)); + scoped_ptr trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); + + int rv = trans->Start(&request, callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + HostPortPair http_host_port_pair("www.example.org", 80); + HttpServerProperties& http_server_properties = + *session->http_server_properties(); + AlternativeServiceVector alternative_service_vector = + http_server_properties.GetAlternativeServices(http_host_port_pair); + EXPECT_TRUE(alternative_service_vector.empty()); + + EXPECT_EQ(OK, callback.WaitForResult()); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + ASSERT_TRUE(response->headers.get() != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); + EXPECT_FALSE(response->was_fetched_via_spdy); + EXPECT_FALSE(response->was_npn_negotiated); + + std::string response_data; + ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); + EXPECT_EQ("hello world", response_data); + + alternative_service_vector = + http_server_properties.GetAlternativeServices(http_host_port_pair); + ASSERT_EQ(1u, alternative_service_vector.size()); + EXPECT_EQ(AlternateProtocolFromNextProto(GetParam()), + alternative_service_vector[0].protocol); + EXPECT_EQ("www.example.com", alternative_service_vector[0].host); + EXPECT_EQ(443, alternative_service_vector[0].port); +} + +TEST_P(HttpNetworkTransactionTest, HonorMultipleAlternativeServiceHeader) { + session_deps_.next_protos = SpdyNextProtos(); + session_deps_.use_alternate_protocols = true; + + MockRead data_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead("Alt-Svc: "), + MockRead(GetAlternateProtocolFromParam()), + MockRead("=\"www.example.com:443\";p=1.0,"), + MockRead("quic=\":1234\"\r\n\r\n"), + MockRead("hello world"), + MockRead(SYNCHRONOUS, OK), + }; + + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.example.org/"); + request.load_flags = 0; + + StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0); + + session_deps_.socket_factory->AddSocketDataProvider(&data); + + TestCompletionCallback callback; + + scoped_refptr session(CreateSession(&session_deps_)); + scoped_ptr trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); + + int rv = trans->Start(&request, callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + HostPortPair http_host_port_pair("www.example.org", 80); + HttpServerProperties& http_server_properties = + *session->http_server_properties(); + AlternativeServiceVector alternative_service_vector = + http_server_properties.GetAlternativeServices(http_host_port_pair); + EXPECT_TRUE(alternative_service_vector.empty()); + + EXPECT_EQ(OK, callback.WaitForResult()); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + ASSERT_TRUE(response->headers.get() != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); + EXPECT_FALSE(response->was_fetched_via_spdy); + EXPECT_FALSE(response->was_npn_negotiated); + + std::string response_data; + ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); + EXPECT_EQ("hello world", response_data); + + alternative_service_vector = + http_server_properties.GetAlternativeServices(http_host_port_pair); + ASSERT_EQ(2u, alternative_service_vector.size()); + EXPECT_EQ(AlternateProtocolFromNextProto(GetParam()), + alternative_service_vector[0].protocol); + EXPECT_EQ("www.example.com", alternative_service_vector[0].host); + EXPECT_EQ(443, alternative_service_vector[0].port); + EXPECT_EQ(QUIC, alternative_service_vector[1].protocol); + EXPECT_EQ("www.example.org", alternative_service_vector[1].host); + EXPECT_EQ(1234, alternative_service_vector[1].port); +} + TEST_P(HttpNetworkTransactionTest, HonorAlternateProtocolHeader) { session_deps_.next_protos = SpdyNextProtos(); session_deps_.use_alternate_protocols = true; @@ -8729,10 +8859,11 @@ TEST_P(HttpNetworkTransactionTest, HonorAlternateProtocolHeader) { GetAlternateProtocolHttpHeader(); MockRead data_reads[] = { - MockRead("HTTP/1.1 200 OK\r\n"), - MockRead(alternate_protocol_http_header.c_str()), - MockRead("hello world"), - MockRead(SYNCHRONOUS, OK), + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead(alternate_protocol_http_header.c_str()), + MockRead("\r\n"), + MockRead("hello world"), + MockRead(SYNCHRONOUS, OK), }; HttpRequestInfo request; @@ -8842,6 +8973,70 @@ TEST_P(HttpNetworkTransactionTest, EmptyAlternateProtocolHeader) { EXPECT_TRUE(alternative_service_vector.empty()); } +TEST_P(HttpNetworkTransactionTest, AltSvcOverwritesAlternateProtocol) { + session_deps_.next_protos = SpdyNextProtos(); + session_deps_.use_alternate_protocols = true; + + std::string alternative_service_http_header = + GetAlternativeServiceHttpHeader(); + std::string alternate_protocol_http_header = GetAlternateProtocolHttpHeader(); + + MockRead data_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead(alternative_service_http_header.c_str()), + MockRead(alternate_protocol_http_header.c_str()), + MockRead("\r\n"), + MockRead("hello world"), + MockRead(SYNCHRONOUS, OK), + }; + + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.example.org/"); + request.load_flags = 0; + + StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0); + + session_deps_.socket_factory->AddSocketDataProvider(&data); + + TestCompletionCallback callback; + + scoped_refptr session(CreateSession(&session_deps_)); + scoped_ptr trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); + + int rv = trans->Start(&request, callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + HostPortPair http_host_port_pair("www.example.org", 80); + HttpServerProperties& http_server_properties = + *session->http_server_properties(); + AlternativeServiceVector alternative_service_vector = + http_server_properties.GetAlternativeServices(http_host_port_pair); + EXPECT_TRUE(alternative_service_vector.empty()); + + EXPECT_EQ(OK, callback.WaitForResult()); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + ASSERT_TRUE(response->headers.get() != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); + EXPECT_FALSE(response->was_fetched_via_spdy); + EXPECT_FALSE(response->was_npn_negotiated); + + std::string response_data; + ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); + EXPECT_EQ("hello world", response_data); + + alternative_service_vector = + http_server_properties.GetAlternativeServices(http_host_port_pair); + ASSERT_EQ(1u, alternative_service_vector.size()); + EXPECT_EQ(AlternateProtocolFromNextProto(GetParam()), + alternative_service_vector[0].protocol); + EXPECT_EQ("www.example.com", alternative_service_vector[0].host); + EXPECT_EQ(443, alternative_service_vector[0].port); +} + TEST_P(HttpNetworkTransactionTest, MarkBrokenAlternateProtocolAndFallback) { session_deps_.use_alternate_protocols = true; @@ -9232,12 +9427,12 @@ TEST_P(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdy) { GetAlternateProtocolHttpHeader(); MockRead data_reads[] = { - MockRead("HTTP/1.1 200 OK\r\n"), - MockRead(alternate_protocol_http_header.c_str()), - MockRead("hello world"), - MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), - MockRead(ASYNC, OK) - }; + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead(alternate_protocol_http_header.c_str()), + MockRead("\r\n"), + MockRead("hello world"), + MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), + MockRead(ASYNC, OK)}; StaticSocketDataProvider first_transaction( data_reads, arraysize(data_reads), NULL, 0); @@ -9320,11 +9515,12 @@ TEST_P(HttpNetworkTransactionTest, AlternateProtocolWithSpdyLateBinding) { GetAlternateProtocolHttpHeader(); MockRead data_reads[] = { - MockRead("HTTP/1.1 200 OK\r\n"), - MockRead(alternate_protocol_http_header.c_str()), - MockRead("hello world"), - MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), - MockRead(ASYNC, OK), + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead(alternate_protocol_http_header.c_str()), + MockRead("\r\n"), + MockRead("hello world"), + MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), + MockRead(ASYNC, OK), }; StaticSocketDataProvider first_transaction( @@ -9436,11 +9632,12 @@ TEST_P(HttpNetworkTransactionTest, StallAlternateProtocolForNpnSpdy) { GetAlternateProtocolHttpHeader(); MockRead data_reads[] = { - MockRead("HTTP/1.1 200 OK\r\n"), - MockRead(alternate_protocol_http_header.c_str()), - MockRead("hello world"), - MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), - MockRead(ASYNC, OK), + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead(alternate_protocol_http_header.c_str()), + MockRead("\r\n"), + MockRead("hello world"), + MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), + MockRead(ASYNC, OK), }; StaticSocketDataProvider first_transaction( @@ -9575,11 +9772,12 @@ TEST_P(HttpNetworkTransactionTest, GetAlternateProtocolHttpHeader(); MockRead data_reads[] = { - MockRead("HTTP/1.1 200 OK\r\n"), - MockRead(alternate_protocol_http_header.c_str()), - MockRead("hello world"), - MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), - MockRead(ASYNC, OK), + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead(alternate_protocol_http_header.c_str()), + MockRead("\r\n"), + MockRead("hello world"), + MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), + MockRead(ASYNC, OK), }; StaticSocketDataProvider first_transaction( @@ -9687,10 +9885,11 @@ TEST_P(HttpNetworkTransactionTest, GetAlternateProtocolHttpHeader(); MockRead data_reads[] = { - MockRead("HTTP/1.1 200 OK\r\n"), - MockRead(alternate_protocol_http_header.c_str()), - MockRead("hello world"), - MockRead(ASYNC, OK), + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead(alternate_protocol_http_header.c_str()), + MockRead("\r\n"), + MockRead("hello world"), + MockRead(ASYNC, OK), }; StaticSocketDataProvider first_transaction( @@ -10393,10 +10592,11 @@ TEST_P(HttpNetworkTransactionTest, NpnWithHttpOverSSL) { GetAlternateProtocolHttpHeader(); MockRead data_reads[] = { - MockRead("HTTP/1.1 200 OK\r\n"), - MockRead(alternate_protocol_http_header.c_str()), - MockRead("hello world"), - MockRead(SYNCHRONOUS, OK), + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead(alternate_protocol_http_header.c_str()), + MockRead("\r\n"), + MockRead("hello world"), + MockRead(SYNCHRONOUS, OK), }; SSLSocketDataProvider ssl(ASYNC, OK); diff --git a/net/http/http_server_properties.cc b/net/http/http_server_properties.cc index 8d4ef9473178..08059c27466b 100644 --- a/net/http/http_server_properties.cc +++ b/net/http/http_server_properties.cc @@ -13,6 +13,7 @@ namespace net { const char kAlternateProtocolHeader[] = "Alternate-Protocol"; +const char kAlternativeServiceHeader[] = "Alt-Svc"; namespace { diff --git a/net/http/http_server_properties.h b/net/http/http_server_properties.h index 9c0c221bbf81..4668937a2337 100644 --- a/net/http/http_server_properties.h +++ b/net/http/http_server_properties.h @@ -216,6 +216,7 @@ typedef base::MRUCache SpdySettingsMap; typedef base::MRUCache ServerNetworkStatsMap; extern const char kAlternateProtocolHeader[]; +extern const char kAlternativeServiceHeader[]; // The interface for setting/retrieving the HTTP server properties. // Currently, this class manages servers': diff --git a/net/http/http_stream_factory.cc b/net/http/http_stream_factory.cc index 97e0243435b4..1b6e66fcceff 100644 --- a/net/http/http_stream_factory.cc +++ b/net/http/http_stream_factory.cc @@ -13,6 +13,7 @@ #include "net/base/host_port_pair.h" #include "net/base/port_util.h" #include "net/http/http_network_session.h" +#include "net/spdy/spdy_alt_svc_wire_format.h" #include "url/gurl.h" namespace net { @@ -30,6 +31,44 @@ void HttpStreamFactory::ResetStaticSettingsToInit() { spdy_enabled_ = true; } +void HttpStreamFactory::ProcessAlternativeService( + const base::WeakPtr& http_server_properties, + base::StringPiece alternative_service_str, + const HostPortPair& http_host_port_pair, + const HttpNetworkSession& session) { + SpdyAltSvcWireFormat::AlternativeServiceVector alternative_service_vector; + if (!SpdyAltSvcWireFormat::ParseHeaderFieldValue( + alternative_service_str, &alternative_service_vector)) { + return; + } + + // Convert SpdyAltSvcWireFormat::AlternativeService entries + // to net::AlternativeServiceInfo. + AlternativeServiceInfoVector alternative_service_info_vector; + for (const SpdyAltSvcWireFormat::AlternativeService& + alternative_service_entry : alternative_service_vector) { + AlternateProtocol protocol = + AlternateProtocolFromString(alternative_service_entry.protocol_id); + if (!IsAlternateProtocolValid(protocol) || + !session.IsProtocolEnabled(protocol) || + !IsPortValid(alternative_service_entry.port)) { + continue; + } + AlternativeService alternative_service(protocol, + alternative_service_entry.host, + alternative_service_entry.port); + base::Time expiration = + base::Time::Now() + + base::TimeDelta::FromSeconds(alternative_service_entry.max_age); + AlternativeServiceInfo alternative_service_info( + alternative_service, alternative_service_entry.p, expiration); + alternative_service_info_vector.push_back(alternative_service_info); + } + + http_server_properties->SetAlternativeServices( + RewriteHost(http_host_port_pair), alternative_service_info_vector); +} + void HttpStreamFactory::ProcessAlternateProtocol( const base::WeakPtr& http_server_properties, const std::vector& alternate_protocol_values, @@ -92,14 +131,10 @@ void HttpStreamFactory::ProcessAlternateProtocol( return; } - HostPortPair host_port(http_host_port_pair); - const HostMappingRules* mapping_rules = GetHostMappingRules(); - if (mapping_rules) - mapping_rules->RewriteHost(&host_port); - http_server_properties->SetAlternativeService( - host_port, AlternativeService(protocol, "", static_cast(port)), - probability, base::Time::Now() + base::TimeDelta::FromDays(1)); + RewriteHost(http_host_port_pair), + AlternativeService(protocol, "", static_cast(port)), probability, + base::Time::Now() + base::TimeDelta::FromDays(1)); } GURL HttpStreamFactory::ApplyHostMappingRules(const GURL& url, @@ -118,4 +153,11 @@ GURL HttpStreamFactory::ApplyHostMappingRules(const GURL& url, HttpStreamFactory::HttpStreamFactory() {} +HostPortPair HttpStreamFactory::RewriteHost(HostPortPair host_port_pair) { + const HostMappingRules* mapping_rules = GetHostMappingRules(); + if (mapping_rules) + mapping_rules->RewriteHost(&host_port_pair); + return host_port_pair; +} + } // namespace net diff --git a/net/http/http_stream_factory.h b/net/http/http_stream_factory.h index 3d92a83b31b7..231271dffe70 100644 --- a/net/http/http_stream_factory.h +++ b/net/http/http_stream_factory.h @@ -184,6 +184,12 @@ class NET_EXPORT HttpStreamFactory { public: virtual ~HttpStreamFactory(); + void ProcessAlternativeService( + const base::WeakPtr& http_server_properties, + base::StringPiece alternative_service_str, + const HostPortPair& http_host_port_pair, + const HttpNetworkSession& session); + void ProcessAlternateProtocol( const base::WeakPtr& http_server_properties, const std::vector& alternate_protocol_values, @@ -243,6 +249,8 @@ class NET_EXPORT HttpStreamFactory { private: static bool spdy_enabled_; + HostPortPair RewriteHost(HostPortPair host_port_pair); + DISALLOW_COPY_AND_ASSIGN(HttpStreamFactory); }; diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc index 08a0eae8ee2f..d6ba1061e800 100644 --- a/net/quic/quic_network_transaction_unittest.cc +++ b/net/quic/quic_network_transaction_unittest.cc @@ -53,7 +53,6 @@ namespace test { namespace { -// This is the expected return from a current server advertising QUIC. static const char kQuicAlternateProtocolHttpHeader[] = "Alternate-Protocol: 80:quic\r\n\r\n"; static const char kQuicAlternateProtocol50pctHttpHeader[] = @@ -62,6 +61,14 @@ static const char kQuicAlternateProtocolDifferentPortHttpHeader[] = "Alternate-Protocol: 137:quic\r\n\r\n"; static const char kQuicAlternateProtocolHttpsHeader[] = "Alternate-Protocol: 443:quic\r\n\r\n"; +static const char kQuicAlternativeServiceHttpHeader[] = + "Alt-Svc: quic=\":80\"\r\n\r\n"; +static const char kQuicAlternativeService50pctHttpHeader[] = + "Alt-Svc: quic=\":80\";p=.5\r\n\r\n"; +static const char kQuicAlternativeServiceDifferentPortHttpHeader[] = + "Alt-Svc: quic=\":137\"\r\n\r\n"; +static const char kQuicAlternativeServiceHttpsHeader[] = + "Alt-Svc: quic=\":443\"\r\n\r\n"; const char kDefaultServerHostName[] = "www.google.com"; @@ -573,6 +580,301 @@ TEST_P(QuicNetworkTransactionTest, DoNotForceQuicForHttps) { SendRequestAndExpectHttpResponse("hello world"); } +TEST_P(QuicNetworkTransactionTest, UseAlternativeServiceForQuic) { + params_.enable_insecure_quic = true; + + MockRead http_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead(kQuicAlternativeServiceHttpHeader), MockRead("hello world"), + MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), + MockRead(ASYNC, OK)}; + + StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), nullptr, + 0); + socket_factory_.AddSocketDataProvider(&http_data); + + MockQuicData mock_quic_data; + mock_quic_data.AddWrite( + ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true, + GetRequestHeaders("GET", "http", "/"))); + mock_quic_data.AddRead(ConstructResponseHeadersPacket( + 1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK"))); + mock_quic_data.AddRead( + ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!")); + mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); + mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING); // No more data to read + mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF + + mock_quic_data.AddSocketDataToFactory(&socket_factory_); + + AddHangingNonAlternateProtocolSocketData(); + CreateSessionWithNextProtos(); + + SendRequestAndExpectHttpResponse("hello world"); + SendRequestAndExpectQuicResponse("hello!"); +} + +// When multiple alternative services are advertised, +// HttpStreamFactoryImpl::RequestStreamInternal() only passes the first one to +// Job. This is what the following test verifies. +// TODO(bnc): Update this test when multiple alternative services are handled +// properly. +TEST_P(QuicNetworkTransactionTest, UseFirstAlternativeServiceForQuic) { + params_.enable_insecure_quic = true; + + MockRead http_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead("Alt-Svc: quic=\":443\", quic=\":1234\"\r\n\r\n"), + MockRead("hello world"), + MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), + MockRead(ASYNC, OK)}; + + StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), nullptr, + 0); + socket_factory_.AddSocketDataProvider(&http_data); + + MockQuicData mock_quic_data; + mock_quic_data.AddWrite( + ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true, + GetRequestHeaders("GET", "http", "/"))); + mock_quic_data.AddRead(ConstructResponseHeadersPacket( + 1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK"))); + mock_quic_data.AddRead( + ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!")); + mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); + mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING); // No more data to read + mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF + + mock_quic_data.AddSocketDataToFactory(&socket_factory_); + + AddHangingNonAlternateProtocolSocketData(); + CreateSessionWithNextProtos(); + + SendRequestAndExpectHttpResponse("hello world"); + SendRequestAndExpectQuicResponseOnPort("hello!", 443); +} + +TEST_P(QuicNetworkTransactionTest, AlternativeServiceDifferentPort) { + params_.enable_insecure_quic = true; + + MockRead http_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead(kQuicAlternativeServiceDifferentPortHttpHeader), + MockRead("hello world"), + MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), + MockRead(ASYNC, OK)}; + + StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), nullptr, + 0); + socket_factory_.AddSocketDataProvider(&http_data); + + MockQuicData mock_quic_data; + mock_quic_data.AddWrite( + ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true, + GetRequestHeaders("GET", "http", "/"))); + mock_quic_data.AddRead(ConstructResponseHeadersPacket( + 1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK"))); + mock_quic_data.AddRead( + ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!")); + mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); + mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING); // No more data to read + mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF + + mock_quic_data.AddSocketDataToFactory(&socket_factory_); + + AddHangingNonAlternateProtocolSocketData(); + CreateSessionWithNextProtos(); + + SendRequestAndExpectHttpResponse("hello world"); + SendRequestAndExpectQuicResponseOnPort("hello!", 137); +} + +TEST_P(QuicNetworkTransactionTest, ConfirmAlternativeService) { + params_.enable_insecure_quic = true; + + MockRead http_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead(kQuicAlternativeServiceHttpHeader), MockRead("hello world"), + MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), + MockRead(ASYNC, OK)}; + + StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), nullptr, + 0); + socket_factory_.AddSocketDataProvider(&http_data); + + MockQuicData mock_quic_data; + mock_quic_data.AddWrite( + ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true, + GetRequestHeaders("GET", "http", "/"))); + mock_quic_data.AddRead(ConstructResponseHeadersPacket( + 1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK"))); + mock_quic_data.AddRead( + ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!")); + mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); + mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING); // No more data to read + mock_quic_data.AddRead(ASYNC, 0); // EOF + mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF + + mock_quic_data.AddSocketDataToFactory(&socket_factory_); + + AddHangingNonAlternateProtocolSocketData(); + CreateSessionWithNextProtos(); + + AlternativeService alternative_service(QUIC, + HostPortPair::FromURL(request_.url)); + http_server_properties_.MarkAlternativeServiceRecentlyBroken( + alternative_service); + EXPECT_TRUE(http_server_properties_.WasAlternativeServiceRecentlyBroken( + alternative_service)); + + SendRequestAndExpectHttpResponse("hello world"); + SendRequestAndExpectQuicResponse("hello!"); + + mock_quic_data.CompleteRead(); + + EXPECT_FALSE(http_server_properties_.WasAlternativeServiceRecentlyBroken( + alternative_service)); +} + +TEST_P(QuicNetworkTransactionTest, UseAlternativeServiceProbabilityForQuic) { + params_.enable_insecure_quic = true; + + MockRead http_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead(kQuicAlternativeService50pctHttpHeader), MockRead("hello world"), + MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), + MockRead(ASYNC, OK)}; + + StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), nullptr, + 0); + socket_factory_.AddSocketDataProvider(&http_data); + + MockQuicData mock_quic_data; + mock_quic_data.AddWrite( + ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true, + GetRequestHeaders("GET", "http", "/"))); + mock_quic_data.AddRead(ConstructResponseHeadersPacket( + 1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK"))); + mock_quic_data.AddRead( + ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!")); + mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); + mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING); // No more data to read + mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF + + mock_quic_data.AddSocketDataToFactory(&socket_factory_); + + AddHangingNonAlternateProtocolSocketData(); + params_.alternative_service_probability_threshold = 0.25; + CreateSessionWithNextProtos(); + + SendRequestAndExpectHttpResponse("hello world"); + SendRequestAndExpectQuicResponse("hello!"); +} + +TEST_P(QuicNetworkTransactionTest, + DontUseAlternativeServiceProbabilityForQuic) { + params_.enable_insecure_quic = true; + + MockRead http_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead(kQuicAlternativeService50pctHttpHeader), MockRead("hello world"), + MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), + MockRead(ASYNC, OK)}; + + StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), nullptr, + 0); + socket_factory_.AddSocketDataProvider(&http_data); + socket_factory_.AddSocketDataProvider(&http_data); + + params_.alternative_service_probability_threshold = 0.75; + CreateSessionWithNextProtos(); + + SendRequestAndExpectHttpResponse("hello world"); + SendRequestAndExpectHttpResponse("hello world"); +} + +TEST_P(QuicNetworkTransactionTest, DontUseAlternativeServiceForInsecureQuic) { + params_.enable_insecure_quic = true; + + MockRead http_reads[] = {MockRead("HTTP/1.1 200 OK\r\n"), + MockRead("Content-length: 11\r\n"), + MockRead("Alt-Svc: quic=\":443\"\r\n\r\n"), + MockRead("hello world"), + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead("Content-length: 11\r\n"), + MockRead("Alt-Svc: quic=\":443\"\r\n\r\n"), + MockRead("hello world"), + MockRead(ASYNC, OK)}; + + StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), nullptr, + 0); + socket_factory_.AddSocketDataProvider(&http_data); + socket_factory_.AddSocketDataProvider(&http_data); + + params_.enable_insecure_quic = false; + CreateSessionWithNextProtos(); + + SendRequestAndExpectHttpResponse("hello world"); + SendRequestAndExpectHttpResponse("hello world"); +} + +TEST_P(QuicNetworkTransactionTest, + DontUseAlternativeServiceWithBadProbabilityForQuic) { + params_.enable_insecure_quic = true; + + MockRead http_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead("Alt-Svc: quic=\":443\";p=2\r\n\r\n"), MockRead("hello world"), + MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), + MockRead(ASYNC, OK)}; + + StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), nullptr, + 0); + socket_factory_.AddSocketDataProvider(&http_data); + socket_factory_.AddSocketDataProvider(&http_data); + + params_.alternative_service_probability_threshold = 0.75; + CreateSessionWithNextProtos(); + + SendRequestAndExpectHttpResponse("hello world"); + SendRequestAndExpectHttpResponse("hello world"); +} + +TEST_P(QuicNetworkTransactionTest, UseAlternativeServiceForQuicForHttps) { + params_.enable_insecure_quic = true; + params_.origin_to_force_quic_on = + HostPortPair::FromString("www.google.com:443"); + + MockRead http_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead(kQuicAlternativeServiceHttpsHeader), MockRead("hello world"), + MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), + MockRead(ASYNC, OK)}; + + StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), nullptr, + 0); + socket_factory_.AddSocketDataProvider(&http_data); + + MockQuicData mock_quic_data; + mock_quic_data.AddWrite( + ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true, + GetRequestHeaders("GET", "http", "/"))); + mock_quic_data.AddRead(ConstructResponseHeadersPacket( + 1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK"))); + mock_quic_data.AddRead( + ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!")); + mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); + mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF + + mock_quic_data.AddSocketDataToFactory(&socket_factory_); + + AddHangingNonAlternateProtocolSocketData(); + CreateSessionWithNextProtos(); + + // TODO(rtenneti): Test QUIC over HTTPS, GetSSLInfo(). + SendRequestAndExpectHttpResponse("hello world"); +} + TEST_P(QuicNetworkTransactionTest, UseAlternateProtocolForQuic) { params_.enable_insecure_quic = true; @@ -651,7 +953,7 @@ TEST_P(QuicNetworkTransactionTest, AlternateProtocolDifferentPort) { SendRequestAndExpectQuicResponseOnPort("hello!", 137); } -TEST_P(QuicNetworkTransactionTest, ConfirmAlternativeService) { +TEST_P(QuicNetworkTransactionTest, ConfirmAlternateProtocol) { params_.enable_insecure_quic = true; MockRead http_reads[] = { -- 2.11.4.GIT