HttpSession.cpp 21.6 KB
Newer Older
xiongziliang committed
1
/*
xiongziliang committed
2
 * MIT License
xzl committed
3
 *
xiongziliang committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 * 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.
xzl committed
25
 */
26 27 28 29
#if !defined(_WIN32)
#include <dirent.h>
#endif //!defined(_WIN32)

xzl committed
30 31 32 33
#include <stdio.h>
#include <sys/stat.h>
#include <algorithm>

xiongziliang committed
34
#include "Common/config.h"
xzl committed
35 36 37 38 39 40
#include "strCoding.h"
#include "HttpSession.h"
#include "Util/File.h"
#include "Util/util.h"
#include "Util/TimeTicker.h"
#include "Util/onceToken.h"
xiongzilaing committed
41
#include "Util/mini.h"
xzl committed
42
#include "Util/NoticeCenter.h"
xiongziliang committed
43 44
#include "Util/base64.h"
#include "Util/SHA1.h"
xiongziliang committed
45
#include "Rtmp/utils.h"
xiongziliang committed
46
using namespace toolkit;
47

xiongziliang committed
48
namespace mediakit {
xzl committed
49

50
static int kSockFlags = SOCKET_DEFAULE_FLAGS | FLAG_MORE;
51

xzl committed
52 53 54 55 56 57 58 59 60 61
string dateStr() {
	char buf[64];
	time_t tt = time(NULL);
	strftime(buf, sizeof buf, "%a, %b %d %Y %H:%M:%S GMT", gmtime(&tt));
	return buf;
}
static const char*
get_mime_type(const char* name) {
	const char* dot;
	dot = strrchr(name, '.');
xiongziliang committed
62
	static HttpSession::KeyValue mapType;
xzl committed
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
	static onceToken token([&]() {
		mapType.emplace(".html","text/html");
		mapType.emplace(".htm","text/html");
		mapType.emplace(".mp4","video/mp4");
		mapType.emplace(".m3u8","application/vnd.apple.mpegurl");
		mapType.emplace(".jpg","image/jpeg");
		mapType.emplace(".jpeg","image/jpeg");
		mapType.emplace(".gif","image/gif");
		mapType.emplace(".png","image/png");
		mapType.emplace(".ico","image/x-icon");
		mapType.emplace(".css","text/css");
		mapType.emplace(".js","application/javascript");
		mapType.emplace(".au","audio/basic");
		mapType.emplace(".wav","audio/wav");
		mapType.emplace(".avi","video/x-msvideo");
		mapType.emplace(".mov","video/quicktime");
		mapType.emplace(".qt","video/quicktime");
		mapType.emplace(".mpeg","video/mpeg");
		mapType.emplace(".mpe","video/mpeg");
		mapType.emplace(".vrml","model/vrml");
		mapType.emplace(".wrl","model/vrml");
		mapType.emplace(".midi","audio/midi");
		mapType.emplace(".mid","audio/midi");
		mapType.emplace(".mp3","audio/mpeg");
		mapType.emplace(".ogg","application/ogg");
		mapType.emplace(".pac","application/x-ns-proxy-autoconfig");
xiongziliang committed
89
        mapType.emplace(".flv","video/x-flv");
xzl committed
90 91 92 93
	}, nullptr);
	if(!dot){
		return "text/plain";
	}
xiongziliang committed
94
	auto it = mapType.find(dot);
xzl committed
95 96 97 98 99 100 101
	if (it == mapType.end()) {
		return "text/plain";
	}
	return it->second.data();
}


xiongziliang committed
102
HttpSession::HttpSession(const Socket::Ptr &pSock) : TcpSession(pSock) {
xiongziliang committed
103 104 105
	//设置15秒发送超时时间
	pSock->setSendTimeOutSecond(15);

xiongziliang committed
106
    GET_CONFIG_AND_REGISTER(string,rootPath,Http::kRootPath);
107
    _strPath = rootPath;
xzl committed
108 109 110 111 112 113
}

HttpSession::~HttpSession() {
	//DebugL;
}

114
int64_t HttpSession::onRecvHeader(const char *header,uint64_t len) {
115
	typedef bool (HttpSession::*HttpCMDHandle)(int64_t &);
xiongziliang committed
116 117 118 119 120 121
	static unordered_map<string, HttpCMDHandle> g_mapCmdIndex;
	static onceToken token([]() {
		g_mapCmdIndex.emplace("GET",&HttpSession::Handle_Req_GET);
		g_mapCmdIndex.emplace("POST",&HttpSession::Handle_Req_POST);
	}, nullptr);

122 123 124
	_parser.Parse(header);
	urlDecode(_parser);
	string cmd = _parser.Method();
xzl committed
125 126 127 128
	auto it = g_mapCmdIndex.find(cmd);
	if (it == g_mapCmdIndex.end()) {
		WarnL << cmd;
		sendResponse("403 Forbidden", makeHttpHeader(true), "");
129 130 131 132 133 134 135 136 137
		shutdown();
		return 0;
	}

	//默认后面数据不是content而是header
	int64_t content_len = 0;
	auto &fun = it->second;
	if(!(this->*fun)(content_len)){
		shutdown();
xzl committed
138
	}
139
	//清空解析器节省内存
140
	_parser.Clear();
141 142
	//返回content长度
	return content_len;
xzl committed
143
}
144

145
void HttpSession::onRecvContent(const char *data,uint64_t len) {
146 147 148
	if(_contentCallBack){
		if(!_contentCallBack(data,len)){
			_contentCallBack = nullptr;
149 150 151 152 153
		}
	}
}

void HttpSession::onRecv(const Buffer::Ptr &pBuf) {
154
    _ticker.resetTime();
155
    input(pBuf->data(),pBuf->size());
156 157
}

xzl committed
158 159
void HttpSession::onError(const SockException& err) {
	//WarnL << err.what();
160 161
    GET_CONFIG_AND_REGISTER(uint32_t,iFlowThreshold,Broadcast::kFlowThreshold);

162
    if(_ui64TotalBytes > iFlowThreshold * 1024){
163
        NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport,
164 165 166
										   _mediaInfo,
										   _ui64TotalBytes,
										   _ticker.createdTime()/1000,
167
										   *this);
168
    }
xzl committed
169 170 171
}

