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.

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