cherry-pick.v2.9.4-8-ge13b274.allow-content-length-and-transfer-encoding-chunked.patch 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. Subject: Allow Content-Length and Transfer-Encoding: chunked
  2. Origin: v2.9.4-8-ge13b274 <https://github.com/joyent/http-parser/commit/e13b274>
  3. Upstream-Author: Oleg Guba <oleg@dropbox.com>
  4. Date: Fri Jul 10 11:49:58 2020 +0200
  5. Fixes: https://github.com/nodejs/http-parser/issues/517
  6. PR-URL: https://github.com/nodejs/http-parser/pull/518
  7. Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
  8. Reviewed-By: Pierce Lopez <pierce.lopez@gmail.com>
  9. --- a/http_parser.c
  10. +++ b/http_parser.c
  11. @@ -653,6 +653,8 @@
  12. const char *status_mark = 0;
  13. enum state p_state = (enum state) parser->state;
  14. const unsigned int lenient = parser->lenient_http_headers;
  15. + const unsigned int allow_chunked_length = parser->allow_chunked_length;
  16. +
  17. uint32_t nread = parser->nread;
  18. /* We're in an error state. Don't bother doing anything. */
  19. @@ -731,7 +733,7 @@
  20. if (ch == CR || ch == LF)
  21. break;
  22. parser->flags = 0;
  23. - parser->extra_flags = 0;
  24. + parser->uses_transfer_encoding = 0;
  25. parser->content_length = ULLONG_MAX;
  26. if (ch == 'H') {
  27. @@ -769,7 +771,7 @@
  28. if (ch == CR || ch == LF)
  29. break;
  30. parser->flags = 0;
  31. - parser->extra_flags = 0;
  32. + parser->uses_transfer_encoding = 0;
  33. parser->content_length = ULLONG_MAX;
  34. if (ch == 'H') {
  35. @@ -927,7 +929,7 @@
  36. if (ch == CR || ch == LF)
  37. break;
  38. parser->flags = 0;
  39. - parser->extra_flags = 0;
  40. + parser->uses_transfer_encoding = 0;
  41. parser->content_length = ULLONG_MAX;
  42. if (UNLIKELY(!IS_ALPHA(ch))) {
  43. @@ -1341,7 +1343,7 @@
  44. parser->header_state = h_general;
  45. } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
  46. parser->header_state = h_transfer_encoding;
  47. - parser->extra_flags |= F_TRANSFER_ENCODING >> 8;
  48. + parser->uses_transfer_encoding = 1;
  49. }
  50. break;
  51. @@ -1801,14 +1803,19 @@
  52. REEXECUTE();
  53. }
  54. - /* Cannot us transfer-encoding and a content-length header together
  55. + /* Cannot use transfer-encoding and a content-length header together
  56. per the HTTP specification. (RFC 7230 Section 3.3.3) */
  57. - if ((parser->extra_flags & (F_TRANSFER_ENCODING >> 8)) &&
  58. + if ((parser->uses_transfer_encoding == 1) &&
  59. (parser->flags & F_CONTENTLENGTH)) {
  60. /* Allow it for lenient parsing as long as `Transfer-Encoding` is
  61. - * not `chunked`
  62. + * not `chunked` or allow_length_with_encoding is set
  63. */
  64. - if (!lenient || (parser->flags & F_CHUNKED)) {
  65. + if (parser->flags & F_CHUNKED) {
  66. + if (!allow_chunked_length) {
  67. + SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
  68. + goto error;
  69. + }
  70. + } else if (!lenient) {
  71. SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
  72. goto error;
  73. }
  74. @@ -1889,7 +1896,7 @@
  75. /* chunked encoding - ignore Content-Length header,
  76. * prepare for a chunk */
  77. UPDATE_STATE(s_chunk_size_start);
  78. - } else if (parser->extra_flags & (F_TRANSFER_ENCODING >> 8)) {
  79. + } else if (parser->uses_transfer_encoding == 1) {
  80. if (parser->type == HTTP_REQUEST && !lenient) {
  81. /* RFC 7230 3.3.3 */
  82. @@ -2165,7 +2172,7 @@
  83. }
  84. /* RFC 7230 3.3.3, see `s_headers_almost_done` */
  85. - if ((parser->extra_flags & (F_TRANSFER_ENCODING >> 8)) &&
  86. + if ((parser->uses_transfer_encoding == 1) &&
  87. (parser->flags & F_CHUNKED) == 0) {
  88. return 1;
  89. }
  90. --- a/http_parser.h
  91. +++ b/http_parser.h
  92. @@ -225,7 +225,6 @@
  93. , F_UPGRADE = 1 << 5
  94. , F_SKIPBODY = 1 << 6
  95. , F_CONTENTLENGTH = 1 << 7
  96. - , F_TRANSFER_ENCODING = 1 << 8 /* Never set in http_parser.flags */
  97. };
  98. @@ -300,7 +299,10 @@
  99. unsigned int state : 7; /* enum state from http_parser.c */
  100. unsigned int header_state : 7; /* enum header_state from http_parser.c */
  101. unsigned int index : 5; /* index into current matcher */
  102. - unsigned int extra_flags : 2;
  103. + unsigned int uses_transfer_encoding : 1; /* Transfer-Encoding header is present */
  104. + unsigned int allow_chunked_length : 1; /* Allow headers with both
  105. + * `Content-Length` and
  106. + * `Transfer-Encoding: chunked` set */
  107. unsigned int lenient_http_headers : 1;
  108. uint32_t nread; /* # bytes read in various scenarios */
  109. --- a/test.c
  110. +++ b/test.c
  111. @@ -82,6 +82,7 @@
  112. int status_cb_called;
  113. int message_complete_on_eof;
  114. int body_is_final;
  115. + int allow_chunked_length;
  116. };
  117. static int currently_parsing_eof;
  118. @@ -1293,6 +1294,37 @@
  119. ,.num_chunks_complete= 2
  120. ,.chunk_lengths= { 0x1e }
  121. }
  122. +
  123. +#define CHUNKED_CONTENT_LENGTH 46
  124. +, {.name= "chunked with content-length set, allow_chunked_length flag is set"
  125. + ,.type= HTTP_REQUEST
  126. + ,.raw= "POST /chunked_w_content_length HTTP/1.1\r\n"
  127. + "Content-Length: 10\r\n"
  128. + "Transfer-Encoding: chunked\r\n"
  129. + "\r\n"
  130. + "5; ilovew3;whattheluck=aretheseparametersfor\r\nhello\r\n"
  131. + "6; blahblah; blah\r\n world\r\n"
  132. + "0\r\n"
  133. + "\r\n"
  134. + ,.allow_chunked_length = 1
  135. + ,.should_keep_alive= TRUE
  136. + ,.message_complete_on_eof= FALSE
  137. + ,.http_major= 1
  138. + ,.http_minor= 1
  139. + ,.method= HTTP_POST
  140. + ,.query_string= ""
  141. + ,.fragment= ""
  142. + ,.request_path= "/chunked_w_content_length"
  143. + ,.request_url= "/chunked_w_content_length"
  144. + ,.content_length= 10
  145. + ,.num_headers= 2
  146. + ,.headers={ { "Content-Length", "10"}
  147. + , { "Transfer-Encoding", "chunked" }
  148. + }
  149. + ,.body= "hello world"
  150. + ,.num_chunks_complete= 3
  151. + ,.chunk_lengths= { 5, 6 }
  152. + }
  153. };
  154. /* * R E S P O N S E S * */
  155. @@ -3582,6 +3614,9 @@
  156. size_t msg1len;
  157. for (msg1len = 0; msg1len < raw_len; msg1len++) {
  158. parser_init(message->type);
  159. + if (message->allow_chunked_length) {
  160. + parser.allow_chunked_length = 1;
  161. + }
  162. size_t read;
  163. const char *msg1 = message->raw;
  164. @@ -4023,6 +4058,11 @@
  165. strcat(total, r3->raw);
  166. parser_init(r1->type);
  167. + if (r1->allow_chunked_length ||
  168. + r2->allow_chunked_length ||
  169. + r3->allow_chunked_length) {
  170. + parser.allow_chunked_length = 1;
  171. + }
  172. size_t read;
  173. @@ -4225,6 +4265,9 @@
  174. size_t nread;
  175. parser_init(msg->type);
  176. + if (msg->allow_chunked_length) {
  177. + parser.allow_chunked_length = 1;
  178. + }
  179. do {
  180. nread = parse_pause(buf, buflen);