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
|
||||
|
||||
* 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);
|
||||
}
|
||||
|
@ -178,8 +181,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***");
|
||||
|
||||
return 0;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -873,15 +893,9 @@ srs_error_t SrsHttpResponseWriter::send_header(char* data, int size)
|
|||
|
||||
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…
Add table
Add a link
Reference in a new issue