feat: sync httpdns sdk/platform updates without large binaries

This commit is contained in:
robin
2026-03-04 17:59:14 +08:00
parent 853897a6f8
commit 532891fad0
700 changed files with 6096 additions and 2712 deletions

View File

@@ -0,0 +1,291 @@
//
// HttpdnsIPQualityDetector.m
// NewHttpDNS
//
// Created by xuyecan on 2025/3/13.
// Copyright © 2025 new-inc.com. All rights reserved.
//
#import "HttpdnsIPQualityDetector.h"
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <netdb.h>
#import <unistd.h>
#import <sys/time.h>
#import <fcntl.h>
#import <errno.h>
#import "HttpdnsLog_Internal.h"
#import "HttpdnsUtil.h"
//
@interface HttpdnsDetectionTask : NSObject
@property (nonatomic, copy) NSString *cacheKey;
@property (nonatomic, copy) NSString *ip;
@property (nonatomic, strong) NSNumber *port;
@property (nonatomic, copy) HttpdnsIPQualityCallback callback;
@end
@implementation HttpdnsDetectionTask
@end
@interface HttpdnsIPQualityDetector ()
@property (nonatomic, strong) dispatch_queue_t detectQueue;
@property (nonatomic, strong) dispatch_semaphore_t concurrencySemaphore;
@property (nonatomic, strong) NSMutableArray<HttpdnsDetectionTask *> *pendingTasks;
@property (nonatomic, strong) NSLock *pendingTasksLock;
@property (nonatomic, assign) BOOL isProcessingPendingTasks;
/**
* 10
*/
@property (nonatomic, assign) NSUInteger maxConcurrentDetections;
@end
@implementation HttpdnsIPQualityDetector
+ (instancetype)sharedInstance {
static HttpdnsIPQualityDetector *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[HttpdnsIPQualityDetector alloc] init];
});
return instance;
}
- (instancetype)init {
self = [super init];
if (self) {
_detectQueue = dispatch_queue_create("com.new.httpdns.ipqualitydetector", DISPATCH_QUEUE_CONCURRENT);
_maxConcurrentDetections = 10;
_concurrencySemaphore = dispatch_semaphore_create(_maxConcurrentDetections);
_pendingTasks = [NSMutableArray array];
_pendingTasksLock = [[NSLock alloc] init];
_isProcessingPendingTasks = NO;
}
return self;
}
- (void)scheduleIPQualityDetection:(NSString *)cacheKey
ip:(NSString *)ip
port:(NSNumber *)port
callback:(HttpdnsIPQualityCallback)callback {
if (!cacheKey || !ip || !callback) {
HttpdnsLogDebug("IPQualityDetector invalid parameters for detection: cacheKey=%@, ip=%@", cacheKey, ip);
return;
}
//
if (dispatch_semaphore_wait(_concurrencySemaphore, DISPATCH_TIME_NOW) != 0) {
//
[self addPendingTask:cacheKey ip:ip port:port callback:callback];
return;
}
//
[self executeDetection:cacheKey ip:ip port:port callback:callback];
}
- (void)addPendingTask:(NSString *)cacheKey ip:(NSString *)ip port:(NSNumber *)port callback:(HttpdnsIPQualityCallback)callback {
// ARC
HttpdnsDetectionTask *task = [[HttpdnsDetectionTask alloc] init];
task.cacheKey = cacheKey;
task.ip = ip;
task.port = port;
task.callback = callback;
//
[_pendingTasksLock lock];
[_pendingTasks addObject:task];
[_pendingTasksLock unlock];
//
[self processPendingTasksIfNeeded];
}
- (NSUInteger)pendingTasksCount {
[_pendingTasksLock lock];
NSUInteger count = _pendingTasks.count;
[_pendingTasksLock unlock];
return count;
}
- (void)processPendingTasksIfNeeded {
[_pendingTasksLock lock];
BOOL shouldProcess = !_isProcessingPendingTasks && _pendingTasks.count > 0;
if (shouldProcess) {
_isProcessingPendingTasks = YES;
}
[_pendingTasksLock unlock];
if (shouldProcess) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self processPendingTasks];
});
}
}
- (void)processPendingTasks {
while (1) {
//
if (dispatch_semaphore_wait(_concurrencySemaphore, DISPATCH_TIME_NOW) != 0) {
//
[NSThread sleepForTimeInterval:0.1];
continue;
}
//
HttpdnsDetectionTask *task = nil;
[_pendingTasksLock lock];
if (_pendingTasks.count > 0) {
task = _pendingTasks.firstObject;
[_pendingTasks removeObjectAtIndex:0];
} else {
//
_isProcessingPendingTasks = NO;
[_pendingTasksLock unlock];
//
dispatch_semaphore_signal(_concurrencySemaphore);
break;
}
[_pendingTasksLock unlock];
//
[self executeDetection:task.cacheKey ip:task.ip port:task.port callback:task.callback];
}
}
- (void)executeDetection:(NSString *)cacheKey ip:(NSString *)ip port:(NSNumber *)port callback:(HttpdnsIPQualityCallback)callback {
//
HttpdnsIPQualityCallback strongCallback = [callback copy];
// 使线
dispatch_async(self.detectQueue, ^{
NSInteger costTime = [self tcpConnectToIP:ip port:port ? [port intValue] : 80];
// 线
dispatch_async(dispatch_get_global_queue(0, 0), ^{
strongCallback(cacheKey, ip, costTime);
//
dispatch_semaphore_signal(self->_concurrencySemaphore);
//
[self processPendingTasksIfNeeded];
});
});
}
- (NSInteger)tcpConnectToIP:(NSString *)ip port:(int)port {
if (!ip || port <= 0) {
return -1;
}
int socketFd;
struct sockaddr_in serverAddr;
struct sockaddr_in6 serverAddr6;
void *serverAddrPtr;
socklen_t serverAddrLen;
BOOL isIPv6 = [HttpdnsUtil isIPv6Address:ip];
BOOL isIpv4 = [HttpdnsUtil isIPv4Address:ip];
// socket
if (isIPv6) {
socketFd = socket(AF_INET6, SOCK_STREAM, 0);
if (socketFd < 0) {
HttpdnsLogDebug("IPQualityDetector failed to create IPv6 socket: %s", strerror(errno));
return -1;
}
memset(&serverAddr6, 0, sizeof(serverAddr6));
serverAddr6.sin6_family = AF_INET6;
serverAddr6.sin6_port = htons(port);
inet_pton(AF_INET6, [ip UTF8String], &serverAddr6.sin6_addr);
serverAddrPtr = &serverAddr6;
serverAddrLen = sizeof(serverAddr6);
} else if (isIpv4) {
socketFd = socket(AF_INET, SOCK_STREAM, 0);
if (socketFd < 0) {
HttpdnsLogDebug("IPQualityDetector failed to create IPv4 socket: %s", strerror(errno));
return -1;
}
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port);
inet_pton(AF_INET, [ip UTF8String], &serverAddr.sin_addr);
serverAddrPtr = &serverAddr;
serverAddrLen = sizeof(serverAddr);
} else {
return -1;
}
//
int flags = fcntl(socketFd, F_GETFL, 0);
fcntl(socketFd, F_SETFL, flags | O_NONBLOCK);
//
struct timeval startTime, endTime;
gettimeofday(&startTime, NULL);
//
int connectResult = connect(socketFd, serverAddrPtr, serverAddrLen);
if (connectResult < 0) {
if (errno == EINPROGRESS) {
// 使select
fd_set fdSet;
struct timeval timeout;
FD_ZERO(&fdSet);
FD_SET(socketFd, &fdSet);
// 2
// 2IP
timeout.tv_sec = 2;
timeout.tv_usec = 0;
int selectResult = select(socketFd + 1, NULL, &fdSet, NULL, &timeout);
if (selectResult <= 0) {
//
HttpdnsLogDebug("IPQualityDetector connection to %@ timed out or error: %s", ip, strerror(errno));
close(socketFd);
return -1;
} else {
//
int error;
socklen_t errorLen = sizeof(error);
if (getsockopt(socketFd, SOL_SOCKET, SO_ERROR, &error, &errorLen) < 0 || error != 0) {
HttpdnsLogDebug("IPQualityDetector connection to %@ failed after select: %s", ip, strerror(error));
close(socketFd);
return -1;
}
}
} else {
//
HttpdnsLogDebug("IPQualityDetector connection to %@ failed: %s", ip, strerror(errno));
close(socketFd);
return -1;
}
}
//
gettimeofday(&endTime, NULL);
// socket
close(socketFd);
//
long seconds = endTime.tv_sec - startTime.tv_sec;
long microseconds = endTime.tv_usec - startTime.tv_usec;
NSInteger costTime = (seconds * 1000) + (microseconds / 1000);
return costTime;
}
@end