1 // Copyright 2019 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/socks_connect_job.h"
6
7 #include "base/containers/flat_set.h"
8 #include "base/containers/span.h"
9 #include "base/functional/callback.h"
10 #include "base/run_loop.h"
11 #include "base/test/task_environment.h"
12 #include "base/time/time.h"
13 #include "build/build_config.h"
14 #include "net/base/load_states.h"
15 #include "net/base/load_timing_info.h"
16 #include "net/base/load_timing_info_test_util.h"
17 #include "net/base/net_errors.h"
18 #include "net/base/network_isolation_key.h"
19 #include "net/dns/mock_host_resolver.h"
20 #include "net/dns/public/secure_dns_policy.h"
21 #include "net/log/net_log.h"
22 #include "net/socket/client_socket_factory.h"
23 #include "net/socket/client_socket_handle.h"
24 #include "net/socket/connect_job_test_util.h"
25 #include "net/socket/socket_tag.h"
26 #include "net/socket/socket_test_util.h"
27 #include "net/socket/socks_connect_job.h"
28 #include "net/socket/transport_client_socket_pool_test_util.h"
29 #include "net/socket/transport_connect_job.h"
30 #include "net/test/gtest_util.h"
31 #include "net/test/test_with_task_environment.h"
32 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
33 #include "net/url_request/static_http_user_agent_settings.h"
34 #include "testing/gmock/include/gmock/gmock.h"
35 #include "testing/gtest/include/gtest/gtest.h"
36
37 namespace net {
38 namespace {
39
40 const char kProxyHostName[] = "proxy.test";
41 const int kProxyPort = 4321;
42
43 constexpr base::TimeDelta kTinyTime = base::Microseconds(1);
44
45 class SOCKSConnectJobTest : public testing::Test, public WithTaskEnvironment {
46 public:
47 enum class SOCKSVersion {
48 V4,
49 V5,
50 };
51
SOCKSConnectJobTest()52 SOCKSConnectJobTest()
53 : WithTaskEnvironment(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
54 common_connect_job_params_(
55 &client_socket_factory_,
56 &host_resolver_,
57 /*http_auth_cache=*/nullptr,
58 /*http_auth_handler_factory=*/nullptr,
59 /*spdy_session_pool=*/nullptr,
60 /*quic_supported_versions=*/nullptr,
61 /*quic_session_pool=*/nullptr,
62 /*proxy_delegate=*/nullptr,
63 &http_user_agent_settings_,
64 /*ssl_client_context=*/nullptr,
65 /*socket_performance_watcher_factory=*/nullptr,
66 /*network_quality_estimator=*/nullptr,
67 NetLog::Get(),
68 /*websocket_endpoint_lock_manager=*/nullptr,
69 /*http_server_properties=*/nullptr,
70 /*alpn_protos=*/nullptr,
71 /*application_settings=*/nullptr,
72 /*ignore_certificate_errors=*/nullptr,
73 /*early_data_enabled=*/nullptr) {}
74
75 ~SOCKSConnectJobTest() override = default;
76
CreateSOCKSParams(SOCKSVersion socks_version,SecureDnsPolicy secure_dns_policy=SecureDnsPolicy::kAllow)77 static scoped_refptr<SOCKSSocketParams> CreateSOCKSParams(
78 SOCKSVersion socks_version,
79 SecureDnsPolicy secure_dns_policy = SecureDnsPolicy::kAllow) {
80 return base::MakeRefCounted<SOCKSSocketParams>(
81 ConnectJobParams(base::MakeRefCounted<TransportSocketParams>(
82 HostPortPair(kProxyHostName, kProxyPort), NetworkAnonymizationKey(),
83 secure_dns_policy, OnHostResolutionCallback(),
84 /*supported_alpns=*/base::flat_set<std::string>())),
85 socks_version == SOCKSVersion::V5,
86 socks_version == SOCKSVersion::V4
87 ? HostPortPair(kSOCKS4TestHost, kSOCKS4TestPort)
88 : HostPortPair(kSOCKS5TestHost, kSOCKS5TestPort),
89 NetworkAnonymizationKey(), TRAFFIC_ANNOTATION_FOR_TESTS);
90 }
91
92 protected:
93 MockHostResolver host_resolver_{/*default_result=*/MockHostResolverBase::
94 RuleResolver::GetLocalhostResult()};
95 MockTaggingClientSocketFactory client_socket_factory_;
96 const StaticHttpUserAgentSettings http_user_agent_settings_ = {"*",
97 "test-ua"};
98 const CommonConnectJobParams common_connect_job_params_;
99 };
100
TEST_F(SOCKSConnectJobTest,HostResolutionFailure)101 TEST_F(SOCKSConnectJobTest, HostResolutionFailure) {
102 host_resolver_.rules()->AddSimulatedTimeoutFailure(kProxyHostName);
103
104 for (bool failure_synchronous : {false, true}) {
105 host_resolver_.set_synchronous_mode(failure_synchronous);
106 TestConnectJobDelegate test_delegate;
107 SOCKSConnectJob socks_connect_job(DEFAULT_PRIORITY, SocketTag(),
108 &common_connect_job_params_,
109 CreateSOCKSParams(SOCKSVersion::V5),
110 &test_delegate, nullptr /* net_log */);
111 test_delegate.StartJobExpectingResult(
112 &socks_connect_job, ERR_PROXY_CONNECTION_FAILED, failure_synchronous);
113 EXPECT_THAT(socks_connect_job.GetResolveErrorInfo().error,
114 test::IsError(ERR_DNS_TIMED_OUT));
115 }
116 }
117
TEST_F(SOCKSConnectJobTest,HostResolutionFailureSOCKS4Endpoint)118 TEST_F(SOCKSConnectJobTest, HostResolutionFailureSOCKS4Endpoint) {
119 const char hostname[] = "google.com";
120 host_resolver_.rules()->AddSimulatedTimeoutFailure(hostname);
121
122 for (bool failure_synchronous : {false, true}) {
123 host_resolver_.set_synchronous_mode(failure_synchronous);
124
125 SequencedSocketData sequenced_socket_data{base::span<MockRead>(),
126 base::span<MockWrite>()};
127 sequenced_socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
128 client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
129
130 scoped_refptr<SOCKSSocketParams> socket_params =
131 base::MakeRefCounted<SOCKSSocketParams>(
132 ConnectJobParams(base::MakeRefCounted<TransportSocketParams>(
133 HostPortPair(kProxyHostName, kProxyPort),
134 NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
135 OnHostResolutionCallback(),
136 /*supported_alpns=*/base::flat_set<std::string>())),
137 false /* socks_v5 */, HostPortPair(hostname, kSOCKS4TestPort),
138 NetworkAnonymizationKey(), TRAFFIC_ANNOTATION_FOR_TESTS);
139
140 TestConnectJobDelegate test_delegate;
141 SOCKSConnectJob socks_connect_job(
142 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
143 socket_params, &test_delegate, nullptr /* net_log */);
144 test_delegate.StartJobExpectingResult(
145 &socks_connect_job, ERR_NAME_NOT_RESOLVED, failure_synchronous);
146 EXPECT_THAT(socks_connect_job.GetResolveErrorInfo().error,
147 test::IsError(ERR_DNS_TIMED_OUT));
148 }
149 }
150
TEST_F(SOCKSConnectJobTest,HandshakeError)151 TEST_F(SOCKSConnectJobTest, HandshakeError) {
152 for (bool host_resolution_synchronous : {false, true}) {
153 for (bool write_failure_synchronous : {false, true}) {
154 host_resolver_.set_synchronous_mode(host_resolution_synchronous);
155
156 // No need to distinguish which part of the handshake fails. Those details
157 // are all handled at the StreamSocket layer, not the SOCKSConnectJob.
158 MockWrite writes[] = {
159 MockWrite(write_failure_synchronous ? SYNCHRONOUS : ASYNC,
160 ERR_UNEXPECTED, 0),
161 };
162 SequencedSocketData sequenced_socket_data(base::span<MockRead>(), writes);
163 // Host resolution is used to switch between sync and async connection
164 // behavior. The SOCKS layer can't distinguish between sync and async host
165 // resolution vs sync and async connection establishment, so just always
166 // make connection establishment synchroonous.
167 sequenced_socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
168 client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
169
170 TestConnectJobDelegate test_delegate;
171 SOCKSConnectJob socks_connect_job(DEFAULT_PRIORITY, SocketTag(),
172 &common_connect_job_params_,
173 CreateSOCKSParams(SOCKSVersion::V5),
174 &test_delegate, nullptr /* net_log */);
175 test_delegate.StartJobExpectingResult(
176 &socks_connect_job, ERR_UNEXPECTED,
177 host_resolution_synchronous && write_failure_synchronous);
178 }
179 }
180 }
181
TEST_F(SOCKSConnectJobTest,SOCKS4)182 TEST_F(SOCKSConnectJobTest, SOCKS4) {
183 for (bool host_resolution_synchronous : {false, true}) {
184 for (bool read_and_writes_synchronous : {true}) {
185 host_resolver_.set_synchronous_mode(host_resolution_synchronous);
186
187 MockWrite writes[] = {
188 MockWrite(SYNCHRONOUS, kSOCKS4OkRequestLocalHostPort80,
189 kSOCKS4OkRequestLocalHostPort80Length, 0),
190 };
191
192 MockRead reads[] = {
193 MockRead(SYNCHRONOUS, kSOCKS4OkReply, kSOCKS4OkReplyLength, 1),
194 };
195
196 SequencedSocketData sequenced_socket_data(reads, writes);
197 // Host resolution is used to switch between sync and async connection
198 // behavior. The SOCKS layer can't distinguish between sync and async host
199 // resolution vs sync and async connection establishment, so just always
200 // make connection establishment synchroonous.
201 sequenced_socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
202 client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
203
204 TestConnectJobDelegate test_delegate;
205 SOCKSConnectJob socks_connect_job(DEFAULT_PRIORITY, SocketTag(),
206 &common_connect_job_params_,
207 CreateSOCKSParams(SOCKSVersion::V4),
208 &test_delegate, nullptr /* net_log */);
209 test_delegate.StartJobExpectingResult(
210 &socks_connect_job, OK,
211 host_resolution_synchronous && read_and_writes_synchronous);
212
213 // Proxies should not set any DNS aliases.
214 EXPECT_TRUE(test_delegate.socket()->GetDnsAliases().empty());
215 }
216 }
217 }
218
TEST_F(SOCKSConnectJobTest,SOCKS5)219 TEST_F(SOCKSConnectJobTest, SOCKS5) {
220 for (bool host_resolution_synchronous : {false, true}) {
221 for (bool read_and_writes_synchronous : {true}) {
222 host_resolver_.set_synchronous_mode(host_resolution_synchronous);
223
224 MockWrite writes[] = {
225 MockWrite(SYNCHRONOUS, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength,
226 0),
227 MockWrite(SYNCHRONOUS, kSOCKS5OkRequest, kSOCKS5OkRequestLength, 2),
228 };
229
230 MockRead reads[] = {
231 MockRead(SYNCHRONOUS, kSOCKS5GreetResponse,
232 kSOCKS5GreetResponseLength, 1),
233 MockRead(SYNCHRONOUS, kSOCKS5OkResponse, kSOCKS5OkResponseLength, 3),
234 };
235
236 SequencedSocketData sequenced_socket_data(reads, writes);
237 // Host resolution is used to switch between sync and async connection
238 // behavior. The SOCKS layer can't distinguish between sync and async host
239 // resolution vs sync and async connection establishment, so just always
240 // make connection establishment synchroonous.
241 sequenced_socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
242 client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
243
244 TestConnectJobDelegate test_delegate;
245 SOCKSConnectJob socks_connect_job(DEFAULT_PRIORITY, SocketTag(),
246 &common_connect_job_params_,
247 CreateSOCKSParams(SOCKSVersion::V5),
248 &test_delegate, nullptr /* net_log */);
249 test_delegate.StartJobExpectingResult(
250 &socks_connect_job, OK,
251 host_resolution_synchronous && read_and_writes_synchronous);
252
253 // Proxies should not set any DNS aliases.
254 EXPECT_TRUE(test_delegate.socket()->GetDnsAliases().empty());
255 }
256 }
257 }
258
TEST_F(SOCKSConnectJobTest,HasEstablishedConnection)259 TEST_F(SOCKSConnectJobTest, HasEstablishedConnection) {
260 host_resolver_.set_ondemand_mode(true);
261 MockWrite writes[] = {
262 MockWrite(ASYNC, kSOCKS4OkRequestLocalHostPort80,
263 kSOCKS4OkRequestLocalHostPort80Length, 0),
264 };
265
266 MockRead reads[] = {
267 MockRead(ASYNC, ERR_IO_PENDING, 1),
268 MockRead(ASYNC, kSOCKS4OkReply, kSOCKS4OkReplyLength, 2),
269 };
270
271 SequencedSocketData sequenced_socket_data(reads, writes);
272 sequenced_socket_data.set_connect_data(MockConnect(ASYNC, OK));
273 client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
274
275 TestConnectJobDelegate test_delegate;
276 SOCKSConnectJob socks_connect_job(DEFAULT_PRIORITY, SocketTag(),
277 &common_connect_job_params_,
278 CreateSOCKSParams(SOCKSVersion::V4),
279 &test_delegate, nullptr /* net_log */);
280 socks_connect_job.Connect();
281 EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, socks_connect_job.GetLoadState());
282 EXPECT_FALSE(socks_connect_job.HasEstablishedConnection());
283
284 host_resolver_.ResolveNow(1);
285 EXPECT_EQ(LOAD_STATE_CONNECTING, socks_connect_job.GetLoadState());
286 EXPECT_FALSE(socks_connect_job.HasEstablishedConnection());
287
288 sequenced_socket_data.RunUntilPaused();
289 // "LOAD_STATE_CONNECTING" is also returned when negotiating a SOCKS
290 // connection.
291 EXPECT_EQ(LOAD_STATE_CONNECTING, socks_connect_job.GetLoadState());
292 EXPECT_TRUE(socks_connect_job.HasEstablishedConnection());
293 EXPECT_FALSE(test_delegate.has_result());
294
295 sequenced_socket_data.Resume();
296 EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
297 EXPECT_TRUE(test_delegate.has_result());
298 }
299
300 // Check that TransportConnectJob's timeout is respected for the nested
301 // TransportConnectJob.
TEST_F(SOCKSConnectJobTest,TimeoutDuringDnsResolution)302 TEST_F(SOCKSConnectJobTest, TimeoutDuringDnsResolution) {
303 // Set HostResolver to hang.
304 host_resolver_.set_ondemand_mode(true);
305
306 TestConnectJobDelegate test_delegate;
307 SOCKSConnectJob socks_connect_job(DEFAULT_PRIORITY, SocketTag(),
308 &common_connect_job_params_,
309 CreateSOCKSParams(SOCKSVersion::V5),
310 &test_delegate, nullptr /* net_log */);
311 socks_connect_job.Connect();
312
313 // Just before the TransportConnectJob's timeout, nothing should have
314 // happened.
315 FastForwardBy(TransportConnectJob::ConnectionTimeout() - kTinyTime);
316 EXPECT_TRUE(host_resolver_.has_pending_requests());
317 EXPECT_FALSE(test_delegate.has_result());
318
319 // Wait for exactly the TransportConnectJob's timeout to have passed. The Job
320 // should time out.
321 FastForwardBy(kTinyTime);
322 EXPECT_TRUE(test_delegate.has_result());
323 EXPECT_THAT(test_delegate.WaitForResult(),
324 test::IsError(ERR_PROXY_CONNECTION_FAILED));
325 }
326
327 // Check that SOCKSConnectJob's timeout is respected for the handshake phase.
TEST_F(SOCKSConnectJobTest,TimeoutDuringHandshake)328 TEST_F(SOCKSConnectJobTest, TimeoutDuringHandshake) {
329 host_resolver_.set_ondemand_mode(true);
330
331 MockWrite writes[] = {
332 MockWrite(SYNCHRONOUS, ERR_IO_PENDING, 0),
333 };
334
335 SequencedSocketData sequenced_socket_data(base::span<MockRead>(), writes);
336 sequenced_socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
337 client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
338
339 TestConnectJobDelegate test_delegate;
340 SOCKSConnectJob socks_connect_job(DEFAULT_PRIORITY, SocketTag(),
341 &common_connect_job_params_,
342 CreateSOCKSParams(SOCKSVersion::V5),
343 &test_delegate, nullptr /* net_log */);
344 socks_connect_job.Connect();
345
346 // Just before the TransportConnectJob's timeout, nothing should have
347 // happened.
348 FastForwardBy(TransportConnectJob::ConnectionTimeout() - kTinyTime);
349 EXPECT_FALSE(test_delegate.has_result());
350 EXPECT_TRUE(host_resolver_.has_pending_requests());
351
352 // DNS resolution completes, and the socket connects. The request should not
353 // time out, even after the TransportConnectJob's timeout passes. The
354 // SOCKSConnectJob's handshake timer should also be started.
355 host_resolver_.ResolveAllPending();
356
357 // Waiting until just before the SOCKS handshake times out. There should cause
358 // no observable change in the SOCKSConnectJob's status.
359 FastForwardBy(SOCKSConnectJob::HandshakeTimeoutForTesting() - kTinyTime);
360 EXPECT_FALSE(test_delegate.has_result());
361
362 // Wait for exactly the SOCKSConnectJob's handshake timeout has fully elapsed.
363 // The Job should time out.
364 FastForwardBy(kTinyTime);
365 EXPECT_FALSE(host_resolver_.has_pending_requests());
366 EXPECT_TRUE(test_delegate.has_result());
367 EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
368 }
369
370 // Check initial priority is passed to the HostResolver, and priority can be
371 // modified.
TEST_F(SOCKSConnectJobTest,Priority)372 TEST_F(SOCKSConnectJobTest, Priority) {
373 host_resolver_.set_ondemand_mode(true);
374 for (int initial_priority = MINIMUM_PRIORITY;
375 initial_priority <= MAXIMUM_PRIORITY; ++initial_priority) {
376 for (int new_priority = MINIMUM_PRIORITY; new_priority <= MAXIMUM_PRIORITY;
377 ++new_priority) {
378 // Don't try changing priority to itself, as APIs may not allow that.
379 if (new_priority == initial_priority) {
380 continue;
381 }
382 TestConnectJobDelegate test_delegate;
383 SOCKSConnectJob socks_connect_job(
384 static_cast<RequestPriority>(initial_priority), SocketTag(),
385 &common_connect_job_params_, CreateSOCKSParams(SOCKSVersion::V4),
386 &test_delegate, nullptr /* net_log */);
387 ASSERT_THAT(socks_connect_job.Connect(), test::IsError(ERR_IO_PENDING));
388 ASSERT_TRUE(host_resolver_.has_pending_requests());
389 int request_id = host_resolver_.num_resolve();
390 EXPECT_EQ(initial_priority, host_resolver_.request_priority(request_id));
391
392 // Change priority.
393 socks_connect_job.ChangePriority(
394 static_cast<RequestPriority>(new_priority));
395 EXPECT_EQ(new_priority, host_resolver_.request_priority(request_id));
396
397 // Restore initial priority.
398 socks_connect_job.ChangePriority(
399 static_cast<RequestPriority>(initial_priority));
400 EXPECT_EQ(initial_priority, host_resolver_.request_priority(request_id));
401 }
402 }
403 }
404
TEST_F(SOCKSConnectJobTest,SecureDnsPolicy)405 TEST_F(SOCKSConnectJobTest, SecureDnsPolicy) {
406 for (auto secure_dns_policy :
407 {SecureDnsPolicy::kAllow, SecureDnsPolicy::kDisable}) {
408 TestConnectJobDelegate test_delegate;
409 SOCKSConnectJob socks_connect_job(
410 DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_,
411 CreateSOCKSParams(SOCKSVersion::V4, secure_dns_policy), &test_delegate,
412 nullptr /* net_log */);
413 ASSERT_THAT(socks_connect_job.Connect(), test::IsError(ERR_IO_PENDING));
414 EXPECT_EQ(secure_dns_policy, host_resolver_.last_secure_dns_policy());
415 }
416 }
417
TEST_F(SOCKSConnectJobTest,ConnectTiming)418 TEST_F(SOCKSConnectJobTest, ConnectTiming) {
419 host_resolver_.set_ondemand_mode(true);
420
421 MockWrite writes[] = {
422 MockWrite(ASYNC, ERR_IO_PENDING, 0),
423 MockWrite(ASYNC, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength, 1),
424 MockWrite(SYNCHRONOUS, kSOCKS5OkRequest, kSOCKS5OkRequestLength, 3),
425 };
426
427 MockRead reads[] = {
428 MockRead(SYNCHRONOUS, kSOCKS5GreetResponse, kSOCKS5GreetResponseLength,
429 2),
430 MockRead(SYNCHRONOUS, kSOCKS5OkResponse, kSOCKS5OkResponseLength, 4),
431 };
432
433 SequencedSocketData sequenced_socket_data(reads, writes);
434 // Host resolution is used to switch between sync and async connection
435 // behavior. The SOCKS layer can't distinguish between sync and async host
436 // resolution vs sync and async connection establishment, so just always
437 // make connection establishment synchroonous.
438 sequenced_socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
439 client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
440
441 TestConnectJobDelegate test_delegate;
442 SOCKSConnectJob socks_connect_job(DEFAULT_PRIORITY, SocketTag(),
443 &common_connect_job_params_,
444 CreateSOCKSParams(SOCKSVersion::V5),
445 &test_delegate, nullptr /* net_log */);
446 base::TimeTicks start = base::TimeTicks::Now();
447 socks_connect_job.Connect();
448
449 // DNS resolution completes after a short delay. The connection should be
450 // immediately established as well. The first write to the socket stalls.
451 FastForwardBy(kTinyTime);
452 host_resolver_.ResolveAllPending();
453 RunUntilIdle();
454
455 // After another short delay, data is received from the server.
456 FastForwardBy(kTinyTime);
457 sequenced_socket_data.Resume();
458
459 EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
460 // Proxy name resolution is not considered resolving the host name for
461 // ConnectionInfo. For SOCKS4, where the host name is also looked up via DNS,
462 // the resolution time is not currently reported.
463 EXPECT_EQ(base::TimeTicks(),
464 socks_connect_job.connect_timing().domain_lookup_start);
465 EXPECT_EQ(base::TimeTicks(),
466 socks_connect_job.connect_timing().domain_lookup_end);
467
468 // The "connect" time for socks proxies includes DNS resolution time.
469 EXPECT_EQ(start, socks_connect_job.connect_timing().connect_start);
470 EXPECT_EQ(start + 2 * kTinyTime,
471 socks_connect_job.connect_timing().connect_end);
472
473 // Since SSL was not negotiated, SSL times are null.
474 EXPECT_EQ(base::TimeTicks(), socks_connect_job.connect_timing().ssl_start);
475 EXPECT_EQ(base::TimeTicks(), socks_connect_job.connect_timing().ssl_end);
476 }
477
TEST_F(SOCKSConnectJobTest,CancelDuringDnsResolution)478 TEST_F(SOCKSConnectJobTest, CancelDuringDnsResolution) {
479 // Set HostResolver to hang.
480 host_resolver_.set_ondemand_mode(true);
481
482 TestConnectJobDelegate test_delegate;
483 std::unique_ptr<SOCKSConnectJob> socks_connect_job =
484 std::make_unique<SOCKSConnectJob>(DEFAULT_PRIORITY, SocketTag(),
485 &common_connect_job_params_,
486 CreateSOCKSParams(SOCKSVersion::V5),
487 &test_delegate, nullptr /* net_log */);
488 socks_connect_job->Connect();
489
490 EXPECT_TRUE(host_resolver_.has_pending_requests());
491
492 socks_connect_job.reset();
493 RunUntilIdle();
494 EXPECT_FALSE(host_resolver_.has_pending_requests());
495 EXPECT_FALSE(test_delegate.has_result());
496 }
497
TEST_F(SOCKSConnectJobTest,CancelDuringConnect)498 TEST_F(SOCKSConnectJobTest, CancelDuringConnect) {
499 host_resolver_.set_synchronous_mode(true);
500
501 SequencedSocketData sequenced_socket_data{base::span<MockRead>(),
502 base::span<MockWrite>()};
503 sequenced_socket_data.set_connect_data(MockConnect(ASYNC, OK));
504 client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
505
506 TestConnectJobDelegate test_delegate;
507 std::unique_ptr<SOCKSConnectJob> socks_connect_job =
508 std::make_unique<SOCKSConnectJob>(DEFAULT_PRIORITY, SocketTag(),
509 &common_connect_job_params_,
510 CreateSOCKSParams(SOCKSVersion::V5),
511 &test_delegate, nullptr /* net_log */);
512 socks_connect_job->Connect();
513 // Host resolution should resolve immediately. The ConnectJob should currently
514 // be trying to connect.
515 EXPECT_FALSE(host_resolver_.has_pending_requests());
516
517 socks_connect_job.reset();
518 RunUntilIdle();
519 EXPECT_FALSE(test_delegate.has_result());
520 // Socket should have been destroyed.
521 EXPECT_FALSE(sequenced_socket_data.socket());
522 }
523
TEST_F(SOCKSConnectJobTest,CancelDuringHandshake)524 TEST_F(SOCKSConnectJobTest, CancelDuringHandshake) {
525 host_resolver_.set_synchronous_mode(true);
526
527 // Hang at start of handshake.
528 MockWrite writes[] = {
529 MockWrite(SYNCHRONOUS, ERR_IO_PENDING, 0),
530 };
531 SequencedSocketData sequenced_socket_data(base::span<MockRead>(), writes);
532 sequenced_socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
533 client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
534
535 TestConnectJobDelegate test_delegate;
536 std::unique_ptr<SOCKSConnectJob> socks_connect_job =
537 std::make_unique<SOCKSConnectJob>(DEFAULT_PRIORITY, SocketTag(),
538 &common_connect_job_params_,
539 CreateSOCKSParams(SOCKSVersion::V5),
540 &test_delegate, nullptr /* net_log */);
541 socks_connect_job->Connect();
542 // Host resolution should resolve immediately. The socket connecting, and the
543 // ConnectJob should currently be trying to send the SOCKS handshake.
544 EXPECT_FALSE(host_resolver_.has_pending_requests());
545
546 socks_connect_job.reset();
547 RunUntilIdle();
548 EXPECT_FALSE(test_delegate.has_result());
549 // Socket should have been destroyed.
550 EXPECT_FALSE(sequenced_socket_data.socket());
551 EXPECT_TRUE(sequenced_socket_data.AllWriteDataConsumed());
552 }
553
554 } // namespace
555 } // namespace net
556