Replace http-parser with llhttp

llhttp does not include URL parser.  We extracted URL parser code from
http-parser and put it under third-party/url-parser.

llhttp bd3d224eb8cdc92c6fc8f508d7bbe0ba266e8e92
This commit is contained in:
Tatsuhiro Tsujikawa 2018-11-23 20:44:36 +09:00
parent f028cc4392
commit c64d2573dc
51 changed files with 8059 additions and 8407 deletions

View File

@ -9,6 +9,7 @@ if(ENABLE_EXAMPLES)
include_directories( include_directories(
${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
"${CMAKE_CURRENT_SOURCE_DIR}/../third-party" "${CMAKE_CURRENT_SOURCE_DIR}/../third-party"
"${CMAKE_CURRENT_SOURCE_DIR}/../third-party/llhttp/include"
${LIBEVENT_INCLUDE_DIRS} ${LIBEVENT_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS}
@ -21,14 +22,24 @@ if(ENABLE_EXAMPLES)
${APP_LIBRARIES} ${APP_LIBRARIES}
) )
add_executable(client client.c $<TARGET_OBJECTS:http-parser>) add_executable(client client.c $<TARGET_OBJECTS:llhttp>
add_executable(libevent-client libevent-client.c $<TARGET_OBJECTS:http-parser>) $<TARGET_OBJECTS:url-parser>
add_executable(libevent-server libevent-server.c $<TARGET_OBJECTS:http-parser>) )
add_executable(deflate deflate.c $<TARGET_OBJECTS:http-parser>) add_executable(libevent-client libevent-client.c $<TARGET_OBJECTS:llhttp>
$<TARGET_OBJECTS:url-parser>
)
add_executable(libevent-server libevent-server.c $<TARGET_OBJECTS:llhttp>
$<TARGET_OBJECTS:url-parser>
)
add_executable(deflate deflate.c $<TARGET_OBJECTS:llhttp>
$<TARGET_OBJECTS:url-parser>
)
if(ENABLE_ASIO_LIB) if(ENABLE_ASIO_LIB)
foreach(name asio-sv asio-sv2 asio-cl asio-cl2) foreach(name asio-sv asio-sv2 asio-cl asio-cl2)
add_executable(${name} ${name}.cc $<TARGET_OBJECTS:http-parser>) add_executable(${name} ${name}.cc $<TARGET_OBJECTS:llhttp>
$<TARGET_OBJECTS:url-parser>
)
target_include_directories(${name} PRIVATE target_include_directories(${name} PRIVATE
${OPENSSL_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS}
${Boost_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}

View File

@ -36,7 +36,7 @@ AM_CPPFLAGS = \
@OPENSSL_CFLAGS@ \ @OPENSSL_CFLAGS@ \
@DEFS@ @DEFS@
LDADD = $(top_builddir)/lib/libnghttp2.la \ LDADD = $(top_builddir)/lib/libnghttp2.la \
$(top_builddir)/third-party/libhttp-parser.la \ $(top_builddir)/third-party/liburl-parser.la \
@LIBEVENT_OPENSSL_LIBS@ \ @LIBEVENT_OPENSSL_LIBS@ \
@OPENSSL_LIBS@ \ @OPENSSL_LIBS@ \
@APPLDFLAGS@ @APPLDFLAGS@
@ -61,7 +61,7 @@ noinst_PROGRAMS += asio-sv asio-sv2 asio-cl asio-cl2
ASIOCPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS} ASIOCPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS}
ASIOLDADD = $(top_builddir)/lib/libnghttp2.la \ ASIOLDADD = $(top_builddir)/lib/libnghttp2.la \
$(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ \ $(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ \
$(top_builddir)/third-party/libhttp-parser.la \ $(top_builddir)/third-party/liburl-parser.la \
@OPENSSL_LIBS@ \ @OPENSSL_LIBS@ \
${BOOST_LDFLAGS} \ ${BOOST_LDFLAGS} \
${BOOST_ASIO_LIB} \ ${BOOST_ASIO_LIB} \

View File

@ -65,7 +65,7 @@ char *strndup(const char *s, size_t size);
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#include "http-parser/http_parser.h" #include "url-parser/url_parser.h"
#define ARRLEN(x) (sizeof(x) / sizeof(x[0])) #define ARRLEN(x) (sizeof(x) / sizeof(x[0]))

View File

@ -4,42 +4,42 @@ from io import StringIO
from gentokenlookup import gentokenlookup from gentokenlookup import gentokenlookup
# copied from http-parser/http_parser.h, and stripped trailing spaces # copied from llhttp.h, and stripped trailing spaces and backslashes.
# and backslashes.
SRC = ''' SRC = '''
XX(0, DELETE, DELETE) XX(0, DELETE, DELETE)
XX(1, GET, GET) XX(1, GET, GET)
XX(2, HEAD, HEAD) XX(2, HEAD, HEAD)
XX(3, POST, POST) XX(3, POST, POST)
XX(4, PUT, PUT) XX(4, PUT, PUT)
/* pathological */ XX(5, CONNECT, CONNECT)
XX(5, CONNECT, CONNECT) XX(6, OPTIONS, OPTIONS)
XX(6, OPTIONS, OPTIONS) XX(7, TRACE, TRACE)
XX(7, TRACE, TRACE) XX(8, COPY, COPY)
/* webdav */ XX(9, LOCK, LOCK)
XX(8, COPY, COPY) XX(10, MKCOL, MKCOL)
XX(9, LOCK, LOCK) XX(11, MOVE, MOVE)
XX(10, MKCOL, MKCOL) XX(12, PROPFIND, PROPFIND)
XX(11, MOVE, MOVE) XX(13, PROPPATCH, PROPPATCH)
XX(12, PROPFIND, PROPFIND) XX(14, SEARCH, SEARCH)
XX(13, PROPPATCH, PROPPATCH) XX(15, UNLOCK, UNLOCK)
XX(14, SEARCH, SEARCH) XX(16, BIND, BIND)
XX(15, UNLOCK, UNLOCK) XX(17, REBIND, REBIND)
/* subversion */ XX(18, UNBIND, UNBIND)
XX(16, REPORT, REPORT) XX(19, ACL, ACL)
XX(17, MKACTIVITY, MKACTIVITY) XX(20, REPORT, REPORT)
XX(18, CHECKOUT, CHECKOUT) XX(21, MKACTIVITY, MKACTIVITY)
XX(19, MERGE, MERGE) XX(22, CHECKOUT, CHECKOUT)
/* upnp */ XX(23, MERGE, MERGE)
XX(20, MSEARCH, M-SEARCH) XX(24, MSEARCH, M-SEARCH)
XX(21, NOTIFY, NOTIFY) XX(25, NOTIFY, NOTIFY)
XX(22, SUBSCRIBE, SUBSCRIBE) XX(26, SUBSCRIBE, SUBSCRIBE)
XX(23, UNSUBSCRIBE, UNSUBSCRIBE) XX(27, UNSUBSCRIBE, UNSUBSCRIBE)
/* RFC-5789 */ XX(28, PATCH, PATCH)
XX(24, PATCH, PATCH) XX(29, PURGE, PURGE)
XX(25, PURGE, PURGE) XX(30, MKCALENDAR, MKCALENDAR)
/* CalDAV */ XX(31, LINK, LINK)
XX(26, MKCALENDAR, MKCALENDAR) XX(32, UNLINK, UNLINK)
XX(33, SOURCE, SOURCE)
''' '''
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -10,6 +10,7 @@ set_source_files_properties(${cxx_sources} PROPERTIES
include_directories( include_directories(
"${CMAKE_CURRENT_SOURCE_DIR}/includes" "${CMAKE_CURRENT_SOURCE_DIR}/includes"
"${CMAKE_CURRENT_SOURCE_DIR}/../third-party" "${CMAKE_CURRENT_SOURCE_DIR}/../third-party"
"${CMAKE_CURRENT_SOURCE_DIR}/../third-party/llhttp/include"
${JEMALLOC_INCLUDE_DIRS} ${JEMALLOC_INCLUDE_DIRS}
${SPDYLAY_INCLUDE_DIRS} ${SPDYLAY_INCLUDE_DIRS}
@ -166,7 +167,8 @@ if(ENABLE_APP)
) )
add_executable(nghttpx-unittest EXCLUDE_FROM_ALL add_executable(nghttpx-unittest EXCLUDE_FROM_ALL
${NGHTTPX_UNITTEST_SOURCES} ${NGHTTPX_UNITTEST_SOURCES}
$<TARGET_OBJECTS:http-parser> $<TARGET_OBJECTS:llhttp>
$<TARGET_OBJECTS:url-parser>
) )
target_include_directories(nghttpx-unittest PRIVATE ${CUNIT_INCLUDE_DIRS}) target_include_directories(nghttpx-unittest PRIVATE ${CUNIT_INCLUDE_DIRS})
target_compile_definitions(nghttpx-unittest target_compile_definitions(nghttpx-unittest
@ -184,12 +186,20 @@ if(ENABLE_APP)
add_dependencies(check nghttpx-unittest) add_dependencies(check nghttpx-unittest)
endif() endif()
add_executable(nghttp ${NGHTTP_SOURCES} $<TARGET_OBJECTS:http-parser>) add_executable(nghttp ${NGHTTP_SOURCES} $<TARGET_OBJECTS:llhttp>
add_executable(nghttpd ${NGHTTPD_SOURCES} $<TARGET_OBJECTS:http-parser>) $<TARGET_OBJECTS:url-parser>
add_executable(nghttpx ${NGHTTPX-bin_SOURCES} $<TARGET_OBJECTS:http-parser>) )
add_executable(nghttpd ${NGHTTPD_SOURCES} $<TARGET_OBJECTS:llhttp>
$<TARGET_OBJECTS:url-parser>
)
add_executable(nghttpx ${NGHTTPX-bin_SOURCES} $<TARGET_OBJECTS:llhttp>
$<TARGET_OBJECTS:url-parser>
)
target_compile_definitions(nghttpx PRIVATE "-DPKGDATADIR=\"${PKGDATADIR}\"") target_compile_definitions(nghttpx PRIVATE "-DPKGDATADIR=\"${PKGDATADIR}\"")
target_link_libraries(nghttpx nghttpx_static) target_link_libraries(nghttpx nghttpx_static)
add_executable(h2load ${H2LOAD_SOURCES} $<TARGET_OBJECTS:http-parser>) add_executable(h2load ${H2LOAD_SOURCES} $<TARGET_OBJECTS:llhttp>
$<TARGET_OBJECTS:url-parser>
)
install(TARGETS nghttp nghttpd nghttpx h2load install(TARGETS nghttp nghttpd nghttpx h2load
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
@ -243,7 +253,8 @@ if(ENABLE_ASIO_LIB)
add_library(nghttp2_asio SHARED add_library(nghttp2_asio SHARED
${NGHTTP2_ASIO_SOURCES} ${NGHTTP2_ASIO_SOURCES}
$<TARGET_OBJECTS:http-parser> $<TARGET_OBJECTS:llhttp>
$<TARGET_OBJECTS:url-parser>
) )
target_include_directories(nghttp2_asio PRIVATE target_include_directories(nghttp2_asio PRIVATE
${OPENSSL_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS}

View File

@ -40,6 +40,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/lib \ -I$(top_srcdir)/lib \
-I$(top_srcdir)/src/includes \ -I$(top_srcdir)/src/includes \
-I$(top_srcdir)/third-party \ -I$(top_srcdir)/third-party \
-I$(top_srcdir)/third-party/llhttp/include \
@LIBXML2_CFLAGS@ \ @LIBXML2_CFLAGS@ \
@LIBEV_CFLAGS@ \ @LIBEV_CFLAGS@ \
@OPENSSL_CFLAGS@ \ @OPENSSL_CFLAGS@ \
@ -49,7 +50,8 @@ AM_CPPFLAGS = \
@DEFS@ @DEFS@
LDADD = $(top_builddir)/lib/libnghttp2.la \ LDADD = $(top_builddir)/lib/libnghttp2.la \
$(top_builddir)/third-party/libhttp-parser.la \ $(top_builddir)/third-party/liburl-parser.la \
$(top_builddir)/third-party/libllhttp.la \
@JEMALLOC_LIBS@ \ @JEMALLOC_LIBS@ \
@LIBXML2_LIBS@ \ @LIBXML2_LIBS@ \
@LIBEV_LIBS@ \ @LIBEV_LIBS@ \
@ -263,7 +265,8 @@ libnghttp2_asio_la_CPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS}
libnghttp2_asio_la_LDFLAGS = -no-undefined -version-info 1:0:0 libnghttp2_asio_la_LDFLAGS = -no-undefined -version-info 1:0:0
libnghttp2_asio_la_LIBADD = \ libnghttp2_asio_la_LIBADD = \
$(top_builddir)/lib/libnghttp2.la \ $(top_builddir)/lib/libnghttp2.la \
$(top_builddir)/third-party/libhttp-parser.la \ $(top_builddir)/third-party/liburl-parser.la \
$(top_builddir)/third-party/libllhttp.la \
@OPENSSL_LIBS@ \ @OPENSSL_LIBS@ \
${BOOST_LDFLAGS} \ ${BOOST_LDFLAGS} \
${BOOST_ASIO_LIB} \ ${BOOST_ASIO_LIB} \

View File

@ -48,7 +48,7 @@
#include <openssl/err.h> #include <openssl/err.h>
#include "http-parser/http_parser.h" #include "url-parser/url_parser.h"
#include "h2load_http1_session.h" #include "h2load_http1_session.h"
#include "h2load_http2_session.h" #include "h2load_http2_session.h"

View File

@ -34,27 +34,13 @@
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include "http-parser/http_parser.h"
using namespace nghttp2; using namespace nghttp2;
namespace h2load { namespace h2load {
Http1Session::Http1Session(Client *client)
: stream_req_counter_(1),
stream_resp_counter_(1),
client_(client),
htp_(),
complete_(false) {
http_parser_init(&htp_, HTTP_RESPONSE);
htp_.data = this;
}
Http1Session::~Http1Session() {}
namespace { namespace {
// HTTP response message begin // HTTP response message begin
int htp_msg_begincb(http_parser *htp) { int htp_msg_begincb(llhttp_t *htp) {
auto session = static_cast<Http1Session *>(htp->data); auto session = static_cast<Http1Session *>(htp->data);
if (session->stream_resp_counter_ > session->stream_req_counter_) { if (session->stream_resp_counter_ > session->stream_req_counter_) {
@ -67,7 +53,7 @@ int htp_msg_begincb(http_parser *htp) {
namespace { namespace {
// HTTP response status code // HTTP response status code
int htp_statuscb(http_parser *htp, const char *at, size_t length) { int htp_statuscb(llhttp_t *htp, const char *at, size_t length) {
auto session = static_cast<Http1Session *>(htp->data); auto session = static_cast<Http1Session *>(htp->data);
auto client = session->get_client(); auto client = session->get_client();
@ -83,7 +69,7 @@ int htp_statuscb(http_parser *htp, const char *at, size_t length) {
namespace { namespace {
// HTTP response message complete // HTTP response message complete
int htp_msg_completecb(http_parser *htp) { int htp_msg_completecb(llhttp_t *htp) {
auto session = static_cast<Http1Session *>(htp->data); auto session = static_cast<Http1Session *>(htp->data);
auto client = session->get_client(); auto client = session->get_client();
@ -91,7 +77,7 @@ int htp_msg_completecb(http_parser *htp) {
return 0; return 0;
} }
client->final = http_should_keep_alive(htp) == 0; client->final = llhttp_should_keep_alive(htp) == 0;
auto req_stat = client->get_req_stat(session->stream_resp_counter_); auto req_stat = client->get_req_stat(session->stream_resp_counter_);
assert(req_stat); assert(req_stat);
@ -106,14 +92,13 @@ int htp_msg_completecb(http_parser *htp) {
if (client->final) { if (client->final) {
session->stream_req_counter_ = session->stream_resp_counter_; session->stream_req_counter_ = session->stream_resp_counter_;
http_parser_pause(htp, 1);
// Connection is going down. If we have still request to do, // Connection is going down. If we have still request to do,
// create new connection and keep on doing the job. // create new connection and keep on doing the job.
if (client->req_left) { if (client->req_left) {
client->try_new_connection(); client->try_new_connection();
} }
return 0; return HPE_PAUSED;
} }
return 0; return 0;
@ -121,7 +106,7 @@ int htp_msg_completecb(http_parser *htp) {
} // namespace } // namespace
namespace { namespace {
int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { int htp_hdr_keycb(llhttp_t *htp, const char *data, size_t len) {
auto session = static_cast<Http1Session *>(htp->data); auto session = static_cast<Http1Session *>(htp->data);
auto client = session->get_client(); auto client = session->get_client();
@ -132,7 +117,7 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
} // namespace } // namespace
namespace { namespace {
int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) { int htp_hdr_valcb(llhttp_t *htp, const char *data, size_t len) {
auto session = static_cast<Http1Session *>(htp->data); auto session = static_cast<Http1Session *>(htp->data);
auto client = session->get_client(); auto client = session->get_client();
@ -143,13 +128,13 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
} // namespace } // namespace
namespace { namespace {
int htp_hdrs_completecb(http_parser *htp) { int htp_hdrs_completecb(llhttp_t *htp) {
return !http2::expect_response_body(htp->status_code); return !http2::expect_response_body(htp->status_code);
} }
} // namespace } // namespace
namespace { namespace {
int htp_body_cb(http_parser *htp, const char *data, size_t len) { int htp_body_cb(llhttp_t *htp, const char *data, size_t len) {
auto session = static_cast<Http1Session *>(htp->data); auto session = static_cast<Http1Session *>(htp->data);
auto client = session->get_client(); auto client = session->get_client();
@ -161,18 +146,32 @@ int htp_body_cb(http_parser *htp, const char *data, size_t len) {
} // namespace } // namespace
namespace { namespace {
constexpr http_parser_settings htp_hooks = { constexpr llhttp_settings_t htp_hooks = {
htp_msg_begincb, // http_cb on_message_begin; htp_msg_begincb, // llhttp_cb on_message_begin;
nullptr, // http_data_cb on_url; nullptr, // llhttp_data_cb on_url;
htp_statuscb, // http_data_cb on_status; htp_statuscb, // llhttp_data_cb on_status;
htp_hdr_keycb, // http_data_cb on_header_field; htp_hdr_keycb, // llhttp_data_cb on_header_field;
htp_hdr_valcb, // http_data_cb on_header_value; htp_hdr_valcb, // llhttp_data_cb on_header_value;
htp_hdrs_completecb, // http_cb on_headers_complete; htp_hdrs_completecb, // llhttp_cb on_headers_complete;
htp_body_cb, // http_data_cb on_body; htp_body_cb, // llhttp_data_cb on_body;
htp_msg_completecb // http_cb on_message_complete; htp_msg_completecb, // llhttp_cb on_message_complete;
nullptr, // llhttp_cb on_chunk_header
nullptr, // llhttp_cb on_chunk_complete
}; };
} // namespace } // namespace
Http1Session::Http1Session(Client *client)
: stream_req_counter_(1),
stream_resp_counter_(1),
client_(client),
htp_(),
complete_(false) {
llhttp_init(&htp_, HTTP_RESPONSE, &htp_hooks);
htp_.data = this;
}
Http1Session::~Http1Session() {}
void Http1Session::on_connect() { client_->signal_write(); } void Http1Session::on_connect() { client_->signal_write(); }
int Http1Session::submit_request() { int Http1Session::submit_request() {
@ -202,15 +201,15 @@ int Http1Session::submit_request() {
} }
int Http1Session::on_read(const uint8_t *data, size_t len) { int Http1Session::on_read(const uint8_t *data, size_t len) {
auto nread = http_parser_execute(&htp_, &htp_hooks, auto htperr =
reinterpret_cast<const char *>(data), len); llhttp_execute(&htp_, reinterpret_cast<const char *>(data), len);
auto nread = static_cast<size_t>(
reinterpret_cast<const uint8_t *>(llhttp_get_error_pos(&htp_)) - data);
if (client_->worker->config->verbose) { if (client_->worker->config->verbose) {
std::cout.write(reinterpret_cast<const char *>(data), nread); std::cout.write(reinterpret_cast<const char *>(data), nread);
} }
auto htperr = HTTP_PARSER_ERRNO(&htp_);
if (htperr == HPE_PAUSED) { if (htperr == HPE_PAUSED) {
// pause is done only when connection: close is requested // pause is done only when connection: close is requested
return -1; return -1;
@ -218,8 +217,8 @@ int Http1Session::on_read(const uint8_t *data, size_t len) {
if (htperr != HPE_OK) { if (htperr != HPE_OK) {
std::cerr << "[ERROR] HTTP parse error: " std::cerr << "[ERROR] HTTP parse error: "
<< "(" << http_errno_name(htperr) << ") " << "(" << llhttp_errno_name(htperr) << ") "
<< http_errno_description(htperr) << std::endl; << llhttp_get_error_reason(&htp_) << std::endl;
return -1; return -1;
} }

View File

@ -29,6 +29,8 @@
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#include "llhttp.h"
namespace h2load { namespace h2load {
struct Client; struct Client;
@ -49,7 +51,7 @@ public:
private: private:
Client *client_; Client *client_;
http_parser htp_; llhttp_t htp_;
bool complete_; bool complete_;
}; };

View File

@ -24,6 +24,8 @@
*/ */
#include "http2.h" #include "http2.h"
#include "llhttp.h"
#include "util.h" #include "util.h"
namespace nghttp2 { namespace nghttp2 {
@ -1386,6 +1388,11 @@ int lookup_method_token(const uint8_t *name, size_t namelen) {
switch (namelen) { switch (namelen) {
case 3: case 3:
switch (name[2]) { switch (name[2]) {
case 'L':
if (util::streq_l("AC", name, 2)) {
return HTTP_ACL;
}
break;
case 'T': case 'T':
if (util::streq_l("GE", name, 2)) { if (util::streq_l("GE", name, 2)) {
return HTTP_GET; return HTTP_GET;
@ -1399,6 +1406,9 @@ int lookup_method_token(const uint8_t *name, size_t namelen) {
case 4: case 4:
switch (name[3]) { switch (name[3]) {
case 'D': case 'D':
if (util::streq_l("BIN", name, 3)) {
return HTTP_BIND;
}
if (util::streq_l("HEA", name, 3)) { if (util::streq_l("HEA", name, 3)) {
return HTTP_HEAD; return HTTP_HEAD;
} }
@ -1409,6 +1419,9 @@ int lookup_method_token(const uint8_t *name, size_t namelen) {
} }
break; break;
case 'K': case 'K':
if (util::streq_l("LIN", name, 3)) {
return HTTP_LINK;
}
if (util::streq_l("LOC", name, 3)) { if (util::streq_l("LOC", name, 3)) {
return HTTP_LOCK; return HTTP_LOCK;
} }
@ -1452,10 +1465,21 @@ int lookup_method_token(const uint8_t *name, size_t namelen) {
break; break;
case 6: case 6:
switch (name[5]) { switch (name[5]) {
case 'D':
if (util::streq_l("REBIN", name, 5)) {
return HTTP_REBIND;
}
if (util::streq_l("UNBIN", name, 5)) {
return HTTP_UNBIND;
}
break;
case 'E': case 'E':
if (util::streq_l("DELET", name, 5)) { if (util::streq_l("DELET", name, 5)) {
return HTTP_DELETE; return HTTP_DELETE;
} }
if (util::streq_l("SOURC", name, 5)) {
return HTTP_SOURCE;
}
break; break;
case 'H': case 'H':
if (util::streq_l("SEARC", name, 5)) { if (util::streq_l("SEARC", name, 5)) {
@ -1463,6 +1487,9 @@ int lookup_method_token(const uint8_t *name, size_t namelen) {
} }
break; break;
case 'K': case 'K':
if (util::streq_l("UNLIN", name, 5)) {
return HTTP_UNLINK;
}
if (util::streq_l("UNLOC", name, 5)) { if (util::streq_l("UNLOC", name, 5)) {
return HTTP_UNLOCK; return HTTP_UNLOCK;
} }
@ -1554,8 +1581,9 @@ int lookup_method_token(const uint8_t *name, size_t namelen) {
} }
StringRef to_method_string(int method_token) { StringRef to_method_string(int method_token) {
// we happened to use same value for method with http-parser. // we happened to use same value for method with llhttp.
return StringRef{http_method_str(static_cast<http_method>(method_token))}; return StringRef{
llhttp_method_name(static_cast<llhttp_method>(method_token))};
} }
StringRef get_pure_path_component(const StringRef &uri) { StringRef get_pure_path_component(const StringRef &uri) {

View File

@ -35,7 +35,7 @@
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#include "http-parser/http_parser.h" #include "url-parser/url_parser.h"
#include "util.h" #include "util.h"
#include "memchunk.h" #include "memchunk.h"
@ -396,15 +396,15 @@ bool expect_response_body(int method_token, int status_code);
bool expect_response_body(int status_code); bool expect_response_body(int status_code);
// Looks up method token for method name |name| of length |namelen|. // Looks up method token for method name |name| of length |namelen|.
// Only methods defined in http-parser/http-parser.h (http_method) are // Only methods defined in llhttp.h (llhttp_method) are tokenized. If
// tokenized. If method name cannot be tokenized, returns -1. // method name cannot be tokenized, returns -1.
int lookup_method_token(const uint8_t *name, size_t namelen); int lookup_method_token(const uint8_t *name, size_t namelen);
int lookup_method_token(const StringRef &name); int lookup_method_token(const StringRef &name);
// Returns string representation of |method_token|. This is wrapper // Returns string representation of |method_token|. This is wrapper
// function over http_method_str from http-parser. If |method_token| // around llhttp_method_name from llhttp. If |method_token| is
// is not known to http-parser, "<unknown>" is returned. The returned // unknown, program aborts. The returned StringRef is guaranteed to
// StringRef is guaranteed to be NULL-terminated. // be NULL-terminated.
StringRef to_method_string(int method_token); StringRef to_method_string(int method_token);
StringRef normalize_path(BlockAllocator &balloc, const StringRef &path, StringRef normalize_path(BlockAllocator &balloc, const StringRef &path,

View File

@ -30,7 +30,7 @@
#include <CUnit/CUnit.h> #include <CUnit/CUnit.h>
#include "http-parser/http_parser.h" #include "url-parser/url_parser.h"
#include "http2.h" #include "http2.h"
#include "util.h" #include "util.h"

View File

@ -401,7 +401,7 @@ void ContinueTimer::dispatch_continue() {
} }
namespace { namespace {
int htp_msg_begincb(http_parser *htp) { int htp_msg_begincb(llhttp_t *htp) {
if (config.verbose) { if (config.verbose) {
print_timer(); print_timer();
std::cout << " HTTP Upgrade response" << std::endl; std::cout << " HTTP Upgrade response" << std::endl;
@ -411,7 +411,7 @@ int htp_msg_begincb(http_parser *htp) {
} // namespace } // namespace
namespace { namespace {
int htp_msg_completecb(http_parser *htp) { int htp_msg_completecb(llhttp_t *htp) {
auto client = static_cast<HttpClient *>(htp->data); auto client = static_cast<HttpClient *>(htp->data);
client->upgrade_response_status_code = htp->status_code; client->upgrade_response_status_code = htp->status_code;
client->upgrade_response_complete = true; client->upgrade_response_complete = true;
@ -420,15 +420,17 @@ int htp_msg_completecb(http_parser *htp) {
} // namespace } // namespace
namespace { namespace {
constexpr http_parser_settings htp_hooks = { constexpr llhttp_settings_t htp_hooks = {
htp_msg_begincb, // http_cb on_message_begin; htp_msg_begincb, // llhttp_cb on_message_begin;
nullptr, // http_data_cb on_url; nullptr, // llhttp_data_cb on_url;
nullptr, // http_data_cb on_status; nullptr, // llhttp_data_cb on_status;
nullptr, // http_data_cb on_header_field; nullptr, // llhttp_data_cb on_header_field;
nullptr, // http_data_cb on_header_value; nullptr, // llhttp_data_cb on_header_value;
nullptr, // http_cb on_headers_complete; nullptr, // llhttp_cb on_headers_complete;
nullptr, // http_data_cb on_body; nullptr, // llhttp_data_cb on_body;
htp_msg_completecb // http_cb on_message_complete; htp_msg_completecb, // llhttp_cb on_message_complete;
nullptr, // llhttp_cb on_chunk_header
nullptr, // llhttp_cb on_chunk_complete
}; };
} // namespace } // namespace
@ -885,8 +887,8 @@ int HttpClient::connected() {
writefn = &HttpClient::write_clear; writefn = &HttpClient::write_clear;
if (need_upgrade()) { if (need_upgrade()) {
htp = std::make_unique<http_parser>(); htp = std::make_unique<llhttp_t>();
http_parser_init(htp.get(), HTTP_RESPONSE); llhttp_init(htp.get(), HTTP_RESPONSE, &htp_hooks);
htp->data = this; htp->data = this;
return do_write(); return do_write();
@ -1031,19 +1033,20 @@ int HttpClient::on_upgrade_connect() {
int HttpClient::on_upgrade_read(const uint8_t *data, size_t len) { int HttpClient::on_upgrade_read(const uint8_t *data, size_t len) {
int rv; int rv;
auto nread = http_parser_execute(htp.get(), &htp_hooks, auto htperr =
reinterpret_cast<const char *>(data), len); llhttp_execute(htp.get(), reinterpret_cast<const char *>(data), len);
auto nread = static_cast<size_t>(
reinterpret_cast<const uint8_t *>(llhttp_get_error_pos(htp.get())) -
data);
if (config.verbose) { if (config.verbose) {
std::cout.write(reinterpret_cast<const char *>(data), nread); std::cout.write(reinterpret_cast<const char *>(data), nread);
} }
auto htperr = HTTP_PARSER_ERRNO(htp.get()); if (htperr != HPE_OK && htperr != HPE_PAUSED_UPGRADE) {
if (htperr != HPE_OK) {
std::cerr << "[ERROR] Failed to parse HTTP Upgrade response header: " std::cerr << "[ERROR] Failed to parse HTTP Upgrade response header: "
<< "(" << http_errno_name(htperr) << ") " << "(" << llhttp_errno_name(htperr) << ") "
<< http_errno_description(htperr) << std::endl; << llhttp_get_error_reason(htp.get()) << std::endl;
return -1; return -1;
} }

View File

@ -47,7 +47,7 @@
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#include "http-parser/http_parser.h" #include "llhttp.h"
#include "memchunk.h" #include "memchunk.h"
#include "http2.h" #include "http2.h"
@ -265,7 +265,7 @@ struct HttpClient {
std::string host; std::string host;
std::string hostport; std::string hostport;
// Used for parse the HTTP upgrade response from server // Used for parse the HTTP upgrade response from server
std::unique_ptr<http_parser> htp; std::unique_ptr<llhttp_t> htp;
SessionTiming timing; SessionTiming timing;
ev_io wev; ev_io wev;
ev_io rev; ev_io rev;

View File

@ -51,7 +51,7 @@
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#include "http-parser/http_parser.h" #include "url-parser/url_parser.h"
#include "shrpx_log.h" #include "shrpx_log.h"
#include "shrpx_tls.h" #include "shrpx_tls.h"

View File

@ -26,7 +26,7 @@
#include <cassert> #include <cassert>
#include "http-parser/http_parser.h" #include "url-parser/url_parser.h"
#include "shrpx_upstream.h" #include "shrpx_upstream.h"
#include "shrpx_client_handler.h" #include "shrpx_client_handler.h"

View File

@ -38,6 +38,8 @@
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#include "llhttp.h"
#include "shrpx_io_control.h" #include "shrpx_io_control.h"
#include "shrpx_log_config.h" #include "shrpx_log_config.h"
#include "http2.h" #include "http2.h"

View File

@ -28,7 +28,7 @@
# include <unistd.h> # include <unistd.h>
#endif // HAVE_UNISTD_H #endif // HAVE_UNISTD_H
#include "http-parser/http_parser.h" #include "llhttp.h"
#include "shrpx_client_handler.h" #include "shrpx_client_handler.h"
#include "shrpx_upstream.h" #include "shrpx_upstream.h"

View File

@ -336,6 +336,25 @@ int Http2Session::resolve_name() {
} }
} }
namespace {
int htp_hdrs_completecb(llhttp_t *htp);
} // namespace
namespace {
constexpr llhttp_settings_t htp_hooks = {
nullptr, // llhttp_cb on_message_begin;
nullptr, // llhttp_data_cb on_url;
nullptr, // llhttp_data_cb on_status;
nullptr, // llhttp_data_cb on_header_field;
nullptr, // llhttp_data_cb on_header_value;
htp_hdrs_completecb, // llhttp_cb on_headers_complete;
nullptr, // llhttp_data_cb on_body;
nullptr, // llhttp_cb on_message_complete;
nullptr, // llhttp_cb on_chunk_header
nullptr, // llhttp_cb on_chunk_complete
};
} // namespace
int Http2Session::initiate_connection() { int Http2Session::initiate_connection() {
int rv = 0; int rv = 0;
@ -402,8 +421,8 @@ int Http2Session::initiate_connection() {
on_read_ = &Http2Session::downstream_read_proxy; on_read_ = &Http2Session::downstream_read_proxy;
on_write_ = &Http2Session::downstream_connect_proxy; on_write_ = &Http2Session::downstream_connect_proxy;
proxy_htp_ = std::make_unique<http_parser>(); proxy_htp_ = std::make_unique<llhttp_t>();
http_parser_init(proxy_htp_.get(), HTTP_RESPONSE); llhttp_init(proxy_htp_.get(), HTTP_RESPONSE, &htp_hooks);
proxy_htp_->data = this; proxy_htp_->data = this;
state_ = Http2SessionState::PROXY_CONNECTING; state_ = Http2SessionState::PROXY_CONNECTING;
@ -604,19 +623,12 @@ int Http2Session::initiate_connection() {
} }
namespace { namespace {
int htp_hdrs_completecb(http_parser *htp) { int htp_hdrs_completecb(llhttp_t *htp) {
auto http2session = static_cast<Http2Session *>(htp->data); auto http2session = static_cast<Http2Session *>(htp->data);
// We only read HTTP header part. If tunneling succeeds, response // We only read HTTP header part. If tunneling succeeds, response
// body is a different protocol (HTTP/2 in this case), we don't read // body is a different protocol (HTTP/2 in this case), we don't read
// them here. // them here.
//
// Here is a caveat: http-parser returns 1 less bytes if we pause
// here. The reason why they do this is probably they want to eat
// last 1 byte in s_headers_done state, on the other hand, this
// callback is called its previous state s_headers_almost_done. We
// will do "+ 1" to the return value to workaround this.
http_parser_pause(htp, 1);
// We just check status code here // We just check status code here
if (htp->status_code / 100 == 2) { if (htp->status_code / 100 == 2) {
@ -625,37 +637,19 @@ int htp_hdrs_completecb(http_parser *htp) {
} }
http2session->set_state(Http2SessionState::PROXY_CONNECTED); http2session->set_state(Http2SessionState::PROXY_CONNECTED);
return 0; return HPE_PAUSED;
} }
SSLOG(WARN, http2session) << "Tunneling failed: " << htp->status_code; SSLOG(WARN, http2session) << "Tunneling failed: " << htp->status_code;
http2session->set_state(Http2SessionState::PROXY_FAILED); http2session->set_state(Http2SessionState::PROXY_FAILED);
return 0; return HPE_PAUSED;
} }
} // namespace } // namespace
namespace {
constexpr http_parser_settings htp_hooks = {
nullptr, // http_cb on_message_begin;
nullptr, // http_data_cb on_url;
nullptr, // http_data_cb on_status;
nullptr, // http_data_cb on_header_field;
nullptr, // http_data_cb on_header_value;
htp_hdrs_completecb, // http_cb on_headers_complete;
nullptr, // http_data_cb on_body;
nullptr // http_cb on_message_complete;
};
} // namespace
int Http2Session::downstream_read_proxy(const uint8_t *data, size_t datalen) { int Http2Session::downstream_read_proxy(const uint8_t *data, size_t datalen) {
auto nread = auto htperr = llhttp_execute(proxy_htp_.get(),
http_parser_execute(proxy_htp_.get(), &htp_hooks, reinterpret_cast<const char *>(data), datalen);
reinterpret_cast<const char *>(data), datalen);
(void)nread;
auto htperr = HTTP_PARSER_ERRNO(proxy_htp_.get());
if (htperr == HPE_PAUSED) { if (htperr == HPE_PAUSED) {
switch (state_) { switch (state_) {
case Http2SessionState::PROXY_CONNECTED: case Http2SessionState::PROXY_CONNECTED:

View File

@ -36,7 +36,7 @@
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#include "http-parser/http_parser.h" #include "llhttp.h"
#include "shrpx_connection.h" #include "shrpx_connection.h"
#include "buffer.h" #include "buffer.h"
@ -265,7 +265,7 @@ private:
std::function<int(Http2Session &, const uint8_t *, size_t)> on_read_; std::function<int(Http2Session &, const uint8_t *, size_t)> on_read_;
std::function<int(Http2Session &)> on_write_; std::function<int(Http2Session &)> on_write_;
// Used to parse the response from HTTP proxy // Used to parse the response from HTTP proxy
std::unique_ptr<http_parser> proxy_htp_; std::unique_ptr<llhttp_t> proxy_htp_;
Worker *worker_; Worker *worker_;
// NULL if no TLS is configured // NULL if no TLS is configured
SSL_CTX *ssl_ctx_; SSL_CTX *ssl_ctx_;

View File

@ -243,6 +243,30 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
return 0; return 0;
} }
namespace {
int htp_msg_begincb(llhttp_t *htp);
int htp_hdr_keycb(llhttp_t *htp, const char *data, size_t len);
int htp_hdr_valcb(llhttp_t *htp, const char *data, size_t len);
int htp_hdrs_completecb(llhttp_t *htp);
int htp_bodycb(llhttp_t *htp, const char *data, size_t len);
int htp_msg_completecb(llhttp_t *htp);
} // namespace
namespace {
constexpr llhttp_settings_t htp_hooks = {
htp_msg_begincb, // llhttp_cb on_message_begin;
nullptr, // llhttp_data_cb on_url;
nullptr, // llhttp_data_cb on_status;
htp_hdr_keycb, // llhttp_data_cb on_header_field;
htp_hdr_valcb, // llhttp_data_cb on_header_value;
htp_hdrs_completecb, // llhttp_cb on_headers_complete;
htp_bodycb, // llhttp_data_cb on_body;
htp_msg_completecb, // llhttp_cb on_message_complete;
nullptr, // llhttp_cb on_chunk_header
nullptr, // llhttp_cb on_chunk_complete
};
} // namespace
int HttpDownstreamConnection::initiate_connection() { int HttpDownstreamConnection::initiate_connection() {
int rv; int rv;
@ -416,7 +440,7 @@ int HttpDownstreamConnection::initiate_connection() {
request_header_written_ = false; request_header_written_ = false;
} }
http_parser_init(&response_htp_, HTTP_RESPONSE); llhttp_init(&response_htp_, HTTP_RESPONSE, &htp_hooks);
response_htp_.data = downstream_; response_htp_.data = downstream_;
return 0; return 0;
@ -855,11 +879,12 @@ void HttpDownstreamConnection::force_resume_read() {
} }
namespace { namespace {
int htp_msg_begincb(http_parser *htp) { int htp_msg_begincb(llhttp_t *htp) {
auto downstream = static_cast<Downstream *>(htp->data); auto downstream = static_cast<Downstream *>(htp->data);
if (downstream->get_response_state() != DownstreamState::INITIAL) { if (downstream->get_response_state() != DownstreamState::INITIAL) {
return -1; llhttp_set_error_reason(htp, "HTTP message started when it shouldn't");
return HPE_USER;
} }
return 0; return 0;
@ -867,7 +892,7 @@ int htp_msg_begincb(http_parser *htp) {
} // namespace } // namespace
namespace { namespace {
int htp_hdrs_completecb(http_parser *htp) { int htp_hdrs_completecb(llhttp_t *htp) {
auto downstream = static_cast<Downstream *>(htp->data); auto downstream = static_cast<Downstream *>(htp->data);
auto upstream = downstream->get_upstream(); auto upstream = downstream->get_upstream();
auto handler = upstream->get_client_handler(); auto handler = upstream->get_client_handler();
@ -948,7 +973,7 @@ int htp_hdrs_completecb(http_parser *htp) {
return 1; return 1;
} }
resp.connection_close = !http_should_keep_alive(htp); resp.connection_close = !llhttp_should_keep_alive(htp);
downstream->set_response_state(DownstreamState::HEADER_COMPLETE); downstream->set_response_state(DownstreamState::HEADER_COMPLETE);
downstream->inspect_http1_response(); downstream->inspect_http1_response();
if (downstream->get_upgraded()) { if (downstream->get_upgraded()) {
@ -994,7 +1019,7 @@ int htp_hdrs_completecb(http_parser *htp) {
// https://tools.ietf.org/html/rfc7230#section-3.3 // https://tools.ietf.org/html/rfc7230#section-3.3
// TODO It seems that the cases other than HEAD are handled by // TODO It seems that the cases other than HEAD are handled by
// http-parser. Need test. // llhttp. Need test.
return !http2::expect_response_body(req.method, resp.http_status); return !http2::expect_response_body(req.method, resp.http_status);
} }
} // namespace } // namespace
@ -1034,7 +1059,7 @@ int ensure_max_header_fields(const Downstream *downstream,
} // namespace } // namespace
namespace { namespace {
int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { int htp_hdr_keycb(llhttp_t *htp, const char *data, size_t len) {
auto downstream = static_cast<Downstream *>(htp->data); auto downstream = static_cast<Downstream *>(htp->data);
auto &resp = downstream->response(); auto &resp = downstream->response();
auto &httpconf = get_config()->http; auto &httpconf = get_config()->http;
@ -1071,7 +1096,7 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
} // namespace } // namespace
namespace { namespace {
int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) { int htp_hdr_valcb(llhttp_t *htp, const char *data, size_t len) {
auto downstream = static_cast<Downstream *>(htp->data); auto downstream = static_cast<Downstream *>(htp->data);
auto &resp = downstream->response(); auto &resp = downstream->response();
auto &httpconf = get_config()->http; auto &httpconf = get_config()->http;
@ -1090,7 +1115,7 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
} // namespace } // namespace
namespace { namespace {
int htp_bodycb(http_parser *htp, const char *data, size_t len) { int htp_bodycb(llhttp_t *htp, const char *data, size_t len) {
auto downstream = static_cast<Downstream *>(htp->data); auto downstream = static_cast<Downstream *>(htp->data);
auto &resp = downstream->response(); auto &resp = downstream->response();
@ -1102,14 +1127,13 @@ int htp_bodycb(http_parser *htp, const char *data, size_t len) {
} // namespace } // namespace
namespace { namespace {
int htp_msg_completecb(http_parser *htp) { int htp_msg_completecb(llhttp_t *htp) {
auto downstream = static_cast<Downstream *>(htp->data); auto downstream = static_cast<Downstream *>(htp->data);
// http-parser does not treat "200 connection established" response // llhttp does not treat "200 connection established" response
// against CONNECT request, and in that case, this function is not // against CONNECT request, and in that case, this function is not
// called. But if HTTP Upgrade is made (e.g., WebSocket), this // called. But if HTTP Upgrade is made (e.g., WebSocket), this
// function is called, and http_parser_execute() returns just after // function is called, and llhttp_execute() returns just after that.
// that.
if (downstream->get_upgraded()) { if (downstream->get_upgraded()) {
return 0; return 0;
} }
@ -1129,19 +1153,6 @@ int htp_msg_completecb(http_parser *htp) {
} }
} // namespace } // namespace
namespace {
constexpr http_parser_settings htp_hooks = {
htp_msg_begincb, // http_cb on_message_begin;
nullptr, // http_data_cb on_url;
nullptr, // http_data_cb on_status;
htp_hdr_keycb, // http_data_cb on_header_field;
htp_hdr_valcb, // http_data_cb on_header_value;
htp_hdrs_completecb, // http_cb on_headers_complete;
htp_bodycb, // http_data_cb on_body;
htp_msg_completecb // http_cb on_message_complete;
};
} // namespace
int HttpDownstreamConnection::write_first() { int HttpDownstreamConnection::write_first() {
int rv; int rv;
@ -1389,13 +1400,14 @@ int HttpDownstreamConnection::process_input(const uint8_t *data,
return 0; return 0;
} }
auto nproc = auto htperr = llhttp_execute(&response_htp_,
http_parser_execute(&response_htp_, &htp_hooks, reinterpret_cast<const char *>(data), datalen);
reinterpret_cast<const char *>(data), datalen); auto nproc = static_cast<size_t>(
reinterpret_cast<const uint8_t *>(llhttp_get_error_pos(&response_htp_)) -
data);
auto htperr = HTTP_PARSER_ERRNO(&response_htp_); if (htperr != HPE_OK &&
(!downstream_->get_upgraded() || htperr != HPE_PAUSED_UPGRADE)) {
if (htperr != HPE_OK) {
// Handling early return (in other words, response was hijacked by // Handling early return (in other words, response was hijacked by
// mruby scripting). // mruby scripting).
if (downstream_->get_response_state() == DownstreamState::MSG_COMPLETE) { if (downstream_->get_response_state() == DownstreamState::MSG_COMPLETE) {
@ -1404,8 +1416,8 @@ int HttpDownstreamConnection::process_input(const uint8_t *data,
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
DCLOG(INFO, this) << "HTTP parser failure: " DCLOG(INFO, this) << "HTTP parser failure: "
<< "(" << http_errno_name(htperr) << ") " << "(" << llhttp_errno_name(htperr) << ") "
<< http_errno_description(htperr); << llhttp_get_error_reason(&response_htp_);
} }
return -1; return -1;

View File

@ -27,7 +27,7 @@
#include "shrpx.h" #include "shrpx.h"
#include "http-parser/http_parser.h" #include "llhttp.h"
#include "shrpx_downstream_connection.h" #include "shrpx_downstream_connection.h"
#include "shrpx_io_control.h" #include "shrpx_io_control.h"
@ -110,7 +110,7 @@ private:
std::unique_ptr<Address> resolved_addr_; std::unique_ptr<Address> resolved_addr_;
std::unique_ptr<DNSQuery> dns_query_; std::unique_ptr<DNSQuery> dns_query_;
IOControl ioctrl_; IOControl ioctrl_;
http_parser response_htp_; llhttp_t response_htp_;
// true if first write succeeded. // true if first write succeeded.
bool first_write_done_; bool first_write_done_;
// true if this object can be reused // true if this object can be reused

View File

@ -45,17 +45,43 @@
#include "util.h" #include "util.h"
#include "template.h" #include "template.h"
#include "base64.h" #include "base64.h"
#include "url-parser/url_parser.h"
using namespace nghttp2; using namespace nghttp2;
namespace shrpx { namespace shrpx {
namespace {
int htp_msg_begin(llhttp_t *htp);
int htp_uricb(llhttp_t *htp, const char *data, size_t len);
int htp_hdr_keycb(llhttp_t *htp, const char *data, size_t len);
int htp_hdr_valcb(llhttp_t *htp, const char *data, size_t len);
int htp_hdrs_completecb(llhttp_t *htp);
int htp_bodycb(llhttp_t *htp, const char *data, size_t len);
int htp_msg_completecb(llhttp_t *htp);
} // namespace
namespace {
constexpr llhttp_settings_t htp_hooks = {
htp_msg_begin, // llhttp_cb on_message_begin;
htp_uricb, // llhttp_data_cb on_url;
nullptr, // llhttp_data_cb on_status;
htp_hdr_keycb, // llhttp_data_cb on_header_field;
htp_hdr_valcb, // llhttp_data_cb on_header_value;
htp_hdrs_completecb, // llhttp_cb on_headers_complete;
htp_bodycb, // llhttp_data_cb on_body;
htp_msg_completecb, // llhttp_cb on_message_complete;
nullptr, // llhttp_cb on_chunk_header;
nullptr, // llhttp_cb on_chunk_complete;
};
} // namespace
HttpsUpstream::HttpsUpstream(ClientHandler *handler) HttpsUpstream::HttpsUpstream(ClientHandler *handler)
: handler_(handler), : handler_(handler),
current_header_length_(0), current_header_length_(0),
ioctrl_(handler->get_rlimit()), ioctrl_(handler->get_rlimit()),
num_requests_(0) { num_requests_(0) {
http_parser_init(&htp_, HTTP_REQUEST); llhttp_init(&htp_, HTTP_REQUEST, &htp_hooks);
htp_.data = this; htp_.data = this;
} }
@ -87,7 +113,7 @@ void HttpsUpstream::on_start_request() {
} }
namespace { namespace {
int htp_msg_begin(http_parser *htp) { int htp_msg_begin(llhttp_t *htp) {
auto upstream = static_cast<HttpsUpstream *>(htp->data); auto upstream = static_cast<HttpsUpstream *>(htp->data);
upstream->on_start_request(); upstream->on_start_request();
return 0; return 0;
@ -95,7 +121,7 @@ int htp_msg_begin(http_parser *htp) {
} // namespace } // namespace
namespace { namespace {
int htp_uricb(http_parser *htp, const char *data, size_t len) { int htp_uricb(llhttp_t *htp, const char *data, size_t len) {
auto upstream = static_cast<HttpsUpstream *>(htp->data); auto upstream = static_cast<HttpsUpstream *>(htp->data);
auto downstream = upstream->get_downstream(); auto downstream = upstream->get_downstream();
auto &req = downstream->request(); auto &req = downstream->request();
@ -114,7 +140,8 @@ int htp_uricb(http_parser *htp, const char *data, size_t len) {
assert(downstream->get_request_state() == DownstreamState::INITIAL); assert(downstream->get_request_state() == DownstreamState::INITIAL);
downstream->set_request_state( downstream->set_request_state(
DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE); DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE);
return -1; llhttp_set_error_reason(htp, "too long request URI");
return HPE_USER;
} }
req.fs.add_extra_buffer_size(len); req.fs.add_extra_buffer_size(len);
@ -131,7 +158,7 @@ int htp_uricb(http_parser *htp, const char *data, size_t len) {
} // namespace } // namespace
namespace { namespace {
int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { int htp_hdr_keycb(llhttp_t *htp, const char *data, size_t len) {
auto upstream = static_cast<HttpsUpstream *>(htp->data); auto upstream = static_cast<HttpsUpstream *>(htp->data);
auto downstream = upstream->get_downstream(); auto downstream = upstream->get_downstream();
auto &req = downstream->request(); auto &req = downstream->request();
@ -146,7 +173,8 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
downstream->set_request_state( downstream->set_request_state(
DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE); DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE);
} }
return -1; llhttp_set_error_reason(htp, "too large header");
return HPE_USER;
} }
if (downstream->get_request_state() == DownstreamState::INITIAL) { if (downstream->get_request_state() == DownstreamState::INITIAL) {
if (req.fs.header_key_prev()) { if (req.fs.header_key_prev()) {
@ -159,7 +187,8 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
} }
downstream->set_request_state( downstream->set_request_state(
DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE); DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE);
return -1; llhttp_set_error_reason(htp, "too many headers");
return HPE_USER;
} }
req.fs.alloc_add_header_name(StringRef{data, len}); req.fs.alloc_add_header_name(StringRef{data, len});
} }
@ -173,7 +202,8 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
ULOG(INFO, upstream) ULOG(INFO, upstream)
<< "Too many header field num=" << req.fs.num_fields() + 1; << "Too many header field num=" << req.fs.num_fields() + 1;
} }
return -1; llhttp_set_error_reason(htp, "too many headers");
return HPE_USER;
} }
req.fs.alloc_add_trailer_name(StringRef{data, len}); req.fs.alloc_add_trailer_name(StringRef{data, len});
} }
@ -183,7 +213,7 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
} // namespace } // namespace
namespace { namespace {
int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) { int htp_hdr_valcb(llhttp_t *htp, const char *data, size_t len) {
auto upstream = static_cast<HttpsUpstream *>(htp->data); auto upstream = static_cast<HttpsUpstream *>(htp->data);
auto downstream = upstream->get_downstream(); auto downstream = upstream->get_downstream();
auto &req = downstream->request(); auto &req = downstream->request();
@ -198,7 +228,8 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
downstream->set_request_state( downstream->set_request_state(
DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE); DownstreamState::HTTP1_REQUEST_HEADER_TOO_LARGE);
} }
return -1; llhttp_set_error_reason(htp, "too large header");
return HPE_USER;
} }
if (downstream->get_request_state() == DownstreamState::INITIAL) { if (downstream->get_request_state() == DownstreamState::INITIAL) {
req.fs.append_last_header_value(data, len); req.fs.append_last_header_value(data, len);
@ -286,7 +317,7 @@ void rewrite_request_host_path_from_uri(BlockAllocator &balloc, Request &req,
} // namespace } // namespace
namespace { namespace {
int htp_hdrs_completecb(http_parser *htp) { int htp_hdrs_completecb(llhttp_t *htp) {
int rv; int rv;
auto upstream = static_cast<HttpsUpstream *>(htp->data); auto upstream = static_cast<HttpsUpstream *>(htp->data);
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
@ -305,7 +336,7 @@ int htp_hdrs_completecb(http_parser *htp) {
req.http_major = htp->http_major; req.http_major = htp->http_major;
req.http_minor = htp->http_minor; req.http_minor = htp->http_minor;
req.connection_close = !http_should_keep_alive(htp); req.connection_close = !llhttp_should_keep_alive(htp);
handler->stop_read_timer(); handler->stop_read_timer();
@ -332,15 +363,11 @@ int htp_hdrs_completecb(http_parser *htp) {
// transfer-encoding is given. If transfer-encoding is given, leave // transfer-encoding is given. If transfer-encoding is given, leave
// req.fs.content_length to -1. // req.fs.content_length to -1.
if (method != HTTP_CONNECT && !req.fs.header(http2::HD_TRANSFER_ENCODING)) { if (method != HTTP_CONNECT && !req.fs.header(http2::HD_TRANSFER_ENCODING)) {
// http-parser returns (uint64_t)-1 if there is no content-length // llhttp sets 0 to htp->content_length if there is no
// header field. If we don't have both transfer-encoding, and // content-length header field. If we don't have both
// content-length header field, we assume that there is no request // transfer-encoding and content-length header field, we assume
// body. // that there is no request body.
if (htp->content_length == std::numeric_limits<uint64_t>::max()) { req.fs.content_length = htp->content_length;
req.fs.content_length = 0;
} else {
req.fs.content_length = htp->content_length;
}
} }
auto host = req.fs.header(http2::HD_HOST); auto host = req.fs.header(http2::HD_HOST);
@ -440,7 +467,6 @@ int htp_hdrs_completecb(http_parser *htp) {
upstream->redirect_to_https(downstream); upstream->redirect_to_https(downstream);
} }
downstream->set_request_state(DownstreamState::CONNECT_FAIL); downstream->set_request_state(DownstreamState::CONNECT_FAIL);
return -1; return -1;
} }
@ -494,7 +520,7 @@ int htp_hdrs_completecb(http_parser *htp) {
} // namespace } // namespace
namespace { namespace {
int htp_bodycb(http_parser *htp, const char *data, size_t len) { int htp_bodycb(llhttp_t *htp, const char *data, size_t len) {
int rv; int rv;
auto upstream = static_cast<HttpsUpstream *>(htp->data); auto upstream = static_cast<HttpsUpstream *>(htp->data);
auto downstream = upstream->get_downstream(); auto downstream = upstream->get_downstream();
@ -507,14 +533,15 @@ int htp_bodycb(http_parser *htp, const char *data, size_t len) {
return 0; return 0;
} }
return -1; llhttp_set_error_reason(htp, "could not process request body");
return HPE_USER;
} }
return 0; return 0;
} }
} // namespace } // namespace
namespace { namespace {
int htp_msg_completecb(http_parser *htp) { int htp_msg_completecb(llhttp_t *htp) {
int rv; int rv;
auto upstream = static_cast<HttpsUpstream *>(htp->data); auto upstream = static_cast<HttpsUpstream *>(htp->data);
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
@ -532,11 +559,10 @@ int htp_msg_completecb(http_parser *htp) {
// next request handling (if we don't close the connection). We // next request handling (if we don't close the connection). We
// first pause parser here just as we normally do, and call // first pause parser here just as we normally do, and call
// signal_write() to run on_write(). // signal_write() to run on_write().
http_parser_pause(htp, 1); return HPE_PAUSED;
return 0;
} }
return -1; llhttp_set_error_reason(htp, "could not finish request body");
return HPE_USER;
} }
if (handler->get_http2_upgrade_allowed() && if (handler->get_http2_upgrade_allowed() &&
@ -548,24 +574,10 @@ int htp_msg_completecb(http_parser *htp) {
} }
// Stop further processing to complete this request // Stop further processing to complete this request
http_parser_pause(htp, 1); return HPE_PAUSED;
return 0;
} }
} // namespace } // namespace
namespace {
constexpr http_parser_settings htp_hooks = {
htp_msg_begin, // http_cb on_message_begin;
htp_uricb, // http_data_cb on_url;
nullptr, // http_data_cb on_status;
htp_hdr_keycb, // http_data_cb on_header_field;
htp_hdr_valcb, // http_data_cb on_header_value;
htp_hdrs_completecb, // http_cb on_headers_complete;
htp_bodycb, // http_data_cb on_body;
htp_msg_completecb // http_cb on_message_complete;
};
} // namespace
// on_read() does not consume all available data in input buffer if // on_read() does not consume all available data in input buffer if
// one http request is fully received. // one http request is fully received.
int HttpsUpstream::on_read() { int HttpsUpstream::on_read() {
@ -578,7 +590,7 @@ int HttpsUpstream::on_read() {
} }
// downstream can be nullptr here, because it is initialized in the // downstream can be nullptr here, because it is initialized in the
// callback chain called by http_parser_execute() // callback chain called by llhttp_execute()
if (downstream && downstream->get_upgraded()) { if (downstream && downstream->get_upgraded()) {
auto rv = downstream->push_upload_data_chunk(rb->pos(), rb->rleft()); auto rv = downstream->push_upload_data_chunk(rb->pos(), rb->rleft());
@ -613,11 +625,12 @@ int HttpsUpstream::on_read() {
} }
} }
// http_parser_execute() does nothing once it entered error state. // llhttp_execute() does nothing once it entered error state.
auto nread = http_parser_execute(&htp_, &htp_hooks, auto htperr = llhttp_execute(&htp_, reinterpret_cast<const char *>(rb->pos()),
reinterpret_cast<const char *>(rb->pos()), rb->rleft());
rb->rleft());
auto nread = reinterpret_cast<const uint8_t *>(llhttp_get_error_pos(&htp_)) -
rb->pos();
rb->drain(nread); rb->drain(nread);
rlimit->startw(); rlimit->startw();
@ -628,8 +641,6 @@ int HttpsUpstream::on_read() {
// execution // execution
downstream = get_downstream(); downstream = get_downstream();
auto htperr = HTTP_PARSER_ERRNO(&htp_);
if (htperr == HPE_PAUSED) { if (htperr == HPE_PAUSED) {
// We may pause parser in htp_msg_completecb when both side are // We may pause parser in htp_msg_completecb when both side are
// completed. Signal write, so that we can run on_write(). // completed. Signal write, so that we can run on_write().
@ -644,8 +655,8 @@ int HttpsUpstream::on_read() {
if (htperr != HPE_OK) { if (htperr != HPE_OK) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
ULOG(INFO, this) << "HTTP parse failure: " ULOG(INFO, this) << "HTTP parse failure: "
<< "(" << http_errno_name(htperr) << ") " << "(" << llhttp_errno_name(htperr) << ") "
<< http_errno_description(htperr); << llhttp_get_error_reason(&htp_);
} }
if (downstream && if (downstream &&
@ -759,7 +770,7 @@ int HttpsUpstream::resume_read(IOCtrlReason reason, Downstream *downstream,
if (ioctrl_.resume_read(reason)) { if (ioctrl_.resume_read(reason)) {
// Process remaining data in input buffer here because these bytes // Process remaining data in input buffer here because these bytes
// are not notified by readcb until new data arrive. // are not notified by readcb until new data arrive.
http_parser_pause(&htp_, 0); llhttp_resume(&htp_);
auto conn = handler_->get_connection(); auto conn = handler_->get_connection();
ev_feed_event(conn->loop, &conn->rev, EV_READ); ev_feed_event(conn->loop, &conn->rev, EV_READ);

View File

@ -30,7 +30,7 @@
#include <cinttypes> #include <cinttypes>
#include <memory> #include <memory>
#include "http-parser/http_parser.h" #include "llhttp.h"
#include "shrpx_upstream.h" #include "shrpx_upstream.h"
#include "memchunk.h" #include "memchunk.h"
@ -100,7 +100,7 @@ public:
private: private:
ClientHandler *handler_; ClientHandler *handler_;
http_parser htp_; llhttp_t htp_;
size_t current_header_length_; size_t current_header_length_;
std::unique_ptr<Downstream> downstream_; std::unique_ptr<Downstream> downstream_;
IOControl ioctrl_; IOControl ioctrl_;

View File

@ -594,7 +594,8 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
auto &balloc = downstream->get_block_allocator(); auto &balloc = downstream->get_block_allocator();
auto downstream_addr = downstream->get_addr(); auto downstream_addr = downstream->get_addr();
auto method = http2::to_method_string(req.method); auto method = req.method == -1 ? StringRef::from_lit("<unknown>")
: http2::to_method_string(req.method);
auto path = req.method == HTTP_CONNECT auto path = req.method == HTTP_CONNECT
? req.authority ? req.authority
: config->http2_proxy : config->http2_proxy

View File

@ -47,7 +47,7 @@
#include <map> #include <map>
#include <random> #include <random>
#include "http-parser/http_parser.h" #include "url-parser/url_parser.h"
#include "template.h" #include "template.h"
#include "network.h" #include "network.h"

View File

@ -1,9 +1,22 @@
if(ENABLE_THIRD_PARTY) if(ENABLE_THIRD_PARTY)
set(LIBHTTP_PARSER_SOURCES set(LIBLLHTTP_SOURCES
http-parser/http_parser.c llhttp/src/api.c
llhttp/src/http.c
llhttp/src/llhttp.c
) )
add_library(http-parser OBJECT ${LIBHTTP_PARSER_SOURCES}) add_library(llhttp OBJECT ${LIBLLHTTP_SOURCES})
set_target_properties(http-parser PROPERTIES target_include_directories(llhttp PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/llhttp/include"
)
set_target_properties(llhttp PROPERTIES
POSITION_INDEPENDENT_CODE ON
)
set(LIBURL_PARSER_SOURCES
url-parser/url_parser.c
)
add_library(url-parser OBJECT ${LIBURL_PARSER_SOURCES})
set_target_properties(url-parser PROPERTIES
POSITION_INDEPENDENT_CODE ON) POSITION_INDEPENDENT_CODE ON)
if(HAVE_NEVERBLEED) if(HAVE_NEVERBLEED)

View File

@ -27,10 +27,18 @@ EXTRA_DIST = CMakeLists.txt
if ENABLE_THIRD_PARTY if ENABLE_THIRD_PARTY
noinst_LTLIBRARIES = libhttp-parser.la noinst_LTLIBRARIES = liburl-parser.la
libhttp_parser_la_SOURCES = \ liburl_parser_la_SOURCES = \
http-parser/http_parser.c \ url-parser/url_parser.c \
http-parser/http_parser.h url-parser/url_parser.h
noinst_LTLIBRARIES += libllhttp.la
libllhttp_la_SOURCES = \
llhttp/src/api.c \
llhttp/src/http.c \
llhttp/src/llhttp.c \
llhttp/include/llhttp.h
libllhttp_la_CPPFLAGS = -I${srcdir}/llhttp/include
if HAVE_NEVERBLEED if HAVE_NEVERBLEED
noinst_LTLIBRARIES += libneverbleed.la noinst_LTLIBRARIES += libneverbleed.la

View File

@ -1 +0,0 @@
.dirstamp

View File

@ -1,246 +0,0 @@
HTTP Parser
===========
[![Build Status](https://api.travis-ci.org/nodejs/http-parser.svg?branch=master)](https://travis-ci.org/nodejs/http-parser)
This is a parser for HTTP messages written in C. It parses both requests and
responses. The parser is designed to be used in performance HTTP
applications. It does not make any syscalls nor allocations, it does not
buffer data, it can be interrupted at anytime. Depending on your
architecture, it only requires about 40 bytes of data per message
stream (in a web server that is per connection).
Features:
* No dependencies
* Handles persistent streams (keep-alive).
* Decodes chunked encoding.
* Upgrade support
* Defends against buffer overflow attacks.
The parser extracts the following information from HTTP messages:
* Header fields and values
* Content-Length
* Request method
* Response status code
* Transfer-Encoding
* HTTP version
* Request URL
* Message body
Usage
-----
One `http_parser` object is used per TCP connection. Initialize the struct
using `http_parser_init()` and set the callbacks. That might look something
like this for a request parser:
```c
http_parser_settings settings;
settings.on_url = my_url_callback;
settings.on_header_field = my_header_field_callback;
/* ... */
http_parser *parser = malloc(sizeof(http_parser));
http_parser_init(parser, HTTP_REQUEST);
parser->data = my_socket;
```
When data is received on the socket execute the parser and check for errors.
```c
size_t len = 80*1024, nparsed;
char buf[len];
ssize_t recved;
recved = recv(fd, buf, len, 0);
if (recved < 0) {
/* Handle error. */
}
/* Start up / continue the parser.
* Note we pass recved==0 to signal that EOF has been received.
*/
nparsed = http_parser_execute(parser, &settings, buf, recved);
if (parser->upgrade) {
/* handle new protocol */
} else if (nparsed != recved) {
/* Handle error. Usually just close the connection. */
}
```
`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
`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.
Scalar valued message information such as `status_code`, `method`, and the
HTTP version are stored in the parser structure. This data is only
temporally stored in `http_parser` and gets reset on each new message. If
this information is needed later, copy it out of the structure during the
`headers_complete` callback.
The parser decodes the transfer-encoding for both requests and responses
transparently. That is, a chunked encoding is decoded before being sent to
the on_body callback.
The Special Problem of Upgrade
------------------------------
`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
GET /demo HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
WebSocket-Protocol: sample
followed by non-HTTP data.
(See [RFC6455](https://tools.ietf.org/html/rfc6455) for more information the
WebSocket protocol.)
To support this, the parser will treat this as a normal HTTP message without a
body, issuing both on_headers_complete and on_message_complete callbacks. However
http_parser_execute() will stop parsing at the end of the headers and return.
The user is expected to check if `parser->upgrade` has been set to 1 after
`http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied
offset by the return value of `http_parser_execute()`.
Callbacks
---------
During the `http_parser_execute()` call, the callbacks set in
`http_parser_settings` will be executed. The parser maintains state and
never looks behind, so buffering the data is not necessary. If you need to
save certain data for later usage, you can do that from the callbacks.
There are two types of callbacks:
* notification `typedef int (*http_cb) (http_parser*);`
Callbacks: on_message_begin, on_headers_complete, on_message_complete.
* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);`
Callbacks: (requests only) on_url,
(common) on_header_field, on_header_value, on_body;
Callbacks must return 0 on success. Returning a non-zero value indicates
error to the parser, making it exit immediately.
For cases where it is necessary to pass local information to/from a callback,
the `http_parser` object's `data` field can be used.
An example of such a case is when using threads to handle a socket connection,
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
multi-threaded contexts.
Example:
```c
typedef struct {
socket_t sock;
void* buffer;
int buf_len;
} custom_data_t;
int my_url_callback(http_parser* parser, const char *at, size_t length) {
/* access to thread local custom_data_t struct.
Use this access save parsed data for later use into thread local
buffer, or communicate over socket
*/
parser->data;
...
return 0;
}
...
void http_parser_thread(socket_t sock) {
int nparsed = 0;
/* allocate memory for user data */
custom_data_t *my_data = malloc(sizeof(custom_data_t));
/* some information for use by callbacks.
* achieves thread -> callback information flow */
my_data->sock = sock;
/* instantiate a thread-local parser */
http_parser *parser = malloc(sizeof(http_parser));
http_parser_init(parser, HTTP_REQUEST); /* initialise parser */
/* this custom data reference is accessible through the reference to the
parser supplied to callback functions */
parser->data = my_data;
http_parser_settings settings; /* set up callbacks */
settings.on_url = my_url_callback;
/* execute parser */
nparsed = http_parser_execute(parser, &settings, buf, recved);
...
/* parsed information copied from callback.
can now perform action on data copied into thread-local memory from callbacks.
achieves callback -> thread information flow */
my_data->buffer;
...
}
```
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
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.
Reading headers may be a tricky task if you read/parse headers partially.
Basically, you need to remember whether last header callback was field or value
and apply the following logic:
(on_header_field and on_header_value shortened to on_h_*)
------------------------ ------------ --------------------------------------------
| State (prev. callback) | Callback | Description/action |
------------------------ ------------ --------------------------------------------
| nothing (first call) | on_h_field | Allocate new buffer and copy callback data |
| | | into it |
------------------------ ------------ --------------------------------------------
| value | on_h_field | New header started. |
| | | Copy current name,value buffers to headers |
| | | list and allocate new buffer for new name |
------------------------ ------------ --------------------------------------------
| field | on_h_field | Previous name continues. Reallocate name |
| | | buffer and append callback data to it |
------------------------ ------------ --------------------------------------------
| field | on_h_value | Value for current header started. Allocate |
| | | new buffer and copy callback data to it |
------------------------ ------------ --------------------------------------------
| value | on_h_value | Value continues. Reallocate value buffer |
| | | and append callback data to it |
------------------------ ------------ --------------------------------------------
Parsing URLs
------------
A simplistic zero-copy URL parser is provided as `http_parser_parse_url()`.
Users of this library may wish to use it to parse URLs constructed from
consecutive `on_url` callbacks.
See examples of reading in headers:
* [partial example](http://gist.github.com/155877) in C
* [from http-parser tests](http://github.com/joyent/http-parser/blob/37a0ff8/test.c#L403) in C
* [from Node library](http://github.com/joyent/node/blob/842eaf4/src/http.js#L284) in Javascript

View File

@ -1,128 +0,0 @@
/* Copyright Fedor Indutny. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#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"
"DNT: 1\r\n"
"Accept-Encoding: gzip, deflate, sdch\r\n"
"Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4\r\n"
"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/39.0.2171.65 Safari/537.36\r\n"
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,"
"image/webp,*/*;q=0.8\r\n"
"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";
static const size_t data_len = sizeof(data) - 1;
static int on_info(http_parser* p) {
return 0;
}
static int on_data(http_parser* p, const char *at, size_t length) {
return 0;
}
static http_parser_settings settings = {
.on_message_begin = on_info,
.on_headers_complete = on_info,
.on_message_complete = on_info,
.on_header_field = on_data,
.on_header_value = on_data,
.on_url = on_data,
.on_status = on_data,
.on_body = on_data
};
int bench(int iter_count, int silent) {
struct http_parser parser;
int i;
int err;
struct timeval start;
struct timeval end;
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);
parsed = http_parser_execute(&parser, &settings, data, data_len);
assert(parsed == data_len);
}
if (!silent) {
double elapsed;
double bw;
double total;
err = gettimeofday(&end, NULL);
assert(err == 0);
fprintf(stdout, "Benchmark result:\n");
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);
fflush(stdout);
}
return 0;
}
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(iterations, 1);
return 0;
} else {
return bench(iterations, 0);
}
}

View File

@ -1,157 +0,0 @@
/* 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
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/* Dump what the parser finds to stdout as it happen */
#include "http_parser.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int on_message_begin(http_parser* _) {
(void)_;
printf("\n***MESSAGE BEGIN***\n\n");
return 0;
}
int on_headers_complete(http_parser* _) {
(void)_;
printf("\n***HEADERS COMPLETE***\n\n");
return 0;
}
int on_message_complete(http_parser* _) {
(void)_;
printf("\n***MESSAGE COMPLETE***\n\n");
return 0;
}
int on_url(http_parser* _, const char* at, size_t length) {
(void)_;
printf("Url: %.*s\n", (int)length, at);
return 0;
}
int on_header_field(http_parser* _, const char* at, size_t length) {
(void)_;
printf("Header field: %.*s\n", (int)length, at);
return 0;
}
int on_header_value(http_parser* _, const char* at, size_t length) {
(void)_;
printf("Header value: %.*s\n", (int)length, at);
return 0;
}
int on_body(http_parser* _, const char* at, size_t length) {
(void)_;
printf("Body: %.*s\n", (int)length, at);
return 0;
}
void usage(const char* name) {
fprintf(stderr,
"Usage: %s $type $filename\n"
" type: -x, where x is one of {r,b,q}\n"
" parses file as a Response, reQuest, or Both\n",
name);
exit(EXIT_FAILURE);
}
int main(int argc, char* argv[]) {
enum http_parser_type file_type;
if (argc != 3) {
usage(argv[0]);
}
char* type = argv[1];
if (type[0] != '-') {
usage(argv[0]);
}
switch (type[1]) {
/* in the case of "-", type[1] will be NUL */
case 'r':
file_type = HTTP_RESPONSE;
break;
case 'q':
file_type = HTTP_REQUEST;
break;
case 'b':
file_type = HTTP_BOTH;
break;
default:
usage(argv[0]);
}
char* filename = argv[2];
FILE* file = fopen(filename, "r");
if (file == NULL) {
perror("fopen");
goto fail;
}
fseek(file, 0, SEEK_END);
long file_length = ftell(file);
if (file_length == -1) {
perror("ftell");
goto fail;
}
fseek(file, 0, SEEK_SET);
char* data = malloc(file_length);
if (fread(data, 1, file_length, file) != (size_t)file_length) {
fprintf(stderr, "couldn't read entire file\n");
free(data);
goto fail;
}
http_parser_settings settings;
memset(&settings, 0, sizeof(settings));
settings.on_message_begin = on_message_begin;
settings.on_url = on_url;
settings.on_header_field = on_header_field;
settings.on_header_value = on_header_value;
settings.on_headers_complete = on_headers_complete;
settings.on_body = on_body;
settings.on_message_complete = on_message_complete;
http_parser parser;
http_parser_init(&parser, file_type);
size_t nparsed = http_parser_execute(&parser, &settings, data, file_length);
free(data);
if (nparsed != (size_t)file_length) {
fprintf(stderr,
"Error: %s (%s)\n",
http_errno_description(HTTP_PARSER_ERRNO(&parser)),
http_errno_name(HTTP_PARSER_ERRNO(&parser)));
goto fail;
}
return EXIT_SUCCESS;
fail:
fclose(file);
return EXIT_FAILURE;
}

View File

@ -1,47 +0,0 @@
#include "http_parser.h"
#include <stdio.h>
#include <string.h>
void
dump_url (const char *url, const struct http_parser_url *u)
{
unsigned int i;
printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
for (i = 0; i < UF_MAX; i++) {
if ((u->field_set & (1 << i)) == 0) {
printf("\tfield_data[%u]: unset\n", i);
continue;
}
printf("\tfield_data[%u]: off: %u, len: %u, part: %.*s\n",
i,
u->field_data[i].off,
u->field_data[i].len,
u->field_data[i].len,
url + u->field_data[i].off);
}
}
int main(int argc, char ** argv) {
struct http_parser_url u;
int len, connect, result;
if (argc != 3) {
printf("Syntax : %s connect|get url\n", argv[0]);
return 1;
}
len = strlen(argv[2]);
connect = strcmp("connect", argv[1]) == 0 ? 1 : 0;
printf("Parsing %s, connect %d\n", argv[2], connect);
http_parser_url_init(&u);
result = http_parser_parse_url(argv[2], len, connect, &u);
if (result != 0) {
printf("Parse error : %d\n", result);
return result;
}
printf("Parse ok, result : \n");
dump_url(argv[2], &u);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,111 +0,0 @@
# This file is used with the GYP meta build system.
# http://code.google.com/p/gyp/
# To build try this:
# svn co http://gyp.googlecode.com/svn/trunk gyp
# ./gyp/gyp -f make --depth=`pwd` http_parser.gyp
# ./out/Debug/test
{
'target_defaults': {
'default_configuration': 'Debug',
'configurations': {
# TODO: hoist these out and put them somewhere common, because
# RuntimeLibrary MUST MATCH across the entire project
'Debug': {
'defines': [ 'DEBUG', '_DEBUG' ],
'cflags': [ '-Wall', '-Wextra', '-O0', '-g', '-ftrapv' ],
'msvs_settings': {
'VCCLCompilerTool': {
'RuntimeLibrary': 1, # static debug
},
},
},
'Release': {
'defines': [ 'NDEBUG' ],
'cflags': [ '-Wall', '-Wextra', '-O3' ],
'msvs_settings': {
'VCCLCompilerTool': {
'RuntimeLibrary': 0, # static release
},
},
}
},
'msvs_settings': {
'VCCLCompilerTool': {
},
'VCLibrarianTool': {
},
'VCLinkerTool': {
'GenerateDebugInformation': 'true',
},
},
'conditions': [
['OS == "win"', {
'defines': [
'WIN32'
],
}]
],
},
'targets': [
{
'target_name': 'http_parser',
'type': 'static_library',
'include_dirs': [ '.' ],
'direct_dependent_settings': {
'defines': [ 'HTTP_PARSER_STRICT=0' ],
'include_dirs': [ '.' ],
},
'defines': [ 'HTTP_PARSER_STRICT=0' ],
'sources': [ './http_parser.c', ],
'conditions': [
['OS=="win"', {
'msvs_settings': {
'VCCLCompilerTool': {
# Compile as C++. http_parser.c is actually C99, but C++ is
# close enough in this case.
'CompileAs': 2,
},
},
}]
],
},
{
'target_name': 'http_parser_strict',
'type': 'static_library',
'include_dirs': [ '.' ],
'direct_dependent_settings': {
'defines': [ 'HTTP_PARSER_STRICT=1' ],
'include_dirs': [ '.' ],
},
'defines': [ 'HTTP_PARSER_STRICT=1' ],
'sources': [ './http_parser.c', ],
'conditions': [
['OS=="win"', {
'msvs_settings': {
'VCCLCompilerTool': {
# Compile as C++. http_parser.c is actually C99, but C++ is
# close enough in this case.
'CompileAs': 2,
},
},
}]
],
},
{
'target_name': 'test-nonstrict',
'type': 'executable',
'dependencies': [ 'http_parser' ],
'sources': [ 'test.c' ]
},
{
'target_name': 'test-strict',
'type': 'executable',
'dependencies': [ 'http_parser_strict' ],
'sources': [ 'test.c' ]
}
]
}

View File

@ -1,439 +0,0 @@
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef http_parser_h
#define http_parser_h
#ifdef __cplusplus
extern "C" {
#endif
/* Also update SONAME in the Makefile whenever you change these. */
#define HTTP_PARSER_VERSION_MAJOR 2
#define HTTP_PARSER_VERSION_MINOR 9
#define HTTP_PARSER_VERSION_PATCH 1
#include <stddef.h>
#if defined(_WIN32) && !defined(__MINGW32__) && \
(!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
#include <BaseTsd.h>
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
* faster
*/
#ifndef HTTP_PARSER_STRICT
# define HTTP_PARSER_STRICT 1
#endif
/* Maximium header size allowed. If the macro is not defined
* before including this header then the default is used. To
* change the maximum header size, define the macro in the build
* environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
* the effective limit on the size of the header, define the macro
* to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
*/
#ifndef HTTP_MAX_HEADER_SIZE
# define HTTP_MAX_HEADER_SIZE (80*1024)
#endif
typedef struct http_parser http_parser;
typedef struct http_parser_settings http_parser_settings;
/* Callbacks should return non-zero to indicate an error. The parser will
* then halt execution.
*
* The one exception is on_headers_complete. In a HTTP_RESPONSE parser
* returning '1' from on_headers_complete will tell the parser that it
* should not expect a body. This is used when receiving a response to a
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
* chunked' headers that indicate the presence of a body.
*
* Returning `2` from on_headers_complete will tell parser that it should not
* expect neither a body nor any futher responses on this connection. This is
* useful for handling responses to a CONNECT request which may not contain
* `Upgrade` or `Connection: upgrade` headers.
*
* http_data_cb does not return data chunks. It will be called arbitrarily
* many times for each string. E.G. you might get 10 callbacks for "on_url"
* each providing just a few characters more data.
*/
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) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
/* pathological */ \
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
/* WebDAV */ \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
XX(11, MOVE, MOVE) \
XX(12, PROPFIND, PROPFIND) \
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
/* subversion */ \
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
/* upnp */ \
XX(24, MSEARCH, M-SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
/* RFC-5789 */ \
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
/* CalDAV */ \
XX(30, MKCALENDAR, MKCALENDAR) \
/* RFC-2068, section 19.6.1.2 */ \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
/* icecast */ \
XX(33, SOURCE, SOURCE) \
enum http_method
{
#define XX(num, name, string) HTTP_##name = num,
HTTP_METHOD_MAP(XX)
#undef XX
};
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
/* Flag values for http_parser.flags field */
enum flags
{ F_CHUNKED = 1 << 0
, F_CONNECTION_KEEP_ALIVE = 1 << 1
, F_CONNECTION_CLOSE = 1 << 2
, F_CONNECTION_UPGRADE = 1 << 3
, F_TRAILING = 1 << 4
, F_UPGRADE = 1 << 5
, F_SKIPBODY = 1 << 6
, F_CONTENTLENGTH = 1 << 7
};
/* Map for errno-related constants
*
* The provided argument should be a macro that takes 2 arguments.
*/
#define HTTP_ERRNO_MAP(XX) \
/* No error */ \
XX(OK, "success") \
\
/* Callback-related errors */ \
XX(CB_message_begin, "the on_message_begin callback failed") \
XX(CB_url, "the on_url callback failed") \
XX(CB_header_field, "the on_header_field callback failed") \
XX(CB_header_value, "the on_header_value callback failed") \
XX(CB_headers_complete, "the on_headers_complete callback failed") \
XX(CB_body, "the on_body callback failed") \
XX(CB_message_complete, "the on_message_complete callback failed") \
XX(CB_status, "the on_status callback failed") \
XX(CB_chunk_header, "the on_chunk_header callback failed") \
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
\
/* Parsing-related errors */ \
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
XX(HEADER_OVERFLOW, \
"too many header bytes seen; overflow detected") \
XX(CLOSED_CONNECTION, \
"data received after completed connection: close message") \
XX(INVALID_VERSION, "invalid HTTP version") \
XX(INVALID_STATUS, "invalid HTTP status code") \
XX(INVALID_METHOD, "invalid HTTP method") \
XX(INVALID_URL, "invalid URL") \
XX(INVALID_HOST, "invalid host") \
XX(INVALID_PORT, "invalid port") \
XX(INVALID_PATH, "invalid path") \
XX(INVALID_QUERY_STRING, "invalid query string") \
XX(INVALID_FRAGMENT, "invalid fragment") \
XX(LF_EXPECTED, "LF character expected") \
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
XX(INVALID_CONTENT_LENGTH, \
"invalid character in content-length header") \
XX(UNEXPECTED_CONTENT_LENGTH, \
"unexpected content-length header") \
XX(INVALID_CHUNK_SIZE, \
"invalid character in chunk size header") \
XX(INVALID_CONSTANT, "invalid constant string") \
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
XX(STRICT, "strict mode assertion failed") \
XX(PAUSED, "parser is paused") \
XX(UNKNOWN, "an unknown error occurred")
/* Define HPE_* values for each errno value above */
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
enum http_errno {
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
};
#undef HTTP_ERRNO_GEN
/* Get an http_errno value from an http_parser */
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->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;
uint32_t nread; /* # bytes read in various scenarios */
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
/** READ-ONLY **/
unsigned short http_major;
unsigned short http_minor;
unsigned int status_code : 16; /* responses only */
unsigned int method : 8; /* requests only */
unsigned int http_errno : 7;
/* 1 = Upgrade header was present and the parser has exited because of that.
* 0 = No upgrade header present.
* Should be checked when http_parser_execute() returns in addition to
* error checking.
*/
unsigned int upgrade : 1;
/** PUBLIC **/
void *data; /* A pointer to get hook to the "connection" or "socket" object */
};
struct http_parser_settings {
http_cb on_message_begin;
http_data_cb on_url;
http_data_cb on_status;
http_data_cb on_header_field;
http_data_cb on_header_value;
http_cb on_headers_complete;
http_data_cb on_body;
http_cb on_message_complete;
/* When on_chunk_header is called, the current chunk length is stored
* in parser->content_length.
*/
http_cb on_chunk_header;
http_cb on_chunk_complete;
};
enum http_parser_url_fields
{ UF_SCHEMA = 0
, UF_HOST = 1
, UF_PORT = 2
, UF_PATH = 3
, UF_QUERY = 4
, UF_FRAGMENT = 5
, UF_USERINFO = 6
, UF_MAX = 7
};
/* Result structure for http_parser_parse_url().
*
* Callers should index into field_data[] with UF_* values iff field_set
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
* because we probably have padding left over), we convert any port to
* a uint16_t.
*/
struct http_parser_url {
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
uint16_t port; /* Converted UF_PORT string */
struct {
uint16_t off; /* Offset into buffer in which field starts */
uint16_t len; /* Length of run in buffer */
} field_data[UF_MAX];
};
/* Returns the library version. Bits 16-23 contain the major version number,
* bits 8-15 the minor version number and bits 0-7 the patch level.
* Usage example:
*
* unsigned long version = http_parser_version();
* unsigned major = (version >> 16) & 255;
* unsigned minor = (version >> 8) & 255;
* unsigned patch = version & 255;
* printf("http_parser v%u.%u.%u\n", major, minor, patch);
*/
unsigned long http_parser_version(void);
void http_parser_init(http_parser *parser, enum http_parser_type type);
/* Initialize http_parser_settings members to 0
*/
void http_parser_settings_init(http_parser_settings *settings);
/* Executes the parser. Returns number of parsed bytes. Sets
* `parser->http_errno` on error. */
size_t http_parser_execute(http_parser *parser,
const http_parser_settings *settings,
const char *data,
size_t len);
/* If http_should_keep_alive() in the on_headers_complete or
* on_message_complete callback returns 0, then this should be
* the last message on the connection.
* If you are the server, respond with the "Connection: close" header.
* If you are the client, close the connection.
*/
int http_should_keep_alive(const http_parser *parser);
/* 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);
/* Return a string description of the given error */
const char *http_errno_description(enum http_errno err);
/* Initialize all http_parser_url members to 0 */
void http_parser_url_init(struct http_parser_url *u);
/* Parse a URL; return nonzero on failure */
int http_parser_parse_url(const char *buf, size_t buflen,
int is_connect,
struct http_parser_url *u);
/* Pause or un-pause the parser; a nonzero value pauses */
void http_parser_pause(http_parser *parser, int paused);
/* Checks if this is the final chunk of the body. */
int http_body_is_final(const http_parser *parser);
/* Change the maximum header size provided at compile time. */
void http_parser_set_max_header_size(uint32_t size);
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

22
third-party/llhttp/LICENSE-MIT vendored Normal file
View File

@ -0,0 +1,22 @@
This software is licensed under the MIT License.
Copyright Fedor Indutny, 2018.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.

129
third-party/llhttp/README.md vendored Normal file
View File

@ -0,0 +1,129 @@
# llhttp
[![Build Status](https://secure.travis-ci.org/indutny/llhttp.svg)](http://travis-ci.org/nodejs/llhttp)
Port of [http_parser][0] to [llparse][1].
## Why?
Let's face it, [http_parser][0] is practically unmaintainable. Even
introduction of a single new method results in a significant code churn.
This project aims to:
* Make it maintainable
* Verifiable
* Improving benchmarks where possible
## How?
Over time, different approaches for improving [http_parser][0]'s code base
were tried. However, all of them failed due to resulting significant performance
degradation.
This project is a port of [http_parser][0] to TypeScript. [llparse][1] is used
to generate the output C and/or bitcode artifacts, which could be compiled and
linked with the embedder's program (like [Node.js][7]).
## Peformance
So far llhttp outperforms http_parser:
| | input size | bandwidth | reqs/sec | time |
|:----------------|-----------:|-------------:|-----------:|--------:|
| **llhttp** _(C)_ | 8192.00 mb | 1497.88 mb/s | 3020458.87 ops/sec | 5.47 s |
| **llhttp** _(bitcode)_ | 8192.00 mb | 1131.75 mb/s | 2282171.24 ops/sec | 7.24 s |
| **http_parser** | 8192.00 mb | 694.66 mb/s | 1406180.33 req/sec | 11.79 s |
llhttp is faster by approximately **116%**.
## Maintenance
llhttp project has about 1400 lines of TypeScript code describing the parser
itself and around 450 lines of C code and headers providing the helper methods.
The whole [http_parser][0] is implemented in approximately 2500 lines of C, and
436 lines of headers.
All optimizations and multi-character matching in llhttp are generated
automatically, and thus doesn't add any extra maintenance cost. On the contrary,
most of http_parser's code is hand-optimized and unrolled. Instead describing
"how" it should parse the HTTP requests/responses, a maintainer should
implement the new features in [http_parser][0] cautiously, considering
possible performance degradation and manually optimizing the new code.
## Verification
The state machine graph is encoded explicitly in llhttp. The [llparse][1]
automatically checks the graph for absence of loops and correct reporting of the
input ranges (spans) like header names and values. In the future, additional
checks could be performed to get even stricter verification of the llhttp.
## Usage
```C
#include "llhttp.h"
llhttp_t parser;
llhttp_settings_t settings;
/* Initialize user callbacks and settings */
llhttp_settings_init(&settings);
/* Set user callback */
settings.on_message_complete = handle_on_message_complete;
/* Initialize the parser in HTTP_BOTH mode, meaning that it will select between
* HTTP_REQUEST and HTTP_RESPONSE parsing automatically while reading the first
* input.
*/
llhttp_init(&parser, HTTP_BOTH, &settings);
/* Use `llhttp_set_type(&parser, HTTP_REQUEST);` to override the mode */
/* Parse request! */
const char* request = "GET / HTTP/1.1\r\n\r\n";
int request_len = strlen(request);
enum llhttp_errno err = llhttp_execute(&parser, request, request_len);
if (err == HPE_OK) {
/* Successfully parsed! */
} else {
fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(err),
parser.reason);
}
```
---
#### LICENSE
This software is licensed under the MIT License.
Copyright Fedor Indutny, 2018.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
[0]: https://github.com/nodejs/http-parser
[1]: https://github.com/nodejs/llparse
[2]: https://en.wikipedia.org/wiki/Register_allocation#Spilling
[3]: https://en.wikipedia.org/wiki/Tail_call
[4]: https://llvm.org/docs/LangRef.html
[5]: https://llvm.org/docs/LangRef.html#call-instruction
[6]: https://clang.llvm.org/
[7]: https://github.com/nodejs/node

46
third-party/llhttp/common.gypi vendored Normal file
View File

@ -0,0 +1,46 @@
{
'target_defaults': {
'default_configuration': 'Debug',
'configurations': {
# TODO: hoist these out and put them somewhere common, because
# RuntimeLibrary MUST MATCH across the entire project
'Debug': {
'defines': [ 'DEBUG', '_DEBUG' ],
'cflags': [ '-Wall', '-Wextra', '-O0', '-g', '-ftrapv' ],
'msvs_settings': {
'VCCLCompilerTool': {
'RuntimeLibrary': 1, # static debug
},
},
},
'Release': {
'defines': [ 'NDEBUG' ],
'cflags': [ '-Wall', '-Wextra', '-O3' ],
'msvs_settings': {
'VCCLCompilerTool': {
'RuntimeLibrary': 0, # static release
},
},
}
},
'msvs_settings': {
'VCCLCompilerTool': {
# Compile as C++. llhttp.c is actually C99, but C++ is
# close enough in this case.
'CompileAs': 2,
},
'VCLibrarianTool': {
},
'VCLinkerTool': {
'GenerateDebugInformation': 'true',
},
},
'conditions': [
['OS == "win"', {
'defines': [
'WIN32'
],
}]
],
},
}

361
third-party/llhttp/include/llhttp.h vendored Normal file
View File

@ -0,0 +1,361 @@
#ifndef INCLUDE_LLHTTP_H_
#define INCLUDE_LLHTTP_H_
#define LLHTTP_VERSION_MAJOR 1
#define LLHTTP_VERSION_MINOR 1
#define LLHTTP_VERSION_PATCH 1
#ifndef INCLUDE_LLHTTP_ITSELF_H_
#define INCLUDE_LLHTTP_ITSELF_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef struct llhttp__internal_s llhttp__internal_t;
struct llhttp__internal_s {
int32_t _index;
void* _span_pos0;
void* _span_cb0;
int32_t error;
const char* reason;
const char* error_pos;
void* data;
void* _current;
uint64_t content_length;
uint8_t type;
uint8_t method;
uint8_t http_major;
uint8_t http_minor;
uint8_t header_state;
uint8_t flags;
uint8_t upgrade;
uint16_t status_code;
uint8_t finish;
void* settings;
};
int llhttp__internal_init(llhttp__internal_t* s);
int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* INCLUDE_LLHTTP_ITSELF_H_ */
#ifndef LLLLHTTP_C_HEADERS_
#define LLLLHTTP_C_HEADERS_
#ifdef __cplusplus
extern "C" {
#endif
enum llhttp_errno {
HPE_OK = 0,
HPE_INTERNAL = 1,
HPE_STRICT = 2,
HPE_LF_EXPECTED = 3,
HPE_UNEXPECTED_CONTENT_LENGTH = 4,
HPE_CLOSED_CONNECTION = 5,
HPE_INVALID_METHOD = 6,
HPE_INVALID_URL = 7,
HPE_INVALID_CONSTANT = 8,
HPE_INVALID_VERSION = 9,
HPE_INVALID_HEADER_TOKEN = 10,
HPE_INVALID_CONTENT_LENGTH = 11,
HPE_INVALID_CHUNK_SIZE = 12,
HPE_INVALID_STATUS = 13,
HPE_INVALID_EOF_STATE = 14,
HPE_CB_MESSAGE_BEGIN = 15,
HPE_CB_HEADERS_COMPLETE = 16,
HPE_CB_MESSAGE_COMPLETE = 17,
HPE_CB_CHUNK_HEADER = 18,
HPE_CB_CHUNK_COMPLETE = 19,
HPE_PAUSED = 20,
HPE_PAUSED_UPGRADE = 21,
HPE_USER = 22
};
typedef enum llhttp_errno llhttp_errno_t;
enum llhttp_flags {
F_CONNECTION_KEEP_ALIVE = 0x1,
F_CONNECTION_CLOSE = 0x2,
F_CONNECTION_UPGRADE = 0x4,
F_CHUNKED = 0x8,
F_UPGRADE = 0x10,
F_CONTENT_LENGTH = 0x20,
F_SKIPBODY = 0x40,
F_TRAILING = 0x80
};
typedef enum llhttp_flags llhttp_flags_t;
enum llhttp_type {
HTTP_BOTH = 0,
HTTP_REQUEST = 1,
HTTP_RESPONSE = 2
};
typedef enum llhttp_type llhttp_type_t;
enum llhttp_finish {
HTTP_FINISH_SAFE = 0,
HTTP_FINISH_SAFE_WITH_CB = 1,
HTTP_FINISH_UNSAFE = 2
};
typedef enum llhttp_finish llhttp_finish_t;
enum llhttp_method {
HTTP_DELETE = 0,
HTTP_GET = 1,
HTTP_HEAD = 2,
HTTP_POST = 3,
HTTP_PUT = 4,
HTTP_CONNECT = 5,
HTTP_OPTIONS = 6,
HTTP_TRACE = 7,
HTTP_COPY = 8,
HTTP_LOCK = 9,
HTTP_MKCOL = 10,
HTTP_MOVE = 11,
HTTP_PROPFIND = 12,
HTTP_PROPPATCH = 13,
HTTP_SEARCH = 14,
HTTP_UNLOCK = 15,
HTTP_BIND = 16,
HTTP_REBIND = 17,
HTTP_UNBIND = 18,
HTTP_ACL = 19,
HTTP_REPORT = 20,
HTTP_MKACTIVITY = 21,
HTTP_CHECKOUT = 22,
HTTP_MERGE = 23,
HTTP_MSEARCH = 24,
HTTP_NOTIFY = 25,
HTTP_SUBSCRIBE = 26,
HTTP_UNSUBSCRIBE = 27,
HTTP_PATCH = 28,
HTTP_PURGE = 29,
HTTP_MKCALENDAR = 30,
HTTP_LINK = 31,
HTTP_UNLINK = 32,
HTTP_SOURCE = 33
};
typedef enum llhttp_method llhttp_method_t;
#define HTTP_ERRNO_MAP(XX) \
XX(0, OK, OK) \
XX(1, INTERNAL, INTERNAL) \
XX(2, STRICT, STRICT) \
XX(3, LF_EXPECTED, LF_EXPECTED) \
XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \
XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \
XX(6, INVALID_METHOD, INVALID_METHOD) \
XX(7, INVALID_URL, INVALID_URL) \
XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \
XX(9, INVALID_VERSION, INVALID_VERSION) \
XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \
XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \
XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \
XX(13, INVALID_STATUS, INVALID_STATUS) \
XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \
XX(15, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \
XX(16, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \
XX(17, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \
XX(18, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \
XX(19, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \
XX(20, PAUSED, PAUSED) \
XX(21, PAUSED_UPGRADE, PAUSED_UPGRADE) \
XX(22, USER, USER) \
#define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
XX(11, MOVE, MOVE) \
XX(12, PROPFIND, PROPFIND) \
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
XX(24, MSEARCH, M-SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
XX(30, MKCALENDAR, MKCALENDAR) \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
XX(33, SOURCE, SOURCE) \
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LLLLHTTP_C_HEADERS_ */
#ifndef INCLUDE_LLHTTP_API_H_
#define INCLUDE_LLHTTP_API_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
typedef llhttp__internal_t llhttp_t;
typedef struct llhttp_settings_s llhttp_settings_t;
typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length);
typedef int (*llhttp_cb)(llhttp_t*);
struct llhttp_settings_s {
/* Possible return values 0, -1, `HPE_PAUSED` */
llhttp_cb on_message_begin;
llhttp_data_cb on_url;
llhttp_data_cb on_status;
llhttp_data_cb on_header_field;
llhttp_data_cb on_header_value;
/* Possible return values:
* 0 - Proceed normally
* 1 - Assume that request/response has no body, and proceed to parsing the
* next message
* 2 - Assume absence of body (as above) and make `llhttp_execute()` return
* `HPE_PAUSED_UPGRADE`
* -1 - Error
* `HPE_PAUSED`
*/
llhttp_cb on_headers_complete;
llhttp_data_cb on_body;
/* Possible return values 0, -1, `HPE_PAUSED` */
llhttp_cb on_message_complete;
/* When on_chunk_header is called, the current chunk length is stored
* in parser->content_length.
* Possible return values 0, -1, `HPE_PAUSED`
*/
llhttp_cb on_chunk_header;
llhttp_cb on_chunk_complete;
};
/* Initialize the parser with specific type and user settings */
void llhttp_init(llhttp_t* parser, llhttp_type_t type,
const llhttp_settings_t* settings);
/* Initialize the settings object */
void llhttp_settings_init(llhttp_settings_t* settings);
/* Parse full or partial request/response, invoking user callbacks along the
* way.
*
* If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing
* interrupts, and such errno is returned from `llhttp_execute()`. If
* `HPE_PAUSED` was used as a errno, the execution can be resumed with
* `llhttp_resume()` call.
*
* In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE`
* is returned after fully parsing the request/response. If the user wishes to
* continue parsing, they need to invoke `llhttp_resume_after_upgrade()`.
*
* NOTE: if this function ever returns a non-pause type error, it will continue
* to return the same error upon each successive call up until `llhttp_init()`
* is called.
*/
llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len);
/* This method should be called when the other side has no further bytes to
* send (e.g. shutdown of readable side of the TCP connection.)
*
* Requests without `Content-Length` and other messages might require treating
* all incoming bytes as the part of the body, up to the last byte of the
* connection. This method will invoke `on_message_complete()` callback if the
* request was terminated safely. Otherwise a error code would be returned.
*/
llhttp_errno_t llhttp_finish(llhttp_t* parser);
/* Returns `1` if the incoming message is parsed until the last byte, and has
* to be completed by calling `llhttp_finish()` on EOF
*/
int llhttp_message_needs_eof(const llhttp_t* parser);
/* Returns `1` if there might be any other messages following the last that was
* successfuly parsed.
*/
int llhttp_should_keep_alive(const llhttp_t* parser);
/* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set
* appropriate error reason.
*
* Important: do not call this from user callbacks! User callbacks must return
* `HPE_PAUSED` if pausing is required.
*/
void llhttp_pause(llhttp_t* parser);
/* Might be called to resume the execution after the pause in user's callback.
* See `llhttp_execute()` above for details.
*
* Call this only if `llhttp_execute()` returns `HPE_PAUSED`.
*/
void llhttp_resume(llhttp_t* parser);
/* Might be called to resume the execution after the pause in user's callback.
* See `llhttp_execute()` above for details.
*
* Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE`
*/
void llhttp_resume_after_upgrade(llhttp_t* parser);
/* Returns the latest return error */
llhttp_errno_t llhttp_get_errno(const llhttp_t* parser);
/* Returns the verbal explanation of the latest returned error.
*
* Note: User callback should set error reason when returning the error. See
* `llhttp_set_error_reason()` for details.
*/
const char* llhttp_get_error_reason(const llhttp_t* parser);
/* Assign verbal description to the returned error. Must be called in user
* callbacks right before returning the errno.
*
* Note: `HPE_USER` error code might be useful in user callbacks.
*/
void llhttp_set_error_reason(llhttp_t* parser, const char* reason);
/* Returns the pointer to the last parsed byte before the returned error. The
* pointer is relative to the `data` argument of `llhttp_execute()`.
*
* Note: this method might be useful for counting the number of parsed bytes.
*/
const char* llhttp_get_error_pos(const llhttp_t* parser);
/* Returns textual name of error code */
const char* llhttp_errno_name(llhttp_errno_t err);
/* Returns textual name of HTTP method */
const char* llhttp_method_name(llhttp_method_t method);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* INCLUDE_LLHTTP_API_H_ */
#endif /* INCLUDE_LLHTTP_H_ */

13
third-party/llhttp/llhttp.gyp vendored Normal file
View File

@ -0,0 +1,13 @@
{
'targets': [
{
'target_name': 'llhttp',
'type': 'static_library',
'include_dirs': [ '.', 'include' ],
'direct_dependent_settings': {
'include_dirs': [ 'include' ],
},
'sources': [ 'src/llhttp.c', 'src/api.c', 'src/http.c' ],
},
]
}

0
third-party/llhttp/src/.dirstamp vendored Normal file
View File

215
third-party/llhttp/src/api.c vendored Normal file
View File

@ -0,0 +1,215 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "llhttp.h"
#define CALLBACK_MAYBE(PARSER, NAME, ...) \
do { \
llhttp_settings_t* settings; \
settings = (llhttp_settings_t*) (PARSER)->settings; \
if (settings == NULL || settings->NAME == NULL) { \
err = 0; \
break; \
} \
err = settings->NAME(__VA_ARGS__); \
} while (0)
void llhttp_init(llhttp_t* parser, llhttp_type_t type,
const llhttp_settings_t* settings) {
llhttp__internal_init(parser);
parser->type = type;
parser->settings = (void*) settings;
}
llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) {
return llhttp__internal_execute(parser, data, data + len);
}
void llhttp_settings_init(llhttp_settings_t* settings) {
memset(settings, 0, sizeof(*settings));
}
llhttp_errno_t llhttp_finish(llhttp_t* parser) {
int err;
/* We're in an error state. Don't bother doing anything. */
if (parser->error != 0) {
return 0;
}
switch (parser->finish) {
case HTTP_FINISH_SAFE_WITH_CB:
CALLBACK_MAYBE(parser, on_message_complete, parser);
if (err != HPE_OK) return err;
/* FALLTHROUGH */
case HTTP_FINISH_SAFE:
return HPE_OK;
case HTTP_FINISH_UNSAFE:
parser->reason = "Invalid EOF state";
return HPE_INVALID_EOF_STATE;
default:
abort();
}
}
void llhttp_pause(llhttp_t* parser) {
if (parser->error != HPE_OK) {
return;
}
parser->error = HPE_PAUSED;
parser->reason = "Paused";
}
void llhttp_resume(llhttp_t* parser) {
if (parser->error != HPE_PAUSED) {
return;
}
parser->error = 0;
}
void llhttp_resume_after_upgrade(llhttp_t* parser) {
if (parser->error != HPE_PAUSED_UPGRADE) {
return;
}
parser->error = 0;
}
llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) {
return parser->error;
}
const char* llhttp_get_error_reason(const llhttp_t* parser) {
return parser->reason;
}
void llhttp_set_error_reason(llhttp_t* parser, const char* reason) {
parser->reason = reason;
}
const char* llhttp_get_error_pos(const llhttp_t* parser) {
return parser->error_pos;
}
const char* llhttp_errno_name(llhttp_errno_t err) {
#define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME;
switch (err) {
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
default: abort();
}
#undef HTTP_ERRNO_GEN
}
const char* llhttp_method_name(llhttp_method_t method) {
#define HTTP_METHOD_GEN(NUM, NAME, STRING) case HTTP_##NAME: return #STRING;
switch (method) {
HTTP_METHOD_MAP(HTTP_METHOD_GEN)
default: abort();
}
#undef HTTP_METHOD_GEN
}
/* Callbacks */
int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_message_begin, s);
return err;
}
int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_url, s, p, endp - p);
return err;
}
int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_status, s, p, endp - p);
return err;
}
int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_header_field, s, p, endp - p);
return err;
}
int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_header_value, s, p, endp - p);
return err;
}
int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_headers_complete, s);
return err;
}
int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_message_complete, s);
return err;
}
int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_body, s, p, endp - p);
return err;
}
int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_chunk_header, s);
return err;
}
int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) {
int err;
CALLBACK_MAYBE(s, on_chunk_complete, s);
return err;
}
/* Private */
void llhttp__debug(llhttp_t* s, const char* p, const char* endp,
const char* msg) {
if (p == endp) {
fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type,
s->flags, msg);
} else {
fprintf(stderr, "p=%p type=%d flags=%02x next=%02x debug=%s\n", s,
s->type, s->flags, *p, msg);
}
}

120
third-party/llhttp/src/http.c vendored Normal file
View File

@ -0,0 +1,120 @@
#include <stdio.h>
#ifndef LLHTTP__TEST
# include "llhttp.h"
#else
# define llhttp_t llparse_t
#endif /* */
int llhttp_message_needs_eof(const llhttp_t* parser);
int llhttp_should_keep_alive(const llhttp_t* parser);
int llhttp__before_headers_complete(llhttp_t* parser, const char* p,
const char* endp) {
/* Set this here so that on_headers_complete() callbacks can see it */
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);
}
return 0;
}
/* Return values:
* 0 - No body, `restart`, message_complete
* 1 - CONNECT request, `restart`, message_complete, and pause
* 2 - chunk_size_start
* 3 - body_identity
* 4 - body_identity_eof
*/
int llhttp__after_headers_complete(llhttp_t* parser, const char* p,
const char* endp) {
int hasBody;
hasBody = parser->flags & F_CHUNKED || parser->content_length > 0;
if (parser->upgrade && (parser->method == HTTP_CONNECT ||
(parser->flags & F_SKIPBODY) || !hasBody)) {
/* Exit, the rest of the message is in a different protocol. */
return 1;
}
if (parser->flags & F_SKIPBODY) {
return 0;
} else if (parser->flags & F_CHUNKED) {
/* chunked encoding - ignore Content-Length header */
return 2;
} else {
if (!(parser->flags & F_CONTENT_LENGTH)) {
if (!llhttp_message_needs_eof(parser)) {
/* Assume content-length 0 - read the next */
return 0;
} else {
/* Read body until EOF */
return 4;
}
} else if (parser->content_length == 0) {
/* Content-Length header given but zero: Content-Length: 0\r\n */
return 0;
} else {
/* Content-Length header given and non-zero */
return 3;
}
}
}
int llhttp__after_message_complete(llhttp_t* parser, const char* p,
const char* endp) {
int should_keep_alive;
should_keep_alive = llhttp_should_keep_alive(parser);
parser->flags = 0;
parser->finish = HTTP_FINISH_SAFE;
/* NOTE: this is ignored in loose parsing mode */
return should_keep_alive;
}
int llhttp_message_needs_eof(const llhttp_t* parser) {
if (parser->type == HTTP_REQUEST) {
return 0;
}
/* See RFC 2616 section 4.4 */
if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
parser->status_code == 204 || /* No Content */
parser->status_code == 304 || /* Not Modified */
(parser->flags & F_SKIPBODY)) { /* response to a HEAD request */
return 0;
}
if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) {
return 0;
}
return 1;
}
int llhttp_should_keep_alive(const llhttp_t* parser) {
if (parser->http_major > 0 && parser->http_minor > 0) {
/* HTTP/1.1 */
if (parser->flags & F_CONNECTION_CLOSE) {
return 0;
}
} else {
/* HTTP/1.0 or earlier */
if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
return 0;
}
}
return !llhttp_message_needs_eof(parser);
}

6045
third-party/llhttp/src/llhttp.c vendored Normal file

File diff suppressed because it is too large Load Diff

1
third-party/url-parser/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/.dirstamp

652
third-party/url-parser/url_parser.c vendored Normal file
View File

@ -0,0 +1,652 @@
/* 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
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "url_parser.h"
#include <assert.h>
#include <stddef.h>
#include <ctype.h>
#include <string.h>
#include <limits.h>
#ifndef BIT_AT
# define BIT_AT(a, i) \
(!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \
(1 << ((unsigned int) (i) & 7))))
#endif
#if HTTP_PARSER_STRICT
# define T(v) 0
#else
# define T(v) v
#endif
static const uint8_t normal_url_char[32] = {
/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0,
/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
/* 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 | 2 | 4 | 0 | 16 | 32 | 64 | 128,
/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, };
#undef T
enum state
{ s_dead = 1 /* important that this is > 0 */
, s_start_req_or_res
, s_res_or_resp_H
, s_start_res
, s_res_H
, s_res_HT
, s_res_HTT
, s_res_HTTP
, s_res_http_major
, 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
, s_res_status
, s_res_line_almost_done
, s_start_req
, s_req_method
, s_req_spaces_before_url
, s_req_schema
, s_req_schema_slash
, s_req_schema_slash_slash
, s_req_server_start
, s_req_server
, s_req_server_with_at
, s_req_path
, s_req_query_string_start
, s_req_query_string
, s_req_fragment_start
, s_req_fragment
, s_req_http_start
, s_req_http_H
, 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
, s_req_http_end
, s_req_line_almost_done
, s_header_field_start
, s_header_field
, s_header_value_discard_ws
, s_header_value_discard_ws_almost_done
, s_header_value_discard_lws
, s_header_value_start
, s_header_value
, s_header_value_lws
, s_header_almost_done
, s_chunk_size_start
, s_chunk_size
, s_chunk_parameters
, s_chunk_size_almost_done
, s_headers_almost_done
, s_headers_done
/* Important: 's_headers_done' must be the last 'header' state. All
* states beyond this must be 'body' states. It is used for overflow
* checking. See the PARSING_HEADER() macro.
*/
, s_chunk_data
, s_chunk_data_almost_done
, s_chunk_data_done
, s_body_identity
, s_body_identity_eof
, s_message_done
};
enum http_host_state
{
s_http_host_dead = 1
, s_http_userinfo_start
, s_http_userinfo
, s_http_host_start
, s_http_host_v6_start
, s_http_host
, s_http_host_v6
, s_http_host_v6_end
, s_http_host_v6_zone_start
, s_http_host_v6_zone
, s_http_host_port_start
, s_http_host_port
};
/* Macros for character classes; depends on strict-mode */
#define CR '\r'
#define LF '\n'
#define LOWER(c) (unsigned char)(c | 0x20)
#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z')
#define IS_NUM(c) ((c) >= '0' && (c) <= '9')
#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \
(c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
(c) == ')')
#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
(c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
(c) == '$' || (c) == ',')
#define STRICT_TOKEN(c) ((c == ' ') ? 0 : tokens[(unsigned char)c])
#if HTTP_PARSER_STRICT
#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) tokens[(unsigned char)c]
#define IS_URL_CHAR(c) \
(BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
#define IS_HOST_CHAR(c) \
(IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
#endif
/* Our URL parser.
*
* This is designed to be shared by http_parser_execute() for URL validation,
* hence it has a state transition + byte-for-byte interface. In addition, it
* is meant to be embedded in http_parser_parse_url(), which does the dirty
* work of turning state transitions URL components for its API.
*
* This function should only be invoked with non-space characters. It is
* assumed that the caller cares about (and can detect) the transition between
* URL and non-URL states by looking for these.
*/
static enum state
parse_url_char(enum state s, const char ch)
{
if (ch == ' ' || ch == '\r' || ch == '\n') {
return s_dead;
}
#if HTTP_PARSER_STRICT
if (ch == '\t' || ch == '\f') {
return s_dead;
}
#endif
switch (s) {
case s_req_spaces_before_url:
/* Proxied requests are followed by scheme of an absolute URI (alpha).
* All methods except CONNECT are followed by '/' or '*'.
*/
if (ch == '/' || ch == '*') {
return s_req_path;
}
if (IS_ALPHA(ch)) {
return s_req_schema;
}
break;
case s_req_schema:
if (IS_ALPHA(ch)) {
return s;
}
if (ch == ':') {
return s_req_schema_slash;
}
break;
case s_req_schema_slash:
if (ch == '/') {
return s_req_schema_slash_slash;
}
break;
case s_req_schema_slash_slash:
if (ch == '/') {
return s_req_server_start;
}
break;
case s_req_server_with_at:
if (ch == '@') {
return s_dead;
}
/* fall through */
case s_req_server_start:
case s_req_server:
if (ch == '/') {
return s_req_path;
}
if (ch == '?') {
return s_req_query_string_start;
}
if (ch == '@') {
return s_req_server_with_at;
}
if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
return s_req_server;
}
break;
case s_req_path:
if (IS_URL_CHAR(ch)) {
return s;
}
switch (ch) {
case '?':
return s_req_query_string_start;
case '#':
return s_req_fragment_start;
}
break;
case s_req_query_string_start:
case s_req_query_string:
if (IS_URL_CHAR(ch)) {
return s_req_query_string;
}
switch (ch) {
case '?':
/* allow extra '?' in query string */
return s_req_query_string;
case '#':
return s_req_fragment_start;
}
break;
case s_req_fragment_start:
if (IS_URL_CHAR(ch)) {
return s_req_fragment;
}
switch (ch) {
case '?':
return s_req_fragment;
case '#':
return s;
}
break;
case s_req_fragment:
if (IS_URL_CHAR(ch)) {
return s;
}
switch (ch) {
case '?':
case '#':
return s;
}
break;
default:
break;
}
/* We should never fall out of the switch above unless there's an error */
return s_dead;
}
static enum http_host_state
http_parse_host_char(enum http_host_state s, const char ch) {
switch(s) {
case s_http_userinfo:
case s_http_userinfo_start:
if (ch == '@') {
return s_http_host_start;
}
if (IS_USERINFO_CHAR(ch)) {
return s_http_userinfo;
}
break;
case s_http_host_start:
if (ch == '[') {
return s_http_host_v6_start;
}
if (IS_HOST_CHAR(ch)) {
return s_http_host;
}
break;
case s_http_host:
if (IS_HOST_CHAR(ch)) {
return s_http_host;
}
/* fall through */
case s_http_host_v6_end:
if (ch == ':') {
return s_http_host_port_start;
}
break;
case s_http_host_v6:
if (ch == ']') {
return s_http_host_v6_end;
}
/* fall through */
case s_http_host_v6_start:
if (IS_HEX(ch) || ch == ':' || ch == '.') {
return s_http_host_v6;
}
if (s == s_http_host_v6 && ch == '%') {
return s_http_host_v6_zone_start;
}
break;
case s_http_host_v6_zone:
if (ch == ']') {
return s_http_host_v6_end;
}
/* 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 == '_' ||
ch == '~') {
return s_http_host_v6_zone;
}
break;
case s_http_host_port:
case s_http_host_port_start:
if (IS_NUM(ch)) {
return s_http_host_port;
}
break;
default:
break;
}
return s_http_host_dead;
}
static int
http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
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;
for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
enum http_host_state new_s = http_parse_host_char(s, *p);
if (new_s == s_http_host_dead) {
return 1;
}
switch(new_s) {
case s_http_host:
if (s != s_http_host) {
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 = (uint16_t)(p - buf);
}
u->field_data[UF_HOST].len++;
break;
case s_http_host_v6_zone_start:
case s_http_host_v6_zone:
u->field_data[UF_HOST].len++;
break;
case s_http_host_port:
if (s != s_http_host_port) {
u->field_data[UF_PORT].off = (uint16_t)(p - buf);
u->field_data[UF_PORT].len = 0;
u->field_set |= (1 << UF_PORT);
}
u->field_data[UF_PORT].len++;
break;
case s_http_userinfo:
if (s != s_http_userinfo) {
u->field_data[UF_USERINFO].off = (uint16_t)(p - buf);
u->field_data[UF_USERINFO].len = 0;
u->field_set |= (1 << UF_USERINFO);
}
u->field_data[UF_USERINFO].len++;
break;
default:
break;
}
s = new_s;
}
/* Make sure we don't end somewhere unexpected */
switch (s) {
case s_http_host_start:
case s_http_host_v6_start:
case s_http_host_v6:
case s_http_host_v6_zone_start:
case s_http_host_v6_zone:
case s_http_host_port_start:
case s_http_userinfo:
case s_http_userinfo_start:
return 1;
default:
break;
}
return 0;
}
void
http_parser_url_init(struct http_parser_url *u) {
memset(u, 0, sizeof(*u));
}
int
http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
struct http_parser_url *u)
{
enum state s;
const char *p;
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;
for (p = buf; p < buf + buflen; p++) {
s = parse_url_char(s, *p);
/* Figure out the next field that we're operating on */
switch (s) {
case s_dead:
return 1;
/* Skip delimeters */
case s_req_schema_slash:
case s_req_schema_slash_slash:
case s_req_server_start:
case s_req_query_string_start:
case s_req_fragment_start:
continue;
case s_req_schema:
uf = UF_SCHEMA;
break;
case s_req_server_with_at:
found_at = 1;
/* fall through */
case s_req_server:
uf = UF_HOST;
break;
case s_req_path:
uf = UF_PATH;
break;
case s_req_query_string:
uf = UF_QUERY;
break;
case s_req_fragment:
uf = UF_FRAGMENT;
break;
default:
assert(!"Unexpected state");
return 1;
}
/* Nothing's changed; soldier on */
if (uf == old_uf) {
u->field_data[uf].len++;
continue;
}
u->field_data[uf].off = (uint16_t)(p - buf);
u->field_data[uf].len = 1;
u->field_set |= (1 << uf);
old_uf = uf;
}
/* host must be present if there is a schema */
/* parsing http:///toto will fail */
if ((u->field_set & (1 << UF_SCHEMA)) &&
(u->field_set & (1 << UF_HOST)) == 0) {
return 1;
}
if (u->field_set & (1 << UF_HOST)) {
if (http_parse_host(buf, u, found_at) != 0) {
return 1;
}
}
/* CONNECT requests can only contain "hostname:port" */
if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
return 1;
}
if (u->field_set & (1 << UF_PORT)) {
uint16_t off;
uint16_t len;
const char* p;
const char* end;
unsigned long v;
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;
}
return 0;
}

94
third-party/url-parser/url_parser.h vendored Normal file
View File

@ -0,0 +1,94 @@
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef url_parser_h
#define url_parser_h
#ifdef __cplusplus
extern "C" {
#endif
/* Also update SONAME in the Makefile whenever you change these. */
#define HTTP_PARSER_VERSION_MAJOR 2
#define HTTP_PARSER_VERSION_MINOR 9
#define HTTP_PARSER_VERSION_PATCH 1
#include <stddef.h>
#if defined(_WIN32) && !defined(__MINGW32__) && \
(!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
#include <BaseTsd.h>
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
* faster
*/
#ifndef HTTP_PARSER_STRICT
# define HTTP_PARSER_STRICT 1
#endif
enum http_parser_url_fields
{ UF_SCHEMA = 0
, UF_HOST = 1
, UF_PORT = 2
, UF_PATH = 3
, UF_QUERY = 4
, UF_FRAGMENT = 5
, UF_USERINFO = 6
, UF_MAX = 7
};
/* Result structure for http_parser_parse_url().
*
* Callers should index into field_data[] with UF_* values iff field_set
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
* because we probably have padding left over), we convert any port to
* a uint16_t.
*/
struct http_parser_url {
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
uint16_t port; /* Converted UF_PORT string */
struct {
uint16_t off; /* Offset into buffer in which field starts */
uint16_t len; /* Length of run in buffer */
} field_data[UF_MAX];
};
/* Initialize all http_parser_url members to 0 */
void http_parser_url_init(struct http_parser_url *u);
/* Parse a URL; return nonzero on failure */
int http_parser_parse_url(const char *buf, size_t buflen,
int is_connect,
struct http_parser_url *u);
#ifdef __cplusplus
}
#endif
#endif