add http client

This commit is contained in:
xiongziliang 2017-05-05 18:02:54 +08:00
parent 5901ff197d
commit f1a41022cb
10 changed files with 616 additions and 2 deletions

BIN
src/Http/.DS_Store vendored Normal file

Binary file not shown.

152
src/Http/HttpClient.cpp Normal file
View File

@ -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 */

130
src/Http/HttpClient.h Normal file
View File

@ -0,0 +1,130 @@
//
// HttpClient.h
// ZLMediaKit
//
// Created by xzl on 2017/5/4.
//
#ifndef Http_HttpClient_h
#define Http_HttpClient_h
#include <string.h>
#include <functional>
#include <memory>
#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<string,string>
{
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<HttpClient> 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 */

View File

@ -0,0 +1,72 @@
/*
* HttpClientImp.cpp
*
* Created on: 201754
* Author: xzl
*/
#include <Http/HttpClientImp.h>
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 */

38
src/Http/HttpClientImp.h Normal file
View File

@ -0,0 +1,38 @@
/*
* HttpClientImp.h
*
* Created on: 201754
* 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<HttpClientImp> 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<SSL_Box> _sslBox;
#endif //ENABLE_OPENSSL
};
} /* namespace Http */
} /* namespace ZL */
#endif /* SRC_HTTP_HTTPCLIENTIMP_H_ */

View File

@ -0,0 +1,87 @@
/*
* HttpDownloader.cpp
*
* Created on: 201755
* 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 */

46
src/Http/HttpDownloader.h Normal file
View File

@ -0,0 +1,46 @@
/*
* HttpDownloader.h
*
* Created on: 201755
* 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<HttpDownloader> Ptr;
typedef std::function<void(int code,const char *errMsg,const char *filePath)> 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_ */

View File

@ -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

40
src/Http/HttpRequester.h Normal file
View File

@ -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<HttpRequester> Ptr;
typedef std::function<void(const SockException &ex,const string &status,const HttpHeader &header,const string &strRecvBody)> 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 */

View File

@ -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<MAX_TCP_SESSION> {
public:
typedef map<string,string> KeyValue;
typedef StrCaseMap KeyValue;
typedef std::function<void(const string &codeOut,
const KeyValue &headerOut,
const string &contentOut)> HttpResponseInvoker;