Replace http-parser with llhttp (#1913)

This commit is contained in:
Eddy Ashton 2020-11-24 13:02:06 +00:00 коммит произвёл GitHub
Родитель 6528a33907
Коммит 18e3e03483
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
29 изменённых файлов: 15987 добавлений и 3300 удалений

2568
3rdparty/http-parser/http_parser.c поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

443
3rdparty/http-parser/http_parser.h поставляемый
Просмотреть файл

@ -1,443 +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 4
#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
, F_TRANSFER_ENCODING = 1 << 8 /* Never set in http_parser.flags */
};
/* 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") \
XX(INVALID_TRANSFER_ENCODING, \
"request has invalid transfer-encoding") \
/* 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 : 5; /* index into current matcher */
unsigned int extra_flags : 2;
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

224
3rdparty/llhttp/api.c поставляемый Normal file
Просмотреть файл

@ -0,0 +1,224 @@
#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
}
void llhttp_set_lenient(llhttp_t* parser, int enabled) {
if (enabled) {
parser->flags |= F_LENIENT;
} else {
parser->flags &= ~F_LENIENT;
}
}
/* 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);
}
}

164
3rdparty/llhttp/api.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,164 @@
#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.
*
* NOTE: lifetime of `settings` has to be at least the same as the lifetime of
* the `parser` here. In practice, `settings` has to be either a static
* variable or be allocated with `malloc`, `new`, etc.
*/
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
* successfully 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);
/* Enables/disables lenient header value parsing (disabled by default).
*
* Lenient parsing disables header value token checks, extending llhttp's
* protocol support to highly non-compliant clients/server. No
* `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when
* lenient parsing is "on".
*
* **(USE AT YOUR OWN RISK)**
*/
void llhttp_set_lenient(llhttp_t* parser, int enabled);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* INCLUDE_LLHTTP_API_H_ */

150
3rdparty/llhttp/http.c поставляемый Normal file
Просмотреть файл

@ -0,0 +1,150 @@
#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
* 5 - invalid transfer-encoding for request
*/
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, prepare for a chunk */
return 2;
} else if (parser->flags & F_TRANSFER_ENCODING) {
if (parser->type == HTTP_REQUEST && (parser->flags & F_LENIENT) == 0) {
/* RFC 7230 3.3.3 */
/* If a Transfer-Encoding header field
* is present in a request and the chunked transfer coding is not
* the final encoding, the message body length cannot be determined
* reliably; the server MUST respond with the 400 (Bad Request)
* status code and then close the connection.
*/
return 5;
} else {
/* RFC 7230 3.3.3 */
/* If a Transfer-Encoding header field is present in a response and
* the chunked transfer coding is not the final encoding, the
* message body length is determined by reading the connection until
* it is closed by the server.
*/
return 4;
}
} 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->finish = HTTP_FINISH_SAFE;
/* Keep `F_LENIENT` flag between messages, but reset every other flag */
parser->flags &= F_LENIENT;
/* 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;
}
/* RFC 7230 3.3.3, see `llhttp__after_headers_complete` */
if ((parser->flags & F_TRANSFER_ENCODING) &&
(parser->flags & F_CHUNKED) == 0) {
return 1;
}
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);
}

14645
3rdparty/llhttp/llhttp.c поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

410
3rdparty/llhttp/llhttp.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,410 @@
#ifndef INCLUDE_LLHTTP_H_
#define INCLUDE_LLHTTP_H_
#define LLHTTP_VERSION_MAJOR 2
#define LLHTTP_VERSION_MINOR 2
#define LLHTTP_VERSION_PATCH 0
#ifndef LLHTTP_STRICT_MODE
# define LLHTTP_STRICT_MODE 0
#endif
#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;
uint16_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_INVALID_TRANSFER_ENCODING = 15,
HPE_CB_MESSAGE_BEGIN = 16,
HPE_CB_HEADERS_COMPLETE = 17,
HPE_CB_MESSAGE_COMPLETE = 18,
HPE_CB_CHUNK_HEADER = 19,
HPE_CB_CHUNK_COMPLETE = 20,
HPE_PAUSED = 21,
HPE_PAUSED_UPGRADE = 22,
HPE_USER = 23
};
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,
F_LENIENT = 0x100,
F_TRANSFER_ENCODING = 0x200
};
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,
HTTP_PRI = 34,
HTTP_DESCRIBE = 35,
HTTP_ANNOUNCE = 36,
HTTP_SETUP = 37,
HTTP_PLAY = 38,
HTTP_PAUSE = 39,
HTTP_TEARDOWN = 40,
HTTP_GET_PARAMETER = 41,
HTTP_SET_PARAMETER = 42,
HTTP_REDIRECT = 43,
HTTP_RECORD = 44,
HTTP_FLUSH = 45
};
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, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \
XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \
XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \
XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \
XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \
XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \
XX(21, PAUSED, PAUSED) \
XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \
XX(23, 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) \
XX(34, PRI, PRI) \
XX(35, DESCRIBE, DESCRIBE) \
XX(36, ANNOUNCE, ANNOUNCE) \
XX(37, SETUP, SETUP) \
XX(38, PLAY, PLAY) \
XX(39, PAUSE, PAUSE) \
XX(40, TEARDOWN, TEARDOWN) \
XX(41, GET_PARAMETER, GET_PARAMETER) \
XX(42, SET_PARAMETER, SET_PARAMETER) \
XX(43, REDIRECT, REDIRECT) \
XX(44, RECORD, RECORD) \
XX(45, FLUSH, FLUSH) \
#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.
*
* NOTE: lifetime of `settings` has to be at least the same as the lifetime of
* the `parser` here. In practice, `settings` has to be either a static
* variable or be allocated with `malloc`, `new`, etc.
*/
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
* successfully 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);
/* Enables/disables lenient header value parsing (disabled by default).
*
* Lenient parsing disables header value token checks, extending llhttp's
* protocol support to highly non-compliant clients/server. No
* `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when
* lenient parsing is "on".
*
* **(USE AT YOUR OWN RISK)**
*/
void llhttp_set_lenient(llhttp_t* parser, int enabled);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* INCLUDE_LLHTTP_API_H_ */
#endif /* INCLUDE_LLHTTP_H_ */

