зеркало из https://github.com/microsoft/CCF.git
Replace http-parser with llhttp (#1913)
This commit is contained in:
Родитель
6528a33907
Коммит
18e3e03483
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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_ */
|
|
@ -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);
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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,35 +99,11 @@ 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)
|
||||
{
|
||||
p.execute(data, 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;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_FAIL_FMT("Error parsing request");
|
||||
|
@ -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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
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(),
|
||||
|
|
|
@ -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,8 +591,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
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());
|
||||
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());
|
||||
REQUIRE(processor.received.size() == 1);
|
||||
|
||||
return processor.received.front();
|
||||
|
|
Загрузка…
Ссылка в новой задаче