Source code of Windows XP (NT5)
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.

726 lines
22 KiB

  1. /*--
  2. Copyright (c) 1995-1998 Microsoft Corporation
  3. Module Name: ISAPI.CPP
  4. Author: Arul Menezes
  5. Abstract: ISAPI handling code
  6. --*/
  7. #include "pch.h"
  8. #pragma hdrstop
  9. #include "httpd.h"
  10. #define RK_OLE L"SOFTWARE\\Microsoft\\Ole"
  11. #define RK_FREETIMEOUT L"FreeTimeout"
  12. // Amount of time ISAPI cache timeout thread should sleep between firing.
  13. #define CACHE_SLEEP_TIMEOUT 60000
  14. BOOL InitExtensions(CISAPICache **ppISAPICache, DWORD *pdwCacheSleep)
  15. {
  16. CReg reg(HKEY_LOCAL_MACHINE, RK_OLE);
  17. // We double the amount of time COM tells us it's using to be safe.
  18. *pdwCacheSleep = 3*reg.ValueDW(RK_FREETIMEOUT,600000); // default is 30 minutes, 1800000
  19. *ppISAPICache = new CISAPICache();
  20. return(NULL != *ppISAPICache);
  21. }
  22. BOOL CHttpRequest::ExecuteISAPI(void)
  23. {
  24. DWORD dwRet = HSE_STATUS_ERROR;
  25. EXTENSION_CONTROL_BLOCK ECB;
  26. CISAPI *pISAPIDLL = NULL;
  27. // wrap all calls to the ISAPI in a _try--_except
  28. __try
  29. {
  30. if (! g_pVars->m_pISAPICache->Load(m_wszPath,&pISAPIDLL))
  31. return FALSE;
  32. // create an ECB (this allocates no memory)
  33. FillECB(&ECB);
  34. DEBUGCHK(m_hEvent == NULL);
  35. m_hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
  36. m_pECB = &ECB;
  37. // call the ISAPI
  38. dwRet = pISAPIDLL->CallExtension(&ECB);
  39. // grab log data if any
  40. if (ECB.lpszLogData[0])
  41. {
  42. DEBUGCHK(!m_pszLogParam);
  43. m_pszLogParam = MySzDupA(ECB.lpszLogData);
  44. }
  45. }
  46. __except(1) // catch all exceptions
  47. {
  48. TraceTag(ttidWebServer, "ISAPI DLL caused exception 0x%08x and was terminated", GetExceptionCode());
  49. g_pVars->m_pLog->WriteEvent(IDS_HTTPD_EXT_EXCEPTION,m_wszPath,GetExceptionCode(),L"HttpExtensionProc",GetLastError());
  50. }
  51. // set keep-alive status based on return code
  52. m_fKeepAlive = (dwRet==HSE_STATUS_SUCCESS_AND_KEEP_CONN);
  53. TraceTag(ttidWebServer, "ISAPI ExtProc returned %d keep=%d", dwRet, m_fKeepAlive);
  54. if (dwRet == HSE_STATUS_PENDING)
  55. {
  56. DWORD dwRes = WAIT_ABANDONED;
  57. // Wait up to 5 minutes for the request to complete
  58. if (m_hEvent != NULL)
  59. dwRes = WaitForSingleObject (m_hEvent, 300000);
  60. AssertSz(dwRes == WAIT_OBJECT_0, "Pended request was timed out after 5 minutes");
  61. if (dwRes == WAIT_OBJECT_0)
  62. dwRet = m_dwStatus;
  63. else
  64. dwRet = HSE_STATUS_ERROR;
  65. }
  66. if (m_hEvent != NULL)
  67. {
  68. CloseHandle (m_hEvent);
  69. m_hEvent = NULL;
  70. }
  71. StartRemoveISAPICacheIfNeeded();
  72. g_pVars->m_pISAPICache->Unload(pISAPIDLL);
  73. return(dwRet!=HSE_STATUS_ERROR);
  74. }
  75. void CHttpRequest::FillECB(LPEXTENSION_CONTROL_BLOCK pECB)
  76. {
  77. ZEROMEM(pECB);
  78. pECB->cbSize = sizeof(*pECB);
  79. pECB->dwVersion = HSE_VERSION;
  80. pECB->ConnID = (HCONN)this;
  81. DEBUGCHK(m_pszMethod);
  82. // BUGBUG 13244: IIS examines dwHttpStatusCode if user doesn't send their own headers
  83. // and uses it. However, there's a work around for this on CE (direct use to
  84. // ServerSupportFunction) and using the status code rather than m_rs would
  85. // be a rearchitecting problem. Fix: None for now.
  86. pECB->dwHttpStatusCode = 200;
  87. pECB->lpszMethod = m_pszMethod;
  88. pECB->lpszQueryString = (PSTR)(m_pszQueryString ? m_pszQueryString : cszEmpty);
  89. pECB->lpszContentType = (PSTR)(m_pszContentType ? m_pszContentType : cszEmpty);
  90. pECB->lpszPathInfo = (PSTR) (m_pszPathInfo ? m_pszPathInfo : cszEmpty);
  91. pECB->lpszPathTranslated = (PSTR) (m_pszPathTranslated ? m_pszPathTranslated : cszEmpty);
  92. pECB->cbTotalBytes = m_dwContentLength;
  93. pECB->cbAvailable = m_bufRequest.Count();
  94. pECB->lpbData = m_bufRequest.Data();
  95. pECB->GetServerVariable = ::GetServerVariable;
  96. pECB->WriteClient = ::WriteClient;
  97. pECB->ReadClient = ::ReadClient;
  98. pECB->ServerSupportFunction = ::ServerSupportFunction;
  99. }
  100. BOOL WINAPI GetServerVariable(HCONN hConn, PSTR psz, PVOID pv, PDWORD pdw)
  101. {
  102. CHECKHCONN(hConn);
  103. CHECKPTRS2(psz, pdw);
  104. return((CHttpRequest*)hConn)->GetServerVariable(psz, pv, pdw, FALSE);
  105. }
  106. int RecvToBuf(SOCKET socket, PVOID pv, DWORD dwReadBufSize, DWORD dwTimeout)
  107. {
  108. DEBUG_CODE_INIT;
  109. int iRecv = SOCKET_ERROR;
  110. DWORD dwAvailable;
  111. if (!MySelect(socket,dwTimeout))
  112. {
  113. SetLastError(WSAETIMEDOUT);
  114. myleave(1400);
  115. }
  116. if (ioctlsocket(socket, FIONREAD, &dwAvailable))
  117. {
  118. SetLastError(WSAETIMEDOUT);
  119. myleave(1401);
  120. }
  121. iRecv = recv(socket, (PSTR) pv, (dwAvailable < dwReadBufSize) ? dwAvailable : dwReadBufSize, 0);
  122. if (iRecv == 0)
  123. {
  124. SetLastError(WSAETIMEDOUT);
  125. myleave(1402);
  126. }
  127. if (iRecv == SOCKET_ERROR)
  128. {
  129. // recv() already has called SetLastError with appropriate message
  130. myleave(1403);
  131. }
  132. done:
  133. TraceTag(ttidWebServer, "RecvToBuf returns error err = %d, GLE = 0x%08x",err,GetLastError());
  134. return iRecv;
  135. }
  136. BOOL WINAPI ReadClient(HCONN hConn, PVOID pv, PDWORD pdw)
  137. {
  138. CHECKHCONN(hConn);
  139. CHECKPTRS2(pv, pdw);
  140. if ( *pdw == 0)
  141. {
  142. SetLastError(ERROR_INVALID_PARAMETER);
  143. return FALSE;
  144. }
  145. return((CHttpRequest*)hConn)->ReadClient(pv,pdw);
  146. }
  147. BOOL CHttpRequest::ReadClient(PVOID pv, PDWORD pdw)
  148. {
  149. DEBUG_CODE_INIT;
  150. BOOL ret = FALSE;
  151. PVOID pvFilterModify = pv;
  152. DWORD dwBytesReceived;
  153. DWORD dwBufferSize = *pdw;
  154. dwBytesReceived = RecvToBuf(m_socket,pv,*pdw,RECVTIMEOUT);
  155. if (dwBytesReceived == 0 || dwBytesReceived == SOCKET_ERROR)
  156. myleave(1399);
  157. if (g_pVars->m_fFilters &&
  158. ! CallFilter(SF_NOTIFY_READ_RAW_DATA,(PSTR*) &pvFilterModify,(int*) &dwBytesReceived, NULL, (int *) &dwBufferSize))
  159. {
  160. myleave(1404);
  161. }
  162. // Check if filter modified pointer, copy if there's enough room for it.
  163. if (pvFilterModify != pv)
  164. {
  165. if (*pdw <= dwBufferSize)
  166. {
  167. memcpy(pv,pvFilterModify,dwBufferSize);
  168. }
  169. else
  170. {
  171. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  172. myleave(1405);
  173. }
  174. }
  175. *pdw = dwBytesReceived;
  176. ret = TRUE;
  177. done:
  178. TraceTag(ttidWebServer, "HTTPD:ReadClient failed, GLE = 0x%08x, err = % err",GetLastError(), err);
  179. return ret;
  180. }
  181. BOOL WINAPI WriteClient(HCONN hConn, PVOID pv, PDWORD pdw, DWORD dwFlags)
  182. {
  183. CHECKHCONN(hConn);
  184. CHECKPTRS2(pv, pdw);
  185. if (dwFlags & HSE_IO_ASYNC)
  186. return((CHttpRequest*)hConn)->WriteClientAsync(pv, pdw, FALSE);
  187. else
  188. return((CHttpRequest*)hConn)->WriteClient(pv, pdw,FALSE);
  189. }
  190. BOOL WINAPI ServerSupportFunction(HCONN hConn, DWORD dwReq, PVOID pvBuf, PDWORD pdwSize, PDWORD pdwType)
  191. {
  192. CHECKHCONN(hConn);
  193. return((CHttpRequest*)hConn)->ServerSupportFunction(dwReq, pvBuf, pdwSize, pdwType);
  194. }
  195. BOOL CHttpRequest::ServerSupportFunction(DWORD dwReq, PVOID pvBuf, PDWORD pdwSize, PDWORD pdwType)
  196. {
  197. switch (dwReq)
  198. {
  199. // Can never support these
  200. //case HSE_REQ_ABORTIVE_CLOSE:
  201. //case HSE_REQ_ASYNC_READ_CLIENT:
  202. //case HSE_REQ_GET_CERT_INFO_EX:
  203. //case HSE_REQ_GET_IMPERSONATION_TOKEN:
  204. //case HSE_REQ_GET_SSPI_INFO:
  205. //case HSE_REQ_REFRESH_ISAPI_ACL:
  206. //case HSE_REQ_TRANSMIT_FILE:
  207. default:
  208. {
  209. SetLastError(ERROR_INVALID_PARAMETER);
  210. return FALSE;
  211. }
  212. case HSE_REQ_IS_KEEP_CONN:
  213. {
  214. CHECKPTR(pvBuf);
  215. *((BOOL *) pvBuf) = m_fKeepAlive;
  216. return TRUE;
  217. }
  218. case HSE_REQ_SEND_URL:
  219. case HSE_REQ_SEND_URL_REDIRECT_RESP:
  220. {
  221. // close connection, because ISAPI won't have a chance to add headers anyway
  222. CHttpResponse resp(m_socket, STATUS_MOVED, CONN_CLOSE,this);
  223. // m_rs = STATUS_MOVED;
  224. resp.SendRedirect((PSTR)pvBuf); // send a special redirect body
  225. m_fKeepAlive = FALSE;
  226. return TRUE;
  227. }
  228. case HSE_REQ_MAP_URL_TO_PATH_EX:
  229. case HSE_REQ_MAP_URL_TO_PATH:
  230. {
  231. CHECKPTRS2(pvBuf,pdwSize);
  232. if (dwReq == HSE_REQ_MAP_URL_TO_PATH_EX)
  233. {
  234. if (!pdwType)
  235. {
  236. SetLastError(ERROR_INVALID_PARAMETER);
  237. return FALSE;
  238. }
  239. return MapURLToPath((PSTR)pvBuf,pdwSize,(LPHSE_URL_MAPEX_INFO) pdwType);
  240. }
  241. else
  242. {
  243. // IIS docs are misleading here, but even if a valid param is passed in non-EX
  244. // case, ignore it. (Like IIS.)
  245. return MapURLToPath((PSTR)pvBuf,pdwSize);
  246. }
  247. }
  248. case HSE_REQ_SEND_RESPONSE_HEADER:
  249. {
  250. // no Connection header...let ISAPI send one if it wants
  251. CHttpResponse resp(m_socket, STATUS_OK, CONN_NONE,this);
  252. // no body, default or otherwise (leave that to the ISAPI), but add default headers
  253. m_rs = STATUS_OK;
  254. resp.SendResponse((PSTR) pdwType, (PSTR) pvBuf);
  255. return TRUE;
  256. }
  257. case HSE_REQ_SEND_RESPONSE_HEADER_EX:
  258. {
  259. // Note: We ignore cchStatus and cchHeader members.
  260. CHECKPTR(pvBuf);
  261. HSE_SEND_HEADER_EX_INFO *pHeaderEx = (HSE_SEND_HEADER_EX_INFO *) pvBuf;
  262. // Connection header determined by fKeepConn of passed in struct
  263. CHttpResponse resp(m_socket, STATUS_OK,
  264. pHeaderEx->fKeepConn ? CONN_KEEP : CONN_CLOSE,
  265. this);
  266. m_fKeepAlive = pHeaderEx->fKeepConn;
  267. // no body, default or otherwise (leave that to the ISAPI), but add default headers
  268. m_rs = STATUS_OK;
  269. resp.SendResponse(pHeaderEx->pszHeader, pHeaderEx->pszStatus);
  270. return TRUE;
  271. }
  272. case HSE_APPEND_LOG_PARAMETER:
  273. {
  274. return MyStrCatA(&m_pszLogParam,(PSTR) pvBuf,",");
  275. }
  276. case HSE_REQ_CLOSE_CONNECTION:
  277. {
  278. // Per ISAPI documentation:
  279. //
  280. // Once you use the HSE_REQ_CLOSE_CONNECTION server
  281. // support function to close a connection, you must
  282. // wait for IIS to call the asynchronous I/O function
  283. // (specified by HSE_REQ_IO_COMPLETION) before you end
  284. // the session with HSE_REQ_DONE_WITH_SESSION.
  285. //
  286. // Since our async I/O calls are all really sync, we
  287. // will call the completion routine at the end of the
  288. // operation and get a HSE_REQ_DONE_WITH_SESSION to do
  289. // the rest of the cleanup.
  290. return TRUE;
  291. }
  292. case HSE_REQ_DONE_WITH_SESSION:
  293. {
  294. AssertSz (m_hEvent != NULL, "Call to end a non-pended session, treating as error");
  295. if (m_hEvent != NULL)
  296. {
  297. m_dwStatus = *((DWORD *) pvBuf);
  298. m_fKeepAlive = FALSE;
  299. SetEvent (m_hEvent);
  300. return TRUE;
  301. }
  302. else
  303. {
  304. SetLastError(ERROR_INVALID_PARAMETER);
  305. return FALSE;
  306. }
  307. }
  308. case HSE_REQ_IO_COMPLETION:
  309. {
  310. m_pfnCompletion = (PFN_HSE_IO_COMPLETION)pvBuf;
  311. m_pvContext = (PVOID) pdwType;
  312. return TRUE;
  313. }
  314. }
  315. }
  316. BOOL CHttpRequest::MapURLToPath(PSTR pszBuffer, PDWORD pdwSize, LPHSE_URL_MAPEX_INFO pUrlMapEx)
  317. {
  318. DWORD dwPermissions;
  319. PWSTR wszPath;
  320. PSTR pszURL;
  321. DWORD dwBufNeeded = 0;
  322. BOOL ret;
  323. wszPath = g_pVars->m_pVroots->URLAtoPathW(pszBuffer,&dwPermissions);
  324. if (!wszPath)
  325. {
  326. // Assume failure on matching to virtual root, and not on mem alloc
  327. SetLastError(ERROR_INVALID_PARAMETER);
  328. return FALSE;
  329. }
  330. dwBufNeeded = (DWORD) WideCharToMultiByte(CP_ACP,0, wszPath, -1, pszBuffer, 0 ,0,0);
  331. // For MAP_EX case, we set these vars from the passed structure, else we use the raw ptrs.
  332. if (pUrlMapEx)
  333. {
  334. pszURL = pUrlMapEx->lpszPath;
  335. *pdwSize = MAX_PATH;
  336. }
  337. else
  338. {
  339. pszURL = pszBuffer;
  340. }
  341. // To keep this like IIS, we translate "/" to "\". We do the conversion
  342. // only if we're using filters or if there's enough space in the buffer.
  343. if (g_pVars->m_fFilters || *pdwSize >= dwBufNeeded)
  344. {
  345. for (int i = 0; i < (int) wcslen(wszPath); i++)
  346. {
  347. if ( wszPath[i] == L'/')
  348. wszPath[i] = L'\\';
  349. }
  350. }
  351. if (FALSE == g_pVars->m_fFilters)
  352. {
  353. // We check to make sure buffer is the right size because WideToMultyByte
  354. // will overwrite pieces of pszURL even on failure, leaving pszURL's
  355. // content invalid
  356. if (*pdwSize < dwBufNeeded)
  357. {
  358. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  359. ret = FALSE;
  360. }
  361. else
  362. {
  363. MyW2A(wszPath, pszURL, *pdwSize);
  364. ret = TRUE;
  365. }
  366. *pdwSize = dwBufNeeded;
  367. }
  368. else
  369. {
  370. // for EX case, put original URL as optional 5th parameter.
  371. ret = FilterMapURL(pszURL, wszPath, pdwSize,dwBufNeeded, pUrlMapEx ? pszBuffer : NULL);
  372. }
  373. if (ret && pUrlMapEx)
  374. {
  375. pUrlMapEx->cchMatchingPath = *pdwSize - 1; // don't count \0
  376. pUrlMapEx->cchMatchingURL = strlen(pszBuffer);
  377. pUrlMapEx->dwFlags = dwPermissions;
  378. }
  379. MyFree(wszPath);
  380. return ret;
  381. }
  382. void CISAPI::Unload(PWSTR wszDLLName)
  383. {
  384. if (m_pfnTerminate)
  385. {
  386. __try
  387. {
  388. if (SCRIPT_TYPE_FILTER == m_scriptType)
  389. ((PFN_TERMINATEFILTER)m_pfnTerminate)(HSE_TERM_MUST_UNLOAD);
  390. else if (SCRIPT_TYPE_EXTENSION == m_scriptType)
  391. ((PFN_TERMINATEEXTENSION)m_pfnTerminate)(HSE_TERM_MUST_UNLOAD);
  392. else if (SCRIPT_TYPE_ASP == m_scriptType)
  393. ((PFN_TERMINATEASP)m_pfnTerminate)();
  394. }
  395. __except(1)
  396. {
  397. DWORD dwExceptionCode = SCRIPT_TYPE_FILTER == m_scriptType ?
  398. IDS_HTTPD_FILT_EXCEPTION : IDS_HTTPD_EXT_EXCEPTION;
  399. PWSTR wszFunction = SCRIPT_TYPE_FILTER == m_scriptType ?
  400. L"TerminateFilter" : L"TerminateExtension";
  401. TraceTag(ttidWebServer, "HTTPD: TerminateExtension faulted");
  402. g_pVars->m_pLog->WriteEvent(dwExceptionCode,wszDLLName ? wszDLLName : m_wszDLLName,
  403. GetExceptionCode(),wszFunction,GetLastError());
  404. }
  405. }
  406. MyFreeLib(m_hinst);
  407. m_hinst = NULL;
  408. m_pfnTerminate = NULL;
  409. }
  410. BOOL CISAPI::Load(PWSTR wszPath)
  411. {
  412. m_hinst = LoadLibrary(wszPath);
  413. if (!m_hinst)
  414. return FALSE;
  415. if (SCRIPT_TYPE_ASP == m_scriptType)
  416. {
  417. if (! (m_pfnHttpProc = GetProcAddress(m_hinst, CE_STRING("ExecuteASP"))))
  418. goto error;
  419. if (! (m_pfnTerminate = GetProcAddress(m_hinst, CE_STRING("TerminateASP"))))
  420. goto error;
  421. return TRUE;
  422. }
  423. if (SCRIPT_TYPE_EXTENSION == m_scriptType)
  424. {
  425. m_pfnGetVersion = GetProcAddress(m_hinst, CE_STRING("GetExtensionVersion"));
  426. m_pfnHttpProc = GetProcAddress(m_hinst, CE_STRING("HttpExtensionProc"));
  427. m_pfnTerminate = GetProcAddress(m_hinst, CE_STRING("TerminateExtension"));
  428. }
  429. else if (SCRIPT_TYPE_FILTER == m_scriptType)
  430. {
  431. m_pfnGetVersion = GetProcAddress(m_hinst, CE_STRING("GetFilterVersion"));
  432. m_pfnHttpProc = GetProcAddress(m_hinst, CE_STRING("HttpFilterProc"));
  433. m_pfnTerminate = GetProcAddress(m_hinst, CE_STRING("TerminateFilter"));
  434. }
  435. if (!m_pfnHttpProc || !m_pfnGetVersion)
  436. goto error;
  437. __try
  438. {
  439. // call GetVersion immediately after load on extensions and Filters, but not ASP
  440. // if it's a filter we need to do some flags work first
  441. if (SCRIPT_TYPE_FILTER == m_scriptType)
  442. {
  443. HTTP_FILTER_VERSION vFilt;
  444. // IIS ignores ISAPI version info, so do we.
  445. ((PFN_GETFILTERVERSION)m_pfnGetVersion)(&vFilt);
  446. dwFilterFlags = vFilt.dwFlags;
  447. // client didn't set the prio flags, assign them to default
  448. // If they set more than one prio we use the highest + ignore others
  449. if (0 == (dwFilterFlags & SF_NOTIFY_ORDER_MASK))
  450. {
  451. dwFilterFlags |= SF_NOTIFY_ORDER_DEFAULT;
  452. }
  453. }
  454. else if (SCRIPT_TYPE_EXTENSION == m_scriptType)
  455. {
  456. HSE_VERSION_INFO vExt;
  457. // IIS ignores ISAPI version info, so do we.
  458. ((PFN_GETEXTENSIONVERSION)m_pfnGetVersion)(&vExt);
  459. }
  460. return TRUE;
  461. }
  462. __except(1)
  463. {
  464. DWORD dwExceptionCode = SCRIPT_TYPE_FILTER == m_scriptType ?
  465. IDS_HTTPD_FILT_EXCEPTION : IDS_HTTPD_EXT_EXCEPTION;
  466. PWSTR wszFunction = SCRIPT_TYPE_FILTER == m_scriptType ?
  467. L"GetFilterVersion" : L"GetExtensionVersion";
  468. TraceTag(ttidWebServer, "HTTPD: GetExtensionVersion faulted");
  469. g_pVars->m_pLog->WriteEvent(dwExceptionCode,wszPath,GetExceptionCode(),wszFunction,GetLastError());
  470. }
  471. // fall through to error
  472. error:
  473. m_pfnGetVersion = 0;
  474. m_pfnHttpProc = 0;
  475. m_pfnTerminate = 0;
  476. MyFreeLib(m_hinst);
  477. m_hinst = 0;
  478. return FALSE;
  479. }
  480. //**********************************************************************
  481. // ISAPI Caching functions
  482. //**********************************************************************
  483. HINSTANCE CISAPICache::Load(PWSTR wszDLLName, CISAPI **ppISAPI, SCRIPT_TYPE st)
  484. {
  485. DEBUG_CODE_INIT;
  486. BOOL ret = FALSE;
  487. PISAPINODE pTrav = NULL;
  488. EnterCriticalSection(&m_CritSec);
  489. for (pTrav = m_pHead; pTrav != NULL; pTrav = pTrav->m_pNext)
  490. {
  491. if ( 0 == lstrcmpi(pTrav->m_pISAPI->m_wszDLLName, wszDLLName))
  492. {
  493. TraceTag(ttidWebServer, "Found ISAPI dll in cache, name = %s, cur ref count = %d",
  494. wszDLLName, pTrav->m_pISAPI->m_cRef);
  495. break;
  496. }
  497. }
  498. if (NULL == pTrav)
  499. {
  500. TraceTag(ttidWebServer, "ISAPI dll name = %s not found in cache, creating new entry",wszDLLName);
  501. if (NULL == (pTrav = MyAllocNZ(ISAPINODE)))
  502. myleave(1200);
  503. if (NULL == (pTrav->m_pISAPI = new CISAPI(st)))
  504. myleave(1201);
  505. if (NULL == (pTrav->m_pISAPI->m_wszDLLName = MySzDupW(wszDLLName)))
  506. myleave(1202);
  507. if (! pTrav->m_pISAPI->Load(wszDLLName))
  508. myleave(1203);
  509. pTrav->m_pNext = m_pHead;
  510. m_pHead = pTrav;
  511. }
  512. pTrav->m_pISAPI->m_cRef++;
  513. *ppISAPI = pTrav->m_pISAPI;
  514. ret = TRUE;
  515. done:
  516. if (!ret)
  517. {
  518. if (pTrav && pTrav->m_pISAPI)
  519. {
  520. MyFree(pTrav->m_pISAPI->m_wszDLLName);
  521. delete pTrav->m_pISAPI;
  522. }
  523. MyFree(pTrav);
  524. }
  525. TraceTag(ttidWebServer, "CISAPICache::LoadISAPI failed, err = %d, GLE = 0x%08x",err,GetLastError());
  526. LeaveCriticalSection(&m_CritSec);
  527. if (ret)
  528. return pTrav->m_pISAPI->m_hinst;
  529. return NULL;
  530. }
  531. void CHttpRequest::StartRemoveISAPICacheIfNeeded()
  532. {
  533. DEBUGCHK(g_pVars->m_fExtensions);
  534. if (InterlockedCompareExchange(&g_pVars->m_fISAPICacheRunning,1,0) == 0)
  535. {
  536. TraceTag(ttidWebServer, "HTTPD: ExecuteISAPI: Creating RemoveUnusedISAPIs timer");
  537. // We never stop the timer. This is handled on call to Thread Pool Shutdown
  538. g_pVars->m_pThreadPool->StartTimer(RemoveUnusedISAPIs,0,CACHE_SLEEP_TIMEOUT);
  539. }
  540. }
  541. // lpv = 0 for thread that sleeps forever.
  542. // lpv = 1 to remove all ISAPIs, called on shutdown.
  543. DWORD WINAPI RemoveUnusedISAPIs(LPVOID lpv)
  544. {
  545. // We set lpv to 1 if we want to unload all ISAPIs during shutdown.
  546. if (lpv)
  547. {
  548. g_pVars->m_pISAPICache->RemoveUnusedISAPIs(TRUE);
  549. return 0;
  550. }
  551. if (g_pVars->m_pISAPICache->m_pHead != NULL)
  552. g_pVars->m_pISAPICache->RemoveUnusedISAPIs(FALSE);
  553. g_pVars->m_pThreadPool->StartTimer(RemoveUnusedISAPIs,0,CACHE_SLEEP_TIMEOUT);
  554. return 0;
  555. }
  556. // to milliseconds (10 * 1000)
  557. #define FILETIME_TO_MILLISECONDS ((__int64)10000L)
  558. // fRemoveAll = TRUE ==> remove all ISAPI's, we're shutting down
  559. // = FALSE ==> only remove ones who aren't in use and whose time has expired
  560. void CISAPICache::RemoveUnusedISAPIs(BOOL fRemoveAll)
  561. {
  562. PISAPINODE pTrav = NULL;
  563. PISAPINODE pFollow = NULL;
  564. PISAPINODE pDelete = NULL;
  565. SYSTEMTIME st;
  566. __int64 ft;
  567. EnterCriticalSection(&m_CritSec);
  568. GetSystemTime(&st);
  569. SystemTimeToFileTime(&st,(FILETIME*) &ft);
  570. // Figure out what time it was g_pVars->m_dwCacheSleep milliseconds ago.
  571. // Elements that haven't been used since then and that have no references
  572. // are deleted.
  573. ft -= FILETIME_TO_MILLISECONDS * g_pVars->m_dwCacheSleep;
  574. for (pTrav = m_pHead; pTrav != NULL; )
  575. {
  576. if (fRemoveAll ||
  577. (pTrav->m_pISAPI->m_cRef == 0 && pTrav->m_pISAPI->m_ftLastUsed < ft))
  578. {
  579. TraceTag(ttidWebServer, "Freeing unused ISAPI Dll %s",pTrav->m_pISAPI->m_wszDLLName);
  580. pTrav->m_pISAPI->Unload();
  581. delete pTrav->m_pISAPI;
  582. if (pFollow)
  583. pFollow->m_pNext = pTrav->m_pNext;
  584. else
  585. m_pHead = pTrav->m_pNext;
  586. pDelete = pTrav;
  587. pTrav = pTrav->m_pNext;
  588. MyFree(pDelete);
  589. }
  590. else
  591. {
  592. if (pTrav->m_pISAPI->m_cRef == 0)
  593. pTrav->m_pISAPI->CoFreeUnusedLibrariesIfASP();
  594. pFollow = pTrav;
  595. pTrav = pTrav->m_pNext;
  596. }
  597. }
  598. LeaveCriticalSection(&m_CritSec);
  599. }