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.

3608 lines
92 KiB

  1. /*++
  2. Copyright (c) 1998-2002 Microsoft Corporation
  3. Module Name:
  4. httpconn.c
  5. Abstract:
  6. This module implements the HTTP_CONNECTION object.
  7. Author:
  8. Keith Moore (keithmo) 08-Jul-1998
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #include "httpconnp.h"
  13. //
  14. // Private globals.
  15. //
  16. BOOLEAN g_HttpconnInitialized = FALSE;
  17. UL_ZOMBIE_HTTP_CONNECTION_LIST g_ZombieConnectionList;
  18. //
  19. // Global connection Limits stuff
  20. //
  21. ULONG g_MaxConnections = HTTP_LIMIT_INFINITE;
  22. ULONG g_CurrentConnections = 0;
  23. BOOLEAN g_InitGlobalConnections = FALSE;
  24. #ifdef ALLOC_PRAGMA
  25. #pragma alloc_text(INIT, UlInitializeHttpConnection)
  26. #endif // ALLOC_PRAGMA
  27. #if 0
  28. NOT PAGEABLE -- UlBindConnectionToProcess
  29. NOT PAGEABLE -- UlQueryProcessBinding
  30. NOT PAGEABLE -- UlpCreateProcBinding
  31. NOT PAGEABLE -- UlpFreeProcBinding
  32. NOT PAGEABLE -- UlpFindProcBinding
  33. NOT PAGEABLE -- UlCreateHttpConnection
  34. NOT PAGEABLE -- UlReferenceHttpConnection
  35. NOT PAGEABLE -- UlDereferenceHttpConnection
  36. NOT PAGEABLE -- UlReferenceHttpRequest
  37. NOT PAGEABLE -- UlDereferenceHttpRequest
  38. NOT PAGEABLE -- UlpCreateHttpRequest
  39. NOT PAGEABLE -- UlpFreeHttpRequest
  40. NOT PAGEABLE -- UlTerminateHttpConnection
  41. NOT PAGEABLE -- UlPurgeZombieConnections
  42. NOT PAGEABLE -- UlpZombifyHttpConnection
  43. NOT PAGEABLE -- UlZombieTimerDpcRoutine
  44. NOT PAGEABLE -- UlpZombieTimerWorker
  45. NOT PAGEABLE -- UlpTerminateZombieList
  46. NOT PAGEABLE -- UlLogZombieConnection
  47. NOT PAGEABLE -- UlDisconnectHttpConnection
  48. #endif
  49. //
  50. // Public functions.
  51. //
  52. /***************************************************************************++
  53. Routine Description:
  54. Performs global initialization of this module.
  55. --***************************************************************************/
  56. NTSTATUS
  57. UlInitializeHttpConnection(
  58. VOID
  59. )
  60. {
  61. //
  62. // Sanity check.
  63. //
  64. PAGED_CODE();
  65. ASSERT(!g_HttpconnInitialized);
  66. if (g_HttpconnInitialized)
  67. {
  68. return STATUS_SUCCESS;
  69. }
  70. //
  71. // Initialize global data.
  72. //
  73. UlInitalizeLockedList(
  74. &g_ZombieConnectionList.LockList,
  75. "ZombieHttpConnectionList"
  76. );
  77. g_ZombieConnectionList.TimerRunning = 0;
  78. #ifdef ENABLE_HTTP_CONN_STATS
  79. g_ZombieConnectionList.TotalCount = 0;
  80. g_ZombieConnectionList.TotalZombieCount = 0;
  81. g_ZombieConnectionList.TotalZombieRefusal = 0;
  82. g_ZombieConnectionList.MaxZombieCount = 0;
  83. #endif
  84. //
  85. // Zombie Timer stuff
  86. //
  87. g_ZombieConnectionList.TimerInitialized = TRUE;
  88. UlInitializeSpinLock(
  89. &g_ZombieConnectionList.TimerSpinLock,
  90. "HttpZombieConnectionTimerSpinLock"
  91. );
  92. KeInitializeDpc(
  93. &g_ZombieConnectionList.DpcObject, // DPC object
  94. &UlZombieTimerDpcRoutine, // DPC routine
  95. NULL // context
  96. );
  97. UlInitializeWorkItem(&g_ZombieConnectionList.WorkItem);
  98. KeInitializeTimer(&g_ZombieConnectionList.Timer);
  99. UlpSetZombieTimer();
  100. //
  101. // Everything is initialized.
  102. //
  103. g_HttpconnInitialized = TRUE;
  104. return STATUS_SUCCESS;
  105. }
  106. /***************************************************************************++
  107. Routine Description:
  108. Performs global termination of this module.
  109. --***************************************************************************/
  110. VOID
  111. UlTerminateHttpConnection(
  112. VOID
  113. )
  114. {
  115. KIRQL OldIrql;
  116. //
  117. // Sanity check.
  118. //
  119. if (g_HttpconnInitialized)
  120. {
  121. // Zombie timer
  122. UlAcquireSpinLock(&g_ZombieConnectionList.TimerSpinLock, &OldIrql);
  123. g_ZombieConnectionList.TimerInitialized = FALSE;
  124. KeCancelTimer(&g_ZombieConnectionList.Timer);
  125. UlReleaseSpinLock(&g_ZombieConnectionList.TimerSpinLock, OldIrql);
  126. UlpTerminateZombieList();
  127. #ifdef ENABLE_HTTP_CONN_STATS
  128. UlTrace(HTTP_IO,
  129. ("UlTerminateHttpConnection, Stats:\n"
  130. "\tTotalConnections = %lu\n"
  131. "\tMaxZombieCount = %lu\n"
  132. "\tTotalDisconnectedCount = %lu\n"
  133. "\tTotalRefusedCount =%lu\n",
  134. g_ZombieConnectionList.TotalCount,
  135. g_ZombieConnectionList.MaxZombieCount,
  136. g_ZombieConnectionList.TotalZombieCount,
  137. g_ZombieConnectionList.TotalZombieRefusal
  138. ));
  139. #endif
  140. UlDestroyLockedList(&g_ZombieConnectionList.LockList);
  141. g_HttpconnInitialized = FALSE;
  142. }
  143. }
  144. /***************************************************************************++
  145. Routine Description:
  146. Creates a new HTTP_CONNECTION object.
  147. Arguments:
  148. ppHttpConnection - Receives a pointer to the new HTTP_CONNECTION
  149. object if successful.
  150. pConnection - Supplies a pointer to the UL_CONNECTION to associate
  151. with the newly created HTTP_CONNECTION.
  152. Return Value:
  153. NTSTATUS - Completion status.
  154. --***************************************************************************/
  155. NTSTATUS
  156. UlCreateHttpConnection(
  157. OUT PUL_HTTP_CONNECTION *ppHttpConnection,
  158. IN PUL_CONNECTION pConnection
  159. )
  160. {
  161. PUL_HTTP_CONNECTION pHttpConn;
  162. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  163. pHttpConn = &pConnection->HttpConnection;
  164. RtlZeroMemory(pHttpConn, sizeof(*pHttpConn));
  165. REFERENCE_CONNECTION(pConnection);
  166. pHttpConn->Signature = UL_HTTP_CONNECTION_POOL_TAG;
  167. pHttpConn->RefCount = 1;
  168. pHttpConn->pConnection = pConnection;
  169. pHttpConn->SecureConnection = pConnection->FilterInfo.SecureConnection;
  170. UlInitializeWorkItem(&pHttpConn->WorkItem);
  171. UlInitializeWorkItem(&pHttpConn->DisconnectWorkItem);
  172. UlInitializeWorkItem(&pHttpConn->DisconnectDrainWorkItem);
  173. UlInitializeWorkItem(&pHttpConn->ReceiveBufferWorkItem);
  174. UlInitializeExclusiveLock(&pHttpConn->ExLock);
  175. //
  176. // Initialize the disconnect management fields.
  177. //
  178. UlInitializeNotifyHead(&pHttpConn->WaitForDisconnectHead, NULL);
  179. //
  180. // Init our buffer list
  181. //
  182. InitializeListHead(&pHttpConn->BufferHead);
  183. //
  184. // Init pending receive buffer list
  185. //
  186. InitializeSListHead(&pHttpConn->ReceiveBufferSList);
  187. //
  188. // init app pool process binding list
  189. //
  190. InitializeListHead(&(pHttpConn->BindingHead));
  191. UlInitializePushLock(
  192. &(pHttpConn->PushLock),
  193. "UL_HTTP_CONNECTION[%p].PushLock",
  194. pHttpConn,
  195. UL_HTTP_CONNECTION_PUSHLOCK_TAG
  196. );
  197. //
  198. // Initialize BufferingInfo structure.
  199. //
  200. UlInitializeSpinLock(
  201. &pHttpConn->BufferingInfo.BufferingSpinLock,
  202. "BufferingSpinLock"
  203. );
  204. UlInitializeSpinLock(
  205. &pHttpConn->RequestIdSpinLock,
  206. "RequestIdSpinLock"
  207. );
  208. UlTrace(
  209. REFCOUNT, (
  210. "http!UlCreateHttpConnection conn=%p refcount=%d\n",
  211. pHttpConn,
  212. pHttpConn->RefCount)
  213. );
  214. *ppHttpConnection = pHttpConn;
  215. RETURN(STATUS_SUCCESS);
  216. } // UlCreateHttpConnection
  217. NTSTATUS
  218. UlCreateHttpConnectionId(
  219. IN PUL_HTTP_CONNECTION pHttpConnection
  220. )
  221. {
  222. NTSTATUS Status;
  223. //
  224. // Sanity check.
  225. //
  226. PAGED_CODE();
  227. //
  228. // Create an opaque id for the connection
  229. //
  230. Status = UlAllocateOpaqueId(
  231. &pHttpConnection->ConnectionId, // pOpaqueId
  232. UlOpaqueIdTypeHttpConnection, // OpaqueIdType
  233. pHttpConnection // pContext
  234. );
  235. if (NT_SUCCESS(Status))
  236. {
  237. UL_REFERENCE_HTTP_CONNECTION(pHttpConnection);
  238. TRACE_TIME(
  239. pHttpConnection->ConnectionId,
  240. 0,
  241. TIME_ACTION_CREATE_CONNECTION
  242. );
  243. }
  244. RETURN(Status);
  245. } // UlCreateHttpConnectionId
  246. /***************************************************************************++
  247. Routine Description:
  248. In normal case it cleans up the connection and unlink the request.
  249. (if there is one). But if the last sendresponse hasn't been received on
  250. the connection yet, it moves the connection to the zombie list w/o
  251. releasing it's refcount.
  252. Arguments:
  253. pWorkItem - workitem to get the http connection
  254. --***************************************************************************/
  255. VOID
  256. UlConnectionDestroyedWorker(
  257. IN PUL_WORK_ITEM pWorkItem
  258. )
  259. {
  260. NTSTATUS Status = STATUS_SUCCESS;
  261. PUL_HTTP_CONNECTION pHttpConnection;
  262. BOOLEAN ZombieConnection = FALSE;
  263. PUL_INTERNAL_REQUEST pRequest;
  264. //
  265. // Sanity check.
  266. //
  267. PAGED_CODE();
  268. pHttpConnection = CONTAINING_RECORD(
  269. pWorkItem,
  270. UL_HTTP_CONNECTION,
  271. WorkItem
  272. );
  273. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConnection));
  274. UlTrace(HTTP_IO, (
  275. "Http!UlConnectionDestroyedWorker: httpconn=%p\n",
  276. pHttpConnection
  277. ));
  278. #ifdef ENABLE_HTTP_CONN_STATS
  279. InterlockedIncrement(
  280. (PLONG) &g_ZombieConnectionList.TotalCount);
  281. #endif
  282. //
  283. // Don't let connection go away while we are working. If it becomes
  284. // zombie it may get destroyed as soon as we put it on the zombie
  285. // list and release the connection eresource.
  286. //
  287. UL_REFERENCE_HTTP_CONNECTION(pHttpConnection);
  288. UlAcquirePushLockExclusive(&pHttpConnection->PushLock);
  289. pRequest = pHttpConnection->pRequest;
  290. //
  291. // Kill the request if there is one. But only if we have done with
  292. // the logging, otherwise we will keep it in the zombie list until last
  293. // the last sendresponse with logging data arrives or the request
  294. // itself get scavenged out from the zombie list later.
  295. //
  296. if (pRequest)
  297. {
  298. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  299. //
  300. // Only if the last send did not happened yet.
  301. //
  302. if (0 == InterlockedCompareExchange(
  303. (PLONG) &pRequest->ZombieCheck,
  304. 1,
  305. 0
  306. ))
  307. {
  308. //
  309. // Only if request got routed up to the worker process
  310. // and logging enabled but not happened.
  311. //
  312. if (pRequest->ConfigInfo.pLoggingConfig != NULL &&
  313. UlCheckAppPoolState(pRequest))
  314. {
  315. ZombieConnection = TRUE;
  316. }
  317. }
  318. if (ZombieConnection)
  319. {
  320. ASSERT(pHttpConnection->Zombified == FALSE);
  321. ASSERT(pHttpConnection->pRequestIdContext != NULL);
  322. Status = UlpZombifyHttpConnection(pHttpConnection);
  323. if (!NT_SUCCESS(Status))
  324. {
  325. //
  326. // We didn't make it to the zombie list. The list
  327. // is probably full. Lets destroy this connection
  328. // completely.
  329. //
  330. ZombieConnection = FALSE;
  331. }
  332. }
  333. if (!ZombieConnection)
  334. {
  335. //
  336. // If we got far enough to deliver the request then unlink it
  337. // from the app pool. This needs to happen here because the
  338. // queue'd IRPs keep references to the UL_INTERNAL_REQUEST
  339. // objects. This kicks their references off and allows them
  340. // to delete.
  341. //
  342. if (pRequest->ConfigInfo.pAppPool)
  343. {
  344. UlUnlinkRequestFromProcess(
  345. pRequest->ConfigInfo.pAppPool,
  346. pRequest
  347. );
  348. }
  349. UlUnlinkHttpRequest(pRequest);
  350. //
  351. // If connection didn't go to the zombie list then
  352. // null out our request pointer since our refcount
  353. // has gone already.
  354. //
  355. pHttpConnection->pRequest = NULL;
  356. }
  357. }
  358. //
  359. // Make sure no one adds a new request now that we're done
  360. //
  361. pHttpConnection->UlconnDestroyed = 1;
  362. if (!ZombieConnection)
  363. {
  364. //
  365. // If we don't need to keep the connection around
  366. // then remove its opaque id.
  367. //
  368. if (!HTTP_IS_NULL_ID(&pHttpConnection->ConnectionId))
  369. {
  370. UlFreeOpaqueId(
  371. pHttpConnection->ConnectionId,
  372. UlOpaqueIdTypeHttpConnection
  373. );
  374. HTTP_SET_NULL_ID(&pHttpConnection->ConnectionId);
  375. UL_DEREFERENCE_HTTP_CONNECTION(pHttpConnection);
  376. }
  377. }
  378. //
  379. // Complete all WaitForDisconnect IRPs
  380. //
  381. // Prevent new IRPs from getting attached to the connection,
  382. // by setting the DisconnectFlag.
  383. //
  384. ASSERT(!pHttpConnection->DisconnectFlag);
  385. pHttpConnection->DisconnectFlag = 1;
  386. if (pHttpConnection->WaitForDisconnectFlag)
  387. {
  388. UlCompleteAllWaitForDisconnect(pHttpConnection);
  389. }
  390. UlReleasePushLockExclusive(&pHttpConnection->PushLock);
  391. //
  392. // Release the temporary refcount.
  393. //
  394. UL_DEREFERENCE_HTTP_CONNECTION(pHttpConnection);
  395. //
  396. // Only remove the ULTDI reference from the HTTP_CONNECTION,
  397. // if connection didn't make it to the zombie list. Otherwise
  398. // the actual cleanup will happen later. Zombie list takes the
  399. // ownership of the TDI's original refcount now.
  400. //
  401. if (!ZombieConnection)
  402. {
  403. UL_DEREFERENCE_HTTP_CONNECTION(pHttpConnection);
  404. }
  405. } // UlConnectionDestroyedWorker
  406. /***************************************************************************++
  407. Routine Description:
  408. Tries to establish a binding between a connection and an app pool
  409. process. You can query these bindings with UlQueryProcessBinding.
  410. Arguments:
  411. pHttpConnection - the connection to bind
  412. pAppPool - the app pool that owns the process (key for lookups)
  413. pProcess - the process to bind to
  414. --***************************************************************************/
  415. NTSTATUS
  416. UlBindConnectionToProcess(
  417. IN PUL_HTTP_CONNECTION pHttpConnection,
  418. IN PUL_APP_POOL_OBJECT pAppPool,
  419. IN PUL_APP_POOL_PROCESS pProcess
  420. )
  421. {
  422. NTSTATUS Status = STATUS_SUCCESS;
  423. PUL_APOOL_PROC_BINDING pBinding = NULL;
  424. //
  425. // Sanity check
  426. //
  427. ASSERT( UL_IS_VALID_HTTP_CONNECTION( pHttpConnection ) );
  428. ASSERT( pAppPool->NumberActiveProcesses > 1 || pProcess == NULL );
  429. ASSERT( UlDbgSpinLockOwned( &pAppPool->SpinLock ) );
  430. //
  431. // see if there's already a binding object
  432. //
  433. pBinding = UlpFindProcBinding(pHttpConnection, pAppPool);
  434. if (pBinding) {
  435. if (pProcess) {
  436. //
  437. // modify the binding
  438. //
  439. pBinding->pProcess = pProcess;
  440. } else {
  441. //
  442. // we're supposed to kill this binding
  443. //
  444. RemoveEntryList(&pBinding->BindingEntry);
  445. UlpFreeProcBinding(pBinding);
  446. }
  447. } else {
  448. if (pProcess) {
  449. //
  450. // create a new entry
  451. //
  452. pBinding = UlpCreateProcBinding(pAppPool, pProcess);
  453. if (pBinding) {
  454. InsertHeadList(
  455. &pHttpConnection->BindingHead,
  456. &pBinding->BindingEntry
  457. );
  458. } else {
  459. Status = STATUS_INSUFFICIENT_RESOURCES;
  460. }
  461. }
  462. }
  463. UlTrace(ROUTING, (
  464. "http!UlBindConnectionToProcess(\n"
  465. " pHttpConnection = %p (%I64x)\n"
  466. " pAppPool = %p\n"
  467. " pProcess = %p )\n"
  468. " Status = 0x%x\n",
  469. pHttpConnection,
  470. pHttpConnection->ConnectionId,
  471. pAppPool,
  472. pProcess,
  473. Status
  474. ));
  475. return Status;
  476. }
  477. /***************************************************************************++
  478. Routine Description:
  479. Removes an HTTP request from all lists and cleans up it's opaque id.
  480. Arguments:
  481. pRequest - the request to be unlinked
  482. --***************************************************************************/
  483. VOID
  484. UlCleanupHttpConnection(
  485. IN PUL_HTTP_CONNECTION pHttpConnection
  486. )
  487. {
  488. PLIST_ENTRY pEntry;
  489. PUL_INTERNAL_REQUEST pRequest;
  490. ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pHttpConnection->pRequest ) );
  491. ASSERT( pHttpConnection->WaitingForResponse );
  492. ASSERT( UlDbgPushLockOwnedExclusive( &pHttpConnection->PushLock ) );
  493. pRequest = pHttpConnection->pRequest;
  494. if (pRequest->ContentLength > 0 || 1 == pRequest->Chunked)
  495. {
  496. //
  497. // Unlink the request from the process for POST. The time to
  498. // unlink a GET request is when the last send completes.
  499. // However, similar logic can't be applied to POST because
  500. // we may still have to drain the entity body so we are not
  501. // fully done with the request on send completion. So the
  502. // arrangement for POST is to 1) defer resuming parsing until send
  503. // completion, and 2) unlink the request from process when we
  504. // have drained the whole entity body unless server has issued
  505. // the disconnect.
  506. //
  507. if (pRequest->ConfigInfo.pAppPool)
  508. {
  509. UlUnlinkRequestFromProcess(
  510. pRequest->ConfigInfo.pAppPool,
  511. pRequest
  512. );
  513. }
  514. }
  515. UlUnlinkHttpRequest(pRequest);
  516. pHttpConnection->pRequest = NULL;
  517. pHttpConnection->WaitingForResponse = 0;
  518. UlTrace( PARSER, (
  519. "***1 pConnection %p->WaitingForResponse = 0\n",
  520. pHttpConnection
  521. ));
  522. //
  523. // Free the old request's buffers
  524. //
  525. ASSERT( UL_IS_VALID_REQUEST_BUFFER( pHttpConnection->pCurrentBuffer ) );
  526. pEntry = pHttpConnection->BufferHead.Flink;
  527. while (pEntry != &pHttpConnection->BufferHead)
  528. {
  529. PUL_REQUEST_BUFFER pBuffer;
  530. //
  531. // Get the object
  532. //
  533. pBuffer = CONTAINING_RECORD(
  534. pEntry,
  535. UL_REQUEST_BUFFER,
  536. ListEntry
  537. );
  538. //
  539. // did we reach the end?
  540. //
  541. if (pBuffer == pHttpConnection->pCurrentBuffer) {
  542. break;
  543. }
  544. //
  545. // remember the next one
  546. //
  547. pEntry = pEntry->Flink;
  548. //
  549. // unlink it
  550. //
  551. UlFreeRequestBuffer(pBuffer);
  552. }
  553. } // UlCleanupHttpConnection
  554. VOID
  555. UlReferenceHttpConnection(
  556. IN PVOID pObject
  557. REFERENCE_DEBUG_FORMAL_PARAMS
  558. )
  559. {
  560. LONG refCount;
  561. PUL_HTTP_CONNECTION pHttpConnection = (PUL_HTTP_CONNECTION) pObject;
  562. refCount = InterlockedIncrement( &pHttpConnection->RefCount );
  563. WRITE_REF_TRACE_LOG2(
  564. g_pHttpConnectionTraceLog,
  565. pHttpConnection->pConnection->pHttpTraceLog,
  566. REF_ACTION_REFERENCE_HTTP_CONNECTION,
  567. refCount,
  568. pHttpConnection,
  569. pFileName,
  570. LineNumber
  571. );
  572. UlTrace(
  573. REFCOUNT, (
  574. "http!UlReferenceHttpConnection conn=%p refcount=%d\n",
  575. pHttpConnection,
  576. refCount)
  577. );
  578. } // UlReferenceHttpConnection
  579. VOID
  580. UlDereferenceHttpConnection(
  581. IN PUL_HTTP_CONNECTION pHttpConnection
  582. REFERENCE_DEBUG_FORMAL_PARAMS
  583. )
  584. {
  585. LONG refCount;
  586. ASSERT( pHttpConnection );
  587. ASSERT( pHttpConnection->RefCount > 0 );
  588. WRITE_REF_TRACE_LOG2(
  589. g_pHttpConnectionTraceLog,
  590. pHttpConnection->pConnection->pHttpTraceLog,
  591. REF_ACTION_DEREFERENCE_HTTP_CONNECTION,
  592. pHttpConnection->RefCount - 1,
  593. pHttpConnection,
  594. pFileName,
  595. LineNumber
  596. );
  597. refCount = InterlockedDecrement( &pHttpConnection->RefCount );
  598. UlTrace(
  599. REFCOUNT, (
  600. "http!UlDereferenceHttpConnection conn=%p refcount=%d\n",
  601. pHttpConnection,
  602. refCount)
  603. );
  604. if (refCount == 0)
  605. {
  606. //
  607. // now it's time to free the connection, we need to do
  608. // this at lower'd irql, let's check it out
  609. //
  610. UL_CALL_PASSIVE(
  611. &(pHttpConnection->WorkItem),
  612. &UlDeleteHttpConnection
  613. );
  614. }
  615. } // UlDereferenceHttpConnection
  616. /***************************************************************************++
  617. Routine Description:
  618. Frees all resources associated with the specified HTTP_CONNECTION.
  619. Arguments:
  620. pWorkItem - Supplies the HTTP_CONNECTION to free.
  621. --***************************************************************************/
  622. VOID
  623. UlDeleteHttpConnection(
  624. IN PUL_WORK_ITEM pWorkItem
  625. )
  626. {
  627. PLIST_ENTRY pEntry;
  628. PUL_HTTP_CONNECTION pHttpConnection;
  629. //
  630. // Sanity check.
  631. //
  632. PAGED_CODE();
  633. pHttpConnection = CONTAINING_RECORD(
  634. pWorkItem,
  635. UL_HTTP_CONNECTION,
  636. WorkItem
  637. );
  638. ASSERT( pHttpConnection != NULL &&
  639. pHttpConnection->Signature == UL_HTTP_CONNECTION_POOL_TAG );
  640. ASSERT(HTTP_IS_NULL_ID(&pHttpConnection->ConnectionId));
  641. ASSERT(pHttpConnection->TotalSendBytes == 0);
  642. WRITE_REF_TRACE_LOG2(
  643. g_pHttpConnectionTraceLog,
  644. pHttpConnection->pConnection->pHttpTraceLog,
  645. REF_ACTION_DESTROY_HTTP_CONNECTION,
  646. 0,
  647. pHttpConnection,
  648. __FILE__,
  649. __LINE__
  650. );
  651. //
  652. // The request is gone by now
  653. //
  654. ASSERT(pHttpConnection->pRequest == NULL);
  655. //
  656. // remove from Timeout Timer Wheel(c)
  657. //
  658. UlTimeoutRemoveTimerWheelEntry(
  659. &pHttpConnection->TimeoutInfo
  660. );
  661. //
  662. // delete the buffer list
  663. //
  664. pEntry = pHttpConnection->BufferHead.Flink;
  665. while (pEntry != &pHttpConnection->BufferHead)
  666. {
  667. PUL_REQUEST_BUFFER pBuffer;
  668. //
  669. // Get the object
  670. //
  671. pBuffer = CONTAINING_RECORD(
  672. pEntry,
  673. UL_REQUEST_BUFFER,
  674. ListEntry
  675. );
  676. //
  677. // remember the next one
  678. //
  679. pEntry = pEntry->Flink;
  680. //
  681. // unlink it
  682. //
  683. UlFreeRequestBuffer(pBuffer);
  684. }
  685. ASSERT(IsListEmpty(&pHttpConnection->BufferHead));
  686. //
  687. // get rid of any bindings we're keeping
  688. //
  689. while (!IsListEmpty(&pHttpConnection->BindingHead))
  690. {
  691. PUL_APOOL_PROC_BINDING pBinding;
  692. //
  693. // Get the object
  694. //
  695. pEntry = RemoveHeadList(&pHttpConnection->BindingHead);
  696. pBinding = CONTAINING_RECORD(
  697. pEntry,
  698. UL_APOOL_PROC_BINDING,
  699. BindingEntry
  700. );
  701. ASSERT( IS_VALID_PROC_BINDING(pBinding) );
  702. //
  703. // free it
  704. //
  705. UlpFreeProcBinding(pBinding);
  706. }
  707. //
  708. // get rid of our resource
  709. //
  710. UlDeletePushLock(&pHttpConnection->PushLock);
  711. //
  712. // Attempt to remove any QoS filter on this connection,
  713. // if BWT is enabled.
  714. //
  715. if (pHttpConnection->BandwidthThrottlingEnabled)
  716. {
  717. UlTcDeleteFilter(pHttpConnection);
  718. }
  719. //
  720. // Decrement the global connection limit. Its safe to decrement for
  721. // global case because http object doesn't even allocated for global
  722. // rejection which happens as tcp refusal early when acepting the
  723. // connection request.
  724. //
  725. UlDecrementConnections( &g_CurrentConnections );
  726. //
  727. // Decrement the site connections and let our ref go away. If the
  728. // pConnectionCountEntry is not null, we have been accepted.
  729. //
  730. if (pHttpConnection->pConnectionCountEntry)
  731. {
  732. UlDecrementConnections(
  733. &pHttpConnection->pConnectionCountEntry->CurConnections );
  734. DEREFERENCE_CONNECTION_COUNT_ENTRY(pHttpConnection->pConnectionCountEntry);
  735. pHttpConnection->pConnectionCountEntry = NULL;
  736. }
  737. //
  738. // Remove final (previous) site counter entry reference
  739. // (matches reference in UlSendCachedResponse/UlDeliverHttpRequest)
  740. //
  741. if (pHttpConnection->pPrevSiteCounters)
  742. {
  743. UlDecSiteCounter(
  744. pHttpConnection->pPrevSiteCounters,
  745. HttpSiteCounterCurrentConns
  746. );
  747. DEREFERENCE_SITE_COUNTER_ENTRY(pHttpConnection->pPrevSiteCounters);
  748. pHttpConnection->pPrevSiteCounters = NULL;
  749. }
  750. //
  751. // free the memory
  752. //
  753. pHttpConnection->Signature = MAKE_FREE_SIGNATURE(
  754. UL_HTTP_CONNECTION_POOL_TAG
  755. );
  756. //
  757. // let go the UL_CONNECTION
  758. //
  759. DEREFERENCE_CONNECTION( pHttpConnection->pConnection );
  760. } // UlDeleteHttpConnection
  761. /***************************************************************************++
  762. Routine Description:
  763. Arguments:
  764. Return Value:
  765. NTSTATUS - Completion status.
  766. --***************************************************************************/
  767. NTSTATUS
  768. UlpCreateHttpRequest(
  769. IN PUL_HTTP_CONNECTION pHttpConnection,
  770. OUT PUL_INTERNAL_REQUEST *ppRequest
  771. )
  772. {
  773. PUL_INTERNAL_REQUEST pRequest = NULL;
  774. //
  775. // Sanity check.
  776. //
  777. PAGED_CODE();
  778. pRequest = UlPplAllocateInternalRequest();
  779. if (pRequest == NULL)
  780. {
  781. return STATUS_NO_MEMORY;
  782. }
  783. ASSERT( pRequest->Signature == UL_INTERNAL_REQUEST_POOL_TAG );
  784. //
  785. // Keep a reference to the connection.
  786. //
  787. UL_REFERENCE_HTTP_CONNECTION( pHttpConnection );
  788. pRequest->Signature = UL_INTERNAL_REQUEST_POOL_TAG;
  789. pRequest->pHttpConn = pHttpConnection;
  790. pRequest->ConnectionId = pHttpConnection->ConnectionId;
  791. pRequest->Secure = pHttpConnection->SecureConnection;
  792. //
  793. // Set first request flag, used to decide if we need to complete demand
  794. // start IRPs.
  795. //
  796. ASSERT( pHttpConnection->pCurrentBuffer );
  797. if (0 == pHttpConnection->pCurrentBuffer->BufferNumber &&
  798. 0 == pHttpConnection->pCurrentBuffer->ParsedBytes)
  799. {
  800. pRequest->FirstRequest = TRUE;
  801. }
  802. else
  803. {
  804. pRequest->FirstRequest = FALSE;
  805. }
  806. //
  807. // Grab the raw connection id off the UL_CONNECTION.
  808. //
  809. pRequest->RawConnectionId = pHttpConnection->pConnection->FilterInfo.ConnectionId;
  810. CREATE_REF_TRACE_LOG( pRequest->pTraceLog,
  811. 32 - REF_TRACE_OVERHEAD,
  812. 0, TRACELOG_LOW_PRIORITY,
  813. UL_INTERNAL_REQUEST_REF_TRACE_LOG_POOL_TAG );
  814. //
  815. // UL Handle the request received Event. Record the Connection Id
  816. // and the client IP address.
  817. //
  818. if (ETW_LOG_RESOURCE())
  819. {
  820. UlEtwTraceEvent(
  821. &UlTransGuid,
  822. ETW_TYPE_START,
  823. (PVOID) &pRequest,
  824. sizeof(PVOID),
  825. (PVOID) &pHttpConnection->pConnection->AddressType,
  826. sizeof(USHORT),
  827. (PVOID) &pHttpConnection->pConnection->RemoteAddress,
  828. pHttpConnection->pConnection->AddressLength,
  829. NULL,
  830. 0
  831. );
  832. }
  833. //
  834. // Increase the number of outstanding requests on this connection.
  835. //
  836. InterlockedIncrement((PLONG) &pHttpConnection->PipelinedRequests);
  837. //
  838. // return it
  839. //
  840. *ppRequest = pRequest;
  841. UlTrace(REFCOUNT, (
  842. "http!UlpCreateHttpRequest req=%p refcount=%d\n",
  843. pRequest,
  844. pRequest->RefCount
  845. ));
  846. return STATUS_SUCCESS;
  847. } // UlpCreateHttpRequest
  848. VOID
  849. UlReferenceHttpRequest(
  850. IN PVOID pObject
  851. REFERENCE_DEBUG_FORMAL_PARAMS
  852. )
  853. {
  854. LONG refCount;
  855. PUL_INTERNAL_REQUEST pRequest = (PUL_INTERNAL_REQUEST) pObject;
  856. refCount = InterlockedIncrement( &pRequest->RefCount );
  857. WRITE_REF_TRACE_LOG2(
  858. g_pHttpRequestTraceLog,
  859. pRequest->pTraceLog,
  860. REF_ACTION_REFERENCE_HTTP_REQUEST,
  861. refCount,
  862. pRequest,
  863. pFileName,
  864. LineNumber
  865. );
  866. UlTrace(
  867. REFCOUNT, (
  868. "http!UlReferenceHttpRequest req=%p refcount=%d\n",
  869. pRequest,
  870. refCount)
  871. );
  872. } // UlReferenceHttpRequest
  873. VOID
  874. UlDereferenceHttpRequest(
  875. IN PUL_INTERNAL_REQUEST pRequest
  876. REFERENCE_DEBUG_FORMAL_PARAMS
  877. )
  878. {
  879. LONG refCount;
  880. WRITE_REF_TRACE_LOG2(
  881. g_pHttpRequestTraceLog,
  882. pRequest->pTraceLog,
  883. REF_ACTION_DEREFERENCE_HTTP_REQUEST,
  884. pRequest->RefCount - 1,
  885. pRequest,
  886. pFileName,
  887. LineNumber
  888. );
  889. refCount = InterlockedDecrement( &pRequest->RefCount );
  890. UlTrace(
  891. REFCOUNT, (
  892. "http!UlDereferenceHttpRequest req=%p refcount=%d\n",
  893. pRequest,
  894. refCount)
  895. );
  896. if (refCount == 0)
  897. {
  898. UL_CALL_PASSIVE(
  899. &pRequest->WorkItem,
  900. &UlpFreeHttpRequest
  901. );
  902. }
  903. } // UlDereferenceHttpRequest
  904. /***************************************************************************++
  905. Routine Description:
  906. Cancels all pending http io.
  907. Arguments:
  908. pRequest - Supplies the HTTP_REQUEST.
  909. --***************************************************************************/
  910. VOID
  911. UlCancelRequestIo(
  912. IN PUL_INTERNAL_REQUEST pRequest
  913. )
  914. {
  915. PLIST_ENTRY pEntry;
  916. PIRP pIrp;
  917. PUL_CHUNK_TRACKER pTracker;
  918. //
  919. // Sanity check.
  920. //
  921. PAGED_CODE();
  922. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  923. ASSERT(UlDbgPushLockOwnedExclusive(&pRequest->pHttpConn->PushLock));
  924. //
  925. // Mark the request as InCleanup, so additional IRPs
  926. // on the request will not be queued.
  927. //
  928. pRequest->InCleanup = 1;
  929. //
  930. // tank all pending io on this request
  931. //
  932. while (IsListEmpty(&pRequest->ResponseHead) == FALSE)
  933. {
  934. //
  935. // Pop the SendHttpResponse/EntityBody IRP off the list.
  936. //
  937. pEntry = RemoveHeadList(&pRequest->ResponseHead);
  938. pTracker = CONTAINING_RECORD(pEntry, UL_CHUNK_TRACKER, ListEntry);
  939. ASSERT(IS_VALID_CHUNK_TRACKER(pTracker));
  940. //
  941. // Complete the send with STATUS_CONNECTION_RESET. This is better
  942. // than using STATUS_CANCELLED since we always reset the connection
  943. // upon hitting an error.
  944. //
  945. UlCompleteSendResponse(pTracker, STATUS_CONNECTION_RESET);
  946. }
  947. while (IsListEmpty(&pRequest->IrpHead) == FALSE)
  948. {
  949. //
  950. // Pop the ReceiveEntityBody IRP off the list.
  951. //
  952. pEntry = RemoveHeadList(&pRequest->IrpHead);
  953. pEntry->Blink = pEntry->Flink = NULL;
  954. pIrp = CONTAINING_RECORD(pEntry, IRP, Tail.Overlay.ListEntry);
  955. ASSERT(IS_VALID_IRP(pIrp));
  956. //
  957. // pop the cancel routine
  958. //
  959. if (IoSetCancelRoutine(pIrp, NULL) == NULL)
  960. {
  961. //
  962. // IoCancelIrp pop'd it first
  963. //
  964. // ok to just ignore this irp, it's been pop'd off the queue
  965. // and will be completed in the cancel routine.
  966. //
  967. // keep looping
  968. //
  969. pIrp = NULL;
  970. }
  971. else
  972. {
  973. PUL_INTERNAL_REQUEST pIrpRequest;
  974. //
  975. // cancel it. even if pIrp->Cancel == TRUE we are supposed to
  976. // complete it, our cancel routine will never run.
  977. //
  978. pIrpRequest = (PUL_INTERNAL_REQUEST)(
  979. IoGetCurrentIrpStackLocation(pIrp)->
  980. Parameters.DeviceIoControl.Type3InputBuffer
  981. );
  982. ASSERT(pIrpRequest == pRequest);
  983. UL_DEREFERENCE_INTERNAL_REQUEST(pIrpRequest);
  984. IoGetCurrentIrpStackLocation(pIrp)->
  985. Parameters.DeviceIoControl.Type3InputBuffer = NULL;
  986. pIrp->IoStatus.Status = STATUS_CANCELLED;
  987. pIrp->IoStatus.Information = 0;
  988. UlCompleteRequest(pIrp, IO_NO_INCREMENT);
  989. pIrp = NULL;
  990. }
  991. } // while (IsListEmpty(&pRequest->IrpHead) == FALSE)
  992. } // UlCancelRequestIo
  993. /***************************************************************************++
  994. Routine Description:
  995. Frees all resources associated with the specified UL_INTERNAL_REQUEST.
  996. Arguments:
  997. pRequest - Supplies the UL_INTERNAL_REQUEST to free.
  998. --***************************************************************************/
  999. VOID
  1000. UlpFreeHttpRequest(
  1001. IN PUL_WORK_ITEM pWorkItem
  1002. )
  1003. {
  1004. PUL_INTERNAL_REQUEST pRequest;
  1005. PLIST_ENTRY pEntry;
  1006. ULONG i;
  1007. //
  1008. // Sanity check.
  1009. //
  1010. PAGED_CODE();
  1011. pRequest = CONTAINING_RECORD(
  1012. pWorkItem,
  1013. UL_INTERNAL_REQUEST,
  1014. WorkItem
  1015. );
  1016. //
  1017. // There should not be a LogData hanging around.
  1018. //
  1019. ASSERT(!pRequest->pLogData);
  1020. //
  1021. // our opaque id should already be free'd (UlDeleteOpaqueIds)
  1022. //
  1023. ASSERT(HTTP_IS_NULL_ID(&pRequest->RequestId));
  1024. //
  1025. // free any known header buffers allocated
  1026. //
  1027. if (pRequest->HeadersAppended)
  1028. {
  1029. for (i = 0; i < HttpHeaderRequestMaximum; i++)
  1030. {
  1031. HTTP_HEADER_ID HeaderId = (HTTP_HEADER_ID)pRequest->HeaderIndex[i];
  1032. if (HeaderId == HttpHeaderRequestMaximum)
  1033. {
  1034. break;
  1035. }
  1036. ASSERT( pRequest->HeaderValid[HeaderId] );
  1037. if (pRequest->Headers[HeaderId].OurBuffer == 1)
  1038. {
  1039. UL_FREE_POOL(
  1040. pRequest->Headers[HeaderId].pHeader,
  1041. HEADER_VALUE_POOL_TAG
  1042. );
  1043. }
  1044. }
  1045. //
  1046. // and any unknown header buffers allocated
  1047. //
  1048. while (IsListEmpty(&pRequest->UnknownHeaderList) == FALSE)
  1049. {
  1050. PUL_HTTP_UNKNOWN_HEADER pUnknownHeader;
  1051. pEntry = RemoveHeadList(&pRequest->UnknownHeaderList);
  1052. pUnknownHeader = CONTAINING_RECORD(
  1053. pEntry,
  1054. UL_HTTP_UNKNOWN_HEADER,
  1055. List
  1056. );
  1057. if (pUnknownHeader->HeaderValue.OurBuffer == 1)
  1058. {
  1059. UL_FREE_POOL(
  1060. pUnknownHeader->HeaderValue.pHeader,
  1061. HEADER_VALUE_POOL_TAG
  1062. );
  1063. }
  1064. //
  1065. // Free the header structure
  1066. //
  1067. if (pUnknownHeader->HeaderValue.ExternalAllocated == 1)
  1068. {
  1069. UL_FREE_POOL(
  1070. pUnknownHeader,
  1071. UL_HTTP_UNKNOWN_HEADER_POOL_TAG
  1072. );
  1073. }
  1074. }
  1075. }
  1076. //
  1077. // there better not be any pending io, it would have been cancelled either
  1078. // in UlDeleteHttpConnection or in UlDetachProcessFromAppPool .
  1079. //
  1080. ASSERT(IsListEmpty(&pRequest->IrpHead));
  1081. ASSERT(pRequest->SendsPending == 0);
  1082. //
  1083. // dereferenc request buffers.
  1084. //
  1085. for (i = 0; i < pRequest->UsedRefBuffers; i++)
  1086. {
  1087. ASSERT( UL_IS_VALID_REQUEST_BUFFER(pRequest->pRefBuffers[i]) );
  1088. UL_DEREFERENCE_REQUEST_BUFFER(pRequest->pRefBuffers[i]);
  1089. }
  1090. if (pRequest->AllocRefBuffers > 1)
  1091. {
  1092. UL_FREE_POOL(
  1093. pRequest->pRefBuffers,
  1094. UL_REF_REQUEST_BUFFER_POOL_TAG
  1095. );
  1096. }
  1097. //
  1098. // free any url that was allocated
  1099. //
  1100. if (pRequest->CookedUrl.pUrl != NULL)
  1101. {
  1102. if (pRequest->CookedUrl.pUrl != pRequest->pUrlBuffer)
  1103. {
  1104. UL_FREE_POOL(pRequest->CookedUrl.pUrl, URL_POOL_TAG);
  1105. }
  1106. }
  1107. if (pRequest->CookedUrl.pRoutingToken != NULL)
  1108. {
  1109. if (pRequest->CookedUrl.pRoutingToken != pRequest->pDefaultRoutingTokenBuffer)
  1110. {
  1111. UL_FREE_POOL(pRequest->CookedUrl.pRoutingToken, URL_POOL_TAG);
  1112. }
  1113. }
  1114. //
  1115. // free any config group info
  1116. //
  1117. ASSERT( IS_VALID_URL_CONFIG_GROUP_INFO(&pRequest->ConfigInfo) );
  1118. ASSERT( pRequest->pHttpConn );
  1119. //
  1120. // Perf counters
  1121. // NOTE: Assumes cache & non-cache paths both go through here
  1122. // NOTE: If connection is refused the pConnectionCountEntry will be NULL
  1123. //
  1124. if (pRequest->ConfigInfo.pSiteCounters &&
  1125. pRequest->pHttpConn->pConnectionCountEntry)
  1126. {
  1127. PUL_SITE_COUNTER_ENTRY pCtr = pRequest->ConfigInfo.pSiteCounters;
  1128. UlAddSiteCounter64(
  1129. pCtr,
  1130. HttpSiteCounterBytesSent,
  1131. pRequest->BytesSent
  1132. );
  1133. UlAddSiteCounter64(
  1134. pCtr,
  1135. HttpSiteCounterBytesReceived,
  1136. pRequest->BytesReceived
  1137. );
  1138. UlAddSiteCounter64(
  1139. pCtr,
  1140. HttpSiteCounterBytesTransfered,
  1141. (pRequest->BytesSent + pRequest->BytesReceived)
  1142. );
  1143. }
  1144. //
  1145. // Release all the references from the UL_URL_CONFIG_GROUP_INFO.
  1146. //
  1147. if (pRequest->ConfigInfo.UrlInfoSet)
  1148. {
  1149. UlConfigGroupInfoRelease(&pRequest->ConfigInfo);
  1150. }
  1151. //
  1152. // Decrease the number of outstanding requests on this connection.
  1153. //
  1154. InterlockedDecrement((PLONG) &pRequest->pHttpConn->PipelinedRequests);
  1155. //
  1156. // release our reference to the connection
  1157. //
  1158. UL_DEREFERENCE_HTTP_CONNECTION( pRequest->pHttpConn );
  1159. pRequest->pHttpConn = NULL;
  1160. //
  1161. // release our reference to the process
  1162. //
  1163. if (pRequest->AppPool.pProcess)
  1164. {
  1165. DEREFERENCE_APP_POOL_PROCESS( pRequest->AppPool.pProcess );
  1166. pRequest->AppPool.pProcess = NULL;
  1167. }
  1168. //
  1169. // Free the object buffer
  1170. //
  1171. ASSERT( pRequest->Signature == UL_INTERNAL_REQUEST_POOL_TAG );
  1172. DESTROY_REF_TRACE_LOG( pRequest->pTraceLog,
  1173. UL_INTERNAL_REQUEST_REF_TRACE_LOG_POOL_TAG );
  1174. //
  1175. // Initialize the Request structure before putting it on the free list
  1176. //
  1177. INIT_HTTP_REQUEST( pRequest );
  1178. UlPplFreeInternalRequest( pRequest );
  1179. }
  1180. /***************************************************************************++
  1181. Routine Description:
  1182. Allocates and initializes a new UL_REQUEST_BUFFER.
  1183. Arguments:
  1184. BufferSize - size of the new buffer in bytes
  1185. --***************************************************************************/
  1186. PUL_REQUEST_BUFFER
  1187. UlCreateRequestBuffer(
  1188. ULONG BufferSize,
  1189. ULONG BufferNumber,
  1190. BOOLEAN UseLookaside
  1191. )
  1192. {
  1193. PUL_REQUEST_BUFFER pBuffer;
  1194. BOOLEAN FromLookaside;
  1195. if (UseLookaside && BufferSize <= DEFAULT_MAX_REQUEST_BUFFER_SIZE)
  1196. {
  1197. BufferSize = DEFAULT_MAX_REQUEST_BUFFER_SIZE;
  1198. FromLookaside = TRUE;
  1199. pBuffer = UlPplAllocateRequestBuffer();
  1200. }
  1201. else
  1202. {
  1203. FromLookaside = FALSE;
  1204. pBuffer = UL_ALLOCATE_STRUCT_WITH_SPACE(
  1205. NonPagedPool,
  1206. UL_REQUEST_BUFFER,
  1207. BufferSize,
  1208. UL_REQUEST_BUFFER_POOL_TAG
  1209. );
  1210. }
  1211. if (pBuffer == NULL)
  1212. {
  1213. return NULL;
  1214. }
  1215. RtlZeroMemory((PCHAR)pBuffer, sizeof(UL_REQUEST_BUFFER));
  1216. pBuffer->Signature = UL_REQUEST_BUFFER_POOL_TAG;
  1217. UlInitializeWorkItem(&pBuffer->WorkItem);
  1218. UlTrace(HTTP_IO, (
  1219. "http!UlCreateRequestBuffer buff=%p num=#%d size=%d\n",
  1220. pBuffer,
  1221. BufferNumber,
  1222. BufferSize
  1223. ));
  1224. pBuffer->RefCount = 1;
  1225. pBuffer->AllocBytes = BufferSize;
  1226. pBuffer->BufferNumber = BufferNumber;
  1227. pBuffer->FromLookaside = FromLookaside;
  1228. return pBuffer;
  1229. } // UlCreateRequestBuffer
  1230. /***************************************************************************++
  1231. Routine Description:
  1232. Removes a request buffer from the buffer list and destroys it.
  1233. Arguments:
  1234. pBuffer - the buffer to be deleted
  1235. --***************************************************************************/
  1236. VOID
  1237. UlFreeRequestBuffer(
  1238. PUL_REQUEST_BUFFER pBuffer
  1239. )
  1240. {
  1241. ASSERT( UL_IS_VALID_REQUEST_BUFFER( pBuffer ) );
  1242. UlTrace(HTTP_IO, (
  1243. "http!UlFreeRequestBuffer(pBuffer = %p, Num = #%d)\n",
  1244. pBuffer,
  1245. pBuffer->BufferNumber
  1246. ));
  1247. if (pBuffer->ListEntry.Flink != NULL)
  1248. {
  1249. RemoveEntryList(&pBuffer->ListEntry);
  1250. pBuffer->ListEntry.Blink = pBuffer->ListEntry.Flink = NULL;
  1251. }
  1252. UL_DEREFERENCE_REQUEST_BUFFER(pBuffer);
  1253. } // UlFreeRequestBuffer
  1254. //
  1255. // Private functions.
  1256. //
  1257. /***************************************************************************++
  1258. Routine Description:
  1259. Retrieves a binding set with UlBindConnectionToProcess.
  1260. Arguments:
  1261. pHttpConnection - the connection to query
  1262. pAppPool - the key to use for the lookup
  1263. Return Values:
  1264. A pointer to the bound process if one was found. NULL otherwise.
  1265. --***************************************************************************/
  1266. PUL_APP_POOL_PROCESS
  1267. UlQueryProcessBinding(
  1268. IN PUL_HTTP_CONNECTION pHttpConnection,
  1269. IN PUL_APP_POOL_OBJECT pAppPool
  1270. )
  1271. {
  1272. PUL_APOOL_PROC_BINDING pBinding;
  1273. PUL_APP_POOL_PROCESS pProcess = NULL;
  1274. //
  1275. // Sanity check
  1276. //
  1277. ASSERT( UL_IS_VALID_HTTP_CONNECTION( pHttpConnection ) );
  1278. ASSERT( UlDbgSpinLockOwned( &pAppPool->SpinLock ) );
  1279. pBinding = UlpFindProcBinding(pHttpConnection, pAppPool);
  1280. if (pBinding) {
  1281. pProcess = pBinding->pProcess;
  1282. }
  1283. return pProcess;
  1284. }
  1285. /***************************************************************************++
  1286. Routine Description:
  1287. Allocates and builds a UL_APOOL_PROC_BINDING object.
  1288. Arguments:
  1289. pAppPool - the lookup key
  1290. pProcess - the binding
  1291. Return Values:
  1292. a pointer to the allocated object, or NULL on failure
  1293. --***************************************************************************/
  1294. PUL_APOOL_PROC_BINDING
  1295. UlpCreateProcBinding(
  1296. IN PUL_APP_POOL_OBJECT pAppPool,
  1297. IN PUL_APP_POOL_PROCESS pProcess
  1298. )
  1299. {
  1300. PUL_APOOL_PROC_BINDING pBinding;
  1301. ASSERT( UlDbgSpinLockOwned( &pAppPool->SpinLock ) );
  1302. ASSERT( pAppPool->NumberActiveProcesses > 1 );
  1303. //
  1304. // CODEWORK: lookaside
  1305. //
  1306. pBinding = UL_ALLOCATE_STRUCT(
  1307. NonPagedPool,
  1308. UL_APOOL_PROC_BINDING,
  1309. UL_APOOL_PROC_BINDING_POOL_TAG
  1310. );
  1311. if (pBinding) {
  1312. pBinding->Signature = UL_APOOL_PROC_BINDING_POOL_TAG;
  1313. pBinding->pAppPool = pAppPool;
  1314. pBinding->pProcess = pProcess;
  1315. UlTrace(ROUTING, (
  1316. "http!UlpCreateProcBinding(\n"
  1317. " pAppPool = %p\n"
  1318. " pProcess = %p )\n"
  1319. " pBinding = %p\n",
  1320. pAppPool,
  1321. pProcess,
  1322. pBinding
  1323. ));
  1324. }
  1325. return pBinding;
  1326. }
  1327. /***************************************************************************++
  1328. Routine Description:
  1329. Gets rid of a proc binding
  1330. Arguments:
  1331. pBinding - the binding to get rid of
  1332. --***************************************************************************/
  1333. VOID
  1334. UlpFreeProcBinding(
  1335. IN PUL_APOOL_PROC_BINDING pBinding
  1336. )
  1337. {
  1338. UL_FREE_POOL_WITH_SIG(pBinding, UL_APOOL_PROC_BINDING_POOL_TAG);
  1339. }
  1340. /***************************************************************************++
  1341. Routine Description:
  1342. Searches a connection's list of bindings for one that has the right
  1343. app pool key
  1344. Arguments:
  1345. pHttpConnection - the connection to search
  1346. pAppPool - the key
  1347. Return Values:
  1348. The binding if found or NULL if not found.
  1349. --***************************************************************************/
  1350. PUL_APOOL_PROC_BINDING
  1351. UlpFindProcBinding(
  1352. IN PUL_HTTP_CONNECTION pHttpConnection,
  1353. IN PUL_APP_POOL_OBJECT pAppPool
  1354. )
  1355. {
  1356. PLIST_ENTRY pEntry;
  1357. PUL_APOOL_PROC_BINDING pBinding = NULL;
  1358. //
  1359. // Sanity check
  1360. //
  1361. ASSERT( UlDbgSpinLockOwned( &pAppPool->SpinLock ) );
  1362. ASSERT( UL_IS_VALID_HTTP_CONNECTION(pHttpConnection) );
  1363. pEntry = pHttpConnection->BindingHead.Flink;
  1364. while (pEntry != &pHttpConnection->BindingHead) {
  1365. pBinding = CONTAINING_RECORD(
  1366. pEntry,
  1367. UL_APOOL_PROC_BINDING,
  1368. BindingEntry
  1369. );
  1370. ASSERT( IS_VALID_PROC_BINDING(pBinding) );
  1371. if (pBinding->pAppPool == pAppPool) {
  1372. //
  1373. // got it!
  1374. //
  1375. break;
  1376. }
  1377. pEntry = pEntry->Flink;
  1378. }
  1379. return pBinding;
  1380. }
  1381. /***************************************************************************++
  1382. Routine Description:
  1383. Removes an HTTP request from all lists and cleans up it's opaque id.
  1384. Arguments:
  1385. pRequest - the request to be unlinked
  1386. --***************************************************************************/
  1387. VOID
  1388. UlUnlinkHttpRequest(
  1389. IN PUL_INTERNAL_REQUEST pRequest
  1390. )
  1391. {
  1392. ASSERT(UlDbgPushLockOwnedExclusive(&pRequest->pHttpConn->PushLock));
  1393. //
  1394. // cancel i/o
  1395. //
  1396. UlCancelRequestIo(pRequest);
  1397. //
  1398. // delete its opaque id
  1399. //
  1400. if (HTTP_IS_NULL_ID(&pRequest->RequestId) == FALSE)
  1401. {
  1402. UlFreeRequestId(pRequest);
  1403. HTTP_SET_NULL_ID(&pRequest->RequestId);
  1404. //
  1405. // it is still referenced by this connection
  1406. //
  1407. ASSERT(pRequest->RefCount > 1);
  1408. UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
  1409. }
  1410. //
  1411. // deref it
  1412. //
  1413. UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
  1414. }
  1415. /***************************************************************************++
  1416. Routine Description:
  1417. If we need to keep the connection around (after disconnect/reset) to be
  1418. able to get the logging data from worker process, we put the connection
  1419. in zombie list until last sendresponse with the logging data arrives or
  1420. until timeout code decides on purging this connection completely.
  1421. Http Connection lock must be acquired exclusive before calling this
  1422. function.
  1423. Arguments:
  1424. pHttpConnection - the http connection to be inserted to the zombie list.
  1425. Return Value:
  1426. STATUS_INVALID_DEVICE_STATE - If the zombie list length is reached the
  1427. allowed value.
  1428. STATUS_SUCCESS - If the connection has been inserted to the zombie list
  1429. and marked as zombie.
  1430. --***************************************************************************/
  1431. NTSTATUS
  1432. UlpZombifyHttpConnection(
  1433. IN PUL_HTTP_CONNECTION pHttpConnection
  1434. )
  1435. {
  1436. PUL_INTERNAL_REQUEST pRequest = pHttpConnection->pRequest;
  1437. PLOCKED_LIST_HEAD pListHead = &g_ZombieConnectionList.LockList;
  1438. KIRQL OldIrql;
  1439. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  1440. ASSERT(UlDbgPushLockOwnedExclusive(&pHttpConnection->PushLock));
  1441. //
  1442. // Try to add the new zombie connection by paying attention to the
  1443. // zombie list limit restriction.
  1444. //
  1445. UlAcquireSpinLock(&pListHead->SpinLock, &OldIrql);
  1446. ASSERT(NULL == pHttpConnection->ZombieListEntry.Flink);
  1447. if (HTTP_LIMIT_INFINITE != g_UlMaxZombieHttpConnectionCount &&
  1448. (pListHead->Count + 1) >= g_UlMaxZombieHttpConnectionCount)
  1449. {
  1450. //
  1451. // We are already at the maximum possible zombie list size.
  1452. // Refuse the connection and let it get destroyed.
  1453. //
  1454. #ifdef ENABLE_HTTP_CONN_STATS
  1455. InterlockedIncrement(
  1456. (PLONG) &g_ZombieConnectionList.TotalZombieRefusal
  1457. );
  1458. #endif
  1459. UlReleaseSpinLock(&pListHead->SpinLock, OldIrql);
  1460. return STATUS_INVALID_DEVICE_STATE;
  1461. }
  1462. //
  1463. // Double-check if the AppPool has been detached inside the lock for
  1464. // g_ZombieConnectionList. Because we purge all zombie connections
  1465. // related to the process after it is detached, we guarantee we never
  1466. // have a dangling http connection in the zombie list when a process
  1467. // is detached. Also check if the listening endpoint has been closed.
  1468. //
  1469. if (!UlCheckAppPoolState(pRequest) ||
  1470. !UlCheckListeningEndpointState(pHttpConnection->pConnection))
  1471. {
  1472. #ifdef ENABLE_HTTP_CONN_STATS
  1473. InterlockedIncrement(
  1474. (PLONG) &g_ZombieConnectionList.TotalZombieRefusal
  1475. );
  1476. #endif
  1477. UlReleaseSpinLock(&pListHead->SpinLock, OldIrql);
  1478. return STATUS_INVALID_DEVICE_STATE;
  1479. }
  1480. pListHead->Count += 1;
  1481. InsertTailList(
  1482. &pListHead->ListHead,
  1483. &pHttpConnection->ZombieListEntry
  1484. );
  1485. //
  1486. // Remember the process that the connection was delivered so we can
  1487. // force-purge later when the process is detached. This needs to be
  1488. // done inside the lock to synchronize with UlPurgeZombieConnections.
  1489. //
  1490. ASSERT(IS_VALID_AP_PROCESS(pRequest->AppPool.pProcess));
  1491. pHttpConnection->pAppPoolProcess = pRequest->AppPool.pProcess;
  1492. UlReleaseSpinLock(&pListHead->SpinLock, OldIrql);
  1493. #ifdef ENABLE_HTTP_CONN_STATS
  1494. {
  1495. LONG ZombieCount;
  1496. ZombieCount = (LONG) g_ZombieConnectionList.LockList.Count;
  1497. if (ZombieCount > (LONG) g_ZombieConnectionList.MaxZombieCount)
  1498. {
  1499. InterlockedExchange(
  1500. (PLONG) &g_ZombieConnectionList.MaxZombieCount,
  1501. ZombieCount
  1502. );
  1503. }
  1504. InterlockedIncrement((PLONG) &g_ZombieConnectionList.TotalZombieCount);
  1505. }
  1506. #endif
  1507. UlTrace(HTTP_IO, (
  1508. "Http!UlZombifyHttpRequest: httpconn=%p & request=%p \n",
  1509. pHttpConnection, pRequest
  1510. ));
  1511. //
  1512. // If we got far enough to deliver the request then unlink it from
  1513. // the app pool. So that queued Irps release their references on
  1514. // the request.
  1515. //
  1516. if (pRequest->ConfigInfo.pAppPool)
  1517. {
  1518. UlUnlinkRequestFromProcess(
  1519. pRequest->ConfigInfo.pAppPool,
  1520. pRequest
  1521. );
  1522. }
  1523. //
  1524. // Cancel (Receive) I/O
  1525. //
  1526. UlCancelRequestIo(pRequest);
  1527. //
  1528. // Keep its opaque id while its siting on the zombie list. But
  1529. // owner is now the zombie list rather than the http connection.
  1530. //
  1531. ASSERT(pRequest->RefCount > 1);
  1532. //
  1533. // Mark that now we are in the zombie list.
  1534. //
  1535. InterlockedExchange((PLONG) &pHttpConnection->Zombified, 1);
  1536. return STATUS_SUCCESS;
  1537. }
  1538. /***************************************************************************++
  1539. Routine Description:
  1540. Removes the http connection from the zombie list.
  1541. Caller should hold the zombie list spinlock prior to calling this
  1542. function.
  1543. Arguments:
  1544. pHttpConnection - the http connection to be removed frm zombie list.
  1545. --***************************************************************************/
  1546. VOID
  1547. UlpRemoveZombieHttpConnection(
  1548. IN PUL_HTTP_CONNECTION pHttpConn
  1549. )
  1550. {
  1551. LONG NewCount;
  1552. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConn));
  1553. ASSERT(UlDbgSpinLockOwned(
  1554. &g_ZombieConnectionList.LockList.SpinLock));
  1555. //
  1556. // This connection should be in the zombie list.
  1557. //
  1558. ASSERT(pHttpConn->Zombified == TRUE);
  1559. ASSERT(pHttpConn->ZombieListEntry.Flink != NULL);
  1560. //
  1561. // Remove this from the zombie list of http connections.
  1562. //
  1563. RemoveEntryList(&pHttpConn->ZombieListEntry);
  1564. pHttpConn->ZombieListEntry.Flink = NULL;
  1565. NewCount = InterlockedDecrement(
  1566. (PLONG) &g_ZombieConnectionList.LockList.Count);
  1567. ASSERT(NewCount >= 0);
  1568. }
  1569. /***************************************************************************++
  1570. Routine Description:
  1571. Once the due SendResponse happens or timeout happens (whichever the
  1572. earliest) then we remove this connection from zombie list and cleanup
  1573. it and it's request as well.
  1574. You should hold the http connection lock exclusive before calling
  1575. this function.
  1576. You should also hold the zombie list lock prior to calling this function.
  1577. Arguments:
  1578. pHttpConnection - the http connection to be removed frm zombie list.
  1579. --***************************************************************************/
  1580. VOID
  1581. UlpCleanupZombieHttpConnection(
  1582. IN PUL_HTTP_CONNECTION pHttpConnection
  1583. )
  1584. {
  1585. PUL_INTERNAL_REQUEST pRequest;
  1586. //
  1587. // Both connection and request should be in the good shape.
  1588. //
  1589. pRequest = pHttpConnection->pRequest;
  1590. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  1591. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConnection));
  1592. ASSERT(UlDbgPushLockOwnedExclusive(&pHttpConnection->PushLock));
  1593. UlTrace(HTTP_IO, (
  1594. "Http!UlCleanupZombieHttpConnection: httpconn=%p & request=%p \n",
  1595. pHttpConnection, pRequest
  1596. ));
  1597. //
  1598. // Release Request's opaque id and it's refcount.
  1599. //
  1600. if (!HTTP_IS_NULL_ID(&pRequest->RequestId))
  1601. {
  1602. UlFreeRequestId(pRequest);
  1603. HTTP_SET_NULL_ID(&pRequest->RequestId);
  1604. UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
  1605. }
  1606. //
  1607. // Release httpconn's refcount on request.
  1608. //
  1609. UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
  1610. pHttpConnection->pRequest = NULL;
  1611. //
  1612. // Release httpconn's opaque id and its refcount.
  1613. //
  1614. if (!HTTP_IS_NULL_ID(&pHttpConnection->ConnectionId))
  1615. {
  1616. UlFreeOpaqueId(
  1617. pHttpConnection->ConnectionId,
  1618. UlOpaqueIdTypeHttpConnection
  1619. );
  1620. HTTP_SET_NULL_ID(&pHttpConnection->ConnectionId);
  1621. UL_DEREFERENCE_HTTP_CONNECTION(pHttpConnection);
  1622. }
  1623. }
  1624. /***************************************************************************++
  1625. Routine Description:
  1626. DPC routine get called every 30 seconds. (Default)
  1627. Arguments:
  1628. Ignored
  1629. --***************************************************************************/
  1630. VOID
  1631. UlZombieTimerDpcRoutine(
  1632. PKDPC Dpc,
  1633. PVOID DeferredContext,
  1634. PVOID SystemArgument1,
  1635. PVOID SystemArgument2
  1636. )
  1637. {
  1638. UNREFERENCED_PARAMETER(Dpc);
  1639. UNREFERENCED_PARAMETER(DeferredContext);
  1640. UNREFERENCED_PARAMETER(SystemArgument1);
  1641. UNREFERENCED_PARAMETER(SystemArgument2);
  1642. //
  1643. // Queue a worker for terminating the timed out zombie entries.
  1644. // Worker can acquire the individual http connection eresources.
  1645. // But do not queue a worker if there's already one running.
  1646. //
  1647. UlAcquireSpinLockAtDpcLevel(&g_ZombieConnectionList.TimerSpinLock);
  1648. if (g_ZombieConnectionList.TimerInitialized == TRUE)
  1649. {
  1650. if (FALSE == InterlockedExchange(
  1651. &g_ZombieConnectionList.TimerRunning, TRUE))
  1652. {
  1653. UL_QUEUE_WORK_ITEM(
  1654. &g_ZombieConnectionList.WorkItem,
  1655. &UlpZombieTimerWorker
  1656. );
  1657. }
  1658. }
  1659. UlReleaseSpinLockFromDpcLevel(&g_ZombieConnectionList.TimerSpinLock);
  1660. }
  1661. /***************************************************************************++
  1662. Routine Description:
  1663. Purge the zombie connections according to pPurgeRoutine or
  1664. pHttpConn->ZombieExpired.
  1665. Arguments:
  1666. pPurgeRoutine - purge routine to decide which connection to be purged.
  1667. pPurgeContext - context passed to the purge routine.
  1668. --***************************************************************************/
  1669. VOID
  1670. UlPurgeZombieConnections(
  1671. IN PUL_PURGE_ROUTINE pPurgeRoutine,
  1672. IN PVOID pPurgeContext
  1673. )
  1674. {
  1675. KIRQL OldIrql;
  1676. PLIST_ENTRY pLink;
  1677. PUL_HTTP_CONNECTION pHttpConn;
  1678. LIST_ENTRY TempZombieList;
  1679. PLOCKED_LIST_HEAD pList;
  1680. BOOLEAN ForceExpire;
  1681. //
  1682. // Init temp zombie list
  1683. //
  1684. InitializeListHead(&TempZombieList);
  1685. #ifdef ENABLE_HTTP_CONN_STATS
  1686. UlTraceVerbose(HTTP_IO,
  1687. ("Http!UlPurgeZombieConnections, Stats:\n"
  1688. "\tpPurgeRoutine = %p\n"
  1689. "\tpPurgeContext = %p\n"
  1690. "\tCurrentZombieCount = %lu\n"
  1691. "\tMaxZombieCount = %lu\n"
  1692. "\tTotalConnections = %lu\n"
  1693. "\tTotalZombieCount = %lu\n"
  1694. "\tTotalZombieRefusal = %lu\n",
  1695. pPurgeRoutine,
  1696. pPurgeContext,
  1697. g_ZombieConnectionList.LockList.Count,
  1698. g_ZombieConnectionList.MaxZombieCount,
  1699. g_ZombieConnectionList.TotalCount,
  1700. g_ZombieConnectionList.TotalZombieCount,
  1701. g_ZombieConnectionList.TotalZombieRefusal
  1702. ));
  1703. #endif
  1704. pList = &g_ZombieConnectionList.LockList;
  1705. UlAcquireSpinLock(&pList->SpinLock, &OldIrql);
  1706. pLink = pList->ListHead.Flink;
  1707. while (pLink != &pList->ListHead)
  1708. {
  1709. pHttpConn = CONTAINING_RECORD(
  1710. pLink,
  1711. UL_HTTP_CONNECTION,
  1712. ZombieListEntry
  1713. );
  1714. pLink = pLink->Flink;
  1715. ForceExpire = FALSE;
  1716. if (pPurgeRoutine)
  1717. {
  1718. ForceExpire = (*pPurgeRoutine)(pHttpConn, pPurgeContext);
  1719. if (!ForceExpire)
  1720. {
  1721. continue;
  1722. }
  1723. }
  1724. else
  1725. if (pHttpConn->ZombieExpired)
  1726. {
  1727. ForceExpire = TRUE;
  1728. }
  1729. else
  1730. {
  1731. //
  1732. // It will go away next time we wake up.
  1733. //
  1734. pHttpConn->ZombieExpired = TRUE;
  1735. }
  1736. if (ForceExpire)
  1737. {
  1738. //
  1739. // Guard against multiple cleanups by looking at the below flag.
  1740. // Final send may already be on the run and it will do the
  1741. // cleanup when it's done.
  1742. //
  1743. if (0 == InterlockedCompareExchange(
  1744. (PLONG) &pHttpConn->CleanedUp,
  1745. 1,
  1746. 0
  1747. ))
  1748. {
  1749. //
  1750. // Timed out already or we need to purge by force. Move to
  1751. // the temp list and cleanup outside of the spinlock.
  1752. //
  1753. UlpRemoveZombieHttpConnection(pHttpConn);
  1754. InsertTailList(&TempZombieList, &pHttpConn->ZombieListEntry);
  1755. }
  1756. }
  1757. }
  1758. UlReleaseSpinLock(&pList->SpinLock, OldIrql);
  1759. //
  1760. // Now cleanup the temp list.
  1761. //
  1762. pLink = TempZombieList.Flink;
  1763. while (pLink != &TempZombieList)
  1764. {
  1765. pHttpConn = CONTAINING_RECORD(
  1766. pLink,
  1767. UL_HTTP_CONNECTION,
  1768. ZombieListEntry
  1769. );
  1770. pLink = pLink->Flink;
  1771. UlAcquirePushLockExclusive(&pHttpConn->PushLock);
  1772. //
  1773. // Log the zombified connection before drop.
  1774. //
  1775. UlErrorLog(pHttpConn,
  1776. NULL,
  1777. ERROR_LOG_INFO_FOR_ZOMBIE_DROP,
  1778. ERROR_LOG_INFO_FOR_ZOMBIE_DROP_SIZE,
  1779. TRUE
  1780. );
  1781. UlpCleanupZombieHttpConnection(pHttpConn);
  1782. pHttpConn->ZombieListEntry.Flink = NULL;
  1783. UlReleasePushLockExclusive(&pHttpConn->PushLock);
  1784. //
  1785. // Release the zombie list's refcount on http connection.
  1786. //
  1787. UL_DEREFERENCE_HTTP_CONNECTION(pHttpConn);
  1788. }
  1789. }
  1790. /***************************************************************************++
  1791. Routine Description:
  1792. Timer function walks through the zombie list and decides on terminating
  1793. the old zombie connections once and for all.
  1794. Arguments:
  1795. WorkItem - Ignored.
  1796. --***************************************************************************/
  1797. VOID
  1798. UlpZombieTimerWorker(
  1799. IN PUL_WORK_ITEM pWorkItem
  1800. )
  1801. {
  1802. UNREFERENCED_PARAMETER(pWorkItem);
  1803. ASSERT(g_ZombieConnectionList.TimerRunning == TRUE);
  1804. //
  1805. // Purge all zombie connections that have expired.
  1806. //
  1807. UlPurgeZombieConnections(NULL, NULL);
  1808. //
  1809. // Finally allow other instances of the timer to run.
  1810. //
  1811. InterlockedExchange(&g_ZombieConnectionList.TimerRunning, FALSE);
  1812. }
  1813. /***************************************************************************++
  1814. Routine Description:
  1815. Purge the zombie list when we are shutting down.
  1816. Arguments:
  1817. Ignored
  1818. --***************************************************************************/
  1819. VOID
  1820. UlpTerminateZombieList(
  1821. VOID
  1822. )
  1823. {
  1824. KIRQL OldIrql;
  1825. PLIST_ENTRY pLink;
  1826. PUL_HTTP_CONNECTION pHttpConn;
  1827. LIST_ENTRY TempZombieList;
  1828. PLOCKED_LIST_HEAD pList;
  1829. //
  1830. // Init temp zombie list
  1831. //
  1832. InitializeListHead(&TempZombieList);
  1833. pList = &g_ZombieConnectionList.LockList;
  1834. UlTrace(HTTP_IO, (
  1835. "Http!UlpTerminateZombieList: Terminating for ZombieCount =%d\n",
  1836. pList->Count
  1837. ));
  1838. UlAcquireSpinLock(&pList->SpinLock, &OldIrql);
  1839. //
  1840. // Move the entire list to the temp zombie list and cleanup
  1841. // outside of the spinlock.
  1842. //
  1843. pLink = pList->ListHead.Flink;
  1844. while (pLink != &pList->ListHead)
  1845. {
  1846. PLIST_ENTRY pNextLink = pLink->Flink;
  1847. pHttpConn = CONTAINING_RECORD(
  1848. pLink,
  1849. UL_HTTP_CONNECTION,
  1850. ZombieListEntry
  1851. );
  1852. UlpRemoveZombieHttpConnection(pHttpConn);
  1853. InsertTailList(&TempZombieList, &pHttpConn->ZombieListEntry);
  1854. pLink = pNextLink;
  1855. }
  1856. UlReleaseSpinLock(&pList->SpinLock, OldIrql);
  1857. //
  1858. // Now cleanup everything.
  1859. //
  1860. pLink = TempZombieList.Flink;
  1861. while (pLink != &TempZombieList)
  1862. {
  1863. pHttpConn = CONTAINING_RECORD(
  1864. pLink,
  1865. UL_HTTP_CONNECTION,
  1866. ZombieListEntry
  1867. );
  1868. pLink = pLink->Flink;
  1869. UlAcquirePushLockExclusive(&pHttpConn->PushLock);
  1870. //
  1871. // Log the zombified connection before dropping it.
  1872. //
  1873. UlErrorLog(pHttpConn,
  1874. NULL,
  1875. ERROR_LOG_INFO_FOR_ZOMBIE_DROP,
  1876. ERROR_LOG_INFO_FOR_ZOMBIE_DROP_SIZE,
  1877. TRUE
  1878. );
  1879. UlpCleanupZombieHttpConnection(pHttpConn);
  1880. pHttpConn->ZombieListEntry.Flink = NULL;
  1881. UlReleasePushLockExclusive(&pHttpConn->PushLock);
  1882. //
  1883. // Release the zombie list's refcount on http connection.
  1884. //
  1885. UL_DEREFERENCE_HTTP_CONNECTION(pHttpConn);
  1886. }
  1887. }
  1888. /***************************************************************************++
  1889. Routine Description:
  1890. Timer for the zombie http connection list wakes up every 30 seconds and
  1891. in the max case it terminates the zombie connection no later than 60
  1892. seconds.
  1893. --***************************************************************************/
  1894. VOID
  1895. UlpSetZombieTimer(
  1896. VOID
  1897. )
  1898. {
  1899. LONGLONG PeriodTime100Ns;
  1900. LONG PeriodTimeMs;
  1901. LARGE_INTEGER PeriodTime;
  1902. PeriodTimeMs = DEFAULT_ZOMBIE_HTTP_CONNECTION_TIMER_PERIOD_IN_SECONDS * 1000;
  1903. PeriodTime100Ns = (LONGLONG) PeriodTimeMs * 10 * 1000;
  1904. UlTrace(HTTP_IO,("http!UlpSetZombieTimer: period of %d seconds.\n",
  1905. DEFAULT_ZOMBIE_HTTP_CONNECTION_TIMER_PERIOD_IN_SECONDS
  1906. ));
  1907. PeriodTime.QuadPart = -PeriodTime100Ns; // Relative time
  1908. KeSetTimerEx(
  1909. &g_ZombieConnectionList.Timer,
  1910. PeriodTime, // In nanosec
  1911. PeriodTimeMs, // In millisec
  1912. &g_ZombieConnectionList.DpcObject
  1913. );
  1914. }
  1915. /***************************************************************************++
  1916. Routine Description:
  1917. Probes and prepares the user provided log buffers and completes the
  1918. logging for this zombie connection. After that it triggers the cleanup
  1919. of the zombie connection.
  1920. If any error happens, it cleans up the zombie connection But returns
  1921. success regardless.
  1922. You should hold the http connection lock exclusive before calling this
  1923. function.
  1924. Arguments:
  1925. pRequest - The request for the zombie connection.
  1926. pHttpConnection - Supplies the HTTP_CONNECTION to send the response on.
  1927. Return Value:
  1928. Always STATUS_CONNECTION_INVALID. Handling the zombie connection is a
  1929. best effort to write a log record for the connection. If something fails
  1930. we terminate the zombie connection and do not allow a second chance.
  1931. --***************************************************************************/
  1932. NTSTATUS
  1933. UlLogZombieConnection(
  1934. IN PUL_INTERNAL_REQUEST pRequest,
  1935. IN PUL_HTTP_CONNECTION pHttpConn,
  1936. IN PHTTP_LOG_FIELDS_DATA pCapturedUserLogData,
  1937. IN KPROCESSOR_MODE RequestorMode
  1938. )
  1939. {
  1940. PUL_LOG_DATA_BUFFER pLogData = NULL;
  1941. NTSTATUS Status = STATUS_SUCCESS;
  1942. KIRQL OldIrql;
  1943. //
  1944. // Sanity check.
  1945. //
  1946. Status = STATUS_SUCCESS;
  1947. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConn));
  1948. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  1949. ASSERT(pHttpConn->Zombified == TRUE);
  1950. ASSERT(pHttpConn->ZombieListEntry.Flink != NULL);
  1951. UlTrace(HTTP_IO, (
  1952. "Http!UlLogZombieConnection: Logging for pRequest=%p pHttpConn=%p\n",
  1953. pRequest, pHttpConn
  1954. ));
  1955. //
  1956. // No user logging data, bail out and cleanup the zombie connection.
  1957. //
  1958. if (!pCapturedUserLogData)
  1959. {
  1960. goto cleanup;
  1961. }
  1962. //
  1963. // Probe the log data. If something fails, then cleanup
  1964. // the zombie connection.
  1965. //
  1966. __try
  1967. {
  1968. //
  1969. // The pCapturedUserLogData is already probed and captured. However
  1970. // we need to probe the individual log fields (pointers) inside the
  1971. // structure.
  1972. //
  1973. UlProbeLogData(pCapturedUserLogData, RequestorMode);
  1974. } __except(UL_EXCEPTION_FILTER())
  1975. {
  1976. Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
  1977. goto cleanup;
  1978. }
  1979. //
  1980. // Now we will allocate a kernel pLogData and built and format it
  1981. // from the provided user log fields. However if logging is not
  1982. // enabled for the pRequest's cgroup then capture will simply
  1983. // return success w/o setting the pLogData. Therefore we need to
  1984. // make sure we only log if the pLogData is set.
  1985. //
  1986. Status = UlCaptureUserLogData(
  1987. pCapturedUserLogData,
  1988. pRequest,
  1989. &pLogData
  1990. );
  1991. if (NT_SUCCESS(Status) && pLogData)
  1992. {
  1993. ASSERT(IS_VALID_LOG_DATA_BUFFER(pLogData));
  1994. //
  1995. // The actual logging takes place inline.
  1996. //
  1997. LOG_SET_WIN32STATUS(pLogData, STATUS_CONNECTION_RESET);
  1998. //
  1999. // Pick the right logging type.
  2000. //
  2001. if (pLogData->Flags.Binary)
  2002. {
  2003. UlRawLogHttpHit(pLogData);
  2004. }
  2005. else
  2006. {
  2007. UlLogHttpHit(pLogData);
  2008. }
  2009. }
  2010. cleanup:
  2011. //
  2012. // Cleanup the pLogData & the zombie connection before
  2013. // completing the Ioctl.
  2014. //
  2015. if (pLogData)
  2016. {
  2017. UlDestroyLogDataBuffer(pLogData);
  2018. }
  2019. UlAcquireSpinLock(
  2020. &g_ZombieConnectionList.LockList.SpinLock, &OldIrql);
  2021. UlpRemoveZombieHttpConnection(pHttpConn);
  2022. UlReleaseSpinLock(
  2023. &g_ZombieConnectionList.LockList.SpinLock, OldIrql);
  2024. UlpCleanupZombieHttpConnection(pHttpConn);
  2025. //
  2026. // Release Zombie List's refcount on the http connection.
  2027. //
  2028. UL_DEREFERENCE_HTTP_CONNECTION(pHttpConn);
  2029. return STATUS_CONNECTION_INVALID;
  2030. }
  2031. //
  2032. // Following code has been implemented for Global & Site specific connection
  2033. // limits feature. If enforced, incoming connections get refused when they
  2034. // exceed the existing limit. Control channel & config group (re)sets this
  2035. // values whereas httprcv and sendresponse updates the limits for incoming
  2036. // requests & connections. A seperate connection_count_entry structure keeps
  2037. // track of the limits per site. Global limits are tracked by the global
  2038. // variables g_MaxConnections & g_CurrentConnections same API has been used
  2039. // for both purposes.
  2040. //
  2041. /***************************************************************************++
  2042. Routine Description:
  2043. Allocates a connection count entry which will hold the site specific
  2044. connection count info. Get called by cgroup when Config group is
  2045. attempting to allocate a connection count entry.
  2046. Arguments:
  2047. pConfigGroup - To receive the newly allocated count entry
  2048. MaxConnections - The maximum allowed connections
  2049. --***************************************************************************/
  2050. NTSTATUS
  2051. UlCreateConnectionCountEntry(
  2052. IN OUT PUL_CONFIG_GROUP_OBJECT pConfigGroup,
  2053. IN ULONG MaxConnections
  2054. )
  2055. {
  2056. PUL_CONNECTION_COUNT_ENTRY pEntry;
  2057. // Sanity check.
  2058. PAGED_CODE();
  2059. ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
  2060. // Alloc new struct from Paged Pool
  2061. pEntry = UL_ALLOCATE_STRUCT(
  2062. PagedPool,
  2063. UL_CONNECTION_COUNT_ENTRY,
  2064. UL_CONNECTION_COUNT_ENTRY_POOL_TAG
  2065. );
  2066. if (!pEntry)
  2067. {
  2068. UlTrace(LIMITS,
  2069. ("Http!UlCreateConnectionCountEntry: OutOfMemory pConfigGroup %p\n",
  2070. pConfigGroup
  2071. ));
  2072. return STATUS_NO_MEMORY;
  2073. }
  2074. pEntry->Signature = UL_CONNECTION_COUNT_ENTRY_POOL_TAG;
  2075. pEntry->RefCount = 1;
  2076. pEntry->MaxConnections = MaxConnections;
  2077. pEntry->CurConnections = 0;
  2078. // Update cgroup pointer
  2079. ASSERT( pConfigGroup->pConnectionCountEntry == NULL );
  2080. pConfigGroup->pConnectionCountEntry = pEntry;
  2081. UlTrace(LIMITS,
  2082. ("Http!UlCreateConnectionCountEntry: "
  2083. "pNewEntry %p, pConfigGroup %p, Max %d\n",
  2084. pEntry,
  2085. pConfigGroup,
  2086. MaxConnections
  2087. ));
  2088. return STATUS_SUCCESS;
  2089. } // UlCreateConnectionCountEntry
  2090. /***************************************************************************++
  2091. Routine Description:
  2092. increments the refcount for ConnectionCountEntry.
  2093. Arguments:
  2094. pEntry - the object to increment.
  2095. --***************************************************************************/
  2096. VOID
  2097. UlReferenceConnectionCountEntry(
  2098. IN PUL_CONNECTION_COUNT_ENTRY pEntry
  2099. REFERENCE_DEBUG_FORMAL_PARAMS
  2100. )
  2101. {
  2102. LONG refCount;
  2103. //
  2104. // Sanity check.
  2105. //
  2106. PAGED_CODE();
  2107. ASSERT( IS_VALID_CONNECTION_COUNT_ENTRY(pEntry) );
  2108. refCount = InterlockedIncrement( &pEntry->RefCount );
  2109. //
  2110. // Tracing.
  2111. //
  2112. WRITE_REF_TRACE_LOG(
  2113. g_pConnectionCountTraceLog,
  2114. REF_ACTION_REFERENCE_CONNECTION_COUNT_ENTRY,
  2115. refCount,
  2116. pEntry,
  2117. pFileName,
  2118. LineNumber
  2119. );
  2120. UlTrace(
  2121. REFCOUNT,
  2122. ("Http!UlReferenceConnectionCountEntry pEntry=%p refcount=%d\n",
  2123. pEntry,
  2124. refCount
  2125. ));
  2126. } // UlReferenceConnectionCountEntry
  2127. /***************************************************************************++
  2128. Routine Description:
  2129. decrements the refcount. if it hits 0, destruct's ConnectionCountEntry
  2130. Arguments:
  2131. pEntry - the object to decrement.
  2132. --***************************************************************************/
  2133. VOID
  2134. UlDereferenceConnectionCountEntry(
  2135. IN PUL_CONNECTION_COUNT_ENTRY pEntry
  2136. REFERENCE_DEBUG_FORMAL_PARAMS
  2137. )
  2138. {
  2139. LONG refCount;
  2140. //
  2141. // Sanity check.
  2142. //
  2143. PAGED_CODE();
  2144. ASSERT( IS_VALID_CONNECTION_COUNT_ENTRY(pEntry) );
  2145. refCount = InterlockedDecrement( &pEntry->RefCount );
  2146. //
  2147. // Tracing.
  2148. //
  2149. WRITE_REF_TRACE_LOG(
  2150. g_pConnectionCountTraceLog,
  2151. REF_ACTION_DEREFERENCE_CONNECTION_COUNT_ENTRY,
  2152. refCount,
  2153. pEntry,
  2154. pFileName,
  2155. LineNumber
  2156. );
  2157. UlTrace(
  2158. REFCOUNT,
  2159. ("Http!UlDereferenceConnectionCountEntry pEntry=%p refcount=%d\n",
  2160. pEntry,
  2161. refCount
  2162. ));
  2163. //
  2164. // Cleanup the memory and do few checks !
  2165. //
  2166. if ( refCount == 0 )
  2167. {
  2168. // Make sure no connection on the site
  2169. ASSERT( 0 == pEntry->CurConnections );
  2170. UlTrace(
  2171. LIMITS,
  2172. ("Http!UlDereferenceConnectionCountEntry: Removing pEntry=%p\n",
  2173. pEntry
  2174. ));
  2175. // Release memory
  2176. UL_FREE_POOL_WITH_SIG(pEntry,UL_CONNECTION_COUNT_ENTRY_POOL_TAG);
  2177. }
  2178. } // UlDereferenceConnectionCountEntry
  2179. /***************************************************************************++
  2180. Routine Description:
  2181. This
  2182. function set the maximum limit. Maximum allowed number of connections
  2183. at any time by the active control channel.
  2184. Arguments:
  2185. MaxConnections - Maximum allowed number of connections
  2186. Return Value:
  2187. Old Max Connection count
  2188. --***************************************************************************/
  2189. ULONG
  2190. UlSetMaxConnections(
  2191. IN OUT PULONG pCurMaxConnection,
  2192. IN ULONG NewMaxConnection
  2193. )
  2194. {
  2195. ULONG OldMaxConnection;
  2196. UlTrace(LIMITS,
  2197. ("Http!UlSetMaxConnections pCurMaxConnection=%p NewMaxConnection=%d\n",
  2198. pCurMaxConnection,
  2199. NewMaxConnection
  2200. ));
  2201. //
  2202. // By setting this we are not forcing the existing connections to
  2203. // termination but this number will be effective for all newcoming
  2204. // connections, as soon as the atomic operation completed.
  2205. //
  2206. OldMaxConnection = (ULONG) InterlockedExchange((LONG *) pCurMaxConnection,
  2207. (LONG) NewMaxConnection
  2208. );
  2209. return OldMaxConnection;
  2210. } // UlSetMaxConnections
  2211. /***************************************************************************++
  2212. Routine Description:
  2213. Control channel uses this function to set or reset the global connection
  2214. limits.
  2215. --***************************************************************************/
  2216. ULONG
  2217. UlGetGlobalConnectionLimit(
  2218. VOID
  2219. )
  2220. {
  2221. return g_MaxConnections;
  2222. }
  2223. /***************************************************************************++
  2224. Routine Description:
  2225. Control channel uses this function to initialize the global connection
  2226. limits. Assuming the existince of one and only one active control channel
  2227. this globals get set only once during init. But could be set again later
  2228. because of a reconfig.
  2229. --***************************************************************************/
  2230. NTSTATUS
  2231. UlInitGlobalConnectionLimits(
  2232. VOID
  2233. )
  2234. {
  2235. ULONG PerConnectionEstimate;
  2236. SIZE_T HttpMaxConnections;
  2237. SIZE_T AvailablePages;
  2238. NTSTATUS Status = STATUS_SUCCESS;
  2239. ASSERT(!g_InitGlobalConnections);
  2240. if (!g_InitGlobalConnections)
  2241. {
  2242. g_CurrentConnections = 0;
  2243. //
  2244. // Set global max connection limit
  2245. //
  2246. // Heuristic Calculation to estmiate the size
  2247. // needed per connection
  2248. // First, there's the UL_CONNECTION
  2249. PerConnectionEstimate = sizeof(UL_CONNECTION);
  2250. // Second, assume one UL_INTERNAL_REQUEST+Full Tracker per Connection
  2251. PerConnectionEstimate += (sizeof(UL_INTERNAL_REQUEST) +
  2252. MAX(g_UlFullTrackerSize, g_UlChunkTrackerSize) +
  2253. g_UlMaxInternalUrlLength +
  2254. DEFAULT_MAX_ROUTING_TOKEN_LENGTH +
  2255. sizeof(WCHAR) // for the null on the InternalUrl
  2256. );
  2257. // Third, add one UL_REQUEST_BUFFER
  2258. PerConnectionEstimate += (sizeof(UL_REQUEST_BUFFER) +
  2259. DEFAULT_MAX_REQUEST_BUFFER_SIZE);
  2260. // Fourth, add the size of a response
  2261. PerConnectionEstimate += g_UlResponseBufferSize;
  2262. // Finally, round up to the next page size & convert to pages
  2263. PerConnectionEstimate =
  2264. (ULONG)ROUND_TO_PAGES(PerConnectionEstimate);
  2265. PerConnectionEstimate /= PAGE_SIZE;
  2266. ASSERT(0 != PerConnectionEstimate);
  2267. AvailablePages = BYTES_TO_PAGES(g_UlTotalNonPagedPoolBytes);
  2268. // Assume only 50% of NPP available to HTTP.SYS
  2269. AvailablePages /= 2;
  2270. // Assume 20% set aside for HttpDataChunkFromFileHandle send buffers.
  2271. // (this works out to roughly 3% to 5% of the connections)
  2272. AvailablePages -= (AvailablePages/5);
  2273. if ( AvailablePages < PerConnectionEstimate )
  2274. {
  2275. // this machine has so little NPP, that it can't even support one
  2276. // measely little connection!
  2277. AvailablePages = PerConnectionEstimate;
  2278. }
  2279. // Okay, how many fit?
  2280. HttpMaxConnections = (AvailablePages / PerConnectionEstimate);
  2281. ASSERT(0 != HttpMaxConnections);
  2282. // And set it (if it wasn't overridden in UlpReadRegistry)
  2283. if (HTTP_LIMIT_INFINITE == g_MaxConnections)
  2284. {
  2285. g_MaxConnections = (ULONG)MIN(HttpMaxConnections, LONG_MAX);
  2286. }
  2287. else
  2288. {
  2289. UlTrace(LIMITS,
  2290. ("Http!UlInitGlobalConnectionLimits: User has overridden "
  2291. "g_MaxConnections: %d\n",
  2292. g_MaxConnections
  2293. ));
  2294. }
  2295. g_InitGlobalConnections = TRUE;
  2296. UlTrace2Either(LIMITS, TDI_STATS,
  2297. ("Http!UlInitGlobalConnectionLimits: Init g_MaxConnections %d,"
  2298. "g_CurrentConnections %d, HttpMaxConnections %d\n",
  2299. g_MaxConnections,
  2300. g_CurrentConnections,
  2301. HttpMaxConnections
  2302. ));
  2303. if (DEFAULT_MAX_REQUESTS_QUEUED == g_UlMaxRequestsQueued)
  2304. {
  2305. //
  2306. // If it's been left at the default, set it equal to
  2307. // the number of Max Connections; this assumes a 1:1 ratio
  2308. // of requests per connection.
  2309. //
  2310. g_UlMaxRequestsQueued = (ULONG) g_MaxConnections;
  2311. }
  2312. UlTrace2Either(LIMITS, TDI_STATS,
  2313. ("Http!UlInitGlobalConnectionLimits: Init g_UlMaxRequestsQueued %d\n",
  2314. g_UlMaxRequestsQueued
  2315. ));
  2316. }
  2317. return Status;
  2318. } // UlInitGlobalConnectionLimits
  2319. /***************************************************************************++
  2320. Routine Description:
  2321. Wrapper around the Accept Connection for global connections
  2322. --***************************************************************************/
  2323. BOOLEAN
  2324. UlAcceptGlobalConnection(
  2325. VOID
  2326. )
  2327. {
  2328. return UlAcceptConnection( &g_MaxConnections, &g_CurrentConnections );
  2329. } // UlAcceptGlobalConnection
  2330. /***************************************************************************++
  2331. Routine Description:
  2332. This function checks if we are allowed to accept the incoming connection
  2333. based on the number enforced by the control channel.
  2334. Return value:
  2335. Decision for the newcoming connection either ACCEPT or REJECT as boolean
  2336. --***************************************************************************/
  2337. BOOLEAN
  2338. UlAcceptConnection(
  2339. IN PULONG pMaxConnection,
  2340. IN OUT PULONG pCurConnection
  2341. )
  2342. {
  2343. ULONG LocalCur;
  2344. ULONG LocalCurPlusOne;
  2345. ULONG LocalMax;
  2346. do
  2347. {
  2348. //
  2349. // Capture the Max & Cur. Do the comparison. If in the limit
  2350. // attempt to increment the connection count by ensuring nobody
  2351. // else did it before us.
  2352. //
  2353. LocalMax = *((volatile ULONG *) pMaxConnection);
  2354. LocalCur = *((volatile ULONG *) pCurConnection);
  2355. //
  2356. // Its greater than or equal because Max may get updated to
  2357. // a smaller number on-the-fly and we end up having current
  2358. // connections greater than the maximum allowed.
  2359. // NOTE: HTTP_LIMIT_INFINITE has been picked as (ULONG)-1 so
  2360. // following comparison won't reject for the infinite case.
  2361. //
  2362. if ( LocalCur >= LocalMax )
  2363. {
  2364. //
  2365. // We are already at the limit refuse it.
  2366. //
  2367. UlTrace(LIMITS,
  2368. ("Http!UlAcceptConnection REFUSE pCurConnection=%p[%d]"
  2369. "pMaxConnection=%p[%d]\n",
  2370. pCurConnection, LocalCur,
  2371. pMaxConnection, LocalMax
  2372. ));
  2373. return FALSE;
  2374. }
  2375. //
  2376. // Either the limit was infinite or conn count was not exceeding
  2377. // the limit. Lets attempt to increment the count and accept the
  2378. // connection in the while statement.
  2379. //
  2380. LocalCurPlusOne = LocalCur + 1;
  2381. }
  2382. while ( LocalCur != (ULONG) InterlockedCompareExchange(
  2383. (LONG *) pCurConnection,
  2384. (LONG) LocalCurPlusOne,
  2385. (LONG) LocalCur
  2386. ) );
  2387. //
  2388. // Successfully incremented the counter. Let it go with success.
  2389. //
  2390. UlTrace(LIMITS,
  2391. ("Http!UlAcceptConnection ACCEPT pCurConnection=%p[%d]"
  2392. "pMaxConnection=%p[%d]\n",
  2393. pCurConnection, LocalCur,
  2394. pMaxConnection, LocalMax
  2395. ));
  2396. return TRUE;
  2397. } // UlAcceptConnection
  2398. /***************************************************************************++
  2399. Routine Description:
  2400. Everytime a disconnection happens we will decrement the count here.
  2401. Return Value:
  2402. The newly decremented value
  2403. --***************************************************************************/
  2404. LONG
  2405. UlDecrementConnections(
  2406. IN OUT PULONG pCurConnection
  2407. )
  2408. {
  2409. LONG NewConnectionCount;
  2410. NewConnectionCount = InterlockedDecrement( (LONG *) pCurConnection );
  2411. ASSERT( NewConnectionCount >= 0 );
  2412. return NewConnectionCount;
  2413. } // UlDecrementConnections
  2414. /***************************************************************************++
  2415. Routine Description:
  2416. For cache & non-cache hits this function get called. Connection resource
  2417. has assumed to be acquired at this time. The function decide to accept or
  2418. reject the request by looking at the corresponding count entries.
  2419. Arguments:
  2420. pConnection - For getting the previous site's connection count entry
  2421. pConfigInfo - Holds a pointer to newly received request's site's
  2422. connection count entry.
  2423. Return Value:
  2424. Shows reject or accept
  2425. --***************************************************************************/
  2426. BOOLEAN
  2427. UlCheckSiteConnectionLimit(
  2428. IN OUT PUL_HTTP_CONNECTION pConnection,
  2429. IN OUT PUL_URL_CONFIG_GROUP_INFO pConfigInfo
  2430. )
  2431. {
  2432. BOOLEAN Accept;
  2433. PUL_CONNECTION_COUNT_ENTRY pConEntry;
  2434. PUL_CONNECTION_COUNT_ENTRY pCIEntry;
  2435. if (pConfigInfo->pMaxConnections == NULL || pConfigInfo->pConnectionCountEntry == NULL)
  2436. {
  2437. //
  2438. // No connection count entry for the new request perhaps WAS never
  2439. // set this before otherwise its a problem.
  2440. //
  2441. UlTrace(LIMITS,
  2442. ("Http!UlCheckSiteConnectionLimit: NO LIMIT pConnection=%p pConfigInfo=%p\n",
  2443. pConnection,
  2444. pConfigInfo
  2445. ));
  2446. return TRUE;
  2447. }
  2448. ASSERT(IS_VALID_CONNECTION_COUNT_ENTRY(pConfigInfo->pConnectionCountEntry));
  2449. pCIEntry = pConfigInfo->pConnectionCountEntry;
  2450. pConEntry = pConnection->pConnectionCountEntry;
  2451. Accept = FALSE;
  2452. //
  2453. // Make a check on the connection limit of the site. Refuse the request
  2454. // if the limit is exceded.
  2455. //
  2456. if (pConEntry)
  2457. {
  2458. ASSERT(IS_VALID_CONNECTION_COUNT_ENTRY(pConEntry));
  2459. //
  2460. // For consecutive requests we decrement the previous site's connection count
  2461. // before proceeding to the decision on the newcoming request,if the two sides
  2462. // are not same.That means we assume this connection on site x until (if ever)
  2463. // a request changes this to site y. Naturally until the first request arrives
  2464. // and successfully parsed, the connection does not count to any specific site
  2465. //
  2466. if (pConEntry != pCIEntry)
  2467. {
  2468. UlDecrementConnections(&pConEntry->CurConnections);
  2469. DEREFERENCE_CONNECTION_COUNT_ENTRY(pConEntry);
  2470. //
  2471. // We do not increment the connection here yet, since the AcceptConnection
  2472. // will decide and do that.
  2473. //
  2474. REFERENCE_CONNECTION_COUNT_ENTRY(pCIEntry);
  2475. pConnection->pConnectionCountEntry = pCIEntry;
  2476. }
  2477. else
  2478. {
  2479. //
  2480. // There was an old entry, that means this connection already got through.
  2481. // And the entry hasn't been changed with this new request.
  2482. // No need to check again, our design is not forcing existing connections
  2483. // to disconnect.
  2484. //
  2485. return TRUE;
  2486. }
  2487. }
  2488. else
  2489. {
  2490. REFERENCE_CONNECTION_COUNT_ENTRY(pCIEntry);
  2491. pConnection->pConnectionCountEntry = pCIEntry;
  2492. }
  2493. Accept = UlAcceptConnection(
  2494. &pConnection->pConnectionCountEntry->MaxConnections,
  2495. &pConnection->pConnectionCountEntry->CurConnections
  2496. );
  2497. if (Accept == FALSE)
  2498. {
  2499. // We are going to refuse. Let our ref & refcount go away
  2500. // on the connection entry to prevent the possible incorrect
  2501. // decrement in the UlConnectionDestroyedWorker. If refused
  2502. // current connection hasn't been incremented in the accept
  2503. // connection. Perf counters also depends on the fact that
  2504. // pConnectionCountEntry will be Null when con got refused
  2505. DEREFERENCE_CONNECTION_COUNT_ENTRY(pConnection->pConnectionCountEntry);
  2506. pConnection->pConnectionCountEntry = NULL;
  2507. }
  2508. return Accept;
  2509. } // UlCheckSiteConnectionLimit
  2510. /***************************************************************************++
  2511. Routine Description:
  2512. Allocate a request opaque ID.
  2513. Return Value:
  2514. NT_SUCCESS
  2515. --***************************************************************************/
  2516. NTSTATUS
  2517. UlAllocateRequestId(
  2518. IN PUL_INTERNAL_REQUEST pRequest
  2519. )
  2520. {
  2521. PUL_HTTP_CONNECTION pConnection;
  2522. KIRQL OldIrql;
  2523. PAGED_CODE();
  2524. pConnection = pRequest->pHttpConn;
  2525. UlAcquireSpinLock(&pConnection->RequestIdSpinLock, &OldIrql);
  2526. pConnection->pRequestIdContext = pRequest;
  2527. UlReleaseSpinLock(&pConnection->RequestIdSpinLock, OldIrql);
  2528. pRequest->RequestId = pConnection->ConnectionId;
  2529. UL_REFERENCE_INTERNAL_REQUEST(pRequest);
  2530. pRequest->RequestIdCopy = pRequest->RequestId;
  2531. return STATUS_SUCCESS;
  2532. }
  2533. /***************************************************************************++
  2534. Routine Description:
  2535. Free a request opaque ID.
  2536. Return Value:
  2537. VOID
  2538. --***************************************************************************/
  2539. VOID
  2540. UlFreeRequestId(
  2541. IN PUL_INTERNAL_REQUEST pRequest
  2542. )
  2543. {
  2544. PUL_HTTP_CONNECTION pConnection;
  2545. KIRQL OldIrql;
  2546. pConnection = pRequest->pHttpConn;
  2547. UlAcquireSpinLock(&pConnection->RequestIdSpinLock, &OldIrql);
  2548. pConnection->pRequestIdContext = NULL;
  2549. UlReleaseSpinLock(&pConnection->RequestIdSpinLock, OldIrql);
  2550. return;
  2551. }
  2552. /***************************************************************************++
  2553. Routine Description:
  2554. Get a request from the connection opaque ID.
  2555. Return Value:
  2556. PUL_INTERNAL_REQUEST
  2557. --***************************************************************************/
  2558. PUL_INTERNAL_REQUEST
  2559. UlGetRequestFromId(
  2560. IN HTTP_REQUEST_ID RequestId,
  2561. IN PUL_APP_POOL_PROCESS pProcess
  2562. )
  2563. {
  2564. PUL_HTTP_CONNECTION pConnection;
  2565. PUL_INTERNAL_REQUEST pRequest;
  2566. KIRQL OldIrql;
  2567. pConnection = UlGetConnectionFromId(RequestId);
  2568. if (pConnection != NULL)
  2569. {
  2570. UlAcquireSpinLock(&pConnection->RequestIdSpinLock, &OldIrql);
  2571. pRequest = pConnection->pRequestIdContext;
  2572. if (pRequest != NULL)
  2573. {
  2574. //
  2575. // Check to make sure the user that is asking for the request
  2576. // comes from the same process we have delivered the request.
  2577. //
  2578. if (pRequest->AppPool.pProcess == pProcess)
  2579. {
  2580. UL_REFERENCE_INTERNAL_REQUEST(pRequest);
  2581. }
  2582. else
  2583. {
  2584. pRequest = NULL;
  2585. }
  2586. }
  2587. UlReleaseSpinLock(&pConnection->RequestIdSpinLock, OldIrql);
  2588. //
  2589. // Release the reference added by UlGetConnectionFromId.
  2590. //
  2591. UL_DEREFERENCE_HTTP_CONNECTION(pConnection);
  2592. return pRequest;
  2593. }
  2594. return NULL;
  2595. }
  2596. /***************************************************************************++
  2597. Routine Description:
  2598. Check if the pHttpConnection is associated with pListeningContext.
  2599. Return value:
  2600. TRUE if match or FALSE
  2601. --***************************************************************************/
  2602. BOOLEAN
  2603. UlPurgeListeningEndpoint(
  2604. IN PUL_HTTP_CONNECTION pHttpConnection,
  2605. IN PVOID pListeningContext
  2606. )
  2607. {
  2608. ASSERT(pHttpConnection->pConnection->pListeningContext);
  2609. if (pListeningContext == pHttpConnection->pConnection->pListeningContext)
  2610. {
  2611. return TRUE;
  2612. }
  2613. else
  2614. {
  2615. return FALSE;
  2616. }
  2617. }
  2618. /***************************************************************************++
  2619. Routine Description:
  2620. Check if the pHttpConnection is associated with pProcessContext.
  2621. Return value:
  2622. TRUE if match or FALSE
  2623. --***************************************************************************/
  2624. BOOLEAN
  2625. UlPurgeAppPoolProcess(
  2626. IN PUL_HTTP_CONNECTION pHttpConnection,
  2627. IN PVOID pProcessContext
  2628. )
  2629. {
  2630. ASSERT(pHttpConnection->pAppPoolProcess);
  2631. if (pProcessContext == pHttpConnection->pAppPoolProcess)
  2632. {
  2633. return TRUE;
  2634. }
  2635. else
  2636. {
  2637. return FALSE;
  2638. }
  2639. }
  2640. /***************************************************************************++
  2641. Routine Description:
  2642. Gracefully close a connection.
  2643. Return value:
  2644. None
  2645. --***************************************************************************/
  2646. NTSTATUS
  2647. UlDisconnectHttpConnection(
  2648. IN PUL_HTTP_CONNECTION pHttpConnection,
  2649. IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
  2650. IN PVOID pCompletionContext
  2651. )
  2652. {
  2653. PUL_TIMEOUT_INFO_ENTRY pTimeoutInfo;
  2654. KIRQL OldIrql;
  2655. //
  2656. // Start the Idle timer (do not wait forever for FIN).
  2657. //
  2658. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConnection));
  2659. pTimeoutInfo = &pHttpConnection->TimeoutInfo;
  2660. UlAcquireSpinLock(&pTimeoutInfo->Lock, &OldIrql);
  2661. if (UlIsConnectionTimerOff(pTimeoutInfo, TimerConnectionIdle))
  2662. {
  2663. UlSetConnectionTimer(pTimeoutInfo, TimerConnectionIdle);
  2664. }
  2665. UlReleaseSpinLock(&pTimeoutInfo->Lock, OldIrql);
  2666. UlEvaluateTimerState(pTimeoutInfo);
  2667. return UlCloseConnection(
  2668. pHttpConnection->pConnection,
  2669. FALSE,
  2670. pCompletionRoutine,
  2671. pCompletionContext
  2672. );
  2673. }