1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/socket/connect_job.h"
6
7 #include "base/functional/bind.h"
8 #include "base/functional/callback.h"
9 #include "base/run_loop.h"
10 #include "base/test/task_environment.h"
11 #include "net/base/address_list.h"
12 #include "net/base/net_errors.h"
13 #include "net/base/request_priority.h"
14 #include "net/dns/public/resolve_error_info.h"
15 #include "net/log/test_net_log.h"
16 #include "net/log/test_net_log_util.h"
17 #include "net/socket/connect_job_test_util.h"
18 #include "net/socket/socket_tag.h"
19 #include "net/socket/socket_test_util.h"
20 #include "net/test/gtest_util.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22
23 namespace net {
24 namespace {
25
26 class TestConnectJob : public ConnectJob {
27 public:
28 enum class JobType {
29 kSyncSuccess,
30 kAsyncSuccess,
31 kHung,
32 };
33
TestConnectJob(JobType job_type,base::TimeDelta timeout_duration,const CommonConnectJobParams * common_connect_job_params,ConnectJob::Delegate * delegate)34 TestConnectJob(JobType job_type,
35 base::TimeDelta timeout_duration,
36 const CommonConnectJobParams* common_connect_job_params,
37 ConnectJob::Delegate* delegate)
38 : ConnectJob(DEFAULT_PRIORITY,
39 SocketTag(),
40 timeout_duration,
41 common_connect_job_params,
42 delegate,
43 nullptr /* net_log */,
44 NetLogSourceType::TRANSPORT_CONNECT_JOB,
45 NetLogEventType::TRANSPORT_CONNECT_JOB_CONNECT),
46 job_type_(job_type) {
47 switch (job_type_) {
48 case JobType::kSyncSuccess:
49 socket_data_provider_.set_connect_data(MockConnect(SYNCHRONOUS, OK));
50 return;
51 case JobType::kAsyncSuccess:
52 socket_data_provider_.set_connect_data(MockConnect(ASYNC, OK));
53 return;
54 case JobType::kHung:
55 socket_data_provider_.set_connect_data(
56 MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
57 return;
58 }
59 }
60
61 TestConnectJob(const TestConnectJob&) = delete;
62 TestConnectJob& operator=(const TestConnectJob&) = delete;
63
64 // From ConnectJob:
GetLoadState() const65 LoadState GetLoadState() const override { return LOAD_STATE_IDLE; }
HasEstablishedConnection() const66 bool HasEstablishedConnection() const override { return false; }
GetResolveErrorInfo() const67 ResolveErrorInfo GetResolveErrorInfo() const override {
68 return ResolveErrorInfo(net::OK);
69 }
ConnectInternal()70 int ConnectInternal() override {
71 SetSocket(std::make_unique<MockTCPClientSocket>(
72 AddressList(), net_log().net_log(), &socket_data_provider_),
73 std::nullopt /* dns_aliases */);
74 return socket()->Connect(base::BindOnce(
75 &TestConnectJob::NotifyDelegateOfCompletion, base::Unretained(this)));
76 }
ChangePriorityInternal(RequestPriority priority)77 void ChangePriorityInternal(RequestPriority priority) override {
78 last_seen_priority_ = priority;
79 }
80
81 using ConnectJob::ResetTimer;
82
83 // The priority seen during the most recent call to ChangePriorityInternal().
last_seen_priority() const84 RequestPriority last_seen_priority() const { return last_seen_priority_; }
85
86 protected:
87 const JobType job_type_;
88 StaticSocketDataProvider socket_data_provider_;
89 RequestPriority last_seen_priority_ = DEFAULT_PRIORITY;
90 };
91
92 class ConnectJobTest : public testing::Test {
93 public:
ConnectJobTest()94 ConnectJobTest()
95 : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
96 common_connect_job_params_(
97 /*client_socket_factory=*/nullptr,
98 /*host_resolver=*/nullptr,
99 /*http_auth_cache=*/nullptr,
100 /*http_auth_handler_factory=*/nullptr,
101 /*spdy_session_pool=*/nullptr,
102 /*quic_supported_versions=*/nullptr,
103 /*quic_session_pool=*/nullptr,
104 /*proxy_delegate=*/nullptr,
105 /*http_user_agent_settings=*/nullptr,
106 /*ssl_client_context=*/nullptr,
107 /*socket_performance_watcher_factory=*/nullptr,
108 /*network_quality_estimator=*/nullptr,
109 NetLog::Get(),
110 /*websocket_endpoint_lock_manager=*/nullptr,
111 /*http_server_properties*/ nullptr,
112 /*alpn_protos=*/nullptr,
113 /*application_settings=*/nullptr,
114 /*ignore_certificate_errors=*/nullptr,
115 /*early_data_enabled=*/nullptr) {}
116 ~ConnectJobTest() override = default;
117
118 protected:
119 base::test::TaskEnvironment task_environment_;
120 RecordingNetLogObserver net_log_observer_;
121 const CommonConnectJobParams common_connect_job_params_;
122 TestConnectJobDelegate delegate_;
123 };
124
125 // Even though a timeout is specified, it doesn't time out on a synchronous
126 // completion.
TEST_F(ConnectJobTest,NoTimeoutOnSyncCompletion)127 TEST_F(ConnectJobTest, NoTimeoutOnSyncCompletion) {
128 TestConnectJob job(TestConnectJob::JobType::kSyncSuccess,
129 base::Microseconds(1), &common_connect_job_params_,
130 &delegate_);
131 EXPECT_THAT(job.Connect(), test::IsOk());
132 }
133
134 // Even though a timeout is specified, it doesn't time out on an asynchronous
135 // completion.
TEST_F(ConnectJobTest,NoTimeoutOnAsyncCompletion)136 TEST_F(ConnectJobTest, NoTimeoutOnAsyncCompletion) {
137 TestConnectJob job(TestConnectJob::JobType::kAsyncSuccess, base::Minutes(1),
138 &common_connect_job_params_, &delegate_);
139 ASSERT_THAT(job.Connect(), test::IsError(ERR_IO_PENDING));
140 EXPECT_THAT(delegate_.WaitForResult(), test::IsOk());
141 }
142
143 // Job shouldn't timeout when passed a TimeDelta of zero.
TEST_F(ConnectJobTest,NoTimeoutWithNoTimeDelta)144 TEST_F(ConnectJobTest, NoTimeoutWithNoTimeDelta) {
145 TestConnectJob job(TestConnectJob::JobType::kHung, base::TimeDelta(),
146 &common_connect_job_params_, &delegate_);
147 ASSERT_THAT(job.Connect(), test::IsError(ERR_IO_PENDING));
148 task_environment_.RunUntilIdle();
149 EXPECT_FALSE(delegate_.has_result());
150 }
151
152 // Make sure that ChangePriority() works, and new priority is visible to
153 // subclasses during the SetPriorityInternal call.
TEST_F(ConnectJobTest,SetPriority)154 TEST_F(ConnectJobTest, SetPriority) {
155 TestConnectJob job(TestConnectJob::JobType::kAsyncSuccess,
156 base::Microseconds(1), &common_connect_job_params_,
157 &delegate_);
158 ASSERT_THAT(job.Connect(), test::IsError(ERR_IO_PENDING));
159
160 job.ChangePriority(HIGHEST);
161 EXPECT_EQ(HIGHEST, job.priority());
162 EXPECT_EQ(HIGHEST, job.last_seen_priority());
163
164 job.ChangePriority(MEDIUM);
165 EXPECT_EQ(MEDIUM, job.priority());
166 EXPECT_EQ(MEDIUM, job.last_seen_priority());
167
168 EXPECT_THAT(delegate_.WaitForResult(), test::IsOk());
169 }
170
TEST_F(ConnectJobTest,TimedOut)171 TEST_F(ConnectJobTest, TimedOut) {
172 const base::TimeDelta kTimeout = base::Hours(1);
173
174 std::unique_ptr<TestConnectJob> job =
175 std::make_unique<TestConnectJob>(TestConnectJob::JobType::kHung, kTimeout,
176 &common_connect_job_params_, &delegate_);
177 ASSERT_THAT(job->Connect(), test::IsError(ERR_IO_PENDING));
178
179 // Nothing should happen before the specified time.
180 task_environment_.FastForwardBy(kTimeout - base::Milliseconds(1));
181 base::RunLoop().RunUntilIdle();
182 EXPECT_FALSE(delegate_.has_result());
183
184 // At which point the job should time out.
185 task_environment_.FastForwardBy(base::Milliseconds(1));
186 EXPECT_THAT(delegate_.WaitForResult(), test::IsError(ERR_TIMED_OUT));
187
188 // Have to delete the job for it to log the end event.
189 job.reset();
190
191 auto entries = net_log_observer_.GetEntries();
192
193 EXPECT_EQ(6u, entries.size());
194 EXPECT_TRUE(LogContainsBeginEvent(entries, 0, NetLogEventType::CONNECT_JOB));
195 EXPECT_TRUE(LogContainsBeginEvent(
196 entries, 1, NetLogEventType::TRANSPORT_CONNECT_JOB_CONNECT));
197 EXPECT_TRUE(LogContainsEvent(entries, 2,
198 NetLogEventType::CONNECT_JOB_SET_SOCKET,
199 NetLogEventPhase::NONE));
200 EXPECT_TRUE(LogContainsEvent(entries, 3,
201 NetLogEventType::CONNECT_JOB_TIMED_OUT,
202 NetLogEventPhase::NONE));
203 EXPECT_TRUE(LogContainsEndEvent(
204 entries, 4, NetLogEventType::TRANSPORT_CONNECT_JOB_CONNECT));
205 EXPECT_TRUE(LogContainsEndEvent(entries, 5, NetLogEventType::CONNECT_JOB));
206 }
207
TEST_F(ConnectJobTest,TimedOutWithRestartedTimer)208 TEST_F(ConnectJobTest, TimedOutWithRestartedTimer) {
209 const base::TimeDelta kTimeout = base::Hours(1);
210
211 TestConnectJob job(TestConnectJob::JobType::kHung, kTimeout,
212 &common_connect_job_params_, &delegate_);
213 ASSERT_THAT(job.Connect(), test::IsError(ERR_IO_PENDING));
214
215 // Nothing should happen before the specified time.
216 task_environment_.FastForwardBy(kTimeout - base::Milliseconds(1));
217 base::RunLoop().RunUntilIdle();
218 EXPECT_FALSE(delegate_.has_result());
219
220 // Make sure restarting the timer is respected.
221 job.ResetTimer(kTimeout);
222 task_environment_.FastForwardBy(kTimeout - base::Milliseconds(1));
223 base::RunLoop().RunUntilIdle();
224 EXPECT_FALSE(delegate_.has_result());
225
226 task_environment_.FastForwardBy(base::Milliseconds(1));
227 EXPECT_THAT(delegate_.WaitForResult(), test::IsError(ERR_TIMED_OUT));
228 }
229
230 } // namespace
231 } // namespace net
232