Merge branch '3.0release' into develop

min
winlin 5 years ago
commit 5e57afc11e

@ -147,6 +147,12 @@ For previous versions, please read:
## V3 changes
* v3.0, 2019-12-17, Fix HTTP CORS bug when sending response for OPTIONS. 3.0.72
* v3.0, 2019-12-17, Enhance HTTP response write for final_request.
* v3.0, 2019-12-17, Refactor HTTP stream to disconnect client when unpublish.
* v3.0, 2019-12-17, Fix HTTP-FLV and VOD-FLV conflicting bug.
* v3.0, 2019-12-17, Refactor HttpResponseWriter.write, default to single text mode.
* v3.0, 2019-12-16, For [#1042][bug #1042], add test for HTTP protocol.
* <strong>v3.0, 2019-12-13, [3.0 alpha4(3.0.71)][r3.0a4] released. 112928 lines.</strong>
* v3.0, 2019-12-12, For [#547][bug #547], [#1506][bug #1506], default hls_dts_directly to on. 3.0.71
* v3.0, 2019-12-12, SrsPacket supports converting to message, so can be sent by one API.

2
trunk/configure vendored

@ -315,7 +315,7 @@ fi
# utest, the unit-test cases of srs, base on gtest1.6
if [ $SRS_UTEST = YES ]; then
MODULE_FILES=("srs_utest" "srs_utest_amf0" "srs_utest_protocol" "srs_utest_kernel" "srs_utest_core"
"srs_utest_config" "srs_utest_protostack" "srs_utest_reload" "srs_utest_service" "srs_utest_app")
"srs_utest_config" "srs_utest_rtmp" "srs_utest_http" "srs_utest_reload" "srs_utest_service" "srs_utest_app")
ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibSSLRoot})
ModuleLibFiles=(${LibSTfile} ${LibSSLfile})
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE" "APP")

@ -907,7 +907,7 @@ SrsJsonAny* SrsConfDirective::dumps_arg0_to_str()
SrsJsonAny* SrsConfDirective::dumps_arg0_to_integer()
{
return SrsJsonAny::integer(::atol(arg0().c_str()));
return SrsJsonAny::integer(::atoll(arg0().c_str()));
}
SrsJsonAny* SrsConfDirective::dumps_arg0_to_number()

