nsHttpConnection.cpp (92034B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=4 sw=2 sts=2 et cin: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 // HttpLog.h should generally be included first 8 #include "HttpLog.h" 9 10 // Log on level :5, instead of default :4. 11 #undef LOG 12 #define LOG(args) LOG5(args) 13 #undef LOG_ENABLED 14 #define LOG_ENABLED() LOG5_ENABLED() 15 16 #include "ASpdySession.h" 17 #include "NSSErrorsService.h" 18 #include "TLSTransportLayer.h" 19 #include "mozilla/ChaosMode.h" 20 #include "mozilla/glean/NetwerkMetrics.h" 21 #include "mozilla/glean/NetwerkProtocolHttpMetrics.h" 22 #include "mozilla/StaticPrefs_network.h" 23 #include "mozpkix/pkixnss.h" 24 #include "nsCRT.h" 25 #include "nsHttpConnection.h" 26 #include "nsHttpHandler.h" 27 #include "nsHttpRequestHead.h" 28 #include "nsHttpResponseHead.h" 29 #include "nsIClassOfService.h" 30 #include "nsIOService.h" 31 #include "nsISocketTransport.h" 32 #include "nsISupportsPriority.h" 33 #include "nsITLSSocketControl.h" 34 #include "nsITransportSecurityInfo.h" 35 #include "nsPreloadedStream.h" 36 #include "nsProxyRelease.h" 37 #include "nsQueryObject.h" 38 #include "nsSocketTransport2.h" 39 #include "nsSocketTransportService2.h" 40 #include "nsStringStream.h" 41 #include "sslerr.h" 42 #include "sslt.h" 43 44 namespace mozilla::net { 45 extern const nsCString& TRRProviderKey(); 46 } 47 48 namespace mozilla::net { 49 50 //----------------------------------------------------------------------------- 51 // nsHttpConnection <public> 52 //----------------------------------------------------------------------------- 53 54 nsHttpConnection::nsHttpConnection() : mHttpHandler(gHttpHandler) { 55 LOG(("Creating nsHttpConnection @%p\n", this)); 56 57 // the default timeout is for when this connection has not yet processed a 58 // transaction 59 static const PRIntervalTime k5Sec = PR_SecondsToInterval(5); 60 mIdleTimeout = (k5Sec < gHttpHandler->IdleTimeout()) 61 ? k5Sec 62 : gHttpHandler->IdleTimeout(); 63 } 64 65 nsHttpConnection::~nsHttpConnection() { 66 LOG(("Destroying nsHttpConnection @%p\n", this)); 67 68 if (!mEverUsedSpdy) { 69 LOG(("nsHttpConnection %p performed %d HTTP/1.x transactions\n", this, 70 mHttp1xTransactionCount)); 71 glean::http::request_per_conn.AccumulateSingleSample( 72 mHttp1xTransactionCount); 73 nsHttpConnectionInfo* ci = nullptr; 74 if (mTransaction) { 75 ci = mTransaction->ConnectionInfo(); 76 } 77 if (!ci) { 78 ci = mConnInfo; 79 } 80 81 MOZ_ASSERT(ci); 82 if (ci->GetIsTrrServiceChannel() && mHttp1xTransactionCount) { 83 mozilla::glean::networking::trr_request_count_per_conn 84 .Get(nsPrintfCString("%s_h1", ci->Origin())) 85 .Add(static_cast<int32_t>(mHttp1xTransactionCount)); 86 } 87 } 88 89 if (mTotalBytesRead) { 90 uint32_t totalKBRead = static_cast<uint32_t>(mTotalBytesRead >> 10); 91 LOG(("nsHttpConnection %p read %dkb on connection spdy=%d\n", this, 92 totalKBRead, mEverUsedSpdy)); 93 if (mEverUsedSpdy) { 94 glean::spdy::kbread_per_conn.Accumulate(totalKBRead); 95 } else { 96 glean::http::kbread_per_conn2.Accumulate(totalKBRead); 97 } 98 } 99 100 if (mForceSendTimer) { 101 mForceSendTimer->Cancel(); 102 mForceSendTimer = nullptr; 103 } 104 105 auto ReleaseSocketTransport = 106 [socketTransport(std::move(mSocketTransport))]() mutable { 107 socketTransport = nullptr; 108 }; 109 if (OnSocketThread()) { 110 ReleaseSocketTransport(); 111 } else { 112 gSocketTransportService->Dispatch(NS_NewRunnableFunction( 113 "nsHttpConnection::~nsHttpConnection", ReleaseSocketTransport)); 114 } 115 } 116 117 nsresult nsHttpConnection::Init( 118 nsHttpConnectionInfo* info, uint16_t maxHangTime, 119 nsISocketTransport* transport, nsIAsyncInputStream* instream, 120 nsIAsyncOutputStream* outstream, bool connectedTransport, nsresult status, 121 nsIInterfaceRequestor* callbacks, PRIntervalTime rtt, bool forWebSocket) { 122 LOG1(("nsHttpConnection::Init this=%p sockettransport=%p forWebSocket=%d", 123 this, transport, forWebSocket)); 124 NS_ENSURE_ARG_POINTER(info); 125 NS_ENSURE_TRUE(!mConnInfo, NS_ERROR_ALREADY_INITIALIZED); 126 MOZ_ASSERT(NS_SUCCEEDED(status) || !connectedTransport); 127 128 mConnectedTransport = connectedTransport; 129 mConnInfo = info; 130 MOZ_ASSERT(mConnInfo); 131 132 mLastWriteTime = mLastReadTime = PR_IntervalNow(); 133 mRtt = rtt; 134 mMaxHangTime = PR_SecondsToInterval(maxHangTime); 135 136 mSocketTransport = transport; 137 mSocketIn = instream; 138 mSocketOut = outstream; 139 mForWebSocket = forWebSocket; 140 141 // See explanation for non-strictness of this operation in 142 // SetSecurityCallbacks. 143 mCallbacks = new nsMainThreadPtrHolder<nsIInterfaceRequestor>( 144 "nsHttpConnection::mCallbacks", callbacks, false); 145 146 mErrorBeforeConnect = status; 147 if (NS_SUCCEEDED(mErrorBeforeConnect)) { 148 mSocketTransport->SetEventSink(this, nullptr); 149 mSocketTransport->SetSecurityCallbacks(this); 150 ChangeConnectionState(ConnectionState::INITED); 151 } else { 152 SetCloseReason(ToCloseReason(mErrorBeforeConnect)); 153 } 154 155 mTlsHandshaker = new TlsHandshaker(mConnInfo, this); 156 return NS_OK; 157 } 158 159 nsresult nsHttpConnection::TryTakeSubTransactions( 160 nsTArray<RefPtr<nsAHttpTransaction> >& list) { 161 nsresult rv = mTransaction->TakeSubTransactions(list); 162 163 if (rv == NS_ERROR_ALREADY_OPENED) { 164 // Has the interface for TakeSubTransactions() changed? 165 LOG( 166 ("TakeSubTransactions somehow called after " 167 "nsAHttpTransaction began processing\n")); 168 MOZ_ASSERT(false, 169 "TakeSubTransactions somehow called after " 170 "nsAHttpTransaction began processing"); 171 mTransaction->Close(NS_ERROR_ABORT); 172 return rv; 173 } 174 175 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) { 176 // Has the interface for TakeSubTransactions() changed? 177 LOG(("unexpected rv from nnsAHttpTransaction::TakeSubTransactions()")); 178 MOZ_ASSERT(false, 179 "unexpected result from " 180 "nsAHttpTransaction::TakeSubTransactions()"); 181 mTransaction->Close(NS_ERROR_ABORT); 182 return rv; 183 } 184 185 return rv; 186 } 187 188 void nsHttpConnection::ResetTransaction(RefPtr<nsAHttpTransaction>&& trans, 189 bool aForH2Proxy) { 190 MOZ_ASSERT(trans); 191 mSpdySession->SetConnection(trans->Connection()); 192 trans->SetConnection(nullptr); 193 trans->DoNotRemoveAltSvc(); 194 if (!aForH2Proxy) { 195 // Only do this when this is for websocket or webtransport over HTTP/2. 196 trans->SetResettingForTunnelConn(true); 197 } 198 if (trans->IsForFallback()) { 199 trans->InvokeCallback(); 200 trans->Close(NS_OK); 201 } else { 202 trans->Close(NS_ERROR_NET_RESET); 203 } 204 } 205 206 nsresult nsHttpConnection::MoveTransactionsToSpdy( 207 nsresult status, nsTArray<RefPtr<nsAHttpTransaction> >& list) { 208 if (NS_FAILED(status)) { // includes NS_ERROR_NOT_IMPLEMENTED 209 MOZ_ASSERT(list.IsEmpty(), "sub transaction list not empty"); 210 211 // If this transaction is used to drive websocket or webtransport, we reset 212 // it to put it in the pending queue. Once we know if the server supports 213 // websocket or not, the pending queue will be processed. 214 nsHttpTransaction* trans = mTransaction->QueryHttpTransaction(); 215 if (trans && (trans->IsWebsocketUpgrade() || trans->IsForWebTransport())) { 216 LOG( 217 ("nsHttpConnection resetting transaction for websocket or " 218 "webtransport upgrade")); 219 // websocket upgrade needs NonSticky for transaction reset 220 mTransaction->MakeNonSticky(); 221 ResetTransaction(std::move(mTransaction)); 222 mTransaction = nullptr; 223 return NS_OK; 224 } 225 226 // This is ok - treat mTransaction as a single real request. 227 // Wrap the old http transaction into the new spdy session 228 // as the first stream. 229 LOG( 230 ("nsHttpConnection::MoveTransactionsToSpdy moves single transaction %p " 231 "into SpdySession %p\n", 232 mTransaction.get(), mSpdySession.get())); 233 nsresult rv = AddTransaction(mTransaction, mPriority); 234 if (NS_FAILED(rv)) { 235 return rv; 236 } 237 } else { 238 int32_t count = list.Length(); 239 240 LOG( 241 ("nsHttpConnection::MoveTransactionsToSpdy moving transaction list " 242 "len=%d " 243 "into SpdySession %p\n", 244 count, mSpdySession.get())); 245 246 if (!count) { 247 mTransaction->Close(NS_ERROR_ABORT); 248 return NS_ERROR_ABORT; 249 } 250 251 for (int32_t index = 0; index < count; ++index) { 252 RefPtr<nsAHttpTransaction> transaction = list[index]; 253 nsHttpTransaction* trans = transaction->QueryHttpTransaction(); 254 if (trans && 255 (trans->IsWebsocketUpgrade() || trans->IsForWebTransport())) { 256 LOG( 257 ("nsHttpConnection resetting a transaction for websocket or " 258 "webtransport upgrade")); 259 // websocket upgrade needs NonSticky for transaction reset 260 transaction->MakeNonSticky(); 261 ResetTransaction(std::move(transaction)); 262 transaction = nullptr; 263 continue; 264 } 265 nsresult rv = AddTransaction(list[index], mPriority); 266 if (NS_FAILED(rv)) { 267 return rv; 268 } 269 } 270 } 271 272 return NS_OK; 273 } 274 275 void nsHttpConnection::Start0RTTSpdy(SpdyVersion spdyVersion) { 276 LOG(("nsHttpConnection::Start0RTTSpdy [this=%p]", this)); 277 278 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 279 280 mDid0RTTSpdy = true; 281 mUsingSpdyVersion = spdyVersion; 282 mEverUsedSpdy = true; 283 mSpdySession = 284 ASpdySession::NewSpdySession(spdyVersion, mSocketTransport, true); 285 286 if (mTransaction) { 287 nsTArray<RefPtr<nsAHttpTransaction> > list; 288 nsresult rv = TryTakeSubTransactions(list); 289 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) { 290 LOG( 291 ("nsHttpConnection::Start0RTTSpdy [this=%p] failed taking " 292 "subtransactions rv=%" PRIx32, 293 this, static_cast<uint32_t>(rv))); 294 return; 295 } 296 297 rv = MoveTransactionsToSpdy(rv, list); 298 if (NS_FAILED(rv)) { 299 LOG( 300 ("nsHttpConnection::Start0RTTSpdy [this=%p] failed moving " 301 "transactions rv=%" PRIx32, 302 this, static_cast<uint32_t>(rv))); 303 return; 304 } 305 } 306 307 mTransaction = mSpdySession; 308 } 309 310 void nsHttpConnection::StartSpdy(nsITLSSocketControl* sslControl, 311 SpdyVersion spdyVersion) { 312 LOG(("nsHttpConnection::StartSpdy [this=%p, mDid0RTTSpdy=%d]\n", this, 313 mDid0RTTSpdy)); 314 315 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 316 MOZ_ASSERT(!mSpdySession || mDid0RTTSpdy); 317 318 mUsingSpdyVersion = spdyVersion; 319 mEverUsedSpdy = true; 320 if (sslControl) { 321 sslControl->SetDenyClientCert(true); 322 } 323 324 if (!mDid0RTTSpdy) { 325 mSpdySession = 326 ASpdySession::NewSpdySession(spdyVersion, mSocketTransport, false); 327 } 328 329 if (!mReportedSpdy) { 330 mReportedSpdy = true; 331 // See bug 1797729. 332 // It's possible that we already have a HTTP/3 connection that can be 333 // coleased with this connection. We should avoid coalescing with the 334 // existing HTTP/3 connection if the transaction doesn't allow to use 335 // HTTP/3. 336 gHttpHandler->ConnMgr()->ReportSpdyConnection(this, true, 337 mTransactionDisallowHttp3); 338 } 339 340 // Setting the connection as reused allows some transactions that fail 341 // with NS_ERROR_NET_RESET to be restarted and SPDY uses that code 342 // to handle clean rejections (such as those that arrived after 343 // a server goaway was generated). 344 mIsReused = true; 345 346 // If mTransaction is a muxed object it might represent 347 // several requests. If so, we need to unpack that and 348 // pack them all into a new spdy session. 349 350 nsTArray<RefPtr<nsAHttpTransaction> > list; 351 nsresult status = NS_OK; 352 if (!mDid0RTTSpdy && mTransaction) { 353 status = TryTakeSubTransactions(list); 354 355 if (NS_FAILED(status) && status != NS_ERROR_NOT_IMPLEMENTED) { 356 return; 357 } 358 } 359 360 if (NeedSpdyTunnel()) { 361 LOG3( 362 ("nsHttpConnection::StartSpdy %p Connecting To a HTTP/2 " 363 "Proxy and Need Connect", 364 this)); 365 SetTunnelSetupDone(); 366 } 367 368 nsresult rv = NS_OK; 369 bool spdyProxy = mConnInfo->UsingHttpsProxy() && mConnInfo->UsingConnect() && 370 !mHasTLSTransportLayer; 371 if (spdyProxy) { 372 RefPtr<nsHttpConnectionInfo> wildCardProxyCi; 373 rv = mConnInfo->CreateWildCard(getter_AddRefs(wildCardProxyCi)); 374 MOZ_ASSERT(NS_SUCCEEDED(rv)); 375 gHttpHandler->ConnMgr()->MoveToWildCardConnEntry(mConnInfo, wildCardProxyCi, 376 this); 377 mConnInfo = wildCardProxyCi; 378 MOZ_ASSERT(mConnInfo); 379 } 380 381 if (!mDid0RTTSpdy && mTransaction) { 382 if (spdyProxy) { 383 if (NS_FAILED(status)) { 384 // proxy upgrade needs Restartable for transaction reset 385 // note that using NonSticky here won't work because it breaks 386 // netwerk/test/unit/test_websocket_server.js - h1 ws with h2 proxy 387 mTransaction->MakeRestartable(); 388 ResetTransaction(std::move(mTransaction), true); 389 mTransaction = nullptr; 390 } else { 391 for (auto trans : list) { 392 if (!mSpdySession->Connection()) { 393 mSpdySession->SetConnection(trans->Connection()); 394 } 395 trans->SetConnection(nullptr); 396 trans->DoNotRemoveAltSvc(); 397 trans->Close(NS_ERROR_NET_RESET); 398 } 399 } 400 } else { 401 rv = MoveTransactionsToSpdy(status, list); 402 if (NS_FAILED(rv)) { 403 return; 404 } 405 } 406 } 407 408 // Disable TCP Keepalives - use SPDY ping instead. 409 rv = DisableTCPKeepalives(); 410 if (NS_FAILED(rv)) { 411 LOG( 412 ("nsHttpConnection::StartSpdy [%p] DisableTCPKeepalives failed " 413 "rv[0x%" PRIx32 "]", 414 this, static_cast<uint32_t>(rv))); 415 } 416 417 mIdleTimeout = gHttpHandler->SpdyTimeout() * mDefaultTimeoutFactor; 418 419 mTransaction = mSpdySession; 420 421 if (mDontReuse) { 422 mSpdySession->DontReuse(); 423 } 424 } 425 426 void nsHttpConnection::PostProcessNPNSetup(bool handshakeSucceeded, 427 bool hasSecurityInfo, 428 bool earlyDataUsed) { 429 if (mTransaction) { 430 mTransaction->OnTransportStatus(mSocketTransport, 431 NS_NET_STATUS_TLS_HANDSHAKE_ENDED, 0); 432 } 433 434 // this is happening after the bootstrap was originally written to. so update 435 // it. 436 if (mTransaction && mTransaction->QueryNullTransaction() && 437 (mBootstrappedTimings.secureConnectionStart.IsNull() || 438 mBootstrappedTimings.tcpConnectEnd.IsNull())) { 439 mBootstrappedTimings.secureConnectionStart = 440 mTransaction->QueryNullTransaction()->GetSecureConnectionStart(); 441 mBootstrappedTimings.tcpConnectEnd = 442 mTransaction->QueryNullTransaction()->GetTcpConnectEnd(); 443 } 444 445 if (hasSecurityInfo) { 446 mBootstrappedTimings.connectEnd = TimeStamp::Now(); 447 } 448 449 if (earlyDataUsed) { 450 // Didn't get 0RTT OK, back out of the "attempting 0RTT" state 451 LOG(("nsHttpConnection::PostProcessNPNSetup [this=%p] 0rtt failed", this)); 452 if (mTransaction && NS_FAILED(mTransaction->Finish0RTT(true, true))) { 453 mTransaction->Close(NS_ERROR_NET_RESET); 454 } 455 mContentBytesWritten0RTT = 0; 456 if (mDid0RTTSpdy) { 457 Reset0RttForSpdy(); 458 } 459 } 460 461 if (hasSecurityInfo) { 462 // Telemetry for tls failure rate with and without esni; 463 bool echConfigUsed = false; 464 mSocketTransport->GetEchConfigUsed(&echConfigUsed); 465 glean::http::EchconfigSuccessRateLabel label = 466 echConfigUsed 467 ? (handshakeSucceeded 468 ? glean::http::EchconfigSuccessRateLabel::eEchconfigsucceeded 469 : glean::http::EchconfigSuccessRateLabel::eEchconfigfailed) 470 : (handshakeSucceeded ? glean::http::EchconfigSuccessRateLabel:: 471 eNoechconfigsucceeded 472 : glean::http::EchconfigSuccessRateLabel:: 473 eNoechconfigfailed); 474 glean::http::echconfig_success_rate.EnumGet(label).Add(); 475 } 476 } 477 478 void nsHttpConnection::Reset0RttForSpdy() { 479 // Reset the work done by Start0RTTSpdy 480 mUsingSpdyVersion = SpdyVersion::NONE; 481 mTransaction = nullptr; 482 mSpdySession = nullptr; 483 // We have to reset this here, just in case we end up starting spdy again, 484 // so it can actually do everything it needs to do. 485 mDid0RTTSpdy = false; 486 } 487 488 // called on the socket thread 489 nsresult nsHttpConnection::Activate(nsAHttpTransaction* trans, uint32_t caps, 490 int32_t pri) { 491 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 492 LOG1(("nsHttpConnection::Activate [this=%p trans=%p caps=%x]\n", this, trans, 493 caps)); 494 495 if (!mExperienced && !trans->IsNullTransaction()) { 496 mHasFirstHttpTransaction = true; 497 if (mTlsHandshaker->NPNComplete()) { 498 mExperienced = true; 499 } 500 if (mBootstrappedTimingsSet) { 501 mBootstrappedTimingsSet = false; 502 nsHttpTransaction* hTrans = trans->QueryHttpTransaction(); 503 if (hTrans) { 504 hTrans->BootstrapTimings(mBootstrappedTimings); 505 SetUrgentStartPreferred(hTrans->GetClassOfService().Flags() & 506 nsIClassOfService::UrgentStart); 507 } 508 } 509 mBootstrappedTimings = TimingStruct(); 510 } 511 512 if (caps & NS_HTTP_LARGE_KEEPALIVE) { 513 mDefaultTimeoutFactor = StaticPrefs::network_http_largeKeepaliveFactor(); 514 } 515 516 mTransactionCaps = caps; 517 mPriority = pri; 518 519 if (mHasFirstHttpTransaction && mExperienced) { 520 mHasFirstHttpTransaction = false; 521 mExperienceState |= ConnectionExperienceState::Experienced; 522 } 523 524 if (mTransaction && (mUsingSpdyVersion != SpdyVersion::NONE)) { 525 return AddTransaction(trans, pri); 526 } 527 528 NS_ENSURE_ARG_POINTER(trans); 529 NS_ENSURE_TRUE(!mTransaction, NS_ERROR_IN_PROGRESS); 530 531 // reset the read timers to wash away any idle time 532 mLastWriteTime = mLastReadTime = PR_IntervalNow(); 533 534 // Connection failures are Activated() just like regular transacions. 535 // If we don't have a confirmation of a connected socket then test it 536 // with a write() to get relevant error code. 537 if (NS_FAILED(mErrorBeforeConnect)) { 538 mSocketOutCondition = mErrorBeforeConnect; 539 mTransaction = trans; 540 CloseTransaction(mTransaction, mSocketOutCondition); 541 return mSocketOutCondition; 542 } 543 544 if (!mConnectedTransport) { 545 uint32_t count; 546 mSocketOutCondition = NS_ERROR_FAILURE; 547 if (mSocketOut) { 548 mSocketOutCondition = mSocketOut->Write("", 0, &count); 549 } 550 if (NS_FAILED(mSocketOutCondition) && 551 mSocketOutCondition != NS_BASE_STREAM_WOULD_BLOCK) { 552 LOG(("nsHttpConnection::Activate [this=%p] Bad Socket %" PRIx32 "\n", 553 this, static_cast<uint32_t>(mSocketOutCondition))); 554 mSocketOut->AsyncWait(nullptr, 0, 0, nullptr); 555 mTransaction = trans; 556 CloseTransaction(mTransaction, mSocketOutCondition); 557 return mSocketOutCondition; 558 } 559 } 560 561 // take ownership of the transaction 562 mTransaction = trans; 563 564 // check if this is LNA failure 565 // XXX - This check should be made earlier, possibly in the 566 // DnsAndConnectSocket code to avoid unnecessary operations 567 // See Bug 1968908 568 nsHttpTransaction* httpTrans = mTransaction->QueryHttpTransaction(); 569 570 if (httpTrans && httpTrans->Connection() && !mConnInfo->UsingProxy()) { 571 NetAddr peerAddr; 572 httpTrans->Connection()->GetPeerAddr(&peerAddr); 573 if (!httpTrans->AllowedToConnectToIpAddressSpace( 574 peerAddr.GetIpAddressSpace())) { 575 mSocketOutCondition = NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED; 576 CloseTransaction(mTransaction, mSocketOutCondition); 577 return mSocketOutCondition; 578 } 579 } 580 581 // Update security callbacks 582 nsCOMPtr<nsIInterfaceRequestor> callbacks; 583 trans->GetSecurityCallbacks(getter_AddRefs(callbacks)); 584 SetSecurityCallbacks(callbacks); 585 mTlsHandshaker->SetupSSL(mInSpdyTunnel, mForcePlainText); 586 if (mTlsHandshaker->NPNComplete()) { 587 // For non-HTTPS connection, change the state to TRANSFERING directly. 588 ChangeConnectionState(ConnectionState::TRANSFERING); 589 } else { 590 ChangeConnectionState(ConnectionState::TLS_HANDSHAKING); 591 } 592 593 nsCOMPtr<nsITLSSocketControl> tlsSocketControl; 594 if (NS_SUCCEEDED(mSocketTransport->GetTlsSocketControl( 595 getter_AddRefs(tlsSocketControl))) && 596 tlsSocketControl) { 597 tlsSocketControl->SetBrowserId(mTransaction->BrowserId()); 598 } 599 600 MOZ_ASSERT(!mIdleMonitoring, "Activating a connection with an Idle Monitor"); 601 mIdleMonitoring = false; 602 603 // set mKeepAlive according to what will be requested 604 mKeepAliveMask = mKeepAlive = (caps & NS_HTTP_ALLOW_KEEPALIVE); 605 606 mTransactionDisallowHttp3 |= (caps & NS_HTTP_DISALLOW_HTTP3); 607 608 // need to handle HTTP CONNECT tunnels if this is the first time if 609 // we are tunneling through a proxy 610 nsresult rv = CheckTunnelIsNeeded(mTransaction); 611 if (NS_FAILED(rv)) goto failed_activation; 612 613 // Clear the per activation counter 614 mCurrentBytesRead = 0; 615 616 // The overflow state is not needed between activations 617 mInputOverflow = nullptr; 618 619 mResponseTimeoutEnabled = gHttpHandler->ResponseTimeoutEnabled() && 620 mTransaction->ResponseTimeout() > 0 && 621 mTransaction->ResponseTimeoutEnabled(); 622 623 rv = StartShortLivedTCPKeepalives(); 624 if (NS_FAILED(rv)) { 625 LOG( 626 ("nsHttpConnection::Activate [%p] " 627 "StartShortLivedTCPKeepalives failed rv[0x%" PRIx32 "]", 628 this, static_cast<uint32_t>(rv))); 629 } 630 631 trans->OnActivated(); 632 633 rv = OnOutputStreamReady(mSocketOut); 634 635 if (NS_SUCCEEDED(rv) && mContinueHandshakeDone) { 636 mContinueHandshakeDone(); 637 } 638 mContinueHandshakeDone = nullptr; 639 640 failed_activation: 641 if (NS_FAILED(rv)) { 642 mTransaction = nullptr; 643 } 644 645 return rv; 646 } 647 648 nsresult nsHttpConnection::AddTransaction(nsAHttpTransaction* httpTransaction, 649 int32_t priority) { 650 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 651 MOZ_ASSERT(mSpdySession && (mUsingSpdyVersion != SpdyVersion::NONE), 652 "AddTransaction to live http connection without spdy/quic"); 653 654 // If this is a wild card nshttpconnection (i.e. a spdy proxy) then 655 // it is important to start the stream using the specific connection 656 // info of the transaction to ensure it is routed on the right tunnel 657 nsHttpTransaction* httpTrans = httpTransaction->QueryHttpTransaction(); 658 nsHttpConnectionInfo* transCI = httpTransaction->ConnectionInfo(); 659 if (httpTrans && !transCI->UsingProxy()) { 660 // this is a httptransaction object, being dispatched into a H2 session 661 // ensure it does not violate local network access. 662 NetAddr peerAddr; 663 if (NS_SUCCEEDED(GetPeerAddr(&peerAddr)) && 664 !httpTrans->AllowedToConnectToIpAddressSpace( 665 peerAddr.GetIpAddressSpace())) { 666 mSocketOutCondition = NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED; 667 CloseTransaction(httpTransaction, mSocketOutCondition); 668 httpTransaction->Close(mSocketOutCondition); 669 return mSocketOutCondition; 670 } 671 } 672 673 bool needTunnel = transCI->UsingHttpsProxy(); 674 needTunnel = needTunnel && !mHasTLSTransportLayer; 675 needTunnel = needTunnel && transCI->UsingConnect(); 676 needTunnel = needTunnel && httpTransaction->QueryHttpTransaction(); 677 678 // Let the transaction know that the tunnel is already established and we 679 // don't need to setup the tunnel again. 680 if (transCI->UsingConnect() && mEverUsedSpdy && mHasTLSTransportLayer) { 681 httpTransaction->OnProxyConnectComplete(200); 682 } 683 684 LOG(("nsHttpConnection::AddTransaction [this=%p] for %s%s", this, 685 mSpdySession ? "SPDY" : "QUIC", needTunnel ? " over tunnel" : "")); 686 687 if (mSpdySession) { 688 if (!mSpdySession->AddStream(httpTransaction, priority, mCallbacks)) { 689 MOZ_ASSERT(false); // this cannot happen! 690 httpTransaction->Close(NS_ERROR_ABORT); 691 return NS_ERROR_FAILURE; 692 } 693 } 694 695 (void)ResumeSend(); 696 return NS_OK; 697 } 698 699 nsresult nsHttpConnection::CreateTunnelStream( 700 nsAHttpTransaction* httpTransaction, HttpConnectionBase** aHttpConnection, 701 bool aIsExtendedCONNECT) { 702 if (!mSpdySession) { 703 return NS_ERROR_UNEXPECTED; 704 } 705 706 auto result = mSpdySession->CreateTunnelStream(httpTransaction, mCallbacks, 707 mRtt, aIsExtendedCONNECT); 708 if (result.isErr()) { 709 return result.unwrapErr(); 710 } 711 RefPtr<nsHttpConnection> conn = result.unwrap(); 712 713 // We need to store the refrence of the Http2Session in the tunneled 714 // connection, so when nsHttpConnection::DontReuse is called the Http2Session 715 // can't be reused. 716 if (aIsExtendedCONNECT) { 717 LOG( 718 ("nsHttpConnection::CreateTunnelStream %p Set h2 session %p to " 719 "tunneled conn %p", 720 this, mSpdySession.get(), conn.get())); 721 conn->mExtendedCONNECTHttp2Session = mSpdySession; 722 } 723 conn.forget(aHttpConnection); 724 return NS_OK; 725 } 726 727 void nsHttpConnection::Close(nsresult reason, bool aIsShutdown) { 728 LOG(("nsHttpConnection::Close [this=%p reason=%" PRIx32 729 " mExperienceState=%x]\n", 730 this, static_cast<uint32_t>(reason), 731 static_cast<uint32_t>(mExperienceState))); 732 733 if (mConnectionState != ConnectionState::CLOSED) { 734 RecordConnectionCloseTelemetry(reason); 735 ChangeConnectionState(ConnectionState::CLOSED); 736 } 737 738 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 739 mTlsHandshaker->NotifyClose(); 740 mContinueHandshakeDone = nullptr; 741 mExtendedCONNECTHttp2Session = nullptr; 742 // Ensure TCP keepalive timer is stopped. 743 if (mTCPKeepaliveTransitionTimer) { 744 mTCPKeepaliveTransitionTimer->Cancel(); 745 mTCPKeepaliveTransitionTimer = nullptr; 746 } 747 if (mForceSendTimer) { 748 mForceSendTimer->Cancel(); 749 mForceSendTimer = nullptr; 750 } 751 752 if (!mTrafficCategory.IsEmpty()) { 753 HttpTrafficAnalyzer* hta = gHttpHandler->GetHttpTrafficAnalyzer(); 754 if (hta) { 755 hta->IncrementHttpConnection(std::move(mTrafficCategory)); 756 MOZ_ASSERT(mTrafficCategory.IsEmpty()); 757 } 758 } 759 760 nsCOMPtr<nsITLSSocketControl> tlsSocketControl; 761 GetTLSSocketControl(getter_AddRefs(tlsSocketControl)); 762 if (tlsSocketControl) { 763 tlsSocketControl->SetHandshakeCallbackListener(nullptr); 764 } 765 766 if (NS_FAILED(reason)) { 767 if (mIdleMonitoring) EndIdleMonitoring(); 768 769 // The connection and security errors clear out alt-svc mappings 770 // in case any previously validated ones are now invalid 771 if (((reason == NS_ERROR_NET_RESET) || 772 (NS_ERROR_GET_MODULE(reason) == NS_ERROR_MODULE_SECURITY)) && 773 mConnInfo && !(mTransactionCaps & NS_HTTP_ERROR_SOFTLY)) { 774 gHttpHandler->ClearHostMapping(mConnInfo); 775 } 776 if (mTlsHandshaker->EarlyDataWasAvailable() && 777 PossibleZeroRTTRetryError(reason)) { 778 gHttpHandler->Exclude0RttTcp(mConnInfo); 779 } 780 781 if (mSocketTransport) { 782 mSocketTransport->SetEventSink(nullptr, nullptr); 783 784 // If there are bytes sitting in the input queue then read them 785 // into a junk buffer to avoid generating a tcp rst by closing a 786 // socket with data pending. TLS is a classic case of this where 787 // a Alert record might be superfulous to a clean HTTP/SPDY shutdown. 788 // Never block to do this and limit it to a small amount of data. 789 // During shutdown just be fast! 790 if (mSocketIn && !aIsShutdown && !mInSpdyTunnel) { 791 char buffer[4000]; 792 uint32_t count, total = 0; 793 nsresult rv; 794 do { 795 rv = mSocketIn->Read(buffer, 4000, &count); 796 if (NS_SUCCEEDED(rv)) total += count; 797 } while (NS_SUCCEEDED(rv) && count > 0 && total < 64000); 798 LOG(("nsHttpConnection::Close drained %d bytes\n", total)); 799 } 800 801 mSocketTransport->SetSecurityCallbacks(nullptr); 802 mSocketTransport->Close(reason); 803 if (mSocketOut) mSocketOut->AsyncWait(nullptr, 0, 0, nullptr); 804 } 805 mKeepAlive = false; 806 } 807 808 if (mConnInfo->GetIsTrrServiceChannel() && !mLastTRRResponseTime.IsNull() && 809 NS_SUCCEEDED(reason) && !aIsShutdown) { 810 // Record telemetry keyed by TRR provider. 811 glean::network::trr_idle_close_time_h1.Get(TRRProviderKey()) 812 .AccumulateRawDuration(TimeStamp::Now() - mLastTRRResponseTime); 813 } 814 } 815 816 void nsHttpConnection::MarkAsDontReuse() { 817 LOG(("nsHttpConnection::MarkAsDontReuse %p\n", this)); 818 mKeepAliveMask = false; 819 mKeepAlive = false; 820 mDontReuse = true; 821 mIdleTimeout = 0; 822 } 823 824 void nsHttpConnection::DontReuse() { 825 LOG(("nsHttpConnection::DontReuse %p spdysession=%p\n", this, 826 mSpdySession.get())); 827 MarkAsDontReuse(); 828 if (mSpdySession) { 829 mSpdySession->DontReuse(); 830 } else if (mExtendedCONNECTHttp2Session) { 831 LOG(("nsHttpConnection::DontReuse %p mExtendedCONNECTHttp2Session=%p\n", 832 this, mExtendedCONNECTHttp2Session.get())); 833 mExtendedCONNECTHttp2Session->DontReuse(); 834 } 835 } 836 837 bool nsHttpConnection::TestJoinConnection(const nsACString& hostname, 838 int32_t port) { 839 if (mSpdySession && CanDirectlyActivate()) { 840 return mSpdySession->TestJoinConnection(hostname, port); 841 } 842 843 return false; 844 } 845 846 bool nsHttpConnection::JoinConnection(const nsACString& hostname, 847 int32_t port) { 848 if (mSpdySession && CanDirectlyActivate()) { 849 return mSpdySession->JoinConnection(hostname, port); 850 } 851 852 return false; 853 } 854 855 bool nsHttpConnection::CanReuse() { 856 if (mDontReuse || !mRemainingConnectionUses) { 857 return false; 858 } 859 860 if ((mTransaction ? (mTransaction->IsDone() ? 0U : 1U) : 0U) >= 861 mRemainingConnectionUses) { 862 return false; 863 } 864 865 bool canReuse; 866 if (mSpdySession) { 867 canReuse = mSpdySession->CanReuse(); 868 } else { 869 canReuse = IsKeepAlive(); 870 } 871 872 canReuse = canReuse && (IdleTime() < mIdleTimeout) && IsAlive(); 873 874 // An idle persistent connection should not have data waiting to be read 875 // before a request is sent. Data here is likely a 408 timeout response 876 // which we would deal with later on through the restart logic, but that 877 // path is more expensive than just closing the socket now. 878 879 uint64_t dataSize; 880 if (canReuse && mSocketIn && (mUsingSpdyVersion == SpdyVersion::NONE) && 881 mHttp1xTransactionCount && 882 NS_SUCCEEDED(mSocketIn->Available(&dataSize)) && dataSize) { 883 LOG( 884 ("nsHttpConnection::CanReuse %p %s" 885 "Socket not reusable because read data pending (%" PRIu64 ") on it.\n", 886 this, mConnInfo->Origin(), dataSize)); 887 canReuse = false; 888 } 889 return canReuse; 890 } 891 892 bool nsHttpConnection::CanDirectlyActivate() { 893 // return true if a new transaction can be addded to ths connection at any 894 // time through Activate(). In practice this means this is a healthy SPDY 895 // connection with room for more concurrent streams. 896 897 return UsingSpdy() && CanReuse() && mSpdySession && 898 mSpdySession->RoomForMoreStreams(); 899 } 900 901 PRIntervalTime nsHttpConnection::IdleTime() { 902 return mSpdySession ? mSpdySession->IdleTime() 903 : (PR_IntervalNow() - mLastReadTime); 904 } 905 906 // returns the number of seconds left before the allowable idle period 907 // expires, or 0 if the period has already expied. 908 uint32_t nsHttpConnection::TimeToLive() { 909 LOG(("nsHttpConnection::TTL: %p %s idle %d timeout %d\n", this, 910 mConnInfo->Origin(), IdleTime(), mIdleTimeout)); 911 912 if (IdleTime() >= mIdleTimeout) { 913 return 0; 914 } 915 916 uint32_t timeToLive = PR_IntervalToSeconds(mIdleTimeout - IdleTime()); 917 918 // a positive amount of time can be rounded to 0. Because 0 is used 919 // as the expiration signal, round all values from 0 to 1 up to 1. 920 if (!timeToLive) { 921 timeToLive = 1; 922 } 923 return timeToLive; 924 } 925 926 bool nsHttpConnection::IsAlive() { 927 if (!mSocketTransport || !mConnectedTransport) return false; 928 929 // SocketTransport::IsAlive can run the SSL state machine, so make sure 930 // the NPN options are set before that happens. 931 mTlsHandshaker->SetupSSL(mInSpdyTunnel, mForcePlainText); 932 933 bool alive; 934 nsresult rv = mSocketTransport->IsAlive(&alive); 935 if (NS_FAILED(rv)) alive = false; 936 937 return alive; 938 } 939 940 void nsHttpConnection::SetUrgentStartPreferred(bool urgent) { 941 if (mExperienced && !mUrgentStartPreferredKnown) { 942 // Set only according the first ever dispatched non-null transaction 943 mUrgentStartPreferredKnown = true; 944 mUrgentStartPreferred = urgent; 945 LOG(("nsHttpConnection::SetUrgentStartPreferred [this=%p urgent=%d]", this, 946 urgent)); 947 } 948 } 949 950 //---------------------------------------------------------------------------- 951 // nsHttpConnection::nsAHttpConnection compatible methods 952 //---------------------------------------------------------------------------- 953 954 nsresult nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction* trans, 955 nsHttpRequestHead* requestHead, 956 nsHttpResponseHead* responseHead, 957 bool* reset) { 958 LOG( 959 ("nsHttpConnection::OnHeadersAvailable [this=%p trans=%p " 960 "response-head=%p]\n", 961 this, trans, responseHead)); 962 963 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 964 NS_ENSURE_ARG_POINTER(trans); 965 MOZ_ASSERT(responseHead, "No response head?"); 966 967 if (mInSpdyTunnel) { 968 DebugOnly<nsresult> rv = 969 responseHead->SetHeader(nsHttp::X_Firefox_Spdy_Proxy, "true"_ns); 970 MOZ_ASSERT(NS_SUCCEEDED(rv)); 971 } 972 973 // we won't change our keep-alive policy unless the server has explicitly 974 // told us to do so. 975 976 // inspect the connection headers for keep-alive info provided the 977 // transaction completed successfully. In the case of a non-sensical close 978 // and keep-alive favor the close out of conservatism. 979 980 bool explicitKeepAlive = false; 981 bool explicitClose = 982 responseHead->HasHeaderValue(nsHttp::Connection, "close") || 983 responseHead->HasHeaderValue(nsHttp::Proxy_Connection, "close"); 984 if (!explicitClose) { 985 explicitKeepAlive = 986 responseHead->HasHeaderValue(nsHttp::Connection, "keep-alive") || 987 responseHead->HasHeaderValue(nsHttp::Proxy_Connection, "keep-alive"); 988 } 989 990 // deal with 408 Server Timeouts 991 uint16_t responseStatus = responseHead->Status(); 992 if (responseStatus == 408) { 993 // timeouts that are not caused by persistent connection reuse should 994 // not be retried for browser compatibility reasons. bug 907800. The 995 // server driven close is implicit in the 408. 996 explicitClose = true; 997 explicitKeepAlive = false; 998 } 999 1000 if ((responseHead->Version() < HttpVersion::v1_1) || 1001 (requestHead->Version() < HttpVersion::v1_1)) { 1002 // HTTP/1.0 connections are by default NOT persistent 1003 mKeepAlive = explicitKeepAlive; 1004 } else { 1005 // HTTP/1.1 connections are by default persistent 1006 mKeepAlive = !explicitClose; 1007 } 1008 mKeepAliveMask = mKeepAlive; 1009 1010 // if this connection is persistent, then the server may send a "Keep-Alive" 1011 // header specifying the maximum number of times the connection can be 1012 // reused as well as the maximum amount of time the connection can be idle 1013 // before the server will close it. we ignore the max reuse count, because 1014 // a "keep-alive" connection is by definition capable of being reused, and 1015 // we only care about being able to reuse it once. if a timeout is not 1016 // specified then we use our advertized timeout value. 1017 bool foundKeepAliveMax = false; 1018 if (mKeepAlive) { 1019 nsAutoCString keepAlive; 1020 (void)responseHead->GetHeader(nsHttp::Keep_Alive, keepAlive); 1021 1022 if (mUsingSpdyVersion == SpdyVersion::NONE) { 1023 const char* cp = nsCRT::strcasestr(keepAlive.get(), "timeout="); 1024 if (cp) { 1025 mIdleTimeout = PR_SecondsToInterval((uint32_t)atoi(cp + 8)); 1026 } else { 1027 mIdleTimeout = gHttpHandler->IdleTimeout() * mDefaultTimeoutFactor; 1028 } 1029 1030 cp = nsCRT::strcasestr(keepAlive.get(), "max="); 1031 if (cp) { 1032 int maxUses = atoi(cp + 4); 1033 if (maxUses > 0) { 1034 foundKeepAliveMax = true; 1035 mRemainingConnectionUses = static_cast<uint32_t>(maxUses); 1036 } 1037 } 1038 } 1039 1040 LOG(("Connection can be reused [this=%p idle-timeout=%usec]\n", this, 1041 PR_IntervalToSeconds(mIdleTimeout))); 1042 } 1043 1044 if (!foundKeepAliveMax && mRemainingConnectionUses && 1045 (mUsingSpdyVersion == SpdyVersion::NONE)) { 1046 --mRemainingConnectionUses; 1047 } 1048 1049 switch (mState) { 1050 case HttpConnectionState::SETTING_UP_TUNNEL: { 1051 nsHttpTransaction* trans = mTransaction->QueryHttpTransaction(); 1052 // Distinguish SETTING_UP_TUNNEL for proxy or websocket via proxy 1053 // See bug 1848013. Do not call HandleTunnelResponse for a tunnel 1054 // connection created for WebSocket. 1055 if (trans && trans->IsWebsocketUpgrade() && 1056 (trans->GetProxyConnectResponseCode() == 200 || 1057 (mForWebSocket && mInSpdyTunnel))) { 1058 HandleWebSocketResponse(requestHead, responseHead, responseStatus); 1059 } else { 1060 HandleTunnelResponse(responseStatus, reset); 1061 } 1062 break; 1063 } 1064 default: 1065 if (requestHead->HasHeader(nsHttp::Upgrade)) { 1066 HandleWebSocketResponse(requestHead, responseHead, responseStatus); 1067 } else if (responseStatus == 101) { 1068 // We got an 101 but we are not asking of a WebSsocket? 1069 Close(NS_ERROR_ABORT); 1070 } 1071 } 1072 1073 mLastHttpResponseVersion = responseHead->Version(); 1074 1075 return NS_OK; 1076 } 1077 1078 void nsHttpConnection::HandleTunnelResponse(uint16_t responseStatus, 1079 bool* reset) { 1080 LOG(("nsHttpConnection::HandleTunnelResponse()")); 1081 MOZ_ASSERT(TunnelSetupInProgress()); 1082 MOZ_ASSERT(mProxyConnectStream); 1083 MOZ_ASSERT(mUsingSpdyVersion == SpdyVersion::NONE, 1084 "SPDY NPN Complete while using proxy connect stream"); 1085 // If we're doing a proxy connect, we need to check whether or not 1086 // it was successful. If so, we have to reset the transaction and step-up 1087 // the socket connection if using SSL. Finally, we have to wake up the 1088 // socket write request. 1089 1090 if (responseStatus == 200) { 1091 ChangeState(HttpConnectionState::REQUEST); 1092 } 1093 mProxyConnectStream = nullptr; 1094 bool isHttps = mTransaction ? mTransaction->ConnectionInfo()->EndToEndSSL() 1095 : mConnInfo->EndToEndSSL(); 1096 bool onlyConnect = mTransactionCaps & NS_HTTP_CONNECT_ONLY; 1097 1098 mTransaction->OnProxyConnectComplete(responseStatus); 1099 if (responseStatus == 200) { 1100 LOG(("proxy CONNECT succeeded! endtoendssl=%d onlyconnect=%d\n", isHttps, 1101 onlyConnect)); 1102 // If we're only connecting, we don't need to reset the transaction 1103 // state. We need to upgrade the socket now without doing the actual 1104 // http request. 1105 if (!onlyConnect) { 1106 *reset = true; 1107 } 1108 nsresult rv; 1109 if (isHttps) { 1110 bool skipSSL = false; 1111 if (mConnInfo->UsingHttpsProxy() || 1112 mTransactionCaps & NS_HTTP_TLS_TUNNEL) { 1113 LOG(("%p SetupSecondaryTLS %s %d\n", this, mConnInfo->Origin(), 1114 mConnInfo->OriginPort())); 1115 SetupSecondaryTLS(); 1116 } else if (onlyConnect) { 1117 MOZ_ASSERT(mConnInfo->UsingOnlyHttpProxy(), "Must be a HTTP proxy"); 1118 1119 // We have CONNECT only flag and a HTTP proxy is used here, so we can 1120 // just skip setting up SSL. We have to mark this as complete to finish 1121 // the transaction and be upgraded. 1122 mTlsHandshaker->SetNPNComplete(); 1123 skipSSL = true; 1124 } 1125 1126 if (!skipSSL) { 1127 rv = mTlsHandshaker->InitSSLParams(false, true); 1128 LOG(("InitSSLParams [rv=%" PRIx32 "]\n", static_cast<uint32_t>(rv))); 1129 } 1130 } 1131 rv = mSocketOut->AsyncWait(this, 0, 0, nullptr); 1132 // XXX what if this fails -- need to handle this error 1133 MOZ_ASSERT(NS_SUCCEEDED(rv), "mSocketOut->AsyncWait failed"); 1134 } else { 1135 LOG(("proxy CONNECT failed! endtoendssl=%d onlyconnect=%d\n", isHttps, 1136 onlyConnect)); 1137 mTransaction->SetProxyConnectFailed(); 1138 } 1139 } 1140 1141 void nsHttpConnection::HandleWebSocketResponse(nsHttpRequestHead* requestHead, 1142 nsHttpResponseHead* responseHead, 1143 uint16_t responseStatus) { 1144 LOG(("nsHttpConnection::HandleWebSocketResponse()")); 1145 1146 // Don't use persistent connection for Upgrade unless there's an auth failure: 1147 // some proxies expect to see auth response on persistent connection. 1148 // Also allow persistent conn for h2, as we don't want to waste connections 1149 // for multiplexed upgrades. 1150 if (responseStatus != 401 && responseStatus != 407 && !mSpdySession) { 1151 LOG(("HTTP Upgrade in play - disable keepalive for http/1.x\n")); 1152 MarkAsDontReuse(); 1153 } 1154 1155 // the new Http2StreamWebSocket breaks wpt on 1156 // h2 basic authentication 401, due to MakeSticky() work around 1157 // so we DontReuse() in this circumstance 1158 if (mInSpdyTunnel && (responseStatus == 401 || responseStatus == 407)) { 1159 MarkAsDontReuse(); 1160 return; 1161 } 1162 1163 if (responseStatus == 101) { 1164 nsAutoCString upgradeReq; 1165 bool hasUpgradeReq = 1166 NS_SUCCEEDED(requestHead->GetHeader(nsHttp::Upgrade, upgradeReq)); 1167 nsAutoCString upgradeResp; 1168 bool hasUpgradeResp = 1169 NS_SUCCEEDED(responseHead->GetHeader(nsHttp::Upgrade, upgradeResp)); 1170 if (!hasUpgradeReq || !hasUpgradeResp || 1171 !nsHttp::FindToken(upgradeResp.get(), upgradeReq.get(), 1172 HTTP_HEADER_VALUE_SEPS)) { 1173 LOG(("HTTP 101 Upgrade header mismatch req = %s, resp = %s\n", 1174 upgradeReq.get(), 1175 !upgradeResp.IsEmpty() ? upgradeResp.get() 1176 : "RESPONSE's nsHttp::Upgrade is empty")); 1177 Close(NS_ERROR_ABORT); 1178 } else { 1179 LOG(("HTTP Upgrade Response to %s\n", upgradeResp.get())); 1180 } 1181 } 1182 } 1183 1184 bool nsHttpConnection::IsReused() { 1185 if (mIsReused) return true; 1186 if (!mConsiderReusedAfterInterval) return false; 1187 1188 // ReusedAfter allows a socket to be consider reused only after a certain 1189 // interval of time has passed 1190 return (PR_IntervalNow() - mConsiderReusedAfterEpoch) >= 1191 mConsiderReusedAfterInterval; 1192 } 1193 1194 void nsHttpConnection::SetIsReusedAfter(uint32_t afterMilliseconds) { 1195 mConsiderReusedAfterEpoch = PR_IntervalNow(); 1196 mConsiderReusedAfterInterval = PR_MillisecondsToInterval(afterMilliseconds); 1197 } 1198 1199 nsresult nsHttpConnection::TakeTransport(nsISocketTransport** aTransport, 1200 nsIAsyncInputStream** aInputStream, 1201 nsIAsyncOutputStream** aOutputStream) { 1202 if (mUsingSpdyVersion != SpdyVersion::NONE) return NS_ERROR_FAILURE; 1203 if (mTransaction && !mTransaction->IsDone()) return NS_ERROR_IN_PROGRESS; 1204 if (!(mSocketTransport && mSocketIn && mSocketOut)) { 1205 return NS_ERROR_NOT_INITIALIZED; 1206 } 1207 1208 if (mInputOverflow) mSocketIn = mInputOverflow.forget(); 1209 1210 // Change TCP Keepalive frequency to long-lived if currently short-lived. 1211 if (mTCPKeepaliveConfig == kTCPKeepaliveShortLivedConfig) { 1212 if (mTCPKeepaliveTransitionTimer) { 1213 mTCPKeepaliveTransitionTimer->Cancel(); 1214 mTCPKeepaliveTransitionTimer = nullptr; 1215 } 1216 nsresult rv = StartLongLivedTCPKeepalives(); 1217 LOG( 1218 ("nsHttpConnection::TakeTransport [%p] calling " 1219 "StartLongLivedTCPKeepalives", 1220 this)); 1221 if (NS_FAILED(rv)) { 1222 LOG( 1223 ("nsHttpConnection::TakeTransport [%p] " 1224 "StartLongLivedTCPKeepalives failed rv[0x%" PRIx32 "]", 1225 this, static_cast<uint32_t>(rv))); 1226 } 1227 } 1228 1229 if (mHasTLSTransportLayer) { 1230 RefPtr<TLSTransportLayer> tlsTransportLayer = 1231 do_QueryObject(mSocketTransport); 1232 if (tlsTransportLayer) { 1233 // This transport layer is no longer owned by this connection. 1234 tlsTransportLayer->ReleaseOwner(); 1235 } 1236 } 1237 1238 mSocketTransport->SetSecurityCallbacks(nullptr); 1239 mSocketTransport->SetEventSink(nullptr, nullptr); 1240 1241 mSocketTransport.forget(aTransport); 1242 mSocketIn.forget(aInputStream); 1243 mSocketOut.forget(aOutputStream); 1244 1245 return NS_OK; 1246 } 1247 1248 uint32_t nsHttpConnection::ReadTimeoutTick(PRIntervalTime now) { 1249 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1250 1251 // make sure timer didn't tick before Activate() 1252 if (!mTransaction) return UINT32_MAX; 1253 1254 // Spdy implements some timeout handling using the SPDY ping frame. 1255 if (mSpdySession) { 1256 return mSpdySession->ReadTimeoutTick(now); 1257 } 1258 1259 uint32_t nextTickAfter = UINT32_MAX; 1260 // Timeout if the response is taking too long to arrive. 1261 if (mResponseTimeoutEnabled) { 1262 NS_WARNING_ASSERTION( 1263 gHttpHandler->ResponseTimeoutEnabled(), 1264 "Timing out a response, but response timeout is disabled!"); 1265 1266 PRIntervalTime initialResponseDelta = now - mLastWriteTime; 1267 1268 if (initialResponseDelta > mTransaction->ResponseTimeout()) { 1269 LOG(("canceling transaction: no response for %ums: timeout is %dms\n", 1270 PR_IntervalToMilliseconds(initialResponseDelta), 1271 PR_IntervalToMilliseconds(mTransaction->ResponseTimeout()))); 1272 1273 mResponseTimeoutEnabled = false; 1274 SetCloseReason(ConnectionCloseReason::IDLE_TIMEOUT); 1275 // This will also close the connection 1276 CloseTransaction(mTransaction, NS_ERROR_NET_TIMEOUT); 1277 return UINT32_MAX; 1278 } 1279 nextTickAfter = PR_IntervalToSeconds(mTransaction->ResponseTimeout()) - 1280 PR_IntervalToSeconds(initialResponseDelta); 1281 nextTickAfter = std::max(nextTickAfter, 1U); 1282 } 1283 1284 if (!mTlsHandshaker->NPNComplete()) { 1285 // We can reuse mLastWriteTime here, because it is set when the 1286 // connection is activated and only change when a transaction 1287 // succesfullu write to the socket and this can only happen after 1288 // the TLS handshake is done. 1289 PRIntervalTime initialTLSDelta = now - mLastWriteTime; 1290 if (initialTLSDelta > 1291 PR_MillisecondsToInterval(gHttpHandler->TLSHandshakeTimeout())) { 1292 LOG( 1293 ("canceling transaction: tls handshake takes too long: tls handshake " 1294 "last %ums, timeout is %dms.", 1295 PR_IntervalToMilliseconds(initialTLSDelta), 1296 gHttpHandler->TLSHandshakeTimeout())); 1297 1298 // This will also close the connection 1299 SetCloseReason(ConnectionCloseReason::TLS_TIMEOUT); 1300 CloseTransaction(mTransaction, NS_ERROR_NET_TIMEOUT); 1301 return UINT32_MAX; 1302 } 1303 } 1304 1305 return nextTickAfter; 1306 } 1307 1308 void nsHttpConnection::UpdateTCPKeepalive(nsITimer* aTimer, void* aClosure) { 1309 MOZ_ASSERT(aTimer); 1310 MOZ_ASSERT(aClosure); 1311 1312 nsHttpConnection* self = static_cast<nsHttpConnection*>(aClosure); 1313 1314 if (NS_WARN_IF(self->mUsingSpdyVersion != SpdyVersion::NONE)) { 1315 return; 1316 } 1317 1318 // Do not reduce keepalive probe frequency for idle connections. 1319 if (self->mIdleMonitoring) { 1320 return; 1321 } 1322 1323 nsresult rv = self->StartLongLivedTCPKeepalives(); 1324 if (NS_FAILED(rv)) { 1325 LOG( 1326 ("nsHttpConnection::UpdateTCPKeepalive [%p] " 1327 "StartLongLivedTCPKeepalives failed rv[0x%" PRIx32 "]", 1328 self, static_cast<uint32_t>(rv))); 1329 } 1330 } 1331 1332 void nsHttpConnection::GetTLSSocketControl( 1333 nsITLSSocketControl** tlsSocketControl) { 1334 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1335 LOG(("nsHttpConnection::GetTLSSocketControl trans=%p socket=%p\n", 1336 mTransaction.get(), mSocketTransport.get())); 1337 1338 *tlsSocketControl = nullptr; 1339 1340 if (mTransaction && NS_SUCCEEDED(mTransaction->GetTransactionTLSSocketControl( 1341 tlsSocketControl))) { 1342 return; 1343 } 1344 1345 if (mSocketTransport && 1346 NS_SUCCEEDED(mSocketTransport->GetTlsSocketControl(tlsSocketControl))) { 1347 return; 1348 } 1349 } 1350 1351 nsresult nsHttpConnection::PushBack(const char* data, uint32_t length) { 1352 LOG(("nsHttpConnection::PushBack [this=%p, length=%d]\n", this, length)); 1353 1354 if (mInputOverflow) { 1355 NS_ERROR("nsHttpConnection::PushBack only one buffer supported"); 1356 return NS_ERROR_UNEXPECTED; 1357 } 1358 1359 mInputOverflow = new nsPreloadedStream(mSocketIn, data, length); 1360 return NS_OK; 1361 } 1362 1363 class HttpConnectionForceIO : public Runnable { 1364 public: 1365 HttpConnectionForceIO(nsHttpConnection* aConn, bool doRecv) 1366 : Runnable("net::HttpConnectionForceIO"), mConn(aConn), mDoRecv(doRecv) {} 1367 1368 NS_IMETHOD Run() override { 1369 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1370 1371 if (mDoRecv) { 1372 if (!mConn->mSocketIn) return NS_OK; 1373 return mConn->OnInputStreamReady(mConn->mSocketIn); 1374 } 1375 1376 MOZ_ASSERT(mConn->mForceSendPending); 1377 mConn->mForceSendPending = false; 1378 1379 if (!mConn->mSocketOut) { 1380 return NS_OK; 1381 } 1382 return mConn->OnOutputStreamReady(mConn->mSocketOut); 1383 } 1384 1385 private: 1386 RefPtr<nsHttpConnection> mConn; 1387 bool mDoRecv; 1388 }; 1389 1390 nsresult nsHttpConnection::ResumeSend() { 1391 LOG(("nsHttpConnection::ResumeSend [this=%p]\n", this)); 1392 1393 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1394 1395 if (mSocketOut) { 1396 return mSocketOut->AsyncWait(this, 0, 0, nullptr); 1397 } 1398 1399 MOZ_ASSERT_UNREACHABLE("no socket output stream"); 1400 return NS_ERROR_UNEXPECTED; 1401 } 1402 1403 nsresult nsHttpConnection::ResumeRecv() { 1404 LOG(("nsHttpConnection::ResumeRecv [this=%p]\n", this)); 1405 1406 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1407 1408 // the mLastReadTime timestamp is used for finding slowish readers 1409 // and can be pretty sensitive. For that reason we actually reset it 1410 // when we ask to read (resume recv()) so that when we get called back 1411 // with actual read data in OnSocketReadable() we are only measuring 1412 // the latency between those two acts and not all the processing that 1413 // may get done before the ResumeRecv() call 1414 mLastReadTime = PR_IntervalNow(); 1415 1416 if (mSocketIn) { 1417 if (mHasTLSTransportLayer) { 1418 RefPtr<TLSTransportLayer> tlsTransportLayer = 1419 do_QueryObject(mSocketTransport); 1420 if (tlsTransportLayer) { 1421 bool hasDataToRecv = tlsTransportLayer->HasDataToRecv(); 1422 if (hasDataToRecv && NS_SUCCEEDED(ForceRecv())) { 1423 return NS_OK; 1424 } 1425 (void)mSocketIn->AsyncWait(this, 0, 0, nullptr); 1426 // We have to return an error here to let the underlying layer know this 1427 // connection doesn't read any data. 1428 return NS_BASE_STREAM_WOULD_BLOCK; 1429 } 1430 } 1431 return mSocketIn->AsyncWait(this, 0, 0, nullptr); 1432 } 1433 1434 MOZ_ASSERT_UNREACHABLE("no socket input stream"); 1435 return NS_ERROR_UNEXPECTED; 1436 } 1437 1438 void nsHttpConnection::ForceSendIO(nsITimer* aTimer, void* aClosure) { 1439 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1440 nsHttpConnection* self = static_cast<nsHttpConnection*>(aClosure); 1441 MOZ_ASSERT(aTimer == self->mForceSendTimer); 1442 self->mForceSendTimer = nullptr; 1443 NS_DispatchToCurrentThread(new HttpConnectionForceIO(self, false)); 1444 } 1445 1446 nsresult nsHttpConnection::MaybeForceSendIO() { 1447 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1448 // due to bug 1213084 sometimes real I/O events do not get serviced when 1449 // NSPR derived I/O events are ready and this can cause a deadlock with 1450 // https over https proxying. Normally we would expect the write callback to 1451 // be invoked before this timer goes off, but set it at the old windows 1452 // tick interval (kForceDelay) as a backup for those circumstances. 1453 static const uint32_t kForceDelay = 17; // ms 1454 1455 if (mForceSendPending) { 1456 return NS_OK; 1457 } 1458 MOZ_ASSERT(!mForceSendTimer); 1459 mForceSendPending = true; 1460 return NS_NewTimerWithFuncCallback( 1461 getter_AddRefs(mForceSendTimer), nsHttpConnection::ForceSendIO, this, 1462 kForceDelay, nsITimer::TYPE_ONE_SHOT, 1463 "net::nsHttpConnection::MaybeForceSendIO"_ns); 1464 } 1465 1466 // trigger an asynchronous read 1467 nsresult nsHttpConnection::ForceRecv() { 1468 LOG(("nsHttpConnection::ForceRecv [this=%p]\n", this)); 1469 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1470 1471 return NS_DispatchToCurrentThread(new HttpConnectionForceIO(this, true)); 1472 } 1473 1474 // trigger an asynchronous write 1475 nsresult nsHttpConnection::ForceSend() { 1476 LOG(("nsHttpConnection::ForceSend [this=%p]\n", this)); 1477 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1478 1479 return MaybeForceSendIO(); 1480 } 1481 1482 void nsHttpConnection::BeginIdleMonitoring() { 1483 LOG(("nsHttpConnection::BeginIdleMonitoring [this=%p]\n", this)); 1484 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1485 MOZ_ASSERT(!mTransaction, "BeginIdleMonitoring() while active"); 1486 MOZ_ASSERT(mUsingSpdyVersion == SpdyVersion::NONE, 1487 "Idle monitoring of spdy not allowed"); 1488 1489 LOG(("Entering Idle Monitoring Mode [this=%p]", this)); 1490 mIdleMonitoring = true; 1491 if (mSocketIn) mSocketIn->AsyncWait(this, 0, 0, nullptr); 1492 } 1493 1494 void nsHttpConnection::EndIdleMonitoring() { 1495 LOG(("nsHttpConnection::EndIdleMonitoring [this=%p]\n", this)); 1496 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1497 MOZ_ASSERT(!mTransaction, "EndIdleMonitoring() while active"); 1498 1499 if (mIdleMonitoring) { 1500 LOG(("Leaving Idle Monitoring Mode [this=%p]", this)); 1501 mIdleMonitoring = false; 1502 if (mSocketIn) mSocketIn->AsyncWait(nullptr, 0, 0, nullptr); 1503 } 1504 } 1505 1506 HttpVersion nsHttpConnection::Version() { 1507 if (mUsingSpdyVersion != SpdyVersion::NONE) { 1508 return HttpVersion::v2_0; 1509 } 1510 return mLastHttpResponseVersion; 1511 } 1512 1513 PRIntervalTime nsHttpConnection::LastWriteTime() { return mLastWriteTime; } 1514 1515 //----------------------------------------------------------------------------- 1516 // nsHttpConnection <private> 1517 //----------------------------------------------------------------------------- 1518 1519 void nsHttpConnection::CloseTransaction(nsAHttpTransaction* trans, 1520 nsresult reason, bool aIsShutdown) { 1521 LOG(("nsHttpConnection::CloseTransaction[this=%p trans=%p reason=%" PRIx32 1522 "]\n", 1523 this, trans, static_cast<uint32_t>(reason))); 1524 1525 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1526 1527 if (mCurrentBytesRead > mMaxBytesRead) mMaxBytesRead = mCurrentBytesRead; 1528 1529 // mask this error code because its not a real error. 1530 if (reason == NS_BASE_STREAM_CLOSED) reason = NS_OK; 1531 1532 if (mUsingSpdyVersion != SpdyVersion::NONE) { 1533 DontReuse(); 1534 // if !mSpdySession then mUsingSpdyVersion must be false for canreuse() 1535 mSpdySession->SetCleanShutdown(aIsShutdown); 1536 mUsingSpdyVersion = SpdyVersion::NONE; 1537 mSpdySession = nullptr; 1538 } 1539 1540 if ((NS_SUCCEEDED(reason) || NS_BASE_STREAM_CLOSED == reason) && 1541 trans->ConnectionInfo() && 1542 trans->ConnectionInfo()->GetIsTrrServiceChannel()) { 1543 // save time of last successful response 1544 mLastTRRResponseTime = TimeStamp::Now(); 1545 } 1546 1547 if (mTransaction) { 1548 LOG((" closing associated mTransaction")); 1549 if (NS_SUCCEEDED(reason)) { 1550 mHttp1xTransactionCount += mTransaction->Http1xTransactionCount(); 1551 } 1552 1553 mTransaction->Close(reason); 1554 mTransaction = nullptr; 1555 } 1556 1557 { 1558 MutexAutoLock lock(mCallbacksLock); 1559 mCallbacks = nullptr; 1560 } 1561 1562 if (NS_FAILED(reason) && (reason != NS_BINDING_RETARGETED)) { 1563 Close(reason, aIsShutdown); 1564 } 1565 1566 // flag the connection as reused here for convenience sake. certainly 1567 // it might be going away instead ;-) 1568 mIsReused = true; 1569 } 1570 1571 bool nsHttpConnection::CheckCanWrite0RTTData() { 1572 MOZ_ASSERT(mTlsHandshaker->EarlyDataAvailable()); 1573 nsCOMPtr<nsITLSSocketControl> tlsSocketControl; 1574 GetTLSSocketControl(getter_AddRefs(tlsSocketControl)); 1575 if (!tlsSocketControl) { 1576 return false; 1577 } 1578 nsCOMPtr<nsITransportSecurityInfo> securityInfo; 1579 if (NS_FAILED( 1580 tlsSocketControl->GetSecurityInfo(getter_AddRefs(securityInfo)))) { 1581 return false; 1582 } 1583 if (!securityInfo) { 1584 return false; 1585 } 1586 nsAutoCString negotiatedNPN; 1587 // If the following code fails means that the handshake is not done 1588 // yet, so continue writing 0RTT data. 1589 nsresult rv = securityInfo->GetNegotiatedNPN(negotiatedNPN); 1590 if (NS_FAILED(rv)) { 1591 return true; 1592 } 1593 bool earlyDataAccepted = false; 1594 rv = tlsSocketControl->GetEarlyDataAccepted(&earlyDataAccepted); 1595 // If 0RTT data is accepted we can continue writing data, 1596 // if it is reject stop writing more data. 1597 return NS_SUCCEEDED(rv) && earlyDataAccepted; 1598 } 1599 1600 nsresult nsHttpConnection::OnReadSegment(const char* buf, uint32_t count, 1601 uint32_t* countRead) { 1602 LOG(("nsHttpConnection::OnReadSegment [this=%p]\n", this)); 1603 if (count == 0) { 1604 // some ReadSegments implementations will erroneously call the writer 1605 // to consume 0 bytes worth of data. we must protect against this case 1606 // or else we'd end up closing the socket prematurely. 1607 NS_ERROR("bad ReadSegments implementation"); 1608 return NS_ERROR_FAILURE; // stop iterating 1609 } 1610 1611 // If we are waiting for 0RTT Response, check maybe nss has finished 1612 // handshake already. 1613 // IsAlive() calls drive the handshake and that may cause nss and necko 1614 // to be out of sync. 1615 if (mTlsHandshaker->EarlyDataAvailable() && !CheckCanWrite0RTTData()) { 1616 MOZ_DIAGNOSTIC_ASSERT(mTlsHandshaker->TlsHandshakeComplitionPending()); 1617 LOG( 1618 ("nsHttpConnection::OnReadSegment Do not write any data, wait" 1619 " for EnsureNPNComplete to be called [this=%p]", 1620 this)); 1621 *countRead = 0; 1622 return NS_BASE_STREAM_WOULD_BLOCK; 1623 } 1624 1625 nsresult rv = mSocketOut->Write(buf, count, countRead); 1626 if (NS_FAILED(rv)) { 1627 mSocketOutCondition = rv; 1628 } else if (*countRead == 0) { 1629 mSocketOutCondition = NS_BASE_STREAM_CLOSED; 1630 } else { 1631 mLastWriteTime = PR_IntervalNow(); 1632 mSocketOutCondition = NS_OK; // reset condition 1633 if (!TunnelSetupInProgress()) { 1634 mTotalBytesWritten += *countRead; 1635 mExperienceState |= ConnectionExperienceState::First_Request_Sent; 1636 } 1637 } 1638 1639 return mSocketOutCondition; 1640 } 1641 1642 nsresult nsHttpConnection::OnSocketWritable() { 1643 LOG(("nsHttpConnection::OnSocketWritable [this=%p] host=%s\n", this, 1644 mConnInfo->Origin())); 1645 1646 nsresult rv; 1647 uint32_t transactionBytes; 1648 bool again = true; 1649 1650 // Prevent STS thread from being blocked by single OnOutputStreamReady 1651 // callback. 1652 const uint32_t maxWriteAttempts = 128; 1653 uint32_t writeAttempts = 0; 1654 1655 if (mTransactionCaps & NS_HTTP_CONNECT_ONLY) { 1656 if (!mConnInfo->UsingConnect()) { 1657 // A CONNECT has been requested for this connection but will never 1658 // be performed. This should never happen. 1659 MOZ_ASSERT(false, "proxy connect will never happen"); 1660 LOG(("return failure because proxy connect will never happen\n")); 1661 return NS_ERROR_FAILURE; 1662 } 1663 1664 if (mState == HttpConnectionState::REQUEST && 1665 mTlsHandshaker->EnsureNPNComplete()) { 1666 // Don't need to check this each write attempt since it is only 1667 // updated after OnSocketWritable completes. 1668 // We've already done primary tls (if needed) and sent our CONNECT. 1669 // If we're doing a CONNECT only request there's no need to write 1670 // the http transaction. 1671 LOG(("return NS_BASE_STREAM_CLOSED to make transaction closed\n")); 1672 return NS_BASE_STREAM_CLOSED; 1673 } 1674 } 1675 1676 do { 1677 ++writeAttempts; 1678 rv = mSocketOutCondition = NS_OK; 1679 transactionBytes = 0; 1680 1681 switch (mState) { 1682 case HttpConnectionState::SETTING_UP_TUNNEL: 1683 if (mConnInfo->UsingHttpsProxy() && 1684 !mTlsHandshaker->EnsureNPNComplete()) { 1685 MOZ_DIAGNOSTIC_ASSERT(!mTlsHandshaker->EarlyDataAvailable()); 1686 mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK; 1687 } else { 1688 rv = SendConnectRequest(this, &transactionBytes); 1689 } 1690 break; 1691 default: { 1692 // The SSL handshake must be completed before the 1693 // transaction->readsegments() processing can proceed because we need to 1694 // know how to format the request differently for http/1, http/2, spdy, 1695 // etc.. and that is negotiated with NPN/ALPN in the SSL handshake. 1696 if (!mTlsHandshaker->EnsureNPNComplete() && 1697 (!mTlsHandshaker->EarlyDataUsed() || 1698 mTlsHandshaker->TlsHandshakeComplitionPending())) { 1699 // The handshake is not done and we cannot write 0RTT data or nss has 1700 // already finished 0RTT data. 1701 mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK; 1702 } else if (!mTransaction) { 1703 rv = NS_ERROR_FAILURE; 1704 LOG((" No Transaction In OnSocketWritable\n")); 1705 } else if (NS_SUCCEEDED(rv)) { 1706 // for non spdy sessions let the connection manager know 1707 if (!mReportedSpdy && mTlsHandshaker->NPNComplete()) { 1708 mReportedSpdy = true; 1709 MOZ_ASSERT(!mEverUsedSpdy); 1710 gHttpHandler->ConnMgr()->ReportSpdyConnection(this, false, false); 1711 } 1712 1713 LOG((" writing transaction request stream\n")); 1714 RefPtr<nsAHttpTransaction> transaction = mTransaction; 1715 rv = transaction->ReadSegmentsAgain(this, 1716 nsIOService::gDefaultSegmentSize, 1717 &transactionBytes, &again); 1718 if (mTlsHandshaker->EarlyDataUsed()) { 1719 mContentBytesWritten0RTT += transactionBytes; 1720 if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) { 1721 // If an error happens while writting 0RTT data, restart 1722 // the transactiions without 0RTT. 1723 mTlsHandshaker->FinishNPNSetup(false, true); 1724 } 1725 } else { 1726 mContentBytesWritten += transactionBytes; 1727 } 1728 } 1729 } 1730 } 1731 1732 LOG( 1733 ("nsHttpConnection::OnSocketWritable %p " 1734 "ReadSegments returned [rv=%" PRIx32 " read=%u " 1735 "sock-cond=%" PRIx32 " again=%d]\n", 1736 this, static_cast<uint32_t>(rv), transactionBytes, 1737 static_cast<uint32_t>(mSocketOutCondition), again)); 1738 1739 // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF. 1740 if (rv == NS_BASE_STREAM_CLOSED && 1741 (mTransaction && !mTransaction->IsDone())) { 1742 rv = NS_OK; 1743 transactionBytes = 0; 1744 } 1745 1746 if (NS_FAILED(rv)) { 1747 // if the transaction didn't want to write any more data, then 1748 // wait for the transaction to call ResumeSend. 1749 if (rv == NS_BASE_STREAM_WOULD_BLOCK) { 1750 rv = NS_OK; 1751 } 1752 again = false; 1753 } else if (NS_FAILED(mSocketOutCondition)) { 1754 if (mSocketOutCondition == NS_BASE_STREAM_WOULD_BLOCK) { 1755 if (!mTlsHandshaker->EarlyDataCanNotBeUsed()) { 1756 // continue writing 1757 // We are not going to poll for write if the handshake is in progress, 1758 // but early data cannot be used. 1759 rv = mSocketOut->AsyncWait(this, 0, 0, nullptr); 1760 } 1761 } else { 1762 rv = mSocketOutCondition; 1763 } 1764 again = false; 1765 } else if (!transactionBytes) { 1766 rv = NS_OK; 1767 1768 if (mTransaction) { // in case the ReadSegments stack called 1769 // CloseTransaction() 1770 // 1771 // at this point we've written out the entire transaction, and now we 1772 // must wait for the server's response. we manufacture a status message 1773 // here to reflect the fact that we are waiting. this message will be 1774 // trumped (overwritten) if the server responds quickly. 1775 // 1776 mTransaction->OnTransportStatus(mSocketTransport, 1777 NS_NET_STATUS_WAITING_FOR, 0); 1778 1779 rv = ResumeRecv(); // start reading 1780 } 1781 // When Spdy tunnel is used we need to explicitly set when a request is 1782 // done. 1783 if ((mState != HttpConnectionState::SETTING_UP_TUNNEL) && !mSpdySession) { 1784 nsHttpTransaction* trans = 1785 mTransaction ? mTransaction->QueryHttpTransaction() : nullptr; 1786 // needed for websocket over h2 (direct) 1787 if (!trans || 1788 (!trans->IsWebsocketUpgrade() && !trans->IsForWebTransport())) { 1789 mRequestDone = true; 1790 } 1791 } 1792 again = false; 1793 } else if (writeAttempts >= maxWriteAttempts) { 1794 LOG((" yield for other transactions\n")); 1795 rv = mSocketOut->AsyncWait(this, 0, 0, nullptr); // continue writing 1796 again = false; 1797 } 1798 // write more to the socket until error or end-of-request... 1799 } while (again && gHttpHandler->Active()); 1800 1801 return rv; 1802 } 1803 1804 nsresult nsHttpConnection::OnWriteSegment(char* buf, uint32_t count, 1805 uint32_t* countWritten) { 1806 if (count == 0) { 1807 // some WriteSegments implementations will erroneously call the reader 1808 // to provide 0 bytes worth of data. we must protect against this case 1809 // or else we'd end up closing the socket prematurely. 1810 NS_ERROR("bad WriteSegments implementation"); 1811 return NS_ERROR_FAILURE; // stop iterating 1812 } 1813 1814 if (ChaosMode::isActive(ChaosFeature::IOAmounts) && 1815 ChaosMode::randomUint32LessThan(2)) { 1816 // read 1...count bytes 1817 count = ChaosMode::randomUint32LessThan(count) + 1; 1818 } 1819 1820 nsresult rv = mSocketIn->Read(buf, count, countWritten); 1821 if (NS_FAILED(rv)) { 1822 mSocketInCondition = rv; 1823 } else if (*countWritten == 0) { 1824 mSocketInCondition = NS_BASE_STREAM_CLOSED; 1825 } else { 1826 mSocketInCondition = NS_OK; // reset condition 1827 mExperienceState |= ConnectionExperienceState::First_Response_Received; 1828 } 1829 1830 return mSocketInCondition; 1831 } 1832 1833 nsresult nsHttpConnection::OnSocketReadable() { 1834 LOG(("nsHttpConnection::OnSocketReadable [this=%p]\n", this)); 1835 1836 PRIntervalTime now = PR_IntervalNow(); 1837 PRIntervalTime delta = now - mLastReadTime; 1838 1839 // Reset mResponseTimeoutEnabled to stop response timeout checks. 1840 mResponseTimeoutEnabled = false; 1841 1842 if ((mTransactionCaps & NS_HTTP_CONNECT_ONLY) && !mConnInfo->UsingConnect()) { 1843 // A CONNECT has been requested for this connection but will never 1844 // be performed. This should never happen. 1845 MOZ_ASSERT(false, "proxy connect will never happen"); 1846 LOG(("return failure because proxy connect will never happen\n")); 1847 return NS_ERROR_FAILURE; 1848 } 1849 1850 if (mKeepAliveMask && (delta >= mMaxHangTime)) { 1851 LOG(("max hang time exceeded!\n")); 1852 // give the handler a chance to create a new persistent connection to 1853 // this host if we've been busy for too long. 1854 mKeepAliveMask = false; 1855 (void)gHttpHandler->ProcessPendingQ(mConnInfo); 1856 } 1857 1858 // Reduce the estimate of the time since last read by up to 1 RTT to 1859 // accommodate exhausted sender TCP congestion windows or minor I/O delays. 1860 mLastReadTime = now; 1861 1862 nsresult rv = NS_OK; 1863 uint32_t n; 1864 bool again = true; 1865 1866 do { 1867 if (!TunnelSetupInProgress() && !mTlsHandshaker->EnsureNPNComplete()) { 1868 // Unless we are setting up a tunnel via CONNECT, prevent reading 1869 // from the socket until the results of NPN 1870 // negotiation are known (which is determined from the write path). 1871 // If the server speaks SPDY it is likely the readable data here is 1872 // a spdy settings frame and without NPN it would be misinterpreted 1873 // as HTTP/* 1874 1875 LOG( 1876 ("nsHttpConnection::OnSocketReadable %p return due to inactive " 1877 "tunnel setup but incomplete NPN state\n", 1878 this)); 1879 if (mTlsHandshaker->EarlyDataAvailable() || mHasTLSTransportLayer) { 1880 rv = ResumeRecv(); 1881 } 1882 break; 1883 } 1884 1885 mSocketInCondition = NS_OK; 1886 if (!mTransaction) { 1887 rv = NS_ERROR_FAILURE; 1888 LOG((" No Transaction In OnSocketWritable\n")); 1889 } else { 1890 RefPtr<nsAHttpTransaction> transaction = mTransaction; 1891 rv = transaction->WriteSegmentsAgain( 1892 this, nsIOService::gDefaultSegmentSize, &n, &again); 1893 } 1894 LOG(("nsHttpConnection::OnSocketReadable %p trans->ws rv=%" PRIx32 1895 " n=%d socketin=%" PRIx32 "\n", 1896 this, static_cast<uint32_t>(rv), n, 1897 static_cast<uint32_t>(mSocketInCondition))); 1898 if (NS_FAILED(rv)) { 1899 // if the transaction didn't want to take any more data, then 1900 // wait for the transaction to call ResumeRecv. 1901 if (rv == NS_BASE_STREAM_WOULD_BLOCK) { 1902 rv = NS_OK; 1903 } 1904 again = false; 1905 } else { 1906 mCurrentBytesRead += n; 1907 mTotalBytesRead += n; 1908 if (NS_FAILED(mSocketInCondition)) { 1909 // continue waiting for the socket if necessary... 1910 if (mSocketInCondition == NS_BASE_STREAM_WOULD_BLOCK) { 1911 rv = ResumeRecv(); 1912 } else { 1913 rv = mSocketInCondition; 1914 } 1915 again = false; 1916 } 1917 } 1918 // read more from the socket until error... 1919 } while (again && gHttpHandler->Active()); 1920 1921 return rv; 1922 } 1923 1924 void nsHttpConnection::SetupSecondaryTLS() { 1925 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1926 MOZ_ASSERT(!mHasTLSTransportLayer); 1927 LOG(("nsHttpConnection %p SetupSecondaryTLS %s %d\n", this, 1928 mConnInfo->Origin(), mConnInfo->OriginPort())); 1929 1930 nsHttpConnectionInfo* ci = nullptr; 1931 if (mTransaction) { 1932 ci = mTransaction->ConnectionInfo(); 1933 } 1934 if (!ci) { 1935 ci = mConnInfo; 1936 } 1937 MOZ_ASSERT(ci); 1938 1939 RefPtr<TLSTransportLayer> transportLayer = 1940 new TLSTransportLayer(mSocketTransport, mSocketIn, mSocketOut, this); 1941 if (transportLayer->Init(ci->Origin(), ci->OriginPort())) { 1942 mSocketIn = transportLayer->GetInputStreamWrapper(); 1943 mSocketOut = transportLayer->GetOutputStreamWrapper(); 1944 mSocketTransport = transportLayer; 1945 mHasTLSTransportLayer = true; 1946 LOG(("Create mTLSTransportLayer %p", this)); 1947 } 1948 } 1949 1950 void nsHttpConnection::SetInTunnel() { 1951 mInSpdyTunnel = true; 1952 mForcePlainText = true; 1953 } 1954 1955 // static 1956 nsresult nsHttpConnection::MakeConnectString(nsAHttpTransaction* trans, 1957 nsHttpRequestHead* request, 1958 nsACString& result, bool h2ws, 1959 bool aShouldResistFingerprinting) { 1960 result.Truncate(); 1961 if (!trans->ConnectionInfo()) { 1962 return NS_ERROR_NOT_INITIALIZED; 1963 } 1964 1965 DebugOnly<nsresult> rv{}; 1966 1967 rv = nsHttpHandler::GenerateHostPort( 1968 nsDependentCString(trans->ConnectionInfo()->Origin()), 1969 trans->ConnectionInfo()->OriginPort(), result); 1970 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1971 1972 // CONNECT host:port HTTP/1.1 1973 request->SetMethod("CONNECT"_ns); 1974 request->SetVersion(gHttpHandler->HttpVersion()); 1975 if (h2ws) { 1976 // HTTP/2 websocket CONNECT forms need the full request URI 1977 nsAutoCString requestURI; 1978 trans->RequestHead()->RequestURI(requestURI); 1979 request->SetRequestURI(requestURI); 1980 1981 request->SetHTTPS(trans->RequestHead()->IsHTTPS()); 1982 1983 nsAutoCString val; 1984 if (NS_SUCCEEDED(trans->RequestHead()->GetHeader( 1985 nsHttp::Sec_WebSocket_Extensions, val))) { 1986 rv = request->SetHeader(nsHttp::Sec_WebSocket_Extensions, val); 1987 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1988 } 1989 if (NS_SUCCEEDED(trans->RequestHead()->GetHeader( 1990 nsHttp::Sec_WebSocket_Protocol, val))) { 1991 rv = request->SetHeader(nsHttp::Sec_WebSocket_Protocol, val); 1992 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1993 } 1994 if (NS_SUCCEEDED(trans->RequestHead()->GetHeader( 1995 nsHttp::Sec_WebSocket_Version, val))) { 1996 rv = request->SetHeader(nsHttp::Sec_WebSocket_Version, val); 1997 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1998 } 1999 } else { 2000 request->SetRequestURI(result); 2001 } 2002 rv = request->SetHeader(nsHttp::User_Agent, 2003 gHttpHandler->UserAgent(aShouldResistFingerprinting)); 2004 MOZ_ASSERT(NS_SUCCEEDED(rv)); 2005 2006 // a CONNECT is always persistent 2007 rv = request->SetHeader(nsHttp::Proxy_Connection, "keep-alive"_ns); 2008 MOZ_ASSERT(NS_SUCCEEDED(rv)); 2009 rv = request->SetHeader(nsHttp::Connection, "keep-alive"_ns); 2010 MOZ_ASSERT(NS_SUCCEEDED(rv)); 2011 2012 // all HTTP/1.1 requests must include a Host header (even though it 2013 // may seem redundant in this case; see bug 82388). 2014 rv = request->SetHeader(nsHttp::Host, result); 2015 MOZ_ASSERT(NS_SUCCEEDED(rv)); 2016 2017 nsAutoCString val; 2018 if (NS_SUCCEEDED( 2019 trans->RequestHead()->GetHeader(nsHttp::Proxy_Authorization, val))) { 2020 // we don't know for sure if this authorization is intended for the 2021 // SSL proxy, so we add it just in case. 2022 rv = request->SetHeader(nsHttp::Proxy_Authorization, val); 2023 MOZ_ASSERT(NS_SUCCEEDED(rv)); 2024 } 2025 if ((trans->Caps() & NS_HTTP_CONNECT_ONLY) && 2026 NS_SUCCEEDED(trans->RequestHead()->GetHeader(nsHttp::Upgrade, val))) { 2027 // rfc7639 proposes using the ALPN header to indicate the protocol used 2028 // in CONNECT when not used for TLS. The protocol is stored in Upgrade. 2029 // We have to copy this header here since a new HEAD request is created 2030 // for the CONNECT. 2031 rv = request->SetHeader("ALPN"_ns, val); 2032 MOZ_ASSERT(NS_SUCCEEDED(rv)); 2033 } 2034 2035 result.Truncate(); 2036 request->Flatten(result, false); 2037 2038 if (LOG1_ENABLED()) { 2039 LOG(("nsHttpConnection::MakeConnectString for transaction=%p h2ws=%d[", 2040 trans->QueryHttpTransaction(), h2ws)); 2041 LogHeaders(result.BeginReading()); 2042 LOG(("]")); 2043 } 2044 2045 result.AppendLiteral("\r\n"); 2046 return NS_OK; 2047 } 2048 2049 nsresult nsHttpConnection::StartShortLivedTCPKeepalives() { 2050 if (mUsingSpdyVersion != SpdyVersion::NONE) { 2051 return NS_OK; 2052 } 2053 MOZ_ASSERT(mSocketTransport); 2054 if (!mSocketTransport) { 2055 return NS_ERROR_NOT_INITIALIZED; 2056 } 2057 2058 nsresult rv = NS_OK; 2059 int32_t idleTimeS = -1; 2060 int32_t retryIntervalS = -1; 2061 if (gHttpHandler->TCPKeepaliveEnabledForShortLivedConns()) { 2062 // Set the idle time. 2063 idleTimeS = gHttpHandler->GetTCPKeepaliveShortLivedIdleTime(); 2064 LOG( 2065 ("nsHttpConnection::StartShortLivedTCPKeepalives[%p] " 2066 "idle time[%ds].", 2067 this, idleTimeS)); 2068 2069 retryIntervalS = std::max<int32_t>((int32_t)PR_IntervalToSeconds(mRtt), 1); 2070 rv = mSocketTransport->SetKeepaliveVals(idleTimeS, retryIntervalS); 2071 if (NS_FAILED(rv)) { 2072 return rv; 2073 } 2074 rv = mSocketTransport->SetKeepaliveEnabled(true); 2075 mTCPKeepaliveConfig = kTCPKeepaliveShortLivedConfig; 2076 } else { 2077 rv = mSocketTransport->SetKeepaliveEnabled(false); 2078 mTCPKeepaliveConfig = kTCPKeepaliveDisabled; 2079 } 2080 if (NS_FAILED(rv)) { 2081 return rv; 2082 } 2083 2084 // Start a timer to move to long-lived keepalive config. 2085 if (!mTCPKeepaliveTransitionTimer) { 2086 mTCPKeepaliveTransitionTimer = NS_NewTimer(); 2087 } 2088 2089 if (mTCPKeepaliveTransitionTimer) { 2090 int32_t time = gHttpHandler->GetTCPKeepaliveShortLivedTime(); 2091 2092 // Adjust |time| to ensure a full set of keepalive probes can be sent 2093 // at the end of the short-lived phase. 2094 if (gHttpHandler->TCPKeepaliveEnabledForShortLivedConns()) { 2095 if (NS_WARN_IF(!gSocketTransportService)) { 2096 return NS_ERROR_NOT_INITIALIZED; 2097 } 2098 int32_t probeCount = -1; 2099 rv = gSocketTransportService->GetKeepaliveProbeCount(&probeCount); 2100 if (NS_WARN_IF(NS_FAILED(rv))) { 2101 return rv; 2102 } 2103 if (NS_WARN_IF(probeCount <= 0)) { 2104 return NS_ERROR_UNEXPECTED; 2105 } 2106 // Add time for final keepalive probes, and 2 seconds for a buffer. 2107 time += ((probeCount)*retryIntervalS) - (time % idleTimeS) + 2; 2108 } 2109 mTCPKeepaliveTransitionTimer->InitWithNamedFuncCallback( 2110 nsHttpConnection::UpdateTCPKeepalive, this, (uint32_t)time * 1000, 2111 nsITimer::TYPE_ONE_SHOT, 2112 "net::nsHttpConnection::StartShortLivedTCPKeepalives"_ns); 2113 } else { 2114 NS_WARNING( 2115 "nsHttpConnection::StartShortLivedTCPKeepalives failed to " 2116 "create timer."); 2117 } 2118 2119 return NS_OK; 2120 } 2121 2122 nsresult nsHttpConnection::StartLongLivedTCPKeepalives() { 2123 MOZ_ASSERT(mUsingSpdyVersion == SpdyVersion::NONE, 2124 "Don't use TCP Keepalive with SPDY!"); 2125 if (NS_WARN_IF(mUsingSpdyVersion != SpdyVersion::NONE)) { 2126 return NS_OK; 2127 } 2128 MOZ_ASSERT(mSocketTransport); 2129 if (!mSocketTransport) { 2130 return NS_ERROR_NOT_INITIALIZED; 2131 } 2132 2133 nsresult rv = NS_OK; 2134 if (gHttpHandler->TCPKeepaliveEnabledForLongLivedConns()) { 2135 // Increase the idle time. 2136 int32_t idleTimeS = gHttpHandler->GetTCPKeepaliveLongLivedIdleTime(); 2137 LOG(("nsHttpConnection::StartLongLivedTCPKeepalives[%p] idle time[%ds]", 2138 this, idleTimeS)); 2139 2140 int32_t retryIntervalS = 2141 std::max<int32_t>((int32_t)PR_IntervalToSeconds(mRtt), 1); 2142 rv = mSocketTransport->SetKeepaliveVals(idleTimeS, retryIntervalS); 2143 if (NS_FAILED(rv)) { 2144 return rv; 2145 } 2146 2147 // Ensure keepalive is enabled, if current status is disabled. 2148 if (mTCPKeepaliveConfig == kTCPKeepaliveDisabled) { 2149 rv = mSocketTransport->SetKeepaliveEnabled(true); 2150 if (NS_FAILED(rv)) { 2151 return rv; 2152 } 2153 } 2154 mTCPKeepaliveConfig = kTCPKeepaliveLongLivedConfig; 2155 } else { 2156 rv = mSocketTransport->SetKeepaliveEnabled(false); 2157 mTCPKeepaliveConfig = kTCPKeepaliveDisabled; 2158 } 2159 2160 if (NS_FAILED(rv)) { 2161 return rv; 2162 } 2163 return NS_OK; 2164 } 2165 2166 nsresult nsHttpConnection::DisableTCPKeepalives() { 2167 MOZ_ASSERT(mSocketTransport); 2168 if (!mSocketTransport) { 2169 return NS_ERROR_NOT_INITIALIZED; 2170 } 2171 2172 LOG(("nsHttpConnection::DisableTCPKeepalives [%p]", this)); 2173 if (mTCPKeepaliveConfig != kTCPKeepaliveDisabled) { 2174 nsresult rv = mSocketTransport->SetKeepaliveEnabled(false); 2175 if (NS_FAILED(rv)) { 2176 return rv; 2177 } 2178 mTCPKeepaliveConfig = kTCPKeepaliveDisabled; 2179 } 2180 if (mTCPKeepaliveTransitionTimer) { 2181 mTCPKeepaliveTransitionTimer->Cancel(); 2182 mTCPKeepaliveTransitionTimer = nullptr; 2183 } 2184 return NS_OK; 2185 } 2186 2187 //----------------------------------------------------------------------------- 2188 // nsHttpConnection::nsISupports 2189 //----------------------------------------------------------------------------- 2190 2191 NS_IMPL_ADDREF(nsHttpConnection) 2192 NS_IMPL_RELEASE(nsHttpConnection) 2193 2194 NS_INTERFACE_MAP_BEGIN(nsHttpConnection) 2195 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 2196 NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback) 2197 NS_INTERFACE_MAP_ENTRY(nsIOutputStreamCallback) 2198 NS_INTERFACE_MAP_ENTRY(nsITransportEventSink) 2199 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) 2200 NS_INTERFACE_MAP_ENTRY(HttpConnectionBase) 2201 NS_INTERFACE_MAP_ENTRY_CONCRETE(nsHttpConnection) 2202 NS_INTERFACE_MAP_END 2203 2204 //----------------------------------------------------------------------------- 2205 // nsHttpConnection::nsIInputStreamCallback 2206 //----------------------------------------------------------------------------- 2207 2208 // called on the socket transport thread 2209 NS_IMETHODIMP 2210 nsHttpConnection::OnInputStreamReady(nsIAsyncInputStream* in) { 2211 MOZ_ASSERT(in == mSocketIn, "unexpected stream"); 2212 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 2213 2214 if (mIdleMonitoring) { 2215 MOZ_ASSERT(!mTransaction, "Idle Input Event While Active"); 2216 2217 // The only read event that is protocol compliant for an idle connection 2218 // is an EOF, which we check for with CanReuse(). If the data is 2219 // something else then just ignore it and suspend checking for EOF - 2220 // our normal timers or protocol stack are the place to deal with 2221 // any exception logic. 2222 2223 if (!CanReuse()) { 2224 LOG(("Server initiated close of idle conn %p\n", this)); 2225 (void)gHttpHandler->ConnMgr()->CloseIdleConnection(this); 2226 return NS_OK; 2227 } 2228 2229 LOG(("Input data on idle conn %p, but not closing yet\n", this)); 2230 return NS_OK; 2231 } 2232 2233 // if the transaction was dropped... 2234 if (!mTransaction) { 2235 LOG((" no transaction; ignoring event\n")); 2236 return NS_OK; 2237 } 2238 2239 nsresult rv = OnSocketReadable(); 2240 if (rv == NS_BASE_STREAM_WOULD_BLOCK) { 2241 return rv; 2242 } 2243 2244 if (NS_FAILED(rv)) { 2245 CloseTransaction(mTransaction, rv); 2246 } 2247 2248 return NS_OK; 2249 } 2250 2251 //----------------------------------------------------------------------------- 2252 // nsHttpConnection::nsIOutputStreamCallback 2253 //----------------------------------------------------------------------------- 2254 2255 NS_IMETHODIMP 2256 nsHttpConnection::OnOutputStreamReady(nsIAsyncOutputStream* out) { 2257 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 2258 MOZ_ASSERT(out == mSocketOut, "unexpected socket"); 2259 // if the transaction was dropped... 2260 if (!mTransaction) { 2261 LOG((" no transaction; ignoring event\n")); 2262 return NS_OK; 2263 } 2264 2265 nsresult rv = OnSocketWritable(); 2266 if (rv == NS_BASE_STREAM_WOULD_BLOCK) { 2267 return NS_OK; 2268 } 2269 2270 if (NS_FAILED(rv)) CloseTransaction(mTransaction, rv); 2271 2272 return NS_OK; 2273 } 2274 2275 //----------------------------------------------------------------------------- 2276 // nsHttpConnection::nsITransportEventSink 2277 //----------------------------------------------------------------------------- 2278 2279 NS_IMETHODIMP 2280 nsHttpConnection::OnTransportStatus(nsITransport* trans, nsresult status, 2281 int64_t progress, int64_t progressMax) { 2282 if (mTransaction) mTransaction->OnTransportStatus(trans, status, progress); 2283 return NS_OK; 2284 } 2285 2286 //----------------------------------------------------------------------------- 2287 // nsHttpConnection::nsIInterfaceRequestor 2288 //----------------------------------------------------------------------------- 2289 2290 // not called on the socket transport thread 2291 NS_IMETHODIMP 2292 nsHttpConnection::GetInterface(const nsIID& iid, void** result) { 2293 // NOTE: This function is only called on the UI thread via sync proxy from 2294 // the socket transport thread. If that weren't the case, then we'd 2295 // have to worry about the possibility of mTransaction going away 2296 // part-way through this function call. See CloseTransaction. 2297 2298 // NOTE - there is a bug here, the call to getinterface is proxied off the 2299 // nss thread, not the ui thread as the above comment says. So there is 2300 // indeed a chance of mTransaction going away. bug 615342 2301 2302 MOZ_ASSERT(!OnSocketThread(), "on socket thread"); 2303 2304 nsCOMPtr<nsIInterfaceRequestor> callbacks; 2305 { 2306 MutexAutoLock lock(mCallbacksLock); 2307 callbacks = mCallbacks; 2308 } 2309 if (callbacks) return callbacks->GetInterface(iid, result); 2310 return NS_ERROR_NO_INTERFACE; 2311 } 2312 2313 void nsHttpConnection::CheckForTraffic(bool check) { 2314 if (check) { 2315 LOG((" CheckForTraffic conn %p\n", this)); 2316 if (mSpdySession) { 2317 if (PR_IntervalToMilliseconds(IdleTime()) >= 500) { 2318 // Send a ping to verify it is still alive if it has been idle 2319 // more than half a second, the network changed events are 2320 // rate-limited to one per 1000 ms. 2321 LOG((" SendPing\n")); 2322 mSpdySession->SendPing(); 2323 } else { 2324 LOG((" SendPing skipped due to network activity\n")); 2325 } 2326 } else { 2327 // If not SPDY, Store snapshot amount of data right now 2328 mTrafficCount = mTotalBytesWritten + mTotalBytesRead; 2329 mTrafficStamp = true; 2330 } 2331 } else { 2332 // mark it as not checked 2333 mTrafficStamp = false; 2334 } 2335 } 2336 2337 void nsHttpConnection::SetEvent(nsresult aStatus) { 2338 LOG(("nsHttpConnection::SetEvent [this=%p status=%" PRIx32 "]\n", this, 2339 static_cast<uint32_t>(aStatus))); 2340 if (!mBootstrappedTimingsSet) { 2341 mBootstrappedTimingsSet = true; 2342 } 2343 switch (aStatus) { 2344 case NS_NET_STATUS_RESOLVING_HOST: 2345 mBootstrappedTimings.domainLookupStart = TimeStamp::Now(); 2346 break; 2347 case NS_NET_STATUS_RESOLVED_HOST: 2348 mBootstrappedTimings.domainLookupEnd = TimeStamp::Now(); 2349 break; 2350 case NS_NET_STATUS_CONNECTING_TO: 2351 mBootstrappedTimings.connectStart = TimeStamp::Now(); 2352 break; 2353 case NS_NET_STATUS_CONNECTED_TO: { 2354 TimeStamp tnow = TimeStamp::Now(); 2355 mBootstrappedTimings.tcpConnectEnd = tnow; 2356 mBootstrappedTimings.connectEnd = tnow; 2357 mBootstrappedTimings.secureConnectionStart = tnow; 2358 break; 2359 } 2360 case NS_NET_STATUS_TLS_HANDSHAKE_STARTING: 2361 mBootstrappedTimings.secureConnectionStart = TimeStamp::Now(); 2362 break; 2363 case NS_NET_STATUS_TLS_HANDSHAKE_ENDED: 2364 mBootstrappedTimings.connectEnd = TimeStamp::Now(); 2365 break; 2366 default: 2367 break; 2368 } 2369 } 2370 2371 bool nsHttpConnection::NoClientCertAuth() const { 2372 if (!mSocketTransport) { 2373 return false; 2374 } 2375 2376 nsCOMPtr<nsITLSSocketControl> tlsSocketControl; 2377 mSocketTransport->GetTlsSocketControl(getter_AddRefs(tlsSocketControl)); 2378 if (!tlsSocketControl) { 2379 return false; 2380 } 2381 2382 return !tlsSocketControl->GetClientCertSent(); 2383 } 2384 2385 ExtendedCONNECTSupport nsHttpConnection::GetExtendedCONNECTSupport() { 2386 LOG3(("nsHttpConnection::GetExtendedCONNECTSupport")); 2387 if (!UsingSpdy()) { 2388 return ExtendedCONNECTSupport::SUPPORTED; 2389 } 2390 LOG3(("nsHttpConnection::ExtendedCONNECTSupport checking spdy session")); 2391 if (mSpdySession) { 2392 return mSpdySession->GetExtendedCONNECTSupport(); 2393 } 2394 2395 return ExtendedCONNECTSupport::NO_SUPPORT; 2396 } 2397 2398 bool nsHttpConnection::LastTransactionExpectedNoContent() { 2399 return mLastTransactionExpectedNoContent; 2400 } 2401 2402 void nsHttpConnection::SetLastTransactionExpectedNoContent(bool val) { 2403 mLastTransactionExpectedNoContent = val; 2404 } 2405 2406 bool nsHttpConnection::IsPersistent() { return IsKeepAlive() && !mDontReuse; } 2407 2408 nsAHttpTransaction* nsHttpConnection::Transaction() { return mTransaction; } 2409 2410 nsresult nsHttpConnection::GetSelfAddr(NetAddr* addr) { 2411 if (!mSocketTransport) { 2412 return NS_ERROR_FAILURE; 2413 } 2414 return mSocketTransport->GetSelfAddr(addr); 2415 } 2416 2417 nsresult nsHttpConnection::GetPeerAddr(NetAddr* addr) { 2418 if (!mSocketTransport) { 2419 return NS_ERROR_FAILURE; 2420 } 2421 return mSocketTransport->GetPeerAddr(addr); 2422 } 2423 2424 bool nsHttpConnection::ResolvedByTRR() { 2425 bool val = false; 2426 if (mSocketTransport) { 2427 mSocketTransport->ResolvedByTRR(&val); 2428 } 2429 return val; 2430 } 2431 2432 nsIRequest::TRRMode nsHttpConnection::EffectiveTRRMode() { 2433 nsIRequest::TRRMode mode = nsIRequest::TRR_DEFAULT_MODE; 2434 if (mSocketTransport) { 2435 mSocketTransport->GetEffectiveTRRMode(&mode); 2436 } 2437 return mode; 2438 } 2439 2440 TRRSkippedReason nsHttpConnection::TRRSkipReason() { 2441 TRRSkippedReason reason = nsITRRSkipReason::TRR_UNSET; 2442 if (mSocketTransport) { 2443 mSocketTransport->GetTrrSkipReason(&reason); 2444 } 2445 return reason; 2446 } 2447 2448 bool nsHttpConnection::GetEchConfigUsed() { 2449 bool val = false; 2450 if (mSocketTransport) { 2451 mSocketTransport->GetEchConfigUsed(&val); 2452 } 2453 return val; 2454 } 2455 2456 void nsHttpConnection::HandshakeDoneInternal() { 2457 LOG(("nsHttpConnection::HandshakeDoneInternal [this=%p]\n", this)); 2458 if (mTlsHandshaker->NPNComplete()) { 2459 return; 2460 } 2461 2462 ChangeConnectionState(ConnectionState::TRANSFERING); 2463 2464 nsCOMPtr<nsITLSSocketControl> tlsSocketControl; 2465 GetTLSSocketControl(getter_AddRefs(tlsSocketControl)); 2466 if (!tlsSocketControl) { 2467 mTlsHandshaker->FinishNPNSetup(false, false); 2468 return; 2469 } 2470 2471 nsCOMPtr<nsITransportSecurityInfo> securityInfo; 2472 if (NS_FAILED( 2473 tlsSocketControl->GetSecurityInfo(getter_AddRefs(securityInfo)))) { 2474 mTlsHandshaker->FinishNPNSetup(false, false); 2475 return; 2476 } 2477 if (!securityInfo) { 2478 mTlsHandshaker->FinishNPNSetup(false, false); 2479 return; 2480 } 2481 2482 nsAutoCString negotiatedNPN; 2483 DebugOnly<nsresult> rvDebug = securityInfo->GetNegotiatedNPN(negotiatedNPN); 2484 MOZ_ASSERT(NS_SUCCEEDED(rvDebug)); 2485 2486 nsAutoCString transactionNPN; 2487 transactionNPN = mConnInfo->GetNPNToken(); 2488 LOG(("negotiatedNPN: %s - transactionNPN: %s", negotiatedNPN.get(), 2489 transactionNPN.get())); 2490 if (!transactionNPN.IsEmpty() && negotiatedNPN != transactionNPN) { 2491 LOG(("Resetting connection due to mismatched NPN token")); 2492 mozilla::glean::network::alpn_mismatch_count.Get(negotiatedNPN).Add(); 2493 DontReuse(); 2494 if (mTransaction) { 2495 mTransaction->Close(NS_ERROR_NET_RESET); 2496 } 2497 return; 2498 } 2499 2500 bool earlyDataAccepted = false; 2501 if (mTlsHandshaker->EarlyDataUsed()) { 2502 // Check if early data has been accepted. 2503 nsresult rvEarlyData = 2504 tlsSocketControl->GetEarlyDataAccepted(&earlyDataAccepted); 2505 LOG( 2506 ("nsHttpConnection::HandshakeDone [this=%p] - early data " 2507 "that was sent during 0RTT %s been accepted [rv=%" PRIx32 "].", 2508 this, earlyDataAccepted ? "has" : "has not", 2509 static_cast<uint32_t>(rvEarlyData))); 2510 2511 if (NS_FAILED(rvEarlyData) || 2512 (mTransaction && 2513 NS_FAILED(mTransaction->Finish0RTT( 2514 !earlyDataAccepted, 2515 negotiatedNPN != mTlsHandshaker->EarlyNegotiatedALPN())))) { 2516 LOG( 2517 ("nsHttpConnection::HandshakeDone [this=%p] closing transaction " 2518 "%p", 2519 this, mTransaction.get())); 2520 if (mTransaction) { 2521 mTransaction->Close(NS_ERROR_NET_RESET); 2522 } 2523 mTlsHandshaker->FinishNPNSetup(false, true); 2524 return; 2525 } 2526 if (mDid0RTTSpdy && 2527 (negotiatedNPN != mTlsHandshaker->EarlyNegotiatedALPN())) { 2528 Reset0RttForSpdy(); 2529 } 2530 } 2531 2532 if (mTlsHandshaker->EarlyDataAvailable() && !earlyDataAccepted) { 2533 // When the early-data were used but not accepted, we need to start 2534 // from the begining here and start writing the request again. 2535 // The same is true if 0RTT was available but not used. 2536 if (mSocketIn) { 2537 mSocketIn->AsyncWait(nullptr, 0, 0, nullptr); 2538 } 2539 (void)ResumeSend(); 2540 } 2541 2542 int16_t tlsVersion; 2543 tlsSocketControl->GetSSLVersionUsed(&tlsVersion); 2544 mConnInfo->SetLessThanTls13( 2545 (tlsVersion < nsITLSSocketControl::TLS_VERSION_1_3) && 2546 (tlsVersion != nsITLSSocketControl::SSL_VERSION_UNKNOWN)); 2547 #ifndef ANDROID 2548 mTlsHandshaker->EarlyDataTelemetry(tlsVersion, earlyDataAccepted, 2549 mContentBytesWritten0RTT); 2550 #endif 2551 mTlsHandshaker->EarlyDataDone(); 2552 2553 if (!earlyDataAccepted) { 2554 LOG( 2555 ("nsHttpConnection::HandshakeDone [this=%p] early data not " 2556 "accepted or early data were not used", 2557 this)); 2558 2559 const SpdyInformation* info = gHttpHandler->SpdyInfo(); 2560 if (negotiatedNPN.Equals(info->VersionString)) { 2561 if (mTransaction) { 2562 StartSpdy(tlsSocketControl, info->Version); 2563 } else { 2564 LOG( 2565 ("nsHttpConnection::HandshakeDone [this=%p] set " 2566 "mContinueHandshakeDone", 2567 this)); 2568 RefPtr<nsHttpConnection> self = this; 2569 mContinueHandshakeDone = [self = RefPtr{this}, 2570 tlsSocketControl(tlsSocketControl), 2571 info(info->Version)]() { 2572 LOG(("nsHttpConnection do mContinueHandshakeDone [this=%p]", 2573 self.get())); 2574 self->StartSpdy(tlsSocketControl, info); 2575 self->mTlsHandshaker->FinishNPNSetup(true, true); 2576 }; 2577 return; 2578 } 2579 } 2580 } else { 2581 LOG(("nsHttpConnection::HandshakeDone [this=%p] - %" PRId64 " bytes " 2582 "has been sent during 0RTT.", 2583 this, mContentBytesWritten0RTT)); 2584 mContentBytesWritten = mContentBytesWritten0RTT; 2585 2586 if (mSpdySession) { 2587 // We had already started 0RTT-spdy, now we need to fully set up 2588 // spdy, since we know we're sticking with it. 2589 LOG( 2590 ("nsHttpConnection::HandshakeDone [this=%p] - finishing " 2591 "StartSpdy for 0rtt spdy session %p", 2592 this, mSpdySession.get())); 2593 StartSpdy(tlsSocketControl, mSpdySession->SpdyVersion()); 2594 } 2595 } 2596 2597 mTlsHandshaker->FinishNPNSetup(true, true); 2598 (void)ResumeSend(); 2599 } 2600 2601 void nsHttpConnection::SetTunnelSetupDone() { 2602 MOZ_ASSERT(mProxyConnectStream); 2603 MOZ_ASSERT(mState == HttpConnectionState::SETTING_UP_TUNNEL); 2604 2605 ChangeState(HttpConnectionState::REQUEST); 2606 mProxyConnectStream = nullptr; 2607 } 2608 2609 nsresult nsHttpConnection::SetupProxyConnectStream() { 2610 LOG(("nsHttpConnection::SetupStream\n")); 2611 NS_ENSURE_TRUE(!mProxyConnectStream, NS_ERROR_ALREADY_INITIALIZED); 2612 MOZ_ASSERT(mState == HttpConnectionState::SETTING_UP_TUNNEL); 2613 2614 nsAutoCString buf; 2615 nsHttpRequestHead request; 2616 nsresult rv = MakeConnectString(mTransaction, &request, buf, 2617 mForWebSocket && mInSpdyTunnel, 2618 mTransactionCaps & NS_HTTP_USE_RFP); 2619 if (NS_FAILED(rv)) { 2620 return rv; 2621 } 2622 rv = NS_NewCStringInputStream(getter_AddRefs(mProxyConnectStream), 2623 std::move(buf)); 2624 return rv; 2625 } 2626 2627 nsresult nsHttpConnection::ReadFromStream(nsIInputStream* input, void* closure, 2628 const char* buf, uint32_t offset, 2629 uint32_t count, uint32_t* countRead) { 2630 // thunk for nsIInputStream instance 2631 nsHttpConnection* conn = (nsHttpConnection*)closure; 2632 return conn->OnReadSegment(buf, count, countRead); 2633 } 2634 2635 nsresult nsHttpConnection::SendConnectRequest(void* closure, 2636 uint32_t* transactionBytes) { 2637 LOG((" writing CONNECT request stream\n")); 2638 return mProxyConnectStream->ReadSegments(ReadFromStream, closure, 2639 nsIOService::gDefaultSegmentSize, 2640 transactionBytes); 2641 } 2642 2643 WebTransportSessionBase* nsHttpConnection::GetWebTransportSession( 2644 nsAHttpTransaction* aTransaction) { 2645 LOG( 2646 ("nsHttpConnection::GetWebTransportSession %p mSpdySession=%p " 2647 "mExtendedCONNECTHttp2Session=%p", 2648 this, mSpdySession.get(), mExtendedCONNECTHttp2Session.get())); 2649 if (!mExtendedCONNECTHttp2Session) { 2650 return nullptr; 2651 } 2652 2653 return mExtendedCONNECTHttp2Session->GetWebTransportSession(aTransaction); 2654 } 2655 2656 } // namespace mozilla::net