Просмотреть файл

@ -72,15 +72,6 @@
}
}
},
{
"component": {
"type": "git",
"git": {
"repositoryUrl": "https://github.com/nodejs/http-parser",
"commitHash": "2343fd6b5214b2ded2cdcf76de2bf60903bb90cd"
}
}
},
{
"component": {
"type": "git",
@ -144,6 +135,15 @@
"commitHash": "08cba57ed7207c8ad5c94fd2a20dc0bfecabe878"
}
}
},
{
"component": {
"type": "git",
"git": {
"repositoryUrl": "https://github.com/nodejs/llhttp",
"commitHash": "8b3939e29a01ffbf9e481a76db81e372f0fb2f0c"
}
}
}
],
"Version": 1

Просмотреть файл

@ -172,7 +172,10 @@ set(LUA_SOURCES
${LUA_DIR}/lzio.c
)
set(HTTP_PARSER_SOURCES ${CCF_DIR}/3rdparty/http-parser/http_parser.c)
set(HTTP_PARSER_SOURCES
${CCF_DIR}/3rdparty/llhttp/api.c ${CCF_DIR}/3rdparty/llhttp/http.c
${CCF_DIR}/3rdparty/llhttp/llhttp.c
)
find_library(CRYPTO_LIBRARY crypto)

Просмотреть файл

