From d593267f61bbc6c5b0acfd55de21e73d1af85852 Mon Sep 17 00:00:00 2001 From: alexliyu7352 Date: Thu, 23 Nov 2023 11:09:01 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0http=E4=BB=A3=E7=90=86?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=20(#2988)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持通过http代理拉流, 避免内外网复杂网络环境时需要专门配置防火墙,现在只需要配置一台代理内网的服务器. --------- Co-authored-by: xia-chu <771730766@qq.com> --- src/Common/Parser.cpp | 21 +++++++++++++- src/Common/Parser.h | 2 ++ src/Common/config.cpp | 1 + src/Common/config.h | 2 ++ src/Http/HlsPlayer.cpp | 2 ++ src/Http/HttpClient.cpp | 59 ++++++++++++++++++++++++++++++-------- src/Http/HttpClient.h | 19 +++++++++++- src/Http/HttpClientImp.cpp | 23 +++++++++++++-- src/Http/HttpClientImp.h | 1 + src/Http/TsPlayer.cpp | 1 + src/Rtmp/FlvPlayer.cpp | 1 + 11 files changed, 115 insertions(+), 17 deletions(-) diff --git a/src/Common/Parser.cpp b/src/Common/Parser.cpp index 9c073f2c..15636c33 100644 --- a/src/Common/Parser.cpp +++ b/src/Common/Parser.cpp @@ -11,7 +11,7 @@ #include #include "Parser.h" #include "strCoding.h" -#include "macros.h" +#include "Util/base64.h" #include "Network/sockutil.h" #include "Common/macros.h" @@ -325,6 +325,25 @@ void splitUrl(const std::string &url, std::string &host, uint16_t &port) { host = url.substr(0, pos); checkHost(host); } + +void parseProxyUrl(const std::string &proxy_url, std::string &proxy_host, uint16_t &proxy_port, std::string &proxy_auth) { + // 判断是否包含http://, 如果是则去掉 + std::string host; + auto pos = proxy_url.find("://"); + if (pos != string::npos) { + host = proxy_url.substr(pos + 3); + } else { + host = proxy_url; + } + // 判断是否包含用户名和密码 + pos = host.rfind('@'); + if (pos != string::npos) { + proxy_auth = encodeBase64(host.substr(0, pos)); + host = host.substr(pos + 1, host.size()); + } + splitUrl(host, proxy_host, proxy_port); +} + #if 0 //测试代码 static onceToken token([](){ diff --git a/src/Common/Parser.h b/src/Common/Parser.h index 3741f4bc..a2165ce1 100644 --- a/src/Common/Parser.h +++ b/src/Common/Parser.h @@ -21,6 +21,8 @@ namespace mediakit { std::string findSubString(const char *buf, const char *start, const char *end, size_t buf_size = 0); // 把url解析为主机地址和端口号,兼容ipv4/ipv6/dns void splitUrl(const std::string &url, std::string &host, uint16_t &port); +// 解析proxy url,仅支持http +void parseProxyUrl(const std::string &proxy_url, std::string &proxy_host, uint16_t &proxy_port, std::string &proxy_auth); struct StrCaseCompare { bool operator()(const std::string &__x, const std::string &__y) const { return strcasecmp(__x.data(), __y.data()) < 0; } diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 603c434c..1009f1e6 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -353,6 +353,7 @@ const string kBeatIntervalMS = "beat_interval_ms"; const string kBenchmarkMode = "benchmark_mode"; const string kWaitTrackReady = "wait_track_ready"; const string kPlayTrack = "play_track"; +const string kProxyUrl = "proxy_url"; } // namespace Client } // namespace mediakit diff --git a/src/Common/config.h b/src/Common/config.h index 25f36271..dcbc5514 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -403,6 +403,8 @@ extern const std::string kWaitTrackReady; // rtsp播放指定track,可选项有0(不指定,默认)、1(视频)、2(音频) // 设置方法:player[Client::kPlayTrack] = 0/1/2; extern const std::string kPlayTrack; +//设置代理url,目前只支持http协议 +extern const std::string kProxyUrl; } // namespace Client } // namespace mediakit diff --git a/src/Http/HlsPlayer.cpp b/src/Http/HlsPlayer.cpp index c6509c0c..3e27a012 100644 --- a/src/Http/HlsPlayer.cpp +++ b/src/Http/HlsPlayer.cpp @@ -22,6 +22,7 @@ HlsPlayer::HlsPlayer(const EventPoller::Ptr &poller) { void HlsPlayer::play(const string &url) { _play_result = false; _play_url = url; + setProxyUrl((*this)[Client::kProxyUrl]); fetchIndexFile(); } @@ -88,6 +89,7 @@ void HlsPlayer::fetchSegment() { weak_ptr weak_self = static_pointer_cast(shared_from_this()); if (!_http_ts_player) { _http_ts_player = std::make_shared(getPoller()); + _http_ts_player->setProxyUrl((*this)[Client::kProxyUrl]); _http_ts_player->setOnCreateSocket([weak_self](const EventPoller::Ptr &poller) { auto strong_self = weak_self.lock(); if (strong_self) { diff --git a/src/Http/HttpClient.cpp b/src/Http/HttpClient.cpp index dd1546db..793e9ca1 100644 --- a/src/Http/HttpClient.cpp +++ b/src/Http/HttpClient.cpp @@ -78,12 +78,15 @@ void HttpClient::sendRequest(const string &url) { printer.pop_back(); _header.emplace("Cookie", printer); } - - if (!alive() || host_changed) { - startConnect(host, port, _wait_header_ms / 1000.0f); + if (isUsedProxy()) { + startConnect(_proxy_host, _proxy_port, _wait_header_ms / 1000.0f); } else { - SockException ex; - onConnect_l(ex); + if (!alive() || host_changed) { + startConnect(host, port, _wait_header_ms / 1000.0f); + } else { + SockException ex; + onConnect_l(ex); + } } } @@ -158,15 +161,23 @@ void HttpClient::onConnect_l(const SockException &ex) { onResponseCompleted_l(ex); return; } - _StrPrinter printer; - printer << _method + " " << _path + " HTTP/1.1\r\n"; - for (auto &pr : _header) { - printer << pr.first + ": "; - printer << pr.second + "\r\n"; + //不使用代理或者代理服务器已经连接成功 + if (_proxy_connected || !isUsedProxy()) { + printer << _method + " " << _path + " HTTP/1.1\r\n"; + for (auto &pr : _header) { + printer << pr.first + ": "; + printer << pr.second + "\r\n"; + } + _header.clear(); + _path.clear(); + } else { + printer << "CONNECT " << _last_host << " HTTP/1.1\r\n"; + printer << "Proxy-Connection: keep-alive\r\n"; + if (!_proxy_auth.empty()) { + printer << "Proxy-Authorization: Basic " << _proxy_auth << "\r\n"; + } } - _header.clear(); - _path.clear(); SockSender::send(printer << "\r\n"); onFlush(); } @@ -401,4 +412,28 @@ void HttpClient::setCompleteTimeout(size_t timeout_ms) { _wait_complete_ms = timeout_ms; } +bool HttpClient::isUsedProxy() const { + return _used_proxy; +} + +bool HttpClient::isProxyConnected() const { + return _proxy_connected; +} + +void HttpClient::setProxyUrl(string proxy_url) { + _proxy_url = std::move(proxy_url); + if (!_proxy_url.empty()) { + parseProxyUrl(_proxy_url, _proxy_host, _proxy_port, _proxy_auth); + _used_proxy = true; + } else { + _used_proxy = false; + } +} + +bool HttpClient::checkProxyConnected(const char *data, size_t len) { + auto ret = strstr(data, "HTTP/1.1 200 Connection established"); + _proxy_connected = ret != nullptr; + return _proxy_connected; +} + } /* namespace mediakit */ diff --git a/src/Http/HttpClient.h b/src/Http/HttpClient.h index 497f4a1d..6a00c63a 100644 --- a/src/Http/HttpClient.h +++ b/src/Http/HttpClient.h @@ -141,6 +141,11 @@ public: */ void setCompleteTimeout(size_t timeout_ms); + /** + * 设置http代理url + */ + void setProxyUrl(std::string proxy_url); + protected: /** * 收到http回复头 @@ -181,11 +186,16 @@ protected: void onFlush() override; void onManager() override; + void clearResponse(); + + bool checkProxyConnected(const char *data, size_t len); + bool isUsedProxy() const; + bool isProxyConnected() const; + private: void onResponseCompleted_l(const toolkit::SockException &ex); void onConnect_l(const toolkit::SockException &ex); void checkCookie(HttpHeader &headers); - void clearResponse(); private: //for http response @@ -215,6 +225,13 @@ private: toolkit::Ticker _wait_header; toolkit::Ticker _wait_body; toolkit::Ticker _wait_complete; + + bool _used_proxy = false; + bool _proxy_connected = false; + uint16_t _proxy_port; + std::string _proxy_url; + std::string _proxy_host; + std::string _proxy_auth; }; } /* namespace mediakit */ diff --git a/src/Http/HttpClientImp.cpp b/src/Http/HttpClientImp.cpp index 7b36b46c..26babbb0 100644 --- a/src/Http/HttpClientImp.cpp +++ b/src/Http/HttpClientImp.cpp @@ -15,13 +15,30 @@ using namespace toolkit; namespace mediakit { void HttpClientImp::onConnect(const SockException &ex) { - if (!isHttps()) { - //https 302跳转 http时,需要关闭ssl + if (isUsedProxy() && !isProxyConnected()) { + // 连接代理服务器 setDoNotUseSSL(); HttpClient::onConnect(ex); } else { - TcpClientWithSSL::onConnect(ex); + if (!isHttps()) { + // https 302跳转 http时,需要关闭ssl + setDoNotUseSSL(); + HttpClient::onConnect(ex); + } else { + TcpClientWithSSL::onConnect(ex); + } } } +ssize_t HttpClientImp::onRecvHeader(const char *data, size_t len) { + if (isUsedProxy() && !isProxyConnected()) { + if (checkProxyConnected(data, len)) { + clearResponse(); + onConnect(SockException(Err_success, "proxy connected")); + return 0; + } + } + return HttpClient::onRecvHeader(data, len); +} + } /* namespace mediakit */ diff --git a/src/Http/HttpClientImp.h b/src/Http/HttpClientImp.h index 491b594f..2d32f065 100644 --- a/src/Http/HttpClientImp.h +++ b/src/Http/HttpClientImp.h @@ -24,6 +24,7 @@ public: protected: void onConnect(const toolkit::SockException &ex) override; + ssize_t onRecvHeader(const char *data, size_t len) override; }; } /* namespace mediakit */ diff --git a/src/Http/TsPlayer.cpp b/src/Http/TsPlayer.cpp index 20737bc5..66a7a687 100644 --- a/src/Http/TsPlayer.cpp +++ b/src/Http/TsPlayer.cpp @@ -21,6 +21,7 @@ void TsPlayer::play(const string &url) { TraceL << "play http-ts: " << url; _play_result = false; _benchmark_mode = (*this)[Client::kBenchmarkMode].as(); + setProxyUrl((*this)[Client::kProxyUrl]); setHeaderTimeout((*this)[Client::kTimeoutMS].as()); setBodyTimeout((*this)[Client::kMediaTimeoutMS].as()); setMethod("GET"); diff --git a/src/Rtmp/FlvPlayer.cpp b/src/Rtmp/FlvPlayer.cpp index 0095eb04..3724a219 100644 --- a/src/Rtmp/FlvPlayer.cpp +++ b/src/Rtmp/FlvPlayer.cpp @@ -22,6 +22,7 @@ FlvPlayer::FlvPlayer(const EventPoller::Ptr &poller) { void FlvPlayer::play(const string &url) { TraceL << "play http-flv: " << url; _play_result = false; + setProxyUrl((*this)[Client::kProxyUrl]); setHeaderTimeout((*this)[Client::kTimeoutMS].as()); setBodyTimeout((*this)[Client::kMediaTimeoutMS].as()); setMethod("GET");