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