diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 8b0dc96b..afbb069a 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -13,14 +13,19 @@ #include "Http/HttpRequester.h" #include "Http/HttpSession.h" #include "Network/TcpServer.h" +#include "Player/PlayerProxy.h" using namespace Json; using namespace toolkit; using namespace mediakit; + +typedef map ApiArgsType; + + #define API_ARGS HttpSession::KeyValue &headerIn, \ HttpSession::KeyValue &headerOut, \ - HttpSession::KeyValue &allArgs, \ + ApiArgsType &allArgs, \ Json::Value &val #define API_REGIST(field, name, ...) \ @@ -49,7 +54,7 @@ typedef enum { #define API_FIELD "api." const char kApiDebug[] = API_FIELD"apiDebug"; static onceToken token([]() { - mINI::Instance()[kApiDebug] = "0"; + mINI::Instance()[kApiDebug] = "1"; }); }//namespace API @@ -72,20 +77,34 @@ public: //获取HTTP请求中url参数、content参数 -static HttpSession::KeyValue getAllArgs(const Parser &parser) { - HttpSession::KeyValue allArgs; - { - //TraceL << parser.FullUrl() << "\r\n" << parser.Content(); - auto &urlArgs = parser.getUrlArgs(); +static ApiArgsType getAllArgs(const Parser &parser) { + ApiArgsType allArgs; + if(parser["Content-Type"].find("application/x-www-form-urlencoded") == 0){ auto contentArgs = parser.parseArgs(parser.Content()); for (auto &pr : contentArgs) { - allArgs.emplace(pr.first, HttpSession::urlDecode(pr.second)); + allArgs[pr.first] = HttpSession::urlDecode(pr.second); } - for (auto &pr : urlArgs) { - allArgs.emplace(pr.first, HttpSession::urlDecode(pr.second)); + }else if(parser["Content-Type"].find("application/json") == 0){ + try { + stringstream ss(parser.Content()); + Value jsonArgs; + ss >> jsonArgs; + auto keys = jsonArgs.getMemberNames(); + for (auto key = keys.begin(); key != keys.end(); ++key){ + allArgs[*key] = jsonArgs[*key].asString(); + } + }catch (std::exception &ex){ + WarnL << ex.what(); } + }else if(!parser["Content-Type"].empty()){ + WarnL << "invalid Content-Type:" << parser["Content-Type"]; } - return allArgs; + + auto &urlArgs = parser.getUrlArgs(); + for (auto &pr : urlArgs) { + allArgs[pr.first] = HttpSession::urlDecode(pr.second); + } + return std::move(allArgs); } static inline void addHttpListener(){ @@ -107,7 +126,7 @@ static inline void addHttpListener(){ val["code"] = API::Success; HttpSession::KeyValue &headerIn = parser.getValues(); HttpSession::KeyValue headerOut; - HttpSession::KeyValue allArgs = getAllArgs(parser); + auto allArgs = getAllArgs(parser); headerOut["Content-Type"] = "application/json; charset=utf-8"; if(api_debug){ auto newInvoker = [invoker,parser,allArgs](const string &codeOut, @@ -115,13 +134,13 @@ static inline void addHttpListener(){ const string &contentOut){ stringstream ss; for(auto &pr : allArgs ){ - ss << pr.first << " : " << pr.second << "\r\n"; + ss << pr.first << " : " << (string)pr.second << "\r\n"; } - DebugL << "request:\r\n" << parser.Method() << " " << parser.FullUrl() << "\r\n" - << "content:\r\n" << parser.Content() << "\r\n" - << "args:\r\n" << ss.str() - << "response:\r\n" + DebugL << "\r\n# request:\r\n" << parser.Method() << " " << parser.FullUrl() << "\r\n" + << "# content:\r\n" << parser.Content() << "\r\n" + << "# args:\r\n" << ss.str() + << "# response:\r\n" << contentOut << "\r\n"; invoker(codeOut,headerOut,contentOut); @@ -263,7 +282,7 @@ void installWebApi() { item["vhost"] = vhost; item["app"] = app; item["stream"] = stream; - val["data"]["array"].append(item); + val["data"].append(item); }); }); @@ -303,6 +322,33 @@ void installWebApi() { }); + static unordered_map s_proxyMap; + static recursive_mutex s_proxyMapMtx; + API_REGIST(api,addStreamProxy,{ + //添加拉流代理 + PlayerProxy::Ptr player(new PlayerProxy( + allArgs["vhost"], + allArgs["app"], + allArgs["stream"], + allArgs["enable_hls"], + allArgs["enable_mp4"] + )); + //指定RTP over TCP(播放rtsp时有效) + (*player)[kRtpType] = allArgs["rtp_type"].as(); + //开始播放,如果播放失败或者播放中止,将会自动重试若干次,重试次数在配置文件中配置,默认一直重试 + player->play(allArgs["url"]); + + val["data"]["id"] = player.get(); + lock_guard lck(s_proxyMapMtx); + s_proxyMap[(uint64_t)player.get()] = player; + }); + + API_REGIST(api,delStreamProxy,{ + lock_guard lck(s_proxyMapMtx); + val["data"]["flag"] = s_proxyMap.erase(allArgs["id"].as()) == 1; + }); + + ////////////以下是注册的Hook API//////////// API_REGIST(hook,on_publish,{ //开始推流事件 @@ -321,4 +367,40 @@ void installWebApi() { val["code"] = 0; val["msg"] = "success"; }); + + API_REGIST(hook,on_rtsp_realm,{ + //rtsp是否需要鉴权 + val["code"] = 0; + val["realm"] = "zlmediakit_reaml"; + }); + + API_REGIST(hook,on_rtsp_auth,{ + //rtsp鉴权密码,密码等于用户名 + //rtsp可以有双重鉴权!后面还会触发on_play事件 + val["code"] = 0; + val["encrypted"] = false; + val["passwd"] = allArgs["user_name"]; + }); + + API_REGIST(hook,on_stream_changed,{ + //媒体注册或反注册事件 + val["code"] = 0; + val["msg"] = "success"; + }); + + API_REGIST(hook,on_stream_not_found,{ + //媒体未找到事件 + val["code"] = 0; + val["msg"] = "success"; + }); + + + API_REGIST(hook,on_record_mp4,{ + //录制mp4分片完毕事件 + val["code"] = 0; + val["msg"] = "success"; + }); + + + } \ No newline at end of file diff --git a/server/WebHook.cpp b/server/WebHook.cpp index 078efce4..f5102b71 100644 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -11,11 +11,23 @@ #include "Common/MediaSource.h" #include "Http/HttpRequester.h" #include "Network/TcpSession.h" +#include "Rtsp/RtspSession.h" using namespace Json; using namespace toolkit; using namespace mediakit; + +//支持json或urlencoded方式传输参数 +#define JSON_ARGS + +#ifdef JSON_ARGS +typedef Value ArgsType; +#else +typedef HttpArgs ArgsType; +#endif + + namespace Hook { #define HOOK_FIELD "hook." @@ -24,6 +36,11 @@ const char kTimeoutSec[] = HOOK_FIELD"timeoutSec"; const char kOnPublish[] = HOOK_FIELD"on_publish"; const char kOnPlay[] = HOOK_FIELD"on_play"; const char kOnFlowReport[] = HOOK_FIELD"on_flow_report"; +const char kOnRtspRealm[] = HOOK_FIELD"on_rtsp_realm"; +const char kOnRtspAuth[] = HOOK_FIELD"on_rtsp_auth"; +const char kOnStreamChanged[] = HOOK_FIELD"on_stream_changed"; +const char kOnStreamNotFound[] = HOOK_FIELD"on_stream_not_found"; +const char kOnRecordMp4[] = HOOK_FIELD"on_record_mp4"; const char kAdminParams[] = HOOK_FIELD"admin_params"; onceToken token([](){ @@ -32,6 +49,11 @@ onceToken token([](){ mINI::Instance()[kOnPublish] = "http://127.0.0.1/index/hook/on_publish"; mINI::Instance()[kOnPlay] = "http://127.0.0.1/index/hook/on_play"; mINI::Instance()[kOnFlowReport] = "http://127.0.0.1/index/hook/on_flow_report"; + mINI::Instance()[kOnRtspRealm] = "http://127.0.0.1/index/hook/on_rtsp_realm"; + mINI::Instance()[kOnRtspAuth] = "http://127.0.0.1/index/hook/on_rtsp_auth"; + mINI::Instance()[kOnStreamChanged] = "http://127.0.0.1/index/hook/on_stream_changed"; + mINI::Instance()[kOnStreamNotFound] = "http://127.0.0.1/index/hook/on_stream_not_found"; + mINI::Instance()[kOnRecordMp4] = "http://127.0.0.1/index/hook/on_record_mp4"; mINI::Instance()[kAdminParams] = "token=035c73f7-bb6b-4889-a715-d9eb2d1925cc"; },nullptr); }//namespace Hook @@ -68,14 +90,31 @@ static void parse_http_response(const SockException &ex, } } -static void do_http_hook(const string &url,const Value &body,const function &fun){ +string to_string(const Value &value){ + return value.toStyledString(); +} + +string to_string(const HttpArgs &value){ + return value.make(); +} + +const char *getContentType(const Value &value){ + return "application/json"; +} + +const char *getContentType(const HttpArgs &value){ + return "application/x-www-form-urlencoded"; +} + +static void do_http_hook(const string &url,const ArgsType &body,const function &fun){ GET_CONFIG_AND_REGISTER(float,hook_timeoutSec,Hook::kTimeoutSec); HttpRequester::Ptr requester(new HttpRequester); requester->setMethod("POST"); - requester->setBody(body.toStyledString()); - requester->addHeader("Content-Type","application/json; charset=utf-8"); + auto bodyStr = to_string(body); + requester->setBody(bodyStr); + requester->addHeader("Content-Type",getContentType(body)); std::shared_ptr pTicker(new Ticker); - requester->startRequester(url,[url,fun,body,requester,pTicker](const SockException &ex, + requester->startRequester(url,[url,fun,bodyStr,requester,pTicker](const SockException &ex, const string &status, const HttpClient::HttpHeader &header, const string &strRecvBody){ @@ -87,22 +126,22 @@ static void do_http_hook(const string &url,const Value &body,const functionelapsedTime() << "ms,failed" << err << ":" << body; + WarnL << "hook " << url << " " <elapsedTime() << "ms,failed" << err << ":" << bodyStr; }else if(pTicker->elapsedTime() > 500){ - DebugL << "hook " << url << " " <elapsedTime() << "ms,success:" << body; + DebugL << "hook " << url << " " <elapsedTime() << "ms,success:" << bodyStr; } }); },hook_timeoutSec); } -static Value make_json(const MediaInfo &args){ - Value body; +static ArgsType make_json(const MediaInfo &args){ + ArgsType body; body["schema"] = args._schema; body["vhost"] = args._vhost; body["app"] = args._app; body["stream"] = args._streamid; body["params"] = args._param_strs; - return body; + return std::move(body); } @@ -112,14 +151,20 @@ void installWebHook(){ GET_CONFIG_AND_REGISTER(string,hook_play,Hook::kOnPlay); GET_CONFIG_AND_REGISTER(string,hook_flowreport,Hook::kOnFlowReport); GET_CONFIG_AND_REGISTER(string,hook_adminparams,Hook::kAdminParams); + GET_CONFIG_AND_REGISTER(string,hook_rtsp_realm,Hook::kOnRtspRealm); + GET_CONFIG_AND_REGISTER(string,hook_rtsp_auth,Hook::kOnRtspAuth); + GET_CONFIG_AND_REGISTER(string,hook_stream_chaned,Hook::kOnStreamChanged); + GET_CONFIG_AND_REGISTER(string,hook_stream_not_found,Hook::kOnStreamNotFound); + GET_CONFIG_AND_REGISTER(string,hook_record_mp4,Hook::kOnRecordMp4); - NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastRtmpPublish,[](BroadcastRtmpPublishArgs){ - if(!hook_enable || args._param_strs == hook_adminparams){ + + NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastMediaPublish,[](BroadcastMediaPublishArgs){ + if(!hook_enable || args._param_strs == hook_adminparams || hook_publish.empty()){ invoker(""); return; } //异步执行该hook api,防止阻塞NoticeCenter - Value body = make_json(args); + auto body = make_json(args); body["ip"] = sender.get_peer_ip(); body["port"] = sender.get_peer_port(); body["id"] = sender.getIdentifier(); @@ -132,11 +177,11 @@ void installWebHook(){ }); NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastMediaPlayed,[](BroadcastMediaPlayedArgs){ - if(!hook_enable || args._param_strs == hook_adminparams){ + if(!hook_enable || args._param_strs == hook_adminparams || hook_play.empty()){ invoker(""); return; } - Value body = make_json(args); + auto body = make_json(args); body["ip"] = sender.get_peer_ip(); body["port"] = sender.get_peer_port(); body["id"] = sender.getIdentifier(); @@ -150,10 +195,10 @@ void installWebHook(){ }); NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastFlowReport,[](BroadcastFlowReportArgs){ - if(!hook_enable || args._param_strs == hook_adminparams){ + if(!hook_enable || args._param_strs == hook_adminparams || hook_flowreport.empty()){ return; } - Value body = make_json(args); + auto body = make_json(args); body["ip"] = sender.get_peer_ip(); body["port"] = sender.get_peer_port(); body["id"] = sender.getIdentifier(); @@ -166,4 +211,122 @@ void installWebHook(){ do_http_hook(hook_flowreport,body, nullptr); }); }); + + + static const string unAuthedRealm = "unAuthedRealm"; + + //监听kBroadcastOnGetRtspRealm事件决定rtsp链接是否需要鉴权(传统的rtsp鉴权方案)才能访问 + NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastOnGetRtspRealm,[](BroadcastOnGetRtspRealmArgs){ + if(!hook_enable || args._param_strs == hook_adminparams || hook_rtsp_realm.empty()){ + //无需认证 + invoker(""); + return; + } + auto body = make_json(args); + body["ip"] = sender.get_peer_ip(); + body["port"] = sender.get_peer_port(); + body["id"] = sender.getIdentifier(); + + EventPollerPool::Instance().getExecutor()->async([body,invoker](){ + //执行hook + do_http_hook(hook_rtsp_realm,body, [invoker](const Value &obj,const string &err){ + if(!err.empty()){ + //如果接口访问失败,那么该rtsp流认证失败 + invoker(unAuthedRealm); + return; + } + invoker(obj["realm"].asString()); + }); + }); + }); + + //监听kBroadcastOnRtspAuth事件返回正确的rtsp鉴权用户密码 + NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastOnRtspAuth,[](BroadcastOnRtspAuthArgs){ + if(unAuthedRealm == realm || !hook_enable || hook_rtsp_auth.empty()){ + //认证失败 + invoker(false,makeRandStr(12)); + return; + } + auto body = make_json(args); + body["ip"] = sender.get_peer_ip(); + body["port"] = sender.get_peer_port(); + body["id"] = sender.getIdentifier(); + body["user_name"] = user_name; + body["must_no_encrypt"] = must_no_encrypt; + body["realm"] = realm; + + EventPollerPool::Instance().getExecutor()->async([body,invoker](){ + //执行hook + do_http_hook(hook_rtsp_auth,body, [invoker](const Value &obj,const string &err){ + if(!err.empty()){ + //认证失败 + invoker(false,makeRandStr(12)); + return; + } + invoker(obj["encrypted"].asBool(),obj["passwd"].asString()); + }); + }); + }); + + + //监听rtsp、rtmp源注册或注销事件 + NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastMediaChanged,[](BroadcastMediaChangedArgs){ + if(!hook_enable || hook_stream_chaned.empty()){ + return; + } + ArgsType body; + body["regist"] = bRegist; + body["schema"] = schema; + body["vhost"] = vhost; + body["app"] = app; + body["stream"] = stream; + + EventPollerPool::Instance().getExecutor()->async([body](){ + //执行hook + do_http_hook(hook_stream_chaned,body, nullptr); + }); + }); + + //监听播放失败(未找到特定的流)事件 + NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastNotFoundStream,[](BroadcastNotFoundStreamArgs){ + if(!hook_enable || hook_stream_not_found.empty()){ + return; + } + auto body = make_json(args); + body["ip"] = sender.get_peer_ip(); + body["port"] = sender.get_peer_port(); + body["id"] = sender.getIdentifier(); + + EventPollerPool::Instance().getExecutor()->async([body](){ + //执行hook + do_http_hook(hook_stream_not_found,body, nullptr); + }); + }); + +#ifdef ENABLE_MP4V2 + //录制mp4文件成功后广播 + NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastRecordMP4,[](BroadcastRecordMP4Args){ + if(!hook_enable || hook_record_mp4.empty()){ + return; + } + ArgsType body; + body["start_time"] = (uint64_t)info.ui64StartedTime; + body["time_len"] = (uint64_t)info.ui64TimeLen; + body["file_size"] = info.ui64FileSize; + body["file_path"] = info.strFilePath; + body["file_name"] = info.strFileName; + body["folder"] = info.strFolder; + body["url"] = info.strUrl; + body["app"] = info.strAppName; + body["stream"] = info.strStreamId; + body["vhost"] = info.strVhost; + + EventPollerPool::Instance().getExecutor()->async([body](){ + //执行hook + do_http_hook(hook_record_mp4,body, nullptr); + }); + }); +#endif //ENABLE_MP4V2 + + } \ No newline at end of file diff --git a/server/main.cpp b/server/main.cpp index ec5bd7aa..bf3d891a 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -188,95 +188,100 @@ static void inline listen_shell_input(){ EventPollerPool::Instance().getFirstPoller()->addEvent(STDIN_FILENO, Event_Read | Event_Error | Event_LT,oninput); } int main(int argc,char *argv[]) { - CMD_main cmd_main; - try { - cmd_main.operator()(argc,argv); - } catch (std::exception &ex) { - cout << ex.what() << endl; - return -1; - } + { + CMD_main cmd_main; + try { + cmd_main.operator()(argc, argv); + } catch (std::exception &ex) { + cout << ex.what() << endl; + return -1; + } - bool bDaemon = cmd_main.hasKey("daemon"); - LogLevel logLevel = (LogLevel)cmd_main["level"].as(); - logLevel = MIN(MAX(logLevel,LTrace),LError); - string ini_file = cmd_main["config"]; - string ssl_file = cmd_main["ssl"]; - int threads = cmd_main["threads"]; + bool bDaemon = cmd_main.hasKey("daemon"); + LogLevel logLevel = (LogLevel) cmd_main["level"].as(); + logLevel = MIN(MAX(logLevel, LTrace), LError); + string ini_file = cmd_main["config"]; + string ssl_file = cmd_main["ssl"]; + int threads = cmd_main["threads"]; - //设置日志 - Logger::Instance().add(std::make_shared("ConsoleChannel",logLevel)); + //设置日志 + Logger::Instance().add(std::make_shared("ConsoleChannel", logLevel)); #if defined(__linux__) || defined(__linux) - Logger::Instance().add(std::make_shared("SysLogChannel",logLevel)); + Logger::Instance().add(std::make_shared("SysLogChannel",logLevel)); #else - Logger::Instance().add(std::make_shared("FileChannel",exePath() + ".log",logLevel)); + Logger::Instance().add(std::make_shared("FileChannel", exePath() + ".log", logLevel)); #endif - if(bDaemon){ - //启动守护进程 - System::startDaemon(); + if (bDaemon) { + //启动守护进程 + System::startDaemon(); + } + + //启动异步日志线程 + Logger::Instance().setWriter(std::make_shared()); + //加载配置文件,如果配置文件不存在就创建一个 + loadIniConfig(ini_file.data()); + + //加载证书,证书包含公钥和私钥 + SSL_Initor::Instance().loadCertificate(ssl_file.data()); + //信任某个自签名证书 + SSL_Initor::Instance().trustCertificate(ssl_file.data()); + //不忽略无效证书证书(例如自签名或过期证书) + SSL_Initor::Instance().ignoreInvalidCertificate(false); + + uint16_t shellPort = mINI::Instance()[Shell::kPort]; + uint16_t rtspPort = mINI::Instance()[Rtsp::kPort]; + uint16_t rtspsPort = mINI::Instance()[Rtsp::kSSLPort]; + uint16_t rtmpPort = mINI::Instance()[Rtmp::kPort]; + uint16_t httpPort = mINI::Instance()[Http::kPort]; + uint16_t httpsPort = mINI::Instance()[Http::kSSLPort]; + + /** + * 设置poller线程数,该函数必须在使用ZLToolKit网络相关对象之前调用才能生效 + */ + EventPollerPool::setPoolSize(threads); + + //简单的telnet服务器,可用于服务器调试,但是不能使用23端口,否则telnet上了莫名其妙的现象 + //测试方法:telnet 127.0.0.1 9000 + TcpServer::Ptr shellSrv(new TcpServer()); + TcpServer::Ptr rtspSrv(new TcpServer()); + TcpServer::Ptr rtmpSrv(new TcpServer()); + TcpServer::Ptr httpSrv(new TcpServer()); + + shellSrv->start(shellPort); + rtspSrv->start(rtspPort);//默认554 + rtmpSrv->start(rtmpPort);//默认1935 + //http服务器,支持websocket + httpSrv->start(httpPort);//默认80 + + //如果支持ssl,还可以开启https服务器 + TcpServer::Ptr httpsSrv(new TcpServer()); + //https服务器,支持websocket + httpsSrv->start(httpsPort);//默认443 + + //支持ssl加密的rtsp服务器,可用于诸如亚马逊echo show这样的设备访问 + TcpServer::Ptr rtspSSLSrv(new TcpServer()); + rtspSSLSrv->start(rtspsPort);//默认322 + + installWebApi(); + InfoL << "已启动http api 接口"; + installWebHook(); + InfoL << "已启动http hook 接口"; + + if (!bDaemon) { + //交互式shell输入 + listen_shell_input(); + } + + //设置退出信号处理函数 + static semaphore sem; + signal(SIGINT, [](int) { + InfoL << "SIGINT:exit"; + sem.post(); + });// 设置退出信号 + signal(SIGHUP, [](int) { mediakit::loadIniConfig(); }); + sem.wait(); } - - //启动异步日志线程 - Logger::Instance().setWriter(std::make_shared()); - //加载配置文件,如果配置文件不存在就创建一个 - loadIniConfig(ini_file.data()); - - //加载证书,证书包含公钥和私钥 - SSL_Initor::Instance().loadCertificate(ssl_file.data()); - //信任某个自签名证书 - SSL_Initor::Instance().trustCertificate(ssl_file.data()); - //不忽略无效证书证书(例如自签名或过期证书) - SSL_Initor::Instance().ignoreInvalidCertificate(false); - - uint16_t shellPort = mINI::Instance()[Shell::kPort]; - uint16_t rtspPort = mINI::Instance()[Rtsp::kPort]; - uint16_t rtspsPort = mINI::Instance()[Rtsp::kSSLPort]; - uint16_t rtmpPort = mINI::Instance()[Rtmp::kPort]; - uint16_t httpPort = mINI::Instance()[Http::kPort]; - uint16_t httpsPort = mINI::Instance()[Http::kSSLPort]; - - /** - * 设置poller线程数,该函数必须在使用ZLToolKit网络相关对象之前调用才能生效 - */ - EventPollerPool::setPoolSize(threads); - - //简单的telnet服务器,可用于服务器调试,但是不能使用23端口,否则telnet上了莫名其妙的现象 - //测试方法:telnet 127.0.0.1 9000 - TcpServer::Ptr shellSrv(new TcpServer()); - TcpServer::Ptr rtspSrv(new TcpServer()); - TcpServer::Ptr rtmpSrv(new TcpServer()); - TcpServer::Ptr httpSrv(new TcpServer()); - - shellSrv->start(shellPort); - rtspSrv->start(rtspPort);//默认554 - rtmpSrv->start(rtmpPort);//默认1935 - //http服务器,支持websocket - httpSrv->start(httpPort);//默认80 - - //如果支持ssl,还可以开启https服务器 - TcpServer::Ptr httpsSrv(new TcpServer()); - //https服务器,支持websocket - httpsSrv->start(httpsPort);//默认443 - - //支持ssl加密的rtsp服务器,可用于诸如亚马逊echo show这样的设备访问 - TcpServer::Ptr rtspSSLSrv(new TcpServer()); - rtspSSLSrv->start(rtspsPort);//默认322 - - installWebApi(); - InfoL << "已启动http api 接口"; - installWebHook(); - InfoL << "已启动http hook 接口"; - - if(!bDaemon) { - //交互式shell输入 - listen_shell_input(); - } - - //设置退出信号处理函数 - static semaphore sem; - signal(SIGINT, [](int) { InfoL << "SIGINT:exit"; sem.post(); });// 设置退出信号 - signal(SIGHUP, [](int) { mediakit::loadIniConfig(); }); - sem.wait(); return 0; } diff --git a/src/Common/config.h b/src/Common/config.h index 7f49715c..d96cc0ec 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -86,7 +86,7 @@ extern const char kBroadcastOnGetRtspRealm[]; //请求认证用户密码事件,user_name为用户名,must_no_encrypt如果为true,则必须提供明文密码(因为此时是base64认证方式),否则会导致认证失败 //获取到密码后请调用invoker并输入对应类型的密码和密码类型,invoker执行时会匹配密码 extern const char kBroadcastOnRtspAuth[]; -#define BroadcastOnRtspAuthArgs const MediaInfo &args,const string &user_name,const bool &must_no_encrypt,const RtspSession::onAuth &invoker,TcpSession &sender +#define BroadcastOnRtspAuthArgs const MediaInfo &args,const string &realm,const string &user_name,const bool &must_no_encrypt,const RtspSession::onAuth &invoker,TcpSession &sender //鉴权结果回调对象 //如果errMessage为空则代表鉴权成功 diff --git a/src/Http/HttpClient.h b/src/Http/HttpClient.h index 9d2b429e..ffd6b1ac 100644 --- a/src/Http/HttpClient.h +++ b/src/Http/HttpClient.h @@ -37,13 +37,14 @@ #include "HttpRequestSplitter.h" #include "HttpCookie.h" #include "HttpChunkedSplitter.h" +#include "strCoding.h" using namespace std; using namespace toolkit; namespace mediakit { -class HttpArgs : public StrCaseMap { +class HttpArgs : public map { public: HttpArgs(){} virtual ~HttpArgs(){} @@ -52,7 +53,7 @@ public: for(auto &pr : *this){ ret.append(pr.first); ret.append("="); - ret.append(pr.second); + ret.append(strCoding::UrlUTF8Encode(pr.second)); ret.append("&"); } if(ret.size()){ @@ -96,7 +97,8 @@ private: class HttpMultiFormBody : public HttpBody { public: typedef std::shared_ptr Ptr; - HttpMultiFormBody(const StrCaseMap &args,const string &filePath,const string &boundary,uint32_t sliceSize = 4 * 1024){ + template + HttpMultiFormBody(const MapType &args,const string &filePath,const string &boundary,uint32_t sliceSize = 4 * 1024){ _fp = fopen(filePath.data(),"rb"); if(!_fp){ throw std::invalid_argument(StrPrinter << "打开文件失败:" << filePath << " " << get_uv_errmsg()); @@ -156,7 +158,8 @@ public: } public: - static string multiFormBodyPrefix(const StrCaseMap &args,const string &boundary,const string &fileName){ + template + static string multiFormBodyPrefix(const MapType &args,const string &boundary,const string &fileName){ string MPboundary = string("--") + boundary; _StrPrinter body; for(auto &pr : args){ diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index d0c57785..a3f1e3de 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -422,7 +422,7 @@ void RtspSession::onAuthBasic(const weak_ptr &weakSelf,const string } //此时必须提供明文密码 - if(!NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastOnRtspAuth,strongSelf->_mediaInfo,user, true,invoker,*strongSelf)){ + if(!NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastOnRtspAuth,strongSelf->_mediaInfo,realm,user, true,invoker,*strongSelf)){ //表明该流需要认证却没监听请求密码事件,这一般是大意的程序所为,警告之 WarnL << "请监听kBroadcastOnRtspAuth事件!"; //但是我们还是忽略认证以便完成播放 @@ -503,7 +503,7 @@ void RtspSession::onAuthDigest(const weak_ptr &weakSelf,const strin }; //此时可以提供明文或md5加密的密码 - if(!NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastOnRtspAuth,strongSelf->_mediaInfo,username, false,invoker,*strongSelf)){ + if(!NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastOnRtspAuth,strongSelf->_mediaInfo,realm,username, false,invoker,*strongSelf)){ //表明该流需要认证却没监听请求密码事件,这一般是大意的程序所为,警告之 WarnL << "请监听kBroadcastOnRtspAuth事件!"; //但是我们还是忽略认证以便完成播放