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.

4187 lines
118 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. GetServerInfo
  9. FindServerInfo
  10. FindNearestServer
  11. ReleaseServerInfo
  12. PurgeServerInfoList
  13. PingServerInfoList
  14. LoadServerInfoDatabase
  15. SaveServerInfoDatabase
  16. CServerInfo::CServerInfo
  17. CServerInfo::~CServerInfo
  18. CServerInfo::Reference
  19. CServerInfo::Dereference
  20. CServerInfo::ResolveHostName
  21. CServerInfo::UpdateConnectTime
  22. CServerInfo::UpdateRTT
  23. CServerInfo::UpdateDownloadRate
  24. CServerInfo::UpdateUploadRate
  25. CServerInfo::GetConnection
  26. CFsm_GetConnection::RunSM
  27. CServerInfo::GetConnection_Fsm
  28. CServerInfo::ReleaseConnection
  29. CServerInfo::RemoveWaiter
  30. (CServerInfo::FindKeepAliveConnection)
  31. (CServerInfo::KeepAliveWaiters)
  32. (CServerInfo::RunOutOfConnections)
  33. (CServerInfo::UpdateConnectionLimit)
  34. CServerInfo::PurgeKeepAlives
  35. ContainingServerInfo
  36. Author:
  37. Richard L Firth (rfirth) 07-Oct-1996
  38. Revision History:
  39. 07-Oct-1996 rfirth
  40. Created
  41. --*/
  42. #include <wininetp.h>
  43. #include <perfdiag.hxx>
  44. //
  45. // private macros
  46. //
  47. //#define CHECK_CONNECTION_COUNT() \
  48. // INET_ASSERT(!UnlimitedConnections() \
  49. // ? (TotalAvailableConnections() <= ConnectionLimit()) : TRUE)
  50. #define CHECK_CONNECTION_COUNT() /* NOTHING */
  51. //#define RLF_DEBUG 1
  52. #if INET_DEBUG
  53. #ifdef RLF_DEBUG
  54. #define DPRINTF dprintf
  55. #else
  56. #define DPRINTF (void)
  57. #endif
  58. #else
  59. #define DPRINTF (void)
  60. #endif
  61. //
  62. // functions
  63. //
  64. DWORD
  65. GetServerInfo(
  66. IN LPSTR lpszHostName,
  67. IN DWORD dwServiceType,
  68. IN BOOL bDoResolution,
  69. OUT CServerInfo * * lplpServerInfo
  70. )
  71. /*++
  72. Routine Description:
  73. Finds or creates a CServerInfo entry
  74. Arguments:
  75. lpszHostName - pointer to server name to get info for
  76. dwServiceType - type of service for which CServerInfo requested
  77. bDoResolution - TRUE if we are to resolve host name
  78. lplpServerInfo - pointer to created/found CServerInfo if successful
  79. Return Value:
  80. DWORD
  81. Success - ERROR_SUCCESS
  82. Failure - ERROR_NOT_ENOUGH_MEMORY
  83. Couldn't create the CServerInfo
  84. ERROR_INTERNET_NAME_NOT_RESOLVED
  85. We were asked to resolve the name, but failed
  86. --*/
  87. {
  88. DEBUG_ENTER((DBG_SESSION,
  89. Dword,
  90. "GetServerInfo",
  91. "%q, %s (%d), %B, %#x",
  92. lpszHostName,
  93. InternetMapService(dwServiceType),
  94. dwServiceType,
  95. bDoResolution,
  96. lplpServerInfo
  97. ));
  98. ICSTRING hostName(lpszHostName);
  99. CServerInfo * lpServerInfo;
  100. BOOL bCreated = FALSE;
  101. DWORD error = ERROR_SUCCESS;
  102. if (hostName.HaveString()) {
  103. hostName.MakeLowerCase();
  104. LPSTR lpszHostNameLower = hostName.StringAddress();
  105. LockSerializedList(&GlobalServerInfoList);
  106. lpServerInfo = FindServerInfo(lpszHostNameLower);
  107. if (lpServerInfo == NULL) {
  108. lpServerInfo = new CServerInfo(lpszHostNameLower,
  109. &error,
  110. dwServiceType
  111. );
  112. if (lpServerInfo != NULL) {
  113. if (error != ERROR_SUCCESS) {
  114. delete lpServerInfo;
  115. lpServerInfo = NULL;
  116. }
  117. else {
  118. bCreated = TRUE;
  119. // Reference this to keep it alive beyond the unlock/
  120. lpServerInfo->Reference();
  121. }
  122. } else {
  123. error = ERROR_NOT_ENOUGH_MEMORY;
  124. }
  125. }
  126. UnlockSerializedList(&GlobalServerInfoList);
  127. } else {
  128. //
  129. // failed to create ICSTRING
  130. //
  131. error = GetLastError();
  132. INET_ASSERT(error != ERROR_SUCCESS);
  133. lpServerInfo = NULL;
  134. }
  135. //
  136. // if we created a new CServerInfo and we are instructed to resolve the host
  137. // name then do it now, outside of the global server info list lock. This
  138. // operation may take some time
  139. //
  140. if (bDoResolution && (lpServerInfo != NULL)) {
  141. //error = lpServerInfo->ResolveHostName();
  142. if (error != ERROR_SUCCESS) {
  143. ReleaseServerInfo(lpServerInfo);
  144. lpServerInfo = NULL;
  145. }
  146. }
  147. *lplpServerInfo = lpServerInfo;
  148. DEBUG_LEAVE(error);
  149. return error;
  150. }
  151. CServerInfo *
  152. FindServerInfo(
  153. IN LPSTR lpszHostName
  154. )
  155. /*++
  156. Routine Description:
  157. Walks the server info list looking for the requested server
  158. Arguments:
  159. lpszHostName - pointer to server name to find (IN LOWER CASE!)
  160. Return Value:
  161. CServerInfo *
  162. Success - pointer to found list entry
  163. Failure - NULL
  164. --*/
  165. {
  166. DEBUG_ENTER((DBG_SESSION,
  167. Pointer,
  168. "FindServerInfo",
  169. "%q",
  170. lpszHostName
  171. ));
  172. DWORD hashHostName = CalculateHashValue(lpszHostName);
  173. CServerInfo * lpServerInfo;
  174. BOOL found = FALSE;
  175. LockSerializedList(&GlobalServerInfoList);
  176. for (lpServerInfo = (CServerInfo *)HeadOfSerializedList(&GlobalServerInfoList);
  177. lpServerInfo != (CServerInfo *)SlSelf(&GlobalServerInfoList);
  178. lpServerInfo = lpServerInfo->Next()) {
  179. if (lpServerInfo->Match(hashHostName, lpszHostName)) {
  180. found = TRUE;
  181. break;
  182. }
  183. }
  184. if (!found)
  185. {
  186. lpServerInfo = NULL;
  187. }
  188. // Need to keep this alive beyond the lock.
  189. if (lpServerInfo)
  190. {
  191. lpServerInfo->Reference();
  192. }
  193. UnlockSerializedList(&GlobalServerInfoList);
  194. DEBUG_LEAVE(lpServerInfo);
  195. return lpServerInfo;
  196. }
  197. CServerInfo *
  198. FindNearestServer(
  199. VOID
  200. )
  201. /*++
  202. Routine Description:
  203. Returns pointer to the CServerInfo which has the shortest connect time.
  204. Returned info is referenced, so caller must call ReleaseServerInfo() when
  205. done
  206. Arguments:
  207. None.
  208. Return Value:
  209. CServerInfo *
  210. Success - valid pointer
  211. Failure - NULL
  212. --*/
  213. {
  214. DEBUG_ENTER((DBG_SESSION,
  215. Pointer,
  216. "FindNearestServer",
  217. NULL
  218. ));
  219. CServerInfo * lpServerInfo;
  220. CServerInfo * lpsiResult = NULL;
  221. DWORD connectTime = (DWORD)-1;
  222. LockSerializedList(&GlobalServerInfoList);
  223. for (lpServerInfo = (CServerInfo *)HeadOfSerializedList(&GlobalServerInfoList);
  224. lpServerInfo != (CServerInfo *)SlSelf(&GlobalServerInfoList);
  225. lpServerInfo = lpServerInfo->Next()) {
  226. if (lpServerInfo->GetConnectTime() < connectTime) {
  227. lpsiResult = lpServerInfo;
  228. connectTime = lpServerInfo->GetConnectTime();
  229. }
  230. }
  231. // We used to reference this in the caller, which is a bad idea, since it
  232. // can get deleted between unlocking the serialized list and getting to the addref.
  233. if(lpsiResult)
  234. {
  235. lpsiResult->Reference();
  236. }
  237. UnlockSerializedList(&GlobalServerInfoList);
  238. DEBUG_LEAVE(lpsiResult);
  239. return lpsiResult;
  240. }
  241. VOID
  242. ReleaseServerInfo(
  243. IN CServerInfo * lpServerInfo
  244. )
  245. /*++
  246. Routine Description:
  247. Release a CServerInfo by dereferencing it. If the reference count goes to
  248. zero, the CServerInfo will be destroyed
  249. Arguments:
  250. lpServerInfo - pointer to CServerInfo to release
  251. Return Value:
  252. None.
  253. --*/
  254. {
  255. DEBUG_ENTER((DBG_SESSION,
  256. None,
  257. "ReleaseServerInfo",
  258. "%#x [%q]",
  259. lpServerInfo,
  260. lpServerInfo->GetHostName()
  261. ));
  262. lpServerInfo->Dereference();
  263. DEBUG_LEAVE(0);
  264. }
  265. VOID
  266. PurgeServerInfoList(
  267. IN BOOL bForce
  268. )
  269. /*++
  270. Routine Description:
  271. Throw out any CServerInfo entries that have expired or any KEEP_ALIVE
  272. entries (for any CServerInfo) that have expired
  273. Arguments:
  274. bForce - TRUE if we forcibly remove entries which have not yet expired but
  275. which have a reference count of 1, else FALSE to remove only
  276. entries that have expired
  277. Return Value:
  278. None.
  279. --*/
  280. {
  281. DEBUG_ENTER((DBG_SESSION,
  282. None,
  283. "PurgeServerInfoList",
  284. "%B",
  285. bForce
  286. ));
  287. BOOL bActivate = FALSE;
  288. LockSerializedList(&GlobalServerInfoList);
  289. PLIST_ENTRY pEntry = HeadOfSerializedList(&GlobalServerInfoList);
  290. PLIST_ENTRY pPrevious = (PLIST_ENTRY)SlSelf(&GlobalServerInfoList);
  291. while (TRUE) {
  292. if (pEntry == (PLIST_ENTRY)SlSelf(&GlobalServerInfoList)) {
  293. break;
  294. }
  295. CServerInfo * pServerInfo;
  296. //pServerInfo = (CServerInfo *)pEntry;
  297. //pServerInfo = CONTAINING_RECORD(pEntry, CONNECTION_LIMIT, m_List);
  298. pServerInfo = ContainingServerInfo(pEntry);
  299. BOOL deleted = FALSE;
  300. if (pServerInfo->ReferenceCount() == 1) {
  301. if (bForce || pServerInfo->Expired()) {
  302. //dprintf("purging server info entry for %q\n", pServerInfo->GetHostName());
  303. deleted = pServerInfo->Dereference();
  304. } else {
  305. pServerInfo->PurgeKeepAlives(PKA_NO_FORCE);
  306. bActivate = TRUE;
  307. }
  308. }
  309. if (!deleted) {
  310. pPrevious = pEntry;
  311. }
  312. pEntry = pPrevious->Flink;
  313. }
  314. UnlockSerializedList(&GlobalServerInfoList);
  315. if (bActivate && g_bHibernating)
  316. {
  317. InterruptSelect();
  318. }
  319. DEBUG_LEAVE(0);
  320. }
  321. VOID
  322. PurgeKeepAlives(
  323. IN DWORD dwForce
  324. )
  325. /*++
  326. Routine Description:
  327. Throw out any KEEP_ALIVE entries from any CServerInfo that have expired or
  328. which have failed authentication or which are unused, depending on dwForce
  329. Arguments:
  330. dwForce - force to apply when purging. Value can be:
  331. PKA_NO_FORCE - only purge timed-out sockets or sockets in
  332. close-wait state (default)
  333. PKA_NOW - purge all sockets
  334. PKA_AUTH_FAILED - purge sockets that have been marked as failing
  335. authentication
  336. Return Value:
  337. None.
  338. --*/
  339. {
  340. DEBUG_ENTER((DBG_SESSION,
  341. None,
  342. "PurgeKeepAlives",
  343. "%s [%d]",
  344. (dwForce == PKA_NO_FORCE) ? "NO_FORCE"
  345. : (dwForce == PKA_NOW) ? "NOW"
  346. : (dwForce == PKA_AUTH_FAILED) ? "AUTH_FAILED"
  347. : "?",
  348. dwForce
  349. ));
  350. LockSerializedList(&GlobalServerInfoList);
  351. PLIST_ENTRY pEntry = HeadOfSerializedList(&GlobalServerInfoList);
  352. while (pEntry != (PLIST_ENTRY)SlSelf(&GlobalServerInfoList)) {
  353. CServerInfo * lpServerInfo = ContainingServerInfo(pEntry);
  354. lpServerInfo->PurgeKeepAlives(dwForce);
  355. pEntry = pEntry->Flink;
  356. }
  357. UnlockSerializedList(&GlobalServerInfoList);
  358. DEBUG_LEAVE(0);
  359. }
  360. //
  361. //DWORD
  362. //PingServerInfoList(
  363. // OUT LPBOOL lpbUnreachable
  364. // )
  365. //
  366. ///*++
  367. //
  368. //Routine Description:
  369. //
  370. // Determines online/offline state by attempting to ping a known server address.
  371. // If any ping succeeds, this function succeeds. If all pings fail, then this
  372. // function fails
  373. //
  374. // Assumes: 1. global ping object has been instantiated
  375. //
  376. //Arguments:
  377. //
  378. // lpbUnreachable - TRUE if one or more servers were unreachable (the network
  379. // seems to be alive, just that we couldn't reach one or more
  380. // servers. Useful to indicate that an address we are in the
  381. // process of resolving is bad
  382. //
  383. //Return Value:
  384. //
  385. // DWORD
  386. // Success - ERROR_SUCCESS
  387. // at least one address pinged
  388. //
  389. // Failure - ERROR_INTERNET_NO_KNOWN_SERVERS (internal)
  390. // There are no known server addresses (there may be items in
  391. // the list, but the addresses are not yet resolved)
  392. //
  393. // ERROR_INTERNET_PING_FAILED (internal)
  394. // We have (resolved) addresses, but couldn't successfully ping
  395. // any. We believe we have connectivity
  396. //
  397. // ERROR_INTERNET_NO_PING_SUPPORT (internal)
  398. // We couldn't ping any addresses because ping support is not
  399. // loaded, or globally disabled
  400. //
  401. //--*/
  402. //
  403. //{
  404. // DEBUG_ENTER((DBG_SOCKETS,
  405. // Dword,
  406. // "PingServerInfoList",
  407. // "%#x",
  408. // lpbUnreachable
  409. // ));
  410. //
  411. // DWORD error;
  412. //
  413. // *lpbUnreachable = FALSE;
  414. //
  415. // CServerInfo * lpServerInfo;
  416. //
  417. // error = ERROR_INTERNET_NO_KNOWN_SERVERS;
  418. //
  419. // LockSerializedList(&GlobalServerInfoList);
  420. //
  421. // for (lpServerInfo = (CServerInfo *)HeadOfSerializedList(&GlobalServerInfoList);
  422. // lpServerInfo != (CServerInfo *)SlSelf(&GlobalServerInfoList);
  423. // lpServerInfo = lpServerInfo->Next()) {
  424. //
  425. // DWORD err;
  426. // DWORD dwIpAddress;
  427. // DWORD addressLength = sizeof(dwIpAddress);
  428. //
  429. // //
  430. // // just use first address from each address list
  431. // //
  432. //
  433. // err = DestinationAddressFromAddressList(lpServerInfo->GetAddressList(),
  434. // 0,
  435. // (LPBYTE)&dwIpAddress,
  436. // &addressLength
  437. // );
  438. //
  439. // INET_ASSERT(IS_VALID_NON_LOOPBACK_IP_ADDRESS(dwIpAddress));
  440. //
  441. // if ((err == ERROR_SUCCESS)
  442. // && IS_VALID_NON_LOOPBACK_IP_ADDRESS(dwIpAddress)) {
  443. // error = Ping(dwIpAddress);
  444. // if (error == ERROR_SUCCESS) {
  445. //
  446. // //
  447. // // ping succeeded, net is alive. All we need to know for now
  448. // //
  449. //
  450. // lpServerInfo->SetReachable();
  451. // break;
  452. // } else if (error == ERROR_INTERNET_SERVER_UNREACHABLE) {
  453. //
  454. // //
  455. // // our equivalent of net unreachable
  456. // //
  457. //
  458. // lpServerInfo->SetUnreachable();
  459. // *lpbUnreachable = TRUE;
  460. //
  461. // //
  462. // // although the server is unreachable, we still have
  463. // // connectivity (the ping would have failed completely
  464. // // otherwise)
  465. // //
  466. //
  467. // error = ERROR_SUCCESS;
  468. // } else if (error == ERROR_INTERNET_NO_PING_SUPPORT) {
  469. //
  470. // //
  471. // // can't ping - no ping support. quit
  472. // //
  473. //
  474. // break;
  475. // }
  476. // }
  477. // }
  478. //
  479. // UnlockSerializedList(&GlobalServerInfoList);
  480. //
  481. // DEBUG_LEAVE(error);
  482. //
  483. // return error;
  484. //}
  485. //
  486. //
  487. //DWORD
  488. //LoadServerInfoDatabase(
  489. // VOID
  490. // )
  491. //
  492. ///*++
  493. //
  494. //Routine Description:
  495. //
  496. // Populates the server info database from the registry. This allows us to
  497. // avoid server capability negotiation each time we start IE/Wininet
  498. //
  499. //Arguments:
  500. //
  501. // None.
  502. //
  503. //Return Value:
  504. //
  505. // DWORD
  506. // Success - ERROR_SUCCESS
  507. //
  508. // Failure -
  509. //
  510. //--*/
  511. //
  512. //{
  513. // return ERROR_SUCCESS;
  514. //}
  515. //
  516. //
  517. //DWORD
  518. //SaveServerInfoDatabase(
  519. // VOID
  520. // )
  521. //
  522. ///*++
  523. //
  524. //Routine Description:
  525. //
  526. // Copies the contents of the current server info database to the registry.
  527. // This information is read the next time we start IE/Wininet
  528. //
  529. //Arguments:
  530. //
  531. // None.
  532. //
  533. //Return Value:
  534. //
  535. // DWORD
  536. // Success - ERROR_SUCCESS
  537. //
  538. // Failure -
  539. //
  540. //--*/
  541. //
  542. //{
  543. // return ERROR_SUCCESS;
  544. //}
  545. //
  546. // methods
  547. //
  548. CServerInfo::CServerInfo(
  549. IN LPSTR lpszHostName,
  550. OUT LPDWORD pdwError,
  551. IN DWORD dwService,
  552. IN DWORD dwMaxConnections
  553. )
  554. /*++
  555. Routine Description:
  556. CServerInfo constructor
  557. Arguments:
  558. lpszHostName - server for which to create CServerInfo
  559. pdwError - return status in case a failure occurs during init
  560. dwService - which service to create CServerInfo for
  561. dwMaxConnections - maximum number of simultaneous connections to this
  562. server
  563. Return Value:
  564. None.
  565. --*/
  566. {
  567. DEBUG_ENTER((DBG_OBJECTS,
  568. None,
  569. "CServerInfo::CServerInfo",
  570. "%q, %s (%d), %d",
  571. lpszHostName,
  572. InternetMapService(dwService),
  573. dwService,
  574. dwMaxConnections
  575. ));
  576. INIT_SERVER_INFO();
  577. //GlobalServerInfoAllocCount++;
  578. *pdwError = ERROR_SUCCESS;
  579. InitializeListHead(&m_List);
  580. m_Expires = 0;
  581. m_Wrap = 0;
  582. m_ReferenceCount = 1;
  583. m_HostName = lpszHostName;
  584. if (!m_HostName.StringAddress())
  585. {
  586. *pdwError = ERROR_NOT_ENOUGH_MEMORY;
  587. goto quit;
  588. }
  589. m_HostName.MakeLowerCase();
  590. m_Hash = CalculateHashValue(m_HostName.StringAddress());
  591. m_Services.Word = 0;
  592. m_HttpSupport.Word = 0;
  593. m_Flags.Word = 0;
  594. m_ProxyLink = NULL;
  595. switch (dwService) {
  596. case INTERNET_SERVICE_HTTP:
  597. SetHTTP();
  598. break;
  599. case INTERNET_SERVICE_FTP:
  600. SetFTP();
  601. break;
  602. case INTERNET_SERVICE_GOPHER:
  603. SetGopher();
  604. break;
  605. default:
  606. INET_ASSERT(FALSE);
  607. }
  608. //
  609. // only initialize the keep-alive and connection limit lists if we are
  610. // creating the server info entry for a HTTP server (or CERN proxy)
  611. //
  612. //
  613. // BUGBUG - we only want to do this on demand
  614. //
  615. //if (IsHTTP()) {
  616. InitializeSerializedList(&m_KeepAliveList);
  617. InitializeSerializedList(&m_PipelinedList);
  618. //InitializeSerializedList(&m_Waiters);
  619. SetKeepAliveListInitialized();
  620. //
  621. // the maximum number of connections per server is initialized to the
  622. // default (registry) value unless overridden by the caller
  623. //
  624. if (dwMaxConnections == 0) {
  625. dwMaxConnections = DEFAULT_MAX_CONNECTIONS_PER_SERVER;
  626. }
  627. m_ConnectionLimit = dwMaxConnections;
  628. //} else {
  629. // m_ConnectionLimit = UNLIMITED_CONNECTIONS;
  630. //}
  631. //dprintf("*** %s: limit = %d\n", GetHostName(), m_ConnectionLimit);
  632. //
  633. // BUGBUG - only create event if limiting connections. Need method to manage
  634. // connection limit count/event creation
  635. //
  636. m_NewLimit = m_ConnectionLimit;
  637. m_ConnectionsAvailable = m_ConnectionLimit;
  638. //m_ActiveConnections = 0;
  639. m_LastActiveTime = 0;
  640. m_ConnectTime = (DWORD)-1;
  641. m_RTT = 0;
  642. m_DownloadRate = 0;
  643. m_UploadRate = 0;
  644. m_dwError = ERROR_SUCCESS;
  645. //
  646. // add to the global list. We are assuming here that the caller has already
  647. // checked for dupes
  648. //
  649. InsertAtHeadOfSerializedList(&GlobalServerInfoList, &m_List);
  650. quit:
  651. DEBUG_LEAVE(0);
  652. }
  653. CServerInfo::~CServerInfo()
  654. /*++
  655. Routine Description:
  656. CServerInfo destructor
  657. Arguments:
  658. None.
  659. Return Value:
  660. None.
  661. --*/
  662. {
  663. DEBUG_ENTER((DBG_OBJECTS,
  664. None,
  665. "CServerInfo::~CServerInfo",
  666. "{%q}",
  667. GetHostName()
  668. ));
  669. CHECK_SERVER_INFO();
  670. //GlobalServerInfoDeAllocCount++;
  671. // unlink if we have a nested obj
  672. if ( m_ProxyLink ) {
  673. CServerInfo *pDerefObj;
  674. LockSerializedList(&GlobalServerInfoList);
  675. pDerefObj = m_ProxyLink;
  676. m_ProxyLink = NULL;
  677. UnlockSerializedList(&GlobalServerInfoList);
  678. if (pDerefObj) {
  679. pDerefObj->Dereference();
  680. }
  681. }
  682. RemoveFromSerializedList(&GlobalServerInfoList, &m_List);
  683. INET_ASSERT(m_ReferenceCount == 0);
  684. if (IsKeepAliveListInitialized()) {
  685. LockSerializedList(&m_KeepAliveList);
  686. while (!IsSerializedListEmpty(&m_KeepAliveList)) {
  687. //dprintf("%#x ~S-I killing K-A %#x\n", GetCurrentThreadId(), HeadOfSerializedList(&m_KeepAliveList));
  688. LPVOID pEntry = SlDequeueHead(&m_KeepAliveList);
  689. INET_ASSERT(pEntry != NULL);
  690. if (pEntry != NULL) {
  691. ICSocket * pSocket = ContainingICSocket(pEntry);
  692. //dprintf("~CServerInfo: destroying socket %#x\n", pSocket->GetSocket());
  693. pSocket->Destroy();
  694. }
  695. }
  696. UnlockSerializedList(&m_KeepAliveList);
  697. TerminateSerializedList(&m_KeepAliveList);
  698. TerminateSerializedList(&m_PipelinedList);
  699. //TerminateSerializedList(&m_Waiters);
  700. }
  701. DEBUG_LEAVE(0);
  702. }
  703. VOID
  704. CServerInfo::Reference(
  705. VOID
  706. )
  707. /*++
  708. Routine Description:
  709. Increments the reference count for the CServerInfo
  710. Arguments:
  711. None.
  712. Return Value:
  713. None.
  714. --*/
  715. {
  716. DEBUG_ENTER((DBG_SESSION,
  717. None,
  718. "CServerInfo::Reference",
  719. "{%q}",
  720. GetHostName()
  721. ));
  722. CHECK_SERVER_INFO();
  723. INET_ASSERT(m_ReferenceCount > 0);
  724. InterlockedIncrement(&m_ReferenceCount);
  725. //dprintf("CServerInfo %s - %d\n", GetHostName(), m_ReferenceCount);
  726. DEBUG_PRINT(SESSION,
  727. INFO,
  728. ("Reference count = %d\n",
  729. ReferenceCount()
  730. ));
  731. DEBUG_LEAVE(0);
  732. }
  733. BOOL
  734. CServerInfo::Dereference(
  735. VOID
  736. )
  737. /*++
  738. Routine Description:
  739. Dereferences the SESSION_INFO. If the reference count goes to zero then this
  740. entry is deleted. If the reference count goes to 1 then the expiry timer is
  741. started
  742. Arguments:
  743. None.
  744. Return Value:
  745. BOOL
  746. TRUE - entry was deleted
  747. FALSE - entry was not deleted
  748. --*/
  749. {
  750. DEBUG_ENTER((DBG_SESSION,
  751. None,
  752. "CServerInfo::Dereference",
  753. "{%q}",
  754. GetHostName()
  755. ));
  756. CHECK_SERVER_INFO();
  757. INET_ASSERT(m_ReferenceCount > 0);
  758. //
  759. // we need to grab the list - we may be removing this entry or updating
  760. // the reference count and expiry fields which must be done atomically
  761. //
  762. LockSerializedList(&GlobalServerInfoList);
  763. LONG result = InterlockedDecrement(&m_ReferenceCount);
  764. //dprintf("CServerInfo %s - %d\n", GetHostName(), m_ReferenceCount);
  765. DEBUG_PRINT(SESSION,
  766. INFO,
  767. ("Reference count = %d\n",
  768. ReferenceCount()
  769. ));
  770. BOOL deleted = FALSE;
  771. if (result == 0) {
  772. delete this;
  773. deleted = TRUE;
  774. } else if (result == 1) {
  775. //
  776. // start expiration proceedings...
  777. //
  778. SetExpiryTime();
  779. }
  780. UnlockSerializedList(&GlobalServerInfoList);
  781. DEBUG_LEAVE(deleted);
  782. return deleted;
  783. }
  784. DWORD
  785. CServerInfo::SetCachedProxyServerInfo(
  786. IN CServerInfo * pProxyServer,
  787. IN DWORD dwProxyVersion,
  788. IN BOOL fUseProxy,
  789. IN INTERNET_SCHEME HostScheme,
  790. IN INTERNET_PORT HostPort,
  791. IN INTERNET_SCHEME ProxyScheme,
  792. IN INTERNET_PORT ProxyPort
  793. )
  794. /*++
  795. Routine Description:
  796. If the Version information match up, copies
  797. the proxy information and links this server object
  798. to the appopriate proxy server object
  799. Assumes that this is called on successful use of the proxy
  800. object.
  801. Arguments:
  802. None.
  803. Return Value:
  804. DWORD
  805. ERROR_SUCCESS
  806. FALSE - entry was not deleted
  807. --*/
  808. {
  809. DWORD error=ERROR_SUCCESS;
  810. LockSerializedList(&GlobalServerInfoList);
  811. if ( dwProxyVersion != GlobalProxyVersionCount )
  812. {
  813. SetProxyScriptCached(FALSE);
  814. goto quit; // bail, we don't accept out of date additions to the cache
  815. }
  816. if ( m_ProxyLink )
  817. {
  818. if ( IsProxyScriptCached() &&
  819. HostScheme == m_HostScheme &&
  820. HostPort == m_HostPort &&
  821. fUseProxy )
  822. {
  823. if ( pProxyServer == m_ProxyLink ) {
  824. INET_ASSERT(dwProxyVersion == GlobalProxyVersionCount);
  825. m_dwProxyVersion = dwProxyVersion; // we're now up to date
  826. goto quit; // match, no version or host changes
  827. }
  828. INET_ASSERT(pProxyServer != m_ProxyLink );
  829. }
  830. //
  831. // unlink, because we have a new entry to save,
  832. // and the previous entry is bad
  833. //
  834. m_ProxyLink->Dereference();
  835. m_ProxyLink = NULL;
  836. }
  837. //
  838. // Add new cached entry
  839. //
  840. SetProxyScriptCached(TRUE);
  841. m_HostScheme = HostScheme;
  842. m_HostPort = HostPort;
  843. m_dwProxyVersion = dwProxyVersion; // we're now up to date
  844. if ( fUseProxy )
  845. {
  846. INET_ASSERT(this != pProxyServer);
  847. m_ProxyLink = pProxyServer;
  848. m_ProxyLink->Reference();
  849. m_ProxyLink->m_HostScheme = ProxyScheme;
  850. m_ProxyLink->m_HostPort = ProxyPort;
  851. switch (ProxyScheme)
  852. {
  853. case INTERNET_SCHEME_HTTP:
  854. m_ProxyLink->SetCernProxy();
  855. break;
  856. case INTERNET_SCHEME_SOCKS:
  857. m_ProxyLink->SetSocksGateway();
  858. break;
  859. case INTERNET_SCHEME_FTP:
  860. m_ProxyLink->SetFTPProxy();
  861. break;
  862. }
  863. }
  864. quit:
  865. UnlockSerializedList(&GlobalServerInfoList);
  866. return error;
  867. }
  868. CServerInfo *
  869. CServerInfo::GetCachedProxyServerInfo(
  870. IN INTERNET_SCHEME HostScheme,
  871. IN INTERNET_PORT HostPort,
  872. OUT BOOL *pfCachedEntry
  873. )
  874. /*++
  875. Routine Description:
  876. Retrieves a cached server object, that indicates
  877. a probable proxy to use
  878. On Success, the return has an additional increment
  879. on its ref count, assumition that caller derefs
  880. Arguments:
  881. None.
  882. Return Value:
  883. CServerInfo *
  884. NULL on failure
  885. --*/
  886. {
  887. CServerInfo *pProxyServer = NULL;
  888. LockSerializedList(&GlobalServerInfoList);
  889. *pfCachedEntry = FALSE;
  890. if ( IsProxyScriptCached() )
  891. {
  892. //
  893. // Examine Version Count
  894. //
  895. if ( GlobalProxyVersionCount == m_dwProxyVersion &&
  896. HostScheme == m_HostScheme &&
  897. HostPort == m_HostPort
  898. )
  899. {
  900. *pfCachedEntry = TRUE;
  901. if ( m_ProxyLink ) {
  902. // matched cached entry
  903. m_ProxyLink->Reference();
  904. pProxyServer = m_ProxyLink;
  905. }
  906. }
  907. else
  908. {
  909. // version is expired, remove reference
  910. SetProxyScriptCached(FALSE);
  911. if ( m_ProxyLink ) {
  912. m_ProxyLink->Dereference();
  913. m_ProxyLink = NULL;
  914. }
  915. }
  916. }
  917. UnlockSerializedList(&GlobalServerInfoList);
  918. return pProxyServer;
  919. }
  920. BOOL
  921. CServerInfo::CopyCachedProxyInfoToProxyMsg(
  922. IN OUT AUTO_PROXY_ASYNC_MSG *pQueryForProxyInfo
  923. )
  924. /*++
  925. Routine Description:
  926. Retrieves Cached Proxy info from object
  927. Arguments:
  928. None.
  929. Return Value:
  930. BOOL
  931. TRUE - sucess
  932. --*/
  933. {
  934. BOOL fSuccess = FALSE;
  935. // really only need to lock to proctect m_HostPort && m_HostScheme
  936. LockSerializedList(&GlobalServerInfoList);
  937. pQueryForProxyInfo->SetUseProxy(FALSE);
  938. pQueryForProxyInfo->_lpszProxyHostName =
  939. m_HostName.StringAddress() ?
  940. NewString(m_HostName.StringAddress()) :
  941. NULL;
  942. if ( pQueryForProxyInfo->_lpszProxyHostName != NULL ) {
  943. // copy out cached entry to proxy message structure
  944. pQueryForProxyInfo->_nProxyHostPort = m_HostPort;
  945. pQueryForProxyInfo->_tProxyScheme = m_HostScheme;
  946. pQueryForProxyInfo->_bFreeProxyHostName = TRUE;
  947. pQueryForProxyInfo->_dwProxyHostNameLength =
  948. strlen((pQueryForProxyInfo)->_lpszProxyHostName);
  949. pQueryForProxyInfo->SetUseProxy(TRUE);
  950. fSuccess = TRUE; // success
  951. }
  952. UnlockSerializedList(&GlobalServerInfoList);
  953. return fSuccess;
  954. }
  955. //
  956. //DWORD
  957. //CServerInfo::ResolveHostName(
  958. // IN BOOL bForce
  959. // )
  960. //
  961. ///*++
  962. //
  963. //Routine Description:
  964. //
  965. // Resolves the host name for a CServerInfo. If we already have a resolved
  966. // name for this info then it is freed. There can be only one thread updating
  967. // or using the host name info
  968. //
  969. //Arguments:
  970. //
  971. // bForce - TRUE if we need to force a re-resolution
  972. //
  973. //Return Value:
  974. //
  975. // DWORD
  976. // Success - ERROR_SUCCESS
  977. //
  978. // Failure -
  979. //
  980. //--*/
  981. //
  982. //{
  983. // DEBUG_ENTER((DBG_SESSION,
  984. // Dword,
  985. // "CServerInfo::ResolveHostName",
  986. // "{%q} %B",
  987. // GetHostName(),
  988. // bForce
  989. // ));
  990. //
  991. // DWORD error = ERROR_SUCCESS;
  992. //
  993. // if (bForce || IsAddressListEmpty(&m_AddressList)) {
  994. // EnterCriticalSection(&m_AddressListCritSec);
  995. //
  996. // //
  997. // // BUGBUG - ideally, we want to test TTL here if bForce is TRUE to stop
  998. // // multiple simultaneous forced requests re-resolving the same
  999. // // info
  1000. // //
  1001. //
  1002. // if (bForce || IsAddressListEmpty(&m_AddressList)) {
  1003. //
  1004. // //
  1005. // // FreeAddressList() checks for an empty list
  1006. // //
  1007. //
  1008. // FreeAddressList(&m_AddressList);
  1009. //
  1010. // //
  1011. // // resolve the name & generate an address list. Since we don't know
  1012. // // the port address we want to talk to right now, we will generate a
  1013. // // list containing a default port number (0) which we must override
  1014. // // when we really connect to the server
  1015. // //
  1016. //
  1017. // error = ::GetServiceAddress(GetHostName(),
  1018. // NULL, // service name
  1019. // NULL, // service guid
  1020. // NS_DEFAULT,
  1021. // INTERNET_INVALID_PORT_NUMBER,
  1022. // 0, // protocol characteristics
  1023. // &m_AddressList
  1024. // );
  1025. // }
  1026. // LeaveCriticalSection(&m_AddressListCritSec);
  1027. // }
  1028. //
  1029. // DEBUG_LEAVE(error);
  1030. //
  1031. // return error;
  1032. //}
  1033. VOID
  1034. CServerInfo::UpdateConnectTime(
  1035. IN DWORD dwConnectTime
  1036. )
  1037. /*++
  1038. Routine Description:
  1039. Calculates average connect time
  1040. Arguments:
  1041. dwConnectTime - current connect time
  1042. Return Value:
  1043. None.
  1044. --*/
  1045. {
  1046. DEBUG_ENTER((DBG_SESSION,
  1047. None,
  1048. "CServerInfo::UpdateConnectTime",
  1049. "{%q} %d",
  1050. GetHostName(),
  1051. dwConnectTime
  1052. ));
  1053. DWORD connectTime = m_ConnectTime;
  1054. if (connectTime == (DWORD)-1) {
  1055. connectTime = dwConnectTime;
  1056. } else {
  1057. connectTime = (connectTime + dwConnectTime) / 2;
  1058. }
  1059. //dprintf("%s: connect time = %d, ave = %d\n", GetHostName(), dwConnectTime, connectTime);
  1060. DEBUG_PRINT(SESSION,
  1061. INFO,
  1062. ("average connect time = %d mSec\n",
  1063. connectTime
  1064. ));
  1065. InterlockedExchange((LPLONG)&m_ConnectTime, connectTime);
  1066. DEBUG_LEAVE(0);
  1067. }
  1068. VOID
  1069. CServerInfo::UpdateRTT(
  1070. IN DWORD dwRTT
  1071. )
  1072. /*++
  1073. Routine Description:
  1074. Calculates rolling average round-trip time
  1075. Arguments:
  1076. dwRTT - current round-trip time
  1077. Return Value:
  1078. None.
  1079. --*/
  1080. {
  1081. DEBUG_ENTER((DBG_SESSION,
  1082. None,
  1083. "CServerInfo::UpdateRTT",
  1084. "{%q} %d",
  1085. GetHostName(),
  1086. dwRTT
  1087. ));
  1088. DWORD RTT = m_RTT;
  1089. if (RTT == 0) {
  1090. RTT = dwRTT;
  1091. } else {
  1092. RTT = (RTT + dwRTT) / 2;
  1093. }
  1094. //dprintf("%s: RTT = %d, ave = %d\n", GetHostName(), dwRTT, RTT);
  1095. DEBUG_PRINT(SESSION,
  1096. INFO,
  1097. ("average round trip time = %d mSec\n",
  1098. RTT
  1099. ));
  1100. InterlockedExchange((LPLONG)&m_RTT, RTT);
  1101. DEBUG_LEAVE(0);
  1102. }
  1103. VOID
  1104. CServerInfo::UpdateDownloadRate(
  1105. IN DWORD dwBytesPerSecond
  1106. )
  1107. /*++
  1108. Routine Description:
  1109. Calculates average download rate
  1110. Arguments:
  1111. dwBytesPerSecond - current download rate
  1112. Return Value:
  1113. None.
  1114. --*/
  1115. {
  1116. DEBUG_ENTER((DBG_SESSION,
  1117. None,
  1118. "CServerInfo::UpdateDownloadRate",
  1119. "{%q} %d",
  1120. GetHostName(),
  1121. dwBytesPerSecond
  1122. ));
  1123. DWORD downloadRate = m_DownloadRate;
  1124. if (downloadRate == 0) {
  1125. downloadRate = dwBytesPerSecond;
  1126. } else {
  1127. downloadRate = (downloadRate + dwBytesPerSecond) / 2;
  1128. }
  1129. DEBUG_PRINT(SESSION,
  1130. INFO,
  1131. ("average download rate = %d bytes/second\n",
  1132. downloadRate
  1133. ));
  1134. InterlockedExchange((LPLONG)&m_DownloadRate, downloadRate);
  1135. DEBUG_LEAVE(0);
  1136. }
  1137. VOID
  1138. CServerInfo::UpdateUploadRate(
  1139. IN DWORD dwBytesPerSecond
  1140. )
  1141. /*++
  1142. Routine Description:
  1143. Calculates average upload rate
  1144. Arguments:
  1145. dwBytesPerSecond - current upload rate
  1146. Return Value:
  1147. None.
  1148. --*/
  1149. {
  1150. DEBUG_ENTER((DBG_SESSION,
  1151. None,
  1152. "CServerInfo::UpdateUploadRate",
  1153. "{%q} %d",
  1154. GetHostName(),
  1155. dwBytesPerSecond
  1156. ));
  1157. DWORD uploadRate = m_UploadRate;
  1158. if (uploadRate == 0) {
  1159. uploadRate = dwBytesPerSecond;
  1160. } else {
  1161. uploadRate = (uploadRate + dwBytesPerSecond) / 2;
  1162. }
  1163. DEBUG_PRINT(SESSION,
  1164. INFO,
  1165. ("average upload rate = %d bytes/second\n",
  1166. uploadRate
  1167. ));
  1168. InterlockedExchange((LPLONG)&m_UploadRate, uploadRate);
  1169. DEBUG_LEAVE(0);
  1170. }
  1171. //
  1172. //DWORD
  1173. //CServerInfo::GetConnection(
  1174. // IN DWORD dwSocketFlags,
  1175. // IN INTERNET_PORT nPort,
  1176. // IN DWORD dwTimeout,
  1177. // OUT ICSocket * * lplpSocket
  1178. // )
  1179. //
  1180. ///*++
  1181. //
  1182. //Routine Description:
  1183. //
  1184. // Combines connection limiting and keep-alive list management. If keep-alive
  1185. // connection requested and one is available, it is given out. If we can create
  1186. // a connection, the connection count is increased (actually decremented) and
  1187. // an OK-to-continue indication returned to the caller. If all connections are
  1188. // currently in use, we wait for one to become available. If this is a sync
  1189. // operation, we block waiting for the waiter event. If this is an async
  1190. // operation, we return ERROR_IO_PENDING and pick up the request on a worker
  1191. // thread when it becomes unblocked or times out proper
  1192. //
  1193. //Arguments:
  1194. //
  1195. // dwSocketFlags - flags identifying what type of connection we want:
  1196. //
  1197. // SF_SECURE - we want https connection
  1198. //
  1199. // SF_KEEP_ALIVE - we want persistent connection
  1200. //
  1201. // SF_NON_BLOCKING - we want non-blocking (async) socket
  1202. //
  1203. // nPort - required port
  1204. //
  1205. // dwTimeout - number of milliseconds we are willing to wait for
  1206. // connection to become available
  1207. //
  1208. // lplpSocket - returned pointer to ICSocket. ONLY used if the request is
  1209. // for a keep-alive socket and we had one available
  1210. //
  1211. //Return Value:
  1212. //
  1213. // DWORD
  1214. // Success - ERROR_SUCCESS
  1215. // Depending on *lplpSocket, we either returned the socket to
  1216. // use, or its okay to create a new connection
  1217. //
  1218. // ERROR_IO_PENDING
  1219. // Request will complete asynchronously
  1220. //
  1221. // Failure - ERROR_INTERNET_TIMEOUT
  1222. // Failed to get connection in time allowed
  1223. //
  1224. //--*/
  1225. //
  1226. //{
  1227. // DEBUG_ENTER((DBG_SESSION,
  1228. // Dword,
  1229. // "CServerInfo::GetConnection",
  1230. // "{%q} %#x, %d, %d, %#x",
  1231. // GetHostName(),
  1232. // dwSocketFlags,
  1233. // nPort,
  1234. // dwTimeout,
  1235. // lplpSocket
  1236. // ));
  1237. //
  1238. // *lplpSocket = NULL;
  1239. //
  1240. // DWORD error = DoFsm(new CFsm_GetConnection(dwSocketFlags,
  1241. // nPort,
  1242. // dwTimeout,
  1243. // lplpSocket,
  1244. // this
  1245. // ));
  1246. //
  1247. // DEBUG_LEAVE(error);
  1248. //
  1249. // return error;
  1250. //}
  1251. DWORD
  1252. CFsm_GetConnection::RunSM(
  1253. IN CFsm * Fsm
  1254. )
  1255. /*++
  1256. Routine Description:
  1257. Runs next CFsm_GetConnection state
  1258. Arguments:
  1259. Fsm - FSM controlling operation
  1260. Return Value:
  1261. DWORD
  1262. Success - ERROR_SUCCESS
  1263. Failure -
  1264. --*/
  1265. {
  1266. //dprintf("%#x: %s FSM %#x state %s\n", GetCurrentThreadId(), Fsm->MapType(), Fsm, Fsm->MapState());
  1267. DEBUG_ENTER((DBG_SESSION,
  1268. Dword,
  1269. "CFsm_GetConnection::RunSM",
  1270. "%#x",
  1271. Fsm
  1272. ));
  1273. CServerInfo * pServerInfo = (CServerInfo *)Fsm->GetContext();
  1274. CFsm_GetConnection * stateMachine = (CFsm_GetConnection *)Fsm;
  1275. DWORD error;
  1276. switch (Fsm->GetState()) {
  1277. case FSM_STATE_INIT:
  1278. stateMachine->StartTimer();
  1279. //
  1280. // fall through
  1281. //
  1282. case FSM_STATE_CONTINUE:
  1283. #ifdef NEW_CONNECTION_SCHEME
  1284. case FSM_STATE_ERROR:
  1285. #endif
  1286. error = pServerInfo->GetConnection_Fsm(stateMachine);
  1287. break;
  1288. #ifndef NEW_CONNECTION_SCHEME
  1289. case FSM_STATE_ERROR:
  1290. INET_ASSERT((Fsm->GetError() == ERROR_INTERNET_TIMEOUT)
  1291. || (Fsm->GetError() == ERROR_INTERNET_OPERATION_CANCELLED));
  1292. pServerInfo->RemoveWaiter((DWORD_PTR)Fsm);
  1293. error = Fsm->GetError();
  1294. Fsm->SetDone();
  1295. //dprintf("%#x: FSM_STATE_ERROR - %d\n", GetCurrentThreadId(), error);
  1296. break;
  1297. #endif
  1298. default:
  1299. error = ERROR_INTERNET_INTERNAL_ERROR;
  1300. Fsm->SetDone(ERROR_INTERNET_INTERNAL_ERROR);
  1301. INET_ASSERT(FALSE);
  1302. break;
  1303. }
  1304. DEBUG_LEAVE(error);
  1305. return error;
  1306. }
  1307. #ifdef NEW_CONNECTION_SCHEME
  1308. //
  1309. //
  1310. //DWORD
  1311. //CServerInfo::GetConnection_Fsm(
  1312. // IN CFsm_GetConnection * Fsm
  1313. // )
  1314. //
  1315. ///*++
  1316. //
  1317. //Routine Description:
  1318. //
  1319. // Tries to get a connection of requested type for caller. If no connection is
  1320. // available then one of the following happens:
  1321. //
  1322. // * If there are available keep-alive connections of a different type then
  1323. // one is closed and the caller allowed to create a new connection
  1324. //
  1325. // * If this is an async request, the FSM is blocked and the thread returns
  1326. // to the pool if a worker, or back to the app if an app thread
  1327. //
  1328. // * If this is a sync request, we wait on an event for a conneciton to be
  1329. // made available, or the connect timeout to elapse
  1330. //
  1331. // * In the situation where we are being run out of connections (number of
  1332. // active connections >= connection limit AND no activity has taken place
  1333. // on any connection in the last dwLimitTimeout mSec (in FSM)) then we
  1334. // allow a new connection to be created
  1335. //
  1336. //Arguments:
  1337. //
  1338. // Fsm - get connection FSM
  1339. //
  1340. //Return Value:
  1341. //
  1342. // DWORD
  1343. // Success - ERROR_SUCCESS
  1344. // Depending on *lplpSocket, we either returned the socket to
  1345. // use, or its okay to create a new connection
  1346. //
  1347. // ERROR_IO_PENDING
  1348. // Request will complete asynchronously
  1349. //
  1350. // Failure - ERROR_INTERNET_TIMEOUT
  1351. // Failed to get connection in time allowed
  1352. //
  1353. // ERROR_INTERNET_INTERNAL_ERROR
  1354. // Something unexpected happened
  1355. //
  1356. //--*/
  1357. //
  1358. //{
  1359. // DEBUG_ENTER((DBG_SESSION,
  1360. // Dword,
  1361. // "CServerInfo::GetConnection_Fsm",
  1362. // "{%q [%d+%d/%d]} %#x(%#x, %d, %d, %d)",
  1363. // GetHostName(),
  1364. // AvailableConnections(),
  1365. // KeepAliveConnections(),
  1366. // ConnectionLimit(),
  1367. // Fsm,
  1368. // Fsm->m_dwSocketFlags,
  1369. // Fsm->m_nPort,
  1370. // Fsm->m_dwTimeout,
  1371. // Fsm->m_dwLimitTimeout
  1372. // ));
  1373. //
  1374. // DEBUG_PRINT(SESSION,
  1375. // INFO,
  1376. // ("FSM %#x state %s elapsed %d\n",
  1377. // Fsm,
  1378. // Fsm->MapState(),
  1379. // Fsm->GetElapsedTime()
  1380. // ));
  1381. //
  1382. // DPRINTF("%#x: FSM %#x state %s elapsed %d\n",
  1383. // GetCurrentThreadId(),
  1384. // Fsm,
  1385. // Fsm->MapState(),
  1386. // Fsm->GetElapsedTime()
  1387. // );
  1388. //
  1389. // PERF_ENTER(GetConnection);
  1390. //
  1391. // CFsm_GetConnection & fsm = *Fsm;
  1392. // DWORD error = fsm.GetError();
  1393. // FSM_STATE state = fsm.GetState();
  1394. // LPINTERNET_THREAD_INFO lpThreadInfo = fsm.GetThreadInfo();
  1395. // ICSocket * pSocket = NULL;
  1396. // BOOL bUnlockList = FALSE;
  1397. // BOOL bKeepAliveWaiters;
  1398. //
  1399. // *fsm.m_lplpSocket = NULL;
  1400. //
  1401. // //
  1402. // // FSM_STATE_ERROR processing. Typically a timeout has occurred. We now want
  1403. // // to force a new connection instead of timing out
  1404. // //
  1405. //
  1406. // if (error != ERROR_SUCCESS) {
  1407. // RemoveWaiter((DWORD)Fsm);
  1408. // }
  1409. //
  1410. // //
  1411. // // timeout error is OK - we just try again until we get cancelled out or hit
  1412. // // the retry limit. Any other error causes failure. We should not be getting
  1413. // // ERROR_SUCCESS (test put here for defensive purposes only)
  1414. // //
  1415. //
  1416. // if ((state == FSM_STATE_ERROR)
  1417. // && (error != ERROR_INTERNET_TIMEOUT)
  1418. // && (error != ERROR_SUCCESS)) {
  1419. //
  1420. // DPRINTF("%#x: FSM %#x FSM_STATE_ERROR, error = %d\n",
  1421. // GetCurrentThreadId(),
  1422. // Fsm,
  1423. // error
  1424. // );
  1425. //
  1426. // DEBUG_PRINT(SESSION,
  1427. // ERROR,
  1428. // ("FSM %#x FSM_STATE_ERROR, error = %s (%d)\n",
  1429. // Fsm,
  1430. // InternetMapError(error),
  1431. // error
  1432. // ));
  1433. //
  1434. // INET_ASSERT(error != ERROR_SUCCESS);
  1435. //
  1436. // goto quit;
  1437. // }
  1438. //
  1439. // INET_ASSERT(lpThreadInfo != NULL);
  1440. // INET_ASSERT(lpThreadInfo->hObjectMapped != NULL);
  1441. //
  1442. // if ((lpThreadInfo == NULL) || (lpThreadInfo->hObjectMapped == NULL)) {
  1443. // error = ERROR_INTERNET_INTERNAL_ERROR;
  1444. // goto quit;
  1445. // }
  1446. //
  1447. // BOOL bAsyncRequest;
  1448. //
  1449. // bAsyncRequest = lpThreadInfo->IsAsyncWorkerThread
  1450. // || ((INTERNET_HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)->
  1451. // IsAsyncHandle();
  1452. //
  1453. // //
  1454. // // before we do anything, check if we've been run out of connections. If we
  1455. // // don't do this check here and we are out, then we'll wait unnecessarily
  1456. // // (assuming connections are not returned)
  1457. // //
  1458. //
  1459. // if ((state == FSM_STATE_INIT) && RunOutOfConnections()) {
  1460. //
  1461. // DPRINTF("%#x: out of connections on first attempt: %d+%d/%d\n",
  1462. // GetCurrentThreadId(),
  1463. // AvailableConnections(),
  1464. // KeepAliveConnections(),
  1465. // ConnectionLimit()
  1466. // );
  1467. //
  1468. // DEBUG_PRINT(SESSION,
  1469. // ERROR,
  1470. // ("out of connections on first attempt: %d+%d/%d\n",
  1471. // AvailableConnections(),
  1472. // KeepAliveConnections(),
  1473. // ConnectionLimit()
  1474. // ));
  1475. //
  1476. // error = ERROR_SUCCESS;
  1477. // pSocket = NULL;
  1478. // goto quit;
  1479. // }
  1480. //
  1481. //try_again:
  1482. //
  1483. // bUnlockList = TRUE;
  1484. //
  1485. // //
  1486. // // use m_Waiters to serialize access. N.B. - we will acquire m_KeepAliveList
  1487. // // from within m_Waiters
  1488. // //
  1489. //
  1490. // LockSerializedList(&m_Waiters);
  1491. // bKeepAliveWaiters = KeepAliveWaiters();
  1492. // if (fsm.m_dwSocketFlags & SF_KEEP_ALIVE) {
  1493. //
  1494. // //
  1495. // // maintain requester order - if there are already waiters then queue
  1496. // // this request, else try to satisfy the requester. HOWEVER, only check
  1497. // // for existing requesters the FIRST time through. If we're here with
  1498. // // FSM_STATE_CONTINUE then we've been unblocked and we can ignore any
  1499. // // waiters that came after us
  1500. // //
  1501. //
  1502. // if ((state == FSM_STATE_CONTINUE) || !bKeepAliveWaiters) {
  1503. //
  1504. // DEBUG_PRINT(SESSION,
  1505. // INFO,
  1506. // ("continuing or no current waiters for K-A connections\n"
  1507. // ));
  1508. //
  1509. // while (pSocket = FindKeepAliveConnection(fsm.m_dwSocketFlags, fsm.m_nPort)) {
  1510. // if (pSocket->IsReset()) {
  1511. //
  1512. // DPRINTF("%#x: ********* socket %#x is closed already\n",
  1513. // GetCurrentThreadId(),
  1514. // pSocket->GetSocket()
  1515. // );
  1516. //
  1517. // DEBUG_PRINT(SESSION,
  1518. // INFO,
  1519. // ("K-A connection %#x [%#x] is closed\n",
  1520. // pSocket,
  1521. // pSocket->GetSocket()
  1522. // ));
  1523. //
  1524. // pSocket->SetLinger(FALSE, 0);
  1525. // pSocket->Shutdown(2);
  1526. ////dprintf("GetConnection: destroying reset socket %#x\n", pSocket->GetSocket());
  1527. // pSocket->Destroy();
  1528. // if (!UnlimitedConnections()) {
  1529. // ++m_ConnectionsAvailable;
  1530. // }
  1531. // CHECK_CONNECTION_COUNT();
  1532. // } else {
  1533. //
  1534. // DPRINTF("%#x: *** matched %#x, %#x\n",
  1535. // GetCurrentThreadId(),
  1536. // pSocket->GetSocket(),
  1537. // pSocket->GetFlags()
  1538. // );
  1539. //
  1540. // break;
  1541. // }
  1542. // }
  1543. // if (pSocket == NULL) {
  1544. //
  1545. // DEBUG_PRINT(SESSION,
  1546. // INFO,
  1547. // ("no available K-A connections\n"
  1548. // ));
  1549. //
  1550. // /*
  1551. // //
  1552. // // if all connections are in use as keep-alive connections then
  1553. // // since we're here, we want a keep-alive connection that doesn't
  1554. // // match the currently available keep-alive connections. Terminate
  1555. // // the oldest keep-alive connection (at the head of the queue)
  1556. // // and generate a new connection
  1557. // //
  1558. //
  1559. // LockSerializedList(&m_KeepAliveList);
  1560. // if (ElementsOnSerializedList(&m_KeepAliveList) == m_ConnectionLimit) {
  1561. // pSocket = ContainingICSocket(SlDequeueHead(&m_KeepAliveList));
  1562. // pSocket->SetLinger(FALSE, 0);
  1563. // pSocket->Shutdown(2);
  1564. // pSocket->Destroy();
  1565. // if (!UnlimitedConnections()) {
  1566. // ++m_ConnectionsAvailable;
  1567. // }
  1568. // CHECK_CONNECTION_COUNT();
  1569. // }
  1570. // UnlockSerializedList(&m_KeepAliveList);
  1571. // */
  1572. // }
  1573. // } else {
  1574. //
  1575. // DEBUG_PRINT(SESSION,
  1576. // INFO,
  1577. // ("%d waiters for K-A connection to %q\n",
  1578. // ElementsOnSerializedList(&m_KeepAliveList),
  1579. // GetHostName()
  1580. // ));
  1581. //
  1582. // }
  1583. // }
  1584. //
  1585. // //
  1586. // // if we found a matching keep-alive connection or we are not limiting
  1587. // // connections then we're done
  1588. // //
  1589. //
  1590. // if ((pSocket != NULL) || UnlimitedConnections()) {
  1591. //
  1592. // INET_ASSERT(error == ERROR_SUCCESS);
  1593. //
  1594. // error = ERROR_SUCCESS;
  1595. // goto exit;
  1596. // }
  1597. //
  1598. // //
  1599. // // no keep-alive connections matched, or there are already waiters for
  1600. // // keep-alive connections
  1601. // //
  1602. //
  1603. // INET_ASSERT((AvailableConnections() >= 0)
  1604. // && (AvailableConnections() <= ConnectionLimit()));
  1605. //
  1606. // if (AvailableConnections() > 0) {
  1607. //
  1608. // //
  1609. // // can create a connection
  1610. // //
  1611. //
  1612. // DEBUG_PRINT(SESSION,
  1613. // INFO,
  1614. // ("%s: OK to create new connection: %d/%d\n",
  1615. // GetHostName(),
  1616. // AvailableConnections(),
  1617. // ConnectionLimit()
  1618. // ));
  1619. //
  1620. // DPRINTF("%#x: *** %s: OK to create connection: %d/%d\n",
  1621. // GetCurrentThreadId(),
  1622. // GetHostName(),
  1623. // AvailableConnections(),
  1624. // ConnectionLimit()
  1625. // );
  1626. //
  1627. // INET_ASSERT(error == ERROR_SUCCESS);
  1628. // INET_ASSERT(pSocket == NULL);
  1629. //
  1630. // --m_ConnectionsAvailable;
  1631. // //} else if (fsm.GetElapsedTime() > fsm.m_dwTimeout) {
  1632. // // error = ERROR_INTERNET_TIMEOUT;
  1633. // } else {
  1634. //
  1635. // //
  1636. // // if there are keep-alive connections but no keep-alive waiters
  1637. // // then either we don't want a keep-alive connection, or the ones
  1638. // // available don't match our requirements.
  1639. // // If we need a connection of a different type - e.g. SSL when all
  1640. // // we have is non-SSL then close a connection & generate a new one.
  1641. // // If we need a non-keep-alive connection then its okay to return
  1642. // // a current keep-alive connection, the understanding being that the
  1643. // // caller will not add Connection: Keep-Alive header (HTTP 1.0) or
  1644. // // will add Connection: Close header (HTTP 1.1)
  1645. // //
  1646. //
  1647. // //
  1648. // // BUGBUG - what about waiters for non-keep-alive connections?
  1649. // //
  1650. // // scenario - limit of 1 connection:
  1651. // //
  1652. // // A. request for k-a
  1653. // // continue & create connection
  1654. // // B. request non-k-a
  1655. // // none available; wait
  1656. // // C. release k-a connection; unblock sync waiter B
  1657. // // D. request non-k-a
  1658. // // k-a available; return it; caller converts to non-k-a
  1659. // // E. unblocked waiter B request non-k-a
  1660. // // none available; wait
  1661. // //
  1662. // // If this situation continues, eventually B will time-out, whereas it
  1663. // // could have had the connection taken by D. Request D is younger and
  1664. // // therefore can afford to wait while B continues with the connection
  1665. // //
  1666. //
  1667. // BOOL fHaveConnection = FALSE;
  1668. //
  1669. // if (!bKeepAliveWaiters) {
  1670. // LockSerializedList(&m_KeepAliveList);
  1671. // if (KeepAliveConnections() != 0) {
  1672. // pSocket = ContainingICSocket(SlDequeueHead(&m_KeepAliveList));
  1673. // fHaveConnection = TRUE;
  1674. //
  1675. //#define SOCK_FLAGS (SF_ENCRYPT | SF_DECRYPT | SF_SECURE)
  1676. //
  1677. // DWORD dwSocketTypeFlags = pSocket->GetFlags() & SOCK_FLAGS;
  1678. // DWORD dwRequestTypeFlags = fsm.m_dwSocketFlags & SOCK_FLAGS;
  1679. //
  1680. // if (dwSocketTypeFlags ^ dwRequestTypeFlags) {
  1681. //
  1682. // DEBUG_PRINT(SESSION,
  1683. // INFO,
  1684. // ("different socket types requested: %#x, %#x\n",
  1685. // fsm.m_dwSocketFlags,
  1686. // pSocket->GetFlags()
  1687. // ));
  1688. //
  1689. // DPRINTF("%#x: *** closing socket %#x: %#x vs. %#x\n",
  1690. // GetCurrentThreadId(),
  1691. // pSocket->GetSocket(),
  1692. // pSocket->GetFlags(),
  1693. // fsm.m_dwSocketFlags
  1694. // );
  1695. //
  1696. // pSocket->SetLinger(FALSE, 0);
  1697. // pSocket->Shutdown(2);
  1698. ////dprintf("GetConnection: destroying different type socket %#x\n", pSocket->GetSocket());
  1699. // pSocket->Destroy();
  1700. // pSocket = NULL;
  1701. // } else {
  1702. //
  1703. // DPRINTF("%#x: *** returning k-a connection %#x as non-k-a\n",
  1704. // GetCurrentThreadId(),
  1705. // pSocket->GetSocket()
  1706. // );
  1707. //
  1708. // }
  1709. // CHECK_CONNECTION_COUNT();
  1710. // }
  1711. // UnlockSerializedList(&m_KeepAliveList);
  1712. // if (fHaveConnection) {
  1713. // goto exit;
  1714. // }
  1715. // }
  1716. //
  1717. // //
  1718. // // about to wait for a connection. If it looks as though we're being run
  1719. // // out of connections, then create a new one
  1720. // //
  1721. //
  1722. // if (RunOutOfConnections()) {
  1723. // pSocket = NULL;
  1724. // error = ERROR_SUCCESS;
  1725. // goto exit;
  1726. // }
  1727. //
  1728. // DPRINTF("%#x: blocking %s FSM %#x state %s %d/%d elapsed: %d mSec\n",
  1729. // GetCurrentThreadId(),
  1730. // Fsm->MapType(),
  1731. // Fsm,
  1732. // Fsm->MapState(),
  1733. // AvailableConnections(),
  1734. // ConnectionLimit(),
  1735. // Fsm->GetElapsedTime()
  1736. // );
  1737. //
  1738. // //
  1739. // // we have to wait for a connection to become available. If we are an
  1740. // // async request then we queue this FSM & return the thread to the pool
  1741. // // or, if app thread, return pending indication to the app. If this is
  1742. // // a sync request (in an app thread) then we block on an event waiting
  1743. // // for a connection to become available
  1744. // //
  1745. //
  1746. // HANDLE hEvent = NULL;
  1747. //
  1748. // if (!bAsyncRequest) {
  1749. //
  1750. // //
  1751. // // create unnamed, initially unsignalled, auto-reset event
  1752. // //
  1753. //
  1754. // hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  1755. // if (hEvent == NULL) {
  1756. // error = GetLastError();
  1757. // goto exit;
  1758. // }
  1759. // }
  1760. //
  1761. // CConnectionWaiter * pWaiter;
  1762. //
  1763. //#if INET_DEBUG
  1764. //
  1765. // for (pWaiter = (CConnectionWaiter *)HeadOfSerializedList(&m_Waiters);
  1766. // pWaiter != (CConnectionWaiter *)SlSelf(&m_Waiters);
  1767. // pWaiter = (CConnectionWaiter *)pWaiter->Next()) {
  1768. //
  1769. // INET_ASSERT(pWaiter->Id() != (DWORD)(bAsyncRequest ? (DWORD)Fsm : lpThreadInfo->ThreadId));
  1770. // }
  1771. //#endif
  1772. //
  1773. // pWaiter = new CConnectionWaiter(&m_Waiters,
  1774. // !bAsyncRequest,
  1775. // (fsm.m_dwSocketFlags & SF_KEEP_ALIVE)
  1776. // ? TRUE
  1777. // : FALSE,
  1778. // bAsyncRequest
  1779. // ? (DWORD)Fsm
  1780. // : lpThreadInfo->ThreadId,
  1781. // hEvent);
  1782. // if (pWaiter == NULL) {
  1783. // error = ERROR_NOT_ENOUGH_MEMORY;
  1784. // goto exit;
  1785. // }
  1786. // if (bAsyncRequest) {
  1787. //
  1788. // //
  1789. // // ensure that when the FSM is unblocked normally, the new state
  1790. // // is STATE_CONTINUE
  1791. // //
  1792. //
  1793. // DWORD dwWaitTime = min(fsm.m_dwLimitTimeout, fsm.m_dwTimeout);
  1794. //
  1795. // ////
  1796. // //// make wait time elastic: we don't want to time out after the
  1797. // //// connect timeout value elapses. We keep expanding the wait time
  1798. // //// for a connection until we get one, or the request is cancelled
  1799. // ////
  1800. // //
  1801. // //fsm.m_dwTimeout += dwWaitTime;
  1802. // //if (fsm.m_dwTimeout == 0xffffffff) {
  1803. // // --fsm.m_dwTimeout;
  1804. // //}
  1805. // fsm.SetState(FSM_STATE_CONTINUE);
  1806. // fsm.SetNextState(FSM_STATE_CONTINUE);
  1807. // error = BlockWorkItem(Fsm, (DWORD)pWaiter, dwWaitTime);
  1808. // if (error == ERROR_SUCCESS) {
  1809. // error = ERROR_IO_PENDING;
  1810. // }
  1811. // } else {
  1812. // UnlockSerializedList(&m_Waiters);
  1813. // bUnlockList = FALSE;
  1814. //
  1815. // DPRINTF("%#x: %s FSM %#x %s waiting %d mSec\n",
  1816. // GetCurrentThreadId(),
  1817. // Fsm->MapType(),
  1818. // Fsm,
  1819. // Fsm->MapState(),
  1820. // fsm.m_dwTimeout
  1821. // );
  1822. //
  1823. // //DWORD dwWaitTime = fsm.m_dwTimeout - fsm.GetElapsedTime();
  1824. // DWORD dwWaitTime = fsm.m_dwTimeout;
  1825. //
  1826. // if ((int)dwWaitTime <= 0) {
  1827. //
  1828. // DEBUG_PRINT(SESSION,
  1829. // ERROR,
  1830. // ("SYNC wait timed out (%d mSec)\n",
  1831. // fsm.m_dwTimeout
  1832. // ));
  1833. //
  1834. // error = ERROR_INTERNET_TIMEOUT;
  1835. // } else {
  1836. //
  1837. // DEBUG_PRINT(SESSION,
  1838. // INFO,
  1839. // ("waiting %d mSec for SYNC event %#x\n",
  1840. // dwWaitTime,
  1841. // hEvent
  1842. // ));
  1843. //
  1844. // //
  1845. // // we'd better not be doing a sync wait if we are in the
  1846. // // context of an app thread making an async request
  1847. // //
  1848. //
  1849. // INET_ASSERT(!lpThreadInfo->IsAsyncWorkerThread
  1850. // && !((INTERNET_HANDLE_OBJECT *)lpThreadInfo->
  1851. // hObjectMapped)->IsAsyncHandle());
  1852. //
  1853. // //INET_ASSERT(dwWaitTime <= 60000);
  1854. //
  1855. // DWORD dwDeltaWaitTime = min(dwWaitTime, fsm.m_dwLimitTimeout);
  1856. // DWORD dwTimeStarted = GetTickCount();
  1857. //
  1858. // do {
  1859. //
  1860. // DPRINTF("%#x: sync wait %d mSec\n",
  1861. // GetCurrentThreadId(),
  1862. // dwDeltaWaitTime
  1863. // );
  1864. //
  1865. // error = WaitForSingleObject(hEvent, dwDeltaWaitTime);
  1866. // if (error == STATUS_TIMEOUT) {
  1867. // if (RunOutOfConnections()) {
  1868. //
  1869. // DPRINTF("%#x: run out of connections\n",
  1870. // GetCurrentThreadId()
  1871. // );
  1872. //
  1873. // break;
  1874. // }
  1875. // }
  1876. // } while (((GetTickCount() - dwTimeStarted) < dwWaitTime)
  1877. // && (error == STATUS_TIMEOUT));
  1878. //
  1879. // DPRINTF("%#x: sync waiter unblocked - error = %d\n",
  1880. // GetCurrentThreadId(),
  1881. // error
  1882. // );
  1883. //
  1884. // }
  1885. // if (error == STATUS_TIMEOUT) {
  1886. //
  1887. // DPRINTF("%#x: %s %d+%d/%d: timed out %#x (%s FSM %#x %s)\n",
  1888. // GetCurrentThreadId(),
  1889. // GetHostName(),
  1890. // AvailableConnections(),
  1891. // KeepAliveConnections(),
  1892. // ConnectionLimit(),
  1893. // GetCurrentThreadId(),
  1894. // Fsm->MapType(),
  1895. // Fsm,
  1896. // Fsm->MapState()
  1897. // );
  1898. //
  1899. // RemoveWaiter(lpThreadInfo->ThreadId);
  1900. // //bUnlockList = FALSE;
  1901. // /*
  1902. // LockSerializedList(&m_Waiters);
  1903. // for (pWaiter = (CConnectionWaiter *)HeadOfSerializedList(&m_Waiters);
  1904. // pWaiter != (CConnectionWaiter *)SlSelf(&m_Waiters);
  1905. // pWaiter = (CConnectionWaiter *)pWaiter->Next()) {
  1906. //
  1907. // if (pWaiter->Id() == lpThreadInfo->ThreadId) {
  1908. // RemoveFromSerializedList(&m_Waiters, pWaiter->List());
  1909. // delete pWaiter;
  1910. // break;
  1911. // }
  1912. // }
  1913. // */
  1914. // error = RunOutOfConnections()
  1915. // ? WAIT_OBJECT_0
  1916. // : ERROR_INTERNET_TIMEOUT;
  1917. // //} else {
  1918. // // bUnlockList = FALSE;
  1919. // }
  1920. //
  1921. // BOOL bOk;
  1922. //
  1923. // bOk = CloseHandle(hEvent);
  1924. //
  1925. // INET_ASSERT(bOk);
  1926. //
  1927. // if (error == WAIT_OBJECT_0) {
  1928. //
  1929. // DPRINTF("%#x: sync requester trying again\n",
  1930. // GetCurrentThreadId()
  1931. // );
  1932. //
  1933. // fsm.SetState(FSM_STATE_CONTINUE);
  1934. // goto try_again;
  1935. // }
  1936. // }
  1937. // }
  1938. //
  1939. //exit:
  1940. //
  1941. // //
  1942. // // if we are returning a (keep-alive) socket that has a different blocking
  1943. // // mode from that requested, change it
  1944. // //
  1945. //
  1946. // if (pSocket != NULL) {
  1947. // if ((pSocket->GetFlags() & SF_NON_BLOCKING)
  1948. // ^ (fsm.m_dwSocketFlags & SF_NON_BLOCKING)) {
  1949. //
  1950. // DEBUG_PRINT(SESSION,
  1951. // INFO,
  1952. // ("different blocking modes requested: %#x, %#x\n",
  1953. // fsm.m_dwSocketFlags,
  1954. // pSocket->GetFlags()
  1955. // ));
  1956. //
  1957. // DPRINTF("%#x: *** changing socket %#x to %sBLOCKING\n",
  1958. // GetCurrentThreadId(),
  1959. // pSocket->GetSocket(),
  1960. // fsm.m_dwSocketFlags & SF_NON_BLOCKING
  1961. // ? "NON-"
  1962. // : ""
  1963. // );
  1964. //
  1965. // pSocket->SetNonBlockingMode(fsm.m_dwSocketFlags
  1966. // & SF_NON_BLOCKING);
  1967. // }
  1968. // *fsm.m_lplpSocket = pSocket;
  1969. // }
  1970. //
  1971. // if (bUnlockList) {
  1972. // UnlockSerializedList(&m_Waiters);
  1973. // }
  1974. //
  1975. //quit:
  1976. //
  1977. // if (error != ERROR_IO_PENDING) {
  1978. // fsm.SetDone();
  1979. // }
  1980. //
  1981. // DPRINTF("%#x: %s %d+%d/%d: get: %d, %#x, %d\n",
  1982. // GetCurrentThreadId(),
  1983. // GetHostName(),
  1984. // AvailableConnections(),
  1985. // KeepAliveConnections(),
  1986. // ConnectionLimit(),
  1987. // error,
  1988. // pSocket ? pSocket->GetSocket() : 0,
  1989. // ElementsOnSerializedList(&m_Waiters)
  1990. // );
  1991. //
  1992. // PERF_LEAVE(GetConnection);
  1993. //
  1994. // DEBUG_LEAVE(error);
  1995. //
  1996. // return error;
  1997. //}
  1998. //
  1999. //
  2000. //DWORD
  2001. //CServerInfo::ReleaseConnection(
  2002. // IN ICSocket * lpSocket OPTIONAL
  2003. // )
  2004. //
  2005. ///*++
  2006. //
  2007. //Routine Description:
  2008. //
  2009. // Returns a keep-alive connection to the pool, or allows another requester to
  2010. // create a connection.
  2011. //
  2012. // If we will break the connection limit then we assume we created an additional
  2013. // connection when we were being starved, and we discard this one
  2014. //
  2015. //Arguments:
  2016. //
  2017. // lpSocket - pointer to ICSocket if we are returning a keep-alive connection
  2018. //
  2019. //Return Value:
  2020. //
  2021. // DWORD
  2022. // Success - ERROR_SUCCESS
  2023. //
  2024. // Failure -
  2025. //
  2026. //--*/
  2027. //
  2028. //{
  2029. // DPRINTF("%#x: rls %#x %d+%d/%d\n",
  2030. // GetCurrentThreadId(),
  2031. // lpSocket ? lpSocket->GetSocket() : 0,
  2032. // AvailableConnections(),
  2033. // KeepAliveConnections(),
  2034. // ConnectionLimit()
  2035. // );
  2036. //
  2037. // //return ERROR_SUCCESS;
  2038. // DEBUG_ENTER((DBG_SESSION,
  2039. // Dword,
  2040. // "CServerInfo::ReleaseConnection",
  2041. // "{%q [%d+%d/%d]} %#x [%#x]",
  2042. // GetHostName(),
  2043. // AvailableConnections(),
  2044. // KeepAliveConnections(),
  2045. // ConnectionLimit(),
  2046. // lpSocket,
  2047. // lpSocket ? lpSocket->GetSocket() : 0
  2048. // ));
  2049. //
  2050. // PERF_ENTER(ReleaseConnection);
  2051. //
  2052. // DWORD error = ERROR_SUCCESS;
  2053. // BOOL bRelease = FALSE;
  2054. //
  2055. // LockSerializedList(&m_Waiters);
  2056. //
  2057. // //
  2058. // // quite often (at least with catapult proxy based on IIS) the server may
  2059. // // drop the connection even though it indicated it would keep it open. This
  2060. // // typically happens on 304 (frequent) and 302 (less so) responses. If we
  2061. // // determine the server has dropped the connection then throw it away and
  2062. // // allow the app to create a new one
  2063. // //
  2064. //
  2065. // if (lpSocket != NULL) {
  2066. // if (TotalAvailableConnections() >= ConnectionLimit()) {
  2067. //
  2068. // DPRINTF("%#x: !!! too many K-A connections %d+%d/%d - discarding %#x\n",
  2069. // GetCurrentThreadId(),
  2070. // AvailableConnections(),
  2071. // KeepAliveConnections(),
  2072. // ConnectionLimit(),
  2073. // lpSocket->GetSocket()
  2074. // );
  2075. //
  2076. // //
  2077. // // trying to return keep-alive socket would overflow limit (if we
  2078. // // are not already over). If we have less than the max. keep-alive
  2079. // // connections, we will keep this one else close it
  2080. // //
  2081. //
  2082. // if (KeepAliveConnections() >= ConnectionLimit()) {
  2083. //
  2084. // INET_ASSERT(KeepAliveConnections() == ConnectionLimit());
  2085. //
  2086. // //
  2087. // // BUGBUG - discarding k-a connection: it should be the oldest
  2088. // // (if oldest not this one)
  2089. // //
  2090. //
  2091. // DPRINTF("%#x: closing k-a %#x\n",
  2092. // GetCurrentThreadId(),
  2093. // lpSocket->GetSocket()
  2094. // );
  2095. //
  2096. // lpSocket->Close();
  2097. // } else {
  2098. //
  2099. // INET_ASSERT(AvailableConnections() > 0);
  2100. //
  2101. // if (AvailableConnections() > 0) {
  2102. // --m_ConnectionsAvailable;
  2103. // }
  2104. // }
  2105. // }
  2106. // if (lpSocket->IsClosed() || lpSocket->IsReset()) {
  2107. //
  2108. // DEBUG_PRINT(SESSION,
  2109. // INFO,
  2110. // ("socket %#x already dead - throwing it out\n",
  2111. // lpSocket->GetSocket()
  2112. // ));
  2113. //
  2114. // DPRINTF("%#x: socket %#x: already reset\n",
  2115. // GetCurrentThreadId(),
  2116. // lpSocket->GetSocket()
  2117. // );
  2118. //
  2119. ////dprintf("ReleaseConnection: destroying already closed socket %#x\n", lpSocket->GetSocket());
  2120. //
  2121. // BOOL bDestroyed = lpSocket->Dereference();
  2122. //
  2123. // INET_ASSERT(bDestroyed);
  2124. //
  2125. // lpSocket = NULL;
  2126. // } else {
  2127. //
  2128. // //
  2129. // // if we are returning a keep-alive socket, put it in non-blocking
  2130. // // mode if not already. Typically, Internet Explorer uses non-blocking
  2131. // // sockets. In the infrequent cases where we want a blocking socket
  2132. // // - mainly when doing java downloads - we will convert the socket
  2133. // // to blocking mode when we get it from the pool
  2134. // //
  2135. //
  2136. // if (!lpSocket->IsNonBlocking()) {
  2137. //
  2138. // DPRINTF("%#x: ***** WARNING: releasing BLOCKING k-a socket %#x\n",
  2139. // GetCurrentThreadId(),
  2140. // lpSocket->GetSocket()
  2141. // );
  2142. //
  2143. // lpSocket->SetNonBlockingMode(TRUE);
  2144. // }
  2145. // }
  2146. // }
  2147. // if (lpSocket != NULL) {
  2148. //
  2149. // DPRINTF("%#x: releasing K-A %#x (%d/%d)\n",
  2150. // GetCurrentThreadId(),
  2151. // lpSocket ? lpSocket->GetSocket() : 0,
  2152. // AvailableConnections(),
  2153. // ConnectionLimit()
  2154. // );
  2155. //
  2156. // INET_ASSERT(lpSocket->IsOpen());
  2157. // INET_ASSERT(!lpSocket->IsOnList());
  2158. // //INET_ASSERT(!lpSocket->IsReset());
  2159. //
  2160. // lpSocket->SetKeepAlive();
  2161. //
  2162. // DEBUG_PRINT(SESSION,
  2163. // INFO,
  2164. // ("releasing keep-alive socket %#x\n",
  2165. // lpSocket->GetSocket()
  2166. // ));
  2167. //
  2168. // lpSocket->SetExpiryTime(GlobalKeepAliveSocketTimeout);
  2169. //
  2170. // INET_ASSERT(!IsOnSerializedList(&m_KeepAliveList, lpSocket->List()));
  2171. //
  2172. // InsertAtTailOfSerializedList(&m_KeepAliveList, lpSocket->List());
  2173. //
  2174. // INET_ASSERT(UnlimitedConnections()
  2175. // ? TRUE
  2176. // : (KeepAliveConnections() <= ConnectionLimit()));
  2177. //
  2178. // bRelease = TRUE;
  2179. // } else {
  2180. //
  2181. // DPRINTF("%#x: releasing connection (%d+%d/%d)\n",
  2182. // GetCurrentThreadId(),
  2183. // AvailableConnections(),
  2184. // KeepAliveConnections(),
  2185. // ConnectionLimit()
  2186. // );
  2187. //
  2188. // if (!UnlimitedConnections()) {
  2189. // if (AvailableConnections() < ConnectionLimit()) {
  2190. // ++m_ConnectionsAvailable;
  2191. // } else {
  2192. //
  2193. // DPRINTF("%#x: !!! not increasing avail cons (%d+%d) - at limit (%d)\n",
  2194. // GetCurrentThreadId(),
  2195. // AvailableConnections(),
  2196. // KeepAliveConnections(),
  2197. // ConnectionLimit()
  2198. // );
  2199. //
  2200. // }
  2201. // }
  2202. //
  2203. // CHECK_CONNECTION_COUNT();
  2204. //
  2205. // bRelease = TRUE;
  2206. // }
  2207. // if (bRelease && !UnlimitedConnections()) {
  2208. //
  2209. // CHECK_CONNECTION_COUNT();
  2210. //
  2211. // CConnectionWaiter * pWaiter;
  2212. // BOOL bFreed = FALSE;
  2213. //
  2214. // //
  2215. // // loop here until we free a waiter or until there are no waiters left.
  2216. // // The reason we do this is that we must free a waiter if there are any
  2217. // // but the waiter corresponding to pWaiter may have been concurrently
  2218. // // timed out and cannot be unblocked by us
  2219. // //
  2220. //
  2221. // do {
  2222. // pWaiter = (CConnectionWaiter *)SlDequeueHead(&m_Waiters);
  2223. // if (pWaiter != NULL) {
  2224. //
  2225. // DEBUG_PRINT(SESSION,
  2226. // INFO,
  2227. // ("unblocking %s waiter %#x\n",
  2228. // pWaiter->IsSync() ? "Sync" : "Async",
  2229. // pWaiter->Id()
  2230. // ));
  2231. //
  2232. // DPRINTF("%#x: Unblocking %s connection waiter %#x\n",
  2233. // GetCurrentThreadId(),
  2234. // pWaiter->IsSync() ? "Sync" : "Async",
  2235. // pWaiter->Id()
  2236. // );
  2237. //
  2238. // if (pWaiter->IsSync()) {
  2239. // pWaiter->Signal();
  2240. // bFreed = TRUE;
  2241. // } else {
  2242. //
  2243. // int n = (int)UnblockWorkItems(1, (DWORD)pWaiter, ERROR_SUCCESS);
  2244. //
  2245. // if (n >= 1) {
  2246. //
  2247. // //
  2248. // // should never be > 1
  2249. // //
  2250. //
  2251. // INET_ASSERT(n == 1);
  2252. //
  2253. // bFreed = TRUE;
  2254. //
  2255. // DPRINTF("%#x: unblocked waiting FSM %#x\n",
  2256. // GetCurrentThreadId(),
  2257. // pWaiter->Id()
  2258. // );
  2259. //
  2260. // DEBUG_PRINT(SESSION,
  2261. // INFO,
  2262. // ("unblocked waiting FSM %#x\n",
  2263. // pWaiter->Id()
  2264. // ));
  2265. //
  2266. // } else {
  2267. //
  2268. // //
  2269. // // should never be < 0
  2270. // //
  2271. //
  2272. // INET_ASSERT(n == 0);
  2273. //
  2274. // DPRINTF("%#x: ********* waiting FSM %#x NOT UNBLOCKED\n",
  2275. // GetCurrentThreadId(),
  2276. // pWaiter->Id()
  2277. // );
  2278. //
  2279. // DEBUG_PRINT(SESSION,
  2280. // ERROR,
  2281. // ("waiting FSM %#x not unblocked\n",
  2282. // pWaiter->Id()
  2283. // ));
  2284. //
  2285. // }
  2286. // }
  2287. // delete pWaiter;
  2288. // } else {
  2289. //
  2290. // DEBUG_PRINT(SESSION,
  2291. // INFO,
  2292. // ("no waiters\n"
  2293. // ));
  2294. //
  2295. // DPRINTF("%#x: !!! NOT unblocking connection waiter\n",
  2296. // GetCurrentThreadId()
  2297. // );
  2298. //
  2299. // }
  2300. // } while ((pWaiter != NULL) && !bFreed);
  2301. // } else {
  2302. //
  2303. // DPRINTF("%#x: !!! NOT releasing or unlimited?\n",
  2304. // GetCurrentThreadId()
  2305. // );
  2306. //
  2307. // DEBUG_PRINT(SESSION,
  2308. // INFO,
  2309. // ("bRelease = %B, UnlimitedConnections() = %B\n",
  2310. // bRelease,
  2311. // UnlimitedConnections()
  2312. // ));
  2313. //
  2314. // }
  2315. //
  2316. // if (TotalAvailableConnections() >= ConnectionLimit()) {
  2317. // ResetLastActiveTime();
  2318. // }
  2319. //
  2320. // DEBUG_PRINT(SESSION,
  2321. // INFO,
  2322. // ("avail+k-a/limit = %d+%d/%d\n",
  2323. // AvailableConnections(),
  2324. // KeepAliveConnections(),
  2325. // ConnectionLimit()
  2326. // ));
  2327. //
  2328. // UnlockSerializedList(&m_Waiters);
  2329. //
  2330. // PERF_LEAVE(ReleaseConnection);
  2331. //
  2332. // DEBUG_LEAVE(error);
  2333. //
  2334. // DPRINTF("%#x: %s %d+%d/%d: rls %#x: %d, %d\n",
  2335. // GetCurrentThreadId(),
  2336. // GetHostName(),
  2337. // AvailableConnections(),
  2338. // KeepAliveConnections(),
  2339. // ConnectionLimit(),
  2340. // lpSocket ? lpSocket->GetSocket() : 0,
  2341. // error,
  2342. // ElementsOnSerializedList(&m_Waiters)
  2343. // );
  2344. //
  2345. // return error;
  2346. //}
  2347. //
  2348. #else
  2349. DWORD
  2350. CServerInfo::GetConnection_Fsm(
  2351. IN CFsm_GetConnection * Fsm
  2352. )
  2353. /*++
  2354. Routine Description:
  2355. Tries to get a connection of requested type for caller. If no connection is
  2356. available then one of the following happens:
  2357. * If there are available keep-alive connections of a different type then
  2358. one is closed and the caller allowed to create a new connection
  2359. * If this is an async request, the FSM is blocked and the thread returns
  2360. to the pool if a worker, or back to the app if an app thread
  2361. * If this is a sync request, we wait on an event for a connection to be
  2362. made available, or the connect timeout to elapse
  2363. Arguments:
  2364. Fsm - get connection FSM
  2365. Return Value:
  2366. DWORD
  2367. Success - ERROR_SUCCESS
  2368. Depending on *lplpSocket, we either returned the socket to
  2369. use, or its okay to create a new connection
  2370. ERROR_IO_PENDING
  2371. Request will complete asynchronously
  2372. Failure - ERROR_INTERNET_TIMEOUT
  2373. Failed to get connection in time allowed
  2374. ERROR_INTERNET_INTERNAL_ERROR
  2375. Something unexpected happened
  2376. --*/
  2377. {
  2378. DEBUG_ENTER((DBG_SESSION,
  2379. Dword,
  2380. "CServerInfo::GetConnection_Fsm",
  2381. "{%q [%d+%d/%d]} %#x(%#x, %d, %d)",
  2382. GetHostName(),
  2383. m_ConnectionsAvailable,
  2384. ElementsOnSerializedList(&m_KeepAliveList),
  2385. m_ConnectionLimit,
  2386. Fsm,
  2387. Fsm->m_dwSocketFlags,
  2388. Fsm->m_nPort,
  2389. Fsm->m_dwTimeout
  2390. ));
  2391. PERF_ENTER(GetConnection);
  2392. BOOL bFound = FALSE;
  2393. DWORD error = ERROR_SUCCESS;
  2394. CFsm_GetConnection & fsm = *Fsm;
  2395. ICSocket * pSocket = NULL;
  2396. LPINTERNET_THREAD_INFO lpThreadInfo = fsm.GetThreadInfo();
  2397. HANDLE hEvent = NULL;
  2398. BOOL bUnlockList = TRUE;
  2399. BOOL bKeepAliveWaiters;
  2400. BOOL fExemptConnLimit = FALSE;
  2401. if (fsm.IsInvalid())
  2402. {
  2403. error = ERROR_INTERNET_OPERATION_CANCELLED;
  2404. goto quit;
  2405. }
  2406. INET_ASSERT(lpThreadInfo != NULL);
  2407. INET_ASSERT(lpThreadInfo->hObjectMapped != NULL);
  2408. INET_ASSERT(((HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)->
  2409. GetHandleType() == TypeHttpRequestHandle);
  2410. fExemptConnLimit = ((INTERNET_HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)->ConnLimitExempted();
  2411. if ((lpThreadInfo == NULL) || (lpThreadInfo->hObjectMapped == NULL)) {
  2412. error = ERROR_INTERNET_INTERNAL_ERROR;
  2413. goto quit;
  2414. }
  2415. BOOL bAsyncRequest;
  2416. bAsyncRequest = lpThreadInfo->IsAsyncWorkerThread
  2417. || ((INTERNET_HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)->
  2418. IsAsyncHandle();
  2419. *fsm.m_lplpSocket = NULL;
  2420. try_again:
  2421. bUnlockList = TRUE;
  2422. //
  2423. // use m_Waiters to serialize access. N.B. - we will acquire m_KeepAliveList
  2424. // from within m_Waiters
  2425. //
  2426. m_Waiters.Acquire();
  2427. if (IsNewLimit()) {
  2428. UpdateConnectionLimit();
  2429. }
  2430. bKeepAliveWaiters = KeepAliveWaiters();
  2431. if (fsm.m_dwSocketFlags & SF_KEEP_ALIVE) {
  2432. //
  2433. // maintain requester order - if there are already waiters then queue
  2434. // this request, else try to satisfy the requester. HOWEVER, only check
  2435. // for existing requesters the FIRST time through. If we're here with
  2436. // FSM_STATE_CONTINUE then we've been unblocked and we can ignore any
  2437. // waiters that came after us
  2438. //
  2439. if ((fsm.GetState() == FSM_STATE_CONTINUE) || !bKeepAliveWaiters) {
  2440. DEBUG_PRINT(SESSION,
  2441. INFO,
  2442. ("no current waiters for K-A connections\n"
  2443. ));
  2444. while (pSocket = FindKeepAliveConnection(fsm.m_dwSocketFlags,
  2445. fsm.m_nPort,
  2446. fsm.m_lpszSecureTunnelHost)) {
  2447. if (pSocket->IsReset() || pSocket->HasExpired()) {
  2448. DPRINTF("%#x: %#x: ********* socket %#x is closed already\n",
  2449. GetCurrentThreadId(),
  2450. Fsm,
  2451. pSocket->GetSocket()
  2452. );
  2453. DEBUG_PRINT(SESSION,
  2454. INFO,
  2455. ("K-A connection %#x [%#x/%d] is reset (%B) or expired (%B)\n",
  2456. pSocket,
  2457. pSocket->GetSocket(),
  2458. pSocket->GetSourcePort(),
  2459. pSocket->IsReset(),
  2460. pSocket->HasExpired()
  2461. ));
  2462. pSocket->SetLinger(FALSE, 0);
  2463. pSocket->Shutdown(2);
  2464. //dprintf("GetConnection: destroying reset socket %#x\n", pSocket->GetSocket());
  2465. pSocket->Destroy();
  2466. pSocket = NULL;
  2467. if (!UnlimitedConnections()) {
  2468. ++m_ConnectionsAvailable;
  2469. }
  2470. CHECK_CONNECTION_COUNT();
  2471. } else {
  2472. DPRINTF("%#x: %#x: *** matched %#x, %#x\n",
  2473. GetCurrentThreadId(),
  2474. Fsm,
  2475. pSocket->GetSocket(),
  2476. pSocket->GetFlags()
  2477. );
  2478. break;
  2479. }
  2480. }
  2481. if (pSocket == NULL) {
  2482. DEBUG_PRINT(SESSION,
  2483. INFO,
  2484. ("no available K-A connections\n"
  2485. ));
  2486. /*
  2487. //
  2488. // if all connections are in use as keep-alive connections then
  2489. // since we're here, we want a keep-alive connection that doesn't
  2490. // match the currently available keep-alive connections. Terminate
  2491. // the oldest keep-alive connection (at the head of the queue)
  2492. // and generate a new connection
  2493. //
  2494. LockSerializedList(&m_KeepAliveList);
  2495. if (ElementsOnSerializedList(&m_KeepAliveList) == m_ConnectionLimit) {
  2496. pSocket = ContainingICSocket(SlDequeueHead(&m_KeepAliveList));
  2497. pSocket->SetLinger(FALSE, 0);
  2498. pSocket->Shutdown(2);
  2499. pSocket->Destroy();
  2500. if (!UnlimitedConnections()) {
  2501. ++m_ConnectionsAvailable;
  2502. }
  2503. CHECK_CONNECTION_COUNT();
  2504. }
  2505. UnlockSerializedList(&m_KeepAliveList);
  2506. */
  2507. }
  2508. } else {
  2509. DEBUG_PRINT(SESSION,
  2510. INFO,
  2511. ("%d waiters for K-A connection to %q\n",
  2512. ElementsOnSerializedList(&m_KeepAliveList),
  2513. GetHostName()
  2514. ));
  2515. }
  2516. }
  2517. //
  2518. // if we found a matching keep-alive connection or we are not limiting
  2519. // connections then we're done
  2520. //
  2521. if ((pSocket != NULL) || UnlimitedConnections()) {
  2522. INET_ASSERT(error == ERROR_SUCCESS);
  2523. if (pSocket != NULL && fExemptConnLimit)
  2524. {
  2525. // Passport grabs a connection from the pool, so we need to bump up the availabilty to let
  2526. // others get one more. Effectively we excempt the passport connection
  2527. if (!UnlimitedConnections() /*&& !pSocket->ConnLimitExempted()*/)
  2528. {
  2529. // we convert a connection that is subjecting to connection limit to one that isn't,
  2530. // therefore, we need to up the availability by 1.
  2531. pSocket->ExemptConnLimit();
  2532. ++m_ConnectionsAvailable;
  2533. }
  2534. }
  2535. goto exit;
  2536. }
  2537. //
  2538. // no keep-alive connections matched, or there are already waiters for
  2539. // keep-alive connections
  2540. //
  2541. INET_ASSERT(m_ConnectionsAvailable <= m_ConnectionLimit);
  2542. if (m_ConnectionsAvailable > 0) {
  2543. if (fsm.m_lpszSecureTunnelHost)
  2544. goto exit; // don't create a connection here for SSL tunneling
  2545. //
  2546. // can create a connection
  2547. //
  2548. DEBUG_PRINT(SESSION,
  2549. INFO,
  2550. ("OK to create new connection\n"
  2551. ));
  2552. DPRINTF("%#x: %#x: *** %s OK to create connection %d/%d\n",
  2553. GetCurrentThreadId(),
  2554. Fsm,
  2555. GetHostName(),
  2556. m_ConnectionsAvailable,
  2557. m_ConnectionLimit
  2558. );
  2559. if (!fExemptConnLimit) // make this a freeby for passport connection
  2560. {
  2561. --m_ConnectionsAvailable;
  2562. }
  2563. } else if (fsm.GetElapsedTime() > fsm.m_dwTimeout) {
  2564. error = ERROR_INTERNET_TIMEOUT;
  2565. } else {
  2566. //
  2567. // if there are keep-alive connections but no keep-alive waiters
  2568. // then either we don't want a keep-alive connection, or the ones
  2569. // available don't match our requirements.
  2570. // If we need a connection of a different type - e.g. SSL when all
  2571. // we have is non-SSL then close a connection & generate a new one.
  2572. // If we need a non-keep-alive connection then its okay to return
  2573. // a current keep-alive connection, the understanding being that the
  2574. // caller will not add Connection: Keep-Alive header (HTTP 1.0) or
  2575. // will add Connection: Close header (HTTP 1.1)
  2576. //
  2577. //
  2578. // BUGBUG - what about waiters for non-keep-alive connections?
  2579. //
  2580. // scenario - limit of 1 connection:
  2581. //
  2582. // A. request for k-a
  2583. // continue & create connection
  2584. // B. request non-k-a
  2585. // none available; wait
  2586. // C. release k-a connection; unblock sync waiter B
  2587. // D. request non-k-a
  2588. // k-a available; return it; caller converts to non-k-a
  2589. // E. unblocked waiter B request non-k-a
  2590. // none available; wait
  2591. //
  2592. // If this situation continues, eventually B will time-out, whereas it
  2593. // could have had the connection taken by D. Request D is younger and
  2594. // therefore can afford to wait while B continues with the connection
  2595. //
  2596. BOOL fHaveConnection = FALSE;
  2597. if (!bKeepAliveWaiters || (fsm.GetState() == FSM_STATE_CONTINUE)) {
  2598. LockSerializedList(&m_KeepAliveList);
  2599. if (ElementsOnSerializedList(&m_KeepAliveList) != 0) {
  2600. pSocket = ContainingICSocket(SlDequeueHead(&m_KeepAliveList));
  2601. fHaveConnection = TRUE;
  2602. #define SOCK_FLAGS (SF_ENCRYPT | SF_DECRYPT | SF_SECURE | SF_TUNNEL)
  2603. DWORD dwSocketTypeFlags = pSocket->GetFlags() & SOCK_FLAGS;
  2604. DWORD dwRequestTypeFlags = fsm.m_dwSocketFlags & SOCK_FLAGS;
  2605. if ((dwSocketTypeFlags ^ dwRequestTypeFlags)
  2606. || (fsm.m_nPort != pSocket->GetPort())) {
  2607. DEBUG_PRINT(SESSION,
  2608. INFO,
  2609. ("different socket types (%#x, %#x) or ports (%d, %d) requested\n",
  2610. fsm.m_dwSocketFlags,
  2611. pSocket->GetFlags(),
  2612. fsm.m_nPort,
  2613. pSocket->GetPort()
  2614. ));
  2615. DPRINTF("%#x: %#x: *** closing socket %#x: %#x vs. %#x\n",
  2616. GetCurrentThreadId(),
  2617. Fsm,
  2618. pSocket->GetSocket(),
  2619. pSocket->GetFlags(),
  2620. fsm.m_dwSocketFlags
  2621. );
  2622. pSocket->SetLinger(FALSE, 0);
  2623. pSocket->Shutdown(2);
  2624. //dprintf("GetConnection: destroying different type socket %#x\n", pSocket->GetSocket());
  2625. pSocket->Destroy();
  2626. pSocket = NULL;
  2627. // If we were trying to wait for established SSL tunnel,
  2628. // but one wasn't found, then this connection is open
  2629. // for anyone.
  2630. if (!UnlimitedConnections() && fsm.m_lpszSecureTunnelHost) {
  2631. ++m_ConnectionsAvailable;
  2632. }
  2633. } else {
  2634. DPRINTF("%#x: %#x: *** returning k-a connection %#x as non-k-a\n",
  2635. GetCurrentThreadId(),
  2636. Fsm,
  2637. pSocket->GetSocket()
  2638. );
  2639. }
  2640. CHECK_CONNECTION_COUNT();
  2641. if (fExemptConnLimit)
  2642. {
  2643. if (!UnlimitedConnections() /*&& !pSocket->ConnLimitExempted()*/)
  2644. {
  2645. // we convert a connection that is subjecting to connection limit to one that isn't,
  2646. // therefore, we need to up the availability by 1.
  2647. if (pSocket)
  2648. {
  2649. pSocket->ExemptConnLimit();
  2650. }
  2651. ++m_ConnectionsAvailable;
  2652. }
  2653. }
  2654. }
  2655. UnlockSerializedList(&m_KeepAliveList);
  2656. if (fHaveConnection) {
  2657. goto exit;
  2658. }
  2659. }
  2660. DPRINTF("%#x: %#x: blocking %s FSM %#x state %s %d/%d\n",
  2661. GetCurrentThreadId(),
  2662. Fsm,
  2663. Fsm->MapType(),
  2664. Fsm,
  2665. Fsm->MapState(),
  2666. m_ConnectionsAvailable,
  2667. m_ConnectionLimit
  2668. );
  2669. //
  2670. // we have to wait for a connection to become available. If we are an
  2671. // async request then we queue this FSM & return the thread to the pool
  2672. // or, if app thread, return pending indication to the app. If this is
  2673. // a sync request (in an app thread) then we block on an event waiting
  2674. // for a connection to become available
  2675. //
  2676. if (fExemptConnLimit)
  2677. {
  2678. goto exit;
  2679. }
  2680. if (!bAsyncRequest) {
  2681. //
  2682. // create unnamed, initially unsignalled, auto-reset event
  2683. //
  2684. hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  2685. if (hEvent == NULL) {
  2686. error = GetLastError();
  2687. goto exit;
  2688. }
  2689. }
  2690. CConnectionWaiter * pWaiter;
  2691. #if INET_DEBUG
  2692. for (pWaiter = (CConnectionWaiter *)m_Waiters.Head();
  2693. pWaiter != (CConnectionWaiter *)m_Waiters.Self();
  2694. pWaiter = (CConnectionWaiter *)pWaiter->Next()) {
  2695. INET_ASSERT(pWaiter->Id() != (DWORD_PTR)(bAsyncRequest ? (DWORD_PTR)Fsm : lpThreadInfo->ThreadId));
  2696. }
  2697. #endif
  2698. pWaiter = new CConnectionWaiter(&m_Waiters,
  2699. !bAsyncRequest,
  2700. (fsm.m_dwSocketFlags & SF_KEEP_ALIVE)
  2701. ? TRUE
  2702. : FALSE,
  2703. bAsyncRequest
  2704. ? (DWORD_PTR)Fsm
  2705. : lpThreadInfo->ThreadId,
  2706. hEvent,
  2707. //
  2708. // priority in request handle object
  2709. // controls relative position in list
  2710. // of waiters
  2711. //
  2712. ((HTTP_REQUEST_HANDLE_OBJECT *)
  2713. lpThreadInfo->hObjectMapped)->
  2714. GetPriority()
  2715. );
  2716. DPRINTF("%#x: %#x: new waiter %#x: as=%B, K-A=%B, id=%#x, hE=%#x, pri=%d, sf=%#x, preq=%#x ssl=%s, url=%s\n",
  2717. GetCurrentThreadId(),
  2718. Fsm,
  2719. pWaiter,
  2720. bAsyncRequest,
  2721. (fsm.m_dwSocketFlags & SF_KEEP_ALIVE)
  2722. ? TRUE
  2723. : FALSE,
  2724. bAsyncRequest
  2725. ? (DWORD_PTR)Fsm
  2726. : lpThreadInfo->ThreadId,
  2727. hEvent,
  2728. ((HTTP_REQUEST_HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)->
  2729. GetPriority(),
  2730. fsm.m_dwSocketFlags,
  2731. ((HTTP_REQUEST_HANDLE_OBJECT *)lpThreadInfo->hObjectMapped),
  2732. fsm.m_lpszSecureTunnelHost ? fsm.m_lpszSecureTunnelHost : "",
  2733. ((HTTP_REQUEST_HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)->GetOriginalUrl()
  2734. );
  2735. if (pWaiter == NULL) {
  2736. error = ERROR_NOT_ENOUGH_MEMORY;
  2737. goto exit;
  2738. }
  2739. if (bAsyncRequest) {
  2740. //
  2741. // ensure that when the FSM is unblocked normally, the new state
  2742. // is STATE_CONTINUE
  2743. //
  2744. Fsm->SetState(FSM_STATE_CONTINUE);
  2745. error = BlockWorkItem(Fsm,
  2746. (DWORD_PTR)pWaiter,
  2747. fsm.m_dwTimeout
  2748. );
  2749. if (error == ERROR_SUCCESS) {
  2750. error = ERROR_IO_PENDING;
  2751. }
  2752. } else {
  2753. m_Waiters.Release();
  2754. bUnlockList = FALSE;
  2755. DPRINTF("%#x: %#x: %s FSM %#x %s waiting %d msec\n",
  2756. GetCurrentThreadId(),
  2757. Fsm,
  2758. Fsm->MapType(),
  2759. Fsm,
  2760. Fsm->MapState(),
  2761. fsm.m_dwTimeout
  2762. );
  2763. DWORD dwWaitTime = fsm.m_dwTimeout - fsm.GetElapsedTime();
  2764. if ((int)dwWaitTime <= 0) {
  2765. DEBUG_PRINT(SESSION,
  2766. ERROR,
  2767. ("SYNC wait timed out (%d mSec)\n",
  2768. fsm.m_dwTimeout
  2769. ));
  2770. error = ERROR_INTERNET_TIMEOUT;
  2771. } else {
  2772. DEBUG_PRINT(SESSION,
  2773. INFO,
  2774. ("waiting %d mSec for SYNC event %#x\n",
  2775. dwWaitTime,
  2776. hEvent
  2777. ));
  2778. //
  2779. // we'd better not be doing a sync wait if we are in the
  2780. // context of an app thread making an async request
  2781. //
  2782. INET_ASSERT(lpThreadInfo->IsAsyncWorkerThread
  2783. || !((INTERNET_HANDLE_OBJECT *)lpThreadInfo->
  2784. hObjectMapped)->IsAsyncHandle());
  2785. //INET_ASSERT(dwWaitTime <= 60000);
  2786. error = WaitForSingleObject(hEvent, dwWaitTime);
  2787. DPRINTF("%#x: %#x: sync waiter unblocked - error = %d\n",
  2788. GetCurrentThreadId(),
  2789. Fsm,
  2790. error
  2791. );
  2792. }
  2793. if (error == STATUS_TIMEOUT) {
  2794. DPRINTF("%#x: %#x: %s: %d+%d/%d: timed out %#x (%s FSM %#x %s)\n",
  2795. GetCurrentThreadId(),
  2796. Fsm,
  2797. GetHostName(),
  2798. m_ConnectionsAvailable,
  2799. ElementsOnSerializedList(&m_KeepAliveList),
  2800. m_ConnectionLimit,
  2801. GetCurrentThreadId(),
  2802. Fsm->MapType(),
  2803. Fsm,
  2804. Fsm->MapState()
  2805. );
  2806. RemoveWaiter(lpThreadInfo->ThreadId);
  2807. error = ERROR_INTERNET_TIMEOUT;
  2808. }
  2809. BOOL bOk;
  2810. bOk = CloseHandle(hEvent);
  2811. INET_ASSERT(bOk);
  2812. if (error == WAIT_OBJECT_0) {
  2813. DPRINTF("%#x: %#x: sync requester trying again\n",
  2814. GetCurrentThreadId(),
  2815. Fsm
  2816. );
  2817. fsm.SetState(FSM_STATE_CONTINUE);
  2818. goto try_again;
  2819. }
  2820. }
  2821. }
  2822. exit:
  2823. //
  2824. // if we are returning a (keep-alive) socket that has a different blocking
  2825. // mode from that requested, change it
  2826. //
  2827. if (pSocket != NULL) {
  2828. if ((pSocket->GetFlags() & SF_NON_BLOCKING)
  2829. ^ (fsm.m_dwSocketFlags & SF_NON_BLOCKING)) {
  2830. DEBUG_PRINT(SESSION,
  2831. INFO,
  2832. ("different blocking modes requested: %#x, %#x\n",
  2833. fsm.m_dwSocketFlags,
  2834. pSocket->GetFlags()
  2835. ));
  2836. DPRINTF("%#x: %#x: *** changing socket %#x to %sBLOCKING\n",
  2837. GetCurrentThreadId(),
  2838. Fsm,
  2839. pSocket->GetSocket(),
  2840. fsm.m_dwSocketFlags & SF_NON_BLOCKING ? "NON-" : ""
  2841. );
  2842. if (!(GlobalRunningNovellClient32 && !GlobalNonBlockingClient32)) {
  2843. pSocket->SetNonBlockingMode(fsm.m_dwSocketFlags & SF_NON_BLOCKING);
  2844. }
  2845. }
  2846. *fsm.m_lplpSocket = pSocket;
  2847. }
  2848. if (bUnlockList) {
  2849. m_Waiters.Release();
  2850. }
  2851. quit:
  2852. if (error != ERROR_IO_PENDING) {
  2853. fsm.SetDone();
  2854. }
  2855. DPRINTF("%#x: %#x: %s: %d+%d/%d: get: %d, %#x, %d\n",
  2856. GetCurrentThreadId(),
  2857. Fsm,
  2858. GetHostName(),
  2859. m_ConnectionsAvailable,
  2860. ElementsOnSerializedList(&m_KeepAliveList),
  2861. m_ConnectionLimit,
  2862. error,
  2863. pSocket ? pSocket->GetSocket() : 0,
  2864. m_Waiters.Count()
  2865. );
  2866. PERF_LEAVE(GetConnection);
  2867. DEBUG_LEAVE(error);
  2868. return error;
  2869. }
  2870. DWORD
  2871. CServerInfo::ReleaseConnection(
  2872. IN ICSocket * lpSocket OPTIONAL,
  2873. IN BOOL fExemptConnLimit
  2874. )
  2875. /*++
  2876. Routine Description:
  2877. Returns a keep-alive connection to the pool, or allows another requester to
  2878. create a connection
  2879. Arguments:
  2880. lpSocket - pointer to ICSocket if we are returning a keep-alive connection
  2881. Return Value:
  2882. DWORD
  2883. Success - ERROR_SUCCESS
  2884. Failure -
  2885. --*/
  2886. {
  2887. DEBUG_ENTER((DBG_SESSION,
  2888. Dword,
  2889. "CServerInfo::ReleaseConnection",
  2890. "{%q [%d+%d/%d]} %#x [%#x]",
  2891. GetHostName(),
  2892. AvailableConnections(),
  2893. KeepAliveConnections(),
  2894. ConnectionLimit(),
  2895. lpSocket,
  2896. lpSocket ? lpSocket->GetSocket() : 0
  2897. ));
  2898. PERF_ENTER(ReleaseConnection);
  2899. DWORD error = ERROR_SUCCESS;
  2900. BOOL bRelease = FALSE;
  2901. m_Waiters.Acquire();
  2902. //
  2903. // quite often (at least with catapult proxy based on IIS) the server may
  2904. // drop the connection even though it indicated it would keep it open. This
  2905. // typically happens on 304 (frequent) and 302 (less so) responses. If we
  2906. // determine the server has dropped the connection then throw it away and
  2907. // allow the app to create a new one
  2908. //
  2909. if (lpSocket != NULL) {
  2910. if (lpSocket->IsClosed() || lpSocket->IsReset()) {
  2911. DEBUG_PRINT(SESSION,
  2912. INFO,
  2913. ("socket %#x already dead - throwing it out\n",
  2914. lpSocket->GetSocket()
  2915. ));
  2916. DPRINTF("%#x: socket %#x: already reset\n",
  2917. GetCurrentThreadId(),
  2918. lpSocket->GetSocket()
  2919. );
  2920. //dprintf("ReleaseConnection: destroying already closed socket %#x\n", lpSocket->GetSocket());
  2921. BOOL bDestroyed = lpSocket->Dereference();
  2922. INET_ASSERT(bDestroyed);
  2923. lpSocket = NULL;
  2924. } else {
  2925. //
  2926. // if we are returning a keep-alive socket, put it in non-blocking
  2927. // mode if not already. Typically, Internet Explorer uses non-blocking
  2928. // sockets. In the infrequent cases where we want a blocking socket
  2929. // - mainly when doing java downloads - we will convert the socket
  2930. // to blocking mode when we get it from the pool
  2931. //
  2932. if (!lpSocket->IsNonBlocking()) {
  2933. DPRINTF("%#x: ***** WARNING: releasing BLOCKING k-a socket %#x\n",
  2934. GetCurrentThreadId(),
  2935. lpSocket->GetSocket()
  2936. );
  2937. if (!(GlobalRunningNovellClient32 && !GlobalNonBlockingClient32)) {
  2938. lpSocket->SetNonBlockingMode(TRUE);
  2939. }
  2940. }
  2941. }
  2942. }
  2943. if (lpSocket != NULL) {
  2944. DPRINTF("%#x: releasing K-A %#x (%d+%d/%d)\n",
  2945. GetCurrentThreadId(),
  2946. lpSocket ? lpSocket->GetSocket() : 0,
  2947. AvailableConnections(),
  2948. KeepAliveConnections(),
  2949. ConnectionLimit()
  2950. );
  2951. INET_ASSERT(lpSocket->IsOpen());
  2952. INET_ASSERT(!lpSocket->IsOnList());
  2953. //INET_ASSERT(!lpSocket->IsReset());
  2954. lpSocket->SetKeepAlive();
  2955. DEBUG_PRINT(SESSION,
  2956. INFO,
  2957. ("releasing keep-alive socket %#x\n",
  2958. lpSocket->GetSocket()
  2959. ));
  2960. lpSocket->SetExpiryTime(GlobalKeepAliveSocketTimeout);
  2961. INET_ASSERT(!IsOnSerializedList(&m_KeepAliveList, lpSocket->List()));
  2962. InsertAtTailOfSerializedList(&m_KeepAliveList, lpSocket->List());
  2963. if (lpSocket->ConnLimitExempted())
  2964. {
  2965. lpSocket->ResetExemptFlag();
  2966. --m_ConnectionsAvailable;
  2967. if (KeepAliveConnections() > ConnectionLimit())
  2968. {
  2969. INET_ASSERT(KeepAliveConnections() - ConnectionLimit() == 1);
  2970. ICSocket* pOldestSocket = ContainingICSocket(SlDequeueHead(&m_KeepAliveList));
  2971. pOldestSocket->SetLinger(FALSE, 0);
  2972. pOldestSocket->Shutdown(2);
  2973. pOldestSocket->Destroy();
  2974. pOldestSocket = NULL;
  2975. }
  2976. }
  2977. lpSocket = NULL;
  2978. INET_ASSERT(UnlimitedConnections()
  2979. ? TRUE
  2980. : (KeepAliveConnections() <= ConnectionLimit()));
  2981. bRelease = TRUE;
  2982. } else {
  2983. DPRINTF("%#x: releasing connection (%d+%d/%d)\n",
  2984. GetCurrentThreadId(),
  2985. AvailableConnections(),
  2986. KeepAliveConnections(),
  2987. ConnectionLimit()
  2988. );
  2989. if (!UnlimitedConnections() && !fExemptConnLimit) {
  2990. ++m_ConnectionsAvailable;
  2991. }
  2992. CHECK_CONNECTION_COUNT();
  2993. bRelease = TRUE;
  2994. }
  2995. if (bRelease && !UnlimitedConnections() && !fExemptConnLimit) {
  2996. CHECK_CONNECTION_COUNT();
  2997. CConnectionWaiter * pWaiter = (CConnectionWaiter *)m_Waiters.RemoveHead();
  2998. if (pWaiter != NULL) {
  2999. DEBUG_PRINT(SESSION,
  3000. INFO,
  3001. ("unblocking %s waiter %#x, pri=%d\n",
  3002. pWaiter->IsSync() ? "SYNC" : "ASYNC",
  3003. pWaiter->Id(),
  3004. pWaiter->GetPriority()
  3005. ));
  3006. DPRINTF("%#x: Unblocking %s connection waiter %#x, pri=%d\n",
  3007. GetCurrentThreadId(),
  3008. pWaiter->IsSync() ? "Sync" : "Async",
  3009. pWaiter->Id(),
  3010. pWaiter->GetPriority()
  3011. );
  3012. if (pWaiter->IsSync()) {
  3013. pWaiter->Signal();
  3014. } else {
  3015. DWORD n = UnblockWorkItems(1, (DWORD_PTR)pWaiter, ERROR_SUCCESS);
  3016. //INET_ASSERT(n == 1);
  3017. }
  3018. delete pWaiter;
  3019. } else {
  3020. DEBUG_PRINT(SESSION,
  3021. INFO,
  3022. ("no waiters\n"
  3023. ));
  3024. DPRINTF("%#x: !!! NOT unblocking connection waiter\n",
  3025. GetCurrentThreadId()
  3026. );
  3027. }
  3028. } else {
  3029. DPRINTF("%#x: !!! NOT releasing or unlimited?\n",
  3030. GetCurrentThreadId()
  3031. );
  3032. DEBUG_PRINT(SESSION,
  3033. INFO,
  3034. ("bRelease = %B, UnlimitedConnections() = %B\n",
  3035. bRelease,
  3036. UnlimitedConnections()
  3037. ));
  3038. }
  3039. DEBUG_PRINT(SESSION,
  3040. INFO,
  3041. ("avail+k-a/limit = %d+%d/%d\n",
  3042. AvailableConnections(),
  3043. KeepAliveConnections(),
  3044. ConnectionLimit()
  3045. ));
  3046. if (IsNewLimit()) {
  3047. UpdateConnectionLimit();
  3048. }
  3049. m_Waiters.Release();
  3050. PERF_LEAVE(ReleaseConnection);
  3051. DEBUG_LEAVE(error);
  3052. DPRINTF("%#x: %s: %d+%d/%d: rls %#x: %d, %d\n",
  3053. GetCurrentThreadId(),
  3054. GetHostName(),
  3055. AvailableConnections(),
  3056. KeepAliveConnections(),
  3057. ConnectionLimit(),
  3058. lpSocket ? lpSocket->GetSocket() : 0,
  3059. error,
  3060. m_Waiters.Count()
  3061. );
  3062. return error;
  3063. }
  3064. #endif // NEW_CONNECTION_SCHEME
  3065. VOID
  3066. CServerInfo::RemoveWaiter(
  3067. IN DWORD_PTR dwId
  3068. )
  3069. /*++
  3070. Routine Description:
  3071. Removes a CConnectionWaiter corresponding to the FSM
  3072. Arguments:
  3073. dwId - waiter id to match
  3074. Return Value:
  3075. None.
  3076. --*/
  3077. {
  3078. DEBUG_ENTER((DBG_SESSION,
  3079. None,
  3080. "CServerInfo::RemoveWaiter",
  3081. "%#x",
  3082. dwId
  3083. ));
  3084. m_Waiters.Acquire();
  3085. CConnectionWaiter * pWaiter;
  3086. BOOL found = FALSE;
  3087. for (pWaiter = (CConnectionWaiter *)m_Waiters.Head();
  3088. pWaiter != (CConnectionWaiter *)m_Waiters.Self();
  3089. pWaiter = (CConnectionWaiter *)pWaiter->Next()) {
  3090. if (pWaiter->Id() == dwId) {
  3091. m_Waiters.Remove((CPriorityListEntry *)pWaiter);
  3092. delete pWaiter;
  3093. found = TRUE;
  3094. break;
  3095. }
  3096. }
  3097. m_Waiters.Release();
  3098. //INET_ASSERT(found);
  3099. DEBUG_LEAVE(0);
  3100. }
  3101. //
  3102. // private CServerInfo methods
  3103. //
  3104. ICSocket *
  3105. CServerInfo::FindKeepAliveConnection(
  3106. IN DWORD dwSocketFlags,
  3107. IN INTERNET_PORT nPort,
  3108. IN LPSTR pszTunnelServer
  3109. )
  3110. /*++
  3111. Routine Description:
  3112. Find a keep-alive connection with the requested attributes and port number
  3113. Arguments:
  3114. dwSocketFlags - socket type flags (e.g. SF_SECURE)
  3115. nPort - port to server
  3116. pszTunnelServer - hostname of server through SSL tunnel, or
  3117. NULL if not checked.
  3118. Return Value:
  3119. ICSocket *
  3120. --*/
  3121. {
  3122. DPRINTF("%#x: *** looking for K-A connection\n", GetCurrentThreadId());
  3123. DEBUG_ENTER((DBG_SESSION,
  3124. Pointer,
  3125. "CServerInfo::FindKeepAliveConnection",
  3126. "{%q} %#x, %d",
  3127. GetHostName(),
  3128. dwSocketFlags,
  3129. nPort
  3130. ));
  3131. ICSocket * pSocket = NULL;
  3132. BOOL bFound = FALSE;
  3133. //
  3134. // don't check whether socket is non-blocking - we only really want to match
  3135. // on secure/non-secure. Possible flags to check on are:
  3136. //
  3137. // SF_ENCRYPT - should be subsumed by SF_SECURE
  3138. // SF_DECRYPT - should be subsumed by SF_SECURE
  3139. // SF_NON_BLOCKING - this isn't criterion for match
  3140. // SF_CONNECTIONLESS - not implemented?
  3141. // SF_AUTHORIZED - must be set if authorized & in pool
  3142. // SF_SECURE - opened for SSL/PCT if set
  3143. // SF_KEEP_ALIVE - must be set
  3144. // SF_TUNNEL - must be set if we're looking for a CONNECT tunnel to proxy
  3145. //
  3146. dwSocketFlags &= ~SF_NON_BLOCKING;
  3147. LockSerializedList(&m_KeepAliveList);
  3148. PLIST_ENTRY pEntry;
  3149. for (pEntry = HeadOfSerializedList(&m_KeepAliveList);
  3150. pEntry != (PLIST_ENTRY)SlSelf(&m_KeepAliveList);
  3151. pEntry = pEntry->Flink) {
  3152. pSocket = ContainingICSocket(pEntry);
  3153. INET_ASSERT(pSocket->IsKeepAlive());
  3154. //
  3155. // We make sure the socket we request is the correct socket,
  3156. // Match() is a bit confusing and needs a bit of explaining,
  3157. // Match IS NOT AN EXACT MATCH, it mearly checks to make sure
  3158. // that the requesting flags (dwSocketFlags) are found in the
  3159. // socket flags. So this can lead to a secure socket being returned
  3160. // on a non-secure open request, now realistically this doesn't happen
  3161. // because of the port number. But in the case of tunnelling this may be
  3162. // an issue, so we add an additional check to make sure that we only
  3163. // get a tunneled socket to a proxy if we specifically request one.
  3164. //
  3165. if (pSocket->Match(dwSocketFlags)
  3166. && (pSocket->GetPort() == nPort)
  3167. && pSocket->MatchTunnelSemantics(dwSocketFlags, pszTunnelServer)) {
  3168. RemoveFromSerializedList(&m_KeepAliveList, pSocket->List());
  3169. INET_ASSERT(!IsOnSerializedList(&m_KeepAliveList, pSocket->List()));
  3170. bFound = TRUE;
  3171. DEBUG_PRINT(SESSION,
  3172. INFO,
  3173. ("returning keep-alive socket %#x\n",
  3174. pSocket->GetSocket()
  3175. ));
  3176. DPRINTF("%#x: *** %s keep-alive connection %#x (%d/%d), wantf=%#x, gotf=%#x\n",
  3177. GetCurrentThreadId(),
  3178. GetHostName(),
  3179. pSocket->GetSocket(),
  3180. AvailableConnections(),
  3181. ConnectionLimit(),
  3182. dwSocketFlags,
  3183. pSocket->GetFlags()
  3184. );
  3185. break;
  3186. }
  3187. }
  3188. UnlockSerializedList(&m_KeepAliveList);
  3189. if (!bFound) {
  3190. pSocket = NULL;
  3191. }
  3192. DEBUG_LEAVE(pSocket);
  3193. return pSocket;
  3194. }
  3195. BOOL
  3196. CServerInfo::KeepAliveWaiters(
  3197. VOID
  3198. )
  3199. /*++
  3200. Routine Description:
  3201. Determine if any of the waiters on the list are for keep-alive connections
  3202. Arguments:
  3203. None.
  3204. Return Value:
  3205. BOOL
  3206. --*/
  3207. {
  3208. DEBUG_ENTER((DBG_SESSION,
  3209. Bool,
  3210. "CServerInfo::KeepAliveWaiters",
  3211. NULL
  3212. ));
  3213. BOOL found = FALSE;
  3214. CConnectionWaiter * pWaiter;
  3215. m_Waiters.Acquire();
  3216. for (pWaiter = (CConnectionWaiter *)m_Waiters.Head();
  3217. pWaiter != (CConnectionWaiter *)m_Waiters.Self();
  3218. pWaiter = (CConnectionWaiter *)pWaiter->Next()) {
  3219. if (pWaiter->IsKeepAlive()) {
  3220. found = TRUE;
  3221. break;
  3222. }
  3223. }
  3224. m_Waiters.Release();
  3225. DEBUG_LEAVE(found);
  3226. return found;
  3227. }
  3228. #ifdef NEW_CONNECTION_SCHEME
  3229. //
  3230. //
  3231. //BOOL
  3232. //CServerInfo::RunOutOfConnections(
  3233. // VOID
  3234. // )
  3235. //
  3236. ///*++
  3237. //
  3238. //Routine Description:
  3239. //
  3240. // Determines whether we have been run out of connections. Criteria for being
  3241. // out of connections is: no available connections or keep-alive connections
  3242. // and no recorded connection activity within GlobalConnectionInactiveTimeout
  3243. //
  3244. //Arguments:
  3245. //
  3246. // None.
  3247. //
  3248. //Return Value:
  3249. //
  3250. // BOOL
  3251. // TRUE - we are out of connections
  3252. //
  3253. // FALSE - not
  3254. //
  3255. //--*/
  3256. //
  3257. //{
  3258. // DEBUG_ENTER((DBG_SESSION,
  3259. // Bool,
  3260. // "CServerInfo::RunOutOfConnections",
  3261. // NULL
  3262. // ));
  3263. //
  3264. // BOOL bOut = FALSE;
  3265. //
  3266. // if ((TotalAvailableConnections() == 0)
  3267. // && (GetLastActiveTime() != 0)
  3268. // && !ConnectionActivity()) {
  3269. //
  3270. // DPRINTF("%#x: >>>>>>>> Run out of connections. Last activity %d mSec ago. Create new\n",
  3271. // GetCurrentThreadId(),
  3272. // (GetLastActiveTime() == 0)
  3273. // ? -1
  3274. // : (GetTickCount() - GetLastActiveTime())
  3275. // );
  3276. //
  3277. // INET_ASSERT(!UnlimitedConnections());
  3278. //
  3279. // DEBUG_PRINT(SESSION,
  3280. // WARNING,
  3281. // ("out of connections! Last activity %d mSec ago. Creating new\n",
  3282. // (GetLastActiveTime() == 0)
  3283. // ? -1
  3284. // : (GetTickCount() - GetLastActiveTime())
  3285. // ));
  3286. //
  3287. // bOut = TRUE;
  3288. // } else if (TotalAvailableConnections() == 0) {
  3289. //
  3290. // DPRINTF("%#x: no connections, last activity %d mSec ago\n",
  3291. // GetCurrentThreadId(),
  3292. // (GetLastActiveTime() == 0)
  3293. // ? -1
  3294. // : (GetTickCount() - GetLastActiveTime())
  3295. // );
  3296. //
  3297. // DEBUG_PRINT(SESSION,
  3298. // INFO,
  3299. // ("no connections, last activity %d mSec ago\n",
  3300. // (GetLastActiveTime() == 0)
  3301. // ? -1
  3302. // : (GetTickCount() - GetLastActiveTime())
  3303. // ));
  3304. //
  3305. // }
  3306. //
  3307. // DEBUG_LEAVE(bOut);
  3308. //
  3309. // return bOut;
  3310. //}
  3311. //
  3312. #endif // NEW_CONNECTION_SCHEME
  3313. VOID
  3314. CServerInfo::UpdateConnectionLimit(
  3315. VOID
  3316. )
  3317. /*++
  3318. Routine Description:
  3319. Change connection limit to new limit
  3320. Assumes: 1. Caller has acquired this object before calling this function
  3321. Arguments:
  3322. None.
  3323. Return Value:
  3324. None.
  3325. --*/
  3326. {
  3327. DEBUG_ENTER((DBG_SESSION,
  3328. None,
  3329. "CServerInfo::UpdateConnectionLimit",
  3330. "{%q: %d=>%d (%d+%d)}",
  3331. GetHostName(),
  3332. ConnectionLimit(),
  3333. GetNewLimit(),
  3334. AvailableConnections(),
  3335. KeepAliveConnections()
  3336. ));
  3337. LONG difference = GetNewLimit() - ConnectionLimit();
  3338. //
  3339. // BUGBUG - only handling increases in limit for now
  3340. //
  3341. INET_ASSERT(difference > 0);
  3342. if (difference > 0) {
  3343. m_ConnectionsAvailable += difference;
  3344. }
  3345. m_ConnectionLimit = m_NewLimit;
  3346. DEBUG_PRINT(SESSION,
  3347. INFO,
  3348. ("%q: new: %d+%d/%d\n",
  3349. GetHostName(),
  3350. AvailableConnections(),
  3351. KeepAliveConnections(),
  3352. ConnectionLimit()
  3353. ));
  3354. DEBUG_LEAVE(0);
  3355. }
  3356. VOID
  3357. CServerInfo::PurgeKeepAlives(
  3358. IN DWORD dwForce
  3359. )
  3360. /*++
  3361. Routine Description:
  3362. Purges any timed-out keep-alive connections
  3363. Arguments:
  3364. dwForce - force to apply when purging. Value can be:
  3365. PKA_NO_FORCE - only purge timed-out sockets or sockets in
  3366. close-wait state (default)
  3367. PKA_NOW - purge all sockets
  3368. PKA_AUTH_FAILED - purge sockets that have been marked as failing
  3369. authentication
  3370. Return Value:
  3371. None.
  3372. --*/
  3373. {
  3374. //dprintf("%#x PurgeKeepAlives(%d)\n", GetCurrentThreadId(), dwForce);
  3375. DEBUG_ENTER((DBG_SESSION,
  3376. None,
  3377. "CServerInfo::PurgeKeepAlives",
  3378. "{%q [ref=%d, k-a=%d]} %s [%d]",
  3379. GetHostName(),
  3380. ReferenceCount(),
  3381. KeepAliveConnections(),
  3382. (dwForce == PKA_NO_FORCE) ? "NO_FORCE"
  3383. : (dwForce == PKA_NOW) ? "NOW"
  3384. : (dwForce == PKA_AUTH_FAILED) ? "AUTH_FAILED"
  3385. : "?",
  3386. dwForce
  3387. ));
  3388. if (IsKeepAliveListInitialized()) {
  3389. INET_ASSERT(ReferenceCount() >= 1);
  3390. m_Waiters.Acquire();
  3391. LockSerializedList(&m_KeepAliveList);
  3392. PLIST_ENTRY last = (PLIST_ENTRY)SlSelf(&m_KeepAliveList);
  3393. DWORD ticks = GetTickCountWrap();
  3394. for (PLIST_ENTRY pEntry = HeadOfSerializedList(&m_KeepAliveList);
  3395. pEntry != (PLIST_ENTRY)SlSelf(&m_KeepAliveList);
  3396. pEntry = last->Flink) {
  3397. ICSocket * pSocket = ContainingICSocket(pEntry);
  3398. BOOL bDelete;
  3399. if (pSocket->IsReset()) {
  3400. //dprintf("%q: socket %#x/%d CLOSE-WAIT\n", GetHostName(), pSocket->GetSocket(), pSocket->GetSourcePort());
  3401. bDelete = TRUE;
  3402. } else if (dwForce == PKA_NO_FORCE) {
  3403. bDelete = pSocket->HasExpired(ticks);
  3404. } else if (dwForce == PKA_NOW) {
  3405. bDelete = TRUE;
  3406. } else if (dwForce == PKA_AUTH_FAILED) {
  3407. bDelete = pSocket->IsAuthorized();
  3408. }
  3409. if (bDelete) {
  3410. //dprintf("%q: socket %#x/%d. Close-Wait=%B, Expired=%B, Now=%B, Auth=%B\n",
  3411. // GetHostName(),
  3412. // pSocket->GetSocket(),
  3413. // pSocket->GetSourcePort(),
  3414. // pSocket->IsReset(),
  3415. // pSocket->HasExpired(ticks),
  3416. // (dwForce == PKA_NOW),
  3417. // (dwForce == PKA_AUTH_FAILED) && pSocket->IsAuthorized()
  3418. // );
  3419. DEBUG_PRINT(SESSION,
  3420. INFO,
  3421. ("purging keep-alive socket %#x/%d: Close-Wait=%B, Expired=%B, Now=%B, Auth=%B\n",
  3422. pSocket->GetSocket(),
  3423. pSocket->GetSourcePort(),
  3424. pSocket->IsReset(),
  3425. pSocket->HasExpired(ticks),
  3426. (dwForce == PKA_NOW),
  3427. (dwForce == PKA_AUTH_FAILED) && pSocket->IsAuthorized()
  3428. ));
  3429. RemoveFromSerializedList(&m_KeepAliveList, pEntry);
  3430. BOOL bDestroyed;
  3431. bDestroyed = pSocket->Dereference();
  3432. INET_ASSERT(bDestroyed);
  3433. if (!UnlimitedConnections()) {
  3434. ++m_ConnectionsAvailable;
  3435. INET_ASSERT(m_ConnectionsAvailable <= m_ConnectionLimit);
  3436. }
  3437. } else {
  3438. DEBUG_PRINT(SESSION,
  3439. INFO,
  3440. ("socket %#x/%d expires in %d mSec\n",
  3441. pSocket->GetSocket(),
  3442. pSocket->GetSourcePort(),
  3443. pSocket->GetExpiryTime() - ticks
  3444. ));
  3445. last = pEntry;
  3446. }
  3447. }
  3448. UnlockSerializedList(&m_KeepAliveList);
  3449. m_Waiters.Release();
  3450. }
  3451. DEBUG_LEAVE(0);
  3452. }
  3453. //
  3454. // friend functions
  3455. //
  3456. CServerInfo *
  3457. ContainingServerInfo(
  3458. IN LPVOID lpAddress
  3459. )
  3460. /*++
  3461. Routine Description:
  3462. Returns address of CServerInfo given address of m_List
  3463. Arguments:
  3464. lpAddress - address of m_List
  3465. Return Value:
  3466. CServerInfo *
  3467. --*/
  3468. {
  3469. return CONTAINING_RECORD(lpAddress, CServerInfo, m_List);
  3470. }