2083 lines
65 KiB
Diff
2083 lines
65 KiB
Diff
|
ported commits,
|
|||
|
|
|||
|
commit e2c8f89b7572a7aea62927923e425bbd7725dca2
|
|||
|
Author: Sam Roberts <vieuxtech@gmail.com>
|
|||
|
Date: Thu Jan 16 11:55:52 2020 -0800
|
|||
|
|
|||
|
test: using TE to smuggle reqs is not possible
|
|||
|
|
|||
|
See: https://hackerone.com/reports/735748
|
|||
|
|
|||
|
PR-URL: https://github.com/nodejs-private/node-private/pull/192
|
|||
|
Reviewed-By: Beth Griggs <Bethany.Griggs@uk.ibm.com>
|
|||
|
|
|||
|
commit 49f4220ce5b92bec68c040f46823e55c27d50517
|
|||
|
Author: Sam Roberts <vieuxtech@gmail.com>
|
|||
|
Date: Tue Feb 4 10:36:57 2020 -0800
|
|||
|
|
|||
|
deps: upgrade http-parser to v2.9.3
|
|||
|
|
|||
|
PR-URL: https://github.com/nodejs-private/http-parser-private/pull/4
|
|||
|
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
|
|||
|
Reviewed-By: James M Snell <jasnell@gmail.com>
|
|||
|
Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
|
|||
|
|
|||
|
commit d616722f65fcfbce57e597f41466e864eba22c4f
|
|||
|
Author: Sam Roberts <vieuxtech@gmail.com>
|
|||
|
Date: Tue Jan 7 14:24:54 2020 -0800
|
|||
|
|
|||
|
test: check that --insecure-http-parser works
|
|||
|
|
|||
|
Test that using --insecure-http-parser will disable validation of
|
|||
|
invalid characters in HTTP headers.
|
|||
|
|
|||
|
See:
|
|||
|
- https://github.com/nodejs/node/pull/30567
|
|||
|
|
|||
|
Backport-PR-URL: https://github.com/nodejs/node/pull/30471
|
|||
|
PR-URL: https://github.com/nodejs/node/pull/31253
|
|||
|
Reviewed-By: Richard Lau <riclau@uk.ibm.com>
|
|||
|
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
|
|||
|
|
|||
|
commit a9849c0ff6b4459880f8f6da10e6fedb3c4df620
|
|||
|
Author: Sam Roberts <vieuxtech@gmail.com>
|
|||
|
Date: Wed Nov 20 11:48:58 2019 -0800
|
|||
|
|
|||
|
http: opt-in insecure HTTP header parsing
|
|||
|
|
|||
|
Allow insecure HTTP header parsing. Make clear it is insecure.
|
|||
|
|
|||
|
See:
|
|||
|
- https://github.com/nodejs/node/pull/30553
|
|||
|
- https://github.com/nodejs/node/issues/27711#issuecomment-556265881
|
|||
|
- https://github.com/nodejs/node/issues/30515
|
|||
|
|
|||
|
Backport-PR-URL: https://github.com/nodejs/node/pull/30471
|
|||
|
PR-URL: https://github.com/nodejs/node/pull/30567
|
|||
|
Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com>
|
|||
|
Reviewed-By: Anna Henningsen <anna@addaleax.net>
|
|||
|
Reviewed-By: Denys Otrishko <shishugi@gmail.com>
|
|||
|
Reviewed-By: James M Snell <jasnell@gmail.com>
|
|||
|
|
|||
|
commit a28e5cc1ed7e298118bd3ea8b5b96712467c3703
|
|||
|
Author: Sam Roberts <vieuxtech@gmail.com>
|
|||
|
Date: Wed Nov 13 10:05:38 2019 -0800
|
|||
|
|
|||
|
deps: upgrade http-parser to v2.9.1
|
|||
|
|
|||
|
PR-URL: https://github.com/nodejs/node/pull/30471
|
|||
|
Reviewed-By: James M Snell <jasnell@gmail.com>
|
|||
|
Reviewed-By: Jiawen Geng <technicalcute@gmail.com>
|
|||
|
Reviewed-By: Richard Lau <riclau@uk.ibm.com>
|
|||
|
Reviewed-By: Beth Griggs <Bethany.Griggs@uk.ibm.com>
|
|||
|
|
|||
|
Index: node-v8.17.0/deps/http_parser/Makefile
|
|||
|
===================================================================
|
|||
|
--- node-v8.17.0.orig/deps/http_parser/Makefile
|
|||
|
+++ node-v8.17.0/deps/http_parser/Makefile
|
|||
|
@@ -23,8 +23,8 @@ HELPER ?=
|
|||
|
BINEXT ?=
|
|||
|
SOLIBNAME = libhttp_parser
|
|||
|
SOMAJOR = 2
|
|||
|
-SOMINOR = 8
|
|||
|
-SOREV = 0
|
|||
|
+SOMINOR = 9
|
|||
|
+SOREV = 3
|
|||
|
ifeq (darwin,$(PLATFORM))
|
|||
|
SOEXT ?= dylib
|
|||
|
SONAME ?= $(SOLIBNAME).$(SOMAJOR).$(SOMINOR).$(SOEXT)
|
|||
|
@@ -133,14 +133,14 @@ tags: http_parser.c http_parser.h test.c
|
|||
|
install: library
|
|||
|
$(INSTALL) -D http_parser.h $(DESTDIR)$(INCLUDEDIR)/http_parser.h
|
|||
|
$(INSTALL) -D $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(LIBNAME)
|
|||
|
- ln -s $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SONAME)
|
|||
|
- ln -s $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SOLIBNAME).$(SOEXT)
|
|||
|
+ ln -sf $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SONAME)
|
|||
|
+ ln -sf $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SOLIBNAME).$(SOEXT)
|
|||
|
|
|||
|
install-strip: library
|
|||
|
$(INSTALL) -D http_parser.h $(DESTDIR)$(INCLUDEDIR)/http_parser.h
|
|||
|
$(INSTALL) -D -s $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(LIBNAME)
|
|||
|
- ln -s $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SONAME)
|
|||
|
- ln -s $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SOLIBNAME).$(SOEXT)
|
|||
|
+ ln -sf $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SONAME)
|
|||
|
+ ln -sf $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SOLIBNAME).$(SOEXT)
|
|||
|
|
|||
|
uninstall:
|
|||
|
rm $(DESTDIR)$(INCLUDEDIR)/http_parser.h
|
|||
|
Index: node-v8.17.0/deps/http_parser/README.md
|
|||
|
===================================================================
|
|||
|
--- node-v8.17.0.orig/deps/http_parser/README.md
|
|||
|
+++ node-v8.17.0/deps/http_parser/README.md
|
|||
|
@@ -148,7 +148,7 @@ callback in a threadsafe manner. This al
|
|||
|
multi-threaded contexts.
|
|||
|
|
|||
|
Example:
|
|||
|
-```
|
|||
|
+```c
|
|||
|
typedef struct {
|
|||
|
socket_t sock;
|
|||
|
void* buffer;
|
|||
|
@@ -184,7 +184,7 @@ void http_parser_thread(socket_t sock) {
|
|||
|
parser supplied to callback functions */
|
|||
|
parser->data = my_data;
|
|||
|
|
|||
|
- http_parser_settings settings; / * set up callbacks */
|
|||
|
+ http_parser_settings settings; /* set up callbacks */
|
|||
|
settings.on_url = my_url_callback;
|
|||
|
|
|||
|
/* execute parser */
|
|||
|
Index: node-v8.17.0/deps/http_parser/bench.c
|
|||
|
===================================================================
|
|||
|
--- node-v8.17.0.orig/deps/http_parser/bench.c
|
|||
|
+++ node-v8.17.0/deps/http_parser/bench.c
|
|||
|
@@ -20,10 +20,14 @@
|
|||
|
*/
|
|||
|
#include "http_parser.h"
|
|||
|
#include <assert.h>
|
|||
|
+#include <stdint.h>
|
|||
|
#include <stdio.h>
|
|||
|
#include <string.h>
|
|||
|
#include <sys/time.h>
|
|||
|
|
|||
|
+/* 8 gb */
|
|||
|
+static const int64_t kBytes = 8LL << 30;
|
|||
|
+
|
|||
|
static const char data[] =
|
|||
|
"POST /joyent/http-parser HTTP/1.1\r\n"
|
|||
|
"Host: github.com\r\n"
|
|||
|
@@ -38,7 +42,7 @@ static const char data[] =
|
|||
|
"Referer: https://github.com/joyent/http-parser\r\n"
|
|||
|
"Connection: keep-alive\r\n"
|
|||
|
"Transfer-Encoding: chunked\r\n"
|
|||
|
- "Cache-Control: max-age=0\r\n\r\nb\r\nhello world\r\n0\r\n\r\n";
|
|||
|
+ "Cache-Control: max-age=0\r\n\r\nb\r\nhello world\r\n0\r\n";
|
|||
|
static const size_t data_len = sizeof(data) - 1;
|
|||
|
|
|||
|
static int on_info(http_parser* p) {
|
|||
|
@@ -67,13 +71,13 @@ int bench(int iter_count, int silent) {
|
|||
|
int err;
|
|||
|
struct timeval start;
|
|||
|
struct timeval end;
|
|||
|
- float rps;
|
|||
|
|
|||
|
if (!silent) {
|
|||
|
err = gettimeofday(&start, NULL);
|
|||
|
assert(err == 0);
|
|||
|
}
|
|||
|
|
|||
|
+ fprintf(stderr, "req_len=%d\n", (int) data_len);
|
|||
|
for (i = 0; i < iter_count; i++) {
|
|||
|
size_t parsed;
|
|||
|
http_parser_init(&parser, HTTP_REQUEST);
|
|||
|
@@ -83,17 +87,27 @@ int bench(int iter_count, int silent) {
|
|||
|
}
|
|||
|
|
|||
|
if (!silent) {
|
|||
|
+ double elapsed;
|
|||
|
+ double bw;
|
|||
|
+ double total;
|
|||
|
+
|
|||
|
err = gettimeofday(&end, NULL);
|
|||
|
assert(err == 0);
|
|||
|
|
|||
|
fprintf(stdout, "Benchmark result:\n");
|
|||
|
|
|||
|
- rps = (float) (end.tv_sec - start.tv_sec) +
|
|||
|
- (end.tv_usec - start.tv_usec) * 1e-6f;
|
|||
|
- fprintf(stdout, "Took %f seconds to run\n", rps);
|
|||
|
+ elapsed = (double) (end.tv_sec - start.tv_sec) +
|
|||
|
+ (end.tv_usec - start.tv_usec) * 1e-6f;
|
|||
|
+
|
|||
|
+ total = (double) iter_count * data_len;
|
|||
|
+ bw = (double) total / elapsed;
|
|||
|
+
|
|||
|
+ fprintf(stdout, "%.2f mb | %.2f mb/s | %.2f req/sec | %.2f s\n",
|
|||
|
+ (double) total / (1024 * 1024),
|
|||
|
+ bw / (1024 * 1024),
|
|||
|
+ (double) iter_count / elapsed,
|
|||
|
+ elapsed);
|
|||
|
|
|||
|
- rps = (float) iter_count / rps;
|
|||
|
- fprintf(stdout, "%f req/sec\n", rps);
|
|||
|
fflush(stdout);
|
|||
|
}
|
|||
|
|
|||
|
@@ -101,11 +115,14 @@ int bench(int iter_count, int silent) {
|
|||
|
}
|
|||
|
|
|||
|
int main(int argc, char** argv) {
|
|||
|
+ int64_t iterations;
|
|||
|
+
|
|||
|
+ iterations = kBytes / (int64_t) data_len;
|
|||
|
if (argc == 2 && strcmp(argv[1], "infinite") == 0) {
|
|||
|
for (;;)
|
|||
|
- bench(5000000, 1);
|
|||
|
+ bench(iterations, 1);
|
|||
|
return 0;
|
|||
|
} else {
|
|||
|
- return bench(5000000, 0);
|
|||
|
+ return bench(iterations, 0);
|
|||
|
}
|
|||
|
}
|
|||
|
Index: node-v8.17.0/deps/http_parser/http_parser.c
|
|||
|
===================================================================
|
|||
|
--- node-v8.17.0.orig/deps/http_parser/http_parser.c
|
|||
|
+++ node-v8.17.0/deps/http_parser/http_parser.c
|
|||
|
@@ -51,6 +51,7 @@ static uint32_t max_header_size = HTTP_M
|
|||
|
|
|||
|
#define SET_ERRNO(e) \
|
|||
|
do { \
|
|||
|
+ parser->nread = nread; \
|
|||
|
parser->http_errno = (e); \
|
|||
|
} while(0)
|
|||
|
|
|||
|
@@ -58,6 +59,7 @@ do {
|
|||
|
#define UPDATE_STATE(V) p_state = (enum state) (V);
|
|||
|
#define RETURN(V) \
|
|||
|
do { \
|
|||
|
+ parser->nread = nread; \
|
|||
|
parser->state = CURRENT_STATE(); \
|
|||
|
return (V); \
|
|||
|
} while (0);
|
|||
|
@@ -151,8 +153,8 @@ do {
|
|||
|
*/
|
|||
|
#define COUNT_HEADER_SIZE(V) \
|
|||
|
do { \
|
|||
|
- parser->nread += (V); \
|
|||
|
- if (UNLIKELY(parser->nread > max_header_size)) { \
|
|||
|
+ nread += (uint32_t)(V); \
|
|||
|
+ if (UNLIKELY(nread > max_header_size)) { \
|
|||
|
SET_ERRNO(HPE_HEADER_OVERFLOW); \
|
|||
|
goto error; \
|
|||
|
} \
|
|||
|
@@ -194,7 +196,7 @@ static const char tokens[256] = {
|
|||
|
/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
|
|||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|||
|
/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
|
|||
|
- 0, '!', 0, '#', '$', '%', '&', '\'',
|
|||
|
+ ' ', '!', 0, '#', '$', '%', '&', '\'',
|
|||
|
/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
|
|||
|
0, 0, '*', '+', 0, '-', '.', 0,
|
|||
|
/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
|
|||
|
@@ -314,6 +316,8 @@ enum state
|
|||
|
, s_req_http_HT
|
|||
|
, s_req_http_HTT
|
|||
|
, s_req_http_HTTP
|
|||
|
+ , s_req_http_I
|
|||
|
+ , s_req_http_IC
|
|||
|
, s_req_http_major
|
|||
|
, s_req_http_dot
|
|||
|
, s_req_http_minor
|
|||
|
@@ -377,7 +381,10 @@ enum header_states
|
|||
|
, h_transfer_encoding
|
|||
|
, h_upgrade
|
|||
|
|
|||
|
+ , h_matching_transfer_encoding_token_start
|
|||
|
, h_matching_transfer_encoding_chunked
|
|||
|
+ , h_matching_transfer_encoding_token
|
|||
|
+
|
|||
|
, h_matching_connection_token_start
|
|||
|
, h_matching_connection_keep_alive
|
|||
|
, h_matching_connection_close
|
|||
|
@@ -421,14 +428,14 @@ enum http_host_state
|
|||
|
(c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
|
|||
|
(c) == '$' || (c) == ',')
|
|||
|
|
|||
|
-#define STRICT_TOKEN(c) (tokens[(unsigned char)c])
|
|||
|
+#define STRICT_TOKEN(c) ((c == ' ') ? 0 : tokens[(unsigned char)c])
|
|||
|
|
|||
|
#if HTTP_PARSER_STRICT
|
|||
|
-#define TOKEN(c) (tokens[(unsigned char)c])
|
|||
|
+#define TOKEN(c) STRICT_TOKEN(c)
|
|||
|
#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
|
|||
|
#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
|
|||
|
#else
|
|||
|
-#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c])
|
|||
|
+#define TOKEN(c) tokens[(unsigned char)c]
|
|||
|
#define IS_URL_CHAR(c) \
|
|||
|
(BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
|
|||
|
#define IS_HOST_CHAR(c) \
|
|||
|
@@ -542,7 +549,7 @@ parse_url_char(enum state s, const char
|
|||
|
return s_dead;
|
|||
|
}
|
|||
|
|
|||
|
- /* FALLTHROUGH */
|
|||
|
+ /* fall through */
|
|||
|
case s_req_server_start:
|
|||
|
case s_req_server:
|
|||
|
if (ch == '/') {
|
|||
|
@@ -646,6 +653,7 @@ size_t http_parser_execute (http_parser
|
|||
|
const char *status_mark = 0;
|
|||
|
enum state p_state = (enum state) parser->state;
|
|||
|
const unsigned int lenient = parser->lenient_http_headers;
|
|||
|
+ uint32_t nread = parser->nread;
|
|||
|
|
|||
|
/* We're in an error state. Don't bother doing anything. */
|
|||
|
if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
|
|||
|
@@ -757,21 +765,16 @@ reexecute:
|
|||
|
|
|||
|
case s_start_res:
|
|||
|
{
|
|||
|
+ if (ch == CR || ch == LF)
|
|||
|
+ break;
|
|||
|
parser->flags = 0;
|
|||
|
parser->content_length = ULLONG_MAX;
|
|||
|
|
|||
|
- switch (ch) {
|
|||
|
- case 'H':
|
|||
|
- UPDATE_STATE(s_res_H);
|
|||
|
- break;
|
|||
|
-
|
|||
|
- case CR:
|
|||
|
- case LF:
|
|||
|
- break;
|
|||
|
-
|
|||
|
- default:
|
|||
|
- SET_ERRNO(HPE_INVALID_CONSTANT);
|
|||
|
- goto error;
|
|||
|
+ if (ch == 'H') {
|
|||
|
+ UPDATE_STATE(s_res_H);
|
|||
|
+ } else {
|
|||
|
+ SET_ERRNO(HPE_INVALID_CONSTANT);
|
|||
|
+ goto error;
|
|||
|
}
|
|||
|
|
|||
|
CALLBACK_NOTIFY(message_begin);
|
|||
|
@@ -1088,11 +1091,17 @@ reexecute:
|
|||
|
|
|||
|
case s_req_http_start:
|
|||
|
switch (ch) {
|
|||
|
+ case ' ':
|
|||
|
+ break;
|
|||
|
case 'H':
|
|||
|
UPDATE_STATE(s_req_http_H);
|
|||
|
break;
|
|||
|
- case ' ':
|
|||
|
- break;
|
|||
|
+ case 'I':
|
|||
|
+ if (parser->method == HTTP_SOURCE) {
|
|||
|
+ UPDATE_STATE(s_req_http_I);
|
|||
|
+ break;
|
|||
|
+ }
|
|||
|
+ /* fall through */
|
|||
|
default:
|
|||
|
SET_ERRNO(HPE_INVALID_CONSTANT);
|
|||
|
goto error;
|
|||
|
@@ -1114,6 +1123,16 @@ reexecute:
|
|||
|
UPDATE_STATE(s_req_http_HTTP);
|
|||
|
break;
|
|||
|
|
|||
|
+ case s_req_http_I:
|
|||
|
+ STRICT_CHECK(ch != 'C');
|
|||
|
+ UPDATE_STATE(s_req_http_IC);
|
|||
|
+ break;
|
|||
|
+
|
|||
|
+ case s_req_http_IC:
|
|||
|
+ STRICT_CHECK(ch != 'E');
|
|||
|
+ UPDATE_STATE(s_req_http_HTTP); /* Treat "ICE" as "HTTP". */
|
|||
|
+ break;
|
|||
|
+
|
|||
|
case s_req_http_HTTP:
|
|||
|
STRICT_CHECK(ch != '/');
|
|||
|
UPDATE_STATE(s_req_http_major);
|
|||
|
@@ -1240,8 +1259,14 @@ reexecute:
|
|||
|
break;
|
|||
|
|
|||
|
switch (parser->header_state) {
|
|||
|
- case h_general:
|
|||
|
+ case h_general: {
|
|||
|
+ size_t left = data + len - p;
|
|||
|
+ const char* pe = p + MIN(left, max_header_size);
|
|||
|
+ while (p+1 < pe && TOKEN(p[1])) {
|
|||
|
+ p++;
|
|||
|
+ }
|
|||
|
break;
|
|||
|
+ }
|
|||
|
|
|||
|
case h_C:
|
|||
|
parser->index++;
|
|||
|
@@ -1313,6 +1338,7 @@ reexecute:
|
|||
|
parser->header_state = h_general;
|
|||
|
} else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
|
|||
|
parser->header_state = h_transfer_encoding;
|
|||
|
+ parser->flags |= F_TRANSFER_ENCODING;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
@@ -1341,13 +1367,14 @@ reexecute:
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
- COUNT_HEADER_SIZE(p - start);
|
|||
|
-
|
|||
|
if (p == data + len) {
|
|||
|
--p;
|
|||
|
+ COUNT_HEADER_SIZE(p - start);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
+ COUNT_HEADER_SIZE(p - start);
|
|||
|
+
|
|||
|
if (ch == ':') {
|
|||
|
UPDATE_STATE(s_header_value_discard_ws);
|
|||
|
CALLBACK_DATA(header_field);
|
|||
|
@@ -1371,7 +1398,7 @@ reexecute:
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
- /* FALLTHROUGH */
|
|||
|
+ /* fall through */
|
|||
|
|
|||
|
case s_header_value_start:
|
|||
|
{
|
|||
|
@@ -1393,10 +1420,14 @@ reexecute:
|
|||
|
if ('c' == c) {
|
|||
|
parser->header_state = h_matching_transfer_encoding_chunked;
|
|||
|
} else {
|
|||
|
- parser->header_state = h_general;
|
|||
|
+ parser->header_state = h_matching_transfer_encoding_token;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
+ /* Multi-value `Transfer-Encoding` header */
|
|||
|
+ case h_matching_transfer_encoding_token_start:
|
|||
|
+ break;
|
|||
|
+
|
|||
|
case h_content_length:
|
|||
|
if (UNLIKELY(!IS_NUM(ch))) {
|
|||
|
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
|
|||
|
@@ -1413,6 +1444,11 @@ reexecute:
|
|||
|
parser->header_state = h_content_length_num;
|
|||
|
break;
|
|||
|
|
|||
|
+ /* when obsolete line folding is encountered for content length
|
|||
|
+ * continue to the s_header_value state */
|
|||
|
+ case h_content_length_ws:
|
|||
|
+ break;
|
|||
|
+
|
|||
|
case h_connection:
|
|||
|
/* looking for 'Connection: keep-alive' */
|
|||
|
if (c == 'k') {
|
|||
|
@@ -1468,29 +1504,25 @@ reexecute:
|
|||
|
|
|||
|
switch (h_state) {
|
|||
|
case h_general:
|
|||
|
- {
|
|||
|
- const char* p_cr;
|
|||
|
- const char* p_lf;
|
|||
|
- size_t limit = data + len - p;
|
|||
|
-
|
|||
|
- limit = MIN(limit, max_header_size);
|
|||
|
-
|
|||
|
- p_cr = (const char*) memchr(p, CR, limit);
|
|||
|
- p_lf = (const char*) memchr(p, LF, limit);
|
|||
|
- if (p_cr != NULL) {
|
|||
|
- if (p_lf != NULL && p_cr >= p_lf)
|
|||
|
- p = p_lf;
|
|||
|
- else
|
|||
|
- p = p_cr;
|
|||
|
- } else if (UNLIKELY(p_lf != NULL)) {
|
|||
|
- p = p_lf;
|
|||
|
- } else {
|
|||
|
- p = data + len;
|
|||
|
+ {
|
|||
|
+ size_t left = data + len - p;
|
|||
|
+ const char* pe = p + MIN(left, max_header_size);
|
|||
|
+
|
|||
|
+ for (; p != pe; p++) {
|
|||
|
+ ch = *p;
|
|||
|
+ if (ch == CR || ch == LF) {
|
|||
|
+ --p;
|
|||
|
+ break;
|
|||
|
+ }
|
|||
|
+ if (!lenient && !IS_HEADER_CHAR(ch)) {
|
|||
|
+ SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
|
|||
|
+ goto error;
|
|||
|
+ }
|
|||
|
+ }
|
|||
|
+ if (p == data + len)
|
|||
|
+ --p;
|
|||
|
+ break;
|
|||
|
}
|
|||
|
- --p;
|
|||
|
-
|
|||
|
- break;
|
|||
|
- }
|
|||
|
|
|||
|
case h_connection:
|
|||
|
case h_transfer_encoding:
|
|||
|
@@ -1500,7 +1532,7 @@ reexecute:
|
|||
|
case h_content_length:
|
|||
|
if (ch == ' ') break;
|
|||
|
h_state = h_content_length_num;
|
|||
|
- /* FALLTHROUGH */
|
|||
|
+ /* fall through */
|
|||
|
|
|||
|
case h_content_length_num:
|
|||
|
{
|
|||
|
@@ -1539,16 +1571,41 @@ reexecute:
|
|||
|
goto error;
|
|||
|
|
|||
|
/* Transfer-Encoding: chunked */
|
|||
|
+ case h_matching_transfer_encoding_token_start:
|
|||
|
+ /* looking for 'Transfer-Encoding: chunked' */
|
|||
|
+ if ('c' == c) {
|
|||
|
+ h_state = h_matching_transfer_encoding_chunked;
|
|||
|
+ } else if (STRICT_TOKEN(c)) {
|
|||
|
+ /* TODO(indutny): similar code below does this, but why?
|
|||
|
+ * At the very least it seems to be inconsistent given that
|
|||
|
+ * h_matching_transfer_encoding_token does not check for
|
|||
|
+ * `STRICT_TOKEN`
|
|||
|
+ */
|
|||
|
+ h_state = h_matching_transfer_encoding_token;
|
|||
|
+ } else if (c == ' ' || c == '\t') {
|
|||
|
+ /* Skip lws */
|
|||
|
+ } else {
|
|||
|
+ h_state = h_general;
|
|||
|
+ }
|
|||
|
+ break;
|
|||
|
+
|
|||
|
case h_matching_transfer_encoding_chunked:
|
|||
|
parser->index++;
|
|||
|
if (parser->index > sizeof(CHUNKED)-1
|
|||
|
|| c != CHUNKED[parser->index]) {
|
|||
|
- h_state = h_general;
|
|||
|
+ h_state = h_matching_transfer_encoding_token;
|
|||
|
} else if (parser->index == sizeof(CHUNKED)-2) {
|
|||
|
h_state = h_transfer_encoding_chunked;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
+ case h_matching_transfer_encoding_token:
|
|||
|
+ if (ch == ',') {
|
|||
|
+ h_state = h_matching_transfer_encoding_token_start;
|
|||
|
+ parser->index = 0;
|
|||
|
+ }
|
|||
|
+ break;
|
|||
|
+
|
|||
|
case h_matching_connection_token_start:
|
|||
|
/* looking for 'Connection: keep-alive' */
|
|||
|
if (c == 'k') {
|
|||
|
@@ -1607,7 +1664,7 @@ reexecute:
|
|||
|
break;
|
|||
|
|
|||
|
case h_transfer_encoding_chunked:
|
|||
|
- if (ch != ' ') h_state = h_general;
|
|||
|
+ if (ch != ' ') h_state = h_matching_transfer_encoding_token;
|
|||
|
break;
|
|||
|
|
|||
|
case h_connection_keep_alive:
|
|||
|
@@ -1636,10 +1693,10 @@ reexecute:
|
|||
|
}
|
|||
|
parser->header_state = h_state;
|
|||
|
|
|||
|
- COUNT_HEADER_SIZE(p - start);
|
|||
|
-
|
|||
|
if (p == data + len)
|
|||
|
--p;
|
|||
|
+
|
|||
|
+ COUNT_HEADER_SIZE(p - start);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
@@ -1657,6 +1714,10 @@ reexecute:
|
|||
|
case s_header_value_lws:
|
|||
|
{
|
|||
|
if (ch == ' ' || ch == '\t') {
|
|||
|
+ if (parser->header_state == h_content_length_num) {
|
|||
|
+ /* treat obsolete line folding as space */
|
|||
|
+ parser->header_state = h_content_length_ws;
|
|||
|
+ }
|
|||
|
UPDATE_STATE(s_header_value_start);
|
|||
|
REEXECUTE();
|
|||
|
}
|
|||
|
@@ -1709,6 +1770,11 @@ reexecute:
|
|||
|
case h_transfer_encoding_chunked:
|
|||
|
parser->flags |= F_CHUNKED;
|
|||
|
break;
|
|||
|
+ case h_content_length:
|
|||
|
+ /* do not allow empty content length */
|
|||
|
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
|
|||
|
+ goto error;
|
|||
|
+ break;
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
@@ -1732,12 +1798,17 @@ reexecute:
|
|||
|
REEXECUTE();
|
|||
|
}
|
|||
|
|
|||
|
- /* Cannot use chunked encoding and a content-length header together
|
|||
|
- per the HTTP specification. */
|
|||
|
- if ((parser->flags & F_CHUNKED) &&
|
|||
|
+ /* Cannot us transfer-encoding and a content-length header together
|
|||
|
+ per the HTTP specification. (RFC 7230 Section 3.3.3) */
|
|||
|
+ if ((parser->flags & F_TRANSFER_ENCODING) &&
|
|||
|
(parser->flags & F_CONTENTLENGTH)) {
|
|||
|
- SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
|
|||
|
- goto error;
|
|||
|
+ /* Allow it for lenient parsing as long as `Transfer-Encoding` is
|
|||
|
+ * not `chunked`
|
|||
|
+ */
|
|||
|
+ if (!lenient || (parser->flags & F_CHUNKED)) {
|
|||
|
+ SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
|
|||
|
+ goto error;
|
|||
|
+ }
|
|||
|
}
|
|||
|
|
|||
|
UPDATE_STATE(s_headers_done);
|
|||
|
@@ -1772,7 +1843,7 @@ reexecute:
|
|||
|
case 2:
|
|||
|
parser->upgrade = 1;
|
|||
|
|
|||
|
- /* FALLTHROUGH */
|
|||
|
+ /* fall through */
|
|||
|
case 1:
|
|||
|
parser->flags |= F_SKIPBODY;
|
|||
|
break;
|
|||
|
@@ -1796,6 +1867,7 @@ reexecute:
|
|||
|
STRICT_CHECK(ch != LF);
|
|||
|
|
|||
|
parser->nread = 0;
|
|||
|
+ nread = 0;
|
|||
|
|
|||
|
hasBody = parser->flags & F_CHUNKED ||
|
|||
|
(parser->content_length > 0 && parser->content_length != ULLONG_MAX);
|
|||
|
@@ -1811,8 +1883,31 @@ reexecute:
|
|||
|
UPDATE_STATE(NEW_MESSAGE());
|
|||
|
CALLBACK_NOTIFY(message_complete);
|
|||
|
} else if (parser->flags & F_CHUNKED) {
|
|||
|
- /* chunked encoding - ignore Content-Length header */
|
|||
|
+ /* chunked encoding - ignore Content-Length header,
|
|||
|
+ * prepare for a chunk */
|
|||
|
UPDATE_STATE(s_chunk_size_start);
|
|||
|
+ } else if (parser->flags & F_TRANSFER_ENCODING) {
|
|||
|
+ if (parser->type == HTTP_REQUEST && !lenient) {
|
|||
|
+ /* RFC 7230 3.3.3 */
|
|||
|
+
|
|||
|
+ /* If a Transfer-Encoding header field
|
|||
|
+ * is present in a request and the chunked transfer coding is not
|
|||
|
+ * the final encoding, the message body length cannot be determined
|
|||
|
+ * reliably; the server MUST respond with the 400 (Bad Request)
|
|||
|
+ * status code and then close the connection.
|
|||
|
+ */
|
|||
|
+ SET_ERRNO(HPE_INVALID_TRANSFER_ENCODING);
|
|||
|
+ RETURN(p - data); /* Error */
|
|||
|
+ } else {
|
|||
|
+ /* RFC 7230 3.3.3 */
|
|||
|
+
|
|||
|
+ /* If a Transfer-Encoding header field is present in a response and
|
|||
|
+ * the chunked transfer coding is not the final encoding, the
|
|||
|
+ * message body length is determined by reading the connection until
|
|||
|
+ * it is closed by the server.
|
|||
|
+ */
|
|||
|
+ UPDATE_STATE(s_body_identity_eof);
|
|||
|
+ }
|
|||
|
} else {
|
|||
|
if (parser->content_length == 0) {
|
|||
|
/* Content-Length header given but zero: Content-Length: 0\r\n */
|
|||
|
@@ -1890,7 +1985,7 @@ reexecute:
|
|||
|
|
|||
|
case s_chunk_size_start:
|
|||
|
{
|
|||
|
- assert(parser->nread == 1);
|
|||
|
+ assert(nread == 1);
|
|||
|
assert(parser->flags & F_CHUNKED);
|
|||
|
|
|||
|
unhex_val = unhex[(unsigned char)ch];
|
|||
|
@@ -1958,6 +2053,7 @@ reexecute:
|
|||
|
STRICT_CHECK(ch != LF);
|
|||
|
|
|||
|
parser->nread = 0;
|
|||
|
+ nread = 0;
|
|||
|
|
|||
|
if (parser->content_length == 0) {
|
|||
|
parser->flags |= F_TRAILING;
|
|||
|
@@ -2004,6 +2100,7 @@ reexecute:
|
|||
|
assert(parser->flags & F_CHUNKED);
|
|||
|
STRICT_CHECK(ch != LF);
|
|||
|
parser->nread = 0;
|
|||
|
+ nread = 0;
|
|||
|
UPDATE_STATE(s_chunk_size_start);
|
|||
|
CALLBACK_NOTIFY(chunk_complete);
|
|||
|
break;
|
|||
|
@@ -2015,7 +2112,7 @@ reexecute:
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
- /* Run callbacks for any marks that we have leftover after we ran our of
|
|||
|
+ /* Run callbacks for any marks that we have leftover after we ran out of
|
|||
|
* bytes. There should be at most one of these set, so it's OK to invoke
|
|||
|
* them in series (unset marks will not result in callbacks).
|
|||
|
*
|
|||
|
@@ -2064,6 +2161,12 @@ http_message_needs_eof (const http_parse
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
+ /* RFC 7230 3.3.3, see `s_headers_almost_done` */
|
|||
|
+ if ((parser->flags & F_TRANSFER_ENCODING) &&
|
|||
|
+ (parser->flags & F_CHUNKED) == 0) {
|
|||
|
+ return 1;
|
|||
|
+ }
|
|||
|
+
|
|||
|
if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
|
|||
|
return 0;
|
|||
|
}
|
|||
|
@@ -2097,6 +2200,16 @@ http_method_str (enum http_method m)
|
|||
|
return ELEM_AT(method_strings, m, "<unknown>");
|
|||
|
}
|
|||
|
|
|||
|
+const char *
|
|||
|
+http_status_str (enum http_status s)
|
|||
|
+{
|
|||
|
+ switch (s) {
|
|||
|
+#define XX(num, name, string) case HTTP_STATUS_##name: return #string;
|
|||
|
+ HTTP_STATUS_MAP(XX)
|
|||
|
+#undef XX
|
|||
|
+ default: return "<unknown>";
|
|||
|
+ }
|
|||
|
+}
|
|||
|
|
|||
|
void
|
|||
|
http_parser_init (http_parser *parser, enum http_parser_type t)
|
|||
|
@@ -2157,7 +2270,7 @@ http_parse_host_char(enum http_host_stat
|
|||
|
return s_http_host;
|
|||
|
}
|
|||
|
|
|||
|
- /* FALLTHROUGH */
|
|||
|
+ /* fall through */
|
|||
|
case s_http_host_v6_end:
|
|||
|
if (ch == ':') {
|
|||
|
return s_http_host_port_start;
|
|||
|
@@ -2170,7 +2283,7 @@ http_parse_host_char(enum http_host_stat
|
|||
|
return s_http_host_v6_end;
|
|||
|
}
|
|||
|
|
|||
|
- /* FALLTHROUGH */
|
|||
|
+ /* fall through */
|
|||
|
case s_http_host_v6_start:
|
|||
|
if (IS_HEX(ch) || ch == ':' || ch == '.') {
|
|||
|
return s_http_host_v6;
|
|||
|
@@ -2186,7 +2299,7 @@ http_parse_host_char(enum http_host_stat
|
|||
|
return s_http_host_v6_end;
|
|||
|
}
|
|||
|
|
|||
|
- /* FALLTHROUGH */
|
|||
|
+ /* fall through */
|
|||
|
case s_http_host_v6_zone_start:
|
|||
|
/* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
|
|||
|
if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
|
|||
|
@@ -2211,12 +2324,13 @@ http_parse_host_char(enum http_host_stat
|
|||
|
|
|||
|
static int
|
|||
|
http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
|
|||
|
- assert(u->field_set & (1 << UF_HOST));
|
|||
|
enum http_host_state s;
|
|||
|
|
|||
|
const char *p;
|
|||
|
size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
|
|||
|
|
|||
|
+ assert(u->field_set & (1 << UF_HOST));
|
|||
|
+
|
|||
|
u->field_data[UF_HOST].len = 0;
|
|||
|
|
|||
|
s = found_at ? s_http_userinfo_start : s_http_host_start;
|
|||
|
@@ -2231,14 +2345,14 @@ http_parse_host(const char * buf, struct
|
|||
|
switch(new_s) {
|
|||
|
case s_http_host:
|
|||
|
if (s != s_http_host) {
|
|||
|
- u->field_data[UF_HOST].off = p - buf;
|
|||
|
+ u->field_data[UF_HOST].off = (uint16_t)(p - buf);
|
|||
|
}
|
|||
|
u->field_data[UF_HOST].len++;
|
|||
|
break;
|
|||
|
|
|||
|
case s_http_host_v6:
|
|||
|
if (s != s_http_host_v6) {
|
|||
|
- u->field_data[UF_HOST].off = p - buf;
|
|||
|
+ u->field_data[UF_HOST].off = (uint16_t)(p - buf);
|
|||
|
}
|
|||
|
u->field_data[UF_HOST].len++;
|
|||
|
break;
|
|||
|
@@ -2250,7 +2364,7 @@ http_parse_host(const char * buf, struct
|
|||
|
|
|||
|
case s_http_host_port:
|
|||
|
if (s != s_http_host_port) {
|
|||
|
- u->field_data[UF_PORT].off = p - buf;
|
|||
|
+ u->field_data[UF_PORT].off = (uint16_t)(p - buf);
|
|||
|
u->field_data[UF_PORT].len = 0;
|
|||
|
u->field_set |= (1 << UF_PORT);
|
|||
|
}
|
|||
|
@@ -2259,7 +2373,7 @@ http_parse_host(const char * buf, struct
|
|||
|
|
|||
|
case s_http_userinfo:
|
|||
|
if (s != s_http_userinfo) {
|
|||
|
- u->field_data[UF_USERINFO].off = p - buf ;
|
|||
|
+ u->field_data[UF_USERINFO].off = (uint16_t)(p - buf);
|
|||
|
u->field_data[UF_USERINFO].len = 0;
|
|||
|
u->field_set |= (1 << UF_USERINFO);
|
|||
|
}
|
|||
|
@@ -2304,6 +2418,10 @@ http_parser_parse_url(const char *buf, s
|
|||
|
enum http_parser_url_fields uf, old_uf;
|
|||
|
int found_at = 0;
|
|||
|
|
|||
|
+ if (buflen == 0) {
|
|||
|
+ return 1;
|
|||
|
+ }
|
|||
|
+
|
|||
|
u->port = u->field_set = 0;
|
|||
|
s = is_connect ? s_req_server_start : s_req_spaces_before_url;
|
|||
|
old_uf = UF_MAX;
|
|||
|
@@ -2331,7 +2449,7 @@ http_parser_parse_url(const char *buf, s
|
|||
|
case s_req_server_with_at:
|
|||
|
found_at = 1;
|
|||
|
|
|||
|
- /* FALLTHROUGH */
|
|||
|
+ /* fall through */
|
|||
|
case s_req_server:
|
|||
|
uf = UF_HOST;
|
|||
|
break;
|
|||
|
@@ -2359,7 +2477,7 @@ http_parser_parse_url(const char *buf, s
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
- u->field_data[uf].off = p - buf;
|
|||
|
+ u->field_data[uf].off = (uint16_t)(p - buf);
|
|||
|
u->field_data[uf].len = 1;
|
|||
|
|
|||
|
u->field_set |= (1 << uf);
|
|||
|
@@ -2422,6 +2540,7 @@ http_parser_pause(http_parser *parser, i
|
|||
|
*/
|
|||
|
if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
|
|||
|
HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
|
|||
|
+ uint32_t nread = parser->nread; /* used by the SET_ERRNO macro */
|
|||
|
SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
|
|||
|
} else {
|
|||
|
assert(0 && "Attempting to pause parser in error state");
|
|||
|
Index: node-v8.17.0/deps/http_parser/http_parser.gyp
|
|||
|
===================================================================
|
|||
|
--- node-v8.17.0.orig/deps/http_parser/http_parser.gyp
|
|||
|
+++ node-v8.17.0/deps/http_parser/http_parser.gyp
|
|||
|
@@ -56,7 +56,7 @@
|
|||
|
'defines': [ 'HTTP_PARSER_STRICT=0' ],
|
|||
|
'include_dirs': [ '.' ],
|
|||
|
},
|
|||
|
- 'defines': [ 'HTTP_MAX_HEADER_SIZE=8192', 'HTTP_PARSER_STRICT=0' ],
|
|||
|
+ 'defines': [ 'HTTP_PARSER_STRICT=0' ],
|
|||
|
'sources': [ './http_parser.c', ],
|
|||
|
'conditions': [
|
|||
|
['OS=="win"', {
|
|||
|
@@ -79,7 +79,7 @@
|
|||
|
'defines': [ 'HTTP_PARSER_STRICT=1' ],
|
|||
|
'include_dirs': [ '.' ],
|
|||
|
},
|
|||
|
- 'defines': [ 'HTTP_MAX_HEADER_SIZE=8192', 'HTTP_PARSER_STRICT=1' ],
|
|||
|
+ 'defines': [ 'HTTP_PARSER_STRICT=1' ],
|
|||
|
'sources': [ './http_parser.c', ],
|
|||
|
'conditions': [
|
|||
|
['OS=="win"', {
|
|||
|
Index: node-v8.17.0/deps/http_parser/http_parser.h
|
|||
|
===================================================================
|
|||
|
--- node-v8.17.0.orig/deps/http_parser/http_parser.h
|
|||
|
+++ node-v8.17.0/deps/http_parser/http_parser.h
|
|||
|
@@ -26,8 +26,8 @@ extern "C" {
|
|||
|
|
|||
|
/* Also update SONAME in the Makefile whenever you change these. */
|
|||
|
#define HTTP_PARSER_VERSION_MAJOR 2
|
|||
|
-#define HTTP_PARSER_VERSION_MINOR 8
|
|||
|
-#define HTTP_PARSER_VERSION_PATCH 0
|
|||
|
+#define HTTP_PARSER_VERSION_MINOR 9
|
|||
|
+#define HTTP_PARSER_VERSION_PATCH 3
|
|||
|
|
|||
|
#include <stddef.h>
|
|||
|
#if defined(_WIN32) && !defined(__MINGW32__) && \
|
|||
|
@@ -225,6 +225,7 @@ enum flags
|
|||
|
, F_UPGRADE = 1 << 5
|
|||
|
, F_SKIPBODY = 1 << 6
|
|||
|
, F_CONTENTLENGTH = 1 << 7
|
|||
|
+ , F_TRANSFER_ENCODING = 1 << 8
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
@@ -271,6 +272,8 @@ enum flags
|
|||
|
"unexpected content-length header") \
|
|||
|
XX(INVALID_CHUNK_SIZE, \
|
|||
|
"invalid character in chunk size header") \
|
|||
|
+ XX(INVALID_TRANSFER_ENCODING, \
|
|||
|
+ "request has invalid transfer-encoding") \
|
|||
|
XX(INVALID_CONSTANT, "invalid constant string") \
|
|||
|
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
|
|||
|
XX(STRICT, "strict mode assertion failed") \
|
|||
|
@@ -293,11 +296,11 @@ enum http_errno {
|
|||
|
struct http_parser {
|
|||
|
/** PRIVATE **/
|
|||
|
unsigned int type : 2; /* enum http_parser_type */
|
|||
|
- unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
|
|||
|
unsigned int state : 7; /* enum state from http_parser.c */
|
|||
|
unsigned int header_state : 7; /* enum header_state from http_parser.c */
|
|||
|
unsigned int index : 7; /* index into current matcher */
|
|||
|
unsigned int lenient_http_headers : 1;
|
|||
|
+ unsigned int flags : 16; /* F_* values from 'flags' enum; semi-public */
|
|||
|
|
|||
|
uint32_t nread; /* # bytes read in various scenarios */
|
|||
|
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
|
|||
|
@@ -407,6 +410,9 @@ int http_should_keep_alive(const http_pa
|
|||
|
/* Returns a string version of the HTTP method. */
|
|||
|
const char *http_method_str(enum http_method m);
|
|||
|
|
|||
|
+/* Returns a string version of the HTTP status code. */
|
|||
|
+const char *http_status_str(enum http_status s);
|
|||
|
+
|
|||
|
/* Return a string name of the given error */
|
|||
|
const char *http_errno_name(enum http_errno err);
|
|||
|
|
|||
|
Index: node-v8.17.0/deps/http_parser/test.c
|
|||
|
===================================================================
|
|||
|
--- node-v8.17.0.orig/deps/http_parser/test.c
|
|||
|
+++ node-v8.17.0/deps/http_parser/test.c
|
|||
|
@@ -27,9 +27,7 @@
|
|||
|
#include <stdarg.h>
|
|||
|
|
|||
|
#if defined(__APPLE__)
|
|||
|
-# undef strlcat
|
|||
|
# undef strlncpy
|
|||
|
-# undef strlcpy
|
|||
|
#endif /* defined(__APPLE__) */
|
|||
|
|
|||
|
#undef TRUE
|
|||
|
@@ -43,7 +41,9 @@
|
|||
|
|
|||
|
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
|||
|
|
|||
|
-static http_parser *parser;
|
|||
|
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
|
|||
|
+
|
|||
|
+static http_parser parser;
|
|||
|
|
|||
|
struct message {
|
|||
|
const char *name; // for debugging purposes
|
|||
|
@@ -153,10 +153,10 @@ const struct message requests[] =
|
|||
|
,.body= ""
|
|||
|
}
|
|||
|
|
|||
|
-#define DUMBFUCK 2
|
|||
|
-, {.name= "dumbfuck"
|
|||
|
+#define DUMBLUCK 2
|
|||
|
+, {.name= "dumbluck"
|
|||
|
,.type= HTTP_REQUEST
|
|||
|
- ,.raw= "GET /dumbfuck HTTP/1.1\r\n"
|
|||
|
+ ,.raw= "GET /dumbluck HTTP/1.1\r\n"
|
|||
|
"aaaaaaaaaaaaa:++++++++++\r\n"
|
|||
|
"\r\n"
|
|||
|
,.should_keep_alive= TRUE
|
|||
|
@@ -166,8 +166,8 @@ const struct message requests[] =
|
|||
|
,.method= HTTP_GET
|
|||
|
,.query_string= ""
|
|||
|
,.fragment= ""
|
|||
|
- ,.request_path= "/dumbfuck"
|
|||
|
- ,.request_url= "/dumbfuck"
|
|||
|
+ ,.request_path= "/dumbluck"
|
|||
|
+ ,.request_url= "/dumbluck"
|
|||
|
,.num_headers= 1
|
|||
|
,.headers=
|
|||
|
{ { "aaaaaaaaaaaaa", "++++++++++" }
|
|||
|
@@ -262,7 +262,6 @@ const struct message requests[] =
|
|||
|
,.type= HTTP_REQUEST
|
|||
|
,.raw= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n"
|
|||
|
"Accept: */*\r\n"
|
|||
|
- "Transfer-Encoding: identity\r\n"
|
|||
|
"Content-Length: 5\r\n"
|
|||
|
"\r\n"
|
|||
|
"World"
|
|||
|
@@ -275,10 +274,9 @@ const struct message requests[] =
|
|||
|
,.fragment= "hey"
|
|||
|
,.request_path= "/post_identity_body_world"
|
|||
|
,.request_url= "/post_identity_body_world?q=search#hey"
|
|||
|
- ,.num_headers= 3
|
|||
|
+ ,.num_headers= 2
|
|||
|
,.headers=
|
|||
|
{ { "Accept", "*/*" }
|
|||
|
- , { "Transfer-Encoding", "identity" }
|
|||
|
, { "Content-Length", "5" }
|
|||
|
}
|
|||
|
,.body= "World"
|
|||
|
@@ -371,13 +369,13 @@ const struct message requests[] =
|
|||
|
,.chunk_lengths= { 5, 6 }
|
|||
|
}
|
|||
|
|
|||
|
-#define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11
|
|||
|
-, {.name= "with bullshit after the length"
|
|||
|
+#define CHUNKED_W_NONSENSE_AFTER_LENGTH 11
|
|||
|
+, {.name= "with nonsense after the length"
|
|||
|
,.type= HTTP_REQUEST
|
|||
|
- ,.raw= "POST /chunked_w_bullshit_after_length HTTP/1.1\r\n"
|
|||
|
+ ,.raw= "POST /chunked_w_nonsense_after_length HTTP/1.1\r\n"
|
|||
|
"Transfer-Encoding: chunked\r\n"
|
|||
|
"\r\n"
|
|||
|
- "5; ihatew3;whatthefuck=aretheseparametersfor\r\nhello\r\n"
|
|||
|
+ "5; ilovew3;whattheluck=aretheseparametersfor\r\nhello\r\n"
|
|||
|
"6; blahblah; blah\r\n world\r\n"
|
|||
|
"0\r\n"
|
|||
|
"\r\n"
|
|||
|
@@ -388,8 +386,8 @@ const struct message requests[] =
|
|||
|
,.method= HTTP_POST
|
|||
|
,.query_string= ""
|
|||
|
,.fragment= ""
|
|||
|
- ,.request_path= "/chunked_w_bullshit_after_length"
|
|||
|
- ,.request_url= "/chunked_w_bullshit_after_length"
|
|||
|
+ ,.request_path= "/chunked_w_nonsense_after_length"
|
|||
|
+ ,.request_url= "/chunked_w_nonsense_after_length"
|
|||
|
,.num_headers= 1
|
|||
|
,.headers=
|
|||
|
{ { "Transfer-Encoding", "chunked" }
|
|||
|
@@ -1174,7 +1172,80 @@ const struct message requests[] =
|
|||
|
,.body= ""
|
|||
|
}
|
|||
|
|
|||
|
-, {.name= NULL } /* sentinel */
|
|||
|
+#define SOURCE_ICE_REQUEST 42
|
|||
|
+, {.name = "source request"
|
|||
|
+ ,.type= HTTP_REQUEST
|
|||
|
+ ,.raw= "SOURCE /music/sweet/music ICE/1.0\r\n"
|
|||
|
+ "Host: example.com\r\n"
|
|||
|
+ "\r\n"
|
|||
|
+ ,.should_keep_alive= FALSE
|
|||
|
+ ,.message_complete_on_eof= FALSE
|
|||
|
+ ,.http_major= 1
|
|||
|
+ ,.http_minor= 0
|
|||
|
+ ,.method= HTTP_SOURCE
|
|||
|
+ ,.request_path= "/music/sweet/music"
|
|||
|
+ ,.request_url= "/music/sweet/music"
|
|||
|
+ ,.query_string= ""
|
|||
|
+ ,.fragment= ""
|
|||
|
+ ,.num_headers= 1
|
|||
|
+ ,.headers= { { "Host", "example.com" } }
|
|||
|
+ ,.body= ""
|
|||
|
+ }
|
|||
|
+
|
|||
|
+#define POST_MULTI_TE_LAST_CHUNKED 43
|
|||
|
+, {.name= "post - multi coding transfer-encoding chunked body"
|
|||
|
+ ,.type= HTTP_REQUEST
|
|||
|
+ ,.raw= "POST / HTTP/1.1\r\n"
|
|||
|
+ "Transfer-Encoding: deflate, chunked\r\n"
|
|||
|
+ "\r\n"
|
|||
|
+ "1e\r\nall your base are belong to us\r\n"
|
|||
|
+ "0\r\n"
|
|||
|
+ "\r\n"
|
|||
|
+ ,.should_keep_alive= TRUE
|
|||
|
+ ,.message_complete_on_eof= FALSE
|
|||
|
+ ,.http_major= 1
|
|||
|
+ ,.http_minor= 1
|
|||
|
+ ,.method= HTTP_POST
|
|||
|
+ ,.query_string= ""
|
|||
|
+ ,.fragment= ""
|
|||
|
+ ,.request_path= "/"
|
|||
|
+ ,.request_url= "/"
|
|||
|
+ ,.num_headers= 1
|
|||
|
+ ,.headers=
|
|||
|
+ { { "Transfer-Encoding" , "deflate, chunked" }
|
|||
|
+ }
|
|||
|
+ ,.body= "all your base are belong to us"
|
|||
|
+ ,.num_chunks_complete= 2
|
|||
|
+ ,.chunk_lengths= { 0x1e }
|
|||
|
+ }
|
|||
|
+
|
|||
|
+#define POST_MULTI_LINE_TE_LAST_CHUNKED 43
|
|||
|
+, {.name= "post - multi coding transfer-encoding chunked body"
|
|||
|
+ ,.type= HTTP_REQUEST
|
|||
|
+ ,.raw= "POST / HTTP/1.1\r\n"
|
|||
|
+ "Transfer-Encoding: deflate,\r\n"
|
|||
|
+ " chunked\r\n"
|
|||
|
+ "\r\n"
|
|||
|
+ "1e\r\nall your base are belong to us\r\n"
|
|||
|
+ "0\r\n"
|
|||
|
+ "\r\n"
|
|||
|
+ ,.should_keep_alive= TRUE
|
|||
|
+ ,.message_complete_on_eof= FALSE
|
|||
|
+ ,.http_major= 1
|
|||
|
+ ,.http_minor= 1
|
|||
|
+ ,.method= HTTP_POST
|
|||
|
+ ,.query_string= ""
|
|||
|
+ ,.fragment= ""
|
|||
|
+ ,.request_path= "/"
|
|||
|
+ ,.request_url= "/"
|
|||
|
+ ,.num_headers= 1
|
|||
|
+ ,.headers=
|
|||
|
+ { { "Transfer-Encoding" , "deflate, chunked" }
|
|||
|
+ }
|
|||
|
+ ,.body= "all your base are belong to us"
|
|||
|
+ ,.num_chunks_complete= 2
|
|||
|
+ ,.chunk_lengths= { 0x1e }
|
|||
|
+ }
|
|||
|
};
|
|||
|
|
|||
|
/* * R E S P O N S E S * */
|
|||
|
@@ -1952,8 +2023,28 @@ const struct message responses[] =
|
|||
|
,.num_chunks_complete= 3
|
|||
|
,.chunk_lengths= { 2, 2 }
|
|||
|
}
|
|||
|
-
|
|||
|
-, {.name= NULL } /* sentinel */
|
|||
|
+#define HTTP_200_MULTI_TE_NOT_LAST_CHUNKED 28
|
|||
|
+, {.name= "HTTP 200 response with `chunked` being *not last* Transfer-Encoding"
|
|||
|
+ ,.type= HTTP_RESPONSE
|
|||
|
+ ,.raw= "HTTP/1.1 200 OK\r\n"
|
|||
|
+ "Transfer-Encoding: chunked, identity\r\n"
|
|||
|
+ "\r\n"
|
|||
|
+ "2\r\n"
|
|||
|
+ "OK\r\n"
|
|||
|
+ "0\r\n"
|
|||
|
+ "\r\n"
|
|||
|
+ ,.should_keep_alive= FALSE
|
|||
|
+ ,.message_complete_on_eof= TRUE
|
|||
|
+ ,.http_major= 1
|
|||
|
+ ,.http_minor= 1
|
|||
|
+ ,.status_code= 200
|
|||
|
+ ,.response_status= "OK"
|
|||
|
+ ,.num_headers= 1
|
|||
|
+ ,.headers= { { "Transfer-Encoding", "chunked, identity" }
|
|||
|
+ }
|
|||
|
+ ,.body= "2\r\nOK\r\n0\r\n\r\n"
|
|||
|
+ ,.num_chunks_complete= 0
|
|||
|
+ }
|
|||
|
};
|
|||
|
|
|||
|
/* strnlen() is a POSIX.2008 addition. Can't rely on it being available so
|
|||
|
@@ -1994,12 +2085,6 @@ strlncat(char *dst, size_t len, const ch
|
|||
|
}
|
|||
|
|
|||
|
size_t
|
|||
|
-strlcat(char *dst, const char *src, size_t len)
|
|||
|
-{
|
|||
|
- return strlncat(dst, len, src, (size_t) -1);
|
|||
|
-}
|
|||
|
-
|
|||
|
-size_t
|
|||
|
strlncpy(char *dst, size_t len, const char *src, size_t n)
|
|||
|
{
|
|||
|
size_t slen;
|
|||
|
@@ -2017,16 +2102,10 @@ strlncpy(char *dst, size_t len, const ch
|
|||
|
return slen;
|
|||
|
}
|
|||
|
|
|||
|
-size_t
|
|||
|
-strlcpy(char *dst, const char *src, size_t len)
|
|||
|
-{
|
|||
|
- return strlncpy(dst, len, src, (size_t) -1);
|
|||
|
-}
|
|||
|
-
|
|||
|
int
|
|||
|
request_url_cb (http_parser *p, const char *buf, size_t len)
|
|||
|
{
|
|||
|
- assert(p == parser);
|
|||
|
+ assert(p == &parser);
|
|||
|
strlncat(messages[num_messages].request_url,
|
|||
|
sizeof(messages[num_messages].request_url),
|
|||
|
buf,
|
|||
|
@@ -2037,7 +2116,7 @@ request_url_cb (http_parser *p, const ch
|
|||
|
int
|
|||
|
header_field_cb (http_parser *p, const char *buf, size_t len)
|
|||
|
{
|
|||
|
- assert(p == parser);
|
|||
|
+ assert(p == &parser);
|
|||
|
struct message *m = &messages[num_messages];
|
|||
|
|
|||
|
if (m->last_header_element != FIELD)
|
|||
|
@@ -2056,7 +2135,7 @@ header_field_cb (http_parser *p, const c
|
|||
|
int
|
|||
|
header_value_cb (http_parser *p, const char *buf, size_t len)
|
|||
|
{
|
|||
|
- assert(p == parser);
|
|||
|
+ assert(p == &parser);
|
|||
|
struct message *m = &messages[num_messages];
|
|||
|
|
|||
|
strlncat(m->headers[m->num_headers-1][1],
|
|||
|
@@ -2085,7 +2164,7 @@ check_body_is_final (const http_parser *
|
|||
|
int
|
|||
|
body_cb (http_parser *p, const char *buf, size_t len)
|
|||
|
{
|
|||
|
- assert(p == parser);
|
|||
|
+ assert(p == &parser);
|
|||
|
strlncat(messages[num_messages].body,
|
|||
|
sizeof(messages[num_messages].body),
|
|||
|
buf,
|
|||
|
@@ -2099,7 +2178,7 @@ body_cb (http_parser *p, const char *buf
|
|||
|
int
|
|||
|
count_body_cb (http_parser *p, const char *buf, size_t len)
|
|||
|
{
|
|||
|
- assert(p == parser);
|
|||
|
+ assert(p == &parser);
|
|||
|
assert(buf);
|
|||
|
messages[num_messages].body_size += len;
|
|||
|
check_body_is_final(p);
|
|||
|
@@ -2109,7 +2188,8 @@ count_body_cb (http_parser *p, const cha
|
|||
|
int
|
|||
|
message_begin_cb (http_parser *p)
|
|||
|
{
|
|||
|
- assert(p == parser);
|
|||
|
+ assert(p == &parser);
|
|||
|
+ assert(!messages[num_messages].message_begin_cb_called);
|
|||
|
messages[num_messages].message_begin_cb_called = TRUE;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
@@ -2117,21 +2197,22 @@ message_begin_cb (http_parser *p)
|
|||
|
int
|
|||
|
headers_complete_cb (http_parser *p)
|
|||
|
{
|
|||
|
- assert(p == parser);
|
|||
|
- messages[num_messages].method = parser->method;
|
|||
|
- messages[num_messages].status_code = parser->status_code;
|
|||
|
- messages[num_messages].http_major = parser->http_major;
|
|||
|
- messages[num_messages].http_minor = parser->http_minor;
|
|||
|
+ assert(p == &parser);
|
|||
|
+ messages[num_messages].method = parser.method;
|
|||
|
+ messages[num_messages].status_code = parser.status_code;
|
|||
|
+ messages[num_messages].http_major = parser.http_major;
|
|||
|
+ messages[num_messages].http_minor = parser.http_minor;
|
|||
|
messages[num_messages].headers_complete_cb_called = TRUE;
|
|||
|
- messages[num_messages].should_keep_alive = http_should_keep_alive(parser);
|
|||
|
+ messages[num_messages].should_keep_alive = http_should_keep_alive(&parser);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
int
|
|||
|
message_complete_cb (http_parser *p)
|
|||
|
{
|
|||
|
- assert(p == parser);
|
|||
|
- if (messages[num_messages].should_keep_alive != http_should_keep_alive(parser))
|
|||
|
+ assert(p == &parser);
|
|||
|
+ if (messages[num_messages].should_keep_alive !=
|
|||
|
+ http_should_keep_alive(&parser))
|
|||
|
{
|
|||
|
fprintf(stderr, "\n\n *** Error http_should_keep_alive() should have same "
|
|||
|
"value in both on_message_complete and on_headers_complete "
|
|||
|
@@ -2162,7 +2243,7 @@ message_complete_cb (http_parser *p)
|
|||
|
int
|
|||
|
response_status_cb (http_parser *p, const char *buf, size_t len)
|
|||
|
{
|
|||
|
- assert(p == parser);
|
|||
|
+ assert(p == &parser);
|
|||
|
|
|||
|
messages[num_messages].status_cb_called = TRUE;
|
|||
|
|
|||
|
@@ -2176,7 +2257,7 @@ response_status_cb (http_parser *p, cons
|
|||
|
int
|
|||
|
chunk_header_cb (http_parser *p)
|
|||
|
{
|
|||
|
- assert(p == parser);
|
|||
|
+ assert(p == &parser);
|
|||
|
int chunk_idx = messages[num_messages].num_chunks;
|
|||
|
messages[num_messages].num_chunks++;
|
|||
|
if (chunk_idx < MAX_CHUNKS) {
|
|||
|
@@ -2189,7 +2270,7 @@ chunk_header_cb (http_parser *p)
|
|||
|
int
|
|||
|
chunk_complete_cb (http_parser *p)
|
|||
|
{
|
|||
|
- assert(p == parser);
|
|||
|
+ assert(p == &parser);
|
|||
|
|
|||
|
/* Here we want to verify that each chunk_header_cb is matched by a
|
|||
|
* chunk_complete_cb, so not only should the total number of calls to
|
|||
|
@@ -2394,7 +2475,7 @@ connect_headers_complete_cb (http_parser
|
|||
|
int
|
|||
|
connect_message_complete_cb (http_parser *p)
|
|||
|
{
|
|||
|
- messages[num_messages].should_keep_alive = http_should_keep_alive(parser);
|
|||
|
+ messages[num_messages].should_keep_alive = http_should_keep_alive(&parser);
|
|||
|
return message_complete_cb(p);
|
|||
|
}
|
|||
|
|
|||
|
@@ -2467,30 +2548,15 @@ void
|
|||
|
parser_init (enum http_parser_type type)
|
|||
|
{
|
|||
|
num_messages = 0;
|
|||
|
-
|
|||
|
- assert(parser == NULL);
|
|||
|
-
|
|||
|
- parser = malloc(sizeof(http_parser));
|
|||
|
-
|
|||
|
- http_parser_init(parser, type);
|
|||
|
-
|
|||
|
+ http_parser_init(&parser, type);
|
|||
|
memset(&messages, 0, sizeof messages);
|
|||
|
-
|
|||
|
-}
|
|||
|
-
|
|||
|
-void
|
|||
|
-parser_free ()
|
|||
|
-{
|
|||
|
- assert(parser);
|
|||
|
- free(parser);
|
|||
|
- parser = NULL;
|
|||
|
}
|
|||
|
|
|||
|
size_t parse (const char *buf, size_t len)
|
|||
|
{
|
|||
|
size_t nparsed;
|
|||
|
currently_parsing_eof = (len == 0);
|
|||
|
- nparsed = http_parser_execute(parser, &settings, buf, len);
|
|||
|
+ nparsed = http_parser_execute(&parser, &settings, buf, len);
|
|||
|
return nparsed;
|
|||
|
}
|
|||
|
|
|||
|
@@ -2498,7 +2564,7 @@ size_t parse_count_body (const char *buf
|
|||
|
{
|
|||
|
size_t nparsed;
|
|||
|
currently_parsing_eof = (len == 0);
|
|||
|
- nparsed = http_parser_execute(parser, &settings_count_body, buf, len);
|
|||
|
+ nparsed = http_parser_execute(&parser, &settings_count_body, buf, len);
|
|||
|
return nparsed;
|
|||
|
}
|
|||
|
|
|||
|
@@ -2509,7 +2575,7 @@ size_t parse_pause (const char *buf, siz
|
|||
|
|
|||
|
currently_parsing_eof = (len == 0);
|
|||
|
current_pause_parser = &s;
|
|||
|
- nparsed = http_parser_execute(parser, current_pause_parser, buf, len);
|
|||
|
+ nparsed = http_parser_execute(&parser, current_pause_parser, buf, len);
|
|||
|
return nparsed;
|
|||
|
}
|
|||
|
|
|||
|
@@ -2517,7 +2583,7 @@ size_t parse_connect (const char *buf, s
|
|||
|
{
|
|||
|
size_t nparsed;
|
|||
|
currently_parsing_eof = (len == 0);
|
|||
|
- nparsed = http_parser_execute(parser, &settings_connect, buf, len);
|
|||
|
+ nparsed = http_parser_execute(&parser, &settings_connect, buf, len);
|
|||
|
return nparsed;
|
|||
|
}
|
|||
|
|
|||
|
@@ -2737,7 +2803,7 @@ static void
|
|||
|
print_error (const char *raw, size_t error_location)
|
|||
|
{
|
|||
|
fprintf(stderr, "\n*** %s ***\n\n",
|
|||
|
- http_errno_description(HTTP_PARSER_ERRNO(parser)));
|
|||
|
+ http_errno_description(HTTP_PARSER_ERRNO(&parser)));
|
|||
|
|
|||
|
int this_line = 0, char_len = 0;
|
|||
|
size_t i, j, len = strlen(raw), error_location_line = 0;
|
|||
|
@@ -3280,6 +3346,24 @@ const struct url_test url_tests[] =
|
|||
|
,.rv=1 /* s_dead */
|
|||
|
}
|
|||
|
|
|||
|
+, {.name="empty url"
|
|||
|
+ ,.url=""
|
|||
|
+ ,.is_connect=0
|
|||
|
+ ,.rv=1
|
|||
|
+ }
|
|||
|
+
|
|||
|
+, {.name="NULL url"
|
|||
|
+ ,.url=NULL
|
|||
|
+ ,.is_connect=0
|
|||
|
+ ,.rv=1
|
|||
|
+ }
|
|||
|
+
|
|||
|
+, {.name="full of spaces url"
|
|||
|
+ ,.url=" "
|
|||
|
+ ,.is_connect=0
|
|||
|
+ ,.rv=1
|
|||
|
+ }
|
|||
|
+
|
|||
|
#if HTTP_PARSER_STRICT
|
|||
|
|
|||
|
, {.name="tab in URL"
|
|||
|
@@ -3364,7 +3448,7 @@ test_parse_url (void)
|
|||
|
memset(&u, 0, sizeof(u));
|
|||
|
|
|||
|
rv = http_parser_parse_url(test->url,
|
|||
|
- strlen(test->url),
|
|||
|
+ test->url ? strlen(test->url) : 0,
|
|||
|
test->is_connect,
|
|||
|
&u);
|
|||
|
|
|||
|
@@ -3405,6 +3489,14 @@ test_method_str (void)
|
|||
|
}
|
|||
|
|
|||
|
void
|
|||
|
+test_status_str (void)
|
|||
|
+{
|
|||
|
+ assert(0 == strcmp("OK", http_status_str(HTTP_STATUS_OK)));
|
|||
|
+ assert(0 == strcmp("Not Found", http_status_str(HTTP_STATUS_NOT_FOUND)));
|
|||
|
+ assert(0 == strcmp("<unknown>", http_status_str(1337)));
|
|||
|
+}
|
|||
|
+
|
|||
|
+void
|
|||
|
test_message (const struct message *message)
|
|||
|
{
|
|||
|
size_t raw_len = strlen(message->raw);
|
|||
|
@@ -3418,9 +3510,18 @@ test_message (const struct message *mess
|
|||
|
size_t msg2len = raw_len - msg1len;
|
|||
|
|
|||
|
if (msg1len) {
|
|||
|
+ assert(num_messages == 0);
|
|||
|
+ messages[0].headers_complete_cb_called = FALSE;
|
|||
|
+
|
|||
|
read = parse(msg1, msg1len);
|
|||
|
|
|||
|
- if (message->upgrade && parser->upgrade && num_messages > 0) {
|
|||
|
+ if (!messages[0].headers_complete_cb_called && parser.nread != read) {
|
|||
|
+ assert(parser.nread == read);
|
|||
|
+ print_error(msg1, read);
|
|||
|
+ abort();
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ if (message->upgrade && parser.upgrade && num_messages > 0) {
|
|||
|
messages[num_messages - 1].upgrade = msg1 + read;
|
|||
|
goto test;
|
|||
|
}
|
|||
|
@@ -3434,7 +3535,7 @@ test_message (const struct message *mess
|
|||
|
|
|||
|
read = parse(msg2, msg2len);
|
|||
|
|
|||
|
- if (message->upgrade && parser->upgrade) {
|
|||
|
+ if (message->upgrade && parser.upgrade) {
|
|||
|
messages[num_messages - 1].upgrade = msg2 + read;
|
|||
|
goto test;
|
|||
|
}
|
|||
|
@@ -3459,8 +3560,6 @@ test_message (const struct message *mess
|
|||
|
}
|
|||
|
|
|||
|
if(!message_eq(0, 0, message)) abort();
|
|||
|
-
|
|||
|
- parser_free();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
@@ -3496,8 +3595,6 @@ test_message_count_body (const struct me
|
|||
|
}
|
|||
|
|
|||
|
if(!message_eq(0, 0, message)) abort();
|
|||
|
-
|
|||
|
- parser_free();
|
|||
|
}
|
|||
|
|
|||
|
void
|
|||
|
@@ -3510,11 +3607,9 @@ test_simple_type (const char *buf,
|
|||
|
enum http_errno err;
|
|||
|
|
|||
|
parse(buf, strlen(buf));
|
|||
|
- err = HTTP_PARSER_ERRNO(parser);
|
|||
|
+ err = HTTP_PARSER_ERRNO(&parser);
|
|||
|
parse(NULL, 0);
|
|||
|
|
|||
|
- parser_free();
|
|||
|
-
|
|||
|
/* In strict mode, allow us to pass with an unexpected HPE_STRICT as
|
|||
|
* long as the caller isn't expecting success.
|
|||
|
*/
|
|||
|
@@ -3643,7 +3738,7 @@ test_chunked_content_length_error (int r
|
|||
|
parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
|
|||
|
assert(parsed == strlen(buf));
|
|||
|
|
|||
|
- buf = "Transfer-Encoding: chunked\r\nContent-Length: 1\r\n\r\n";
|
|||
|
+ buf = "Transfer-Encoding: anything\r\nContent-Length: 1\r\n\r\n";
|
|||
|
size_t buflen = strlen(buf);
|
|||
|
|
|||
|
parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
|
|||
|
@@ -3854,7 +3949,7 @@ test_multiple3 (const struct message *r1
|
|||
|
|
|||
|
read = parse(total, strlen(total));
|
|||
|
|
|||
|
- if (parser->upgrade) {
|
|||
|
+ if (parser.upgrade) {
|
|||
|
upgrade_message_fix(total, read, 3, r1, r2, r3);
|
|||
|
goto test;
|
|||
|
}
|
|||
|
@@ -3881,8 +3976,6 @@ test:
|
|||
|
if (!message_eq(0, 0, r1)) abort();
|
|||
|
if (message_count > 1 && !message_eq(1, 0, r2)) abort();
|
|||
|
if (message_count > 2 && !message_eq(2, 0, r3)) abort();
|
|||
|
-
|
|||
|
- parser_free();
|
|||
|
}
|
|||
|
|
|||
|
/* SCAN through every possible breaking to make sure the
|
|||
|
@@ -3936,9 +4029,17 @@ test_scan (const struct message *r1, con
|
|||
|
strlncpy(buf3, sizeof(buf1), total+j, buf3_len);
|
|||
|
buf3[buf3_len] = 0;
|
|||
|
|
|||
|
+ assert(num_messages == 0);
|
|||
|
+ messages[0].headers_complete_cb_called = FALSE;
|
|||
|
+
|
|||
|
read = parse(buf1, buf1_len);
|
|||
|
|
|||
|
- if (parser->upgrade) goto test;
|
|||
|
+ if (!messages[0].headers_complete_cb_called && parser.nread != read) {
|
|||
|
+ print_error(buf1, read);
|
|||
|
+ goto error;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ if (parser.upgrade) goto test;
|
|||
|
|
|||
|
if (read != buf1_len) {
|
|||
|
print_error(buf1, read);
|
|||
|
@@ -3947,7 +4048,7 @@ test_scan (const struct message *r1, con
|
|||
|
|
|||
|
read += parse(buf2, buf2_len);
|
|||
|
|
|||
|
- if (parser->upgrade) goto test;
|
|||
|
+ if (parser.upgrade) goto test;
|
|||
|
|
|||
|
if (read != buf1_len + buf2_len) {
|
|||
|
print_error(buf2, read);
|
|||
|
@@ -3956,7 +4057,7 @@ test_scan (const struct message *r1, con
|
|||
|
|
|||
|
read += parse(buf3, buf3_len);
|
|||
|
|
|||
|
- if (parser->upgrade) goto test;
|
|||
|
+ if (parser.upgrade) goto test;
|
|||
|
|
|||
|
if (read != buf1_len + buf2_len + buf3_len) {
|
|||
|
print_error(buf3, read);
|
|||
|
@@ -3966,7 +4067,7 @@ test_scan (const struct message *r1, con
|
|||
|
parse(NULL, 0);
|
|||
|
|
|||
|
test:
|
|||
|
- if (parser->upgrade) {
|
|||
|
+ if (parser.upgrade) {
|
|||
|
upgrade_message_fix(total, read, 3, r1, r2, r3);
|
|||
|
}
|
|||
|
|
|||
|
@@ -3990,8 +4091,6 @@ test:
|
|||
|
fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
|
|||
|
goto error;
|
|||
|
}
|
|||
|
-
|
|||
|
- parser_free();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
@@ -4055,7 +4154,7 @@ test_message_pause (const struct message
|
|||
|
// completion callback.
|
|||
|
if (messages[0].message_complete_cb_called &&
|
|||
|
msg->upgrade &&
|
|||
|
- parser->upgrade) {
|
|||
|
+ parser.upgrade) {
|
|||
|
messages[0].upgrade = buf + nread;
|
|||
|
goto test;
|
|||
|
}
|
|||
|
@@ -4063,17 +4162,16 @@ test_message_pause (const struct message
|
|||
|
if (nread < buflen) {
|
|||
|
|
|||
|
// Not much do to if we failed a strict-mode check
|
|||
|
- if (HTTP_PARSER_ERRNO(parser) == HPE_STRICT) {
|
|||
|
- parser_free();
|
|||
|
+ if (HTTP_PARSER_ERRNO(&parser) == HPE_STRICT) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
- assert (HTTP_PARSER_ERRNO(parser) == HPE_PAUSED);
|
|||
|
+ assert (HTTP_PARSER_ERRNO(&parser) == HPE_PAUSED);
|
|||
|
}
|
|||
|
|
|||
|
buf += nread;
|
|||
|
buflen -= nread;
|
|||
|
- http_parser_pause(parser, 0);
|
|||
|
+ http_parser_pause(&parser, 0);
|
|||
|
} while (buflen > 0);
|
|||
|
|
|||
|
nread = parse_pause(NULL, 0);
|
|||
|
@@ -4086,8 +4184,6 @@ test:
|
|||
|
}
|
|||
|
|
|||
|
if(!message_eq(0, 0, msg)) abort();
|
|||
|
-
|
|||
|
- parser_free();
|
|||
|
}
|
|||
|
|
|||
|
/* Verify that body and next message won't be parsed in responses to CONNECT */
|
|||
|
@@ -4107,17 +4203,12 @@ test_message_connect (const struct messa
|
|||
|
}
|
|||
|
|
|||
|
if(!message_eq(0, 1, msg)) abort();
|
|||
|
-
|
|||
|
- parser_free();
|
|||
|
}
|
|||
|
|
|||
|
int
|
|||
|
main (void)
|
|||
|
{
|
|||
|
- parser = NULL;
|
|||
|
- int i, j, k;
|
|||
|
- int request_count;
|
|||
|
- int response_count;
|
|||
|
+ unsigned i, j, k;
|
|||
|
unsigned long version;
|
|||
|
unsigned major;
|
|||
|
unsigned minor;
|
|||
|
@@ -4131,13 +4222,11 @@ main (void)
|
|||
|
|
|||
|
printf("sizeof(http_parser) = %u\n", (unsigned int)sizeof(http_parser));
|
|||
|
|
|||
|
- for (request_count = 0; requests[request_count].name; request_count++);
|
|||
|
- for (response_count = 0; responses[response_count].name; response_count++);
|
|||
|
-
|
|||
|
//// API
|
|||
|
test_preserve_data();
|
|||
|
test_parse_url();
|
|||
|
test_method_str();
|
|||
|
+ test_status_str();
|
|||
|
|
|||
|
//// NREAD
|
|||
|
test_header_nread_value();
|
|||
|
@@ -4170,6 +4259,13 @@ main (void)
|
|||
|
|
|||
|
test_simple_type(
|
|||
|
"POST / HTTP/1.1\r\n"
|
|||
|
+ "Content-Length:\r\n" // empty
|
|||
|
+ "\r\n",
|
|||
|
+ HPE_INVALID_CONTENT_LENGTH,
|
|||
|
+ HTTP_REQUEST);
|
|||
|
+
|
|||
|
+ test_simple_type(
|
|||
|
+ "POST / HTTP/1.1\r\n"
|
|||
|
"Content-Length: 42 \r\n" // Note the surrounding whitespace.
|
|||
|
"\r\n",
|
|||
|
HPE_OK,
|
|||
|
@@ -4189,6 +4285,20 @@ main (void)
|
|||
|
HPE_INVALID_CONTENT_LENGTH,
|
|||
|
HTTP_REQUEST);
|
|||
|
|
|||
|
+ test_simple_type(
|
|||
|
+ "POST / HTTP/1.1\r\n"
|
|||
|
+ "Content-Length: 42\r\n"
|
|||
|
+ " Hello world!\r\n",
|
|||
|
+ HPE_INVALID_CONTENT_LENGTH,
|
|||
|
+ HTTP_REQUEST);
|
|||
|
+
|
|||
|
+ test_simple_type(
|
|||
|
+ "POST / HTTP/1.1\r\n"
|
|||
|
+ "Content-Length: 42\r\n"
|
|||
|
+ " \r\n",
|
|||
|
+ HPE_OK,
|
|||
|
+ HTTP_REQUEST);
|
|||
|
+
|
|||
|
//// RESPONSES
|
|||
|
|
|||
|
test_simple_type("HTP/1.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
|
|||
|
@@ -4196,24 +4306,25 @@ main (void)
|
|||
|
test_simple_type("HTTP/11.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
|
|||
|
test_simple_type("HTTP/1.01 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
|
|||
|
test_simple_type("HTTP/1.1\t200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
|
|||
|
+ test_simple_type("\rHTTP/1.1\t200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
|
|||
|
|
|||
|
- for (i = 0; i < response_count; i++) {
|
|||
|
+ for (i = 0; i < ARRAY_SIZE(responses); i++) {
|
|||
|
test_message(&responses[i]);
|
|||
|
}
|
|||
|
|
|||
|
- for (i = 0; i < response_count; i++) {
|
|||
|
+ for (i = 0; i < ARRAY_SIZE(responses); i++) {
|
|||
|
test_message_pause(&responses[i]);
|
|||
|
}
|
|||
|
|
|||
|
- for (i = 0; i < response_count; i++) {
|
|||
|
+ for (i = 0; i < ARRAY_SIZE(responses); i++) {
|
|||
|
test_message_connect(&responses[i]);
|
|||
|
}
|
|||
|
|
|||
|
- for (i = 0; i < response_count; i++) {
|
|||
|
+ for (i = 0; i < ARRAY_SIZE(responses); i++) {
|
|||
|
if (!responses[i].should_keep_alive) continue;
|
|||
|
- for (j = 0; j < response_count; j++) {
|
|||
|
+ for (j = 0; j < ARRAY_SIZE(responses); j++) {
|
|||
|
if (!responses[j].should_keep_alive) continue;
|
|||
|
- for (k = 0; k < response_count; k++) {
|
|||
|
+ for (k = 0; k < ARRAY_SIZE(responses); k++) {
|
|||
|
test_multiple3(&responses[i], &responses[j], &responses[k]);
|
|||
|
}
|
|||
|
}
|
|||
|
@@ -4273,11 +4384,16 @@ main (void)
|
|||
|
|
|||
|
/// REQUESTS
|
|||
|
|
|||
|
+ test_simple("GET / IHTTP/1.0\r\n\r\n", HPE_INVALID_CONSTANT);
|
|||
|
+ test_simple("GET / ICE/1.0\r\n\r\n", HPE_INVALID_CONSTANT);
|
|||
|
test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
|
|||
|
test_simple("GET / HTTP/01.1\r\n\r\n", HPE_INVALID_VERSION);
|
|||
|
test_simple("GET / HTTP/11.1\r\n\r\n", HPE_INVALID_VERSION);
|
|||
|
test_simple("GET / HTTP/1.01\r\n\r\n", HPE_INVALID_VERSION);
|
|||
|
|
|||
|
+ test_simple("GET / HTTP/1.0\r\nHello: w\1rld\r\n\r\n", HPE_INVALID_HEADER_TOKEN);
|
|||
|
+ test_simple("GET / HTTP/1.0\r\nHello: woooo\2rld\r\n\r\n", HPE_INVALID_HEADER_TOKEN);
|
|||
|
+
|
|||
|
// Extended characters - see nodejs/test/parallel/test-http-headers-obstext.js
|
|||
|
test_simple("GET / HTTP/1.1\r\n"
|
|||
|
"Test: Düsseldorf\r\n",
|
|||
|
@@ -4291,6 +4407,12 @@ main (void)
|
|||
|
"fooba",
|
|||
|
HPE_OK);
|
|||
|
|
|||
|
+ // Unknown Transfer-Encoding in request
|
|||
|
+ test_simple("GET / HTTP/1.1\r\n"
|
|||
|
+ "Transfer-Encoding: unknown\r\n"
|
|||
|
+ "\r\n",
|
|||
|
+ HPE_INVALID_TRANSFER_ENCODING);
|
|||
|
+
|
|||
|
static const char *all_methods[] = {
|
|||
|
"DELETE",
|
|||
|
"GET",
|
|||
|
@@ -4360,9 +4482,9 @@ main (void)
|
|||
|
"\r\n",
|
|||
|
HPE_INVALID_HEADER_TOKEN);
|
|||
|
|
|||
|
- const char *dumbfuck2 =
|
|||
|
+ const char *dumbluck2 =
|
|||
|
"GET / HTTP/1.1\r\n"
|
|||
|
- "X-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n"
|
|||
|
+ "X-SSL-Nonsense: -----BEGIN CERTIFICATE-----\r\n"
|
|||
|
"\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n"
|
|||
|
"\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n"
|
|||
|
"\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n"
|
|||
|
@@ -4395,7 +4517,7 @@ main (void)
|
|||
|
"\tRA==\r\n"
|
|||
|
"\t-----END CERTIFICATE-----\r\n"
|
|||
|
"\r\n";
|
|||
|
- test_simple(dumbfuck2, HPE_OK);
|
|||
|
+ test_simple(dumbluck2, HPE_OK);
|
|||
|
|
|||
|
const char *corrupted_connection =
|
|||
|
"GET / HTTP/1.1\r\n"
|
|||
|
@@ -4429,19 +4551,19 @@ main (void)
|
|||
|
|
|||
|
|
|||
|
/* check to make sure our predefined requests are okay */
|
|||
|
- for (i = 0; requests[i].name; i++) {
|
|||
|
+ for (i = 0; i < ARRAY_SIZE(requests); i++) {
|
|||
|
test_message(&requests[i]);
|
|||
|
}
|
|||
|
|
|||
|
- for (i = 0; i < request_count; i++) {
|
|||
|
+ for (i = 0; i < ARRAY_SIZE(requests); i++) {
|
|||
|
test_message_pause(&requests[i]);
|
|||
|
}
|
|||
|
|
|||
|
- for (i = 0; i < request_count; i++) {
|
|||
|
+ for (i = 0; i < ARRAY_SIZE(requests); i++) {
|
|||
|
if (!requests[i].should_keep_alive) continue;
|
|||
|
- for (j = 0; j < request_count; j++) {
|
|||
|
+ for (j = 0; j < ARRAY_SIZE(requests); j++) {
|
|||
|
if (!requests[j].should_keep_alive) continue;
|
|||
|
- for (k = 0; k < request_count; k++) {
|
|||
|
+ for (k = 0; k < ARRAY_SIZE(requests); k++) {
|
|||
|
test_multiple3(&requests[i], &requests[j], &requests[k]);
|
|||
|
}
|
|||
|
}
|
|||
|
@@ -4462,7 +4584,7 @@ main (void)
|
|||
|
printf("request scan 3/4 ");
|
|||
|
test_scan( &requests[TWO_CHUNKS_MULT_ZERO_END]
|
|||
|
, &requests[CHUNKED_W_TRAILING_HEADERS]
|
|||
|
- , &requests[CHUNKED_W_BULLSHIT_AFTER_LENGTH]
|
|||
|
+ , &requests[CHUNKED_W_NONSENSE_AFTER_LENGTH]
|
|||
|
);
|
|||
|
|
|||
|
printf("request scan 4/4 ");
|
|||
|
Index: node-v8.17.0/test/parallel/test-http-invalid-te.js
|
|||
|
===================================================================
|
|||
|
--- /dev/null
|
|||
|
+++ node-v8.17.0/test/parallel/test-http-invalid-te.js
|
|||
|
@@ -0,0 +1,40 @@
|
|||
|
+'use strict';
|
|||
|
+
|
|||
|
+const common = require('../common');
|
|||
|
+
|
|||
|
+// Test https://hackerone.com/reports/735748 is fixed.
|
|||
|
+
|
|||
|
+const assert = require('assert');
|
|||
|
+const http = require('http');
|
|||
|
+const net = require('net');
|
|||
|
+
|
|||
|
+const REQUEST_BB = `POST / HTTP/1.1
|
|||
|
+Content-Type: text/plain; charset=utf-8
|
|||
|
+Host: hacker.exploit.com
|
|||
|
+Connection: keep-alive
|
|||
|
+Content-Length: 10
|
|||
|
+Transfer-Encoding: chunked, eee
|
|||
|
+
|
|||
|
+HELLOWORLDPOST / HTTP/1.1
|
|||
|
+Content-Type: text/plain; charset=utf-8
|
|||
|
+Host: hacker.exploit.com
|
|||
|
+Connection: keep-alive
|
|||
|
+Content-Length: 28
|
|||
|
+
|
|||
|
+I AM A SMUGGLED REQUEST!!!
|
|||
|
+`;
|
|||
|
+
|
|||
|
+const server = http.createServer(common.mustNotCall());
|
|||
|
+
|
|||
|
+server.on('clientError', common.mustCall((err) => {
|
|||
|
+ assert.strictEqual(err.code, 'HPE_UNEXPECTED_CONTENT_LENGTH');
|
|||
|
+ server.close();
|
|||
|
+}));
|
|||
|
+
|
|||
|
+server.listen(0, common.mustCall(() => {
|
|||
|
+ const client = net.connect(
|
|||
|
+ server.address().port,
|
|||
|
+ common.mustCall(() => {
|
|||
|
+ client.end(REQUEST_BB.replace(/\n/g, '\r\n'));
|
|||
|
+ }));
|
|||
|
+}));
|
|||
|
Index: node-v8.17.0/test/parallel/test-http-insecure-parser.js
|
|||
|
===================================================================
|
|||
|
--- /dev/null
|
|||
|
+++ node-v8.17.0/test/parallel/test-http-insecure-parser.js
|
|||
|
@@ -0,0 +1,35 @@
|
|||
|
+// Flags: --insecure-http-parser
|
|||
|
+
|
|||
|
+'use strict';
|
|||
|
+const common = require('../common');
|
|||
|
+const assert = require('assert');
|
|||
|
+const http = require('http');
|
|||
|
+const net = require('net');
|
|||
|
+
|
|||
|
+const server = http.createServer(function(req, res) {
|
|||
|
+ assert.strictEqual(req.headers['content-type'], 'text/te\bt');
|
|||
|
+ req.pipe(res);
|
|||
|
+});
|
|||
|
+
|
|||
|
+server.listen(0, common.mustCall(function() {
|
|||
|
+ const bufs = [];
|
|||
|
+ const client = net.connect(
|
|||
|
+ this.address().port,
|
|||
|
+ function() {
|
|||
|
+ client.write(
|
|||
|
+ 'GET / HTTP/1.1\r\n' +
|
|||
|
+ 'Content-Type: text/te\x08t\r\n' +
|
|||
|
+ 'Connection: close\r\n\r\n');
|
|||
|
+ }
|
|||
|
+ );
|
|||
|
+ client.on('data', function(chunk) {
|
|||
|
+ bufs.push(chunk);
|
|||
|
+ });
|
|||
|
+ client.on('end', common.mustCall(function() {
|
|||
|
+ const head = Buffer.concat(bufs)
|
|||
|
+ .toString('latin1')
|
|||
|
+ .split('\r\n')[0];
|
|||
|
+ assert.strictEqual(head, 'HTTP/1.1 200 OK');
|
|||
|
+ server.close();
|
|||
|
+ }));
|
|||
|
+}));
|
|||
|
Index: node-v8.17.0/doc/api/cli.md
|
|||
|
===================================================================
|
|||
|
--- node-v8.17.0.orig/doc/api/cli.md
|
|||
|
+++ node-v8.17.0/doc/api/cli.md
|
|||
|
@@ -412,6 +412,17 @@ added: v8.15.0
|
|||
|
|
|||
|
Specify the maximum size, in bytes, of HTTP headers. Defaults to 8KB.
|
|||
|
|
|||
|
+### `--insecure-http-parser`
|
|||
|
+<!-- YAML
|
|||
|
+added: REPLACEME
|
|||
|
+-->
|
|||
|
+
|
|||
|
+Use an insecure HTTP parser that accepts invalid HTTP headers. This may allow
|
|||
|
+interoperability with non-conformant HTTP implementations. It may also allow
|
|||
|
+request smuggling and other HTTP attacks that rely on invalid headers being
|
|||
|
+accepted. Avoid using this option.
|
|||
|
+
|
|||
|
+
|
|||
|
## Environment Variables
|
|||
|
|
|||
|
### `NODE_DEBUG=module[,…]`
|
|||
|
Index: node-v8.17.0/doc/node.1
|
|||
|
===================================================================
|
|||
|
--- node-v8.17.0.orig/doc/node.1
|
|||
|
+++ node-v8.17.0/doc/node.1
|
|||
|
@@ -115,6 +115,13 @@ Set the host:port to be used when the in
|
|||
|
Specify the maximum size of HTTP headers in bytes. Defaults to 8KB.
|
|||
|
|
|||
|
.TP
|
|||
|
+.BR \-\-insecure\-http\-parser
|
|||
|
+Use an insecure HTTP parser that accepts invalid HTTP headers. This may allow
|
|||
|
+interoperability with non-conformant HTTP implementations. It may also allow
|
|||
|
+request smuggling and other HTTP attacks that rely on invalid headers being
|
|||
|
+accepted. Avoid using this option.
|
|||
|
+
|
|||
|
+.TP
|
|||
|
.BR \-\-no\-deprecation
|
|||
|
Silence deprecation warnings.
|
|||
|
|
|||
|
Index: node-v8.17.0/lib/_http_client.js
|
|||
|
===================================================================
|
|||
|
--- node-v8.17.0.orig/lib/_http_client.js
|
|||
|
+++ node-v8.17.0/lib/_http_client.js
|
|||
|
@@ -31,6 +31,7 @@ const {
|
|||
|
debug,
|
|||
|
freeParser,
|
|||
|
httpSocketSetup,
|
|||
|
+ isLenient,
|
|||
|
parsers
|
|||
|
} = require('_http_common');
|
|||
|
const { OutgoingMessage } = require('_http_outgoing');
|
|||
|
@@ -622,7 +623,7 @@ function tickOnSocket(req, socket) {
|
|||
|
var parser = parsers.alloc();
|
|||
|
req.socket = socket;
|
|||
|
req.connection = socket;
|
|||
|
- parser.reinitialize(HTTPParser.RESPONSE, parser[is_reused_symbol]);
|
|||
|
+ parser.reinitialize(HTTPParser.RESPONSE, parser[is_reused_symbol], isLenient());
|
|||
|
if (process.domain) {
|
|||
|
process.domain.add(parser);
|
|||
|
}
|
|||
|
Index: node-v8.17.0/lib/_http_common.js
|
|||
|
===================================================================
|
|||
|
--- node-v8.17.0.orig/lib/_http_common.js
|
|||
|
+++ node-v8.17.0/lib/_http_common.js
|
|||
|
@@ -164,7 +164,7 @@ function parserOnMessageComplete() {
|
|||
|
|
|||
|
|
|||
|
var parsers = new FreeList('parsers', 1000, function() {
|
|||
|
- var parser = new HTTPParser(HTTPParser.REQUEST);
|
|||
|
+ var parser = new HTTPParser(HTTPParser.REQUEST, isLenient());
|
|||
|
|
|||
|
parser._headers = [];
|
|||
|
parser._url = '';
|
|||
|
@@ -353,6 +353,16 @@ function checkInvalidHeaderChar(val) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
+let warnedLenient = false;
|
|||
|
+
|
|||
|
+function isLenient() {
|
|||
|
+ if (process.insecureHttpParser && !warnedLenient) {
|
|||
|
+ warnedLenient = true;
|
|||
|
+ process.emitWarning('Using insecure HTTP parsing');
|
|||
|
+ }
|
|||
|
+ return !!process.insecureHttpParser;
|
|||
|
+}
|
|||
|
+
|
|||
|
module.exports = {
|
|||
|
_checkInvalidHeaderChar: checkInvalidHeaderChar,
|
|||
|
_checkIsHttpToken: checkIsHttpToken,
|
|||
|
@@ -364,5 +374,6 @@ module.exports = {
|
|||
|
httpSocketSetup,
|
|||
|
methods,
|
|||
|
parsers,
|
|||
|
- kIncomingMessage
|
|||
|
+ kIncomingMessage,
|
|||
|
+ isLenient
|
|||
|
};
|
|||
|
Index: node-v8.17.0/lib/_http_server.js
|
|||
|
===================================================================
|
|||
|
--- node-v8.17.0.orig/lib/_http_server.js
|
|||
|
+++ node-v8.17.0/lib/_http_server.js
|
|||
|
@@ -34,6 +34,7 @@ const {
|
|||
|
chunkExpression,
|
|||
|
httpSocketSetup,
|
|||
|
kIncomingMessage,
|
|||
|
+ isLenient,
|
|||
|
_checkInvalidHeaderChar: checkInvalidHeaderChar
|
|||
|
} = require('_http_common');
|
|||
|
const { OutgoingMessage } = require('_http_outgoing');
|
|||
|
@@ -333,7 +334,7 @@ function connectionListenerInternal(serv
|
|||
|
socket.on('timeout', socketOnTimeout);
|
|||
|
|
|||
|
var parser = parsers.alloc();
|
|||
|
- parser.reinitialize(HTTPParser.REQUEST, parser[is_reused_symbol]);
|
|||
|
+ parser.reinitialize(HTTPParser.REQUEST, parser[is_reused_symbol], isLenient());
|
|||
|
parser.socket = socket;
|
|||
|
|
|||
|
// We are starting to wait for our headers.
|
|||
|
Index: node-v8.17.0/src/node.cc
|
|||
|
===================================================================
|
|||
|
--- node-v8.17.0.orig/src/node.cc
|
|||
|
+++ node-v8.17.0/src/node.cc
|
|||
|
@@ -206,6 +206,8 @@ uint64_t max_http_header_size = 8 * 1024
|
|||
|
// used by C++ modules as well
|
|||
|
bool no_deprecation = false;
|
|||
|
|
|||
|
+bool insecure_http_parser = false;
|
|||
|
+
|
|||
|
#if HAVE_OPENSSL
|
|||
|
// use OpenSSL's cert store instead of bundled certs
|
|||
|
bool ssl_openssl_cert_store =
|
|||
|
@@ -2865,6 +2867,11 @@ void SetupProcessObject(Environment* env
|
|||
|
READONLY_PROPERTY(process, "noDeprecation", True(env->isolate()));
|
|||
|
}
|
|||
|
|
|||
|
+ // --insecure-http-parser
|
|||
|
+ if (insecure_http_parser) {
|
|||
|
+ READONLY_PROPERTY(process, "insecureHttpParser", True(env->isolate()));
|
|||
|
+ }
|
|||
|
+
|
|||
|
// --no-warnings
|
|||
|
if (no_process_warnings) {
|
|||
|
READONLY_PROPERTY(process, "noProcessWarnings", True(env->isolate()));
|
|||
|
@@ -3403,6 +3410,8 @@ static void ParseArgs(int* argc,
|
|||
|
force_repl = true;
|
|||
|
} else if (strcmp(arg, "--no-deprecation") == 0) {
|
|||
|
no_deprecation = true;
|
|||
|
+ } else if (strcmp(arg, "--insecure-http-parser") == 0) {
|
|||
|
+ insecure_http_parser = true;
|
|||
|
} else if (strcmp(arg, "--napi-modules") == 0) {
|
|||
|
// no-op
|
|||
|
} else if (strcmp(arg, "--no-warnings") == 0) {
|
|||
|
Index: node-v8.17.0/src/node_http_parser.cc
|
|||
|
===================================================================
|
|||
|
--- node-v8.17.0.orig/src/node_http_parser.cc
|
|||
|
+++ node-v8.17.0/src/node_http_parser.cc
|
|||
|
@@ -161,12 +161,12 @@ struct StringPtr {
|
|||
|
|
|||
|
class Parser : public AsyncWrap {
|
|||
|
public:
|
|||
|
- Parser(Environment* env, Local<Object> wrap, enum http_parser_type type)
|
|||
|
+ Parser(Environment* env, Local<Object> wrap, enum http_parser_type type, bool lenient)
|
|||
|
: AsyncWrap(env, wrap, AsyncWrap::PROVIDER_HTTPPARSER),
|
|||
|
current_buffer_len_(0),
|
|||
|
current_buffer_data_(nullptr) {
|
|||
|
Wrap(object(), this);
|
|||
|
- Init(type);
|
|||
|
+ Init(type, lenient);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
@@ -383,7 +383,7 @@ class Parser : public AsyncWrap {
|
|||
|
http_parser_type type =
|
|||
|
static_cast<http_parser_type>(args[0]->Int32Value());
|
|||
|
CHECK(type == HTTP_REQUEST || type == HTTP_RESPONSE);
|
|||
|
- new Parser(env, args.This(), type);
|
|||
|
+ new Parser(env, args.This(), type, args[1]->IsTrue());
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
@@ -476,6 +476,7 @@ class Parser : public AsyncWrap {
|
|||
|
|
|||
|
static void Reinitialize(const FunctionCallbackInfo<Value>& args) {
|
|||
|
Environment* env = Environment::GetCurrent(args);
|
|||
|
+ bool lenient = args[2]->IsTrue();
|
|||
|
|
|||
|
CHECK(args[0]->IsInt32());
|
|||
|
CHECK(args[1]->IsBoolean());
|
|||
|
@@ -494,7 +495,7 @@ class Parser : public AsyncWrap {
|
|||
|
if (isReused) {
|
|||
|
parser->AsyncReset();
|
|||
|
}
|
|||
|
- parser->Init(type);
|
|||
|
+ parser->Init(type, lenient);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
@@ -738,8 +739,9 @@ class Parser : public AsyncWrap {
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
- void Init(enum http_parser_type type) {
|
|||
|
+ void Init(enum http_parser_type type, bool lenient) {
|
|||
|
http_parser_init(&parser_, type);
|
|||
|
+ parser_.lenient_http_headers = lenient;
|
|||
|
url_.Reset();
|
|||
|
status_message_.Reset();
|
|||
|
num_fields_ = 0;
|
|||
|
Index: node-v8.17.0/src/node.h
|
|||
|
===================================================================
|
|||
|
--- node-v8.17.0.orig/src/node.h
|
|||
|
+++ node-v8.17.0/src/node.h
|
|||
|
@@ -197,6 +197,7 @@ typedef intptr_t ssize_t;
|
|||
|
namespace node {
|
|||
|
|
|||
|
NODE_EXTERN extern bool no_deprecation;
|
|||
|
+NODE_EXTERN extern bool insecure_http_parser;
|
|||
|
#if HAVE_OPENSSL
|
|||
|
NODE_EXTERN extern bool ssl_openssl_cert_store;
|
|||
|
# if NODE_FIPS_MODE
|