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

3264 lines
79 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name :
  4. w3response.cxx
  5. Abstract:
  6. W3_RESPONSE object (a friendly wrapper of UL_HTTP_RESPONSE)
  7. Author:
  8. Bilal Alam (BAlam) 10-Dec-99
  9. Project:
  10. ULW3.DLL
  11. --*/
  12. #include "precomp.hxx"
  13. //
  14. // HTTP Status codes
  15. //
  16. HTTP_STATUS HttpStatusOk = { 200, REASON("OK") };
  17. HTTP_STATUS HttpStatusPartialContent = { 206, REASON("Partial Content") };
  18. HTTP_STATUS HttpStatusMultiStatus = { 207, REASON("Multi Status") };
  19. HTTP_STATUS HttpStatusMovedPermanently = { 301, REASON("Moved Permanently") };
  20. HTTP_STATUS HttpStatusRedirect = { 302, REASON("Redirect") };
  21. HTTP_STATUS HttpStatusMovedTemporarily = { 307, REASON("Moved Temporarily") };
  22. HTTP_STATUS HttpStatusNotModified = { 304, REASON("Not Modified") };
  23. HTTP_STATUS HttpStatusBadRequest = { 400, REASON("Bad Request") };
  24. HTTP_STATUS HttpStatusUnauthorized = { 401, REASON("Unauthorized") };
  25. HTTP_STATUS HttpStatusForbidden = { 403, REASON("Forbidden") };
  26. HTTP_STATUS HttpStatusNotFound = { 404, REASON("Not Found") };
  27. HTTP_STATUS HttpStatusMethodNotAllowed = { 405, REASON("Method Not Allowed") };
  28. HTTP_STATUS HttpStatusNotAcceptable = { 406, REASON("Not Acceptable") };
  29. HTTP_STATUS HttpStatusProxyAuthRequired = { 407, REASON("Proxy Authorization Required") };
  30. HTTP_STATUS HttpStatusPreconditionFailed= { 412, REASON("Precondition Failed") };
  31. HTTP_STATUS HttpStatusUrlTooLong = { 414, REASON("URL Too Long") };
  32. HTTP_STATUS HttpStatusRangeNotSatisfiable={ 416, REASON("Requested Range Not Satisfiable") };
  33. HTTP_STATUS HttpStatusLockedError = { 423, REASON("Locked Error") };
  34. HTTP_STATUS HttpStatusServerError = { 500, REASON("Internal Server Error") };
  35. HTTP_STATUS HttpStatusNotImplemented = { 501, REASON("Not Implemented") };
  36. HTTP_STATUS HttpStatusBadGateway = { 502, REASON("Bad Gateway") };
  37. HTTP_STATUS HttpStatusServiceUnavailable= { 503, REASON("Service Unavailable") };
  38. HTTP_STATUS HttpStatusGatewayTimeout = { 504, REASON("Gateway Timeout") };
  39. //
  40. // HTTP SubErrors. This goo is used in determining the proper default error
  41. // message to send to the client when an applicable custom error is not
  42. // configured
  43. //
  44. // As you can see, some sub errors have no corresponding resource string.
  45. // (signified by a 0 index)
  46. //
  47. HTTP_SUB_ERROR HttpNoSubError = { 0, 0 };
  48. HTTP_SUB_ERROR Http401BadLogon = { MD_ERROR_SUB401_LOGON, 0 };
  49. HTTP_SUB_ERROR Http401Config = { MD_ERROR_SUB401_LOGON_CONFIG, 0 };
  50. HTTP_SUB_ERROR Http401Resource = { MD_ERROR_SUB401_LOGON_ACL, 0 };
  51. HTTP_SUB_ERROR Http401Filter = { MD_ERROR_SUB401_FILTER, 0 };
  52. HTTP_SUB_ERROR Http401Application = { MD_ERROR_SUB401_APPLICATION, 0 };
  53. HTTP_SUB_ERROR Http403ExecAccessDenied = { MD_ERROR_SUB403_EXECUTE_ACCESS_DENIED, IDS_EXECUTE_ACCESS_DENIED };
  54. HTTP_SUB_ERROR Http403ReadAccessDenied = { MD_ERROR_SUB403_READ_ACCESS_DENIED, IDS_READ_ACCESS_DENIED };
  55. HTTP_SUB_ERROR Http403WriteAccessDenied = { MD_ERROR_SUB403_WRITE_ACCESS_DENIED, IDS_WRITE_ACCESS_DENIED };
  56. HTTP_SUB_ERROR Http403SSLRequired = { MD_ERROR_SUB403_SSL_REQUIRED, IDS_SSL_REQUIRED };
  57. HTTP_SUB_ERROR Http403SSL128Required = { MD_ERROR_SUB403_SSL128_REQUIRED, IDS_SSL128_REQUIRED };
  58. HTTP_SUB_ERROR Http403IPAddressReject = { MD_ERROR_SUB403_ADDR_REJECT, IDS_ADDR_REJECT };
  59. HTTP_SUB_ERROR Http403CertRequired = { MD_ERROR_SUB403_CERT_REQUIRED, IDS_CERT_REQUIRED };
  60. HTTP_SUB_ERROR Http403SiteAccessDenied = { MD_ERROR_SUB403_SITE_ACCESS_DENIED, IDS_SITE_ACCESS_DENIED };
  61. HTTP_SUB_ERROR Http403TooManyUsers = { MD_ERROR_SUB403_TOO_MANY_USERS, IDS_TOO_MANY_USERS };
  62. HTTP_SUB_ERROR Http403PasswordChange = { MD_ERROR_SUB403_PWD_CHANGE, IDS_PWD_CHANGE };
  63. HTTP_SUB_ERROR Http403MapperDenyAccess = { MD_ERROR_SUB403_MAPPER_DENY_ACCESS, IDS_MAPPER_DENY_ACCESS };
  64. HTTP_SUB_ERROR Http403CertRevoked = { MD_ERROR_SUB403_CERT_REVOKED, IDS_CERT_REVOKED };
  65. HTTP_SUB_ERROR Http403DirBrowsingDenied = { MD_ERROR_SUB403_DIR_LIST_DENIED, IDS_DIR_LIST_DENIED };
  66. HTTP_SUB_ERROR Http403CertInvalid = { MD_ERROR_SUB403_CERT_BAD, IDS_CERT_BAD };
  67. HTTP_SUB_ERROR Http403CertTimeInvalid = { MD_ERROR_SUB403_CERT_TIME_INVALID, IDS_CERT_TIME_INVALID };
  68. HTTP_SUB_ERROR Http502Timeout = { MD_ERROR_SUB502_TIMEOUT, IDS_CGI_APP_TIMEOUT };
  69. HTTP_SUB_ERROR Http502PrematureExit = { MD_ERROR_SUB502_PREMATURE_EXIT, IDS_BAD_CGI_APP };
  70. ALLOC_CACHE_HANDLER * SEND_RAW_BUFFER::sm_pachSendRawBuffers;
  71. HRESULT
  72. W3_RESPONSE::SetHeader(
  73. DWORD ulResponseHeaderIndex,
  74. CHAR * pszHeaderValue,
  75. DWORD cchHeaderValue,
  76. BOOL fAppend
  77. )
  78. /*++
  79. Routine Description:
  80. Set a response based on known header index
  81. Arguments:
  82. ulResponseHeaderIndex - index
  83. pszHeaderValue - Header value
  84. cchHeaderValue - Number of characters (without \0) in pszHeaderValue
  85. Return Value:
  86. HRESULT
  87. --*/
  88. {
  89. STACK_STRA ( strNewHeaderValue, 32);
  90. CHAR * pszNewHeaderValue = NULL;
  91. HRESULT hr;
  92. //
  93. // If value is too long, reject now
  94. //
  95. if (cchHeaderValue > MAXUSHORT)
  96. {
  97. return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
  98. }
  99. DBG_ASSERT( ulResponseHeaderIndex < HttpHeaderResponseMaximum );
  100. if ( _responseMode == RESPONSE_MODE_RAW )
  101. {
  102. hr = SwitchToParsedMode();
  103. if ( FAILED( hr ) )
  104. {
  105. return NULL;
  106. }
  107. DBG_ASSERT( _responseMode == RESPONSE_MODE_PARSED );
  108. }
  109. if ( fAppend )
  110. {
  111. CHAR * headerValue = GetHeader( ulResponseHeaderIndex );
  112. if ( headerValue == NULL )
  113. {
  114. fAppend = FALSE;
  115. }
  116. else
  117. {
  118. hr = strNewHeaderValue.Append( headerValue );
  119. if ( FAILED( hr ) )
  120. {
  121. return hr;
  122. }
  123. hr = strNewHeaderValue.Append( ",", 1 );
  124. if ( FAILED( hr ) )
  125. {
  126. return hr;
  127. }
  128. hr = strNewHeaderValue.Append( pszHeaderValue,
  129. cchHeaderValue );
  130. if ( FAILED( hr ) )
  131. {
  132. return hr;
  133. }
  134. cchHeaderValue = strNewHeaderValue.QueryCCH();
  135. }
  136. }
  137. //
  138. // Regardless of the "known"osity of the header, we will have to
  139. // copy the value. Do so now.
  140. //
  141. hr = _HeaderBuffer.AllocateSpace( fAppend ? strNewHeaderValue.QueryStr() :
  142. pszHeaderValue,
  143. cchHeaderValue,
  144. &pszNewHeaderValue );
  145. if ( FAILED( hr ) )
  146. {
  147. return hr;
  148. }
  149. //
  150. // Set the header
  151. //
  152. return SetHeaderByReference( ulResponseHeaderIndex,
  153. pszNewHeaderValue,
  154. cchHeaderValue );
  155. }
  156. HRESULT
  157. W3_RESPONSE::SetHeader(
  158. CHAR * pszHeaderName,
  159. DWORD cchHeaderName,
  160. CHAR * pszHeaderValue,
  161. DWORD cchHeaderValue,
  162. BOOL fAppend,
  163. BOOL fForceParsed,
  164. BOOL fAlwaysAddUnknown
  165. )
  166. /*++
  167. Routine Description:
  168. Set a response based on header name
  169. Arguments:
  170. pszHeaderName - Points to header name
  171. cchHeaderName - Number of characters (without \0) in pszHeaderName
  172. pszHeaderValue - Points to the header value
  173. cchHeaderValue - Number of characters (without \0) in pszHeaderValue
  174. fAppend - Should we remove any existing value
  175. fForceParsed - Regardless of mode, set the header structurally
  176. fAlwaysAddUnknown - Add as a unknown header always
  177. Return Value:
  178. HRESULT
  179. --*/
  180. {
  181. DWORD cHeaders;
  182. HTTP_UNKNOWN_HEADER* pHeader;
  183. CHAR * pszNewName = NULL;
  184. CHAR * pszNewValue = NULL;
  185. HRESULT hr;
  186. ULONG ulHeaderIndex;
  187. STACK_STRA( strOldHeaderValue, 128 );
  188. //
  189. // If name/value is too long, reject now
  190. //
  191. if (cchHeaderName > MAXUSHORT ||
  192. cchHeaderValue > MAXUSHORT)
  193. {
  194. return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
  195. }
  196. //
  197. // Try to stay in raw mode if we're already in that mode
  198. //
  199. // If we are not appending, that means we are just adding a new header
  200. // so we can just append
  201. //
  202. if ( !fForceParsed )
  203. {
  204. if ( _responseMode == RESPONSE_MODE_RAW &&
  205. !fAppend )
  206. {
  207. DBG_ASSERT( QueryChunks()->DataChunkType == HttpDataChunkFromMemory );
  208. DBG_ASSERT( QueryChunks()->FromMemory.pBuffer == _strRawCoreHeaders.QueryStr() );
  209. hr = _strRawCoreHeaders.Append( pszHeaderName, cchHeaderName );
  210. if ( FAILED( hr ) )
  211. {
  212. return hr;
  213. }
  214. hr = _strRawCoreHeaders.Append( ": ", 2 );
  215. if ( FAILED( hr ) )
  216. {
  217. return hr;
  218. }
  219. hr = _strRawCoreHeaders.Append( pszHeaderValue, cchHeaderValue );
  220. if ( FAILED( hr ) )
  221. {
  222. return hr;
  223. }
  224. hr = _strRawCoreHeaders.Append( "\r\n", 2 );
  225. if ( FAILED( hr ) )
  226. {
  227. return hr;
  228. }
  229. //
  230. // Patch the headers back in
  231. //
  232. QueryChunks()->FromMemory.pBuffer = _strRawCoreHeaders.QueryStr();
  233. QueryChunks()->FromMemory.BufferLength = _strRawCoreHeaders.QueryCB();
  234. return NO_ERROR;
  235. }
  236. else
  237. {
  238. //
  239. // No luck. We'll have to parse the headers and switch into parsed
  240. // mode.
  241. //
  242. if ( _responseMode == RESPONSE_MODE_RAW )
  243. {
  244. hr = SwitchToParsedMode();
  245. if ( FAILED( hr ) )
  246. {
  247. return hr;
  248. }
  249. }
  250. DBG_ASSERT( _responseMode == RESPONSE_MODE_PARSED );
  251. }
  252. }
  253. //
  254. // If we're appending, then get the old header value (if any) and
  255. // append the new value (with a comma delimiter)
  256. //
  257. if ( fAppend )
  258. {
  259. hr = GetHeader( pszHeaderName,
  260. &strOldHeaderValue );
  261. if ( FAILED( hr ) )
  262. {
  263. fAppend = FALSE;
  264. hr = NO_ERROR;
  265. }
  266. else
  267. {
  268. hr = strOldHeaderValue.Append( ",", 1 );
  269. if ( FAILED( hr ) )
  270. {
  271. return hr;
  272. }
  273. hr = strOldHeaderValue.Append( pszHeaderValue,
  274. cchHeaderValue );
  275. if ( FAILED( hr ) )
  276. {
  277. return hr;
  278. }
  279. cchHeaderValue = strOldHeaderValue.QueryCCH();
  280. DeleteHeader( pszHeaderName );
  281. }
  282. }
  283. //
  284. // Regardless of the "known"osity of the header, we will have to
  285. // copy the value. Do so now.
  286. //
  287. hr = _HeaderBuffer.AllocateSpace( fAppend ? strOldHeaderValue.QueryStr() :
  288. pszHeaderValue,
  289. cchHeaderValue,
  290. &pszNewValue );
  291. if ( FAILED( hr ) )
  292. {
  293. return hr;
  294. }
  295. //
  296. // Is this a known header? If so, we can just set by reference now
  297. // since we have copied the header value
  298. //
  299. if ( !fAlwaysAddUnknown )
  300. {
  301. ulHeaderIndex = RESPONSE_HEADER_HASH::GetIndex( pszHeaderName );
  302. if ( ulHeaderIndex != UNKNOWN_INDEX )
  303. {
  304. DBG_ASSERT( ulHeaderIndex < HttpHeaderResponseMaximum );
  305. return SetHeaderByReference( ulHeaderIndex,
  306. pszNewValue,
  307. cchHeaderValue,
  308. fForceParsed );
  309. }
  310. }
  311. //
  312. // OK. This is an unknown header. Make a copy of the header name as
  313. // well and proceed the long way.
  314. //
  315. hr = _HeaderBuffer.AllocateSpace( pszHeaderName,
  316. cchHeaderName,
  317. &pszNewName );
  318. if ( FAILED( hr ) )
  319. {
  320. return hr;
  321. }
  322. cHeaders = ++_ulHttpResponse.Headers.UnknownHeaderCount;
  323. if ( cHeaders * sizeof( HTTP_UNKNOWN_HEADER )
  324. > _bufUnknownHeaders.QuerySize() )
  325. {
  326. if ( !_bufUnknownHeaders.Resize( cHeaders *
  327. sizeof( HTTP_UNKNOWN_HEADER ),
  328. 512 ) )
  329. {
  330. return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  331. }
  332. }
  333. _ulHttpResponse.Headers.pUnknownHeaders = (HTTP_UNKNOWN_HEADER*)
  334. _bufUnknownHeaders.QueryPtr();
  335. //
  336. // We should have a place to put the header now!
  337. //
  338. pHeader = &(_ulHttpResponse.Headers.pUnknownHeaders[ cHeaders - 1 ]);
  339. pHeader->pName = pszNewName;
  340. pHeader->NameLength = cchHeaderName;
  341. pHeader->pRawValue = pszNewValue;
  342. pHeader->RawValueLength = (USHORT)cchHeaderValue;
  343. _fResponseTouched = TRUE;
  344. return S_OK;
  345. }
  346. HRESULT
  347. W3_RESPONSE::SetHeaderByReference(
  348. DWORD ulResponseHeaderIndex,
  349. CHAR * pszHeaderValue,
  350. DWORD cchHeaderValue,
  351. BOOL fForceParsed
  352. )
  353. /*++
  354. Routine Description:
  355. Set a header value by reference. In other words, the caller takes the
  356. reponsibility of managing the memory referenced. The other setheader
  357. methods copy the header values to a private buffer.
  358. Arguments:
  359. ulResponseHeaderIndex - index
  360. pszHeaderValue - Header value
  361. cbHeaderValue - Size of header value in characters (without 0 terminator)
  362. fForceParsed - Set to TRUE if we should always used parsed
  363. Return Value:
  364. HRESULT
  365. --*/
  366. {
  367. HTTP_KNOWN_HEADER * pHeader;
  368. HRESULT hr;
  369. //
  370. // If value is too long, reject now
  371. //
  372. if (cchHeaderValue > MAXUSHORT)
  373. {
  374. return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
  375. }
  376. DBG_ASSERT( ulResponseHeaderIndex < HttpHeaderResponseMaximum );
  377. DBG_ASSERT( pszHeaderValue != NULL || cchHeaderValue == 0 );
  378. if ( !fForceParsed )
  379. {
  380. if ( _responseMode == RESPONSE_MODE_RAW )
  381. {
  382. hr = SwitchToParsedMode();
  383. if ( FAILED( hr ) )
  384. {
  385. return hr;
  386. }
  387. DBG_ASSERT( _responseMode == RESPONSE_MODE_PARSED );
  388. }
  389. }
  390. //
  391. // Set the header
  392. //
  393. pHeader = &(_ulHttpResponse.Headers.pKnownHeaders[ ulResponseHeaderIndex ]);
  394. if ( cchHeaderValue == 0 )
  395. {
  396. pHeader->pRawValue = NULL;
  397. }
  398. else
  399. {
  400. pHeader->pRawValue = pszHeaderValue;
  401. _fResponseTouched = TRUE;
  402. }
  403. pHeader->RawValueLength = (USHORT)cchHeaderValue;
  404. return NO_ERROR;
  405. }
  406. HRESULT
  407. W3_RESPONSE::DeleteHeader(
  408. CHAR * pszHeaderName
  409. )
  410. /*++
  411. Routine Description:
  412. Delete a response header
  413. Arguments:
  414. pszHeaderName - Header to delete
  415. Return Value:
  416. HRESULT
  417. --*/
  418. {
  419. ULONG ulHeaderIndex;
  420. HRESULT hr;
  421. HTTP_UNKNOWN_HEADER * pUnknownHeader;
  422. DWORD i;
  423. if ( _responseMode == RESPONSE_MODE_RAW )
  424. {
  425. hr = SwitchToParsedMode();
  426. if ( FAILED( hr ) )
  427. {
  428. return hr;
  429. }
  430. }
  431. DBG_ASSERT( _responseMode == RESPONSE_MODE_PARSED );
  432. //
  433. // Is this a known header? If so, we can just set by reference now
  434. // since we have copied the header value
  435. //
  436. ulHeaderIndex = RESPONSE_HEADER_HASH::GetIndex( pszHeaderName );
  437. if ( ulHeaderIndex != UNKNOWN_INDEX &&
  438. ulHeaderIndex < HttpHeaderResponseMaximum )
  439. {
  440. _ulHttpResponse.Headers.pKnownHeaders[ ulHeaderIndex ].pRawValue = "";
  441. _ulHttpResponse.Headers.pKnownHeaders[ ulHeaderIndex ].RawValueLength = 0;
  442. }
  443. else
  444. {
  445. //
  446. // Unknown header. First check if it exists
  447. //
  448. for ( i = 0;
  449. i < _ulHttpResponse.Headers.UnknownHeaderCount;
  450. i++ )
  451. {
  452. pUnknownHeader = &(_ulHttpResponse.Headers.pUnknownHeaders[ i ]);
  453. DBG_ASSERT( pUnknownHeader != NULL );
  454. if ( _stricmp( pUnknownHeader->pName, pszHeaderName ) == 0 )
  455. {
  456. break;
  457. }
  458. }
  459. if ( i < _ulHttpResponse.Headers.UnknownHeaderCount )
  460. {
  461. //
  462. // Now shrink the array to remove the header
  463. //
  464. memmove( _ulHttpResponse.Headers.pUnknownHeaders + i,
  465. _ulHttpResponse.Headers.pUnknownHeaders + i + 1,
  466. ( _ulHttpResponse.Headers.UnknownHeaderCount - i - 1 ) *
  467. sizeof( HTTP_UNKNOWN_HEADER ) );
  468. _ulHttpResponse.Headers.UnknownHeaderCount--;
  469. }
  470. }
  471. return NO_ERROR;
  472. }
  473. HRESULT
  474. W3_RESPONSE::SetStatus(
  475. USHORT statusCode,
  476. STRA & strReason,
  477. HTTP_SUB_ERROR & subError
  478. )
  479. /*++
  480. Routine Description:
  481. Set the status/reason of the response
  482. Arguments:
  483. status - Status code
  484. strReason - Reason string
  485. subError - Optional (default 0)
  486. Return Value:
  487. HRESULT
  488. --*/
  489. {
  490. HRESULT hr;
  491. CHAR * pszNewStatus;
  492. hr = _HeaderBuffer.AllocateSpace( strReason.QueryStr(),
  493. strReason.QueryCCH(),
  494. &pszNewStatus );
  495. if ( FAILED( hr ) )
  496. {
  497. return hr;
  498. }
  499. _ulHttpResponse.StatusCode = statusCode;
  500. _ulHttpResponse.pReason = pszNewStatus;
  501. _ulHttpResponse.ReasonLength = strReason.QueryCCH();
  502. _subError = subError;
  503. return NO_ERROR;
  504. }
  505. HRESULT
  506. W3_RESPONSE::GetStatusLine(
  507. STRA * pstrStatusLine
  508. )
  509. /*++
  510. Routine Description:
  511. What a stupid little function. Here we generate what the response's
  512. status line will be
  513. Arguments:
  514. pstrStatusLine - Filled with status like
  515. Return Value:
  516. HRESULT
  517. --*/
  518. {
  519. HRESULT hr = NO_ERROR;
  520. CHAR achNum[ 32 ];
  521. if ( pstrStatusLine == NULL )
  522. {
  523. DBG_ASSERT( FALSE );
  524. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  525. }
  526. // BUGBUG
  527. hr = pstrStatusLine->Copy( "HTTP/1.1 " );
  528. if ( FAILED( hr ) )
  529. {
  530. return hr;
  531. }
  532. _itoa( _ulHttpResponse.StatusCode, achNum, 10 );
  533. hr = pstrStatusLine->Append( achNum );
  534. if ( FAILED( hr ) )
  535. {
  536. return hr;
  537. }
  538. hr = pstrStatusLine->Append( " ", 1 );
  539. if ( FAILED( hr ) )
  540. {
  541. return hr;
  542. }
  543. hr = pstrStatusLine->Append( _ulHttpResponse.pReason,
  544. _ulHttpResponse.ReasonLength );
  545. return hr;
  546. }
  547. HRESULT
  548. W3_RESPONSE::GetHeader(
  549. CHAR * pszHeaderName,
  550. STRA * pstrHeaderValue
  551. )
  552. /*++
  553. Routine Description:
  554. Get a response header
  555. Arguments:
  556. pszHeaderName - Header to retrieve
  557. pstrHeaderValue - Filled with header value
  558. Return Value:
  559. HRESULT
  560. --*/
  561. {
  562. ULONG ulHeaderIndex;
  563. HTTP_UNKNOWN_HEADER * pUnknownHeader;
  564. HTTP_KNOWN_HEADER * pKnownHeader;
  565. HRESULT hr;
  566. BOOL fFound = FALSE;
  567. if ( pstrHeaderValue == NULL ||
  568. pszHeaderName == NULL )
  569. {
  570. DBG_ASSERT( FALSE );
  571. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  572. }
  573. if ( _responseMode == RESPONSE_MODE_RAW )
  574. {
  575. hr = SwitchToParsedMode();
  576. if ( FAILED( hr ) )
  577. {
  578. return hr;
  579. }
  580. }
  581. DBG_ASSERT( _responseMode == RESPONSE_MODE_PARSED );
  582. ulHeaderIndex = RESPONSE_HEADER_HASH::GetIndex( pszHeaderName );
  583. if ( ulHeaderIndex == UNKNOWN_INDEX )
  584. {
  585. //
  586. // Unknown header
  587. //
  588. for ( DWORD i = 0; i < _ulHttpResponse.Headers.UnknownHeaderCount; i++ )
  589. {
  590. pUnknownHeader = &(_ulHttpResponse.Headers.pUnknownHeaders[ i ]);
  591. DBG_ASSERT( pUnknownHeader != NULL );
  592. if ( _stricmp( pszHeaderName,
  593. pUnknownHeader->pName ) == 0 )
  594. {
  595. fFound = TRUE;
  596. break;
  597. }
  598. }
  599. if ( fFound )
  600. {
  601. return pstrHeaderValue->Copy( pUnknownHeader->pRawValue,
  602. pUnknownHeader->RawValueLength );
  603. }
  604. else
  605. {
  606. return HRESULT_FROM_WIN32( ERROR_INVALID_INDEX );
  607. }
  608. }
  609. else
  610. {
  611. //
  612. // Known header
  613. //
  614. // If a filter wanted the Content-Length response header, then we should
  615. // generate it now (lazily)
  616. //
  617. if ( ulHeaderIndex == HttpHeaderContentLength )
  618. {
  619. CHAR achNum[ 32 ];
  620. _ui64toa( QueryContentLength(),
  621. achNum,
  622. 10 );
  623. hr = SetHeader( HttpHeaderContentLength,
  624. achNum,
  625. strlen( achNum ) );
  626. if ( FAILED( hr ) )
  627. {
  628. return hr;
  629. }
  630. }
  631. pKnownHeader = &(_ulHttpResponse.Headers.pKnownHeaders[ ulHeaderIndex ]);
  632. if ( pKnownHeader->pRawValue != NULL &&
  633. pKnownHeader->RawValueLength != 0 )
  634. {
  635. return pstrHeaderValue->Copy( pKnownHeader->pRawValue,
  636. pKnownHeader->RawValueLength );
  637. }
  638. else
  639. {
  640. return HRESULT_FROM_WIN32( ERROR_INVALID_INDEX );
  641. }
  642. }
  643. }
  644. VOID
  645. W3_RESPONSE::ClearHeaders(
  646. VOID
  647. )
  648. /*++
  649. Routine Description:
  650. Clear headers
  651. Arguments:
  652. None
  653. Return Value:
  654. None
  655. --*/
  656. {
  657. memset( &(_ulHttpResponse.Headers),
  658. 0,
  659. sizeof( _ulHttpResponse.Headers ) );
  660. }
  661. HRESULT
  662. W3_RESPONSE::AddFileHandleChunk(
  663. HANDLE hFile,
  664. ULONGLONG cbOffset,
  665. ULONGLONG cbLength
  666. )
  667. /*++
  668. Routine Description:
  669. Add file handle chunk to response
  670. Arguments:
  671. hFile - File handle
  672. cbOffset - Offset in file
  673. cbLength - Length of chunk
  674. Return Value:
  675. HRESULT
  676. --*/
  677. {
  678. HTTP_DATA_CHUNK DataChunk;
  679. HRESULT hr;
  680. _fResponseTouched = TRUE;
  681. DataChunk.DataChunkType = HttpDataChunkFromFileHandle;
  682. DataChunk.FromFileHandle.ByteRange.StartingOffset.QuadPart = cbOffset;
  683. DataChunk.FromFileHandle.ByteRange.Length.QuadPart = cbLength;
  684. DataChunk.FromFileHandle.FileHandle = hFile;
  685. hr = InsertDataChunk( &DataChunk, -1 );
  686. if ( FAILED( hr ) )
  687. {
  688. return hr;
  689. }
  690. //
  691. // Update content length count
  692. //
  693. _cbContentLength += cbLength;
  694. return NO_ERROR;
  695. }
  696. HRESULT
  697. W3_RESPONSE::AddMemoryChunkByReference(
  698. PVOID pvBuffer,
  699. DWORD cbBuffer
  700. )
  701. /*++
  702. Routine Description:
  703. Add memory chunk to W3_RESPONSE. Don't copy the memory -> we assume
  704. the caller will manage the memory lifetime
  705. Arguments:
  706. pvBuffer - Memory buffer
  707. cbBuffer - Size of memory buffer
  708. Return Value:
  709. HRESULT
  710. --*/
  711. {
  712. HTTP_DATA_CHUNK DataChunk;
  713. HRESULT hr;
  714. _fResponseTouched = TRUE;
  715. DataChunk.DataChunkType = HttpDataChunkFromMemory;
  716. DataChunk.FromMemory.pBuffer = pvBuffer;
  717. DataChunk.FromMemory.BufferLength = cbBuffer;
  718. hr = InsertDataChunk( &DataChunk, -1 );
  719. if ( FAILED( hr ) )
  720. {
  721. return hr;
  722. }
  723. //
  724. // Update content length count
  725. //
  726. _cbContentLength += cbBuffer;
  727. return NO_ERROR;
  728. }
  729. HRESULT
  730. W3_RESPONSE::GetChunks(
  731. OUT BUFFER *chunkBuffer,
  732. OUT DWORD *pdwNumChunks)
  733. {
  734. if ( !chunkBuffer->Resize( _cChunks * sizeof( HTTP_DATA_CHUNK ) ) )
  735. {
  736. return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  737. }
  738. memcpy( chunkBuffer->QueryPtr(),
  739. _bufChunks.QueryPtr(),
  740. _cChunks * sizeof( HTTP_DATA_CHUNK ) );
  741. *pdwNumChunks = _cChunks;
  742. Clear( TRUE );
  743. return S_OK;
  744. }
  745. HRESULT
  746. W3_RESPONSE::Clear(
  747. BOOL fClearEntityOnly
  748. )
  749. /*++
  750. Routine Description:
  751. Clear response
  752. Arguments:
  753. fEntityOnly - Set to TRUE to clear only entity
  754. Return Value:
  755. HRESULT
  756. --*/
  757. {
  758. HRESULT hr = NO_ERROR;
  759. if ( !fClearEntityOnly )
  760. {
  761. //
  762. // Must we send the response in raw mode?
  763. //
  764. _fIncompleteHeaders = FALSE;
  765. //
  766. // Raw mode management
  767. //
  768. _strRawCoreHeaders.Reset();
  769. _cFirstEntityChunk = 0;
  770. //
  771. // Always start in parsed mode
  772. //
  773. _responseMode = RESPONSE_MODE_PARSED;
  774. //
  775. // Clear headers/status
  776. //
  777. ClearHeaders();
  778. }
  779. _cChunks = _cFirstEntityChunk;
  780. _cbContentLength = 0;
  781. return hr;
  782. }
  783. HRESULT
  784. W3_RESPONSE::SwitchToParsedMode(
  785. VOID
  786. )
  787. /*++
  788. Routine Description:
  789. Switch to parsed mode
  790. Arguments:
  791. None
  792. Return Value:
  793. HRESULT
  794. --*/
  795. {
  796. HRESULT hr;
  797. CHAR * pszHeaders;
  798. HTTP_DATA_CHUNK * pCurrentChunk;
  799. DWORD i;
  800. DBG_ASSERT( _responseMode == RESPONSE_MODE_RAW );
  801. //
  802. // Loop thru all header chunks and parse them out
  803. //
  804. for ( i = 0;
  805. i < _cFirstEntityChunk;
  806. i++ )
  807. {
  808. pCurrentChunk = &(QueryChunks()[ i ]);
  809. DBG_ASSERT( pCurrentChunk->DataChunkType == HttpDataChunkFromMemory );
  810. pszHeaders = (CHAR*) pCurrentChunk->FromMemory.pBuffer;
  811. if ( i == 0 )
  812. {
  813. //
  814. // The first header chunk contains core headers plus status line
  815. //
  816. // (remember to skip the status line)
  817. //
  818. pszHeaders = strstr( pszHeaders, "\r\n" );
  819. DBG_ASSERT( pszHeaders != NULL );
  820. pszHeaders += 2;
  821. DBG_ASSERT( *pszHeaders != '\0' );
  822. }
  823. hr = ParseHeadersFromStream( pszHeaders );
  824. if ( FAILED( hr ) )
  825. {
  826. return hr;
  827. }
  828. }
  829. _strRawCoreHeaders.Reset();
  830. //
  831. // Any chunks in the response which are actually headers should be
  832. // removed
  833. //
  834. if ( _cFirstEntityChunk != 0 )
  835. {
  836. memmove( QueryChunks(),
  837. QueryChunks() + _cFirstEntityChunk,
  838. ( _cChunks - _cFirstEntityChunk ) * sizeof( HTTP_DATA_CHUNK ) );
  839. _cChunks -= _cFirstEntityChunk;
  840. _cFirstEntityChunk = 0;
  841. }
  842. //
  843. // Cool. Now we are in parsed mode
  844. //
  845. _responseMode = RESPONSE_MODE_PARSED;
  846. return NO_ERROR;
  847. }
  848. HRESULT
  849. W3_RESPONSE::SwitchToRawMode(
  850. W3_CONTEXT * pW3Context,
  851. CHAR * pszAdditionalHeaders,
  852. DWORD cchAdditionalHeaders
  853. )
  854. /*++
  855. Routine Description:
  856. Switch into raw mode.
  857. Builds a raw response for use by raw data filters and/or ISAPI. This
  858. raw response will be a set of chunks which contact the entire response
  859. including serialized headers.
  860. Arguments:
  861. pW3Context - W3 context
  862. pszAdditionalHeaders - Additional raw headers to add
  863. cchAdditionalHeaders - Size of additional headers
  864. Return Value:
  865. HRESULT
  866. --*/
  867. {
  868. HRESULT hr = NO_ERROR;
  869. HTTP_DATA_CHUNK dataChunk;
  870. DBG_ASSERT( _responseMode == RESPONSE_MODE_PARSED );
  871. //
  872. // Generate raw core headers
  873. //
  874. hr = BuildRawCoreHeaders( pW3Context );
  875. if ( FAILED( hr ) )
  876. {
  877. return hr;
  878. }
  879. //
  880. // Now fix up the chunks so the raw stream headers are in the right place
  881. //
  882. //
  883. // First chunk is the raw core headers (includes the status line)
  884. //
  885. dataChunk.DataChunkType = HttpDataChunkFromMemory;
  886. dataChunk.FromMemory.pBuffer = _strRawCoreHeaders.QueryStr();
  887. dataChunk.FromMemory.BufferLength = _strRawCoreHeaders.QueryCB();
  888. hr = InsertDataChunk( &dataChunk, 0 );
  889. if ( FAILED( hr ) )
  890. {
  891. return hr;
  892. }
  893. //
  894. // Remember the beginning of real entity
  895. //
  896. _cFirstEntityChunk = 1;
  897. //
  898. // Now add any additional header stream
  899. //
  900. if ( cchAdditionalHeaders != 0 )
  901. {
  902. dataChunk.DataChunkType = HttpDataChunkFromMemory;
  903. dataChunk.FromMemory.pBuffer = pszAdditionalHeaders;
  904. dataChunk.FromMemory.BufferLength = cchAdditionalHeaders;
  905. hr = InsertDataChunk( &dataChunk, 1 );
  906. if ( FAILED( hr ) )
  907. {
  908. return hr;
  909. }
  910. _cFirstEntityChunk++;
  911. }
  912. //
  913. // We're now in raw mode
  914. //
  915. _responseMode = RESPONSE_MODE_RAW;
  916. return NO_ERROR;
  917. }
  918. HRESULT
  919. W3_RESPONSE::SendResponse(
  920. W3_CONTEXT * pW3Context,
  921. DWORD dwResponseFlags,
  922. DWORD * pcbSent,
  923. HTTP_LOG_FIELDS_DATA * pUlLogData
  924. )
  925. /*++
  926. Routine Description:
  927. Send a W3_RESPONSE to the client. This is a very simple wrapper
  928. of UlAtqSendHttpResponse.
  929. Arguments:
  930. pW3Context - W3 context (contains amongst other things ULATQ context)
  931. dwResponseFlags - W3_RESPONSE* flags
  932. pcbSent - Filled with number of bytes sent (if sync)
  933. pUlLogData - Log data
  934. Return Value:
  935. HRESULT
  936. --*/
  937. {
  938. HRESULT hr;
  939. DWORD dwFlags = 0;
  940. HTTP_CACHE_POLICY cachePolicy;
  941. HTTP_DATA_CHUNK * pStartChunk;
  942. BOOL fFinished = FALSE;
  943. BOOL fAsync;
  944. DBG_ASSERT( CheckSignature() );
  945. if ( dwResponseFlags & W3_RESPONSE_SUPPRESS_HEADERS )
  946. {
  947. if (_responseMode == RESPONSE_MODE_RAW)
  948. {
  949. _cChunks -= _cFirstEntityChunk;
  950. memmove( QueryChunks(),
  951. QueryChunks() + _cFirstEntityChunk,
  952. _cChunks * sizeof( HTTP_DATA_CHUNK ) );
  953. _cFirstEntityChunk = 0;
  954. }
  955. return SendEntity( pW3Context,
  956. dwResponseFlags,
  957. pcbSent,
  958. pUlLogData );
  959. }
  960. if ( dwResponseFlags & W3_RESPONSE_MORE_DATA )
  961. {
  962. //
  963. // More data follows this response?
  964. //
  965. dwFlags |= HTTP_SEND_RESPONSE_FLAG_MORE_DATA;
  966. }
  967. //
  968. // UL needs to see the disconnect flag on the initial response
  969. // so that it knows to send the proper connection header to the
  970. // client. This needs to happen even if the more data flag is
  971. // set.
  972. //
  973. if ( dwResponseFlags & W3_RESPONSE_DISCONNECT )
  974. {
  975. //
  976. // Disconnect or not?
  977. //
  978. dwFlags |= HTTP_SEND_RESPONSE_FLAG_DISCONNECT;
  979. }
  980. //
  981. // Setup cache policy
  982. //
  983. if ( dwResponseFlags & W3_RESPONSE_UL_CACHEABLE )
  984. {
  985. cachePolicy.Policy = HttpCachePolicyUserInvalidates;
  986. }
  987. else
  988. {
  989. cachePolicy.Policy = HttpCachePolicyNocache;
  990. }
  991. //
  992. // Convert to raw if filtering is needed
  993. //
  994. // OR if an ISAPI once gave us incomplete headers and thus we need to
  995. // go back to raw mode (without terminating headers)
  996. //
  997. // OR an ISAPI has called WriteClient() before sending a response
  998. //
  999. if ( pW3Context->IsNotificationNeeded( SF_NOTIFY_SEND_RAW_DATA ) ||
  1000. _fIncompleteHeaders ||
  1001. _fResponseSent )
  1002. {
  1003. if ( _responseMode == RESPONSE_MODE_PARSED )
  1004. {
  1005. hr = GenerateAutomaticHeaders( pW3Context,
  1006. dwFlags );
  1007. if ( FAILED( hr ) )
  1008. {
  1009. return hr;
  1010. }
  1011. hr = SwitchToRawMode( pW3Context,
  1012. _fIncompleteHeaders ? "" : "\r\n",
  1013. _fIncompleteHeaders ? 0 : 2 );
  1014. if ( FAILED( hr ) )
  1015. {
  1016. return hr;
  1017. }
  1018. }
  1019. DBG_ASSERT( _responseMode == RESPONSE_MODE_RAW );
  1020. //
  1021. // OK. This is a little lame. But the _strRawCoreHeaders may have
  1022. // changed a bit since we last setup the chunks for the header
  1023. // stream. Just adjust it here
  1024. //
  1025. pStartChunk = QueryChunks();
  1026. pStartChunk[0].FromMemory.pBuffer = _strRawCoreHeaders.QueryStr();
  1027. pStartChunk[0].FromMemory.BufferLength = _strRawCoreHeaders.QueryCB();
  1028. //
  1029. // If we're going to be kill entity and/or headers, do so now before
  1030. // calling into the filter
  1031. //
  1032. if ( dwResponseFlags & W3_RESPONSE_SUPPRESS_ENTITY )
  1033. {
  1034. _cChunks = _cFirstEntityChunk;
  1035. }
  1036. //
  1037. // Filter the chunks if needed
  1038. //
  1039. if ( pW3Context->IsNotificationNeeded( SF_NOTIFY_SEND_RAW_DATA ) )
  1040. {
  1041. hr = ProcessRawChunks( pW3Context, &fFinished );
  1042. if ( FAILED( hr ) )
  1043. {
  1044. return hr;
  1045. }
  1046. if ( fFinished )
  1047. {
  1048. dwFlags |= HTTP_SEND_RESPONSE_FLAG_DISCONNECT;
  1049. _cChunks = 0;
  1050. }
  1051. }
  1052. }
  1053. //
  1054. // Take care of any compression now
  1055. //
  1056. if ( !_fIncompleteHeaders &&
  1057. pW3Context->QueryUrlContext() != NULL )
  1058. {
  1059. W3_METADATA * pMetaData;
  1060. pMetaData = pW3Context->QueryUrlContext()->QueryMetaData();
  1061. DBG_ASSERT( pMetaData != NULL );
  1062. if ( !pW3Context->QueryDoneWithCompression() &&
  1063. pMetaData->QueryDoDynamicCompression() )
  1064. {
  1065. if (FAILED(hr = HTTP_COMPRESSION::OnSendResponse(
  1066. pW3Context,
  1067. !!( dwResponseFlags & W3_RESPONSE_MORE_DATA ) ) ) )
  1068. {
  1069. return hr;
  1070. }
  1071. }
  1072. }
  1073. //
  1074. // From now on, we must send any future responses raw
  1075. //
  1076. _fResponseSent = TRUE;
  1077. //
  1078. // Async?
  1079. //
  1080. fAsync = dwResponseFlags & W3_RESPONSE_ASYNC ? TRUE : FALSE;
  1081. if ( _responseMode == RESPONSE_MODE_RAW )
  1082. {
  1083. if ( dwResponseFlags & W3_RESPONSE_SUPPRESS_ENTITY )
  1084. {
  1085. _cChunks = _cFirstEntityChunk;
  1086. }
  1087. hr = UlAtqSendEntityBody( pW3Context->QueryUlatqContext(),
  1088. fAsync,
  1089. dwFlags | HTTP_SEND_RESPONSE_FLAG_RAW_HEADER,
  1090. _cChunks,
  1091. _cChunks ? QueryChunks() : NULL,
  1092. pcbSent,
  1093. pUlLogData );
  1094. }
  1095. else
  1096. {
  1097. if ( dwResponseFlags & W3_RESPONSE_SUPPRESS_ENTITY )
  1098. {
  1099. _cChunks = 0;
  1100. }
  1101. _ulHttpResponse.EntityChunkCount = _cChunks;
  1102. _ulHttpResponse.pEntityChunks = _cChunks ? QueryChunks() : NULL;
  1103. hr = UlAtqSendHttpResponse( pW3Context->QueryUlatqContext(),
  1104. fAsync,
  1105. dwFlags,
  1106. &(_ulHttpResponse),
  1107. &cachePolicy,
  1108. pcbSent,
  1109. pUlLogData );
  1110. }
  1111. if ( FAILED( hr ) )
  1112. {
  1113. //
  1114. // If we couldn't send the response thru UL, then this is really bad.
  1115. // Do not reset _fSendRawData since no response will get thru
  1116. //
  1117. }
  1118. return hr;
  1119. }
  1120. HRESULT
  1121. W3_RESPONSE::SendEntity(
  1122. W3_CONTEXT * pW3Context,
  1123. DWORD dwResponseFlags,
  1124. DWORD * pcbSent,
  1125. HTTP_LOG_FIELDS_DATA * pUlLogData
  1126. )
  1127. /*++
  1128. Routine Description:
  1129. Send entity to the client
  1130. Arguments:
  1131. pMainContext - Main context (contains amongst other things ULATQ context)
  1132. dwResponseFlags - W3_REPSONSE flags
  1133. pcbSent - Number of bytes sent (when sync)
  1134. pUlLogData - Log data for the response (this entity is part of response)
  1135. Return Value:
  1136. Win32 Error indicating status
  1137. --*/
  1138. {
  1139. HRESULT hr = NO_ERROR;
  1140. DWORD dwFlags = 0;
  1141. BOOL fAsync;
  1142. BOOL fFinished = FALSE;
  1143. DBG_ASSERT( CheckSignature() );
  1144. //
  1145. // If we get to here and a response hasn't yet been sent, then we must
  1146. // call HttpSendEntity first (not that HTTP.SYS lets us do that)
  1147. //
  1148. if ( !_fResponseSent )
  1149. {
  1150. _fResponseSent = TRUE;
  1151. dwFlags |= HTTP_SEND_RESPONSE_FLAG_RAW_HEADER;
  1152. }
  1153. //
  1154. // Note that both HTTP_SEND_RESPONSE_FLAG_MORE_DATA and
  1155. // HTTP_SEND_RESPONSE_FLAG_DISCONNECT cannot be set at the same time
  1156. //
  1157. if ( dwResponseFlags & W3_RESPONSE_MORE_DATA )
  1158. {
  1159. dwFlags |= HTTP_SEND_RESPONSE_FLAG_MORE_DATA;
  1160. }
  1161. else if ( dwResponseFlags & W3_RESPONSE_DISCONNECT )
  1162. {
  1163. dwFlags |= HTTP_SEND_RESPONSE_FLAG_DISCONNECT;
  1164. }
  1165. //
  1166. // Send chunks to be processed if filtering if needed (and there are
  1167. // chunks available)
  1168. //
  1169. if ( _cChunks &&
  1170. !( dwFlags & W3_RESPONSE_SUPPRESS_ENTITY ) &&
  1171. pW3Context->IsNotificationNeeded( SF_NOTIFY_SEND_RAW_DATA ) )
  1172. {
  1173. hr = ProcessRawChunks( pW3Context, &fFinished );
  1174. if ( FAILED( hr ) )
  1175. {
  1176. return hr;
  1177. }
  1178. if ( fFinished )
  1179. {
  1180. dwFlags |= HTTP_SEND_RESPONSE_FLAG_DISCONNECT;
  1181. _cChunks = 0;
  1182. }
  1183. }
  1184. //
  1185. // Take care of any compression now
  1186. //
  1187. if ( pW3Context->QueryUrlContext() != NULL )
  1188. {
  1189. W3_METADATA *pMetaData;
  1190. pMetaData = pW3Context->QueryUrlContext()->QueryMetaData();
  1191. DBG_ASSERT( pMetaData != NULL);
  1192. if (!pW3Context->QueryDoneWithCompression() &&
  1193. pMetaData->QueryDoDynamicCompression())
  1194. {
  1195. if (FAILED(hr = HTTP_COMPRESSION::DoDynamicCompression(
  1196. pW3Context,
  1197. dwResponseFlags & W3_RESPONSE_MORE_DATA ? TRUE : FALSE )))
  1198. {
  1199. return hr;
  1200. }
  1201. }
  1202. }
  1203. //
  1204. // If we are suppressing entity (in case of HEAD for example) do it
  1205. // now by clearing the chunk count
  1206. //
  1207. if ( dwResponseFlags & W3_RESPONSE_SUPPRESS_ENTITY )
  1208. {
  1209. _cChunks = 0;
  1210. }
  1211. fAsync = ( dwResponseFlags & W3_RESPONSE_ASYNC ) ? TRUE : FALSE;
  1212. //
  1213. // Finally, send stuff out
  1214. //
  1215. hr = UlAtqSendEntityBody(pW3Context->QueryUlatqContext(),
  1216. fAsync,
  1217. dwFlags,
  1218. _cChunks,
  1219. _cChunks ? QueryChunks() : NULL,
  1220. pcbSent,
  1221. pUlLogData);
  1222. return hr;
  1223. }
  1224. HRESULT
  1225. W3_RESPONSE::GenerateAutomaticHeaders(
  1226. W3_CONTEXT * pW3Context,
  1227. DWORD dwFlags
  1228. )
  1229. /*++
  1230. Routine Description:
  1231. Parse-Mode only function
  1232. Generate headers which UL normally generates on our behalf. This means
  1233. Server:
  1234. Connection:
  1235. Content-Length:
  1236. Date:
  1237. Arguments:
  1238. pW3Context - Helps us build the core headers (since we need to look at
  1239. the request). Can be NULL to indicate to use defaults
  1240. dwFlags - Flags we would have passed to UL
  1241. Return Value:
  1242. HRESULT
  1243. --*/
  1244. {
  1245. HTTP_KNOWN_HEADER * pHeader;
  1246. CHAR * pszHeaderValue;
  1247. CHAR achDate[ 128 ];
  1248. CHAR achNum[ 64 ];
  1249. DWORD cchDate;
  1250. DWORD cchNum;
  1251. SYSTEMTIME systemTime;
  1252. HTTP_VERSION httpVersion;
  1253. HRESULT hr;
  1254. BOOL fCreateContentLength;
  1255. BOOL fDisconnecting = FALSE;
  1256. DBG_ASSERT( _responseMode == RESPONSE_MODE_PARSED );
  1257. //
  1258. // Server:
  1259. //
  1260. pHeader = &(_ulHttpResponse.Headers.pKnownHeaders[ HttpHeaderServer ]);
  1261. if ( pHeader->pRawValue == NULL )
  1262. {
  1263. pHeader->pRawValue = SERVER_SOFTWARE_STRING;
  1264. pHeader->RawValueLength = sizeof( SERVER_SOFTWARE_STRING ) - 1;
  1265. }
  1266. //
  1267. // Date:
  1268. //
  1269. pHeader = &(_ulHttpResponse.Headers.pKnownHeaders[ HttpHeaderDate ]);
  1270. if ( pHeader->pRawValue == NULL )
  1271. {
  1272. if(!IISGetCurrentTimeAsSystemTime(&systemTime))
  1273. {
  1274. GetSystemTime( &systemTime );
  1275. }
  1276. if ( !SystemTimeToGMT( systemTime,
  1277. achDate,
  1278. sizeof(achDate) ) )
  1279. {
  1280. return HRESULT_FROM_WIN32( GetLastError() );
  1281. }
  1282. cchDate = strlen( achDate );
  1283. hr = _HeaderBuffer.AllocateSpace( achDate,
  1284. cchDate,
  1285. &pszHeaderValue );
  1286. if ( FAILED( hr ) )
  1287. {
  1288. return hr;
  1289. }
  1290. DBG_ASSERT( pszHeaderValue != NULL );
  1291. pHeader->pRawValue = pszHeaderValue;
  1292. pHeader->RawValueLength = cchDate;
  1293. }
  1294. //
  1295. // Are we going to be disconnecting?
  1296. //
  1297. if ( pW3Context != NULL &&
  1298. ( pW3Context->QueryDisconnect() ||
  1299. pW3Context->QueryRequest()->QueryClientWantsDisconnect() ) )
  1300. {
  1301. fDisconnecting = TRUE;
  1302. }
  1303. //
  1304. // Connection:
  1305. //
  1306. pHeader = &(_ulHttpResponse.Headers.pKnownHeaders[ HttpHeaderConnection ] );
  1307. if ( pHeader->pRawValue == NULL )
  1308. {
  1309. if ( pW3Context == NULL )
  1310. {
  1311. HTTP_SET_VERSION( httpVersion, 1, 0 );
  1312. }
  1313. else
  1314. {
  1315. httpVersion = pW3Context->QueryRequest()->QueryVersion();
  1316. }
  1317. if ( fDisconnecting )
  1318. {
  1319. if ( HTTP_GREATER_EQUAL_VERSION( httpVersion, 1, 0 ) )
  1320. {
  1321. pHeader->pRawValue = "close";
  1322. pHeader->RawValueLength = sizeof( "close" ) - 1;
  1323. }
  1324. }
  1325. else
  1326. {
  1327. if ( HTTP_EQUAL_VERSION( httpVersion, 1, 0 ) )
  1328. {
  1329. pHeader->pRawValue = "keep-alive";
  1330. pHeader->RawValueLength = sizeof( "keep-alive" ) - 1;
  1331. }
  1332. }
  1333. }
  1334. //
  1335. // Should we generate content length?
  1336. //
  1337. fCreateContentLength = TRUE;
  1338. if ( dwFlags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA )
  1339. {
  1340. fCreateContentLength = FALSE;
  1341. }
  1342. if ( fCreateContentLength &&
  1343. QueryStatusCode() / 100 == 1 ||
  1344. QueryStatusCode() == 204 ||
  1345. QueryStatusCode() == 304 )
  1346. {
  1347. fCreateContentLength = FALSE;
  1348. }
  1349. if ( fCreateContentLength &&
  1350. pW3Context != NULL &&
  1351. pW3Context->QueryMainContext()->QueryShouldGenerateContentLength() )
  1352. {
  1353. fCreateContentLength = FALSE;
  1354. }
  1355. //
  1356. // Now generate if needed
  1357. //
  1358. if ( fCreateContentLength )
  1359. {
  1360. //
  1361. // Generate a content length header if needed
  1362. //
  1363. pHeader = &(_ulHttpResponse.Headers.pKnownHeaders[ HttpHeaderContentLength ]);
  1364. if ( pHeader->pRawValue == NULL )
  1365. {
  1366. _ui64toa( QueryContentLength(),
  1367. achNum,
  1368. 10 );
  1369. cchNum = strlen( achNum );
  1370. pszHeaderValue = NULL;
  1371. hr = _HeaderBuffer.AllocateSpace( achNum,
  1372. cchNum,
  1373. &pszHeaderValue );
  1374. if ( FAILED( hr ) )
  1375. {
  1376. return hr;
  1377. }
  1378. DBG_ASSERT( pszHeaderValue != NULL );
  1379. pHeader->pRawValue = pszHeaderValue;
  1380. pHeader->RawValueLength = cchNum;
  1381. }
  1382. }
  1383. return NO_ERROR;
  1384. }
  1385. HRESULT
  1386. W3_RESPONSE::BuildRawCoreHeaders(
  1387. W3_CONTEXT * pW3Context
  1388. )
  1389. /*++
  1390. Routine Description:
  1391. Build raw header stream for the core headers that UL normally generates
  1392. on our behalf. This means structured headers and some special
  1393. "automatic" ones like Connection:, Date:, Server:, etc.
  1394. Arguments:
  1395. pW3Context - Helps us build core header (in particular the Connection:)
  1396. Return Value:
  1397. HRESULT
  1398. --*/
  1399. {
  1400. HRESULT hr = NO_ERROR;
  1401. CHAR achNumber[ 32 ];
  1402. HTTP_KNOWN_HEADER * pKnownHeader;
  1403. HTTP_UNKNOWN_HEADER * pUnknownHeader;
  1404. CHAR * pszHeaderName;
  1405. DWORD cchHeaderName;
  1406. DWORD i;
  1407. if ( pW3Context == NULL )
  1408. {
  1409. DBG_ASSERT( FALSE );
  1410. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1411. }
  1412. _strRawCoreHeaders.Reset();
  1413. //
  1414. // Build a status line
  1415. //
  1416. hr = _strRawCoreHeaders.Copy( "HTTP/1.1 " );
  1417. if ( FAILED( hr ) )
  1418. {
  1419. return hr;
  1420. }
  1421. _itoa( _ulHttpResponse.StatusCode,
  1422. achNumber,
  1423. 10 );
  1424. hr = _strRawCoreHeaders.Append( achNumber );
  1425. if ( FAILED( hr ) )
  1426. {
  1427. return hr;
  1428. }
  1429. hr = _strRawCoreHeaders.Append( " " );
  1430. if ( FAILED( hr ) )
  1431. {
  1432. return hr;
  1433. }
  1434. hr = _strRawCoreHeaders.Append( _ulHttpResponse.pReason );
  1435. if ( FAILED( hr ) )
  1436. {
  1437. return hr;
  1438. }
  1439. hr = _strRawCoreHeaders.Append( "\r\n" );
  1440. if ( FAILED( hr ) )
  1441. {
  1442. return hr;
  1443. }
  1444. //
  1445. // Iterate thru all the headers in our structured response and set
  1446. // append them to the stream.
  1447. //
  1448. // Start with the known headers
  1449. //
  1450. for ( i = 0;
  1451. i < HttpHeaderResponseMaximum;
  1452. i++ )
  1453. {
  1454. pKnownHeader = &(_ulHttpResponse.Headers.pKnownHeaders[ i ]);
  1455. if ( pKnownHeader->pRawValue != NULL &&
  1456. pKnownHeader->pRawValue[ 0 ] != '\0' )
  1457. {
  1458. pszHeaderName = RESPONSE_HEADER_HASH::GetString( i, &cchHeaderName );
  1459. DBG_ASSERT( pszHeaderName != NULL );
  1460. hr = _strRawCoreHeaders.Append( pszHeaderName, cchHeaderName );
  1461. if ( FAILED( hr ) )
  1462. {
  1463. return hr;
  1464. }
  1465. hr = _strRawCoreHeaders.Append( ": ", 2 );
  1466. if ( FAILED( hr ) )
  1467. {
  1468. return hr;
  1469. }
  1470. hr = _strRawCoreHeaders.Append( pKnownHeader->pRawValue,
  1471. pKnownHeader->RawValueLength );
  1472. if ( FAILED( hr ) )
  1473. {
  1474. return hr;
  1475. }
  1476. hr = _strRawCoreHeaders.Append( "\r\n", 2 );
  1477. if ( FAILED( hr ) )
  1478. {
  1479. return hr;
  1480. }
  1481. //
  1482. // Now clear the header
  1483. //
  1484. pKnownHeader->pRawValue = NULL;
  1485. pKnownHeader->RawValueLength = 0;
  1486. }
  1487. }
  1488. //
  1489. // Next, the unknown headers
  1490. //
  1491. for ( i = 0;
  1492. i < _ulHttpResponse.Headers.UnknownHeaderCount;
  1493. i++ )
  1494. {
  1495. pUnknownHeader = &(_ulHttpResponse.Headers.pUnknownHeaders[ i ]);
  1496. hr = _strRawCoreHeaders.Append( pUnknownHeader->pName,
  1497. pUnknownHeader->NameLength );
  1498. if ( FAILED( hr ) )
  1499. {
  1500. return hr;
  1501. }
  1502. hr = _strRawCoreHeaders.Append( ": ", 2 );
  1503. if ( FAILED( hr ) )
  1504. {
  1505. return hr;
  1506. }
  1507. hr = _strRawCoreHeaders.Append( pUnknownHeader->pRawValue,
  1508. pUnknownHeader->RawValueLength );
  1509. if ( FAILED( hr ) )
  1510. {
  1511. return hr;
  1512. }
  1513. hr = _strRawCoreHeaders.Append( "\r\n", 2 );
  1514. if ( FAILED( hr ) )
  1515. {
  1516. return hr;
  1517. }
  1518. }
  1519. //
  1520. // Clear the unknown headers
  1521. //
  1522. _ulHttpResponse.Headers.UnknownHeaderCount = 0;
  1523. return NO_ERROR;
  1524. }
  1525. HRESULT
  1526. W3_RESPONSE::AppendResponseHeaders(
  1527. STRA & strHeaders
  1528. )
  1529. /*++
  1530. Routine Description:
  1531. Add response headers (an ISAPI filter special)
  1532. Arguments:
  1533. strHeaders - Additional headers to add
  1534. (may contain entity -> LAAAAAAMMMMME)
  1535. Return Value:
  1536. HRESULT
  1537. --*/
  1538. {
  1539. HRESULT hr;
  1540. LPSTR pszEntity;
  1541. HTTP_DATA_CHUNK DataChunk;
  1542. if ( strHeaders.IsEmpty() )
  1543. {
  1544. return NO_ERROR;
  1545. }
  1546. if ( _responseMode == RESPONSE_MODE_RAW &&
  1547. QueryChunks()->FromMemory.pBuffer == _strRawCoreHeaders.QueryStr() )
  1548. {
  1549. DBG_ASSERT( QueryChunks()->DataChunkType == HttpDataChunkFromMemory );
  1550. DBG_ASSERT( QueryChunks()->FromMemory.pBuffer == _strRawCoreHeaders.QueryStr() );
  1551. hr = _strRawCoreHeaders.Append( strHeaders );
  1552. if ( FAILED( hr ) )
  1553. {
  1554. return hr;
  1555. }
  1556. //
  1557. // Patch first chunk since point may have changed
  1558. //
  1559. QueryChunks()->FromMemory.pBuffer = _strRawCoreHeaders.QueryStr();
  1560. QueryChunks()->FromMemory.BufferLength = _strRawCoreHeaders.QueryCB();
  1561. }
  1562. else
  1563. {
  1564. hr = ParseHeadersFromStream( strHeaders.QueryStr() );
  1565. if ( FAILED( hr ) )
  1566. {
  1567. return hr;
  1568. }
  1569. //
  1570. // Look for entity body in headers
  1571. //
  1572. pszEntity = strstr( strHeaders.QueryStr(), "\r\n\r\n" );
  1573. if ( pszEntity != NULL )
  1574. {
  1575. DataChunk.DataChunkType = HttpDataChunkFromMemory;
  1576. DataChunk.FromMemory.pBuffer = pszEntity + ( sizeof( "\r\n\r\n" ) - 1 );
  1577. DataChunk.FromMemory.BufferLength = strlen( (LPSTR) DataChunk.FromMemory.pBuffer );
  1578. hr = InsertDataChunk( &DataChunk, 0 );
  1579. if ( FAILED( hr ) )
  1580. {
  1581. return hr;
  1582. }
  1583. }
  1584. }
  1585. return NO_ERROR;
  1586. }
  1587. HRESULT
  1588. W3_RESPONSE::BuildStatusFromIsapi(
  1589. CHAR * pszStatus
  1590. )
  1591. /*++
  1592. Routine Description:
  1593. Build up status for response given raw status line from ISAPI
  1594. Arguments:
  1595. pszStatus - Status line for response
  1596. Return Value:
  1597. HRESULT
  1598. --*/
  1599. {
  1600. USHORT status;
  1601. STACK_STRA( strReason, 32 );
  1602. CHAR * pszCursor = NULL;
  1603. HRESULT hr = NO_ERROR;
  1604. DBG_ASSERT( pszStatus != NULL );
  1605. status = (USHORT) atoi( pszStatus );
  1606. if ( status >= 100 &&
  1607. status <= 999 )
  1608. {
  1609. //
  1610. // Need to find the reason string
  1611. //
  1612. pszCursor = pszStatus;
  1613. while ( isdigit( *pszCursor ) )
  1614. {
  1615. pszCursor++;
  1616. }
  1617. if ( *pszCursor == ' ' )
  1618. {
  1619. hr = strReason.Copy( pszCursor + 1 );
  1620. if ( FAILED( hr ) )
  1621. {
  1622. return hr;
  1623. }
  1624. }
  1625. hr = SetStatus( (USHORT)status, strReason );
  1626. if ( FAILED( hr ) )
  1627. {
  1628. return hr;
  1629. }
  1630. }
  1631. return NO_ERROR;
  1632. }
  1633. HRESULT
  1634. W3_RESPONSE::FilterWriteClient(
  1635. W3_CONTEXT * pW3Context,
  1636. PVOID pvData,
  1637. DWORD cbData
  1638. )
  1639. /*++
  1640. Routine Description:
  1641. A non-intrusive WriteClient() for use with filters. Non-intrusive means
  1642. the current response structure (chunks/headers) is not reset/effected
  1643. by sending this data (think of a WriteClient() done in a SEND_RESPONSE
  1644. filter notification)
  1645. Arguments:
  1646. pW3Context - W3 Context used to help build core response header
  1647. pvData - Pointer to data sent
  1648. cbData - Size of data to send
  1649. Return Value:
  1650. HRESULT
  1651. --*/
  1652. {
  1653. HTTP_DATA_CHUNK dataChunk;
  1654. DWORD cbSent;
  1655. HTTP_FILTER_RAW_DATA rawStream;
  1656. BOOL fRet;
  1657. BOOL fFinished = FALSE;
  1658. DWORD dwFlags = HTTP_SEND_RESPONSE_FLAG_MORE_DATA |
  1659. HTTP_SEND_RESPONSE_FLAG_RAW_HEADER;
  1660. dataChunk.DataChunkType = HttpDataChunkFromMemory;
  1661. dataChunk.FromMemory.pBuffer = pvData;
  1662. dataChunk.FromMemory.BufferLength = cbData;
  1663. _fResponseTouched = TRUE;
  1664. _fResponseSent = TRUE;
  1665. //
  1666. // If there are send raw filters to be notified, do so now
  1667. //
  1668. if ( pW3Context->IsNotificationNeeded( SF_NOTIFY_SEND_RAW_DATA ) )
  1669. {
  1670. rawStream.pvInData = pvData;
  1671. rawStream.cbInData = cbData;
  1672. rawStream.cbInBuffer = cbData;
  1673. fRet = pW3Context->NotifyFilters( SF_NOTIFY_SEND_RAW_DATA,
  1674. &rawStream,
  1675. &fFinished );
  1676. if ( !fRet )
  1677. {
  1678. return HRESULT_FROM_WIN32( GetLastError() );
  1679. }
  1680. if ( fFinished )
  1681. {
  1682. rawStream.cbInData = 0;
  1683. rawStream.cbInBuffer = 0;
  1684. dwFlags = HTTP_SEND_RESPONSE_FLAG_DISCONNECT |
  1685. HTTP_SEND_RESPONSE_FLAG_RAW_HEADER;
  1686. }
  1687. dataChunk.FromMemory.pBuffer = rawStream.pvInData;
  1688. dataChunk.FromMemory.BufferLength = rawStream.cbInData;
  1689. }
  1690. return UlAtqSendEntityBody( pW3Context->QueryUlatqContext(),
  1691. FALSE, // sync
  1692. dwFlags,
  1693. 1,
  1694. &dataChunk,
  1695. &cbSent,
  1696. NULL );
  1697. }
  1698. HRESULT
  1699. W3_RESPONSE::BuildResponseFromIsapi(
  1700. W3_CONTEXT * pW3Context,
  1701. LPSTR pszStatusStream,
  1702. LPSTR pszHeaderStream,
  1703. DWORD cchHeaderStream
  1704. )
  1705. /*++
  1706. Routine Description:
  1707. Shift this response into raw mode since we want to hold onto the
  1708. streams from ISAPI and use them for the response if possible
  1709. Arguments:
  1710. pW3Context - W3 Context used to help build core response header
  1711. (can be NULL)
  1712. pszStatusStream - Status stream
  1713. pszHeaderStream - Header stream
  1714. cchHeaderStream - Size of above
  1715. Return Value:
  1716. HRESULT
  1717. --*/
  1718. {
  1719. HRESULT hr = NO_ERROR;
  1720. CHAR * pszEndOfHeaders = NULL;
  1721. CHAR * pszCursor;
  1722. CHAR * pszRawAdditionalIsapiHeaders = NULL;
  1723. DWORD cchRawAdditionalIsapiHeaders = 0;
  1724. CHAR * pszRawAdditionalIsapiEntity = NULL;
  1725. DWORD cchRawAdditionalIsapiEntity = 0;
  1726. HTTP_DATA_CHUNK DataChunk;
  1727. _fResponseTouched = TRUE;
  1728. //
  1729. // First parse the status line. We do this before switching into raw
  1730. // mode because we want the _strRawCoreHeader string to contain the
  1731. // correct status line and reason
  1732. //
  1733. if ( pszStatusStream != NULL &&
  1734. *pszStatusStream != '\0' )
  1735. {
  1736. hr = BuildStatusFromIsapi( pszStatusStream );
  1737. if ( FAILED( hr ) )
  1738. {
  1739. return hr;
  1740. }
  1741. }
  1742. //
  1743. // If there is no ISAPI header stream set, then we're done
  1744. //
  1745. if ( pszHeaderStream == NULL )
  1746. {
  1747. return NO_ERROR;
  1748. }
  1749. //
  1750. // Create automatic headers if necessary (but no content-length)
  1751. //
  1752. hr = GenerateAutomaticHeaders( pW3Context,
  1753. HTTP_SEND_RESPONSE_FLAG_MORE_DATA );
  1754. if ( FAILED( hr ) )
  1755. {
  1756. return hr;
  1757. }
  1758. //
  1759. // The ISAPI set some headers. Store them now
  1760. //
  1761. pszRawAdditionalIsapiHeaders = pszHeaderStream;
  1762. cchRawAdditionalIsapiHeaders = cchHeaderStream;
  1763. //
  1764. // If there is additional entity body (after ISAPI headers), then add it
  1765. // Look for a complete set of additional headers. Complete means that
  1766. // we can find a "\r\n\r\n".
  1767. //
  1768. pszEndOfHeaders = strstr( pszHeaderStream, "\r\n\r\n" );
  1769. if ( pszEndOfHeaders != NULL )
  1770. {
  1771. pszEndOfHeaders += 4; // go past the \r\n\r\n
  1772. //
  1773. // Update the header length since there is entity tacked on
  1774. //
  1775. cchRawAdditionalIsapiHeaders = DIFF( pszEndOfHeaders - pszHeaderStream );
  1776. if ( *pszEndOfHeaders != '\0' )
  1777. {
  1778. pszRawAdditionalIsapiEntity = pszEndOfHeaders;
  1779. cchRawAdditionalIsapiEntity = cchHeaderStream - cchRawAdditionalIsapiHeaders;
  1780. }
  1781. }
  1782. else
  1783. {
  1784. //
  1785. // ISAPI didn't complete the headers. That means the ISAPI will
  1786. // be completing the headers later. What this means for us is we
  1787. // must send the headers in the raw form with out adding our own
  1788. // \r\n\r\n
  1789. //
  1790. _fIncompleteHeaders = TRUE;
  1791. }
  1792. //
  1793. // Switch into raw mode if we're not already in it
  1794. //
  1795. if ( _responseMode == RESPONSE_MODE_PARSED )
  1796. {
  1797. hr = SwitchToRawMode( pW3Context,
  1798. pszRawAdditionalIsapiHeaders,
  1799. cchRawAdditionalIsapiHeaders );
  1800. if ( FAILED( hr ) )
  1801. {
  1802. return hr;
  1803. }
  1804. }
  1805. DBG_ASSERT( _responseMode == RESPONSE_MODE_RAW );
  1806. //
  1807. // Now add the additional ISAPI entity
  1808. //
  1809. if ( cchRawAdditionalIsapiEntity != 0 )
  1810. {
  1811. DataChunk.DataChunkType = HttpDataChunkFromMemory;
  1812. DataChunk.FromMemory.pBuffer = pszRawAdditionalIsapiEntity;
  1813. DataChunk.FromMemory.BufferLength = cchRawAdditionalIsapiEntity;
  1814. hr = InsertDataChunk( &DataChunk, -1 );
  1815. if ( FAILED( hr ) )
  1816. {
  1817. return hr;
  1818. }
  1819. }
  1820. return NO_ERROR;
  1821. }
  1822. HRESULT
  1823. W3_RESPONSE::GetRawResponseStream(
  1824. STRA * pstrResponseStream
  1825. )
  1826. /*++
  1827. Routine Description:
  1828. Fill in the raw response stream for use by raw data filter code
  1829. Arguments:
  1830. pstrResponseStream - Filled with response stream
  1831. Return Value:
  1832. HRESULT
  1833. --*/
  1834. {
  1835. HRESULT hr;
  1836. DWORD i;
  1837. CHAR * pszChunk;
  1838. HTTP_DATA_CHUNK * pChunks;
  1839. if ( pstrResponseStream == NULL )
  1840. {
  1841. DBG_ASSERT( FALSE );
  1842. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1843. }
  1844. DBG_ASSERT( _responseMode == RESPONSE_MODE_RAW );
  1845. pChunks = QueryChunks();
  1846. for ( i = 0;
  1847. i < _cFirstEntityChunk;
  1848. i++ )
  1849. {
  1850. DBG_ASSERT( pChunks[ i ].DataChunkType == HttpDataChunkFromMemory );
  1851. pszChunk = (CHAR*) pChunks[ i ].FromMemory.pBuffer;
  1852. DBG_ASSERT( pszChunk != NULL );
  1853. hr = pstrResponseStream->Append( pszChunk );
  1854. if ( FAILED( hr ) )
  1855. {
  1856. return hr;
  1857. }
  1858. }
  1859. return NO_ERROR;
  1860. }
  1861. VOID
  1862. W3_RESPONSE::Reset(
  1863. VOID
  1864. )
  1865. /*++
  1866. Routine Description:
  1867. Initialization of a W3_RESPONSE
  1868. Arguments:
  1869. None
  1870. Return Value:
  1871. None
  1872. --*/
  1873. {
  1874. _ulHttpResponse.Flags = 0;
  1875. Clear();
  1876. //
  1877. // Set status to 200 (default)
  1878. //
  1879. SetStatus( HttpStatusOk );
  1880. //
  1881. // Keep track of whether the response has been touched (augmented). This
  1882. // is useful when determining whether an response was intended
  1883. //
  1884. _fResponseTouched = FALSE;
  1885. //
  1886. // This response hasn't been sent yet
  1887. //
  1888. _fResponseSent = FALSE;
  1889. }
  1890. HRESULT
  1891. W3_RESPONSE::InsertDataChunk(
  1892. HTTP_DATA_CHUNK * pNewChunk,
  1893. LONG cPosition
  1894. )
  1895. /*++
  1896. Routine Description:
  1897. Insert given data chunk into list of chunks. The position is determined
  1898. by cPosition. If a chunk occupies the given spot, it (along with all
  1899. remaining) are shifted forward.
  1900. Arguments:
  1901. pNewChunk - Chunk to insert
  1902. cPosition - Position of new chunk (0 prepends, -1 appends)
  1903. Return Value:
  1904. HRESULT
  1905. --*/
  1906. {
  1907. HTTP_DATA_CHUNK * pChunks = NULL;
  1908. DWORD cOriginalChunkCount;
  1909. if ( pNewChunk == NULL )
  1910. {
  1911. DBG_ASSERT( FALSE );
  1912. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1913. }
  1914. //
  1915. // Must be real position or -1
  1916. //
  1917. DBG_ASSERT( cPosition >= -1 );
  1918. //
  1919. // Allocate the new chunk if needed
  1920. //
  1921. cOriginalChunkCount = _cChunks;
  1922. _cChunks += 1;
  1923. if ( !_bufChunks.Resize( _cChunks * sizeof( HTTP_DATA_CHUNK ),
  1924. 512 ) )
  1925. {
  1926. return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  1927. }
  1928. pChunks = QueryChunks();
  1929. //
  1930. // If we're appending then this is simple. Otherwise we must shift
  1931. //
  1932. if ( cPosition == -1 )
  1933. {
  1934. memcpy( pChunks + cOriginalChunkCount,
  1935. pNewChunk,
  1936. sizeof( HTTP_DATA_CHUNK ) );
  1937. }
  1938. else
  1939. {
  1940. if ( cOriginalChunkCount > cPosition )
  1941. {
  1942. memmove( pChunks + cPosition + 1,
  1943. pChunks + cPosition,
  1944. sizeof( HTTP_DATA_CHUNK ) * ( cOriginalChunkCount - cPosition ) );
  1945. }
  1946. memcpy( pChunks + cPosition,
  1947. pNewChunk,
  1948. sizeof( HTTP_DATA_CHUNK ) );
  1949. }
  1950. return NO_ERROR;
  1951. }
  1952. HRESULT
  1953. W3_RESPONSE::ParseHeadersFromStream(
  1954. CHAR * pszStream
  1955. )
  1956. /*++
  1957. Routine Description:
  1958. Parse raw headers from ISAPI into the HTTP_RESPONSE
  1959. Arguments:
  1960. pszStream - Stream of headers in form (Header: Value\r\nHeader2: value2\r\n)
  1961. Return Value:
  1962. HRESULT
  1963. --*/
  1964. {
  1965. CHAR * pszCursor;
  1966. CHAR * pszEnd;
  1967. CHAR * pszColon;
  1968. HRESULT hr = NO_ERROR;
  1969. STACK_STRA( strHeaderLine, 128 );
  1970. STACK_STRA( strHeaderName, 32 );
  1971. STACK_STRA( strHeaderValue, 64 );
  1972. if ( pszStream == NULL )
  1973. {
  1974. DBG_ASSERT( FALSE );
  1975. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1976. }
  1977. //
  1978. // \r\n delimited
  1979. //
  1980. pszCursor = pszStream;
  1981. while ( pszCursor != NULL && *pszCursor != '\0' )
  1982. {
  1983. //
  1984. // Check to see if pszCursor points to the "\r\n"
  1985. // that separates the headers from the entity body
  1986. // of the response and add a memory chunk for any
  1987. // data that exists after it.
  1988. //
  1989. // This is to support ISAPI's that do something like
  1990. // SEND_RESPONSE_HEADER with "head1: value1\r\n\r\nEntity"
  1991. //
  1992. if ( *pszCursor == '\r' && *(pszCursor + 1) == '\n' )
  1993. {
  1994. break;
  1995. }
  1996. pszEnd = strstr( pszCursor, "\r\n" );
  1997. if ( pszEnd == NULL )
  1998. {
  1999. break;
  2000. }
  2001. //
  2002. // Split out a line and convert to unicode
  2003. //
  2004. hr = strHeaderLine.Copy( pszCursor,
  2005. DIFF(pszEnd - pszCursor) );
  2006. if ( FAILED( hr ) )
  2007. {
  2008. goto Finished;
  2009. }
  2010. //
  2011. // Advance the cursor the right after the \r\n
  2012. //
  2013. pszCursor = pszEnd + 2;
  2014. //
  2015. // Split the line above into header:value
  2016. //
  2017. pszColon = strchr( strHeaderLine.QueryStr(), ':' );
  2018. if ( pszColon == NULL )
  2019. {
  2020. continue;
  2021. }
  2022. else
  2023. {
  2024. if ( pszColon == strHeaderLine.QueryStr() )
  2025. {
  2026. strHeaderName.Reset();
  2027. }
  2028. else
  2029. {
  2030. hr = strHeaderName.Copy( strHeaderLine.QueryStr(),
  2031. DIFF(pszColon - strHeaderLine.QueryStr()) );
  2032. }
  2033. }
  2034. if ( FAILED( hr ) )
  2035. {
  2036. goto Finished;
  2037. }
  2038. //
  2039. // Skip the first space after the : if there is one
  2040. //
  2041. if ( pszColon[ 1 ] == ' ' )
  2042. {
  2043. pszColon++;
  2044. }
  2045. hr = strHeaderValue.Copy( pszColon + 1 );
  2046. if ( FAILED( hr ) )
  2047. {
  2048. goto Finished;
  2049. }
  2050. //
  2051. // Add the header to the response
  2052. //
  2053. hr = SetHeader( strHeaderName.QueryStr(),
  2054. strHeaderName.QueryCCH(),
  2055. strHeaderValue.QueryStr(),
  2056. strHeaderValue.QueryCCH(),
  2057. FALSE,
  2058. TRUE );
  2059. if ( FAILED( hr ) )
  2060. {
  2061. goto Finished;
  2062. }
  2063. }
  2064. Finished:
  2065. if ( FAILED( hr ) )
  2066. {
  2067. //
  2068. // Don't allow the response to get into a quasi-bogus-state
  2069. //
  2070. Clear();
  2071. }
  2072. return hr;
  2073. }
  2074. //static
  2075. HRESULT
  2076. W3_RESPONSE::ReadFileIntoBuffer(
  2077. HANDLE hFile,
  2078. SEND_RAW_BUFFER * pSendBuffer,
  2079. ULONGLONG cbCurrentFileOffset
  2080. )
  2081. /*++
  2082. Routine Description:
  2083. Read contents of file into buffer
  2084. Arguments:
  2085. hFile - File to read
  2086. pSendBuffer - Buffer to read into
  2087. cbCurrentFileOffset - Offset to read from
  2088. Return Value:
  2089. HRESULT
  2090. --*/
  2091. {
  2092. OVERLAPPED overlapped;
  2093. LARGE_INTEGER liOffset;
  2094. BOOL fRet;
  2095. DWORD cbRead;
  2096. DWORD dwError;
  2097. if ( hFile == NULL ||
  2098. pSendBuffer == NULL )
  2099. {
  2100. DBG_ASSERT( FALSE );
  2101. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  2102. }
  2103. pSendBuffer->SetLen( 0 );
  2104. liOffset.QuadPart = cbCurrentFileOffset;
  2105. //
  2106. // Setup overlapped with offset
  2107. //
  2108. ZeroMemory( &overlapped, sizeof( overlapped ) );
  2109. overlapped.Offset = liOffset.LowPart;
  2110. overlapped.OffsetHigh = liOffset.HighPart;
  2111. //
  2112. // Do the read
  2113. //
  2114. fRet = ReadFile( hFile,
  2115. pSendBuffer->QueryPtr(),
  2116. pSendBuffer->QuerySize(),
  2117. &cbRead,
  2118. &overlapped );
  2119. if ( !fRet )
  2120. {
  2121. dwError = GetLastError();
  2122. if ( dwError == ERROR_IO_PENDING )
  2123. {
  2124. fRet = GetOverlappedResult( hFile,
  2125. &overlapped,
  2126. &cbRead,
  2127. TRUE );
  2128. if ( !fRet )
  2129. {
  2130. dwError = GetLastError();
  2131. if ( dwError == ERROR_HANDLE_EOF )
  2132. {
  2133. fRet = TRUE;
  2134. }
  2135. }
  2136. }
  2137. else if ( dwError == ERROR_HANDLE_EOF )
  2138. {
  2139. fRet = TRUE;
  2140. }
  2141. }
  2142. //
  2143. // If there was an error, bail
  2144. //
  2145. if ( !fRet )
  2146. {
  2147. return HRESULT_FROM_WIN32( dwError );
  2148. }
  2149. else
  2150. {
  2151. pSendBuffer->SetLen( cbRead );
  2152. return NO_ERROR;
  2153. }
  2154. }
  2155. HRESULT
  2156. W3_RESPONSE::ProcessRawChunks(
  2157. W3_CONTEXT * pW3Context,
  2158. BOOL * pfFinished
  2159. )
  2160. /*++
  2161. Routine Description:
  2162. Iterate thru chunks, serializing and filtering as needed
  2163. Arguments:
  2164. pW3Context - Context used to filter with
  2165. pfFinished - Set to TRUE if filter wanted to finish
  2166. Return Value:
  2167. HRESULT
  2168. --*/
  2169. {
  2170. DWORD cCurrentChunk = 0;
  2171. DWORD cCurrentInsertPos = 0;
  2172. DWORD cFileChunkCount = 0;
  2173. HTTP_DATA_CHUNK * pChunk;
  2174. HTTP_DATA_CHUNK DataChunk;
  2175. HTTP_FILTER_RAW_DATA origStream;
  2176. HTTP_FILTER_RAW_DATA currStream;
  2177. HRESULT hr = NO_ERROR;
  2178. BOOL fRet;
  2179. LONGLONG cbCurrentFileOffset;
  2180. LONGLONG cbTarget;
  2181. SEND_RAW_BUFFER * pSendBuffer = NULL;
  2182. HANDLE hFile;
  2183. DBG_ASSERT( pfFinished != NULL );
  2184. DBG_ASSERT( pW3Context != NULL );
  2185. *pfFinished = FALSE;
  2186. //
  2187. // Start iterating thru chunks
  2188. //
  2189. for ( cCurrentChunk = 0;
  2190. cCurrentChunk < _cChunks;
  2191. cCurrentChunk++ )
  2192. {
  2193. pChunk = &(QueryChunks()[ cCurrentChunk ]);
  2194. //
  2195. // First remember this chunk incase filter changed it
  2196. //
  2197. //
  2198. // If memory chunk, this is easy. Otherwise we need to read the file
  2199. // handle and build up memory chunks
  2200. //
  2201. if ( pChunk->DataChunkType == HttpDataChunkFromMemory )
  2202. {
  2203. origStream.pvInData = pChunk->FromMemory.pBuffer;
  2204. origStream.cbInData = pChunk->FromMemory.BufferLength;
  2205. origStream.cbInBuffer = pChunk->FromMemory.BufferLength;
  2206. memcpy( &currStream, &origStream, sizeof( currStream ) );
  2207. fRet = pW3Context->NotifyFilters( SF_NOTIFY_SEND_RAW_DATA,
  2208. &currStream,
  2209. pfFinished );
  2210. if ( !fRet )
  2211. {
  2212. hr = HRESULT_FROM_WIN32( GetLastError() );
  2213. break;
  2214. }
  2215. if ( *pfFinished )
  2216. {
  2217. goto Finished;
  2218. }
  2219. if ( currStream.pvInData == origStream.pvInData )
  2220. {
  2221. //
  2222. // Just adjust the chunk
  2223. //
  2224. pChunk->FromMemory.BufferLength = currStream.cbInData;
  2225. }
  2226. else
  2227. {
  2228. //
  2229. // Allocate new buffer and tweak chunk to refer to it
  2230. //
  2231. pSendBuffer = new SEND_RAW_BUFFER;
  2232. if ( pSendBuffer == NULL )
  2233. {
  2234. hr = HRESULT_FROM_WIN32( GetLastError() );
  2235. break;
  2236. }
  2237. hr = pSendBuffer->Resize( currStream.cbInData );
  2238. if ( FAILED( hr ) )
  2239. {
  2240. break;
  2241. }
  2242. memcpy( pSendBuffer->QueryPtr(),
  2243. currStream.pvInData,
  2244. currStream.cbInData );
  2245. pChunk->FromMemory.BufferLength = currStream.cbInData;
  2246. pChunk->FromMemory.pBuffer = pSendBuffer->QueryPtr();
  2247. InsertHeadList( &_SendRawBufferHead,
  2248. &(pSendBuffer->_listEntry) );
  2249. pSendBuffer = NULL;
  2250. }
  2251. }
  2252. else if ( pChunk->DataChunkType == HttpDataChunkFromFileHandle )
  2253. {
  2254. //
  2255. // Reset file offsets
  2256. //
  2257. cbCurrentFileOffset = 0;
  2258. //
  2259. // How many bytes are we reading?
  2260. //
  2261. cbTarget = pChunk->FromFileHandle.ByteRange.Length.QuadPart;
  2262. //
  2263. // Remember where to start inserting memory chunks
  2264. //
  2265. cCurrentInsertPos = cCurrentChunk;
  2266. //
  2267. // First memory replacement chunk can use file handle chunk
  2268. //
  2269. cFileChunkCount = 0;
  2270. //
  2271. // Remember the handle now since we will be overwriting this
  2272. // chunk with a memory chunk
  2273. //
  2274. hFile = pChunk->FromFileHandle.FileHandle;
  2275. while ( cbCurrentFileOffset < cbTarget )
  2276. {
  2277. //
  2278. // Allocate a buffer
  2279. //
  2280. pSendBuffer = new SEND_RAW_BUFFER;
  2281. if ( pSendBuffer == NULL )
  2282. {
  2283. hr = HRESULT_FROM_WIN32( GetLastError() );
  2284. break;
  2285. }
  2286. //
  2287. // Read some of the file into buffer
  2288. //
  2289. hr = ReadFileIntoBuffer( hFile,
  2290. pSendBuffer,
  2291. cbCurrentFileOffset );
  2292. if ( FAILED( hr ) )
  2293. {
  2294. break;
  2295. }
  2296. //
  2297. // Update offset
  2298. //
  2299. cbCurrentFileOffset += pSendBuffer->QueryCB();
  2300. //
  2301. // Setup memory chunk to filter
  2302. //
  2303. origStream.pvInData = pSendBuffer->QueryPtr();
  2304. origStream.cbInBuffer = pSendBuffer->QuerySize();
  2305. origStream.cbInData = pSendBuffer->QueryCB();
  2306. memcpy( &currStream, &origStream, sizeof( currStream ) );
  2307. //
  2308. // Filter the data
  2309. //
  2310. fRet = pW3Context->NotifyFilters( SF_NOTIFY_SEND_RAW_DATA,
  2311. &currStream,
  2312. pfFinished );
  2313. if ( !fRet )
  2314. {
  2315. hr = HRESULT_FROM_WIN32( GetLastError() );
  2316. break;
  2317. }
  2318. if ( *pfFinished )
  2319. {
  2320. goto Finished;
  2321. }
  2322. //
  2323. // Should we keep the file content chunk
  2324. //
  2325. if ( currStream.pvInData != origStream.pvInData )
  2326. {
  2327. //
  2328. // We have a new memory address containing data
  2329. //
  2330. if ( currStream.cbInData > pSendBuffer->QuerySize() )
  2331. {
  2332. hr = pSendBuffer->Resize( currStream.cbInData );
  2333. if ( FAILED( hr ) )
  2334. {
  2335. break;
  2336. }
  2337. }
  2338. memcpy( pSendBuffer->QueryPtr(),
  2339. currStream.pvInData,
  2340. currStream.cbInData );
  2341. }
  2342. //
  2343. // Add the chunk. If the first chunk, we can replace
  2344. // the original file handle chunk
  2345. //
  2346. if ( cFileChunkCount == 0 )
  2347. {
  2348. pChunk->DataChunkType = HttpDataChunkFromMemory;
  2349. pChunk->FromMemory.pBuffer = pSendBuffer->QueryPtr();
  2350. pChunk->FromMemory.BufferLength = currStream.cbInData;
  2351. cCurrentInsertPos++;
  2352. }
  2353. else
  2354. {
  2355. DataChunk.DataChunkType = HttpDataChunkFromMemory;
  2356. DataChunk.FromMemory.pBuffer = pSendBuffer->QueryPtr();
  2357. DataChunk.FromMemory.BufferLength = currStream.cbInData;
  2358. hr = InsertDataChunk( &DataChunk,
  2359. cCurrentInsertPos++ );
  2360. if ( FAILED( hr ) )
  2361. {
  2362. break;
  2363. }
  2364. }
  2365. InsertHeadList( &_SendRawBufferHead,
  2366. &(pSendBuffer->_listEntry) );
  2367. pSendBuffer = NULL;
  2368. cFileChunkCount++;
  2369. }
  2370. //
  2371. // If we're here because of failure, bail
  2372. //
  2373. if ( FAILED( hr ) )
  2374. {
  2375. break;
  2376. }
  2377. //
  2378. // Update current chunk to be processed
  2379. //
  2380. cCurrentChunk = cCurrentInsertPos;
  2381. }
  2382. else
  2383. {
  2384. //
  2385. // Only support file-handle and memory chunks
  2386. //
  2387. DBG_ASSERT( FALSE );
  2388. }
  2389. }
  2390. Finished:
  2391. if ( pSendBuffer != NULL )
  2392. {
  2393. delete pSendBuffer;
  2394. }
  2395. return hr;
  2396. }
  2397. //static
  2398. HRESULT
  2399. W3_RESPONSE::Initialize(
  2400. VOID
  2401. )
  2402. /*++
  2403. Routine Description:
  2404. Enable W3_RESPONSE globals
  2405. Arguments:
  2406. None
  2407. Return Value:
  2408. HRESULT
  2409. --*/
  2410. {
  2411. HRESULT hr;
  2412. hr = RESPONSE_HEADER_HASH::Initialize();
  2413. if ( FAILED( hr ) )
  2414. {
  2415. return hr;
  2416. }
  2417. hr = SEND_RAW_BUFFER::Initialize();
  2418. if ( FAILED( hr ) )
  2419. {
  2420. RESPONSE_HEADER_HASH::Terminate();
  2421. }
  2422. return hr;
  2423. }
  2424. //static
  2425. VOID
  2426. W3_RESPONSE::Terminate(
  2427. VOID
  2428. )
  2429. {
  2430. SEND_RAW_BUFFER::Terminate();
  2431. RESPONSE_HEADER_HASH::Terminate();
  2432. }
  2433. CONTEXT_STATUS
  2434. W3_STATE_RESPONSE::DoWork(
  2435. W3_MAIN_CONTEXT * pMainContext,
  2436. DWORD cbCompletion,
  2437. DWORD dwCompletionStatus
  2438. )
  2439. /*++
  2440. Routine Description:
  2441. This state is responsible for ensuring that a response does get sent
  2442. back to the client. We hope/expect that the handlers will do their
  2443. thing -> but if they don't we will catch that here and send a response
  2444. Arguments:
  2445. pMainContext - Context
  2446. cbCompletion - Number of bytes in an async completion
  2447. dwCompletionStatus - Error status of a completion
  2448. Return Value:
  2449. CONTEXT_STATUS_PENDING or CONTEXT_STATUS_CONTINUE
  2450. --*/
  2451. {
  2452. W3_RESPONSE* pResponse;
  2453. DWORD dwOldState;
  2454. HRESULT hr;
  2455. pResponse = pMainContext->QueryResponse();
  2456. DBG_ASSERT( pResponse != NULL );
  2457. //
  2458. // Has a response been sent? If not, bail
  2459. //
  2460. if ( pMainContext->QueryResponseSent() )
  2461. {
  2462. return CONTEXT_STATUS_CONTINUE;
  2463. }
  2464. //
  2465. // If the response has been touched, then just send that response. Else
  2466. // send a 500 error
  2467. //
  2468. if ( !pResponse->QueryResponseTouched() )
  2469. {
  2470. pResponse->SetStatus( HttpStatusServerError );
  2471. }
  2472. //
  2473. // Send it out
  2474. //
  2475. hr = pMainContext->SendResponse( W3_FLAG_ASYNC );
  2476. if ( FAILED( hr ) )
  2477. {
  2478. pMainContext->SetErrorStatus( hr );
  2479. return CONTEXT_STATUS_CONTINUE;
  2480. }
  2481. else
  2482. {
  2483. return CONTEXT_STATUS_PENDING;
  2484. }
  2485. }
  2486. CONTEXT_STATUS
  2487. W3_STATE_RESPONSE::OnCompletion(
  2488. W3_MAIN_CONTEXT * pW3Context,
  2489. DWORD cbCompletion,
  2490. DWORD dwCompletionStatus
  2491. )
  2492. /*++
  2493. Routine Description:
  2494. Subsequent completions in this state
  2495. Arguments:
  2496. pW3Context - Context
  2497. cbCompletion - Number of bytes in an async completion
  2498. dwCompletionStatus - Error status of a completion
  2499. Return Value:
  2500. CONTEXT_STATUS_PENDING or CONTEXT_STATUS_CONTINUE
  2501. --*/
  2502. {
  2503. //
  2504. // We received an IO completion. Just advance since we have nothing to
  2505. // cleanup
  2506. //
  2507. return CONTEXT_STATUS_CONTINUE;
  2508. }
  2509. //static
  2510. HRESULT
  2511. SEND_RAW_BUFFER::Initialize(
  2512. VOID
  2513. )
  2514. /*++
  2515. Routine Description:
  2516. Global initialization routine for SEND_RAW_BUFFERs
  2517. Arguments:
  2518. None
  2519. Return Value:
  2520. HRESULT
  2521. --*/
  2522. {
  2523. ALLOC_CACHE_CONFIGURATION acConfig;
  2524. HRESULT hr = NO_ERROR;
  2525. //
  2526. // Setup allocation lookaside
  2527. //
  2528. acConfig.nConcurrency = 1;
  2529. acConfig.nThreshold = 100;
  2530. acConfig.cbSize = sizeof( SEND_RAW_BUFFER );
  2531. DBG_ASSERT( sm_pachSendRawBuffers == NULL );
  2532. sm_pachSendRawBuffers = new ALLOC_CACHE_HANDLER( "SEND_RAW_BUFFER",
  2533. &acConfig );
  2534. if ( sm_pachSendRawBuffers == NULL )
  2535. {
  2536. return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  2537. }
  2538. return NO_ERROR;
  2539. }
  2540. //static
  2541. VOID
  2542. SEND_RAW_BUFFER::Terminate(
  2543. VOID
  2544. )
  2545. /*++
  2546. Routine Description:
  2547. Terminate MAIN_CONTEXT globals
  2548. Arguments:
  2549. None
  2550. Return Value:
  2551. None
  2552. --*/
  2553. {
  2554. if ( sm_pachSendRawBuffers != NULL )
  2555. {
  2556. delete sm_pachSendRawBuffers;
  2557. sm_pachSendRawBuffers = NULL;
  2558. }
  2559. }