优化http服务器

This commit is contained in:
xiongziliang 2019-10-29 00:35:44 +08:00
parent fd10ef1187
commit 43e1cc69a8
3 changed files with 177 additions and 155 deletions

View File

@ -616,12 +616,7 @@ void installWebApi() {
//测试url http://127.0.0.1/index/api/downloadBin //测试url http://127.0.0.1/index/api/downloadBin
API_REGIST_INVOKER(api,downloadBin,{ API_REGIST_INVOKER(api,downloadBin,{
CHECK_SECRET(); CHECK_SECRET();
auto body = std::make_shared<HttpFileBody>(exePath()); invoker.responseFile(headerIn,StrCaseMap(),exePath());
if(!body->remainSize()){
invoker("404 Not Found", HttpSession::KeyValue(), "");
return;
}
invoker("200 OK", HttpSession::KeyValue(), body);
}); });
////////////以下是注册的Hook API//////////// ////////////以下是注册的Hook API////////////

View File

@ -59,8 +59,8 @@ string dateStr() {
strftime(buf, sizeof buf, "%a, %b %d %Y %H:%M:%S GMT", gmtime(&tt)); strftime(buf, sizeof buf, "%a, %b %d %Y %H:%M:%S GMT", gmtime(&tt));
return buf; return buf;
} }
static const char*
get_mime_type(const char* name) { const char *HttpSession::get_mime_type(const char *name) {
const char *dot; const char *dot;
dot = strrchr(name, '.'); dot = strrchr(name, '.');
static HttpSession::KeyValue mapType; static HttpSession::KeyValue mapType;
@ -68,6 +68,9 @@ get_mime_type(const char* name) {
mapType.emplace(".html", "text/html"); mapType.emplace(".html", "text/html");
mapType.emplace(".htm", "text/html"); mapType.emplace(".htm", "text/html");
mapType.emplace(".mp4", "video/mp4"); mapType.emplace(".mp4", "video/mp4");
mapType.emplace(".mkv", "video/x-matroska");
mapType.emplace(".rmvb", "application/vnd.rn-realmedia");
mapType.emplace(".rm", "application/vnd.rn-realmedia");
mapType.emplace(".m3u8", "application/vnd.apple.mpegurl"); mapType.emplace(".m3u8", "application/vnd.apple.mpegurl");
mapType.emplace(".jpg", "image/jpeg"); mapType.emplace(".jpg", "image/jpeg");
mapType.emplace(".jpeg", "image/jpeg"); mapType.emplace(".jpeg", "image/jpeg");
@ -132,6 +135,54 @@ HttpResponseInvokerImp::HttpResponseInvokerImp(const HttpResponseInvokerImp::Htt
}; };
} }
void HttpResponseInvokerImp::responseFile(const StrCaseMap &requestHeader,
const StrCaseMap &responseHeader,
const string &filePath) const {
try {
struct stat tFileStat;
if (filePath.empty() || 0 != stat(filePath.data(), &tFileStat)) {
//文件不存在
throw std::runtime_error("file not exited");
}
auto &strRange = const_cast<StrCaseMap &>(requestHeader)["Range"];
int64_t iRangeStart = 0, iRangeEnd = 0;
iRangeStart = atoll(FindField(strRange.data(), "bytes=", "-").data());
iRangeEnd = atoll(FindField(strRange.data(), "-", "\r\n").data());
if (iRangeEnd == 0) {
iRangeEnd = tFileStat.st_size - 1;
}
const char *pcHttpResult = NULL;
StrCaseMap &httpHeader = const_cast<StrCaseMap&>(responseHeader);
if (strRange.size() == 0) {
//全部下载
pcHttpResult = "200 OK";
} else {
//分节下载
pcHttpResult = "206 Partial Content";
//分节下载返回Content-Range头
httpHeader.emplace("Content-Range", StrPrinter << "bytes " << iRangeStart << "-" << iRangeEnd << "/" << tFileStat.st_size << endl);
}
std::shared_ptr<FILE> fp(fopen(filePath.data(), "rb"), [](FILE *fp) {
if (fp) {
fclose(fp);
}
});
if (!fp) {
//打开文件失败
throw std::runtime_error(StrPrinter << "open file not failed:" << get_uv_errmsg(false));
}
//回复文件
HttpBody::Ptr fileBody = std::make_shared<HttpFileBody>(fp, iRangeStart, iRangeEnd - iRangeStart + 1);
(*this)(pcHttpResult, httpHeader, fileBody);
}catch (std::exception &ex){
(*this)("404 Not Found", responseHeader, ex.what());
}
}
HttpResponseInvokerImp::operator bool(){ HttpResponseInvokerImp::operator bool(){
return _lambad.operator bool(); return _lambad.operator bool();
} }
@ -163,7 +214,7 @@ int64_t HttpSession::onRecvHeader(const char *header,uint64_t len) {
string cmd = _parser.Method(); string cmd = _parser.Method();
auto it = g_mapCmdIndex.find(cmd); auto it = g_mapCmdIndex.find(cmd);
if (it == g_mapCmdIndex.end()) { if (it == g_mapCmdIndex.end()) {
sendResponse("403 Forbidden", makeHttpHeader(true), ""); sendResponse("403 Forbidden", true);
shutdown(SockException(Err_shutdown,StrPrinter << "403 Forbidden:" << cmd)); shutdown(SockException(Err_shutdown,StrPrinter << "403 Forbidden:" << cmd));
return 0; return 0;
} }
@ -248,7 +299,7 @@ bool HttpSession::checkWebSocket(){
} }
auto Sec_WebSocket_Accept = encodeBase64(SHA1::encode_bin(Sec_WebSocket_Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")); auto Sec_WebSocket_Accept = encodeBase64(SHA1::encode_bin(Sec_WebSocket_Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
KeyValue headerOut = makeHttpHeader(); KeyValue headerOut;
headerOut["Upgrade"] = "websocket"; headerOut["Upgrade"] = "websocket";
headerOut["Connection"] = "Upgrade"; headerOut["Connection"] = "Upgrade";
headerOut["Sec-WebSocket-Accept"] = Sec_WebSocket_Accept; headerOut["Sec-WebSocket-Accept"] = Sec_WebSocket_Accept;
@ -258,7 +309,7 @@ bool HttpSession::checkWebSocket(){
auto res_cb = [this,headerOut](){ auto res_cb = [this,headerOut](){
_flv_over_websocket = true; _flv_over_websocket = true;
sendResponse("101 Switching Protocols",headerOut,""); sendResponse("101 Switching Protocols",false,nullptr,headerOut,nullptr,false);
}; };
//判断是否为websocket-flv //判断是否为websocket-flv
@ -269,11 +320,11 @@ bool HttpSession::checkWebSocket(){
//如果checkLiveFlvStream返回false,则代表不是websocket-flv而是普通的websocket连接 //如果checkLiveFlvStream返回false,则代表不是websocket-flv而是普通的websocket连接
if(!onWebSocketConnect(_parser)){ if(!onWebSocketConnect(_parser)){
sendResponse("501 Not Implemented",headerOut,""); sendResponse("501 Not Implemented",true, nullptr, headerOut);
shutdown(SockException(Err_shutdown,"WebSocket server not implemented")); shutdown(SockException(Err_shutdown,"WebSocket server not implemented"));
return true; return true;
} }
sendResponse("101 Switching Protocols",headerOut,""); sendResponse("101 Switching Protocols",false, nullptr,headerOut);
return true; return true;
} }
//http-flv 链接格式:http://vhost-url:port/app/streamid.flv?key1=value1&key2=value2 //http-flv 链接格式:http://vhost-url:port/app/streamid.flv?key1=value1&key2=value2
@ -320,14 +371,14 @@ bool HttpSession::checkLiveFlvStream(const function<void()> &cb){
auto onRes = [this,rtmp_src,cb](const string &err){ auto onRes = [this,rtmp_src,cb](const string &err){
bool authSuccess = err.empty(); bool authSuccess = err.empty();
if(!authSuccess){ if(!authSuccess){
sendResponse("401 Unauthorized", makeHttpHeader(true,err.size()),err); sendResponse("401 Unauthorized", true, nullptr, KeyValue(), std::make_shared<HttpStringBody>(err));
shutdown(SockException(Err_shutdown,StrPrinter << "401 Unauthorized:" << err)); shutdown(SockException(Err_shutdown,StrPrinter << "401 Unauthorized:" << err));
return ; return ;
} }
if(!cb) { if(!cb) {
//找到rtmp源发送http头负载后续发送 //找到rtmp源发送http头负载后续发送
sendResponse("200 OK", makeHttpHeader(false, -1, get_mime_type(".flv")), ""); sendResponse("200 OK", false, get_mime_type(".flv"),KeyValue(),nullptr,false);
}else{ }else{
cb(); cb();
} }
@ -571,11 +622,11 @@ void HttpSession::Handle_Req_GET(int64_t &content_len) {
if(!errMsg.empty()){ if(!errMsg.empty()){
const_cast<string &>(strMeun) = errMsg; const_cast<string &>(strMeun) = errMsg;
} }
auto headerOut = makeHttpHeader(bClose,strMeun.size()); KeyValue headerOut;
if(cookie){ if(cookie){
headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get<string>()); headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get<string>());
} }
sendResponse(errMsg.empty() ? "200 OK" : "401 Unauthorized" , headerOut, strMeun); sendResponse(errMsg.empty() ? "200 OK" : "401 Unauthorized" ,bClose, "text/html", headerOut, std::make_shared<HttpStringBody>(strMeun));
throw SockException(bClose ? Err_shutdown : Err_success,"close connection after access folder"); throw SockException(bClose ? Err_shutdown : Err_success,"close connection after access folder");
}); });
return; return;
@ -594,64 +645,28 @@ void HttpSession::Handle_Req_GET(int64_t &content_len) {
sendNotFound(bClose); sendNotFound(bClose);
throw SockException(bClose ? Err_shutdown : Err_success,"close connection after send 404 not found on file"); throw SockException(bClose ? Err_shutdown : Err_success,"close connection after send 404 not found on file");
} }
//文件智能指针,防止退出时未关闭
std::shared_ptr<FILE> pFilePtr(fopen(strFile.data(), "rb"), [](FILE *pFile) {
if(pFile){
fclose(pFile);
}
});
if (!pFilePtr) {
//打开文件失败
sendNotFound(bClose);
throw SockException(bClose ? Err_shutdown : Err_success,"close connection after send 404 not found on open file failed");
}
auto parser = _parser; auto parser = _parser;
//判断是否有权限访问该文件 //判断是否有权限访问该文件
canAccessPath(_parser.Url(),false,[this,parser,tFileStat,pFilePtr,bClose,strFile](const string &errMsg,const HttpServerCookie::Ptr &cookie){ canAccessPath(_parser.Url(),false,[this,parser,tFileStat,bClose,strFile](const string &errMsg,const HttpServerCookie::Ptr &cookie){
if(!errMsg.empty()){ if(!errMsg.empty()){
auto headerOut = makeHttpHeader(bClose,errMsg.size()); KeyValue headerOut;
if(cookie){ if(cookie){
headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get<string>()); headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get<string>());
} }
sendResponse("401 Unauthorized" , headerOut, errMsg); sendResponse("401 Unauthorized" ,bClose, nullptr, headerOut, std::make_shared<HttpStringBody>(errMsg));
throw SockException(bClose ? Err_shutdown : Err_success,"close connection after access file failed"); throw SockException(bClose ? Err_shutdown : Err_success,"close connection after access file failed");
} }
//判断是不是分节下载 KeyValue httpHeader;
auto &strRange = parser["Range"];
int64_t iRangeStart = 0, iRangeEnd = 0;
iRangeStart = atoll(FindField(strRange.data(), "bytes=", "-").data());
iRangeEnd = atoll(FindField(strRange.data(), "-", "\r\n").data());
if (iRangeEnd == 0) {
iRangeEnd = tFileStat.st_size - 1;
}
auto httpHeader = makeHttpHeader(bClose, iRangeEnd - iRangeStart + 1, get_mime_type(strFile.data()));
const char *pcHttpResult = NULL;
if (strRange.size() == 0) {
//全部下载
pcHttpResult = "200 OK";
} else {
//分节下载
pcHttpResult = "206 Partial Content";
//分节下载返回Content-Range头
httpHeader.emplace("Content-Range",StrPrinter<<"bytes " << iRangeStart << "-" << iRangeEnd << "/" << tFileStat.st_size<< endl);
}
if(cookie){ if(cookie){
httpHeader["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get<string>()); httpHeader["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get<string>());
} }
if (iRangeEnd - iRangeStart < 0) { HttpResponseInvoker invoker = [this,bClose,&strFile](const string &codeOut, const KeyValue &headerOut, const HttpBody::Ptr &body){
sendResponse(pcHttpResult,httpHeader,""); sendResponse(codeOut.data(), bClose, get_mime_type(strFile.data()), headerOut, body);
//文件是空的! };
throw SockException(bClose ? Err_shutdown : Err_success,"close connection after access file"); invoker.responseFile(parser.getValues(),httpHeader,strFile);
}
//先回复HTTP头部分
HttpBody::Ptr fileBody = std::make_shared<HttpFileBody>(pFilePtr,iRangeStart,iRangeEnd - iRangeStart + 1);
sendResponse(pcHttpResult,httpHeader,fileBody,bClose);
}); });
} }
@ -754,22 +769,77 @@ bool makeMeun(const string &httpPath,const string &strFullPath, string &strRet)
return true; return true;
} }
void HttpSession::sendResponse(const char* pcStatus, const KeyValue& header, const string& strContent) {
sendResponse(pcStatus, header,strContent.empty() ? nullptr : std::make_shared<HttpStringBody>(strContent), false); void HttpSession::sendResponse(const char *pcStatus,
bool bClose,
const char *pcContentType,
const HttpSession::KeyValue &header,
const HttpBody::Ptr &body,
bool set_content_len ){
GET_CONFIG(string,charSet,Http::kCharSet);
GET_CONFIG(uint32_t,keepAliveSec,Http::kKeepAliveSecond);
GET_CONFIG(uint32_t,reqCnt,Http::kMaxReqCount);
//body默认为空
int64_t size = 0;
if (body && body->remainSize()) {
//有body获取body大小
size = body->remainSize();
if (size >= INT64_MAX) {
//不固定长度的body那么不设置content-length字段
size = -1;
}
}
if(!set_content_len || size == -1){
//如果是不定长度body或者不设置conten-length,
//那么一定是Keep-Alive类型
bClose = false;
}
HttpSession::KeyValue &headerOut = const_cast<HttpSession::KeyValue &>(header);
headerOut.emplace("Date", dateStr());
headerOut.emplace("Server", SERVER_NAME);
headerOut.emplace("Connection", bClose ? "close" : "keep-alive");
if(!bClose){
headerOut.emplace("Keep-Alive",StrPrinter << "timeout=" << keepAliveSec << ", max=" << reqCnt << endl);
}
if(!_origin.empty()){
//设置跨域
headerOut.emplace("Access-Control-Allow-Origin",_origin);
headerOut.emplace("Access-Control-Allow-Credentials", "true");
}
if(set_content_len && size >= 0){
//文件长度为定值或者,且不是http-flv设置Content-Length
headerOut.emplace("Content-Length", StrPrinter << size <<endl);
}
if(size && !pcContentType){
//有body时设置缺省类型
pcContentType = "text/plain";
}
if(size && pcContentType){
//有body时设置文件类型
auto strContentType = StrPrinter << pcContentType << "; charset=" << charSet << endl;
headerOut.emplace("Content-Type",strContentType);
} }
void HttpSession::sendResponse(const char *pcStatus,const KeyValue &header,const HttpBody::Ptr &body,bool bClose){
//发送http头 //发送http头
_StrPrinter printer; _StrPrinter printer;
printer << "HTTP/1.1 " << pcStatus << "\r\n"; printer << "HTTP/1.1 " << pcStatus << "\r\n";
for (auto &pr : header) { for (auto &pr : header) {
printer << pr.first << ": " << pr.second << "\r\n"; printer << pr.first << ": " << pr.second << "\r\n";
} }
printer << "\r\n"; printer << "\r\n";
send(printer << endl); send(printer << endl);
_ticker.resetTime(); _ticker.resetTime();
if(!body || !body->remainSize()){ if(!size){
//没有body //没有body
if(bClose){ if(bClose){
shutdown(SockException(Err_shutdown,"close connection after send http header completed")); shutdown(SockException(Err_shutdown,"close connection after send http header completed"));
@ -832,34 +902,6 @@ void HttpSession::sendResponse(const char *pcStatus,const KeyValue &header,const
_sock->setOnFlush(onFlush); _sock->setOnFlush(onFlush);
} }
HttpSession::KeyValue HttpSession::makeHttpHeader(bool bClose, int64_t iContentSize,const char* pcContentType) {
KeyValue headerOut;
GET_CONFIG(string,charSet,Http::kCharSet);
GET_CONFIG(uint32_t,keepAliveSec,Http::kKeepAliveSecond);
GET_CONFIG(uint32_t,reqCnt,Http::kMaxReqCount);
headerOut.emplace("Date", dateStr());
headerOut.emplace("Server", SERVER_NAME);
headerOut.emplace("Connection", bClose ? "close" : "keep-alive");
if(!bClose){
headerOut.emplace("Keep-Alive",StrPrinter << "timeout=" << keepAliveSec << ", max=" << reqCnt << endl);
}
if(pcContentType){
auto strContentType = StrPrinter << pcContentType << "; charset=" << charSet << endl;
headerOut.emplace("Content-Type",strContentType);
}
if(iContentSize >= 0){
headerOut.emplace("Content-Length", StrPrinter<<iContentSize<<endl);
}
if(!_origin.empty()){
headerOut.emplace("Access-Control-Allow-Origin",_origin);
headerOut.emplace("Access-Control-Allow-Credentials", "true");
}
return headerOut;
}
string HttpSession::urlDecode(const string &str){ string HttpSession::urlDecode(const string &str){
auto ret = strCoding::UrlDecode(str); auto ret = strCoding::UrlDecode(str);
#ifdef _WIN32 #ifdef _WIN32
@ -904,23 +946,7 @@ bool HttpSession::emitHttpEvent(bool doInvoke){
return; return;
} }
//body默认为空 strongSelf->sendResponse(codeOut.data(), bClose, nullptr, headerOut, body);
int64_t size = 0;
if (body && body->remainSize()) {
//有body获取body大小
size = body->remainSize();
if (size >= INT64_MAX) {
//不固定长度的body那么不设置content-length字段
size = -1;
}
}
auto headerOther = strongSelf->makeHttpHeader(bClose, size, "text/plain");
for (auto &pr : headerOther) {
//添加默认http头默认http头不能覆盖用户自定义的头
const_cast<KeyValue &>(headerOut).emplace(pr.first, pr.second);
}
strongSelf->sendResponse(codeOut.data(), headerOut, body, bClose);
}); });
}; };
///////////////////广播HTTP事件/////////////////////////// ///////////////////广播HTTP事件///////////////////////////
@ -1012,7 +1038,7 @@ void HttpSession::Handle_Req_POST(int64_t &content_len) {
void HttpSession::sendNotFound(bool bClose) { void HttpSession::sendNotFound(bool bClose) {
GET_CONFIG(string,notFound,Http::kNotFound); GET_CONFIG(string,notFound,Http::kNotFound);
sendResponse("404 Not Found", makeHttpHeader(bClose, notFound.size()), notFound); sendResponse("404 Not Found", bClose,"text/html",KeyValue(),std::make_shared<HttpStringBody>(notFound));
} }
void HttpSession::setSocketFlags(){ void HttpSession::setSocketFlags(){

View File

@ -61,6 +61,7 @@ public:
void operator()(const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) const; void operator()(const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) const;
void operator()(const string &codeOut, const StrCaseMap &headerOut, const string &body) const; void operator()(const string &codeOut, const StrCaseMap &headerOut, const string &body) const;
void responseFile(const StrCaseMap &requestHeader,const StrCaseMap &responseHeader,const string &filePath) const;
operator bool(); operator bool();
private: private:
HttpResponseInvokerLambda0 _lambad; HttpResponseInvokerLambda0 _lambad;
@ -89,6 +90,7 @@ public:
virtual void onManager() override; virtual void onManager() override;
static string urlDecode(const string &str); static string urlDecode(const string &str);
static const char* get_mime_type(const char* name);
protected: protected:
//FlvMuxer override //FlvMuxer override
void onWrite(const Buffer::Ptr &data) override ; void onWrite(const Buffer::Ptr &data) override ;
@ -140,10 +142,9 @@ private:
bool emitHttpEvent(bool doInvoke); bool emitHttpEvent(bool doInvoke);
void urlDecode(Parser &parser); void urlDecode(Parser &parser);
void sendNotFound(bool bClose); void sendNotFound(bool bClose);
void sendResponse(const char *pcStatus,const KeyValue &header,const string &strContent); void sendResponse(const char *pcStatus, bool bClose, const char *pcContentType = nullptr,
void sendResponse(const char *pcStatus,const KeyValue &header,const HttpBody::Ptr &body,bool bClose); const HttpSession::KeyValue &header = HttpSession::KeyValue(),
KeyValue makeHttpHeader(bool bClose=false,int64_t iContentSize=-1,const char *pcContentType="text/html"); const HttpBody::Ptr &body = nullptr,bool set_content_len = true);
/** /**
* http客户端是否有权限访问文件的逻辑步骤 * http客户端是否有权限访问文件的逻辑步骤
* *