Upgrade HTTP parser from http-parser to llhttp. v7.0.77 (#4469)

This PR modernizes SRS's HTTP handling by upgrading from the legacy
http-parser library to the more performant and actively maintained
llhttp library.

* Replace http-parser with llhttp: Migrated from the deprecated
http-parser to llhttp for better performance and maintenance
* API compatibility: Updated all HTTP parsing logic to use llhttp APIs
while maintaining backward compatibility
* Simplified URL parsing: Replaced complex http-parser URL parsing with
custom simple parser implementation
Enhanced error handling: Improved error reporting with llhttp's better
error context and positioning


---------

Co-authored-by: OSSRS-AI <winlinam@gmail.com>
This commit is contained in:
Winlin 2025-09-03 20:12:59 -04:00 committed by GitHub
parent 421ab6c3fb
commit 6720e96745
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 12611 additions and 3201 deletions

View File

@ -1648,6 +1648,10 @@ stream_caster {
# For gb28181 converter, listen at TCP port. for example, 9000.
# @remark We always enable bundle for media streams at this port.
listen 9000;
# SIP server for GB28181. The embedded SIP server is disabled by default, please use external SIP server
# such as [jsip](https://github.com/usnistgov/jsip) and there is a demo [srs-sip](https://github.com/ossrs/srs-sip)
# also base on it, for more information please see project [GB: External SIP](https://ossrs.io/lts/zh-cn/docs/v7/doc/gb28181#external-sip).
}
#

4
trunk/configure vendored
View File

@ -263,7 +263,9 @@ MODULE_FILES=("srs_protocol_amf0" "srs_protocol_io" "srs_protocol_conn" "srs_pro
"srs_protocol_rtmp_stack" "srs_protocol_utility" "srs_protocol_rtmp_msg_array" "srs_protocol_stream"
"srs_protocol_raw_avc" "srs_protocol_http_stack" "srs_protocol_kbps" "srs_protocol_json"
"srs_protocol_format" "srs_protocol_log" "srs_protocol_st" "srs_protocol_http_client"
"srs_protocol_http_conn" "srs_protocol_rtmp_conn" "srs_protocol_protobuf")
"srs_protocol_http_conn" "srs_protocol_rtmp_conn" "srs_protocol_protobuf"
"srs_protocol_http_stack_llhttp" "srs_protocol_http_stack_llhttpapi"
"srs_protocol_http_stack_llhttpadapter" "srs_protocol_http_stack_llhttphttp")
# Always include SRT protocol
MODULE_FILES+=("srs_protocol_srt")
ModuleLibIncs+=(${LibSRTRoot})

View File

