deps: upgrade http-parser to v2.8.0

PR-URL: https://github.com/nodejs-private/http-parser-private/pull/1
Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rod Vagg <rod@vagg.org>
This commit is contained in:
Ben Noordhuis 2018-03-27 16:45:33 +02:00 committed by Myles Borins
parent 80310e916a
commit 32050065f1
No known key found for this signature in database
GPG Key ID: 933B01F40B5CA946
9 changed files with 451 additions and 164 deletions

View File

@ -12,6 +12,8 @@ parsertrace_g
*.mk
*.Makefile
*.so.*
*.exe.*
*.exe
*.a

View File

@ -1,8 +1,4 @@
http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright
Igor Sysoev.
Additional changes are licensed under the same terms as NGINX and
copyright Joyent, Inc. and other Node contributors. All rights reserved.
Copyright Joyent, Inc. and other Node contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to

View File

@ -21,16 +21,22 @@
PLATFORM ?= $(shell sh -c 'uname -s | tr "[A-Z]" "[a-z]"')
HELPER ?=
BINEXT ?=
SOLIBNAME = libhttp_parser
SOMAJOR = 2
SOMINOR = 8
SOREV = 0
ifeq (darwin,$(PLATFORM))
SONAME ?= libhttp_parser.2.7.0.dylib
SOEXT ?= dylib
SONAME ?= $(SOLIBNAME).$(SOMAJOR).$(SOMINOR).$(SOEXT)
LIBNAME ?= $(SOLIBNAME).$(SOMAJOR).$(SOMINOR).$(SOREV).$(SOEXT)
else ifeq (wine,$(PLATFORM))
CC = winegcc
BINEXT = .exe.so
HELPER = wine
else
SONAME ?= libhttp_parser.so.2.7.0
SOEXT ?= so
SONAME ?= $(SOLIBNAME).$(SOEXT).$(SOMAJOR).$(SOMINOR)
LIBNAME ?= $(SOLIBNAME).$(SOEXT).$(SOMAJOR).$(SOMINOR).$(SOREV)
endif
CC?=gcc
@ -55,11 +61,13 @@ CFLAGS_LIB = $(CFLAGS_FAST) -fPIC
LDFLAGS_LIB = $(LDFLAGS) -shared
INSTALL ?= install
PREFIX ?= $(DESTDIR)/usr/local
PREFIX ?= /usr/local
LIBDIR = $(PREFIX)/lib
INCLUDEDIR = $(PREFIX)/include
ifneq (darwin,$(PLATFORM))
ifeq (darwin,$(PLATFORM))
LDFLAGS_LIB += -Wl,-install_name,$(LIBDIR)/$(SONAME)
else
# TODO(bnoordhuis) The native SunOS linker expects -h rather than -soname...
LDFLAGS_LIB += -Wl,-soname=$(SONAME)
endif
@ -102,7 +110,7 @@ libhttp_parser.o: http_parser.c http_parser.h Makefile
$(CC) $(CPPFLAGS_FAST) $(CFLAGS_LIB) -c http_parser.c -o libhttp_parser.o
library: libhttp_parser.o
$(CC) $(LDFLAGS_LIB) -o $(SONAME) $<
$(CC) $(LDFLAGS_LIB) -o $(LIBNAME) $<
package: http_parser.o
$(AR) rcs libhttp_parser.a http_parser.o
@ -123,19 +131,22 @@ tags: http_parser.c http_parser.h test.c
ctags $^
install: library
$(INSTALL) -D http_parser.h $(INCLUDEDIR)/http_parser.h
$(INSTALL) -D $(SONAME) $(LIBDIR)/$(SONAME)
ln -s $(LIBDIR)/$(SONAME) $(LIBDIR)/libhttp_parser.$(SOEXT)
$(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)
install-strip: library
$(INSTALL) -D http_parser.h $(INCLUDEDIR)/http_parser.h
$(INSTALL) -D -s $(SONAME) $(LIBDIR)/$(SONAME)
ln -s $(LIBDIR)/$(SONAME) $(LIBDIR)/libhttp_parser.$(SOEXT)
$(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)
uninstall:
rm $(INCLUDEDIR)/http_parser.h
rm $(LIBDIR)/$(SONAME)
rm $(LIBDIR)/libhttp_parser.so
rm $(DESTDIR)$(INCLUDEDIR)/http_parser.h
rm $(DESTDIR)$(LIBDIR)/$(SOLIBNAME).$(SOEXT)
rm $(DESTDIR)$(LIBDIR)/$(SONAME)
rm $(DESTDIR)$(LIBDIR)/$(LIBNAME)
clean:
rm -f *.o *.a tags test test_fast test_g \