void HttpSession::onManager() {
xiongziliang committed
172
    GET_CONFIG_AND_REGISTER(uint32_t,keepAliveSec,Http::kKeepAliveSecond);
173

174
    if(_ticker.elapsedTime() > keepAliveSec * 1000){
xzl committed
175
		//1分钟超时
176
		WarnL<<"HttpSession timeouted!";
xzl committed
177 178 179
		shutdown();
	}
}
xiongziliang committed
180 181 182


inline bool HttpSession::checkWebSocket(){
183
	auto Sec_WebSocket_Key = _parser["Sec-WebSocket-Key"];
184
	if(Sec_WebSocket_Key.empty()){
xiongziliang committed
185 186 187 188 189 190 191 192
		return false;
	}
	auto Sec_WebSocket_Accept = encodeBase64(SHA1::encode_bin(Sec_WebSocket_Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));

	KeyValue headerOut;
	headerOut["Upgrade"] = "websocket";
	headerOut["Connection"] = "Upgrade";
	headerOut["Sec-WebSocket-Accept"] = Sec_WebSocket_Accept;
xiongziliang committed
193 194 195
	if(!_parser["Sec-WebSocket-Protocol"].empty()){
		headerOut["Sec-WebSocket-Protocol"] = _parser["Sec-WebSocket-Protocol"];
	}
xiongziliang committed
196 197 198
	sendResponse("101 Switching Protocols",headerOut,"");
	return true;
}
199 200
//http-flv 链接格式:http://vhost-url:port/app/streamid.flv?key1=value1&key2=value2
//如果url(除去?以及后面的参数)后缀是.flv,那么表明该url是一个http-flv直播。
xiongziliang committed
201
inline bool HttpSession::checkLiveFlvStream(){
202
	auto pos = strrchr(_parser.Url().data(),'.');
xiongziliang committed
203 204 205 206
	if(!pos){
		//未找到".flv"后缀
		return false;
	}
xiongziliang committed
207
	if(strcasecmp(pos,".flv") != 0){
xiongziliang committed
208 209 210
		//未找到".flv"后缀
		return false;
	}
211
    //拼接成完整url
212 213 214
    auto fullUrl = string(HTTP_SCHEMA) + "://" + _parser["Host"] + _parser.FullUrl();
    _mediaInfo.parse(fullUrl);
    _mediaInfo._streamid.erase(_mediaInfo._streamid.size() - 4);//去除.flv后缀
215

216
	auto mediaSrc = dynamic_pointer_cast<RtmpMediaSource>(MediaSource::find(RTMP_SCHEMA,_mediaInfo._vhost,_mediaInfo._app,_mediaInfo._streamid));
217
	if(!mediaSrc){
xiongziliang committed
218
		//该rtmp源不存在
219
		return false;
xiongziliang committed
220 221
	}

222 223
    auto onRes = [this,mediaSrc](const string &err){
        bool authSuccess = err.empty();
224
        if(!authSuccess){
225
            sendResponse("401 Unauthorized", makeHttpHeader(true,err.size()),err);
226 227 228 229 230
            shutdown();
            return ;
        }
        //找到rtmp源,发送http头,负载后续发送
        sendResponse("200 OK", makeHttpHeader(false,0,get_mime_type(".flv")), "");
xiongziliang committed
231

232
        //开始发送rtmp负载
233 234
        //关闭tcp_nodelay ,优化性能
        SockUtil::setNoDelay(_sock->rawFD(),false);
235
        (*this) << SocketFlags(kSockFlags);
236

xiongziliang committed
237
		try{
238
			start(getPoller(),mediaSrc);
xiongziliang committed
239 240 241 242
		}catch (std::exception &ex){
			//该rtmp源不存在
			shutdown();
		}
243 244 245
    };

    weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
246
    Broadcast::AuthInvoker invoker = [weakSelf,onRes](const string &err){
247 248 249 250
        auto strongSelf = weakSelf.lock();
        if(!strongSelf){
            return;
        }
251
        strongSelf->async([weakSelf,onRes,err](){
252 253 254 255
            auto strongSelf = weakSelf.lock();
            if(!strongSelf){
                return;
            }
256
            onRes(err);
257 258
        });
    };
259
    auto flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed,_mediaInfo,invoker,*this);
260 261
    if(!flag){
        //该事件无人监听,默认不鉴权
262
        onRes("");
263 264
    }
    return true;
xiongziliang committed
265
}
266
inline bool HttpSession::Handle_Req_GET(int64_t &content_len) {
xiongziliang committed
267 268 269
	//先看看是否为WebSocket请求
	if(checkWebSocket()){
		content_len = -1;
270 271
		auto parserCopy = _parser;
		_contentCallBack = [this,parserCopy](const char *data,uint64_t len){
272
			onRecvWebSocketData(parserCopy,data,len);
273
			//_contentCallBack是可持续的,后面还要处理后续数据
xiongziliang committed
274 275 276 277 278
			return true;
		};
		return true;
	}

xiongziliang committed
279 280
	//先看看该http事件是否被拦截
	if(emitHttpEvent(false)){
281
		return true;
282
	}
xiongziliang committed
283

284
    //再看看是否为http-flv直播请求
xiongziliang committed
285
	if(checkLiveFlvStream()){
286
		return true;
xiongziliang committed
287
	}
288

xiongziliang committed
289
	//事件未被拦截,则认为是http下载请求
290 291
	auto fullUrl = string(HTTP_SCHEMA) + "://" + _parser["Host"] + _parser.FullUrl();
    _mediaInfo.parse(fullUrl);
292

293
	string strFile = _strPath + "/" + _mediaInfo._vhost + _parser.Url();
xiongziliang committed
294
	/////////////HTTP连接是否需要被关闭////////////////
xiongziliang committed
295
    GET_CONFIG_AND_REGISTER(uint32_t,reqCnt,Http::kMaxReqCount);
296

xiongziliang committed
297
    bool bClose = (strcasecmp(_parser["Connection"].data(),"close") == 0) || ( ++_iReqCnt > reqCnt);
xiongziliang committed
298
	//访问的是文件夹
xzl committed
299
	if (strFile.back() == '/') {
xiongziliang committed
300
		//生成文件夹菜单索引
xzl committed
301
		string strMeun;
302
		if (!makeMeun(strFile,_mediaInfo._vhost, strMeun)) {
xiongziliang committed
303
			//文件夹不存在
xzl committed
304
			sendNotFound(bClose);
305
			return !bClose;
xzl committed
306 307
		}
		sendResponse("200 OK", makeHttpHeader(bClose,strMeun.size() ), strMeun);
308
		return !bClose;
xzl committed
309
	}
xiongziliang committed
310
	//访问的是文件
xzl committed
311 312
	struct stat tFileStat;
	if (0 != stat(strFile.data(), &tFileStat)) {
xiongziliang committed
313
		//文件不存在
xzl committed
314
		sendNotFound(bClose);
315
		return !bClose;
xzl committed
316
	}
317 318 319 320 321 322 323 324
    //文件智能指针,防止退出时未关闭
    std::shared_ptr<FILE> pFilePtr(fopen(strFile.data(), "rb"), [](FILE *pFile) {
        if(pFile){
            fclose(pFile);
        }
    });

	if (!pFilePtr) {
xiongziliang committed
325
		//打开文件失败
xzl committed
326
		sendNotFound(bClose);
327
		return !bClose;
xzl committed
328
	}
329

xiongziliang committed
330
	//判断是不是分节下载
331
	auto &strRange = _parser["Range"];
xzl committed
332 333 334 335 336 337 338 339
	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;
	if (strRange.size() == 0) {
xiongziliang committed
340
		//全部下载
xzl committed
341 342
		pcHttpResult = "200 OK";
	} else {
xiongziliang committed
343
		//分节下载
xzl committed
344
		pcHttpResult = "206 Partial Content";
345
		fseek(pFilePtr.get(), iRangeStart, SEEK_SET);
xzl committed
346
	}
xiongziliang committed
347
	auto httpHeader=makeHttpHeader(bClose, iRangeEnd - iRangeStart + 1, get_mime_type(strFile.data()));
xzl committed
348
	if (strRange.size() != 0) {
xiongziliang committed
349
		//分节下载返回Content-Range头
xzl committed
350 351
		httpHeader.emplace("Content-Range",StrPrinter<<"bytes " << iRangeStart << "-" << iRangeEnd << "/" << tFileStat.st_size<< endl);
	}
352
	auto Origin = _parser["Origin"];
353 354 355 356
	if(!Origin.empty()){
		httpHeader["Access-Control-Allow-Origin"] = Origin;
		httpHeader["Access-Control-Allow-Credentials"] = "true";
	}
xiongziliang committed
357
	//先回复HTTP头部分
xzl committed
358 359
	sendResponse(pcHttpResult, httpHeader, "");
	if (iRangeEnd - iRangeStart < 0) {
xiongziliang committed
360
		//文件是空的!
361
		return !bClose;
xzl committed
362
	}
xiongziliang committed
363
	//回复Content部分
xzl committed
364
	std::shared_ptr<int64_t> piLeft(new int64_t(iRangeEnd - iRangeStart + 1));
365

xiongziliang committed
366
    GET_CONFIG_AND_REGISTER(uint32_t,sendBufSize,Http::kSendBufSize);
367

xzl committed
368
	weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
369
	auto onFlush = [pFilePtr,bClose,weakSelf,piLeft]() {
xzl committed
370 371 372
		TimeTicker();
		auto strongSelf = weakSelf.lock();
		while(*piLeft && strongSelf){
373
            //更新超时定时器
374
            strongSelf->_ticker.resetTime();
375
            //从循环池获取一个内存片
376
            auto sendBuf = strongSelf->obtainBuffer();
377 378
            sendBuf->setCapacity(sendBufSize);
            //本次需要读取文件字节数
xzl committed
379
			int64_t iReq = MIN(sendBufSize,*piLeft);
root committed
380 381 382 383 384
            //读文件	
			int iRead;
			do{
				 iRead = fread(sendBuf->data(), 1, iReq, pFilePtr.get());
			}while(-1 == iRead && UV_EINTR == get_uv_error(false));
385
            //文件剩余字节数
root committed
386
			
xzl committed
387
			*piLeft -= iRead;
388

xzl committed
389
			if (iRead < iReq || !*piLeft) {
390
                //文件读完
root committed
391
				//InfoL << "send complete!" << iRead << " " << iReq << " " << *piLeft;
xzl committed
392
				if(iRead>0) {
393
					sendBuf->setSize(iRead);
394
					strongSelf->send(sendBuf);
xzl committed
395 396 397 398 399 400
				}
				if(bClose) {
					strongSelf->shutdown();
				}
				return false;
			}
401 402
            //文件还未读完
            sendBuf->setSize(iRead);
403
            int iSent = strongSelf->send(sendBuf);
xzl committed
404
			if(iSent == -1) {
root committed
405
				//InfoL << "send error";
xzl committed
406 407 408
				return false;
			}
			if(iSent < iRead) {
root committed
409 410 411
				//数据回滚
				fseek(pFilePtr.get(), -iRead, SEEK_CUR);
				*piLeft += iRead;
xzl committed
412 413
				return true;
			}
xiongziliang committed
414 415 416 417
            if(strongSelf->isSocketBusy()){
                //套接字忙,那么停止继续写
                return true;
            }
xzl committed
418 419 420 421
			//send success
		}
		return false;
	};
422
	//关闭tcp_nodelay ,优化性能
423
	SockUtil::setNoDelay(_sock->rawFD(),false);
424
    //设置MSG_MORE,优化性能
425
    (*this) << SocketFlags(kSockFlags);
426

427
    onFlush();
428
	_sock->setOnFlush(onFlush);
429
	return true;
xzl committed
430 431
}

