ConnectionEntry.cpp (37800B)
1 /* vim:set ts=4 sw=2 sts=2 et cin: */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 // HttpLog.h should generally be included first 7 #include "HttpLog.h" 8 9 // Log on level :5, instead of default :4. 10 #undef LOG 11 #define LOG(args) LOG5(args) 12 #undef LOG_ENABLED 13 #define LOG_ENABLED() LOG5_ENABLED() 14 15 #include "ConnectionEntry.h" 16 #include "HttpConnectionUDP.h" 17 #include "nsQueryObject.h" 18 #include "mozilla/StaticPrefs_network.h" 19 #include "nsHttpHandler.h" 20 #include "mozilla/net/neqo_glue_ffi_generated.h" 21 22 namespace mozilla { 23 namespace net { 24 25 // ConnectionEntry 26 ConnectionEntry::~ConnectionEntry() { 27 LOG(("ConnectionEntry::~ConnectionEntry this=%p", this)); 28 29 MOZ_ASSERT(!mIdleConns.Length()); 30 MOZ_ASSERT(!mActiveConns.Length()); 31 MOZ_DIAGNOSTIC_ASSERT(!mDnsAndConnectSockets.Length()); 32 MOZ_ASSERT(!PendingQueueLength()); 33 MOZ_ASSERT(!UrgentStartQueueLength()); 34 MOZ_ASSERT(!mDoNotDestroy); 35 } 36 37 ConnectionEntry::ConnectionEntry(nsHttpConnectionInfo* ci) 38 : mConnInfo(ci), 39 mUsingSpdy(false), 40 mCanUseSpdy(true), 41 mPreferIPv4(false), 42 mPreferIPv6(false), 43 mUsedForConnection(false), 44 mDoNotDestroy(false) { 45 LOG(("ConnectionEntry::ConnectionEntry this=%p key=%s", this, 46 ci->HashKey().get())); 47 } 48 49 bool ConnectionEntry::AvailableForDispatchNow() { 50 if (mIdleConns.Length() && mIdleConns[0]->CanReuse()) { 51 return true; 52 } 53 54 return gHttpHandler->ConnMgr()->GetH2orH3ActiveConn(this, false, false) != 55 nullptr; 56 } 57 58 uint32_t ConnectionEntry::UnconnectedDnsAndConnectSockets() const { 59 uint32_t unconnectedDnsAndConnectSockets = 0; 60 for (uint32_t i = 0; i < mDnsAndConnectSockets.Length(); ++i) { 61 if (!mDnsAndConnectSockets[i]->HasConnected()) { 62 ++unconnectedDnsAndConnectSockets; 63 } 64 } 65 return unconnectedDnsAndConnectSockets; 66 } 67 68 void ConnectionEntry::InsertIntoDnsAndConnectSockets( 69 DnsAndConnectSocket* sock) { 70 mDnsAndConnectSockets.AppendElement(sock); 71 gHttpHandler->ConnMgr()->IncreaseNumDnsAndConnectSockets(); 72 } 73 74 void ConnectionEntry::RemoveDnsAndConnectSocket(DnsAndConnectSocket* dnsAndSock, 75 bool abandon) { 76 if (abandon) { 77 dnsAndSock->Abandon(); 78 } 79 if (mDnsAndConnectSockets.RemoveElement(dnsAndSock)) { 80 gHttpHandler->ConnMgr()->DecreaseNumDnsAndConnectSockets(); 81 } 82 83 if (!UnconnectedDnsAndConnectSockets()) { 84 // perhaps this reverted RestrictConnections() 85 // use the PostEvent version of processpendingq to avoid 86 // altering the pending q vector from an arbitrary stack 87 nsresult rv = gHttpHandler->ConnMgr()->ProcessPendingQ(mConnInfo); 88 if (NS_FAILED(rv)) { 89 LOG( 90 ("ConnectionEntry::RemoveDnsAndConnectSocket\n" 91 " failed to process pending queue\n")); 92 } 93 } 94 } 95 96 void ConnectionEntry::CloseAllDnsAndConnectSockets() { 97 for (const auto& dnsAndSock : mDnsAndConnectSockets) { 98 dnsAndSock->Abandon(); 99 gHttpHandler->ConnMgr()->DecreaseNumDnsAndConnectSockets(); 100 } 101 mDnsAndConnectSockets.Clear(); 102 nsresult rv = gHttpHandler->ConnMgr()->ProcessPendingQ(mConnInfo); 103 if (NS_FAILED(rv)) { 104 LOG( 105 ("ConnectionEntry::CloseAllDnsAndConnectSockets\n" 106 " failed to process pending queue\n")); 107 } 108 } 109 110 void ConnectionEntry::DisallowHttp2() { 111 mCanUseSpdy = false; 112 113 // If we have any spdy connections, we want to go ahead and close them when 114 // they're done so we can free up some connections. 115 for (uint32_t i = 0; i < mActiveConns.Length(); ++i) { 116 if (mActiveConns[i]->UsingSpdy()) { 117 mActiveConns[i]->DontReuse(); 118 } 119 } 120 for (uint32_t i = 0; i < mIdleConns.Length(); ++i) { 121 if (mIdleConns[i]->UsingSpdy()) { 122 mIdleConns[i]->DontReuse(); 123 } 124 } 125 126 // Can't coalesce if we're not using spdy 127 mCoalescingKeys.Clear(); 128 mAddresses.Clear(); 129 } 130 131 void ConnectionEntry::DontReuseHttp3Conn() { 132 MOZ_ASSERT(mConnInfo->IsHttp3()); 133 134 // If we have any spdy connections, we want to go ahead and close them when 135 // they're done so we can free up some connections. 136 for (uint32_t i = 0; i < mActiveConns.Length(); ++i) { 137 mActiveConns[i]->DontReuse(); 138 } 139 140 // Can't coalesce if we're not using http3 141 mCoalescingKeys.Clear(); 142 mAddresses.Clear(); 143 } 144 145 void ConnectionEntry::RecordIPFamilyPreference(uint16_t family) { 146 LOG(("ConnectionEntry::RecordIPFamilyPreference %p, af=%u", this, family)); 147 148 if (family == PR_AF_INET && !mPreferIPv6) { 149 mPreferIPv4 = true; 150 } 151 152 if (family == PR_AF_INET6 && !mPreferIPv4) { 153 mPreferIPv6 = true; 154 } 155 156 LOG((" %p prefer ipv4=%d, ipv6=%d", this, (bool)mPreferIPv4, 157 (bool)mPreferIPv6)); 158 } 159 160 void ConnectionEntry::ResetIPFamilyPreference() { 161 LOG(("ConnectionEntry::ResetIPFamilyPreference %p", this)); 162 163 mPreferIPv4 = false; 164 mPreferIPv6 = false; 165 } 166 167 bool net::ConnectionEntry::PreferenceKnown() const { 168 return (bool)mPreferIPv4 || (bool)mPreferIPv6; 169 } 170 171 size_t ConnectionEntry::PendingQueueLength() const { 172 return mPendingQ.PendingQueueLength(); 173 } 174 175 size_t ConnectionEntry::PendingQueueLengthForWindow(uint64_t windowId) const { 176 return mPendingQ.PendingQueueLengthForWindow(windowId); 177 } 178 179 void ConnectionEntry::AppendPendingUrgentStartQ( 180 nsTArray<RefPtr<PendingTransactionInfo>>& result) { 181 mPendingQ.AppendPendingUrgentStartQ(result); 182 } 183 184 void ConnectionEntry::AppendPendingQForFocusedWindow( 185 uint64_t windowId, nsTArray<RefPtr<PendingTransactionInfo>>& result, 186 uint32_t maxCount) { 187 mPendingQ.AppendPendingQForFocusedWindow(windowId, result, maxCount); 188 LOG( 189 ("ConnectionEntry::AppendPendingQForFocusedWindow [ci=%s], " 190 "pendingQ count=%zu for focused window (id=%" PRIu64 ")\n", 191 mConnInfo->HashKey().get(), result.Length(), windowId)); 192 } 193 194 void ConnectionEntry::AppendPendingQForNonFocusedWindows( 195 uint64_t windowId, nsTArray<RefPtr<PendingTransactionInfo>>& result, 196 uint32_t maxCount) { 197 mPendingQ.AppendPendingQForNonFocusedWindows(windowId, result, maxCount); 198 LOG( 199 ("ConnectionEntry::AppendPendingQForNonFocusedWindows [ci=%s], " 200 "pendingQ count=%zu for non focused window\n", 201 mConnInfo->HashKey().get(), result.Length())); 202 } 203 204 void ConnectionEntry::RemoveEmptyPendingQ() { mPendingQ.RemoveEmptyPendingQ(); } 205 206 void ConnectionEntry::InsertTransactionSorted( 207 nsTArray<RefPtr<PendingTransactionInfo>>& pendingQ, 208 PendingTransactionInfo* pendingTransInfo, 209 bool aInsertAsFirstForTheSamePriority /*= false*/) { 210 mPendingQ.InsertTransactionSorted(pendingQ, pendingTransInfo, 211 aInsertAsFirstForTheSamePriority); 212 } 213 214 void ConnectionEntry::ReschedTransaction(nsHttpTransaction* aTrans) { 215 mPendingQ.ReschedTransaction(aTrans); 216 } 217 218 void ConnectionEntry::InsertTransaction( 219 PendingTransactionInfo* pendingTransInfo, 220 bool aInsertAsFirstForTheSamePriority /* = false */) { 221 mPendingQ.InsertTransaction(pendingTransInfo, 222 aInsertAsFirstForTheSamePriority); 223 pendingTransInfo->Transaction()->OnPendingQueueInserted(mConnInfo->HashKey()); 224 } 225 226 nsTArray<RefPtr<PendingTransactionInfo>>* 227 ConnectionEntry::GetTransactionPendingQHelper(nsAHttpTransaction* trans) { 228 return mPendingQ.GetTransactionPendingQHelper(trans); 229 } 230 231 bool ConnectionEntry::RestrictConnections() { 232 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 233 234 if (AvailableForDispatchNow()) { 235 // this might be a h2/spdy connection in this connection entry that 236 // is able to be immediately muxxed, or it might be one that 237 // was found in the same state through a coalescing hash 238 LOG( 239 ("ConnectionEntry::RestrictConnections %p %s restricted due to " 240 "active >=h2\n", 241 this, mConnInfo->HashKey().get())); 242 return true; 243 } 244 245 // If this host is trying to negotiate a SPDY session right now, 246 // don't create any new ssl connections until the result of the 247 // negotiation is known. 248 249 bool doRestrict = mConnInfo->FirstHopSSL() && 250 StaticPrefs::network_http_http2_enabled() && mUsingSpdy && 251 (mDnsAndConnectSockets.Length() || mActiveConns.Length()); 252 253 // If there are no restrictions, we are done 254 if (!doRestrict) { 255 return false; 256 } 257 258 // If the restriction is based on a tcp handshake in progress 259 // let that connect and then see if it was SPDY or not 260 if (UnconnectedDnsAndConnectSockets()) { 261 return true; 262 } 263 264 // There is a concern that a host is using a mix of HTTP/1 and SPDY. 265 // In that case we don't want to restrict connections just because 266 // there is a single active HTTP/1 session in use. 267 // When a tunnel is used, we should avoid bypassing connection restrictions. 268 // Otherwise, we might create too many unused tunnels. 269 if (mUsingSpdy && mActiveConns.Length() && 270 !(mConnInfo->UsingHttpsProxy() && mConnInfo->UsingConnect())) { 271 bool confirmedRestrict = false; 272 for (uint32_t index = 0; index < mActiveConns.Length(); ++index) { 273 HttpConnectionBase* conn = mActiveConns[index]; 274 RefPtr<nsHttpConnection> connTCP = do_QueryObject(conn); 275 if ((connTCP && !connTCP->ReportedNPN()) || conn->CanDirectlyActivate()) { 276 confirmedRestrict = true; 277 break; 278 } 279 } 280 doRestrict = confirmedRestrict; 281 if (!confirmedRestrict) { 282 LOG( 283 ("nsHttpConnectionMgr spdy connection restriction to " 284 "%s bypassed.\n", 285 mConnInfo->Origin())); 286 } 287 } 288 return doRestrict; 289 } 290 291 uint32_t ConnectionEntry::TotalActiveConnections() const { 292 // Add in the in-progress tcp connections, we will assume they are 293 // keepalive enabled. 294 // Exclude DnsAndConnectSocket's that has already created a usable connection. 295 // This prevents the limit being stuck on ipv6 connections that 296 // eventually time out after typical 21 seconds of no ACK+SYN reply. 297 return mActiveConns.Length() + UnconnectedDnsAndConnectSockets(); 298 } 299 300 size_t ConnectionEntry::UrgentStartQueueLength() { 301 return mPendingQ.UrgentStartQueueLength(); 302 } 303 304 void ConnectionEntry::PrintPendingQ() { mPendingQ.PrintPendingQ(); } 305 306 void ConnectionEntry::Compact() { 307 mIdleConns.Compact(); 308 mActiveConns.Compact(); 309 mPendingQ.Compact(); 310 } 311 312 void ConnectionEntry::RemoveFromIdleConnectionsIndex(size_t inx) { 313 mIdleConns.RemoveElementAt(inx); 314 gHttpHandler->ConnMgr()->DecrementNumIdleConns(); 315 } 316 317 bool ConnectionEntry::RemoveFromIdleConnections(nsHttpConnection* conn) { 318 if (!mIdleConns.RemoveElement(conn)) { 319 return false; 320 } 321 322 gHttpHandler->ConnMgr()->DecrementNumIdleConns(); 323 return true; 324 } 325 326 void ConnectionEntry::CancelAllTransactions(nsresult reason) { 327 mPendingQ.CancelAllTransactions(reason); 328 } 329 330 nsresult ConnectionEntry::CloseIdleConnection(nsHttpConnection* conn) { 331 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 332 333 RefPtr<nsHttpConnection> deleteProtector(conn); 334 if (!RemoveFromIdleConnections(conn)) { 335 return NS_ERROR_UNEXPECTED; 336 } 337 338 // The connection is closed immediately no need to call EndIdleMonitoring. 339 conn->Close(NS_ERROR_ABORT); 340 return NS_OK; 341 } 342 343 void ConnectionEntry::CloseIdleConnections() { 344 while (mIdleConns.Length()) { 345 RefPtr<nsHttpConnection> conn(mIdleConns[0]); 346 RemoveFromIdleConnectionsIndex(0); 347 // The connection is closed immediately no need to call EndIdleMonitoring. 348 conn->Close(NS_ERROR_ABORT); 349 } 350 } 351 352 void ConnectionEntry::CloseIdleConnections(uint32_t maxToClose) { 353 uint32_t closed = 0; 354 while (mIdleConns.Length() && (closed < maxToClose)) { 355 RefPtr<nsHttpConnection> conn(mIdleConns[0]); 356 RemoveFromIdleConnectionsIndex(0); 357 // The connection is closed immediately no need to call EndIdleMonitoring. 358 conn->Close(NS_ERROR_ABORT); 359 closed++; 360 } 361 } 362 363 void ConnectionEntry::CloseExtendedCONNECTConnections() { 364 while (mExtendedCONNECTConns.Length()) { 365 RefPtr<HttpConnectionBase> conn(mExtendedCONNECTConns[0]); 366 mExtendedCONNECTConns.RemoveElementAt(0); 367 368 // safe to close connection since we are on the socket thread 369 // closing via transaction to break connection/transaction bond 370 conn->CloseTransaction(conn->Transaction(), NS_ERROR_ABORT, true); 371 } 372 } 373 374 nsresult ConnectionEntry::RemoveIdleConnection(nsHttpConnection* conn) { 375 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 376 377 if (!RemoveFromIdleConnections(conn)) { 378 return NS_ERROR_UNEXPECTED; 379 } 380 381 conn->EndIdleMonitoring(); 382 return NS_OK; 383 } 384 385 bool ConnectionEntry::IsInIdleConnections(HttpConnectionBase* conn) { 386 RefPtr<nsHttpConnection> connTCP = do_QueryObject(conn); 387 return connTCP && mIdleConns.Contains(connTCP); 388 } 389 390 already_AddRefed<nsHttpConnection> ConnectionEntry::GetIdleConnection( 391 bool respectUrgency, bool urgentTrans, bool* onlyUrgent) { 392 RefPtr<nsHttpConnection> conn; 393 size_t index = 0; 394 while (!conn && (mIdleConns.Length() > index)) { 395 conn = mIdleConns[index]; 396 397 if (!conn->CanReuse()) { 398 RemoveFromIdleConnectionsIndex(index); 399 LOG((" dropping stale connection: [conn=%p]\n", conn.get())); 400 conn->Close(NS_ERROR_ABORT); 401 conn = nullptr; 402 continue; 403 } 404 405 // non-urgent transactions can only be dispatched on non-urgent 406 // started or used connections. 407 if (respectUrgency && conn->IsUrgentStartPreferred() && !urgentTrans) { 408 LOG((" skipping urgent: [conn=%p]", conn.get())); 409 conn = nullptr; 410 ++index; 411 continue; 412 } 413 414 *onlyUrgent = false; 415 416 RemoveFromIdleConnectionsIndex(index); 417 conn->EndIdleMonitoring(); 418 LOG((" reusing connection: [conn=%p]\n", conn.get())); 419 } 420 421 return conn.forget(); 422 } 423 424 nsresult ConnectionEntry::RemoveActiveConnection(HttpConnectionBase* conn) { 425 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 426 427 if (!mActiveConns.RemoveElement(conn)) { 428 return NS_ERROR_UNEXPECTED; 429 } 430 conn->SetOwner(nullptr); 431 gHttpHandler->ConnMgr()->DecrementActiveConnCount(conn); 432 433 return NS_OK; 434 } 435 436 nsresult ConnectionEntry::RemovePendingConnection(HttpConnectionBase* conn) { 437 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 438 439 if (!mPendingConns.RemoveElement(conn)) { 440 return NS_ERROR_UNEXPECTED; 441 } 442 443 return NS_OK; 444 } 445 446 void ConnectionEntry::ClosePersistentConnections() { 447 LOG(("ConnectionEntry::ClosePersistentConnections [ci=%s]\n", 448 mConnInfo->HashKey().get())); 449 CloseIdleConnections(); 450 451 int32_t activeCount = mActiveConns.Length(); 452 for (int32_t i = 0; i < activeCount; i++) { 453 mActiveConns[i]->DontReuse(); 454 } 455 456 mCoalescingKeys.Clear(); 457 mAddresses.Clear(); 458 } 459 460 uint32_t ConnectionEntry::PruneDeadConnections() { 461 uint32_t timeToNextExpire = UINT32_MAX; 462 463 for (int32_t len = mIdleConns.Length(); len > 0; --len) { 464 int32_t idx = len - 1; 465 RefPtr<nsHttpConnection> conn(mIdleConns[idx]); 466 if (!conn->CanReuse()) { 467 RemoveFromIdleConnectionsIndex(idx); 468 // The connection is closed immediately no need to call 469 // EndIdleMonitoring. 470 conn->Close(NS_ERROR_ABORT); 471 } else { 472 timeToNextExpire = std::min(timeToNextExpire, conn->TimeToLive()); 473 } 474 } 475 476 if (mUsingSpdy) { 477 for (uint32_t i = 0; i < mActiveConns.Length(); ++i) { 478 RefPtr<nsHttpConnection> connTCP = do_QueryObject(mActiveConns[i]); 479 // Http3 has its own timers, it is not using this one. 480 if (connTCP && connTCP->UsingSpdy()) { 481 if (!connTCP->CanReuse()) { 482 // Marking it don't-reuse will create an active 483 // tear down if the spdy session is idle. 484 connTCP->DontReuse(); 485 } else { 486 timeToNextExpire = std::min(timeToNextExpire, connTCP->TimeToLive()); 487 } 488 } 489 } 490 } 491 492 return timeToNextExpire; 493 } 494 495 void ConnectionEntry::MakeConnectionPendingAndDontReuse( 496 HttpConnectionBase* conn) { 497 gHttpHandler->ConnMgr()->DecrementActiveConnCount(conn); 498 mPendingConns.AppendElement(conn); 499 // After DontReuse(), the connection will be closed after the last 500 // transition is done. 501 conn->DontReuse(); 502 LOG(("Move active connection to pending list [conn=%p]\n", conn)); 503 } 504 505 template <typename ConnType> 506 static void CheckForTrafficForConns(nsTArray<RefPtr<ConnType>>& aConns, 507 bool aCheck) { 508 for (uint32_t index = 0; index < aConns.Length(); ++index) { 509 RefPtr<nsHttpConnection> conn = do_QueryObject(aConns[index]); 510 if (conn) { 511 conn->CheckForTraffic(aCheck); 512 } 513 } 514 } 515 516 void ConnectionEntry::VerifyTraffic() { 517 if (!mConnInfo->IsHttp3()) { 518 CheckForTrafficForConns(mPendingConns, true); 519 // Iterate the idle connections and unmark them for traffic checks. 520 CheckForTrafficForConns(mIdleConns, false); 521 } 522 523 uint32_t numConns = mActiveConns.Length(); 524 if (numConns) { 525 // Walk the list backwards to allow us to remove entries easily. 526 for (int index = numConns - 1; index >= 0; index--) { 527 RefPtr<nsHttpConnection> conn = do_QueryObject(mActiveConns[index]); 528 RefPtr<HttpConnectionUDP> connUDP = do_QueryObject(mActiveConns[index]); 529 if (conn) { 530 conn->CheckForTraffic(true); 531 if (conn->EverUsedSpdy() && 532 StaticPrefs:: 533 network_http_move_to_pending_list_after_network_change()) { 534 mActiveConns.RemoveElementAt(index); 535 conn->SetOwner(nullptr); 536 MakeConnectionPendingAndDontReuse(conn); 537 } 538 } else if (connUDP && 539 StaticPrefs:: 540 network_http_move_to_pending_list_after_network_change()) { 541 mActiveConns.RemoveElementAt(index); 542 connUDP->SetOwner(nullptr); 543 MakeConnectionPendingAndDontReuse(connUDP); 544 } 545 } 546 } 547 } 548 549 void ConnectionEntry::InsertIntoIdleConnections_internal( 550 nsHttpConnection* conn) { 551 uint32_t idx; 552 for (idx = 0; idx < mIdleConns.Length(); idx++) { 553 nsHttpConnection* idleConn = mIdleConns[idx]; 554 if (idleConn->MaxBytesRead() < conn->MaxBytesRead()) { 555 break; 556 } 557 } 558 559 mIdleConns.InsertElementAt(idx, conn); 560 } 561 562 void ConnectionEntry::InsertIntoIdleConnections(nsHttpConnection* conn) { 563 InsertIntoIdleConnections_internal(conn); 564 gHttpHandler->ConnMgr()->NewIdleConnectionAdded(conn->TimeToLive()); 565 conn->BeginIdleMonitoring(); 566 } 567 568 bool ConnectionEntry::IsInActiveConns(HttpConnectionBase* conn) { 569 return mActiveConns.Contains(conn); 570 } 571 572 void ConnectionEntry::InsertIntoActiveConns(HttpConnectionBase* conn) { 573 mActiveConns.AppendElement(conn); 574 conn->SetOwner(this); 575 gHttpHandler->ConnMgr()->IncrementActiveConnCount(); 576 } 577 578 bool ConnectionEntry::IsInExtendedCONNECTConns(HttpConnectionBase* conn) { 579 return mExtendedCONNECTConns.Contains(conn); 580 } 581 582 void ConnectionEntry::InsertIntoExtendedCONNECTConns(HttpConnectionBase* conn) { 583 // no incrementing of connection count since it is a tunneled connection 584 mExtendedCONNECTConns.AppendElement(conn); 585 } 586 587 void ConnectionEntry::RemoveExtendedCONNECTConns(HttpConnectionBase* conn) { 588 mExtendedCONNECTConns.RemoveElement(conn); 589 } 590 591 void ConnectionEntry::MakeAllDontReuseExcept(HttpConnectionBase* conn) { 592 for (uint32_t index = 0; index < mActiveConns.Length(); ++index) { 593 HttpConnectionBase* otherConn = mActiveConns[index]; 594 if (otherConn != conn) { 595 LOG( 596 ("ConnectionEntry::MakeAllDontReuseExcept shutting down old " 597 "connection (%p) " 598 "because new " 599 "spdy connection (%p) takes precedence\n", 600 otherConn, conn)); 601 otherConn->SetCloseReason( 602 ConnectionCloseReason::CLOSE_EXISTING_CONN_FOR_COALESCING); 603 otherConn->DontReuse(); 604 } 605 } 606 607 // Cancel any other pending connections - their associated transactions 608 // are in the pending queue and will be dispatched onto this new connection 609 CloseAllDnsAndConnectSockets(); 610 } 611 612 bool ConnectionEntry::FindConnToClaim( 613 PendingTransactionInfo* pendingTransInfo) { 614 nsHttpTransaction* trans = pendingTransInfo->Transaction(); 615 616 for (const auto& dnsAndSock : mDnsAndConnectSockets) { 617 if (dnsAndSock->AcceptsTransaction(trans) && dnsAndSock->Claim()) { 618 pendingTransInfo->RememberDnsAndConnectSocket(dnsAndSock); 619 // We've found a speculative connection or a connection that 620 // is free to be used in the DnsAndConnectSockets list. 621 // A free to be used connection is a connection that was 622 // open for a concrete transaction, but that trunsaction 623 // ended up using another connection. 624 LOG( 625 ("ConnectionEntry::FindConnToClaim [ci = %s]\n" 626 "Found a speculative or a free-to-use DnsAndConnectSocket\n", 627 mConnInfo->HashKey().get())); 628 629 // return OK because we have essentially opened a new connection 630 // by converting a speculative DnsAndConnectSockets to general use 631 return true; 632 } 633 } 634 635 // consider null transactions that are being used to drive the ssl handshake 636 // if the transaction creating this connection can re-use persistent 637 // connections 638 if (trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) { 639 uint32_t activeLength = mActiveConns.Length(); 640 for (uint32_t i = 0; i < activeLength; i++) { 641 if (pendingTransInfo->TryClaimingActiveConn(mActiveConns[i])) { 642 LOG( 643 ("ConnectionEntry::FindConnectingSocket [ci = %s] " 644 "Claiming a null transaction for later use\n", 645 mConnInfo->HashKey().get())); 646 return true; 647 } 648 } 649 } 650 return false; 651 } 652 653 bool ConnectionEntry::MakeFirstActiveSpdyConnDontReuse() { 654 if (!mUsingSpdy) { 655 return false; 656 } 657 658 for (uint32_t index = 0; index < mActiveConns.Length(); ++index) { 659 HttpConnectionBase* conn = mActiveConns[index]; 660 if (conn->UsingSpdy() && conn->CanReuse()) { 661 conn->DontReuse(); 662 return true; 663 } 664 } 665 return false; 666 } 667 668 // Return an active h2 or h3 connection 669 // that can be directly activated or null. 670 HttpConnectionBase* ConnectionEntry::GetH2orH3ActiveConn() { 671 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 672 673 HttpConnectionBase* experienced = nullptr; 674 HttpConnectionBase* noExperience = nullptr; 675 uint32_t activeLen = mActiveConns.Length(); 676 677 // activeLen should generally be 1.. this is a setup race being resolved 678 // take a conn who can activate and is experienced 679 for (uint32_t index = 0; index < activeLen; ++index) { 680 HttpConnectionBase* tmp = mActiveConns[index]; 681 if (tmp->CanDirectlyActivate()) { 682 if (tmp->IsExperienced()) { 683 experienced = tmp; 684 break; 685 } 686 noExperience = tmp; // keep looking for a better option 687 } 688 } 689 690 // if that worked, cleanup anything else and exit 691 if (experienced) { 692 for (uint32_t index = 0; index < activeLen; ++index) { 693 HttpConnectionBase* tmp = mActiveConns[index]; 694 // in the case where there is a functional h2 session, drop the others 695 if (tmp != experienced) { 696 tmp->DontReuse(); 697 } 698 } 699 700 LOG( 701 ("GetH2orH3ActiveConn() request for ent %p %s " 702 "found an active experienced connection %p in native connection " 703 "entry\n", 704 this, mConnInfo->HashKey().get(), experienced)); 705 return experienced; 706 } 707 708 if (noExperience) { 709 LOG( 710 ("GetH2orH3ActiveConn() request for ent %p %s " 711 "found an active but inexperienced connection %p in native connection " 712 "entry\n", 713 this, mConnInfo->HashKey().get(), noExperience)); 714 return noExperience; 715 } 716 717 return nullptr; 718 } 719 720 already_AddRefed<nsHttpConnection> ConnectionEntry::GetH2TunnelActiveConn() { 721 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 722 723 for (const auto& conn : mActiveConns) { 724 RefPtr<nsHttpConnection> connTCP = do_QueryObject(conn); 725 if (connTCP && connTCP->UsingSpdy() && connTCP->CanDirectlyActivate()) { 726 LOG( 727 ("GetH2TunnelActiveConn() request for ent %p %s " 728 "found an H2 tunnel connection %p\n", 729 this, mConnInfo->HashKey().get(), connTCP.get())); 730 return connTCP.forget(); 731 } 732 } 733 734 return nullptr; 735 } 736 737 void ConnectionEntry::CloseActiveConnections() { 738 while (mActiveConns.Length()) { 739 RefPtr<HttpConnectionBase> conn(mActiveConns[0]); 740 mActiveConns.RemoveElementAt(0); 741 conn->SetOwner(nullptr); 742 gHttpHandler->ConnMgr()->DecrementActiveConnCount(conn); 743 744 // Since HttpConnectionBase::Close doesn't break the bond with 745 // the connection's transaction, we must explicitely tell it 746 // to close its transaction and not just self. 747 conn->CloseTransaction(conn->Transaction(), NS_ERROR_ABORT, true); 748 } 749 } 750 751 void ConnectionEntry::CloseAllActiveConnsWithNullTransactcion( 752 nsresult aCloseCode) { 753 for (uint32_t index = 0; index < mActiveConns.Length(); ++index) { 754 RefPtr<HttpConnectionBase> activeConn = mActiveConns[index]; 755 nsAHttpTransaction* liveTransaction = activeConn->Transaction(); 756 if (liveTransaction && liveTransaction->IsNullTransaction()) { 757 LOG( 758 ("ConnectionEntry::CloseAllActiveConnsWithNullTransactcion " 759 "also canceling Null Transaction %p on conn %p\n", 760 liveTransaction, activeConn.get())); 761 activeConn->CloseTransaction(liveTransaction, aCloseCode); 762 } 763 } 764 } 765 766 void ConnectionEntry::ClosePendingConnections() { 767 while (mPendingConns.Length()) { 768 RefPtr<HttpConnectionBase> conn(mPendingConns[0]); 769 mPendingConns.RemoveElementAt(0); 770 771 // Since HttpConnectionBase::Close doesn't break the bond with 772 // the connection's transaction, we must explicitely tell it 773 // to close its transaction and not just self. 774 conn->CloseTransaction(conn->Transaction(), NS_ERROR_ABORT, true); 775 } 776 } 777 778 void ConnectionEntry::PruneNoTraffic() { 779 LOG((" pruning no traffic [ci=%s]\n", mConnInfo->HashKey().get())); 780 if (mConnInfo->IsHttp3()) { 781 return; 782 } 783 784 uint32_t numConns = mActiveConns.Length(); 785 if (numConns) { 786 // Walk the list backwards to allow us to remove entries easily. 787 for (int index = numConns - 1; index >= 0; index--) { 788 RefPtr<nsHttpConnection> conn = do_QueryObject(mActiveConns[index]); 789 if (conn && conn->NoTraffic()) { 790 mActiveConns.RemoveElementAt(index); 791 conn->SetOwner(nullptr); 792 gHttpHandler->ConnMgr()->DecrementActiveConnCount(conn); 793 conn->Close(NS_ERROR_ABORT); 794 LOG( 795 (" closed active connection due to no traffic " 796 "[conn=%p]\n", 797 conn.get())); 798 } 799 } 800 } 801 } 802 803 uint32_t ConnectionEntry::TimeoutTick() { 804 uint32_t timeoutTickNext = 3600; // 1hr 805 806 if (mConnInfo->IsHttp3()) { 807 return timeoutTickNext; 808 } 809 810 LOG( 811 ("ConnectionEntry::TimeoutTick() this=%p host=%s " 812 "idle=%zu active=%zu" 813 " dnsAndSock-len=%zu pending=%zu" 814 " urgentStart pending=%zu\n", 815 this, mConnInfo->Origin(), IdleConnectionsLength(), ActiveConnsLength(), 816 mDnsAndConnectSockets.Length(), PendingQueueLength(), 817 UrgentStartQueueLength())); 818 819 // First call the tick handler for each active connection. 820 PRIntervalTime tickTime = PR_IntervalNow(); 821 for (uint32_t index = 0; index < mActiveConns.Length(); ++index) { 822 RefPtr<nsHttpConnection> conn = do_QueryObject(mActiveConns[index]); 823 if (conn) { 824 uint32_t connNextTimeout = conn->ReadTimeoutTick(tickTime); 825 timeoutTickNext = std::min(timeoutTickNext, connNextTimeout); 826 } 827 } 828 829 // Now check for any stalled DnsAndConnectSockets. 830 if (mDnsAndConnectSockets.Length()) { 831 TimeStamp currentTime = TimeStamp::Now(); 832 double maxConnectTime_ms = gHttpHandler->ConnectTimeout(); 833 834 for (const auto& dnsAndSock : Reversed(mDnsAndConnectSockets)) { 835 double delta = dnsAndSock->Duration(currentTime); 836 // If the socket has timed out, close it so the waiting 837 // transaction will get the proper signal. 838 if (delta > maxConnectTime_ms) { 839 LOG(("Force timeout of DnsAndConnectSocket to %s after %.2fms.\n", 840 mConnInfo->HashKey().get(), delta)); 841 dnsAndSock->CloseTransports(NS_ERROR_NET_TIMEOUT); 842 } 843 844 // If this DnsAndConnectSocket hangs around for 5 seconds after we've 845 // closed() it then just abandon the socket. 846 if (delta > maxConnectTime_ms + 5000) { 847 LOG(("Abandon DnsAndConnectSocket to %s after %.2fms.\n", 848 mConnInfo->HashKey().get(), delta)); 849 RemoveDnsAndConnectSocket(dnsAndSock, true); 850 } 851 } 852 } 853 if (mDnsAndConnectSockets.Length()) { 854 timeoutTickNext = 1; 855 } 856 857 return timeoutTickNext; 858 } 859 860 void ConnectionEntry::MoveConnection(HttpConnectionBase* proxyConn, 861 ConnectionEntry* otherEnt) { 862 // To avoid changing mNumActiveConns/mNumIdleConns counter use internal 863 // functions. 864 RefPtr<HttpConnectionBase> deleteProtector(proxyConn); 865 if (mActiveConns.RemoveElement(proxyConn)) { 866 otherEnt->mActiveConns.AppendElement(proxyConn); 867 proxyConn->SetOwner(otherEnt); 868 return; 869 } 870 871 RefPtr<nsHttpConnection> proxyConnTCP = do_QueryObject(proxyConn); 872 if (proxyConnTCP) { 873 if (mIdleConns.RemoveElement(proxyConnTCP)) { 874 otherEnt->InsertIntoIdleConnections_internal(proxyConnTCP); 875 return; 876 } 877 } 878 } 879 880 HttpRetParams ConnectionEntry::GetConnectionData() { 881 HttpRetParams data; 882 data.host = mConnInfo->Origin(); 883 data.port = mConnInfo->OriginPort(); 884 for (uint32_t i = 0; i < mActiveConns.Length(); i++) { 885 HttpConnInfo info; 886 RefPtr<nsHttpConnection> connTCP = do_QueryObject(mActiveConns[i]); 887 if (connTCP) { 888 info.ttl = connTCP->TimeToLive(); 889 } else { 890 info.ttl = 0; 891 } 892 info.rtt = mActiveConns[i]->Rtt(); 893 info.SetHTTPProtocolVersion(mActiveConns[i]->Version()); 894 data.active.AppendElement(info); 895 } 896 for (uint32_t i = 0; i < mIdleConns.Length(); i++) { 897 HttpConnInfo info; 898 info.ttl = mIdleConns[i]->TimeToLive(); 899 info.rtt = mIdleConns[i]->Rtt(); 900 info.SetHTTPProtocolVersion(mIdleConns[i]->Version()); 901 data.idle.AppendElement(info); 902 } 903 for (uint32_t i = 0; i < mDnsAndConnectSockets.Length(); i++) { 904 DnsAndConnectSockets dnsAndSock{}; 905 dnsAndSock.speculative = mDnsAndConnectSockets[i]->IsSpeculative(); 906 data.dnsAndSocks.AppendElement(dnsAndSock); 907 } 908 if (mConnInfo->IsHttp3()) { 909 data.httpVersion = "HTTP/3"_ns; 910 } else if (mUsingSpdy) { 911 data.httpVersion = "HTTP/2"_ns; 912 } else { 913 data.httpVersion = "HTTP <= 1.1"_ns; 914 } 915 data.ssl = mConnInfo->EndToEndSSL(); 916 return data; 917 } 918 919 Http3ConnectionStatsParams ConnectionEntry::GetHttp3ConnectionStatsData() { 920 Http3ConnectionStatsParams data; 921 if (!mConnInfo->IsHttp3()) { 922 return data; 923 } 924 data.host = mConnInfo->Origin(); 925 data.port = mConnInfo->OriginPort(); 926 927 for (uint32_t i = 0; i < mActiveConns.Length(); i++) { 928 RefPtr<HttpConnectionUDP> connUDP = do_QueryObject(mActiveConns[i]); 929 if (!connUDP) { 930 continue; 931 } 932 933 Http3Stats stats = connUDP->GetStats(); 934 Http3ConnStats res; 935 res.packetsRx = stats.packets_rx; 936 res.dupsRx = stats.dups_rx; 937 res.droppedRx = stats.dropped_rx; 938 res.savedDatagrams = stats.saved_datagrams; 939 res.packetsTx = stats.packets_tx; 940 res.lost = stats.lost; 941 res.lateAck = stats.late_ack; 942 res.ptoAck = stats.pto_ack; 943 res.wouldBlockRx = stats.would_block_rx; 944 res.wouldBlockTx = stats.would_block_tx; 945 res.ptoCounts.AppendElements(&stats.pto_counts[0], 16); 946 947 data.stats.AppendElement(std::move(res)); 948 } 949 return data; 950 } 951 952 void ConnectionEntry::LogConnections() { 953 LOG(("active conns [")); 954 for (HttpConnectionBase* conn : mActiveConns) { 955 LOG((" %p", conn)); 956 } 957 958 LOG(("] idle conns [")); 959 for (nsHttpConnection* conn : mIdleConns) { 960 LOG((" %p", conn)); 961 } 962 LOG(("]")); 963 } 964 965 bool ConnectionEntry::RemoveTransFromPendingQ(nsHttpTransaction* aTrans) { 966 // We will abandon all DnsAndConnectSockets belonging to the given 967 // transaction. 968 nsTArray<RefPtr<PendingTransactionInfo>>* infoArray = 969 GetTransactionPendingQHelper(aTrans); 970 971 RefPtr<PendingTransactionInfo> pendingTransInfo; 972 int32_t transIndex = 973 infoArray ? infoArray->IndexOf(aTrans, 0, PendingComparator()) : -1; 974 if (transIndex >= 0) { 975 pendingTransInfo = (*infoArray)[transIndex]; 976 infoArray->RemoveElementAt(transIndex); 977 } 978 979 if (!pendingTransInfo) { 980 return false; 981 } 982 983 // Abandon all DnsAndConnectSockets belonging to the given transaction. 984 nsWeakPtr tmp = pendingTransInfo->ForgetDnsAndConnectSocketAndActiveConn(); 985 RefPtr<DnsAndConnectSocket> dnsAndSock = do_QueryReferent(tmp); 986 if (dnsAndSock) { 987 RemoveDnsAndConnectSocket(dnsAndSock, true); 988 } 989 return true; 990 } 991 992 void ConnectionEntry::MaybeUpdateEchConfig(nsHttpConnectionInfo* aConnInfo) { 993 if (!mConnInfo->HashKey().Equals(aConnInfo->HashKey())) { 994 return; 995 } 996 997 const nsCString& echConfig = aConnInfo->GetEchConfig(); 998 if (mConnInfo->GetEchConfig().Equals(echConfig)) { 999 return; 1000 } 1001 1002 LOG(("ConnectionEntry::MaybeUpdateEchConfig [ci=%s]\n", 1003 mConnInfo->HashKey().get())); 1004 1005 mConnInfo->SetEchConfig(echConfig); 1006 1007 // If echConfig is changed, we should close all DnsAndConnectSockets and idle 1008 // connections. This is to make sure the new echConfig will be used for the 1009 // next connection. 1010 CloseAllDnsAndConnectSockets(); 1011 CloseIdleConnections(); 1012 } 1013 1014 bool ConnectionEntry::MaybeProcessCoalescingKeys(nsIDNSAddrRecord* dnsRecord, 1015 bool aIsHttp3) { 1016 if (!mConnInfo || !mConnInfo->EndToEndSSL() || (!aIsHttp3 && !AllowHttp2()) || 1017 mConnInfo->UsingProxy() || !mCoalescingKeys.IsEmpty() || !dnsRecord) { 1018 return false; 1019 } 1020 1021 nsresult rv = dnsRecord->GetAddresses(mAddresses); 1022 if (NS_FAILED(rv) || mAddresses.IsEmpty()) { 1023 return false; 1024 } 1025 1026 for (uint32_t i = 0; i < mAddresses.Length(); ++i) { 1027 if ((mAddresses[i].raw.family == AF_INET && mAddresses[i].inet.ip == 0) || 1028 (mAddresses[i].raw.family == AF_INET6 && 1029 mAddresses[i].inet6.ip.u64[0] == 0 && 1030 mAddresses[i].inet6.ip.u64[1] == 0)) { 1031 // Bug 1680249 - Don't create the coalescing key if the ip address is 1032 // `0.0.0.0` or `::`. 1033 LOG( 1034 ("ConnectionEntry::MaybeProcessCoalescingKeys skip creating " 1035 "Coalescing Key for host [%s]", 1036 mConnInfo->Origin())); 1037 continue; 1038 } 1039 nsCString* newKey = mCoalescingKeys.AppendElement(nsCString()); 1040 newKey->SetLength(kIPv6CStrBufSize + 26); 1041 mAddresses[i].ToStringBuffer(newKey->BeginWriting(), kIPv6CStrBufSize); 1042 newKey->SetLength(strlen(newKey->BeginReading())); 1043 if (mConnInfo->GetAnonymous()) { 1044 newKey->AppendLiteral("~A:"); 1045 } else { 1046 newKey->AppendLiteral("~.:"); 1047 } 1048 if (mConnInfo->GetFallbackConnection()) { 1049 newKey->AppendLiteral("~F:"); 1050 } else { 1051 newKey->AppendLiteral("~.:"); 1052 } 1053 newKey->AppendInt(mConnInfo->OriginPort()); 1054 newKey->AppendLiteral("/["); 1055 nsAutoCString suffix; 1056 mConnInfo->GetOriginAttributes().CreateSuffix(suffix); 1057 newKey->Append(suffix); 1058 newKey->AppendLiteral("]viaDNS"); 1059 LOG( 1060 ("ConnectionEntry::MaybeProcessCoalescingKeys " 1061 "Established New Coalescing Key # %d for host " 1062 "%s [%s]", 1063 i, mConnInfo->Origin(), newKey->get())); 1064 } 1065 return true; 1066 } 1067 1068 nsresult ConnectionEntry::CreateDnsAndConnectSocket( 1069 nsAHttpTransaction* trans, uint32_t caps, bool speculative, 1070 bool urgentStart, bool allow1918, 1071 PendingTransactionInfo* pendingTransInfo) { 1072 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1073 MOZ_ASSERT((speculative && !pendingTransInfo) || 1074 (!speculative && pendingTransInfo)); 1075 1076 RefPtr<DnsAndConnectSocket> sock = 1077 new DnsAndConnectSocket(mConnInfo, trans, caps, speculative, urgentStart); 1078 1079 if (speculative) { 1080 sock->SetAllow1918(allow1918); 1081 } 1082 1083 nsresult rv = sock->Init(this); 1084 if (NS_FAILED(rv)) { 1085 sock->Abandon(); 1086 return rv; 1087 } 1088 1089 InsertIntoDnsAndConnectSockets(sock); 1090 1091 if (pendingTransInfo && sock->Claim()) { 1092 pendingTransInfo->RememberDnsAndConnectSocket(sock); 1093 } 1094 1095 return NS_OK; 1096 } 1097 1098 bool ConnectionEntry::AllowToRetryDifferentIPFamilyForHttp3(nsresult aError) { 1099 LOG( 1100 ("ConnectionEntry::AllowToRetryDifferentIPFamilyForHttp3 %p " 1101 "error=%" PRIx32, 1102 this, static_cast<uint32_t>(aError))); 1103 if (!mConnInfo->IsHttp3() && !mConnInfo->IsHttp3ProxyConnection()) { 1104 MOZ_ASSERT(false, "Should not be called for non Http/3 connection"); 1105 return false; 1106 } 1107 1108 if (!StaticPrefs::network_http_http3_retry_different_ip_family()) { 1109 return false; 1110 } 1111 1112 // Only allow to retry with these two errors. 1113 if (aError != NS_ERROR_CONNECTION_REFUSED && 1114 aError != NS_ERROR_PROXY_CONNECTION_REFUSED) { 1115 return false; 1116 } 1117 1118 // Already retried once. 1119 if (mRetriedDifferentIPFamilyForHttp3) { 1120 return false; 1121 } 1122 1123 return true; 1124 } 1125 1126 void ConnectionEntry::SetRetryDifferentIPFamilyForHttp3(uint16_t aIPFamily) { 1127 LOG(("ConnectionEntry::SetRetryDifferentIPFamilyForHttp3 %p, af=%u", this, 1128 aIPFamily)); 1129 1130 mPreferIPv4 = false; 1131 mPreferIPv6 = false; 1132 1133 if (aIPFamily == AF_INET) { 1134 mPreferIPv6 = true; 1135 } 1136 1137 if (aIPFamily == AF_INET6) { 1138 mPreferIPv4 = true; 1139 } 1140 1141 mRetriedDifferentIPFamilyForHttp3 = true; 1142 1143 LOG((" %p prefer ipv4=%d, ipv6=%d", this, (bool)mPreferIPv4, 1144 (bool)mPreferIPv6)); 1145 MOZ_DIAGNOSTIC_ASSERT(mPreferIPv4 ^ mPreferIPv6); 1146 } 1147 1148 void ConnectionEntry::SetServerCertHashes( 1149 nsTArray<RefPtr<nsIWebTransportHash>>&& aHashes) { 1150 mServerCertHashes = std::move(aHashes); 1151 } 1152 1153 const nsTArray<RefPtr<nsIWebTransportHash>>& 1154 ConnectionEntry::GetServerCertHashes() { 1155 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1156 return mServerCertHashes; 1157 } 1158 1159 const nsCString& ConnectionEntry::OriginFrameHashKey() { 1160 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1161 if (mOriginFrameHashKey.IsEmpty()) { 1162 nsHttpConnectionInfo::BuildOriginFrameHashKey( 1163 mOriginFrameHashKey, mConnInfo, mConnInfo->GetOrigin(), 1164 mConnInfo->OriginPort()); 1165 } 1166 return mOriginFrameHashKey; 1167 } 1168 1169 } // namespace net 1170 } // namespace mozilla