2025-11-12 14:18:56 +08:00
//
// NetworkStreamHandler . m
// CustomKeyboard
//
// Created by Mac on 2025 / 11 / 12.
//
# import "NetworkStreamHandler.h"
2026-03-05 14:30:07 +08:00
# import < Security / Security . h >
2026-03-07 13:29:29 +08:00
# import "KBLocalizationManager.h"
2025-11-12 14:18:56 +08:00
@ interface NetworkStreamHandler ( )
@ property ( nonatomic , strong ) NSURLSession * session ;
@ property ( nonatomic , strong ) NSURLSessionDataTask * dataTask ;
@ property ( nonatomic , strong ) NSURLRequest * request ;
@ property ( nonatomic , strong ) NSMutableData * receivedData ;
@ property ( nonatomic , assign ) long long expectedContentLength ;
@ property ( nonatomic , assign ) NetworkStreamState state ;
@ property ( nonatomic , strong ) NSURLResponse * response ;
// Block 回 调
@ property ( nonatomic , copy ) NetworkStreamProgressBlock progressBlock ;
@ property ( nonatomic , copy ) NetworkStreamDataBlock dataBlock ;
@ property ( nonatomic , copy ) NetworkStreamTextBlock textBlock ;
@ property ( nonatomic , copy ) NetworkStreamCompletionBlock completionBlock ;
@ end
@ implementation NetworkStreamHandler
- ( instancetype ) initWithURL : ( NSURL * ) url {
NSURLRequest * request = [ NetworkStreamHandler createDefaultRequestWithURL : url method : @ "GET" ] ;
return [ self initWithRequest : request ] ;
}
- ( instancetype ) initWithRequest : ( NSURLRequest * ) request {
self = [ super init ] ;
if ( self ) {
_request = request ;
_receivedData = [ NSMutableData data ] ;
_state = NetworkStreamStateIdle ;
_totalBytesReceived = 0 ;
// 创 建 URLSession 配 置
NSURLSessionConfiguration * config = [ NSURLSessionConfiguration defaultSessionConfiguration ] ;
config . timeoutIntervalForRequest = 30.0 ;
config . timeoutIntervalForResource = 300.0 ;
config . requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData ;
// 创 建 URLSession
_session = [ NSURLSession sessionWithConfiguration : config
delegate : self
delegateQueue : [ NSOperationQueue mainQueue ] ] ;
}
return self ;
}
- ( void ) dealloc {
[ self cancelRequest ] ;
}
# pragma mark - Public Methods
- ( void ) startRequest {
if ( self . state ! = NetworkStreamStateIdle ) {
NSLog ( @ "Request already in progress" ) ;
return ;
}
[ self updateState : NetworkStreamStateConnecting ] ;
self . dataTask = [ self . session dataTaskWithRequest : self . request ] ;
[ self . dataTask resume ] ;
}
- ( void ) startRequestWithProgress : ( NetworkStreamProgressBlock ) progress
onData : ( NetworkStreamDataBlock ) dataBlock
onText : ( NetworkStreamTextBlock ) textBlock
completion : ( NetworkStreamCompletionBlock ) completion {
self . progressBlock = progress ;
self . dataBlock = dataBlock ;
self . textBlock = textBlock ;
self . completionBlock = completion ;
[ self startRequest ] ;
}
- ( void ) cancelRequest {
if ( self . dataTask ) {
[ self . dataTask cancel ] ;
self . dataTask = nil ;
}
[ self updateState : NetworkStreamStateIdle ] ;
}
+ ( NSURLRequest * ) createDefaultRequestWithURL : ( NSURL * ) url method : ( NSString * ) method {
NSMutableURLRequest * request = [ NSMutableURLRequest requestWithURL : url ] ;
request . HTTPMethod = method ;
request . timeoutInterval = 30.0 ;
// 设 置 常 见 的 请 求 头 ( 根 据 您 的 截 图 )
[ request setValue : @ "text/html, application/xhtml+xml, application/xml; q=0.9, image/avif, image/webp, image/apng, */*; q=0.8, application/signed-exchange; v=b3; q=0.7" forHTTPHeaderField : @ "Accept" ] ;
[ request setValue : @ "gzip, deflate" forHTTPHeaderField : @ "Accept-Encoding" ] ;
2026-03-07 13:29:29 +08:00
NSString * lang = [ [ KBLocalizationManager shared ] currentLanguageHeaderValue ] ;
if ( lang . length = = 0 ) {
lang = @ "en" ;
}
[ request setValue : lang forHTTPHeaderField : @ "Accept-Language" ] ;
2025-11-12 14:18:56 +08:00
[ request setValue : @ "keep-alive" forHTTPHeaderField : @ "Connection" ] ;
[ request setValue : @ "1" forHTTPHeaderField : @ "Upgrade-Insecure-Requests" ] ;
// 用 户 代 理 ( 可 选 )
NSString * userAgent = @ "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1" ;
[ request setValue : userAgent forHTTPHeaderField : @ "User-Agent" ] ;
return [ request copy ] ;
}
# pragma mark - Private Methods
- ( void ) updateState : ( NetworkStreamState ) newState {
if ( _state ! = newState ) {
_state = newState ;
// 通 知 代 理 状 态 改 变
if ( [ self . delegate respondsToSelector : @ selector ( networkStream : stateChanged : ) ] ) {
[ self . delegate networkStream : self stateChanged : newState ] ;
}
}
}
- ( void ) notifyProgress : ( float ) progress {
if ( self . progressBlock ) {
self . progressBlock ( progress ) ;
}
if ( [ self . delegate respondsToSelector : @ selector ( networkStream : downloadProgress : ) ] ) {
[ self . delegate networkStream : self downloadProgress : progress ] ;
}
}
- ( void ) notifyReceivedData : ( NSData * ) data {
if ( self . dataBlock ) {
self . dataBlock ( data ) ;
}
if ( [ self . delegate respondsToSelector : @ selector ( networkStream : didReceiveData : ) ] ) {
[ self . delegate networkStream : self didReceiveData : data ] ;
}
// 如 果 是 文 本 数 据 , 尝 试 转 换 为 字 符 串
if ( self . textBlock || [ self . delegate respondsToSelector : @ selector ( networkStream : didReceiveText : ) ] ) {
NSString * text = [ [ NSString alloc ] initWithData : data encoding : NSUTF8StringEncoding ] ;
if ( text ) {
if ( self . textBlock ) {
self . textBlock ( text ) ;
}
if ( [ self . delegate respondsToSelector : @ selector ( networkStream : didReceiveText : ) ] ) {
[ self . delegate networkStream : self didReceiveText : text ] ;
}
}
}
}
- ( void ) notifyCompletionWithError : ( NSError * _Nullable ) error {
if ( self . completionBlock ) {
self . completionBlock ( error ) ;
}
if ( [ self . delegate respondsToSelector : @ selector ( networkStream : didCompleteWithError : ) ] ) {
[ self . delegate networkStream : self didCompleteWithError : error ] ;
}
}
# pragma mark - NSURLSessionDataDelegate
- ( void ) URLSession : ( NSURLSession * ) session
dataTask : ( NSURLSessionDataTask * ) dataTask
didReceiveResponse : ( NSURLResponse * ) response
completionHandler : ( void ( ^ ) ( NSURLSessionResponseDisposition ) ) completionHandler {
self . response = response ;
self . expectedContentLength = response . expectedContentLength ;
_totalBytesReceived = 0 ;
[ self . receivedData setLength : 0 ] ;
[ self updateState : NetworkStreamStateReceiving ] ;
// 检 查 响 应 头 , 处 理 CORS 等
if ( [ response isKindOfClass : [ NSHTTPURLResponse class ] ] ) {
NSHTTPURLResponse * httpResponse = ( NSHTTPURLResponse * ) response ;
NSLog ( @ "Response headers: %@" , httpResponse . allHeaderFields ) ;
// 可 以 在 这 里 检 查 CORS 头 等 信 息
NSString * allowOrigin = httpResponse . allHeaderFields [ @ "Access-Control-Allow-Origin" ] ;
if ( allowOrigin ) {
NSLog ( @ "CORS Allow Origin: %@" , allowOrigin ) ;
}
}
completionHandler ( NSURLSessionResponseAllow ) ;
}
- ( void ) URLSession : ( NSURLSession * ) session
dataTask : ( NSURLSessionDataTask * ) dataTask
didReceiveData : ( NSData * ) data {
_totalBytesReceived + = data . length ;
[ self . receivedData appendData : data ] ;
// 通 知 接 收 到 数 据 块
[ self notifyReceivedData : data ] ;
// 计 算 并 通 知 进 度
if ( self . expectedContentLength ! = NSURLResponseUnknownLength ) {
float progress = ( float ) self . totalBytesReceived / ( float ) self . expectedContentLength ;
[ self notifyProgress : progress ] ;
} else {
// 对 于 chunked 传 输 , 可 能 没 有 确 切 的 内 容 长 度
[ self notifyProgress : -1 ] ; // 使 用 -1 表 示 未 知 进 度
}
}
- ( void ) URLSession : ( NSURLSession * ) session
task : ( NSURLSessionTask * ) task
didCompleteWithError : ( NSError * ) error {
if ( error ) {
[ self updateState : NetworkStreamStateError ] ;
NSLog ( @ "Request failed with error: %@" , error ) ;
} else {
[ self updateState : NetworkStreamStateCompleted ] ;
NSLog ( @ "Request completed successfully. Total bytes: %lld" , self . totalBytesReceived ) ;
}
[ self notifyCompletionWithError : error ] ;
// 清 理
[ self . session finishTasksAndInvalidate ] ;
self . dataTask = nil ;
}
# pragma mark - URL Session Delegate ( 处 理 SSL / 认 证 )
- ( void ) URLSession : ( NSURLSession * ) session
didReceiveChallenge : ( NSURLAuthenticationChallenge * ) challenge
completionHandler : ( void ( ^ ) ( NSURLSessionAuthChallengeDisposition , NSURLCredential * _Nullable ) ) completionHandler {
// 处 理 SSL 认 证 挑 战
if ( [ challenge . protectionSpace . authenticationMethod isEqualToString : NSURLAuthenticationMethodServerTrust ] ) {
2026-03-05 14:30:07 +08:00
SecTrustRef trust = challenge . protectionSpace . serverTrust ;
if ( ! trust ) {
completionHandler ( NSURLSessionAuthChallengeCancelAuthenticationChallenge , nil ) ;
return ;
}
BOOL ok = NO ;
if ( @ available ( iOS 13.0 , * ) ) {
ok = SecTrustEvaluateWithError ( trust , nil ) ;
} else {
SecTrustResultType result = kSecTrustResultInvalid ;
OSStatus status = SecTrustEvaluate ( trust , & result ) ;
ok = ( status = = errSecSuccess ) &&
( result = = kSecTrustResultUnspecified || result = = kSecTrustResultProceed ) ;
}
if ( ok ) {
NSURLCredential * credential = [ NSURLCredential credentialForTrust : trust ] ;
completionHandler ( NSURLSessionAuthChallengeUseCredential , credential ) ;
} else {
completionHandler ( NSURLSessionAuthChallengeCancelAuthenticationChallenge , nil ) ;
}
2025-11-12 14:18:56 +08:00
} else {
completionHandler ( NSURLSessionAuthChallengePerformDefaultHandling , nil ) ;
}
}
@ end