mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
GB28181: Refine HTTP parser to support SIP. v5.0.70
This commit is contained in:
parent
dae46a59ae
commit
1e6143e2eb
9 changed files with 456 additions and 79 deletions
|
@ -7,6 +7,7 @@ The changelog for SRS.
|
||||||
|
|
||||||
## SRS 5.0 Changelog
|
## 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, 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-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
|
* 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_MAJOR 5
|
||||||
#define VERSION_MINOR 0
|
#define VERSION_MINOR 0
|
||||||
#define VERSION_REVISION 69
|
#define VERSION_REVISION 70
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -324,6 +324,7 @@ srs_error_t SrsHttpClient::post(string path, string req, ISrsHttpMessage** ppmsg
|
||||||
path = "/";
|
path = "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: FIXME: Use SrsHttpMessageWriter, never use stringstream and headers.
|
||||||
// send POST request to uri
|
// send POST request to uri
|
||||||
// POST %s HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s
|
// POST %s HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
|
@ -28,6 +28,7 @@ SrsHttpParser::SrsHttpParser()
|
||||||
|
|
||||||
p_body_start = p_header_tail = NULL;
|
p_body_start = p_header_tail = NULL;
|
||||||
type_ = HTTP_REQUEST;
|
type_ = HTTP_REQUEST;
|
||||||
|
parsed_type_ = HTTP_BOTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsHttpParser::~SrsHttpParser()
|
SrsHttpParser::~SrsHttpParser()
|
||||||
|
@ -70,7 +71,7 @@ srs_error_t SrsHttpParser::parse_message(ISrsReader* reader, ISrsHttpMessage** p
|
||||||
|
|
||||||
*ppmsg = NULL;
|
*ppmsg = NULL;
|
||||||
|
|
||||||
// Reset request data.
|
// Reset parser data and state.
|
||||||
state = SrsHttpParseStateInit;
|
state = SrsHttpParseStateInit;
|
||||||
memset(&hp_header, 0, sizeof(http_parser));
|
memset(&hp_header, 0, sizeof(http_parser));
|
||||||
// The body that we have read from cache.
|
// 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.
|
// and the message fail.
|
||||||
// @note You can comment the bellow line, the utest will fail.
|
// @note You can comment the bellow line, the utest will fail.
|
||||||
http_parser_init(&parser, type_);
|
http_parser_init(&parser, type_);
|
||||||
|
// Reset the parsed type.
|
||||||
|
parsed_type_ = HTTP_BOTH;
|
||||||
// callback object ptr.
|
// callback object ptr.
|
||||||
parser.data = (void*)this;
|
parser.data = (void*)this;
|
||||||
|
|
||||||
|
@ -105,10 +108,10 @@ srs_error_t SrsHttpParser::parse_message(ISrsReader* reader, ISrsHttpMessage** p
|
||||||
SrsHttpMessage* msg = new SrsHttpMessage(reader, buffer);
|
SrsHttpMessage* msg = new SrsHttpMessage(reader, buffer);
|
||||||
|
|
||||||
// Initialize the basic information.
|
// 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));
|
msg->set_header(header, http_should_keep_alive(&hp_header));
|
||||||
// For HTTP response, no url.
|
// 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);
|
srs_freep(msg);
|
||||||
return srs_error_wrap(err, "set url=%s, jsonp=%d", url.c_str(), jsonp);
|
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;
|
SrsHttpParser* obj = (SrsHttpParser*)parser->data;
|
||||||
srs_assert(obj);
|
srs_assert(obj);
|
||||||
|
|
||||||
|
// Now, we start to parse HTTP message.
|
||||||
obj->state = SrsHttpParseStateStart;
|
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***");
|
srs_info("***MESSAGE BEGIN***");
|
||||||
|
|
||||||
|
@ -305,9 +312,9 @@ SrsHttpMessage::SrsHttpMessage(ISrsReader* reader, SrsFastStream* buffer) : ISrs
|
||||||
jsonp = false;
|
jsonp = false;
|
||||||
|
|
||||||
// As 0 is DELETE, so we use GET as default.
|
// 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.
|
// 200 is ok.
|
||||||
_status = SRS_CONSTS_HTTP_OK;
|
_status = (http_status)SRS_CONSTS_HTTP_OK;
|
||||||
// -1 means infinity chunked mode.
|
// -1 means infinity chunked mode.
|
||||||
_content_length = -1;
|
_content_length = -1;
|
||||||
// From HTTP/1.1, default to keep alive.
|
// From HTTP/1.1, default to keep alive.
|
||||||
|
@ -323,7 +330,7 @@ SrsHttpMessage::~SrsHttpMessage()
|
||||||
srs_freep(_uri);
|
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;
|
type_ = type;
|
||||||
_method = method;
|
_method = method;
|
||||||
|
@ -376,6 +383,11 @@ srs_error_t SrsHttpMessage::set_url(string url, bool allow_jsonp)
|
||||||
host = srs_get_public_internet_address(true);
|
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()) {
|
if (!host.empty()) {
|
||||||
uri = "http://" + host + _url;
|
uri = "http://" + host + _url;
|
||||||
}
|
}
|
||||||
|
@ -425,6 +437,11 @@ string SrsHttpMessage::schema()
|
||||||
return schema_;
|
return schema_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t SrsHttpMessage::message_type()
|
||||||
|
{
|
||||||
|
return type_;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t SrsHttpMessage::method()
|
uint8_t SrsHttpMessage::method()
|
||||||
{
|
{
|
||||||
if (jsonp && !jsonp_method.empty()) {
|
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;
|
skt = io;
|
||||||
hdr = new SrsHttpHeader();
|
hdr = new SrsHttpHeader();
|
||||||
header_wrote = false;
|
header_wrote_ = false;
|
||||||
status = SRS_CONSTS_HTTP_OK;
|
|
||||||
content_length = -1;
|
content_length = -1;
|
||||||
written = 0;
|
written = 0;
|
||||||
header_sent = false;
|
header_sent = false;
|
||||||
nb_iovss_cache = 0;
|
nb_iovss_cache = 0;
|
||||||
iovss_cache = NULL;
|
iovss_cache = NULL;
|
||||||
hf = NULL;
|
hf_ = NULL;
|
||||||
|
flw_ = flw;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsHttpResponseWriter::~SrsHttpResponseWriter()
|
SrsHttpMessageWriter::~SrsHttpMessageWriter()
|
||||||
{
|
{
|
||||||
srs_freep(hdr);
|
srs_freep(hdr);
|
||||||
srs_freepa(iovss_cache);
|
srs_freepa(iovss_cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsHttpResponseWriter::final_request()
|
srs_error_t SrsHttpMessageWriter::final_request()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
// write the header data in memory.
|
// write the header data in memory.
|
||||||
if (!header_wrote) {
|
if (!header_wrote_) {
|
||||||
write_header(SRS_CONSTS_HTTP_OK);
|
flw_->write_default_header();
|
||||||
}
|
}
|
||||||
|
|
||||||
// whatever header is wrote, we should try to send 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);
|
return write(NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsHttpHeader* SrsHttpResponseWriter::header()
|
SrsHttpHeader* SrsHttpMessageWriter::header()
|
||||||
{
|
{
|
||||||
return hdr;
|
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;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
// write the header data in memory.
|
// write the header data in memory.
|
||||||
if (!header_wrote) {
|
if (!header_wrote_) {
|
||||||
if (hdr->content_type().empty()) {
|
if (hdr->content_type().empty()) {
|
||||||
hdr->set_content_type("text/plain; charset=utf-8");
|
hdr->set_content_type("text/plain; charset=utf-8");
|
||||||
}
|
}
|
||||||
if (hdr->content_length() == -1) {
|
if (hdr->content_length() == -1) {
|
||||||
hdr->set_content_length(size);
|
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.
|
// 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;
|
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;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
// when header not ready, or not chunked, send one by one.
|
// 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;
|
ssize_t nwrite = 0;
|
||||||
for (int i = 0; i < iovcnt; i++) {
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
nwrite += iov[i].iov_len;
|
nwrite += iov[i].iov_len;
|
||||||
|
@ -848,21 +873,16 @@ srs_error_t SrsHttpResponseWriter::writev(const iovec* iov, int iovcnt, ssize_t*
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SrsHttpResponseWriter::write_header(int code)
|
void SrsHttpMessageWriter::write_header()
|
||||||
{
|
{
|
||||||
if (header_wrote) {
|
if (header_wrote_) return;
|
||||||
srs_warn("http: multiple write_header calls, code=%d", code);
|
header_wrote_ = true;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
header_wrote = true;
|
|
||||||
status = code;
|
|
||||||
|
|
||||||
// parse the content length from header.
|
// parse the content length from header.
|
||||||
content_length = hdr->content_length();
|
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;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
@ -872,16 +892,10 @@ srs_error_t SrsHttpResponseWriter::send_header(char* data, int size)
|
||||||
header_sent = true;
|
header_sent = true;
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
||||||
// status_line
|
// First line, the request line or status line.
|
||||||
ss << "HTTP/1.1 " << status << " "
|
if ((err = flw_->build_first_line(ss, data, size)) != srs_success) {
|
||||||
<< srs_generate_http_status_text(status) << SRS_HTTP_CRLF;
|
return srs_error_wrap(err, "first line");
|
||||||
|
|
||||||
// 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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// set server if not set.
|
// 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.
|
// 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");
|
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);
|
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)
|
SrsHttpResponseReader::SrsHttpResponseReader(SrsHttpMessage* msg, ISrsReader* reader, SrsFastStream* body)
|
||||||
{
|
{
|
||||||
skt = reader;
|
skt = reader;
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <srs_core.hpp>
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include <srs_protocol_http_stack.hpp>
|
#include <srs_protocol_http_stack.hpp>
|
||||||
|
|
||||||
|
@ -39,6 +40,7 @@ private:
|
||||||
std::string url;
|
std::string url;
|
||||||
SrsHttpHeader* header;
|
SrsHttpHeader* header;
|
||||||
enum http_parser_type type_;
|
enum http_parser_type type_;
|
||||||
|
enum http_parser_type parsed_type_;
|
||||||
private:
|
private:
|
||||||
// Point to the start of body.
|
// Point to the start of body.
|
||||||
const char* p_body_start;
|
const char* p_body_start;
|
||||||
|
@ -92,8 +94,8 @@ private:
|
||||||
// enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
|
// enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
|
||||||
uint8_t type_;
|
uint8_t type_;
|
||||||
// The HTTP method defined by HTTP_METHOD_MAP
|
// The HTTP method defined by HTTP_METHOD_MAP
|
||||||
uint8_t _method;
|
http_method _method;
|
||||||
uint16_t _status;
|
http_status _status;
|
||||||
int64_t _content_length;
|
int64_t _content_length;
|
||||||
private:
|
private:
|
||||||
// The http headers
|
// The http headers
|
||||||
|
@ -123,7 +125,7 @@ public:
|
||||||
public:
|
public:
|
||||||
// Set the basic information for HTTP request.
|
// 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.
|
// @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.
|
// 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.
|
// @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);
|
virtual void set_header(SrsHttpHeader* header, bool keep_alive);
|
||||||
|
@ -138,6 +140,7 @@ public:
|
||||||
public:
|
public:
|
||||||
// The schema, http or https.
|
// The schema, http or https.
|
||||||
virtual std::string schema();
|
virtual std::string schema();
|
||||||
|
virtual uint8_t message_type();
|
||||||
virtual uint8_t method();
|
virtual uint8_t method();
|
||||||
virtual uint16_t status_code();
|
virtual uint16_t status_code();
|
||||||
// The method helpers.
|
// The method helpers.
|
||||||
|
@ -198,24 +201,38 @@ public:
|
||||||
virtual srs_error_t filter(SrsHttpHeader* h) = 0;
|
virtual srs_error_t filter(SrsHttpHeader* h) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Response writer use st socket
|
class ISrsHttpFirstLineWriter
|
||||||
class SrsHttpResponseWriter : public ISrsHttpResponseWriter
|
{
|
||||||
|
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:
|
private:
|
||||||
ISrsProtocolReadWriter* skt;
|
ISrsProtocolReadWriter* skt;
|
||||||
SrsHttpHeader* hdr;
|
SrsHttpHeader* hdr;
|
||||||
// Before writing header, there is a chance to filter it,
|
// Before writing header, there is a chance to filter it,
|
||||||
// such as remove some headers or inject new.
|
// such as remove some headers or inject new.
|
||||||
ISrsHttpHeaderFilter* hf;
|
ISrsHttpHeaderFilter* hf_;
|
||||||
|
// The first line writer.
|
||||||
|
ISrsHttpFirstLineWriter* flw_;
|
||||||
private:
|
private:
|
||||||
char header_cache[SRS_HTTP_HEADER_CACHE_SIZE];
|
char header_cache[SRS_HTTP_HEADER_CACHE_SIZE];
|
||||||
iovec* iovss_cache;
|
iovec* iovss_cache;
|
||||||
int nb_iovss_cache;
|
int nb_iovss_cache;
|
||||||
private:
|
private:
|
||||||
// Reply header has been (logically) written
|
// Reply header has been (logically) written
|
||||||
bool header_wrote;
|
bool header_wrote_;
|
||||||
// The status code passed to WriteHeader
|
|
||||||
int status;
|
|
||||||
private:
|
private:
|
||||||
// The explicitly-declared Content-Length; or -1
|
// The explicitly-declared Content-Length; or -1
|
||||||
int64_t content_length;
|
int64_t content_length;
|
||||||
|
@ -227,16 +244,68 @@ private:
|
||||||
// (*response).wroteHeader, which tells only whether it was
|
// (*response).wroteHeader, which tells only whether it was
|
||||||
// logically written.
|
// logically written.
|
||||||
bool header_sent;
|
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:
|
public:
|
||||||
SrsHttpResponseWriter(ISrsProtocolReadWriter* io);
|
SrsHttpResponseWriter(ISrsProtocolReadWriter* io);
|
||||||
virtual ~SrsHttpResponseWriter();
|
virtual ~SrsHttpResponseWriter();
|
||||||
|
public:
|
||||||
|
void set_header_filter(ISrsHttpHeaderFilter* hf);
|
||||||
|
// Interface ISrsHttpResponseWriter
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t final_request();
|
virtual srs_error_t final_request();
|
||||||
virtual SrsHttpHeader* header();
|
virtual SrsHttpHeader* header();
|
||||||
virtual srs_error_t write(char* data, int size);
|
virtual srs_error_t write(char* data, int size);
|
||||||
virtual srs_error_t writev(const iovec* iov, int iovcnt, ssize_t* pnwrite);
|
virtual srs_error_t writev(const iovec* iov, int iovcnt, ssize_t* pnwrite);
|
||||||
virtual void write_header(int code);
|
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.
|
// Response reader use st socket.
|
||||||
|
|
|
@ -255,6 +255,14 @@ ISrsHttpResponseReader::~ISrsHttpResponseReader()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ISrsHttpRequestWriter::ISrsHttpRequestWriter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsHttpRequestWriter::~ISrsHttpRequestWriter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
ISrsHttpHandler::ISrsHttpHandler()
|
ISrsHttpHandler::ISrsHttpHandler()
|
||||||
{
|
{
|
||||||
entry = NULL;
|
entry = NULL;
|
||||||
|
@ -1696,6 +1704,9 @@ enum state
|
||||||
, s_res_HT
|
, s_res_HT
|
||||||
, s_res_HTT
|
, s_res_HTT
|
||||||
, s_res_HTTP
|
, 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_major
|
||||||
, s_res_http_dot
|
, s_res_http_dot
|
||||||
, s_res_http_minor
|
, s_res_http_minor
|
||||||
|
@ -1728,6 +1739,9 @@ enum state
|
||||||
, s_req_http_HTTP
|
, s_req_http_HTTP
|
||||||
, s_req_http_I
|
, s_req_http_I
|
||||||
, s_req_http_IC
|
, 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_major
|
||||||
, s_req_http_dot
|
, s_req_http_dot
|
||||||
, s_req_http_minor
|
, s_req_http_minor
|
||||||
|
@ -2143,6 +2157,11 @@ reexecute:
|
||||||
if (ch == 'H') {
|
if (ch == 'H') {
|
||||||
UPDATE_STATE(s_res_or_resp_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);
|
CALLBACK_NOTIFY(message_begin);
|
||||||
} else {
|
} else {
|
||||||
parser->type = HTTP_REQUEST;
|
parser->type = HTTP_REQUEST;
|
||||||
|
@ -2179,6 +2198,8 @@ reexecute:
|
||||||
|
|
||||||
if (ch == 'H') {
|
if (ch == 'H') {
|
||||||
UPDATE_STATE(s_res_H);
|
UPDATE_STATE(s_res_H);
|
||||||
|
} else if (ch == 'S') { /* SIP https://www.ietf.org/rfc/rfc3261.html */
|
||||||
|
UPDATE_STATE(s_res_S);
|
||||||
} else {
|
} else {
|
||||||
SET_ERRNO(HPE_INVALID_CONSTANT);
|
SET_ERRNO(HPE_INVALID_CONSTANT);
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -2208,6 +2229,24 @@ reexecute:
|
||||||
UPDATE_STATE(s_res_http_major);
|
UPDATE_STATE(s_res_http_major);
|
||||||
break;
|
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:
|
case s_res_http_major:
|
||||||
if (UNLIKELY(!IS_NUM(ch))) {
|
if (UNLIKELY(!IS_NUM(ch))) {
|
||||||
SET_ERRNO(HPE_INVALID_VERSION);
|
SET_ERRNO(HPE_INVALID_VERSION);
|
||||||
|
@ -2342,20 +2381,21 @@ reexecute:
|
||||||
parser->method = (enum http_method) 0;
|
parser->method = (enum http_method) 0;
|
||||||
parser->index = 1;
|
parser->index = 1;
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case 'A': parser->method = HTTP_ACL; break;
|
case 'A': parser->method = HTTP_ACL; /* or ACK */ break;
|
||||||
case 'B': parser->method = HTTP_BIND; break;
|
case 'B': parser->method = HTTP_BIND; /* or BYE */ break;
|
||||||
case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
|
case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
|
||||||
case 'D': parser->method = HTTP_DELETE; break;
|
case 'D': parser->method = HTTP_DELETE; break;
|
||||||
case 'G': parser->method = HTTP_GET; break;
|
case 'G': parser->method = HTTP_GET; break;
|
||||||
case 'H': parser->method = HTTP_HEAD; 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 '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 'N': parser->method = HTTP_NOTIFY; break;
|
||||||
case 'O': parser->method = HTTP_OPTIONS; break;
|
case 'O': parser->method = HTTP_OPTIONS; break;
|
||||||
case 'P': parser->method = HTTP_POST;
|
case 'P': parser->method = HTTP_POST;
|
||||||
/* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
|
/* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
|
||||||
break;
|
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 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH, SOURCE */ break;
|
||||||
case 'T': parser->method = HTTP_TRACE; break;
|
case 'T': parser->method = HTTP_TRACE; break;
|
||||||
case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ 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, 'A', PATCH)
|
||||||
XX(POST, 1, 'R', PROPFIND)
|
XX(POST, 1, 'R', PROPFIND)
|
||||||
XX(PUT, 2, 'R', PURGE)
|
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, 1, 'H', CHECKOUT)
|
||||||
XX(CONNECT, 2, 'P', COPY)
|
XX(CONNECT, 2, 'P', COPY)
|
||||||
XX(MKCOL, 1, 'O', MOVE)
|
XX(MKCOL, 1, 'O', MOVE)
|
||||||
|
@ -2401,9 +2443,11 @@ reexecute:
|
||||||
XX(MKCOL, 1, '-', MSEARCH)
|
XX(MKCOL, 1, '-', MSEARCH)
|
||||||
XX(MKCOL, 2, 'A', MKACTIVITY)
|
XX(MKCOL, 2, 'A', MKACTIVITY)
|
||||||
XX(MKCOL, 3, 'A', MKCALENDAR)
|
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, 'E', SEARCH)
|
||||||
XX(SUBSCRIBE, 1, 'O', SOURCE)
|
XX(SUBSCRIBE, 1, 'O', SOURCE)
|
||||||
XX(REPORT, 2, 'B', REBIND)
|
XX(REPORT, 2, 'B', REBIND)
|
||||||
|
XX(REPORT, 2, 'G', REGISTER) /* SIP https://www.ietf.org/rfc/rfc3261.html */
|
||||||
XX(PROPFIND, 4, 'P', PROPPATCH)
|
XX(PROPFIND, 4, 'P', PROPPATCH)
|
||||||
XX(LOCK, 1, 'I', LINK)
|
XX(LOCK, 1, 'I', LINK)
|
||||||
XX(UNLOCK, 2, 'S', UNSUBSCRIBE)
|
XX(UNLOCK, 2, 'S', UNSUBSCRIBE)
|
||||||
|
@ -2432,6 +2476,11 @@ reexecute:
|
||||||
UPDATE_STATE(s_req_server_start);
|
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));
|
UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
|
||||||
if (UNLIKELY(CURRENT_STATE() == s_dead)) {
|
if (UNLIKELY(CURRENT_STATE() == s_dead)) {
|
||||||
SET_ERRNO(HPE_INVALID_URL);
|
SET_ERRNO(HPE_INVALID_URL);
|
||||||
|
@ -2503,6 +2552,9 @@ reexecute:
|
||||||
case 'H':
|
case 'H':
|
||||||
UPDATE_STATE(s_req_http_H);
|
UPDATE_STATE(s_req_http_H);
|
||||||
break;
|
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':
|
case 'I':
|
||||||
if (parser->method == HTTP_SOURCE) {
|
if (parser->method == HTTP_SOURCE) {
|
||||||
UPDATE_STATE(s_req_http_I);
|
UPDATE_STATE(s_req_http_I);
|
||||||
|
@ -2545,6 +2597,24 @@ reexecute:
|
||||||
UPDATE_STATE(s_req_http_major);
|
UPDATE_STATE(s_req_http_major);
|
||||||
break;
|
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:
|
case s_req_http_major:
|
||||||
if (UNLIKELY(!IS_NUM(ch))) {
|
if (UNLIKELY(!IS_NUM(ch))) {
|
||||||
SET_ERRNO(HPE_INVALID_VERSION);
|
SET_ERRNO(HPE_INVALID_VERSION);
|
||||||
|
|
|
@ -207,6 +207,72 @@ public:
|
||||||
virtual bool eof() = 0;
|
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
|
// Objects implementing the Handler interface can be
|
||||||
// registered to serve a particular path or subtree
|
// registered to serve a particular path or subtree
|
||||||
// in the HTTP server.
|
// in the HTTP server.
|
||||||
|
@ -455,6 +521,7 @@ public:
|
||||||
ISrsHttpMessage();
|
ISrsHttpMessage();
|
||||||
virtual ~ISrsHttpMessage();
|
virtual ~ISrsHttpMessage();
|
||||||
public:
|
public:
|
||||||
|
virtual uint8_t message_type() = 0;
|
||||||
virtual uint8_t method() = 0;
|
virtual uint8_t method() = 0;
|
||||||
virtual uint16_t status_code() = 0;
|
virtual uint16_t status_code() = 0;
|
||||||
// Method helpers.
|
// Method helpers.
|
||||||
|
@ -783,6 +850,12 @@ enum http_status
|
||||||
XX(32, UNLINK, UNLINK) \
|
XX(32, UNLINK, UNLINK) \
|
||||||
/* icecast */ \
|
/* icecast */ \
|
||||||
XX(33, SOURCE, SOURCE) \
|
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
|
enum http_method
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,20 +19,6 @@ using namespace std;
|
||||||
#include <srs_protocol_utility.hpp>
|
#include <srs_protocol_utility.hpp>
|
||||||
#include <srs_core_autofree.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()
|
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 MockMSegmentsReader::read(void* buf, size_t size, ssize_t* nread)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
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()
|
MockResponseWriter::MockResponseWriter()
|
||||||
{
|
{
|
||||||
w = new SrsHttpResponseWriter(&io);
|
w = new SrsHttpResponseWriter(&io);
|
||||||
w->hf = this;
|
w->set_header_filter(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
MockResponseWriter::~MockResponseWriter()
|
MockResponseWriter::~MockResponseWriter()
|
||||||
|
@ -1053,7 +1044,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerCORS)
|
||||||
|
|
||||||
MockResponseWriter w;
|
MockResponseWriter w;
|
||||||
SrsHttpMessage r(NULL, NULL);
|
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));
|
HELPER_ASSERT_SUCCESS(r.set_url("/index.html", false));
|
||||||
|
|
||||||
SrsHttpCorsMux cs;
|
SrsHttpCorsMux cs;
|
||||||
|
@ -1073,7 +1064,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerCORS)
|
||||||
|
|
||||||
MockResponseWriter w;
|
MockResponseWriter w;
|
||||||
SrsHttpMessage r(NULL, NULL);
|
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));
|
HELPER_ASSERT_SUCCESS(r.set_url("/index.html", false));
|
||||||
|
|
||||||
SrsHttpCorsMux cs;
|
SrsHttpCorsMux cs;
|
||||||
|
@ -1093,7 +1084,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerCORS)
|
||||||
|
|
||||||
MockResponseWriter w;
|
MockResponseWriter w;
|
||||||
SrsHttpMessage r(NULL, NULL);
|
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));
|
HELPER_ASSERT_SUCCESS(r.set_url("/index.html", false));
|
||||||
|
|
||||||
SrsHttpCorsMux cs;
|
SrsHttpCorsMux cs;
|
||||||
|
@ -1113,7 +1104,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerCORS)
|
||||||
|
|
||||||
MockResponseWriter w;
|
MockResponseWriter w;
|
||||||
SrsHttpMessage r(NULL, NULL);
|
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));
|
HELPER_ASSERT_SUCCESS(r.set_url("/index.html", false));
|
||||||
|
|
||||||
SrsHttpCorsMux cs;
|
SrsHttpCorsMux cs;
|
||||||
|
@ -1778,7 +1769,7 @@ VOID TEST(ProtocolHTTPTest, HTTPMessageUpdate)
|
||||||
|
|
||||||
SrsHttpMessage m;
|
SrsHttpMessage m;
|
||||||
m.set_header(&h, false);
|
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());
|
EXPECT_EQ(10, m.content_length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1788,7 +1779,7 @@ VOID TEST(ProtocolHTTPTest, HTTPMessageUpdate)
|
||||||
h.set("Content-Length", "10");
|
h.set("Content-Length", "10");
|
||||||
|
|
||||||
SrsHttpMessage m;
|
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);
|
m.set_header(&h, false);
|
||||||
EXPECT_EQ(10, m.content_length());
|
EXPECT_EQ(10, m.content_length());
|
||||||
}
|
}
|
||||||
|
@ -1822,7 +1813,7 @@ VOID TEST(ProtocolHTTPTest, HTTPMessageUpdate)
|
||||||
|
|
||||||
if (true) {
|
if (true) {
|
||||||
SrsHttpMessage m;
|
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());
|
EXPECT_EQ(0, m.content_length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2117,6 +2108,7 @@ VOID TEST(ProtocolHTTPTest, QueryEscape)
|
||||||
VOID TEST(ProtocolHTTPTest, PathEscape)
|
VOID TEST(ProtocolHTTPTest, PathEscape)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
struct EscapeTest path[] = {
|
struct EscapeTest path[] = {
|
||||||
{"", "", srs_success},
|
{"", "", srs_success},
|
||||||
{"abc", "abc", srs_success},
|
{"abc", "abc", srs_success},
|
||||||
|
@ -2138,5 +2130,5 @@ VOID TEST(ProtocolHTTPTest, PathEscape)
|
||||||
EXPECT_STREQ(d.in.c_str(), value.c_str());
|
EXPECT_STREQ(d.in.c_str(), value.c_str());
|
||||||
EXPECT_EQ(0, memcmp(d.in.c_str(), value.c_str(), d.in.length()));
|
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);
|
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_response(int status, string content);
|
||||||
string mock_http_response2(int status, string content);
|
string mock_http_response2(int status, string content);
|
||||||
bool is_string_contain(string substr, string str);
|
bool is_string_contain(string substr, string str);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue