mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-22 19:00:01 +08:00
支持Http multipart/form-data 文件上传
优化http客户端超时逻辑 去除rtmp/rtsp代理时间戳覆盖逻辑
This commit is contained in:
parent
4c82903ee8
commit
ca885013af
@ -24,6 +24,7 @@
|
|||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "Player/Player.h"
|
||||||
#include "Common/config.h"
|
#include "Common/config.h"
|
||||||
#include "PlayerProxy.h"
|
#include "PlayerProxy.h"
|
||||||
#include "Util/mini.h"
|
#include "Util/mini.h"
|
||||||
@ -58,7 +59,7 @@ void PlayerProxy::play(const char* strUrl) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(strongSelf->m_pChn){
|
if(strongSelf->m_pChn){
|
||||||
strongSelf->m_pChn->inputH264((char *)data.data.data(), data.data.size(), 0);
|
strongSelf->m_pChn->inputH264((char *)data.data.data(), data.data.size(), data.timeStamp);
|
||||||
}else{
|
}else{
|
||||||
strongSelf->initMedia();
|
strongSelf->initMedia();
|
||||||
}
|
}
|
||||||
@ -69,7 +70,7 @@ void PlayerProxy::play(const char* strUrl) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(strongSelf->m_pChn){
|
if(strongSelf->m_pChn){
|
||||||
strongSelf->m_pChn->inputAAC((char *)data.data, data.aac_frame_length, 0);
|
strongSelf->m_pChn->inputAAC((char *)data.data, data.aac_frame_length, data.timeStamp);
|
||||||
}else{
|
}else{
|
||||||
strongSelf->initMedia();
|
strongSelf->initMedia();
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ HttpClient::HttpClient(){
|
|||||||
HttpClient::~HttpClient(){
|
HttpClient::~HttpClient(){
|
||||||
}
|
}
|
||||||
void HttpClient::sendRequest(const string &strUrl,float fTimeOutSec){
|
void HttpClient::sendRequest(const string &strUrl,float fTimeOutSec){
|
||||||
|
_aliveTicker.resetTime();
|
||||||
auto protocol = FindField(strUrl.data(), NULL , "://");
|
auto protocol = FindField(strUrl.data(), NULL , "://");
|
||||||
uint16_t defaultPort;
|
uint16_t defaultPort;
|
||||||
bool isHttps;
|
bool isHttps;
|
||||||
@ -73,15 +74,15 @@ void HttpClient::sendRequest(const string &strUrl,float fTimeOutSec){
|
|||||||
_header.emplace(string("Accept-Language"),"zh-CN,zh;q=0.8");
|
_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");
|
_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()){
|
if(_body && _body->remainSize()){
|
||||||
_header.emplace(string("Content-Length"),to_string(_body.size()));
|
_header.emplace(string("Content-Length"),to_string(_body->remainSize()));
|
||||||
_header.emplace(string("Content-Type"),"application/x-www-form-urlencoded; charset=UTF-8");
|
_header.emplace(string("Content-Type"),"application/x-www-form-urlencoded; charset=UTF-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bChanged = (_lastHost != host + ":" + to_string(port)) || (_isHttps != isHttps);
|
bool bChanged = (_lastHost != host + ":" + to_string(port)) || (_isHttps != isHttps);
|
||||||
_lastHost = host + ":" + to_string(port);
|
_lastHost = host + ":" + to_string(port);
|
||||||
_isHttps = isHttps;
|
_isHttps = isHttps;
|
||||||
|
_fTimeOutSec = fTimeOutSec;
|
||||||
if(!alive() || bChanged){
|
if(!alive() || bChanged){
|
||||||
//InfoL << "reconnet:" << _lastHost;
|
//InfoL << "reconnet:" << _lastHost;
|
||||||
startConnect(host, port,fTimeOutSec);
|
startConnect(host, port,fTimeOutSec);
|
||||||
@ -93,33 +94,39 @@ void HttpClient::sendRequest(const string &strUrl,float fTimeOutSec){
|
|||||||
|
|
||||||
|
|
||||||
void HttpClient::onConnect(const SockException &ex) {
|
void HttpClient::onConnect(const SockException &ex) {
|
||||||
|
_aliveTicker.resetTime();
|
||||||
if(ex){
|
if(ex){
|
||||||
onDisconnect(ex);
|
onDisconnect(ex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_recvedBodySize = -1;
|
_recvedBodySize = -1;
|
||||||
_recvedResponse.clear();
|
_recvedResponse.clear();
|
||||||
send(_method + " ");
|
_StrPrinter printer;
|
||||||
send(_path + " HTTP/1.1\r\n");
|
printer << _method + " " << _path + " HTTP/1.1\r\n";
|
||||||
for (auto &pr : _header) {
|
for (auto &pr : _header) {
|
||||||
send(pr.first + ": ");
|
printer << pr.first + ": ";
|
||||||
send(pr.second + "\r\n");
|
printer << pr.second + "\r\n";
|
||||||
}
|
|
||||||
send("\r\n");
|
|
||||||
if (!_body.empty()) {
|
|
||||||
send(_body);
|
|
||||||
}
|
}
|
||||||
|
send(printer << "\r\n");
|
||||||
|
onSend();
|
||||||
}
|
}
|
||||||
void HttpClient::onRecv(const Buffer::Ptr &pBuf) {
|
void HttpClient::onRecv(const Buffer::Ptr &pBuf) {
|
||||||
onRecvBytes(pBuf->data(),pBuf->size());
|
onRecvBytes(pBuf->data(),pBuf->size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpClient::onErr(const SockException &ex) {
|
void HttpClient::onErr(const SockException &ex) {
|
||||||
|
if(ex.getErrCode() == Err_eof && _totalBodySize == INT64_MAX){
|
||||||
|
//如果Content-Length未指定 但服务器断开链接
|
||||||
|
//则认为本次http请求完成
|
||||||
|
_totalBodySize = 0;
|
||||||
|
onResponseCompleted();
|
||||||
|
}
|
||||||
onDisconnect(ex);
|
onDisconnect(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpClient::onRecvBytes(const char* data, int size) {
|
void HttpClient::onRecvBytes(const char* data, int size) {
|
||||||
if(_recvedBodySize == -1){
|
_aliveTicker.resetTime();
|
||||||
|
if(_recvedBodySize == -1){
|
||||||
//还没有收到http body,这只是http头
|
//还没有收到http body,这只是http头
|
||||||
auto lastLen = _recvedResponse.size();
|
auto lastLen = _recvedResponse.size();
|
||||||
_recvedResponse.append(data,size);
|
_recvedResponse.append(data,size);
|
||||||
@ -132,6 +139,9 @@ void HttpClient::onRecvBytes(const char* data, int size) {
|
|||||||
onResponseHeader(_parser.Url(),_parser.getValues());
|
onResponseHeader(_parser.Url(),_parser.getValues());
|
||||||
|
|
||||||
_totalBodySize = atoll(((HttpHeader &)_parser.getValues())["Content-Length"].data());
|
_totalBodySize = atoll(((HttpHeader &)_parser.getValues())["Content-Length"].data());
|
||||||
|
if(_totalBodySize == 0){
|
||||||
|
_totalBodySize = INT64_MAX;
|
||||||
|
}
|
||||||
_recvedBodySize = _recvedResponse.size() - pos - 4;
|
_recvedBodySize = _recvedResponse.size() - pos - 4;
|
||||||
if(_totalBodySize < _recvedBodySize){
|
if(_totalBodySize < _recvedBodySize){
|
||||||
//http body 比声明的大 这个不可能的
|
//http body 比声明的大 这个不可能的
|
||||||
@ -149,6 +159,7 @@ void HttpClient::onRecvBytes(const char* data, int size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(_recvedBodySize >= _totalBodySize){
|
if(_recvedBodySize >= _totalBodySize){
|
||||||
|
_totalBodySize = 0;
|
||||||
onResponseCompleted();
|
onResponseCompleted();
|
||||||
}
|
}
|
||||||
_recvedResponse.clear();
|
_recvedResponse.clear();
|
||||||
@ -159,6 +170,9 @@ void HttpClient::onRecvBytes(const char* data, int size) {
|
|||||||
_recvedBodySize += size;
|
_recvedBodySize += size;
|
||||||
onResponseBody(data,size,_recvedBodySize,_totalBodySize);
|
onResponseBody(data,size,_recvedBodySize,_totalBodySize);
|
||||||
if(_recvedBodySize >= _totalBodySize){
|
if(_recvedBodySize >= _totalBodySize){
|
||||||
|
//如果接收的数据大于Content-Length
|
||||||
|
//则认为本次http请求完成
|
||||||
|
_totalBodySize = 0;
|
||||||
onResponseCompleted();
|
onResponseCompleted();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -171,8 +185,39 @@ void HttpClient::onRecvBytes(const char* data, int size) {
|
|||||||
ErrorL << _totalBodySize << ":" << _recvedBodySize << "\r\n" << (printer << endl);
|
ErrorL << _totalBodySize << ":" << _recvedBodySize << "\r\n" << (printer << endl);
|
||||||
shutdown();
|
shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HttpClient::onSend() {
|
||||||
|
_aliveTicker.resetTime();
|
||||||
|
while (_body && _body->remainSize() && !isSocketBusy()){
|
||||||
|
auto buffer = _body->readData();
|
||||||
|
if (!buffer){
|
||||||
|
//数据发送结束或读取数据异常
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(send(buffer) <= 0){
|
||||||
|
//发送数据失败,不需要回滚数据,因为发送前已经通过isSocketBusy()判断socket可写
|
||||||
|
//所以发送缓存区肯定未满,该buffer肯定已经写入socket
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpClient::onManager() {
|
||||||
|
if(_aliveTicker.elapsedTime() > 3 * 1000 && _totalBodySize == INT64_MAX){
|
||||||
|
//如果Content-Length未指定 但接收数据超时
|
||||||
|
//则认为本次http请求完成
|
||||||
|
_totalBodySize = 0;
|
||||||
|
onResponseCompleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_fTimeOutSec > 0 && _aliveTicker.elapsedTime() > _fTimeOutSec * 1000){
|
||||||
|
//超时
|
||||||
|
onDisconnect(SockException(Err_timeout,"http request timeout"));
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} /* namespace Http */
|
} /* namespace Http */
|
||||||
} /* namespace ZL */
|
} /* namespace ZL */
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#ifndef Http_HttpClient_h
|
#ifndef Http_HttpClient_h
|
||||||
#define Http_HttpClient_h
|
#define Http_HttpClient_h
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -41,8 +42,7 @@ using namespace ZL::Network;
|
|||||||
namespace ZL {
|
namespace ZL {
|
||||||
namespace Http {
|
namespace Http {
|
||||||
|
|
||||||
class HttpArgs : public StrCaseMap
|
class HttpArgs : public StrCaseMap {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
HttpArgs(){}
|
HttpArgs(){}
|
||||||
virtual ~HttpArgs(){}
|
virtual ~HttpArgs(){}
|
||||||
@ -60,7 +60,144 @@ public:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class HttpBody{
|
||||||
|
public:
|
||||||
|
typedef std::shared_ptr<HttpBody> Ptr;
|
||||||
|
HttpBody(){}
|
||||||
|
virtual ~HttpBody(){}
|
||||||
|
//剩余数据大小
|
||||||
|
virtual uint64_t remainSize() = 0;
|
||||||
|
virtual Buffer::Ptr readData() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HttpBodyString : public HttpBody{
|
||||||
|
public:
|
||||||
|
typedef std::shared_ptr<HttpBodyString> Ptr;
|
||||||
|
HttpBodyString(const string &str){
|
||||||
|
_str = str;
|
||||||
|
}
|
||||||
|
virtual ~HttpBodyString(){}
|
||||||
|
|
||||||
|
uint64_t remainSize() override {
|
||||||
|
return _str.size();
|
||||||
|
}
|
||||||
|
Buffer::Ptr readData() override {
|
||||||
|
auto ret = std::make_shared<BufferString>(_str);
|
||||||
|
_str.clear();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
mutable string _str;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class HttpMultiFormBody : public HttpBody {
|
||||||
|
public:
|
||||||
|
typedef std::shared_ptr<HttpMultiFormBody> Ptr;
|
||||||
|
HttpMultiFormBody(const StrCaseMap &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());
|
||||||
|
}
|
||||||
|
auto fileName = filePath;
|
||||||
|
auto pos = filePath.rfind('/');
|
||||||
|
if(pos != string::npos){
|
||||||
|
fileName = filePath.substr(pos + 1);
|
||||||
|
}
|
||||||
|
_bodyPrefix = multiFormBodyPrefix(args,boundary,fileName);
|
||||||
|
_bodySuffix = multiFormBodySuffix(boundary);
|
||||||
|
_totalSize = _bodyPrefix.size() + _bodySuffix.size() + fileSize(_fp);
|
||||||
|
_sliceSize = sliceSize;
|
||||||
|
}
|
||||||
|
virtual ~HttpMultiFormBody(){
|
||||||
|
fclose(_fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t remainSize() override {
|
||||||
|
return _totalSize - _offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::Ptr readData() override{
|
||||||
|
if(_bodyPrefix.size()){
|
||||||
|
auto ret = std::make_shared<BufferString>(_bodyPrefix);
|
||||||
|
_offset += _bodyPrefix.size();
|
||||||
|
_bodyPrefix.clear();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(0 == feof(_fp)){
|
||||||
|
auto ret = std::make_shared<BufferRaw>(_sliceSize);
|
||||||
|
//读文件
|
||||||
|
int size;
|
||||||
|
do{
|
||||||
|
size = fread(ret->data(),1,_sliceSize,_fp);
|
||||||
|
}while(-1 == size && UV_EINTR == get_uv_error(false));
|
||||||
|
|
||||||
|
if(size == -1){
|
||||||
|
_offset = _totalSize;
|
||||||
|
WarnL << "fread failed:" << get_uv_errmsg();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
_offset += size;
|
||||||
|
ret->setSize(size);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_bodySuffix.size()){
|
||||||
|
auto ret = std::make_shared<BufferString>(_bodySuffix);
|
||||||
|
_offset = _totalSize;
|
||||||
|
_bodySuffix.clear();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static string multiFormBodyPrefix(const StrCaseMap &args,const string &boundary,const string &fileName){
|
||||||
|
string MPboundary = string("--") + boundary;
|
||||||
|
_StrPrinter body;
|
||||||
|
for(auto &pr : args){
|
||||||
|
body << MPboundary << "\r\n";
|
||||||
|
body << "Content-Disposition: form-data; name=\"" << pr.first << "\"\r\n\r\n";
|
||||||
|
body << pr.second << "\r\n";
|
||||||
|
}
|
||||||
|
body << MPboundary << "\r\n";
|
||||||
|
body << "Content-Disposition: form-data; name=\"" << "file" << "\";filename=\"" << fileName << "\"\r\n";
|
||||||
|
body << "Content-Type: application/octet-stream\r\n\r\n" ;
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
static string multiFormBodySuffix(const string &boundary){
|
||||||
|
string MPboundary = string("--") + boundary;
|
||||||
|
string endMPboundary = MPboundary + "--";
|
||||||
|
_StrPrinter body;
|
||||||
|
body << "\r\n" << endMPboundary;
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t fileSize(FILE *fp) {
|
||||||
|
auto current = ftell(fp);
|
||||||
|
fseek(fp,0L,SEEK_END); /* 定位到文件末尾 */
|
||||||
|
auto end = ftell(fp); /* 得到文件大小 */
|
||||||
|
fseek(fp,current,SEEK_SET);
|
||||||
|
return end - current;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string multiFormContentType(const string &boundary){
|
||||||
|
return StrPrinter << "multipart/form-data; boundary=" << boundary;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
FILE *_fp;
|
||||||
|
string _bodyPrefix;
|
||||||
|
string _bodySuffix;
|
||||||
|
uint64_t _offset = 0;
|
||||||
|
uint64_t _totalSize;
|
||||||
|
uint32_t _sliceSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class HttpClient : public TcpClient
|
class HttpClient : public TcpClient
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -71,7 +208,7 @@ public:
|
|||||||
virtual void sendRequest(const string &url,float fTimeOutSec);
|
virtual void sendRequest(const string &url,float fTimeOutSec);
|
||||||
void clear(){
|
void clear(){
|
||||||
_header.clear();
|
_header.clear();
|
||||||
_body.clear();
|
_body.reset();
|
||||||
_method.clear();
|
_method.clear();
|
||||||
_path.clear();
|
_path.clear();
|
||||||
_recvedResponse.clear();
|
_recvedResponse.clear();
|
||||||
@ -83,10 +220,18 @@ public:
|
|||||||
void setHeader(const HttpHeader &header){
|
void setHeader(const HttpHeader &header){
|
||||||
_header = header;
|
_header = header;
|
||||||
}
|
}
|
||||||
void addHeader(const string &key,const string &val){
|
HttpClient & addHeader(const string &key,const string &val,bool force = false){
|
||||||
_header.emplace(key,val);
|
if(!force){
|
||||||
|
_header.emplace(key,val);
|
||||||
|
}else{
|
||||||
|
_header[key] = val;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
void setBody(const string &body){
|
void setBody(const string &body){
|
||||||
|
_body.reset(new HttpBodyString(body));
|
||||||
|
}
|
||||||
|
void setBody(const HttpBody::Ptr &body){
|
||||||
_body = body;
|
_body = body;
|
||||||
}
|
}
|
||||||
const string &responseStatus(){
|
const string &responseStatus(){
|
||||||
@ -96,8 +241,6 @@ public:
|
|||||||
return _parser.getValues();
|
return _parser.getValues();
|
||||||
}
|
}
|
||||||
protected:
|
protected:
|
||||||
bool _isHttps;
|
|
||||||
|
|
||||||
virtual void onResponseHeader(const string &status,const HttpHeader &headers){
|
virtual void onResponseHeader(const string &status,const HttpHeader &headers){
|
||||||
DebugL << status;
|
DebugL << status;
|
||||||
};
|
};
|
||||||
@ -109,24 +252,28 @@ protected:
|
|||||||
}
|
}
|
||||||
virtual void onRecvBytes(const char *data,int size);
|
virtual void onRecvBytes(const char *data,int size);
|
||||||
virtual void onDisconnect(const SockException &ex){}
|
virtual void onDisconnect(const SockException &ex){}
|
||||||
private:
|
protected:
|
||||||
virtual void onConnect(const SockException &ex) override;
|
virtual void onConnect(const SockException &ex) override;
|
||||||
virtual void onRecv(const Buffer::Ptr &pBuf) override;
|
virtual void onRecv(const Buffer::Ptr &pBuf) override;
|
||||||
virtual void onErr(const SockException &ex) override;
|
virtual void onErr(const SockException &ex) override;
|
||||||
|
virtual void onSend() override;
|
||||||
|
virtual void onManager() override;
|
||||||
|
protected:
|
||||||
|
bool _isHttps;
|
||||||
|
private:
|
||||||
//send
|
//send
|
||||||
HttpHeader _header;
|
HttpHeader _header;
|
||||||
string _body;
|
HttpBody::Ptr _body;
|
||||||
string _method;
|
string _method;
|
||||||
string _path;
|
string _path;
|
||||||
|
|
||||||
//recv
|
//recv
|
||||||
string _recvedResponse;
|
string _recvedResponse;
|
||||||
size_t _recvedBodySize;
|
int64_t _recvedBodySize;
|
||||||
size_t _totalBodySize;
|
int64_t _totalBodySize;
|
||||||
Parser _parser;
|
Parser _parser;
|
||||||
|
|
||||||
string _lastHost;
|
string _lastHost;
|
||||||
|
Ticker _aliveTicker;
|
||||||
|
float _fTimeOutSec = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,8 +43,6 @@ HttpDownloader::~HttpDownloader() {
|
|||||||
|
|
||||||
void HttpDownloader::startDownload(const string& url, const string& filePath,bool bAppend,float timeOutSecond) {
|
void HttpDownloader::startDownload(const string& url, const string& filePath,bool bAppend,float timeOutSecond) {
|
||||||
_filePath = filePath;
|
_filePath = filePath;
|
||||||
_timeOutSecond = timeOutSecond;
|
|
||||||
_downloadTicker.resetTime();
|
|
||||||
if(_filePath.empty()){
|
if(_filePath.empty()){
|
||||||
_filePath = exeDir() + "HttpDownloader/" + MD5(url).hexdigest();
|
_filePath = exeDir() + "HttpDownloader/" + MD5(url).hexdigest();
|
||||||
}
|
}
|
||||||
@ -68,7 +66,6 @@ void HttpDownloader::startDownload(const string& url, const string& filePath,boo
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HttpDownloader::onResponseHeader(const string& status,const HttpHeader& headers) {
|
void HttpDownloader::onResponseHeader(const string& status,const HttpHeader& headers) {
|
||||||
_downloadTicker.resetTime();
|
|
||||||
if(status != "200" && status != "206"){
|
if(status != "200" && status != "206"){
|
||||||
//失败
|
//失败
|
||||||
shutdown();
|
shutdown();
|
||||||
@ -83,22 +80,11 @@ void HttpDownloader::onResponseHeader(const string& status,const HttpHeader& hea
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HttpDownloader::onResponseBody(const char* buf, size_t size, size_t recvedSize, size_t totalSize) {
|
void HttpDownloader::onResponseBody(const char* buf, size_t size, size_t recvedSize, size_t totalSize) {
|
||||||
_downloadTicker.resetTime();
|
|
||||||
if(_saveFile){
|
if(_saveFile){
|
||||||
fwrite(buf,size,1,_saveFile);
|
fwrite(buf,size,1,_saveFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//string getMd5Sum(const string &filePath){
|
|
||||||
// auto fp = File::createfile_file(filePath.data(),"rb");
|
|
||||||
// fseek(fp,0,SEEK_END);
|
|
||||||
// auto sz = ftell(fp);
|
|
||||||
// char tmp[sz];
|
|
||||||
// fseek(fp,0,SEEK_SET);
|
|
||||||
// auto rd = fread(tmp,1,sz,fp);
|
|
||||||
// InfoL << sz << " " << rd;
|
|
||||||
// fclose(fp);
|
|
||||||
// return MD5(string(tmp,sz)).hexdigest();
|
|
||||||
//}
|
|
||||||
void HttpDownloader::onResponseCompleted() {
|
void HttpDownloader::onResponseCompleted() {
|
||||||
closeFile();
|
closeFile();
|
||||||
//InfoL << "md5Sum:" << getMd5Sum(_filePath);
|
//InfoL << "md5Sum:" << getMd5Sum(_filePath);
|
||||||
@ -128,14 +114,6 @@ void HttpDownloader::closeFile() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpDownloader::onManager(){
|
|
||||||
if(_downloadTicker.elapsedTime() > _timeOutSecond * 1000){
|
|
||||||
//超时
|
|
||||||
onDisconnect(SockException(Err_timeout,"download timeout"));
|
|
||||||
shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} /* namespace Http */
|
} /* namespace Http */
|
||||||
} /* namespace ZL */
|
} /* namespace ZL */
|
||||||
|
@ -52,16 +52,12 @@ private:
|
|||||||
void onResponseBody(const char *buf,size_t size,size_t recvedSize,size_t totalSize) override;
|
void onResponseBody(const char *buf,size_t size,size_t recvedSize,size_t totalSize) override;
|
||||||
void onResponseCompleted() override;
|
void onResponseCompleted() override;
|
||||||
void onDisconnect(const SockException &ex) override;
|
void onDisconnect(const SockException &ex) override;
|
||||||
void onManager() override;
|
|
||||||
|
|
||||||
void closeFile();
|
void closeFile();
|
||||||
|
private:
|
||||||
FILE *_saveFile = nullptr;
|
FILE *_saveFile = nullptr;
|
||||||
string _filePath;
|
string _filePath;
|
||||||
onDownloadResult _onResult;
|
onDownloadResult _onResult;
|
||||||
uint32_t _timeOutSecond;
|
|
||||||
bool _bDownloadSuccess = false;
|
bool _bDownloadSuccess = false;
|
||||||
Ticker _downloadTicker;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace Http */
|
} /* namespace Http */
|
||||||
|
@ -59,18 +59,9 @@ void HttpRequester::onDisconnect(const SockException &ex){
|
|||||||
|
|
||||||
void HttpRequester::startRequester(const string &url,const HttpRequesterResult &onResult , float timeOutSecond){
|
void HttpRequester::startRequester(const string &url,const HttpRequesterResult &onResult , float timeOutSecond){
|
||||||
_onResult = onResult;
|
_onResult = onResult;
|
||||||
_resTicker.resetTime();
|
|
||||||
_timeOutSecond = timeOutSecond;
|
|
||||||
sendRequest(url,timeOutSecond);
|
sendRequest(url,timeOutSecond);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpRequester::onManager(){
|
|
||||||
if(_onResult && _resTicker.elapsedTime() > _timeOutSecond * 1000){
|
|
||||||
//超时
|
|
||||||
onDisconnect(SockException(Err_timeout,"wait http response timeout"));
|
|
||||||
shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}//namespace Http
|
}//namespace Http
|
||||||
|
@ -39,18 +39,15 @@ public:
|
|||||||
typedef std::function<void(const SockException &ex,const string &status,const HttpHeader &header,const string &strRecvBody)> HttpRequesterResult;
|
typedef std::function<void(const SockException &ex,const string &status,const HttpHeader &header,const string &strRecvBody)> HttpRequesterResult;
|
||||||
HttpRequester();
|
HttpRequester();
|
||||||
virtual ~HttpRequester();
|
virtual ~HttpRequester();
|
||||||
|
|
||||||
void startRequester(const string &url,const HttpRequesterResult &onResult,float timeOutSecond = 10);
|
void startRequester(const string &url,const HttpRequesterResult &onResult,float timeOutSecond = 10);
|
||||||
private:
|
private:
|
||||||
void onResponseHeader(const string &status,const HttpHeader &headers) override;
|
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 onResponseBody(const char *buf,size_t size,size_t recvedSize,size_t totalSize) override;
|
||||||
void onResponseCompleted() override;
|
void onResponseCompleted() override;
|
||||||
void onDisconnect(const SockException &ex) override;
|
void onDisconnect(const SockException &ex) override;
|
||||||
void onManager() override;
|
private:
|
||||||
string _strRecvBody;
|
string _strRecvBody;
|
||||||
HttpRequesterResult _onResult;
|
HttpRequesterResult _onResult;
|
||||||
Ticker _resTicker;
|
|
||||||
float _timeOutSecond;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}//namespace Http
|
}//namespace Http
|
||||||
|
@ -42,116 +42,153 @@ using namespace ZL::Poller;
|
|||||||
using namespace ZL::Network;
|
using namespace ZL::Network;
|
||||||
|
|
||||||
|
|
||||||
int main(int argc,char *argv[]){
|
int main(int argc, char *argv[]) {
|
||||||
//设置退出信号处理函数
|
//设置退出信号处理函数
|
||||||
signal(SIGINT, [](int){EventPoller::Instance().shutdown();});
|
signal(SIGINT, [](int) { EventPoller::Instance().shutdown(); });
|
||||||
//设置日志
|
//设置日志
|
||||||
Logger::Instance().add(std::make_shared<ConsoleChannel>("stdout", LTrace));
|
Logger::Instance().add(std::make_shared<ConsoleChannel>("stdout", LTrace));
|
||||||
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
|
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
|
||||||
///////////////////////////////http downloader///////////////////////
|
{
|
||||||
//下载器map
|
///////////////////////////////http downloader///////////////////////
|
||||||
map<string,HttpDownloader::Ptr> downloaderMap;
|
//下载器map
|
||||||
//下载两个文件,一个是http下载,一个https下载
|
map<string, HttpDownloader::Ptr> downloaderMap;
|
||||||
auto urlList = {"http://img3.imgtn.bdimg.com/it/u=158031390,1321729164&fm=214&gp=0.jpg",
|
//下载两个文件,一个是http下载,一个https下载
|
||||||
"https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=931786003,1029770543&fm=27&gp=0.jpg"};
|
auto urlList = {"http://img3.imgtn.bdimg.com/it/u=158031390,1321729164&fm=214&gp=0.jpg",
|
||||||
|
"https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=931786003,1029770543&fm=27&gp=0.jpg"};
|
||||||
|
|
||||||
for(auto &url : urlList){
|
for (auto &url : urlList) {
|
||||||
//创建下载器
|
//创建下载器
|
||||||
HttpDownloader::Ptr downloader(new HttpDownloader());
|
HttpDownloader::Ptr downloader(new HttpDownloader());
|
||||||
downloader->setOnResult([](ErrCode code,const char *errMsg,const char *filePath){
|
downloader->setOnResult([](ErrCode code, const char *errMsg, const char *filePath) {
|
||||||
DebugL << "=====================HttpDownloader result=======================";
|
DebugL << "=====================HttpDownloader result=======================";
|
||||||
//下载结果回调
|
//下载结果回调
|
||||||
if(code == Err_success){
|
if (code == Err_success) {
|
||||||
//文件下载成功
|
//文件下载成功
|
||||||
InfoL << "download file success:" << filePath;
|
InfoL << "download file success:" << filePath;
|
||||||
}else{
|
} else {
|
||||||
//下载失败
|
//下载失败
|
||||||
WarnL << "code:" << code << " msg:" << errMsg;
|
WarnL << "code:" << code << " msg:" << errMsg;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
//断点续传功能,开启后可能会遇到416的错误(因为上次文件已经下载完全)
|
//断点续传功能,开启后可能会遇到416的错误(因为上次文件已经下载完全)
|
||||||
downloader->startDownload(url,exeDir() + MD5(url).hexdigest() + ".jpg",true);
|
downloader->startDownload(url, exeDir() + MD5(url).hexdigest() + ".jpg", true);
|
||||||
//下载器必须被强引用,否则作用域一失效就会导致对象销毁
|
//下载器必须被强引用,否则作用域一失效就会导致对象销毁
|
||||||
downloaderMap.emplace(url,downloader);
|
downloaderMap.emplace(url, downloader);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////http get///////////////////////
|
///////////////////////////////http get///////////////////////
|
||||||
//创建一个Http请求器
|
//创建一个Http请求器
|
||||||
HttpRequester::Ptr requesterGet(new HttpRequester());
|
HttpRequester::Ptr requesterGet(new HttpRequester());
|
||||||
//使用GET方式请求
|
//使用GET方式请求
|
||||||
requesterGet->setMethod("GET");
|
requesterGet->setMethod("GET");
|
||||||
//设置http请求头,我们假设设置cookie,当然你也可以设置其他http头
|
//设置http请求头,我们假设设置cookie,当然你也可以设置其他http头
|
||||||
requesterGet->addHeader("Cookie","SESSIONID=e1aa89b3-f79f-4ac6-8ae2-0cea9ae8e2d7");
|
requesterGet->addHeader("Cookie", "SESSIONID=e1aa89b3-f79f-4ac6-8ae2-0cea9ae8e2d7");
|
||||||
//开启请求,该api会返回当前主机外网ip等信息
|
//开启请求,该api会返回当前主机外网ip等信息
|
||||||
requesterGet->startRequester("http://pv.sohu.com/cityjson?ie=utf-8",//url地址
|
requesterGet->startRequester("http://pv.sohu.com/cityjson?ie=utf-8",//url地址
|
||||||
[](const SockException &ex, //网络相关的失败信息,如果为空就代表成功
|
[](const SockException &ex, //网络相关的失败信息,如果为空就代表成功
|
||||||
const string &status, //http回复的状态码,比如说200/404
|
const string &status, //http回复的状态码,比如说200/404
|
||||||
const HttpClient::HttpHeader &header, //http回复头
|
const HttpClient::HttpHeader &header, //http回复头
|
||||||
const string &strRecvBody){ //http回复body
|
const string &strRecvBody) { //http回复body
|
||||||
DebugL << "=====================HttpRequester GET===========================";
|
DebugL << "=====================HttpRequester GET===========================";
|
||||||
if(ex){
|
if (ex) {
|
||||||
//网络相关的错误
|
//网络相关的错误
|
||||||
WarnL << "network err:" << ex.getErrCode() << " " << ex.what();
|
WarnL << "network err:" << ex.getErrCode() << " " << ex.what();
|
||||||
}else{
|
} else {
|
||||||
//打印http回复信息
|
//打印http回复信息
|
||||||
_StrPrinter printer;
|
_StrPrinter printer;
|
||||||
for(auto &pr: header){
|
for (auto &pr: header) {
|
||||||
printer << pr.first << ":" << pr.second << "\r\n";
|
printer << pr.first << ":" << pr.second << "\r\n";
|
||||||
}
|
}
|
||||||
InfoL << "status:" << status << "\r\n"
|
InfoL << "status:" << status << "\r\n"
|
||||||
<< "header:\r\n" << (printer << endl)
|
<< "header:\r\n" << (printer << endl)
|
||||||
<< "\r\nbody:" << strRecvBody;
|
<< "\r\nbody:" << strRecvBody;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
///////////////////////////////http post///////////////////////
|
///////////////////////////////http post///////////////////////
|
||||||
//创建一个Http请求器
|
//创建一个Http请求器
|
||||||
HttpRequester::Ptr requesterPost(new HttpRequester());
|
HttpRequester::Ptr requesterPost(new HttpRequester());
|
||||||
//使用POST方式请求
|
//使用POST方式请求
|
||||||
requesterPost->setMethod("POST");
|
requesterPost->setMethod("POST");
|
||||||
//设置http请求头
|
//设置http请求头
|
||||||
requesterPost->addHeader("X-Requested-With","XMLHttpRequest");
|
requesterPost->addHeader("X-Requested-With", "XMLHttpRequest");
|
||||||
requesterPost->addHeader("Origin","http://fanyi.baidu.com");
|
requesterPost->addHeader("Origin", "http://fanyi.baidu.com");
|
||||||
//设置POST参数列表
|
//设置POST参数列表
|
||||||
HttpArgs args;
|
HttpArgs args;
|
||||||
args["query"] = "test";
|
args["query"] = "test";
|
||||||
args["from"] = "en";
|
args["from"] = "en";
|
||||||
args["to"] = "zh";
|
args["to"] = "zh";
|
||||||
args["transtype"] = "translang";
|
args["transtype"] = "translang";
|
||||||
args["simple_means_flag"] = "3";
|
args["simple_means_flag"] = "3";
|
||||||
requesterPost->setBody(args.make());
|
requesterPost->setBody(args.make());
|
||||||
//开启请求
|
//开启请求
|
||||||
requesterPost->startRequester("http://fanyi.baidu.com/langdetect",//url地址
|
requesterPost->startRequester("http://fanyi.baidu.com/langdetect",//url地址
|
||||||
[](const SockException &ex, //网络相关的失败信息,如果为空就代表成功
|
[](const SockException &ex, //网络相关的失败信息,如果为空就代表成功
|
||||||
const string &status, //http回复的状态码,比如说200/404
|
const string &status, //http回复的状态码,比如说200/404
|
||||||
const HttpClient::HttpHeader &header, //http回复头
|
const HttpClient::HttpHeader &header, //http回复头
|
||||||
const string &strRecvBody){ //http回复body
|
const string &strRecvBody) { //http回复body
|
||||||
DebugL << "=====================HttpRequester POST==========================";
|
DebugL << "=====================HttpRequester POST==========================";
|
||||||
if(ex){
|
if (ex) {
|
||||||
//网络相关的错误
|
//网络相关的错误
|
||||||
WarnL << "network err:" << ex.getErrCode() << " " << ex.what();
|
WarnL << "network err:" << ex.getErrCode() << " " << ex.what();
|
||||||
} else {
|
} else {
|
||||||
//打印http回复信息
|
//打印http回复信息
|
||||||
_StrPrinter printer;
|
_StrPrinter printer;
|
||||||
for(auto &pr: header){
|
for (auto &pr: header) {
|
||||||
printer << pr.first << ":" << pr.second << "\r\n";
|
printer << pr.first << ":" << pr.second << "\r\n";
|
||||||
}
|
}
|
||||||
InfoL << "status:" << status << "\r\n"
|
InfoL << "status:" << status << "\r\n"
|
||||||
<< "header:\r\n" << (printer << endl)
|
<< "header:\r\n" << (printer << endl)
|
||||||
<< "\r\nbody:" << strRecvBody;
|
<< "\r\nbody:" << strRecvBody;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//事件轮询
|
///////////////////////////////http upload///////////////////////
|
||||||
EventPoller::Instance().runLoop();
|
//创建一个Http请求器
|
||||||
//清空下载器
|
HttpRequester::Ptr requesterUploader(new HttpRequester());
|
||||||
downloaderMap.clear();
|
//使用POST方式请求
|
||||||
requesterGet.reset();
|
requesterUploader->setMethod("POST");
|
||||||
requesterPost.reset();
|
//设置http请求头
|
||||||
//程序开始退出
|
HttpArgs argsUploader;
|
||||||
EventPoller::Destory();
|
argsUploader["query"] = "test";
|
||||||
AsyncTaskThread::Destory();
|
argsUploader["from"] = "en";
|
||||||
Logger::Destory();
|
argsUploader["to"] = "zh";
|
||||||
return 0;
|
argsUploader["transtype"] = "translang";
|
||||||
|
argsUploader["simple_means_flag"] = "3";
|
||||||
|
|
||||||
|
static string boundary = "0xKhTmLbOuNdArY";
|
||||||
|
HttpMultiFormBody::Ptr body(new HttpMultiFormBody(argsUploader, exePath(), boundary));
|
||||||
|
requesterUploader->setBody(body);
|
||||||
|
requesterUploader->addHeader("Content-Type", HttpMultiFormBody::multiFormContentType(boundary));
|
||||||
|
//开启请求
|
||||||
|
requesterUploader->startRequester("http://fanyi.baidu.com/langdetect",//url地址
|
||||||
|
[](const SockException &ex, //网络相关的失败信息,如果为空就代表成功
|
||||||
|
const string &status, //http回复的状态码,比如说200/404
|
||||||
|
const HttpClient::HttpHeader &header, //http回复头
|
||||||
|
const string &strRecvBody) { //http回复body
|
||||||
|
DebugL << "=====================HttpRequester Uploader==========================";
|
||||||
|
if (ex) {
|
||||||
|
//网络相关的错误
|
||||||
|
WarnL << "network err:" << ex.getErrCode() << " " << ex.what();
|
||||||
|
} else {
|
||||||
|
//打印http回复信息
|
||||||
|
_StrPrinter printer;
|
||||||
|
for (auto &pr: header) {
|
||||||
|
printer << pr.first << ":" << pr.second << "\r\n";
|
||||||
|
}
|
||||||
|
InfoL << "status:" << status << "\r\n"
|
||||||
|
<< "header:\r\n" << (printer << endl)
|
||||||
|
<< "\r\nbody:" << strRecvBody;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//事件轮询
|
||||||
|
EventPoller::Instance().runLoop();
|
||||||
|
}
|
||||||
|
//程序开始退出
|
||||||
|
EventPoller::Destory();
|
||||||
|
AsyncTaskThread::Destory();
|
||||||
|
Logger::Destory();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user