/* vim:set ts=4 sw=2 sts=2 et cin: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // HttpLog.h should generally be included first #include "HttpLog.h" // Log on level :5, instead of default :4. #undef LOG #define LOG(args) LOG5(args) #undef LOG_ENABLED #define LOG_ENABLED() LOG5_ENABLED() #include "ConnectionAttemptPool.h" #include "ConnectionEntry.h" #include "DnsAndConnectSocket.h" #include "HappyEyeballsConnectionAttempt.h" #include "nsHttpHandler.h" #include "nsHttpConnectionMgr.h" namespace mozilla::net { ConnectionAttemptPool::ConnectionAttemptPool(ConnectionEntry* aEntry) : mEntry(aEntry) { LOG(("ConnectionAttemptPool ctor %p", this)); } ConnectionAttemptPool::~ConnectionAttemptPool() { LOG(("ConnectionAttemptPool dtor %p", this)); MOZ_DIAGNOSTIC_ASSERT(mAttempts.IsEmpty()); } nsresult ConnectionAttemptPool::StartConnectionEstablishment( ConnectionEntry* entry, nsAHttpTransaction* trans, uint32_t caps, bool speculative, bool urgentStart, bool allow1918, PendingTransactionInfo* pendingTransInfo) { MOZ_ASSERT(OnSocketThread(), "not on socket thread"); MOZ_ASSERT((speculative && !pendingTransInfo) || (!speculative && pendingTransInfo)); RefPtr connAttempt; nsHttpConnectionInfo* ci = trans->ConnectionInfo(); if (ci->GetHappyEyeballsEnabled()) { connAttempt = new HappyEyeballsConnectionAttempt(ci, trans, caps, speculative, urgentStart); } else { connAttempt = new DnsAndConnectSocket(entry->mConnInfo, trans, caps, speculative, urgentStart); } if (speculative) { connAttempt->SetAllow1918(allow1918); } nsresult rv = connAttempt->Init(entry); if (NS_FAILED(rv)) { connAttempt->Abandon(); return rv; } InsertIntoConnectionAttempts(connAttempt); if (pendingTransInfo) { bool claimed = connAttempt->Claim(); if (!claimed) { // We should always be able to claim this. MOZ_ASSERT(false, "Failed to claim a connAttempt"); return NS_ERROR_UNEXPECTED; } pendingTransInfo->RememberConnectionAttempt(connAttempt); } return NS_OK; } void ConnectionAttemptPool::InsertIntoConnectionAttempts( ConnectionAttempt* sock) { mAttempts.AppendElement(sock); gHttpHandler->ConnMgr()->IncreaseNumDnsAndConnectSockets(); } void ConnectionAttemptPool::RemoveConnectionAttempt(ConnectionAttempt* sock, bool abandon) { if (abandon) { sock->Abandon(); } if (mAttempts.RemoveElement(sock)) { gHttpHandler->ConnMgr()->DecreaseNumDnsAndConnectSockets(); } if (!UnconnectedConnectionAttempts()) { // perhaps this reverted RestrictConnections() // use the PostEvent version of processpendingq to avoid // altering the pending q vector from an arbitrary stack RefPtr entry(mEntry); if (entry) { gHttpHandler->ConnMgr()->ProcessPendingQForEntry(entry); } } } uint32_t ConnectionAttemptPool::UnconnectedConnectionAttempts() const { uint32_t unconnectedConns = 0; for (uint32_t i = 0; i < mAttempts.Length(); ++i) { if (!mAttempts[i]->HasConnected()) { ++unconnectedConns; } } return unconnectedConns; } void ConnectionAttemptPool::CloseAllConnectionAttempts() { for (const auto& sock : mAttempts) { sock->Abandon(); gHttpHandler->ConnMgr()->DecreaseNumDnsAndConnectSockets(); } mAttempts.Clear(); RefPtr entry(mEntry); if (entry) { gHttpHandler->ConnMgr()->ProcessPendingQForEntry(entry); } } bool ConnectionAttemptPool::FindConnToClaim( PendingTransactionInfo* pendingTransInfo) { nsHttpTransaction* trans = pendingTransInfo->Transaction(); for (const auto& sock : mAttempts) { if (sock->AcceptsTransaction(trans) && sock->Claim(trans)) { pendingTransInfo->RememberConnectionAttempt(sock); // We've found a speculative connection or a connection that // is free to be used in the mAttempts list. // A free to be used connection is a connection that was // open for a concrete transaction, but that trunsaction // ended up using another connection. LOG( ("ConnectionAttemptPool::FindConnToClaim [ci = %s]\n" "Found a speculative or a free-to-use ConnectionAttempt\n", trans->ConnectionInfo()->HashKey().get())); // return OK because we have essentially opened a new connection // by converting a speculative connection to general use return true; } } return false; } void ConnectionAttemptPool::TimeoutTick() { if (mAttempts.IsEmpty()) { return; } TimeStamp currentTime = TimeStamp::Now(); double maxConnectTime_ms = gHttpHandler->ConnectTimeout(); for (const auto& sock : Reversed(mAttempts)) { double delta = sock->Duration(currentTime); // If the socket has timed out, close it so the waiting // transaction will get the proper signal. if (delta > maxConnectTime_ms) { LOG(("Force timeout of ConnectionAttempt to %p after %.2fms.\n", sock.get(), delta)); sock->OnTimeout(); } // If this ConnectionAttempt hangs around for 5 seconds after we've // closed() it then just abandon the socket. if (delta > maxConnectTime_ms + 5000) { LOG(("Abandon ConnectionAttempt to %p after %.2fms.\n", sock.get(), delta)); RemoveConnectionAttempt(sock, true); } } } void ConnectionAttemptPool::PrintDiagnostics(nsCString& log) { if (mAttempts.IsEmpty()) { return; } uint32_t count = 0; for (const auto& sock : Reversed(mAttempts)) { log.AppendPrintf(" :: Half Open #%u\n", count); sock->PrintDiagnostics(log); count++; } } void ConnectionAttemptPool::GetConnectionData(HttpRetParams& data) { for (uint32_t i = 0; i < mAttempts.Length(); i++) { DnsAndConnectSockets dnsAndSock{}; dnsAndSock.speculative = mAttempts[i]->IsSpeculative(); data.dnsAndSocks.AppendElement(dnsAndSock); } } uint32_t ConnectionAttemptPool::UnconnectedUDPConnsLength() const { uint32_t len = 0; for (const auto& sock : mAttempts) { len += sock->UnconnectedUDPConnsLength(); } return len; } } // namespace mozilla::net