gecko-dev/netwerk/protocol/http/ConnectionEntry.cpp

941 строка
29 KiB
C++

/* 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 "ConnectionEntry.h"
#include "nsQueryObject.h"
#include "mozilla/ChaosMode.h"
namespace mozilla {
namespace net {
// ConnectionEntry
ConnectionEntry::~ConnectionEntry() {
LOG(("ConnectionEntry::~ConnectionEntry this=%p", this));
MOZ_ASSERT(!mIdleConns.Length());
MOZ_ASSERT(!mActiveConns.Length());
MOZ_ASSERT(!mHalfOpens.Length());
MOZ_ASSERT(!PendingQueueLength());
MOZ_ASSERT(!UrgentStartQueueLength());
MOZ_ASSERT(!mHalfOpenFastOpenBackups.Length());
MOZ_ASSERT(!mDoNotDestroy);
}
ConnectionEntry::ConnectionEntry(nsHttpConnectionInfo* ci)
: mConnInfo(ci),
mUsingSpdy(false),
mCanUseSpdy(true),
mPreferIPv4(false),
mPreferIPv6(false),
mUsedForConnection(false),
mDoNotDestroy(false) {
if (mConnInfo->FirstHopSSL() && !mConnInfo->IsHttp3()) {
mUseFastOpen = gHttpHandler->UseFastOpen();
} else {
// Only allow the TCP fast open on a secure connection.
mUseFastOpen = false;
}
LOG(("ConnectionEntry::ConnectionEntry this=%p key=%s", this,
ci->HashKey().get()));
}
bool ConnectionEntry::AvailableForDispatchNow() {
if (mIdleConns.Length() && mIdleConns[0]->CanReuse()) {
return true;
}
return gHttpHandler->ConnMgr()->GetH2orH3ActiveConn(this, false, false)
? true
: false;
}
uint32_t ConnectionEntry::UnconnectedHalfOpens() const {
uint32_t unconnectedHalfOpens = 0;
for (uint32_t i = 0; i < mHalfOpens.Length(); ++i) {
if (!mHalfOpens[i]->HasConnected()) {
++unconnectedHalfOpens;
}
}
return unconnectedHalfOpens;
}
bool ConnectionEntry::RemoveHalfOpen(HalfOpenSocket* halfOpen) {
bool isPrimary = false;
// A failure to create the transport object at all
// will result in it not being present in the halfopen table. That's expected.
if (mHalfOpens.RemoveElement(halfOpen)) {
isPrimary = true;
if (halfOpen->IsSpeculative()) {
Telemetry::AutoCounter<Telemetry::HTTPCONNMGR_UNUSED_SPECULATIVE_CONN>
unusedSpeculativeConn;
++unusedSpeculativeConn;
if (halfOpen->IsFromPredictor()) {
Telemetry::AutoCounter<Telemetry::PREDICTOR_TOTAL_PRECONNECTS_UNUSED>
totalPreconnectsUnused;
++totalPreconnectsUnused;
}
}
gHttpHandler->ConnMgr()->DecreaseNumHalfOpenConns();
} else {
mHalfOpenFastOpenBackups.RemoveElement(halfOpen);
}
if (!UnconnectedHalfOpens()) {
// perhaps this reverted RestrictConnections()
// use the PostEvent version of processpendingq to avoid
// altering the pending q vector from an arbitrary stack
nsresult rv = gHttpHandler->ConnMgr()->ProcessPendingQ(mConnInfo);
if (NS_FAILED(rv)) {
LOG(
("ConnectionEntry::RemoveHalfOpen\n"
" failed to process pending queue\n"));
}
}
return isPrimary;
}
void ConnectionEntry::DisallowHttp2() {
mCanUseSpdy = false;
// If we have any spdy connections, we want to go ahead and close them when
// they're done so we can free up some connections.
for (uint32_t i = 0; i < mActiveConns.Length(); ++i) {
if (mActiveConns[i]->UsingSpdy()) {
mActiveConns[i]->DontReuse();
}
}
for (uint32_t i = 0; i < mIdleConns.Length(); ++i) {
if (mIdleConns[i]->UsingSpdy()) {
mIdleConns[i]->DontReuse();
}
}
// Can't coalesce if we're not using spdy
mCoalescingKeys.Clear();
}
void ConnectionEntry::DontReuseHttp3Conn() {
MOZ_ASSERT(mConnInfo->IsHttp3());
// If we have any spdy connections, we want to go ahead and close them when
// they're done so we can free up some connections.
for (uint32_t i = 0; i < mActiveConns.Length(); ++i) {
mActiveConns[i]->DontReuse();
}
// Can't coalesce if we're not using http3
mCoalescingKeys.Clear();
}
void ConnectionEntry::RecordIPFamilyPreference(uint16_t family) {
LOG(("ConnectionEntry::RecordIPFamilyPreference %p, af=%u", this, family));
if (family == PR_AF_INET && !mPreferIPv6) {
mPreferIPv4 = true;
}
if (family == PR_AF_INET6 && !mPreferIPv4) {
mPreferIPv6 = true;
}
LOG((" %p prefer ipv4=%d, ipv6=%d", this, (bool)mPreferIPv4,
(bool)mPreferIPv6));
}
void ConnectionEntry::ResetIPFamilyPreference() {
LOG(("ConnectionEntry::ResetIPFamilyPreference %p", this));
mPreferIPv4 = false;
mPreferIPv6 = false;
}
bool net::ConnectionEntry::PreferenceKnown() const {
return (bool)mPreferIPv4 || (bool)mPreferIPv6;
}
size_t ConnectionEntry::PendingQueueLength() const {
return mPendingQ.PendingQueueLength();
}
size_t ConnectionEntry::PendingQueueLengthForWindow(uint64_t windowId) const {
return mPendingQ.PendingQueueLengthForWindow(windowId);
}
void ConnectionEntry::AppendPendingUrgentStartQ(
nsTArray<RefPtr<PendingTransactionInfo>>& result) {
mPendingQ.AppendPendingUrgentStartQ(result);
}
void ConnectionEntry::AppendPendingQForFocusedWindow(
uint64_t windowId, nsTArray<RefPtr<PendingTransactionInfo>>& result,
uint32_t maxCount) {
mPendingQ.AppendPendingQForFocusedWindow(windowId, result, maxCount);
LOG(
("ConnectionEntry::AppendPendingQForFocusedWindow [ci=%s], "
"pendingQ count=%zu for focused window (id=%" PRIu64 ")\n",
mConnInfo->HashKey().get(), result.Length(), windowId));
}
void ConnectionEntry::AppendPendingQForNonFocusedWindows(
uint64_t windowId, nsTArray<RefPtr<PendingTransactionInfo>>& result,
uint32_t maxCount) {
mPendingQ.AppendPendingQForNonFocusedWindows(windowId, result, maxCount);
LOG(
("ConnectionEntry::AppendPendingQForNonFocusedWindows [ci=%s], "
"pendingQ count=%zu for non focused window\n",
mConnInfo->HashKey().get(), result.Length()));
}
void ConnectionEntry::RemoveEmptyPendingQ() { mPendingQ.RemoveEmptyPendingQ(); }
void ConnectionEntry::InsertTransactionSorted(
nsTArray<RefPtr<PendingTransactionInfo>>& pendingQ,
PendingTransactionInfo* pendingTransInfo,
bool aInsertAsFirstForTheSamePriority /*= false*/) {
mPendingQ.InsertTransactionSorted(pendingQ, pendingTransInfo,
aInsertAsFirstForTheSamePriority);
}
void ConnectionEntry::ReschedTransaction(nsHttpTransaction* aTrans) {
mPendingQ.ReschedTransaction(aTrans);
}
void ConnectionEntry::InsertTransaction(
PendingTransactionInfo* pendingTransInfo,
bool aInsertAsFirstForTheSamePriority /* = false */) {
mPendingQ.InsertTransaction(pendingTransInfo,
aInsertAsFirstForTheSamePriority);
pendingTransInfo->Transaction()->OnPendingQueueInserted();
}
nsTArray<RefPtr<PendingTransactionInfo>>*
ConnectionEntry::GetTransactionPendingQHelper(nsAHttpTransaction* trans) {
return mPendingQ.GetTransactionPendingQHelper(trans);
}
bool ConnectionEntry::RestrictConnections() {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
if (AvailableForDispatchNow()) {
// this might be a h2/spdy connection in this connection entry that
// is able to be immediately muxxed, or it might be one that
// was found in the same state through a coalescing hash
LOG(
("ConnectionEntry::RestrictConnections %p %s restricted due to "
"active >=h2\n",
this, mConnInfo->HashKey().get()));
return true;
}
// If this host is trying to negotiate a SPDY session right now,
// don't create any new ssl connections until the result of the
// negotiation is known.
bool doRestrict = mConnInfo->FirstHopSSL() && gHttpHandler->IsSpdyEnabled() &&
mUsingSpdy &&
(mHalfOpens.Length() || mActiveConns.Length());
// If there are no restrictions, we are done
if (!doRestrict) {
return false;
}
// If the restriction is based on a tcp handshake in progress
// let that connect and then see if it was SPDY or not
if (UnconnectedHalfOpens()) {
return true;
}
// There is a concern that a host is using a mix of HTTP/1 and SPDY.
// In that case we don't want to restrict connections just because
// there is a single active HTTP/1 session in use.
if (mUsingSpdy && mActiveConns.Length()) {
bool confirmedRestrict = false;
for (uint32_t index = 0; index < mActiveConns.Length(); ++index) {
HttpConnectionBase* conn = mActiveConns[index];
RefPtr<nsHttpConnection> connTCP = do_QueryObject(conn);
if ((connTCP && !connTCP->ReportedNPN()) || conn->CanDirectlyActivate()) {
confirmedRestrict = true;
break;
}
}
doRestrict = confirmedRestrict;
if (!confirmedRestrict) {
LOG(
("nsHttpConnectionMgr spdy connection restriction to "
"%s bypassed.\n",
mConnInfo->Origin()));
}
}
return doRestrict;
}
uint32_t ConnectionEntry::TotalActiveConnections() const {
// Add in the in-progress tcp connections, we will assume they are
// keepalive enabled.
// Exclude half-open's that has already created a usable connection.
// This prevents the limit being stuck on ipv6 connections that
// eventually time out after typical 21 seconds of no ACK+SYN reply.
return mActiveConns.Length() + UnconnectedHalfOpens();
}
size_t ConnectionEntry::UrgentStartQueueLength() {
return mPendingQ.UrgentStartQueueLength();
}
void ConnectionEntry::PrintPendingQ() { mPendingQ.PrintPendingQ(); }
void ConnectionEntry::Compact() {
mIdleConns.Compact();
mActiveConns.Compact();
mPendingQ.Compact();
}
void ConnectionEntry::RemoveFromIdleConnectionsIndex(size_t inx) {
mIdleConns.RemoveElementAt(inx);
gHttpHandler->ConnMgr()->DecrementNumIdleConns();
}
bool ConnectionEntry::RemoveFromIdleConnections(nsHttpConnection* conn) {
if (!mIdleConns.RemoveElement(conn)) {
return false;
}
gHttpHandler->ConnMgr()->DecrementNumIdleConns();
return true;
}
void ConnectionEntry::CancelAllTransactions(nsresult reason) {
mPendingQ.CancelAllTransactions(reason);
}
nsresult ConnectionEntry::CloseIdleConnection(nsHttpConnection* conn) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
RefPtr<nsHttpConnection> deleteProtector(conn);
if (!RemoveFromIdleConnections(conn)) {
return NS_ERROR_UNEXPECTED;
}
// The connection is closed immediately no need to call EndIdleMonitoring.
conn->Close(NS_ERROR_ABORT);
return NS_OK;
}
void ConnectionEntry::CloseIdleConnections() {
while (mIdleConns.Length()) {
RefPtr<nsHttpConnection> conn(mIdleConns[0]);
RemoveFromIdleConnectionsIndex(0);
// The connection is closed immediately no need to call EndIdleMonitoring.
conn->Close(NS_ERROR_ABORT);
}
}
void ConnectionEntry::CloseIdleConnections(uint32_t maxToClose) {
uint32_t closed = 0;
while (mIdleConns.Length() && (closed < maxToClose)) {
RefPtr<nsHttpConnection> conn(mIdleConns[0]);
RemoveFromIdleConnectionsIndex(0);
// The connection is closed immediately no need to call EndIdleMonitoring.
conn->Close(NS_ERROR_ABORT);
closed++;
}
}
nsresult ConnectionEntry::RemoveIdleConnection(nsHttpConnection* conn) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
if (!RemoveFromIdleConnections(conn)) {
return NS_ERROR_UNEXPECTED;
}
conn->EndIdleMonitoring();
return NS_OK;
}
bool ConnectionEntry::IsInIdleConnections(HttpConnectionBase* conn) {
RefPtr<nsHttpConnection> connTCP = do_QueryObject(conn);
return connTCP && mIdleConns.Contains(connTCP);
}
already_AddRefed<nsHttpConnection> ConnectionEntry::GetIdleConnection(
bool respectUrgency, bool urgentTrans, bool* onlyUrgent) {
RefPtr<nsHttpConnection> conn;
size_t index = 0;
while (!conn && (mIdleConns.Length() > index)) {
conn = mIdleConns[index];
if (!conn->CanReuse()) {
RemoveFromIdleConnectionsIndex(index);
LOG((" dropping stale connection: [conn=%p]\n", conn.get()));
conn->Close(NS_ERROR_ABORT);
conn = nullptr;
continue;
}
// non-urgent transactions can only be dispatched on non-urgent
// started or used connections.
if (respectUrgency && conn->IsUrgentStartPreferred() && !urgentTrans) {
LOG((" skipping urgent: [conn=%p]", conn.get()));
conn = nullptr;
++index;
continue;
}
*onlyUrgent = false;
RemoveFromIdleConnectionsIndex(index);
conn->EndIdleMonitoring();
LOG((" reusing connection: [conn=%p]\n", conn.get()));
}
return conn.forget();
}
nsresult ConnectionEntry::RemoveActiveConnection(HttpConnectionBase* conn) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
if (!mActiveConns.RemoveElement(conn)) {
return NS_ERROR_UNEXPECTED;
}
gHttpHandler->ConnMgr()->DecrementActiveConnCount(conn);
return NS_OK;
}
void ConnectionEntry::ClosePersistentConnections() {
LOG(("ConnectionEntry::ClosePersistentConnections [ci=%s]\n",
mConnInfo->HashKey().get()));
CloseIdleConnections();
int32_t activeCount = mActiveConns.Length();
for (int32_t i = 0; i < activeCount; i++) {
mActiveConns[i]->DontReuse();
}
for (int32_t index = mHalfOpenFastOpenBackups.Length() - 1; index >= 0;
--index) {
RefPtr<HalfOpenSocket> half = mHalfOpenFastOpenBackups[index];
half->CancelFastOpenConnection();
}
}
uint32_t ConnectionEntry::PruneDeadConnections() {
uint32_t timeToNextExpire = UINT32_MAX;
for (int32_t len = mIdleConns.Length(); len > 0; --len) {
int32_t idx = len - 1;
RefPtr<nsHttpConnection> conn(mIdleConns[idx]);
if (!conn->CanReuse()) {
RemoveFromIdleConnectionsIndex(idx);
// The connection is closed immediately no need to call
// EndIdleMonitoring.
conn->Close(NS_ERROR_ABORT);
} else {
timeToNextExpire = std::min(timeToNextExpire, conn->TimeToLive());
}
}
if (mUsingSpdy) {
for (uint32_t i = 0; i < mActiveConns.Length(); ++i) {
RefPtr<nsHttpConnection> connTCP = do_QueryObject(mActiveConns[i]);
// Http3 has its own timers, it is not using this one.
if (connTCP && connTCP->UsingSpdy()) {
if (!connTCP->CanReuse()) {
// Marking it don't-reuse will create an active
// tear down if the spdy session is idle.
connTCP->DontReuse();
} else {
timeToNextExpire = std::min(timeToNextExpire, connTCP->TimeToLive());
}
}
}
}
return timeToNextExpire;
}
void ConnectionEntry::VerifyTraffic() {
if (!mConnInfo->IsHttp3()) {
// Iterate over all active connections and check them.
for (uint32_t index = 0; index < mActiveConns.Length(); ++index) {
RefPtr<nsHttpConnection> conn = do_QueryObject(mActiveConns[index]);
if (conn) {
conn->CheckForTraffic(true);
}
}
// Iterate the idle connections and unmark them for traffic checks.
for (uint32_t index = 0; index < mIdleConns.Length(); ++index) {
RefPtr<nsHttpConnection> conn = do_QueryObject(mIdleConns[index]);
if (conn) {
conn->CheckForTraffic(false);
}
}
}
}
void ConnectionEntry::InsertIntoIdleConnections_internal(
nsHttpConnection* conn) {
uint32_t idx;
for (idx = 0; idx < mIdleConns.Length(); idx++) {
nsHttpConnection* idleConn = mIdleConns[idx];
if (idleConn->MaxBytesRead() < conn->MaxBytesRead()) {
break;
}
}
mIdleConns.InsertElementAt(idx, conn);
}
void ConnectionEntry::InsertIntoIdleConnections(nsHttpConnection* conn) {
InsertIntoIdleConnections_internal(conn);
gHttpHandler->ConnMgr()->NewIdleConnectionAdded(conn->TimeToLive());
conn->BeginIdleMonitoring();
}
bool ConnectionEntry::IsInActiveConns(HttpConnectionBase* conn) {
return mActiveConns.Contains(conn);
}
void ConnectionEntry::InsertIntoActiveConns(HttpConnectionBase* conn) {
mActiveConns.AppendElement(conn);
gHttpHandler->ConnMgr()->IncrementActiveConnCount();
}
void ConnectionEntry::MakeAllDontReuseExcept(HttpConnectionBase* conn) {
for (uint32_t index = 0; index < mActiveConns.Length(); ++index) {
HttpConnectionBase* otherConn = mActiveConns[index];
if (otherConn != conn) {
LOG(
("ConnectionEntry::MakeAllDontReuseExcept shutting down old "
"connection (%p) "
"because new "
"spdy connection (%p) takes precedence\n",
otherConn, conn));
otherConn->DontReuse();
}
}
// Cancel any other pending connections - their associated transactions
// are in the pending queue and will be dispatched onto this new connection
for (int32_t index = HalfOpensLength() - 1; index >= 0; --index) {
RefPtr<HalfOpenSocket> half = mHalfOpens[index];
LOG((
"ConnectionEntry::MakeAllDontReuseExcept forcing halfopen abandon %p\n",
half.get()));
mHalfOpens[index]->Abandon();
}
for (int32_t index = mHalfOpenFastOpenBackups.Length() - 1; index >= 0;
--index) {
LOG(
("ConnectionEntry::MakeAllDontReuseExcept shutting down connection in "
"fast "
"open state (%p) because new spdy connection (%p) takes "
"precedence\n",
mHalfOpenFastOpenBackups[index].get(), conn));
RefPtr<HalfOpenSocket> half = mHalfOpenFastOpenBackups[index];
half->CancelFastOpenConnection();
}
}
bool ConnectionEntry::FindConnToClaim(
PendingTransactionInfo* pendingTransInfo) {
nsHttpTransaction* trans = pendingTransInfo->Transaction();
uint32_t halfOpenLength = HalfOpensLength();
for (uint32_t i = 0; i < halfOpenLength; i++) {
auto halfOpen = mHalfOpens[i];
if (halfOpen->AcceptsTransaction(trans) &&
pendingTransInfo->TryClaimingHalfOpen(halfOpen)) {
// We've found a speculative connection or a connection that
// is free to be used in the half open 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(
("ConnectionEntry::FindConnToClaim [ci = %s]\n"
"Found a speculative or a free-to-use half open connection\n",
mConnInfo->HashKey().get()));
// return OK because we have essentially opened a new connection
// by converting a speculative half-open to general use
return true;
}
}
// consider null transactions that are being used to drive the ssl handshake
// if the transaction creating this connection can re-use persistent
// connections
if (trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) {
uint32_t activeLength = mActiveConns.Length();
for (uint32_t i = 0; i < activeLength; i++) {
if (pendingTransInfo->TryClaimingActiveConn(mActiveConns[i])) {
LOG(
("ConnectionEntry::FindConnectingSocket [ci = %s] "
"Claiming a null transaction for later use\n",
mConnInfo->HashKey().get()));
return true;
}
}
}
return false;
}
bool ConnectionEntry::MakeFirstActiveSpdyConnDontReuse() {
if (!mUsingSpdy) {
return false;
}
for (uint32_t index = 0; index < mActiveConns.Length(); ++index) {
HttpConnectionBase* conn = mActiveConns[index];
if (conn->UsingSpdy() && conn->CanReuse()) {
conn->DontReuse();
return true;
}
}
return false;
}
// Return an active h2 or h3 connection
// that can be directly activated or null.
HttpConnectionBase* ConnectionEntry::GetH2orH3ActiveConn() {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
HttpConnectionBase* experienced = nullptr;
HttpConnectionBase* noExperience = nullptr;
uint32_t activeLen = mActiveConns.Length();
// activeLen should generally be 1.. this is a setup race being resolved
// take a conn who can activate and is experienced
for (uint32_t index = 0; index < activeLen; ++index) {
HttpConnectionBase* tmp = mActiveConns[index];
if (tmp->CanDirectlyActivate()) {
if (tmp->IsExperienced()) {
experienced = tmp;
break;
}
noExperience = tmp; // keep looking for a better option
}
}
// if that worked, cleanup anything else and exit
if (experienced) {
for (uint32_t index = 0; index < activeLen; ++index) {
HttpConnectionBase* tmp = mActiveConns[index];
// in the case where there is a functional h2 session, drop the others
if (tmp != experienced) {
tmp->DontReuse();
}
}
for (int32_t index = mHalfOpenFastOpenBackups.Length() - 1; index >= 0;
--index) {
LOG(
("GetH2orH3ActiveConn() shutting down connection in fast "
"open state (%p) because we have an experienced spdy "
"connection (%p).\n",
mHalfOpenFastOpenBackups[index].get(), experienced));
RefPtr<HalfOpenSocket> half = mHalfOpenFastOpenBackups[index];
half->CancelFastOpenConnection();
}
LOG(
("GetH2orH3ActiveConn() request for ent %p %s "
"found an active experienced connection %p in native connection "
"entry\n",
this, mConnInfo->HashKey().get(), experienced));
return experienced;
}
if (noExperience) {
LOG(
("GetH2orH3ActiveConn() request for ent %p %s "
"found an active but inexperienced connection %p in native connection "
"entry\n",
this, mConnInfo->HashKey().get(), noExperience));
return noExperience;
}
return nullptr;
}
void ConnectionEntry::CloseActiveConnections() {
while (mActiveConns.Length()) {
RefPtr<HttpConnectionBase> conn(mActiveConns[0]);
mActiveConns.RemoveElementAt(0);
gHttpHandler->ConnMgr()->DecrementActiveConnCount(conn);
// Since HttpConnectionBase::Close doesn't break the bond with
// the connection's transaction, we must explicitely tell it
// to close its transaction and not just self.
conn->CloseTransaction(conn->Transaction(), NS_ERROR_ABORT, true);
}
}
void ConnectionEntry::CloseAllActiveConnsWithNullTransactcion(
nsresult aCloseCode) {
for (uint32_t index = 0; index < mActiveConns.Length(); ++index) {
RefPtr<HttpConnectionBase> activeConn = mActiveConns[index];
nsAHttpTransaction* liveTransaction = activeConn->Transaction();
if (liveTransaction && liveTransaction->IsNullTransaction()) {
LOG(
("ConnectionEntry::CloseAllActiveConnsWithNullTransactcion "
"also canceling Null Transaction %p on conn %p\n",
liveTransaction, activeConn.get()));
activeConn->CloseTransaction(liveTransaction, aCloseCode);
}
}
}
void ConnectionEntry::PruneNoTraffic() {
LOG((" pruning no traffic [ci=%s]\n", mConnInfo->HashKey().get()));
if (mConnInfo->IsHttp3()) {
return;
}
uint32_t numConns = mActiveConns.Length();
if (numConns) {
// Walk the list backwards to allow us to remove entries easily.
for (int index = numConns - 1; index >= 0; index--) {
RefPtr<nsHttpConnection> conn = do_QueryObject(mActiveConns[index]);
if (conn && conn->NoTraffic()) {
mActiveConns.RemoveElementAt(index);
gHttpHandler->ConnMgr()->DecrementActiveConnCount(conn);
conn->Close(NS_ERROR_ABORT);
LOG(
(" closed active connection due to no traffic "
"[conn=%p]\n",
conn.get()));
}
}
}
}
uint32_t ConnectionEntry::TimeoutTick() {
uint32_t timeoutTickNext = 3600; // 1hr
if (mConnInfo->IsHttp3()) {
return timeoutTickNext;
}
LOG(
("ConnectionEntry::TimeoutTick() this=%p host=%s "
"idle=%zu active=%zu"
" half-len=%zu pending=%zu"
" urgentStart pending=%zu\n",
this, mConnInfo->Origin(), IdleConnectionsLength(), ActiveConnsLength(),
mHalfOpens.Length(), PendingQueueLength(), UrgentStartQueueLength()));
// First call the tick handler for each active connection.
PRIntervalTime tickTime = PR_IntervalNow();
for (uint32_t index = 0; index < mActiveConns.Length(); ++index) {
RefPtr<nsHttpConnection> conn = do_QueryObject(mActiveConns[index]);
if (conn) {
uint32_t connNextTimeout = conn->ReadTimeoutTick(tickTime);
timeoutTickNext = std::min(timeoutTickNext, connNextTimeout);
}
}
// Now check for any stalled half open sockets.
if (mHalfOpens.Length()) {
TimeStamp currentTime = TimeStamp::Now();
double maxConnectTime_ms = gHttpHandler->ConnectTimeout();
for (uint32_t index = mHalfOpens.Length(); index > 0;) {
index--;
HalfOpenSocket* half = mHalfOpens[index];
double delta = half->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 half open to %s after %.2fms.\n",
mConnInfo->HashKey().get(), delta));
if (half->SocketTransport()) {
half->SocketTransport()->Close(NS_ERROR_NET_TIMEOUT);
}
if (half->BackupTransport()) {
half->BackupTransport()->Close(NS_ERROR_NET_TIMEOUT);
}
}
// If this half open hangs around for 5 seconds after we've
// closed() it then just abandon the socket.
if (delta > maxConnectTime_ms + 5000) {
LOG(("Abandon half open to %s after %.2fms.\n",
mConnInfo->HashKey().get(), delta));
half->Abandon();
}
}
}
if (mHalfOpens.Length()) {
timeoutTickNext = 1;
}
return timeoutTickNext;
}
void ConnectionEntry::MoveConnection(HttpConnectionBase* proxyConn,
ConnectionEntry* otherEnt) {
// To avoid changing mNumActiveConns/mNumIdleConns counter use internal
// functions.
RefPtr<HttpConnectionBase> deleteProtector(proxyConn);
if (mActiveConns.RemoveElement(proxyConn)) {
otherEnt->mActiveConns.AppendElement(proxyConn);
return;
}
RefPtr<nsHttpConnection> proxyConnTCP = do_QueryObject(proxyConn);
if (proxyConnTCP) {
if (mIdleConns.RemoveElement(proxyConnTCP)) {
otherEnt->InsertIntoIdleConnections_internal(proxyConnTCP);
return;
}
}
}
void ConnectionEntry::InsertIntoHalfOpens(HalfOpenSocket* sock) {
mHalfOpens.AppendElement(sock);
gHttpHandler->ConnMgr()->IncreaseNumHalfOpenConns();
}
void ConnectionEntry::InsertIntoHalfOpenFastOpenBackups(HalfOpenSocket* sock) {
mHalfOpenFastOpenBackups.AppendElement(sock);
}
void ConnectionEntry::CloseAllHalfOpens() {
for (int32_t i = int32_t(HalfOpensLength()) - 1; i >= 0; i--) {
mHalfOpens[i]->Abandon();
}
}
void ConnectionEntry::RemoveHalfOpenFastOpenBackups(HalfOpenSocket* sock) {
mHalfOpenFastOpenBackups.RemoveElement(sock);
}
bool ConnectionEntry::IsInHalfOpens(HalfOpenSocket* sock) {
return mHalfOpens.Contains(sock);
}
HttpRetParams ConnectionEntry::GetConnectionData() {
HttpRetParams data;
data.host = mConnInfo->Origin();
data.port = mConnInfo->OriginPort();
for (uint32_t i = 0; i < mActiveConns.Length(); i++) {
HttpConnInfo info;
RefPtr<nsHttpConnection> connTCP = do_QueryObject(mActiveConns[i]);
if (connTCP) {
info.ttl = connTCP->TimeToLive();
} else {
info.ttl = 0;
}
info.rtt = mActiveConns[i]->Rtt();
info.SetHTTPProtocolVersion(mActiveConns[i]->Version());
data.active.AppendElement(info);
}
for (uint32_t i = 0; i < mIdleConns.Length(); i++) {
HttpConnInfo info;
info.ttl = mIdleConns[i]->TimeToLive();
info.rtt = mIdleConns[i]->Rtt();
info.SetHTTPProtocolVersion(mIdleConns[i]->Version());
data.idle.AppendElement(info);
}
for (uint32_t i = 0; i < mHalfOpens.Length(); i++) {
HalfOpenSockets hSocket;
hSocket.speculative = mHalfOpens[i]->IsSpeculative();
data.halfOpens.AppendElement(hSocket);
}
if (mConnInfo->IsHttp3()) {
data.httpVersion = "HTTP/3"_ns;
} else if (mUsingSpdy) {
data.httpVersion = "HTTP/2"_ns;
} else {
data.httpVersion = "HTTP <= 1.1"_ns;
}
data.ssl = mConnInfo->EndToEndSSL();
return data;
}
void ConnectionEntry::LogConnections() {
if (!mConnInfo->IsHttp3()) {
LOG(("active urgent conns ["));
for (HttpConnectionBase* conn : mActiveConns) {
RefPtr<nsHttpConnection> connTCP = do_QueryObject(conn);
MOZ_ASSERT(connTCP);
if (connTCP->IsUrgentStartPreferred()) {
LOG((" %p", conn));
}
}
LOG(("] active regular conns ["));
for (HttpConnectionBase* conn : mActiveConns) {
RefPtr<nsHttpConnection> connTCP = do_QueryObject(conn);
MOZ_ASSERT(connTCP);
if (!connTCP->IsUrgentStartPreferred()) {
LOG((" %p", conn));
}
}
LOG(("] idle urgent conns ["));
for (nsHttpConnection* conn : mIdleConns) {
if (conn->IsUrgentStartPreferred()) {
LOG((" %p", conn));
}
}
LOG(("] idle regular conns ["));
for (nsHttpConnection* conn : mIdleConns) {
if (!conn->IsUrgentStartPreferred()) {
LOG((" %p", conn));
}
}
} else {
LOG(("active conns ["));
for (HttpConnectionBase* conn : mActiveConns) {
LOG((" %p", conn));
}
MOZ_ASSERT(mIdleConns.Length() == 0);
}
LOG(("]"));
}
bool ConnectionEntry::RemoveTransFromPendingQ(nsHttpTransaction* aTrans) {
// We will abandon all half-open sockets belonging to the given
// transaction.
nsTArray<RefPtr<PendingTransactionInfo>>* infoArray =
GetTransactionPendingQHelper(aTrans);
RefPtr<PendingTransactionInfo> pendingTransInfo;
int32_t transIndex =
infoArray ? infoArray->IndexOf(aTrans, 0, PendingComparator()) : -1;
if (transIndex >= 0) {
pendingTransInfo = (*infoArray)[transIndex];
infoArray->RemoveElementAt(transIndex);
}
if (!pendingTransInfo) {
return false;
}
// Abandon all half-open sockets belonging to the given transaction.
pendingTransInfo->AbandonHalfOpenAndForgetActiveConn();
return true;
}
} // namespace net
} // namespace mozilla