// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/http/http_stream_factory_impl.h" #include #include #include #include #include "base/compiler_specific.h" #include "base/macros.h" #include "base/run_loop.h" #include "net/base/port_util.h" #include "net/base/test_completion_callback.h" #include "net/base/test_data_directory.h" #include "net/cert/mock_cert_verifier.h" #include "net/dns/mock_host_resolver.h" #include "net/http/bidirectional_stream_impl.h" #include "net/http/bidirectional_stream_request_info.h" #include "net/http/http_auth_handler_factory.h" #include "net/http/http_network_session.h" #include "net/http/http_network_session_peer.h" #include "net/http/http_network_transaction.h" #include "net/http/http_request_info.h" #include "net/http/http_server_properties.h" #include "net/http/http_server_properties_impl.h" #include "net/http/http_stream.h" #include "net/http/transport_security_state.h" #include "net/log/net_log.h" #include "net/proxy/proxy_info.h" #include "net/proxy/proxy_service.h" #include "net/quic/quic_http_utils.h" #include "net/quic/quic_server_id.h" #include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/mock_crypto_client_stream_factory.h" #include "net/quic/test_tools/mock_random.h" #include "net/quic/test_tools/quic_stream_factory_peer.h" #include "net/quic/test_tools/quic_test_packet_maker.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/socket/client_socket_handle.h" #include "net/socket/mock_client_socket_pool_manager.h" #include "net/socket/next_proto.h" #include "net/socket/socket_test_util.h" #include "net/spdy/spdy_session.h" #include "net/spdy/spdy_session_pool.h" #include "net/spdy/spdy_test_util_common.h" #include "net/ssl/ssl_config_service.h" #include "net/ssl/ssl_config_service_defaults.h" #include "net/test/cert_test_util.h" // This file can be included from net/http even though // it is in net/websockets because it doesn't // introduce any link dependency to net/websockets. #include "net/websockets/websocket_handshake_stream_base.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { class BidirectionalStreamImpl; namespace { class MockWebSocketHandshakeStream : public WebSocketHandshakeStreamBase { public: enum StreamType { kStreamTypeBasic, kStreamTypeSpdy, }; explicit MockWebSocketHandshakeStream(StreamType type) : type_(type) {} ~MockWebSocketHandshakeStream() override {} StreamType type() const { return type_; } // HttpStream methods int InitializeStream(const HttpRequestInfo* request_info, RequestPriority priority, const BoundNetLog& net_log, const CompletionCallback& callback) override { return ERR_IO_PENDING; } int SendRequest(const HttpRequestHeaders& request_headers, HttpResponseInfo* response, const CompletionCallback& callback) override { return ERR_IO_PENDING; } int ReadResponseHeaders(const CompletionCallback& callback) override { return ERR_IO_PENDING; } int ReadResponseBody(IOBuffer* buf, int buf_len, const CompletionCallback& callback) override { return ERR_IO_PENDING; } void Close(bool not_reusable) override {} bool IsResponseBodyComplete() const override { return false; } bool IsConnectionReused() const override { return false; } void SetConnectionReused() override {} bool CanReuseConnection() const override { return false; } int64_t GetTotalReceivedBytes() const override { return 0; } int64_t GetTotalSentBytes() const override { return 0; } bool GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const override { return false; } void GetSSLInfo(SSLInfo* ssl_info) override {} void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info) override {} bool GetRemoteEndpoint(IPEndPoint* endpoint) override { return false; } Error GetSignedEKMForTokenBinding(crypto::ECPrivateKey* key, std::vector* out) override { ADD_FAILURE(); return ERR_NOT_IMPLEMENTED; } void Drain(HttpNetworkSession* session) override {} void PopulateNetErrorDetails(NetErrorDetails* details) override { return; } void SetPriority(RequestPriority priority) override {} UploadProgress GetUploadProgress() const override { return UploadProgress(); } HttpStream* RenewStreamForAuth() override { return nullptr; } scoped_ptr Upgrade() override { return scoped_ptr(); } private: const StreamType type_; }; // HttpStreamFactoryImpl subclass that can wait until a preconnect is complete. class MockHttpStreamFactoryImplForPreconnect : public HttpStreamFactoryImpl { public: MockHttpStreamFactoryImplForPreconnect(HttpNetworkSession* session) : HttpStreamFactoryImpl(session, false), preconnect_done_(false), waiting_for_preconnect_(false) {} void WaitForPreconnects() { while (!preconnect_done_) { waiting_for_preconnect_ = true; base::MessageLoop::current()->Run(); waiting_for_preconnect_ = false; } } private: // HttpStreamFactoryImpl methods. void OnPreconnectsCompleteInternal() override { preconnect_done_ = true; if (waiting_for_preconnect_) base::MessageLoop::current()->QuitWhenIdle(); } bool preconnect_done_; bool waiting_for_preconnect_; }; class StreamRequestWaiter : public HttpStreamRequest::Delegate { public: StreamRequestWaiter() : waiting_for_stream_(false), stream_done_(false), error_status_(OK) {} // HttpStreamRequest::Delegate void OnStreamReady(const SSLConfig& used_ssl_config, const ProxyInfo& used_proxy_info, HttpStream* stream) override { stream_done_ = true; if (waiting_for_stream_) base::MessageLoop::current()->QuitWhenIdle(); stream_.reset(stream); used_ssl_config_ = used_ssl_config; used_proxy_info_ = used_proxy_info; } void OnWebSocketHandshakeStreamReady( const SSLConfig& used_ssl_config, const ProxyInfo& used_proxy_info, WebSocketHandshakeStreamBase* stream) override { stream_done_ = true; if (waiting_for_stream_) base::MessageLoop::current()->QuitWhenIdle(); websocket_stream_.reset(stream); used_ssl_config_ = used_ssl_config; used_proxy_info_ = used_proxy_info; } void OnBidirectionalStreamImplReady( const SSLConfig& used_ssl_config, const ProxyInfo& used_proxy_info, BidirectionalStreamImpl* stream) override { stream_done_ = true; if (waiting_for_stream_) base::MessageLoop::current()->QuitWhenIdle(); bidirectional_stream_impl_.reset(stream); used_ssl_config_ = used_ssl_config; used_proxy_info_ = used_proxy_info; } void OnStreamFailed(int status, const SSLConfig& used_ssl_config, SSLFailureState ssl_failure_state) override { stream_done_ = true; if (waiting_for_stream_) base::MessageLoop::current()->QuitWhenIdle(); used_ssl_config_ = used_ssl_config; error_status_ = status; } void OnCertificateError(int status, const SSLConfig& used_ssl_config, const SSLInfo& ssl_info) override {} void OnNeedsProxyAuth(const HttpResponseInfo& proxy_response, const SSLConfig& used_ssl_config, const ProxyInfo& used_proxy_info, HttpAuthController* auth_controller) override {} void OnNeedsClientAuth(const SSLConfig& used_ssl_config, SSLCertRequestInfo* cert_info) override {} void OnHttpsProxyTunnelResponse(const HttpResponseInfo& response_info, const SSLConfig& used_ssl_config, const ProxyInfo& used_proxy_info, HttpStream* stream) override {} void OnQuicBroken() override {} void WaitForStream() { while (!stream_done_) { waiting_for_stream_ = true; base::MessageLoop::current()->Run(); waiting_for_stream_ = false; } } const SSLConfig& used_ssl_config() const { return used_ssl_config_; } const ProxyInfo& used_proxy_info() const { return used_proxy_info_; } HttpStream* stream() { return stream_.get(); } MockWebSocketHandshakeStream* websocket_stream() { return static_cast(websocket_stream_.get()); } BidirectionalStreamImpl* bidirectional_stream_impl() { return bidirectional_stream_impl_.get(); } bool stream_done() const { return stream_done_; } int error_status() const { return error_status_; } private: bool waiting_for_stream_; bool stream_done_; scoped_ptr stream_; scoped_ptr websocket_stream_; scoped_ptr bidirectional_stream_impl_; SSLConfig used_ssl_config_; ProxyInfo used_proxy_info_; int error_status_; DISALLOW_COPY_AND_ASSIGN(StreamRequestWaiter); }; class WebSocketSpdyHandshakeStream : public MockWebSocketHandshakeStream { public: explicit WebSocketSpdyHandshakeStream( const base::WeakPtr& spdy_session) : MockWebSocketHandshakeStream(kStreamTypeSpdy), spdy_session_(spdy_session) {} ~WebSocketSpdyHandshakeStream() override {} SpdySession* spdy_session() { return spdy_session_.get(); } private: base::WeakPtr spdy_session_; }; class WebSocketBasicHandshakeStream : public MockWebSocketHandshakeStream { public: explicit WebSocketBasicHandshakeStream( scoped_ptr connection) : MockWebSocketHandshakeStream(kStreamTypeBasic), connection_(std::move(connection)) {} ~WebSocketBasicHandshakeStream() override { connection_->socket()->Disconnect(); } ClientSocketHandle* connection() { return connection_.get(); } private: scoped_ptr connection_; }; class WebSocketStreamCreateHelper : public WebSocketHandshakeStreamBase::CreateHelper { public: ~WebSocketStreamCreateHelper() override {} WebSocketHandshakeStreamBase* CreateBasicStream( scoped_ptr connection, bool using_proxy) override { return new WebSocketBasicHandshakeStream(std::move(connection)); } WebSocketHandshakeStreamBase* CreateSpdyStream( const base::WeakPtr& spdy_session, bool use_relative_url) override { return new WebSocketSpdyHandshakeStream(spdy_session); } }; struct TestCase { int num_streams; bool ssl; }; TestCase kTests[] = { { 1, false }, { 2, false }, { 1, true}, { 2, true}, }; void PreconnectHelperForURL(int num_streams, const GURL& url, HttpNetworkSession* session) { HttpNetworkSessionPeer peer(session); MockHttpStreamFactoryImplForPreconnect* mock_factory = new MockHttpStreamFactoryImplForPreconnect(session); peer.SetHttpStreamFactory(scoped_ptr(mock_factory)); SSLConfig ssl_config; session->ssl_config_service()->GetSSLConfig(&ssl_config); HttpRequestInfo request; request.method = "GET"; request.url = url; request.load_flags = 0; session->http_stream_factory()->PreconnectStreams(num_streams, request, ssl_config, ssl_config); mock_factory->WaitForPreconnects(); } void PreconnectHelper(const TestCase& test, HttpNetworkSession* session) { GURL url = test.ssl ? GURL("https://www.google.com") : GURL("http://www.google.com"); PreconnectHelperForURL(test.num_streams, url, session); } template class CapturePreconnectsSocketPool : public ParentPool { public: CapturePreconnectsSocketPool(HostResolver* host_resolver, CertVerifier* cert_verifier); int last_num_streams() const { return last_num_streams_; } int RequestSocket(const std::string& group_name, const void* socket_params, RequestPriority priority, ClientSocketPool::RespectLimits respect_limits, ClientSocketHandle* handle, const CompletionCallback& callback, const BoundNetLog& net_log) override { ADD_FAILURE(); return ERR_UNEXPECTED; } void RequestSockets(const std::string& group_name, const void* socket_params, int num_sockets, const BoundNetLog& net_log) override { last_num_streams_ = num_sockets; } void CancelRequest(const std::string& group_name, ClientSocketHandle* handle) override { ADD_FAILURE(); } void ReleaseSocket(const std::string& group_name, scoped_ptr socket, int id) override { ADD_FAILURE(); } void CloseIdleSockets() override { ADD_FAILURE(); } int IdleSocketCount() const override { ADD_FAILURE(); return 0; } int IdleSocketCountInGroup(const std::string& group_name) const override { ADD_FAILURE(); return 0; } LoadState GetLoadState(const std::string& group_name, const ClientSocketHandle* handle) const override { ADD_FAILURE(); return LOAD_STATE_IDLE; } base::TimeDelta ConnectionTimeout() const override { return base::TimeDelta(); } private: int last_num_streams_; }; typedef CapturePreconnectsSocketPool CapturePreconnectsTransportSocketPool; typedef CapturePreconnectsSocketPool CapturePreconnectsHttpProxySocketPool; typedef CapturePreconnectsSocketPool CapturePreconnectsSOCKSSocketPool; typedef CapturePreconnectsSocketPool CapturePreconnectsSSLSocketPool; template CapturePreconnectsSocketPool::CapturePreconnectsSocketPool( HostResolver* host_resolver, CertVerifier* /* cert_verifier */) : ParentPool(0, 0, host_resolver, nullptr, nullptr), last_num_streams_(-1) { } template <> CapturePreconnectsHttpProxySocketPool::CapturePreconnectsSocketPool( HostResolver* /* host_resolver */, CertVerifier* /* cert_verifier */) : HttpProxyClientSocketPool(0, 0, nullptr, nullptr, nullptr), last_num_streams_(-1) { } template <> CapturePreconnectsSSLSocketPool::CapturePreconnectsSocketPool( HostResolver* /* host_resolver */, CertVerifier* cert_verifier) : SSLClientSocketPool(0, 0, cert_verifier, nullptr, // channel_id_store nullptr, // transport_security_state nullptr, // cert_transparency_verifier nullptr, // ct_policy_enforcer std::string(), // ssl_session_cache_shard nullptr, // deterministic_socket_factory nullptr, // transport_socket_pool nullptr, nullptr, nullptr, // ssl_config_service nullptr), // net_log last_num_streams_(-1) { } class HttpStreamFactoryTest : public ::testing::Test, public ::testing::WithParamInterface { }; INSTANTIATE_TEST_CASE_P(NextProto, HttpStreamFactoryTest, testing::Values(kProtoSPDY31, kProtoHTTP2)); TEST_P(HttpStreamFactoryTest, PreconnectDirect) { for (size_t i = 0; i < arraysize(kTests); ++i) { SpdySessionDependencies session_deps( GetParam(), ProxyService::CreateDirect()); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); HttpNetworkSessionPeer peer(session.get()); CapturePreconnectsTransportSocketPool* transport_conn_pool = new CapturePreconnectsTransportSocketPool( session_deps.host_resolver.get(), session_deps.cert_verifier.get()); CapturePreconnectsSSLSocketPool* ssl_conn_pool = new CapturePreconnectsSSLSocketPool( session_deps.host_resolver.get(), session_deps.cert_verifier.get()); scoped_ptr mock_pool_manager( new MockClientSocketPoolManager); mock_pool_manager->SetTransportSocketPool(transport_conn_pool); mock_pool_manager->SetSSLSocketPool(ssl_conn_pool); peer.SetClientSocketPoolManager(std::move(mock_pool_manager)); PreconnectHelper(kTests[i], session.get()); if (kTests[i].ssl) EXPECT_EQ(kTests[i].num_streams, ssl_conn_pool->last_num_streams()); else EXPECT_EQ(kTests[i].num_streams, transport_conn_pool->last_num_streams()); } } TEST_P(HttpStreamFactoryTest, PreconnectHttpProxy) { for (size_t i = 0; i < arraysize(kTests); ++i) { SpdySessionDependencies session_deps( GetParam(), ProxyService::CreateFixed("http_proxy")); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); HttpNetworkSessionPeer peer(session.get()); HostPortPair proxy_host("http_proxy", 80); CapturePreconnectsHttpProxySocketPool* http_proxy_pool = new CapturePreconnectsHttpProxySocketPool( session_deps.host_resolver.get(), session_deps.cert_verifier.get()); CapturePreconnectsSSLSocketPool* ssl_conn_pool = new CapturePreconnectsSSLSocketPool( session_deps.host_resolver.get(), session_deps.cert_verifier.get()); scoped_ptr mock_pool_manager( new MockClientSocketPoolManager); mock_pool_manager->SetSocketPoolForHTTPProxy(proxy_host, http_proxy_pool); mock_pool_manager->SetSocketPoolForSSLWithProxy(proxy_host, ssl_conn_pool); peer.SetClientSocketPoolManager(std::move(mock_pool_manager)); PreconnectHelper(kTests[i], session.get()); if (kTests[i].ssl) EXPECT_EQ(kTests[i].num_streams, ssl_conn_pool->last_num_streams()); else EXPECT_EQ(kTests[i].num_streams, http_proxy_pool->last_num_streams()); } } TEST_P(HttpStreamFactoryTest, PreconnectSocksProxy) { for (size_t i = 0; i < arraysize(kTests); ++i) { SpdySessionDependencies session_deps( GetParam(), ProxyService::CreateFixed("socks4://socks_proxy:1080")); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); HttpNetworkSessionPeer peer(session.get()); HostPortPair proxy_host("socks_proxy", 1080); CapturePreconnectsSOCKSSocketPool* socks_proxy_pool = new CapturePreconnectsSOCKSSocketPool( session_deps.host_resolver.get(), session_deps.cert_verifier.get()); CapturePreconnectsSSLSocketPool* ssl_conn_pool = new CapturePreconnectsSSLSocketPool( session_deps.host_resolver.get(), session_deps.cert_verifier.get()); scoped_ptr mock_pool_manager( new MockClientSocketPoolManager); mock_pool_manager->SetSocketPoolForSOCKSProxy(proxy_host, socks_proxy_pool); mock_pool_manager->SetSocketPoolForSSLWithProxy(proxy_host, ssl_conn_pool); peer.SetClientSocketPoolManager(std::move(mock_pool_manager)); PreconnectHelper(kTests[i], session.get()); if (kTests[i].ssl) EXPECT_EQ(kTests[i].num_streams, ssl_conn_pool->last_num_streams()); else EXPECT_EQ(kTests[i].num_streams, socks_proxy_pool->last_num_streams()); } } TEST_P(HttpStreamFactoryTest, PreconnectDirectWithExistingSpdySession) { for (size_t i = 0; i < arraysize(kTests); ++i) { SpdySessionDependencies session_deps( GetParam(), ProxyService::CreateDirect()); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); HttpNetworkSessionPeer peer(session.get()); // Put a SpdySession in the pool. HostPortPair host_port_pair("www.google.com", 443); SpdySessionKey key(host_port_pair, ProxyServer::Direct(), PRIVACY_MODE_DISABLED); ignore_result(CreateFakeSpdySession(session->spdy_session_pool(), key)); CapturePreconnectsTransportSocketPool* transport_conn_pool = new CapturePreconnectsTransportSocketPool( session_deps.host_resolver.get(), session_deps.cert_verifier.get()); CapturePreconnectsSSLSocketPool* ssl_conn_pool = new CapturePreconnectsSSLSocketPool( session_deps.host_resolver.get(), session_deps.cert_verifier.get()); scoped_ptr mock_pool_manager( new MockClientSocketPoolManager); mock_pool_manager->SetTransportSocketPool(transport_conn_pool); mock_pool_manager->SetSSLSocketPool(ssl_conn_pool); peer.SetClientSocketPoolManager(std::move(mock_pool_manager)); PreconnectHelper(kTests[i], session.get()); // We shouldn't be preconnecting if we have an existing session, which is // the case for https://www.google.com. if (kTests[i].ssl) EXPECT_EQ(-1, ssl_conn_pool->last_num_streams()); else EXPECT_EQ(kTests[i].num_streams, transport_conn_pool->last_num_streams()); } } // Verify that preconnects to unsafe ports are cancelled before they reach // the SocketPool. TEST_P(HttpStreamFactoryTest, PreconnectUnsafePort) { ASSERT_FALSE(IsPortAllowedForScheme(7, "http")); SpdySessionDependencies session_deps( GetParam(), ProxyService::CreateDirect()); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); HttpNetworkSessionPeer peer(session.get()); CapturePreconnectsTransportSocketPool* transport_conn_pool = new CapturePreconnectsTransportSocketPool( session_deps.host_resolver.get(), session_deps.cert_verifier.get()); scoped_ptr mock_pool_manager( new MockClientSocketPoolManager); mock_pool_manager->SetTransportSocketPool(transport_conn_pool); peer.SetClientSocketPoolManager(std::move(mock_pool_manager)); PreconnectHelperForURL(1, GURL("http://www.google.com:7"), session.get()); EXPECT_EQ(-1, transport_conn_pool->last_num_streams()); } TEST_P(HttpStreamFactoryTest, JobNotifiesProxy) { const char* kProxyString = "PROXY bad:99; PROXY maybe:80; DIRECT"; SpdySessionDependencies session_deps( GetParam(), ProxyService::CreateFixedFromPacResult(kProxyString)); // First connection attempt fails StaticSocketDataProvider socket_data1; socket_data1.set_connect_data(MockConnect(ASYNC, ERR_ADDRESS_UNREACHABLE)); session_deps.socket_factory->AddSocketDataProvider(&socket_data1); // Second connection attempt succeeds StaticSocketDataProvider socket_data2; socket_data2.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data2); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. It should succeed using the second proxy in the // list. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("http://www.google.com"); SSLConfig ssl_config; StreamRequestWaiter waiter; scoped_ptr request( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, BoundNetLog())); waiter.WaitForStream(); // The proxy that failed should now be known to the proxy_service as bad. const ProxyRetryInfoMap& retry_info = session->proxy_service()->proxy_retry_info(); EXPECT_EQ(1u, retry_info.size()); ProxyRetryInfoMap::const_iterator iter = retry_info.find("bad:99"); EXPECT_TRUE(iter != retry_info.end()); } TEST_P(HttpStreamFactoryTest, UnreachableQuicProxyMarkedAsBad) { const int mock_error[] = {ERR_PROXY_CONNECTION_FAILED, ERR_NAME_NOT_RESOLVED, ERR_INTERNET_DISCONNECTED, ERR_ADDRESS_UNREACHABLE, ERR_CONNECTION_CLOSED, ERR_CONNECTION_TIMED_OUT, ERR_CONNECTION_RESET, ERR_CONNECTION_REFUSED, ERR_CONNECTION_ABORTED, ERR_TIMED_OUT, ERR_TUNNEL_CONNECTION_FAILED, ERR_SOCKS_CONNECTION_FAILED, ERR_PROXY_CERTIFICATE_INVALID, ERR_QUIC_PROTOCOL_ERROR, ERR_QUIC_HANDSHAKE_FAILED, ERR_SSL_PROTOCOL_ERROR, ERR_MSG_TOO_BIG}; for (size_t i = 0; i < arraysize(mock_error); ++i) { scoped_ptr proxy_service; proxy_service = ProxyService::CreateFixedFromPacResult("QUIC bad:99; DIRECT"); HttpNetworkSession::Params params; params.enable_quic = true; params.quic_disable_preconnect_if_0rtt = false; params.enable_quic_for_proxies = true; scoped_refptr ssl_config_service( new SSLConfigServiceDefaults); HttpServerPropertiesImpl http_server_properties; MockClientSocketFactory socket_factory; params.client_socket_factory = &socket_factory; MockHostResolver host_resolver; params.host_resolver = &host_resolver; TransportSecurityState transport_security_state; params.transport_security_state = &transport_security_state; params.proxy_service = proxy_service.get(); params.ssl_config_service = ssl_config_service.get(); params.http_server_properties = http_server_properties.GetWeakPtr(); scoped_ptr session(new HttpNetworkSession(params)); session->quic_stream_factory()->set_require_confirmation(false); StaticSocketDataProvider socket_data1; socket_data1.set_connect_data(MockConnect(ASYNC, mock_error[i])); socket_factory.AddSocketDataProvider(&socket_data1); // Second connection attempt succeeds. StaticSocketDataProvider socket_data2; socket_data2.set_connect_data(MockConnect(ASYNC, OK)); socket_factory.AddSocketDataProvider(&socket_data2); // Now request a stream. It should succeed using the second proxy in the // list. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("http://www.google.com"); SSLConfig ssl_config; StreamRequestWaiter waiter; scoped_ptr request( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, BoundNetLog())); waiter.WaitForStream(); // The proxy that failed should now be known to the proxy_service as bad. const ProxyRetryInfoMap& retry_info = session->proxy_service()->proxy_retry_info(); EXPECT_EQ(1u, retry_info.size()) << mock_error[i]; EXPECT_TRUE(waiter.used_proxy_info().is_direct()); ProxyRetryInfoMap::const_iterator iter = retry_info.find("quic://bad:99"); EXPECT_TRUE(iter != retry_info.end()) << mock_error[i]; } } // BidirectionalStreamImpl::Delegate to wait until response headers are // received. class TestBidirectionalDelegate : public BidirectionalStreamImpl::Delegate { public: void WaitUntilDone() { loop_.Run(); } const SpdyHeaderBlock& response_headers() const { return response_headers_; } private: void OnHeadersSent() override {} void OnHeadersReceived(const SpdyHeaderBlock& response_headers) override { response_headers_ = response_headers; loop_.Quit(); } void OnDataRead(int bytes_read) override { NOTREACHED(); } void OnDataSent() override { NOTREACHED(); } void OnTrailersReceived(const SpdyHeaderBlock& trailers) override { NOTREACHED(); } void OnFailed(int error) override { NOTREACHED(); } base::RunLoop loop_; SpdyHeaderBlock response_headers_; }; // Helper class to encapsulate MockReads and MockWrites for QUIC. // Simplify ownership issues and the interaction with the MockSocketFactory. class MockQuicData { public: MockQuicData() : packet_number_(0) {} ~MockQuicData() { STLDeleteElements(&packets_); } void AddRead(scoped_ptr packet) { reads_.push_back( MockRead(ASYNC, packet->data(), packet->length(), packet_number_++)); packets_.push_back(packet.release()); } void AddRead(IoMode mode, int rv) { reads_.push_back(MockRead(mode, rv, packet_number_++)); } void AddWrite(scoped_ptr packet) { writes_.push_back(MockWrite(SYNCHRONOUS, packet->data(), packet->length(), packet_number_++)); packets_.push_back(packet.release()); } void AddSocketDataToFactory(MockClientSocketFactory* factory) { MockRead* reads = reads_.empty() ? nullptr : &reads_[0]; MockWrite* writes = writes_.empty() ? nullptr : &writes_[0]; socket_data_.reset( new SequencedSocketData(reads, reads_.size(), writes, writes_.size())); factory->AddSocketDataProvider(socket_data_.get()); } private: std::vector packets_; std::vector writes_; std::vector reads_; size_t packet_number_; scoped_ptr socket_data_; }; } // namespace TEST_P(HttpStreamFactoryTest, QuicLossyProxyMarkedAsBad) { // Checks if a scoped_ptr proxy_service; proxy_service = ProxyService::CreateFixedFromPacResult("QUIC bad:99; DIRECT"); HttpNetworkSession::Params params; params.enable_quic = true; params.enable_quic_for_proxies = true; params.quic_disable_preconnect_if_0rtt = false; scoped_refptr ssl_config_service( new SSLConfigServiceDefaults); HttpServerPropertiesImpl http_server_properties; MockClientSocketFactory socket_factory; params.client_socket_factory = &socket_factory; MockHostResolver host_resolver; params.host_resolver = &host_resolver; TransportSecurityState transport_security_state; params.transport_security_state = &transport_security_state; params.proxy_service = proxy_service.get(); params.ssl_config_service = ssl_config_service.get(); params.http_server_properties = http_server_properties.GetWeakPtr(); params.quic_max_number_of_lossy_connections = 2; scoped_ptr session(new HttpNetworkSession(params)); session->quic_stream_factory()->set_require_confirmation(false); session->quic_stream_factory()->number_of_lossy_connections_[99] = params.quic_max_number_of_lossy_connections; session->quic_stream_factory()->MaybeDisableQuic(99); ASSERT_TRUE(session->quic_stream_factory()->IsQuicDisabled(99)); StaticSocketDataProvider socket_data2; socket_data2.set_connect_data(MockConnect(ASYNC, OK)); socket_factory.AddSocketDataProvider(&socket_data2); // Now request a stream. It should succeed using the second proxy in the // list. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("http://www.google.com"); SSLConfig ssl_config; StreamRequestWaiter waiter; scoped_ptr request( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, BoundNetLog())); waiter.WaitForStream(); // The proxy that failed should now be known to the proxy_service as bad. const ProxyRetryInfoMap& retry_info = session->proxy_service()->proxy_retry_info(); EXPECT_EQ(1u, retry_info.size()); EXPECT_TRUE(waiter.used_proxy_info().is_direct()); ProxyRetryInfoMap::const_iterator iter = retry_info.find("quic://bad:99"); EXPECT_TRUE(iter != retry_info.end()); } TEST_P(HttpStreamFactoryTest, UsePreConnectIfNoZeroRTT) { for (int num_streams = 1; num_streams < 3; ++num_streams) { GURL url = GURL("https://www.google.com"); // Set up QUIC as alternative_service. HttpServerPropertiesImpl http_server_properties; const AlternativeService alternative_service(QUIC, url.host().c_str(), url.IntPort()); AlternativeServiceInfoVector alternative_service_info_vector; base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1); alternative_service_info_vector.push_back( AlternativeServiceInfo(alternative_service, expiration)); HostPortPair host_port_pair(alternative_service.host_port_pair()); http_server_properties.SetAlternativeServices( host_port_pair, alternative_service_info_vector); SpdySessionDependencies session_deps( GetParam(), ProxyService::CreateFixed("http_proxy")); // Setup params to disable preconnect, but QUIC doesn't 0RTT. HttpNetworkSession::Params params = SpdySessionDependencies::CreateSessionParams(&session_deps); params.enable_quic = true; params.quic_disable_preconnect_if_0rtt = true; params.http_server_properties = http_server_properties.GetWeakPtr(); scoped_ptr session(new HttpNetworkSession(params)); HttpNetworkSessionPeer peer(session.get()); HostPortPair proxy_host("http_proxy", 80); CapturePreconnectsHttpProxySocketPool* http_proxy_pool = new CapturePreconnectsHttpProxySocketPool( session_deps.host_resolver.get(), session_deps.cert_verifier.get()); CapturePreconnectsSSLSocketPool* ssl_conn_pool = new CapturePreconnectsSSLSocketPool(session_deps.host_resolver.get(), session_deps.cert_verifier.get()); scoped_ptr mock_pool_manager( new MockClientSocketPoolManager); mock_pool_manager->SetSocketPoolForHTTPProxy(proxy_host, http_proxy_pool); mock_pool_manager->SetSocketPoolForSSLWithProxy(proxy_host, ssl_conn_pool); peer.SetClientSocketPoolManager(std::move(mock_pool_manager)); PreconnectHelperForURL(num_streams, url, session.get()); EXPECT_EQ(num_streams, ssl_conn_pool->last_num_streams()); } } TEST_P(HttpStreamFactoryTest, QuicDisablePreConnectIfZeroRtt) { for (int num_streams = 1; num_streams < 3; ++num_streams) { GURL url = GURL("https://www.google.com"); // Set up QUIC as alternative_service. HttpServerPropertiesImpl http_server_properties; const AlternativeService alternative_service(QUIC, "www.google.com", 443); AlternativeServiceInfoVector alternative_service_info_vector; base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1); alternative_service_info_vector.push_back( AlternativeServiceInfo(alternative_service, expiration)); HostPortPair host_port_pair(alternative_service.host_port_pair()); http_server_properties.SetAlternativeServices( host_port_pair, alternative_service_info_vector); SpdySessionDependencies session_deps(GetParam()); // Setup params to disable preconnect, but QUIC does 0RTT. HttpNetworkSession::Params params = SpdySessionDependencies::CreateSessionParams(&session_deps); params.enable_quic = true; params.quic_disable_preconnect_if_0rtt = true; params.http_server_properties = http_server_properties.GetWeakPtr(); scoped_ptr session(new HttpNetworkSession(params)); // Setup 0RTT for QUIC. QuicStreamFactory* factory = session->quic_stream_factory(); factory->set_require_confirmation(false); test::QuicStreamFactoryPeer::CacheDummyServerConfig( factory, QuicServerId(host_port_pair, PRIVACY_MODE_DISABLED)); HttpNetworkSessionPeer peer(session.get()); CapturePreconnectsTransportSocketPool* transport_conn_pool = new CapturePreconnectsTransportSocketPool( session_deps.host_resolver.get(), session_deps.cert_verifier.get()); scoped_ptr mock_pool_manager( new MockClientSocketPoolManager); mock_pool_manager->SetTransportSocketPool(transport_conn_pool); peer.SetClientSocketPoolManager(std::move(mock_pool_manager)); HttpRequestInfo request; request.method = "GET"; request.url = url; request.load_flags = 0; SSLConfig ssl_config; session->ssl_config_service()->GetSSLConfig(&ssl_config); session->http_stream_factory()->PreconnectStreams(num_streams, request, ssl_config, ssl_config); EXPECT_EQ(-1, transport_conn_pool->last_num_streams()); } } namespace { TEST_P(HttpStreamFactoryTest, PrivacyModeDisablesChannelId) { SpdySessionDependencies session_deps( GetParam(), ProxyService::CreateDirect()); StaticSocketDataProvider socket_data; socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); SSLSocketDataProvider ssl(ASYNC, OK); session_deps.socket_factory->AddSSLSocketDataProvider(&ssl); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Set an existing SpdySession in the pool. HostPortPair host_port_pair("www.google.com", 443); SpdySessionKey key(host_port_pair, ProxyServer::Direct(), PRIVACY_MODE_ENABLED); HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); request_info.load_flags = 0; request_info.privacy_mode = PRIVACY_MODE_DISABLED; SSLConfig ssl_config; StreamRequestWaiter waiter; scoped_ptr request( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, BoundNetLog())); waiter.WaitForStream(); // The stream shouldn't come from spdy as we are using different privacy mode EXPECT_FALSE(request->using_spdy()); SSLConfig used_ssl_config = waiter.used_ssl_config(); EXPECT_EQ(used_ssl_config.channel_id_enabled, ssl_config.channel_id_enabled); } namespace { // Return count of distinct groups in given socket pool. int GetSocketPoolGroupCount(ClientSocketPool* pool) { int count = 0; scoped_ptr dict(pool->GetInfoAsValue("", "", false)); EXPECT_TRUE(dict != nullptr); base::DictionaryValue* groups = nullptr; if (dict->GetDictionary("groups", &groups) && (groups != nullptr)) { count = static_cast(groups->size()); } return count; } // Return count of distinct spdy sessions. int GetSpdySessionCount(HttpNetworkSession* session) { scoped_ptr value( session->spdy_session_pool()->SpdySessionPoolInfoToValue()); base::ListValue* session_list; if (!value || !value->GetAsList(&session_list)) return -1; return session_list->GetSize(); } } // namespace TEST_P(HttpStreamFactoryTest, PrivacyModeUsesDifferentSocketPoolGroup) { SpdySessionDependencies session_deps( GetParam(), ProxyService::CreateDirect()); StaticSocketDataProvider socket_data; socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); SSLSocketDataProvider ssl(ASYNC, OK); session_deps.socket_factory->AddSSLSocketDataProvider(&ssl); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); SSLClientSocketPool* ssl_pool = session->GetSSLSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL); EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 0); HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); request_info.load_flags = 0; request_info.privacy_mode = PRIVACY_MODE_DISABLED; SSLConfig ssl_config; StreamRequestWaiter waiter; scoped_ptr request1( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, BoundNetLog())); waiter.WaitForStream(); EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 1); scoped_ptr request2( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, BoundNetLog())); waiter.WaitForStream(); EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 1); request_info.privacy_mode = PRIVACY_MODE_ENABLED; scoped_ptr request3( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, BoundNetLog())); waiter.WaitForStream(); EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 2); } TEST_P(HttpStreamFactoryTest, GetLoadState) { SpdySessionDependencies session_deps( GetParam(), ProxyService::CreateDirect()); StaticSocketDataProvider socket_data; socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("http://www.google.com"); SSLConfig ssl_config; StreamRequestWaiter waiter; scoped_ptr request( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, BoundNetLog())); EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, request->GetLoadState()); waiter.WaitForStream(); } TEST_P(HttpStreamFactoryTest, RequestHttpStream) { SpdySessionDependencies session_deps( GetParam(), ProxyService::CreateDirect()); StaticSocketDataProvider socket_data; socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. It should succeed using the second proxy in the // list. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("http://www.google.com"); request_info.load_flags = 0; SSLConfig ssl_config; StreamRequestWaiter waiter; scoped_ptr request( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, BoundNetLog())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); ASSERT_TRUE(nullptr != waiter.stream()); EXPECT_TRUE(nullptr == waiter.websocket_stream()); EXPECT_EQ(0, GetSpdySessionCount(session.get())); EXPECT_EQ(1, GetSocketPoolGroupCount( session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount( session->GetTransportSocketPool( HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool( HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_TRUE(waiter.used_proxy_info().is_direct()); } TEST_P(HttpStreamFactoryTest, RequestHttpStreamOverSSL) { SpdySessionDependencies session_deps( GetParam(), ProxyService::CreateDirect()); MockRead mock_read(ASYNC, OK); StaticSocketDataProvider socket_data(&mock_read, 1, nullptr, 0); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); request_info.load_flags = 0; SSLConfig ssl_config; StreamRequestWaiter waiter; scoped_ptr request( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, BoundNetLog())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); ASSERT_TRUE(nullptr != waiter.stream()); EXPECT_TRUE(nullptr == waiter.websocket_stream()); EXPECT_EQ(0, GetSpdySessionCount(session.get())); EXPECT_EQ(1, GetSocketPoolGroupCount( session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(1, GetSocketPoolGroupCount( session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount( session->GetTransportSocketPool( HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool( HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_TRUE(waiter.used_proxy_info().is_direct()); } TEST_P(HttpStreamFactoryTest, RequestHttpStreamOverProxy) { SpdySessionDependencies session_deps( GetParam(), ProxyService::CreateFixed("myproxy:8888")); StaticSocketDataProvider socket_data; socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. It should succeed using the second proxy in the // list. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("http://www.google.com"); request_info.load_flags = 0; SSLConfig ssl_config; StreamRequestWaiter waiter; scoped_ptr request( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, BoundNetLog())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); ASSERT_TRUE(nullptr != waiter.stream()); EXPECT_TRUE(nullptr == waiter.websocket_stream()); EXPECT_EQ(0, GetSpdySessionCount(session.get())); EXPECT_EQ(0, GetSocketPoolGroupCount( session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount( session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSocketPoolForHTTPProxy( HttpNetworkSession::NORMAL_SOCKET_POOL, HostPortPair("myproxy", 8888)))); EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPoolForSSLWithProxy( HttpNetworkSession::NORMAL_SOCKET_POOL, HostPortPair("myproxy", 8888)))); EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPoolForHTTPProxy( HttpNetworkSession::WEBSOCKET_SOCKET_POOL, HostPortPair("myproxy", 8888)))); EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPoolForSSLWithProxy( HttpNetworkSession::WEBSOCKET_SOCKET_POOL, HostPortPair("myproxy", 8888)))); EXPECT_FALSE(waiter.used_proxy_info().is_direct()); } TEST_P(HttpStreamFactoryTest, RequestWebSocketBasicHandshakeStream) { SpdySessionDependencies session_deps( GetParam(), ProxyService::CreateDirect()); StaticSocketDataProvider socket_data; socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("ws://www.google.com"); request_info.load_flags = 0; SSLConfig ssl_config; StreamRequestWaiter waiter; WebSocketStreamCreateHelper create_helper; scoped_ptr request( session->http_stream_factory_for_websocket() ->RequestWebSocketHandshakeStream(request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, &create_helper, BoundNetLog())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); EXPECT_TRUE(nullptr == waiter.stream()); ASSERT_TRUE(nullptr != waiter.websocket_stream()); EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeBasic, waiter.websocket_stream()->type()); EXPECT_EQ(0, GetSocketPoolGroupCount( session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount( session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount( session->GetSSLSocketPool(HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_TRUE(waiter.used_proxy_info().is_direct()); } TEST_P(HttpStreamFactoryTest, RequestWebSocketBasicHandshakeStreamOverSSL) { SpdySessionDependencies session_deps( GetParam(), ProxyService::CreateDirect()); MockRead mock_read(ASYNC, OK); StaticSocketDataProvider socket_data(&mock_read, 1, nullptr, 0); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("wss://www.google.com"); request_info.load_flags = 0; SSLConfig ssl_config; StreamRequestWaiter waiter; WebSocketStreamCreateHelper create_helper; scoped_ptr request( session->http_stream_factory_for_websocket() ->RequestWebSocketHandshakeStream(request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, &create_helper, BoundNetLog())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); EXPECT_TRUE(nullptr == waiter.stream()); ASSERT_TRUE(nullptr != waiter.websocket_stream()); EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeBasic, waiter.websocket_stream()->type()); EXPECT_EQ(0, GetSocketPoolGroupCount( session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount( session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(1, GetSocketPoolGroupCount( session->GetSSLSocketPool(HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_TRUE(waiter.used_proxy_info().is_direct()); } TEST_P(HttpStreamFactoryTest, RequestWebSocketBasicHandshakeStreamOverProxy) { SpdySessionDependencies session_deps( GetParam(), ProxyService::CreateFixed("myproxy:8888")); MockRead read(SYNCHRONOUS, "HTTP/1.0 200 Connection established\r\n\r\n"); StaticSocketDataProvider socket_data(&read, 1, 0, 0); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("ws://www.google.com"); request_info.load_flags = 0; SSLConfig ssl_config; StreamRequestWaiter waiter; WebSocketStreamCreateHelper create_helper; scoped_ptr request( session->http_stream_factory_for_websocket() ->RequestWebSocketHandshakeStream(request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, &create_helper, BoundNetLog())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); EXPECT_TRUE(nullptr == waiter.stream()); ASSERT_TRUE(nullptr != waiter.websocket_stream()); EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeBasic, waiter.websocket_stream()->type()); EXPECT_EQ(0, GetSocketPoolGroupCount( session->GetTransportSocketPool( HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount( session->GetSSLSocketPool(HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPoolForHTTPProxy( HttpNetworkSession::NORMAL_SOCKET_POOL, HostPortPair("myproxy", 8888)))); EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPoolForSSLWithProxy( HttpNetworkSession::NORMAL_SOCKET_POOL, HostPortPair("myproxy", 8888)))); EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSocketPoolForHTTPProxy( HttpNetworkSession::WEBSOCKET_SOCKET_POOL, HostPortPair("myproxy", 8888)))); EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPoolForSSLWithProxy( HttpNetworkSession::WEBSOCKET_SOCKET_POOL, HostPortPair("myproxy", 8888)))); EXPECT_FALSE(waiter.used_proxy_info().is_direct()); } TEST_P(HttpStreamFactoryTest, RequestSpdyHttpStream) { SpdySessionDependencies session_deps(GetParam(), ProxyService::CreateDirect()); MockRead mock_read(SYNCHRONOUS, ERR_IO_PENDING); SequencedSocketData socket_data(&mock_read, 1, nullptr, 0); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); ssl_socket_data.SetNextProto(GetParam()); session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); HostPortPair host_port_pair("www.google.com", 443); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); request_info.load_flags = 0; SSLConfig ssl_config; StreamRequestWaiter waiter; scoped_ptr request( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, BoundNetLog())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); EXPECT_TRUE(nullptr == waiter.websocket_stream()); ASSERT_TRUE(nullptr != waiter.stream()); EXPECT_EQ(1, GetSpdySessionCount(session.get())); EXPECT_EQ(1, GetSocketPoolGroupCount( session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(1, GetSocketPoolGroupCount( session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount( session->GetTransportSocketPool( HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount( session->GetSSLSocketPool(HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_TRUE(waiter.used_proxy_info().is_direct()); } TEST_P(HttpStreamFactoryTest, RequestBidirectionalStreamImpl) { SpdySessionDependencies session_deps(GetParam(), ProxyService::CreateDirect()); MockRead mock_read(ASYNC, OK); SequencedSocketData socket_data(&mock_read, 1, nullptr, 0); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); ssl_socket_data.SetNextProto(GetParam()); session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); HostPortPair host_port_pair("www.google.com", 443); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); request_info.load_flags = 0; SSLConfig ssl_config; StreamRequestWaiter waiter; scoped_ptr request( session->http_stream_factory()->RequestBidirectionalStreamImpl( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, BoundNetLog())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); EXPECT_FALSE(waiter.websocket_stream()); ASSERT_FALSE(waiter.stream()); ASSERT_TRUE(waiter.bidirectional_stream_impl()); EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSSLSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool( HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool( HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_TRUE(waiter.used_proxy_info().is_direct()); ASSERT_EQ(0u, static_cast(session->http_stream_factory()) ->num_orphaned_jobs()); } class HttpStreamFactoryBidirectionalQuicTest : public ::testing::Test, public ::testing::WithParamInterface { protected: HttpStreamFactoryBidirectionalQuicTest() : clock_(new MockClock), packet_maker_(GetParam(), 0, clock_, "www.example.org"), random_generator_(0), proxy_service_(ProxyService::CreateDirect()), ssl_config_service_(new SSLConfigServiceDefaults) { clock_->AdvanceTime(QuicTime::Delta::FromMilliseconds(20)); } // Disable bidirectional stream over QUIC. This should be invoked before // Initialize(). void DisableQuicBidirectionalStream() { params_.quic_disable_bidirectional_streams = true; } void Initialize() { params_.enable_quic = true; params_.http_server_properties = http_server_properties_.GetWeakPtr(); params_.quic_host_whitelist.insert("www.example.org"); params_.quic_random = &random_generator_; params_.quic_clock = clock_; // Load a certificate that is valid for *.example.org scoped_refptr test_cert( ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem")); EXPECT_TRUE(test_cert.get()); verify_details_.cert_verify_result.verified_cert = test_cert; verify_details_.cert_verify_result.is_issued_by_known_root = true; crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details_); crypto_client_stream_factory_.set_handshake_mode( MockCryptoClientStream::CONFIRM_HANDSHAKE); params_.quic_crypto_client_stream_factory = &crypto_client_stream_factory_; params_.quic_supported_versions = test::SupportedVersions(GetParam()); params_.transport_security_state = &transport_security_state_; params_.host_resolver = &host_resolver_; params_.proxy_service = proxy_service_.get(); params_.ssl_config_service = ssl_config_service_.get(); params_.client_socket_factory = &socket_factory_; session_.reset(new HttpNetworkSession(params_)); session_->quic_stream_factory()->set_require_confirmation(false); } void AddQuicAlternativeService() { const AlternativeService alternative_service(QUIC, "www.example.org", 443); AlternativeServiceInfoVector alternative_service_info_vector; base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1); alternative_service_info_vector.push_back( AlternativeServiceInfo(alternative_service, expiration)); HostPortPair host_port_pair(alternative_service.host_port_pair()); http_server_properties_.SetAlternativeServices( host_port_pair, alternative_service_info_vector); }; test::QuicTestPacketMaker& packet_maker() { return packet_maker_; } MockClientSocketFactory& socket_factory() { return socket_factory_; } HttpNetworkSession* session() { return session_.get(); } private: MockClock* clock_; // Owned by QuicStreamFactory test::QuicTestPacketMaker packet_maker_; MockClientSocketFactory socket_factory_; scoped_ptr session_; test::MockRandom random_generator_; ProofVerifyDetailsChromium verify_details_; MockCryptoClientStreamFactory crypto_client_stream_factory_; HttpServerPropertiesImpl http_server_properties_; TransportSecurityState transport_security_state_; MockHostResolver host_resolver_; scoped_ptr proxy_service_; scoped_refptr ssl_config_service_; HttpNetworkSession::Params params_; }; INSTANTIATE_TEST_CASE_P(Version, HttpStreamFactoryBidirectionalQuicTest, ::testing::ValuesIn(QuicSupportedVersions())); TEST_P(HttpStreamFactoryBidirectionalQuicTest, RequestBidirectionalStreamImplQuicAlternative) { GURL url = GURL("https://www.example.org"); MockQuicData mock_quic_data; SpdyPriority priority = ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY); size_t spdy_headers_frame_length; mock_quic_data.AddWrite(packet_maker().MakeRequestHeadersPacket( 1, test::kClientDataStreamId1, /*should_include_version=*/true, /*fin=*/true, priority, packet_maker().GetRequestHeaders("GET", "https", "/"), &spdy_headers_frame_length)); size_t spdy_response_headers_frame_length; mock_quic_data.AddRead(packet_maker().MakeResponseHeadersPacket( 1, test::kClientDataStreamId1, /*should_include_version=*/false, /*fin=*/true, packet_maker().GetResponseHeaders("200"), &spdy_response_headers_frame_length)); mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // No more read data. mock_quic_data.AddSocketDataToFactory(&socket_factory()); // Add hanging data for http job. scoped_ptr hanging_data; hanging_data.reset(new StaticSocketDataProvider()); MockConnect hanging_connect(SYNCHRONOUS, ERR_IO_PENDING); hanging_data->set_connect_data(hanging_connect); socket_factory().AddSocketDataProvider(hanging_data.get()); SSLSocketDataProvider ssl_data(ASYNC, OK); socket_factory().AddSSLSocketDataProvider(&ssl_data); // Set up QUIC as alternative_service. AddQuicAlternativeService(); Initialize(); // Now request a stream. SSLConfig ssl_config; HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.example.org"); request_info.load_flags = 0; StreamRequestWaiter waiter; scoped_ptr request( session()->http_stream_factory()->RequestBidirectionalStreamImpl( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, BoundNetLog())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); EXPECT_FALSE(waiter.websocket_stream()); ASSERT_FALSE(waiter.stream()); ASSERT_TRUE(waiter.bidirectional_stream_impl()); BidirectionalStreamImpl* stream_impl = waiter.bidirectional_stream_impl(); BidirectionalStreamRequestInfo bidi_request_info; bidi_request_info.method = "GET"; bidi_request_info.url = GURL("https://www.example.org/"); bidi_request_info.end_stream_on_headers = true; bidi_request_info.priority = LOWEST; TestBidirectionalDelegate delegate; stream_impl->Start(&bidi_request_info, BoundNetLog(), &delegate, nullptr); delegate.WaitUntilDone(); scoped_refptr buffer = new net::IOBuffer(1); EXPECT_EQ(OK, stream_impl->ReadData(buffer.get(), 1)); EXPECT_EQ(kProtoQUIC1SPDY3, stream_impl->GetProtocol()); EXPECT_EQ("200", delegate.response_headers().find(":status")->second); EXPECT_EQ(1, GetSocketPoolGroupCount(session()->GetTransportSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(1, GetSocketPoolGroupCount(session()->GetSSLSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetTransportSocketPool( HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetSSLSocketPool( HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_TRUE(waiter.used_proxy_info().is_direct()); } // Tests that when QUIC is not enabled for bidirectional streaming, HTTP/2 is // used instead. TEST_P(HttpStreamFactoryBidirectionalQuicTest, RequestBidirectionalStreamImplQuicNotEnabled) { GURL url = GURL("https://www.example.org"); // Make the http job fail. scoped_ptr http_job_data; http_job_data.reset(new StaticSocketDataProvider()); MockConnect failed_connect(ASYNC, ERR_CONNECTION_REFUSED); http_job_data->set_connect_data(failed_connect); socket_factory().AddSocketDataProvider(http_job_data.get()); SSLSocketDataProvider ssl_data(ASYNC, OK); socket_factory().AddSSLSocketDataProvider(&ssl_data); // Set up QUIC as alternative_service. AddQuicAlternativeService(); DisableQuicBidirectionalStream(); Initialize(); // Now request a stream. SSLConfig ssl_config; HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.example.org"); request_info.load_flags = 0; StreamRequestWaiter waiter; scoped_ptr request( session()->http_stream_factory()->RequestBidirectionalStreamImpl( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, BoundNetLog())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); EXPECT_FALSE(waiter.websocket_stream()); ASSERT_FALSE(waiter.stream()); ASSERT_FALSE(waiter.bidirectional_stream_impl()); // Since the alternative service job is not started, we will get the error // from the http job. ASSERT_EQ(ERR_CONNECTION_REFUSED, waiter.error_status()); } // Tests that if Http job fails, but Quic job succeeds, we return // BidirectionalStreamQuicImpl. TEST_P(HttpStreamFactoryBidirectionalQuicTest, RequestBidirectionalStreamImplHttpJobFailsQuicJobSucceeds) { GURL url = GURL("https://www.example.org"); // Set up Quic data. MockQuicData mock_quic_data; SpdyPriority priority = ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY); size_t spdy_headers_frame_length; mock_quic_data.AddWrite(packet_maker().MakeRequestHeadersPacket( 1, test::kClientDataStreamId1, /*should_include_version=*/true, /*fin=*/true, priority, packet_maker().GetRequestHeaders("GET", "https", "/"), &spdy_headers_frame_length)); size_t spdy_response_headers_frame_length; mock_quic_data.AddRead(packet_maker().MakeResponseHeadersPacket( 1, test::kClientDataStreamId1, /*should_include_version=*/false, /*fin=*/true, packet_maker().GetResponseHeaders("200"), &spdy_response_headers_frame_length)); mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // No more read data. mock_quic_data.AddSocketDataToFactory(&socket_factory()); // Make the http job fail. scoped_ptr http_job_data; http_job_data.reset(new StaticSocketDataProvider()); MockConnect failed_connect(ASYNC, ERR_CONNECTION_REFUSED); http_job_data->set_connect_data(failed_connect); socket_factory().AddSocketDataProvider(http_job_data.get()); SSLSocketDataProvider ssl_data(ASYNC, OK); socket_factory().AddSSLSocketDataProvider(&ssl_data); // Set up QUIC as alternative_service. AddQuicAlternativeService(); Initialize(); // Now request a stream. SSLConfig ssl_config; HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.example.org"); request_info.load_flags = 0; StreamRequestWaiter waiter; scoped_ptr request( session()->http_stream_factory()->RequestBidirectionalStreamImpl( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, BoundNetLog())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); EXPECT_FALSE(waiter.websocket_stream()); ASSERT_FALSE(waiter.stream()); ASSERT_TRUE(waiter.bidirectional_stream_impl()); BidirectionalStreamImpl* stream_impl = waiter.bidirectional_stream_impl(); BidirectionalStreamRequestInfo bidi_request_info; bidi_request_info.method = "GET"; bidi_request_info.url = GURL("https://www.example.org/"); bidi_request_info.end_stream_on_headers = true; bidi_request_info.priority = LOWEST; TestBidirectionalDelegate delegate; stream_impl->Start(&bidi_request_info, BoundNetLog(), &delegate, nullptr); delegate.WaitUntilDone(); // Make sure the BidirectionalStream negotiated goes through QUIC. scoped_refptr buffer = new net::IOBuffer(1); EXPECT_EQ(OK, stream_impl->ReadData(buffer.get(), 1)); EXPECT_EQ(kProtoQUIC1SPDY3, stream_impl->GetProtocol()); EXPECT_EQ("200", delegate.response_headers().find(":status")->second); // There is no Http2 socket pool. EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetTransportSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetSSLSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetTransportSocketPool( HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetSSLSocketPool( HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_TRUE(waiter.used_proxy_info().is_direct()); } TEST_P(HttpStreamFactoryTest, RequestBidirectionalStreamImplFailure) { SpdySessionDependencies session_deps(GetParam(), ProxyService::CreateDirect()); MockRead mock_read(ASYNC, OK); SequencedSocketData socket_data(&mock_read, 1, nullptr, 0); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); // If HTTP/1 is used, BidirectionalStreamImpl should not be obtained. ssl_socket_data.SetNextProto(kProtoHTTP11); session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); HostPortPair host_port_pair("www.google.com", 443); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); request_info.load_flags = 0; SSLConfig ssl_config; StreamRequestWaiter waiter; scoped_ptr request( session->http_stream_factory()->RequestBidirectionalStreamImpl( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, BoundNetLog())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); ASSERT_EQ(ERR_FAILED, waiter.error_status()); EXPECT_FALSE(waiter.websocket_stream()); ASSERT_FALSE(waiter.stream()); ASSERT_FALSE(waiter.bidirectional_stream_impl()); EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSSLSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool( HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool( HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); ASSERT_EQ(0u, static_cast(session->http_stream_factory()) ->num_orphaned_jobs()); } // TODO(ricea): This test can be removed once the new WebSocket stack supports // SPDY. Currently, even if we connect to a SPDY-supporting server, we need to // use plain SSL. TEST_P(HttpStreamFactoryTest, RequestWebSocketSpdyHandshakeStreamButGetSSL) { SpdySessionDependencies session_deps(GetParam(), ProxyService::CreateDirect()); MockRead mock_read(SYNCHRONOUS, ERR_IO_PENDING); StaticSocketDataProvider socket_data(&mock_read, 1, nullptr, 0); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); HostPortPair host_port_pair("www.google.com", 80); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("wss://www.google.com"); request_info.load_flags = 0; SSLConfig ssl_config; StreamRequestWaiter waiter1; WebSocketStreamCreateHelper create_helper; scoped_ptr request1( session->http_stream_factory_for_websocket() ->RequestWebSocketHandshakeStream(request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter1, &create_helper, BoundNetLog())); waiter1.WaitForStream(); EXPECT_TRUE(waiter1.stream_done()); ASSERT_TRUE(nullptr != waiter1.websocket_stream()); EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeBasic, waiter1.websocket_stream()->type()); EXPECT_TRUE(nullptr == waiter1.stream()); EXPECT_EQ(0, GetSocketPoolGroupCount( session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount( session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(1, GetSocketPoolGroupCount( session->GetSSLSocketPool(HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_TRUE(waiter1.used_proxy_info().is_direct()); } // TODO(ricea): Re-enable once WebSocket-over-SPDY is implemented. TEST_P(HttpStreamFactoryTest, DISABLED_RequestWebSocketSpdyHandshakeStream) { SpdySessionDependencies session_deps(GetParam(), ProxyService::CreateDirect()); MockRead mock_read(SYNCHRONOUS, ERR_IO_PENDING); StaticSocketDataProvider socket_data(&mock_read, 1, nullptr, 0); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); ssl_socket_data.SetNextProto(GetParam()); session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); HostPortPair host_port_pair("www.google.com", 80); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("wss://www.google.com"); request_info.load_flags = 0; SSLConfig ssl_config; StreamRequestWaiter waiter1; WebSocketStreamCreateHelper create_helper; scoped_ptr request1( session->http_stream_factory_for_websocket() ->RequestWebSocketHandshakeStream(request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter1, &create_helper, BoundNetLog())); waiter1.WaitForStream(); EXPECT_TRUE(waiter1.stream_done()); ASSERT_TRUE(nullptr != waiter1.websocket_stream()); EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeSpdy, waiter1.websocket_stream()->type()); EXPECT_TRUE(nullptr == waiter1.stream()); StreamRequestWaiter waiter2; scoped_ptr request2( session->http_stream_factory_for_websocket() ->RequestWebSocketHandshakeStream(request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter2, &create_helper, BoundNetLog())); waiter2.WaitForStream(); EXPECT_TRUE(waiter2.stream_done()); ASSERT_TRUE(nullptr != waiter2.websocket_stream()); EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeSpdy, waiter2.websocket_stream()->type()); EXPECT_TRUE(nullptr == waiter2.stream()); EXPECT_NE(waiter2.websocket_stream(), waiter1.websocket_stream()); EXPECT_EQ(static_cast( waiter2.websocket_stream())->spdy_session(), static_cast( waiter1.websocket_stream())->spdy_session()); EXPECT_EQ(0, GetSocketPoolGroupCount( session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount( session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(1, GetSocketPoolGroupCount( session->GetTransportSocketPool( HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_EQ(1, GetSocketPoolGroupCount( session->GetSSLSocketPool(HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_TRUE(waiter1.used_proxy_info().is_direct()); } // TODO(ricea): Re-enable once WebSocket over SPDY is implemented. TEST_P(HttpStreamFactoryTest, DISABLED_OrphanedWebSocketStream) { SpdySessionDependencies session_deps(GetParam(), ProxyService::CreateDirect()); session_deps.parse_alternative_services = true; session_deps.enable_alternative_service_with_different_host = false; MockRead mock_read(ASYNC, OK); SequencedSocketData socket_data(&mock_read, 1, nullptr, 0); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); MockRead mock_read2(ASYNC, OK); SequencedSocketData socket_data2(&mock_read2, 1, nullptr, 0); socket_data2.set_connect_data(MockConnect(ASYNC, ERR_IO_PENDING)); session_deps.socket_factory->AddSocketDataProvider(&socket_data2); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); ssl_socket_data.SetNextProto(GetParam()); session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); scoped_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("ws://www.google.com:8888"); request_info.load_flags = 0; base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1); session->http_server_properties()->SetAlternativeService( HostPortPair("www.google.com", 8888), AlternativeService(NPN_HTTP_2, "www.google.com", 9999), expiration); SSLConfig ssl_config; StreamRequestWaiter waiter; WebSocketStreamCreateHelper create_helper; scoped_ptr request( session->http_stream_factory_for_websocket() ->RequestWebSocketHandshakeStream(request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, &create_helper, BoundNetLog())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); EXPECT_TRUE(nullptr == waiter.stream()); ASSERT_TRUE(nullptr != waiter.websocket_stream()); EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeSpdy, waiter.websocket_stream()->type()); // Make sure that there was an alternative connection // which consumes extra connections. EXPECT_EQ(0, GetSocketPoolGroupCount( session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(0, GetSocketPoolGroupCount( session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); EXPECT_EQ(2, GetSocketPoolGroupCount( session->GetTransportSocketPool( HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_EQ(1, GetSocketPoolGroupCount( session->GetSSLSocketPool(HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); EXPECT_TRUE(waiter.used_proxy_info().is_direct()); // Make sure there is no orphaned job. it is already canceled. ASSERT_EQ(0u, static_cast( session->http_stream_factory_for_websocket())->num_orphaned_jobs()); } } // namespace } // namespace net