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:
parent
421ab6c3fb
commit
6720e96745
|
|
@ -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
4
trunk/configure
vendored
|
|
@ -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})
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,6 @@
|
|||
|
||||
#define VERSION_MAJOR 7
|
||||
#define VERSION_MINOR 0
|
||||
#define VERSION_REVISION 76
|
||||
#define VERSION_REVISION 77
|
||||
|
||||
#endif
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
10162
trunk/src/protocol/srs_protocol_http_stack_llhttp.cpp
Normal file
10162
trunk/src/protocol/srs_protocol_http_stack_llhttp.cpp
Normal file
File diff suppressed because it is too large
Load Diff
946
trunk/src/protocol/srs_protocol_http_stack_llhttp.hpp
Normal file
946
trunk/src/protocol/srs_protocol_http_stack_llhttp.hpp
Normal 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
|
||||
201
trunk/src/protocol/srs_protocol_http_stack_llhttpadapter.cpp
Normal file
201
trunk/src/protocol/srs_protocol_http_stack_llhttpadapter.cpp
Normal 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;
|
||||
}
|
||||
14
trunk/src/protocol/srs_protocol_http_stack_llhttpadapter.hpp
Normal file
14
trunk/src/protocol/srs_protocol_http_stack_llhttpadapter.hpp
Normal 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
|
||||
632
trunk/src/protocol/srs_protocol_http_stack_llhttpapi.cpp
Normal file
632
trunk/src/protocol/srs_protocol_http_stack_llhttpapi.cpp
Normal 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
|
||||
14
trunk/src/protocol/srs_protocol_http_stack_llhttpapi.hpp
Normal file
14
trunk/src/protocol/srs_protocol_http_stack_llhttpapi.hpp
Normal 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
|
||||
233
trunk/src/protocol/srs_protocol_http_stack_llhttphttp.cpp
Normal file
233
trunk/src/protocol/srs_protocol_http_stack_llhttphttp.cpp
Normal 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
|
||||
14
trunk/src/protocol/srs_protocol_http_stack_llhttphttp.hpp
Normal file
14
trunk/src/protocol/srs_protocol_http_stack_llhttphttp.hpp
Normal 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
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user