SSL ConnectionEntry.cpp
Interaktion und PortierbarkeitC
/* 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()
void ConnectionEntry::RemoveDnsAndConnectSocket(DnsAndConnectSocket* dnsAndSock, bool abandon) { if (abandon) {
dnsAndSock->Abandon();
} if (mDnsAndConnectSockets.RemoveElement(dnsAndSock)) {
gHttpHandler->ConnMgr()->DecreaseNumDnsAndConnectSockets();
}
if (!UnconnectedDnsAndConnectSockets()) { // 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::RemoveDnsAndConnectSocket\n" " failed to process pending queue\n"));
}
}
}
void ConnectionEntry::CloseAllDnsAndConnectSockets() { for (constauto& dnsAndSock : mDnsAndConnectSockets) {
dnsAndSock->Abandon();
gHttpHandler->ConnMgr()->DecreaseNumDnsAndConnectSockets();
}
mDnsAndConnectSockets.Clear();
nsresult rv = gHttpHandler->ConnMgr()->ProcessPendingQ(mConnInfo); if (NS_FAILED(rv)) {
LOG(
("ConnectionEntry::CloseAllDnsAndConnectSockets\n" " failed to process pending queue\n"));
}
}
// 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();
mAddresses.Clear();
}
// 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();
mAddresses.Clear();
}
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())); returntrue;
}
// 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.
// If there are no restrictions, we are done if (!doRestrict) { returnfalse;
}
// If the restriction is based on a tcp handshake in progress // let that connect and then see if it was SPDY or not if (UnconnectedDnsAndConnectSockets()) { returntrue;
}
// 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 DnsAndConnectSocket'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() + UnconnectedDnsAndConnectSockets();
}
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++;
}
}
void ConnectionEntry::CloseH2WebsocketConnections() { while (mH2WebsocketConns.Length()) {
RefPtr<HttpConnectionBase> conn(mH2WebsocketConns[0]);
mH2WebsocketConns.RemoveElementAt(0);
// safe to close connection since we are on the socket thread // closing via transaction to break connection/transaction bond
conn->CloseTransaction(conn->Transaction(), NS_ERROR_ABORT, true);
}
}
nsresult ConnectionEntry::RemoveIdleConnection(nsHttpConnection* conn) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
if (!RemoveFromIdleConnections(conn)) { return NS_ERROR_UNEXPECTED;
}
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;
}
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::MakeConnectionPendingAndDontReuse(
HttpConnectionBase* conn) {
gHttpHandler->ConnMgr()->DecrementActiveConnCount(conn);
mPendingConns.AppendElement(conn); // After DontReuse(), the connection will be closed after the last // transition is done.
conn->DontReuse();
LOG(("Move active connection to pending list [conn=%p]\n", conn));
}
template <typename ConnType> staticvoid CheckForTrafficForConns(nsTArray<RefPtr<ConnType>>& aConns, bool aCheck) { for (uint32_t index = 0; index < aConns.Length(); ++index) {
RefPtr<nsHttpConnection> conn = do_QueryObject(aConns[index]); if (conn) {
conn->CheckForTraffic(aCheck);
}
}
}
void ConnectionEntry::VerifyTraffic() { if (!mConnInfo->IsHttp3()) {
CheckForTrafficForConns(mPendingConns, true); // Iterate the idle connections and unmark them for traffic checks.
CheckForTrafficForConns(mIdleConns, false);
}
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]);
RefPtr<HttpConnectionUDP> connUDP = do_QueryObject(mActiveConns[index]); if (conn) {
conn->CheckForTraffic(true); if (conn->EverUsedSpdy() &&
StaticPrefs::
network_http_move_to_pending_list_after_network_change()) {
mActiveConns.RemoveElementAt(index);
MakeConnectionPendingAndDontReuse(conn);
}
} elseif (connUDP &&
StaticPrefs::
network_http_move_to_pending_list_after_network_change()) {
mActiveConns.RemoveElementAt(index);
MakeConnectionPendingAndDontReuse(connUDP);
}
}
}
}
void ConnectionEntry::InsertIntoH2WebsocketConns(HttpConnectionBase* conn) { // no incrementing of connection count since it is just a "fake" connection
mH2WebsocketConns.AppendElement(conn);
}
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->SetCloseReason(
ConnectionCloseReason::CLOSE_EXISTING_CONN_FOR_COALESCING);
otherConn->DontReuse();
}
}
// Cancel any other pending connections - their associated transactions // are in the pending queue and will be dispatched onto this new connection
CloseAllDnsAndConnectSockets();
}
bool ConnectionEntry::FindConnToClaim(
PendingTransactionInfo* pendingTransInfo) {
nsHttpTransaction* trans = pendingTransInfo->Transaction();
for (constauto& dnsAndSock : mDnsAndConnectSockets) { if (dnsAndSock->AcceptsTransaction(trans) && dnsAndSock->Claim()) {
pendingTransInfo->RememberDnsAndConnectSocket(dnsAndSock); // We've found a speculative connection or a connection that // is free to be used in the DnsAndConnectSockets 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 DnsAndConnectSocket\n",
mConnInfo->HashKey().get()));
// return OK because we have essentially opened a new connection // by converting a speculative DnsAndConnectSockets to general use returntrue;
}
}
// 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())); returntrue;
}
}
} returnfalse;
}
bool ConnectionEntry::MakeFirstActiveSpdyConnDontReuse() { if (!mUsingSpdy) { returnfalse;
}
for (uint32_t index = 0; index < mActiveConns.Length(); ++index) {
HttpConnectionBase* conn = mActiveConns[index]; if (conn->UsingSpdy() && conn->CanReuse()) {
conn->DontReuse(); returntrue;
}
} returnfalse;
}
// Return an active h2 or h3 connection // that can be directly activated or null.
HttpConnectionBase* ConnectionEntry::GetH2orH3ActiveConn() {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
// 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();
}
}
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::ClosePendingConnections() { while (mPendingConns.Length()) {
RefPtr<HttpConnectionBase> conn(mPendingConns[0]);
mPendingConns.RemoveElementAt(0);
// 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::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()));
}
}
}
}
// 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 DnsAndConnectSockets. if (mDnsAndConnectSockets.Length()) {
TimeStamp currentTime = TimeStamp::Now(); double maxConnectTime_ms = gHttpHandler->ConnectTimeout();
for (constauto& dnsAndSock : Reversed(mDnsAndConnectSockets)) { double delta = dnsAndSock->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 DnsAndConnectSocket to %s after %.2fms.\n",
mConnInfo->HashKey().get(), delta));
dnsAndSock->CloseTransports(NS_ERROR_NET_TIMEOUT);
}
// If this DnsAndConnectSocket hangs around for 5 seconds after we've // closed() it then just abandon the socket. if (delta > maxConnectTime_ms + 5000) {
LOG(("Abandon DnsAndConnectSocket to %s after %.2fms.\n",
mConnInfo->HashKey().get(), delta));
RemoveDnsAndConnectSocket(dnsAndSock, true);
}
}
} if (mDnsAndConnectSockets.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;
}
}
}
bool ConnectionEntry::RemoveTransFromPendingQ(nsHttpTransaction* aTrans) { // We will abandon all DnsAndConnectSockets belonging to the given // transaction.
nsTArray<RefPtr<PendingTransactionInfo>>* infoArray =
GetTransactionPendingQHelper(aTrans);
// Abandon all DnsAndConnectSockets belonging to the given transaction.
nsWeakPtr tmp = pendingTransInfo->ForgetDnsAndConnectSocketAndActiveConn();
RefPtr<DnsAndConnectSocket> dnsAndSock = do_QueryReferent(tmp); if (dnsAndSock) {
RemoveDnsAndConnectSocket(dnsAndSock, true);
} returntrue;
}
void ConnectionEntry::MaybeUpdateEchConfig(nsHttpConnectionInfo* aConnInfo) { if (!mConnInfo->HashKey().Equals(aConnInfo->HashKey())) { return;
}
const nsCString& echConfig = aConnInfo->GetEchConfig(); if (mConnInfo->GetEchConfig().Equals(echConfig)) { return;
}
// If echConfig is changed, we should close all DnsAndConnectSockets and idle // connections. This is to make sure the new echConfig will be used for the // next connection.
CloseAllDnsAndConnectSockets();
CloseIdleConnections();
}
if (pendingTransInfo && sock->Claim()) {
pendingTransInfo->RememberDnsAndConnectSocket(sock);
}
return NS_OK;
}
bool ConnectionEntry::AllowToRetryDifferentIPFamilyForHttp3(nsresult aError) {
LOG(
("ConnectionEntry::AllowToRetryDifferentIPFamilyForHttp3 %p " "error=%" PRIx32, this, static_cast<uint32_t>(aError))); if (!IsHttp3()) {
MOZ_ASSERT(false, "Should not be called for non Http/3 connection"); returnfalse;
}
if (!StaticPrefs::network_http_http3_retry_different_ip_family()) { returnfalse;
}
// Only allow to retry with these two errors. if (aError != NS_ERROR_CONNECTION_REFUSED &&
aError != NS_ERROR_PROXY_CONNECTION_REFUSED) { returnfalse;
}
// Already retried once. if (mRetriedDifferentIPFamilyForHttp3) { returnfalse;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.