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.

7840 lines
214 KiB

  1. /*++
  2. Copyright (c) 1998-2002 Microsoft Corporation
  3. Module Name:
  4. sendresponse.c
  5. Abstract:
  6. This module implements the UlSendHttpResponse() API.
  7. CODEWORK: The current implementation is not super performant.
  8. Specifically, it ends up allocating & freeing a ton of IRPs to send
  9. a response. There are a number of optimizations that need to be made
  10. to this code:
  11. 1. Coalesce contiguious from-memory chunks and send them
  12. with a single TCP send.
  13. 2. Defer sending the from-memory chunks until either
  14. a) We reach the end of the response
  15. b) We reach a from-file chunk, have read the
  16. (first?) block of data from the file,
  17. and are ready to send the first block. Also,
  18. after that last (only?) file block is read and
  19. subsequent from-memory chunks exist in the response,
  20. we can attach the from-memory chunks before sending.
  21. The end result of these optimizations is that, for the
  22. common case (one or more from-memory chunks containing
  23. response headers, followed by one from-file chunk containing
  24. static file data, followed by zero or more from-memory chunks
  25. containing footer data) the response can be sent with a single
  26. TCP send. This is a Good Thing.
  27. 3. Build a small "IRP pool" in the send tracker structure,
  28. then use this pool for all IRP allocations. This will
  29. require a bit of work to determine the maximum IRP stack
  30. size needed.
  31. 4. Likewise, build a small "MDL pool" for the MDLs that need
  32. to be created for the various MDL chains. Keep in mind that
  33. we cannot chain the MDLs that come directly from the captured
  34. response structure, nor can we chain the MDLs that come back
  35. from the file system. In both cases, these MDLs are considered
  36. "shared resources" and we're not allowed to modify them. We
  37. can, however, "clone" the MDLs and chain the cloned MDLs
  38. together. We'll need to run some experiments to determine
  39. if the overhead for cloning a MDL is worth the effort. I
  40. strongly suspect it will be.
  41. Author:
  42. Keith Moore (keithmo) 07-Aug-1998
  43. Revision History:
  44. Paul McDaniel (paulmcd) 15-Mar-1999 Modified to handle
  45. multiple sends
  46. Michael Courage (mcourage) 15-Jun-1999 Integrated cache functionality
  47. Chun Ye (chunye) 08-Jun-2002 Implemented split send
  48. --*/
  49. #include "precomp.h"
  50. #include "sendresponsep.h"
  51. //
  52. // Private globals.
  53. //
  54. ULONGLONG g_UlTotalSendBytes = 0;
  55. UL_EXCLUSIVE_LOCK g_UlTotalSendBytesExLock = UL_EX_LOCK_FREE;
  56. #ifdef ALLOC_PRAGMA
  57. #pragma alloc_text( PAGE, UlCaptureHttpResponse )
  58. #pragma alloc_text( PAGE, UlCaptureUserLogData )
  59. #pragma alloc_text( PAGE, UlPrepareHttpResponse )
  60. #pragma alloc_text( PAGE, UlCleanupHttpResponse )
  61. #pragma alloc_text( PAGE, UlAllocateLockedMdl )
  62. #pragma alloc_text( PAGE, UlInitializeAndLockMdl )
  63. #pragma alloc_text( PAGE, UlSendCachedResponse )
  64. #pragma alloc_text( PAGE, UlCacheAndSendResponse )
  65. #pragma alloc_text( PAGE, UlpEnqueueSendHttpResponse )
  66. #pragma alloc_text( PAGE, UlpDequeueSendHttpResponse )
  67. #pragma alloc_text( PAGE, UlpSendHttpResponseWorker )
  68. #pragma alloc_text( PAGE, UlpMdlSendCompleteWorker )
  69. #pragma alloc_text( PAGE, UlpMdlReadCompleteWorker )
  70. #pragma alloc_text( PAGE, UlpCacheMdlReadCompleteWorker )
  71. #pragma alloc_text( PAGE, UlpFlushMdlRuns )
  72. #pragma alloc_text( PAGE, UlpFreeMdlRuns )
  73. #pragma alloc_text( PAGE, UlpCopySend )
  74. #pragma alloc_text( PAGE, UlpBuildCacheEntry )
  75. #pragma alloc_text( PAGE, UlpBuildCacheEntryWorker )
  76. #pragma alloc_text( PAGE, UlpCompleteCacheBuildWorker )
  77. #pragma alloc_text( PAGE, UlpSendCacheEntry )
  78. #pragma alloc_text( PAGE, UlpSendCacheEntryWorker )
  79. #pragma alloc_text( PAGE, UlpAllocateCacheTracker )
  80. #pragma alloc_text( PAGE, UlpFreeCacheTracker )
  81. #endif // ALLOC_PRAGMA
  82. #if 0
  83. NOT PAGEABLE -- UlSendHttpResponse
  84. NOT PAGEABLE -- UlReferenceHttpResponse
  85. NOT PAGEABLE -- UlDereferenceHttpResponse
  86. NOT PAGEABLE -- UlFreeLockedMdl
  87. NOT PAGEABLE -- UlCompleteSendResponse
  88. NOT PAGEABLE -- UlSetRequestSendsPending
  89. NOT PAGEABLE -- UlUnsetRequestSendsPending
  90. NOT PAGEABLE -- UlpDestroyCapturedResponse
  91. NOT PAGEABLE -- UlpAllocateChunkTracker
  92. NOT PAGEABLE -- UlpFreeChunkTracker
  93. NOT PAGEABLE -- UlpRestartMdlRead
  94. NOT PAGEABLE -- UlpRestartMdlSend
  95. NOT PAGEABLE -- UlpRestartCopySend
  96. NOT PAGEABLE -- UlpIncrementChunkPointer
  97. NOT PAGEABLE -- UlpRestartCacheMdlRead
  98. NOT PAGEABLE -- UlpRestartCacheMdlFree
  99. NOT PAGEABLE -- UlpIssueFileChunkIo
  100. NOT PAGEABLE -- UlpCompleteCacheBuild
  101. NOT PAGEABLE -- UlpCompleteSendCacheEntry
  102. NOT PAGEABLE -- UlpCompleteSendResponseWorker
  103. NOT PAGEABLE -- UlpCompleteSendCacheEntryWorker
  104. #endif
  105. //
  106. // Public functions.
  107. //
  108. /***************************************************************************++
  109. Routine Description:
  110. Sends an HTTP response on the specified connection.
  111. Arguments:
  112. pConnection - Supplies the HTTP_CONNECTION to send the response on.
  113. pResponse - Supplies the HTTP response.
  114. pCompletionRoutine - Supplies a pointer to a completion routine to
  115. invoke after the send has completed.
  116. pCompletionContext - Supplies an uninterpreted context value for the
  117. completion routine.
  118. Return Value:
  119. NTSTATUS - Completion status.
  120. --***************************************************************************/
  121. NTSTATUS
  122. UlSendHttpResponse(
  123. IN PUL_INTERNAL_REQUEST pRequest,
  124. IN PUL_INTERNAL_RESPONSE pResponse,
  125. IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
  126. IN PVOID pCompletionContext
  127. )
  128. {
  129. NTSTATUS Status;
  130. PUL_CHUNK_TRACKER pTracker;
  131. PUL_HTTP_CONNECTION pHttpConn;
  132. UL_CONN_HDR ConnHeader;
  133. BOOLEAN Disconnect;
  134. ULONG VarHeaderGenerated;
  135. ULONGLONG TotalResponseSize;
  136. ULONG ContentLengthStringLength;
  137. UCHAR ContentLength[MAX_ULONGLONG_STR];
  138. ULONG Flags = pResponse->Flags;
  139. //
  140. // Sanity check.
  141. //
  142. ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pRequest ) );
  143. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pResponse ) );
  144. pHttpConn = pRequest->pHttpConn;
  145. ASSERT( UL_IS_VALID_HTTP_CONNECTION( pHttpConn ) );
  146. TRACE_TIME(
  147. pRequest->ConnectionId,
  148. pRequest->RequestId,
  149. TIME_ACTION_SEND_RESPONSE
  150. );
  151. if (Flags & (~HTTP_SEND_RESPONSE_FLAG_VALID))
  152. {
  153. return STATUS_INVALID_PARAMETER;
  154. }
  155. //
  156. // Setup locals so we know how to cleanup on exit.
  157. //
  158. pTracker = NULL;
  159. //
  160. // Should we close the connection?
  161. //
  162. if ((Flags & HTTP_SEND_RESPONSE_FLAG_DISCONNECT))
  163. {
  164. //
  165. // Caller is forcing a disconnect.
  166. //
  167. Disconnect = TRUE;
  168. }
  169. else
  170. {
  171. Disconnect = UlCheckDisconnectInfo( pRequest );
  172. if (0 == (Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA))
  173. {
  174. //
  175. // No more data is coming, should we disconnect?
  176. //
  177. if (Disconnect)
  178. {
  179. Flags |= HTTP_SEND_RESPONSE_FLAG_DISCONNECT;
  180. pResponse->Flags = Flags;
  181. }
  182. }
  183. }
  184. //
  185. // How big is the response?
  186. //
  187. TotalResponseSize = pResponse->ResponseLength;
  188. //
  189. // Generate the Content-Length header if we meet the following conditions:
  190. // 1. This is a response (not entity body).
  191. // 2. The app didn't provide Content-Length.
  192. // 3. The app didn't generate a chunked response.
  193. //
  194. if (pResponse->HeaderLength > 0 &&
  195. !pResponse->ContentLengthSpecified &&
  196. !pResponse->ChunkedSpecified &&
  197. UlNeedToGenerateContentLength(
  198. pRequest->Verb,
  199. pResponse->StatusCode,
  200. Flags
  201. ))
  202. {
  203. //
  204. // Autogenerate a Content-Length header.
  205. //
  206. PCHAR pEnd = UlStrPrintUlonglong(
  207. (PCHAR) ContentLength,
  208. pResponse->ResponseLength - pResponse->HeaderLength,
  209. ANSI_NULL
  210. );
  211. ContentLengthStringLength = DIFF(pEnd - (PCHAR) ContentLength);
  212. }
  213. else
  214. {
  215. //
  216. // Either we cannot or do not need to autogenerate a
  217. // Content-Length header.
  218. //
  219. ContentLength[0] = ANSI_NULL;
  220. ContentLengthStringLength = 0;
  221. }
  222. //
  223. // See if user explicitly wants Connection: header removed, or we will
  224. // choose one for them.
  225. //
  226. if (ConnHdrNone == pResponse->ConnHeader)
  227. {
  228. ConnHeader = pResponse->ConnHeader;
  229. }
  230. else
  231. {
  232. ConnHeader = UlChooseConnectionHeader( pRequest->Version, Disconnect );
  233. }
  234. //
  235. // Completion info.
  236. //
  237. pResponse->pCompletionRoutine = pCompletionRoutine;
  238. pResponse->pCompletionContext = pCompletionContext;
  239. //
  240. // Allocate and initialize a tracker for this response.
  241. //
  242. pTracker =
  243. UlpAllocateChunkTracker(
  244. UlTrackerTypeSend,
  245. pHttpConn->pConnection->ConnectionObject.pDeviceObject->StackSize,
  246. pResponse->MaxFileSystemStackSize,
  247. TRUE,
  248. pHttpConn,
  249. pResponse
  250. );
  251. if (pTracker == NULL)
  252. {
  253. Status = STATUS_INSUFFICIENT_RESOURCES;
  254. goto cleanup;
  255. }
  256. //
  257. // Initialize the first chunk and MDL_RUN for the send.
  258. //
  259. UlpIncrementChunkPointer( pResponse );
  260. //
  261. // Generate var headers, and init the second chunk.
  262. //
  263. if (pResponse->HeaderLength)
  264. {
  265. UlGenerateVariableHeaders(
  266. ConnHeader,
  267. pResponse->GenDateHeader,
  268. ContentLength,
  269. ContentLengthStringLength,
  270. pResponse->pVariableHeader,
  271. &VarHeaderGenerated,
  272. &pResponse->CreationTime
  273. );
  274. ASSERT( VarHeaderGenerated <= g_UlMaxVariableHeaderSize );
  275. pResponse->VariableHeaderLength = VarHeaderGenerated;
  276. //
  277. // Increment total size.
  278. //
  279. TotalResponseSize += VarHeaderGenerated;
  280. //
  281. // Build a MDL for it.
  282. //
  283. pResponse->pDataChunks[1].ChunkType = HttpDataChunkFromMemory;
  284. pResponse->pDataChunks[1].FromMemory.BufferLength = VarHeaderGenerated;
  285. pResponse->pDataChunks[1].FromMemory.pMdl =
  286. UlAllocateMdl(
  287. pResponse->pVariableHeader, // VirtualAddress
  288. VarHeaderGenerated, // Length
  289. FALSE, // SecondaryBuffer
  290. FALSE, // ChargeQuota
  291. NULL // Irp
  292. );
  293. if (pResponse->pDataChunks[1].FromMemory.pMdl == NULL)
  294. {
  295. Status = STATUS_INSUFFICIENT_RESOURCES;
  296. goto cleanup;
  297. }
  298. MmBuildMdlForNonPagedPool( pResponse->pDataChunks[1].FromMemory.pMdl );
  299. }
  300. UlTrace(SEND_RESPONSE, (
  301. "UlSendHttpResponse: tracker %p, response %p\n",
  302. pTracker,
  303. pResponse
  304. ));
  305. //
  306. // Adjust SendsPending and while holding the lock, transfer the
  307. // ownership of pLogData and ResumeParsing information from
  308. // pResponse to pRequest.
  309. //
  310. UlSetRequestSendsPending(
  311. pRequest,
  312. &pResponse->pLogData,
  313. &pResponse->ResumeParsingType
  314. );
  315. //
  316. // Start MinBytesPerSecond timer, since we now know TotalResponseSize.
  317. //
  318. UlSetMinBytesPerSecondTimer(
  319. &pHttpConn->TimeoutInfo,
  320. TotalResponseSize
  321. );
  322. if (ETW_LOG_MIN() && (0 == (Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA)))
  323. {
  324. UlEtwTraceEvent(
  325. &UlTransGuid,
  326. ETW_TYPE_END,
  327. (PVOID) &pTracker->pResponse->pRequest->RequestIdCopy,
  328. sizeof(HTTP_REQUEST_ID),
  329. (PVOID) &pResponse->StatusCode,
  330. sizeof(USHORT),
  331. NULL,
  332. 0
  333. );
  334. }
  335. //
  336. // Queue the response to the request's pending response list or start
  337. // processing the send right away if no other sends are pending. As an
  338. // optimization, we can call UlpSendHttpResponseWorker directly without
  339. // applying the queuing logic if no send is pending, if this is from an
  340. // IOCTL and all of the response's data chunks are FromMemory, in which
  341. // case we are guaranteed that returning from the current routine will
  342. // have all the send data pended in TDI.
  343. //
  344. if (pRequest->SendInProgress ||
  345. pResponse->MaxFileSystemStackSize ||
  346. pResponse->FromKernelMode)
  347. {
  348. pResponse->SendEnqueued = TRUE;
  349. UlpEnqueueSendHttpResponse( pTracker, pResponse->FromKernelMode );
  350. }
  351. else
  352. {
  353. pResponse->SendEnqueued = FALSE;
  354. UlpSendHttpResponseWorker( &pTracker->WorkItem );
  355. }
  356. return STATUS_PENDING;
  357. cleanup:
  358. UlTrace(SEND_RESPONSE, (
  359. "UlSendHttpResponse: failure %08lx\n",
  360. Status
  361. ));
  362. ASSERT( !NT_SUCCESS( Status ) );
  363. if (pTracker != NULL)
  364. {
  365. //
  366. // Very early termination for the chunk tracker. RefCounting not
  367. // even started yet. ( Means UlpSendHttpResponseWorker hasn't been
  368. // called ). Therefore straight cleanup.
  369. //
  370. ASSERT( pTracker->RefCount == 1 );
  371. UlpFreeChunkTracker( &pTracker->WorkItem );
  372. }
  373. return Status;
  374. } // UlSendHttpResponse
  375. /***************************************************************************++
  376. Routine Description:
  377. Captures a user-mode HTTP response and morphs it into a form suitable
  378. for kernel-mode.
  379. Arguments:
  380. pUserResponse - Supplies the user-mode HTTP response.
  381. Flags - Supplies zero or more UL_CAPTURE_* flags.
  382. pStatusCode - Receives the captured HTTP status code.
  383. pKernelResponse - Receives the captured response if successful.
  384. Return Value:
  385. NTSTATUS - Completion status.
  386. --***************************************************************************/
  387. NTSTATUS
  388. UlCaptureHttpResponse(
  389. IN PUL_APP_POOL_PROCESS pProcess OPTIONAL,
  390. IN PHTTP_RESPONSE pUserResponse OPTIONAL,
  391. IN PUL_INTERNAL_REQUEST pRequest,
  392. IN USHORT ChunkCount,
  393. IN PHTTP_DATA_CHUNK pUserDataChunks,
  394. IN UL_CAPTURE_FLAGS Flags,
  395. IN ULONG SendFlags,
  396. IN BOOLEAN CaptureCache,
  397. IN PHTTP_LOG_FIELDS_DATA pUserLogData OPTIONAL,
  398. OUT PUSHORT pStatusCode,
  399. OUT PUL_INTERNAL_RESPONSE *ppKernelResponse
  400. )
  401. {
  402. ULONG i;
  403. NTSTATUS Status = STATUS_SUCCESS;
  404. PUL_INTERNAL_RESPONSE pKeResponse = NULL;
  405. PUL_HTTP_CONNECTION pHttpConn;
  406. ULONG AuxBufferLength;
  407. ULONG CopiedBufferLength;
  408. ULONG UncopiedBufferLength;
  409. PUCHAR pBuffer;
  410. ULONG HeaderLength;
  411. ULONG VariableHeaderLength = 0;
  412. ULONG SpaceLength;
  413. PUL_INTERNAL_DATA_CHUNK pKeDataChunks;
  414. BOOLEAN FromKernelMode;
  415. BOOLEAN FromLookaside;
  416. ULONG KernelChunkCount;
  417. HTTP_KNOWN_HEADER ETagHeader = { 0, NULL };
  418. HTTP_KNOWN_HEADER ContentEncodingHeader = { 0, NULL };
  419. KPROCESSOR_MODE RequestorMode;
  420. HTTP_VERSION Version;
  421. HTTP_VERB Verb;
  422. PHTTP_KNOWN_HEADER pKnownHeaders;
  423. USHORT RawValueLength;
  424. PCSTR pRawValue;
  425. UNICODE_STRING KernelFragmentName;
  426. UNICODE_STRING UserFragmentName;
  427. //
  428. // Sanity check.
  429. //
  430. PAGED_CODE();
  431. ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pRequest ) );
  432. ASSERT( pUserDataChunks != NULL || ChunkCount == 0 );
  433. ASSERT( ppKernelResponse != NULL );
  434. Version = pRequest->Version;
  435. Verb = pRequest->Verb;
  436. pHttpConn = pRequest->pHttpConn;
  437. __try
  438. {
  439. FromKernelMode =
  440. (BOOLEAN) ((Flags & UlCaptureKernelMode) == UlCaptureKernelMode);
  441. if (ChunkCount >= UL_MAX_CHUNKS)
  442. {
  443. Status = STATUS_INVALID_PARAMETER;
  444. goto end;
  445. }
  446. if (FromKernelMode)
  447. {
  448. RequestorMode = (KPROCESSOR_MODE) KernelMode;
  449. }
  450. else
  451. {
  452. RequestorMode = (KPROCESSOR_MODE) UserMode;
  453. }
  454. if (pUserResponse)
  455. {
  456. UlProbeForRead(
  457. pUserResponse,
  458. sizeof(HTTP_RESPONSE),
  459. sizeof(PVOID),
  460. RequestorMode
  461. );
  462. //
  463. // Remember the HTTP status code for ETW tracing.
  464. //
  465. if (pStatusCode)
  466. {
  467. *pStatusCode = pUserResponse->StatusCode;
  468. }
  469. }
  470. //
  471. // ProbeForRead every buffer we will access.
  472. //
  473. Status = UlpProbeHttpResponse(
  474. pUserResponse,
  475. ChunkCount,
  476. pUserDataChunks,
  477. Flags,
  478. pUserLogData,
  479. RequestorMode
  480. );
  481. if (!NT_SUCCESS(Status))
  482. {
  483. goto end;
  484. }
  485. //
  486. // Figure out how much memory we need.
  487. //
  488. Status = UlComputeFixedHeaderSize(
  489. Version,
  490. pUserResponse,
  491. RequestorMode,
  492. &HeaderLength
  493. );
  494. if (!NT_SUCCESS(Status))
  495. {
  496. goto end;
  497. }
  498. //
  499. // Allocate space for variable headers with fixed headers.
  500. //
  501. if (HeaderLength)
  502. {
  503. VariableHeaderLength = g_UlMaxVariableHeaderSize;
  504. }
  505. UlpComputeChunkBufferSizes(
  506. ChunkCount,
  507. pUserDataChunks,
  508. Flags,
  509. &AuxBufferLength,
  510. &CopiedBufferLength,
  511. &UncopiedBufferLength
  512. );
  513. UlTrace(SEND_RESPONSE, (
  514. "Http!UlCaptureHttpResponse(pUserResponse = %p) "
  515. " ChunkCount = %d\n"
  516. " Flags = 0x%x\n"
  517. " AuxBufferLength = 0x%x\n"
  518. " UncopiedBufferLength = 0x%x\n",
  519. pUserResponse,
  520. ChunkCount,
  521. Flags,
  522. AuxBufferLength,
  523. UncopiedBufferLength
  524. ));
  525. //
  526. // Add two extra chunks for the headers (fixed & variable).
  527. //
  528. if (HeaderLength > 0)
  529. {
  530. KernelChunkCount = ChunkCount + HEADER_CHUNK_COUNT;
  531. }
  532. else
  533. {
  534. KernelChunkCount = ChunkCount;
  535. }
  536. //
  537. // Compute the space needed for all of our structures.
  538. //
  539. SpaceLength = (KernelChunkCount * sizeof(UL_INTERNAL_DATA_CHUNK))
  540. + ALIGN_UP(HeaderLength, sizeof(CHAR))
  541. + ALIGN_UP(VariableHeaderLength, sizeof(CHAR))
  542. + AuxBufferLength;
  543. //
  544. // Add space for ETag and Content-Encoding if it exists, including space
  545. // for ANSI_NULL.
  546. //
  547. if (CaptureCache && pUserResponse)
  548. {
  549. ETagHeader = pUserResponse->Headers.KnownHeaders[HttpHeaderEtag];
  550. if (ETagHeader.RawValueLength)
  551. {
  552. SpaceLength += (ETagHeader.RawValueLength + sizeof(CHAR));
  553. UlProbeAnsiString(
  554. ETagHeader.pRawValue,
  555. ETagHeader.RawValueLength,
  556. RequestorMode
  557. );
  558. UlTrace(SEND_RESPONSE, (
  559. "http!UlCaptureHttpResponse(pUserResponse = %p)\n"
  560. " ETag: %s\n"
  561. " Length: %d\n",
  562. pUserResponse,
  563. ETagHeader.pRawValue,
  564. ETagHeader.RawValueLength
  565. ));
  566. }
  567. ContentEncodingHeader =
  568. pUserResponse->Headers.KnownHeaders[HttpHeaderContentEncoding];
  569. if (ContentEncodingHeader.RawValueLength)
  570. {
  571. SpaceLength += (ContentEncodingHeader.RawValueLength +
  572. sizeof(CHAR));
  573. UlProbeAnsiString(
  574. ContentEncodingHeader.pRawValue,
  575. ContentEncodingHeader.RawValueLength,
  576. RequestorMode
  577. );
  578. //
  579. // FUTURE: if the app sets the content encoding to "identity",
  580. // treat it as empty.
  581. //
  582. UlTrace(SEND_RESPONSE, (
  583. "http!UlCaptureHttpResponse(pUserResponse = %p)\n"
  584. " ContentEncoding: %s\n"
  585. " Length: %d\n",
  586. pUserResponse,
  587. ContentEncodingHeader.pRawValue,
  588. ContentEncodingHeader.RawValueLength
  589. ));
  590. }
  591. }
  592. //
  593. // Allocate the internal response.
  594. //
  595. if (pUserResponse &&
  596. g_UlResponseBufferSize >=
  597. (ALIGN_UP(sizeof(UL_INTERNAL_RESPONSE), PVOID) + SpaceLength))
  598. {
  599. pKeResponse = UlPplAllocateResponseBuffer();
  600. FromLookaside = TRUE;
  601. }
  602. else
  603. {
  604. pKeResponse = UL_ALLOCATE_STRUCT_WITH_SPACE(
  605. NonPagedPool,
  606. UL_INTERNAL_RESPONSE,
  607. SpaceLength,
  608. UL_INTERNAL_RESPONSE_POOL_TAG
  609. );
  610. FromLookaside = FALSE;
  611. }
  612. if (pKeResponse == NULL)
  613. {
  614. Status = STATUS_INSUFFICIENT_RESOURCES;
  615. goto end;
  616. }
  617. //
  618. // Initialize the fixed fields in the response.
  619. //
  620. pKeResponse->FromLookaside = FromLookaside;
  621. pKeResponse->Signature = UL_INTERNAL_RESPONSE_POOL_TAG;
  622. pKeResponse->ReferenceCount = 1;
  623. pKeResponse->ChunkCount = KernelChunkCount;
  624. //
  625. // Note that we set the current chunk to just *before* the first
  626. // chunk, then call the increment function. This allows us to go
  627. // through the common increment/update path.
  628. //
  629. pKeResponse->CurrentChunk = ULONG_MAX;
  630. pKeResponse->FileOffset.QuadPart = 0;
  631. pKeResponse->FileBytesRemaining.QuadPart = 0;
  632. RtlZeroMemory(
  633. pKeResponse->pDataChunks,
  634. sizeof(UL_INTERNAL_DATA_CHUNK) * KernelChunkCount
  635. );
  636. UL_REFERENCE_INTERNAL_REQUEST( pRequest );
  637. pKeResponse->pRequest = pRequest;
  638. pKeResponse->Flags = SendFlags;
  639. pKeResponse->SyncRead = FALSE;
  640. pKeResponse->ContentLengthSpecified = FALSE;
  641. pKeResponse->ChunkedSpecified = FALSE;
  642. pKeResponse->CopySend = FALSE;
  643. pKeResponse->ResponseLength = 0;
  644. pKeResponse->FromMemoryLength = 0;
  645. pKeResponse->BytesTransferred = 0;
  646. pKeResponse->IoStatus.Status = STATUS_SUCCESS;
  647. pKeResponse->IoStatus.Information = 0;
  648. pKeResponse->pIrp = NULL;
  649. pKeResponse->StatusCode = 0;
  650. pKeResponse->FromKernelMode = FromKernelMode;
  651. pKeResponse->MaxFileSystemStackSize = 0;
  652. pKeResponse->CreationTime.QuadPart = 0;
  653. pKeResponse->ETagLength = 0;
  654. pKeResponse->pETag = NULL;
  655. pKeResponse->pContentEncoding = NULL;
  656. pKeResponse->ContentEncodingLength = 0;
  657. pKeResponse->GenDateHeader = TRUE;
  658. pKeResponse->ConnHeader = ConnHdrKeepAlive;
  659. pKeResponse->pLogData = NULL;
  660. pKeResponse->pCompletionRoutine = NULL;
  661. pKeResponse->pCompletionContext = NULL;
  662. UlInitializePushLock(
  663. &pKeResponse->PushLock,
  664. "UL_INTERNAL_RESPONSE[%p].PushLock",
  665. pKeResponse,
  666. UL_INTERNAL_RESPONSE_PUSHLOCK_TAG
  667. );
  668. //
  669. // Decide whether we need to resume parsing and how. Ideally
  670. // if we have seen the last response, we should be able to
  671. // resume parsing right away after the send but before the
  672. // send completion. When requests are pipelined, this arrangement
  673. // alleviates the problem of delayed-ACK of 200ms when an odd numbers
  674. // of TCP frames are sent. The logic is disabled if we have reached
  675. // the limit of concurrent outstanding pipelined requests we allow.
  676. //
  677. if (0 == (SendFlags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA))
  678. {
  679. if (pHttpConn->PipelinedRequests < g_UlMaxPipelinedRequests &&
  680. 0 == pRequest->ContentLength &&
  681. 0 == pRequest->Chunked)
  682. {
  683. pKeResponse->ResumeParsingType = UlResumeParsingOnLastSend;
  684. }
  685. else
  686. {
  687. pKeResponse->ResumeParsingType = UlResumeParsingOnSendCompletion;
  688. }
  689. }
  690. else
  691. {
  692. pKeResponse->ResumeParsingType = UlResumeParsingNone;
  693. }
  694. RtlZeroMemory(
  695. &pKeResponse->ContentType,
  696. sizeof(UL_CONTENT_TYPE)
  697. );
  698. //
  699. // Point to the header buffer space.
  700. //
  701. pKeResponse->HeaderLength = HeaderLength;
  702. pKeResponse->pHeaders = (PUCHAR)
  703. (pKeResponse->pDataChunks + pKeResponse->ChunkCount);
  704. //
  705. // And the variable header buffer space.
  706. //
  707. pKeResponse->VariableHeaderLength = VariableHeaderLength;
  708. pKeResponse->pVariableHeader = pKeResponse->pHeaders + HeaderLength;
  709. //
  710. // And the aux buffer space.
  711. //
  712. pKeResponse->AuxBufferLength = AuxBufferLength;
  713. pKeResponse->pAuxiliaryBuffer = (PVOID)
  714. (pKeResponse->pHeaders + HeaderLength + VariableHeaderLength);
  715. //
  716. // And the ETag and Content-Encoding buffer space plus the ANSI_NULLs.
  717. //
  718. if (ETagHeader.RawValueLength)
  719. {
  720. pKeResponse->ETagLength = ETagHeader.RawValueLength + sizeof(CHAR);
  721. pKeResponse->pETag = (PUCHAR) pKeResponse->pAuxiliaryBuffer +
  722. AuxBufferLength;
  723. }
  724. if (ContentEncodingHeader.RawValueLength)
  725. {
  726. pKeResponse->ContentEncodingLength =
  727. (ContentEncodingHeader.RawValueLength + sizeof(CHAR));
  728. pKeResponse->pContentEncoding =
  729. (PUCHAR) pKeResponse->pAuxiliaryBuffer +
  730. AuxBufferLength +
  731. pKeResponse->ETagLength ;
  732. }
  733. //
  734. // Remember if a Content-Length header was specified.
  735. //
  736. if (pUserResponse != NULL)
  737. {
  738. pKeResponse->Verb = Verb;
  739. pKeResponse->StatusCode = pUserResponse->StatusCode;
  740. if (pKeResponse->StatusCode > UL_MAX_HTTP_STATUS_CODE)
  741. {
  742. ExRaiseStatus( STATUS_INVALID_PARAMETER );
  743. }
  744. pKnownHeaders = pUserResponse->Headers.KnownHeaders;
  745. //
  746. // If the response explicitly deletes the Connection: header,
  747. // make sure we DON'T generate it.
  748. //
  749. RawValueLength = pKnownHeaders[HttpHeaderConnection].RawValueLength;
  750. pRawValue = pKnownHeaders[HttpHeaderConnection].pRawValue;
  751. if (0 == RawValueLength && NULL != pRawValue)
  752. {
  753. UlProbeAnsiString(
  754. pRawValue,
  755. sizeof(ANSI_NULL),
  756. RequestorMode
  757. );
  758. if (ANSI_NULL == pRawValue[0])
  759. {
  760. pKeResponse->ConnHeader = ConnHdrNone;
  761. }
  762. }
  763. //
  764. // Decide if we need to generate a Date: header.
  765. //
  766. RawValueLength = pKnownHeaders[HttpHeaderDate].RawValueLength;
  767. pRawValue = pKnownHeaders[HttpHeaderDate].pRawValue;
  768. if (0 == RawValueLength && NULL != pRawValue)
  769. {
  770. UlProbeAnsiString(
  771. pRawValue,
  772. sizeof(ANSI_NULL),
  773. RequestorMode
  774. );
  775. //
  776. // Only permit non-generation in the "delete" case.
  777. //
  778. if (ANSI_NULL == pRawValue[0])
  779. {
  780. pKeResponse->GenDateHeader = FALSE;
  781. }
  782. }
  783. else
  784. {
  785. pKeResponse->GenDateHeader = TRUE;
  786. }
  787. if (pKnownHeaders[HttpHeaderContentLength].RawValueLength > 0)
  788. {
  789. pKeResponse->ContentLengthSpecified = TRUE;
  790. }
  791. //
  792. // As long as we're here, also remember if "Chunked"
  793. // Transfer-Encoding was specified.
  794. //
  795. if (UlpIsChunkSpecified(pKnownHeaders, RequestorMode))
  796. {
  797. //
  798. // NOTE: If a response has a chunked Transfer-Encoding,
  799. // then it shouldn't have a Content-Length
  800. //
  801. if (pKeResponse->ContentLengthSpecified)
  802. {
  803. ExRaiseStatus( STATUS_INVALID_PARAMETER );
  804. }
  805. pKeResponse->ChunkedSpecified = TRUE;
  806. }
  807. //
  808. // Only capture the following if we're building a cached response.
  809. //
  810. if (CaptureCache)
  811. {
  812. //
  813. // Capture the ETag and put it on the UL_INTERNAL_RESPONSE.
  814. //
  815. if (ETagHeader.RawValueLength)
  816. {
  817. //
  818. // NOTE: Already probed above
  819. //
  820. RtlCopyMemory(
  821. pKeResponse->pETag, // Dest
  822. ETagHeader.pRawValue, // Src
  823. ETagHeader.RawValueLength // Bytes
  824. );
  825. //
  826. // Add NULL termination.
  827. //
  828. pKeResponse->pETag[ETagHeader.RawValueLength] = ANSI_NULL;
  829. }
  830. //
  831. // Capture the ContentType and put it on the
  832. // UL_INTERNAL_RESPONSE.
  833. //
  834. pRawValue = pKnownHeaders[HttpHeaderContentType].pRawValue;
  835. RawValueLength =
  836. pKnownHeaders[HttpHeaderContentType].RawValueLength;
  837. if (RawValueLength > 0)
  838. {
  839. UlProbeAnsiString(
  840. pRawValue,
  841. RawValueLength,
  842. RequestorMode
  843. );
  844. UlGetTypeAndSubType(
  845. pRawValue,
  846. RawValueLength,
  847. &pKeResponse->ContentType
  848. );
  849. UlTrace(SEND_RESPONSE, (
  850. "http!UlCaptureHttpResponse(pUserResponse = %p) \n"
  851. " Content Type: %s \n"
  852. " Content SubType: %s\n",
  853. pUserResponse,
  854. (pKeResponse->ContentType.Type ?
  855. pKeResponse->ContentType.Type : (PUCHAR)"<none>"),
  856. (pKeResponse->ContentType.SubType ?
  857. pKeResponse->ContentType.SubType : (PUCHAR)"<none>")
  858. ));
  859. }
  860. //
  861. // Capture the Content-Encoding
  862. //
  863. if (ContentEncodingHeader.RawValueLength)
  864. {
  865. //
  866. // NOTE: Already probed above
  867. //
  868. RtlCopyMemory(
  869. pKeResponse->pContentEncoding, // Dest
  870. ContentEncodingHeader.pRawValue, // Src
  871. ContentEncodingHeader.RawValueLength // Bytes
  872. );
  873. //
  874. // Add NULL termination.
  875. //
  876. pKeResponse->pContentEncoding[
  877. ContentEncodingHeader.RawValueLength] = ANSI_NULL;
  878. }
  879. //
  880. // Capture the Last-Modified time (if it exists).
  881. //
  882. pRawValue = pKnownHeaders[HttpHeaderLastModified].pRawValue;
  883. RawValueLength =
  884. pKnownHeaders[HttpHeaderLastModified].RawValueLength;
  885. if (RawValueLength)
  886. {
  887. UlProbeAnsiString(
  888. pRawValue,
  889. RawValueLength,
  890. RequestorMode
  891. );
  892. if (!StringTimeToSystemTime(
  893. pRawValue,
  894. RawValueLength,
  895. &pKeResponse->CreationTime
  896. ))
  897. {
  898. ExRaiseStatus( STATUS_INVALID_PARAMETER );
  899. }
  900. }
  901. else
  902. {
  903. KeQuerySystemTime( &pKeResponse->CreationTime );
  904. }
  905. }
  906. }
  907. //
  908. // Copy the aux bytes from the chunks.
  909. //
  910. pBuffer = (PUCHAR) pKeResponse->pAuxiliaryBuffer;
  911. //
  912. // Skip the header chunks.
  913. //
  914. if (pKeResponse->HeaderLength > 0)
  915. {
  916. pKeDataChunks = pKeResponse->pDataChunks + HEADER_CHUNK_COUNT;
  917. }
  918. else
  919. {
  920. pKeDataChunks = pKeResponse->pDataChunks;
  921. }
  922. for (i = 0 ; i < ChunkCount ; i++)
  923. {
  924. pKeDataChunks[i].ChunkType = pUserDataChunks[i].DataChunkType;
  925. switch (pUserDataChunks[i].DataChunkType)
  926. {
  927. case HttpDataChunkFromMemory:
  928. //
  929. // From-memory chunk. If the caller wants us to copy
  930. // the data (or if its relatively small), then do it
  931. // We allocate space for all of the copied data and any
  932. // filename buffers. Otherwise (it's OK to just lock
  933. // down the data), then allocate a MDL describing the
  934. // user's buffer and lock it down. Note that
  935. // MmProbeAndLockPages() and MmLockPagesSpecifyCache()
  936. // will raise exceptions if they fail.
  937. //
  938. pKeResponse->FromMemoryLength +=
  939. pUserDataChunks[i].FromMemory.BufferLength;
  940. pKeDataChunks[i].FromMemory.pCopiedBuffer = NULL;
  941. if ((Flags & UlCaptureCopyData) ||
  942. pUserDataChunks[i].FromMemory.BufferLength
  943. <= g_UlMaxCopyThreshold)
  944. {
  945. ASSERT(pKeResponse->AuxBufferLength > 0);
  946. pKeDataChunks[i].FromMemory.pUserBuffer =
  947. pUserDataChunks[i].FromMemory.pBuffer;
  948. pKeDataChunks[i].FromMemory.BufferLength =
  949. pUserDataChunks[i].FromMemory.BufferLength;
  950. RtlCopyMemory(
  951. pBuffer,
  952. pKeDataChunks[i].FromMemory.pUserBuffer,
  953. pKeDataChunks[i].FromMemory.BufferLength
  954. );
  955. pKeDataChunks[i].FromMemory.pCopiedBuffer = pBuffer;
  956. pBuffer += pKeDataChunks[i].FromMemory.BufferLength;
  957. //
  958. // Allocate a new MDL describing our new location
  959. // in the auxiliary buffer, then build the MDL
  960. // to properly describe nonpaged pool.
  961. //
  962. pKeDataChunks[i].FromMemory.pMdl =
  963. UlAllocateMdl(
  964. pKeDataChunks[i].FromMemory.pCopiedBuffer,
  965. pKeDataChunks[i].FromMemory.BufferLength,
  966. FALSE,
  967. FALSE,
  968. NULL
  969. );
  970. if (pKeDataChunks[i].FromMemory.pMdl == NULL)
  971. {
  972. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  973. break;
  974. }
  975. MmBuildMdlForNonPagedPool(
  976. pKeDataChunks[i].FromMemory.pMdl
  977. );
  978. }
  979. else
  980. {
  981. //
  982. // Build a MDL describing the user's buffer.
  983. //
  984. pKeDataChunks[i].FromMemory.BufferLength =
  985. pUserDataChunks[i].FromMemory.BufferLength;
  986. pKeDataChunks[i].FromMemory.pMdl =
  987. UlAllocateMdl(
  988. pUserDataChunks[i].FromMemory.pBuffer,
  989. pUserDataChunks[i].FromMemory.BufferLength,
  990. FALSE,
  991. FALSE,
  992. NULL
  993. );
  994. if (pKeDataChunks[i].FromMemory.pMdl == NULL)
  995. {
  996. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  997. break;
  998. }
  999. //
  1000. // Lock it down.
  1001. //
  1002. MmProbeAndLockPages(
  1003. pKeDataChunks[i].FromMemory.pMdl, // MDL
  1004. UserMode, // AccessMode
  1005. IoReadAccess // Operation
  1006. );
  1007. if (CaptureCache)
  1008. {
  1009. MmMapLockedPagesSpecifyCache(
  1010. pKeDataChunks[i].FromMemory.pMdl,
  1011. KernelMode,
  1012. MmCached,
  1013. NULL,
  1014. FALSE,
  1015. LowPagePriority
  1016. );
  1017. }
  1018. }
  1019. break;
  1020. case HttpDataChunkFromFileHandle:
  1021. //
  1022. // From handle.
  1023. //
  1024. pKeDataChunks[i].FromFileHandle.ByteRange =
  1025. pUserDataChunks[i].FromFileHandle.ByteRange;
  1026. pKeDataChunks[i].FromFileHandle.FileHandle =
  1027. pUserDataChunks[i].FromFileHandle.FileHandle;
  1028. break;
  1029. case HttpDataChunkFromFragmentCache:
  1030. //
  1031. // From fragment cache.
  1032. //
  1033. if (CaptureCache)
  1034. {
  1035. //
  1036. // Content from fragment cache are meant to be dynamic
  1037. // so they shouldn't go to the static response cache.
  1038. //
  1039. Status = STATUS_NOT_SUPPORTED;
  1040. goto end;
  1041. }
  1042. UserFragmentName.Buffer = (PWSTR)
  1043. pUserDataChunks[i].FromFragmentCache.pFragmentName;
  1044. UserFragmentName.Length =
  1045. pUserDataChunks[i].FromFragmentCache.FragmentNameLength;
  1046. UserFragmentName.MaximumLength =
  1047. UserFragmentName.Length;
  1048. Status = UlProbeAndCaptureUnicodeString(
  1049. &UserFragmentName,
  1050. RequestorMode,
  1051. &KernelFragmentName,
  1052. 0
  1053. );
  1054. if (!NT_SUCCESS(Status))
  1055. {
  1056. goto end;
  1057. }
  1058. Status = UlCheckoutFragmentCacheEntry(
  1059. KernelFragmentName.Buffer,
  1060. KernelFragmentName.Length,
  1061. pProcess,
  1062. &pKeDataChunks[i].FromFragmentCache.pCacheEntry
  1063. );
  1064. UlFreeCapturedUnicodeString( &KernelFragmentName );
  1065. if (!NT_SUCCESS(Status))
  1066. {
  1067. goto end;
  1068. }
  1069. ASSERT( pKeDataChunks[i].FromFragmentCache.pCacheEntry );
  1070. break;
  1071. default :
  1072. ExRaiseStatus( STATUS_INVALID_PARAMETER );
  1073. break;
  1074. } // switch (pUserDataChunks[i].DataChunkType)
  1075. } // for (i = 0 ; i < ChunkCount ; i++)
  1076. //
  1077. // Ensure we didn't mess up our buffer calculations.
  1078. //
  1079. ASSERT( DIFF(pBuffer - (PUCHAR)(pKeResponse->pAuxiliaryBuffer)) ==
  1080. AuxBufferLength );
  1081. UlTrace(SEND_RESPONSE, (
  1082. "Http!UlCaptureHttpResponse: captured %p from user %p\n",
  1083. pKeResponse,
  1084. pUserResponse
  1085. ));
  1086. }
  1087. __except( UL_EXCEPTION_FILTER() )
  1088. {
  1089. Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() );
  1090. }
  1091. end:
  1092. if (NT_SUCCESS(Status) == FALSE)
  1093. {
  1094. if (pKeResponse != NULL)
  1095. {
  1096. UlpDestroyCapturedResponse( pKeResponse );
  1097. pKeResponse = NULL;
  1098. }
  1099. }
  1100. //
  1101. // Return the captured response.
  1102. //
  1103. *ppKernelResponse = pKeResponse;
  1104. RETURN( Status );
  1105. } // UlCaptureHttpResponse
  1106. /***************************************************************************++
  1107. Routine Description:
  1108. Captures a user-mode log data to kernel pLogData structure.
  1109. Arguments:
  1110. pCapturedUserLogData - Supplies the captured HTTP_LOG_FIELDS_DATA.
  1111. However there are still embedded pointers pointing to user mode
  1112. memory inside this captured structure.
  1113. pRequest - Supplies the request to capture.
  1114. ppKernelLogData - Buffer to hold the pointer to the newly allocated
  1115. LogData. WILL be set to null if logging is disabled
  1116. Return Value:
  1117. NTSTATUS - Completion status.
  1118. --***************************************************************************/
  1119. NTSTATUS
  1120. UlCaptureUserLogData(
  1121. IN PHTTP_LOG_FIELDS_DATA pCapturedUserLogData,
  1122. IN PUL_INTERNAL_REQUEST pRequest,
  1123. OUT PUL_LOG_DATA_BUFFER *ppKernelLogData
  1124. )
  1125. {
  1126. NTSTATUS Status = STATUS_SUCCESS;
  1127. PUL_CONFIG_GROUP_OBJECT pLoggingConfig;
  1128. BOOLEAN BinaryLoggingEnabled;
  1129. PUL_LOG_DATA_BUFFER pLogData;
  1130. ASSERT( pCapturedUserLogData );
  1131. ASSERT( ppKernelLogData );
  1132. ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pRequest ) );
  1133. //
  1134. // Init the caller's pLogData pointer to NULL.
  1135. //
  1136. *ppKernelLogData = pLogData = NULL;
  1137. //
  1138. // Capture the log data. Note that the binary logging takes
  1139. // precedence over the normal logging.
  1140. //
  1141. BinaryLoggingEnabled = UlBinaryLoggingEnabled(
  1142. pRequest->ConfigInfo.pControlChannel
  1143. );
  1144. pLoggingConfig = pRequest->ConfigInfo.pLoggingConfig;
  1145. __try
  1146. {
  1147. //
  1148. // If either type of logging is enabled, then allocate a kernel buffer
  1149. // and capture it down.
  1150. //
  1151. if (BinaryLoggingEnabled)
  1152. {
  1153. Status = UlCaptureRawLogData(
  1154. pCapturedUserLogData,
  1155. pRequest,
  1156. &pLogData
  1157. );
  1158. }
  1159. else
  1160. if (pLoggingConfig)
  1161. {
  1162. ASSERT( IS_VALID_CONFIG_GROUP( pLoggingConfig ) );
  1163. switch(pLoggingConfig->LoggingConfig.LogFormat)
  1164. {
  1165. case HttpLoggingTypeW3C:
  1166. Status = UlCaptureLogFieldsW3C(
  1167. pCapturedUserLogData,
  1168. pRequest,
  1169. &pLogData
  1170. );
  1171. break;
  1172. case HttpLoggingTypeNCSA:
  1173. Status = UlCaptureLogFieldsNCSA(
  1174. pCapturedUserLogData,
  1175. pRequest,
  1176. &pLogData
  1177. );
  1178. break;
  1179. case HttpLoggingTypeIIS:
  1180. Status = UlCaptureLogFieldsIIS(
  1181. pCapturedUserLogData,
  1182. pRequest,
  1183. &pLogData
  1184. );
  1185. break;
  1186. default:
  1187. ASSERT( !"Invalid Text Logging Format!" );
  1188. return STATUS_INVALID_PARAMETER;
  1189. }
  1190. }
  1191. else
  1192. {
  1193. return STATUS_SUCCESS; // Logging is disabled for this site.
  1194. }
  1195. if (NT_SUCCESS(Status))
  1196. {
  1197. //
  1198. // Success set the callers buffer to point to the freshly
  1199. // allocated/formatted pLogData.
  1200. //
  1201. ASSERT( IS_VALID_LOG_DATA_BUFFER( pLogData ) );
  1202. *ppKernelLogData = pLogData;
  1203. }
  1204. else
  1205. {
  1206. //
  1207. // If the logging capture function returns an error,
  1208. // kernel buffer should not have been allocated.
  1209. //
  1210. ASSERT( pLogData == NULL );
  1211. }
  1212. }
  1213. __except( UL_EXCEPTION_FILTER() )
  1214. {
  1215. if (pLogData)
  1216. {
  1217. //
  1218. // If the logging capture function raised and exception
  1219. // after allocating a pLogData, we need to cleanup here.
  1220. //
  1221. UlDestroyLogDataBuffer( pLogData );
  1222. }
  1223. Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() );
  1224. }
  1225. return Status;
  1226. } // UlCaptureUserLogData
  1227. /***************************************************************************++
  1228. Routine Description:
  1229. Probes all the buffers passed to use in a user-mode HTTP response.
  1230. Arguments:
  1231. pUserResponse - Supplies the response to probe.
  1232. ChunkCount - Supplies the number of data chunks.
  1233. pDataChunks - Supplies the array of data chunks.
  1234. Flags - Capture flags.
  1235. Return Value:
  1236. NTSTATUS - Completion status.
  1237. --***************************************************************************/
  1238. NTSTATUS
  1239. UlpProbeHttpResponse(
  1240. IN PHTTP_RESPONSE pUserResponse OPTIONAL,
  1241. IN USHORT ChunkCount,
  1242. IN PHTTP_DATA_CHUNK pCapturedDataChunks OPTIONAL,
  1243. IN UL_CAPTURE_FLAGS Flags,
  1244. IN PHTTP_LOG_FIELDS_DATA pCapturedLogData OPTIONAL,
  1245. IN KPROCESSOR_MODE RequestorMode
  1246. )
  1247. {
  1248. USHORT KeyUriLength;
  1249. PCWSTR pKeyUri;
  1250. NTSTATUS Status;
  1251. ULONG i;
  1252. Status = STATUS_SUCCESS;
  1253. __try
  1254. {
  1255. //
  1256. // Probe the response structure if it exits.
  1257. //
  1258. if (pUserResponse)
  1259. {
  1260. if (pUserResponse->Flags & ~HTTP_RESPONSE_FLAG_VALID)
  1261. {
  1262. ExRaiseStatus( STATUS_INVALID_PARAMETER );
  1263. }
  1264. //
  1265. // We don't support trailers for this release.
  1266. //
  1267. if (pUserResponse->Headers.TrailerCount != 0 ||
  1268. pUserResponse->Headers.pTrailers != NULL)
  1269. {
  1270. ExRaiseStatus( STATUS_INVALID_PARAMETER );
  1271. }
  1272. }
  1273. //
  1274. // Probe the log data if it exists.
  1275. //
  1276. if (pCapturedLogData)
  1277. {
  1278. UlProbeLogData( pCapturedLogData, RequestorMode );
  1279. }
  1280. //
  1281. // And now the body part.
  1282. //
  1283. if (pCapturedDataChunks != NULL)
  1284. {
  1285. for (i = 0 ; i < ChunkCount ; i++)
  1286. {
  1287. switch (pCapturedDataChunks[i].DataChunkType)
  1288. {
  1289. case HttpDataChunkFromMemory:
  1290. //
  1291. // From-memory chunk.
  1292. //
  1293. if (pCapturedDataChunks[i].FromMemory.BufferLength == 0 ||
  1294. pCapturedDataChunks[i].FromMemory.pBuffer == NULL)
  1295. {
  1296. return STATUS_INVALID_PARAMETER;
  1297. }
  1298. if ((Flags & UlCaptureCopyData) ||
  1299. pCapturedDataChunks[i].FromMemory.BufferLength <=
  1300. g_UlMaxCopyThreshold)
  1301. {
  1302. UlProbeForRead(
  1303. pCapturedDataChunks[i].FromMemory.pBuffer,
  1304. pCapturedDataChunks[i].FromMemory.BufferLength,
  1305. sizeof(CHAR),
  1306. RequestorMode
  1307. );
  1308. }
  1309. break;
  1310. case HttpDataChunkFromFileHandle:
  1311. //
  1312. // From handle chunk. the handle will be validated later
  1313. // by the object manager.
  1314. //
  1315. break;
  1316. case HttpDataChunkFromFragmentCache:
  1317. KeyUriLength =
  1318. pCapturedDataChunks[i].FromFragmentCache.FragmentNameLength;
  1319. pKeyUri =
  1320. pCapturedDataChunks[i].FromFragmentCache.pFragmentName;
  1321. //
  1322. // From-fragment-cache chunk. Probe the KeyUri buffer.
  1323. //
  1324. UlProbeWideString(
  1325. pKeyUri,
  1326. KeyUriLength,
  1327. RequestorMode
  1328. );
  1329. break;
  1330. default :
  1331. Status = STATUS_INVALID_PARAMETER;
  1332. break;
  1333. } // switch (pCapturedDataChunks[i].DataChunkType)
  1334. } // for (i = 0 ; i < ChunkCount ; i++)
  1335. } // if (pCapturedDataChunks != NULL)
  1336. }
  1337. __except( UL_EXCEPTION_FILTER() )
  1338. {
  1339. Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() );
  1340. }
  1341. return Status;
  1342. } // UlpProbeHttpResponse
  1343. /***************************************************************************++
  1344. Routine Description:
  1345. Figures out how much space we need in the internal response aux buffer.
  1346. The buffer contains copied memory chunks, and names of files to open.
  1347. CODEWORK: need to be aware of chunk encoding.
  1348. Arguments:
  1349. ChunkCount - The number of chunks in the array.
  1350. pDataChunks - The array of data chunks.
  1351. Flags - Capture flags.
  1352. pAuxBufferSize - Returns the size of the aux buffer.
  1353. pCopiedMemorySize - Returns the size of user buffer that is going to
  1354. be copied.
  1355. pUncopiedMemorySize - Returns the size of the user buffer that is
  1356. going to be ProbeAndLocked.
  1357. Return Value:
  1358. None.
  1359. --***************************************************************************/
  1360. VOID
  1361. UlpComputeChunkBufferSizes(
  1362. IN ULONG ChunkCount,
  1363. IN PHTTP_DATA_CHUNK pDataChunks,
  1364. IN UL_CAPTURE_FLAGS Flags,
  1365. OUT PULONG pAuxBufferSize,
  1366. OUT PULONG pCopiedMemorySize,
  1367. OUT PULONG pUncopiedMemorySize
  1368. )
  1369. {
  1370. ULONG AuxLength = 0;
  1371. ULONG CopiedLength = 0;
  1372. ULONG UncopiedLength = 0;
  1373. ULONG i;
  1374. for (i = 0; i < ChunkCount; i++)
  1375. {
  1376. switch (pDataChunks[i].DataChunkType)
  1377. {
  1378. case HttpDataChunkFromMemory:
  1379. //
  1380. // If we're going to copy the chunk, then make some space in
  1381. // the aux buffer.
  1382. //
  1383. if ((Flags & UlCaptureCopyData) ||
  1384. pDataChunks[i].FromMemory.BufferLength <= g_UlMaxCopyThreshold)
  1385. {
  1386. AuxLength += pDataChunks[i].FromMemory.BufferLength;
  1387. CopiedLength += pDataChunks[i].FromMemory.BufferLength;
  1388. }
  1389. else
  1390. {
  1391. UncopiedLength += pDataChunks[i].FromMemory.BufferLength;
  1392. }
  1393. break;
  1394. case HttpDataChunkFromFileHandle:
  1395. case HttpDataChunkFromFragmentCache:
  1396. break;
  1397. default:
  1398. //
  1399. // We should have caught this in the probe.
  1400. //
  1401. ASSERT( !"Invalid chunk type" );
  1402. break;
  1403. }
  1404. }
  1405. *pAuxBufferSize = AuxLength;
  1406. *pCopiedMemorySize = CopiedLength;
  1407. *pUncopiedMemorySize = UncopiedLength;
  1408. } // UlpComputeChunkBufferSizes
  1409. /***************************************************************************++
  1410. Routine Description:
  1411. Prepares the specified response for sending. This preparation
  1412. consists mostly of opening any files referenced by the response.
  1413. Arguments:
  1414. Version - Supplies the version of the user response to prepare.
  1415. pUserResponse - Supplies the user response to prepare.
  1416. pResponse - Supplies the kernel response to prepare.
  1417. AccessMode - Supplies the access mode.
  1418. Return Value:
  1419. NTSTATUS - Completion status.
  1420. --***************************************************************************/
  1421. NTSTATUS
  1422. UlPrepareHttpResponse(
  1423. IN HTTP_VERSION Version,
  1424. IN PHTTP_RESPONSE pUserResponse,
  1425. IN PUL_INTERNAL_RESPONSE pResponse,
  1426. IN KPROCESSOR_MODE AccessMode
  1427. )
  1428. {
  1429. ULONG i;
  1430. NTSTATUS Status = STATUS_SUCCESS;
  1431. PUL_INTERNAL_DATA_CHUNK pInternalChunk;
  1432. PUL_FILE_CACHE_ENTRY pFileCacheEntry;
  1433. ULONG HeaderLength;
  1434. CCHAR MaxStackSize = 0;
  1435. //
  1436. // Sanity check.
  1437. //
  1438. PAGED_CODE();
  1439. UlTrace(SEND_RESPONSE, (
  1440. "Http!UlPrepareHttpResponse: response %p\n",
  1441. pResponse
  1442. ));
  1443. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pResponse ) );
  1444. //
  1445. // Build the HTTP response protocol part and check that the caller
  1446. // passed in headers to send.
  1447. //
  1448. if (pResponse->HeaderLength > 0)
  1449. {
  1450. ASSERT( pUserResponse != NULL );
  1451. //
  1452. // Generate the fixed headers.
  1453. //
  1454. Status = UlGenerateFixedHeaders(
  1455. Version,
  1456. pUserResponse,
  1457. pResponse->StatusCode,
  1458. pResponse->HeaderLength,
  1459. AccessMode,
  1460. pResponse->pHeaders,
  1461. &HeaderLength
  1462. );
  1463. if (!NT_SUCCESS(Status))
  1464. goto end;
  1465. if (HeaderLength < pResponse->HeaderLength)
  1466. {
  1467. Status = STATUS_INVALID_PARAMETER;
  1468. goto end;
  1469. }
  1470. //
  1471. // It is possible that no headers got generated (0.9 request).
  1472. //
  1473. if (HeaderLength > 0)
  1474. {
  1475. //
  1476. // Build a MDL for it.
  1477. //
  1478. pResponse->pDataChunks[0].ChunkType = HttpDataChunkFromMemory;
  1479. pResponse->pDataChunks[0].FromMemory.BufferLength =
  1480. pResponse->HeaderLength;
  1481. pResponse->pDataChunks[0].FromMemory.pMdl =
  1482. UlAllocateMdl(
  1483. pResponse->pHeaders, // VirtualAddress
  1484. pResponse->HeaderLength, // Length
  1485. FALSE, // SecondaryBuffer
  1486. FALSE, // ChargeQuota
  1487. NULL // Irp
  1488. );
  1489. if (pResponse->pDataChunks[0].FromMemory.pMdl == NULL)
  1490. {
  1491. Status = STATUS_INSUFFICIENT_RESOURCES;
  1492. goto end;
  1493. }
  1494. MmBuildMdlForNonPagedPool(
  1495. pResponse->pDataChunks[0].FromMemory.pMdl
  1496. );
  1497. }
  1498. }
  1499. //
  1500. // Scan the chunks looking for "from file" chunks.
  1501. //
  1502. for (i = 0, pInternalChunk = pResponse->pDataChunks;
  1503. i < pResponse->ChunkCount;
  1504. i++, pInternalChunk++)
  1505. {
  1506. switch (pInternalChunk->ChunkType)
  1507. {
  1508. case HttpDataChunkFromFileHandle:
  1509. //
  1510. // File chunk.
  1511. //
  1512. pFileCacheEntry = &pInternalChunk->FromFileHandle.FileCacheEntry;
  1513. UlTrace(SEND_RESPONSE, (
  1514. "UlPrepareHttpResponse: opening handle %p\n",
  1515. &pInternalChunk->FromFileHandle.FileHandle
  1516. ));
  1517. //
  1518. // Found one. Try to open it.
  1519. //
  1520. Status = UlCreateFileEntry(
  1521. pInternalChunk->FromFileHandle.FileHandle,
  1522. pFileCacheEntry
  1523. );
  1524. if (NT_SUCCESS(Status) == FALSE)
  1525. goto end;
  1526. //
  1527. // Check if this is going to be a sync read. All sync reads
  1528. // goto special thread pools since they can be blocking.
  1529. //
  1530. if (!pFileCacheEntry->BytesPerSector)
  1531. {
  1532. pResponse->SyncRead = TRUE;
  1533. }
  1534. if (pFileCacheEntry->pDeviceObject->StackSize > MaxStackSize)
  1535. {
  1536. MaxStackSize = pFileCacheEntry->pDeviceObject->StackSize;
  1537. }
  1538. //
  1539. // Validate & sanitize the specified byte range.
  1540. //
  1541. Status = UlSanitizeFileByteRange(
  1542. &pInternalChunk->FromFileHandle.ByteRange,
  1543. &pInternalChunk->FromFileHandle.ByteRange,
  1544. pFileCacheEntry->EndOfFile.QuadPart
  1545. );
  1546. if (NT_SUCCESS(Status) == FALSE)
  1547. goto end;
  1548. pResponse->ResponseLength +=
  1549. pInternalChunk->FromFileHandle.ByteRange.Length.QuadPart;
  1550. break;
  1551. case HttpDataChunkFromMemory:
  1552. pResponse->ResponseLength +=
  1553. pInternalChunk->FromMemory.BufferLength;
  1554. break;
  1555. case HttpDataChunkFromFragmentCache:
  1556. pResponse->ResponseLength +=
  1557. pInternalChunk->FromFragmentCache.pCacheEntry->ContentLength;
  1558. break;
  1559. default:
  1560. ASSERT( FALSE );
  1561. Status = STATUS_INVALID_PARAMETER;
  1562. goto end;
  1563. } // switch (pInternalChunk->ChunkType)
  1564. }
  1565. pResponse->MaxFileSystemStackSize = MaxStackSize;
  1566. end:
  1567. if (NT_SUCCESS(Status) == FALSE)
  1568. {
  1569. //
  1570. // Undo anything done above.
  1571. //
  1572. UlCleanupHttpResponse( pResponse );
  1573. }
  1574. RETURN( Status );
  1575. } // UlPrepareHttpResponse
  1576. /***************************************************************************++
  1577. Routine Description:
  1578. Cleans a response by undoing anything done in UlPrepareHttpResponse().
  1579. Arguments:
  1580. pResponse - Supplies the response to cleanup.
  1581. Return Value:
  1582. None.
  1583. --***************************************************************************/
  1584. VOID
  1585. UlCleanupHttpResponse(
  1586. IN PUL_INTERNAL_RESPONSE pResponse
  1587. )
  1588. {
  1589. ULONG i;
  1590. PUL_INTERNAL_DATA_CHUNK pInternalChunk;
  1591. //
  1592. // Sanity check.
  1593. //
  1594. PAGED_CODE();
  1595. UlTrace(SEND_RESPONSE, (
  1596. "UlCleanupHttpResponse: response %p\n",
  1597. pResponse
  1598. ));
  1599. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pResponse ) );
  1600. //
  1601. // Scan the chunks looking for "from file" chunks.
  1602. //
  1603. pInternalChunk = pResponse->pDataChunks;
  1604. for (i = 0; i < pResponse->ChunkCount; i++, pInternalChunk++)
  1605. {
  1606. if (IS_FROM_FILE_HANDLE(pInternalChunk))
  1607. {
  1608. if (!pInternalChunk->FromFileHandle.FileCacheEntry.pFileObject)
  1609. {
  1610. break;
  1611. }
  1612. UlDestroyFileCacheEntry(
  1613. &pInternalChunk->FromFileHandle.FileCacheEntry
  1614. );
  1615. pInternalChunk->FromFileHandle.FileCacheEntry.pFileObject = NULL;
  1616. }
  1617. else
  1618. {
  1619. ASSERT( IS_FROM_MEMORY( pInternalChunk ) ||
  1620. IS_FROM_FRAGMENT_CACHE( pInternalChunk ) );
  1621. }
  1622. }
  1623. } // UlCleanupHttpResponse
  1624. /***************************************************************************++
  1625. Routine Description:
  1626. References the specified response.
  1627. Arguments:
  1628. pResponse - Supplies the response to reference.
  1629. Return Value:
  1630. None.
  1631. --***************************************************************************/
  1632. VOID
  1633. UlReferenceHttpResponse(
  1634. IN PUL_INTERNAL_RESPONSE pResponse
  1635. REFERENCE_DEBUG_FORMAL_PARAMS
  1636. )
  1637. {
  1638. LONG RefCount;
  1639. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pResponse ) );
  1640. RefCount = InterlockedIncrement( &pResponse->ReferenceCount );
  1641. WRITE_REF_TRACE_LOG(
  1642. g_pHttpResponseTraceLog,
  1643. REF_ACTION_REFERENCE_HTTP_RESPONSE,
  1644. RefCount,
  1645. pResponse,
  1646. pFileName,
  1647. LineNumber
  1648. );
  1649. UlTrace(SEND_RESPONSE, (
  1650. "UlReferenceHttpResponse: response %p refcount %ld\n",
  1651. pResponse,
  1652. RefCount
  1653. ));
  1654. } // UlReferenceHttpResponse
  1655. /***************************************************************************++
  1656. Routine Description:
  1657. Dereferences the specified response.
  1658. Arguments:
  1659. pResponse - Supplies the response to dereference.
  1660. Return Value:
  1661. None.
  1662. --***************************************************************************/
  1663. VOID
  1664. UlDereferenceHttpResponse(
  1665. IN PUL_INTERNAL_RESPONSE pResponse
  1666. REFERENCE_DEBUG_FORMAL_PARAMS
  1667. )
  1668. {
  1669. LONG RefCount;
  1670. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pResponse ) );
  1671. RefCount = InterlockedDecrement( &pResponse->ReferenceCount );
  1672. WRITE_REF_TRACE_LOG(
  1673. g_pHttpResponseTraceLog,
  1674. REF_ACTION_DEREFERENCE_HTTP_RESPONSE,
  1675. RefCount,
  1676. pResponse,
  1677. pFileName,
  1678. LineNumber
  1679. );
  1680. UlTrace(SEND_RESPONSE, (
  1681. "UlDereferenceHttpResponse: response %p refcount %ld\n",
  1682. pResponse,
  1683. RefCount
  1684. ));
  1685. if (RefCount == 0)
  1686. {
  1687. UlpDestroyCapturedResponse( pResponse );
  1688. }
  1689. } // UlDereferenceHttpResponse
  1690. /***************************************************************************++
  1691. Routine Description:
  1692. A helper function that allocates an MDL for a range of memory, and
  1693. locks it down. UlpSendCacheEntry uses these MDLs to make sure the
  1694. (normally paged) cache entries don't get paged out when TDI is
  1695. sending them.
  1696. Arguments:
  1697. VirtualAddress - Supplies the address of the memory.
  1698. Length - Supplies the length of the memory to allocate a MDL for.
  1699. Operation - Either IoWriteAcess or IoReadAccess.
  1700. Return Values:
  1701. Pointer to a MDL if success or NULL otherwise.
  1702. --***************************************************************************/
  1703. PMDL
  1704. UlAllocateLockedMdl(
  1705. IN PVOID VirtualAddress,
  1706. IN ULONG Length,
  1707. IN LOCK_OPERATION Operation
  1708. )
  1709. {
  1710. PMDL pMdl = NULL;
  1711. NTSTATUS Status = STATUS_SUCCESS;
  1712. //
  1713. // Sanity check.
  1714. //
  1715. PAGED_CODE();
  1716. __try
  1717. {
  1718. pMdl = UlAllocateMdl(
  1719. VirtualAddress, // VirtualAddress
  1720. Length, // Length
  1721. FALSE, // SecondaryBuffer
  1722. FALSE, // ChargeQuota
  1723. NULL // Irp
  1724. );
  1725. if (pMdl)
  1726. {
  1727. MmProbeAndLockPages(
  1728. pMdl, // MDL
  1729. KernelMode, // AccessMode
  1730. Operation // Operation
  1731. );
  1732. }
  1733. }
  1734. __except( UL_EXCEPTION_FILTER() )
  1735. {
  1736. Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() );
  1737. UlFreeMdl( pMdl );
  1738. pMdl = NULL;
  1739. }
  1740. return pMdl;
  1741. } // UlAllocateLockedMdl
  1742. /***************************************************************************++
  1743. Routine Description:
  1744. Unlocks and frees an MDL allocated with UlAllocateLockedMdl.
  1745. Arguments:
  1746. pMdl - Supplies the MDL to free.
  1747. Return Values:
  1748. None.
  1749. --***************************************************************************/
  1750. VOID
  1751. UlFreeLockedMdl(
  1752. IN PMDL pMdl
  1753. )
  1754. {
  1755. //
  1756. // Sanity check.
  1757. //
  1758. ASSERT( IS_MDL_LOCKED(pMdl) );
  1759. MmUnlockPages( pMdl );
  1760. UlFreeMdl( pMdl );
  1761. } // UlFreeLockedMdl
  1762. /***************************************************************************++
  1763. Routine Description:
  1764. A helper function that initializes an MDL for a range of memory, and
  1765. locks it down. UlpSendCacheEntry uses these MDLs to make sure the
  1766. (normally paged) cache entries don't get paged out when TDI is
  1767. sending them.
  1768. Arguments:
  1769. pMdl - Supplies the memory descriptor for the MDL to initialize.
  1770. VirtualAddress - Supplies the address of the memory.
  1771. Length - Supplies the length of the memory.
  1772. Operation - Either IoWriteAcess or IoReadAccess.
  1773. Return Values:
  1774. NTSTATUS - Completion status.
  1775. --***************************************************************************/
  1776. NTSTATUS
  1777. UlInitializeAndLockMdl(
  1778. IN PMDL pMdl,
  1779. IN PVOID VirtualAddress,
  1780. IN ULONG Length,
  1781. IN LOCK_OPERATION Operation
  1782. )
  1783. {
  1784. NTSTATUS Status;
  1785. //
  1786. // Sanity check.
  1787. //
  1788. PAGED_CODE();
  1789. Status = STATUS_SUCCESS;
  1790. __try
  1791. {
  1792. MmInitializeMdl(
  1793. pMdl,
  1794. VirtualAddress,
  1795. Length
  1796. );
  1797. MmProbeAndLockPages(
  1798. pMdl, // MDL
  1799. KernelMode, // AccessMode
  1800. Operation // Operation
  1801. );
  1802. }
  1803. __except( UL_EXCEPTION_FILTER() )
  1804. {
  1805. Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() );
  1806. }
  1807. return Status;
  1808. } // UlInitializeAndLockMdl
  1809. /***************************************************************************++
  1810. Routine Description:
  1811. Once we've parsed a request, we pass it in here to try and serve
  1812. from the response cache. This function will either send the response,
  1813. or do nothing at all.
  1814. Arguments:
  1815. pHttpConn - Supplies the connection with a request to be handled.
  1816. pSendCacheResult - Result of the cache sent attempt.
  1817. pResumeParsing - Returns to the parser if parsing needs to be resumed.
  1818. Return Value:
  1819. NTSTATUS - Completion status.
  1820. --***************************************************************************/
  1821. NTSTATUS
  1822. UlSendCachedResponse(
  1823. IN PUL_HTTP_CONNECTION pHttpConn,
  1824. OUT PUL_SEND_CACHE_RESULT pSendCacheResult,
  1825. OUT PBOOLEAN pResumeParsing
  1826. )
  1827. {
  1828. NTSTATUS Status;
  1829. PUL_URI_CACHE_ENTRY pUriCacheEntry = NULL;
  1830. ULONG Flags;
  1831. ULONG RetCacheControl;
  1832. LONGLONG BytesToSend;
  1833. ULONG SiteId;
  1834. URI_SEARCH_KEY SearchKey;
  1835. PUL_INTERNAL_REQUEST pRequest;
  1836. PUL_SITE_COUNTER_ENTRY pCtr;
  1837. ULONG Connections;
  1838. UL_RESUME_PARSING_TYPE ResumeParsingType;
  1839. //
  1840. // Sanity check.
  1841. //
  1842. PAGED_CODE();
  1843. ASSERT( UL_IS_VALID_HTTP_CONNECTION( pHttpConn ) );
  1844. ASSERT( UlDbgPushLockOwnedExclusive( &pHttpConn->PushLock ) );
  1845. ASSERT( pSendCacheResult );
  1846. ASSERT( pResumeParsing );
  1847. pRequest = pHttpConn->pRequest;
  1848. ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pRequest ) );
  1849. *pResumeParsing = FALSE;
  1850. //
  1851. // See if we need to lookup based on Host + IP.
  1852. //
  1853. if (UlGetHostPlusIpBoundUriCacheCount() > 0)
  1854. {
  1855. Status = UlGenerateRoutingToken( pRequest, FALSE );
  1856. if (!NT_SUCCESS(Status))
  1857. {
  1858. //
  1859. // Memory failure bail out. Always set the error code if we are
  1860. // going to fail the request.
  1861. //
  1862. UlSetErrorCode(
  1863. pRequest,
  1864. UlErrorInternalServer,
  1865. NULL
  1866. );
  1867. *pSendCacheResult = UlSendCacheFailed;
  1868. return Status;
  1869. }
  1870. UlpBuildExtendedSearchKey( pRequest, &SearchKey );
  1871. pUriCacheEntry = UlCheckoutUriCacheEntry( &SearchKey );
  1872. if (pUriCacheEntry)
  1873. {
  1874. ASSERT( pUriCacheEntry->ConfigInfo.SiteUrlType ==
  1875. HttpUrlSite_NamePlusIP );
  1876. UlTrace(URI_CACHE, (
  1877. "Http!UlSendCachedResponse (Host+Ip) "
  1878. "pUriCacheEntry: (%p) Uri: (%S)\n",
  1879. pUriCacheEntry,
  1880. pUriCacheEntry ? pUriCacheEntry->UriKey.pUri : L""
  1881. ));
  1882. }
  1883. }
  1884. //
  1885. // Do not serve from cache if there's a Host + IP bound site
  1886. // in the cgroup.
  1887. //
  1888. if (pUriCacheEntry == NULL)
  1889. {
  1890. Status = UlLookupHostPlusIPSite( pRequest );
  1891. if (NT_SUCCESS(Status))
  1892. {
  1893. //
  1894. // There is a Host + IP bound site for this request.
  1895. // This should not be served from cache.
  1896. // Bail out!
  1897. //
  1898. *pSendCacheResult = UlSendCacheMiss;
  1899. return Status;
  1900. }
  1901. else
  1902. if (STATUS_NO_MEMORY == Status)
  1903. {
  1904. UlSetErrorCode(
  1905. pRequest,
  1906. UlErrorInternalServer,
  1907. NULL
  1908. );
  1909. *pSendCacheResult = UlSendCacheFailed;
  1910. return Status;
  1911. }
  1912. }
  1913. //
  1914. // Do the normal lookup based on the cooked Url.
  1915. //
  1916. if (pUriCacheEntry == NULL)
  1917. {
  1918. SearchKey.Type = UriKeyTypeNormal;
  1919. SearchKey.Key.Hash = pRequest->CookedUrl.Hash;
  1920. SearchKey.Key.Length = pRequest->CookedUrl.Length;
  1921. SearchKey.Key.pUri = pRequest->CookedUrl.pUrl;
  1922. SearchKey.Key.pPath = NULL;
  1923. pUriCacheEntry = UlCheckoutUriCacheEntry( &SearchKey );
  1924. UlTrace(URI_CACHE, (
  1925. "Http!UlSendCachedResponse (CookedUrl) "
  1926. "pUriCacheEntry: (%p) Uri: (%S)\n",
  1927. pUriCacheEntry,
  1928. pUriCacheEntry ? pUriCacheEntry->UriKey.pUri : L""
  1929. ));
  1930. }
  1931. if (pUriCacheEntry == NULL)
  1932. {
  1933. //
  1934. // No match in the URI cache, bounce up to user-mode.
  1935. //
  1936. *pSendCacheResult = UlSendCacheMiss;
  1937. return STATUS_SUCCESS;
  1938. }
  1939. //
  1940. // Verify the cache entry.
  1941. //
  1942. ASSERT( IS_VALID_URI_CACHE_ENTRY( pUriCacheEntry ) );
  1943. ASSERT( IS_VALID_URL_CONFIG_GROUP_INFO( &pUriCacheEntry->ConfigInfo ) );
  1944. if (!pUriCacheEntry->HeaderLength)
  1945. {
  1946. //
  1947. // Treat a match to a headless fragment cache entry as no-match.
  1948. //
  1949. UlCheckinUriCacheEntry( pUriCacheEntry );
  1950. *pSendCacheResult = UlSendCacheMiss;
  1951. return STATUS_SUCCESS;
  1952. }
  1953. //
  1954. // Check "Accept:" header.
  1955. //
  1956. if (FALSE == pRequest->AcceptWildcard)
  1957. {
  1958. if (FALSE == UlIsAcceptHeaderOk(pRequest, pUriCacheEntry))
  1959. {
  1960. //
  1961. // Cache entry did not match requested accept header; bounce up
  1962. // to user-mode for response.
  1963. //
  1964. UlCheckinUriCacheEntry( pUriCacheEntry );
  1965. *pSendCacheResult = UlSendCacheMiss;
  1966. return STATUS_SUCCESS;
  1967. }
  1968. }
  1969. //
  1970. // Check "Accept-Encoding:" header
  1971. //
  1972. if (FALSE == UlIsContentEncodingOk(pRequest, pUriCacheEntry))
  1973. {
  1974. //
  1975. // Cache entry did not match requested Accept-Encoding
  1976. // header; bounce up to user-mode for response.
  1977. //
  1978. UlCheckinUriCacheEntry( pUriCacheEntry );
  1979. *pSendCacheResult = UlSendCacheMiss;
  1980. return STATUS_SUCCESS;
  1981. }
  1982. //
  1983. // Now from this point on, the response will either be from cache or
  1984. // we will fail/refuse the connection. Always return from end, so that
  1985. // tracing can work.
  1986. //
  1987. Status = STATUS_SUCCESS;
  1988. //
  1989. // Enforce the connection limit now.
  1990. //
  1991. if (FALSE == UlCheckSiteConnectionLimit(
  1992. pHttpConn,
  1993. &pUriCacheEntry->ConfigInfo
  1994. ))
  1995. {
  1996. //
  1997. // Check in the cache entry back. Connection is refused!
  1998. //
  1999. UlSetErrorCode(
  2000. pRequest,
  2001. UlErrorConnectionLimit,
  2002. pUriCacheEntry->ConfigInfo.pAppPool
  2003. );
  2004. UlCheckinUriCacheEntry( pUriCacheEntry );
  2005. *pSendCacheResult = UlSendCacheConnectionRefused;
  2006. Status = STATUS_INVALID_DEVICE_STATE;
  2007. goto end;
  2008. }
  2009. //
  2010. // Perf Counters (cached).
  2011. //
  2012. pCtr = pUriCacheEntry->ConfigInfo.pSiteCounters;
  2013. if (pCtr)
  2014. {
  2015. //
  2016. // NOTE: pCtr may be NULL if the SiteId was never set on the root-level
  2017. // NOTE: Config Group for the site. BVTs may need to be updated.
  2018. //
  2019. ASSERT( IS_VALID_SITE_COUNTER_ENTRY( pCtr ) );
  2020. if (pUriCacheEntry->Verb == HttpVerbGET)
  2021. {
  2022. UlIncSiteNonCriticalCounterUlong( pCtr, HttpSiteCounterGetReqs );
  2023. }
  2024. else
  2025. if (pUriCacheEntry->Verb == HttpVerbHEAD)
  2026. {
  2027. UlIncSiteNonCriticalCounterUlong( pCtr, HttpSiteCounterHeadReqs );
  2028. }
  2029. UlIncSiteNonCriticalCounterUlong( pCtr, HttpSiteCounterAllReqs );
  2030. if (pCtr != pHttpConn->pPrevSiteCounters)
  2031. {
  2032. if (pHttpConn->pPrevSiteCounters)
  2033. {
  2034. UlDecSiteCounter(
  2035. pHttpConn->pPrevSiteCounters,
  2036. HttpSiteCounterCurrentConns
  2037. );
  2038. DEREFERENCE_SITE_COUNTER_ENTRY( pHttpConn->pPrevSiteCounters );
  2039. }
  2040. UlIncSiteNonCriticalCounterUlong(
  2041. pCtr,
  2042. HttpSiteCounterConnAttempts
  2043. );
  2044. Connections =
  2045. (ULONG) UlIncSiteCounter( pCtr, HttpSiteCounterCurrentConns );
  2046. UlMaxSiteCounter(
  2047. pCtr,
  2048. HttpSiteCounterMaxConnections,
  2049. Connections
  2050. );
  2051. //
  2052. // Add ref for new site counters.
  2053. //
  2054. REFERENCE_SITE_COUNTER_ENTRY( pCtr );
  2055. pHttpConn->pPrevSiteCounters = pCtr;
  2056. }
  2057. }
  2058. //
  2059. // Install a filter if BWT is enabled for this request's site
  2060. // or for the control channel that owns the site. If fails,
  2061. // refuse the connection back (503).
  2062. //
  2063. Status = UlTcAddFilterForConnection(
  2064. pHttpConn,
  2065. &pUriCacheEntry->ConfigInfo
  2066. );
  2067. if (!NT_SUCCESS(Status))
  2068. {
  2069. UlSetErrorCode(
  2070. pRequest,
  2071. UlErrorUnavailable,
  2072. pUriCacheEntry->ConfigInfo.pAppPool
  2073. );
  2074. UlCheckinUriCacheEntry( pUriCacheEntry );
  2075. *pSendCacheResult = UlSendCacheFailed;
  2076. goto end;
  2077. }
  2078. //
  2079. // Now we are about to do a cache send, we need to enforce the limit for
  2080. // pipelined requests on the connection. If we return FALSE for resume
  2081. // parsing, the next request on the connection will be parsed after
  2082. // the send completion. Otherwise the HTTP receive logic will kick the
  2083. // parser back into action.
  2084. //
  2085. if (pHttpConn->PipelinedRequests < g_UlMaxPipelinedRequests)
  2086. {
  2087. ResumeParsingType = UlResumeParsingOnLastSend;
  2088. }
  2089. else
  2090. {
  2091. ResumeParsingType = UlResumeParsingOnSendCompletion;
  2092. }
  2093. //
  2094. // Set BytesToSend and SiteId since we are reasonably sure this is a
  2095. // cache-hit and so that the 304 code path will get these values too.
  2096. //
  2097. BytesToSend = pUriCacheEntry->ContentLength + pUriCacheEntry->HeaderLength;
  2098. SiteId = pUriCacheEntry->ConfigInfo.SiteId;
  2099. //
  2100. // Cache-Control: Check the If-* headers to see if we can/should skip
  2101. // sending of the cached response. This does a passive syntax check on
  2102. // the Etags in the request's If-* headers. This call will issue a send
  2103. // if the return code is 304.
  2104. //
  2105. RetCacheControl =
  2106. UlCheckCacheControlHeaders(
  2107. pRequest,
  2108. pUriCacheEntry,
  2109. (BOOLEAN) (UlResumeParsingOnSendCompletion == ResumeParsingType)
  2110. );
  2111. if (RetCacheControl)
  2112. {
  2113. //
  2114. // Check-in cache entry, since completion won't run.
  2115. //
  2116. UlCheckinUriCacheEntry( pUriCacheEntry );
  2117. switch (RetCacheControl)
  2118. {
  2119. case 304:
  2120. //
  2121. // Resume parsing only if we have sent a 304. In other cases,
  2122. // the request is deliverd to the user or the connection is reset.
  2123. //
  2124. *pResumeParsing =
  2125. (BOOLEAN) (UlResumeParsingOnLastSend == ResumeParsingType);
  2126. //
  2127. // Mark as "served from cache".
  2128. //
  2129. *pSendCacheResult = UlSendCacheServedFromCache;
  2130. break;
  2131. case 412:
  2132. //
  2133. // Indicate that the parser should send error 412 (Precondition
  2134. // Failed). Just the send the error response but do not close
  2135. // the connection.
  2136. //
  2137. UlSetErrorCode( pRequest, UlErrorPreconditionFailed, NULL );
  2138. *pSendCacheResult = UlSendCachePreconditionFailed;
  2139. Status = STATUS_INVALID_DEVICE_STATE;
  2140. break;
  2141. case 400:
  2142. default:
  2143. //
  2144. // Indicate that the parser should send error 400 (Bad Request).
  2145. //
  2146. UlSetErrorCode( pRequest, UlError, NULL );
  2147. *pSendCacheResult = UlSendCacheFailed;
  2148. Status = STATUS_INVALID_DEVICE_STATE;
  2149. break;
  2150. }
  2151. //
  2152. // Return success.
  2153. //
  2154. goto end;
  2155. }
  2156. //
  2157. // Figure out correct flags.
  2158. //
  2159. if (UlCheckDisconnectInfo(pHttpConn->pRequest))
  2160. {
  2161. Flags = HTTP_SEND_RESPONSE_FLAG_DISCONNECT;
  2162. }
  2163. else
  2164. {
  2165. Flags = 0;
  2166. }
  2167. //
  2168. // Start the MinBytesPerSecond timer, since the data length
  2169. // is in the UL_URI_CACHE_ENTRY.
  2170. //
  2171. UlSetMinBytesPerSecondTimer(
  2172. &pHttpConn->TimeoutInfo,
  2173. BytesToSend
  2174. );
  2175. Status = UlpSendCacheEntry(
  2176. pHttpConn, // pHttpConnection
  2177. Flags, // Flags
  2178. pUriCacheEntry, // pUriCacheEntry
  2179. NULL, // pCompletionRoutine
  2180. NULL, // pCompletionContext
  2181. NULL, // pLogData
  2182. ResumeParsingType // ResumeParsingType
  2183. );
  2184. //
  2185. // Check in cache entry on failure since our completion
  2186. // routine won't run.
  2187. //
  2188. if (!NT_SUCCESS(Status) )
  2189. {
  2190. //
  2191. // Return failure so that the request doesn't bounce back to
  2192. // user mode.
  2193. //
  2194. UlSetErrorCode(
  2195. pRequest,
  2196. UlErrorUnavailable,
  2197. pUriCacheEntry->ConfigInfo.pAppPool
  2198. );
  2199. UlCheckinUriCacheEntry( pUriCacheEntry );
  2200. *pSendCacheResult = UlSendCacheFailed;
  2201. }
  2202. else
  2203. {
  2204. //
  2205. // Success!
  2206. //
  2207. *pSendCacheResult = UlSendCacheServedFromCache;
  2208. }
  2209. end:
  2210. //
  2211. // If the request is served from cache, fire the ETW end event here.
  2212. //
  2213. if (ETW_LOG_MIN() && (*pSendCacheResult == UlSendCacheServedFromCache))
  2214. {
  2215. UlEtwTraceEvent(
  2216. &UlTransGuid,
  2217. ETW_TYPE_CACHED_END,
  2218. (PVOID) &pRequest,
  2219. sizeof(PVOID),
  2220. &SiteId,
  2221. sizeof(ULONG),
  2222. &BytesToSend,
  2223. sizeof(ULONG),
  2224. NULL,
  2225. 0
  2226. );
  2227. }
  2228. UlTrace(URI_CACHE, (
  2229. "Http!UlSendCachedResponse(httpconn = %p) ServedFromCache = (%s),"
  2230. "Status = %x\n",
  2231. pHttpConn,
  2232. TRANSLATE_SEND_CACHE_RESULT(*pSendCacheResult),
  2233. Status
  2234. ));
  2235. return Status;
  2236. } // UlSendCachedResponse
  2237. /***************************************************************************++
  2238. Routine Description:
  2239. If the response is cacheable, then this routine starts building a
  2240. cache entry for it. When the entry is complete it will be sent to
  2241. the client and may be added to the hash table.
  2242. Arguments:
  2243. pRequest - Supplies the initiating request.
  2244. pResponse - Supplies the generated response.
  2245. pProcess - Supplies the WP that is sending the response.
  2246. Flags - UlSendHttpResponse flags.
  2247. Policy - Supplies the cache policy for this response.
  2248. pCompletionRoutine - Supplies the completion routine to be called
  2249. after entry is sent.
  2250. pCompletionContext - Supplies the context passed to pCompletionRoutine.
  2251. pServedFromCache - Always set. TRUE if we'll handle sending response.
  2252. FALSE indicates that the caller should send it.
  2253. Return Value:
  2254. NTSTATUS - Completion status.
  2255. --***************************************************************************/
  2256. NTSTATUS
  2257. UlCacheAndSendResponse(
  2258. IN PUL_INTERNAL_REQUEST pRequest,
  2259. IN PUL_INTERNAL_RESPONSE pResponse,
  2260. IN PUL_APP_POOL_PROCESS pProcess,
  2261. IN HTTP_CACHE_POLICY Policy,
  2262. IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
  2263. IN PVOID pCompletionContext,
  2264. OUT PBOOLEAN pServedFromCache
  2265. )
  2266. {
  2267. NTSTATUS Status = STATUS_SUCCESS;
  2268. ULONG Flags = pResponse->Flags;
  2269. USHORT StatusCode = pResponse->StatusCode;
  2270. //
  2271. // Sanity check.
  2272. //
  2273. PAGED_CODE();
  2274. ASSERT( pServedFromCache );
  2275. //
  2276. // Should we close the connection?
  2277. //
  2278. if (UlCheckDisconnectInfo(pRequest))
  2279. {
  2280. Flags |= HTTP_SEND_RESPONSE_FLAG_DISCONNECT;
  2281. pResponse->Flags = Flags;
  2282. }
  2283. //
  2284. // Do the real work.
  2285. //
  2286. if (UlCheckCacheResponseConditions(pRequest, pResponse, Flags, Policy))
  2287. {
  2288. Status = UlpBuildCacheEntry(
  2289. pRequest,
  2290. pResponse,
  2291. pProcess,
  2292. Policy,
  2293. pCompletionRoutine,
  2294. pCompletionContext
  2295. );
  2296. if (NT_SUCCESS(Status))
  2297. {
  2298. *pServedFromCache = TRUE;
  2299. }
  2300. else
  2301. {
  2302. *pServedFromCache = FALSE;
  2303. Status = STATUS_SUCCESS;
  2304. }
  2305. }
  2306. else
  2307. {
  2308. *pServedFromCache = FALSE;
  2309. }
  2310. UlTrace(URI_CACHE, (
  2311. "Http!UlCacheAndSendResponse ServedFromCache = %d\n",
  2312. *pServedFromCache
  2313. ));
  2314. //
  2315. // We will record this as cache miss since the original request
  2316. // was a miss.
  2317. //
  2318. if (ETW_LOG_MIN() && *pServedFromCache)
  2319. {
  2320. UlEtwTraceEvent(
  2321. &UlTransGuid,
  2322. ETW_TYPE_CACHE_AND_SEND,
  2323. (PVOID) &pRequest->RequestIdCopy,
  2324. sizeof(HTTP_REQUEST_ID),
  2325. (PVOID) &StatusCode,
  2326. sizeof(USHORT),
  2327. NULL,
  2328. 0
  2329. );
  2330. }
  2331. return Status;
  2332. } // UlCacheAndSendResponse
  2333. /***************************************************************************++
  2334. Routine Description:
  2335. Completes a "send response" represented by a send tracker.
  2336. UlCompleteSendResponse takes the ownership of the tracker reference.
  2337. Arguments:
  2338. pTracker - Supplies the tracker to complete.
  2339. Status - Supplies the completion status.
  2340. Return Value:
  2341. None.
  2342. --***************************************************************************/
  2343. VOID
  2344. UlCompleteSendResponse(
  2345. IN PUL_CHUNK_TRACKER pTracker,
  2346. IN NTSTATUS Status
  2347. )
  2348. {
  2349. //
  2350. // Although the chunk tracker will be around until all the outstanding
  2351. // Read/Send IRPs are complete, we should only complete the send once.
  2352. //
  2353. if (FALSE != InterlockedExchange(&pTracker->Terminated, TRUE))
  2354. {
  2355. return;
  2356. }
  2357. UlTrace(SEND_RESPONSE,(
  2358. "UlCompleteSendResponse: tracker %p, status %08lx\n",
  2359. pTracker,
  2360. Status
  2361. ));
  2362. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  2363. pTracker->IoStatus.Status = Status;
  2364. UL_QUEUE_WORK_ITEM(
  2365. &pTracker->WorkItem,
  2366. UlpCompleteSendResponseWorker
  2367. );
  2368. } // UlCompleteSendResponse
  2369. /***************************************************************************++
  2370. Routine Description:
  2371. Increment pRequest->SendsPending in a lock and decide if we need to
  2372. transfer the logging and resume parsing information to pRequest.
  2373. Arguments:
  2374. pRequest - Supplies the pointer to a UL_INTERNAL_REQUEST structure
  2375. that SendsPending needs incremented.
  2376. ppLogData - Supplies the pointer to a PUL_LOG_DATA_BUFFER structure
  2377. that we need to transfer to pRequest.
  2378. pResumeParsingType - Supplies the pointer to UL_RESUME_PARSING_TYPE
  2379. that we need to transfer to pRequest.
  2380. Return Value:
  2381. None.
  2382. --***************************************************************************/
  2383. VOID
  2384. UlSetRequestSendsPending(
  2385. IN PUL_INTERNAL_REQUEST pRequest,
  2386. IN OUT PUL_LOG_DATA_BUFFER * ppLogData,
  2387. IN OUT PUL_RESUME_PARSING_TYPE pResumeParsingType
  2388. )
  2389. {
  2390. KIRQL OldIrql;
  2391. //
  2392. // Sanity check.
  2393. //
  2394. ASSERT( PASSIVE_LEVEL == KeGetCurrentIrql() );
  2395. ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pRequest ) );
  2396. ASSERT( ppLogData );
  2397. ASSERT( NULL == *ppLogData || IS_VALID_LOG_DATA_BUFFER( *ppLogData ) );
  2398. ASSERT( pResumeParsingType );
  2399. UlAcquireSpinLock( &pRequest->SpinLock, &OldIrql );
  2400. pRequest->SendsPending++;
  2401. if (*ppLogData)
  2402. {
  2403. ASSERT( NULL == pRequest->pLogData );
  2404. pRequest->pLogData = *ppLogData;
  2405. *ppLogData = NULL;
  2406. }
  2407. if (UlResumeParsingOnSendCompletion == *pResumeParsingType)
  2408. {
  2409. ASSERT( FALSE == pRequest->ResumeParsing );
  2410. pRequest->ResumeParsing = TRUE;
  2411. *pResumeParsingType = UlResumeParsingNone;
  2412. }
  2413. UlReleaseSpinLock( &pRequest->SpinLock, OldIrql );
  2414. } // UlSetRequestSendsPending
  2415. /***************************************************************************++
  2416. Routine Description:
  2417. Decrement pRequest->SendsPending in a lock and decide if we need to
  2418. log and resume parsing. The caller then should either log and/or
  2419. resume parsing depending on the values returned. It is assumed here
  2420. the values for both *ppLogData and *pResumeParsing are initialized
  2421. when entering this function.
  2422. Arguments:
  2423. pRequest - Supplies the pointer to a UL_INTERNAL_REQUEST structure
  2424. that SendsPending needs decremented.
  2425. ppLogData - Supplies the pointer to a PUL_LOG_DATA_BUFFER structure
  2426. to receive the logging information.
  2427. pResumeParsing - Supplies the pointer to a BOOLEAN to receive the
  2428. resume parsing information.
  2429. Return Value:
  2430. None.
  2431. --***************************************************************************/
  2432. VOID
  2433. UlUnsetRequestSendsPending(
  2434. IN PUL_INTERNAL_REQUEST pRequest,
  2435. OUT PUL_LOG_DATA_BUFFER * ppLogData,
  2436. OUT PBOOLEAN pResumeParsing
  2437. )
  2438. {
  2439. KIRQL OldIrql;
  2440. //
  2441. // Sanity check.
  2442. //
  2443. ASSERT( PASSIVE_LEVEL == KeGetCurrentIrql() );
  2444. ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pRequest ) );
  2445. ASSERT( ppLogData );
  2446. ASSERT( NULL == *ppLogData );
  2447. ASSERT( pResumeParsing );
  2448. ASSERT( FALSE == *pResumeParsing );
  2449. UlAcquireSpinLock( &pRequest->SpinLock, &OldIrql );
  2450. pRequest->SendsPending--;
  2451. if (0 == pRequest->SendsPending)
  2452. {
  2453. if (pRequest->pLogData)
  2454. {
  2455. *ppLogData = pRequest->pLogData;
  2456. pRequest->pLogData = NULL;
  2457. }
  2458. if (pRequest->ResumeParsing)
  2459. {
  2460. *pResumeParsing = pRequest->ResumeParsing;
  2461. pRequest->ResumeParsing = FALSE;
  2462. }
  2463. }
  2464. UlReleaseSpinLock( &pRequest->SpinLock, OldIrql );
  2465. } // UlUnsetRequestSendsPending
  2466. //
  2467. // Private functions.
  2468. //
  2469. /***************************************************************************++
  2470. Routine Description:
  2471. Destroys an internal HTTP response captured by UlCaptureHttpResponse().
  2472. This involves closing open files, unlocking memory, and releasing any
  2473. resources allocated to the response.
  2474. Arguments:
  2475. pResponse - Supplies the internal response to destroy.
  2476. Return Values:
  2477. None.
  2478. --***************************************************************************/
  2479. VOID
  2480. UlpDestroyCapturedResponse(
  2481. IN PUL_INTERNAL_RESPONSE pResponse
  2482. )
  2483. {
  2484. PUL_INTERNAL_DATA_CHUNK pDataChunk;
  2485. PIRP pIrp;
  2486. PIO_STACK_LOCATION pIrpSp;
  2487. ULONG i;
  2488. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pResponse ) );
  2489. ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pResponse->pRequest ) );
  2490. UlTrace(SEND_RESPONSE,(
  2491. "UlpDestroyCapturedResponse: response %p\n",
  2492. pResponse
  2493. ));
  2494. UlDeletePushLock( &pResponse->PushLock );
  2495. //
  2496. // Scan the chunks.
  2497. //
  2498. for (i = 0; i < pResponse->ChunkCount; ++i)
  2499. {
  2500. pDataChunk = &pResponse->pDataChunks[i];
  2501. if (IS_FROM_MEMORY(pDataChunk))
  2502. {
  2503. //
  2504. // It's from memory. If necessary, unlock the pages, then
  2505. // free the MDL.
  2506. //
  2507. if (pDataChunk->FromMemory.pMdl != NULL)
  2508. {
  2509. if (IS_MDL_LOCKED(pDataChunk->FromMemory.pMdl))
  2510. {
  2511. MmUnlockPages( pDataChunk->FromMemory.pMdl );
  2512. }
  2513. UlFreeMdl( pDataChunk->FromMemory.pMdl );
  2514. pDataChunk->FromMemory.pMdl = NULL;
  2515. }
  2516. }
  2517. else
  2518. if (IS_FROM_FRAGMENT_CACHE(pDataChunk))
  2519. {
  2520. //
  2521. // It's a fragment chunk. If there is a cache entry checked
  2522. // out, check it back in.
  2523. //
  2524. if (pDataChunk->FromFragmentCache.pCacheEntry != NULL)
  2525. {
  2526. UlCheckinUriCacheEntry(
  2527. pDataChunk->FromFragmentCache.pCacheEntry
  2528. );
  2529. }
  2530. }
  2531. else
  2532. {
  2533. //
  2534. // It's a file chunk. If there is an associated file cache
  2535. // entry, then dereference it.
  2536. //
  2537. ASSERT( IS_FROM_FILE_HANDLE( pDataChunk ) );
  2538. if (pDataChunk->FromFileHandle.FileCacheEntry.pFileObject != NULL)
  2539. {
  2540. UlDestroyFileCacheEntry(
  2541. &pDataChunk->FromFileHandle.FileCacheEntry
  2542. );
  2543. pDataChunk->FromFileHandle.FileCacheEntry.pFileObject = NULL;
  2544. }
  2545. }
  2546. }
  2547. //
  2548. // We should clean up the log buffer here if nobody has cleaned it up yet.
  2549. // Unless there's an error during capture, the log buffer will be cleaned
  2550. // up when send tracker's (cache/chunk) are completed in their respective
  2551. // routines.
  2552. //
  2553. if (pResponse->pLogData)
  2554. {
  2555. UlDestroyLogDataBuffer( pResponse->pLogData );
  2556. }
  2557. //
  2558. // Complete the IRP if we have one.
  2559. //
  2560. pIrp = pResponse->pIrp;
  2561. if (pIrp)
  2562. {
  2563. //
  2564. // Uncheck either ConnectionSendBytes or GlobalSendBytes.
  2565. //
  2566. UlUncheckSendLimit(
  2567. pResponse->pRequest->pHttpConn,
  2568. pResponse->ConnectionSendBytes,
  2569. pResponse->GlobalSendBytes
  2570. );
  2571. //
  2572. // Unset the Type3InputBuffer since we are completing the IRP.
  2573. //
  2574. pIrpSp = IoGetCurrentIrpStackLocation( pIrp );
  2575. pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
  2576. pIrp->IoStatus = pResponse->IoStatus;
  2577. UlCompleteRequest( pIrp, IO_NETWORK_INCREMENT );
  2578. }
  2579. UL_DEREFERENCE_INTERNAL_REQUEST( pResponse->pRequest );
  2580. if (pResponse->FromLookaside)
  2581. {
  2582. UlPplFreeResponseBuffer( pResponse );
  2583. }
  2584. else
  2585. {
  2586. UL_FREE_POOL_WITH_SIG( pResponse, UL_INTERNAL_RESPONSE_POOL_TAG );
  2587. }
  2588. } // UlpDestroyCapturedResponse
  2589. /***************************************************************************++
  2590. Routine Description:
  2591. Process the UL_INTERNAL_RESPONSE we have created. If no other sends
  2592. are being processed, start processing the current one by scheduling
  2593. a worker item; otherwise, queue the current response in the pending
  2594. response queue of the request. In the case where the request has been
  2595. cleaned up (UlCancelRequestIo has been called), we immediately complete
  2596. the response with STATUS_CANCELLED.
  2597. Arguments:
  2598. pTracker - Supplies the tracker to send the response.
  2599. FromKernelMode - If this comes from UlSendErrorResponse or not.
  2600. Return Values:
  2601. None.
  2602. --***************************************************************************/
  2603. VOID
  2604. UlpEnqueueSendHttpResponse(
  2605. IN PUL_CHUNK_TRACKER pTracker,
  2606. IN BOOLEAN FromKernelMode
  2607. )
  2608. {
  2609. PUL_INTERNAL_RESPONSE pResponse;
  2610. PUL_INTERNAL_REQUEST pRequest;
  2611. BOOLEAN ProcessCurrentResponse = FALSE;
  2612. //
  2613. // Sanity check.
  2614. //
  2615. PAGED_CODE();
  2616. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  2617. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pTracker->pResponse ) );
  2618. ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pTracker->pResponse->pRequest ) );
  2619. pResponse = pTracker->pResponse;
  2620. pRequest = pResponse->pRequest;
  2621. if (!FromKernelMode)
  2622. {
  2623. UlAcquirePushLockExclusive( &pRequest->pHttpConn->PushLock );
  2624. }
  2625. if (!pRequest->InCleanup)
  2626. {
  2627. if (pRequest->SendInProgress)
  2628. {
  2629. InsertTailList(
  2630. &pRequest->ResponseHead,
  2631. &pTracker->ListEntry
  2632. );
  2633. }
  2634. else
  2635. {
  2636. ASSERT( IsListEmpty( &pRequest->ResponseHead ) );
  2637. //
  2638. // Start the send process (and set the SendInProgress flag) if
  2639. // there are no other sends in progress. The SendInProgress flag
  2640. // is removed from the list when the last piece of the data is
  2641. // pended in TDI. If an error occurs, the connection gets reset
  2642. // so UlCancelRequestIo will eventually cancel all pending sends.
  2643. //
  2644. pRequest->SendInProgress = 1;
  2645. ProcessCurrentResponse = TRUE;
  2646. }
  2647. }
  2648. else
  2649. {
  2650. UlCompleteSendResponse( pTracker, STATUS_CONNECTION_RESET );
  2651. }
  2652. if (!FromKernelMode)
  2653. {
  2654. UlReleasePushLockExclusive( &pRequest->pHttpConn->PushLock );
  2655. if (ProcessCurrentResponse)
  2656. {
  2657. //
  2658. // Call UlpSendHttpResponseWorker directly if this comes from the
  2659. // IOCTL. This is to reduce potential contentions on the
  2660. // HttpConnection push lock when sends are overlapped since an
  2661. // application semantically can't really send again until the
  2662. // previous call returns (not necessarily completes). Calling
  2663. // UlpSendHttpResponseWorker directly will cleanup the
  2664. // SendInProgress flag most of the time when the send returns
  2665. // unless the send requires disk I/O.
  2666. //
  2667. UlpSendHttpResponseWorker( &pTracker->WorkItem );
  2668. }
  2669. }
  2670. else
  2671. {
  2672. if (ProcessCurrentResponse)
  2673. {
  2674. //
  2675. // But if this is called from kernel mode, we need to queue a
  2676. // work item to be safe because TDI can complete a send inline
  2677. // resulting UlResumeParsing getting called with the
  2678. // HttpConnection push lock held.
  2679. //
  2680. UlpQueueResponseWorkItem(
  2681. &pTracker->WorkItem,
  2682. UlpSendHttpResponseWorker,
  2683. pTracker->pResponse->SyncRead
  2684. );
  2685. }
  2686. }
  2687. } // UlpEnqueueSendHttpResponse
  2688. /***************************************************************************++
  2689. Routine Description:
  2690. Unset SendInProgress flag and the try to remove the next response from
  2691. the request's response list. If there are more responses pending, start
  2692. processing them as well.
  2693. Arguments:
  2694. pRequest - Supplies the request that has a list of responses queued.
  2695. Return Values:
  2696. None.
  2697. --***************************************************************************/
  2698. VOID
  2699. UlpDequeueSendHttpResponse(
  2700. IN PUL_INTERNAL_REQUEST pRequest
  2701. )
  2702. {
  2703. PLIST_ENTRY pEntry;
  2704. IN PUL_CHUNK_TRACKER pTracker;
  2705. //
  2706. // Sanity check.
  2707. //
  2708. PAGED_CODE();
  2709. ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pRequest ) );
  2710. ASSERT( pRequest->SendInProgress );
  2711. UlAcquirePushLockExclusive( &pRequest->pHttpConn->PushLock );
  2712. if (!pRequest->InCleanup && !IsListEmpty(&pRequest->ResponseHead))
  2713. {
  2714. pEntry = RemoveHeadList( &pRequest->ResponseHead );
  2715. pTracker = CONTAINING_RECORD(
  2716. pEntry,
  2717. UL_CHUNK_TRACKER,
  2718. ListEntry
  2719. );
  2720. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  2721. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pTracker->pResponse ) );
  2722. //
  2723. // Start the send process for the next response in the request's
  2724. // response list.
  2725. //
  2726. UlpQueueResponseWorkItem(
  2727. &pTracker->WorkItem,
  2728. UlpSendHttpResponseWorker,
  2729. pTracker->pResponse->SyncRead
  2730. );
  2731. }
  2732. else
  2733. {
  2734. ASSERT( IsListEmpty( &pRequest->ResponseHead ) );
  2735. //
  2736. // No more pending send IRPs. This means we can take the fast send
  2737. // path if asked so.
  2738. //
  2739. pRequest->SendInProgress = 0;
  2740. }
  2741. UlReleasePushLockExclusive( &pRequest->pHttpConn->PushLock );
  2742. } // UlpDequeueSendHttpResponse
  2743. /***************************************************************************++
  2744. Routine Description:
  2745. Worker routine for managing an in-progress UlSendHttpResponse().
  2746. Arguments:
  2747. pWorkItem - Supplies a pointer to the work item queued. This should
  2748. point to the WORK_ITEM structure embedded in a UL_CHUNK_TRACKER.
  2749. Return Values:
  2750. None.
  2751. --***************************************************************************/
  2752. VOID
  2753. UlpSendHttpResponseWorker(
  2754. IN PUL_WORK_ITEM pWorkItem
  2755. )
  2756. {
  2757. PUL_CHUNK_TRACKER pTracker;
  2758. NTSTATUS Status;
  2759. PUL_INTERNAL_DATA_CHUNK pCurrentChunk;
  2760. PUL_FILE_CACHE_ENTRY pFileCacheEntry;
  2761. PUL_FILE_BUFFER pFileBuffer;
  2762. PMDL pNewMdl;
  2763. ULONG RunCount;
  2764. ULONG BytesToRead;
  2765. BOOLEAN ResumeParsing = FALSE;
  2766. PUL_URI_CACHE_ENTRY pFragmentCacheEntry;
  2767. PUL_INTERNAL_RESPONSE pResponse;
  2768. PUL_MDL_RUN pMdlRuns;
  2769. //
  2770. // Sanity check.
  2771. //
  2772. PAGED_CODE();
  2773. pTracker = CONTAINING_RECORD(
  2774. pWorkItem,
  2775. UL_CHUNK_TRACKER,
  2776. WorkItem
  2777. );
  2778. UlTrace(SEND_RESPONSE, (
  2779. "UlpSendHttpResponseWorker: tracker %p\n",
  2780. pTracker
  2781. ));
  2782. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  2783. pResponse = pTracker->pResponse;
  2784. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pResponse ) );
  2785. Status = STATUS_SUCCESS;
  2786. pMdlRuns = &pTracker->SendInfo.MdlRuns[0];
  2787. while (TRUE)
  2788. {
  2789. //
  2790. // Capture the current chunk pointer, then check for end of
  2791. // response.
  2792. //
  2793. pCurrentChunk = &pResponse->pDataChunks[pResponse->CurrentChunk];
  2794. if (IS_SEND_COMPLETE(pResponse))
  2795. {
  2796. ASSERT( Status == STATUS_SUCCESS );
  2797. break;
  2798. }
  2799. RunCount = pTracker->SendInfo.MdlRunCount;
  2800. //
  2801. // Determine the chunk type.
  2802. //
  2803. if (IS_FROM_MEMORY(pCurrentChunk) ||
  2804. IS_FROM_FRAGMENT_CACHE(pCurrentChunk))
  2805. {
  2806. //
  2807. // It's from a locked-down memory buffer or fragment cache.
  2808. // Since these are always handled in-line (never pended) we can
  2809. // go ahead and adjust the current chunk pointer in the
  2810. // tracker.
  2811. //
  2812. UlpIncrementChunkPointer( pResponse );
  2813. if (IS_FROM_MEMORY(pCurrentChunk))
  2814. {
  2815. //
  2816. // Ignore empty buffers.
  2817. //
  2818. if (pCurrentChunk->FromMemory.BufferLength == 0)
  2819. {
  2820. continue;
  2821. }
  2822. //
  2823. // Clone the incoming MDL.
  2824. //
  2825. ASSERT( pCurrentChunk->FromMemory.pMdl->Next == NULL );
  2826. pNewMdl = UlCloneMdl(
  2827. pCurrentChunk->FromMemory.pMdl,
  2828. MmGetMdlByteCount(pCurrentChunk->FromMemory.pMdl)
  2829. );
  2830. }
  2831. else
  2832. {
  2833. //
  2834. // Build a partial MDL for the cached data.
  2835. //
  2836. pFragmentCacheEntry =
  2837. pCurrentChunk->FromFragmentCache.pCacheEntry;
  2838. //
  2839. // Ignore cached HEAD responses.
  2840. //
  2841. if (pFragmentCacheEntry->ContentLength == 0)
  2842. {
  2843. continue;
  2844. }
  2845. pNewMdl = UlCloneMdl(
  2846. pFragmentCacheEntry->pMdl,
  2847. pFragmentCacheEntry->ContentLength
  2848. );
  2849. }
  2850. if (pNewMdl == NULL)
  2851. {
  2852. Status = STATUS_INSUFFICIENT_RESOURCES;
  2853. break;
  2854. }
  2855. //
  2856. // Update the buffered byte count and append the cloned MDL
  2857. // onto our MDL chain.
  2858. //
  2859. pTracker->SendInfo.BytesBuffered += MmGetMdlByteCount( pNewMdl );
  2860. (*pTracker->SendInfo.pMdlLink) = pNewMdl;
  2861. pTracker->SendInfo.pMdlLink = &pNewMdl->Next;
  2862. //
  2863. // Add the MDL to the run list. As an optimization, if the
  2864. // last run in the list was "from memory", we can just
  2865. // append the MDL to the last run. A "from fragment cache"
  2866. // chunk is similar to "from memory".
  2867. //
  2868. if (RunCount == 0 ||
  2869. IS_FILE_BUFFER_IN_USE(&pMdlRuns[RunCount - 1].FileBuffer))
  2870. {
  2871. //
  2872. // Create a new run.
  2873. //
  2874. pMdlRuns[RunCount].pMdlTail = pNewMdl;
  2875. pTracker->SendInfo.MdlRunCount++;
  2876. pFileBuffer = &pMdlRuns[RunCount].FileBuffer;
  2877. RtlZeroMemory( pFileBuffer, sizeof(*pFileBuffer) );
  2878. //
  2879. // If we have exhausted our static MDL run array,
  2880. // then we'll need to initiate a flush.
  2881. //
  2882. if (UL_MAX_MDL_RUNS == pTracker->SendInfo.MdlRunCount)
  2883. {
  2884. ASSERT( Status == STATUS_SUCCESS );
  2885. break;
  2886. }
  2887. }
  2888. else
  2889. {
  2890. //
  2891. // Append to the last run in the list.
  2892. //
  2893. pMdlRuns[RunCount - 1].pMdlTail->Next = pNewMdl;
  2894. pMdlRuns[RunCount - 1].pMdlTail = pNewMdl;
  2895. }
  2896. }
  2897. else
  2898. {
  2899. //
  2900. // It's a filesystem MDL.
  2901. //
  2902. ASSERT( IS_FROM_FILE_HANDLE( pCurrentChunk ) );
  2903. //
  2904. // Ignore 0 bytes read.
  2905. //
  2906. if (pResponse->FileBytesRemaining.QuadPart == 0)
  2907. {
  2908. UlpIncrementChunkPointer( pResponse );
  2909. continue;
  2910. }
  2911. pFileCacheEntry = &pCurrentChunk->FromFileHandle.FileCacheEntry;
  2912. ASSERT( IS_VALID_FILE_CACHE_ENTRY( pFileCacheEntry ) );
  2913. pFileBuffer = &pMdlRuns[RunCount].FileBuffer;
  2914. ASSERT( pFileBuffer->pMdl == NULL );
  2915. ASSERT( pFileBuffer->pFileData == NULL );
  2916. RtlZeroMemory( pFileBuffer, sizeof(*pFileBuffer) );
  2917. //
  2918. // Initiate file read.
  2919. //
  2920. BytesToRead = MIN(
  2921. g_UlMaxBytesPerRead,
  2922. (ULONG) pResponse->FileBytesRemaining.QuadPart
  2923. );
  2924. //
  2925. // Initialize the UL_FILE_BUFFER.
  2926. //
  2927. pFileBuffer->pFileCacheEntry = pFileCacheEntry;
  2928. pFileBuffer->FileOffset = pResponse->FileOffset;
  2929. pFileBuffer->Length = BytesToRead;
  2930. pFileBuffer->pCompletionRoutine = UlpRestartMdlRead;
  2931. pFileBuffer->pContext = pTracker;
  2932. //
  2933. // Bump up the tracker refcount before starting the Read I/O.
  2934. // In case Send operation later on will complete before the read,
  2935. // we still want the tracker around until UlpRestartMdlRead
  2936. // finishes its business. It will be released when
  2937. // UlpRestartMdlRead gets called back.
  2938. //
  2939. UL_REFERENCE_CHUNK_TRACKER( pTracker );
  2940. //
  2941. // Issue the I/O.
  2942. //
  2943. Status = UlReadFileEntry(
  2944. pFileBuffer,
  2945. pTracker->pIrp
  2946. );
  2947. //
  2948. // If the read isn't pending, then deref the tracker since
  2949. // UlpRestartMdlRead isn't going to get called.
  2950. //
  2951. if (Status != STATUS_PENDING)
  2952. {
  2953. UL_DEREFERENCE_CHUNK_TRACKER( pTracker );
  2954. }
  2955. break;
  2956. }
  2957. }
  2958. //
  2959. // If we fell out of the above loop with status == STATUS_SUCCESS,
  2960. // then the last send we issued was buffered and needs to be flushed.
  2961. // Otherwise, if the status is anything but STATUS_PENDING, then we
  2962. // hit an in-line failure and need to complete the original request.
  2963. //
  2964. if (Status == STATUS_SUCCESS)
  2965. {
  2966. if (IS_SEND_COMPLETE(pResponse) &&
  2967. UlResumeParsingOnLastSend == pResponse->ResumeParsingType)
  2968. {
  2969. ResumeParsing = TRUE;
  2970. }
  2971. if (pTracker->SendInfo.BytesBuffered > 0)
  2972. {
  2973. //
  2974. // Flush the send.
  2975. //
  2976. Status = UlpFlushMdlRuns( pTracker );
  2977. }
  2978. else
  2979. if (IS_DISCONNECT_TIME(pResponse))
  2980. {
  2981. //
  2982. // Increment up until connection close is complete.
  2983. //
  2984. UL_REFERENCE_CHUNK_TRACKER( pTracker );
  2985. Status = UlDisconnectHttpConnection(
  2986. pTracker->pHttpConnection,
  2987. UlpCloseConnectionComplete,
  2988. pTracker
  2989. );
  2990. ASSERT( Status == STATUS_PENDING );
  2991. }
  2992. //
  2993. // Kick the parser into action if this is the last send for the
  2994. // keep-alive. Resuming parsing here improves latency when incoming
  2995. // requests are pipelined.
  2996. //
  2997. if (ResumeParsing)
  2998. {
  2999. UlTrace(HTTP_IO, (
  3000. "http!UlpSendHttpResponseWorker(pHttpConn = %p), "
  3001. "RequestVerb=%d, ResponseStatusCode=%hu\n",
  3002. pResponse->pRequest->pHttpConn,
  3003. pResponse->pRequest->Verb,
  3004. pResponse->StatusCode
  3005. ));
  3006. UlResumeParsing(
  3007. pResponse->pRequest->pHttpConn,
  3008. FALSE,
  3009. (BOOLEAN) (pResponse->Flags & HTTP_SEND_RESPONSE_FLAG_DISCONNECT)
  3010. );
  3011. }
  3012. }
  3013. //
  3014. // Did everything complete?
  3015. //
  3016. if (Status != STATUS_PENDING)
  3017. {
  3018. //
  3019. // Nope, something went wrong!
  3020. //
  3021. UlCompleteSendResponse( pTracker, Status );
  3022. }
  3023. else
  3024. {
  3025. //
  3026. // Release our grab on the tracker we are done with it.
  3027. //
  3028. UL_DEREFERENCE_CHUNK_TRACKER( pTracker );
  3029. }
  3030. } // UlpSendHttpResponseWorker
  3031. /***************************************************************************++
  3032. Routine Description:
  3033. Completion handler for UlCloseConnection().
  3034. Arguments:
  3035. pCompletionContext - Supplies an uninterpreted context value
  3036. as passed to the asynchronous API. This is actually a
  3037. PUL_CHUNK_TRACKER pointer.
  3038. Status - Supplies the final completion status of the
  3039. asynchronous API.
  3040. Information - Optionally supplies additional information about
  3041. the completed operation, such as the number of bytes
  3042. transferred. This field is unused for UlCloseConnection().
  3043. Return Values:
  3044. None.
  3045. --***************************************************************************/
  3046. VOID
  3047. UlpCloseConnectionComplete(
  3048. IN PVOID pCompletionContext,
  3049. IN NTSTATUS Status,
  3050. IN ULONG_PTR Information
  3051. )
  3052. {
  3053. PUL_CHUNK_TRACKER pTracker;
  3054. UNREFERENCED_PARAMETER( Information );
  3055. //
  3056. // Snag the context.
  3057. //
  3058. pTracker = (PUL_CHUNK_TRACKER) pCompletionContext;
  3059. UlTrace(SEND_RESPONSE, (
  3060. "UlpCloseConnectionComplete: tracker %p\n",
  3061. pTracker
  3062. ));
  3063. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  3064. UlCompleteSendResponse( pTracker, Status );
  3065. } // UlpCloseConnectionComplete
  3066. /***************************************************************************++
  3067. Routine Description:
  3068. Allocates a new send tracker. The newly created tracker must eventually
  3069. be freed with UlpFreeChunkTracker().
  3070. Arguments:
  3071. SendIrpStackSize - Supplies the stack size for the network send IRPs.
  3072. ReadIrpStackSize - Supplies the stack size for the file system read
  3073. IRPs.
  3074. Return Value:
  3075. PUL_CHUNK_TRACKER - The new send tracker if successful, NULL otherwise.
  3076. --***************************************************************************/
  3077. PUL_CHUNK_TRACKER
  3078. UlpAllocateChunkTracker(
  3079. IN UL_TRACKER_TYPE TrackerType,
  3080. IN CCHAR SendIrpStackSize,
  3081. IN CCHAR ReadIrpStackSize,
  3082. IN BOOLEAN FirstResponse,
  3083. IN PUL_HTTP_CONNECTION pHttpConnection,
  3084. IN PUL_INTERNAL_RESPONSE pResponse
  3085. )
  3086. {
  3087. PUL_CHUNK_TRACKER pTracker;
  3088. CCHAR MaxIrpStackSize;
  3089. USHORT MaxIrpSize;
  3090. ULONG ChunkTrackerSize;
  3091. ASSERT( TrackerType == UlTrackerTypeSend ||
  3092. TrackerType == UlTrackerTypeBuildUriEntry
  3093. );
  3094. MaxIrpStackSize = MAX(SendIrpStackSize, ReadIrpStackSize);
  3095. //
  3096. // Try to allocate from the lookaside list if possible.
  3097. //
  3098. if (MaxIrpStackSize > DEFAULT_MAX_IRP_STACK_SIZE)
  3099. {
  3100. MaxIrpSize = (USHORT) ALIGN_UP(IoSizeOfIrp(MaxIrpStackSize), PVOID);
  3101. ChunkTrackerSize = ALIGN_UP(sizeof(UL_CHUNK_TRACKER), PVOID) +
  3102. MaxIrpSize;
  3103. pTracker = (PUL_CHUNK_TRACKER) UL_ALLOCATE_POOL(
  3104. NonPagedPool,
  3105. ChunkTrackerSize,
  3106. UL_CHUNK_TRACKER_POOL_TAG
  3107. );
  3108. if (pTracker)
  3109. {
  3110. pTracker->Signature = UL_CHUNK_TRACKER_POOL_TAG;
  3111. pTracker->IrpContext.Signature = UL_IRP_CONTEXT_SIGNATURE;
  3112. pTracker->FromLookaside = FALSE;
  3113. //
  3114. // Set up the IRP.
  3115. //
  3116. pTracker->pIrp = (PIRP)
  3117. ((PCHAR)pTracker + ALIGN_UP(sizeof(UL_CHUNK_TRACKER), PVOID));
  3118. IoInitializeIrp(
  3119. pTracker->pIrp,
  3120. MaxIrpSize,
  3121. MaxIrpStackSize
  3122. );
  3123. }
  3124. }
  3125. else
  3126. {
  3127. pTracker = UlPplAllocateChunkTracker();
  3128. }
  3129. if (pTracker != NULL)
  3130. {
  3131. pTracker->Type = TrackerType;
  3132. pTracker->FirstResponse = FirstResponse;
  3133. //
  3134. // RefCounting is necessary since we might have two Aysnc (Read & Send)
  3135. // Io Operation on the same tracker along the way.
  3136. //
  3137. pTracker->RefCount = 1;
  3138. pTracker->Terminated = 0;
  3139. //
  3140. // Tracker will keep a reference to the connection.
  3141. //
  3142. UL_REFERENCE_HTTP_CONNECTION( pHttpConnection );
  3143. pTracker->pHttpConnection = pHttpConnection;
  3144. //
  3145. // Response info.
  3146. //
  3147. UL_REFERENCE_INTERNAL_RESPONSE( pResponse );
  3148. pTracker->pResponse = pResponse;
  3149. //
  3150. // Zero the remaining fields.
  3151. //
  3152. UlInitializeWorkItem( &pTracker->WorkItem );
  3153. RtlZeroMemory(
  3154. (PUCHAR)pTracker + FIELD_OFFSET(UL_CHUNK_TRACKER, IoStatus),
  3155. sizeof(*pTracker) - FIELD_OFFSET(UL_CHUNK_TRACKER, IoStatus)
  3156. );
  3157. if (TrackerType == UlTrackerTypeSend)
  3158. {
  3159. UlpInitMdlRuns( pTracker );
  3160. }
  3161. }
  3162. if (TrackerType == UlTrackerTypeSend)
  3163. {
  3164. UlTrace(SEND_RESPONSE, (
  3165. "Http!UlpAllocateChunkTracker: tracker %p (send)\n",
  3166. pTracker
  3167. ));
  3168. }
  3169. else
  3170. {
  3171. UlTrace(URI_CACHE, (
  3172. "Http!UlpAllocateChunkTracker: tracker %p (build uri)\n",
  3173. pTracker
  3174. ));
  3175. }
  3176. return pTracker;
  3177. } // UlpAllocateChunkTracker
  3178. /***************************************************************************++
  3179. Routine Description:
  3180. Frees a chunk tracker allocated with UlpAllocateChunkTracker().
  3181. If this is a send tracker, also free the MDL_RUNs attached to it.
  3182. Arguments:
  3183. pWorkItem - Supplies the work item embedded in UL_CHUNK_TRACKER.
  3184. Return Value:
  3185. None.
  3186. --***************************************************************************/
  3187. VOID
  3188. UlpFreeChunkTracker(
  3189. IN PUL_WORK_ITEM pWorkItem
  3190. )
  3191. {
  3192. PUL_CHUNK_TRACKER pTracker;
  3193. //
  3194. // Sanity check.
  3195. //
  3196. PAGED_CODE();
  3197. pTracker = CONTAINING_RECORD(
  3198. pWorkItem,
  3199. UL_CHUNK_TRACKER,
  3200. WorkItem
  3201. );
  3202. ASSERT( pTracker );
  3203. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  3204. ASSERT( pTracker->Type == UlTrackerTypeSend ||
  3205. pTracker->Type == UlTrackerTypeBuildUriEntry
  3206. );
  3207. //
  3208. // Free the MDLs attached if this is a send tracker.
  3209. //
  3210. if (pTracker->Type == UlTrackerTypeSend)
  3211. {
  3212. UlTrace(SEND_RESPONSE, (
  3213. "Http!UlpFreeChunkTracker: tracker %p (send)\n",
  3214. pTracker
  3215. ));
  3216. UlpFreeMdlRuns( pTracker );
  3217. }
  3218. else
  3219. {
  3220. UlTrace(URI_CACHE, (
  3221. "Http!UlpFreeChunkTracker: tracker %p (build uri)\n",
  3222. pTracker
  3223. ));
  3224. }
  3225. #if DBG
  3226. //
  3227. // There should be no file buffer hanging around at this time.
  3228. //
  3229. if (pTracker->Type == UlTrackerTypeSend)
  3230. {
  3231. ULONG i;
  3232. PUL_MDL_RUN pMdlRun = &pTracker->SendInfo.MdlRuns[0];
  3233. for (i = 0; i < UL_MAX_MDL_RUNS; i++)
  3234. {
  3235. if (pMdlRun->FileBuffer.pFileCacheEntry)
  3236. {
  3237. ASSERT( pMdlRun->FileBuffer.pMdl == NULL );
  3238. ASSERT( pMdlRun->FileBuffer.pFileData == NULL );
  3239. }
  3240. pMdlRun++;
  3241. }
  3242. }
  3243. #endif // DBG
  3244. //
  3245. // Release our ref to the connection and response.
  3246. //
  3247. UL_DEREFERENCE_HTTP_CONNECTION( pTracker->pHttpConnection );
  3248. UL_DEREFERENCE_INTERNAL_RESPONSE( pTracker->pResponse );
  3249. if (pTracker->FromLookaside)
  3250. {
  3251. UlPplFreeChunkTracker( pTracker );
  3252. }
  3253. else
  3254. {
  3255. UL_FREE_POOL_WITH_SIG( pTracker, UL_CHUNK_TRACKER_POOL_TAG );
  3256. }
  3257. } // UlpFreeChunkTracker
  3258. /***************************************************************************++
  3259. Routine Description:
  3260. Increments the reference count on the chunk tracker.
  3261. Arguments:
  3262. pTracker - Supplies the chunk trucker to the reference.
  3263. pFileName (REFERENCE_DEBUG only) - Supplies the name of the file
  3264. containing the calling function.
  3265. LineNumber (REFERENCE_DEBUG only) - Supplies the line number of
  3266. the calling function.
  3267. Return Value:
  3268. None.
  3269. --***************************************************************************/
  3270. VOID
  3271. UlReferenceChunkTracker(
  3272. IN PUL_CHUNK_TRACKER pTracker
  3273. REFERENCE_DEBUG_FORMAL_PARAMS
  3274. )
  3275. {
  3276. LONG RefCount;
  3277. //
  3278. // Sanity check.
  3279. //
  3280. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  3281. //
  3282. // Reference it.
  3283. //
  3284. RefCount = InterlockedIncrement( &pTracker->RefCount );
  3285. ASSERT( RefCount > 1 );
  3286. //
  3287. // Keep the logs updated.
  3288. //
  3289. WRITE_REF_TRACE_LOG(
  3290. g_pChunkTrackerTraceLog,
  3291. REF_ACTION_REFERENCE_CHUNK_TRACKER,
  3292. RefCount,
  3293. pTracker,
  3294. pFileName,
  3295. LineNumber
  3296. );
  3297. UlTrace(SEND_RESPONSE,(
  3298. "Http!UlReferenceChunkTracker: tracker %p RefCount %ld\n",
  3299. pTracker,
  3300. RefCount
  3301. ));
  3302. } // UlReferenceChunkTracker
  3303. /***************************************************************************++
  3304. Routine Description:
  3305. Decrements the reference count on the specified chunk tracker.
  3306. Arguments:
  3307. pTracker - Supplies the chunk trucker to the reference.
  3308. pFileName (REFERENCE_DEBUG only) - Supplies the name of the file
  3309. containing the calling function.
  3310. LineNumber (REFERENCE_DEBUG only) - Supplies the line number of
  3311. the calling function.
  3312. Return Value:
  3313. None.
  3314. --***************************************************************************/
  3315. VOID
  3316. UlDereferenceChunkTracker(
  3317. IN PUL_CHUNK_TRACKER pTracker
  3318. REFERENCE_DEBUG_FORMAL_PARAMS
  3319. )
  3320. {
  3321. LONG RefCount;
  3322. //
  3323. // Sanity check.
  3324. //
  3325. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  3326. //
  3327. // Dereference it.
  3328. //
  3329. RefCount = InterlockedDecrement( &pTracker->RefCount );
  3330. ASSERT(RefCount >= 0);
  3331. //
  3332. // Keep the logs updated.
  3333. //
  3334. WRITE_REF_TRACE_LOG(
  3335. g_pChunkTrackerTraceLog,
  3336. REF_ACTION_DEREFERENCE_CHUNK_TRACKER,
  3337. RefCount,
  3338. pTracker,
  3339. pFileName,
  3340. LineNumber
  3341. );
  3342. UlTrace(SEND_RESPONSE,(
  3343. "Http!UlDereferenceChunkTracker: tracker %p RefCount %ld\n",
  3344. pTracker,
  3345. RefCount
  3346. ));
  3347. if (RefCount == 0)
  3348. {
  3349. //
  3350. // The final reference to the chunk tracker has been removed,
  3351. // so it's time to free-up the ChunkTracker.
  3352. //
  3353. UL_CALL_PASSIVE(
  3354. &pTracker->WorkItem,
  3355. UlpFreeChunkTracker
  3356. );
  3357. }
  3358. } // UlDereferenceChunkTracker
  3359. /***************************************************************************++
  3360. Routine Description:
  3361. Closes the connection if neccessary, cleans up trackers, and completes
  3362. the response.
  3363. Arguments:
  3364. pWorkItem - Supplies the work item embedded in our UL_CHUNK_TRACKER.
  3365. Return Value:
  3366. None.
  3367. --***************************************************************************/
  3368. VOID
  3369. UlpCompleteSendResponseWorker(
  3370. PUL_WORK_ITEM pWorkItem
  3371. )
  3372. {
  3373. PUL_CHUNK_TRACKER pTracker;
  3374. PUL_COMPLETION_ROUTINE pCompletionRoutine;
  3375. PVOID pCompletionContext;
  3376. PUL_HTTP_CONNECTION pHttpConnection;
  3377. PUL_INTERNAL_REQUEST pRequest;
  3378. PUL_INTERNAL_RESPONSE pResponse;
  3379. NTSTATUS Status;
  3380. ULONGLONG BytesTransferred;
  3381. KIRQL OldIrql;
  3382. BOOLEAN ResumeParsing;
  3383. BOOLEAN InDisconnect;
  3384. HTTP_VERB RequestVerb;
  3385. USHORT ResponseStatusCode;
  3386. PUL_LOG_DATA_BUFFER pLogData;
  3387. //
  3388. // Sanity check.
  3389. //
  3390. PAGED_CODE();
  3391. pTracker = CONTAINING_RECORD(
  3392. pWorkItem,
  3393. UL_CHUNK_TRACKER,
  3394. WorkItem
  3395. );
  3396. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  3397. pResponse = pTracker->pResponse;
  3398. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pResponse ) );
  3399. ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pResponse->pRequest ) );
  3400. ASSERT( !pResponse->pLogData );
  3401. ASSERT( pResponse->ResumeParsingType != UlResumeParsingOnSendCompletion );
  3402. //
  3403. // Pull info from the tracker.
  3404. //
  3405. RequestVerb = pResponse->pRequest->Verb;
  3406. pCompletionRoutine = pResponse->pCompletionRoutine;
  3407. pCompletionContext = pResponse->pCompletionContext;
  3408. pRequest = pResponse->pRequest;
  3409. BytesTransferred = pResponse->BytesTransferred;
  3410. ResponseStatusCode = pResponse->StatusCode;
  3411. Status = pTracker->IoStatus.Status;
  3412. pHttpConnection = pTracker->pHttpConnection;
  3413. ResumeParsing = FALSE;
  3414. pLogData = NULL;
  3415. InDisconnect = (BOOLEAN)
  3416. (pResponse->Flags & HTTP_SEND_RESPONSE_FLAG_DISCONNECT);
  3417. TRACE_TIME(
  3418. pHttpConnection->ConnectionId,
  3419. pRequest->RequestId,
  3420. TIME_ACTION_SEND_COMPLETE
  3421. );
  3422. //
  3423. // Reset the connection if there was an error.
  3424. //
  3425. if (!NT_SUCCESS(Status))
  3426. {
  3427. UlCloseConnection(
  3428. pHttpConnection->pConnection,
  3429. TRUE,
  3430. NULL,
  3431. NULL
  3432. );
  3433. }
  3434. //
  3435. // Adjust the bytes sent and send status on the request.
  3436. //
  3437. UlInterlockedAdd64(
  3438. (PLONGLONG) &pRequest->BytesSent,
  3439. BytesTransferred
  3440. );
  3441. if (!NT_SUCCESS(Status) && NT_SUCCESS(pRequest->LogStatus))
  3442. {
  3443. pRequest->LogStatus = Status;
  3444. }
  3445. IF_DEBUG(LOGBYTES)
  3446. {
  3447. TIME_FIELDS RcvdTimeFields;
  3448. RtlTimeToTimeFields( &pRequest->TimeStamp, &RcvdTimeFields );
  3449. UlTrace(LOGBYTES, (
  3450. "Http!UlpCompleteSendResponseWorker: [Rcvd @ %02d:%02d:%02d] "
  3451. "Bytes %010I64u Total %010I64u Status %08lX\n",
  3452. RcvdTimeFields.Hour,
  3453. RcvdTimeFields.Minute,
  3454. RcvdTimeFields.Second,
  3455. BytesTransferred,
  3456. pRequest->BytesSent,
  3457. Status
  3458. ));
  3459. }
  3460. //
  3461. // Stop MinBytesPerSecond timer and start Connection Idle timer.
  3462. //
  3463. UlLockTimeoutInfo(
  3464. &pHttpConnection->TimeoutInfo,
  3465. &OldIrql
  3466. );
  3467. //
  3468. // Turn off MinBytesPerSecond timer if there are no outstanding sends.
  3469. //
  3470. UlResetConnectionTimer(
  3471. &pHttpConnection->TimeoutInfo,
  3472. TimerMinBytesPerSecond
  3473. );
  3474. if (0 == (pResponse->Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA) &&
  3475. pRequest->ParseState >= ParseDoneState)
  3476. {
  3477. //
  3478. // Turn on Idle Timer if there's no more response data AND all of
  3479. // the request data has been received.
  3480. //
  3481. UlSetConnectionTimer(
  3482. &pHttpConnection->TimeoutInfo,
  3483. TimerConnectionIdle
  3484. );
  3485. }
  3486. UlUnlockTimeoutInfo(
  3487. &pHttpConnection->TimeoutInfo,
  3488. OldIrql
  3489. );
  3490. UlEvaluateTimerState(
  3491. &pHttpConnection->TimeoutInfo
  3492. );
  3493. //
  3494. // Adjust SendsPending and if that drops to zero, see if we need to log
  3495. // and resume parsing.
  3496. //
  3497. UlUnsetRequestSendsPending(
  3498. pRequest,
  3499. &pLogData,
  3500. &ResumeParsing
  3501. );
  3502. if (pLogData)
  3503. {
  3504. UlLogHttpResponse( pRequest, pLogData );
  3505. }
  3506. //
  3507. // Unlink the request from process if we are done with all sends.
  3508. //
  3509. if (0 == (pResponse->Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA) &&
  3510. 0 == pRequest->ContentLength &&
  3511. 0 == pRequest->Chunked &&
  3512. pRequest->ConfigInfo.pAppPool)
  3513. {
  3514. ASSERT( pRequest->SentLast );
  3515. UlUnlinkRequestFromProcess(
  3516. pRequest->ConfigInfo.pAppPool,
  3517. pRequest
  3518. );
  3519. }
  3520. //
  3521. // Complete the send response IRP.
  3522. //
  3523. ASSERT( pCompletionRoutine != NULL );
  3524. (pCompletionRoutine)(
  3525. pCompletionContext,
  3526. Status,
  3527. (ULONG) MIN(BytesTransferred, MAXULONG)
  3528. );
  3529. //
  3530. // Kick the parser on the connection and release our hold.
  3531. //
  3532. if (ResumeParsing && STATUS_SUCCESS == Status)
  3533. {
  3534. UlTrace(HTTP_IO, (
  3535. "http!UlpCompleteSendResponseWorker(pHttpConn = %p), "
  3536. "RequestVerb=%d, ResponseStatusCode=%hu\n",
  3537. pHttpConnection,
  3538. RequestVerb,
  3539. ResponseStatusCode
  3540. ));
  3541. UlResumeParsing( pHttpConnection, FALSE, InDisconnect );
  3542. }
  3543. //
  3544. // Deref the tracker that we have bumped up before queueing this worker
  3545. // function. This has to be done after UlResumeParsing since the tracker
  3546. // holds a reference on the HTTP connection.
  3547. //
  3548. UL_DEREFERENCE_CHUNK_TRACKER( pTracker );
  3549. } // UlpCompleteSendResponseWorker
  3550. /***************************************************************************++
  3551. Routine Description:
  3552. Completion handler for MDL READ IRPs used for reading file data.
  3553. Arguments:
  3554. pDeviceObject - Supplies the device object for the IRP being
  3555. completed.
  3556. pIrp - Supplies the IRP being completed.
  3557. pContext - Supplies the context associated with this request.
  3558. This is actually a PUL_CHUNK_TRACKER.
  3559. Return Value:
  3560. NTSTATUS - STATUS_SUCCESS if IO should continue processing this
  3561. IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
  3562. this IRP.
  3563. --***************************************************************************/
  3564. NTSTATUS
  3565. UlpRestartMdlRead(
  3566. IN PDEVICE_OBJECT pDeviceObject,
  3567. IN PIRP pIrp,
  3568. IN PVOID pContext
  3569. )
  3570. {
  3571. PUL_CHUNK_TRACKER pTracker;
  3572. UNREFERENCED_PARAMETER( pDeviceObject );
  3573. pTracker = (PUL_CHUNK_TRACKER) pContext;
  3574. UlTrace(SEND_RESPONSE, (
  3575. "UlpRestartMdlRead: tracker %p\n",
  3576. pTracker
  3577. ));
  3578. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  3579. pTracker->IoStatus = pIrp->IoStatus;
  3580. UlpQueueResponseWorkItem(
  3581. &pTracker->WorkItem,
  3582. UlpMdlReadCompleteWorker,
  3583. pTracker->pResponse->SyncRead
  3584. );
  3585. return STATUS_MORE_PROCESSING_REQUIRED;
  3586. } // UlpRestartMdlRead
  3587. /***************************************************************************++
  3588. Routine Description:
  3589. The worker routine for UlpRestartMdlRead since we can potentially call
  3590. into UlResumeParsing which requires PASSIVE.
  3591. Arguments:
  3592. pWorkItem - Supplies the work item embedded in UL_CHUNK_TRACKER.
  3593. Return Value:
  3594. None.
  3595. --***************************************************************************/
  3596. VOID
  3597. UlpMdlReadCompleteWorker(
  3598. IN PUL_WORK_ITEM pWorkItem
  3599. )
  3600. {
  3601. NTSTATUS Status;
  3602. PUL_CHUNK_TRACKER pTracker;
  3603. PUL_INTERNAL_RESPONSE pResponse;
  3604. ULONG BytesRead;
  3605. PMDL pMdl;
  3606. PMDL pMdlTail;
  3607. BOOLEAN ResumeParsing = FALSE;
  3608. PUL_MDL_RUN pMdlRun;
  3609. PUL_FILE_BUFFER pFileBuffer;
  3610. ULONG RunCount;
  3611. //
  3612. // Sanity check.
  3613. //
  3614. PAGED_CODE();
  3615. pTracker = CONTAINING_RECORD(
  3616. pWorkItem,
  3617. UL_CHUNK_TRACKER,
  3618. WorkItem
  3619. );
  3620. UlTrace(SEND_RESPONSE, (
  3621. "UlpMdlReadCompleteWorker: tracker %p\n",
  3622. pTracker
  3623. ));
  3624. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  3625. pResponse = pTracker->pResponse;
  3626. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pResponse ) );
  3627. //
  3628. // Get the last MdlRun from the tracker.
  3629. //
  3630. RunCount = pTracker->SendInfo.MdlRunCount;
  3631. pMdlRun = &pTracker->SendInfo.MdlRuns[RunCount];
  3632. Status = pTracker->IoStatus.Status;
  3633. if (NT_SUCCESS(Status))
  3634. {
  3635. BytesRead = (ULONG) pTracker->IoStatus.Information;
  3636. if (BytesRead)
  3637. {
  3638. pFileBuffer = &pMdlRun->FileBuffer;
  3639. pMdl = pFileBuffer->pMdl;
  3640. ASSERT( pMdl );
  3641. //
  3642. // Update the buffered byte count and append the new MDL onto
  3643. // our MDL chain.
  3644. //
  3645. pMdlTail = UlFindLastMdlInChain( pMdl );
  3646. pTracker->SendInfo.BytesBuffered += BytesRead;
  3647. (*pTracker->SendInfo.pMdlLink) = pMdl;
  3648. pTracker->SendInfo.pMdlLink = &pMdlTail->Next;
  3649. pMdlRun->pMdlTail = pMdlTail;
  3650. pTracker->SendInfo.MdlRunCount++;
  3651. //
  3652. // Update the file offset & bytes remaining. If we've
  3653. // finished this file chunk (bytes remaining is now zero)
  3654. // then advance to the next chunk.
  3655. //
  3656. pResponse->FileOffset.QuadPart += (ULONGLONG) BytesRead;
  3657. pResponse->FileBytesRemaining.QuadPart -= (ULONGLONG) BytesRead;
  3658. }
  3659. else
  3660. {
  3661. ASSERT( !"Status success but zero bytes received!" );
  3662. }
  3663. if (pResponse->FileBytesRemaining.QuadPart == 0)
  3664. {
  3665. UlpIncrementChunkPointer( pResponse );
  3666. }
  3667. //
  3668. // If we've not exhausted our static MDL run array,
  3669. // we've exceeded the maximum number of bytes we want to
  3670. // buffer, then we'll need to initiate a flush.
  3671. //
  3672. if (IS_SEND_COMPLETE(pResponse) ||
  3673. UL_MAX_MDL_RUNS == pTracker->SendInfo.MdlRunCount ||
  3674. pTracker->SendInfo.BytesBuffered >= g_UlMaxBytesPerSend)
  3675. {
  3676. if (IS_SEND_COMPLETE(pResponse) &&
  3677. UlResumeParsingOnLastSend == pResponse->ResumeParsingType)
  3678. {
  3679. ResumeParsing = TRUE;
  3680. }
  3681. Status = UlpFlushMdlRuns( pTracker );
  3682. //
  3683. // Kick the parser into action if this is the last send for the
  3684. // keep-alive. Resuming parsing here improves latency when incoming
  3685. // requests are pipelined.
  3686. //
  3687. if (ResumeParsing)
  3688. {
  3689. UlTrace(HTTP_IO, (
  3690. "http!UlpRestartMdlRead(pHttpConn = %p), "
  3691. "RequestVerb=%d, ResponseStatusCode=%hu\n",
  3692. pResponse->pRequest->pHttpConn,
  3693. pResponse->pRequest->Verb,
  3694. pResponse->StatusCode
  3695. ));
  3696. UlResumeParsing(
  3697. pResponse->pRequest->pHttpConn,
  3698. FALSE,
  3699. (BOOLEAN) (pResponse->Flags & HTTP_SEND_RESPONSE_FLAG_DISCONNECT)
  3700. );
  3701. }
  3702. }
  3703. else
  3704. {
  3705. //
  3706. // RefCount the chunk tracker up for the UlpSendHttpResponseWorker.
  3707. // It will DeRef it when it's done with the chunk tracker itself.
  3708. // Since this is a passive call we had to increment the refcount
  3709. // for this guy to make sure that tracker is around until it wakes
  3710. // up. Other places makes calls to UlpSendHttpResponseWorker has
  3711. // also been updated as well.
  3712. //
  3713. UL_REFERENCE_CHUNK_TRACKER( pTracker );
  3714. UlpSendHttpResponseWorker( &pTracker->WorkItem );
  3715. }
  3716. }
  3717. else
  3718. {
  3719. //
  3720. // Do not increment the MdlRunCount, as we are not able to update the
  3721. // MDL Links. Instead cleanup the last allocated MDL Run for the read.
  3722. //
  3723. UlpFreeFileMdlRun( pTracker, pMdlRun );
  3724. }
  3725. if (!NT_SUCCESS(Status))
  3726. {
  3727. UlCompleteSendResponse( pTracker, Status );
  3728. }
  3729. else
  3730. {
  3731. //
  3732. // Read I/O has been completed release our refcount
  3733. // on the chunk tracker.
  3734. //
  3735. UL_DEREFERENCE_CHUNK_TRACKER( pTracker );
  3736. }
  3737. } // UlpMdlReadCompleteWorker
  3738. /***************************************************************************++
  3739. Routine Description:
  3740. Completion handler for UlSendData().
  3741. Arguments:
  3742. pCompletionContext - Supplies an uninterpreted context value
  3743. as passed to the asynchronous API. This is actually a
  3744. pointer to a UL_CHUNK_TRACKER structure.
  3745. Status - Supplies the final completion status of the
  3746. asynchronous API.
  3747. Information - Optionally supplies additional information about
  3748. the completed operation, such as the number of bytes
  3749. transferred.
  3750. Return Value:
  3751. None.
  3752. --***************************************************************************/
  3753. VOID
  3754. UlpRestartMdlSend(
  3755. IN PVOID pCompletionContext,
  3756. IN NTSTATUS Status,
  3757. IN ULONG_PTR Information
  3758. )
  3759. {
  3760. PUL_CHUNK_TRACKER pTracker;
  3761. LONG SendCount;
  3762. pTracker = (PUL_CHUNK_TRACKER) pCompletionContext;
  3763. UlTrace(SEND_RESPONSE, (
  3764. "UlpRestartMdlSend: tracker %p\n",
  3765. pTracker
  3766. ));
  3767. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  3768. //
  3769. // Handle the completion in a work item. We need to get to passive
  3770. // level and we also need to prevent a recursive loop on filtered
  3771. // connections or any other case where our sends might all be
  3772. // completing in-line.
  3773. //
  3774. if (pTracker->SendInfo.pMdlToSplit)
  3775. {
  3776. //
  3777. // This is the split send.
  3778. //
  3779. SendCount = InterlockedDecrement( &pTracker->SendInfo.SendCount );
  3780. ASSERT( SendCount >= 0 );
  3781. if (0 == SendCount)
  3782. {
  3783. //
  3784. // Simply drops the reference on the tracker if this is the
  3785. // second part of the split send.
  3786. //
  3787. UL_DEREFERENCE_CHUNK_TRACKER( pTracker );
  3788. }
  3789. else
  3790. {
  3791. pTracker->IoStatus.Status = Status;
  3792. if (NT_SUCCESS(Status))
  3793. {
  3794. //
  3795. // Report the bytes transferred for the whole send in the
  3796. // success case since we may have split into 2 TDI calls.
  3797. //
  3798. pTracker->IoStatus.Information = pTracker->SendInfo.BytesBuffered;
  3799. }
  3800. else
  3801. {
  3802. pTracker->IoStatus.Information = Information;
  3803. }
  3804. UlpQueueResponseWorkItem(
  3805. &pTracker->WorkItem,
  3806. UlpMdlSendCompleteWorker,
  3807. pTracker->pResponse->SyncRead
  3808. );
  3809. }
  3810. }
  3811. else
  3812. {
  3813. //
  3814. // This is the normal send.
  3815. //
  3816. ASSERT( -1 == pTracker->SendInfo.SendCount );
  3817. pTracker->IoStatus.Status = Status;
  3818. pTracker->IoStatus.Information = Information;
  3819. UlpQueueResponseWorkItem(
  3820. &pTracker->WorkItem,
  3821. UlpMdlSendCompleteWorker,
  3822. pTracker->pResponse->SyncRead
  3823. );
  3824. }
  3825. } // UlpRestartMdlSend
  3826. /***************************************************************************++
  3827. Routine Description:
  3828. Deferred handler for UlpRestartMdlSend.
  3829. Arguments:
  3830. pWorkItem - Supplies a pointer to the work item queued. This should
  3831. point to the WORK_ITEM structure embedded in a UL_CHUNK_TRACKER.
  3832. Return Value:
  3833. None.
  3834. --***************************************************************************/
  3835. VOID
  3836. UlpMdlSendCompleteWorker(
  3837. IN PUL_WORK_ITEM pWorkItem
  3838. )
  3839. {
  3840. PUL_CHUNK_TRACKER pTracker;
  3841. PUL_CHUNK_TRACKER pSendTracker;
  3842. PUL_INTERNAL_RESPONSE pResponse;
  3843. PUL_HTTP_CONNECTION pHttpConn;
  3844. PDEVICE_OBJECT pConnectionObject;
  3845. NTSTATUS Status;
  3846. BOOLEAN DerefChunkTracker = TRUE;
  3847. //
  3848. // Sanity check.
  3849. //
  3850. PAGED_CODE();
  3851. pTracker = CONTAINING_RECORD(
  3852. pWorkItem,
  3853. UL_CHUNK_TRACKER,
  3854. WorkItem
  3855. );
  3856. UlTrace(SEND_RESPONSE, (
  3857. "UlpMdlSendCompleteWorker: tracker %p\n",
  3858. pTracker
  3859. ));
  3860. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  3861. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pTracker->pResponse ) );
  3862. pResponse = pTracker->pResponse;
  3863. //
  3864. // If the chunk completed successfully, then update the bytes
  3865. // transferred and queue another work item for the next chunk if
  3866. // there's more work to do. Otherwise, just complete the request now.
  3867. //
  3868. Status = pTracker->IoStatus.Status;
  3869. if (NT_SUCCESS(Status))
  3870. {
  3871. pResponse->BytesTransferred += pTracker->IoStatus.Information;
  3872. if (!IS_SEND_COMPLETE(pResponse))
  3873. {
  3874. //
  3875. // Allocate a new send tracker for the next round of MDL_RUNs.
  3876. //
  3877. pHttpConn = pTracker->pHttpConnection;
  3878. pConnectionObject =
  3879. pHttpConn->pConnection->ConnectionObject.pDeviceObject;
  3880. pSendTracker =
  3881. UlpAllocateChunkTracker(
  3882. UlTrackerTypeSend,
  3883. pConnectionObject->StackSize,
  3884. pResponse->MaxFileSystemStackSize,
  3885. FALSE,
  3886. pHttpConn,
  3887. pResponse
  3888. );
  3889. if (pSendTracker)
  3890. {
  3891. UlpSendHttpResponseWorker( &pSendTracker->WorkItem );
  3892. goto end;
  3893. }
  3894. else
  3895. {
  3896. //
  3897. // Reset the connection since we hit an internal error.
  3898. //
  3899. UlCloseConnection(
  3900. pHttpConn->pConnection,
  3901. TRUE,
  3902. NULL,
  3903. NULL
  3904. );
  3905. Status = STATUS_NO_MEMORY;
  3906. }
  3907. }
  3908. }
  3909. //
  3910. // All done.
  3911. //
  3912. UlCompleteSendResponse( pTracker, Status );
  3913. //
  3914. // UlCompleteSendResponse takes ownership of the CHUNK_TRACKER so no extra
  3915. // dereference is required.
  3916. //
  3917. DerefChunkTracker = FALSE;
  3918. end:
  3919. //
  3920. // Release our grab on the Tracker. Send I/O is done for this MDL run.
  3921. //
  3922. if (DerefChunkTracker)
  3923. {
  3924. UL_DEREFERENCE_CHUNK_TRACKER( pTracker );
  3925. }
  3926. } // UlpMdlSendCompleteWorker
  3927. /***************************************************************************++
  3928. Routine Description:
  3929. Flush the MDL_RUNs we have built so far.
  3930. Arguments:
  3931. pTracker - Supplies the send tracker to flush.
  3932. Return Value:
  3933. NTSTATUS - Completion status.
  3934. --***************************************************************************/
  3935. NTSTATUS
  3936. UlpFlushMdlRuns(
  3937. IN PUL_CHUNK_TRACKER pTracker
  3938. )
  3939. {
  3940. PUL_INTERNAL_RESPONSE pResponse;
  3941. PMDL pMdlToSplit = NULL;
  3942. PMDL pMdlPrevious;
  3943. PMDL pMdlSplitFirst;
  3944. PMDL pMdlSplitSecond = NULL;
  3945. PMDL pMdlHead = NULL;
  3946. ULONG BytesToSplit = 0;
  3947. ULONG BytesBuffered;
  3948. ULONG BytesPart1;
  3949. ULONG BytesPart2;
  3950. NTSTATUS Status;
  3951. BOOLEAN SendComplete;
  3952. BOOLEAN CopySend = FALSE;
  3953. //
  3954. // Sanity check.
  3955. //
  3956. PAGED_CODE();
  3957. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  3958. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pTracker->pResponse ) );
  3959. ASSERT( pTracker->SendInfo.pMdlHead );
  3960. pResponse = pTracker->pResponse;
  3961. SendComplete = (BOOLEAN) IS_SEND_COMPLETE( pResponse );
  3962. //
  3963. // We may need to split the send into 2 TDI calls if the send is *large*
  3964. // and it is not filtered.
  3965. //
  3966. if (!pTracker->pHttpConnection->pConnection->FilterInfo.pFilterChannel &&
  3967. pTracker->SendInfo.BytesBuffered > g_UlMaxCopyThreshold &&
  3968. (!SendComplete || pResponse->CopySend))
  3969. {
  3970. //
  3971. // These many bytes go to the first part of the MDL chain after split.
  3972. //
  3973. if (!SendComplete)
  3974. {
  3975. BytesToSplit = pTracker->SendInfo.BytesBuffered / 2;
  3976. }
  3977. else
  3978. {
  3979. ASSERT( pResponse->CopySend );
  3980. CopySend = TRUE;
  3981. BytesToSplit = pTracker->SendInfo.BytesBuffered -
  3982. g_UlMaxCopyThreshold;
  3983. }
  3984. //
  3985. // Find the first MDL starting from pMdlHead that has more than
  3986. // or equal to BytesToSplit bytes buffered.
  3987. //
  3988. pMdlPrevious = NULL;
  3989. pMdlToSplit = pTracker->SendInfo.pMdlHead;
  3990. BytesBuffered = 0;
  3991. while (pMdlToSplit->Next)
  3992. {
  3993. if ((BytesBuffered + pMdlToSplit->ByteCount) >= BytesToSplit)
  3994. {
  3995. //
  3996. // So the current MDL splits the chain.
  3997. //
  3998. break;
  3999. }
  4000. BytesBuffered += pMdlToSplit->ByteCount;
  4001. pMdlPrevious = pMdlToSplit;
  4002. pMdlToSplit = pMdlToSplit->Next;
  4003. }
  4004. ASSERT( pMdlToSplit );
  4005. ASSERT( (BytesBuffered + pMdlToSplit->ByteCount) >= BytesToSplit );
  4006. if ((BytesBuffered + pMdlToSplit->ByteCount) == BytesToSplit)
  4007. {
  4008. //
  4009. // There is no need to build partial MDLs of the split MDL. The
  4010. // whole MDL chain up to and including pMdlToSplit goes to the
  4011. // first half the splitted chain and the MDL chain starting from
  4012. // pMdlToSplit->Next goes to the second half.
  4013. //
  4014. ASSERT( pMdlToSplit->Next );
  4015. pMdlHead = pTracker->SendInfo.pMdlHead;
  4016. pMdlSplitFirst = pMdlToSplit;
  4017. pMdlSplitSecond = pMdlToSplit->Next;
  4018. pMdlToSplit->Next = NULL;
  4019. }
  4020. else
  4021. {
  4022. BytesPart2 = BytesBuffered + pMdlToSplit->ByteCount - BytesToSplit;
  4023. BytesPart1 = pMdlToSplit->ByteCount - BytesPart2;
  4024. ASSERT( BytesPart1 );
  4025. ASSERT( BytesPart2 );
  4026. pMdlSplitFirst =
  4027. UlAllocateMdl(
  4028. (PCHAR) MmGetMdlVirtualAddress(pMdlToSplit),
  4029. BytesPart1,
  4030. FALSE,
  4031. FALSE,
  4032. NULL
  4033. );
  4034. if (!pMdlSplitFirst)
  4035. {
  4036. return STATUS_INSUFFICIENT_RESOURCES;
  4037. }
  4038. pMdlSplitSecond =
  4039. UlAllocateMdl(
  4040. (PCHAR) MmGetMdlVirtualAddress(pMdlToSplit) + BytesPart1,
  4041. BytesPart2,
  4042. FALSE,
  4043. FALSE,
  4044. NULL
  4045. );
  4046. if (!pMdlSplitSecond)
  4047. {
  4048. UlFreeMdl( pMdlSplitFirst );
  4049. return STATUS_INSUFFICIENT_RESOURCES;
  4050. }
  4051. IoBuildPartialMdl(
  4052. pMdlToSplit,
  4053. pMdlSplitFirst,
  4054. (PCHAR) MmGetMdlVirtualAddress(pMdlToSplit),
  4055. BytesPart1
  4056. );
  4057. IoBuildPartialMdl(
  4058. pMdlToSplit,
  4059. pMdlSplitSecond,
  4060. (PCHAR) MmGetMdlVirtualAddress(pMdlToSplit) + BytesPart1,
  4061. BytesPart2
  4062. );
  4063. //
  4064. // Relink the MDL chains after the split.
  4065. //
  4066. if (pMdlPrevious)
  4067. {
  4068. pMdlHead = pTracker->SendInfo.pMdlHead;
  4069. pMdlPrevious->Next = pMdlSplitFirst;
  4070. }
  4071. else
  4072. {
  4073. ASSERT( pMdlToSplit == pTracker->SendInfo.pMdlHead );
  4074. pMdlHead = pMdlSplitFirst;
  4075. }
  4076. pMdlSplitSecond->Next = pMdlToSplit->Next;
  4077. }
  4078. //
  4079. // Remember how we have split the send.
  4080. //
  4081. pTracker->SendInfo.pMdlToSplit = pMdlToSplit;
  4082. pTracker->SendInfo.pMdlPrevious = pMdlPrevious;
  4083. pTracker->SendInfo.pMdlSplitFirst = pMdlSplitFirst;
  4084. pTracker->SendInfo.pMdlSplitSecond = pMdlSplitSecond;
  4085. }
  4086. //
  4087. // Make sure there are no other sends in progress on this response.
  4088. // Wait if this is the case. Since it is possible for the first part
  4089. // of the split send to complete inline, it can start a new MDL run
  4090. // and proceed to flush *before* the second part of the split send
  4091. // has a chance to pend the data in TDI. Of course, this logic is not
  4092. // needed if we know the current flush is both the first and the last
  4093. // of MDL runs.
  4094. //
  4095. if (!SendComplete || !pTracker->FirstResponse)
  4096. {
  4097. UlAcquirePushLockExclusive( &pResponse->PushLock );
  4098. }
  4099. //
  4100. // Increment the reference on tracker for each Send I/O.
  4101. // UlpMdlSendCompleteWorker will release it later.
  4102. //
  4103. UL_REFERENCE_CHUNK_TRACKER( pTracker );
  4104. if (pMdlToSplit)
  4105. {
  4106. //
  4107. // We need to issue 2 TDI calls since we have split the send.
  4108. //
  4109. pTracker->SendInfo.SendCount = 2;
  4110. Status = UlSendData(
  4111. pTracker->pHttpConnection->pConnection,
  4112. pMdlHead,
  4113. BytesToSplit,
  4114. UlpRestartMdlSend,
  4115. pTracker,
  4116. pTracker->pIrp,
  4117. &pTracker->IrpContext,
  4118. FALSE,
  4119. FALSE
  4120. );
  4121. ASSERT( Status == STATUS_PENDING);
  4122. //
  4123. // Increment the extra reference on tracker for the Split Send I/O.
  4124. // UlpMdlSendCompleteWorker will release it later.
  4125. //
  4126. UL_REFERENCE_CHUNK_TRACKER( pTracker );
  4127. if (CopySend)
  4128. {
  4129. Status = UlpCopySend(
  4130. pTracker,
  4131. pMdlSplitSecond,
  4132. pTracker->SendInfo.BytesBuffered - BytesToSplit,
  4133. (BOOLEAN) (SendComplete &&
  4134. IS_DISCONNECT_TIME(pResponse)),
  4135. (BOOLEAN) (pResponse->pRequest->ParseState >=
  4136. ParseDoneState)
  4137. );
  4138. }
  4139. else
  4140. {
  4141. Status = UlSendData(
  4142. pTracker->pHttpConnection->pConnection,
  4143. pMdlSplitSecond,
  4144. pTracker->SendInfo.BytesBuffered - BytesToSplit,
  4145. UlpRestartMdlSend,
  4146. pTracker,
  4147. NULL,
  4148. NULL,
  4149. (BOOLEAN) (SendComplete &&
  4150. IS_DISCONNECT_TIME(pResponse)),
  4151. (BOOLEAN) (pResponse->pRequest->ParseState >=
  4152. ParseDoneState)
  4153. );
  4154. }
  4155. }
  4156. else
  4157. {
  4158. //
  4159. // Use -1 so we know we haven't done any split on this send.
  4160. //
  4161. pTracker->SendInfo.SendCount = -1;
  4162. //
  4163. // If this the last send to be issued for this response, we can ask
  4164. // UlSendData to initiate a disconnect on our behalf if appropriate.
  4165. //
  4166. Status = UlSendData(
  4167. pTracker->pHttpConnection->pConnection,
  4168. pTracker->SendInfo.pMdlHead,
  4169. pTracker->SendInfo.BytesBuffered,
  4170. UlpRestartMdlSend,
  4171. pTracker,
  4172. pTracker->pIrp,
  4173. &pTracker->IrpContext,
  4174. (BOOLEAN) (SendComplete &&
  4175. IS_DISCONNECT_TIME(pResponse)),
  4176. (BOOLEAN) (pResponse->pRequest->ParseState >=
  4177. ParseDoneState)
  4178. );
  4179. }
  4180. //
  4181. // Pave the way for a new UlpFlushMdlRuns to proceed.
  4182. //
  4183. if (!SendComplete || !pTracker->FirstResponse)
  4184. {
  4185. UlReleasePushLockExclusive( &pResponse->PushLock );
  4186. }
  4187. //
  4188. // Start the next response in the pending response list if exists.
  4189. // The tracker should still have one reference held by the caller
  4190. // so it is safe to touch its fields here.
  4191. //
  4192. if (pResponse->SendEnqueued && SendComplete)
  4193. {
  4194. ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pResponse->pRequest ) );
  4195. UlpDequeueSendHttpResponse( pResponse->pRequest );
  4196. }
  4197. ASSERT( Status == STATUS_PENDING);
  4198. return Status;
  4199. } // UlpFlushMdlRuns
  4200. /***************************************************************************++
  4201. Routine Description:
  4202. Cleans the MDL_RUNs in the specified tracker and prepares the
  4203. tracker for reuse.
  4204. Arguments:
  4205. pTracker - Supplies the tracker to clean.
  4206. Return Value:
  4207. None.
  4208. --***************************************************************************/
  4209. VOID
  4210. UlpFreeMdlRuns(
  4211. IN PUL_CHUNK_TRACKER pTracker
  4212. )
  4213. {
  4214. PMDL pMdlHead;
  4215. PMDL pMdlNext;
  4216. PMDL pMdlTmp;
  4217. PMDL pMdlToSplit;
  4218. PMDL pMdlPrevious;
  4219. PMDL pMdlSplitFirst;
  4220. PMDL pMdlSplitSecond;
  4221. PUL_MDL_RUN pMdlRun;
  4222. ULONG RunCount;
  4223. //
  4224. // Sanity check.
  4225. //
  4226. PAGED_CODE();
  4227. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  4228. //
  4229. // Restore the original MDL chain and ByteCount if we have splitted
  4230. // this send.
  4231. //
  4232. pMdlToSplit = pTracker->SendInfo.pMdlToSplit;
  4233. if (pMdlToSplit)
  4234. {
  4235. pMdlPrevious = pTracker->SendInfo.pMdlPrevious;
  4236. pMdlSplitFirst = pTracker->SendInfo.pMdlSplitFirst;
  4237. pMdlSplitSecond = pTracker->SendInfo.pMdlSplitSecond;
  4238. ASSERT( pMdlSplitFirst );
  4239. ASSERT( pMdlSplitSecond );
  4240. if (pMdlSplitFirst == pMdlToSplit)
  4241. {
  4242. //
  4243. // No partial MDL involved. Simply link back the MDLs.
  4244. //
  4245. pMdlSplitFirst->Next = pMdlSplitSecond;
  4246. }
  4247. else
  4248. {
  4249. ASSERT( pMdlToSplit->Next == pMdlSplitSecond->Next );
  4250. if (pMdlPrevious)
  4251. {
  4252. pMdlPrevious->Next = pMdlToSplit;
  4253. }
  4254. else
  4255. {
  4256. ASSERT( pMdlToSplit == pTracker->SendInfo.pMdlHead );
  4257. }
  4258. //
  4259. // Free the partial MDLs we have built for the split send.
  4260. //
  4261. UlFreeMdl( pMdlSplitFirst );
  4262. UlFreeMdl( pMdlSplitSecond );
  4263. }
  4264. }
  4265. pMdlHead = pTracker->SendInfo.pMdlHead;
  4266. pMdlRun = &pTracker->SendInfo.MdlRuns[0];
  4267. RunCount = pTracker->SendInfo.MdlRunCount;
  4268. while (RunCount > 0)
  4269. {
  4270. ASSERT( pMdlHead != NULL );
  4271. ASSERT( pMdlRun->pMdlTail != NULL );
  4272. pMdlNext = pMdlRun->pMdlTail->Next;
  4273. pMdlRun->pMdlTail->Next = NULL;
  4274. if (pMdlRun->FileBuffer.pFileCacheEntry == NULL)
  4275. {
  4276. //
  4277. // It's a memory/cache run; just walk & free the MDL chain.
  4278. // UlFreeMdl unmaps the data for partial MDLs so no need to
  4279. // unmap here.
  4280. //
  4281. while (pMdlHead != NULL)
  4282. {
  4283. pMdlTmp = pMdlHead->Next;
  4284. UlFreeMdl( pMdlHead );
  4285. pMdlHead = pMdlTmp;
  4286. }
  4287. }
  4288. else
  4289. {
  4290. //
  4291. // It's a file run, free the Mdl.
  4292. //
  4293. UlpFreeFileMdlRun( pTracker, pMdlRun );
  4294. }
  4295. pMdlHead = pMdlNext;
  4296. pMdlRun++;
  4297. RunCount--;
  4298. }
  4299. } // UlpFreeMdlRuns
  4300. /***************************************************************************++
  4301. Routine Description:
  4302. Clean up the specified Read File MDL_RUN.
  4303. Arguments:
  4304. pTracker - Supplies the UL_CHUNK_TRACKER to clean up.
  4305. pMdlRun - Supplies the Read File MDL_RUN.
  4306. Return Value:
  4307. None.
  4308. --***************************************************************************/
  4309. VOID
  4310. UlpFreeFileMdlRun(
  4311. IN OUT PUL_CHUNK_TRACKER pTracker,
  4312. IN OUT PUL_MDL_RUN pMdlRun
  4313. )
  4314. {
  4315. NTSTATUS Status;
  4316. PUL_FILE_BUFFER pFileBuffer;
  4317. //
  4318. // Sanity check.
  4319. //
  4320. PAGED_CODE();
  4321. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  4322. //
  4323. // It should be a file run.
  4324. //
  4325. pFileBuffer = &pMdlRun->FileBuffer;
  4326. ASSERT( pFileBuffer->pFileCacheEntry );
  4327. Status = UlReadCompleteFileEntryFast( pFileBuffer );
  4328. if (!NT_SUCCESS(Status))
  4329. {
  4330. //
  4331. // Fast path failed, we'll need an IRP which has been pre-built.
  4332. // We need to do this synchronously as the read IRP can be used by
  4333. // the next UlpFreeFileMdlRun. UlReadCompleteFileEntry will complete
  4334. // synchronously if we set pCompletionRoutine to NULL.
  4335. //
  4336. pFileBuffer->pCompletionRoutine = NULL;
  4337. pFileBuffer->pContext = NULL;
  4338. Status = UlReadCompleteFileEntry(
  4339. pFileBuffer,
  4340. pTracker->pIrp
  4341. );
  4342. ASSERT( STATUS_SUCCESS == Status );
  4343. }
  4344. } // UlpFreeFileMdlRun
  4345. /***************************************************************************++
  4346. Routine Description:
  4347. Copy the data from the MDL chain starting from pMdl and send it to TDI.
  4348. Arguments:
  4349. pTracker - Supplies the tracker to send.
  4350. pMdl - Supplies the MDL chain to send.
  4351. Length - Supplies the total length of the MDL chain.
  4352. InitiateDisconnect - Supplies the disconnect flag passed to TDI.
  4353. RequestComplete - Supplies the request-complete flag passed to TDI.
  4354. Return Value:
  4355. NTSTATUS - Completion status.
  4356. --***************************************************************************/
  4357. NTSTATUS
  4358. UlpCopySend(
  4359. IN PUL_CHUNK_TRACKER pTracker,
  4360. IN PMDL pMdl,
  4361. IN ULONG Length,
  4362. IN BOOLEAN InitiateDisconnect,
  4363. IN BOOLEAN RequestComplete
  4364. )
  4365. {
  4366. PMDL pMdlCopied = NULL;
  4367. PUCHAR pDataCopied = NULL;
  4368. PUCHAR pData;
  4369. NTSTATUS Status;
  4370. //
  4371. // Sanity check.
  4372. //
  4373. PAGED_CODE();
  4374. ASSERT( pMdl );
  4375. ASSERT( g_UlMaxCopyThreshold == Length );
  4376. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  4377. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pTracker->pResponse ) );
  4378. //
  4379. // Allocate memory and MDL that can hold the whole incoming MDL chain.
  4380. //
  4381. pDataCopied = (PUCHAR) UL_ALLOCATE_POOL(
  4382. NonPagedPool,
  4383. Length,
  4384. UL_COPY_SEND_DATA_POOL_TAG
  4385. );
  4386. if (!pDataCopied)
  4387. {
  4388. Status = STATUS_INSUFFICIENT_RESOURCES;
  4389. goto end;
  4390. }
  4391. pMdlCopied = UlAllocateMdl(
  4392. pDataCopied,
  4393. Length,
  4394. FALSE,
  4395. FALSE,
  4396. NULL
  4397. );
  4398. if (!pMdlCopied)
  4399. {
  4400. Status = STATUS_INSUFFICIENT_RESOURCES;
  4401. goto end;
  4402. }
  4403. MmBuildMdlForNonPagedPool( pMdlCopied );
  4404. //
  4405. // Copy the data from the MDL chain starting pMdl to pMdlCopied.
  4406. //
  4407. while (pMdl)
  4408. {
  4409. pData = MmGetSystemAddressForMdlSafe(
  4410. pMdl,
  4411. LowPagePriority
  4412. );
  4413. if (!pData)
  4414. {
  4415. Status = STATUS_INSUFFICIENT_RESOURCES;
  4416. goto end;
  4417. }
  4418. RtlCopyMemory(
  4419. pDataCopied,
  4420. pData,
  4421. MmGetMdlByteCount(pMdl)
  4422. );
  4423. pDataCopied += MmGetMdlByteCount(pMdl);
  4424. pMdl = pMdl->Next;
  4425. }
  4426. //
  4427. // Send pMdlCopied if everything is ok so far.
  4428. //
  4429. Status = UlSendData(
  4430. pTracker->pHttpConnection->pConnection,
  4431. pMdlCopied,
  4432. Length,
  4433. UlpRestartCopySend,
  4434. pMdlCopied,
  4435. NULL,
  4436. NULL,
  4437. InitiateDisconnect,
  4438. RequestComplete
  4439. );
  4440. ASSERT( Status == STATUS_PENDING);
  4441. end:
  4442. //
  4443. // Return pending from here since we always complete the send
  4444. // inline in both error and success cases.
  4445. //
  4446. if (!NT_SUCCESS(Status))
  4447. {
  4448. if (pDataCopied)
  4449. {
  4450. UL_FREE_POOL( pDataCopied, UL_COPY_SEND_DATA_POOL_TAG );
  4451. }
  4452. if (pMdlCopied)
  4453. {
  4454. UlFreeMdl( pMdlCopied );
  4455. }
  4456. UlpRestartMdlSend( pTracker, Status, 0 );
  4457. }
  4458. else
  4459. {
  4460. UlpRestartMdlSend( pTracker, STATUS_SUCCESS, Length );
  4461. }
  4462. return STATUS_PENDING;
  4463. } // UlpCopySend
  4464. /***************************************************************************++
  4465. Routine Description:
  4466. Completion for the second half of a copy send.
  4467. Arguments:
  4468. pCompletionContext - Supplies an uninterpreted context value
  4469. as passed to the asynchronous API.
  4470. Status - Supplies the final completion status of the
  4471. asynchronous API.
  4472. Information - Optionally supplies additional information about
  4473. the completed operation, such as the number of bytes
  4474. transferred.
  4475. Return Value:
  4476. None
  4477. --***************************************************************************/
  4478. VOID
  4479. UlpRestartCopySend(
  4480. IN PVOID pCompletionContext,
  4481. IN NTSTATUS Status,
  4482. IN ULONG_PTR Information
  4483. )
  4484. {
  4485. PMDL pMdl = (PMDL) pCompletionContext;
  4486. UNREFERENCED_PARAMETER( Status );
  4487. UNREFERENCED_PARAMETER( Information );
  4488. UL_FREE_POOL(
  4489. MmGetMdlVirtualAddress( pMdl ),
  4490. UL_COPY_SEND_DATA_POOL_TAG
  4491. );
  4492. UlFreeMdl( pMdl );
  4493. } // UlpRestartCopySend
  4494. /***************************************************************************++
  4495. Routine Description:
  4496. Increments the current chunk pointer in the tracker and initializes
  4497. some of the "from file" related tracker fields if necessary.
  4498. Arguments:
  4499. pTracker - Supplies the UL_CHUNK_TRACKER to manipulate.
  4500. Return Value:
  4501. None.
  4502. --***************************************************************************/
  4503. VOID
  4504. UlpIncrementChunkPointer(
  4505. IN OUT PUL_INTERNAL_RESPONSE pResponse
  4506. )
  4507. {
  4508. PUL_INTERNAL_DATA_CHUNK pCurrentChunk;
  4509. //
  4510. // Bump the data chunk. If the request is still incomplete, then
  4511. // check the new current chunk. If it's "from file", then
  4512. // initialize the file offset & bytes remaining from the
  4513. // supplied byte range.
  4514. //
  4515. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pResponse ) );
  4516. ASSERT( pResponse->CurrentChunk == ULONG_MAX ||
  4517. pResponse->CurrentChunk < pResponse->ChunkCount );
  4518. if (ULONG_MAX == pResponse->CurrentChunk)
  4519. {
  4520. pResponse->CurrentChunk = 0;
  4521. }
  4522. else
  4523. {
  4524. pResponse->CurrentChunk++;
  4525. }
  4526. if (!IS_SEND_COMPLETE(pResponse))
  4527. {
  4528. pCurrentChunk = &pResponse->pDataChunks[pResponse->CurrentChunk];
  4529. if (IS_FROM_FILE_HANDLE(pCurrentChunk))
  4530. {
  4531. pResponse->FileOffset =
  4532. pCurrentChunk->FromFileHandle.ByteRange.StartingOffset;
  4533. pResponse->FileBytesRemaining =
  4534. pCurrentChunk->FromFileHandle.ByteRange.Length;
  4535. }
  4536. else
  4537. {
  4538. ASSERT( IS_FROM_MEMORY(pCurrentChunk) ||
  4539. IS_FROM_FRAGMENT_CACHE(pCurrentChunk) );
  4540. }
  4541. }
  4542. } // UlpIncrementChunkPointer
  4543. /***************************************************************************++
  4544. Routine Description:
  4545. Creates a cache entry for the given response. This routine actually
  4546. allocates the entry and partly initializes it. Then it allocates
  4547. a UL_CHUNK_TRACKER to keep track of filesystem reads.
  4548. Arguments:
  4549. pRequest - Supplies the initiating request.
  4550. pResponse - Supplies the generated response.
  4551. pProcess - UL_APP_POOL_PROCESS that is building this cache entry.
  4552. Flags - UlSendHttpResponse flags.
  4553. CachePolicy - Supplies the cache policy to be enforced on the cache entry.
  4554. pCompletionRoutine - Supplies the completion routine to be called after
  4555. entry is sent.
  4556. pCompletionContext - Supplies the completion context passed to
  4557. pCompletionRoutine.
  4558. Return Value:
  4559. NTSTATUS - Completion status.
  4560. --***************************************************************************/
  4561. NTSTATUS
  4562. UlpBuildCacheEntry(
  4563. IN PUL_INTERNAL_REQUEST pRequest,
  4564. IN PUL_INTERNAL_RESPONSE pResponse,
  4565. IN PUL_APP_POOL_PROCESS pProcess,
  4566. IN HTTP_CACHE_POLICY CachePolicy,
  4567. IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
  4568. IN PVOID pCompletionContext
  4569. )
  4570. {
  4571. NTSTATUS Status = STATUS_SUCCESS;
  4572. PUL_URI_CACHE_ENTRY pEntry = NULL;
  4573. PUL_CHUNK_TRACKER pTracker = NULL;
  4574. ULONG SpaceLength = 0;
  4575. USHORT LogDataLength = 0;
  4576. ULONG ContentLength;
  4577. PUL_LOG_DATA_BUFFER pLogData = NULL;
  4578. ULONG CookedUrlLength = pRequest->CookedUrl.Length;
  4579. LONG AbsPathLength;
  4580. ULONG i;
  4581. //
  4582. // Sanity check.
  4583. //
  4584. PAGED_CODE();
  4585. if (HttpCachePolicyTimeToLive == CachePolicy.Policy &&
  4586. 0 == CachePolicy.SecondsToLive )
  4587. {
  4588. //
  4589. // A TTL of 0 seconds doesn't make sense. Bail out.
  4590. //
  4591. return STATUS_INVALID_PARAMETER;
  4592. }
  4593. ContentLength =
  4594. (ULONG) (pResponse->ResponseLength - pResponse->HeaderLength);
  4595. //
  4596. // See if we need to store any logging data. If so, we need to
  4597. // calculate the required cache space for the logging data.
  4598. //
  4599. if (pResponse->pLogData)
  4600. {
  4601. pLogData = pResponse->pLogData;
  4602. ASSERT( IS_VALID_LOG_DATA_BUFFER( pLogData ) );
  4603. LogDataLength = UlComputeCachedLogDataLength( pLogData );
  4604. }
  4605. if (pRequest->ConfigInfo.SiteUrlType == HttpUrlSite_NamePlusIP)
  4606. {
  4607. //
  4608. // RoutingToken + AbsPath goes to cache.
  4609. //
  4610. ASSERT( DIFF(pRequest->CookedUrl.pAbsPath - pRequest->CookedUrl.pUrl) > 0 );
  4611. CookedUrlLength -=
  4612. DIFF(pRequest->CookedUrl.pAbsPath - pRequest->CookedUrl.pUrl)
  4613. * sizeof(WCHAR);
  4614. CookedUrlLength += pRequest->CookedUrl.RoutingTokenLength;
  4615. }
  4616. SpaceLength =
  4617. CookedUrlLength + sizeof(WCHAR) + // Space for Hash key +
  4618. pResponse->ETagLength + // ETag +
  4619. pResponse->ContentEncodingLength + // Content-Encoding +
  4620. LogDataLength; // Logging
  4621. UlTrace(URI_CACHE, (
  4622. "Http!UlpBuildCacheEntry allocating UL_URI_CACHE_ENTRY, "
  4623. "0x%x bytes of data\n"
  4624. " Url.Length = 0x%x, aligned Length = 0x%x\n"
  4625. " ContentLength=0x%x, %d\n"
  4626. "\n",
  4627. SpaceLength,
  4628. CookedUrlLength,
  4629. ALIGN_UP(CookedUrlLength, WCHAR),
  4630. ContentLength,
  4631. ContentLength
  4632. ));
  4633. //
  4634. // Allocate a cache entry.
  4635. //
  4636. pEntry = UlAllocateCacheEntry(
  4637. SpaceLength,
  4638. ContentLength + pResponse->HeaderLength
  4639. );
  4640. if (pEntry)
  4641. {
  4642. //
  4643. // Initialize the entry.
  4644. //
  4645. if (pRequest->ConfigInfo.SiteUrlType == HttpUrlSite_NamePlusIP)
  4646. {
  4647. AbsPathLength =
  4648. pRequest->CookedUrl.Length
  4649. - (DIFF(pRequest->CookedUrl.pAbsPath
  4650. - pRequest->CookedUrl.pUrl) * sizeof(WCHAR));
  4651. ASSERT( AbsPathLength > 0 );
  4652. UlInitCacheEntry(
  4653. pEntry,
  4654. pRequest->CookedUrl.RoutingHash,
  4655. (ULONG) AbsPathLength,
  4656. pRequest->CookedUrl.pAbsPath,
  4657. NULL,
  4658. pRequest->CookedUrl.pRoutingToken,
  4659. pRequest->CookedUrl.RoutingTokenLength
  4660. );
  4661. }
  4662. else
  4663. {
  4664. UlInitCacheEntry(
  4665. pEntry,
  4666. pRequest->CookedUrl.Hash,
  4667. pRequest->CookedUrl.Length,
  4668. pRequest->CookedUrl.pUrl,
  4669. pRequest->CookedUrl.pAbsPath,
  4670. NULL,
  4671. 0
  4672. );
  4673. }
  4674. //
  4675. // Copy the ETag from the response (for If-* headers).
  4676. //
  4677. pEntry->pETag =
  4678. (((PUCHAR) pEntry->UriKey.pUri) + // Start of URI +
  4679. pEntry->UriKey.Length + sizeof(WCHAR)); // Length of URI
  4680. pEntry->ETagLength = pResponse->ETagLength;
  4681. if (pEntry->ETagLength)
  4682. {
  4683. RtlCopyMemory(
  4684. pEntry->pETag,
  4685. pResponse->pETag,
  4686. pEntry->ETagLength
  4687. );
  4688. }
  4689. //
  4690. // Capture Content-Encoding so we can verify the Accept-Encoding header
  4691. // on requests.
  4692. //
  4693. pEntry->pContentEncoding = pEntry->pETag + pEntry->ETagLength;
  4694. pEntry->ContentEncodingLength = pResponse->ContentEncodingLength;
  4695. if (pEntry->ContentEncodingLength)
  4696. {
  4697. RtlCopyMemory(
  4698. pEntry->pContentEncoding,
  4699. pResponse->pContentEncoding,
  4700. pEntry->ContentEncodingLength
  4701. );
  4702. }
  4703. //
  4704. // Capture Content-Type so we can verify the Accept: header on requests.
  4705. //
  4706. if (pResponse->ContentType.Type &&
  4707. pResponse->ContentType.SubType )
  4708. {
  4709. RtlCopyMemory(
  4710. &pEntry->ContentType,
  4711. &pResponse->ContentType,
  4712. sizeof(UL_CONTENT_TYPE)
  4713. );
  4714. }
  4715. //
  4716. // Get the System Time of the Date: header (for If-* headers).
  4717. //
  4718. pEntry->CreationTime.QuadPart = pResponse->CreationTime.QuadPart;
  4719. pEntry->ContentLengthSpecified = pResponse->ContentLengthSpecified;
  4720. pEntry->StatusCode = pResponse->StatusCode;
  4721. pEntry->Verb = pRequest->Verb;
  4722. pEntry->CachePolicy = CachePolicy;
  4723. if (CachePolicy.Policy == HttpCachePolicyTimeToLive)
  4724. {
  4725. ASSERT( 0 != CachePolicy.SecondsToLive );
  4726. KeQuerySystemTime( &pEntry->ExpirationTime );
  4727. if (CachePolicy.SecondsToLive > C_SECS_PER_YEAR)
  4728. {
  4729. //
  4730. // Maximum TTL is 1 year.
  4731. //
  4732. pEntry->CachePolicy.SecondsToLive = C_SECS_PER_YEAR;
  4733. }
  4734. //
  4735. // Convert seconds to 100 nanosecond intervals (x * 10^7).
  4736. //
  4737. pEntry->ExpirationTime.QuadPart +=
  4738. pEntry->CachePolicy.SecondsToLive * C_NS_TICKS_PER_SEC;
  4739. }
  4740. else
  4741. {
  4742. pEntry->ExpirationTime.QuadPart = 0;
  4743. }
  4744. //
  4745. // Capture the Config Info from the request.
  4746. //
  4747. ASSERT( IS_VALID_URL_CONFIG_GROUP_INFO( &pRequest->ConfigInfo ) );
  4748. UlConfigGroupInfoDeepCopy(
  4749. &pRequest->ConfigInfo,
  4750. &pEntry->ConfigInfo
  4751. );
  4752. //
  4753. // Remember who created us.
  4754. //
  4755. pEntry->pProcess = pProcess;
  4756. //
  4757. // Generate the content and fixed headers.
  4758. //
  4759. if (NULL == pEntry->pMdl)
  4760. {
  4761. Status = STATUS_NO_MEMORY;
  4762. goto cleanup;
  4763. }
  4764. pEntry->HeaderLength = pResponse->HeaderLength;
  4765. if (FALSE == UlCacheEntrySetData(
  4766. pEntry, // Dest CacheEntry
  4767. pResponse->pHeaders, // Buffer to copy
  4768. pResponse->HeaderLength, // Length to copy
  4769. ContentLength // Offset in Dest MDL
  4770. ))
  4771. {
  4772. Status = STATUS_INSUFFICIENT_RESOURCES;
  4773. goto cleanup;
  4774. }
  4775. //
  4776. // Generate the content body.
  4777. //
  4778. pEntry->ContentLength = ContentLength;
  4779. //
  4780. // Copy over the log data.
  4781. //
  4782. if (pLogData)
  4783. {
  4784. //
  4785. // There may be no field to save in the cache entry but the logging
  4786. // might still be enabled for those fields we generate later such as
  4787. // date and time.
  4788. //
  4789. pEntry->LoggingEnabled = TRUE;
  4790. pEntry->LogDataLength = LogDataLength;
  4791. pEntry->pLogData = pEntry->pContentEncoding +
  4792. pEntry->ContentEncodingLength;
  4793. //
  4794. // Copy over the partially complete log line excluding the date and
  4795. // time fields to the cache entry. Also remember the length of the
  4796. // data.
  4797. //
  4798. UlCopyCachedLogData(
  4799. pLogData,
  4800. LogDataLength,
  4801. pEntry
  4802. );
  4803. }
  4804. UlTrace(URI_CACHE, (
  4805. "Http!UlpBuildCacheEntry\n"
  4806. " entry = %p\n"
  4807. " pUri = %p '%ls'\n"
  4808. " pMdl = %p (%d bytes)\n"
  4809. " pETag = %p\n"
  4810. " pContentEncoding = %p\n"
  4811. " pLogData = %p\n"
  4812. " end = %p\n",
  4813. pEntry,
  4814. pEntry->UriKey.pUri, pEntry->UriKey.pUri,
  4815. pEntry->pMdl, pEntry->ContentLength + pEntry->HeaderLength,
  4816. pEntry->pETag,
  4817. pEntry->pContentEncoding,
  4818. pEntry->pLogData,
  4819. ((PUCHAR)pEntry->UriKey.pUri) + SpaceLength
  4820. ));
  4821. //
  4822. // Completion info.
  4823. //
  4824. pResponse->pCompletionRoutine = pCompletionRoutine;
  4825. pResponse->pCompletionContext = pCompletionContext;
  4826. pTracker = UlpAllocateChunkTracker(
  4827. UlTrackerTypeBuildUriEntry,
  4828. 0,
  4829. pResponse->MaxFileSystemStackSize,
  4830. TRUE,
  4831. pRequest->pHttpConn,
  4832. pResponse
  4833. );
  4834. if (pTracker)
  4835. {
  4836. //
  4837. // Initialize the first chunk for the cache build.
  4838. //
  4839. UlpIncrementChunkPointer( pResponse );
  4840. //
  4841. // Init the tracker's BuildInfo.
  4842. //
  4843. pTracker->BuildInfo.pUriEntry = pEntry;
  4844. pTracker->BuildInfo.Offset = 0;
  4845. RtlZeroMemory(
  4846. &pTracker->BuildInfo.FileBuffer,
  4847. sizeof(pTracker->BuildInfo.FileBuffer)
  4848. );
  4849. //
  4850. // Skip over the header chunks because we already
  4851. // got that stuff.
  4852. //
  4853. for (i = 0; i < HEADER_CHUNK_COUNT; i++)
  4854. {
  4855. ASSERT( !IS_SEND_COMPLETE( pResponse ) );
  4856. UlpIncrementChunkPointer( pResponse );
  4857. }
  4858. //
  4859. // Let the worker do the dirty work, no reason to queue off,
  4860. // it will queue the first time it needs to do I/O.
  4861. //
  4862. UlpBuildCacheEntryWorker( &pTracker->WorkItem );
  4863. Status = STATUS_PENDING;
  4864. }
  4865. else
  4866. {
  4867. Status = STATUS_INSUFFICIENT_RESOURCES;
  4868. }
  4869. }
  4870. else
  4871. {
  4872. Status = STATUS_NO_MEMORY;
  4873. }
  4874. cleanup:
  4875. UlTrace(URI_CACHE, (
  4876. "Http!UlpBuildCacheEntry Status = %x, pEntry = %x\n",
  4877. Status,
  4878. pEntry
  4879. ));
  4880. if (!NT_SUCCESS(Status))
  4881. {
  4882. if (pEntry)
  4883. {
  4884. UlFreeCacheEntry( pEntry );
  4885. }
  4886. if (pTracker)
  4887. {
  4888. UL_FREE_POOL_WITH_SIG( pTracker, UL_CHUNK_TRACKER_POOL_TAG );
  4889. }
  4890. }
  4891. return Status;
  4892. } // UlpBuildCacheEntry
  4893. /***************************************************************************++
  4894. Routine Description:
  4895. Worker routine for managing an in-progress UlpBuildCacheEntry().
  4896. This routine iterates through all the chunks in the response
  4897. and copies the data into the cache entry.
  4898. Arguments:
  4899. pWorkItem - Supplies a pointer to the work item queued. This should
  4900. point to the WORK_ITEM structure embedded in a UL_CHUNK_TRACKER.
  4901. Return Value:
  4902. None.
  4903. --***************************************************************************/
  4904. VOID
  4905. UlpBuildCacheEntryWorker(
  4906. IN PUL_WORK_ITEM pWorkItem
  4907. )
  4908. {
  4909. PUL_CHUNK_TRACKER pTracker;
  4910. NTSTATUS Status;
  4911. PUL_INTERNAL_DATA_CHUNK pCurrentChunk;
  4912. PUCHAR pBuffer;
  4913. ULONG BufferLength;
  4914. PUL_FILE_BUFFER pFileBuffer;
  4915. PUL_INTERNAL_RESPONSE pResponse;
  4916. //
  4917. // Sanity check.
  4918. //
  4919. PAGED_CODE();
  4920. pTracker = CONTAINING_RECORD(
  4921. pWorkItem,
  4922. UL_CHUNK_TRACKER,
  4923. WorkItem
  4924. );
  4925. UlTrace(URI_CACHE, (
  4926. "Http!UlpBuildCacheEntryWorker: tracker %p\n",
  4927. pTracker
  4928. ));
  4929. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  4930. pResponse = pTracker->pResponse;
  4931. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pResponse ) );
  4932. Status = STATUS_SUCCESS;
  4933. while (TRUE)
  4934. {
  4935. //
  4936. // Capture the current chunk pointer, then check for end of
  4937. // response.
  4938. //
  4939. pCurrentChunk = &pResponse->pDataChunks[pResponse->CurrentChunk];
  4940. if (IS_SEND_COMPLETE(pResponse))
  4941. {
  4942. ASSERT( Status == STATUS_SUCCESS );
  4943. break;
  4944. }
  4945. //
  4946. // Determine the chunk type.
  4947. //
  4948. if (IS_FROM_MEMORY(pCurrentChunk))
  4949. {
  4950. //
  4951. // It's from a locked-down memory buffer. Since these
  4952. // are always handled in-line (never pended) we can
  4953. // go ahead and adjust the current chunk pointer in the
  4954. // tracker.
  4955. //
  4956. UlpIncrementChunkPointer( pResponse );
  4957. //
  4958. // Ignore empty buffers.
  4959. //
  4960. if (pCurrentChunk->FromMemory.BufferLength == 0)
  4961. {
  4962. continue;
  4963. }
  4964. //
  4965. // Copy the incoming memory.
  4966. //
  4967. ASSERT( pCurrentChunk->FromMemory.pMdl->Next == NULL );
  4968. pBuffer = (PUCHAR) MmGetMdlVirtualAddress(
  4969. pCurrentChunk->FromMemory.pMdl
  4970. );
  4971. BufferLength = MmGetMdlByteCount( pCurrentChunk->FromMemory.pMdl );
  4972. UlTrace(LARGE_MEM, (
  4973. "Http!UlpBuildCacheEntryWorker: "
  4974. "copy range %u (%x) -> %u\n",
  4975. pTracker->BuildInfo.Offset,
  4976. BufferLength,
  4977. pTracker->BuildInfo.Offset + BufferLength
  4978. ));
  4979. if (FALSE == UlCacheEntrySetData(
  4980. pTracker->BuildInfo.pUriEntry,
  4981. pBuffer,
  4982. BufferLength,
  4983. pTracker->BuildInfo.Offset
  4984. ))
  4985. {
  4986. Status = STATUS_INSUFFICIENT_RESOURCES;
  4987. break;
  4988. }
  4989. pTracker->BuildInfo.Offset += BufferLength;
  4990. ASSERT( pTracker->BuildInfo.Offset <=
  4991. pTracker->BuildInfo.pUriEntry->ContentLength );
  4992. }
  4993. else
  4994. {
  4995. //
  4996. // It's a filesystem MDL.
  4997. //
  4998. ASSERT( IS_FROM_FILE_HANDLE( pCurrentChunk ) );
  4999. //
  5000. // Ignore empty file ranges.
  5001. //
  5002. if (pCurrentChunk->FromFileHandle.ByteRange.Length.QuadPart == 0)
  5003. {
  5004. UlpIncrementChunkPointer( pResponse );
  5005. continue;
  5006. }
  5007. //
  5008. // Do the read.
  5009. //
  5010. pFileBuffer = &pTracker->BuildInfo.FileBuffer;
  5011. pFileBuffer->pFileCacheEntry =
  5012. &pCurrentChunk->FromFileHandle.FileCacheEntry;
  5013. pFileBuffer->FileOffset = pResponse->FileOffset;
  5014. pFileBuffer->Length =
  5015. MIN(
  5016. (ULONG) pResponse->FileBytesRemaining.QuadPart,
  5017. g_UlMaxBytesPerRead
  5018. );
  5019. pFileBuffer->pCompletionRoutine = UlpRestartCacheMdlRead;
  5020. pFileBuffer->pContext = pTracker;
  5021. Status = UlReadFileEntry(
  5022. pFileBuffer,
  5023. pTracker->pIrp
  5024. );
  5025. break;
  5026. }
  5027. }
  5028. //
  5029. // Did everything complete?
  5030. //
  5031. if (Status != STATUS_PENDING)
  5032. {
  5033. //
  5034. // Yep, complete the response.
  5035. //
  5036. UlpCompleteCacheBuild( pTracker, Status );
  5037. }
  5038. } // UlpBuildCacheEntryWorker
  5039. /***************************************************************************++
  5040. Routine Description:
  5041. Completion handler for MDL READ IRPs used for reading file data.
  5042. Arguments:
  5043. pDeviceObject - Supplies the device object for the IRP being
  5044. completed.
  5045. pIrp - Supplies the IRP being completed.
  5046. pContext - Supplies the context associated with this request.
  5047. This is actually a PUL_CHUNK_TRACKER.
  5048. Return Value:
  5049. NTSTATUS - STATUS_SUCCESS if IO should continue processing this
  5050. IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
  5051. this IRP.
  5052. --***************************************************************************/
  5053. NTSTATUS
  5054. UlpRestartCacheMdlRead(
  5055. IN PDEVICE_OBJECT pDeviceObject,
  5056. IN PIRP pIrp,
  5057. IN PVOID pContext
  5058. )
  5059. {
  5060. PUL_CHUNK_TRACKER pTracker = (PUL_CHUNK_TRACKER) pContext;
  5061. UNREFERENCED_PARAMETER( pDeviceObject );
  5062. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  5063. pTracker->IoStatus = pIrp->IoStatus;
  5064. UlpQueueResponseWorkItem(
  5065. &pTracker->WorkItem,
  5066. UlpCacheMdlReadCompleteWorker,
  5067. pTracker->pResponse->SyncRead
  5068. );
  5069. return STATUS_MORE_PROCESSING_REQUIRED;
  5070. } // UlpRestartCacheMdlRead
  5071. /***************************************************************************++
  5072. Routine Description:
  5073. The worker routine for UlpRestartCacheMdlRead since UlpRestartCacheMdlRead
  5074. can be called at DISPATH_LEVEL but the cache entry is from PagedPool.
  5075. Arguments:
  5076. pWorkItem - Supplies the work item embedded in UL_CHUNK_TRACKER.
  5077. Return Value:
  5078. None.
  5079. --***************************************************************************/
  5080. VOID
  5081. UlpCacheMdlReadCompleteWorker(
  5082. IN PUL_WORK_ITEM pWorkItem
  5083. )
  5084. {
  5085. NTSTATUS Status;
  5086. NTSTATUS TempStatus;
  5087. PUL_CHUNK_TRACKER pTracker;
  5088. PMDL pMdl;
  5089. PUCHAR pData;
  5090. ULONG DataLen;
  5091. PUL_FILE_BUFFER pFileBuffer;
  5092. PAGED_CODE();
  5093. pTracker = CONTAINING_RECORD(
  5094. pWorkItem,
  5095. UL_CHUNK_TRACKER,
  5096. WorkItem
  5097. );
  5098. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  5099. UlTrace(URI_CACHE, (
  5100. "Http!UlpCacheMdlReadCompleteWorker: tracker %p, status %x info %Iu\n",
  5101. pTracker,
  5102. pTracker->IoStatus.Status,
  5103. pTracker->IoStatus.Information
  5104. ));
  5105. Status = pTracker->IoStatus.Status;
  5106. if (NT_SUCCESS(Status))
  5107. {
  5108. //
  5109. // Copy read data into the cache buffer.
  5110. //
  5111. pMdl = pTracker->BuildInfo.FileBuffer.pMdl;
  5112. while (pMdl)
  5113. {
  5114. //
  5115. // The MDL chain returned by CacheManager is only guranteed to be
  5116. // locked but not mapped so we have to map before using them.
  5117. // However, there is no need to unmap after using the MDLs as
  5118. // IRP_MN_COMPLETE calls MmUnlockPages which automatically unmaps
  5119. // MDLs if they are mapped into system space.
  5120. //
  5121. pData = MmGetSystemAddressForMdlSafe(
  5122. pMdl,
  5123. LowPagePriority
  5124. );
  5125. if (!pData)
  5126. {
  5127. Status = STATUS_INSUFFICIENT_RESOURCES;
  5128. break;
  5129. }
  5130. DataLen = MmGetMdlByteCount( pMdl );
  5131. UlTrace(LARGE_MEM, (
  5132. "Http!UlpRestartCacheMdlRead: "
  5133. "copy range %u (%x) -> %u\n",
  5134. pTracker->BuildInfo.Offset,
  5135. DataLen,
  5136. pTracker->BuildInfo.Offset + DataLen
  5137. ));
  5138. if (FALSE == UlCacheEntrySetData(
  5139. pTracker->BuildInfo.pUriEntry,
  5140. pData,
  5141. DataLen,
  5142. pTracker->BuildInfo.Offset
  5143. ))
  5144. {
  5145. Status = STATUS_INSUFFICIENT_RESOURCES;
  5146. break;
  5147. }
  5148. pTracker->BuildInfo.Offset += DataLen;
  5149. ASSERT( pTracker->BuildInfo.Offset <=
  5150. pTracker->BuildInfo.pUriEntry->ContentLength );
  5151. pMdl = pMdl->Next;
  5152. }
  5153. //
  5154. // Free the MDLs.
  5155. //
  5156. pFileBuffer = &pTracker->BuildInfo.FileBuffer;
  5157. if (NT_SUCCESS(Status))
  5158. {
  5159. pFileBuffer->pCompletionRoutine = UlpRestartCacheMdlFree;
  5160. pFileBuffer->pContext = pTracker;
  5161. Status = UlReadCompleteFileEntry(
  5162. pFileBuffer,
  5163. pTracker->pIrp
  5164. );
  5165. ASSERT( STATUS_PENDING == Status );
  5166. }
  5167. else
  5168. {
  5169. pFileBuffer->pCompletionRoutine = NULL;
  5170. pFileBuffer->pContext = NULL;
  5171. TempStatus = UlReadCompleteFileEntry(
  5172. pFileBuffer,
  5173. pTracker->pIrp
  5174. );
  5175. ASSERT( STATUS_SUCCESS == TempStatus );
  5176. }
  5177. }
  5178. if (!NT_SUCCESS(Status))
  5179. {
  5180. UlpCompleteCacheBuild( pTracker, Status );
  5181. }
  5182. } // UlpRestartCacheMdlRead
  5183. /***************************************************************************++
  5184. Routine Description:
  5185. Completion handler for MDL free IRPs used after reading/copying file data.
  5186. Arguments:
  5187. pDeviceObject - Supplies the device object for the IRP being
  5188. completed.
  5189. pIrp - Supplies the IRP being completed.
  5190. pContext - Supplies the context associated with this request.
  5191. This is actually a PUL_CHUNK_TRACKER.
  5192. Return Value:
  5193. NTSTATUS - STATUS_SUCCESS if IO should continue processing this
  5194. IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
  5195. this IRP.
  5196. --***************************************************************************/
  5197. NTSTATUS
  5198. UlpRestartCacheMdlFree(
  5199. IN PDEVICE_OBJECT pDeviceObject,
  5200. IN PIRP pIrp,
  5201. IN PVOID pContext
  5202. )
  5203. {
  5204. NTSTATUS Status;
  5205. PUL_CHUNK_TRACKER pTracker;
  5206. PUL_INTERNAL_RESPONSE pResponse;
  5207. UNREFERENCED_PARAMETER( pDeviceObject );
  5208. pTracker = (PUL_CHUNK_TRACKER) pContext;
  5209. UlTrace(URI_CACHE, (
  5210. "Http!UlpRestartCacheMdlFree: tracker %p, status %x info %Iu\n",
  5211. pTracker,
  5212. pIrp->IoStatus.Status,
  5213. pIrp->IoStatus.Information
  5214. ));
  5215. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  5216. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pTracker->pResponse ) );
  5217. pResponse = pTracker->pResponse;
  5218. Status = pIrp->IoStatus.Status;
  5219. if (NT_SUCCESS(Status))
  5220. {
  5221. //
  5222. // Update the file offset & bytes remaining. If we've
  5223. // finished this file chunk (bytes remaining is now zero)
  5224. // then advance to the next chunk.
  5225. //
  5226. pResponse->FileOffset.QuadPart += pIrp->IoStatus.Information;
  5227. pResponse->FileBytesRemaining.QuadPart -= pIrp->IoStatus.Information;
  5228. if (pResponse->FileBytesRemaining.QuadPart == 0 )
  5229. {
  5230. UlpIncrementChunkPointer( pResponse );
  5231. }
  5232. //
  5233. // Go back into the loop if there's more to read
  5234. //
  5235. if (IS_SEND_COMPLETE(pResponse))
  5236. {
  5237. UlpCompleteCacheBuild( pTracker, Status );
  5238. }
  5239. else
  5240. {
  5241. UlpQueueResponseWorkItem(
  5242. &pTracker->WorkItem,
  5243. UlpBuildCacheEntryWorker,
  5244. pTracker->pResponse->SyncRead
  5245. );
  5246. }
  5247. }
  5248. else
  5249. {
  5250. //
  5251. // MDL free should never fail.
  5252. //
  5253. ASSERT( FALSE );
  5254. UlpCompleteCacheBuild( pTracker, Status );
  5255. }
  5256. return STATUS_MORE_PROCESSING_REQUIRED;
  5257. } // UlpRestartCacheMdlFree
  5258. /***************************************************************************++
  5259. Routine Description:
  5260. This routine gets called when we finish building a cache entry.
  5261. Arguments:
  5262. pTracker - Supplies the tracker to complete.
  5263. Status - Supplies the completion status.
  5264. Return Value:
  5265. None.
  5266. --***************************************************************************/
  5267. VOID
  5268. UlpCompleteCacheBuild(
  5269. IN PUL_CHUNK_TRACKER pTracker,
  5270. IN NTSTATUS Status
  5271. )
  5272. {
  5273. UlTrace(URI_CACHE, (
  5274. "Http!UlpCompleteCacheBuild: tracker %p, status %08lx\n",
  5275. pTracker,
  5276. Status
  5277. ));
  5278. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  5279. pTracker->IoStatus.Status = Status;
  5280. UL_CALL_PASSIVE(
  5281. &pTracker->WorkItem,
  5282. UlpCompleteCacheBuildWorker
  5283. );
  5284. } // UlpCompleteCacheBuild
  5285. /***************************************************************************++
  5286. Routine Description:
  5287. Called when we finish building a cache entry. If the entry was
  5288. built successfully, we send the response down the wire.
  5289. Arguments:
  5290. pWorkItem - Supplies a pointer to the work item queued. This should
  5291. point to the WORK_ITEM structure embedded in a UL_CHUNK_TRACKER.
  5292. Return Value:
  5293. None.
  5294. --***************************************************************************/
  5295. VOID
  5296. UlpCompleteCacheBuildWorker(
  5297. IN PUL_WORK_ITEM pWorkItem
  5298. )
  5299. {
  5300. PUL_CHUNK_TRACKER pTracker;
  5301. PUL_URI_CACHE_ENTRY pUriCacheEntry;
  5302. PUL_HTTP_CONNECTION pHttpConnection;
  5303. PUL_COMPLETION_ROUTINE pCompletionRoutine;
  5304. PVOID pCompletionContext;
  5305. ULONG Flags;
  5306. PUL_LOG_DATA_BUFFER pLogData;
  5307. LONGLONG BytesToSend;
  5308. NTSTATUS Status;
  5309. //
  5310. // Sanity check.
  5311. //
  5312. PAGED_CODE();
  5313. pTracker = CONTAINING_RECORD(
  5314. pWorkItem,
  5315. UL_CHUNK_TRACKER,
  5316. WorkItem
  5317. );
  5318. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  5319. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pTracker->pResponse ) );
  5320. pUriCacheEntry = pTracker->BuildInfo.pUriEntry;
  5321. ASSERT( IS_VALID_URI_CACHE_ENTRY( pUriCacheEntry ) );
  5322. pHttpConnection = pTracker->pHttpConnection;
  5323. Flags = pTracker->pResponse->Flags;
  5324. pCompletionRoutine = pTracker->pResponse->pCompletionRoutine;
  5325. pCompletionContext = pTracker->pResponse->pCompletionContext;
  5326. Status = pTracker->IoStatus.Status;
  5327. //
  5328. // Save the logging data pointer before releasing the tracker and
  5329. // its response pointer.
  5330. //
  5331. pLogData = pTracker->pResponse->pLogData;
  5332. if (pLogData)
  5333. {
  5334. //
  5335. // To prevent SendResponse to free our log buffer.
  5336. //
  5337. pTracker->pResponse->pLogData = NULL;
  5338. //
  5339. // Give the sign that this log data buffer is ready and later
  5340. // there's no need to refresh its content from cache again.
  5341. //
  5342. pLogData->Flags.CacheAndSendResponse = TRUE;
  5343. }
  5344. if (NT_SUCCESS(Status))
  5345. {
  5346. //
  5347. // Try to put the entry into the hash table.
  5348. //
  5349. UlAddCacheEntry( pUriCacheEntry );
  5350. //
  5351. // Start the MinBytesPerSecond timer, since the data length
  5352. // is in the UL_URI_CACHE_ENTRY.
  5353. //
  5354. BytesToSend = pUriCacheEntry->ContentLength +
  5355. pUriCacheEntry->HeaderLength;
  5356. UlSetMinBytesPerSecondTimer(
  5357. &pHttpConnection->TimeoutInfo,
  5358. BytesToSend
  5359. );
  5360. //
  5361. // Grab the connection lock because UlpSendCacheEntry assumes you
  5362. // have it.
  5363. //
  5364. UlAcquirePushLockExclusive( &pHttpConnection->PushLock );
  5365. //
  5366. // Send the cache entry.
  5367. //
  5368. // We never pipeline requests for build & send path. We will defer
  5369. // resume parsing until send completion.
  5370. //
  5371. Status = UlpSendCacheEntry(
  5372. pHttpConnection,
  5373. Flags,
  5374. pUriCacheEntry,
  5375. pCompletionRoutine,
  5376. pCompletionContext,
  5377. pLogData,
  5378. UlResumeParsingOnSendCompletion
  5379. );
  5380. //
  5381. // Get rid of the cache entry if it didn't work.
  5382. //
  5383. if (!NT_SUCCESS(Status))
  5384. {
  5385. UlCheckinUriCacheEntry( pUriCacheEntry );
  5386. }
  5387. //
  5388. // Done with the connection lock.
  5389. //
  5390. UlReleasePushLockExclusive( &pHttpConnection->PushLock );
  5391. }
  5392. //
  5393. // We assume the ownership of the original log buffer.
  5394. // If send didn't go through, then we have to clean it
  5395. // up here.
  5396. //
  5397. if (pLogData && !NT_SUCCESS(Status))
  5398. {
  5399. UlDestroyLogDataBuffer( pLogData );
  5400. }
  5401. //
  5402. // Free the read tracker. Do this after calling UlpSendCacheEntry
  5403. // as to hold onto the HTTP connection reference.
  5404. //
  5405. UlpFreeChunkTracker( &pTracker->WorkItem );
  5406. //
  5407. // If it's not STATUS_PENDING for some reason, complete the request.
  5408. //
  5409. if (Status != STATUS_PENDING && pCompletionRoutine != NULL)
  5410. {
  5411. (pCompletionRoutine)(
  5412. pCompletionContext,
  5413. Status,
  5414. 0
  5415. );
  5416. }
  5417. } // UlpCompleteCacheBuildWorker
  5418. /***************************************************************************++
  5419. Routine Description:
  5420. Sends a cache entry down the wire.
  5421. The logging related part of this function below, surely depends on
  5422. the fact that pCompletionContext will be null if this is called for
  5423. pure cache hits (in other words from UlSendCachedResponse) otherwise
  5424. pointer to Irp will be passed down as the pCompletionContext.
  5425. Arguments:
  5426. pHttpConnection - Supplies the UL_HTTP_CONNECTION to send the cached
  5427. response.
  5428. Flags - HTTP_SEND_RESPONSE flags.
  5429. pUriCacheEntry - Supplies the cache entry to send the response.
  5430. pCompletionRoutine - Supplies the completion routine to call upon
  5431. send completion.
  5432. pCompletionContext - Passed to pCompletionRoutine.
  5433. pLogData - Supplies the log data (only in the build cache case).
  5434. ResumeParsingType - Tells whether resume parsing happens on send completion
  5435. or after send but before send completion.
  5436. Return Value:
  5437. NTSTATUS - Completion status.
  5438. --***************************************************************************/
  5439. NTSTATUS
  5440. UlpSendCacheEntry(
  5441. PUL_HTTP_CONNECTION pHttpConnection,
  5442. ULONG Flags,
  5443. PUL_URI_CACHE_ENTRY pUriCacheEntry,
  5444. PUL_COMPLETION_ROUTINE pCompletionRoutine,
  5445. PVOID pCompletionContext,
  5446. PUL_LOG_DATA_BUFFER pLogData,
  5447. UL_RESUME_PARSING_TYPE ResumeParsingType
  5448. )
  5449. {
  5450. NTSTATUS Status = STATUS_SUCCESS;
  5451. PUL_FULL_TRACKER pTracker;
  5452. CCHAR SendIrpStackSize;
  5453. UL_CONN_HDR ConnHeader;
  5454. ULONG VarHeaderGenerated;
  5455. ULONG ContentLengthStringLength;
  5456. UCHAR ContentLength[MAX_ULONGLONG_STR];
  5457. LARGE_INTEGER CreationTime;
  5458. //
  5459. // Sanity check.
  5460. //
  5461. PAGED_CODE();
  5462. ASSERT( UL_IS_VALID_HTTP_CONNECTION( pHttpConnection ) );
  5463. ASSERT( IS_VALID_URI_CACHE_ENTRY( pUriCacheEntry ) );
  5464. ASSERT( UlDbgPushLockOwnedExclusive( &pHttpConnection->PushLock ) );
  5465. UlTrace(URI_CACHE, (
  5466. "Http!UlpSendCacheEntry(httpconn %p, flags %x, uri %p, ...)\n",
  5467. pHttpConnection,
  5468. Flags,
  5469. pUriCacheEntry
  5470. ));
  5471. //
  5472. // Init vars so we can cleanup correctly if we jump to the end.
  5473. //
  5474. pTracker = NULL;
  5475. //
  5476. // Make sure we're still connected.
  5477. //
  5478. if (pHttpConnection->UlconnDestroyed)
  5479. {
  5480. Status = STATUS_CONNECTION_ABORTED;
  5481. goto cleanup;
  5482. }
  5483. ASSERT( pHttpConnection->pRequest );
  5484. if (!pUriCacheEntry->ContentLengthSpecified &&
  5485. UlNeedToGenerateContentLength(
  5486. pUriCacheEntry->Verb,
  5487. pUriCacheEntry->StatusCode,
  5488. Flags
  5489. ))
  5490. {
  5491. //
  5492. // Autogenerate a Content-Length header.
  5493. //
  5494. PCHAR pEnd = UlStrPrintUlonglong(
  5495. (PCHAR) ContentLength,
  5496. (ULONGLONG) pUriCacheEntry->ContentLength,
  5497. ANSI_NULL
  5498. );
  5499. ContentLengthStringLength = DIFF(pEnd - (PCHAR) ContentLength);
  5500. }
  5501. else
  5502. {
  5503. //
  5504. // Either we cannot or do not need to autogenerate a
  5505. // Content-Length header.
  5506. //
  5507. ContentLength[0] = ANSI_NULL;
  5508. ContentLengthStringLength = 0;
  5509. }
  5510. ConnHeader = UlChooseConnectionHeader(
  5511. pHttpConnection->pRequest->Version,
  5512. (BOOLEAN) (Flags & HTTP_SEND_RESPONSE_FLAG_DISCONNECT)
  5513. );
  5514. //
  5515. // Create a cache tracker.
  5516. //
  5517. SendIrpStackSize =
  5518. pHttpConnection->pConnection->ConnectionObject.pDeviceObject->StackSize;
  5519. if (SendIrpStackSize > DEFAULT_MAX_IRP_STACK_SIZE)
  5520. {
  5521. pTracker = UlpAllocateCacheTracker( SendIrpStackSize );
  5522. }
  5523. else
  5524. {
  5525. pTracker = pHttpConnection->pRequest->pTracker;
  5526. }
  5527. if (pTracker)
  5528. {
  5529. //
  5530. // Init the tracker.
  5531. //
  5532. UL_REFERENCE_HTTP_CONNECTION( pHttpConnection );
  5533. UL_REFERENCE_INTERNAL_REQUEST( pHttpConnection->pRequest );
  5534. pTracker->pUriEntry = pUriCacheEntry;
  5535. pTracker->pHttpConnection = pHttpConnection;
  5536. pTracker->pRequest = pHttpConnection->pRequest;
  5537. pTracker->pCompletionRoutine = pCompletionRoutine;
  5538. pTracker->pCompletionContext = pCompletionContext;
  5539. pTracker->Flags = Flags;
  5540. pTracker->pLogData = NULL;
  5541. //
  5542. // Resume parse if this cache response comes from
  5543. // the UlpCompleteCacheBuildWorker path, or if we are at maximum
  5544. // pipelined requests.
  5545. //
  5546. pTracker->ResumeParsingType = ResumeParsingType;
  5547. //
  5548. // Build MDLs for send.
  5549. //
  5550. ASSERT( pUriCacheEntry->pMdl != NULL );
  5551. MmInitializeMdl(
  5552. pTracker->pMdlFixedHeaders,
  5553. (PCHAR) MmGetMdlVirtualAddress(pUriCacheEntry->pMdl) +
  5554. pUriCacheEntry->ContentLength,
  5555. pUriCacheEntry->HeaderLength
  5556. );
  5557. IoBuildPartialMdl(
  5558. pUriCacheEntry->pMdl,
  5559. pTracker->pMdlFixedHeaders,
  5560. (PCHAR) MmGetMdlVirtualAddress(pUriCacheEntry->pMdl) +
  5561. pUriCacheEntry->ContentLength,
  5562. pUriCacheEntry->HeaderLength
  5563. );
  5564. //
  5565. // Generate the variable headers and build a MDL for them.
  5566. //
  5567. UlGenerateVariableHeaders(
  5568. ConnHeader,
  5569. TRUE,
  5570. ContentLength,
  5571. ContentLengthStringLength,
  5572. pTracker->pAuxiliaryBuffer,
  5573. &VarHeaderGenerated,
  5574. &CreationTime
  5575. );
  5576. ASSERT( VarHeaderGenerated <= g_UlMaxVariableHeaderSize );
  5577. ASSERT( VarHeaderGenerated <= pTracker->AuxilaryBufferLength );
  5578. pTracker->pMdlVariableHeaders->ByteCount = VarHeaderGenerated;
  5579. pTracker->pMdlFixedHeaders->Next = pTracker->pMdlVariableHeaders;
  5580. //
  5581. // Build a MDL for the body.
  5582. //
  5583. if (pUriCacheEntry->ContentLength)
  5584. {
  5585. MmInitializeMdl(
  5586. pTracker->pMdlContent,
  5587. MmGetMdlVirtualAddress(pUriCacheEntry->pMdl),
  5588. pUriCacheEntry->ContentLength
  5589. );
  5590. IoBuildPartialMdl(
  5591. pUriCacheEntry->pMdl,
  5592. pTracker->pMdlContent,
  5593. MmGetMdlVirtualAddress(pUriCacheEntry->pMdl),
  5594. pUriCacheEntry->ContentLength
  5595. );
  5596. pTracker->pMdlVariableHeaders->Next = pTracker->pMdlContent;
  5597. }
  5598. else
  5599. {
  5600. pTracker->pMdlVariableHeaders->Next = NULL;
  5601. }
  5602. //
  5603. // Check whether we have to log this cache hit or not.
  5604. //
  5605. if (pUriCacheEntry->LoggingEnabled)
  5606. {
  5607. //
  5608. // If logging data is provided use it rather than allocating
  5609. // a new one. Because the log buffer already gets allocated
  5610. // for build & send cache hits.
  5611. //
  5612. if (pLogData)
  5613. {
  5614. ASSERT( pCompletionContext != NULL );
  5615. pTracker->pLogData = pLogData;
  5616. }
  5617. //
  5618. // Or else, LogData will get allocated when send completion
  5619. // happens just before we do the logging.
  5620. //
  5621. }
  5622. //
  5623. // Go go go!
  5624. //
  5625. UL_QUEUE_WORK_ITEM(
  5626. &pTracker->WorkItem,
  5627. UlpSendCacheEntryWorker
  5628. );
  5629. Status = STATUS_PENDING;
  5630. }
  5631. else
  5632. {
  5633. Status = STATUS_INSUFFICIENT_RESOURCES;
  5634. }
  5635. cleanup:
  5636. //
  5637. // Clean up the tracker if we don't need it.
  5638. //
  5639. if (!NT_SUCCESS(Status))
  5640. {
  5641. if (pTracker)
  5642. {
  5643. UL_DEREFERENCE_INTERNAL_REQUEST( pTracker->pRequest );
  5644. pTracker->pRequest = NULL;
  5645. UL_DEREFERENCE_HTTP_CONNECTION( pTracker->pHttpConnection );
  5646. pTracker->pHttpConnection = NULL;
  5647. UlpFreeCacheTracker( pTracker );
  5648. }
  5649. }
  5650. UlTrace(URI_CACHE, (
  5651. "Http!UlpSendCacheEntry status = %08x\n",
  5652. Status
  5653. ));
  5654. return Status;
  5655. } // UlpSendCacheEntry
  5656. /***************************************************************************++
  5657. Routine Description:
  5658. Called to send a cached response. This is done in a worker to avoid
  5659. holding the connection resource for too long. This also prevents
  5660. recursion if we keep on hitting pipelined cache-hit responses.
  5661. Arguments:
  5662. pWorkItem - Supplies a pointer to the work item queued. This should
  5663. point to the WORK_ITEM structure embedded in a UL_FULL_TRACKER.
  5664. Return Value:
  5665. None.
  5666. --***************************************************************************/
  5667. VOID
  5668. UlpSendCacheEntryWorker(
  5669. IN PUL_WORK_ITEM pWorkItem
  5670. )
  5671. {
  5672. PUL_FULL_TRACKER pTracker;
  5673. NTSTATUS Status;
  5674. PUL_HTTP_CONNECTION pHttpConnection = NULL;
  5675. BOOLEAN ResumeParsing = FALSE;
  5676. BOOLEAN InDisconnect = FALSE;
  5677. //
  5678. // Sanity check.
  5679. //
  5680. PAGED_CODE();
  5681. pTracker = CONTAINING_RECORD(
  5682. pWorkItem,
  5683. UL_FULL_TRACKER,
  5684. WorkItem
  5685. );
  5686. ASSERT( IS_VALID_FULL_TRACKER( pTracker ) );
  5687. UlTrace(URI_CACHE, (
  5688. "Http!UlpSendCacheEntryWorker(pTracker %p)\n",
  5689. pTracker
  5690. ));
  5691. //
  5692. // Take an extra reference for the pHttpConnection if we are going to
  5693. // resume parsing inline because UlpCompleteSendCacheEntry can be
  5694. // called when UlSendData returns.
  5695. //
  5696. if (UlResumeParsingOnLastSend == pTracker->ResumeParsingType)
  5697. {
  5698. pHttpConnection = pTracker->pHttpConnection;
  5699. UL_REFERENCE_HTTP_CONNECTION( pHttpConnection );
  5700. ResumeParsing = TRUE;
  5701. }
  5702. if (pTracker->Flags & HTTP_SEND_RESPONSE_FLAG_DISCONNECT)
  5703. {
  5704. InDisconnect = TRUE;
  5705. }
  5706. Status = UlSendData(
  5707. pTracker->pHttpConnection->pConnection,
  5708. pTracker->pMdlFixedHeaders,
  5709. MmGetMdlByteCount(pTracker->pMdlFixedHeaders) +
  5710. MmGetMdlByteCount(pTracker->pMdlVariableHeaders) +
  5711. pTracker->pUriEntry->ContentLength,
  5712. UlpCompleteSendCacheEntry,
  5713. pTracker,
  5714. pTracker->pSendIrp,
  5715. &pTracker->IrpContext,
  5716. InDisconnect,
  5717. TRUE
  5718. );
  5719. if (ResumeParsing)
  5720. {
  5721. //
  5722. // pHttpConnection is safe to use since we have already added an
  5723. // extra reference for it.
  5724. //
  5725. if (NT_SUCCESS(Status))
  5726. {
  5727. UlResumeParsing( pHttpConnection, TRUE, InDisconnect );
  5728. }
  5729. UL_DEREFERENCE_HTTP_CONNECTION( pHttpConnection );
  5730. }
  5731. if (!NT_SUCCESS(Status))
  5732. {
  5733. if (pTracker->pLogData)
  5734. {
  5735. UlDestroyLogDataBuffer( pTracker->pLogData );
  5736. pTracker->pLogData = NULL;
  5737. }
  5738. UL_DEREFERENCE_INTERNAL_REQUEST( pTracker->pRequest );
  5739. UL_DEREFERENCE_HTTP_CONNECTION( pTracker->pHttpConnection );
  5740. UlpFreeCacheTracker( pTracker );
  5741. }
  5742. } // UlpSendCacheEntryWorker
  5743. /***************************************************************************++
  5744. Routine Description:
  5745. Called when we finish sending data to the client. Just queues to
  5746. a worker that runs at passive level.
  5747. Arguments:
  5748. pCompletionContext - Supplies a pointer to UL_FULL_TRACKER.
  5749. Status - Status of the send.
  5750. Information - Bytes transferred.
  5751. Return Value:
  5752. None.
  5753. --***************************************************************************/
  5754. VOID
  5755. UlpCompleteSendCacheEntry(
  5756. IN PVOID pCompletionContext,
  5757. IN NTSTATUS Status,
  5758. IN ULONG_PTR Information
  5759. )
  5760. {
  5761. PUL_FULL_TRACKER pTracker;
  5762. pTracker = (PUL_FULL_TRACKER) pCompletionContext;
  5763. pTracker->IoStatus.Status = Status;
  5764. pTracker->IoStatus.Information = Information;
  5765. UlTrace(URI_CACHE, (
  5766. "UlpCompleteSendCacheEntry: "
  5767. "tracker=%p, status = %x, transferred %d bytes\n",
  5768. pTracker,
  5769. Status,
  5770. (LONG) Information
  5771. ));
  5772. IF_DEBUG(LOGBYTES)
  5773. {
  5774. TIME_FIELDS RcvdTimeFields;
  5775. RtlTimeToTimeFields( &pTracker->pRequest->TimeStamp, &RcvdTimeFields );
  5776. UlTrace(LOGBYTES,
  5777. ("Http!UlpCompleteSendCacheEntry : [Rcvd @ %02d:%02d:%02d] "
  5778. "Bytes %010I64u Status %08lx\n",
  5779. RcvdTimeFields.Hour,
  5780. RcvdTimeFields.Minute,
  5781. RcvdTimeFields.Second,
  5782. (ULONGLONG) pTracker->IoStatus.Information,
  5783. Status
  5784. ));
  5785. }
  5786. UL_QUEUE_WORK_ITEM(
  5787. &pTracker->WorkItem,
  5788. UlpCompleteSendCacheEntryWorker
  5789. );
  5790. } // UlpCompleteSendCacheEntry
  5791. /***************************************************************************++
  5792. Routine Description:
  5793. Called when we finish sending cached data to the client. This routine
  5794. frees the UL_FULL_TRACKER, and calls the completion routine originally
  5795. passed to UlCacheAndSendResponse.
  5796. Arguments:
  5797. pWorkItem - Supplies a pointer to the work item queued. This should
  5798. point to the WORK_ITEM structure embedded in a UL_FULL_TRACKER.
  5799. Return Value:
  5800. None.
  5801. --***************************************************************************/
  5802. VOID
  5803. UlpCompleteSendCacheEntryWorker(
  5804. IN PUL_WORK_ITEM pWorkItem
  5805. )
  5806. {
  5807. PUL_FULL_TRACKER pTracker;
  5808. PUL_HTTP_CONNECTION pHttpConnection;
  5809. PUL_INTERNAL_REQUEST pRequest;
  5810. ULONG Flags;
  5811. PUL_URI_CACHE_ENTRY pUriCacheEntry;
  5812. NTSTATUS Status;
  5813. KIRQL OldIrql;
  5814. BOOLEAN ResumeParsing;
  5815. HTTP_VERB RequestVerb;
  5816. USHORT ResponseStatusCode;
  5817. BOOLEAN FromCache;
  5818. //
  5819. // Sanity check.
  5820. //
  5821. PAGED_CODE();
  5822. pTracker = CONTAINING_RECORD(
  5823. pWorkItem,
  5824. UL_FULL_TRACKER,
  5825. WorkItem
  5826. );
  5827. UlTrace(URI_CACHE, (
  5828. "Http!UlpCompleteSendCacheEntryWorker(pTracker %p)\n",
  5829. pTracker
  5830. ));
  5831. //
  5832. // Pull context out of the tracker.
  5833. //
  5834. pHttpConnection = pTracker->pHttpConnection;
  5835. pRequest = pTracker->pRequest;
  5836. RequestVerb = pTracker->RequestVerb;
  5837. ResponseStatusCode = pTracker->ResponseStatusCode;
  5838. Flags = pTracker->Flags;
  5839. pUriCacheEntry = pTracker->pUriEntry;
  5840. ASSERT( UL_IS_VALID_HTTP_CONNECTION( pHttpConnection ) );
  5841. ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pRequest ) );
  5842. ASSERT( IS_VALID_URI_CACHE_ENTRY( pUriCacheEntry ) );
  5843. Status = pTracker->IoStatus.Status;
  5844. //
  5845. // If the send failed, then initiate an *abortive* disconnect.
  5846. //
  5847. if (!NT_SUCCESS(Status))
  5848. {
  5849. UlTrace(URI_CACHE, (
  5850. "Http!UlpCompleteSendCacheEntryWorker(pTracker %p) "
  5851. "Closing connection\n",
  5852. pTracker
  5853. ));
  5854. UlCloseConnection(
  5855. pHttpConnection->pConnection,
  5856. TRUE,
  5857. NULL,
  5858. NULL
  5859. );
  5860. }
  5861. //
  5862. // Stop MinBytesPerSecond timer and start Connection Idle timer.
  5863. //
  5864. UlLockTimeoutInfo(
  5865. &pHttpConnection->TimeoutInfo,
  5866. &OldIrql
  5867. );
  5868. UlResetConnectionTimer(
  5869. &pHttpConnection->TimeoutInfo,
  5870. TimerMinBytesPerSecond
  5871. );
  5872. UlSetConnectionTimer(
  5873. &pHttpConnection->TimeoutInfo,
  5874. TimerConnectionIdle
  5875. );
  5876. UlUnlockTimeoutInfo(
  5877. &pHttpConnection->TimeoutInfo,
  5878. OldIrql
  5879. );
  5880. UlEvaluateTimerState(
  5881. &pHttpConnection->TimeoutInfo
  5882. );
  5883. //
  5884. // Unmap the FixedHeaders and Content MDLs if necessary.
  5885. //
  5886. if (pTracker->pMdlFixedHeaders->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
  5887. {
  5888. MmUnmapLockedPages(
  5889. pTracker->pMdlFixedHeaders->MappedSystemVa,
  5890. pTracker->pMdlFixedHeaders
  5891. );
  5892. }
  5893. if (pTracker->pMdlVariableHeaders->Next &&
  5894. (pTracker->pMdlContent->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA))
  5895. {
  5896. ASSERT( pTracker->pMdlVariableHeaders->Next == pTracker->pMdlContent );
  5897. MmUnmapLockedPages(
  5898. pTracker->pMdlContent->MappedSystemVa,
  5899. pTracker->pMdlContent
  5900. );
  5901. }
  5902. //
  5903. // Do the logging before cleaning up the tracker.
  5904. //
  5905. if (pUriCacheEntry->LoggingEnabled)
  5906. {
  5907. if (pUriCacheEntry->BinaryLogged)
  5908. {
  5909. UlRawLogHttpCacheHit( pTracker );
  5910. }
  5911. else
  5912. {
  5913. UlLogHttpCacheHit( pTracker );
  5914. }
  5915. }
  5916. //
  5917. // Unlink the request from process if we are done with all sends.
  5918. //
  5919. if (0 == (pTracker->Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA) &&
  5920. 0 == pRequest->ContentLength &&
  5921. 0 == pRequest->Chunked &&
  5922. pRequest->ConfigInfo.pAppPool)
  5923. {
  5924. ASSERT( pRequest->SentLast );
  5925. UlUnlinkRequestFromProcess(
  5926. pRequest->ConfigInfo.pAppPool,
  5927. pRequest
  5928. );
  5929. }
  5930. //
  5931. // Kick the parser back into action, if resume parsing is set in tracker.
  5932. //
  5933. ResumeParsing = (BOOLEAN)
  5934. (UlResumeParsingOnSendCompletion == pTracker->ResumeParsingType);
  5935. //
  5936. // Invoke completion routine.
  5937. //
  5938. if (pTracker->pCompletionRoutine != NULL)
  5939. {
  5940. (pTracker->pCompletionRoutine)(
  5941. pTracker->pCompletionContext,
  5942. Status,
  5943. pTracker->IoStatus.Information
  5944. );
  5945. }
  5946. //
  5947. // Clean up tracker.
  5948. //
  5949. FromCache = (BOOLEAN) (pTracker->pCompletionContext == NULL);
  5950. UlpFreeCacheTracker( pTracker );
  5951. //
  5952. // Deref the cache entry.
  5953. //
  5954. UlCheckinUriCacheEntry( pUriCacheEntry );
  5955. //
  5956. // Deref the internal request.
  5957. //
  5958. UL_DEREFERENCE_INTERNAL_REQUEST( pRequest );
  5959. if (ResumeParsing)
  5960. {
  5961. UlTrace(HTTP_IO, (
  5962. "http!UlpCompleteSendCacheEntryWorker(pHttpConn = %p), "
  5963. "RequestVerb=%d, ResponseStatusCode=%hu\n",
  5964. pHttpConnection,
  5965. RequestVerb,
  5966. ResponseStatusCode
  5967. ));
  5968. UlResumeParsing(
  5969. pHttpConnection,
  5970. FromCache,
  5971. (BOOLEAN) (Flags & HTTP_SEND_RESPONSE_FLAG_DISCONNECT)
  5972. );
  5973. }
  5974. //
  5975. // Deref the HTTP connection.
  5976. //
  5977. UL_DEREFERENCE_HTTP_CONNECTION( pHttpConnection );
  5978. } // UlpCompleteSendCacheEntryWorker
  5979. /***************************************************************************++
  5980. Routine Description:
  5981. Allocates a non-paged UL_FULL_TRACKER used as context for sending
  5982. cached content to the client.
  5983. CODEWORK: this routine should probably do all tracker init.
  5984. Arguments:
  5985. SendIrpStackSize - Size of the stack for the send IRP.
  5986. Return Values:
  5987. Either a pointer to a UL_FULL_TRACKER, or NULL if it couldn't be made.
  5988. --***************************************************************************/
  5989. PUL_FULL_TRACKER
  5990. UlpAllocateCacheTracker(
  5991. IN CCHAR SendIrpStackSize
  5992. )
  5993. {
  5994. PUL_FULL_TRACKER pTracker;
  5995. USHORT SendIrpSize;
  5996. ULONG CacheTrackerSize;
  5997. //
  5998. // Sanity check.
  5999. //
  6000. PAGED_CODE();
  6001. ASSERT( SendIrpStackSize > DEFAULT_MAX_IRP_STACK_SIZE );
  6002. SendIrpSize = (USHORT) ALIGN_UP(IoSizeOfIrp(SendIrpStackSize), PVOID);
  6003. //
  6004. // No need to allocate space for the entire auxiliary buffer in this
  6005. // case since this is one-time deal only.
  6006. //
  6007. CacheTrackerSize = ALIGN_UP(sizeof(UL_FULL_TRACKER), PVOID) +
  6008. SendIrpSize +
  6009. g_UlMaxVariableHeaderSize +
  6010. g_UlFixedHeadersMdlLength +
  6011. g_UlVariableHeadersMdlLength +
  6012. g_UlContentMdlLength;
  6013. pTracker = (PUL_FULL_TRACKER) UL_ALLOCATE_POOL(
  6014. NonPagedPool,
  6015. CacheTrackerSize,
  6016. UL_FULL_TRACKER_POOL_TAG
  6017. );
  6018. if (pTracker)
  6019. {
  6020. pTracker->Signature = UL_FULL_TRACKER_POOL_TAG;
  6021. pTracker->FromLookaside = FALSE;
  6022. pTracker->FromRequest = FALSE;
  6023. pTracker->AuxilaryBufferLength = g_UlMaxVariableHeaderSize;
  6024. pTracker->RequestVerb = HttpVerbInvalid;
  6025. pTracker->ResponseStatusCode = 200; // OK
  6026. UlInitializeFullTrackerPool( pTracker, SendIrpStackSize );
  6027. }
  6028. UlTrace( URI_CACHE, (
  6029. "Http!UlpAllocateCacheTracker: tracker %p\n",
  6030. pTracker
  6031. ));
  6032. return pTracker;
  6033. } // UlpAllocateCacheTracker
  6034. /***************************************************************************++
  6035. Routine Description:
  6036. Frees a UL_FULL_TRACKER.
  6037. Arguments:
  6038. pTracker - Specifies the UL_FULL_TRACKER to free.
  6039. Return Values:
  6040. None.
  6041. --***************************************************************************/
  6042. VOID
  6043. UlpFreeCacheTracker(
  6044. IN PUL_FULL_TRACKER pTracker
  6045. )
  6046. {
  6047. UlTrace(URI_CACHE, (
  6048. "Http!UlpFreeCacheTracker: tracker %p\n",
  6049. pTracker
  6050. ));
  6051. ASSERT( IS_VALID_FULL_TRACKER( pTracker ) );
  6052. pTracker->pHttpConnection = NULL;
  6053. if (pTracker->FromRequest == FALSE)
  6054. {
  6055. if (pTracker->FromLookaside)
  6056. {
  6057. UlPplFreeFullTracker( pTracker );
  6058. }
  6059. else
  6060. {
  6061. UL_FREE_POOL_WITH_SIG( pTracker, UL_FULL_TRACKER_POOL_TAG );
  6062. }
  6063. }
  6064. } // UlpFreeCacheTracker