HttpConnectionUDP.cpp (39869B)
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 #define TLS_EARLY_DATA_NOT_AVAILABLE 0 17 #define TLS_EARLY_DATA_AVAILABLE_BUT_NOT_USED 1 18 #define TLS_EARLY_DATA_AVAILABLE_AND_USED 2 19 20 #include "ASpdySession.h" 21 #include "ConnectionHandle.h" 22 #include "mozilla/StaticPrefs_network.h" 23 #include "mozilla/glean/NetwerkMetrics.h" 24 #include "HttpConnectionUDP.h" 25 #include "nsHttpHandler.h" 26 #include "Http3Session.h" 27 #include "nsComponentManagerUtils.h" 28 #include "nsIHttpChannelInternal.h" 29 #include "nsISocketProvider.h" 30 #include "nsNetAddr.h" 31 #include "nsINetAddr.h" 32 #include "nsStringStream.h" 33 34 namespace mozilla { 35 namespace net { 36 37 //----------------------------------------------------------------------------- 38 // ConnectUDPTransaction 39 //----------------------------------------------------------------------------- 40 41 class ConnectUDPTransaction : public nsAHttpTransaction { 42 public: 43 NS_DECL_THREADSAFE_ISUPPORTS 44 45 explicit ConnectUDPTransaction(nsAHttpTransaction* aTrans, 46 nsIInputStream* aStream) 47 : mTransaction(aTrans), mProxyConnectStream(aStream) { 48 LOG(("ConnectUDPTransaction ctor: %p", this)); 49 } 50 51 void SetConnection(nsAHttpConnection* aConn) override { mConnection = aConn; } 52 nsAHttpConnection* Connection() override { return mConnection; } 53 void GetSecurityCallbacks(nsIInterfaceRequestor** aCallbacks) override { 54 mTransaction->GetSecurityCallbacks(aCallbacks); 55 } 56 void OnTransportStatus(nsITransport* transport, nsresult status, 57 int64_t progress) override { 58 mTransaction->OnTransportStatus(transport, status, progress); 59 } 60 bool IsDone() override { return mIsDone; } 61 nsresult Status() override { return mTransaction->Status(); } 62 uint32_t Caps() override { return mTransaction->Caps(); } 63 64 static nsresult ReadRequestSegment(nsIInputStream* stream, void* closure, 65 const char* buf, uint32_t offset, 66 uint32_t count, uint32_t* countRead) { 67 ConnectUDPTransaction* trans = (ConnectUDPTransaction*)closure; 68 nsresult rv = trans->mReader->OnReadSegment(buf, count, countRead); 69 return rv; 70 } 71 72 [[nodiscard]] nsresult ReadSegments(nsAHttpSegmentReader* reader, 73 uint32_t count, 74 uint32_t* countRead) override { 75 mReader = reader; 76 (void)mProxyConnectStream->ReadSegments(ReadRequestSegment, this, count, 77 countRead); 78 mReader = nullptr; 79 uint64_t avil = 0; 80 (void)mProxyConnectStream->Available(&avil); 81 if (!avil) { 82 mIsDone = true; 83 } 84 return NS_OK; 85 } 86 [[nodiscard]] nsresult WriteSegments(nsAHttpSegmentWriter* writer, 87 uint32_t count, 88 uint32_t* countWritten) override { 89 // This is hacky. We should set Connection properly in 90 // nsHttpConnectionMgr::DispatchTransaction. 91 if (!mTransaction->Connection()) { 92 mTransaction->SetConnection(mConnection); 93 } 94 nsresult rv = mTransaction->WriteSegments(writer, count, countWritten); 95 mTransaction = nullptr; 96 return rv; 97 } 98 void Close(nsresult reason) override { 99 LOG(("ConnectUDPTransaction close mTransaction=%p", mTransaction.get())); 100 if (mTransaction) { 101 mTransaction->Close(reason); 102 } 103 } 104 nsHttpConnectionInfo* ConnectionInfo() override { 105 return mTransaction->ConnectionInfo(); 106 } 107 void SetProxyConnectFailed() override { 108 mTransaction->SetProxyConnectFailed(); 109 } 110 nsHttpRequestHead* RequestHead() override { 111 return mTransaction->RequestHead(); 112 } 113 uint32_t Http1xTransactionCount() override { return 0; } 114 [[nodiscard]] nsresult TakeSubTransactions( 115 nsTArray<RefPtr<nsAHttpTransaction>>& outTransactions) override { 116 return NS_OK; 117 } 118 119 protected: 120 virtual ~ConnectUDPTransaction() { 121 LOG(("ConnectUDPTransaction dtor: %p", this)); 122 } 123 124 RefPtr<nsAHttpTransaction> mTransaction; 125 nsCOMPtr<nsIInputStream> mProxyConnectStream; 126 RefPtr<nsAHttpConnection> mConnection; 127 bool mIsDone = false; 128 nsAHttpSegmentReader* mReader = nullptr; 129 }; 130 131 NS_IMPL_ISUPPORTS(ConnectUDPTransaction, nsISupportsWeakReference) 132 133 //----------------------------------------------------------------------------- 134 // Http3ConnectTransaction 135 //----------------------------------------------------------------------------- 136 137 // We need a dummy transaction to keep the Http3StreamTunnel alive. 138 // Http3StreamTunnel instances are owned via the transaction-to-stream map 139 // in Http3Session::mStreamTransactionHash. 140 class Http3ConnectTransaction : public ConnectUDPTransaction { 141 public: 142 explicit Http3ConnectTransaction(uint32_t aCaps, nsHttpConnectionInfo* aInfo) 143 : ConnectUDPTransaction(nullptr, nullptr), 144 mCaps(aCaps), 145 mConnInfo(aInfo) { 146 LOG(("Http3ConnectTransaction ctor: %p", this)); 147 } 148 149 uint32_t Caps() override { return mCaps; } 150 nsHttpConnectionInfo* ConnectionInfo() override { return mConnInfo; } 151 152 void OnTransportStatus(nsITransport* transport, nsresult status, 153 int64_t progress) override {} 154 155 [[nodiscard]] nsresult ReadSegments(nsAHttpSegmentReader* reader, 156 uint32_t count, 157 uint32_t* countRead) override { 158 MOZ_ASSERT_UNREACHABLE("Shouldn't be called"); 159 return NS_ERROR_NOT_IMPLEMENTED; 160 } 161 [[nodiscard]] nsresult WriteSegments(nsAHttpSegmentWriter* writer, 162 uint32_t count, 163 uint32_t* countWritten) override { 164 MOZ_ASSERT_UNREACHABLE("Shouldn't be called"); 165 return NS_ERROR_NOT_IMPLEMENTED; 166 } 167 168 void Close(nsresult reason) override { mConnection = nullptr; } 169 170 private: 171 virtual ~Http3ConnectTransaction() { 172 LOG(("Http3ConnectTransaction dtor: %p", this)); 173 } 174 175 uint32_t mCaps = 0; 176 RefPtr<nsHttpConnectionInfo> mConnInfo; 177 }; 178 179 //----------------------------------------------------------------------------- 180 // HttpConnectionUDP <public> 181 //----------------------------------------------------------------------------- 182 183 HttpConnectionUDP::HttpConnectionUDP() : mHttpHandler(gHttpHandler) { 184 LOG(("Creating HttpConnectionUDP @%p\n", this)); 185 } 186 187 HttpConnectionUDP::~HttpConnectionUDP() { 188 LOG(("Destroying HttpConnectionUDP @%p\n", this)); 189 190 if (mForceSendTimer) { 191 mForceSendTimer->Cancel(); 192 mForceSendTimer = nullptr; 193 } 194 195 MOZ_ASSERT(mQueuedHttpConnectTransaction.IsEmpty(), 196 "Should not have any queued transactions"); 197 MOZ_ASSERT(mQueuedConnectUdpTransaction.IsEmpty(), 198 "Should not have any queued transactions"); 199 } 200 201 nsresult HttpConnectionUDP::Init(nsHttpConnectionInfo* info, 202 nsIDNSRecord* dnsRecord, nsresult status, 203 nsIInterfaceRequestor* callbacks, 204 uint32_t caps) { 205 LOG1(("HttpConnectionUDP::Init this=%p", this)); 206 NS_ENSURE_ARG_POINTER(info); 207 NS_ENSURE_TRUE(!mConnInfo, NS_ERROR_ALREADY_INITIALIZED); 208 209 mConnInfo = info; 210 MOZ_ASSERT(mConnInfo); 211 MOZ_ASSERT(mConnInfo->IsHttp3() || mConnInfo->IsHttp3ProxyConnection()); 212 213 mErrorBeforeConnect = status; 214 mAlpnToken = mConnInfo->GetNPNToken(); 215 if (NS_FAILED(mErrorBeforeConnect)) { 216 // See explanation for non-strictness of this operation in 217 // SetSecurityCallbacks. 218 mCallbacks = new nsMainThreadPtrHolder<nsIInterfaceRequestor>( 219 "HttpConnectionUDP::mCallbacks", callbacks, false); 220 SetCloseReason(ToCloseReason(mErrorBeforeConnect)); 221 return mErrorBeforeConnect; 222 } 223 224 nsCOMPtr<nsIDNSAddrRecord> dnsAddrRecord = do_QueryInterface(dnsRecord); 225 if (!dnsAddrRecord) { 226 return NS_ERROR_FAILURE; 227 } 228 dnsAddrRecord->IsTRR(&mResolvedByTRR); 229 dnsAddrRecord->GetEffectiveTRRMode(&mEffectiveTRRMode); 230 dnsAddrRecord->GetTrrSkipReason(&mTRRSkipReason); 231 NetAddr peerAddr; 232 uint16_t port = 233 mConnInfo->IsHttp3ProxyConnection() 234 ? mConnInfo->ProxyPort() 235 : (!mConnInfo->GetRoutedHost().IsEmpty() ? mConnInfo->RoutedPort() 236 : mConnInfo->OriginPort()); 237 238 nsresult rv = dnsAddrRecord->GetNextAddr(port, &peerAddr); 239 if (NS_FAILED(rv)) { 240 return rv; 241 } 242 243 // We are disabling 0.0.0.0 for non-test purposes. 244 // See https://github.com/whatwg/fetch/pull/1763 for context. 245 if (peerAddr.IsIPAddrAny()) { 246 if (StaticPrefs::network_socket_ip_addr_any_disabled()) { 247 LOG(("Connection refused because of 0.0.0.0 IP address\n")); 248 return NS_ERROR_CONNECTION_REFUSED; 249 } 250 } 251 252 mSocket = do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv); 253 if (NS_FAILED(rv)) { 254 return rv; 255 } 256 257 return InitCommon(mSocket, peerAddr, callbacks, caps, false); 258 } 259 260 nsresult HttpConnectionUDP::InitWithSocket(nsHttpConnectionInfo* info, 261 nsIUDPSocket* aSocket, 262 NetAddr aPeerAddr, 263 nsIInterfaceRequestor* callbacks, 264 uint32_t caps) { 265 LOG1(("HttpConnectionUDP::InitWithSocket this=%p", this)); 266 NS_ENSURE_ARG_POINTER(info); 267 NS_ENSURE_TRUE(!mConnInfo, NS_ERROR_ALREADY_INITIALIZED); 268 269 mConnInfo = info; 270 MOZ_ASSERT(mConnInfo->IsHttp3() || mConnInfo->IsHttp3ProxyConnection()); 271 272 mErrorBeforeConnect = NS_OK; 273 mAlpnToken = mConnInfo->GetNPNToken(); 274 275 return InitCommon(aSocket, aPeerAddr, callbacks, caps, true); 276 } 277 278 nsresult HttpConnectionUDP::InitCommon(nsIUDPSocket* aSocket, 279 const NetAddr& aPeerAddr, 280 nsIInterfaceRequestor* callbacks, 281 uint32_t caps, bool isInTunnel) { 282 mSocket = aSocket; 283 284 NetAddr local; 285 local.raw.family = aPeerAddr.raw.family; 286 nsresult rv = mSocket->InitWithAddress(&local, nullptr, false, 1); 287 if (NS_FAILED(rv)) { 288 mSocket = nullptr; 289 return rv; 290 } 291 292 rv = mSocket->SetRecvBufferSize( 293 StaticPrefs::network_http_http3_recvBufferSize()); 294 if (NS_FAILED(rv)) { 295 LOG(("HttpConnectionUDP::InitCommon SetRecvBufferSize failed %d [this=%p]", 296 static_cast<uint32_t>(rv), this)); 297 mSocket->Close(); 298 mSocket = nullptr; 299 return rv; 300 } 301 302 if (aPeerAddr.raw.family == AF_INET) { 303 rv = mSocket->SetDontFragment(true); 304 if (NS_FAILED(rv)) { 305 LOG(("HttpConnectionUDP::InitCommon SetDontFragment failed %d [this=%p]", 306 static_cast<uint32_t>(rv), this)); 307 } 308 } 309 310 // get the resulting socket address. 311 rv = mSocket->GetLocalAddr(getter_AddRefs(mSelfAddr)); 312 if (NS_FAILED(rv)) { 313 mSocket->Close(); 314 mSocket = nullptr; 315 return rv; 316 } 317 318 uint32_t providerFlags = 0; 319 if (caps & NS_HTTP_LOAD_ANONYMOUS) { 320 providerFlags |= nsISocketProvider::ANONYMOUS_CONNECT; 321 } 322 if (mConnInfo->GetPrivate()) { 323 providerFlags |= nsISocketProvider::NO_PERMANENT_STORAGE; 324 } 325 if (((caps & NS_HTTP_BE_CONSERVATIVE) || mConnInfo->GetBeConservative()) && 326 gHttpHandler->ConnMgr()->BeConservativeIfProxied( 327 mConnInfo->ProxyInfo())) { 328 providerFlags |= nsISocketProvider::BE_CONSERVATIVE; 329 } 330 if ((caps & NS_HTTP_IS_RETRY) || 331 (mConnInfo->GetTlsFlags() & 332 nsIHttpChannelInternal::TLS_FLAG_CONFIGURE_AS_RETRY)) { 333 providerFlags |= nsISocketProvider::IS_RETRY; 334 } 335 336 if (mResolvedByTRR) { 337 providerFlags |= nsISocketProvider::USED_PRIVATE_DNS; 338 } 339 340 mPeerAddr = new nsNetAddr(&aPeerAddr); 341 mHttp3Session = new Http3Session(); 342 rv = mHttp3Session->Init(mConnInfo, mSelfAddr, mPeerAddr, this, providerFlags, 343 callbacks, mSocket, isInTunnel); 344 if (NS_FAILED(rv)) { 345 LOG( 346 ("HttpConnectionUDP::InitCommon Http3Session::Init failed [this=%p " 347 "rv=%x]", 348 this, static_cast<uint32_t>(rv))); 349 mSocket->Close(); 350 mSocket = nullptr; 351 mHttp3Session = nullptr; 352 return rv; 353 } 354 355 mIsInTunnel = isInTunnel; 356 if (mIsInTunnel) { 357 mHttp3Session->SetIsInTunnel(); 358 } 359 360 ChangeConnectionState(ConnectionState::INITED); 361 // See explanation for non-strictness of this operation in 362 // SetSecurityCallbacks. 363 mCallbacks = new nsMainThreadPtrHolder<nsIInterfaceRequestor>( 364 "HttpConnectionUDP::mCallbacks", callbacks, false); 365 366 // Call SyncListen at the end of this function. This call will actually 367 // attach the sockte to SocketTransportService. 368 rv = mSocket->SyncListen(this); 369 if (NS_FAILED(rv)) { 370 mSocket->Close(); 371 mSocket = nullptr; 372 return rv; 373 } 374 375 return NS_OK; 376 } 377 378 // called on the socket thread 379 nsresult HttpConnectionUDP::Activate(nsAHttpTransaction* trans, uint32_t caps, 380 int32_t pri) { 381 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 382 LOG1(("HttpConnectionUDP::Activate [this=%p trans=%p caps=%x]\n", this, trans, 383 caps)); 384 385 nsHttpTransaction* hTrans = trans->QueryHttpTransaction(); 386 nsHttpConnectionInfo* transCI = trans->ConnectionInfo(); 387 NetAddr peerAddr; 388 if (!transCI->UsingProxy() && hTrans && 389 NS_SUCCEEDED(GetPeerAddr(&peerAddr))) { 390 // set the targetIpAddressSpace in the transaction object, this might be 391 // needed by the channel for determining the kind of LNA permissions and/or 392 // LNA telemetry 393 if (!hTrans->AllowedToConnectToIpAddressSpace( 394 peerAddr.GetIpAddressSpace())) { 395 // we could probably fail early and avoid recreating the H3 session 396 // See Bug 1968908 397 CloseTransaction(mHttp3Session, NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED); 398 trans->Close(NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED); 399 return NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED; 400 } 401 } 402 403 if (!mExperienced && !trans->IsNullTransaction()) { 404 mHasFirstHttpTransaction = true; 405 // For QUIC we have HttpConnectionUDP before the actual connection 406 // has been establish so wait for TLS handshake to be finished before 407 // we mark the connection 'experienced'. 408 if (!mExperienced && mHttp3Session && mHttp3Session->IsConnected()) { 409 mExperienced = true; 410 } 411 if (mBootstrappedTimingsSet) { 412 mBootstrappedTimingsSet = false; 413 if (hTrans) { 414 hTrans->BootstrapTimings(mBootstrappedTimings); 415 } 416 } 417 mBootstrappedTimings = TimingStruct(); 418 } 419 420 mTransactionCaps = caps; 421 mPriority = pri; 422 423 NS_ENSURE_ARG_POINTER(trans); 424 425 mErrorBeforeConnect = CheckTunnelIsNeeded(trans); 426 427 // Connection failures are Activated() just like regular transacions. 428 // If we don't have a confirmation of a connected socket then test it 429 // with a write() to get relevant error code. 430 if (NS_FAILED(mErrorBeforeConnect)) { 431 CloseTransaction(nullptr, mErrorBeforeConnect); 432 trans->Close(mErrorBeforeConnect); 433 gHttpHandler->ExcludeHttp3(mConnInfo); 434 return mErrorBeforeConnect; 435 } 436 437 // When mIsInTunnel is false, this HttpConnectionUDP represents the *outer* 438 // connection to the proxy. If a proxy CONNECT is still in progress, 439 // we need to queue the transaction until the outer connection is fully 440 // established. 441 // 442 // Important: we must not reset the transaction while the outer connection 443 // is still connecting. Resetting here could lead to opening another HTTP/3 444 // connection. 445 if (IsProxyConnectInProgress() && !mIsInTunnel && hTrans) { 446 if (!mConnected) { 447 mQueuedHttpConnectTransaction.AppendElement(hTrans); 448 (void)ResumeSend(); 449 } else { 450 // Don’t call ResetTransaction() directly here. 451 // HttpConnectionUDP::Activate() may be invoked from 452 // nsHttpConnectionMgr::DispatchSpdyPendingQ(), which could run while 453 // enumerating all connection entries. ResetTransaction() can insert a new 454 // “wild” entry, and modifying the connection-entry table during iteration 455 // is not allowed. 456 RefPtr<HttpConnectionUDP> self(this); 457 RefPtr<nsHttpTransaction> httpTransaction(hTrans); 458 NS_DispatchToCurrentThread(NS_NewRunnableFunction( 459 "HttpConnectionUDP::ResetTransaction", 460 [self{std::move(self)}, 461 httpTransaction{std::move(httpTransaction)}]() { 462 self->ResetTransaction(httpTransaction); 463 })); 464 } 465 return NS_OK; 466 } 467 468 // This is the CONNECT-UDP case. When mIsInTunnel is true, this is 469 // the *inner* connection from the proxy tunnel to the destination website. 470 // If the proxy CONNECT is still in progress, we cannot send any data 471 // yet because Http3ConnectUDPStream is not allowed to transmit until the 472 // tunnel is established. In this case, we queue the transaction and will 473 // add it to the Http3Session once the proxy CONNECT completes. 474 if (mIsInTunnel && IsProxyConnectInProgress() && hTrans) { 475 LOG(("Queue trans %p due to proxy connct in progress", hTrans)); 476 mQueuedConnectUdpTransaction.AppendElement(hTrans); 477 return NS_OK; 478 } 479 480 if (!mHttp3Session->AddStream(trans, pri, mCallbacks)) { 481 MOZ_ASSERT(false); // this cannot happen! 482 trans->Close(NS_ERROR_ABORT); 483 return NS_ERROR_FAILURE; 484 } 485 486 if (mHasFirstHttpTransaction && mExperienced) { 487 mHasFirstHttpTransaction = false; 488 mExperienceState |= ConnectionExperienceState::Experienced; 489 } 490 491 (void)ResumeSend(); 492 return NS_OK; 493 } 494 495 void HttpConnectionUDP::OnConnected() { 496 LOG(("HttpConnectionUDP::OnConnected %p", this)); 497 MOZ_ASSERT(!mConnected, "Called more than once"); 498 499 mConnected = true; 500 if (mIsInTunnel) { 501 return; 502 } 503 504 for (const auto& trans : mQueuedHttpConnectTransaction) { 505 ResetTransaction(trans); 506 } 507 mQueuedHttpConnectTransaction.Clear(); 508 } 509 510 already_AddRefed<nsIInputStream> HttpConnectionUDP::CreateProxyConnectStream( 511 nsAHttpTransaction* trans) { 512 UniquePtr<nsHttpRequestHead> request = MakeUnique<nsHttpRequestHead>(); 513 nsAutoCString host; 514 DebugOnly<nsresult> rv{}; 515 rv = nsHttpHandler::GenerateHostPort( 516 nsDependentCString(trans->ConnectionInfo()->Origin()), 517 trans->ConnectionInfo()->OriginPort(), host); 518 MOZ_ASSERT(NS_SUCCEEDED(rv)); 519 520 request->SetMethod("CONNECT"_ns); 521 request->SetVersion(gHttpHandler->HttpVersion()); 522 523 bool shouldResistFingerprinting = trans->Caps() & NS_HTTP_USE_RFP; 524 rv = request->SetHeader(nsHttp::User_Agent, 525 gHttpHandler->UserAgent(shouldResistFingerprinting)); 526 MOZ_ASSERT(NS_SUCCEEDED(rv)); 527 528 rv = request->SetHeader(nsHttp::Host, host); 529 MOZ_ASSERT(NS_SUCCEEDED(rv)); 530 531 nsAutoCString val; 532 if (NS_SUCCEEDED( 533 trans->RequestHead()->GetHeader(nsHttp::Proxy_Authorization, val))) { 534 // TODO: we should use Proxy_Authorization here. 535 rv = request->SetHeader(nsHttp::Authorization, val); 536 MOZ_ASSERT(NS_SUCCEEDED(rv)); 537 } 538 539 nsAutoCString result; 540 request->Flatten(result, false); 541 if (LOG1_ENABLED()) { 542 LOG(("HttpConnectionUDP::MakeConnectString for transaction=%p[", 543 trans->QueryHttpTransaction())); 544 LogHeaders(result.BeginReading()); 545 LOG(("]")); 546 } 547 result.AppendLiteral("\r\n"); 548 549 nsCOMPtr<nsIInputStream> stream; 550 NS_NewCStringInputStream(getter_AddRefs(stream), std::move(result)); 551 return stream.forget(); 552 } 553 554 nsresult HttpConnectionUDP::CreateTunnelStream( 555 nsAHttpTransaction* httpTransaction, HttpConnectionBase** aHttpConnection, 556 bool aIsExtendedCONNECT) { 557 LOG(("HttpConnectionUDP::CreateTunnelStream %p", this)); 558 if (!mHttp3Session) { 559 return NS_ERROR_UNEXPECTED; 560 } 561 562 bool isHttp3 = httpTransaction->ConnectionInfo()->IsHttp3(); 563 564 if (!isHttp3) { 565 RefPtr<Http3ConnectTransaction> trans = new Http3ConnectTransaction( 566 httpTransaction->Caps(), httpTransaction->ConnectionInfo()); 567 RefPtr<nsHttpConnection> conn = 568 mHttp3Session->CreateTunnelStream(trans, mCallbacks, mRtt, false); 569 RefPtr<ConnectionHandle> handle = new ConnectionHandle(conn); 570 trans->SetConnection(handle); 571 572 conn.forget(aHttpConnection); 573 return NS_OK; 574 } 575 576 nsCOMPtr<nsIInputStream> proxyConnectStream = 577 CreateProxyConnectStream(httpTransaction); 578 if (!proxyConnectStream) { 579 return NS_ERROR_OUT_OF_MEMORY; 580 } 581 582 RefPtr<ConnectUDPTransaction> trans = 583 new ConnectUDPTransaction(httpTransaction, proxyConnectStream); 584 RefPtr<HttpConnectionUDP> conn = 585 mHttp3Session->CreateTunnelStream(trans, mCallbacks); 586 RefPtr<ConnectionHandle> handle = new ConnectionHandle(conn); 587 trans->SetConnection(handle); 588 589 conn.forget(aHttpConnection); 590 return NS_OK; 591 } 592 593 void HttpConnectionUDP::Close(nsresult reason, bool aIsShutdown) { 594 LOG(("HttpConnectionUDP::Close [this=%p reason=%" PRIx32 "]\n", this, 595 static_cast<uint32_t>(reason))); 596 597 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 598 599 if (mConnectionState != ConnectionState::CLOSED) { 600 RecordConnectionCloseTelemetry(reason); 601 ChangeConnectionState(ConnectionState::CLOSED); 602 } 603 604 if (mForceSendTimer) { 605 mForceSendTimer->Cancel(); 606 mForceSendTimer = nullptr; 607 } 608 609 if (!mTrafficCategory.IsEmpty()) { 610 HttpTrafficAnalyzer* hta = gHttpHandler->GetHttpTrafficAnalyzer(); 611 if (hta) { 612 hta->IncrementHttpConnection(std::move(mTrafficCategory)); 613 MOZ_ASSERT(mTrafficCategory.IsEmpty()); 614 } 615 } 616 617 nsCOMPtr<nsIUDPSocket> socket = std::move(mSocket); 618 if (socket) { 619 socket->Close(); 620 } 621 if (mHttp3Session) { 622 mHttp3Session->SetCleanShutdown(true); 623 mHttp3Session->Close(reason); 624 mHttp3Session = nullptr; 625 } 626 627 for (const auto& trans : mQueuedHttpConnectTransaction) { 628 trans->Close(reason); 629 } 630 mQueuedHttpConnectTransaction.Clear(); 631 for (const auto& trans : mQueuedConnectUdpTransaction) { 632 trans->Close(reason); 633 } 634 mQueuedConnectUdpTransaction.Clear(); 635 } 636 637 void HttpConnectionUDP::DontReuse() { 638 LOG(("HttpConnectionUDP::DontReuse %p http3session=%p\n", this, 639 mHttp3Session.get())); 640 mDontReuse = true; 641 if (mHttp3Session) { 642 mHttp3Session->DontReuse(); 643 } 644 } 645 646 bool HttpConnectionUDP::TestJoinConnection(const nsACString& hostname, 647 int32_t port) { 648 if (mHttp3Session && CanDirectlyActivate()) { 649 return mHttp3Session->TestJoinConnection(hostname, port); 650 } 651 652 return false; 653 } 654 655 bool HttpConnectionUDP::JoinConnection(const nsACString& hostname, 656 int32_t port) { 657 if (mHttp3Session && CanDirectlyActivate()) { 658 return mHttp3Session->JoinConnection(hostname, port); 659 } 660 661 return false; 662 } 663 664 bool HttpConnectionUDP::CanReuse() { 665 if (NS_FAILED(mErrorBeforeConnect)) { 666 return false; 667 } 668 if (mDontReuse) { 669 return false; 670 } 671 672 if (mHttp3Session) { 673 return mHttp3Session->CanReuse(); 674 } 675 return false; 676 } 677 678 bool HttpConnectionUDP::CanDirectlyActivate() { 679 // return true if a new transaction can be addded to ths connection at any 680 // time through Activate(). In practice this means this is a healthy SPDY 681 // connection with room for more concurrent streams. 682 683 if (mHttp3Session) { 684 return CanReuse(); 685 } 686 return false; 687 } 688 689 //---------------------------------------------------------------------------- 690 // HttpConnectionUDP::nsAHttpConnection compatible methods 691 //---------------------------------------------------------------------------- 692 693 nsresult HttpConnectionUDP::OnHeadersAvailable(nsAHttpTransaction* trans, 694 nsHttpRequestHead* requestHead, 695 nsHttpResponseHead* responseHead, 696 bool* reset) { 697 LOG( 698 ("HttpConnectionUDP::OnHeadersAvailable [this=%p trans=%p " 699 "response-head=%p]\n", 700 this, trans, responseHead)); 701 702 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 703 NS_ENSURE_ARG_POINTER(trans); 704 MOZ_ASSERT(responseHead, "No response head?"); 705 706 DebugOnly<nsresult> rv = 707 responseHead->SetHeader(nsHttp::X_Firefox_Http3, mAlpnToken); 708 MOZ_ASSERT(NS_SUCCEEDED(rv)); 709 710 uint16_t responseStatus = responseHead->Status(); 711 nsHttpTransaction* hTrans = trans->QueryHttpTransaction(); 712 if (mState == HttpConnectionState::SETTING_UP_TUNNEL) { 713 HandleTunnelResponse(hTrans, responseStatus, reset); 714 return NS_OK; 715 } 716 717 // deal with 408 Server Timeouts 718 static const PRIntervalTime k1000ms = PR_MillisecondsToInterval(1000); 719 if (responseStatus == 408) { 720 // If this error could be due to a persistent connection reuse then 721 // we pass an error code of NS_ERROR_NET_RESET to 722 // trigger the transaction 'restart' mechanism. We tell it to reset its 723 // response headers so that it will be ready to receive the new response. 724 if (mIsReused && 725 ((PR_IntervalNow() - mHttp3Session->LastWriteTime()) < k1000ms)) { 726 CloseTransaction(mHttp3Session, NS_ERROR_NET_RESET); 727 *reset = true; 728 return NS_OK; 729 } 730 } 731 732 return NS_OK; 733 } 734 735 void HttpConnectionUDP::HandleTunnelResponse( 736 nsHttpTransaction* aHttpTransaction, uint16_t responseStatus, bool* reset) { 737 LOG(("HttpConnectionUDP::HandleTunnelResponse mIsInTunnel=%d", mIsInTunnel)); 738 MOZ_ASSERT(TunnelSetupInProgress()); 739 MOZ_ASSERT(mIsInTunnel); 740 741 if (responseStatus == 200) { 742 ChangeState(HttpConnectionState::REQUEST); 743 } 744 745 bool onlyConnect = mTransactionCaps & NS_HTTP_CONNECT_ONLY; 746 aHttpTransaction->OnProxyConnectComplete(responseStatus); 747 if (responseStatus == 200) { 748 LOG(("proxy CONNECT succeeded! onlyconnect=%d mIsInTunnel=%d\n", 749 onlyConnect, mIsInTunnel)); 750 // If we're only connecting, we don't need to reset the transaction 751 // state. We need to upgrade the socket now without doing the actual 752 // http request. 753 if (!onlyConnect) { 754 *reset = true; 755 } 756 757 for (const auto& trans : mQueuedConnectUdpTransaction) { 758 LOG(("add trans=%p", trans.get())); 759 if (!mHttp3Session->AddStream(trans, trans->Priority(), mCallbacks)) { 760 MOZ_ASSERT(false); // this cannot happen! 761 trans->Close(NS_ERROR_ABORT); 762 } 763 } 764 mQueuedConnectUdpTransaction.Clear(); 765 mProxyConnectSucceeded = true; 766 (void)ResumeSend(); 767 } else { 768 LOG(("proxy CONNECT failed! onlyconnect=%d\n", onlyConnect)); 769 aHttpTransaction->SetProxyConnectFailed(); 770 mQueuedConnectUdpTransaction.Clear(); 771 } 772 } 773 774 void HttpConnectionUDP::ResetTransaction(nsHttpTransaction* aHttpTransaction) { 775 LOG(("HttpConnectionUDP::ResetTransaction [this=%p mState=%d]\n", this, 776 static_cast<uint32_t>(mState))); 777 778 RefPtr<nsHttpConnectionInfo> wildCardProxyCi; 779 nsresult rv = mConnInfo->CreateWildCard(getter_AddRefs(wildCardProxyCi)); 780 if (NS_FAILED(rv)) { 781 CloseTransaction(mHttp3Session, rv); 782 aHttpTransaction->Close(rv); 783 return; 784 } 785 786 // Both Http3Session and nsHttpTransaction keeps a strong reference to a 787 // ConnectionHandle, which itself holds a strong ref back to the concrete 788 // connection, and when the last reference to the handle drops, the underlying 789 // connection is released. 790 // Notes: 791 // 1) SetConnection() may drop the previous 792 // connection reference, which can immediately destroy that handle. 793 // Avoid calling it unless we actually need to create/attach a handle. 794 // 2) For speculative connections, the transaction may already have a handle. 795 // Reuse it rather than creating a new one. 796 // 3) Only set the session's connection if it doesn't already have one. 797 if (!mHttp3Session->Connection()) { 798 if (!aHttpTransaction->Connection()) { 799 RefPtr<ConnectionHandle> handle = new ConnectionHandle(this); 800 aHttpTransaction->SetConnection(handle); 801 } 802 mHttp3Session->SetConnection(aHttpTransaction->Connection()); 803 } 804 aHttpTransaction->SetConnection(nullptr); 805 gHttpHandler->ConnMgr()->MoveToWildCardConnEntry(mConnInfo, wildCardProxyCi, 806 this); 807 mConnInfo = wildCardProxyCi; 808 aHttpTransaction->DoNotRemoveAltSvc(); 809 aHttpTransaction->Close(NS_ERROR_NET_RESET); 810 } 811 812 bool HttpConnectionUDP::IsReused() { return mIsReused; } 813 814 nsresult HttpConnectionUDP::TakeTransport( 815 nsISocketTransport** aTransport, nsIAsyncInputStream** aInputStream, 816 nsIAsyncOutputStream** aOutputStream) { 817 return NS_ERROR_FAILURE; 818 } 819 820 void HttpConnectionUDP::GetTLSSocketControl(nsITLSSocketControl** secinfo) { 821 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 822 LOG(("HttpConnectionUDP::GetTLSSocketControl http3Session=%p\n", 823 mHttp3Session.get())); 824 825 if (mHttp3Session && 826 NS_SUCCEEDED(mHttp3Session->GetTransactionTLSSocketControl(secinfo))) { 827 return; 828 } 829 830 *secinfo = nullptr; 831 } 832 833 nsresult HttpConnectionUDP::PushBack(const char* data, uint32_t length) { 834 LOG(("HttpConnectionUDP::PushBack [this=%p, length=%d]\n", this, length)); 835 836 return NS_ERROR_UNEXPECTED; 837 } 838 839 class HttpConnectionUDPForceIO : public Runnable { 840 public: 841 HttpConnectionUDPForceIO(HttpConnectionUDP* aConn, bool doRecv) 842 : Runnable("net::HttpConnectionUDPForceIO"), 843 mConn(aConn), 844 mDoRecv(doRecv) {} 845 846 NS_IMETHOD Run() override { 847 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 848 849 if (mDoRecv) { 850 return mConn->RecvData(); 851 } 852 853 MOZ_ASSERT(mConn->mForceSendPending); 854 mConn->mForceSendPending = false; 855 856 return mConn->SendData(); 857 } 858 859 private: 860 RefPtr<HttpConnectionUDP> mConn; 861 bool mDoRecv; 862 }; 863 864 nsresult HttpConnectionUDP::ResumeSend() { 865 LOG(("HttpConnectionUDP::ResumeSend [this=%p]\n", this)); 866 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 867 RefPtr<HttpConnectionUDP> self(this); 868 NS_DispatchToCurrentThread( 869 NS_NewRunnableFunction("HttpConnectionUDP::CallSendData", 870 [self{std::move(self)}]() { self->SendData(); })); 871 return NS_OK; 872 } 873 874 nsresult HttpConnectionUDP::ResumeRecv() { return NS_OK; } 875 876 void HttpConnectionUDP::ForceSendIO(nsITimer* aTimer, void* aClosure) { 877 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 878 HttpConnectionUDP* self = static_cast<HttpConnectionUDP*>(aClosure); 879 MOZ_ASSERT(aTimer == self->mForceSendTimer); 880 self->mForceSendTimer = nullptr; 881 NS_DispatchToCurrentThread(new HttpConnectionUDPForceIO(self, false)); 882 } 883 884 nsresult HttpConnectionUDP::MaybeForceSendIO() { 885 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 886 // due to bug 1213084 sometimes real I/O events do not get serviced when 887 // NSPR derived I/O events are ready and this can cause a deadlock with 888 // https over https proxying. Normally we would expect the write callback to 889 // be invoked before this timer goes off, but set it at the old windows 890 // tick interval (kForceDelay) as a backup for those circumstances. 891 static const uint32_t kForceDelay = 17; // ms 892 893 if (mForceSendPending) { 894 return NS_OK; 895 } 896 MOZ_ASSERT(!mForceSendTimer); 897 mForceSendPending = true; 898 return NS_NewTimerWithFuncCallback( 899 getter_AddRefs(mForceSendTimer), HttpConnectionUDP::ForceSendIO, this, 900 kForceDelay, nsITimer::TYPE_ONE_SHOT, 901 "net::HttpConnectionUDP::MaybeForceSendIO"_ns); 902 } 903 904 // trigger an asynchronous read 905 nsresult HttpConnectionUDP::ForceRecv() { 906 LOG(("HttpConnectionUDP::ForceRecv [this=%p]\n", this)); 907 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 908 909 return NS_DispatchToCurrentThread(new HttpConnectionUDPForceIO(this, true)); 910 } 911 912 // trigger an asynchronous write 913 nsresult HttpConnectionUDP::ForceSend() { 914 LOG(("HttpConnectionUDP::ForceSend [this=%p]\n", this)); 915 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 916 917 return MaybeForceSendIO(); 918 } 919 920 HttpVersion HttpConnectionUDP::Version() { return HttpVersion::v3_0; } 921 922 PRIntervalTime HttpConnectionUDP::LastWriteTime() { 923 return mHttp3Session->LastWriteTime(); 924 } 925 926 //----------------------------------------------------------------------------- 927 // HttpConnectionUDP <private> 928 //----------------------------------------------------------------------------- 929 930 void HttpConnectionUDP::CloseTransaction(nsAHttpTransaction* trans, 931 nsresult reason, bool aIsShutdown) { 932 LOG(("HttpConnectionUDP::CloseTransaction[this=%p trans=%p reason=%" PRIx32 933 "]\n", 934 this, trans, static_cast<uint32_t>(reason))); 935 936 // CloseTransaction may be called by nsHttpTransaction when a fallback 937 // is needed. In this case, the transaction is still in mQueuedTransaction 938 // and the proxy connect is still in progress. 939 bool transInQueue = mQueuedHttpConnectTransaction.Contains(trans) || 940 mQueuedConnectUdpTransaction.Contains(trans); 941 MOZ_ASSERT(trans == mHttp3Session || 942 (transInQueue && IsProxyConnectInProgress())); 943 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 944 945 if (NS_SUCCEEDED(reason) || (reason == NS_BASE_STREAM_CLOSED)) { 946 return; 947 } 948 949 // The connection and security errors clear out alt-svc mappings 950 // in case any previously validated ones are now invalid 951 if (((reason == NS_ERROR_NET_RESET) || 952 (NS_ERROR_GET_MODULE(reason) == NS_ERROR_MODULE_SECURITY)) && 953 mConnInfo && !(mTransactionCaps & NS_HTTP_ERROR_SOFTLY)) { 954 gHttpHandler->ClearHostMapping(mConnInfo); 955 } 956 957 mDontReuse = true; 958 if (mHttp3Session) { 959 // When proxy connnect failed, we call Http3Session::SetCleanShutdown to 960 // force Http3Session to release this UDP connection. 961 mHttp3Session->SetCleanShutdown(aIsShutdown || transInQueue || 962 (mIsInTunnel && !mProxyConnectSucceeded)); 963 mHttp3Session->Close(reason); 964 if (!mHttp3Session->IsClosed()) { 965 // During closing phase we still keep mHttp3Session session, 966 // to resend CLOSE_CONNECTION frames. 967 return; 968 } 969 } 970 971 mHttp3Session = nullptr; 972 973 { 974 MutexAutoLock lock(mCallbacksLock); 975 mCallbacks = nullptr; 976 } 977 978 Close(reason, aIsShutdown); 979 980 // flag the connection as reused here for convenience sake. certainly 981 // it might be going away instead ;-) 982 mIsReused = true; 983 } 984 985 void HttpConnectionUDP::OnQuicTimeoutExpired() { 986 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 987 LOG(("HttpConnectionUDP::OnQuicTimeoutExpired [this=%p]\n", this)); 988 // if the transaction was dropped... 989 if (!mHttp3Session) { 990 LOG((" no transaction; ignoring event\n")); 991 return; 992 } 993 994 nsresult rv = mHttp3Session->ProcessOutputAndEvents(mSocket); 995 if (NS_FAILED(rv)) { 996 CloseTransaction(mHttp3Session, rv); 997 } 998 } 999 1000 //----------------------------------------------------------------------------- 1001 // HttpConnectionUDP::nsISupports 1002 //----------------------------------------------------------------------------- 1003 1004 NS_IMPL_ADDREF(HttpConnectionUDP) 1005 NS_IMPL_RELEASE(HttpConnectionUDP) 1006 1007 NS_INTERFACE_MAP_BEGIN(HttpConnectionUDP) 1008 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 1009 NS_INTERFACE_MAP_ENTRY(nsIUDPSocketSyncListener) 1010 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) 1011 NS_INTERFACE_MAP_ENTRY(HttpConnectionBase) 1012 NS_INTERFACE_MAP_ENTRY_CONCRETE(HttpConnectionUDP) 1013 NS_INTERFACE_MAP_END 1014 1015 void HttpConnectionUDP::NotifyDataRead() { 1016 mExperienceState |= ConnectionExperienceState::First_Response_Received; 1017 } 1018 1019 void HttpConnectionUDP::NotifyDataWrite() { 1020 mExperienceState |= ConnectionExperienceState::First_Request_Sent; 1021 } 1022 1023 // called on the socket transport thread 1024 nsresult HttpConnectionUDP::RecvData() { 1025 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1026 1027 // if the transaction was dropped... 1028 if (!mHttp3Session) { 1029 LOG((" no Http3Session; ignoring event\n")); 1030 return NS_OK; 1031 } 1032 1033 nsresult rv = mHttp3Session->RecvData(mSocket); 1034 LOG(("HttpConnectionUDP::OnInputReady %p rv=%" PRIx32, this, 1035 static_cast<uint32_t>(rv))); 1036 1037 if (NS_FAILED(rv)) CloseTransaction(mHttp3Session, rv); 1038 1039 return NS_OK; 1040 } 1041 1042 // called on the socket transport thread 1043 nsresult HttpConnectionUDP::SendData() { 1044 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1045 1046 // if the transaction was dropped... 1047 if (!mHttp3Session) { 1048 LOG((" no Http3Session; ignoring event\n")); 1049 return NS_OK; 1050 } 1051 1052 nsresult rv = mHttp3Session->SendData(mSocket); 1053 LOG(("HttpConnectionUDP::OnInputReady %p rv=%" PRIx32, this, 1054 static_cast<uint32_t>(rv))); 1055 1056 if (NS_FAILED(rv)) CloseTransaction(mHttp3Session, rv); 1057 1058 return NS_OK; 1059 } 1060 1061 //----------------------------------------------------------------------------- 1062 // HttpConnectionUDP::nsIInterfaceRequestor 1063 //----------------------------------------------------------------------------- 1064 1065 // not called on the socket transport thread 1066 NS_IMETHODIMP 1067 HttpConnectionUDP::GetInterface(const nsIID& iid, void** result) { 1068 // NOTE: This function is only called on the UI thread via sync proxy from 1069 // the socket transport thread. If that weren't the case, then we'd 1070 // have to worry about the possibility of mHttp3Session going away 1071 // part-way through this function call. See CloseTransaction. 1072 1073 // NOTE - there is a bug here, the call to getinterface is proxied off the 1074 // nss thread, not the ui thread as the above comment says. So there is 1075 // indeed a chance of mSession going away. bug 615342 1076 1077 MOZ_ASSERT(!OnSocketThread(), "on socket thread"); 1078 1079 nsCOMPtr<nsIInterfaceRequestor> callbacks; 1080 { 1081 MutexAutoLock lock(mCallbacksLock); 1082 callbacks = mCallbacks; 1083 } 1084 if (callbacks) return callbacks->GetInterface(iid, result); 1085 return NS_ERROR_NO_INTERFACE; 1086 } 1087 1088 void HttpConnectionUDP::SetEvent(nsresult aStatus) { 1089 switch (aStatus) { 1090 case NS_NET_STATUS_RESOLVING_HOST: 1091 mBootstrappedTimings.domainLookupStart = TimeStamp::Now(); 1092 break; 1093 case NS_NET_STATUS_RESOLVED_HOST: 1094 mBootstrappedTimings.domainLookupEnd = TimeStamp::Now(); 1095 break; 1096 case NS_NET_STATUS_CONNECTING_TO: 1097 mBootstrappedTimings.connectStart = TimeStamp::Now(); 1098 mBootstrappedTimings.secureConnectionStart = 1099 mBootstrappedTimings.connectStart; 1100 break; 1101 case NS_NET_STATUS_CONNECTED_TO: 1102 mBootstrappedTimings.connectEnd = TimeStamp::Now(); 1103 break; 1104 default: 1105 break; 1106 } 1107 } 1108 1109 bool HttpConnectionUDP::LastTransactionExpectedNoContent() { 1110 return mLastTransactionExpectedNoContent; 1111 } 1112 1113 void HttpConnectionUDP::SetLastTransactionExpectedNoContent(bool val) { 1114 mLastTransactionExpectedNoContent = val; 1115 } 1116 1117 bool HttpConnectionUDP::IsPersistent() { return !mDontReuse; } 1118 1119 nsAHttpTransaction* HttpConnectionUDP::Transaction() { return mHttp3Session; } 1120 1121 int64_t HttpConnectionUDP::BytesWritten() { 1122 if (!mHttp3Session) { 1123 return 0; 1124 } 1125 return mHttp3Session->GetBytesWritten(); 1126 } 1127 1128 NS_IMETHODIMP HttpConnectionUDP::OnPacketReceived(nsIUDPSocket* aSocket) { 1129 RecvData(); 1130 return NS_OK; 1131 } 1132 1133 NS_IMETHODIMP HttpConnectionUDP::OnStopListening(nsIUDPSocket* aSocket, 1134 nsresult aStatus) { 1135 // At this point, the UDP socket has already been closed. Set aIsShutdown to 1136 // true to ensure that mHttp3Session is also closed. 1137 CloseTransaction(mHttp3Session, aStatus, true); 1138 return NS_OK; 1139 } 1140 1141 nsresult HttpConnectionUDP::GetSelfAddr(NetAddr* addr) { 1142 if (mSelfAddr) { 1143 return mSelfAddr->GetNetAddr(addr); 1144 } 1145 return NS_ERROR_FAILURE; 1146 } 1147 1148 nsresult HttpConnectionUDP::GetPeerAddr(NetAddr* addr) { 1149 if (mPeerAddr) { 1150 return mPeerAddr->GetNetAddr(addr); 1151 } 1152 return NS_ERROR_FAILURE; 1153 } 1154 1155 bool HttpConnectionUDP::ResolvedByTRR() { return mResolvedByTRR; } 1156 1157 nsIRequest::TRRMode HttpConnectionUDP::EffectiveTRRMode() { 1158 return mEffectiveTRRMode; 1159 } 1160 1161 TRRSkippedReason HttpConnectionUDP::TRRSkipReason() { return mTRRSkipReason; } 1162 1163 Http3Stats HttpConnectionUDP::GetStats() { 1164 if (!mHttp3Session) { 1165 return Http3Stats(); 1166 } 1167 return mHttp3Session->GetStats(); 1168 } 1169 1170 } // namespace net 1171 } // namespace mozilla