Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2559 lines
66 KiB

  1. /*++
  2. Copyright (c) 1996-1997 Microsoft Corporation
  3. Module Name:
  4. servinfo.cxx
  5. Abstract:
  6. Class implementation for global server info list
  7. Contents:
  8. INTERNET_HANDLE_OBJECT::GetServerInfo
  9. INTERNET_HANDLE_OBJECT::FindServerInfo
  10. ReleaseServerInfo
  11. INTERNET_HANDLE_OBJECT::PurgeServerInfoList
  12. CServerInfo::CServerInfo
  13. CServerInfo::~CServerInfo
  14. CServerInfo::Reference
  15. CServerInfo::Dereference
  16. CServerInfo::UpdateConnectTime
  17. CServerInfo::UpdateRTT
  18. CServerInfo::GetConnection
  19. CFsm_GetConnection::RunSM
  20. CServerInfo::GetConnection_Fsm
  21. CServerInfo::ReleaseConnection
  22. CServerInfo::RemoveWaiter
  23. (CServerInfo::FindKeepAliveConnection)
  24. (CServerInfo::KeepAliveWaiters)
  25. (CServerInfo::RunOutOfConnections)
  26. (CServerInfo::UpdateConnectionLimit)
  27. CServerInfo::PurgeKeepAlives
  28. ContainingServerInfo
  29. Author:
  30. Richard L Firth (rfirth) 07-Oct-1996
  31. Revision History:
  32. 07-Oct-1996 rfirth
  33. Created
  34. --*/
  35. #include <wininetp.h>
  36. #include <perfdiag.hxx>
  37. //
  38. // private macros
  39. //
  40. //#define CHECK_CONNECTION_COUNT() \
  41. // INET_ASSERT(!UnlimitedConnections() \
  42. // ? (TotalAvailableConnections() <= ConnectionLimit()) : TRUE)
  43. #define CHECK_CONNECTION_COUNT() /* NOTHING */
  44. //#define RLF_DEBUG 1
  45. #if INET_DEBUG
  46. #ifdef RLF_DEBUG
  47. #define DPRINTF dprintf
  48. #else
  49. #define DPRINTF (void)
  50. #endif
  51. #else
  52. #define DPRINTF (void)
  53. #endif
  54. //
  55. // functions
  56. //
  57. DWORD
  58. INTERNET_HANDLE_OBJECT::GetServerInfo(
  59. IN LPSTR lpszHostName,
  60. IN DWORD dwServiceType,
  61. IN BOOL bDoResolution,
  62. OUT CServerInfo * * lplpServerInfo
  63. )
  64. /*++
  65. Routine Description:
  66. Finds or creates a CServerInfo entry
  67. Arguments:
  68. lpszHostName - pointer to server name to get info for
  69. dwServiceType - type of service for which CServerInfo requested
  70. bDoResolution - TRUE if we are to resolve host name
  71. lplpServerInfo - pointer to created/found CServerInfo if successful
  72. Return Value:
  73. DWORD
  74. Success - ERROR_SUCCESS
  75. Failure - ERROR_NOT_ENOUGH_MEMORY
  76. Couldn't create the CServerInfo
  77. ERROR_WINHTTP_NAME_NOT_RESOLVED
  78. We were asked to resolve the name, but failed
  79. --*/
  80. {
  81. DEBUG_ENTER((DBG_SESSION,
  82. Dword,
  83. "GetServerInfo",
  84. "%q, %s (%d), %B, %#x",
  85. lpszHostName,
  86. InternetMapService(dwServiceType),
  87. dwServiceType,
  88. bDoResolution,
  89. lplpServerInfo
  90. ));
  91. ICSTRING hostName(lpszHostName);
  92. CServerInfo * lpServerInfo = NULL;
  93. BOOL bCreated = FALSE;
  94. DWORD error = ERROR_SUCCESS;
  95. if (hostName.HaveString()) {
  96. hostName.MakeLowerCase();
  97. LPSTR lpszHostNameLower = hostName.StringAddress();
  98. if (!LockSerializedList(&_ServerInfoList))
  99. {
  100. error = ERROR_NOT_ENOUGH_MEMORY;
  101. goto quit;
  102. }
  103. lpServerInfo = FindServerInfo(lpszHostNameLower);
  104. if (lpServerInfo == NULL)
  105. {
  106. lpServerInfo = New CServerInfo(&_ServerInfoList,
  107. lpszHostNameLower,
  108. &error,
  109. dwServiceType,
  110. GetMaxConnectionsPerServer(WINHTTP_OPTION_MAX_CONNS_PER_SERVER)
  111. );
  112. if (lpServerInfo != NULL)
  113. {
  114. if (error != ERROR_SUCCESS)
  115. {
  116. delete lpServerInfo;
  117. lpServerInfo = NULL;
  118. }
  119. else
  120. {
  121. bCreated = TRUE;
  122. // Reference this to keep it alive beyond the unlock/
  123. lpServerInfo->Reference();
  124. }
  125. }
  126. else
  127. {
  128. error = ERROR_NOT_ENOUGH_MEMORY;
  129. }
  130. }
  131. UnlockSerializedList(&_ServerInfoList);
  132. } else {
  133. //
  134. // failed to create ICSTRING
  135. //
  136. error = GetLastError();
  137. INET_ASSERT(error != ERROR_SUCCESS);
  138. lpServerInfo = NULL;
  139. }
  140. //
  141. // if we created a new CServerInfo and we are instructed to resolve the host
  142. // name then do it now, outside of the global server info list lock. This
  143. // operation may take some time
  144. //
  145. if (bDoResolution && (lpServerInfo != NULL)) {
  146. //error = lpServerInfo->ResolveHostName();
  147. if (error != ERROR_SUCCESS) {
  148. ReleaseServerInfo(lpServerInfo);
  149. lpServerInfo = NULL;
  150. }
  151. }
  152. quit:
  153. *lplpServerInfo = lpServerInfo;
  154. DEBUG_LEAVE(error);
  155. return error;
  156. }
  157. CServerInfo *
  158. INTERNET_HANDLE_OBJECT::FindServerInfo(
  159. IN LPSTR lpszHostName
  160. )
  161. /*++
  162. Routine Description:
  163. Walks the server info list looking for the requested server
  164. Arguments:
  165. lpszHostName - pointer to server name to find (IN LOWER CASE!)
  166. Return Value:
  167. CServerInfo *
  168. Success - pointer to found list entry
  169. Failure - NULL
  170. --*/
  171. {
  172. DEBUG_ENTER((DBG_SESSION,
  173. Pointer,
  174. "FindServerInfo",
  175. "%q",
  176. lpszHostName
  177. ));
  178. DWORD hashHostName = CalculateHashValue(lpszHostName);
  179. CServerInfo * lpServerInfo = NULL;
  180. BOOL found = FALSE;
  181. if (!LockSerializedList(&_ServerInfoList))
  182. {
  183. goto quit;
  184. }
  185. for (lpServerInfo = (CServerInfo *)HeadOfSerializedList(&_ServerInfoList);
  186. lpServerInfo != (CServerInfo *)SlSelf(&_ServerInfoList);
  187. lpServerInfo = lpServerInfo->Next()) {
  188. if (lpServerInfo->Match(hashHostName, lpszHostName)) {
  189. found = TRUE;
  190. break;
  191. }
  192. }
  193. if (!found)
  194. {
  195. lpServerInfo = NULL;
  196. }
  197. // Need to keep this alive beyond the lock.
  198. if (lpServerInfo)
  199. {
  200. lpServerInfo->Reference();
  201. }
  202. UnlockSerializedList(&_ServerInfoList);
  203. quit:
  204. DEBUG_LEAVE(lpServerInfo);
  205. return lpServerInfo;
  206. }
  207. VOID
  208. ReleaseServerInfo(
  209. IN CServerInfo * lpServerInfo
  210. )
  211. /*++
  212. Routine Description:
  213. Release a CServerInfo by dereferencing it. If the reference count goes to
  214. zero, the CServerInfo will be destroyed
  215. Arguments:
  216. lpServerInfo - pointer to CServerInfo to release
  217. Return Value:
  218. None.
  219. --*/
  220. {
  221. DEBUG_ENTER((DBG_SESSION,
  222. None,
  223. "ReleaseServerInfo",
  224. "%#x [%q]",
  225. lpServerInfo,
  226. lpServerInfo->GetHostName()
  227. ));
  228. lpServerInfo->Dereference();
  229. DEBUG_LEAVE(0);
  230. }
  231. VOID
  232. INTERNET_HANDLE_OBJECT::PurgeServerInfoList(
  233. IN BOOL bForce
  234. )
  235. /*++
  236. Routine Description:
  237. Throw out any CServerInfo entries that have expired or any KEEP_ALIVE
  238. entries (for any CServerInfo) that have expired
  239. Arguments:
  240. bForce - TRUE if we forcibly remove entries which have not yet expired but
  241. which have a reference count of 1, else FALSE to remove only
  242. entries that have expired
  243. Return Value:
  244. None.
  245. --*/
  246. {
  247. DEBUG_ENTER((DBG_SESSION,
  248. None,
  249. "PurgeServerInfoList",
  250. "%B",
  251. bForce
  252. ));
  253. if (!LockSerializedList(&_ServerInfoList))
  254. {
  255. // Can't purge list if unable to obtain the lock.
  256. goto quit;
  257. }
  258. PLIST_ENTRY pEntry = HeadOfSerializedList(&_ServerInfoList);
  259. PLIST_ENTRY pPrevious = (PLIST_ENTRY)SlSelf(&_ServerInfoList);
  260. while (TRUE) {
  261. if (pEntry == (PLIST_ENTRY)SlSelf(&_ServerInfoList)) {
  262. break;
  263. }
  264. CServerInfo * pServerInfo;
  265. //pServerInfo = (CServerInfo *)pEntry;
  266. //pServerInfo = CONTAINING_RECORD(pEntry, CONNECTION_LIMIT, m_List);
  267. pServerInfo = ContainingServerInfo(pEntry);
  268. BOOL deleted = FALSE;
  269. if (pServerInfo->ReferenceCount() == 1) {
  270. if (bForce || pServerInfo->Expired()) {
  271. //dprintf("purging server info entry for %q\n", pServerInfo->GetHostName());
  272. deleted = pServerInfo->Dereference();
  273. } else {
  274. pServerInfo->PurgeKeepAlives(PKA_NO_FORCE);
  275. }
  276. }
  277. if (!deleted) {
  278. pPrevious = pEntry;
  279. }
  280. pEntry = pPrevious->Flink;
  281. }
  282. UnlockSerializedList(&_ServerInfoList);
  283. quit:
  284. DEBUG_LEAVE(0);
  285. }
  286. VOID
  287. INTERNET_HANDLE_OBJECT::PurgeKeepAlives(
  288. IN DWORD dwForce
  289. )
  290. /*++
  291. Routine Description:
  292. Throw out any KEEP_ALIVE entries from any CServerInfo that have expired or
  293. which have failed authentication or which are unused, depending on dwForce
  294. Arguments:
  295. dwForce - force to apply when purging. Value can be:
  296. PKA_NO_FORCE - only purge timed-out sockets or sockets in
  297. close-wait state (default)
  298. PKA_NOW - purge all sockets
  299. PKA_AUTH_FAILED - purge sockets that have been marked as failing
  300. authentication
  301. Return Value:
  302. None.
  303. --*/
  304. {
  305. DEBUG_ENTER((DBG_SESSION,
  306. None,
  307. "PurgeKeepAlives",
  308. "%s [%d]",
  309. (dwForce == PKA_NO_FORCE) ? "NO_FORCE"
  310. : (dwForce == PKA_NOW) ? "NOW"
  311. : (dwForce == PKA_AUTH_FAILED) ? "AUTH_FAILED"
  312. : "?",
  313. dwForce
  314. ));
  315. if (!LockSerializedList(&_ServerInfoList))
  316. {
  317. goto quit;
  318. }
  319. PLIST_ENTRY pEntry = HeadOfSerializedList(&_ServerInfoList);
  320. while (pEntry != (PLIST_ENTRY)SlSelf(&_ServerInfoList)) {
  321. CServerInfo * lpServerInfo = ContainingServerInfo(pEntry);
  322. lpServerInfo->PurgeKeepAlives(dwForce);
  323. pEntry = pEntry->Flink;
  324. }
  325. UnlockSerializedList(&_ServerInfoList);
  326. quit:
  327. DEBUG_LEAVE(0);
  328. }
  329. //
  330. // methods
  331. //
  332. CServerInfo::CServerInfo(
  333. IN SERIALIZED_LIST * ServerInfoList,
  334. IN LPSTR lpszHostName,
  335. OUT DWORD* pdwError,
  336. IN DWORD dwService,
  337. IN DWORD dwMaxConnections
  338. )
  339. /*++
  340. Routine Description:
  341. CServerInfo constructor
  342. Arguments:
  343. lpszHostName - server for which to create CServerInfo
  344. dwService - which service to create CServerInfo for
  345. dwMaxConnections - maximum number of simultaneous connections to this
  346. server
  347. Return Value:
  348. None.
  349. --*/
  350. {
  351. DEBUG_ENTER((DBG_OBJECTS,
  352. None,
  353. "CServerInfo::CServerInfo",
  354. "%q, %s (%d), %d",
  355. lpszHostName,
  356. InternetMapService(dwService),
  357. dwService,
  358. dwMaxConnections
  359. ));
  360. INIT_SERVER_INFO();
  361. m_ServerInfoList = ServerInfoList;
  362. *pdwError = ERROR_SUCCESS;
  363. InitializeListHead(&m_List);
  364. m_Expires = 0;
  365. m_Wrap = 0;
  366. m_ReferenceCount = 1;
  367. m_HostName = lpszHostName;
  368. if (!m_HostName.StringAddress())
  369. {
  370. goto error;
  371. }
  372. m_HostName.MakeLowerCase();
  373. m_Hash = CalculateHashValue(m_HostName.StringAddress());
  374. m_Services.Word = 0;
  375. m_HttpSupport.Word = 0;
  376. m_Flags.Word = 0;
  377. m_ProxyLink = NULL;
  378. INET_ASSERT(dwService == INTERNET_SERVICE_HTTP);
  379. SetHTTP();
  380. //
  381. // only initialize the keep-alive and connection limit lists if we are
  382. // creating the server info entry for a HTTP server (or CERN proxy)
  383. //
  384. //
  385. // BUGBUG - we only want to do this on demand
  386. //
  387. //if (IsHTTP()) {
  388. InitializeSerializedList(&m_KeepAliveList);
  389. SetKeepAliveListInitialized();
  390. //
  391. // the maximum number of connections per server is initialized to the
  392. // default (registry) value unless overridden by the caller
  393. //
  394. if (dwMaxConnections == 0)
  395. {
  396. dwMaxConnections = DEFAULT_MAX_CONNECTIONS_PER_SERVER;
  397. }
  398. m_ConnectionLimit = dwMaxConnections;
  399. //} else {
  400. // m_ConnectionLimit = UNLIMITED_CONNECTIONS;
  401. //}
  402. //dprintf("*** %s: limit = %d\n", GetHostName(), m_ConnectionLimit);
  403. //
  404. // BUGBUG - only create event if limiting connections. Need method to manage
  405. // connection limit count/event creation
  406. //
  407. m_NewLimit = m_ConnectionLimit;
  408. m_ConnectionsAvailable = m_ConnectionLimit;
  409. //m_ActiveConnections = 0;
  410. m_LastActiveTime = 0;
  411. m_ConnectTime = (DWORD)-1;
  412. m_RTT = 0;
  413. m_dwError = ERROR_SUCCESS;
  414. //
  415. // add to the global list. We are assuming here that the caller has already
  416. // checked for dupes
  417. //
  418. if (!InsertAtHeadOfSerializedList(m_ServerInfoList, &m_List))
  419. *pdwError = ERROR_NOT_ENOUGH_MEMORY;
  420. quit:
  421. DEBUG_LEAVE(0);
  422. return;
  423. error:
  424. *pdwError = ERROR_NOT_ENOUGH_MEMORY;
  425. goto quit;
  426. }
  427. CServerInfo::~CServerInfo()
  428. /*++
  429. Routine Description:
  430. CServerInfo destructor
  431. Arguments:
  432. None.
  433. Return Value:
  434. None.
  435. --*/
  436. {
  437. DEBUG_ENTER((DBG_OBJECTS,
  438. None,
  439. "CServerInfo::~CServerInfo",
  440. "{%q}",
  441. GetHostName()
  442. ));
  443. CHECK_SERVER_INFO();
  444. //GlobalServerInfoDeAllocCount++;
  445. // unlink if we have a nested obj
  446. if ( m_ProxyLink )
  447. {
  448. CServerInfo *pDerefObj = NULL;
  449. // will leak if unable to dereference
  450. if (LockSerializedList(m_ServerInfoList))
  451. {
  452. pDerefObj = m_ProxyLink;
  453. m_ProxyLink = NULL;
  454. UnlockSerializedList(m_ServerInfoList);
  455. }
  456. if (pDerefObj)
  457. {
  458. pDerefObj->Dereference();
  459. }
  460. }
  461. RemoveFromSerializedList(m_ServerInfoList, &m_List);
  462. INET_ASSERT(m_ReferenceCount == 0);
  463. if (IsKeepAliveListInitialized() && LockSerializedList(&m_KeepAliveList))
  464. {
  465. while (!IsSerializedListEmpty(&m_KeepAliveList))
  466. {
  467. //dprintf("%#x ~S-I killing K-A %#x\n", GetCurrentThreadId(), HeadOfSerializedList(&m_KeepAliveList));
  468. LPVOID pEntry = SlDequeueHead(&m_KeepAliveList);
  469. INET_ASSERT(pEntry != NULL);
  470. if (pEntry != NULL) {
  471. ICSocket * pSocket = ContainingICSocket(pEntry);
  472. //dprintf("~CServerInfo: destroying socket %#x\n", pSocket->GetSocket());
  473. pSocket->Destroy();
  474. }
  475. }
  476. UnlockSerializedList(&m_KeepAliveList);
  477. TerminateSerializedList(&m_KeepAliveList);
  478. }
  479. DEBUG_LEAVE(0);
  480. }
  481. VOID
  482. CServerInfo::Reference(
  483. VOID
  484. )
  485. /*++
  486. Routine Description:
  487. Increments the reference count for the CServerInfo
  488. Arguments:
  489. None.
  490. Return Value:
  491. None.
  492. --*/
  493. {
  494. DEBUG_ENTER((DBG_SESSION,
  495. None,
  496. "CServerInfo::Reference",
  497. "{%q}",
  498. GetHostName()
  499. ));
  500. CHECK_SERVER_INFO();
  501. INET_ASSERT(m_ReferenceCount > 0);
  502. InterlockedIncrement(&m_ReferenceCount);
  503. //dprintf("CServerInfo %s - %d\n", GetHostName(), m_ReferenceCount);
  504. DEBUG_PRINT(SESSION,
  505. INFO,
  506. ("Reference count = %d\n",
  507. ReferenceCount()
  508. ));
  509. DEBUG_LEAVE(0);
  510. }
  511. BOOL
  512. CServerInfo::Dereference(
  513. VOID
  514. )
  515. /*++
  516. Routine Description:
  517. Dereferences the SESSION_INFO. If the reference count goes to zero then this
  518. entry is deleted. If the reference count goes to 1 then the expiry timer is
  519. started
  520. Arguments:
  521. None.
  522. Return Value:
  523. BOOL
  524. TRUE - entry was deleted
  525. FALSE - entry was not deleted
  526. --*/
  527. {
  528. DEBUG_ENTER((DBG_SESSION,
  529. None,
  530. "CServerInfo::Dereference",
  531. "{%q}",
  532. GetHostName()
  533. ));
  534. CHECK_SERVER_INFO();
  535. INET_ASSERT(m_ReferenceCount > 0);
  536. //
  537. // we need to grab the list - we may be removing this entry or updating
  538. // the reference count and expiry fields which must be done atomically
  539. //
  540. SERIALIZED_LIST * ServerInfoList = m_ServerInfoList;
  541. BOOL deleted = FALSE;
  542. if (!LockSerializedList(ServerInfoList))
  543. goto quit;
  544. LONG result = InterlockedDecrement(&m_ReferenceCount);
  545. //dprintf("CServerInfo %s - %d\n", GetHostName(), m_ReferenceCount);
  546. DEBUG_PRINT(SESSION,
  547. INFO,
  548. ("Reference count = %d\n",
  549. ReferenceCount()
  550. ));
  551. if (result == 0) {
  552. delete this;
  553. deleted = TRUE;
  554. } else if (result == 1) {
  555. //
  556. // start expiration proceedings...
  557. //
  558. SetExpiryTime();
  559. }
  560. UnlockSerializedList(ServerInfoList);
  561. quit:
  562. DEBUG_LEAVE(deleted);
  563. return deleted;
  564. }
  565. DWORD
  566. CServerInfo::SetCachedProxyServerInfo(
  567. IN CServerInfo * pProxyServer,
  568. IN DWORD dwProxyVersion,
  569. IN BOOL fUseProxy,
  570. IN INTERNET_SCHEME HostScheme,
  571. IN INTERNET_PORT HostPort,
  572. IN INTERNET_SCHEME ProxyScheme,
  573. IN INTERNET_PORT ProxyPort
  574. )
  575. /*++
  576. Routine Description:
  577. If the Version information match up, copies
  578. the proxy information and links this server object
  579. to the appopriate proxy server object
  580. Assumes that this is called on successful use of the proxy
  581. object.
  582. Arguments:
  583. None.
  584. Return Value:
  585. DWORD
  586. ERROR_SUCCESS
  587. ERROR_NOT_ENOUGH_MEMORY - entry was not deleted because there
  588. wasn't available memory to obtain lock
  589. --*/
  590. {
  591. DWORD error=ERROR_SUCCESS;
  592. if (!LockSerializedList(m_ServerInfoList))
  593. {
  594. error = ERROR_NOT_ENOUGH_MEMORY;
  595. goto quit;
  596. }
  597. if ( dwProxyVersion != GlobalProxyVersionCount )
  598. {
  599. SetProxyScriptCached(FALSE);
  600. goto cleanup; // bail, we don't accept out of date additions to the cache
  601. }
  602. if ( m_ProxyLink )
  603. {
  604. if ( IsProxyScriptCached() &&
  605. HostScheme == m_HostScheme &&
  606. HostPort == m_HostPort &&
  607. fUseProxy )
  608. {
  609. if ( pProxyServer == m_ProxyLink ) {
  610. INET_ASSERT(dwProxyVersion == GlobalProxyVersionCount);
  611. m_dwProxyVersion = dwProxyVersion; // we're now up to date
  612. goto cleanup; // match, no version or host changes
  613. }
  614. INET_ASSERT(pProxyServer != m_ProxyLink );
  615. }
  616. //
  617. // unlink, because we have a new entry to save,
  618. // and the previous entry is bad
  619. //
  620. m_ProxyLink->Dereference();
  621. m_ProxyLink = NULL;
  622. }
  623. //
  624. // Add new cached entry
  625. //
  626. SetProxyScriptCached(TRUE);
  627. m_HostScheme = HostScheme;
  628. m_HostPort = HostPort;
  629. m_dwProxyVersion = dwProxyVersion; // we're now up to date
  630. if ( fUseProxy )
  631. {
  632. INET_ASSERT(this != pProxyServer);
  633. m_ProxyLink = pProxyServer;
  634. m_ProxyLink->Reference();
  635. m_ProxyLink->m_HostScheme = ProxyScheme;
  636. m_ProxyLink->m_HostPort = ProxyPort;
  637. switch (ProxyScheme)
  638. {
  639. case INTERNET_SCHEME_HTTP:
  640. m_ProxyLink->SetCernProxy();
  641. break;
  642. case INTERNET_SCHEME_SOCKS:
  643. m_ProxyLink->SetSocksGateway();
  644. break;
  645. }
  646. }
  647. cleanup:
  648. UnlockSerializedList(m_ServerInfoList);
  649. quit:
  650. return error;
  651. }
  652. CServerInfo *
  653. CServerInfo::GetCachedProxyServerInfo(
  654. IN INTERNET_SCHEME HostScheme,
  655. IN INTERNET_PORT HostPort,
  656. OUT BOOL *pfCachedEntry
  657. )
  658. /*++
  659. Routine Description:
  660. Retrieves a cached server object, that indicates
  661. a probable proxy to use
  662. On Success, the return has an additional increment
  663. on its ref count, assumition that caller derefs
  664. Arguments:
  665. None.
  666. Return Value:
  667. CServerInfo *
  668. NULL on failure
  669. --*/
  670. {
  671. CServerInfo *pProxyServer = NULL;
  672. if (!LockSerializedList(m_ServerInfoList))
  673. return NULL;
  674. *pfCachedEntry = FALSE;
  675. if ( IsProxyScriptCached() )
  676. {
  677. //
  678. // Examine Version Count
  679. //
  680. if ( GlobalProxyVersionCount == m_dwProxyVersion &&
  681. HostScheme == m_HostScheme &&
  682. HostPort == m_HostPort
  683. )
  684. {
  685. *pfCachedEntry = TRUE;
  686. if ( m_ProxyLink ) {
  687. // matched cached entry
  688. m_ProxyLink->Reference();
  689. pProxyServer = m_ProxyLink;
  690. }
  691. }
  692. else
  693. {
  694. // version is expired, remove reference
  695. SetProxyScriptCached(FALSE);
  696. if ( m_ProxyLink ) {
  697. m_ProxyLink->Dereference();
  698. m_ProxyLink = NULL;
  699. }
  700. }
  701. }
  702. UnlockSerializedList(m_ServerInfoList);
  703. return pProxyServer;
  704. }
  705. BOOL
  706. CServerInfo::CopyCachedProxyInfoToProxyMsg(
  707. IN OUT AUTO_PROXY_ASYNC_MSG *pQueryForProxyInfo
  708. )
  709. /*++
  710. Routine Description:
  711. Retrieves Cached Proxy info from object
  712. Arguments:
  713. None.
  714. Return Value:
  715. BOOL
  716. TRUE - sucess
  717. --*/
  718. {
  719. BOOL fSuccess = FALSE;
  720. // really only need to lock to proctect m_HostPort && m_HostScheme
  721. if (!LockSerializedList(m_ServerInfoList))
  722. return FALSE;
  723. pQueryForProxyInfo->SetUseProxy(FALSE);
  724. pQueryForProxyInfo->_lpszProxyHostName =
  725. m_HostName.StringAddress() ?
  726. NewString(m_HostName.StringAddress()) :
  727. NULL;
  728. if ( pQueryForProxyInfo->_lpszProxyHostName != NULL ) {
  729. // copy out cached entry to proxy message structure
  730. pQueryForProxyInfo->_nProxyHostPort = m_HostPort;
  731. pQueryForProxyInfo->_tProxyScheme = m_HostScheme;
  732. pQueryForProxyInfo->_bFreeProxyHostName = TRUE;
  733. pQueryForProxyInfo->_dwProxyHostNameLength =
  734. strlen((pQueryForProxyInfo)->_lpszProxyHostName);
  735. pQueryForProxyInfo->SetUseProxy(TRUE);
  736. fSuccess = TRUE; // success
  737. }
  738. UnlockSerializedList(m_ServerInfoList);
  739. return fSuccess;
  740. }
  741. VOID
  742. CServerInfo::UpdateConnectTime(
  743. IN DWORD dwConnectTime
  744. )
  745. /*++
  746. Routine Description:
  747. Calculates average connect time
  748. Arguments:
  749. dwConnectTime - current connect time
  750. Return Value:
  751. None.
  752. --*/
  753. {
  754. DEBUG_ENTER((DBG_SESSION,
  755. None,
  756. "CServerInfo::UpdateConnectTime",
  757. "{%q} %d",
  758. GetHostName(),
  759. dwConnectTime
  760. ));
  761. DWORD connectTime = m_ConnectTime;
  762. if (connectTime == (DWORD)-1) {
  763. connectTime = dwConnectTime;
  764. } else {
  765. connectTime = (connectTime + dwConnectTime) / 2;
  766. }
  767. //dprintf("%s: connect time = %d, ave = %d\n", GetHostName(), dwConnectTime, connectTime);
  768. DEBUG_PRINT(SESSION,
  769. INFO,
  770. ("average connect time = %d mSec\n",
  771. connectTime
  772. ));
  773. InterlockedExchange((LPLONG)&m_ConnectTime, connectTime);
  774. DEBUG_LEAVE(0);
  775. }
  776. VOID
  777. CServerInfo::UpdateRTT(
  778. IN DWORD dwRTT
  779. )
  780. /*++
  781. Routine Description:
  782. Calculates rolling average round-trip time
  783. Arguments:
  784. dwRTT - current round-trip time
  785. Return Value:
  786. None.
  787. --*/
  788. {
  789. DEBUG_ENTER((DBG_SESSION,
  790. None,
  791. "CServerInfo::UpdateRTT",
  792. "{%q} %d",
  793. GetHostName(),
  794. dwRTT
  795. ));
  796. DWORD RTT = m_RTT;
  797. if (RTT == 0) {
  798. RTT = dwRTT;
  799. } else {
  800. RTT = (RTT + dwRTT) / 2;
  801. }
  802. //dprintf("%s: RTT = %d, ave = %d\n", GetHostName(), dwRTT, RTT);
  803. DEBUG_PRINT(SESSION,
  804. INFO,
  805. ("average round trip time = %d mSec\n",
  806. RTT
  807. ));
  808. InterlockedExchange((LPLONG)&m_RTT, RTT);
  809. DEBUG_LEAVE(0);
  810. }
  811. DWORD
  812. CFsm_GetConnection::RunSM(
  813. IN CFsm * Fsm
  814. )
  815. /*++
  816. Routine Description:
  817. Runs next CFsm_GetConnection state
  818. Arguments:
  819. Fsm - FSM controlling operation
  820. Return Value:
  821. DWORD
  822. Success - ERROR_SUCCESS
  823. Failure -
  824. --*/
  825. {
  826. //dprintf("%#x: %s FSM %#x state %s\n", GetCurrentThreadId(), Fsm->MapType(), Fsm, Fsm->MapState());
  827. DEBUG_ENTER((DBG_SESSION,
  828. Dword,
  829. "CFsm_GetConnection::RunSM",
  830. "%#x",
  831. Fsm
  832. ));
  833. CServerInfo * pServerInfo = (CServerInfo *)Fsm->GetContext();
  834. CFsm_GetConnection * stateMachine = (CFsm_GetConnection *)Fsm;
  835. DWORD error;
  836. switch (Fsm->GetState()) {
  837. case FSM_STATE_INIT:
  838. stateMachine->StartTimer();
  839. //
  840. // fall through
  841. //
  842. case FSM_STATE_CONTINUE:
  843. #ifdef NEW_CONNECTION_SCHEME
  844. case FSM_STATE_ERROR:
  845. #endif
  846. error = pServerInfo->GetConnection_Fsm(stateMachine);
  847. break;
  848. #ifndef NEW_CONNECTION_SCHEME
  849. case FSM_STATE_ERROR:
  850. INET_ASSERT((Fsm->GetError() == ERROR_WINHTTP_TIMEOUT)
  851. || (Fsm->GetError() == ERROR_WINHTTP_OPERATION_CANCELLED));
  852. pServerInfo->RemoveWaiter((DWORD_PTR)Fsm);
  853. error = Fsm->GetError();
  854. Fsm->SetDone();
  855. //dprintf("%#x: FSM_STATE_ERROR - %d\n", GetCurrentThreadId(), error);
  856. break;
  857. #endif
  858. default:
  859. error = ERROR_WINHTTP_INTERNAL_ERROR;
  860. Fsm->SetDone(ERROR_WINHTTP_INTERNAL_ERROR);
  861. INET_ASSERT(FALSE);
  862. break;
  863. }
  864. DEBUG_LEAVE(error);
  865. return error;
  866. }
  867. DWORD
  868. CServerInfo::GetConnection_Fsm(
  869. IN CFsm_GetConnection * Fsm
  870. )
  871. /*++
  872. Routine Description:
  873. Tries to get a connection of requested type for caller. If no connection is
  874. available then one of the following happens:
  875. * If there are available keep-alive connections of a different type then
  876. one is closed and the caller allowed to create a new connection
  877. * If this is an async request, the FSM is blocked and the thread returns
  878. to the pool if a worker, or back to the app if an app thread
  879. * If this is a sync request, we wait on an event for a connection to be
  880. made available, or the connect timeout to elapse
  881. Arguments:
  882. Fsm - get connection FSM
  883. Return Value:
  884. DWORD
  885. Success - ERROR_SUCCESS
  886. Depending on *lplpSocket, we either returned the socket to
  887. use, or its okay to create a new connection
  888. ERROR_IO_PENDING
  889. Request will complete asynchronously
  890. Failure - ERROR_WINHTTP_TIMEOUT
  891. Failed to get connection in time allowed
  892. ERROR_WINHTTP_INTERNAL_ERROR
  893. Something unexpected happened
  894. --*/
  895. {
  896. DEBUG_ENTER((DBG_SESSION,
  897. Dword,
  898. "CServerInfo::GetConnection_Fsm",
  899. "{%q [%d+%d/%d]} %#x(%#x, %d, %d)",
  900. GetHostName(),
  901. m_ConnectionsAvailable,
  902. ElementsOnSerializedList(&m_KeepAliveList),
  903. m_ConnectionLimit,
  904. Fsm,
  905. Fsm->m_dwSocketFlags,
  906. Fsm->m_nPort,
  907. Fsm->m_dwTimeout
  908. ));
  909. PERF_ENTER(GetConnection);
  910. BOOL bFound = FALSE;
  911. DWORD error = ERROR_SUCCESS;
  912. CFsm_GetConnection & fsm = *Fsm;
  913. ICSocket * pSocket = NULL;
  914. LPINTERNET_THREAD_INFO lpThreadInfo = fsm.GetThreadInfo();
  915. HANDLE hEvent = NULL;
  916. BOOL bUnlockList = TRUE;
  917. BOOL bKeepAliveWaiters;
  918. INET_ASSERT(lpThreadInfo != NULL);
  919. INET_ASSERT(lpThreadInfo->hObjectMapped != NULL);
  920. INET_ASSERT(((HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)->
  921. GetHandleType() == TypeHttpRequestHandle);
  922. if ((lpThreadInfo == NULL) || (lpThreadInfo->hObjectMapped == NULL)) {
  923. error = ERROR_WINHTTP_INTERNAL_ERROR;
  924. goto quit;
  925. }
  926. BOOL bAsyncRequest;
  927. bAsyncRequest = lpThreadInfo->IsAsyncWorkerThread
  928. || ((INTERNET_HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)->
  929. IsAsyncHandle();
  930. *fsm.m_lplpSocket = NULL;
  931. try_again:
  932. bUnlockList = TRUE;
  933. //
  934. // use m_Waiters to serialize access. N.B. - we will acquire m_KeepAliveList
  935. // from within m_Waiters
  936. //
  937. if (!m_Waiters.Acquire()) {
  938. error = ERROR_NOT_ENOUGH_MEMORY;
  939. goto quit;
  940. }
  941. if (IsNewLimit()) {
  942. UpdateConnectionLimit();
  943. }
  944. bKeepAliveWaiters = KeepAliveWaiters();
  945. if (fsm.m_dwSocketFlags & SF_KEEP_ALIVE) {
  946. //
  947. // maintain requester order - if there are already waiters then queue
  948. // this request, else try to satisfy the requester. HOWEVER, only check
  949. // for existing requesters the FIRST time through. If we're here with
  950. // FSM_STATE_CONTINUE then we've been unblocked and we can ignore any
  951. // waiters that came after us
  952. //
  953. if ((fsm.GetState() == FSM_STATE_CONTINUE) || !bKeepAliveWaiters) {
  954. DEBUG_PRINT(SESSION,
  955. INFO,
  956. ("no current waiters for K-A connections\n"
  957. ));
  958. while (pSocket = FindKeepAliveConnection(fsm.m_dwSocketFlags,
  959. fsm.m_nPort,
  960. fsm.m_lpszSecureTunnelHost)) {
  961. if (pSocket->IsReset() || pSocket->HasExpired()) {
  962. DPRINTF("%#x: %#x: ********* socket %#x is closed already\n",
  963. GetCurrentThreadId(),
  964. Fsm,
  965. pSocket->GetSocket()
  966. );
  967. DEBUG_PRINT(SESSION,
  968. INFO,
  969. ("K-A connection %#x [%#x/%d] is reset (%B) or expired (%B)\n",
  970. pSocket,
  971. pSocket->GetSocket(),
  972. pSocket->GetSourcePort(),
  973. pSocket->IsReset(),
  974. pSocket->HasExpired()
  975. ));
  976. pSocket->SetLinger(FALSE, 0);
  977. pSocket->Shutdown(2);
  978. //dprintf("GetConnection: destroying reset socket %#x\n", pSocket->GetSocket());
  979. pSocket->Destroy();
  980. pSocket = NULL;
  981. if (!UnlimitedConnections()) {
  982. ++m_ConnectionsAvailable;
  983. }
  984. CHECK_CONNECTION_COUNT();
  985. } else {
  986. DPRINTF("%#x: %#x: *** matched %#x, %#x\n",
  987. GetCurrentThreadId(),
  988. Fsm,
  989. pSocket->GetSocket(),
  990. pSocket->GetFlags()
  991. );
  992. break;
  993. }
  994. }
  995. if (pSocket == NULL) {
  996. DEBUG_PRINT(SESSION,
  997. INFO,
  998. ("no available K-A connections\n"
  999. ));
  1000. /*
  1001. //
  1002. // if all connections are in use as keep-alive connections then
  1003. // since we're here, we want a keep-alive connection that doesn't
  1004. // match the currently available keep-alive connections. Terminate
  1005. // the oldest keep-alive connection (at the head of the queue)
  1006. // and generate a new connection
  1007. //
  1008. LockSerializedList(&m_KeepAliveList);
  1009. if (ElementsOnSerializedList(&m_KeepAliveList) == m_ConnectionLimit) {
  1010. pSocket = ContainingICSocket(SlDequeueHead(&m_KeepAliveList));
  1011. pSocket->SetLinger(FALSE, 0);
  1012. pSocket->Shutdown(2);
  1013. pSocket->Destroy();
  1014. if (!UnlimitedConnections()) {
  1015. ++m_ConnectionsAvailable;
  1016. }
  1017. CHECK_CONNECTION_COUNT();
  1018. }
  1019. UnlockSerializedList(&m_KeepAliveList);
  1020. */
  1021. }
  1022. } else {
  1023. DEBUG_PRINT(SESSION,
  1024. INFO,
  1025. ("%d waiters for K-A connection to %q\n",
  1026. ElementsOnSerializedList(&m_KeepAliveList),
  1027. GetHostName()
  1028. ));
  1029. }
  1030. }
  1031. //
  1032. // if we found a matching keep-alive connection or we are not limiting
  1033. // connections then we're done
  1034. //
  1035. if ((pSocket != NULL) || UnlimitedConnections()) {
  1036. INET_ASSERT(error == ERROR_SUCCESS);
  1037. goto exit;
  1038. }
  1039. //
  1040. // no keep-alive connections matched, or there are already waiters for
  1041. // keep-alive connections
  1042. //
  1043. INET_ASSERT(m_ConnectionsAvailable <= m_ConnectionLimit);
  1044. if (m_ConnectionsAvailable > 0) {
  1045. if (fsm.m_lpszSecureTunnelHost)
  1046. goto exit; // don't create a connection here for SSL tunneling
  1047. //
  1048. // can create a connection
  1049. //
  1050. DEBUG_PRINT(SESSION,
  1051. INFO,
  1052. ("OK to create new connection\n"
  1053. ));
  1054. DPRINTF("%#x: %#x: *** %s OK to create connection %d/%d\n",
  1055. GetCurrentThreadId(),
  1056. Fsm,
  1057. GetHostName(),
  1058. m_ConnectionsAvailable,
  1059. m_ConnectionLimit
  1060. );
  1061. --m_ConnectionsAvailable;
  1062. } else if (fsm.GetElapsedTime() > fsm.m_dwTimeout) {
  1063. error = ERROR_WINHTTP_TIMEOUT;
  1064. } else {
  1065. //
  1066. // if there are keep-alive connections but no keep-alive waiters
  1067. // then either we don't want a keep-alive connection, or the ones
  1068. // available don't match our requirements.
  1069. // If we need a connection of a different type - e.g. SSL when all
  1070. // we have is non-SSL then close a connection & generate a new one.
  1071. // If we need a non-keep-alive connection then its okay to return
  1072. // a current keep-alive connection, the understanding being that the
  1073. // caller will not add Connection: Keep-Alive header (HTTP 1.0) or
  1074. // will add Connection: Close header (HTTP 1.1)
  1075. //
  1076. //
  1077. // BUGBUG - what about waiters for non-keep-alive connections?
  1078. //
  1079. // scenario - limit of 1 connection:
  1080. //
  1081. // A. request for k-a
  1082. // continue & create connection
  1083. // B. request non-k-a
  1084. // none available; wait
  1085. // C. release k-a connection; unblock sync waiter B
  1086. // D. request non-k-a
  1087. // k-a available; return it; caller converts to non-k-a
  1088. // E. unblocked waiter B request non-k-a
  1089. // none available; wait
  1090. //
  1091. // If this situation continues, eventually B will time-out, whereas it
  1092. // could have had the connection taken by D. Request D is younger and
  1093. // therefore can afford to wait while B continues with the connection
  1094. //
  1095. BOOL fHaveConnection = FALSE;
  1096. if (!bKeepAliveWaiters || (fsm.GetState() == FSM_STATE_CONTINUE)) {
  1097. if (!LockSerializedList(&m_KeepAliveList)) {
  1098. error = ERROR_NOT_ENOUGH_MEMORY;
  1099. goto exit;
  1100. }
  1101. if (ElementsOnSerializedList(&m_KeepAliveList) != 0) {
  1102. pSocket = ContainingICSocket(SlDequeueHead(&m_KeepAliveList));
  1103. fHaveConnection = TRUE;
  1104. #define SOCK_FLAGS (SF_ENCRYPT | SF_DECRYPT | SF_SECURE | SF_TUNNEL)
  1105. DWORD dwSocketTypeFlags = pSocket->GetFlags() & SOCK_FLAGS;
  1106. DWORD dwRequestTypeFlags = fsm.m_dwSocketFlags & SOCK_FLAGS;
  1107. if ((dwSocketTypeFlags ^ dwRequestTypeFlags)
  1108. || (fsm.m_nPort != pSocket->GetPort())) {
  1109. DEBUG_PRINT(SESSION,
  1110. INFO,
  1111. ("different socket types (%#x, %#x) or ports (%d, %d) requested\n",
  1112. fsm.m_dwSocketFlags,
  1113. pSocket->GetFlags(),
  1114. fsm.m_nPort,
  1115. pSocket->GetPort()
  1116. ));
  1117. DPRINTF("%#x: %#x: *** closing socket %#x: %#x vs. %#x\n",
  1118. GetCurrentThreadId(),
  1119. Fsm,
  1120. pSocket->GetSocket(),
  1121. pSocket->GetFlags(),
  1122. fsm.m_dwSocketFlags
  1123. );
  1124. pSocket->SetLinger(FALSE, 0);
  1125. pSocket->Shutdown(2);
  1126. //dprintf("GetConnection: destroying different type socket %#x\n", pSocket->GetSocket());
  1127. pSocket->Destroy();
  1128. pSocket = NULL;
  1129. // If we were trying to wait for established SSL tunnel,
  1130. // but one wasn't found, then this connection is open
  1131. // for anyone.
  1132. if (!UnlimitedConnections() && fsm.m_lpszSecureTunnelHost) {
  1133. ++m_ConnectionsAvailable;
  1134. }
  1135. } else {
  1136. DPRINTF("%#x: %#x: *** returning k-a connection %#x as non-k-a\n",
  1137. GetCurrentThreadId(),
  1138. Fsm,
  1139. pSocket->GetSocket()
  1140. );
  1141. }
  1142. CHECK_CONNECTION_COUNT();
  1143. }
  1144. UnlockSerializedList(&m_KeepAliveList);
  1145. if (fHaveConnection) {
  1146. goto exit;
  1147. }
  1148. }
  1149. DPRINTF("%#x: %#x: blocking %s FSM %#x state %s %d/%d\n",
  1150. GetCurrentThreadId(),
  1151. Fsm,
  1152. Fsm->MapType(),
  1153. Fsm,
  1154. Fsm->MapState(),
  1155. m_ConnectionsAvailable,
  1156. m_ConnectionLimit
  1157. );
  1158. //
  1159. // we have to wait for a connection to become available. If we are an
  1160. // async request then we queue this FSM & return the thread to the pool
  1161. // or, if app thread, return pending indication to the app. If this is
  1162. // a sync request (in an app thread) then we block on an event waiting
  1163. // for a connection to become available
  1164. //
  1165. if (!bAsyncRequest) {
  1166. //
  1167. // create unnamed, initially unsignalled, auto-reset event
  1168. //
  1169. hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  1170. if (hEvent == NULL) {
  1171. error = GetLastError();
  1172. goto exit;
  1173. }
  1174. }
  1175. CConnectionWaiter * pWaiter;
  1176. DWORD dwStatus = ERROR_SUCCESS;
  1177. #if INET_DEBUG
  1178. for (pWaiter = (CConnectionWaiter *)m_Waiters.Head();
  1179. pWaiter != (CConnectionWaiter *)m_Waiters.Self();
  1180. pWaiter = (CConnectionWaiter *)pWaiter->Next()) {
  1181. INET_ASSERT(pWaiter->Id() != (DWORD_PTR)(bAsyncRequest ? (DWORD_PTR)Fsm : lpThreadInfo->ThreadId));
  1182. }
  1183. #endif
  1184. pWaiter = New CConnectionWaiter(&m_Waiters,
  1185. !bAsyncRequest,
  1186. (fsm.m_dwSocketFlags & SF_KEEP_ALIVE)
  1187. ? TRUE
  1188. : FALSE,
  1189. bAsyncRequest
  1190. ? (DWORD_PTR)Fsm
  1191. : lpThreadInfo->ThreadId,
  1192. hEvent,
  1193. //
  1194. // priority in request handle object
  1195. // controls relative position in list
  1196. // of waiters
  1197. //
  1198. ((HTTP_REQUEST_HANDLE_OBJECT *)
  1199. lpThreadInfo->hObjectMapped)->
  1200. GetPriority(),
  1201. &dwStatus
  1202. );
  1203. DPRINTF("%#x: %#x: new waiter %#x: as=%B, K-A=%B, id=%#x, hE=%#x, pri=%d, status=%#x, sf=%#x, preq=%#x ssl=%s\n",
  1204. GetCurrentThreadId(),
  1205. Fsm,
  1206. pWaiter,
  1207. bAsyncRequest,
  1208. (fsm.m_dwSocketFlags & SF_KEEP_ALIVE)
  1209. ? TRUE
  1210. : FALSE,
  1211. bAsyncRequest
  1212. ? (DWORD_PTR)Fsm
  1213. : lpThreadInfo->ThreadId,
  1214. hEvent,
  1215. ((HTTP_REQUEST_HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)->
  1216. GetPriority(),
  1217. dwStatus,
  1218. fsm.m_dwSocketFlags,
  1219. ((HTTP_REQUEST_HANDLE_OBJECT *)lpThreadInfo->hObjectMapped),
  1220. fsm.m_lpszSecureTunnelHost ? fsm.m_lpszSecureTunnelHost : ""
  1221. );
  1222. if (pWaiter == NULL) {
  1223. error = ERROR_NOT_ENOUGH_MEMORY;
  1224. goto exit;
  1225. }
  1226. else if (dwStatus != ERROR_SUCCESS) {
  1227. error = dwStatus;
  1228. delete pWaiter; // free since it wasn't inserted
  1229. goto exit;
  1230. }
  1231. if (bAsyncRequest) {
  1232. //
  1233. // ensure that when the FSM is unblocked normally, the new state
  1234. // is STATE_CONTINUE
  1235. //
  1236. Fsm->SetState(FSM_STATE_CONTINUE);
  1237. error = BlockWorkItem(Fsm,
  1238. (DWORD_PTR)pWaiter,
  1239. fsm.m_dwTimeout
  1240. );
  1241. if (error == ERROR_SUCCESS) {
  1242. error = ERROR_IO_PENDING;
  1243. }
  1244. }
  1245. else
  1246. {
  1247. m_Waiters.Release();
  1248. bUnlockList = FALSE;
  1249. DPRINTF("%#x: %#x: %s FSM %#x %s waiting %d msec\n",
  1250. GetCurrentThreadId(),
  1251. Fsm,
  1252. Fsm->MapType(),
  1253. Fsm,
  1254. Fsm->MapState(),
  1255. fsm.m_dwTimeout
  1256. );
  1257. DWORD dwWaitTime = (fsm.m_dwTimeout != INFINITE) ?
  1258. (fsm.m_dwTimeout - fsm.GetElapsedTime()) :
  1259. INFINITE;
  1260. if (((int)dwWaitTime <= 0) && (dwWaitTime != INFINITE)) {
  1261. DEBUG_PRINT(SESSION,
  1262. ERROR,
  1263. ("SYNC wait timed out (%d mSec)\n",
  1264. fsm.m_dwTimeout
  1265. ));
  1266. error = ERROR_WINHTTP_TIMEOUT;
  1267. } else {
  1268. DEBUG_PRINT(SESSION,
  1269. INFO,
  1270. ("waiting %d mSec for SYNC event %#x\n",
  1271. dwWaitTime,
  1272. hEvent
  1273. ));
  1274. //
  1275. // we'd better not be doing a sync wait if we are in the
  1276. // context of an app thread making an async request
  1277. //
  1278. INET_ASSERT(lpThreadInfo->IsAsyncWorkerThread
  1279. || !((INTERNET_HANDLE_OBJECT *)lpThreadInfo->
  1280. hObjectMapped)->IsAsyncHandle());
  1281. //INET_ASSERT(dwWaitTime <= 60000);
  1282. error = WaitForSingleObject(hEvent, dwWaitTime);
  1283. DPRINTF("%#x: %#x: sync waiter unblocked - error = %d\n",
  1284. GetCurrentThreadId(),
  1285. Fsm,
  1286. error
  1287. );
  1288. }
  1289. if (error == STATUS_TIMEOUT) {
  1290. DPRINTF("%#x: %#x: %s: %d+%d/%d: timed out %#x (%s FSM %#x %s)\n",
  1291. GetCurrentThreadId(),
  1292. Fsm,
  1293. GetHostName(),
  1294. m_ConnectionsAvailable,
  1295. ElementsOnSerializedList(&m_KeepAliveList),
  1296. m_ConnectionLimit,
  1297. GetCurrentThreadId(),
  1298. Fsm->MapType(),
  1299. Fsm,
  1300. Fsm->MapState()
  1301. );
  1302. RemoveWaiter(lpThreadInfo->ThreadId);
  1303. error = ERROR_WINHTTP_TIMEOUT;
  1304. }
  1305. BOOL bOk;
  1306. bOk = CloseHandle(hEvent);
  1307. INET_ASSERT(bOk);
  1308. if (error == WAIT_OBJECT_0) {
  1309. DPRINTF("%#x: %#x: sync requester trying again\n",
  1310. GetCurrentThreadId(),
  1311. Fsm
  1312. );
  1313. fsm.SetState(FSM_STATE_CONTINUE);
  1314. goto try_again;
  1315. }
  1316. }
  1317. }
  1318. exit:
  1319. //
  1320. // if we are returning a (keep-alive) socket that has a different blocking
  1321. // mode from that requested, change it
  1322. //
  1323. if (pSocket != NULL) {
  1324. if ((pSocket->GetFlags() & SF_NON_BLOCKING)
  1325. ^ (fsm.m_dwSocketFlags & SF_NON_BLOCKING)) {
  1326. DEBUG_PRINT(SESSION,
  1327. INFO,
  1328. ("different blocking modes requested: %#x, %#x\n",
  1329. fsm.m_dwSocketFlags,
  1330. pSocket->GetFlags()
  1331. ));
  1332. DPRINTF("%#x: %#x: *** changing socket %#x to %sBLOCKING\n",
  1333. GetCurrentThreadId(),
  1334. Fsm,
  1335. pSocket->GetSocket(),
  1336. fsm.m_dwSocketFlags & SF_NON_BLOCKING ? "NON-" : ""
  1337. );
  1338. if (!(GlobalRunningNovellClient32 && !GlobalNonBlockingClient32)) {
  1339. pSocket->SetNonBlockingMode(fsm.m_dwSocketFlags & SF_NON_BLOCKING);
  1340. }
  1341. }
  1342. *fsm.m_lplpSocket = pSocket;
  1343. }
  1344. if (bUnlockList) {
  1345. m_Waiters.Release();
  1346. }
  1347. quit:
  1348. if (error != ERROR_IO_PENDING) {
  1349. fsm.SetDone();
  1350. }
  1351. DPRINTF("%#x: %#x: %s: %d+%d/%d: get: %d, %#x, %d\n",
  1352. GetCurrentThreadId(),
  1353. Fsm,
  1354. GetHostName(),
  1355. m_ConnectionsAvailable,
  1356. ElementsOnSerializedList(&m_KeepAliveList),
  1357. m_ConnectionLimit,
  1358. error,
  1359. pSocket ? pSocket->GetSocket() : 0,
  1360. m_Waiters.Count()
  1361. );
  1362. PERF_LEAVE(GetConnection);
  1363. DEBUG_LEAVE(error);
  1364. return error;
  1365. }
  1366. DWORD
  1367. CServerInfo::ReleaseConnection(
  1368. IN ICSocket * lpSocket OPTIONAL
  1369. )
  1370. /*++
  1371. Routine Description:
  1372. Returns a keep-alive connection to the pool, or allows another requester to
  1373. create a connection
  1374. Arguments:
  1375. lpSocket - pointer to ICSocket if we are returning a keep-alive connection
  1376. Return Value:
  1377. DWORD
  1378. Success - ERROR_SUCCESS
  1379. Failure -
  1380. --*/
  1381. {
  1382. DEBUG_ENTER((DBG_SESSION,
  1383. Dword,
  1384. "CServerInfo::ReleaseConnection",
  1385. "{%q [%d+%d/%d]} %#x [%#x]",
  1386. GetHostName(),
  1387. AvailableConnections(),
  1388. KeepAliveConnections(),
  1389. ConnectionLimit(),
  1390. lpSocket,
  1391. lpSocket ? lpSocket->GetSocket() : 0
  1392. ));
  1393. PERF_ENTER(ReleaseConnection);
  1394. DWORD error = ERROR_SUCCESS;
  1395. BOOL bRelease = FALSE;
  1396. if (!m_Waiters.Acquire()) {
  1397. error = ERROR_NOT_ENOUGH_MEMORY;
  1398. goto quit;
  1399. }
  1400. //
  1401. // quite often (at least with catapult proxy based on IIS) the server may
  1402. // drop the connection even though it indicated it would keep it open. This
  1403. // typically happens on 304 (frequent) and 302 (less so) responses. If we
  1404. // determine the server has dropped the connection then throw it away and
  1405. // allow the app to create a new one
  1406. //
  1407. if (lpSocket != NULL) {
  1408. if (lpSocket->IsClosed() || lpSocket->IsReset()) {
  1409. DEBUG_PRINT(SESSION,
  1410. INFO,
  1411. ("socket %#x already dead - throwing it out\n",
  1412. lpSocket->GetSocket()
  1413. ));
  1414. DPRINTF("%#x: socket %#x: already reset\n",
  1415. GetCurrentThreadId(),
  1416. lpSocket->GetSocket()
  1417. );
  1418. //dprintf("ReleaseConnection: destroying already closed socket %#x\n", lpSocket->GetSocket());
  1419. BOOL bDestroyed = lpSocket->Dereference();
  1420. INET_ASSERT(bDestroyed);
  1421. lpSocket = NULL;
  1422. } else {
  1423. //
  1424. // if we are returning a keep-alive socket, put it in non-blocking
  1425. // mode if not already. Typically, Internet Explorer uses non-blocking
  1426. // sockets. In the infrequent cases where we want a blocking socket
  1427. // - mainly when doing java downloads - we will convert the socket
  1428. // to blocking mode when we get it from the pool
  1429. //
  1430. if (!lpSocket->IsNonBlocking()) {
  1431. DPRINTF("%#x: ***** WARNING: releasing BLOCKING k-a socket %#x\n",
  1432. GetCurrentThreadId(),
  1433. lpSocket->GetSocket()
  1434. );
  1435. if (!(GlobalRunningNovellClient32 && !GlobalNonBlockingClient32)) {
  1436. lpSocket->SetNonBlockingMode(TRUE);
  1437. }
  1438. }
  1439. }
  1440. }
  1441. if (lpSocket != NULL) {
  1442. DPRINTF("%#x: releasing K-A %#x (%d+%d/%d)\n",
  1443. GetCurrentThreadId(),
  1444. lpSocket ? lpSocket->GetSocket() : 0,
  1445. AvailableConnections(),
  1446. KeepAliveConnections(),
  1447. ConnectionLimit()
  1448. );
  1449. INET_ASSERT(lpSocket->IsOpen());
  1450. INET_ASSERT(!lpSocket->IsOnList());
  1451. //INET_ASSERT(!lpSocket->IsReset());
  1452. lpSocket->SetKeepAlive();
  1453. DEBUG_PRINT(SESSION,
  1454. INFO,
  1455. ("releasing keep-alive socket %#x\n",
  1456. lpSocket->GetSocket()
  1457. ));
  1458. lpSocket->SetExpiryTime(GlobalKeepAliveSocketTimeout);
  1459. INET_ASSERT(!IsOnSerializedList(&m_KeepAliveList, lpSocket->List()));
  1460. if (!InsertAtTailOfSerializedList(&m_KeepAliveList, lpSocket->List()))
  1461. {
  1462. DEBUG_PRINT(SESSION,
  1463. INFO,
  1464. ("not enough memory to release %#x to k-a pool\n",
  1465. lpSocket->GetSocket()
  1466. ));
  1467. lpSocket->Dereference();
  1468. if (!UnlimitedConnections())
  1469. {
  1470. ++m_ConnectionsAvailable;
  1471. }
  1472. }
  1473. lpSocket = NULL;
  1474. INET_ASSERT(UnlimitedConnections()
  1475. ? TRUE
  1476. : (KeepAliveConnections() <= ConnectionLimit()));
  1477. bRelease = TRUE;
  1478. } else {
  1479. DPRINTF("%#x: releasing connection (%d+%d/%d)\n",
  1480. GetCurrentThreadId(),
  1481. AvailableConnections(),
  1482. KeepAliveConnections(),
  1483. ConnectionLimit()
  1484. );
  1485. if (!UnlimitedConnections()) {
  1486. ++m_ConnectionsAvailable;
  1487. }
  1488. CHECK_CONNECTION_COUNT();
  1489. bRelease = TRUE;
  1490. }
  1491. if (bRelease && !UnlimitedConnections()) {
  1492. CHECK_CONNECTION_COUNT();
  1493. CConnectionWaiter * pWaiter = (CConnectionWaiter *)m_Waiters.RemoveHead();
  1494. if (pWaiter != NULL) {
  1495. DEBUG_PRINT(SESSION,
  1496. INFO,
  1497. ("unblocking %s waiter %#x, pri=%d\n",
  1498. pWaiter->IsSync() ? "SYNC" : "ASYNC",
  1499. pWaiter->Id(),
  1500. pWaiter->GetPriority()
  1501. ));
  1502. DPRINTF("%#x: Unblocking %s connection waiter %#x, pri=%d\n",
  1503. GetCurrentThreadId(),
  1504. pWaiter->IsSync() ? "Sync" : "Async",
  1505. pWaiter->Id(),
  1506. pWaiter->GetPriority()
  1507. );
  1508. if (pWaiter->IsSync()) {
  1509. pWaiter->Signal();
  1510. } else {
  1511. DWORD n = UnblockWorkItems(1, (DWORD_PTR)pWaiter, ERROR_SUCCESS);
  1512. //INET_ASSERT(n == 1);
  1513. }
  1514. delete pWaiter;
  1515. } else {
  1516. DEBUG_PRINT(SESSION,
  1517. INFO,
  1518. ("no waiters\n"
  1519. ));
  1520. DPRINTF("%#x: !!! NOT unblocking connection waiter\n",
  1521. GetCurrentThreadId()
  1522. );
  1523. }
  1524. } else {
  1525. DPRINTF("%#x: !!! NOT releasing or unlimited?\n",
  1526. GetCurrentThreadId()
  1527. );
  1528. DEBUG_PRINT(SESSION,
  1529. INFO,
  1530. ("bRelease = %B, UnlimitedConnections() = %B\n",
  1531. bRelease,
  1532. UnlimitedConnections()
  1533. ));
  1534. }
  1535. DEBUG_PRINT(SESSION,
  1536. INFO,
  1537. ("avail+k-a/limit = %d+%d/%d\n",
  1538. AvailableConnections(),
  1539. KeepAliveConnections(),
  1540. ConnectionLimit()
  1541. ));
  1542. if (IsNewLimit()) {
  1543. UpdateConnectionLimit();
  1544. }
  1545. m_Waiters.Release();
  1546. quit:
  1547. PERF_LEAVE(ReleaseConnection);
  1548. DEBUG_LEAVE(error);
  1549. DPRINTF("%#x: %s: %d+%d/%d: rls %#x: %d, %d\n",
  1550. GetCurrentThreadId(),
  1551. GetHostName(),
  1552. AvailableConnections(),
  1553. KeepAliveConnections(),
  1554. ConnectionLimit(),
  1555. lpSocket ? lpSocket->GetSocket() : 0,
  1556. error,
  1557. m_Waiters.Count()
  1558. );
  1559. return error;
  1560. }
  1561. VOID
  1562. CServerInfo::RemoveWaiter(
  1563. IN DWORD_PTR dwId
  1564. )
  1565. /*++
  1566. Routine Description:
  1567. Removes a CConnectionWaiter corresponding to the FSM
  1568. Arguments:
  1569. dwId - waiter id to match
  1570. Return Value:
  1571. None.
  1572. --*/
  1573. {
  1574. DEBUG_ENTER((DBG_SESSION,
  1575. None,
  1576. "CServerInfo::RemoveWaiter",
  1577. "%#x",
  1578. dwId
  1579. ));
  1580. if (!m_Waiters.Acquire())
  1581. goto quit;
  1582. CConnectionWaiter * pWaiter;
  1583. BOOL found = FALSE;
  1584. for (pWaiter = (CConnectionWaiter *)m_Waiters.Head();
  1585. pWaiter != (CConnectionWaiter *)m_Waiters.Self();
  1586. pWaiter = (CConnectionWaiter *)pWaiter->Next()) {
  1587. if (pWaiter->Id() == dwId) {
  1588. m_Waiters.Remove((CPriorityListEntry *)pWaiter);
  1589. delete pWaiter;
  1590. found = TRUE;
  1591. break;
  1592. }
  1593. }
  1594. m_Waiters.Release();
  1595. quit:
  1596. //INET_ASSERT(found);
  1597. DEBUG_LEAVE(0);
  1598. }
  1599. //
  1600. // private CServerInfo methods
  1601. //
  1602. ICSocket *
  1603. CServerInfo::FindKeepAliveConnection(
  1604. IN DWORD dwSocketFlags,
  1605. IN INTERNET_PORT nPort,
  1606. IN LPSTR pszTunnelServer
  1607. )
  1608. /*++
  1609. Routine Description:
  1610. Find a keep-alive connection with the requested attributes and port number
  1611. Arguments:
  1612. dwSocketFlags - socket type flags (e.g. SF_SECURE)
  1613. nPort - port to server
  1614. pszTunnelServer - hostname of server through SSL tunnel, or
  1615. NULL if not checked.
  1616. Return Value:
  1617. ICSocket *
  1618. --*/
  1619. {
  1620. DPRINTF("%#x: *** looking for K-A connection\n", GetCurrentThreadId());
  1621. DEBUG_ENTER((DBG_SESSION,
  1622. Pointer,
  1623. "CServerInfo::FindKeepAliveConnection",
  1624. "{%q} %#x, %d",
  1625. GetHostName(),
  1626. dwSocketFlags,
  1627. nPort
  1628. ));
  1629. ICSocket * pSocket = NULL;
  1630. BOOL bFound = FALSE;
  1631. //
  1632. // don't check whether socket is non-blocking - we only really want to match
  1633. // on secure/non-secure. Possible flags to check on are:
  1634. //
  1635. // SF_ENCRYPT - should be subsumed by SF_SECURE
  1636. // SF_DECRYPT - should be subsumed by SF_SECURE
  1637. // SF_NON_BLOCKING - this isn't criterion for match
  1638. // SF_CONNECTIONLESS - not implemented?
  1639. // SF_AUTHORIZED - must be set if authorized & in pool
  1640. // SF_SECURE - opened for SSL/PCT if set
  1641. // SF_KEEP_ALIVE - must be set
  1642. // SF_TUNNEL - must be set if we're looking for a CONNECT tunnel to proxy
  1643. //
  1644. dwSocketFlags &= ~SF_NON_BLOCKING;
  1645. if (!LockSerializedList(&m_KeepAliveList))
  1646. goto quit;
  1647. PLIST_ENTRY pEntry;
  1648. for (pEntry = HeadOfSerializedList(&m_KeepAliveList);
  1649. pEntry != (PLIST_ENTRY)SlSelf(&m_KeepAliveList);
  1650. pEntry = pEntry->Flink) {
  1651. pSocket = ContainingICSocket(pEntry);
  1652. INET_ASSERT(pSocket->IsKeepAlive());
  1653. //
  1654. // We make sure the socket we request is the correct socket,
  1655. // Match() is a bit confusing and needs a bit of explaining,
  1656. // Match IS NOT AN EXACT MATCH, it mearly checks to make sure
  1657. // that the requesting flags (dwSocketFlags) are found in the
  1658. // socket flags. So this can lead to a secure socket being returned
  1659. // on a non-secure open request, now realistically this doesn't happen
  1660. // because of the port number. But in the case of tunnelling this may be
  1661. // an issue, so we add an additional check to make sure that we only
  1662. // get a tunneled socket to a proxy if we specifically request one.
  1663. //
  1664. if (pSocket->Match(dwSocketFlags)
  1665. && (pSocket->GetPort() == nPort)
  1666. && pSocket->MatchTunnelSemantics(dwSocketFlags, pszTunnelServer)
  1667. && RemoveFromSerializedList(&m_KeepAliveList, pSocket->List())) {
  1668. INET_ASSERT(!IsOnSerializedList(&m_KeepAliveList, pSocket->List()));
  1669. bFound = TRUE;
  1670. DEBUG_PRINT(SESSION,
  1671. INFO,
  1672. ("returning keep-alive socket %#x\n",
  1673. pSocket->GetSocket()
  1674. ));
  1675. DPRINTF("%#x: *** %s keep-alive connection %#x (%d/%d), wantf=%#x, gotf=%#x\n",
  1676. GetCurrentThreadId(),
  1677. GetHostName(),
  1678. pSocket->GetSocket(),
  1679. AvailableConnections(),
  1680. ConnectionLimit(),
  1681. dwSocketFlags,
  1682. pSocket->GetFlags()
  1683. );
  1684. break;
  1685. }
  1686. }
  1687. UnlockSerializedList(&m_KeepAliveList);
  1688. if (!bFound) {
  1689. pSocket = NULL;
  1690. }
  1691. quit:
  1692. DEBUG_LEAVE(pSocket);
  1693. return pSocket;
  1694. }
  1695. BOOL
  1696. CServerInfo::KeepAliveWaiters(
  1697. VOID
  1698. )
  1699. /*++
  1700. Routine Description:
  1701. Determine if any of the waiters on the list are for keep-alive connections
  1702. Arguments:
  1703. None.
  1704. Return Value:
  1705. BOOL
  1706. --*/
  1707. {
  1708. DEBUG_ENTER((DBG_SESSION,
  1709. Bool,
  1710. "CServerInfo::KeepAliveWaiters",
  1711. NULL
  1712. ));
  1713. BOOL found = FALSE;
  1714. CConnectionWaiter * pWaiter;
  1715. if (!m_Waiters.Acquire())
  1716. goto quit;
  1717. for (pWaiter = (CConnectionWaiter *)m_Waiters.Head();
  1718. pWaiter != (CConnectionWaiter *)m_Waiters.Self();
  1719. pWaiter = (CConnectionWaiter *)pWaiter->Next()) {
  1720. if (pWaiter->IsKeepAlive()) {
  1721. found = TRUE;
  1722. break;
  1723. }
  1724. }
  1725. m_Waiters.Release();
  1726. quit:
  1727. DEBUG_LEAVE(found);
  1728. return found;
  1729. }
  1730. VOID
  1731. CServerInfo::UpdateConnectionLimit(
  1732. VOID
  1733. )
  1734. /*++
  1735. Routine Description:
  1736. Change connection limit to new limit
  1737. Assumes: 1. Caller has acquired this object before calling this function
  1738. Arguments:
  1739. None.
  1740. Return Value:
  1741. None.
  1742. --*/
  1743. {
  1744. DEBUG_ENTER((DBG_SESSION,
  1745. None,
  1746. "CServerInfo::UpdateConnectionLimit",
  1747. "{%q: %d=>%d (%d+%d)}",
  1748. GetHostName(),
  1749. ConnectionLimit(),
  1750. GetNewLimit(),
  1751. AvailableConnections(),
  1752. KeepAliveConnections()
  1753. ));
  1754. LONG difference = GetNewLimit() - ConnectionLimit();
  1755. //
  1756. // BUGBUG - only handling increases in limit for now
  1757. //
  1758. INET_ASSERT(difference > 0);
  1759. if (difference > 0) {
  1760. m_ConnectionsAvailable += difference;
  1761. }
  1762. m_ConnectionLimit = m_NewLimit;
  1763. DEBUG_PRINT(SESSION,
  1764. INFO,
  1765. ("%q: new: %d+%d/%d\n",
  1766. GetHostName(),
  1767. AvailableConnections(),
  1768. KeepAliveConnections(),
  1769. ConnectionLimit()
  1770. ));
  1771. DEBUG_LEAVE(0);
  1772. }
  1773. VOID
  1774. CServerInfo::PurgeKeepAlives(
  1775. IN DWORD dwForce
  1776. )
  1777. /*++
  1778. Routine Description:
  1779. Purges any timed-out keep-alive connections
  1780. Arguments:
  1781. dwForce - force to apply when purging. Value can be:
  1782. PKA_NO_FORCE - only purge timed-out sockets or sockets in
  1783. close-wait state (default)
  1784. PKA_NOW - purge all sockets
  1785. PKA_AUTH_FAILED - purge sockets that have been marked as failing
  1786. authentication
  1787. Return Value:
  1788. None.
  1789. --*/
  1790. {
  1791. //dprintf("%#x PurgeKeepAlives(%d)\n", GetCurrentThreadId(), dwForce);
  1792. DEBUG_ENTER((DBG_SESSION,
  1793. None,
  1794. "CServerInfo::PurgeKeepAlives",
  1795. "{%q [ref=%d, k-a=%d]} %s [%d]",
  1796. GetHostName(),
  1797. ReferenceCount(),
  1798. KeepAliveConnections(),
  1799. (dwForce == PKA_NO_FORCE) ? "NO_FORCE"
  1800. : (dwForce == PKA_NOW) ? "NOW"
  1801. : (dwForce == PKA_AUTH_FAILED) ? "AUTH_FAILED"
  1802. : "?",
  1803. dwForce
  1804. ));
  1805. if (IsKeepAliveListInitialized()) {
  1806. INET_ASSERT(ReferenceCount() >= 1);
  1807. if (!m_Waiters.Acquire())
  1808. goto quit;
  1809. if (!LockSerializedList(&m_KeepAliveList))
  1810. goto Cleanup;
  1811. PLIST_ENTRY last = (PLIST_ENTRY)SlSelf(&m_KeepAliveList);
  1812. DWORD ticks = GetTickCountWrap();
  1813. for (PLIST_ENTRY pEntry = HeadOfSerializedList(&m_KeepAliveList);
  1814. pEntry != (PLIST_ENTRY)SlSelf(&m_KeepAliveList);
  1815. pEntry = last->Flink) {
  1816. ICSocket * pSocket = ContainingICSocket(pEntry);
  1817. BOOL bDelete;
  1818. if (pSocket->IsReset()) {
  1819. //dprintf("%q: socket %#x/%d CLOSE-WAIT\n", GetHostName(), pSocket->GetSocket(), pSocket->GetSourcePort());
  1820. bDelete = TRUE;
  1821. } else if (dwForce == PKA_NO_FORCE) {
  1822. bDelete = pSocket->HasExpired(ticks);
  1823. } else if (dwForce == PKA_NOW) {
  1824. bDelete = TRUE;
  1825. } else if (dwForce == PKA_AUTH_FAILED) {
  1826. bDelete = pSocket->IsAuthorized();
  1827. } else {
  1828. INET_ASSERT(FALSE); // invalid value for dwForce!
  1829. bDelete = TRUE;
  1830. }
  1831. if (bDelete) {
  1832. //dprintf("%q: socket %#x/%d. Close-Wait=%B, Expired=%B, Now=%B, Auth=%B\n",
  1833. // GetHostName(),
  1834. // pSocket->GetSocket(),
  1835. // pSocket->GetSourcePort(),
  1836. // pSocket->IsReset(),
  1837. // pSocket->HasExpired(ticks),
  1838. // (dwForce == PKA_NOW),
  1839. // (dwForce == PKA_AUTH_FAILED) && pSocket->IsAuthorized()
  1840. // );
  1841. DEBUG_PRINT(SESSION,
  1842. INFO,
  1843. ("purging keep-alive socket %#x/%d: Close-Wait=%B, Expired=%B, Now=%B, Auth=%B\n",
  1844. pSocket->GetSocket(),
  1845. pSocket->GetSourcePort(),
  1846. pSocket->IsReset(),
  1847. pSocket->HasExpired(ticks),
  1848. (dwForce == PKA_NOW),
  1849. (dwForce == PKA_AUTH_FAILED) && pSocket->IsAuthorized()
  1850. ));
  1851. if (RemoveFromSerializedList(&m_KeepAliveList, pEntry))
  1852. {
  1853. BOOL bDestroyed;
  1854. bDestroyed = pSocket->Dereference();
  1855. INET_ASSERT(bDestroyed);
  1856. if (!UnlimitedConnections()) {
  1857. ++m_ConnectionsAvailable;
  1858. INET_ASSERT(m_ConnectionsAvailable <= m_ConnectionLimit);
  1859. }
  1860. }
  1861. else
  1862. {
  1863. DEBUG_PRINT(SESSION,
  1864. INFO,
  1865. ("k-a socket %#x couldn't be removed from the list\n",
  1866. pSocket->GetSocket()
  1867. ));
  1868. }
  1869. } else {
  1870. DEBUG_PRINT(SESSION,
  1871. INFO,
  1872. ("socket %#x/%d expires in %d mSec\n",
  1873. pSocket->GetSocket(),
  1874. pSocket->GetSourcePort(),
  1875. pSocket->GetExpiryTime() - ticks
  1876. ));
  1877. last = pEntry;
  1878. }
  1879. }
  1880. UnlockSerializedList(&m_KeepAliveList);
  1881. Cleanup:
  1882. m_Waiters.Release();
  1883. }
  1884. quit:
  1885. DEBUG_LEAVE(0);
  1886. }
  1887. //
  1888. // friend functions
  1889. //
  1890. CServerInfo *
  1891. ContainingServerInfo(
  1892. IN LPVOID lpAddress
  1893. )
  1894. /*++
  1895. Routine Description:
  1896. Returns address of CServerInfo given address of m_List
  1897. Arguments:
  1898. lpAddress - address of m_List
  1899. Return Value:
  1900. CServerInfo *
  1901. --*/
  1902. {
  1903. return CONTAINING_RECORD(lpAddress, CServerInfo, m_List);
  1904. }