View File

@ -72,9 +72,9 @@ if (parser->upgrade) {
}
```
HTTP needs to know where the end of the stream is. For example, sometimes
`http_parser` needs to know where the end of the stream is. For example, sometimes
servers send responses without Content-Length and expect the client to
consume input (for the body) until EOF. To tell http_parser about EOF, give
consume input (for the body) until EOF. To tell `http_parser` about EOF, give
`0` as the fourth parameter to `http_parser_execute()`. Callbacks and errors
can still be encountered during an EOF, so one must still be prepared
to receive them.
@ -93,7 +93,7 @@ the on_body callback.
The Special Problem of Upgrade
------------------------------
HTTP supports upgrading the connection to a different protocol. An
`http_parser` supports upgrading the connection to a different protocol. An
increasingly common example of this is the WebSocket protocol which sends
a request like
@ -144,7 +144,7 @@ parse a request, and then give a response over that socket. By instantiation
of a thread-local struct containing relevant data (e.g. accepted socket,
allocated memory for callbacks to write into, etc), a parser's callbacks are
able to communicate data between the scope of the thread and the scope of the
callback in a threadsafe manner. This allows http-parser to be used in
callback in a threadsafe manner. This allows `http_parser` to be used in
multi-threaded contexts.
Example:
@ -202,7 +202,7 @@ void http_parser_thread(socket_t sock) {
In case you parse HTTP message in chunks (i.e. `read()` request line
from socket, parse, read half headers, parse, etc) your data callbacks
may be called more than once. Http-parser guarantees that data pointer is only
may be called more than once. `http_parser` guarantees that data pointer is only
valid for the lifetime of callback. You can also `read()` into a heap allocated
buffer to avoid copying memory around if this fits your application.

View File

@ -1,7 +1,4 @@
/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
*
* Additional changes are licensed under the same terms as NGINX and
* copyright Joyent, Inc. and other Node contributors. All rights reserved.
/* Copyright Joyent, Inc. and other Node contributors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to

View File

@ -1,7 +1,4 @@
/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
*
* Additional changes are licensed under the same terms as NGINX and
* copyright Joyent, Inc. and other Node contributors. All rights reserved.
/* Copyright Joyent, Inc. and other Node contributors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@ -25,7 +22,6 @@
#include <assert.h>
#include <stddef.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
@ -286,10 +282,10 @@ enum state
, s_res_HT
, s_res_HTT
, s_res_HTTP
, s_res_first_http_major
, s_res_http_major
, s_res_first_http_minor
, s_res_http_dot
, s_res_http_minor
, s_res_http_end
, s_res_first_status_code
, s_res_status_code
, s_res_status_start
@ -316,10 +312,10 @@ enum state
, s_req_http_HT
, s_req_http_HTT
, s_req_http_HTTP
, s_req_first_http_major
, s_req_http_major
, s_req_first_http_minor
, s_req_http_dot
, s_req_http_minor
, s_req_http_end
, s_req_line_almost_done
, s_header_field_start
@ -795,75 +791,48 @@ reexecute:
case s_res_HTTP:
STRICT_CHECK(ch != '/');
UPDATE_STATE(s_res_first_http_major);
UPDATE_STATE(s_res_http_major);
break;
case s_res_first_http_major:
if (UNLIKELY(ch < '0' || ch > '9')) {
case s_res_http_major:
if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_major = ch - '0';
UPDATE_STATE(s_res_http_major);
UPDATE_STATE(s_res_http_dot);
break;
/* major HTTP version or dot */
case s_res_http_major:
case s_res_http_dot:
{
if (ch == '.') {
UPDATE_STATE(s_res_first_http_minor);
break;
}
if (!IS_NUM(ch)) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_major *= 10;
parser->http_major += ch - '0';
if (UNLIKELY(parser->http_major > 999)) {
if (UNLIKELY(ch != '.')) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
UPDATE_STATE(s_res_http_minor);
break;
}
/* first digit of minor HTTP version */
case s_res_first_http_minor:
case s_res_http_minor:
if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_minor = ch - '0';
UPDATE_STATE(s_res_http_minor);
UPDATE_STATE(s_res_http_end);
break;
/* minor HTTP version or end of request line */
case s_res_http_minor:
case s_res_http_end:
{
if (ch == ' ') {
UPDATE_STATE(s_res_first_status_code);
break;
}
if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_minor *= 10;
parser->http_minor += ch - '0';
if (UNLIKELY(parser->http_minor > 999)) {
if (UNLIKELY(ch != ' ')) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
UPDATE_STATE(s_res_first_status_code);
break;
}
@ -890,10 +859,9 @@ reexecute:
UPDATE_STATE(s_res_status_start);
break;
case CR:
UPDATE_STATE(s_res_line_almost_done);
break;
case LF:
UPDATE_STATE(s_header_field_start);
UPDATE_STATE(s_res_status_start);
REEXECUTE();
break;
default:
SET_ERRNO(HPE_INVALID_STATUS);
@ -915,19 +883,13 @@ reexecute:
case s_res_status_start:
{
if (ch == CR) {
UPDATE_STATE(s_res_line_almost_done);
break;
}
if (ch == LF) {
UPDATE_STATE(s_header_field_start);
break;
}
MARK(status);
UPDATE_STATE(s_res_status);
parser->index = 0;
if (ch == CR || ch == LF)
REEXECUTE();
break;
}
@ -980,7 +942,7 @@ reexecute:
/* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
break;
case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH, SOURCE */ break;
case 'T': parser->method = HTTP_TRACE; break;
case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
default:
@ -1007,7 +969,7 @@ reexecute:
UPDATE_STATE(s_req_spaces_before_url);
} else if (ch == matcher[parser->index]) {
; /* nada */
} else if (IS_ALPHA(ch)) {
} else if ((ch >= 'A' && ch <= 'Z') || ch == '-') {
switch (parser->method << 16 | parser->index << 8 | ch) {
#define XX(meth, pos, ch, new_meth) \
@ -1016,31 +978,28 @@ reexecute:
XX(POST, 1, 'U', PUT)
XX(POST, 1, 'A', PATCH)
XX(POST, 1, 'R', PROPFIND)
XX(PUT, 2, 'R', PURGE)
XX(CONNECT, 1, 'H', CHECKOUT)
XX(CONNECT, 2, 'P', COPY)
XX(MKCOL, 1, 'O', MOVE)
XX(MKCOL, 1, 'E', MERGE)
XX(MKCOL, 1, '-', MSEARCH)
XX(MKCOL, 2, 'A', MKACTIVITY)
XX(MKCOL, 3, 'A', MKCALENDAR)
XX(SUBSCRIBE, 1, 'E', SEARCH)
XX(SUBSCRIBE, 1, 'O', SOURCE)
XX(REPORT, 2, 'B', REBIND)
XX(POST, 1, 'R', PROPFIND)
XX(PROPFIND, 4, 'P', PROPPATCH)
XX(PUT, 2, 'R', PURGE)
XX(LOCK, 1, 'I', LINK)
XX(UNLOCK, 2, 'S', UNSUBSCRIBE)
XX(UNLOCK, 2, 'B', UNBIND)
XX(UNLOCK, 3, 'I', UNLINK)
#undef XX
default:
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
} else if (ch == '-' &&
parser->index == 1 &&
parser->method == HTTP_MKCOL) {
parser->method = HTTP_MSEARCH;
} else {
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
@ -1153,57 +1112,41 @@ reexecute:
case s_req_http_HTTP:
STRICT_CHECK(ch != '/');
UPDATE_STATE(s_req_first_http_major);
break;
/* first digit of major HTTP version */
case s_req_first_http_major:
if (UNLIKELY(ch < '1' || ch > '9')) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_major = ch - '0';
UPDATE_STATE(s_req_http_major);
break;
/* major HTTP version or dot */
case s_req_http_major:
{
if (ch == '.') {
UPDATE_STATE(s_req_first_http_minor);
break;
}
if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_major *= 10;
parser->http_major += ch - '0';
parser->http_major = ch - '0';
UPDATE_STATE(s_req_http_dot);
break;
if (UNLIKELY(parser->http_major > 999)) {
case s_req_http_dot:
{
if (UNLIKELY(ch != '.')) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
UPDATE_STATE(s_req_http_minor);
break;
}
/* first digit of minor HTTP version */
case s_req_first_http_minor:
case s_req_http_minor:
if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_minor = ch - '0';
UPDATE_STATE(s_req_http_minor);
UPDATE_STATE(s_req_http_end);
break;
/* minor HTTP version or end of request line */
case s_req_http_minor:
case s_req_http_end:
{
if (ch == CR) {
UPDATE_STATE(s_req_line_almost_done);
@ -1215,21 +1158,8 @@ reexecute:
break;
}
/* XXX allow spaces after digit? */
if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_minor *= 10;
parser->http_minor += ch - '0';
if (UNLIKELY(parser->http_minor > 999)) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
break;
}
@ -1366,12 +1296,7 @@ reexecute:
|| c != CONTENT_LENGTH[parser->index]) {
parser->header_state = h_general;
} else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
if (parser->flags & F_CONTENTLENGTH) {
SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
goto error;
}
parser->header_state = h_content_length;
parser->flags |= F_CONTENTLENGTH;
}
break;
@ -1474,6 +1399,12 @@ reexecute:
goto error;
}
if (parser->flags & F_CONTENTLENGTH) {
SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
goto error;
}
parser->flags |= F_CONTENTLENGTH;
parser->content_length = ch - '0';
break;
@ -1793,10 +1724,17 @@ reexecute:
UPDATE_STATE(s_headers_done);
/* Set this here so that on_headers_complete() callbacks can see it */
parser->upgrade =
((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) ==
(F_UPGRADE | F_CONNECTION_UPGRADE) ||
parser->method == HTTP_CONNECT);
if ((parser->flags & F_UPGRADE) &&
(parser->flags & F_CONNECTION_UPGRADE)) {
/* For responses, "Upgrade: foo" and "Connection: upgrade" are
* mandatory only when it is a 101 Switching Protocols response,
* otherwise it is purely informational, to announce support.
*/
parser->upgrade =
(parser->type == HTTP_REQUEST || parser->status_code == 101);
} else {
parser->upgrade = (parser->method == HTTP_CONNECT);
}
/* Here we call the headers_complete callback. This is somewhat
* different than other callbacks because if the user returns 1, we
@ -1815,6 +1753,7 @@ reexecute:
case 2:
parser->upgrade = 1;
/* FALLTHROUGH */
case 1:
parser->flags |= F_SKIPBODY;
break;
@ -2373,7 +2312,7 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
case s_req_server_with_at:
found_at = 1;
/* FALLTROUGH */
/* FALLTHROUGH */
case s_req_server:
uf = UF_HOST;
break;
@ -2427,12 +2366,27 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
}
if (u->field_set & (1 << UF_PORT)) {
/* Don't bother with endp; we've already validated the string */
unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);
uint16_t off;
uint16_t len;
const char* p;
const char* end;
unsigned long v;
/* Ports have a max value of 2^16 */
if (v > 0xffff) {
return 1;
off = u->field_data[UF_PORT].off;
len = u->field_data[UF_PORT].len;
end = buf + off + len;
/* NOTE: The characters are already validated and are in the [0-9] range */
assert(off + len <= buflen && "Port number overflow");
v = 0;
for (p = buf + off; p < end; p++) {
v *= 10;
v += *p - '0';
/* Ports have a max value of 2^16 */
if (v > 0xffff) {
return 1;
}
}
u->port = (uint16_t) v;

View File

@ -26,14 +26,13 @@ extern "C" {
/* Also update SONAME in the Makefile whenever you change these. */
#define HTTP_PARSER_VERSION_MAJOR 2
#define HTTP_PARSER_VERSION_MINOR 7
#define HTTP_PARSER_VERSION_MINOR 8
#define HTTP_PARSER_VERSION_PATCH 0
#include <sys/types.h>
#include <stddef.h>
#if defined(_WIN32) && !defined(__MINGW32__) && \
(!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
#include <BaseTsd.h>
#include <stddef.h>
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
@ -90,6 +89,76 @@ typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
typedef int (*http_cb) (http_parser*);
/* Status Codes */
#define HTTP_STATUS_MAP(XX) \
XX(100, CONTINUE, Continue) \
XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
XX(102, PROCESSING, Processing) \
XX(200, OK, OK) \
XX(201, CREATED, Created) \
XX(202, ACCEPTED, Accepted) \
XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \
XX(204, NO_CONTENT, No Content) \
XX(205, RESET_CONTENT, Reset Content) \
XX(206, PARTIAL_CONTENT, Partial Content) \
XX(207, MULTI_STATUS, Multi-Status) \
XX(208, ALREADY_REPORTED, Already Reported) \
XX(226, IM_USED, IM Used) \
XX(300, MULTIPLE_CHOICES, Multiple Choices) \
XX(301, MOVED_PERMANENTLY, Moved Permanently) \
XX(302, FOUND, Found) \
XX(303, SEE_OTHER, See Other) \
XX(304, NOT_MODIFIED, Not Modified) \
XX(305, USE_PROXY, Use Proxy) \
XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \
XX(308, PERMANENT_REDIRECT, Permanent Redirect) \
XX(400, BAD_REQUEST, Bad Request) \
XX(401, UNAUTHORIZED, Unauthorized) \
XX(402, PAYMENT_REQUIRED, Payment Required) \
XX(403, FORBIDDEN, Forbidden) \
XX(404, NOT_FOUND, Not Found) \
XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \
XX(406, NOT_ACCEPTABLE, Not Acceptable) \
XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \
XX(408, REQUEST_TIMEOUT, Request Timeout) \
XX(409, CONFLICT, Conflict) \
XX(410, GONE, Gone) \
XX(411, LENGTH_REQUIRED, Length Required) \
XX(412, PRECONDITION_FAILED, Precondition Failed) \
XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \
XX(414, URI_TOO_LONG, URI Too Long) \
XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \
XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \
XX(417, EXPECTATION_FAILED, Expectation Failed) \
XX(421, MISDIRECTED_REQUEST, Misdirected Request) \
XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \
XX(423, LOCKED, Locked) \
XX(424, FAILED_DEPENDENCY, Failed Dependency) \
XX(426, UPGRADE_REQUIRED, Upgrade Required) \
XX(428, PRECONDITION_REQUIRED, Precondition Required) \
XX(429, TOO_MANY_REQUESTS, Too Many Requests) \
XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \
XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \
XX(501, NOT_IMPLEMENTED, Not Implemented) \
XX(502, BAD_GATEWAY, Bad Gateway) \
XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \
XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \
XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \
XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \
XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \
XX(508, LOOP_DETECTED, Loop Detected) \
XX(510, NOT_EXTENDED, Not Extended) \
XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \
enum http_status
{
#define XX(num, name, string) HTTP_STATUS_##name = num,
HTTP_STATUS_MAP(XX)
#undef XX
};
/* Request Methods */
#define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
@ -132,6 +201,8 @@ typedef int (*http_cb) (http_parser*);
/* RFC-2068, section 19.6.1.2 */ \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
/* icecast */ \
XX(33, SOURCE, SOURCE) \
enum http_method
{

View File

@ -78,6 +78,7 @@ struct message {
int message_begin_cb_called;
int headers_complete_cb_called;
int message_complete_cb_called;
int status_cb_called;
int message_complete_on_eof;
int body_is_final;
};
@ -1131,7 +1132,7 @@ const struct message requests[] =
}
#define UNLINK_REQUEST 41
, {.name = "link request"
, {.name = "unlink request"
,.type= HTTP_REQUEST
,.raw= "UNLINK /images/my_dog.jpg HTTP/1.1\r\n"
"Host: example.com\r\n"
@ -1153,6 +1154,26 @@ const struct message requests[] =
,.body= ""
}
#define SOURCE_REQUEST 42
, {.name = "source request"
,.type= HTTP_REQUEST
,.raw= "SOURCE /music/sweet/music HTTP/1.1\r\n"
"Host: example.com\r\n"
"\r\n"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.http_major= 1
,.http_minor= 1
,.method= HTTP_SOURCE
,.request_path= "/music/sweet/music"
,.request_url= "/music/sweet/music"
,.query_string= ""
,.fragment= ""
,.num_headers= 1
,.headers= { { "Host", "example.com" } }
,.body= ""
}
, {.name= NULL } /* sentinel */
};
@ -1745,6 +1766,193 @@ const struct message responses[] =
,.body= ""
}
#define CONTENT_LENGTH_X 21
, {.name= "Content-Length-X"
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 200 OK\r\n"
"Content-Length-X: 0\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"2\r\n"
"OK\r\n"
"0\r\n"
"\r\n"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.http_major= 1
,.http_minor= 1
,.status_code= 200
,.response_status= "OK"
,.num_headers= 2
,.headers= { { "Content-Length-X", "0" }
, { "Transfer-Encoding", "chunked" }
}
,.body= "OK"
,.num_chunks_complete= 2
,.chunk_lengths= { 2 }
}
#define HTTP_101_RESPONSE_WITH_UPGRADE_HEADER 22
, {.name= "HTTP 101 response with Upgrade header"
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 101 Switching Protocols\r\n"
"Connection: upgrade\r\n"
"Upgrade: h2c\r\n"
"\r\n"
"proto"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.http_major= 1
,.http_minor= 1
,.status_code= 101
,.response_status= "Switching Protocols"
,.upgrade= "proto"
,.num_headers= 2
,.headers=
{ { "Connection", "upgrade" }
, { "Upgrade", "h2c" }
}
}
#define HTTP_101_RESPONSE_WITH_UPGRADE_HEADER_AND_CONTENT_LENGTH 23
, {.name= "HTTP 101 response with Upgrade and Content-Length header"
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 101 Switching Protocols\r\n"
"Connection: upgrade\r\n"
"Upgrade: h2c\r\n"
"Content-Length: 4\r\n"
"\r\n"
"body"
"proto"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.http_major= 1
,.http_minor= 1
,.status_code= 101
,.response_status= "Switching Protocols"
,.body= "body"
,.upgrade= "proto"
,.num_headers= 3
,.headers=
{ { "Connection", "upgrade" }
, { "Upgrade", "h2c" }
, { "Content-Length", "4" }
}
}
#define HTTP_101_RESPONSE_WITH_UPGRADE_HEADER_AND_TRANSFER_ENCODING 24
, {.name= "HTTP 101 response with Upgrade and Transfer-Encoding header"
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 101 Switching Protocols\r\n"
"Connection: upgrade\r\n"
"Upgrade: h2c\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"2\r\n"
"bo\r\n"
"2\r\n"
"dy\r\n"
"0\r\n"
"\r\n"
"proto"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.http_major= 1
,.http_minor= 1
,.status_code= 101
,.response_status= "Switching Protocols"
,.body= "body"
,.upgrade= "proto"
,.num_headers= 3
,.headers=
{ { "Connection", "upgrade" }
, { "Upgrade", "h2c" }
, { "Transfer-Encoding", "chunked" }
}
,.num_chunks_complete= 3
,.chunk_lengths= { 2, 2 }
}
#define HTTP_200_RESPONSE_WITH_UPGRADE_HEADER 25
, {.name= "HTTP 200 response with Upgrade header"
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 200 OK\r\n"
"Connection: upgrade\r\n"
"Upgrade: h2c\r\n"
"\r\n"
"body"
,.should_keep_alive= FALSE
,.message_complete_on_eof= TRUE
,.http_major= 1
,.http_minor= 1
,.status_code= 200
,.response_status= "OK"
,.body= "body"
,.upgrade= NULL
,.num_headers= 2
,.headers=
{ { "Connection", "upgrade" }
, { "Upgrade", "h2c" }
}
}
#define HTTP_200_RESPONSE_WITH_UPGRADE_HEADER_AND_CONTENT_LENGTH 26
, {.name= "HTTP 200 response with Upgrade and Content-Length header"
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 200 OK\r\n"
"Connection: upgrade\r\n"
"Upgrade: h2c\r\n"
"Content-Length: 4\r\n"
"\r\n"
"body"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.http_major= 1
,.http_minor= 1
,.status_code= 200
,.response_status= "OK"
,.num_headers= 3
,.body= "body"
,.upgrade= NULL
,.headers=
{ { "Connection", "upgrade" }
, { "Upgrade", "h2c" }
, { "Content-Length", "4" }
}
}
#define HTTP_200_RESPONSE_WITH_UPGRADE_HEADER_AND_TRANSFER_ENCODING 27
, {.name= "HTTP 200 response with Upgrade and Transfer-Encoding header"
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 200 OK\r\n"
"Connection: upgrade\r\n"
"Upgrade: h2c\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"2\r\n"
"bo\r\n"
"2\r\n"
"dy\r\n"
"0\r\n"
"\r\n"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.http_major= 1
,.http_minor= 1
,.status_code= 200
,.response_status= "OK"
,.num_headers= 3
,.body= "body"
,.upgrade= NULL
,.headers=
{ { "Connection", "upgrade" }
, { "Upgrade", "h2c" }
, { "Transfer-Encoding", "chunked" }
}
,.num_chunks_complete= 3
,.chunk_lengths= { 2, 2 }
}
, {.name= NULL } /* sentinel */
};
@ -1955,6 +2163,9 @@ int
response_status_cb (http_parser *p, const char *buf, size_t len)
{
assert(p == parser);
messages[num_messages].status_cb_called = TRUE;
strlncat(messages[num_messages].response_status,
sizeof(messages[num_messages].response_status),
buf,
@ -2379,6 +2590,7 @@ message_eq (int index, int connect, const struct message *expected)
} else {
MESSAGE_CHECK_NUM_EQ(expected, m, status_code);
MESSAGE_CHECK_STR_EQ(expected, m, response_status);
assert(m->status_cb_called);
}
if (!connect) {
@ -2450,7 +2662,9 @@ message_eq (int index, int connect, const struct message *expected)
if (!r) return 0;
}
MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
if (!connect) {
MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
}
return 1;
}
@ -3287,9 +3501,11 @@ test_message_count_body (const struct message *message)
}
void
test_simple (const char *buf, enum http_errno err_expected)
test_simple_type (const char *buf,
enum http_errno err_expected,
enum http_parser_type type)
{
parser_init(HTTP_REQUEST);
parser_init(type);
enum http_errno err;
@ -3313,6 +3529,12 @@ test_simple (const char *buf, enum http_errno err_expected)
}
}
void
test_simple (const char *buf, enum http_errno err_expected)
{
test_simple_type(buf, err_expected, HTTP_REQUEST);
}
void
test_invalid_header_content (int req, const char* str)
{
@ -3462,6 +3684,30 @@ test_header_cr_no_lf_error (int req)
abort();
}
void
test_no_overflow_parse_url (void)
{
int rv;
struct http_parser_url u;
http_parser_url_init(&u);
rv = http_parser_parse_url("http://example.com:8001", 22, 0, &u);
if (rv != 0) {
fprintf(stderr,
"\n*** test_no_overflow_parse_url invalid return value=%d\n",
rv);
abort();
}
if (u.port != 800) {
fprintf(stderr,
"\n*** test_no_overflow_parse_url invalid port number=%d\n",
u.port);
abort();
}
}
void
test_header_overflow_error (int req)
{
@ -3850,11 +4096,10 @@ test_message_connect (const struct message *msg)
{
char *buf = (char*) msg->raw;
size_t buflen = strlen(msg->raw);
size_t nread;
parser_init(msg->type);
nread = parse_connect(buf, buflen);
parse_connect(buf, buflen);
if (num_messages != 1) {
printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name);
@ -3898,6 +4143,7 @@ main (void)
test_header_nread_value();
//// OVERFLOW CONDITIONS
test_no_overflow_parse_url();
test_header_overflow_error(HTTP_REQUEST);
test_no_overflow_long_body(HTTP_REQUEST, 1000);
@ -3924,6 +4170,12 @@ main (void)
//// RESPONSES
test_simple_type("HTP/1.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
test_simple_type("HTTP/01.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
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);
for (i = 0; i < response_count; i++) {
test_message(&responses[i]);
}
@ -4001,6 +4253,9 @@ main (void)
/// REQUESTS
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);
// Extended characters - see nodejs/test/parallel/test-http-headers-obstext.js
test_simple("GET / HTTP/1.1\r\n"

View File

@ -60,7 +60,8 @@ const methods = [
'PURGE',
'MKCALENDAR',
'LINK',
'UNLINK'
'UNLINK',
'SOURCE',
];
assert.deepStrictEqual(http.METHODS, methods.sort());