432
inline bool HttpSession::makeMeun(const string &strFullPath,const string &vhost, string &strRet) {
xzl committed
433 434 435 436 437 438 439 440 441 442 443 444 445
	string strPathPrefix(strFullPath);
	strPathPrefix = strPathPrefix.substr(0, strPathPrefix.length() - 1);
	if (!File::is_dir(strPathPrefix.data())) {
		return false;
	}
	strRet = "<html>\r\n"
			"<head>\r\n"
			"<title>文件索引</title>\r\n"
			"</head>\r\n"
			"<body>\r\n"
			"<h1>文件索引:";

	string strPath = strFullPath;
446
	strPath = strPath.substr(_strPath.length() + vhost.length() + 1);
xzl committed
447 448 449 450 451 452
	strRet += strPath;
	strRet += "</h1>\r\n";
	if (strPath != "/") {
		strRet += "<li><a href=\"";
		strRet += "/";
		strRet += "\">";
453
		strRet += "根目录";
xzl committed
454 455 456 457 458
		strRet += "</a></li>\r\n";

		strRet += "<li><a href=\"";
		strRet += "../";
		strRet += "\">";
459
		strRet += "上级目录";
xzl committed
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
		strRet += "</a></li>\r\n";
	}

	DIR *pDir;
	dirent *pDirent;
	if ((pDir = opendir(strPathPrefix.data())) == NULL) {
		return false;
	}
	set<string> setFile;
	while ((pDirent = readdir(pDir)) != NULL) {
		if (File::is_special_dir(pDirent->d_name)) {
			continue;
		}
		if(pDirent->d_name[0] == '.'){
			continue;
		}
		setFile.emplace(pDirent->d_name);
	}
	for(auto &strFile :setFile ){
		string strAbsolutePath = strPathPrefix + "/" + strFile;
		if (File::is_dir(strAbsolutePath.data())) {
			strRet += "<li><a href=\"";
			strRet += strFile;
			strRet += "/\">";
			strRet += strFile;
			strRet += "/</a></li>\r\n";
		} else { //是文件
			strRet += "<li><a href=\"";
			strRet += strFile;
			strRet += "\">";
			strRet += strFile;
			struct stat fileData;
			if (0 == stat(strAbsolutePath.data(), &fileData)) {
				auto &fileSize = fileData.st_size;
				if (fileSize < 1024) {
					strRet += StrPrinter << " (" << fileData.st_size << "B)" << endl;
				} else if (fileSize < 1024 * 1024) {
					strRet += StrPrinter << " (" << fileData.st_size / 1024 << "KB)" << endl;
				} else if (fileSize < 1024 * 1024 * 1024) {
					strRet += StrPrinter << " (" << fileData.st_size / 1024 / 1024 << "MB)" << endl;
				} else {
					strRet += StrPrinter << " (" << fileData.st_size / 1024 / 1024 / 1024 << "GB)" << endl;
				}
			}
			strRet += "</a></li>\r\n";
		}
	}
	closedir(pDir);
	strRet += "<ul>\r\n";
	strRet += "</ul>\r\n</body></html>";
	return true;
}
inline void HttpSession::sendResponse(const char* pcStatus, const KeyValue& header, const string& strContent) {
	_StrPrinter printer;
xiongziliang committed
514
	printer << "HTTP/1.1 " << pcStatus << "\r\n";
xzl committed
515 516 517 518 519 520 521
	for (auto &pr : header) {
		printer << pr.first << ": " << pr.second << "\r\n";
	}
	printer << "\r\n" << strContent;
	auto strSend = printer << endl;
	//DebugL << strSend;
	send(strSend);
522
	_ticker.resetTime();
xzl committed
523 524 525
}
inline HttpSession::KeyValue HttpSession::makeHttpHeader(bool bClose, int64_t iContentSize,const char* pcContentType) {
	KeyValue headerOut;
xiongziliang committed
526 527 528
    GET_CONFIG_AND_REGISTER(string,charSet,Http::kCharSet);
    GET_CONFIG_AND_REGISTER(uint32_t,keepAliveSec,Http::kKeepAliveSecond);
    GET_CONFIG_AND_REGISTER(uint32_t,reqCnt,Http::kMaxReqCount);
xzl committed
529

xiongziliang committed
530
	headerOut.emplace("Date", dateStr());
531
	headerOut.emplace("Server", SERVER_NAME);
xiongziliang committed
532
	headerOut.emplace("Connection", bClose ? "close" : "keep-alive");
xiongziliang committed
533 534 535 536
	if(!bClose){
		headerOut.emplace("Keep-Alive",StrPrinter << "timeout=" << keepAliveSec << ", max=" << reqCnt << endl);
	}
	if(pcContentType){
xzl committed
537
		auto strContentType = StrPrinter << pcContentType << "; charset=" << charSet << endl;
xiongziliang committed
538 539 540
		headerOut.emplace("Content-Type",strContentType);
	}
	if(iContentSize > 0){
xzl committed
541 542 543 544 545
		headerOut.emplace("Content-Length", StrPrinter<<iContentSize<<endl);
	}
	return headerOut;
}

