Files
waf-platform/HttpDNSSDK/sdk/ios/NewHttpDNSTests/Network/HttpdnsNWHTTPClient_ConcurrencyTests.m

535 lines
24 KiB
Objective-C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// HttpdnsNWHTTPClient_ConcurrencyTests.m
// TrustHttpDNSTests
//
// @author Created by Claude Code on 2025-11-01
// Copyright © 2025 trustapp.com. All rights reserved.
//
// å¹¶å<C2B6>æµè¯• - 包å<E280A6>«å¹¶å<C2B6>è¯·æ± (H)ã€<C3A3>竞æ€<C3A6>æ<EFBFBD>¡ä»?(I)ã€<C3A3>å¹¶å<C2B6>多端å<C2AF>£ (N) 测试ç»?
// 测试总数�3 个(H:5 + I:5 + N:3�
//
#import "HttpdnsNWHTTPClientTestBase.h"
@interface HttpdnsNWHTTPClient_ConcurrencyTests : HttpdnsNWHTTPClientTestBase
@end
@implementation HttpdnsNWHTTPClient_ConcurrencyTests
#pragma mark - H. å¹¶å<C2B6>æµè¯•
// H.1 å¹¶å<C2B6>请æ±å<E2809A>Œä¸€ä¸»æœº
- (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];
// 验è¯<C3A8>至å°éƒ¨åˆ†è¯·æ±å¤<C3A5>用了连接(å“<C3A5>应时间有差å¼ï¼‰
XCTAssertEqual(responseTimes.count, concurrentCount);
}
// H.2 å¹¶å<C2B6>请æ±ä¸<C3A4>å<EFBFBD>Œè·¯å¾„
- (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 å¹¶å<C2B6> 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 高负载压力测�
- (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];
// 至å°å¤§éƒ¨åˆ†è¯·æ±åº”该æˆ<C3A6>功(å…<C3A5>许部分失败,å ä¸ºé«˜è´Ÿè½½ï¼?
XCTAssertGreaterThan(successCount, concurrentCount * 0.8, @"At least 80%% should succeed");
}
// H.5 æ··å<C2B7>ˆä¸²è¡Œ+å¹¶å<C2B6>
- (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];
});
// å¹¶å<C2B6>线ç¨
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. 竞æ€<C3A6>æ<EFBFBD>¡ä»¶æµè¯?
// I.1 连接池容é‡<C3A9>æµè¯?
- (void)testRaceCondition_ExceedPoolCapacity_MaxFourConnections {
XCTestExpectation *expectation = [self expectationWithDescription:@"Pool capacity test"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 快速连续å<C2AD>èµ?10 个请æ±?
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];
// 注æ„<C3A6>ï¼šæ— æ³•ç´æŽ¥æ£€æŸ¥æ± å¤§å°<C3A5>(内部实现),å<C592>ªèƒ½é€šè¿‡è¡Œä¸ºéªŒè¯<C3A8>
// 妿žœå®žçŽ°æ­£ç¡®ï¼Œæ± åº”è‡ªåŠ¨é™<C3A9>制为最å¤?4 个空闲连æŽ?
[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);
// 连接在这里自动归�
[expectation fulfill];
});
}
[self waitForExpectations:expectations timeout:30.0];
// 妿žœæ²¡æœ‰å´©æºƒæˆæ­è¨€å¤±è´¥ï¼Œè¯´æ˜Žå¹¶å<C2B6>å½è¿˜å¤„ç<E2809E>†æ­£ç¡?
}
// I.3 获å<C2B7>-归还-å†<C3A5>获å<C2B7>竞æ€?
- (void)testRaceCondition_AcquireReturnReacquire_CorrectState {
XCTestExpectation *expectation = [self expectationWithDescription:@"Acquire-Return-Reacquire"];
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:@"First"
timeout:15.0
error:&error1];
XCTAssertTrue(response1 != nil || error1 != nil);
// æž<C3A6>短æšç­‰å¾…ç¡®ä¿<C3A4>连接å½è¿?
[NSThread sleepForTimeInterval:0.1];
// 第二个请æ±åº”该能å¤<C3A5>用连接
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 超时与活跃连接冲çª<C3A7>(需è¦?1ç§ï¼Œæ ‡è®°ä¸ºæ…¢æµè¯•ï¼?
- (void)testRaceCondition_ExpiredConnectionPruning_CreatesNewConnection {
// 跳过此æµè¯•妿žœçŽ¯å¢ƒå<C692>˜é‡<C3A9>设置了 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ç§è¶…æ—?
[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 错误æ<C2AF>¢å¤<C3A5>竞æ€?
- (void)testRaceCondition_ErrorRecovery_PoolRemainsHealthy {
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// å<>起一äºä¼šå¤±è´¥çš„请æ±?
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;
// 使用短超时导致失�
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];
// 验è¯<C3A8>å<EFBFBD>Žç»­æ­£å¸¸è¯·æ±ä»<C3A4>能æˆ<C3A6>功
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. å¹¶å<C2B6>多端å<C2AF>£æµè¯?
// N.1 å¹¶å<C2B6>ä¿<C3A4>æŒ<C3A6>连接(慢æµè¯•ï¼?
- (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:å<C5A1>端å<C2AF>£ 11443 å<>èµ· 10 个请求,间隔 1 ç§?
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:å<C5A1>Œæ—¶å<C2B6>端å<C2AF>£ 11444 å<>èµ· 10 个请求,间隔 1 ç§?
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 轮询端å<C2AF>£åˆ†é…<C3A9>模å¼<C3A5>
- (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];
// åˆ<C3A5>å§åŒè®¡æ•°å™¨
for (NSNumber *port in ports) {
portRequestCounts[port] = @0;
}
// 以轮询æ¹å¼<C3A5>å<EFBFBD> 4 个端å<C2AF>£åˆ†å<E280A0>?100 个请æ±?
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);
}
}
// 验è¯<C3A8>æ¯<C3A6>个端å<C2AF>£å¤§çº¦èŽ·å¾— 25 个请æ±?
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 æ··å<C2B7>ˆè´Ÿè½½å¤šç«¯å<C2AF>£åœºæ™?
- (void)testConcurrentMultiPort_MixedLoadPattern_RobustHandling {
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 端å<C2AF>£ 11443:高负载ï¼?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];
});
}
// 端å<C2AF>£ 11444:中负载ï¼?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];
});
}
// 端å<C2AF>£ 11445:低负载ï¼? 个请求)
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