http_proxy_connect_job_unittest.cc 71 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "net/http/http_proxy_connect_job.h"
  5. #include <algorithm>
  6. #include <map>
  7. #include <string>
  8. #include <utility>
  9. #include "base/metrics/field_trial.h"
  10. #include "base/metrics/field_trial_param_associator.h"
  11. #include "base/metrics/field_trial_params.h"
  12. #include "base/strings/string_number_conversions.h"
  13. #include "base/strings/string_util.h"
  14. #include "base/strings/stringprintf.h"
  15. #include "base/strings/utf_string_conversions.h"
  16. #include "base/test/metrics/histogram_tester.h"
  17. #include "base/test/task_environment.h"
  18. #include "base/time/time.h"
  19. #include "build/build_config.h"
  20. #include "net/base/host_port_pair.h"
  21. #include "net/base/network_isolation_key.h"
  22. #include "net/base/proxy_string_util.h"
  23. #include "net/base/test_proxy_delegate.h"
  24. #include "net/dns/mock_host_resolver.h"
  25. #include "net/dns/public/secure_dns_policy.h"
  26. #include "net/http/http_network_session.h"
  27. #include "net/http/http_response_headers.h"
  28. #include "net/nqe/network_quality_estimator_test_util.h"
  29. #include "net/socket/client_socket_handle.h"
  30. #include "net/socket/connect_job_test_util.h"
  31. #include "net/socket/socket_test_util.h"
  32. #include "net/socket/socks_connect_job.h"
  33. #include "net/socket/ssl_client_socket.h"
  34. #include "net/socket/ssl_connect_job.h"
  35. #include "net/socket/transport_connect_job.h"
  36. #include "net/spdy/spdy_test_util_common.h"
  37. #include "net/test/cert_test_util.h"
  38. #include "net/test/gtest_util.h"
  39. #include "net/test/test_data_directory.h"
  40. #include "net/test/test_with_task_environment.h"
  41. #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
  42. #include "testing/gtest/include/gtest/gtest.h"
  43. #include "url/gurl.h"
  44. #include "url/scheme_host_port.h"
  45. namespace net {
  46. namespace {
  47. const char kEndpointHost[] = "www.endpoint.test";
  48. enum HttpProxyType { HTTP, HTTPS, SPDY };
  49. const char kHttpProxyHost[] = "httpproxy.example.test";
  50. const char kHttpsProxyHost[] = "httpsproxy.example.test";
  51. } // namespace
  52. class HttpProxyConnectJobTest : public ::testing::TestWithParam<HttpProxyType>,
  53. public WithTaskEnvironment {
  54. protected:
  55. HttpProxyConnectJobTest()
  56. : WithTaskEnvironment(
  57. base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
  58. // Used a mock HostResolver that does not have a cache.
  59. session_deps_.host_resolver = std::make_unique<MockHostResolver>(
  60. /*default_result=*/MockHostResolverBase::RuleResolver::
  61. GetLocalhostResult());
  62. network_quality_estimator_ =
  63. std::make_unique<TestNetworkQualityEstimator>();
  64. session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
  65. InitCommonConnectJobParams();
  66. }
  67. ~HttpProxyConnectJobTest() override {
  68. // Reset global field trial parameters to defaults values.
  69. base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
  70. HttpProxyConnectJob::UpdateFieldTrialParametersForTesting();
  71. }
  72. // Initializes the field trial parameters for the field trial that determines
  73. // connection timeout based on the network quality.
  74. void InitAdaptiveTimeoutFieldTrialWithParams(
  75. bool use_default_params,
  76. int ssl_http_rtt_multiplier,
  77. int non_ssl_http_rtt_multiplier,
  78. base::TimeDelta min_proxy_connection_timeout,
  79. base::TimeDelta max_proxy_connection_timeout) {
  80. std::string trial_name = "NetAdaptiveProxyConnectionTimeout";
  81. std::string group_name = "GroupName";
  82. std::map<std::string, std::string> params;
  83. if (!use_default_params) {
  84. params["ssl_http_rtt_multiplier"] =
  85. base::NumberToString(ssl_http_rtt_multiplier);
  86. params["non_ssl_http_rtt_multiplier"] =
  87. base::NumberToString(non_ssl_http_rtt_multiplier);
  88. params["min_proxy_connection_timeout_seconds"] =
  89. base::NumberToString(min_proxy_connection_timeout.InSeconds());
  90. params["max_proxy_connection_timeout_seconds"] =
  91. base::NumberToString(max_proxy_connection_timeout.InSeconds());
  92. }
  93. base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
  94. EXPECT_TRUE(
  95. base::AssociateFieldTrialParams(trial_name, group_name, params));
  96. EXPECT_TRUE(base::FieldTrialList::CreateFieldTrial(trial_name, group_name));
  97. // Force static global that reads the field trials to update.
  98. HttpProxyConnectJob::UpdateFieldTrialParametersForTesting();
  99. }
  100. scoped_refptr<TransportSocketParams> CreateHttpProxyParams(
  101. SecureDnsPolicy secure_dns_policy) const {
  102. if (GetParam() != HTTP)
  103. return nullptr;
  104. return base::MakeRefCounted<TransportSocketParams>(
  105. HostPortPair(kHttpProxyHost, 80), NetworkIsolationKey(),
  106. secure_dns_policy, OnHostResolutionCallback(),
  107. /*supported_alpns=*/base::flat_set<std::string>());
  108. }
  109. scoped_refptr<SSLSocketParams> CreateHttpsProxyParams(
  110. SecureDnsPolicy secure_dns_policy) const {
  111. if (GetParam() == HTTP)
  112. return nullptr;
  113. return base::MakeRefCounted<SSLSocketParams>(
  114. base::MakeRefCounted<TransportSocketParams>(
  115. HostPortPair(kHttpsProxyHost, 443), NetworkIsolationKey(),
  116. secure_dns_policy, OnHostResolutionCallback(),
  117. /*supported_alpns=*/base::flat_set<std::string>()),
  118. nullptr, nullptr, HostPortPair(kHttpsProxyHost, 443), SSLConfig(),
  119. PRIVACY_MODE_DISABLED, NetworkIsolationKey());
  120. }
  121. // Returns a correctly constructed HttpProxyParams for the HTTP or HTTPS
  122. // proxy.
  123. scoped_refptr<HttpProxySocketParams> CreateParams(
  124. bool tunnel,
  125. SecureDnsPolicy secure_dns_policy) {
  126. return base::MakeRefCounted<HttpProxySocketParams>(
  127. CreateHttpProxyParams(secure_dns_policy),
  128. CreateHttpsProxyParams(secure_dns_policy), false /* is_quic */,
  129. HostPortPair(kEndpointHost, tunnel ? 443 : 80), tunnel,
  130. TRAFFIC_ANNOTATION_FOR_TESTS, NetworkIsolationKey());
  131. }
  132. std::unique_ptr<HttpProxyConnectJob> CreateConnectJobForHttpRequest(
  133. ConnectJob::Delegate* delegate,
  134. RequestPriority priority = DEFAULT_PRIORITY,
  135. SecureDnsPolicy secure_dns_policy = SecureDnsPolicy::kAllow) {
  136. return CreateConnectJob(CreateParams(false /* tunnel */, secure_dns_policy),
  137. delegate, priority);
  138. }
  139. std::unique_ptr<HttpProxyConnectJob> CreateConnectJobForTunnel(
  140. ConnectJob::Delegate* delegate,
  141. RequestPriority priority = DEFAULT_PRIORITY,
  142. SecureDnsPolicy secure_dns_policy = SecureDnsPolicy::kAllow) {
  143. return CreateConnectJob(CreateParams(true /* tunnel */, secure_dns_policy),
  144. delegate, priority);
  145. }
  146. std::unique_ptr<HttpProxyConnectJob> CreateConnectJob(
  147. scoped_refptr<HttpProxySocketParams> http_proxy_socket_params,
  148. ConnectJob::Delegate* delegate,
  149. RequestPriority priority) {
  150. return std::make_unique<HttpProxyConnectJob>(
  151. priority, SocketTag(), common_connect_job_params_.get(),
  152. std::move(http_proxy_socket_params), delegate, nullptr /* net_log */);
  153. }
  154. // This may only be called at the start of the test, before any ConnectJobs
  155. // have been created.
  156. void InitProxyDelegate() {
  157. proxy_delegate_ = std::make_unique<TestProxyDelegate>();
  158. InitCommonConnectJobParams();
  159. }
  160. // This may only be called at the start of the test, before any ConnectJobs
  161. // have been created.
  162. void InitCommonConnectJobParams() {
  163. common_connect_job_params_ = std::make_unique<CommonConnectJobParams>(
  164. session_->CreateCommonConnectJobParams());
  165. // TODO(mmenke): Consider reworking this so it can be done through
  166. // |session_deps_|.
  167. common_connect_job_params_->proxy_delegate = proxy_delegate_.get();
  168. common_connect_job_params_->network_quality_estimator =
  169. network_quality_estimator_.get();
  170. }
  171. void Initialize(base::span<const MockRead> reads,
  172. base::span<const MockWrite> writes,
  173. base::span<const MockRead> spdy_reads,
  174. base::span<const MockWrite> spdy_writes,
  175. IoMode connect_and_ssl_io_mode) {
  176. if (GetParam() == SPDY) {
  177. data_ = std::make_unique<SequencedSocketData>(spdy_reads, spdy_writes);
  178. } else {
  179. data_ = std::make_unique<SequencedSocketData>(reads, writes);
  180. }
  181. data_->set_connect_data(MockConnect(connect_and_ssl_io_mode, OK));
  182. session_deps_.socket_factory->AddSocketDataProvider(data_.get());
  183. if (GetParam() != HTTP) {
  184. // Keep the old ssl_data in case there is a draining socket.
  185. old_ssl_data_.swap(ssl_data_);
  186. ssl_data_ =
  187. std::make_unique<SSLSocketDataProvider>(connect_and_ssl_io_mode, OK);
  188. if (GetParam() == SPDY) {
  189. InitializeSpdySsl(ssl_data_.get());
  190. }
  191. session_deps_.socket_factory->AddSSLSocketDataProvider(ssl_data_.get());
  192. }
  193. }
  194. void InitializeSpdySsl(SSLSocketDataProvider* ssl_data) {
  195. ssl_data->next_proto = kProtoHTTP2;
  196. }
  197. // Return the timeout for establishing the lower layer connection. i.e., for
  198. // an HTTP proxy, the TCP connection timeout, and for an HTTPS proxy, the
  199. // TCP+SSL connection timeout. In many cases, this will return the return
  200. // value of the "AlternateNestedConnectionTimeout()".
  201. base::TimeDelta GetNestedConnectionTimeout() {
  202. base::TimeDelta normal_nested_connection_timeout =
  203. TransportConnectJob::ConnectionTimeout();
  204. if (GetParam() != HTTP)
  205. normal_nested_connection_timeout +=
  206. SSLConnectJob::HandshakeTimeoutForTesting();
  207. // Doesn't actually matter whether or not this is for a tunnel - the
  208. // connection timeout is the same, though it probably shouldn't be the same,
  209. // since tunnels need an extra round trip.
  210. base::TimeDelta alternate_connection_timeout =
  211. HttpProxyConnectJob::AlternateNestedConnectionTimeout(
  212. *CreateParams(true /* tunnel */, SecureDnsPolicy::kAllow),
  213. network_quality_estimator_.get());
  214. // If there's an alternate connection timeout, and it's less than the
  215. // standard TCP+SSL timeout (Which is also applied by the nested connect
  216. // jobs), return the alternate connection timeout. Otherwise, return the
  217. // normal timeout.
  218. if (!alternate_connection_timeout.is_zero() &&
  219. alternate_connection_timeout < normal_nested_connection_timeout) {
  220. return alternate_connection_timeout;
  221. }
  222. return normal_nested_connection_timeout;
  223. }
  224. protected:
  225. std::unique_ptr<TestProxyDelegate> proxy_delegate_;
  226. std::unique_ptr<SSLSocketDataProvider> ssl_data_;
  227. std::unique_ptr<SSLSocketDataProvider> old_ssl_data_;
  228. std::unique_ptr<SequencedSocketData> data_;
  229. SpdySessionDependencies session_deps_;
  230. std::unique_ptr<TestNetworkQualityEstimator> network_quality_estimator_;
  231. std::unique_ptr<HttpNetworkSession> session_;
  232. SpdyTestUtil spdy_util_;
  233. TestCompletionCallback callback_;
  234. std::unique_ptr<CommonConnectJobParams> common_connect_job_params_;
  235. };
  236. // All tests are run with three different proxy types: HTTP, HTTPS (non-SPDY)
  237. // and SPDY.
  238. INSTANTIATE_TEST_SUITE_P(HttpProxyType,
  239. HttpProxyConnectJobTest,
  240. ::testing::Values(HTTP, HTTPS, SPDY));
  241. TEST_P(HttpProxyConnectJobTest, NoTunnel) {
  242. InitProxyDelegate();
  243. for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
  244. SCOPED_TRACE(io_mode);
  245. session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
  246. base::HistogramTester histogram_tester;
  247. Initialize(base::span<MockRead>(), base::span<MockWrite>(),
  248. base::span<MockRead>(), base::span<MockWrite>(), io_mode);
  249. TestConnectJobDelegate test_delegate;
  250. std::unique_ptr<ConnectJob> connect_job =
  251. CreateConnectJobForHttpRequest(&test_delegate);
  252. test_delegate.StartJobExpectingResult(connect_job.get(), OK,
  253. io_mode == SYNCHRONOUS);
  254. EXPECT_FALSE(proxy_delegate_->on_before_tunnel_request_called());
  255. // Proxies should not set any DNS aliases.
  256. EXPECT_TRUE(test_delegate.socket()->GetDnsAliases().empty());
  257. bool is_secure_proxy = GetParam() == HTTPS || GetParam() == SPDY;
  258. histogram_tester.ExpectTotalCount(
  259. "Net.HttpProxy.ConnectLatency.Insecure.Success",
  260. is_secure_proxy ? 0 : 1);
  261. histogram_tester.ExpectTotalCount(
  262. "Net.HttpProxy.ConnectLatency.Secure.Success", is_secure_proxy ? 1 : 0);
  263. }
  264. }
  265. // Pauses an HttpProxyConnectJob at various states, and check the value of
  266. // HasEstablishedConnection().
  267. TEST_P(HttpProxyConnectJobTest, HasEstablishedConnectionNoTunnel) {
  268. session_deps_.host_resolver->set_ondemand_mode(true);
  269. SequencedSocketData data;
  270. data.set_connect_data(MockConnect(ASYNC, OK));
  271. session_deps_.socket_factory->AddSocketDataProvider(&data);
  272. // Set up SSL, if needed.
  273. SSLSocketDataProvider ssl_data(ASYNC, OK);
  274. switch (GetParam()) {
  275. case HTTP:
  276. // No SSL needed.
  277. break;
  278. case HTTPS:
  279. // SSL negotiation is the last step in non-tunnel connections over HTTPS
  280. // proxies, so pause there, to check the final state before completion.
  281. ssl_data = SSLSocketDataProvider(SYNCHRONOUS, ERR_IO_PENDING);
  282. session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
  283. break;
  284. case SPDY:
  285. InitializeSpdySsl(&ssl_data);
  286. session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
  287. break;
  288. }
  289. TestConnectJobDelegate test_delegate;
  290. std::unique_ptr<ConnectJob> connect_job =
  291. CreateConnectJobForHttpRequest(&test_delegate);
  292. // Connecting should run until the request hits the HostResolver.
  293. EXPECT_THAT(connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  294. EXPECT_FALSE(test_delegate.has_result());
  295. EXPECT_TRUE(session_deps_.host_resolver->has_pending_requests());
  296. EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, connect_job->GetLoadState());
  297. EXPECT_FALSE(connect_job->HasEstablishedConnection());
  298. // Once the HostResolver completes, the job should start establishing a
  299. // connection, which will complete asynchronously.
  300. session_deps_.host_resolver->ResolveOnlyRequestNow();
  301. EXPECT_FALSE(test_delegate.has_result());
  302. EXPECT_EQ(LOAD_STATE_CONNECTING, connect_job->GetLoadState());
  303. EXPECT_FALSE(connect_job->HasEstablishedConnection());
  304. switch (GetParam()) {
  305. case HTTP:
  306. case SPDY:
  307. // Connection completes. Since no tunnel is established, the socket is
  308. // returned immediately, and HasEstablishedConnection() is only specified
  309. // to work before the ConnectJob completes.
  310. EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
  311. break;
  312. case HTTPS:
  313. base::RunLoop().RunUntilIdle();
  314. EXPECT_FALSE(test_delegate.has_result());
  315. EXPECT_EQ(LOAD_STATE_SSL_HANDSHAKE, connect_job->GetLoadState());
  316. EXPECT_TRUE(connect_job->HasEstablishedConnection());
  317. // Unfortunately, there's no API to advance the paused SSL negotiation,
  318. // so just end the test here.
  319. }
  320. }
  321. // Pauses an HttpProxyConnectJob at various states, and check the value of
  322. // HasEstablishedConnection().
  323. TEST_P(HttpProxyConnectJobTest, HasEstablishedConnectionTunnel) {
  324. session_deps_.host_resolver->set_ondemand_mode(true);
  325. // HTTP proxy CONNECT request / response, with a pause during the read.
  326. MockWrite http1_writes[] = {
  327. MockWrite(ASYNC, 0,
  328. "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
  329. "Host: www.endpoint.test:443\r\n"
  330. "Proxy-Connection: keep-alive\r\n\r\n"),
  331. };
  332. MockRead http1_reads[] = {
  333. // Pause at first read.
  334. MockRead(ASYNC, ERR_IO_PENDING, 1),
  335. MockRead(ASYNC, 2, "HTTP/1.1 200 Connection Established\r\n\r\n"),
  336. };
  337. SequencedSocketData http1_data(http1_reads, http1_writes);
  338. http1_data.set_connect_data(MockConnect(ASYNC, OK));
  339. // SPDY proxy CONNECT request / response, with a pause during the read.
  340. spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyConnect(
  341. nullptr, 0, 1, HttpProxyConnectJob::kH2QuicTunnelPriority,
  342. HostPortPair(kEndpointHost, 443)));
  343. MockWrite spdy_writes[] = {CreateMockWrite(req, 0)};
  344. spdy::SpdySerializedFrame resp(
  345. spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
  346. MockRead spdy_reads[] = {
  347. // Pause at first read.
  348. MockRead(ASYNC, ERR_IO_PENDING, 1),
  349. CreateMockRead(resp, 2, ASYNC),
  350. MockRead(ASYNC, 0, 3),
  351. };
  352. SequencedSocketData spdy_data(spdy_reads, spdy_writes);
  353. spdy_data.set_connect_data(MockConnect(ASYNC, OK));
  354. // Will point to either the HTTP/1.x or SPDY data, depending on GetParam().
  355. SequencedSocketData* sequenced_data = nullptr;
  356. SSLSocketDataProvider ssl_data(ASYNC, OK);
  357. ssl_data.ssl_info.cert =
  358. ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem");
  359. ASSERT_TRUE(ssl_data.ssl_info.cert);
  360. switch (GetParam()) {
  361. case HTTP:
  362. sequenced_data = &http1_data;
  363. break;
  364. case HTTPS:
  365. sequenced_data = &http1_data;
  366. ssl_data.next_proto = NextProto::kProtoHTTP11;
  367. session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
  368. break;
  369. case SPDY:
  370. sequenced_data = &spdy_data;
  371. InitializeSpdySsl(&ssl_data);
  372. session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
  373. break;
  374. }
  375. session_deps_.socket_factory->AddSocketDataProvider(sequenced_data);
  376. TestConnectJobDelegate test_delegate;
  377. std::unique_ptr<ConnectJob> connect_job =
  378. CreateConnectJobForTunnel(&test_delegate);
  379. // Connecting should run until the request hits the HostResolver.
  380. EXPECT_THAT(connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  381. EXPECT_FALSE(test_delegate.has_result());
  382. EXPECT_TRUE(session_deps_.host_resolver->has_pending_requests());
  383. EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, connect_job->GetLoadState());
  384. EXPECT_FALSE(connect_job->HasEstablishedConnection());
  385. // Once the HostResolver completes, the job should start establishing a
  386. // connection, which will complete asynchronously.
  387. session_deps_.host_resolver->ResolveOnlyRequestNow();
  388. EXPECT_FALSE(test_delegate.has_result());
  389. EXPECT_EQ(LOAD_STATE_CONNECTING, connect_job->GetLoadState());
  390. EXPECT_FALSE(connect_job->HasEstablishedConnection());
  391. // Run until the socket starts reading the proxy's handshake response.
  392. sequenced_data->RunUntilPaused();
  393. EXPECT_FALSE(test_delegate.has_result());
  394. EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL, connect_job->GetLoadState());
  395. EXPECT_TRUE(connect_job->HasEstablishedConnection());
  396. // Finish the read, and run the job until it's complete.
  397. sequenced_data->Resume();
  398. EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
  399. // Proxies should not set any DNS aliases.
  400. EXPECT_TRUE(test_delegate.socket()->GetDnsAliases().empty());
  401. // Although the underlying proxy connection may use TLS or negotiate ALPN, the
  402. // tunnel itself is a TCP connection to the origin and should not report these
  403. // values.
  404. SSLInfo ssl_info;
  405. EXPECT_FALSE(test_delegate.socket()->GetSSLInfo(&ssl_info));
  406. EXPECT_FALSE(test_delegate.socket()->WasAlpnNegotiated());
  407. EXPECT_EQ(test_delegate.socket()->GetNegotiatedProtocol(),
  408. NextProto::kProtoUnknown);
  409. }
  410. TEST_P(HttpProxyConnectJobTest, ProxyDelegateExtraHeaders) {
  411. InitProxyDelegate();
  412. ProxyServer proxy_server(
  413. GetParam() == HTTP ? ProxyServer::SCHEME_HTTP : ProxyServer::SCHEME_HTTPS,
  414. HostPortPair(GetParam() == HTTP ? kHttpProxyHost : kHttpsProxyHost,
  415. GetParam() == HTTP ? 80 : 443));
  416. std::string proxy_server_uri = ProxyServerToProxyUri(proxy_server);
  417. std::string http1_request =
  418. "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
  419. "Host: www.endpoint.test:443\r\n"
  420. "Proxy-Connection: keep-alive\r\n"
  421. "Foo: " +
  422. proxy_server_uri + "\r\n\r\n";
  423. MockWrite writes[] = {
  424. MockWrite(ASYNC, 0, http1_request.c_str()),
  425. };
  426. const char kResponseHeaderName[] = "foo";
  427. const char kResponseHeaderValue[] = "Response";
  428. std::string http1_response = base::StringPrintf(
  429. "HTTP/1.1 200 Connection Established\r\n"
  430. "%s: %s\r\n\r\n",
  431. kResponseHeaderName, kResponseHeaderValue);
  432. MockRead reads[] = {
  433. MockRead(ASYNC, 1, http1_response.c_str()),
  434. };
  435. const char* const kExtraRequestHeaders[] = {
  436. "foo",
  437. proxy_server_uri.c_str(),
  438. };
  439. const char* const kExtraResponseHeaders[] = {
  440. kResponseHeaderName,
  441. kResponseHeaderValue,
  442. };
  443. spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyConnect(
  444. kExtraRequestHeaders, std::size(kExtraRequestHeaders) / 2, 1,
  445. HttpProxyConnectJob::kH2QuicTunnelPriority,
  446. HostPortPair(kEndpointHost, 443)));
  447. MockWrite spdy_writes[] = {CreateMockWrite(req, 0)};
  448. spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(
  449. kExtraResponseHeaders, std::size(kExtraResponseHeaders) / 2, 1));
  450. MockRead spdy_reads[] = {
  451. CreateMockRead(resp, 1, ASYNC),
  452. MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2),
  453. };
  454. Initialize(reads, writes, spdy_reads, spdy_writes, ASYNC);
  455. TestConnectJobDelegate test_delegate;
  456. std::unique_ptr<ConnectJob> connect_job =
  457. CreateConnectJobForTunnel(&test_delegate);
  458. test_delegate.StartJobExpectingResult(connect_job.get(), OK,
  459. false /* expect_sync_result */);
  460. proxy_delegate_->VerifyOnTunnelHeadersReceived(
  461. proxy_server, kResponseHeaderName, kResponseHeaderValue);
  462. }
  463. // Test the case where auth credentials are not cached.
  464. TEST_P(HttpProxyConnectJobTest, NeedAuth) {
  465. for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
  466. SCOPED_TRACE(io_mode);
  467. session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
  468. MockWrite writes[] = {
  469. MockWrite(io_mode, 0,
  470. "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
  471. "Host: www.endpoint.test:443\r\n"
  472. "Proxy-Connection: keep-alive\r\n\r\n"),
  473. MockWrite(io_mode, 5,
  474. "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
  475. "Host: www.endpoint.test:443\r\n"
  476. "Proxy-Connection: keep-alive\r\n"
  477. "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
  478. };
  479. MockRead reads[] = {
  480. // No credentials.
  481. MockRead(io_mode, 1, "HTTP/1.1 407 Proxy Authentication Required\r\n"),
  482. MockRead(io_mode, 2,
  483. "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
  484. MockRead(io_mode, 3, "Content-Length: 10\r\n\r\n"),
  485. MockRead(io_mode, 4, "0123456789"),
  486. MockRead(io_mode, 6, "HTTP/1.1 200 Connection Established\r\n\r\n"),
  487. };
  488. SpdyTestUtil spdy_util;
  489. spdy::SpdySerializedFrame connect(spdy_util.ConstructSpdyConnect(
  490. nullptr, 0, 1, HttpProxyConnectJob::kH2QuicTunnelPriority,
  491. HostPortPair(kEndpointHost, 443)));
  492. spdy::SpdySerializedFrame rst(
  493. spdy_util.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL));
  494. spdy_util.UpdateWithStreamDestruction(1);
  495. // After calling trans.RestartWithAuth(), this is the request we should
  496. // be issuing -- the final header line contains the credentials.
  497. const char* const kSpdyAuthCredentials[] = {
  498. "proxy-authorization",
  499. "Basic Zm9vOmJhcg==",
  500. };
  501. spdy::SpdySerializedFrame connect2(spdy_util.ConstructSpdyConnect(
  502. kSpdyAuthCredentials, std::size(kSpdyAuthCredentials) / 2, 3,
  503. HttpProxyConnectJob::kH2QuicTunnelPriority,
  504. HostPortPair(kEndpointHost, 443)));
  505. MockWrite spdy_writes[] = {
  506. CreateMockWrite(connect, 0, io_mode),
  507. CreateMockWrite(rst, 2, io_mode),
  508. CreateMockWrite(connect2, 3, io_mode),
  509. };
  510. // The proxy responds to the connect with a 407, using a persistent
  511. // connection.
  512. const char kAuthStatus[] = "407";
  513. const char* const kAuthChallenge[] = {
  514. "proxy-authenticate",
  515. "Basic realm=\"MyRealm1\"",
  516. };
  517. spdy::SpdySerializedFrame connect_auth_resp(
  518. spdy_util.ConstructSpdyReplyError(kAuthStatus, kAuthChallenge,
  519. std::size(kAuthChallenge) / 2, 1));
  520. spdy::SpdySerializedFrame connect2_resp(
  521. spdy_util.ConstructSpdyGetReply(nullptr, 0, 3));
  522. MockRead spdy_reads[] = {
  523. CreateMockRead(connect_auth_resp, 1, ASYNC),
  524. CreateMockRead(connect2_resp, 4, ASYNC),
  525. MockRead(ASYNC, OK, 5),
  526. };
  527. Initialize(reads, writes, spdy_reads, spdy_writes, io_mode);
  528. TestConnectJobDelegate test_delegate;
  529. std::unique_ptr<ConnectJob> connect_job =
  530. CreateConnectJobForTunnel(&test_delegate);
  531. ASSERT_EQ(ERR_IO_PENDING, connect_job->Connect());
  532. // Auth callback is always invoked asynchronously when a challenge is
  533. // observed.
  534. EXPECT_EQ(0, test_delegate.num_auth_challenges());
  535. test_delegate.WaitForAuthChallenge(1);
  536. ASSERT_TRUE(test_delegate.auth_response_info().headers);
  537. EXPECT_EQ(407, test_delegate.auth_response_info().headers->response_code());
  538. std::string proxy_authenticate;
  539. ASSERT_TRUE(test_delegate.auth_response_info().headers->EnumerateHeader(
  540. nullptr, "Proxy-Authenticate", &proxy_authenticate));
  541. EXPECT_EQ(proxy_authenticate, "Basic realm=\"MyRealm1\"");
  542. ASSERT_TRUE(test_delegate.auth_controller());
  543. EXPECT_FALSE(test_delegate.has_result());
  544. test_delegate.auth_controller()->ResetAuth(AuthCredentials(u"foo", u"bar"));
  545. test_delegate.RunAuthCallback();
  546. // Per API contract, the request can not complete synchronously.
  547. EXPECT_FALSE(test_delegate.has_result());
  548. EXPECT_EQ(net::OK, test_delegate.WaitForResult());
  549. EXPECT_EQ(1, test_delegate.num_auth_challenges());
  550. // Close the H2 session to prevent reuse.
  551. if (GetParam() == SPDY)
  552. session_->CloseAllConnections(ERR_FAILED, "Very good reason");
  553. // Also need to clear the auth cache before re-running the test.
  554. session_->http_auth_cache()->ClearAllEntries();
  555. }
  556. }
  557. // Test the case where auth credentials are not cached and the first time
  558. // credentials are sent, they are rejected.
  559. TEST_P(HttpProxyConnectJobTest, NeedAuthTwice) {
  560. for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
  561. SCOPED_TRACE(io_mode);
  562. session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
  563. MockWrite writes[] = {
  564. MockWrite(io_mode, 0,
  565. "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
  566. "Host: www.endpoint.test:443\r\n"
  567. "Proxy-Connection: keep-alive\r\n\r\n"),
  568. MockWrite(io_mode, 2,
  569. "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
  570. "Host: www.endpoint.test:443\r\n"
  571. "Proxy-Connection: keep-alive\r\n"
  572. "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
  573. MockWrite(io_mode, 4,
  574. "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
  575. "Host: www.endpoint.test:443\r\n"
  576. "Proxy-Connection: keep-alive\r\n"
  577. "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
  578. };
  579. MockRead reads[] = {
  580. // No credentials.
  581. MockRead(io_mode, 1,
  582. "HTTP/1.1 407 Proxy Authentication Required\r\n"
  583. "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"
  584. "Content-Length: 0\r\n\r\n"),
  585. MockRead(io_mode, 3,
  586. "HTTP/1.1 407 Proxy Authentication Required\r\n"
  587. "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"
  588. "Content-Length: 0\r\n\r\n"),
  589. MockRead(io_mode, 5, "HTTP/1.1 200 Connection Established\r\n\r\n"),
  590. };
  591. SpdyTestUtil spdy_util;
  592. spdy::SpdySerializedFrame connect(spdy_util.ConstructSpdyConnect(
  593. nullptr, 0, 1, HttpProxyConnectJob::kH2QuicTunnelPriority,
  594. HostPortPair(kEndpointHost, 443)));
  595. spdy::SpdySerializedFrame rst(
  596. spdy_util.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL));
  597. spdy_util.UpdateWithStreamDestruction(1);
  598. // After calling trans.RestartWithAuth(), this is the request we should
  599. // be issuing -- the final header line contains the credentials.
  600. const char* const kSpdyAuthCredentials[] = {
  601. "proxy-authorization",
  602. "Basic Zm9vOmJhcg==",
  603. };
  604. spdy::SpdySerializedFrame connect2(spdy_util.ConstructSpdyConnect(
  605. kSpdyAuthCredentials, std::size(kSpdyAuthCredentials) / 2, 3,
  606. HttpProxyConnectJob::kH2QuicTunnelPriority,
  607. HostPortPair(kEndpointHost, 443)));
  608. spdy::SpdySerializedFrame rst2(
  609. spdy_util.ConstructSpdyRstStream(3, spdy::ERROR_CODE_CANCEL));
  610. spdy_util.UpdateWithStreamDestruction(3);
  611. spdy::SpdySerializedFrame connect3(spdy_util.ConstructSpdyConnect(
  612. kSpdyAuthCredentials, std::size(kSpdyAuthCredentials) / 2, 5,
  613. HttpProxyConnectJob::kH2QuicTunnelPriority,
  614. HostPortPair(kEndpointHost, 443)));
  615. MockWrite spdy_writes[] = {
  616. CreateMockWrite(connect, 0, io_mode),
  617. CreateMockWrite(rst, 2, io_mode),
  618. CreateMockWrite(connect2, 3, io_mode),
  619. CreateMockWrite(rst2, 5, io_mode),
  620. CreateMockWrite(connect3, 6, io_mode),
  621. };
  622. // The proxy responds to the connect with a 407, using a persistent
  623. // connection.
  624. const char kAuthStatus[] = "407";
  625. const char* const kAuthChallenge[] = {
  626. "proxy-authenticate",
  627. "Basic realm=\"MyRealm1\"",
  628. };
  629. spdy::SpdySerializedFrame connect_auth_resp(
  630. spdy_util.ConstructSpdyReplyError(kAuthStatus, kAuthChallenge,
  631. std::size(kAuthChallenge) / 2, 1));
  632. spdy::SpdySerializedFrame connect2_auth_resp(
  633. spdy_util.ConstructSpdyReplyError(kAuthStatus, kAuthChallenge,
  634. std::size(kAuthChallenge) / 2, 3));
  635. spdy::SpdySerializedFrame connect3_resp(
  636. spdy_util.ConstructSpdyGetReply(nullptr, 0, 5));
  637. MockRead spdy_reads[] = {
  638. CreateMockRead(connect_auth_resp, 1, ASYNC),
  639. CreateMockRead(connect2_auth_resp, 4, ASYNC),
  640. CreateMockRead(connect3_resp, 7, ASYNC),
  641. MockRead(ASYNC, OK, 8),
  642. };
  643. Initialize(reads, writes, spdy_reads, spdy_writes, io_mode);
  644. TestConnectJobDelegate test_delegate;
  645. std::unique_ptr<ConnectJob> connect_job =
  646. CreateConnectJobForTunnel(&test_delegate);
  647. ASSERT_EQ(ERR_IO_PENDING, connect_job->Connect());
  648. // Auth callback is always invoked asynchronously when a challenge is
  649. // observed.
  650. EXPECT_EQ(0, test_delegate.num_auth_challenges());
  651. test_delegate.WaitForAuthChallenge(1);
  652. ASSERT_TRUE(test_delegate.auth_response_info().headers);
  653. EXPECT_EQ(407, test_delegate.auth_response_info().headers->response_code());
  654. std::string proxy_authenticate;
  655. ASSERT_TRUE(test_delegate.auth_response_info().headers->EnumerateHeader(
  656. nullptr, "Proxy-Authenticate", &proxy_authenticate));
  657. EXPECT_EQ(proxy_authenticate, "Basic realm=\"MyRealm1\"");
  658. EXPECT_FALSE(test_delegate.has_result());
  659. test_delegate.auth_controller()->ResetAuth(AuthCredentials(u"foo", u"bar"));
  660. test_delegate.RunAuthCallback();
  661. // Per API contract, the auth callback can't be invoked synchronously.
  662. EXPECT_FALSE(test_delegate.auth_controller());
  663. EXPECT_FALSE(test_delegate.has_result());
  664. test_delegate.WaitForAuthChallenge(2);
  665. ASSERT_TRUE(test_delegate.auth_response_info().headers);
  666. EXPECT_EQ(407, test_delegate.auth_response_info().headers->response_code());
  667. ASSERT_TRUE(test_delegate.auth_response_info().headers->EnumerateHeader(
  668. nullptr, "Proxy-Authenticate", &proxy_authenticate));
  669. EXPECT_EQ(proxy_authenticate, "Basic realm=\"MyRealm1\"");
  670. EXPECT_FALSE(test_delegate.has_result());
  671. test_delegate.auth_controller()->ResetAuth(AuthCredentials(u"foo", u"bar"));
  672. test_delegate.RunAuthCallback();
  673. // Per API contract, the request can't complete synchronously.
  674. EXPECT_FALSE(test_delegate.has_result());
  675. EXPECT_EQ(net::OK, test_delegate.WaitForResult());
  676. EXPECT_EQ(2, test_delegate.num_auth_challenges());
  677. // Close the H2 session to prevent reuse.
  678. if (GetParam() == SPDY)
  679. session_->CloseAllConnections(ERR_FAILED, "Very good reason");
  680. // Also need to clear the auth cache before re-running the test.
  681. session_->http_auth_cache()->ClearAllEntries();
  682. }
  683. }
  684. // Test the case where auth credentials are cached.
  685. TEST_P(HttpProxyConnectJobTest, HaveAuth) {
  686. // Prepopulate auth cache.
  687. const std::u16string kFoo(u"foo");
  688. const std::u16string kBar(u"bar");
  689. url::SchemeHostPort proxy_scheme_host_port(
  690. GetParam() == HTTP ? GURL(std::string("http://") + kHttpProxyHost)
  691. : GURL(std::string("https://") + kHttpsProxyHost));
  692. session_->http_auth_cache()->Add(
  693. proxy_scheme_host_port, HttpAuth::AUTH_PROXY, "MyRealm1",
  694. HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
  695. "Basic realm=MyRealm1", AuthCredentials(kFoo, kBar), "/");
  696. for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
  697. SCOPED_TRACE(io_mode);
  698. session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
  699. MockWrite writes[] = {
  700. MockWrite(io_mode, 0,
  701. "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
  702. "Host: www.endpoint.test:443\r\n"
  703. "Proxy-Connection: keep-alive\r\n"
  704. "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
  705. };
  706. MockRead reads[] = {
  707. MockRead(io_mode, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"),
  708. };
  709. const char* const kSpdyAuthCredentials[] = {
  710. "proxy-authorization",
  711. "Basic Zm9vOmJhcg==",
  712. };
  713. SpdyTestUtil spdy_util;
  714. spdy::SpdySerializedFrame connect(spdy_util.ConstructSpdyConnect(
  715. kSpdyAuthCredentials, std::size(kSpdyAuthCredentials) / 2, 1,
  716. HttpProxyConnectJob::kH2QuicTunnelPriority,
  717. HostPortPair(kEndpointHost, 443)));
  718. MockWrite spdy_writes[] = {
  719. CreateMockWrite(connect, 0, ASYNC),
  720. };
  721. spdy::SpdySerializedFrame connect_resp(
  722. spdy_util.ConstructSpdyGetReply(nullptr, 0, 1));
  723. MockRead spdy_reads[] = {
  724. // SpdySession starts trying to read from the socket as soon as it's
  725. // created, so this cannot be SYNCHRONOUS.
  726. CreateMockRead(connect_resp, 1, ASYNC),
  727. MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2),
  728. };
  729. Initialize(reads, writes, spdy_reads, spdy_writes, io_mode);
  730. TestConnectJobDelegate test_delegate;
  731. std::unique_ptr<ConnectJob> connect_job =
  732. CreateConnectJobForTunnel(&test_delegate);
  733. // SPDY operations always complete asynchronously.
  734. test_delegate.StartJobExpectingResult(
  735. connect_job.get(), OK, io_mode == SYNCHRONOUS && GetParam() != SPDY);
  736. // Close the H2 session to prevent reuse.
  737. if (GetParam() == SPDY)
  738. session_->CloseAllConnections(ERR_FAILED, "Very good reason");
  739. }
  740. }
  741. TEST_P(HttpProxyConnectJobTest, HostResolutionFailure) {
  742. session_deps_.host_resolver->rules()->AddSimulatedTimeoutFailure(
  743. kHttpProxyHost);
  744. session_deps_.host_resolver->rules()->AddSimulatedTimeoutFailure(
  745. kHttpsProxyHost);
  746. TestConnectJobDelegate test_delegate;
  747. std::unique_ptr<ConnectJob> connect_job =
  748. CreateConnectJobForHttpRequest(&test_delegate, DEFAULT_PRIORITY);
  749. test_delegate.StartJobExpectingResult(connect_job.get(),
  750. ERR_PROXY_CONNECTION_FAILED,
  751. false /* expect_sync_result */);
  752. EXPECT_THAT(connect_job->GetResolveErrorInfo().error,
  753. test::IsError(ERR_DNS_TIMED_OUT));
  754. }
  755. TEST_P(HttpProxyConnectJobTest, RequestPriority) {
  756. // Make request hang during host resolution, so can observe priority there.
  757. session_deps_.host_resolver->set_ondemand_mode(true);
  758. for (int initial_priority = MINIMUM_PRIORITY;
  759. initial_priority <= MAXIMUM_PRIORITY; ++initial_priority) {
  760. SCOPED_TRACE(initial_priority);
  761. for (int new_priority = MINIMUM_PRIORITY; new_priority <= MAXIMUM_PRIORITY;
  762. ++new_priority) {
  763. SCOPED_TRACE(new_priority);
  764. if (initial_priority == new_priority)
  765. continue;
  766. TestConnectJobDelegate test_delegate;
  767. std::unique_ptr<ConnectJob> connect_job = CreateConnectJobForHttpRequest(
  768. &test_delegate, static_cast<RequestPriority>(initial_priority));
  769. EXPECT_THAT(connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  770. EXPECT_FALSE(test_delegate.has_result());
  771. MockHostResolverBase* host_resolver = session_deps_.host_resolver.get();
  772. size_t request_id = host_resolver->last_id();
  773. EXPECT_EQ(initial_priority, host_resolver->request_priority(request_id));
  774. connect_job->ChangePriority(static_cast<RequestPriority>(new_priority));
  775. EXPECT_EQ(new_priority, host_resolver->request_priority(request_id));
  776. connect_job->ChangePriority(
  777. static_cast<RequestPriority>(initial_priority));
  778. EXPECT_EQ(initial_priority, host_resolver->request_priority(request_id));
  779. }
  780. }
  781. }
  782. TEST_P(HttpProxyConnectJobTest, SecureDnsPolicy) {
  783. for (auto secure_dns_policy :
  784. {SecureDnsPolicy::kAllow, SecureDnsPolicy::kDisable}) {
  785. TestConnectJobDelegate test_delegate;
  786. std::unique_ptr<ConnectJob> connect_job = CreateConnectJobForHttpRequest(
  787. &test_delegate, DEFAULT_PRIORITY, secure_dns_policy);
  788. EXPECT_THAT(connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  789. EXPECT_EQ(secure_dns_policy,
  790. session_deps_.host_resolver->last_secure_dns_policy());
  791. }
  792. }
  793. TEST_P(HttpProxyConnectJobTest, SpdySessionKeyDisableSecureDns) {
  794. if (GetParam() != SPDY)
  795. return;
  796. SSLSocketDataProvider ssl_data(ASYNC, OK);
  797. InitializeSpdySsl(&ssl_data);
  798. session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
  799. // SPDY proxy CONNECT request / response, with a pause during the read.
  800. spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyConnect(
  801. nullptr, 0, 1, HttpProxyConnectJob::kH2QuicTunnelPriority,
  802. HostPortPair(kEndpointHost, 443)));
  803. MockWrite spdy_writes[] = {CreateMockWrite(req, 0)};
  804. spdy::SpdySerializedFrame resp(
  805. spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
  806. MockRead spdy_reads[] = {CreateMockRead(resp, 1), MockRead(ASYNC, 0, 2)};
  807. SequencedSocketData spdy_data(spdy_reads, spdy_writes);
  808. spdy_data.set_connect_data(MockConnect(ASYNC, OK));
  809. SequencedSocketData* sequenced_data = &spdy_data;
  810. session_deps_.socket_factory->AddSocketDataProvider(sequenced_data);
  811. TestConnectJobDelegate test_delegate;
  812. auto ssl_params = base::MakeRefCounted<SSLSocketParams>(
  813. base::MakeRefCounted<TransportSocketParams>(
  814. HostPortPair(kHttpsProxyHost, 443), NetworkIsolationKey(),
  815. SecureDnsPolicy::kDisable, OnHostResolutionCallback(),
  816. /*supported_alpns=*/base::flat_set<std::string>()),
  817. nullptr, nullptr, HostPortPair(kHttpsProxyHost, 443), SSLConfig(),
  818. PRIVACY_MODE_DISABLED, NetworkIsolationKey());
  819. auto http_proxy_params = base::MakeRefCounted<HttpProxySocketParams>(
  820. nullptr /* tcp_params */, std::move(ssl_params), false /* is_quic */,
  821. HostPortPair(kEndpointHost, 443),
  822. /*tunnel=*/true, TRAFFIC_ANNOTATION_FOR_TESTS, NetworkIsolationKey());
  823. std::unique_ptr<ConnectJob> connect_job = CreateConnectJob(
  824. std::move(http_proxy_params), &test_delegate, DEFAULT_PRIORITY);
  825. EXPECT_THAT(connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  826. EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
  827. EXPECT_TRUE(
  828. common_connect_job_params_->spdy_session_pool->FindAvailableSession(
  829. SpdySessionKey(HostPortPair(kHttpsProxyHost, 443),
  830. ProxyServer::Direct(), PRIVACY_MODE_DISABLED,
  831. SpdySessionKey::IsProxySession::kTrue, SocketTag(),
  832. NetworkIsolationKey(), SecureDnsPolicy::kDisable),
  833. /* enable_ip_based_pooling = */ false,
  834. /* is_websocket = */ false, NetLogWithSource()));
  835. EXPECT_FALSE(
  836. common_connect_job_params_->spdy_session_pool->FindAvailableSession(
  837. SpdySessionKey(HostPortPair(kHttpsProxyHost, 443),
  838. ProxyServer::Direct(), PRIVACY_MODE_DISABLED,
  839. SpdySessionKey::IsProxySession::kTrue, SocketTag(),
  840. NetworkIsolationKey(), SecureDnsPolicy::kAllow),
  841. /* enable_ip_based_pooling = */ false,
  842. /* is_websocket = */ false, NetLogWithSource()));
  843. }
  844. // Make sure that HttpProxyConnectJob does not pass on its priority to its
  845. // SPDY session's socket request on Init, or on SetPriority.
  846. TEST_P(HttpProxyConnectJobTest, SetSpdySessionSocketRequestPriority) {
  847. if (GetParam() != SPDY)
  848. return;
  849. session_deps_.host_resolver->set_synchronous_mode(true);
  850. // The SPDY CONNECT request should have a priority of kH2QuicTunnelPriority,
  851. // even though the ConnectJob's priority is set to HIGHEST after connection
  852. // establishment.
  853. spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyConnect(
  854. nullptr /* extra_headers */, 0 /* extra_header_count */,
  855. 1 /* stream_id */, HttpProxyConnectJob::kH2QuicTunnelPriority,
  856. HostPortPair(kEndpointHost, 443)));
  857. MockWrite spdy_writes[] = {CreateMockWrite(req, 0, ASYNC)};
  858. spdy::SpdySerializedFrame resp(
  859. spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
  860. MockRead spdy_reads[] = {CreateMockRead(resp, 1, ASYNC),
  861. MockRead(ASYNC, 0, 2)};
  862. Initialize(base::span<MockRead>(), base::span<MockWrite>(), spdy_reads,
  863. spdy_writes, SYNCHRONOUS);
  864. TestConnectJobDelegate test_delegate;
  865. std::unique_ptr<ConnectJob> connect_job =
  866. CreateConnectJobForTunnel(&test_delegate, IDLE);
  867. EXPECT_THAT(connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  868. EXPECT_FALSE(test_delegate.has_result());
  869. connect_job->ChangePriority(HIGHEST);
  870. // Wait for tunnel to be established. If the frame has a MEDIUM priority
  871. // instead of highest, the written data will not match what is expected, and
  872. // the test will fail.
  873. EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
  874. }
  875. TEST_P(HttpProxyConnectJobTest, TCPError) {
  876. // SPDY and HTTPS are identical, as they only differ once a connection is
  877. // established.
  878. if (GetParam() == SPDY)
  879. return;
  880. for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
  881. SCOPED_TRACE(io_mode);
  882. session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
  883. base::HistogramTester histogram_tester;
  884. SequencedSocketData data;
  885. data.set_connect_data(MockConnect(io_mode, ERR_CONNECTION_CLOSED));
  886. session_deps_.socket_factory->AddSocketDataProvider(&data);
  887. TestConnectJobDelegate test_delegate;
  888. std::unique_ptr<ConnectJob> connect_job =
  889. CreateConnectJobForHttpRequest(&test_delegate);
  890. test_delegate.StartJobExpectingResult(
  891. connect_job.get(), ERR_PROXY_CONNECTION_FAILED, io_mode == SYNCHRONOUS);
  892. bool is_secure_proxy = GetParam() == HTTPS;
  893. histogram_tester.ExpectTotalCount(
  894. "Net.HttpProxy.ConnectLatency.Insecure.Error", is_secure_proxy ? 0 : 1);
  895. histogram_tester.ExpectTotalCount(
  896. "Net.HttpProxy.ConnectLatency.Secure.Error", is_secure_proxy ? 1 : 0);
  897. }
  898. }
  899. TEST_P(HttpProxyConnectJobTest, SSLError) {
  900. if (GetParam() == HTTP)
  901. return;
  902. for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
  903. SCOPED_TRACE(io_mode);
  904. session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
  905. base::HistogramTester histogram_tester;
  906. SequencedSocketData data;
  907. data.set_connect_data(MockConnect(io_mode, OK));
  908. session_deps_.socket_factory->AddSocketDataProvider(&data);
  909. SSLSocketDataProvider ssl_data(io_mode, ERR_CERT_AUTHORITY_INVALID);
  910. if (GetParam() == SPDY) {
  911. InitializeSpdySsl(&ssl_data);
  912. }
  913. session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
  914. TestConnectJobDelegate test_delegate;
  915. std::unique_ptr<ConnectJob> connect_job =
  916. CreateConnectJobForTunnel(&test_delegate);
  917. test_delegate.StartJobExpectingResult(connect_job.get(),
  918. ERR_PROXY_CERTIFICATE_INVALID,
  919. io_mode == SYNCHRONOUS);
  920. histogram_tester.ExpectTotalCount(
  921. "Net.HttpProxy.ConnectLatency.Secure.Error", 1);
  922. histogram_tester.ExpectTotalCount(
  923. "Net.HttpProxy.ConnectLatency.Insecure.Error", 0);
  924. }
  925. }
  926. TEST_P(HttpProxyConnectJobTest, TunnelUnexpectedClose) {
  927. for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
  928. SCOPED_TRACE(io_mode);
  929. session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
  930. MockWrite writes[] = {
  931. MockWrite(io_mode, 0,
  932. "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
  933. "Host: www.endpoint.test:443\r\n"
  934. "Proxy-Connection: keep-alive\r\n\r\n"),
  935. };
  936. MockRead reads[] = {
  937. MockRead(io_mode, 1, "HTTP/1.1 200 Conn"),
  938. MockRead(io_mode, ERR_CONNECTION_CLOSED, 2),
  939. };
  940. spdy::SpdySerializedFrame req(SpdyTestUtil().ConstructSpdyConnect(
  941. nullptr /*extra_headers */, 0 /*extra_header_count */,
  942. 1 /* stream_id */, HttpProxyConnectJob::kH2QuicTunnelPriority,
  943. HostPortPair(kEndpointHost, 443)));
  944. MockWrite spdy_writes[] = {CreateMockWrite(req, 0, io_mode)};
  945. // Sync reads don't really work with SPDY, since it constantly reads from
  946. // the socket.
  947. MockRead spdy_reads[] = {
  948. MockRead(ASYNC, ERR_CONNECTION_CLOSED, 1),
  949. };
  950. Initialize(reads, writes, spdy_reads, spdy_writes, io_mode);
  951. TestConnectJobDelegate test_delegate;
  952. std::unique_ptr<ConnectJob> connect_job =
  953. CreateConnectJobForTunnel(&test_delegate);
  954. if (GetParam() == SPDY) {
  955. // SPDY cannot process a headers block unless it's complete and so it
  956. // returns ERR_CONNECTION_CLOSED in this case. SPDY also doesn't return
  957. // this failure synchronously.
  958. test_delegate.StartJobExpectingResult(connect_job.get(),
  959. ERR_CONNECTION_CLOSED,
  960. false /* expect_sync_result */);
  961. } else {
  962. test_delegate.StartJobExpectingResult(connect_job.get(),
  963. ERR_RESPONSE_HEADERS_TRUNCATED,
  964. io_mode == SYNCHRONOUS);
  965. }
  966. }
  967. }
  968. TEST_P(HttpProxyConnectJobTest, Tunnel1xxResponse) {
  969. // Tests that 1xx responses are rejected for a CONNECT request.
  970. if (GetParam() == SPDY) {
  971. // SPDY doesn't have 1xx responses.
  972. return;
  973. }
  974. for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
  975. SCOPED_TRACE(io_mode);
  976. session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
  977. MockWrite writes[] = {
  978. MockWrite(io_mode, 0,
  979. "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
  980. "Host: www.endpoint.test:443\r\n"
  981. "Proxy-Connection: keep-alive\r\n\r\n"),
  982. };
  983. MockRead reads[] = {
  984. MockRead(io_mode, 1, "HTTP/1.1 100 Continue\r\n\r\n"),
  985. MockRead(io_mode, 2, "HTTP/1.1 200 Connection Established\r\n\r\n"),
  986. };
  987. Initialize(reads, writes, base::span<MockRead>(), base::span<MockWrite>(),
  988. io_mode);
  989. TestConnectJobDelegate test_delegate;
  990. std::unique_ptr<ConnectJob> connect_job =
  991. CreateConnectJobForTunnel(&test_delegate);
  992. test_delegate.StartJobExpectingResult(connect_job.get(),
  993. ERR_TUNNEL_CONNECTION_FAILED,
  994. io_mode == SYNCHRONOUS);
  995. }
  996. }
  997. TEST_P(HttpProxyConnectJobTest, TunnelSetupError) {
  998. for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
  999. SCOPED_TRACE(io_mode);
  1000. session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
  1001. MockWrite writes[] = {
  1002. MockWrite(io_mode, 0,
  1003. "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
  1004. "Host: www.endpoint.test:443\r\n"
  1005. "Proxy-Connection: keep-alive\r\n\r\n"),
  1006. };
  1007. MockRead reads[] = {
  1008. MockRead(io_mode, 1, "HTTP/1.1 304 Not Modified\r\n\r\n"),
  1009. };
  1010. SpdyTestUtil spdy_util;
  1011. spdy::SpdySerializedFrame req(spdy_util.ConstructSpdyConnect(
  1012. nullptr /* extra_headers */, 0 /* extra_header_count */,
  1013. 1 /* stream_id */, HttpProxyConnectJob::kH2QuicTunnelPriority,
  1014. HostPortPair("www.endpoint.test", 443)));
  1015. spdy::SpdySerializedFrame rst(
  1016. spdy_util.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL));
  1017. MockWrite spdy_writes[] = {
  1018. CreateMockWrite(req, 0, io_mode),
  1019. CreateMockWrite(rst, 2, io_mode),
  1020. };
  1021. spdy::SpdySerializedFrame resp(spdy_util.ConstructSpdyReplyError(1));
  1022. // Sync reads don't really work with SPDY, since it constantly reads from
  1023. // the socket.
  1024. MockRead spdy_reads[] = {
  1025. CreateMockRead(resp, 1, ASYNC),
  1026. MockRead(ASYNC, OK, 3),
  1027. };
  1028. Initialize(reads, writes, spdy_reads, spdy_writes, io_mode);
  1029. TestConnectJobDelegate test_delegate;
  1030. std::unique_ptr<ConnectJob> connect_job =
  1031. CreateConnectJobForTunnel(&test_delegate, LOW);
  1032. test_delegate.StartJobExpectingResult(
  1033. connect_job.get(), ERR_TUNNEL_CONNECTION_FAILED,
  1034. io_mode == SYNCHRONOUS && GetParam() != SPDY);
  1035. // Need to close the session to prevent reuse in the next loop iteration.
  1036. session_->spdy_session_pool()->CloseAllSessions();
  1037. }
  1038. }
  1039. TEST_P(HttpProxyConnectJobTest, SslClientAuth) {
  1040. if (GetParam() == HTTP)
  1041. return;
  1042. for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
  1043. SCOPED_TRACE(io_mode);
  1044. session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
  1045. base::HistogramTester histogram_tester;
  1046. SequencedSocketData socket_data(MockConnect(io_mode, OK),
  1047. base::span<const MockRead>(),
  1048. base::span<const MockWrite>());
  1049. session_deps_.socket_factory->AddSocketDataProvider(&socket_data);
  1050. SSLSocketDataProvider ssl_data(io_mode, ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
  1051. if (GetParam() == SPDY)
  1052. InitializeSpdySsl(&ssl_data);
  1053. session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
  1054. // Redirects in the HTTPS case return errors, but also return sockets.
  1055. TestConnectJobDelegate test_delegate;
  1056. std::unique_ptr<ConnectJob> connect_job =
  1057. CreateConnectJobForTunnel(&test_delegate);
  1058. test_delegate.StartJobExpectingResult(connect_job.get(),
  1059. ERR_SSL_CLIENT_AUTH_CERT_NEEDED,
  1060. io_mode == SYNCHRONOUS);
  1061. histogram_tester.ExpectTotalCount(
  1062. "Net.HttpProxy.ConnectLatency.Secure.Error", 1);
  1063. histogram_tester.ExpectTotalCount(
  1064. "Net.HttpProxy.ConnectLatency.Insecure.Error", 0);
  1065. }
  1066. }
  1067. TEST_P(HttpProxyConnectJobTest, TunnelSetupRedirect) {
  1068. const std::string kRedirectTarget = "https://foo.google.com/";
  1069. for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
  1070. SCOPED_TRACE(io_mode);
  1071. session_deps_.host_resolver->set_synchronous_mode(io_mode == SYNCHRONOUS);
  1072. const std::string kResponseText =
  1073. "HTTP/1.1 302 Found\r\n"
  1074. "Location: " +
  1075. kRedirectTarget +
  1076. "\r\n"
  1077. "Set-Cookie: foo=bar\r\n"
  1078. "\r\n";
  1079. MockWrite writes[] = {
  1080. MockWrite(io_mode, 0,
  1081. "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
  1082. "Host: www.endpoint.test:443\r\n"
  1083. "Proxy-Connection: keep-alive\r\n\r\n"),
  1084. };
  1085. MockRead reads[] = {
  1086. MockRead(io_mode, 1, kResponseText.c_str()),
  1087. };
  1088. SpdyTestUtil spdy_util;
  1089. spdy::SpdySerializedFrame req(spdy_util.ConstructSpdyConnect(
  1090. nullptr /* extra_headers */, 0 /* extra_header_count */, 1,
  1091. DEFAULT_PRIORITY, HostPortPair(kEndpointHost, 443)));
  1092. spdy::SpdySerializedFrame rst(
  1093. spdy_util.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL));
  1094. MockWrite spdy_writes[] = {
  1095. CreateMockWrite(req, 0, io_mode),
  1096. CreateMockWrite(rst, 3, io_mode),
  1097. };
  1098. const char* const responseHeaders[] = {
  1099. "location",
  1100. kRedirectTarget.c_str(),
  1101. "set-cookie",
  1102. "foo=bar",
  1103. };
  1104. const int responseHeadersSize = std::size(responseHeaders) / 2;
  1105. spdy::SpdySerializedFrame resp(spdy_util.ConstructSpdyReplyError(
  1106. "302", responseHeaders, responseHeadersSize, 1));
  1107. MockRead spdy_reads[] = {
  1108. CreateMockRead(resp, 1, ASYNC),
  1109. MockRead(ASYNC, 0, 2),
  1110. };
  1111. Initialize(reads, writes, spdy_reads, spdy_writes, io_mode);
  1112. // Redirects during CONNECT returns an error.
  1113. TestConnectJobDelegate test_delegate(
  1114. TestConnectJobDelegate::SocketExpected::ON_SUCCESS_ONLY);
  1115. std::unique_ptr<ConnectJob> connect_job =
  1116. CreateConnectJobForTunnel(&test_delegate);
  1117. // H2 never completes synchronously.
  1118. bool expect_sync_result = (io_mode == SYNCHRONOUS && GetParam() != SPDY);
  1119. // We don't trust 302 responses to CONNECT from proxies.
  1120. test_delegate.StartJobExpectingResult(
  1121. connect_job.get(), ERR_TUNNEL_CONNECTION_FAILED, expect_sync_result);
  1122. EXPECT_FALSE(test_delegate.socket());
  1123. // Need to close the session to prevent reuse in the next loop iteration.
  1124. session_->spdy_session_pool()->CloseAllSessions();
  1125. }
  1126. }
  1127. // Test timeouts in the case of an auth challenge and response.
  1128. TEST_P(HttpProxyConnectJobTest, TestTimeoutsAuthChallenge) {
  1129. // Wait until this amount of time before something times out.
  1130. const base::TimeDelta kTinyTime = base::Microseconds(1);
  1131. enum class TimeoutPhase {
  1132. CONNECT,
  1133. PROXY_HANDSHAKE,
  1134. SECOND_PROXY_HANDSHAKE,
  1135. NONE,
  1136. };
  1137. const TimeoutPhase kTimeoutPhases[] = {
  1138. TimeoutPhase::CONNECT,
  1139. TimeoutPhase::PROXY_HANDSHAKE,
  1140. TimeoutPhase::SECOND_PROXY_HANDSHAKE,
  1141. TimeoutPhase::NONE,
  1142. };
  1143. session_deps_.host_resolver->set_ondemand_mode(true);
  1144. MockWrite writes[] = {
  1145. MockWrite(ASYNC, 0,
  1146. "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
  1147. "Host: www.endpoint.test:443\r\n"
  1148. "Proxy-Connection: keep-alive\r\n\r\n"),
  1149. MockWrite(ASYNC, 3,
  1150. "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
  1151. "Host: www.endpoint.test:443\r\n"
  1152. "Proxy-Connection: keep-alive\r\n"
  1153. "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
  1154. };
  1155. MockRead reads[] = {
  1156. // Pause before first response is read.
  1157. MockRead(ASYNC, ERR_IO_PENDING, 1),
  1158. MockRead(ASYNC, 2,
  1159. "HTTP/1.1 407 Proxy Authentication Required\r\n"
  1160. "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"
  1161. "Content-Length: 0\r\n\r\n"),
  1162. // Pause again before second response is read.
  1163. MockRead(ASYNC, ERR_IO_PENDING, 4),
  1164. MockRead(ASYNC, 5, "HTTP/1.1 200 Connection Established\r\n\r\n"),
  1165. };
  1166. SpdyTestUtil spdy_util;
  1167. spdy::SpdySerializedFrame connect(spdy_util.ConstructSpdyConnect(
  1168. nullptr, 0, 1, HttpProxyConnectJob::kH2QuicTunnelPriority,
  1169. HostPortPair(kEndpointHost, 443)));
  1170. spdy::SpdySerializedFrame rst(
  1171. spdy_util.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL));
  1172. spdy_util.UpdateWithStreamDestruction(1);
  1173. // After calling trans.RestartWithAuth(), this is the request we should
  1174. // be issuing -- the final header line contains the credentials.
  1175. const char* const kSpdyAuthCredentials[] = {
  1176. "proxy-authorization",
  1177. "Basic Zm9vOmJhcg==",
  1178. };
  1179. spdy::SpdySerializedFrame connect2(spdy_util.ConstructSpdyConnect(
  1180. kSpdyAuthCredentials, std::size(kSpdyAuthCredentials) / 2, 3,
  1181. HttpProxyConnectJob::kH2QuicTunnelPriority,
  1182. HostPortPair(kEndpointHost, 443)));
  1183. // This may be sent in some tests, either when tearing down a successful
  1184. // connection, or on timeout.
  1185. spdy::SpdySerializedFrame rst2(
  1186. spdy_util.ConstructSpdyRstStream(3, spdy::ERROR_CODE_CANCEL));
  1187. MockWrite spdy_writes[] = {
  1188. CreateMockWrite(connect, 0, ASYNC),
  1189. CreateMockWrite(rst, 3, ASYNC),
  1190. CreateMockWrite(connect2, 4, ASYNC),
  1191. CreateMockWrite(rst2, 8, ASYNC),
  1192. };
  1193. // The proxy responds to the connect with a 407, using a persistent
  1194. // connection.
  1195. const char kAuthStatus[] = "407";
  1196. const char* const kAuthChallenge[] = {
  1197. "proxy-authenticate",
  1198. "Basic realm=\"MyRealm1\"",
  1199. };
  1200. spdy::SpdySerializedFrame connect_auth_resp(spdy_util.ConstructSpdyReplyError(
  1201. kAuthStatus, kAuthChallenge, std::size(kAuthChallenge) / 2, 1));
  1202. spdy::SpdySerializedFrame connect2_resp(
  1203. spdy_util.ConstructSpdyGetReply(nullptr, 0, 3));
  1204. MockRead spdy_reads[] = {
  1205. // Pause before first response is read.
  1206. MockRead(ASYNC, ERR_IO_PENDING, 1),
  1207. CreateMockRead(connect_auth_resp, 2, ASYNC),
  1208. // Pause again before second response is read.
  1209. MockRead(ASYNC, ERR_IO_PENDING, 5),
  1210. CreateMockRead(connect2_resp, 6, ASYNC),
  1211. MockRead(ASYNC, OK, 7),
  1212. };
  1213. for (TimeoutPhase timeout_phase : kTimeoutPhases) {
  1214. SCOPED_TRACE(static_cast<int>(timeout_phase));
  1215. // Need to close the session to prevent reuse of a session from the last
  1216. // loop iteration.
  1217. session_->spdy_session_pool()->CloseAllSessions();
  1218. // And clear the auth cache to prevent reusing cache entries.
  1219. session_->http_auth_cache()->ClearAllEntries();
  1220. TestConnectJobDelegate test_delegate;
  1221. std::unique_ptr<ConnectJob> connect_job =
  1222. CreateConnectJobForTunnel(&test_delegate);
  1223. // Connecting should run until the request hits the HostResolver.
  1224. EXPECT_THAT(connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  1225. EXPECT_FALSE(test_delegate.has_result());
  1226. EXPECT_TRUE(session_deps_.host_resolver->has_pending_requests());
  1227. EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, connect_job->GetLoadState());
  1228. // Run until just before timeout.
  1229. FastForwardBy(GetNestedConnectionTimeout() - kTinyTime);
  1230. EXPECT_FALSE(test_delegate.has_result());
  1231. // Wait until timeout, if appropriate.
  1232. if (timeout_phase == TimeoutPhase::CONNECT) {
  1233. FastForwardBy(kTinyTime);
  1234. ASSERT_TRUE(test_delegate.has_result());
  1235. EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
  1236. continue;
  1237. }
  1238. // Add mock reads for socket needed in next step. Connect phase is timed out
  1239. // before establishing a connection, so don't need them for
  1240. // TimeoutPhase::CONNECT.
  1241. Initialize(reads, writes, spdy_reads, spdy_writes, SYNCHRONOUS);
  1242. // Finish resolution.
  1243. session_deps_.host_resolver->ResolveOnlyRequestNow();
  1244. EXPECT_FALSE(test_delegate.has_result());
  1245. EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL,
  1246. connect_job->GetLoadState());
  1247. // Wait until just before negotiation with the tunnel should time out.
  1248. FastForwardBy(HttpProxyConnectJob::TunnelTimeoutForTesting() - kTinyTime);
  1249. EXPECT_FALSE(test_delegate.has_result());
  1250. if (timeout_phase == TimeoutPhase::PROXY_HANDSHAKE) {
  1251. FastForwardBy(kTinyTime);
  1252. ASSERT_TRUE(test_delegate.has_result());
  1253. EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
  1254. continue;
  1255. }
  1256. data_->Resume();
  1257. test_delegate.WaitForAuthChallenge(1);
  1258. EXPECT_FALSE(test_delegate.has_result());
  1259. // ConnectJobs cannot timeout while showing an auth dialog.
  1260. FastForwardBy(base::Days(1));
  1261. EXPECT_FALSE(test_delegate.has_result());
  1262. // Send credentials
  1263. test_delegate.auth_controller()->ResetAuth(AuthCredentials(u"foo", u"bar"));
  1264. test_delegate.RunAuthCallback();
  1265. EXPECT_FALSE(test_delegate.has_result());
  1266. FastForwardBy(HttpProxyConnectJob::TunnelTimeoutForTesting() - kTinyTime);
  1267. EXPECT_FALSE(test_delegate.has_result());
  1268. if (timeout_phase == TimeoutPhase::SECOND_PROXY_HANDSHAKE) {
  1269. FastForwardBy(kTinyTime);
  1270. ASSERT_TRUE(test_delegate.has_result());
  1271. EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
  1272. continue;
  1273. }
  1274. data_->Resume();
  1275. EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
  1276. }
  1277. }
  1278. // Same as above, except test the case the first connection cannot be reused
  1279. // once credentials are received.
  1280. TEST_P(HttpProxyConnectJobTest, TestTimeoutsAuthChallengeNewConnection) {
  1281. // Proxy-Connection: Close doesn't make sense with H2.
  1282. if (GetParam() == SPDY)
  1283. return;
  1284. enum class TimeoutPhase {
  1285. CONNECT,
  1286. PROXY_HANDSHAKE,
  1287. SECOND_CONNECT,
  1288. SECOND_PROXY_HANDSHAKE,
  1289. // This has to be last for the H2 proxy case, since success will populate
  1290. // the H2 session pool.
  1291. NONE,
  1292. };
  1293. const TimeoutPhase kTimeoutPhases[] = {
  1294. TimeoutPhase::CONNECT, TimeoutPhase::PROXY_HANDSHAKE,
  1295. TimeoutPhase::SECOND_CONNECT, TimeoutPhase::SECOND_PROXY_HANDSHAKE,
  1296. TimeoutPhase::NONE,
  1297. };
  1298. // Wait until this amount of time before something times out.
  1299. const base::TimeDelta kTinyTime = base::Microseconds(1);
  1300. session_deps_.host_resolver->set_ondemand_mode(true);
  1301. MockWrite writes[] = {
  1302. MockWrite(ASYNC, 0,
  1303. "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
  1304. "Host: www.endpoint.test:443\r\n"
  1305. "Proxy-Connection: keep-alive\r\n\r\n"),
  1306. };
  1307. MockRead reads[] = {
  1308. // Pause at read.
  1309. MockRead(ASYNC, ERR_IO_PENDING, 1),
  1310. MockRead(ASYNC, 2,
  1311. "HTTP/1.1 407 Proxy Authentication Required\r\n"
  1312. "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"
  1313. "Proxy-Connection: Close\r\n"
  1314. "Content-Length: 0\r\n\r\n"),
  1315. };
  1316. MockWrite writes2[] = {
  1317. MockWrite(ASYNC, 0,
  1318. "CONNECT www.endpoint.test:443 HTTP/1.1\r\n"
  1319. "Host: www.endpoint.test:443\r\n"
  1320. "Proxy-Connection: keep-alive\r\n"
  1321. "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
  1322. };
  1323. MockRead reads2[] = {
  1324. // Pause at read.
  1325. MockRead(ASYNC, ERR_IO_PENDING, 1),
  1326. MockRead(ASYNC, 2, "HTTP/1.1 200 Connection Established\r\n\r\n"),
  1327. };
  1328. for (TimeoutPhase timeout_phase : kTimeoutPhases) {
  1329. SCOPED_TRACE(static_cast<int>(timeout_phase));
  1330. // Need to clear the auth cache to prevent reusing cache entries.
  1331. session_->http_auth_cache()->ClearAllEntries();
  1332. TestConnectJobDelegate test_delegate;
  1333. std::unique_ptr<ConnectJob> connect_job =
  1334. CreateConnectJobForTunnel(&test_delegate);
  1335. // Connecting should run until the request hits the HostResolver.
  1336. EXPECT_THAT(connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  1337. EXPECT_FALSE(test_delegate.has_result());
  1338. EXPECT_TRUE(session_deps_.host_resolver->has_pending_requests());
  1339. EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, connect_job->GetLoadState());
  1340. // Run until just before timeout.
  1341. FastForwardBy(GetNestedConnectionTimeout() - kTinyTime);
  1342. EXPECT_FALSE(test_delegate.has_result());
  1343. // Wait until timeout, if appropriate.
  1344. if (timeout_phase == TimeoutPhase::CONNECT) {
  1345. FastForwardBy(kTinyTime);
  1346. ASSERT_TRUE(test_delegate.has_result());
  1347. EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
  1348. continue;
  1349. }
  1350. // Add mock reads for socket needed in next step. Connect phase is timed out
  1351. // before establishing a connection, so don't need them for
  1352. // TimeoutPhase::CONNECT.
  1353. Initialize(reads, writes, base::span<MockRead>(), base::span<MockWrite>(),
  1354. SYNCHRONOUS);
  1355. // Finish resolution.
  1356. session_deps_.host_resolver->ResolveOnlyRequestNow();
  1357. EXPECT_FALSE(test_delegate.has_result());
  1358. EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL,
  1359. connect_job->GetLoadState());
  1360. // Wait until just before negotiation with the tunnel should time out.
  1361. FastForwardBy(HttpProxyConnectJob::TunnelTimeoutForTesting() - kTinyTime);
  1362. EXPECT_FALSE(test_delegate.has_result());
  1363. if (timeout_phase == TimeoutPhase::PROXY_HANDSHAKE) {
  1364. FastForwardBy(kTinyTime);
  1365. ASSERT_TRUE(test_delegate.has_result());
  1366. EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
  1367. continue;
  1368. }
  1369. data_->Resume();
  1370. test_delegate.WaitForAuthChallenge(1);
  1371. EXPECT_FALSE(test_delegate.has_result());
  1372. // ConnectJobs cannot timeout while showing an auth dialog.
  1373. FastForwardBy(base::Days(1));
  1374. EXPECT_FALSE(test_delegate.has_result());
  1375. // Send credentials
  1376. test_delegate.auth_controller()->ResetAuth(AuthCredentials(u"foo", u"bar"));
  1377. test_delegate.RunAuthCallback();
  1378. EXPECT_FALSE(test_delegate.has_result());
  1379. // Since the connection was not reusable, a new connection needs to be
  1380. // established.
  1381. base::RunLoop().RunUntilIdle();
  1382. EXPECT_FALSE(test_delegate.has_result());
  1383. EXPECT_TRUE(session_deps_.host_resolver->has_pending_requests());
  1384. EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, connect_job->GetLoadState());
  1385. // Run until just before timeout.
  1386. FastForwardBy(GetNestedConnectionTimeout() - kTinyTime);
  1387. EXPECT_FALSE(test_delegate.has_result());
  1388. // Wait until timeout, if appropriate.
  1389. if (timeout_phase == TimeoutPhase::SECOND_CONNECT) {
  1390. FastForwardBy(kTinyTime);
  1391. ASSERT_TRUE(test_delegate.has_result());
  1392. EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
  1393. continue;
  1394. }
  1395. // Add mock reads for socket needed in next step. Connect phase is timed out
  1396. // before establishing a connection, so don't need them for
  1397. // TimeoutPhase::SECOND_CONNECT.
  1398. Initialize(reads2, writes2, base::span<MockRead>(), base::span<MockWrite>(),
  1399. SYNCHRONOUS);
  1400. // Finish resolution.
  1401. session_deps_.host_resolver->ResolveOnlyRequestNow();
  1402. EXPECT_FALSE(test_delegate.has_result());
  1403. EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL,
  1404. connect_job->GetLoadState());
  1405. // Wait until just before negotiation with the tunnel should time out.
  1406. FastForwardBy(HttpProxyConnectJob::TunnelTimeoutForTesting() - kTinyTime);
  1407. EXPECT_FALSE(test_delegate.has_result());
  1408. if (timeout_phase == TimeoutPhase::SECOND_PROXY_HANDSHAKE) {
  1409. FastForwardBy(kTinyTime);
  1410. ASSERT_TRUE(test_delegate.has_result());
  1411. EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
  1412. continue;
  1413. }
  1414. data_->Resume();
  1415. ASSERT_TRUE(test_delegate.has_result());
  1416. EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
  1417. }
  1418. }
  1419. TEST_P(HttpProxyConnectJobTest, ConnectionTimeoutNoNQE) {
  1420. // Doesn't actually matter whether or not this is for a tunnel - the
  1421. // connection timeout is the same, though it probably shouldn't be the same,
  1422. // since tunnels need an extra round trip.
  1423. base::TimeDelta alternate_connection_timeout =
  1424. HttpProxyConnectJob::AlternateNestedConnectionTimeout(
  1425. *CreateParams(true /* tunnel */, SecureDnsPolicy::kAllow),
  1426. nullptr /* network_quality_estimator */);
  1427. #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
  1428. // On Android and iOS, when there's no NQE, there's a hard-coded alternate
  1429. // proxy timeout.
  1430. EXPECT_EQ(base::Seconds(10), alternate_connection_timeout);
  1431. #else
  1432. // On other platforms, there is not.
  1433. EXPECT_EQ(base::TimeDelta(), alternate_connection_timeout);
  1434. #endif
  1435. }
  1436. TEST_P(HttpProxyConnectJobTest, ConnectionTimeoutMin) {
  1437. // Set RTT estimate to a low value.
  1438. base::TimeDelta rtt_estimate = base::Milliseconds(1);
  1439. network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
  1440. EXPECT_LE(base::TimeDelta(), GetNestedConnectionTimeout());
  1441. // Test against a large value.
  1442. EXPECT_GE(base::Minutes(10), GetNestedConnectionTimeout());
  1443. EXPECT_EQ(base::Seconds(8), GetNestedConnectionTimeout());
  1444. }
  1445. TEST_P(HttpProxyConnectJobTest, ConnectionTimeoutMax) {
  1446. // Set RTT estimate to a high value.
  1447. base::TimeDelta rtt_estimate = base::Seconds(100);
  1448. network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
  1449. EXPECT_LE(base::TimeDelta(), GetNestedConnectionTimeout());
  1450. // Test against a large value.
  1451. EXPECT_GE(base::Minutes(10), GetNestedConnectionTimeout());
  1452. EXPECT_EQ(base::Seconds(30), GetNestedConnectionTimeout());
  1453. }
  1454. // Tests the connection timeout values when the field trial parameters are
  1455. // specified.
  1456. TEST_P(HttpProxyConnectJobTest, ConnectionTimeoutWithExperiment) {
  1457. // Timeout should be kMultiplier times the HTTP RTT estimate.
  1458. const int kMultiplier = 4;
  1459. const base::TimeDelta kMinTimeout = base::Seconds(8);
  1460. const base::TimeDelta kMaxTimeout = base::Seconds(20);
  1461. InitAdaptiveTimeoutFieldTrialWithParams(false, kMultiplier, kMultiplier,
  1462. kMinTimeout, kMaxTimeout);
  1463. EXPECT_LE(base::TimeDelta(), GetNestedConnectionTimeout());
  1464. base::TimeDelta rtt_estimate = base::Seconds(4);
  1465. network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
  1466. base::TimeDelta expected_connection_timeout = kMultiplier * rtt_estimate;
  1467. EXPECT_EQ(expected_connection_timeout, GetNestedConnectionTimeout());
  1468. // Connection timeout should not exceed kMaxTimeout.
  1469. rtt_estimate = base::Seconds(25);
  1470. network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
  1471. EXPECT_EQ(kMaxTimeout, GetNestedConnectionTimeout());
  1472. // Connection timeout should not be less than kMinTimeout.
  1473. rtt_estimate = base::Seconds(0);
  1474. network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
  1475. EXPECT_EQ(kMinTimeout, GetNestedConnectionTimeout());
  1476. }
  1477. // Tests the connection timeout values when the field trial parameters are
  1478. // specified.
  1479. TEST_P(HttpProxyConnectJobTest, ConnectionTimeoutExperimentDifferentParams) {
  1480. // Timeout should be kMultiplier times the HTTP RTT estimate.
  1481. const int kMultiplier = 3;
  1482. const base::TimeDelta kMinTimeout = base::Seconds(2);
  1483. const base::TimeDelta kMaxTimeout = base::Seconds(30);
  1484. InitAdaptiveTimeoutFieldTrialWithParams(false, kMultiplier, kMultiplier,
  1485. kMinTimeout, kMaxTimeout);
  1486. EXPECT_LE(base::TimeDelta(), GetNestedConnectionTimeout());
  1487. base::TimeDelta rtt_estimate = base::Seconds(2);
  1488. network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
  1489. EXPECT_EQ(kMultiplier * rtt_estimate, GetNestedConnectionTimeout());
  1490. // A change in RTT estimate should also change the connection timeout.
  1491. rtt_estimate = base::Seconds(7);
  1492. network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
  1493. EXPECT_EQ(kMultiplier * rtt_estimate, GetNestedConnectionTimeout());
  1494. // Connection timeout should not exceed kMaxTimeout.
  1495. rtt_estimate = base::Seconds(35);
  1496. network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
  1497. EXPECT_EQ(kMaxTimeout, GetNestedConnectionTimeout());
  1498. // Connection timeout should not be less than kMinTimeout.
  1499. rtt_estimate = base::Seconds(0);
  1500. network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
  1501. EXPECT_EQ(kMinTimeout, GetNestedConnectionTimeout());
  1502. }
  1503. TEST_P(HttpProxyConnectJobTest, ConnectionTimeoutWithConnectionProperty) {
  1504. const int kSecureMultiplier = 3;
  1505. const int kNonSecureMultiplier = 5;
  1506. const base::TimeDelta kMinTimeout = base::Seconds(2);
  1507. const base::TimeDelta kMaxTimeout = base::Seconds(30);
  1508. InitAdaptiveTimeoutFieldTrialWithParams(
  1509. false, kSecureMultiplier, kNonSecureMultiplier, kMinTimeout, kMaxTimeout);
  1510. const base::TimeDelta kRttEstimate = base::Seconds(2);
  1511. network_quality_estimator_->SetStartTimeNullHttpRtt(kRttEstimate);
  1512. // By default, connection timeout should return the timeout for secure
  1513. // proxies.
  1514. if (GetParam() != HTTP) {
  1515. EXPECT_EQ(kSecureMultiplier * kRttEstimate, GetNestedConnectionTimeout());
  1516. } else {
  1517. EXPECT_EQ(kNonSecureMultiplier * kRttEstimate,
  1518. GetNestedConnectionTimeout());
  1519. }
  1520. }
  1521. // Tests the connection timeout values when the field trial parameters are not
  1522. // specified.
  1523. TEST_P(HttpProxyConnectJobTest, ProxyPoolTimeoutWithExperimentDefaultParams) {
  1524. InitAdaptiveTimeoutFieldTrialWithParams(true, 0, 0, base::TimeDelta(),
  1525. base::TimeDelta());
  1526. EXPECT_LE(base::TimeDelta(), GetNestedConnectionTimeout());
  1527. // Timeout should be |http_rtt_multiplier| times the HTTP RTT
  1528. // estimate.
  1529. base::TimeDelta rtt_estimate = base::Milliseconds(10);
  1530. network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
  1531. // Connection timeout should not be less than the HTTP RTT estimate.
  1532. EXPECT_LE(rtt_estimate, GetNestedConnectionTimeout());
  1533. // A change in RTT estimate should also change the connection timeout.
  1534. rtt_estimate = base::Seconds(10);
  1535. network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
  1536. // Connection timeout should not be less than the HTTP RTT estimate.
  1537. EXPECT_LE(rtt_estimate, GetNestedConnectionTimeout());
  1538. // Set RTT to a very large value.
  1539. rtt_estimate = base::Minutes(60);
  1540. network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
  1541. EXPECT_GT(rtt_estimate, GetNestedConnectionTimeout());
  1542. // Set RTT to a very small value.
  1543. rtt_estimate = base::Seconds(0);
  1544. network_quality_estimator_->SetStartTimeNullHttpRtt(rtt_estimate);
  1545. EXPECT_LT(rtt_estimate, GetNestedConnectionTimeout());
  1546. }
  1547. } // namespace net