xiongziliang committed
546 547
string HttpSession::urlDecode(const string &str){
	auto ret = strCoding::UrlUTF8Decode(str);
548
#ifdef _WIN32
xiongziliang committed
549
    GET_CONFIG_AND_REGISTER(string,charSet,Http::kCharSet);
xiongziliang committed
550
	bool isGb2312 = !strcasecmp(charSet.data(), "gb2312");
551
	if (isGb2312) {
xiongziliang committed
552
		ret = strCoding::UTF8ToGB2312(ret);
553 554
	}
#endif // _WIN32
xiongziliang committed
555 556
    return ret;
}
557

xiongziliang committed
558 559
inline void HttpSession::urlDecode(Parser &parser){
	parser.setUrl(urlDecode(parser.Url()));
560
	for(auto &pr : _parser.getUrlArgs()){
xiongziliang committed
561 562 563
		const_cast<string &>(pr.second) = urlDecode(pr.second);
	}
}
xzl committed
564

xiongziliang committed
565 566
inline bool HttpSession::emitHttpEvent(bool doInvoke){
	///////////////////是否断开本链接///////////////////////
xiongziliang committed
567
    GET_CONFIG_AND_REGISTER(uint32_t,reqCnt,Http::kMaxReqCount);
568

xiongziliang committed
569
    bool bClose = (strcasecmp(_parser["Connection"].data(),"close") == 0) || ( ++_iReqCnt > reqCnt);
570
	auto Origin = _parser["Origin"];
xiongziliang committed
571
	/////////////////////异步回复Invoker///////////////////////////////
572
	weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
xiongziliang committed
573
	HttpResponseInvoker invoker = [weakSelf,bClose,Origin](const string &codeOut, const KeyValue &headerOut, const string &contentOut){
574 575 576 577
		auto strongSelf = weakSelf.lock();
		if(!strongSelf) {
			return;
		}
xiongziliang committed
578
		strongSelf->async([weakSelf,bClose,codeOut,headerOut,contentOut,Origin]() {
579 580 581 582
			auto strongSelf = weakSelf.lock();
			if(!strongSelf) {
				return;
			}
xiongziliang committed
583
			strongSelf->responseDelay(Origin,bClose,codeOut,headerOut,contentOut);
584 585 586 587 588
			if(bClose){
				strongSelf->shutdown();
			}
		});
	};
xiongziliang committed
589 590
	///////////////////广播HTTP事件///////////////////////////
	bool consumed = false;//该事件是否被消费
xiongziliang committed
591
	NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastHttpRequest,_parser,invoker,consumed,*this);
