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