HttpSession.cpp 19.7 KB
Newer Older
xiongziliang committed
1
/*
xiongziliang committed
2
 * MIT License
xzl committed
3
 *
xiongziliang committed
4
 * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
xiongziliang committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 * 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
#include <stdio.h>
#include <sys/stat.h>
#include <algorithm>
33
#include <iomanip>
xzl committed
34

xiongziliang committed
35
#include "Common/config.h"
xzl committed
36 37
#include "strCoding.h"
#include "HttpSession.h"
xiongziliang committed
38 39
#include "Util/base64.h"
#include "Util/SHA1.h"
xiongziliang committed
40
using namespace toolkit;
41

xiongziliang committed
42
namespace mediakit {
xzl committed
43

xiongziliang committed
44
HttpSession::HttpSession(const Socket::Ptr &pSock) : TcpSession(pSock) {
xiongziliang committed
45
    TraceP(this);
46 47
    GET_CONFIG(uint32_t,keep_alive_sec,Http::kKeepAliveSecond);
    pSock->setSendTimeOutSecond(keep_alive_sec);
48 49
	//起始接收buffer缓存设置为4K,节省内存
	pSock->setReadBuffer(std::make_shared<BufferRaw>(4 * 1024));
xzl committed
50 51 52
}

HttpSession::~HttpSession() {
xiongziliang committed
53
    TraceP(this);
xzl committed
54 55
}

56
int64_t HttpSession::onRecvHeader(const char *header,uint64_t len) {
xiongziliang committed
57
	typedef void (HttpSession::*HttpCMDHandle)(int64_t &);
58
	static unordered_map<string, HttpCMDHandle> s_func_map;
xiongziliang committed
59
	static onceToken token([]() {
60 61
		s_func_map.emplace("GET",&HttpSession::Handle_Req_GET);
		s_func_map.emplace("POST",&HttpSession::Handle_Req_POST);
xiongziliang committed
62 63
	}, nullptr);

64 65 66
	_parser.Parse(header);
	urlDecode(_parser);
	string cmd = _parser.Method();
67 68
	auto it = s_func_map.find(cmd);
	if (it == s_func_map.end()) {
xiongziliang committed
69
		sendResponse("403 Forbidden", true);
xiongziliang committed
70
        return 0;
71 72
	}

xiongziliang committed
73 74 75 76
    //跨域
    _origin = _parser["Origin"];

    //默认后面数据不是content而是header
77 78
	int64_t content_len = 0;
	auto &fun = it->second;
xiongziliang committed
79 80 81 82 83 84
    try {
        (this->*fun)(content_len);
    }catch (exception &ex){
        shutdown(SockException(Err_shutdown,ex.what()));
    }

85
	//清空解析器节省内存
86
	_parser.Clear();
87 88
	//返回content长度
	return content_len;
xzl committed
89
}
90

91
void HttpSession::onRecvContent(const char *data,uint64_t len) {
92 93 94
	if(_contentCallBack){
		if(!_contentCallBack(data,len)){
			_contentCallBack = nullptr;
95 96 97 98 99
		}
	}
}

void HttpSession::onRecv(const Buffer::Ptr &pBuf) {
100
    _ticker.resetTime();
101
    input(pBuf->data(),pBuf->size());
102 103
}

xzl committed
104
void HttpSession::onError(const SockException& err) {
xiongziliang committed
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
    if(_is_flv_stream){
        //flv播放器
        WarnP(this) << "播放器("
                    << _mediaInfo._vhost << "/"
                    << _mediaInfo._app << "/"
                    << _mediaInfo._streamid
                    << ")断开:" << err.what();

        GET_CONFIG(uint32_t,iFlowThreshold,General::kFlowThreshold);
        if(_ui64TotalBytes > iFlowThreshold * 1024){
            NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport,
                                               _mediaInfo,
                                               _ui64TotalBytes,
                                               _ticker.createdTime()/1000,
                                               true,
                                               *this);
        }
        return;
    }

    //http客户端
xiongziliang committed
126 127 128 129 130
    if(_ticker.createdTime() < 10 * 1000){
        TraceP(this) << err.what();
    }else{
        WarnP(this) << err.what();
    }
xzl committed
131 132 133
}

void HttpSession::onManager() {
134
    GET_CONFIG(uint32_t,keepAliveSec,Http::kKeepAliveSecond);
135

136
    if(_ticker.elapsedTime() > keepAliveSec * 1000){
xzl committed
137
		//1分钟超时
xiongziliang committed
138
		shutdown(SockException(Err_timeout,"session timeouted"));
xzl committed
139 140
	}
}
xiongziliang committed
141

142
bool HttpSession::checkWebSocket(){
143
	auto Sec_WebSocket_Key = _parser["Sec-WebSocket-Key"];
144
	if(Sec_WebSocket_Key.empty()){
xiongziliang committed
145 146 147 148
		return false;
	}
	auto Sec_WebSocket_Accept = encodeBase64(SHA1::encode_bin(Sec_WebSocket_Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));

xiongziliang committed
149
	KeyValue headerOut;
xiongziliang committed
150 151 152
	headerOut["Upgrade"] = "websocket";
	headerOut["Connection"] = "Upgrade";
	headerOut["Sec-WebSocket-Accept"] = Sec_WebSocket_Accept;
xiongziliang committed
153 154 155
	if(!_parser["Sec-WebSocket-Protocol"].empty()){
		headerOut["Sec-WebSocket-Protocol"] = _parser["Sec-WebSocket-Protocol"];
	}
156 157 158

    auto res_cb = [this,headerOut](){
        _flv_over_websocket = true;
159
        sendResponse("101 Switching Protocols",false,nullptr,headerOut,nullptr, true);
160 161 162 163 164 165 166 167 168
    };

    //判断是否为websocket-flv
    if(checkLiveFlvStream(res_cb)){
        //这里是websocket-flv直播请求
        return true;
    }

    //如果checkLiveFlvStream返回false,则代表不是websocket-flv,而是普通的websocket连接
169
    if(!onWebSocketConnect(_parser)){
xiongziliang committed
170
        sendResponse("501 Not Implemented",true, nullptr, headerOut);
171 172
        return true;
    }
xiongziliang committed
173
    sendResponse("101 Switching Protocols",false, nullptr,headerOut);
xiongziliang committed
174 175
	return true;
}
176

177 178
//http-flv 链接格式:http://vhost-url:port/app/streamid.flv?key1=value1&key2=value2
//如果url(除去?以及后面的参数)后缀是.flv,那么表明该url是一个http-flv直播。
179
bool HttpSession::checkLiveFlvStream(const function<void()> &cb){
180
	auto pos = strrchr(_parser.Url().data(),'.');
xiongziliang committed
181 182 183 184
	if(!pos){
		//未找到".flv"后缀
		return false;
	}
xiongziliang committed
185
	if(strcasecmp(pos,".flv") != 0){
xiongziliang committed
186 187 188
		//未找到".flv"后缀
		return false;
	}
189

190 191 192 193 194
	//这是个.flv的流
    _mediaInfo.parse(string(RTMP_SCHEMA) + "://" + _parser["Host"] + _parser.FullUrl());
	if(_mediaInfo._app.empty() || _mediaInfo._streamid.size() < 5){
	    //url不合法
        return false;
xiongziliang committed
195
	}
196
    _mediaInfo._streamid.erase(_mediaInfo._streamid.size() - 4);//去除.flv后缀
197
    bool bClose = !strcasecmp(_parser["Connection"].data(),"close");
198 199

    weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
xiongziliang committed
200
    MediaSource::findAsync(_mediaInfo,weakSelf.lock(),[weakSelf,bClose,this,cb](const MediaSource::Ptr &src){
201 202
        auto strongSelf = weakSelf.lock();
        if(!strongSelf){
203 204 205 206 207 208
            //本对象已经销毁
            return;
        }
        auto rtmp_src = dynamic_pointer_cast<RtmpMediaSource>(src);
        if(!rtmp_src){
            //未找到该流
209
            sendNotFound(bClose);
210 211
            return;
        }
212
        //找到流了
213
        auto onRes = [this,rtmp_src,cb](const string &err){
214 215
            bool authSuccess = err.empty();
            if(!authSuccess){
xiongziliang committed
216
                sendResponse("401 Unauthorized", true, nullptr, KeyValue(), std::make_shared<HttpStringBody>(err));
217 218 219
                return ;
            }

220
            if(!cb) {
221
                //找到rtmp源,发送http头,负载后续发送
222
                sendResponse("200 OK", false, "video/x-flv",KeyValue(),nullptr,true);
223 224
            }else{
                cb();
225
            }
226

227 228 229
            //http-flv直播牺牲延时提升发送性能
            setSocketFlags();

230 231
            try{
                start(getPoller(),rtmp_src);
xiongziliang committed
232
                _is_flv_stream = true;
233 234
            }catch (std::exception &ex){
                //该rtmp源不存在
xiongziliang committed
235
                shutdown(SockException(Err_shutdown,"rtmp mediasource released"));
236 237 238 239 240
            }
        };

        weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
        Broadcast::AuthInvoker invoker = [weakSelf,onRes](const string &err){
241 242 243 244
            auto strongSelf = weakSelf.lock();
            if(!strongSelf){
                return;
            }
245 246 247 248 249 250 251 252 253 254 255 256 257 258
            strongSelf->async([weakSelf,onRes,err](){
                auto strongSelf = weakSelf.lock();
                if(!strongSelf){
                    return;
                }
                onRes(err);
            });
        };
        auto flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed,_mediaInfo,invoker,*this);
        if(!flag){
            //该事件无人监听,默认不鉴权
            onRes("");
        }
    });
259
    return true;
xiongziliang committed
260
}
261

262

263
void HttpSession::Handle_Req_GET(int64_t &content_len) {
xiongziliang committed
264 265 266
	//先看看是否为WebSocket请求
	if(checkWebSocket()){
		content_len = -1;
267 268
		_contentCallBack = [this](const char *data,uint64_t len){
            WebSocketSplitter::decode((uint8_t *)data,len);
269
			//_contentCallBack是可持续的,后面还要处理后续数据
xiongziliang committed
270 271
			return true;
		};
xiongziliang committed
272
		return;
xiongziliang committed
273 274
	}

xiongziliang committed
275
	if(emitHttpEvent(false)){
276
        //拦截http api事件
xiongziliang committed
277
		return;
278
	}
xiongziliang committed
279

280 281 282 283 284
    if(checkLiveFlvStream()){
        //拦截http-flv播放器
        return;
    }

285
    bool bClose = !strcasecmp(_parser["Connection"].data(),"close");
286

287 288 289 290 291
    weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
    HttpFileManager::onAccessPath(*this, _parser, [weakSelf, bClose](const string &status_code, const string &content_type,
                                                                     const StrCaseMap &responseHeader, const HttpBody::Ptr &body) {
        auto strongSelf = weakSelf.lock();
        if (!strongSelf) {
292
            return;
293
        }
294 295 296 297
        strongSelf->async([weakSelf, bClose, status_code, content_type, responseHeader, body]() {
            auto strongSelf = weakSelf.lock();
            if (!strongSelf) {
                return;
298
            }
xiongziliang committed
299
            strongSelf->sendResponse(status_code.data(), bClose, content_type.data(), responseHeader, body);
300
        });
301
    });
xzl committed
302 303
}

304 305 306 307 308
static 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;
xzl committed
309
}
310

xiongziliang committed
311 312 313 314 315
void HttpSession::sendResponse(const char *pcStatus,
                               bool bClose,
                               const char *pcContentType,
                               const HttpSession::KeyValue &header,
                               const HttpBody::Ptr &body,
316
                               bool is_http_flv ){
xiongziliang committed
317 318 319 320 321 322 323 324 325 326
    GET_CONFIG(string,charSet,Http::kCharSet);
    GET_CONFIG(uint32_t,keepAliveSec,Http::kKeepAliveSecond);

    //body默认为空
    int64_t size = 0;
    if (body && body->remainSize()) {
        //有body,获取body大小
        size = body->remainSize();
    }

327 328
    if(is_http_flv){
        //http-flv直播是Keep-Alive类型
xiongziliang committed
329
        bClose = false;
330 331 332
    }else if(size >= INT64_MAX){
        //不固定长度的body,那么发送完body后应该关闭socket,以便浏览器做下载完毕的判断
        bClose = true;
xiongziliang committed
333 334 335 336 337 338 339
    }

    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){
340
        headerOut.emplace("Keep-Alive",StrPrinter << "timeout=" << keepAliveSec << ", max=100" << endl);
xiongziliang committed
341 342 343 344 345 346 347 348
    }

    if(!_origin.empty()){
        //设置跨域
        headerOut.emplace("Access-Control-Allow-Origin",_origin);
        headerOut.emplace("Access-Control-Allow-Credentials", "true");
    }

349 350
    if(!is_http_flv && size >= 0 && size < INT64_MAX){
        //文件长度为固定值,且不是http-flv强制设置Content-Length
351
        headerOut["Content-Length"] = StrPrinter << size << endl;
xiongziliang committed
352 353 354 355 356 357 358 359 360 361 362 363 364
    }

    if(size && !pcContentType){
        //有body时,设置缺省类型
        pcContentType = "text/plain";
    }

    if(size && pcContentType){
        //有body时,设置文件类型
        auto strContentType = StrPrinter << pcContentType << "; charset=" << charSet << endl;
        headerOut.emplace("Content-Type",strContentType);
    }

365 366 367 368 369 370
    //发送http头
    _StrPrinter printer;
    printer << "HTTP/1.1 " << pcStatus << "\r\n";
    for (auto &pr : header) {
        printer << pr.first << ": " << pr.second << "\r\n";
    }
xiongziliang committed
371

372 373 374 375
    printer << "\r\n";
    send(printer << endl);
    _ticker.resetTime();

xiongziliang committed
376
    if(!size){
377 378
        //没有body
        if(bClose){
379
            shutdown(SockException(Err_shutdown,StrPrinter << "close connection after send http header completed with status code:" << pcStatus));
380 381 382 383 384 385 386 387
        }
        return;
    }

    //发送http body
    GET_CONFIG(uint32_t,sendBufSize,Http::kSendBufSize);
    weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());

388 389
    string status_code = pcStatus;
    auto onFlush = [body,bClose,weakSelf,status_code]() {
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
        auto strongSelf = weakSelf.lock();
        if(!strongSelf){
            //本对象已经销毁
            return false;
        }
        while(true){
            //更新超时计时器
            strongSelf->_ticker.resetTime();
            //读取文件
            auto sendBuf = body->readData(sendBufSize);
            if (!sendBuf) {
                //文件读完
                if(strongSelf->isSocketBusy() && bClose){
                    //套接字忙,我们等待触发下一次onFlush事件
                    //待所有数据flush到socket fd再移除onFlush事件监听
                    //标记文件读写完毕
                    return true;
                }
                //文件全部flush到socket fd,可以直接关闭socket了
                break;
            }

            //文件还未读完
            if(strongSelf->send(sendBuf) == -1) {
                //socket已经销毁,不再监听onFlush事件
                return false;
            }
            if(strongSelf->isSocketBusy()){
                //socket忙,那么停止继续写,等待下一次onFlush事件,然后再读文件写socket
                return true;
            }
            //socket还可写,继续写socket
        }

        if(bClose) {
            //最后一次flush事件,文件也发送完毕了,可以关闭socket了
426
            strongSelf->shutdown(SockException(Err_shutdown, StrPrinter << "close connection after send http body completed with status code:" << status_code));
427 428 429 430 431 432 433 434 435 436 437 438 439
        }
        //不再监听onFlush事件
        return false;
    };

    if(body->remainSize() > sendBufSize){
        //文件下载提升发送性能
        setSocketFlags();
    }
    onFlush();
    _sock->setOnFlush(onFlush);
}

xiongziliang committed
440
string HttpSession::urlDecode(const string &str){
xiongziliang committed
441
	auto ret = strCoding::UrlDecode(str);
442
#ifdef _WIN32
443
    GET_CONFIG(string,charSet,Http::kCharSet);
xiongziliang committed
444
	bool isGb2312 = !strcasecmp(charSet.data(), "gb2312");
445
	if (isGb2312) {
xiongziliang committed
446
		ret = strCoding::UTF8ToGB2312(ret);
447 448
	}
#endif // _WIN32
xiongziliang committed
449 450
    return ret;
}
451

452
void HttpSession::urlDecode(Parser &parser){
xiongziliang committed
453
	parser.setUrl(urlDecode(parser.Url()));
454
	for(auto &pr : _parser.getUrlArgs()){
xiongziliang committed
455 456 457
		const_cast<string &>(pr.second) = urlDecode(pr.second);
	}
}
xzl committed
458

459
bool HttpSession::emitHttpEvent(bool doInvoke){
460
    bool bClose = !strcasecmp(_parser["Connection"].data(),"close");
xiongziliang committed
461
	/////////////////////异步回复Invoker///////////////////////////////
462
	weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
463
	HttpResponseInvoker invoker = [weakSelf,bClose](const string &codeOut, const KeyValue &headerOut, const HttpBody::Ptr &body){
464 465 466 467
		auto strongSelf = weakSelf.lock();
		if(!strongSelf) {
			return;
		}
468
		strongSelf->async([weakSelf,bClose,codeOut,headerOut,body]() {
469 470
			auto strongSelf = weakSelf.lock();
			if(!strongSelf) {
471
                //本对象已经销毁
472 473
				return;
			}
xiongziliang committed
474
            strongSelf->sendResponse(codeOut.data(), bClose, nullptr, headerOut, body);
475 476
		});
	};
xiongziliang committed
477 478
	///////////////////广播HTTP事件///////////////////////////
	bool consumed = false;//该事件是否被消费
xiongziliang committed
479
	NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastHttpRequest,_parser,invoker,consumed,*this);
xiongziliang committed
480 481
	if(!consumed && doInvoke){
		//该事件无人消费,所以返回404
482
		invoker("404 Not Found",KeyValue(), HttpBody::Ptr());
xzl committed
483
	}
xiongziliang committed
484 485
	return consumed;
}
486 487

void HttpSession::Handle_Req_POST(int64_t &content_len) {
488
	GET_CONFIG(uint64_t,maxReqSize,Http::kMaxReqSize);
489

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

492
	if(totalContentLen == 0){
493 494
		//content为空
		//emitHttpEvent内部会选择是否关闭连接
495
		emitHttpEvent(true);
xiongziliang committed
496
		return;
497 498
	}

499 500 501 502 503 504 505 506 507
    //根据Content-Length设置接收缓存大小
    if(totalContentLen > 0){
        _sock->setReadBuffer(std::make_shared<BufferRaw>(MIN(totalContentLen + 1,256 * 1024)));
    }else{
	    //不定长度的Content-Length
        _sock->setReadBuffer(std::make_shared<BufferRaw>(256 * 1024));
	}

    if(totalContentLen > 0 && totalContentLen < maxReqSize ){
508
		//返回固定长度的content
509
		content_len = totalContentLen;
510 511
		auto parserCopy = _parser;
		_contentCallBack = [this,parserCopy](const char *data,uint64_t len){
512
			//恢复http头
513
			_parser = parserCopy;
514
			//设置content
515
			_parser.setContent(string(data,len));
516
			//触发http事件,emitHttpEvent内部会选择是否关闭连接
517 518
			emitHttpEvent(true);
			//清空数据,节省内存
519
			_parser.Clear();
520
			//content已经接收完毕
521 522 523 524 525
			return false;
		};
	}else{
		//返回不固定长度的content
		content_len = -1;
526
		auto parserCopy = _parser;
527
		std::shared_ptr<uint64_t> recvedContentLen = std::make_shared<uint64_t>(0);
528
		bool bClose = !strcasecmp(_parser["Connection"].data(),"close");
529

530
		_contentCallBack = [this,parserCopy,totalContentLen,recvedContentLen,bClose](const char *data,uint64_t len){
531
		    *(recvedContentLen) += len;
532

533
		    onRecvUnlimitedContent(parserCopy,data,len,totalContentLen,*(recvedContentLen));
534 535 536

			if(*(recvedContentLen) < totalContentLen){
			    //数据还没接收完毕
537
                //_contentCallBack是可持续的,后面还要处理后续content数据
538 539 540 541 542 543 544 545
                return true;
			}

			//数据接收完毕
            if(!bClose){
			    //keep-alive类型连接
				//content接收完毕,后续都是http header
				setContentLen(0);
546
                //content已经接收完毕
547 548 549 550
                return false;
            }

            //连接类型是close类型,收完content就关闭连接
xiongziliang committed
551
            shutdown(SockException(Err_shutdown,"recv http content completed"));
552
            //content已经接收完毕
553
            return false ;
554 555
		};
	}
556
	//有后续content数据要处理,暂时不关闭连接
557
}
558 559

void HttpSession::sendNotFound(bool bClose) {
560
    GET_CONFIG(string,notFound,Http::kNotFound);
xiongziliang committed
561
    sendResponse("404 Not Found", bClose,"text/html",KeyValue(),std::make_shared<HttpStringBody>(notFound));
xzl committed
562 563
}

564 565 566 567 568 569 570 571 572
void HttpSession::setSocketFlags(){
    GET_CONFIG(bool,ultraLowDelay,General::kUltraLowDelay);
    if(!ultraLowDelay) {
        //推流模式下,关闭TCP_NODELAY会增加推流端的延时,但是服务器性能将提高
        SockUtil::setNoDelay(_sock->rawFD(), false);
        //播放模式下,开启MSG_MORE会增加延时,但是能提高发送性能
        (*this) << SocketFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE);
    }
}
xiongziliang committed
573 574

void HttpSession::onWrite(const Buffer::Ptr &buffer) {
575
	_ticker.resetTime();
576 577 578 579 580 581 582 583 584 585 586
    if(!_flv_over_websocket){
        _ui64TotalBytes += buffer->size();
        send(buffer);
        return;
    }

    WebSocketHeader header;
    header._fin = true;
    header._reserved = 0;
    header._opcode = WebSocketHeader::BINARY;
    header._mask_flag = false;
587
    WebSocketSplitter::encode(header,buffer);
588 589
}

590 591 592
void HttpSession::onWebSocketEncodeData(const Buffer::Ptr &buffer){
    _ui64TotalBytes += buffer->size();
    send(buffer);
xiongziliang committed
593 594
}

xiongziliang committed
595
void HttpSession::onDetach() {
xiongziliang committed
596
	shutdown(SockException(Err_shutdown,"rtmp ring buffer detached"));
xiongziliang committed
597 598 599 600
}

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

xiongziliang committed
603
} /* namespace mediakit */