1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807 |
- // Copyright (c) 2013 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/spdy/spdy_session_pool.h"
- #include <cstddef>
- #include <tuple>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/memory/raw_ptr.h"
- #include "base/memory/ref_counted.h"
- #include "base/run_loop.h"
- #include "base/test/bind.h"
- #include "base/test/metrics/histogram_tester.h"
- #include "base/trace_event/memory_allocator_dump.h"
- #include "base/trace_event/process_memory_dump.h"
- #include "base/trace_event/traced_value.h"
- #include "build/build_config.h"
- #include "net/base/proxy_string_util.h"
- #include "net/base/test_completion_callback.h"
- #include "net/dns/host_cache.h"
- #include "net/dns/public/secure_dns_policy.h"
- #include "net/http/http_network_session.h"
- #include "net/log/net_log_with_source.h"
- #include "net/log/test_net_log.h"
- #include "net/socket/client_socket_handle.h"
- #include "net/socket/socket_tag.h"
- #include "net/socket/socket_test_util.h"
- #include "net/socket/transport_client_socket_pool.h"
- #include "net/spdy/spdy_session.h"
- #include "net/spdy/spdy_stream_test_util.h"
- #include "net/spdy/spdy_test_util_common.h"
- #include "net/test/cert_test_util.h"
- #include "net/test/gtest_util.h"
- #include "net/test/test_certificate_data.h"
- #include "net/test/test_data_directory.h"
- #include "net/test/test_with_task_environment.h"
- #include "net/third_party/quiche/src/quiche/spdy/core/spdy_protocol.h"
- #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
- #include "testing/gmock/include/gmock/gmock.h"
- #include "testing/gtest/include/gtest/gtest.h"
- using base::trace_event::MemoryAllocatorDump;
- using net::test::IsError;
- using net::test::IsOk;
- using testing::Contains;
- using testing::Eq;
- using testing::Contains;
- using testing::ByRef;
- namespace net {
- class SpdySessionPoolTest : public TestWithTaskEnvironment {
- protected:
- // Used by RunIPPoolingTest().
- enum SpdyPoolCloseSessionsType {
- SPDY_POOL_CLOSE_SESSIONS_MANUALLY,
- SPDY_POOL_CLOSE_CURRENT_SESSIONS,
- SPDY_POOL_CLOSE_IDLE_SESSIONS,
- };
- SpdySessionPoolTest() = default;
- void CreateNetworkSession() {
- http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
- spdy_session_pool_ = http_session_->spdy_session_pool();
- }
- void AddSSLSocketData() {
- auto ssl = std::make_unique<SSLSocketDataProvider>(SYNCHRONOUS, OK);
- ssl->ssl_info.cert =
- ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
- ASSERT_TRUE(ssl->ssl_info.cert);
- session_deps_.socket_factory->AddSSLSocketDataProvider(ssl.get());
- ssl_data_vector_.push_back(std::move(ssl));
- }
- void RunIPPoolingTest(SpdyPoolCloseSessionsType close_sessions_type);
- void RunIPPoolingDisabledTest(SSLSocketDataProvider* ssl);
- size_t num_active_streams(base::WeakPtr<SpdySession> session) {
- return session->active_streams_.size();
- }
- size_t max_concurrent_streams(base::WeakPtr<SpdySession> session) {
- return session->max_concurrent_streams_;
- }
- SpdySessionDependencies session_deps_;
- std::unique_ptr<HttpNetworkSession> http_session_;
- raw_ptr<SpdySessionPool> spdy_session_pool_ = nullptr;
- std::vector<std::unique_ptr<SSLSocketDataProvider>> ssl_data_vector_;
- };
- class SpdySessionRequestDelegate
- : public SpdySessionPool::SpdySessionRequest::Delegate {
- public:
- SpdySessionRequestDelegate() = default;
- SpdySessionRequestDelegate(const SpdySessionRequestDelegate&) = delete;
- SpdySessionRequestDelegate& operator=(const SpdySessionRequestDelegate&) =
- delete;
- ~SpdySessionRequestDelegate() override = default;
- void OnSpdySessionAvailable(
- base::WeakPtr<SpdySession> spdy_session) override {
- EXPECT_FALSE(callback_invoked_);
- callback_invoked_ = true;
- spdy_session_ = spdy_session;
- }
- bool callback_invoked() const { return callback_invoked_; }
- SpdySession* spdy_session() { return spdy_session_.get(); }
- private:
- bool callback_invoked_ = false;
- base::WeakPtr<SpdySession> spdy_session_;
- };
- // Attempts to set up an alias for |key| using an already existing session in
- // |pool|. To do this, simulates a host resolution that returns
- // |ip_address_list|.
- bool TryCreateAliasedSpdySession(SpdySessionPool* pool,
- const SpdySessionKey& key,
- const std::string& ip_address_list,
- bool enable_ip_based_pooling = true,
- bool is_websocket = false) {
- // The requested session must not already exist.
- EXPECT_FALSE(pool->FindAvailableSession(key, enable_ip_based_pooling,
- is_websocket, NetLogWithSource()));
- // Create a request for the session. There should be no matching session
- // (aliased or otherwise) yet. A pending request is necessary for the session
- // to create an alias on host resolution completion.
- std::unique_ptr<SpdySessionPool::SpdySessionRequest> request;
- bool is_blocking_request_for_session = false;
- SpdySessionRequestDelegate request_delegate;
- EXPECT_FALSE(pool->RequestSession(
- key, enable_ip_based_pooling, is_websocket, NetLogWithSource(),
- /* on_blocking_request_destroyed_callback = */ base::RepeatingClosure(),
- &request_delegate, &request, &is_blocking_request_for_session));
- EXPECT_TRUE(request);
- EXPECT_TRUE(is_blocking_request_for_session);
- AddressList address_list;
- EXPECT_THAT(ParseAddressList(ip_address_list, &address_list.endpoints()),
- IsOk());
- address_list = AddressList::CopyWithPort(address_list, 443);
- // Simulate a host resolution completing.
- OnHostResolutionCallbackResult result =
- pool->OnHostResolutionComplete(key, is_websocket, address_list);
- // Spin the message loop and see if it creates an H2 session.
- base::RunLoop().RunUntilIdle();
- EXPECT_EQ(request_delegate.callback_invoked(),
- result == OnHostResolutionCallbackResult::kMayBeDeletedAsync);
- EXPECT_EQ(request_delegate.callback_invoked(),
- request_delegate.spdy_session() != nullptr);
- request.reset();
- // Calling RequestSession again should return request_delegate.spdy_session()
- // (i.e. the newly created session, if a session was created, or nullptr, if
- // one was not.)
- EXPECT_EQ(request_delegate.spdy_session(),
- pool->RequestSession(key, enable_ip_based_pooling, is_websocket,
- NetLogWithSource(),
- /* on_blocking_request_destroyed_callback = */
- base::RepeatingClosure(), &request_delegate,
- &request, &is_blocking_request_for_session)
- .get());
- return request_delegate.spdy_session() != nullptr;
- }
- // A delegate that opens a new session when it is closed.
- class SessionOpeningDelegate : public SpdyStream::Delegate {
- public:
- SessionOpeningDelegate(SpdySessionPool* spdy_session_pool,
- const SpdySessionKey& key)
- : spdy_session_pool_(spdy_session_pool),
- key_(key) {}
- ~SessionOpeningDelegate() override = default;
- void OnHeadersSent() override {}
- void OnEarlyHintsReceived(const spdy::Http2HeaderBlock& headers) override {}
- void OnHeadersReceived(
- const spdy::Http2HeaderBlock& response_headers,
- const spdy::Http2HeaderBlock* pushed_request_headers) override {}
- void OnDataReceived(std::unique_ptr<SpdyBuffer> buffer) override {}
- void OnDataSent() override {}
- void OnTrailers(const spdy::Http2HeaderBlock& trailers) override {}
- void OnClose(int status) override {
- std::ignore = CreateFakeSpdySession(spdy_session_pool_, key_);
- }
- bool CanGreaseFrameType() const override { return false; }
- NetLogSource source_dependency() const override { return NetLogSource(); }
- private:
- const raw_ptr<SpdySessionPool> spdy_session_pool_;
- const SpdySessionKey key_;
- };
- // Set up a SpdyStream to create a new session when it is closed.
- // CloseCurrentSessions should not close the newly-created session.
- TEST_F(SpdySessionPoolTest, CloseCurrentSessions) {
- const char kTestHost[] = "www.foo.com";
- const int kTestPort = 80;
- HostPortPair test_host_port_pair(kTestHost, kTestPort);
- SpdySessionKey test_key = SpdySessionKey(
- test_host_port_pair, ProxyServer::Direct(), PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- MockConnect connect_data(SYNCHRONOUS, OK);
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever.
- };
- StaticSocketDataProvider data(reads, base::span<MockWrite>());
- data.set_connect_data(connect_data);
- session_deps_.socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
- CreateNetworkSession();
- // Setup the first session to the first host.
- base::WeakPtr<SpdySession> session =
- CreateSpdySession(http_session_.get(), test_key, NetLogWithSource());
- // Flush the SpdySession::OnReadComplete() task.
- base::RunLoop().RunUntilIdle();
- // Verify that we have sessions for everything.
- EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_key));
- // Set the stream to create a new session when it is closed.
- base::WeakPtr<SpdyStream> spdy_stream = CreateStreamSynchronously(
- SPDY_BIDIRECTIONAL_STREAM, session, GURL("http://www.foo.com"), MEDIUM,
- NetLogWithSource());
- SessionOpeningDelegate delegate(spdy_session_pool_, test_key);
- spdy_stream->SetDelegate(&delegate);
- // Close the current session.
- spdy_session_pool_->CloseCurrentSessions(ERR_ABORTED);
- EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_key));
- }
- TEST_F(SpdySessionPoolTest, CloseCurrentIdleSessions) {
- const std::string close_session_description = "Closing idle sessions.";
- MockConnect connect_data(SYNCHRONOUS, OK);
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever.
- };
- StaticSocketDataProvider data1(reads, base::span<MockWrite>());
- data1.set_connect_data(connect_data);
- session_deps_.socket_factory->AddSocketDataProvider(&data1);
- AddSSLSocketData();
- AddSSLSocketData();
- AddSSLSocketData();
- CreateNetworkSession();
- // Set up session 1
- const GURL url1("https://www.example.org");
- HostPortPair test_host_port_pair1(HostPortPair::FromURL(url1));
- SpdySessionKey key1(test_host_port_pair1, ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> session1 =
- CreateSpdySession(http_session_.get(), key1, NetLogWithSource());
- base::WeakPtr<SpdyStream> spdy_stream1 = CreateStreamSynchronously(
- SPDY_BIDIRECTIONAL_STREAM, session1, url1, MEDIUM, NetLogWithSource());
- ASSERT_TRUE(spdy_stream1);
- // Set up session 2
- StaticSocketDataProvider data2(reads, base::span<MockWrite>());
- session_deps_.socket_factory->AddSocketDataProvider(&data2);
- const GURL url2("https://mail.example.org");
- HostPortPair test_host_port_pair2(HostPortPair::FromURL(url2));
- SpdySessionKey key2(test_host_port_pair2, ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> session2 =
- CreateSpdySession(http_session_.get(), key2, NetLogWithSource());
- base::WeakPtr<SpdyStream> spdy_stream2 = CreateStreamSynchronously(
- SPDY_BIDIRECTIONAL_STREAM, session2, url2, MEDIUM, NetLogWithSource());
- ASSERT_TRUE(spdy_stream2);
- // Set up session 3
- StaticSocketDataProvider data3(reads, base::span<MockWrite>());
- data3.set_connect_data(connect_data);
- session_deps_.socket_factory->AddSocketDataProvider(&data3);
- const GURL url3("https://mail.example.com");
- HostPortPair test_host_port_pair3(HostPortPair::FromURL(url3));
- SpdySessionKey key3(test_host_port_pair3, ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> session3 =
- CreateSpdySession(http_session_.get(), key3, NetLogWithSource());
- base::WeakPtr<SpdyStream> spdy_stream3 = CreateStreamSynchronously(
- SPDY_BIDIRECTIONAL_STREAM, session3, url3, MEDIUM, NetLogWithSource());
- ASSERT_TRUE(spdy_stream3);
- // All sessions are active and not closed
- EXPECT_TRUE(session1->is_active());
- EXPECT_TRUE(session1->IsAvailable());
- EXPECT_TRUE(session2->is_active());
- EXPECT_TRUE(session2->IsAvailable());
- EXPECT_TRUE(session3->is_active());
- EXPECT_TRUE(session3->IsAvailable());
- // Should not do anything, all are active
- spdy_session_pool_->CloseCurrentIdleSessions(close_session_description);
- EXPECT_TRUE(session1->is_active());
- EXPECT_TRUE(session1->IsAvailable());
- EXPECT_TRUE(session2->is_active());
- EXPECT_TRUE(session2->IsAvailable());
- EXPECT_TRUE(session3->is_active());
- EXPECT_TRUE(session3->IsAvailable());
- // Make sessions 1 and 3 inactive, but keep them open.
- // Session 2 still open and active
- session1->CloseCreatedStream(spdy_stream1, OK);
- EXPECT_FALSE(spdy_stream1);
- session3->CloseCreatedStream(spdy_stream3, OK);
- EXPECT_FALSE(spdy_stream3);
- EXPECT_FALSE(session1->is_active());
- EXPECT_TRUE(session1->IsAvailable());
- EXPECT_TRUE(session2->is_active());
- EXPECT_TRUE(session2->IsAvailable());
- EXPECT_FALSE(session3->is_active());
- EXPECT_TRUE(session3->IsAvailable());
- // Should close session 1 and 3, 2 should be left open
- spdy_session_pool_->CloseCurrentIdleSessions(close_session_description);
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(session1);
- EXPECT_TRUE(session2->is_active());
- EXPECT_TRUE(session2->IsAvailable());
- EXPECT_FALSE(session3);
- // Should not do anything
- spdy_session_pool_->CloseCurrentIdleSessions(close_session_description);
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(session2->is_active());
- EXPECT_TRUE(session2->IsAvailable());
- // Make 2 not active
- session2->CloseCreatedStream(spdy_stream2, OK);
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(spdy_stream2);
- EXPECT_FALSE(session2->is_active());
- EXPECT_TRUE(session2->IsAvailable());
- // This should close session 2
- spdy_session_pool_->CloseCurrentIdleSessions(close_session_description);
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(session2);
- }
- // Set up a SpdyStream to create a new session when it is closed.
- // CloseAllSessions should close the newly-created session.
- TEST_F(SpdySessionPoolTest, CloseAllSessions) {
- const char kTestHost[] = "www.foo.com";
- const int kTestPort = 80;
- HostPortPair test_host_port_pair(kTestHost, kTestPort);
- SpdySessionKey test_key = SpdySessionKey(
- test_host_port_pair, ProxyServer::Direct(), PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- MockConnect connect_data(SYNCHRONOUS, OK);
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever.
- };
- StaticSocketDataProvider data(reads, base::span<MockWrite>());
- data.set_connect_data(connect_data);
- session_deps_.socket_factory->AddSocketDataProvider(&data);
- SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
- session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
- CreateNetworkSession();
- // Setup the first session to the first host.
- base::WeakPtr<SpdySession> session =
- CreateSpdySession(http_session_.get(), test_key, NetLogWithSource());
- // Flush the SpdySession::OnReadComplete() task.
- base::RunLoop().RunUntilIdle();
- // Verify that we have sessions for everything.
- EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_key));
- // Set the stream to create a new session when it is closed.
- base::WeakPtr<SpdyStream> spdy_stream = CreateStreamSynchronously(
- SPDY_BIDIRECTIONAL_STREAM, session, GURL("http://www.foo.com"), MEDIUM,
- NetLogWithSource());
- SessionOpeningDelegate delegate(spdy_session_pool_, test_key);
- spdy_stream->SetDelegate(&delegate);
- // Close the current session.
- spdy_session_pool_->CloseAllSessions();
- EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_key));
- }
- // Code testing SpdySessionPool::OnIPAddressChange requires a SpdySessionPool
- // with some active sessions. This fixture takes care of setting most things up
- // but doesn't create the pool yet, allowing tests to possibly further
- // configure sessions_deps_.
- class SpdySessionPoolOnIPAddressChangeTest : public SpdySessionPoolTest {
- protected:
- SpdySessionPoolOnIPAddressChangeTest()
- : test_host_port_pair_(kTestHost, kTestPort),
- reads_({
- MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever.
- }),
- test_key_(SpdySessionKey(test_host_port_pair_,
- ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse,
- SocketTag(),
- NetworkIsolationKey(),
- SecureDnsPolicy::kAllow)),
- connect_data_(SYNCHRONOUS, OK),
- data_(reads_, base::span<MockWrite>()),
- ssl_(SYNCHRONOUS, OK) {
- data_.set_connect_data(connect_data_);
- session_deps_.socket_factory->AddSocketDataProvider(&data_);
- session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_);
- }
- static constexpr char kTestHost[] = "www.foo.com";
- static constexpr int kTestPort = 80;
- static constexpr int kReadSize = 1;
- const HostPortPair test_host_port_pair_;
- const std::array<MockRead, kReadSize> reads_;
- const SpdySessionKey test_key_;
- const MockConnect connect_data_;
- StaticSocketDataProvider data_;
- SSLSocketDataProvider ssl_;
- };
- TEST_F(SpdySessionPoolOnIPAddressChangeTest, DoNotIgnoreIPAddressChanges) {
- // Default behavior should be ignore_ip_address_changes = false;
- CreateNetworkSession();
- base::WeakPtr<SpdySession> session =
- CreateSpdySession(http_session_.get(), test_key_, NetLogWithSource());
- // Flush the SpdySession::OnReadComplete() task.
- base::RunLoop().RunUntilIdle();
- // Verify that we have a session.
- EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_key_));
- // Without setting session_deps_.ignore_ip_address_changes = true the pool
- // should close (or make unavailable) all sessions after an IP address change.
- NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_key_));
- }
- TEST_F(SpdySessionPoolOnIPAddressChangeTest, IgnoreIPAddressChanges) {
- session_deps_.ignore_ip_address_changes = true;
- CreateNetworkSession();
- // Setup the first session to the first host.
- base::WeakPtr<SpdySession> session =
- CreateSpdySession(http_session_.get(), test_key_, NetLogWithSource());
- // Flush the SpdySession::OnReadComplete() task.
- base::RunLoop().RunUntilIdle();
- // Verify that we have a session.
- EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_key_));
- // Since we set ignore_ip_address_changes = true, the session should still be
- // there after an IP address change.
- NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_key_));
- }
- // This test has three variants, one for each style of closing the connection.
- // If |clean_via_close_current_sessions| is SPDY_POOL_CLOSE_SESSIONS_MANUALLY,
- // the sessions are closed manually, calling SpdySessionPool::Remove() directly.
- // If |clean_via_close_current_sessions| is SPDY_POOL_CLOSE_CURRENT_SESSIONS,
- // sessions are closed with SpdySessionPool::CloseCurrentSessions().
- // If |clean_via_close_current_sessions| is SPDY_POOL_CLOSE_IDLE_SESSIONS,
- // sessions are closed with SpdySessionPool::CloseIdleSessions().
- void SpdySessionPoolTest::RunIPPoolingTest(
- SpdyPoolCloseSessionsType close_sessions_type) {
- constexpr int kTestPort = 443;
- struct TestHosts {
- std::string url;
- std::string name;
- std::string iplist;
- SpdySessionKey key;
- } test_hosts[] = {
- {"http://www.example.org", "www.example.org",
- "192.0.2.33,192.168.0.1,192.168.0.5"},
- {"http://mail.example.org", "mail.example.org",
- "192.168.0.2,192.168.0.3,192.168.0.5,192.0.2.33"},
- {"http://mail.example.com", "mail.example.com",
- "192.168.0.4,192.168.0.3"},
- };
- for (auto& test_host : test_hosts) {
- session_deps_.host_resolver->rules()->AddIPLiteralRule(
- test_host.name, test_host.iplist, std::string());
- test_host.key = SpdySessionKey(
- HostPortPair(test_host.name, kTestPort), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED, SpdySessionKey::IsProxySession::kFalse,
- SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- }
- MockConnect connect_data(SYNCHRONOUS, OK);
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever.
- };
- StaticSocketDataProvider data1(reads, base::span<MockWrite>());
- data1.set_connect_data(connect_data);
- session_deps_.socket_factory->AddSocketDataProvider(&data1);
- AddSSLSocketData();
- CreateNetworkSession();
- // Setup the first session to the first host.
- base::WeakPtr<SpdySession> session = CreateSpdySession(
- http_session_.get(), test_hosts[0].key, NetLogWithSource());
- // Flush the SpdySession::OnReadComplete() task.
- base::RunLoop().RunUntilIdle();
- // The third host has no overlap with the first, so it can't pool IPs.
- EXPECT_FALSE(TryCreateAliasedSpdySession(
- spdy_session_pool_, test_hosts[2].key, test_hosts[2].iplist));
- // The second host overlaps with the first, and should IP pool.
- EXPECT_TRUE(TryCreateAliasedSpdySession(spdy_session_pool_, test_hosts[1].key,
- test_hosts[1].iplist));
- // However, if IP pooling is disabled, FindAvailableSession() should not find
- // |session| for the second host.
- base::WeakPtr<SpdySession> session1 =
- spdy_session_pool_->FindAvailableSession(
- test_hosts[1].key, /* enable_ip_based_pooling = */ false,
- /* is_websocket = */ false, NetLogWithSource());
- EXPECT_FALSE(session1);
- // Verify that the second host, through a proxy, won't share the IP, even if
- // the IP list matches.
- SpdySessionKey proxy_key(
- test_hosts[1].key.host_port_pair(),
- PacResultElementToProxyServer("HTTP http://proxy.foo.com/"),
- PRIVACY_MODE_DISABLED, SpdySessionKey::IsProxySession::kFalse,
- SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- EXPECT_FALSE(TryCreateAliasedSpdySession(spdy_session_pool_, proxy_key,
- test_hosts[1].iplist));
- // Verify that the second host, with a different SecureDnsPolicy,
- // won't share the IP, even if the IP list matches.
- SpdySessionKey disable_secure_dns_key(
- test_hosts[1].key.host_port_pair(), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED, SpdySessionKey::IsProxySession::kFalse,
- SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kDisable);
- EXPECT_FALSE(TryCreateAliasedSpdySession(
- spdy_session_pool_, disable_secure_dns_key, test_hosts[1].iplist));
- // Overlap between 2 and 3 is not transitive to 1.
- EXPECT_FALSE(TryCreateAliasedSpdySession(
- spdy_session_pool_, test_hosts[2].key, test_hosts[2].iplist));
- // Create a new session to host 2.
- StaticSocketDataProvider data2(reads, base::span<MockWrite>());
- data2.set_connect_data(connect_data);
- session_deps_.socket_factory->AddSocketDataProvider(&data2);
- AddSSLSocketData();
- base::WeakPtr<SpdySession> session2 = CreateSpdySession(
- http_session_.get(), test_hosts[2].key, NetLogWithSource());
- // Verify that we have sessions for everything.
- EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[0].key));
- EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[1].key));
- EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[2].key));
- // Grab the session to host 1 and verify that it is the same session
- // we got with host 0, and that is a different from host 2's session.
- session1 = spdy_session_pool_->FindAvailableSession(
- test_hosts[1].key, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ false, NetLogWithSource());
- EXPECT_EQ(session.get(), session1.get());
- EXPECT_NE(session2.get(), session1.get());
- // Remove the aliases and observe that we still have a session for host1.
- SpdySessionPoolPeer pool_peer(spdy_session_pool_);
- pool_peer.RemoveAliases(test_hosts[0].key);
- pool_peer.RemoveAliases(test_hosts[1].key);
- EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[1].key));
- // Cleanup the sessions.
- switch (close_sessions_type) {
- case SPDY_POOL_CLOSE_SESSIONS_MANUALLY:
- session->CloseSessionOnError(ERR_ABORTED, std::string());
- session2->CloseSessionOnError(ERR_ABORTED, std::string());
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(session);
- EXPECT_FALSE(session2);
- break;
- case SPDY_POOL_CLOSE_CURRENT_SESSIONS:
- spdy_session_pool_->CloseCurrentSessions(ERR_ABORTED);
- break;
- case SPDY_POOL_CLOSE_IDLE_SESSIONS:
- GURL url(test_hosts[0].url);
- base::WeakPtr<SpdyStream> spdy_stream = CreateStreamSynchronously(
- SPDY_BIDIRECTIONAL_STREAM, session, url, MEDIUM, NetLogWithSource());
- GURL url1(test_hosts[1].url);
- base::WeakPtr<SpdyStream> spdy_stream1 =
- CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM, session1, url1,
- MEDIUM, NetLogWithSource());
- GURL url2(test_hosts[2].url);
- base::WeakPtr<SpdyStream> spdy_stream2 =
- CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM, session2, url2,
- MEDIUM, NetLogWithSource());
- // Close streams to make spdy_session and spdy_session1 inactive.
- session->CloseCreatedStream(spdy_stream, OK);
- EXPECT_FALSE(spdy_stream);
- session1->CloseCreatedStream(spdy_stream1, OK);
- EXPECT_FALSE(spdy_stream1);
- // Check spdy_session and spdy_session1 are not closed.
- EXPECT_FALSE(session->is_active());
- EXPECT_TRUE(session->IsAvailable());
- EXPECT_FALSE(session1->is_active());
- EXPECT_TRUE(session1->IsAvailable());
- EXPECT_TRUE(session2->is_active());
- EXPECT_TRUE(session2->IsAvailable());
- // Test that calling CloseIdleSessions, does not cause a crash.
- // http://crbug.com/181400
- spdy_session_pool_->CloseCurrentIdleSessions("Closing idle sessions.");
- base::RunLoop().RunUntilIdle();
- // Verify spdy_session and spdy_session1 are closed.
- EXPECT_FALSE(session);
- EXPECT_FALSE(session1);
- EXPECT_TRUE(session2->is_active());
- EXPECT_TRUE(session2->IsAvailable());
- spdy_stream2->Cancel(ERR_ABORTED);
- EXPECT_FALSE(spdy_stream);
- EXPECT_FALSE(spdy_stream1);
- EXPECT_FALSE(spdy_stream2);
- session2->CloseSessionOnError(ERR_ABORTED, std::string());
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(session2);
- break;
- }
- // Verify that the map is all cleaned up.
- EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[0].key));
- EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[1].key));
- EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[2].key));
- EXPECT_FALSE(TryCreateAliasedSpdySession(
- spdy_session_pool_, test_hosts[0].key, test_hosts[0].iplist));
- EXPECT_FALSE(TryCreateAliasedSpdySession(
- spdy_session_pool_, test_hosts[1].key, test_hosts[1].iplist));
- EXPECT_FALSE(TryCreateAliasedSpdySession(
- spdy_session_pool_, test_hosts[2].key, test_hosts[2].iplist));
- }
- void SpdySessionPoolTest::RunIPPoolingDisabledTest(SSLSocketDataProvider* ssl) {
- constexpr int kTestPort = 443;
- struct TestHosts {
- std::string name;
- std::string iplist;
- SpdySessionKey key;
- } test_hosts[] = {
- {"www.webkit.org", "192.0.2.33,192.168.0.1,192.168.0.5"},
- {"js.webkit.com", "192.168.0.4,192.168.0.1,192.0.2.33"},
- };
- session_deps_.host_resolver->set_synchronous_mode(true);
- for (auto& test_host : test_hosts) {
- session_deps_.host_resolver->rules()->AddIPLiteralRule(
- test_host.name, test_host.iplist, std::string());
- // Setup a SpdySessionKey
- test_host.key = SpdySessionKey(
- HostPortPair(test_host.name, kTestPort), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED, SpdySessionKey::IsProxySession::kFalse,
- SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- }
- MockRead reads[] = {
- MockRead(ASYNC, ERR_IO_PENDING),
- };
- StaticSocketDataProvider data(reads, base::span<MockWrite>());
- session_deps_.socket_factory->AddSocketDataProvider(&data);
- session_deps_.socket_factory->AddSSLSocketDataProvider(ssl);
- CreateNetworkSession();
- base::WeakPtr<SpdySession> spdy_session = CreateSpdySession(
- http_session_.get(), test_hosts[0].key, NetLogWithSource());
- EXPECT_TRUE(
- HasSpdySession(http_session_->spdy_session_pool(), test_hosts[0].key));
- EXPECT_FALSE(TryCreateAliasedSpdySession(
- spdy_session_pool_, test_hosts[1].key, test_hosts[1].iplist,
- /* enable_ip_based_pooling = */ false));
- http_session_->spdy_session_pool()->CloseAllSessions();
- }
- TEST_F(SpdySessionPoolTest, IPPooling) {
- RunIPPoolingTest(SPDY_POOL_CLOSE_SESSIONS_MANUALLY);
- }
- TEST_F(SpdySessionPoolTest, IPPoolingCloseCurrentSessions) {
- RunIPPoolingTest(SPDY_POOL_CLOSE_CURRENT_SESSIONS);
- }
- TEST_F(SpdySessionPoolTest, IPPoolingCloseIdleSessions) {
- RunIPPoolingTest(SPDY_POOL_CLOSE_IDLE_SESSIONS);
- }
- // Regression test for https://crbug.com/643025.
- TEST_F(SpdySessionPoolTest, IPPoolingNetLog) {
- // Define two hosts with identical IP address.
- constexpr int kTestPort = 443;
- struct TestHosts {
- std::string name;
- std::string iplist;
- SpdySessionKey key;
- } test_hosts[] = {
- {"www.example.org", "192.168.0.1"}, {"mail.example.org", "192.168.0.1"},
- };
- // Populate the HostResolver cache.
- session_deps_.host_resolver->set_synchronous_mode(true);
- for (auto& test_host : test_hosts) {
- session_deps_.host_resolver->rules()->AddIPLiteralRule(
- test_host.name, test_host.iplist, std::string());
- test_host.key = SpdySessionKey(
- HostPortPair(test_host.name, kTestPort), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED, SpdySessionKey::IsProxySession::kFalse,
- SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- }
- MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING)};
- StaticSocketDataProvider data(reads, base::span<MockWrite>());
- MockConnect connect_data(SYNCHRONOUS, OK);
- data.set_connect_data(connect_data);
- session_deps_.socket_factory->AddSocketDataProvider(&data);
- AddSSLSocketData();
- CreateNetworkSession();
- // Open SpdySession to the first host.
- base::WeakPtr<SpdySession> session0 = CreateSpdySession(
- http_session_.get(), test_hosts[0].key, NetLogWithSource());
- // The second host should pool to the existing connection.
- RecordingNetLogObserver net_log_observer;
- base::HistogramTester histogram_tester;
- EXPECT_TRUE(TryCreateAliasedSpdySession(spdy_session_pool_, test_hosts[1].key,
- test_hosts[1].iplist));
- histogram_tester.ExpectTotalCount("Net.SpdySessionGet", 1);
- base::WeakPtr<SpdySession> session1 =
- spdy_session_pool_->FindAvailableSession(
- test_hosts[1].key, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ false,
- NetLogWithSource::Make(NetLogSourceType::NONE));
- EXPECT_EQ(session0.get(), session1.get());
- ASSERT_EQ(1u, net_log_observer.GetSize());
- histogram_tester.ExpectTotalCount("Net.SpdySessionGet", 2);
- // FindAvailableSession() should have logged a netlog event indicating IP
- // pooling.
- auto entry_list = net_log_observer.GetEntries();
- EXPECT_EQ(
- NetLogEventType::HTTP2_SESSION_POOL_FOUND_EXISTING_SESSION_FROM_IP_POOL,
- entry_list[0].type);
- // Both FindAvailableSession() calls (including one from
- // TryCreateAliasedSpdySession) should log histogram entries indicating IP
- // pooling.
- histogram_tester.ExpectUniqueSample("Net.SpdySessionGet", 2, 2);
- }
- TEST_F(SpdySessionPoolTest, IPPoolingDisabled) {
- // Define two hosts with identical IP address.
- constexpr int kTestPort = 443;
- struct TestHosts {
- std::string name;
- std::string iplist;
- SpdySessionKey key;
- } test_hosts[] = {
- {"www.example.org", "192.168.0.1"}, {"mail.example.org", "192.168.0.1"},
- };
- // Populate the HostResolver cache.
- session_deps_.host_resolver->set_synchronous_mode(true);
- for (auto& test_host : test_hosts) {
- session_deps_.host_resolver->rules()->AddIPLiteralRule(
- test_host.name, test_host.iplist, std::string());
- test_host.key = SpdySessionKey(
- HostPortPair(test_host.name, kTestPort), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED, SpdySessionKey::IsProxySession::kFalse,
- SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- }
- MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING)};
- StaticSocketDataProvider data(reads, base::span<MockWrite>());
- MockConnect connect_data(SYNCHRONOUS, OK);
- data.set_connect_data(connect_data);
- session_deps_.socket_factory->AddSocketDataProvider(&data);
- AddSSLSocketData();
- MockRead reads1[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING)};
- StaticSocketDataProvider data1(reads1, base::span<MockWrite>());
- MockConnect connect_data1(SYNCHRONOUS, OK);
- data1.set_connect_data(connect_data1);
- session_deps_.socket_factory->AddSocketDataProvider(&data1);
- AddSSLSocketData();
- CreateNetworkSession();
- // Open SpdySession to the first host.
- base::WeakPtr<SpdySession> session0 = CreateSpdySession(
- http_session_.get(), test_hosts[0].key, NetLogWithSource());
- // |test_hosts[1]| should pool to the existing connection.
- EXPECT_TRUE(TryCreateAliasedSpdySession(spdy_session_pool_, test_hosts[1].key,
- test_hosts[1].iplist));
- base::WeakPtr<SpdySession> session1 =
- spdy_session_pool_->FindAvailableSession(
- test_hosts[1].key, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ false, NetLogWithSource());
- EXPECT_EQ(session0.get(), session1.get());
- // A request to the second host should not pool to the existing connection if
- // IP based pooling is disabled.
- session1 = spdy_session_pool_->FindAvailableSession(
- test_hosts[1].key, /* enable_ip_based_pooling = */ false,
- /* is_websocket = */ false, NetLogWithSource());
- EXPECT_FALSE(session1);
- // It should be possible to open a new SpdySession, even if a previous call to
- // FindAvailableSession() linked the second key to the first connection in the
- // IP pooled bucket of SpdySessionPool::available_session_map_.
- session1 = CreateSpdySessionWithIpBasedPoolingDisabled(
- http_session_.get(), test_hosts[1].key, NetLogWithSource());
- EXPECT_TRUE(session1);
- EXPECT_NE(session0.get(), session1.get());
- }
- // Verifies that an SSL connection with client authentication disables SPDY IP
- // pooling.
- TEST_F(SpdySessionPoolTest, IPPoolingClientCert) {
- SSLSocketDataProvider ssl(ASYNC, OK);
- ssl.ssl_info.cert = X509Certificate::CreateFromBytes(webkit_der);
- ASSERT_TRUE(ssl.ssl_info.cert);
- ssl.ssl_info.client_cert_sent = true;
- ssl.next_proto = kProtoHTTP2;
- RunIPPoolingDisabledTest(&ssl);
- }
- // Construct a Pool with SpdySessions in various availability states. Simulate
- // an IP address change. Ensure sessions gracefully shut down. Regression test
- // for crbug.com/379469.
- TEST_F(SpdySessionPoolTest, GoAwayOnIPAddressChanged) {
- MockConnect connect_data(SYNCHRONOUS, OK);
- session_deps_.host_resolver->set_synchronous_mode(true);
- // This isn't testing anything having to do with SPDY frames; we
- // can ignore issues of how dependencies are set. We default to
- // setting them (when doing the appropriate protocol) since that's
- // where we're eventually headed for all HTTP/2 connections.
- SpdyTestUtil spdy_util;
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever.
- };
- spdy::SpdySerializedFrame req(
- spdy_util.ConstructSpdyGet("http://www.example.org", 1, MEDIUM));
- MockWrite writes[] = {CreateMockWrite(req, 1)};
- StaticSocketDataProvider dataA(reads, writes);
- dataA.set_connect_data(connect_data);
- session_deps_.socket_factory->AddSocketDataProvider(&dataA);
- AddSSLSocketData();
- session_deps_.go_away_on_ip_change = true;
- CreateNetworkSession();
- // Set up session A: Going away, but with an active stream.
- const std::string kTestHostA("www.example.org");
- HostPortPair test_host_port_pairA(kTestHostA, 80);
- SpdySessionKey keyA(test_host_port_pairA, ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> sessionA =
- CreateSpdySession(http_session_.get(), keyA, NetLogWithSource());
- GURL urlA("http://www.example.org");
- base::WeakPtr<SpdyStream> spdy_streamA = CreateStreamSynchronously(
- SPDY_BIDIRECTIONAL_STREAM, sessionA, urlA, MEDIUM, NetLogWithSource());
- test::StreamDelegateDoNothing delegateA(spdy_streamA);
- spdy_streamA->SetDelegate(&delegateA);
- spdy::Http2HeaderBlock headers(
- spdy_util.ConstructGetHeaderBlock(urlA.spec()));
- spdy_streamA->SendRequestHeaders(std::move(headers), NO_MORE_DATA_TO_SEND);
- base::RunLoop().RunUntilIdle(); // Allow headers to write.
- EXPECT_TRUE(delegateA.send_headers_completed());
- sessionA->MakeUnavailable();
- EXPECT_TRUE(sessionA->IsGoingAway());
- EXPECT_FALSE(delegateA.StreamIsClosed());
- // Set up session B: Available, with a created stream.
- StaticSocketDataProvider dataB(reads, writes);
- dataB.set_connect_data(connect_data);
- session_deps_.socket_factory->AddSocketDataProvider(&dataB);
- AddSSLSocketData();
- const std::string kTestHostB("mail.example.org");
- HostPortPair test_host_port_pairB(kTestHostB, 80);
- SpdySessionKey keyB(test_host_port_pairB, ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> sessionB =
- CreateSpdySession(http_session_.get(), keyB, NetLogWithSource());
- EXPECT_TRUE(sessionB->IsAvailable());
- GURL urlB("http://mail.example.org");
- base::WeakPtr<SpdyStream> spdy_streamB = CreateStreamSynchronously(
- SPDY_BIDIRECTIONAL_STREAM, sessionB, urlB, MEDIUM, NetLogWithSource());
- test::StreamDelegateDoNothing delegateB(spdy_streamB);
- spdy_streamB->SetDelegate(&delegateB);
- // Set up session C: Draining.
- StaticSocketDataProvider dataC(reads, writes);
- dataC.set_connect_data(connect_data);
- session_deps_.socket_factory->AddSocketDataProvider(&dataC);
- AddSSLSocketData();
- const std::string kTestHostC("mail.example.com");
- HostPortPair test_host_port_pairC(kTestHostC, 80);
- SpdySessionKey keyC(test_host_port_pairC, ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> sessionC =
- CreateSpdySession(http_session_.get(), keyC, NetLogWithSource());
- sessionC->CloseSessionOnError(ERR_HTTP2_PROTOCOL_ERROR, "Error!");
- EXPECT_TRUE(sessionC->IsDraining());
- spdy_session_pool_->OnIPAddressChanged();
- EXPECT_TRUE(sessionA->IsGoingAway());
- EXPECT_TRUE(sessionB->IsDraining());
- EXPECT_TRUE(sessionC->IsDraining());
- EXPECT_EQ(1u,
- num_active_streams(sessionA)); // Active stream is still active.
- EXPECT_FALSE(delegateA.StreamIsClosed());
- EXPECT_TRUE(delegateB.StreamIsClosed()); // Created stream was closed.
- EXPECT_THAT(delegateB.WaitForClose(), IsError(ERR_NETWORK_CHANGED));
- sessionA->CloseSessionOnError(ERR_ABORTED, "Closing");
- sessionB->CloseSessionOnError(ERR_ABORTED, "Closing");
- EXPECT_TRUE(delegateA.StreamIsClosed());
- EXPECT_THAT(delegateA.WaitForClose(), IsError(ERR_ABORTED));
- }
- // Construct a Pool with SpdySessions in various availability states. Simulate
- // an IP address change. Ensure sessions gracefully shut down. Regression test
- // for crbug.com/379469.
- TEST_F(SpdySessionPoolTest, CloseOnIPAddressChanged) {
- MockConnect connect_data(SYNCHRONOUS, OK);
- session_deps_.host_resolver->set_synchronous_mode(true);
- // This isn't testing anything having to do with SPDY frames; we
- // can ignore issues of how dependencies are set. We default to
- // setting them (when doing the appropriate protocol) since that's
- // where we're eventually headed for all HTTP/2 connections.
- SpdyTestUtil spdy_util;
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever.
- };
- spdy::SpdySerializedFrame req(
- spdy_util.ConstructSpdyGet("http://www.example.org", 1, MEDIUM));
- MockWrite writes[] = {CreateMockWrite(req, 1)};
- StaticSocketDataProvider dataA(reads, writes);
- dataA.set_connect_data(connect_data);
- session_deps_.socket_factory->AddSocketDataProvider(&dataA);
- AddSSLSocketData();
- session_deps_.go_away_on_ip_change = false;
- CreateNetworkSession();
- // Set up session A: Going away, but with an active stream.
- const std::string kTestHostA("www.example.org");
- HostPortPair test_host_port_pairA(kTestHostA, 80);
- SpdySessionKey keyA(test_host_port_pairA, ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> sessionA =
- CreateSpdySession(http_session_.get(), keyA, NetLogWithSource());
- GURL urlA("http://www.example.org");
- base::WeakPtr<SpdyStream> spdy_streamA = CreateStreamSynchronously(
- SPDY_BIDIRECTIONAL_STREAM, sessionA, urlA, MEDIUM, NetLogWithSource());
- test::StreamDelegateDoNothing delegateA(spdy_streamA);
- spdy_streamA->SetDelegate(&delegateA);
- spdy::Http2HeaderBlock headers(
- spdy_util.ConstructGetHeaderBlock(urlA.spec()));
- spdy_streamA->SendRequestHeaders(std::move(headers), NO_MORE_DATA_TO_SEND);
- base::RunLoop().RunUntilIdle(); // Allow headers to write.
- EXPECT_TRUE(delegateA.send_headers_completed());
- sessionA->MakeUnavailable();
- EXPECT_TRUE(sessionA->IsGoingAway());
- EXPECT_FALSE(delegateA.StreamIsClosed());
- // Set up session B: Available, with a created stream.
- StaticSocketDataProvider dataB(reads, writes);
- dataB.set_connect_data(connect_data);
- session_deps_.socket_factory->AddSocketDataProvider(&dataB);
- AddSSLSocketData();
- const std::string kTestHostB("mail.example.org");
- HostPortPair test_host_port_pairB(kTestHostB, 80);
- SpdySessionKey keyB(test_host_port_pairB, ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> sessionB =
- CreateSpdySession(http_session_.get(), keyB, NetLogWithSource());
- EXPECT_TRUE(sessionB->IsAvailable());
- GURL urlB("http://mail.example.org");
- base::WeakPtr<SpdyStream> spdy_streamB = CreateStreamSynchronously(
- SPDY_BIDIRECTIONAL_STREAM, sessionB, urlB, MEDIUM, NetLogWithSource());
- test::StreamDelegateDoNothing delegateB(spdy_streamB);
- spdy_streamB->SetDelegate(&delegateB);
- // Set up session C: Draining.
- StaticSocketDataProvider dataC(reads, writes);
- dataC.set_connect_data(connect_data);
- session_deps_.socket_factory->AddSocketDataProvider(&dataC);
- AddSSLSocketData();
- const std::string kTestHostC("mail.example.com");
- HostPortPair test_host_port_pairC(kTestHostC, 80);
- SpdySessionKey keyC(test_host_port_pairC, ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> sessionC =
- CreateSpdySession(http_session_.get(), keyC, NetLogWithSource());
- sessionC->CloseSessionOnError(ERR_HTTP2_PROTOCOL_ERROR, "Error!");
- EXPECT_TRUE(sessionC->IsDraining());
- spdy_session_pool_->OnIPAddressChanged();
- EXPECT_TRUE(sessionA->IsDraining());
- EXPECT_TRUE(sessionB->IsDraining());
- EXPECT_TRUE(sessionC->IsDraining());
- // Both streams were closed with an error.
- EXPECT_TRUE(delegateA.StreamIsClosed());
- EXPECT_THAT(delegateA.WaitForClose(), IsError(ERR_NETWORK_CHANGED));
- EXPECT_TRUE(delegateB.StreamIsClosed());
- EXPECT_THAT(delegateB.WaitForClose(), IsError(ERR_NETWORK_CHANGED));
- }
- // Regression test for https://crbug.com/789791.
- TEST_F(SpdySessionPoolTest, HandleIPAddressChangeThenShutdown) {
- MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING)};
- SpdyTestUtil spdy_util;
- spdy::SpdySerializedFrame req(
- spdy_util.ConstructSpdyGet(kDefaultUrl, 1, MEDIUM));
- MockWrite writes[] = {CreateMockWrite(req, 1)};
- StaticSocketDataProvider data(reads, writes);
- MockConnect connect_data(SYNCHRONOUS, OK);
- data.set_connect_data(connect_data);
- session_deps_.socket_factory->AddSocketDataProvider(&data);
- AddSSLSocketData();
- CreateNetworkSession();
- const GURL url(kDefaultUrl);
- SpdySessionKey key(HostPortPair::FromURL(url), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> session =
- CreateSpdySession(http_session_.get(), key, NetLogWithSource());
- base::WeakPtr<SpdyStream> spdy_stream = CreateStreamSynchronously(
- SPDY_BIDIRECTIONAL_STREAM, session, url, MEDIUM, NetLogWithSource());
- test::StreamDelegateDoNothing delegate(spdy_stream);
- spdy_stream->SetDelegate(&delegate);
- spdy::Http2HeaderBlock headers(spdy_util.ConstructGetHeaderBlock(url.spec()));
- spdy_stream->SendRequestHeaders(std::move(headers), NO_MORE_DATA_TO_SEND);
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(delegate.send_headers_completed());
- spdy_session_pool_->OnIPAddressChanged();
- #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_IOS)
- EXPECT_EQ(1u, num_active_streams(session));
- EXPECT_TRUE(session->IsGoingAway());
- EXPECT_FALSE(session->IsDraining());
- #else
- EXPECT_EQ(0u, num_active_streams(session));
- EXPECT_FALSE(session->IsGoingAway());
- EXPECT_TRUE(session->IsDraining());
- #endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_IOS)
- http_session_.reset();
- data.AllReadDataConsumed();
- data.AllWriteDataConsumed();
- }
- // Regression test for https://crbug.com/789791.
- TEST_F(SpdySessionPoolTest, HandleGracefulGoawayThenShutdown) {
- SpdyTestUtil spdy_util;
- spdy::SpdySerializedFrame goaway(spdy_util.ConstructSpdyGoAway(
- 0x7fffffff, spdy::ERROR_CODE_NO_ERROR, "Graceful shutdown."));
- MockRead reads[] = {
- MockRead(ASYNC, ERR_IO_PENDING, 1), CreateMockRead(goaway, 2),
- MockRead(ASYNC, ERR_IO_PENDING, 3), MockRead(ASYNC, OK, 4)};
- spdy::SpdySerializedFrame req(
- spdy_util.ConstructSpdyGet(kDefaultUrl, 1, MEDIUM));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- SequencedSocketData data(reads, writes);
- MockConnect connect_data(SYNCHRONOUS, OK);
- data.set_connect_data(connect_data);
- session_deps_.socket_factory->AddSocketDataProvider(&data);
- AddSSLSocketData();
- CreateNetworkSession();
- const GURL url(kDefaultUrl);
- SpdySessionKey key(HostPortPair::FromURL(url), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> session =
- CreateSpdySession(http_session_.get(), key, NetLogWithSource());
- base::WeakPtr<SpdyStream> spdy_stream = CreateStreamSynchronously(
- SPDY_BIDIRECTIONAL_STREAM, session, url, MEDIUM, NetLogWithSource());
- test::StreamDelegateDoNothing delegate(spdy_stream);
- spdy_stream->SetDelegate(&delegate);
- spdy::Http2HeaderBlock headers(spdy_util.ConstructGetHeaderBlock(url.spec()));
- spdy_stream->SendRequestHeaders(std::move(headers), NO_MORE_DATA_TO_SEND);
- // Send headers.
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(delegate.send_headers_completed());
- EXPECT_EQ(1u, num_active_streams(session));
- EXPECT_FALSE(session->IsGoingAway());
- EXPECT_FALSE(session->IsDraining());
- // Read GOAWAY.
- data.Resume();
- base::RunLoop().RunUntilIdle();
- EXPECT_EQ(1u, num_active_streams(session));
- EXPECT_TRUE(session->IsGoingAway());
- EXPECT_FALSE(session->IsDraining());
- http_session_.reset();
- data.AllReadDataConsumed();
- data.AllWriteDataConsumed();
- }
- TEST_F(SpdySessionPoolTest, IPConnectionPoolingWithWebSockets) {
- // Define two hosts with identical IP address.
- const int kTestPort = 443;
- struct TestHosts {
- std::string name;
- std::string iplist;
- SpdySessionKey key;
- } test_hosts[] = {
- {"www.example.org", "192.168.0.1"}, {"mail.example.org", "192.168.0.1"},
- };
- // Populate the HostResolver cache.
- session_deps_.host_resolver->set_synchronous_mode(true);
- for (auto& test_host : test_hosts) {
- session_deps_.host_resolver->rules()->AddIPLiteralRule(
- test_host.name, test_host.iplist, std::string());
- test_host.key = SpdySessionKey(
- HostPortPair(test_host.name, kTestPort), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED, SpdySessionKey::IsProxySession::kFalse,
- SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- }
- SpdyTestUtil spdy_util;
- spdy::SpdySerializedFrame req(
- spdy_util.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame settings_ack(spdy_util.ConstructSpdySettingsAck());
- MockWrite writes[] = {CreateMockWrite(req, 0),
- CreateMockWrite(settings_ack, 2)};
- spdy::SettingsMap settings;
- settings[spdy::SETTINGS_ENABLE_CONNECT_PROTOCOL] = 1;
- spdy::SpdySerializedFrame settings_frame(
- spdy_util.ConstructSpdySettings(settings));
- spdy::SpdySerializedFrame resp(
- spdy_util.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {CreateMockRead(settings_frame, 1),
- CreateMockRead(resp, 3), CreateMockRead(body, 4),
- MockRead(ASYNC, ERR_IO_PENDING, 5),
- MockRead(ASYNC, 0, 6)};
- SequencedSocketData data(reads, writes);
- session_deps_.socket_factory->AddSocketDataProvider(&data);
- AddSSLSocketData();
- CreateNetworkSession();
- // Create a connection to the first host.
- base::WeakPtr<SpdySession> session = CreateSpdySession(
- http_session_.get(), test_hosts[0].key, NetLogWithSource());
- // SpdySession does not support Websocket before SETTINGS frame is read.
- EXPECT_FALSE(session->support_websocket());
- NetLogWithSource net_log_with_source{
- NetLogWithSource::Make(NetLogSourceType::NONE)};
- // TryCreateAliasedSpdySession should not find |session| for either
- // SpdySessionKeys if |is_websocket| argument is set.
- EXPECT_FALSE(TryCreateAliasedSpdySession(
- spdy_session_pool_, test_hosts[0].key, test_hosts[0].iplist,
- /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ true));
- EXPECT_FALSE(TryCreateAliasedSpdySession(
- spdy_session_pool_, test_hosts[1].key, test_hosts[1].iplist,
- /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ true));
- // Start request that triggers reading the SETTINGS frame.
- const GURL url(kDefaultUrl);
- base::WeakPtr<SpdyStream> spdy_stream = CreateStreamSynchronously(
- SPDY_BIDIRECTIONAL_STREAM, session, url, LOWEST, NetLogWithSource());
- test::StreamDelegateDoNothing delegate(spdy_stream);
- spdy_stream->SetDelegate(&delegate);
- spdy::Http2HeaderBlock headers(spdy_util.ConstructGetHeaderBlock(url.spec()));
- spdy_stream->SendRequestHeaders(std::move(headers), NO_MORE_DATA_TO_SEND);
- base::RunLoop().RunUntilIdle();
- // Now SpdySession has read the SETTINGS frame and thus supports Websocket.
- EXPECT_TRUE(session->support_websocket());
- // FindAvailableSession() on the first host should now find the existing
- // session with websockets enabled, and TryCreateAliasedSpdySession() should
- // now set up aliases for |session| for the second one.
- base::WeakPtr<SpdySession> result = spdy_session_pool_->FindAvailableSession(
- test_hosts[0].key, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ true, net_log_with_source);
- EXPECT_EQ(session.get(), result.get());
- EXPECT_TRUE(TryCreateAliasedSpdySession(spdy_session_pool_, test_hosts[1].key,
- test_hosts[1].iplist,
- /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ true));
- // FindAvailableSession() should return |session| for either SpdySessionKeys
- // when IP based pooling is enabled.
- result = spdy_session_pool_->FindAvailableSession(
- test_hosts[0].key, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ true, net_log_with_source);
- EXPECT_EQ(session.get(), result.get());
- result = spdy_session_pool_->FindAvailableSession(
- test_hosts[1].key, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ true, net_log_with_source);
- EXPECT_EQ(session.get(), result.get());
- // FindAvailableSession() should only return |session| for the first
- // SpdySessionKey when IP based pooling is disabled.
- result = spdy_session_pool_->FindAvailableSession(
- test_hosts[0].key, /* enable_ip_based_pooling = */ false,
- /* is_websocket = */ true, net_log_with_source);
- EXPECT_EQ(session.get(), result.get());
- result = spdy_session_pool_->FindAvailableSession(
- test_hosts[1].key, /* enable_ip_based_pooling = */ false,
- /* is_websocket = */ true, net_log_with_source);
- EXPECT_FALSE(result);
- // Read EOF.
- data.Resume();
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(data.AllReadDataConsumed());
- EXPECT_TRUE(data.AllWriteDataConsumed());
- }
- class TestOnRequestDeletedCallback {
- public:
- TestOnRequestDeletedCallback() = default;
- TestOnRequestDeletedCallback(const TestOnRequestDeletedCallback&) = delete;
- TestOnRequestDeletedCallback& operator=(const TestOnRequestDeletedCallback&) =
- delete;
- ~TestOnRequestDeletedCallback() = default;
- base::RepeatingClosure Callback() {
- return base::BindRepeating(&TestOnRequestDeletedCallback::OnRequestDeleted,
- base::Unretained(this));
- }
- bool invoked() const { return invoked_; }
- void WaitUntilInvoked() { run_loop_.Run(); }
- void SetRequestDeletedCallback(base::OnceClosure request_deleted_callback) {
- DCHECK(!request_deleted_callback_);
- request_deleted_callback_ = std::move(request_deleted_callback);
- }
- private:
- void OnRequestDeleted() {
- EXPECT_FALSE(invoked_);
- invoked_ = true;
- if (request_deleted_callback_)
- std::move(request_deleted_callback_).Run();
- run_loop_.Quit();
- }
- bool invoked_ = false;
- base::RunLoop run_loop_;
- base::OnceClosure request_deleted_callback_;
- };
- class TestRequestDelegate
- : public SpdySessionPool::SpdySessionRequest::Delegate {
- public:
- TestRequestDelegate() = default;
- TestRequestDelegate(const TestRequestDelegate&) = delete;
- TestRequestDelegate& operator=(const TestRequestDelegate&) = delete;
- ~TestRequestDelegate() override = default;
- // SpdySessionPool::SpdySessionRequest::Delegate implementation:
- void OnSpdySessionAvailable(
- base::WeakPtr<SpdySession> spdy_session) override {}
- };
- TEST_F(SpdySessionPoolTest, RequestSessionWithNoSessions) {
- const SpdySessionKey kSessionKey(
- HostPortPair("foo.test", 443), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED, SpdySessionKey::IsProxySession::kFalse,
- SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- CreateNetworkSession();
- // First request. Its request deleted callback should never be invoked.
- TestOnRequestDeletedCallback request_deleted_callback1;
- TestRequestDelegate request_delegate1;
- std::unique_ptr<SpdySessionPool::SpdySessionRequest> spdy_session_request1;
- bool is_first_request_for_session;
- EXPECT_FALSE(spdy_session_pool_->RequestSession(
- kSessionKey, /* enable_ip_based_pooling = */ false,
- /* is_websocket = */ false, NetLogWithSource(),
- request_deleted_callback1.Callback(), &request_delegate1,
- &spdy_session_request1, &is_first_request_for_session));
- EXPECT_TRUE(is_first_request_for_session);
- // Second request.
- TestOnRequestDeletedCallback request_deleted_callback2;
- TestRequestDelegate request_delegate2;
- std::unique_ptr<SpdySessionPool::SpdySessionRequest> spdy_session_request2;
- EXPECT_FALSE(spdy_session_pool_->RequestSession(
- kSessionKey, /* enable_ip_based_pooling = */ false,
- /* is_websocket = */ false, NetLogWithSource(),
- request_deleted_callback2.Callback(), &request_delegate2,
- &spdy_session_request2, &is_first_request_for_session));
- EXPECT_FALSE(is_first_request_for_session);
- // Third request.
- TestOnRequestDeletedCallback request_deleted_callback3;
- TestRequestDelegate request_delegate3;
- std::unique_ptr<SpdySessionPool::SpdySessionRequest> spdy_session_request3;
- EXPECT_FALSE(spdy_session_pool_->RequestSession(
- kSessionKey, /* enable_ip_based_pooling = */ false,
- /* is_websocket = */ false, NetLogWithSource(),
- request_deleted_callback3.Callback(), &request_delegate3,
- &spdy_session_request3, &is_first_request_for_session));
- EXPECT_FALSE(is_first_request_for_session);
- // Destroying the second request shouldn't cause anything to happen.
- spdy_session_request2.reset();
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(request_deleted_callback1.invoked());
- EXPECT_FALSE(request_deleted_callback2.invoked());
- EXPECT_FALSE(request_deleted_callback3.invoked());
- // But destroying the first request should cause the second and third
- // callbacks to be invoked.
- spdy_session_request1.reset();
- request_deleted_callback2.WaitUntilInvoked();
- request_deleted_callback3.WaitUntilInvoked();
- EXPECT_FALSE(request_deleted_callback1.invoked());
- // Nothing should happen when the third request is destroyed.
- spdy_session_request3.reset();
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(request_deleted_callback1.invoked());
- }
- TEST_F(SpdySessionPoolTest, RequestSessionDuringNotification) {
- const SpdySessionKey kSessionKey(
- HostPortPair("foo.test", 443), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED, SpdySessionKey::IsProxySession::kFalse,
- SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- CreateNetworkSession();
- // First request. Its request deleted callback should never be invoked.
- TestOnRequestDeletedCallback request_deleted_callback1;
- TestRequestDelegate request_delegate1;
- std::unique_ptr<SpdySessionPool::SpdySessionRequest> spdy_session_request1;
- bool is_first_request_for_session;
- EXPECT_FALSE(spdy_session_pool_->RequestSession(
- kSessionKey, /* enable_ip_based_pooling = */ false,
- /* is_websocket = */ false, NetLogWithSource(),
- request_deleted_callback1.Callback(), &request_delegate1,
- &spdy_session_request1, &is_first_request_for_session));
- EXPECT_TRUE(is_first_request_for_session);
- // Second request.
- TestOnRequestDeletedCallback request_deleted_callback2;
- TestRequestDelegate request_delegate2;
- std::unique_ptr<SpdySessionPool::SpdySessionRequest> spdy_session_request2;
- EXPECT_FALSE(spdy_session_pool_->RequestSession(
- kSessionKey, /* enable_ip_based_pooling = */ false,
- /* is_websocket = */ false, NetLogWithSource(),
- request_deleted_callback2.Callback(), &request_delegate2,
- &spdy_session_request2, &is_first_request_for_session));
- EXPECT_FALSE(is_first_request_for_session);
- TestOnRequestDeletedCallback request_deleted_callback3;
- TestRequestDelegate request_delegate3;
- std::unique_ptr<SpdySessionPool::SpdySessionRequest> spdy_session_request3;
- TestOnRequestDeletedCallback request_deleted_callback4;
- TestRequestDelegate request_delegate4;
- std::unique_ptr<SpdySessionPool::SpdySessionRequest> spdy_session_request4;
- request_deleted_callback2.SetRequestDeletedCallback(
- base::BindLambdaForTesting([&]() {
- // Third request. It should again be marked as the first request for the
- // session, since it's only created after the original two have been
- // removed.
- bool is_first_request_for_session;
- EXPECT_FALSE(spdy_session_pool_->RequestSession(
- kSessionKey, /* enable_ip_based_pooling = */ false,
- /* is_websocket = */ false, NetLogWithSource(),
- request_deleted_callback3.Callback(), &request_delegate3,
- &spdy_session_request3, &is_first_request_for_session));
- EXPECT_TRUE(is_first_request_for_session);
- // Fourth request.
- EXPECT_FALSE(spdy_session_pool_->RequestSession(
- kSessionKey, /* enable_ip_based_pooling = */ false,
- /* is_websocket = */ false, NetLogWithSource(),
- request_deleted_callback4.Callback(), &request_delegate4,
- &spdy_session_request4, &is_first_request_for_session));
- EXPECT_FALSE(is_first_request_for_session);
- }));
- // Destroying the first request should cause the second callback to be
- // invoked, and the third and fourth request to be made.
- spdy_session_request1.reset();
- request_deleted_callback2.WaitUntilInvoked();
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(request_deleted_callback1.invoked());
- EXPECT_FALSE(request_deleted_callback3.invoked());
- EXPECT_FALSE(request_deleted_callback4.invoked());
- EXPECT_TRUE(spdy_session_request3);
- EXPECT_TRUE(spdy_session_request4);
- // Destroying the third request should cause the fourth callback to be
- // invoked.
- spdy_session_request3.reset();
- request_deleted_callback4.WaitUntilInvoked();
- EXPECT_FALSE(request_deleted_callback1.invoked());
- EXPECT_FALSE(request_deleted_callback3.invoked());
- }
- static const char kSSLServerTestHost[] = "config-changed.test";
- static const struct {
- const char* url;
- const char* proxy_pac_string;
- bool expect_invalidated;
- } kSSLServerTests[] = {
- // If the host and port match, the session should be invalidated.
- {"https://config-changed.test", "DIRECT", true},
- // If host and port do not match, the session should not be invalidated.
- {"https://mail.config-changed.test", "DIRECT", false},
- {"https://config-changed.test:444", "DIRECT", false},
- // If the proxy matches, the session should be invalidated independent of
- // the host.
- {"https://config-changed.test", "HTTPS config-changed.test:443", true},
- {"https://mail.config-changed.test", "HTTPS config-changed.test:443", true},
- // HTTP and SOCKS proxies do not have client certificates.
- {"https://mail.config-changed.test", "PROXY config-changed.test:443",
- false},
- {"https://mail.config-changed.test", "SOCKS5 config-changed.test:443",
- false},
- // The proxy host and port must match.
- {"https://mail.config-changed.test", "HTTPS mail.config-changed.test:443",
- false},
- {"https://mail.config-changed.test", "HTTPS config-changed.test:444",
- false},
- };
- // Tests the OnSSLConfigForServerChanged() method matches SpdySessions as
- // expected.
- TEST_F(SpdySessionPoolTest, SSLConfigForServerChanged) {
- const MockConnect connect_data(SYNCHRONOUS, OK);
- const MockRead reads[] = {
- MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever.
- };
- std::vector<std::unique_ptr<StaticSocketDataProvider>> socket_data;
- size_t num_tests = std::size(kSSLServerTests);
- for (size_t i = 0; i < num_tests; i++) {
- socket_data.push_back(std::make_unique<StaticSocketDataProvider>(
- reads, base::span<MockWrite>()));
- socket_data.back()->set_connect_data(connect_data);
- session_deps_.socket_factory->AddSocketDataProvider(
- socket_data.back().get());
- AddSSLSocketData();
- }
- CreateNetworkSession();
- std::vector<base::WeakPtr<SpdySession>> sessions;
- for (size_t i = 0; i < num_tests; i++) {
- SpdySessionKey key(
- HostPortPair::FromURL(GURL(kSSLServerTests[i].url)),
- PacResultElementToProxyServer(kSSLServerTests[i].proxy_pac_string),
- PRIVACY_MODE_DISABLED, SpdySessionKey::IsProxySession::kFalse,
- SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- sessions.push_back(
- CreateSpdySession(http_session_.get(), key, NetLogWithSource()));
- }
- // All sessions are available.
- for (size_t i = 0; i < num_tests; i++) {
- SCOPED_TRACE(i);
- EXPECT_TRUE(sessions[i]->IsAvailable());
- }
- spdy_session_pool_->OnSSLConfigForServerChanged(
- HostPortPair(kSSLServerTestHost, 443));
- base::RunLoop().RunUntilIdle();
- // Sessions were inactive, so the unavailable sessions are closed.
- for (size_t i = 0; i < num_tests; i++) {
- SCOPED_TRACE(i);
- if (kSSLServerTests[i].expect_invalidated) {
- EXPECT_FALSE(sessions[i]);
- } else {
- ASSERT_TRUE(sessions[i]);
- EXPECT_TRUE(sessions[i]->IsAvailable());
- }
- }
- }
- // Tests the OnSSLConfigForServerChanged() method when there are streams open.
- TEST_F(SpdySessionPoolTest, SSLConfigForServerChangedWithStreams) {
- // Set up a SpdySession with an active, created, and pending stream.
- SpdyTestUtil spdy_util;
- spdy::SettingsMap settings;
- settings[spdy::SETTINGS_MAX_CONCURRENT_STREAMS] = 2;
- spdy::SpdySerializedFrame settings_frame =
- spdy_util.ConstructSpdySettings(settings);
- spdy::SpdySerializedFrame settings_ack = spdy_util.ConstructSpdySettingsAck();
- spdy::SpdySerializedFrame req(
- spdy_util.ConstructSpdyGet(nullptr, 0, 1, MEDIUM));
- const MockConnect connect_data(SYNCHRONOUS, OK);
- const MockRead reads[] = {
- CreateMockRead(settings_frame),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever.
- };
- const MockWrite writes[] = {
- CreateMockWrite(settings_ack),
- CreateMockWrite(req),
- };
- StaticSocketDataProvider socket_data(reads, writes);
- socket_data.set_connect_data(connect_data);
- session_deps_.socket_factory->AddSocketDataProvider(&socket_data);
- AddSSLSocketData();
- CreateNetworkSession();
- const GURL url(kDefaultUrl);
- SpdySessionKey key(HostPortPair::FromURL(url), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> session =
- CreateSpdySession(http_session_.get(), key, NetLogWithSource());
- // Pick up the SETTINGS frame to update SETTINGS_MAX_CONCURRENT_STREAMS.
- base::RunLoop().RunUntilIdle();
- EXPECT_EQ(2u, max_concurrent_streams(session));
- // The first two stream requests should succeed.
- base::WeakPtr<SpdyStream> active_stream = CreateStreamSynchronously(
- SPDY_REQUEST_RESPONSE_STREAM, session, url, MEDIUM, NetLogWithSource());
- test::StreamDelegateDoNothing active_stream_delegate(active_stream);
- active_stream->SetDelegate(&active_stream_delegate);
- base::WeakPtr<SpdyStream> created_stream = CreateStreamSynchronously(
- SPDY_REQUEST_RESPONSE_STREAM, session, url, MEDIUM, NetLogWithSource());
- test::StreamDelegateDoNothing created_stream_delegate(created_stream);
- created_stream->SetDelegate(&created_stream_delegate);
- // The third will block.
- TestCompletionCallback callback;
- SpdyStreamRequest stream_request;
- EXPECT_THAT(
- stream_request.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session, url,
- /*can_send_early=*/false, MEDIUM, SocketTag(),
- NetLogWithSource(), callback.callback(),
- TRAFFIC_ANNOTATION_FOR_TESTS),
- IsError(ERR_IO_PENDING));
- // Activate the first stream by sending data.
- spdy::Http2HeaderBlock headers(spdy_util.ConstructGetHeaderBlock(url.spec()));
- active_stream->SendRequestHeaders(std::move(headers), NO_MORE_DATA_TO_SEND);
- base::RunLoop().RunUntilIdle();
- // The active stream should now have a stream ID.
- EXPECT_EQ(1u, active_stream->stream_id());
- EXPECT_EQ(spdy::kInvalidStreamId, created_stream->stream_id());
- EXPECT_TRUE(session->is_active());
- EXPECT_TRUE(session->IsAvailable());
- spdy_session_pool_->OnSSLConfigForServerChanged(HostPortPair::FromURL(url));
- base::RunLoop().RunUntilIdle();
- // The active stream is still alive, so the session is still active.
- ASSERT_TRUE(session);
- EXPECT_TRUE(session->is_active());
- ASSERT_TRUE(active_stream);
- // The session is no longer available.
- EXPECT_FALSE(session->IsAvailable());
- EXPECT_TRUE(session->IsGoingAway());
- // The pending and created stream are cancelled.
- // TODO(https://crbug.com/1213609): Ideally, this would be recoverable.
- EXPECT_THAT(callback.WaitForResult(), IsError(ERR_NETWORK_CHANGED));
- EXPECT_THAT(created_stream_delegate.WaitForClose(),
- IsError(ERR_NETWORK_CHANGED));
- // Close the active stream.
- active_stream->Close();
- // TODO(https://crbug.com/982499): The invalidated session should be closed
- // after a RunUntilIdle(), but it is not.
- }
- // Tests the OnSSLConfigForServerChanged() method when there only pending
- // streams active.
- TEST_F(SpdySessionPoolTest, SSLConfigForServerChangedWithOnlyPendingStreams) {
- // Set up a SpdySession that accepts no streams.
- SpdyTestUtil spdy_util;
- spdy::SettingsMap settings;
- settings[spdy::SETTINGS_MAX_CONCURRENT_STREAMS] = 0;
- spdy::SpdySerializedFrame settings_frame =
- spdy_util.ConstructSpdySettings(settings);
- spdy::SpdySerializedFrame settings_ack = spdy_util.ConstructSpdySettingsAck();
- const MockConnect connect_data(SYNCHRONOUS, OK);
- const MockRead reads[] = {
- CreateMockRead(settings_frame),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever.
- };
- const MockWrite writes[] = {
- CreateMockWrite(settings_ack),
- };
- StaticSocketDataProvider socket_data(reads, writes);
- socket_data.set_connect_data(connect_data);
- session_deps_.socket_factory->AddSocketDataProvider(&socket_data);
- AddSSLSocketData();
- CreateNetworkSession();
- const GURL url(kDefaultUrl);
- SpdySessionKey key(HostPortPair::FromURL(url), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> session =
- CreateSpdySession(http_session_.get(), key, NetLogWithSource());
- // Pick up the SETTINGS frame to update SETTINGS_MAX_CONCURRENT_STREAMS.
- base::RunLoop().RunUntilIdle();
- EXPECT_EQ(0u, max_concurrent_streams(session));
- // Create a stream. It should block on the stream limit.
- TestCompletionCallback callback;
- SpdyStreamRequest stream_request;
- ASSERT_THAT(
- stream_request.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session, url,
- /*can_send_early=*/false, MEDIUM, SocketTag(),
- NetLogWithSource(), callback.callback(),
- TRAFFIC_ANNOTATION_FOR_TESTS),
- IsError(ERR_IO_PENDING));
- spdy_session_pool_->OnSSLConfigForServerChanged(HostPortPair::FromURL(url));
- base::RunLoop().RunUntilIdle();
- // The pending stream is cancelled.
- // TODO(https://crbug.com/1213609): Ideally, this would be recoverable.
- EXPECT_THAT(callback.WaitForResult(), IsError(ERR_NETWORK_CHANGED));
- EXPECT_FALSE(session);
- }
- } // namespace net
|