Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

7009 lines
200 KiB

  1. /*++
  2. Copyright (c) 1998-2002 Microsoft Corporation
  3. Module Name:
  4. httprcv.c
  5. Abstract:
  6. Contains core HTTP receive code.
  7. Author:
  8. Henry Sanders (henrysa) 10-Jun-1998
  9. Revision History:
  10. Paul McDaniel (paulmcd) 01-Mar-1999
  11. massively rewrote it to handle request spanning tdi packets.
  12. moved all http parsing to PASSIVE irql (from DISPATCH).
  13. also merged tditest into this module.
  14. Eric Stenson (EricSten) 11-Sep-2000
  15. Added support for sending "100 Continue" responses to PUT
  16. and POST requests. Added #pragma's for PAGED -vs- Non-PAGED
  17. functions.
  18. --*/
  19. #include "precomp.h"
  20. #include "httprcvp.h"
  21. //
  22. // Declare pageable and non-pageable functions
  23. //
  24. #ifdef ALLOC_PRAGMA
  25. // Public
  26. #pragma alloc_text( PAGE, UlCheckProtocolCompliance )
  27. #pragma alloc_text( PAGE, UlGetCGroupForRequest )
  28. #pragma alloc_text( PAGE, UlSendSimpleStatus )
  29. #pragma alloc_text( PAGE, UlSendSimpleStatusEx )
  30. #pragma alloc_text( PAGE, UlProcessBufferQueue )
  31. #pragma alloc_text( PAGE, UlErrorLog )
  32. // Private
  33. #pragma alloc_text( PAGE, UlpDeliverHttpRequest )
  34. #pragma alloc_text( PAGE, UlpCancelEntityBodyWorker )
  35. #pragma alloc_text( PAGE, UlpConnectionDisconnectWorker )
  36. #pragma alloc_text( PAGE, UlpInitErrorLogInfo )
  37. #if DBG
  38. #pragma alloc_text( PAGE, UlpIsValidRequestBufferList )
  39. #endif // DBG
  40. #endif // ALLOC_PRAGMA
  41. #if 0 // Non-Pageable Functions
  42. // Public
  43. NOT PAGEABLE -- UlHttpReceive
  44. NOT PAGEABLE -- UlResumeParsing
  45. NOT PAGEABLE -- UlConnectionRequest
  46. NOT PAGEABLE -- UlConnectionComplete
  47. NOT PAGEABLE -- UlConnectionDisconnect
  48. NOT PAGEABLE -- UlConnectionDisconnectComplete
  49. NOT PAGEABLE -- UlConnectionDestroyed
  50. NOT PAGEABLE -- UlReceiveEntityBody
  51. NOT PAGEABLE -- UlSetErrorCodeFileLine
  52. NOT PAGEABLE -- UlSendErrorResponse
  53. // Private
  54. NOT PAGEABLE -- UlpHandleRequest
  55. NOT PAGEABLE -- UlpFreeReceiveBufferList
  56. NOT PAGEABLE -- UlpParseNextRequest
  57. NOT PAGEABLE -- UlpInsertBuffer
  58. NOT PAGEABLE -- UlpMergeBuffers
  59. NOT PAGEABLE -- UlpAdjustBuffers
  60. NOT PAGEABLE -- UlpConsumeBytesFromConnection
  61. NOT PAGEABLE -- UlpCancelEntityBody
  62. NOT PAGEABLE -- UlpCompleteSendErrorResponse
  63. NOT PAGEABLE -- UlpRestartSendSimpleResponse
  64. NOT PAGEABLE -- UlpSendSimpleCleanupWorker
  65. NOT PAGEABLE -- UlpDoConnectionDisconnect
  66. #endif // Non-Pageable Functions
  67. //
  68. // Private globals.
  69. //
  70. #if DBG
  71. BOOLEAN g_CheckRequestBufferList = FALSE;
  72. #endif
  73. /*++
  74. Paul McDaniel (paulmcd) 26-May-1999
  75. here is a brief description of the data structures used by this module:
  76. the connection keeps track of all buffers received by TDI into a list anchored
  77. by HTTP_CONNECTION::BufferHead. this list is sequenced and sorted. the
  78. buffers are refcounted.
  79. HTTP_REQUEST(s) keep pointers into these buffers for the parts they consume.
  80. HTTP_REQUEST::pHeaderBuffer and HTTP_REQUEST::pChunkBuffer.
  81. the buffers fall off the list as they are no longer needed. the connection
  82. only keeps a reference at HTTP_CONNECTION::pCurrentBuffer. so as it completes
  83. the processing of a buffer, if no other objects kept that buffer, it will be
  84. released.
  85. here is a brief description of the functions in this module, and how they
  86. are used:
  87. UlHttpReceive - the TDI data indication handler. copies buffers and queues to
  88. UlpHandleRequest.
  89. UlpHandleRequest - the main processing function for connections.
  90. UlCreateHttpConnectionId - creates the connections opaque id.
  91. UlpInsertBuffer - inserts the buffer into pConnection->BufferHead - sorted.
  92. UlpAdjustBuffers - determines where in BufferHead the current connection
  93. should be parsing. handle buffer merging and copying if a protocol
  94. token spans buffers
  95. UlParseHttp - the main http parser. expects that no protocol tokens span
  96. a buffer. will return a status code if it does.
  97. UlProcessBufferQueue - handles entity body buffer processing.
  98. synchronizes access to pRequest->IrpHead at pRequest->pChunkBuffer
  99. with UlReceiveEntityBody.
  100. UlConnectionRequest - called when a new connection comes in. allocates a new
  101. HTTP_CONNECTION. does not create the opaque id.
  102. UlConnectionComplete - called if the client is happy with our accept.
  103. closes the connection if error status.
  104. UlConnectionDisconnect - called when the client disconnects. it calls tdi to
  105. close the server end. always a graceful close.
  106. UlConnectionDestroyed - called when the connection is dropped. both sides have
  107. closed it. deletes all opaque ids . removes the tdi reference on the
  108. HTTP_CONNECTION (and hopefully vice versa) .
  109. UlReceiveEntityBody - called by user mode to read entity body. pends the irp
  110. to pRequest->IrpHead and calls UlProcessBufferQueue .
  111. --*/
  112. /*++
  113. Routine Description:
  114. The main http receive routine, called by TDI.
  115. Arguments:
  116. pHttpConn - Pointer to HTTP connection on which data was received.
  117. pVoidBuffer - Pointer to data received.
  118. BufferLength - Length of data pointed to by pVoidBuffer.
  119. UnreceivedLength- Bytes that the transport has, but aren't in pBuffer
  120. pBytesTaken - Pointer to where to return bytes taken.
  121. Return Value:
  122. Status of receive.
  123. --*/
  124. NTSTATUS
  125. UlHttpReceive(
  126. IN PVOID pListeningContext,
  127. IN PVOID pConnectionContext,
  128. IN PVOID pVoidBuffer,
  129. IN ULONG BufferLength,
  130. IN ULONG UnreceivedLength,
  131. OUT PULONG pBytesTaken
  132. )
  133. {
  134. PUL_REQUEST_BUFFER pRequestBuffer;
  135. PUL_HTTP_CONNECTION pConnection;
  136. BOOLEAN DrainAfterDisconnect = FALSE;
  137. BOOLEAN CopyRequest = FALSE;
  138. ULONG NextBufferNumber = ULONG_MAX;
  139. KIRQL OldIrql;
  140. BOOLEAN UseLookaside = FALSE;
  141. UNREFERENCED_PARAMETER(pListeningContext);
  142. ASSERT(BufferLength != 0);
  143. ASSERT(pConnectionContext != NULL);
  144. pConnection = (PUL_HTTP_CONNECTION)pConnectionContext;
  145. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pConnection));
  146. //
  147. // Make sure we are not buffering too much data.
  148. // Need to adjust the BufferLength to be no more
  149. // than the number of bytes we can accept at this time.
  150. //
  151. //
  152. // PerfBug: need to get rid of this lock
  153. //
  154. UlAcquireSpinLock(&pConnection->BufferingInfo.BufferingSpinLock, &OldIrql);
  155. DrainAfterDisconnect = pConnection->BufferingInfo.DrainAfterDisconnect;
  156. //
  157. // For filtered connections, a receive indication may happen while
  158. // there is a pending read. Therefore we need to increment up here.
  159. //
  160. pConnection->BufferingInfo.TransportBytesNotTaken += UnreceivedLength;
  161. if (!DrainAfterDisconnect)
  162. {
  163. //
  164. // Use the RequestBuffer lookaside list if we haven't previously
  165. // buffered any request buffers.
  166. //
  167. if (0 == pConnection->BufferingInfo.BytesBuffered)
  168. {
  169. UseLookaside = TRUE;
  170. }
  171. if ((pConnection->BufferingInfo.BytesBuffered + BufferLength)
  172. > g_UlMaxBufferedBytes)
  173. {
  174. ULONG SpaceAvailable = g_UlMaxBufferedBytes
  175. - pConnection->BufferingInfo.BytesBuffered;
  176. pConnection->BufferingInfo.TransportBytesNotTaken
  177. += (BufferLength - SpaceAvailable);
  178. BufferLength = SpaceAvailable;
  179. }
  180. pConnection->BufferingInfo.BytesBuffered += BufferLength;
  181. UlTraceVerbose(HTTP_IO,
  182. ("UlHttpReceive(conn=%p): BytesBuffered %lu->%lu, "
  183. "TransportBytesNotTaken %lu->%lu\n",
  184. pConnection,
  185. pConnection->BufferingInfo.BytesBuffered
  186. - BufferLength,
  187. pConnection->BufferingInfo.BytesBuffered,
  188. pConnection->BufferingInfo.TransportBytesNotTaken
  189. - UnreceivedLength,
  190. pConnection->BufferingInfo.TransportBytesNotTaken
  191. ));
  192. }
  193. if (BufferLength && DrainAfterDisconnect == FALSE)
  194. {
  195. CopyRequest = TRUE;
  196. NextBufferNumber = pConnection->NextBufferNumber;
  197. pConnection->NextBufferNumber++;
  198. }
  199. UlReleaseSpinLock(&pConnection->BufferingInfo.BufferingSpinLock, OldIrql);
  200. if (CopyRequest)
  201. {
  202. //
  203. // get a new request buffer
  204. //
  205. pRequestBuffer = UlCreateRequestBuffer(
  206. BufferLength,
  207. NextBufferNumber,
  208. UseLookaside
  209. );
  210. if (pRequestBuffer == NULL)
  211. {
  212. return STATUS_NO_MEMORY;
  213. }
  214. //
  215. // copy the tdi buffer
  216. //
  217. RtlCopyMemory(pRequestBuffer->pBuffer, pVoidBuffer, BufferLength);
  218. pRequestBuffer->UsedBytes = BufferLength;
  219. //
  220. // Add backpointer to connection.
  221. //
  222. pRequestBuffer->pConnection = pConnection;
  223. UlTrace( PARSER, (
  224. "*** Request Buffer %p (#%d) has connection %p\n",
  225. pRequestBuffer,
  226. pRequestBuffer->BufferNumber,
  227. pConnection
  228. ));
  229. IF_DEBUG2BOTH(HTTP_IO, VERBOSE)
  230. {
  231. UlTraceVerbose( HTTP_IO, (
  232. "<<<< Request(%p), "
  233. "RequestBuffer %p(#%d), %lu bytes, "
  234. "Conn %p.\n",
  235. pConnection->pRequest,
  236. pRequestBuffer, pRequestBuffer->BufferNumber, BufferLength,
  237. pConnection
  238. ));
  239. UlDbgPrettyPrintBuffer(pRequestBuffer->pBuffer, BufferLength);
  240. UlTraceVerbose( HTTP_IO, (">>>>\n"));
  241. }
  242. //
  243. // Queue a work item to handle the data.
  244. //
  245. // Reference the connection so it doesn't go
  246. // away while we're waiting for our work item
  247. // to run. UlpHandleRequest will release the ref.
  248. //
  249. // If ReceiveBufferSList is empty, then queue a UlpHandleRequest
  250. // workitem to handle any request buffers that have accumulated
  251. // by the time that UlpHandleRequest is finally invoked.
  252. //
  253. if (NULL == InterlockedPushEntrySList(
  254. &pConnection->ReceiveBufferSList,
  255. &pRequestBuffer->SListEntry
  256. ))
  257. {
  258. UL_REFERENCE_HTTP_CONNECTION(pConnection);
  259. UL_QUEUE_WORK_ITEM(
  260. &pConnection->ReceiveBufferWorkItem,
  261. &UlpHandleRequest
  262. );
  263. }
  264. }
  265. else if ( DrainAfterDisconnect && UnreceivedLength != 0 )
  266. {
  267. // Handle the case where we are in drain state and there's
  268. // unreceived data indicated but not available by the tdi.
  269. UlpDiscardBytesFromConnection( pConnection );
  270. }
  271. //
  272. // Tell the caller how many bytes we consumed.
  273. //
  274. *pBytesTaken = BufferLength;
  275. return STATUS_SUCCESS;
  276. } // UlHttpReceive
  277. /***************************************************************************++
  278. Routine Description:
  279. links a set of request buffers into the connection and processes the list.
  280. starts http request parsing.
  281. Arguments:
  282. pWorkItem - embedded in an UL_HTTP_CONNECTION
  283. --***************************************************************************/
  284. VOID
  285. UlpHandleRequest(
  286. IN PUL_WORK_ITEM pWorkItem
  287. )
  288. {
  289. NTSTATUS Status = STATUS_SUCCESS;
  290. PUL_REQUEST_BUFFER pRequestBuffer;
  291. PUL_HTTP_CONNECTION pConnection;
  292. SLIST_ENTRY BufferSList;
  293. PSLIST_ENTRY pListEntry, pNext;
  294. PIRP pIrp, pIrpToComplete = NULL;
  295. //
  296. // Sanity check.
  297. //
  298. PAGED_CODE();
  299. pConnection = CONTAINING_RECORD(
  300. pWorkItem,
  301. UL_HTTP_CONNECTION,
  302. ReceiveBufferWorkItem
  303. );
  304. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pConnection));
  305. //
  306. // Yank the receive buffers accumulated so far into a local list.
  307. //
  308. pListEntry = InterlockedFlushSList(&pConnection->ReceiveBufferSList);
  309. ASSERT( NULL != pListEntry );
  310. //
  311. // Reverse-order of what we received.
  312. //
  313. BufferSList.Next = NULL;
  314. while (pListEntry != NULL)
  315. {
  316. pNext = pListEntry->Next;
  317. pListEntry->Next = BufferSList.Next;
  318. BufferSList.Next = pListEntry;
  319. pListEntry = pNext;
  320. }
  321. //
  322. // grab the lock
  323. //
  324. UlAcquirePushLockExclusive(&pConnection->PushLock);
  325. //
  326. // if the connection is going down, just bail out.
  327. //
  328. if (pConnection->UlconnDestroyed)
  329. {
  330. UlpFreeReceiveBufferList(&BufferSList);
  331. Status = STATUS_SUCCESS;
  332. goto end;
  333. }
  334. while (NT_SUCCESS(Status) && NULL != BufferSList.Next)
  335. {
  336. pListEntry = BufferSList.Next;
  337. BufferSList.Next = pListEntry->Next;
  338. pRequestBuffer = CONTAINING_RECORD(
  339. pListEntry,
  340. UL_REQUEST_BUFFER,
  341. ListEntry
  342. );
  343. ASSERT( UL_IS_VALID_REQUEST_BUFFER(pRequestBuffer) );
  344. pRequestBuffer->ListEntry.Blink = NULL;
  345. pRequestBuffer->ListEntry.Flink = NULL;
  346. //
  347. // insert it into the list
  348. //
  349. ASSERT( 0 != pRequestBuffer->UsedBytes );
  350. UlTraceVerbose( PARSER, (
  351. "http!UlpHandleRequest: conn = %p, Req = %p: "
  352. "about to insert buffer %p\n",
  353. pConnection,
  354. pConnection->pRequest,
  355. pRequestBuffer
  356. ));
  357. UlpInsertBuffer(pConnection, pRequestBuffer);
  358. //
  359. // Kick off the parser
  360. //
  361. UlTraceVerbose( PARSER, (
  362. "http!UlpHandleRequest: conn = %p, Req = %p: "
  363. "about to parse next request (MoreRequestBuffers=%d)\n",
  364. pConnection,
  365. pConnection->pRequest,
  366. BufferSList.Next != NULL
  367. ));
  368. pIrp = NULL;
  369. Status = UlpParseNextRequest(
  370. pConnection,
  371. (BOOLEAN) (BufferSList.Next != NULL),
  372. &pIrp
  373. );
  374. if (pIrp)
  375. {
  376. //
  377. // There shall be only one IRP to complete since we handle
  378. // cache-miss request one at a time.
  379. //
  380. ASSERT(pIrpToComplete == NULL);
  381. pIrpToComplete = pIrp;
  382. }
  383. }
  384. if (!NT_SUCCESS(Status))
  385. {
  386. UlpFreeReceiveBufferList(&BufferSList);
  387. }
  388. end:
  389. UlTraceVerbose( PARSER, (
  390. "http!UlpHandleRequest: %s, pConnection %p, pRequest %p\n",
  391. HttpStatusToString(Status),
  392. pConnection,
  393. pConnection->pRequest
  394. ));
  395. if (!NT_SUCCESS(Status) && pConnection->pRequest != NULL)
  396. {
  397. UlTraceError( PARSER, (
  398. "*** %s, pConnection %p, pRequest %p\n",
  399. HttpStatusToString(Status),
  400. pConnection,
  401. pConnection->pRequest
  402. ));
  403. //
  404. // An error happened, most propably during parsing.
  405. // Send an error back if user hasn't send one yet.
  406. // E.g. We have received a request, then delivered
  407. // it to the WP, therefore WaitingForResponse is
  408. // set. And then encountered an error when dealing
  409. // with entity body.
  410. //
  411. // Not all error paths explicitly set pRequest->ErrorCode, so
  412. // we may have to fall back on the most generic error, UlError.
  413. //
  414. if (UlErrorNone == pConnection->pRequest->ErrorCode)
  415. UlSetErrorCode(pConnection->pRequest, UlError, NULL);
  416. UlSendErrorResponse( pConnection );
  417. }
  418. //
  419. // done with the lock
  420. //
  421. UlReleasePushLockExclusive(&pConnection->PushLock);
  422. //
  423. // and release the connection added in UlHttpReceive
  424. //
  425. UL_DEREFERENCE_HTTP_CONNECTION(pConnection);
  426. //
  427. // Complete the IRP outside the connection resource to reduce chance
  428. // of contentions. This is because the delivered request can cause
  429. // a response being sent on another thread than the current one,
  430. // which calls UlResumeParsing after done with the send. Completing the
  431. // receive IRP inside the connection resource can make UlResumeParsing
  432. // block because we may not have released the resource by then (this
  433. // is the case because IoCompleteRequest can cause a thread switch).
  434. //
  435. if (pIrpToComplete)
  436. {
  437. //
  438. // Use IO_NO_INCREMENT to avoid the work thread being rescheduled.
  439. //
  440. UlCompleteRequest(pIrpToComplete, IO_NO_INCREMENT);
  441. }
  442. CHECK_STATUS(Status);
  443. } // UlpHandleRequest
  444. /***************************************************************************++
  445. Routine Description:
  446. When we finish sending a response we call into this function to
  447. kick the parser back into action.
  448. Arguments:
  449. pHttpConn - the connection on which to resume
  450. FromCache - True if we are called from send cache completion.
  451. InDisconnect - if a disconnect is in progress
  452. --***************************************************************************/
  453. VOID
  454. UlResumeParsing(
  455. IN PUL_HTTP_CONNECTION pHttpConn,
  456. IN BOOLEAN FromCache,
  457. IN BOOLEAN InDisconnect
  458. )
  459. {
  460. NTSTATUS Status = STATUS_SUCCESS;
  461. PUL_INTERNAL_REQUEST pRequest;
  462. KIRQL OldIrql;
  463. PIRP pIrpToComplete = NULL;
  464. //
  465. // Sanity check.
  466. //
  467. PAGED_CODE();
  468. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConn));
  469. //
  470. // If the connection is going down, just bail out.
  471. //
  472. if (!pHttpConn->UlconnDestroyed)
  473. {
  474. UlAcquirePushLockExclusive(&pHttpConn->PushLock);
  475. if (!pHttpConn->UlconnDestroyed)
  476. {
  477. pRequest = pHttpConn->pRequest;
  478. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  479. if (FromCache)
  480. {
  481. //
  482. // For cache case, cleanup the last request and try to
  483. // resume parse right away.
  484. //
  485. UlCleanupHttpConnection(pHttpConn);
  486. }
  487. else if (!pRequest->ContentLength && !pRequest->Chunked)
  488. {
  489. //
  490. // For cache-miss, cleanup the last request only if a graceful
  491. // disconnect is in progress, or if this request does not have
  492. // any entity body.
  493. //
  494. ASSERT(1 == pRequest->SentLast);
  495. UlCleanupHttpConnection(pHttpConn);
  496. }
  497. else
  498. {
  499. //
  500. // This is a cache-miss case however we may still have entity
  501. // body to drain before we can continue parsing the next request.
  502. //
  503. pRequest->InDrain = 1;
  504. UlProcessBufferQueue(pRequest, NULL, 0);
  505. //
  506. // We are done with the request if we have parsed all the data.
  507. // Clean up the request from the connection so we can start
  508. // parsing a new request.
  509. //
  510. if (ParseDoneState == pRequest->ParseState)
  511. {
  512. ASSERT(0 == pRequest->ChunkBytesToRead);
  513. UlCleanupHttpConnection(pHttpConn);
  514. }
  515. else
  516. {
  517. PUL_TIMEOUT_INFO_ENTRY pTimeoutInfo;
  518. //
  519. // Waiting for more data to parse/drain. Put the
  520. // connection back to idle timer to avoid waiting forever
  521. // under DOS attack.
  522. //
  523. pTimeoutInfo = &pHttpConn->TimeoutInfo;
  524. UlLockTimeoutInfo(
  525. pTimeoutInfo,
  526. &OldIrql
  527. );
  528. if (UlIsConnectionTimerOff(pTimeoutInfo,
  529. TimerConnectionIdle))
  530. {
  531. UlSetConnectionTimer(
  532. pTimeoutInfo,
  533. TimerConnectionIdle
  534. );
  535. }
  536. UlUnlockTimeoutInfo(
  537. pTimeoutInfo,
  538. OldIrql
  539. );
  540. UlEvaluateTimerState(
  541. pTimeoutInfo
  542. );
  543. }
  544. }
  545. //
  546. // Kick off the parser if no disconnect is in progress.
  547. //
  548. if (!InDisconnect)
  549. {
  550. Status = UlpParseNextRequest(pHttpConn, FALSE, &pIrpToComplete);
  551. if (!NT_SUCCESS(Status) && pHttpConn->pRequest != NULL)
  552. {
  553. //
  554. // Uh oh, something bad happened: send back an error (which
  555. // should have been set by UlpParseNextRequest).
  556. //
  557. ASSERT(UlErrorNone != pHttpConn->pRequest->ErrorCode);
  558. UlSendErrorResponse(pHttpConn);
  559. }
  560. }
  561. }
  562. UlReleasePushLockExclusive(&pHttpConn->PushLock);
  563. }
  564. //
  565. // Complete the IRP outside the connection resource. See comment
  566. // in UlpHandleRequest for detailed reasons.
  567. //
  568. if (pIrpToComplete)
  569. {
  570. //
  571. // Use IO_NO_INCREMENT to avoid the work thread being rescheduled.
  572. //
  573. UlCompleteRequest(pIrpToComplete, IO_NO_INCREMENT);
  574. }
  575. CHECK_STATUS(Status);
  576. } // UlResumeParsing
  577. /***************************************************************************++
  578. Routine Description:
  579. Validates certain requirements about verbs and versions.
  580. If not met, will set the error code and return STATUS_INVALID_PARAMETER
  581. Arguments:
  582. pRequest - the request to validate. Must be cooked.
  583. --***************************************************************************/
  584. NTSTATUS
  585. UlCheckProtocolCompliance(
  586. IN PUL_INTERNAL_REQUEST pRequest
  587. )
  588. {
  589. HTTP_VERB Verb = pRequest->Verb;
  590. //
  591. // Sanity check
  592. //
  593. PAGED_CODE();
  594. ASSERT( UL_IS_VALID_INTERNAL_REQUEST(pRequest) );
  595. ASSERT(pRequest->ParseState > ParseCookState);
  596. //
  597. // If the major version is greater than 1, fail.
  598. //
  599. if (HTTP_GREATER_EQUAL_VERSION(pRequest->Version, 2, 0))
  600. {
  601. UlTraceError(PARSER,
  602. ("UlCheckProtocolCompliance: HTTP/%hu.%hu is invalid\n",
  603. pRequest->Version.MajorVersion,
  604. pRequest->Version.MinorVersion
  605. ));
  606. UlSetErrorCode(pRequest, UlErrorVersion, NULL);
  607. return STATUS_INVALID_PARAMETER;
  608. }
  609. if (HTTP_GREATER_EQUAL_VERSION(pRequest->Version, 1, 1))
  610. {
  611. //
  612. // 1.1 requests MUST have a host header
  613. //
  614. if (!pRequest->HeaderValid[HttpHeaderHost])
  615. {
  616. UlTraceError(PARSER,
  617. ("UlCheckProtocolCompliance: "
  618. "HTTP/1.%hu must have Host header\n",
  619. pRequest->Version.MinorVersion
  620. ));
  621. UlSetErrorCode(pRequest, UlErrorHost, NULL);
  622. return STATUS_INVALID_PARAMETER;
  623. }
  624. }
  625. else if (HTTP_LESS_VERSION(pRequest->Version, 1, 0))
  626. {
  627. // Anything other than HTTP/0.9 should have been rejected earlier
  628. ASSERT(HTTP_EQUAL_VERSION(pRequest->Version, 0, 9));
  629. // HTTP/0.9 only supports GET
  630. if (Verb != HttpVerbGET)
  631. {
  632. UlTraceError(PARSER,
  633. ("UlCheckProtocolCompliance: "
  634. "'%s' invalid on HTTP/0.9\n",
  635. UlVerbToString(Verb)
  636. ));
  637. UlSetErrorCode(pRequest, UlErrorVerb, NULL);
  638. return STATUS_INVALID_PARAMETER;
  639. }
  640. return STATUS_SUCCESS;
  641. }
  642. //
  643. // Make sure that POSTs and PUTs have a message body.
  644. // Requests must either be chunked or have a content length.
  645. //
  646. if ((Verb == HttpVerbPOST || Verb == HttpVerbPUT)
  647. && (!pRequest->Chunked)
  648. && (!pRequest->HeaderValid[HttpHeaderContentLength]))
  649. {
  650. UlTraceError(PARSER,
  651. ("UlCheckProtocolCompliance: "
  652. "HTTP/1.%hu '%s' must have entity body\n",
  653. pRequest->Version.MinorVersion,
  654. UlVerbToString(Verb)
  655. ));
  656. UlSetErrorCode(pRequest, UlErrorContentLength, NULL);
  657. return STATUS_INVALID_PARAMETER;
  658. }
  659. //
  660. // TRACE and TRACK are not allowed to have an entity body.
  661. // If an entity body is expected, we will be in ParseEntityBodyState.
  662. //
  663. if ((pRequest->ParseState != ParseDoneState)
  664. && (Verb == HttpVerbTRACE || Verb == HttpVerbTRACK))
  665. {
  666. UlTraceError(PARSER,
  667. ("UlCheckProtocolCompliance: "
  668. "HTTP/1.%hu '%s' must not have entity body\n",
  669. pRequest->Version.MinorVersion,
  670. UlVerbToString(Verb)
  671. ));
  672. UlSetErrorCode(pRequest, UlError, NULL);
  673. return STATUS_INVALID_PARAMETER;
  674. }
  675. return STATUS_SUCCESS;
  676. } // UlCheckProtocolCompliance
  677. /***************************************************************************++
  678. Routine Description:
  679. Tries to parse data attached to the connection into a request. If
  680. a complete request header is parsed, the request will be dispatched
  681. to an Application Pool.
  682. This function assumes the caller is holding the connection resource!
  683. Arguments:
  684. pConnection - the HTTP_CONNECTION with data to parse.
  685. MoreRequestBuffers - if TRUE, this is not the last request buffer
  686. currently attached to the connection
  687. --***************************************************************************/
  688. NTSTATUS
  689. UlpParseNextRequest(
  690. IN PUL_HTTP_CONNECTION pConnection,
  691. IN BOOLEAN MoreRequestBuffers,
  692. OUT PIRP *pIrpToComplete
  693. )
  694. {
  695. NTSTATUS Status;
  696. PUL_INTERNAL_REQUEST pRequest = NULL;
  697. ULONG BytesTaken;
  698. ULONG BufferLength;
  699. BOOLEAN ResumeParsing;
  700. KIRQL OldIrql;
  701. PARSE_STATE OldState;
  702. //
  703. // Sanity check.
  704. //
  705. PAGED_CODE();
  706. ASSERT( UL_IS_VALID_HTTP_CONNECTION( pConnection ) );
  707. ASSERT( NULL == pIrpToComplete || NULL == *pIrpToComplete );
  708. ASSERT(UlDbgPushLockOwnedExclusive(&pConnection->PushLock));
  709. Status = STATUS_SUCCESS;
  710. UlTrace(HTTP_IO, ("http!UlpParseNextRequest(httpconn = %p)\n", pConnection));
  711. //
  712. // Only parse the next request if
  713. //
  714. // We haven't dispatched the current request yet
  715. // OR
  716. // The current request has unparsed entity body or trailers.
  717. //
  718. if ((pConnection->pRequest == NULL)
  719. || (!pConnection->WaitingForResponse)
  720. || (pConnection->pRequest->ParseState == ParseEntityBodyState)
  721. || (pConnection->pRequest->ParseState == ParseTrailerState))
  722. {
  723. //
  724. // loop consuming the buffer, we will make multiple iterations
  725. // if a single request spans multiple buffers.
  726. //
  727. for (;;)
  728. {
  729. ASSERT( UlpIsValidRequestBufferList( pConnection ) );
  730. Status = UlpAdjustBuffers(pConnection);
  731. if (!NT_SUCCESS(Status))
  732. {
  733. if (Status == STATUS_MORE_PROCESSING_REQUIRED)
  734. {
  735. Status = STATUS_SUCCESS;
  736. }
  737. break;
  738. }
  739. //
  740. // Since BufferLength is a ULONG, it can never be negative.
  741. // So, if UsedBytes is less than ParsedBytes, BufferLength
  742. // will be very large, and non-zero.
  743. //
  744. ASSERT( pConnection->pCurrentBuffer->UsedBytes >
  745. pConnection->pCurrentBuffer->ParsedBytes );
  746. BufferLength = pConnection->pCurrentBuffer->UsedBytes -
  747. pConnection->pCurrentBuffer->ParsedBytes;
  748. //
  749. // do we need to create a request object?
  750. //
  751. if (pConnection->pRequest == NULL)
  752. {
  753. //
  754. // First shot at reading a request, allocate a request object
  755. //
  756. Status = UlpCreateHttpRequest(
  757. pConnection,
  758. &pConnection->pRequest
  759. );
  760. if (NT_SUCCESS(Status) == FALSE)
  761. goto end;
  762. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pConnection->pRequest));
  763. UlTrace(HTTP_IO, (
  764. "http!UlpParseNextRequest created "
  765. "pRequest = %p for httpconn = %p\n",
  766. pConnection->pRequest,
  767. pConnection
  768. ));
  769. //
  770. // To be exact precise about the life-time of this
  771. // request, copy the starting TIMESTAMP from connection
  772. // pointer. But that won't work since we may get hit by
  773. // multiple requests to the same connection. So we won't
  774. // be that much precise.
  775. //
  776. KeQuerySystemTime( &(pConnection->pRequest->TimeStamp) );
  777. TRACE_TIME(
  778. pConnection->ConnectionId,
  779. pConnection->pRequest->RequestId,
  780. TIME_ACTION_CREATE_REQUEST
  781. );
  782. WRITE_REF_TRACE_LOG2(
  783. g_pHttpConnectionTraceLog,
  784. pConnection->pConnection->pHttpTraceLog,
  785. REF_ACTION_INSERT_REQUEST,
  786. pConnection->RefCount,
  787. pConnection->pRequest,
  788. __FILE__,
  789. __LINE__
  790. );
  791. //
  792. // stop the Connection Timeout timer
  793. // and start the Header Wait timer
  794. //
  795. UlLockTimeoutInfo(
  796. &pConnection->TimeoutInfo,
  797. &OldIrql
  798. );
  799. UlResetConnectionTimer(
  800. &pConnection->TimeoutInfo,
  801. TimerConnectionIdle
  802. );
  803. UlSetConnectionTimer(
  804. &pConnection->TimeoutInfo,
  805. TimerHeaderWait
  806. );
  807. UlUnlockTimeoutInfo(
  808. &pConnection->TimeoutInfo,
  809. OldIrql
  810. );
  811. UlEvaluateTimerState(
  812. &pConnection->TimeoutInfo
  813. );
  814. }
  815. OldState = pConnection->pRequest->ParseState;
  816. UlTrace( PARSER, (
  817. "*** pConn %p, pReq %p, ParseState %d (%s), curbuf=%d\n",
  818. pConnection,
  819. pConnection->pRequest,
  820. OldState,
  821. UlParseStateToString(OldState),
  822. pConnection->pCurrentBuffer->BufferNumber
  823. ));
  824. switch (pConnection->pRequest->ParseState)
  825. {
  826. case ParseVerbState:
  827. case ParseUrlState:
  828. case ParseVersionState:
  829. case ParseHeadersState:
  830. case ParseCookState:
  831. pRequest = pConnection->pRequest;
  832. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  833. //
  834. // parse it !
  835. //
  836. Status = UlParseHttp(
  837. pRequest,
  838. GET_REQUEST_BUFFER_POS(pConnection->pCurrentBuffer),
  839. BufferLength,
  840. &BytesTaken
  841. );
  842. ASSERT(BytesTaken <= BufferLength);
  843. UlTraceVerbose(PARSER, (
  844. "UlpParseNextRequest(pRequest = %p) "
  845. "UlParseHttp: states "
  846. "%d (%s) -> %d (%s), %lu bytes taken; "
  847. "%s\n",
  848. pRequest,
  849. OldState,
  850. UlParseStateToString(OldState),
  851. pConnection->pRequest->ParseState,
  852. UlParseStateToString(pConnection->pRequest->ParseState),
  853. BytesTaken,
  854. HttpStatusToString(Status)
  855. ));
  856. pConnection->pCurrentBuffer->ParsedBytes += BytesTaken;
  857. BufferLength -= BytesTaken;
  858. //
  859. // Need some accounting for Logging
  860. //
  861. pRequest->BytesReceived += BytesTaken;
  862. //
  863. // did we consume any of the data? if so, give the request
  864. // a pointer to the buffer
  865. //
  866. if (BytesTaken > 0)
  867. {
  868. if (pRequest->pHeaderBuffer == NULL)
  869. {
  870. //
  871. // store its location, for later release
  872. //
  873. pRequest->pHeaderBuffer = pConnection->pCurrentBuffer;
  874. }
  875. pRequest->pLastHeaderBuffer = pConnection->pCurrentBuffer;
  876. if (!UlpReferenceBuffers(
  877. pRequest,
  878. pConnection->pCurrentBuffer
  879. ))
  880. {
  881. Status = STATUS_NO_MEMORY;
  882. goto end;
  883. }
  884. }
  885. //
  886. // We may still need to receive some transport bytes not taken
  887. // even if UlParseHttp calls returns zero. Especially if some
  888. // large header value is spanning over multiple request buffers
  889. // and some part of it is available in tdi but not received yet.
  890. //
  891. UlpConsumeBytesFromConnection(pConnection, BytesTaken);
  892. //
  893. // did everything work out ok?
  894. //
  895. if (!NT_SUCCESS(Status))
  896. {
  897. if (Status == STATUS_MORE_PROCESSING_REQUIRED)
  898. {
  899. ULONG FullBytesReceived;
  900. FullBytesReceived = (ULONG)(
  901. (pRequest->BytesReceived + BufferLength));
  902. if (FullBytesReceived < g_UlMaxRequestBytes)
  903. {
  904. //
  905. // we need more transport data
  906. //
  907. pConnection->NeedMoreData = 1;
  908. Status = STATUS_SUCCESS;
  909. }
  910. else
  911. {
  912. //
  913. // The request has grown too large. Send back
  914. // an error.
  915. //
  916. if (pRequest->ParseState == ParseUrlState)
  917. {
  918. UlTraceError(PARSER, (
  919. "UlpParseNextRequest(pRequest = %p)"
  920. " ERROR: URL is too big\n",
  921. pRequest
  922. ));
  923. UlSetErrorCode(
  924. pRequest,
  925. UlErrorUrlLength,
  926. NULL
  927. );
  928. }
  929. else
  930. {
  931. UlTraceError(PARSER, (
  932. "UlpParseNextRequest(pRequest = %p)"
  933. " ERROR: request is too big\n",
  934. pRequest
  935. ));
  936. UlSetErrorCode(
  937. pRequest,
  938. UlErrorRequestLength,
  939. NULL
  940. );
  941. }
  942. Status = STATUS_SECTION_TOO_BIG;
  943. goto end;
  944. }
  945. }
  946. else
  947. {
  948. //
  949. // some other bad error!
  950. //
  951. goto end;
  952. }
  953. }
  954. //
  955. // if we're not done parsing the request, we need more data.
  956. // it's not bad enough to set NeedMoreData as nothing important
  957. // spanned buffer boundaries (header values, etc..) . it was
  958. // a clean split. no buffer merging is necessary. simply skip
  959. // to the next buffer.
  960. //
  961. if (pRequest->ParseState <= ParseCookState)
  962. {
  963. continue;
  964. }
  965. //
  966. // all done, mark the sequence number on this request
  967. //
  968. pRequest->RecvNumber = pConnection->NextRecvNumber;
  969. pConnection->NextRecvNumber += 1;
  970. UlTrace(HTTP_IO, (
  971. "http!UlpParseNextRequest(httpconn = %p) built request %p\n",
  972. pConnection,
  973. pRequest
  974. ));
  975. //
  976. // Stop the Header Wait timer
  977. //
  978. UlLockTimeoutInfo(
  979. &pConnection->TimeoutInfo,
  980. &OldIrql
  981. );
  982. UlResetConnectionTimer(
  983. &pConnection->TimeoutInfo,
  984. TimerHeaderWait
  985. );
  986. UlUnlockTimeoutInfo(
  987. &pConnection->TimeoutInfo,
  988. OldIrql
  989. );
  990. UlEvaluateTimerState(
  991. &pConnection->TimeoutInfo
  992. );
  993. //
  994. // check protocol compliance
  995. //
  996. Status = UlCheckProtocolCompliance(pRequest);
  997. if (!NT_SUCCESS(Status))
  998. {
  999. //
  1000. // This request is bad. Send a 400.
  1001. //
  1002. ASSERT(pRequest->ParseState == ParseErrorState);
  1003. goto end;
  1004. }
  1005. //
  1006. // Record the Request Details.
  1007. // This should be the only place where the URL is logged.
  1008. //
  1009. if (ETW_LOG_RESOURCE())
  1010. {
  1011. UlEtwTraceEvent(
  1012. &UlTransGuid,
  1013. ETW_TYPE_ULPARSE_REQ,
  1014. (PVOID) &pRequest,
  1015. sizeof(PVOID),
  1016. &pRequest->Verb,
  1017. sizeof(HTTP_VERB),
  1018. pRequest->CookedUrl.pUrl ,
  1019. pRequest->CookedUrl.Length,
  1020. NULL,
  1021. 0
  1022. );
  1023. }
  1024. Status = UlpDeliverHttpRequest(
  1025. pConnection,
  1026. &ResumeParsing,
  1027. pIrpToComplete
  1028. );
  1029. if (!NT_SUCCESS(Status)) {
  1030. goto end;
  1031. }
  1032. if (ResumeParsing)
  1033. {
  1034. //
  1035. // We have hit the cache entry and sent the response.
  1036. // There is no more use for the request anymore so
  1037. // unlink it from the connection and try parsing the
  1038. // next request immediately. However if we have reached to
  1039. // max allowed pipelined requests, we will resume parse at
  1040. // cache send completion. In which case UlpDeliverHttpRequest
  1041. // returns False too.
  1042. //
  1043. UlCleanupHttpConnection(pConnection);
  1044. continue;
  1045. }
  1046. //
  1047. // if we're done parsing the request break out
  1048. // of the loop. Otherwise keep going around
  1049. // so we can pick up the entity body.
  1050. //
  1051. if (pRequest->ParseState == ParseDoneState)
  1052. {
  1053. goto end;
  1054. }
  1055. //
  1056. // done with protocol parsing. keep looping.
  1057. //
  1058. break;
  1059. case ParseEntityBodyState:
  1060. case ParseTrailerState:
  1061. pRequest = pConnection->pRequest;
  1062. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  1063. //
  1064. // is there anything for us to parse?
  1065. //
  1066. UlTraceVerbose(PARSER, (
  1067. "UlpParseNextRequest(pRequest=%p, httpconn=%p): "
  1068. "ChunkBytesToParse = %I64u, ParseState = %d (%s).\n",
  1069. pRequest, pConnection, pRequest->ChunkBytesToParse,
  1070. pConnection->pRequest->ParseState,
  1071. UlParseStateToString(pConnection->pRequest->ParseState)
  1072. ));
  1073. if (pRequest->ChunkBytesToParse > 0 || pRequest->Chunked)
  1074. {
  1075. //
  1076. // Set/bump the Entity Body Receive timer
  1077. //
  1078. UlLockTimeoutInfo(
  1079. &pConnection->TimeoutInfo,
  1080. &OldIrql
  1081. );
  1082. UlSetConnectionTimer(
  1083. &pConnection->TimeoutInfo,
  1084. TimerEntityBody
  1085. );
  1086. UlUnlockTimeoutInfo(
  1087. &pConnection->TimeoutInfo,
  1088. OldIrql
  1089. );
  1090. UlEvaluateTimerState(
  1091. &pConnection->TimeoutInfo
  1092. );
  1093. }
  1094. if (pRequest->ChunkBytesToParse > 0)
  1095. {
  1096. ULONG BytesToSkip;
  1097. //
  1098. // is this the first chunk we've parsed?
  1099. //
  1100. ASSERT(pConnection->pCurrentBuffer);
  1101. if (pRequest->pChunkBuffer == NULL)
  1102. {
  1103. //
  1104. // store its location, this is where to start reading
  1105. //
  1106. pRequest->pChunkBuffer = pConnection->pCurrentBuffer;
  1107. pRequest->pChunkLocation = GET_REQUEST_BUFFER_POS(
  1108. pConnection->pCurrentBuffer
  1109. );
  1110. }
  1111. //
  1112. // how much should we parse?
  1113. //
  1114. BytesToSkip = (ULONG)(
  1115. MIN(
  1116. pRequest->ChunkBytesToParse,
  1117. BufferLength
  1118. )
  1119. );
  1120. //
  1121. // update that we parsed this piece
  1122. //
  1123. pRequest->ChunkBytesToParse -= BytesToSkip;
  1124. pRequest->ChunkBytesParsed += BytesToSkip;
  1125. pConnection->pCurrentBuffer->ParsedBytes += BytesToSkip;
  1126. BufferLength -= BytesToSkip;
  1127. //
  1128. // Need some accounting info for Logging
  1129. //
  1130. pRequest->BytesReceived += BytesToSkip;
  1131. }
  1132. //
  1133. // process any irp's waiting for entity body
  1134. //
  1135. UlTraceVerbose(PARSER, (
  1136. "UlpParseNextRequest(pRequest=%p, httpconn=%p): "
  1137. "%sabout to process buffer queue\n",
  1138. pRequest, pConnection,
  1139. MoreRequestBuffers ? "not " : ""
  1140. ));
  1141. if (!MoreRequestBuffers)
  1142. {
  1143. UlProcessBufferQueue(pRequest, NULL, 0);
  1144. }
  1145. //
  1146. // check to see there is another chunk
  1147. //
  1148. UlTraceVerbose(PARSER, (
  1149. "UlpParseNextRequest(pRequest=%p, httpconn=%p, "
  1150. "curbuf=%p(#%d)): checking to see if another chunk.\n",
  1151. pRequest, pConnection,
  1152. pConnection->pCurrentBuffer,
  1153. pConnection->pCurrentBuffer->BufferNumber
  1154. ));
  1155. Status = UlParseHttp(
  1156. pRequest,
  1157. GET_REQUEST_BUFFER_POS(pConnection->pCurrentBuffer),
  1158. BufferLength,
  1159. &BytesTaken
  1160. );
  1161. UlTraceVerbose(PARSER, (
  1162. "UlpParseNextRequest(pRequest = %p) "
  1163. "UlParseHttp: states (EB/T) %d (%s) -> %d (%s), "
  1164. "%lu bytes taken\n",
  1165. pRequest,
  1166. OldState,
  1167. UlParseStateToString(OldState),
  1168. pConnection->pRequest->ParseState,
  1169. UlParseStateToString(pConnection->pRequest->ParseState),
  1170. BytesTaken
  1171. ));
  1172. pConnection->pCurrentBuffer->ParsedBytes += BytesTaken;
  1173. BufferLength -= BytesTaken;
  1174. //
  1175. // Need some accounting info for Logging
  1176. //
  1177. pRequest->BytesReceived += BytesTaken;
  1178. //
  1179. // was there enough in the buffer to please?
  1180. //
  1181. if (NT_SUCCESS(Status) == FALSE)
  1182. {
  1183. if (Status == STATUS_MORE_PROCESSING_REQUIRED)
  1184. {
  1185. //
  1186. // we need more transport data
  1187. //
  1188. pConnection->NeedMoreData = 1;
  1189. Status = STATUS_SUCCESS;
  1190. continue;
  1191. }
  1192. else
  1193. {
  1194. //
  1195. // some other bad error !
  1196. //
  1197. goto end;
  1198. }
  1199. }
  1200. //
  1201. // are we all done parsing it ?
  1202. //
  1203. if (pRequest->ParseState == ParseDoneState)
  1204. {
  1205. UlTraceVerbose(PARSER, (
  1206. "UlpParseNextRequest(pRequest = %p) all done\n",
  1207. pRequest
  1208. ));
  1209. //
  1210. // Once more, with feeling. Check to see if there
  1211. // are any remaining buffers to be processed or irps
  1212. // to be completed (e.g., catch a solo zero-length
  1213. // chunk)
  1214. //
  1215. UlProcessBufferQueue(pRequest, NULL, 0);
  1216. //
  1217. // Stop all timers (including entity body)
  1218. //
  1219. UlLockTimeoutInfo(
  1220. &pConnection->TimeoutInfo,
  1221. &OldIrql
  1222. );
  1223. UlResetConnectionTimer(
  1224. &pConnection->TimeoutInfo,
  1225. TimerEntityBody
  1226. );
  1227. UlUnlockTimeoutInfo(
  1228. &pConnection->TimeoutInfo,
  1229. OldIrql
  1230. );
  1231. if (pRequest->InDrain)
  1232. {
  1233. //
  1234. // If we enter the parser in drain mode, clean up the
  1235. // request from the connection so we can start parsing
  1236. // a new request.
  1237. //
  1238. ASSERT(0 == pRequest->ChunkBytesToRead);
  1239. UlCleanupHttpConnection(pConnection);
  1240. }
  1241. else
  1242. {
  1243. //
  1244. // Exit the parser and wait for the ReceiveEntityBody
  1245. // IRPs to pick up the data. Make sure we don't
  1246. // disconnect a half-closed connection in this case.
  1247. //
  1248. goto end;
  1249. }
  1250. }
  1251. //
  1252. // keep looping.
  1253. //
  1254. break;
  1255. case ParseErrorState:
  1256. //
  1257. // ignore this buffer
  1258. //
  1259. Status = STATUS_SUCCESS;
  1260. goto end;
  1261. case ParseDoneState:
  1262. default:
  1263. //
  1264. // this should never happen
  1265. //
  1266. ASSERT(! "invalid parse state");
  1267. Status = STATUS_INVALID_DEVICE_STATE;
  1268. goto end;
  1269. } // switch (pConnection->pRequest->ParseState)
  1270. } // for(;;)
  1271. }
  1272. //
  1273. // Handle a graceful close by the client.
  1274. //
  1275. if (pConnection->LastBufferNumber > 0 &&
  1276. pConnection->NextBufferToParse == pConnection->LastBufferNumber)
  1277. {
  1278. ASSERT(pConnection->LastBufferNumber > 0);
  1279. #if 0
  1280. if (pConnection->pRequest)
  1281. {
  1282. // can't drain from a gracefully disconnected connection
  1283. pConnection->pRequest->InDrain = 0;
  1284. }
  1285. #endif // 0
  1286. UlpCloseDisconnectedConnection(pConnection);
  1287. }
  1288. end:
  1289. if (!NT_SUCCESS(Status))
  1290. {
  1291. if (NULL != pConnection->pRequest
  1292. && UlErrorNone == pConnection->pRequest->ErrorCode)
  1293. {
  1294. UlTraceError(PARSER, (
  1295. "UlpParseNextRequest(pRequest = %p): "
  1296. "generic failure for %s\n",
  1297. pRequest, HttpStatusToString(Status)
  1298. ));
  1299. UlSetErrorCode( pConnection->pRequest, UlError, NULL);
  1300. }
  1301. }
  1302. UlTrace(PARSER, (
  1303. "UlpParseNextRequest(pRequest = %p): returning %s. "
  1304. "NeedMoreData=%d\n",
  1305. pRequest, HttpStatusToString(Status),
  1306. pConnection->NeedMoreData
  1307. ));
  1308. return Status;
  1309. } // UlpParseNextRequest
  1310. /***************************************************************************++
  1311. Routine Description:
  1312. DeliverHttpRequest may want to get the cgroup info for the request if it's
  1313. not a cache hit. Similarly sendresponse may want to get this info - later-
  1314. even if it's cache hit, when logging is enabled on the hit. Therefore we
  1315. have created a new function for this to easily maintain the functionality.
  1316. Arguments:
  1317. pConnection - The connection whose request we are to deliver.
  1318. --***************************************************************************/
  1319. NTSTATUS
  1320. UlGetCGroupForRequest(
  1321. IN PUL_INTERNAL_REQUEST pRequest
  1322. )
  1323. {
  1324. NTSTATUS Status;
  1325. BOOLEAN OptionsStar;
  1326. //
  1327. // Sanity check
  1328. //
  1329. PAGED_CODE();
  1330. Status = STATUS_SUCCESS;
  1331. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  1332. //
  1333. // Lookup the config group information for this url .
  1334. //
  1335. // don't include the query string in the lookup.
  1336. // route OPTIONS * as though it were OPTIONS /
  1337. //
  1338. if (pRequest->CookedUrl.pQueryString != NULL)
  1339. {
  1340. pRequest->CookedUrl.pQueryString[0] = UNICODE_NULL;
  1341. }
  1342. if ((pRequest->Verb == HttpVerbOPTIONS)
  1343. && (pRequest->CookedUrl.pAbsPath[0] == '*')
  1344. && (pRequest->CookedUrl.pAbsPath[1] == UNICODE_NULL))
  1345. {
  1346. pRequest->CookedUrl.pAbsPath[0] = '/';
  1347. OptionsStar = TRUE;
  1348. } else {
  1349. OptionsStar = FALSE;
  1350. }
  1351. //
  1352. // Get the Url Config Info
  1353. //
  1354. Status = UlGetConfigGroupInfoForUrl(
  1355. pRequest->CookedUrl.pUrl,
  1356. pRequest,
  1357. &pRequest->ConfigInfo
  1358. );
  1359. if (pRequest->CookedUrl.pQueryString != NULL)
  1360. {
  1361. pRequest->CookedUrl.pQueryString[0] = L'?';
  1362. }
  1363. //
  1364. // restore the * in the path
  1365. //
  1366. if (OptionsStar) {
  1367. pRequest->CookedUrl.pAbsPath[0] = '*';
  1368. }
  1369. return Status;
  1370. } // UlGetCGroupForRequest
  1371. /***************************************************************************++
  1372. Routine Description:
  1373. Takes a parsed http request and tries to deliver it to something
  1374. that can send a response.
  1375. First we try the cache. If there is no cache entry we try to route
  1376. to an app pool.
  1377. We send back an auto response if the control channel
  1378. or config group is inactive. If we can't do any of those things we
  1379. set an error code in the HTTP_REQUEST and return a failure status.
  1380. The caller will take care of sending the error.
  1381. Arguments:
  1382. pConnection - The connection whose request we are to deliver.
  1383. --***************************************************************************/
  1384. NTSTATUS
  1385. UlpDeliverHttpRequest(
  1386. IN PUL_HTTP_CONNECTION pConnection,
  1387. OUT PBOOLEAN pResumeParsing,
  1388. OUT PIRP *pIrpToComplete
  1389. )
  1390. {
  1391. NTSTATUS Status;
  1392. PUL_INTERNAL_REQUEST pRequest;
  1393. UL_SEND_CACHE_RESULT SendCacheResult;
  1394. HTTP_ENABLED_STATE CurrentState;
  1395. ULONG Connections;
  1396. PUL_SITE_COUNTER_ENTRY pCtr;
  1397. //
  1398. // Sanity check
  1399. //
  1400. PAGED_CODE();
  1401. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pConnection));
  1402. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pConnection->pRequest));
  1403. pRequest = pConnection->pRequest;
  1404. *pResumeParsing = FALSE;
  1405. SendCacheResult = UlSendCacheResultNotSet;
  1406. Status = STATUS_SUCCESS;
  1407. //
  1408. // Do we have a cache hit?
  1409. // Set WaitingForResponse to 1 before calling UlSendCachedResponse
  1410. // because the send may be completed before we return.
  1411. //
  1412. pConnection->WaitingForResponse = 1;
  1413. UlTrace( PARSER, (
  1414. "***3 pConnection %p->WaitingForResponse = 1\n",
  1415. pConnection
  1416. ));
  1417. pRequest->CachePreconditions = UlCheckCachePreconditions(
  1418. pRequest,
  1419. pConnection
  1420. );
  1421. if (pRequest->CachePreconditions)
  1422. {
  1423. Status = UlSendCachedResponse(
  1424. pConnection,
  1425. &SendCacheResult,
  1426. pResumeParsing
  1427. );
  1428. switch (SendCacheResult)
  1429. {
  1430. case UlSendCacheResultNotSet:
  1431. ASSERT(!"CacheSendResult should be specified !");
  1432. break;
  1433. case UlSendCacheMiss:
  1434. g_UriCacheStats.MissTableCount++;
  1435. UlIncCounter(HttpGlobalCounterUriCacheMisses);
  1436. // Bounce back to user mode.
  1437. break;
  1438. case UlSendCacheServedFromCache:
  1439. ASSERT(NT_SUCCESS(Status));
  1440. //
  1441. // All done with this request. It's served from cache.
  1442. //
  1443. g_UriCacheStats.HitCount++;
  1444. UlIncCounter(HttpGlobalCounterUriCacheHits);
  1445. goto end;
  1446. case UlSendCachePreconditionFailed:
  1447. ASSERT(UlErrorPreconditionFailed == pRequest->ErrorCode); // Fall down
  1448. case UlSendCacheConnectionRefused:
  1449. ASSERT(STATUS_INVALID_DEVICE_STATE == Status); // Fall down
  1450. case UlSendCacheFailed:
  1451. {
  1452. //
  1453. // If a cache precondition failed during SendCacheResponse,
  1454. // Or connection is refused, or any other failure then bail
  1455. // out.
  1456. //
  1457. ASSERT(!NT_SUCCESS(Status));
  1458. pConnection->WaitingForResponse = 0;
  1459. UlTrace( PARSER, (
  1460. "***3 pConnection %p->WaitingForResponse = 0\n",
  1461. pConnection
  1462. ));
  1463. goto end;
  1464. }
  1465. break;
  1466. default:
  1467. ASSERT(! "Invalid UL_SEND_CACHE_RESULT !");
  1468. break;
  1469. }
  1470. }
  1471. else
  1472. {
  1473. //
  1474. // Update the cache-miss counters.
  1475. //
  1476. g_UriCacheStats.MissTableCount++;
  1477. UlIncCounter(HttpGlobalCounterUriCacheMisses);
  1478. }
  1479. //
  1480. // We didn't do a send from the cache, so we are not
  1481. // yet WaitingForResponse.
  1482. //
  1483. pConnection->WaitingForResponse = 0;
  1484. UlTrace( PARSER, (
  1485. "***3 pConnection %p->WaitingForResponse = 0\n",
  1486. pConnection
  1487. ));
  1488. //
  1489. // Allocate connection ID here since the request is going to be delivered
  1490. // to user.
  1491. //
  1492. if (HTTP_IS_NULL_ID(&(pConnection->ConnectionId)))
  1493. {
  1494. Status = UlCreateHttpConnectionId(pConnection);
  1495. if (!NT_SUCCESS(Status))
  1496. {
  1497. UlTraceError(PARSER, (
  1498. "UlpDeliverHttpRequest(pRequest = %p): "
  1499. "Failed to create conn ID\n",
  1500. pRequest
  1501. ));
  1502. UlSetErrorCode(pRequest, UlErrorInternalServer, NULL);
  1503. goto end;
  1504. }
  1505. pRequest->ConnectionId = pConnection->ConnectionId;
  1506. }
  1507. //
  1508. // Allocate request ID here since we didn't do it in UlCreateHttpRequest.
  1509. //
  1510. Status = UlAllocateRequestId(pRequest);
  1511. if (!NT_SUCCESS(Status))
  1512. {
  1513. UlTraceError(PARSER, (
  1514. "UlpDeliverHttpRequest(pRequest = %p): "
  1515. "Failed to allocate request ID\n",
  1516. pRequest
  1517. ));
  1518. UlSetErrorCode(pRequest, UlErrorInternalServer, NULL);
  1519. goto end;
  1520. }
  1521. //
  1522. // Get the cgroup for this request.
  1523. //
  1524. Status = UlGetCGroupForRequest( pRequest );
  1525. //
  1526. // CODEWORK+BUGBUG: need to check the port's actually matched
  1527. //
  1528. //
  1529. // check that the config group tree lookup matched
  1530. //
  1531. if (!NT_SUCCESS(Status) || pRequest->ConfigInfo.pAppPool == NULL)
  1532. {
  1533. //
  1534. // Could not route to a listening url, send
  1535. // back an http error. Always return error 400
  1536. // to show that host not found. This will also
  1537. // make us to be compliant with HTTP1.1 / 5.2
  1538. //
  1539. // REVIEW: What do we do about the site counter(s)
  1540. // REVIEW: when we can't route to a site? i.e., Connection Attempts?
  1541. UlTraceError(PARSER, (
  1542. "UlpDeliverHttpRequest(pRequest = %p): "
  1543. "no config group (%s) or AppPool(%p)\n",
  1544. pRequest,
  1545. HttpStatusToString(Status),
  1546. pRequest->ConfigInfo.pAppPool
  1547. ));
  1548. UlSetErrorCode(pRequest, UlErrorHost, NULL);
  1549. Status = STATUS_INVALID_DEVICE_STATE;
  1550. goto end;
  1551. }
  1552. //
  1553. // Check to see if there's a connection timeout value override
  1554. //
  1555. if (0L != pRequest->ConfigInfo.ConnectionTimeout)
  1556. {
  1557. UlSetPerSiteConnectionTimeoutValue(
  1558. &pRequest->pHttpConn->TimeoutInfo,
  1559. pRequest->ConfigInfo.ConnectionTimeout
  1560. );
  1561. }
  1562. //
  1563. // Check the connection limit of the site.
  1564. //
  1565. if (UlCheckSiteConnectionLimit(pConnection, &pRequest->ConfigInfo) == FALSE)
  1566. {
  1567. // If exceeding the site limit, send back 503 error and disconnect.
  1568. // NOTE: This code depends on the fact that UlSendErrorResponse always
  1569. // NOTE: disconnects. Otherwise we need a force disconnect here.
  1570. UlTraceError(PARSER, (
  1571. "UlpDeliverHttpRequest(pRequest = %p): "
  1572. "exceeded site connection limit\n",
  1573. pRequest
  1574. ));
  1575. UlSetErrorCode( pRequest,
  1576. UlErrorConnectionLimit,
  1577. pRequest->ConfigInfo.pAppPool
  1578. );
  1579. Status = STATUS_INVALID_DEVICE_STATE;
  1580. goto end;
  1581. }
  1582. //
  1583. // Perf Counters (non-cached)
  1584. //
  1585. pCtr = pRequest->ConfigInfo.pSiteCounters;
  1586. if (pCtr)
  1587. {
  1588. // NOTE: pCtr may be NULL if the SiteId was never set on the root-level
  1589. // NOTE: Config Group for the site. BVTs may need to be updated.
  1590. ASSERT(IS_VALID_SITE_COUNTER_ENTRY(pCtr));
  1591. UlIncSiteNonCriticalCounterUlong(pCtr, HttpSiteCounterAllReqs);
  1592. if (pCtr != pConnection->pPrevSiteCounters)
  1593. {
  1594. if (pConnection->pPrevSiteCounters)
  1595. {
  1596. // Decrement old site's counters & release ref count
  1597. UlDecSiteCounter(
  1598. pConnection->pPrevSiteCounters,
  1599. HttpSiteCounterCurrentConns
  1600. );
  1601. DEREFERENCE_SITE_COUNTER_ENTRY(pConnection->pPrevSiteCounters);
  1602. }
  1603. UlIncSiteNonCriticalCounterUlong(pCtr, HttpSiteCounterConnAttempts);
  1604. Connections = (ULONG) UlIncSiteCounter(pCtr, HttpSiteCounterCurrentConns);
  1605. UlMaxSiteCounter(
  1606. pCtr,
  1607. HttpSiteCounterMaxConnections,
  1608. Connections
  1609. );
  1610. // add ref for new site counters
  1611. REFERENCE_SITE_COUNTER_ENTRY(pCtr);
  1612. pConnection->pPrevSiteCounters = pCtr;
  1613. }
  1614. }
  1615. ASSERT(NT_SUCCESS(Status));
  1616. //
  1617. // Install a filter if BWT is enabled for this request's site.
  1618. // or for the control channel that owns the site. If fails
  1619. // refuse the connection back. (503)
  1620. //
  1621. Status = UlTcAddFilterForConnection(
  1622. pConnection,
  1623. &pRequest->ConfigInfo
  1624. );
  1625. if (!NT_SUCCESS(Status))
  1626. {
  1627. UlTraceError(PARSER, (
  1628. "UlpDeliverHttpRequest(pRequest = %p): "
  1629. "Bandwidth throttling failed: %s\n",
  1630. pRequest,
  1631. HttpStatusToString(Status)
  1632. ));
  1633. UlSetErrorCode( pRequest,
  1634. UlErrorUnavailable,
  1635. pRequest->ConfigInfo.pAppPool
  1636. );
  1637. goto end;
  1638. }
  1639. //
  1640. // the routing matched, let's check and see if we are active.
  1641. // first check the control channel.
  1642. //
  1643. if (pRequest->ConfigInfo.pControlChannel->State != HttpEnabledStateActive)
  1644. {
  1645. UlTraceError(HTTP_IO,
  1646. ("http!UlpDeliverHttpRequest Control Channel is inactive\n"
  1647. ));
  1648. CurrentState = HttpEnabledStateInactive;
  1649. }
  1650. // now check the cgroup
  1651. else if (pRequest->ConfigInfo.CurrentState != HttpEnabledStateActive)
  1652. {
  1653. UlTraceError(HTTP_IO,
  1654. ("http!UlpDeliverHttpRequest Config Group is inactive\n"
  1655. ));
  1656. CurrentState = HttpEnabledStateInactive;
  1657. }
  1658. else
  1659. {
  1660. CurrentState = HttpEnabledStateActive;
  1661. }
  1662. //
  1663. // well, are we active?
  1664. //
  1665. if (CurrentState == HttpEnabledStateActive)
  1666. {
  1667. //
  1668. // it's a normal request. Deliver to
  1669. // app pool (aka client)
  1670. //
  1671. Status = UlDeliverRequestToProcess(
  1672. pRequest->ConfigInfo.pAppPool,
  1673. pRequest,
  1674. pIrpToComplete
  1675. );
  1676. if (NT_SUCCESS(Status))
  1677. {
  1678. //
  1679. // All done with this request. Wait for response
  1680. // before going on.
  1681. //
  1682. pConnection->WaitingForResponse = 1;
  1683. // CODEWORK: Start the "Processing" Connection Timeout Timer.
  1684. UlTrace( PARSER, (
  1685. "***4 pConnection %p->WaitingForResponse = 1\n",
  1686. pConnection
  1687. ));
  1688. }
  1689. }
  1690. else
  1691. {
  1692. //
  1693. // we are not active. Send a 503 response
  1694. //
  1695. UlTraceError(PARSER, (
  1696. "UlpDeliverHttpRequest(pRequest = %p): inactive\n",
  1697. pRequest
  1698. ));
  1699. UlSetErrorCode( pRequest,
  1700. UlErrorUnavailable,
  1701. pRequest->ConfigInfo.pAppPool
  1702. );
  1703. Status = STATUS_INVALID_DEVICE_STATE;
  1704. }
  1705. end:
  1706. return Status;
  1707. } // UlpDeliverHttpRequest
  1708. /***************************************************************************++
  1709. Routine Description:
  1710. links the buffer into the sorted connection list.
  1711. Arguments:
  1712. pConnection - the connection to insert into
  1713. pRequestBuffer - the buffer to link in
  1714. --***************************************************************************/
  1715. VOID
  1716. UlpInsertBuffer(
  1717. PUL_HTTP_CONNECTION pConnection,
  1718. PUL_REQUEST_BUFFER pRequestBuffer
  1719. )
  1720. {
  1721. PLIST_ENTRY pEntry;
  1722. PUL_REQUEST_BUFFER pListBuffer = NULL;
  1723. ASSERT( UL_IS_VALID_HTTP_CONNECTION(pConnection) );
  1724. ASSERT( UlDbgPushLockOwnedExclusive( &pConnection->PushLock ) );
  1725. ASSERT( UL_IS_VALID_REQUEST_BUFFER(pRequestBuffer) );
  1726. ASSERT( pRequestBuffer->UsedBytes != 0 );
  1727. //
  1728. // figure out where to insert the buffer into our
  1729. // sorted queue (we need to enforce FIFO by number -
  1730. // head is the first in). optimize for ordered inserts by
  1731. // searching tail to head.
  1732. //
  1733. pEntry = pConnection->BufferHead.Blink;
  1734. while (pEntry != &(pConnection->BufferHead))
  1735. {
  1736. pListBuffer = CONTAINING_RECORD(
  1737. pEntry,
  1738. UL_REQUEST_BUFFER,
  1739. ListEntry
  1740. );
  1741. ASSERT( UL_IS_VALID_REQUEST_BUFFER(pListBuffer) );
  1742. //
  1743. // if the number is less than, put it here, we are
  1744. // searching in reverse sort order
  1745. //
  1746. if (pListBuffer->BufferNumber < pRequestBuffer->BufferNumber)
  1747. {
  1748. break;
  1749. }
  1750. //
  1751. // go on to the preceding one
  1752. //
  1753. pEntry = pEntry->Blink;
  1754. }
  1755. ASSERT(pEntry == &pConnection->BufferHead || NULL != pListBuffer);
  1756. UlTrace(
  1757. HTTP_IO, (
  1758. "http!UlpInsertBuffer(conn=%p): inserting %p(#%d) after %p(#%d)\n",
  1759. pConnection,
  1760. pRequestBuffer,
  1761. pRequestBuffer->BufferNumber,
  1762. pListBuffer,
  1763. (pEntry == &(pConnection->BufferHead)) ?
  1764. -1 : pListBuffer->BufferNumber
  1765. )
  1766. );
  1767. //
  1768. // and insert it
  1769. //
  1770. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pConnection));
  1771. InsertHeadList(
  1772. pEntry,
  1773. &(pRequestBuffer->ListEntry)
  1774. );
  1775. WRITE_REF_TRACE_LOG2(
  1776. g_pHttpConnectionTraceLog,
  1777. pConnection->pConnection->pHttpTraceLog,
  1778. REF_ACTION_INSERT_BUFFER,
  1779. pConnection->RefCount,
  1780. pRequestBuffer,
  1781. __FILE__,
  1782. __LINE__
  1783. );
  1784. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pConnection));
  1785. } // UlpInsertBuffer
  1786. /***************************************************************************++
  1787. Routine Description:
  1788. Merges the unparsed bytes on a source buffer to a destination buffer.
  1789. Assumes that there is space in the buffer.
  1790. Arguments:
  1791. pDest - the buffer that gets the bytes
  1792. pSrc - the buffer that gives the bytes
  1793. --***************************************************************************/
  1794. VOID
  1795. UlpMergeBuffers(
  1796. PUL_REQUEST_BUFFER pDest,
  1797. PUL_REQUEST_BUFFER pSrc
  1798. )
  1799. {
  1800. ASSERT( UL_IS_VALID_REQUEST_BUFFER( pDest ) );
  1801. ASSERT( UL_IS_VALID_REQUEST_BUFFER( pSrc ) );
  1802. ASSERT( pDest->AllocBytes - pDest->UsedBytes >= UNPARSED_BUFFER_BYTES( pSrc ) );
  1803. ASSERT( UlpIsValidRequestBufferList( pSrc->pConnection ) );
  1804. UlTrace(HTTP_IO, (
  1805. "http!UlpMergeBuffers(pDest = %p(#%d), pSrc = %p(#%d))\n"
  1806. " Copying %lu bytes from pSrc.\n"
  1807. " pDest->AllocBytes (%lu) - pDest->UsedBytes(%lu) = %lu available\n",
  1808. pDest,
  1809. pDest->BufferNumber,
  1810. pSrc,
  1811. pSrc->BufferNumber,
  1812. UNPARSED_BUFFER_BYTES( pSrc ),
  1813. pDest->AllocBytes,
  1814. pDest->UsedBytes,
  1815. pDest->AllocBytes - pDest->UsedBytes
  1816. ));
  1817. //
  1818. // copy the unparsed bytes
  1819. //
  1820. RtlCopyMemory(
  1821. pDest->pBuffer + pDest->UsedBytes,
  1822. GET_REQUEST_BUFFER_POS( pSrc ),
  1823. UNPARSED_BUFFER_BYTES( pSrc )
  1824. );
  1825. //
  1826. // adjust buffer byte counters to match the transfer
  1827. //
  1828. pDest->UsedBytes += UNPARSED_BUFFER_BYTES( pSrc );
  1829. pSrc->UsedBytes = pSrc->ParsedBytes;
  1830. ASSERT( pDest->UsedBytes != 0 );
  1831. ASSERT( pDest->UsedBytes <= pDest->AllocBytes );
  1832. } // UlpMergeBuffers
  1833. /***************************************************************************++
  1834. Routine Description:
  1835. sets up pCurrentBuffer to the proper location, merging any blocks
  1836. as needed.
  1837. Arguments:
  1838. pConnection - the connection to adjust buffers for
  1839. --***************************************************************************/
  1840. NTSTATUS
  1841. UlpAdjustBuffers(
  1842. PUL_HTTP_CONNECTION pConnection
  1843. )
  1844. {
  1845. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pConnection));
  1846. ASSERT(UlDbgPushLockOwnedExclusive(&pConnection->PushLock));
  1847. //
  1848. // do we have a starting buffer?
  1849. //
  1850. if (pConnection->pCurrentBuffer == NULL)
  1851. {
  1852. //
  1853. // the list can't be empty, this is the FIRST time in
  1854. // pCurrentBuffer is NULL
  1855. //
  1856. ASSERT(IsListEmpty(&(pConnection->BufferHead)) == FALSE);
  1857. ASSERT(pConnection->NextBufferToParse == 0);
  1858. //
  1859. // pop from the head
  1860. //
  1861. pConnection->pCurrentBuffer = CONTAINING_RECORD(
  1862. pConnection->BufferHead.Flink,
  1863. UL_REQUEST_BUFFER,
  1864. ListEntry
  1865. );
  1866. ASSERT( UL_IS_VALID_REQUEST_BUFFER(pConnection->pCurrentBuffer) );
  1867. //
  1868. // is this the right number?
  1869. //
  1870. if (pConnection->pCurrentBuffer->BufferNumber !=
  1871. pConnection->NextBufferToParse)
  1872. {
  1873. pConnection->pCurrentBuffer = NULL;
  1874. return STATUS_MORE_PROCESSING_REQUIRED;
  1875. }
  1876. pConnection->NextBufferToParse += 1;
  1877. pConnection->NeedMoreData = 0;
  1878. }
  1879. //
  1880. // did we need more transport data?
  1881. //
  1882. if (pConnection->NeedMoreData == 1)
  1883. {
  1884. PUL_REQUEST_BUFFER pNextBuffer;
  1885. //
  1886. // is it there?
  1887. //
  1888. if (pConnection->pCurrentBuffer->ListEntry.Flink ==
  1889. &(pConnection->BufferHead))
  1890. {
  1891. //
  1892. // need to wait for more
  1893. //
  1894. UlTrace(HTTP_IO, (
  1895. "http!UlpAdjustBuffers(pHttpConn %p) NeedMoreData == 1\n"
  1896. " No new request buffer available yet\n",
  1897. pConnection
  1898. ));
  1899. return STATUS_MORE_PROCESSING_REQUIRED;
  1900. }
  1901. pNextBuffer = CONTAINING_RECORD(
  1902. pConnection->pCurrentBuffer->ListEntry.Flink,
  1903. UL_REQUEST_BUFFER,
  1904. ListEntry
  1905. );
  1906. ASSERT( UL_IS_VALID_REQUEST_BUFFER(pNextBuffer) );
  1907. //
  1908. // is the next buffer really the 'next' buffer?
  1909. //
  1910. if (pNextBuffer->BufferNumber != pConnection->NextBufferToParse)
  1911. {
  1912. UlTrace(HTTP_IO, (
  1913. "http!UlpAdjustBuffers(pHttpConn %p) NeedMoreData == 1\n"
  1914. " Buffer %d available, but we're waiting for %d\n",
  1915. pConnection,
  1916. pNextBuffer->BufferNumber,
  1917. pConnection->NextBufferToParse
  1918. ));
  1919. return STATUS_MORE_PROCESSING_REQUIRED;
  1920. }
  1921. UlTrace(HTTP_IO, (
  1922. "http!UlpAdjustBuffers(pHttpConn %p) NeedMoreData == 1\n",
  1923. pConnection
  1924. ));
  1925. //
  1926. // is there space to merge the blocks?
  1927. //
  1928. if (pNextBuffer->UsedBytes <
  1929. (pConnection->pCurrentBuffer->AllocBytes -
  1930. pConnection->pCurrentBuffer->UsedBytes))
  1931. {
  1932. //
  1933. // merge 'em .. copy the next buffer into this buffer
  1934. //
  1935. UlpMergeBuffers(
  1936. pConnection->pCurrentBuffer, // dest
  1937. pNextBuffer // src
  1938. );
  1939. //
  1940. // remove the next (now empty) buffer
  1941. //
  1942. ASSERT( pNextBuffer->UsedBytes == 0 );
  1943. UlFreeRequestBuffer(pNextBuffer);
  1944. ASSERT( UlpIsValidRequestBufferList( pConnection ) );
  1945. //
  1946. // skip the buffer sequence number as we deleted that next buffer
  1947. // placing the data in the current buffer. the "new" next buffer
  1948. // will have a 1 higher sequence number.
  1949. //
  1950. pConnection->NextBufferToParse += 1;
  1951. //
  1952. // reset the signal for more data needed
  1953. //
  1954. pConnection->NeedMoreData = 0;
  1955. }
  1956. else
  1957. {
  1958. PUL_REQUEST_BUFFER pNewBuffer;
  1959. //
  1960. // allocate a new buffer with space for the remaining stuff
  1961. // from the old buffer, and everything in the new buffer.
  1962. //
  1963. // this new buffer is replacing pNextBuffer so gets its
  1964. // BufferNumber.
  1965. //
  1966. pNewBuffer = UlCreateRequestBuffer(
  1967. (pConnection->pCurrentBuffer->UsedBytes -
  1968. pConnection->pCurrentBuffer->ParsedBytes) +
  1969. pNextBuffer->UsedBytes,
  1970. pNextBuffer->BufferNumber,
  1971. FALSE
  1972. );
  1973. if (pNewBuffer == NULL)
  1974. {
  1975. return STATUS_INSUFFICIENT_RESOURCES;
  1976. }
  1977. pNewBuffer->pConnection = pConnection;
  1978. UlTrace( PARSER, (
  1979. "*** Request Buffer %p (#%d) has connection %p\n",
  1980. pNewBuffer,
  1981. pNewBuffer->BufferNumber,
  1982. pConnection
  1983. ));
  1984. //
  1985. // copy the unused portion into the start of this buffer
  1986. //
  1987. UlpMergeBuffers(
  1988. pNewBuffer, // dest
  1989. pConnection->pCurrentBuffer // src
  1990. );
  1991. if ( 0 == pConnection->pCurrentBuffer->UsedBytes )
  1992. {
  1993. //
  1994. // Whoops! Accidently ate everything...zap this buffer!
  1995. // This happens when we're ahead of the parser and there
  1996. // are 0 ParsedBytes.
  1997. //
  1998. ASSERT( 0 == pConnection->pCurrentBuffer->ParsedBytes );
  1999. UlTrace(HTTP_IO, (
  2000. "http!UlpAdjustBuffers: "
  2001. "Zapping pConnection->pCurrentBuffer %p(#%d)\n",
  2002. pConnection->pCurrentBuffer,
  2003. pConnection->pCurrentBuffer->BufferNumber
  2004. ));
  2005. UlFreeRequestBuffer( pConnection->pCurrentBuffer );
  2006. pConnection->pCurrentBuffer = NULL;
  2007. }
  2008. //
  2009. // merge the next block into this one
  2010. //
  2011. UlpMergeBuffers(
  2012. pNewBuffer, // dest
  2013. pNextBuffer // src
  2014. );
  2015. //
  2016. // Dispose of the now empty next buffer
  2017. //
  2018. ASSERT(pNextBuffer->UsedBytes == 0);
  2019. UlFreeRequestBuffer(pNextBuffer);
  2020. pNextBuffer = NULL;
  2021. //
  2022. // link in the new buffer
  2023. //
  2024. ASSERT(pNewBuffer->UsedBytes != 0 );
  2025. UlpInsertBuffer(pConnection, pNewBuffer);
  2026. ASSERT( UlpIsValidRequestBufferList( pConnection ) );
  2027. //
  2028. // this newly created (larger) buffer is still the next
  2029. // buffer to parse
  2030. //
  2031. ASSERT(pNewBuffer->BufferNumber == pConnection->NextBufferToParse);
  2032. //
  2033. // so make it the current buffer now
  2034. //
  2035. pConnection->pCurrentBuffer = pNewBuffer;
  2036. //
  2037. // and advance the sequence checker
  2038. //
  2039. pConnection->NextBufferToParse += 1;
  2040. //
  2041. // now reset the signal for more data needed
  2042. //
  2043. pConnection->NeedMoreData = 0;
  2044. }
  2045. }
  2046. else
  2047. {
  2048. //
  2049. // is this buffer drained?
  2050. //
  2051. if (pConnection->pCurrentBuffer->UsedBytes ==
  2052. pConnection->pCurrentBuffer->ParsedBytes)
  2053. {
  2054. PUL_REQUEST_BUFFER pOldBuffer;
  2055. //
  2056. // are there any more buffers?
  2057. //
  2058. if (pConnection->pCurrentBuffer->ListEntry.Flink ==
  2059. &(pConnection->BufferHead))
  2060. {
  2061. //
  2062. // need to wait for more.
  2063. //
  2064. // we leave this empty buffer around refcount'd
  2065. // in pCurrentBuffer until a new buffer shows up,
  2066. // or the connection is dropped.
  2067. //
  2068. // this is so we don't lose our place
  2069. // and have to search the sorted queue
  2070. //
  2071. UlTrace(HTTP_IO, (
  2072. "http!UlpAdjustBuffers(pHttpConn = %p) NeedMoreData == 0\n"
  2073. " buffer %p(#%d) is drained, more required\n",
  2074. pConnection,
  2075. pConnection->pCurrentBuffer,
  2076. pConnection->pCurrentBuffer->BufferNumber
  2077. ));
  2078. return STATUS_MORE_PROCESSING_REQUIRED;
  2079. }
  2080. // else
  2081. //
  2082. // grab the next buffer
  2083. //
  2084. pOldBuffer = pConnection->pCurrentBuffer;
  2085. pConnection->
  2086. pCurrentBuffer = CONTAINING_RECORD(
  2087. pConnection->
  2088. pCurrentBuffer->ListEntry.Flink,
  2089. UL_REQUEST_BUFFER,
  2090. ListEntry
  2091. );
  2092. ASSERT( UL_IS_VALID_REQUEST_BUFFER(pConnection->pCurrentBuffer) );
  2093. //
  2094. // is it the 'next' buffer?
  2095. //
  2096. if (pConnection->pCurrentBuffer->BufferNumber !=
  2097. pConnection->NextBufferToParse)
  2098. {
  2099. UlTrace(HTTP_IO, (
  2100. "http!UlpAdjustBuffers(pHttpConn = %p) NeedMoreData == 0\n"
  2101. " Buffer %p(#%d) available, but we're waiting for buffer %d\n",
  2102. pConnection,
  2103. pConnection->pCurrentBuffer,
  2104. pConnection->pCurrentBuffer->BufferNumber,
  2105. pConnection->NextBufferToParse
  2106. ));
  2107. pConnection->pCurrentBuffer = pOldBuffer;
  2108. return STATUS_MORE_PROCESSING_REQUIRED;
  2109. }
  2110. //
  2111. // bump up the buffer number
  2112. //
  2113. pConnection->NextBufferToParse += 1;
  2114. pConnection->NeedMoreData = 0;
  2115. }
  2116. }
  2117. return STATUS_SUCCESS;
  2118. } // UlpAdjustBuffers
  2119. /***************************************************************************++
  2120. Routine Description:
  2121. Routine invoked after an incoming TCP/MUX connection has been
  2122. received (but not yet accepted).
  2123. Arguments:
  2124. pListeningContext - Supplies an uninterpreted context value as
  2125. passed to the UlCreateListeningEndpoint() API.
  2126. pConnection - Supplies the connection being established.
  2127. pRemoteAddress - Supplies the remote (client-side) address
  2128. requesting the connection.
  2129. RemoteAddressLength - Supplies the total byte length of the
  2130. pRemoteAddress structure.
  2131. ppConnectionContext - Receives a pointer to an uninterpreted
  2132. context value to be associated with the new connection if
  2133. accepted. If the new connection is not accepted, this
  2134. parameter is ignored.
  2135. Return Value:
  2136. BOOLEAN - TRUE if the connection was accepted, FALSE if not.
  2137. --***************************************************************************/
  2138. BOOLEAN
  2139. UlConnectionRequest(
  2140. IN PVOID pListeningContext,
  2141. IN PUL_CONNECTION pConnection,
  2142. IN PTRANSPORT_ADDRESS pRemoteAddress,
  2143. IN ULONG RemoteAddressLength,
  2144. OUT PVOID *ppConnectionContext
  2145. )
  2146. {
  2147. PUL_HTTP_CONNECTION pHttpConnection;
  2148. NTSTATUS status;
  2149. UNREFERENCED_PARAMETER(pListeningContext);
  2150. UNREFERENCED_PARAMETER(pRemoteAddress);
  2151. UNREFERENCED_PARAMETER(RemoteAddressLength);
  2152. //
  2153. // Sanity check.
  2154. //
  2155. ASSERT( IS_VALID_CONNECTION( pConnection ) );
  2156. UlTrace(HTTP_IO,("UlConnectionRequest: conn %p\n",pConnection));
  2157. //
  2158. // Check the global connection limit. If it's reached then
  2159. // enforce it by refusing the connection request. The TDI will
  2160. // return STATUS_CONNECTION_REFUSED when we return FALSE here
  2161. //
  2162. if (UlAcceptGlobalConnection() == FALSE)
  2163. {
  2164. UL_INC_CONNECTION_STATS( GlobalLimit );
  2165. UlTraceError(LIMITS,
  2166. ("UlConnectionRequest: conn %p refused global limit is reached.\n",
  2167. pConnection
  2168. ));
  2169. return FALSE;
  2170. }
  2171. //
  2172. // Create a new HTTP connection.
  2173. //
  2174. status = UlCreateHttpConnection( &pHttpConnection, pConnection );
  2175. ASSERT( NT_SUCCESS(status) );
  2176. //
  2177. // We the HTTP_CONNECTION pointer as our connection context,
  2178. // ULTDI now owns a reference (from the create).
  2179. //
  2180. *ppConnectionContext = pHttpConnection;
  2181. return TRUE;
  2182. } // UlConnectionRequest
  2183. /***************************************************************************++
  2184. Routine Description:
  2185. Routine invoked after an incoming TCP/MUX connection has been
  2186. fully accepted.
  2187. This routine is also invoked if an incoming connection was not
  2188. accepted *after* PUL_CONNECTION_REQUEST returned TRUE. In other
  2189. words, if PUL_CONNECTION_REQUEST indicated that the connection
  2190. should be accepted but a fatal error occurred later, then
  2191. PUL_CONNECTION_COMPLETE is invoked.
  2192. Arguments:
  2193. pListeningContext - Supplies an uninterpreted context value
  2194. as passed to the UlCreateListeningEndpoint() API.
  2195. pConnectionContext - Supplies the uninterpreted context value
  2196. as returned by PUL_CONNECTION_REQUEST.
  2197. Status - Supplies the completion status. If this value is
  2198. STATUS_SUCCESS, then the connection is now fully accepted.
  2199. Otherwise, the connection has been aborted.
  2200. --***************************************************************************/
  2201. VOID
  2202. UlConnectionComplete(
  2203. IN PVOID pListeningContext,
  2204. IN PVOID pConnectionContext,
  2205. IN NTSTATUS Status
  2206. )
  2207. {
  2208. PUL_CONNECTION pConnection;
  2209. PUL_HTTP_CONNECTION pHttpConnection;
  2210. UNREFERENCED_PARAMETER(pListeningContext);
  2211. //
  2212. // Sanity check.
  2213. //
  2214. pHttpConnection = (PUL_HTTP_CONNECTION)pConnectionContext;
  2215. ASSERT( UL_IS_VALID_HTTP_CONNECTION(pHttpConnection) );
  2216. pConnection = pHttpConnection->pConnection;
  2217. ASSERT( IS_VALID_CONNECTION( pConnection ) );
  2218. UlTrace(HTTP_IO,("UlConnectionComplete: http %p conn %p status %s\n",
  2219. pHttpConnection,
  2220. pConnection,
  2221. HttpStatusToString(Status)
  2222. ));
  2223. //
  2224. // Blow away our HTTP connection if the connect failed.
  2225. //
  2226. if (!NT_SUCCESS(Status))
  2227. {
  2228. UL_DEREFERENCE_HTTP_CONNECTION( pHttpConnection );
  2229. }
  2230. else
  2231. {
  2232. //
  2233. // Init connection timeout info block; implicitly starts
  2234. // ConnectionIdle timer. We can't start the timer in
  2235. // UlCreateHttpConnection because if the timer expires before
  2236. // the connection is fully accepted, the connection won't have
  2237. // an initial idle timer running. This is because abort has no
  2238. // effect on a connection being accepted.
  2239. //
  2240. UlInitializeConnectionTimerInfo( &pHttpConnection->TimeoutInfo );
  2241. }
  2242. } // UlConnectionComplete
  2243. /***************************************************************************++
  2244. Routine Description:
  2245. Routine invoked after an established TCP/MUX connection has been
  2246. disconnected by the remote (client) side.
  2247. This routine flags a UL_HTTP_CONNECTION that has been gracefully
  2248. closed by the client. When the connection is idle, we'll close
  2249. our half of it.
  2250. Arguments:
  2251. pListeningContext - Supplies an uninterpreted context value
  2252. as passed to the UlCreateListeningEndpoint() API.
  2253. pConnectionContext - Supplies the uninterpreted context value
  2254. as returned by PUL_CONNECTION_REQUEST.
  2255. --***************************************************************************/
  2256. VOID
  2257. UlConnectionDisconnect(
  2258. IN PVOID pListeningContext,
  2259. IN PVOID pConnectionContext
  2260. )
  2261. {
  2262. PUL_CONNECTION pConnection;
  2263. PUL_HTTP_CONNECTION pHttpConnection;
  2264. UNREFERENCED_PARAMETER(pListeningContext);
  2265. //
  2266. // Sanity check.
  2267. //
  2268. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  2269. pHttpConnection = (PUL_HTTP_CONNECTION)pConnectionContext;
  2270. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConnection));
  2271. pConnection = pHttpConnection->pConnection;
  2272. ASSERT(IS_VALID_CONNECTION(pConnection));
  2273. UlTrace(HTTP_IO,("UlConnectionDisconnect: http %p conn %p NextBufferNumber %d\n",
  2274. pHttpConnection,
  2275. pConnection,
  2276. pHttpConnection->NextBufferNumber
  2277. ));
  2278. UlAcquireSpinLockAtDpcLevel(
  2279. &pHttpConnection->BufferingInfo.BufferingSpinLock
  2280. );
  2281. if (pHttpConnection->BufferingInfo.ReadIrpPending)
  2282. {
  2283. //
  2284. // Read IRP is pending, defer setting pHttpConnection->LastBufferNumber
  2285. // in read completion.
  2286. //
  2287. pHttpConnection->BufferingInfo.ConnectionDisconnect = TRUE;
  2288. }
  2289. else
  2290. {
  2291. //
  2292. // Do it now.
  2293. //
  2294. UlpDoConnectionDisconnect(pHttpConnection);
  2295. }
  2296. UlReleaseSpinLockFromDpcLevel(
  2297. &pHttpConnection->BufferingInfo.BufferingSpinLock
  2298. );
  2299. } // UlConnectionDisconnect
  2300. /***************************************************************************++
  2301. Routine Description:
  2302. Routine invoked after an established TCP/MUX connection has been
  2303. disconnected by the remote (client) side.
  2304. This routine flags a UL_HTTP_CONNECTION that has been gracefully
  2305. closed by the client. When the connection is idle, we'll close
  2306. our half of it.
  2307. Arguments:
  2308. pConnection - Supplies the UL_HTTP_CONNECTION.
  2309. --***************************************************************************/
  2310. VOID
  2311. UlpDoConnectionDisconnect(
  2312. IN PUL_HTTP_CONNECTION pConnection
  2313. )
  2314. {
  2315. //
  2316. // Sanity check.
  2317. //
  2318. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pConnection));
  2319. ASSERT(UlDbgSpinLockOwned(&pConnection->BufferingInfo.BufferingSpinLock));
  2320. UlTrace(HTTP_IO,("UlpDoConnectionDisconnect: http %p NextBufferNumber %d\n",
  2321. pConnection,
  2322. pConnection->NextBufferNumber
  2323. ));
  2324. if (pConnection->NextBufferNumber > 0)
  2325. {
  2326. //
  2327. // Mark the connection as having been disconnected gracefully
  2328. // by the client. We do this by remembering the current
  2329. // buffer number. This lets the parser figure out when it
  2330. // has received the last buffer.
  2331. //
  2332. pConnection->LastBufferNumber = pConnection->NextBufferNumber;
  2333. //
  2334. // Client disconnected gracefully. If the connection is idle
  2335. // we should clean up now. Otherwise we wait until the
  2336. // connection is idle, and then close our half.
  2337. //
  2338. UL_REFERENCE_HTTP_CONNECTION(pConnection);
  2339. UL_QUEUE_WORK_ITEM(
  2340. &pConnection->DisconnectWorkItem,
  2341. &UlpConnectionDisconnectWorker
  2342. );
  2343. }
  2344. else
  2345. {
  2346. //
  2347. // We have not received any data on this connection
  2348. // before the disconnect. Close the connection now.
  2349. //
  2350. // We have to handle this as a special case, because
  2351. // the parser takes (LastBufferNumber == 0) to
  2352. // mean that we haven't yet received a disconnect.
  2353. //
  2354. UL_REFERENCE_HTTP_CONNECTION(pConnection);
  2355. UL_QUEUE_WORK_ITEM(
  2356. &pConnection->DisconnectWorkItem,
  2357. &UlpCloseConnectionWorker
  2358. );
  2359. }
  2360. } // UlpDoConnectionDisconnect
  2361. /***************************************************************************++
  2362. Routine Description:
  2363. Closes the connection after a graceful client disconnect, if the
  2364. connection is idle or if the client sent only part of a request
  2365. before disconnecting.
  2366. Arguments:
  2367. pWorkItem -- a pointer to a UL_WORK_ITEM DisconnectWorkItem
  2368. --***************************************************************************/
  2369. VOID
  2370. UlpConnectionDisconnectWorker(
  2371. IN PUL_WORK_ITEM pWorkItem
  2372. )
  2373. {
  2374. PUL_HTTP_CONNECTION pConnection;
  2375. PAGED_CODE();
  2376. ASSERT(pWorkItem);
  2377. pConnection = CONTAINING_RECORD(
  2378. pWorkItem,
  2379. UL_HTTP_CONNECTION,
  2380. DisconnectWorkItem
  2381. );
  2382. UlTrace(HTTP_IO, (
  2383. "http!UlpConnectionDisconnectWorker (%p) pConnection (%p)\n",
  2384. pWorkItem,
  2385. pConnection
  2386. ));
  2387. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pConnection));
  2388. ASSERT(pConnection->LastBufferNumber > 0);
  2389. //
  2390. // grab the lock
  2391. //
  2392. UlAcquirePushLockExclusive(&pConnection->PushLock);
  2393. //
  2394. // If the parser has handled all the data, call
  2395. // UlpCloseDisconnectedConnection, which will close the
  2396. // connection if appropriate.
  2397. //
  2398. UlTrace(HTTP_IO, (
  2399. "http!UlpConnectionDisconnectWorker\n"
  2400. " NextBufferNumber %d, NextBufferToParse %d, LastBufferNumber %d\n"
  2401. " pRequest %p, ParseState %d (%s)",
  2402. pConnection->NextBufferNumber,
  2403. pConnection->NextBufferToParse,
  2404. pConnection->LastBufferNumber,
  2405. pConnection->pRequest,
  2406. pConnection->pRequest ? pConnection->pRequest->ParseState : 0,
  2407. pConnection->pRequest
  2408. ? UlParseStateToString(pConnection->pRequest->ParseState)
  2409. : "<None>"
  2410. ));
  2411. if (!pConnection->UlconnDestroyed &&
  2412. pConnection->NextBufferToParse == pConnection->LastBufferNumber)
  2413. {
  2414. UlpCloseDisconnectedConnection(pConnection);
  2415. }
  2416. //
  2417. // done with the lock
  2418. //
  2419. UlReleasePushLockExclusive(&pConnection->PushLock);
  2420. //
  2421. // release the reference added in UlConnectionDisconnect
  2422. //
  2423. UL_DEREFERENCE_HTTP_CONNECTION(pConnection);
  2424. } // UlpConnectionDisconnectWorker
  2425. /***************************************************************************++
  2426. Routine Description:
  2427. Closes the connection after a graceful client disconnect, if the
  2428. connection is idle.
  2429. Arguments:
  2430. pWorkItem -- a pointer to a UL_WORK_ITEM DisconnectWorkItem
  2431. --***************************************************************************/
  2432. VOID
  2433. UlpCloseConnectionWorker(
  2434. IN PUL_WORK_ITEM pWorkItem
  2435. )
  2436. {
  2437. PUL_HTTP_CONNECTION pConnection;
  2438. PAGED_CODE();
  2439. ASSERT(pWorkItem);
  2440. pConnection = CONTAINING_RECORD(
  2441. pWorkItem,
  2442. UL_HTTP_CONNECTION,
  2443. DisconnectWorkItem
  2444. );
  2445. UlTrace(HTTP_IO, (
  2446. "http!UlpCloseConnectionWorker (%p) pConnection (%p)\n",
  2447. pWorkItem,
  2448. pConnection
  2449. ));
  2450. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pConnection));
  2451. ASSERT(0 == pConnection->LastBufferNumber);
  2452. UlCloseConnection(
  2453. pConnection->pConnection,
  2454. TRUE, // AbortiveDisconnect
  2455. NULL, // pCompletionRoutine
  2456. NULL // pCompletionContext
  2457. );
  2458. //
  2459. // release the reference added in UlConnectionDisconnect
  2460. //
  2461. UL_DEREFERENCE_HTTP_CONNECTION(pConnection);
  2462. } // UlpCloseConnectionWorker
  2463. /***************************************************************************++
  2464. Routine Description:
  2465. Closes the connection after a graceful client disconnect, if the
  2466. connection is idle or if the client sent only part of a request
  2467. before disconnecting.
  2468. Arguments:
  2469. pConnection - the connection to be disconnected
  2470. --***************************************************************************/
  2471. VOID
  2472. UlpCloseDisconnectedConnection(
  2473. IN PUL_HTTP_CONNECTION pConnection
  2474. )
  2475. {
  2476. //
  2477. // Sanity check.
  2478. //
  2479. PAGED_CODE();
  2480. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pConnection));
  2481. ASSERT(UlDbgPushLockOwnedExclusive(&pConnection->PushLock));
  2482. ASSERT(pConnection->NextBufferNumber == pConnection->LastBufferNumber);
  2483. //
  2484. // The parser has parsed all the available data.
  2485. //
  2486. if (pConnection->pRequest == NULL || pConnection->pRequest->InDrain)
  2487. {
  2488. //
  2489. // We can't be ParseDone if we are in drain because if so pRequest
  2490. // should have been cleaned up already.
  2491. //
  2492. ASSERT(pConnection->pRequest == NULL ||
  2493. pConnection->pRequest->ParseState != ParseDoneState);
  2494. //
  2495. // We're completely idle. Close the connection.
  2496. //
  2497. UlTrace(HTTP_IO, (
  2498. "http!UlpCloseDisconnectedConnection closing idle conn %p\n",
  2499. pConnection
  2500. ));
  2501. UlDisconnectHttpConnection(
  2502. pConnection,
  2503. NULL, // pCompletionRoutine
  2504. NULL // pCompletionContext
  2505. );
  2506. }
  2507. else if (pConnection->pRequest->ParseState != ParseDoneState)
  2508. {
  2509. //
  2510. // The connection was closed before a full request
  2511. // was sent out so send a 400 error.
  2512. //
  2513. // UlSendErrorResponse will close the connection.
  2514. //
  2515. UlTraceError(HTTP_IO, (
  2516. "http!UlpCloseDisconnectedConnection sending 400 on %p\n",
  2517. pConnection
  2518. ));
  2519. UlSetErrorCode( pConnection->pRequest, UlError, NULL);
  2520. UlSendErrorResponse(pConnection);
  2521. }
  2522. else
  2523. {
  2524. //
  2525. // Connection isn't ready to close yet.
  2526. //
  2527. UlTrace(HTTP_IO, (
  2528. "http!UlpCloseDisconnectedConnection NOT ready to close conn %p\n",
  2529. pConnection
  2530. ));
  2531. }
  2532. } // UlpCloseDisconnectedConnection
  2533. /***************************************************************************++
  2534. Routine Description:
  2535. Routine invoked after an established TCP/MUX connection has been
  2536. disconnected by us (server side) we make a final check here to see
  2537. if we have to drain the connection or not.
  2538. Arguments:
  2539. pListeningContext - Supplies an uninterpreted context value
  2540. as passed to the UlCreateListeningEndpoint() API.
  2541. pConnectionContext - Supplies the uninterpreted context value
  2542. as returned by PUL_CONNECTION_REQUEST.
  2543. --***************************************************************************/
  2544. VOID
  2545. UlConnectionDisconnectComplete(
  2546. IN PVOID pListeningContext,
  2547. IN PVOID pConnectionContext
  2548. )
  2549. {
  2550. PUL_HTTP_CONNECTION pConnection;
  2551. KIRQL OldIrql;
  2552. BOOLEAN Drained;
  2553. UNREFERENCED_PARAMETER(pListeningContext);
  2554. //
  2555. // Sanity check.
  2556. //
  2557. pConnection = (PUL_HTTP_CONNECTION)pConnectionContext;
  2558. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pConnection));
  2559. UlTrace( HTTP_IO, ("UlConnectionDisconnectComplete: pConnection %p \n",
  2560. pConnection
  2561. ));
  2562. UlAcquireSpinLock(
  2563. &pConnection->BufferingInfo.BufferingSpinLock,
  2564. &OldIrql
  2565. );
  2566. pConnection->BufferingInfo.DrainAfterDisconnect = TRUE;
  2567. Drained = (BOOLEAN) (pConnection->BufferingInfo.ReadIrpPending
  2568. || (pConnection->BufferingInfo.TransportBytesNotTaken == 0));
  2569. UlReleaseSpinLock(
  2570. &pConnection->BufferingInfo.BufferingSpinLock,
  2571. OldIrql
  2572. );
  2573. // Avoid adding this connection to the workitem queue, if possible
  2574. if (Drained)
  2575. {
  2576. WRITE_REF_TRACE_LOG2(
  2577. g_pTdiTraceLog,
  2578. pConnection->pConnection->pTraceLog,
  2579. REF_ACTION_DRAIN_UL_CONN_DISCONNECT_COMPLETE,
  2580. pConnection->pConnection->ReferenceCount,
  2581. pConnection->pConnection,
  2582. __FILE__,
  2583. __LINE__
  2584. );
  2585. }
  2586. else
  2587. {
  2588. UL_REFERENCE_HTTP_CONNECTION( pConnection );
  2589. UL_QUEUE_WORK_ITEM(
  2590. &pConnection->DisconnectDrainWorkItem,
  2591. &UlpConnectionDisconnectCompleteWorker
  2592. );
  2593. }
  2594. } // UlConnectionDisconnectComplete
  2595. /***************************************************************************++
  2596. Routine Description:
  2597. Worker function to do cleanup work that shouldn't happen above DPC level.
  2598. Arguments:
  2599. pWorkItem -- a pointer to a UL_WORK_ITEM DisconnectDrainWorkItem
  2600. --***************************************************************************/
  2601. VOID
  2602. UlpConnectionDisconnectCompleteWorker(
  2603. IN PUL_WORK_ITEM pWorkItem
  2604. )
  2605. {
  2606. PUL_HTTP_CONNECTION pConnection;
  2607. PAGED_CODE();
  2608. ASSERT(pWorkItem);
  2609. pConnection = CONTAINING_RECORD(
  2610. pWorkItem,
  2611. UL_HTTP_CONNECTION,
  2612. DisconnectDrainWorkItem
  2613. );
  2614. UlTrace(HTTP_IO, (
  2615. "http!UlpConnectionDisconnectCompleteWorker (%p) pConnection (%p)\n",
  2616. pWorkItem,
  2617. pConnection
  2618. ));
  2619. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pConnection));
  2620. //
  2621. // If connection is already get destroyed just bail out !
  2622. //
  2623. WRITE_REF_TRACE_LOG2(
  2624. g_pTdiTraceLog,
  2625. pConnection->pConnection->pTraceLog,
  2626. REF_ACTION_DRAIN_UL_CONN_DISCONNECT_COMPLETE,
  2627. pConnection->pConnection->ReferenceCount,
  2628. pConnection->pConnection,
  2629. __FILE__,
  2630. __LINE__
  2631. );
  2632. //
  2633. // Check to see if we have to drain out or not.
  2634. //
  2635. UlpDiscardBytesFromConnection( pConnection );
  2636. //
  2637. // Deref the http connection added in UlConnectionDisconnectComplete.
  2638. //
  2639. UL_DEREFERENCE_HTTP_CONNECTION( pConnection );
  2640. } // UlpConnectionDisconnectCompleteWorker
  2641. /***************************************************************************++
  2642. Routine Description:
  2643. Routine invoked after an established TCP/MUX connection has been
  2644. destroyed.
  2645. Arguments:
  2646. pListeningContext - Supplies an uninterpreted context value
  2647. as passed to the UlCreateListeningEndpoint() API.
  2648. pConnectionContext - Supplies the uninterpreted context value
  2649. as returned by PUL_CONNECTION_REQUEST.
  2650. --***************************************************************************/
  2651. VOID
  2652. UlConnectionDestroyed(
  2653. IN PVOID pListeningContext,
  2654. IN PVOID pConnectionContext
  2655. )
  2656. {
  2657. PUL_CONNECTION pConnection;
  2658. PUL_HTTP_CONNECTION pHttpConnection;
  2659. UNREFERENCED_PARAMETER(pListeningContext);
  2660. //
  2661. // Sanity check.
  2662. //
  2663. pHttpConnection = (PUL_HTTP_CONNECTION)pConnectionContext;
  2664. pConnection = pHttpConnection->pConnection;
  2665. ASSERT( IS_VALID_CONNECTION( pConnection ) );
  2666. UlTrace(
  2667. HTTP_IO, (
  2668. "http!UlConnectionDestroyed: httpconn %p ulconn %p\n",
  2669. pHttpConnection,
  2670. pConnection
  2671. )
  2672. );
  2673. //
  2674. // Remove the CONNECTION and REQUEST opaque id entries and the ULTDI
  2675. // reference
  2676. //
  2677. UL_QUEUE_WORK_ITEM(
  2678. &pHttpConnection->WorkItem,
  2679. UlConnectionDestroyedWorker
  2680. );
  2681. } // UlConnectionDestroyed
  2682. /***************************************************************************++
  2683. Routine Description:
  2684. handles retrieving entity body from the http request and placing into
  2685. user mode buffers.
  2686. Arguments:
  2687. pRequest - the request to receive from.
  2688. pIrp - the user irp to copy it into. this will be pended, always.
  2689. --***************************************************************************/
  2690. NTSTATUS
  2691. UlReceiveEntityBody(
  2692. IN PUL_APP_POOL_PROCESS pProcess,
  2693. IN PUL_INTERNAL_REQUEST pRequest,
  2694. IN PIRP pIrp
  2695. )
  2696. {
  2697. NTSTATUS Status;
  2698. PIO_STACK_LOCATION pIrpSp;
  2699. UNREFERENCED_PARAMETER(pProcess);
  2700. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  2701. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pRequest->pHttpConn));
  2702. //
  2703. // get the current stack location (a macro)
  2704. //
  2705. pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
  2706. UlTraceVerbose(HTTP_IO, (
  2707. "http!UlReceiveEntityBody: process=%p, req=%p, irp=%p, irpsp=%p\n",
  2708. pProcess, pRequest, pIrp, pIrpSp
  2709. ));
  2710. //
  2711. // is there any recv buffer?
  2712. //
  2713. if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength == 0)
  2714. {
  2715. //
  2716. // nope, shortcircuit this
  2717. //
  2718. Status = STATUS_PENDING;
  2719. pIrp->IoStatus.Information = 0;
  2720. goto end;
  2721. }
  2722. //
  2723. // grab our lock
  2724. //
  2725. UlAcquirePushLockExclusive(&pRequest->pHttpConn->PushLock);
  2726. //
  2727. // Make sure we're not cleaning up the request before queuing an
  2728. // IRP on it.
  2729. //
  2730. if (pRequest->InCleanup)
  2731. {
  2732. Status = STATUS_CONNECTION_DISCONNECTED;
  2733. UlReleasePushLockExclusive(&pRequest->pHttpConn->PushLock);
  2734. UlTraceVerbose(HTTP_IO, (
  2735. "http!UlReceiveEntityBody(req=%p, irp=%p): "
  2736. "Cleaning up request, %s\n",
  2737. pRequest,
  2738. pIrp,
  2739. HttpStatusToString(Status)
  2740. ));
  2741. goto end;
  2742. }
  2743. //
  2744. // is there any data to read? either
  2745. //
  2746. // 1) there were no entity chunks OR
  2747. //
  2748. // 2) there were and :
  2749. //
  2750. // 2b) we've are done parsing all of them AND
  2751. //
  2752. // 2c) we've read all we parsed
  2753. //
  2754. // 3) we have encountered an error when parsing
  2755. // the entity body. Therefore parser was in the
  2756. // error state.
  2757. //
  2758. if ((pRequest->ContentLength == 0 && pRequest->Chunked == 0) ||
  2759. (pRequest->ParseState > ParseEntityBodyState &&
  2760. pRequest->ChunkBytesRead == pRequest->ChunkBytesParsed) ||
  2761. (pRequest->ParseState == ParseErrorState)
  2762. )
  2763. {
  2764. if ( pRequest->ParseState == ParseErrorState )
  2765. {
  2766. //
  2767. // Do not route up the entity body if we have
  2768. // encountered an error when parsing it.
  2769. //
  2770. Status = STATUS_INVALID_DEVICE_REQUEST;
  2771. }
  2772. else
  2773. {
  2774. //
  2775. // nope, complete right away
  2776. //
  2777. Status = STATUS_END_OF_FILE;
  2778. }
  2779. UlReleasePushLockExclusive(&pRequest->pHttpConn->PushLock);
  2780. UlTraceVerbose(HTTP_IO, (
  2781. "http!UlReceiveEntityBody(req=%p, irp=%p): "
  2782. "No data to read, %s\n",
  2783. pRequest,
  2784. pIrp,
  2785. HttpStatusToString(Status)
  2786. ));
  2787. goto end;
  2788. }
  2789. //
  2790. // queue the irp
  2791. //
  2792. IoMarkIrpPending(pIrp);
  2793. //
  2794. // handle 100 continue message reponses
  2795. //
  2796. if ( HTTP_GREATER_EQUAL_VERSION(pRequest->Version, 1, 1) )
  2797. {
  2798. //
  2799. // if this is a HTTP/1.1 PUT or POST request,
  2800. // send "100 Continue" response.
  2801. //
  2802. if ( (HttpVerbPUT == pRequest->Verb) ||
  2803. (HttpVerbPOST == pRequest->Verb) )
  2804. {
  2805. //
  2806. // Only send continue once...
  2807. //
  2808. if ( (0 == pRequest->SentContinue) &&
  2809. (0 == pRequest->SentResponse) &&
  2810. //
  2811. // The following two conditions ensure we have NOT yet
  2812. // received any of the entity body for this request.
  2813. //
  2814. ((pRequest->Chunked && (0 == pRequest->ParsedFirstChunk))
  2815. || (!pRequest->Chunked && (0 == pRequest->ChunkBytesParsed))))
  2816. {
  2817. ULONG BytesSent;
  2818. BytesSent = UlSendSimpleStatus(pRequest, UlStatusContinue);
  2819. pRequest->SentContinue = 1;
  2820. // Update the server to client bytes sent.
  2821. // The logging & perf counters will use it.
  2822. pRequest->BytesSent += BytesSent;
  2823. UlTraceVerbose(HTTP_IO, (
  2824. "http!UlReceiveEntityBody(req=%p, irp=%p): "
  2825. "sent \"100 Continue\", bytes sent = %I64u\n",
  2826. pRequest, pIrp, pRequest->BytesSent
  2827. ));
  2828. }
  2829. }
  2830. }
  2831. //
  2832. // give it a pointer to the request object
  2833. //
  2834. pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = pRequest;
  2835. UL_REFERENCE_INTERNAL_REQUEST(pRequest);
  2836. //
  2837. // set to these to null just in case the cancel routine runs
  2838. //
  2839. pIrp->Tail.Overlay.ListEntry.Flink = NULL;
  2840. pIrp->Tail.Overlay.ListEntry.Blink = NULL;
  2841. IoSetCancelRoutine(pIrp, &UlpCancelEntityBody);
  2842. //
  2843. // cancelled?
  2844. //
  2845. if (pIrp->Cancel)
  2846. {
  2847. //
  2848. // darn it, need to make sure the irp get's completed
  2849. //
  2850. if (IoSetCancelRoutine( pIrp, NULL ) != NULL)
  2851. {
  2852. //
  2853. // we are in charge of completion, IoCancelIrp didn't
  2854. // see our cancel routine (and won't). ioctl wrapper
  2855. // will complete it
  2856. //
  2857. UlReleasePushLockExclusive(&pRequest->pHttpConn->PushLock);
  2858. //
  2859. // let go of the request reference
  2860. //
  2861. UL_DEREFERENCE_INTERNAL_REQUEST(
  2862. (PUL_INTERNAL_REQUEST)(pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer)
  2863. );
  2864. pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
  2865. pIrp->IoStatus.Information = 0;
  2866. UlUnmarkIrpPending( pIrp );
  2867. Status = STATUS_CANCELLED;
  2868. goto end;
  2869. }
  2870. //
  2871. // our cancel routine will run and complete the irp,
  2872. // don't touch it
  2873. //
  2874. //
  2875. // STATUS_PENDING will cause the ioctl wrapper to
  2876. // not complete (or touch in any way) the irp
  2877. //
  2878. Status = STATUS_PENDING;
  2879. UlReleasePushLockExclusive(&pRequest->pHttpConn->PushLock);
  2880. goto end;
  2881. }
  2882. //
  2883. // now we are safe to queue it
  2884. //
  2885. //
  2886. // queue the irp on the request
  2887. //
  2888. InsertHeadList(&(pRequest->IrpHead), &(pIrp->Tail.Overlay.ListEntry));
  2889. //
  2890. // all done
  2891. //
  2892. Status = STATUS_PENDING;
  2893. //
  2894. // Process the buffer queue (which might process the irp we just queued)
  2895. //
  2896. ASSERT( UlpIsValidRequestBufferList( pRequest->pHttpConn ) );
  2897. UlProcessBufferQueue(pRequest, NULL, 0);
  2898. UlReleasePushLockExclusive(&pRequest->pHttpConn->PushLock);
  2899. //
  2900. // all done
  2901. //
  2902. end:
  2903. UlTraceVerbose(HTTP_IO, (
  2904. "http!UlReceiveEntityBody(req=%p, irp=%p): returning %s\n",
  2905. pRequest,
  2906. pIrp,
  2907. HttpStatusToString(Status)
  2908. ));
  2909. RETURN(Status);
  2910. } // UlReceiveEntityBody
  2911. /***************************************************************************++
  2912. Routine Description:
  2913. processes the pending irp queue and buffered body. copying data from the
  2914. buffers into the irps, releasing the buffers and completing the irps
  2915. you must already have the resource locked exclusive on the request prior
  2916. to calling this procedure.
  2917. Arguments:
  2918. pRequest - the request which we should process.
  2919. pEntityBody - optionally provides a buffer to copy entity body
  2920. EntityBody - the length of the optional buffer to copy entity body
  2921. --***************************************************************************/
  2922. VOID
  2923. UlProcessBufferQueue(
  2924. IN PUL_INTERNAL_REQUEST pRequest,
  2925. IN PUCHAR pEntityBody OPTIONAL,
  2926. IN ULONG EntityBodyLength OPTIONAL
  2927. )
  2928. {
  2929. ULONG OutputBufferLength = 0;
  2930. PUCHAR pOutputBuffer = NULL;
  2931. PIRP pIrp;
  2932. PIO_STACK_LOCATION pIrpSp = NULL;
  2933. PLIST_ENTRY pEntry;
  2934. ULONG BytesToCopy;
  2935. ULONG BufferLength;
  2936. ULONG TotalBytesConsumed;
  2937. PUL_REQUEST_BUFFER pNewBuffer;
  2938. BOOLEAN InDrain;
  2939. //
  2940. // Sanity check.
  2941. //
  2942. PAGED_CODE();
  2943. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  2944. ASSERT(UlDbgPushLockOwnedExclusive(&pRequest->pHttpConn->PushLock));
  2945. //
  2946. // now let's pop some buffers off the list
  2947. //
  2948. TotalBytesConsumed = 0;
  2949. pIrp = NULL;
  2950. InDrain = (BOOLEAN) pRequest->InDrain;
  2951. if (InDrain)
  2952. {
  2953. //
  2954. // pseudo buffer has unlimited space in drain mode
  2955. //
  2956. OutputBufferLength = ULONG_MAX;
  2957. }
  2958. else
  2959. if (pEntityBody)
  2960. {
  2961. OutputBufferLength = EntityBodyLength;
  2962. pOutputBuffer = pEntityBody;
  2963. }
  2964. while (TRUE)
  2965. {
  2966. //
  2967. // is there any more entity body to read?
  2968. //
  2969. UlTraceVerbose(HTTP_IO, (
  2970. "http!UlProcessBufferQueue(req=%p): "
  2971. "ParseState=%d (%s), ChunkBytesRead=%I64u, ChunkBytesParsed=%I64u, "
  2972. "pChunkBuffer=%p\n",
  2973. pRequest,
  2974. pRequest->ParseState,
  2975. UlParseStateToString(pRequest->ParseState),
  2976. pRequest->ChunkBytesRead, pRequest->ChunkBytesParsed,
  2977. pRequest->pChunkBuffer
  2978. ));
  2979. if (pRequest->ParseState > ParseEntityBodyState &&
  2980. pRequest->ChunkBytesRead == pRequest->ChunkBytesParsed)
  2981. {
  2982. //
  2983. // nope, let's loop through all of the irp's, completing 'em
  2984. //
  2985. UlTraceVerbose(HTTP_IO, (
  2986. "http!UlProcessBufferQueue(req=%p): no more EntityBody\n",
  2987. pRequest
  2988. ));
  2989. BufferLength = 0;
  2990. }
  2991. //
  2992. // Do we have data ready to be read ?
  2993. //
  2994. // we have not received the first chunk from the parser? OR
  2995. // the parser has not parsed any more data, we've read it all so far
  2996. //
  2997. else if (pRequest->pChunkBuffer == NULL ||
  2998. pRequest->ChunkBytesRead == pRequest->ChunkBytesParsed)
  2999. {
  3000. //
  3001. // Wait for the parser .... UlpParseNextRequest will call
  3002. // this function when it has seen more.
  3003. //
  3004. UlTraceVerbose(HTTP_IO, (
  3005. "http!UlProcessBufferQueue(req=%p): pChunkBuffer=%p, "
  3006. "ChunkBytesRead=%I64u, ChunkBytesParsed=%I64u; breaking.\n",
  3007. pRequest, pRequest->pChunkBuffer,
  3008. pRequest->ChunkBytesRead, pRequest->ChunkBytesParsed
  3009. ));
  3010. break;
  3011. }
  3012. //
  3013. // We are ready to process !
  3014. //
  3015. else
  3016. {
  3017. BufferLength = pRequest->pChunkBuffer->UsedBytes -
  3018. DIFF(pRequest->pChunkLocation -
  3019. pRequest->pChunkBuffer->pBuffer);
  3020. UlTraceVerbose(HTTP_IO, (
  3021. "http!UlProcessBufferQueue(req=%p): BufferLength=0x%x\n",
  3022. pRequest, BufferLength
  3023. ));
  3024. //
  3025. // Do we really have parsed bytes to process ?
  3026. //
  3027. if (0 == BufferLength)
  3028. {
  3029. if (pRequest->pChunkBuffer->ListEntry.Flink !=
  3030. &(pRequest->pHttpConn->BufferHead))
  3031. {
  3032. pNewBuffer = CONTAINING_RECORD(
  3033. pRequest->pChunkBuffer->ListEntry.Flink,
  3034. UL_REQUEST_BUFFER,
  3035. ListEntry
  3036. );
  3037. ASSERT( UL_IS_VALID_REQUEST_BUFFER(pNewBuffer) );
  3038. //
  3039. // There had better be some bytes in this buffer
  3040. //
  3041. ASSERT( 0 != pNewBuffer->UsedBytes );
  3042. }
  3043. else
  3044. {
  3045. pNewBuffer = NULL;
  3046. }
  3047. if (NULL == pNewBuffer || 0 == pNewBuffer->ParsedBytes)
  3048. {
  3049. //
  3050. // Still waiting for the parser, so break the loop.
  3051. // We will get stuck if this check is not done (477936).
  3052. //
  3053. break;
  3054. }
  3055. }
  3056. }
  3057. //
  3058. // do we need a fresh irp?
  3059. //
  3060. if (OutputBufferLength == 0)
  3061. {
  3062. if (pEntityBody || InDrain)
  3063. {
  3064. //
  3065. // break the loop if we drained all data
  3066. //
  3067. break;
  3068. }
  3069. //
  3070. // need to complete the current in-use irp first
  3071. //
  3072. if (pIrp != NULL)
  3073. {
  3074. //
  3075. // let go of the request reference
  3076. //
  3077. UL_DEREFERENCE_INTERNAL_REQUEST(
  3078. (PUL_INTERNAL_REQUEST)pIrpSp->Parameters.
  3079. DeviceIoControl.Type3InputBuffer
  3080. );
  3081. pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
  3082. //
  3083. // complete the used irp
  3084. //
  3085. UlTraceVerbose(HTTP_IO, (
  3086. "http!UlProcessBufferQueue(req=%p): "
  3087. "completing Irp %p, %s\n",
  3088. pRequest,
  3089. pIrp,
  3090. HttpStatusToString(pIrp->IoStatus.Status)
  3091. ));
  3092. //
  3093. // Use IO_NO_INCREMENT to avoid the work thread being
  3094. // rescheduled.
  3095. //
  3096. UlCompleteRequest(pIrp, IO_NO_INCREMENT);
  3097. pIrp = NULL;
  3098. }
  3099. //
  3100. // dequeue an irp from the request
  3101. //
  3102. while (IsListEmpty(&(pRequest->IrpHead)) == FALSE)
  3103. {
  3104. pEntry = RemoveTailList(&(pRequest->IrpHead));
  3105. pEntry->Blink = pEntry->Flink = NULL;
  3106. pIrp = CONTAINING_RECORD(pEntry, IRP, Tail.Overlay.ListEntry);
  3107. pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
  3108. //
  3109. // pop the cancel routine
  3110. //
  3111. if (IoSetCancelRoutine(pIrp, NULL) == NULL)
  3112. {
  3113. //
  3114. // IoCancelIrp pop'd it first
  3115. //
  3116. // ok to just ignore this irp, it's been pop'd off the
  3117. // queue and will be completed in the cancel routine.
  3118. //
  3119. // keep looking for a irp to use
  3120. //
  3121. pIrp = NULL;
  3122. }
  3123. else if (pIrp->Cancel)
  3124. {
  3125. //
  3126. // we pop'd it first. but the irp is being cancelled
  3127. // and our cancel routine will never run. lets be
  3128. // nice and complete the irp now (vs. using it
  3129. // then completing it - which would also be legal).
  3130. //
  3131. //
  3132. // let go of the request reference
  3133. //
  3134. UL_DEREFERENCE_INTERNAL_REQUEST(
  3135. (PUL_INTERNAL_REQUEST)pIrpSp->Parameters.
  3136. DeviceIoControl.Type3InputBuffer
  3137. );
  3138. pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
  3139. //
  3140. // complete the irp
  3141. //
  3142. pIrp->IoStatus.Status = STATUS_CANCELLED;
  3143. pIrp->IoStatus.Information = 0;
  3144. UlTraceVerbose(HTTP_IO, (
  3145. "http!UlProcessBufferQueue(req=%p): "
  3146. "completing cancelled Irp %p, %s\n",
  3147. pRequest,
  3148. pIrp,
  3149. HttpStatusToString(pIrp->IoStatus.Status)
  3150. ));
  3151. UlCompleteRequest(pIrp, IO_NO_INCREMENT);
  3152. pIrp = NULL;
  3153. }
  3154. else
  3155. {
  3156. //
  3157. // we are free to use this irp !
  3158. //
  3159. break;
  3160. }
  3161. } // while (IsListEmpty(&(pRequest->IrpHead)) == FALSE)
  3162. //
  3163. // did we get an irp?
  3164. //
  3165. if (pIrp == NULL)
  3166. {
  3167. //
  3168. // stop looping
  3169. //
  3170. break;
  3171. }
  3172. UlTraceVerbose(HTTP_IO, (
  3173. "http!UlProcessBufferQueue(req=%p): found Irp %p\n",
  3174. pRequest, pIrp
  3175. ));
  3176. //
  3177. // CODEWORK: we could release the request now.
  3178. //
  3179. OutputBufferLength =
  3180. pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
  3181. ASSERT(NULL != pIrp->MdlAddress);
  3182. pOutputBuffer = (PUCHAR) MmGetSystemAddressForMdlSafe(
  3183. pIrp->MdlAddress,
  3184. NormalPagePriority
  3185. );
  3186. if ( pOutputBuffer == NULL )
  3187. {
  3188. pIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
  3189. pIrp->IoStatus.Information = 0;
  3190. break;
  3191. }
  3192. //
  3193. // fill in the IO_STATUS_BLOCK
  3194. //
  3195. pIrp->IoStatus.Status = STATUS_SUCCESS;
  3196. pIrp->IoStatus.Information = 0;
  3197. } // if (OutputBufferLength == 0)
  3198. UlTrace(
  3199. HTTP_IO, (
  3200. "http!UlProcessBufferQueue(req=%p): pChunkBuffer=%p(#%d)\n",
  3201. pRequest,
  3202. pRequest->pChunkBuffer,
  3203. pRequest->pChunkBuffer == NULL ?
  3204. -1 :
  3205. pRequest->pChunkBuffer->BufferNumber
  3206. )
  3207. );
  3208. //
  3209. // how much of it can we copy? min of both buffer sizes
  3210. // and the chunk size
  3211. //
  3212. BytesToCopy = MIN(BufferLength, OutputBufferLength);
  3213. BytesToCopy = (ULONG)(MIN(
  3214. (ULONGLONG)(BytesToCopy),
  3215. pRequest->ChunkBytesToRead
  3216. ));
  3217. if (BytesToCopy > 0)
  3218. {
  3219. ASSERT(pRequest->pChunkBuffer != NULL) ;
  3220. if (!InDrain)
  3221. {
  3222. //
  3223. // copy the buffer
  3224. //
  3225. RtlCopyMemory(
  3226. pOutputBuffer,
  3227. pRequest->pChunkLocation,
  3228. BytesToCopy
  3229. );
  3230. if (pIrp)
  3231. {
  3232. pIrp->IoStatus.Information += BytesToCopy;
  3233. }
  3234. IF_DEBUG2BOTH(HTTP_IO, VERBOSE)
  3235. {
  3236. UlTraceVerbose( HTTP_IO, (
  3237. ">>>> http!UlProcessBufferQueue(req=%p): %lu bytes\n",
  3238. pRequest, BytesToCopy
  3239. ));
  3240. UlDbgPrettyPrintBuffer(pRequest->pChunkLocation, BytesToCopy);
  3241. UlTraceVerbose( HTTP_IO, ("<<<<\n"));
  3242. }
  3243. }
  3244. else
  3245. {
  3246. //
  3247. // Since we're draining, we need to account for the
  3248. // bytes received here, rather than up in UlpParseNextRequest.
  3249. //
  3250. pRequest->BytesReceived += BytesToCopy;
  3251. UlTrace(HTTP_IO, (
  3252. "http!UlProcessBufferQueue(req=%p): "
  3253. "InDrain: draining %lu bytes\n",
  3254. pRequest,
  3255. BytesToCopy
  3256. ));
  3257. }
  3258. pRequest->pChunkLocation += BytesToCopy;
  3259. BufferLength -= BytesToCopy;
  3260. pRequest->ChunkBytesToRead -= BytesToCopy;
  3261. pRequest->ChunkBytesRead += BytesToCopy;
  3262. pOutputBuffer += BytesToCopy;
  3263. OutputBufferLength -= BytesToCopy;
  3264. TotalBytesConsumed += BytesToCopy;
  3265. }
  3266. else
  3267. {
  3268. UlTraceVerbose(HTTP_IO, (
  3269. "http!UlProcessBufferQueue(req=%p): BytesToCopy=0\n",
  3270. pRequest
  3271. ));
  3272. }
  3273. //
  3274. // are we all done with body?
  3275. //
  3276. // when the parser is all done, and we caught up with the parser
  3277. // we are all done.
  3278. //
  3279. UlTraceVerbose(HTTP_IO, (
  3280. "http!UlProcessBufferQueue(req=%p): "
  3281. "ParseState=%d (%s), Chunk: BytesRead=%I64u, BytesParsed=%I64u, "
  3282. "BytesToRead=%I64u, BytesToParse=%I64u, BufferLength=%lu\n",
  3283. pRequest,
  3284. pRequest->ParseState, UlParseStateToString(pRequest->ParseState),
  3285. pRequest->ChunkBytesRead, pRequest->ChunkBytesParsed,
  3286. pRequest->ChunkBytesToRead,
  3287. pRequest->ChunkBytesToParse, BufferLength
  3288. ));
  3289. if (pRequest->ParseState > ParseEntityBodyState &&
  3290. pRequest->ChunkBytesRead == pRequest->ChunkBytesParsed)
  3291. {
  3292. //
  3293. // we are done buffering, mark this irp's return status
  3294. // if we didn't copy any data into it
  3295. //
  3296. if (!InDrain && pIrp && pIrp->IoStatus.Information == 0)
  3297. {
  3298. pIrp->IoStatus.Status = STATUS_END_OF_FILE;
  3299. }
  3300. //
  3301. // force it to complete at the top of the loop
  3302. //
  3303. OutputBufferLength = 0;
  3304. UlTraceVerbose(HTTP_IO, (
  3305. "http!UlProcessBufferQueue(req=%p): "
  3306. "set Irp %p status to EOF\n",
  3307. pRequest, pIrp
  3308. ));
  3309. }
  3310. //
  3311. // need to do buffer management? three cases to worry about:
  3312. //
  3313. // 1) consumed the buffer, but more chunk bytes exist
  3314. //
  3315. // 2) consumed the buffer, and no more chunk bytes exist
  3316. //
  3317. // 3) did not consume the buffer, but no more chunk bytes exist
  3318. //
  3319. else if (BufferLength == 0)
  3320. {
  3321. //
  3322. // consumed the buffer, has the parser already seen another?
  3323. //
  3324. //
  3325. // end of the list?
  3326. //
  3327. if (pRequest->pChunkBuffer->ListEntry.Flink !=
  3328. &(pRequest->pHttpConn->BufferHead))
  3329. {
  3330. pNewBuffer = CONTAINING_RECORD(
  3331. pRequest->pChunkBuffer->ListEntry.Flink,
  3332. UL_REQUEST_BUFFER,
  3333. ListEntry
  3334. );
  3335. ASSERT( UL_IS_VALID_REQUEST_BUFFER(pNewBuffer) );
  3336. //
  3337. // There had better be some bytes in this buffer
  3338. //
  3339. ASSERT( 0 != pNewBuffer->UsedBytes );
  3340. }
  3341. else
  3342. {
  3343. pNewBuffer = NULL;
  3344. }
  3345. UlTraceVerbose(HTTP_IO, (
  3346. "http!UlProcessBufferQueue(req=%p): "
  3347. "pNewBuffer = %p, %lu parsed bytes\n",
  3348. pRequest, pNewBuffer, (pNewBuffer ? pNewBuffer->ParsedBytes : 0)
  3349. ));
  3350. //
  3351. // the flink buffer is a "next buffer" (the list is circular)
  3352. // AND that buffer has been consumed by the parser,
  3353. //
  3354. // then we too can move on to it and start consuming.
  3355. //
  3356. if (pNewBuffer != NULL && pNewBuffer->ParsedBytes > 0)
  3357. {
  3358. PUL_REQUEST_BUFFER pOldBuffer;
  3359. //
  3360. // remember the old buffer
  3361. //
  3362. pOldBuffer = pRequest->pChunkBuffer;
  3363. ASSERT(pNewBuffer->BufferNumber > pOldBuffer->BufferNumber);
  3364. //
  3365. // use it the new one
  3366. //
  3367. pRequest->pChunkBuffer = pNewBuffer;
  3368. ASSERT( UL_IS_VALID_REQUEST_BUFFER(pRequest->pChunkBuffer) );
  3369. //
  3370. // update our current location in the buffer and record
  3371. // its length
  3372. //
  3373. pRequest->pChunkLocation = pRequest->pChunkBuffer->pBuffer;
  3374. BufferLength = pRequest->pChunkBuffer->UsedBytes;
  3375. //
  3376. // did the chunk end on that buffer boundary and there are
  3377. // more chunks ?
  3378. //
  3379. if (pRequest->ChunkBytesToRead == 0)
  3380. {
  3381. NTSTATUS Status;
  3382. ULONG BytesTaken = 0L;
  3383. //
  3384. // we know there are more chunk buffers,
  3385. // thus we must be chunk encoded
  3386. //
  3387. ASSERT(pRequest->Chunked == 1);
  3388. //
  3389. // the chunk length is not allowed to span buffers,
  3390. // let's parse it
  3391. //
  3392. Status = ParseChunkLength(
  3393. pRequest->ParsedFirstChunk,
  3394. pRequest->pChunkLocation,
  3395. BufferLength,
  3396. &BytesTaken,
  3397. &(pRequest->ChunkBytesToRead)
  3398. );
  3399. UlTraceVerbose(HTTP_IO, (
  3400. "http!UlProcessBufferQueue(pReq=%p): %s. "
  3401. "Chunk length (a): %lu bytes taken, "
  3402. "%I64u bytes to read.\n",
  3403. pRequest,
  3404. HttpStatusToString(Status),
  3405. BytesTaken,
  3406. pRequest->ChunkBytesToRead
  3407. ));
  3408. //
  3409. // this can't fail, the only failure case from
  3410. // ParseChunkLength spanning buffers, which the parser
  3411. // would have fixed in HandleRequest
  3412. //
  3413. ASSERT(NT_SUCCESS(Status) && BytesTaken > 0);
  3414. ASSERT(pRequest->ChunkBytesToRead > 0);
  3415. ASSERT(BytesTaken <= BufferLength);
  3416. pRequest->pChunkLocation += BytesTaken;
  3417. BufferLength -= BytesTaken;
  3418. //
  3419. // Keep track of the chunk encoding overhead. If we don't,
  3420. // then we'll slowly "leak" a few bytes in the BufferingInfo
  3421. // for every chunk processed.
  3422. //
  3423. TotalBytesConsumed += BytesTaken;
  3424. } // if (pRequest->ChunkBytesToRead == 0)
  3425. UlTrace(HTTP_IO, (
  3426. "http!UlProcessBufferQueue(pRequest = %p)\n"
  3427. " finished with pOldBuffer = %p(#%d)\n"
  3428. " moved on to pChunkBuffer = %p(#%d)\n"
  3429. " pConn(%p)->pCurrentBuffer = %p(#%d)\n"
  3430. " pRequest->pLastHeaderBuffer = %p(#%d)\n",
  3431. pRequest,
  3432. pOldBuffer,
  3433. pOldBuffer->BufferNumber,
  3434. pRequest->pChunkBuffer,
  3435. pRequest->pChunkBuffer ? pRequest->pChunkBuffer->BufferNumber : -1,
  3436. pRequest->pHttpConn,
  3437. pRequest->pHttpConn->pCurrentBuffer,
  3438. pRequest->pHttpConn->pCurrentBuffer->BufferNumber,
  3439. pRequest->pLastHeaderBuffer,
  3440. pRequest->pLastHeaderBuffer->BufferNumber
  3441. ));
  3442. //
  3443. // let the old buffer go if it doesn't contain any header
  3444. // data. We're done with it.
  3445. //
  3446. if (pOldBuffer != pRequest->pLastHeaderBuffer)
  3447. {
  3448. //
  3449. // the connection should be all done using this, the only
  3450. // way we get here is if the parser has seen this buffer
  3451. // thus pCurrentBuffer points at least to pNewBuffer.
  3452. //
  3453. ASSERT(pRequest->pHttpConn->pCurrentBuffer != pOldBuffer);
  3454. UlFreeRequestBuffer(pOldBuffer);
  3455. pOldBuffer = NULL;
  3456. }
  3457. } // if (pNewBuffer != NULL && pNewBuffer->ParsedBytes > 0)
  3458. } // else if (BufferLength == 0)
  3459. //
  3460. // ok, there's more bytes in the buffer, but how about the chunk?
  3461. //
  3462. //
  3463. // Have we taken all of the current chunk?
  3464. //
  3465. else if (pRequest->ChunkBytesToRead == 0)
  3466. {
  3467. //
  3468. // Are we are still behind the parser?
  3469. //
  3470. if (pRequest->ChunkBytesRead < pRequest->ChunkBytesParsed)
  3471. {
  3472. NTSTATUS Status;
  3473. ULONG BytesTaken;
  3474. ASSERT(pRequest->Chunked == 1);
  3475. //
  3476. // the chunk length is not allowed to span buffers,
  3477. // let's parse it
  3478. //
  3479. Status = ParseChunkLength(
  3480. pRequest->ParsedFirstChunk,
  3481. pRequest->pChunkLocation,
  3482. BufferLength,
  3483. &BytesTaken,
  3484. &(pRequest->ChunkBytesToRead)
  3485. );
  3486. UlTraceVerbose(HTTP_IO, (
  3487. "http!UlProcessBufferQueue(pRequest=%p): %s. "
  3488. "chunk length (b): %lu bytes taken, "
  3489. "%I64u bytes to read.\n",
  3490. pRequest,
  3491. HttpStatusToString(Status),
  3492. BytesTaken,
  3493. pRequest->ChunkBytesToRead
  3494. ));
  3495. //
  3496. // this can't fail, the only failure case from
  3497. // ParseChunkLength spanning buffers, which the parser
  3498. // would have fixed in HandleRequest
  3499. //
  3500. ASSERT(NT_SUCCESS(Status) && BytesTaken > 0);
  3501. ASSERT(pRequest->ChunkBytesToRead > 0);
  3502. ASSERT(BytesTaken <= BufferLength);
  3503. pRequest->pChunkLocation += BytesTaken;
  3504. BufferLength -= BytesTaken;
  3505. //
  3506. // Keep track of the chunk encoding overhead. If we don't,
  3507. // then we'll slowly "leak" a few bytes in the BufferingInfo
  3508. // for every chunk processed.
  3509. //
  3510. TotalBytesConsumed += BytesTaken;
  3511. }
  3512. else
  3513. {
  3514. //
  3515. // Need to wait for the parser to parse more
  3516. //
  3517. UlTraceVerbose(HTTP_IO, (
  3518. "http!UlProcessBufferQueue(pRequest = %p): "
  3519. "need to parse more\n",
  3520. pRequest
  3521. ));
  3522. break;
  3523. }
  3524. } // else if (pRequest->ChunkBytesToRead == 0)
  3525. //
  3526. // next irp or buffer
  3527. //
  3528. } // while (TRUE)
  3529. //
  3530. // complete the irp we put partial data in
  3531. //
  3532. if (pIrp != NULL)
  3533. {
  3534. //
  3535. // let go of the request reference
  3536. //
  3537. UL_DEREFERENCE_INTERNAL_REQUEST(
  3538. (PUL_INTERNAL_REQUEST)pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer
  3539. );
  3540. pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
  3541. //
  3542. // complete the used irp
  3543. //
  3544. UlTraceVerbose(HTTP_IO, (
  3545. "http!UlProcessBufferQueue(req=%p): "
  3546. "completing partially used Irp %p, %s\n",
  3547. pRequest,
  3548. pIrp,
  3549. HttpStatusToString(pIrp->IoStatus.Status)
  3550. ));
  3551. //
  3552. // Use IO_NO_INCREMENT to avoid the work thread being rescheduled.
  3553. //
  3554. UlCompleteRequest(pIrp, IO_NO_INCREMENT);
  3555. pIrp = NULL;
  3556. }
  3557. else
  3558. {
  3559. UlTraceVerbose(HTTP_IO, (
  3560. "http!UlProcessBufferQueue(req=%p): no irp with partial data\n",
  3561. pRequest
  3562. ));
  3563. }
  3564. //
  3565. // Tell the connection how many bytes we consumed. This
  3566. // may allow us to restart receive indications.
  3567. //
  3568. UlTraceVerbose(HTTP_IO, (
  3569. "http!UlProcessBufferQueue(req=%p, httpconn=%p): "
  3570. "%lu bytes consumed\n",
  3571. pRequest, pRequest->pHttpConn, TotalBytesConsumed
  3572. ));
  3573. if (TotalBytesConsumed != 0)
  3574. {
  3575. UlpConsumeBytesFromConnection(pRequest->pHttpConn, TotalBytesConsumed);
  3576. }
  3577. //
  3578. // all done
  3579. //
  3580. } // UlProcessBufferQueue
  3581. /***************************************************************************++
  3582. Routine Description:
  3583. This function subtracts from the total number of bytes currently buffered
  3584. on the UL_HTTP_CONNECTION object. If there are bytes from the transport
  3585. that we previously refused, this function may issue a receive to restart
  3586. the flow of data from TCP.
  3587. Arguments:
  3588. pConnection - the connection on which the bytes came in
  3589. BytesCount - the number of bytes consumed
  3590. --***************************************************************************/
  3591. VOID
  3592. UlpConsumeBytesFromConnection(
  3593. IN PUL_HTTP_CONNECTION pConnection,
  3594. IN ULONG ByteCount
  3595. )
  3596. {
  3597. KIRQL OldIrql;
  3598. ULONG SpaceAvailable;
  3599. ULONG BytesToRead;
  3600. BOOLEAN IssueReadIrp;
  3601. //
  3602. // Sanity check.
  3603. //
  3604. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pConnection));
  3605. //
  3606. // Set up locals.
  3607. //
  3608. BytesToRead = 0;
  3609. IssueReadIrp = FALSE;
  3610. //
  3611. // Consume the bytes.
  3612. //
  3613. UlAcquireSpinLock(
  3614. &pConnection->BufferingInfo.BufferingSpinLock,
  3615. &OldIrql
  3616. );
  3617. //
  3618. // Tell the connection how many bytes we consumed.
  3619. // HTTP header bytes are "consumed" as soon as we
  3620. // parse them.
  3621. //
  3622. if (ByteCount)
  3623. {
  3624. UlTrace(HTTP_IO, (
  3625. "UlpConsumeBytesFromConnection(pconn = %p, bytes = %lu)"
  3626. " ZeroBytes consumed !"
  3627. ));
  3628. ASSERT(ByteCount <= pConnection->BufferingInfo.BytesBuffered);
  3629. if (ByteCount > pConnection->BufferingInfo.BytesBuffered)
  3630. {
  3631. //
  3632. // This should never happen, but if it does then make sure
  3633. // we don't subtract more BufferedBytes than we have.
  3634. //
  3635. ByteCount = pConnection->BufferingInfo.BytesBuffered;
  3636. }
  3637. //
  3638. // Compute the new number of buffered bytes.
  3639. //
  3640. pConnection->BufferingInfo.BytesBuffered -= ByteCount;
  3641. }
  3642. //
  3643. // Enforce the 16K Buffer Limit.
  3644. //
  3645. if (g_UlMaxBufferedBytes > pConnection->BufferingInfo.BytesBuffered)
  3646. {
  3647. SpaceAvailable = g_UlMaxBufferedBytes
  3648. - pConnection->BufferingInfo.BytesBuffered;
  3649. }
  3650. else
  3651. {
  3652. SpaceAvailable = 0;
  3653. }
  3654. UlTrace(HTTP_IO, (
  3655. "UlpConsumeBytesFromConnection(pconn = %p, bytes = %lu)\n"
  3656. " SpaceAvailable = %lu, BytesBuffered %lu->%lu, not taken = %lu\n",
  3657. pConnection,
  3658. ByteCount,
  3659. SpaceAvailable,
  3660. pConnection->BufferingInfo.BytesBuffered + ByteCount,
  3661. pConnection->BufferingInfo.BytesBuffered,
  3662. pConnection->BufferingInfo.TransportBytesNotTaken
  3663. ));
  3664. //
  3665. // See if we need to issue a receive to restart the flow of data.
  3666. //
  3667. if ((SpaceAvailable > 0) &&
  3668. (pConnection->BufferingInfo.TransportBytesNotTaken > 0) &&
  3669. (!pConnection->BufferingInfo.ReadIrpPending))
  3670. {
  3671. //
  3672. // Remember that we issued an IRP.
  3673. //
  3674. pConnection->BufferingInfo.ReadIrpPending = TRUE;
  3675. //
  3676. // Issue the Read IRP outside the spinlock.
  3677. //
  3678. IssueReadIrp = TRUE;
  3679. BytesToRead = pConnection->BufferingInfo.TransportBytesNotTaken;
  3680. //
  3681. // Don't read more bytes than we want to buffer.
  3682. //
  3683. BytesToRead = MIN(BytesToRead, SpaceAvailable);
  3684. }
  3685. UlReleaseSpinLock(
  3686. &pConnection->BufferingInfo.BufferingSpinLock,
  3687. OldIrql
  3688. );
  3689. if (IssueReadIrp)
  3690. {
  3691. NTSTATUS Status;
  3692. PUL_REQUEST_BUFFER pRequestBuffer;
  3693. //
  3694. // get a new request buffer, but initialize it
  3695. // with a bogus number. We have to allocate it now,
  3696. // but we want to set the number when the data
  3697. // arrives in the completion routine (like UlHttpReceive
  3698. // does) to avoid synchronization trouble.
  3699. //
  3700. ASSERT(BytesToRead > 0);
  3701. pRequestBuffer = UlCreateRequestBuffer(
  3702. BytesToRead,
  3703. (ULONG)-1, // BufferNumber
  3704. FALSE
  3705. );
  3706. if (pRequestBuffer)
  3707. {
  3708. //
  3709. // Add a backpointer to the connection.
  3710. //
  3711. pRequestBuffer->pConnection = pConnection;
  3712. UlTrace(HTTP_IO, (
  3713. "UlpConsumeBytesFromConnection(pconn=%p). About to read %lu bytes.\n",
  3714. pConnection, BytesToRead
  3715. ));
  3716. //
  3717. // We've got the buffer. Issue the receive.
  3718. // Reference the connection so it doesn't
  3719. // go away while we're waiting. The reference
  3720. // will be removed after the completion.
  3721. //
  3722. UL_REFERENCE_HTTP_CONNECTION( pConnection );
  3723. Status = UlReceiveData(
  3724. pConnection->pConnection,
  3725. pRequestBuffer->pBuffer,
  3726. BytesToRead,
  3727. &UlpRestartHttpReceive,
  3728. pRequestBuffer
  3729. );
  3730. }
  3731. else
  3732. {
  3733. //
  3734. // We're out of memory. Nothing we can do.
  3735. //
  3736. Status = STATUS_NO_MEMORY;
  3737. }
  3738. if (!NT_SUCCESS(Status))
  3739. {
  3740. //
  3741. // Couldn't issue the read. Close the connection.
  3742. //
  3743. UlCloseConnection(
  3744. pConnection->pConnection,
  3745. TRUE, // AbortiveDisconnect
  3746. NULL, // pCompletionRoutine
  3747. NULL // pCompletionContext
  3748. );
  3749. }
  3750. }
  3751. else
  3752. {
  3753. UlTrace(HTTP_IO, (
  3754. "UlpConsumeBytesFromConnection(pconn=%p). Not reading.\n",
  3755. pConnection, BytesToRead
  3756. ));
  3757. }
  3758. } // UlpConsumeBytesFromConnection
  3759. /***************************************************************************++
  3760. Routine Description:
  3761. Once a connection get disconnected gracefully and there's still unreceived
  3762. data on it. We have to drain this extra bytes to expect the tdi disconnect
  3763. indication. We have to drain this data because we need the disconnect indi
  3764. cation to clean up the connection. And we cannot simply abort it. If we do
  3765. not do this we will leak this connection object and finally it will cause
  3766. shutdown failures.
  3767. Arguments:
  3768. pConnection - stuck connection we have to drain out to complete the
  3769. gracefull disconnect.
  3770. --***************************************************************************/
  3771. VOID
  3772. UlpDiscardBytesFromConnection(
  3773. IN PUL_HTTP_CONNECTION pConnection
  3774. )
  3775. {
  3776. NTSTATUS Status;
  3777. PUL_REQUEST_BUFFER pRequestBuffer;
  3778. KIRQL OldIrql;
  3779. ULONG BytesToRead;
  3780. //
  3781. // Sanity check and init
  3782. //
  3783. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pConnection));
  3784. Status = STATUS_SUCCESS;
  3785. BytesToRead = 0;
  3786. pRequestBuffer = NULL;
  3787. //
  3788. // Mark the drain state and restart receive if necessary.
  3789. //
  3790. UlAcquireSpinLock(
  3791. &pConnection->BufferingInfo.BufferingSpinLock,
  3792. &OldIrql
  3793. );
  3794. pConnection->BufferingInfo.DrainAfterDisconnect = TRUE;
  3795. //
  3796. // Even if ReadIrp is pending, it does not matter as we will just discard
  3797. // the indications from now on. We indicate this by marking the above flag
  3798. //
  3799. if ( pConnection->BufferingInfo.ReadIrpPending ||
  3800. pConnection->BufferingInfo.TransportBytesNotTaken == 0
  3801. )
  3802. {
  3803. UlReleaseSpinLock(
  3804. &pConnection->BufferingInfo.BufferingSpinLock,
  3805. OldIrql
  3806. );
  3807. return;
  3808. }
  3809. //
  3810. // As soon as we enter this "DrainAfterDisconnect" state we will not be
  3811. // processing and inserting request buffers anymore. For any new receive
  3812. // indications, we will just mark the whole available data as taken and
  3813. // don't do nothing about it.
  3814. //
  3815. WRITE_REF_TRACE_LOG2(
  3816. g_pTdiTraceLog,
  3817. pConnection->pConnection->pTraceLog,
  3818. REF_ACTION_DRAIN_UL_CONN_START,
  3819. pConnection->pConnection->ReferenceCount,
  3820. pConnection->pConnection,
  3821. __FILE__,
  3822. __LINE__
  3823. );
  3824. //
  3825. // We need to issue a receive to restart the flow of data again. Therefore
  3826. // we can drain.
  3827. //
  3828. pConnection->BufferingInfo.ReadIrpPending = TRUE;
  3829. BytesToRead = pConnection->BufferingInfo.TransportBytesNotTaken;
  3830. UlReleaseSpinLock(
  3831. &pConnection->BufferingInfo.BufferingSpinLock,
  3832. OldIrql
  3833. );
  3834. //
  3835. // Do not try to drain more than g_UlMaxBufferedBytes. If necessary we will
  3836. // issue another receive later.
  3837. //
  3838. BytesToRead = MIN( BytesToRead, g_UlMaxBufferedBytes );
  3839. UlTrace(HTTP_IO,(
  3840. "UlpDiscardBytesFromConnection: pConnection (%p) consuming %lu bytes\n",
  3841. pConnection,
  3842. BytesToRead
  3843. ));
  3844. //
  3845. // Issue the Read IRP outside the spinlock. Issue the receive. Reference
  3846. // the connection so it doesn't go away while we're waiting. The reference
  3847. // will be removed after the completion.
  3848. //
  3849. pRequestBuffer = UlCreateRequestBuffer( BytesToRead, (ULONG)-1, FALSE );
  3850. if (pRequestBuffer)
  3851. {
  3852. //
  3853. // We won't use this buffer but simply discard it when completion happens.
  3854. // Let's still set the pConnection so that completion function doesn't
  3855. // complain
  3856. //
  3857. pRequestBuffer->pConnection = pConnection;
  3858. UL_REFERENCE_HTTP_CONNECTION( pConnection );
  3859. Status = UlReceiveData(pConnection->pConnection,
  3860. pRequestBuffer->pBuffer,
  3861. BytesToRead,
  3862. &UlpRestartHttpReceive,
  3863. pRequestBuffer
  3864. );
  3865. }
  3866. else
  3867. {
  3868. //
  3869. // We're out of memory. Nothing we can do.
  3870. //
  3871. Status = STATUS_NO_MEMORY;
  3872. }
  3873. if ( !NT_SUCCESS(Status) )
  3874. {
  3875. //
  3876. // Couldn't issue the receive. ABORT the connection.
  3877. //
  3878. // CODEWORK: We need a real abort here. If connection is
  3879. // previously gracefully disconnected and a fatal failure
  3880. // happened during drain after disconnect. This abort will
  3881. // be discarded by the Close handler. We have to provide a
  3882. // way to do a forceful abort here.
  3883. //
  3884. UlCloseConnection(
  3885. pConnection->pConnection,
  3886. TRUE, // Abortive
  3887. NULL, // pCompletionRoutine
  3888. NULL // pCompletionContext
  3889. );
  3890. }
  3891. } // UlpDiscardBytesFromConnection
  3892. /***************************************************************************++
  3893. Routine Description:
  3894. Called on a read completion. This happens when we had stopped
  3895. data indications for some reason and then restarted them. This
  3896. function mirrors UlHttpReceive.
  3897. Arguments:
  3898. pContext - pointer to the UL_REQUEST_BUFFER
  3899. Status - Status from UlReceiveData
  3900. Information - bytes transferred
  3901. --***************************************************************************/
  3902. VOID
  3903. UlpRestartHttpReceive(
  3904. IN PVOID pContext,
  3905. IN NTSTATUS Status,
  3906. IN ULONG_PTR Information
  3907. )
  3908. {
  3909. PUL_HTTP_CONNECTION pConnection;
  3910. PUL_REQUEST_BUFFER pRequestBuffer;
  3911. KIRQL OldIrql;
  3912. ULONG TransportBytesNotTaken;
  3913. pRequestBuffer = (PUL_REQUEST_BUFFER)pContext;
  3914. ASSERT(UL_IS_VALID_REQUEST_BUFFER(pRequestBuffer));
  3915. pConnection = pRequestBuffer->pConnection;
  3916. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pConnection));
  3917. ASSERT(!NT_SUCCESS(Status) || 0 != Information);
  3918. if (NT_SUCCESS(Status))
  3919. {
  3920. //
  3921. // update our stats
  3922. //
  3923. UlAcquireSpinLock(
  3924. &pConnection->BufferingInfo.BufferingSpinLock,
  3925. &OldIrql
  3926. );
  3927. ASSERT(Information <= pConnection->BufferingInfo.TransportBytesNotTaken);
  3928. //
  3929. // We've now read the bytes from the transport and
  3930. // buffered them.
  3931. //
  3932. pConnection->BufferingInfo.TransportBytesNotTaken -= (ULONG) Information;
  3933. pConnection->BufferingInfo.BytesBuffered += (ULONG) Information;
  3934. UlTraceVerbose(HTTP_IO,
  3935. ("UlpRestartHttpReceive(conn=%p): "
  3936. "BytesBuffered %lu->%lu, "
  3937. "TransportBytesNotTaken %lu->%lu\n",
  3938. pConnection,
  3939. pConnection->BufferingInfo.BytesBuffered
  3940. - (ULONG) Information,
  3941. pConnection->BufferingInfo.BytesBuffered,
  3942. pConnection->BufferingInfo.TransportBytesNotTaken
  3943. + (ULONG) Information,
  3944. pConnection->BufferingInfo.TransportBytesNotTaken
  3945. ));
  3946. pConnection->BufferingInfo.ReadIrpPending = FALSE;
  3947. if ( pConnection->BufferingInfo.DrainAfterDisconnect )
  3948. {
  3949. //
  3950. // Just free the memory and restart the receive if necessary.
  3951. //
  3952. TransportBytesNotTaken = pConnection->BufferingInfo.TransportBytesNotTaken;
  3953. UlReleaseSpinLock(
  3954. &pConnection->BufferingInfo.BufferingSpinLock,
  3955. OldIrql
  3956. );
  3957. WRITE_REF_TRACE_LOG2(
  3958. g_pTdiTraceLog,
  3959. pConnection->pConnection->pTraceLog,
  3960. REF_ACTION_DRAIN_UL_CONN_RESTART,
  3961. pConnection->pConnection->ReferenceCount,
  3962. pConnection->pConnection,
  3963. __FILE__,
  3964. __LINE__
  3965. );
  3966. if ( TransportBytesNotTaken )
  3967. {
  3968. //
  3969. // Keep draining ...
  3970. //
  3971. UlpDiscardBytesFromConnection( pConnection );
  3972. }
  3973. UlTrace(HTTP_IO,(
  3974. "UlpRestartHttpReceive(d): "
  3975. "pConnection (%p) drained %Iu remaining %lu\n",
  3976. pConnection,
  3977. Information,
  3978. TransportBytesNotTaken
  3979. ));
  3980. //
  3981. // Free the request buffer. And release our reference.
  3982. //
  3983. pRequestBuffer->pConnection = NULL;
  3984. UlFreeRequestBuffer( pRequestBuffer );
  3985. UL_DEREFERENCE_HTTP_CONNECTION( pConnection );
  3986. return;
  3987. }
  3988. //
  3989. // Get the request buffer ready to be inserted.
  3990. //
  3991. pRequestBuffer->UsedBytes = (ULONG) Information;
  3992. ASSERT( 0 != pRequestBuffer->UsedBytes );
  3993. pRequestBuffer->BufferNumber = pConnection->NextBufferNumber;
  3994. pConnection->NextBufferNumber++;
  3995. //
  3996. // Do connection disconnect logic here if we got deferred previously.
  3997. //
  3998. if (pConnection->BufferingInfo.ConnectionDisconnect)
  3999. {
  4000. pConnection->BufferingInfo.ConnectionDisconnect = FALSE;
  4001. UlpDoConnectionDisconnect(pConnection);
  4002. }
  4003. UlReleaseSpinLock(
  4004. &pConnection->BufferingInfo.BufferingSpinLock,
  4005. OldIrql
  4006. );
  4007. UlTrace(HTTP_IO, (
  4008. "UlpRestartHttpReceive(pconn = %p, %s, bytes rec'd=%Iu)\n"
  4009. " BytesBuffered = %lu, not taken = %lu\n",
  4010. pConnection,
  4011. HttpStatusToString(Status),
  4012. Information,
  4013. pConnection->BufferingInfo.BytesBuffered,
  4014. pConnection->BufferingInfo.TransportBytesNotTaken
  4015. ));
  4016. //
  4017. // queue it off
  4018. //
  4019. UlTrace( PARSER, (
  4020. "*** Request Buffer %p(#%d) has connection %p\n",
  4021. pRequestBuffer,
  4022. pRequestBuffer->BufferNumber,
  4023. pConnection
  4024. ));
  4025. if (NULL == InterlockedPushEntrySList(
  4026. &pConnection->ReceiveBufferSList,
  4027. &pRequestBuffer->SListEntry
  4028. ))
  4029. {
  4030. UL_QUEUE_WORK_ITEM(
  4031. &pConnection->ReceiveBufferWorkItem,
  4032. &UlpHandleRequest
  4033. );
  4034. UlTraceVerbose( HTTP_IO, (
  4035. "Request Buffer %p(#%d) + UlpHandleRequest workitem "
  4036. "queued to connection %p\n",
  4037. pRequestBuffer,
  4038. pRequestBuffer->BufferNumber,
  4039. pConnection
  4040. ));
  4041. }
  4042. else
  4043. {
  4044. UL_DEREFERENCE_HTTP_CONNECTION(pConnection);
  4045. }
  4046. }
  4047. else // !NT_SUCCESS(Status)
  4048. {
  4049. UlCloseConnection(
  4050. pConnection->pConnection,
  4051. TRUE,
  4052. NULL,
  4053. NULL
  4054. );
  4055. //
  4056. // Release the reference we added to the connection
  4057. // before issuing the read. Normally this ref would
  4058. // be released in UlpHandleRequest.
  4059. //
  4060. UL_DEREFERENCE_HTTP_CONNECTION(pConnection);
  4061. //
  4062. // free the request buffer.
  4063. //
  4064. UlFreeRequestBuffer(pRequestBuffer);
  4065. }
  4066. } // UlpRestartHttpReceive
  4067. /***************************************************************************++
  4068. Routine Description:
  4069. cancels the pending user mode irp which was to receive entity body. this
  4070. routine ALWAYS results in the irp being completed.
  4071. note: we queue off to cancel in order to process the cancellation at lower
  4072. irql.
  4073. Arguments:
  4074. pDeviceObject - the device object
  4075. pIrp - the irp to cancel
  4076. --***************************************************************************/
  4077. VOID
  4078. UlpCancelEntityBody(
  4079. IN PDEVICE_OBJECT pDeviceObject,
  4080. IN PIRP pIrp
  4081. )
  4082. {
  4083. UNREFERENCED_PARAMETER(pDeviceObject);
  4084. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  4085. ASSERT(pIrp != NULL);
  4086. //
  4087. // release the cancel spinlock. This means the cancel routine
  4088. // must be the one completing the irp (to avoid the race of
  4089. // completion + reuse prior to the cancel routine running).
  4090. //
  4091. IoReleaseCancelSpinLock(pIrp->CancelIrql);
  4092. //
  4093. // queue the cancel to a worker to ensure passive irql.
  4094. //
  4095. UL_CALL_PASSIVE(
  4096. UL_WORK_ITEM_FROM_IRP( pIrp ),
  4097. &UlpCancelEntityBodyWorker
  4098. );
  4099. } // UlpCancelEntityBody
  4100. /***************************************************************************++
  4101. Routine Description:
  4102. Actually performs the cancel for the irp.
  4103. Arguments:
  4104. pWorkItem - the work item to process.
  4105. --***************************************************************************/
  4106. VOID
  4107. UlpCancelEntityBodyWorker(
  4108. IN PUL_WORK_ITEM pWorkItem
  4109. )
  4110. {
  4111. PIRP pIrp;
  4112. PUL_INTERNAL_REQUEST pRequest;
  4113. //
  4114. // Sanity check.
  4115. //
  4116. PAGED_CODE();
  4117. //
  4118. // grab the irp off the work item
  4119. //
  4120. pIrp = UL_WORK_ITEM_TO_IRP( pWorkItem );
  4121. ASSERT(IS_VALID_IRP(pIrp));
  4122. //
  4123. // grab the request off the irp
  4124. //
  4125. pRequest = (PUL_INTERNAL_REQUEST)(
  4126. IoGetCurrentIrpStackLocation(pIrp)->
  4127. Parameters.DeviceIoControl.Type3InputBuffer
  4128. );
  4129. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  4130. //
  4131. // grab the lock protecting the queue'd irp
  4132. //
  4133. UlAcquirePushLockExclusive(&pRequest->pHttpConn->PushLock);
  4134. //
  4135. // does it need to be dequeue'd ?
  4136. //
  4137. if (pIrp->Tail.Overlay.ListEntry.Flink != NULL)
  4138. {
  4139. //
  4140. // remove it
  4141. //
  4142. RemoveEntryList(&(pIrp->Tail.Overlay.ListEntry));
  4143. pIrp->Tail.Overlay.ListEntry.Flink = NULL;
  4144. pIrp->Tail.Overlay.ListEntry.Blink = NULL;
  4145. }
  4146. //
  4147. // let the lock go
  4148. //
  4149. UlReleasePushLockExclusive(&pRequest->pHttpConn->PushLock);
  4150. //
  4151. // let our reference go
  4152. //
  4153. IoGetCurrentIrpStackLocation(pIrp)->
  4154. Parameters.DeviceIoControl.Type3InputBuffer = NULL;
  4155. UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
  4156. //
  4157. // complete the irp
  4158. //
  4159. pIrp->IoStatus.Status = STATUS_CANCELLED;
  4160. pIrp->IoStatus.Information = 0;
  4161. UlTrace(HTTP_IO, (
  4162. "UlpCancelEntityBodyWorker(pIrp=%p): %s.\n",
  4163. pIrp,
  4164. HttpStatusToString(pIrp->IoStatus.Status)
  4165. ));
  4166. UlCompleteRequest( pIrp, IO_NO_INCREMENT );
  4167. } // UlpCancelEntityBodyWorker
  4168. //
  4169. // types and functions for sending error responses
  4170. //
  4171. typedef struct _UL_HTTP_ERROR_ENTRY
  4172. {
  4173. UL_HTTP_ERROR ErrorCode;
  4174. USHORT StatusCode;
  4175. USHORT ReasonLength;
  4176. USHORT BodyLength;
  4177. USHORT LogLength;
  4178. PCSTR ErrorCodeString;
  4179. PCSTR pReasonPhrase;
  4180. PCSTR pBody;
  4181. PCSTR pLog; // Goes to error log file
  4182. } UL_HTTP_ERROR_ENTRY, PUL_HTTP_ERROR_ENTRY;
  4183. #define HTTP_ERROR_ENTRY(ErrorCode, StatusCode, pReasonPhrase, pBody, pLog) \
  4184. { \
  4185. (ErrorCode), \
  4186. (StatusCode), \
  4187. sizeof((pReasonPhrase))-sizeof(CHAR), \
  4188. sizeof((pBody))-sizeof(CHAR), \
  4189. sizeof((pLog))-sizeof(CHAR), \
  4190. #ErrorCode, \
  4191. (pReasonPhrase), \
  4192. (pBody), \
  4193. (pLog) \
  4194. }
  4195. //
  4196. // ErrorTable[] must match the order of the UL_HTTP_ERROR enum
  4197. // in httptypes.h. The Reason Phrases are generally taken from
  4198. // RFC 2616, section 10.4 "Client Error 4xx" and
  4199. // section 10.5 "Server Error 5xx".
  4200. //
  4201. const
  4202. UL_HTTP_ERROR_ENTRY ErrorTable[] =
  4203. {
  4204. HTTP_ERROR_ENTRY(UlErrorNone, 0, "", "", ""), // not a valid error
  4205. //
  4206. // 4xx Client Errors
  4207. //
  4208. HTTP_ERROR_ENTRY(UlError,
  4209. 400,
  4210. "Bad Request",
  4211. "<h1>Bad Request</h1>",
  4212. "BadRequest"
  4213. ),
  4214. HTTP_ERROR_ENTRY(UlErrorVerb,
  4215. 400,
  4216. "Bad Request",
  4217. "<h1>Bad Request (Invalid Verb)</h1>",
  4218. "Verb"
  4219. ),
  4220. HTTP_ERROR_ENTRY(UlErrorUrl,
  4221. 400,
  4222. "Bad Request",
  4223. "<h1>Bad Request (Invalid URL)</h1>",
  4224. "URL"
  4225. ),
  4226. HTTP_ERROR_ENTRY(UlErrorHeader,
  4227. 400,
  4228. "Bad Request",
  4229. "<h1>Bad Request (Invalid Header Name)</h1>",
  4230. "Header"
  4231. ),
  4232. HTTP_ERROR_ENTRY(UlErrorHost,
  4233. 400,
  4234. "Bad Request",
  4235. "<h1>Bad Request (Invalid Hostname)</h1>",
  4236. "Hostname"
  4237. ),
  4238. HTTP_ERROR_ENTRY(UlErrorCRLF,
  4239. 400,
  4240. "Bad Request",
  4241. "<h1>Bad Request (Invalid CR or LF)</h1>",
  4242. "Invalid_CR/LF"
  4243. ),
  4244. HTTP_ERROR_ENTRY(UlErrorNum,
  4245. 400,
  4246. "Bad Request",
  4247. "<h1>Bad Request (Invalid Number)</h1>",
  4248. "Number"
  4249. ),
  4250. HTTP_ERROR_ENTRY(UlErrorFieldLength,
  4251. 400,
  4252. "Bad Request",
  4253. "<h1>Bad Request (Header Field Too Long)</h1>",
  4254. "FieldLength"
  4255. ),
  4256. HTTP_ERROR_ENTRY(UlErrorRequestLength,
  4257. 400,
  4258. "Bad Request",
  4259. "<h1>Bad Request (Request Header Too Long)</h1>",
  4260. "RequestLength"
  4261. ),
  4262. HTTP_ERROR_ENTRY(UlErrorForbiddenUrl,
  4263. 403,
  4264. "Forbidden",
  4265. "<h1>Forbidden (Invalid URL)</h1>",
  4266. "Forbidden"
  4267. ),
  4268. HTTP_ERROR_ENTRY(UlErrorContentLength,
  4269. 411,
  4270. "Length Required",
  4271. "<h1>Length Required</h1>",
  4272. "LengthRequired"
  4273. ),
  4274. HTTP_ERROR_ENTRY(UlErrorPreconditionFailed,
  4275. 412,
  4276. "Precondition Failed",
  4277. "<h1>Precondition Failed</h1>",
  4278. "Precondition"
  4279. ),
  4280. HTTP_ERROR_ENTRY(UlErrorEntityTooLarge,
  4281. 413,
  4282. "Request Entity Too Large",
  4283. "<h1>Request Entity Too Large</h1>",
  4284. "EntityTooLarge"
  4285. ),
  4286. HTTP_ERROR_ENTRY(UlErrorUrlLength,
  4287. 414,
  4288. "Request-URI Too Long",
  4289. "<h1>Url Too Long</h1>",
  4290. "URL_Length"
  4291. ),
  4292. //
  4293. // 5xx Server Errors
  4294. //
  4295. HTTP_ERROR_ENTRY(UlErrorInternalServer,
  4296. 500,
  4297. "Internal Server Error",
  4298. "<h1>Internal Server Error</h1>",
  4299. "Internal"
  4300. ),
  4301. HTTP_ERROR_ENTRY(UlErrorNotImplemented,
  4302. 501,
  4303. "Not Implemented",
  4304. "<h1>Not Implemented</h1>",
  4305. "N/I"
  4306. ),
  4307. HTTP_ERROR_ENTRY(UlErrorUnavailable,
  4308. 503,
  4309. "Service Unavailable",
  4310. "<h1>Service Unavailable</h1>",
  4311. "N/A"
  4312. ),
  4313. //
  4314. // We used to return extended AppPool state in HTTP 503 Error messages.
  4315. // We decided to replace these with a generic 503 error, to reduce
  4316. // information disclosure. However, we'll still keep the state,
  4317. // as we might change this in the future. We still report the detailed
  4318. // reason in the error log file.
  4319. //
  4320. // The comments represent the old error code.
  4321. //
  4322. // "Forbidden - Too Many Users",
  4323. // "<h1>Forbidden - Too Many Users</h1>"
  4324. HTTP_ERROR_ENTRY(UlErrorConnectionLimit,
  4325. 503,
  4326. "Service Unavailable",
  4327. "<h1>Service Unavailable</h1>",
  4328. "ConnLimit"
  4329. ),
  4330. // "Multiple Application Errors - Application Taken Offline",
  4331. // "<h1>Multiple Application Errors - Application Taken Offline</h1>"
  4332. HTTP_ERROR_ENTRY(UlErrorRapidFailProtection,
  4333. 503,
  4334. "Service Unavailable",
  4335. "<h1>Service Unavailable</h1>",
  4336. "AppOffline"
  4337. ),
  4338. // "Application Request Queue Full",
  4339. // "<h1>Application Request Queue Full</h1>"
  4340. HTTP_ERROR_ENTRY(UlErrorAppPoolQueueFull,
  4341. 503,
  4342. "Service Unavailable",
  4343. "<h1>Service Unavailable</h1>",
  4344. "QueueFull"
  4345. ),
  4346. // "Administrator Has Taken Application Offline",
  4347. // "<h1>Administrator Has Taken Application Offline</h1>"
  4348. HTTP_ERROR_ENTRY(UlErrorDisabledByAdmin,
  4349. 503,
  4350. "Service Unavailable",
  4351. "<h1>Service Unavailable</h1>",
  4352. "Disabled"
  4353. ),
  4354. // "Application Automatically Shutdown Due to Administrator Policy",
  4355. // "<h1>Application Automatically Shutdown Due to Administrator Policy</h1>"
  4356. HTTP_ERROR_ENTRY(UlErrorJobObjectFired,
  4357. 503,
  4358. "Service Unavailable",
  4359. "<h1>Service Unavailable</h1>",
  4360. "AppShutdown"
  4361. ),
  4362. // Appool process is too busy to handle the request. The connection is
  4363. // timed out because of TimerAppPool.
  4364. HTTP_ERROR_ENTRY(UlErrorAppPoolBusy,
  4365. 503,
  4366. "Service Unavailable",
  4367. "<h1>Service Unavailable</h1>",
  4368. "AppPoolTimer"
  4369. ),
  4370. HTTP_ERROR_ENTRY(UlErrorVersion,
  4371. 505,
  4372. "HTTP Version Not Supported",
  4373. "<h1>HTTP Version Not Supported</h1>",
  4374. "Version_N/S"
  4375. ),
  4376. }; // ErrorTable[]
  4377. /***************************************************************************++
  4378. Routine Description:
  4379. Set the pRequest->ErrorCode. Makes breakpointing easier to have
  4380. a special function.
  4381. Arguments:
  4382. self explanatory
  4383. --***************************************************************************/
  4384. VOID
  4385. UlSetErrorCodeFileLine(
  4386. IN OUT PUL_INTERNAL_REQUEST pRequest,
  4387. IN UL_HTTP_ERROR ErrorCode,
  4388. IN PUL_APP_POOL_OBJECT pAppPool,
  4389. IN PCSTR pFileName,
  4390. IN USHORT LineNumber
  4391. )
  4392. {
  4393. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  4394. ASSERT(UlErrorNone < ErrorCode && ErrorCode < UlErrorMax);
  4395. pRequest->ParseState = ParseErrorState;
  4396. pRequest->ErrorCode = ErrorCode;
  4397. /* If pAppPool is not null then try to set the LB Capacity as well */
  4398. /* It is required if the error code is 503 related */
  4399. if (pAppPool)
  4400. {
  4401. ASSERT(IS_VALID_AP_OBJECT(pAppPool));
  4402. pRequest->LoadBalancerCapability = pAppPool->LoadBalancerCapability;
  4403. }
  4404. else
  4405. {
  4406. ASSERT(503 != ErrorTable[ErrorCode].StatusCode);
  4407. }
  4408. UlTraceError(PARSER,
  4409. ("http!UlSetErrorCode(Req=%p, Err=%d %s, Status=%hu, \"%s\") "
  4410. "@ %s:%hu\n",
  4411. pRequest, ErrorCode,
  4412. ErrorTable[ErrorCode].ErrorCodeString,
  4413. ErrorTable[ErrorCode].StatusCode,
  4414. ErrorTable[ErrorCode].pBody,
  4415. UlDbgFindFilePart(pFileName),
  4416. LineNumber
  4417. ));
  4418. #if DBG
  4419. if (g_UlBreakOnError)
  4420. DbgBreakPoint();
  4421. #else // !DBG
  4422. UNREFERENCED_PARAMETER(pFileName);
  4423. UNREFERENCED_PARAMETER(LineNumber);
  4424. #endif // !DBG
  4425. } // UlSetErrorCodeFileLine
  4426. /***************************************************************************++
  4427. Routine Description:
  4428. Logs an error record.
  4429. Caller must own the httpconn pushlock.
  4430. The main reason that this function is here because, we use the above
  4431. error table. I din't want to duplicate the table.
  4432. Arguments:
  4433. pHttpConn - Connection and its request.
  4434. pInfo - Extra info (ANSI)
  4435. InfoSize - Size of the info in bytes, excluding the terminating
  4436. null.
  4437. CheckIfDropped - Caller must set this to TRUE, if it is going to abort
  4438. the conenction after calling this function.
  4439. This is to prevent double logging.
  4440. --***************************************************************************/
  4441. VOID
  4442. UlErrorLog(
  4443. IN PUL_HTTP_CONNECTION pHttpConn,
  4444. IN PUL_INTERNAL_REQUEST pRequest,
  4445. IN PCHAR pInfo,
  4446. IN USHORT InfoSize,
  4447. IN BOOLEAN CheckIfDropped
  4448. )
  4449. {
  4450. NTSTATUS LogStatus;
  4451. UL_ERROR_LOG_INFO ErrorLogInfo;
  4452. //
  4453. // Sanity check
  4454. //
  4455. PAGED_CODE();
  4456. ASSERT(UlDbgPushLockOwnedExclusive( &pHttpConn->PushLock ));
  4457. ASSERT(UL_IS_VALID_HTTP_CONNECTION( pHttpConn ));
  4458. if ( CheckIfDropped )
  4459. {
  4460. if ( pHttpConn->Dropped )
  4461. {
  4462. return;
  4463. }
  4464. pHttpConn->Dropped = TRUE;
  4465. }
  4466. UlpInitErrorLogInfo( &ErrorLogInfo,
  4467. pHttpConn,
  4468. pRequest,
  4469. pInfo,
  4470. InfoSize
  4471. );
  4472. LogStatus = UlLogHttpError( &ErrorLogInfo );
  4473. if (!NT_SUCCESS( LogStatus ))
  4474. {
  4475. UlTraceError( ERROR_LOGGING, (
  4476. "UlErrorLog(pHttpConn=%p)"
  4477. " Unable to log an entry to the error log file Failure: %08lx\n",
  4478. pHttpConn,
  4479. LogStatus
  4480. ));
  4481. }
  4482. }
  4483. /***************************************************************************++
  4484. Routine Description:
  4485. Inits a error log info structure.
  4486. Caller must own the httpconn pushlock.
  4487. Arguments:
  4488. pErrorLogInfo - Will be initialized
  4489. pHttpConn - Connection and its request.
  4490. pRequest - optional
  4491. pInfo - Extra info (ANSI)
  4492. InfoSize - Size of the info in bytes, excluding the terminating
  4493. null.
  4494. --***************************************************************************/
  4495. VOID
  4496. UlpInitErrorLogInfo(
  4497. IN OUT PUL_ERROR_LOG_INFO pErrorLogInfo,
  4498. IN PUL_HTTP_CONNECTION pHttpConn,
  4499. IN PUL_INTERNAL_REQUEST pRequest,
  4500. IN PCHAR pInfo,
  4501. IN USHORT InfoSize
  4502. )
  4503. {
  4504. //
  4505. // Sanity check.
  4506. //
  4507. PAGED_CODE();
  4508. ASSERT(pInfo);
  4509. ASSERT(InfoSize);
  4510. ASSERT(pErrorLogInfo);
  4511. ASSERT(UlDbgPushLockOwnedExclusive(&pHttpConn->PushLock));
  4512. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConn));
  4513. RtlZeroMemory(pErrorLogInfo, sizeof(UL_ERROR_LOG_INFO));
  4514. pErrorLogInfo->pHttpConn = pHttpConn;
  4515. //
  4516. // See if pRequest is already provided if not try to grab
  4517. // it from the http connection.
  4518. //
  4519. if (!pRequest)
  4520. {
  4521. pRequest = pHttpConn->pRequest;
  4522. }
  4523. //
  4524. // Request may not be there.
  4525. //
  4526. if (pRequest)
  4527. {
  4528. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  4529. pErrorLogInfo->pRequest = pRequest;
  4530. pErrorLogInfo->ProtocolStatus =
  4531. ErrorTable[pRequest->ErrorCode].StatusCode;
  4532. }
  4533. //
  4534. // Point to the callers buffer.
  4535. //
  4536. pErrorLogInfo->pInfo = pInfo;
  4537. pErrorLogInfo->InfoSize = InfoSize;
  4538. }
  4539. /***************************************************************************++
  4540. Routine Description:
  4541. You should hold the connection Resource before calling this function.
  4542. Arguments:
  4543. self explanatory
  4544. --***************************************************************************/
  4545. VOID
  4546. UlSendErrorResponse(
  4547. PUL_HTTP_CONNECTION pConnection
  4548. )
  4549. {
  4550. UL_HTTP_ERROR ErrorCode;
  4551. NTSTATUS Status = STATUS_SUCCESS;
  4552. PUL_INTERNAL_REQUEST pRequest;
  4553. HTTP_RESPONSE Response;
  4554. HTTP_DATA_CHUNK DataChunk;
  4555. PUL_INTERNAL_RESPONSE pKeResponse = NULL;
  4556. const CHAR ContentType[] = "text/html";
  4557. USHORT ContentTypeLength = STRLEN_LIT("text/html");
  4558. UCHAR contentLength[MAX_ULONG_STR];
  4559. PHTTP_DATA_CHUNK pResponseBody;
  4560. USHORT DataChunkCount;
  4561. ULONG Flags = HTTP_SEND_RESPONSE_FLAG_DISCONNECT;
  4562. //
  4563. // This function should not be marked as PAGEable, because it's
  4564. // useful to set breakpoints on it, and that interacts badly
  4565. // with the driver verifier's IRQL checking
  4566. //
  4567. PAGED_CODE();
  4568. //
  4569. // Sanity check.
  4570. //
  4571. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pConnection));
  4572. ASSERT(UlDbgPushLockOwnedExclusive(&pConnection->PushLock));
  4573. ASSERT(!pConnection->UlconnDestroyed);
  4574. pRequest = pConnection->pRequest;
  4575. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  4576. //
  4577. // To prevent sending back double responses. We will
  4578. // check if user (WP) has already sent one.
  4579. //
  4580. pConnection->WaitingForResponse = 1;
  4581. UlTrace( PARSER, (
  4582. "*** pConnection %p->WaitingForResponse = 1\n",
  4583. pConnection
  4584. ));
  4585. //
  4586. // We will send a response back. won't we ?
  4587. // An error response.
  4588. //
  4589. if (!NT_SUCCESS(UlCheckSendHttpResponseFlags(pRequest, Flags)))
  4590. {
  4591. UlTraceError( PARSER, (
  4592. "*** pConnection %p, pRequest %p, skipping SendError.\n",
  4593. pConnection,
  4594. pRequest
  4595. ));
  4596. goto end;
  4597. }
  4598. //
  4599. // Proceed with constructing and sending the error
  4600. // back to the client
  4601. //
  4602. RtlZeroMemory(&Response, sizeof(Response));
  4603. //
  4604. // Mark as a driver-generated response
  4605. //
  4606. Response.Flags = HTTP_RESPONSE_FLAG_DRIVER;
  4607. if (pRequest->ErrorCode <= UlErrorNone
  4608. || pRequest->ErrorCode >= UlErrorMax)
  4609. {
  4610. ASSERT(! "Invalid Request ErrorCode");
  4611. pRequest->ErrorCode = UlError;
  4612. }
  4613. ErrorCode = pRequest->ErrorCode;
  4614. //
  4615. // ErrorTable[] must be kept in sync with the UL_HTTP_ERROR enum
  4616. //
  4617. ASSERT(ErrorTable[ErrorCode].ErrorCode == ErrorCode);
  4618. Response.StatusCode = ErrorTable[ErrorCode].StatusCode;
  4619. Response.ReasonLength = ErrorTable[ErrorCode].ReasonLength;
  4620. Response.pReason = ErrorTable[ErrorCode].pReasonPhrase;
  4621. UlTraceError( PARSER, (
  4622. "UlSendErrorResponse(pReq=%p), Error=%d %s, %hu, \"%s\"\n",
  4623. pRequest,
  4624. ErrorCode,
  4625. ErrorTable[ErrorCode].ErrorCodeString,
  4626. Response.StatusCode,
  4627. ErrorTable[ErrorCode].pBody
  4628. ));
  4629. //
  4630. // Log an entry to the error log file.
  4631. //
  4632. UlErrorLog( pConnection,
  4633. pRequest,
  4634. (PCHAR) ErrorTable[ErrorCode].pLog,
  4635. ErrorTable[ErrorCode].LogLength,
  4636. FALSE
  4637. );
  4638. //
  4639. // Special-case handling for 503s and load balancers
  4640. //
  4641. if (Response.StatusCode == 503)
  4642. {
  4643. //
  4644. // For dumb L3/L4 load balancers, UlpHandle503Response will return an
  4645. // error, which will cause us to abort the connection
  4646. //
  4647. Status = UlpHandle503Response(pRequest, &Response);
  4648. if (!NT_SUCCESS(Status))
  4649. {
  4650. goto end;
  4651. }
  4652. }
  4653. Response.Headers.KnownHeaders[HttpHeaderContentType].RawValueLength =
  4654. ContentTypeLength;
  4655. Response.Headers.KnownHeaders[HttpHeaderContentType].pRawValue =
  4656. ContentType;
  4657. if (pConnection->pRequest->Verb != HttpVerbHEAD)
  4658. {
  4659. //
  4660. // generate a body
  4661. //
  4662. DataChunk.DataChunkType = HttpDataChunkFromMemory;
  4663. DataChunk.FromMemory.pBuffer = (PVOID) ErrorTable[ErrorCode].pBody;
  4664. DataChunk.FromMemory.BufferLength = ErrorTable[ErrorCode].BodyLength;
  4665. DataChunkCount = 1;
  4666. pResponseBody = &DataChunk;
  4667. }
  4668. else
  4669. {
  4670. PCHAR pEnd;
  4671. USHORT contentLengthStringLength;
  4672. //
  4673. // HEAD requests MUST NOT have a body, so we don't include
  4674. // the data chunk. However we do have to manually generate a content
  4675. // length header, which would have been generated automatically
  4676. // had we specified the entity body.
  4677. //
  4678. pEnd = UlStrPrintUlong(
  4679. (PCHAR) contentLength,
  4680. ErrorTable[ErrorCode].BodyLength,
  4681. '\0');
  4682. contentLengthStringLength = DIFF_USHORT(pEnd - (PCHAR) contentLength);
  4683. Response.Headers.KnownHeaders[HttpHeaderContentLength].RawValueLength =
  4684. contentLengthStringLength;
  4685. Response.Headers.KnownHeaders[HttpHeaderContentLength].pRawValue =
  4686. (PSTR) contentLength;
  4687. //
  4688. // Set the empty entity body.
  4689. //
  4690. DataChunkCount = 0;
  4691. pResponseBody = NULL;
  4692. }
  4693. Status = UlCaptureHttpResponse(
  4694. NULL,
  4695. &Response,
  4696. pRequest,
  4697. DataChunkCount,
  4698. pResponseBody,
  4699. UlCaptureCopyDataInKernelMode,
  4700. Flags,
  4701. FALSE,
  4702. NULL,
  4703. NULL,
  4704. &pKeResponse
  4705. );
  4706. if (NT_SUCCESS(Status) == FALSE)
  4707. goto end;
  4708. Status = UlPrepareHttpResponse(
  4709. pRequest->Version,
  4710. &Response,
  4711. pKeResponse,
  4712. KernelMode
  4713. );
  4714. if (NT_SUCCESS(Status) == FALSE)
  4715. goto end;
  4716. Status = UlSendHttpResponse(
  4717. pRequest,
  4718. pKeResponse,
  4719. &UlpCompleteSendErrorResponse,
  4720. pKeResponse
  4721. );
  4722. if (NT_SUCCESS(Status) == FALSE)
  4723. goto end;
  4724. ASSERT(Status == STATUS_PENDING);
  4725. end:
  4726. if (NT_SUCCESS(Status) == FALSE)
  4727. {
  4728. if (pKeResponse != NULL)
  4729. {
  4730. UL_DEREFERENCE_INTERNAL_RESPONSE(pKeResponse);
  4731. }
  4732. //
  4733. // Abort the connection
  4734. //
  4735. UlTraceError(HTTP_IO, (
  4736. "http!UlSendErrorResponse(%p): "
  4737. "Failed to send error response, %s\n",
  4738. pConnection,
  4739. HttpStatusToString(Status)
  4740. ));
  4741. //
  4742. // cancel any pending io
  4743. //
  4744. UlCancelRequestIo(pRequest);
  4745. //
  4746. // We are about to drop this conenction, set the flag.
  4747. // So that we don't error log twice.
  4748. //
  4749. pConnection->Dropped = TRUE;
  4750. //
  4751. // abort the connection this request is associated with
  4752. //
  4753. UlCloseConnection(
  4754. pConnection->pConnection,
  4755. TRUE,
  4756. NULL,
  4757. NULL
  4758. );
  4759. }
  4760. } // UlSendErrorResponse
  4761. const CHAR g_RetryAfter10Seconds[] = "10";
  4762. const CHAR g_RetryAfter5Minutes[] = "300"; // 5 * 60 == 300 seconds
  4763. /***************************************************************************++
  4764. Routine Description:
  4765. Special-case handling for 503s and load balancers.
  4766. Arguments:
  4767. pConnection - connection that's being 503'd
  4768. Returns:
  4769. Error NTSTATUS - caller should abort the connection
  4770. STATUS_SUCCESS => caller should send the 503 response
  4771. --***************************************************************************/
  4772. NTSTATUS
  4773. UlpHandle503Response(
  4774. IN PUL_INTERNAL_REQUEST pRequest,
  4775. IN PHTTP_RESPONSE pResponse
  4776. )
  4777. {
  4778. HTTP_LOAD_BALANCER_CAPABILITIES LBCapability
  4779. = pRequest->LoadBalancerCapability;
  4780. PCSTR RetryAfterString = NULL;
  4781. USHORT RetryAfterLength = 0;
  4782. UNREFERENCED_PARAMETER(pResponse);
  4783. //
  4784. // If this is an L3/L4 load balancer, we just want to send a TCP RST.
  4785. // We should not send a 503 response. Returning an error code to
  4786. // UlSendErrorResponse will cause it to abort the connection.
  4787. //
  4788. if (HttpLoadBalancerBasicCapability == LBCapability)
  4789. {
  4790. return STATUS_UNSUCCESSFUL;
  4791. }
  4792. //
  4793. // The only other load balancer we know how to deal with currently
  4794. // is an L7
  4795. //
  4796. ASSERT(HttpLoadBalancerSophisticatedCapability == LBCapability);
  4797. switch (pRequest->ErrorCode)
  4798. {
  4799. case UlErrorUnavailable:
  4800. case UlErrorAppPoolQueueFull:
  4801. case UlErrorConnectionLimit:
  4802. case UlErrorAppPoolBusy:
  4803. RetryAfterString = g_RetryAfter10Seconds;
  4804. RetryAfterLength = sizeof(g_RetryAfter10Seconds) - sizeof(CHAR);
  4805. break;
  4806. case UlErrorRapidFailProtection:
  4807. case UlErrorDisabledByAdmin:
  4808. case UlErrorJobObjectFired:
  4809. RetryAfterString = g_RetryAfter5Minutes;
  4810. RetryAfterLength = sizeof(g_RetryAfter5Minutes) - sizeof(CHAR);
  4811. break;
  4812. default:
  4813. ASSERT(! "Invalid UL_HTTP_ERROR");
  4814. break;
  4815. }
  4816. // We don't want to disclose too much information in our error messages,
  4817. // so, we'll not add the HttpHeaderRetryAfter. We might change this in
  4818. // the future, for now, we just don't use the
  4819. // RetryAfterString & RetryAfterLength.
  4820. //
  4821. return STATUS_SUCCESS;
  4822. } // UlpHandle503Response
  4823. /***************************************************************************++
  4824. Routine Description:
  4825. Completion function for UlSendErrorResponse
  4826. Arguments:
  4827. pCompletionContext - a UL_INTERNAL_RESPONSE
  4828. --***************************************************************************/
  4829. VOID
  4830. UlpCompleteSendErrorResponse(
  4831. IN PVOID pCompletionContext,
  4832. IN NTSTATUS Status,
  4833. IN ULONG_PTR Information
  4834. )
  4835. {
  4836. UNREFERENCED_PARAMETER(Status);
  4837. UNREFERENCED_PARAMETER(Information);
  4838. //
  4839. // release the response
  4840. //
  4841. if (pCompletionContext != NULL)
  4842. {
  4843. PUL_INTERNAL_RESPONSE pResponse
  4844. = (PUL_INTERNAL_RESPONSE)(pCompletionContext);
  4845. ASSERT(UL_IS_VALID_INTERNAL_RESPONSE(pResponse));
  4846. UL_DEREFERENCE_INTERNAL_RESPONSE(pResponse);
  4847. }
  4848. } // UlpCompleteSendErrorResponse
  4849. //
  4850. // Types and functions for sending simple status responses
  4851. //
  4852. // REVIEW: Does this status code need to be localized?
  4853. // REVIEW: Do we need to load this as a localized resource?
  4854. //
  4855. typedef struct _UL_SIMPLE_STATUS_ITEM
  4856. {
  4857. UL_WORK_ITEM WorkItem;
  4858. UL_HTTP_SIMPLE_STATUS Status;
  4859. ULONG Length;
  4860. PCHAR pMessage;
  4861. PMDL pMdl;
  4862. PUL_HTTP_CONNECTION pHttpConn;
  4863. BOOLEAN ResumeParsing;
  4864. } UL_SIMPLE_STATUS_ITEM, *PUL_SIMPLE_STATUS_ITEM;
  4865. typedef struct _UL_HTTP_SIMPLE_STATUS_ENTRY
  4866. {
  4867. USHORT StatusCode; // HTTP Status
  4868. ULONG Length; // size (bytes) of response in pResponse, minus trailing NULL
  4869. PSTR pResponse; // header line only with trailing <CRLF><CRLF>
  4870. } UL_HTTP_SIMPLE_STATUS_ENTRY, *PUL_HTTP_SIMPLE_STATUS_ENTRY;
  4871. #define HTTP_SIMPLE_STATUS_ENTRY(StatusCode, pResp) \
  4872. { \
  4873. (StatusCode), \
  4874. sizeof((pResp))-sizeof(CHAR), \
  4875. (pResp) \
  4876. }
  4877. //
  4878. // This must match the order of UL_HTTP_SIMPLE_STATUS in httptypes.h
  4879. //
  4880. UL_HTTP_SIMPLE_STATUS_ENTRY g_SimpleStatusTable[] =
  4881. {
  4882. //
  4883. // UlStatusContinue
  4884. //
  4885. HTTP_SIMPLE_STATUS_ENTRY( 100, "HTTP/1.1 100 Continue\r\n\r\n" ),
  4886. //
  4887. // UlStatusNoContent
  4888. //
  4889. HTTP_SIMPLE_STATUS_ENTRY( 204, "HTTP/1.1 204 No Content\r\n\r\n" ),
  4890. //
  4891. // UlStatusNotModified (must add Date:)
  4892. //
  4893. HTTP_SIMPLE_STATUS_ENTRY( 304, "HTTP/1.1 304 Not Modified\r\nDate:" ),
  4894. };
  4895. /***************************************************************************++
  4896. Routine Description:
  4897. Sends a "Simple" status response: one which does not have a body and is
  4898. terminated by the first empty line after the header field(s).
  4899. See RFC 2616, Section 4.4 for more info.
  4900. Notes:
  4901. According to RFC 2616, Section 8.2.3 [Use of the 100 (Continue)
  4902. Status], "An origin server that sends a 100 (Continue) response
  4903. MUST ultimately send a final status code, once the request body is
  4904. received and processed, unless it terminates the transport
  4905. connection prematurely."
  4906. The connection will not be closed after the response is sent. Caller
  4907. is responsible for cleanup.
  4908. Arguments:
  4909. pRequest a valid pointer to an internal request object
  4910. Response the status code for the simple response to send
  4911. Return
  4912. ULONG the number of bytes sent for this simple response
  4913. if not successfull returns zero
  4914. --***************************************************************************/
  4915. #define ETAG_HDR "Etag:"
  4916. #define ETAG_HDR_LENGTH (sizeof(ETAG_HDR) - sizeof(CHAR))
  4917. ULONG
  4918. UlSendSimpleStatusEx(
  4919. PUL_INTERNAL_REQUEST pRequest,
  4920. UL_HTTP_SIMPLE_STATUS Response,
  4921. PUL_URI_CACHE_ENTRY pUriCacheEntry OPTIONAL,
  4922. BOOLEAN ResumeParsing
  4923. )
  4924. {
  4925. NTSTATUS Status;
  4926. ULONG BytesSent = 0;
  4927. PUL_SIMPLE_STATUS_ITEM pItem = NULL;
  4928. //
  4929. // Sanity check.
  4930. //
  4931. PAGED_CODE();
  4932. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  4933. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pRequest->pHttpConn));
  4934. ASSERT( (Response >= 0) && (Response < UlStatusMaxStatus) );
  4935. switch( Response )
  4936. {
  4937. case UlStatusNotModified:
  4938. {
  4939. ULONG Length;
  4940. PCHAR pTemp;
  4941. CHAR DateBuffer[DATE_HDR_LENGTH + 1];
  4942. LARGE_INTEGER liNow;
  4943. IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry);
  4944. // 304 MUST include a "Date:" header, which is
  4945. // present on the cached item.
  4946. // Add the ETag as well.
  4947. // Calc size of buffer to send
  4948. Length = g_SimpleStatusTable[Response].Length + // Pre-formed message
  4949. 1 + // space
  4950. DATE_HDR_LENGTH + // size of date field
  4951. (2 * CRLF_SIZE) + // size of two <CRLF> sequences
  4952. 1 ; // trailing NULL (for nifty debug printing)
  4953. if (pUriCacheEntry && pUriCacheEntry->ETagLength )
  4954. {
  4955. Length += (pUriCacheEntry->ETagLength - 1) + // Etag (without NULL)
  4956. ETAG_HDR_LENGTH + // Etag: prefix
  4957. 1 + // space
  4958. CRLF_SIZE;
  4959. }
  4960. // Alloc some non-page buffer for the response
  4961. pItem = UL_ALLOCATE_STRUCT_WITH_SPACE(
  4962. NonPagedPool,
  4963. UL_SIMPLE_STATUS_ITEM,
  4964. Length,
  4965. UL_SIMPLE_STATUS_ITEM_POOL_TAG
  4966. );
  4967. if (!pItem)
  4968. {
  4969. Status = STATUS_INSUFFICIENT_RESOURCES;
  4970. goto end;
  4971. }
  4972. UlInitializeWorkItem(&pItem->WorkItem);
  4973. pItem->pHttpConn = pRequest->pHttpConn;
  4974. pItem->Length = (Length - 1); // Don't include the NULL in the outbound message
  4975. pItem->pMessage = (PCHAR) (pItem + 1);
  4976. pItem->Status = Response;
  4977. // Get date buffer
  4978. UlGenerateDateHeader(
  4979. (PUCHAR) DateBuffer,
  4980. &liNow
  4981. );
  4982. // Copy the chunks into the Message buffer
  4983. pTemp = UlStrPrintStr(
  4984. pItem->pMessage,
  4985. g_SimpleStatusTable[Response].pResponse,
  4986. ' '
  4987. );
  4988. pTemp = UlStrPrintStr(
  4989. pTemp,
  4990. DateBuffer,
  4991. '\r'
  4992. );
  4993. // If we have an Etag, copy that in too.
  4994. if (pUriCacheEntry && pUriCacheEntry->ETagLength )
  4995. {
  4996. ASSERT( pUriCacheEntry->pETag );
  4997. *pTemp = '\n';
  4998. pTemp++;
  4999. pTemp = UlStrPrintStr(
  5000. pTemp,
  5001. ETAG_HDR,
  5002. ' '
  5003. );
  5004. pTemp = UlStrPrintStr(
  5005. pTemp,
  5006. (PCHAR)pUriCacheEntry->pETag,
  5007. '\r'
  5008. );
  5009. }
  5010. // Trailing *LF-CRLF
  5011. pTemp = UlStrPrintStr(
  5012. pTemp,
  5013. "\n\r\n",
  5014. '\0'
  5015. );
  5016. // pTemp should point at the trailing NULL;
  5017. // pItem->Length should not include trailing NULL.
  5018. ASSERT( DIFF((pTemp) - pItem->pMessage) == pItem->Length );
  5019. UlTraceVerbose(HTTP_IO, (
  5020. "http!UlSendSimpleStatusEx: %s\n",
  5021. pItem->pMessage
  5022. ));
  5023. // Construct MDL for buffer
  5024. pItem->pMdl = UlAllocateMdl(
  5025. pItem->pMessage,
  5026. pItem->Length,
  5027. FALSE,
  5028. FALSE,
  5029. NULL
  5030. );
  5031. if (!pItem->pMdl)
  5032. {
  5033. Status = STATUS_INSUFFICIENT_RESOURCES;
  5034. goto end;
  5035. }
  5036. MmBuildMdlForNonPagedPool(pItem->pMdl);
  5037. BytesSent = pItem->Length;
  5038. }
  5039. break;
  5040. case UlStatusContinue:
  5041. case UlStatusNoContent:
  5042. {
  5043. //
  5044. // Alloc non-page buffer for the simple send tracker
  5045. // NOTE: no need to alloc extra space for the static message
  5046. //
  5047. pItem = UL_ALLOCATE_STRUCT(
  5048. NonPagedPool,
  5049. UL_SIMPLE_STATUS_ITEM,
  5050. UL_SIMPLE_STATUS_ITEM_POOL_TAG
  5051. );
  5052. if (!pItem)
  5053. {
  5054. Status = STATUS_INSUFFICIENT_RESOURCES;
  5055. goto end;
  5056. }
  5057. UlInitializeWorkItem(&pItem->WorkItem);
  5058. pItem->pHttpConn = pRequest->pHttpConn;
  5059. pItem->pMessage = g_SimpleStatusTable[Response].pResponse;
  5060. pItem->Length = g_SimpleStatusTable[Response].Length;
  5061. pItem->Status = Response;
  5062. UlTraceVerbose(HTTP_IO, (
  5063. "http!UlSendSimpleStatusEx: %s\n",
  5064. pItem->pMessage
  5065. ));
  5066. // Construct MDL for buffer
  5067. pItem->pMdl = UlAllocateMdl(
  5068. pItem->pMessage,
  5069. pItem->Length,
  5070. FALSE,
  5071. FALSE,
  5072. NULL
  5073. );
  5074. if (!pItem->pMdl)
  5075. {
  5076. Status = STATUS_INSUFFICIENT_RESOURCES;
  5077. goto end;
  5078. }
  5079. MmBuildMdlForNonPagedPool(pItem->pMdl);
  5080. BytesSent = g_SimpleStatusTable[Response].Length;
  5081. }
  5082. break;
  5083. default:
  5084. {
  5085. UlTraceError(HTTP_IO, (
  5086. "http!UlSendSimpleStatusEx: Invalid simple status (%d)\n",
  5087. Response
  5088. ));
  5089. ASSERT(!"UlSendSimpleStatusEx: Invalid status!");
  5090. Status = STATUS_SUCCESS; // quietly ignore
  5091. BytesSent = 0;
  5092. goto end;
  5093. }
  5094. }
  5095. //
  5096. // We need to hold a ref to the connection while we send.
  5097. //
  5098. UL_REFERENCE_HTTP_CONNECTION(pRequest->pHttpConn);
  5099. //
  5100. // Turn on the min bytes per sec timer
  5101. //
  5102. UlSetMinBytesPerSecondTimer(
  5103. &pRequest->pHttpConn->TimeoutInfo,
  5104. BytesSent
  5105. );
  5106. //
  5107. // We will only resume parse, if the response type is UlStatusNotModified
  5108. // (cache response). Otherwise this must have been a cache-miss call.
  5109. //
  5110. pItem->ResumeParsing = ResumeParsing;
  5111. ASSERT((ResumeParsing == FALSE
  5112. || Response == UlStatusNotModified));
  5113. Status = UlSendData(
  5114. pRequest->pHttpConn->pConnection,
  5115. pItem->pMdl,
  5116. pItem->Length,
  5117. UlpRestartSendSimpleStatus,
  5118. pItem,
  5119. NULL,
  5120. NULL,
  5121. FALSE,
  5122. (BOOLEAN)(pRequest->ParseState >= ParseDoneState)
  5123. );
  5124. //
  5125. // In an error case the completion routine will always
  5126. // get called.
  5127. //
  5128. return BytesSent;
  5129. end:
  5130. if (NT_SUCCESS(Status) == FALSE)
  5131. {
  5132. //
  5133. // Clean up simple send item since completion routine
  5134. // won't get called
  5135. //
  5136. if (pItem)
  5137. {
  5138. if (pItem->pMdl)
  5139. {
  5140. UlFreeMdl( pItem->pMdl );
  5141. }
  5142. UL_FREE_POOL( pItem, UL_SIMPLE_STATUS_ITEM_POOL_TAG );
  5143. }
  5144. //
  5145. // Abort the connection
  5146. //
  5147. UlTraceError(HTTP_IO, (
  5148. "http!UlSendSimpleStatusEx(%p, %d): aborting request\n",
  5149. pRequest,
  5150. Response
  5151. ));
  5152. //
  5153. // cancel any pending io
  5154. //
  5155. UlCancelRequestIo(pRequest);
  5156. //
  5157. // abort the connection this request is associated with
  5158. //
  5159. UlCloseConnection(
  5160. pRequest->pHttpConn->pConnection,
  5161. TRUE,
  5162. NULL,
  5163. NULL
  5164. );
  5165. return 0;
  5166. }
  5167. else
  5168. {
  5169. return BytesSent;
  5170. }
  5171. } // UlSendSimpleStatusEx
  5172. /***************************************************************************++
  5173. Routine Description:
  5174. Shim for UlSendSimpleStatusEx
  5175. Arguments:
  5176. pRequest - Request associated with simple response to send
  5177. Response - Simple Response type to send
  5178. --***************************************************************************/
  5179. ULONG
  5180. UlSendSimpleStatus(
  5181. PUL_INTERNAL_REQUEST pRequest,
  5182. UL_HTTP_SIMPLE_STATUS Response
  5183. )
  5184. {
  5185. return UlSendSimpleStatusEx( pRequest, Response, NULL, FALSE );
  5186. } // UlSendSimpleStatus
  5187. /***************************************************************************++
  5188. Routine Description:
  5189. Callback for when UlSendData completes sending a UL_SIMPLE_STATUS message
  5190. Arguments:
  5191. pCompletionContext (OPTIONAL) -- If non-NULL, a pointer to a
  5192. UL_SIMPLE_STATUS_ITEM.
  5193. Status -- Ignored.
  5194. Information -- Ignored.
  5195. --***************************************************************************/
  5196. VOID
  5197. UlpRestartSendSimpleStatus(
  5198. IN PVOID pCompletionContext,
  5199. IN NTSTATUS Status,
  5200. IN ULONG_PTR Information
  5201. )
  5202. {
  5203. PUL_SIMPLE_STATUS_ITEM pItem;
  5204. UNREFERENCED_PARAMETER(Status);
  5205. UNREFERENCED_PARAMETER(Information);
  5206. UlTrace(HTTP_IO, (
  5207. "http!UlpRestartSendSimpleStatus: \n"
  5208. " pCompletionContext: %p\n"
  5209. " %s\n"
  5210. " Information: %Iu\n",
  5211. pCompletionContext,
  5212. HttpStatusToString(Status),
  5213. Information
  5214. ));
  5215. if ( pCompletionContext )
  5216. {
  5217. pItem = (PUL_SIMPLE_STATUS_ITEM) pCompletionContext;
  5218. // Queue up work item for passive level
  5219. UL_QUEUE_WORK_ITEM(
  5220. &pItem->WorkItem,
  5221. &UlpSendSimpleCleanupWorker
  5222. );
  5223. }
  5224. } // UlpRestartSendSimpleStatus
  5225. /***************************************************************************++
  5226. Routine Description:
  5227. Worker function to do cleanup work that shouldn't happen above DPC level.
  5228. Arguments:
  5229. pWorkItem -- If non-NULL, a pointer to a UL_WORK_ITEM
  5230. contained within a UL_SIMPLE_STATUS_ITEM.
  5231. --***************************************************************************/
  5232. VOID
  5233. UlpSendSimpleCleanupWorker(
  5234. IN PUL_WORK_ITEM pWorkItem
  5235. )
  5236. {
  5237. KIRQL OldIrql;
  5238. PUL_SIMPLE_STATUS_ITEM pItem;
  5239. PAGED_CODE();
  5240. ASSERT(pWorkItem);
  5241. pItem = CONTAINING_RECORD(
  5242. pWorkItem,
  5243. UL_SIMPLE_STATUS_ITEM,
  5244. WorkItem
  5245. );
  5246. UlTrace(HTTP_IO, (
  5247. "http!UlpSendSimpleCleanupWorker (%p) \n",
  5248. pWorkItem
  5249. ));
  5250. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pItem->pHttpConn));
  5251. //
  5252. // start the Connection Timeout timer
  5253. //
  5254. UlLockTimeoutInfo(
  5255. &(pItem->pHttpConn->TimeoutInfo),
  5256. &OldIrql
  5257. );
  5258. UlResetConnectionTimer(
  5259. &(pItem->pHttpConn->TimeoutInfo),
  5260. TimerMinBytesPerSecond
  5261. );
  5262. if ( UlStatusContinue != pItem->Status )
  5263. {
  5264. UlSetConnectionTimer(
  5265. &(pItem->pHttpConn->TimeoutInfo),
  5266. TimerConnectionIdle
  5267. );
  5268. }
  5269. UlUnlockTimeoutInfo(
  5270. &(pItem->pHttpConn->TimeoutInfo),
  5271. OldIrql
  5272. );
  5273. UlEvaluateTimerState(
  5274. &(pItem->pHttpConn->TimeoutInfo)
  5275. );
  5276. if ( pItem->ResumeParsing )
  5277. {
  5278. //
  5279. // Only possible path which will invoke the simple send path
  5280. // with a requirement of resume parsing on send completion is
  5281. // the 304 (not-modifed) cache sends.
  5282. //
  5283. UlResumeParsing(
  5284. pItem->pHttpConn,
  5285. TRUE,
  5286. FALSE
  5287. );
  5288. }
  5289. //
  5290. // deref http connection
  5291. //
  5292. UL_DEREFERENCE_HTTP_CONNECTION( pItem->pHttpConn );
  5293. //
  5294. // free alloc'd mdl and tracker struct
  5295. //
  5296. ASSERT( pItem->pMdl );
  5297. UlFreeMdl( pItem->pMdl );
  5298. UL_FREE_POOL( pItem, UL_SIMPLE_STATUS_ITEM_POOL_TAG );
  5299. } // UlpSendSimpleCleanupWorker
  5300. #if DBG
  5301. /***************************************************************************++
  5302. Routine Description:
  5303. Invasive assert predicate. DEBUG ONLY!!! Use this only inside an
  5304. ASSERT() macro.
  5305. --***************************************************************************/
  5306. BOOLEAN
  5307. UlpIsValidRequestBufferList(
  5308. PUL_HTTP_CONNECTION pHttpConn
  5309. )
  5310. {
  5311. PLIST_ENTRY pEntry;
  5312. PUL_REQUEST_BUFFER pReqBuf;
  5313. ULONG LastSeqNum = 0;
  5314. BOOLEAN fRet = TRUE;
  5315. if (!g_CheckRequestBufferList)
  5316. return TRUE;
  5317. PAGED_CODE();
  5318. ASSERT( pHttpConn );
  5319. //
  5320. // pop from the head
  5321. //
  5322. pEntry = pHttpConn->BufferHead.Flink;
  5323. while ( pEntry != &(pHttpConn->BufferHead) )
  5324. {
  5325. pReqBuf = CONTAINING_RECORD( pEntry,
  5326. UL_REQUEST_BUFFER,
  5327. ListEntry
  5328. );
  5329. ASSERT( UL_IS_VALID_REQUEST_BUFFER(pReqBuf) );
  5330. ASSERT( pReqBuf->UsedBytes != 0 );
  5331. if ( 0 == pReqBuf->UsedBytes )
  5332. {
  5333. fRet = FALSE;
  5334. }
  5335. //
  5336. // ignore case when BufferNumber is zero (0).
  5337. //
  5338. if ( pReqBuf->BufferNumber && (LastSeqNum >= pReqBuf->BufferNumber) )
  5339. {
  5340. fRet = FALSE;
  5341. }
  5342. LastSeqNum = pReqBuf->BufferNumber;
  5343. pEntry = pEntry->Flink;
  5344. }
  5345. return fRet;
  5346. } // UlpIsValidRequestBufferList
  5347. #endif // DBG
  5348. /***************************************************************************++
  5349. Routine Description:
  5350. Add a request buffer to the end of the array of request buffers in
  5351. a request. Reallocate that array if necessary. Increase the reqbuff's
  5352. reference count, to indicate that a header is holding a reference on it.
  5353. --***************************************************************************/
  5354. BOOLEAN
  5355. UlpReferenceBuffers(
  5356. IN PUL_INTERNAL_REQUEST pRequest,
  5357. IN PUL_REQUEST_BUFFER pRequestBuffer
  5358. )
  5359. {
  5360. PUL_REQUEST_BUFFER * pNewRefBuffers;
  5361. if (pRequest->UsedRefBuffers >= pRequest->AllocRefBuffers)
  5362. {
  5363. ASSERT( pRequest->UsedRefBuffers == pRequest->AllocRefBuffers );
  5364. pNewRefBuffers = UL_ALLOCATE_ARRAY(
  5365. NonPagedPool,
  5366. PUL_REQUEST_BUFFER,
  5367. pRequest->AllocRefBuffers + ALLOC_REQUEST_BUFFER_INCREMENT,
  5368. UL_REF_REQUEST_BUFFER_POOL_TAG
  5369. );
  5370. if (!pNewRefBuffers)
  5371. {
  5372. return FALSE;
  5373. }
  5374. RtlCopyMemory(
  5375. pNewRefBuffers,
  5376. pRequest->pRefBuffers,
  5377. pRequest->UsedRefBuffers * sizeof(PUL_REQUEST_BUFFER)
  5378. );
  5379. if (pRequest->AllocRefBuffers > 1)
  5380. {
  5381. UL_FREE_POOL(
  5382. pRequest->pRefBuffers,
  5383. UL_REF_REQUEST_BUFFER_POOL_TAG
  5384. );
  5385. }
  5386. pRequest->AllocRefBuffers += ALLOC_REQUEST_BUFFER_INCREMENT;
  5387. pRequest->pRefBuffers = pNewRefBuffers;
  5388. }
  5389. pRequest->pRefBuffers[pRequest->UsedRefBuffers] = pRequestBuffer;
  5390. pRequest->UsedRefBuffers++;
  5391. UL_REFERENCE_REQUEST_BUFFER(pRequestBuffer);
  5392. return TRUE;
  5393. } // UlpReferenceBuffers
  5394. /***************************************************************************++
  5395. Routine Description:
  5396. Frees all pending request buffers on this connection.
  5397. Arguments:
  5398. pConnection - points to a UL_HTTP_CONNECTION
  5399. --***************************************************************************/
  5400. VOID
  5401. UlpFreeReceiveBufferList(
  5402. IN PSLIST_ENTRY pBufferSList
  5403. )
  5404. {
  5405. PUL_REQUEST_BUFFER pBuffer;
  5406. PSLIST_ENTRY pEntry;
  5407. while (NULL != pBufferSList->Next)
  5408. {
  5409. pEntry = pBufferSList->Next;
  5410. pBufferSList->Next = pEntry->Next;
  5411. pBuffer = CONTAINING_RECORD(
  5412. pEntry,
  5413. UL_REQUEST_BUFFER,
  5414. ListEntry
  5415. );
  5416. pBuffer->ListEntry.Flink = NULL;
  5417. pBuffer->ListEntry.Blink = NULL;
  5418. UL_DEREFERENCE_REQUEST_BUFFER(pBuffer);
  5419. }
  5420. } // UlpFreeReceiveBufferList