@ -1235,7 +1235,7 @@ namespace ccfapp
EndpointRegistry::execute_endpoint(e, args);
}
static std::pair<http_method, std::string> split_script_key(
static std::pair<llhttp_method, std::string> split_script_key(
const std::string& key)
{
size_t s = key.find(' ');

Просмотреть файл

@ -60,7 +60,7 @@ protected:
const std::string& method,
const CBuffer params,
const std::string& content_type,
http_method verb)
llhttp_method verb)
{
auto path = method;
if (prefix.has_value())
@ -96,7 +96,7 @@ protected:
const std::string& method,
const CBuffer params,
const std::string& content_type,
http_method verb)
llhttp_method verb)
{
if (is_ws)
return gen_ws_request_internal(method, params);
@ -149,7 +149,7 @@ public:
const std::string& method,
const CBuffer params,
const std::string& content_type,
http_method verb = HTTP_POST)
llhttp_method verb = HTTP_POST)
{
return {gen_request_internal(method, params, content_type, verb),
next_send_id++};
@ -158,7 +158,7 @@ public:
PreparedRpc gen_request(
const std::string& method,
const nlohmann::json& params = nullptr,
http_method verb = HTTP_POST)
llhttp_method verb = HTTP_POST)
{
std::vector<uint8_t> body;
if (!params.is_null())
@ -175,7 +175,7 @@ public:
Response call(
const std::string& method,
const nlohmann::json& params = nullptr,
http_method verb = HTTP_POST)
llhttp_method verb = HTTP_POST)
{
return call_raw(gen_request(method, params, verb));
}
@ -183,7 +183,7 @@ public:
Response call(
const std::string& method,
const CBuffer& params,
http_method verb = HTTP_POST)
llhttp_method verb = HTTP_POST)
{
return call_raw(gen_request(
method, params, http::headervalues::contenttype::OCTET_STREAM, verb));

Просмотреть файл

@ -4,8 +4,9 @@
#include "ds/json.h"
#include "ds/nonstd.h"
#include "http/http_status.h"
#include <http-parser/http_parser.h>
#include <llhttp/llhttp.h>
#include <nlohmann/json.hpp>
#include <string>
@ -99,10 +100,10 @@ namespace ds
}
static inline nlohmann::json& path_operation(
nlohmann::json& path, http_method verb)
nlohmann::json& path, llhttp_method verb)
{
// HTTP_GET becomes the string "get"
std::string s = http_method_str(verb);
std::string s = llhttp_method_name(verb);
nonstd::to_lower(s);
auto& po = access::get_object(path, s);
// responses is required field in a path_operation
@ -300,7 +301,7 @@ namespace ds
static inline void add_request_body_schema(
nlohmann::json& document,
const std::string& uri,
http_method verb,
llhttp_method verb,
const std::string& content_type,
const std::string& schema_name,
const nlohmann::json& schema_)
@ -316,7 +317,7 @@ namespace ds
static inline void add_request_body_schema(
nlohmann::json& document,
const std::string& uri,
http_method verb,
llhttp_method verb,
const std::string& content_type)
{
auto& rb = request_body(path_operation(path(document, uri), verb));
@ -342,7 +343,7 @@ namespace ds
static inline void add_request_parameter_schema(
nlohmann::json& document,
const std::string& uri,
http_method verb,
llhttp_method verb,
const nlohmann::json& param)
{
auto& params = parameters(path_operation(path(document, uri), verb));
@ -352,7 +353,7 @@ namespace ds
static inline void add_response_schema(
nlohmann::json& document,
const std::string& uri,
http_method verb,
llhttp_method verb,
http_status status,
const std::string& content_type,
const std::string& schema_name,
@ -368,7 +369,7 @@ namespace ds
static inline void add_response_schema(
nlohmann::json& document,
const std::string& uri,
http_method verb,
llhttp_method verb,
http_status status,
const std::string& content_type)
{

Просмотреть файл

@ -8,7 +8,7 @@
#include "node/client_signatures.h"
#include "node/entities.h"
#include <http-parser/http_parser.h>
#include <llhttp/llhttp.h>
#include <variant>
#include <vector>
@ -16,9 +16,9 @@ namespace ccf
{
static_assert(
static_cast<int>(ws::Verb::WEBSOCKET) <
static_cast<int>(http_method::HTTP_DELETE));
static_cast<int>(llhttp_method::HTTP_DELETE));
/*!
Extension of http_method including a special "WEBSOCKET" method,
Extension of llhttp_method including a special "WEBSOCKET" method,
to allow make_*_endpoint() to be a single uniform interface to define
handlers for either use cases.
@ -33,17 +33,17 @@ namespace ccf
public:
RESTVerb() : verb(std::numeric_limits<int>::min()) {}
RESTVerb(const http_method& hm) : verb(hm) {}
RESTVerb(const llhttp_method& hm) : verb(hm) {}
RESTVerb(const ws::Verb& wv) : verb(wv) {}
std::optional<http_method> get_http_method() const
std::optional<llhttp_method> get_http_method() const
{
if (verb == ws::WEBSOCKET)
{
return std::nullopt;
}
return static_cast<http_method>(verb);
return static_cast<llhttp_method>(verb);
}
const char* c_str() const
@ -54,7 +54,7 @@ namespace ccf
}
else
{
return http_method_str(static_cast<http_method>(verb));
return llhttp_method_name(static_cast<llhttp_method>(verb));
}
}

Просмотреть файл

@ -3,12 +3,13 @@
#pragma once
#include "http_consts.h"
#include "http_status.h"
#include "tls/base64.h"
#include "tls/hash.h"
#define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <http-parser/http_parser.h>
#include <llhttp/llhttp.h>
#include <map>
#include <string>
#include <vector>
@ -31,12 +32,12 @@ namespace http
// Most builder function are unused from enclave
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
static http_method http_method_from_str(const char* s)
static llhttp_method http_method_from_str(const char* s)
{
#define XX(num, name, string) \
if (strcmp(s, #string) == 0) \
{ \
return http_method(num); \
return llhttp_method(num); \
}
HTTP_METHOD_MAP(XX)
#undef XX
@ -114,19 +115,19 @@ namespace http
class Request : public Message
{
private:
http_method method;
llhttp_method method;
std::string path = "/";
std::map<std::string, std::string> query_params = {};
public:
Request(const std::string_view& p = "/", http_method m = HTTP_POST) :
Request(const std::string_view& p = "/", llhttp_method m = HTTP_POST) :
Message(),
method(m)
{
set_path(p);
}
http_method get_method() const
llhttp_method get_method() const
{
return method;
}
@ -187,7 +188,7 @@ namespace http
"{}"
"\r\n"
"{}",
http_method_str(method),
llhttp_method_name(method),
uri,
get_header_string(headers),
body_view);
@ -231,7 +232,7 @@ namespace http
// Generic
static std::vector<uint8_t> build_header(
http_method method, const std::vector<uint8_t>& body)
llhttp_method method, const std::vector<uint8_t>& body)
{
Request r("/", method);
r.set_body(&body);
@ -239,7 +240,7 @@ namespace http
}
static std::vector<uint8_t> build_request(
http_method method, const std::vector<uint8_t>& body)
llhttp_method method, const std::vector<uint8_t>& body)
{
Request r("/", method);
r.set_body(&body);

Просмотреть файл

@ -99,34 +99,10 @@ namespace http
try
{
const auto used = p.execute(data, n_read);
if (used == 0)
{
// Parsing error
LOG_FAIL_FMT("Failed to parse request");
return;
}
else if (used > n_read)
{
// Something has gone very wrong
LOG_FAIL_FMT(
"Unexpected return result - tried to parse {} bytes, actually "
"parsed {}",
n_read,
used);
return;
}
else if (used == n_read)
{
// Used all provided bytes - check if more are available
n_read = read(buf.data(), buf.size(), false);
}
else
{
// Used some bytes - pass over these and retry with remainder
data += used;
n_read -= used;
}
p.execute(data, n_read);
// Used all provided bytes - check if more are available
n_read = read(buf.data(), buf.size(), false);
}
catch (const std::exception& e)
{
@ -176,15 +152,16 @@ namespace http
}
void handle_request(
http_method verb,
llhttp_method verb,
const std::string_view& path,
const std::string& query,
const std::string&,
http::HeaderMap&& headers,
std::vector<uint8_t>&& body) override
{
LOG_TRACE_FMT(
"Processing msg({}, {}, {}, [{} bytes])",
http_method_str(verb),
llhttp_method_name(verb),
path,
query,
body.size());

Просмотреть файл

@ -8,10 +8,12 @@
#include <algorithm>
#include <cctype>
#include <http-parser/http_parser.h>
#include <llhttp/llhttp.h>
#include <map>
#include <queue>
#include <regex>
#include <string>
#include <string_view>
namespace http
{
@ -72,9 +74,10 @@ namespace http
public:
struct Request
{
http_method method;
llhttp_method method;
std::string path;
std::string query;
std::string fragment;
http::HeaderMap headers;
std::vector<uint8_t> body;
};
@ -82,14 +85,19 @@ namespace http
std::queue<Request> received;
virtual void handle_request(
http_method method,
llhttp_method method,
const std::string_view& path,
const std::string& query,
const std::string& fragment,
http::HeaderMap&& headers,
std::vector<uint8_t>&& body) override
{
received.emplace(
Request{method, std::string(path), std::string(query), headers, body});
received.emplace(Request{method,
std::string(path),
std::string(query),
std::string(fragment),
headers,
body});
}
};
@ -120,87 +128,83 @@ namespace http
IN_MESSAGE
};
static int on_msg_begin(http_parser* parser);
static int on_url(http_parser* parser, const char* at, size_t length);
static int on_header_field(
http_parser* parser, const char* at, size_t length);
static int on_header_value(
http_parser* parser, const char* at, size_t length);
static int on_headers_complete(http_parser* parser);
static int on_body(http_parser* parser, const char* at, size_t length);
static int on_msg_end(http_parser* parser);
static int on_msg_begin(llhttp_t* parser);
static int on_url(llhttp_t* parser, const char* at, size_t length);
static int on_header_field(llhttp_t* parser, const char* at, size_t length);
static int on_header_value(llhttp_t* parser, const char* at, size_t length);
static int on_headers_complete(llhttp_t* parser);
static int on_body(llhttp_t* parser, const char* at, size_t length);
static int on_msg_end(llhttp_t* parser);
inline std::string_view extract_url_field(
const http_parser_url& parser_url,
http_parser_url_fields field,
const std::string& url)
{
if ((1 << field) & parser_url.field_set)
{
const auto& data = parser_url.field_data[field];
const auto start = url.data();
return std::string_view(start + data.off, data.len);
}
return {};
}
inline auto parse_url(const std::string& url)
inline auto split_url_path(const std::string_view& url)
{
LOG_TRACE_FMT("Received url to parse: {}", std::string_view(url));
http_parser_url parser_url;
http_parser_url_init(&parser_url);
const auto path_end = url.find('?');
const auto query_start =
path_end == std::string::npos ? url.size() : path_end + 1;
const auto err =
http_parser_parse_url(url.data(), url.size(), 0, &parser_url);
if (err != 0)
{
throw std::invalid_argument(fmt::format("Error parsing url: {}", err));
}
const auto query_end = url.find('#', query_start);
const auto fragment_start =
query_end == std::string::npos ? url.size() : query_end + 1;
return std::make_pair(
extract_url_field(parser_url, UF_PATH, url),
extract_url_field(parser_url, UF_QUERY, url));
return std::make_tuple(
url.substr(0, path_end),
url.substr(query_start, query_end - query_start),
url.substr(fragment_start));
}
struct URL
{
std::string_view schema;
std::string_view host;
std::string_view port;
std::string_view path;
std::string_view query;
std::string_view fragment;
std::string scheme;
std::string host;
std::string port;
std::string path;
std::string query;
std::string fragment;
};
inline URL parse_url_full(const std::string& url)
{
LOG_TRACE_FMT("Received url to parse: {}", url);
http_parser_url parser_url;
http_parser_url_init(&parser_url);
// From https://tools.ietf.org/html/rfc3986#appendix-B
std::regex url_regex(
"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?");
const auto err =
http_parser_parse_url(url.data(), url.size(), 0, &parser_url);
if (err != 0)
std::smatch match;
if (!std::regex_match(url, match, url_regex))
{
throw std::invalid_argument(fmt::format("Error parsing url: {}", err));
throw std::invalid_argument(fmt::format("Unable to parse url: {}", url));
}
return {extract_url_field(parser_url, UF_SCHEMA, url),
extract_url_field(parser_url, UF_HOST, url),
extract_url_field(parser_url, UF_PORT, url),
extract_url_field(parser_url, UF_PATH, url),
extract_url_field(parser_url, UF_QUERY, url),
extract_url_field(parser_url, UF_FRAGMENT, url)};
const auto host_port = match[4].str();
// IPv6 hosts may contain colons, so only search for port after the closing
// square bracket
const auto closing_bracket = host_port.rfind(']');
const auto port_delim_start =
closing_bracket == std::string::npos ? 0 : closing_bracket;
const auto port_delim = host_port.find(':', port_delim_start);
URL u;
u.scheme = match[2].str();
u.host = host_port.substr(0, port_delim);
if (port_delim != std::string::npos)
{
u.port = host_port.substr(port_delim + 1);
}
u.path = match[5].str();
u.query = match[7].str();
u.fragment = match[9].str();
return u;
}
class Parser
{
protected:
http_parser parser;
http_parser_settings settings;
llhttp_t parser;
llhttp_settings_t settings;
State state = DONE;
std::vector<uint8_t> body_buf;
@ -215,9 +219,9 @@ namespace http
partial_parsed_header.second.clear();
}
Parser(http_parser_type type)
Parser(llhttp_type_t type)
{
http_parser_settings_init(&settings);
llhttp_settings_init(&settings);
settings.on_message_begin = on_msg_begin;
settings.on_header_field = on_header_field;
@ -226,34 +230,29 @@ namespace http
settings.on_body = on_body;
settings.on_message_complete = on_msg_end;
http_parser_init(&parser, type);
llhttp_init(&parser, type, &settings);
parser.data = this;
}
public:
http_parser* get_raw_parser()
void execute(const uint8_t* data, size_t size)
{
return &parser;
}
auto err_no = llhttp_execute(&parser, (const char*)data, size);
size_t execute(const uint8_t* data, size_t size)
{
auto parsed =
http_parser_execute(&parser, &settings, (const char*)data, size);
LOG_TRACE_FMT("Parsed {} bytes", parsed);
auto err = HTTP_PARSER_ERRNO(&parser);
if (err)
if (err_no == HPE_PAUSED_UPGRADE)
{
// Assume Upgrade requests will be handled by caller inspecting headers,
// so we can instantly resume the parser.
llhttp_resume_after_upgrade(&parser);
}
else if (err_no != HPE_OK)
{
throw std::runtime_error(fmt::format(
"HTTP parsing failed: '{}: {}' while parsing fragment '{}'",
http_errno_name(err),
http_errno_description(err),
llhttp_errno_name(err_no),
llhttp_get_error_reason(&parser),
std::string((char const*)data, size)));
}
return parsed;
}
void append_body(const char* at, size_t length)
@ -326,46 +325,46 @@ namespace http
}
};
static int on_msg_begin(http_parser* parser)
static int on_msg_begin(llhttp_t* parser)
{
Parser* p = reinterpret_cast<Parser*>(parser->data);
p->new_message();
return 0;
return HPE_OK;
}
static int on_header_field(http_parser* parser, const char* at, size_t length)
static int on_header_field(llhttp_t* parser, const char* at, size_t length)
{
Parser* p = reinterpret_cast<Parser*>(parser->data);
p->header_field(at, length);
return 0;
return HPE_OK;
}
static int on_header_value(http_parser* parser, const char* at, size_t length)
static int on_header_value(llhttp_t* parser, const char* at, size_t length)
{
Parser* p = reinterpret_cast<Parser*>(parser->data);
p->header_value(at, length);
return 0;
return HPE_OK;
}
static int on_headers_complete(http_parser* parser)
static int on_headers_complete(llhttp_t* parser)
{
Parser* p = reinterpret_cast<Parser*>(parser->data);
p->headers_complete();
return 0;
return HPE_OK;
}
static int on_body(http_parser* parser, const char* at, size_t length)
static int on_body(llhttp_t* parser, const char* at, size_t length)
{
Parser* p = reinterpret_cast<Parser*>(parser->data);
p->append_body(at, length);
return 0;
return HPE_OK;
}
static int on_msg_end(http_parser* parser)
static int on_msg_end(llhttp_t* parser)
{
Parser* p = reinterpret_cast<Parser*>(parser->data);
p->end_message();
return 0;
return HPE_OK;
}
// Request-specific
@ -398,7 +397,8 @@ namespace http
if (url.empty())
{
proc.handle_request(
http_method(parser.method),
llhttp_method(parser.method),
{},
{},
{},
std::move(headers),
@ -406,23 +406,25 @@ namespace http
}
else
{
const auto [path, query] = parse_url(url);
std::string decoded_query = url_decode(query);
const auto [path, query, fragment] = split_url_path(url);
const std::string decoded_query = url_decode(query);
const std::string decoded_fragment = url_decode(fragment);
proc.handle_request(
http_method(parser.method),
llhttp_method(parser.method),
path,
decoded_query,
decoded_fragment,
std::move(headers),
std::move(body_buf));
}
}
};
static int on_url(http_parser* parser, const char* at, size_t length)
static int on_url(llhttp_t* parser, const char* at, size_t length)
{
RequestParser* p = reinterpret_cast<RequestParser*>(parser->data);
p->append_url(at, length);
return 0;
return HPE_OK;
}
// Response-specific

Просмотреть файл

@ -8,7 +8,7 @@
#include <algorithm>
#include <cctype>
#include <endian.h>
#include <http-parser/http_parser.h>
#include <llhttp/llhttp.h>
#include <map>
#include <queue>
#include <string>
@ -19,9 +19,10 @@ namespace http
{
public:
virtual void handle_request(
http_method method,
llhttp_method method,
const std::string_view& path,
const std::string& query,
const std::string& fragment,
HeaderMap&& headers,
std::vector<uint8_t>&& body) = 0;
};

Просмотреть файл

@ -150,7 +150,7 @@ namespace http
HttpRpcContext(
size_t request_index_,
std::shared_ptr<enclave::SessionContext> s,
http_method verb_,
llhttp_method verb_,
const std::string_view& path_,
const std::string_view& query_,
const http::HeaderMap& headers_,
@ -350,18 +350,7 @@ namespace enclave
http::SimpleRequestProcessor processor;
http::RequestParser parser(processor);
const auto parsed_count = parser.execute(packed.data(), packed.size());
if (parsed_count != packed.size())
{
const auto err_no = (http_errno)parser.get_raw_parser()->http_errno;
throw std::logic_error(fmt::format(
"Failed to fully parse HTTP request. Parsed only {} bytes. Error code "
"{} ({}: {})",
parsed_count,
err_no,
http_errno_name(err_no),
http_errno_description(err_no)));
}
parser.execute(packed.data(), packed.size());
if (processor.received.size() != 1)
{
@ -391,51 +380,15 @@ namespace enclave
enclave::FrameFormat frame_format,
const std::vector<uint8_t>& raw_bft = {})
{
http::SimpleRequestProcessor processor;
switch (frame_format)
{
case enclave::FrameFormat::http:
{
http::RequestParser parser(processor);
const auto parsed_count = parser.execute(packed.data(), packed.size());
if (parsed_count != packed.size())
{
const auto err_no = (http_errno)parser.get_raw_parser()->http_errno;
throw std::logic_error(fmt::format(
"Failed to fully parse HTTP request. Parsed only {} bytes. Error "
"code "
"{} ({}: {})",
parsed_count,
err_no,
http_errno_name(err_no),
http_errno_description(err_no)));
}
if (processor.received.size() != 1)
{
throw std::logic_error(fmt::format(
"Expected packed to contain a single complete HTTP message. "
"Actually "
"parsed {} messages",
processor.received.size()));
}
const auto& msg = processor.received.front();
return std::make_shared<http::HttpRpcContext>(
0,
s,
msg.method,
msg.path,
msg.query,
msg.headers,
msg.body,
packed,
raw_bft);
return make_rpc_context(s, packed, raw_bft);
}
case enclave::FrameFormat::ws:
{
http::SimpleRequestProcessor processor;
ws::RequestParser parser(processor);
auto next_read = ws::INITIAL_READ;
@ -464,6 +417,5 @@ namespace enclave
default:
throw std::logic_error("Unknown Frame Format");
}
http::RequestParser parser(processor);
}
}

Просмотреть файл

@ -99,7 +99,7 @@ namespace http
add_digest_header(request);
const auto to_sign = construct_raw_signed_string(
http_method_str(request.get_method()),
llhttp_method_name(request.get_method()),
request.get_path(),
request.get_formatted_query(),
request.get_headers(),

87
src/http/http_status.h Normal file
Просмотреть файл

@ -0,0 +1,87 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
/* 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
};
/* Returns a string version of the HTTP status code. */
static inline const char* http_status_str(enum http_status s)
{
switch (s)
{
#define XX(num, name, string) \
case HTTP_STATUS_##name: \
return #string;
HTTP_STATUS_MAP(XX)
#undef XX
default:
return "<unknown>";
}
}

Просмотреть файл

@ -41,7 +41,7 @@ DOCTEST_TEST_CASE("Complete request")
auto request = http::Request(url, method);
request.set_body(&r);
auto req = request.build_request();
auto parsed = p.execute(req.data(), req.size());
p.execute(req.data(), req.size());
DOCTEST_CHECK(!sp.received.empty());
const auto& m = sp.received.front();
@ -65,7 +65,7 @@ DOCTEST_TEST_CASE("Complete response")
auto response = http::Response(status);
response.set_body(&r);
auto res = response.build_response();
auto parsed = p.execute(res.data(), res.size());
p.execute(res.data(), res.size());
DOCTEST_CHECK(!sp.received.empty());
const auto& m = sp.received.front();
@ -107,10 +107,8 @@ DOCTEST_TEST_CASE("Partial request")
auto req = http::build_post_request(r0);
size_t offset = 10;
auto parsed = p.execute(req.data(), req.size() - offset);
DOCTEST_CHECK(parsed == req.size() - offset);
parsed = p.execute(req.data() + req.size() - offset, offset);
DOCTEST_CHECK(parsed == offset);
p.execute(req.data(), req.size() - offset);
p.execute(req.data() + req.size() - offset, offset);
DOCTEST_CHECK(!sp.received.empty());
const auto& m = sp.received.front();
@ -125,12 +123,10 @@ DOCTEST_TEST_CASE("Partial body")
const auto r0 = s_to_v(request_0);
auto req = http::build_post_request(r0);
size_t offset = http::build_post_header(r0).size() + 4;
size_t offset = http::build_post_header(r0).size() + r0.size() / 3;
auto parsed = p.execute(req.data(), req.size() - offset);
DOCTEST_CHECK(parsed == req.size() - offset);
parsed = p.execute(req.data() + req.size() - offset, offset);
DOCTEST_CHECK(parsed == offset);
p.execute(req.data(), req.size() - offset);
p.execute(req.data() + req.size() - offset, offset);
DOCTEST_CHECK(!sp.received.empty());
const auto& m = sp.received.front();
@ -149,8 +145,35 @@ DOCTEST_TEST_CASE("Multiple requests")
auto req1 = http::build_post_request(r1);
std::copy(req1.begin(), req1.end(), std::back_inserter(req));
auto parsed = p.execute(req.data(), req.size());
DOCTEST_CHECK(parsed == req.size());
DOCTEST_SUBCASE("All at once")
{
p.execute(req.data(), req.size());
}
DOCTEST_SUBCASE("In chunks")
{
constexpr auto chunks = 7;
const auto chunk_size = req.size() / chunks;
auto remaining = req.size();
auto next_data = req.data();
while (remaining > 0)
{
const auto next = std::min(remaining, chunk_size);
p.execute(next_data, next);
next_data += next;
remaining -= next;
}
}
DOCTEST_SUBCASE("Byte-by-byte")
{
constexpr size_t next = 1;
for (size_t i = 0; i < req.size(); ++i)
{
p.execute(req.data() + i, next);
}
}
{
DOCTEST_CHECK(!sp.received.empty());
@ -179,7 +202,7 @@ DOCTEST_TEST_CASE("Method parsing")
{
const auto r = s_to_v(choice ? request_0 : request_1);
auto req = http::build_request(method, r);
auto parsed = p.execute(req.data(), req.size());
p.execute(req.data(), req.size());
DOCTEST_CHECK(!sp.received.empty());
const auto& m = sp.received.front();
@ -206,8 +229,7 @@ DOCTEST_TEST_CASE("URL parsing")
r.set_body(&body);
auto req = r.build_request();
auto parsed = p.execute(req.data(), req.size());
DOCTEST_CHECK(parsed == req.size());
p.execute(req.data(), req.size());
DOCTEST_CHECK(!sp.received.empty());
const auto& m = sp.received.front();
@ -253,8 +275,7 @@ DOCTEST_TEST_CASE("Pessimal transport")
// Simulate dreadful transport - send 1 byte at a time
size_t next = 1;
next = std::min(next, req.size() - done);
auto parsed = p.execute(req.data() + done, next);
DOCTEST_CHECK(parsed == next);
p.execute(req.data() + done, next);
done += next;
}
@ -292,20 +313,24 @@ DOCTEST_TEST_CASE("Escaping")
{
const std::string request =
"GET /foo/bar?this=that&awkward=escaped+string+%3A%3B-%3D%3F%21%22 "
"GET "
"/foo/"
"bar?this=that&awkward=escaped+string+%3A%3B-%3D%3F%21%22%25%23#"
"AndThisFragment+%3A%3B-%3D%3F%21%22%25%23 "
"HTTP/1.1\r\n\r\n";
http::SimpleRequestProcessor sp;
http::RequestParser p(sp);
const std::vector<uint8_t> req(request.begin(), request.end());
auto parsed = p.execute(req.data(), req.size());
p.execute(req.data(), req.size());
DOCTEST_CHECK(!sp.received.empty());
const auto& m = sp.received.front();
DOCTEST_CHECK(m.method == HTTP_GET);
DOCTEST_CHECK(m.path == "/foo/bar");
DOCTEST_CHECK(m.query == "this=that&awkward=escaped string :;-=?!\"");
DOCTEST_CHECK(m.query == "this=that&awkward=escaped string :;-=?!\"%#");
DOCTEST_CHECK(m.fragment == "AndThisFragment :;-=?!\"%#");
}
{
@ -318,7 +343,7 @@ DOCTEST_TEST_CASE("Escaping")
http::RequestParser p(sp);
const std::vector<uint8_t> req(request.begin(), request.end());
auto parsed = p.execute(req.data(), req.size());
p.execute(req.data(), req.size());
DOCTEST_CHECK(!sp.received.empty());
const auto& m = sp.received.front();
@ -329,25 +354,97 @@ DOCTEST_TEST_CASE("Escaping")
}
}
DOCTEST_TEST_CASE("URL parser")
{
// Test cases taken from https://tools.ietf.org/html/rfc3986
{
constexpr auto url_s = "http://www.ietf.org/rfc/rfc2396.txt";
const auto url = http::parse_url_full(url_s);
DOCTEST_CHECK(url.scheme == "http");
DOCTEST_CHECK(url.host == "www.ietf.org");
DOCTEST_CHECK(url.port.empty());
DOCTEST_CHECK(url.path == "/rfc/rfc2396.txt");
DOCTEST_CHECK(url.query.empty());
DOCTEST_CHECK(url.fragment.empty());
}
{
constexpr auto url_s = "ftp://ftp.is.co.za/rfc/rfc1808.txt";
const auto url = http::parse_url_full(url_s);
DOCTEST_CHECK(url.scheme == "ftp");
DOCTEST_CHECK(url.host == "ftp.is.co.za");
DOCTEST_CHECK(url.port.empty());
DOCTEST_CHECK(url.path == "/rfc/rfc1808.txt");
DOCTEST_CHECK(url.query.empty());
DOCTEST_CHECK(url.fragment.empty());
}
{
constexpr auto url_s = "foo://example.com";
const auto url = http::parse_url_full(url_s);
DOCTEST_CHECK(url.scheme == "foo");
DOCTEST_CHECK(url.host == "example.com");
DOCTEST_CHECK(url.port.empty());
DOCTEST_CHECK(url.path.empty());
DOCTEST_CHECK(url.query.empty());
DOCTEST_CHECK(url.fragment.empty());
}
{
constexpr auto url_s = "foo://example.com:8042/over/there?name=ferret#nose";
const auto url = http::parse_url_full(url_s);
DOCTEST_CHECK(url.scheme == "foo");
DOCTEST_CHECK(url.host == "example.com");
DOCTEST_CHECK(url.port == "8042");
DOCTEST_CHECK(url.path == "/over/there");
DOCTEST_CHECK(url.query == "name=ferret");
DOCTEST_CHECK(url.fragment == "nose");
}
{
constexpr auto url_s =
"https://[2001:0db8:0000:0000:0000::1428:57ab]:8042/over/there#nose";
const auto url = http::parse_url_full(url_s);
DOCTEST_CHECK(url.scheme == "https");
DOCTEST_CHECK(url.host == "[2001:0db8:0000:0000:0000::1428:57ab]");
DOCTEST_CHECK(url.port == "8042");
DOCTEST_CHECK(url.path == "/over/there");
DOCTEST_CHECK(url.query.empty());
DOCTEST_CHECK(url.fragment == "nose");
}
{
constexpr auto url_s = "http://[::ffff:0c22:384e]/";
const auto url = http::parse_url_full(url_s);
DOCTEST_CHECK(url.scheme == "http");
DOCTEST_CHECK(url.host == "[::ffff:0c22:384e]");
DOCTEST_CHECK(url.port.empty());
DOCTEST_CHECK(url.path == "/");
DOCTEST_CHECK(url.query.empty());
DOCTEST_CHECK(url.fragment.empty());
}
}
struct SignedRequestProcessor : public http::SimpleRequestProcessor
{
std::queue<ccf::SignedReq> signed_reqs;
virtual void handle_request(
http_method method,
llhttp_method method,
const std::string_view& path,
const std::string& query,
const std::string& fragment,
http::HeaderMap&& headers,
std::vector<uint8_t>&& body) override
{
const auto signed_req = http::HttpSignatureVerifier::parse(
http_method_str(method), path, query, headers, body);
llhttp_method_name(method), path, query, headers, body);
DOCTEST_REQUIRE(signed_req.has_value());
signed_reqs.push(signed_req.value());
http::SimpleRequestProcessor::handle_request(
method, path, query, std::move(headers), std::move(body));
method, path, query, fragment, std::move(headers), std::move(body));
}
};
@ -394,8 +491,7 @@ DOCTEST_TEST_CASE("Signatures")
SignedRequestProcessor sp;
http::RequestParser p(sp);
auto parsed = p.execute(serial_request.data(), serial_request.size());
DOCTEST_REQUIRE(parsed == serial_request.size());
p.execute(serial_request.data(), serial_request.size());
DOCTEST_REQUIRE(!sp.signed_reqs.empty());
}
@ -419,8 +515,7 @@ DOCTEST_TEST_CASE("Signatures")
SignedRequestProcessor sp;
http::RequestParser p(sp);
auto parsed = p.execute(serial_request.data(), serial_request.size());
DOCTEST_REQUIRE(parsed == serial_request.size());
p.execute(serial_request.data(), serial_request.size());
DOCTEST_REQUIRE(sp.signed_reqs.size() == 1);
sp.signed_reqs.pop();
@ -496,9 +591,8 @@ DOCTEST_TEST_CASE("Signatures")
SignedRequestProcessor sp;
http::RequestParser p(sp);
auto parsed = p.execute(serial_request.data(), serial_request.size());
DOCTEST_REQUIRE(parsed == serial_request.size());
p.execute(serial_request.data(), serial_request.size());
DOCTEST_REQUIRE(sp.signed_reqs.size() == 1);
}
}
}
}