@ -181,10 +181,12 @@ srs_error_t SrsConnection::cycle()
// client close peer.
// TODO: FIXME: Only reset the error when client closed it.
if (srs_is_client_gracefully_close(srs_error_code(err))) {
if (srs_is_client_gracefully_close(err)) {
srs_warn("client disconnect peer. ret=%d", srs_error_code(err));
} else if (srs_is_server_gracefully_close(err)) {
srs_warn("server disconnect. ret=%d", srs_error_code(err));
} else {
srs_error("connect error %s", srs_error_desc(err).c_str());
srs_error("serve error %s", srs_error_desc(err).c_str());
}
srs_freep(err);

@ -664,12 +664,7 @@ srs_error_t SrsGoApiRequests::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMess
// request headers
SrsJsonObject* headers = SrsJsonAny::object();
data->set("headers", headers);
for (int i = 0; i < r->request_header_count(); i++) {
std::string key = r->request_header_key_at(i);
std::string value = r->request_header_value_at(i);
headers->set(key, SrsJsonAny::str(value.c_str()));
}
r->header()->dumps(headers);
// server informations
SrsJsonObject* server = SrsJsonAny::object();

@ -81,11 +81,9 @@ srs_error_t SrsHttpConn::do_cycle()
{
srs_error_t err = srs_success;
srs_trace("HTTP client ip=%s", ip.c_str());
// initialize parser
if ((err = parser->initialize(HTTP_REQUEST, false)) != srs_success) {
return srs_error_wrap(err, "init parser");
return srs_error_wrap(err, "init parser for %s", ip.c_str());
}
// set the recv timeout, for some clients never disconnect the connection.
@ -102,10 +100,12 @@ srs_error_t SrsHttpConn::do_cycle()
}
// process http messages.
while ((err = trd->pull()) == srs_success) {
ISrsHttpMessage* req = NULL;
for (int req_id = 0; (err = trd->pull()) == srs_success; req_id++) {
// Try to receive a message from http.
srs_trace("HTTP client ip=%s, request=%d, to=%dms", ip.c_str(), req_id, srsu2ms(SRS_HTTP_RECV_TIMEOUT));
// get a http message
ISrsHttpMessage* req = NULL;
if ((err = parser->parse_message(skt, &req)) != srs_success) {
break;
}

@ -51,8 +51,7 @@ using namespace std;
#include <srs_app_source.hpp>
#include <srs_app_server.hpp>
SrsVodStream::SrsVodStream(string root_dir)
: SrsHttpFileServer(root_dir)
SrsVodStream::SrsVodStream(string root_dir) : SrsHttpFileServer(root_dir)
{
}
@ -64,22 +63,23 @@ srs_error_t SrsVodStream::serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMe
{
srs_error_t err = srs_success;
SrsFileReader fs;
SrsFileReader* fs = fs_factory->create_file_reader();
SrsAutoFree(SrsFileReader, fs);
// open flv file
if ((err = fs.open(fullpath)) != srs_success) {
if ((err = fs->open(fullpath)) != srs_success) {
return srs_error_wrap(err, "open file");
}
if (offset > fs.filesize()) {
if (offset > fs->filesize()) {
return srs_error_new(ERROR_HTTP_REMUX_OFFSET_OVERFLOW, "http flv streaming %s overflow. size=%" PRId64 ", offset=%d",
fullpath.c_str(), fs.filesize(), offset);
fullpath.c_str(), fs->filesize(), offset);
}
SrsFlvVodStreamDecoder ffd;
// open fast decoder
if ((err = ffd.initialize(&fs)) != srs_success) {
if ((err = ffd.initialize(fs)) != srs_success) {
return srs_error_wrap(err, "init ffd");
}
@ -107,16 +107,17 @@ srs_error_t SrsVodStream::serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMe
}
sh_data = new char[sh_size];
SrsAutoFreeA(char, sh_data);
if ((err = fs.read(sh_data, sh_size, NULL)) != srs_success) {
if ((err = fs->read(sh_data, sh_size, NULL)) != srs_success) {
return srs_error_wrap(err, "fs read");
}
// seek to data offset
int64_t left = fs.filesize() - offset;
int64_t left = fs->filesize() - offset;
// write http header for ts.
w->header()->set_content_length((int)(sizeof(flv_header) + sh_size + left));
w->header()->set_content_type("video/x-flv");
w->write_header(SRS_CONSTS_HTTP_OK);
// write flv header and sequence header.
if ((err = w->write(flv_header, sizeof(flv_header))) != srs_success) {
@ -132,7 +133,7 @@ srs_error_t SrsVodStream::serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMe
}
// send data
if ((err = copy(w, &fs, r, (int)left)) != srs_success) {
if ((err = copy(w, fs, r, (int)left)) != srs_success) {
return srs_error_wrap(err, "read flv=%s size=%d", fullpath.c_str(), left);
}
@ -146,21 +147,22 @@ srs_error_t SrsVodStream::serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMe
srs_assert(start >= 0);
srs_assert(end == -1 || end >= 0);
SrsFileReader fs;
SrsFileReader* fs = fs_factory->create_file_reader();
SrsAutoFree(SrsFileReader, fs);
// open flv file
if ((err = fs.open(fullpath)) != srs_success) {
if ((err = fs->open(fullpath)) != srs_success) {
return srs_error_wrap(err, "fs open");
}
// parse -1 to whole file.
if (end == -1) {
end = (int)fs.filesize();
end = (int)fs->filesize();
}
if (end > fs.filesize() || start > end) {
if (end > fs->filesize() || start > end) {
return srs_error_new(ERROR_HTTP_REMUX_OFFSET_OVERFLOW, "http mp4 streaming %s overflow. size=%" PRId64 ", offset=%d",
fullpath.c_str(), fs.filesize(), start);
fullpath.c_str(), fs->filesize(), start);
}
// seek to data offset, [start, end] for range.
@ -169,20 +171,19 @@ srs_error_t SrsVodStream::serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMe
// write http header for ts.
w->header()->set_content_length(left);
w->header()->set_content_type("video/mp4");
// status code 206 to make dash.as happy.
w->write_header(SRS_CONSTS_HTTP_PartialContent);
// response the content range header.
// https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Range_requests
std::stringstream content_range;
content_range << "bytes " << start << "-" << end << "/" << fs.filesize();
content_range << "bytes " << start << "-" << end << "/" << fs->filesize();
w->header()->set("Content-Range", content_range.str());
// write body.
fs.seek2(start);
fs->seek2(start);
// send data
if ((err = copy(w, &fs, r, (int)left)) != srs_success) {
if ((err = copy(w, fs, r, (int)left)) != srs_success) {
return srs_error_wrap(err, "read mp4=%s size=%d", fullpath.c_str(), left);
}

@ -541,6 +541,9 @@ srs_error_t SrsLiveStream::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMess
}
SrsAutoFree(ISrsBufferEncoder, enc);
// Enter chunked mode, because we didn't set the content-length.
w->write_header(SRS_CONSTS_HTTP_OK);
// create consumer of souce, ignore gop cache, use the audio gop cache.
SrsConsumer* consumer = NULL;
if ((err = source->create_consumer(NULL, consumer, true, true, !enc->has_cache())) != srs_success) {
@ -607,6 +610,7 @@ srs_error_t SrsLiveStream::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMess
enc->has_cache(), msgs.max);
// TODO: free and erase the disabled entry after all related connections is closed.
// TODO: FXIME: Support timeout for player, quit infinite-loop.
while (entry->enabled) {
pprint->elapse();
@ -657,7 +661,9 @@ srs_error_t SrsLiveStream::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMess
}
}
return err;
// Here, the entry is disabled by encoder un-publishing or reloading,
// so we must return a io.EOF error to disconnect the client, or the client will never quit.
return srs_error_new(ERROR_HTTP_STREAM_EOF, "Stream EOF");
}
srs_error_t SrsLiveStream::http_hooks_on_play(ISrsHttpMessage* r)
@ -1047,6 +1053,18 @@ srs_error_t SrsHttpStreamServer::hijack(ISrsHttpMessage* request, ISrsHttpHandle
}
}
// For HTTP-FLV stream, the template must have the same schema with upath.
// The template is defined in config, the mout of http stream. The upath is specified by http request path.
// If template is "[vhost]/[app]/[stream].flv", the upath should be:
// matched for "/live/livestream.flv"
// matched for "ossrs.net/live/livestream.flv"
// not-matched for "/livestream.flv", which is actually "/__defaultApp__/livestream.flv", HTTP not support default app.
// not-matched for "/live/show/livestream.flv"
string upath = request->path();
if (srs_string_count(upath, "/") != srs_string_count(entry->mount, "/")) {
return err;
}
// convert to concreate class.
SrsHttpMessage* hreq = dynamic_cast<SrsHttpMessage*>(request);
srs_assert(hreq);

@ -390,8 +390,8 @@ srs_error_t SrsRtmpConn::service_cycle()
// stream service must terminated with error, never success.
// when terminated with success, it's user required to stop.
if (srs_error_code(err) == ERROR_SUCCESS) {
srs_freep(err);
// TODO: FIXME: Support RTMP client timeout, https://github.com/ossrs/srs/issues/1134
if (err == srs_success) {
continue;
}

@ -387,10 +387,9 @@ srs_error_t SrsRtspConn::cycle()
if (err == srs_success) {
srs_trace("client finished.");
} else if (srs_is_client_gracefully_close(srs_error_code(err))) {
} else if (srs_is_client_gracefully_close(err)) {
srs_warn("client disconnect peer. code=%d", srs_error_code(err));
srs_freep(err);
err = srs_success;
}
if (video_rtp) {

@ -27,7 +27,7 @@
// The version config.
#define VERSION_MAJOR 3
#define VERSION_MINOR 0
#define VERSION_REVISION 71
#define VERSION_REVISION 72
// The macros generated by configure script.
#include <srs_auto_headers.hpp>

@ -213,7 +213,7 @@
#define SRS_CONSTS_HTTP_QUERY_SEP '?'
// The default recv timeout.
#define SRS_HTTP_RECV_TIMEOUT (60 * SRS_UTIME_SECONDS)
#define SRS_HTTP_RECV_TIMEOUT (15 * SRS_UTIME_SECONDS)
// 6.1.1 Status Code and Reason Phrase
#define SRS_CONSTS_HTTP_Continue 100

@ -30,30 +30,26 @@
#include <stdarg.h>
using namespace std;
bool srs_is_system_control_error(int error_code)
bool srs_is_system_control_error(srs_error_t err)
{
int error_code = srs_error_code(err);
return error_code == ERROR_CONTROL_RTMP_CLOSE
|| error_code == ERROR_CONTROL_REPUBLISH
|| error_code == ERROR_CONTROL_REDIRECT;
}
bool srs_is_system_control_error(srs_error_t err)
bool srs_is_client_gracefully_close(srs_error_t err)
{
int error_code = srs_error_code(err);
return srs_is_system_control_error(error_code);
}
bool srs_is_client_gracefully_close(int error_code)
{
return error_code == ERROR_SOCKET_READ
|| error_code == ERROR_SOCKET_READ_FULLY
|| error_code == ERROR_SOCKET_WRITE;
}
bool srs_is_client_gracefully_close(srs_error_t err)
bool srs_is_server_gracefully_close(srs_error_t err)
{
int error_code = srs_error_code(err);
return srs_is_client_gracefully_close(error_code);
int code = srs_error_code(err);
return code == ERROR_HTTP_STREAM_EOF;
}
SrsCplxError::SrsCplxError()

@ -320,6 +320,7 @@
#define ERROR_HTTP_REQUEST_EOF 4029
#define ERROR_HTTP_302_INVALID 4038
#define ERROR_BASE64_DECODE 4039
#define ERROR_HTTP_STREAM_EOF 4040
///////////////////////////////////////////////////////
// HTTP API error.
@ -336,10 +337,11 @@
// Whether the error code is an system control error.
// TODO: FIXME: Remove it from underlayer for confused with error and logger.
extern bool srs_is_system_control_error(int error_code);
extern bool srs_is_system_control_error(srs_error_t err);
extern bool srs_is_client_gracefully_close(int error_code);
// It's closed by client.
extern bool srs_is_client_gracefully_close(srs_error_t err);
// It's closed by server, such as streaming EOF.
extern bool srs_is_server_gracefully_close(srs_error_t err);
// The complex error carries code, message, callstack and instant variables,
// which is more strong and easy to locate problem by log,

@ -175,6 +175,19 @@ srs_error_t SrsFileWriter::lseek(off_t offset, int whence, off_t* seeked)
return srs_success;
}
ISrsFileReaderFactory::ISrsFileReaderFactory()
{
}
ISrsFileReaderFactory::~ISrsFileReaderFactory()
{
}
SrsFileReader* ISrsFileReaderFactory::create_file_reader()
{
return new SrsFileReader();
}
SrsFileReader::SrsFileReader()
{
fd = -1;

@ -35,6 +35,8 @@
#include <sys/uio.h>
#endif
class SrsFileReader;
/**
* file writer, to write to file.
*/
@ -73,6 +75,16 @@ public:
virtual srs_error_t lseek(off_t offset, int whence, off_t* seeked);
};
// The file reader factory.
class ISrsFileReaderFactory
{
public:
ISrsFileReaderFactory();
virtual ~ISrsFileReaderFactory();
public:
virtual SrsFileReader* create_file_reader();
};
/**
* file reader, to read from file.
*/

@ -37,6 +37,7 @@
#include <stdlib.h>
#include <vector>
#include <algorithm>
using namespace std;
#include <srs_core_autofree.hpp>
@ -451,6 +452,16 @@ bool srs_string_contains(string str, string flag0, string flag1, string flag2)
return str.find(flag0) != string::npos || str.find(flag1) != string::npos || str.find(flag2) != string::npos;
}
int srs_string_count(string str, string flag)
{
int nn = 0;
for (int i = 0; i < (int)flag.length(); i++) {
char ch = flag.at(i);
nn += std::count(str.begin(), str.end(), ch);
}
return nn;
}
vector<string> srs_string_split(string str, string flag)
{
vector<string> arr;

@ -98,6 +98,8 @@ extern bool srs_string_starts_with(std::string str, std::string flag0, std::stri
extern bool srs_string_contains(std::string str, std::string flag);
extern bool srs_string_contains(std::string str, std::string flag0, std::string flag1);
extern bool srs_string_contains(std::string str, std::string flag0, std::string flag1, std::string flag2);
// Count each char of flag in string
extern int srs_string_count(std::string str, std::string flag);
// Find the min match in str for flags.
extern std::string srs_string_min_match(std::string str, std::vector<std::string> flags);
// Split the string by flag to array.

@ -35,9 +35,13 @@ using namespace std;
#include <srs_kernel_utility.hpp>
#include <srs_kernel_file.hpp>
#include <srs_protocol_json.hpp>
#include <srs_core_autofree.hpp>
#define SRS_HTTP_DEFAULT_PAGE "index.html"
// @see ISrsHttpMessage._http_ts_send_buffer
#define SRS_HTTP_TS_SEND_BUFFER_SIZE 4096
// get the status text of code.
string srs_generate_http_status_text(int status)
{
@ -99,9 +103,9 @@ string srs_generate_http_status_text(int status)
// permits a body. See RFC2616, section 4.4.
bool srs_go_http_body_allowd(int status)
{
if (status >= 100 && status <= 199) {
if (status >= SRS_CONSTS_HTTP_Continue && status < SRS_CONSTS_HTTP_OK) {
return false;
} else if (status == 204 || status == 304) {
} else if (status == SRS_CONSTS_HTTP_NoContent || status == SRS_CONSTS_HTTP_NotModified) {
return false;
}
@ -116,9 +120,7 @@ bool srs_go_http_body_allowd(int status)
// returns "application/octet-stream".
string srs_go_http_detect(char* data, int size)
{
// detect only when data specified.
if (data) {
}
// TODO: Implement the request content-type detecting.
return "application/octet-stream"; // fallback
}
@ -159,13 +161,36 @@ string SrsHttpHeader::get(string key)
{
std::string v;
if (headers.find(key) != headers.end()) {
v = headers[key];
map<string, string>::iterator it = headers.find(key);
if (it != headers.end()) {
v = it->second;
}
return v;
}
void SrsHttpHeader::del(string key)
{
map<string, string>::iterator it = headers.find(key);
if (it != headers.end()) {
headers.erase(it);
}
}
int SrsHttpHeader::count()
{
return (int)headers.size();
}
void SrsHttpHeader::dumps(SrsJsonObject* o)
{
map<string, string>::iterator it;
for (it = headers.begin(); it != headers.end(); ++it) {
string v = it->second;
o->set(it->first, SrsJsonAny::str(v.c_str()));
}
}
int64_t SrsHttpHeader::content_length()
{
std::string cl = get("Content-Length");
@ -194,7 +219,7 @@ void SrsHttpHeader::set_content_type(string ct)
void SrsHttpHeader::write(stringstream& ss)
{
std::map<std::string, std::string>::iterator it;
map<string, string>::iterator it;
for (it = headers.begin(); it != headers.end(); ++it) {
ss << it->first << ": " << it->second << SRS_HTTP_CRLF;
}
@ -247,7 +272,7 @@ srs_error_t SrsHttpRedirectHandler::serve_http(ISrsHttpResponseWriter* w, ISrsHt
location += "?" + r->query();
}
string msg = "Redirect to" + location;
string msg = "Redirect to " + location;
w->header()->set_content_type("text/plain; charset=utf-8");
w->header()->set_content_length(msg.length());
@ -279,37 +304,67 @@ srs_error_t SrsHttpNotFoundHandler::serve_http(ISrsHttpResponseWriter* w, ISrsHt
return srs_go_http_error(w, SRS_CONSTS_HTTP_NotFound);
}
string srs_http_fs_fullpath(string dir, string pattern, string upath)
{
// add default pages.
if (srs_string_ends_with(upath, "/")) {
upath += SRS_HTTP_DEFAULT_PAGE;
}
// Remove the virtual directory.
// For example:
// pattern=/api, the virtual directory is api, upath=/api/index.html, fullpath={dir}/index.html
// pattern=/api, the virtual directory is api, upath=/api/views/index.html, fullpath={dir}/views/index.html
// The vhost prefix is ignored, for example:
// pattern=ossrs.net/api, the vhost is ossrs.net, the pattern equals to /api under this vhost,
// so the virtual directory is also api
size_t pos = pattern.find("/");
string filename = upath;
if (upath.length() > pattern.length() && pos != string::npos) {
filename = upath.substr(pattern.length() - pos);
}
string fullpath = srs_string_trim_end(dir, "/");
if (!srs_string_starts_with(filename, "/")) {
fullpath += "/";
}
fullpath += filename;
return fullpath;
}
SrsHttpFileServer::SrsHttpFileServer(string root_dir)
{
dir = root_dir;
fs_factory = new ISrsFileReaderFactory();
_srs_path_exists = srs_path_exists;
}
SrsHttpFileServer::~SrsHttpFileServer()
{
srs_freep(fs_factory);
}
srs_error_t SrsHttpFileServer::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
void SrsHttpFileServer::set_fs_factory(ISrsFileReaderFactory* f)
{
string upath = r->path();
// add default pages.
if (srs_string_ends_with(upath, "/")) {
upath += SRS_HTTP_DEFAULT_PAGE;
}
srs_freep(fs_factory);
fs_factory = f;
}
string fullpath = dir + "/";
void SrsHttpFileServer::set_path_check(_pfn_srs_path_exists pfn)
{
_srs_path_exists = pfn;
}
// remove the virtual directory.
srs_error_t SrsHttpFileServer::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
srs_assert(entry);
size_t pos = entry->pattern.find("/");
if (upath.length() > entry->pattern.length() && pos != string::npos) {
fullpath += upath.substr(entry->pattern.length() - pos);
} else {
fullpath += upath;
}
string upath = r->path();
string fullpath = srs_http_fs_fullpath(dir, entry->pattern, upath);
// stat current dir, if exists, return error.
if (!srs_path_exists(fullpath)) {
if (!_srs_path_exists(fullpath)) {
srs_warn("http miss file=%s, pattern=%s, upath=%s",
fullpath.c_str(), entry->pattern.c_str(), upath.c_str());
return SrsHttpNotFoundHandler().serve_http(w, r);
@ -333,14 +388,15 @@ srs_error_t SrsHttpFileServer::serve_file(ISrsHttpResponseWriter* w, ISrsHttpMes
{
srs_error_t err = srs_success;
// open the target file.
SrsFileReader fs;
SrsFileReader* fs = fs_factory->create_file_reader();
SrsAutoFree(SrsFileReader, fs);
if ((err = fs.open(fullpath)) != srs_success) {
if ((err = fs->open(fullpath)) != srs_success) {
return srs_error_wrap(err, "open file %s", fullpath.c_str());
}
int64_t length = fs.filesize();
// The length of bytes we could response to.
int64_t length = fs->filesize() - fs->tellg();
// unset the content length to encode in chunked encoding.
w->header()->set_content_length(length);
@ -391,9 +447,12 @@ srs_error_t SrsHttpFileServer::serve_file(ISrsHttpResponseWriter* w, ISrsHttpMes
}
}
// Enter chunked mode, because we didn't set the content-length.
w->write_header(SRS_CONSTS_HTTP_OK);
// write body.
int64_t left = length;
if ((err = copy(w, &fs, r, (int)left)) != srs_success) {
if ((err = copy(w, fs, r, (int)left)) != srs_success) {
return srs_error_wrap(err, "copy file=%s size=%d", fullpath.c_str(), left);
}
@ -422,10 +481,8 @@ srs_error_t SrsHttpFileServer::serve_flv_file(ISrsHttpResponseWriter* w, ISrsHtt
srs_error_t SrsHttpFileServer::serve_mp4_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath)
{
// for flash to request mp4 range in query string.
// for example, http://digitalprimates.net/dash/DashTest.html?url=http://dashdemo.edgesuite.net/digitalprimates/nexus/oops-20120802-manifest.mpd
std::string range = r->query_get("range");
// or, use bytes to request range,
// for example, http://dashas.castlabs.com/demo/try.html
// or, use bytes to request range.
if (range.empty()) {
range = r->query_get("bytes");
}
@ -458,11 +515,15 @@ srs_error_t SrsHttpFileServer::serve_mp4_file(ISrsHttpResponseWriter* w, ISrsHtt
srs_error_t SrsHttpFileServer::serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath, int offset)
{
// @remark For common http file server, we don't support stream request, please use SrsVodStream instead.
// TODO: FIXME: Support range in header https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Range_requests
return serve_file(w, r, fullpath);
}
srs_error_t SrsHttpFileServer::serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath, int start, int end)
{
// @remark For common http file server, we don't support stream request, please use SrsVodStream instead.
// TODO: FIXME: Support range in header https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Range_requests
return serve_file(w, r, fullpath);
}
@ -471,25 +532,22 @@ srs_error_t SrsHttpFileServer::copy(ISrsHttpResponseWriter* w, SrsFileReader* fs
srs_error_t err = srs_success;
int left = size;
char* buf = r->http_ts_send_buffer();
char* buf = new char[SRS_HTTP_TS_SEND_BUFFER_SIZE];
SrsAutoFreeA(char, buf);
while (left > 0) {
ssize_t nread = -1;
int max_read = srs_min(left, SRS_HTTP_TS_SEND_BUFFER_SIZE);
if ((err = fs->read(buf, max_read, &nread)) != srs_success) {
break;
return srs_error_wrap(err, "read limit=%d, left=%d", max_read, left);
}
left -= nread;
if ((err = w->write(buf, (int)nread)) != srs_success) {
break;
return srs_error_wrap(err, "write limit=%d, bytes=%d, left=%d", max_read, nread, left);
}
}
if (err != srs_success) {
return srs_error_wrap(err, "copy");
}
return err;
}
@ -609,16 +667,13 @@ srs_error_t SrsHttpServeMux::handle(std::string pattern, ISrsHttpHandler* handle
std::string rpattern = pattern.substr(0, pattern.length() - 1);
SrsHttpMuxEntry* entry = NULL;
// free the exists not explicit entry
// free the exists implicit entry
if (entries.find(rpattern) != entries.end()) {
SrsHttpMuxEntry* exists = entries[rpattern];
if (!exists->explicit_match) {
entry = exists;
}
entry = entries[rpattern];
}
// create implicit redirect.
if (!entry || entry->explicit_match) {
if (!entry || !entry->explicit_match) {
srs_freep(entry);
entry = new SrsHttpMuxEntry();
@ -666,7 +721,7 @@ srs_error_t SrsHttpServeMux::find_handler(ISrsHttpMessage* r, ISrsHttpHandler**
// always hijack.
if (!hijackers.empty()) {
// notice all hijacker the match failed.
// notify all hijackers unless matching failed.
std::vector<ISrsHttpMatchHijacker*>::iterator it;
for (it = hijackers.begin(); it != hijackers.end(); ++it) {
ISrsHttpMatchHijacker* hijacker = *it;
@ -765,17 +820,10 @@ srs_error_t SrsHttpCorsMux::initialize(ISrsHttpServeMux* worker, bool cros_enabl
srs_error_t SrsHttpCorsMux::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
srs_error_t err = srs_success;
// If CORS enabled, and there is a "Origin" header, it's CORS.
if (enabled) {
for (int i = 0; i < r->request_header_count(); i++) {
string k = r->request_header_key_at(i);
if (k == "Origin" || k == "origin") {
required = true;
break;
}
}
SrsHttpHeader* h = r->header();
required = !h->get("Origin").empty();
}
// When CORS required, set the CORS headers.
@ -795,9 +843,7 @@ srs_error_t SrsHttpCorsMux::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessag
} else {
w->write_header(SRS_CONSTS_HTTP_MethodNotAllowed);
}
if ((err = w->final_request()) != srs_success) {
return srs_error_wrap(err, "final request");
}
return w->final_request();
}
srs_assert(next);
@ -806,17 +852,10 @@ srs_error_t SrsHttpCorsMux::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessag
ISrsHttpMessage::ISrsHttpMessage()
{
_http_ts_send_buffer = new char[SRS_HTTP_TS_SEND_BUFFER_SIZE];
}
ISrsHttpMessage::~ISrsHttpMessage()
{
srs_freepa(_http_ts_send_buffer);
}
char* ISrsHttpMessage::http_ts_send_buffer()
{
return _http_ts_send_buffer;
}
SrsHttpUri::SrsHttpUri()
@ -912,6 +951,8 @@ string SrsHttpUri::get_uri_field(string uri, void* php_u, int ifield)
// For #if !defined(SRS_EXPORT_LIBRTMP)
#endif
// LCOV_EXCL_START
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
@ -3436,3 +3477,13 @@ http_parser_set_max_header_size(uint32_t size) {
max_header_size = size;
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// LCOV_EXCL_STOP

@ -45,6 +45,8 @@ class SrsHttpHeader;
class ISrsHttpMessage;
class SrsHttpMuxEntry;
class ISrsHttpResponseWriter;
class SrsJsonObject;
class ISrsFileReaderFactory;
// From http specification
// CR = <US-ASCII CR, carriage return (13)>
@ -62,9 +64,6 @@ class ISrsHttpResponseWriter;
#define SRS_HTTP_CRLF "\r\n" // 0x0D0A
#define SRS_HTTP_CRLFCRLF "\r\n\r\n" // 0x0D0A0D0A
// @see ISrsHttpMessage._http_ts_send_buffer
#define SRS_HTTP_TS_SEND_BUFFER_SIZE 4096
// For ead all of http body, read each time.
#define SRS_HTTP_READ_CACHE_BYTES 4096
@ -108,6 +107,11 @@ enum SrsHttpParseState {
class SrsHttpHeader
{
private:
// The order in which header fields with differing field names are
// received is not significant. However, it is "good practice" to send
// general-header fields first, followed by request-header or response-
// header fields, and ending with the entity-header fields.
// @doc https://tools.ietf.org/html/rfc2616#section-4.2
std::map<std::string, std::string> headers;
public:
SrsHttpHeader();
@ -121,6 +125,14 @@ public:
// To access multiple values of a key, access the map directly
// with CanonicalHeaderKey.
virtual std::string get(std::string key);
// Delete the http header indicated by key.
// Return the removed header field.
virtual void del(std::string);
// Get the count of headers.
virtual int count();
public:
// Dumps to a JSON object.
virtual void dumps(SrsJsonObject* o);
public:
// Get the content length. -1 if not set.
virtual int64_t content_length();
@ -138,7 +150,11 @@ public:
// A ResponseWriter interface is used by an HTTP handler to
// construct an HTTP response.
// Usage 1, response with specified length content:
// Usage 0, response with a message once:
// ISrsHttpResponseWriter* w; // create or get response.
// std::string msg = "Hello, HTTP!";
// w->write((char*)msg.data(), (int)msg.length());
// Usage 1, response with specified length content, same to #0:
// ISrsHttpResponseWriter* w; // create or get response.
// std::string msg = "Hello, HTTP!";
// w->header()->set_content_type("text/plain; charset=utf-8");
@ -265,6 +281,12 @@ public:
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
};
// For utest to mock it.
typedef bool (*_pfn_srs_path_exists)(std::string path);
// Build the file path from request r.
extern std::string srs_http_fs_fullpath(std::string dir, std::string pattern, std::string upath);
// FileServer returns a handler that serves HTTP requests
// with the contents of the file system rooted at root.
//
@ -277,9 +299,17 @@ class SrsHttpFileServer : public ISrsHttpHandler
{
protected:
std::string dir;
protected:
ISrsFileReaderFactory* fs_factory;
_pfn_srs_path_exists _srs_path_exists;
public:
SrsHttpFileServer(std::string root_dir);
virtual ~SrsHttpFileServer();
private:
// For utest to mock the fs.
virtual void set_fs_factory(ISrsFileReaderFactory* v);
// For utest to mock the path check function.
virtual void set_path_check(_pfn_srs_path_exists pfn);
public:
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
private:
@ -420,9 +450,6 @@ public:
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
};
// For http header.
typedef std::pair<std::string, std::string> SrsHttpHeaderField;
// A Request represents an HTTP request received by a server
// or to be sent by a client.
//
@ -444,16 +471,9 @@ typedef std::pair<std::string, std::string> SrsHttpHeaderField;
// @rmark for mode 2, the infinite chunked, all left data is body.
class ISrsHttpMessage
{
private:
// Use a buffer to read and send ts file.
// TODO: FIXME: remove it.
char* _http_ts_send_buffer;
public:
ISrsHttpMessage();
virtual ~ISrsHttpMessage();
public:
// The http request level cache.
virtual char* http_ts_send_buffer();
public:
virtual uint8_t method() = 0;
virtual uint16_t status_code() = 0;
@ -500,9 +520,7 @@ public:
// then query_get("start") is "100", and query_get("end") is "200"
virtual std::string query_get(std::string key) = 0;
// Get the headers.
virtual int request_header_count() = 0;
virtual std::string request_header_key_at(int index) = 0;
virtual std::string request_header_value_at(int index) = 0;
virtual SrsHttpHeader* header() = 0;
public:
// Whether the current request is JSONP,
// which has a "callback=xxx" in QueryString.

@ -23,6 +23,7 @@
#include <srs_core.hpp>
// LCOV_EXCL_START
/* vim: set et ts=3 sw=3 sts=3 ft=c:
*
* Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved.
@ -1314,6 +1315,7 @@ void json_value_free (json_value * value)
}
#endif
// LCOV_EXCL_STOP
/**
* The MIT License (MIT)

@ -1884,21 +1884,10 @@ srs_error_t SrsRtmpClient::handshake()
SrsAutoFree(SrsComplexHandshake, complex_hs);
if ((err = complex_hs->handshake_with_server(hs_bytes, io)) != srs_success) {
if (srs_error_code(err) == ERROR_RTMP_TRY_SIMPLE_HS) {
srs_freep(err);
// always alloc object at heap.
// @see https://github.com/ossrs/srs/issues/509
SrsSimpleHandshake* simple_hs = new SrsSimpleHandshake();
SrsAutoFree(SrsSimpleHandshake, simple_hs);
if ((err = simple_hs->handshake_with_server(hs_bytes, io)) != srs_success) {
return srs_error_wrap(err, "simple handshake");
}
} else {
// As client, we never verify s0s1s2, because some server doesn't follow the RTMP spec.
// So we never have chance to use simple handshake.
return srs_error_wrap(err, "complex handshake");
}
}
hs_bytes->dispose();

@ -887,7 +887,7 @@ srs_error_t SrsRtspStack::do_recv_message(SrsRtspRequest* req)
if ((err = recv_token_eof(seq)) != srs_success) {
return srs_error_wrap(err, "seq");
}
req->seq = ::atol(seq.c_str());
req->seq = ::atoll(seq.c_str());
} else if (token == SRS_RTSP_TOKEN_CONTENT_TYPE) {
std::string ct;
if ((err = recv_token_eof(ct)) != srs_success) {
@ -899,7 +899,7 @@ srs_error_t SrsRtspStack::do_recv_message(SrsRtspRequest* req)
if ((err = recv_token_eof(cl)) != srs_success) {
return srs_error_wrap(err, "cl");
}
req->content_length = ::atol(cl.c_str());
req->content_length = ::atoll(cl.c_str());
} else if (token == SRS_RTSP_TOKEN_TRANSPORT) {
std::string transport;
if ((err = recv_token_eof(transport)) != srs_success) {

@ -39,11 +39,13 @@ using namespace std;
SrsHttpParser::SrsHttpParser()
{
buffer = new SrsFastStream();
header = NULL;
}
SrsHttpParser::~SrsHttpParser()
{
srs_freep(buffer);
srs_freep(header);
}
srs_error_t SrsHttpParser::initialize(enum http_parser_type type, bool allow_jsonp)
@ -70,19 +72,20 @@ srs_error_t SrsHttpParser::initialize(enum http_parser_type type, bool allow_jso
srs_error_t SrsHttpParser::parse_message(ISrsReader* reader, ISrsHttpMessage** ppmsg)
{
*ppmsg = NULL;
srs_error_t err = srs_success;
// reset request data.
field_name = "";
field_value = "";
expect_field_name = true;
*ppmsg = NULL;
// Reset request data.
state = SrsHttpParseStateInit;
header = http_parser();
url = "";
headers.clear();
hp_header = http_parser();
// The body that we have read from cache.
pbody = NULL;
// We must reset the field name and value, because we may get a partial value in on_header_value.
field_name = field_value = "";
// The header of the request.
srs_freep(header);
header = new SrsHttpHeader();
// do parse
if ((err = parse_message_imp(reader)) != srs_success) {
@ -90,10 +93,12 @@ srs_error_t SrsHttpParser::parse_message(ISrsReader* reader, ISrsHttpMessage** p
}
// create msg
SrsHttpMessage* msg = new SrsHttpMessage(reader);
SrsHttpMessage* msg = new SrsHttpMessage(reader, buffer);
// initalize http msg, parse url.
if ((err = msg->update(url, jsonp, &header, buffer, headers)) != srs_success) {
// Initialize the basic information.
msg->set_basic(hp_header.method, hp_header.status_code, hp_header.content_length);
msg->set_header(header, http_should_keep_alive(&hp_header));
if ((err = msg->set_url(url, jsonp)) != srs_success) {
srs_freep(msg);
return srs_error_wrap(err, "update message");
}
@ -138,9 +143,9 @@ srs_error_t SrsHttpParser::parse_message_imp(ISrsReader* reader)
}
}
// parse last header.
if (!field_name.empty() && !field_value.empty()) {
headers.push_back(std::make_pair(field_name, field_value));
SrsHttpParser* obj = this;
if (!obj->field_value.empty()) {
obj->header->set(obj->field_name, obj->field_value);
}
return err;
@ -163,7 +168,7 @@ int SrsHttpParser::on_headers_complete(http_parser* parser)
SrsHttpParser* obj = (SrsHttpParser*)parser->data;
srs_assert(obj);
obj->header = *parser;
obj->hp_header = *parser;
// save the parser when header parse completed.
obj->state = SrsHttpParseStateHeaderComplete;
@ -192,7 +197,7 @@ int SrsHttpParser::on_url(http_parser* parser, const char* at, size_t length)
srs_assert(obj);
if (length > 0) {
obj->url.append(at, (int)length);
obj->url = string(at, (int)length);
}
srs_info("Method: %d, Url: %.*s", parser->method, (int)length, at);
@ -205,15 +210,10 @@ int SrsHttpParser::on_header_field(http_parser* parser, const char* at, size_t l
SrsHttpParser* obj = (SrsHttpParser*)parser->data;
srs_assert(obj);
// field value=>name, reap the field.
if (!obj->expect_field_name) {
obj->headers.push_back(std::make_pair(obj->field_name, obj->field_value));
// reset the field name when parsed.
obj->field_name = "";
obj->field_value = "";
if (!obj->field_value.empty()) {
obj->header->set(obj->field_name, obj->field_value);
obj->field_name = obj->field_value = "";
}
obj->expect_field_name = true;
if (length > 0) {
obj->field_name.append(at, (int)length);
@ -231,7 +231,6 @@ int SrsHttpParser::on_header_value(http_parser* parser, const char* at, size_t l
if (length > 0) {
obj->field_value.append(at, (int)length);
}
obj->expect_field_name = false;
srs_info("Header value(%d bytes): %.*s", (int)length, (int)length, at);
return 0;
@ -253,58 +252,72 @@ int SrsHttpParser::on_body(http_parser* parser, const char* at, size_t length)
return 0;
}
SrsHttpMessage::SrsHttpMessage(ISrsReader* reader) : ISrsHttpMessage()
SrsHttpMessage::SrsHttpMessage(ISrsReader* reader, SrsFastStream* buffer) : ISrsHttpMessage()
{
owner_conn = NULL;
chunked = false;
infinite_chunked = false;
keep_alive = true;
_uri = new SrsHttpUri();
_body = new SrsHttpResponseReader(this, reader);
_http_ts_send_buffer = new char[SRS_HTTP_TS_SEND_BUFFER_SIZE];
_body = new SrsHttpResponseReader(this, reader, buffer);
jsonp = false;
_method = 0;
_status = 0;
_content_length = -1;
_keep_alive = true;
}
SrsHttpMessage::~SrsHttpMessage()
{
srs_freep(_body);
srs_freep(_uri);
srs_freepa(_http_ts_send_buffer);
}
srs_error_t SrsHttpMessage::update(string url, bool allow_jsonp, http_parser* header, SrsFastStream* body, vector<SrsHttpHeaderField>& headers)
void SrsHttpMessage::set_basic(uint8_t method, uint16_t status, int64_t content_length)
{
srs_error_t err = srs_success;
_method = method;
_status = status;
if (_content_length == -1) {
_content_length = content_length;
}
}
_url = url;
void SrsHttpMessage::set_header(SrsHttpHeader* header, bool keep_alive)
{
_header = *header;
_headers = headers;
_keep_alive = keep_alive;
// whether chunked.
std::string transfer_encoding = get_request_header("Transfer-Encoding");
chunked = (transfer_encoding == "chunked");
chunked = (header->get("Transfer-Encoding") == "chunked");
// whether keep alive.
keep_alive = http_should_keep_alive(header);
// set the buffer.
if ((err = _body->initialize(body)) != srs_success) {
return srs_error_wrap(err, "init body");
// Update the content-length in header.
string clv = header->get("Content-Length");
if (!clv.empty()) {
_content_length = ::atoll(clv.c_str());
}
}
srs_error_t SrsHttpMessage::set_url(string url, bool allow_jsonp)
{
srs_error_t err = srs_success;
// parse uri from url.
std::string host = get_request_header("Host");
_url = url;
// use server public ip when no host specified.
// use server public ip when host not specified.
// to make telnet happy.
std::string host = _header.get("Host");
if (host.empty()) {
host= srs_get_public_internet_address();
}
// parse uri to schema/server:port/path?query
std::string uri = "http://" + host + _url;
// parse uri from schema/server:port/path?query
std::string uri = _url;
if (!host.empty()) {
uri = "http://" + host + _url;
}
if ((err = _uri->initialize(uri)) != srs_success) {
return srs_error_wrap(err, "init uri");
return srs_error_wrap(err, "init uri %s", uri.c_str());
}
// parse ext.
@ -350,12 +363,12 @@ uint8_t SrsHttpMessage::method()
}
}
return (uint8_t)_header.method;
return _method;
}
uint16_t SrsHttpMessage::status_code()
{
return (uint16_t)_header.status_code;
return _status;
}
string SrsHttpMessage::method_str()
@ -405,7 +418,7 @@ bool SrsHttpMessage::is_http_delete()
bool SrsHttpMessage::is_http_options()
{
return _header.method == SRS_CONSTS_HTTP_OPTIONS;
return _method == SRS_CONSTS_HTTP_OPTIONS;
}
bool SrsHttpMessage::is_chunked()
@ -415,7 +428,7 @@ bool SrsHttpMessage::is_chunked()
bool SrsHttpMessage::is_keep_alive()
{
return keep_alive;
return _keep_alive;
}
bool SrsHttpMessage::is_infinite_chunked()
@ -447,6 +460,11 @@ string SrsHttpMessage::host()
return _uri->get_host();
}
int SrsHttpMessage::port()
{
return _uri->get_port();
}
string SrsHttpMessage::path()
{
return _uri->get_path();
@ -524,7 +542,7 @@ ISrsHttpResponseReader* SrsHttpMessage::body_reader()
int64_t SrsHttpMessage::content_length()
{
return _header.content_length;
return _content_length;
}
string SrsHttpMessage::query_get(string key)
@ -538,39 +556,9 @@ string SrsHttpMessage::query_get(string key)
return v;
}
int SrsHttpMessage::request_header_count()
{
return (int)_headers.size();
}
string SrsHttpMessage::request_header_key_at(int index)
{
srs_assert(index < request_header_count());
SrsHttpHeaderField item = _headers[index];
return item.first;
}
string SrsHttpMessage::request_header_value_at(int index)
SrsHttpHeader* SrsHttpMessage::header()
{
srs_assert(index < request_header_count());
SrsHttpHeaderField item = _headers[index];
return item.second;
}
string SrsHttpMessage::get_request_header(string name)
{
std::vector<SrsHttpHeaderField>::iterator it;
for (it = _headers.begin(); it != _headers.end(); ++it) {
SrsHttpHeaderField& elem = *it;
std::string key = elem.first;
std::string value = elem.second;
if (key == name) {
return value;
}
}
return "";
return &_header;
}
SrsRequest* SrsHttpMessage::to_request(string vhost)
@ -590,7 +578,7 @@ SrsRequest* SrsHttpMessage::to_request(string vhost)
// generate others.
req->tcUrl = "rtmp://" + vhost + "/" + req->app;
req->pageUrl = get_request_header("Referer");
req->pageUrl = _header.get("Referer");
req->objectEncoding = 0;
std::string query = _uri->get_query();
@ -614,7 +602,15 @@ bool SrsHttpMessage::is_jsonp()
return jsonp;
}
SrsHttpResponseWriter::SrsHttpResponseWriter(SrsStSocket* io)
ISrsHttpHeaderFilter::ISrsHttpHeaderFilter()
{
}
ISrsHttpHeaderFilter::~ISrsHttpHeaderFilter()
{
}
SrsHttpResponseWriter::SrsHttpResponseWriter(ISrsProtocolReadWriter* io)
{
skt = io;
hdr = new SrsHttpHeader();
@ -625,6 +621,7 @@ SrsHttpResponseWriter::SrsHttpResponseWriter(SrsStSocket* io)
header_sent = false;
nb_iovss_cache = 0;
iovss_cache = NULL;
hf = NULL;
}
SrsHttpResponseWriter::~SrsHttpResponseWriter()
@ -635,11 +632,18 @@ SrsHttpResponseWriter::~SrsHttpResponseWriter()
srs_error_t SrsHttpResponseWriter::final_request()
{
srs_error_t err = srs_success;
// write the header data in memory.
if (!header_wrote) {
write_header(SRS_CONSTS_HTTP_OK);
}
// whatever header is wrote, we should try to send header.
if ((err = send_header(NULL, 0)) != srs_success) {
return srs_error_wrap(err, "send header");
}
// complete the chunked encoding.
if (content_length == -1) {
std::stringstream ss;
@ -663,6 +667,10 @@ srs_error_t SrsHttpResponseWriter::write(char* data, int size)
// write the header data in memory.
if (!header_wrote) {
if (hdr->content_type().empty()) {
hdr->set_content_type("text/plain; charset=utf-8");
}
hdr->set_content_length(size);
write_header(SRS_CONSTS_HTTP_OK);
}
@ -678,7 +686,7 @@ srs_error_t SrsHttpResponseWriter::write(char* data, int size)
}
// ignore NULL content.
if (!data) {
if (!data || size <= 0) {
return err;
}
@ -823,7 +831,7 @@ srs_error_t SrsHttpResponseWriter::send_header(char* data, int size)
// detect content type
if (srs_go_http_body_allowd(status)) {
if (hdr->content_type().empty()) {
if (data && hdr->content_type().empty()) {
hdr->set_content_type(srs_go_http_detect(data, size));
}
}
@ -841,7 +849,12 @@ srs_error_t SrsHttpResponseWriter::send_header(char* data, int size)
// keep alive to make vlc happy.
hdr->set("Connection", "Keep-Alive");
// write headers
// Filter the header before writing it.
if (hf && ((err = hf->filter(hdr)) != srs_success)) {
return srs_error_wrap(err, "filter header");
}
// write header
hdr->write(ss);
// header_eof
@ -851,32 +864,20 @@ srs_error_t SrsHttpResponseWriter::send_header(char* data, int size)
return skt->write((void*)buf.c_str(), buf.length(), NULL);
}
SrsHttpResponseReader::SrsHttpResponseReader(SrsHttpMessage* msg, ISrsReader* reader)
SrsHttpResponseReader::SrsHttpResponseReader(SrsHttpMessage* msg, ISrsReader* reader, SrsFastStream* body)
{
skt = reader;
owner = msg;
is_eof = false;
nb_total_read = 0;
nb_left_chunk = 0;
buffer = NULL;
buffer = body;
}
SrsHttpResponseReader::~SrsHttpResponseReader()
{
}
srs_error_t SrsHttpResponseReader::initialize(SrsFastStream* body)
{
srs_error_t err = srs_success;
nb_chunk = 0;
nb_left_chunk = 0;
nb_total_read = 0;
buffer = body;
return err;
}
bool SrsHttpResponseReader::eof()
{
return is_eof;

@ -35,7 +35,7 @@ class SrsFastStream;
class SrsRequest;
class ISrsReader;
class SrsHttpResponseReader;
class SrsStSocket;
class ISrsProtocolReadWriter;
// A wrapper for http-parser,
// provides HTTP message originted service.
@ -49,14 +49,12 @@ private:
// Whether allow jsonp parse.
bool jsonp;
private:
// http parse data, reset before parse message.
bool expect_field_name;
std::string field_name;
std::string field_value;
SrsHttpParseState state;
http_parser header;
http_parser hp_header;
std::string url;
std::vector<SrsHttpHeaderField> headers;
SrsHttpHeader* header;
const char* pbody;
public:
SrsHttpParser();
@ -85,9 +83,6 @@ private:
static int on_body(http_parser* parser, const char* at, size_t length);
};
// for http header.
typedef std::pair<std::string, std::string> SrsHttpHeaderField;
// A Request represents an HTTP request received by a server
// or to be sent by a client.
//
@ -97,42 +92,51 @@ typedef std::pair<std::string, std::string> SrsHttpHeaderField;
class SrsHttpMessage : public ISrsHttpMessage
{
private:
// The parsed url.
std::string _url;
// The extension of file, for example, .flv
std::string _ext;
// parsed http header.
http_parser _header;
// The body object, reader object.
// @remark, user can get body in string by get_body().
SrsHttpResponseReader* _body;
// Whether the body is chunked.
bool chunked;
// Whether the body is infinite chunked.
bool infinite_chunked;
// Use a buffer to read and send ts file.
// The transport connection, can be NULL.
SrsConnection* owner_conn;
private:
uint8_t _method;
uint16_t _status;
int64_t _content_length;
private:
// The http headers
SrsHttpHeader _header;
// Whether the request indicates should keep alive for the http connection.
bool keep_alive;
bool _keep_alive;
// Whether the body is chunked.
bool chunked;
private:
// The parsed url.
std::string _url;
// The extension of file, for example, .flv
std::string _ext;
// The uri parser
SrsHttpUri* _uri;
// Use a buffer to read and send ts file.
// TODO: FIXME: remove it.
char* _http_ts_send_buffer;
// The http headers
std::vector<SrsHttpHeaderField> _headers;
// The query map
std::map<std::string, std::string> _query;
// The transport connection, can be NULL.
SrsConnection* owner_conn;
private:
// Whether request is jsonp.
bool jsonp;
// The method in QueryString will override the HTTP method.
std::string jsonp_method;
public:
SrsHttpMessage(ISrsReader* io);
SrsHttpMessage(ISrsReader* reader = NULL, SrsFastStream* buffer = NULL);
virtual ~SrsHttpMessage();
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 method, uint16_t status, int64_t content_length);
// Set HTTP header and whether the request require keep alive.
// @remark User must call set_header before set_url, because the Host in header is used for url.
virtual void set_header(SrsHttpHeader* header, bool keep_alive);
// set the original messages, then update the message.
virtual srs_error_t update(std::string url, bool allow_jsonp, http_parser* header, SrsFastStream* body, std::vector<SrsHttpHeaderField>& headers);
virtual srs_error_t set_url(std::string url, bool allow_jsonp);
public:
// Get the owner connection, maybe NULL.
virtual SrsConnection* connection();
@ -159,6 +163,7 @@ public:
// The url maybe the path.
virtual std::string url();
virtual std::string host();
virtual int port();
virtual std::string path();
virtual std::string query();
virtual std::string ext();
@ -179,10 +184,7 @@ public:
// then query_get("start") is "100", and query_get("end") is "200"
virtual std::string query_get(std::string key);
// Get the headers.
virtual int request_header_count();
virtual std::string request_header_key_at(int index);
virtual std::string request_header_value_at(int index);
virtual std::string get_request_header(std::string name);
virtual SrsHttpHeader* header();
public:
// Convert the http message to a request.
// @remark user must free the return request.
@ -195,12 +197,25 @@ public:
// for writev, there always one chunk to send it.
#define SRS_HTTP_HEADER_CACHE_SIZE 64
class ISrsHttpHeaderFilter
{
public:
ISrsHttpHeaderFilter();
virtual ~ISrsHttpHeaderFilter();
public:
// Filter the HTTP header h.
virtual srs_error_t filter(SrsHttpHeader* h) = 0;
};
// Response writer use st socket
class SrsHttpResponseWriter : public ISrsHttpResponseWriter
{
private:
SrsStSocket* skt;
ISrsProtocolReadWriter* skt;
SrsHttpHeader* hdr;
// Before writing header, there is a chance to filter it,
// such as remove some headers or inject new.
ISrsHttpHeaderFilter* hf;
private:
char header_cache[SRS_HTTP_HEADER_CACHE_SIZE];
iovec* iovss_cache;
@ -222,7 +237,7 @@ private:
// logically written.
bool header_sent;
public:
SrsHttpResponseWriter(SrsStSocket* io);
SrsHttpResponseWriter(ISrsProtocolReadWriter* io);
virtual ~SrsHttpResponseWriter();
public:
virtual srs_error_t final_request();
@ -248,11 +263,10 @@ private:
// Already read total bytes.
int64_t nb_total_read;
public:
SrsHttpResponseReader(SrsHttpMessage* msg, ISrsReader* reader);
// Generally the reader is the under-layer io such as socket,
// while buffer is a fast cache which may have cached some data from reader.
SrsHttpResponseReader(SrsHttpMessage* msg, ISrsReader* reader, SrsFastStream* buffer);
virtual ~SrsHttpResponseReader();
public:
// Initialize the response reader with buffer.
virtual srs_error_t initialize(SrsFastStream* buffer);
// Interface ISrsHttpResponseReader
public:
virtual bool eof();

@ -35,8 +35,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "gtest/gtest.h"
#include <string>
using namespace std;
#include <srs_app_log.hpp>
#include <srs_kernel_stream.hpp>
// we add an empty macro for upp to show the smart tips.
#define VOID
@ -61,6 +63,10 @@ extern srs_utime_t _srs_tmp_timeout;
#define HELPER_ARRAY_INIT(buf, sz, val) \
for (int i = 0; i < (int)sz; i++) (buf)[i]=val
// Dump simple stream to string.
#define HELPER_BUFFER2STR(io) \
string((const char*)(io)->bytes(), (size_t)(io)->length())
// the asserts of gtest:
// * {ASSERT|EXPECT}_EQ(expected, actual): Tests that expected == actual
// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2

File diff suppressed because it is too large Load Diff

@ -25,21 +25,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define SRS_UTEST_PROTO_STACK_HPP
/*
#include <srs_utest_protostack.hpp>
#include <srs_utest_http.hpp>
*/
#include <srs_utest.hpp>
#include <string>
#include <srs_protocol_utility.hpp>
#include <srs_rtmp_stack.hpp>
#include <srs_rtmp_handshake.hpp>
#include <srs_protocol_stream.hpp>
#include <srs_protocol_kbps.hpp>
using namespace _srs_internal;
#include <srs_protocol_io.hpp>
#include <srs_utest_protocol.hpp>
#endif

@ -2319,6 +2319,14 @@ VOID TEST(KernelUtility, StringUtils)
EXPECT_TRUE(srs_string_contains("srs", "s", "sr", "srs"));
}
if (true) {
EXPECT_EQ(0, srs_string_count("srs", "y"));
EXPECT_EQ(0, srs_string_count("srs", ""));
EXPECT_EQ(1, srs_string_count("srs", "r"));
EXPECT_EQ(2, srs_string_count("srs", "s"));
EXPECT_EQ(3, srs_string_count("srs", "sr"));
}
if (true) {
vector<string> flags;
EXPECT_TRUE("srs" == srs_string_min_match("srs", flags));
@ -2633,9 +2641,9 @@ VOID TEST(KernelUtility, RTMPUtils2)
VOID TEST(KernelErrorTest, CoverAll)
{
if (true) {
EXPECT_TRUE(srs_is_system_control_error(ERROR_CONTROL_RTMP_CLOSE));
EXPECT_TRUE(srs_is_system_control_error(ERROR_CONTROL_REPUBLISH));
EXPECT_TRUE(srs_is_system_control_error(ERROR_CONTROL_REDIRECT));
EXPECT_TRUE(srs_is_system_control_error(srs_error_new(ERROR_CONTROL_RTMP_CLOSE, "err")));
EXPECT_TRUE(srs_is_system_control_error(srs_error_new(ERROR_CONTROL_REPUBLISH, "err")));
EXPECT_TRUE(srs_is_system_control_error(srs_error_new(ERROR_CONTROL_REDIRECT, "err")));
}
if (true) {
@ -2645,9 +2653,9 @@ VOID TEST(KernelErrorTest, CoverAll)
}
if (true) {
EXPECT_TRUE(srs_is_client_gracefully_close(ERROR_SOCKET_READ));
EXPECT_TRUE(srs_is_client_gracefully_close(ERROR_SOCKET_READ_FULLY));
EXPECT_TRUE(srs_is_client_gracefully_close(ERROR_SOCKET_WRITE));
EXPECT_TRUE(srs_is_client_gracefully_close(srs_error_new(ERROR_SOCKET_READ, "err")));
EXPECT_TRUE(srs_is_client_gracefully_close(srs_error_new(ERROR_SOCKET_READ_FULLY, "err")));
EXPECT_TRUE(srs_is_client_gracefully_close(srs_error_new(ERROR_SOCKET_WRITE, "err")));
}
if (true) {

@ -20,9 +20,7 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <srs_utest_protostack.hpp>
#include <srs_utest_protocol.hpp>
#include <srs_utest_rtmp.hpp>
#include <srs_kernel_error.hpp>
#include <srs_core_autofree.hpp>
@ -75,7 +73,7 @@ public:
}
};
VOID TEST(ProtoStackTest, PacketEncode)
VOID TEST(ProtocolRTMPTest, PacketEncode)
{
srs_error_t err;
@ -112,7 +110,7 @@ VOID TEST(ProtoStackTest, PacketEncode)
}
}
VOID TEST(ProtoStackTest, ManualFlush)
VOID TEST(ProtocolRTMPTest, ManualFlush)
{
srs_error_t err;
@ -222,7 +220,7 @@ VOID TEST(ProtoStackTest, ManualFlush)
}
}
VOID TEST(ProtoStackTest, SendPacketsError)
VOID TEST(ProtocolRTMPTest, SendPacketsError)
{
srs_error_t err;
@ -303,7 +301,7 @@ VOID TEST(ProtoStackTest, SendPacketsError)
}
}
VOID TEST(ProtoStackTest, SendHugePacket)
VOID TEST(ProtocolRTMPTest, SendHugePacket)
{
srs_error_t err;
@ -318,7 +316,7 @@ VOID TEST(ProtoStackTest, SendHugePacket)
}
}
VOID TEST(ProtoStackTest, SendZeroMessages)
VOID TEST(ProtocolRTMPTest, SendZeroMessages)
{
srs_error_t err;
if (true) {
@ -345,7 +343,7 @@ VOID TEST(ProtoStackTest, SendZeroMessages)
}
}
VOID TEST(ProtoStackTest, HugeMessages)
VOID TEST(ProtocolRTMPTest, HugeMessages)
{
srs_error_t err;
if (true) {
@ -409,7 +407,7 @@ VOID TEST(ProtoStackTest, HugeMessages)
}
}
VOID TEST(ProtoStackTest, DecodeMessages)
VOID TEST(ProtocolRTMPTest, DecodeMessages)
{
srs_error_t err;
@ -428,7 +426,7 @@ VOID TEST(ProtoStackTest, DecodeMessages)
}
}
VOID TEST(ProtoStackTest, OnDecodeMessages)
VOID TEST(ProtocolRTMPTest, OnDecodeMessages)
{
srs_error_t err;
@ -469,7 +467,7 @@ SrsCommonMessage* _create_amf0(char* bytes, int size, int stream_id)
return msg;
}
VOID TEST(ProtoStackTest, OnDecodeMessages2)
VOID TEST(ProtocolRTMPTest, OnDecodeMessages2)
{
srs_error_t err;
@ -536,7 +534,7 @@ VOID TEST(ProtoStackTest, OnDecodeMessages2)
}
}
VOID TEST(ProtoStackTest, OnDecodeMessages3)
VOID TEST(ProtocolRTMPTest, OnDecodeMessages3)
{
srs_error_t err;
@ -705,7 +703,7 @@ VOID TEST(ProtoStackTest, OnDecodeMessages3)
}
}
VOID TEST(ProtoStackTest, OnDecodeMessages4)
VOID TEST(ProtocolRTMPTest, OnDecodeMessages4)
{
srs_error_t err;
@ -1070,7 +1068,7 @@ VOID TEST(ProtoStackTest, OnDecodeMessages4)
}
}
VOID TEST(ProtoStackTest, RecvMessage)
VOID TEST(ProtocolRTMPTest, RecvMessage)
{
srs_error_t err;
@ -1120,7 +1118,7 @@ VOID TEST(ProtoStackTest, RecvMessage)
}
}
VOID TEST(ProtoStackTest, RecvMessage2)
VOID TEST(ProtocolRTMPTest, RecvMessage2)
{
srs_error_t err;
@ -1179,7 +1177,7 @@ VOID TEST(ProtoStackTest, RecvMessage2)
}
}
VOID TEST(ProtoStackTest, RecvMessage3)
VOID TEST(ProtocolRTMPTest, RecvMessage3)
{
if (true) {
SrsRequest req;
@ -1239,7 +1237,7 @@ VOID TEST(ProtoStackTest, RecvMessage3)
}
}
VOID TEST(ProtoStackTest, RecvMessage4)
VOID TEST(ProtocolRTMPTest, RecvMessage4)
{
srs_error_t err;
@ -1279,7 +1277,7 @@ VOID TEST(ProtoStackTest, RecvMessage4)
}
}
VOID TEST(ProtoStackTest, HandshakeC0C1)
VOID TEST(ProtocolRTMPTest, HandshakeC0C1)
{
srs_error_t err;
@ -1379,7 +1377,7 @@ VOID TEST(ProtoStackTest, HandshakeC0C1)
}
}
VOID TEST(ProtoStackTest, HandshakeS0S1S2)
VOID TEST(ProtocolRTMPTest, HandshakeS0S1S2)
{
srs_error_t err;
@ -1424,7 +1422,7 @@ VOID TEST(ProtoStackTest, HandshakeS0S1S2)
}
}
VOID TEST(ProtoStackTest, HandshakeC2)
VOID TEST(ProtocolRTMPTest, HandshakeC2)
{
srs_error_t err;
@ -1469,7 +1467,7 @@ VOID TEST(ProtoStackTest, HandshakeC2)
}
}
VOID TEST(ProtoStackTest, ServerInfo)
VOID TEST(ProtocolRTMPTest, ServerInfo)
{
SrsServerInfo si;
EXPECT_EQ(0, si.pid);
@ -1480,7 +1478,7 @@ VOID TEST(ProtoStackTest, ServerInfo)
EXPECT_EQ(0, si.build);
}
VOID TEST(ProtoStackTest, ClientCommandMessage)
VOID TEST(ProtocolRTMPTest, ClientCommandMessage)
{
srs_error_t err;
@ -1581,7 +1579,7 @@ VOID TEST(ProtoStackTest, ClientCommandMessage)
}
}
VOID TEST(ProtoStackTest, ServerCommandMessage)
VOID TEST(ProtocolRTMPTest, ServerCommandMessage)
{
srs_error_t err;
@ -1736,7 +1734,7 @@ VOID TEST(ProtoStackTest, ServerCommandMessage)
}
}
VOID TEST(ProtoStackTest, ServerRedirect)
VOID TEST(ProtocolRTMPTest, ServerRedirect)
{
srs_error_t err;
@ -1843,7 +1841,7 @@ VOID TEST(ProtoStackTest, ServerRedirect)
}
}
VOID TEST(ProtoStackTest, ServerIdentify)
VOID TEST(ProtocolRTMPTest, ServerIdentify)
{
srs_error_t err;
@ -2023,7 +2021,7 @@ VOID TEST(ProtoStackTest, ServerIdentify)
}
}
VOID TEST(ProtoStackTest, ServerFMLEStart)
VOID TEST(ProtocolRTMPTest, ServerFMLEStart)
{
srs_error_t err;
@ -2088,7 +2086,7 @@ VOID TEST(ProtoStackTest, ServerFMLEStart)
}
}
VOID TEST(ProtoStackTest, ServerHaivisionPublish)
VOID TEST(ProtocolRTMPTest, ServerHaivisionPublish)
{
srs_error_t err;
@ -2127,7 +2125,7 @@ VOID TEST(ProtoStackTest, ServerHaivisionPublish)
}
}
VOID TEST(ProtoStackTest, ServerFMLEUnpublish)
VOID TEST(ProtocolRTMPTest, ServerFMLEUnpublish)
{
srs_error_t err;
@ -2180,7 +2178,7 @@ VOID TEST(ProtoStackTest, ServerFMLEUnpublish)
}
}
VOID TEST(ProtoStackTest, ServerFlashPublish)
VOID TEST(ProtocolRTMPTest, ServerFlashPublish)
{
srs_error_t err;
@ -2205,7 +2203,7 @@ VOID TEST(ProtoStackTest, ServerFlashPublish)
}
}
VOID TEST(ProtoStackTest, ServerRecursiveDepth)
VOID TEST(ProtocolRTMPTest, ServerRecursiveDepth)
{
srs_error_t err;
@ -2260,7 +2258,7 @@ VOID TEST(ProtoStackTest, ServerRecursiveDepth)
}
}
VOID TEST(ProtoStackTest, ServerResponseCommands)
VOID TEST(ProtocolRTMPTest, ServerResponseCommands)
{
srs_error_t err;
@ -2402,7 +2400,7 @@ VOID TEST(ProtoStackTest, ServerResponseCommands)
}
}
VOID TEST(ProtoStackTest, CoverAll)
VOID TEST(ProtocolRTMPTest, CoverAll)
{
srs_error_t err;
@ -2483,7 +2481,7 @@ VOID TEST(ProtoStackTest, CoverAll)
}
}
VOID TEST(ProtoStackTest, CoverBandwidth)
VOID TEST(ProtocolRTMPTest, CoverBandwidth)
{
if (true) {
SrsBandwidthPacket p;
@ -2555,7 +2553,7 @@ VOID TEST(ProtoStackTest, CoverBandwidth)
}
}
VOID TEST(ProtoStackTest, CoverAllUnmarshal)
VOID TEST(ProtocolRTMPTest, CoverAllUnmarshal)
{
srs_error_t err;
@ -2762,7 +2760,7 @@ VOID TEST(ProtoStackTest, CoverAllUnmarshal)
}
}
VOID TEST(ProtoStackTest, ComplexToSimpleHandshake)
VOID TEST(ProtocolRTMPTest, ComplexToSimpleHandshake)
{
srs_error_t err;
@ -2785,7 +2783,7 @@ VOID TEST(ProtoStackTest, ComplexToSimpleHandshake)
}
}
VOID TEST(ProtoStackTest, ConnectAppWithArgs)
VOID TEST(ProtocolRTMPTest, ConnectAppWithArgs)
{
srs_error_t err;
@ -2848,7 +2846,7 @@ VOID TEST(ProtoStackTest, ConnectAppWithArgs)
}
}
VOID TEST(ProtoStackTest, AgentMessageCodec)
VOID TEST(ProtocolRTMPTest, AgentMessageCodec)
{
srs_error_t err;
@ -2950,7 +2948,7 @@ srs_error_t _mock_packet_to_shared_msg(SrsPacket* packet, int stream_id, SrsShar
return err;
}
VOID TEST(ProtoStackTest, CheckStreamID)
VOID TEST(ProtocolRTMPTest, CheckStreamID)
{
srs_error_t err;
@ -2995,7 +2993,7 @@ VOID TEST(ProtoStackTest, CheckStreamID)
}
}
VOID TEST(ProtoStackTest, AgentMessageTransform)
VOID TEST(ProtocolRTMPTest, AgentMessageTransform)
{
srs_error_t err;
@ -3095,7 +3093,7 @@ public:
}
};
VOID TEST(ProtoStackTest, MergeReadHandler)
VOID TEST(ProtocolRTMPTest, MergeReadHandler)
{
srs_error_t err;

@ -0,0 +1,35 @@
/*
The MIT License (MIT)
Copyright (c) 2013-2019 Winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SRS_UTEST_PROTO_STACK_HPP
#define SRS_UTEST_PROTO_STACK_HPP
/*
#include <srs_utest_rtmp.hpp>
*/
#include <srs_utest.hpp>
#include <srs_utest_protocol.hpp>
#endif
Loading…
Cancel
Save