diff --git a/src/Http/.DS_Store b/src/Http/.DS_Store new file mode 100644 index 00000000..6da22b4c Binary files /dev/null and b/src/Http/.DS_Store differ diff --git a/src/Http/HttpClient.cpp b/src/Http/HttpClient.cpp new file mode 100644 index 00000000..3b424be9 --- /dev/null +++ b/src/Http/HttpClient.cpp @@ -0,0 +1,152 @@ +// +// HttpClient.cpp +// ZLMediaKit +// +// Created by xzl on 2017/5/4. +// + +#include "HttpClient.h" +#include "Rtsp/Rtsp.h" + +namespace ZL { +namespace Http { + + +HttpClient::HttpClient(){ +} +HttpClient::~HttpClient(){ +} +void HttpClient::sendRequest(const string &strUrl){ + auto protocol = FindField(strUrl.data(), NULL , "://"); + uint16_t defaultPort; + bool isHttps; + if (strcasecmp(protocol.data(), "http") == 0) { + defaultPort = 80; + isHttps = false; + }else if(strcasecmp(protocol.data(), "https") ==0 ){ + defaultPort = 443; + isHttps = true; + }else{ + auto strErr = StrPrinter << "非法的协议:" << protocol << endl; + throw std::invalid_argument(strErr); + } + + auto host = FindField(strUrl.data(), "://", "/"); + if (host.empty()) { + host = FindField(strUrl.data(), "://", NULL); + } + _path = FindField(strUrl.data(), host.data(), NULL); + if (_path.empty()) { + _path = "/"; + } + auto port = atoi(FindField(host.data(), ":", NULL).data()); + if (port <= 0) { + //默认端口 + port = defaultPort; + } else { + //服务器域名 + host = FindField(host.data(), NULL, ":"); + } + _header.emplace(string("Host"),host); + _header.emplace(string("Tools"),"ZLMediaKit"); + _header.emplace(string("Connection"),"keep-alive"); + _header.emplace(string("Accept"),"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); + _header.emplace(string("Accept-Encoding"),"gzip, deflate, sdch, br"); + _header.emplace(string("Accept-Language"),"zh-CN,zh;q=0.8"); + _header.emplace(string("User-Agent"),"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"); + + if(!_body.empty()){ + _header.emplace(string("Content-Length"),std::to_string(_body.size())); + _header.emplace(string("Content-Type"),"application/x-www-form-urlencoded; charset=UTF-8"); + } + + bool bChanged = (_lastHost != host + ":" + to_string(port)) || (_isHttps != isHttps); + _lastHost = host + ":" + to_string(port); + _isHttps = isHttps; + + if(!alive() || bChanged){ + InfoL << "reconnet:" << _lastHost; + startConnect(host, port); + }else{ + SockException ex; + onConnect(ex); + } +} + + +void HttpClient::onConnect(const SockException &ex) { + if(ex){ + onDisconnect(ex); + return; + } + _recvedBodySize = -1; + _recvedResponse.clear(); + send(_method + " "); + send(_path + " HTTP/1.1\r\n"); + for (auto &pr : _header) { + send(pr.first + ": "); + send(pr.second + "\r\n"); + } + send("\r\n"); + if (!_body.empty()) { + send(_body); + } +} +void HttpClient::onRecv(const Socket::Buffer::Ptr &pBuf) { + onRecvBytes(pBuf->data(),pBuf->size()); +} + +void HttpClient::onErr(const SockException &ex) { + onDisconnect(ex); +} + +void HttpClient::onRecvBytes(const char* data, int size) { + if(_recvedBodySize == -1){ + //还没有收到http body,这只是http头 + auto lastLen = _recvedResponse.size(); + _recvedResponse.append(data,size); + auto pos = _recvedResponse.find("\r\n\r\n",lastLen); + if(pos == string::npos){ + //http 头还未收到 + return; + } + _parser.Parse(_recvedResponse.data()); + onResponseHeader(_parser.Url(),_parser.getValues()); + + _totalBodySize = atoll(((HttpHeader &)_parser.getValues())["Content-Length"].data()); + _recvedBodySize = _recvedResponse.size() - pos - 4; + if(_totalBodySize < _recvedBodySize){ + //http body 比声明的大 这个不可能的 + FatalL; + shutdown(); + return; + } + if (_recvedBodySize) { + //_recvedResponse里面包含body负载 + onResponseBody(_recvedResponse.data() + _recvedResponse.size() - _recvedBodySize, _recvedBodySize,_recvedBodySize,_totalBodySize); + } + + if(_recvedBodySize >= _totalBodySize){ + onResponseCompleted(); + } + _recvedResponse.clear(); + return; + } + //http body + if(_recvedBodySize < _totalBodySize){ + _recvedBodySize += size; + onResponseBody(data,size,_recvedBodySize,_totalBodySize); + if(_recvedBodySize >= _totalBodySize){ + onResponseCompleted(); + } + return; + } + //http body 比声明的大 这个不可能的 + FatalL; + shutdown(); +} + + +} /* namespace Http */ +} /* namespace ZL */ + diff --git a/src/Http/HttpClient.h b/src/Http/HttpClient.h new file mode 100644 index 00000000..78535b4d --- /dev/null +++ b/src/Http/HttpClient.h @@ -0,0 +1,130 @@ +// +// HttpClient.h +// ZLMediaKit +// +// Created by xzl on 2017/5/4. +// + +#ifndef Http_HttpClient_h +#define Http_HttpClient_h + +#include +#include +#include +#include "Rtsp/Rtsp.h" +#include "Util/util.h" +#include "Network/TcpClient.h" + +using namespace std; +using namespace ZL::Util; +using namespace ZL::Network; + +namespace ZL { +namespace Http { + +class HttpArgs : public map +{ +public: + HttpArgs(){} + virtual ~HttpArgs(){} + string make() const { + string ret; + for(auto &pr : *this){ + ret.append(pr.first); + ret.append("="); + ret.append(pr.second); + ret.append("&"); + } + if(ret.size()){ + ret.pop_back(); + } + return ret; + } +}; + +class HttpClient : public TcpClient +{ +public: + typedef StrCaseMap HttpHeader; + typedef std::shared_ptr Ptr; + HttpClient(); + virtual ~HttpClient(); + virtual void sendRequest(const string &url); + void clear(){ + _header.clear(); + _body.clear(); + _method.clear(); + _path.clear(); + _recvedResponse.clear(); + _parser.Clear(); + } + void setMethod(const string &method){ + _method = method; + } + void setHeader(const HttpHeader &header){ + _header = _header; + } + void addHeader(const string &key,const string &val){ + _header.emplace(key,val); + } + void setBody(const string &body){ + _body = body; + } + const string &responseStatus(){ + return _parser.Url(); + } + const HttpHeader &responseHeader(){ + return _parser.getValues(); + } +protected: + bool _isHttps; + + virtual void onResponseHeader(const string &status,const HttpHeader &headers){ + DebugL << status; + }; + virtual void onResponseBody(const char *buf,size_t size,size_t recvedSize,size_t totalSize){ + DebugL << size << " " << recvedSize << " " << totalSize; + }; + virtual void onResponseCompleted(){ + DebugL; + } + virtual void onRecvBytes(const char *data,int size); + virtual void onDisconnect(const SockException &ex){} +private: + virtual void onConnect(const SockException &ex) override; + virtual void onRecv(const Socket::Buffer::Ptr &pBuf) override; + virtual void onErr(const SockException &ex) override; + + //send + HttpHeader _header; + string _body; + string _method; + string _path; + + //recv + string _recvedResponse; + size_t _recvedBodySize; + size_t _totalBodySize; + Parser _parser; + + string _lastHost; +}; + + + + + + + + +} /* namespace Http */ +} /* namespace ZL */ + + + + + + + + +#endif /* Http_HttpClient_h */ diff --git a/src/Http/HttpClientImp.cpp b/src/Http/HttpClientImp.cpp new file mode 100644 index 00000000..33625f50 --- /dev/null +++ b/src/Http/HttpClientImp.cpp @@ -0,0 +1,72 @@ +/* + * HttpClientImp.cpp + * + * Created on: 2017年5月4日 + * Author: xzl + */ + +#include + +namespace ZL { +namespace Http { + +HttpClientImp::HttpClientImp() { + // TODO Auto-generated constructor stub + +} + +HttpClientImp::~HttpClientImp() { +} + +void HttpClientImp::sendRequest(const string& url) { + HttpClient::sendRequest(url); + if(_isHttps){ +#ifndef ENABLE_OPENSSL + shutdown(); + throw std::invalid_argument("不支持HTTPS协议"); +#else + _sslBox.reset(new SSL_Box(false)); + _sslBox->setOnDecData([this](const char *data, uint32_t len){ + HttpClient::onRecvBytes(data,len); + }); + _sslBox->setOnEncData([this](const char *data, uint32_t len){ + HttpClient::send(data,len); + }); +#endif //ENABLE_OPENSSL + + }else{ +#ifdef ENABLE_OPENSSL + _sslBox.reset(); +#endif //ENABLE_OPENSSL + } +} + +#ifdef ENABLE_OPENSSL +void HttpClientImp::onRecvBytes(const char* data, int size) { + if(_sslBox){ + _sslBox->onRecv(data,size); + }else{ + HttpClient::onRecvBytes(data,size); + } +} + +int HttpClientImp::send(const string& str) { + if(_sslBox){ + _sslBox->onSend(str.data(),str.size()); + return str.size(); + } + return HttpClient::send(str); +} + +int HttpClientImp::send(const char* str, int len) { + if(_sslBox){ + _sslBox->onSend(str,len); + return len; + } + return HttpClient::send(str,len); + +} +#endif //ENABLE_OPENSSL + +} /* namespace Http */ +} /* namespace ZL */ diff --git a/src/Http/HttpClientImp.h b/src/Http/HttpClientImp.h new file mode 100644 index 00000000..22f54696 --- /dev/null +++ b/src/Http/HttpClientImp.h @@ -0,0 +1,38 @@ +/* + * HttpClientImp.h + * + * Created on: 2017年5月4日 + * Author: xzl + */ + +#ifndef SRC_HTTP_HTTPCLIENTIMP_H_ +#define SRC_HTTP_HTTPCLIENTIMP_H_ + +#include "HttpClient.h" +#ifdef ENABLE_OPENSSL +#include "Util/SSLBox.h" +using namespace ZL::Util; +#endif //ENABLE_OPENSSL + +namespace ZL { +namespace Http { + +class HttpClientImp: public HttpClient { +public: + typedef std::shared_ptr Ptr; + HttpClientImp(); + virtual ~HttpClientImp(); + virtual void sendRequest(const string &url) override; +private: +#ifdef ENABLE_OPENSSL + virtual void onRecvBytes(const char *data,int size) override; + virtual int send(const string &str) override; + virtual int send(const char *str, int len) override; + std::shared_ptr _sslBox; +#endif //ENABLE_OPENSSL +}; + +} /* namespace Http */ +} /* namespace ZL */ + +#endif /* SRC_HTTP_HTTPCLIENTIMP_H_ */ diff --git a/src/Http/HttpDownloader.cpp b/src/Http/HttpDownloader.cpp new file mode 100644 index 00000000..1d352080 --- /dev/null +++ b/src/Http/HttpDownloader.cpp @@ -0,0 +1,87 @@ +/* + * HttpDownloader.cpp + * + * Created on: 2017年5月5日 + * Author: xzl + */ + +#include "HttpDownloader.h" +#include "Util/MD5.h" +#include "Util/File.h" + +using namespace ZL::Util; + +namespace ZL { +namespace Http { + +HttpDownloader::HttpDownloader() { + +} + +HttpDownloader::~HttpDownloader() { + closeFile(); +} + +void HttpDownloader::startDownload(const string& url, const string& filePath,bool bAppend) { + _filePath = filePath; + if(_filePath.empty()){ + _filePath = exeDir() + "/HttpDownloader/" + MD5(url).hexdigest(); + } + _saveFile = File::createfile_file(_filePath.data(),bAppend ? "ab" : "wb"); + if(!_saveFile){ + auto strErr = StrPrinter << "打开文件失败:" << filePath << endl; + throw std::runtime_error(strErr); + } + if(bAppend){ + auto currentLen = ftell(_saveFile); + addHeader("Range", StrPrinter << "bytes=" << currentLen << "-" << endl); + } + setMethod("GET"); + sendRequest(url); +} + +void HttpDownloader::onResponseHeader(const string& status,const HttpHeader& headers) { + if(status != "200" && status != "206"){ + //失败 + shutdown(); + closeFile(); + File::delete_file(_filePath.data()); + if(_onResult){ + auto errMsg = StrPrinter << "Http Status:" << status << endl; + _onResult(Err_other,errMsg.data(),_filePath.data()); + _onResult = nullptr; + } + } +} + +void HttpDownloader::onResponseBody(const char* buf, size_t size, size_t recvedSize, size_t totalSize) { + if(_saveFile){ + fwrite(buf,size,1,_saveFile); + } +} + +void HttpDownloader::onResponseCompleted() { + closeFile(); + if(_onResult){ + _onResult(Err_success,"success",_filePath.data()); + _onResult = nullptr; + } +} + +void HttpDownloader::onDisconnect(const SockException &ex) { + closeFile(); + if(_onResult){ + _onResult(ex.getErrCode(),ex.what(),_filePath.data()); + _onResult = nullptr; + } +} + +void HttpDownloader::closeFile() { + if(_saveFile){ + fclose(_saveFile); + _saveFile = nullptr; + } +} + +} /* namespace Http */ +} /* namespace ZL */ diff --git a/src/Http/HttpDownloader.h b/src/Http/HttpDownloader.h new file mode 100644 index 00000000..93a04c19 --- /dev/null +++ b/src/Http/HttpDownloader.h @@ -0,0 +1,46 @@ +/* + * HttpDownloader.h + * + * Created on: 2017年5月5日 + * Author: xzl + */ + +#ifndef SRC_HTTP_HTTPDOWNLOADER_H_ +#define SRC_HTTP_HTTPDOWNLOADER_H_ + +#include "HttpClientImp.h" + +namespace ZL { +namespace Http { + +class HttpDownloader: public HttpClientImp { +public: + typedef std::shared_ptr Ptr; + typedef std::function onDownloadResult; + HttpDownloader(); + virtual ~HttpDownloader(); + //开始下载文件,默认断点续传方式下载 + void startDownload(const string &url,const string &filePath = "",bool bAppend = true); + void startDownload(const string &url,const onDownloadResult &cb){ + setOnResult(cb); + startDownload(url); + } + void setOnResult(const onDownloadResult &cb){ + _onResult = cb; + } +private: + void onResponseHeader(const string &status,const HttpHeader &headers) override; + void onResponseBody(const char *buf,size_t size,size_t recvedSize,size_t totalSize) override; + void onResponseCompleted() override; + void onDisconnect(const SockException &ex) override; + void closeFile(); + + FILE *_saveFile = nullptr; + string _filePath; + onDownloadResult _onResult; +}; + +} /* namespace Http */ +} /* namespace ZL */ + +#endif /* SRC_HTTP_HTTPDOWNLOADER_H_ */ diff --git a/src/Http/HttpRequester.cpp b/src/Http/HttpRequester.cpp new file mode 100644 index 00000000..764049fc --- /dev/null +++ b/src/Http/HttpRequester.cpp @@ -0,0 +1,50 @@ +// +// HttpRequester.cpp +// ZLMediaKit +// +// Created by xzl on 2017/5/5. +// + +#include "HttpRequester.h" + +namespace ZL{ +namespace Http{ + +HttpRequester::HttpRequester(){ + +} +HttpRequester::~HttpRequester(){ + +} + +void HttpRequester::onResponseHeader(const string &status,const HttpHeader &headers) { + _strRecvBody.clear(); +} + +void HttpRequester::onResponseBody(const char *buf,size_t size,size_t recvedSize,size_t totalSize) { + _strRecvBody.append(buf,size); +} + +void HttpRequester::onResponseCompleted() { + if(_onResult){ + _onResult(SockException(),responseStatus(),responseHeader(),_strRecvBody); + _onResult = nullptr; + } +} + +void HttpRequester::onDisconnect(const SockException &ex){ + if(_onResult){ + _onResult(ex,responseStatus(),responseHeader(),_strRecvBody); + _onResult = nullptr; + } +} + +void HttpRequester::startRequester(const string &url,const HttpRequesterResult &onResult){ + _onResult = onResult; + sendRequest(url); + +} + + +}//namespace Http +}//namespace ZL diff --git a/src/Http/HttpRequester.h b/src/Http/HttpRequester.h new file mode 100644 index 00000000..76370e79 --- /dev/null +++ b/src/Http/HttpRequester.h @@ -0,0 +1,40 @@ +// +// HttpRequester_h +// ZLMediaKit +// +// Created by xzl on 2017/5/5. +// + +#ifndef Htt_HttpRequester_h +#define Htt_HttpRequester_h + +#include "HttpClientImp.h" + +namespace ZL{ +namespace Http{ + +class HttpRequester : public HttpClientImp +{ +public: + typedef std::shared_ptr Ptr; + typedef std::function HttpRequesterResult; + HttpRequester(); + virtual ~HttpRequester(); + + void startRequester(const string &url,const HttpRequesterResult &onResult); +private: + void onResponseHeader(const string &status,const HttpHeader &headers) override; + void onResponseBody(const char *buf,size_t size,size_t recvedSize,size_t totalSize) override; + void onResponseCompleted() override; + void onDisconnect(const SockException &ex) override; + + string _strRecvBody; + HttpRequesterResult _onResult; + + +}; + +}//namespace Http +}//namespace ZL + +#endif /* Htt_HttpRequester_h */ diff --git a/src/Http/HttpSession.h b/src/Http/HttpSession.h index 1370d6ba..c7ec0c39 100644 --- a/src/Http/HttpSession.h +++ b/src/Http/HttpSession.h @@ -15,7 +15,6 @@ using namespace std; using namespace ZL::Network; -using namespace ZL::Network; namespace ZL { namespace Http { @@ -23,7 +22,7 @@ namespace Http { class HttpSession: public TcpLimitedSession { public: - typedef map KeyValue; + typedef StrCaseMap KeyValue; typedef std::function HttpResponseInvoker;