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,534 @@
//
// HttpdnsNWHTTPClient_ConcurrencyTests.m
// TrustHttpDNSTests
//
// @author Created by Claude Code on 2025-11-01
// Copyright © 2025 trustapp.com. All rights reserved.
//
// - (H)<EFBFBD><EFBFBD>?(I) (N) <EFBFBD><EFBFBD>?
// <EFBFBD><EFBFBD>?3 H:5 + I:5 + N:3<EFBFBD><EFBFBD>?
//
#import "HttpdnsNWHTTPClientTestBase.h"
@interface HttpdnsNWHTTPClient_ConcurrencyTests : HttpdnsNWHTTPClientTestBase
@end
@implementation HttpdnsNWHTTPClient_ConcurrencyTests
#pragma mark - H.
// H.1
- (void)testConcurrency_ParallelRequestsSameHost_AllSucceed {
NSInteger concurrentCount = 10;
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSMutableArray<NSNumber *> *responseTimes = [NSMutableArray array];
NSLock *lock = [[NSLock alloc] init];
for (NSInteger i = 0; i < concurrentCount; i++) {
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Request %ld", (long)i]];
[expectations addObject:expectation];
dispatch_group_enter(group);
dispatch_async(queue, ^{
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"HttpdnsNWHTTPClient/1.0"
timeout:15.0
error:&error];
CFAbsoluteTime endTime = CFAbsoluteTimeGetCurrent();
XCTAssertNotNil(response, @"Response %ld should not be nil", (long)i);
XCTAssertTrue(response.statusCode == 200 || response.statusCode == 503,
@"Request %ld got statusCode=%ld, expected 200 or 503", (long)i, (long)response.statusCode);
[lock lock];
[responseTimes addObject:@(endTime - startTime)];
[lock unlock];
[expectation fulfill];
dispatch_group_leave(group);
});
}
[self waitForExpectations:expectations timeout:30.0];
//
XCTAssertEqual(responseTimes.count, concurrentCount);
}
// H.2
- (void)testConcurrency_ParallelRequestsDifferentPaths_AllSucceed {
NSArray<NSString *> *paths = @[@"/get", @"/status/200", @"/headers", @"/user-agent", @"/uuid"];
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (NSString *path in paths) {
XCTestExpectation *expectation = [self expectationWithDescription:path];
[expectations addObject:expectation];
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSString *urlString = [NSString stringWithFormat:@"http://127.0.0.1:11080%@", path];
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:urlString
userAgent:@"HttpdnsNWHTTPClient/1.0"
timeout:15.0
error:&error];
XCTAssertNotNil(response, @"Response for %@ should not be nil", path);
XCTAssertTrue(response.statusCode == 200 || response.statusCode == 503, @"Request %@ should get valid status", path);
[expectation fulfill];
dispatch_group_leave(group);
});
}
[self waitForExpectations:expectations timeout:30.0];
}
// H.3 HTTP + HTTPS
- (void)testConcurrency_MixedHTTPAndHTTPS_BothSucceed {
NSInteger httpCount = 5;
NSInteger httpsCount = 5;
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// HTTP
for (NSInteger i = 0; i < httpCount; i++) {
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"HTTP %ld", (long)i]];
[expectations addObject:expectation];
dispatch_async(queue, ^{
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"HttpdnsNWHTTPClient/1.0"
timeout:15.0
error:&error];
XCTAssertNotNil(response);
[expectation fulfill];
});
}
// HTTPS
for (NSInteger i = 0; i < httpsCount; i++) {
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"HTTPS %ld", (long)i]];
[expectations addObject:expectation];
dispatch_async(queue, ^{
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
userAgent:@"HttpdnsNWHTTPClient/1.0"
timeout:15.0
error:&error];
XCTAssertNotNil(response);
[expectation fulfill];
});
}
[self waitForExpectations:expectations timeout:40.0];
}
// H.4 <EFBFBD><EFBFBD>?
- (void)testConcurrency_HighLoad50Concurrent_NoDeadlock {
NSInteger concurrentCount = 50;
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLock *successCountLock = [[NSLock alloc] init];
__block NSInteger successCount = 0;
for (NSInteger i = 0; i < concurrentCount; i++) {
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Request %ld", (long)i]];
[expectations addObject:expectation];
dispatch_async(queue, ^{
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"HttpdnsNWHTTPClient/1.0"
timeout:15.0
error:&error];
if (response && (response.statusCode == 200 || response.statusCode == 503)) {
[successCountLock lock];
successCount++;
[successCountLock unlock];
}
[expectation fulfill];
});
}
[self waitForExpectations:expectations timeout:60.0];
// <EFBFBD><EFBFBD>?
XCTAssertGreaterThan(successCount, concurrentCount * 0.8, @"At least 80%% should succeed");
}
// H.5 +
- (void)testConcurrency_MixedSerialAndParallel_NoInterference {
XCTestExpectation *serialExpectation = [self expectationWithDescription:@"Serial requests"];
XCTestExpectation *parallel1 = [self expectationWithDescription:@"Parallel 1"];
XCTestExpectation *parallel2 = [self expectationWithDescription:@"Parallel 2"];
XCTestExpectation *parallel3 = [self expectationWithDescription:@"Parallel 3"];
dispatch_queue_t serialQueue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t parallelQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 线
dispatch_async(serialQueue, ^{
for (NSInteger i = 0; i < 5; i++) {
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"Serial"
timeout:15.0
error:&error];
XCTAssertNotNil(response);
}
[serialExpectation fulfill];
});
// 线
dispatch_async(parallelQueue, ^{
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/uuid"
userAgent:@"Parallel1"
timeout:15.0
error:&error];
XCTAssertNotNil(response);
[parallel1 fulfill];
});
dispatch_async(parallelQueue, ^{
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/headers"
userAgent:@"Parallel2"
timeout:15.0
error:&error];
XCTAssertNotNil(response);
[parallel2 fulfill];
});
dispatch_async(parallelQueue, ^{
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/user-agent"
userAgent:@"Parallel3"
timeout:15.0
error:&error];
XCTAssertNotNil(response);
[parallel3 fulfill];
});
[self waitForExpectations:@[serialExpectation, parallel1, parallel2, parallel3] timeout:60.0];
}
#pragma mark - I. <EFBFBD><EFBFBD>?
// I.1 <EFBFBD><EFBFBD>?
- (void)testRaceCondition_ExceedPoolCapacity_MaxFourConnections {
XCTestExpectation *expectation = [self expectationWithDescription:@"Pool capacity test"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// <EFBFBD><EFBFBD>?10 <EFBFBD><EFBFBD>?
for (NSInteger i = 0; i < 10; i++) {
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"PoolTest"
timeout:15.0
error:&error];
XCTAssertTrue(response != nil || error != nil);
}
//
[NSThread sleepForTimeInterval:1.0];
//
// <EFBFBD><EFBFBD>?4 <EFBFBD><EFBFBD>?
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:120.0];
}
// I.2
- (void)testRaceCondition_SimultaneousConnectionReturn_NoDataRace {
NSInteger concurrentCount = 5;
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (NSInteger i = 0; i < concurrentCount; i++) {
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Return %ld", (long)i]];
[expectations addObject:expectation];
dispatch_async(queue, ^{
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"ReturnTest"
timeout:15.0
error:&error];
XCTAssertTrue(response != nil || error != nil);
// <EFBFBD><EFBFBD>?
[expectation fulfill];
});
}
[self waitForExpectations:expectations timeout:30.0];
// <EFBFBD><EFBFBD>?
}
// I.3 --<EFBFBD><EFBFBD>?
- (void)testRaceCondition_AcquireReturnReacquire_CorrectState {
XCTestExpectation *expectation = [self expectationWithDescription:@"Acquire-Return-Reacquire"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// <EFBFBD><EFBFBD>?
NSError *error1 = nil;
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"First"
timeout:15.0
error:&error1];
XCTAssertTrue(response1 != nil || error1 != nil);
// <EFBFBD><EFBFBD>?
[NSThread sleepForTimeInterval:0.1];
//
NSError *error2 = nil;
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"Second"
timeout:15.0
error:&error2];
XCTAssertTrue(response2 != nil || error2 != nil);
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:35.0];
}
// I.4 <EFBFBD><EFBFBD>?1<EFBFBD><EFBFBD>?
- (void)testRaceCondition_ExpiredConnectionPruning_CreatesNewConnection {
// SKIP_SLOW_TESTS
if (getenv("SKIP_SLOW_TESTS")) {
return;
}
XCTestExpectation *expectation = [self expectationWithDescription:@"Connection expiry"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//
NSError *error1 = nil;
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"Initial"
timeout:15.0
error:&error1];
XCTAssertTrue(response1 != nil || error1 != nil);
// 30<EFBFBD><EFBFBD>?
[NSThread sleepForTimeInterval:31.0];
//
NSError *error2 = nil;
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"AfterExpiry"
timeout:15.0
error:&error2];
XCTAssertTrue(response2 != nil || error2 != nil);
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:70.0];
}
// I.5 <EFBFBD><EFBFBD>?
- (void)testRaceCondition_ErrorRecovery_PoolRemainsHealthy {
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// <EFBFBD><EFBFBD>?
for (NSInteger i = 0; i < 3; i++) {
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Error %ld", (long)i]];
[expectations addObject:expectation];
dispatch_async(queue, ^{
NSError *error = nil;
// 使<EFBFBD><EFBFBD>?
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/delay/5"
userAgent:@"ErrorTest"
timeout:1.0
error:&error];
//
[expectation fulfill];
});
}
[self waitForExpectations:expectations timeout:15.0];
//
XCTestExpectation *recoveryExpectation = [self expectationWithDescription:@"Recovery"];
dispatch_async(queue, ^{
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"Recovery"
timeout:15.0
error:&error];
XCTAssertTrue(response != nil || error != nil);
[recoveryExpectation fulfill];
});
[self waitForExpectations:@[recoveryExpectation] timeout:20.0];
}
#pragma mark - N. <EFBFBD><EFBFBD>?
// N.1 <EFBFBD><EFBFBD>?
- (void)testConcurrentMultiPort_ParallelKeepAlive_IndependentConnections {
if (getenv("SKIP_SLOW_TESTS")) {
return;
}
XCTestExpectation *expectation11443 = [self expectationWithDescription:@"Port 11443 keep-alive"];
XCTestExpectation *expectation11444 = [self expectationWithDescription:@"Port 11444 keep-alive"];
// 线 1 11443 10 1 <EFBFBD><EFBFBD>?
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSInteger i = 0; i < 10; i++) {
if (i > 0) {
[NSThread sleepForTimeInterval:1.0];
}
NSError *error = nil;
[self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
userAgent:@"KeepAlive11443"
timeout:15.0
error:&error];
}
[expectation11443 fulfill];
});
// 线 2 11444 10 1 <EFBFBD><EFBFBD>?
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSInteger i = 0; i < 10; i++) {
if (i > 0) {
[NSThread sleepForTimeInterval:1.0];
}
NSError *error = nil;
[self.client performRequestWithURLString:@"https://127.0.0.1:11444/get"
userAgent:@"KeepAlive11444"
timeout:15.0
error:&error];
}
[expectation11444 fulfill];
});
[self waitForExpectations:@[expectation11443, expectation11444] timeout:40.0];
}
// N.2
- (void)testConcurrentMultiPort_RoundRobinDistribution_EvenLoad {
XCTestExpectation *expectation = [self expectationWithDescription:@"Round-robin distribution"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray<NSNumber *> *ports = @[@11443, @11444, @11445, @11446];
NSInteger totalRequests = 100;
NSMutableDictionary<NSNumber *, NSNumber *> *portRequestCounts = [NSMutableDictionary dictionary];
//
for (NSNumber *port in ports) {
portRequestCounts[port] = @0;
}
// 4 <EFBFBD><EFBFBD>?100 <EFBFBD><EFBFBD>?
for (NSInteger i = 0; i < totalRequests; i++) {
NSNumber *port = ports[i % ports.count];
NSString *urlString = [NSString stringWithFormat:@"https://127.0.0.1:%@/get", port];
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:urlString
userAgent:@"RoundRobin"
timeout:15.0
error:&error];
if (response && response.statusCode == 200) {
NSInteger count = [portRequestCounts[port] integerValue];
portRequestCounts[port] = @(count + 1);
}
}
// 25 <EFBFBD><EFBFBD>?
for (NSNumber *port in ports) {
NSInteger count = [portRequestCounts[port] integerValue];
XCTAssertEqual(count, 25, @"Port %@ should receive 25 requests", port);
}
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:180.0];
}
// N.3 <EFBFBD><EFBFBD>?
- (void)testConcurrentMultiPort_MixedLoadPattern_RobustHandling {
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 11443<EFBFBD><EFBFBD>?0
for (NSInteger i = 0; i < 20; i++) {
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Heavy11443 %ld", (long)i]];
[expectations addObject:expectation];
dispatch_async(queue, ^{
NSError *error = nil;
[self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
userAgent:@"HeavyLoad"
timeout:15.0
error:&error];
[expectation fulfill];
});
}
// 11444<EFBFBD><EFBFBD>?0
for (NSInteger i = 0; i < 10; i++) {
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Medium11444 %ld", (long)i]];
[expectations addObject:expectation];
dispatch_async(queue, ^{
NSError *error = nil;
[self.client performRequestWithURLString:@"https://127.0.0.1:11444/get"
userAgent:@"MediumLoad"
timeout:15.0
error:&error];
[expectation fulfill];
});
}
// 11445<EFBFBD><EFBFBD>?
for (NSInteger i = 0; i < 5; i++) {
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Light11445 %ld", (long)i]];
[expectations addObject:expectation];
dispatch_async(queue, ^{
NSError *error = nil;
[self.client performRequestWithURLString:@"https://127.0.0.1:11445/get"
userAgent:@"LightLoad"
timeout:15.0
error:&error];
[expectation fulfill];
});
}
[self waitForExpectations:expectations timeout:80.0];
}
@end