diff --git a/README.md b/README.md index 31044195d..bacdbeb57 100755 --- a/README.md +++ b/README.md @@ -566,6 +566,7 @@ Supported operating systems and hardware: ### SRS 2.0 history +* v2.0, 2015-05-23, fix [#391](https://github.com/simple-rtmp-server/srs/issues/391) copy request for async call. * v2.0, 2015-05-22, fix [#397](https://github.com/simple-rtmp-server/srs/issues/397) the USER_HZ maybe not 100. 2.0.165 * v2.0, 2015-05-22, for [#400](https://github.com/simple-rtmp-server/srs/issues/400), parse when got entire http header, by feilong. 2.0.164. * v2.0, 2015-05-19, merge from bravo system, add the rtmfp to bms(commercial srs). 2.0.163. diff --git a/trunk/configure b/trunk/configure index 7f8727442..d0841c79a 100755 --- a/trunk/configure +++ b/trunk/configure @@ -159,7 +159,7 @@ MODULE_DEPENDS=("CORE" "KERNEL") ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSSLRoot}) MODULE_FILES=("srs_rtmp_amf0" "srs_rtmp_io" "srs_rtmp_stack" "srs_rtmp_sdk" "srs_rtmp_handshake" "srs_rtmp_utility" "srs_rtmp_msg_array" "srs_rtmp_buffer" - "srs_raw_avc" "srs_rtsp_stack") + "srs_raw_avc" "srs_rtsp_stack" "srs_http_stack") RTMP_INCS="src/protocol"; MODULE_DIR=${RTMP_INCS} . auto/modules.sh RTMP_OBJS="${MODULE_OBJS[@]}" # @@ -169,7 +169,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then MODULE_DEPENDS=("CORE" "KERNEL" "RTMP") ModuleLibIncs=(${LibSTRoot} ${LibHttpParserRoot} ${SRS_OBJS_DIR}) MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_st_socket" "srs_app_source" - "srs_app_refer" "srs_app_hls" "srs_app_forward" "srs_app_encoder" "srs_app_http" + "srs_app_refer" "srs_app_hls" "srs_app_forward" "srs_app_encoder" "srs_app_thread" "srs_app_bandwidth" "srs_app_st" "srs_app_log" "srs_app_config" "srs_app_pithy_print" "srs_app_reload" "srs_app_http_api" "srs_app_http_conn" "srs_app_http_hooks" "srs_app_json" "srs_app_ingest" "srs_app_ffmpeg" "srs_app_utility" "srs_app_dvr" "srs_app_edge" diff --git a/trunk/ide/srs_upp/srs_upp.upp b/trunk/ide/srs_upp/srs_upp.upp index e302bb98b..edba5ddc9 100755 --- a/trunk/ide/srs_upp/srs_upp.upp +++ b/trunk/ide/srs_upp/srs_upp.upp @@ -46,6 +46,8 @@ file ../../src/kernel/srs_kernel_utility.hpp, ../../src/kernel/srs_kernel_utility.cpp, protocol readonly separator, + ../../src/protocol/srs_http_stack.hpp, + ../../src/protocol/srs_http_stack.cpp, ../../src/protocol/srs_raw_avc.hpp, ../../src/protocol/srs_raw_avc.cpp, ../../src/protocol/srs_rtmp_amf0.hpp, @@ -91,8 +93,6 @@ file ../../src/app/srs_app_heartbeat.cpp, ../../src/app/srs_app_hls.hpp, ../../src/app/srs_app_hls.cpp, - ../../src/app/srs_app_http.hpp, - ../../src/app/srs_app_http.cpp, ../../src/app/srs_app_http_api.hpp, ../../src/app/srs_app_http_api.cpp, ../../src/app/srs_app_http_client.hpp, diff --git a/trunk/ide/srs_vs2010/srs.vcxproj b/trunk/ide/srs_vs2010/srs.vcxproj index f7d0a9ff1..76a86d491 100755 --- a/trunk/ide/srs_vs2010/srs.vcxproj +++ b/trunk/ide/srs_vs2010/srs.vcxproj @@ -74,7 +74,6 @@ - @@ -119,6 +118,7 @@ + @@ -155,7 +155,6 @@ - @@ -201,6 +200,7 @@ + diff --git a/trunk/ide/srs_xcode/srs_xcode.xcodeproj/project.pbxproj b/trunk/ide/srs_xcode/srs_xcode.xcodeproj/project.pbxproj index 6daf2f9b7..ebc5dce14 100644 --- a/trunk/ide/srs_xcode/srs_xcode.xcodeproj/project.pbxproj +++ b/trunk/ide/srs_xcode/srs_xcode.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 3C0E1B8D1B0F5ADF003ADEF7 /* srs_http_stack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C0E1B8B1B0F5ADF003ADEF7 /* srs_http_stack.cpp */; }; 3C1231F61AAE652D00CE8F6C /* srs_core_autofree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C1231F01AAE652C00CE8F6C /* srs_core_autofree.cpp */; }; 3C1231F71AAE652D00CE8F6C /* srs_core_performance.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C1231F21AAE652C00CE8F6C /* srs_core_performance.cpp */; }; 3C1231F81AAE652D00CE8F6C /* srs_core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C1231F41AAE652D00CE8F6C /* srs_core.cpp */; }; @@ -48,7 +49,6 @@ 3C1232A01AAE81D900CE8F6C /* srs_app_http_client.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C1232641AAE81D900CE8F6C /* srs_app_http_client.cpp */; }; 3C1232A11AAE81D900CE8F6C /* srs_app_http_conn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C1232661AAE81D900CE8F6C /* srs_app_http_conn.cpp */; }; 3C1232A21AAE81D900CE8F6C /* srs_app_http_hooks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C1232681AAE81D900CE8F6C /* srs_app_http_hooks.cpp */; }; - 3C1232A31AAE81D900CE8F6C /* srs_app_http.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C12326A1AAE81D900CE8F6C /* srs_app_http.cpp */; }; 3C1232A41AAE81D900CE8F6C /* srs_app_ingest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C12326C1AAE81D900CE8F6C /* srs_app_ingest.cpp */; }; 3C1232A51AAE81D900CE8F6C /* srs_app_json.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C12326E1AAE81D900CE8F6C /* srs_app_json.cpp */; }; 3C1232A61AAE81D900CE8F6C /* srs_app_kbps.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C1232701AAE81D900CE8F6C /* srs_app_kbps.cpp */; }; @@ -122,6 +122,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 3C0E1B8B1B0F5ADF003ADEF7 /* srs_http_stack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_http_stack.cpp; path = ../../../src/protocol/srs_http_stack.cpp; sourceTree = ""; }; + 3C0E1B8C1B0F5ADF003ADEF7 /* srs_http_stack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_http_stack.hpp; path = ../../../src/protocol/srs_http_stack.hpp; sourceTree = ""; }; 3C1231E51AAE64A400CE8F6C /* srs_xcode */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = srs_xcode; sourceTree = BUILT_PRODUCTS_DIR; }; 3C1231F01AAE652C00CE8F6C /* srs_core_autofree.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_core_autofree.cpp; path = ../../../src/core/srs_core_autofree.cpp; sourceTree = ""; }; 3C1231F11AAE652C00CE8F6C /* srs_core_autofree.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_core_autofree.hpp; path = ../../../src/core/srs_core_autofree.hpp; sourceTree = ""; }; @@ -205,8 +207,6 @@ 3C1232671AAE81D900CE8F6C /* srs_app_http_conn.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_app_http_conn.hpp; path = ../../../src/app/srs_app_http_conn.hpp; sourceTree = ""; }; 3C1232681AAE81D900CE8F6C /* srs_app_http_hooks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_app_http_hooks.cpp; path = ../../../src/app/srs_app_http_hooks.cpp; sourceTree = ""; }; 3C1232691AAE81D900CE8F6C /* srs_app_http_hooks.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_app_http_hooks.hpp; path = ../../../src/app/srs_app_http_hooks.hpp; sourceTree = ""; }; - 3C12326A1AAE81D900CE8F6C /* srs_app_http.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_app_http.cpp; path = ../../../src/app/srs_app_http.cpp; sourceTree = ""; }; - 3C12326B1AAE81D900CE8F6C /* srs_app_http.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_app_http.hpp; path = ../../../src/app/srs_app_http.hpp; sourceTree = ""; }; 3C12326C1AAE81D900CE8F6C /* srs_app_ingest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_app_ingest.cpp; path = ../../../src/app/srs_app_ingest.cpp; sourceTree = ""; }; 3C12326D1AAE81D900CE8F6C /* srs_app_ingest.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_app_ingest.hpp; path = ../../../src/app/srs_app_ingest.hpp; sourceTree = ""; }; 3C12326E1AAE81D900CE8F6C /* srs_app_json.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_app_json.cpp; path = ../../../src/app/srs_app_json.cpp; sourceTree = ""; }; @@ -487,6 +487,8 @@ 3C12322C1AAE819900CE8F6C /* protocol */ = { isa = PBXGroup; children = ( + 3C0E1B8B1B0F5ADF003ADEF7 /* srs_http_stack.cpp */, + 3C0E1B8C1B0F5ADF003ADEF7 /* srs_http_stack.hpp */, 3C12322D1AAE81A400CE8F6C /* srs_raw_avc.cpp */, 3C12322E1AAE81A400CE8F6C /* srs_raw_avc.hpp */, 3C12322F1AAE81A400CE8F6C /* srs_rtmp_amf0.cpp */, @@ -550,8 +552,6 @@ 3C1232671AAE81D900CE8F6C /* srs_app_http_conn.hpp */, 3C1232681AAE81D900CE8F6C /* srs_app_http_hooks.cpp */, 3C1232691AAE81D900CE8F6C /* srs_app_http_hooks.hpp */, - 3C12326A1AAE81D900CE8F6C /* srs_app_http.cpp */, - 3C12326B1AAE81D900CE8F6C /* srs_app_http.hpp */, 3C12326C1AAE81D900CE8F6C /* srs_app_ingest.cpp */, 3C12326D1AAE81D900CE8F6C /* srs_app_ingest.hpp */, 3C12326E1AAE81D900CE8F6C /* srs_app_json.cpp */, @@ -886,6 +886,7 @@ 3C1232411AAE81A400CE8F6C /* srs_raw_avc.cpp in Sources */, 3C1232491AAE81A400CE8F6C /* srs_rtmp_utility.cpp in Sources */, 3C663F191AB0155100286D8B /* srs_publish.c in Sources */, + 3C0E1B8D1B0F5ADF003ADEF7 /* srs_http_stack.cpp in Sources */, 3C1232A01AAE81D900CE8F6C /* srs_app_http_client.cpp in Sources */, 3C689F981AB6AAAC00C9CEEE /* key.c in Sources */, 3C12329B1AAE81D900CE8F6C /* srs_app_ffmpeg.cpp in Sources */, @@ -936,7 +937,6 @@ 3C1232AF1AAE81D900CE8F6C /* srs_app_rtsp.cpp in Sources */, 3CC52DDD1ACE4023006FEB01 /* srs_utest_reload.cpp in Sources */, 3C689FA11AB6AAC800C9CEEE /* sync.c in Sources */, - 3C1232A31AAE81D900CE8F6C /* srs_app_http.cpp in Sources */, 3C12329A1AAE81D900CE8F6C /* srs_app_encoder.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/trunk/src/app/srs_app_async_call.cpp b/trunk/src/app/srs_app_async_call.cpp index 24f7228ca..28968de71 100644 --- a/trunk/src/app/srs_app_async_call.cpp +++ b/trunk/src/app/srs_app_async_call.cpp @@ -31,65 +31,64 @@ using namespace std; // the sleep interval for http async callback. #define SRS_AUTO_ASYNC_CALLBACL_SLEEP_US 300000 -ISrsDvrAsyncCall::ISrsDvrAsyncCall() +ISrsAsyncCallTask::ISrsAsyncCallTask() { } -ISrsDvrAsyncCall::~ISrsDvrAsyncCall() +ISrsAsyncCallTask::~ISrsAsyncCallTask() { } -SrsDvrAsyncCallThread::SrsDvrAsyncCallThread() +SrsAsyncCallWorker::SrsAsyncCallWorker() { - pthread = new SrsThread("async", this, SRS_AUTO_ASYNC_CALLBACL_SLEEP_US, true); + pthread = new SrsReusableThread("async", this, SRS_AUTO_ASYNC_CALLBACL_SLEEP_US); } -SrsDvrAsyncCallThread::~SrsDvrAsyncCallThread() +SrsAsyncCallWorker::~SrsAsyncCallWorker() { - stop(); srs_freep(pthread); - std::vector::iterator it; - for (it = callbacks.begin(); it != callbacks.end(); ++it) { - ISrsDvrAsyncCall* call = *it; - srs_freep(call); + std::vector::iterator it; + for (it = tasks.begin(); it != tasks.end(); ++it) { + ISrsAsyncCallTask* task = *it; + srs_freep(task); } - callbacks.clear(); + tasks.clear(); } -int SrsDvrAsyncCallThread::call(ISrsDvrAsyncCall* c) +int SrsAsyncCallWorker::execute(ISrsAsyncCallTask* t) { int ret = ERROR_SUCCESS; - callbacks.push_back(c); + tasks.push_back(t); return ret; } -int SrsDvrAsyncCallThread::start() +int SrsAsyncCallWorker::start() { return pthread->start(); } -void SrsDvrAsyncCallThread::stop() +void SrsAsyncCallWorker::stop() { pthread->stop(); } -int SrsDvrAsyncCallThread::cycle() +int SrsAsyncCallWorker::cycle() { int ret = ERROR_SUCCESS; - std::vector copies = callbacks; - callbacks.clear(); + std::vector copies = tasks; + tasks.clear(); - std::vector::iterator it; + std::vector::iterator it; for (it = copies.begin(); it != copies.end(); ++it) { - ISrsDvrAsyncCall* call = *it; - if ((ret = call->call()) != ERROR_SUCCESS) { - srs_warn("ignore async callback %s, ret=%d", call->to_string().c_str(), ret); + ISrsAsyncCallTask* task = *it; + if ((ret = task->call()) != ERROR_SUCCESS) { + srs_warn("ignore async callback %s, ret=%d", task->to_string().c_str(), ret); } - srs_freep(call); + srs_freep(task); } return ret; diff --git a/trunk/src/app/srs_app_async_call.hpp b/trunk/src/app/srs_app_async_call.hpp index e014e3cc7..42f334ff8 100644 --- a/trunk/src/app/srs_app_async_call.hpp +++ b/trunk/src/app/srs_app_async_call.hpp @@ -42,32 +42,36 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * a video and pass it to the dvr again. * futhurmore, the aync call never block the main worker thread. */ -class ISrsDvrAsyncCall +class ISrsAsyncCallTask { public: - ISrsDvrAsyncCall(); - virtual ~ISrsDvrAsyncCall(); + ISrsAsyncCallTask(); + virtual ~ISrsAsyncCallTask(); public: virtual int call() = 0; virtual std::string to_string() = 0; }; /** -* the async callback for dvr. -*/ -class SrsDvrAsyncCallThread : public ISrsThreadHandler + * the async callback for dvr. + * when worker call with the task, the worker will do it in isolate thread. + * that is, the task is execute/call in async mode. + */ +class SrsAsyncCallWorker : public ISrsReusableThreadHandler { private: - SrsThread* pthread; - std::vector callbacks; + SrsReusableThread* pthread; + std::vector tasks; public: - SrsDvrAsyncCallThread(); - virtual ~SrsDvrAsyncCallThread(); + SrsAsyncCallWorker(); + virtual ~SrsAsyncCallWorker(); public: - virtual int call(ISrsDvrAsyncCall* c); + virtual int execute(ISrsAsyncCallTask* t); public: virtual int start(); virtual void stop(); +// interface ISrsReusableThreadHandler +public: virtual int cycle(); }; diff --git a/trunk/src/app/srs_app_conn.cpp b/trunk/src/app/srs_app_conn.cpp index 4c483ba1c..a03a17489 100644 --- a/trunk/src/app/srs_app_conn.cpp +++ b/trunk/src/app/srs_app_conn.cpp @@ -45,12 +45,17 @@ SrsConnection::SrsConnection(IConnectionManager* cm, st_netfd_t c) // so we never use joinable. // TODO: FIXME: maybe other thread need to stop it. // @see: https://github.com/simple-rtmp-server/srs/issues/78 - pthread = new SrsThread("conn", this, 0, false); + pthread = new SrsOneCycleThread("conn", this); } SrsConnection::~SrsConnection() { - stop(); + /** + * when delete the connection, stop the connection, + * close the underlayer socket, delete the thread. + */ + srs_close_stfd(stfd); + srs_freep(pthread); } int SrsConnection::start() @@ -83,9 +88,6 @@ int SrsConnection::cycle() if (ret == ERROR_SOCKET_CLOSED) { srs_warn("client disconnect peer. ret=%d", ret); } - - // set loop to stop to quit. - pthread->stop_loop(); return ERROR_SUCCESS; } @@ -101,10 +103,4 @@ int SrsConnection::srs_id() return id; } -void SrsConnection::stop() -{ - srs_close_stfd(stfd); - srs_freep(pthread); -} - diff --git a/trunk/src/app/srs_app_conn.hpp b/trunk/src/app/srs_app_conn.hpp index 29a6eace7..e00add8ce 100644 --- a/trunk/src/app/srs_app_conn.hpp +++ b/trunk/src/app/srs_app_conn.hpp @@ -58,14 +58,14 @@ public: * all connections accept from listener must extends from this base class, * server will add the connection to manager, and delete it when remove. */ -class SrsConnection : public virtual ISrsThreadHandler, public virtual IKbpsDelta +class SrsConnection : public virtual ISrsOneCycleThreadHandler, public virtual IKbpsDelta { private: /** * each connection start a green thread, * when thread stop, the connection will be delete by server. */ - SrsThread* pthread; + SrsOneCycleThread* pthread; /** * the id of connection. */ @@ -97,6 +97,8 @@ public: * to remove the client by server->remove(this). */ virtual int start(); +// interface ISrsOneCycleThreadHandler +public: /** * the thread cycle function, * when serve connection completed, terminate the loop which will terminate the thread, @@ -119,12 +121,6 @@ protected: * for concrete connection to do the cycle. */ virtual int do_cycle() = 0; -private: - /** - * when delete the connection, stop the connection, - * close the underlayer socket, delete the thread. - */ - virtual void stop(); }; #endif diff --git a/trunk/src/app/srs_app_dvr.cpp b/trunk/src/app/srs_app_dvr.cpp index 10725fe75..1ed737f44 100644 --- a/trunk/src/app/srs_app_dvr.cpp +++ b/trunk/src/app/srs_app_dvr.cpp @@ -498,12 +498,13 @@ int SrsFlvSegment::on_reload_vhost_dvr(std::string /*vhost*/) SrsDvrAsyncCallOnDvr::SrsDvrAsyncCallOnDvr(SrsRequest* r, string p) { - req = r; + req = r->copy(); path = p; } SrsDvrAsyncCallOnDvr::~SrsDvrAsyncCallOnDvr() { + srs_freep(req); } int SrsDvrAsyncCallOnDvr::call() @@ -547,7 +548,7 @@ SrsDvrPlan::SrsDvrPlan() dvr_enabled = false; segment = new SrsFlvSegment(this); - async = new SrsDvrAsyncCallThread(); + async = new SrsAsyncCallWorker(); } SrsDvrPlan::~SrsDvrPlan() @@ -628,7 +629,7 @@ int SrsDvrPlan::on_reap_segment() { int ret = ERROR_SUCCESS; - if ((ret = async->call(new SrsDvrAsyncCallOnDvr(req, segment->get_path()))) != ERROR_SUCCESS) { + if ((ret = async->execute(new SrsDvrAsyncCallOnDvr(req, segment->get_path()))) != ERROR_SUCCESS) { return ret; } diff --git a/trunk/src/app/srs_app_dvr.hpp b/trunk/src/app/srs_app_dvr.hpp index 4fb653a20..42e3521a1 100644 --- a/trunk/src/app/srs_app_dvr.hpp +++ b/trunk/src/app/srs_app_dvr.hpp @@ -178,7 +178,7 @@ public: /** * the dvr async call. */ -class SrsDvrAsyncCallOnDvr : public ISrsDvrAsyncCall +class SrsDvrAsyncCallOnDvr : public ISrsAsyncCallTask { private: std::string path; @@ -206,7 +206,7 @@ public: SrsRequest* req; protected: SrsFlvSegment* segment; - SrsDvrAsyncCallThread* async; + SrsAsyncCallWorker* async; bool dvr_enabled; public: SrsDvrPlan(); diff --git a/trunk/src/app/srs_app_edge.cpp b/trunk/src/app/srs_app_edge.cpp index 3a8483be9..c95480c15 100644 --- a/trunk/src/app/srs_app_edge.cpp +++ b/trunk/src/app/srs_app_edge.cpp @@ -70,7 +70,7 @@ SrsEdgeIngester::SrsEdgeIngester() origin_index = 0; stream_id = 0; stfd = NULL; - pthread = new SrsThread("edge-igs", this, SRS_EDGE_INGESTER_SLEEP_US, true); + pthread = new SrsReusableThread2("edge-igs", this, SRS_EDGE_INGESTER_SLEEP_US); } SrsEdgeIngester::~SrsEdgeIngester() @@ -171,7 +171,7 @@ int SrsEdgeIngester::ingest() SrsPithyPrint* pprint = SrsPithyPrint::create_edge(); SrsAutoFree(SrsPithyPrint, pprint); - while (pthread->can_loop()) { + while (!pthread->interrupted()) { pprint->elapse(); // pithy print @@ -397,7 +397,7 @@ SrsEdgeForwarder::SrsEdgeForwarder() origin_index = 0; stream_id = 0; stfd = NULL; - pthread = new SrsThread("edge-fwr", this, SRS_EDGE_FORWARDER_SLEEP_US, true); + pthread = new SrsReusableThread2("edge-fwr", this, SRS_EDGE_FORWARDER_SLEEP_US); queue = new SrsMessageQueue(); send_error_code = ERROR_SUCCESS; } @@ -489,7 +489,7 @@ int SrsEdgeForwarder::cycle() SrsMessageArray msgs(SYS_MAX_EDGE_SEND_MSGS); - while (pthread->can_loop()) { + while (!pthread->interrupted()) { if (send_error_code != ERROR_SUCCESS) { st_usleep(SRS_EDGE_FORWARDER_ERROR_US); continue; diff --git a/trunk/src/app/srs_app_edge.hpp b/trunk/src/app/srs_app_edge.hpp index 99147e62c..aa0cfc82d 100644 --- a/trunk/src/app/srs_app_edge.hpp +++ b/trunk/src/app/srs_app_edge.hpp @@ -75,7 +75,7 @@ enum SrsEdgeUserState /** * edge used to ingest stream from origin. */ -class SrsEdgeIngester : public ISrsThreadHandler +class SrsEdgeIngester : public ISrsReusableThread2Handler { private: int stream_id; @@ -83,7 +83,7 @@ private: SrsSource* _source; SrsPlayEdge* _edge; SrsRequest* _req; - SrsThread* pthread; + SrsReusableThread2* pthread; st_netfd_t stfd; ISrsProtocolReaderWriter* io; SrsKbps* kbps; @@ -96,7 +96,7 @@ public: virtual int initialize(SrsSource* source, SrsPlayEdge* edge, SrsRequest* req); virtual int start(); virtual void stop(); -// interface ISrsThreadHandler +// interface ISrsReusableThread2Handler public: virtual int cycle(); private: @@ -110,7 +110,7 @@ private: /** * edge used to forward stream to origin. */ -class SrsEdgeForwarder : public ISrsThreadHandler +class SrsEdgeForwarder : public ISrsReusableThread2Handler { private: int stream_id; @@ -118,7 +118,7 @@ private: SrsSource* _source; SrsPublishEdge* _edge; SrsRequest* _req; - SrsThread* pthread; + SrsReusableThread2* pthread; st_netfd_t stfd; ISrsProtocolReaderWriter* io; SrsKbps* kbps; @@ -144,7 +144,7 @@ public: virtual int initialize(SrsSource* source, SrsPublishEdge* edge, SrsRequest* req); virtual int start(); virtual void stop(); -// interface ISrsThreadHandler +// interface ISrsReusableThread2Handler public: virtual int cycle(); public: diff --git a/trunk/src/app/srs_app_encoder.cpp b/trunk/src/app/srs_app_encoder.cpp index 3bcbc230c..a64c81c6d 100644 --- a/trunk/src/app/srs_app_encoder.cpp +++ b/trunk/src/app/srs_app_encoder.cpp @@ -44,7 +44,7 @@ static std::vector _transcoded_url; SrsEncoder::SrsEncoder() { - pthread = new SrsThread("encoder", this, SRS_RTMP_ENCODER_SLEEP_US, true); + pthread = new SrsReusableThread("encoder", this, SRS_RTMP_ENCODER_SLEEP_US); pprint = SrsPithyPrint::create_encoder(); } diff --git a/trunk/src/app/srs_app_encoder.hpp b/trunk/src/app/srs_app_encoder.hpp index f728e7579..557c29201 100644 --- a/trunk/src/app/srs_app_encoder.hpp +++ b/trunk/src/app/srs_app_encoder.hpp @@ -45,13 +45,13 @@ class SrsFFMPEG; * the encoder for a stream, * may use multiple ffmpegs to transcode the specified stream. */ -class SrsEncoder : public ISrsThreadHandler +class SrsEncoder : public ISrsReusableThreadHandler { private: std::string input_stream_name; std::vector ffmpegs; private: - SrsThread* pthread; + SrsReusableThread* pthread; SrsPithyPrint* pprint; public: SrsEncoder(); @@ -59,7 +59,7 @@ public: public: virtual int on_publish(SrsRequest* req); virtual void on_unpublish(); -// interface ISrsThreadHandler. +// interface ISrsReusableThreadHandler. public: virtual int cycle(); virtual void on_thread_stop(); diff --git a/trunk/src/app/srs_app_forward.cpp b/trunk/src/app/srs_app_forward.cpp index 1d77d8854..4e68c0e9f 100644 --- a/trunk/src/app/srs_app_forward.cpp +++ b/trunk/src/app/srs_app_forward.cpp @@ -59,7 +59,7 @@ SrsForwarder::SrsForwarder(SrsSource* _source) kbps = new SrsKbps(); stream_id = 0; - pthread = new SrsThread("forward", this, SRS_FORWARDER_SLEEP_US, true); + pthread = new SrsReusableThread2("forward", this, SRS_FORWARDER_SLEEP_US); queue = new SrsMessageQueue(); jitter = new SrsRtmpJitter(); @@ -407,7 +407,7 @@ int SrsForwarder::forward() } } - while (pthread->can_loop()) { + while (!pthread->interrupted()) { pprint->elapse(); // read from client. diff --git a/trunk/src/app/srs_app_forward.hpp b/trunk/src/app/srs_app_forward.hpp index dbad1c827..68b4314ab 100644 --- a/trunk/src/app/srs_app_forward.hpp +++ b/trunk/src/app/srs_app_forward.hpp @@ -48,7 +48,7 @@ class SrsKbps; * forward the stream to other servers. */ // TODO: FIXME: refine the error log, comments it. -class SrsForwarder : public ISrsThreadHandler +class SrsForwarder : public ISrsReusableThread2Handler { private: // the ep to forward, server[:port]. @@ -57,7 +57,7 @@ private: int stream_id; private: st_netfd_t stfd; - SrsThread* pthread; + SrsReusableThread2* pthread; private: SrsSource* source; ISrsProtocolReaderWriter* io; @@ -95,7 +95,7 @@ public: * @param shared_video, directly ptr, copy it if need to save it. */ virtual int on_video(SrsSharedPtrMessage* shared_video); -// interface ISrsThreadHandler. +// interface ISrsReusableThread2Handler. public: virtual int cycle(); private: diff --git a/trunk/src/app/srs_app_heartbeat.cpp b/trunk/src/app/srs_app_heartbeat.cpp index ecb9b9196..165e38e90 100644 --- a/trunk/src/app/srs_app_heartbeat.cpp +++ b/trunk/src/app/srs_app_heartbeat.cpp @@ -33,9 +33,9 @@ using namespace std; #include #include #include -#include #include #include +#include SrsHttpHeartbeat::SrsHttpHeartbeat() { @@ -82,14 +82,14 @@ void SrsHttpHeartbeat::heartbeat() return; } - SrsHttpMessage* msg = NULL; + ISrsHttpMessage* msg = NULL; if ((ret = http.post(uri.get_path(), req, &msg)) != ERROR_SUCCESS) { srs_info("http post hartbeart uri failed. " "url=%s, request=%s, response=%s, ret=%d", url.c_str(), req.c_str(), res.c_str(), ret); return; } - SrsAutoFree(SrsHttpMessage, msg); + SrsAutoFree(ISrsHttpMessage, msg); std::string res; if ((ret = msg->body_read_all(res)) != ERROR_SUCCESS) { diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index cf322b22b..0ec4caedf 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -172,7 +172,7 @@ void SrsHlsSegment::update_duration(int64_t current_frame_dts) SrsDvrAsyncCallOnHls::SrsDvrAsyncCallOnHls(SrsRequest* r, string p, string t, string m, string mu, int s, double d) { - req = r; + req = r->copy(); path = p; ts_url = t; m3u8 = m; @@ -183,6 +183,7 @@ SrsDvrAsyncCallOnHls::SrsDvrAsyncCallOnHls(SrsRequest* r, string p, string t, st SrsDvrAsyncCallOnHls::~SrsDvrAsyncCallOnHls() { + srs_freep(req); } int SrsDvrAsyncCallOnHls::call() @@ -221,12 +222,13 @@ string SrsDvrAsyncCallOnHls::to_string() SrsDvrAsyncCallOnHlsNotify::SrsDvrAsyncCallOnHlsNotify(SrsRequest* r, string u) { - req = r; + req = r->copy(); ts_url = u; } SrsDvrAsyncCallOnHlsNotify::~SrsDvrAsyncCallOnHlsNotify() { + srs_freep(req); } int SrsDvrAsyncCallOnHlsNotify::call() @@ -284,7 +286,7 @@ SrsHlsMuxer::SrsHlsMuxer() acodec = SrsCodecAudioReserved1; should_write_cache = false; should_write_file = true; - async = new SrsDvrAsyncCallThread(); + async = new SrsAsyncCallWorker(); context = new SrsTsContext(); } @@ -667,7 +669,7 @@ int SrsHlsMuxer::segment_close(string log_desc) segments.push_back(current); // use async to call the http hooks, for it will cause thread switch. - if ((ret = async->call(new SrsDvrAsyncCallOnHls(req, + if ((ret = async->execute(new SrsDvrAsyncCallOnHls(req, current->full_path, current->uri, m3u8, m3u8_url, current->sequence_no, current->duration))) != ERROR_SUCCESS) { @@ -675,7 +677,7 @@ int SrsHlsMuxer::segment_close(string log_desc) } // use async to call the http hooks, for it will cause thread switch. - if ((ret = async->call(new SrsDvrAsyncCallOnHlsNotify(req, current->uri))) != ERROR_SUCCESS) { + if ((ret = async->execute(new SrsDvrAsyncCallOnHlsNotify(req, current->uri))) != ERROR_SUCCESS) { return ret; } diff --git a/trunk/src/app/srs_app_hls.hpp b/trunk/src/app/srs_app_hls.hpp index a79872a10..ccfd72a7e 100644 --- a/trunk/src/app/srs_app_hls.hpp +++ b/trunk/src/app/srs_app_hls.hpp @@ -159,7 +159,7 @@ public: /** * the hls async call: on_hls */ -class SrsDvrAsyncCallOnHls : public ISrsDvrAsyncCall +class SrsDvrAsyncCallOnHls : public ISrsAsyncCallTask { private: std::string path; @@ -180,7 +180,7 @@ public: /** * the hls async call: on_hls_notify */ -class SrsDvrAsyncCallOnHlsNotify : public ISrsDvrAsyncCall +class SrsDvrAsyncCallOnHlsNotify : public ISrsAsyncCallTask { private: std::string ts_url; @@ -215,7 +215,7 @@ private: double hls_aof_ratio; double hls_fragment; double hls_window; - SrsDvrAsyncCallThread* async; + SrsAsyncCallWorker* async; private: // whether use floor algorithm for timestamp. bool hls_ts_floor; diff --git a/trunk/src/app/srs_app_http.cpp b/trunk/src/app/srs_app_http.cpp deleted file mode 100644 index ff7bf19b9..000000000 --- a/trunk/src/app/srs_app_http.cpp +++ /dev/null @@ -1,1700 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013-2015 SRS(simple-rtmp-server) - -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. -*/ - -#include - -#ifdef SRS_AUTO_HTTP_PARSER - -#include -#include -#include -using namespace std; - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define SRS_DEFAULT_HTTP_PORT 80 - -// for http parser macros -#define SRS_CONSTS_HTTP_OPTIONS HTTP_OPTIONS -#define SRS_CONSTS_HTTP_GET HTTP_GET -#define SRS_CONSTS_HTTP_POST HTTP_POST -#define SRS_CONSTS_HTTP_PUT HTTP_PUT -#define SRS_CONSTS_HTTP_DELETE HTTP_DELETE - -// for ead all of http body, read each time. -#define SRS_HTTP_READ_CACHE_BYTES 4096 - -#define SRS_HTTP_DEFAULT_PAGE "index.html" - -int srs_go_http_response_json(ISrsHttpResponseWriter* w, string data) -{ - w->header()->set_content_length(data.length()); - w->header()->set_content_type("application/json"); - - return w->write((char*)data.data(), (int)data.length()); -} - -// get the status text of code. -string srs_generate_http_status_text(int status) -{ - static std::map _status_map; - if (_status_map.empty()) { - _status_map[SRS_CONSTS_HTTP_Continue ] = SRS_CONSTS_HTTP_Continue_str ; - _status_map[SRS_CONSTS_HTTP_SwitchingProtocols ] = SRS_CONSTS_HTTP_SwitchingProtocols_str ; - _status_map[SRS_CONSTS_HTTP_OK ] = SRS_CONSTS_HTTP_OK_str ; - _status_map[SRS_CONSTS_HTTP_Created ] = SRS_CONSTS_HTTP_Created_str ; - _status_map[SRS_CONSTS_HTTP_Accepted ] = SRS_CONSTS_HTTP_Accepted_str ; - _status_map[SRS_CONSTS_HTTP_NonAuthoritativeInformation ] = SRS_CONSTS_HTTP_NonAuthoritativeInformation_str ; - _status_map[SRS_CONSTS_HTTP_NoContent ] = SRS_CONSTS_HTTP_NoContent_str ; - _status_map[SRS_CONSTS_HTTP_ResetContent ] = SRS_CONSTS_HTTP_ResetContent_str ; - _status_map[SRS_CONSTS_HTTP_PartialContent ] = SRS_CONSTS_HTTP_PartialContent_str ; - _status_map[SRS_CONSTS_HTTP_MultipleChoices ] = SRS_CONSTS_HTTP_MultipleChoices_str ; - _status_map[SRS_CONSTS_HTTP_MovedPermanently ] = SRS_CONSTS_HTTP_MovedPermanently_str ; - _status_map[SRS_CONSTS_HTTP_Found ] = SRS_CONSTS_HTTP_Found_str ; - _status_map[SRS_CONSTS_HTTP_SeeOther ] = SRS_CONSTS_HTTP_SeeOther_str ; - _status_map[SRS_CONSTS_HTTP_NotModified ] = SRS_CONSTS_HTTP_NotModified_str ; - _status_map[SRS_CONSTS_HTTP_UseProxy ] = SRS_CONSTS_HTTP_UseProxy_str ; - _status_map[SRS_CONSTS_HTTP_TemporaryRedirect ] = SRS_CONSTS_HTTP_TemporaryRedirect_str ; - _status_map[SRS_CONSTS_HTTP_BadRequest ] = SRS_CONSTS_HTTP_BadRequest_str ; - _status_map[SRS_CONSTS_HTTP_Unauthorized ] = SRS_CONSTS_HTTP_Unauthorized_str ; - _status_map[SRS_CONSTS_HTTP_PaymentRequired ] = SRS_CONSTS_HTTP_PaymentRequired_str ; - _status_map[SRS_CONSTS_HTTP_Forbidden ] = SRS_CONSTS_HTTP_Forbidden_str ; - _status_map[SRS_CONSTS_HTTP_NotFound ] = SRS_CONSTS_HTTP_NotFound_str ; - _status_map[SRS_CONSTS_HTTP_MethodNotAllowed ] = SRS_CONSTS_HTTP_MethodNotAllowed_str ; - _status_map[SRS_CONSTS_HTTP_NotAcceptable ] = SRS_CONSTS_HTTP_NotAcceptable_str ; - _status_map[SRS_CONSTS_HTTP_ProxyAuthenticationRequired ] = SRS_CONSTS_HTTP_ProxyAuthenticationRequired_str ; - _status_map[SRS_CONSTS_HTTP_RequestTimeout ] = SRS_CONSTS_HTTP_RequestTimeout_str ; - _status_map[SRS_CONSTS_HTTP_Conflict ] = SRS_CONSTS_HTTP_Conflict_str ; - _status_map[SRS_CONSTS_HTTP_Gone ] = SRS_CONSTS_HTTP_Gone_str ; - _status_map[SRS_CONSTS_HTTP_LengthRequired ] = SRS_CONSTS_HTTP_LengthRequired_str ; - _status_map[SRS_CONSTS_HTTP_PreconditionFailed ] = SRS_CONSTS_HTTP_PreconditionFailed_str ; - _status_map[SRS_CONSTS_HTTP_RequestEntityTooLarge ] = SRS_CONSTS_HTTP_RequestEntityTooLarge_str ; - _status_map[SRS_CONSTS_HTTP_RequestURITooLarge ] = SRS_CONSTS_HTTP_RequestURITooLarge_str ; - _status_map[SRS_CONSTS_HTTP_UnsupportedMediaType ] = SRS_CONSTS_HTTP_UnsupportedMediaType_str ; - _status_map[SRS_CONSTS_HTTP_RequestedRangeNotSatisfiable ] = SRS_CONSTS_HTTP_RequestedRangeNotSatisfiable_str ; - _status_map[SRS_CONSTS_HTTP_ExpectationFailed ] = SRS_CONSTS_HTTP_ExpectationFailed_str ; - _status_map[SRS_CONSTS_HTTP_InternalServerError ] = SRS_CONSTS_HTTP_InternalServerError_str ; - _status_map[SRS_CONSTS_HTTP_NotImplemented ] = SRS_CONSTS_HTTP_NotImplemented_str ; - _status_map[SRS_CONSTS_HTTP_BadGateway ] = SRS_CONSTS_HTTP_BadGateway_str ; - _status_map[SRS_CONSTS_HTTP_ServiceUnavailable ] = SRS_CONSTS_HTTP_ServiceUnavailable_str ; - _status_map[SRS_CONSTS_HTTP_GatewayTimeout ] = SRS_CONSTS_HTTP_GatewayTimeout_str ; - _status_map[SRS_CONSTS_HTTP_HTTPVersionNotSupported ] = SRS_CONSTS_HTTP_HTTPVersionNotSupported_str ; - } - - std::string status_text; - if (_status_map.find(status) == _status_map.end()) { - status_text = "Status Unknown"; - } else { - status_text = _status_map[status]; - } - - return status_text; -} - -// bodyAllowedForStatus reports whether a given response status code -// permits a body. See RFC2616, section 4.4. -bool srs_go_http_body_allowd(int status) -{ - if (status >= 100 && status <= 199) { - return false; - } else if (status == 204 || status == 304) { - return false; - } - - return true; -} - -// DetectContentType implements the algorithm described -// at http://mimesniff.spec.whatwg.org/ to determine the -// Content-Type of the given data. It considers at most the -// first 512 bytes of data. DetectContentType always returns -// a valid MIME type: if it cannot determine a more specific one, it -// returns "application/octet-stream". -string srs_go_http_detect(char* data, int size) -{ - // detect only when data specified. - if (data) { - } - return "application/octet-stream"; // fallback -} - -// Error replies to the request with the specified error message and HTTP code. -// The error message should be plain text. -int srs_go_http_error(ISrsHttpResponseWriter* w, int code, string error) -{ - int ret = ERROR_SUCCESS; - - w->header()->set_content_type("text/plain; charset=utf-8"); - w->header()->set_content_length(error.length()); - w->write_header(code); - w->write((char*)error.data(), (int)error.length()); - - return ret; -} - -SrsHttpHeader::SrsHttpHeader() -{ -} - -SrsHttpHeader::~SrsHttpHeader() -{ -} - -void SrsHttpHeader::set(string key, string value) -{ - headers[key] = value; -} - -string SrsHttpHeader::get(string key) -{ - std::string v; - - if (headers.find(key) != headers.end()) { - v = headers[key]; - } - - return v; -} - -int64_t SrsHttpHeader::content_length() -{ - std::string cl = get("Content-Length"); - - if (cl.empty()) { - return -1; - } - - return (int64_t)::atof(cl.c_str()); -} - -void SrsHttpHeader::set_content_length(int64_t size) -{ - char buf[64]; - snprintf(buf, sizeof(buf), "%"PRId64, size); - set("Content-Length", buf); -} - -string SrsHttpHeader::content_type() -{ - return get("Content-Type"); -} - -void SrsHttpHeader::set_content_type(string ct) -{ - set("Content-Type", ct); -} - -void SrsHttpHeader::write(stringstream& ss) -{ - std::map::iterator it; - for (it = headers.begin(); it != headers.end(); ++it) { - ss << it->first << ": " << it->second << SRS_HTTP_CRLF; - } -} - -ISrsHttpResponseWriter::ISrsHttpResponseWriter() -{ -} - -ISrsHttpResponseWriter::~ISrsHttpResponseWriter() -{ -} - -ISrsHttpResponseReader::ISrsHttpResponseReader() -{ -} - -ISrsHttpResponseReader::~ISrsHttpResponseReader() -{ -} - -ISrsHttpHandler::ISrsHttpHandler() -{ - entry = NULL; -} - -ISrsHttpHandler::~ISrsHttpHandler() -{ -} - -SrsHttpRedirectHandler::SrsHttpRedirectHandler(string u, int c) -{ - url = u; - code = c; -} - -SrsHttpRedirectHandler::~SrsHttpRedirectHandler() -{ -} - -int SrsHttpRedirectHandler::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) -{ - int ret = ERROR_SUCCESS; - // TODO: FIXME: implements it. - return ret; -} - -SrsHttpNotFoundHandler::SrsHttpNotFoundHandler() -{ -} - -SrsHttpNotFoundHandler::~SrsHttpNotFoundHandler() -{ -} - -int SrsHttpNotFoundHandler::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) -{ - return srs_go_http_error(w, - SRS_CONSTS_HTTP_NotFound, SRS_CONSTS_HTTP_NotFound_str); -} - -SrsHttpFileServer::SrsHttpFileServer(string root_dir) -{ - dir = root_dir; -} - -SrsHttpFileServer::~SrsHttpFileServer() -{ -} - -int SrsHttpFileServer::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) -{ - string upath = r->path(); - - // add default pages. - if (srs_string_ends_with(upath, "/")) { - upath += SRS_HTTP_DEFAULT_PAGE; - } - - string fullpath = dir + "/"; - - // remove the virtual directory. - 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; - } - - // stat current dir, if exists, return error. - 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); - } - srs_trace("http match file=%s, pattern=%s, upath=%s", - fullpath.c_str(), entry->pattern.c_str(), upath.c_str()); - - // handle file according to its extension. - // use vod stream for .flv/.fhv - if (srs_string_ends_with(fullpath, ".flv") || srs_string_ends_with(fullpath, ".fhv")) { - return serve_flv_file(w, r, fullpath); - } else if (srs_string_ends_with(fullpath, ".mp4")) { - return serve_mp4_file(w, r, fullpath); - } - - // serve common static file. - return serve_file(w, r, fullpath); -} - -int SrsHttpFileServer::serve_file(ISrsHttpResponseWriter* w, SrsHttpMessage* r, string fullpath) -{ - int ret = ERROR_SUCCESS; - - // open the target file. - SrsFileReader fs; - - if ((ret = fs.open(fullpath)) != ERROR_SUCCESS) { - srs_warn("open file %s failed, ret=%d", fullpath.c_str(), ret); - return ret; - } - - int64_t length = fs.filesize(); - - // unset the content length to encode in chunked encoding. - w->header()->set_content_length(length); - - static std::map _mime; - if (_mime.empty()) { - _mime[".ts"] = "video/MP2T"; - _mime[".flv"] = "video/x-flv"; - _mime[".m4v"] = "video/x-m4v"; - _mime[".3gpp"] = "video/3gpp"; - _mime[".3gp"] = "video/3gpp"; - _mime[".mp4"] = "video/mp4"; - _mime[".aac"] = "audio/x-aac"; - _mime[".mp3"] = "audio/mpeg"; - _mime[".m4a"] = "audio/x-m4a"; - _mime[".ogg"] = "audio/ogg"; - // @see hls-m3u8-draft-pantos-http-live-streaming-12.pdf, page 5. - _mime[".m3u8"] = "application/vnd.apple.mpegurl"; // application/x-mpegURL - _mime[".rss"] = "application/rss+xml"; - _mime[".json"] = "application/json"; - _mime[".swf"] = "application/x-shockwave-flash"; - _mime[".doc"] = "application/msword"; - _mime[".zip"] = "application/zip"; - _mime[".rar"] = "application/x-rar-compressed"; - _mime[".xml"] = "text/xml"; - _mime[".html"] = "text/html"; - _mime[".js"] = "text/javascript"; - _mime[".css"] = "text/css"; - _mime[".ico"] = "image/x-icon"; - _mime[".png"] = "image/png"; - _mime[".jpeg"] = "image/jpeg"; - _mime[".jpg"] = "image/jpeg"; - _mime[".gif"] = "image/gif"; - } - - if (true) { - size_t pos; - std::string ext = fullpath; - if ((pos = ext.rfind(".")) != string::npos) { - ext = ext.substr(pos); - } - - if (_mime.find(ext) == _mime.end()) { - w->header()->set_content_type("application/octet-stream"); - } else { - w->header()->set_content_type(_mime[ext]); - } - } - - // write body. - int64_t left = length; - if ((ret = copy(w, &fs, r, (int)left)) != ERROR_SUCCESS) { - if (!srs_is_client_gracefully_close(ret)) { - srs_error("read file=%s size=%d failed, ret=%d", fullpath.c_str(), left, ret); - } - return ret; - } - - return w->final_request(); -} - -int SrsHttpFileServer::serve_flv_file(ISrsHttpResponseWriter* w, SrsHttpMessage* r, string fullpath) -{ - std::string start = r->query_get("start"); - if (start.empty()) { - return serve_file(w, r, fullpath); - } - - int offset = ::atoi(start.c_str()); - if (offset <= 0) { - return serve_file(w, r, fullpath); - } - - return serve_flv_stream(w, r, fullpath, offset); -} - -int SrsHttpFileServer::serve_mp4_file(ISrsHttpResponseWriter* w, SrsHttpMessage* 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 - if (range.empty()) { - range = r->query_get("bytes"); - } - - // rollback to serve whole file. - size_t pos = string::npos; - if (range.empty() || (pos = range.find("-")) == string::npos) { - return serve_file(w, r, fullpath); - } - - // parse the start in query string - int start = 0; - if (pos > 0) { - start = ::atoi(range.substr(0, pos).c_str()); - } - - // parse end in query string. - int end = -1; - if (pos < range.length() - 1) { - end = ::atoi(range.substr(pos + 1).c_str()); - } - - // invalid param, serve as whole mp4 file. - if (start < 0 || (end != -1 && start > end)) { - return serve_file(w, r, fullpath); - } - - return serve_mp4_stream(w, r, fullpath, start, end); -} - -int SrsHttpFileServer::serve_flv_stream(ISrsHttpResponseWriter* w, SrsHttpMessage* r, string fullpath, int offset) -{ - return serve_file(w, r, fullpath); -} - -int SrsHttpFileServer::serve_mp4_stream(ISrsHttpResponseWriter* w, SrsHttpMessage* r, string fullpath, int start, int end) -{ - return serve_file(w, r, fullpath); -} - -int SrsHttpFileServer::copy(ISrsHttpResponseWriter* w, SrsFileReader* fs, SrsHttpMessage* r, int size) -{ - int ret = ERROR_SUCCESS; - - int left = size; - char* buf = r->http_ts_send_buffer(); - - while (left > 0) { - ssize_t nread = -1; - int max_read = srs_min(left, SRS_HTTP_TS_SEND_BUFFER_SIZE); - if ((ret = fs->read(buf, max_read, &nread)) != ERROR_SUCCESS) { - break; - } - - left -= nread; - if ((ret = w->write(buf, (int)nread)) != ERROR_SUCCESS) { - break; - } - } - - return ret; -} - -SrsHttpMuxEntry::SrsHttpMuxEntry() -{ - enabled = true; - explicit_match = false; - handler = NULL; -} - -SrsHttpMuxEntry::~SrsHttpMuxEntry() -{ - srs_freep(handler); -} - -ISrsHttpMatchHijacker::ISrsHttpMatchHijacker() -{ -} - -ISrsHttpMatchHijacker::~ISrsHttpMatchHijacker() -{ -} - -SrsHttpServeMux::SrsHttpServeMux() -{ -} - -SrsHttpServeMux::~SrsHttpServeMux() -{ - std::map::iterator it; - for (it = entries.begin(); it != entries.end(); ++it) { - SrsHttpMuxEntry* entry = it->second; - srs_freep(entry); - } - entries.clear(); - - vhosts.clear(); - hijackers.clear(); -} - -int SrsHttpServeMux::initialize() -{ - int ret = ERROR_SUCCESS; - // TODO: FIXME: implements it. - return ret; -} - -void SrsHttpServeMux::hijack(ISrsHttpMatchHijacker* h) -{ - std::vector::iterator it = ::find(hijackers.begin(), hijackers.end(), h); - if (it != hijackers.end()) { - return; - } - hijackers.push_back(h); -} - -void SrsHttpServeMux::unhijack(ISrsHttpMatchHijacker* h) -{ - std::vector::iterator it = ::find(hijackers.begin(), hijackers.end(), h); - if (it == hijackers.end()) { - return; - } - hijackers.erase(it); -} - -int SrsHttpServeMux::handle(std::string pattern, ISrsHttpHandler* handler) -{ - int ret = ERROR_SUCCESS; - - srs_assert(handler); - - if (pattern.empty()) { - ret = ERROR_HTTP_PATTERN_EMPTY; - srs_error("http: empty pattern. ret=%d", ret); - return ret; - } - - if (entries.find(pattern) != entries.end()) { - SrsHttpMuxEntry* exists = entries[pattern]; - if (exists->explicit_match) { - ret = ERROR_HTTP_PATTERN_DUPLICATED; - srs_error("http: multiple registrations for %s. ret=%d", pattern.c_str(), ret); - return ret; - } - } - - std::string vhost = pattern; - if (pattern.at(0) != '/') { - if (pattern.find("/") != string::npos) { - vhost = pattern.substr(0, pattern.find("/")); - } - vhosts[vhost] = handler; - } - - if (true) { - SrsHttpMuxEntry* entry = new SrsHttpMuxEntry(); - entry->explicit_match = true; - entry->handler = handler; - entry->pattern = pattern; - entry->handler->entry = entry; - - if (entries.find(pattern) != entries.end()) { - SrsHttpMuxEntry* exists = entries[pattern]; - srs_freep(exists); - } - entries[pattern] = entry; - } - - // Helpful behavior: - // If pattern is /tree/, insert an implicit permanent redirect for /tree. - // It can be overridden by an explicit registration. - if (pattern != "/" && !pattern.empty() && pattern.at(pattern.length() - 1) == '/') { - std::string rpattern = pattern.substr(0, pattern.length() - 1); - SrsHttpMuxEntry* entry = NULL; - - // free the exists not explicit entry - if (entries.find(rpattern) != entries.end()) { - SrsHttpMuxEntry* exists = entries[rpattern]; - if (!exists->explicit_match) { - entry = exists; - } - } - - // create implicit redirect. - if (!entry || entry->explicit_match) { - srs_freep(entry); - - entry = new SrsHttpMuxEntry(); - entry->explicit_match = false; - entry->handler = new SrsHttpRedirectHandler(pattern, SRS_CONSTS_HTTP_MovedPermanently); - entry->pattern = pattern; - entry->handler->entry = entry; - - entries[rpattern] = entry; - } - } - - return ret; -} - -int SrsHttpServeMux::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) -{ - int ret = ERROR_SUCCESS; - - ISrsHttpHandler* h = NULL; - if ((ret = find_handler(r, &h)) != ERROR_SUCCESS) { - srs_error("find handler failed. ret=%d", ret); - return ret; - } - - srs_assert(h); - if ((ret = h->serve_http(w, r)) != ERROR_SUCCESS) { - if (!srs_is_client_gracefully_close(ret)) { - srs_error("handler serve http failed. ret=%d", ret); - } - return ret; - } - - return ret; -} - -int SrsHttpServeMux::find_handler(SrsHttpMessage* r, ISrsHttpHandler** ph) -{ - int ret = ERROR_SUCCESS; - - // TODO: FIXME: support the path . and .. - if (r->url().find("..") != std::string::npos) { - ret = ERROR_HTTP_URL_NOT_CLEAN; - srs_error("htt url not canonical, url=%s. ret=%d", r->url().c_str(), ret); - return ret; - } - - if ((ret = match(r, ph)) != ERROR_SUCCESS) { - srs_error("http match handler failed. ret=%d", ret); - return ret; - } - - // always hijack. - if (!hijackers.empty()) { - // notice all hijacker the match failed. - std::vector::iterator it; - for (it = hijackers.begin(); it != hijackers.end(); ++it) { - ISrsHttpMatchHijacker* hijacker = *it; - if ((ret = hijacker->hijack(r, ph)) != ERROR_SUCCESS) { - srs_error("hijacker match failed. ret=%d", ret); - return ret; - } - } - } - - if (*ph == NULL) { - // TODO: FIXME: memory leak. - *ph = new SrsHttpNotFoundHandler(); - } - - return ret; -} - -int SrsHttpServeMux::match(SrsHttpMessage* r, ISrsHttpHandler** ph) -{ - int ret = ERROR_SUCCESS; - - std::string path = r->path(); - - // Host-specific pattern takes precedence over generic ones - if (!vhosts.empty() && vhosts.find(r->host()) != vhosts.end()) { - path = r->host() + path; - } - - int nb_matched = 0; - ISrsHttpHandler* h = NULL; - - std::map::iterator it; - for (it = entries.begin(); it != entries.end(); ++it) { - std::string pattern = it->first; - SrsHttpMuxEntry* entry = it->second; - - if (!entry->enabled) { - continue; - } - - if (!path_match(pattern, path)) { - continue; - } - - if (!h || (int)pattern.length() > nb_matched) { - nb_matched = (int)pattern.length(); - h = entry->handler; - } - } - - *ph = h; - - return ret; -} - -bool SrsHttpServeMux::path_match(string pattern, string path) -{ - if (pattern.empty()) { - return false; - } - - int n = (int)pattern.length(); - - // not endswith '/', exactly match. - if (pattern.at(n - 1) != '/') { - return pattern == path; - } - - // endswith '/', match any, - // for example, '/api/' match '/api/[N]' - if ((int)path.length() >= n) { - if (memcmp(pattern.data(), path.data(), n) == 0) { - return true; - } - } - - return false; -} - -SrsHttpResponseWriter::SrsHttpResponseWriter(SrsStSocket* io) -{ - skt = io; - hdr = new SrsHttpHeader(); - header_wrote = false; - status = SRS_CONSTS_HTTP_OK; - content_length = -1; - written = 0; - header_sent = false; -} - -SrsHttpResponseWriter::~SrsHttpResponseWriter() -{ - srs_freep(hdr); -} - -int SrsHttpResponseWriter::final_request() -{ - // complete the chunked encoding. - if (content_length == -1) { - std::stringstream ss; - ss << 0 << SRS_HTTP_CRLF << SRS_HTTP_CRLF; - std::string ch = ss.str(); - return skt->write((void*)ch.data(), (int)ch.length(), NULL); - } - - // flush when send with content length - return write(NULL, 0); -} - -SrsHttpHeader* SrsHttpResponseWriter::header() -{ - return hdr; -} - -int SrsHttpResponseWriter::write(char* data, int size) -{ - int ret = ERROR_SUCCESS; - - if (!header_wrote) { - write_header(SRS_CONSTS_HTTP_OK); - } - - written += size; - if (content_length != -1 && written > content_length) { - ret = ERROR_HTTP_CONTENT_LENGTH; - srs_error("http: exceed content length. ret=%d", ret); - return ret; - } - - if ((ret = send_header(data, size)) != ERROR_SUCCESS) { - srs_error("http: send header failed. ret=%d", ret); - return ret; - } - - // ignore NULL content. - if (!data) { - return ret; - } - - // directly send with content length - if (content_length != -1) { - return skt->write((void*)data, size, NULL); - } - - // send in chunked encoding. - std::stringstream ss; - ss << hex << size << SRS_HTTP_CRLF; - std::string ch = ss.str(); - if ((ret = skt->write((void*)ch.data(), (int)ch.length(), NULL)) != ERROR_SUCCESS) { - return ret; - } - if ((ret = skt->write((void*)data, size, NULL)) != ERROR_SUCCESS) { - return ret; - } - if ((ret = skt->write((void*)SRS_HTTP_CRLF, 2, NULL)) != ERROR_SUCCESS) { - return ret; - } - - return ret; -} - -void SrsHttpResponseWriter::write_header(int code) -{ - if (header_wrote) { - srs_warn("http: multiple write_header calls, code=%d", code); - return; - } - - header_wrote = true; - status = code; - - // parse the content length from header. - content_length = hdr->content_length(); -} - -int SrsHttpResponseWriter::send_header(char* data, int size) -{ - int ret = ERROR_SUCCESS; - - if (header_sent) { - return ret; - } - header_sent = true; - - std::stringstream ss; - - // status_line - ss << "HTTP/1.1 " << status << " " - << srs_generate_http_status_text(status) << SRS_HTTP_CRLF; - - // detect content type - if (srs_go_http_body_allowd(status)) { - if (hdr->content_type().empty()) { - hdr->set_content_type(srs_go_http_detect(data, size)); - } - } - - // set server if not set. - if (hdr->get("Server").empty()) { - hdr->set("Server", RTMP_SIG_SRS_KEY"/"RTMP_SIG_SRS_VERSION); - } - - // chunked encoding - if (content_length == -1) { - hdr->set("Transfer-Encoding", "chunked"); - } - - // keep alive to make vlc happy. - hdr->set("Connection", "Keep-Alive"); - - // write headers - hdr->write(ss); - - // header_eof - ss << SRS_HTTP_CRLF; - - std::string buf = ss.str(); - return skt->write((void*)buf.c_str(), buf.length(), NULL); -} - -SrsHttpResponseReader::SrsHttpResponseReader(SrsHttpMessage* msg, SrsStSocket* io) -{ - skt = io; - owner = msg; - is_eof = false; - nb_total_read = 0; - nb_left_chunk = 0; - buffer = NULL; -} - -SrsHttpResponseReader::~SrsHttpResponseReader() -{ -} - -int SrsHttpResponseReader::initialize(SrsFastBuffer* body) -{ - int ret = ERROR_SUCCESS; - - nb_chunk = 0; - nb_left_chunk = 0; - nb_total_read = 0; - buffer = body; - - return ret; -} - -bool SrsHttpResponseReader::eof() -{ - return is_eof; -} - -int SrsHttpResponseReader::read(char* data, int nb_data, int* nb_read) -{ - int ret = ERROR_SUCCESS; - - if (is_eof) { - ret = ERROR_HTTP_RESPONSE_EOF; - srs_error("http: response EOF. ret=%d", ret); - return ret; - } - - // chunked encoding. - if (owner->is_chunked()) { - return read_chunked(data, nb_data, nb_read); - } - - // read by specified content-length - int max = (int)owner->content_length() - (int)nb_total_read; - if (max <= 0) { - is_eof = true; - return ret; - } - - // change the max to read. - nb_data = srs_min(nb_data, max); - return read_specified(data, nb_data, nb_read); -} - -int SrsHttpResponseReader::read_chunked(char* data, int nb_data, int* nb_read) -{ - int ret = ERROR_SUCCESS; - - // when no bytes left in chunk, - // parse the chunk length first. - if (nb_left_chunk <= 0) { - char* at = NULL; - int length = 0; - while (!at) { - // find the CRLF of chunk header end. - char* start = buffer->bytes(); - char* end = start + buffer->size(); - for (char* p = start; p < end - 1; p++) { - if (p[0] == SRS_HTTP_CR && p[1] == SRS_HTTP_LF) { - // invalid chunk, ignore. - if (p == start) { - ret = ERROR_HTTP_INVALID_CHUNK_HEADER; - srs_error("chunk header start with CRLF. ret=%d", ret); - return ret; - } - length = (int)(p - start + 2); - at = buffer->read_slice(length); - break; - } - } - - // got at, ok. - if (at) { - break; - } - - // when empty, only grow 1bytes, but the buffer will cache more. - if ((ret = buffer->grow(skt, buffer->size() + 1)) != ERROR_SUCCESS) { - if (!srs_is_client_gracefully_close(ret)) { - srs_error("read body from server failed. ret=%d", ret); - } - return ret; - } - } - srs_assert(length >= 3); - - // it's ok to set the pos and pos+1 to NULL. - at[length - 1] = 0; - at[length - 2] = 0; - - // size is the bytes size, excludes the chunk header and end CRLF. - int ilength = (int)::strtol(at, NULL, 16); - if (ilength < 0) { - ret = ERROR_HTTP_INVALID_CHUNK_HEADER; - srs_error("chunk header negative, length=%d. ret=%d", ilength, ret); - return ret; - } - - // all bytes in chunk is left now. - nb_chunk = nb_left_chunk = ilength; - } - - if (nb_chunk <= 0) { - // for the last chunk, eof. - is_eof = true; - } else { - // for not the last chunk, there must always exists bytes. - // left bytes in chunk, read some. - srs_assert(nb_left_chunk); - - int nb_bytes = srs_min(nb_left_chunk, nb_data); - ret = read_specified(data, nb_bytes, &nb_bytes); - - // the nb_bytes used for output already read size of bytes. - if (nb_read) { - *nb_read = nb_bytes; - } - nb_left_chunk -= nb_bytes; - srs_info("http: read %d bytes of chunk", nb_bytes); - - // error or still left bytes in chunk, ignore and read in future. - if (nb_left_chunk > 0 || (ret != ERROR_SUCCESS)) { - return ret; - } - srs_info("http: read total chunk %dB", nb_chunk); - } - - // for both the last or not, the CRLF of chunk payload end. - if ((ret = buffer->grow(skt, 2)) != ERROR_SUCCESS) { - if (!srs_is_client_gracefully_close(ret)) { - srs_error("read EOF of chunk from server failed. ret=%d", ret); - } - return ret; - } - buffer->read_slice(2); - - return ret; -} - -int SrsHttpResponseReader::read_specified(char* data, int nb_data, int* nb_read) -{ - int ret = ERROR_SUCCESS; - - if (buffer->size() <= 0) { - // when empty, only grow 1bytes, but the buffer will cache more. - if ((ret = buffer->grow(skt, 1)) != ERROR_SUCCESS) { - if (!srs_is_client_gracefully_close(ret)) { - srs_error("read body from server failed. ret=%d", ret); - } - return ret; - } - } - - int nb_bytes = srs_min(nb_data, buffer->size()); - - // read data to buffer. - srs_assert(nb_bytes); - char* p = buffer->read_slice(nb_bytes); - memcpy(data, p, nb_bytes); - if (nb_read) { - *nb_read = nb_bytes; - } - - // increase the total read to determine whether EOF. - nb_total_read += nb_bytes; - - // for not chunked - if (!owner->is_chunked()) { - // when read completed, eof. - if (nb_total_read >= (int)owner->content_length()) { - is_eof = true; - } - } - - return ret; -} - -SrsHttpMessage::SrsHttpMessage(SrsStSocket* io, SrsConnection* c) -{ - conn = c; - chunked = false; - keep_alive = true; - _uri = new SrsHttpUri(); - _body = new SrsHttpResponseReader(this, io); - _http_ts_send_buffer = new char[SRS_HTTP_TS_SEND_BUFFER_SIZE]; -} - -SrsHttpMessage::~SrsHttpMessage() -{ - srs_freep(_body); - srs_freep(_uri); - srs_freep(_http_ts_send_buffer); -} - -int SrsHttpMessage::update(string url, http_parser* header, SrsFastBuffer* body, vector& headers) -{ - int ret = ERROR_SUCCESS; - - _url = url; - _header = *header; - _headers = headers; - - // whether chunked. - std::string transfer_encoding = get_request_header("Transfer-Encoding"); - chunked = (transfer_encoding == "chunked"); - - // whether keep alive. - keep_alive = http_should_keep_alive(header); - - // set the buffer. - if ((ret = _body->initialize(body)) != ERROR_SUCCESS) { - return ret; - } - - // parse uri from url. - std::string host = get_request_header("Host"); - - // donot parse the empty host for uri, - // for example, the response contains no host, - // ignore it is ok. - if (host.empty()) { - return ret; - } - - // parse uri to schema/server:port/path?query - std::string uri = "http://" + host + _url; - if ((ret = _uri->initialize(uri)) != ERROR_SUCCESS) { - return ret; - } - - // must format as key=value&...&keyN=valueN - std::string q = _uri->get_query(); - size_t pos = string::npos; - while (!q.empty()) { - std::string k = q; - if ((pos = q.find("=")) != string::npos) { - k = q.substr(0, pos); - q = q.substr(pos + 1); - } else { - q = ""; - } - - std::string v = q; - if ((pos = q.find("&")) != string::npos) { - v = q.substr(0, pos); - q = q.substr(pos + 1); - } else { - q = ""; - } - - _query[k] = v; - } - - // parse ext. - _ext = _uri->get_path(); - if ((pos = _ext.rfind(".")) != string::npos) { - _ext = _ext.substr(pos); - } else { - _ext = ""; - } - - return ret; -} - -char* SrsHttpMessage::http_ts_send_buffer() -{ - return _http_ts_send_buffer; -} - -SrsConnection* SrsHttpMessage::connection() -{ - return conn; -} - -u_int8_t SrsHttpMessage::method() -{ - return (u_int8_t)_header.method; -} - -u_int16_t SrsHttpMessage::status_code() -{ - return (u_int16_t)_header.status_code; -} - -string SrsHttpMessage::method_str() -{ - if (is_http_get()) { - return "GET"; - } - if (is_http_put()) { - return "PUT"; - } - if (is_http_post()) { - return "POST"; - } - if (is_http_delete()) { - return "DELETE"; - } - if (is_http_options()) { - return "OPTIONS"; - } - - return "OTHER"; -} - -bool SrsHttpMessage::is_http_get() -{ - return _header.method == SRS_CONSTS_HTTP_GET; -} - -bool SrsHttpMessage::is_http_put() -{ - return _header.method == SRS_CONSTS_HTTP_PUT; -} - -bool SrsHttpMessage::is_http_post() -{ - return _header.method == SRS_CONSTS_HTTP_POST; -} - -bool SrsHttpMessage::is_http_delete() -{ - return _header.method == SRS_CONSTS_HTTP_DELETE; -} - -bool SrsHttpMessage::is_http_options() -{ - return _header.method == SRS_CONSTS_HTTP_OPTIONS; -} - -bool SrsHttpMessage::is_chunked() -{ - return chunked; -} - -bool SrsHttpMessage::is_keep_alive() -{ - return keep_alive; -} - -string SrsHttpMessage::uri() -{ - std::string uri = _uri->get_schema(); - if (uri.empty()) { - uri += "http"; - } - uri += "://"; - - uri += host(); - uri += path(); - return uri; -} - -string SrsHttpMessage::url() -{ - return _uri->get_url(); -} - -string SrsHttpMessage::host() -{ - return _uri->get_host(); -} - -string SrsHttpMessage::path() -{ - return _uri->get_path(); -} - -string SrsHttpMessage::ext() -{ - return _ext; -} - -int SrsHttpMessage::body_read_all(string& body) -{ - int ret = ERROR_SUCCESS; - - // cache to read. - char* buf = new char[SRS_HTTP_READ_CACHE_BYTES]; - SrsAutoFree(char, buf); - - // whatever, read util EOF. - while (!_body->eof()) { - int nb_read = 0; - if ((ret = _body->read(buf, SRS_HTTP_READ_CACHE_BYTES, &nb_read)) != ERROR_SUCCESS) { - return ret; - } - - if (nb_read > 0) { - body.append(buf, nb_read); - } - } - - return ret; -} - -ISrsHttpResponseReader* SrsHttpMessage::body_reader() -{ - return _body; -} - -int64_t SrsHttpMessage::content_length() -{ - return _header.content_length; -} - -string SrsHttpMessage::query_get(string key) -{ - std::string v; - - if (_query.find(key) != _query.end()) { - v = _query[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) -{ - srs_assert(index < request_header_count()); - SrsHttpHeaderField item = _headers[index]; - return item.second; -} - -string SrsHttpMessage::get_request_header(string name) -{ - std::vector::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 ""; -} - -SrsRequest* SrsHttpMessage::to_request(string vhost) -{ - SrsRequest* req = new SrsRequest(); - - req->app = _uri->get_path(); - size_t pos = string::npos; - if ((pos = req->app.rfind("/")) != string::npos) { - req->stream = req->app.substr(pos + 1); - req->app = req->app.substr(0, pos); - } - if ((pos = req->stream.rfind(".")) != string::npos) { - req->stream = req->stream.substr(0, pos); - } - - req->tcUrl = "rtmp://" + vhost + req->app; - req->pageUrl = get_request_header("Referer"); - req->objectEncoding = 0; - - srs_discovery_tc_url(req->tcUrl, - req->schema, req->host, req->vhost, req->app, req->port, - req->param); - req->strip(); - - return req; -} - -SrsHttpParser::SrsHttpParser() -{ - buffer = new SrsFastBuffer(); -} - -SrsHttpParser::~SrsHttpParser() -{ - srs_freep(buffer); -} - -int SrsHttpParser::initialize(enum http_parser_type type) -{ - int ret = ERROR_SUCCESS; - - memset(&settings, 0, sizeof(settings)); - settings.on_message_begin = on_message_begin; - settings.on_url = on_url; - settings.on_header_field = on_header_field; - settings.on_header_value = on_header_value; - settings.on_headers_complete = on_headers_complete; - settings.on_body = on_body; - settings.on_message_complete = on_message_complete; - - http_parser_init(&parser, type); - // callback object ptr. - parser.data = (void*)this; - - return ret; -} - -int SrsHttpParser::parse_message(SrsStSocket* skt, SrsConnection* conn, SrsHttpMessage** ppmsg) -{ - *ppmsg = NULL; - - int ret = ERROR_SUCCESS; - - // reset request data. - field_name = ""; - field_value = ""; - expect_field_name = true; - state = SrsHttpParseStateInit; - header = http_parser(); - url = ""; - headers.clear(); - header_parsed = 0; - - // do parse - if ((ret = parse_message_imp(skt)) != ERROR_SUCCESS) { - if (!srs_is_client_gracefully_close(ret)) { - srs_error("parse http msg failed. ret=%d", ret); - } - return ret; - } - - // create msg - SrsHttpMessage* msg = new SrsHttpMessage(skt, conn); - - // initalize http msg, parse url. - if ((ret = msg->update(url, &header, buffer, headers)) != ERROR_SUCCESS) { - srs_error("initialize http msg failed. ret=%d", ret); - srs_freep(msg); - return ret; - } - - // parse ok, return the msg. - *ppmsg = msg; - - return ret; -} - -int SrsHttpParser::parse_message_imp(SrsStSocket* skt) -{ - int ret = ERROR_SUCCESS; - - while (true) { - ssize_t nparsed = 0; - - // when got entire http header, parse it. - // @see https://github.com/simple-rtmp-server/srs/issues/400 - char* start = buffer->bytes(); - char* end = start + buffer->size(); - for (char* p = start; p <= end - 4; p++) { - // SRS_HTTP_CRLFCRLF "\r\n\r\n" // 0x0D0A0D0A - if (p[0] == SRS_CONSTS_CR && p[1] == SRS_CONSTS_LF && p[2] == SRS_CONSTS_CR && p[3] == SRS_CONSTS_LF) { - nparsed = http_parser_execute(&parser, &settings, buffer->bytes(), buffer->size()); - srs_info("buffer=%d, nparsed=%d, header=%d", buffer->size(), (int)nparsed, header_parsed); - break; - } - } - - // consume the parsed bytes. - if (nparsed && header_parsed) { - buffer->read_slice(header_parsed); - } - - // ok atleast header completed, - // never wait for body completed, for maybe chunked. - if (state == SrsHttpParseStateHeaderComplete || state == SrsHttpParseStateMessageComplete) { - break; - } - - // when nothing parsed, read more to parse. - if (nparsed == 0) { - // when requires more, only grow 1bytes, but the buffer will cache more. - if ((ret = buffer->grow(skt, buffer->size() + 1)) != ERROR_SUCCESS) { - if (!srs_is_client_gracefully_close(ret)) { - srs_error("read body from server failed. ret=%d", ret); - } - return ret; - } - } - } - - // parse last header. - if (!field_name.empty() && !field_value.empty()) { - headers.push_back(std::make_pair(field_name, field_value)); - } - - return ret; -} - -int SrsHttpParser::on_message_begin(http_parser* parser) -{ - SrsHttpParser* obj = (SrsHttpParser*)parser->data; - srs_assert(obj); - - obj->state = SrsHttpParseStateStart; - - srs_info("***MESSAGE BEGIN***"); - - return 0; -} - -int SrsHttpParser::on_headers_complete(http_parser* parser) -{ - SrsHttpParser* obj = (SrsHttpParser*)parser->data; - srs_assert(obj); - - obj->header = *parser; - // save the parser when header parse completed. - obj->state = SrsHttpParseStateHeaderComplete; - obj->header_parsed = (int)parser->nread; - - srs_info("***HEADERS COMPLETE***"); - - // see http_parser.c:1570, return 1 to skip body. - return 0; -} - -int SrsHttpParser::on_message_complete(http_parser* parser) -{ - SrsHttpParser* obj = (SrsHttpParser*)parser->data; - srs_assert(obj); - - // save the parser when body parse completed. - obj->state = SrsHttpParseStateMessageComplete; - - srs_info("***MESSAGE COMPLETE***\n"); - - return 0; -} - -int SrsHttpParser::on_url(http_parser* parser, const char* at, size_t length) -{ - SrsHttpParser* obj = (SrsHttpParser*)parser->data; - srs_assert(obj); - - if (length > 0) { - obj->url.append(at, (int)length); - } - - srs_info("Method: %d, Url: %.*s", parser->method, (int)length, at); - - return 0; -} - -int SrsHttpParser::on_header_field(http_parser* parser, const char* at, size_t length) -{ - 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 = ""; - } - obj->expect_field_name = true; - - if (length > 0) { - obj->field_name.append(at, (int)length); - } - - srs_info("Header field(%d bytes): %.*s", (int)length, (int)length, at); - return 0; -} - -int SrsHttpParser::on_header_value(http_parser* parser, const char* at, size_t length) -{ - SrsHttpParser* obj = (SrsHttpParser*)parser->data; - srs_assert(obj); - - 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; -} - -int SrsHttpParser::on_body(http_parser* parser, const char* at, size_t length) -{ - SrsHttpParser* obj = (SrsHttpParser*)parser->data; - srs_assert(obj); - - srs_info("Body: %.*s", (int)length, at); - - return 0; -} - -SrsHttpUri::SrsHttpUri() -{ - port = SRS_DEFAULT_HTTP_PORT; -} - -SrsHttpUri::~SrsHttpUri() -{ -} - -int SrsHttpUri::initialize(string _url) -{ - int ret = ERROR_SUCCESS; - - url = _url; - const char* purl = url.c_str(); - - http_parser_url hp_u; - if((ret = http_parser_parse_url(purl, url.length(), 0, &hp_u)) != 0){ - int code = ret; - ret = ERROR_HTTP_PARSE_URI; - - srs_error("parse url %s failed, code=%d, ret=%d", purl, code, ret); - return ret; - } - - std::string field = get_uri_field(url, &hp_u, UF_SCHEMA); - if(!field.empty()){ - schema = field; - } - - host = get_uri_field(url, &hp_u, UF_HOST); - - field = get_uri_field(url, &hp_u, UF_PORT); - if(!field.empty()){ - port = atoi(field.c_str()); - } - - path = get_uri_field(url, &hp_u, UF_PATH); - srs_info("parse url %s success", purl); - - query = get_uri_field(url, &hp_u, UF_QUERY); - srs_info("parse query %s success", query.c_str()); - - return ret; -} - -const char* SrsHttpUri::get_url() -{ - return url.data(); -} - -const char* SrsHttpUri::get_schema() -{ - return schema.data(); -} - -const char* SrsHttpUri::get_host() -{ - return host.data(); -} - -int SrsHttpUri::get_port() -{ - return port; -} - -const char* SrsHttpUri::get_path() -{ - return path.data(); -} - -const char* SrsHttpUri::get_query() -{ - return query.data(); -} - -string SrsHttpUri::get_uri_field(string uri, http_parser_url* hp_u, http_parser_url_fields field) -{ - if((hp_u->field_set & (1 << field)) == 0){ - return ""; - } - - srs_verbose("uri field matched, off=%d, len=%d, value=%.*s", - hp_u->field_data[field].off, - hp_u->field_data[field].len, - hp_u->field_data[field].len, - uri.c_str() + hp_u->field_data[field].off); - - int offset = hp_u->field_data[field].off; - int len = hp_u->field_data[field].len; - - return uri.substr(offset, len); -} - -#endif - diff --git a/trunk/src/app/srs_app_http.hpp b/trunk/src/app/srs_app_http.hpp deleted file mode 100644 index 07b9fee08..000000000 --- a/trunk/src/app/srs_app_http.hpp +++ /dev/null @@ -1,686 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013-2015 SRS(simple-rtmp-server) - -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_APP_HTTP_HPP -#define SRS_APP_HTTP_HPP - -/* -#include -*/ -#include - -#ifdef SRS_AUTO_HTTP_PARSER - -#include -#include -#include -#include - -#include - -#include -#include - -class SrsRequest; -class SrsStSocket; -class SrsHttpUri; -class SrsHttpMessage; -class SrsFileReader; -class SrsSimpleBuffer; -class SrsHttpMuxEntry; -class ISrsHttpResponseWriter; -class SrsFastBuffer; -class SrsConnection; - -// http specification -// CR = -#define SRS_HTTP_CR SRS_CONSTS_CR // 0x0D -// LF = -#define SRS_HTTP_LF SRS_CONSTS_LF // 0x0A -// SP = -#define SRS_HTTP_SP ' ' // 0x20 -// HT = -#define SRS_HTTP_HT '\x09' // 0x09 - -// HTTP/1.1 defines the sequence CR LF as the end-of-line marker for all -// protocol elements except the entity-body (see appendix 19.3 for -// tolerant applications). -#define SRS_HTTP_CRLF "\r\n" // 0x0D0A -#define SRS_HTTP_CRLFCRLF "\r\n\r\n" // 0x0D0A0D0A - -// @see SrsHttpMessage._http_ts_send_buffer -#define SRS_HTTP_TS_SEND_BUFFER_SIZE 4096 - -// helper function: response in json format. -extern int srs_go_http_response_json(ISrsHttpResponseWriter* w, std::string data); - -// state of message -enum SrsHttpParseState { - SrsHttpParseStateInit = 0, - SrsHttpParseStateStart, - SrsHttpParseStateHeaderComplete, - SrsHttpParseStateMessageComplete -}; - -// A Header represents the key-value pairs in an HTTP header. -class SrsHttpHeader -{ -private: - std::map headers; -public: - SrsHttpHeader(); - virtual ~SrsHttpHeader(); -public: - // Add adds the key, value pair to the header. - // It appends to any existing values associated with key. - virtual void set(std::string key, std::string value); - // Get gets the first value associated with the given key. - // If there are no values associated with the key, Get returns "". - // To access multiple values of a key, access the map directly - // with CanonicalHeaderKey. - virtual std::string get(std::string key); -public: - /** - * get the content length. -1 if not set. - */ - virtual int64_t content_length(); - /** - * set the content length by header "Content-Length" - */ - virtual void set_content_length(int64_t size); -public: - /** - * get the content type. empty string if not set. - */ - virtual std::string content_type(); - /** - * set the content type by header "Content-Type" - */ - virtual void set_content_type(std::string ct); -public: - /** - * write all headers to string stream. - */ - virtual void write(std::stringstream& ss); -}; - -// A ResponseWriter interface is used by an HTTP handler to -// construct an HTTP response. -// Usage 1, response with specified length content: -// ISrsHttpResponseWriter* w; // create or get response. -// 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(SRS_CONSTS_HTTP_OK); -// w->write((char*)msg.data(), (int)msg.length()); -// w->final_request(); // optional flush. -// Usage 2, response with HTTP code only, zero content length. -// ISrsHttpResponseWriter* w; // create or get response. -// w->header()->set_content_length(0); -// w->write_header(SRS_CONSTS_HTTP_OK); -// w->final_request(); -// Usage 3, response in chunked encoding. -// ISrsHttpResponseWriter* w; // create or get response. -// std::string msg = "Hello, HTTP!"; -// w->header()->set_content_type("application/octet-stream"); -// w->write_header(SRS_CONSTS_HTTP_OK); -// 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 ISrsHttpResponseWriter -{ -public: - ISrsHttpResponseWriter(); - virtual ~ISrsHttpResponseWriter(); -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 int 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 int write(char* data, int size) = 0; - - // WriteHeader sends an HTTP response 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(int code) = 0; -}; - -/** -* the reader interface for http response. -*/ -class ISrsHttpResponseReader -{ -public: - ISrsHttpResponseReader(); - virtual ~ISrsHttpResponseReader(); -public: - /** - * whether response read EOF. - */ - virtual bool eof() = 0; - /** - * read from the response body. - * @param data, the buffer to read data buffer to. - * @param nb_data, the max size of data buffer. - * @param nb_read, the actual read size of bytes. NULL to ignore. - * @remark when eof(), return error. - */ - virtual int read(char* data, int nb_data, int* nb_read) = 0; -}; - -// Objects implementing the Handler interface can be -// registered to serve a particular path or subtree -// in the HTTP server. -// -// ServeHTTP should write reply headers and data to the ResponseWriter -// and then return. Returning signals that the request is finished -// and that the HTTP server can move on to the next request on -// the connection. -class ISrsHttpHandler -{ -public: - SrsHttpMuxEntry* entry; -public: - ISrsHttpHandler(); - virtual ~ISrsHttpHandler(); -public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) = 0; -}; - -// Redirect to a fixed URL -class SrsHttpRedirectHandler : public ISrsHttpHandler -{ -private: - std::string url; - int code; -public: - SrsHttpRedirectHandler(std::string u, int c); - virtual ~SrsHttpRedirectHandler(); -public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); -}; - -// NotFound replies to the request with an HTTP 404 not found error. -class SrsHttpNotFoundHandler : public ISrsHttpHandler -{ -public: - SrsHttpNotFoundHandler(); - virtual ~SrsHttpNotFoundHandler(); -public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); -}; - -// FileServer returns a handler that serves HTTP requests -// with the contents of the file system rooted at root. -// -// To use the operating system's file system implementation, -// use http.Dir: -// -// http.Handle("/", SrsHttpFileServer("/tmp")) -// http.Handle("/", SrsHttpFileServer("static-dir")) -class SrsHttpFileServer : public ISrsHttpHandler -{ -protected: - std::string dir; -public: - SrsHttpFileServer(std::string root_dir); - virtual ~SrsHttpFileServer(); -public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); -private: - /** - * serve the file by specified path - */ - virtual int serve_file(ISrsHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath); - virtual int serve_flv_file(ISrsHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath); - virtual int serve_mp4_file(ISrsHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath); -protected: - /** - * when access flv file with x.flv?start=xxx - */ - virtual int serve_flv_stream(ISrsHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int offset); - /** - * when access mp4 file with x.mp4?range=start-end - * @param start the start offset in bytes. - * @param end the end offset in bytes. -1 to end of file. - * @remark response data in [start, end]. - */ - virtual int serve_mp4_stream(ISrsHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int start, int end); -protected: - /** - * copy the fs to response writer in size bytes. - */ - virtual int copy(ISrsHttpResponseWriter* w, SrsFileReader* fs, SrsHttpMessage* r, int size); -}; - -// the mux entry for server mux. -// the matcher info, for example, the pattern and handler. -class SrsHttpMuxEntry -{ -public: - bool explicit_match; - ISrsHttpHandler* handler; - std::string pattern; - bool enabled; -public: - SrsHttpMuxEntry(); - virtual ~SrsHttpMuxEntry(); -}; - -/** -* the hijacker for http pattern match. -*/ -class ISrsHttpMatchHijacker -{ -public: - ISrsHttpMatchHijacker(); - virtual ~ISrsHttpMatchHijacker(); -public: - /** - * when match the request failed, no handler to process request. - * @param request the http request message to match the handler. - * @param ph the already matched handler, hijack can rewrite it. - */ - virtual int hijack(SrsHttpMessage* request, ISrsHttpHandler** ph) = 0; -}; - -// ServeMux is an HTTP request multiplexer. -// It matches the URL of each incoming request against a list of registered -// patterns and calls the handler for the pattern that -// most closely matches the URL. -// -// Patterns name fixed, rooted paths, like "/favicon.ico", -// or rooted subtrees, like "/images/" (note the trailing slash). -// Longer patterns take precedence over shorter ones, so that -// if there are handlers registered for both "/images/" -// and "/images/thumbnails/", the latter handler will be -// called for paths beginning "/images/thumbnails/" and the -// former will receive requests for any other paths in the -// "/images/" subtree. -// -// Note that since a pattern ending in a slash names a rooted subtree, -// the pattern "/" matches all paths not matched by other registered -// patterns, not just the URL with Path == "/". -// -// Patterns may optionally begin with a host name, restricting matches to -// URLs on that host only. Host-specific patterns take precedence over -// general patterns, so that a handler might register for the two patterns -// "/codesearch" and "codesearch.google.com/" without also taking over -// requests for "http://www.google.com/". -// -// ServeMux also takes care of sanitizing the URL request path, -// redirecting any request containing . or .. elements to an -// equivalent .- and ..-free URL. -class SrsHttpServeMux -{ -private: - // the pattern handler, to handle the http request. - std::map entries; - // the vhost handler. - // when find the handler to process the request, - // append the matched vhost when pattern not starts with /, - // for example, for pattern /live/livestream.flv of vhost ossrs.net, - // the path will rewrite to ossrs.net/live/livestream.flv - std::map vhosts; - // all hijackers for http match. - // for example, the hstrs(http stream trigger rtmp source) - // can hijack and install handler when request incoming and no handler. - std::vector hijackers; -public: - SrsHttpServeMux(); - virtual ~SrsHttpServeMux(); -public: - /** - * initialize the http serve mux. - */ - virtual int initialize(); - /** - * hijack the http match. - */ - virtual void hijack(ISrsHttpMatchHijacker* h); - virtual void unhijack(ISrsHttpMatchHijacker* h); -public: - // Handle registers the handler for the given pattern. - // If a handler already exists for pattern, Handle panics. - virtual int handle(std::string pattern, ISrsHttpHandler* handler); -// interface ISrsHttpHandler -public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); -private: - virtual int find_handler(SrsHttpMessage* r, ISrsHttpHandler** ph); - virtual int match(SrsHttpMessage* r, ISrsHttpHandler** ph); - virtual bool path_match(std::string pattern, std::string path); -}; - -/** -* response writer use st socket -*/ -class SrsHttpResponseWriter : public ISrsHttpResponseWriter -{ -private: - SrsStSocket* skt; - SrsHttpHeader* hdr; -private: - // reply header has been (logically) written - bool header_wrote; - // status code passed to WriteHeader - int status; -private: - // explicitly-declared Content-Length; or -1 - int64_t content_length; - // number of bytes written in body - int64_t written; -private: - // wroteHeader tells whether the header's been written to "the - // wire" (or rather: w.conn.buf). this is unlike - // (*response).wroteHeader, which tells only whether it was - // logically written. - bool header_sent; -public: - SrsHttpResponseWriter(SrsStSocket* io); - virtual ~SrsHttpResponseWriter(); -public: - virtual int final_request(); - virtual SrsHttpHeader* header(); - virtual int write(char* data, int size); - virtual void write_header(int code); - virtual int send_header(char* data, int size); -}; - -/** -* response reader use st socket. -*/ -class SrsHttpResponseReader : virtual public ISrsHttpResponseReader -{ -private: - SrsStSocket* skt; - SrsHttpMessage* owner; - SrsFastBuffer* buffer; - bool is_eof; - // the left bytes in chunk. - int nb_left_chunk; - // the number of bytes of current chunk. - int nb_chunk; - // already read total bytes. - int64_t nb_total_read; -public: - SrsHttpResponseReader(SrsHttpMessage* msg, SrsStSocket* io); - virtual ~SrsHttpResponseReader(); -public: - /** - * initialize the response reader with buffer. - */ - virtual int initialize(SrsFastBuffer* buffer); -// interface ISrsHttpResponseReader -public: - virtual bool eof(); - virtual int read(char* data, int nb_data, int* nb_read); -private: - virtual int read_chunked(char* data, int nb_data, int* nb_read); - virtual int read_specified(char* data, int nb_data, int* nb_read); -}; - -// for http header. -typedef std::pair SrsHttpHeaderField; - -// A Request represents an HTTP request received by a server -// or to be sent by a client. -// -// The field semantics differ slightly between client and server -// usage. In addition to the notes on the fields below, see the -// documentation for Request.Write and RoundTripper. -/** -* the http message, request or response. -*/ -class SrsHttpMessage -{ -private: - /** - * parsed url. - */ - std::string _url; - /** - * the extension of file, for example, .flv - */ - std::string _ext; - /** - * parsed http header. - */ - http_parser _header; - /** - * 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 request indicates should keep alive - * for the http connection. - */ - bool keep_alive; - /** - * uri parser - */ - SrsHttpUri* _uri; - /** - * use a buffer to read and send ts file. - */ - // TODO: FIXME: remove it. - char* _http_ts_send_buffer; - // http headers - std::vector _headers; - // the query map - std::map _query; - // the transport connection, can be NULL. - SrsConnection* conn; -public: - SrsHttpMessage(SrsStSocket* io, SrsConnection* c); - virtual ~SrsHttpMessage(); -public: - /** - * set the original messages, then update the message. - */ - virtual int update(std::string url, http_parser* header, - SrsFastBuffer* body, std::vector& headers - ); -public: - virtual char* http_ts_send_buffer(); - virtual SrsConnection* connection(); -public: - virtual u_int8_t method(); - virtual u_int16_t status_code(); - /** - * method helpers. - */ - virtual std::string method_str(); - virtual bool is_http_get(); - virtual bool is_http_put(); - virtual bool is_http_post(); - virtual bool is_http_delete(); - virtual bool is_http_options(); - /** - * whether body is chunked encoding, for reader only. - */ - virtual bool is_chunked(); - /** - * whether should keep the connection alive. - */ - virtual bool is_keep_alive(); - /** - * the uri contains the host and path. - */ - virtual std::string uri(); - /** - * the url maybe the path. - */ - virtual std::string url(); - virtual std::string host(); - virtual std::string path(); - virtual std::string ext(); -public: - /** - * read body to string. - * @remark for small http body. - */ - virtual int body_read_all(std::string& body); - /** - * get the body reader, to read one by one. - * @remark when body is very large, or chunked, use this. - */ - virtual ISrsHttpResponseReader* body_reader(); - /** - * the content length, -1 for chunked or not set. - */ - virtual int64_t content_length(); - /** - * get the param in query string, - * for instance, query is "start=100&end=200", - * 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); -public: - /** - * convert the http message to a request. - * @remark user must free the return request. - */ - virtual SrsRequest* to_request(std::string vhost); -}; - -/** -* wrapper for http-parser, -* provides HTTP message originted service. -*/ -class SrsHttpParser -{ -private: - http_parser_settings settings; - http_parser parser; - // the global parse buffer. - SrsFastBuffer* buffer; -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; - std::string url; - std::vector headers; - int header_parsed; -public: - SrsHttpParser(); - virtual ~SrsHttpParser(); -public: - /** - * initialize the http parser with specified type, - * one parser can only parse request or response messages. - */ - virtual int initialize(enum http_parser_type type); - /** - * always parse a http message, - * that is, the *ppmsg always NOT-NULL when return success. - * or error and *ppmsg must be NULL. - * @remark, if success, *ppmsg always NOT-NULL, *ppmsg always is_complete(). - */ - virtual int parse_message(SrsStSocket* skt, SrsConnection* conn, SrsHttpMessage** ppmsg); -private: - /** - * parse the HTTP message to member field: msg. - */ - virtual int parse_message_imp(SrsStSocket* skt); -private: - static int on_message_begin(http_parser* parser); - static int on_headers_complete(http_parser* parser); - static int on_message_complete(http_parser* parser); - static int on_url(http_parser* parser, const char* at, size_t length); - static int on_header_field(http_parser* parser, const char* at, size_t length); - static int on_header_value(http_parser* parser, const char* at, size_t length); - static int on_body(http_parser* parser, const char* at, size_t length); -}; - -/** -* used to resolve the http uri. -*/ -class SrsHttpUri -{ -private: - std::string url; - std::string schema; - std::string host; - int port; - std::string path; - std::string query; -public: - SrsHttpUri(); - virtual ~SrsHttpUri(); -public: - /** - * initialize the http uri. - */ - virtual int initialize(std::string _url); -public: - virtual const char* get_url(); - virtual const char* get_schema(); - virtual const char* get_host(); - virtual int get_port(); - virtual const char* get_path(); - virtual const char* get_query(); -private: - /** - * get the parsed url field. - * @return return empty string if not set. - */ - virtual std::string get_uri_field(std::string uri, http_parser_url* hp_u, http_parser_url_fields field); -}; - -#endif - -#endif diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index 43652643d..393943f34 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -39,6 +39,7 @@ using namespace std; #include #include #include +#include SrsGoApiRoot::SrsGoApiRoot() { @@ -48,7 +49,7 @@ SrsGoApiRoot::~SrsGoApiRoot() { } -int SrsGoApiRoot::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiRoot::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream ss; @@ -59,7 +60,7 @@ int SrsGoApiRoot::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) << SRS_JOBJECT_END << SRS_JOBJECT_END; - return srs_go_http_response_json(w, ss.str()); + return srs_http_response_json(w, ss.str()); } SrsGoApiApi::SrsGoApiApi() @@ -70,7 +71,7 @@ SrsGoApiApi::~SrsGoApiApi() { } -int SrsGoApiApi::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiApi::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream ss; @@ -81,7 +82,7 @@ int SrsGoApiApi::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) << SRS_JOBJECT_END << SRS_JOBJECT_END; - return srs_go_http_response_json(w, ss.str()); + return srs_http_response_json(w, ss.str()); } SrsGoApiV1::SrsGoApiV1() @@ -92,7 +93,7 @@ SrsGoApiV1::~SrsGoApiV1() { } -int SrsGoApiV1::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiV1::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream ss; @@ -112,7 +113,7 @@ int SrsGoApiV1::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) << SRS_JOBJECT_END << SRS_JOBJECT_END; - return srs_go_http_response_json(w, ss.str()); + return srs_http_response_json(w, ss.str()); } SrsGoApiVersion::SrsGoApiVersion() @@ -123,7 +124,7 @@ SrsGoApiVersion::~SrsGoApiVersion() { } -int SrsGoApiVersion::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiVersion::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream ss; @@ -137,7 +138,7 @@ int SrsGoApiVersion::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) << SRS_JOBJECT_END << SRS_JOBJECT_END; - return srs_go_http_response_json(w, ss.str()); + return srs_http_response_json(w, ss.str()); } SrsGoApiSummaries::SrsGoApiSummaries() @@ -148,11 +149,11 @@ SrsGoApiSummaries::~SrsGoApiSummaries() { } -int SrsGoApiSummaries::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiSummaries::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream ss; srs_api_dump_summaries(ss); - return srs_go_http_response_json(w, ss.str()); + return srs_http_response_json(w, ss.str()); } SrsGoApiRusages::SrsGoApiRusages() @@ -163,7 +164,7 @@ SrsGoApiRusages::~SrsGoApiRusages() { } -int SrsGoApiRusages::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* req) +int SrsGoApiRusages::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* req) { std::stringstream ss; @@ -193,7 +194,7 @@ int SrsGoApiRusages::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* req) << SRS_JOBJECT_END << SRS_JOBJECT_END; - return srs_go_http_response_json(w, ss.str()); + return srs_http_response_json(w, ss.str()); } SrsGoApiSelfProcStats::SrsGoApiSelfProcStats() @@ -204,7 +205,7 @@ SrsGoApiSelfProcStats::~SrsGoApiSelfProcStats() { } -int SrsGoApiSelfProcStats::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiSelfProcStats::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream ss; @@ -263,7 +264,7 @@ int SrsGoApiSelfProcStats::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* << SRS_JOBJECT_END << SRS_JOBJECT_END; - return srs_go_http_response_json(w, ss.str()); + return srs_http_response_json(w, ss.str()); } SrsGoApiSystemProcStats::SrsGoApiSystemProcStats() @@ -274,7 +275,7 @@ SrsGoApiSystemProcStats::~SrsGoApiSystemProcStats() { } -int SrsGoApiSystemProcStats::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiSystemProcStats::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream ss; @@ -298,7 +299,7 @@ int SrsGoApiSystemProcStats::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessag << SRS_JOBJECT_END << SRS_JOBJECT_END; - return srs_go_http_response_json(w, ss.str()); + return srs_http_response_json(w, ss.str()); } SrsGoApiMemInfos::SrsGoApiMemInfos() @@ -309,7 +310,7 @@ SrsGoApiMemInfos::~SrsGoApiMemInfos() { } -int SrsGoApiMemInfos::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiMemInfos::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream ss; @@ -334,7 +335,7 @@ int SrsGoApiMemInfos::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) << SRS_JOBJECT_END << SRS_JOBJECT_END; - return srs_go_http_response_json(w, ss.str()); + return srs_http_response_json(w, ss.str()); } SrsGoApiAuthors::SrsGoApiAuthors() @@ -345,7 +346,7 @@ SrsGoApiAuthors::~SrsGoApiAuthors() { } -int SrsGoApiAuthors::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiAuthors::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream ss; @@ -359,7 +360,7 @@ int SrsGoApiAuthors::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) << SRS_JOBJECT_END << SRS_JOBJECT_END; - return srs_go_http_response_json(w, ss.str()); + return srs_http_response_json(w, ss.str()); } SrsGoApiRequests::SrsGoApiRequests() @@ -370,9 +371,9 @@ SrsGoApiRequests::~SrsGoApiRequests() { } -int SrsGoApiRequests::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiRequests::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { - SrsHttpMessage* req = r; + ISrsHttpMessage* req = r; std::stringstream ss; @@ -420,7 +421,7 @@ int SrsGoApiRequests::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) << SRS_JOBJECT_END << SRS_JOBJECT_END; - return srs_go_http_response_json(w, ss.str()); + return srs_http_response_json(w, ss.str()); } SrsGoApiVhosts::SrsGoApiVhosts() @@ -431,7 +432,7 @@ SrsGoApiVhosts::~SrsGoApiVhosts() { } -int SrsGoApiVhosts::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiVhosts::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream data; SrsStatistic* stat = SrsStatistic::instance(); @@ -445,7 +446,7 @@ int SrsGoApiVhosts::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) << SRS_JFIELD_ORG("vhosts", data.str()) << SRS_JOBJECT_END; - return srs_go_http_response_json(w, ss.str()); + return srs_http_response_json(w, ss.str()); } SrsGoApiStreams::SrsGoApiStreams() @@ -456,7 +457,7 @@ SrsGoApiStreams::~SrsGoApiStreams() { } -int SrsGoApiStreams::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsGoApiStreams::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { std::stringstream data; SrsStatistic* stat = SrsStatistic::instance(); @@ -470,7 +471,7 @@ int SrsGoApiStreams::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) << SRS_JFIELD_ORG("streams", data.str()) << SRS_JOBJECT_END; - return srs_go_http_response_json(w, ss.str()); + return srs_http_response_json(w, ss.str()); } SrsHttpApi::SrsHttpApi(IConnectionManager* cm, st_netfd_t fd, SrsHttpServeMux* m) @@ -529,7 +530,7 @@ int SrsHttpApi::do_cycle() // process http messages. for (;;) { - SrsHttpMessage* req = NULL; + ISrsHttpMessage* req = NULL; // get a http message if ((ret = parser->parse_message(&skt, this, &req)) != ERROR_SUCCESS) { @@ -540,7 +541,7 @@ int SrsHttpApi::do_cycle() srs_assert(req); // always free it in this scope. - SrsAutoFree(SrsHttpMessage, req); + SrsAutoFree(ISrsHttpMessage, req); // TODO: FIXME: use the post body. std::string res; @@ -566,7 +567,7 @@ int SrsHttpApi::do_cycle() return ret; } -int SrsHttpApi::process_request(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsHttpApi::process_request(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { int ret = ERROR_SUCCESS; diff --git a/trunk/src/app/srs_app_http_api.hpp b/trunk/src/app/srs_app_http_api.hpp index 66e971088..c5f4ed462 100644 --- a/trunk/src/app/srs_app_http_api.hpp +++ b/trunk/src/app/srs_app_http_api.hpp @@ -33,13 +33,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifdef SRS_AUTO_HTTP_API class SrsStSocket; -class SrsHttpMessage; +class ISrsHttpMessage; class SrsHttpParser; class SrsHttpHandler; #include #include -#include +#include // for http root. class SrsGoApiRoot : public ISrsHttpHandler @@ -48,7 +48,7 @@ public: SrsGoApiRoot(); virtual ~SrsGoApiRoot(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiApi : public ISrsHttpHandler @@ -57,7 +57,7 @@ public: SrsGoApiApi(); virtual ~SrsGoApiApi(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiV1 : public ISrsHttpHandler @@ -66,7 +66,7 @@ public: SrsGoApiV1(); virtual ~SrsGoApiV1(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiVersion : public ISrsHttpHandler @@ -75,7 +75,7 @@ public: SrsGoApiVersion(); virtual ~SrsGoApiVersion(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiSummaries : public ISrsHttpHandler @@ -84,7 +84,7 @@ public: SrsGoApiSummaries(); virtual ~SrsGoApiSummaries(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiRusages : public ISrsHttpHandler @@ -93,7 +93,7 @@ public: SrsGoApiRusages(); virtual ~SrsGoApiRusages(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiSelfProcStats : public ISrsHttpHandler @@ -102,7 +102,7 @@ public: SrsGoApiSelfProcStats(); virtual ~SrsGoApiSelfProcStats(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiSystemProcStats : public ISrsHttpHandler @@ -111,7 +111,7 @@ public: SrsGoApiSystemProcStats(); virtual ~SrsGoApiSystemProcStats(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiMemInfos : public ISrsHttpHandler @@ -120,7 +120,7 @@ public: SrsGoApiMemInfos(); virtual ~SrsGoApiMemInfos(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiAuthors : public ISrsHttpHandler @@ -129,7 +129,7 @@ public: SrsGoApiAuthors(); virtual ~SrsGoApiAuthors(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiRequests : public ISrsHttpHandler @@ -138,7 +138,7 @@ public: SrsGoApiRequests(); virtual ~SrsGoApiRequests(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiVhosts : public ISrsHttpHandler @@ -147,7 +147,7 @@ public: SrsGoApiVhosts(); virtual ~SrsGoApiVhosts(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsGoApiStreams : public ISrsHttpHandler @@ -156,7 +156,7 @@ public: SrsGoApiStreams(); virtual ~SrsGoApiStreams(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsHttpApi : public SrsConnection @@ -177,7 +177,7 @@ public: protected: virtual int do_cycle(); private: - virtual int process_request(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int process_request(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; #endif diff --git a/trunk/src/app/srs_app_http_client.cpp b/trunk/src/app/srs_app_http_client.cpp index 377f9a79b..60ec8e44e 100644 --- a/trunk/src/app/srs_app_http_client.cpp +++ b/trunk/src/app/srs_app_http_client.cpp @@ -29,13 +29,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using namespace std; -#include #include #include #include #include #include #include +#include SrsHttpClient::SrsHttpClient() { @@ -71,7 +71,7 @@ int SrsHttpClient::initialize(string h, int p, int64_t t_us) return ret; } -int SrsHttpClient::post(string path, string req, SrsHttpMessage** ppmsg) +int SrsHttpClient::post(string path, string req, ISrsHttpMessage** ppmsg) { *ppmsg = NULL; @@ -104,7 +104,7 @@ int SrsHttpClient::post(string path, string req, SrsHttpMessage** ppmsg) return ret; } - SrsHttpMessage* msg = NULL; + ISrsHttpMessage* msg = NULL; if ((ret = parser->parse_message(skt, NULL, &msg)) != ERROR_SUCCESS) { srs_error("parse http post response failed. ret=%d", ret); return ret; @@ -117,7 +117,7 @@ int SrsHttpClient::post(string path, string req, SrsHttpMessage** ppmsg) return ret; } -int SrsHttpClient::get(string path, std::string req, SrsHttpMessage** ppmsg) +int SrsHttpClient::get(string path, std::string req, ISrsHttpMessage** ppmsg) { *ppmsg = NULL; @@ -150,7 +150,7 @@ int SrsHttpClient::get(string path, std::string req, SrsHttpMessage** ppmsg) return ret; } - SrsHttpMessage* msg = NULL; + ISrsHttpMessage* msg = NULL; if ((ret = parser->parse_message(skt, NULL, &msg)) != ERROR_SUCCESS) { srs_error("parse http post response failed. ret=%d", ret); return ret; diff --git a/trunk/src/app/srs_app_http_client.hpp b/trunk/src/app/srs_app_http_client.hpp index 87f2f36bd..8a2dc88b7 100644 --- a/trunk/src/app/srs_app_http_client.hpp +++ b/trunk/src/app/srs_app_http_client.hpp @@ -37,7 +37,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. class SrsHttpUri; class SrsHttpParser; -class SrsHttpMessage; +class ISrsHttpMessage; class SrsStSocket; // the default timeout for http client. @@ -73,14 +73,14 @@ public: * @param req the data post to uri. empty string to ignore. * @param ppmsg output the http message to read the response. */ - virtual int post(std::string path, std::string req, SrsHttpMessage** ppmsg); + virtual int post(std::string path, std::string req, ISrsHttpMessage** ppmsg); /** * to get data from the uri. * @param the path to request on. * @param req the data post to uri. empty string to ignore. * @param ppmsg output the http message to read the response. */ - virtual int get(std::string path, std::string req, SrsHttpMessage** ppmsg); + virtual int get(std::string path, std::string req, ISrsHttpMessage** ppmsg); private: virtual void disconnect(); virtual int connect(); diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp index 5c99f79ef..90dd35fbd 100644 --- a/trunk/src/app/srs_app_http_conn.cpp +++ b/trunk/src/app/srs_app_http_conn.cpp @@ -23,7 +23,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -#ifdef SRS_AUTO_HTTP_SERVER +#if defined(SRS_AUTO_HTTP_PARSER) || defined(SRS_AUTO_HTTP_SERVER) #include #include @@ -33,6 +33,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include using namespace std; +#include +#include #include #include #include @@ -51,6 +53,959 @@ using namespace std; #include #include +#endif + +#ifdef SRS_AUTO_HTTP_PARSER + +SrsHttpResponseWriter::SrsHttpResponseWriter(SrsStSocket* io) +{ + skt = io; + hdr = new SrsHttpHeader(); + header_wrote = false; + status = SRS_CONSTS_HTTP_OK; + content_length = -1; + written = 0; + header_sent = false; +} + +SrsHttpResponseWriter::~SrsHttpResponseWriter() +{ + srs_freep(hdr); +} + +int SrsHttpResponseWriter::final_request() +{ + // complete the chunked encoding. + if (content_length == -1) { + std::stringstream ss; + ss << 0 << SRS_HTTP_CRLF << SRS_HTTP_CRLF; + std::string ch = ss.str(); + return skt->write((void*)ch.data(), (int)ch.length(), NULL); + } + + // flush when send with content length + return write(NULL, 0); +} + +SrsHttpHeader* SrsHttpResponseWriter::header() +{ + return hdr; +} + +int SrsHttpResponseWriter::write(char* data, int size) +{ + int ret = ERROR_SUCCESS; + + if (!header_wrote) { + write_header(SRS_CONSTS_HTTP_OK); + } + + written += size; + if (content_length != -1 && written > content_length) { + ret = ERROR_HTTP_CONTENT_LENGTH; + srs_error("http: exceed content length. ret=%d", ret); + return ret; + } + + if ((ret = send_header(data, size)) != ERROR_SUCCESS) { + srs_error("http: send header failed. ret=%d", ret); + return ret; + } + + // ignore NULL content. + if (!data) { + return ret; + } + + // directly send with content length + if (content_length != -1) { + return skt->write((void*)data, size, NULL); + } + + // send in chunked encoding. + std::stringstream ss; + ss << hex << size << SRS_HTTP_CRLF; + std::string ch = ss.str(); + if ((ret = skt->write((void*)ch.data(), (int)ch.length(), NULL)) != ERROR_SUCCESS) { + return ret; + } + if ((ret = skt->write((void*)data, size, NULL)) != ERROR_SUCCESS) { + return ret; + } + if ((ret = skt->write((void*)SRS_HTTP_CRLF, 2, NULL)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +void SrsHttpResponseWriter::write_header(int code) +{ + if (header_wrote) { + srs_warn("http: multiple write_header calls, code=%d", code); + return; + } + + header_wrote = true; + status = code; + + // parse the content length from header. + content_length = hdr->content_length(); +} + +int SrsHttpResponseWriter::send_header(char* data, int size) +{ + int ret = ERROR_SUCCESS; + + if (header_sent) { + return ret; + } + header_sent = true; + + std::stringstream ss; + + // status_line + ss << "HTTP/1.1 " << status << " " + << srs_generate_http_status_text(status) << SRS_HTTP_CRLF; + + // detect content type + if (srs_go_http_body_allowd(status)) { + if (hdr->content_type().empty()) { + hdr->set_content_type(srs_go_http_detect(data, size)); + } + } + + // set server if not set. + if (hdr->get("Server").empty()) { + hdr->set("Server", RTMP_SIG_SRS_KEY"/"RTMP_SIG_SRS_VERSION); + } + + // chunked encoding + if (content_length == -1) { + hdr->set("Transfer-Encoding", "chunked"); + } + + // keep alive to make vlc happy. + hdr->set("Connection", "Keep-Alive"); + + // write headers + hdr->write(ss); + + // header_eof + ss << SRS_HTTP_CRLF; + + std::string buf = ss.str(); + return skt->write((void*)buf.c_str(), buf.length(), NULL); +} + +SrsHttpResponseReader::SrsHttpResponseReader(SrsHttpMessage* msg, SrsStSocket* io) +{ + skt = io; + owner = msg; + is_eof = false; + nb_total_read = 0; + nb_left_chunk = 0; + buffer = NULL; +} + +SrsHttpResponseReader::~SrsHttpResponseReader() +{ +} + +int SrsHttpResponseReader::initialize(SrsFastBuffer* body) +{ + int ret = ERROR_SUCCESS; + + nb_chunk = 0; + nb_left_chunk = 0; + nb_total_read = 0; + buffer = body; + + return ret; +} + +bool SrsHttpResponseReader::eof() +{ + return is_eof; +} + +int SrsHttpResponseReader::read(char* data, int nb_data, int* nb_read) +{ + int ret = ERROR_SUCCESS; + + if (is_eof) { + ret = ERROR_HTTP_RESPONSE_EOF; + srs_error("http: response EOF. ret=%d", ret); + return ret; + } + + // chunked encoding. + if (owner->is_chunked()) { + return read_chunked(data, nb_data, nb_read); + } + + // read by specified content-length + int max = (int)owner->content_length() - (int)nb_total_read; + if (max <= 0) { + is_eof = true; + return ret; + } + + // change the max to read. + nb_data = srs_min(nb_data, max); + return read_specified(data, nb_data, nb_read); +} + +int SrsHttpResponseReader::read_chunked(char* data, int nb_data, int* nb_read) +{ + int ret = ERROR_SUCCESS; + + // when no bytes left in chunk, + // parse the chunk length first. + if (nb_left_chunk <= 0) { + char* at = NULL; + int length = 0; + while (!at) { + // find the CRLF of chunk header end. + char* start = buffer->bytes(); + char* end = start + buffer->size(); + for (char* p = start; p < end - 1; p++) { + if (p[0] == SRS_HTTP_CR && p[1] == SRS_HTTP_LF) { + // invalid chunk, ignore. + if (p == start) { + ret = ERROR_HTTP_INVALID_CHUNK_HEADER; + srs_error("chunk header start with CRLF. ret=%d", ret); + return ret; + } + length = (int)(p - start + 2); + at = buffer->read_slice(length); + break; + } + } + + // got at, ok. + if (at) { + break; + } + + // when empty, only grow 1bytes, but the buffer will cache more. + if ((ret = buffer->grow(skt, buffer->size() + 1)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("read body from server failed. ret=%d", ret); + } + return ret; + } + } + srs_assert(length >= 3); + + // it's ok to set the pos and pos+1 to NULL. + at[length - 1] = 0; + at[length - 2] = 0; + + // size is the bytes size, excludes the chunk header and end CRLF. + int ilength = (int)::strtol(at, NULL, 16); + if (ilength < 0) { + ret = ERROR_HTTP_INVALID_CHUNK_HEADER; + srs_error("chunk header negative, length=%d. ret=%d", ilength, ret); + return ret; + } + + // all bytes in chunk is left now. + nb_chunk = nb_left_chunk = ilength; + } + + if (nb_chunk <= 0) { + // for the last chunk, eof. + is_eof = true; + } else { + // for not the last chunk, there must always exists bytes. + // left bytes in chunk, read some. + srs_assert(nb_left_chunk); + + int nb_bytes = srs_min(nb_left_chunk, nb_data); + ret = read_specified(data, nb_bytes, &nb_bytes); + + // the nb_bytes used for output already read size of bytes. + if (nb_read) { + *nb_read = nb_bytes; + } + nb_left_chunk -= nb_bytes; + srs_info("http: read %d bytes of chunk", nb_bytes); + + // error or still left bytes in chunk, ignore and read in future. + if (nb_left_chunk > 0 || (ret != ERROR_SUCCESS)) { + return ret; + } + srs_info("http: read total chunk %dB", nb_chunk); + } + + // for both the last or not, the CRLF of chunk payload end. + if ((ret = buffer->grow(skt, 2)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("read EOF of chunk from server failed. ret=%d", ret); + } + return ret; + } + buffer->read_slice(2); + + return ret; +} + +int SrsHttpResponseReader::read_specified(char* data, int nb_data, int* nb_read) +{ + int ret = ERROR_SUCCESS; + + if (buffer->size() <= 0) { + // when empty, only grow 1bytes, but the buffer will cache more. + if ((ret = buffer->grow(skt, 1)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("read body from server failed. ret=%d", ret); + } + return ret; + } + } + + int nb_bytes = srs_min(nb_data, buffer->size()); + + // read data to buffer. + srs_assert(nb_bytes); + char* p = buffer->read_slice(nb_bytes); + memcpy(data, p, nb_bytes); + if (nb_read) { + *nb_read = nb_bytes; + } + + // increase the total read to determine whether EOF. + nb_total_read += nb_bytes; + + // for not chunked + if (!owner->is_chunked()) { + // when read completed, eof. + if (nb_total_read >= (int)owner->content_length()) { + is_eof = true; + } + } + + return ret; +} + +SrsHttpMessage::SrsHttpMessage(SrsStSocket* io, SrsConnection* c) : ISrsHttpMessage() +{ + conn = c; + chunked = false; + keep_alive = true; + _uri = new SrsHttpUri(); + _body = new SrsHttpResponseReader(this, io); + _http_ts_send_buffer = new char[SRS_HTTP_TS_SEND_BUFFER_SIZE]; +} + +SrsHttpMessage::~SrsHttpMessage() +{ + srs_freep(_body); + srs_freep(_uri); + srs_freep(_http_ts_send_buffer); +} + +int SrsHttpMessage::update(string url, http_parser* header, SrsFastBuffer* body, vector& headers) +{ + int ret = ERROR_SUCCESS; + + _url = url; + _header = *header; + _headers = headers; + + // whether chunked. + std::string transfer_encoding = get_request_header("Transfer-Encoding"); + chunked = (transfer_encoding == "chunked"); + + // whether keep alive. + keep_alive = http_should_keep_alive(header); + + // set the buffer. + if ((ret = _body->initialize(body)) != ERROR_SUCCESS) { + return ret; + } + + // parse uri from url. + std::string host = get_request_header("Host"); + + // donot parse the empty host for uri, + // for example, the response contains no host, + // ignore it is ok. + if (host.empty()) { + return ret; + } + + // parse uri to schema/server:port/path?query + std::string uri = "http://" + host + _url; + if ((ret = _uri->initialize(uri)) != ERROR_SUCCESS) { + return ret; + } + + // must format as key=value&...&keyN=valueN + std::string q = _uri->get_query(); + size_t pos = string::npos; + while (!q.empty()) { + std::string k = q; + if ((pos = q.find("=")) != string::npos) { + k = q.substr(0, pos); + q = q.substr(pos + 1); + } else { + q = ""; + } + + std::string v = q; + if ((pos = q.find("&")) != string::npos) { + v = q.substr(0, pos); + q = q.substr(pos + 1); + } else { + q = ""; + } + + _query[k] = v; + } + + // parse ext. + _ext = _uri->get_path(); + if ((pos = _ext.rfind(".")) != string::npos) { + _ext = _ext.substr(pos); + } else { + _ext = ""; + } + + return ret; +} + +SrsConnection* SrsHttpMessage::connection() +{ + return conn; +} + +u_int8_t SrsHttpMessage::method() +{ + return (u_int8_t)_header.method; +} + +u_int16_t SrsHttpMessage::status_code() +{ + return (u_int16_t)_header.status_code; +} + +string SrsHttpMessage::method_str() +{ + if (is_http_get()) { + return "GET"; + } + if (is_http_put()) { + return "PUT"; + } + if (is_http_post()) { + return "POST"; + } + if (is_http_delete()) { + return "DELETE"; + } + if (is_http_options()) { + return "OPTIONS"; + } + + return "OTHER"; +} + +bool SrsHttpMessage::is_http_get() +{ + return _header.method == SRS_CONSTS_HTTP_GET; +} + +bool SrsHttpMessage::is_http_put() +{ + return _header.method == SRS_CONSTS_HTTP_PUT; +} + +bool SrsHttpMessage::is_http_post() +{ + return _header.method == SRS_CONSTS_HTTP_POST; +} + +bool SrsHttpMessage::is_http_delete() +{ + return _header.method == SRS_CONSTS_HTTP_DELETE; +} + +bool SrsHttpMessage::is_http_options() +{ + return _header.method == SRS_CONSTS_HTTP_OPTIONS; +} + +bool SrsHttpMessage::is_chunked() +{ + return chunked; +} + +bool SrsHttpMessage::is_keep_alive() +{ + return keep_alive; +} + +string SrsHttpMessage::uri() +{ + std::string uri = _uri->get_schema(); + if (uri.empty()) { + uri += "http"; + } + uri += "://"; + + uri += host(); + uri += path(); + return uri; +} + +string SrsHttpMessage::url() +{ + return _uri->get_url(); +} + +string SrsHttpMessage::host() +{ + return _uri->get_host(); +} + +string SrsHttpMessage::path() +{ + return _uri->get_path(); +} + +string SrsHttpMessage::ext() +{ + return _ext; +} + +int SrsHttpMessage::body_read_all(string& body) +{ + int ret = ERROR_SUCCESS; + + // cache to read. + char* buf = new char[SRS_HTTP_READ_CACHE_BYTES]; + SrsAutoFree(char, buf); + + // whatever, read util EOF. + while (!_body->eof()) { + int nb_read = 0; + if ((ret = _body->read(buf, SRS_HTTP_READ_CACHE_BYTES, &nb_read)) != ERROR_SUCCESS) { + return ret; + } + + if (nb_read > 0) { + body.append(buf, nb_read); + } + } + + return ret; +} + +ISrsHttpResponseReader* SrsHttpMessage::body_reader() +{ + return _body; +} + +int64_t SrsHttpMessage::content_length() +{ + return _header.content_length; +} + +string SrsHttpMessage::query_get(string key) +{ + std::string v; + + if (_query.find(key) != _query.end()) { + v = _query[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) +{ + srs_assert(index < request_header_count()); + SrsHttpHeaderField item = _headers[index]; + return item.second; +} + +string SrsHttpMessage::get_request_header(string name) +{ + std::vector::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 ""; +} + +SrsRequest* SrsHttpMessage::to_request(string vhost) +{ + SrsRequest* req = new SrsRequest(); + + req->app = _uri->get_path(); + size_t pos = string::npos; + if ((pos = req->app.rfind("/")) != string::npos) { + req->stream = req->app.substr(pos + 1); + req->app = req->app.substr(0, pos); + } + if ((pos = req->stream.rfind(".")) != string::npos) { + req->stream = req->stream.substr(0, pos); + } + + req->tcUrl = "rtmp://" + vhost + req->app; + req->pageUrl = get_request_header("Referer"); + req->objectEncoding = 0; + + srs_discovery_tc_url(req->tcUrl, + req->schema, req->host, req->vhost, req->app, req->port, + req->param); + req->strip(); + + return req; +} + +SrsHttpParser::SrsHttpParser() +{ + buffer = new SrsFastBuffer(); +} + +SrsHttpParser::~SrsHttpParser() +{ + srs_freep(buffer); +} + +int SrsHttpParser::initialize(enum http_parser_type type) +{ + int ret = ERROR_SUCCESS; + + memset(&settings, 0, sizeof(settings)); + settings.on_message_begin = on_message_begin; + settings.on_url = on_url; + settings.on_header_field = on_header_field; + settings.on_header_value = on_header_value; + settings.on_headers_complete = on_headers_complete; + settings.on_body = on_body; + settings.on_message_complete = on_message_complete; + + http_parser_init(&parser, type); + // callback object ptr. + parser.data = (void*)this; + + return ret; +} + +int SrsHttpParser::parse_message(SrsStSocket* skt, SrsConnection* conn, ISrsHttpMessage** ppmsg) +{ + *ppmsg = NULL; + + int ret = ERROR_SUCCESS; + + // reset request data. + field_name = ""; + field_value = ""; + expect_field_name = true; + state = SrsHttpParseStateInit; + header = http_parser(); + url = ""; + headers.clear(); + header_parsed = 0; + + // do parse + if ((ret = parse_message_imp(skt)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("parse http msg failed. ret=%d", ret); + } + return ret; + } + + // create msg + SrsHttpMessage* msg = new SrsHttpMessage(skt, conn); + + // initalize http msg, parse url. + if ((ret = msg->update(url, &header, buffer, headers)) != ERROR_SUCCESS) { + srs_error("initialize http msg failed. ret=%d", ret); + srs_freep(msg); + return ret; + } + + // parse ok, return the msg. + *ppmsg = msg; + + return ret; +} + +int SrsHttpParser::parse_message_imp(SrsStSocket* skt) +{ + int ret = ERROR_SUCCESS; + + while (true) { + ssize_t nparsed = 0; + + // when got entire http header, parse it. + // @see https://github.com/simple-rtmp-server/srs/issues/400 + char* start = buffer->bytes(); + char* end = start + buffer->size(); + for (char* p = start; p <= end - 4; p++) { + // SRS_HTTP_CRLFCRLF "\r\n\r\n" // 0x0D0A0D0A + if (p[0] == SRS_CONSTS_CR && p[1] == SRS_CONSTS_LF && p[2] == SRS_CONSTS_CR && p[3] == SRS_CONSTS_LF) { + nparsed = http_parser_execute(&parser, &settings, buffer->bytes(), buffer->size()); + srs_info("buffer=%d, nparsed=%d, header=%d", buffer->size(), (int)nparsed, header_parsed); + break; + } + } + + // consume the parsed bytes. + if (nparsed && header_parsed) { + buffer->read_slice(header_parsed); + } + + // ok atleast header completed, + // never wait for body completed, for maybe chunked. + if (state == SrsHttpParseStateHeaderComplete || state == SrsHttpParseStateMessageComplete) { + break; + } + + // when nothing parsed, read more to parse. + if (nparsed == 0) { + // when requires more, only grow 1bytes, but the buffer will cache more. + if ((ret = buffer->grow(skt, buffer->size() + 1)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("read body from server failed. ret=%d", ret); + } + return ret; + } + } + } + + // parse last header. + if (!field_name.empty() && !field_value.empty()) { + headers.push_back(std::make_pair(field_name, field_value)); + } + + return ret; +} + +int SrsHttpParser::on_message_begin(http_parser* parser) +{ + SrsHttpParser* obj = (SrsHttpParser*)parser->data; + srs_assert(obj); + + obj->state = SrsHttpParseStateStart; + + srs_info("***MESSAGE BEGIN***"); + + return 0; +} + +int SrsHttpParser::on_headers_complete(http_parser* parser) +{ + SrsHttpParser* obj = (SrsHttpParser*)parser->data; + srs_assert(obj); + + obj->header = *parser; + // save the parser when header parse completed. + obj->state = SrsHttpParseStateHeaderComplete; + obj->header_parsed = (int)parser->nread; + + srs_info("***HEADERS COMPLETE***"); + + // see http_parser.c:1570, return 1 to skip body. + return 0; +} + +int SrsHttpParser::on_message_complete(http_parser* parser) +{ + SrsHttpParser* obj = (SrsHttpParser*)parser->data; + srs_assert(obj); + + // save the parser when body parse completed. + obj->state = SrsHttpParseStateMessageComplete; + + srs_info("***MESSAGE COMPLETE***\n"); + + return 0; +} + +int SrsHttpParser::on_url(http_parser* parser, const char* at, size_t length) +{ + SrsHttpParser* obj = (SrsHttpParser*)parser->data; + srs_assert(obj); + + if (length > 0) { + obj->url.append(at, (int)length); + } + + srs_info("Method: %d, Url: %.*s", parser->method, (int)length, at); + + return 0; +} + +int SrsHttpParser::on_header_field(http_parser* parser, const char* at, size_t length) +{ + 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 = ""; + } + obj->expect_field_name = true; + + if (length > 0) { + obj->field_name.append(at, (int)length); + } + + srs_info("Header field(%d bytes): %.*s", (int)length, (int)length, at); + return 0; +} + +int SrsHttpParser::on_header_value(http_parser* parser, const char* at, size_t length) +{ + SrsHttpParser* obj = (SrsHttpParser*)parser->data; + srs_assert(obj); + + 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; +} + +int SrsHttpParser::on_body(http_parser* parser, const char* at, size_t length) +{ + SrsHttpParser* obj = (SrsHttpParser*)parser->data; + srs_assert(obj); + + srs_info("Body: %.*s", (int)length, at); + + return 0; +} + +SrsHttpUri::SrsHttpUri() +{ + port = SRS_DEFAULT_HTTP_PORT; +} + +SrsHttpUri::~SrsHttpUri() +{ +} + +int SrsHttpUri::initialize(string _url) +{ + int ret = ERROR_SUCCESS; + + url = _url; + const char* purl = url.c_str(); + + http_parser_url hp_u; + if((ret = http_parser_parse_url(purl, url.length(), 0, &hp_u)) != 0){ + int code = ret; + ret = ERROR_HTTP_PARSE_URI; + + srs_error("parse url %s failed, code=%d, ret=%d", purl, code, ret); + return ret; + } + + std::string field = get_uri_field(url, &hp_u, UF_SCHEMA); + if(!field.empty()){ + schema = field; + } + + host = get_uri_field(url, &hp_u, UF_HOST); + + field = get_uri_field(url, &hp_u, UF_PORT); + if(!field.empty()){ + port = atoi(field.c_str()); + } + + path = get_uri_field(url, &hp_u, UF_PATH); + srs_info("parse url %s success", purl); + + query = get_uri_field(url, &hp_u, UF_QUERY); + srs_info("parse query %s success", query.c_str()); + + return ret; +} + +const char* SrsHttpUri::get_url() +{ + return url.data(); +} + +const char* SrsHttpUri::get_schema() +{ + return schema.data(); +} + +const char* SrsHttpUri::get_host() +{ + return host.data(); +} + +int SrsHttpUri::get_port() +{ + return port; +} + +const char* SrsHttpUri::get_path() +{ + return path.data(); +} + +const char* SrsHttpUri::get_query() +{ + return query.data(); +} + +string SrsHttpUri::get_uri_field(string uri, http_parser_url* hp_u, http_parser_url_fields field) +{ + if((hp_u->field_set & (1 << field)) == 0){ + return ""; + } + + srs_verbose("uri field matched, off=%d, len=%d, value=%.*s", + hp_u->field_data[field].off, + hp_u->field_data[field].len, + hp_u->field_data[field].len, + uri.c_str() + hp_u->field_data[field].off); + + int offset = hp_u->field_data[field].off; + int len = hp_u->field_data[field].len; + + return uri.substr(offset, len); +} + +#endif + +#ifdef SRS_AUTO_HTTP_SERVER + SrsVodStream::SrsVodStream(string root_dir) : SrsHttpFileServer(root_dir) { @@ -60,7 +1015,7 @@ SrsVodStream::~SrsVodStream() { } -int SrsVodStream::serve_flv_stream(ISrsHttpResponseWriter* w, SrsHttpMessage* r, string fullpath, int offset) +int SrsVodStream::serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath, int offset) { int ret = ERROR_SUCCESS; @@ -144,7 +1099,7 @@ int SrsVodStream::serve_flv_stream(ISrsHttpResponseWriter* w, SrsHttpMessage* r, return ret; } -int SrsVodStream::serve_mp4_stream(ISrsHttpResponseWriter* w, SrsHttpMessage* r, string fullpath, int start, int end) +int SrsVodStream::serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath, int start, int end) { int ret = ERROR_SUCCESS; @@ -202,12 +1157,11 @@ SrsStreamCache::SrsStreamCache(SrsSource* s, SrsRequest* r) req = r->copy(); source = s; queue = new SrsMessageQueue(true); - pthread = new SrsThread("http-stream", this, 0, false); + pthread = new SrsEndlessThread("http-stream", this); } SrsStreamCache::~SrsStreamCache() { - pthread->stop(); srs_freep(pthread); srs_freep(queue); @@ -567,7 +1521,7 @@ SrsLiveStream::~SrsLiveStream() srs_freep(req); } -int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { int ret = ERROR_SUCCESS; @@ -743,7 +1697,7 @@ void SrsHlsM3u8Stream::set_m3u8(std::string v) m3u8 = v; } -int SrsHlsM3u8Stream::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsHlsM3u8Stream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { int ret = ERROR_SUCCESS; @@ -775,7 +1729,7 @@ void SrsHlsTsStream::set_ts(std::string v) ts = v; } -int SrsHlsTsStream::serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsHlsTsStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { int ret = ERROR_SUCCESS; @@ -1110,7 +2064,7 @@ int SrsHttpServer::on_reload_vhost_hls(string vhost) return ret; } -int SrsHttpServer::hijack(SrsHttpMessage* request, ISrsHttpHandler** ph) +int SrsHttpServer::hijack(ISrsHttpMessage* request, ISrsHttpHandler** ph) { int ret = ERROR_SUCCESS; @@ -1169,8 +2123,12 @@ int SrsHttpServer::hijack(SrsHttpMessage* request, ISrsHttpHandler** ph) } } + // convert to concreate class. + SrsHttpMessage* hreq = dynamic_cast(request); + srs_assert(hreq); + // hijack for entry. - SrsRequest* r = request->to_request(vhost->arg0()); + SrsRequest* r = hreq->to_request(vhost->arg0()); SrsAutoFree(SrsRequest, r); SrsSource* s = SrsSource::fetch(r); if (!s) { @@ -1389,7 +2347,7 @@ int SrsHttpConn::do_cycle() // process http messages. for (;;) { - SrsHttpMessage* req = NULL; + ISrsHttpMessage* req = NULL; // get a http message if ((ret = parser->parse_message(&skt, this, &req)) != ERROR_SUCCESS) { @@ -1400,7 +2358,7 @@ int SrsHttpConn::do_cycle() srs_assert(req); // always free it in this scope. - SrsAutoFree(SrsHttpMessage, req); + SrsAutoFree(ISrsHttpMessage, req); // may should discard the body. if ((ret = on_got_http_message(req)) != ERROR_SUCCESS) { @@ -1423,7 +2381,7 @@ int SrsHttpConn::do_cycle() return ret; } -int SrsHttpConn::process_request(ISrsHttpResponseWriter* w, SrsHttpMessage* r) +int SrsHttpConn::process_request(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { int ret = ERROR_SUCCESS; @@ -1450,7 +2408,7 @@ SrsStaticHttpConn::~SrsStaticHttpConn() { } -int SrsStaticHttpConn::on_got_http_message(SrsHttpMessage* msg) +int SrsStaticHttpConn::on_got_http_message(ISrsHttpMessage* msg) { int ret = ERROR_SUCCESS; diff --git a/trunk/src/app/srs_app_http_conn.hpp b/trunk/src/app/srs_app_http_conn.hpp index 5b4bf62d5..891c744b3 100644 --- a/trunk/src/app/srs_app_http_conn.hpp +++ b/trunk/src/app/srs_app_http_conn.hpp @@ -30,14 +30,22 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -#ifdef SRS_AUTO_HTTP_SERVER +#ifdef SRS_AUTO_HTTP_PARSER +#include +#endif + +#if defined(SRS_AUTO_HTTP_PARSER) || defined(SRS_AUTO_HTTP_SERVER) + +#include +#include +#include #include -#include -#include +#include #include #include #include +#include class SrsServer; class SrsSource; @@ -49,10 +57,313 @@ class SrsAacEncoder; class SrsMp3Encoder; class SrsFlvEncoder; class SrsHttpParser; -class SrsHttpMessage; +class ISrsHttpMessage; class SrsHttpHandler; class SrsMessageQueue; class SrsSharedPtrMessage; +class SrsRequest; +class SrsFastBuffer; +class SrsHttpUri; +class SrsConnection; +class SrsHttpMessage; + +#endif + +#ifdef SRS_AUTO_HTTP_PARSER + +/** + * response writer use st socket + */ +class SrsHttpResponseWriter : public ISrsHttpResponseWriter +{ +private: + SrsStSocket* skt; + SrsHttpHeader* hdr; +private: + // reply header has been (logically) written + bool header_wrote; + // status code passed to WriteHeader + int status; +private: + // explicitly-declared Content-Length; or -1 + int64_t content_length; + // number of bytes written in body + int64_t written; +private: + // wroteHeader tells whether the header's been written to "the + // wire" (or rather: w.conn.buf). this is unlike + // (*response).wroteHeader, which tells only whether it was + // logically written. + bool header_sent; +public: + SrsHttpResponseWriter(SrsStSocket* io); + virtual ~SrsHttpResponseWriter(); +public: + virtual int final_request(); + virtual SrsHttpHeader* header(); + virtual int write(char* data, int size); + virtual void write_header(int code); + virtual int send_header(char* data, int size); +}; + +/** + * response reader use st socket. + */ +class SrsHttpResponseReader : virtual public ISrsHttpResponseReader +{ +private: + SrsStSocket* skt; + SrsHttpMessage* owner; + SrsFastBuffer* buffer; + bool is_eof; + // the left bytes in chunk. + int nb_left_chunk; + // the number of bytes of current chunk. + int nb_chunk; + // already read total bytes. + int64_t nb_total_read; +public: + SrsHttpResponseReader(SrsHttpMessage* msg, SrsStSocket* io); + virtual ~SrsHttpResponseReader(); +public: + /** + * initialize the response reader with buffer. + */ + virtual int initialize(SrsFastBuffer* buffer); + // interface ISrsHttpResponseReader +public: + virtual bool eof(); + virtual int read(char* data, int nb_data, int* nb_read); +private: + virtual int read_chunked(char* data, int nb_data, int* nb_read); + virtual int read_specified(char* data, int nb_data, int* nb_read); +}; + +// for http header. +typedef std::pair SrsHttpHeaderField; + +// A Request represents an HTTP request received by a server +// or to be sent by a client. +// +// The field semantics differ slightly between client and server +// usage. In addition to the notes on the fields below, see the +// documentation for Request.Write and RoundTripper. +/** + * the http message, request or response. + */ +class SrsHttpMessage : public ISrsHttpMessage +{ +private: + /** + * parsed url. + */ + std::string _url; + /** + * the extension of file, for example, .flv + */ + std::string _ext; + /** + * parsed http header. + */ + http_parser _header; + /** + * 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 request indicates should keep alive + * for the http connection. + */ + bool keep_alive; + /** + * uri parser + */ + SrsHttpUri* _uri; + /** + * use a buffer to read and send ts file. + */ + // TODO: FIXME: remove it. + char* _http_ts_send_buffer; + // http headers + std::vector _headers; + // the query map + std::map _query; + // the transport connection, can be NULL. + SrsConnection* conn; +public: + SrsHttpMessage(SrsStSocket* io, SrsConnection* c); + virtual ~SrsHttpMessage(); +public: + /** + * set the original messages, then update the message. + */ + virtual int update(std::string url, http_parser* header, + SrsFastBuffer* body, std::vector& headers + ); +private: + virtual SrsConnection* connection(); +public: + virtual u_int8_t method(); + virtual u_int16_t status_code(); + /** + * method helpers. + */ + virtual std::string method_str(); + virtual bool is_http_get(); + virtual bool is_http_put(); + virtual bool is_http_post(); + virtual bool is_http_delete(); + virtual bool is_http_options(); + /** + * whether body is chunked encoding, for reader only. + */ + virtual bool is_chunked(); + /** + * whether should keep the connection alive. + */ + virtual bool is_keep_alive(); + /** + * the uri contains the host and path. + */ + virtual std::string uri(); + /** + * the url maybe the path. + */ + virtual std::string url(); + virtual std::string host(); + virtual std::string path(); + virtual std::string ext(); +public: + /** + * read body to string. + * @remark for small http body. + */ + virtual int body_read_all(std::string& body); + /** + * get the body reader, to read one by one. + * @remark when body is very large, or chunked, use this. + */ + virtual ISrsHttpResponseReader* body_reader(); + /** + * the content length, -1 for chunked or not set. + */ + virtual int64_t content_length(); + /** + * get the param in query string, + * for instance, query is "start=100&end=200", + * 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); +public: + /** + * convert the http message to a request. + * @remark user must free the return request. + */ + virtual SrsRequest* to_request(std::string vhost); +}; + +/** + * wrapper for http-parser, + * provides HTTP message originted service. + */ +class SrsHttpParser +{ +private: + http_parser_settings settings; + http_parser parser; + // the global parse buffer. + SrsFastBuffer* buffer; +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; + std::string url; + std::vector headers; + int header_parsed; +public: + SrsHttpParser(); + virtual ~SrsHttpParser(); +public: + /** + * initialize the http parser with specified type, + * one parser can only parse request or response messages. + */ + virtual int initialize(enum http_parser_type type); + /** + * always parse a http message, + * that is, the *ppmsg always NOT-NULL when return success. + * or error and *ppmsg must be NULL. + * @remark, if success, *ppmsg always NOT-NULL, *ppmsg always is_complete(). + */ + virtual int parse_message(SrsStSocket* skt, SrsConnection* conn, ISrsHttpMessage** ppmsg); +private: + /** + * parse the HTTP message to member field: msg. + */ + virtual int parse_message_imp(SrsStSocket* skt); +private: + static int on_message_begin(http_parser* parser); + static int on_headers_complete(http_parser* parser); + static int on_message_complete(http_parser* parser); + static int on_url(http_parser* parser, const char* at, size_t length); + static int on_header_field(http_parser* parser, const char* at, size_t length); + static int on_header_value(http_parser* parser, const char* at, size_t length); + static int on_body(http_parser* parser, const char* at, size_t length); +}; + +/** + * used to resolve the http uri. + */ +class SrsHttpUri +{ +private: + std::string url; + std::string schema; + std::string host; + int port; + std::string path; + std::string query; +public: + SrsHttpUri(); + virtual ~SrsHttpUri(); +public: + /** + * initialize the http uri. + */ + virtual int initialize(std::string _url); +public: + virtual const char* get_url(); + virtual const char* get_schema(); + virtual const char* get_host(); + virtual int get_port(); + virtual const char* get_path(); + virtual const char* get_query(); +private: + /** + * get the parsed url field. + * @return return empty string if not set. + */ + virtual std::string get_uri_field(std::string uri, http_parser_url* hp_u, http_parser_url_fields field); +}; + +#endif + +#ifdef SRS_AUTO_HTTP_SERVER /** * the flv vod stream supports flv?start=offset-bytes. @@ -66,8 +377,8 @@ public: SrsVodStream(std::string root_dir); virtual ~SrsVodStream(); protected: - virtual int serve_flv_stream(ISrsHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int offset); - virtual int serve_mp4_stream(ISrsHttpResponseWriter* w, SrsHttpMessage* r, std::string fullpath, int start, int end); + virtual int serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath, int offset); + virtual int serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath, int start, int end); }; /** @@ -75,20 +386,20 @@ protected: * for example, the audio stream cache to make android(weixin) happy. * we start a thread to shrink the queue. */ -class SrsStreamCache : public ISrsThreadHandler +class SrsStreamCache : public ISrsEndlessThreadHandler { private: SrsMessageQueue* queue; SrsSource* source; SrsRequest* req; - SrsThread* pthread; + SrsEndlessThread* pthread; public: SrsStreamCache(SrsSource* s, SrsRequest* r); virtual ~SrsStreamCache(); public: virtual int start(); virtual int dump_cache(SrsConsumer* consumer); -// interface ISrsThreadHandler. +// interface ISrsEndlessThreadHandler. public: virtual int cycle(); }; @@ -243,7 +554,7 @@ public: SrsLiveStream(SrsSource* s, SrsRequest* r, SrsStreamCache* c); virtual ~SrsLiveStream(); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); private: virtual int streaming_send_messages(ISrsStreamEncoder* enc, SrsSharedPtrMessage** msgs, int nb_msgs); }; @@ -289,7 +600,7 @@ public: public: virtual void set_m3u8(std::string v); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; /** @@ -305,7 +616,7 @@ public: public: virtual void set_ts(std::string v); public: - virtual int serve_http(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; /** @@ -358,14 +669,14 @@ public: virtual int hls_update_m3u8(SrsRequest* r, std::string m3u8); virtual int hls_update_ts(SrsRequest* r, std::string uri, std::string ts); virtual void unmount_hls(SrsRequest* r); -// interface ISrsThreadHandler. +// interface ISrsReloadHandler. public: virtual int on_reload_vhost_http_updated(); virtual int on_reload_vhost_http_remux_updated(); virtual int on_reload_vhost_hls(std::string vhost); // interface ISrsHttpMatchHijacker public: - virtual int hijack(SrsHttpMessage* request, ISrsHttpHandler** ph); + virtual int hijack(ISrsHttpMessage* request, ISrsHttpHandler** ph); private: virtual int initialize_static_file(); virtual int initialize_flv_streaming(); @@ -392,9 +703,9 @@ protected: // when got http message, // for the static service or api, discard any body. // for the stream caster, for instance, http flv streaming, may discard the flv header or not. - virtual int on_got_http_message(SrsHttpMessage* msg) = 0; + virtual int on_got_http_message(ISrsHttpMessage* msg) = 0; private: - virtual int process_request(ISrsHttpResponseWriter* w, SrsHttpMessage* r); + virtual int process_request(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; class SrsStaticHttpConn : public SrsHttpConn @@ -403,7 +714,7 @@ public: SrsStaticHttpConn(IConnectionManager* cm, st_netfd_t fd, SrsHttpServeMux* m); virtual ~SrsStaticHttpConn(); public: - virtual int on_got_http_message(SrsHttpMessage* msg); + virtual int on_got_http_message(ISrsHttpMessage* msg); }; #endif diff --git a/trunk/src/app/srs_app_http_hooks.cpp b/trunk/src/app/srs_app_http_hooks.cpp index 71b1e6348..3b99fb400 100644 --- a/trunk/src/app/srs_app_http_hooks.cpp +++ b/trunk/src/app/srs_app_http_hooks.cpp @@ -31,13 +31,13 @@ using namespace std; #include #include #include -#include #include #include #include #include #include #include +#include #define SRS_HTTP_RESPONSE_OK SRS_XSTR(ERROR_SUCCESS) @@ -371,11 +371,11 @@ int SrsHttpHooks::on_hls_notify(std::string url, SrsRequest* req, std::string ts } srs_warn("GET %s", path.c_str()); - SrsHttpMessage* msg = NULL; + ISrsHttpMessage* msg = NULL; if ((ret = http.get(path.c_str(), "", &msg)) != ERROR_SUCCESS) { return ret; } - SrsAutoFree(SrsHttpMessage, msg); + SrsAutoFree(ISrsHttpMessage, msg); int nb_buf = srs_min(nb_notify, SRS_HTTP_READ_BUFFER); char* buf = new char[nb_buf]; @@ -416,11 +416,11 @@ int SrsHttpHooks::do_post(std::string url, std::string req, int& code, string& r return ret; } - SrsHttpMessage* msg = NULL; + ISrsHttpMessage* msg = NULL; if ((ret = http.post(uri.get_path(), req, &msg)) != ERROR_SUCCESS) { return ret; } - SrsAutoFree(SrsHttpMessage, msg); + SrsAutoFree(ISrsHttpMessage, msg); code = msg->status_code(); if ((ret = msg->body_read_all(res)) != ERROR_SUCCESS) { diff --git a/trunk/src/app/srs_app_ingest.cpp b/trunk/src/app/srs_app_ingest.cpp index c378a6195..c2c623439 100644 --- a/trunk/src/app/srs_app_ingest.cpp +++ b/trunk/src/app/srs_app_ingest.cpp @@ -55,7 +55,7 @@ SrsIngester::SrsIngester() { _srs_config->subscribe(this); - pthread = new SrsThread("ingest", this, SRS_AUTO_INGESTER_SLEEP_US, true); + pthread = new SrsReusableThread("ingest", this, SRS_AUTO_INGESTER_SLEEP_US); pprint = SrsPithyPrint::create_ingester(); } diff --git a/trunk/src/app/srs_app_ingest.hpp b/trunk/src/app/srs_app_ingest.hpp index acdeb7d8c..7a14a8dac 100644 --- a/trunk/src/app/srs_app_ingest.hpp +++ b/trunk/src/app/srs_app_ingest.hpp @@ -59,12 +59,12 @@ public: * encode with FFMPEG(optional), * push to SRS(or any RTMP server) over RTMP. */ -class SrsIngester : public ISrsThreadHandler, public ISrsReloadHandler +class SrsIngester : public ISrsReusableThreadHandler, public ISrsReloadHandler { private: std::vector ingesters; private: - SrsThread* pthread; + SrsReusableThread* pthread; SrsPithyPrint* pprint; public: SrsIngester(); @@ -72,7 +72,7 @@ public: public: virtual int start(); virtual void stop(); -// interface ISrsThreadHandler. +// interface ISrsReusableThreadHandler. public: virtual int cycle(); virtual void on_thread_stop(); diff --git a/trunk/src/app/srs_app_kbps.hpp b/trunk/src/app/srs_app_kbps.hpp index 39e5c11eb..d580a319b 100644 --- a/trunk/src/app/srs_app_kbps.hpp +++ b/trunk/src/app/srs_app_kbps.hpp @@ -135,27 +135,34 @@ public: }; /** -* to statistic the kbps of io. -* itself can be a statistic source, for example, used for SRS bytes stat. -* there are two usage scenarios: -* 1. connections to calc kbps by sample(): -* SrsKbps* kbps = ...; -* kbps->set_io(in, out) -* kbps->sample() -* kbps->get_xxx_kbps(). -* the connections know how many bytes already send/recv. -* 2. server to calc kbps by add_delta(): -* SrsKbps* kbps = ...; -* kbps->set_io(NULL, NULL) -* for each connection in connections: -* IKbpsDelta* delta = connection; // where connection implements IKbpsDelta -* delta->resample() -* kbps->add_delta(delta) -* delta->cleanup() -* kbps->sample() -* kbps->get_xxx_kbps(). -* the server never know how many bytes already send/recv, for the connection maybe closed. -*/ + * to statistic the kbps of io. + * itself can be a statistic source, for example, used for SRS bytes stat. + * there are some usage scenarios: + * 1. connections to calc kbps by sample(): + * SrsKbps* kbps = ...; + * kbps->set_io(in, out) + * kbps->sample() + * kbps->get_xxx_kbps(). + * the connections know how many bytes already send/recv. + * 2. server to calc kbps by add_delta(): + * SrsKbps* kbps = ...; + * kbps->set_io(NULL, NULL) + * for each connection in connections: + * IKbpsDelta* delta = connection; // where connection implements IKbpsDelta + * delta->resample() + * kbps->add_delta(delta) + * delta->cleanup() + * kbps->sample() + * kbps->get_xxx_kbps(). + * 3. kbps used as IKbpsDelta, to provides delta bytes: + * SrsKbps* kbps = ...; + * kbps->set_io(in, out); + * IKbpsDelta* delta = (IKbpsDelta*)kbps; + * delta->resample(); + * printf("delta is %d/%d", delta->get_send_bytes_delta(), delta->get_recv_bytes_delta()); + * delta->cleanup(); + * the server never know how many bytes already send/recv, for the connection maybe closed. + */ class SrsKbps : public virtual ISrsProtocolStatistic, public virtual IKbpsDelta { private: diff --git a/trunk/src/app/srs_app_listener.cpp b/trunk/src/app/srs_app_listener.cpp index 915c95c23..42d77414f 100644 --- a/trunk/src/app/srs_app_listener.cpp +++ b/trunk/src/app/srs_app_listener.cpp @@ -79,7 +79,7 @@ SrsUdpListener::SrsUdpListener(ISrsUdpHandler* h, string i, int p) nb_buf = SRS_UDP_MAX_PACKET_SIZE; buf = new char[nb_buf]; - pthread = new SrsThread("udp", this, 0, true); + pthread = new SrsReusableThread("udp", this); } SrsUdpListener::~SrsUdpListener() @@ -156,26 +156,24 @@ int SrsUdpListener::listen() int SrsUdpListener::cycle() { int ret = ERROR_SUCCESS; + + // TODO: FIXME: support ipv6, @see man 7 ipv6 + sockaddr_in from; + int nb_from = sizeof(sockaddr_in); + int nread = 0; + + if ((nread = st_recvfrom(_stfd, buf, nb_buf, (sockaddr*)&from, &nb_from, ST_UTIME_NO_TIMEOUT)) <= 0) { + srs_warn("ignore recv udp packet failed, nread=%d", nread); + return ret; + } - while (pthread->can_loop()) { - // TODO: FIXME: support ipv6, @see man 7 ipv6 - sockaddr_in from; - int nb_from = sizeof(sockaddr_in); - int nread = 0; - - if ((nread = st_recvfrom(_stfd, buf, nb_buf, (sockaddr*)&from, &nb_from, ST_UTIME_NO_TIMEOUT)) <= 0) { - srs_warn("ignore recv udp packet failed, nread=%d", nread); - continue; - } - - if ((ret = handler->on_udp_packet(&from, buf, nread)) != ERROR_SUCCESS) { - srs_warn("handle udp packet failed. ret=%d", ret); - continue; - } - - if (SRS_UDP_PACKET_RECV_CYCLE_INTERVAL_MS > 0) { - st_usleep(SRS_UDP_PACKET_RECV_CYCLE_INTERVAL_MS * 1000); - } + if ((ret = handler->on_udp_packet(&from, buf, nread)) != ERROR_SUCCESS) { + srs_warn("handle udp packet failed. ret=%d", ret); + return ret; + } + + if (SRS_UDP_PACKET_RECV_CYCLE_INTERVAL_MS > 0) { + st_usleep(SRS_UDP_PACKET_RECV_CYCLE_INTERVAL_MS * 1000); } return ret; @@ -190,7 +188,7 @@ SrsTcpListener::SrsTcpListener(ISrsTcpHandler* h, string i, int p) _fd = -1; _stfd = NULL; - pthread = new SrsThread("tcp", this, 0, true); + pthread = new SrsReusableThread("tcp", this); } SrsTcpListener::~SrsTcpListener() diff --git a/trunk/src/app/srs_app_listener.hpp b/trunk/src/app/srs_app_listener.hpp index 371b5acba..d5d97eede 100644 --- a/trunk/src/app/srs_app_listener.hpp +++ b/trunk/src/app/srs_app_listener.hpp @@ -82,12 +82,12 @@ public: /** * bind udp port, start thread to recv packet and handler it. */ -class SrsUdpListener : public ISrsThreadHandler +class SrsUdpListener : public ISrsReusableThreadHandler { private: int _fd; st_netfd_t _stfd; - SrsThread* pthread; + SrsReusableThread* pthread; private: char* buf; int nb_buf; @@ -103,7 +103,7 @@ public: virtual st_netfd_t stfd(); public: virtual int listen(); -// interface ISrsThreadHandler. +// interface ISrsReusableThreadHandler. public: virtual int cycle(); }; @@ -111,12 +111,12 @@ public: /** * bind and listen tcp port, use handler to process the client. */ -class SrsTcpListener : public ISrsThreadHandler +class SrsTcpListener : public ISrsReusableThreadHandler { private: int _fd; st_netfd_t _stfd; - SrsThread* pthread; + SrsReusableThread* pthread; private: ISrsTcpHandler* handler; std::string ip; @@ -128,7 +128,7 @@ public: virtual int fd(); public: virtual int listen(); -// interface ISrsThreadHandler. +// interface ISrsReusableThreadHandler. public: virtual int cycle(); }; diff --git a/trunk/src/app/srs_app_log.hpp b/trunk/src/app/srs_app_log.hpp index cdb86c8db..9645079fa 100644 --- a/trunk/src/app/srs_app_log.hpp +++ b/trunk/src/app/srs_app_log.hpp @@ -82,7 +82,7 @@ public: virtual void trace(const char* tag, int context_id, const char* fmt, ...); virtual void warn(const char* tag, int context_id, const char* fmt, ...); virtual void error(const char* tag, int context_id, const char* fmt, ...); -// interface ISrsThreadHandler. +// interface ISrsReloadHandler. public: virtual int on_reload_log_tank(); virtual int on_reload_log_level(); diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 537e3dbf1..dad1a7823 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -50,7 +50,7 @@ SrsRecvThread::SrsRecvThread(ISrsMessageHandler* msg_handler, SrsRtmpServer* rtm timeout = timeout_ms; handler = msg_handler; rtmp = rtmp_sdk; - trd = new SrsThread("recv", this, 0, true); + trd = new SrsReusableThread2("recv", this); } SrsRecvThread::~SrsRecvThread() @@ -72,11 +72,16 @@ void SrsRecvThread::stop() trd->stop(); } +void SrsRecvThread::stop_loop() +{ + trd->interrupt(); +} + int SrsRecvThread::cycle() { int ret = ERROR_SUCCESS; - while (trd->can_loop()) { + while (!trd->interrupted()) { if (!handler->can_handle()) { st_usleep(timeout * 1000); continue; @@ -96,7 +101,7 @@ int SrsRecvThread::cycle() } // we use no timeout to recv, should never got any error. - trd->stop_loop(); + trd->interrupt(); // notice the handler got a recv error. handler->on_recv_error(ret); @@ -109,11 +114,6 @@ int SrsRecvThread::cycle() return ret; } -void SrsRecvThread::stop_loop() -{ - trd->stop_loop(); -} - void SrsRecvThread::on_thread_start() { // the multiple messages writev improve performance large, diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index f6ba086df..7d0271e28 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -79,10 +79,10 @@ public: /** * the recv thread, use message handler to handle each received message. */ -class SrsRecvThread : public ISrsThreadHandler +class SrsRecvThread : public ISrsReusableThread2Handler { protected: - SrsThread* trd; + SrsReusableThread2* trd; ISrsMessageHandler* handler; SrsRtmpServer* rtmp; int timeout; @@ -92,9 +92,10 @@ public: public: virtual int start(); virtual void stop(); - virtual int cycle(); virtual void stop_loop(); +// interface ISrsReusableThread2Handler public: + virtual int cycle(); virtual void on_thread_start(); virtual void on_thread_stop(); }; diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index efadfa969..e2613343a 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -41,7 +41,6 @@ using namespace std; #include #include #include -#include #include #include #include diff --git a/trunk/src/app/srs_app_rtsp.cpp b/trunk/src/app/srs_app_rtsp.cpp index 5fe5fb5d6..bc961bd51 100644 --- a/trunk/src/app/srs_app_rtsp.cpp +++ b/trunk/src/app/srs_app_rtsp.cpp @@ -192,7 +192,7 @@ SrsRtspConn::SrsRtspConn(SrsRtspCaster* c, st_netfd_t fd, std::string o) stfd = fd; skt = new SrsStSocket(fd); rtsp = new SrsRtspStack(skt); - trd = new SrsThread("rtsp", this, 0, false); + trd = new SrsOneCycleThread("rtsp", this); req = NULL; io = NULL; @@ -210,7 +210,6 @@ SrsRtspConn::SrsRtspConn(SrsRtspCaster* c, st_netfd_t fd, std::string o) SrsRtspConn::~SrsRtspConn() { srs_close_stfd(stfd); - trd->stop(); srs_freep(video_rtp); srs_freep(audio_rtp); @@ -219,7 +218,9 @@ SrsRtspConn::~SrsRtspConn() srs_freep(skt); srs_freep(rtsp); - close(); + srs_freep(client); + srs_freep(io); + srs_freep(req); srs_freep(vjitter); srs_freep(ajitter); @@ -412,9 +413,6 @@ int SrsRtspConn::cycle() srs_warn("client disconnect peer. ret=%d", ret); } - // terminate thread in the thread cycle itself. - trd->stop_loop(); - return ERROR_SUCCESS; } @@ -763,14 +761,6 @@ int SrsRtspConn::connect_app(string ep_server, string ep_port) return ret; } -void SrsRtspConn::close() -{ - srs_freep(client); - srs_freep(io); - srs_freep(req); - srs_close_stfd(stfd); -} - SrsRtspCaster::SrsRtspCaster(SrsConfDirective* c) { // TODO: FIXME: support reload. diff --git a/trunk/src/app/srs_app_rtsp.hpp b/trunk/src/app/srs_app_rtsp.hpp index 86397c033..cb25dbdc4 100644 --- a/trunk/src/app/srs_app_rtsp.hpp +++ b/trunk/src/app/srs_app_rtsp.hpp @@ -113,7 +113,7 @@ public: /** * the rtsp connection serve the fd. */ -class SrsRtspConn : public ISrsThreadHandler +class SrsRtspConn : public ISrsOneCycleThreadHandler { private: std::string output_template; @@ -136,7 +136,7 @@ private: SrsStSocket* skt; SrsRtspStack* rtsp; SrsRtspCaster* caster; - SrsThread* trd; + SrsOneCycleThread* trd; private: SrsRequest* req; SrsStSocket* io; @@ -163,7 +163,7 @@ private: // internal methods public: virtual int on_rtp_packet(SrsRtpPacket* pkt, int stream_id); -// interface ISrsThreadHandler +// interface ISrsOneCycleThreadHandler public: virtual int cycle(); virtual void on_thread_stop(); @@ -182,8 +182,6 @@ private: // @remark ignore when not connected, reconnect when disconnected. virtual int connect(); virtual int connect_app(std::string ep_server, std::string ep_port); - // close the connected io and rtmp to ready to be re-connect. - virtual void close(); }; /** diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index 7e2c0809a..5634db625 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -367,13 +367,12 @@ SrsSignalManager::SrsSignalManager(SrsServer* server) _server = server; sig_pipe[0] = sig_pipe[1] = -1; - pthread = new SrsThread("signal", this, 0, true); + pthread = new SrsEndlessThread("signal", this); signal_read_stfd = NULL; } SrsSignalManager::~SrsSignalManager() { - pthread->stop(); srs_freep(pthread); srs_close_stfd(signal_read_stfd); diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp index 76b681484..8dbcb47f1 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -179,7 +179,7 @@ public: * convert signal to io, * @see: st-1.9/docs/notes.html */ -class SrsSignalManager : public ISrsThreadHandler +class SrsSignalManager : public ISrsEndlessThreadHandler { private: /* Per-process pipe which is used as a signal queue. */ @@ -188,14 +188,14 @@ private: st_netfd_t signal_read_stfd; private: SrsServer* _server; - SrsThread* pthread; + SrsEndlessThread* pthread; public: SrsSignalManager(SrsServer* server); virtual ~SrsSignalManager(); public: virtual int initialize(); virtual int start(); -// interface ISrsThreadHandler. +// interface ISrsEndlessThreadHandler. public: virtual int cycle(); private: diff --git a/trunk/src/app/srs_app_statistic.hpp b/trunk/src/app/srs_app_statistic.hpp index 2468acf62..8c6c108a4 100644 --- a/trunk/src/app/srs_app_statistic.hpp +++ b/trunk/src/app/srs_app_statistic.hpp @@ -167,6 +167,7 @@ public: * sample the kbps, add delta bytes of conn. * use kbps_sample() to get all result of kbps stat. */ + // TODO: FIXME: the add delta must use IKbpsDelta interface instead. virtual void kbps_add_delta(SrsConnection* conn); /** * calc the result for all kbps. diff --git a/trunk/src/app/srs_app_thread.cpp b/trunk/src/app/srs_app_thread.cpp index a7e89c85e..dbc09ee0d 100644 --- a/trunk/src/app/srs_app_thread.cpp +++ b/trunk/src/app/srs_app_thread.cpp @@ -26,206 +26,512 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -ISrsThreadHandler::ISrsThreadHandler() +namespace internal { + ISrsThreadHandler::ISrsThreadHandler() + { + } + + ISrsThreadHandler::~ISrsThreadHandler() + { + } + + void ISrsThreadHandler::on_thread_start() + { + } + + int ISrsThreadHandler::on_before_cycle() + { + int ret = ERROR_SUCCESS; + return ret; + } + + int ISrsThreadHandler::on_end_cycle() + { + int ret = ERROR_SUCCESS; + return ret; + } + + void ISrsThreadHandler::on_thread_stop() + { + } + + SrsThread::SrsThread(const char* name, ISrsThreadHandler* thread_handler, int64_t interval_us, bool joinable) + { + _name = name; + handler = thread_handler; + cycle_interval_us = interval_us; + + tid = NULL; + loop = false; + really_terminated = true; + _cid = -1; + _joinable = joinable; + + // in start(), the thread cycle method maybe stop and remove the thread itself, + // and the thread start() is waiting for the _cid, and segment fault then. + // @see https://github.com/simple-rtmp-server/srs/issues/110 + // thread will set _cid, callback on_thread_start(), then wait for the can_run signal. + can_run = false; + } + + SrsThread::~SrsThread() + { + stop(); + } + + int SrsThread::cid() + { + return _cid; + } + + int SrsThread::start() + { + int ret = ERROR_SUCCESS; + + if(tid) { + srs_info("thread %s already running.", _name); + return ret; + } + + if((tid = st_thread_create(thread_fun, this, (_joinable? 1:0), 0)) == NULL){ + ret = ERROR_ST_CREATE_CYCLE_THREAD; + srs_error("st_thread_create failed. ret=%d", ret); + return ret; + } + + // we set to loop to true for thread to run. + loop = true; + + // wait for cid to ready, for parent thread to get the cid. + while (_cid < 0 && loop) { + st_usleep(10 * 1000); + } + + // now, cycle thread can run. + can_run = true; + + return ret; + } + + void SrsThread::stop() + { + if (tid) { + loop = false; + + // the interrupt will cause the socket to read/write error, + // which will terminate the cycle thread. + st_thread_interrupt(tid); + + // when joinable, wait util quit. + if (_joinable) { + // wait the thread to exit. + int ret = st_thread_join(tid, NULL); + if (ret) { + srs_warn("core: ignore join thread failed."); + } + + // wait the thread actually terminated. + // sometimes the thread join return -1, for example, + // when thread use st_recvfrom, the thread join return -1. + // so here, we use a variable to ensure the thread stopped. + while (!really_terminated) { + st_usleep(10 * 1000); + + if (really_terminated) { + break; + } + srs_warn("core: wait thread to actually terminated"); + } + } + + tid = NULL; + } + } + + bool SrsThread::can_loop() + { + return loop; + } + + void SrsThread::stop_loop() + { + loop = false; + } + + void SrsThread::thread_cycle() + { + int ret = ERROR_SUCCESS; + + _srs_context->generate_id(); + srs_info("thread %s cycle start", _name); + + _cid = _srs_context->get_id(); + + srs_assert(handler); + handler->on_thread_start(); + + // thread is running now. + really_terminated = false; + + // wait for cid to ready, for parent thread to get the cid. + while (!can_run && loop) { + st_usleep(10 * 1000); + } + + while (loop) { + if ((ret = handler->on_before_cycle()) != ERROR_SUCCESS) { + srs_warn("thread %s on before cycle failed, ignored and retry, ret=%d", _name, ret); + goto failed; + } + srs_info("thread %s on before cycle success"); + + if ((ret = handler->cycle()) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_warn("thread %s cycle failed, ignored and retry, ret=%d", _name, ret); + } + goto failed; + } + srs_info("thread %s cycle success", _name); + + if ((ret = handler->on_end_cycle()) != ERROR_SUCCESS) { + srs_warn("thread %s on end cycle failed, ignored and retry, ret=%d", _name, ret); + goto failed; + } + srs_info("thread %s on end cycle success", _name); + + failed: + if (!loop) { + break; + } + + // to improve performance, donot sleep when interval is zero. + // @see: https://github.com/simple-rtmp-server/srs/issues/237 + if (cycle_interval_us != 0) { + st_usleep(cycle_interval_us); + } + } + + // readly terminated now. + really_terminated = true; + + handler->on_thread_stop(); + srs_info("thread %s cycle finished", _name); + } + + void* SrsThread::thread_fun(void* arg) + { + SrsThread* obj = (SrsThread*)arg; + srs_assert(obj); + + obj->thread_cycle(); + + st_thread_exit(NULL); + + return NULL; + } +} + +ISrsEndlessThreadHandler::ISrsEndlessThreadHandler() { } -ISrsThreadHandler::~ISrsThreadHandler() +ISrsEndlessThreadHandler::~ISrsEndlessThreadHandler() { } -void ISrsThreadHandler::on_thread_start() +void ISrsEndlessThreadHandler::on_thread_start() { } -int ISrsThreadHandler::on_before_cycle() +int ISrsEndlessThreadHandler::on_before_cycle() { - int ret = ERROR_SUCCESS; - return ret; + return ERROR_SUCCESS; } -int ISrsThreadHandler::on_end_cycle() +int ISrsEndlessThreadHandler::on_end_cycle() { - int ret = ERROR_SUCCESS; - return ret; + return ERROR_SUCCESS; } -void ISrsThreadHandler::on_thread_stop() +void ISrsEndlessThreadHandler::on_thread_stop() { } -SrsThread::SrsThread(const char* name, ISrsThreadHandler* thread_handler, int64_t interval_us, bool joinable) +SrsEndlessThread::SrsEndlessThread(const char* n, ISrsEndlessThreadHandler* h) { - _name = name; - handler = thread_handler; - cycle_interval_us = interval_us; - - tid = NULL; - loop = false; - really_terminated = true; - _cid = -1; - _joinable = joinable; - - // in start(), the thread cycle method maybe stop and remove the thread itself, - // and the thread start() is waiting for the _cid, and segment fault then. - // @see https://github.com/simple-rtmp-server/srs/issues/110 - // thread will set _cid, callback on_thread_start(), then wait for the can_run signal. - can_run = false; + handler = h; + pthread = new internal::SrsThread(n, this, 0, false); } -SrsThread::~SrsThread() +SrsEndlessThread::~SrsEndlessThread() { - stop(); + pthread->stop(); + srs_freep(pthread); } -int SrsThread::cid() +int SrsEndlessThread::start() { - return _cid; + return pthread->start(); } -int SrsThread::start() +int SrsEndlessThread::cycle() { - int ret = ERROR_SUCCESS; - - if(tid) { - srs_info("thread %s already running.", _name); - return ret; - } - - if((tid = st_thread_create(thread_fun, this, (_joinable? 1:0), 0)) == NULL){ - ret = ERROR_ST_CREATE_CYCLE_THREAD; - srs_error("st_thread_create failed. ret=%d", ret); - return ret; - } - - // we set to loop to true for thread to run. - loop = true; - - // wait for cid to ready, for parent thread to get the cid. - while (_cid < 0 && loop) { - st_usleep(10 * 1000); - } - - // now, cycle thread can run. - can_run = true; - + return handler->cycle(); +} + +void SrsEndlessThread::on_thread_start() +{ + handler->on_thread_start(); +} + +int SrsEndlessThread::on_before_cycle() +{ + return handler->on_before_cycle(); +} + +int SrsEndlessThread::on_end_cycle() +{ + return handler->on_end_cycle(); +} + +void SrsEndlessThread::on_thread_stop() +{ + handler->on_thread_stop(); +} + +ISrsOneCycleThreadHandler::ISrsOneCycleThreadHandler() +{ +} + +ISrsOneCycleThreadHandler::~ISrsOneCycleThreadHandler() +{ +} + +void ISrsOneCycleThreadHandler::on_thread_start() +{ +} + +int ISrsOneCycleThreadHandler::on_before_cycle() +{ + return ERROR_SUCCESS; +} + +int ISrsOneCycleThreadHandler::on_end_cycle() +{ + return ERROR_SUCCESS; +} + +void ISrsOneCycleThreadHandler::on_thread_stop() +{ +} + +SrsOneCycleThread::SrsOneCycleThread(const char* n, ISrsOneCycleThreadHandler* h) +{ + handler = h; + pthread = new internal::SrsThread(n, this, 0, false); +} + +SrsOneCycleThread::~SrsOneCycleThread() +{ + pthread->stop(); + srs_freep(pthread); +} + +int SrsOneCycleThread::start() +{ + return pthread->start(); +} + +int SrsOneCycleThread::cycle() +{ + int ret = handler->cycle(); + pthread->stop_loop(); return ret; } -void SrsThread::stop() +void SrsOneCycleThread::on_thread_start() { - if (tid) { - loop = false; - - // the interrupt will cause the socket to read/write error, - // which will terminate the cycle thread. - st_thread_interrupt(tid); - - // when joinable, wait util quit. - if (_joinable) { - // wait the thread to exit. - int ret = st_thread_join(tid, NULL); - if (ret) { - srs_warn("core: ignore join thread failed."); - } + handler->on_thread_start(); +} - // wait the thread actually terminated. - // sometimes the thread join return -1, for example, - // when thread use st_recvfrom, the thread join return -1. - // so here, we use a variable to ensure the thread stopped. - while (!really_terminated) { - st_usleep(10 * 1000); +int SrsOneCycleThread::on_before_cycle() +{ + return handler->on_before_cycle(); +} - if (really_terminated) { - break; - } - srs_warn("core: wait thread to actually terminated"); - } - } - - tid = NULL; - } +int SrsOneCycleThread::on_end_cycle() +{ + return handler->on_end_cycle(); } -bool SrsThread::can_loop() +void SrsOneCycleThread::on_thread_stop() { - return loop; + handler->on_thread_stop(); } -void SrsThread::stop_loop() +ISrsReusableThreadHandler::ISrsReusableThreadHandler() { - loop = false; } -void SrsThread::thread_cycle() +ISrsReusableThreadHandler::~ISrsReusableThreadHandler() +{ +} + +void ISrsReusableThreadHandler::on_thread_start() +{ +} + +int ISrsReusableThreadHandler::on_before_cycle() +{ + return ERROR_SUCCESS; +} + +int ISrsReusableThreadHandler::on_end_cycle() +{ + return ERROR_SUCCESS; +} + +void ISrsReusableThreadHandler::on_thread_stop() +{ +} + +SrsReusableThread::SrsReusableThread(const char* n, ISrsReusableThreadHandler* h, int64_t interval_us) +{ + handler = h; + pthread = new internal::SrsThread(n, this, interval_us, true); +} + +SrsReusableThread::~SrsReusableThread() +{ + pthread->stop(); + srs_freep(pthread); +} + +int SrsReusableThread::start() +{ + return pthread->start(); +} + +void SrsReusableThread::stop() +{ + pthread->stop(); +} + +int SrsReusableThread::cid() +{ + return pthread->cid(); +} + +int SrsReusableThread::cycle() +{ + return handler->cycle(); +} + +void SrsReusableThread::on_thread_start() { - int ret = ERROR_SUCCESS; - - _srs_context->generate_id(); - srs_info("thread %s cycle start", _name); - - _cid = _srs_context->get_id(); - - srs_assert(handler); handler->on_thread_start(); +} - // thread is running now. - really_terminated = false; - - // wait for cid to ready, for parent thread to get the cid. - while (!can_run && loop) { - st_usleep(10 * 1000); - } - - while (loop) { - if ((ret = handler->on_before_cycle()) != ERROR_SUCCESS) { - srs_warn("thread %s on before cycle failed, ignored and retry, ret=%d", _name, ret); - goto failed; - } - srs_info("thread %s on before cycle success"); - - if ((ret = handler->cycle()) != ERROR_SUCCESS) { - if (!srs_is_client_gracefully_close(ret)) { - srs_warn("thread %s cycle failed, ignored and retry, ret=%d", _name, ret); - } - goto failed; - } - srs_info("thread %s cycle success", _name); - - if ((ret = handler->on_end_cycle()) != ERROR_SUCCESS) { - srs_warn("thread %s on end cycle failed, ignored and retry, ret=%d", _name, ret); - goto failed; - } - srs_info("thread %s on end cycle success", _name); +int SrsReusableThread::on_before_cycle() +{ + return handler->on_before_cycle(); +} -failed: - if (!loop) { - break; - } - - // to improve performance, donot sleep when interval is zero. - // @see: https://github.com/simple-rtmp-server/srs/issues/237 - if (cycle_interval_us != 0) { - st_usleep(cycle_interval_us); - } - } - - // readly terminated now. - really_terminated = true; - +int SrsReusableThread::on_end_cycle() +{ + return handler->on_end_cycle(); +} + +void SrsReusableThread::on_thread_stop() +{ handler->on_thread_stop(); - srs_info("thread %s cycle finished", _name); } -void* SrsThread::thread_fun(void* arg) +ISrsReusableThread2Handler::ISrsReusableThread2Handler() { - SrsThread* obj = (SrsThread*)arg; - srs_assert(obj); - - obj->thread_cycle(); +} - st_thread_exit(NULL); - - return NULL; +ISrsReusableThread2Handler::~ISrsReusableThread2Handler() +{ +} + +void ISrsReusableThread2Handler::on_thread_start() +{ +} + +int ISrsReusableThread2Handler::on_before_cycle() +{ + return ERROR_SUCCESS; +} + +int ISrsReusableThread2Handler::on_end_cycle() +{ + return ERROR_SUCCESS; +} + +void ISrsReusableThread2Handler::on_thread_stop() +{ +} + +SrsReusableThread2::SrsReusableThread2(const char* n, ISrsReusableThread2Handler* h, int64_t interval_us) +{ + handler = h; + pthread = new internal::SrsThread(n, this, interval_us, true); +} + +SrsReusableThread2::~SrsReusableThread2() +{ + pthread->stop(); + srs_freep(pthread); +} + +int SrsReusableThread2::start() +{ + return pthread->start(); +} + +void SrsReusableThread2::stop() +{ + pthread->stop(); +} + +int SrsReusableThread2::cid() +{ + return pthread->cid(); +} + +void SrsReusableThread2::interrupt() +{ + pthread->stop_loop(); +} + +bool SrsReusableThread2::interrupted() +{ + return !pthread->can_loop(); +} + +int SrsReusableThread2::cycle() +{ + return handler->cycle(); +} + +void SrsReusableThread2::on_thread_start() +{ + handler->on_thread_start(); +} + +int SrsReusableThread2::on_before_cycle() +{ + return handler->on_before_cycle(); +} + +int SrsReusableThread2::on_end_cycle() +{ + return handler->on_end_cycle(); +} + +void SrsReusableThread2::on_thread_stop() +{ + handler->on_thread_stop(); } diff --git a/trunk/src/app/srs_app_thread.hpp b/trunk/src/app/srs_app_thread.hpp index db89b8df0..1974cff22 100644 --- a/trunk/src/app/srs_app_thread.hpp +++ b/trunk/src/app/srs_app_thread.hpp @@ -31,189 +31,421 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +// the internal classes, user should never use it. +// user should use the public classes at the bellow: +// @see SrsEndlessThread, SrsOneCycleThread, SrsReusableThread +namespace internal { + /** + * the handler for the thread, callback interface. + * the thread model defines as: + * handler->on_thread_start() + * while loop: + * handler->on_before_cycle() + * handler->cycle() + * handler->on_end_cycle() + * if !loop then break for user stop thread. + * sleep(CycleIntervalMilliseconds) + * handler->on_thread_stop() + * when stop, the thread will interrupt the st_thread, + * which will cause the socket to return error and + * terminate the cycle thread. + * + * @remark why should check can_loop() in cycle method? + * when thread interrupt, the socket maybe not got EINT, + * espectially on st_usleep(), so the cycle must check the loop, + * when handler->cycle() has loop itself, for example: + * while (true): + * if (read_from_socket(skt) < 0) break; + * if thread stop when read_from_socket, it's ok, the loop will break, + * but when thread stop interrupt the s_usleep(0), then the loop is + * death loop. + * in a word, the handler->cycle() must: + * while (pthread->can_loop()): + * if (read_from_socket(skt) < 0) break; + * check the loop, then it works. + * + * @remark why should use stop_loop() to terminate thread in itself? + * in the thread itself, that is the cycle method, + * if itself want to terminate the thread, should never use stop(), + * but use stop_loop() to set the loop to false and terminate normally. + * + * @remark when should set the interval_us, and when not? + * the cycle will invoke util cannot loop, eventhough the return code of cycle is error, + * so the interval_us used to sleep for each cycle. + */ + class ISrsThreadHandler + { + public: + ISrsThreadHandler(); + virtual ~ISrsThreadHandler(); + public: + virtual void on_thread_start(); + virtual int on_before_cycle(); + virtual int cycle() = 0; + virtual int on_end_cycle(); + virtual void on_thread_stop(); + }; + + /** + * provides servies from st_thread_t, + * for common thread usage. + */ + class SrsThread + { + private: + st_thread_t tid; + int _cid; + bool loop; + bool can_run; + bool really_terminated; + bool _joinable; + const char* _name; + private: + ISrsThreadHandler* handler; + int64_t cycle_interval_us; + public: + /** + * initialize the thread. + * @param name, human readable name for st debug. + * @param thread_handler, the cycle handler for the thread. + * @param interval_us, the sleep interval when cycle finished. + * @param joinable, if joinable, other thread must stop the thread. + * @remark if joinable, thread never quit itself, or memory leak. + * @see: https://github.com/simple-rtmp-server/srs/issues/78 + * @remark about st debug, see st-1.9/README, _st_iterate_threads_flag + */ + /** + * TODO: FIXME: maybe all thread must be reap by others threads, + * @see: https://github.com/simple-rtmp-server/srs/issues/77 + */ + SrsThread(const char* name, ISrsThreadHandler* thread_handler, int64_t interval_us, bool joinable); + virtual ~SrsThread(); + public: + /** + * get the context id. @see: ISrsThreadContext.get_id(). + * used for parent thread to get the id. + * @remark when start thread, parent thread will block and wait for this id ready. + */ + virtual int cid(); + /** + * start the thread, invoke the cycle of handler util + * user stop the thread. + * @remark ignore any error of cycle of handler. + * @remark user can start multiple times, ignore if already started. + * @remark wait for the cid is set by thread pfn. + */ + virtual int start(); + /** + * stop the thread, wait for the thread to terminate. + * @remark user can stop multiple times, ignore if already stopped. + */ + virtual void stop(); + public: + /** + * whether the thread should loop, + * used for handler->cycle() which has a loop method, + * to check this method, break if false. + */ + virtual bool can_loop(); + /** + * for the loop thread to stop the loop. + * other thread can directly use stop() to stop loop and wait for quit. + * this stop loop method only set loop to false. + */ + virtual void stop_loop(); + private: + virtual void thread_cycle(); + static void* thread_fun(void* arg); + }; +} + /** - * the handler for the thread, callback interface. - * the thread model defines as: - * handler->on_thread_start() - * while loop: - * handler->on_before_cycle() - * handler->cycle() - * handler->on_end_cycle() - * if !loop then break for user stop thread. - * sleep(CycleIntervalMilliseconds) - * handler->on_thread_stop() - * when stop, the thread will interrupt the st_thread, - * which will cause the socket to return error and - * terminate the cycle thread. - * - * Usage 1: loop thread never quit. + * the endless thread is a loop thread never quit. * user can create thread always running util server terminate. * the step to create a thread never stop: - * 1. create SrsThread field, with joinable false. + * 1. create SrsEndlessThread field. * for example: - * class SrsStreamCache : public ISrsThreadHandler { - * public: SrsStreamCache() { pthread = new SrsThread("http-stream", this, SRS_AUTO_STREAM_SLEEP_US, false); } + * class SrsStreamCache : public ISrsEndlessThreadHandler { + * public: SrsStreamCache() { pthread = new SrsEndlessThread("http-stream", this); } * public: virtual int cycle() { - * // check status, start ffmpeg when stopped. + * // do some work never end. * } * } - * - * Usage 2: stop by other thread. - * user can create thread and stop then start again and again, - * generally must provides a start and stop method, @see SrsIngester. - * the step to create a thread stop by other thread: - * 1. create SrsThread field, with joinable true. - * 2. must use stop to stop and join the thread. - * for example: - * class SrsIngester : public ISrsThreadHandler { - * public: SrsIngester() { pthread = new SrsThread("ingest", this, SRS_AUTO_INGESTER_SLEEP_US, true); } - * public: virtual int start() { return pthread->start(); } - * public: virtual void stop() { pthread->stop(); } - * public: virtual int cycle() { - * // check status, start ffmpeg when stopped. - * } - * }; - * - * Usage 3: stop by thread itself. + * @remark user must use block method in cycle method, for example, sleep or socket io. + */ +class ISrsEndlessThreadHandler +{ +public: + ISrsEndlessThreadHandler(); + virtual ~ISrsEndlessThreadHandler(); +public: + /** + * the cycle method for the common thread. + * @remark user must use block method in cycle method, for example, sleep or socket io. + */ + virtual int cycle() = 0; +public: + /** + * other callback for handler. + * @remark all callback is optional, handler can ignore it. + */ + virtual void on_thread_start(); + virtual int on_before_cycle(); + virtual int on_end_cycle(); + virtual void on_thread_stop(); +}; +class SrsEndlessThread : public internal::ISrsThreadHandler +{ +private: + internal::SrsThread* pthread; + ISrsEndlessThreadHandler* handler; +public: + SrsEndlessThread(const char* n, ISrsEndlessThreadHandler* h); + virtual ~SrsEndlessThread(); +public: + /** + * for the endless thread, never quit. + */ + virtual int start(); +// interface internal::ISrsThreadHandler +public: + virtual int cycle(); + virtual void on_thread_start(); + virtual int on_before_cycle(); + virtual int on_end_cycle(); + virtual void on_thread_stop(); +}; + +/** + * the one cycle thread is a thread do the cycle only one time, + * that is, the thread will quit when return from the cycle. * user can create thread which stop itself, * generally only need to provides a start method, * the object will destroy itself then terminate the thread, @see SrsConnection - * 1. create SrsThread field, with joinable false. - * 2. owner stop thread loop, destroy itself when thread stop. + * 1. create SrsThread field + * 2. the thread quit when return from cycle. * for example: - * class SrsConnection : public ISrsThreadHandler { - * public: SrsConnection() { pthread = new SrsThread("conn", this, 0, false); } + * class SrsConnection : public ISrsOneCycleThreadHandler { + * public: SrsConnection() { pthread = new SrsOneCycleThread("conn", this); } * public: virtual int start() { return pthread->start(); } * public: virtual int cycle() { * // serve client. * // set loop to stop to quit, stop thread itself. * pthread->stop_loop(); * } - * public: virtual int on_thread_stop() { + * public: virtual void on_thread_stop() { * // remove the connection in thread itself. * server->remove(this); * } * }; - * - * Usage 4: loop in the cycle method. - * user can use loop code in the cycle method, @see SrsForwarder - * 1. create SrsThread field, with or without joinable is ok. - * 2. loop code in cycle method, check the can_loop() for thread to quit. + */ +class ISrsOneCycleThreadHandler +{ +public: + ISrsOneCycleThreadHandler(); + virtual ~ISrsOneCycleThreadHandler(); +public: + /** + * the cycle method for the one cycle thread. + */ + virtual int cycle() = 0; +public: + /** + * other callback for handler. + * @remark all callback is optional, handler can ignore it. + */ + virtual void on_thread_start(); + virtual int on_before_cycle(); + virtual int on_end_cycle(); + virtual void on_thread_stop(); +}; +class SrsOneCycleThread : public internal::ISrsThreadHandler +{ +private: + internal::SrsThread* pthread; + ISrsOneCycleThreadHandler* handler; +public: + SrsOneCycleThread(const char* n, ISrsOneCycleThreadHandler* h); + virtual ~SrsOneCycleThread(); +public: + /** + * for the one cycle thread, quit when cycle return. + */ + virtual int start(); +// interface internal::ISrsThreadHandler +public: + virtual int cycle(); + virtual void on_thread_start(); + virtual int on_before_cycle(); + virtual int on_end_cycle(); + virtual void on_thread_stop(); +}; + +/** + * the reuse thread is a thread stop and start by other thread. + * user can create thread and stop then start again and again, + * generally must provides a start and stop method, @see SrsIngester. + * the step to create a thread stop by other thread: + * 1. create SrsReusableThread field. + * 2. must manually stop the thread when started it. * for example: - * class SrsForwarder : public ISrsThreadHandler { + * class SrsIngester : public ISrsReusableThreadHandler { + * public: SrsIngester() { pthread = new SrsReusableThread("ingest", this, SRS_AUTO_INGESTER_SLEEP_US); } + * public: virtual int start() { return pthread->start(); } + * public: virtual void stop() { pthread->stop(); } * public: virtual int cycle() { - * while (pthread->can_loop()) { - * // read msgs from queue and forward to server. - * } + * // check status, start ffmpeg when stopped. * } * }; - * - * @remark why should check can_loop() in cycle method? - * when thread interrupt, the socket maybe not got EINT, - * espectially on st_usleep(), so the cycle must check the loop, - * when handler->cycle() has loop itself, for example: - * while (true): - * if (read_from_socket(skt) < 0) break; - * if thread stop when read_from_socket, it's ok, the loop will break, - * but when thread stop interrupt the s_usleep(0), then the loop is - * death loop. - * in a word, the handler->cycle() must: - * while (pthread->can_loop()): - * if (read_from_socket(skt) < 0) break; - * check the loop, then it works. - * - * @remark why should use stop_loop() to terminate thread in itself? - * in the thread itself, that is the cycle method, - * if itself want to terminate the thread, should never use stop(), - * but use stop_loop() to set the loop to false and terminate normally. - * - * @remark when should set the interval_us, and when not? - * the cycle will invoke util cannot loop, eventhough the return code of cycle is error, - * so the interval_us used to sleep for each cycle. */ -class ISrsThreadHandler +class ISrsReusableThreadHandler { public: - ISrsThreadHandler(); - virtual ~ISrsThreadHandler(); + ISrsReusableThreadHandler(); + virtual ~ISrsReusableThreadHandler(); +public: + /** + * the cycle method for the one cycle thread. + * @remark when the cycle has its inner loop, it must check whether + * the thread is interrupted. + */ + virtual int cycle() = 0; public: + /** + * other callback for handler. + * @remark all callback is optional, handler can ignore it. + */ + virtual void on_thread_start(); + virtual int on_before_cycle(); + virtual int on_end_cycle(); + virtual void on_thread_stop(); +}; +class SrsReusableThread : public internal::ISrsThreadHandler +{ +private: + internal::SrsThread* pthread; + ISrsReusableThreadHandler* handler; +public: + SrsReusableThread(const char* n, ISrsReusableThreadHandler* h, int64_t interval_us = 0); + virtual ~SrsReusableThread(); +public: + /** + * for the reusable thread, start and stop by user. + */ + virtual int start(); + /** + * stop the thread, wait for the thread to terminate. + * @remark user can stop multiple times, ignore if already stopped. + */ + virtual void stop(); +public: + /** + * get the context id. @see: ISrsThreadContext.get_id(). + * used for parent thread to get the id. + * @remark when start thread, parent thread will block and wait for this id ready. + */ + virtual int cid(); +// interface internal::ISrsThreadHandler +public: + virtual int cycle(); virtual void on_thread_start(); virtual int on_before_cycle(); - virtual int cycle() = 0; virtual int on_end_cycle(); virtual void on_thread_stop(); }; /** -* provides servies from st_thread_t, -* for common thread usage. -*/ -class SrsThread + * the reuse thread is a thread stop and start by other thread. + * the version 2, is the thread cycle has its inner loop, which should + * check the intterrupt, and should interrupt thread when the inner loop want + * to quit the thread. + * user can create thread and stop then start again and again, + * generally must provides a start and stop method, @see SrsIngester. + * the step to create a thread stop by other thread: + * 1. create SrsReusableThread field. + * 2. must manually stop the thread when started it. + * for example: + * class SrsIngester : public ISrsReusableThreadHandler { + * public: SrsIngester() { pthread = new SrsReusableThread("ingest", this, SRS_AUTO_INGESTER_SLEEP_US); } + * public: virtual int start() { return pthread->start(); } + * public: virtual void stop() { pthread->stop(); } + * public: virtual int cycle() { + * while (!pthread->interrupted()) { + * // quit thread when error. + * if (ret != ERROR_SUCCESS) { + * pthread->interrupt(); + * } + * + * // do something. + * } + * } + * }; + */ +class ISrsReusableThread2Handler +{ +public: + ISrsReusableThread2Handler(); + virtual ~ISrsReusableThread2Handler(); +public: + /** + * the cycle method for the one cycle thread. + * @remark when the cycle has its inner loop, it must check whether + * the thread is interrupted. + */ + virtual int cycle() = 0; +public: + /** + * other callback for handler. + * @remark all callback is optional, handler can ignore it. + */ + virtual void on_thread_start(); + virtual int on_before_cycle(); + virtual int on_end_cycle(); + virtual void on_thread_stop(); +}; +class SrsReusableThread2 : public internal::ISrsThreadHandler { private: - st_thread_t tid; - int _cid; - bool loop; - bool can_run; - bool really_terminated; - bool _joinable; - const char* _name; -private: - ISrsThreadHandler* handler; - int64_t cycle_interval_us; -public: - /** - * initialize the thread. - * @param name, human readable name for st debug. - * @param thread_handler, the cycle handler for the thread. - * @param interval_us, the sleep interval when cycle finished. - * @param joinable, if joinable, other thread must stop the thread. - * @remark if joinable, thread never quit itself, or memory leak. - * @see: https://github.com/simple-rtmp-server/srs/issues/78 - * @remark about st debug, see st-1.9/README, _st_iterate_threads_flag - */ - /** - * TODO: FIXME: maybe all thread must be reap by others threads, - * @see: https://github.com/simple-rtmp-server/srs/issues/77 - */ - SrsThread(const char* name, ISrsThreadHandler* thread_handler, int64_t interval_us, bool joinable); - virtual ~SrsThread(); -public: - /** - * get the context id. @see: ISrsThreadContext.get_id(). - * used for parent thread to get the id. - * @remark when start thread, parent thread will block and wait for this id ready. - */ - virtual int cid(); + internal::SrsThread* pthread; + ISrsReusableThread2Handler* handler; +public: + SrsReusableThread2(const char* n, ISrsReusableThread2Handler* h, int64_t interval_us = 0); + virtual ~SrsReusableThread2(); +public: /** - * start the thread, invoke the cycle of handler util - * user stop the thread. - * @remark ignore any error of cycle of handler. - * @remark user can start multiple times, ignore if already started. - * @remark wait for the cid is set by thread pfn. - */ + * for the reusable thread, start and stop by user. + */ virtual int start(); /** - * stop the thread, wait for the thread to terminate. - * @remark user can stop multiple times, ignore if already stopped. - */ + * stop the thread, wait for the thread to terminate. + * @remark user can stop multiple times, ignore if already stopped. + */ virtual void stop(); public: /** - * whether the thread should loop, - * used for handler->cycle() which has a loop method, - * to check this method, break if false. - */ - virtual bool can_loop(); + * get the context id. @see: ISrsThreadContext.get_id(). + * used for parent thread to get the id. + * @remark when start thread, parent thread will block and wait for this id ready. + */ + virtual int cid(); /** - * for the loop thread to stop the loop. - * other thread can directly use stop() to stop loop and wait for quit. - * this stop loop method only set loop to false. - */ - virtual void stop_loop(); -private: - virtual void thread_cycle(); - static void* thread_fun(void* arg); + * interrupt the thread to stop loop. + * we only set the loop flag to false, not really interrupt the thread. + */ + virtual void interrupt(); + /** + * whether the thread is interrupted, + * for the cycle has its loop, the inner loop should quit when thread + * is interrupted. + */ + virtual bool interrupted(); +// interface internal::ISrsThreadHandler +public: + virtual int cycle(); + virtual void on_thread_start(); + virtual int on_before_cycle(); + virtual int on_end_cycle(); + virtual void on_thread_stop(); }; #endif diff --git a/trunk/src/main/srs_main_ingest_hls.cpp b/trunk/src/main/srs_main_ingest_hls.cpp index 4de2740ca..f63301166 100644 --- a/trunk/src/main/srs_main_ingest_hls.cpp +++ b/trunk/src/main/srs_main_ingest_hls.cpp @@ -39,7 +39,6 @@ using namespace std; #include #include #include -#include #include #include #include @@ -47,6 +46,7 @@ using namespace std; #include #include #include +#include // pre-declare int proxy_hls2rtmp(std::string hls, std::string rtmp); @@ -383,14 +383,14 @@ int SrsIngestSrsInput::parseM3u8(SrsHttpUri* url, double& td, double& duration) return ret; } - SrsHttpMessage* msg = NULL; + ISrsHttpMessage* msg = NULL; if ((ret = client.get(url->get_path(), "", &msg)) != ERROR_SUCCESS) { srs_error("HTTP GET %s failed. ret=%d", url->get_url(), ret); return ret; } srs_assert(msg); - SrsAutoFree(SrsHttpMessage, msg); + SrsAutoFree(ISrsHttpMessage, msg); std::string body; if ((ret = msg->body_read_all(body)) != ERROR_SUCCESS) { @@ -605,14 +605,14 @@ int SrsIngestSrsInput::SrsTsPiece::fetch(string m3u8) return ret; } - SrsHttpMessage* msg = NULL; + ISrsHttpMessage* msg = NULL; if ((ret = client.get(uri.get_path(), "", &msg)) != ERROR_SUCCESS) { srs_error("HTTP GET %s failed. ret=%d", uri.get_url(), ret); return ret; } srs_assert(msg); - SrsAutoFree(SrsHttpMessage, msg); + SrsAutoFree(ISrsHttpMessage, msg); if ((ret = msg->body_read_all(body)) != ERROR_SUCCESS) { srs_error("read ts failed. ret=%d", ret); diff --git a/trunk/src/protocol/srs_http_stack.cpp b/trunk/src/protocol/srs_http_stack.cpp new file mode 100644 index 000000000..6e46e23e2 --- /dev/null +++ b/trunk/src/protocol/srs_http_stack.cpp @@ -0,0 +1,738 @@ +/* + The MIT License (MIT) + + Copyright (c) 2013-2015 SRS(simple-rtmp-server) + + 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. + */ + +#include + +#include +using namespace std; + +#include +#include +#include +#include + +#define SRS_HTTP_DEFAULT_PAGE "index.html" + +// get the status text of code. +string srs_generate_http_status_text(int status) +{ + static std::map _status_map; + if (_status_map.empty()) { + _status_map[SRS_CONSTS_HTTP_Continue ] = SRS_CONSTS_HTTP_Continue_str ; + _status_map[SRS_CONSTS_HTTP_SwitchingProtocols ] = SRS_CONSTS_HTTP_SwitchingProtocols_str ; + _status_map[SRS_CONSTS_HTTP_OK ] = SRS_CONSTS_HTTP_OK_str ; + _status_map[SRS_CONSTS_HTTP_Created ] = SRS_CONSTS_HTTP_Created_str ; + _status_map[SRS_CONSTS_HTTP_Accepted ] = SRS_CONSTS_HTTP_Accepted_str ; + _status_map[SRS_CONSTS_HTTP_NonAuthoritativeInformation ] = SRS_CONSTS_HTTP_NonAuthoritativeInformation_str ; + _status_map[SRS_CONSTS_HTTP_NoContent ] = SRS_CONSTS_HTTP_NoContent_str ; + _status_map[SRS_CONSTS_HTTP_ResetContent ] = SRS_CONSTS_HTTP_ResetContent_str ; + _status_map[SRS_CONSTS_HTTP_PartialContent ] = SRS_CONSTS_HTTP_PartialContent_str ; + _status_map[SRS_CONSTS_HTTP_MultipleChoices ] = SRS_CONSTS_HTTP_MultipleChoices_str ; + _status_map[SRS_CONSTS_HTTP_MovedPermanently ] = SRS_CONSTS_HTTP_MovedPermanently_str ; + _status_map[SRS_CONSTS_HTTP_Found ] = SRS_CONSTS_HTTP_Found_str ; + _status_map[SRS_CONSTS_HTTP_SeeOther ] = SRS_CONSTS_HTTP_SeeOther_str ; + _status_map[SRS_CONSTS_HTTP_NotModified ] = SRS_CONSTS_HTTP_NotModified_str ; + _status_map[SRS_CONSTS_HTTP_UseProxy ] = SRS_CONSTS_HTTP_UseProxy_str ; + _status_map[SRS_CONSTS_HTTP_TemporaryRedirect ] = SRS_CONSTS_HTTP_TemporaryRedirect_str ; + _status_map[SRS_CONSTS_HTTP_BadRequest ] = SRS_CONSTS_HTTP_BadRequest_str ; + _status_map[SRS_CONSTS_HTTP_Unauthorized ] = SRS_CONSTS_HTTP_Unauthorized_str ; + _status_map[SRS_CONSTS_HTTP_PaymentRequired ] = SRS_CONSTS_HTTP_PaymentRequired_str ; + _status_map[SRS_CONSTS_HTTP_Forbidden ] = SRS_CONSTS_HTTP_Forbidden_str ; + _status_map[SRS_CONSTS_HTTP_NotFound ] = SRS_CONSTS_HTTP_NotFound_str ; + _status_map[SRS_CONSTS_HTTP_MethodNotAllowed ] = SRS_CONSTS_HTTP_MethodNotAllowed_str ; + _status_map[SRS_CONSTS_HTTP_NotAcceptable ] = SRS_CONSTS_HTTP_NotAcceptable_str ; + _status_map[SRS_CONSTS_HTTP_ProxyAuthenticationRequired ] = SRS_CONSTS_HTTP_ProxyAuthenticationRequired_str ; + _status_map[SRS_CONSTS_HTTP_RequestTimeout ] = SRS_CONSTS_HTTP_RequestTimeout_str ; + _status_map[SRS_CONSTS_HTTP_Conflict ] = SRS_CONSTS_HTTP_Conflict_str ; + _status_map[SRS_CONSTS_HTTP_Gone ] = SRS_CONSTS_HTTP_Gone_str ; + _status_map[SRS_CONSTS_HTTP_LengthRequired ] = SRS_CONSTS_HTTP_LengthRequired_str ; + _status_map[SRS_CONSTS_HTTP_PreconditionFailed ] = SRS_CONSTS_HTTP_PreconditionFailed_str ; + _status_map[SRS_CONSTS_HTTP_RequestEntityTooLarge ] = SRS_CONSTS_HTTP_RequestEntityTooLarge_str ; + _status_map[SRS_CONSTS_HTTP_RequestURITooLarge ] = SRS_CONSTS_HTTP_RequestURITooLarge_str ; + _status_map[SRS_CONSTS_HTTP_UnsupportedMediaType ] = SRS_CONSTS_HTTP_UnsupportedMediaType_str ; + _status_map[SRS_CONSTS_HTTP_RequestedRangeNotSatisfiable ] = SRS_CONSTS_HTTP_RequestedRangeNotSatisfiable_str ; + _status_map[SRS_CONSTS_HTTP_ExpectationFailed ] = SRS_CONSTS_HTTP_ExpectationFailed_str ; + _status_map[SRS_CONSTS_HTTP_InternalServerError ] = SRS_CONSTS_HTTP_InternalServerError_str ; + _status_map[SRS_CONSTS_HTTP_NotImplemented ] = SRS_CONSTS_HTTP_NotImplemented_str ; + _status_map[SRS_CONSTS_HTTP_BadGateway ] = SRS_CONSTS_HTTP_BadGateway_str ; + _status_map[SRS_CONSTS_HTTP_ServiceUnavailable ] = SRS_CONSTS_HTTP_ServiceUnavailable_str ; + _status_map[SRS_CONSTS_HTTP_GatewayTimeout ] = SRS_CONSTS_HTTP_GatewayTimeout_str ; + _status_map[SRS_CONSTS_HTTP_HTTPVersionNotSupported ] = SRS_CONSTS_HTTP_HTTPVersionNotSupported_str ; + } + + std::string status_text; + if (_status_map.find(status) == _status_map.end()) { + status_text = "Status Unknown"; + } else { + status_text = _status_map[status]; + } + + return status_text; +} + +// bodyAllowedForStatus reports whether a given response status code +// permits a body. See RFC2616, section 4.4. +bool srs_go_http_body_allowd(int status) +{ + if (status >= 100 && status <= 199) { + return false; + } else if (status == 204 || status == 304) { + return false; + } + + return true; +} + +// DetectContentType implements the algorithm described +// at http://mimesniff.spec.whatwg.org/ to determine the +// Content-Type of the given data. It considers at most the +// first 512 bytes of data. DetectContentType always returns +// a valid MIME type: if it cannot determine a more specific one, it +// returns "application/octet-stream". +string srs_go_http_detect(char* data, int size) +{ + // detect only when data specified. + if (data) { + } + return "application/octet-stream"; // fallback +} + +// Error replies to the request with the specified error message and HTTP code. +// The error message should be plain text. +int srs_go_http_error(ISrsHttpResponseWriter* w, int code, string error) +{ + int ret = ERROR_SUCCESS; + + w->header()->set_content_type("text/plain; charset=utf-8"); + w->header()->set_content_length(error.length()); + w->write_header(code); + w->write((char*)error.data(), (int)error.length()); + + return ret; +} + +int srs_http_response_json(ISrsHttpResponseWriter* w, string data) +{ + SrsHttpHeader* h = w->header(); + + h->set_content_length(data.length()); + h->set_content_type("application/json"); + + return w->write((char*)data.data(), (int)data.length()); +} + +SrsHttpHeader::SrsHttpHeader() +{ +} + +SrsHttpHeader::~SrsHttpHeader() +{ +} + +void SrsHttpHeader::set(string key, string value) +{ + headers[key] = value; +} + +string SrsHttpHeader::get(string key) +{ + std::string v; + + if (headers.find(key) != headers.end()) { + v = headers[key]; + } + + return v; +} + +int64_t SrsHttpHeader::content_length() +{ + std::string cl = get("Content-Length"); + + if (cl.empty()) { + return -1; + } + + return (int64_t)::atof(cl.c_str()); +} + +void SrsHttpHeader::set_content_length(int64_t size) +{ + char buf[64]; + snprintf(buf, sizeof(buf), "%"PRId64, size); + set("Content-Length", buf); +} + +string SrsHttpHeader::content_type() +{ + return get("Content-Type"); +} + +void SrsHttpHeader::set_content_type(string ct) +{ + set("Content-Type", ct); +} + +void SrsHttpHeader::write(stringstream& ss) +{ + std::map::iterator it; + for (it = headers.begin(); it != headers.end(); ++it) { + ss << it->first << ": " << it->second << SRS_HTTP_CRLF; + } +} + +ISrsHttpResponseWriter::ISrsHttpResponseWriter() +{ +} + +ISrsHttpResponseWriter::~ISrsHttpResponseWriter() +{ +} + +ISrsHttpResponseReader::ISrsHttpResponseReader() +{ +} + +ISrsHttpResponseReader::~ISrsHttpResponseReader() +{ +} + +ISrsHttpHandler::ISrsHttpHandler() +{ + entry = NULL; +} + +ISrsHttpHandler::~ISrsHttpHandler() +{ +} + +SrsHttpRedirectHandler::SrsHttpRedirectHandler(string u, int c) +{ + url = u; + code = c; +} + +SrsHttpRedirectHandler::~SrsHttpRedirectHandler() +{ +} + +int SrsHttpRedirectHandler::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) +{ + int ret = ERROR_SUCCESS; + // TODO: FIXME: implements it. + return ret; +} + +SrsHttpNotFoundHandler::SrsHttpNotFoundHandler() +{ +} + +SrsHttpNotFoundHandler::~SrsHttpNotFoundHandler() +{ +} + +int SrsHttpNotFoundHandler::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) +{ + return srs_go_http_error(w, SRS_CONSTS_HTTP_NotFound, SRS_CONSTS_HTTP_NotFound_str); +} + +SrsHttpFileServer::SrsHttpFileServer(string root_dir) +{ + dir = root_dir; +} + +SrsHttpFileServer::~SrsHttpFileServer() +{ +} + +int SrsHttpFileServer::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) +{ + string upath = r->path(); + + // add default pages. + if (srs_string_ends_with(upath, "/")) { + upath += SRS_HTTP_DEFAULT_PAGE; + } + + string fullpath = dir + "/"; + + // remove the virtual directory. + 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; + } + + // stat current dir, if exists, return error. + 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); + } + srs_trace("http match file=%s, pattern=%s, upath=%s", + fullpath.c_str(), entry->pattern.c_str(), upath.c_str()); + + // handle file according to its extension. + // use vod stream for .flv/.fhv + if (srs_string_ends_with(fullpath, ".flv") || srs_string_ends_with(fullpath, ".fhv")) { + return serve_flv_file(w, r, fullpath); + } else if (srs_string_ends_with(fullpath, ".mp4")) { + return serve_mp4_file(w, r, fullpath); + } + + // serve common static file. + return serve_file(w, r, fullpath); +} + +int SrsHttpFileServer::serve_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath) +{ + int ret = ERROR_SUCCESS; + + // open the target file. + SrsFileReader fs; + + if ((ret = fs.open(fullpath)) != ERROR_SUCCESS) { + srs_warn("open file %s failed, ret=%d", fullpath.c_str(), ret); + return ret; + } + + int64_t length = fs.filesize(); + + // unset the content length to encode in chunked encoding. + w->header()->set_content_length(length); + + static std::map _mime; + if (_mime.empty()) { + _mime[".ts"] = "video/MP2T"; + _mime[".flv"] = "video/x-flv"; + _mime[".m4v"] = "video/x-m4v"; + _mime[".3gpp"] = "video/3gpp"; + _mime[".3gp"] = "video/3gpp"; + _mime[".mp4"] = "video/mp4"; + _mime[".aac"] = "audio/x-aac"; + _mime[".mp3"] = "audio/mpeg"; + _mime[".m4a"] = "audio/x-m4a"; + _mime[".ogg"] = "audio/ogg"; + // @see hls-m3u8-draft-pantos-http-live-streaming-12.pdf, page 5. + _mime[".m3u8"] = "application/vnd.apple.mpegurl"; // application/x-mpegURL + _mime[".rss"] = "application/rss+xml"; + _mime[".json"] = "application/json"; + _mime[".swf"] = "application/x-shockwave-flash"; + _mime[".doc"] = "application/msword"; + _mime[".zip"] = "application/zip"; + _mime[".rar"] = "application/x-rar-compressed"; + _mime[".xml"] = "text/xml"; + _mime[".html"] = "text/html"; + _mime[".js"] = "text/javascript"; + _mime[".css"] = "text/css"; + _mime[".ico"] = "image/x-icon"; + _mime[".png"] = "image/png"; + _mime[".jpeg"] = "image/jpeg"; + _mime[".jpg"] = "image/jpeg"; + _mime[".gif"] = "image/gif"; + } + + if (true) { + size_t pos; + std::string ext = fullpath; + if ((pos = ext.rfind(".")) != string::npos) { + ext = ext.substr(pos); + } + + if (_mime.find(ext) == _mime.end()) { + w->header()->set_content_type("application/octet-stream"); + } else { + w->header()->set_content_type(_mime[ext]); + } + } + + // write body. + int64_t left = length; + if ((ret = copy(w, &fs, r, (int)left)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("read file=%s size=%d failed, ret=%d", fullpath.c_str(), left, ret); + } + return ret; + } + + return w->final_request(); +} + +int SrsHttpFileServer::serve_flv_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath) +{ + std::string start = r->query_get("start"); + if (start.empty()) { + return serve_file(w, r, fullpath); + } + + int offset = ::atoi(start.c_str()); + if (offset <= 0) { + return serve_file(w, r, fullpath); + } + + return serve_flv_stream(w, r, fullpath, offset); +} + +int 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 + if (range.empty()) { + range = r->query_get("bytes"); + } + + // rollback to serve whole file. + size_t pos = string::npos; + if (range.empty() || (pos = range.find("-")) == string::npos) { + return serve_file(w, r, fullpath); + } + + // parse the start in query string + int start = 0; + if (pos > 0) { + start = ::atoi(range.substr(0, pos).c_str()); + } + + // parse end in query string. + int end = -1; + if (pos < range.length() - 1) { + end = ::atoi(range.substr(pos + 1).c_str()); + } + + // invalid param, serve as whole mp4 file. + if (start < 0 || (end != -1 && start > end)) { + return serve_file(w, r, fullpath); + } + + return serve_mp4_stream(w, r, fullpath, start, end); +} + +int SrsHttpFileServer::serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath, int offset) +{ + return serve_file(w, r, fullpath); +} + +int SrsHttpFileServer::serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath, int start, int end) +{ + return serve_file(w, r, fullpath); +} + +int SrsHttpFileServer::copy(ISrsHttpResponseWriter* w, SrsFileReader* fs, ISrsHttpMessage* r, int size) +{ + int ret = ERROR_SUCCESS; + + int left = size; + char* buf = r->http_ts_send_buffer(); + + while (left > 0) { + ssize_t nread = -1; + int max_read = srs_min(left, SRS_HTTP_TS_SEND_BUFFER_SIZE); + if ((ret = fs->read(buf, max_read, &nread)) != ERROR_SUCCESS) { + break; + } + + left -= nread; + if ((ret = w->write(buf, (int)nread)) != ERROR_SUCCESS) { + break; + } + } + + return ret; +} + +SrsHttpMuxEntry::SrsHttpMuxEntry() +{ + enabled = true; + explicit_match = false; + handler = NULL; +} + +SrsHttpMuxEntry::~SrsHttpMuxEntry() +{ + srs_freep(handler); +} + +ISrsHttpMatchHijacker::ISrsHttpMatchHijacker() +{ +} + +ISrsHttpMatchHijacker::~ISrsHttpMatchHijacker() +{ +} + +SrsHttpServeMux::SrsHttpServeMux() +{ +} + +SrsHttpServeMux::~SrsHttpServeMux() +{ + std::map::iterator it; + for (it = entries.begin(); it != entries.end(); ++it) { + SrsHttpMuxEntry* entry = it->second; + srs_freep(entry); + } + entries.clear(); + + vhosts.clear(); + hijackers.clear(); +} + +int SrsHttpServeMux::initialize() +{ + int ret = ERROR_SUCCESS; + // TODO: FIXME: implements it. + return ret; +} + +void SrsHttpServeMux::hijack(ISrsHttpMatchHijacker* h) +{ + std::vector::iterator it = ::find(hijackers.begin(), hijackers.end(), h); + if (it != hijackers.end()) { + return; + } + hijackers.push_back(h); +} + +void SrsHttpServeMux::unhijack(ISrsHttpMatchHijacker* h) +{ + std::vector::iterator it = ::find(hijackers.begin(), hijackers.end(), h); + if (it == hijackers.end()) { + return; + } + hijackers.erase(it); +} + +int SrsHttpServeMux::handle(std::string pattern, ISrsHttpHandler* handler) +{ + int ret = ERROR_SUCCESS; + + srs_assert(handler); + + if (pattern.empty()) { + ret = ERROR_HTTP_PATTERN_EMPTY; + srs_error("http: empty pattern. ret=%d", ret); + return ret; + } + + if (entries.find(pattern) != entries.end()) { + SrsHttpMuxEntry* exists = entries[pattern]; + if (exists->explicit_match) { + ret = ERROR_HTTP_PATTERN_DUPLICATED; + srs_error("http: multiple registrations for %s. ret=%d", pattern.c_str(), ret); + return ret; + } + } + + std::string vhost = pattern; + if (pattern.at(0) != '/') { + if (pattern.find("/") != string::npos) { + vhost = pattern.substr(0, pattern.find("/")); + } + vhosts[vhost] = handler; + } + + if (true) { + SrsHttpMuxEntry* entry = new SrsHttpMuxEntry(); + entry->explicit_match = true; + entry->handler = handler; + entry->pattern = pattern; + entry->handler->entry = entry; + + if (entries.find(pattern) != entries.end()) { + SrsHttpMuxEntry* exists = entries[pattern]; + srs_freep(exists); + } + entries[pattern] = entry; + } + + // Helpful behavior: + // If pattern is /tree/, insert an implicit permanent redirect for /tree. + // It can be overridden by an explicit registration. + if (pattern != "/" && !pattern.empty() && pattern.at(pattern.length() - 1) == '/') { + std::string rpattern = pattern.substr(0, pattern.length() - 1); + SrsHttpMuxEntry* entry = NULL; + + // free the exists not explicit entry + if (entries.find(rpattern) != entries.end()) { + SrsHttpMuxEntry* exists = entries[rpattern]; + if (!exists->explicit_match) { + entry = exists; + } + } + + // create implicit redirect. + if (!entry || entry->explicit_match) { + srs_freep(entry); + + entry = new SrsHttpMuxEntry(); + entry->explicit_match = false; + entry->handler = new SrsHttpRedirectHandler(pattern, SRS_CONSTS_HTTP_MovedPermanently); + entry->pattern = pattern; + entry->handler->entry = entry; + + entries[rpattern] = entry; + } + } + + return ret; +} + +int SrsHttpServeMux::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) +{ + int ret = ERROR_SUCCESS; + + ISrsHttpHandler* h = NULL; + if ((ret = find_handler(r, &h)) != ERROR_SUCCESS) { + srs_error("find handler failed. ret=%d", ret); + return ret; + } + + srs_assert(h); + if ((ret = h->serve_http(w, r)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("handler serve http failed. ret=%d", ret); + } + return ret; + } + + return ret; +} + +int SrsHttpServeMux::find_handler(ISrsHttpMessage* r, ISrsHttpHandler** ph) +{ + int ret = ERROR_SUCCESS; + + // TODO: FIXME: support the path . and .. + if (r->url().find("..") != std::string::npos) { + ret = ERROR_HTTP_URL_NOT_CLEAN; + srs_error("htt url not canonical, url=%s. ret=%d", r->url().c_str(), ret); + return ret; + } + + if ((ret = match(r, ph)) != ERROR_SUCCESS) { + srs_error("http match handler failed. ret=%d", ret); + return ret; + } + + // always hijack. + if (!hijackers.empty()) { + // notice all hijacker the match failed. + std::vector::iterator it; + for (it = hijackers.begin(); it != hijackers.end(); ++it) { + ISrsHttpMatchHijacker* hijacker = *it; + if ((ret = hijacker->hijack(r, ph)) != ERROR_SUCCESS) { + srs_error("hijacker match failed. ret=%d", ret); + return ret; + } + } + } + + if (*ph == NULL) { + // TODO: FIXME: memory leak. + *ph = new SrsHttpNotFoundHandler(); + } + + return ret; +} + +int SrsHttpServeMux::match(ISrsHttpMessage* r, ISrsHttpHandler** ph) +{ + int ret = ERROR_SUCCESS; + + std::string path = r->path(); + + // Host-specific pattern takes precedence over generic ones + if (!vhosts.empty() && vhosts.find(r->host()) != vhosts.end()) { + path = r->host() + path; + } + + int nb_matched = 0; + ISrsHttpHandler* h = NULL; + + std::map::iterator it; + for (it = entries.begin(); it != entries.end(); ++it) { + std::string pattern = it->first; + SrsHttpMuxEntry* entry = it->second; + + if (!entry->enabled) { + continue; + } + + if (!path_match(pattern, path)) { + continue; + } + + if (!h || (int)pattern.length() > nb_matched) { + nb_matched = (int)pattern.length(); + h = entry->handler; + } + } + + *ph = h; + + return ret; +} + +bool SrsHttpServeMux::path_match(string pattern, string path) +{ + if (pattern.empty()) { + return false; + } + + int n = (int)pattern.length(); + + // not endswith '/', exactly match. + if (pattern.at(n - 1) != '/') { + return pattern == path; + } + + // endswith '/', match any, + // for example, '/api/' match '/api/[N]' + if ((int)path.length() >= n) { + if (memcmp(pattern.data(), path.data(), n) == 0) { + return true; + } + } + + return false; +} + +ISrsHttpMessage::ISrsHttpMessage() +{ + _http_ts_send_buffer = new char[SRS_HTTP_TS_SEND_BUFFER_SIZE]; +} + +ISrsHttpMessage::~ISrsHttpMessage() +{ + srs_freep(_http_ts_send_buffer); +} + +char* ISrsHttpMessage::http_ts_send_buffer() +{ + return _http_ts_send_buffer; +} diff --git a/trunk/src/protocol/srs_http_stack.hpp b/trunk/src/protocol/srs_http_stack.hpp new file mode 100644 index 000000000..8b14aad0e --- /dev/null +++ b/trunk/src/protocol/srs_http_stack.hpp @@ -0,0 +1,494 @@ +/* + The MIT License (MIT) + + Copyright (c) 2013-2015 SRS(simple-rtmp-server) + + 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_PROTOCOL_HTTP_HPP +#define SRS_PROTOCOL_HTTP_HPP + +/* + #include + */ +#include + +#include +#include +#include + +class SrsFileReader; +class SrsHttpHeader; +class ISrsHttpMessage; +class SrsHttpMuxEntry; +class ISrsHttpResponseWriter; + +// http specification +// CR = +#define SRS_HTTP_CR SRS_CONSTS_CR // 0x0D +// LF = +#define SRS_HTTP_LF SRS_CONSTS_LF // 0x0A +// SP = +#define SRS_HTTP_SP ' ' // 0x20 +// HT = +#define SRS_HTTP_HT '\x09' // 0x09 + +// HTTP/1.1 defines the sequence CR LF as the end-of-line marker for all +// protocol elements except the entity-body (see appendix 19.3 for +// tolerant applications). +#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 + +// default http listen port. +#define SRS_DEFAULT_HTTP_PORT 80 + +// for http parser macros +#define SRS_CONSTS_HTTP_OPTIONS HTTP_OPTIONS +#define SRS_CONSTS_HTTP_GET HTTP_GET +#define SRS_CONSTS_HTTP_POST HTTP_POST +#define SRS_CONSTS_HTTP_PUT HTTP_PUT +#define SRS_CONSTS_HTTP_DELETE HTTP_DELETE + +// helper function: response in json format. +extern int srs_http_response_json(ISrsHttpResponseWriter* w, std::string data); + +// get the status text of code. +extern std::string srs_generate_http_status_text(int status); + +// bodyAllowedForStatus reports whether a given response status code +// permits a body. See RFC2616, section 4.4. +extern bool srs_go_http_body_allowd(int status); + +// DetectContentType implements the algorithm described +// at http://mimesniff.spec.whatwg.org/ to determine the +// Content-Type of the given data. It considers at most the +// first 512 bytes of data. DetectContentType always returns +// a valid MIME type: if it cannot determine a more specific one, it +// returns "application/octet-stream". +extern std::string srs_go_http_detect(char* data, int size); + +// state of message +enum SrsHttpParseState { + SrsHttpParseStateInit = 0, + SrsHttpParseStateStart, + SrsHttpParseStateHeaderComplete, + SrsHttpParseStateMessageComplete +}; + +// A Header represents the key-value pairs in an HTTP header. +class SrsHttpHeader +{ +private: + std::map headers; +public: + SrsHttpHeader(); + virtual ~SrsHttpHeader(); +public: + // Add adds the key, value pair to the header. + // It appends to any existing values associated with key. + virtual void set(std::string key, std::string value); + // Get gets the first value associated with the given key. + // If there are no values associated with the key, Get returns "". + // To access multiple values of a key, access the map directly + // with CanonicalHeaderKey. + virtual std::string get(std::string key); +public: + /** + * get the content length. -1 if not set. + */ + virtual int64_t content_length(); + /** + * set the content length by header "Content-Length" + */ + virtual void set_content_length(int64_t size); +public: + /** + * get the content type. empty string if not set. + */ + virtual std::string content_type(); + /** + * set the content type by header "Content-Type" + */ + virtual void set_content_type(std::string ct); +public: + /** + * write all headers to string stream. + */ + virtual void write(std::stringstream& ss); +}; + +// A ResponseWriter interface is used by an HTTP handler to +// construct an HTTP response. +// Usage 1, response with specified length content: +// ISrsHttpResponseWriter* w; // create or get response. +// 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(SRS_CONSTS_HTTP_OK); +// w->write((char*)msg.data(), (int)msg.length()); +// w->final_request(); // optional flush. +// Usage 2, response with HTTP code only, zero content length. +// ISrsHttpResponseWriter* w; // create or get response. +// w->header()->set_content_length(0); +// w->write_header(SRS_CONSTS_HTTP_OK); +// w->final_request(); +// Usage 3, response in chunked encoding. +// ISrsHttpResponseWriter* w; // create or get response. +// std::string msg = "Hello, HTTP!"; +// w->header()->set_content_type("application/octet-stream"); +// w->write_header(SRS_CONSTS_HTTP_OK); +// 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 ISrsHttpResponseWriter +{ +public: + ISrsHttpResponseWriter(); + virtual ~ISrsHttpResponseWriter(); +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 int 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 int write(char* data, int size) = 0; + + // WriteHeader sends an HTTP response 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(int code) = 0; +}; + +/** + * the reader interface for http response. + */ +class ISrsHttpResponseReader +{ +public: + ISrsHttpResponseReader(); + virtual ~ISrsHttpResponseReader(); +public: + /** + * whether response read EOF. + */ + virtual bool eof() = 0; + /** + * read from the response body. + * @param data, the buffer to read data buffer to. + * @param nb_data, the max size of data buffer. + * @param nb_read, the actual read size of bytes. NULL to ignore. + * @remark when eof(), return error. + */ + virtual int read(char* data, int nb_data, int* nb_read) = 0; +}; + +// Objects implementing the Handler interface can be +// registered to serve a particular path or subtree +// in the HTTP server. +// +// ServeHTTP should write reply headers and data to the ResponseWriter +// and then return. Returning signals that the request is finished +// and that the HTTP server can move on to the next request on +// the connection. +class ISrsHttpHandler +{ +public: + SrsHttpMuxEntry* entry; +public: + ISrsHttpHandler(); + virtual ~ISrsHttpHandler(); +public: + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) = 0; +}; + +// Redirect to a fixed URL +class SrsHttpRedirectHandler : public ISrsHttpHandler +{ +private: + std::string url; + int code; +public: + SrsHttpRedirectHandler(std::string u, int c); + virtual ~SrsHttpRedirectHandler(); +public: + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); +}; + +// NotFound replies to the request with an HTTP 404 not found error. +class SrsHttpNotFoundHandler : public ISrsHttpHandler +{ +public: + SrsHttpNotFoundHandler(); + virtual ~SrsHttpNotFoundHandler(); +public: + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); +}; + +// FileServer returns a handler that serves HTTP requests +// with the contents of the file system rooted at root. +// +// To use the operating system's file system implementation, +// use http.Dir: +// +// http.Handle("/", SrsHttpFileServer("/tmp")) +// http.Handle("/", SrsHttpFileServer("static-dir")) +class SrsHttpFileServer : public ISrsHttpHandler +{ +protected: + std::string dir; +public: + SrsHttpFileServer(std::string root_dir); + virtual ~SrsHttpFileServer(); +public: + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); +private: + /** + * serve the file by specified path + */ + virtual int serve_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath); + virtual int serve_flv_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath); + virtual int serve_mp4_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath); +protected: + /** + * when access flv file with x.flv?start=xxx + */ + virtual int serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath, int offset); + /** + * when access mp4 file with x.mp4?range=start-end + * @param start the start offset in bytes. + * @param end the end offset in bytes. -1 to end of file. + * @remark response data in [start, end]. + */ + virtual int serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath, int start, int end); +protected: + /** + * copy the fs to response writer in size bytes. + */ + virtual int copy(ISrsHttpResponseWriter* w, SrsFileReader* fs, ISrsHttpMessage* r, int size); +}; + +// the mux entry for server mux. +// the matcher info, for example, the pattern and handler. +class SrsHttpMuxEntry +{ +public: + bool explicit_match; + ISrsHttpHandler* handler; + std::string pattern; + bool enabled; +public: + SrsHttpMuxEntry(); + virtual ~SrsHttpMuxEntry(); +}; + +/** + * the hijacker for http pattern match. + */ +class ISrsHttpMatchHijacker +{ +public: + ISrsHttpMatchHijacker(); + virtual ~ISrsHttpMatchHijacker(); +public: + /** + * when match the request failed, no handler to process request. + * @param request the http request message to match the handler. + * @param ph the already matched handler, hijack can rewrite it. + */ + virtual int hijack(ISrsHttpMessage* request, ISrsHttpHandler** ph) = 0; +}; + +// ServeMux is an HTTP request multiplexer. +// It matches the URL of each incoming request against a list of registered +// patterns and calls the handler for the pattern that +// most closely matches the URL. +// +// Patterns name fixed, rooted paths, like "/favicon.ico", +// or rooted subtrees, like "/images/" (note the trailing slash). +// Longer patterns take precedence over shorter ones, so that +// if there are handlers registered for both "/images/" +// and "/images/thumbnails/", the latter handler will be +// called for paths beginning "/images/thumbnails/" and the +// former will receive requests for any other paths in the +// "/images/" subtree. +// +// Note that since a pattern ending in a slash names a rooted subtree, +// the pattern "/" matches all paths not matched by other registered +// patterns, not just the URL with Path == "/". +// +// Patterns may optionally begin with a host name, restricting matches to +// URLs on that host only. Host-specific patterns take precedence over +// general patterns, so that a handler might register for the two patterns +// "/codesearch" and "codesearch.google.com/" without also taking over +// requests for "http://www.google.com/". +// +// ServeMux also takes care of sanitizing the URL request path, +// redirecting any request containing . or .. elements to an +// equivalent .- and ..-free URL. +class SrsHttpServeMux +{ +private: + // the pattern handler, to handle the http request. + std::map entries; + // the vhost handler. + // when find the handler to process the request, + // append the matched vhost when pattern not starts with /, + // for example, for pattern /live/livestream.flv of vhost ossrs.net, + // the path will rewrite to ossrs.net/live/livestream.flv + std::map vhosts; + // all hijackers for http match. + // for example, the hstrs(http stream trigger rtmp source) + // can hijack and install handler when request incoming and no handler. + std::vector hijackers; +public: + SrsHttpServeMux(); + virtual ~SrsHttpServeMux(); +public: + /** + * initialize the http serve mux. + */ + virtual int initialize(); + /** + * hijack the http match. + */ + virtual void hijack(ISrsHttpMatchHijacker* h); + virtual void unhijack(ISrsHttpMatchHijacker* h); +public: + // Handle registers the handler for the given pattern. + // If a handler already exists for pattern, Handle panics. + virtual int handle(std::string pattern, ISrsHttpHandler* handler); + // interface ISrsHttpHandler +public: + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); +private: + virtual int find_handler(ISrsHttpMessage* r, ISrsHttpHandler** ph); + virtual int match(ISrsHttpMessage* r, ISrsHttpHandler** ph); + virtual bool path_match(std::string pattern, std::string path); +}; + +// for http header. +typedef std::pair SrsHttpHeaderField; + +// A Request represents an HTTP request received by a server +// or to be sent by a client. +// +// The field semantics differ slightly between client and server +// usage. In addition to the notes on the fields below, see the +// documentation for Request.Write and RoundTripper. +/** + * the http message, request or response. + */ +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 u_int8_t method() = 0; + virtual u_int16_t status_code() = 0; + /** + * method helpers. + */ + virtual std::string method_str() = 0; + virtual bool is_http_get() = 0; + virtual bool is_http_put() = 0; + virtual bool is_http_post() = 0; + virtual bool is_http_delete() = 0; + virtual bool is_http_options() = 0; +public: + /** + * whether should keep the connection alive. + */ + virtual bool is_keep_alive() = 0; + /** + * the uri contains the host and path. + */ + virtual std::string uri() = 0; + /** + * the url maybe the path. + */ + virtual std::string url() = 0; + virtual std::string host() = 0; + virtual std::string path() = 0; + virtual std::string ext() = 0; +public: + /** + * read body to string. + * @remark for small http body. + */ + virtual int body_read_all(std::string& body) = 0; + /** + * get the body reader, to read one by one. + * @remark when body is very large, or chunked, use this. + */ + virtual ISrsHttpResponseReader* body_reader() = 0; + /** + * the content length, -1 for chunked or not set. + */ + virtual int64_t content_length() = 0; +public: + /** + * get the param in query string, + * for instance, query is "start=100&end=200", + * 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; +}; + +#endif