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.

727 lines
22 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. chunk.cxx
  5. Abstract:
  6. Contains a generic chunked transfer implimentation
  7. Author:
  8. Arthur L Bierer (arthurbi) 03-May-1997
  9. Revision History:
  10. 03-May-1997 arthurbi
  11. Created
  12. --*/
  13. #include <wininetp.h>
  14. inline
  15. CHUNK_TOKEN
  16. CHUNK_TRANSFER::GetToken(
  17. IN OUT LPSTR *lplpInputBuffer,
  18. IN LPSTR lpEndOfInputBuffer,
  19. OUT LPDWORD lpdwValue,
  20. IN DWORD dwExpectedTokenSize,
  21. OUT LPDWORD lpdwBytesTokenized
  22. )
  23. /*++
  24. Routine Description:
  25. Lexes through a byte stream, seperating data into tokens. Data is special cased for efficency.
  26. Arguments:
  27. lplpInputBuffer - Pointer to Pointer of Buffer that should be lexed, on return contains
  28. an offset where the next character to lex is.
  29. lpEndofInputBuffer - Pointer to last character to passed in Buffer.
  30. lpdwValue - On return, MAY contain numerical conversion of a text number (digit) token
  31. dwExpectedTokenSize - Expected size of token, in all cases except for data should be 1
  32. lpdwBytesTokenized - On return, contains size of token
  33. Return Value:
  34. CHUNK_TOKEN
  35. Success - The Correct token.
  36. Failure - CHUNK_TOKEN_INVALID
  37. --*/
  38. {
  39. BOOL fFirstIteration = TRUE;
  40. CHUNK_TOKEN ctToken = CHUNK_TOKEN_INVALID;
  41. DEBUG_ENTER((DBG_HTTP,
  42. Dword,
  43. "CHUNK_TRANSFER::GetToken",
  44. "%x [%x, %.10q], %x, %x, %u, %x",
  45. lplpInputBuffer,
  46. *lplpInputBuffer,
  47. *lplpInputBuffer,
  48. lpEndOfInputBuffer,
  49. lpdwValue,
  50. dwExpectedTokenSize,
  51. lpdwBytesTokenized
  52. ));
  53. *lpdwBytesTokenized = 0;
  54. while ( *lplpInputBuffer < lpEndOfInputBuffer
  55. && *lpdwBytesTokenized < dwExpectedTokenSize )
  56. {
  57. //
  58. // Set Default Token type
  59. //
  60. ctToken = CHUNK_TOKEN_DATA;
  61. //
  62. // Handle Other, "special" tokens, only if asked for by the parser.
  63. //
  64. if ( dwExpectedTokenSize == 1 )
  65. {
  66. if ( **lplpInputBuffer == '\r' )
  67. {
  68. ctToken = CHUNK_TOKEN_CR;
  69. goto quit;
  70. }
  71. if ( **lplpInputBuffer == '\n' )
  72. {
  73. ctToken = CHUNK_TOKEN_LF;
  74. goto quit;
  75. }
  76. if ( **lplpInputBuffer == ':' )
  77. {
  78. ctToken = CHUNK_TOKEN_COLON;
  79. goto quit;
  80. }
  81. if ( **lplpInputBuffer >= '0' && **lplpInputBuffer <= '9' )
  82. {
  83. *lpdwValue = (DWORD) (**lplpInputBuffer - '0');
  84. ctToken = CHUNK_TOKEN_DIGIT;
  85. goto quit;
  86. }
  87. if ( **lplpInputBuffer >= 'A' && **lplpInputBuffer <= 'F' )
  88. {
  89. *lpdwValue = (DWORD) (**lplpInputBuffer - 'A') + 10;
  90. ctToken = CHUNK_TOKEN_DIGIT;
  91. goto quit;
  92. }
  93. if ( **lplpInputBuffer >= 'a' && **lplpInputBuffer <= 'f' )
  94. {
  95. *lpdwValue = (DWORD) (**lplpInputBuffer - 'a') + 10;
  96. ctToken = CHUNK_TOKEN_DIGIT;
  97. goto quit;
  98. }
  99. }
  100. fFirstIteration = FALSE;
  101. (*lplpInputBuffer)++;
  102. (*lpdwBytesTokenized)++;
  103. }
  104. quit:
  105. if (ctToken != CHUNK_TOKEN_DATA && ctToken != CHUNK_TOKEN_INVALID)
  106. {
  107. if ( !fFirstIteration)
  108. {
  109. ctToken = CHUNK_TOKEN_DATA;
  110. }
  111. else
  112. {
  113. //
  114. // Advance past this token, since we've only
  115. // lexed one token
  116. //
  117. (*lplpInputBuffer)++;
  118. (*lpdwBytesTokenized)++;
  119. }
  120. }
  121. DEBUG_PRINT(HTTP,
  122. INFO,
  123. ("GetToken: %q, expected=%u, actual=%u\n",
  124. InternetMapChunkToken(ctToken),
  125. dwExpectedTokenSize,
  126. *lpdwBytesTokenized
  127. ));
  128. DEBUG_LEAVE((DWORD)ctToken);
  129. return ctToken;
  130. }
  131. DWORD
  132. CHUNK_TRANSFER::ParseChunkInput(
  133. LPSTR lpInputBuffer,
  134. DWORD dwInputBufferSize,
  135. LPSTR *lplpInputBufferNew,
  136. LPDWORD lpdwInputBufferNewSize
  137. )
  138. /*++
  139. Routine Description:
  140. Parses a buffer of an assumed chunked encoding byte stream. Seperates out data from header information.
  141. Arguments:
  142. lpInputBuffer - Pointer to buffer containing a stream of bytes to parse
  143. dwInputBufferSize - size of byte lpInputBuffer
  144. lplpInputBufferNew - Offset into passed in lpInputBuffer, (not used, yet)
  145. lpdwInputBufferNewSize - On Return, cotains the size of lpInputBuffer ( data is compressed )
  146. Return Value:
  147. DWORD
  148. Success - ERROR_SUCCESS
  149. Failure -
  150. --*/
  151. {
  152. CHUNK_TOKEN ctToken;
  153. DWORD dwValue;
  154. DWORD dwExpectedTokenSize = 1;
  155. DWORD dwActualTokenSize = 0;
  156. LPSTR lpszEndOfInputBuffer = (LPSTR) (lpInputBuffer + dwInputBufferSize);
  157. LPSTR lpszStartOfFirstDataBuffer = NULL;
  158. DWORD dwFirstDataBufferSize = 0;
  159. LPSTR lpszStartOfNextDataBuffer = NULL;
  160. DWORD error = ERROR_SUCCESS;
  161. DEBUG_ENTER((DBG_HTTP,
  162. Dword,
  163. "CHUNK_TRANSFER::ParseChunkInput",
  164. "%x [%.10q], %u, %x, %x",
  165. lpInputBuffer,
  166. lpInputBuffer,
  167. dwInputBufferSize,
  168. lplpInputBufferNew,
  169. lpdwInputBufferNewSize
  170. ));
  171. *lplpInputBufferNew = lpInputBuffer;
  172. while ( *lplpInputBufferNew < lpszEndOfInputBuffer)
  173. {
  174. //
  175. // Calculate the max size of the token can be, only
  176. // relevant for data, since all other tokens are assumed
  177. // to be size of 1.
  178. //
  179. if (_csState == CHUNK_STATE_DATA_PARSE)
  180. {
  181. dwExpectedTokenSize = (_dwChunkDataSize - _dwChunkDataRead);
  182. lpszStartOfNextDataBuffer = *lplpInputBufferNew;
  183. }
  184. else
  185. {
  186. dwExpectedTokenSize = 1;
  187. }
  188. DEBUG_PRINT(HTTP,
  189. INFO,
  190. ("ParseChunk: %q, %u/%u\n",
  191. InternetMapChunkState(_csState),
  192. _dwChunkDataRead,
  193. _dwChunkDataSize
  194. ));
  195. //
  196. // Lex through the byte stream looking for our next token.
  197. //
  198. ctToken = GetToken( lplpInputBufferNew,
  199. lpszEndOfInputBuffer,
  200. &dwValue,
  201. dwExpectedTokenSize,
  202. &dwActualTokenSize
  203. );
  204. if ( ctToken == CHUNK_TOKEN_INVALID )
  205. {
  206. //
  207. // Need more data to parse...
  208. //
  209. error = ERROR_SUCCESS;
  210. goto quit;
  211. }
  212. //
  213. // Based on our current state, evalulate the token,
  214. // and figure out what to do next.
  215. //
  216. switch ( _csState )
  217. {
  218. case CHUNK_STATE_START:
  219. ResetSubStateInfo();
  220. if ( ctToken != CHUNK_TOKEN_DIGIT )
  221. {
  222. DEBUG_PRINT(HTTP,
  223. INFO,
  224. ("-->CHUNK err: Got %q, while looking for NOT %q\n",
  225. InternetMapChunkToken(ctToken),
  226. InternetMapChunkToken(CHUNK_TOKEN_DIGIT)
  227. ));
  228. error = ERROR_INTERNET_INTERNAL_ERROR;
  229. goto quit;
  230. }
  231. SetState(CHUNK_STATE_SIZE_PARSE);
  232. // otherwise fall through
  233. case CHUNK_STATE_SIZE_PARSE:
  234. switch ( ctToken )
  235. {
  236. case CHUNK_TOKEN_DIGIT:
  237. _dwCalculatedChunkSize *= BASE_HEX;
  238. _dwCalculatedChunkSize += dwValue;
  239. break;
  240. case CHUNK_TOKEN_CR:
  241. _dwCr++;
  242. // fall through
  243. case CHUNK_TOKEN_DATA:
  244. case CHUNK_TOKEN_COLON:
  245. _dwChunkDataSize = _dwCalculatedChunkSize;
  246. _dwChunkDataRead = 0;
  247. DEBUG_PRINT(HTTP,
  248. INFO,
  249. ("ChunkParse-GotChunksize: size=%u, %x\n",
  250. _dwCalculatedChunkSize,
  251. _dwCalculatedChunkSize
  252. ));
  253. if (ctToken == CHUNK_TOKEN_CR)
  254. {
  255. SetState(CHUNK_STATE_SIZE_CRLF);
  256. }
  257. else
  258. {
  259. SetState(CHUNK_STATE_EXT_PARSE);
  260. }
  261. break;
  262. default:
  263. // ERROR
  264. error = ERROR_INTERNET_INTERNAL_ERROR;
  265. goto quit;
  266. }
  267. break;
  268. case CHUNK_STATE_EXT_PARSE:
  269. switch ( ctToken )
  270. {
  271. case CHUNK_TOKEN_CR:
  272. _dwCr++;
  273. SetState(CHUNK_STATE_SIZE_CRLF);
  274. break;
  275. case CHUNK_TOKEN_DIGIT:
  276. case CHUNK_TOKEN_DATA:
  277. case CHUNK_TOKEN_COLON:
  278. break;
  279. default:
  280. // ERROR
  281. error = ERROR_INTERNET_INTERNAL_ERROR;
  282. goto quit;
  283. }
  284. break;
  285. case CHUNK_STATE_SIZE_CRLF:
  286. switch ( ctToken )
  287. {
  288. case CHUNK_TOKEN_LF:
  289. _dwLf++;
  290. if ( IsCrLf() )
  291. {
  292. ClearCrLf();
  293. if ( _dwCalculatedChunkSize == 0 )
  294. {
  295. SetState(CHUNK_STATE_ZERO_FOOTER);
  296. }
  297. else
  298. {
  299. SetState(CHUNK_STATE_DATA_PARSE);
  300. }
  301. }
  302. else
  303. {
  304. DEBUG_PRINT(HTTP,
  305. INFO,
  306. ("-->CHUNK err: Got %q, But CRLF not matched, CR=%u, LF=%u\n",
  307. InternetMapChunkToken(ctToken),
  308. _dwCr,
  309. _dwLf
  310. ));
  311. error = ERROR_INTERNET_INTERNAL_ERROR;
  312. goto quit;
  313. }
  314. break;
  315. default:
  316. DEBUG_PRINT(HTTP,
  317. INFO,
  318. ("-->CHUNK err: Got %q, while looking for %q\n",
  319. InternetMapChunkToken(ctToken),
  320. InternetMapChunkToken(CHUNK_TOKEN_LF)
  321. ));
  322. error = ERROR_INTERNET_INTERNAL_ERROR;
  323. goto quit;
  324. }
  325. break;
  326. case CHUNK_STATE_DATA_PARSE:
  327. switch ( ctToken )
  328. {
  329. case CHUNK_TOKEN_CR:
  330. case CHUNK_TOKEN_LF:
  331. case CHUNK_TOKEN_DATA:
  332. case CHUNK_TOKEN_DIGIT:
  333. case CHUNK_TOKEN_COLON:
  334. //
  335. // If this is the first piece of data we receive
  336. // than save it off, so we know the start of the
  337. // buffer we are returning as data.
  338. //
  339. if ( lpszStartOfFirstDataBuffer == NULL )
  340. {
  341. lpszStartOfFirstDataBuffer = lpInputBuffer;
  342. }
  343. //
  344. // If this is not the first block of data in the passed in buffer,
  345. // we must move the block of data OVER any Chunked-tranfer header
  346. // information.
  347. //
  348. if ( (lpszStartOfFirstDataBuffer+dwFirstDataBufferSize) != lpszStartOfNextDataBuffer )
  349. {
  350. MoveMemory((LPVOID) (lpszStartOfFirstDataBuffer+dwFirstDataBufferSize), // Dest
  351. (LPVOID) lpszStartOfNextDataBuffer, // Source
  352. dwActualTokenSize // size
  353. );
  354. }
  355. //
  356. // Update the size of data we've parsed out, and
  357. // check to see if we've completely received all data.
  358. //
  359. dwFirstDataBufferSize += dwActualTokenSize;
  360. _dwChunkDataRead += dwActualTokenSize;
  361. if ( _dwChunkDataRead == _dwChunkDataSize )
  362. {
  363. SetState(CHUNK_STATE_DATA_CRLF);
  364. }
  365. INET_ASSERT(_dwChunkDataRead <= _dwChunkDataSize);
  366. break;
  367. default:
  368. DEBUG_PRINT(HTTP,
  369. INFO,
  370. ("-->CHUNK err: Got %q, while looking for CR,LF,DATA,DIGIT,COLON\n",
  371. InternetMapChunkToken(ctToken)
  372. ));
  373. error = ERROR_INTERNET_INTERNAL_ERROR;
  374. goto quit;
  375. }
  376. break;
  377. case CHUNK_STATE_DATA_CRLF:
  378. switch (ctToken)
  379. {
  380. case CHUNK_TOKEN_CR:
  381. _dwCr++;
  382. break;
  383. case CHUNK_TOKEN_LF:
  384. _dwLf++;
  385. if ( IsCrLf() )
  386. {
  387. ClearCrLf();
  388. SetState(CHUNK_STATE_START);
  389. }
  390. else
  391. {
  392. DEBUG_PRINT(HTTP,
  393. INFO,
  394. ("-->CHUNK err: Got %q, BUT CRLF not matched, CR=%u, LF=%u\n",
  395. InternetMapChunkToken(ctToken),
  396. _dwCr,
  397. _dwLf
  398. ));
  399. error = ERROR_INTERNET_INTERNAL_ERROR;
  400. goto quit;
  401. }
  402. break;
  403. default:
  404. DEBUG_PRINT(HTTP,
  405. INFO,
  406. ("-->CHUNK err: Got %q, while looking for CR or LF (CHUNK DATA SIZE INCORRECT??)\n",
  407. InternetMapChunkToken(ctToken)
  408. ));
  409. error = ERROR_INTERNET_INTERNAL_ERROR;
  410. goto quit;
  411. }
  412. break;
  413. case CHUNK_STATE_ZERO_FOOTER:
  414. switch (ctToken)
  415. {
  416. case CHUNK_TOKEN_CR:
  417. _dwCr++;
  418. SetState(CHUNK_STATE_ZERO_FOOTER_FINAL_CRLF);
  419. break;
  420. case CHUNK_TOKEN_DATA:
  421. case CHUNK_TOKEN_DIGIT:
  422. case CHUNK_TOKEN_COLON:
  423. SetState(CHUNK_STATE_ZERO_FOOTER_NAME);
  424. break;
  425. default:
  426. DEBUG_PRINT(HTTP,
  427. INFO,
  428. ("-->CHUNK err: Got %q, while looking for DATA or CR\n",
  429. InternetMapChunkToken(ctToken)
  430. ));
  431. error = ERROR_INTERNET_INTERNAL_ERROR;
  432. goto quit;
  433. }
  434. break;
  435. case CHUNK_STATE_ZERO_FOOTER_NAME:
  436. switch (ctToken)
  437. {
  438. case CHUNK_TOKEN_DATA:
  439. case CHUNK_TOKEN_DIGIT:
  440. break;
  441. case CHUNK_TOKEN_COLON:
  442. SetState(CHUNK_STATE_ZERO_FOOTER_VALUE);
  443. break;
  444. default:
  445. DEBUG_PRINT(HTTP,
  446. INFO,
  447. ("-->CHUNK err: Got %q, while looking for DATA, DIGIT, COLON\n",
  448. InternetMapChunkToken(ctToken)
  449. ));
  450. error = ERROR_INTERNET_INTERNAL_ERROR;
  451. goto quit;
  452. }
  453. break;
  454. case CHUNK_STATE_ZERO_FOOTER_VALUE:
  455. switch (ctToken)
  456. {
  457. case CHUNK_TOKEN_DATA:
  458. case CHUNK_TOKEN_DIGIT:
  459. break;
  460. case CHUNK_TOKEN_CR:
  461. _dwCr++;
  462. SetState(CHUNK_STATE_ZERO_FOOTER_CRLF);
  463. break;
  464. default:
  465. DEBUG_PRINT(HTTP,
  466. INFO,
  467. ("-->CHUNK err: Got %q, while looking for DATA, DIGIT, CR\n",
  468. InternetMapChunkToken(ctToken)
  469. ));
  470. error = ERROR_INTERNET_INTERNAL_ERROR;
  471. goto quit;
  472. }
  473. break;
  474. case CHUNK_STATE_ZERO_FOOTER_CRLF:
  475. case CHUNK_STATE_ZERO_FOOTER_FINAL_CRLF:
  476. switch ( ctToken )
  477. {
  478. case CHUNK_TOKEN_LF:
  479. _dwLf++;
  480. if ( IsCrLf() )
  481. {
  482. ClearCrLf();
  483. if ( _csState == CHUNK_STATE_ZERO_FOOTER_CRLF)
  484. {
  485. SetState(CHUNK_STATE_ZERO_FOOTER);
  486. }
  487. else
  488. {
  489. INET_ASSERT( _csState == CHUNK_STATE_ZERO_FOOTER_FINAL_CRLF);
  490. //Done?
  491. SetState(CHUNK_STATE_FINISHED);
  492. DEBUG_PRINT(HTTP,
  493. INFO,
  494. ("EOF chunk\n"
  495. ));
  496. error = ERROR_SUCCESS;
  497. goto quit;
  498. }
  499. }
  500. else
  501. {
  502. DEBUG_PRINT(HTTP,
  503. INFO,
  504. ("-->CHUNK err: Got %q, But CRLF not matched, CR=%u, LF=%u\n",
  505. InternetMapChunkToken(ctToken),
  506. _dwCr,
  507. _dwLf
  508. ));
  509. error = ERROR_INTERNET_INTERNAL_ERROR;
  510. goto quit;
  511. }
  512. break;
  513. default:
  514. DEBUG_PRINT(HTTP,
  515. INFO,
  516. ("-->CHUNK err: Got %q, while looking for LF\n",
  517. InternetMapChunkToken(ctToken)
  518. ));
  519. error = ERROR_INTERNET_INTERNAL_ERROR;
  520. goto quit;
  521. }
  522. break;
  523. case CHUNK_STATE_FINISHED:
  524. INET_ASSERT(FALSE);
  525. error = ERROR_SUCCESS;
  526. goto quit;
  527. default:
  528. INET_ASSERT(FALSE);
  529. error = ERROR_INTERNET_INTERNAL_ERROR;
  530. goto quit;
  531. }
  532. }
  533. quit:
  534. if ( error == ERROR_SUCCESS)
  535. {
  536. if ( dwFirstDataBufferSize > 0 )
  537. {
  538. INET_ASSERT(lpInputBuffer == lpszStartOfFirstDataBuffer);
  539. }
  540. *lplpInputBufferNew = lpInputBuffer;
  541. *lpdwInputBufferNewSize = dwFirstDataBufferSize;
  542. }
  543. DEBUG_LEAVE(error);
  544. return error;
  545. }