/* * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. * * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). * * Use of this source code is governed by MIT-like license that can be found in the * LICENSE file in the root of the source tree. All contributing project authors * may be found in the AUTHORS file in the root of the source tree. */ #include #include #include #include "Common/config.h" #include "Common/strCoding.h" #include "HttpSession.h" #include "HttpConst.h" #include "Util/base64.h" #include "Util/SHA1.h" using namespace std; using namespace toolkit; namespace mediakit { HttpSession::HttpSession(const Socket::Ptr &pSock) : Session(pSock) { // 设置默认参数 [AUTO-TRANSLATED:ae5b72e6] // Set default parameters setMaxReqSize(0); setTimeoutSec(0); } void HttpSession::onHttpRequest_HEAD() { // 暂时全部返回200 OK,因为HTTP GET存在按需生成流的操作,所以不能按照HTTP GET的流程返回 [AUTO-TRANSLATED:0ce05db5] // Temporarily return 200 OK for all, because HTTP GET has on-demand generation stream operations, so it cannot return according to the HTTP GET process // 如果直接返回404,那么又会导致按需生成流的逻辑失效,所以HTTP HEAD在静态文件或者已存在资源时才有效 [AUTO-TRANSLATED:ea2b6faa] // If you return 404 directly, it will also cause the on-demand generation stream logic to fail, so HTTP HEAD is only valid for static files or existing resources // 对于按需生成流的直播场景并不适用 [AUTO-TRANSLATED:5a47bf00] // Not applicable to live streaming scenarios that generate streams on demand sendResponse(200, false); } void HttpSession::onHttpRequest_OPTIONS() { KeyValue header; header.emplace("Allow", "GET, POST, HEAD, OPTIONS"); GET_CONFIG(bool, allow_cross_domains, Http::kAllowCrossDomains); if (allow_cross_domains) { header.emplace("Access-Control-Allow-Origin", "*"); header.emplace("Access-Control-Allow-Headers", "*"); header.emplace("Access-Control-Allow-Methods", "GET, POST, HEAD, OPTIONS"); } header.emplace("Access-Control-Allow-Credentials", "true"); header.emplace("Access-Control-Request-Methods", "GET, POST, OPTIONS"); header.emplace("Access-Control-Request-Headers", "Accept,Accept-Language,Content-Language,Content-Type"); sendResponse(200, true, nullptr, header); } ssize_t HttpSession::onRecvHeader(const char *header, size_t len) { using func_type = void (HttpSession::*)(); static unordered_map s_func_map; static onceToken token([]() { s_func_map.emplace("GET", &HttpSession::onHttpRequest_GET); s_func_map.emplace("POST", &HttpSession::onHttpRequest_POST); // DELETE命令用于whip/whep用,只用于触发http api [AUTO-TRANSLATED:f3b7aaea] // DELETE command is used for whip/whep, only used to trigger http api s_func_map.emplace("DELETE", &HttpSession::onHttpRequest_POST); s_func_map.emplace("HEAD", &HttpSession::onHttpRequest_HEAD); s_func_map.emplace("OPTIONS", &HttpSession::onHttpRequest_OPTIONS); }); _parser.parse(header, len); CHECK(_parser.url()[0] == '/'); _origin = _parser["Origin"]; urlDecode(_parser); auto &cmd = _parser.method(); auto it = s_func_map.find(cmd); if (it == s_func_map.end()) { WarnP(this) << "Http method not supported: " << cmd; sendResponse(405, true); return 0; } size_t content_len; auto &content_len_str = _parser["Content-Length"]; if (content_len_str.empty()) { if (it->first == "POST") { // Http post未指定长度,我们认为是不定长的body [AUTO-TRANSLATED:3578206b] // Http post does not specify length, we consider it to be an indefinite length body WarnL << "Received http post request without content-length, consider it to be unlimited length"; content_len = SIZE_MAX; } else { content_len = 0; } } else { // 已经指定长度 [AUTO-TRANSLATED:a360c374] // Length has been specified content_len = atoll(content_len_str.data()); } if (content_len == 0) { // // 没有body的情况,直接触发回调 //// [AUTO-TRANSLATED:f2988336] // // No body case, trigger callback directly //// (this->*(it->second))(); _parser.clear(); // 如果设置了_on_recv_body, 那么说明后续要处理body [AUTO-TRANSLATED:2dac5fc2] // If _on_recv_body is set, it means that the body will be processed later return _on_recv_body ? -1 : 0; } if (content_len > _max_req_size) { // // 不定长body或超大body //// [AUTO-TRANSLATED:8d66ee77] // // Indefinite length body or oversized body //// if (content_len != SIZE_MAX) { WarnL << "Http body size is too huge: " << content_len << " > " << _max_req_size << ", please set " << Http::kMaxReqSize << " in config.ini file."; } size_t received = 0; auto parser = std::move(_parser); _on_recv_body = [this, parser, received, content_len](const char *data, size_t len) mutable { received += len; onRecvUnlimitedContent(parser, data, len, content_len, received); if (received < content_len) { // 还没收满 [AUTO-TRANSLATED:cecc867e] // Not yet received return true; } // 收满了 [AUTO-TRANSLATED:0c9cebd7] // Received full setContentLen(0); return false; }; // 声明后续都是body;Http body在本对象缓冲,不通过HttpRequestSplitter保存 [AUTO-TRANSLATED:0012b6c1] // Declare that the following is all body; Http body is buffered in this object, not saved through HttpRequestSplitter return -1; } // // body size明确指定且小于最大值的情况 //// [AUTO-TRANSLATED:f1f1ee5d] // // Body size is explicitly specified and less than the maximum value //// _on_recv_body = [this, it](const char *data, size_t len) mutable { // 收集body完毕 [AUTO-TRANSLATED:981ad2c8] // Body collection complete _parser.setContent(std::string(data, len)); (this->*(it->second))(); _parser.clear(); // _on_recv_body置空 [AUTO-TRANSLATED:437a201a] // _on_recv_body is cleared return false; }; // 声明body长度,通过HttpRequestSplitter缓存然后一次性回调到_on_recv_body [AUTO-TRANSLATED:3b11cfb7] // Declare the body length, cache it through HttpRequestSplitter and then callback to _on_recv_body at once return content_len; } void HttpSession::onRecvContent(const char *data, size_t len) { if (_on_recv_body && !_on_recv_body(data, len)) { _on_recv_body = nullptr; } } void HttpSession::onRecv(const Buffer::Ptr &pBuf) { _ticker.resetTime(); input(pBuf->data(), pBuf->size()); } void HttpSession::onError(const SockException &err) { if (_is_live_stream) { // flv/ts播放器 [AUTO-TRANSLATED:5b444fd9] // flv/ts player uint64_t duration = _ticker.createdTime() / 1000; WarnP(this) << "FLV/TS/FMP4播放器(" << _media_info.shortUrl() << ")断开:" << err << ",耗时(s):" << duration; GET_CONFIG(uint32_t, iFlowThreshold, General::kFlowThreshold); if (_total_bytes_usage >= iFlowThreshold * 1024) { NOTICE_EMIT(BroadcastFlowReportArgs, Broadcast::kBroadcastFlowReport, _media_info, _total_bytes_usage, duration, true, *this); } return; } } void HttpSession::setTimeoutSec(size_t keep_alive_sec) { if (!keep_alive_sec) { GET_CONFIG(size_t, s_keep_alive_sec, Http::kKeepAliveSecond); keep_alive_sec = s_keep_alive_sec; } _keep_alive_sec = keep_alive_sec; getSock()->setSendTimeOutSecond(keep_alive_sec); } void HttpSession::setMaxReqSize(size_t max_req_size) { if (!max_req_size) { GET_CONFIG(size_t, s_max_req_size, Http::kMaxReqSize); max_req_size = s_max_req_size; } _max_req_size = max_req_size; setMaxCacheSize(max_req_size); } void HttpSession::onManager() { if (_ticker.elapsedTime() > _keep_alive_sec * 1000) { // http超时 [AUTO-TRANSLATED:6f2fdd1f] // http timeout shutdown(SockException(Err_timeout, "session timeout")); } } bool HttpSession::checkWebSocket() { auto Sec_WebSocket_Key = _parser["Sec-WebSocket-Key"]; if (Sec_WebSocket_Key.empty()) { 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; if (!_parser["Sec-WebSocket-Protocol"].empty()) { headerOut["Sec-WebSocket-Protocol"] = _parser["Sec-WebSocket-Protocol"]; } auto res_cb = [this, headerOut]() { _live_over_websocket = true; sendResponse(101, false, nullptr, headerOut, nullptr, true); }; auto res_cb_flv = [this, headerOut]() mutable { _live_over_websocket = true; headerOut.emplace("Cache-Control", "no-store"); sendResponse(101, false, nullptr, headerOut, nullptr, true); }; // 判断是否为websocket-flv [AUTO-TRANSLATED:31682d7a] // Determine whether it is websocket-flv if (checkLiveStreamFlv(res_cb_flv)) { // 这里是websocket-flv直播请求 [AUTO-TRANSLATED:4bea5956] // This is a websocket-flv live request return true; } // 判断是否为websocket-ts [AUTO-TRANSLATED:9e8eb374] // Determine whether it is websocket-ts if (checkLiveStreamTS(res_cb)) { // 这里是websocket-ts直播请求 [AUTO-TRANSLATED:8ab08dd6] // This is a websocket-ts live request return true; } // 判断是否为websocket-fmp4 [AUTO-TRANSLATED:318f793f] // Determine whether it is websocket-fmp4 if (checkLiveStreamFMP4(res_cb)) { // 这里是websocket-fmp4直播请求 [AUTO-TRANSLATED:ccf0c1e2] // This is a websocket-fmp4 live request return true; } // 这是普通的websocket连接 [AUTO-TRANSLATED:754721f8] // This is a normal websocket connection if (!onWebSocketConnect(_parser)) { sendResponse(501, true, nullptr, headerOut); return true; } sendResponse(101, false, nullptr, headerOut, nullptr, true); return true; } bool HttpSession::checkLiveStream(const string &schema, const string &url_suffix, const function &cb) { std::string url = _parser.url(); auto it = _parser.getUrlArgs().find("schema"); if (it != _parser.getUrlArgs().end()) { if (strcasecmp(it->second.c_str(), schema.c_str())) { // unsupported schema return false; } } else { auto prefix_size = url_suffix.size(); if (url.size() < prefix_size || strcasecmp(url.data() + (url.size() - prefix_size), url_suffix.data())) { // 未找到后缀 [AUTO-TRANSLATED:6635499a] // Suffix not found return false; } // url去除特殊后缀 [AUTO-TRANSLATED:31c0c080] // Remove special suffix from url url.resize(url.size() - prefix_size); } // 带参数的url [AUTO-TRANSLATED:074764b0] // Url with parameters if (!_parser.params().empty()) { url += "?"; url += _parser.params(); } // 解析带上协议+参数完整的url [AUTO-TRANSLATED:5cdc7e68] // Parse the complete url with protocol + parameters _media_info.parse(schema + "://" + _parser["Host"] + url); if (_media_info.app.empty() || _media_info.stream.empty()) { // url不合法 [AUTO-TRANSLATED:9aad134e] // URL is invalid return false; } bool close_flag = !strcasecmp(_parser["Connection"].data(), "close"); weak_ptr weak_self = static_pointer_cast(shared_from_this()); // 鉴权结果回调 [AUTO-TRANSLATED:021df191] // Authentication result callback auto onRes = [cb, weak_self, close_flag](const string &err) { auto strong_self = weak_self.lock(); if (!strong_self) { // 本对象已经销毁 [AUTO-TRANSLATED:713e0f23] // This object has been destroyed return; } if (!err.empty()) { // 播放鉴权失败 [AUTO-TRANSLATED:64f99eeb] // Playback authentication failed strong_self->sendResponse(401, close_flag, nullptr, KeyValue(), std::make_shared(err)); return; } // 异步查找直播流 [AUTO-TRANSLATED:7cde5dac] // Asynchronously find live stream MediaSource::findAsync(strong_self->_media_info, strong_self, [weak_self, close_flag, cb](const MediaSource::Ptr &src) { auto strong_self = weak_self.lock(); if (!strong_self) { // 本对象已经销毁 [AUTO-TRANSLATED:713e0f23] // This object has been destroyed return; } if (!src) { // 未找到该流 [AUTO-TRANSLATED:2699ef82] // Stream not found strong_self->sendNotFound(close_flag); } else { strong_self->_is_live_stream = true; // 触发回调 [AUTO-TRANSLATED:ae2ff258] // Trigger callback cb(src); } }); }; Broadcast::AuthInvoker invoker = [weak_self, onRes](const string &err) { if (auto strong_self = weak_self.lock()) { strong_self->async([onRes, err]() { onRes(err); }); } }; auto flag = NOTICE_EMIT(BroadcastMediaPlayedArgs, Broadcast::kBroadcastMediaPlayed, _media_info, invoker, *this); if (!flag) { // 该事件无人监听,默认不鉴权 [AUTO-TRANSLATED:e1fbc6ae] // No one is listening to this event, no authentication by default onRes(""); } return true; } // http-fmp4 链接格式:http://vhost-url:port/app/streamid.live.mp4?key1=value1&key2=value2 [AUTO-TRANSLATED:c0174f8f] // http-fmp4 link format: http://vhost-url:port/app/streamid.live.mp4?key1=value1&key2=value2 bool HttpSession::checkLiveStreamFMP4(const function &cb) { return checkLiveStream(FMP4_SCHEMA, ".live.mp4", [this, cb](const MediaSource::Ptr &src) { auto fmp4_src = dynamic_pointer_cast(src); assert(fmp4_src); if (!cb) { // 找到源,发送http头,负载后续发送 [AUTO-TRANSLATED:ac272410] // Found the source, send the http header, and send the load later sendResponse(200, false, HttpFileManager::getContentType(".mp4").data(), KeyValue(), nullptr, true); } else { // 自定义发送http头 [AUTO-TRANSLATED:b8a8f683] // Custom send http header cb(); } // 直播牺牲延时提升发送性能 [AUTO-TRANSLATED:7c6616c9] // Live streaming sacrifices delay to improve sending performance setSocketFlags(); onWrite(std::make_shared(fmp4_src->getInitSegment()), true); weak_ptr weak_self = static_pointer_cast(shared_from_this()); fmp4_src->pause(false); _fmp4_reader = fmp4_src->getRing()->attach(getPoller()); _fmp4_reader->setGetInfoCB([weak_self]() { Any ret; ret.set(static_pointer_cast(weak_self.lock())); return ret; }); _fmp4_reader->setDetachCB([weak_self]() { auto strong_self = weak_self.lock(); if (!strong_self) { // 本对象已经销毁 [AUTO-TRANSLATED:713e0f23] // This object has been destroyed return; } strong_self->shutdown(SockException(Err_shutdown, "fmp4 ring buffer detached")); }); _fmp4_reader->setReadCB([weak_self](const FMP4MediaSource::RingDataType &fmp4_list) { auto strong_self = weak_self.lock(); if (!strong_self) { // 本对象已经销毁 [AUTO-TRANSLATED:713e0f23] // This object has been destroyed return; } size_t i = 0; auto size = fmp4_list->size(); fmp4_list->for_each([&](const FMP4Packet::Ptr &ts) { strong_self->onWrite(ts, ++i == size); }); }); }); } // http-ts 链接格式:http://vhost-url:port/app/streamid.live.ts?key1=value1&key2=value2 [AUTO-TRANSLATED:aa1a9151] // http-ts link format: http://vhost-url:port/app/streamid.live.ts?key1=value1&key2=value2 bool HttpSession::checkLiveStreamTS(const function &cb) { return checkLiveStream(TS_SCHEMA, ".live.ts", [this, cb](const MediaSource::Ptr &src) { auto ts_src = dynamic_pointer_cast(src); assert(ts_src); if (!cb) { // 找到源,发送http头,负载后续发送 [AUTO-TRANSLATED:ac272410] // Found the source, send the http header, and send the load later sendResponse(200, false, HttpFileManager::getContentType(".ts").data(), KeyValue(), nullptr, true); } else { // 自定义发送http头 [AUTO-TRANSLATED:b8a8f683] // Custom send http header cb(); } // 直播牺牲延时提升发送性能 [AUTO-TRANSLATED:7c6616c9] // Live streaming sacrifices delay to improve sending performance setSocketFlags(); weak_ptr weak_self = static_pointer_cast(shared_from_this()); ts_src->pause(false); _ts_reader = ts_src->getRing()->attach(getPoller()); _ts_reader->setGetInfoCB([weak_self]() { Any ret; ret.set(static_pointer_cast(weak_self.lock())); return ret; }); _ts_reader->setDetachCB([weak_self]() { auto strong_self = weak_self.lock(); if (!strong_self) { // 本对象已经销毁 [AUTO-TRANSLATED:713e0f23] // This object has been destroyed return; } strong_self->shutdown(SockException(Err_shutdown, "ts ring buffer detached")); }); _ts_reader->setReadCB([weak_self](const TSMediaSource::RingDataType &ts_list) { auto strong_self = weak_self.lock(); if (!strong_self) { // 本对象已经销毁 [AUTO-TRANSLATED:713e0f23] // This object has been destroyed return; } size_t i = 0; auto size = ts_list->size(); ts_list->for_each([&](const TSPacket::Ptr &ts) { strong_self->onWrite(ts, ++i == size); }); }); }); } // http-flv 链接格式:http://vhost-url:port/app/streamid.live.flv?key1=value1&key2=value2 [AUTO-TRANSLATED:7e78aa20] // http-flv link format: http://vhost-url:port/app/streamid.live.flv?key1=value1&key2=value2 bool HttpSession::checkLiveStreamFlv(const function &cb) { auto start_pts = atoll(_parser.getUrlArgs()["starPts"].data()); return checkLiveStream(RTMP_SCHEMA, ".live.flv", [this, cb, start_pts](const MediaSource::Ptr &src) { auto rtmp_src = dynamic_pointer_cast(src); assert(rtmp_src); if (!cb) { // 找到源,发送http头,负载后续发送 [AUTO-TRANSLATED:ac272410] // Found the source, send the http header, and send the load later KeyValue headerOut; headerOut["Cache-Control"] = "no-store"; sendResponse(200, false, HttpFileManager::getContentType(".flv").data(), headerOut, nullptr, true); } else { // 自定义发送http头 [AUTO-TRANSLATED:b8a8f683] // Custom send http header cb(); } // 直播牺牲延时提升发送性能 [AUTO-TRANSLATED:7c6616c9] // Live streaming sacrifices delay to improve sending performance setSocketFlags(); // 非H264/AAC时打印警告日志,防止用户提无效问题 [AUTO-TRANSLATED:59ee60df] // Print warning log when it is not H264/AAC, to prevent users from raising invalid issues auto tracks = src->getTracks(false); for (auto &track : tracks) { switch (track->getCodecId()) { case CodecH264: case CodecAAC: break; default: { WarnP(this) << "flv播放器一般只支持H264和AAC编码,该编码格式可能不被播放器支持:" << track->getCodecName(); break; } } } start(getPoller(), rtmp_src, start_pts); }); } void HttpSession::onHttpRequest_GET() { // 先看看是否为WebSocket请求 [AUTO-TRANSLATED:98cd3a86] // First check if it is a WebSocket request if (checkWebSocket()) { // 后续都是websocket body数据 [AUTO-TRANSLATED:c4fcbdcf] // The following are all websocket body data _on_recv_body = [this](const char *data, size_t len) { WebSocketSplitter::decode((uint8_t *)data, len); // _contentCallBack是可持续的,后面还要处理后续数据 [AUTO-TRANSLATED:920e8c23] // _contentCallBack is sustainable, and subsequent data needs to be processed later return true; }; return; } if (emitHttpEvent(false)) { // 拦截http api事件 [AUTO-TRANSLATED:2f5e319d] // Intercept http api events return; } if (checkLiveStreamFlv()) { // 拦截http-flv播放器 [AUTO-TRANSLATED:299f6449] // Intercept http-flv player return; } if (checkLiveStreamTS()) { // 拦截http-ts播放器 [AUTO-TRANSLATED:d9e303e4] // Intercept http-ts player return; } if (checkLiveStreamFMP4()) { // 拦截http-fmp4播放器 [AUTO-TRANSLATED:78cdf3a1] // Intercept http-fmp4 player return; } bool bClose = !strcasecmp(_parser["Connection"].data(), "close"); weak_ptr weak_self = static_pointer_cast(shared_from_this()); HttpFileManager::onAccessPath(*this, _parser, [weak_self, bClose](int code, const string &content_type, const StrCaseMap &responseHeader, const HttpBody::Ptr &body) { auto strong_self = weak_self.lock(); if (!strong_self) { return; } strong_self->async([weak_self, bClose, code, content_type, responseHeader, body]() { auto strong_self = weak_self.lock(); if (!strong_self) { return; } strong_self->sendResponse(code, bClose, content_type.data(), responseHeader, body); }); }); } 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; } class AsyncSenderData { public: friend class AsyncSender; using Ptr = std::shared_ptr; AsyncSenderData(HttpSession::Ptr session, const HttpBody::Ptr &body, bool close_when_complete) { _session = std::move(session); _body = body; _close_when_complete = close_when_complete; } private: std::weak_ptr _session; HttpBody::Ptr _body; bool _close_when_complete; bool _read_complete = false; }; class AsyncSender { public: using Ptr = std::shared_ptr; static bool onSocketFlushed(const AsyncSenderData::Ptr &data) { if (data->_read_complete) { if (data->_close_when_complete) { // 发送完毕需要关闭socket [AUTO-TRANSLATED:fe660e55] // Close socket after sending is complete shutdown(data->_session.lock()); } return false; } GET_CONFIG(uint32_t, sendBufSize, Http::kSendBufSize); data->_body->readDataAsync(sendBufSize, [data](const Buffer::Ptr &sendBuf) { auto session = data->_session.lock(); if (!session) { // 本对象已经销毁 [AUTO-TRANSLATED:713e0f23] // This object has been destroyed return; } session->async([data, sendBuf]() { auto session = data->_session.lock(); if (!session) { // 本对象已经销毁 [AUTO-TRANSLATED:713e0f23] // This object has been destroyed return; } onRequestData(data, session, sendBuf); }, false); }); return true; } private: static void onRequestData(const AsyncSenderData::Ptr &data, const std::shared_ptr &session, const Buffer::Ptr &sendBuf) { session->_ticker.resetTime(); if (sendBuf && session->send(sendBuf) != -1) { // 文件还未读完,还需要继续发送 [AUTO-TRANSLATED:c454ca1a] // The file has not been read completely, and needs to be sent continuously if (!session->isSocketBusy()) { // socket还可写,继续请求数据 [AUTO-TRANSLATED:041df414] // Socket can still write, continue to request data onSocketFlushed(data); } return; } // 文件写完了 [AUTO-TRANSLATED:a9f8c117] // The file is written data->_read_complete = true; if (!session->isSocketBusy() && data->_close_when_complete) { shutdown(session); } } static void shutdown(const std::shared_ptr &session) { if (session) { session->shutdown(SockException(Err_shutdown, StrPrinter << "close connection after send http body completed.")); } } }; void HttpSession::sendResponse(int code, bool bClose, const char *pcContentType, const HttpSession::KeyValue &header, const HttpBody::Ptr &body, bool no_content_length) { GET_CONFIG(string, charSet, Http::kCharSet); GET_CONFIG(uint32_t, keepAliveSec, Http::kKeepAliveSecond); // body默认为空 [AUTO-TRANSLATED:527ccb6f] // Body defaults to empty int64_t size = 0; if (body && body->remainSize()) { // 有body,获取body大小 [AUTO-TRANSLATED:0d5f4b9a] // There is a body, get the body size size = body->remainSize(); } if (no_content_length) { // http-flv直播是Keep-Alive类型 [AUTO-TRANSLATED:0ef3adfe] // Http-flv live broadcast is Keep-Alive type bClose = false; } else if ((size_t)size >= SIZE_MAX || size < 0) { // 不固定长度的body,那么发送完body后应该关闭socket,以便浏览器做下载完毕的判断 [AUTO-TRANSLATED:fc714997] // If the body is not fixed length, then the socket should be closed after sending the body, so that the browser can judge the download completion bClose = true; } HttpSession::KeyValue &headerOut = const_cast(header); headerOut.emplace("Date", dateStr()); headerOut.emplace("Server", kServerName); headerOut.emplace("Connection", bClose ? "close" : "keep-alive"); GET_CONFIG(bool, allow_cross_domains, Http::kAllowCrossDomains); if (allow_cross_domains && !_origin.empty()) { headerOut.emplace("Access-Control-Allow-Origin", _origin); headerOut.emplace("Access-Control-Allow-Credentials", "true"); } if (!bClose) { string keepAliveString = "timeout="; keepAliveString += to_string(keepAliveSec); keepAliveString += ", max=100"; headerOut.emplace("Keep-Alive", std::move(keepAliveString)); } if (!no_content_length && size >= 0 && (size_t)size < SIZE_MAX) { // 文件长度为固定值,且不是http-flv强制设置Content-Length [AUTO-TRANSLATED:185c02a8] // The file length is a fixed value, and it is not http-flv that forcibly sets Content-Length headerOut["Content-Length"] = to_string(size); } if (size && !pcContentType) { // 有body时,设置缺省类型 [AUTO-TRANSLATED:21c9b233] // When there is a body, set the default type pcContentType = "text/plain"; } if ((size || no_content_length) && pcContentType) { // 有body时,设置文件类型 [AUTO-TRANSLATED:0dcbeecc] // When there is a body, set the file type string strContentType = pcContentType; strContentType += "; charset="; strContentType += charSet; headerOut.emplace("Content-Type", std::move(strContentType)); } // 发送http头 [AUTO-TRANSLATED:cca51598] // Send http header string str; str.reserve(256); str += "HTTP/1.1 "; str += to_string(code); str += ' '; str += HttpConst::getHttpStatusMessage(code); str += "\r\n"; for (auto &pr : header) { str += pr.first; str += ": "; str += pr.second; str += "\r\n"; } str += "\r\n"; SockSender::send(std::move(str)); _ticker.resetTime(); if (!size) { // 没有body [AUTO-TRANSLATED:bf891e3a] // No body if (bClose) { shutdown(SockException(Err_shutdown, StrPrinter << "close connection after send http header completed with status code:" << code)); } return; } #if 0 // sendfile跟共享mmap相比并没有性能上的优势,相反,sendfile还有功能上的缺陷,先屏蔽 [AUTO-TRANSLATED:4de77827] // Sendfile has no performance advantage over shared mmap, on the contrary, sendfile also has functional defects, so it is blocked first if (typeid(*this) == typeid(HttpSession) && !body->sendFile(getSock()->rawFD())) { // http支持sendfile优化 [AUTO-TRANSLATED:04f691f1] // Http supports sendfile optimization return; } #endif GET_CONFIG(uint32_t, sendBufSize, Http::kSendBufSize); if (body->remainSize() > sendBufSize) { // 文件下载提升发送性能 [AUTO-TRANSLATED:500922cc] // File download improves sending performance setSocketFlags(); } // 发送http body [AUTO-TRANSLATED:e9fc35d6] // Send http body AsyncSenderData::Ptr data = std::make_shared(static_pointer_cast(shared_from_this()), body, bClose); getSock()->setOnFlush([data]() { return AsyncSender::onSocketFlushed(data); }); AsyncSender::onSocketFlushed(data); } void HttpSession::urlDecode(Parser &parser) { parser.setUrl(strCoding::UrlDecodePath(parser.url())); for (auto &pr : _parser.getUrlArgs()) { const_cast(pr.second) = strCoding::UrlDecodeComponent(pr.second); } } bool HttpSession::emitHttpEvent(bool doInvoke) { bool bClose = !strcasecmp(_parser["Connection"].data(), "close"); // ///////////////////异步回复Invoker/////////////////////////////// [AUTO-TRANSLATED:6d0c5fda] // ///////////////////Asynchronous reply Invoker/////////////////////////////// weak_ptr weak_self = static_pointer_cast(shared_from_this()); HttpResponseInvoker invoker = [weak_self, bClose](int code, const KeyValue &headerOut, const HttpBody::Ptr &body) { auto strong_self = weak_self.lock(); if (!strong_self) { return; } strong_self->async([weak_self, bClose, code, headerOut, body]() { auto strong_self = weak_self.lock(); if (!strong_self) { // 本对象已经销毁 [AUTO-TRANSLATED:713e0f23] // This object has been destroyed return; } strong_self->sendResponse(code, bClose, nullptr, headerOut, body); }); }; // /////////////////广播HTTP事件/////////////////////////// [AUTO-TRANSLATED:fff9769c] // /////////////////Broadcast HTTP event/////////////////////////// bool consumed = false; // 该事件是否被消费 NOTICE_EMIT(BroadcastHttpRequestArgs, Broadcast::kBroadcastHttpRequest, _parser, invoker, consumed, *this); if (!consumed && doInvoke) { // 该事件无人消费,所以返回404 [AUTO-TRANSLATED:8a890dec] // This event is not consumed, so return 404 invoker(404, KeyValue(), HttpBody::Ptr()); } return consumed; } std::string HttpSession::get_peer_ip() { GET_CONFIG(string, forwarded_ip_header, Http::kForwardedIpHeader); if (!forwarded_ip_header.empty() && !_parser.getHeader()[forwarded_ip_header].empty()) { return _parser.getHeader()[forwarded_ip_header]; } return Session::get_peer_ip(); } void HttpSession::onHttpRequest_POST() { emitHttpEvent(true); } void HttpSession::sendNotFound(bool bClose) { GET_CONFIG(string, notFound, Http::kNotFound); sendResponse(404, bClose, "text/html", KeyValue(), std::make_shared(notFound)); } void HttpSession::setSocketFlags() { GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS); if (mergeWriteMS > 0) { // 推流模式下,关闭TCP_NODELAY会增加推流端的延时,但是服务器性能将提高 [AUTO-TRANSLATED:c8ec8fb8] // In push mode, closing TCP_NODELAY will increase the delay of the push end, but the server performance will be improved SockUtil::setNoDelay(getSock()->rawFD(), false); // 播放模式下,开启MSG_MORE会增加延时,但是能提高发送性能 [AUTO-TRANSLATED:7b558ab9] // In playback mode, enabling MSG_MORE will increase the delay, but it can improve sending performance setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE); } } void HttpSession::onWrite(const Buffer::Ptr &buffer, bool flush) { if (flush) { // 需要flush那么一次刷新缓存 [AUTO-TRANSLATED:8d1ec961] // Need to flush, then flush the cache once HttpSession::setSendFlushFlag(true); } _ticker.resetTime(); if (!_live_over_websocket) { _total_bytes_usage += buffer->size(); send(buffer); } else { WebSocketHeader header; header._fin = true; header._reserved = 0; header._opcode = WebSocketHeader::BINARY; header._mask_flag = false; WebSocketSplitter::encode(header, buffer); } if (flush) { // 本次刷新缓存后,下次不用刷新缓存 [AUTO-TRANSLATED:f56139f7] // After this cache flush, the next time you don't need to flush the cache HttpSession::setSendFlushFlag(false); } } void HttpSession::onWebSocketEncodeData(Buffer::Ptr buffer) { _total_bytes_usage += buffer->size(); send(std::move(buffer)); } void HttpSession::onWebSocketDecodeComplete(const WebSocketHeader &header_in) { WebSocketHeader &header = const_cast(header_in); header._mask_flag = false; switch (header._opcode) { case WebSocketHeader::CLOSE: { encode(header, nullptr); shutdown(SockException(Err_shutdown, "recv close request from client")); break; } default: break; } } void HttpSession::onDetach() { shutdown(SockException(Err_shutdown, "rtmp ring buffer detached")); } std::shared_ptr HttpSession::getSharedPtr() { return dynamic_pointer_cast(shared_from_this()); } } /* namespace mediakit */