Просмотреть файл

@ -221,9 +221,10 @@ namespace ws
std::vector<uint8_t> body(data, data + s);
proc.handle_request(
http_method::HTTP_POST,
llhttp_method::HTTP_POST,
path,
{},
{},
{{"Content-type", "application/json"}},
std::move(body));
state = INIT;

Просмотреть файл

@ -1351,16 +1351,7 @@ namespace ccf
http::SimpleResponseProcessor processor;
http::ResponseParser parser(processor);
const auto parsed_count =
parser.execute(response.data(), response.size());
if (parsed_count != response.size())
{
LOG_FAIL_FMT(
"Tried to parse {} response bytes, actually parsed {}",
response.size(),
parsed_count);
return false;
}
parser.execute(response.data(), response.size());
if (processor.received.size() != 1)
{

Просмотреть файл

@ -14,7 +14,7 @@
#include "serialization.h"
#include <functional>
#include <http-parser/http_parser.h>
#include <llhttp/llhttp.h>
#include <nlohmann/json.hpp>
#include <regex>
#include <set>
@ -448,7 +448,7 @@ namespace ccf
nlohmann::json& document,
const std::string& uri,
const nlohmann::json& schema,
http_method verb)
llhttp_method verb)
{
if (schema["type"] != "object")
{

Просмотреть файл

@ -7,7 +7,7 @@
#include "http/http_consts.h"
#include "node/rpc/serdes.h"
#include <http-parser/http_parser.h>
#include <llhttp/llhttp.h>
namespace ccf
{

Просмотреть файл

@ -768,7 +768,7 @@ namespace ccf
proposal_id);
return false;
}
if (issuer_url.schema != "https")
if (issuer_url.scheme != "https")
{
LOG_FAIL_FMT(
"Proposal {}: issuer must be a URL starting with https:// if "

Просмотреть файл

@ -387,8 +387,7 @@ http::SimpleResponseProcessor::Response parse_response(const vector<uint8_t>& v)
http::SimpleResponseProcessor processor;
http::ResponseParser parser(processor);
const auto parsed_count = parser.execute(v.data(), v.size());
REQUIRE(parsed_count == v.size());
parser.execute(v.data(), v.size());
REQUIRE(processor.received.size() == 1);
return processor.received.front();
@ -903,9 +902,9 @@ TEST_CASE("Restricted verbs")
TestRestrictedVerbsFrontend frontend(*network.tables);
for (auto verb = HTTP_DELETE; verb <= HTTP_SOURCE;
verb = (http_method)(size_t(verb) + 1))
verb = (llhttp_method)(size_t(verb) + 1))
{
INFO(http_method_str(verb));
INFO(llhttp_method_name(verb));
{
http::Request get("get_only", verb);
@ -923,7 +922,7 @@ TEST_CASE("Restricted verbs")
const auto it = response.headers.find(http::headers::ALLOW);
REQUIRE(it != response.headers.end());
const auto v = it->second;
CHECK(v.find(http_method_str(HTTP_GET)) != std::string::npos);
CHECK(v.find(llhttp_method_name(HTTP_GET)) != std::string::npos);
}
}
@ -943,7 +942,7 @@ TEST_CASE("Restricted verbs")
const auto it = response.headers.find(http::headers::ALLOW);
REQUIRE(it != response.headers.end());
const auto v = it->second;
CHECK(v.find(http_method_str(HTTP_POST)) != std::string::npos);
CHECK(v.find(llhttp_method_name(HTTP_POST)) != std::string::npos);
}
}
@ -964,9 +963,9 @@ TEST_CASE("Restricted verbs")
const auto it = response.headers.find(http::headers::ALLOW);
REQUIRE(it != response.headers.end());
const auto v = it->second;
CHECK(v.find(http_method_str(HTTP_PUT)) != std::string::npos);
CHECK(v.find(http_method_str(HTTP_DELETE)) != std::string::npos);
CHECK(v.find(http_method_str(verb)) == std::string::npos);
CHECK(v.find(llhttp_method_name(HTTP_PUT)) != std::string::npos);
CHECK(v.find(llhttp_method_name(HTTP_DELETE)) != std::string::npos);
CHECK(v.find(llhttp_method_name(verb)) == std::string::npos);
}
}
}

