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.

1461 lines
36 KiB

  1. /*++
  2. Copyright (c) 2000-2001 Microsoft Corporation
  3. Module Name:
  4. fastio.cxx
  5. Abstract:
  6. This module implements the fast I/O logic of HTTP.SYS.
  7. Author:
  8. Chun Ye (chunye) 09-Dec-2000
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. //
  13. // Private globals.
  14. //
  15. #ifdef ALLOC_PRAGMA
  16. #pragma alloc_text( PAGE, UlFastIoDeviceControl )
  17. #pragma alloc_text( PAGE, UlSendHttpResponseFastIo )
  18. #pragma alloc_text( PAGE, UlReceiveHttpRequestFastIo )
  19. #pragma alloc_text( PAGE, UlpFastSendHttpResponse )
  20. #pragma alloc_text( PAGE, UlpFastReceiveHttpRequest )
  21. #pragma alloc_text( PAGE, UlpFastCopyHttpRequest )
  22. #endif // ALLOC_PRAGMA
  23. #if 0
  24. NOT PAGEABLE -- UlpRestartFastSendHttpResponse
  25. NOT PAGEABLE -- UlpFastSendCompleteWorker
  26. #endif
  27. FAST_IO_DISPATCH UlFastIoDispatch =
  28. {
  29. sizeof(FAST_IO_DISPATCH), // SizeOfFastIoDispatch
  30. NULL, // FastIoCheckIfPossible
  31. NULL, // FastIoRead
  32. NULL, // FastIoWrite
  33. NULL, // FastIoQueryBasicInfo
  34. NULL, // FastIoQueryStandardInfo
  35. NULL, // FastIoLock
  36. NULL, // FastIoUnlockSingle
  37. NULL, // FastIoUnlockAll
  38. NULL, // FastIoUnlockAllByKey
  39. UlFastIoDeviceControl // FastIoDeviceControl
  40. };
  41. //
  42. // Public functions.
  43. //
  44. BOOLEAN
  45. UlFastIoDeviceControl(
  46. IN PFILE_OBJECT pFileObject,
  47. IN BOOLEAN Wait,
  48. IN PVOID pInputBuffer OPTIONAL,
  49. IN ULONG InputBufferLength,
  50. OUT PVOID pOutputBuffer OPTIONAL,
  51. IN ULONG OutputBufferLength,
  52. IN ULONG IoControlCode,
  53. OUT PIO_STATUS_BLOCK pIoStatus,
  54. IN PDEVICE_OBJECT pDeviceObject
  55. )
  56. {
  57. if (IoControlCode == IOCTL_HTTP_SEND_HTTP_RESPONSE ||
  58. IoControlCode == IOCTL_HTTP_SEND_ENTITY_BODY)
  59. {
  60. return UlSendHttpResponseFastIo(
  61. pFileObject,
  62. Wait,
  63. pInputBuffer,
  64. InputBufferLength,
  65. pOutputBuffer,
  66. OutputBufferLength,
  67. pIoStatus,
  68. pDeviceObject,
  69. IoControlCode == IOCTL_HTTP_SEND_ENTITY_BODY? TRUE: FALSE
  70. );
  71. }
  72. else
  73. if (IoControlCode == IOCTL_HTTP_RECEIVE_HTTP_REQUEST)
  74. {
  75. return UlReceiveHttpRequestFastIo(
  76. pFileObject,
  77. Wait,
  78. pInputBuffer,
  79. InputBufferLength,
  80. pOutputBuffer,
  81. OutputBufferLength,
  82. pIoStatus,
  83. pDeviceObject
  84. );
  85. }
  86. else
  87. {
  88. return FALSE;
  89. }
  90. } // UlFastIoDeviceControl
  91. BOOLEAN
  92. UlSendHttpResponseFastIo(
  93. IN PFILE_OBJECT pFileObject,
  94. IN BOOLEAN Wait,
  95. IN PVOID pInputBuffer,
  96. IN ULONG InputBufferLength,
  97. OUT PVOID pOutputBuffer,
  98. IN ULONG OutputBufferLength,
  99. OUT PIO_STATUS_BLOCK pIoStatus,
  100. IN PDEVICE_OBJECT pDeviceObject,
  101. IN BOOLEAN RawResponse
  102. )
  103. {
  104. NTSTATUS Status;
  105. PHTTP_SEND_HTTP_RESPONSE_INFO pSendInfo;
  106. PUL_INTERNAL_REQUEST pRequest = NULL;
  107. LONG BufferLength = 0;
  108. ULONG BytesSent;
  109. //
  110. // Sanity check.
  111. //
  112. PAGED_CODE();
  113. //
  114. // Initialize IoStatus in the failure case.
  115. //
  116. pIoStatus->Information = 0;
  117. //
  118. // Ensure this is really an app pool, not a control channel.
  119. //
  120. if (IS_APP_POOL(pFileObject) == FALSE)
  121. {
  122. //
  123. // Not an app pool.
  124. //
  125. Status = STATUS_INVALID_DEVICE_REQUEST;
  126. goto end;
  127. }
  128. //
  129. // Ensure the input buffer is large enough.
  130. //
  131. if (pInputBuffer == NULL || InputBufferLength < sizeof(*pSendInfo))
  132. {
  133. //
  134. // Input buffer too small.
  135. //
  136. Status = STATUS_BUFFER_TOO_SMALL;
  137. goto end;
  138. }
  139. pSendInfo = (PHTTP_SEND_HTTP_RESPONSE_INFO)pInputBuffer;
  140. //
  141. // Check if fast path can be taken for this response.
  142. //
  143. __try
  144. {
  145. ProbeTestForRead(
  146. pSendInfo,
  147. sizeof(*pSendInfo),
  148. sizeof(PVOID)
  149. );
  150. //
  151. // Fast path is only enabled if the response can be buffered.
  152. //
  153. if (RawResponse == FALSE &&
  154. pSendInfo->CachePolicy.Policy != HttpCachePolicyNocache &&
  155. g_UriCacheConfig.EnableCache)
  156. {
  157. //
  158. // Take the slow path if we need to build a cache entry.
  159. //
  160. Status = STATUS_NOT_IMPLEMENTED;
  161. }
  162. else
  163. if (pSendInfo->EntityChunkCount == 0)
  164. {
  165. //
  166. // Fast path should have no problem handling zero chunk responses.
  167. // Validate the response type.
  168. //
  169. if (RawResponse)
  170. {
  171. Status = STATUS_INVALID_PARAMETER;
  172. }
  173. else
  174. {
  175. Status = STATUS_SUCCESS;
  176. }
  177. }
  178. else
  179. {
  180. PHTTP_DATA_CHUNK pEntityChunks = pSendInfo->pEntityChunks;
  181. ULONG i;
  182. //
  183. // Make sure all chunks are from memory and their total size
  184. // is <= g_UlMaxCopyThreshold. Take the slow path if this is
  185. // not the case.
  186. //
  187. Status = STATUS_SUCCESS;
  188. for (i = 0; i < pSendInfo->EntityChunkCount; i++)
  189. {
  190. ProbeTestForRead(
  191. pEntityChunks,
  192. sizeof(HTTP_DATA_CHUNK),
  193. sizeof(PVOID)
  194. );
  195. if (pEntityChunks->DataChunkType != HttpDataChunkFromMemory)
  196. {
  197. Status = STATUS_NOT_SUPPORTED;
  198. break;
  199. }
  200. BufferLength += pEntityChunks->FromMemory.BufferLength;
  201. if (BufferLength > (LONG)g_UlMaxCopyThreshold)
  202. {
  203. Status = STATUS_NOT_SUPPORTED;
  204. break;
  205. }
  206. pEntityChunks++;
  207. }
  208. }
  209. }
  210. __except( UL_EXCEPTION_FILTER() )
  211. {
  212. Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() );
  213. }
  214. if (NT_SUCCESS(Status) == FALSE)
  215. {
  216. // BUGBUG: should close connection
  217. goto end;
  218. }
  219. //
  220. // SendHttpResponse *must* take a PHTTP_RESPONSE. This will
  221. // protect us from those whackos that attempt to build their own
  222. // raw response headers. This is ok for SendEntityBody. The
  223. // two cases are differentiated by the RawResponse flag.
  224. //
  225. if (RawResponse == FALSE && pSendInfo->pHttpResponse == NULL)
  226. {
  227. Status = STATUS_INVALID_PARAMETER;
  228. goto end;
  229. }
  230. //
  231. // Now get the request from the request ID. This gives us a reference
  232. // to the request.
  233. //
  234. pRequest = UlGetRequestFromId( pSendInfo->RequestId );
  235. if (pRequest == NULL)
  236. {
  237. Status = STATUS_INVALID_PARAMETER;
  238. goto end;
  239. }
  240. ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pRequest ) );
  241. ASSERT( UL_IS_VALID_HTTP_CONNECTION( pRequest->pHttpConn ) );
  242. //
  243. // Check if we have exceeded maximum SendBufferedBytes limit.
  244. //
  245. if ((BufferLength + pRequest->pHttpConn->SendBufferedBytes) >
  246. (LONG)g_UlMaxSendBufferedBytes)
  247. {
  248. Status = STATUS_ALLOTTED_SPACE_EXCEEDED;
  249. goto end;
  250. }
  251. //
  252. // Check if a response has already been sent on this request. We can test
  253. // this without acquiring the request resource, since the flag is only
  254. // set (never reset).
  255. //
  256. if (RawResponse == FALSE)
  257. {
  258. //
  259. // Make sure only one response header goes back.
  260. //
  261. if (1 == InterlockedCompareExchange(
  262. (PLONG)&pRequest->SentResponse,
  263. 1,
  264. 0
  265. ))
  266. {
  267. Status = STATUS_INVALID_DEVICE_STATE;
  268. goto end;
  269. }
  270. }
  271. else
  272. if (pRequest->SentResponse == 0)
  273. {
  274. //
  275. // Ensure a response has already been sent. If the application is
  276. // sending entity without first having sent a response header, check
  277. // the HTTP_SEND_RESPONSE_FLAG_RAW_HEADER flag.
  278. //
  279. if ((pSendInfo->Flags & HTTP_SEND_RESPONSE_FLAG_RAW_HEADER))
  280. {
  281. //
  282. // Make sure only one response header goes back.
  283. //
  284. if (1 == InterlockedCompareExchange(
  285. (PLONG)&pRequest->SentResponse,
  286. 1,
  287. 0
  288. ))
  289. {
  290. Status = STATUS_INVALID_DEVICE_STATE;
  291. goto end;
  292. }
  293. }
  294. else
  295. {
  296. Status = STATUS_INVALID_DEVICE_STATE;
  297. goto end;
  298. }
  299. }
  300. //
  301. // Also ensure that all previous calls to SendHttpResponse
  302. // and SendEntityBody had the MORE_DATA flag set.
  303. //
  304. if ((pSendInfo->Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0)
  305. {
  306. //
  307. // Set if we have sent the last response, but bail out if the flag
  308. // is already set since there can be only one last response.
  309. //
  310. if (1 == InterlockedCompareExchange(
  311. (PLONG)&pRequest->SentLast,
  312. 1,
  313. 0
  314. ))
  315. {
  316. Status = STATUS_INVALID_DEVICE_STATE;
  317. goto end;
  318. }
  319. }
  320. else
  321. if (pRequest->SentLast == 1)
  322. {
  323. Status = STATUS_INVALID_DEVICE_STATE;
  324. goto end;
  325. }
  326. //
  327. // OK, we have the connection. Now capture the incoming HTTP_RESPONSE
  328. // structure, map it to our internal format and send the response.
  329. //
  330. Status = UlpFastSendHttpResponse(
  331. pSendInfo->pHttpResponse,
  332. pSendInfo->pLogData,
  333. pSendInfo->pEntityChunks,
  334. pSendInfo->EntityChunkCount,
  335. pSendInfo->Flags,
  336. pRequest,
  337. NULL,
  338. &BytesSent
  339. );
  340. if (NT_SUCCESS(Status))
  341. {
  342. //
  343. // Record the number of bytes we have sent successfully.
  344. //
  345. pIoStatus->Information = BytesSent;
  346. }
  347. end:
  348. if (pRequest != NULL)
  349. {
  350. UL_DEREFERENCE_INTERNAL_REQUEST( pRequest );
  351. }
  352. //
  353. // Complete the fast I/O.
  354. //
  355. if (NT_SUCCESS(Status))
  356. {
  357. //
  358. // If we took the fast path, always return success even if completion
  359. // routine returns failure later.
  360. //
  361. pIoStatus->Status = STATUS_SUCCESS;
  362. return TRUE;
  363. }
  364. else
  365. {
  366. pIoStatus->Status = Status;
  367. return FALSE;
  368. }
  369. } // UlSendHttpResponseFastIo
  370. BOOLEAN
  371. UlReceiveHttpRequestFastIo(
  372. IN PFILE_OBJECT pFileObject,
  373. IN BOOLEAN Wait,
  374. IN PVOID pInputBuffer,
  375. IN ULONG InputBufferLength,
  376. OUT PVOID pOutputBuffer,
  377. IN ULONG OutputBufferLength,
  378. OUT PIO_STATUS_BLOCK pIoStatus,
  379. IN PDEVICE_OBJECT pDeviceObject
  380. )
  381. {
  382. NTSTATUS Status;
  383. PHTTP_RECEIVE_REQUEST_INFO pRequestInfo;
  384. ULONG BytesRead;
  385. //
  386. // Sanity check.
  387. //
  388. PAGED_CODE();
  389. //
  390. // Ensure this is really an app pool, not a control channel.
  391. //
  392. if (IS_APP_POOL(pFileObject) || NULL != pInputBuffer)
  393. {
  394. if (NULL != pOutputBuffer &&
  395. OutputBufferLength >= sizeof(HTTP_REQUEST) &&
  396. InputBufferLength == sizeof(HTTP_RECEIVE_REQUEST_INFO))
  397. {
  398. pRequestInfo = (PHTTP_RECEIVE_REQUEST_INFO) pInputBuffer;
  399. Status = STATUS_SUCCESS;
  400. __try
  401. {
  402. ProbeTestForRead(
  403. pRequestInfo,
  404. sizeof(*pRequestInfo),
  405. sizeof(PVOID)
  406. );
  407. }
  408. __except( UL_EXCEPTION_FILTER() )
  409. {
  410. Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() );
  411. }
  412. if (NT_SUCCESS(Status))
  413. {
  414. Status = UlpFastReceiveHttpRequest(
  415. pRequestInfo->RequestId,
  416. GET_APP_POOL_PROCESS(pFileObject),
  417. pOutputBuffer,
  418. OutputBufferLength,
  419. &BytesRead
  420. );
  421. }
  422. }
  423. else
  424. {
  425. Status = STATUS_BUFFER_TOO_SMALL;
  426. }
  427. }
  428. else
  429. {
  430. Status = STATUS_INVALID_DEVICE_REQUEST;
  431. }
  432. //
  433. // Complete the fast I/O.
  434. //
  435. if (NT_SUCCESS(Status))
  436. {
  437. pIoStatus->Status = STATUS_SUCCESS;
  438. pIoStatus->Information = BytesRead;
  439. return TRUE;
  440. }
  441. else
  442. {
  443. pIoStatus->Status = Status;
  444. pIoStatus->Information = 0;
  445. return FALSE;
  446. }
  447. } // UlReceiveHttpRequestFastIo
  448. //
  449. // Private functions.
  450. //
  451. NTSTATUS
  452. UlpFastSendHttpResponse(
  453. IN PHTTP_RESPONSE pUserResponse OPTIONAL,
  454. IN PHTTP_LOG_FIELDS_DATA pUserLogData OPTIONAL,
  455. IN PHTTP_DATA_CHUNK pUserEntityDataChunk,
  456. IN ULONG ChunkCount,
  457. IN ULONG Flags,
  458. IN PUL_INTERNAL_REQUEST pRequest,
  459. IN PIRP pUserIrp OPTIONAL,
  460. OUT PULONG BytesSent
  461. )
  462. {
  463. NTSTATUS Status;
  464. PUCHAR pResponseBuffer;
  465. ULONG ResponseBufferLength;
  466. ULONG HeaderLength;
  467. ULONG FixedHeaderLength;
  468. ULONG VarHeaderLength;
  469. ULONG ContentLength = 0;
  470. ULONG TotalResponseSize;
  471. ULONG ContentLengthStringLength;
  472. CHAR ContentLengthString[MAX_ULONGLONG_STR];
  473. PUL_FULL_TRACKER pTracker = NULL;
  474. PUL_HTTP_CONNECTION pHttpConnection = NULL;
  475. PUL_CONNECTION pConnection;
  476. CCHAR SendIrpStackSize;
  477. BOOLEAN Disconnect;
  478. UL_CONN_HDR ConnHeader;
  479. LARGE_INTEGER TimeSent;
  480. BOOLEAN LastResponse;
  481. PMDL pSendMdl;
  482. ULONG i;
  483. KIRQL OldIrql;
  484. //
  485. // Sanity check.
  486. //
  487. PAGED_CODE();
  488. __try
  489. {
  490. //
  491. // Setup locals so we know how to cleanup on exit.
  492. //
  493. Status = UlProbeHttpHeaders( pUserResponse );
  494. if (NT_SUCCESS(Status) == FALSE)
  495. {
  496. // BUGBUG: 389145: should close the connection
  497. goto end;
  498. }
  499. for (i = 0; i < ChunkCount; i++)
  500. {
  501. ContentLength += pUserEntityDataChunk[i].FromMemory.BufferLength;
  502. }
  503. ASSERT( pUserIrp != NULL || ContentLength <= g_UlMaxCopyThreshold );
  504. ASSERT( pUserIrp == NULL || ContentLength <= MAX_BYTES_BUFFERED );
  505. //
  506. // Allocate a fast tracker to send the response.
  507. //
  508. pConnection = pRequest->pHttpConn->pConnection;
  509. SendIrpStackSize = pConnection->ConnectionObject.pDeviceObject->StackSize;
  510. LastResponse = (0 == (Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA));
  511. if (LastResponse && SendIrpStackSize <= DEFAULT_MAX_IRP_STACK_SIZE)
  512. {
  513. pTracker = pRequest->pTracker;
  514. }
  515. else
  516. {
  517. pTracker = UlpAllocateFastTracker( 0, SendIrpStackSize );
  518. }
  519. if (pTracker == NULL)
  520. {
  521. Status = STATUS_INSUFFICIENT_RESOURCES;
  522. goto end;
  523. }
  524. //
  525. // Try to generate the fixed header within the default fixed header
  526. // buffer first. If this fails, take the normal path.
  527. //
  528. pResponseBuffer = pTracker->pAuxiliaryBuffer;
  529. if (pUserIrp != NULL)
  530. {
  531. ResponseBufferLength =
  532. g_UlMaxFixedHeaderSize + g_UlMaxCopyThreshold;
  533. }
  534. else
  535. {
  536. ResponseBufferLength =
  537. g_UlMaxFixedHeaderSize + g_UlMaxCopyThreshold - ContentLength;
  538. }
  539. if (pUserResponse != NULL)
  540. {
  541. Status = UlGenerateFixedHeaders(
  542. pRequest->Version,
  543. pUserResponse,
  544. ResponseBufferLength,
  545. pResponseBuffer,
  546. &FixedHeaderLength
  547. );
  548. if (!NT_SUCCESS(Status))
  549. {
  550. // Either the buffer was too small or an exception was thrown.
  551. if (Status != STATUS_INSUFFICIENT_RESOURCES)
  552. goto end;
  553. if (pTracker->IsFromRequest == FALSE)
  554. {
  555. if (pTracker->IsFromLookaside)
  556. {
  557. pTracker->Signature
  558. = MAKE_FREE_TAG(UL_FULL_TRACKER_POOL_TAG);
  559. UlPplFreeFullTracker( pTracker );
  560. }
  561. else
  562. {
  563. UL_FREE_POOL_WITH_SIG(
  564. pTracker,
  565. UL_FULL_TRACKER_POOL_TAG
  566. );
  567. }
  568. }
  569. //
  570. // Calculate the fixed header size.
  571. //
  572. Status = UlComputeFixedHeaderSize(
  573. pRequest->Version,
  574. pUserResponse,
  575. &HeaderLength
  576. );
  577. if (!NT_SUCCESS(Status))
  578. goto end;
  579. ASSERT( HeaderLength > ResponseBufferLength );
  580. pTracker = UlpAllocateFastTracker(
  581. HeaderLength,
  582. SendIrpStackSize
  583. );
  584. if (pTracker == NULL)
  585. {
  586. Status = STATUS_INSUFFICIENT_RESOURCES;
  587. goto end;
  588. }
  589. pResponseBuffer = pTracker->pAuxiliaryBuffer;
  590. Status = UlGenerateFixedHeaders(
  591. pRequest->Version,
  592. pUserResponse,
  593. HeaderLength,
  594. pResponseBuffer,
  595. &FixedHeaderLength
  596. );
  597. if (!NT_SUCCESS(Status))
  598. goto end;
  599. ASSERT( HeaderLength == FixedHeaderLength );
  600. }
  601. pResponseBuffer += FixedHeaderLength;
  602. }
  603. else
  604. {
  605. FixedHeaderLength = 0;
  606. }
  607. //
  608. // Take a reference of HTTP connection for the tracker.
  609. //
  610. pHttpConnection = pRequest->pHttpConn;
  611. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConnection));
  612. UL_REFERENCE_HTTP_CONNECTION( pHttpConnection );
  613. //
  614. // Take a reference of the request too because logging needs it.
  615. //
  616. UL_REFERENCE_INTERNAL_REQUEST( pRequest );
  617. //
  618. // Initialization.
  619. //
  620. pTracker->Signature = UL_FULL_TRACKER_POOL_TAG;
  621. pTracker->pRequest = pRequest;
  622. pTracker->pHttpConnection = pHttpConnection;
  623. pTracker->Flags = Flags;
  624. pTracker->pLogData = NULL;
  625. pTracker->pUserIrp = pUserIrp;
  626. pTracker->SendBufferedBytes = 0;
  627. //
  628. // Capture the log data.
  629. //
  630. if (NULL != pUserLogData &&
  631. LastResponse &&
  632. pRequest->ConfigInfo.pLoggingConfig)
  633. {
  634. Status = UlProbeLogData( pUserLogData );
  635. if (NT_SUCCESS(Status) == FALSE)
  636. {
  637. goto end;
  638. }
  639. pTracker->pLogData = &pRequest->LogData;
  640. Status = UlAllocateLogDataBuffer(
  641. pTracker->pLogData,
  642. pRequest,
  643. pRequest->ConfigInfo.pLoggingConfig
  644. );
  645. ASSERT( NT_SUCCESS( Status ) );
  646. Status = UlCaptureLogFields(
  647. pUserLogData,
  648. pRequest->Version,
  649. pTracker->pLogData
  650. );
  651. if (NT_SUCCESS(Status) == FALSE)
  652. {
  653. goto end;
  654. }
  655. }
  656. //
  657. // Generate the variable header.
  658. //
  659. if (FixedHeaderLength > 0)
  660. {
  661. //
  662. // Should we close the connection?
  663. //
  664. Disconnect = FALSE;
  665. if ((Flags & HTTP_SEND_RESPONSE_FLAG_DISCONNECT) != 0)
  666. {
  667. //
  668. // Caller is forcing a disconnect.
  669. //
  670. Disconnect = TRUE;
  671. }
  672. else
  673. if (LastResponse)
  674. {
  675. //
  676. // No more data is coming, should we disconnect?
  677. //
  678. if (UlCheckDisconnectInfo(pRequest))
  679. {
  680. Disconnect = TRUE;
  681. pTracker->Flags |= HTTP_SEND_RESPONSE_FLAG_DISCONNECT;
  682. }
  683. }
  684. //
  685. // Choose the connection header to send back.
  686. //
  687. ConnHeader = UlChooseConnectionHeader( pRequest->Version, Disconnect );
  688. //
  689. // Decide if we need to generate a content-length header.
  690. //
  691. if (IS_LENGTH_SPECIFIED(pUserResponse->Headers.pKnownHeaders) == FALSE &&
  692. IS_CHUNK_SPECIFIED(pUserResponse->Headers.pKnownHeaders) == FALSE &&
  693. UlNeedToGenerateContentLength(
  694. pRequest->Verb,
  695. pUserResponse->StatusCode,
  696. pTracker->Flags
  697. ))
  698. {
  699. //
  700. // Autogenerate a content-length header. We can use our own fast
  701. // generator here because we know the content-length is <= 64k.
  702. //
  703. ContentLengthStringLength = UlpFastGenerateContentLength(
  704. ContentLengthString,
  705. ContentLength
  706. );
  707. }
  708. else
  709. {
  710. ContentLengthString[0] = ANSI_NULL;
  711. ContentLengthStringLength = 0;
  712. }
  713. //
  714. // Now generate the variable header.
  715. //
  716. UlGenerateVariableHeaders(
  717. ConnHeader,
  718. (PUCHAR)ContentLengthString,
  719. ContentLengthStringLength,
  720. pResponseBuffer,
  721. &VarHeaderLength,
  722. &TimeSent
  723. );
  724. ASSERT( VarHeaderLength <= g_UlMaxVariableHeaderSize );
  725. pResponseBuffer += VarHeaderLength;
  726. }
  727. else
  728. {
  729. VarHeaderLength = 0;
  730. }
  731. pTracker->pMdlAuxiliary->ByteCount
  732. = FixedHeaderLength + VarHeaderLength;
  733. //
  734. // If this routine is called from the fast I/O path, copy the content
  735. // to the auxilary buffer inside the tracker and set up the send MDL.
  736. // Otherwise, we need to MmProbeAndLock the user buffer into another
  737. // separate MDL.
  738. //
  739. if (pUserIrp == NULL)
  740. {
  741. PUCHAR pAuxiliaryBuffer = pResponseBuffer;
  742. Status = STATUS_SUCCESS;
  743. __try
  744. {
  745. for (i = 0; i < ChunkCount; i++)
  746. {
  747. if (pUserEntityDataChunk[i].FromMemory.BufferLength == 0 ||
  748. pUserEntityDataChunk[i].FromMemory.pBuffer == NULL)
  749. {
  750. ExRaiseStatus( STATUS_INVALID_PARAMETER );
  751. }
  752. ProbeTestForRead(
  753. pUserEntityDataChunk[i].FromMemory.pBuffer,
  754. pUserEntityDataChunk[i].FromMemory.BufferLength,
  755. sizeof(CHAR)
  756. );
  757. RtlCopyMemory(
  758. pAuxiliaryBuffer,
  759. pUserEntityDataChunk[i].FromMemory.pBuffer,
  760. pUserEntityDataChunk[i].FromMemory.BufferLength
  761. );
  762. pAuxiliaryBuffer
  763. += pUserEntityDataChunk[i].FromMemory.BufferLength;
  764. }
  765. }
  766. __except( UL_EXCEPTION_FILTER() )
  767. {
  768. Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() );
  769. }
  770. if (NT_SUCCESS(Status) == FALSE)
  771. {
  772. goto end;
  773. }
  774. pTracker->pMdlAuxiliary->ByteCount += ContentLength;
  775. pTracker->pMdlAuxiliary->Next = NULL;
  776. }
  777. else
  778. {
  779. ASSERT( ChunkCount == 1 );
  780. Status = STATUS_SUCCESS;
  781. __try
  782. {
  783. if (pUserEntityDataChunk->FromMemory.BufferLength == 0 ||
  784. pUserEntityDataChunk->FromMemory.pBuffer == NULL)
  785. {
  786. ExRaiseStatus( STATUS_INVALID_PARAMETER );
  787. }
  788. ProbeTestForRead(
  789. pUserEntityDataChunk->FromMemory.pBuffer,
  790. pUserEntityDataChunk->FromMemory.BufferLength,
  791. sizeof(CHAR)
  792. );
  793. Status = UlpInitializeAndLockMdl(
  794. pTracker->pMdlUserBuffer,
  795. pUserEntityDataChunk->FromMemory.pBuffer,
  796. ContentLength,
  797. IoReadAccess
  798. );
  799. }
  800. __except( UL_EXCEPTION_FILTER() )
  801. {
  802. Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() );
  803. }
  804. if (NT_SUCCESS(Status) == FALSE)
  805. {
  806. goto end;
  807. }
  808. pTracker->pMdlAuxiliary->Next = pTracker->pMdlUserBuffer;
  809. }
  810. TotalResponseSize = FixedHeaderLength + VarHeaderLength + ContentLength;
  811. //
  812. // Fail the zero length response which can be generated in the case
  813. // where the request is 0.9 and there is no body attached to the
  814. // response. We have to take this code path to possibly force a
  815. // disconnect here because the slow path may not go far enough to the
  816. // disconnect logic since we may have flags such as SentLast or
  817. // SentResponse set.
  818. //
  819. if (TotalResponseSize == 0)
  820. {
  821. if (IS_DISCONNECT_TIME(pTracker))
  822. {
  823. UlCloseConnection(
  824. pTracker->pHttpConnection->pConnection,
  825. FALSE,
  826. NULL,
  827. NULL
  828. );
  829. }
  830. Status = STATUS_INVALID_PARAMETER;
  831. goto end;
  832. }
  833. //
  834. // Add to MinKBSec watch list, since we now know TotalResponseSize.
  835. //
  836. UlSetMinKBSecTimer(
  837. &pHttpConnection->TimeoutInfo,
  838. TotalResponseSize
  839. );
  840. //
  841. // Mark the IRP as pending before the send as we are guaranteed to
  842. // return pending from this point on.
  843. //
  844. if (pUserIrp != NULL)
  845. {
  846. IoMarkIrpPending( pUserIrp );
  847. }
  848. //
  849. // Skip the zero length MDL if created one.
  850. //
  851. pSendMdl = pTracker->pMdlAuxiliary;
  852. if (pSendMdl->ByteCount == 0)
  853. {
  854. pSendMdl = pSendMdl->Next;
  855. }
  856. ASSERT( pSendMdl != NULL );
  857. ASSERT( pSendMdl->ByteCount != 0 );
  858. //
  859. // Adjust SendBufferedBytes for the fast I/O path only.
  860. //
  861. if (pUserIrp == NULL)
  862. {
  863. pTracker->SendBufferedBytes = ContentLength;
  864. InterlockedExchangeAdd(
  865. &pHttpConnection->SendBufferedBytes,
  866. ContentLength
  867. );
  868. }
  869. //
  870. // Send the response. Notice the logic to disconnect the connection is
  871. // different from sending back a disconnect header.
  872. //
  873. Status = UlSendData(
  874. pHttpConnection->pConnection,
  875. pSendMdl,
  876. TotalResponseSize,
  877. &UlpRestartFastSendHttpResponse,
  878. pTracker,
  879. pTracker->pSendIrp,
  880. &pTracker->IrpContext,
  881. IS_DISCONNECT_TIME(pTracker)
  882. );
  883. ASSERT( Status == STATUS_PENDING );
  884. if (BytesSent != NULL)
  885. {
  886. *BytesSent = TotalResponseSize;
  887. }
  888. return STATUS_PENDING;
  889. }
  890. __except( UL_EXCEPTION_FILTER() )
  891. {
  892. Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() );
  893. }
  894. end:
  895. //
  896. // Cleanup.
  897. //
  898. if (pTracker)
  899. {
  900. UlpFreeFastTracker( pTracker );
  901. //
  902. // Let the references go.
  903. //
  904. if (pHttpConnection != NULL)
  905. {
  906. UL_DEREFERENCE_HTTP_CONNECTION( pHttpConnection );
  907. UL_DEREFERENCE_INTERNAL_REQUEST( pRequest );
  908. }
  909. }
  910. return Status;
  911. } // UlpFastSendHttpResponse
  912. VOID
  913. UlpRestartFastSendHttpResponse(
  914. IN PVOID pCompletionContext,
  915. IN NTSTATUS Status,
  916. IN ULONG_PTR Information
  917. )
  918. {
  919. PUL_FULL_TRACKER pTracker;
  920. PIRP pIrp;
  921. pTracker = (PUL_FULL_TRACKER)pCompletionContext;
  922. ASSERT( IS_VALID_FULL_TRACKER( pTracker ) );
  923. //
  924. // Set status and bytes transferred fields as returned.
  925. //
  926. pTracker->IoStatus.Status = Status;
  927. pTracker->IoStatus.Information = Information;
  928. if (NT_SUCCESS(Status) == FALSE &&
  929. IS_DISCONNECT_TIME(pTracker) == FALSE)
  930. {
  931. //
  932. // Disconnect if there was an error and we didn't disconnect already.
  933. //
  934. UlCloseConnection(
  935. pTracker->pHttpConnection->pConnection,
  936. TRUE,
  937. NULL,
  938. NULL
  939. );
  940. }
  941. //
  942. // Complete the orignal user send IRP if set.
  943. //
  944. pIrp = pTracker->pUserIrp;
  945. if (pIrp != NULL)
  946. {
  947. //
  948. // Don't forget to unlock the user buffer.
  949. //
  950. ASSERT( pTracker->pMdlAuxiliary->Next != NULL );
  951. ASSERT( pTracker->pMdlAuxiliary->Next == pTracker->pMdlUserBuffer );
  952. MmUnlockPages( pTracker->pMdlUserBuffer );
  953. pIrp->IoStatus.Status = Status;
  954. pIrp->IoStatus.Information = Information;
  955. UlCompleteRequest( pIrp, g_UlPriorityBoost );
  956. }
  957. else
  958. {
  959. //
  960. // Adjust SendBufferedBytes.
  961. //
  962. InterlockedExchangeAdd(
  963. &pTracker->pHttpConnection->SendBufferedBytes,
  964. - pTracker->SendBufferedBytes
  965. );
  966. }
  967. UL_QUEUE_WORK_ITEM(
  968. &pTracker->WorkItem,
  969. &UlpFastSendCompleteWorker
  970. );
  971. } // UlpRestartFastSendHttpResponse
  972. VOID
  973. UlpFastSendCompleteWorker(
  974. IN PUL_WORK_ITEM pWorkItem
  975. )
  976. {
  977. PUL_FULL_TRACKER pTracker;
  978. PUL_HTTP_CONNECTION pHttpConnection;
  979. PUL_INTERNAL_REQUEST pRequest;
  980. NTSTATUS Status;
  981. KIRQL OldIrql;
  982. //
  983. // Sanity check.
  984. //
  985. PAGED_CODE();
  986. pTracker = CONTAINING_RECORD(
  987. pWorkItem,
  988. UL_FULL_TRACKER,
  989. WorkItem
  990. );
  991. ASSERT( IS_VALID_FULL_TRACKER( pTracker ) );
  992. Status = pTracker->IoStatus.Status;
  993. pHttpConnection = pTracker->pHttpConnection;
  994. ASSERT( UL_IS_VALID_HTTP_CONNECTION( pHttpConnection ) );
  995. pRequest = pTracker->pRequest;
  996. ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pRequest ) );
  997. //
  998. // Update the BytesSent counter in the request.
  999. //
  1000. UlInterlockedAdd64(
  1001. (PLONGLONG)&pRequest->BytesSent,
  1002. pTracker->IoStatus.Information
  1003. );
  1004. if ((pTracker->Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0)
  1005. {
  1006. //
  1007. // Stop the MinKBSec timer and start Idle timer
  1008. //
  1009. UlLockTimeoutInfo(
  1010. &pHttpConnection->TimeoutInfo,
  1011. &OldIrql
  1012. );
  1013. UlResetConnectionTimer(
  1014. &pHttpConnection->TimeoutInfo,
  1015. TimerMinKBSec
  1016. );
  1017. UlSetConnectionTimer(
  1018. &pHttpConnection->TimeoutInfo,
  1019. TimerConnectionIdle
  1020. );
  1021. UlUnlockTimeoutInfo(
  1022. &pHttpConnection->TimeoutInfo,
  1023. OldIrql
  1024. );
  1025. UlEvaluateTimerState(
  1026. &pHttpConnection->TimeoutInfo
  1027. );
  1028. }
  1029. //
  1030. // If this is the last response for this request and there was a log
  1031. // data passed down by the user then now its time to log.
  1032. //
  1033. if (NULL != pTracker->pLogData)
  1034. {
  1035. UlLogHttpHit( pTracker->pLogData );
  1036. }
  1037. //
  1038. // Kick the parser on the connection and release our hold.
  1039. //
  1040. if (Status == STATUS_SUCCESS &&
  1041. (pTracker->Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0 &&
  1042. (pTracker->Flags & HTTP_SEND_RESPONSE_FLAG_DISCONNECT) == 0)
  1043. {
  1044. UlResumeParsing( pHttpConnection );
  1045. }
  1046. //
  1047. // Cleanup.
  1048. //
  1049. UlpFreeFastTracker( pTracker );
  1050. UL_DEREFERENCE_HTTP_CONNECTION( pHttpConnection );
  1051. UL_DEREFERENCE_INTERNAL_REQUEST( pRequest );
  1052. } // UlpFastSendCompleteWorker
  1053. NTSTATUS
  1054. UlpFastReceiveHttpRequest(
  1055. IN HTTP_REQUEST_ID RequestId,
  1056. IN PUL_APP_POOL_PROCESS pProcess,
  1057. IN PVOID pOutputBuffer,
  1058. IN ULONG OutputBufferLength,
  1059. OUT PULONG pBytesRead
  1060. )
  1061. {
  1062. NTSTATUS Status;
  1063. PUL_INTERNAL_REQUEST pRequest = NULL;
  1064. //
  1065. // Sanity check.
  1066. //
  1067. PAGED_CODE();
  1068. ASSERT( IS_VALID_AP_PROCESS(pProcess) );
  1069. ASSERT( IS_VALID_AP_OBJECT(pProcess->pAppPool) );
  1070. ASSERT( pOutputBuffer != NULL);
  1071. UlAcquireResourceShared( &pProcess->pAppPool->pResource->Resource, TRUE );
  1072. //
  1073. // Make sure we're not cleaning up the process.
  1074. //
  1075. if (!pProcess->InCleanup)
  1076. {
  1077. //
  1078. // Obtain the request based on the request ID. This can be from the
  1079. // NewRequestQueue of the AppPool if the ID is NULL, or directly
  1080. // from the matching opaque ID entry.
  1081. //
  1082. if (HTTP_IS_NULL_ID(&RequestId))
  1083. {
  1084. pRequest = UlpDequeueNewRequest( pProcess );
  1085. }
  1086. else
  1087. {
  1088. pRequest = UlGetRequestFromId( RequestId );
  1089. if (NULL != pRequest)
  1090. {
  1091. ASSERT( UL_IS_VALID_INTERNAL_REQUEST(pRequest) );
  1092. //
  1093. // Weed out the request in bad state.
  1094. //
  1095. if (pRequest->AppPool.QueueState != QueueCopiedState ||
  1096. pRequest->AppPool.pProcess != pProcess)
  1097. {
  1098. UL_DEREFERENCE_INTERNAL_REQUEST( pRequest );
  1099. pRequest = NULL;
  1100. }
  1101. }
  1102. }
  1103. }
  1104. //
  1105. // Let go the lock since we have taken a short-lived reference of
  1106. // the request in the success case.
  1107. //
  1108. UlReleaseResource( &pProcess->pAppPool->pResource->Resource );
  1109. //
  1110. // Return immediately if no request is found and let the slow path
  1111. // handle this.
  1112. //
  1113. if (NULL == pRequest)
  1114. {
  1115. return STATUS_INVALID_PARAMETER;
  1116. }
  1117. //
  1118. // Copy it to the output buffer.
  1119. //
  1120. Status = UlpFastCopyHttpRequest(
  1121. pRequest,
  1122. pOutputBuffer,
  1123. OutputBufferLength,
  1124. pBytesRead
  1125. );
  1126. //
  1127. // Let go our reference.
  1128. //
  1129. UL_DEREFERENCE_INTERNAL_REQUEST( pRequest );
  1130. return Status;
  1131. } // UlpFastReceiveHttpRequest
  1132. NTSTATUS
  1133. UlpFastCopyHttpRequest(
  1134. IN PUL_INTERNAL_REQUEST pRequest,
  1135. IN PVOID pOutputBuffer,
  1136. IN ULONG OutputBufferLength,
  1137. OUT PULONG pBytesRead
  1138. )
  1139. {
  1140. NTSTATUS Status;
  1141. ULONG BytesNeeded;
  1142. //
  1143. // Sanity check.
  1144. //
  1145. PAGED_CODE();
  1146. ASSERT( UL_IS_VALID_INTERNAL_REQUEST(pRequest) );
  1147. ASSERT( pOutputBuffer != NULL);
  1148. //
  1149. // Make sure this is big enough to handle the request, and
  1150. // if so copy it in.
  1151. //
  1152. Status = UlpComputeRequestBytesNeeded( pRequest, &BytesNeeded );
  1153. if (NT_SUCCESS(Status))
  1154. {
  1155. //
  1156. // Make sure we've got enough space to handle the whole request.
  1157. //
  1158. if (BytesNeeded <= OutputBufferLength)
  1159. {
  1160. //
  1161. // This request will fit in this buffer, so copy it.
  1162. //
  1163. __try
  1164. {
  1165. ProbeForWrite(
  1166. pOutputBuffer,
  1167. OutputBufferLength,
  1168. TYPE_ALIGNMENT(HTTP_REQUEST)
  1169. );
  1170. Status = UlpCopyRequestToBuffer(
  1171. pRequest,
  1172. (PUCHAR) pOutputBuffer,
  1173. pOutputBuffer,
  1174. OutputBufferLength,
  1175. NULL,
  1176. 0
  1177. );
  1178. }
  1179. __except( UL_EXCEPTION_FILTER() )
  1180. {
  1181. Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() );
  1182. }
  1183. if (NT_SUCCESS(Status))
  1184. {
  1185. *pBytesRead = BytesNeeded;
  1186. }
  1187. }
  1188. else
  1189. {
  1190. //
  1191. // Let the slow path handle this.
  1192. //
  1193. Status = STATUS_BUFFER_OVERFLOW;
  1194. }
  1195. }
  1196. return Status;
  1197. } // UlpFastCopyHttpRequest