From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- netwerk/protocol/http/nsHttpConnectionMgr.h | 636 ++++++++++++++++++++++++++++ 1 file changed, 636 insertions(+) create mode 100644 netwerk/protocol/http/nsHttpConnectionMgr.h (limited to 'netwerk/protocol/http/nsHttpConnectionMgr.h') diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.h b/netwerk/protocol/http/nsHttpConnectionMgr.h new file mode 100644 index 000000000..7ca2a2b28 --- /dev/null +++ b/netwerk/protocol/http/nsHttpConnectionMgr.h @@ -0,0 +1,636 @@ +/* vim:set ts=4 sw=4 sts=4 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/. */ + +#ifndef nsHttpConnectionMgr_h__ +#define nsHttpConnectionMgr_h__ + +#include "nsHttpConnection.h" +#include "nsHttpTransaction.h" +#include "nsTArray.h" +#include "nsThreadUtils.h" +#include "nsClassHashtable.h" +#include "nsDataHashtable.h" +#include "nsAutoPtr.h" +#include "mozilla/ReentrantMonitor.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/Attributes.h" +#include "AlternateServices.h" +#include "ARefBase.h" + +#include "nsIObserver.h" +#include "nsITimer.h" + +class nsIHttpUpgradeListener; + +namespace mozilla { +namespace net { +class EventTokenBucket; +class NullHttpTransaction; +struct HttpRetParams; + +//----------------------------------------------------------------------------- + +// message handlers have this signature +class nsHttpConnectionMgr; +typedef void (nsHttpConnectionMgr:: *nsConnEventHandler)(int32_t, ARefBase *); + +class nsHttpConnectionMgr final : public nsIObserver + , public AltSvcCache +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIOBSERVER + + // parameter names + enum nsParamName { + MAX_CONNECTIONS, + MAX_PERSISTENT_CONNECTIONS_PER_HOST, + MAX_PERSISTENT_CONNECTIONS_PER_PROXY, + MAX_REQUEST_DELAY, + MAX_PIPELINED_REQUESTS, + MAX_OPTIMISTIC_PIPELINED_REQUESTS + }; + + //------------------------------------------------------------------------- + // NOTE: functions below may only be called on the main thread. + //------------------------------------------------------------------------- + + nsHttpConnectionMgr(); + + nsresult Init(uint16_t maxConnections, + uint16_t maxPersistentConnectionsPerHost, + uint16_t maxPersistentConnectionsPerProxy, + uint16_t maxRequestDelay, + uint16_t maxPipelinedRequests, + uint16_t maxOptimisticPipelinedRequests); + nsresult Shutdown(); + + //------------------------------------------------------------------------- + // NOTE: functions below may be called on any thread. + //------------------------------------------------------------------------- + + // Schedules next pruning of dead connection to happen after + // given time. + void PruneDeadConnectionsAfter(uint32_t time); + + // Stops timer scheduled for next pruning of dead connections if + // there are no more idle connections or active spdy ones + void ConditionallyStopPruneDeadConnectionsTimer(); + + // Stops timer used for the read timeout tick if there are no currently + // active connections. + void ConditionallyStopTimeoutTick(); + + // adds a transaction to the list of managed transactions. + nsresult AddTransaction(nsHttpTransaction *, int32_t priority); + + // called to reschedule the given transaction. it must already have been + // added to the connection manager via AddTransaction. + nsresult RescheduleTransaction(nsHttpTransaction *, int32_t priority); + + // cancels a transaction w/ the given reason. + nsresult CancelTransaction(nsHttpTransaction *, nsresult reason); + nsresult CancelTransactions(nsHttpConnectionInfo *, nsresult reason); + + // called to force the connection manager to prune its list of idle + // connections. + nsresult PruneDeadConnections(); + + // called to close active connections with no registered "traffic" + nsresult PruneNoTraffic(); + + // "VerifyTraffic" means marking connections now, and then check again in + // N seconds to see if there's been any traffic and if not, kill + // that connection. + nsresult VerifyTraffic(); + + // Close all idle persistent connections and prevent any active connections + // from being reused. Optional connection info resets CI specific + // information such as Happy Eyeballs history. + nsresult DoShiftReloadConnectionCleanup(nsHttpConnectionInfo *); + + // called to get a reference to the socket transport service. the socket + // transport service is not available when the connection manager is down. + nsresult GetSocketThreadTarget(nsIEventTarget **); + + // called to indicate a transaction for the connectionInfo is likely coming + // soon. The connection manager may use this information to start a TCP + // and/or SSL level handshake for that resource immediately so that it is + // ready when the transaction is submitted. No obligation is taken on by the + // connection manager, nor is the submitter obligated to actually submit a + // real transaction for this connectionInfo. + nsresult SpeculativeConnect(nsHttpConnectionInfo *, + nsIInterfaceRequestor *, + uint32_t caps = 0, + NullHttpTransaction * = nullptr); + + // called when a connection is done processing a transaction. if the + // connection can be reused then it will be added to the idle list, else + // it will be closed. + nsresult ReclaimConnection(nsHttpConnection *conn); + + // called by the main thread to execute the taketransport() logic on the + // socket thread after a 101 response has been received and the socket + // needs to be transferred to an expectant upgrade listener such as + // websockets. + nsresult CompleteUpgrade(nsAHttpConnection *aConn, + nsIHttpUpgradeListener *aUpgradeListener); + + // called to update a parameter after the connection manager has already + // been initialized. + nsresult UpdateParam(nsParamName name, uint16_t value); + + // called from main thread to post a new request token bucket + // to the socket thread + nsresult UpdateRequestTokenBucket(EventTokenBucket *aBucket); + + // clears the connection history mCT + nsresult ClearConnectionHistory(); + + // Pipielining Interfaces and Datatypes + + const static uint32_t kPipelineInfoTypeMask = 0xffff0000; + const static uint32_t kPipelineInfoIDMask = ~kPipelineInfoTypeMask; + + const static uint32_t kPipelineInfoTypeRed = 0x00010000; + const static uint32_t kPipelineInfoTypeBad = 0x00020000; + const static uint32_t kPipelineInfoTypeNeutral = 0x00040000; + const static uint32_t kPipelineInfoTypeGood = 0x00080000; + + enum PipelineFeedbackInfoType + { + // Used when an HTTP response less than 1.1 is received + RedVersionTooLow = kPipelineInfoTypeRed | kPipelineInfoTypeBad | 0x0001, + + // Used when a HTTP Server response header that is on the banned from + // pipelining list is received + RedBannedServer = kPipelineInfoTypeRed | kPipelineInfoTypeBad | 0x0002, + + // Used when a response is terminated early, when it fails an + // integrity check such as assoc-req or when a 304 contained a Last-Modified + // differnet than the entry being validated. + RedCorruptedContent = kPipelineInfoTypeRed | kPipelineInfoTypeBad | 0x0004, + + // Used when a pipeline is only partly satisfied - for instance if the + // server closed the connection after responding to the first + // request but left some requests unprocessed. + RedCanceledPipeline = kPipelineInfoTypeRed | kPipelineInfoTypeBad | 0x0005, + + // Used when a connection that we expected to stay persistently open + // was closed by the server. Not used when simply timed out. + BadExplicitClose = kPipelineInfoTypeBad | 0x0003, + + // Used when there is a gap of around 400 - 1200ms in between data being + // read from the server + BadSlowReadMinor = kPipelineInfoTypeBad | 0x0006, + + // Used when there is a gap of > 1200ms in between data being + // read from the server + BadSlowReadMajor = kPipelineInfoTypeBad | 0x0007, + + // Used when a response is received that is not framed with either chunked + // encoding or a complete content length. + BadInsufficientFraming = kPipelineInfoTypeBad | 0x0008, + + // Used when a very large response is recevied in a potential pipelining + // context. Large responses cause head of line blocking. + BadUnexpectedLarge = kPipelineInfoTypeBad | 0x000B, + + // Used when a response is received that has headers that appear to support + // pipelining. + NeutralExpectedOK = kPipelineInfoTypeNeutral | 0x0009, + + // Used when a response is received successfully to a pipelined request. + GoodCompletedOK = kPipelineInfoTypeGood | 0x000A + }; + + // called to provide information relevant to the pipelining manager + // may be called from any thread + void PipelineFeedbackInfo(nsHttpConnectionInfo *, + PipelineFeedbackInfoType info, + nsHttpConnection *, + uint32_t); + + void ReportFailedToProcess(nsIURI *uri); + + // Causes a large amount of connection diagnostic information to be + // printed to the javascript console + void PrintDiagnostics(); + + //------------------------------------------------------------------------- + // NOTE: functions below may be called only on the socket thread. + //------------------------------------------------------------------------- + + // called to change the connection entry associated with conn from specific into + // a wildcard (i.e. http2 proxy friendy) mapping + void MoveToWildCardConnEntry(nsHttpConnectionInfo *specificCI, + nsHttpConnectionInfo *wildcardCI, + nsHttpConnection *conn); + + // called to force the transaction queue to be processed once more, giving + // preference to the specified connection. + nsresult ProcessPendingQ(nsHttpConnectionInfo *); + bool ProcessPendingQForEntry(nsHttpConnectionInfo *); + + // Try and process all pending transactions + nsresult ProcessPendingQ(); + + // This is used to force an idle connection to be closed and removed from + // the idle connection list. It is called when the idle connection detects + // that the network peer has closed the transport. + nsresult CloseIdleConnection(nsHttpConnection *); + + // The connection manager needs to know when a normal HTTP connection has been + // upgraded to SPDY because the dispatch and idle semantics are a little + // bit different. + void ReportSpdyConnection(nsHttpConnection *, bool usingSpdy); + + bool SupportsPipelining(nsHttpConnectionInfo *); + + bool GetConnectionData(nsTArray *); + + void ResetIPFamilyPreference(nsHttpConnectionInfo *); + + uint16_t MaxRequestDelay() { return mMaxRequestDelay; } + + // public, so that the SPDY/http2 seesions can activate + void ActivateTimeoutTick(); + +private: + virtual ~nsHttpConnectionMgr(); + + enum PipeliningState { + // Host has proven itself pipeline capable through past experience and + // large pipeline depths are allowed on multiple connections. + PS_GREEN, + + // Not enough information is available yet with this host to be certain + // of pipeline capability. Small pipelines on a single connection are + // allowed in order to decide whether or not to proceed to green. + PS_YELLOW, + + // One or more bad events has happened that indicate that pipelining + // to this host (or a particular type of transaction with this host) + // is a bad idea. Pipelining is not currently allowed, but time and + // other positive experiences will eventually allow it to try again. + PS_RED + }; + + class nsHalfOpenSocket; + + // nsConnectionEntry + // + // mCT maps connection info hash key to nsConnectionEntry object, which + // contains list of active and idle connections as well as the list of + // pending transactions. + // + class nsConnectionEntry + { + public: + explicit nsConnectionEntry(nsHttpConnectionInfo *ci); + ~nsConnectionEntry(); + + RefPtr mConnInfo; + nsTArray > mPendingQ; // pending transaction queue + nsTArray > mActiveConns; // active connections + nsTArray > mIdleConns; // idle persistent connections + nsTArray mHalfOpens; // half open connections + + bool AvailableForDispatchNow(); + + // calculate the number of half open sockets that have not had at least 1 + // connection complete + uint32_t UnconnectedHalfOpens(); + + // Remove a particular half open socket from the mHalfOpens array + void RemoveHalfOpen(nsHalfOpenSocket *); + + // Pipeline depths for various states + const static uint32_t kPipelineUnlimited = 1024; // fully open - extended green + const static uint32_t kPipelineOpen = 6; // 6 on each conn - normal green + const static uint32_t kPipelineRestricted = 2; // 2 on just 1 conn in yellow + + nsHttpConnectionMgr::PipeliningState PipelineState(); + void OnPipelineFeedbackInfo( + nsHttpConnectionMgr::PipelineFeedbackInfoType info, + nsHttpConnection *, uint32_t); + bool SupportsPipelining(); + uint32_t MaxPipelineDepth(nsAHttpTransaction::Classifier classification); + void CreditPenalty(); + + nsHttpConnectionMgr::PipeliningState mPipelineState; + + void SetYellowConnection(nsHttpConnection *); + void OnYellowComplete(); + uint32_t mYellowGoodEvents; + uint32_t mYellowBadEvents; + nsHttpConnection *mYellowConnection; + + // initialGreenDepth is the max depth of a pipeline when you first + // transition to green. Normally this is kPipelineOpen, but it can + // be kPipelineUnlimited in aggressive mode. + uint32_t mInitialGreenDepth; + + // greenDepth is the current max allowed depth of a pipeline when + // in the green state. Normally this starts as kPipelineOpen and + // grows to kPipelineUnlimited after a pipeline of depth 3 has been + // successfully transacted. + uint32_t mGreenDepth; + + // pipeliningPenalty is the current amount of penalty points this host + // entry has earned for participating in events that are not conducive + // to good pipelines - such as head of line blocking, canceled pipelines, + // etc.. penalties are paid back either through elapsed time or simply + // healthy transactions. Having penalty points means that this host is + // not currently eligible for pipelines. + int16_t mPipeliningPenalty; + + // some penalty points only apply to particular classifications of + // transactions - this allows a server that perhaps has head of line + // blocking problems on CGI queries to still serve JS pipelined. + int16_t mPipeliningClassPenalty[nsAHttpTransaction::CLASS_MAX]; + + // for calculating penalty repair credits + TimeStamp mLastCreditTime; + + // Spdy sometimes resolves the address in the socket manager in order + // to re-coalesce sharded HTTP hosts. The dotted decimal address is + // combined with the Anonymous flag from the connection information + // to build the hash key for hosts in the same ip pool. + // + // When a set of hosts are coalesced together one of them is marked + // mSpdyPreferred. The mapping is maintained in the connection mananger + // mSpdyPreferred hash. + // + nsTArray mCoalescingKeys; + + // To have the UsingSpdy flag means some host with the same connection + // entry has done NPN=spdy/* at some point. It does not mean every + // connection is currently using spdy. + bool mUsingSpdy : 1; + + bool mInPreferredHash : 1; + + // Flags to remember our happy-eyeballs decision. + // Reset only by Ctrl-F5 reload. + // True when we've first connected an IPv4 server for this host, + // initially false. + bool mPreferIPv4 : 1; + // True when we've first connected an IPv6 server for this host, + // initially false. + bool mPreferIPv6 : 1; + + // True if this connection entry has initiated a socket + bool mUsedForConnection : 1; + + // Set the IP family preference flags according the connected family + void RecordIPFamilyPreference(uint16_t family); + // Resets all flags to their default values + void ResetIPFamilyPreference(); + }; + +public: + static nsAHttpConnection *MakeConnectionHandle(nsHttpConnection *aWrapped); + +private: + + // nsHalfOpenSocket is used to hold the state of an opening TCP socket + // while we wait for it to establish and bind it to a connection + + class nsHalfOpenSocket final : public nsIOutputStreamCallback, + public nsITransportEventSink, + public nsIInterfaceRequestor, + public nsITimerCallback + { + ~nsHalfOpenSocket(); + + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIOUTPUTSTREAMCALLBACK + NS_DECL_NSITRANSPORTEVENTSINK + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSITIMERCALLBACK + + nsHalfOpenSocket(nsConnectionEntry *ent, + nsAHttpTransaction *trans, + uint32_t caps); + + nsresult SetupStreams(nsISocketTransport **, + nsIAsyncInputStream **, + nsIAsyncOutputStream **, + bool isBackup); + nsresult SetupPrimaryStreams(); + nsresult SetupBackupStreams(); + void SetupBackupTimer(); + void CancelBackupTimer(); + void Abandon(); + double Duration(TimeStamp epoch); + nsISocketTransport *SocketTransport() { return mSocketTransport; } + nsISocketTransport *BackupTransport() { return mBackupTransport; } + + nsAHttpTransaction *Transaction() { return mTransaction; } + + bool IsSpeculative() { return mSpeculative; } + void SetSpeculative(bool val) { mSpeculative = val; } + + bool IsFromPredictor() { return mIsFromPredictor; } + void SetIsFromPredictor(bool val) { mIsFromPredictor = val; } + + bool Allow1918() { return mAllow1918; } + void SetAllow1918(bool val) { mAllow1918 = val; } + + bool HasConnected() { return mHasConnected; } + + void PrintDiagnostics(nsCString &log); + private: + nsConnectionEntry *mEnt; + RefPtr mTransaction; + bool mDispatchedMTransaction; + nsCOMPtr mSocketTransport; + nsCOMPtr mStreamOut; + nsCOMPtr mStreamIn; + uint32_t mCaps; + + // mSpeculative is set if the socket was created from + // SpeculativeConnect(). It is cleared when a transaction would normally + // start a new connection from scratch but instead finds this one in + // the half open list and claims it for its own use. (which due to + // the vagaries of scheduling from the pending queue might not actually + // match up - but it prevents a speculative connection from opening + // more connections that are needed.) + bool mSpeculative; + + // mIsFromPredictor is set if the socket originated from the network + // Predictor. It is used to gather telemetry data on used speculative + // connections from the predictor. + bool mIsFromPredictor; + + bool mAllow1918; + + TimeStamp mPrimarySynStarted; + TimeStamp mBackupSynStarted; + + // for syn retry + nsCOMPtr mSynTimer; + nsCOMPtr mBackupTransport; + nsCOMPtr mBackupStreamOut; + nsCOMPtr mBackupStreamIn; + + // mHasConnected tracks whether one of the sockets has completed the + // connection process. It may have completed unsuccessfully. + bool mHasConnected; + + bool mPrimaryConnectedOK; + bool mBackupConnectedOK; + }; + friend class nsHalfOpenSocket; + + //------------------------------------------------------------------------- + // NOTE: these members may be accessed from any thread (use mReentrantMonitor) + //------------------------------------------------------------------------- + + ReentrantMonitor mReentrantMonitor; + nsCOMPtr mSocketThreadTarget; + + // connection limits + uint16_t mMaxConns; + uint16_t mMaxPersistConnsPerHost; + uint16_t mMaxPersistConnsPerProxy; + uint16_t mMaxRequestDelay; // in seconds + uint16_t mMaxPipelinedRequests; + uint16_t mMaxOptimisticPipelinedRequests; + Atomic mIsShuttingDown; + + //------------------------------------------------------------------------- + // NOTE: these members are only accessed on the socket transport thread + //------------------------------------------------------------------------- + + bool ProcessPendingQForEntry(nsConnectionEntry *, bool considerAll); + bool IsUnderPressure(nsConnectionEntry *ent, + nsHttpTransaction::Classifier classification); + bool AtActiveConnectionLimit(nsConnectionEntry *, uint32_t caps); + nsresult TryDispatchTransaction(nsConnectionEntry *ent, + bool onlyReusedConnection, + nsHttpTransaction *trans); + nsresult DispatchTransaction(nsConnectionEntry *, + nsHttpTransaction *, + nsHttpConnection *); + nsresult DispatchAbstractTransaction(nsConnectionEntry *, + nsAHttpTransaction *, + uint32_t, + nsHttpConnection *, + int32_t); + nsresult BuildPipeline(nsConnectionEntry *, + nsAHttpTransaction *, + nsHttpPipeline **); + bool RestrictConnections(nsConnectionEntry *); + nsresult ProcessNewTransaction(nsHttpTransaction *); + nsresult EnsureSocketThreadTarget(); + void ClosePersistentConnections(nsConnectionEntry *ent); + void ReportProxyTelemetry(nsConnectionEntry *ent); + nsresult CreateTransport(nsConnectionEntry *, nsAHttpTransaction *, + uint32_t, bool, bool, bool); + void AddActiveConn(nsHttpConnection *, nsConnectionEntry *); + void DecrementActiveConnCount(nsHttpConnection *); + void StartedConnect(); + void RecvdConnect(); + + nsConnectionEntry *GetOrCreateConnectionEntry(nsHttpConnectionInfo *, + bool allowWildCard); + + nsresult MakeNewConnection(nsConnectionEntry *ent, + nsHttpTransaction *trans); + bool AddToShortestPipeline(nsConnectionEntry *ent, + nsHttpTransaction *trans, + nsHttpTransaction::Classifier classification, + uint16_t depthLimit); + + // Manage the preferred spdy connection entry for this address + nsConnectionEntry *GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry); + nsConnectionEntry *LookupPreferredHash(nsConnectionEntry *ent); + void StorePreferredHash(nsConnectionEntry *ent); + void RemovePreferredHash(nsConnectionEntry *ent); + nsHttpConnection *GetSpdyPreferredConn(nsConnectionEntry *ent); + nsDataHashtable mSpdyPreferredHash; + nsConnectionEntry *LookupConnectionEntry(nsHttpConnectionInfo *ci, + nsHttpConnection *conn, + nsHttpTransaction *trans); + + void ProcessSpdyPendingQ(nsConnectionEntry *ent); + + // used to marshall events to the socket transport thread. + nsresult PostEvent(nsConnEventHandler handler, + int32_t iparam = 0, + ARefBase *vparam = nullptr); + + // message handlers + void OnMsgShutdown (int32_t, ARefBase *); + void OnMsgShutdownConfirm (int32_t, ARefBase *); + void OnMsgNewTransaction (int32_t, ARefBase *); + void OnMsgReschedTransaction (int32_t, ARefBase *); + void OnMsgCancelTransaction (int32_t, ARefBase *); + void OnMsgCancelTransactions (int32_t, ARefBase *); + void OnMsgProcessPendingQ (int32_t, ARefBase *); + void OnMsgPruneDeadConnections (int32_t, ARefBase *); + void OnMsgSpeculativeConnect (int32_t, ARefBase *); + void OnMsgReclaimConnection (int32_t, ARefBase *); + void OnMsgCompleteUpgrade (int32_t, ARefBase *); + void OnMsgUpdateParam (int32_t, ARefBase *); + void OnMsgDoShiftReloadConnectionCleanup (int32_t, ARefBase *); + void OnMsgProcessFeedback (int32_t, ARefBase *); + void OnMsgProcessAllSpdyPendingQ (int32_t, ARefBase *); + void OnMsgUpdateRequestTokenBucket (int32_t, ARefBase *); + void OnMsgVerifyTraffic (int32_t, ARefBase *); + void OnMsgPruneNoTraffic (int32_t, ARefBase *); + + // Total number of active connections in all of the ConnectionEntry objects + // that are accessed from mCT connection table. + uint16_t mNumActiveConns; + // Total number of idle connections in all of the ConnectionEntry objects + // that are accessed from mCT connection table. + uint16_t mNumIdleConns; + // Total number of spdy connections which are a subset of the active conns + uint16_t mNumSpdyActiveConns; + // Total number of connections in mHalfOpens ConnectionEntry objects + // that are accessed from mCT connection table + uint32_t mNumHalfOpenConns; + + // Holds time in seconds for next wake-up to prune dead connections. + uint64_t mTimeOfNextWakeUp; + // Timer for next pruning of dead connections. + nsCOMPtr mTimer; + // Timer for pruning stalled connections after changed network. + nsCOMPtr mTrafficTimer; + bool mPruningNoTraffic; + + // A 1s tick to call nsHttpConnection::ReadTimeoutTick on + // active http/1 connections and check for orphaned half opens. + // Disabled when there are no active or half open connections. + nsCOMPtr mTimeoutTick; + bool mTimeoutTickArmed; + uint32_t mTimeoutTickNext; + + // + // the connection table + // + // this table is indexed by connection key. each entry is a + // nsConnectionEntry object. It is unlocked and therefore must only + // be accessed from the socket thread. + // + nsClassHashtable mCT; + + // Read Timeout Tick handlers + void TimeoutTick(); + + // For diagnostics + void OnMsgPrintDiagnostics(int32_t, ARefBase *); + + nsCString mLogData; +}; + +} // namespace net +} // namespace mozilla + +#endif // !nsHttpConnectionMgr_h__ -- cgit v1.2.3