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.

609 lines
20 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. chunkflt.cxx
  5. Abstract:
  6. Contains a filter for encoding and decoding chunked transfers.
  7. Contents:
  8. FILTER_LIST::Insert
  9. FILTER_LIST::RemoveAll
  10. FILTER_LIST::Decode
  11. ChunkFilter::Reset
  12. ChunkFilter::Decode
  13. ChunkFilter::Encode
  14. ChunkFilter::RegisterContext
  15. ChunkFilter::UnregisterContext
  16. Revision History:
  17. Created 13-Feb-2001
  18. --*/
  19. #include <wininetp.h>
  20. // Global lookup table to map 0x0 - 0x7f bytes for obtaining mapping to its
  21. // token value. All values above 0x7f are considered to be data.
  22. const BYTE g_bChunkTokenTable[] =
  23. {
  24. /* 0x00 */
  25. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  26. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  27. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_LF, CHUNK_TOKEN_DATA,
  28. CHUNK_TOKEN_DATA, CHUNK_TOKEN_CR, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  29. /* 0x10 */
  30. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  31. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  32. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  33. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  34. /* 0x20 */
  35. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  36. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  37. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  38. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  39. /* 0x30 */
  40. CHUNK_TOKEN_DIGIT, CHUNK_TOKEN_DIGIT, CHUNK_TOKEN_DIGIT, CHUNK_TOKEN_DIGIT,
  41. CHUNK_TOKEN_DIGIT, CHUNK_TOKEN_DIGIT, CHUNK_TOKEN_DIGIT, CHUNK_TOKEN_DIGIT,
  42. CHUNK_TOKEN_DIGIT, CHUNK_TOKEN_DIGIT, CHUNK_TOKEN_COLON, CHUNK_TOKEN_DATA,
  43. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  44. /* 0x40 */
  45. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DIGIT, CHUNK_TOKEN_DIGIT, CHUNK_TOKEN_DIGIT,
  46. CHUNK_TOKEN_DIGIT, CHUNK_TOKEN_DIGIT, CHUNK_TOKEN_DIGIT, CHUNK_TOKEN_DATA,
  47. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  48. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  49. /* 0x50 */
  50. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  51. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  52. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  53. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  54. /* 0x60 */
  55. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DIGIT, CHUNK_TOKEN_DIGIT, CHUNK_TOKEN_DIGIT,
  56. CHUNK_TOKEN_DIGIT, CHUNK_TOKEN_DIGIT, CHUNK_TOKEN_DIGIT, CHUNK_TOKEN_DATA,
  57. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  58. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  59. /* 0x70 */
  60. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  61. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  62. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA,
  63. CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA, CHUNK_TOKEN_DATA
  64. };
  65. // Look-up table to map a token in a given state to the next state
  66. const CHUNK_DECODE_STATE g_eMapChunkTokenToNextState[CHUNK_DECODE_STATE_LAST+1][CHUNK_TOKEN_LAST+1] =
  67. {
  68. /*
  69. |---------DIGIT----------|-------------CR-------------|-------------LF------------|----------COLON----------|-----------DATA----------|
  70. */
  71. // CHUNK_DECODE_STATE_START
  72. CHUNK_DECODE_STATE_SIZE, CHUNK_DECODE_STATE_SIZE, CHUNK_DECODE_STATE_SIZE, CHUNK_DECODE_STATE_SIZE, CHUNK_DECODE_STATE_SIZE,
  73. // CHUNK_DECODE_STATE_SIZE
  74. CHUNK_DECODE_STATE_SIZE, CHUNK_DECODE_STATE_SIZE_CRLF,CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_EXT, CHUNK_DECODE_STATE_EXT,
  75. // CHUNK_DECODE_STATE_SIZE_CRLF
  76. CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_SIZE_CRLF,CHUNK_DECODE_STATE_DATA, CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_ERROR,
  77. // CHUNK_DECODE_STATE_EXT
  78. CHUNK_DECODE_STATE_EXT, CHUNK_DECODE_STATE_SIZE_CRLF,CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_EXT, CHUNK_DECODE_STATE_EXT,
  79. // CHUNK_DECODE_STATE_DATA
  80. CHUNK_DECODE_STATE_SIZE, CHUNK_DECODE_STATE_DATA_CRLF,CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_DATA, CHUNK_DECODE_STATE_ERROR,
  81. // CHUNK_DECODE_STATE_DATA_CRLF
  82. CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_DATA_CRLF,CHUNK_DECODE_STATE_SIZE, CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_ERROR,
  83. // CHUNK_DECODE_STATE_FOOTER_NAME
  84. CHUNK_DECODE_STATE_FOOTER_NAME, CHUNK_DECODE_STATE_FINAL_CRLF, CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_FOOTER_VALUE, CHUNK_DECODE_STATE_FOOTER_NAME,
  85. // CHUNK_DECODE_STATE_FOOTER_VALUE
  86. CHUNK_DECODE_STATE_FOOTER_VALUE, CHUNK_DECODE_STATE_FINAL_CRLF, CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_FOOTER_VALUE,
  87. // CHUNK_DECODE_STATE_FINAL_CRLF
  88. CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_FINAL_CRLF, CHUNK_DECODE_STATE_FINISHED, CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_ERROR,
  89. // CHUNK_DECODE_STATE_ERROR
  90. CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_ERROR,
  91. // CHUNK_DECODE_STATE_FINISHED -- force client to reset before reuse
  92. CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_ERROR, CHUNK_DECODE_STATE_ERROR
  93. };
  94. // Helper macros
  95. // Where to next?
  96. #define MAP_CHUNK_TOKEN_TO_NEXT_STATE(eCurState, chToken) \
  97. (g_eMapChunkTokenToNextState[(eCurState)][(chToken)])
  98. // Given a byte, what does it represent w/regards to chunked responses
  99. #define GET_CHUNK_TOKEN(ch) ((ch) & 0x80 ? CHUNK_TOKEN_DATA : g_bChunkTokenTable[ch])
  100. // Should only be used with digit tokens.
  101. // Expects byte in range 0x30-0x39 (digits), 0x41-0x46 (uppercase hex),
  102. // or 0x61-0x66 (lowercase hex)
  103. #define GET_VALUE_FROM_ASCII_HEX(ch) ((ch) - ((ch) & 0xf0) + (((ch) & 0x40) ? 9 : 0))
  104. HRESULT ChunkFilter::Reset(DWORD_PTR dwContext)
  105. {
  106. DEBUG_ENTER((DBG_HTTP,
  107. Dword,
  108. "ChunkFilter::Reset",
  109. "%#x",
  110. dwContext
  111. ));
  112. if (dwContext)
  113. (reinterpret_cast<ChunkDecodeContext *>(dwContext))->Reset();
  114. DEBUG_LEAVE(TRUE);
  115. return S_OK;
  116. }
  117. HRESULT
  118. ChunkFilter::Decode(
  119. DWORD_PTR dwContext,
  120. IN OUT LPBYTE pInBuffer,
  121. IN DWORD dwInBufSize,
  122. IN OUT LPBYTE *ppOutBuffer,
  123. IN OUT LPDWORD pdwOutBufSize,
  124. OUT LPDWORD pdwBytesRead,
  125. OUT LPDWORD pdwBytesWritten
  126. )
  127. /*++
  128. Routine Description:
  129. Decode downloaded chunked data based on the inputted context and
  130. its current state
  131. Arguments:
  132. dwContext - registered encode/decode context for this filter
  133. pInBuffer - input data buffer to be processed
  134. dwInBufSize - byte count of pInBuffer
  135. ppOutBuffer - allocated buffer containing encoded/decoded data if
  136. not done in place with pInBuffer.
  137. pdwOutBufSize - size of allocated output buffer, or 0 if pInBuffer holds
  138. the processed data
  139. pdwBytesRead - Number of input buffer bytes used
  140. pdwBytesWritten - Number of output buffer bytes written
  141. Return Value:
  142. HRESULT
  143. Success - S_OK
  144. Failure - E_FAIL
  145. --*/
  146. {
  147. HRESULT hResult = S_OK;
  148. LPBYTE pCurrentLoc = pInBuffer;
  149. LPBYTE pStartOfChunk = pInBuffer;
  150. ChunkDecodeContext * pCtx = reinterpret_cast<ChunkDecodeContext *>(dwContext);
  151. CHUNK_DECODE_STATE ePreviousState;
  152. BYTE chToken;
  153. DEBUG_ENTER((DBG_HTTP,
  154. Dword,
  155. "ChunkFilter::Decode",
  156. "%x, [%x, %.10q], %u, %x, %u, %x, %x",
  157. dwContext,
  158. pInBuffer,
  159. *pInBuffer,
  160. dwInBufSize,
  161. ppOutBuffer,
  162. pdwOutBufSize,
  163. pdwBytesRead,
  164. pdwBytesWritten
  165. ));
  166. if (!dwContext)
  167. {
  168. hResult = E_INVALIDARG;
  169. goto quit;
  170. }
  171. else if (!pdwBytesRead || !pdwBytesWritten || !pInBuffer ||
  172. (ppOutBuffer && !pdwOutBufSize))
  173. {
  174. hResult = E_POINTER;
  175. goto quit;
  176. }
  177. *pdwBytesRead = 0;
  178. *pdwBytesWritten = 0;
  179. while (*pdwBytesRead < dwInBufSize &&
  180. pCtx->GetState() != CHUNK_DECODE_STATE_ERROR)
  181. {
  182. chToken = GET_CHUNK_TOKEN(*pCurrentLoc);
  183. ePreviousState = pCtx->GetState();
  184. DEBUG_PRINT(HTTP,
  185. INFO,
  186. ("ChunkFilter::Decode: %q, %q, %u/%u\n",
  187. InternetMapChunkState(ePreviousState),
  188. InternetMapChunkToken((CHUNK_TOKEN_VALUE)chToken),
  189. *pdwBytesRead,
  190. dwInBufSize
  191. ));
  192. INET_ASSERT(pCurrentLoc < pInBuffer + dwInBufSize);
  193. switch (pCtx->GetState())
  194. {
  195. case CHUNK_DECODE_STATE_START:
  196. pCtx->Reset();
  197. pCtx->SetState(CHUNK_DECODE_STATE_SIZE);
  198. // fall through
  199. case CHUNK_DECODE_STATE_SIZE:
  200. {
  201. if (chToken == CHUNK_TOKEN_DIGIT)
  202. {
  203. if (pCtx->m_dwParsedSize & 0xF0000000)
  204. {
  205. // Don't allow overflow if by some chance
  206. // the server is trying to send a chunk over
  207. // 4 gigs worth in size.
  208. pCtx->SetState(CHUNK_DECODE_STATE_ERROR);
  209. break;
  210. }
  211. pCtx->m_dwParsedSize <<= 4;
  212. pCtx->m_dwParsedSize += GET_VALUE_FROM_ASCII_HEX(*pCurrentLoc);
  213. }
  214. else
  215. {
  216. pCtx->SetState(MAP_CHUNK_TOKEN_TO_NEXT_STATE(
  217. CHUNK_DECODE_STATE_SIZE,
  218. chToken));
  219. }
  220. break;
  221. }
  222. case CHUNK_DECODE_STATE_SIZE_CRLF:
  223. // Handle the zero case which can take us to the footer or final CRLF
  224. // If it's the final CRLF, then this should be the end of the data.
  225. if (pCtx->m_dwParsedSize == 0 && chToken == CHUNK_TOKEN_LF)
  226. {
  227. pCtx->SetState(CHUNK_DECODE_STATE_FOOTER_NAME);
  228. }
  229. else
  230. {
  231. pCtx->SetState(MAP_CHUNK_TOKEN_TO_NEXT_STATE(
  232. CHUNK_DECODE_STATE_SIZE_CRLF,
  233. chToken));
  234. }
  235. break;
  236. case CHUNK_DECODE_STATE_DATA:
  237. {
  238. INET_ASSERT(pCtx->m_dwParsedSize);
  239. // account for EOB
  240. if (pCurrentLoc + pCtx->m_dwParsedSize < pInBuffer + dwInBufSize)
  241. {
  242. const DWORD dwParsedSize = pCtx->m_dwParsedSize;
  243. // Move or skip the parsed size and crlf, if needed.
  244. // The start of the chunk could be equal this time if
  245. // spread across multiple decode calls.
  246. if (pStartOfChunk != pCurrentLoc)
  247. {
  248. MoveMemory(pStartOfChunk,
  249. pCurrentLoc,
  250. dwParsedSize);
  251. }
  252. // -1 so we can look at the first byte after the data
  253. // in the next pass.
  254. pCurrentLoc += dwParsedSize - 1;
  255. *pdwBytesRead += dwParsedSize - 1;
  256. *pdwBytesWritten += dwParsedSize;
  257. pStartOfChunk += dwParsedSize;
  258. pCtx->m_dwParsedSize = 0;
  259. // Should be CRLF terminated
  260. pCtx->SetState(CHUNK_DECODE_STATE_DATA_CRLF);
  261. }
  262. else
  263. {
  264. const DWORD dwSlice = dwInBufSize - (DWORD)(pCurrentLoc - pInBuffer);
  265. // We're reaching the end of the buffer before
  266. // the end of the chunk. Update the parsed
  267. // size remaining, so it will be carried over
  268. // to the next call.
  269. if (pStartOfChunk != pCurrentLoc)
  270. {
  271. // Skip over preceding size info.
  272. MoveMemory(pStartOfChunk,
  273. pCurrentLoc,
  274. dwSlice);
  275. }
  276. // -1 so we can look at the first byte after the data
  277. // in the next pass. Offset should never be bigger than DWORD since
  278. // since that's the biggest chunk we can handle.
  279. *pdwBytesWritten += dwSlice;
  280. pCtx->m_dwParsedSize -= dwSlice;
  281. *pdwBytesRead += dwSlice - 1;
  282. pCurrentLoc = pInBuffer + dwInBufSize - 1;
  283. }
  284. break;
  285. }
  286. // All remaining states simply parse over the value and
  287. // change state, depending on the token.
  288. default:
  289. {
  290. pCtx->SetState(MAP_CHUNK_TOKEN_TO_NEXT_STATE(
  291. ePreviousState,
  292. chToken));
  293. break;
  294. }
  295. }
  296. (*pdwBytesRead)++;
  297. pCurrentLoc++;
  298. }
  299. if (pCtx->GetState() == CHUNK_DECODE_STATE_ERROR)
  300. {
  301. DEBUG_PRINT(HTTP,
  302. INFO,
  303. ("ChunkFilter::Decode entered error state\n"
  304. ));
  305. hResult = E_FAIL;
  306. }
  307. quit:
  308. DEBUG_LEAVE(hResult == S_OK ? TRUE : FALSE);
  309. return hResult;
  310. }
  311. HRESULT
  312. ChunkFilter::Encode(
  313. DWORD_PTR dwContext,
  314. IN OUT LPBYTE pInBuffer,
  315. IN DWORD dwInBufSize,
  316. IN OUT LPBYTE *ppOutBuffer,
  317. IN OUT LPDWORD pdwOutBufSize,
  318. OUT LPDWORD pdwBytesRead,
  319. OUT LPDWORD pdwBytesWritten
  320. )
  321. /*++
  322. Routine Description:
  323. Chunk data for uploading based on the inputted context and current state
  324. Arguments:
  325. dwContext - registered encode/decode context for this filter
  326. pInBuffer - input data buffer to be processed
  327. dwInBufSize - byte count of pInBuffer
  328. ppOutBuffer - allocated buffer containing encoded/decoded data if
  329. not done in place with pInBuffer.
  330. pdwOutBufSize - size of allocated output buffer, or 0 if pInBuffer holds
  331. the processed data
  332. pdwBytesRead - Number of input buffer bytes used
  333. pdwBytesWritten - Number of output buffer bytes written
  334. Return Value:
  335. HRESULT
  336. E_NOTIMPL - currently no chunked upload support
  337. --*/
  338. {
  339. // We don't support chunked uploads...yet
  340. return E_NOTIMPL;
  341. }
  342. HRESULT
  343. ChunkFilter::RegisterContext(OUT DWORD_PTR *pdwContext)
  344. {
  345. DEBUG_ENTER((DBG_HTTP,
  346. Dword,
  347. "ChunkFilter::RegisterContext",
  348. "%#x",
  349. pdwContext
  350. ));
  351. HRESULT hr = S_OK;
  352. if (!pdwContext || IsBadWritePtr(pdwContext, sizeof(DWORD_PTR)))
  353. {
  354. hr = E_POINTER;
  355. goto quit;
  356. }
  357. *pdwContext = (DWORD_PTR) New ChunkDecodeContext;
  358. if (!*pdwContext)
  359. {
  360. hr = E_OUTOFMEMORY;
  361. }
  362. quit:
  363. DEBUG_LEAVE(hr == S_OK ? TRUE : FALSE);
  364. return hr;
  365. }
  366. HRESULT
  367. ChunkFilter::UnregisterContext(IN DWORD_PTR dwContext)
  368. {
  369. DEBUG_ENTER((DBG_HTTP,
  370. Dword,
  371. "ChunkFilter::UnregisterContext",
  372. "%#x",
  373. dwContext
  374. ));
  375. HRESULT hr = S_OK;
  376. if (!dwContext)
  377. {
  378. hr = E_INVALIDARG;
  379. goto quit;
  380. }
  381. delete reinterpret_cast<ChunkDecodeContext *>(dwContext);
  382. quit:
  383. DEBUG_LEAVE(hr == S_OK ? TRUE : FALSE);
  384. return hr;
  385. }
  386. // Always inserts as the beginning of the list
  387. BOOL
  388. FILTER_LIST::Insert(IN BaseFilter *pFilter, IN DWORD_PTR dwContext)
  389. {
  390. LPFILTER_LIST_ENTRY pNewEntry;
  391. pNewEntry = New FILTER_LIST_ENTRY;
  392. if (pNewEntry != NULL)
  393. {
  394. pNewEntry->pFilter = pFilter;
  395. pNewEntry->dwContext = dwContext;
  396. pNewEntry->pNext = _pFilterEntry;
  397. _pFilterEntry = pNewEntry;
  398. _uFilterCount++;
  399. return TRUE;
  400. }
  401. else
  402. {
  403. return FALSE;
  404. }
  405. }
  406. VOID
  407. FILTER_LIST::ClearList()
  408. {
  409. LPFILTER_LIST_ENTRY pEntry = _pFilterEntry;
  410. while (pEntry)
  411. {
  412. pEntry->pFilter->UnregisterContext(pEntry->dwContext);
  413. pEntry = pEntry->pNext;
  414. delete _pFilterEntry;
  415. _pFilterEntry = pEntry;
  416. }
  417. _uFilterCount = 0;
  418. }
  419. DWORD
  420. FILTER_LIST::Decode(
  421. IN OUT LPBYTE pInBuffer,
  422. IN DWORD dwInBufSize,
  423. IN OUT LPBYTE *ppOutBuffer,
  424. IN OUT LPDWORD pdwOutBufSize,
  425. OUT LPDWORD pdwBytesRead,
  426. OUT LPDWORD pdwBytesWritten
  427. )
  428. {
  429. LPFILTER_LIST_ENTRY pEntry = _pFilterEntry;
  430. HRESULT hr = S_OK;
  431. DWORD dwBytesRead = 0;
  432. DWORD dwBytesWritten = 0;
  433. LPBYTE pLocalInBuffer = pInBuffer;
  434. DWORD dwLocalInBufSize = dwInBufSize;
  435. *pdwBytesRead = 0;
  436. *pdwBytesWritten = 0;
  437. // Loop through filters which should be in the proper order
  438. while (pEntry)
  439. {
  440. dwBytesRead = 0;
  441. dwBytesWritten = 0;
  442. // At a minimum, we're guaranteed the decode method parses
  443. // the input buffer until one of the following is met:
  444. //
  445. // - Input buffer is fully parsed and processed
  446. // - Output buffer is filled up
  447. // - Decoder reaches a finished state
  448. // - Error occurs while processing input data
  449. //
  450. // Currently, only 1, 3, and 4 are possible since chunked
  451. // transfers are decoded in place. We also don't need
  452. // to loop since chunked decoding is always fully done
  453. // in the first pass.
  454. do
  455. {
  456. pLocalInBuffer = pLocalInBuffer + dwBytesRead;
  457. dwLocalInBufSize = dwLocalInBufSize - dwBytesRead;
  458. dwBytesWritten = 0;
  459. dwBytesRead = 0;
  460. hr = pEntry->pFilter->Decode(pEntry->dwContext,
  461. pLocalInBuffer,
  462. dwLocalInBufSize,
  463. ppOutBuffer,
  464. pdwOutBufSize,
  465. &dwBytesRead,
  466. &dwBytesWritten
  467. );
  468. *pdwBytesWritten += dwBytesWritten;
  469. *pdwBytesRead += dwBytesRead;
  470. if (hr == S_OK && dwBytesRead < dwLocalInBufSize)
  471. {
  472. // Given the current requirements we shouldn't be here
  473. // if there's still input buffer data to process.
  474. RIP(FALSE);
  475. hr = E_FAIL;
  476. goto quit;
  477. }
  478. } while (hr == S_OK &&
  479. dwLocalInBufSize > 0 &&
  480. dwBytesRead < dwLocalInBufSize);
  481. pEntry = pEntry->pNext;
  482. }
  483. INET_ASSERT(hr != S_OK || dwBytesRead == dwLocalInBufSize);
  484. quit:
  485. switch (hr)
  486. {
  487. case S_OK:
  488. return ERROR_SUCCESS;
  489. case E_OUTOFMEMORY:
  490. return ERROR_NOT_ENOUGH_MEMORY;
  491. default:
  492. return ERROR_WINHTTP_INTERNAL_ERROR;
  493. }
  494. }