Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

6239 lines
170 KiB

  1. /*++
  2. Copyright (c) 1998-2001 Microsoft Corporation
  3. Module Name:
  4. sendresponse.cxx
  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. --*/
  48. #include "precomp.h"
  49. #include "iiscnfg.h"
  50. #include "sendresponsep.h"
  51. //
  52. // Private globals.
  53. //
  54. #ifdef ALLOC_PRAGMA
  55. #pragma alloc_text( PAGE, UlCaptureHttpResponse )
  56. #pragma alloc_text( PAGE, UlPrepareHttpResponse )
  57. #pragma alloc_text( PAGE, UlCleanupHttpResponse )
  58. #pragma alloc_text( PAGE, UlpSendHttpResponseWorker )
  59. #pragma alloc_text( PAGE, UlpSendCompleteWorker )
  60. #pragma alloc_text( PAGE, UlpFreeMdlRuns )
  61. #pragma alloc_text( PAGE, UlSendCachedResponse )
  62. #pragma alloc_text( PAGE, UlCacheAndSendResponse )
  63. #pragma alloc_text( PAGE, UlpBuildCacheEntry )
  64. #pragma alloc_text( PAGE, UlpBuildCacheEntryWorker )
  65. #pragma alloc_text( PAGE, UlpBuildBuildTrackerWorker )
  66. #pragma alloc_text( PAGE, UlpCompleteCacheBuildWorker )
  67. #pragma alloc_text( PAGE, UlpSendCacheEntry )
  68. #pragma alloc_text( PAGE, UlpAllocateCacheTracker )
  69. #pragma alloc_text( PAGE, UlpFreeCacheTracker )
  70. #pragma alloc_text( PAGE, UlpAllocateLockedMdl )
  71. #pragma alloc_text( PAGE, UlpInitializeAndLockMdl )
  72. #endif // ALLOC_PRAGMA
  73. #if 0
  74. NOT PAGEABLE -- UlSendHttpResponse
  75. NOT PAGEABLE -- UlReferenceHttpResponse
  76. NOT PAGEABLE -- UlDereferenceHttpResponse
  77. NOT PAGEABLE -- UlpDestroyCapturedResponse
  78. NOT PAGEABLE -- UlpAllocateChunkTracker
  79. NOT PAGEABLE -- UlpFreeChunkTracker
  80. NOT PAGEABLE -- UlpCompleteSendRequest
  81. NOT PAGEABLE -- UlpRestartMdlRead
  82. NOT PAGEABLE -- UlpRestartMdlReadComplete
  83. NOT PAGEABLE -- UlpRestartMdlSend
  84. NOT PAGEABLE -- UlpIncrementChunkPointer
  85. NOT PAGEABLE -- UlpRestartCacheMdlRead
  86. NOT PAGEABLE -- UlpRestartCacheMdlFree
  87. NOT PAGEABLE -- UlpIssueFileChunkIo
  88. NOT PAGEABLE -- UlpCompleteCacheBuild
  89. NOT PAGEABLE -- UlpCompleteSendCacheEntry
  90. NOT PAGEABLE -- UlpCheckCacheControlHeaders
  91. NOT PAGEABLE -- UlpCompleteSendRequestWorker
  92. NOT PAGEABLE -- UlpCompleteSendCacheEntryWorker
  93. NOT PAGEABLE -- UlpFreeLockedMdl
  94. NOT PAGEABLE -- UlpIsAcceptHeaderOk
  95. NOT PAGEABLE -- UlpGetTypeAndSubType
  96. #endif
  97. //
  98. // Public functions.
  99. //
  100. /***************************************************************************++
  101. Routine Description:
  102. Sends an HTTP response on the specified connection.
  103. Arguments:
  104. pConnection - Supplies the HTTP_CONNECTION to send the response on.
  105. pResponse - Supplies the HTTP response.
  106. pCompletionRoutine - Supplies a pointer to a completion routine to
  107. invoke after the send has completed.
  108. pCompletionContext - Supplies an uninterpreted context value for the
  109. completion routine.
  110. Return Value:
  111. NTSTATUS - Completion status.
  112. --***************************************************************************/
  113. NTSTATUS
  114. UlSendHttpResponse(
  115. IN PUL_INTERNAL_REQUEST pRequest,
  116. IN PUL_INTERNAL_RESPONSE pResponse,
  117. IN ULONG Flags,
  118. IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
  119. IN PVOID pCompletionContext
  120. )
  121. {
  122. NTSTATUS status;
  123. PUL_CHUNK_TRACKER pTracker;
  124. PUL_HTTP_CONNECTION pHttpConn;
  125. UL_CONN_HDR ConnHeader;
  126. BOOLEAN Disconnect;
  127. ULONG VarHeaderGenerated;
  128. ULONGLONG TotalResponseSize;
  129. ULONG contentLengthStringLength;
  130. UCHAR contentLength[MAX_ULONGLONG_STR];
  131. BOOLEAN CompleteEarly;
  132. pHttpConn = pRequest->pHttpConn;
  133. ASSERT( UL_IS_VALID_HTTP_CONNECTION( pHttpConn ) );
  134. //
  135. // do a little tracing
  136. //
  137. TRACE_TIME(
  138. pRequest->ConnectionId,
  139. pRequest->RequestId,
  140. TIME_ACTION_SEND_RESPONSE
  141. );
  142. //
  143. // Setup locals so we know how to cleanup on exit.
  144. //
  145. pTracker = NULL;
  146. CompleteEarly = FALSE;
  147. //
  148. // Tracker will keep a reference to the connection
  149. //
  150. UL_REFERENCE_HTTP_CONNECTION(pHttpConn);
  151. //
  152. // Should we close the connection?
  153. //
  154. Disconnect = FALSE;
  155. if ((Flags & HTTP_SEND_RESPONSE_FLAG_DISCONNECT) == 0)
  156. {
  157. if ((Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA) != HTTP_SEND_RESPONSE_FLAG_MORE_DATA)
  158. {
  159. //
  160. // No more data is coming, should we disconnect?
  161. //
  162. if ( UlCheckDisconnectInfo(pRequest) ) {
  163. Disconnect = TRUE;
  164. Flags |= HTTP_SEND_RESPONSE_FLAG_DISCONNECT;
  165. }
  166. }
  167. }
  168. else
  169. {
  170. //
  171. // Caller is forcing a disconnect.
  172. //
  173. Disconnect = TRUE;
  174. }
  175. //
  176. // how big is the response? (keep track for early complete)
  177. //
  178. TotalResponseSize = pResponse->ResponseLength;
  179. //
  180. // figure out what space we need for variable headers
  181. //
  182. if ((pResponse->HeaderLength > 0) && // response (not entity body)
  183. !pResponse->ContentLengthSpecified && // app didn't provide content length
  184. !pResponse->ChunkedSpecified && // app didn't generate a chunked response
  185. UlNeedToGenerateContentLength(
  186. pRequest->Verb,
  187. pResponse->StatusCode,
  188. Flags
  189. ))
  190. {
  191. //
  192. // Autogenerate a content-length header.
  193. //
  194. PCHAR pszEnd = UlStrPrintUlonglong(
  195. (PCHAR) contentLength,
  196. pResponse->ResponseLength - pResponse->HeaderLength,
  197. '\0');
  198. contentLengthStringLength = DIFF(pszEnd - (PCHAR) contentLength);
  199. }
  200. else
  201. {
  202. //
  203. // Either we cannot or do not need to autogenerate a
  204. // content-length header.
  205. //
  206. contentLength[0] = '\0';
  207. contentLengthStringLength = 0;
  208. }
  209. ConnHeader = UlChooseConnectionHeader( pRequest->Version, Disconnect );
  210. //
  211. // Allocate and initialize a tracker for this request.
  212. //
  213. pTracker =
  214. UlpAllocateChunkTracker(
  215. UlTrackerTypeSend,
  216. pHttpConn->pConnection->ConnectionObject.pDeviceObject->StackSize,
  217. pResponse->MaxFileSystemStackSize,
  218. pHttpConn,
  219. Flags,
  220. pRequest,
  221. pResponse,
  222. pCompletionRoutine,
  223. pCompletionContext
  224. );
  225. if (pTracker == NULL)
  226. {
  227. status = STATUS_INSUFFICIENT_RESOURCES;
  228. goto cleanup;
  229. }
  230. UlpInitMdlRuns( pTracker );
  231. //
  232. // generate var headers, and init the second chunk
  233. //
  234. if (pResponse->HeaderLength)
  235. {
  236. UlGenerateVariableHeaders(
  237. ConnHeader,
  238. contentLength,
  239. contentLengthStringLength,
  240. pTracker->pVariableHeader,
  241. &VarHeaderGenerated,
  242. &pResponse->CreationTime
  243. );
  244. ASSERT( VarHeaderGenerated <= g_UlMaxVariableHeaderSize );
  245. pTracker->VariableHeaderLength = VarHeaderGenerated;
  246. //
  247. // increment total size
  248. //
  249. TotalResponseSize += VarHeaderGenerated;
  250. //
  251. // build an mdl for it
  252. //
  253. pResponse->pDataChunks[1].ChunkType = HttpDataChunkFromMemory;
  254. pResponse->pDataChunks[1].FromMemory.BufferLength = VarHeaderGenerated;
  255. pResponse->pDataChunks[1].FromMemory.pMdl =
  256. UlAllocateMdl(
  257. pTracker->pVariableHeader, // VirtualAddress
  258. VarHeaderGenerated, // Length
  259. FALSE, // SecondaryBuffer
  260. FALSE, // ChargeQuota
  261. NULL // Irp
  262. );
  263. if (pResponse->pDataChunks[1].FromMemory.pMdl == NULL)
  264. {
  265. status = STATUS_INSUFFICIENT_RESOURCES;
  266. goto cleanup;
  267. }
  268. MmBuildMdlForNonPagedPool(
  269. pResponse->pDataChunks[1].FromMemory.pMdl
  270. );
  271. }
  272. //
  273. // see if we're supposed to do an early completion call
  274. //
  275. if (pResponse->CompleteIrpEarly &&
  276. (TotalResponseSize < MAX_BYTES_BUFFERED) &&
  277. (pResponse->ChunkCount < MAX_MDL_RUNS))
  278. {
  279. //
  280. // NULL out the tracker's completion routine so it
  281. // won't try to complete the request later.
  282. //
  283. pTracker->pCompletionRoutine = NULL;
  284. //
  285. // Remember that we're supposed to complete early
  286. // but don't do it until after we've initiated
  287. // the actual TCP send.
  288. //
  289. CompleteEarly = TRUE;
  290. }
  291. IF_DEBUG( SEND_RESPONSE )
  292. {
  293. KdPrint((
  294. "UlSendHttpResponse: tracker %p, response %p\n",
  295. pTracker,
  296. pResponse
  297. ));
  298. }
  299. //
  300. // Start MinKBSec timer, since we now know TotalResponseSize
  301. //
  302. UlSetMinKBSecTimer(
  303. &pHttpConn->TimeoutInfo,
  304. TotalResponseSize
  305. );
  306. //
  307. // RefCount the chunk tracker up for the UlpSendHttpResponseWorker.
  308. // It will DeRef it when it's done with the chunk tracker itself.
  309. //
  310. UL_REFERENCE_CHUNK_TRACKER( pTracker );
  311. //
  312. // Let the worker do the dirty work, no reason to queue off
  313. // it will queue the first time it needs to do blocking i/o
  314. //
  315. UlpSendHttpResponseWorker(&pTracker->WorkItem);
  316. //
  317. // If we're supposed to complete early, do it now.
  318. //
  319. if (CompleteEarly)
  320. {
  321. //
  322. // do the completion
  323. //
  324. UlpCompleteSendIrpEarly(
  325. pCompletionRoutine,
  326. pCompletionContext,
  327. STATUS_SUCCESS,
  328. TotalResponseSize
  329. );
  330. }
  331. //
  332. // Release the original reference on the chunk tracker. So that it get
  333. // freed up as soon as all the outstanding IO initiated by the
  334. // UlpSendHttpResponseWorker is complete. This dereference matches with
  335. // our allocation.
  336. //
  337. UL_DEREFERENCE_CHUNK_TRACKER( pTracker );
  338. return STATUS_PENDING;
  339. cleanup:
  340. IF_DEBUG( SEND_RESPONSE )
  341. {
  342. KdPrint((
  343. "UlSendHttpResponse: failure %08lx\n",
  344. status
  345. ));
  346. }
  347. ASSERT( !NT_SUCCESS(status) );
  348. if (pTracker != NULL)
  349. {
  350. //
  351. // Very early termination for the chunk tracker. RefCounting not
  352. // even started yet. ( Means UlpSendHttpResponseWorker hasn't been
  353. // called ). Therefore straight cleanup.
  354. //
  355. ASSERT( pTracker->RefCount == 1 );
  356. UlpFreeChunkTracker( pTracker );
  357. }
  358. //
  359. // Tracker doesn't have a reference after all..
  360. //
  361. UL_DEREFERENCE_HTTP_CONNECTION(pHttpConn);
  362. return status;
  363. } // UlSendHttpResponse
  364. /***************************************************************************++
  365. Routine Description:
  366. Captures a user-mode HTTP response and morphs it into a form suitable
  367. for kernel-mode.
  368. Arguments:
  369. pUserResponse - Supplies the user-mode HTTP response.
  370. Flags - Supplies zero or more UL_CAPTURE_* flags.
  371. pKernelResponse - Receives the captured response if successful.
  372. Return Value:
  373. NTSTATUS - Completion status.
  374. --***************************************************************************/
  375. NTSTATUS
  376. UlCaptureHttpResponse(
  377. IN PHTTP_RESPONSE pUserResponse OPTIONAL,
  378. IN PUL_INTERNAL_REQUEST pRequest,
  379. IN HTTP_VERSION Version,
  380. IN HTTP_VERB Verb,
  381. IN ULONG ChunkCount,
  382. IN PHTTP_DATA_CHUNK pUserDataChunks,
  383. IN UL_CAPTURE_FLAGS Flags,
  384. IN BOOLEAN CaptureCache,
  385. IN PHTTP_LOG_FIELDS_DATA pUserLogData OPTIONAL,
  386. OUT PUL_INTERNAL_RESPONSE *ppKernelResponse
  387. )
  388. {
  389. ULONG i;
  390. NTSTATUS Status = STATUS_SUCCESS;
  391. PUL_INTERNAL_RESPONSE pKeResponse = NULL;
  392. ULONG AuxBufferLength;
  393. ULONG CopiedBufferLength;
  394. ULONG UncopiedBufferLength;
  395. ULONGLONG FromFileLength;
  396. BOOLEAN CompleteEarly;
  397. PUCHAR pBuffer;
  398. USHORT FileLength;
  399. ULONG HeaderLength;
  400. ULONG SpaceLength;
  401. PUL_INTERNAL_DATA_CHUNK pKeDataChunks;
  402. BOOLEAN FromKernelMode;
  403. BOOLEAN IsFromLookaside;
  404. BOOLEAN BufferedSend = TRUE;
  405. ULONG KernelChunkCount;
  406. PHTTP_KNOWN_HEADER pETagHeader = NULL;
  407. //
  408. // Sanity check.
  409. //
  410. PAGED_CODE();
  411. ASSERT(pUserDataChunks != NULL || ChunkCount == 0);
  412. ASSERT(ppKernelResponse != NULL);
  413. __try
  414. {
  415. FromKernelMode = ((Flags & UlCaptureKernelMode) == UlCaptureKernelMode);
  416. //
  417. // ProbeTestForRead every buffer we will access
  418. //
  419. if (!FromKernelMode) {
  420. Status = UlpProbeHttpResponse(
  421. pUserResponse,
  422. ChunkCount,
  423. pUserDataChunks,
  424. Flags,
  425. pUserLogData
  426. );
  427. if (!NT_SUCCESS(Status))
  428. {
  429. goto end;
  430. }
  431. }
  432. //
  433. // figure out how much memory we need
  434. //
  435. Status = UlComputeFixedHeaderSize(
  436. Version,
  437. pUserResponse,
  438. &HeaderLength
  439. );
  440. if (!NT_SUCCESS(Status))
  441. {
  442. goto end;
  443. }
  444. UlpComputeChunkBufferSizes(
  445. ChunkCount, // number of chunks
  446. pUserDataChunks, // array of chunks
  447. Flags, // capture flags
  448. &AuxBufferLength,
  449. &CopiedBufferLength,
  450. &UncopiedBufferLength,
  451. &FromFileLength
  452. );
  453. //
  454. // check if we can still buffer sends
  455. //
  456. if ((Flags & UlCaptureCopyData) == 0 &&
  457. (CopiedBufferLength + pRequest->pHttpConn->SendBufferedBytes)
  458. > g_UlMaxSendBufferedBytes)
  459. {
  460. BufferedSend = FALSE;
  461. }
  462. //
  463. // see if we can complete the IRP early
  464. //
  465. if (BufferedSend && (UncopiedBufferLength + FromFileLength) == 0)
  466. {
  467. //
  468. // we've got all the data in the kernel, so
  469. // we can just tell user mode it's done.
  470. //
  471. CompleteEarly = TRUE;
  472. }
  473. else
  474. {
  475. //
  476. // we're using a handle or buffer from user space
  477. // so they have to wait for us to finish for real.
  478. //
  479. CompleteEarly = FALSE;
  480. }
  481. UlTrace(SEND_RESPONSE, (
  482. "Http!UlCaptureHttpResponse(pUserResponse = %p) "
  483. "CompleteEarly = %s\n"
  484. " ChunkCount = %d\n"
  485. " Flags = 0x%x\n"
  486. " AuxBufferLength = 0x%x\n"
  487. " UncopiedBufferLength = 0x%x\n"
  488. " FromFileLength = 0x%I64x\n",
  489. pUserResponse,
  490. CompleteEarly ? "TRUE" : "FALSE",
  491. ChunkCount,
  492. Flags,
  493. AuxBufferLength,
  494. UncopiedBufferLength,
  495. FromFileLength
  496. ));
  497. //
  498. // add two extra chunks for the headers (fixed & variable)
  499. //
  500. if (HeaderLength > 0)
  501. {
  502. KernelChunkCount = ChunkCount + HEADER_CHUNK_COUNT;
  503. }
  504. else
  505. {
  506. KernelChunkCount = ChunkCount;
  507. }
  508. //
  509. // compute the space needed for all of our structures
  510. //
  511. SpaceLength = (KernelChunkCount * sizeof(UL_INTERNAL_DATA_CHUNK))
  512. + ALIGN_UP(HeaderLength, sizeof(CHAR))
  513. + AuxBufferLength;
  514. //
  515. // Add space for ETag, if it exists.
  516. //
  517. if (CaptureCache &&
  518. pUserResponse &&
  519. pUserResponse->Headers.pKnownHeaders[HttpHeaderEtag].RawValueLength)
  520. {
  521. pETagHeader = &pUserResponse->Headers.pKnownHeaders[HttpHeaderEtag];
  522. SpaceLength += (pETagHeader->RawValueLength + sizeof(CHAR)); // Add space for NULL
  523. UlTrace(SEND_RESPONSE, (
  524. "ul!UlCaptureHttpResponse(pUserResponse = %p) \n"
  525. " ETag: %s \n"
  526. " Length: %d\n",
  527. pUserResponse,
  528. pETagHeader->pRawValue,
  529. pETagHeader->RawValueLength
  530. ));
  531. }
  532. //
  533. // allocate the internal response.
  534. //
  535. if (g_UlResponseBufferSize
  536. < (ALIGN_UP(sizeof(UL_INTERNAL_RESPONSE), PVOID) + SpaceLength)
  537. )
  538. {
  539. pKeResponse = UL_ALLOCATE_STRUCT_WITH_SPACE(
  540. NonPagedPool,
  541. UL_INTERNAL_RESPONSE,
  542. SpaceLength,
  543. UL_INTERNAL_RESPONSE_POOL_TAG
  544. );
  545. IsFromLookaside = FALSE;
  546. }
  547. else
  548. {
  549. pKeResponse = UlPplAllocateResponseBuffer();
  550. IsFromLookaside = TRUE;
  551. }
  552. if (pKeResponse == NULL)
  553. {
  554. Status = STATUS_INSUFFICIENT_RESOURCES;
  555. goto end;
  556. }
  557. //
  558. // Initialize the fixed fields in the response.
  559. //
  560. pKeResponse->IsFromLookaside = IsFromLookaside;
  561. pKeResponse->Signature = UL_INTERNAL_RESPONSE_POOL_TAG;
  562. pKeResponse->ReferenceCount = 1;
  563. pKeResponse->ChunkCount = KernelChunkCount;
  564. RtlZeroMemory(
  565. pKeResponse->pDataChunks,
  566. sizeof(UL_INTERNAL_DATA_CHUNK) * KernelChunkCount
  567. );
  568. pKeResponse->ContentLengthSpecified = FALSE;
  569. pKeResponse->ChunkedSpecified = FALSE;
  570. pKeResponse->ResponseLength = 0;
  571. pKeResponse->MaxFileSystemStackSize = 0;
  572. pKeResponse->CreationTime.QuadPart = 0;
  573. pKeResponse->ETagLength = 0;
  574. pKeResponse->pETag = NULL;
  575. RtlZeroMemory(
  576. &pKeResponse->ContentType,
  577. sizeof(UL_CONTENT_TYPE)
  578. );
  579. //
  580. // Remember how much we have buffered.
  581. //
  582. if (BufferedSend)
  583. {
  584. pKeResponse->SendBufferedBytes = CopiedBufferLength;
  585. }
  586. else
  587. {
  588. pKeResponse->SendBufferedBytes = 0;
  589. }
  590. //
  591. // Remember if it's ok to do early IRP completion.
  592. //
  593. pKeResponse->CompleteIrpEarly = CompleteEarly;
  594. //
  595. // point to the header buffer space
  596. //
  597. pKeResponse->HeaderLength = HeaderLength;
  598. pKeResponse->pHeaders =
  599. (PUCHAR)(pKeResponse->pDataChunks + pKeResponse->ChunkCount);
  600. //
  601. // and the aux buffer space
  602. //
  603. pKeResponse->AuxBufferLength = AuxBufferLength;
  604. pKeResponse->pAuxiliaryBuffer = (PVOID)(
  605. pKeResponse->pHeaders +
  606. ALIGN_UP(HeaderLength, sizeof(CHAR))
  607. );
  608. //
  609. // and the ETag buffer space
  610. //
  611. if (pETagHeader)
  612. {
  613. pKeResponse->ETagLength = pETagHeader->RawValueLength + 1; // Add space for NULL
  614. pKeResponse->pETag = ((PUCHAR)pKeResponse->pAuxiliaryBuffer) +
  615. AuxBufferLength;
  616. }
  617. //
  618. // Capture the logging data if it exists. Allocate an internal
  619. // log data buffer. This buffer will be released later when we
  620. // are done with logging the stuff, either before freeing the
  621. // chunk trucker (in UlpCompleteSendRequestWorker) or before
  622. // freeing the cache tracker (in UlpCompleteSendCacheEntryWorker)
  623. // please see the definition of UlLogHttpHit.
  624. //
  625. ASSERT( pRequest != NULL || pUserLogData == NULL);
  626. if (NULL != pUserLogData &&
  627. 1 == pRequest->SentLast &&
  628. pRequest->ConfigInfo.pLoggingConfig)
  629. {
  630. pKeResponse->pLogData = &pRequest->LogData;
  631. Status = UlAllocateLogDataBuffer(
  632. pKeResponse->pLogData,
  633. pRequest,
  634. pRequest->ConfigInfo.pLoggingConfig
  635. );
  636. ASSERT(NT_SUCCESS(Status));
  637. Status = UlCaptureLogFields(
  638. pUserLogData,
  639. Version,
  640. pKeResponse->pLogData
  641. );
  642. if (!NT_SUCCESS(Status))
  643. {
  644. goto end;
  645. }
  646. }
  647. else
  648. {
  649. //
  650. // User didn't pass down a log buffer so no need to
  651. // do logging for this response. Or it's possible that
  652. // user passed down log buffer but logging wasn't enabled
  653. //
  654. pKeResponse->pLogData = NULL;
  655. }
  656. //
  657. // User didn't pass down a log buffer so no need to
  658. // do logging for this response. Or it's possible that
  659. // user passed down log buffer but logging wasn't enabled
  660. //
  661. //
  662. // Remember if a Content-Length header was specified.
  663. //
  664. if (pUserResponse != NULL)
  665. {
  666. pKeResponse->StatusCode = pUserResponse->StatusCode;
  667. pKeResponse->Verb = Verb;
  668. if (pUserResponse->Headers.pKnownHeaders[HttpHeaderContentLength].RawValueLength > 0)
  669. {
  670. pKeResponse->ContentLengthSpecified = TRUE;
  671. }
  672. //
  673. // As long as we're here, also remember if "Chunked"
  674. // Transfer-Encoding was specified.
  675. //
  676. if (pUserResponse->Headers.pKnownHeaders[HttpHeaderTransferEncoding].RawValueLength > 0 &&
  677. (0 == _strnicmp(pUserResponse->Headers.pKnownHeaders[HttpHeaderTransferEncoding].pRawValue,
  678. "chunked",
  679. sizeof("chunked")-1)))
  680. {
  681. // NOTE: If a response has a chunked Transfer-Encoding,
  682. // then it shouldn't have a Content-Length
  683. ASSERT(!pKeResponse->ContentLengthSpecified);
  684. pKeResponse->ChunkedSpecified = TRUE;
  685. }
  686. //
  687. // Only capture the following if we're building a cached response
  688. //
  689. if ( CaptureCache )
  690. {
  691. //
  692. // Capture the ETag and put it on the UL_INTERNAL_RESPONSE
  693. //
  694. if (pETagHeader)
  695. {
  696. RtlCopyMemory(
  697. pKeResponse->pETag, // Dest
  698. pETagHeader->pRawValue, // Src
  699. pKeResponse->ETagLength // Bytes
  700. );
  701. // Add NULL termination
  702. //
  703. pKeResponse->pETag[pETagHeader->RawValueLength] = '\0';
  704. }
  705. //
  706. // Capture the ContentType and put it on the UL_INTERNAL_RESPONSE
  707. //
  708. if (pUserResponse->Headers.pKnownHeaders[HttpHeaderContentType].RawValueLength > 0)
  709. {
  710. UlpGetTypeAndSubType(
  711. pUserResponse->Headers.pKnownHeaders[HttpHeaderContentType].pRawValue,
  712. pUserResponse->Headers.pKnownHeaders[HttpHeaderContentType].RawValueLength,
  713. &pKeResponse->ContentType
  714. );
  715. UlTrace(SEND_RESPONSE, (
  716. "http!UlCaptureHttpResponse(pUserResponse = %p) \n"
  717. " Content Type: %s \n"
  718. " Content SubType: %s\n",
  719. pUserResponse,
  720. pKeResponse->ContentType.Type,
  721. pKeResponse->ContentType.SubType
  722. ));
  723. }
  724. //
  725. // Capture the Last-Modified time (if it exists)
  726. //
  727. if (pUserResponse->Headers.pKnownHeaders[HttpHeaderLastModified].RawValueLength)
  728. {
  729. StringTimeToSystemTime(
  730. (const PSTR) pUserResponse->Headers.pKnownHeaders[HttpHeaderLastModified].pRawValue,
  731. &pKeResponse->CreationTime);
  732. }
  733. }
  734. }
  735. //
  736. // copy the aux bytes from the chunks
  737. //
  738. pBuffer = (PUCHAR)(pKeResponse->pAuxiliaryBuffer);
  739. //
  740. // skip the header chunks
  741. //
  742. if (pKeResponse->HeaderLength > 0)
  743. {
  744. pKeDataChunks = pKeResponse->pDataChunks + HEADER_CHUNK_COUNT;
  745. }
  746. else
  747. {
  748. pKeDataChunks = pKeResponse->pDataChunks;
  749. }
  750. for (i = 0 ; i < ChunkCount ; i++)
  751. {
  752. pKeDataChunks[i].ChunkType = pUserDataChunks[i].DataChunkType;
  753. switch (pUserDataChunks[i].DataChunkType)
  754. {
  755. case HttpDataChunkFromMemory:
  756. //
  757. // From-memory chunk. If the caller wants us to copy
  758. // the data (or if its relatively small), then do it
  759. // We allocate space for all of the copied data and any
  760. // filename buffers. Otherwise (it's OK to just lock
  761. // down the data), then allocate a MDL describing the
  762. // user's buffer and lock it down. Note that
  763. // MmProbeAndLockPages() and MmLockPagesSpecifyCache()
  764. // will raise exceptions if they fail.
  765. //
  766. pKeDataChunks[i].FromMemory.pCopiedBuffer = NULL;
  767. if ((Flags & UlCaptureCopyData) ||
  768. pUserDataChunks[i].FromMemory.BufferLength <= g_UlMaxCopyThreshold)
  769. {
  770. ASSERT(pKeResponse->AuxBufferLength > 0);
  771. pKeDataChunks[i].FromMemory.pUserBuffer =
  772. pUserDataChunks[i].FromMemory.pBuffer;
  773. pKeDataChunks[i].FromMemory.BufferLength =
  774. pUserDataChunks[i].FromMemory.BufferLength;
  775. RtlCopyMemory(
  776. pBuffer,
  777. pKeDataChunks[i].FromMemory.pUserBuffer,
  778. pKeDataChunks[i].FromMemory.BufferLength
  779. );
  780. pKeDataChunks[i].FromMemory.pCopiedBuffer = pBuffer;
  781. pBuffer += pKeDataChunks[i].FromMemory.BufferLength;
  782. //
  783. // Allocate a new MDL describing our new location
  784. // in the auxiliary buffer, then build the MDL
  785. // to properly describe nonpaged pool.
  786. //
  787. pKeDataChunks[i].FromMemory.pMdl =
  788. UlAllocateMdl(
  789. pKeDataChunks[i].FromMemory.pCopiedBuffer, // VirtualAddress
  790. pKeDataChunks[i].FromMemory.BufferLength, // Length
  791. FALSE, // SecondaryBuffer
  792. FALSE, // ChargeQuota
  793. NULL // Irp
  794. );
  795. if (pKeDataChunks[i].FromMemory.pMdl == NULL)
  796. {
  797. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  798. break;
  799. }
  800. MmBuildMdlForNonPagedPool(pKeDataChunks[i].FromMemory.pMdl);
  801. }
  802. else
  803. {
  804. //
  805. // build an mdl describing the user's buffer
  806. //
  807. pKeDataChunks[i].FromMemory.BufferLength =
  808. pUserDataChunks[i].FromMemory.BufferLength;
  809. pKeDataChunks[i].FromMemory.pMdl =
  810. UlAllocateMdl(
  811. pUserDataChunks[i].FromMemory.pBuffer, // VirtualAddress
  812. pUserDataChunks[i].FromMemory.BufferLength, // Length
  813. FALSE, // SecondaryBuffer
  814. FALSE, // ChargeQuota
  815. NULL // Irp
  816. );
  817. if (pKeDataChunks[i].FromMemory.pMdl == NULL)
  818. {
  819. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  820. break;
  821. }
  822. //
  823. // lock it down
  824. //
  825. MmProbeAndLockPages(
  826. pKeDataChunks[i].FromMemory.pMdl, // MDL
  827. UserMode, // AccessMode
  828. IoReadAccess // Operation
  829. );
  830. MmMapLockedPagesSpecifyCache(
  831. pKeDataChunks[i].FromMemory.pMdl, // MDL
  832. KernelMode, // AccessMode
  833. MmCached, // CacheType
  834. NULL, // BaseAddress
  835. FALSE, // BugCheckOnFailure
  836. LowPagePriority // Priority
  837. );
  838. }
  839. break;
  840. case HttpDataChunkFromFileName:
  841. ASSERT(pKeResponse->AuxBufferLength > 0);
  842. //
  843. // It's a filename. buffer's already been probed
  844. //
  845. FileLength = pUserDataChunks[i].FromFileName.FileNameLength;
  846. pKeDataChunks[i].FromFile.FileName.Buffer = (PWSTR)pBuffer;
  847. pKeDataChunks[i].FromFile.FileName.Length = FileLength;
  848. pKeDataChunks[i].FromFile.FileName.MaximumLength =
  849. FileLength + sizeof(WCHAR);
  850. pKeDataChunks[i].FromFile.ByteRange =
  851. pUserDataChunks[i].FromFileName.ByteRange;
  852. pKeDataChunks[i].FromFile.pFileCacheEntry = NULL;
  853. //
  854. // have to inline convert this fully qualified win32 filename
  855. // into an nt filename.
  856. //
  857. // CODEWORK: need to handle UNC's.
  858. //
  859. RtlCopyMemory(
  860. pBuffer,
  861. L"\\??\\",
  862. sizeof(L"\\??\\") - sizeof(WCHAR)
  863. );
  864. pBuffer += sizeof(L"\\??\\") - sizeof(WCHAR);
  865. //
  866. // copy the win32 filename (including the terminator)
  867. //
  868. RtlCopyMemory(
  869. pBuffer,
  870. pUserDataChunks[i].FromFileName.pFileName,
  871. FileLength + sizeof(WCHAR)
  872. );
  873. pBuffer += FileLength + sizeof(WCHAR);
  874. //
  875. // adjust the string sizes for the new prefix
  876. //
  877. pKeDataChunks[i].FromFile.FileName.Length +=
  878. sizeof(L"\\??\\") - sizeof(WCHAR);
  879. pKeDataChunks[i].FromFile.FileName.MaximumLength +=
  880. sizeof(L"\\??\\") - sizeof(WCHAR);
  881. break;
  882. case HttpDataChunkFromFileHandle:
  883. //
  884. // From handle.
  885. //
  886. pKeDataChunks[i].FromFile.ByteRange =
  887. pUserDataChunks[i].FromFileHandle.ByteRange;
  888. pKeDataChunks[i].FromFile.FileHandle =
  889. pUserDataChunks[i].FromFileHandle.FileHandle;
  890. break;
  891. default :
  892. ExRaiseStatus( STATUS_INVALID_PARAMETER );
  893. break;
  894. } // switch (pUserDataChunks[i].DataChunkType)
  895. } // for (i = 0 ; i < ChunkCount ; i++)
  896. }
  897. __except( UL_EXCEPTION_FILTER() )
  898. {
  899. Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
  900. }
  901. if (NT_SUCCESS(Status) == FALSE)
  902. goto end;
  903. //
  904. // Ensure we didn't mess up our buffer calculations.
  905. //
  906. ASSERT(DIFF(pBuffer - (PUCHAR)(pKeResponse->pAuxiliaryBuffer)) ==
  907. AuxBufferLength);
  908. UlTrace(SEND_RESPONSE, (
  909. "Http!UlCaptureHttpResponse: captured %p from user %p\n",
  910. pKeResponse,
  911. pUserResponse
  912. ));
  913. end:
  914. if (NT_SUCCESS(Status) == FALSE)
  915. {
  916. if (pKeResponse != NULL)
  917. {
  918. UlpDestroyCapturedResponse( pKeResponse );
  919. pKeResponse = NULL;
  920. }
  921. }
  922. //
  923. // Return the captured response.
  924. //
  925. *ppKernelResponse = pKeResponse;
  926. RETURN(Status);
  927. } // UlCaptureHttpResponse
  928. /***************************************************************************++
  929. Routine Description:
  930. Probes all the buffers passed to use in a user mode http response.
  931. Arguments:
  932. pUserResponse - the response to probe
  933. ChunkCount - the number of data chunks
  934. pDataChunks - the array of data chunks
  935. Flags - Capture flags
  936. --***************************************************************************/
  937. NTSTATUS
  938. UlpProbeHttpResponse(
  939. IN PHTTP_RESPONSE pUserResponse,
  940. IN ULONG ChunkCount,
  941. IN PHTTP_DATA_CHUNK pUserDataChunks,
  942. IN ULONG Flags,
  943. IN PHTTP_LOG_FIELDS_DATA pUserLogData
  944. )
  945. {
  946. NTSTATUS Status;
  947. ULONG i;
  948. PHTTP_UNKNOWN_HEADER pUnknownHeaders;
  949. Status = STATUS_SUCCESS;
  950. __try
  951. {
  952. //
  953. // Validate the response.
  954. //
  955. if (pUserResponse != NULL)
  956. {
  957. if ((pUserResponse->Flags & ~HTTP_RESPONSE_FLAG_VALID) ||
  958. (pUserResponse->StatusCode > 999))
  959. {
  960. ExRaiseStatus( STATUS_INVALID_PARAMETER );
  961. }
  962. }
  963. //
  964. // Probe the log data if it exists
  965. //
  966. if (pUserLogData)
  967. {
  968. Status = UlProbeLogData( pUserLogData );
  969. if (!NT_SUCCESS(Status))
  970. {
  971. ExRaiseStatus( Status );
  972. }
  973. }
  974. //
  975. // first count the headers part
  976. //
  977. if (pUserResponse != NULL)
  978. {
  979. // check the response structure
  980. ProbeTestForRead(
  981. pUserResponse,
  982. sizeof(HTTP_RESPONSE),
  983. sizeof(USHORT)
  984. );
  985. // check the reason string
  986. ProbeTestForRead(
  987. pUserResponse->pReason,
  988. pUserResponse->ReasonLength,
  989. sizeof(CHAR)
  990. );
  991. //
  992. // Loop through the known headers.
  993. //
  994. for (i = 0; i < HttpHeaderResponseMaximum; ++i)
  995. {
  996. USHORT RawValueLength
  997. = pUserResponse->Headers.pKnownHeaders[i].RawValueLength;
  998. if (RawValueLength > 0)
  999. {
  1000. ProbeTestForRead(
  1001. pUserResponse->Headers.pKnownHeaders[i].pRawValue,
  1002. RawValueLength,
  1003. sizeof(CHAR)
  1004. );
  1005. }
  1006. }
  1007. //
  1008. // And the unknown headers (this might throw an exception).
  1009. //
  1010. pUnknownHeaders = pUserResponse->Headers.pUnknownHeaders;
  1011. if (pUnknownHeaders != NULL)
  1012. {
  1013. ProbeTestForRead(
  1014. pUnknownHeaders,
  1015. sizeof(HTTP_UNKNOWN_HEADER)
  1016. * pUserResponse->Headers.UnknownHeaderCount,
  1017. sizeof(ULONG)
  1018. );
  1019. for (i = 0 ; i < pUserResponse->Headers.UnknownHeaderCount; ++i)
  1020. {
  1021. USHORT Length;
  1022. if (pUnknownHeaders[i].NameLength > 0)
  1023. {
  1024. ProbeTestForRead(
  1025. pUnknownHeaders[i].pName,
  1026. pUnknownHeaders[i].NameLength,
  1027. sizeof(CHAR)
  1028. );
  1029. ProbeTestForRead(
  1030. pUnknownHeaders[i].pRawValue,
  1031. pUnknownHeaders[i].RawValueLength,
  1032. sizeof(CHAR)
  1033. );
  1034. }
  1035. }
  1036. }
  1037. }
  1038. //
  1039. // and now the body part
  1040. //
  1041. if (pUserDataChunks != NULL)
  1042. {
  1043. ProbeTestForRead(
  1044. pUserDataChunks,
  1045. sizeof(HTTP_DATA_CHUNK) * ChunkCount,
  1046. sizeof(PVOID)
  1047. );
  1048. for (i = 0 ; i < ChunkCount ; i++)
  1049. {
  1050. switch (pUserDataChunks[i].DataChunkType)
  1051. {
  1052. case HttpDataChunkFromMemory:
  1053. //
  1054. // From-memory chunk.
  1055. //
  1056. if (pUserDataChunks[i].FromMemory.BufferLength == 0 ||
  1057. pUserDataChunks[i].FromMemory.pBuffer == NULL)
  1058. {
  1059. ExRaiseStatus( STATUS_INVALID_PARAMETER );
  1060. }
  1061. if ((Flags & UlCaptureCopyData) ||
  1062. (pUserDataChunks[i].FromMemory.BufferLength <= g_UlMaxCopyThreshold))
  1063. {
  1064. ProbeTestForRead(
  1065. pUserDataChunks[i].FromMemory.pBuffer,
  1066. pUserDataChunks[i].FromMemory.BufferLength,
  1067. sizeof(CHAR)
  1068. );
  1069. }
  1070. break;
  1071. case HttpDataChunkFromFileName:
  1072. //
  1073. // From-file chunk. probe the filename buffer.
  1074. //
  1075. //
  1076. // better be a filename there
  1077. //
  1078. if (pUserDataChunks[i].FromFileName.FileNameLength == 0 ||
  1079. pUserDataChunks[i].FromFileName.pFileName == NULL)
  1080. {
  1081. ExRaiseStatus( STATUS_INVALID_PARAMETER );
  1082. }
  1083. ProbeTestForRead(
  1084. pUserDataChunks[i].FromFileName.pFileName,
  1085. pUserDataChunks[i].FromFileName.FileNameLength + sizeof(WCHAR),
  1086. sizeof(WCHAR)
  1087. );
  1088. break;
  1089. case HttpDataChunkFromFileHandle:
  1090. //
  1091. // From handle chunk. the handle will be validated later
  1092. // by the object manager.
  1093. //
  1094. break;
  1095. default :
  1096. ExRaiseStatus( STATUS_INVALID_PARAMETER );
  1097. break;
  1098. } // switch (pUserDataChunks[i].DataChunkType)
  1099. } // for (i = 0 ; i < ChunkCount ; i++)
  1100. } // if (pUserDataChunks != NULL)
  1101. }
  1102. __except( UL_EXCEPTION_FILTER() )
  1103. {
  1104. Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
  1105. }
  1106. return Status;
  1107. }
  1108. /***************************************************************************++
  1109. Routine Description:
  1110. Figures out how much space we need in the internal response aux buffer.
  1111. The buffer contains copied memory chunks, and names of files to open.
  1112. CODEWORK: need to be aware of chunk encoding
  1113. Arguments:
  1114. ChunkCount - number of chunks in the array
  1115. pDataChunks - the array of data chunks
  1116. Flags - capture flags
  1117. --***************************************************************************/
  1118. VOID
  1119. UlpComputeChunkBufferSizes(
  1120. IN ULONG ChunkCount,
  1121. IN PHTTP_DATA_CHUNK pDataChunks,
  1122. IN ULONG Flags,
  1123. OUT PULONG pAuxBufferSize,
  1124. OUT PULONG pCopiedMemorySize,
  1125. OUT PULONG pUncopiedMemorySize,
  1126. OUT PULONGLONG pFromFileSize
  1127. )
  1128. {
  1129. ULONG AuxLength = 0;
  1130. ULONG CopiedLength = 0;
  1131. ULONG UncopiedLength = 0;
  1132. ULONGLONG FromFileLength = 0;
  1133. ULONG i;
  1134. for (i = 0; i < ChunkCount; i++)
  1135. {
  1136. switch (pDataChunks[i].DataChunkType)
  1137. {
  1138. case HttpDataChunkFromMemory:
  1139. //
  1140. // if we're going to copy the chunk, then make some space in
  1141. // the aux buffer.
  1142. //
  1143. if ((Flags & UlCaptureCopyData) ||
  1144. (pDataChunks[i].FromMemory.BufferLength <= g_UlMaxCopyThreshold))
  1145. {
  1146. AuxLength += pDataChunks[i].FromMemory.BufferLength;
  1147. CopiedLength += pDataChunks[i].FromMemory.BufferLength;
  1148. } else {
  1149. UncopiedLength += pDataChunks[i].FromMemory.BufferLength;
  1150. }
  1151. break;
  1152. case HttpDataChunkFromFileName:
  1153. //
  1154. // add up the string length
  1155. //
  1156. AuxLength += sizeof(L"\\??\\");
  1157. AuxLength += pDataChunks[i].FromFileName.FileNameLength;
  1158. FromFileLength += pDataChunks[i].FromFileName.ByteRange.Length.QuadPart;
  1159. break;
  1160. case HttpDataChunkFromFileHandle:
  1161. FromFileLength += pDataChunks[i].FromFileHandle.ByteRange.Length.QuadPart;
  1162. break;
  1163. default:
  1164. // we should have caught this in the probe
  1165. ASSERT(!"Invalid chunk type");
  1166. break;
  1167. } // switch
  1168. }
  1169. *pAuxBufferSize = AuxLength;
  1170. *pCopiedMemorySize = CopiedLength;
  1171. *pUncopiedMemorySize = UncopiedLength;
  1172. *pFromFileSize = FromFileLength;
  1173. }
  1174. /***************************************************************************++
  1175. Routine Description:
  1176. Prepares the specified response for sending. This preparation
  1177. consists mostly of opening any files referenced by the response.
  1178. Arguments:
  1179. pResponse - Supplies the response to prepare.
  1180. Return Value:
  1181. NTSTATUS - Completion status.
  1182. --***************************************************************************/
  1183. NTSTATUS
  1184. UlPrepareHttpResponse(
  1185. IN HTTP_VERSION Version,
  1186. IN PHTTP_RESPONSE pUserResponse,
  1187. IN PUL_INTERNAL_RESPONSE pResponse
  1188. )
  1189. {
  1190. ULONG i;
  1191. NTSTATUS Status = STATUS_SUCCESS;
  1192. PUL_INTERNAL_DATA_CHUNK internalChunk;
  1193. PUL_FILE_CACHE_ENTRY pFileCacheEntry;
  1194. ULONGLONG offset;
  1195. ULONGLONG length;
  1196. ULONGLONG fileLength;
  1197. CCHAR maxStackSize;
  1198. //
  1199. // Sanity check.
  1200. //
  1201. PAGED_CODE();
  1202. UlTrace(SEND_RESPONSE, (
  1203. "Http!UlPrepareHttpResponse: response %p\n",
  1204. pResponse
  1205. ));
  1206. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pResponse ) );
  1207. //
  1208. // build the http response protocol part
  1209. //
  1210. // check that the caller passed in headers to send
  1211. //
  1212. if (pResponse->HeaderLength > 0)
  1213. {
  1214. ULONG HeaderLength;
  1215. ASSERT(pUserResponse != NULL);
  1216. //
  1217. // generate the response
  1218. //
  1219. Status = UlGenerateFixedHeaders(
  1220. Version,
  1221. pUserResponse,
  1222. pResponse->HeaderLength,
  1223. pResponse->pHeaders,
  1224. &HeaderLength
  1225. );
  1226. if (!NT_SUCCESS(Status))
  1227. goto end;
  1228. //
  1229. // it is possible that no headers got generated (0.9 request) .
  1230. //
  1231. if (HeaderLength > 0)
  1232. {
  1233. //
  1234. // build an mdl for it
  1235. //
  1236. pResponse->pDataChunks[0].ChunkType = HttpDataChunkFromMemory;
  1237. pResponse->pDataChunks[0].FromMemory.BufferLength =
  1238. pResponse->HeaderLength;
  1239. pResponse->pDataChunks[0].FromMemory.pMdl =
  1240. UlAllocateMdl(
  1241. pResponse->pHeaders, // VirtualAddress
  1242. pResponse->HeaderLength, // Length
  1243. FALSE, // SecondaryBuffer
  1244. FALSE, // ChargeQuota
  1245. NULL // Irp
  1246. );
  1247. if (pResponse->pDataChunks[0].FromMemory.pMdl == NULL)
  1248. {
  1249. Status = STATUS_INSUFFICIENT_RESOURCES;
  1250. goto end;
  1251. }
  1252. MmBuildMdlForNonPagedPool(
  1253. pResponse->pDataChunks[0].FromMemory.pMdl
  1254. );
  1255. }
  1256. }
  1257. //
  1258. // Scan the chunks looking for "from file" chunks.
  1259. //
  1260. internalChunk = pResponse->pDataChunks - 1;
  1261. maxStackSize = 0;
  1262. for (i = 0 ; i < pResponse->ChunkCount ; i++)
  1263. {
  1264. internalChunk++;
  1265. switch (internalChunk->ChunkType)
  1266. {
  1267. case HttpDataChunkFromFileHandle:
  1268. case HttpDataChunkFromFileName:
  1269. if (IS_FROM_FILE_HANDLE(internalChunk))
  1270. {
  1271. IF_DEBUG( SEND_RESPONSE )
  1272. {
  1273. KdPrint((
  1274. "UlPrepareHttpResponse: opening handle %p\n",
  1275. &internalChunk->FromFile.FileHandle
  1276. ));
  1277. }
  1278. //
  1279. // Found one. Try to open it.
  1280. //
  1281. Status = UlCreateFileEntry(
  1282. NULL,
  1283. internalChunk->FromFile.FileHandle,
  1284. UserMode,
  1285. &pFileCacheEntry
  1286. );
  1287. if (NT_SUCCESS(Status) == FALSE)
  1288. goto end;
  1289. }
  1290. else
  1291. {
  1292. ASSERT(IS_FROM_FILE_NAME(internalChunk));
  1293. IF_DEBUG( SEND_RESPONSE )
  1294. {
  1295. KdPrint((
  1296. "UlPrepareHttpResponse: opening %wZ\n",
  1297. &internalChunk->FromFile.FileName
  1298. ));
  1299. }
  1300. //
  1301. // Found one. Try to open it.
  1302. //
  1303. Status = UlCreateFileEntry(
  1304. &internalChunk->FromFile.FileName,
  1305. NULL,
  1306. UserMode,
  1307. &pFileCacheEntry
  1308. );
  1309. } // if (IS_FROM_FILE_HANDLE(internalChunk))
  1310. if (NT_SUCCESS(Status) == FALSE)
  1311. goto end;
  1312. internalChunk->FromFile.pFileCacheEntry = pFileCacheEntry;
  1313. if (pFileCacheEntry->pDeviceObject->StackSize > maxStackSize)
  1314. {
  1315. maxStackSize = pFileCacheEntry->pDeviceObject->StackSize;
  1316. }
  1317. //
  1318. // Validate & sanitize the specified byte range.
  1319. //
  1320. fileLength = pFileCacheEntry->FileInfo.EndOfFile.QuadPart;
  1321. offset = internalChunk->FromFile.ByteRange.StartingOffset.QuadPart;
  1322. length = internalChunk->FromFile.ByteRange.Length.QuadPart;
  1323. if (offset >= fileLength)
  1324. {
  1325. Status = STATUS_INVALID_PARAMETER;
  1326. goto end;
  1327. }
  1328. //
  1329. // The offset looks good. If the user is asking for
  1330. // "to eof", then calculate the number of bytes in the
  1331. // file beyond the specified offset.
  1332. //
  1333. if (length == HTTP_BYTE_RANGE_TO_EOF)
  1334. {
  1335. length = fileLength - offset;
  1336. }
  1337. if ((offset + length) > fileLength)
  1338. {
  1339. Status = STATUS_INVALID_PARAMETER;
  1340. goto end;
  1341. }
  1342. //
  1343. // The specified length is good. Sanitize the byte range.
  1344. //
  1345. internalChunk->FromFile.ByteRange.StartingOffset.QuadPart = offset;
  1346. internalChunk->FromFile.ByteRange.Length.QuadPart = length;
  1347. pResponse->ResponseLength += length;
  1348. break;
  1349. case HttpDataChunkFromMemory:
  1350. pResponse->ResponseLength += internalChunk->FromMemory.BufferLength;
  1351. break;
  1352. default:
  1353. ASSERT(FALSE);
  1354. Status = STATUS_INVALID_PARAMETER;
  1355. goto end;
  1356. } // switch (internalChunk->ChunkType)
  1357. }
  1358. pResponse->MaxFileSystemStackSize = maxStackSize;
  1359. end:
  1360. if (NT_SUCCESS(Status) == FALSE)
  1361. {
  1362. //
  1363. // Undo anything done above.
  1364. //
  1365. UlCleanupHttpResponse( pResponse );
  1366. }
  1367. RETURN(Status);
  1368. } // UlPrepareHttpResponse
  1369. /***************************************************************************++
  1370. Routine Description:
  1371. Cleans a response by undoing anything done in UlPrepareHttpResponse().
  1372. Arguments:
  1373. pResponse - Supplies the response to cleanup.
  1374. --***************************************************************************/
  1375. VOID
  1376. UlCleanupHttpResponse(
  1377. IN PUL_INTERNAL_RESPONSE pResponse
  1378. )
  1379. {
  1380. ULONG i;
  1381. NTSTATUS status;
  1382. PUL_INTERNAL_DATA_CHUNK internalChunk;
  1383. //
  1384. // Sanity check.
  1385. //
  1386. PAGED_CODE();
  1387. IF_DEBUG( SEND_RESPONSE )
  1388. {
  1389. KdPrint((
  1390. "UlCleanupHttpResponse: response %p\n",
  1391. pResponse
  1392. ));
  1393. }
  1394. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pResponse ) );
  1395. //
  1396. // paulmcd(9/27/99) removed. can't do this anymore. since handle chunks
  1397. // don't use any auxbuffer, it's possible that we have some chunks to
  1398. // look through
  1399. //
  1400. #if 0
  1401. //
  1402. // If this response does not have an attached file name buffer,
  1403. // then we know there are no embedded "from file" chunks and
  1404. // we can just bail quickly.
  1405. //
  1406. if (pResponse->AuxBufferLength == 0)
  1407. {
  1408. return;
  1409. }
  1410. #endif
  1411. //
  1412. // Scan the chunks looking for "from file" chunks.
  1413. //
  1414. internalChunk = pResponse->pDataChunks - 1;
  1415. for (i = 0 ; i < pResponse->ChunkCount ; i++)
  1416. {
  1417. internalChunk++;
  1418. if (IS_FROM_FILE(internalChunk))
  1419. {
  1420. if (internalChunk->FromFile.pFileCacheEntry == NULL)
  1421. {
  1422. break;
  1423. }
  1424. DereferenceCachedFile(
  1425. internalChunk->FromFile.pFileCacheEntry
  1426. );
  1427. internalChunk->FromFile.pFileCacheEntry = NULL;
  1428. }
  1429. else
  1430. {
  1431. ASSERT( IS_FROM_MEMORY(internalChunk) );
  1432. }
  1433. }
  1434. } // UlCleanupHttpResponse
  1435. /***************************************************************************++
  1436. Routine Description:
  1437. References the specified response.
  1438. Arguments:
  1439. pResponse - Supplies the response to reference.
  1440. --***************************************************************************/
  1441. VOID
  1442. UlReferenceHttpResponse(
  1443. IN PUL_INTERNAL_RESPONSE pResponse
  1444. REFERENCE_DEBUG_FORMAL_PARAMS
  1445. )
  1446. {
  1447. LONG refCount;
  1448. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pResponse ) );
  1449. refCount = InterlockedIncrement( &pResponse->ReferenceCount );
  1450. WRITE_REF_TRACE_LOG(
  1451. g_pHttpResponseTraceLog,
  1452. REF_ACTION_REFERENCE_HTTP_RESPONSE,
  1453. refCount,
  1454. pResponse,
  1455. pFileName,
  1456. LineNumber
  1457. );
  1458. IF_DEBUG( SEND_RESPONSE )
  1459. {
  1460. KdPrint((
  1461. "UlReferenceHttpResponse: response %p refcount %ld\n",
  1462. pResponse,
  1463. refCount
  1464. ));
  1465. }
  1466. } // UlReferenceHttpResponse
  1467. /***************************************************************************++
  1468. Routine Description:
  1469. Dereferences the specified response.
  1470. Arguments:
  1471. pResponse - Supplies the response to dereference.
  1472. --***************************************************************************/
  1473. VOID
  1474. UlDereferenceHttpResponse(
  1475. IN PUL_INTERNAL_RESPONSE pResponse
  1476. REFERENCE_DEBUG_FORMAL_PARAMS
  1477. )
  1478. {
  1479. LONG refCount;
  1480. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pResponse ) );
  1481. refCount = InterlockedDecrement( &pResponse->ReferenceCount );
  1482. WRITE_REF_TRACE_LOG(
  1483. g_pHttpResponseTraceLog,
  1484. REF_ACTION_DEREFERENCE_HTTP_RESPONSE,
  1485. refCount,
  1486. pResponse,
  1487. pFileName,
  1488. LineNumber
  1489. );
  1490. IF_DEBUG( SEND_RESPONSE )
  1491. {
  1492. KdPrint((
  1493. "UlDereferenceHttpResponse: response %p refcount %ld\n",
  1494. pResponse,
  1495. refCount
  1496. ));
  1497. }
  1498. if (refCount == 0)
  1499. {
  1500. UlpDestroyCapturedResponse( pResponse );
  1501. }
  1502. } // UlDereferenceHttpResponse
  1503. //
  1504. // Private functions.
  1505. //
  1506. /***************************************************************************++
  1507. Routine Description:
  1508. Destroys an internal HTTP response captured by UlCaptureHttpResponse().
  1509. This involves closing open files, unlocking memory, and releasing any
  1510. resources allocated to the response.
  1511. Arguments:
  1512. pResponse - Supplies the internal response to destroy.
  1513. --***************************************************************************/
  1514. VOID
  1515. UlpDestroyCapturedResponse(
  1516. IN PUL_INTERNAL_RESPONSE pResponse
  1517. )
  1518. {
  1519. ULONG i;
  1520. ASSERT( UL_IS_VALID_INTERNAL_RESPONSE(pResponse) );
  1521. IF_DEBUG( SEND_RESPONSE )
  1522. {
  1523. KdPrint((
  1524. "UlpDestroyCapturedResponse: response %p\n",
  1525. pResponse
  1526. ));
  1527. }
  1528. //
  1529. // Scan the chunks.
  1530. //
  1531. for (i = 0; i < pResponse->ChunkCount ; ++i)
  1532. {
  1533. if (IS_FROM_MEMORY(&(pResponse->pDataChunks[i])))
  1534. {
  1535. //
  1536. // It's from memory. If necessary, unlock the pages, then
  1537. // free the MDL.
  1538. //
  1539. if (pResponse->pDataChunks[i].FromMemory.pMdl != NULL)
  1540. {
  1541. if (IS_MDL_LOCKED(pResponse->pDataChunks[i].FromMemory.pMdl))
  1542. {
  1543. MmUnlockPages( pResponse->pDataChunks[i].FromMemory.pMdl );
  1544. }
  1545. UlFreeMdl( pResponse->pDataChunks[i].FromMemory.pMdl );
  1546. pResponse->pDataChunks[i].FromMemory.pMdl = NULL;
  1547. }
  1548. }
  1549. else
  1550. {
  1551. //
  1552. // It's a filename. If there is an associated file cache
  1553. // entry, then dereference it.
  1554. //
  1555. ASSERT( IS_FROM_FILE(&(pResponse->pDataChunks[i])) );
  1556. if (pResponse->pDataChunks[i].FromFile.pFileCacheEntry != NULL)
  1557. {
  1558. DereferenceCachedFile(
  1559. pResponse->pDataChunks[i].FromFile.pFileCacheEntry
  1560. );
  1561. pResponse->pDataChunks[i].FromFile.pFileCacheEntry = NULL;
  1562. }
  1563. }
  1564. }
  1565. //
  1566. // We should clean up the log buffer here if nobody has cleaned it up yet.
  1567. // Unless there's an error during capture, the log buffer will be cleaned
  1568. // up when send tracker's (cache/chunk) are completed in their respective
  1569. // routines.
  1570. //
  1571. if ( pResponse->pLogData )
  1572. {
  1573. UlDestroyLogDataBuffer( pResponse->pLogData );
  1574. }
  1575. pResponse->Signature = MAKE_FREE_SIGNATURE(UL_INTERNAL_RESPONSE_POOL_TAG);
  1576. if (pResponse->IsFromLookaside)
  1577. {
  1578. UlPplFreeResponseBuffer(pResponse);
  1579. }
  1580. else
  1581. {
  1582. UL_FREE_POOL_WITH_SIG( pResponse, UL_INTERNAL_RESPONSE_POOL_TAG );
  1583. }
  1584. } // UlpDestroyCapturedResponse
  1585. /***************************************************************************++
  1586. Routine Description:
  1587. Worker routine for managing an in-progress UlSendHttpResponse().
  1588. Arguments:
  1589. pWorkItem - Supplies a pointer to the work item queued. This should
  1590. point to the WORK_ITEM structure embedded in a UL_CHUNK_TRACKER.
  1591. --***************************************************************************/
  1592. VOID
  1593. UlpSendHttpResponseWorker(
  1594. IN PUL_WORK_ITEM pWorkItem
  1595. )
  1596. {
  1597. PUL_CHUNK_TRACKER pTracker;
  1598. NTSTATUS status;
  1599. PUL_INTERNAL_DATA_CHUNK pCurrentChunk;
  1600. PUL_FILE_CACHE_ENTRY pFileCacheEntry;
  1601. PUL_FILE_BUFFER pFileBuffer;
  1602. PMDL pNewMdl;
  1603. ULONG runCount;
  1604. ULONG bytesToRead;
  1605. PMDL pMdlTail;
  1606. PIRP pIrp;
  1607. PIO_STACK_LOCATION pIrpSp;
  1608. //
  1609. // Sanity check.
  1610. //
  1611. PAGED_CODE();
  1612. pTracker = CONTAINING_RECORD(
  1613. pWorkItem,
  1614. UL_CHUNK_TRACKER,
  1615. WorkItem
  1616. );
  1617. IF_DEBUG( SEND_RESPONSE )
  1618. {
  1619. KdPrint((
  1620. "UlpSendHttpResponseWorker: tracker %p\n",
  1621. pTracker
  1622. ));
  1623. }
  1624. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  1625. status = STATUS_SUCCESS;
  1626. while( TRUE )
  1627. {
  1628. //
  1629. // Capture the current chunk pointer, then check for end of
  1630. // response.
  1631. //
  1632. pCurrentChunk = pTracker->pCurrentChunk;
  1633. if (IS_REQUEST_COMPLETE(pTracker))
  1634. {
  1635. ASSERT( status == STATUS_SUCCESS );
  1636. break;
  1637. }
  1638. runCount = pTracker->SendInfo.MdlRunCount;
  1639. //
  1640. // Determine the chunk type.
  1641. //
  1642. if (IS_FROM_MEMORY(pCurrentChunk))
  1643. {
  1644. //
  1645. // It's from a locked-down memory buffer. Since these
  1646. // are always handled in-line (never pended) we can
  1647. // go ahead and adjust the current chunk pointer in the
  1648. // tracker.
  1649. //
  1650. UlpIncrementChunkPointer( pTracker );
  1651. //
  1652. // ignore empty buffers
  1653. //
  1654. if (pCurrentChunk->FromMemory.BufferLength == 0)
  1655. {
  1656. continue;
  1657. }
  1658. //
  1659. // Clone the incoming MDL.
  1660. //
  1661. ASSERT( pCurrentChunk->FromMemory.pMdl->Next == NULL );
  1662. pNewMdl = UlCloneMdl( pCurrentChunk->FromMemory.pMdl );
  1663. if (pNewMdl == NULL)
  1664. {
  1665. status = STATUS_INSUFFICIENT_RESOURCES;
  1666. break;
  1667. }
  1668. //
  1669. // Update the buffered byte count and append the cloned MDL
  1670. // onto our MDL chain.
  1671. //
  1672. pTracker->SendInfo.BytesBuffered += MmGetMdlByteCount( pNewMdl );
  1673. (*pTracker->SendInfo.pMdlLink) = pNewMdl;
  1674. pTracker->SendInfo.pMdlLink = &pNewMdl->Next;
  1675. //
  1676. // Add the MDL to the run list. As an optimization, if the
  1677. // last run in the list was "from memory", we can just
  1678. // append the MDL to the last run.
  1679. //
  1680. if (runCount == 0 ||
  1681. IS_FILE_BUFFER_IN_USE(&(pTracker->SendInfo.MdlRuns[runCount-1].FileBuffer)))
  1682. {
  1683. //
  1684. // Create a new run.
  1685. //
  1686. pTracker->SendInfo.MdlRuns[runCount].pMdlTail = pNewMdl;
  1687. pTracker->SendInfo.MdlRunCount++;
  1688. pFileBuffer = &(pTracker->SendInfo.MdlRuns[runCount].FileBuffer);
  1689. INITIALIZE_FILE_BUFFER(pFileBuffer);
  1690. //
  1691. // If we've not exhausted our static MDL run array,
  1692. // then we'll need to initiate a flush.
  1693. //
  1694. if (pTracker->SendInfo.MdlRunCount == MAX_MDL_RUNS)
  1695. {
  1696. ASSERT( status == STATUS_SUCCESS );
  1697. break;
  1698. }
  1699. }
  1700. else
  1701. {
  1702. //
  1703. // Append to the last run in the list.
  1704. //
  1705. pTracker->SendInfo.MdlRuns[runCount-1].pMdlTail->Next = pNewMdl;
  1706. pTracker->SendInfo.MdlRuns[runCount-1].pMdlTail = pNewMdl;
  1707. }
  1708. //
  1709. // If we've now exceeded the maximum number of bytes we
  1710. // want to buffer, then initiate a flush.
  1711. //
  1712. if (pTracker->SendInfo.BytesBuffered >= MAX_BYTES_BUFFERED)
  1713. {
  1714. ASSERT( status == STATUS_SUCCESS );
  1715. break;
  1716. }
  1717. }
  1718. else
  1719. {
  1720. //
  1721. // It's a filesystem MDL.
  1722. //
  1723. ASSERT( IS_FROM_FILE(pCurrentChunk) );
  1724. pFileCacheEntry = pCurrentChunk->FromFile.pFileCacheEntry;
  1725. ASSERT( IS_VALID_FILE_CACHE_ENTRY( pFileCacheEntry ) );
  1726. pFileBuffer = &(pTracker->SendInfo.MdlRuns[runCount].FileBuffer);
  1727. INITIALIZE_FILE_BUFFER(pFileBuffer);
  1728. //
  1729. // Initiate file read
  1730. //
  1731. if (pTracker->FileBytesRemaining.QuadPart > MAX_BYTES_PER_READ)
  1732. {
  1733. //
  1734. // Don't read too much at once.
  1735. //
  1736. bytesToRead = MAX_BYTES_PER_READ;
  1737. }
  1738. else if (pTracker->FileBytesRemaining.QuadPart == 0)
  1739. {
  1740. //
  1741. // Don't try to read zero bytes.
  1742. //
  1743. UlpIncrementChunkPointer( pTracker );
  1744. continue;
  1745. }
  1746. else
  1747. {
  1748. //
  1749. // Looks like a reasonable number of bytes. Go for it.
  1750. //
  1751. bytesToRead = (ULONG)pTracker->FileBytesRemaining.QuadPart;
  1752. }
  1753. //
  1754. // Initialize the UL_FILE_BUFFER.
  1755. //
  1756. pFileBuffer->pFileCacheEntry = pFileCacheEntry;
  1757. pFileBuffer->FileOffset = pTracker->FileOffset;
  1758. pFileBuffer->Length = bytesToRead;
  1759. pFileBuffer->pFileCacheEntry =
  1760. pCurrentChunk->FromFile.pFileCacheEntry;
  1761. pFileBuffer->pCompletionRoutine = UlpRestartMdlRead;
  1762. pFileBuffer->pContext = pTracker;
  1763. //
  1764. // BumpUp the tracker refcount before starting the Read I/O. In case
  1765. // Send operation later on will complete before the read, we still
  1766. // want the tracker around until UlpRestartMdlRead finishes its
  1767. // business. It will be released when UlpRestartMdlRead got called
  1768. // back.
  1769. //
  1770. UL_REFERENCE_CHUNK_TRACKER( pTracker );
  1771. //
  1772. // issue the I/O
  1773. //
  1774. status = UlReadFileEntry(
  1775. pFileBuffer,
  1776. pTracker->pReadIrp
  1777. );
  1778. //
  1779. // If the read isn't pending, then deref the tracker since
  1780. // UlpRestartMdlRead isn't going to get called.
  1781. //
  1782. if (status != STATUS_PENDING)
  1783. {
  1784. UL_DEREFERENCE_CHUNK_TRACKER( pTracker );
  1785. }
  1786. break;
  1787. }
  1788. }
  1789. //
  1790. // If we fell out of the above loop with status == STATUS_SUCCESS,
  1791. // then the last send we issued was buffered and needs to be flushed.
  1792. // Otherwise, if the status is anything but STATUS_PENDING, then we
  1793. // hit an in-line failure and need to complete the original request.
  1794. //
  1795. if (status == STATUS_SUCCESS)
  1796. {
  1797. if (pTracker->SendInfo.BytesBuffered > 0)
  1798. {
  1799. BOOLEAN initiateDisconnect = FALSE;
  1800. //
  1801. // Flush the send. Since this the last (only?) send to be
  1802. // issued for this response, we can ask UlSendData() to
  1803. // initiate a disconnect on our behalf if appropriate.
  1804. //
  1805. if (IS_REQUEST_COMPLETE(pTracker) &&
  1806. IS_DISCONNECT_TIME(pTracker))
  1807. {
  1808. initiateDisconnect = TRUE;
  1809. }
  1810. //
  1811. // Increment the RefCount on Tracker for Send I/O.
  1812. // UlpSendCompleteWorker will release it later.
  1813. //
  1814. UL_REFERENCE_CHUNK_TRACKER( pTracker );
  1815. //
  1816. // Adjust SendBufferedBytes.
  1817. //
  1818. InterlockedExchangeAdd(
  1819. &pTracker->pHttpConnection->SendBufferedBytes,
  1820. pTracker->pResponse->SendBufferedBytes
  1821. );
  1822. status = UlSendData(
  1823. pTracker->pConnection,
  1824. pTracker->SendInfo.pMdlHead,
  1825. pTracker->SendInfo.BytesBuffered,
  1826. &UlpRestartMdlSend,
  1827. pTracker,
  1828. pTracker->pSendIrp,
  1829. &pTracker->IrpContext,
  1830. initiateDisconnect
  1831. );
  1832. }
  1833. else
  1834. if (IS_DISCONNECT_TIME(pTracker))
  1835. {
  1836. PUL_CONNECTION pConnection;
  1837. //
  1838. // Nothing to send, but we need to issue a disconnect.
  1839. //
  1840. pConnection = pTracker->pConnection;
  1841. pTracker->pConnection = NULL;
  1842. //
  1843. // Increment up until connection close is complete
  1844. //
  1845. UL_REFERENCE_CHUNK_TRACKER( pTracker );
  1846. status = UlCloseConnection(
  1847. pConnection,
  1848. FALSE,
  1849. &UlpCloseConnectionComplete,
  1850. pTracker
  1851. );
  1852. ASSERT( status == STATUS_PENDING );
  1853. }
  1854. }
  1855. //
  1856. // did everything complete?
  1857. //
  1858. if (status != STATUS_PENDING)
  1859. {
  1860. //
  1861. // Nope, something went wrong !
  1862. //
  1863. UlpCompleteSendRequest( pTracker, status );
  1864. }
  1865. //
  1866. // Release our grab on the tracker we are done with it.
  1867. //
  1868. UL_DEREFERENCE_CHUNK_TRACKER( pTracker );
  1869. } // UlpSendHttpResponseWorker
  1870. /***************************************************************************++
  1871. Routine Description:
  1872. Completion handler for UlCloseConnection().
  1873. Arguments:
  1874. pCompletionContext - Supplies an uninterpreted context value
  1875. as passed to the asynchronous API. This is actually a
  1876. PUL_CHUNK_TRACKER pointer.
  1877. Status - Supplies the final completion status of the
  1878. asynchronous API.
  1879. Information - Optionally supplies additional information about
  1880. the completed operation, such as the number of bytes
  1881. transferred. This field is unused for UlCloseConnection().
  1882. --***************************************************************************/
  1883. VOID
  1884. UlpCloseConnectionComplete(
  1885. IN PVOID pCompletionContext,
  1886. IN NTSTATUS Status,
  1887. IN ULONG_PTR Information
  1888. )
  1889. {
  1890. PUL_CHUNK_TRACKER pTracker;
  1891. //
  1892. // Snag the context.
  1893. //
  1894. pTracker = (PUL_CHUNK_TRACKER)pCompletionContext;
  1895. IF_DEBUG( SEND_RESPONSE )
  1896. {
  1897. KdPrint((
  1898. "UlpCloseConnectionComplete: tracker %p\n",
  1899. pTracker
  1900. ));
  1901. }
  1902. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  1903. ASSERT( pTracker->pConnection == NULL );
  1904. UlpCompleteSendRequest( pTracker, Status );
  1905. UL_DEREFERENCE_CHUNK_TRACKER( pTracker );
  1906. } // UlpCloseConnectionComplete
  1907. /***************************************************************************++
  1908. Routine Description:
  1909. Allocates a new send tracker. The newly created tracker must eventually
  1910. be freed with UlpFreeChunkTracker().
  1911. Arguments:
  1912. SendIrpStackSize - Supplies the stack size for the network send IRPs.
  1913. ReadIrpStackSize - Supplies the stack size for the file system read
  1914. IRPs.
  1915. Return Value:
  1916. PUL_CHUNK_TRACKER - The new send tracker if successful, NULL otherwise.
  1917. --***************************************************************************/
  1918. PUL_CHUNK_TRACKER
  1919. UlpAllocateChunkTracker(
  1920. IN UL_TRACKER_TYPE TrackerType,
  1921. IN CCHAR SendIrpStackSize,
  1922. IN CCHAR ReadIrpStackSize,
  1923. IN PUL_HTTP_CONNECTION pHttpConnection,
  1924. IN ULONG Flags,
  1925. IN PUL_INTERNAL_REQUEST pRequest,
  1926. IN PUL_INTERNAL_RESPONSE pResponse,
  1927. IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
  1928. IN PVOID pCompletionContext
  1929. )
  1930. {
  1931. PUL_CHUNK_TRACKER pTracker;
  1932. CCHAR MaxIrpStackSize;
  1933. ASSERT( TrackerType == UlTrackerTypeSend ||
  1934. TrackerType == UlTrackerTypeBuildUriEntry
  1935. );
  1936. MaxIrpStackSize = MAX(SendIrpStackSize, ReadIrpStackSize);
  1937. //
  1938. // Try to allocate from the lookaside list if possible.
  1939. //
  1940. if (MaxIrpStackSize > DEFAULT_MAX_IRP_STACK_SIZE)
  1941. {
  1942. ULONG ChunkTrackerSize;
  1943. USHORT ReadIrpSize;
  1944. USHORT SendIrpSize;
  1945. ReadIrpSize = (USHORT)ALIGN_UP(IoSizeOfIrp(ReadIrpStackSize), PVOID);
  1946. SendIrpSize = (USHORT)ALIGN_UP(IoSizeOfIrp(SendIrpStackSize), PVOID);
  1947. ChunkTrackerSize = ALIGN_UP(sizeof(UL_CHUNK_TRACKER), PVOID) +
  1948. ReadIrpSize +
  1949. SendIrpSize +
  1950. g_UlMaxVariableHeaderSize;
  1951. pTracker = (PUL_CHUNK_TRACKER)UL_ALLOCATE_POOL(
  1952. NonPagedPool,
  1953. ChunkTrackerSize,
  1954. UL_CHUNK_TRACKER_POOL_TAG
  1955. );
  1956. if (pTracker)
  1957. {
  1958. pTracker->Signature = UL_CHUNK_TRACKER_POOL_TAG;
  1959. pTracker->IrpContext.Signature = UL_IRP_CONTEXT_SIGNATURE;
  1960. pTracker->IsFromLookaside = FALSE;
  1961. //
  1962. // Set up the IRP.
  1963. //
  1964. pTracker->pReadIrp =
  1965. (PIRP)((PCHAR)pTracker + ALIGN_UP(sizeof(UL_CHUNK_TRACKER), PVOID));
  1966. IoInitializeIrp(
  1967. pTracker->pReadIrp,
  1968. ReadIrpSize,
  1969. ReadIrpStackSize
  1970. );
  1971. pTracker->pSendIrp =
  1972. (PIRP)((PCHAR)pTracker->pReadIrp + ReadIrpSize);
  1973. IoInitializeIrp(
  1974. pTracker->pSendIrp,
  1975. SendIrpSize,
  1976. SendIrpStackSize
  1977. );
  1978. //
  1979. // Set up the variable header pointer.
  1980. //
  1981. pTracker->pVariableHeader =
  1982. (PUCHAR)((PCHAR)pTracker->pSendIrp + SendIrpSize);
  1983. }
  1984. }
  1985. else
  1986. {
  1987. pTracker = UlPplAllocateChunkTracker();
  1988. if (pTracker)
  1989. {
  1990. ASSERT(pTracker->Signature == MAKE_FREE_TAG(UL_CHUNK_TRACKER_POOL_TAG));
  1991. pTracker->Signature = UL_CHUNK_TRACKER_POOL_TAG;
  1992. }
  1993. }
  1994. if (pTracker != NULL)
  1995. {
  1996. pTracker->Type = TrackerType;
  1997. //
  1998. // RefCounting is necessary since we might have two Aysnc (Read & Send)
  1999. // Io Operation on the same tracker along the way.
  2000. //
  2001. pTracker->RefCount = 1;
  2002. pTracker->Terminated = 0;
  2003. //
  2004. // RefCounting for the pHttpConnection will be handled by our caller
  2005. // "UlSendHttpresponse". Not to worry about it.
  2006. //
  2007. pTracker->pHttpConnection = pHttpConnection;
  2008. pTracker->pConnection = pHttpConnection->pConnection;
  2009. pTracker->Flags = Flags;
  2010. //
  2011. // Completion info.
  2012. //
  2013. pTracker->pCompletionRoutine = pCompletionRoutine;
  2014. pTracker->pCompletionContext = pCompletionContext;
  2015. //
  2016. // Response and request info.
  2017. //
  2018. UL_REFERENCE_INTERNAL_RESPONSE( pResponse );
  2019. pTracker->pResponse = pResponse;
  2020. UL_REFERENCE_INTERNAL_REQUEST( pRequest );
  2021. pTracker->pRequest = pRequest;
  2022. //
  2023. // Note that we set the current chunk to just *before* the first
  2024. // chunk, then call the increment function. This allows us to go
  2025. // through the common increment/update path.
  2026. //
  2027. pTracker->pCurrentChunk = pResponse->pDataChunks - 1;
  2028. //
  2029. // Do this prior to calling UlpIncrementChunkPointer because it
  2030. // expects pLastChunk to be initialized.
  2031. //
  2032. pTracker->pLastChunk = (pTracker->pCurrentChunk + 1) + pResponse->ChunkCount;
  2033. //
  2034. // Zero the remaining fields.
  2035. //
  2036. RtlZeroMemory(
  2037. (PUCHAR)pTracker + FIELD_OFFSET(UL_CHUNK_TRACKER, WorkItem),
  2038. sizeof(*pTracker) - FIELD_OFFSET(UL_CHUNK_TRACKER, WorkItem)
  2039. );
  2040. UlpIncrementChunkPointer( pTracker );
  2041. }
  2042. if (TrackerType == UlTrackerTypeSend) {
  2043. UlTrace(SEND_RESPONSE, (
  2044. "Http!UlpAllocateChunkTracker: tracker %p (send)\n",
  2045. pTracker
  2046. ));
  2047. } else {
  2048. UlTrace(URI_CACHE, (
  2049. "Http!UlpAllocateChunkTracker: tracker %p (build uri)\n",
  2050. pTracker
  2051. ));
  2052. }
  2053. return pTracker;
  2054. } // UlpAllocateChunkTracker
  2055. /***************************************************************************++
  2056. Routine Description:
  2057. Frees a send tracker allocated with UlpAllocateChunkTracker().
  2058. Arguments:
  2059. pTracker - Supplies the send tracker to free.
  2060. --***************************************************************************/
  2061. VOID
  2062. UlpFreeChunkTracker(
  2063. IN PUL_CHUNK_TRACKER pTracker
  2064. )
  2065. {
  2066. ASSERT( pTracker );
  2067. ASSERT( IS_VALID_CHUNK_TRACKER(pTracker) );
  2068. ASSERT( pTracker->Type == UlTrackerTypeSend ||
  2069. pTracker->Type == UlTrackerTypeBuildUriEntry
  2070. );
  2071. if (pTracker->Type == UlTrackerTypeSend) {
  2072. UlTrace(SEND_RESPONSE, (
  2073. "Http!UlpFreeChunkTracker: tracker %p (send)\n",
  2074. pTracker
  2075. ));
  2076. } else {
  2077. UlTrace(URI_CACHE, (
  2078. "Http!UlpFreeChunkTracker: tracker %p (build uri)\n",
  2079. pTracker
  2080. ));
  2081. }
  2082. //
  2083. // Release our ref to the response and request.
  2084. //
  2085. UL_DEREFERENCE_INTERNAL_RESPONSE( pTracker->pResponse );
  2086. UL_DEREFERENCE_INTERNAL_REQUEST( pTracker->pRequest );
  2087. pTracker->Signature = MAKE_FREE_TAG(UL_CHUNK_TRACKER_POOL_TAG);
  2088. if (pTracker->IsFromLookaside)
  2089. {
  2090. UlPplFreeChunkTracker( pTracker );
  2091. }
  2092. else
  2093. {
  2094. UL_FREE_POOL_WITH_SIG( pTracker, UL_CHUNK_TRACKER_POOL_TAG );
  2095. }
  2096. } // UlpFreeChunkTracker
  2097. /***************************************************************************++
  2098. Routine Description:
  2099. Increments the reference count on the chunk tracker.
  2100. Used by Send & Read IRPs
  2101. Arguments:
  2102. pTracker - Supplies the chunk trucker to the reference.
  2103. pFileName (REFERENCE_DEBUG only) - Supplies the name of the file
  2104. containing the calling function.
  2105. LineNumber (REFERENCE_DEBUG only) - Supplies the line number of
  2106. the calling function.
  2107. --***************************************************************************/
  2108. VOID
  2109. UlReferenceChunkTracker(
  2110. IN PUL_CHUNK_TRACKER pTracker
  2111. REFERENCE_DEBUG_FORMAL_PARAMS
  2112. )
  2113. {
  2114. LONG RefCount;
  2115. //
  2116. // Sanity check.
  2117. //
  2118. ASSERT(IS_VALID_CHUNK_TRACKER(pTracker));
  2119. //
  2120. // Reference it.
  2121. //
  2122. RefCount = InterlockedIncrement(&pTracker->RefCount);
  2123. ASSERT(RefCount > 1);
  2124. //
  2125. // Keep the logs updated
  2126. //
  2127. WRITE_REF_TRACE_LOG(
  2128. g_pChunkTrackerTraceLog,
  2129. REF_ACTION_REFERENCE_CHUNK_TRACKER,
  2130. RefCount,
  2131. pTracker,
  2132. pFileName,
  2133. LineNumber
  2134. );
  2135. UlTrace(SEND_RESPONSE,(
  2136. "Http!UlReferenceChunkTracker: tracker %p RefCount %ld\n",
  2137. pTracker,
  2138. RefCount
  2139. ));
  2140. } // UlReferenceChunkTracker
  2141. /***************************************************************************++
  2142. Routine Description:
  2143. Decrements the reference count on the specified chunk tracker.
  2144. Arguments:
  2145. pTracker - Supplies the chunk trucker to the reference.
  2146. pFileName (REFERENCE_DEBUG only) - Supplies the name of the file
  2147. containing the calling function.
  2148. LineNumber (REFERENCE_DEBUG only) - Supplies the line number of
  2149. the calling function.
  2150. --***************************************************************************/
  2151. VOID
  2152. UlDereferenceChunkTracker(
  2153. IN PUL_CHUNK_TRACKER pTracker
  2154. REFERENCE_DEBUG_FORMAL_PARAMS
  2155. )
  2156. {
  2157. LONG RefCount;
  2158. //
  2159. // Sanity check.
  2160. //
  2161. ASSERT(IS_VALID_CHUNK_TRACKER(pTracker));
  2162. //
  2163. // Dereference it.
  2164. //
  2165. RefCount = InterlockedDecrement(&pTracker->RefCount);
  2166. ASSERT(RefCount >= 0);
  2167. //
  2168. // Keep the logs updated
  2169. //
  2170. WRITE_REF_TRACE_LOG(
  2171. g_pChunkTrackerTraceLog,
  2172. REF_ACTION_DEREFERENCE_CHUNK_TRACKER,
  2173. RefCount,
  2174. pTracker,
  2175. pFileName,
  2176. LineNumber
  2177. );
  2178. UlTrace(SEND_RESPONSE,(
  2179. "Http!UlDereferenceChunkTracker: tracker %p RefCount %ld\n",
  2180. pTracker,
  2181. RefCount
  2182. ));
  2183. if (RefCount == 0)
  2184. {
  2185. //
  2186. // The final reference to the chunk tracker has been removed
  2187. // So It's time to FreeUp the ChunkTracker
  2188. //
  2189. UlpFreeChunkTracker(pTracker);
  2190. }
  2191. } // UlDereferenceChunkTracker
  2192. /***************************************************************************++
  2193. Routine Description:
  2194. Completes a "send response" represented by a send tracker.
  2195. Arguments:
  2196. pTracker - Supplies the tracker to complete.
  2197. Status - Supplies the completion status.
  2198. --***************************************************************************/
  2199. VOID
  2200. UlpCompleteSendRequest(
  2201. IN PUL_CHUNK_TRACKER pTracker,
  2202. IN NTSTATUS Status
  2203. )
  2204. {
  2205. PUL_COMPLETION_ROUTINE pCompletionRoutine;
  2206. PVOID pCompletionContext;
  2207. //
  2208. // Although the chunk tracker will be around until all the outstanding
  2209. // Read/Send IRPs are complete. We should only complete the send request
  2210. // once.
  2211. //
  2212. if (FALSE != InterlockedExchange(&pTracker->Terminated, TRUE))
  2213. return;
  2214. IF_DEBUG( SEND_RESPONSE )
  2215. {
  2216. KdPrint((
  2217. "UlpCompleteSendRequest: tracker %p, status %08lx\n",
  2218. pTracker,
  2219. Status
  2220. ));
  2221. }
  2222. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  2223. pTracker->IoStatus.Status = Status;
  2224. UL_REFERENCE_CHUNK_TRACKER( pTracker );
  2225. UL_CALL_PASSIVE(
  2226. &pTracker->WorkItem,
  2227. &UlpCompleteSendRequestWorker
  2228. );
  2229. }
  2230. /***************************************************************************++
  2231. Routine Description:
  2232. Closes the connection if neccessary, cleans up trackers, and completes
  2233. the request.
  2234. Arguments:
  2235. pWorkItem - embedded in our UL_CHUNK_TRACKER
  2236. --***************************************************************************/
  2237. VOID
  2238. UlpCompleteSendRequestWorker(
  2239. PUL_WORK_ITEM pWorkItem
  2240. )
  2241. {
  2242. PUL_CHUNK_TRACKER pTracker;
  2243. PUL_COMPLETION_ROUTINE pCompletionRoutine;
  2244. PVOID pCompletionContext;
  2245. NTSTATUS Status;
  2246. ULONGLONG BytesTransferred;
  2247. KIRQL OldIrql;
  2248. //
  2249. // Sanity check
  2250. //
  2251. PAGED_CODE();
  2252. pTracker = CONTAINING_RECORD(
  2253. pWorkItem,
  2254. UL_CHUNK_TRACKER,
  2255. WorkItem
  2256. );
  2257. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  2258. ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pTracker->pRequest ) );
  2259. //
  2260. // Pull info from the tracker
  2261. //
  2262. pCompletionRoutine = pTracker->pCompletionRoutine;
  2263. pCompletionContext = pTracker->pCompletionContext;
  2264. Status = pTracker->IoStatus.Status;
  2265. BytesTransferred = pTracker->BytesTransferred;
  2266. //
  2267. // do some tracing
  2268. //
  2269. TRACE_TIME(
  2270. pTracker->pHttpConnection->ConnectionId,
  2271. pTracker->pHttpConnection->pRequest->RequestId,
  2272. TIME_ACTION_SEND_COMPLETE
  2273. );
  2274. //
  2275. // Free the MDLs attached to the tracker.
  2276. //
  2277. UlpFreeMdlRuns( pTracker );
  2278. //
  2279. // Updates the BytesSent counter in the request. For a single request
  2280. // we might receive multiple sendresponse ioctl calls, i.e. cgi requests.
  2281. // Multiple internal responses will get allocated and as well as a new
  2282. // chunk trucker for each response. Therefore the correct place to hold
  2283. // the Bytes send information should be in request. On the other hand
  2284. // keeping the request around until the send is done is yet another
  2285. // concern here. An outstanding bug #189327 will solve that issue as well.
  2286. //
  2287. UlInterlockedAdd64(
  2288. (PLONGLONG)&pTracker->pRequest->BytesSent,
  2289. BytesTransferred
  2290. );
  2291. if ( (pTracker->Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0 )
  2292. {
  2293. //
  2294. // Stop MinKBSec timer and start Connection Idle timer
  2295. //
  2296. UlLockTimeoutInfo(
  2297. &(pTracker->pHttpConnection->TimeoutInfo),
  2298. &OldIrql
  2299. );
  2300. UlResetConnectionTimer(
  2301. &(pTracker->pHttpConnection->TimeoutInfo),
  2302. TimerMinKBSec
  2303. );
  2304. UlSetConnectionTimer(
  2305. &(pTracker->pHttpConnection->TimeoutInfo),
  2306. TimerConnectionIdle
  2307. );
  2308. UlUnlockTimeoutInfo(
  2309. &(pTracker->pHttpConnection->TimeoutInfo),
  2310. OldIrql
  2311. );
  2312. UlEvaluateTimerState(
  2313. &(pTracker->pHttpConnection->TimeoutInfo)
  2314. );
  2315. }
  2316. //
  2317. // If this is the last response for this request and
  2318. // there was a log data passed down by the user then now
  2319. // its time to log.
  2320. //
  2321. if ( pTracker->pResponse && pTracker->pResponse->pLogData )
  2322. {
  2323. UlLogHttpHit( pTracker->pResponse->pLogData );
  2324. }
  2325. //
  2326. // complete the request
  2327. //
  2328. if (pCompletionRoutine != NULL)
  2329. {
  2330. (pCompletionRoutine)(
  2331. pCompletionContext,
  2332. Status,
  2333. BytesTransferred > MAXULONG ? MAXULONG : (ULONG)BytesTransferred
  2334. );
  2335. }
  2336. //
  2337. // Kick the parser on the connection and release our hold.
  2338. //
  2339. if ( ((pTracker->Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0)
  2340. && ((pTracker->Flags & HTTP_SEND_RESPONSE_FLAG_DISCONNECT) == 0)
  2341. && (Status == STATUS_SUCCESS) )
  2342. {
  2343. UlResumeParsing( pTracker->pHttpConnection );
  2344. }
  2345. UL_DEREFERENCE_HTTP_CONNECTION( pTracker->pHttpConnection );
  2346. //
  2347. // DeRef the trucker that we have bumped up before queueing this worker
  2348. // function.
  2349. //
  2350. UL_DEREFERENCE_CHUNK_TRACKER( pTracker );
  2351. } // UlpCompleteSendRequestWorker
  2352. /***************************************************************************++
  2353. Routine Description:
  2354. This function sends back a fake early completion to the caller, but
  2355. doesn't clean up any response sending structures.
  2356. Arguments:
  2357. pCompletionRoutine - the routine to call
  2358. pCompletionContext - the context given by the caller
  2359. Status - the status code with which to complete
  2360. BytesTransferred - size of the transfer
  2361. --***************************************************************************/
  2362. VOID
  2363. UlpCompleteSendIrpEarly(
  2364. PUL_COMPLETION_ROUTINE pCompletionRoutine,
  2365. PVOID pCompletionContext,
  2366. NTSTATUS Status,
  2367. ULONGLONG BytesTransferred
  2368. )
  2369. {
  2370. UlTrace(SEND_RESPONSE, (
  2371. "Http!UlpCompleteSendIrpEarly(\n"
  2372. " pCompletionRoutine = %p\n"
  2373. " pCompletionContext = %p\n"
  2374. " Status = %08x\n"
  2375. " BytesTransferred = %I64x)\n",
  2376. pCompletionRoutine,
  2377. pCompletionContext,
  2378. Status,
  2379. BytesTransferred
  2380. ));
  2381. if (pCompletionRoutine != NULL)
  2382. {
  2383. (pCompletionRoutine)(
  2384. pCompletionContext,
  2385. Status,
  2386. BytesTransferred > MAXULONG ? MAXULONG : (ULONG)BytesTransferred
  2387. );
  2388. }
  2389. }
  2390. /***************************************************************************++
  2391. Routine Description:
  2392. Completion handler for MDL READ IRPs used for reading file data.
  2393. Arguments:
  2394. pDeviceObject - Supplies the device object for the IRP being
  2395. completed.
  2396. pIrp - Supplies the IRP being completed.
  2397. pContext - Supplies the context associated with this request.
  2398. This is actually a PUL_CHUNK_TRACKER.
  2399. Return Value:
  2400. NTSTATUS - STATUS_SUCCESS if IO should continue processing this
  2401. IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
  2402. this IRP.
  2403. --***************************************************************************/
  2404. NTSTATUS
  2405. UlpRestartMdlRead(
  2406. IN PDEVICE_OBJECT pDeviceObject,
  2407. IN PIRP pIrp,
  2408. IN PVOID pContext
  2409. )
  2410. {
  2411. NTSTATUS status;
  2412. PUL_CHUNK_TRACKER pTracker;
  2413. ULONG bytesRead;
  2414. PMDL pMdl;
  2415. PMDL pMdlTail;
  2416. BOOLEAN initiateDisconnect = FALSE;
  2417. pTracker = (PUL_CHUNK_TRACKER)pContext;
  2418. IF_DEBUG( SEND_RESPONSE )
  2419. {
  2420. KdPrint((
  2421. "UlpRestartMdlRead: tracker %p\n",
  2422. pTracker
  2423. ));
  2424. }
  2425. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  2426. status = pIrp->IoStatus.Status;
  2427. if (NT_SUCCESS(status))
  2428. {
  2429. bytesRead = (ULONG)pIrp->IoStatus.Information;
  2430. if (bytesRead)
  2431. {
  2432. PUL_FILE_BUFFER pFileBuffer;
  2433. ULONG runCount;
  2434. runCount = pTracker->SendInfo.MdlRunCount;
  2435. pFileBuffer = &(pTracker->SendInfo.MdlRuns[runCount].FileBuffer);
  2436. pMdl = pFileBuffer->pMdl;
  2437. ASSERT(pMdl);
  2438. //
  2439. // Update the buffered byte count and append the new MDL onto
  2440. // our MDL chain.
  2441. //
  2442. pMdlTail = UlFindLastMdlInChain( pMdl );
  2443. pTracker->SendInfo.BytesBuffered += bytesRead;
  2444. (*pTracker->SendInfo.pMdlLink) = pMdl;
  2445. pTracker->SendInfo.pMdlLink = &pMdlTail->Next;
  2446. pTracker->SendInfo.MdlRuns[runCount].pMdlTail = pMdlTail;
  2447. pTracker->SendInfo.MdlRunCount++;
  2448. //
  2449. // Update the file offset & bytes remaining. If we've
  2450. // finished this file chunk (bytes remaining is now zero)
  2451. // then advance to the next chunk.
  2452. //
  2453. pTracker->FileOffset.QuadPart += (ULONGLONG)bytesRead;
  2454. pTracker->FileBytesRemaining.QuadPart -= (ULONGLONG)bytesRead;
  2455. }
  2456. if (pTracker->FileBytesRemaining.QuadPart == 0 )
  2457. {
  2458. UlpIncrementChunkPointer( pTracker );
  2459. //
  2460. // If we're finished with the response (in other words, the
  2461. // call to UlSendData() below will be the last send for this
  2462. // response) and we are to initiate a disconnect, then ask
  2463. // UlSendResponse() to initiate the disconnect for us.
  2464. //
  2465. if (IS_REQUEST_COMPLETE(pTracker) &&
  2466. IS_DISCONNECT_TIME(pTracker))
  2467. {
  2468. initiateDisconnect = TRUE;
  2469. }
  2470. }
  2471. //
  2472. // If we've not exhausted our static MDL run array,
  2473. // we've exceeded the maximum number of bytes we want to
  2474. // buffer, then we'll need to initiate a flush.
  2475. //
  2476. if (IS_REQUEST_COMPLETE(pTracker) ||
  2477. pTracker->SendInfo.MdlRunCount == MAX_MDL_RUNS ||
  2478. pTracker->SendInfo.BytesBuffered >= MAX_BYTES_BUFFERED)
  2479. {
  2480. //
  2481. // Increment the RefCount on Tracker for Send I/O.
  2482. // UlpSendCompleteWorker will release it later.
  2483. //
  2484. UL_REFERENCE_CHUNK_TRACKER( pTracker );
  2485. //
  2486. // Adjust SendBufferedBytes.
  2487. //
  2488. InterlockedExchangeAdd(
  2489. &pTracker->pHttpConnection->SendBufferedBytes,
  2490. pTracker->pResponse->SendBufferedBytes
  2491. );
  2492. status = UlSendData(
  2493. pTracker->pConnection,
  2494. pTracker->SendInfo.pMdlHead,
  2495. pTracker->SendInfo.BytesBuffered,
  2496. &UlpRestartMdlSend,
  2497. pTracker,
  2498. pTracker->pSendIrp,
  2499. &pTracker->IrpContext,
  2500. initiateDisconnect
  2501. );
  2502. }
  2503. else
  2504. {
  2505. //
  2506. // RefCount the chunk tracker up for the UlpSendHttpResponseWorker.
  2507. // It will DeRef it when it's done with the chunk tracker itself.
  2508. // Since this is a passive call we had to increment the refcount
  2509. // for this guy to make sure that tracker is around until it wakes
  2510. // up. Other places makes calls to UlpSendHttpResponseWorker has
  2511. // also been updated as well.
  2512. //
  2513. UL_REFERENCE_CHUNK_TRACKER( pTracker );
  2514. UL_CALL_PASSIVE(
  2515. &pTracker->WorkItem,
  2516. &UlpSendHttpResponseWorker
  2517. );
  2518. }
  2519. }
  2520. if (!NT_SUCCESS(status))
  2521. {
  2522. UlpCompleteSendRequest( pTracker, status );
  2523. }
  2524. //
  2525. // Read I/O Has been completed release our refcount
  2526. // on the chunk tracker.
  2527. //
  2528. UL_DEREFERENCE_CHUNK_TRACKER( pTracker );
  2529. return STATUS_MORE_PROCESSING_REQUIRED;
  2530. } // UlpRestartMdlRead
  2531. /***************************************************************************++
  2532. Routine Description:
  2533. Completion handler for MDL READ COMPLETE IRPs used for returning
  2534. MDLs back to the file system.
  2535. Arguments:
  2536. pDeviceObject - Supplies the device object for the IRP being
  2537. completed.
  2538. pIrp - Supplies the IRP being completed.
  2539. pContext - Supplies the context associated with this request.
  2540. This is actually a PFILE_OBJECT.
  2541. Return Value:
  2542. NTSTATUS - STATUS_SUCCESS if IO should continue processing this
  2543. IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
  2544. this IRP.
  2545. --***************************************************************************/
  2546. NTSTATUS
  2547. UlpRestartMdlReadComplete(
  2548. IN PDEVICE_OBJECT pDeviceObject,
  2549. IN PIRP pIrp,
  2550. IN PVOID pContext
  2551. )
  2552. {
  2553. PUL_CHUNK_TRACKER pTracker = (PUL_CHUNK_TRACKER)pContext;
  2554. ASSERT(IS_VALID_CHUNK_TRACKER(pTracker));
  2555. UL_DEREFERENCE_CHUNK_TRACKER(pTracker);
  2556. UlFreeIrp( pIrp );
  2557. return STATUS_MORE_PROCESSING_REQUIRED;
  2558. } // UlpRestartMdlReadComplete
  2559. /***************************************************************************++
  2560. Routine Description:
  2561. Completion handler for UlSendData().
  2562. Arguments:
  2563. pCompletionContext - Supplies an uninterpreted context value
  2564. as passed to the asynchronous API. This is actually a
  2565. pointer to a UL_CHUNK_TRACKER structure.
  2566. Status - Supplies the final completion status of the
  2567. asynchronous API.
  2568. Information - Optionally supplies additional information about
  2569. the completed operation, such as the number of bytes
  2570. transferred.
  2571. --***************************************************************************/
  2572. VOID
  2573. UlpRestartMdlSend(
  2574. IN PVOID pCompletionContext,
  2575. IN NTSTATUS Status,
  2576. IN ULONG_PTR Information
  2577. )
  2578. {
  2579. PUL_CHUNK_TRACKER pTracker;
  2580. pTracker = (PUL_CHUNK_TRACKER)pCompletionContext;
  2581. IF_DEBUG( SEND_RESPONSE )
  2582. {
  2583. KdPrint((
  2584. "UlpRestartMdlSend: tracker %p\n",
  2585. pTracker
  2586. ));
  2587. }
  2588. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  2589. //
  2590. // Adjust SendBufferedBytes.
  2591. //
  2592. InterlockedExchangeAdd(
  2593. &pTracker->pHttpConnection->SendBufferedBytes,
  2594. - pTracker->pResponse->SendBufferedBytes
  2595. );
  2596. //
  2597. // Disconnect if there was an error, and we didn't disconnect already.
  2598. //
  2599. if ((pTracker->pConnection != NULL) &&
  2600. (!NT_SUCCESS(Status)) &&
  2601. (!IS_DISCONNECT_TIME(pTracker)))
  2602. {
  2603. NTSTATUS TempStatus;
  2604. PUL_CONNECTION pConnection;
  2605. pConnection = pTracker->pConnection;
  2606. pTracker->pConnection = NULL;
  2607. TempStatus = UlCloseConnection(
  2608. pConnection,
  2609. TRUE, // AbortiveDisconnect
  2610. NULL, // pCompletionRoutine
  2611. NULL // pCompletionContext
  2612. );
  2613. }
  2614. //
  2615. // Handle the completion in a work item.
  2616. // We need to get to passive level and
  2617. // we also need to prevent a recursive
  2618. // loop on filtered connections or any
  2619. // other case where our sends might all
  2620. // be completing in-line.
  2621. //
  2622. pTracker->IoStatus.Status = Status;
  2623. pTracker->IoStatus.Information = Information;
  2624. UL_QUEUE_WORK_ITEM(
  2625. &pTracker->WorkItem,
  2626. &UlpSendCompleteWorker
  2627. );
  2628. } // UlpRestartMdlSend
  2629. /***************************************************************************++
  2630. Routine Description:
  2631. Deferred handler for completed sends.
  2632. Arguments:
  2633. pWorkItem - Supplies a pointer to the work item queued. This should
  2634. point to the WORK_ITEM structure embedded in a UL_CHUNK_TRACKER.
  2635. --***************************************************************************/
  2636. VOID
  2637. UlpSendCompleteWorker(
  2638. IN PUL_WORK_ITEM pWorkItem
  2639. )
  2640. {
  2641. PUL_CHUNK_TRACKER pTracker;
  2642. NTSTATUS status;
  2643. //
  2644. // Sanity check.
  2645. //
  2646. PAGED_CODE();
  2647. pTracker = CONTAINING_RECORD(
  2648. pWorkItem,
  2649. UL_CHUNK_TRACKER,
  2650. WorkItem
  2651. );
  2652. IF_DEBUG( SEND_RESPONSE )
  2653. {
  2654. KdPrint((
  2655. "UlpSendCompleteWorker: tracker %p\n",
  2656. pTracker
  2657. ));
  2658. }
  2659. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  2660. //
  2661. // If the chunk completed successfully, then update the bytes
  2662. // transferred and queue another work item for the next chunk if
  2663. // there's more work to do. Otherwise, just complete the request now.
  2664. //
  2665. status = pTracker->IoStatus.Status;
  2666. if (NT_SUCCESS(status))
  2667. {
  2668. pTracker->BytesTransferred += pTracker->IoStatus.Information;
  2669. if (!IS_REQUEST_COMPLETE(pTracker))
  2670. {
  2671. //
  2672. // Free the MDLs attached to the tracker.
  2673. //
  2674. UlpFreeMdlRuns( pTracker );
  2675. //
  2676. // RefCount the chunk tracker up for the UlpSendHttpResponseWorker.
  2677. // It will DeRef it when it's done with the chunk tracker itself.
  2678. //
  2679. UL_REFERENCE_CHUNK_TRACKER( pTracker );
  2680. UlpSendHttpResponseWorker(&pTracker->WorkItem);
  2681. goto end;
  2682. }
  2683. }
  2684. //
  2685. // All done.
  2686. //
  2687. UlpCompleteSendRequest( pTracker, status );
  2688. end:
  2689. //
  2690. // Release our grab on the Tracker, Send I/O is done
  2691. //
  2692. UL_DEREFERENCE_CHUNK_TRACKER( pTracker );
  2693. } // UlpSendCompleteWorker
  2694. /***************************************************************************++
  2695. Routine Description:
  2696. Cleans the MDL_RUNs in the specified tracker and prepares the
  2697. tracker for reuse.
  2698. Arguments:
  2699. pTracker - Supplies the tracker to clean.
  2700. --***************************************************************************/
  2701. VOID
  2702. UlpFreeMdlRuns(
  2703. IN OUT PUL_CHUNK_TRACKER pTracker
  2704. )
  2705. {
  2706. PMDL pMdlHead;
  2707. PMDL pMdlNext;
  2708. PMDL pMdlTmp;
  2709. PMDL_RUN pMdlRun;
  2710. ULONG runCount;
  2711. NTSTATUS status;
  2712. PIRP pIrp;
  2713. PIO_STACK_LOCATION pIrpSp;
  2714. PUL_FILE_CACHE_ENTRY pFileCacheEntry;
  2715. //
  2716. // Sanity check.
  2717. //
  2718. PAGED_CODE();
  2719. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  2720. pMdlHead = pTracker->SendInfo.pMdlHead;
  2721. pMdlRun = &pTracker->SendInfo.MdlRuns[0];
  2722. runCount = pTracker->SendInfo.MdlRunCount;
  2723. while (runCount > 0)
  2724. {
  2725. ASSERT( pMdlHead != NULL );
  2726. ASSERT( pMdlRun->pMdlTail != NULL );
  2727. pMdlNext = pMdlRun->pMdlTail->Next;
  2728. pMdlRun->pMdlTail->Next = NULL;
  2729. pFileCacheEntry = pMdlRun->FileBuffer.pFileCacheEntry;
  2730. if (pFileCacheEntry == NULL)
  2731. {
  2732. //
  2733. // It's a memory run; just walk & free the MDL chain.
  2734. //
  2735. while (pMdlHead != NULL)
  2736. {
  2737. pMdlTmp = pMdlHead->Next;
  2738. UlFreeMdl( pMdlHead );
  2739. pMdlHead = pMdlTmp;
  2740. }
  2741. }
  2742. else
  2743. {
  2744. //
  2745. // It's a file run; try the fast path.
  2746. //
  2747. status = UlReadCompleteFileEntryFast(
  2748. &pMdlRun->FileBuffer
  2749. );
  2750. if (!NT_SUCCESS(status))
  2751. {
  2752. //
  2753. // Fast path failed, we'll need an IRP.
  2754. //
  2755. pIrp = UlAllocateIrp(
  2756. pFileCacheEntry->pDeviceObject->StackSize,
  2757. FALSE
  2758. );
  2759. if (pIrp == NULL)
  2760. {
  2761. ASSERT( !"HANDLE NULL IRP!" );
  2762. }
  2763. else
  2764. {
  2765. pMdlRun->FileBuffer.pCompletionRoutine =
  2766. UlpRestartMdlReadComplete;
  2767. pMdlRun->FileBuffer.pContext = pTracker;
  2768. UL_REFERENCE_CHUNK_TRACKER(pTracker);
  2769. status = UlReadCompleteFileEntry(
  2770. &pMdlRun->FileBuffer,
  2771. pIrp
  2772. );
  2773. if (!NT_SUCCESS(status))
  2774. {
  2775. UL_DEREFERENCE_CHUNK_TRACKER(pTracker);
  2776. }
  2777. }
  2778. }
  2779. }
  2780. pMdlHead = pMdlNext;
  2781. pMdlRun++;
  2782. runCount--;
  2783. }
  2784. UlpInitMdlRuns( pTracker );
  2785. } // UlpFreeMdlRuns
  2786. /***************************************************************************++
  2787. Routine Description:
  2788. Increments the current chunk pointer in the tracker and initializes
  2789. some of the "from file" related tracker fields if necessary.
  2790. Arguments:
  2791. pTracker - Supplies the UL_CHUNK_TRACKER to manipulate.
  2792. --***************************************************************************/
  2793. VOID
  2794. UlpIncrementChunkPointer(
  2795. IN OUT PUL_CHUNK_TRACKER pTracker
  2796. )
  2797. {
  2798. //
  2799. // Bump the pointer. If the request is still incomplete, then
  2800. // check the new current chunk. If it's "from file", then
  2801. // initialize the file offset & bytes remaining from the
  2802. // supplied byte range.
  2803. //
  2804. ASSERT( pTracker->pCurrentChunk < pTracker->pLastChunk );
  2805. pTracker->pCurrentChunk++;
  2806. if (!IS_REQUEST_COMPLETE(pTracker) )
  2807. {
  2808. if (IS_FROM_FILE(pTracker->pCurrentChunk))
  2809. {
  2810. pTracker->FileOffset =
  2811. pTracker->pCurrentChunk->FromFile.ByteRange.StartingOffset;
  2812. pTracker->FileBytesRemaining =
  2813. pTracker->pCurrentChunk->FromFile.ByteRange.Length;
  2814. }
  2815. else
  2816. {
  2817. ASSERT( IS_FROM_MEMORY(pTracker->pCurrentChunk) );
  2818. }
  2819. }
  2820. } // UlpIncrementChunkPointer
  2821. /////////////////////////////////////////////////////////////////////
  2822. /////////////////////////////////////////////////////////////////////
  2823. /////////////////////////////////////////////////////////////////////
  2824. /////////////////////////////////////////////////////////////////////
  2825. /***************************************************************************++
  2826. Routine Description:
  2827. Once we've parsed a request, we pass it in here to try and serve
  2828. from the response cache. This function will either send the response,
  2829. or do nothing at all.
  2830. Arguments:
  2831. pHttpConn - the connection with a req to be handled
  2832. pServedFromCache - we set TRUE if we handled the request
  2833. Return Value:
  2834. NTSTATUS - Completion status.
  2835. --***************************************************************************/
  2836. NTSTATUS
  2837. UlSendCachedResponse(
  2838. PUL_HTTP_CONNECTION pHttpConn,
  2839. PBOOLEAN pServedFromCache,
  2840. PBOOLEAN pConnectionRefused
  2841. )
  2842. {
  2843. NTSTATUS Status = STATUS_SUCCESS;
  2844. PUL_URI_CACHE_ENTRY pUriCacheEntry;
  2845. ULONG Flags;
  2846. PUL_CONFIG_GROUP_OBJECT pMaxBandwidth = NULL;
  2847. ULONG RetCacheControl;
  2848. LONGLONG BytesToSend;
  2849. KIRQL OldIrql;
  2850. //
  2851. // Sanity check.
  2852. //
  2853. PAGED_CODE();
  2854. ASSERT( pHttpConn );
  2855. ASSERT( pServedFromCache );
  2856. *pConnectionRefused = FALSE;
  2857. pUriCacheEntry = UlCheckoutUriCacheEntry(pHttpConn->pRequest);
  2858. //
  2859. // Enforce the connection limit
  2860. //
  2861. if (pUriCacheEntry &&
  2862. UlCheckSiteConnectionLimit(pHttpConn, &pUriCacheEntry->ConfigInfo) == FALSE)
  2863. {
  2864. *pConnectionRefused = TRUE;
  2865. }
  2866. if (pUriCacheEntry && *pConnectionRefused == FALSE) {
  2867. PUL_SITE_COUNTER_ENTRY pCtr;
  2868. ULONG Connections;
  2869. //
  2870. // Perf Counters (cached)
  2871. //
  2872. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  2873. ASSERT(IS_VALID_URL_CONFIG_GROUP_INFO(&pUriCacheEntry->ConfigInfo));
  2874. pCtr = pUriCacheEntry->ConfigInfo.pSiteCounters;
  2875. if (pCtr)
  2876. {
  2877. // NOTE: pCtr may be NULL if the SiteId was never set on the root-level
  2878. // NOTE: Config Group for the site. BVTs may need to be updated.
  2879. ASSERT(IS_VALID_SITE_COUNTER_ENTRY(pCtr));
  2880. if ( pUriCacheEntry->Verb == HttpVerbGET )
  2881. {
  2882. UlIncSiteNonCriticalCounterUlong(pCtr, HttpSiteCounterGetReqs);
  2883. }
  2884. else if ( pUriCacheEntry->Verb == HttpVerbHEAD )
  2885. {
  2886. UlIncSiteNonCriticalCounterUlong(pCtr, HttpSiteCounterHeadReqs);
  2887. }
  2888. UlIncSiteNonCriticalCounterUlong(pCtr, HttpSiteCounterAllReqs);
  2889. UlIncSiteNonCriticalCounterUlong(pCtr, HttpSiteCounterConnAttempts);
  2890. if (pCtr != pHttpConn->pPrevSiteCounters)
  2891. {
  2892. if (pHttpConn->pPrevSiteCounters)
  2893. {
  2894. // Decrement old site's counters & release ref count
  2895. UlDecSiteCounter(
  2896. pHttpConn->pPrevSiteCounters,
  2897. HttpSiteCounterCurrentConns
  2898. );
  2899. DEREFERENCE_SITE_COUNTER_ENTRY(pHttpConn->pPrevSiteCounters);
  2900. }
  2901. Connections = (ULONG) UlIncSiteCounter(pCtr, HttpSiteCounterCurrentConns);
  2902. UlMaxSiteCounter(
  2903. pCtr,
  2904. HttpSiteCounterMaxConnections,
  2905. Connections
  2906. );
  2907. // add ref for new site counters
  2908. REFERENCE_SITE_COUNTER_ENTRY(pCtr);
  2909. pHttpConn->pPrevSiteCounters = pCtr;
  2910. }
  2911. }
  2912. //
  2913. // Check "Accept:" header.
  2914. //
  2915. if ( FALSE == pHttpConn->pRequest->AcceptWildcard)
  2916. {
  2917. if ( FALSE == UlpIsAcceptHeaderOk( pHttpConn->pRequest, pUriCacheEntry ) )
  2918. {
  2919. //
  2920. // Cache entry did not match requested accept header; bounce up
  2921. // to user-mode for response.
  2922. //
  2923. UlCheckinUriCacheEntry(pUriCacheEntry);
  2924. *pServedFromCache = FALSE;
  2925. goto end;
  2926. }
  2927. }
  2928. //
  2929. // Cache-Control: Check the If-* headers to see if we can/should skip
  2930. // sending of the cached response.
  2931. //
  2932. RetCacheControl = UlpCheckCacheControlHeaders(
  2933. pHttpConn->pRequest,
  2934. pUriCacheEntry );
  2935. if ( RetCacheControl )
  2936. {
  2937. // check-in cache entry, since completion won't run.
  2938. UlCheckinUriCacheEntry(pUriCacheEntry);
  2939. if ( 304 == RetCacheControl )
  2940. {
  2941. // Mark as "served from cache"
  2942. *pServedFromCache = TRUE;
  2943. }
  2944. else
  2945. {
  2946. // We failed it.
  2947. ASSERT(412 == RetCacheControl);
  2948. //
  2949. // Indicate that the parser should send error 412 (Precondition Failed)
  2950. //
  2951. pHttpConn->pRequest->ParseState = ParseErrorState;
  2952. pHttpConn->pRequest->ErrorCode = UlErrorPreconditionFailed;
  2953. *pServedFromCache = FALSE;
  2954. Status = STATUS_INVALID_DEVICE_STATE;
  2955. }
  2956. // return success.
  2957. goto end;
  2958. }
  2959. // Try to get the corresponding cgroup for the bw settings
  2960. if (pUriCacheEntry)
  2961. {
  2962. pMaxBandwidth = pUriCacheEntry->ConfigInfo.pMaxBandwidth;
  2963. }
  2964. //
  2965. // Install a filter if BWT is enabled for this request's site.
  2966. //
  2967. if (pMaxBandwidth != NULL &&
  2968. pMaxBandwidth->MaxBandwidth.Flags.Present != 0 &&
  2969. pMaxBandwidth->MaxBandwidth.MaxBandwidth != HTTP_LIMIT_INFINITE )
  2970. {
  2971. // Call TCI to do the filter addition
  2972. UlTcAddFilter( pHttpConn, pMaxBandwidth );
  2973. }
  2974. else
  2975. {
  2976. // Attempt to add the filter to the global flow
  2977. if (UlTcGlobalThrottlingEnabled())
  2978. {
  2979. UlTcAddFilter( pHttpConn, NULL );
  2980. }
  2981. }
  2982. //
  2983. // figure out correct flags
  2984. //
  2985. Flags = 0;
  2986. if ( UlCheckDisconnectInfo(pHttpConn->pRequest) ) {
  2987. Flags |= HTTP_SEND_RESPONSE_FLAG_DISCONNECT;
  2988. }
  2989. //
  2990. // Start the MinKBSec timer, since the data length
  2991. // is in the UL_URI_CACHE_ENTRY
  2992. //
  2993. BytesToSend = pUriCacheEntry->ContentLength + pUriCacheEntry->HeaderLength;
  2994. UlSetMinKBSecTimer(
  2995. &pHttpConn->TimeoutInfo,
  2996. BytesToSend
  2997. );
  2998. // send from the cache
  2999. Status = UlpSendCacheEntry(
  3000. pHttpConn, // connection
  3001. Flags, // send flags
  3002. pUriCacheEntry, // cache entry
  3003. NULL, // completion routine
  3004. NULL, // completion context
  3005. NULL
  3006. );
  3007. *pServedFromCache = TRUE;
  3008. // check in cache entry on failure since our completion
  3009. // routine won't run.
  3010. if ( !NT_SUCCESS(Status) ) {
  3011. UlCheckinUriCacheEntry(pUriCacheEntry);
  3012. }
  3013. } else {
  3014. if (*pConnectionRefused)
  3015. {
  3016. // check in the cache entry if connection is refused
  3017. UlCheckinUriCacheEntry(pUriCacheEntry);
  3018. }
  3019. *pServedFromCache = FALSE;
  3020. }
  3021. end:
  3022. UlTrace(URI_CACHE, (
  3023. "Http!UlSendCachedResponse(httpconn = %p) ServedFromCache = %d, Status = %x\n",
  3024. pHttpConn,
  3025. *pServedFromCache,
  3026. Status
  3027. ));
  3028. return Status;
  3029. } // UlSendCachedResponse
  3030. /***************************************************************************++
  3031. Routine Description:
  3032. If the response is cacheable, then this routine starts building a
  3033. cache entry for it. When the entry is complete it will be sent to
  3034. the client and may be added to the hash table.
  3035. Arguments:
  3036. pRequest - the initiating request
  3037. pResponse - the generated response
  3038. Flags - UlSendHttpResponse flags
  3039. pCompletionRoutine - called after entry is sent
  3040. pCompletionContext - passed to pCompletionRoutine
  3041. pServedFromCache - always set. TRUE if we'll handle sending response.
  3042. FALSE indicates that the caller should send it.
  3043. --***************************************************************************/
  3044. NTSTATUS
  3045. UlCacheAndSendResponse(
  3046. IN PUL_INTERNAL_REQUEST pRequest,
  3047. IN PUL_INTERNAL_RESPONSE pResponse,
  3048. IN PUL_APP_POOL_PROCESS pProcess,
  3049. IN ULONG Flags,
  3050. IN HTTP_CACHE_POLICY Policy,
  3051. IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
  3052. IN PVOID pCompletionContext,
  3053. OUT PBOOLEAN pServedFromCache
  3054. )
  3055. {
  3056. NTSTATUS Status = STATUS_SUCCESS;
  3057. //
  3058. // Sanity check
  3059. //
  3060. PAGED_CODE();
  3061. ASSERT( pServedFromCache );
  3062. //
  3063. // should we close the connection ?
  3064. //
  3065. if ( UlCheckDisconnectInfo(pRequest) )
  3066. {
  3067. Flags |= HTTP_SEND_RESPONSE_FLAG_DISCONNECT;
  3068. }
  3069. //
  3070. // do the real work
  3071. //
  3072. if (UlCheckCacheResponseConditions(pRequest, pResponse, Flags, Policy)) {
  3073. Status = UlpBuildCacheEntry(
  3074. pRequest,
  3075. pResponse,
  3076. pProcess,
  3077. Flags,
  3078. Policy,
  3079. pCompletionRoutine,
  3080. pCompletionContext
  3081. );
  3082. if (NT_SUCCESS(Status)) {
  3083. *pServedFromCache = TRUE;
  3084. } else {
  3085. *pServedFromCache = FALSE;
  3086. }
  3087. } else {
  3088. *pServedFromCache = FALSE;
  3089. }
  3090. UlTrace(URI_CACHE, (
  3091. "Http!UlCacheAndSendResponse ServedFromCache = %d\n",
  3092. *pServedFromCache
  3093. ));
  3094. return Status;
  3095. } // UlCacheAndSendResponse
  3096. /***************************************************************************++
  3097. Routine Description:
  3098. Creates a cache entry for the given response. This routine actually
  3099. allocates the entry and partly initializes it. Then it allocates
  3100. a UL_CHUNK_TRACKER to keep track of filesystem reads.
  3101. Arguments:
  3102. pRequest - the initiating request
  3103. pResponse - the generated response
  3104. Flags - UlSendHttpResponse flags
  3105. pCompletionRoutine - called after entry is sent
  3106. pCompletionContext - passed to pCompletionRoutine
  3107. --***************************************************************************/
  3108. NTSTATUS
  3109. UlpBuildCacheEntry(
  3110. IN PUL_INTERNAL_REQUEST pRequest,
  3111. IN PUL_INTERNAL_RESPONSE pResponse,
  3112. IN PUL_APP_POOL_PROCESS pProcess,
  3113. IN ULONG Flags,
  3114. IN HTTP_CACHE_POLICY CachePolicy,
  3115. IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
  3116. IN PVOID pCompletionContext
  3117. )
  3118. {
  3119. NTSTATUS Status = STATUS_SUCCESS;
  3120. PUL_URI_CACHE_ENTRY pEntry = NULL;
  3121. PUL_CHUNK_TRACKER pTracker = NULL;
  3122. ULONG SpaceLength = 0;
  3123. USHORT LogDataLength = 0;
  3124. ULONG ContentLength = (ULONG)(pResponse->ResponseLength - pResponse->HeaderLength);
  3125. //
  3126. // Sanity check
  3127. //
  3128. PAGED_CODE();
  3129. //
  3130. // See if we need to store any logging data. If we need calculate the
  3131. // required cache space for the logging data per format
  3132. //
  3133. if ( pResponse->pLogData )
  3134. {
  3135. switch( pResponse->pLogData->Format )
  3136. {
  3137. case HttpLoggingTypeW3C:
  3138. {
  3139. // The fields until ServerPort will go to the cache entry.
  3140. // Reserved space for date & time will not be copied.
  3141. LogDataLength = pResponse->pLogData->UsedOffset2
  3142. - pResponse->pLogData->UsedOffset1;
  3143. }
  3144. break;
  3145. case HttpLoggingTypeNCSA:
  3146. {
  3147. // Only a small fragment of NCSA log line goes to the cache
  3148. // entry. This fragment is located between offset2 and 1
  3149. // excluding the space reserved for date & time fields
  3150. LogDataLength = pResponse->pLogData->UsedOffset2
  3151. - pResponse->pLogData->UsedOffset1
  3152. - NCSA_FIX_DATE_AND_TIME_FIELD_SIZE;
  3153. }
  3154. break;
  3155. case HttpLoggingTypeIIS:
  3156. {
  3157. // Only the fragments two and three go to the
  3158. // cache entry
  3159. LogDataLength = (USHORT)pResponse->pLogData->Used +
  3160. pResponse->pLogData->UsedOffset2;
  3161. }
  3162. break;
  3163. default:
  3164. ASSERT(!"Unknown Log Format.\n");
  3165. }
  3166. }
  3167. //
  3168. // allocate a cache entry
  3169. //
  3170. SpaceLength =
  3171. pRequest->CookedUrl.Length + sizeof(WCHAR) + // space for hash key
  3172. pResponse->ETagLength + // space for ETag
  3173. LogDataLength; // space for logging
  3174. UlTrace(URI_CACHE, (
  3175. "Http!UlpBuildCacheEntry allocating UL_URI_CACHE_ENTRY, 0x%x bytes of data\n"
  3176. " Url.Length = 0x%x, aligned Length = 0x%x\n"
  3177. "\n",
  3178. SpaceLength,
  3179. pRequest->CookedUrl.Length,
  3180. ALIGN_UP(pRequest->CookedUrl.Length, WCHAR)
  3181. ));
  3182. UlTrace(URI_CACHE, (
  3183. " ContentLength=0x%x, %d\n", ContentLength, ContentLength));
  3184. pEntry = UL_ALLOCATE_STRUCT_WITH_SPACE(
  3185. PagedPool,
  3186. UL_URI_CACHE_ENTRY,
  3187. SpaceLength,
  3188. UL_URI_CACHE_ENTRY_POOL_TAG
  3189. );
  3190. if (pEntry) {
  3191. //
  3192. // init entry
  3193. //
  3194. UlInitCacheEntry(
  3195. pEntry,
  3196. pRequest->CookedUrl.Hash,
  3197. pRequest->CookedUrl.Length,
  3198. pRequest->CookedUrl.pUrl
  3199. );
  3200. //
  3201. // Copy the ETag from the response (for If-* headers)
  3202. //
  3203. pEntry->pETag =
  3204. (((PUCHAR) pEntry->UriKey.pUri) + // start of URI
  3205. pEntry->UriKey.Length + sizeof(WCHAR)); // + length of uri
  3206. pEntry->ETagLength = pResponse->ETagLength;
  3207. if ( pEntry->ETagLength )
  3208. {
  3209. RtlCopyMemory(
  3210. pEntry->pETag,
  3211. pResponse->pETag,
  3212. pEntry->ETagLength
  3213. );
  3214. }
  3215. //
  3216. // Capture Content-Type so we can verify the Accept: header on requests.
  3217. //
  3218. RtlCopyMemory(
  3219. &pEntry->ContentType,
  3220. &pResponse->ContentType,
  3221. sizeof(UL_CONTENT_TYPE)
  3222. );
  3223. //
  3224. // Get the System Time of the Date: header (for If-* headers)
  3225. //
  3226. pEntry->CreationTime.QuadPart = pResponse->CreationTime.QuadPart;
  3227. pEntry->ContentLengthSpecified = pResponse->ContentLengthSpecified;
  3228. pEntry->StatusCode = pResponse->StatusCode;
  3229. pEntry->Verb = pRequest->Verb;
  3230. pEntry->CachePolicy = CachePolicy;
  3231. if (CachePolicy.Policy == HttpCachePolicyTimeToLive)
  3232. {
  3233. KeQuerySystemTime(&pEntry->ExpirationTime);
  3234. //
  3235. // convert seconds to 100 nanosecond intervals (x * 10^7)
  3236. //
  3237. pEntry->ExpirationTime.QuadPart +=
  3238. CachePolicy.SecondsToLive * C_NS_TICKS_PER_SEC;
  3239. } else {
  3240. pEntry->ExpirationTime.QuadPart = 0;
  3241. }
  3242. //
  3243. // Capture the Config Info from the request
  3244. //
  3245. ASSERT(IS_VALID_URL_CONFIG_GROUP_INFO(&pRequest->ConfigInfo));
  3246. UlpConfigGroupInfoDeepCopy(&pRequest->ConfigInfo, &pEntry->ConfigInfo);
  3247. //
  3248. // remember who created us
  3249. //
  3250. pEntry->pProcess = pProcess;
  3251. //
  3252. // generate the content and fixed headers
  3253. //
  3254. pEntry->pResponseMdl = UlLargeMemAllocate(
  3255. ContentLength + pResponse->HeaderLength,
  3256. &pEntry->LongTermCacheable
  3257. );
  3258. if (NULL == pEntry->pResponseMdl)
  3259. {
  3260. Status = STATUS_NO_MEMORY;
  3261. goto cleanup;
  3262. }
  3263. pEntry->HeaderLength = pResponse->HeaderLength;
  3264. if (FALSE == UlLargeMemSetData(
  3265. pEntry->pResponseMdl, // Dest MDL
  3266. pResponse->pHeaders, // Buffer to copy
  3267. pResponse->HeaderLength, // Length to copy
  3268. ContentLength // Offset in Dest MDL
  3269. ))
  3270. {
  3271. Status = STATUS_INSUFFICIENT_RESOURCES;
  3272. goto cleanup;
  3273. }
  3274. //
  3275. // generate the content body
  3276. //
  3277. pEntry->ContentLength = ContentLength;
  3278. //
  3279. // copy over the log data
  3280. //
  3281. if ( pResponse->pLogData == NULL )
  3282. {
  3283. pEntry->LoggingEnabled = FALSE;
  3284. pEntry->LogDataLength = 0;
  3285. pEntry->MaxLength = 0;
  3286. pEntry->pLogData = NULL;
  3287. pEntry->UsedOffset1 = 0;
  3288. pEntry->UsedOffset2 = 0;
  3289. }
  3290. else
  3291. {
  3292. //
  3293. // There could be no field to save in the cache entry but the logging
  3294. // might still be enabled for those fields we generate later i.e.
  3295. // logging enabled with fields date & time.
  3296. //
  3297. pEntry->LoggingEnabled = TRUE;
  3298. pEntry->MaxLength = pResponse->pLogData->Length;
  3299. //
  3300. // Copy over the partially complete log line not including the date & time
  3301. // fields to the cache entry. Also remember the length of the data.
  3302. //
  3303. if ( LogDataLength )
  3304. {
  3305. pEntry->LogDataLength = LogDataLength;
  3306. pEntry->pLogData =
  3307. pEntry->pETag +
  3308. pEntry->ETagLength;
  3309. switch( pResponse->pLogData->Format )
  3310. {
  3311. case HttpLoggingTypeW3C:
  3312. {
  3313. // Discard the date,time,username fields at the beginning of
  3314. // the log line when storing the cache entry.
  3315. pEntry->UsedOffset1 = pResponse->pLogData->UsedOffset1;
  3316. pEntry->UsedOffset2 = pResponse->pLogData->UsedOffset2;
  3317. // Copy the middle fragment
  3318. RtlCopyMemory(
  3319. pEntry->pLogData,
  3320. &pResponse->pLogData->Line[pEntry->UsedOffset1],
  3321. LogDataLength
  3322. );
  3323. }
  3324. break;
  3325. case HttpLoggingTypeNCSA:
  3326. {
  3327. // Calculate the start of the middle fragment.
  3328. pEntry->UsedOffset1 = pResponse->pLogData->UsedOffset1
  3329. + NCSA_FIX_DATE_AND_TIME_FIELD_SIZE;
  3330. pEntry->UsedOffset2 = 0;
  3331. // Copy the middle fragment
  3332. RtlCopyMemory(
  3333. pEntry->pLogData,
  3334. &pResponse->pLogData->Line[pEntry->UsedOffset1],
  3335. LogDataLength
  3336. );
  3337. }
  3338. break;
  3339. case HttpLoggingTypeIIS:
  3340. {
  3341. // UsedOffset1 specifies the second fragment's size.
  3342. // UsedOffset2 specifies the third's size.
  3343. pEntry->UsedOffset1 = pResponse->pLogData->UsedOffset2;
  3344. pEntry->UsedOffset2 = LogDataLength - pEntry->UsedOffset1;
  3345. // Copy over the fragments two and three
  3346. RtlCopyMemory(
  3347. pEntry->pLogData,
  3348. &pResponse->pLogData->Line[IIS_LOG_LINE_SECOND_FRAGMENT_OFFSET],
  3349. pEntry->UsedOffset1
  3350. );
  3351. RtlCopyMemory(
  3352. &pEntry->pLogData[pEntry->UsedOffset1],
  3353. &pResponse->pLogData->Line[IIS_LOG_LINE_THIRD_FRAGMENT_OFFSET],
  3354. pEntry->UsedOffset2
  3355. );
  3356. }
  3357. break;
  3358. default:
  3359. ASSERT(!"Unknown Log Format.\n");
  3360. }
  3361. }
  3362. else
  3363. {
  3364. pEntry->LogDataLength = 0;
  3365. pEntry->pLogData = NULL;
  3366. pEntry->UsedOffset1 = 0;
  3367. pEntry->UsedOffset2 = 0;
  3368. }
  3369. }
  3370. UlTrace(URI_CACHE, (
  3371. "Http!UlpBuildCacheEntry\n"
  3372. " entry = %p\n"
  3373. " pUri = %p '%ls'\n"
  3374. " pResponseMdl = %p (%d bytes)\n"
  3375. " pETag = %p\n"
  3376. " pLogData = %p\n"
  3377. " end = %p\n",
  3378. pEntry,
  3379. pEntry->UriKey.pUri, pEntry->UriKey.pUri,
  3380. pEntry->pResponseMdl, pEntry->ContentLength + pEntry->HeaderLength,
  3381. pEntry->pETag,
  3382. pEntry->pLogData,
  3383. ((PUCHAR)pEntry->UriKey.pUri) + SpaceLength
  3384. ));
  3385. pTracker =
  3386. UlpAllocateChunkTracker(
  3387. UlTrackerTypeBuildUriEntry, // tracker type
  3388. 0, // send irp size
  3389. pResponse->MaxFileSystemStackSize, // read irp size
  3390. pRequest->pHttpConn,
  3391. Flags,
  3392. pRequest,
  3393. pResponse,
  3394. pCompletionRoutine,
  3395. pCompletionContext
  3396. );
  3397. if (pTracker) {
  3398. ULONG i;
  3399. //
  3400. // init tracker BuildInfo
  3401. //
  3402. pTracker->BuildInfo.pUriEntry = pEntry;
  3403. pTracker->BuildInfo.Offset = 0;
  3404. UlTrace(LARGE_MEM, ("UlpBuildCacheEntry: init tracker BuildInfo\n"));
  3405. INITIALIZE_FILE_BUFFER(&pTracker->BuildInfo.FileBuffer);
  3406. //
  3407. // skip over the header chunks because we already
  3408. // got that stuff
  3409. //
  3410. for (i = 0; i < HEADER_CHUNK_COUNT; i++) {
  3411. ASSERT( !IS_REQUEST_COMPLETE( pTracker ) );
  3412. UlpIncrementChunkPointer( pTracker );
  3413. }
  3414. //
  3415. // finish initialization on a system thread so that MDLs
  3416. // will get charged to the System process instead of the
  3417. // current process.
  3418. //
  3419. UlpBuildBuildTrackerWorker(&pTracker->WorkItem);
  3420. Status = STATUS_PENDING;
  3421. } else {
  3422. Status = STATUS_INSUFFICIENT_RESOURCES;
  3423. }
  3424. } else {
  3425. Status = STATUS_NO_MEMORY;
  3426. }
  3427. cleanup:
  3428. UlTrace(URI_CACHE, (
  3429. "Http!UlpBuildCacheEntry Status = %x, pEntry = %x\n",
  3430. Status,
  3431. pEntry
  3432. ));
  3433. if (!NT_SUCCESS(Status)) {
  3434. if (pEntry) {
  3435. if (pEntry->pResponseMdl) {
  3436. UlLargeMemFree(pEntry->pResponseMdl);
  3437. }
  3438. UL_FREE_POOL_WITH_SIG(pEntry, UL_URI_CACHE_ENTRY_POOL_TAG);
  3439. }
  3440. if (pTracker) {
  3441. UL_FREE_POOL_WITH_SIG(pTracker, UL_CHUNK_TRACKER_POOL_TAG);
  3442. }
  3443. }
  3444. return Status;
  3445. } // UlpBuildCacheEntry
  3446. /***************************************************************************++
  3447. Routine Description:
  3448. Worker Routine used to initialize the UL_CHUNK_TRACKER for
  3449. UlpBuildCacheEntry. We need to have this function so that when
  3450. we make a MDL inside the tracker, the page will be charged to the
  3451. System process instead of the current process.
  3452. Arguments:
  3453. pWorkItem - Supplies a pointer to the work item queued. This should
  3454. point to the WORK_ITEM structure embedded in a UL_CHUNK_TRACKER.
  3455. --***************************************************************************/
  3456. VOID
  3457. UlpBuildBuildTrackerWorker(
  3458. IN PUL_WORK_ITEM pWorkItem
  3459. )
  3460. {
  3461. PUL_CHUNK_TRACKER pTracker;
  3462. //
  3463. // Sanity check.
  3464. //
  3465. PAGED_CODE();
  3466. pTracker = CONTAINING_RECORD(
  3467. pWorkItem,
  3468. UL_CHUNK_TRACKER,
  3469. WorkItem
  3470. );
  3471. //
  3472. // init tracker BuildInfo
  3473. //
  3474. UlTrace(LARGE_MEM, ("UlpBuildBuildTrackerWorker\n"));
  3475. //
  3476. // add reference to connection for tracker
  3477. //
  3478. UL_REFERENCE_HTTP_CONNECTION( pTracker->pHttpConnection );
  3479. //
  3480. // Let the worker do the dirty work, no reason to queue off
  3481. //
  3482. // it will queue the first time it needs to do blocking i/o
  3483. //
  3484. UlpBuildCacheEntryWorker(&pTracker->WorkItem);
  3485. } // UlpBuildBuildTrackerWorker
  3486. /***************************************************************************++
  3487. Routine Description:
  3488. Worker routine for managing an in-progress UlpBuildCacheEntry().
  3489. This routine iterates through all the chunks in the response
  3490. and copies the data into the cache entry.
  3491. Arguments:
  3492. pWorkItem - Supplies a pointer to the work item queued. This should
  3493. point to the WORK_ITEM structure embedded in a UL_CHUNK_TRACKER.
  3494. --***************************************************************************/
  3495. VOID
  3496. UlpBuildCacheEntryWorker(
  3497. IN PUL_WORK_ITEM pWorkItem
  3498. )
  3499. {
  3500. PUL_CHUNK_TRACKER pTracker;
  3501. NTSTATUS status;
  3502. PUL_INTERNAL_DATA_CHUNK pCurrentChunk;
  3503. PUL_FILE_CACHE_ENTRY pFileCacheEntry;
  3504. PIRP pIrp;
  3505. PIO_STACK_LOCATION pIrpSp;
  3506. PUCHAR pBuffer;
  3507. ULONG BufferLength;
  3508. //
  3509. // Sanity check.
  3510. //
  3511. PAGED_CODE();
  3512. pTracker = CONTAINING_RECORD(
  3513. pWorkItem,
  3514. UL_CHUNK_TRACKER,
  3515. WorkItem
  3516. );
  3517. UlTrace(URI_CACHE, (
  3518. "Http!UlpBuildCacheEntryWorker: tracker %p\n",
  3519. pTracker
  3520. ));
  3521. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  3522. status = STATUS_SUCCESS;
  3523. while( TRUE )
  3524. {
  3525. //
  3526. // Capture the current chunk pointer, then check for end of
  3527. // response.
  3528. //
  3529. pCurrentChunk = pTracker->pCurrentChunk;
  3530. if (IS_REQUEST_COMPLETE(pTracker))
  3531. {
  3532. ASSERT( status == STATUS_SUCCESS );
  3533. break;
  3534. }
  3535. //
  3536. // Determine the chunk type.
  3537. //
  3538. if (IS_FROM_MEMORY(pCurrentChunk))
  3539. {
  3540. //
  3541. // It's from a locked-down memory buffer. Since these
  3542. // are always handled in-line (never pended) we can
  3543. // go ahead and adjust the current chunk pointer in the
  3544. // tracker.
  3545. //
  3546. UlpIncrementChunkPointer( pTracker );
  3547. //
  3548. // ignore empty buffers
  3549. //
  3550. if (pCurrentChunk->FromMemory.BufferLength == 0)
  3551. {
  3552. continue;
  3553. }
  3554. //
  3555. // Copy the incoming memory.
  3556. //
  3557. ASSERT( pCurrentChunk->FromMemory.pMdl->Next == NULL );
  3558. pBuffer = (PUCHAR) MmGetMdlVirtualAddress(
  3559. pCurrentChunk->FromMemory.pMdl );
  3560. BufferLength = MmGetMdlByteCount( pCurrentChunk->FromMemory.pMdl );
  3561. UlTrace(LARGE_MEM, (
  3562. "Http!UlpBuildCacheEntryWorker: "
  3563. "copy range %u (%x) -> %u\n",
  3564. pTracker->BuildInfo.Offset,
  3565. BufferLength,
  3566. pTracker->BuildInfo.Offset
  3567. + BufferLength
  3568. ));
  3569. if (FALSE == UlLargeMemSetData(
  3570. pTracker->BuildInfo.pUriEntry->pResponseMdl,
  3571. pBuffer,
  3572. BufferLength,
  3573. pTracker->BuildInfo.Offset
  3574. ))
  3575. {
  3576. status = STATUS_INSUFFICIENT_RESOURCES;
  3577. break;
  3578. }
  3579. pTracker->BuildInfo.Offset += BufferLength;
  3580. ASSERT(pTracker->BuildInfo.Offset <= pTracker->BuildInfo.pUriEntry->ContentLength);
  3581. }
  3582. else
  3583. {
  3584. //
  3585. // It's a filesystem MDL.
  3586. //
  3587. ASSERT( IS_FROM_FILE(pCurrentChunk) );
  3588. //
  3589. // ignore empty file ranges
  3590. //
  3591. if (pCurrentChunk->FromFile.ByteRange.Length.QuadPart == 0)
  3592. {
  3593. UlpIncrementChunkPointer( pTracker );
  3594. continue;
  3595. }
  3596. //
  3597. // Do the read.
  3598. //
  3599. pTracker->BuildInfo.FileBuffer.pFileCacheEntry =
  3600. pCurrentChunk->FromFile.pFileCacheEntry;
  3601. pTracker->BuildInfo.FileBuffer.FileOffset = pTracker->FileOffset;
  3602. pTracker->BuildInfo.FileBuffer.Length =
  3603. MIN(
  3604. (ULONG)pTracker->FileBytesRemaining.QuadPart,
  3605. MAX_BYTES_PER_READ
  3606. );
  3607. pTracker->BuildInfo.FileBuffer.pCompletionRoutine =
  3608. UlpRestartCacheMdlRead;
  3609. pTracker->BuildInfo.FileBuffer.pContext = pTracker;
  3610. status = UlReadFileEntry(
  3611. &pTracker->BuildInfo.FileBuffer,
  3612. pTracker->pReadIrp
  3613. );
  3614. break;
  3615. }
  3616. }
  3617. //
  3618. // did everything complete?
  3619. //
  3620. if (status != STATUS_PENDING)
  3621. {
  3622. //
  3623. // yep, complete the response
  3624. //
  3625. UlpCompleteCacheBuild( pTracker, status );
  3626. }
  3627. } // UlpBuildCacheEntryWorker
  3628. /***************************************************************************++
  3629. Routine Description:
  3630. Completion handler for MDL READ IRPs used for reading file data.
  3631. Arguments:
  3632. pDeviceObject - Supplies the device object for the IRP being
  3633. completed.
  3634. pIrp - Supplies the IRP being completed.
  3635. pContext - Supplies the context associated with this request.
  3636. This is actually a PUL_CHUNK_TRACKER.
  3637. Return Value:
  3638. NTSTATUS - STATUS_SUCCESS if IO should continue processing this
  3639. IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
  3640. this IRP.
  3641. --***************************************************************************/
  3642. NTSTATUS
  3643. UlpRestartCacheMdlRead(
  3644. IN PDEVICE_OBJECT pDeviceObject,
  3645. IN PIRP pIrp,
  3646. IN PVOID pContext
  3647. )
  3648. {
  3649. NTSTATUS status;
  3650. PUL_CHUNK_TRACKER pTracker;
  3651. PMDL pMdl;
  3652. PUCHAR pData;
  3653. ULONG DataLen;
  3654. pTracker = (PUL_CHUNK_TRACKER)pContext;
  3655. UlTrace(URI_CACHE, (
  3656. "Http!UlpRestartCacheMdlRead: tracker %p, status %x info %d\n",
  3657. pTracker,
  3658. pIrp->IoStatus.Status,
  3659. (ULONG) pIrp->IoStatus.Information
  3660. ));
  3661. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  3662. status = pIrp->IoStatus.Status;
  3663. if (NT_SUCCESS(status))
  3664. {
  3665. //
  3666. // copy read data into cache buffer
  3667. //
  3668. pMdl = pTracker->BuildInfo.FileBuffer.pMdl;
  3669. while (pMdl) {
  3670. pData = (PUCHAR) MmGetMdlVirtualAddress(pMdl);
  3671. DataLen = MmGetMdlByteCount(pMdl);
  3672. UlTrace(LARGE_MEM, (
  3673. "Http!UlpRestartCacheMdlRead: "
  3674. "copy range %u (%x) -> %u\n",
  3675. pTracker->BuildInfo.Offset,
  3676. DataLen,
  3677. pTracker->BuildInfo.Offset
  3678. + DataLen
  3679. ));
  3680. if (FALSE == UlLargeMemSetData(
  3681. pTracker->BuildInfo.pUriEntry->pResponseMdl,
  3682. pData,
  3683. DataLen,
  3684. pTracker->BuildInfo.Offset
  3685. ))
  3686. {
  3687. status = STATUS_INSUFFICIENT_RESOURCES;
  3688. goto end;
  3689. }
  3690. pTracker->BuildInfo.Offset += DataLen;
  3691. ASSERT(pTracker->BuildInfo.Offset <= pTracker->BuildInfo.pUriEntry->ContentLength);
  3692. pMdl = pMdl->Next;
  3693. }
  3694. //
  3695. // free the MDLs
  3696. //
  3697. pTracker->BuildInfo.FileBuffer.pCompletionRoutine =
  3698. UlpRestartCacheMdlFree;
  3699. pTracker->BuildInfo.FileBuffer.pContext = pTracker;
  3700. status = UlReadCompleteFileEntry(
  3701. &pTracker->BuildInfo.FileBuffer,
  3702. pTracker->pReadIrp
  3703. );
  3704. }
  3705. end:
  3706. if (!NT_SUCCESS(status))
  3707. {
  3708. UlpCompleteCacheBuild( pTracker, status );
  3709. }
  3710. return STATUS_MORE_PROCESSING_REQUIRED;
  3711. } // UlpRestartCacheMdlRead
  3712. /***************************************************************************++
  3713. Routine Description:
  3714. Completion handler for MDL free IRPs used after reading file data.
  3715. Arguments:
  3716. pDeviceObject - Supplies the device object for the IRP being
  3717. completed.
  3718. pIrp - Supplies the IRP being completed.
  3719. pContext - Supplies the context associated with this request.
  3720. This is actually a PUL_CHUNK_TRACKER.
  3721. Return Value:
  3722. NTSTATUS - STATUS_SUCCESS if IO should continue processing this
  3723. IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
  3724. this IRP.
  3725. --***************************************************************************/
  3726. NTSTATUS
  3727. UlpRestartCacheMdlFree(
  3728. IN PDEVICE_OBJECT pDeviceObject,
  3729. IN PIRP pIrp,
  3730. IN PVOID pContext
  3731. )
  3732. {
  3733. NTSTATUS status;
  3734. PUL_CHUNK_TRACKER pTracker;
  3735. pTracker = (PUL_CHUNK_TRACKER)pContext;
  3736. UlTrace(URI_CACHE, (
  3737. "Http!UlpRestartCacheMdlFree: tracker %p, status %x info %d\n",
  3738. pTracker,
  3739. pIrp->IoStatus.Status,
  3740. (ULONG) pIrp->IoStatus.Information
  3741. ));
  3742. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  3743. status = pIrp->IoStatus.Status;
  3744. if (NT_SUCCESS(status))
  3745. {
  3746. //
  3747. // Update the file offset & bytes remaining. If we've
  3748. // finished this file chunk (bytes remaining is now zero)
  3749. // then advance to the next chunk.
  3750. //
  3751. pTracker->FileOffset.QuadPart += pIrp->IoStatus.Information;
  3752. pTracker->FileBytesRemaining.QuadPart -= pIrp->IoStatus.Information;
  3753. if (pTracker->FileBytesRemaining.QuadPart == 0 )
  3754. {
  3755. UlpIncrementChunkPointer( pTracker );
  3756. }
  3757. //
  3758. // Go back into the loop if there's more to read
  3759. //
  3760. if (IS_REQUEST_COMPLETE(pTracker)) {
  3761. UlpCompleteCacheBuild( pTracker, status );
  3762. } else {
  3763. UL_CALL_PASSIVE(
  3764. &pTracker->WorkItem,
  3765. &UlpBuildCacheEntryWorker
  3766. );
  3767. }
  3768. }
  3769. if (!NT_SUCCESS(status))
  3770. {
  3771. UlpCompleteCacheBuild( pTracker, status );
  3772. }
  3773. return STATUS_MORE_PROCESSING_REQUIRED;
  3774. } // UlpRestartCacheMdlFree
  3775. /***************************************************************************++
  3776. Routine Description:
  3777. This routine gets called when we finish building a cache entry.
  3778. Arguments:
  3779. pTracker - Supplies the tracker to complete.
  3780. Status - Supplies the completion status.
  3781. --***************************************************************************/
  3782. VOID
  3783. UlpCompleteCacheBuild(
  3784. IN PUL_CHUNK_TRACKER pTracker,
  3785. IN NTSTATUS Status
  3786. )
  3787. {
  3788. UlTrace(URI_CACHE, (
  3789. "Http!UlpCompleteCacheBuild: tracker %p, status %08lx\n",
  3790. pTracker,
  3791. Status
  3792. ));
  3793. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  3794. UL_CALL_PASSIVE(
  3795. &pTracker->WorkItem,
  3796. &UlpCompleteCacheBuildWorker
  3797. );
  3798. } // UlpCompleteCacheBuild
  3799. /***************************************************************************++
  3800. Routine Description:
  3801. Called when we finish building a cache entry. If the entry was
  3802. built successfully, we send the response down the wire.
  3803. Arguments:
  3804. pWorkItem - Supplies a pointer to the work item queued. This should
  3805. point to the WORK_ITEM structure embedded in a UL_CHUNK_TRACKER.
  3806. --***************************************************************************/
  3807. VOID
  3808. UlpCompleteCacheBuildWorker(
  3809. IN PUL_WORK_ITEM pWorkItem
  3810. )
  3811. {
  3812. PUL_CHUNK_TRACKER pTracker;
  3813. PUL_URI_CACHE_ENTRY pUriCacheEntry;
  3814. PUL_HTTP_CONNECTION pHttpConnection;
  3815. PUL_COMPLETION_ROUTINE pCompletionRoutine;
  3816. PVOID pCompletionContext;
  3817. ULONG Flags;
  3818. PUL_LOG_DATA_BUFFER pLogData;
  3819. NTSTATUS Status;
  3820. //
  3821. // Sanity check.
  3822. //
  3823. PAGED_CODE();
  3824. pTracker = CONTAINING_RECORD(
  3825. pWorkItem,
  3826. UL_CHUNK_TRACKER,
  3827. WorkItem
  3828. );
  3829. ASSERT( IS_VALID_CHUNK_TRACKER( pTracker ) );
  3830. pUriCacheEntry = pTracker->BuildInfo.pUriEntry;
  3831. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  3832. Flags = pTracker->Flags;
  3833. pHttpConnection = pTracker->pHttpConnection;
  3834. pCompletionRoutine = pTracker->pCompletionRoutine;
  3835. pCompletionContext = pTracker->pCompletionContext;
  3836. //
  3837. // save the logging data pointer before
  3838. // releasing the tracker and its response
  3839. // pointer.
  3840. //
  3841. pLogData = pTracker->pResponse->pLogData;
  3842. if (pLogData)
  3843. {
  3844. //
  3845. // To prevent SendResponse to free our
  3846. // log buffer
  3847. //
  3848. pTracker->pResponse->pLogData = NULL;
  3849. //
  3850. // give the sign that this log data buffer
  3851. // is ready and later there's no need to
  3852. // refresh its content from cache again.
  3853. //
  3854. pLogData->CacheAndSendResponse = TRUE;
  3855. }
  3856. //
  3857. // free the read tracker
  3858. //
  3859. UlpFreeChunkTracker( pTracker );
  3860. //
  3861. // try to put the entry into the hash table
  3862. //
  3863. UlAddCacheEntry(pUriCacheEntry);
  3864. //
  3865. // grab the connection lock (because UlpSendCacheEntry
  3866. // assumes you have it)
  3867. //
  3868. UlAcquireResourceExclusive(&(pHttpConnection->Resource), TRUE);
  3869. //
  3870. // Send the cache entry
  3871. //
  3872. Status = UlpSendCacheEntry(
  3873. pHttpConnection, // connection
  3874. Flags, // flags
  3875. pUriCacheEntry, // cache entry
  3876. pCompletionRoutine, // completion routine
  3877. pCompletionContext, // completion context
  3878. pLogData // corresponding log data
  3879. );
  3880. //
  3881. // get rid of the entry if it didn't work
  3882. //
  3883. if ( !NT_SUCCESS(Status) )
  3884. {
  3885. UlCheckinUriCacheEntry(pUriCacheEntry);
  3886. //
  3887. // Free up the log data buffer (if we passed it
  3888. // into UlpSendCacheEntry)
  3889. //
  3890. if ( pLogData )
  3891. {
  3892. UlDestroyLogDataBuffer(pLogData);
  3893. }
  3894. }
  3895. //
  3896. // done with the connection lock
  3897. //
  3898. UlReleaseResource(&(pHttpConnection->Resource));
  3899. //
  3900. // now that UL_FULL_TRACKER has a reference, we can release
  3901. // our hold on the connection.
  3902. //
  3903. UL_DEREFERENCE_HTTP_CONNECTION(pHttpConnection);
  3904. //
  3905. // if it's not STATUS_PENDING for some reason, complete the request
  3906. //
  3907. if (Status != STATUS_PENDING && pCompletionRoutine != NULL)
  3908. {
  3909. (pCompletionRoutine)(
  3910. pCompletionContext,
  3911. Status,
  3912. 0
  3913. );
  3914. }
  3915. } // UlpCompleteCacheBuildWorker
  3916. /***************************************************************************++
  3917. Routine Description:
  3918. Sends a cache entry down the wire.
  3919. The logging related part of this function below, surely depend on
  3920. the fact that pCompletionContext will be null if this is called for
  3921. pure cache hits (in other words from ULSendCachedResponse) otherwise
  3922. pointer to Irp will be passed down as the pCompletionContext.
  3923. Arguments:
  3924. All the time
  3925. --***************************************************************************/
  3926. NTSTATUS
  3927. UlpSendCacheEntry(
  3928. PUL_HTTP_CONNECTION pHttpConnection,
  3929. ULONG Flags,
  3930. PUL_URI_CACHE_ENTRY pUriCacheEntry,
  3931. PUL_COMPLETION_ROUTINE pCompletionRoutine,
  3932. PVOID pCompletionContext,
  3933. PUL_LOG_DATA_BUFFER pLogData
  3934. )
  3935. {
  3936. NTSTATUS Status = STATUS_SUCCESS;
  3937. PUL_FULL_TRACKER pTracker;
  3938. CCHAR SendIrpStackSize;
  3939. UL_CONN_HDR ConnHeader;
  3940. ULONG VarHeaderGenerated;
  3941. ULONG TotalLength;
  3942. ULONG contentLengthStringLength;
  3943. UCHAR contentLength[MAX_ULONGLONG_STR];
  3944. LARGE_INTEGER liCreationTime;
  3945. //
  3946. // Sanity check
  3947. //
  3948. PAGED_CODE();
  3949. ASSERT( UL_IS_VALID_HTTP_CONNECTION(pHttpConnection) );
  3950. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  3951. ASSERT( UlDbgResourceOwnedExclusive(&pHttpConnection->Resource) );
  3952. UlTrace(URI_CACHE, (
  3953. "Http!UlpSendCacheEntry(httpconn %p, flags %x, uri %p, ...)\n",
  3954. pHttpConnection,
  3955. Flags,
  3956. pUriCacheEntry
  3957. ));
  3958. //
  3959. // init vars so we can cleanup correctly if we jump to the end
  3960. //
  3961. pTracker = NULL;
  3962. //
  3963. // make sure we're still connected
  3964. //
  3965. if (pHttpConnection->UlconnDestroyed) {
  3966. Status = STATUS_CONNECTION_ABORTED;
  3967. goto cleanup;
  3968. }
  3969. ASSERT( pHttpConnection->pRequest );
  3970. //
  3971. // figure out how much space we need for variable headers
  3972. //
  3973. if (!pUriCacheEntry->ContentLengthSpecified &&
  3974. UlNeedToGenerateContentLength(
  3975. pUriCacheEntry->Verb,
  3976. pUriCacheEntry->StatusCode,
  3977. Flags
  3978. ))
  3979. {
  3980. //
  3981. // Autogenerate a content-length header.
  3982. //
  3983. PCHAR pszEnd = UlStrPrintUlonglong(
  3984. (PCHAR) contentLength,
  3985. (ULONGLONG) pUriCacheEntry->ContentLength,
  3986. '\0');
  3987. contentLengthStringLength = DIFF(pszEnd - (PCHAR) contentLength);
  3988. }
  3989. else
  3990. {
  3991. //
  3992. // Either we cannot or do not need to autogenerate a
  3993. // content-length header.
  3994. //
  3995. contentLength[0] = '\0';
  3996. contentLengthStringLength = 0;
  3997. }
  3998. ConnHeader = UlChooseConnectionHeader(
  3999. pHttpConnection->pRequest->Version,
  4000. (BOOLEAN)(Flags & HTTP_SEND_RESPONSE_FLAG_DISCONNECT)
  4001. );
  4002. //
  4003. // create tracker
  4004. //
  4005. SendIrpStackSize =
  4006. pHttpConnection->pConnection->ConnectionObject.pDeviceObject->StackSize;
  4007. if (SendIrpStackSize > DEFAULT_MAX_IRP_STACK_SIZE)
  4008. {
  4009. pTracker = UlpAllocateCacheTracker(SendIrpStackSize);
  4010. }
  4011. else
  4012. {
  4013. pTracker = pHttpConnection->pRequest->pTracker;
  4014. }
  4015. if (pTracker) {
  4016. //
  4017. // init tracker
  4018. //
  4019. pTracker->pUriEntry = pUriCacheEntry;
  4020. UL_REFERENCE_HTTP_CONNECTION(pHttpConnection);
  4021. pTracker->pHttpConnection = pHttpConnection;
  4022. UL_REFERENCE_INTERNAL_REQUEST(pHttpConnection->pRequest);
  4023. pTracker->pRequest = pHttpConnection->pRequest;
  4024. pTracker->pCompletionRoutine = pCompletionRoutine;
  4025. pTracker->pCompletionContext = pCompletionContext;
  4026. pTracker->Flags = Flags;
  4027. pTracker->pLogData = NULL;
  4028. //
  4029. // build MDLs for send
  4030. //
  4031. ASSERT(pUriCacheEntry->pResponseMdl != NULL);
  4032. MmInitializeMdl(
  4033. pTracker->pMdlFixedHeaders,
  4034. (PCHAR) MmGetMdlVirtualAddress(pUriCacheEntry->pResponseMdl) + pUriCacheEntry->ContentLength,
  4035. pUriCacheEntry->HeaderLength
  4036. );
  4037. IoBuildPartialMdl(
  4038. pUriCacheEntry->pResponseMdl,
  4039. pTracker->pMdlFixedHeaders,
  4040. (PCHAR) MmGetMdlVirtualAddress(pUriCacheEntry->pResponseMdl) + pUriCacheEntry->ContentLength,
  4041. pUriCacheEntry->HeaderLength
  4042. );
  4043. //
  4044. // generate variable headers
  4045. // and build a MDL for them
  4046. //
  4047. UlGenerateVariableHeaders(
  4048. ConnHeader,
  4049. contentLength,
  4050. contentLengthStringLength,
  4051. pTracker->pAuxiliaryBuffer,
  4052. &VarHeaderGenerated,
  4053. &liCreationTime
  4054. );
  4055. ASSERT( VarHeaderGenerated <= g_UlMaxVariableHeaderSize );
  4056. ASSERT( VarHeaderGenerated <= pTracker->AuxilaryBufferLength );
  4057. if (0 == pUriCacheEntry->CreationTime.QuadPart)
  4058. {
  4059. //
  4060. // If we were unable to capture the Last-Modified time from the
  4061. // original item, use the time we created this cache entry.
  4062. //
  4063. pUriCacheEntry->CreationTime.QuadPart = liCreationTime.QuadPart;
  4064. }
  4065. pTracker->pMdlVariableHeaders->ByteCount = VarHeaderGenerated;
  4066. pTracker->pMdlFixedHeaders->Next = pTracker->pMdlVariableHeaders;
  4067. //
  4068. // build MDL for body
  4069. //
  4070. if (pUriCacheEntry->ContentLength)
  4071. {
  4072. MmInitializeMdl(
  4073. pTracker->pMdlContent,
  4074. MmGetMdlVirtualAddress(pUriCacheEntry->pResponseMdl),
  4075. pUriCacheEntry->ContentLength
  4076. );
  4077. IoBuildPartialMdl(
  4078. pUriCacheEntry->pResponseMdl,
  4079. pTracker->pMdlContent,
  4080. MmGetMdlVirtualAddress(pUriCacheEntry->pResponseMdl),
  4081. pUriCacheEntry->ContentLength
  4082. );
  4083. pTracker->pMdlVariableHeaders->Next = pTracker->pMdlContent;
  4084. }
  4085. else
  4086. {
  4087. pTracker->pMdlVariableHeaders->Next = NULL;
  4088. }
  4089. //
  4090. // We have to log this cache hit. time to allocate a log
  4091. // data buffer and set its request pointer. But only if we
  4092. // had the logging data cached before.
  4093. //
  4094. if (pUriCacheEntry->LoggingEnabled)
  4095. {
  4096. //
  4097. // If this was a user call (UlCacheAndSendResponse) then
  4098. // pCompletionContext will be the pIrp pointer otherwise
  4099. // NULL. As for the build&send cache requests we already
  4100. // allocated a log buffer let's use that one to prevent
  4101. // unnecessary reallocation and copying. Also the pLogData
  4102. // pointer will be NULL if this is a pure cache hit but not
  4103. // CacheAndSend.
  4104. //
  4105. // REVIEW: AliTu mentioned that checking there might be a better
  4106. // REVIEW: way to check and see if this is a "build cache entry"
  4107. // REVIEW: case than checking if pCompletionContext is non-NULL.
  4108. //
  4109. if (pCompletionContext)
  4110. {
  4111. ASSERT(pLogData);
  4112. pTracker->pLogData = pLogData;
  4113. }
  4114. else
  4115. {
  4116. PUL_INTERNAL_REQUEST pRequest = pHttpConnection->pRequest;
  4117. //
  4118. // Pure cache hit
  4119. //
  4120. ASSERT(pLogData==NULL);
  4121. pTracker->pLogData = &pRequest->LogData;
  4122. Status = UlAllocateLogDataBuffer(
  4123. pTracker->pLogData,
  4124. pRequest,
  4125. pUriCacheEntry->ConfigInfo.pLoggingConfig
  4126. );
  4127. ASSERT(NT_SUCCESS(Status));
  4128. }
  4129. }
  4130. //
  4131. // go go go!
  4132. //
  4133. TotalLength = pUriCacheEntry->HeaderLength;
  4134. TotalLength += pUriCacheEntry->ContentLength;
  4135. TotalLength += VarHeaderGenerated;
  4136. Status = UlSendData(
  4137. pTracker->pHttpConnection->pConnection,
  4138. pTracker->pMdlFixedHeaders,
  4139. TotalLength,
  4140. &UlpCompleteSendCacheEntry,
  4141. pTracker,
  4142. pTracker->pSendIrp,
  4143. &pTracker->IrpContext,
  4144. (BOOLEAN)((Flags & HTTP_SEND_RESPONSE_FLAG_DISCONNECT) != 0)
  4145. );
  4146. }
  4147. else
  4148. {
  4149. Status = STATUS_INSUFFICIENT_RESOURCES;
  4150. }
  4151. cleanup:
  4152. //
  4153. // clean up the tracker if we don't need it
  4154. //
  4155. if (!NT_SUCCESS(Status)) {
  4156. if (pTracker) {
  4157. if (pLogData == NULL && pTracker->pLogData)
  4158. {
  4159. //
  4160. // if we allocated a Log Data Buffer, we need to free it;
  4161. // Caller will free if it was passed in.
  4162. //
  4163. UlDestroyLogDataBuffer(pTracker->pLogData);
  4164. }
  4165. UlpFreeCacheTracker(pTracker);
  4166. UL_DEREFERENCE_HTTP_CONNECTION(pHttpConnection);
  4167. UL_DEREFERENCE_INTERNAL_REQUEST(pHttpConnection->pRequest);
  4168. }
  4169. }
  4170. UlTrace(URI_CACHE, (
  4171. "Http!UlpSendCacheEntry status = %08x\n",
  4172. Status
  4173. ));
  4174. return Status;
  4175. } // UlpSendCacheEntry
  4176. /***************************************************************************++
  4177. Routine Description:
  4178. Called when we finish sending data to the client. Just queues to
  4179. a worker that runs at passive level.
  4180. Arguments:
  4181. pCompletionContext - pointer to UL_FULL_TRACKER
  4182. Status - status of send
  4183. Information - Bytes transferred.
  4184. --***************************************************************************/
  4185. VOID
  4186. UlpCompleteSendCacheEntry(
  4187. IN PVOID pCompletionContext,
  4188. IN NTSTATUS Status,
  4189. IN ULONG_PTR Information
  4190. )
  4191. {
  4192. PUL_FULL_TRACKER pTracker;
  4193. pTracker = (PUL_FULL_TRACKER) pCompletionContext;
  4194. pTracker->IoStatus.Status = Status;
  4195. pTracker->IoStatus.Information = Information;
  4196. UlTrace(URI_CACHE,
  4197. ("UlpCompleteSendCacheEntry: "
  4198. "tracker=%p, status = %x, transferred %d bytes\n",
  4199. pTracker, Status, (int) Information));
  4200. //
  4201. // invoke completion routine
  4202. //
  4203. if (pTracker->pCompletionRoutine != NULL)
  4204. {
  4205. (pTracker->pCompletionRoutine)(
  4206. pTracker->pCompletionContext,
  4207. Status,
  4208. Information
  4209. );
  4210. }
  4211. UL_QUEUE_WORK_ITEM(
  4212. &pTracker->WorkItem,
  4213. &UlpCompleteSendCacheEntryWorker
  4214. );
  4215. }
  4216. /***************************************************************************++
  4217. Routine Description:
  4218. Called when we finish sending cached data to the client. This routine
  4219. frees the UL_FULL_TRACKER, and calls the completion routine originally
  4220. passed to UlCacheAndSendResponse.
  4221. Arguments:
  4222. pWorkItem - Supplies a pointer to the work item queued. This should
  4223. point to the WORK_ITEM structure embedded in a UL_FULL_TRACKER.
  4224. --***************************************************************************/
  4225. VOID
  4226. UlpCompleteSendCacheEntryWorker(
  4227. IN PUL_WORK_ITEM pWorkItem
  4228. )
  4229. {
  4230. PUL_FULL_TRACKER pTracker;
  4231. PUL_HTTP_CONNECTION pHttpConnection;
  4232. PUL_INTERNAL_REQUEST pRequest;
  4233. ULONG Flags;
  4234. PUL_URI_CACHE_ENTRY pUriCacheEntry;
  4235. NTSTATUS Status;
  4236. KIRQL OldIrql;
  4237. //
  4238. // Sanity check
  4239. //
  4240. PAGED_CODE();
  4241. pTracker = CONTAINING_RECORD(
  4242. pWorkItem,
  4243. UL_FULL_TRACKER,
  4244. WorkItem
  4245. );
  4246. UlTrace(URI_CACHE, (
  4247. "Http!UlpCompleteSendCacheEntryWorker(pTracker %p)\n",
  4248. pTracker
  4249. ));
  4250. //
  4251. // pull context out of tracker
  4252. //
  4253. pHttpConnection = pTracker->pHttpConnection;
  4254. ASSERT( UL_IS_VALID_HTTP_CONNECTION(pHttpConnection) );
  4255. pRequest = pTracker->pRequest;
  4256. ASSERT( UL_IS_VALID_INTERNAL_REQUEST(pRequest) );
  4257. Flags = pTracker->Flags;
  4258. pUriCacheEntry = pTracker->pUriEntry;
  4259. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  4260. Status = pTracker->IoStatus.Status;
  4261. //
  4262. // If the send failed and we did't ask UlSendData() to disconnect
  4263. // the connection, then initiate an *abortive* disconnect.
  4264. //
  4265. if ((NT_SUCCESS(Status) == FALSE) &&
  4266. ((pTracker->Flags & HTTP_SEND_RESPONSE_FLAG_DISCONNECT) == 0))
  4267. {
  4268. UlTrace(URI_CACHE, (
  4269. "Http!UlpCompleteSendCacheEntryWorker(pTracker %p) Closing connection\n",
  4270. pTracker
  4271. ));
  4272. UlCloseConnection(
  4273. pHttpConnection->pConnection,
  4274. TRUE,
  4275. NULL,
  4276. NULL
  4277. );
  4278. }
  4279. //
  4280. // Stop MinKBSec timer and start Connection Idle timer
  4281. //
  4282. UlLockTimeoutInfo(
  4283. &pHttpConnection->TimeoutInfo,
  4284. &OldIrql
  4285. );
  4286. UlResetConnectionTimer(
  4287. &pHttpConnection->TimeoutInfo,
  4288. TimerMinKBSec
  4289. );
  4290. UlSetConnectionTimer(
  4291. &pHttpConnection->TimeoutInfo,
  4292. TimerConnectionIdle
  4293. );
  4294. UlUnlockTimeoutInfo(
  4295. &pHttpConnection->TimeoutInfo,
  4296. OldIrql
  4297. );
  4298. UlEvaluateTimerState(
  4299. &pHttpConnection->TimeoutInfo
  4300. );
  4301. //
  4302. // unmap the FixedHeaders and Content MDLs if necessary
  4303. //
  4304. if (pTracker->pMdlFixedHeaders->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
  4305. {
  4306. MmUnmapLockedPages(
  4307. pTracker->pMdlFixedHeaders->MappedSystemVa,
  4308. pTracker->pMdlFixedHeaders
  4309. );
  4310. }
  4311. if (pTracker->pMdlContent->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
  4312. {
  4313. MmUnmapLockedPages(
  4314. pTracker->pMdlContent->MappedSystemVa,
  4315. pTracker->pMdlContent
  4316. );
  4317. }
  4318. //
  4319. // Do the logging before cleaning up the tracker.
  4320. //
  4321. if (pTracker->pLogData)
  4322. {
  4323. //
  4324. // Call the cache logger. It will copy over the
  4325. // cached logging data for us, and log it out.
  4326. //
  4327. UlLogHttpCacheHit( pTracker );
  4328. }
  4329. //
  4330. // Kick the parser into action only if this cache response comes from
  4331. // the UlpCompleteCacheBuildWorker path, in which case UlpSendCacheEntry
  4332. // is called with a non-null pCompletionContext.
  4333. //
  4334. if (pTracker->pCompletionContext)
  4335. {
  4336. UlResumeParsing(pHttpConnection);
  4337. }
  4338. //
  4339. // clean up tracker
  4340. //
  4341. UlpFreeCacheTracker(pTracker);
  4342. //
  4343. // deref cache entry
  4344. //
  4345. UlCheckinUriCacheEntry(pUriCacheEntry);
  4346. //
  4347. // deref the internal request
  4348. //
  4349. UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
  4350. //
  4351. // deref http connection
  4352. //
  4353. UL_DEREFERENCE_HTTP_CONNECTION(pHttpConnection);
  4354. } // UlpCompleteSendCacheEntryWorker
  4355. /***************************************************************************++
  4356. Routine Description:
  4357. Allocates a non-paged UL_FULL_TRACKER used as context for sending
  4358. cached content to the client.
  4359. CODEWORK: this routine should probably do all tracker init.
  4360. Arguments:
  4361. SendIrpStackSize - Size of the stack for the send IRP
  4362. Return Values:
  4363. Either a pointer to a UL_FULL_TRACKER, or NULL if it couldn't be made.
  4364. --***************************************************************************/
  4365. PUL_FULL_TRACKER
  4366. UlpAllocateCacheTracker(
  4367. IN CCHAR SendIrpStackSize
  4368. )
  4369. {
  4370. PUL_FULL_TRACKER pTracker;
  4371. USHORT SendIrpSize;
  4372. ULONG CacheTrackerSize;
  4373. //
  4374. // Sanity check
  4375. //
  4376. PAGED_CODE();
  4377. ASSERT(SendIrpStackSize > DEFAULT_MAX_IRP_STACK_SIZE);
  4378. SendIrpSize = (USHORT)ALIGN_UP(IoSizeOfIrp(SendIrpStackSize), PVOID);
  4379. //
  4380. // No need to allocate space for the entire auxiliary buffer in this
  4381. // case since this is one-time deal only.
  4382. //
  4383. CacheTrackerSize = ALIGN_UP(sizeof(UL_FULL_TRACKER), PVOID) +
  4384. SendIrpSize +
  4385. g_UlMaxVariableHeaderSize +
  4386. g_UlFixedHeadersMdlLength +
  4387. g_UlVariableHeadersMdlLength +
  4388. g_UlContentMdlLength;
  4389. pTracker = (PUL_FULL_TRACKER)UL_ALLOCATE_POOL(
  4390. NonPagedPool,
  4391. CacheTrackerSize,
  4392. UL_FULL_TRACKER_POOL_TAG
  4393. );
  4394. if (pTracker)
  4395. {
  4396. pTracker->Signature = UL_FULL_TRACKER_POOL_TAG;
  4397. pTracker->IsFromLookaside = FALSE;
  4398. pTracker->IsFromRequest = FALSE;
  4399. pTracker->AuxilaryBufferLength = g_UlMaxVariableHeaderSize;
  4400. UlInitializeFullTrackerPool(pTracker, SendIrpStackSize);
  4401. }
  4402. UlTrace( URI_CACHE, (
  4403. "Http!UlpAllocateCacheTracker: tracker %p\n",
  4404. pTracker
  4405. ));
  4406. return pTracker;
  4407. } // UlpAllocateCacheTracker
  4408. /***************************************************************************++
  4409. Routine Description:
  4410. Frees a UL_FULL_TRACKER.
  4411. CODEWORK: this routine should probably do all the destruction
  4412. Arguments:
  4413. pTracker - Specifies the UL_FULL_TRACKER to free.
  4414. --***************************************************************************/
  4415. VOID
  4416. UlpFreeCacheTracker(
  4417. IN PUL_FULL_TRACKER pTracker
  4418. )
  4419. {
  4420. UlTrace(URI_CACHE, (
  4421. "Http!UlpFreeCacheTracker: tracker %p\n",
  4422. pTracker
  4423. ));
  4424. ASSERT(pTracker);
  4425. ASSERT(IS_VALID_FULL_TRACKER(pTracker));
  4426. if (pTracker->IsFromRequest == FALSE)
  4427. {
  4428. if (pTracker->IsFromLookaside)
  4429. {
  4430. pTracker->Signature = MAKE_FREE_TAG(UL_FULL_TRACKER_POOL_TAG);
  4431. UlPplFreeFullTracker( pTracker );
  4432. }
  4433. else
  4434. {
  4435. UL_FREE_POOL_WITH_SIG( pTracker, UL_FULL_TRACKER_POOL_TAG );
  4436. }
  4437. }
  4438. }
  4439. /***************************************************************************++
  4440. Routine Description:
  4441. A helper function that allocates an MDL for a range of memory, and
  4442. locks it down. UlpSendCacheEntry uses these MDLs to make sure the
  4443. (normally paged) cache entries don't get paged out when TDI is
  4444. sending them.
  4445. Arguments:
  4446. VirtualAddress - address of the memory
  4447. Length - length of the memory
  4448. Operation - either IoWriteAcess or IoReadAccess
  4449. --***************************************************************************/
  4450. PMDL
  4451. UlpAllocateLockedMdl(
  4452. IN PVOID VirtualAddress,
  4453. IN ULONG Length,
  4454. IN LOCK_OPERATION Operation
  4455. )
  4456. {
  4457. PMDL pMdl = NULL;
  4458. NTSTATUS Status = STATUS_SUCCESS;
  4459. //
  4460. // Sanity check
  4461. //
  4462. PAGED_CODE();
  4463. __try {
  4464. pMdl = UlAllocateMdl(
  4465. VirtualAddress, // VirtualAddress
  4466. Length, // Length
  4467. FALSE, // SecondaryBuffer
  4468. FALSE, // ChargeQuota
  4469. NULL // Irp
  4470. );
  4471. if (pMdl) {
  4472. MmProbeAndLockPages(
  4473. pMdl, // MDL
  4474. KernelMode, // AccessMode
  4475. Operation // Operation
  4476. );
  4477. }
  4478. }
  4479. __except( UL_EXCEPTION_FILTER() )
  4480. {
  4481. //
  4482. // Can this really happen?
  4483. //
  4484. Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
  4485. UlFreeMdl(pMdl);
  4486. pMdl = NULL;
  4487. }
  4488. if (!pMdl || !NT_SUCCESS(Status)) {
  4489. UlTrace(URI_CACHE, (
  4490. "Http!UlpAllocateLockedMdl failed %08x\n",
  4491. Status
  4492. ));
  4493. }
  4494. return pMdl;
  4495. }
  4496. /***************************************************************************++
  4497. Routine Description:
  4498. Unlocks and frees an MDL allocated with UlpAllocateLockedMdl.
  4499. Arguments:
  4500. pMdl - the MDL to free
  4501. --***************************************************************************/
  4502. VOID
  4503. UlpFreeLockedMdl(
  4504. PMDL pMdl
  4505. )
  4506. {
  4507. //
  4508. // Sanity check
  4509. //
  4510. ASSERT( IS_MDL_LOCKED(pMdl) );
  4511. MmUnlockPages(pMdl);
  4512. UlFreeMdl(pMdl);
  4513. }
  4514. /***************************************************************************++
  4515. Routine Description:
  4516. A helper function that initializes an MDL for a range of memory, and
  4517. locks it down. UlpSendCacheEntry uses these MDLs to make sure the
  4518. (normally paged) cache entries don't get paged out when TDI is
  4519. sending them.
  4520. Arguments:
  4521. pMdl - memory descriptor for the MDL to initialize
  4522. VirtualAddress - address of the memory
  4523. Length - length of the memory
  4524. Operation - either IoWriteAcess or IoReadAccess
  4525. --***************************************************************************/
  4526. NTSTATUS
  4527. UlpInitializeAndLockMdl(
  4528. IN PMDL pMdl,
  4529. IN PVOID VirtualAddress,
  4530. IN ULONG Length,
  4531. IN LOCK_OPERATION Operation
  4532. )
  4533. {
  4534. NTSTATUS Status;
  4535. //
  4536. // Sanity check
  4537. //
  4538. PAGED_CODE();
  4539. Status = STATUS_SUCCESS;
  4540. __try {
  4541. MmInitializeMdl(
  4542. pMdl,
  4543. VirtualAddress,
  4544. Length
  4545. );
  4546. MmProbeAndLockPages(
  4547. pMdl, // MDL
  4548. KernelMode, // AccessMode
  4549. Operation // Operation
  4550. );
  4551. }
  4552. __except( UL_EXCEPTION_FILTER() )
  4553. {
  4554. //
  4555. // Can this really happen?
  4556. //
  4557. Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
  4558. UlTrace(URI_CACHE, (
  4559. "UL!UlpInitializeAndLockMdl failed %08x\n",
  4560. Status
  4561. ));
  4562. }
  4563. return Status;
  4564. }
  4565. /***************************************************************************++
  4566. Routine Description:
  4567. Checks the request to see if it has any of the following headers:
  4568. If-Modified-Since:
  4569. If-Match:
  4570. If-None-Match:
  4571. If so, we see if we can skip the sending of the full item. If we can skip,
  4572. we send back the apropriate response of either 304 (not modified) or
  4573. set the parser state to send back a 412 (precondition not met).
  4574. Arguments:
  4575. pRequest - The request to check
  4576. pUriCacheEntry - The cache entry being requested
  4577. Returns:
  4578. 0 Send cannot be skipped; continue with sending the cache entry.
  4579. 304 Send can be skipped. 304 response sent. NOTE: pRequest may be
  4580. invalid on return.
  4581. 412 Send can be skipped. pRequest->ParseState set to ParseErrorState with
  4582. pRequest->ErrorCode set to UlErrorPreconditionFailed (412)
  4583. --***************************************************************************/
  4584. ULONG
  4585. UlpCheckCacheControlHeaders(
  4586. PUL_INTERNAL_REQUEST pRequest,
  4587. PUL_URI_CACHE_ENTRY pUriCacheEntry
  4588. )
  4589. {
  4590. ULONG RetStatus = 0; // Assume can't skip.
  4591. BOOLEAN fIfNoneMatchPassed = TRUE;
  4592. BOOLEAN fSkipIfModifiedSince = FALSE;
  4593. LARGE_INTEGER liModifiedSince;
  4594. LARGE_INTEGER liUnmodifiedSince;
  4595. LARGE_INTEGER liNow;
  4596. ULONG BytesSent = 0;
  4597. ASSERT( UL_IS_VALID_INTERNAL_REQUEST(pRequest) );
  4598. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  4599. //
  4600. // 1. Check If-Match
  4601. //
  4602. if ( pRequest->HeaderValid[HttpHeaderIfMatch] )
  4603. {
  4604. if ( !FindInETagList( pUriCacheEntry->pETag,
  4605. pRequest->Headers[HttpHeaderIfMatch].pHeader,
  4606. FALSE) )
  4607. {
  4608. // Match failed.
  4609. goto PreconditionFailed;
  4610. }
  4611. }
  4612. //
  4613. // 2. Check If-None-Match
  4614. //
  4615. if ( pRequest->HeaderValid[HttpHeaderIfNoneMatch] )
  4616. {
  4617. if ( FindInETagList( pUriCacheEntry->pETag,
  4618. pRequest->Headers[HttpHeaderIfNoneMatch].pHeader,
  4619. TRUE) )
  4620. {
  4621. // ETag found on list.
  4622. fIfNoneMatchPassed = FALSE;
  4623. }
  4624. else
  4625. {
  4626. //
  4627. // Header present and ETag not found on list. This modifies
  4628. // the semantic of the If-Modified-Since header; Namely,
  4629. // If-None-Match takes precidence over If-Modified-Since.
  4630. //
  4631. fSkipIfModifiedSince = TRUE;
  4632. }
  4633. }
  4634. //
  4635. // 3. Check If-Modified-Since
  4636. //
  4637. if ( !fSkipIfModifiedSince &&
  4638. pRequest->HeaderValid[HttpHeaderIfModifiedSince] )
  4639. {
  4640. if ( StringTimeToSystemTime(
  4641. (const PSTR) pRequest->Headers[HttpHeaderIfModifiedSince].pHeader,
  4642. &liModifiedSince) )
  4643. {
  4644. //
  4645. // If the cache entry was created before the
  4646. // time specified in the If-Modified-Since header, we
  4647. // can return a 304 (Not Modified) status.
  4648. //
  4649. if ( pUriCacheEntry->CreationTime.QuadPart <= liModifiedSince.QuadPart )
  4650. {
  4651. //
  4652. // Check if the time specified in the request is
  4653. // greater than the current time (i.e., Invalid). If it is,
  4654. // ignore the If-Modified-Since header.
  4655. //
  4656. KeQuerySystemTime(&liNow);
  4657. if ( liModifiedSince.QuadPart < liNow.QuadPart )
  4658. {
  4659. // Valid time.
  4660. goto NotModified;
  4661. }
  4662. }
  4663. }
  4664. //
  4665. // If-Modified-Since overrides If-None-Match.
  4666. //
  4667. fIfNoneMatchPassed = TRUE;
  4668. }
  4669. if ( !fIfNoneMatchPassed )
  4670. {
  4671. //
  4672. // We could either skip the If-Modified-Since header, or it
  4673. // was not present, AND we did not pass the If-None-Match
  4674. // predicate. Since this is a "GET" or "HEAD" request (because
  4675. // that's all we cache, we should return 304. If this were
  4676. // any other verb, we should return 412.
  4677. //
  4678. ASSERT( (HttpVerbGET == pRequest->Verb) || (HttpVerbHEAD == pRequest->Verb) );
  4679. goto NotModified;
  4680. }
  4681. //
  4682. // 4. Check If-Unmodified-Since
  4683. //
  4684. if ( pRequest->HeaderValid[HttpHeaderIfUnmodifiedSince] )
  4685. {
  4686. if ( StringTimeToSystemTime(
  4687. (const PSTR) pRequest->Headers[HttpHeaderIfUnmodifiedSince].pHeader,
  4688. &liUnmodifiedSince) )
  4689. {
  4690. //
  4691. // If the cache entry was created after the time
  4692. // specified in the If-Unmodified-Since header, we
  4693. // MUST return a 412 (Precondition Failed) status.
  4694. //
  4695. if ( pUriCacheEntry->CreationTime.QuadPart > liUnmodifiedSince.QuadPart )
  4696. {
  4697. goto PreconditionFailed;
  4698. }
  4699. }
  4700. }
  4701. Cleanup:
  4702. return RetStatus;
  4703. NotModified:
  4704. RetStatus = 304;
  4705. //
  4706. // Send 304 (Not Modified) response
  4707. //
  4708. BytesSent =
  4709. UlSendSimpleStatus(
  4710. pRequest,
  4711. UlStatusNotModified
  4712. );
  4713. //
  4714. // Update the server to client bytes sent.
  4715. // The logging & perf counters will use it.
  4716. //
  4717. pRequest->BytesSent += BytesSent;
  4718. goto Cleanup;
  4719. PreconditionFailed:
  4720. RetStatus = 412;
  4721. goto Cleanup;
  4722. }
  4723. /***************************************************************************++
  4724. Routine Description:
  4725. Checks the cached response against the "Accept:" header in the request
  4726. to see if it can satisfy the requested Content-Type(s).
  4727. (Yes, I know this is really gross...I encourage anyone to find a better
  4728. way to parse this! --EricSten)
  4729. Arguments:
  4730. pRequest - The request to check.
  4731. pUriCacheEntry - The cache entry that might possibly match.
  4732. Returns:
  4733. TRUE At least one of the possible formats matched the Content-Type
  4734. of the cached entry.
  4735. FALSE None of the requested types matched the Content-Type of the
  4736. cached entry.
  4737. --***************************************************************************/
  4738. BOOLEAN
  4739. UlpIsAcceptHeaderOk(
  4740. PUL_INTERNAL_REQUEST pRequest,
  4741. PUL_URI_CACHE_ENTRY pUriCacheEntry
  4742. )
  4743. {
  4744. BOOLEAN bRet = TRUE;
  4745. ULONG Len;
  4746. PUCHAR pHdr;
  4747. PUCHAR pSubType;
  4748. PUCHAR pTmp;
  4749. PUL_CONTENT_TYPE pContentType;
  4750. if ( pRequest->HeaderValid[HttpHeaderAccept] &&
  4751. (pRequest->Headers[HttpHeaderAccept].HeaderLength > 0) )
  4752. {
  4753. Len = pRequest->Headers[HttpHeaderAccept].HeaderLength;
  4754. pHdr = pRequest->Headers[HttpHeaderAccept].pHeader;
  4755. pContentType = &pUriCacheEntry->ContentType;
  4756. //
  4757. // First, do "fast-path" check; see if "*/*" is anywhere in the header.
  4758. //
  4759. pTmp = (PUCHAR) strstr( (const char*) pHdr, "*/*" );
  4760. //
  4761. // If we found "*/*" and its either at the beginning of the line,
  4762. // the end of the line, or surrounded by either ' ' or ',', then
  4763. // it's really a wildcard.
  4764. //
  4765. if ((pTmp != NULL) &&
  4766. ((pTmp == pHdr) ||
  4767. IS_HTTP_LWS(pTmp[-1]) ||
  4768. (pTmp[-1] == ',')) &&
  4769. ((pTmp[3] == '\0') ||
  4770. IS_HTTP_LWS(pTmp[3]) ||
  4771. (pTmp[3] == ',')))
  4772. {
  4773. goto end;
  4774. }
  4775. //
  4776. // Wildcard not found; continue with slow path
  4777. //
  4778. while (Len)
  4779. {
  4780. if (pContentType->TypeLen > Len)
  4781. {
  4782. // Bad! No more string left...Bail out.
  4783. bRet = FALSE;
  4784. goto end;
  4785. }
  4786. if ( (pContentType->TypeLen == RtlCompareMemory(
  4787. pHdr,
  4788. pContentType->Type,
  4789. pContentType->TypeLen
  4790. )) &&
  4791. ( '/' == pHdr[pContentType->TypeLen] ) )
  4792. {
  4793. //
  4794. // Found matching type; check subtype
  4795. //
  4796. pSubType = &pHdr[pContentType->TypeLen + 1];
  4797. if ( '*' == *pSubType )
  4798. {
  4799. // Subtype wildcard match!
  4800. goto end;
  4801. }
  4802. else
  4803. {
  4804. if ( pContentType->SubTypeLen >
  4805. (Len - ( pContentType->TypeLen + 1 )) )
  4806. {
  4807. // Bad! No more string left...Bail out.
  4808. bRet = FALSE;
  4809. goto end;
  4810. }
  4811. if ( pContentType->SubTypeLen == RtlCompareMemory(
  4812. pSubType,
  4813. pContentType->SubType,
  4814. pContentType->SubTypeLen
  4815. ) &&
  4816. !IS_HTTP_TOKEN(pSubType[pContentType->SubTypeLen]) )
  4817. {
  4818. // Subtype exact match!
  4819. goto end;
  4820. }
  4821. }
  4822. }
  4823. //
  4824. // Didn't match this one; advance to next Content-Type in the Accept field
  4825. //
  4826. pTmp = (PUCHAR) strchr( (const char *) pHdr, ',' );
  4827. if (pTmp)
  4828. {
  4829. // Found a comma; step over it and any whitespace.
  4830. ASSERT ( Len > DIFF(pTmp - pHdr));
  4831. Len -= (DIFF(pTmp - pHdr) +1);
  4832. pHdr = (pTmp+1);
  4833. while( Len && IS_HTTP_LWS(*pHdr) )
  4834. {
  4835. pHdr++;
  4836. Len--;
  4837. }
  4838. } else
  4839. {
  4840. // No more content-types; bail.
  4841. bRet = FALSE;
  4842. goto end;
  4843. }
  4844. } // walk list of things
  4845. //
  4846. // Walked all Accept items and didn't find a match.
  4847. //
  4848. bRet = FALSE;
  4849. }
  4850. end:
  4851. return bRet;
  4852. } // UlpIsAcceptHeaderOk
  4853. /***************************************************************************++
  4854. Routine Description:
  4855. parses a content-type into its type and subtype components.
  4856. Arguments:
  4857. pStr String containing valid content type
  4858. StrLen Length of string (in bytes)
  4859. pContentType pointer to user provided UL_CONTENT_TYPE structure
  4860. --***************************************************************************/
  4861. VOID
  4862. UlpGetTypeAndSubType(
  4863. PSTR pStr,
  4864. ULONG StrLen,
  4865. PUL_CONTENT_TYPE pContentType
  4866. )
  4867. {
  4868. PCHAR pSlash;
  4869. ASSERT(pStr && StrLen);
  4870. ASSERT(pContentType);
  4871. pSlash = strchr(pStr, '/');
  4872. if (NULL == pSlash)
  4873. {
  4874. //
  4875. // BAD! content types should always have a slash!
  4876. //
  4877. ASSERT( NULL != pSlash );
  4878. return;
  4879. }
  4880. pContentType->TypeLen = (ULONG) MIN( (pSlash - pStr), MAX_TYPE_LENGTH );
  4881. RtlCopyMemory(
  4882. pContentType->Type,
  4883. pStr,
  4884. pContentType->TypeLen
  4885. );
  4886. ASSERT( StrLen > (pContentType->TypeLen + 1) );
  4887. pContentType->SubTypeLen = MIN( (StrLen - (pContentType->TypeLen + 1)), MAX_SUBTYPE_LENGTH );
  4888. RtlCopyMemory(
  4889. pContentType->SubType,
  4890. pSlash+1,
  4891. pContentType->SubTypeLen
  4892. );
  4893. } // UlpGetTypeAndSubType