GB28181: Refine HTTP parser to support SIP. v5.0.70

pull/3195/head
winlin 2 years ago
parent dae46a59ae
commit 1e6143e2eb

@ -7,6 +7,7 @@ The changelog for SRS.
## SRS 5.0 Changelog
* v5.0, 2022-09-30, GB28181: Refine HTTP parser to support SIP. v5.0.70
* v5.0, 2022-09-30, Kernel: Support lazy sweeping simple GC. v5.0.69
* v5.0, 2022-09-30, HTTP: Support HTTP header in creating order. v5.0.68
* v5.0, 2022-09-27, For [#2899](https://github.com/ossrs/srs/issues/2899): API: Support exporter for Prometheus. v5.0.67

@ -9,6 +9,6 @@
#define VERSION_MAJOR 5
#define VERSION_MINOR 0
#define VERSION_REVISION 69
#define VERSION_REVISION 70
#endif

@ -324,6 +324,7 @@ srs_error_t SrsHttpClient::post(string path, string req, ISrsHttpMessage** ppmsg
path = "/";
}
// TODO: FIXME: Use SrsHttpMessageWriter, never use stringstream and headers.
// send POST request to uri
// POST %s HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s
std::stringstream ss;

@ -28,6 +28,7 @@ SrsHttpParser::SrsHttpParser()
p_body_start = p_header_tail = NULL;
type_ = HTTP_REQUEST;
parsed_type_ = HTTP_BOTH;
}
SrsHttpParser::~SrsHttpParser()
@ -70,7 +71,7 @@ srs_error_t SrsHttpParser::parse_message(ISrsReader* reader, ISrsHttpMessage** p
*ppmsg = NULL;
// Reset request data.
// Reset parser data and state.
state = SrsHttpParseStateInit;
memset(&hp_header, 0, sizeof(http_parser));
// The body that we have read from cache.
@ -93,6 +94,8 @@ srs_error_t SrsHttpParser::parse_message(ISrsReader* reader, ISrsHttpMessage** p
// and the message fail.
// @note You can comment the bellow line, the utest will fail.
http_parser_init(&parser, type_);
// Reset the parsed type.
parsed_type_ = HTTP_BOTH;
// callback object ptr.
parser.data = (void*)this;
@ -105,10 +108,10 @@ 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, hp_header.method, hp_header.status_code, hp_header.content_length);
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));
// For HTTP response, no url.
if (type_ != HTTP_RESPONSE && (err = msg->set_url(url, jsonp)) != srs_success) {
if (parsed_type_ != HTTP_RESPONSE && (err = msg->set_url(url, jsonp)) != srs_success) {
srs_freep(msg);
return srs_error_wrap(err, "set url=%s, jsonp=%d", url.c_str(), jsonp);
}
@ -177,8 +180,12 @@ int SrsHttpParser::on_message_begin(http_parser* parser)
{
SrsHttpParser* obj = (SrsHttpParser*)parser->data;
srs_assert(obj);
// Now, we start to parse HTTP message.
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;
srs_info("***MESSAGE BEGIN***");
@ -305,9 +312,9 @@ SrsHttpMessage::SrsHttpMessage(ISrsReader* reader, SrsFastStream* buffer) : ISrs
jsonp = false;
// As 0 is DELETE, so we use GET as default.
_method = SRS_CONSTS_HTTP_GET;
_method = (http_method)SRS_CONSTS_HTTP_GET;
// 200 is ok.
_status = SRS_CONSTS_HTTP_OK;
_status = (http_status)SRS_CONSTS_HTTP_OK;
// -1 means infinity chunked mode.
_content_length = -1;
// From HTTP/1.1, default to keep alive.
@ -323,7 +330,7 @@ SrsHttpMessage::~SrsHttpMessage()
srs_freep(_uri);
}
void SrsHttpMessage::set_basic(uint8_t type, uint8_t method, uint16_t status, int64_t content_length)
void SrsHttpMessage::set_basic(uint8_t type, http_method method, http_status status, int64_t content_length)
{
type_ = type;
_method = method;
@ -376,6 +383,11 @@ srs_error_t SrsHttpMessage::set_url(string url, bool allow_jsonp)
host = srs_get_public_internet_address(true);
}
// The url must starts with slash if no schema. For example, SIP request line starts with "sip".
if (!host.empty() && !srs_string_starts_with(_url, "/")) {
host += "/";
}
if (!host.empty()) {
uri = "http://" + host + _url;
}
@ -425,6 +437,11 @@ string SrsHttpMessage::schema()
return schema_;
}
uint8_t SrsHttpMessage::message_type()
{
return type_;
}
uint8_t SrsHttpMessage::method()
{
if (jsonp && !jsonp_method.empty()) {
@ -654,33 +671,41 @@ ISrsHttpHeaderFilter::~ISrsHttpHeaderFilter()
{
}
SrsHttpResponseWriter::SrsHttpResponseWriter(ISrsProtocolReadWriter* io)
ISrsHttpFirstLineWriter::ISrsHttpFirstLineWriter()
{
}
ISrsHttpFirstLineWriter::~ISrsHttpFirstLineWriter()
{
}
SrsHttpMessageWriter::SrsHttpMessageWriter(ISrsProtocolReadWriter* io, ISrsHttpFirstLineWriter* flw)
{
skt = io;
hdr = new SrsHttpHeader();
header_wrote = false;
status = SRS_CONSTS_HTTP_OK;
header_wrote_ = false;
content_length = -1;
written = 0;
header_sent = false;
nb_iovss_cache = 0;
iovss_cache = NULL;
hf = NULL;
hf_ = NULL;
flw_ = flw;
}
SrsHttpResponseWriter::~SrsHttpResponseWriter()
SrsHttpMessageWriter::~SrsHttpMessageWriter()
{
srs_freep(hdr);
srs_freepa(iovss_cache);
}
srs_error_t SrsHttpResponseWriter::final_request()
srs_error_t SrsHttpMessageWriter::final_request()
{
srs_error_t err = srs_success;
// write the header data in memory.
if (!header_wrote) {
write_header(SRS_CONSTS_HTTP_OK);
if (!header_wrote_) {
flw_->write_default_header();
}
// whatever header is wrote, we should try to send header.
@ -700,24 +725,24 @@ srs_error_t SrsHttpResponseWriter::final_request()
return write(NULL, 0);
}
SrsHttpHeader* SrsHttpResponseWriter::header()
SrsHttpHeader* SrsHttpMessageWriter::header()
{
return hdr;
}
srs_error_t SrsHttpResponseWriter::write(char* data, int size)
srs_error_t SrsHttpMessageWriter::write(char* data, int size)
{
srs_error_t err = srs_success;
// write the header data in memory.
if (!header_wrote) {
if (!header_wrote_) {
if (hdr->content_type().empty()) {
hdr->set_content_type("text/plain; charset=utf-8");
}
if (hdr->content_length() == -1) {
hdr->set_content_length(size);
}
write_header(SRS_CONSTS_HTTP_OK);
flw_->write_default_header();
}
// whatever header is wrote, we should try to send header.
@ -765,12 +790,12 @@ srs_error_t SrsHttpResponseWriter::write(char* data, int size)
return err;
}
srs_error_t SrsHttpResponseWriter::writev(const iovec* iov, int iovcnt, ssize_t* pnwrite)
srs_error_t SrsHttpMessageWriter::writev(const iovec* iov, int iovcnt, ssize_t* pnwrite)
{
srs_error_t err = srs_success;
// when header not ready, or not chunked, send one by one.
if (!header_wrote || content_length != -1) {
if (!header_wrote_ || content_length != -1) {
ssize_t nwrite = 0;
for (int i = 0; i < iovcnt; i++) {
nwrite += iov[i].iov_len;
@ -848,21 +873,16 @@ srs_error_t SrsHttpResponseWriter::writev(const iovec* iov, int iovcnt, ssize_t*
return err;
}
void SrsHttpResponseWriter::write_header(int code)
void SrsHttpMessageWriter::write_header()
{
if (header_wrote) {
srs_warn("http: multiple write_header calls, code=%d", code);
return;
}
header_wrote = true;
status = code;
if (header_wrote_) return;
header_wrote_ = true;
// parse the content length from header.
content_length = hdr->content_length();
}
srs_error_t SrsHttpResponseWriter::send_header(char* data, int size)
srs_error_t SrsHttpMessageWriter::send_header(char* data, int size)
{
srs_error_t err = srs_success;
@ -872,16 +892,10 @@ srs_error_t SrsHttpResponseWriter::send_header(char* data, int size)
header_sent = true;
std::stringstream ss;
// status_line
ss << "HTTP/1.1 " << status << " "
<< srs_generate_http_status_text(status) << SRS_HTTP_CRLF;
// detect content type
if (srs_go_http_body_allowd(status)) {
if (data && hdr->content_type().empty()) {
hdr->set_content_type(srs_go_http_detect(data, size));
}
// First line, the request line or status line.
if ((err = flw_->build_first_line(ss, data, size)) != srs_success) {
return srs_error_wrap(err, "first line");
}
// set server if not set.
@ -900,7 +914,7 @@ srs_error_t SrsHttpResponseWriter::send_header(char* data, int size)
}
// Filter the header before writing it.
if (hf && ((err = hf->filter(hdr)) != srs_success)) {
if (hf_ && ((err = hf_->filter(hdr)) != srs_success)) {
return srs_error_wrap(err, "filter header");
}
@ -914,6 +928,151 @@ srs_error_t SrsHttpResponseWriter::send_header(char* data, int size)
return skt->write((void*)buf.c_str(), buf.length(), NULL);
}
bool SrsHttpMessageWriter::header_wrote()
{
return header_wrote_;
}
void SrsHttpMessageWriter::set_header_filter(ISrsHttpHeaderFilter* hf)
{
hf_ = hf;
}
SrsHttpResponseWriter::SrsHttpResponseWriter(ISrsProtocolReadWriter* io)
{
writer_ = new SrsHttpMessageWriter(io, this);
status = SRS_CONSTS_HTTP_OK;
}
SrsHttpResponseWriter::~SrsHttpResponseWriter()
{
srs_freep(writer_);
}
void SrsHttpResponseWriter::set_header_filter(ISrsHttpHeaderFilter* hf)
{
writer_->set_header_filter(hf);
}
srs_error_t SrsHttpResponseWriter::final_request()
{
return writer_->final_request();
}
SrsHttpHeader* SrsHttpResponseWriter::header()
{
return writer_->header();
}
srs_error_t SrsHttpResponseWriter::write(char* data, int size)
{
return writer_->write(data, size);
}
srs_error_t SrsHttpResponseWriter::writev(const iovec* iov, int iovcnt, ssize_t* pnwrite)
{
return writer_->writev(iov, iovcnt, pnwrite);
}
void SrsHttpResponseWriter::write_header(int code)
{
if (writer_->header_wrote()) {
srs_warn("http: multiple write_header calls, status=%d, code=%d", status, code);
return;
}
status = code;
return writer_->write_header();
}
srs_error_t SrsHttpResponseWriter::build_first_line(std::stringstream& ss, char* data, int size)
{
srs_error_t err = srs_success;
// Write status line for response.
ss << "HTTP/1.1 " << status << " " << srs_generate_http_status_text(status) << SRS_HTTP_CRLF;
// Try to detect content type from response body data.
SrsHttpHeader* hdr = writer_->header();
if (srs_go_http_body_allowd(status)) {
if (data && hdr->content_type().empty()) {
hdr->set_content_type(srs_go_http_detect(data, size));
}
}
return err;
}
void SrsHttpResponseWriter::write_default_header()
{
write_header(SRS_CONSTS_HTTP_OK);
}
SrsHttpRequestWriter::SrsHttpRequestWriter(ISrsProtocolReadWriter* io)
{
writer_ = new SrsHttpMessageWriter(io, this);
method_ = NULL;
path_ = NULL;
}
SrsHttpRequestWriter::~SrsHttpRequestWriter()
{
srs_freep(writer_);
}
srs_error_t SrsHttpRequestWriter::final_request()
{
return writer_->final_request();
}
SrsHttpHeader* SrsHttpRequestWriter::header()
{
return writer_->header();
}
srs_error_t SrsHttpRequestWriter::write(char* data, int size)
{
return writer_->write(data, size);
}
srs_error_t SrsHttpRequestWriter::writev(const iovec* iov, int iovcnt, ssize_t* pnwrite)
{
return writer_->writev(iov, iovcnt, pnwrite);
}
void SrsHttpRequestWriter::write_header(const char* method, const char* path)
{
if (writer_->header_wrote()) {
srs_warn("http: multiple write_header calls, current=%s(%s), now=%s(%s)", method_, path_, method, path);
return;
}
method_ = method;
path_ = path;
return writer_->write_header();
}
srs_error_t SrsHttpRequestWriter::build_first_line(std::stringstream& ss, char* data, int size)
{
srs_error_t err = srs_success;
// Write status line for response.
ss << method_ << " " << path_ << " HTTP/1.1" << SRS_HTTP_CRLF;
// Try to detect content type from request body data.
SrsHttpHeader* hdr = writer_->header();
if (data && hdr->content_type().empty()) {
hdr->set_content_type(srs_go_http_detect(data, size));
}
return err;
}
void SrsHttpRequestWriter::write_default_header()
{
write_header("GET", "/");
}
SrsHttpResponseReader::SrsHttpResponseReader(SrsHttpMessage* msg, ISrsReader* reader, SrsFastStream* body)
{
skt = reader;

@ -10,6 +10,7 @@
#include <srs_core.hpp>
#include <string>
#include <sstream>
#include <srs_protocol_http_stack.hpp>
@ -39,6 +40,7 @@ private:
std::string url;
SrsHttpHeader* header;
enum http_parser_type type_;
enum http_parser_type parsed_type_;
private:
// Point to the start of body.
const char* p_body_start;
@ -92,8 +94,8 @@ private:
// enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
uint8_t type_;
// The HTTP method defined by HTTP_METHOD_MAP
uint8_t _method;
uint16_t _status;
http_method _method;
http_status _status;
int64_t _content_length;
private:
// The http headers
@ -123,7 +125,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, uint8_t method, uint16_t status, int64_t content_length);
virtual void set_basic(uint8_t type, http_method method, http_status 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);
@ -138,6 +140,7 @@ public:
public:
// The schema, http or https.
virtual std::string schema();
virtual uint8_t message_type();
virtual uint8_t method();
virtual uint16_t status_code();
// The method helpers.
@ -198,24 +201,38 @@ public:
virtual srs_error_t filter(SrsHttpHeader* h) = 0;
};
// Response writer use st socket
class SrsHttpResponseWriter : public ISrsHttpResponseWriter
class ISrsHttpFirstLineWriter
{
public:
ISrsHttpFirstLineWriter();
virtual ~ISrsHttpFirstLineWriter();
public:
// Build first line of HTTP message to ss. Note that data with size of bytes is the body to write, which enables us
// to setup the header by detecting the body, and it might be NULL.
virtual srs_error_t build_first_line(std::stringstream& ss, char* data, int size) = 0;
// Write a default header line if user does not specify one.
virtual void write_default_header() = 0;
};
// Message writer use st socket, for writing HTTP request or response, which is only different at the first line. For
// HTTP request, the first line is RequestLine. While for HTTP response, it's StatusLine.
class SrsHttpMessageWriter
{
private:
ISrsProtocolReadWriter* skt;
SrsHttpHeader* hdr;
// Before writing header, there is a chance to filter it,
// such as remove some headers or inject new.
ISrsHttpHeaderFilter* hf;
ISrsHttpHeaderFilter* hf_;
// The first line writer.
ISrsHttpFirstLineWriter* flw_;
private:
char header_cache[SRS_HTTP_HEADER_CACHE_SIZE];
iovec* iovss_cache;
int nb_iovss_cache;
private:
// Reply header has been (logically) written
bool header_wrote;
// The status code passed to WriteHeader
int status;
bool header_wrote_;
private:
// The explicitly-declared Content-Length; or -1
int64_t content_length;
@ -227,16 +244,68 @@ private:
// (*response).wroteHeader, which tells only whether it was
// logically written.
bool header_sent;
public:
SrsHttpMessageWriter(ISrsProtocolReadWriter* io, ISrsHttpFirstLineWriter* flw);
virtual ~SrsHttpMessageWriter();
public:
virtual srs_error_t final_request();
virtual SrsHttpHeader* header();
virtual srs_error_t write(char* data, int size);
virtual srs_error_t writev(const iovec* iov, int iovcnt, ssize_t* pnwrite);
virtual void write_header();
virtual srs_error_t send_header(char* data, int size);
public:
bool header_wrote();
void set_header_filter(ISrsHttpHeaderFilter* hf);
};
// Response writer use st socket
class SrsHttpResponseWriter : public ISrsHttpResponseWriter, public ISrsHttpFirstLineWriter
{
protected:
SrsHttpMessageWriter* writer_;
// The status code passed to WriteHeader, for response only.
int status;
public:
SrsHttpResponseWriter(ISrsProtocolReadWriter* io);
virtual ~SrsHttpResponseWriter();
public:
void set_header_filter(ISrsHttpHeaderFilter* hf);
// Interface ISrsHttpResponseWriter
public:
virtual srs_error_t final_request();
virtual SrsHttpHeader* header();
virtual srs_error_t write(char* data, int size);
virtual srs_error_t writev(const iovec* iov, int iovcnt, ssize_t* pnwrite);
virtual void write_header(int code);
virtual srs_error_t send_header(char* data, int size);
// Interface ISrsHttpFirstLineWriter
public:
virtual srs_error_t build_first_line(std::stringstream& ss, char* data, int size);
virtual void write_default_header();
};
// Request writer use st socket
class SrsHttpRequestWriter : public ISrsHttpRequestWriter, public ISrsHttpFirstLineWriter
{
protected:
SrsHttpMessageWriter* writer_;
// The method and path passed to WriteHeader, for request only.
const char* method_;
const char* path_;
public:
SrsHttpRequestWriter(ISrsProtocolReadWriter* io);
virtual ~SrsHttpRequestWriter();
// Interface ISrsHttpResponseWriter
public:
virtual srs_error_t final_request();
virtual SrsHttpHeader* header();
virtual srs_error_t write(char* data, int size);
virtual srs_error_t writev(const iovec* iov, int iovcnt, ssize_t* pnwrite);
virtual void write_header(const char* method, const char* path);
// Interface ISrsHttpFirstLineWriter
public:
virtual srs_error_t build_first_line(std::stringstream& ss, char* data, int size);
virtual void write_default_header();
};
// Response reader use st socket.

@ -255,6 +255,14 @@ ISrsHttpResponseReader::~ISrsHttpResponseReader()
{
}
ISrsHttpRequestWriter::ISrsHttpRequestWriter()
{
}
ISrsHttpRequestWriter::~ISrsHttpRequestWriter()
{
}
ISrsHttpHandler::ISrsHttpHandler()
{
entry = NULL;
@ -1696,6 +1704,9 @@ enum state
, s_res_HT
, s_res_HTT
, s_res_HTTP
, s_res_S /* SIP https://www.ietf.org/rfc/rfc3261.html */
, s_res_SI /* SIP https://www.ietf.org/rfc/rfc3261.html */
, s_res_SIP /* SIP https://www.ietf.org/rfc/rfc3261.html */
, s_res_http_major
, s_res_http_dot
, s_res_http_minor
@ -1728,6 +1739,9 @@ enum state
, s_req_http_HTTP
, s_req_http_I
, s_req_http_IC
, s_req_http_S /* SIP https://www.ietf.org/rfc/rfc3261.html */
, s_req_http_SI /* SIP https://www.ietf.org/rfc/rfc3261.html */
, s_req_http_SIP /* SIP https://www.ietf.org/rfc/rfc3261.html */
, s_req_http_major
, s_req_http_dot
, s_req_http_minor
@ -2143,6 +2157,11 @@ reexecute:
if (ch == 'H') {
UPDATE_STATE(s_res_or_resp_H);
CALLBACK_NOTIFY(message_begin);
} else if (ch == 'S') { /* SIP https://www.ietf.org/rfc/rfc3261.html */
parser->type = HTTP_RESPONSE;
UPDATE_STATE(s_res_S);
CALLBACK_NOTIFY(message_begin);
} else {
parser->type = HTTP_REQUEST;
@ -2179,6 +2198,8 @@ reexecute:
if (ch == 'H') {
UPDATE_STATE(s_res_H);
} else if (ch == 'S') { /* SIP https://www.ietf.org/rfc/rfc3261.html */
UPDATE_STATE(s_res_S);
} else {
SET_ERRNO(HPE_INVALID_CONSTANT);
goto error;
@ -2208,6 +2229,24 @@ reexecute:
UPDATE_STATE(s_res_http_major);
break;
/* SIP https://www.ietf.org/rfc/rfc3261.html */
case s_res_S:
STRICT_CHECK(ch != 'I');
UPDATE_STATE(s_res_SI);
break;
/* SIP https://www.ietf.org/rfc/rfc3261.html */
case s_res_SI:
STRICT_CHECK(ch != 'P');
UPDATE_STATE(s_res_SIP);
break;
/* SIP https://www.ietf.org/rfc/rfc3261.html */
case s_res_SIP:
STRICT_CHECK(ch != '/');
UPDATE_STATE(s_res_http_major);
break;
case s_res_http_major:
if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_VERSION);
@ -2342,20 +2381,21 @@ reexecute:
parser->method = (enum http_method) 0;
parser->index = 1;
switch (ch) {
case 'A': parser->method = HTTP_ACL; break;
case 'B': parser->method = HTTP_BIND; break;
case 'A': parser->method = HTTP_ACL; /* or ACK */ break;
case 'B': parser->method = HTTP_BIND; /* or BYE */ break;
case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
case 'D': parser->method = HTTP_DELETE; break;
case 'G': parser->method = HTTP_GET; break;
case 'H': parser->method = HTTP_HEAD; break;
case 'I': parser->method = HTTP_INVITE; break; /* SIP https://www.ietf.org/rfc/rfc3261.html */
case 'L': parser->method = HTTP_LOCK; /* or LINK */ break;
case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR, MESSAGE */ break;
case 'N': parser->method = HTTP_NOTIFY; break;
case 'O': parser->method = HTTP_OPTIONS; break;
case 'P': parser->method = HTTP_POST;
/* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
break;
case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
case 'R': parser->method = HTTP_REPORT; /* or REBIND, REGISTER */ break;
case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH, SOURCE */ break;
case 'T': parser->method = HTTP_TRACE; break;
case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
@ -2394,6 +2434,8 @@ reexecute:
XX(POST, 1, 'A', PATCH)
XX(POST, 1, 'R', PROPFIND)
XX(PUT, 2, 'R', PURGE)
XX(ACL, 2, 'K', ACK) /* SIP https://www.ietf.org/rfc/rfc3261.html */
XX(BIND, 1, 'Y', BYE) /* SIP https://www.ietf.org/rfc/rfc3261.html */
XX(CONNECT, 1, 'H', CHECKOUT)
XX(CONNECT, 2, 'P', COPY)
XX(MKCOL, 1, 'O', MOVE)
@ -2401,9 +2443,11 @@ reexecute:
XX(MKCOL, 1, '-', MSEARCH)
XX(MKCOL, 2, 'A', MKACTIVITY)
XX(MKCOL, 3, 'A', MKCALENDAR)
XX(MERGE, 2, 'S', MESSAGE) /* SIP https://www.ietf.org/rfc/rfc3261.html */
XX(SUBSCRIBE, 1, 'E', SEARCH)
XX(SUBSCRIBE, 1, 'O', SOURCE)
XX(REPORT, 2, 'B', REBIND)
XX(REPORT, 2, 'G', REGISTER) /* SIP https://www.ietf.org/rfc/rfc3261.html */
XX(PROPFIND, 4, 'P', PROPPATCH)
XX(LOCK, 1, 'I', LINK)
XX(UNLOCK, 2, 'S', UNSUBSCRIBE)
@ -2432,6 +2476,11 @@ reexecute:
UPDATE_STATE(s_req_server_start);
}
/* SIP https://www.ietf.org/rfc/rfc3261.html */
if (parser->method >= HTTP_REGISTER && parser->method <= HTTP_BYE) {
UPDATE_STATE(s_req_path);
}
UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
if (UNLIKELY(CURRENT_STATE() == s_dead)) {
SET_ERRNO(HPE_INVALID_URL);
@ -2503,6 +2552,9 @@ reexecute:
case 'H':
UPDATE_STATE(s_req_http_H);
break;
case 'S': /* SIP https://www.ietf.org/rfc/rfc3261.html */
UPDATE_STATE(s_req_http_S); /* SIP https://www.ietf.org/rfc/rfc3261.html */
break; /* SIP https://www.ietf.org/rfc/rfc3261.html */
case 'I':
if (parser->method == HTTP_SOURCE) {
UPDATE_STATE(s_req_http_I);
@ -2545,6 +2597,24 @@ reexecute:
UPDATE_STATE(s_req_http_major);
break;
/* SIP https://www.ietf.org/rfc/rfc3261.html */
case s_req_http_S:
STRICT_CHECK(ch != 'I');
UPDATE_STATE(s_req_http_SI);
break;
/* SIP https://www.ietf.org/rfc/rfc3261.html */
case s_req_http_SI:
STRICT_CHECK(ch != 'P');
UPDATE_STATE(s_req_http_SIP);
break;
/* SIP https://www.ietf.org/rfc/rfc3261.html */
case s_req_http_SIP:
STRICT_CHECK(ch != '/');
UPDATE_STATE(s_req_http_major);
break;
case s_req_http_major:
if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_VERSION);

@ -207,6 +207,72 @@ public:
virtual bool eof() = 0;
};
// A RequestWriter interface is used by an HTTP handler to
// construct an HTTP request.
// Usage 0, request with a message once:
// ISrsHttpRequestWriter* w; // create or get request.
// std::string msg = "Hello, HTTP!";
// w->write((char*)msg.data(), (int)msg.length());
// Usage 1, request with specified length content, same to #0:
// ISrsHttpRequestWriter* w; // create or get request.
// std::string msg = "Hello, HTTP!";
// w->header()->set_content_type("text/plain; charset=utf-8");
// w->header()->set_content_length(msg.length());
// w->write_header("POST", "/");
// w->write((char*)msg.data(), (int)msg.length()); // write N times, N>0
// w->final_request(); // optional flush.
// Usage 2, request with HTTP code only, zero content length.
// ISrsHttpRequestWriter* w; // create or get request.
// w->header()->set_content_length(0);
// w->write_header("GET", "/");
// w->final_request();
// Usage 3, request in chunked encoding.
// ISrsHttpRequestWriter* w; // create or get request.
// std::string msg = "Hello, HTTP!";
// w->header()->set_content_type("application/octet-stream");
// w->write_header("POST", "/");
// w->write((char*)msg.data(), (int)msg.length());
// w->write((char*)msg.data(), (int)msg.length());
// w->write((char*)msg.data(), (int)msg.length());
// w->write((char*)msg.data(), (int)msg.length());
// w->final_request(); // required to end the chunked and flush.
class ISrsHttpRequestWriter
{
public:
ISrsHttpRequestWriter();
virtual ~ISrsHttpRequestWriter();
public:
// When chunked mode,
// final the request to complete the chunked encoding.
// For no-chunked mode,
// final to send request, for example, content-length is 0.
virtual srs_error_t final_request() = 0;
// Header returns the header map that will be sent by WriteHeader.
// Changing the header after a call to WriteHeader (or Write) has
// no effect.
virtual SrsHttpHeader* header() = 0;
// Write writes the data to the connection as part of an HTTP reply.
// If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK)
// before writing the data. If the Header does not contain a
// Content-Type line, Write adds a Content-Type set to the result of passing
// The initial 512 bytes of written data to DetectContentType.
// @param data, the data to send. NULL to flush header only.
virtual srs_error_t write(char* data, int size) = 0;
// for the HTTP FLV, to writev to improve performance.
// @see https://github.com/ossrs/srs/issues/405
virtual srs_error_t writev(const iovec* iov, int iovcnt, ssize_t* pnwrite) = 0;
// WriteHeader sends an HTTP request header with status code.
// If WriteHeader is not called explicitly, the first call to Write
// will trigger an implicit WriteHeader(http.StatusOK).
// Thus explicit calls to WriteHeader are mainly used to
// send error codes.
// @remark, user must set header then write or write_header.
virtual void write_header(const char* method, const char* path) = 0;
};
// Objects implementing the Handler interface can be
// registered to serve a particular path or subtree
// in the HTTP server.
@ -455,6 +521,7 @@ public:
ISrsHttpMessage();
virtual ~ISrsHttpMessage();
public:
virtual uint8_t message_type() = 0;
virtual uint8_t method() = 0;
virtual uint16_t status_code() = 0;
// Method helpers.
@ -783,6 +850,12 @@ enum http_status
XX(32, UNLINK, UNLINK) \
/* icecast */ \
XX(33, SOURCE, SOURCE) \
/* SIP https://www.ietf.org/rfc/rfc3261.html */ \
XX(34, REGISTER, REGISTER) \
XX(35, INVITE, INVITE) \
XX(36, ACK, ACK) \
XX(37, MESSAGE, MESSAGE) \
XX(38, BYE, BYE) \
enum http_method
{

@ -19,20 +19,6 @@ using namespace std;
#include <srs_protocol_utility.hpp>
#include <srs_core_autofree.hpp>
class MockMSegmentsReader : public ISrsReader
{
public:
vector<string> in_bytes;
public:
MockMSegmentsReader();
virtual ~MockMSegmentsReader();
public:
virtual void append(string b) {
in_bytes.push_back(b);
}
virtual srs_error_t read(void* buf, size_t size, ssize_t* nread);
};
MockMSegmentsReader::MockMSegmentsReader()
{
}
@ -41,6 +27,11 @@ MockMSegmentsReader::~MockMSegmentsReader()
{
}
void MockMSegmentsReader::append(string b)
{
in_bytes.push_back(b);
}
srs_error_t MockMSegmentsReader::read(void* buf, size_t size, ssize_t* nread)
{
srs_error_t err = srs_success;
@ -76,7 +67,7 @@ srs_error_t MockMSegmentsReader::read(void* buf, size_t size, ssize_t* nread)
MockResponseWriter::MockResponseWriter()
{
w = new SrsHttpResponseWriter(&io);
w->hf = this;
w->set_header_filter(this);
}
MockResponseWriter::~MockResponseWriter()
@ -1053,7 +1044,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerCORS)
MockResponseWriter w;
SrsHttpMessage r(NULL, NULL);
r.set_basic(HTTP_REQUEST, HTTP_POST, 200, -1);
r.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, -1);
HELPER_ASSERT_SUCCESS(r.set_url("/index.html", false));
SrsHttpCorsMux cs;
@ -1073,7 +1064,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerCORS)
MockResponseWriter w;
SrsHttpMessage r(NULL, NULL);
r.set_basic(HTTP_REQUEST, HTTP_OPTIONS, 200, -1);
r.set_basic(HTTP_REQUEST, HTTP_OPTIONS, (http_status)200, -1);
HELPER_ASSERT_SUCCESS(r.set_url("/index.html", false));
SrsHttpCorsMux cs;
@ -1093,7 +1084,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerCORS)
MockResponseWriter w;
SrsHttpMessage r(NULL, NULL);
r.set_basic(HTTP_REQUEST, HTTP_POST, 200, -1);
r.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, -1);
HELPER_ASSERT_SUCCESS(r.set_url("/index.html", false));
SrsHttpCorsMux cs;
@ -1113,7 +1104,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerCORS)
MockResponseWriter w;
SrsHttpMessage r(NULL, NULL);
r.set_basic(HTTP_REQUEST, HTTP_OPTIONS, 200, -1);
r.set_basic(HTTP_REQUEST, HTTP_OPTIONS, (http_status)200, -1);
HELPER_ASSERT_SUCCESS(r.set_url("/index.html", false));
SrsHttpCorsMux cs;
@ -1778,7 +1769,7 @@ VOID TEST(ProtocolHTTPTest, HTTPMessageUpdate)
SrsHttpMessage m;
m.set_header(&h, false);
m.set_basic(HTTP_REQUEST, HTTP_POST, 200, 2);
m.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, 2);
EXPECT_EQ(10, m.content_length());
}
@ -1788,7 +1779,7 @@ VOID TEST(ProtocolHTTPTest, HTTPMessageUpdate)
h.set("Content-Length", "10");
SrsHttpMessage m;
m.set_basic(HTTP_REQUEST, HTTP_POST, 200, 2);
m.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, 2);
m.set_header(&h, false);
EXPECT_EQ(10, m.content_length());
}
@ -1822,7 +1813,7 @@ VOID TEST(ProtocolHTTPTest, HTTPMessageUpdate)
if (true) {
SrsHttpMessage m;
m.set_basic(HTTP_REQUEST, HTTP_POST, 200, 0);
m.set_basic(HTTP_REQUEST, HTTP_POST, (http_status)200, 0);
EXPECT_EQ(0, m.content_length());
}
@ -2117,6 +2108,7 @@ VOID TEST(ProtocolHTTPTest, QueryEscape)
VOID TEST(ProtocolHTTPTest, PathEscape)
{
srs_error_t err = srs_success;
struct EscapeTest path[] = {
{"", "", srs_success},
{"abc", "abc", srs_success},
@ -2138,5 +2130,5 @@ VOID TEST(ProtocolHTTPTest, PathEscape)
EXPECT_STREQ(d.in.c_str(), value.c_str());
EXPECT_EQ(0, memcmp(d.in.c_str(), value.c_str(), d.in.length()));
}
}

@ -37,6 +37,18 @@ public:
virtual srs_error_t filter(SrsHttpHeader* h);
};
class MockMSegmentsReader : public ISrsReader
{
public:
std::vector<string> in_bytes;
public:
MockMSegmentsReader();
virtual ~MockMSegmentsReader();
public:
virtual void append(string b);
virtual srs_error_t read(void* buf, size_t size, ssize_t* nread);
};
string mock_http_response(int status, string content);
string mock_http_response2(int status, string content);
bool is_string_contain(string substr, string str);

Loading…
Cancel
Save