2019-11-30 11:38:00 +08:00
/*
2023-12-09 16:23:51 +08:00
* Copyright ( c ) 2016 - present The ZLMediaKit project authors . All Rights Reserved .
2019-11-30 11:38:00 +08:00
*
2023-12-09 16:23:51 +08:00
* This file is part of ZLMediaKit ( https : //github.com/ZLMediaKit/ZLMediaKit).
2019-11-30 11:38:00 +08:00
*
2023-12-09 16:23:51 +08:00
* Use of this source code is governed by MIT - like license that can be found in the
2020-04-04 20:30:09 +08:00
* 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 .
2019-11-30 11:38:00 +08:00
*/
# include <iomanip>
# include "Util/File.h"
2021-08-25 14:30:31 +08:00
# include "Common/Parser.h"
2022-11-29 11:07:13 +08:00
# include "Common/config.h"
2023-06-10 18:46:30 +08:00
# include "Common/strCoding.h"
2023-12-02 20:56:39 +08:00
# include "Record/HlsMediaSource.h"
# include "HttpConst.h"
# include "HttpSession.h"
# include "HttpFileManager.h"
2019-11-30 11:38:00 +08:00
2022-02-02 20:34:50 +08:00
using namespace std ;
using namespace toolkit ;
2019-11-30 11:38:00 +08:00
namespace mediakit {
2024-09-19 14:53:50 +08:00
// hls的播放cookie缓存时间默认60秒, [AUTO-TRANSLATED:88198dfa]
// The default cache time for the hls playback cookie is 60 seconds.
// 每次访问一次该cookie, 那么将重新刷新cookie有效期 [AUTO-TRANSLATED:a1b76209]
// Each time this cookie is accessed, the cookie's validity period will be refreshed.
// 假如播放器在60秒内都未访问该cookie, 那么将重新触发hls播放鉴权 [AUTO-TRANSLATED:55000c94]
// If the player does not access the cookie within 60 seconds, the hls playback authentication will be triggered again.
2023-12-02 10:22:45 +08:00
static size_t kHlsCookieSecond = 60 ;
static size_t kFindSrcIntervalSecond = 3 ;
2019-11-30 11:38:00 +08:00
static const string kCookieName = " ZL_COOKIE " ;
2019-11-30 14:29:44 +08:00
static const string kHlsSuffix = " /hls.m3u8 " ;
2023-07-02 12:02:33 +08:00
static const string kHlsFMP4Suffix = " /hls.fmp4.m3u8 " ;
2020-01-02 17:46:20 +08:00
2022-10-18 19:23:20 +08:00
struct HttpCookieAttachment {
2024-09-19 14:53:50 +08:00
// 是否已经查找到过MediaSource [AUTO-TRANSLATED:b5b9922a]
// Whether the MediaSource has been found
2022-10-18 19:23:20 +08:00
bool _find_src = false ;
2024-09-19 14:53:50 +08:00
// 查找MediaSource计时 [AUTO-TRANSLATED:39904ba9]
// MediaSource search timing
2023-10-12 11:05:41 +08:00
Ticker _find_src_ticker ;
2024-09-19 14:53:50 +08:00
// cookie生效作用域, 本cookie只对该目录下的文件生效 [AUTO-TRANSLATED:7a59ad9a]
// Cookie effective scope, this cookie only takes effect for files under this directory
2020-01-02 17:46:20 +08:00
string _path ;
2024-09-19 14:53:50 +08:00
// 上次鉴权失败信息,为空则上次鉴权成功 [AUTO-TRANSLATED:de48b753]
// Last authentication failure information, empty means last authentication succeeded
2020-01-02 17:46:20 +08:00
string _err_msg ;
2024-09-19 14:53:50 +08:00
// hls直播时的其他一些信息, 主要用于播放器个数计数以及流量计数 [AUTO-TRANSLATED:790de53a]
// Other information during hls live broadcast, mainly used for player count and traffic count
2020-01-02 17:46:20 +08:00
HlsCookieData : : Ptr _hls_data ;
} ;
2019-11-30 11:38:00 +08:00
2020-01-02 18:24:11 +08:00
const string & HttpFileManager : : getContentType ( const char * name ) {
2023-06-10 10:16:45 +08:00
return HttpConst : : getHttpContentType ( name ) ;
2019-11-30 11:38:00 +08:00
}
2023-09-26 14:48:19 +08:00
namespace {
class UInt128 {
public :
UInt128 ( ) = default ;
2023-07-26 16:33:21 +08:00
2023-09-26 14:48:19 +08:00
UInt128 ( const struct sockaddr_storage & storage ) {
_family = storage . ss_family ;
memset ( _bytes , 0 , 16 ) ;
switch ( storage . ss_family ) {
case AF_INET : {
memcpy ( _bytes , & ( reinterpret_cast < const struct sockaddr_in & > ( storage ) . sin_addr ) , 4 ) ;
break ;
}
case AF_INET6 : {
memcpy ( _bytes , & ( reinterpret_cast < const struct sockaddr_in6 & > ( storage ) . sin6_addr ) , 16 ) ;
break ;
}
default : CHECK ( false , " Invalid socket family " ) ; break ;
2023-07-26 16:33:21 +08:00
}
2023-09-26 14:48:19 +08:00
}
bool operator = = ( const UInt128 & that ) const { return _family = = that . _family & & ! memcmp ( _bytes , that . _bytes , 16 ) ; }
bool operator < = ( const UInt128 & that ) const { return * this < that | | * this = = that ; }
bool operator > = ( const UInt128 & that ) const { return * this > that | | * this = = that ; }
bool operator > ( const UInt128 & that ) const { return that < * this ; }
bool operator < ( const UInt128 & that ) const {
auto sz = _family = = AF_INET ? 4 : 16 ;
for ( int i = 0 ; i < sz ; + + i ) {
if ( _bytes [ i ] < that . _bytes [ i ] ) {
return true ;
} else if ( _bytes [ i ] > that . _bytes [ i ] ) {
return false ;
}
2023-07-26 16:33:21 +08:00
}
2023-09-26 14:48:19 +08:00
return false ;
}
operator bool ( ) const { return _family ! = - 1 ; }
bool same_type ( const UInt128 & that ) const { return _family = = that . _family ; }
private :
int _family = - 1 ;
uint8_t _bytes [ 16 ] ;
} ;
}
static UInt128 get_ip_uint64 ( const std : : string & ip ) {
try {
return UInt128 ( SockUtil : : make_sockaddr ( ip . data ( ) , 0 ) ) ;
2023-07-26 16:33:21 +08:00
} catch ( std : : exception & ex ) {
WarnL < < ex . what ( ) ;
}
2023-09-26 14:48:19 +08:00
return UInt128 ( ) ;
2023-07-26 16:33:21 +08:00
}
bool HttpFileManager : : isIPAllowed ( const std : : string & ip ) {
2023-09-26 14:48:19 +08:00
using IPRangs = std : : vector < std : : pair < UInt128 /*min_ip*/ , UInt128 /*max_ip*/ > > ;
2023-07-26 16:33:21 +08:00
GET_CONFIG_FUNC ( IPRangs , allow_ip_range , Http : : kAllowIPRange , [ ] ( const string & str ) - > IPRangs {
IPRangs ret ;
auto vec = split ( str , " , " ) ;
for ( auto & item : vec ) {
2023-07-27 15:29:11 +08:00
if ( trim ( item ) . empty ( ) ) {
continue ;
}
2023-07-26 16:33:21 +08:00
auto range = split ( item , " - " ) ;
if ( range . size ( ) = = 2 ) {
2023-07-27 15:29:11 +08:00
auto ip_min = get_ip_uint64 ( trim ( range [ 0 ] ) ) ;
auto ip_max = get_ip_uint64 ( trim ( range [ 1 ] ) ) ;
2023-09-26 14:48:19 +08:00
if ( ip_min & & ip_max & & ip_min . same_type ( ip_max ) ) {
2023-07-27 15:29:11 +08:00
ret . emplace_back ( ip_min , ip_max ) ;
2023-09-26 14:48:19 +08:00
} else {
WarnL < < " Invalid ip range or family: " < < item ;
2023-07-27 15:29:11 +08:00
}
2023-07-26 16:33:21 +08:00
} else if ( range . size ( ) = = 1 ) {
auto ip = get_ip_uint64 ( trim ( range [ 0 ] ) ) ;
2023-07-27 15:29:11 +08:00
if ( ip ) {
ret . emplace_back ( ip , ip ) ;
2023-09-26 14:48:19 +08:00
} else {
WarnL < < " Invalid ip: " < < item ;
2023-07-27 15:29:11 +08:00
}
2023-07-26 16:33:21 +08:00
} else {
WarnL < < " Invalid ip range: " < < item ;
}
}
return ret ;
} ) ;
if ( allow_ip_range . empty ( ) ) {
return true ;
}
2023-07-26 16:45:47 +08:00
auto ip_int = get_ip_uint64 ( ip ) ;
2023-07-26 16:33:21 +08:00
for ( auto & range : allow_ip_range ) {
2023-09-26 14:48:19 +08:00
if ( ip_int . same_type ( range . first ) & & ip_int > = range . first & & ip_int < = range . second ) {
2023-07-26 16:33:21 +08:00
return true ;
}
}
return false ;
}
2023-12-02 20:56:39 +08:00
static std : : string fileName ( const string & dir , const string & path ) {
auto ret = path . substr ( dir . size ( ) ) ;
if ( ret . front ( ) = = ' / ' ) {
ret . erase ( 0 , 1 ) ;
2019-11-30 11:38:00 +08:00
}
2023-12-02 20:56:39 +08:00
return ret ;
}
static string searchIndexFile ( const string & dir ) {
std : : string ret ;
static set < std : : string , StrCaseCompare > indexSet = { " index.html " , " index.htm " } ;
File : : scanDir ( dir , [ & ] ( const string & path , bool is_dir ) {
if ( is_dir ) {
return true ;
2019-11-30 11:38:00 +08:00
}
2023-12-02 20:56:39 +08:00
auto name = fileName ( dir , path ) ;
if ( indexSet . find ( name ) = = indexSet . end ( ) ) {
return true ;
}
ret = std : : move ( name ) ;
return false ;
} ) ;
return ret ;
2019-11-30 11:38:00 +08:00
}
static bool makeFolderMenu ( const string & httpPath , const string & strFullPath , string & strRet ) {
2020-06-30 09:16:02 +08:00
GET_CONFIG ( bool , dirMenu , Http : : kDirMenu ) ;
2020-09-20 10:13:15 +08:00
if ( ! dirMenu ) {
2024-09-19 14:53:50 +08:00
// 不允许浏览文件夹 [AUTO-TRANSLATED:a0c30a94]
// Not allowed to browse folders
2020-06-30 09:16:02 +08:00
return false ;
}
2019-11-30 11:38:00 +08:00
string strPathPrefix ( strFullPath ) ;
2024-09-19 14:53:50 +08:00
// url后缀有没有'/'访问文件夹,处理逻辑不一致 [AUTO-TRANSLATED:39c6a933]
// Whether the url suffix has '/' to access the folder, the processing logic is inconsistent
2019-11-30 11:38:00 +08:00
string last_dir_name ;
2020-09-20 10:13:15 +08:00
if ( strPathPrefix . back ( ) = = ' / ' ) {
2019-11-30 11:38:00 +08:00
strPathPrefix . pop_back ( ) ;
2020-09-20 10:13:15 +08:00
} else {
last_dir_name = split ( strPathPrefix , " / " ) . back ( ) ;
2019-11-30 11:38:00 +08:00
}
2023-12-02 19:49:28 +08:00
if ( ! File : : is_dir ( strPathPrefix ) ) {
2019-11-30 11:38:00 +08:00
return false ;
}
stringstream ss ;
2021-08-26 19:36:38 +08:00
ss < < " <html> \r \n "
" <head> \r \n "
2023-12-02 16:43:06 +08:00
" <title>File Index</title> \r \n "
2021-08-26 19:36:38 +08:00
" </head> \r \n "
" <body> \r \n "
2023-12-02 16:43:06 +08:00
" <h1>Index of " ;
2019-11-30 11:38:00 +08:00
ss < < httpPath ;
ss < < " </h1> \r \n " ;
if ( httpPath ! = " / " ) {
ss < < " <li><a href= \" " ;
ss < < " / " ;
ss < < " \" > " ;
2023-12-02 16:43:06 +08:00
ss < < " root " ;
2019-11-30 11:38:00 +08:00
ss < < " </a></li> \r \n " ;
ss < < " <li><a href= \" " ;
2021-08-26 19:36:38 +08:00
if ( ! last_dir_name . empty ( ) ) {
2019-11-30 11:38:00 +08:00
ss < < " ./ " ;
2021-08-26 19:36:38 +08:00
} else {
2019-11-30 11:38:00 +08:00
ss < < " ../ " ;
}
ss < < " \" > " ;
2023-12-02 16:43:06 +08:00
ss < < " ../ " ;
2019-11-30 11:38:00 +08:00
ss < < " </a></li> \r \n " ;
}
2021-08-26 19:36:38 +08:00
multimap < string /*url name*/ , std : : pair < string /*note name*/ , string /*file path*/ > > file_map ;
2023-12-02 20:56:39 +08:00
File : : scanDir ( strPathPrefix , [ & ] ( const std : : string & path , bool isDir ) {
auto name = fileName ( strPathPrefix , path ) ;
2024-01-28 19:18:36 +08:00
file_map . emplace ( strCoding : : UrlEncodePath ( name ) , std : : make_pair ( name , path ) ) ;
2023-12-02 20:56:39 +08:00
return true ;
} ) ;
2024-09-19 14:53:50 +08:00
// 如果是root目录, 添加虚拟目录 [AUTO-TRANSLATED:3149d7f9]
// If it is the root directory, add a virtual directory
2021-08-25 14:30:31 +08:00
if ( httpPath = = " / " ) {
2021-08-27 11:05:26 +08:00
GET_CONFIG_FUNC ( StrCaseMap , virtualPathMap , Http : : kVirtualPath , [ ] ( const string & str ) {
return Parser : : parseArgs ( str , " ; " , " , " ) ;
} ) ;
for ( auto & pr : virtualPathMap ) {
2023-12-02 16:43:06 +08:00
file_map . emplace ( pr . first , std : : make_pair ( string ( " virtual path: " ) + pr . first , File : : absolutePath ( " " , pr . second ) ) ) ;
2021-08-25 14:30:31 +08:00
}
}
2019-11-30 11:38:00 +08:00
int i = 0 ;
2021-08-26 19:36:38 +08:00
for ( auto & pr : file_map ) {
auto & strAbsolutePath = pr . second . second ;
2023-12-02 19:49:28 +08:00
bool isDir = File : : is_dir ( strAbsolutePath ) ;
2019-11-30 11:38:00 +08:00
ss < < " <li><span> " < < i + + < < " </span> \t " ;
ss < < " <a href= \" " ;
2024-09-19 14:53:50 +08:00
// 路径链接地址 [AUTO-TRANSLATED:33bc5f41]
// Path link address
2020-09-20 10:13:15 +08:00
if ( ! last_dir_name . empty ( ) ) {
2021-08-26 19:36:38 +08:00
ss < < last_dir_name < < " / " < < pr . first ;
2020-09-20 10:13:15 +08:00
} else {
2021-08-26 19:36:38 +08:00
ss < < pr . first ;
2019-11-30 11:38:00 +08:00
}
2020-09-20 10:13:15 +08:00
if ( isDir ) {
2019-11-30 11:38:00 +08:00
ss < < " / " ;
}
ss < < " \" > " ;
2024-09-19 14:53:50 +08:00
// 路径名称 [AUTO-TRANSLATED:4dae8790]
// Path name
2021-08-26 19:36:38 +08:00
ss < < pr . second . first ;
2019-11-30 11:38:00 +08:00
if ( isDir ) {
ss < < " /</a></li> \r \n " ;
continue ;
}
2024-09-19 14:53:50 +08:00
// 是文件 [AUTO-TRANSLATED:70473f2f]
// It's a file
2023-12-02 19:49:28 +08:00
auto fileSize = File : : fileSize ( strAbsolutePath ) ;
2022-02-23 17:03:13 +08:00
if ( fileSize < 1024 ) {
ss < < " ( " < < fileSize < < " B) " < < endl ;
} else if ( fileSize < 1024 * 1024 ) {
ss < < fixed < < setprecision ( 2 ) < < " ( " < < fileSize / 1024.0 < < " KB) " ;
} else if ( fileSize < 1024 * 1024 * 1024 ) {
2024-11-29 23:07:02 +08:00
ss < < fixed < < setprecision ( 2 ) < < " ( " < < ( fileSize > > 10 ) / 1024.0 < < " MB) " ;
2022-02-23 17:03:13 +08:00
} else {
2024-11-29 23:07:02 +08:00
ss < < fixed < < setprecision ( 2 ) < < " ( " < < ( fileSize > > 20 ) / 1024.0 < < " GB) " ;
2019-11-30 11:38:00 +08:00
}
ss < < " </a></li> \r \n " ;
}
ss < < " <ul> \r \n " ;
ss < < " </ul> \r \n </body></html> " ;
ss . str ( ) . swap ( strRet ) ;
return true ;
}
2024-09-19 14:53:50 +08:00
// 拦截hls的播放请求 [AUTO-TRANSLATED:dd1bbeec]
// Intercept the hls playback request
2022-11-19 09:33:10 +08:00
static bool emitHlsPlayed ( const Parser & parser , const MediaInfo & media_info , const HttpSession : : HttpAccessPathInvoker & invoker , Session & sender ) {
2024-09-19 14:53:50 +08:00
// 访问的hls.m3u8结尾, 我们转换成kBroadcastMediaPlayed事件 [AUTO-TRANSLATED:b7a67c84]
// The hls.m3u8 ending of the access, we convert it to the kBroadcastMediaPlayed event
2020-09-20 10:13:15 +08:00
Broadcast : : AuthInvoker auth_invoker = [ invoker ] ( const string & err ) {
2024-09-19 14:53:50 +08:00
// cookie有效期为kHlsCookieSecond [AUTO-TRANSLATED:a0026dcd]
// The cookie validity period is kHlsCookieSecond
2020-09-20 10:13:15 +08:00
invoker ( err , " " , kHlsCookieSecond ) ;
2019-11-30 11:38:00 +08:00
} ;
2023-09-02 10:52:07 +08:00
bool flag = NOTICE_EMIT ( BroadcastMediaPlayedArgs , Broadcast : : kBroadcastMediaPlayed , media_info , auth_invoker , sender ) ;
2020-09-20 10:13:15 +08:00
if ( ! flag ) {
2024-09-19 14:53:50 +08:00
// 未开启鉴权,那么允许播放 [AUTO-TRANSLATED:077feed1]
// Authentication is not enabled, so playback is allowed
2020-09-20 10:13:15 +08:00
auth_invoker ( " " ) ;
2020-09-12 19:14:35 +08:00
}
return flag ;
2019-11-30 11:38:00 +08:00
}
2020-04-23 22:04:59 +08:00
class SockInfoImp : public SockInfo {
public :
2022-12-02 14:43:06 +08:00
using Ptr = std : : shared_ptr < SockInfoImp > ;
2020-04-23 22:04:59 +08:00
2020-09-20 10:13:15 +08:00
string get_local_ip ( ) override {
2020-04-23 22:04:59 +08:00
return _local_ip ;
}
2020-09-20 10:13:15 +08:00
uint16_t get_local_port ( ) override {
2020-04-23 22:04:59 +08:00
return _local_port ;
}
2020-09-20 10:13:15 +08:00
string get_peer_ip ( ) override {
2020-04-23 22:04:59 +08:00
return _peer_ip ;
}
2020-09-20 10:13:15 +08:00
uint16_t get_peer_port ( ) override {
2020-04-23 22:04:59 +08:00
return _peer_port ;
}
2020-09-20 10:13:15 +08:00
string getIdentifier ( ) const override {
2020-04-23 22:04:59 +08:00
return _identifier ;
}
string _local_ip ;
string _peer_ip ;
string _identifier ;
uint16_t _local_port ;
uint16_t _peer_port ;
} ;
2019-11-30 11:38:00 +08:00
/**
* 判 断 http客户端是否有权限访问文件的逻辑步骤
* 1 、 根 据 http请求头查找cookie , 找 到 进 入 步 骤 3
* 2 、 根 据 http url参数查找cookie , 如 果 还 是 未 找 到 cookie则进入步骤5
* 3 、 cookie标记是否有权限访问文件 , 如 果 有 权 限 , 直 接 返 回 文 件
* 4 、 cookie中记录的url参数是否跟本次url参数一致 , 如 果 一 致 直 接 返 回 客 户 端 错 误 码
* 5 、 触 发 kBroadcastHttpAccess事件
2024-09-19 14:53:50 +08:00
* The logical steps to determine whether the http client has permission to access the file
* 1. Find the cookie according to the http request header , find it and enter step 3
* 2. Find the cookie according to the http url parameter , if the cookie is still not found , enter step 5
* 3. Whether the cookie mark has permission to access the file , if it has permission , return the file directly
* 4. Whether the url parameter recorded in the cookie is consistent with the current url parameter , if it is consistent , return the client error code directly
* 5. Trigger the kBroadcastHttpAccess event
* [ AUTO - TRANSLATED : dfc0f15f ]
2019-11-30 11:38:00 +08:00
*/
2022-11-19 09:33:10 +08:00
static void canAccessPath ( Session & sender , const Parser & parser , const MediaInfo & media_info , bool is_dir ,
2022-02-12 16:24:55 +08:00
const function < void ( const string & err_msg , const HttpServerCookie : : Ptr & cookie ) > & callback ) {
2024-09-19 14:53:50 +08:00
// 获取用户唯一id [AUTO-TRANSLATED:5b1cf4bf]
// Get the user's unique id
2023-06-10 11:04:52 +08:00
auto uid = parser . params ( ) ;
auto path = parser . url ( ) ;
2019-11-30 11:38:00 +08:00
2024-09-19 14:53:50 +08:00
// 先根据http头中的cookie字段获取cookie [AUTO-TRANSLATED:155cf682]
// First get the cookie according to the cookie field in the http header
2020-04-20 18:13:45 +08:00
HttpServerCookie : : Ptr cookie = HttpCookieManager : : Instance ( ) . getCookie ( kCookieName , parser . getHeader ( ) ) ;
2024-09-19 14:53:50 +08:00
// 是否需要更新cookie [AUTO-TRANSLATED:b95121d5]
// Whether to update the cookie
2022-02-12 16:24:55 +08:00
bool update_cookie = false ;
2019-11-30 11:38:00 +08:00
if ( ! cookie & & ! uid . empty ( ) ) {
2024-09-19 14:53:50 +08:00
// 客户端请求中无cookie,再根据该用户的用户id获取cookie [AUTO-TRANSLATED:42cb8ade]
// There is no cookie in the client request, then get the cookie according to the user id of the user
2019-11-30 11:38:00 +08:00
cookie = HttpCookieManager : : Instance ( ) . getCookieByUid ( kCookieName , uid ) ;
2022-02-12 16:24:55 +08:00
update_cookie = true ;
2019-11-30 11:38:00 +08:00
}
if ( cookie ) {
2022-02-11 14:33:11 +08:00
auto & attach = cookie - > getAttach < HttpCookieAttachment > ( ) ;
if ( path . find ( attach . _path ) = = 0 ) {
2024-09-19 14:53:50 +08:00
// 上次cookie是限定本目录 [AUTO-TRANSLATED:a5c40abf]
// The last cookie is limited to this directory
2022-02-11 14:33:11 +08:00
if ( attach . _err_msg . empty ( ) ) {
2024-09-19 14:53:50 +08:00
// 上次鉴权成功 [AUTO-TRANSLATED:1a23f781]
// Last authentication succeeded
2022-02-12 16:24:55 +08:00
if ( attach . _hls_data ) {
2024-09-19 14:53:50 +08:00
// 如果播放的是hls, 那么刷新hls的cookie(获取ts文件也会刷新) [AUTO-TRANSLATED:02acac59]
// If the playback is hls, then refresh the hls cookie (getting the ts file will also refresh)
2019-11-30 14:29:44 +08:00
cookie - > updateTime ( ) ;
2022-02-12 16:24:55 +08:00
update_cookie = true ;
2019-11-30 14:29:44 +08:00
}
2022-02-12 16:24:55 +08:00
callback ( " " , update_cookie ? cookie : nullptr ) ;
2019-11-30 11:38:00 +08:00
return ;
}
2024-09-19 14:53:50 +08:00
// 上次鉴权失败, 但是如果url参数发生变更, 那么也重新鉴权下 [AUTO-TRANSLATED:df9bd345]
// Last authentication failed, but if the url parameter changes, then re-authenticate
2023-06-10 11:04:52 +08:00
if ( parser . params ( ) . empty ( ) | | parser . params ( ) = = cookie - > getUid ( ) ) {
2024-09-19 14:53:50 +08:00
// url参数未变, 或者本来就没有url参数, 那么判断本次请求为重复请求, 无访问权限 [AUTO-TRANSLATED:f46b4fca]
// The url parameter has not changed, or there is no url parameter at all, then determine that the current request is a duplicate request and has no access permission
2022-02-12 16:24:55 +08:00
callback ( attach . _err_msg , update_cookie ? cookie : nullptr ) ;
2019-11-30 11:38:00 +08:00
return ;
}
}
2024-09-19 14:53:50 +08:00
// 如果url参数变了或者不是限定本目录, 那么旧cookie失效, 重新鉴权 [AUTO-TRANSLATED:acf6d49e]
// If the url parameter changes or is not limited to this directory, then the old cookie expires and re-authentication is required
2019-11-30 11:38:00 +08:00
HttpCookieManager : : Instance ( ) . delCookie ( cookie ) ;
}
2023-07-02 12:02:33 +08:00
bool is_hls = media_info . schema = = HLS_SCHEMA | | media_info . schema = = HLS_FMP4_SCHEMA ;
2020-04-23 22:04:59 +08:00
SockInfoImp : : Ptr info = std : : make_shared < SockInfoImp > ( ) ;
info - > _identifier = sender . getIdentifier ( ) ;
info - > _peer_ip = sender . get_peer_ip ( ) ;
info - > _peer_port = sender . get_peer_port ( ) ;
info - > _local_ip = sender . get_local_ip ( ) ;
info - > _local_port = sender . get_local_port ( ) ;
2020-02-13 11:33:59 +08:00
2024-09-19 14:53:50 +08:00
// 该用户从来未获取过cookie, 这个时候我们广播是否允许该用户访问该http目录 [AUTO-TRANSLATED:8f4b3dd2]
// This user has never obtained a cookie, at this time we broadcast whether to allow this user to access this http directory
2022-02-11 16:36:40 +08:00
HttpSession : : HttpAccessPathInvoker accessPathInvoker = [ callback , uid , path , is_dir , is_hls , media_info , info ]
2022-02-11 14:33:11 +08:00
( const string & err_msg , const string & cookie_path_in , int life_second ) {
2019-11-30 11:38:00 +08:00
HttpServerCookie : : Ptr cookie ;
2022-02-11 14:33:11 +08:00
if ( life_second ) {
2024-09-19 14:53:50 +08:00
// 本次鉴权设置了有效期, 我们把鉴权结果缓存在cookie中 [AUTO-TRANSLATED:5a12f48e]
// This authentication has an expiration date, we cache the authentication result in the cookie
2019-11-30 11:38:00 +08:00
string cookie_path = cookie_path_in ;
if ( cookie_path . empty ( ) ) {
2024-09-19 14:53:50 +08:00
// 如果未设置鉴权目录,那么我们采用当前目录 [AUTO-TRANSLATED:701ada2d]
// If no authentication directory is set, we use the current directory
2019-11-30 11:38:00 +08:00
cookie_path = is_dir ? path : path . substr ( 0 , path . rfind ( " / " ) + 1 ) ;
}
2022-02-11 14:33:11 +08:00
auto attach = std : : make_shared < HttpCookieAttachment > ( ) ;
2024-09-19 14:53:50 +08:00
// 记录用户能访问的路径 [AUTO-TRANSLATED:80a2ba33]
// Record the paths that the user can access
2022-02-11 14:33:11 +08:00
attach - > _path = cookie_path ;
2024-09-19 14:53:50 +08:00
// 记录能否访问 [AUTO-TRANSLATED:972f6fc5]
// Record whether access is allowed
2022-02-11 14:33:11 +08:00
attach - > _err_msg = err_msg ;
2020-09-20 10:13:15 +08:00
if ( is_hls ) {
2024-09-19 14:53:50 +08:00
// hls相关信息 [AUTO-TRANSLATED:37893a71]
// hls related information
2022-02-11 16:36:40 +08:00
attach - > _hls_data = std : : make_shared < HlsCookieData > ( media_info , info ) ;
2019-12-28 16:48:11 +08:00
}
2023-10-12 11:05:41 +08:00
toolkit : : Any any ;
any . set ( std : : move ( attach ) ) ;
callback ( err_msg , HttpCookieManager : : Instance ( ) . addCookie ( kCookieName , uid , life_second , std : : move ( any ) ) ) ;
2020-09-20 10:13:15 +08:00
} else {
2022-02-11 14:33:11 +08:00
callback ( err_msg , nullptr ) ;
2019-11-30 11:38:00 +08:00
}
} ;
2020-09-12 19:14:35 +08:00
if ( is_hls ) {
2024-09-19 14:53:50 +08:00
// 是hls的播放鉴权,拦截之 [AUTO-TRANSLATED:c5ba86bb]
// This is hls playback authentication, intercept it
2022-02-11 16:36:40 +08:00
emitHlsPlayed ( parser , media_info , accessPathInvoker , sender ) ;
2019-11-30 11:38:00 +08:00
return ;
}
2024-09-19 14:53:50 +08:00
// 事件未被拦截, 则认为是http下载请求 [AUTO-TRANSLATED:7d449ccc]
// The event was not intercepted, it is considered an http download request
2023-09-02 10:52:07 +08:00
bool flag = NOTICE_EMIT ( BroadcastHttpAccessArgs , Broadcast : : kBroadcastHttpAccess , parser , path , is_dir , accessPathInvoker , sender ) ;
2019-11-30 11:38:00 +08:00
if ( ! flag ) {
2024-09-19 14:53:50 +08:00
// 此事件无人监听,我们默认都有权限访问 [AUTO-TRANSLATED:e1524c0f]
// No one is listening to this event, we assume that everyone has permission to access it by default
2019-11-30 11:38:00 +08:00
callback ( " " , nullptr ) ;
}
}
/**
* 发 送 404 Not Found
2024-09-19 14:53:50 +08:00
* Send 404 Not Found
* [ AUTO - TRANSLATED : 1297f 2e7 ]
2019-11-30 11:38:00 +08:00
*/
static void sendNotFound ( const HttpFileManager : : invoker & cb ) {
2020-09-20 10:13:15 +08:00
GET_CONFIG ( string , notFound , Http : : kNotFound ) ;
2021-01-02 21:24:06 +08:00
cb ( 404 , " text/html " , StrCaseMap ( ) , std : : make_shared < HttpStringBody > ( notFound ) ) ;
2019-11-30 11:38:00 +08:00
}
/**
* 拼 接 文 件 路 径
2024-09-19 14:53:50 +08:00
* Concatenate the file path
* [ AUTO - TRANSLATED : cf6f5c53 ]
2019-11-30 11:38:00 +08:00
*/
static string pathCat ( const string & a , const string & b ) {
2020-09-20 10:13:15 +08:00
if ( a . back ( ) = = ' / ' ) {
2019-11-30 11:38:00 +08:00
return a + b ;
}
return a + ' / ' + b ;
}
/**
* 访 问 文 件
* @ param sender 事 件 触 发 者
* @ param parser http请求
2022-02-11 16:36:40 +08:00
* @ param media_info http url信息
* @ param file_path 文 件 绝 对 路 径
2019-11-30 11:38:00 +08:00
* @ param cb 回 调 对 象
2024-09-19 14:53:50 +08:00
* Access the file
* @ param sender Event trigger
* @ param parser http request
* @ param media_info http url information
* @ param file_path Absolute file path
* @ param cb Callback object
* [ AUTO - TRANSLATED : 2 d840fe6 ]
2019-11-30 11:38:00 +08:00
*/
2022-11-19 09:33:10 +08:00
static void accessFile ( Session & sender , const Parser & parser , const MediaInfo & media_info , const string & file_path , const HttpFileManager : : invoker & cb ) {
2023-07-02 12:02:33 +08:00
bool is_hls = end_with ( file_path , kHlsSuffix ) | | end_with ( file_path , kHlsFMP4Suffix ) ;
2023-12-02 19:49:28 +08:00
if ( ! is_hls & & ! File : : fileExist ( file_path ) ) {
2024-09-19 14:53:50 +08:00
// 文件不存在且不是hls,那么直接返回404 [AUTO-TRANSLATED:7aae578b]
// The file does not exist and is not hls, so directly return 404
2019-11-30 11:38:00 +08:00
sendNotFound ( cb ) ;
return ;
}
2020-09-20 10:13:15 +08:00
if ( is_hls ) {
2024-09-19 14:53:50 +08:00
// hls, 那么移除掉后缀获取真实的stream_id并且修改协议为HLS [AUTO-TRANSLATED:94b5818a]
// hls, then remove the suffix to get the real stream_id and change the protocol to HLS
2023-07-02 12:02:33 +08:00
if ( end_with ( file_path , kHlsSuffix ) ) {
const_cast < string & > ( media_info . schema ) = HLS_SCHEMA ;
replace ( const_cast < string & > ( media_info . stream ) , kHlsSuffix , " " ) ;
} else {
const_cast < string & > ( media_info . schema ) = HLS_FMP4_SCHEMA ;
replace ( const_cast < string & > ( media_info . stream ) , kHlsFMP4Suffix , " " ) ;
}
2019-12-29 11:52:02 +08:00
}
2023-04-28 22:03:16 +08:00
weak_ptr < Session > weakSession = static_pointer_cast < Session > ( sender . shared_from_this ( ) ) ;
2024-09-19 14:53:50 +08:00
// 判断是否有权限访问该文件 [AUTO-TRANSLATED:b7f595f5]
// Determine whether you have permission to access this file
2022-03-12 10:57:39 +08:00
canAccessPath ( sender , parser , media_info , false , [ cb , file_path , parser , is_hls , media_info , weakSession ] ( const string & err_msg , const HttpServerCookie : : Ptr & cookie ) {
2019-12-29 15:38:29 +08:00
auto strongSession = weakSession . lock ( ) ;
2020-09-20 10:13:15 +08:00
if ( ! strongSession ) {
2024-09-19 14:53:50 +08:00
// http客户端已经断开, 不需要回复 [AUTO-TRANSLATED:9a252e21]
// The http client has disconnected and does not need to reply
2019-12-29 15:38:29 +08:00
return ;
}
2022-02-12 16:24:55 +08:00
if ( ! err_msg . empty ( ) ) {
2024-09-19 14:53:50 +08:00
// 文件鉴权失败 [AUTO-TRANSLATED:0feb8885]
// File authentication failed
2019-12-29 15:38:29 +08:00
StrCaseMap headerOut ;
if ( cookie ) {
2022-02-11 15:14:34 +08:00
headerOut [ " Set-Cookie " ] = cookie - > getCookie ( cookie - > getAttach < HttpCookieAttachment > ( ) . _path ) ;
2019-11-30 11:38:00 +08:00
}
2022-02-12 16:24:55 +08:00
cb ( 401 , " text/html " , headerOut , std : : make_shared < HttpStringBody > ( err_msg ) ) ;
2019-12-29 15:38:29 +08:00
return ;
}
2019-11-30 11:38:00 +08:00
2022-03-12 10:57:39 +08:00
auto response_file = [ is_hls ] ( const HttpServerCookie : : Ptr & cookie , const HttpFileManager : : invoker & cb , const string & file_path , const Parser & parser , const string & file_content = " " ) {
2019-12-29 15:38:29 +08:00
StrCaseMap httpHeader ;
if ( cookie ) {
2022-02-11 15:14:34 +08:00
httpHeader [ " Set-Cookie " ] = cookie - > getCookie ( cookie - > getAttach < HttpCookieAttachment > ( ) . _path ) ;
2019-12-29 15:38:29 +08:00
}
2021-01-02 21:24:06 +08:00
HttpSession : : HttpResponseInvoker invoker = [ & ] ( int code , const StrCaseMap & headerOut , const HttpBody : : Ptr & body ) {
2022-03-12 10:57:39 +08:00
if ( cookie & & body ) {
2022-02-11 14:33:11 +08:00
auto & attach = cookie - > getAttach < HttpCookieAttachment > ( ) ;
2022-02-12 16:24:55 +08:00
if ( attach . _hls_data ) {
2022-02-11 14:33:11 +08:00
attach . _hls_data - > addByteUsage ( body - > remainSize ( ) ) ;
2019-12-29 11:52:02 +08:00
}
2019-12-29 15:38:29 +08:00
}
2022-02-11 16:36:40 +08:00
cb ( code , HttpFileManager : : getContentType ( file_path . data ( ) ) , headerOut , body ) ;
2019-12-29 11:52:02 +08:00
} ;
2022-03-12 10:57:39 +08:00
GET_CONFIG_FUNC ( vector < string > , forbidCacheSuffix , Http : : kForbidCacheSuffix , [ ] ( const string & str ) {
return split ( str , " , " ) ;
} ) ;
2022-03-29 13:43:17 +08:00
bool is_forbid_cache = false ;
2022-03-12 10:57:39 +08:00
for ( auto & suffix : forbidCacheSuffix ) {
if ( suffix ! = " " & & end_with ( file_path , suffix ) ) {
is_forbid_cache = true ;
break ;
}
}
2022-02-17 10:35:10 +08:00
invoker . responseFile ( parser . getHeader ( ) , httpHeader , file_content . empty ( ) ? file_path : file_content , ! is_hls & & ! is_forbid_cache , file_content . empty ( ) ) ;
2019-12-29 15:38:29 +08:00
} ;
2019-12-29 11:52:02 +08:00
2022-02-12 16:24:55 +08:00
if ( ! is_hls | | ! cookie ) {
2024-09-19 14:53:50 +08:00
// 不是hls或访问m3u8文件不带cookie, 直接回复文件或404 [AUTO-TRANSLATED:64e5d19b]
// Not hls or accessing m3u8 files without cookies, directly reply to the file or 404
2022-02-11 16:36:40 +08:00
response_file ( cookie , cb , file_path , parser ) ;
2022-02-12 16:24:55 +08:00
if ( is_hls ) {
WarnL < < " access m3u8 file without cookie: " < < file_path ;
}
2020-09-20 10:13:15 +08:00
return ;
}
2023-10-12 11:05:41 +08:00
auto & attach = cookie - > getAttach < HttpCookieAttachment > ( ) ;
auto src = attach . _hls_data - > getMediaSource ( ) ;
2022-02-12 16:24:55 +08:00
if ( src ) {
2024-09-19 14:53:50 +08:00
// 直接从内存获取m3u8索引文件(而不是从文件系统) [AUTO-TRANSLATED:c772e342]
// Get the m3u8 index file directly from memory (instead of from the file system)
2022-02-12 16:24:55 +08:00
response_file ( cookie , cb , file_path , parser , src - > getIndexFile ( ) ) ;
2020-09-20 10:13:15 +08:00
return ;
}
2023-10-12 11:05:41 +08:00
if ( attach . _find_src & & attach . _find_src_ticker . elapsedTime ( ) < kFindSrcIntervalSecond * 1000 ) {
2024-09-19 14:53:50 +08:00
// 最近已经查找过MediaSource了, 为了防止频繁查找导致占用全局互斥锁的问题, 我们尝试直接从磁盘返回hls索引文件 [AUTO-TRANSLATED:a33d5e4d]
// MediaSource has been searched recently, in order to prevent frequent searches from occupying the global mutex, we try to return the hls index file directly from the disk
2022-10-18 19:23:20 +08:00
response_file ( cookie , cb , file_path , parser ) ;
return ;
}
2022-02-11 16:21:19 +08:00
2024-09-19 14:53:50 +08:00
// hls流可能未注册, MediaSource::findAsync可以触发not_found事件, 然后再按需推拉流 [AUTO-TRANSLATED:f4acd717]
// The hls stream may not be registered, MediaSource::findAsync can trigger the not_found event, and then push and pull the stream on demand
2022-02-12 16:24:55 +08:00
MediaSource : : findAsync ( media_info , strongSession , [ response_file , cookie , cb , file_path , parser ] ( const MediaSource : : Ptr & src ) {
2020-09-20 10:13:15 +08:00
auto hls = dynamic_pointer_cast < HlsMediaSource > ( src ) ;
if ( ! hls ) {
2024-09-19 14:53:50 +08:00
// 流不在线 [AUTO-TRANSLATED:5a6a5695]
// The stream is not online
2022-02-11 16:36:40 +08:00
response_file ( cookie , cb , file_path , parser ) ;
2019-12-29 15:38:29 +08:00
return ;
}
2020-09-12 19:20:18 +08:00
2022-02-12 16:24:55 +08:00
auto & attach = cookie - > getAttach < HttpCookieAttachment > ( ) ;
attach . _hls_data - > setMediaSource ( hls ) ;
2024-09-19 14:53:50 +08:00
// 添加HlsMediaSource的观看人数(HLS是按需生成的, 这样可以触发HLS文件的生成) [AUTO-TRANSLATED:bd98e100]
// Add the number of viewers of HlsMediaSource (HLS is generated on demand, so this can trigger the generation of HLS files)
2022-02-12 16:24:55 +08:00
attach . _hls_data - > addByteUsage ( 0 ) ;
2024-09-19 14:53:50 +08:00
// 标记找到MediaSource [AUTO-TRANSLATED:1e298005]
// Mark that MediaSource has been found
2022-10-18 19:23:20 +08:00
attach . _find_src = true ;
2022-02-12 16:24:55 +08:00
2024-09-19 14:53:50 +08:00
// 重置查找MediaSource计时 [AUTO-TRANSLATED:d1e47e07]
// Reset the MediaSource search timer
2023-10-12 11:05:41 +08:00
attach . _find_src_ticker . resetTime ( ) ;
2024-09-19 14:53:50 +08:00
// m3u8文件可能不存在, 等待m3u8索引文件按需生成 [AUTO-TRANSLATED:0dbd4df2]
// The m3u8 file may not exist, wait for the m3u8 index file to be generated on demand
2022-02-11 16:36:40 +08:00
hls - > getIndexFile ( [ response_file , file_path , cookie , cb , parser ] ( const string & file ) {
response_file ( cookie , cb , file_path , parser , file ) ;
2019-12-29 15:38:29 +08:00
} ) ;
2020-09-20 10:13:15 +08:00
} ) ;
2019-11-30 11:38:00 +08:00
} ) ;
}
2023-08-26 19:43:11 +08:00
static string getFilePath ( const Parser & parser , const MediaInfo & media_info , Session & sender ) {
2019-12-23 12:47:04 +08:00
GET_CONFIG ( bool , enableVhost , General : : kEnableVhost ) ;
2021-08-27 11:05:26 +08:00
GET_CONFIG ( string , rootPath , Http : : kRootPath ) ;
GET_CONFIG_FUNC ( StrCaseMap , virtualPathMap , Http : : kVirtualPath , [ ] ( const string & str ) {
return Parser : : parseArgs ( str , " ; " , " , " ) ;
} ) ;
2023-12-02 20:54:34 +08:00
string url , path , virtual_app ;
2023-05-25 16:23:24 +08:00
auto it = virtualPathMap . find ( media_info . app ) ;
2021-08-27 11:05:26 +08:00
if ( it ! = virtualPathMap . end ( ) ) {
2024-09-19 14:53:50 +08:00
// 访问的是virtualPath [AUTO-TRANSLATED:a36c7b20]
// Accessing virtualPath
2021-08-27 11:05:26 +08:00
path = it - > second ;
2023-06-10 11:04:52 +08:00
url = parser . url ( ) . substr ( 1 + media_info . app . size ( ) ) ;
2023-12-02 20:54:34 +08:00
virtual_app = media_info . app + " / " ;
2021-08-27 11:05:26 +08:00
} else {
2024-09-19 14:53:50 +08:00
// 访问的是rootPath [AUTO-TRANSLATED:600765f0]
// Accessing rootPath
2021-08-26 19:36:38 +08:00
path = rootPath ;
2023-06-10 11:04:52 +08:00
url = parser . url ( ) ;
2021-08-25 14:30:31 +08:00
}
2022-06-18 21:44:16 +08:00
for ( auto & ch : url ) {
if ( ch = = ' \\ ' ) {
2024-09-19 14:53:50 +08:00
// 如果url中存在"\", 这种目录是Windows样式的; 需要批量转换为标准的"/"; 防止访问目录权限外的文件 [AUTO-TRANSLATED:fd6b5900]
// If the url contains "\", this directory is in Windows style; it needs to be converted to standard "/" in batches; prevent access to files outside the directory permissions
2022-06-18 21:44:16 +08:00
ch = ' / ' ;
}
}
2023-05-25 16:23:24 +08:00
auto ret = File : : absolutePath ( enableVhost ? media_info . vhost + url : url , path ) ;
2023-08-26 19:43:11 +08:00
auto http_root = File : : absolutePath ( enableVhost ? media_info . vhost + " / " : " / " , path ) ;
if ( ! start_with ( ret , http_root ) ) {
2024-09-19 14:53:50 +08:00
// 访问的http文件不得在http根目录之外 [AUTO-TRANSLATED:7d85a8f9]
// The accessed http file must not be outside the http root directory
2023-08-26 19:43:11 +08:00
throw std : : runtime_error ( " Attempting to access files outside of the http root directory " ) ;
}
2024-09-19 14:53:50 +08:00
// 替换url, 防止返回的目录索引网页被注入非法内容 [AUTO-TRANSLATED:463ad1b1]
// Replace the url to prevent the returned directory index page from being injected with illegal content
2023-12-02 20:54:34 +08:00
const_cast < Parser & > ( parser ) . setUrl ( " / " + virtual_app + ret . substr ( http_root . size ( ) ) ) ;
2023-09-02 10:52:07 +08:00
NOTICE_EMIT ( BroadcastHttpBeforeAccessArgs , Broadcast : : kBroadcastHttpBeforeAccess , parser , ret , sender ) ;
2020-09-21 14:32:56 +08:00
return ret ;
2019-12-23 12:47:04 +08:00
}
2019-11-30 11:38:00 +08:00
/**
* 访 问 文 件 或 文 件 夹
* @ param sender 事 件 触 发 者
* @ param parser http请求
* @ param cb 回 调 对 象
2024-09-19 14:53:50 +08:00
* Access file or folder
* @ param sender Event trigger
* @ param parser http request
* @ param cb Callback object
* [ AUTO - TRANSLATED : a79c824d ]
2019-11-30 11:38:00 +08:00
*/
2022-11-19 09:33:10 +08:00
void HttpFileManager : : onAccessPath ( Session & sender , Parser & parser , const HttpFileManager : : invoker & cb ) {
2023-12-02 16:31:40 +08:00
auto fullUrl = " http:// " + parser [ " Host " ] + parser . fullUrl ( ) ;
2022-02-11 16:36:40 +08:00
MediaInfo media_info ( fullUrl ) ;
auto file_path = getFilePath ( parser , media_info , sender ) ;
2023-03-03 14:44:59 +08:00
if ( file_path . size ( ) = = 0 ) {
sendNotFound ( cb ) ;
return ;
}
2024-09-19 14:53:50 +08:00
// 访问的是文件夹 [AUTO-TRANSLATED:279974bb]
// Accessing a folder
2023-12-02 19:49:28 +08:00
if ( File : : is_dir ( file_path ) ) {
2022-02-11 16:36:40 +08:00
auto indexFile = searchIndexFile ( file_path ) ;
2019-11-30 11:38:00 +08:00
if ( ! indexFile . empty ( ) ) {
2024-09-19 14:53:50 +08:00
// 发现该文件夹下有index文件 [AUTO-TRANSLATED:4a697758]
// Found index file in this folder
2022-02-11 16:36:40 +08:00
file_path = pathCat ( file_path , indexFile ) ;
2023-12-02 19:49:28 +08:00
if ( ! File : : is_dir ( file_path ) ) {
2024-09-19 14:53:50 +08:00
// 不是文件夹 [AUTO-TRANSLATED:af893469]
// Not a folder
2023-08-08 14:24:01 +08:00
parser . setUrl ( pathCat ( parser . url ( ) , indexFile ) ) ;
accessFile ( sender , parser , media_info , file_path , cb ) ;
return ;
}
2019-11-30 11:38:00 +08:00
}
string strMenu ;
2024-09-19 14:53:50 +08:00
// 生成文件夹菜单索引 [AUTO-TRANSLATED:04150cc8]
// Generate folder menu index
2023-06-10 11:04:52 +08:00
if ( ! makeFolderMenu ( parser . url ( ) , file_path , strMenu ) ) {
2024-09-19 14:53:50 +08:00
// 文件夹不存在 [AUTO-TRANSLATED:a2dc6c89]
// Folder does not exist
2019-11-30 11:38:00 +08:00
sendNotFound ( cb ) ;
return ;
}
2024-09-19 14:53:50 +08:00
// 判断是否有权限访问该目录 [AUTO-TRANSLATED:963d02a6]
// Determine if there is permission to access this directory
2022-02-12 16:24:55 +08:00
canAccessPath ( sender , parser , media_info , true , [ strMenu , cb ] ( const string & err_msg , const HttpServerCookie : : Ptr & cookie ) mutable {
if ( ! err_msg . empty ( ) ) {
strMenu = err_msg ;
2019-11-30 11:38:00 +08:00
}
StrCaseMap headerOut ;
if ( cookie ) {
2022-02-11 15:14:34 +08:00
headerOut [ " Set-Cookie " ] = cookie - > getCookie ( cookie - > getAttach < HttpCookieAttachment > ( ) . _path ) ;
2019-11-30 11:38:00 +08:00
}
2022-02-12 16:24:55 +08:00
cb ( err_msg . empty ( ) ? 200 : 401 , " text/html " , headerOut , std : : make_shared < HttpStringBody > ( strMenu ) ) ;
2019-11-30 11:38:00 +08:00
} ) ;
return ;
}
2024-09-19 14:53:50 +08:00
// 访问的是文件 [AUTO-TRANSLATED:7a400b3c]
// Accessing a file
2022-02-11 16:36:40 +08:00
accessFile ( sender , parser , media_info , file_path , cb ) ;
2019-11-30 11:38:00 +08:00
} ;
////////////////////////////////////HttpResponseInvokerImp//////////////////////////////////////
2021-08-12 21:02:07 +08:00
void HttpResponseInvokerImp : : operator ( ) ( int code , const StrCaseMap & headerOut , const Buffer : : Ptr & body ) const {
return operator ( ) ( code , headerOut , std : : make_shared < HttpBufferBody > ( body ) ) ;
}
2021-01-02 21:24:06 +08:00
void HttpResponseInvokerImp : : operator ( ) ( int code , const StrCaseMap & headerOut , const HttpBody : : Ptr & body ) const {
2020-09-20 10:13:15 +08:00
if ( _lambad ) {
2021-01-02 21:24:06 +08:00
_lambad ( code , headerOut , body ) ;
2019-11-30 11:38:00 +08:00
}
}
2021-01-02 21:24:06 +08:00
void HttpResponseInvokerImp : : operator ( ) ( int code , const StrCaseMap & headerOut , const string & body ) const {
this - > operator ( ) ( code , headerOut , std : : make_shared < HttpStringBody > ( body ) ) ;
2019-11-30 11:38:00 +08:00
}
HttpResponseInvokerImp : : HttpResponseInvokerImp ( const HttpResponseInvokerImp : : HttpResponseInvokerLambda0 & lambda ) {
_lambad = lambda ;
}
HttpResponseInvokerImp : : HttpResponseInvokerImp ( const HttpResponseInvokerImp : : HttpResponseInvokerLambda1 & lambda ) {
2020-09-20 10:13:15 +08:00
if ( ! lambda ) {
2019-11-30 11:38:00 +08:00
_lambad = nullptr ;
return ;
}
2021-01-02 21:24:06 +08:00
_lambad = [ lambda ] ( int code , const StrCaseMap & headerOut , const HttpBody : : Ptr & body ) {
2019-11-30 11:38:00 +08:00
string str ;
2020-09-20 10:13:15 +08:00
if ( body & & body - > remainSize ( ) ) {
2019-11-30 11:38:00 +08:00
str = body - > readData ( body - > remainSize ( ) ) - > toString ( ) ;
}
2021-01-02 21:24:06 +08:00
lambda ( code , headerOut , str ) ;
2019-11-30 11:38:00 +08:00
} ;
}
void HttpResponseInvokerImp : : responseFile ( const StrCaseMap & requestHeader ,
const StrCaseMap & responseHeader ,
2022-02-11 16:21:19 +08:00
const string & file ,
bool use_mmap ,
bool is_path ) const {
if ( ! is_path ) {
2024-09-19 14:53:50 +08:00
// file是文件内容 [AUTO-TRANSLATED:61d0be82]
// file is the file content
2022-02-11 16:21:19 +08:00
( * this ) ( 200 , responseHeader , std : : make_shared < HttpStringBody > ( file ) ) ;
return ;
}
2024-09-19 14:53:50 +08:00
// file是文件路径 [AUTO-TRANSLATED:28dcac38]
// file is the file path
2023-12-02 20:55:53 +08:00
GET_CONFIG ( string , charSet , Http : : kCharSet ) ;
2020-09-20 10:13:15 +08:00
StrCaseMap & httpHeader = const_cast < StrCaseMap & > ( responseHeader ) ;
2022-02-11 16:21:19 +08:00
auto fileBody = std : : make_shared < HttpFileBody > ( file , use_mmap ) ;
2022-02-10 20:23:37 +08:00
if ( fileBody - > remainSize ( ) < 0 ) {
2024-09-19 14:53:50 +08:00
// 打开文件失败 [AUTO-TRANSLATED:1f0405cb]
// Failed to open file
2020-09-20 10:13:15 +08:00
GET_CONFIG ( string , notFound , Http : : kNotFound ) ;
2019-11-30 11:38:00 +08:00
auto strContentType = StrPrinter < < " text/html; charset= " < < charSet < < endl ;
httpHeader [ " Content-Type " ] = strContentType ;
2021-01-02 21:24:06 +08:00
( * this ) ( 404 , httpHeader , notFound ) ;
2019-11-30 11:38:00 +08:00
return ;
}
2024-09-19 14:53:50 +08:00
// 尝试添加Content-Type [AUTO-TRANSLATED:2c08b371]
// Try to add Content-Type
2023-12-02 20:55:53 +08:00
httpHeader . emplace ( " Content-Type " , HttpConst : : getHttpContentType ( file . data ( ) ) + " ; charset= " + charSet ) ;
2023-12-02 15:58:40 +08:00
2019-11-30 11:38:00 +08:00
auto & strRange = const_cast < StrCaseMap & > ( requestHeader ) [ " Range " ] ;
2022-02-10 20:23:37 +08:00
int code = 200 ;
if ( ! strRange . empty ( ) ) {
2024-09-19 14:53:50 +08:00
// 分节下载 [AUTO-TRANSLATED:01920230]
// Segmented download
2021-01-02 21:24:06 +08:00
code = 206 ;
2023-06-10 12:28:49 +08:00
auto iRangeStart = atoll ( findSubString ( strRange . data ( ) , " bytes= " , " - " ) . data ( ) ) ;
auto iRangeEnd = atoll ( findSubString ( strRange . data ( ) , " - " , nullptr ) . data ( ) ) ;
2022-02-10 20:23:37 +08:00
auto fileSize = fileBody - > remainSize ( ) ;
2019-11-30 11:38:00 +08:00
if ( iRangeEnd = = 0 ) {
iRangeEnd = fileSize - 1 ;
}
2024-09-19 14:53:50 +08:00
// 设置文件范围 [AUTO-TRANSLATED:aa51fd28]
// Set file range
2022-02-10 20:23:37 +08:00
fileBody - > setRange ( iRangeStart , iRangeEnd - iRangeStart + 1 ) ;
2024-09-19 14:53:50 +08:00
// 分节下载返回Content-Range头 [AUTO-TRANSLATED:4b78e7b6]
// Segmented download returns Content-Range header
2019-11-30 11:38:00 +08:00
httpHeader . emplace ( " Content-Range " , StrPrinter < < " bytes " < < iRangeStart < < " - " < < iRangeEnd < < " / " < < fileSize < < endl ) ;
}
2024-09-19 14:53:50 +08:00
// 回复文件 [AUTO-TRANSLATED:5d91a916]
// Reply file
2021-01-02 21:24:06 +08:00
( * this ) ( code , httpHeader , fileBody ) ;
2019-11-30 11:38:00 +08:00
}
HttpResponseInvokerImp : : operator bool ( ) {
return _lambad . operator bool ( ) ;
}
2023-07-02 12:02:33 +08:00
} //namespace mediakit