/*++ Copyright (c) 1996 Microsoft Corporation Module Name: servinfo.hxx Abstract: Contains the CServerInfo class declaration Author: Richard L Firth (rfirth) 02-Oct-1996 Notes: In this implementation, we maintain a single host name per server Revision History: 02-Oct-1996 rfirth Created --*/ // // manifests // #define SERVER_INFO_SIGNATURE 'fIvS' #define UNLIMITED_CONNECTIONS 0xffffffff // // values for PurgeKeepAlives dwForce parameter, method & function // #define PKA_NO_FORCE 0 #define PKA_NOW 1 #define PKA_AUTH_FAILED 2 // // data (forward references) // extern SERIALIZED_LIST GlobalServerInfoList; extern DWORD GlobalServerInfoTimeout; // // forward references // class CFsm_GetConnection; class HTTP_REQUEST_HANDLE_OBJECT; // // classes // // // CServerInfo - we maintain an aged list of CServerInfo's. These are used as a // database of all useful information about a server // class CServerInfo { private: // // m_List - CServerInfo's are kept in a list // LIST_ENTRY m_List; // // m_Expires - system tick count at which this entry will expire // LONG m_Expires; LONG m_Wrap; // // m_ReferenceCount - number of other structures referencing this // LONG m_ReferenceCount; // // m_HostName - primary host name of the server. Stored as LOWER CASE so // that we don't have to perform case insensitive string comparisons each // time // ICSTRING m_HostName; // // m_Hash - hash value of m_HostName. Check this value first // DWORD m_Hash; // // m_ProxyLink - if this is an origin server, we link to the proxy // that may carry // CServerInfo * m_ProxyLink; // // m_dwProxyVersion - a copy of the Global Proxy Version Count, // the proxy link goes bad if version count changes // DWORD m_dwProxyVersion; // // m_HostScheme - Not used for direct connections, // only used for matching proxy info with cached information // INTERNET_SCHEME m_HostScheme; // // m_HostPort - can also be, proxy port for cached proxy server // INTERNET_PORT m_HostPort; private: // // m_Services - bitmap of services supported by the server // union { struct { unsigned HTTP : 1; // HTTP server running at host unsigned FTP : 1; // FTP " " " " unsigned Gopher : 1; // gopher " " " " unsigned GopherPlus : 1; // gopher+ " " " " unsigned NNTP : 1; // news " " " " (future) unsigned SMTP : 1; // mail " " " " (future) unsigned CERN_Proxy : 1; // server is running CERN proxy unsigned FTP_Proxy : 1; // server is running (TIS) FTP proxy/gateway unsigned Socks_Proxy: 1; // server is a Socks Gateway } Bits; unsigned Word; } m_Services; // // m_HttpSupport - bitmap of HTTP properties supported by the server, such // as HTTP version, keep-alive, SSL/PCT, etc. // union { struct { unsigned KeepAlive : 1; // HTTP server supports keep-alive unsigned Http1_0 : 1; // " " IS HTTP/1.0 unsigned Http1_1 : 1; // " " " HTTP/1.1 unsigned SSL : 1; // HTTP server supports SSL unsigned PCT : 1; // HTTP server supports PCT unsigned IIS : 1; // HTTP server is MS IIS (could be running others?) unsigned BadNS : 1; // HTTP server is BAD NS Enterprise server } Bits; unsigned Word; } m_HttpSupport; // // m_Flags - collection of boolean flags // union { struct { unsigned KA_Init : 1; // KeepAliveListInitialized unsigned Unreachable: 1; // host (currently) unreachable unsigned ProxyByPassSet: 1; // has the next bit been set. unsigned ProxyByPassed: 1; // did we bypass the proxy talking to this server. unsigned ProxyScriptCached: 1; // did/are we using this entry to cache the result of JS proxy resolution } Bits; unsigned Word; } m_Flags; // // m_KeepAliveList - if the server supports keep-alive, a list of the // keep-alive connections // SERIALIZED_LIST m_KeepAliveList; // // m_PipelinedList - list of HTTP 1.1 pipelined connections // SERIALIZED_LIST m_PipelinedList; // // m_Waiters - list of sync/async waiters for connections // CPriorityList m_Waiters; // // CConnectionWaiter - private class identifying waiters in m_Waiters list // class CConnectionWaiter : public CPriorityListEntry { private: DWORD m_Sync : 1; DWORD m_KeepAlive : 1; DWORD_PTR m_dwId; HANDLE m_hEvent; public: CConnectionWaiter( IN CPriorityList *pList, IN BOOL bSync, IN BOOL bKeepAlive, IN DWORD_PTR dwId, IN HANDLE hEvent, IN LONG lPriority) : CPriorityListEntry(lPriority) { m_Sync = bSync ? 1 : 0; m_KeepAlive = bKeepAlive ? 1 : 0; m_dwId = dwId; m_hEvent = hEvent; pList->Insert(this); } ~CConnectionWaiter() { } BOOL IsSync(VOID) { return (m_Sync == 1) ? TRUE : FALSE; } BOOL IsKeepAlive(VOID) { return (m_KeepAlive == 1) ? TRUE : FALSE; } DWORD_PTR Id(VOID) { return m_dwId; } VOID Signal(VOID) { SetEvent(m_hEvent); } }; // // m_ConnectionLimit - number of simultaneous connections allowed to this // server // LONG m_ConnectionLimit; // // m_NewLimit - set when we need to change limit // LONG m_NewLimit; // // m_ConnectionsAvailable - number of connections available to this server. // If <= 0 (and not unlimited connections) then we have to wait until a // connection is freed // LONG m_ConnectionsAvailable; // // m_ActiveConnections - number of connections that are active. An active // connection is connected and sending or receiving data. This value can be // greater than m_ConnectionLimit if we are in the situation of being run // out of connections // //LONG m_ActiveConnections; // // m_LastActiveTime - timestamp (tick count) of last operation was made on // any connection to this server // DWORD m_LastActiveTime; // // m_ConnectTime - the average time to connect in milliseconds // DWORD m_ConnectTime; // // m_RTT - average Round Trip Time // DWORD m_RTT; // // m_DownloadRate - the average download rate in bytes per second // DWORD m_DownloadRate; // // m_UploadRate - the average upload rate in bytes per second. Will be // different from m_DownloadRate if using ADSL e.g. // DWORD m_UploadRate; // // m_AddressList - list of resolved addresses for this server // CAddressList m_AddressList; // // m_dwError - error code (mainly for constructor) // DWORD m_dwError; #if INET_DEBUG DWORD m_Signature; #define INIT_SERVER_INFO() m_Signature = SERVER_INFO_SIGNATURE #define CHECK_SERVER_INFO() INET_ASSERT(m_Signature == SERVER_INFO_SIGNATURE) #else #define INIT_SERVER_INFO() /* NOTHING */ #define CHECK_SERVER_INFO() /* NOTHING */ #endif // // private methods // ICSocket * FindKeepAliveConnection( IN DWORD dwSocketFlags, IN INTERNET_PORT nPort, IN LPSTR pszTunnelServer ); BOOL KeepAliveWaiters( VOID ); BOOL RunOutOfConnections( VOID ); VOID UpdateConnectionLimit( VOID ); public: CServerInfo( IN LPSTR lpszHostName, OUT LPDWORD pdwError, IN DWORD dwService = INTERNET_SERVICE_HTTP, IN DWORD dwMaxConnections = GlobalMaxConnectionsPerServer ); ~CServerInfo(); CServerInfo * Next(VOID) { return (CServerInfo *)m_List.Flink; } CServerInfo * Prev(VOID) { return (CServerInfo *)m_List.Blink; } // This code needs to handle system time roll over. // SetExpiryTime is passed the duration, and we calculate the ultimate time // However, this may result in a rollover -- e.g. if the current time is // 0xffffff00, the ultimate time could be 0x000000fd // Expired is passed the current tick count, however, and in the past // would return TRUE immediately. // Thus we set a flag is we need to wait for system time rollover to happen, VOID SetExpiryTime(DWORD dwMilliseconds = GlobalServerInfoTimeout) { DWORD dw = GetTickCountWrap(); m_Expires = dw + dwMilliseconds; m_Wrap = (dw > (DWORD)m_Expires); } VOID ResetExpiryTime(VOID) { m_Expires = 0; m_Wrap = 0; } BOOL Expired(LONG dwTime = (LONG)GetTickCountWrap()) { if (m_Wrap) { m_Wrap = ((LONG)dwTime < 0); } return (!m_Wrap && (dwTime > m_Expires)) ? TRUE : FALSE; } VOID Reference( VOID ); BOOL Dereference( VOID ); LONG ReferenceCount(VOID) const { return m_ReferenceCount; } LPSTR GetHostName(VOID) const { return m_HostName.StringAddress(); } DWORD SetCachedProxyServerInfo( IN CServerInfo * pProxyServer, IN DWORD dwProxyVersion, IN BOOL fUseProxy, IN INTERNET_SCHEME HostScheme, IN INTERNET_PORT HostPort, IN INTERNET_SCHEME ProxyScheme, IN INTERNET_PORT ProxyPort ); CServerInfo * GetCachedProxyServerInfo( IN INTERNET_SCHEME HostScheme, IN INTERNET_PORT HostPort, OUT BOOL *pfCachedEntry ); BOOL CopyCachedProxyInfoToProxyMsg( IN OUT AUTO_PROXY_ASYNC_MSG *pQueryForProxyInfo ); BOOL Match(IN DWORD dwHash, IN LPSTR lpszHostName) { return (dwHash == m_Hash) ? m_HostName.Strcmp(lpszHostName) : FALSE; } VOID SetHTTP(VOID) { m_Services.Bits.HTTP = 1; } BOOL IsHTTP(VOID) { return m_Services.Bits.HTTP ? TRUE : FALSE; } VOID SetFTP(VOID) { m_Services.Bits.FTP = 1; } BOOL IsFTP(VOID) { return m_Services.Bits.FTP ? TRUE : FALSE; } VOID SetGopher(VOID) { m_Services.Bits.Gopher = 1; } BOOL IsGopher(VOID) { return m_Services.Bits.Gopher ? TRUE : FALSE; } VOID SetSocksGateway(VOID) { m_Services.Bits.Socks_Proxy = 1; } BOOL IsSocksGateway(VOID) { return m_Services.Bits.Socks_Proxy ? TRUE : FALSE; } VOID SetCernProxy(VOID) { m_Services.Bits.CERN_Proxy = 1; } VOID SetFTPProxy(VOID) { m_Services.Bits.FTP_Proxy = 1; } BOOL IsCernProxy(VOID) { return m_Services.Bits.CERN_Proxy ? TRUE : FALSE; } VOID SetHttp1_1(VOID) { m_HttpSupport.Bits.Http1_1 = 1; } BOOL IsHttp1_1(VOID) { return m_HttpSupport.Bits.Http1_1 ? TRUE : FALSE; } VOID SetHttp1_0(VOID) { m_HttpSupport.Bits.Http1_0 = 1; } VOID SetBadNSServer(VOID) { m_HttpSupport.Bits.BadNS = 1; } BOOL IsBadNSServer(VOID) { return m_HttpSupport.Bits.BadNS ? TRUE : FALSE; } BOOL IsHttp1_0(VOID) { return m_HttpSupport.Bits.Http1_0 ? TRUE : FALSE; } VOID SetKeepAlive(VOID) { m_HttpSupport.Bits.KeepAlive = 1; } BOOL IsKeepAlive(VOID) { return m_HttpSupport.Bits.KeepAlive ? TRUE : FALSE; } VOID SetProxyScriptCached(BOOL fSetCached) { m_Flags.Bits.ProxyScriptCached = (fSetCached) ? 1 : 0; } BOOL IsProxyScriptCached(VOID) { return m_Flags.Bits.ProxyScriptCached ? TRUE : FALSE; } VOID SetKeepAliveListInitialized(VOID) { m_Flags.Bits.KA_Init = 1; } BOOL IsKeepAliveListInitialized(VOID) { return m_Flags.Bits.KA_Init ? TRUE : FALSE; } VOID SetUnreachable(VOID) { m_Flags.Bits.Unreachable = 1; } VOID SetReachable(VOID) { m_Flags.Bits.Unreachable = 0; } BOOL IsUnreachable(VOID) { return m_Flags.Bits.Unreachable ? TRUE : FALSE; } VOID SetProxyByPassed(BOOL fProxyByPassed) { m_Flags.Bits.ProxyByPassed = fProxyByPassed ? TRUE: FALSE; m_Flags.Bits.ProxyByPassSet = TRUE; } BOOL IsProxyByPassSet(VOID) { return m_Flags.Bits.ProxyByPassSet ? TRUE : FALSE; } BOOL WasProxyByPassed(VOID) { return m_Flags.Bits.ProxyByPassed ? TRUE : FALSE; } LONG ConnectionLimit(VOID) const { return m_ConnectionLimit; } VOID SetConnectionLimit(LONG Limit) { m_ConnectionLimit = Limit; } BOOL UnlimitedConnections(VOID) { return (ConnectionLimit() == UNLIMITED_CONNECTIONS) ? TRUE : FALSE; } LONG KeepAliveConnections(VOID) { return ElementsOnSerializedList(&m_KeepAliveList); } LONG AvailableConnections(VOID) const { return m_ConnectionsAvailable; } LONG TotalAvailableConnections(VOID) { return KeepAliveConnections() + AvailableConnections(); } LONG GetNewLimit(VOID) const { return m_NewLimit; } VOID SetNewLimit(LONG Limit) { m_NewLimit = Limit; } BOOL IsNewLimit(VOID) { return (m_ConnectionLimit != m_NewLimit) ? TRUE : FALSE; } VOID UpdateConnectTime( IN DWORD dwConnectTime ); DWORD GetConnectTime(VOID) const { return (m_ConnectTime == (DWORD)-1) ? 0 : m_ConnectTime; } VOID UpdateRTT( IN DWORD dwTime ); DWORD GetRTT(VOID) const { return m_RTT; } VOID UpdateDownloadRate( IN DWORD dwBytesPerSecond ); DWORD GetDownloadRate(VOID) const { return m_DownloadRate; } VOID UpdateUploadRate( IN DWORD dwBytesPerSecond ); DWORD GetUploadRate(VOID) const { return m_UploadRate; } //DWORD //GetConnection( // IN DWORD dwSocketFlags, // IN INTERNET_PORT nPort, // IN DWORD dwTimeout, // OUT ICSocket * * lplpSocket // ); DWORD GetConnection_Fsm( IN CFsm_GetConnection * Fsm ); DWORD ReleaseConnection( IN ICSocket * lpSocket OPTIONAL, IN BOOL fExemptConnLimit ); VOID RemoveWaiter( IN DWORD_PTR dwId ); VOID PurgeKeepAlives( IN DWORD dwForce = PKA_NO_FORCE ); BOOL GetNextAddress( IN OUT LPDWORD lpdwResolutionId, IN OUT LPDWORD lpdwIndex, IN INTERNET_PORT nPort, OUT LPCSADDR_INFO lpAddress ) { return m_AddressList.GetNextAddress(lpdwResolutionId, lpdwIndex, nPort, lpAddress ); } VOID InvalidateAddress( IN DWORD dwResolutionId, IN DWORD dwAddressIndex ) { m_AddressList.InvalidateAddress(dwResolutionId, dwAddressIndex); } DWORD ResolveHost( IN OUT LPDWORD lpdwResolutionId, IN DWORD dwFlags ) { return m_AddressList.ResolveHost(GetHostName(), lpdwResolutionId, dwFlags ); } DWORD GetError(VOID) const { return m_dwError; } VOID SetError(DWORD dwError = GetLastError()) { m_dwError = dwError; } // // connection activity methods // //VOID AddActiveConnection(VOID) { // LockSerializedList(&m_Waiters); // // INET_ASSERT(m_ActiveConnections >= 0); // // ++m_ActiveConnections; // UnlockSerializedList(&m_Waiters); //} // //VOID RemoveActiveConnection(VOID) { // LockSerializedList(&m_Waiters); // --m_ActiveConnections; // // INET_ASSERT(m_ActiveConnections >= 0); // // UnlockSerializedList(&m_Waiters); //} // //LONG ActiveConnectionCount(VOID) const { // // INET_ASSERT(m_ActiveConnections >= 0); // // return m_ActiveConnections; //} VOID SetLastActiveTime(VOID) { m_LastActiveTime = GetTickCountWrap(); } VOID ResetLastActiveTime(VOID) { m_LastActiveTime = 0; } DWORD GetLastActiveTime(VOID) const { return m_LastActiveTime; } BOOL ConnectionActivity(VOID) { DWORD lastActiveTime = GetLastActiveTime(); return (lastActiveTime != 0) ? (((GetTickCountWrap() - lastActiveTime) <= GlobalConnectionInactiveTimeout) ? TRUE : FALSE) : FALSE; } BOOL AllConnectionsInactive(VOID) { //return ((ActiveConnectionCount() >= ConnectionLimit()) // && !ConnectionActivity()) // ? TRUE // : FALSE; return !ConnectionActivity(); } // // friend functions // friend CServerInfo * ContainingServerInfo( IN LPVOID lpAddress ); }; // // prototypes // DWORD GetServerInfo( IN LPSTR lpszHostName, IN DWORD dwServiceType, IN BOOL bDoResolution, OUT CServerInfo * * lplpServerInfo ); CServerInfo * FindServerInfo( IN LPSTR lpszHostName ); CServerInfo * FindNearestServer( VOID ); VOID ReleaseServerInfo( IN CServerInfo * lpServerInfo ); VOID PurgeServerInfoList( IN BOOL bForce ); VOID PurgeKeepAlives( IN DWORD dwForce = PKA_NO_FORCE ); DWORD PingServerInfoList( OUT LPBOOL lpbUnreachable ); DWORD LoadServerInfoDatabase( VOID ); DWORD SaveServerInfoDatabase( VOID ); CServerInfo * ContainingServerInfo( IN LPVOID lpAddress );