xiongziliang committed
592 593
	if(!consumed && doInvoke){
		//该事件无人消费,所以返回404
594
		invoker("404 Not Found",KeyValue(),"");
595 596 597 598
		if(bClose){
			//close类型,回复完毕,关闭连接
			shutdown();
		}
xzl committed
599
	}
xiongziliang committed
600 601
	return consumed;
}
602
inline bool HttpSession::Handle_Req_POST(int64_t &content_len) {
xiongziliang committed
603 604
	GET_CONFIG_AND_REGISTER(uint64_t,maxReqSize,Http::kMaxReqSize);
    GET_CONFIG_AND_REGISTER(int,maxReqCnt,Http::kMaxReqCount);
605

606
    int64_t totalContentLen = _parser["Content-Length"].empty() ? -1 : atoll(_parser["Content-Length"].data());
607

608
	if(totalContentLen == 0){
609 610
		//content为空
		//emitHttpEvent内部会选择是否关闭连接
611
		emitHttpEvent(true);
612
		return true;
613 614
	}

615
	if(totalContentLen > 0 && totalContentLen < maxReqSize ){
616
		//返回固定长度的content
617
		content_len = totalContentLen;
618 619
		auto parserCopy = _parser;
		_contentCallBack = [this,parserCopy](const char *data,uint64_t len){
620
			//恢复http头
621
			_parser = parserCopy;
622
			//设置content
623
			_parser.setContent(string(data,len));
624
			//触发http事件,emitHttpEvent内部会选择是否关闭连接
625 626
			emitHttpEvent(true);
			//清空数据,节省内存
627
			_parser.Clear();
628
			//content已经接收完毕
629 630 631 632 633
			return false;
		};
	}else{
		//返回不固定长度的content
		content_len = -1;
634
		auto parserCopy = _parser;
635
		std::shared_ptr<uint64_t> recvedContentLen = std::make_shared<uint64_t>(0);
xiongziliang committed
636
		bool bClose = (strcasecmp(_parser["Connection"].data(),"close") == 0) || ( ++_iReqCnt > maxReqCnt);
637

638
		_contentCallBack = [this,parserCopy,totalContentLen,recvedContentLen,bClose](const char *data,uint64_t len){
639
		    *(recvedContentLen) += len;
640

641
		    onRecvUnlimitedContent(parserCopy,data,len,totalContentLen,*(recvedContentLen));
642 643 644

			if(*(recvedContentLen) < totalContentLen){
			    //数据还没接收完毕
645
                //_contentCallBack是可持续的,后面还要处理后续content数据
646 647 648 649 650 651 652 653
                return true;
			}

			//数据接收完毕
            if(!bClose){
			    //keep-alive类型连接
				//content接收完毕,后续都是http header
				setContentLen(0);
654
                //content已经接收完毕
655 656 657 658 659
                return false;
            }

            //连接类型是close类型,收完content就关闭连接
            shutdown();
660
            //content已经接收完毕
661
            return false ;
662 663
		};
	}
664
	//有后续content数据要处理,暂时不关闭连接
665
	return true;
666
}
xiongziliang committed
667 668 669
void HttpSession::responseDelay(const string &Origin,bool bClose,
								const string &codeOut,const KeyValue &headerOut,
								const string &contentOut){
670 671 672 673
	if(codeOut.empty()){
		sendNotFound(bClose);
		return;
	}
xiongziliang committed
674
	auto headerOther=makeHttpHeader(bClose,contentOut.size(),"text/plain");
xiongziliang committed
675 676 677 678
	if(!Origin.empty()){
		headerOther["Access-Control-Allow-Origin"] = Origin;
		headerOther["Access-Control-Allow-Credentials"] = "true";
	}
xiongziliang committed
679
	const_cast<KeyValue &>(headerOut).insert(headerOther.begin(), headerOther.end());
680
	sendResponse(codeOut.data(), headerOut, contentOut);
xzl committed
681 682
}
inline void HttpSession::sendNotFound(bool bClose) {
xiongziliang committed
683
    GET_CONFIG_AND_REGISTER(string,notFound,Http::kNotFound);
684
    sendResponse("404 Not Found", makeHttpHeader(bClose, notFound.size()), notFound);
xzl committed
685 686
}

xiongziliang committed
687 688

void HttpSession::onWrite(const Buffer::Ptr &buffer) {
689 690 691
	_ticker.resetTime();
	_ui64TotalBytes += buffer->size();
	send(buffer);
xiongziliang committed
692 693
}

xiongziliang committed
694 695 696
void HttpSession::onWrite(const char *data, int len) {
	BufferRaw::Ptr buffer(new BufferRaw);
	buffer->assign(data,len);
697 698 699
	_ticker.resetTime();
	_ui64TotalBytes += buffer->size();
	send(buffer);
xiongziliang committed
700 701
}

xiongziliang committed
702
void HttpSession::onDetach() {
703
	shutdown();
xiongziliang committed
704 705 706 707
}

std::shared_ptr<FlvMuxer> HttpSession::getSharedPtr(){
	return dynamic_pointer_cast<FlvMuxer>(shared_from_this());
708 709
}

xiongziliang committed
710
} /* namespace mediakit */