Просмотреть файл

@ -102,7 +102,7 @@ void set_whitelists(GenesisGenerator& gen)
std::vector<uint8_t> create_text_request(
const std::string& text,
const string& method_name,
http_method verb = HTTP_POST)
llhttp_method verb = HTTP_POST)
{
http::Request r(method_name, verb);
const auto body = std::vector<uint8_t>(text.begin(), text.end());
@ -111,7 +111,7 @@ std::vector<uint8_t> create_text_request(
}
std::vector<uint8_t> create_request(
const json& params, const string& method_name, http_method verb = HTTP_POST)
const json& params, const string& method_name, llhttp_method verb = HTTP_POST)
{
http::Request r(method_name, verb);
const auto body = params.is_null() ? std::vector<uint8_t>() :
@ -124,7 +124,7 @@ std::vector<uint8_t> create_signed_request(
const json& params,
const string& method_name,
const tls::KeyPairPtr& kp_,
http_method verb = HTTP_POST)
llhttp_method verb = HTTP_POST)
{
http::Request r(method_name, verb);
@ -172,9 +172,7 @@ auto frontend_process(
http::SimpleResponseProcessor processor;
http::ResponseParser parser(processor);
const auto parsed_count =
parser.execute(serialized_response->data(), serialized_response->size());
DOCTEST_REQUIRE(parsed_count == serialized_response->size());
parser.execute(serialized_response->data(), serialized_response->size());
DOCTEST_REQUIRE(processor.received.size() == 1);
return processor.received.front();

Просмотреть файл

@ -60,9 +60,7 @@ TResponse frontend_process(
http::SimpleResponseProcessor processor;
http::ResponseParser parser(processor);
const auto parsed_count =
parser.execute(serialised_response->data(), serialised_response->size());
REQUIRE(parsed_count == serialised_response->size());
parser.execute(serialised_response->data(), serialised_response->size());
REQUIRE(processor.received.size() == 1);
return processor.received.front();