From e3526c0cf6e2a2b78c5dd218c338262f88f90195 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 26 Mar 2017 13:40:39 +0800 Subject: [PATCH] For #820, modules use service only. --- trunk/auto/modules.sh | 5 +- trunk/configure | 40 +- .../srs_xcode.xcodeproj/project.pbxproj | 32 +- trunk/modules/readme.txt | 2 +- trunk/src/app/srs_app_caster_flv.cpp | 6 +- trunk/src/app/srs_app_caster_flv.hpp | 2 +- trunk/src/app/srs_app_conn.cpp | 8 - trunk/src/app/srs_app_conn.hpp | 21 +- trunk/src/app/srs_app_http_client.cpp | 222 ---- trunk/src/app/srs_app_http_client.hpp | 79 +- trunk/src/app/srs_app_http_conn.cpp | 1023 ---------------- trunk/src/app/srs_app_http_conn.hpp | 288 +---- trunk/src/app/srs_app_rtmp_conn.cpp | 191 +-- trunk/src/app/srs_app_rtmp_conn.hpp | 45 +- trunk/src/app/srs_app_server.cpp | 3 +- trunk/src/app/srs_app_server.hpp | 2 +- trunk/src/app/srs_app_st.cpp | 346 ------ trunk/src/app/srs_app_st.hpp | 106 -- trunk/src/app/srs_app_utility.cpp | 242 ---- trunk/src/app/srs_app_utility.hpp | 18 +- trunk/src/main/srs_main_ingest_hls.cpp | 25 +- trunk/src/main/srs_main_mp4_parser.cpp | 9 +- trunk/src/service/srs_service_conn.cpp | 41 + trunk/src/service/srs_service_conn.hpp | 55 + trunk/src/service/srs_service_http_client.cpp | 246 ++++ trunk/src/service/srs_service_http_client.hpp | 111 ++ trunk/src/service/srs_service_http_conn.cpp | 1060 +++++++++++++++++ trunk/src/service/srs_service_http_conn.hpp | 326 +++++ trunk/src/service/srs_service_rtmp_conn.cpp | 241 ++++ trunk/src/service/srs_service_rtmp_conn.hpp | 92 ++ trunk/src/service/srs_service_st.cpp | 352 ++++++ trunk/src/service/srs_service_st.hpp | 109 ++ trunk/src/service/srs_service_utility.cpp | 280 +++++ trunk/src/service/srs_service_utility.hpp | 53 + 34 files changed, 3061 insertions(+), 2620 deletions(-) create mode 100644 trunk/src/service/srs_service_conn.cpp create mode 100644 trunk/src/service/srs_service_conn.hpp create mode 100644 trunk/src/service/srs_service_http_client.cpp create mode 100644 trunk/src/service/srs_service_http_client.hpp create mode 100644 trunk/src/service/srs_service_http_conn.cpp create mode 100644 trunk/src/service/srs_service_http_conn.hpp create mode 100644 trunk/src/service/srs_service_rtmp_conn.cpp create mode 100644 trunk/src/service/srs_service_rtmp_conn.hpp create mode 100644 trunk/src/service/srs_service_utility.cpp create mode 100644 trunk/src/service/srs_service_utility.hpp diff --git a/trunk/auto/modules.sh b/trunk/auto/modules.sh index 5866f8d7d..053eb9104 100755 --- a/trunk/auto/modules.sh +++ b/trunk/auto/modules.sh @@ -41,7 +41,7 @@ echo -n "${INCS_NAME} = -I${MODULE_DIR} " >> ${FILE} for item in ${MODULE_DEPENDS[*]}; do DEP_INCS_NAME="${item}_INCS"do DEP_INCS_NAME="${item}_MODULE_INCS" - echo -n "\$(${DEP_INCS_NAME}) " >> ${FILE} + echo -n "\$(${DEP_INCS_NAME})" >> ${FILE} done # # depends library header files @@ -79,7 +79,8 @@ for item in ${MODULE_FILES[*]}; do MODULE_OBJS="${MODULE_OBJS[@]} ${CPP_FILE}" if [ -f ${CPP_FILE} ]; then echo "${OBJ_FILE}: \$(${DEPS_NAME}) ${CPP_FILE} " >> ${FILE} - echo " \$(CXX) -c \$(CXXFLAGS) ${DEFINES} \$(${INCS_NAME})\\" >> ${FILE} + echo " \$(CXX) -c \$(CXXFLAGS) ${DEFINES}\\" >> ${FILE} + echo " \$(${INCS_NAME})\\" >> ${FILE} echo " -o ${OBJ_FILE} ${CPP_FILE}" >> ${FILE} fi done diff --git a/trunk/configure b/trunk/configure index c86057d5e..72e2b4fdb 100755 --- a/trunk/configure +++ b/trunk/configure @@ -177,8 +177,10 @@ PROTOCOL_OBJS="${MODULE_OBJS[@]}" if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then MODULE_ID="SERVICE" MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL") - ModuleLibIncs=(${LibSTRoot} ${LibSSLRoot} ${SRS_OBJS_DIR}) - MODULE_FILES=("srs_service_log" "srs_service_st") + ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR}) + MODULE_FILES=("srs_service_log" "srs_service_st" "srs_service_http_client" + "srs_service_http_conn" "srs_service_rtmp_conn" "srs_service_utility" + "srs_service_conn") DEFINES="" SERVICE_INCS="src/service"; MODULE_DIR=${SERVICE_INCS} . auto/modules.sh SERVICE_OBJS="${MODULE_OBJS[@]}" @@ -188,7 +190,7 @@ fi if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then MODULE_ID="APP" MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE") - ModuleLibIncs=(${LibSTRoot} ${LibSSLRoot} ${SRS_OBJS_DIR}) + ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR}) MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_source" "srs_app_refer" "srs_app_hls" "srs_app_forward" "srs_app_encoder" "srs_app_http_stream" "srs_app_thread" "srs_app_bandwidth" "srs_app_st" "srs_app_log" "srs_app_config" @@ -218,12 +220,22 @@ MODULE_FILES=("srs_librtmp" "srs_lib_simple_socket" "srs_lib_bandwidth") LIBS_INCS="src/libs"; MODULE_DIR=${LIBS_INCS} . auto/modules.sh LIBS_OBJS="${MODULE_OBJS[@]}" # -#Main Module +#Main Module, for SRS. if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then MODULE_ID="MAIN" MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE" "APP") - ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) + ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot}) MODULE_FILES=("srs_main_server") + MAIN_INCS="src/main"; MODULE_DIR=${MAIN_INCS} . auto/modules.sh + MAIN_OBJS="${MODULE_OBJS[@]}" +fi +# +#Main Module, for app from modules. +if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then + MODULE_ID="MAIN2" + MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE") + ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot}) + MODULE_FILES=() DEFINES="" # add each modules for main for SRS_MODULE in ${SRS_MODULES[*]}; do @@ -231,24 +243,23 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then MODULE_FILES+=("${SRS_MODULE_MAIN[*]}") DEFINES="${DEFINES} ${SRS_MODULE_DEFINES}" done - MAIN_INCS="src/main"; MODULE_DIR=${MAIN_INCS} . auto/modules.sh - MAIN_OBJS="${MODULE_OBJS[@]}" + MAIN2_INCS="src/main"; MODULE_DIR=${MAIN2_INCS} . auto/modules.sh + MAIN2_OBJS="${MODULE_OBJS[@]}" fi ##################################################################################### # Binaries, main entrances, link the module and its depends modules, # then link to a binary, for example, objs/srs # -# disable all app when export librtmp +# Disable SRS application for exporting librtmp. if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then # all main entrances MAIN_ENTRANCES=("srs_main_server") - # add each modules for main for SRS_MODULE in ${SRS_MODULES[*]}; do . $SRS_MODULE/config MAIN_ENTRANCES+=("${SRS_MODULE_MAIN[*]}") done - # + # # all depends libraries ModuleLibFiles=(${LibSTfile} ${LibSSLfile} ${LibGperfFile}) # all depends objects @@ -257,12 +268,15 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then # # srs: srs(simple rtmp server) over st(state-threads) BUILD_KEY="srs" APP_MAIN="srs_main_server" APP_NAME="srs" . auto/apps.sh - # add each modules for application + # + # For modules, without the app module. + MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${SERVICE_OBJS[@]} ${MAIN2_OBJS[@]}" + # for SRS_MODULE in ${SRS_MODULES[*]}; do . $SRS_MODULE/config - # no SRS_MODULE_MAIN + # no SRS_MODULE_MAIN if [[ 0 -eq ${#SRS_MODULE_MAIN[@]} ]]; then continue; fi - BUILD_KEY="$SRS_MODULE_NAME" APP_MAIN="$SRS_MODULE_MAIN" APP_NAME="$SRS_MODULE_NAME" . auto/apps.sh + BUILD_KEY="$SRS_MODULE_NAME" APP_MAIN="${SRS_MODULE_MAIN[0]}" APP_NAME="$SRS_MODULE_NAME" . auto/apps.sh done fi # srs librtmp diff --git a/trunk/ide/srs_xcode/srs_xcode.xcodeproj/project.pbxproj b/trunk/ide/srs_xcode/srs_xcode.xcodeproj/project.pbxproj index 88629e0d9..4ac58a62a 100644 --- a/trunk/ide/srs_xcode/srs_xcode.xcodeproj/project.pbxproj +++ b/trunk/ide/srs_xcode/srs_xcode.xcodeproj/project.pbxproj @@ -127,6 +127,11 @@ 3CE893B51E87508D000B742D /* srs_app_dvr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3CE893B31E87508D000B742D /* srs_app_dvr.cpp */; }; 3CE893B91E8750A9000B742D /* srs_service_log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3CE893B71E8750A9000B742D /* srs_service_log.cpp */; }; 3CE893BC1E875108000B742D /* srs_service_st.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3CE893BA1E875108000B742D /* srs_service_st.cpp */; }; + 3CE893BF1E876A97000B742D /* srs_service_http_client.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3CE893BD1E876A97000B742D /* srs_service_http_client.cpp */; }; + 3CE893C21E876B9E000B742D /* srs_service_http_conn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3CE893C01E876B9E000B742D /* srs_service_http_conn.cpp */; }; + 3CE893C51E876C39000B742D /* srs_service_rtmp_conn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3CE893C31E876C39000B742D /* srs_service_rtmp_conn.cpp */; }; + 3CE893C81E876D04000B742D /* srs_service_utility.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3CE893C61E876D04000B742D /* srs_service_utility.cpp */; }; + 3CE893CB1E8770E2000B742D /* srs_service_conn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3CE893C91E8770E2000B742D /* srs_service_conn.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -431,6 +436,16 @@ 3CE893B81E8750A9000B742D /* srs_service_log.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_service_log.hpp; path = ../../../src/service/srs_service_log.hpp; sourceTree = ""; }; 3CE893BA1E875108000B742D /* srs_service_st.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_service_st.cpp; path = ../../../src/service/srs_service_st.cpp; sourceTree = ""; }; 3CE893BB1E875108000B742D /* srs_service_st.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_service_st.hpp; path = ../../../src/service/srs_service_st.hpp; sourceTree = ""; }; + 3CE893BD1E876A97000B742D /* srs_service_http_client.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_service_http_client.cpp; path = ../../../src/service/srs_service_http_client.cpp; sourceTree = ""; }; + 3CE893BE1E876A97000B742D /* srs_service_http_client.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_service_http_client.hpp; path = ../../../src/service/srs_service_http_client.hpp; sourceTree = ""; }; + 3CE893C01E876B9E000B742D /* srs_service_http_conn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_service_http_conn.cpp; path = ../../../src/service/srs_service_http_conn.cpp; sourceTree = ""; }; + 3CE893C11E876B9E000B742D /* srs_service_http_conn.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_service_http_conn.hpp; path = ../../../src/service/srs_service_http_conn.hpp; sourceTree = ""; }; + 3CE893C31E876C39000B742D /* srs_service_rtmp_conn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_service_rtmp_conn.cpp; path = ../../../src/service/srs_service_rtmp_conn.cpp; sourceTree = ""; }; + 3CE893C41E876C39000B742D /* srs_service_rtmp_conn.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_service_rtmp_conn.hpp; path = ../../../src/service/srs_service_rtmp_conn.hpp; sourceTree = ""; }; + 3CE893C61E876D04000B742D /* srs_service_utility.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_service_utility.cpp; path = ../../../src/service/srs_service_utility.cpp; sourceTree = ""; }; + 3CE893C71E876D04000B742D /* srs_service_utility.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_service_utility.hpp; path = ../../../src/service/srs_service_utility.hpp; sourceTree = ""; }; + 3CE893C91E8770E2000B742D /* srs_service_conn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_service_conn.cpp; path = ../../../src/service/srs_service_conn.cpp; sourceTree = ""; }; + 3CE893CA1E8770E2000B742D /* srs_service_conn.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_service_conn.hpp; path = ../../../src/service/srs_service_conn.hpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -475,9 +490,9 @@ 3C1231EF1AAE651100CE8F6C /* core */, 3C1232071AAE814200CE8F6C /* kernel */, 3C12322C1AAE819900CE8F6C /* protocol */, + 3CE893B61E875095000B742D /* service */, 3C12324B1AAE81CE00CE8F6C /* app */, 3C96ADC41B00A71000885304 /* modules */, - 3CE893B61E875095000B742D /* service */, 3C1232041AAE80CB00CE8F6C /* main */, 3C36DB541ABD1CA70066CCAF /* libs */, 3C1231F91AAE670E00CE8F6C /* objs */, @@ -919,10 +934,20 @@ 3CE893B61E875095000B742D /* service */ = { isa = PBXGroup; children = ( + 3CE893C91E8770E2000B742D /* srs_service_conn.cpp */, + 3CE893CA1E8770E2000B742D /* srs_service_conn.hpp */, + 3CE893BD1E876A97000B742D /* srs_service_http_client.cpp */, + 3CE893BE1E876A97000B742D /* srs_service_http_client.hpp */, + 3CE893C01E876B9E000B742D /* srs_service_http_conn.cpp */, + 3CE893C11E876B9E000B742D /* srs_service_http_conn.hpp */, 3CE893B71E8750A9000B742D /* srs_service_log.cpp */, 3CE893B81E8750A9000B742D /* srs_service_log.hpp */, + 3CE893C31E876C39000B742D /* srs_service_rtmp_conn.cpp */, + 3CE893C41E876C39000B742D /* srs_service_rtmp_conn.hpp */, 3CE893BA1E875108000B742D /* srs_service_st.cpp */, 3CE893BB1E875108000B742D /* srs_service_st.hpp */, + 3CE893C61E876D04000B742D /* srs_service_utility.cpp */, + 3CE893C71E876D04000B742D /* srs_service_utility.hpp */, ); name = service; sourceTree = ""; @@ -1008,6 +1033,7 @@ 3C12322B1AAE814D00CE8F6C /* srs_kernel_utility.cpp in Sources */, 3CA5F1411E65543700E442C7 /* event.c in Sources */, 3C12324A1AAE81A400CE8F6C /* srs_rtsp_stack.cpp in Sources */, + 3CE893BF1E876A97000B742D /* srs_service_http_client.cpp in Sources */, 3C36DB5D1ABD1CB90066CCAF /* srs_librtmp.cpp in Sources */, 3CA5F1421E65543700E442C7 /* io.c in Sources */, 3C12329F1AAE81D900CE8F6C /* srs_app_http_api.cpp in Sources */, @@ -1027,6 +1053,7 @@ 3C1232221AAE814D00CE8F6C /* srs_kernel_codec.cpp in Sources */, 3C1232B71AAE81D900CE8F6C /* srs_app_utility.cpp in Sources */, 3C1232AB1AAE81D900CE8F6C /* srs_app_recv_thread.cpp in Sources */, + 3CE893C51E876C39000B742D /* srs_service_rtmp_conn.cpp in Sources */, 3CC52DDC1ACE4023006FEB01 /* srs_utest_protocol.cpp in Sources */, 3C663F151AB0155100286D8B /* srs_h264_raw_publish.c in Sources */, 3C1231F61AAE652D00CE8F6C /* srs_core_autofree.cpp in Sources */, @@ -1048,9 +1075,11 @@ 3C12329C1AAE81D900CE8F6C /* srs_app_forward.cpp in Sources */, 3C1232251AAE814D00CE8F6C /* srs_kernel_file.cpp in Sources */, 3C1232AD1AAE81D900CE8F6C /* srs_app_reload.cpp in Sources */, + 3CE893C81E876D04000B742D /* srs_service_utility.cpp in Sources */, 3C1231F81AAE652D00CE8F6C /* srs_core.cpp in Sources */, 3C1232A21AAE81D900CE8F6C /* srs_app_http_hooks.cpp in Sources */, 3C663F121AB0155100286D8B /* srs_detect_rtmp.c in Sources */, + 3CE893CB1E8770E2000B742D /* srs_service_conn.cpp in Sources */, 3C1232B11AAE81D900CE8F6C /* srs_app_server.cpp in Sources */, 3C1232061AAE812C00CE8F6C /* srs_main_server.cpp in Sources */, 3C1232281AAE814D00CE8F6C /* srs_kernel_mp3.cpp in Sources */, @@ -1076,6 +1105,7 @@ 3C28EDDF1AF5C43F00A3AEAC /* srs_app_caster_flv.cpp in Sources */, 3C1232241AAE814D00CE8F6C /* srs_kernel_error.cpp in Sources */, 3C036B561B2D0AC10078E2E0 /* srs_app_http_stream.cpp in Sources */, + 3CE893C21E876B9E000B742D /* srs_service_http_conn.cpp in Sources */, 3C068D6D1B10175500AA722C /* srs_protocol_stream.cpp in Sources */, 3CB25C2A1BB269FD00C97A63 /* jmp_sp.cpp in Sources */, 3C068D6D1B10175500AA722C /* srs_protocol_stream.cpp in Sources */, diff --git a/trunk/modules/readme.txt b/trunk/modules/readme.txt index a6190965d..32d7891f0 100644 --- a/trunk/modules/readme.txt +++ b/trunk/modules/readme.txt @@ -1,4 +1,4 @@ -SRS Module Ruler(SRS模块规则) +SRS Module Rules(SRS模块规则) 1. Each module in its seperate home directory(一个模块一个目录). 2. There is a config file in home(目录下放一个config文件). 3. All variables in configure are available(所有的configure中的变量模块中可以使用). diff --git a/trunk/src/app/srs_app_caster_flv.cpp b/trunk/src/app/srs_app_caster_flv.cpp index 498024b9e..edd57a939 100644 --- a/trunk/src/app/srs_app_caster_flv.cpp +++ b/trunk/src/app/srs_app_caster_flv.cpp @@ -83,10 +83,12 @@ int SrsAppCasterFlv::on_tcp_client(st_netfd_t stfd) return ret; } -void SrsAppCasterFlv::remove(SrsConnection* c) +void SrsAppCasterFlv::remove(ISrsConnection* c) { + SrsConnection* conn = dynamic_cast(c); + std::vector::iterator it; - if ((it = std::find(conns.begin(), conns.end(), c)) != conns.end()) { + if ((it = std::find(conns.begin(), conns.end(), conn)) != conns.end()) { conns.erase(it); } } diff --git a/trunk/src/app/srs_app_caster_flv.hpp b/trunk/src/app/srs_app_caster_flv.hpp index 603db1a34..dd318fb2f 100644 --- a/trunk/src/app/srs_app_caster_flv.hpp +++ b/trunk/src/app/srs_app_caster_flv.hpp @@ -68,7 +68,7 @@ public: virtual int on_tcp_client(st_netfd_t stfd); // IConnectionManager public: - virtual void remove(SrsConnection* c); + virtual void remove(ISrsConnection* c); // ISrsHttpHandler public: virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); diff --git a/trunk/src/app/srs_app_conn.cpp b/trunk/src/app/srs_app_conn.cpp index ccbd8a6e8..cdec07f22 100644 --- a/trunk/src/app/srs_app_conn.cpp +++ b/trunk/src/app/srs_app_conn.cpp @@ -30,14 +30,6 @@ using namespace std; #include #include -IConnectionManager::IConnectionManager() -{ -} - -IConnectionManager::~IConnectionManager() -{ -} - SrsConnection::SrsConnection(IConnectionManager* cm, st_netfd_t c, string cip) { id = 0; diff --git a/trunk/src/app/srs_app_conn.hpp b/trunk/src/app/srs_app_conn.hpp index 7632e13dc..8b06bdeb8 100644 --- a/trunk/src/app/srs_app_conn.hpp +++ b/trunk/src/app/srs_app_conn.hpp @@ -32,30 +32,15 @@ #include #include #include - -class SrsConnection; - -/** - * the manager for connection. - */ -class IConnectionManager -{ -public: - IConnectionManager(); - virtual ~IConnectionManager(); -public: - /** - * remove the specified connection. - */ - virtual void remove(SrsConnection* c) = 0; -}; +#include /** * the basic connection of SRS, * 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 : virtual public ISrsOneCycleThreadHandler, virtual public IKbpsDelta, virtual public ISrsReloadHandler +class SrsConnection : virtual public ISrsConnection, virtual public ISrsOneCycleThreadHandler + , virtual public IKbpsDelta, virtual public ISrsReloadHandler { private: /** diff --git a/trunk/src/app/srs_app_http_client.cpp b/trunk/src/app/srs_app_http_client.cpp index 761982cba..7c4785688 100644 --- a/trunk/src/app/srs_app_http_client.cpp +++ b/trunk/src/app/srs_app_http_client.cpp @@ -23,225 +23,3 @@ #include -#include - -using namespace std; - -#include -#include -#include -#include -#include -#include -#include -#include - -SrsHttpClient::SrsHttpClient() -{ - transport = NULL; - kbps = new SrsKbps(); - parser = NULL; - timeout = SRS_CONSTS_NO_TMMS; - port = 0; -} - -SrsHttpClient::~SrsHttpClient() -{ - disconnect(); - - srs_freep(kbps); - srs_freep(parser); -} - -// TODO: FIXME: use ms for timeout. -int SrsHttpClient::initialize(string h, int p, int64_t tm) -{ - int ret = ERROR_SUCCESS; - - srs_freep(parser); - parser = new SrsHttpParser(); - - if ((ret = parser->initialize(HTTP_RESPONSE, false)) != ERROR_SUCCESS) { - srs_error("initialize parser failed. ret=%d", ret); - return ret; - } - - // Always disconnect the transport. - host = h; - port = p; - timeout = tm; - disconnect(); - - // ep used for host in header. - string ep = host; - if (port > 0 && port != SRS_CONSTS_HTTP_DEFAULT_PORT) { - ep += ":" + srs_int2str(port); - } - - // Set default value for headers. - headers["Host"] = ep; - headers["Connection"] = "Keep-Alive"; - headers["User-Agent"] = RTMP_SIG_SRS_SERVER; - headers["Content-Type"] = "application/json"; - - return ret; -} - -SrsHttpClient* SrsHttpClient::set_header(string k, string v) -{ - headers[k] = v; - - return this; -} - -int SrsHttpClient::post(string path, string req, ISrsHttpMessage** ppmsg) -{ - *ppmsg = NULL; - - int ret = ERROR_SUCCESS; - - // always set the content length. - headers["Content-Length"] = srs_int2str(req.length()); - - if ((ret = connect()) != ERROR_SUCCESS) { - srs_warn("http connect server failed. ret=%d", ret); - return ret; - } - - // send POST request to uri - // POST %s HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s - std::stringstream ss; - ss << "POST " << path << " " << "HTTP/1.1" << SRS_HTTP_CRLF; - for (map::iterator it = headers.begin(); it != headers.end(); ++it) { - string key = it->first; - string value = it->second; - ss << key << ": " << value << SRS_HTTP_CRLF; - } - ss << SRS_HTTP_CRLF << req; - - std::string data = ss.str(); - if ((ret = transport->write((void*)data.c_str(), data.length(), NULL)) != ERROR_SUCCESS) { - // Disconnect the transport when channel error, reconnect for next operation. - disconnect(); - srs_error("write http post failed. ret=%d", ret); - return ret; - } - - ISrsHttpMessage* msg = NULL; - if ((ret = parser->parse_message(transport, NULL, &msg)) != ERROR_SUCCESS) { - srs_error("parse http post response failed. ret=%d", ret); - return ret; - } - srs_assert(msg); - - if (ppmsg) { - *ppmsg = msg; - } else { - srs_freep(msg); - } - srs_info("parse http post response success."); - - return ret; -} - -int SrsHttpClient::get(string path, string req, ISrsHttpMessage** ppmsg) -{ - *ppmsg = NULL; - - int ret = ERROR_SUCCESS; - - // always set the content length. - headers["Content-Length"] = srs_int2str(req.length()); - - if ((ret = connect()) != ERROR_SUCCESS) { - srs_warn("http connect server failed. ret=%d", ret); - return ret; - } - - // send POST request to uri - // GET %s HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s - std::stringstream ss; - ss << "GET " << path << " " << "HTTP/1.1" << SRS_HTTP_CRLF; - for (map::iterator it = headers.begin(); it != headers.end(); ++it) { - string key = it->first; - string value = it->second; - ss << key << ": " << value << SRS_HTTP_CRLF; - } - ss << SRS_HTTP_CRLF << req; - - std::string data = ss.str(); - if ((ret = transport->write((void*)data.c_str(), data.length(), NULL)) != ERROR_SUCCESS) { - // Disconnect the transport when channel error, reconnect for next operation. - disconnect(); - srs_error("write http get failed. ret=%d", ret); - return ret; - } - - ISrsHttpMessage* msg = NULL; - if ((ret = parser->parse_message(transport, NULL, &msg)) != ERROR_SUCCESS) { - srs_error("parse http post response failed. ret=%d", ret); - return ret; - } - srs_assert(msg); - - if (ppmsg) { - *ppmsg = msg; - } else { - srs_freep(msg); - } - srs_info("parse http get response success."); - - return ret; -} - -void SrsHttpClient::set_recv_timeout(int64_t tm) -{ - transport->set_recv_timeout(tm); -} - -void SrsHttpClient::kbps_sample(const char* label, int64_t age) -{ - kbps->sample(); - - int sr = kbps->get_send_kbps(); - int sr30s = kbps->get_send_kbps_30s(); - int sr5m = kbps->get_send_kbps_5m(); - int rr = kbps->get_recv_kbps(); - int rr30s = kbps->get_recv_kbps_30s(); - int rr5m = kbps->get_recv_kbps_5m(); - - srs_trace("<- %s time=%"PRId64", okbps=%d,%d,%d, ikbps=%d,%d,%d", label, age, sr, sr30s, sr5m, rr, rr30s, rr5m); -} - -void SrsHttpClient::disconnect() -{ - kbps->set_io(NULL, NULL); - srs_freep(transport); -} - -int SrsHttpClient::connect() -{ - int ret = ERROR_SUCCESS; - - // When transport connected, ignore. - if (transport) { - return ret; - } - - transport = new SrsTcpClient(host, port, timeout); - if ((ret = transport->connect()) != ERROR_SUCCESS) { - disconnect(); - srs_warn("http client failed, server=%s, port=%d, timeout=%"PRId64", ret=%d", host.c_str(), port, timeout, ret); - return ret; - } - srs_info("connect to server success. server=%s, port=%d", host.c_str(), port); - - // Set the recv/send timeout in ms. - transport->set_recv_timeout(timeout); - transport->set_send_timeout(timeout); - - kbps->set_io(transport, transport); - - return ret; -} - diff --git a/trunk/src/app/srs_app_http_client.hpp b/trunk/src/app/srs_app_http_client.hpp index 2f83e563f..8ac42fc64 100644 --- a/trunk/src/app/srs_app_http_client.hpp +++ b/trunk/src/app/srs_app_http_client.hpp @@ -26,84 +26,7 @@ #include -#include -#include - -#include - -class SrsHttpUri; -class SrsHttpParser; -class ISrsHttpMessage; -class SrsStSocket; -class SrsKbps; - -// the default timeout for http client. -#define SRS_HTTP_CLIENT_TMMS (30*1000) - -/** - * The client to GET/POST/PUT/DELETE over HTTP. - * @remark We will reuse the TCP transport until initialize or channel error, - * such as send/recv failed. - * Usage: - * SrsHttpClient hc; - * hc.initialize("127.0.0.1", 80, 9000); - * hc.post("/api/v1/version", "Hello world!", NULL); - */ -class SrsHttpClient -{ -private: - // The underlayer TCP transport, set to NULL when disconnect, or never not NULL when connected. - // We will disconnect transport when initialize or channel error, such as send/recv error. - SrsTcpClient* transport; - SrsHttpParser* parser; - std::map headers; - SrsKbps* kbps; -private: - // The timeout in ms. - int64_t timeout; - // The host name or ip. - std::string host; - int port; -public: - SrsHttpClient(); - virtual ~SrsHttpClient(); -public: - /** - * Initliaze the client, disconnect the transport, renew the HTTP parser. - * @param tm The underlayer TCP transport timeout in ms. - * @remark we will set default values in headers, which can be override by set_header. - */ - virtual int initialize(std::string h, int p, int64_t tm = SRS_HTTP_CLIENT_TMMS); - /** - * Set HTTP request header in header[k]=v. - * @return the HTTP client itself. - */ - virtual SrsHttpClient* set_header(std::string k, std::string v); -public: - /** - * to post data to 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. - * @remark user must free the ppmsg if not NULL. - */ - 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. - * @remark user must free the ppmsg if not NULL. - */ - virtual int get(std::string path, std::string req, ISrsHttpMessage** ppmsg); -private: - virtual void set_recv_timeout(int64_t tm); -public: - virtual void kbps_sample(const char* label, int64_t age); -private: - virtual void disconnect(); - virtual int connect(); -}; +#include #endif diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp index bc5b18b3f..a49b4c6c7 100644 --- a/trunk/src/app/srs_app_http_conn.cpp +++ b/trunk/src/app/srs_app_http_conn.cpp @@ -58,1029 +58,6 @@ using namespace std; #include #include -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; - nb_iovss_cache = 0; - iovss_cache = NULL; -} - -SrsHttpResponseWriter::~SrsHttpResponseWriter() -{ - srs_freep(hdr); - srs_freepa(iovss_cache); -} - -int SrsHttpResponseWriter::final_request() -{ - // write the header data in memory. - if (!header_wrote) { - write_header(SRS_CONSTS_HTTP_OK); - } - - // 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; - - // write the header data in memory. - if (!header_wrote) { - write_header(SRS_CONSTS_HTTP_OK); - } - - // whatever header is wrote, we should try to send header. - if ((ret = send_header(data, size)) != ERROR_SUCCESS) { - srs_error("http: send header failed. ret=%d", ret); - return ret; - } - - // check the bytes send and content length. - 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; - } - - // 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. - int nb_size = snprintf(header_cache, SRS_HTTP_HEADER_CACHE_SIZE, "%x", size); - - iovec iovs[4]; - iovs[0].iov_base = (char*)header_cache; - iovs[0].iov_len = (int)nb_size; - iovs[1].iov_base = (char*)SRS_HTTP_CRLF; - iovs[1].iov_len = 2; - iovs[2].iov_base = (char*)data; - iovs[2].iov_len = size; - iovs[3].iov_base = (char*)SRS_HTTP_CRLF; - iovs[3].iov_len = 2; - - ssize_t nwrite; - if ((ret = skt->writev(iovs, 4, &nwrite)) != ERROR_SUCCESS) { - return ret; - } - - return ret; -} - -int SrsHttpResponseWriter::writev(const iovec* iov, int iovcnt, ssize_t* pnwrite) -{ - int ret = ERROR_SUCCESS; - - // when header not ready, or not chunked, send one by one. - if (!header_wrote || content_length != -1) { - ssize_t nwrite = 0; - for (int i = 0; i < iovcnt; i++) { - const iovec* piovc = iov + i; - nwrite += piovc->iov_len; - if ((ret = write((char*)piovc->iov_base, (int)piovc->iov_len)) != ERROR_SUCCESS) { - return ret; - } - } - - if (pnwrite) { - *pnwrite = nwrite; - } - - return ret; - } - - // ignore NULL content. - if (iovcnt <= 0) { - return ret; - } - - // send in chunked encoding. - int nb_iovss = 3 + iovcnt; - iovec* iovss = iovss_cache; - if (nb_iovss_cache < nb_iovss) { - srs_freepa(iovss_cache); - nb_iovss_cache = nb_iovss; - iovss = iovss_cache = new iovec[nb_iovss]; - } - - // send in chunked encoding. - - // chunk size. - int size = 0; - for (int i = 0; i < iovcnt; i++) { - const iovec* data_iov = iov + i; - size += data_iov->iov_len; - } - written += size; - - // chunk header - int nb_size = snprintf(header_cache, SRS_HTTP_HEADER_CACHE_SIZE, "%x", size); - iovec* iovs = iovss; - iovs[0].iov_base = (char*)header_cache; - iovs[0].iov_len = (int)nb_size; - iovs++; - - // chunk header eof. - iovs[0].iov_base = (char*)SRS_HTTP_CRLF; - iovs[0].iov_len = 2; - iovs++; - - // chunk body. - for (int i = 0; i < iovcnt; i++) { - const iovec* data_iov = iov + i; - iovs[0].iov_base = (char*)data_iov->iov_base; - iovs[0].iov_len = (int)data_iov->iov_len; - iovs++; - } - - // chunk body eof. - iovs[0].iov_base = (char*)SRS_HTTP_CRLF; - iovs[0].iov_len = 2; - iovs++; - - // sendout all ioves. - ssize_t nwrite; - if ((ret = srs_write_large_iovs(skt, iovss, nb_iovss, &nwrite)) != ERROR_SUCCESS) { - return ret; - } - - if (pnwrite) { - *pnwrite = nwrite; - } - - 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_SERVER); - } - - // 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, ISrsProtocolReaderWriter* io) -{ - skt = io; - owner = msg; - is_eof = false; - nb_total_read = 0; - nb_left_chunk = 0; - buffer = NULL; -} - -SrsHttpResponseReader::~SrsHttpResponseReader() -{ -} - -int SrsHttpResponseReader::initialize(SrsFastStream* 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 - if (owner->content_length() != -1) { - 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); - } - - // infinite chunked mode, directly read. - if (owner->is_infinite_chunked()) { - srs_assert(!owner->is_chunked() && owner->content_length() == -1); - return read_specified(data, nb_data, nb_read); - } - - // infinite chunked mode, but user not set it, - // we think there is no data left. - is_eof = true; - - return ret; -} - -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 and specified content length. - if (!owner->is_chunked() && owner->content_length() != -1) { - // when read completed, eof. - if (nb_total_read >= (int)owner->content_length()) { - is_eof = true; - } - } - - return ret; -} - -SrsHttpMessage::SrsHttpMessage(ISrsProtocolReaderWriter* io, SrsConnection* c) : ISrsHttpMessage() -{ - conn = c; - chunked = false; - infinite_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]; - jsonp = false; -} - -SrsHttpMessage::~SrsHttpMessage() -{ - srs_freep(_body); - srs_freep(_uri); - srs_freepa(_http_ts_send_buffer); -} - -int SrsHttpMessage::update(string url, bool allow_jsonp, http_parser* header, SrsFastStream* 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"); - - // use server public ip when no host specified. - // to make telnet happy. - if (host.empty()) { - host= srs_get_public_internet_address(); - } - - // parse uri to schema/server:port/path?query - std::string uri = "http://" + host + _url; - if ((ret = _uri->initialize(uri)) != ERROR_SUCCESS) { - return ret; - } - - // parse ext. - _ext = srs_path_filext(_uri->get_path()); - - // parse query string. - srs_parse_query_string(_uri->get_query(), _query); - - // parse jsonp request message. - if (allow_jsonp) { - if (!query_get("callback").empty()) { - jsonp = true; - } - if (jsonp) { - jsonp_method = query_get("method"); - } - } - - return ret; -} - -SrsConnection* SrsHttpMessage::connection() -{ - return conn; -} - -uint8_t SrsHttpMessage::method() -{ - if (jsonp && !jsonp_method.empty()) { - if (jsonp_method == "GET") { - return SRS_CONSTS_HTTP_GET; - } else if (jsonp_method == "PUT") { - return SRS_CONSTS_HTTP_PUT; - } else if (jsonp_method == "POST") { - return SRS_CONSTS_HTTP_POST; - } else if (jsonp_method == "DELETE") { - return SRS_CONSTS_HTTP_DELETE; - } - } - - return (uint8_t)_header.method; -} - -uint16_t SrsHttpMessage::status_code() -{ - return (uint16_t)_header.status_code; -} - -string SrsHttpMessage::method_str() -{ - if (jsonp && !jsonp_method.empty()) { - return jsonp_method; - } - - 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 method() == SRS_CONSTS_HTTP_GET; -} - -bool SrsHttpMessage::is_http_put() -{ - return method() == SRS_CONSTS_HTTP_PUT; -} - -bool SrsHttpMessage::is_http_post() -{ - return method() == SRS_CONSTS_HTTP_POST; -} - -bool SrsHttpMessage::is_http_delete() -{ - return 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; -} - -bool SrsHttpMessage::is_infinite_chunked() -{ - return infinite_chunked; -} - -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::query() -{ - return _uri->get_query(); -} - -string SrsHttpMessage::ext() -{ - return _ext; -} - -int SrsHttpMessage::parse_rest_id(string pattern) -{ - string p = _uri->get_path(); - if (p.length() <= pattern.length()) { - return -1; - } - - string id = p.substr((int)pattern.length()); - if (!id.empty()) { - return ::atoi(id.c_str()); - } - - return -1; -} - -int SrsHttpMessage::enter_infinite_chunked() -{ - int ret = ERROR_SUCCESS; - - if (infinite_chunked) { - return ret; - } - - if (is_chunked() || content_length() != -1) { - ret = ERROR_HTTP_DATA_INVALID; - srs_error("infinite chunkted not supported in specified codec. ret=%d", ret); - return ret; - } - - infinite_chunked = true; - - return ret; -} - -int SrsHttpMessage::body_read_all(string& body) -{ - int ret = ERROR_SUCCESS; - - // cache to read. - char* buf = new char[SRS_HTTP_READ_CACHE_BYTES]; - SrsAutoFreeA(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(); - - // http path, for instance, /live/livestream.flv, parse to - // app: /live - // stream: livestream.flv - srs_parse_rtmp_url(_uri->get_path(), req->app, req->stream); - - // trim the start slash, for instance, /live to live - req->app = srs_string_trim_start(req->app, "/"); - - // remove the extension, for instance, livestream.flv to livestream - req->stream = srs_path_filename(req->stream); - - // generate others. - 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(); - - // reset the host to http request host. - if (req->host == SRS_CONSTS_RTMP_DEFAULT_VHOST) { - req->host = _uri->get_host(); - } - - return req; -} - -bool SrsHttpMessage::is_jsonp() -{ - return jsonp; -} - -SrsHttpParser::SrsHttpParser() -{ - buffer = new SrsFastStream(); -} - -SrsHttpParser::~SrsHttpParser() -{ - srs_freep(buffer); -} - -int SrsHttpParser::initialize(enum http_parser_type type, bool allow_jsonp) -{ - int ret = ERROR_SUCCESS; - - jsonp = allow_jsonp; - - 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(ISrsProtocolReaderWriter* io, 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(io)) != 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(io, conn); - - // initalize http msg, parse url. - if ((ret = msg->update(url, jsonp, &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(ISrsProtocolReaderWriter* io) -{ - int ret = ERROR_SUCCESS; - - while (true) { - ssize_t nparsed = 0; - - // when got entire http header, parse it. - // @see https://github.com/ossrs/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(io, 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; -} - SrsHttpConn::SrsHttpConn(IConnectionManager* cm, st_netfd_t fd, ISrsHttpServeMux* m, string cip) : SrsConnection(cm, fd, cip) { diff --git a/trunk/src/app/srs_app_http_conn.hpp b/trunk/src/app/srs_app_http_conn.hpp index 01248fc94..525265ddc 100644 --- a/trunk/src/app/srs_app_http_conn.hpp +++ b/trunk/src/app/srs_app_http_conn.hpp @@ -30,8 +30,7 @@ #include #include -#include -#include +#include #include #include #include @@ -56,291 +55,6 @@ class SrsHttpMessage; class SrsHttpStreamServer; class SrsHttpStaticServer; -// the http chunked header size, -// for writev, there always one chunk to send it. -#define SRS_HTTP_HEADER_CACHE_SIZE 64 - -/** - * response writer use st socket - */ -class SrsHttpResponseWriter : public ISrsHttpResponseWriter -{ -private: - SrsStSocket* skt; - SrsHttpHeader* hdr; -private: - char header_cache[SRS_HTTP_HEADER_CACHE_SIZE]; - iovec* iovss_cache; - int nb_iovss_cache; -private: - // reply header has been (logically) written - bool header_wrote; - // 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 int writev(const iovec* iov, int iovcnt, ssize_t* pnwrite); - 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: - ISrsProtocolReaderWriter* skt; - SrsHttpMessage* owner; - SrsFastStream* 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, ISrsProtocolReaderWriter* io); - virtual ~SrsHttpResponseReader(); -public: - /** - * initialize the response reader with buffer. - */ - virtual int initialize(SrsFastStream* 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. -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 body is infinite chunked. - */ - bool infinite_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; - // whether request is jsonp. - bool jsonp; - // the method in QueryString will override the HTTP method. - std::string jsonp_method; -public: - SrsHttpMessage(ISrsProtocolReaderWriter* io, SrsConnection* c); - virtual ~SrsHttpMessage(); -public: - /** - * set the original messages, then update the message. - */ - virtual int update(std::string url, bool allow_jsonp, http_parser* header, SrsFastStream* body, std::vector& headers); -public: - virtual SrsConnection* connection(); -public: - virtual uint8_t method(); - virtual uint16_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 body is infinite chunked encoding. - * @remark set by enter_infinite_chunked. - */ - virtual bool is_infinite_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 query(); - virtual std::string ext(); - /** - * get the RESTful matched id. - */ - virtual int parse_rest_id(std::string pattern); -public: - virtual int enter_infinite_chunked(); -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); -public: - virtual bool is_jsonp(); -}; - -/** - * wrapper for http-parser, - * provides HTTP message originted service. - */ -class SrsHttpParser -{ -private: - http_parser_settings settings; - http_parser parser; - // the global parse buffer. - SrsFastStream* buffer; - // whether allow jsonp parse. - bool jsonp; -private: - // http parse data, reset before parse message. - bool expect_field_name; - std::string field_name; - std::string field_value; - SrsHttpParseState state; - http_parser header; - 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. - * @param allow_jsonp whether allow jsonp parser, which indicates the method in query string. - */ - virtual int initialize(enum http_parser_type type, bool allow_jsonp); - /** - * 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(). - * @remark user must free the ppmsg if not NULL. - */ - virtual int parse_message(ISrsProtocolReaderWriter* io, SrsConnection* conn, ISrsHttpMessage** ppmsg); -private: - /** - * parse the HTTP message to member field: msg. - */ - virtual int parse_message_imp(ISrsProtocolReaderWriter* io); -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); -}; - /** * The http connection which request the static or stream content. */ diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 2b0a1044d..9e78b7932 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -77,210 +77,23 @@ using namespace std; // when edge timeout, retry next. #define SRS_EDGE_TOKEN_TRAVERSE_TMMS (3000) -SrsSimpleRtmpClient::SrsSimpleRtmpClient(string u, int64_t ctm, int64_t stm) +SrsSimpleRtmpClient::SrsSimpleRtmpClient(string u, int64_t ctm, int64_t stm) : SrsBasicRtmpClient(u, ctm, stm) { - kbps = new SrsKbps(); - - url = u; - connect_timeout = ctm; - stream_timeout = stm; - - req = new SrsRequest(); - srs_parse_rtmp_url(url, req->tcUrl, req->stream); - srs_discovery_tc_url(req->tcUrl, req->schema, req->host, req->vhost, req->app, req->port, req->param); - - transport = NULL; - client = NULL; - - stream_id = 0; } SrsSimpleRtmpClient::~SrsSimpleRtmpClient() { - close(); - srs_freep(kbps); -} - -int SrsSimpleRtmpClient::connect() -{ - int ret = ERROR_SUCCESS; - - close(); - - transport = new SrsTcpClient(req->host, req->port, connect_timeout); - client = new SrsRtmpClient(transport); - kbps->set_io(transport, transport); - - if ((ret = transport->connect()) != ERROR_SUCCESS) { - close(); - return ret; - } - - client->set_recv_timeout(stream_timeout); - client->set_send_timeout(stream_timeout); - - // connect to vhost/app - if ((ret = client->handshake()) != ERROR_SUCCESS) { - srs_error("sdk: handshake with server failed. ret=%d", ret); - return ret; - } - if ((ret = connect_app()) != ERROR_SUCCESS) { - srs_error("sdk: connect with server failed. ret=%d", ret); - return ret; - } - if ((ret = client->create_stream(stream_id)) != ERROR_SUCCESS) { - srs_error("sdk: connect with server failed, stream_id=%d. ret=%d", stream_id, ret); - return ret; - } - - return ret; -} - -void SrsSimpleRtmpClient::close() -{ - kbps->set_io(NULL, NULL); - srs_freep(client); - srs_freep(transport); } int SrsSimpleRtmpClient::connect_app() { - int ret = ERROR_SUCCESS; - - // args of request takes the srs info. - if (req->args == NULL) { - req->args = SrsAmf0Any::object(); - } - - // notify server the edge identity, - // @see https://github.com/ossrs/srs/issues/147 - SrsAmf0Object* data = req->args; - data->set("srs_sig", SrsAmf0Any::str(RTMP_SIG_SRS_KEY)); - data->set("srs_server", SrsAmf0Any::str(RTMP_SIG_SRS_SERVER)); - data->set("srs_license", SrsAmf0Any::str(RTMP_SIG_SRS_LICENSE)); - data->set("srs_role", SrsAmf0Any::str(RTMP_SIG_SRS_ROLE)); - data->set("srs_url", SrsAmf0Any::str(RTMP_SIG_SRS_URL)); - data->set("srs_version", SrsAmf0Any::str(RTMP_SIG_SRS_VERSION)); - data->set("srs_site", SrsAmf0Any::str(RTMP_SIG_SRS_WEB)); - data->set("srs_email", SrsAmf0Any::str(RTMP_SIG_SRS_EMAIL)); - data->set("srs_copyright", SrsAmf0Any::str(RTMP_SIG_SRS_COPYRIGHT)); - data->set("srs_primary", SrsAmf0Any::str(RTMP_SIG_SRS_PRIMARY)); - data->set("srs_authors", SrsAmf0Any::str(RTMP_SIG_SRS_AUTHROS)); - // for edge to directly get the id of client. - data->set("srs_pid", SrsAmf0Any::number(getpid())); - data->set("srs_id", SrsAmf0Any::number(_srs_context->get_id())); - - // local ip of edge std::vector ips = srs_get_local_ipv4_ips(); assert(_srs_config->get_stats_network() < (int)ips.size()); std::string local_ip = ips[_srs_config->get_stats_network()]; - data->set("srs_server_ip", SrsAmf0Any::str(local_ip.c_str())); - // generate the tcUrl - std::string param = ""; - std::string target_vhost = req->vhost; - std::string tc_url = srs_generate_tc_url(req->host, req->vhost, req->app, req->port, param); - - // replace the tcUrl in request, - // which will replace the tc_url in client.connect_app(). - req->tcUrl = tc_url; - - // upnode server identity will show in the connect_app of client. - // @see https://github.com/ossrs/srs/issues/160 - // the debug_srs_upnode is config in vhost and default to true. bool debug_srs_upnode = _srs_config->get_debug_srs_upnode(req->vhost); - if ((ret = client->connect_app(req->app, tc_url, req, debug_srs_upnode, NULL)) != ERROR_SUCCESS) { - srs_error("sdk: connect with server failed, tcUrl=%s, dsu=%d. ret=%d", - tc_url.c_str(), debug_srs_upnode, ret); - return ret; - } - return ret; -} - -int SrsSimpleRtmpClient::publish() -{ - int ret = ERROR_SUCCESS; - - // publish. - if ((ret = client->publish(req->stream, stream_id)) != ERROR_SUCCESS) { - srs_error("sdk: publish failed, stream=%s, stream_id=%d. ret=%d", - req->stream.c_str(), stream_id, ret); - return ret; - } - - return ret; -} - -int SrsSimpleRtmpClient::play() -{ - int ret = ERROR_SUCCESS; - - if ((ret = client->play(req->stream, stream_id)) != ERROR_SUCCESS) { - srs_error("connect with server failed, stream=%s, stream_id=%d. ret=%d", - req->stream.c_str(), stream_id, ret); - return ret; - } - - return ret; -} - -void SrsSimpleRtmpClient::kbps_sample(const char* label, int64_t age) -{ - kbps->sample(); - - int sr = kbps->get_send_kbps(); - int sr30s = kbps->get_send_kbps_30s(); - int sr5m = kbps->get_send_kbps_5m(); - int rr = kbps->get_recv_kbps(); - int rr30s = kbps->get_recv_kbps_30s(); - int rr5m = kbps->get_recv_kbps_5m(); - - srs_trace("<- %s time=%"PRId64", okbps=%d,%d,%d, ikbps=%d,%d,%d", label, age, sr, sr30s, sr5m, rr, rr30s, rr5m); -} - -void SrsSimpleRtmpClient::kbps_sample(const char* label, int64_t age, int msgs) -{ - kbps->sample(); - - int sr = kbps->get_send_kbps(); - int sr30s = kbps->get_send_kbps_30s(); - int sr5m = kbps->get_send_kbps_5m(); - int rr = kbps->get_recv_kbps(); - int rr30s = kbps->get_recv_kbps_30s(); - int rr5m = kbps->get_recv_kbps_5m(); - - srs_trace("<- %s time=%"PRId64", msgs=%d, okbps=%d,%d,%d, ikbps=%d,%d,%d", label, age, msgs, sr, sr30s, sr5m, rr, rr30s, rr5m); -} - -int SrsSimpleRtmpClient::sid() -{ - return stream_id; -} - -int SrsSimpleRtmpClient::recv_message(SrsCommonMessage** pmsg) -{ - return client->recv_message(pmsg); -} - -int SrsSimpleRtmpClient::decode_message(SrsCommonMessage* msg, SrsPacket** ppacket) -{ - return client->decode_message(msg, ppacket); -} - -int SrsSimpleRtmpClient::send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs) -{ - return client->send_and_free_messages(msgs, nb_msgs, stream_id); -} - -int SrsSimpleRtmpClient::send_and_free_message(SrsSharedPtrMessage* msg) -{ - return client->send_and_free_message(msg, stream_id); -} - -void SrsSimpleRtmpClient::set_recv_timeout(int64_t timeout) -{ - transport->set_recv_timeout(timeout); + return do_connect_app(local_ip, debug_srs_upnode); } SrsClientInfo::SrsClientInfo() diff --git a/trunk/src/app/srs_app_rtmp_conn.hpp b/trunk/src/app/srs_app_rtmp_conn.hpp index 6def74bdf..312d68718 100644 --- a/trunk/src/app/srs_app_rtmp_conn.hpp +++ b/trunk/src/app/srs_app_rtmp_conn.hpp @@ -32,6 +32,7 @@ #include #include #include +#include class SrsServer; class SrsRtmpServer; @@ -58,53 +59,15 @@ class ISrsKafkaCluster; #endif /** - * The simple RTMP client, provides friendly APIs. - * @remark Should never use client when closed. - * Usage: - * SrsSimpleRtmpClient client("rtmp://127.0.0.1:1935/live/livestream", 3000, 9000); - * client.connect(); - * client.play(); - * client.close(); + * The simple rtmp client for SRS. */ -class SrsSimpleRtmpClient +class SrsSimpleRtmpClient : public SrsBasicRtmpClient { -private: - std::string url; - int64_t connect_timeout; - int64_t stream_timeout; -private: - SrsRequest* req; - SrsTcpClient* transport; - SrsRtmpClient* client; - SrsKbps* kbps; - int stream_id; public: - // Constructor. - // @param u The RTMP url, for example, rtmp://ip:port/app/stream?domain=vhost - // @param ctm The timeout in ms to connect to server. - // @param stm The timeout in ms to delivery A/V stream. SrsSimpleRtmpClient(std::string u, int64_t ctm, int64_t stm); virtual ~SrsSimpleRtmpClient(); -public: - // Connect, handshake and connect app to RTMP server. - // @remark We always close the transport. - virtual int connect(); - virtual void close(); -private: +protected: virtual int connect_app(); -public: - virtual int publish(); - virtual int play(); - virtual void kbps_sample(const char* label, int64_t age); - virtual void kbps_sample(const char* label, int64_t age, int msgs); - virtual int sid(); -public: - virtual int recv_message(SrsCommonMessage** pmsg); - virtual int decode_message(SrsCommonMessage* msg, SrsPacket** ppacket); - virtual int send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs); - virtual int send_and_free_message(SrsSharedPtrMessage* msg); -public: - virtual void set_recv_timeout(int64_t timeout); }; /** diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index 34d14887e..7aa1da359 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -1311,8 +1311,9 @@ SrsConnection* SrsServer::fd2conn(SrsListenerType type, st_netfd_t stfd) return conn; } -void SrsServer::remove(SrsConnection* conn) +void SrsServer::remove(ISrsConnection* c) { + SrsConnection* conn = dynamic_cast(c); std::vector::iterator it = std::find(conns.begin(), conns.end(), conn); // removed by destroy, ignore. diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp index 8c6963829..a1cdfe9cb 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -365,7 +365,7 @@ public: * when connection thread cycle terminated, callback this to delete connection. * @see SrsConnection.on_thread_stop(). */ - virtual void remove(SrsConnection* conn); + virtual void remove(ISrsConnection* c); // interface ISrsReloadHandler. public: virtual int on_reload_listen(); diff --git a/trunk/src/app/srs_app_st.cpp b/trunk/src/app/srs_app_st.cpp index 66e10dd0f..d2e599d23 100755 --- a/trunk/src/app/srs_app_st.cpp +++ b/trunk/src/app/srs_app_st.cpp @@ -208,349 +208,3 @@ namespace internal } } -SrsStSocket::SrsStSocket() -{ - stfd = NULL; - stm = rtm = SRS_CONSTS_NO_TMMS; - rbytes = sbytes = 0; -} - -SrsStSocket::~SrsStSocket() -{ -} - -int SrsStSocket::initialize(st_netfd_t fd) -{ - stfd = fd; - return ERROR_SUCCESS; -} - -bool SrsStSocket::is_never_timeout(int64_t tm) -{ - return tm == SRS_CONSTS_NO_TMMS; -} - -void SrsStSocket::set_recv_timeout(int64_t tm) -{ - rtm = tm; -} - -int64_t SrsStSocket::get_recv_timeout() -{ - return rtm; -} - -void SrsStSocket::set_send_timeout(int64_t tm) -{ - stm = tm; -} - -int64_t SrsStSocket::get_send_timeout() -{ - return stm; -} - -int64_t SrsStSocket::get_recv_bytes() -{ - return rbytes; -} - -int64_t SrsStSocket::get_send_bytes() -{ - return sbytes; -} - -int SrsStSocket::read(void* buf, size_t size, ssize_t* nread) -{ - int ret = ERROR_SUCCESS; - - ssize_t nb_read; - if (rtm == SRS_CONSTS_NO_TMMS) { - nb_read = st_read(stfd, buf, size, ST_UTIME_NO_TIMEOUT); - } else { - nb_read = st_read(stfd, buf, size, rtm * 1000); - } - - if (nread) { - *nread = nb_read; - } - - // On success a non-negative integer indicating the number of bytes actually read is returned - // (a value of 0 means the network connection is closed or end of file is reached). - // Otherwise, a value of -1 is returned and errno is set to indicate the error. - if (nb_read <= 0) { - // @see https://github.com/ossrs/srs/issues/200 - if (nb_read < 0 && errno == ETIME) { - return ERROR_SOCKET_TIMEOUT; - } - - if (nb_read == 0) { - errno = ECONNRESET; - } - - return ERROR_SOCKET_READ; - } - - rbytes += nb_read; - - return ret; -} - -int SrsStSocket::read_fully(void* buf, size_t size, ssize_t* nread) -{ - int ret = ERROR_SUCCESS; - - ssize_t nb_read; - if (rtm == SRS_CONSTS_NO_TMMS) { - nb_read = st_read_fully(stfd, buf, size, ST_UTIME_NO_TIMEOUT); - } else { - nb_read = st_read_fully(stfd, buf, size, rtm * 1000); - } - - if (nread) { - *nread = nb_read; - } - - // On success a non-negative integer indicating the number of bytes actually read is returned - // (a value less than nbyte means the network connection is closed or end of file is reached) - // Otherwise, a value of -1 is returned and errno is set to indicate the error. - if (nb_read != (ssize_t)size) { - // @see https://github.com/ossrs/srs/issues/200 - if (nb_read < 0 && errno == ETIME) { - return ERROR_SOCKET_TIMEOUT; - } - - if (nb_read >= 0) { - errno = ECONNRESET; - } - - return ERROR_SOCKET_READ_FULLY; - } - - rbytes += nb_read; - - return ret; -} - -int SrsStSocket::write(void* buf, size_t size, ssize_t* nwrite) -{ - int ret = ERROR_SUCCESS; - - ssize_t nb_write; - if (stm == SRS_CONSTS_NO_TMMS) { - nb_write = st_write(stfd, buf, size, ST_UTIME_NO_TIMEOUT); - } else { - nb_write = st_write(stfd, buf, size, stm * 1000); - } - - if (nwrite) { - *nwrite = nb_write; - } - - // On success a non-negative integer equal to nbyte is returned. - // Otherwise, a value of -1 is returned and errno is set to indicate the error. - if (nb_write <= 0) { - // @see https://github.com/ossrs/srs/issues/200 - if (nb_write < 0 && errno == ETIME) { - return ERROR_SOCKET_TIMEOUT; - } - - return ERROR_SOCKET_WRITE; - } - - sbytes += nb_write; - - return ret; -} - -int SrsStSocket::writev(const iovec *iov, int iov_size, ssize_t* nwrite) -{ - int ret = ERROR_SUCCESS; - - ssize_t nb_write; - if (stm == SRS_CONSTS_NO_TMMS) { - nb_write = st_writev(stfd, iov, iov_size, ST_UTIME_NO_TIMEOUT); - } else { - nb_write = st_writev(stfd, iov, iov_size, stm * 1000); - } - - if (nwrite) { - *nwrite = nb_write; - } - - // On success a non-negative integer equal to nbyte is returned. - // Otherwise, a value of -1 is returned and errno is set to indicate the error. - if (nb_write <= 0) { - // @see https://github.com/ossrs/srs/issues/200 - if (nb_write < 0 && errno == ETIME) { - return ERROR_SOCKET_TIMEOUT; - } - - return ERROR_SOCKET_WRITE; - } - - sbytes += nb_write; - - return ret; -} - -SrsTcpClient::SrsTcpClient(string h, int p, int64_t tm) -{ - stfd = NULL; - io = new SrsStSocket(); - - host = h; - port = p; - timeout = tm; -} - -SrsTcpClient::~SrsTcpClient() -{ - close(); - - srs_freep(io); -} - -int SrsTcpClient::connect() -{ - int ret = ERROR_SUCCESS; - - close(); - - srs_assert(stfd == NULL); - if ((ret = srs_socket_connect(host, port, timeout, &stfd)) != ERROR_SUCCESS) { - srs_error("connect tcp://%s:%d failed, to=%"PRId64"ms. ret=%d", host.c_str(), port, timeout, ret); - return ret; - } - - if ((ret = io->initialize(stfd)) != ERROR_SUCCESS) { - return ret; - } - - return ret; -} - -void SrsTcpClient::close() -{ - // Ignore when already closed. - if (!io) { - return; - } - - srs_close_stfd(stfd); -} - -bool SrsTcpClient::is_never_timeout(int64_t tm) -{ - return io->is_never_timeout(tm); -} - -void SrsTcpClient::set_recv_timeout(int64_t tm) -{ - io->set_recv_timeout(tm); -} - -int64_t SrsTcpClient::get_recv_timeout() -{ - return io->get_recv_timeout(); -} - -void SrsTcpClient::set_send_timeout(int64_t tm) -{ - io->set_send_timeout(tm); -} - -int64_t SrsTcpClient::get_send_timeout() -{ - return io->get_send_timeout(); -} - -int64_t SrsTcpClient::get_recv_bytes() -{ - return io->get_recv_bytes(); -} - -int64_t SrsTcpClient::get_send_bytes() -{ - return io->get_send_bytes(); -} - -int SrsTcpClient::read(void* buf, size_t size, ssize_t* nread) -{ - return io->read(buf, size, nread); -} - -int SrsTcpClient::read_fully(void* buf, size_t size, ssize_t* nread) -{ - return io->read_fully(buf, size, nread); -} - -int SrsTcpClient::write(void* buf, size_t size, ssize_t* nwrite) -{ - return io->write(buf, size, nwrite); -} - -int SrsTcpClient::writev(const iovec *iov, int iov_size, ssize_t* nwrite) -{ - return io->writev(iov, iov_size, nwrite); -} - -#ifdef __linux__ -#include - -bool srs_st_epoll_is_supported(void) -{ - struct epoll_event ev; - - ev.events = EPOLLIN; - ev.data.ptr = NULL; - /* Guaranteed to fail */ - epoll_ctl(-1, EPOLL_CTL_ADD, -1, &ev); - - return (errno != ENOSYS); -} -#endif - -int srs_st_init() -{ - int ret = ERROR_SUCCESS; - -#ifdef __linux__ - // check epoll, some old linux donot support epoll. - // @see https://github.com/ossrs/srs/issues/162 - if (!srs_st_epoll_is_supported()) { - ret = ERROR_ST_SET_EPOLL; - srs_error("epoll required on Linux. ret=%d", ret); - return ret; - } -#endif - - // Select the best event system available on the OS. In Linux this is - // epoll(). On BSD it will be kqueue. - if (st_set_eventsys(ST_EVENTSYS_ALT) == -1) { - ret = ERROR_ST_SET_EPOLL; - srs_error("st_set_eventsys use %s failed. ret=%d", st_get_eventsys_name(), ret); - return ret; - } - srs_info("st_set_eventsys to %s", st_get_eventsys_name()); - - if(st_init() != 0){ - ret = ERROR_ST_INITIALIZE; - srs_error("st_init failed. ret=%d", ret); - return ret; - } - srs_trace("st_init success, use %s", st_get_eventsys_name()); - - return ret; -} - -void srs_close_stfd(st_netfd_t& stfd) -{ - if (stfd) { - // we must ensure the close is ok. - int err = st_netfd_close(stfd); - srs_assert(err != -1); - stfd = NULL; - } -} - diff --git a/trunk/src/app/srs_app_st.hpp b/trunk/src/app/srs_app_st.hpp index 5f31b9a2b..4313e93b4 100644 --- a/trunk/src/app/srs_app_st.hpp +++ b/trunk/src/app/srs_app_st.hpp @@ -159,111 +159,5 @@ namespace internal }; } -/** - * the socket provides TCP socket over st, - * that is, the sync socket mechanism. - */ -class SrsStSocket : public ISrsProtocolReaderWriter -{ -private: - // The recv/send timeout in ms. - // @remark Use SRS_CONSTS_NO_TMMS for never timeout in ms. - int64_t rtm; - int64_t stm; - // The recv/send data in bytes - int64_t rbytes; - int64_t sbytes; - // The underlayer st fd. - st_netfd_t stfd; -public: - SrsStSocket(); - virtual ~SrsStSocket(); -public: - // Initialize the socket with stfd, user must manage it. - virtual int initialize(st_netfd_t fd); -public: - virtual bool is_never_timeout(int64_t tm); - virtual void set_recv_timeout(int64_t tm); - virtual int64_t get_recv_timeout(); - virtual void set_send_timeout(int64_t tm); - virtual int64_t get_send_timeout(); - virtual int64_t get_recv_bytes(); - virtual int64_t get_send_bytes(); -public: - /** - * @param nread, the actual read bytes, ignore if NULL. - */ - virtual int read(void* buf, size_t size, ssize_t* nread); - virtual int read_fully(void* buf, size_t size, ssize_t* nread); - /** - * @param nwrite, the actual write bytes, ignore if NULL. - */ - virtual int write(void* buf, size_t size, ssize_t* nwrite); - virtual int writev(const iovec *iov, int iov_size, ssize_t* nwrite); -}; - -/** - * The client to connect to server over TCP. - * User must never reuse the client when close it. - * Usage: - * SrsTcpClient client("127.0.0.1", 1935,9000); - * client.connect(); - * client.write("Hello world!", 12, NULL); - * client.read(buf, 4096, NULL); - * @remark User can directly free the object, which will close the fd. - */ -class SrsTcpClient : public ISrsProtocolReaderWriter -{ -private: - st_netfd_t stfd; - SrsStSocket* io; -private: - std::string host; - int port; - // The timeout in ms. - int64_t timeout; -public: - /** - * Constructor. - * @param h the ip or hostname of server. - * @param p the port to connect to. - * @param tm the timeout in ms. - */ - SrsTcpClient(std::string h, int p, int64_t tm); - virtual ~SrsTcpClient(); -public: - /** - * Connect to server over TCP. - * @remark We will close the exists connection before do connect. - */ - virtual int connect(); -private: - /** - * Close the connection to server. - * @remark User should never use the client when close it. - */ - virtual void close(); -// interface ISrsProtocolReaderWriter -public: - virtual bool is_never_timeout(int64_t tm); - virtual void set_recv_timeout(int64_t tm); - virtual int64_t get_recv_timeout(); - virtual void set_send_timeout(int64_t tm); - virtual int64_t get_send_timeout(); - virtual int64_t get_recv_bytes(); - virtual int64_t get_send_bytes(); - virtual int read(void* buf, size_t size, ssize_t* nread); - virtual int read_fully(void* buf, size_t size, ssize_t* nread); - virtual int write(void* buf, size_t size, ssize_t* nwrite); - virtual int writev(const iovec *iov, int iov_size, ssize_t* nwrite); -}; - -// initialize st, requires epoll. -extern int srs_st_init(); - -// close the netfd, and close the underlayer fd. -// @remark when close, user must ensure io completed. -extern void srs_close_stfd(st_netfd_t& stfd); - #endif diff --git a/trunk/src/app/srs_app_utility.cpp b/trunk/src/app/srs_app_utility.cpp index 098d7a356..5e71f2cc2 100644 --- a/trunk/src/app/srs_app_utility.cpp +++ b/trunk/src/app/srs_app_utility.cpp @@ -53,63 +53,6 @@ using namespace std; // the longest time to wait for a process to quit. #define SRS_PROCESS_QUIT_TIMEOUT_MS 1000 -int srs_socket_connect(string server, int port, int64_t tm, st_netfd_t* pstfd) -{ - int ret = ERROR_SUCCESS; - - st_utime_t timeout = ST_UTIME_NO_TIMEOUT; - if (tm != SRS_CONSTS_NO_TMMS) { - timeout = (st_utime_t)(tm * 1000); - } - - *pstfd = NULL; - st_netfd_t stfd = NULL; - sockaddr_in addr; - - int sock = socket(AF_INET, SOCK_STREAM, 0); - if(sock == -1){ - ret = ERROR_SOCKET_CREATE; - srs_error("create socket error. ret=%d", ret); - return ret; - } - - srs_assert(!stfd); - stfd = st_netfd_open_socket(sock); - if(stfd == NULL){ - ret = ERROR_ST_OPEN_SOCKET; - srs_error("st_netfd_open_socket failed. ret=%d", ret); - return ret; - } - - // connect to server. - std::string ip = srs_dns_resolve(server); - if (ip.empty()) { - ret = ERROR_SYSTEM_IP_INVALID; - srs_error("dns resolve server error, ip empty. ret=%d", ret); - goto failed; - } - - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = inet_addr(ip.c_str()); - - if (st_connect(stfd, (const struct sockaddr*)&addr, sizeof(sockaddr_in), timeout) == -1){ - ret = ERROR_ST_CONNECT; - srs_error("connect to server error. ip=%s, port=%d, ret=%d", ip.c_str(), port, ret); - goto failed; - } - srs_info("connect ok. server=%s, ip=%s, port=%d", server.c_str(), ip.c_str(), port); - - *pstfd = stfd; - return ret; - -failed: - if (stfd) { - srs_close_stfd(stfd); - } - return ret; -} - SrsLogLevel srs_get_log_level(string level) { if ("verbose" == level) { @@ -1008,48 +951,6 @@ void srs_update_network_devices() #endif } -// we detect all network device as internet or intranet device, by its ip address. -// key is device name, for instance, eth0 -// value is whether internet, for instance, true. -static std::map _srs_device_ifs; - -bool srs_net_device_is_internet(string ifname) -{ - srs_info("check ifname=%s", ifname.c_str()); - - if (_srs_device_ifs.find(ifname) == _srs_device_ifs.end()) { - return false; - } - return _srs_device_ifs[ifname]; -} - -bool srs_net_device_is_internet(in_addr_t addr) -{ - uint32_t addr_h = ntohl(addr); - - // lo, 127.0.0.0-127.0.0.1 - if (addr_h >= 0x7f000000 && addr_h <= 0x7f000001) { - return false; - } - - // Class A 10.0.0.0-10.255.255.255 - if (addr_h >= 0x0a000000 && addr_h <= 0x0affffff) { - return false; - } - - // Class B 172.16.0.0-172.31.255.255 - if (addr_h >= 0xac100000 && addr_h <= 0xac1fffff) { - return false; - } - - // Class C 192.168.0.0-192.168.255.255 - if (addr_h >= 0xc0a80000 && addr_h <= 0xc0a8ffff) { - return false; - } - - return true; -} - SrsNetworkRtmpServer::SrsNetworkRtmpServer() { ok = false; @@ -1202,139 +1103,6 @@ void srs_update_rtmp_server(int nb_conn, SrsKbps* kbps) } } -vector _srs_system_ipv4_ips; - -void retrieve_local_ipv4_ips() -{ - vector& ips = _srs_system_ipv4_ips; - - ips.clear(); - - ifaddrs* ifap; - if (getifaddrs(&ifap) == -1) { - srs_warn("retrieve local ips, ini ifaddrs failed."); - return; - } - - stringstream ss0; - ss0 << "ips"; - - stringstream ss1; - ss1 << "devices"; - - ifaddrs* p = ifap; - while (p != NULL) { - ifaddrs* cur = p; - sockaddr* addr = cur->ifa_addr; - p = p->ifa_next; - - // retrieve ipv4 addr - // ignore the tun0 network device, - // which addr is NULL. - // @see: https://github.com/ossrs/srs/issues/141 - if (addr && addr->sa_family == AF_INET) { - in_addr* inaddr = &((sockaddr_in*)addr)->sin_addr; - - char buf[16]; - memset(buf, 0, sizeof(buf)); - - if ((inet_ntop(addr->sa_family, inaddr, buf, sizeof(buf))) == NULL) { - srs_warn("convert local ip failed"); - break; - } - - std::string ip = buf; - if (ip != SRS_CONSTS_LOCALHOST) { - ss0 << ", local[" << (int)ips.size() << "] ipv4 " << ip; - ips.push_back(ip); - } - - // set the device internet status. - if (!srs_net_device_is_internet(inaddr->s_addr)) { - ss1 << ", intranet "; - _srs_device_ifs[cur->ifa_name] = false; - } else { - ss1 << ", internet "; - _srs_device_ifs[cur->ifa_name] = true; - } - ss1 << cur->ifa_name << " " << ip; - } - } - srs_trace(ss0.str().c_str()); - srs_trace(ss1.str().c_str()); - - freeifaddrs(ifap); -} - -vector& srs_get_local_ipv4_ips() -{ - if (_srs_system_ipv4_ips.empty()) { - retrieve_local_ipv4_ips(); - } - - return _srs_system_ipv4_ips; -} - -std::string _public_internet_address; - -string srs_get_public_internet_address() -{ - if (!_public_internet_address.empty()) { - return _public_internet_address; - } - - std::vector& ips = srs_get_local_ipv4_ips(); - - // find the best match public address. - for (int i = 0; i < (int)ips.size(); i++) { - std::string ip = ips[i]; - in_addr_t addr = inet_addr(ip.c_str()); - uint32_t addr_h = ntohl(addr); - // lo, 127.0.0.0-127.0.0.1 - if (addr_h >= 0x7f000000 && addr_h <= 0x7f000001) { - srs_trace("ignore private address: %s", ip.c_str()); - continue; - } - // Class A 10.0.0.0-10.255.255.255 - if (addr_h >= 0x0a000000 && addr_h <= 0x0affffff) { - srs_trace("ignore private address: %s", ip.c_str()); - continue; - } - // Class B 172.16.0.0-172.31.255.255 - if (addr_h >= 0xac100000 && addr_h <= 0xac1fffff) { - srs_trace("ignore private address: %s", ip.c_str()); - continue; - } - // Class C 192.168.0.0-192.168.255.255 - if (addr_h >= 0xc0a80000 && addr_h <= 0xc0a8ffff) { - srs_trace("ignore private address: %s", ip.c_str()); - continue; - } - srs_warn("use public address as ip: %s", ip.c_str()); - - _public_internet_address = ip; - return ip; - } - - // no public address, use private address. - for (int i = 0; i < (int)ips.size(); i++) { - std::string ip = ips[i]; - in_addr_t addr = inet_addr(ip.c_str()); - uint32_t addr_h = ntohl(addr); - // lo, 127.0.0.0-127.0.0.1 - if (addr_h >= 0x7f000000 && addr_h <= 0x7f000001) { - srs_trace("ignore private address: %s", ip.c_str()); - continue; - } - srs_warn("use private address as ip: %s", ip.c_str()); - - _public_internet_address = ip; - return ip; - } - - return ""; -} - string srs_get_local_ip(int fd) { std::string ip; @@ -1407,16 +1175,6 @@ string srs_get_peer_ip(int fd) return ip; } -bool srs_string_is_http(string url) -{ - return srs_string_starts_with(url, "http://", "https://"); -} - -bool srs_string_is_rtmp(string url) -{ - return srs_string_starts_with(url, "rtmp://"); -} - bool srs_is_digit_number(const string& str) { if (str.empty()) { diff --git a/trunk/src/app/srs_app_utility.hpp b/trunk/src/app/srs_app_utility.hpp index d64a982e5..02a73df13 100644 --- a/trunk/src/app/srs_app_utility.hpp +++ b/trunk/src/app/srs_app_utility.hpp @@ -35,15 +35,12 @@ #include #include +#include class SrsKbps; class SrsBuffer; class SrsJsonObject; -// client open socket and connect to server. -// @param tm The timeout in ms. -extern int srs_socket_connect(std::string server, int port, int64_t tm, st_netfd_t* pstfd); - /** * convert level in string to log level in int. * @return the log level defined in SrsLogLevel. @@ -602,9 +599,6 @@ extern SrsNetworkDevices* srs_get_network_devices(); extern int srs_get_network_devices_count(); // the deamon st-thread will update it. extern void srs_update_network_devices(); -// detect whether specified device is internet public address. -extern bool srs_net_device_is_internet(std::string ifname); -extern bool srs_net_device_is_internet(in_addr_t addr); // system connections, and srs rtmp network summary class SrsNetworkRtmpServer @@ -649,12 +643,6 @@ extern SrsNetworkRtmpServer* srs_get_network_rtmp_server(); // the deamon st-thread will update it. extern void srs_update_rtmp_server(int nb_conn, SrsKbps* kbps); -// get local ip, fill to @param ips -extern std::vector& srs_get_local_ipv4_ips(); - -// get local public ip, empty string if no public internet address found. -extern std::string srs_get_public_internet_address(); - // get local or peer ip. // where local ip is the server ip which client connected. extern std::string srs_get_local_ip(int fd); @@ -663,10 +651,6 @@ extern int srs_get_local_port(int fd); // where peer ip is the client public ip which connected to server. extern std::string srs_get_peer_ip(int fd); -// whether the url is starts with http:// or https:// -extern bool srs_string_is_http(std::string url); -extern bool srs_string_is_rtmp(std::string url); - // whether string is digit number // is_digit("1234567890") === true // is_digit("0123456789") === false diff --git a/trunk/src/main/srs_main_ingest_hls.cpp b/trunk/src/main/srs_main_ingest_hls.cpp index 770dd03e0..5b7dad342 100644 --- a/trunk/src/main/srs_main_ingest_hls.cpp +++ b/trunk/src/main/srs_main_ingest_hls.cpp @@ -29,26 +29,23 @@ #include using namespace std; +#include #include -#include -#include -#include #include -#include #include #include #include -#include -#include -#include #include -#include -#include #include #include -#include -#include +#include #include +#include +#include +#include +#include +#include +#include // pre-declare int proxy_hls2rtmp(std::string hls, std::string rtmp); @@ -56,8 +53,6 @@ int proxy_hls2rtmp(std::string hls, std::string rtmp); // @global log and context. ISrsLog* _srs_log = new SrsConsoleLog(SrsLogLevelTrace, false); ISrsThreadContext* _srs_context = new SrsThreadContext(); -// @global config object for app module. -SrsConfig* _srs_config = NULL; /** * main entrance. @@ -628,7 +623,7 @@ private: int64_t raw_aac_dts; private: SrsRequest* req; - SrsSimpleRtmpClient* sdk; + SrsBasicRtmpClient* sdk; private: SrsRawH264Stream* avc; std::string h264_sps; @@ -1221,7 +1216,7 @@ int SrsIngestSrsOutput::connect() // connect host. int64_t cto = SRS_CONSTS_RTMP_TMMS; int64_t sto = SRS_CONSTS_RTMP_PULSE_TMMS; - sdk = new SrsSimpleRtmpClient(url, cto, sto); + sdk = new SrsBasicRtmpClient(url, cto, sto); if ((ret = sdk->connect()) != ERROR_SUCCESS) { close(); diff --git a/trunk/src/main/srs_main_mp4_parser.cpp b/trunk/src/main/srs_main_mp4_parser.cpp index 4ba7650af..a186780ea 100644 --- a/trunk/src/main/srs_main_mp4_parser.cpp +++ b/trunk/src/main/srs_main_mp4_parser.cpp @@ -24,14 +24,11 @@ #include #include -#include -#include +#include // @global log and context. -ISrsLog* _srs_log = new SrsFastLog(); -ISrsThreadContext* _srs_context = new ISrsThreadContext(); -// @global config object for app module. -SrsConfig* _srs_config = NULL; +ISrsLog* _srs_log = new SrsConsoleLog(SrsLogLevelTrace, false); +ISrsThreadContext* _srs_context = new SrsThreadContext(); /** * main entrance. diff --git a/trunk/src/service/srs_service_conn.cpp b/trunk/src/service/srs_service_conn.cpp new file mode 100644 index 000000000..3b970160a --- /dev/null +++ b/trunk/src/service/srs_service_conn.cpp @@ -0,0 +1,41 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 OSSRS(winlin) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +ISrsConnection::ISrsConnection() +{ +} + +ISrsConnection::~ISrsConnection() +{ +} + +IConnectionManager::IConnectionManager() +{ +} + +IConnectionManager::~IConnectionManager() +{ +} + diff --git a/trunk/src/service/srs_service_conn.hpp b/trunk/src/service/srs_service_conn.hpp new file mode 100644 index 000000000..980c44e33 --- /dev/null +++ b/trunk/src/service/srs_service_conn.hpp @@ -0,0 +1,55 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 OSSRS(winlin) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SRS_SERVICE_CONN_HPP +#define SRS_SERVICE_CONN_HPP + +#include + +/** + * The connection interface for all HTTP/RTMP/RTSP object. + */ +class ISrsConnection +{ +public: + ISrsConnection(); + virtual ~ISrsConnection(); +}; + +/** + * the manager for connection. + */ +class IConnectionManager +{ +public: + IConnectionManager(); + virtual ~IConnectionManager(); +public: + /** + * Remove then free the specified connection. + */ + virtual void remove(ISrsConnection* c) = 0; +}; + +#endif + diff --git a/trunk/src/service/srs_service_http_client.cpp b/trunk/src/service/srs_service_http_client.cpp new file mode 100644 index 000000000..e3fb18d56 --- /dev/null +++ b/trunk/src/service/srs_service_http_client.cpp @@ -0,0 +1,246 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 OSSRS(winlin) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include +#include +using namespace std; + +#include +#include +#include +#include +#include +#include +#include + +SrsHttpClient::SrsHttpClient() +{ + transport = NULL; + kbps = new SrsKbps(); + parser = NULL; + timeout = SRS_CONSTS_NO_TMMS; + port = 0; +} + +SrsHttpClient::~SrsHttpClient() +{ + disconnect(); + + srs_freep(kbps); + srs_freep(parser); +} + +// TODO: FIXME: use ms for timeout. +int SrsHttpClient::initialize(string h, int p, int64_t tm) +{ + int ret = ERROR_SUCCESS; + + srs_freep(parser); + parser = new SrsHttpParser(); + + if ((ret = parser->initialize(HTTP_RESPONSE, false)) != ERROR_SUCCESS) { + srs_error("initialize parser failed. ret=%d", ret); + return ret; + } + + // Always disconnect the transport. + host = h; + port = p; + timeout = tm; + disconnect(); + + // ep used for host in header. + string ep = host; + if (port > 0 && port != SRS_CONSTS_HTTP_DEFAULT_PORT) { + ep += ":" + srs_int2str(port); + } + + // Set default value for headers. + headers["Host"] = ep; + headers["Connection"] = "Keep-Alive"; + headers["User-Agent"] = RTMP_SIG_SRS_SERVER; + headers["Content-Type"] = "application/json"; + + return ret; +} + +SrsHttpClient* SrsHttpClient::set_header(string k, string v) +{ + headers[k] = v; + + return this; +} + +int SrsHttpClient::post(string path, string req, ISrsHttpMessage** ppmsg) +{ + *ppmsg = NULL; + + int ret = ERROR_SUCCESS; + + // always set the content length. + headers["Content-Length"] = srs_int2str(req.length()); + + if ((ret = connect()) != ERROR_SUCCESS) { + srs_warn("http connect server failed. ret=%d", ret); + return ret; + } + + // send POST request to uri + // POST %s HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s + std::stringstream ss; + ss << "POST " << path << " " << "HTTP/1.1" << SRS_HTTP_CRLF; + for (map::iterator it = headers.begin(); it != headers.end(); ++it) { + string key = it->first; + string value = it->second; + ss << key << ": " << value << SRS_HTTP_CRLF; + } + ss << SRS_HTTP_CRLF << req; + + std::string data = ss.str(); + if ((ret = transport->write((void*)data.c_str(), data.length(), NULL)) != ERROR_SUCCESS) { + // Disconnect the transport when channel error, reconnect for next operation. + disconnect(); + srs_error("write http post failed. ret=%d", ret); + return ret; + } + + ISrsHttpMessage* msg = NULL; + if ((ret = parser->parse_message(transport, NULL, &msg)) != ERROR_SUCCESS) { + srs_error("parse http post response failed. ret=%d", ret); + return ret; + } + srs_assert(msg); + + if (ppmsg) { + *ppmsg = msg; + } else { + srs_freep(msg); + } + srs_info("parse http post response success."); + + return ret; +} + +int SrsHttpClient::get(string path, string req, ISrsHttpMessage** ppmsg) +{ + *ppmsg = NULL; + + int ret = ERROR_SUCCESS; + + // always set the content length. + headers["Content-Length"] = srs_int2str(req.length()); + + if ((ret = connect()) != ERROR_SUCCESS) { + srs_warn("http connect server failed. ret=%d", ret); + return ret; + } + + // send POST request to uri + // GET %s HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s + std::stringstream ss; + ss << "GET " << path << " " << "HTTP/1.1" << SRS_HTTP_CRLF; + for (map::iterator it = headers.begin(); it != headers.end(); ++it) { + string key = it->first; + string value = it->second; + ss << key << ": " << value << SRS_HTTP_CRLF; + } + ss << SRS_HTTP_CRLF << req; + + std::string data = ss.str(); + if ((ret = transport->write((void*)data.c_str(), data.length(), NULL)) != ERROR_SUCCESS) { + // Disconnect the transport when channel error, reconnect for next operation. + disconnect(); + srs_error("write http get failed. ret=%d", ret); + return ret; + } + + ISrsHttpMessage* msg = NULL; + if ((ret = parser->parse_message(transport, NULL, &msg)) != ERROR_SUCCESS) { + srs_error("parse http post response failed. ret=%d", ret); + return ret; + } + srs_assert(msg); + + if (ppmsg) { + *ppmsg = msg; + } else { + srs_freep(msg); + } + srs_info("parse http get response success."); + + return ret; +} + +void SrsHttpClient::set_recv_timeout(int64_t tm) +{ + transport->set_recv_timeout(tm); +} + +void SrsHttpClient::kbps_sample(const char* label, int64_t age) +{ + kbps->sample(); + + int sr = kbps->get_send_kbps(); + int sr30s = kbps->get_send_kbps_30s(); + int sr5m = kbps->get_send_kbps_5m(); + int rr = kbps->get_recv_kbps(); + int rr30s = kbps->get_recv_kbps_30s(); + int rr5m = kbps->get_recv_kbps_5m(); + + srs_trace("<- %s time=%"PRId64", okbps=%d,%d,%d, ikbps=%d,%d,%d", label, age, sr, sr30s, sr5m, rr, rr30s, rr5m); +} + +void SrsHttpClient::disconnect() +{ + kbps->set_io(NULL, NULL); + srs_freep(transport); +} + +int SrsHttpClient::connect() +{ + int ret = ERROR_SUCCESS; + + // When transport connected, ignore. + if (transport) { + return ret; + } + + transport = new SrsTcpClient(host, port, timeout); + if ((ret = transport->connect()) != ERROR_SUCCESS) { + disconnect(); + srs_warn("http client failed, server=%s, port=%d, timeout=%"PRId64", ret=%d", host.c_str(), port, timeout, ret); + return ret; + } + srs_info("connect to server success. server=%s, port=%d", host.c_str(), port); + + // Set the recv/send timeout in ms. + transport->set_recv_timeout(timeout); + transport->set_send_timeout(timeout); + + kbps->set_io(transport, transport); + + return ret; +} + diff --git a/trunk/src/service/srs_service_http_client.hpp b/trunk/src/service/srs_service_http_client.hpp new file mode 100644 index 000000000..201203cd6 --- /dev/null +++ b/trunk/src/service/srs_service_http_client.hpp @@ -0,0 +1,111 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 OSSRS(winlin) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SRS_SERVICE_HTTP_CLIENT_HPP +#define SRS_SERVICE_HTTP_CLIENT_HPP + +#include + +#include +#include + +#include +#include + +class SrsHttpUri; +class SrsHttpParser; +class ISrsHttpMessage; +class SrsStSocket; +class SrsKbps; +class SrsTcpClient; + +// the default timeout for http client. +#define SRS_HTTP_CLIENT_TMMS (30*1000) + +/** + * The client to GET/POST/PUT/DELETE over HTTP. + * @remark We will reuse the TCP transport until initialize or channel error, + * such as send/recv failed. + * Usage: + * SrsHttpClient hc; + * hc.initialize("127.0.0.1", 80, 9000); + * hc.post("/api/v1/version", "Hello world!", NULL); + */ +class SrsHttpClient +{ +private: + // The underlayer TCP transport, set to NULL when disconnect, or never not NULL when connected. + // We will disconnect transport when initialize or channel error, such as send/recv error. + SrsTcpClient* transport; + SrsHttpParser* parser; + std::map headers; + SrsKbps* kbps; +private: + // The timeout in ms. + int64_t timeout; + // The host name or ip. + std::string host; + int port; +public: + SrsHttpClient(); + virtual ~SrsHttpClient(); +public: + /** + * Initliaze the client, disconnect the transport, renew the HTTP parser. + * @param tm The underlayer TCP transport timeout in ms. + * @remark we will set default values in headers, which can be override by set_header. + */ + virtual int initialize(std::string h, int p, int64_t tm = SRS_HTTP_CLIENT_TMMS); + /** + * Set HTTP request header in header[k]=v. + * @return the HTTP client itself. + */ + virtual SrsHttpClient* set_header(std::string k, std::string v); +public: + /** + * to post data to 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. + * @remark user must free the ppmsg if not NULL. + */ + 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. + * @remark user must free the ppmsg if not NULL. + */ + virtual int get(std::string path, std::string req, ISrsHttpMessage** ppmsg); +private: + virtual void set_recv_timeout(int64_t tm); +public: + virtual void kbps_sample(const char* label, int64_t age); +private: + virtual void disconnect(); + virtual int connect(); +}; + +#endif + diff --git a/trunk/src/service/srs_service_http_conn.cpp b/trunk/src/service/srs_service_http_conn.cpp new file mode 100644 index 000000000..8c2500ed3 --- /dev/null +++ b/trunk/src/service/srs_service_http_conn.cpp @@ -0,0 +1,1060 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 OSSRS(winlin) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include +using namespace std; + +#include +#include +#include +#include +#include +#include +#include +#include + +SrsHttpParser::SrsHttpParser() +{ + buffer = new SrsFastStream(); +} + +SrsHttpParser::~SrsHttpParser() +{ + srs_freep(buffer); +} + +int SrsHttpParser::initialize(enum http_parser_type type, bool allow_jsonp) +{ + int ret = ERROR_SUCCESS; + + jsonp = allow_jsonp; + + 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(ISrsProtocolReaderWriter* io, 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(io)) != 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(io, conn); + + // initalize http msg, parse url. + if ((ret = msg->update(url, jsonp, &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(ISrsProtocolReaderWriter* io) +{ + int ret = ERROR_SUCCESS; + + while (true) { + ssize_t nparsed = 0; + + // when got entire http header, parse it. + // @see https://github.com/ossrs/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(io, 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; +} + +SrsHttpMessage::SrsHttpMessage(ISrsProtocolReaderWriter* io, SrsConnection* c) : ISrsHttpMessage() +{ + conn = c; + chunked = false; + infinite_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]; + jsonp = false; +} + +SrsHttpMessage::~SrsHttpMessage() +{ + srs_freep(_body); + srs_freep(_uri); + srs_freepa(_http_ts_send_buffer); +} + +int SrsHttpMessage::update(string url, bool allow_jsonp, http_parser* header, SrsFastStream* 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"); + + // use server public ip when no host specified. + // to make telnet happy. + if (host.empty()) { + host= srs_get_public_internet_address(); + } + + // parse uri to schema/server:port/path?query + std::string uri = "http://" + host + _url; + if ((ret = _uri->initialize(uri)) != ERROR_SUCCESS) { + return ret; + } + + // parse ext. + _ext = srs_path_filext(_uri->get_path()); + + // parse query string. + srs_parse_query_string(_uri->get_query(), _query); + + // parse jsonp request message. + if (allow_jsonp) { + if (!query_get("callback").empty()) { + jsonp = true; + } + if (jsonp) { + jsonp_method = query_get("method"); + } + } + + return ret; +} + +SrsConnection* SrsHttpMessage::connection() +{ + return conn; +} + +uint8_t SrsHttpMessage::method() +{ + if (jsonp && !jsonp_method.empty()) { + if (jsonp_method == "GET") { + return SRS_CONSTS_HTTP_GET; + } else if (jsonp_method == "PUT") { + return SRS_CONSTS_HTTP_PUT; + } else if (jsonp_method == "POST") { + return SRS_CONSTS_HTTP_POST; + } else if (jsonp_method == "DELETE") { + return SRS_CONSTS_HTTP_DELETE; + } + } + + return (uint8_t)_header.method; +} + +uint16_t SrsHttpMessage::status_code() +{ + return (uint16_t)_header.status_code; +} + +string SrsHttpMessage::method_str() +{ + if (jsonp && !jsonp_method.empty()) { + return jsonp_method; + } + + 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 method() == SRS_CONSTS_HTTP_GET; +} + +bool SrsHttpMessage::is_http_put() +{ + return method() == SRS_CONSTS_HTTP_PUT; +} + +bool SrsHttpMessage::is_http_post() +{ + return method() == SRS_CONSTS_HTTP_POST; +} + +bool SrsHttpMessage::is_http_delete() +{ + return 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; +} + +bool SrsHttpMessage::is_infinite_chunked() +{ + return infinite_chunked; +} + +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::query() +{ + return _uri->get_query(); +} + +string SrsHttpMessage::ext() +{ + return _ext; +} + +int SrsHttpMessage::parse_rest_id(string pattern) +{ + string p = _uri->get_path(); + if (p.length() <= pattern.length()) { + return -1; + } + + string id = p.substr((int)pattern.length()); + if (!id.empty()) { + return ::atoi(id.c_str()); + } + + return -1; +} + +int SrsHttpMessage::enter_infinite_chunked() +{ + int ret = ERROR_SUCCESS; + + if (infinite_chunked) { + return ret; + } + + if (is_chunked() || content_length() != -1) { + ret = ERROR_HTTP_DATA_INVALID; + srs_error("infinite chunkted not supported in specified codec. ret=%d", ret); + return ret; + } + + infinite_chunked = true; + + return ret; +} + +int SrsHttpMessage::body_read_all(string& body) +{ + int ret = ERROR_SUCCESS; + + // cache to read. + char* buf = new char[SRS_HTTP_READ_CACHE_BYTES]; + SrsAutoFreeA(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(); + + // http path, for instance, /live/livestream.flv, parse to + // app: /live + // stream: livestream.flv + srs_parse_rtmp_url(_uri->get_path(), req->app, req->stream); + + // trim the start slash, for instance, /live to live + req->app = srs_string_trim_start(req->app, "/"); + + // remove the extension, for instance, livestream.flv to livestream + req->stream = srs_path_filename(req->stream); + + // generate others. + 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(); + + // reset the host to http request host. + if (req->host == SRS_CONSTS_RTMP_DEFAULT_VHOST) { + req->host = _uri->get_host(); + } + + return req; +} + +bool SrsHttpMessage::is_jsonp() +{ + return jsonp; +} + +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; + nb_iovss_cache = 0; + iovss_cache = NULL; +} + +SrsHttpResponseWriter::~SrsHttpResponseWriter() +{ + srs_freep(hdr); + srs_freepa(iovss_cache); +} + +int SrsHttpResponseWriter::final_request() +{ + // write the header data in memory. + if (!header_wrote) { + write_header(SRS_CONSTS_HTTP_OK); + } + + // 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; + + // write the header data in memory. + if (!header_wrote) { + write_header(SRS_CONSTS_HTTP_OK); + } + + // whatever header is wrote, we should try to send header. + if ((ret = send_header(data, size)) != ERROR_SUCCESS) { + srs_error("http: send header failed. ret=%d", ret); + return ret; + } + + // check the bytes send and content length. + 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; + } + + // 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. + int nb_size = snprintf(header_cache, SRS_HTTP_HEADER_CACHE_SIZE, "%x", size); + + iovec iovs[4]; + iovs[0].iov_base = (char*)header_cache; + iovs[0].iov_len = (int)nb_size; + iovs[1].iov_base = (char*)SRS_HTTP_CRLF; + iovs[1].iov_len = 2; + iovs[2].iov_base = (char*)data; + iovs[2].iov_len = size; + iovs[3].iov_base = (char*)SRS_HTTP_CRLF; + iovs[3].iov_len = 2; + + ssize_t nwrite; + if ((ret = skt->writev(iovs, 4, &nwrite)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int SrsHttpResponseWriter::writev(const iovec* iov, int iovcnt, ssize_t* pnwrite) +{ + int ret = ERROR_SUCCESS; + + // when header not ready, or not chunked, send one by one. + if (!header_wrote || content_length != -1) { + ssize_t nwrite = 0; + for (int i = 0; i < iovcnt; i++) { + const iovec* piovc = iov + i; + nwrite += piovc->iov_len; + if ((ret = write((char*)piovc->iov_base, (int)piovc->iov_len)) != ERROR_SUCCESS) { + return ret; + } + } + + if (pnwrite) { + *pnwrite = nwrite; + } + + return ret; + } + + // ignore NULL content. + if (iovcnt <= 0) { + return ret; + } + + // send in chunked encoding. + int nb_iovss = 3 + iovcnt; + iovec* iovss = iovss_cache; + if (nb_iovss_cache < nb_iovss) { + srs_freepa(iovss_cache); + nb_iovss_cache = nb_iovss; + iovss = iovss_cache = new iovec[nb_iovss]; + } + + // send in chunked encoding. + + // chunk size. + int size = 0; + for (int i = 0; i < iovcnt; i++) { + const iovec* data_iov = iov + i; + size += data_iov->iov_len; + } + written += size; + + // chunk header + int nb_size = snprintf(header_cache, SRS_HTTP_HEADER_CACHE_SIZE, "%x", size); + iovec* iovs = iovss; + iovs[0].iov_base = (char*)header_cache; + iovs[0].iov_len = (int)nb_size; + iovs++; + + // chunk header eof. + iovs[0].iov_base = (char*)SRS_HTTP_CRLF; + iovs[0].iov_len = 2; + iovs++; + + // chunk body. + for (int i = 0; i < iovcnt; i++) { + const iovec* data_iov = iov + i; + iovs[0].iov_base = (char*)data_iov->iov_base; + iovs[0].iov_len = (int)data_iov->iov_len; + iovs++; + } + + // chunk body eof. + iovs[0].iov_base = (char*)SRS_HTTP_CRLF; + iovs[0].iov_len = 2; + iovs++; + + // sendout all ioves. + ssize_t nwrite; + if ((ret = srs_write_large_iovs(skt, iovss, nb_iovss, &nwrite)) != ERROR_SUCCESS) { + return ret; + } + + if (pnwrite) { + *pnwrite = nwrite; + } + + 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_SERVER); + } + + // 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, ISrsProtocolReaderWriter* io) +{ + skt = io; + owner = msg; + is_eof = false; + nb_total_read = 0; + nb_left_chunk = 0; + buffer = NULL; +} + +SrsHttpResponseReader::~SrsHttpResponseReader() +{ +} + +int SrsHttpResponseReader::initialize(SrsFastStream* 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 + if (owner->content_length() != -1) { + 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); + } + + // infinite chunked mode, directly read. + if (owner->is_infinite_chunked()) { + srs_assert(!owner->is_chunked() && owner->content_length() == -1); + return read_specified(data, nb_data, nb_read); + } + + // infinite chunked mode, but user not set it, + // we think there is no data left. + is_eof = true; + + return ret; +} + +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 and specified content length. + if (!owner->is_chunked() && owner->content_length() != -1) { + // when read completed, eof. + if (nb_total_read >= (int)owner->content_length()) { + is_eof = true; + } + } + + return ret; +} + diff --git a/trunk/src/service/srs_service_http_conn.hpp b/trunk/src/service/srs_service_http_conn.hpp new file mode 100644 index 000000000..a2f86f3b2 --- /dev/null +++ b/trunk/src/service/srs_service_http_conn.hpp @@ -0,0 +1,326 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 OSSRS(winlin) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SRS_SERVICE_HTTP_CONN_HPP +#define SRS_SERVICE_HTTP_CONN_HPP + +#include + +#include + +#include + +class ISrsProtocolReaderWriter; +class SrsConnection; +class SrsFastStream; +class SrsRequest; +class SrsHttpResponseReader; +class SrsStSocket; + +/** + * wrapper for http-parser, + * provides HTTP message originted service. + */ +class SrsHttpParser +{ +private: + http_parser_settings settings; + http_parser parser; + // the global parse buffer. + SrsFastStream* buffer; + // whether allow jsonp parse. + bool jsonp; +private: + // http parse data, reset before parse message. + bool expect_field_name; + std::string field_name; + std::string field_value; + SrsHttpParseState state; + http_parser header; + 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. + * @param allow_jsonp whether allow jsonp parser, which indicates the method in query string. + */ + virtual int initialize(enum http_parser_type type, bool allow_jsonp); + /** + * 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(). + * @remark user must free the ppmsg if not NULL. + */ + virtual int parse_message(ISrsProtocolReaderWriter* io, SrsConnection* conn, ISrsHttpMessage** ppmsg); +private: + /** + * parse the HTTP message to member field: msg. + */ + virtual int parse_message_imp(ISrsProtocolReaderWriter* io); +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); +}; + +// 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. +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 body is infinite chunked. + */ + bool infinite_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; + // whether request is jsonp. + bool jsonp; + // the method in QueryString will override the HTTP method. + std::string jsonp_method; +public: + SrsHttpMessage(ISrsProtocolReaderWriter* io, SrsConnection* c); + virtual ~SrsHttpMessage(); +public: + /** + * set the original messages, then update the message. + */ + virtual int update(std::string url, bool allow_jsonp, http_parser* header, SrsFastStream* body, std::vector& headers); +public: + virtual SrsConnection* connection(); +public: + virtual uint8_t method(); + virtual uint16_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 body is infinite chunked encoding. + * @remark set by enter_infinite_chunked. + */ + virtual bool is_infinite_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 query(); + virtual std::string ext(); + /** + * get the RESTful matched id. + */ + virtual int parse_rest_id(std::string pattern); +public: + virtual int enter_infinite_chunked(); +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); +public: + virtual bool is_jsonp(); +}; + +// the http chunked header size, +// for writev, there always one chunk to send it. +#define SRS_HTTP_HEADER_CACHE_SIZE 64 + +/** + * response writer use st socket + */ +class SrsHttpResponseWriter : public ISrsHttpResponseWriter +{ +private: + SrsStSocket* skt; + SrsHttpHeader* hdr; +private: + char header_cache[SRS_HTTP_HEADER_CACHE_SIZE]; + iovec* iovss_cache; + int nb_iovss_cache; +private: + // reply header has been (logically) written + bool header_wrote; + // 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 int writev(const iovec* iov, int iovcnt, ssize_t* pnwrite); + 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: + ISrsProtocolReaderWriter* skt; + SrsHttpMessage* owner; + SrsFastStream* 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, ISrsProtocolReaderWriter* io); + virtual ~SrsHttpResponseReader(); +public: + /** + * initialize the response reader with buffer. + */ + virtual int initialize(SrsFastStream* 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); +}; + +#endif + diff --git a/trunk/src/service/srs_service_rtmp_conn.cpp b/trunk/src/service/srs_service_rtmp_conn.cpp new file mode 100644 index 000000000..69a0d948f --- /dev/null +++ b/trunk/src/service/srs_service_rtmp_conn.cpp @@ -0,0 +1,241 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 OSSRS(winlin) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +using namespace std; + +#include +#include +#include +#include +#include +#include + +SrsBasicRtmpClient::SrsBasicRtmpClient(string u, int64_t ctm, int64_t stm) +{ + kbps = new SrsKbps(); + + url = u; + connect_timeout = ctm; + stream_timeout = stm; + + req = new SrsRequest(); + srs_parse_rtmp_url(url, req->tcUrl, req->stream); + srs_discovery_tc_url(req->tcUrl, req->schema, req->host, req->vhost, req->app, req->port, req->param); + + transport = NULL; + client = NULL; + + stream_id = 0; +} + +SrsBasicRtmpClient::~SrsBasicRtmpClient() +{ + close(); + srs_freep(kbps); +} + +int SrsBasicRtmpClient::connect() +{ + int ret = ERROR_SUCCESS; + + close(); + + transport = new SrsTcpClient(req->host, req->port, connect_timeout); + client = new SrsRtmpClient(transport); + kbps->set_io(transport, transport); + + if ((ret = transport->connect()) != ERROR_SUCCESS) { + close(); + return ret; + } + + client->set_recv_timeout(stream_timeout); + client->set_send_timeout(stream_timeout); + + // connect to vhost/app + if ((ret = client->handshake()) != ERROR_SUCCESS) { + srs_error("sdk: handshake with server failed. ret=%d", ret); + return ret; + } + if ((ret = connect_app()) != ERROR_SUCCESS) { + srs_error("sdk: connect with server failed. ret=%d", ret); + return ret; + } + if ((ret = client->create_stream(stream_id)) != ERROR_SUCCESS) { + srs_error("sdk: connect with server failed, stream_id=%d. ret=%d", stream_id, ret); + return ret; + } + + return ret; +} + +void SrsBasicRtmpClient::close() +{ + kbps->set_io(NULL, NULL); + srs_freep(client); + srs_freep(transport); +} + +int SrsBasicRtmpClient::connect_app() +{ + return do_connect_app(srs_get_public_internet_address(), false); +} + +int SrsBasicRtmpClient::do_connect_app(string local_ip, bool debug) +{ + int ret = ERROR_SUCCESS; + + // args of request takes the srs info. + if (req->args == NULL) { + req->args = SrsAmf0Any::object(); + } + + // notify server the edge identity, + // @see https://github.com/ossrs/srs/issues/147 + SrsAmf0Object* data = req->args; + data->set("srs_sig", SrsAmf0Any::str(RTMP_SIG_SRS_KEY)); + data->set("srs_server", SrsAmf0Any::str(RTMP_SIG_SRS_SERVER)); + data->set("srs_license", SrsAmf0Any::str(RTMP_SIG_SRS_LICENSE)); + data->set("srs_role", SrsAmf0Any::str(RTMP_SIG_SRS_ROLE)); + data->set("srs_url", SrsAmf0Any::str(RTMP_SIG_SRS_URL)); + data->set("srs_version", SrsAmf0Any::str(RTMP_SIG_SRS_VERSION)); + data->set("srs_site", SrsAmf0Any::str(RTMP_SIG_SRS_WEB)); + data->set("srs_email", SrsAmf0Any::str(RTMP_SIG_SRS_EMAIL)); + data->set("srs_copyright", SrsAmf0Any::str(RTMP_SIG_SRS_COPYRIGHT)); + data->set("srs_primary", SrsAmf0Any::str(RTMP_SIG_SRS_PRIMARY)); + data->set("srs_authors", SrsAmf0Any::str(RTMP_SIG_SRS_AUTHROS)); + // for edge to directly get the id of client. + data->set("srs_pid", SrsAmf0Any::number(getpid())); + data->set("srs_id", SrsAmf0Any::number(_srs_context->get_id())); + + // local ip of edge + data->set("srs_server_ip", SrsAmf0Any::str(local_ip.c_str())); + + // generate the tcUrl + std::string param = ""; + std::string target_vhost = req->vhost; + std::string tc_url = srs_generate_tc_url(req->host, req->vhost, req->app, req->port, param); + + // replace the tcUrl in request, + // which will replace the tc_url in client.connect_app(). + req->tcUrl = tc_url; + + // upnode server identity will show in the connect_app of client. + // @see https://github.com/ossrs/srs/issues/160 + // the debug_srs_upnode is config in vhost and default to true. + if ((ret = client->connect_app(req->app, tc_url, req, debug, NULL)) != ERROR_SUCCESS) { + srs_error("sdk: connect with server failed, tcUrl=%s, dsu=%d. ret=%d", + tc_url.c_str(), debug, ret); + return ret; + } + + return ret; +} + +int SrsBasicRtmpClient::publish() +{ + int ret = ERROR_SUCCESS; + + // publish. + if ((ret = client->publish(req->stream, stream_id)) != ERROR_SUCCESS) { + srs_error("sdk: publish failed, stream=%s, stream_id=%d. ret=%d", + req->stream.c_str(), stream_id, ret); + return ret; + } + + return ret; +} + +int SrsBasicRtmpClient::play() +{ + int ret = ERROR_SUCCESS; + + if ((ret = client->play(req->stream, stream_id)) != ERROR_SUCCESS) { + srs_error("connect with server failed, stream=%s, stream_id=%d. ret=%d", + req->stream.c_str(), stream_id, ret); + return ret; + } + + return ret; +} + +void SrsBasicRtmpClient::kbps_sample(const char* label, int64_t age) +{ + kbps->sample(); + + int sr = kbps->get_send_kbps(); + int sr30s = kbps->get_send_kbps_30s(); + int sr5m = kbps->get_send_kbps_5m(); + int rr = kbps->get_recv_kbps(); + int rr30s = kbps->get_recv_kbps_30s(); + int rr5m = kbps->get_recv_kbps_5m(); + + srs_trace("<- %s time=%"PRId64", okbps=%d,%d,%d, ikbps=%d,%d,%d", label, age, sr, sr30s, sr5m, rr, rr30s, rr5m); +} + +void SrsBasicRtmpClient::kbps_sample(const char* label, int64_t age, int msgs) +{ + kbps->sample(); + + int sr = kbps->get_send_kbps(); + int sr30s = kbps->get_send_kbps_30s(); + int sr5m = kbps->get_send_kbps_5m(); + int rr = kbps->get_recv_kbps(); + int rr30s = kbps->get_recv_kbps_30s(); + int rr5m = kbps->get_recv_kbps_5m(); + + srs_trace("<- %s time=%"PRId64", msgs=%d, okbps=%d,%d,%d, ikbps=%d,%d,%d", label, age, msgs, sr, sr30s, sr5m, rr, rr30s, rr5m); +} + +int SrsBasicRtmpClient::sid() +{ + return stream_id; +} + +int SrsBasicRtmpClient::recv_message(SrsCommonMessage** pmsg) +{ + return client->recv_message(pmsg); +} + +int SrsBasicRtmpClient::decode_message(SrsCommonMessage* msg, SrsPacket** ppacket) +{ + return client->decode_message(msg, ppacket); +} + +int SrsBasicRtmpClient::send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs) +{ + return client->send_and_free_messages(msgs, nb_msgs, stream_id); +} + +int SrsBasicRtmpClient::send_and_free_message(SrsSharedPtrMessage* msg) +{ + return client->send_and_free_message(msg, stream_id); +} + +void SrsBasicRtmpClient::set_recv_timeout(int64_t timeout) +{ + transport->set_recv_timeout(timeout); +} + diff --git a/trunk/src/service/srs_service_rtmp_conn.hpp b/trunk/src/service/srs_service_rtmp_conn.hpp new file mode 100644 index 000000000..37b895443 --- /dev/null +++ b/trunk/src/service/srs_service_rtmp_conn.hpp @@ -0,0 +1,92 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 OSSRS(winlin) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SRS_SERVICE_RTMP_CONN_HPP +#define SRS_SERVICE_RTMP_CONN_HPP + +#include + +#include + +class SrsRequest; +class SrsTcpClient; +class SrsRtmpClient; +class SrsCommonMessage; +class SrsSharedPtrMessage; +class SrsPacket; +class SrsKbps; + +/** + * The simple RTMP client, provides friendly APIs. + * @remark Should never use client when closed. + * Usage: + * SrsBasicRtmpClient client("rtmp://127.0.0.1:1935/live/livestream", 3000, 9000); + * client.connect(); + * client.play(); + * client.close(); + */ +class SrsBasicRtmpClient +{ +private: + std::string url; + int64_t connect_timeout; + int64_t stream_timeout; +protected: + SrsRequest* req; +private: + SrsTcpClient* transport; + SrsRtmpClient* client; + SrsKbps* kbps; + int stream_id; +public: + // Constructor. + // @param u The RTMP url, for example, rtmp://ip:port/app/stream?domain=vhost + // @param ctm The timeout in ms to connect to server. + // @param stm The timeout in ms to delivery A/V stream. + SrsBasicRtmpClient(std::string u, int64_t ctm, int64_t stm); + virtual ~SrsBasicRtmpClient(); +public: + // Connect, handshake and connect app to RTMP server. + // @remark We always close the transport. + virtual int connect(); + virtual void close(); +protected: + virtual int connect_app(); + virtual int do_connect_app(std::string local_ip, bool debug); +public: + virtual int publish(); + virtual int play(); + virtual void kbps_sample(const char* label, int64_t age); + virtual void kbps_sample(const char* label, int64_t age, int msgs); + virtual int sid(); +public: + virtual int recv_message(SrsCommonMessage** pmsg); + virtual int decode_message(SrsCommonMessage* msg, SrsPacket** ppacket); + virtual int send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs); + virtual int send_and_free_message(SrsSharedPtrMessage* msg); +public: + virtual void set_recv_timeout(int64_t timeout); +}; + +#endif + diff --git a/trunk/src/service/srs_service_st.cpp b/trunk/src/service/srs_service_st.cpp index 4abc2d74e..91e2c014a 100644 --- a/trunk/src/service/srs_service_st.cpp +++ b/trunk/src/service/srs_service_st.cpp @@ -23,3 +23,355 @@ #include +using namespace std; + +#include +#include +#include + +#ifdef __linux__ +#include + +bool srs_st_epoll_is_supported(void) +{ + struct epoll_event ev; + + ev.events = EPOLLIN; + ev.data.ptr = NULL; + /* Guaranteed to fail */ + epoll_ctl(-1, EPOLL_CTL_ADD, -1, &ev); + + return (errno != ENOSYS); +} +#endif + +int srs_st_init() +{ + int ret = ERROR_SUCCESS; + +#ifdef __linux__ + // check epoll, some old linux donot support epoll. + // @see https://github.com/ossrs/srs/issues/162 + if (!srs_st_epoll_is_supported()) { + ret = ERROR_ST_SET_EPOLL; + srs_error("epoll required on Linux. ret=%d", ret); + return ret; + } +#endif + + // Select the best event system available on the OS. In Linux this is + // epoll(). On BSD it will be kqueue. + if (st_set_eventsys(ST_EVENTSYS_ALT) == -1) { + ret = ERROR_ST_SET_EPOLL; + srs_error("st_set_eventsys use %s failed. ret=%d", st_get_eventsys_name(), ret); + return ret; + } + srs_info("st_set_eventsys to %s", st_get_eventsys_name()); + + if(st_init() != 0){ + ret = ERROR_ST_INITIALIZE; + srs_error("st_init failed. ret=%d", ret); + return ret; + } + srs_trace("st_init success, use %s", st_get_eventsys_name()); + + return ret; +} + +void srs_close_stfd(st_netfd_t& stfd) +{ + if (stfd) { + // we must ensure the close is ok. + int err = st_netfd_close(stfd); + srs_assert(err != -1); + stfd = NULL; + } +} + +SrsStSocket::SrsStSocket() +{ + stfd = NULL; + stm = rtm = SRS_CONSTS_NO_TMMS; + rbytes = sbytes = 0; +} + +SrsStSocket::~SrsStSocket() +{ +} + +int SrsStSocket::initialize(st_netfd_t fd) +{ + stfd = fd; + return ERROR_SUCCESS; +} + +bool SrsStSocket::is_never_timeout(int64_t tm) +{ + return tm == SRS_CONSTS_NO_TMMS; +} + +void SrsStSocket::set_recv_timeout(int64_t tm) +{ + rtm = tm; +} + +int64_t SrsStSocket::get_recv_timeout() +{ + return rtm; +} + +void SrsStSocket::set_send_timeout(int64_t tm) +{ + stm = tm; +} + +int64_t SrsStSocket::get_send_timeout() +{ + return stm; +} + +int64_t SrsStSocket::get_recv_bytes() +{ + return rbytes; +} + +int64_t SrsStSocket::get_send_bytes() +{ + return sbytes; +} + +int SrsStSocket::read(void* buf, size_t size, ssize_t* nread) +{ + int ret = ERROR_SUCCESS; + + ssize_t nb_read; + if (rtm == SRS_CONSTS_NO_TMMS) { + nb_read = st_read(stfd, buf, size, ST_UTIME_NO_TIMEOUT); + } else { + nb_read = st_read(stfd, buf, size, rtm * 1000); + } + + if (nread) { + *nread = nb_read; + } + + // On success a non-negative integer indicating the number of bytes actually read is returned + // (a value of 0 means the network connection is closed or end of file is reached). + // Otherwise, a value of -1 is returned and errno is set to indicate the error. + if (nb_read <= 0) { + // @see https://github.com/ossrs/srs/issues/200 + if (nb_read < 0 && errno == ETIME) { + return ERROR_SOCKET_TIMEOUT; + } + + if (nb_read == 0) { + errno = ECONNRESET; + } + + return ERROR_SOCKET_READ; + } + + rbytes += nb_read; + + return ret; +} + +int SrsStSocket::read_fully(void* buf, size_t size, ssize_t* nread) +{ + int ret = ERROR_SUCCESS; + + ssize_t nb_read; + if (rtm == SRS_CONSTS_NO_TMMS) { + nb_read = st_read_fully(stfd, buf, size, ST_UTIME_NO_TIMEOUT); + } else { + nb_read = st_read_fully(stfd, buf, size, rtm * 1000); + } + + if (nread) { + *nread = nb_read; + } + + // On success a non-negative integer indicating the number of bytes actually read is returned + // (a value less than nbyte means the network connection is closed or end of file is reached) + // Otherwise, a value of -1 is returned and errno is set to indicate the error. + if (nb_read != (ssize_t)size) { + // @see https://github.com/ossrs/srs/issues/200 + if (nb_read < 0 && errno == ETIME) { + return ERROR_SOCKET_TIMEOUT; + } + + if (nb_read >= 0) { + errno = ECONNRESET; + } + + return ERROR_SOCKET_READ_FULLY; + } + + rbytes += nb_read; + + return ret; +} + +int SrsStSocket::write(void* buf, size_t size, ssize_t* nwrite) +{ + int ret = ERROR_SUCCESS; + + ssize_t nb_write; + if (stm == SRS_CONSTS_NO_TMMS) { + nb_write = st_write(stfd, buf, size, ST_UTIME_NO_TIMEOUT); + } else { + nb_write = st_write(stfd, buf, size, stm * 1000); + } + + if (nwrite) { + *nwrite = nb_write; + } + + // On success a non-negative integer equal to nbyte is returned. + // Otherwise, a value of -1 is returned and errno is set to indicate the error. + if (nb_write <= 0) { + // @see https://github.com/ossrs/srs/issues/200 + if (nb_write < 0 && errno == ETIME) { + return ERROR_SOCKET_TIMEOUT; + } + + return ERROR_SOCKET_WRITE; + } + + sbytes += nb_write; + + return ret; +} + +int SrsStSocket::writev(const iovec *iov, int iov_size, ssize_t* nwrite) +{ + int ret = ERROR_SUCCESS; + + ssize_t nb_write; + if (stm == SRS_CONSTS_NO_TMMS) { + nb_write = st_writev(stfd, iov, iov_size, ST_UTIME_NO_TIMEOUT); + } else { + nb_write = st_writev(stfd, iov, iov_size, stm * 1000); + } + + if (nwrite) { + *nwrite = nb_write; + } + + // On success a non-negative integer equal to nbyte is returned. + // Otherwise, a value of -1 is returned and errno is set to indicate the error. + if (nb_write <= 0) { + // @see https://github.com/ossrs/srs/issues/200 + if (nb_write < 0 && errno == ETIME) { + return ERROR_SOCKET_TIMEOUT; + } + + return ERROR_SOCKET_WRITE; + } + + sbytes += nb_write; + + return ret; +} + +SrsTcpClient::SrsTcpClient(string h, int p, int64_t tm) +{ + stfd = NULL; + io = new SrsStSocket(); + + host = h; + port = p; + timeout = tm; +} + +SrsTcpClient::~SrsTcpClient() +{ + close(); + + srs_freep(io); +} + +int SrsTcpClient::connect() +{ + int ret = ERROR_SUCCESS; + + close(); + + srs_assert(stfd == NULL); + if ((ret = srs_socket_connect(host, port, timeout, &stfd)) != ERROR_SUCCESS) { + srs_error("connect tcp://%s:%d failed, to=%"PRId64"ms. ret=%d", host.c_str(), port, timeout, ret); + return ret; + } + + if ((ret = io->initialize(stfd)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +void SrsTcpClient::close() +{ + // Ignore when already closed. + if (!io) { + return; + } + + srs_close_stfd(stfd); +} + +bool SrsTcpClient::is_never_timeout(int64_t tm) +{ + return io->is_never_timeout(tm); +} + +void SrsTcpClient::set_recv_timeout(int64_t tm) +{ + io->set_recv_timeout(tm); +} + +int64_t SrsTcpClient::get_recv_timeout() +{ + return io->get_recv_timeout(); +} + +void SrsTcpClient::set_send_timeout(int64_t tm) +{ + io->set_send_timeout(tm); +} + +int64_t SrsTcpClient::get_send_timeout() +{ + return io->get_send_timeout(); +} + +int64_t SrsTcpClient::get_recv_bytes() +{ + return io->get_recv_bytes(); +} + +int64_t SrsTcpClient::get_send_bytes() +{ + return io->get_send_bytes(); +} + +int SrsTcpClient::read(void* buf, size_t size, ssize_t* nread) +{ + return io->read(buf, size, nread); +} + +int SrsTcpClient::read_fully(void* buf, size_t size, ssize_t* nread) +{ + return io->read_fully(buf, size, nread); +} + +int SrsTcpClient::write(void* buf, size_t size, ssize_t* nwrite) +{ + return io->write(buf, size, nwrite); +} + +int SrsTcpClient::writev(const iovec *iov, int iov_size, ssize_t* nwrite) +{ + return io->writev(iov, iov_size, nwrite); +} + diff --git a/trunk/src/service/srs_service_st.hpp b/trunk/src/service/srs_service_st.hpp index 36fc3c984..281de85e4 100644 --- a/trunk/src/service/srs_service_st.hpp +++ b/trunk/src/service/srs_service_st.hpp @@ -26,7 +26,116 @@ #include +#include #include +#include + +// initialize st, requires epoll. +extern int srs_st_init(); + +// close the netfd, and close the underlayer fd. +// @remark when close, user must ensure io completed. +extern void srs_close_stfd(st_netfd_t& stfd); + +/** + * the socket provides TCP socket over st, + * that is, the sync socket mechanism. + */ +class SrsStSocket : public ISrsProtocolReaderWriter +{ +private: + // The recv/send timeout in ms. + // @remark Use SRS_CONSTS_NO_TMMS for never timeout in ms. + int64_t rtm; + int64_t stm; + // The recv/send data in bytes + int64_t rbytes; + int64_t sbytes; + // The underlayer st fd. + st_netfd_t stfd; +public: + SrsStSocket(); + virtual ~SrsStSocket(); +public: + // Initialize the socket with stfd, user must manage it. + virtual int initialize(st_netfd_t fd); +public: + virtual bool is_never_timeout(int64_t tm); + virtual void set_recv_timeout(int64_t tm); + virtual int64_t get_recv_timeout(); + virtual void set_send_timeout(int64_t tm); + virtual int64_t get_send_timeout(); + virtual int64_t get_recv_bytes(); + virtual int64_t get_send_bytes(); +public: + /** + * @param nread, the actual read bytes, ignore if NULL. + */ + virtual int read(void* buf, size_t size, ssize_t* nread); + virtual int read_fully(void* buf, size_t size, ssize_t* nread); + /** + * @param nwrite, the actual write bytes, ignore if NULL. + */ + virtual int write(void* buf, size_t size, ssize_t* nwrite); + virtual int writev(const iovec *iov, int iov_size, ssize_t* nwrite); +}; + +/** + * The client to connect to server over TCP. + * User must never reuse the client when close it. + * Usage: + * SrsTcpClient client("127.0.0.1", 1935,9000); + * client.connect(); + * client.write("Hello world!", 12, NULL); + * client.read(buf, 4096, NULL); + * @remark User can directly free the object, which will close the fd. + */ +class SrsTcpClient : public ISrsProtocolReaderWriter +{ +private: + st_netfd_t stfd; + SrsStSocket* io; +private: + std::string host; + int port; + // The timeout in ms. + int64_t timeout; +public: + /** + * Constructor. + * @param h the ip or hostname of server. + * @param p the port to connect to. + * @param tm the timeout in ms. + */ + SrsTcpClient(std::string h, int p, int64_t tm); + virtual ~SrsTcpClient(); +public: + /** + * Connect to server over TCP. + * @remark We will close the exists connection before do connect. + */ + virtual int connect(); +private: + /** + * Close the connection to server. + * @remark User should never use the client when close it. + */ + virtual void close(); + // interface ISrsProtocolReaderWriter +public: + virtual bool is_never_timeout(int64_t tm); + virtual void set_recv_timeout(int64_t tm); + virtual int64_t get_recv_timeout(); + virtual void set_send_timeout(int64_t tm); + virtual int64_t get_send_timeout(); + virtual int64_t get_recv_bytes(); + virtual int64_t get_send_bytes(); + virtual int read(void* buf, size_t size, ssize_t* nread); + virtual int read_fully(void* buf, size_t size, ssize_t* nread); + virtual int write(void* buf, size_t size, ssize_t* nwrite); + virtual int writev(const iovec *iov, int iov_size, ssize_t* nwrite); +}; + #endif diff --git a/trunk/src/service/srs_service_utility.cpp b/trunk/src/service/srs_service_utility.cpp new file mode 100644 index 000000000..e3454782a --- /dev/null +++ b/trunk/src/service/srs_service_utility.cpp @@ -0,0 +1,280 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 OSSRS(winlin) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include +using namespace std; + +#include +#include +#include +#include +#include + +int srs_socket_connect(string server, int port, int64_t tm, st_netfd_t* pstfd) +{ + int ret = ERROR_SUCCESS; + + st_utime_t timeout = ST_UTIME_NO_TIMEOUT; + if (tm != SRS_CONSTS_NO_TMMS) { + timeout = (st_utime_t)(tm * 1000); + } + + *pstfd = NULL; + st_netfd_t stfd = NULL; + sockaddr_in addr; + + int sock = socket(AF_INET, SOCK_STREAM, 0); + if(sock == -1){ + ret = ERROR_SOCKET_CREATE; + srs_error("create socket error. ret=%d", ret); + return ret; + } + + srs_assert(!stfd); + stfd = st_netfd_open_socket(sock); + if(stfd == NULL){ + ret = ERROR_ST_OPEN_SOCKET; + srs_error("st_netfd_open_socket failed. ret=%d", ret); + return ret; + } + + // connect to server. + std::string ip = srs_dns_resolve(server); + if (ip.empty()) { + ret = ERROR_SYSTEM_IP_INVALID; + srs_error("dns resolve server error, ip empty. ret=%d", ret); + goto failed; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr(ip.c_str()); + + if (st_connect(stfd, (const struct sockaddr*)&addr, sizeof(sockaddr_in), timeout) == -1){ + ret = ERROR_ST_CONNECT; + srs_error("connect to server error. ip=%s, port=%d, ret=%d", ip.c_str(), port, ret); + goto failed; + } + srs_info("connect ok. server=%s, ip=%s, port=%d", server.c_str(), ip.c_str(), port); + + *pstfd = stfd; + return ret; + +failed: + if (stfd) { + srs_close_stfd(stfd); + } + return ret; +} + +bool srs_string_is_http(string url) +{ + return srs_string_starts_with(url, "http://", "https://"); +} + +bool srs_string_is_rtmp(string url) +{ + return srs_string_starts_with(url, "rtmp://"); +} + +// we detect all network device as internet or intranet device, by its ip address. +// key is device name, for instance, eth0 +// value is whether internet, for instance, true. +static std::map _srs_device_ifs; + +bool srs_net_device_is_internet(string ifname) +{ + srs_info("check ifname=%s", ifname.c_str()); + + if (_srs_device_ifs.find(ifname) == _srs_device_ifs.end()) { + return false; + } + return _srs_device_ifs[ifname]; +} + +bool srs_net_device_is_internet(in_addr_t addr) +{ + uint32_t addr_h = ntohl(addr); + + // lo, 127.0.0.0-127.0.0.1 + if (addr_h >= 0x7f000000 && addr_h <= 0x7f000001) { + return false; + } + + // Class A 10.0.0.0-10.255.255.255 + if (addr_h >= 0x0a000000 && addr_h <= 0x0affffff) { + return false; + } + + // Class B 172.16.0.0-172.31.255.255 + if (addr_h >= 0xac100000 && addr_h <= 0xac1fffff) { + return false; + } + + // Class C 192.168.0.0-192.168.255.255 + if (addr_h >= 0xc0a80000 && addr_h <= 0xc0a8ffff) { + return false; + } + + return true; +} + +vector _srs_system_ipv4_ips; + +void retrieve_local_ipv4_ips() +{ + vector& ips = _srs_system_ipv4_ips; + + ips.clear(); + + ifaddrs* ifap; + if (getifaddrs(&ifap) == -1) { + srs_warn("retrieve local ips, ini ifaddrs failed."); + return; + } + + stringstream ss0; + ss0 << "ips"; + + stringstream ss1; + ss1 << "devices"; + + ifaddrs* p = ifap; + while (p != NULL) { + ifaddrs* cur = p; + sockaddr* addr = cur->ifa_addr; + p = p->ifa_next; + + // retrieve ipv4 addr + // ignore the tun0 network device, + // which addr is NULL. + // @see: https://github.com/ossrs/srs/issues/141 + if (addr && addr->sa_family == AF_INET) { + in_addr* inaddr = &((sockaddr_in*)addr)->sin_addr; + + char buf[16]; + memset(buf, 0, sizeof(buf)); + + if ((inet_ntop(addr->sa_family, inaddr, buf, sizeof(buf))) == NULL) { + srs_warn("convert local ip failed"); + break; + } + + std::string ip = buf; + if (ip != SRS_CONSTS_LOCALHOST) { + ss0 << ", local[" << (int)ips.size() << "] ipv4 " << ip; + ips.push_back(ip); + } + + // set the device internet status. + if (!srs_net_device_is_internet(inaddr->s_addr)) { + ss1 << ", intranet "; + _srs_device_ifs[cur->ifa_name] = false; + } else { + ss1 << ", internet "; + _srs_device_ifs[cur->ifa_name] = true; + } + ss1 << cur->ifa_name << " " << ip; + } + } + srs_trace(ss0.str().c_str()); + srs_trace(ss1.str().c_str()); + + freeifaddrs(ifap); +} + +vector& srs_get_local_ipv4_ips() +{ + if (_srs_system_ipv4_ips.empty()) { + retrieve_local_ipv4_ips(); + } + + return _srs_system_ipv4_ips; +} + +std::string _public_internet_address; + +string srs_get_public_internet_address() +{ + if (!_public_internet_address.empty()) { + return _public_internet_address; + } + + std::vector& ips = srs_get_local_ipv4_ips(); + + // find the best match public address. + for (int i = 0; i < (int)ips.size(); i++) { + std::string ip = ips[i]; + in_addr_t addr = inet_addr(ip.c_str()); + uint32_t addr_h = ntohl(addr); + // lo, 127.0.0.0-127.0.0.1 + if (addr_h >= 0x7f000000 && addr_h <= 0x7f000001) { + srs_trace("ignore private address: %s", ip.c_str()); + continue; + } + // Class A 10.0.0.0-10.255.255.255 + if (addr_h >= 0x0a000000 && addr_h <= 0x0affffff) { + srs_trace("ignore private address: %s", ip.c_str()); + continue; + } + // Class B 172.16.0.0-172.31.255.255 + if (addr_h >= 0xac100000 && addr_h <= 0xac1fffff) { + srs_trace("ignore private address: %s", ip.c_str()); + continue; + } + // Class C 192.168.0.0-192.168.255.255 + if (addr_h >= 0xc0a80000 && addr_h <= 0xc0a8ffff) { + srs_trace("ignore private address: %s", ip.c_str()); + continue; + } + srs_warn("use public address as ip: %s", ip.c_str()); + + _public_internet_address = ip; + return ip; + } + + // no public address, use private address. + for (int i = 0; i < (int)ips.size(); i++) { + std::string ip = ips[i]; + in_addr_t addr = inet_addr(ip.c_str()); + uint32_t addr_h = ntohl(addr); + // lo, 127.0.0.0-127.0.0.1 + if (addr_h >= 0x7f000000 && addr_h <= 0x7f000001) { + srs_trace("ignore private address: %s", ip.c_str()); + continue; + } + srs_warn("use private address as ip: %s", ip.c_str()); + + _public_internet_address = ip; + return ip; + } + + return ""; +} + diff --git a/trunk/src/service/srs_service_utility.hpp b/trunk/src/service/srs_service_utility.hpp new file mode 100644 index 000000000..556842b22 --- /dev/null +++ b/trunk/src/service/srs_service_utility.hpp @@ -0,0 +1,53 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 OSSRS(winlin) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SRS_SERVICE_UTILITY_HPP +#define SRS_SERVICE_UTILITY_HPP + +#include + +#include +#include + +#include + +// client open socket and connect to server. +// @param tm The timeout in ms. +extern int srs_socket_connect(std::string server, int port, int64_t tm, st_netfd_t* pstfd); + +// whether the url is starts with http:// or https:// +extern bool srs_string_is_http(std::string url); +extern bool srs_string_is_rtmp(std::string url); + +// get local ip, fill to @param ips +extern std::vector& srs_get_local_ipv4_ips(); + +// get local public ip, empty string if no public internet address found. +extern std::string srs_get_public_internet_address(); + +// detect whether specified device is internet public address. +extern bool srs_net_device_is_internet(std::string ifname); +extern bool srs_net_device_is_internet(in_addr_t addr); + +#endif +