mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-26 12:37:09 +08:00
完善HTTP API功能
This commit is contained in:
parent
ad553900b4
commit
42d382878d
@ -70,7 +70,7 @@ extern const char kBroadcastRecordMP4[];
|
|||||||
#define BroadcastRecordMP4Args const Mp4Info &info
|
#define BroadcastRecordMP4Args const Mp4Info &info
|
||||||
|
|
||||||
extern const char kBroadcastHttpRequest[];
|
extern const char kBroadcastHttpRequest[];
|
||||||
#define BroadcastHttpRequestArgs const Parser &parser,HttpSession::HttpResponseInvoker &invoker
|
#define BroadcastHttpRequestArgs const Parser &parser,HttpSession::HttpResponseInvoker &invoker,bool &consumed
|
||||||
|
|
||||||
} //namespace Broadcast
|
} //namespace Broadcast
|
||||||
|
|
||||||
|
@ -106,7 +106,6 @@ HttpSession::HttpSession(const std::shared_ptr<ThreadPool> &pTh, const Socket::P
|
|||||||
static onceToken token([]() {
|
static onceToken token([]() {
|
||||||
g_mapCmdIndex.emplace("GET",&HttpSession::Handle_Req_GET);
|
g_mapCmdIndex.emplace("GET",&HttpSession::Handle_Req_GET);
|
||||||
g_mapCmdIndex.emplace("POST",&HttpSession::Handle_Req_POST);
|
g_mapCmdIndex.emplace("POST",&HttpSession::Handle_Req_POST);
|
||||||
g_mapCmdIndex.emplace("OPTIONS",&HttpSession::Handle_Req_POST);
|
|
||||||
}, nullptr);
|
}, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,6 +149,7 @@ void HttpSession::onRecv(const char *data,int size){
|
|||||||
}
|
}
|
||||||
inline HttpSession::HttpCode HttpSession::parserHttpReq(const string &str) {
|
inline HttpSession::HttpCode HttpSession::parserHttpReq(const string &str) {
|
||||||
m_parser.Parse(str.data());
|
m_parser.Parse(str.data());
|
||||||
|
urlDecode(m_parser);
|
||||||
string cmd = m_parser.Method();
|
string cmd = m_parser.Method();
|
||||||
auto it = g_mapCmdIndex.find(cmd);
|
auto it = g_mapCmdIndex.find(cmd);
|
||||||
if (it == g_mapCmdIndex.end()) {
|
if (it == g_mapCmdIndex.end()) {
|
||||||
@ -174,42 +174,43 @@ void HttpSession::onManager() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline HttpSession::HttpCode HttpSession::Handle_Req_GET() {
|
inline HttpSession::HttpCode HttpSession::Handle_Req_GET() {
|
||||||
string strUrl = strCoding::UrlUTF8Decode(m_parser.Url());
|
//先看看该http事件是否被拦截
|
||||||
#ifdef _WIN32
|
if(emitHttpEvent(false)){
|
||||||
static bool isGb2312 = !strcasecmp(mINI::Instance()[Config::Http::kCharSet].data(), "gb2312");
|
return Http_success;
|
||||||
if (isGb2312) {
|
|
||||||
strUrl = strCoding::UTF8ToGB2312(strUrl);
|
|
||||||
}
|
}
|
||||||
#endif // _WIN32
|
//事件未被拦截,则认为是http下载请求
|
||||||
string strFile = m_strPath + strUrl;
|
|
||||||
string strConType = m_parser["Connection"];
|
string strFile = m_strPath + m_parser.Url();
|
||||||
|
/////////////HTTP连接是否需要被关闭////////////////
|
||||||
static uint32_t reqCnt = mINI::Instance()[Config::Http::kMaxReqCount].as<uint32_t>();
|
static uint32_t reqCnt = mINI::Instance()[Config::Http::kMaxReqCount].as<uint32_t>();
|
||||||
bool bClose = (strcasecmp(strConType.data(),"close") == 0) && ( ++m_iReqCnt < reqCnt);
|
bool bClose = (strcasecmp(m_parser["Connection"].data(),"close") == 0) && ( ++m_iReqCnt < reqCnt);
|
||||||
HttpCode eHttpCode = bClose ? Http_failed : Http_success;
|
HttpCode eHttpCode = bClose ? Http_failed : Http_success;
|
||||||
|
//访问的是文件夹
|
||||||
if (strFile.back() == '/') {
|
if (strFile.back() == '/') {
|
||||||
//index the folder
|
//生成文件夹菜单索引
|
||||||
string strMeun;
|
string strMeun;
|
||||||
if (!makeMeun(strFile, strMeun)) {
|
if (!makeMeun(strFile, strMeun)) {
|
||||||
|
//文件夹不存在
|
||||||
sendNotFound(bClose);
|
sendNotFound(bClose);
|
||||||
return eHttpCode;
|
return eHttpCode;
|
||||||
}
|
}
|
||||||
sendResponse("200 OK", makeHttpHeader(bClose,strMeun.size() ), strMeun);
|
sendResponse("200 OK", makeHttpHeader(bClose,strMeun.size() ), strMeun);
|
||||||
return eHttpCode;
|
return eHttpCode;
|
||||||
}
|
}
|
||||||
//download the file
|
//访问的是文件
|
||||||
struct stat tFileStat;
|
struct stat tFileStat;
|
||||||
if (0 != stat(strFile.data(), &tFileStat)) {
|
if (0 != stat(strFile.data(), &tFileStat)) {
|
||||||
|
//文件不存在
|
||||||
sendNotFound(bClose);
|
sendNotFound(bClose);
|
||||||
return eHttpCode;
|
return eHttpCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeTicker();
|
|
||||||
FILE *pFile = fopen(strFile.data(), "rb");
|
FILE *pFile = fopen(strFile.data(), "rb");
|
||||||
if (pFile == NULL) {
|
if (pFile == NULL) {
|
||||||
|
//打开文件失败
|
||||||
sendNotFound(bClose);
|
sendNotFound(bClose);
|
||||||
return eHttpCode;
|
return eHttpCode;
|
||||||
}
|
}
|
||||||
|
//判断是不是分节下载
|
||||||
auto &strRange = m_parser["Range"];
|
auto &strRange = m_parser["Range"];
|
||||||
int64_t iRangeStart = 0, iRangeEnd = 0;
|
int64_t iRangeStart = 0, iRangeEnd = 0;
|
||||||
iRangeStart = atoll(FindField(strRange.data(), "bytes=", "-").data());
|
iRangeStart = atoll(FindField(strRange.data(), "bytes=", "-").data());
|
||||||
@ -219,24 +220,25 @@ inline HttpSession::HttpCode HttpSession::Handle_Req_GET() {
|
|||||||
}
|
}
|
||||||
const char *pcHttpResult = NULL;
|
const char *pcHttpResult = NULL;
|
||||||
if (strRange.size() == 0) {
|
if (strRange.size() == 0) {
|
||||||
|
//全部下载
|
||||||
pcHttpResult = "200 OK";
|
pcHttpResult = "200 OK";
|
||||||
} else {
|
} else {
|
||||||
|
//分节下载
|
||||||
pcHttpResult = "206 Partial Content";
|
pcHttpResult = "206 Partial Content";
|
||||||
fseek(pFile, iRangeStart, SEEK_SET);
|
fseek(pFile, iRangeStart, SEEK_SET);
|
||||||
}
|
}
|
||||||
|
auto httpHeader=makeHttpHeader(bClose, iRangeEnd - iRangeStart + 1, get_mime_type(strFile.data()));
|
||||||
auto httpHeader=makeHttpHeader(bClose, iRangeEnd - iRangeStart + 1, get_mime_type(strUrl.data()));
|
|
||||||
if (strRange.size() != 0) {
|
if (strRange.size() != 0) {
|
||||||
|
//分节下载返回Content-Range头
|
||||||
httpHeader.emplace("Content-Range",StrPrinter<<"bytes " << iRangeStart << "-" << iRangeEnd << "/" << tFileStat.st_size<< endl);
|
httpHeader.emplace("Content-Range",StrPrinter<<"bytes " << iRangeStart << "-" << iRangeEnd << "/" << tFileStat.st_size<< endl);
|
||||||
}
|
}
|
||||||
|
//先回复HTTP头部分
|
||||||
sendResponse(pcHttpResult, httpHeader, "");
|
sendResponse(pcHttpResult, httpHeader, "");
|
||||||
if (iRangeEnd - iRangeStart < 0) {
|
if (iRangeEnd - iRangeStart < 0) {
|
||||||
//file is empty!
|
//文件时空的!
|
||||||
return eHttpCode;
|
return eHttpCode;
|
||||||
}
|
}
|
||||||
|
//回复Content部分
|
||||||
//send the file
|
|
||||||
std::shared_ptr<int64_t> piLeft(new int64_t(iRangeEnd - iRangeStart + 1));
|
std::shared_ptr<int64_t> piLeft(new int64_t(iRangeEnd - iRangeStart + 1));
|
||||||
std::shared_ptr<FILE> pFilePtr(pFile, [](FILE *pFp) {
|
std::shared_ptr<FILE> pFilePtr(pFile, [](FILE *pFp) {
|
||||||
fclose(pFp);
|
fclose(pFp);
|
||||||
@ -398,35 +400,32 @@ inline HttpSession::KeyValue HttpSession::makeHttpHeader(bool bClose, int64_t iC
|
|||||||
}
|
}
|
||||||
return headerOut;
|
return headerOut;
|
||||||
}
|
}
|
||||||
inline HttpSession::HttpCode HttpSession::Handle_Req_POST() {
|
|
||||||
int iContentLen = atoi(m_parser["Content-Length"].data());
|
|
||||||
/*if (!iContentLen) {
|
|
||||||
return Http_failed;
|
|
||||||
}*/
|
|
||||||
if ((int) m_strRcvBuf.size() < iContentLen) {
|
|
||||||
return Http_moreData; //需要更多数据
|
|
||||||
}
|
|
||||||
auto strContent = m_strRcvBuf.substr(0, iContentLen);
|
|
||||||
m_strRcvBuf.erase(0, iContentLen);
|
|
||||||
|
|
||||||
string strUrl = strCoding::UrlUTF8Decode(m_parser.Url());
|
string HttpSession::urlDecode(const string &str){
|
||||||
|
auto ret = strCoding::UrlUTF8Decode(str);
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static bool isGb2312 = !strcasecmp(mINI::Instance()[Config::Http::kCharSet].data(), "gb2312");
|
static bool isGb2312 = !strcasecmp(mINI::Instance()[Config::Http::kCharSet].data(), "gb2312");
|
||||||
if (isGb2312) {
|
if (isGb2312) {
|
||||||
strUrl = strCoding::UTF8ToGB2312(strUrl);
|
ret = strCoding::UTF8ToGB2312(ret);
|
||||||
}
|
}
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
string strConType = m_parser["Connection"];
|
inline void HttpSession::urlDecode(Parser &parser){
|
||||||
|
parser.setUrl(urlDecode(parser.Url()));
|
||||||
|
for(auto &pr : m_parser.getUrlArgs()){
|
||||||
|
const_cast<string &>(pr.second) = urlDecode(pr.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool HttpSession::emitHttpEvent(bool doInvoke){
|
||||||
|
///////////////////是否断开本链接///////////////////////
|
||||||
static uint32_t reqCnt = mINI::Instance()[Config::Http::kMaxReqCount].as<uint32_t>();
|
static uint32_t reqCnt = mINI::Instance()[Config::Http::kMaxReqCount].as<uint32_t>();
|
||||||
bool bClose = (strcasecmp(strConType.data(),"close") == 0) && ( ++m_iReqCnt < reqCnt);
|
bool bClose = (strcasecmp(m_parser["Connection"].data(),"close") == 0) && ( ++m_iReqCnt < reqCnt);
|
||||||
m_parser.setUrl(strUrl);
|
/////////////////////异步回复Invoker///////////////////////////////
|
||||||
m_parser.setContent(strContent);
|
|
||||||
|
|
||||||
weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
|
weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
|
||||||
HttpResponseInvoker invoker = [weakSelf,bClose](const string &codeOut,
|
HttpResponseInvoker invoker = [weakSelf,bClose](const string &codeOut, const KeyValue &headerOut, const string &contentOut){
|
||||||
const KeyValue &headerOut,
|
|
||||||
const string &contentOut){
|
|
||||||
auto strongSelf = weakSelf.lock();
|
auto strongSelf = weakSelf.lock();
|
||||||
if(!strongSelf) {
|
if(!strongSelf) {
|
||||||
return;
|
return;
|
||||||
@ -436,27 +435,40 @@ inline HttpSession::HttpCode HttpSession::Handle_Req_POST() {
|
|||||||
if(!strongSelf) {
|
if(!strongSelf) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
strongSelf->responseDelay(bClose,
|
strongSelf->responseDelay(bClose,codeOut,headerOut,contentOut);
|
||||||
const_cast<string &>(codeOut),
|
|
||||||
const_cast<KeyValue &>(headerOut),
|
|
||||||
const_cast<string &>(contentOut));
|
|
||||||
if(bClose){
|
if(bClose){
|
||||||
strongSelf->shutdown();
|
strongSelf->shutdown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
if(!NoticeCenter::Instance().emitEvent(Config::Broadcast::kBroadcastHttpRequest,m_parser,invoker)){
|
///////////////////广播HTTP事件///////////////////////////
|
||||||
|
bool consumed = false;//该事件是否被消费
|
||||||
|
NoticeCenter::Instance().emitEvent(Config::Broadcast::kBroadcastHttpRequest,m_parser,invoker,(bool &)consumed);
|
||||||
|
if(!consumed && doInvoke){
|
||||||
|
//该事件无人消费,所以返回404
|
||||||
invoker("404 Not Found",KeyValue(),"");
|
invoker("404 Not Found",KeyValue(),"");
|
||||||
}
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
inline HttpSession::HttpCode HttpSession::Handle_Req_POST() {
|
||||||
|
//////////////获取HTTP POST Content/////////////
|
||||||
|
int iContentLen = atoi(m_parser["Content-Length"].data());
|
||||||
|
if ((int) m_strRcvBuf.size() < iContentLen) {
|
||||||
|
return Http_moreData; //需要更多数据
|
||||||
|
}
|
||||||
|
m_parser.setContent(m_strRcvBuf.substr(0, iContentLen));
|
||||||
|
m_strRcvBuf.erase(0, iContentLen);
|
||||||
|
//广播事件
|
||||||
|
emitHttpEvent(true);
|
||||||
return Http_success;
|
return Http_success;
|
||||||
}
|
}
|
||||||
void HttpSession::responseDelay(bool bClose,string &codeOut,KeyValue &headerOut, string &contentOut){
|
void HttpSession::responseDelay(bool bClose,const string &codeOut,const KeyValue &headerOut, const string &contentOut){
|
||||||
if(codeOut.empty()){
|
if(codeOut.empty()){
|
||||||
sendNotFound(bClose);
|
sendNotFound(bClose);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto headerOther=makeHttpHeader(bClose,contentOut.size(),"text/json");
|
auto headerOther=makeHttpHeader(bClose,contentOut.size(),"text/plain");
|
||||||
headerOut.insert(headerOther.begin(), headerOther.end());
|
const_cast<KeyValue &>(headerOut).insert(headerOther.begin(), headerOther.end());
|
||||||
sendResponse(codeOut.data(), headerOut, contentOut);
|
sendResponse(codeOut.data(), headerOut, contentOut);
|
||||||
}
|
}
|
||||||
inline void HttpSession::sendNotFound(bool bClose) {
|
inline void HttpSession::sendNotFound(bool bClose) {
|
||||||
|
@ -51,6 +51,8 @@ public:
|
|||||||
virtual void onRecv(const Socket::Buffer::Ptr &) override;
|
virtual void onRecv(const Socket::Buffer::Ptr &) override;
|
||||||
virtual void onError(const SockException &err) override;
|
virtual void onError(const SockException &err) override;
|
||||||
virtual void onManager() override;
|
virtual void onManager() override;
|
||||||
|
|
||||||
|
static string urlDecode(const string &str);
|
||||||
protected:
|
protected:
|
||||||
void onRecv(const char *data,int size);
|
void onRecv(const char *data,int size);
|
||||||
private:
|
private:
|
||||||
@ -73,11 +75,13 @@ private:
|
|||||||
inline HttpCode parserHttpReq(const string &);
|
inline HttpCode parserHttpReq(const string &);
|
||||||
inline HttpCode Handle_Req_GET();
|
inline HttpCode Handle_Req_GET();
|
||||||
inline HttpCode Handle_Req_POST();
|
inline HttpCode Handle_Req_POST();
|
||||||
|
inline bool emitHttpEvent(bool doInvoke);
|
||||||
|
inline void urlDecode(Parser &parser);
|
||||||
inline bool makeMeun(const string &strFullPath, string &strRet);
|
inline bool makeMeun(const string &strFullPath, string &strRet);
|
||||||
inline void sendNotFound(bool bClose);
|
inline void sendNotFound(bool bClose);
|
||||||
inline void sendResponse(const char *pcStatus,const KeyValue &header,const string &strContent);
|
inline void sendResponse(const char *pcStatus,const KeyValue &header,const string &strContent);
|
||||||
inline static KeyValue makeHttpHeader(bool bClose=false,int64_t iContentSize=-1,const char *pcContentType="text/html");
|
inline static KeyValue makeHttpHeader(bool bClose=false,int64_t iContentSize=-1,const char *pcContentType="text/html");
|
||||||
void responseDelay(bool bClose,string &codeOut,KeyValue &headerOut, string &contentOut);
|
void responseDelay(bool bClose,const string &codeOut,const KeyValue &headerOut, const string &contentOut);
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace Http */
|
} /* namespace Http */
|
||||||
|
@ -88,29 +88,31 @@ typedef map<string,string,StrCaseCompare> StrCaseMap;
|
|||||||
|
|
||||||
class Parser {
|
class Parser {
|
||||||
public:
|
public:
|
||||||
Parser() {
|
Parser() {}
|
||||||
}
|
virtual ~Parser() {}
|
||||||
virtual ~Parser() {
|
|
||||||
}
|
|
||||||
void Parse(const char *buf) {
|
void Parse(const char *buf) {
|
||||||
//解析
|
//解析
|
||||||
const char *start = buf;
|
const char *start = buf;
|
||||||
string line;
|
|
||||||
string field;
|
|
||||||
string value;
|
|
||||||
Clear();
|
Clear();
|
||||||
while (true) {
|
while (true) {
|
||||||
line = FindField(start, NULL, "\r\n");
|
auto line = FindField(start, NULL, "\r\n");
|
||||||
if (line.size() == 0) {
|
if (line.size() == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (start == buf) {
|
if (start == buf) {
|
||||||
m_strMethod = FindField(line.c_str(), NULL, " ");
|
m_strMethod = FindField(line.c_str(), NULL, " ");
|
||||||
m_strUrl = FindField(line.c_str(), " ", " ");
|
auto full_url = FindField(line.c_str(), " ", " ");
|
||||||
m_strTail = FindField(line.c_str(), (m_strUrl + " ").c_str(), NULL);
|
auto args_pos = full_url.find('?');
|
||||||
|
if(args_pos != string::npos){
|
||||||
|
m_strUrl = full_url.substr(0,args_pos);
|
||||||
|
m_mapUrlArgs = parseArgs(full_url.substr(args_pos + 1 ));
|
||||||
|
}else{
|
||||||
|
m_strUrl = full_url;
|
||||||
|
}
|
||||||
|
m_strTail = FindField(line.c_str(), (full_url + " ").c_str(), NULL);
|
||||||
} else {
|
} else {
|
||||||
field = FindField(line.c_str(), NULL, ": ");
|
auto field = FindField(line.c_str(), NULL, ": ");
|
||||||
value = FindField(line.c_str(), ": ", NULL);
|
auto value = FindField(line.c_str(), ": ", NULL);
|
||||||
if (field.size() != 0) {
|
if (field.size() != 0) {
|
||||||
m_mapValues[field] = value;
|
m_mapValues[field] = value;
|
||||||
}
|
}
|
||||||
@ -151,6 +153,7 @@ public:
|
|||||||
m_strTail.clear();
|
m_strTail.clear();
|
||||||
m_strContent.clear();
|
m_strContent.clear();
|
||||||
m_mapValues.clear();
|
m_mapValues.clear();
|
||||||
|
m_mapUrlArgs.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setUrl(const string& url) {
|
void setUrl(const string& url) {
|
||||||
@ -163,7 +166,40 @@ public:
|
|||||||
const StrCaseMap& getValues() const {
|
const StrCaseMap& getValues() const {
|
||||||
return m_mapValues;
|
return m_mapValues;
|
||||||
}
|
}
|
||||||
|
const StrCaseMap& getUrlArgs() const {
|
||||||
|
return m_mapUrlArgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
//注意:当字符串为空时,也会返回一个空字符串
|
||||||
|
static vector<string> split(const string& s, const char *delim) {
|
||||||
|
size_t last = 0;
|
||||||
|
size_t index = s.find_first_of(delim, last);
|
||||||
|
vector<string> ret;
|
||||||
|
while (index != string::npos) {
|
||||||
|
ret.push_back(s.substr(last, index - last));
|
||||||
|
last = index + 1;
|
||||||
|
index = s.find_first_of(delim, last);
|
||||||
|
}
|
||||||
|
if (index - last > 0) {
|
||||||
|
ret.push_back(s.substr(last, index - last));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static StrCaseMap parseArgs(const string &str){
|
||||||
|
StrCaseMap ret;
|
||||||
|
auto arg_vec = split(str, "&");
|
||||||
|
for (string &key_val : arg_vec) {
|
||||||
|
if (!key_val.size()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto key_val_vec = split(key_val, "=");
|
||||||
|
if (key_val_vec.size() >= 2) {
|
||||||
|
ret[key_val_vec[0]] = key_val_vec[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
string m_strMethod;
|
string m_strMethod;
|
||||||
string m_strUrl;
|
string m_strUrl;
|
||||||
@ -171,7 +207,7 @@ private:
|
|||||||
string m_strContent;
|
string m_strContent;
|
||||||
string m_strNull;
|
string m_strNull;
|
||||||
StrCaseMap m_mapValues;
|
StrCaseMap m_mapValues;
|
||||||
|
StrCaseMap m_mapUrlArgs;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
136
tests/test_httpApi.cpp
Normal file
136
tests/test_httpApi.cpp
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 xiongziliang <771730766@qq.com>
|
||||||
|
*
|
||||||
|
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include "Common/config.h"
|
||||||
|
|
||||||
|
#ifdef ENABLE_OPENSSL
|
||||||
|
#include "Util/SSLBox.h"
|
||||||
|
#include "Http/HttpsSession.h"
|
||||||
|
#endif//ENABLE_OPENSSL
|
||||||
|
|
||||||
|
#include "Util/File.h"
|
||||||
|
#include "Util/logger.h"
|
||||||
|
#include "Util/onceToken.h"
|
||||||
|
#include "Network/TcpServer.h"
|
||||||
|
#include "Poller/EventPoller.h"
|
||||||
|
#include "Thread/WorkThreadPool.h"
|
||||||
|
#include "Util/NoticeCenter.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace ZL::Util;
|
||||||
|
using namespace ZL::Http;
|
||||||
|
using namespace ZL::Thread;
|
||||||
|
using namespace ZL::Network;
|
||||||
|
|
||||||
|
static onceToken s_token([](){
|
||||||
|
NoticeCenter::Instance().addListener(nullptr,Config::Broadcast::kBroadcastHttpRequest,[](BroadcastHttpRequestArgs){
|
||||||
|
//const Parser &parser,HttpSession::HttpResponseInvoker &invoker,bool &consumed
|
||||||
|
if(strstr(parser.Url().data(),"/api/") != parser.Url().data()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//url以"/api/起始,说明是http api"
|
||||||
|
consumed = true;//该http请求已被消费
|
||||||
|
|
||||||
|
_StrPrinter printer;
|
||||||
|
////////////////method////////////////////
|
||||||
|
printer << "\r\nmethod:\r\n\t" << parser.Method();
|
||||||
|
////////////////url/////////////////
|
||||||
|
printer << "\r\nurl:\r\n\t" << parser.Url();
|
||||||
|
////////////////protocol/////////////////
|
||||||
|
printer << "\r\nprotocol:\r\n\t" << parser.Tail();
|
||||||
|
///////////////args//////////////////
|
||||||
|
printer << "\r\nargs:\r\n";
|
||||||
|
for(auto &pr : parser.getUrlArgs()){
|
||||||
|
printer << "\t" << pr.first << " : " << pr.second << "\r\n";
|
||||||
|
}
|
||||||
|
///////////////header//////////////////
|
||||||
|
printer << "\r\nheader:\r\n";
|
||||||
|
for(auto &pr : parser.getValues()){
|
||||||
|
printer << "\t" << pr.first << " : " << pr.second << "\r\n";
|
||||||
|
}
|
||||||
|
////////////////content/////////////////
|
||||||
|
printer << "\r\ncontent:\r\n" << parser.Content();
|
||||||
|
auto contentOut = printer << endl;
|
||||||
|
|
||||||
|
////////////////我们测算异步回复,当然你也可以同步回复/////////////////
|
||||||
|
EventPoller::Instance().sync([invoker,contentOut](){
|
||||||
|
HttpSession::KeyValue headerOut;
|
||||||
|
//你可以自定义header,如果跟默认header重名,则会覆盖之
|
||||||
|
//默认header有:Server,Connection,Date,Content-Type,Content-Length
|
||||||
|
//请勿覆盖Connection、Content-Length键
|
||||||
|
//键名覆盖时不区分大小写
|
||||||
|
headerOut["TestHeader"] = "HeaderValue";
|
||||||
|
invoker("200 OK",headerOut,contentOut);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, nullptr);
|
||||||
|
|
||||||
|
int main(int argc,char *argv[]){
|
||||||
|
//设置退出信号处理函数
|
||||||
|
signal(SIGINT, [](int){EventPoller::Instance().shutdown();});
|
||||||
|
//设置日志
|
||||||
|
Logger::Instance().add(std::make_shared<ConsoleChannel>("stdout", LTrace));
|
||||||
|
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
|
||||||
|
//加载配置文件,如果配置文件不存在就创建一个
|
||||||
|
Config::loadIniConfig();
|
||||||
|
|
||||||
|
#ifdef ENABLE_OPENSSL
|
||||||
|
//请把证书"test_httpApi.pem"放置在本程序可执行程序同目录下
|
||||||
|
try{
|
||||||
|
//加载证书,证书包含公钥和私钥
|
||||||
|
SSL_Initor::Instance().loadServerPem((exePath() + ".pem").data());
|
||||||
|
}catch(...){
|
||||||
|
FatalL << "请把证书:" << (exeName() + ".pem") << "放置在本程序可执行程序同目录下:" << exeDir() << endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif //ENABLE_OPENSSL
|
||||||
|
|
||||||
|
//开启http服务器
|
||||||
|
TcpServer<HttpSession>::Ptr httpSrv(new TcpServer<HttpSession>());
|
||||||
|
httpSrv->start(mINI::Instance()[Config::Http::kPort]);//默认80
|
||||||
|
|
||||||
|
#ifdef ENABLE_OPENSSL
|
||||||
|
//如果支持ssl,还可以开启https服务器
|
||||||
|
TcpServer<HttpsSession>::Ptr httpsSrv(new TcpServer<HttpsSession>());
|
||||||
|
httpsSrv->start(mINI::Instance()[Config::Http::kSSLPort]);//默认443
|
||||||
|
#endif //ENABLE_OPENSSL
|
||||||
|
|
||||||
|
InfoL << "你可以在浏览器输入:http://127.0.0.1/api/my_api?key0=val0&key1=参数1" << endl;
|
||||||
|
|
||||||
|
EventPoller::Instance().runLoop();
|
||||||
|
|
||||||
|
static onceToken s_token(nullptr,[]() {
|
||||||
|
//TcpServer用到了WorkThreadPool
|
||||||
|
WorkThreadPool::Destory();
|
||||||
|
EventPoller::Destory();
|
||||||
|
Logger::Destory();
|
||||||
|
});
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user