@ -7,6 +7,7 @@ The changelog for SRS.
<a name="v7-changes"></a>
## SRS 7.0 Changelog
* v7.0, 2025-09-03, Merge [#4469](https://github.com/ossrs/srs/pull/4469): Upgrade HTTP parser from http-parser to llhttp. v7.0.77 (#4469)
* v7.0, 2025-09-03, Merge [#4470](https://github.com/ossrs/srs/pull/4470): RTX: Fix race condition for timer. v7.0.76 (#4470)
* v7.0, 2025-09-02, Merge [#4466](https://github.com/ossrs/srs/pull/4466): AI: GB28181: Remove embedded SIP server and enforce external SIP usage. v7.0.75 (#4466)
* v7.0, 2025-09-01, Merge [#4465](https://github.com/ossrs/srs/pull/4465): AI: Replace SrsSharedPtrMessage with SrsMediaPacket for unified media packet handling. v7.0.74 (#4465)

View File

@ -9,6 +9,6 @@
#define VERSION_MAJOR 7
#define VERSION_MINOR 0
#define VERSION_REVISION 76
#define VERSION_REVISION 77
#endif

View File

@ -35,18 +35,15 @@ SrsHttpParser::~SrsHttpParser()
srs_freep(header);
}
srs_error_t SrsHttpParser::initialize(enum http_parser_type type)
srs_error_t SrsHttpParser::initialize(enum llhttp_type type)
{
srs_error_t err = srs_success;
jsonp = false;
type_ = type;
// Initialize the parser, however it's not necessary.
http_parser_init(&parser, type_);
parser.data = (void *)this;
memset(&settings, 0, sizeof(settings));
// Initialize the settings first
llhttp_settings_init(&settings);
settings.on_message_begin = on_message_begin;
settings.on_url = on_url;
settings.on_header_field = on_header_field;
@ -55,6 +52,10 @@ srs_error_t SrsHttpParser::initialize(enum http_parser_type type)
settings.on_body = on_body;
settings.on_message_complete = on_message_complete;
// Initialize the parser with settings
llhttp_init(&parser, type_, &settings);
parser.data = (void *)this;
return err;
}
@ -71,7 +72,7 @@ srs_error_t SrsHttpParser::parse_message(ISrsReader *reader, ISrsHttpMessage **p
// Reset parser data and state.
state = SrsHttpParseStateInit;
memset(&hp_header, 0, sizeof(http_parser));
memset(&hp_header, 0, sizeof(llhttp_t));
// We must reset the field name and value, because we may get a partial value in on_header_value.
field_name = field_value = "";
// Reset the url.
@ -89,7 +90,8 @@ srs_error_t SrsHttpParser::parse_message(ISrsReader *reader, ISrsHttpMessage **p
// when got next message, the whole next message is parsed as the body of previous one,
// and the message fail.
// @note You can comment the bellow line, the utest will fail.
http_parser_init(&parser, type_);
llhttp_reset(&parser);
// Reset the parsed type.
parsed_type_ = HTTP_BOTH;
// callback object ptr.
@ -106,8 +108,8 @@ srs_error_t SrsHttpParser::parse_message(ISrsReader *reader, ISrsHttpMessage **p
SrsHttpMessage *msg = new SrsHttpMessage(reader, buffer);
// Initialize the basic information.
msg->set_basic(hp_header.type, (http_method)hp_header.method, (http_status)hp_header.status_code, hp_header.content_length);
msg->set_header(header, http_should_keep_alive(&hp_header));
msg->set_basic(hp_header.type, (llhttp_method_t)hp_header.method, (llhttp_status_t)hp_header.status_code, hp_header.content_length);
msg->set_header(header, llhttp_should_keep_alive(&hp_header));
// For HTTP response, no url.
if (parsed_type_ != HTTP_RESPONSE && (err = msg->set_url(url, jsonp)) != srs_success) {
srs_freep(msg);
@ -126,19 +128,40 @@ srs_error_t SrsHttpParser::parse_message_imp(ISrsReader *reader)
while (true) {
if (buffer->size() > 0) {
ssize_t consumed = http_parser_execute(&parser, &settings, buffer->bytes(), buffer->size());
const char *data_start = buffer->bytes();
llhttp_errno_t code = llhttp_execute(&parser, data_start, buffer->size());
// The error is set in http_errno.
enum http_errno code = HTTP_PARSER_ERRNO(&parser);
if (code != HPE_OK) {
ssize_t consumed = 0;
if (code == HPE_OK) {
// No problem, all buffer should be consumed.
consumed = buffer->size();
} else if (code == HPE_PAUSED) {
// We only consume the header, not message or body.
const char *error_pos = llhttp_get_error_pos(&parser);
if (error_pos && error_pos < data_start) {
return srs_error_new(ERROR_HTTP_PARSE_HEADER, "llhttp error_pos=%p < data_start=%p", error_pos, data_start);
}
if (error_pos && error_pos >= data_start) {
consumed = error_pos - data_start;
}
}
// Check for errors (but allow certain conditions that are normal)
// HPE_OK: success
// HPE_PAUSED: we use to skip body
if (code != HPE_OK && code != HPE_PAUSED) {
return srs_error_new(ERROR_HTTP_PARSE_HEADER, "parse %dB, nparsed=%d, err=%d/%s %s",
buffer->size(), (int)consumed, code, http_errno_name(code), http_errno_description(code));
buffer->size(), (int)consumed, code, llhttp_errno_name(code),
llhttp_get_error_reason(&parser) ? llhttp_get_error_reason(&parser) : "");
}
srs_info("size=%d, nparsed=%d", buffer->size(), (int)consumed);
// Only consume the header bytes.
buffer->read_slice(consumed);
if (consumed > 0) {
buffer->read_slice(consumed);
}
// Done when header completed, never wait for body completed, because it maybe chunked.
if (state >= SrsHttpParseStateHeaderComplete) {
@ -161,7 +184,7 @@ srs_error_t SrsHttpParser::parse_message_imp(ISrsReader *reader)
return err;
}
int SrsHttpParser::on_message_begin(http_parser *parser)
int SrsHttpParser::on_message_begin(llhttp_t *parser)
{
SrsHttpParser *obj = (SrsHttpParser *)parser->data;
srs_assert(obj);
@ -170,14 +193,14 @@ int SrsHttpParser::on_message_begin(http_parser *parser)
obj->state = SrsHttpParseStateStart;
// If we set to HTTP_BOTH, the type is detected and speicifed by parser.
obj->parsed_type_ = (http_parser_type)parser->type;
obj->parsed_type_ = (llhttp_type)parser->type;
srs_info("***MESSAGE BEGIN***");
return 0;
}
int SrsHttpParser::on_headers_complete(http_parser *parser)
int SrsHttpParser::on_headers_complete(llhttp_t *parser)
{
SrsHttpParser *obj = (SrsHttpParser *)parser->data;
srs_assert(obj);
@ -189,20 +212,21 @@ int SrsHttpParser::on_headers_complete(http_parser *parser)
srs_info("***HEADERS COMPLETE***");
// The return code of this callback:
// 0: Continue to process body.
// 1: Skip body, but continue to parse util all data parsed.
// 2: Upgrade and skip body and left message, because it is in a different protocol.
// N: Error and failed as HPE_CB_headers_complete.
// We choose 2 because we only want to parse the header, not the body.
return 2;
// `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`: Pause parsing and wait for user to call `llhttp_resume()`.
// We use HPE_PAUSED to skip body.
return HPE_PAUSED;
}
int SrsHttpParser::on_message_complete(http_parser *parser)
int SrsHttpParser::on_message_complete(llhttp_t *parser)
{
SrsHttpParser *obj = (SrsHttpParser *)parser->data;
srs_assert(obj);
// save the parser when body parse completed.
// Note that we should never get here, because we always return HPE_PAUSED in on_headers_complete.
obj->state = SrsHttpParseStateMessageComplete;
srs_info("***MESSAGE COMPLETE***\n");
@ -210,7 +234,7 @@ int SrsHttpParser::on_message_complete(http_parser *parser)
return 0;
}
int SrsHttpParser::on_url(http_parser *parser, const char *at, size_t length)
int SrsHttpParser::on_url(llhttp_t *parser, const char *at, size_t length)
{
SrsHttpParser *obj = (SrsHttpParser *)parser->data;
srs_assert(obj);
@ -225,7 +249,7 @@ int SrsHttpParser::on_url(http_parser *parser, const char *at, size_t length)
return 0;
}
int SrsHttpParser::on_header_field(http_parser *parser, const char *at, size_t length)
int SrsHttpParser::on_header_field(llhttp_t *parser, const char *at, size_t length)
{
SrsHttpParser *obj = (SrsHttpParser *)parser->data;
srs_assert(obj);
@ -243,7 +267,7 @@ int SrsHttpParser::on_header_field(http_parser *parser, const char *at, size_t l
return 0;
}
int SrsHttpParser::on_header_value(http_parser *parser, const char *at, size_t length)
int SrsHttpParser::on_header_value(llhttp_t *parser, const char *at, size_t length)
{
SrsHttpParser *obj = (SrsHttpParser *)parser->data;
srs_assert(obj);
@ -256,7 +280,7 @@ int SrsHttpParser::on_header_value(http_parser *parser, const char *at, size_t l
return 0;
}
int SrsHttpParser::on_body(http_parser *parser, const char *at, size_t length)
int SrsHttpParser::on_body(llhttp_t *parser, const char *at, size_t length)
{
SrsHttpParser *obj = (SrsHttpParser *)parser->data;
srs_assert(obj);
@ -279,9 +303,9 @@ SrsHttpMessage::SrsHttpMessage(ISrsReader *reader, SrsFastStream *buffer) : ISrs
jsonp = false;
// As 0 is DELETE, so we use GET as default.
_method = (http_method)SRS_CONSTS_HTTP_GET;
_method = (llhttp_method_t)SRS_CONSTS_HTTP_GET;
// 200 is ok.
_status = (http_status)SRS_CONSTS_HTTP_OK;
_status = (llhttp_status_t)SRS_CONSTS_HTTP_OK;
// -1 means infinity chunked mode.
_content_length = -1;
// From HTTP/1.1, default to keep alive.
@ -297,12 +321,15 @@ SrsHttpMessage::~SrsHttpMessage()
srs_freep(_uri);
}
void SrsHttpMessage::set_basic(uint8_t type, http_method method, http_status status, int64_t content_length)
void SrsHttpMessage::set_basic(uint8_t type, llhttp_method_t method, llhttp_status_t status, int64_t content_length)
{
type_ = type;
_method = method;
_status = status;
if (_content_length == -1) {
// We use -1 as uninitialized mode, while llhttp use 0, so we should only
// update it when it's not 0 and the message is not initialized.
if (_content_length == -1 && content_length) {
_content_length = content_length;
}
}
@ -455,7 +482,7 @@ string SrsHttpMessage::method_str()
return jsonp_method;
}
return http_method_str((http_method)_method);
return llhttp_method_name((llhttp_method_t)_method);
}
bool SrsHttpMessage::is_http_get()
@ -1136,6 +1163,7 @@ srs_error_t SrsHttpResponseReader::read_chunked(void *data, size_t nb_data, ssiz
// find the CRLF of chunk header end.
char *start = buffer->bytes();
char *end = start + buffer->size();
for (char *p = start; p < end - 1; p++) {
if (p[0] == SRS_HTTP_CR && p[1] == SRS_HTTP_LF) {
// invalid chunk, ignore.

View File

@ -21,13 +21,13 @@ class ISrsReader;
class SrsHttpResponseReader;
class ISrsProtocolReadWriter;
// A wrapper for http-parser,
// A wrapper for llhttp,
// provides HTTP message originted service.
class SrsHttpParser
{
private:
http_parser_settings settings;
http_parser parser;
llhttp_settings_t settings;
llhttp_t parser;
// The global parse buffer.
SrsFastStream *buffer;
// Whether allow jsonp parse.
@ -37,20 +37,20 @@ private:
std::string field_name;
std::string field_value;
SrsHttpParseState state;
http_parser hp_header;
llhttp_t hp_header;
std::string url;
SrsHttpHeader *header;
enum http_parser_type type_;
enum http_parser_type parsed_type_;
enum llhttp_type type_;
enum llhttp_type parsed_type_;
public:
SrsHttpParser();
virtual ~SrsHttpParser();
public:
// initialize the http parser with specified type,
// initialize the llhttp parser with specified type,
// one parser can only parse request or response messages.
virtual srs_error_t initialize(enum http_parser_type type);
virtual srs_error_t initialize(enum llhttp_type type);
// Whether allow jsonp parser, which indicates the method in query string.
virtual void set_jsonp(bool allow_jsonp);
// always parse a http message,
@ -65,13 +65,13 @@ private:
virtual srs_error_t parse_message_imp(ISrsReader *reader);
private:
static int on_message_begin(http_parser *parser);
static int on_headers_complete(http_parser *parser);
static int on_message_complete(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_body(http_parser *parser, const char *at, size_t length);
static int on_message_begin(llhttp_t *parser);
static int on_headers_complete(llhttp_t *parser);
static int on_message_complete(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_body(llhttp_t *parser, const char *at, size_t length);
};
// A Request represents an HTTP request received by a server
@ -95,8 +95,8 @@ private:
// enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
uint8_t type_;
// The HTTP method defined by HTTP_METHOD_MAP
http_method _method;
http_status _status;
llhttp_method_t _method;
llhttp_status_t _status;
int64_t _content_length;
private:
@ -131,7 +131,7 @@ public:
public:
// Set the basic information for HTTP request.
// @remark User must call set_basic before set_header, because the content_length will be overwrite by header.
virtual void set_basic(uint8_t type, http_method method, http_status status, int64_t content_length);
virtual void set_basic(uint8_t type, llhttp_method_t method, llhttp_status_t status, int64_t content_length);
// Set HTTP header and whether the request require keep alive.
// @remark User must call set_header before set_url, because the Host in header is used for url.
virtual void set_header(SrsHttpHeader *header, bool keep_alive);

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@
#include <srs_core.hpp>
#include <srs_kernel_io.hpp>
#include <srs_protocol_http_stack_llhttp.hpp>
#include <map>
#include <string>
@ -669,9 +670,10 @@ public:
virtual std::string password();
private:
// Get the parsed url field.
// @return return empty string if not set.
virtual std::string get_uri_field(const std::string &uri, void *hp_u, int field);
// Simple URL parser to replace http-parser URL parsing
virtual srs_error_t parse_url_simple(const std::string &url, std::string &schema, std::string &host, int &port,
std::string &path, std::string &query, std::string &fragment,
std::string &username, std::string &password);
srs_error_t parse_query();
public:
@ -688,441 +690,3 @@ extern srs_error_t srs_av_base64_encode(std::string plaintext, std::string &ciph
// For #ifndef SRS_PROTOCOL_HTTP_HPP
#endif
// The http-parser is license under MIT at https://github.com/nodejs/http-parser/blob/master/LICENSE-MIT
// Version: 2.9.2 from https://github.com/nodejs/http-parser/releases/tag/v2.9.2
// File: https://raw.githubusercontent.com/nodejs/http-parser/5c17dad400e45c5a442a63f250fff2638d144682/http_parser.h
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/* 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 2
#include <stddef.h>
#include <stdint.h>
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
* faster
*/
#ifndef HTTP_PARSER_STRICT
#define HTTP_PARSER_STRICT 1
#endif
/* Maximium header size allowed. If the macro is not defined
* before including this header then the default is used. To
* change the maximum header size, define the macro in the build
* environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
* the effective limit on the size of the header, define the macro
* to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
*/
#ifndef HTTP_MAX_HEADER_SIZE
#define HTTP_MAX_HEADER_SIZE (80 * 1024)
#endif
typedef struct http_parser http_parser;
typedef struct http_parser_settings http_parser_settings;
/* Callbacks should return non-zero to indicate an error. The parser will
* then halt execution.
*
* The one exception is on_headers_complete. In a HTTP_RESPONSE parser
* returning '1' from on_headers_complete will tell the parser that it
* should not expect a body. This is used when receiving a response to a
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
* chunked' headers that indicate the presence of a body.
*
* Returning `2` from on_headers_complete will tell parser that it should not
* expect neither a body nor any futher responses on this connection. This is
* useful for handling responses to a CONNECT request which may not contain
* `Upgrade` or `Connection: upgrade` headers.
*
* http_data_cb does not return data chunks. It will be called arbitrarily
* many times for each string. E.G. you might get 10 callbacks for "on_url"
* each providing just a few characters more data.
*/
typedef int (*http_data_cb)(http_parser *, const char *at, size_t length);
typedef int (*http_cb)(http_parser *);
/* Status Codes */
#define HTTP_STATUS_MAP(XX) \
XX(100, CONTINUE, Continue) \
XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
XX(102, PROCESSING, Processing) \
XX(200, OK, OK) \
XX(201, CREATED, Created) \
XX(202, ACCEPTED, Accepted) \
XX(203, NON_AUTHORITATIVE_INFORMATION, Non - Authoritative Information) \
XX(204, NO_CONTENT, No Content) \
XX(205, RESET_CONTENT, Reset Content) \
XX(206, PARTIAL_CONTENT, Partial Content) \
XX(207, MULTI_STATUS, Multi - Status) \
XX(208, ALREADY_REPORTED, Already Reported) \
XX(226, IM_USED, IM Used) \
XX(300, MULTIPLE_CHOICES, Multiple Choices) \
XX(301, MOVED_PERMANENTLY, Moved Permanently) \
XX(302, FOUND, Found) \
XX(303, SEE_OTHER, See Other) \
XX(304, NOT_MODIFIED, Not Modified) \
XX(305, USE_PROXY, Use Proxy) \
XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \
XX(308, PERMANENT_REDIRECT, Permanent Redirect) \
XX(400, BAD_REQUEST, Bad Request) \
XX(401, UNAUTHORIZED, Unauthorized) \
XX(402, PAYMENT_REQUIRED, Payment Required) \
XX(403, FORBIDDEN, Forbidden) \
XX(404, NOT_FOUND, Not Found) \
XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \
XX(406, NOT_ACCEPTABLE, Not Acceptable) \
XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \
XX(408, REQUEST_TIMEOUT, Request Timeout) \
XX(409, CONFLICT, Conflict) \
XX(410, GONE, Gone) \
XX(411, LENGTH_REQUIRED, Length Required) \
XX(412, PRECONDITION_FAILED, Precondition Failed) \
XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \
XX(414, URI_TOO_LONG, URI Too Long) \
XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \
XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \
XX(417, EXPECTATION_FAILED, Expectation Failed) \
XX(421, MISDIRECTED_REQUEST, Misdirected Request) \
XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \
XX(423, LOCKED, Locked) \
XX(424, FAILED_DEPENDENCY, Failed Dependency) \
XX(426, UPGRADE_REQUIRED, Upgrade Required) \
XX(428, PRECONDITION_REQUIRED, Precondition Required) \
XX(429, TOO_MANY_REQUESTS, Too Many Requests) \
XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \
XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \
XX(501, NOT_IMPLEMENTED, Not Implemented) \
XX(502, BAD_GATEWAY, Bad Gateway) \
XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \
XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \
XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \
XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \
XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \
XX(508, LOOP_DETECTED, Loop Detected) \
XX(510, NOT_EXTENDED, Not Extended) \
XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required)
enum http_status {
#define XX(num, name, string) HTTP_STATUS_##name = num,
HTTP_STATUS_MAP(XX)
#undef XX
};
/* Request Methods */
#define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
/* pathological */ \
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
/* WebDAV */ \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
XX(11, MOVE, MOVE) \
XX(12, PROPFIND, PROPFIND) \
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
/* subversion */ \
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
/* upnp */ \
XX(24, MSEARCH, M - SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
/* RFC-5789 */ \
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
/* CalDAV */ \
XX(30, MKCALENDAR, MKCALENDAR) \
/* RFC-2068, section 19.6.1.2 */ \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
/* icecast */ \
XX(33, SOURCE, SOURCE)
enum http_method {
#define XX(num, name, string) HTTP_##name = num,
HTTP_METHOD_MAP(XX)
#undef XX
};
enum http_parser_type { HTTP_REQUEST,
HTTP_RESPONSE,
HTTP_BOTH };
/* Flag values for http_parser.flags field */
enum flags { F_CHUNKED = 1 << 0,
F_CONNECTION_KEEP_ALIVE = 1 << 1,
F_CONNECTION_CLOSE = 1 << 2,
F_CONNECTION_UPGRADE = 1 << 3,
F_TRAILING = 1 << 4,
F_UPGRADE = 1 << 5,
F_SKIPBODY = 1 << 6,
F_CONTENTLENGTH = 1 << 7
};
/* Map for errno-related constants
*
* The provided argument should be a macro that takes 2 arguments.
*/
#define HTTP_ERRNO_MAP(XX) \
/* No error */ \
XX(OK, "success") \
\
/* Callback-related errors */ \
XX(CB_message_begin, "the on_message_begin callback failed") \
XX(CB_url, "the on_url callback failed") \
XX(CB_header_field, "the on_header_field callback failed") \
XX(CB_header_value, "the on_header_value callback failed") \
XX(CB_headers_complete, "the on_headers_complete callback failed") \
XX(CB_body, "the on_body callback failed") \
XX(CB_message_complete, "the on_message_complete callback failed") \
XX(CB_status, "the on_status callback failed") \
XX(CB_chunk_header, "the on_chunk_header callback failed") \
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
\
/* Parsing-related errors */ \
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
XX(HEADER_OVERFLOW, \
"too many header bytes seen; overflow detected") \
XX(CLOSED_CONNECTION, \
"data received after completed connection: close message") \
XX(INVALID_VERSION, "invalid HTTP version") \
XX(INVALID_STATUS, "invalid HTTP status code") \
XX(INVALID_METHOD, "invalid HTTP method") \
XX(INVALID_URL, "invalid URL") \
XX(INVALID_HOST, "invalid host") \
XX(INVALID_PORT, "invalid port") \
XX(INVALID_PATH, "invalid path") \
XX(INVALID_QUERY_STRING, "invalid query string") \
XX(INVALID_FRAGMENT, "invalid fragment") \
XX(LF_EXPECTED, "LF character expected") \
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
XX(INVALID_CONTENT_LENGTH, \
"invalid character in content-length header") \
XX(UNEXPECTED_CONTENT_LENGTH, \
"unexpected content-length header") \
XX(INVALID_CHUNK_SIZE, \
"invalid character in chunk size header") \
XX(INVALID_CONSTANT, "invalid constant string") \
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state") \
XX(STRICT, "strict mode assertion failed") \
XX(PAUSED, "parser is paused") \
XX(UNKNOWN, "an unknown error occurred")
/* Define HPE_* values for each errno value above */
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
enum http_errno {
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
};
#undef HTTP_ERRNO_GEN
/* Get an http_errno value from an http_parser */
#define HTTP_PARSER_ERRNO(p) ((enum http_errno)(p)->http_errno)
struct http_parser {
/** PRIVATE **/
unsigned int type : 2; /* enum http_parser_type */
unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
unsigned int state : 7; /* enum state from http_parser.c */
unsigned int header_state : 7; /* enum header_state from http_parser.c */
unsigned int index : 7; /* index into current matcher */
unsigned int lenient_http_headers : 1;
uint32_t nread; /* # bytes read in various scenarios */
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
/** READ-ONLY **/
unsigned short http_major;
unsigned short http_minor;
unsigned int status_code : 16; /* responses only */
unsigned int method : 8; /* requests only */
unsigned int http_errno : 7;
/* 1 = Upgrade header was present and the parser has exited because of that.
* 0 = No upgrade header present.
* Should be checked when http_parser_execute() returns in addition to
* error checking.
*/
unsigned int upgrade : 1;
/** PUBLIC **/
void *data; /* A pointer to get hook to the "connection" or "socket" object */
};
struct http_parser_settings {
http_cb on_message_begin;
http_data_cb on_url;
http_data_cb on_status;
http_data_cb on_header_field;
http_data_cb on_header_value;
http_cb on_headers_complete;
http_data_cb on_body;
http_cb on_message_complete;
/* When on_chunk_header is called, the current chunk length is stored
* in parser->content_length.
*/
http_cb on_chunk_header;
http_cb on_chunk_complete;
};
enum http_parser_url_fields { UF_SCHEMA = 0,
UF_HOST = 1,
UF_PORT = 2,
UF_PATH = 3,
UF_QUERY = 4,
UF_FRAGMENT = 5,
UF_USERINFO = 6,
UF_MAX = 7
};
/* Result structure for http_parser_parse_url().
*
* Callers should index into field_data[] with UF_* values iff field_set
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
* because we probably have padding left over), we convert any port to
* a uint16_t.
*/
struct http_parser_url {
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
uint16_t port; /* Converted UF_PORT string */
struct {
uint16_t off; /* Offset into buffer in which field starts */
uint16_t len; /* Length of run in buffer */
} field_data[UF_MAX];
};
/* Returns the library version. Bits 16-23 contain the major version number,
* bits 8-15 the minor version number and bits 0-7 the patch level.
* Usage example:
*
* unsigned long version = http_parser_version();
* unsigned major = (version >> 16) & 255;
* unsigned minor = (version >> 8) & 255;
* unsigned patch = version & 255;
* printf("http_parser v%u.%u.%u\n", major, minor, patch);
*/
unsigned long http_parser_version(void);
void http_parser_init(http_parser *parser, enum http_parser_type type);
/* Initialize http_parser_settings members to 0
*/
void http_parser_settings_init(http_parser_settings *settings);
/* Executes the parser. Returns number of parsed bytes. Sets
* `parser->http_errno` on error. */
size_t http_parser_execute(http_parser *parser,
const http_parser_settings *settings,
const char *data,
size_t len);
/* If http_should_keep_alive() in the on_headers_complete or
* on_message_complete callback returns 0, then this should be
* the last message on the connection.
* If you are the server, respond with the "Connection: close" header.
* If you are the client, close the connection.
*/
int http_should_keep_alive(const http_parser *parser);
/* Returns a string version of the HTTP method. */
const char *http_method_str(enum http_method m);
/* Returns a string version of the HTTP status code. */
const char *http_status_str(enum http_status s);
/* Return a string name of the given error */
const char *http_errno_name(enum http_errno err);
/* Return a string description of the given error */
const char *http_errno_description(enum http_errno err);
/* Initialize all http_parser_url members to 0 */
void http_parser_url_init(struct http_parser_url *u);
/* Parse a URL; return nonzero on failure */
int http_parser_parse_url(const char *buf, size_t buflen,
int is_connect,
struct http_parser_url *u);
/* Pause or un-pause the parser; a nonzero value pauses */
void http_parser_pause(http_parser *parser, int paused);
/* Checks if this is the final chunk of the body. */
int http_body_is_final(const http_parser *parser);
/* Change the maximum header size provided at compile time. */
void http_parser_set_max_header_size(uint32_t size);
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,946 @@
//
// Copyright (c) 2013-2025 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#ifndef SRS_PROTOCOL_HTTP_LLHTTP_HPP
#define SRS_PROTOCOL_HTTP_LLHTTP_HPP
#include <srs_core.hpp>
// The llhttp is licensed under MIT, see https://github.com/nodejs/llhttp
// Version:
// 9.3.0
// The files are generated by:
// git clone https://github.com/nodejs/llhttp.git
// cd llhttp
// make
// Note that these files are copied to SRS and formated by clang-format:
// ./scripts/clang_format.sh
// Copy the header files to bellow:
// build/llhttp.h
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_LLHTTP_H_
#define INCLUDE_LLHTTP_H_
#define LLHTTP_VERSION_MAJOR 9
#define LLHTTP_VERSION_MINOR 3
#define LLHTTP_VERSION_PATCH 0
#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 lenient_flags;
uint8_t upgrade;
uint8_t finish;
uint16_t flags;
uint16_t status_code;
uint8_t initial_message_completed;
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_PAUSED_H2_UPGRADE = 23,
HPE_USER = 24,
HPE_CR_EXPECTED = 25,
HPE_CB_URL_COMPLETE = 26,
HPE_CB_STATUS_COMPLETE = 27,
HPE_CB_HEADER_FIELD_COMPLETE = 28,
HPE_CB_HEADER_VALUE_COMPLETE = 29,
HPE_UNEXPECTED_SPACE = 30,
HPE_CB_RESET = 31,
HPE_CB_METHOD_COMPLETE = 32,
HPE_CB_VERSION_COMPLETE = 33,
HPE_CB_CHUNK_EXTENSION_NAME_COMPLETE = 34,
HPE_CB_CHUNK_EXTENSION_VALUE_COMPLETE = 35,
HPE_CB_PROTOCOL_COMPLETE = 38
};
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_TRANSFER_ENCODING = 0x200
};
typedef enum llhttp_flags llhttp_flags_t;
enum llhttp_lenient_flags {
LENIENT_HEADERS = 0x1,
LENIENT_CHUNKED_LENGTH = 0x2,
LENIENT_KEEP_ALIVE = 0x4,
LENIENT_TRANSFER_ENCODING = 0x8,
LENIENT_VERSION = 0x10,
LENIENT_DATA_AFTER_CLOSE = 0x20,
LENIENT_OPTIONAL_LF_AFTER_CR = 0x40,
LENIENT_OPTIONAL_CRLF_AFTER_CHUNK = 0x80,
LENIENT_OPTIONAL_CR_BEFORE_LF = 0x100,
LENIENT_SPACES_AFTER_CHUNK_SIZE = 0x200
};
typedef enum llhttp_lenient_flags llhttp_lenient_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,
HTTP_QUERY = 46
};
typedef enum llhttp_method llhttp_method_t;
enum llhttp_status {
HTTP_STATUS_CONTINUE = 100,
HTTP_STATUS_SWITCHING_PROTOCOLS = 101,
HTTP_STATUS_PROCESSING = 102,
HTTP_STATUS_EARLY_HINTS = 103,
HTTP_STATUS_RESPONSE_IS_STALE = 110,
HTTP_STATUS_REVALIDATION_FAILED = 111,
HTTP_STATUS_DISCONNECTED_OPERATION = 112,
HTTP_STATUS_HEURISTIC_EXPIRATION = 113,
HTTP_STATUS_MISCELLANEOUS_WARNING = 199,
HTTP_STATUS_OK = 200,
HTTP_STATUS_CREATED = 201,
HTTP_STATUS_ACCEPTED = 202,
HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203,
HTTP_STATUS_NO_CONTENT = 204,
HTTP_STATUS_RESET_CONTENT = 205,
HTTP_STATUS_PARTIAL_CONTENT = 206,
HTTP_STATUS_MULTI_STATUS = 207,
HTTP_STATUS_ALREADY_REPORTED = 208,
HTTP_STATUS_TRANSFORMATION_APPLIED = 214,
HTTP_STATUS_IM_USED = 226,
HTTP_STATUS_MISCELLANEOUS_PERSISTENT_WARNING = 299,
HTTP_STATUS_MULTIPLE_CHOICES = 300,
HTTP_STATUS_MOVED_PERMANENTLY = 301,
HTTP_STATUS_FOUND = 302,
HTTP_STATUS_SEE_OTHER = 303,
HTTP_STATUS_NOT_MODIFIED = 304,
HTTP_STATUS_USE_PROXY = 305,
HTTP_STATUS_SWITCH_PROXY = 306,
HTTP_STATUS_TEMPORARY_REDIRECT = 307,
HTTP_STATUS_PERMANENT_REDIRECT = 308,
HTTP_STATUS_BAD_REQUEST = 400,
HTTP_STATUS_UNAUTHORIZED = 401,
HTTP_STATUS_PAYMENT_REQUIRED = 402,
HTTP_STATUS_FORBIDDEN = 403,
HTTP_STATUS_NOT_FOUND = 404,
HTTP_STATUS_METHOD_NOT_ALLOWED = 405,
HTTP_STATUS_NOT_ACCEPTABLE = 406,
HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407,
HTTP_STATUS_REQUEST_TIMEOUT = 408,
HTTP_STATUS_CONFLICT = 409,
HTTP_STATUS_GONE = 410,
HTTP_STATUS_LENGTH_REQUIRED = 411,
HTTP_STATUS_PRECONDITION_FAILED = 412,
HTTP_STATUS_PAYLOAD_TOO_LARGE = 413,
HTTP_STATUS_URI_TOO_LONG = 414,
HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415,
HTTP_STATUS_RANGE_NOT_SATISFIABLE = 416,
HTTP_STATUS_EXPECTATION_FAILED = 417,
HTTP_STATUS_IM_A_TEAPOT = 418,
HTTP_STATUS_PAGE_EXPIRED = 419,
HTTP_STATUS_ENHANCE_YOUR_CALM = 420,
HTTP_STATUS_MISDIRECTED_REQUEST = 421,
HTTP_STATUS_UNPROCESSABLE_ENTITY = 422,
HTTP_STATUS_LOCKED = 423,
HTTP_STATUS_FAILED_DEPENDENCY = 424,
HTTP_STATUS_TOO_EARLY = 425,
HTTP_STATUS_UPGRADE_REQUIRED = 426,
HTTP_STATUS_PRECONDITION_REQUIRED = 428,
HTTP_STATUS_TOO_MANY_REQUESTS = 429,
HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL = 430,
HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
HTTP_STATUS_LOGIN_TIMEOUT = 440,
HTTP_STATUS_NO_RESPONSE = 444,
HTTP_STATUS_RETRY_WITH = 449,
HTTP_STATUS_BLOCKED_BY_PARENTAL_CONTROL = 450,
HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451,
HTTP_STATUS_CLIENT_CLOSED_LOAD_BALANCED_REQUEST = 460,
HTTP_STATUS_INVALID_X_FORWARDED_FOR = 463,
HTTP_STATUS_REQUEST_HEADER_TOO_LARGE = 494,
HTTP_STATUS_SSL_CERTIFICATE_ERROR = 495,
HTTP_STATUS_SSL_CERTIFICATE_REQUIRED = 496,
HTTP_STATUS_HTTP_REQUEST_SENT_TO_HTTPS_PORT = 497,
HTTP_STATUS_INVALID_TOKEN = 498,
HTTP_STATUS_CLIENT_CLOSED_REQUEST = 499,
HTTP_STATUS_INTERNAL_SERVER_ERROR = 500,
HTTP_STATUS_NOT_IMPLEMENTED = 501,
HTTP_STATUS_BAD_GATEWAY = 502,
HTTP_STATUS_SERVICE_UNAVAILABLE = 503,
HTTP_STATUS_GATEWAY_TIMEOUT = 504,
HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505,
HTTP_STATUS_VARIANT_ALSO_NEGOTIATES = 506,
HTTP_STATUS_INSUFFICIENT_STORAGE = 507,
HTTP_STATUS_LOOP_DETECTED = 508,
HTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509,
HTTP_STATUS_NOT_EXTENDED = 510,
HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511,
HTTP_STATUS_WEB_SERVER_UNKNOWN_ERROR = 520,
HTTP_STATUS_WEB_SERVER_IS_DOWN = 521,
HTTP_STATUS_CONNECTION_TIMEOUT = 522,
HTTP_STATUS_ORIGIN_IS_UNREACHABLE = 523,
HTTP_STATUS_TIMEOUT_OCCURED = 524,
HTTP_STATUS_SSL_HANDSHAKE_FAILED = 525,
HTTP_STATUS_INVALID_SSL_CERTIFICATE = 526,
HTTP_STATUS_RAILGUN_ERROR = 527,
HTTP_STATUS_SITE_IS_OVERLOADED = 529,
HTTP_STATUS_SITE_IS_FROZEN = 530,
HTTP_STATUS_IDENTITY_PROVIDER_AUTHENTICATION_ERROR = 561,
HTTP_STATUS_NETWORK_READ_TIMEOUT = 598,
HTTP_STATUS_NETWORK_CONNECT_TIMEOUT = 599
};
typedef enum llhttp_status llhttp_status_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, PAUSED_H2_UPGRADE, PAUSED_H2_UPGRADE) \
XX(24, USER, USER) \
XX(25, CR_EXPECTED, CR_EXPECTED) \
XX(26, CB_URL_COMPLETE, CB_URL_COMPLETE) \
XX(27, CB_STATUS_COMPLETE, CB_STATUS_COMPLETE) \
XX(28, CB_HEADER_FIELD_COMPLETE, CB_HEADER_FIELD_COMPLETE) \
XX(29, CB_HEADER_VALUE_COMPLETE, CB_HEADER_VALUE_COMPLETE) \
XX(30, UNEXPECTED_SPACE, UNEXPECTED_SPACE) \
XX(31, CB_RESET, CB_RESET) \
XX(32, CB_METHOD_COMPLETE, CB_METHOD_COMPLETE) \
XX(33, CB_VERSION_COMPLETE, CB_VERSION_COMPLETE) \
XX(34, CB_CHUNK_EXTENSION_NAME_COMPLETE, CB_CHUNK_EXTENSION_NAME_COMPLETE) \
XX(35, CB_CHUNK_EXTENSION_VALUE_COMPLETE, CB_CHUNK_EXTENSION_VALUE_COMPLETE) \
XX(38, CB_PROTOCOL_COMPLETE, CB_PROTOCOL_COMPLETE)
#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(46, QUERY, QUERY)
#define RTSP_METHOD_MAP(XX) \
XX(1, GET, GET) \
XX(3, POST, POST) \
XX(6, OPTIONS, OPTIONS) \
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)
#define HTTP_ALL_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) \
XX(46, QUERY, QUERY)
#define HTTP_STATUS_MAP(XX) \
XX(100, CONTINUE, CONTINUE) \
XX(101, SWITCHING_PROTOCOLS, SWITCHING_PROTOCOLS) \
XX(102, PROCESSING, PROCESSING) \
XX(103, EARLY_HINTS, EARLY_HINTS) \
XX(110, RESPONSE_IS_STALE, RESPONSE_IS_STALE) \
XX(111, REVALIDATION_FAILED, REVALIDATION_FAILED) \
XX(112, DISCONNECTED_OPERATION, DISCONNECTED_OPERATION) \
XX(113, HEURISTIC_EXPIRATION, HEURISTIC_EXPIRATION) \
XX(199, MISCELLANEOUS_WARNING, MISCELLANEOUS_WARNING) \
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(214, TRANSFORMATION_APPLIED, TRANSFORMATION_APPLIED) \
XX(226, IM_USED, IM_USED) \
XX(299, MISCELLANEOUS_PERSISTENT_WARNING, MISCELLANEOUS_PERSISTENT_WARNING) \
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(306, SWITCH_PROXY, SWITCH_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(418, IM_A_TEAPOT, IM_A_TEAPOT) \
XX(419, PAGE_EXPIRED, PAGE_EXPIRED) \
XX(420, ENHANCE_YOUR_CALM, ENHANCE_YOUR_CALM) \
XX(421, MISDIRECTED_REQUEST, MISDIRECTED_REQUEST) \
XX(422, UNPROCESSABLE_ENTITY, UNPROCESSABLE_ENTITY) \
XX(423, LOCKED, LOCKED) \
XX(424, FAILED_DEPENDENCY, FAILED_DEPENDENCY) \
XX(425, TOO_EARLY, TOO_EARLY) \
XX(426, UPGRADE_REQUIRED, UPGRADE_REQUIRED) \
XX(428, PRECONDITION_REQUIRED, PRECONDITION_REQUIRED) \
XX(429, TOO_MANY_REQUESTS, TOO_MANY_REQUESTS) \
XX(430, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL) \
XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, REQUEST_HEADER_FIELDS_TOO_LARGE) \
XX(440, LOGIN_TIMEOUT, LOGIN_TIMEOUT) \
XX(444, NO_RESPONSE, NO_RESPONSE) \
XX(449, RETRY_WITH, RETRY_WITH) \
XX(450, BLOCKED_BY_PARENTAL_CONTROL, BLOCKED_BY_PARENTAL_CONTROL) \
XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, UNAVAILABLE_FOR_LEGAL_REASONS) \
XX(460, CLIENT_CLOSED_LOAD_BALANCED_REQUEST, CLIENT_CLOSED_LOAD_BALANCED_REQUEST) \
XX(463, INVALID_X_FORWARDED_FOR, INVALID_X_FORWARDED_FOR) \
XX(494, REQUEST_HEADER_TOO_LARGE, REQUEST_HEADER_TOO_LARGE) \
XX(495, SSL_CERTIFICATE_ERROR, SSL_CERTIFICATE_ERROR) \
XX(496, SSL_CERTIFICATE_REQUIRED, SSL_CERTIFICATE_REQUIRED) \
XX(497, HTTP_REQUEST_SENT_TO_HTTPS_PORT, HTTP_REQUEST_SENT_TO_HTTPS_PORT) \
XX(498, INVALID_TOKEN, INVALID_TOKEN) \
XX(499, CLIENT_CLOSED_REQUEST, CLIENT_CLOSED_REQUEST) \
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(509, BANDWIDTH_LIMIT_EXCEEDED, BANDWIDTH_LIMIT_EXCEEDED) \
XX(510, NOT_EXTENDED, NOT_EXTENDED) \
XX(511, NETWORK_AUTHENTICATION_REQUIRED, NETWORK_AUTHENTICATION_REQUIRED) \
XX(520, WEB_SERVER_UNKNOWN_ERROR, WEB_SERVER_UNKNOWN_ERROR) \
XX(521, WEB_SERVER_IS_DOWN, WEB_SERVER_IS_DOWN) \
XX(522, CONNECTION_TIMEOUT, CONNECTION_TIMEOUT) \
XX(523, ORIGIN_IS_UNREACHABLE, ORIGIN_IS_UNREACHABLE) \
XX(524, TIMEOUT_OCCURED, TIMEOUT_OCCURED) \
XX(525, SSL_HANDSHAKE_FAILED, SSL_HANDSHAKE_FAILED) \
XX(526, INVALID_SSL_CERTIFICATE, INVALID_SSL_CERTIFICATE) \
XX(527, RAILGUN_ERROR, RAILGUN_ERROR) \
XX(529, SITE_IS_OVERLOADED, SITE_IS_OVERLOADED) \
XX(530, SITE_IS_FROZEN, SITE_IS_FROZEN) \
XX(561, IDENTITY_PROVIDER_AUTHENTICATION_ERROR, IDENTITY_PROVIDER_AUTHENTICATION_ERROR) \
XX(598, NETWORK_READ_TIMEOUT, NETWORK_READ_TIMEOUT) \
XX(599, NETWORK_CONNECT_TIMEOUT, NETWORK_CONNECT_TIMEOUT)
#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>
#if defined(__wasm__)
#define LLHTTP_EXPORT __attribute__((visibility("default")))
#elif defined(_WIN32)
#define LLHTTP_EXPORT __declspec(dllexport)
#else
#define LLHTTP_EXPORT
#endif
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;
/* Possible return values 0, -1, HPE_USER */
llhttp_data_cb on_protocol;
llhttp_data_cb on_url;
llhttp_data_cb on_status;
llhttp_data_cb on_method;
llhttp_data_cb on_version;
llhttp_data_cb on_header_field;
llhttp_data_cb on_header_value;
llhttp_data_cb on_chunk_extension_name;
llhttp_data_cb on_chunk_extension_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;
/* Possible return values 0, -1, HPE_USER */
llhttp_data_cb on_body;
/* Possible return values 0, -1, `HPE_PAUSED` */
llhttp_cb on_message_complete;
llhttp_cb on_protocol_complete;
llhttp_cb on_url_complete;
llhttp_cb on_status_complete;
llhttp_cb on_method_complete;
llhttp_cb on_version_complete;
llhttp_cb on_header_field_complete;
llhttp_cb on_header_value_complete;
llhttp_cb on_chunk_extension_name_complete;
llhttp_cb on_chunk_extension_value_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;
llhttp_cb on_reset;
};
/* 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.
*/
LLHTTP_EXPORT
void llhttp_init(llhttp_t *parser, llhttp_type_t type,
const llhttp_settings_t *settings);
LLHTTP_EXPORT
llhttp_t *llhttp_alloc(llhttp_type_t type);
LLHTTP_EXPORT
void llhttp_free(llhttp_t *parser);
LLHTTP_EXPORT
uint8_t llhttp_get_type(llhttp_t *parser);
LLHTTP_EXPORT
uint8_t llhttp_get_http_major(llhttp_t *parser);
LLHTTP_EXPORT
uint8_t llhttp_get_http_minor(llhttp_t *parser);
LLHTTP_EXPORT
uint8_t llhttp_get_method(llhttp_t *parser);
LLHTTP_EXPORT
int llhttp_get_status_code(llhttp_t *parser);
LLHTTP_EXPORT
uint8_t llhttp_get_upgrade(llhttp_t *parser);
/* Reset an already initialized parser back to the start state, preserving the
* existing parser type, callback settings, user data, and lenient flags.
*/
LLHTTP_EXPORT
void llhttp_reset(llhttp_t *parser);
/* Initialize the settings object */
LLHTTP_EXPORT
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_EXPORT
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_EXPORT
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
*/
LLHTTP_EXPORT
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.
*/
LLHTTP_EXPORT
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.
*/
LLHTTP_EXPORT
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`.
*/
LLHTTP_EXPORT
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`
*/
LLHTTP_EXPORT
void llhttp_resume_after_upgrade(llhttp_t *parser);
/* Returns the latest return error */
LLHTTP_EXPORT
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.
*/
LLHTTP_EXPORT
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.
*/
LLHTTP_EXPORT
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.
*/
LLHTTP_EXPORT
const char *llhttp_get_error_pos(const llhttp_t *parser);
/* Returns textual name of error code */
LLHTTP_EXPORT
const char *llhttp_errno_name(llhttp_errno_t err);
/* Returns textual name of HTTP method */
LLHTTP_EXPORT
const char *llhttp_method_name(llhttp_method_t method);
/* Returns textual name of HTTP status */
LLHTTP_EXPORT
const char *llhttp_status_name(llhttp_status_t status);
/* 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".
*
* **Enabling this flag can pose a security issue since you will be exposed to
* request smuggling attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_headers(llhttp_t *parser, int enabled);
/* Enables/disables lenient handling of conflicting `Transfer-Encoding` and
* `Content-Length` headers (disabled by default).
*
* Normally `llhttp` would error when `Transfer-Encoding` is present in
* conjunction with `Content-Length`. This error is important to prevent HTTP
* request smuggling, but may be less desirable for small number of cases
* involving legacy servers.
*
* **Enabling this flag can pose a security issue since you will be exposed to
* request smuggling attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_chunked_length(llhttp_t *parser, int enabled);
/* Enables/disables lenient handling of `Connection: close` and HTTP/1.0
* requests responses.
*
* Normally `llhttp` would error on (in strict mode) or discard (in loose mode)
* the HTTP request/response after the request/response with `Connection: close`
* and `Content-Length`. This is important to prevent cache poisoning attacks,
* but might interact badly with outdated and insecure clients. With this flag
* the extra request/response will be parsed normally.
*
* **Enabling this flag can pose a security issue since you will be exposed to
* poisoning attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_keep_alive(llhttp_t *parser, int enabled);
/* Enables/disables lenient handling of `Transfer-Encoding` header.
*
* Normally `llhttp` would error when a `Transfer-Encoding` has `chunked` value
* and another value after it (either in a single header or in multiple
* headers whose value are internally joined using `, `).
* This is mandated by the spec to reliably determine request body size and thus
* avoid request smuggling.
* With this flag the extra value will be parsed normally.
*
* **Enabling this flag can pose a security issue since you will be exposed to
* request smuggling attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_transfer_encoding(llhttp_t *parser, int enabled);
/* Enables/disables lenient handling of HTTP version.
*
* Normally `llhttp` would error when the HTTP version in the request or status line
* is not `0.9`, `1.0`, `1.1` or `2.0`.
* With this flag the invalid value will be parsed normally.
*
* **Enabling this flag can pose a security issue since you will allow unsupported
* HTTP versions. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_version(llhttp_t *parser, int enabled);
/* Enables/disables lenient handling of additional data received after a message ends
* and keep-alive is disabled.
*
* Normally `llhttp` would error when additional unexpected data is received if the message
* contains the `Connection` header with `close` value.
* With this flag the extra data will discarded without throwing an error.
*
* **Enabling this flag can pose a security issue since you will be exposed to
* poisoning attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_data_after_close(llhttp_t *parser, int enabled);
/* Enables/disables lenient handling of incomplete CRLF sequences.
*
* Normally `llhttp` would error when a CR is not followed by LF when terminating the
* request line, the status line, the headers or a chunk header.
* With this flag only a CR is required to terminate such sections.
*
* **Enabling this flag can pose a security issue since you will be exposed to
* request smuggling attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_optional_lf_after_cr(llhttp_t *parser, int enabled);
/*
* Enables/disables lenient handling of line separators.
*
* Normally `llhttp` would error when a LF is not preceded by CR when terminating the
* request line, the status line, the headers, a chunk header or a chunk data.
* With this flag only a LF is required to terminate such sections.
*
* **Enabling this flag can pose a security issue since you will be exposed to
* request smuggling attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_optional_cr_before_lf(llhttp_t *parser, int enabled);
/* Enables/disables lenient handling of chunks not separated via CRLF.
*
* Normally `llhttp` would error when after a chunk data a CRLF is missing before
* starting a new chunk.
* With this flag the new chunk can start immediately after the previous one.
*
* **Enabling this flag can pose a security issue since you will be exposed to
* request smuggling attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t *parser, int enabled);
/* Enables/disables lenient handling of spaces after chunk size.
*
* Normally `llhttp` would error when after a chunk size is followed by one or more
* spaces are present instead of a CRLF or `;`.
* With this flag this check is disabled.
*
* **Enabling this flag can pose a security issue since you will be exposed to
* request smuggling attacks. USE WITH CAUTION!**
*/
LLHTTP_EXPORT
void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t *parser, int enabled);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* INCLUDE_LLHTTP_API_H_ */
#endif /* INCLUDE_LLHTTP_H_ */
#endif

View File

@ -0,0 +1,201 @@
//
// Copyright (c) 2013-2025 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#include <srs_protocol_http_stack_llhttpadapter.hpp>
// To avoid type mismatch, these functions are used to bridge the APIs.
// Bridge functions for internal callbacks that call the public API implementations
// These bridge the internal callback signature to the public API signature
extern int llhttp__on_message_begin(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_message_begin(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_message_begin((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_protocol(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_protocol(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_protocol((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_url(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_url(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_url((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_status(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_status(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_status((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_method(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_method(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_method((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_version(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_version(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_version((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_header_field(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_header_field(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_header_field((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_header_value(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_header_value(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_header_value((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_chunk_extension_name(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_chunk_extension_name(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_chunk_extension_name((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_chunk_extension_value(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_chunk_extension_value(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_chunk_extension_value((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_headers_complete(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_headers_complete(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_headers_complete((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_body(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_body(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_body((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_message_complete(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_message_complete(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_message_complete((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_url_complete(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_url_complete(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_url_complete((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_status_complete(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_status_complete(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_status_complete((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_method_complete(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_method_complete(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_method_complete((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_version_complete(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_version_complete(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_version_complete((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_header_field_complete(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_header_field_complete(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_header_field_complete((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_header_value_complete(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_header_value_complete(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_header_value_complete((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_chunk_header(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_chunk_header(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_chunk_header((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_chunk_complete(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_chunk_complete(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_chunk_complete((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_chunk_extension_name_complete(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_chunk_extension_name_complete(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_chunk_extension_name_complete((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_chunk_extension_value_complete(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_chunk_extension_value_complete(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_chunk_extension_value_complete((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_reset(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_reset(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_reset((llhttp_t *)s, (const char *)p, (const char *)endp);
}
extern int llhttp__on_protocol_complete(llhttp_t *s, const char *p, const char *endp);
int llhttp__on_protocol_complete(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return llhttp__on_protocol_complete((llhttp_t *)s, (const char *)p, (const char *)endp);
}
int llhttp__before_headers_complete(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return 0;
}
int llhttp__after_headers_complete(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return 0;
}
int llhttp__after_message_complete(llhttp__internal_t *s, const unsigned char *p, const unsigned char *endp)
{
return 0;
}

View File

@ -0,0 +1,14 @@
//
// Copyright (c) 2013-2025 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#ifndef SRS_PROTOCOL_HTTP_LLHTTPBRIDGE_HPP
#define SRS_PROTOCOL_HTTP_LLHTTPBRIDGE_HPP
#include <srs_core.hpp>
#include <srs_protocol_http_stack_llhttp.hpp>
#endif

View File

@ -0,0 +1,632 @@
//
// Copyright (c) 2013-2025 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#include <srs_protocol_http_stack_llhttpapi.hpp>
// The llhttp is licensed under MIT, see https://github.com/nodejs/llhttp
// Copy the implementation files to bellow:
// src/native/api.c
// We need to modify the code, to meet the requirements of g++ compiler.
//
// 1. Change:
// llhttp_type_t type = parser->type;
// To:
// llhttp_type_t type = (llhttp_type_t)parser->type;
//
// 2. Change:
// const llhttp_settings_t* settings = parser->settings;
// To:
// const llhttp_settings_t* settings = (const llhttp_settings_t*)parser->settings;
//
// 3. Change:
// return llhttp__internal_execute(parser, data, data + len);
// To:
// return (llhttp_errno_t)llhttp__internal_execute(parser, data, data + len);
//
// 4. Change:
// return 0;
// To:
// return (llhttp_errno_t)0;
//
// 5. Change:
// return err;
// To:
// return (llhttp_errno_t)err;
//
// 6. Change:
// return parser->error;
// To:
// return (llhttp_errno_t)parser->error;
// LCOV_EXCL_START
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// #include "llhttp.h"
#define CALLBACK_MAYBE(PARSER, NAME) \
do { \
const llhttp_settings_t *settings; \
settings = (const llhttp_settings_t *)(PARSER)->settings; \
if (settings == NULL || settings->NAME == NULL) { \
err = 0; \
break; \
} \
err = settings->NAME((PARSER)); \
} while (0)
#define SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN) \
do { \
const llhttp_settings_t *settings; \
settings = (const llhttp_settings_t *)(PARSER)->settings; \
if (settings == NULL || settings->NAME == NULL) { \
err = 0; \
break; \
} \
err = settings->NAME((PARSER), (START), (LEN)); \
if (err == -1) { \
err = HPE_USER; \
llhttp_set_error_reason((PARSER), "Span callback error in " #NAME); \
} \
} 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;
}
#if defined(__wasm__)
extern int wasm_on_message_begin(llhttp_t *p);
extern int wasm_on_url(llhttp_t *p, const char *at, size_t length);
extern int wasm_on_status(llhttp_t *p, const char *at, size_t length);
extern int wasm_on_header_field(llhttp_t *p, const char *at, size_t length);
extern int wasm_on_header_value(llhttp_t *p, const char *at, size_t length);
extern int wasm_on_headers_complete(llhttp_t *p, int status_code,
uint8_t upgrade, int should_keep_alive);
extern int wasm_on_body(llhttp_t *p, const char *at, size_t length);
extern int wasm_on_message_complete(llhttp_t *p);
static int wasm_on_headers_complete_wrap(llhttp_t *p)
{
return wasm_on_headers_complete(p, p->status_code, p->upgrade,
llhttp_should_keep_alive(p));
}
const llhttp_settings_t wasm_settings = {
.on_message_begin = wasm_on_message_begin,
.on_url = wasm_on_url,
.on_status = wasm_on_status,
.on_header_field = wasm_on_header_field,
.on_header_value = wasm_on_header_value,
.on_headers_complete = wasm_on_headers_complete_wrap,
.on_body = wasm_on_body,
.on_message_complete = wasm_on_message_complete,
};
llhttp_t *llhttp_alloc(llhttp_type_t type)
{
llhttp_t *parser = malloc(sizeof(llhttp_t));
llhttp_init(parser, type, &wasm_settings);
return parser;
}
void llhttp_free(llhttp_t *parser)
{
free(parser);
}
#endif // defined(__wasm__)
/* Some getters required to get stuff from the parser */
uint8_t llhttp_get_type(llhttp_t *parser)
{
return parser->type;
}
uint8_t llhttp_get_http_major(llhttp_t *parser)
{
return parser->http_major;
}
uint8_t llhttp_get_http_minor(llhttp_t *parser)
{
return parser->http_minor;
}
uint8_t llhttp_get_method(llhttp_t *parser)
{
return parser->method;
}
int llhttp_get_status_code(llhttp_t *parser)
{
return parser->status_code;
}
uint8_t llhttp_get_upgrade(llhttp_t *parser)
{
return parser->upgrade;
}
void llhttp_reset(llhttp_t *parser)
{
llhttp_type_t type = (llhttp_type_t)parser->type;
const llhttp_settings_t *settings = (const llhttp_settings_t *)parser->settings;
void *data = parser->data;
uint16_t lenient_flags = parser->lenient_flags;
llhttp__internal_init(parser);
parser->type = type;
parser->settings = (void *)settings;
parser->data = data;
parser->lenient_flags = lenient_flags;
}
llhttp_errno_t llhttp_execute(llhttp_t *parser, const char *data, size_t len)
{
return (llhttp_errno_t)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 (llhttp_errno_t)0;
}
switch (parser->finish) {
case HTTP_FINISH_SAFE_WITH_CB:
CALLBACK_MAYBE(parser, on_message_complete);
if (err != HPE_OK)
return (llhttp_errno_t)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 (llhttp_errno_t)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_ALL_METHOD_MAP(HTTP_METHOD_GEN)
default:
abort();
}
#undef HTTP_METHOD_GEN
}
const char *llhttp_status_name(llhttp_status_t status)
{
#define HTTP_STATUS_GEN(NUM, NAME, STRING) \
case HTTP_STATUS_##NAME: \
return #STRING;
switch (status) {
HTTP_STATUS_MAP(HTTP_STATUS_GEN)
default:
abort();
}
#undef HTTP_STATUS_GEN
}
void llhttp_set_lenient_headers(llhttp_t *parser, int enabled)
{
if (enabled) {
parser->lenient_flags |= LENIENT_HEADERS;
} else {
parser->lenient_flags &= ~LENIENT_HEADERS;
}
}
void llhttp_set_lenient_chunked_length(llhttp_t *parser, int enabled)
{
if (enabled) {
parser->lenient_flags |= LENIENT_CHUNKED_LENGTH;
} else {
parser->lenient_flags &= ~LENIENT_CHUNKED_LENGTH;
}
}
void llhttp_set_lenient_keep_alive(llhttp_t *parser, int enabled)
{
if (enabled) {
parser->lenient_flags |= LENIENT_KEEP_ALIVE;
} else {
parser->lenient_flags &= ~LENIENT_KEEP_ALIVE;
}
}
void llhttp_set_lenient_transfer_encoding(llhttp_t *parser, int enabled)
{
if (enabled) {
parser->lenient_flags |= LENIENT_TRANSFER_ENCODING;
} else {
parser->lenient_flags &= ~LENIENT_TRANSFER_ENCODING;
}
}
void llhttp_set_lenient_version(llhttp_t *parser, int enabled)
{
if (enabled) {
parser->lenient_flags |= LENIENT_VERSION;
} else {
parser->lenient_flags &= ~LENIENT_VERSION;
}
}
void llhttp_set_lenient_data_after_close(llhttp_t *parser, int enabled)
{
if (enabled) {
parser->lenient_flags |= LENIENT_DATA_AFTER_CLOSE;
} else {
parser->lenient_flags &= ~LENIENT_DATA_AFTER_CLOSE;
}
}
void llhttp_set_lenient_optional_lf_after_cr(llhttp_t *parser, int enabled)
{
if (enabled) {
parser->lenient_flags |= LENIENT_OPTIONAL_LF_AFTER_CR;
} else {
parser->lenient_flags &= ~LENIENT_OPTIONAL_LF_AFTER_CR;
}
}
void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t *parser, int enabled)
{
if (enabled) {
parser->lenient_flags |= LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;
} else {
parser->lenient_flags &= ~LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;
}
}
void llhttp_set_lenient_optional_cr_before_lf(llhttp_t *parser, int enabled)
{
if (enabled) {
parser->lenient_flags |= LENIENT_OPTIONAL_CR_BEFORE_LF;
} else {
parser->lenient_flags &= ~LENIENT_OPTIONAL_CR_BEFORE_LF;
}
}
void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t *parser, int enabled)
{
if (enabled) {
parser->lenient_flags |= LENIENT_SPACES_AFTER_CHUNK_SIZE;
} else {
parser->lenient_flags &= ~LENIENT_SPACES_AFTER_CHUNK_SIZE;
}
}
/* Callbacks */
int llhttp__on_message_begin(llhttp_t *s, const char *p, const char *endp)
{
int err;
CALLBACK_MAYBE(s, on_message_begin);
return err;
}
int llhttp__on_protocol(llhttp_t *s, const char *p, const char *endp)
{
int err;
SPAN_CALLBACK_MAYBE(s, on_protocol, p, endp - p);
return err;
}
int llhttp__on_protocol_complete(llhttp_t *s, const char *p, const char *endp)
{
int err;
CALLBACK_MAYBE(s, on_protocol_complete);
return err;
}
int llhttp__on_url(llhttp_t *s, const char *p, const char *endp)
{
int err;
SPAN_CALLBACK_MAYBE(s, on_url, p, endp - p);
return err;
}
int llhttp__on_url_complete(llhttp_t *s, const char *p, const char *endp)
{
int err;
CALLBACK_MAYBE(s, on_url_complete);
return err;
}
int llhttp__on_status(llhttp_t *s, const char *p, const char *endp)
{
int err;
SPAN_CALLBACK_MAYBE(s, on_status, p, endp - p);
return err;
}
int llhttp__on_status_complete(llhttp_t *s, const char *p, const char *endp)
{
int err;
CALLBACK_MAYBE(s, on_status_complete);
return err;
}
int llhttp__on_method(llhttp_t *s, const char *p, const char *endp)
{
int err;
SPAN_CALLBACK_MAYBE(s, on_method, p, endp - p);
return err;
}
int llhttp__on_method_complete(llhttp_t *s, const char *p, const char *endp)
{
int err;
CALLBACK_MAYBE(s, on_method_complete);
return err;
}
int llhttp__on_version(llhttp_t *s, const char *p, const char *endp)
{
int err;
SPAN_CALLBACK_MAYBE(s, on_version, p, endp - p);
return err;
}
int llhttp__on_version_complete(llhttp_t *s, const char *p, const char *endp)
{
int err;
CALLBACK_MAYBE(s, on_version_complete);
return err;
}
int llhttp__on_header_field(llhttp_t *s, const char *p, const char *endp)
{
int err;
SPAN_CALLBACK_MAYBE(s, on_header_field, p, endp - p);
return err;
}
int llhttp__on_header_field_complete(llhttp_t *s, const char *p, const char *endp)
{
int err;
CALLBACK_MAYBE(s, on_header_field_complete);
return err;
}
int llhttp__on_header_value(llhttp_t *s, const char *p, const char *endp)
{
int err;
SPAN_CALLBACK_MAYBE(s, on_header_value, p, endp - p);
return err;
}
int llhttp__on_header_value_complete(llhttp_t *s, const char *p, const char *endp)
{
int err;
CALLBACK_MAYBE(s, on_header_value_complete);
return err;
}
int llhttp__on_headers_complete(llhttp_t *s, const char *p, const char *endp)
{
int err;
CALLBACK_MAYBE(s, on_headers_complete);
return err;
}
int llhttp__on_message_complete(llhttp_t *s, const char *p, const char *endp)
{
int err;
CALLBACK_MAYBE(s, on_message_complete);
return err;
}
int llhttp__on_body(llhttp_t *s, const char *p, const char *endp)
{
int err;
SPAN_CALLBACK_MAYBE(s, on_body, 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);
return err;
}
int llhttp__on_chunk_extension_name(llhttp_t *s, const char *p, const char *endp)
{
int err;
SPAN_CALLBACK_MAYBE(s, on_chunk_extension_name, p, endp - p);
return err;
}
int llhttp__on_chunk_extension_name_complete(llhttp_t *s, const char *p, const char *endp)
{
int err;
CALLBACK_MAYBE(s, on_chunk_extension_name_complete);
return err;
}
int llhttp__on_chunk_extension_value(llhttp_t *s, const char *p, const char *endp)
{
int err;
SPAN_CALLBACK_MAYBE(s, on_chunk_extension_value, p, endp - p);
return err;
}
int llhttp__on_chunk_extension_value_complete(llhttp_t *s, const char *p, const char *endp)
{
int err;
CALLBACK_MAYBE(s, on_chunk_extension_value_complete);
return err;
}
int llhttp__on_chunk_complete(llhttp_t *s, const char *p, const char *endp)
{
int err;
CALLBACK_MAYBE(s, on_chunk_complete);
return err;
}
int llhttp__on_reset(llhttp_t *s, const char *p, const char *endp)
{
int err;
CALLBACK_MAYBE(s, on_reset);
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);
}
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// LCOV_EXCL_STOP

View File

@ -0,0 +1,14 @@
//
// Copyright (c) 2013-2025 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#ifndef SRS_PROTOCOL_HTTP_LLHTTPAPI_HPP
#define SRS_PROTOCOL_HTTP_LLHTTPAPI_HPP
#include <srs_core.hpp>
#include <srs_protocol_http_stack_llhttp.hpp>
#endif

View File

@ -0,0 +1,233 @@
//
// Copyright (c) 2013-2025 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#include <srs_protocol_http_stack_llhttphttp.hpp>
// The llhttp is licensed under MIT, see https://github.com/nodejs/llhttp
// Copy the implementation files to bellow:
// src/native/http.c
// LCOV_EXCL_START
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
#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)) ||
/* See RFC 2616 section 4.4 - 1xx e.g. Continue */
(parser->type == HTTP_RESPONSE && parser->status_code == 101)) {
/* Exit, the rest of the message is in a different protocol. */
return 1;
}
if (parser->type == HTTP_RESPONSE && parser->status_code == 100) {
/* No body, restart as the message is complete */
return 0;
}
/* See RFC 2616 section 4.4 */
if (
parser->flags & F_SKIPBODY || /* response to a HEAD request */
(
parser->type == HTTP_RESPONSE && (parser->status_code == 102 || /* Processing */
parser->status_code == 103 || /* Early Hints */
parser->status_code == 204 || /* No Content */
parser->status_code == 304 /* Not Modified */
))) {
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->lenient_flags & LENIENT_CHUNKED_LENGTH) == 0 &&
(parser->lenient_flags & LENIENT_TRANSFER_ENCODING) == 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;
parser->flags = 0;
/* 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);
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// LCOV_EXCL_STOP

View File

@ -0,0 +1,14 @@
//
// Copyright (c) 2013-2025 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#ifndef SRS_PROTOCOL_HTTP_LLHTTPHTTP_HPP
#define SRS_PROTOCOL_HTTP_LLHTTPHTTP_HPP
#include <srs_core.hpp>
#include <srs_protocol_http_stack_llhttp.hpp>
#endif

View File

@ -537,6 +537,36 @@ VOID TEST(ProtocolHTTPTest, ClientRequest)
{
srs_error_t err;
// Normal case, with empty server.
if (true) {
MockBufferIO io;
io.append(mock_http_response3(200, "Hello, world!"));
SrsHttpParser hp;
HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_RESPONSE));
ISrsHttpMessage *msg = NULL;
HELPER_ASSERT_SUCCESS(hp.parse_message(&io, &msg));
string res;
HELPER_ASSERT_SUCCESS(msg->body_read_all(res));
EXPECT_EQ(200, msg->status_code());
EXPECT_STREQ("Hello, world!", res.c_str());
srs_freep(msg);
}
// Normal case, with specified content-length.
if (true) {
MockBufferIO io;
io.append(mock_http_response(200, "Hello, world!"));
SrsHttpParser hp;
HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_RESPONSE));
ISrsHttpMessage *msg = NULL;
HELPER_ASSERT_SUCCESS(hp.parse_message(&io, &msg));
string res;
HELPER_ASSERT_SUCCESS(msg->body_read_all(res));
EXPECT_EQ(200, msg->status_code());
EXPECT_STREQ("Hello, world!", res.c_str());
srs_freep(msg);
}
// Normal case, with chunked encoding.
if (true) {
MockBufferIO io;
@ -564,36 +594,6 @@ VOID TEST(ProtocolHTTPTest, ClientRequest)
EXPECT_STREQ("Hello!", res.c_str());
srs_freep(msg);
}
// Normal case, with specified content-length.
if (true) {
MockBufferIO io;
io.append(mock_http_response(200, "Hello, world!"));
SrsHttpParser hp;
HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_RESPONSE));
ISrsHttpMessage *msg = NULL;
HELPER_ASSERT_SUCCESS(hp.parse_message(&io, &msg));
string res;
HELPER_ASSERT_SUCCESS(msg->body_read_all(res));
EXPECT_EQ(200, msg->status_code());
EXPECT_STREQ("Hello, world!", res.c_str());
srs_freep(msg);
}
// Normal case, with empty server.
if (true) {
MockBufferIO io;
io.append(mock_http_response3(200, "Hello, world!"));
SrsHttpParser hp;
HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_RESPONSE));
ISrsHttpMessage *msg = NULL;
HELPER_ASSERT_SUCCESS(hp.parse_message(&io, &msg));
string res;
HELPER_ASSERT_SUCCESS(msg->body_read_all(res));
EXPECT_EQ(200, msg->status_code());
EXPECT_STREQ("Hello, world!", res.c_str());
srs_freep(msg);
}
}
VOID TEST(ProtocolHTTPTest, ResponseHTTPError)
@ -1100,7 +1100,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerCORS)
MockResponseWriter w;
SrsHttpMessage r(NULL, NULL);
r.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, -1);
r.set_basic(HTTP_REQUEST, HTTP_POST, (llhttp_status_t)200, -1);
HELPER_ASSERT_SUCCESS(r.set_url("/index.html", false));
SrsHttpCorsMux cs(&s);
@ -1119,7 +1119,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerCORS)
MockResponseWriter w;
SrsHttpMessage r(NULL, NULL);
r.set_basic(HTTP_REQUEST, HTTP_OPTIONS, (http_status)200, -1);
r.set_basic(HTTP_REQUEST, HTTP_OPTIONS, (llhttp_status_t)200, -1);
HELPER_ASSERT_SUCCESS(r.set_url("/index.html", false));
SrsHttpCorsMux cs(&s);
@ -1139,7 +1139,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerCORS)
MockResponseWriter w;
SrsHttpMessage r(NULL, NULL);
r.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, -1);
r.set_basic(HTTP_REQUEST, HTTP_POST, (llhttp_status_t)200, -1);
HELPER_ASSERT_SUCCESS(r.set_url("/index.html", false));
SrsHttpCorsMux cs(&s);
@ -1158,7 +1158,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerCORS)
MockResponseWriter w;
SrsHttpMessage r(NULL, NULL);
r.set_basic(HTTP_REQUEST, HTTP_OPTIONS, (http_status)200, -1);
r.set_basic(HTTP_REQUEST, HTTP_OPTIONS, (llhttp_status_t)200, -1);
HELPER_ASSERT_SUCCESS(r.set_url("/index.html", false));
SrsHttpCorsMux cs(&s);
@ -1199,7 +1199,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerAuth)
MockResponseWriter w;
SrsHttpMessage r(NULL, NULL);
r.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, -1);
r.set_basic(HTTP_REQUEST, HTTP_POST, (llhttp_status_t)200, -1);
SrsHttpHeader h;
h.set("Authorization", "Basic YWRtaW46YWRtaW4="); // admin:admin
@ -1224,7 +1224,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerAuth)
MockResponseWriter w;
SrsHttpMessage r(NULL, NULL);
r.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, -1);
r.set_basic(HTTP_REQUEST, HTTP_POST, (llhttp_status_t)200, -1);
SrsHttpHeader h;
h.set("Authorization", "Basic YWRtaW46YWRtaW4="); // admin:admin
@ -1249,7 +1249,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerAuth)
MockResponseWriter w;
SrsHttpMessage r(NULL, NULL);
r.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, -1);
r.set_basic(HTTP_REQUEST, HTTP_POST, (llhttp_status_t)200, -1);
SrsHttpHeader h;
h.set("Authorization", "Basic BasicYWRtaW46YWRtaW4="); // duplicate 'Basic'
@ -1274,7 +1274,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerAuth)
MockResponseWriter w;
SrsHttpMessage r(NULL, NULL);
r.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, -1);
r.set_basic(HTTP_REQUEST, HTTP_POST, (llhttp_status_t)200, -1);
SrsHttpHeader h;
h.set("Authorization", "YWRtaW46YWRtaW4="); // admin:admin
@ -1299,7 +1299,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerAuth)
MockResponseWriter w;
SrsHttpMessage r(NULL, NULL);
r.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, -1);
r.set_basic(HTTP_REQUEST, HTTP_POST, (llhttp_status_t)200, -1);
SrsHttpHeader h;
h.set("Authorization", "Basic admin:admin"); // admin:admin
@ -1324,7 +1324,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerAuth)
MockResponseWriter w;
SrsHttpMessage r(NULL, NULL);
r.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, -1);
r.set_basic(HTTP_REQUEST, HTTP_POST, (llhttp_status_t)200, -1);
HELPER_ASSERT_SUCCESS(r.set_url("/api/v1/clients/", false));
SrsHttpAuthMux auth(&s);
@ -1344,7 +1344,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerAuth)
MockResponseWriter w;
SrsHttpMessage r(NULL, NULL);
r.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, -1);
r.set_basic(HTTP_REQUEST, HTTP_POST, (llhttp_status_t)200, -1);
HELPER_ASSERT_SUCCESS(r.set_url("/api/v1/clients/", false));
SrsHttpAuthMux auth(&s);
@ -1364,7 +1364,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerAuth)
MockResponseWriter w;
SrsHttpMessage r(NULL, NULL);
r.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, -1);
r.set_basic(HTTP_REQUEST, HTTP_POST, (llhttp_status_t)200, -1);
SrsHttpHeader h;
h.set("Authorization", "Basic YWRtaW46YWRtaW4="); // admin:admin
@ -1389,7 +1389,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerAuth)
MockResponseWriter w;
SrsHttpMessage r(NULL, NULL);
r.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, -1);
r.set_basic(HTTP_REQUEST, HTTP_POST, (llhttp_status_t)200, -1);
SrsHttpHeader h;
h.set("Authorization", "Basic YWRtaW46YWRtaW4="); // admin:admin
@ -1414,7 +1414,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerAuth)
MockResponseWriter w;
SrsHttpMessage r(NULL, NULL);
r.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, -1);
r.set_basic(HTTP_REQUEST, HTTP_POST, (llhttp_status_t)200, -1);
SrsHttpHeader h;
h.set("Authorization", "Basic YWRtaW46YWRtaW4="); // admin:admin
@ -1864,6 +1864,48 @@ VOID TEST(ProtocolHTTPTest, HTTPMessageParser)
{
srs_error_t err;
if (true) {
MockMSegmentsReader r;
r.in_bytes.push_back("G");
r.in_bytes.push_back("ET ");
r.in_bytes.push_back("/api/v1/versions HTTP/1.1\r\n");
r.in_bytes.push_back("Host: ossrs");
r.in_bytes.push_back(".net\r");
r.in_bytes.push_back("\n");
r.in_bytes.push_back("\r\n");
SrsHttpParser p;
HELPER_ASSERT_SUCCESS(p.initialize(HTTP_REQUEST));
ISrsHttpMessage *msg = NULL;
HELPER_ASSERT_SUCCESS(p.parse_message(&r, &msg));
EXPECT_TRUE(msg->is_http_get());
EXPECT_STREQ("/api/v1/versions", msg->path().c_str());
EXPECT_STREQ("ossrs.net", msg->host().c_str());
srs_freep(msg);
}
if (true) {
MockMSegmentsReader r;
r.in_bytes.push_back("GET");
r.in_bytes.push_back(" ");
r.in_bytes.push_back("/api/v1/versions HTTP/1.1\r\n");
r.in_bytes.push_back("Host: ossrs");
r.in_bytes.push_back(".net\r");
r.in_bytes.push_back("\n");
r.in_bytes.push_back("\r\n");
SrsHttpParser p;
HELPER_ASSERT_SUCCESS(p.initialize(HTTP_REQUEST));
ISrsHttpMessage *msg = NULL;
HELPER_ASSERT_SUCCESS(p.parse_message(&r, &msg));
EXPECT_TRUE(msg->is_http_get());
EXPECT_STREQ("/api/v1/versions", msg->path().c_str());
EXPECT_STREQ("ossrs.net", msg->host().c_str());
srs_freep(msg);
}
if (true) {
MockMSegmentsReader r;
r.in_bytes.push_back("GET /api/v1/versions HTTP/1.1\r\n");
@ -2053,7 +2095,7 @@ VOID TEST(ProtocolHTTPTest, HTTPMessageUpdate)
SrsHttpMessage m;
m.set_header(&h, false);
HELPER_EXPECT_FAILED(m.set_url("/api/v1", false));
HELPER_ASSERT_SUCCESS(m.set_url("/api/v1", false));
}
// Port use 80 if error.
@ -2118,7 +2160,7 @@ VOID TEST(ProtocolHTTPTest, HTTPMessageUpdate)
SrsHttpMessage m;
m.set_header(&h, false);
m.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, 2);
m.set_basic(HTTP_REQUEST, HTTP_POST, (llhttp_status_t)200, 2);
EXPECT_EQ(10, m.content_length());
}
@ -2128,7 +2170,7 @@ VOID TEST(ProtocolHTTPTest, HTTPMessageUpdate)
h.set("Content-Length", "10");
SrsHttpMessage m;
m.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, 2);
m.set_basic(HTTP_REQUEST, HTTP_POST, (llhttp_status_t)200, 2);
m.set_header(&h, false);
EXPECT_EQ(10, m.content_length());
}
@ -2162,7 +2204,19 @@ VOID TEST(ProtocolHTTPTest, HTTPMessageUpdate)
if (true) {
SrsHttpMessage m;
m.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, 0);
m.set_basic(HTTP_REQUEST, HTTP_POST, (llhttp_status_t)200, 0);
EXPECT_EQ(-1, m.content_length());
}
if (true) {
SrsHttpHeader h;
h.set("Content-Length", "0");
SrsHttpMessage m;
m.set_header(&h, false);
EXPECT_EQ(0, m.content_length());
m.set_basic(HTTP_REQUEST, HTTP_POST, (llhttp_status_t)200, 0);
EXPECT_EQ(0, m.content_length());
}

View File

@ -7714,14 +7714,14 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt3)
}
struct MockStage {
http_parser parser;
llhttp_t parser;
const char *at;
size_t length;
MockStage(http_parser *from);
MockStage(llhttp_t *from);
};
MockStage::MockStage(http_parser *from)
MockStage::MockStage(llhttp_t *from)
{
parser = *from;
at = NULL;
@ -7731,8 +7731,8 @@ MockStage::MockStage(http_parser *from)
class MockParser
{
private:
http_parser_settings settings;
http_parser *parser;
llhttp_settings_t settings;
llhttp_t *parser;
size_t parsed;
public:
@ -7755,22 +7755,22 @@ public:
srs_error_t parse(string data);
private:
static int on_message_begin(http_parser *parser);
static int on_url(http_parser *parser, const char *at, size_t length);
static int on_status(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_message_complete(http_parser *parser);
static int on_chunk_header(http_parser *parser);
static int on_chunk_complete(http_parser *parser);
static int on_message_begin(llhttp_t *parser);
static int on_url(llhttp_t *parser, const char *at, size_t length);
static int on_status(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_message_complete(llhttp_t *parser);
static int on_chunk_header(llhttp_t *parser);
static int on_chunk_complete(llhttp_t *parser);
};
MockParser::MockParser()
{
parser = new http_parser();
http_parser_init(parser, HTTP_REQUEST);
parser = new llhttp_t();
llhttp_init(parser, HTTP_REQUEST, &settings);
parser->data = (void *)this;
parsed = 0;
@ -7814,7 +7814,7 @@ MockParser::~MockParser()
srs_freep(chunk_complete);
}
int MockParser::on_message_begin(http_parser *parser)
int MockParser::on_message_begin(llhttp_t *parser)
{
MockParser *obj = (MockParser *)parser->data;
srs_assert(obj);
@ -7825,7 +7825,7 @@ int MockParser::on_message_begin(http_parser *parser)
return 0;
}
int MockParser::on_url(http_parser *parser, const char *at, size_t length)
int MockParser::on_url(llhttp_t *parser, const char *at, size_t length)
{
MockParser *obj = (MockParser *)parser->data;
srs_assert(obj);
@ -7838,7 +7838,7 @@ int MockParser::on_url(http_parser *parser, const char *at, size_t length)
return 0;
}
int MockParser::on_status(http_parser *parser, const char *at, size_t length)
int MockParser::on_status(llhttp_t *parser, const char *at, size_t length)
{
MockParser *obj = (MockParser *)parser->data;
srs_assert(obj);
@ -7851,7 +7851,7 @@ int MockParser::on_status(http_parser *parser, const char *at, size_t length)
return 0;
}
int MockParser::on_header_field(http_parser *parser, const char *at, size_t length)
int MockParser::on_header_field(llhttp_t *parser, const char *at, size_t length)
{
MockParser *obj = (MockParser *)parser->data;
srs_assert(obj);
@ -7864,7 +7864,7 @@ int MockParser::on_header_field(http_parser *parser, const char *at, size_t leng
return 0;
}
int MockParser::on_header_value(http_parser *parser, const char *at, size_t length)
int MockParser::on_header_value(llhttp_t *parser, const char *at, size_t length)
{
MockParser *obj = (MockParser *)parser->data;
srs_assert(obj);
@ -7877,7 +7877,7 @@ int MockParser::on_header_value(http_parser *parser, const char *at, size_t leng
return 0;
}
int MockParser::on_headers_complete(http_parser *parser)
int MockParser::on_headers_complete(llhttp_t *parser)
{
MockParser *obj = (MockParser *)parser->data;
srs_assert(obj);
@ -7885,10 +7885,10 @@ int MockParser::on_headers_complete(http_parser *parser)
srs_freep(obj->headers_complete);
obj->headers_complete = new MockStage(parser);
return 0;
return HPE_PAUSED;
}
int MockParser::on_body(http_parser *parser, const char *at, size_t length)
int MockParser::on_body(llhttp_t *parser, const char *at, size_t length)
{
MockParser *obj = (MockParser *)parser->data;
srs_assert(obj);
@ -7901,7 +7901,7 @@ int MockParser::on_body(http_parser *parser, const char *at, size_t length)
return 0;
}
int MockParser::on_message_complete(http_parser *parser)
int MockParser::on_message_complete(llhttp_t *parser)
{
MockParser *obj = (MockParser *)parser->data;
srs_assert(obj);
@ -7912,7 +7912,7 @@ int MockParser::on_message_complete(http_parser *parser)
return 0;
}
int MockParser::on_chunk_header(http_parser *parser)
int MockParser::on_chunk_header(llhttp_t *parser)
{
MockParser *obj = (MockParser *)parser->data;
srs_assert(obj);
@ -7923,7 +7923,7 @@ int MockParser::on_chunk_header(http_parser *parser)
return 0;
}
int MockParser::on_chunk_complete(http_parser *parser)
int MockParser::on_chunk_complete(llhttp_t *parser)
{
MockParser *obj = (MockParser *)parser->data;
srs_assert(obj);
@ -7938,15 +7938,39 @@ srs_error_t MockParser::parse(string data)
{
srs_error_t err = srs_success;
parsed = 0;
const char *buf = (const char *)data.data();
size_t size = (size_t)data.length();
size_t nparsed = http_parser_execute(parser, &settings, buf, size);
parsed = nparsed;
llhttp_errno_t code = llhttp_execute(parser, buf, size);
if (nparsed != size) {
return srs_error_new(-1, "nparsed=%d, size=%d", nparsed, size);
ssize_t consumed = 0;
if (code == HPE_OK) {
// No problem, all buffer should be consumed.
consumed = (int)data.size();
} else if (code == HPE_PAUSED) {
// We only consume the header, not message or body.
const char *error_pos = llhttp_get_error_pos(parser);
if (error_pos && error_pos < buf) {
return srs_error_new(-1, "llhttp error_pos=%p < data_start=%p", error_pos, buf);
}
if (error_pos && error_pos >= buf) {
consumed = error_pos - buf;
}
}
// Check for errors (but allow certain conditions that are normal)
// HPE_OK: success
// HPE_PAUSED: we use to skip body
if (code != HPE_OK && code != HPE_PAUSED) {
return srs_error_new(ERROR_HTTP_PARSE_HEADER, "parse %dB, nparsed=%d, err=%d/%s %s",
(int)data.size(), (int)consumed, code, llhttp_errno_name(code),
llhttp_get_error_reason(parser) ? llhttp_get_error_reason(parser) : "");
}
parsed = consumed;
return err;
}
@ -7954,12 +7978,19 @@ VOID TEST(ProtocolHTTPTest, HTTPParser)
{
srs_error_t err;
if (true) {
MockParser parser;
// size = 79, nparsed = 0, nread = 1, content_length = -1, Header("Content-Length", 5)
HELPER_EXPECT_FAILED(parser.parse("elloGET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHello"));
EXPECT_EQ(0, (int)parser.parsed);
EXPECT_EQ(0, (int64_t)parser.parser->content_length);
}
if (true) {
MockParser parser;
// size = 70, nparsed = 70, nread = 0
HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\n"));
EXPECT_EQ(70, (int)parser.parsed);
EXPECT_EQ(0, (int)parser.parser->nread);
EXPECT_TRUE(!parser.body);
EXPECT_TRUE(parser.headers_complete);
EXPECT_TRUE(!parser.message_complete);
@ -7967,21 +7998,19 @@ VOID TEST(ProtocolHTTPTest, HTTPParser)
if (true) {
MockParser parser;
// size = 75, nparsed = 75, nread = 0
// size = 75, nparsed = 70, nread = 0
HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHello"));
EXPECT_EQ(75, (int)parser.parsed);
EXPECT_EQ(0, (int)parser.parser->nread);
EXPECT_TRUE(parser.body && 5 == parser.body->length);
EXPECT_EQ(70, (int)parser.parsed);
EXPECT_TRUE(!parser.body);
EXPECT_TRUE(parser.headers_complete);
EXPECT_TRUE(parser.message_complete);
EXPECT_TRUE(!parser.message_complete);
}
if (true) {
MockParser parser;
// size = 150, nparsed = 150, nread = 0
// size = 150, nparsed = 70, nread = 0
HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHelloGET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nWorld"));
EXPECT_EQ(150, (int)parser.parsed);
EXPECT_EQ(0, (int)parser.parser->nread);
EXPECT_EQ(70, (int)parser.parsed);
}
if (true) {
@ -7989,14 +8018,7 @@ VOID TEST(ProtocolHTTPTest, HTTPParser)
// size = 70, nparsed = 70, nread = 0, content_length = 5, Header("Content-Length", 5)
HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\n"));
EXPECT_EQ(70, (int)parser.parsed);
EXPECT_EQ(0, (int)parser.parser->nread);
EXPECT_EQ(5, (int)parser.parser->content_length);
// size = 79, nparsed = 5, nread = 1, content_length = -1, Header("Content-Length", 5)
HELPER_EXPECT_FAILED(parser.parse("elloGET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHello"));
EXPECT_EQ(5, (int)parser.parsed);
EXPECT_EQ(1, (int)parser.parser->nread);
EXPECT_EQ(-1, (int64_t)parser.parser->content_length);
}
if (true) {
@ -8004,32 +8026,31 @@ VOID TEST(ProtocolHTTPTest, HTTPParser)
// size = 70, nparsed = 70, nread = 0, content_length = 5, Header("Content-Length", 5)
HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\n"));
EXPECT_EQ(70, (int)parser.parsed);
EXPECT_EQ(0, (int)parser.parser->nread);
EXPECT_EQ(5, (int)parser.parser->content_length);
// Reset for the next message.
parser.parser->content_length = 0;
// size = 80, nparsed = 70, nread = 0, content_length = 0, Header("Content-Length", 5)
HELPER_EXPECT_SUCCESS(parser.parse("HelloGET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nWorld"));
EXPECT_EQ(80, (int)parser.parsed);
EXPECT_EQ(0, (int)parser.parser->nread);
HELPER_EXPECT_FAILED(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nWorld"));
EXPECT_EQ(0, (int)parser.parsed);
EXPECT_EQ(0, (int)parser.parser->content_length);
}
if (true) {
MockParser parser;
// size = 73, nparsed = 73, nread = 0, content_length = 2, Header("Content-Length", 5)
// size = 73, nparsed = 70, nread = 0, content_length = 5, Header("Content-Length", 5)
HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHel"));
EXPECT_EQ(73, (int)parser.parsed);
EXPECT_EQ(0, (int)parser.parser->nread);
EXPECT_EQ(2, (int)parser.parser->content_length);
EXPECT_EQ(70, (int)parser.parsed);
EXPECT_EQ(5, (int)parser.parser->content_length);
}
if (true) {
MockParser parser;
// size = 82, nparsed = 75, nread = 1, content_length = -1, Header("Content-Length", 5)
HELPER_EXPECT_FAILED(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHello World!"));
EXPECT_EQ(75, (int)parser.parsed);
EXPECT_EQ(1, (int)parser.parser->nread);
EXPECT_EQ(-1, (int64_t)parser.parser->content_length);
// size = 82, nparsed = 70, nread = 1, content_length = 5, Header("Content-Length", 5)
HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHello World!"));
EXPECT_EQ(70, (int)parser.parsed);
EXPECT_EQ(5, (int64_t)parser.parser->content_length);
}
if (true) {
@ -8037,12 +8058,10 @@ VOID TEST(ProtocolHTTPTest, HTTPParser)
// size = 34, nparsed = 34, nread = 34
HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHo"));
EXPECT_EQ(34, (int)parser.parsed);
EXPECT_EQ(34, (int)parser.parser->nread);
// size = 41, nparsed = 41, nread = 0
// size = 41, nparsed = 36, nread = 0
HELPER_EXPECT_SUCCESS(parser.parse("st: ossrs.net\r\nContent-Length: 5\r\n\r\nHello"));
EXPECT_EQ(41, (int)parser.parsed);
EXPECT_EQ(0, (int)parser.parser->nread);
EXPECT_EQ(36, (int)parser.parsed);
}
if (true) {
@ -8050,12 +8069,10 @@ VOID TEST(ProtocolHTTPTest, HTTPParser)
// size = 41, nparsed = 41, nread = 41
HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: oss"));
EXPECT_EQ(41, (int)parser.parsed);
EXPECT_EQ(41, (int)parser.parser->nread);
// size = 34, nparsed = 34, nread = 0
// size = 34, nparsed = 29, nread = 0
HELPER_EXPECT_SUCCESS(parser.parse("rs.net\r\nContent-Length: 5\r\n\r\nHello"));
EXPECT_EQ(34, (int)parser.parsed);
EXPECT_EQ(0, (int)parser.parser->nread);
EXPECT_EQ(29, (int)parser.parsed);
}
if (true) {
@ -8063,12 +8080,10 @@ VOID TEST(ProtocolHTTPTest, HTTPParser)
// size = 48, nparsed = 48, nread = 48
HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r"));
EXPECT_EQ(48, (int)parser.parsed);
EXPECT_EQ(48, (int)parser.parser->nread);
// size = 27, nparsed = 27, nread = 0
// size = 27, nparsed = 22, nread = 0
HELPER_EXPECT_SUCCESS(parser.parse("\nContent-Length: 5\r\n\r\nHello"));
EXPECT_EQ(27, (int)parser.parsed);
EXPECT_EQ(0, (int)parser.parser->nread);
EXPECT_EQ(22, (int)parser.parsed);
}
if (true) {
@ -8076,12 +8091,10 @@ VOID TEST(ProtocolHTTPTest, HTTPParser)
// size = 68, nparsed = 68, nread = 68
HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n"));
EXPECT_EQ(68, (int)parser.parsed);
EXPECT_EQ(68, (int)parser.parser->nread);
// size = 7, nparsed = 7, nread = 0
// size = 7, nparsed = 2, nread = 0
HELPER_EXPECT_SUCCESS(parser.parse("\r\nHello"));
EXPECT_EQ(7, (int)parser.parsed);
EXPECT_EQ(0, (int)parser.parser->nread);
EXPECT_EQ(2, (int)parser.parsed);
}
if (true) {
@ -8089,20 +8102,17 @@ VOID TEST(ProtocolHTTPTest, HTTPParser)
// size = 69, nparsed = 69, nread = 69
HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r"));
EXPECT_EQ(69, (int)parser.parsed);
EXPECT_EQ(69, (int)parser.parser->nread);
// size = 6, nparsed = 6, nread = 0
// size = 6, nparsed = 1, nread = 0
HELPER_EXPECT_SUCCESS(parser.parse("\nHello"));
EXPECT_EQ(6, (int)parser.parsed);
EXPECT_EQ(0, (int)parser.parser->nread);
EXPECT_EQ(1, (int)parser.parsed);
}
if (true) {
MockParser parser;
// size = 75, nparsed = 75, nread = 0
// size = 75, nparsed = 70, nread = 0
HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHello"));
EXPECT_EQ(75, (int)parser.parsed);
EXPECT_EQ(0, (int)parser.parser->nread);
EXPECT_EQ(70, (int)parser.parsed);
}
if (true) {
@ -8110,12 +8120,10 @@ VOID TEST(ProtocolHTTPTest, HTTPParser)
// nparsed = 2, size = 2, nread = 2
HELPER_EXPECT_SUCCESS(parser.parse("GE"));
EXPECT_EQ(2, (int)parser.parsed);
EXPECT_EQ(2, (int)parser.parser->nread);
// size = 0, nparsed = 1, nread=2
HELPER_EXPECT_FAILED(parser.parse(""));
EXPECT_EQ(1, (int)parser.parsed);
EXPECT_EQ(2, (int)parser.parser->nread);
HELPER_EXPECT_SUCCESS(parser.parse(""));
EXPECT_EQ(0, (int)parser.parsed);
}
if (true) {
@ -8123,12 +8131,10 @@ VOID TEST(ProtocolHTTPTest, HTTPParser)
// size = 2, nparsed = 2, nread = 2
HELPER_EXPECT_SUCCESS(parser.parse("GE"));
EXPECT_EQ(2, (int)parser.parsed);
EXPECT_EQ(2, (int)parser.parser->nread);
// size = 1, nparsed = 0, nread = 3
HELPER_EXPECT_FAILED(parser.parse("X"));
EXPECT_EQ(0, (int)parser.parsed);
EXPECT_EQ(3, (int)parser.parser->nread);
}
if (true) {
@ -8136,12 +8142,10 @@ VOID TEST(ProtocolHTTPTest, HTTPParser)
// size = 2, nparsed = 2, nread = 2
HELPER_EXPECT_SUCCESS(parser.parse("GE"));
EXPECT_EQ(2, (int)parser.parsed);
EXPECT_EQ(2, (int)parser.parser->nread);
// size = 1, nparsed = 1, nread = 3
HELPER_EXPECT_SUCCESS(parser.parse("T"));
EXPECT_EQ(1, (int)parser.parsed);
EXPECT_EQ(3, (int)parser.parser->nread);
}
if (true) {
@ -8149,7 +8153,6 @@ VOID TEST(ProtocolHTTPTest, HTTPParser)
// size = 3, nparsed = 3, nread = 3
HELPER_EXPECT_SUCCESS(parser.parse("GET"));
EXPECT_EQ(3, (int)parser.parsed);
EXPECT_EQ(3, (int)parser.parser->nread);
}
}

View File

@ -548,21 +548,19 @@ VOID TEST(HTTPServerTest, MessageConnection)
if (true) {
SrsHttpMessage m;
m.set_basic(HTTP_REQUEST, (http_method)100, (http_status)0, 0);
EXPECT_STREQ("<unknown>", m.method_str().c_str());
m.set_basic(HTTP_REQUEST, SRS_CONSTS_HTTP_GET, (http_status)0, 0);
m.set_basic(HTTP_REQUEST, SRS_CONSTS_HTTP_GET, (llhttp_status_t)0, 0);
EXPECT_EQ(SRS_CONSTS_HTTP_GET, m.method());
EXPECT_STREQ("GET", m.method_str().c_str());
m.set_basic(HTTP_REQUEST, SRS_CONSTS_HTTP_PUT, (http_status)0, 0);
m.set_basic(HTTP_REQUEST, SRS_CONSTS_HTTP_PUT, (llhttp_status_t)0, 0);
EXPECT_EQ(SRS_CONSTS_HTTP_PUT, m.method());
EXPECT_STREQ("PUT", m.method_str().c_str());
m.set_basic(HTTP_REQUEST, SRS_CONSTS_HTTP_POST, (http_status)0, 0);
m.set_basic(HTTP_REQUEST, SRS_CONSTS_HTTP_POST, (llhttp_status_t)0, 0);
EXPECT_EQ(SRS_CONSTS_HTTP_POST, m.method());
EXPECT_STREQ("POST", m.method_str().c_str());
m.set_basic(HTTP_REQUEST, SRS_CONSTS_HTTP_DELETE, (http_status)0, 0);
m.set_basic(HTTP_REQUEST, SRS_CONSTS_HTTP_DELETE, (llhttp_status_t)0, 0);
EXPECT_EQ(SRS_CONSTS_HTTP_DELETE, m.method());
EXPECT_STREQ("DELETE", m.method_str().c_str());
m.set_basic(HTTP_REQUEST, SRS_CONSTS_HTTP_OPTIONS, (http_status)0, 0);
m.set_basic(HTTP_REQUEST, SRS_CONSTS_HTTP_OPTIONS, (llhttp_status_t)0, 0);
EXPECT_EQ(SRS_CONSTS_HTTP_OPTIONS, m.method());
EXPECT_STREQ("OPTIONS", m.method_str().c_str());
}