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

775 lines
37 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.

//
// HttpdnsNWHTTPClient_PoolManagementTests.m
// TrustHttpDNSTests
//
// @author Created by Claude Code on 2025-11-01
// Copyright ツゥ 2025 trustapp.com. All rights reserved.
//
// 霑樊磁豎<E7A381>邂。逅<EFBDA1>オ玖ッ?- 蛹<>性螟夂ォッ蜿」髫皮ヲ?(K)縲∫ォッ蜿」豎<EFBDA3>閠怜ース (L)縲∵ア<E288B5>鬪瑚ッ<E7919A> (O)縲∫ゥコ髣イ雜<EFBDB2><E99B9C>?(S) 豬玖ッ慕サ?
// 豬玖ッ墓€サ謨ー<E8ACA8>?6 荳ェ<E88DB3><EFBDAA>:5 + L:3 + O:3 + S:5<>?
//
#import "HttpdnsNWHTTPClientTestBase.h"
@interface HttpdnsNWHTTPClient_PoolManagementTests : HttpdnsNWHTTPClientTestBase
@end
@implementation HttpdnsNWHTTPClient_PoolManagementTests
#pragma mark - K. 螟夂ォッ蜿」霑樊磁髫皮ヲサ豬玖ッ?
// K.1 荳榊酔 HTTPS 遶ッ蜿」菴ソ逕ィ荳榊酔霑樊磁豎?
- (void)testMultiPort_DifferentHTTPSPorts_SeparatePoolKeys {
XCTestExpectation *expectation = [self expectationWithDescription:@"Different ports use different pools"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 隸キ豎らォッ蜿」 11443
NSError *error1 = nil;
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
userAgent:@"Port11443"
timeout:15.0
error:&error1];
XCTAssertNotNil(response1, @"First request to port 11443 should succeed");
XCTAssertEqual(response1.statusCode, 200);
// 隸キ豎らォッ蜿」 11444<34>亥コ碑ッ・蛻帛サコ譁ー霑樊磁<E6A88A>御ク榊、咲畑 11443 逧<><EFBFBD>
NSError *error2 = nil;
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"https://127.0.0.1:11444/get"
userAgent:@"Port11444"
timeout:15.0
error:&error2];
XCTAssertNotNil(response2, @"First request to port 11444 should succeed");
XCTAssertEqual(response2.statusCode, 200);
// 蜀肴ャ。隸キ豎らォッ蜿」 11443<34>亥コ碑ッ・螟咲畑荵句燕逧<E78795>ソ樊磁<E6A88A>?
NSError *error3 = nil;
HttpdnsNWHTTPClientResponse *response3 = [self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
userAgent:@"Port11443Again"
timeout:15.0
error:&error3];
XCTAssertNotNil(response3, @"Second request to port 11443 should reuse connection");
XCTAssertEqual(response3.statusCode, 200);
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:50.0];
}
// K.2 荳我クェ荳榊酔 HTTPS 遶ッ蜿」逧<EFBDA3>ケカ蜿題ッキ豎?
- (void)testMultiPort_ConcurrentThreePorts_AllSucceed {
NSInteger requestsPerPort = 10;
NSArray<NSNumber *> *ports = @[@11443, @11444, @11445];
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 (NSNumber *port in ports) {
for (NSInteger i = 0; i < requestsPerPort; i++) {
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Port %@ Request %ld", port, (long)i]];
[expectations addObject:expectation];
dispatch_async(queue, ^{
NSString *urlString = [NSString stringWithFormat:@"https://127.0.0.1:%@/get", port];
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:urlString
userAgent:@"ConcurrentMultiPort"
timeout:15.0
error:&error];
if (response && response.statusCode == 200) {
[successCountLock lock];
successCount++;
[successCountLock unlock];
}
[expectation fulfill];
});
}
}
[self waitForExpectations:expectations timeout:60.0];
// 鬪瑚ッ∵園譛芽ッキ豎る<E8B18E>謌仙粥
XCTAssertEqual(successCount, ports.count * requestsPerPort, @"All 30 requests should succeed");
}
// K.3 蠢ォ騾溷<E9A8BE>謐「遶ッ蜿」讓。蠑?
- (void)testMultiPort_SequentialPortSwitching_ConnectionReusePerPort {
XCTestExpectation *expectation = [self expectationWithDescription:@"Sequential port switching"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray<NSString *> *urls = @[
@"https://127.0.0.1:11443/get", // 隨ャ荳€谺。隶ソ髣?11443
@"https://127.0.0.1:11444/get", // 隨ャ荳€谺。隶ソ髣?11444
@"https://127.0.0.1:11445/get", // 隨ャ荳€谺。隶ソ髣?11445
@"https://127.0.0.1:11443/get", // 隨ャ莠梧ャ。隶ソ髣?11443<34>亥コ泌、咲畑<E592B2>?
@"https://127.0.0.1:11444/get", // 隨ャ莠梧ャ。隶ソ髣?11444<34>亥コ泌、咲畑<E592B2>?
];
NSMutableArray<NSNumber *> *responseTimes = [NSMutableArray array];
for (NSString *url in urls) {
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:url
userAgent:@"SwitchingPorts"
timeout:15.0
error:&error];
CFAbsoluteTime elapsed = CFAbsoluteTimeGetCurrent() - startTime;
XCTAssertNotNil(response, @"Request to %@ should succeed", url);
XCTAssertEqual(response.statusCode, 200);
[responseTimes addObject:@(elapsed)];
}
// 鬪瑚ッ∵園譛芽ッキ豎る<E8B18E>螳梧<E89EB3>
XCTAssertEqual(responseTimes.count, urls.count);
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:80.0];
}
// K.4 豈丈クェ遶ッ蜿」迢ャ遶狗噪霑樊磁豎<E7A381>髯仙宛
- (void)testMultiPort_PerPortPoolLimit_IndependentPools {
XCTestExpectation *expectation = [self expectationWithDescription:@"Per-port pool limits"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 蜷醍ォッ蜿?11443 蜿鷹€?10 荳ェ隸キ豎?
for (NSInteger i = 0; i < 10; i++) {
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
userAgent:@"Pool11443"
timeout:15.0
error:&error];
XCTAssertTrue(response != nil || error != nil);
}
// 蜷醍ォッ蜿?11444 蜿鷹€?10 荳ェ隸キ豎ゑシ亥コ碑ッ・譛臥峡遶狗噪豎<E599AA><E8B18E><EFBFBD>
for (NSInteger i = 0; i < 10; i++) {
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"https://127.0.0.1:11444/get"
userAgent:@"Pool11444"
timeout:15.0
error:&error];
XCTAssertTrue(response != nil || error != nil);
}
// 遲牙セ<E78999>ソ樊磁蠖定ソ<E5AE9A>
[NSThread sleepForTimeInterval:1.0];
// 鬪瑚ッ∽ク、荳ェ遶ッ蜿」驛ス莉咲┯蜿ッ逕?
NSError *error1 = nil;
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
userAgent:@"Verify11443"
timeout:15.0
error:&error1];
XCTAssertNotNil(response1, @"Port 11443 should still work after heavy usage");
NSError *error2 = nil;
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"https://127.0.0.1:11444/get"
userAgent:@"Verify11444"
timeout:15.0
error:&error2];
XCTAssertNotNil(response2, @"Port 11444 should still work after heavy usage");
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:150.0];
}
// K.5 莠、髞呵ョソ髣ョ螟壻クェ遶ッ蜿」
- (void)testMultiPort_InterleavedRequests_AllPortsAccessible {
XCTestExpectation *expectation = [self expectationWithDescription:@"Interleaved multi-port requests"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray<NSNumber *> *ports = @[@11443, @11444, @11445, @11446];
NSInteger totalRequests = 20;
NSInteger successCount = 0;
// 莠、髞呵ッキ豎ゑシ壻セ晄ャ。蠕ェ邇ッ隶ソ髣ョ謇€譛臥ォッ蜿?
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:@"Interleaved"
timeout:15.0
error:&error];
if (response && response.statusCode == 200) {
successCount++;
}
}
XCTAssertEqual(successCount, totalRequests, @"All interleaved requests should succeed");
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:120.0];
}
#pragma mark - L. 蝓コ莠守ォッ蜿」逧<EFBDA3><EFBFBD>閠怜ース豬玖ッ<E78E96>
// L.1 蝗帑クェ遶ッ蜿」蜷梧慮謇ソ霓ス鬮倩エ溯ス?
- (void)testPoolExhaustion_FourPortsSimultaneous_AllSucceed {
NSInteger requestsPerPort = 10;
NSArray<NSNumber *> *ports = @[@11443, @11444, @11445, @11446];
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;
// 蜷?4 荳ェ遶ッ蜿」蜷<EFBDA3>書襍キ 10 荳ェ蟷カ蜿題ッキ豎ゑシ亥<EFBDBC>?40 荳ェ<E88DB3><EFBDAA>
for (NSNumber *port in ports) {
for (NSInteger i = 0; i < requestsPerPort; i++) {
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Port %@ Request %ld", port, (long)i]];
[expectations addObject:expectation];
dispatch_async(queue, ^{
NSString *urlString = [NSString stringWithFormat:@"https://127.0.0.1:%@/get", port];
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:urlString
userAgent:@"FourPortLoad"
timeout:15.0
error:&error];
if (response && response.statusCode == 200) {
[successCountLock lock];
successCount++;
[successCountLock unlock];
}
[expectation fulfill];
});
}
}
[self waitForExpectations:expectations timeout:80.0];
// 鬪瑚ッ∵<EFBDAF>蜉溽<E89C89>?> 95%<25><EFBFBD>隶ク蟆鷹㍼蝗<E38DBC>蟷カ蜿大ッシ閾エ逧<EFBDB4>、ア雍・<E99B8D><EFBDA5>
XCTAssertGreaterThan(successCount, 38, @"At least 95%% of 40 requests should succeed");
}
// L.2 蜊穂クェ遶ッ蜿」閠怜ース譌カ蜈カ莉也ォッ蜿」荳榊女蠖ア蜩?
- (void)testPoolExhaustion_SinglePortExhausted_OthersUnaffected {
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
__block NSInteger port11444SuccessCount = 0;
NSLock *countLock = [[NSLock alloc] init];
// 蜷醍ォッ蜿?11443 蜿題オキ 20 荳ェ蟷カ蜿題ッキ豎ゑシ亥庄閭ス蟇シ閾エ豎<EFBDB4>閠怜ース<EFBDB0>?
for (NSInteger i = 0; i < 20; i++) {
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Exhaust11443 %ld", (long)i]];
[expectations addObject:expectation];
dispatch_async(queue, ^{
NSError *error = nil;
[self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
userAgent:@"Exhaust11443"
timeout:15.0
error:&error];
[expectation fulfill];
});
}
// 蜷梧慮蜷醍ォッ蜿?11444 蜿題オキ 5 荳ェ隸キ豎ゑシ亥コ碑ッ・荳榊女 11443 蠖ア蜩搾シ?
for (NSInteger i = 0; i < 5; i++) {
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Port11444 %ld", (long)i]];
[expectations addObject:expectation];
dispatch_async(queue, ^{
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"https://127.0.0.1:11444/get"
userAgent:@"Independent11444"
timeout:15.0
error:&error];
CFAbsoluteTime elapsed = CFAbsoluteTimeGetCurrent() - startTime;
if (response && response.statusCode == 200) {
[countLock lock];
port11444SuccessCount++;
[countLock unlock];
}
// 鬪瑚ッ∝桃蠎疲慮髣エ蜷育炊<E882B2>井ク榊コ泌屏 11443 雍溯スス閠梧仞闡怜サカ霑滂シ<E6BB82>
XCTAssertLessThan(elapsed, 10.0, @"Port 11444 should not be delayed by port 11443 load");
[expectation fulfill];
});
}
[self waitForExpectations:expectations timeout:60.0];
// 鬪瑚ッ∫ォッ蜿」 11444 逧<>ッキ豎る<E8B18E>謌仙粥
XCTAssertEqual(port11444SuccessCount, 5, @"All port 11444 requests should succeed despite 11443 load");
}
// L.3 螟夂ォッ蜿」菴ソ逕ィ蜷守噪霑樊磁貂<E7A381><E8B282>?
- (void)testPoolExhaustion_MultiPortCleanup_ExpiredConnectionsPruned {
if (getenv("SKIP_SLOW_TESTS")) {
return;
}
XCTestExpectation *expectation = [self expectationWithDescription:@"Multi-port connection cleanup"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray<NSNumber *> *ports = @[@11443, @11444, @11445];
// 蜷台ク我クェ遶ッ蜿」蜷<EFBDA3>書襍キ荳€荳ェ隸キ豎?
for (NSNumber *port in ports) {
NSString *urlString = [NSString stringWithFormat:@"https://127.0.0.1:%@/get", port];
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:urlString
userAgent:@"Initial"
timeout:15.0
error:&error];
XCTAssertTrue(response != nil || error != nil);
}
// 遲牙セ<E78999><EFBFBD>ソ<EFBFBD> 30 遘抵シ瑚ョゥ謇€譛芽ソ樊磁霑<E7A381><E99C91>?
[NSThread sleepForTimeInterval:31.0];
// 蜀肴ャ。蜷台ク我クェ遶ッ蜿」蜿題オキ隸キ豎ゑシ亥コ碑ッ・蛻帛サコ譁ー霑樊磁<E6A88A><E7A381>
for (NSNumber *port in ports) {
NSString *urlString = [NSString stringWithFormat:@"https://127.0.0.1:%@/get", port];
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:urlString
userAgent:@"AfterExpiry"
timeout:15.0
error:&error];
XCTAssertTrue(response != nil || error != nil, @"Requests should succeed after expiry");
}
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:80.0];
}
#pragma mark - O. 霑樊磁豎<E7A381>鬪瑚ッ∵オ玖ッ包シ井スソ逕ィ譁ー蠅樒噪譽€譟?API<50>?
// O.1 扈シ蜷郁ソ樊磁豎<E7A381>鬪瑚ッ?- 貍皮、コ謇€譛画」€譟・閭ス蜉?
- (void)testPoolVerification_ComprehensiveCheck_AllAspectsVerified {
[self.client resetPoolStatistics];
NSString *poolKey = @"127.0.0.1:11080:tcp";
// 蛻晏ァ狗憾諤<E686BE>シ壽裏霑樊<E99C91>?
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey], 0,
@"Pool should be empty initially");
XCTAssertEqual([self.client totalConnectionCount], 0,
@"Total connections should be 0 initially");
XCTAssertEqual(self.client.connectionCreationCount, 0,
@"Creation count should be 0 initially");
XCTAssertEqual(self.client.connectionReuseCount, 0,
@"Reuse count should be 0 initially");
// 蜿鷹€?5 荳ェ隸キ豎ょ芦蜷御ク€遶ッ轤ケ
for (NSInteger i = 0; i < 5; i++) {
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"PoolVerificationTest"
timeout:15.0
error:&error];
XCTAssertNotNil(response, @"Request %ld should succeed", (long)i);
XCTAssertEqual(response.statusCode, 200, @"Request %ld should return 200", (long)i);
}
// 鬪瑚ッ∬ソ樊磁豎<E7A381>迥カ諤?
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey], 1,
@"Should have exactly 1 connection in pool for key: %@", poolKey);
XCTAssertEqual([self.client totalConnectionCount], 1,
@"Total connection count should be 1");
// 鬪瑚ッ∫サ溯ョ。隶。謨ー
XCTAssertEqual(self.client.connectionCreationCount, 1,
@"Should create only 1 connection");
XCTAssertEqual(self.client.connectionReuseCount, 4,
@"Should reuse connection 4 times");
// 鬪瑚ッ∬ソ樊磁螟咲畑邇?
CGFloat reuseRate = (CGFloat)self.client.connectionReuseCount /
(self.client.connectionCreationCount + self.client.connectionReuseCount);
XCTAssertGreaterThanOrEqual(reuseRate, 0.8,
@"Reuse rate should be at least 80%% (actual: %.1f%%)", reuseRate * 100);
// 鬪瑚ッ<E7919A> pool keys
NSArray<NSString *> *allKeys = [self.client allConnectionPoolKeys];
XCTAssertEqual(allKeys.count, 1, @"Should have exactly 1 pool key");
XCTAssertTrue([allKeys containsObject:poolKey], @"Should contain the expected pool key");
}
// O.2 螟夂ォッ蜿」霑樊磁豎<E7A381>髫皮ヲサ鬪瑚ッ<E7919A>
- (void)testPoolVerification_MultiPort_IndependentPools {
[self.client resetPoolStatistics];
NSString *key11443 = @"127.0.0.1:11443:tls";
NSString *key11444 = @"127.0.0.1:11444:tls";
// 蛻晏ァ具シ壻ク、荳ェ豎<EFBDAA>驛ス荳コ遨?
XCTAssertEqual([self.client connectionPoolCountForKey:key11443], 0);
XCTAssertEqual([self.client connectionPoolCountForKey:key11444], 0);
// 蜷醍ォッ蜿?11443 蜿鷹€?3 荳ェ隸キ豎?
for (NSInteger i = 0; i < 3; i++) {
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
userAgent:@"Port11443"
timeout:15.0
error:&error];
XCTAssertNotNil(response);
}
// 鬪瑚ッ∫ォッ蜿」 11443 逧<><EFBFBD>迥カ諤?
XCTAssertEqual([self.client connectionPoolCountForKey:key11443], 1,
@"Port 11443 should have 1 connection");
XCTAssertEqual([self.client connectionPoolCountForKey:key11444], 0,
@"Port 11444 should still be empty");
// 蜷醍ォッ蜿?11444 蜿鷹€?3 荳ェ隸キ豎?
for (NSInteger i = 0; i < 3; i++) {
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"https://127.0.0.1:11444/get"
userAgent:@"Port11444"
timeout:15.0
error:&error];
XCTAssertNotNil(response);
}
// 鬪瑚ッ∽ク、荳ェ遶ッ蜿」逧<EFBDA3><EFBFBD>驛ス蟄伜惠荳皮峡遶<E5B3A1>
XCTAssertEqual([self.client connectionPoolCountForKey:key11443], 1,
@"Port 11443 should still have 1 connection");
XCTAssertEqual([self.client connectionPoolCountForKey:key11444], 1,
@"Port 11444 should now have 1 connection");
XCTAssertEqual([self.client totalConnectionCount], 2,
@"Total should be 2 connections (one per port)");
// 鬪瑚ッ∫サ溯ョ。<EFBDAE>壼コ碑ッ・蛻帛サコ莠<EFBDBA> 2 荳ェ霑樊磁<E6A88A>悟、咲畑莠?4 谺?
XCTAssertEqual(self.client.connectionCreationCount, 2,
@"Should create 2 connections (one per port)");
XCTAssertEqual(self.client.connectionReuseCount, 4,
@"Should reuse connections 4 times total");
// 鬪瑚ッ<E7919A> pool keys
NSArray<NSString *> *allKeys = [self.client allConnectionPoolKeys];
XCTAssertEqual(allKeys.count, 2, @"Should have 2 pool keys");
XCTAssertTrue([allKeys containsObject:key11443], @"Should contain key for port 11443");
XCTAssertTrue([allKeys containsObject:key11444], @"Should contain key for port 11444");
}
// O.3 霑樊磁豎<E7A381>螳ケ驥城剞蛻カ鬪瑚ッ?
- (void)testPoolVerification_PoolCapacity_MaxFourConnections {
[self.client resetPoolStatistics];
NSString *poolKey = @"127.0.0.1:11080:tcp";
// 蜿鷹€?10 荳ェ霑樒サュ隸キ豎ゑシ域ッ丈クェ隸キ豎る<E8B18E>莨壼ス定ソ倩ソ樊磁蛻ー豎<EFBDB0><E8B18E>?
for (NSInteger i = 0; i < 10; i++) {
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"CapacityTest"
timeout:15.0
error:&error];
XCTAssertNotNil(response);
}
// 遲牙セ<E78999>ソ樊磁蠖定ソ<E5AE9A>
[NSThread sleepForTimeInterval:1.0];
// 鬪瑚ッ∵ア<E288B5>螟ァ蟆丈ク崎カ<E5B48E>ソ<EFBFBD> 4<><34>HttpdnsNWHTTPClientMaxIdleConnectionsPerKey<65>?
NSUInteger poolSize = [self.client connectionPoolCountForKey:poolKey];
XCTAssertLessThanOrEqual(poolSize, 4,
@"Pool size should not exceed 4 (actual: %lu)", (unsigned long)poolSize);
// 鬪瑚ッ∫サ溯ョ。<EFBDAE>壼コ碑ッ・蜿ェ蛻帛サコ莠?1 荳ェ霑樊磁<E6A88A>亥屏荳コ荳イ陦瑚ッキ豎ゑシ梧ッ乗ャ。驛ス螟咲畑<E592B2>?
XCTAssertEqual(self.client.connectionCreationCount, 1,
@"Should create only 1 connection for sequential requests");
XCTAssertEqual(self.client.connectionReuseCount, 9,
@"Should reuse connection 9 times");
}
#pragma mark - S. 遨コ髣イ雜<EFBDB2>慮隸ヲ扈<EFBDA6>オ玖ッ<E78E96>
// S.1 豺キ蜷郁ソ<E98381>悄蜥梧怏謨郁ソ樊<EFBDBF>?- 騾画叫諤ァ貂<EFBDA7><E8B282>?
- (void)testIdleTimeout_MixedExpiredValid_SelectivePruning {
[self.client resetPoolStatistics];
NSString *poolKey = @"127.0.0.1:11080:tcp";
// 蛻帛サコ隨ャ荳€荳ェ霑樊<E99C91>?
NSError *error1 = nil;
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"ConnectionA"
timeout:15.0
error:&error1];
XCTAssertNotNil(response1);
XCTAssertEqual(response1.statusCode, 200);
// 遲牙セ<E78999>ソ樊磁蠖定ソ<E5AE9A>
[NSThread sleepForTimeInterval:0.5];
// 菴ソ逕ィ DEBUG API 闔キ蜿冶ソ樊磁 A 蟷カ隶セ鄂ョ荳コ霑<EFBDBA><EFBFBD>?5 遘貞燕<E8B29E>?
NSArray<HttpdnsNWReusableConnection *> *connections = [self.client connectionsInPoolForKey:poolKey];
XCTAssertEqual(connections.count, 1, @"Should have 1 connection in pool");
HttpdnsNWReusableConnection *connectionA = connections.firstObject;
NSDate *expiredDate = [NSDate dateWithTimeIntervalSinceNow:-35.0];
[connectionA debugSetLastUsedDate:expiredDate];
// 蛻帛サコ隨ャ莠御クェ霑樊磁<E6A88A>€夊ソ<E5A48A>ケカ蜿題ッキ豎ゑシ?
NSError *error2 = nil;
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"ConnectionB"
timeout:15.0
error:&error2];
XCTAssertNotNil(response2);
// 遲牙セ<E78999>ス定ソ<E5AE9A>
[NSThread sleepForTimeInterval:0.5];
// 鬪瑚ッ<E7919A>シ壼コ碑ッ・譛<EFBDA5> 1 荳ェ霑樊磁<E6A88A><E7A381>onnectionA 霑<>悄陲ォ遘サ髯、<E9ABAF>慶onnectionB 逡吩ク具シ?
connections = [self.client connectionsInPoolForKey:poolKey];
XCTAssertEqual(connections.count, 1,
@"Should have only 1 connection (expired A removed, valid B kept)");
// 隨ャ荳我クェ隸キ豎ょコ碑ッ・螟咲<E89E9F>?connectionB
[self.client resetPoolStatistics];
NSError *error3 = nil;
HttpdnsNWHTTPClientResponse *response3 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"ReuseB"
timeout:15.0
error:&error3];
XCTAssertNotNil(response3);
// 鬪瑚ッ<E7919A>シ壼、咲畑莠<E79591> connectionB<6E>域イ。譛牙<E8AD9B>蟒コ譁ー霑樊磁<E6A88A>?
XCTAssertEqual(self.client.connectionCreationCount, 0,
@"Should not create new connection (reuse existing valid connection)");
XCTAssertEqual(self.client.connectionReuseCount, 1,
@"Should reuse the valid connection B");
}
// S.2 In-Use 菫晄侃 - 菴ソ逕ィ荳ュ逧<EFBDAD>ソ樊磁荳堺シ夊ソ<E5A48A>
- (void)testIdleTimeout_InUseProtection_ActiveConnectionNotPruned {
[self.client resetPoolStatistics];
NSString *poolKey = @"127.0.0.1:11080:tcp";
// 蛻帛サコ隨ャ荳€荳ェ霑樊<E99C91>?
NSError *error1 = nil;
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"Initial"
timeout:15.0
error:&error1];
XCTAssertNotNil(response1);
[NSThread sleepForTimeInterval:0.5];
// 蛟溷<E89B9F>霑樊磁蟷カ菫晄<E88FAB>?inUse=YES
NSArray<HttpdnsNWReusableConnection *> *connections = [self.client connectionsInPoolForKey:poolKey];
XCTAssertEqual(connections.count, 1);
HttpdnsNWReusableConnection *conn = connections.firstObject;
// 謇句勘隶セ鄂ョ荳?60 遘貞燕<E8B29E>郁ソ懆カ?30 遘定カ<E5AE9A><EFBFBD><E685AE>
NSDate *veryOldDate = [NSDate dateWithTimeIntervalSinceNow:-60.0];
[conn debugSetLastUsedDate:veryOldDate];
// 蟆<>ソ樊磁譬<E7A381>ョー荳コ菴ソ逕ィ荳?
[conn debugSetInUse:YES];
// 隗ヲ蜿第ク<E7ACAC><EFBFBD>€夊ソ<E5A48A>書襍キ蜿ヲ荳€荳ェ蟷カ蜿題ッキ豎ゑシ<E38291>
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block NSInteger connectionsBefore = 0;
__block NSInteger connectionsAfter = 0;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
connectionsBefore = [self.client totalConnectionCount];
// 蜿題オキ隸キ豎ゑシ井シ夊ァヲ蜿<EFBDA6> pruneConnectionPool<6F>?
NSError *error2 = nil;
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"TriggerPrune"
timeout:15.0
error:&error2];
[NSThread sleepForTimeInterval:0.5];
connectionsAfter = [self.client totalConnectionCount];
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 20 * NSEC_PER_SEC));
// 貂<><EFBFBD>夐㍾鄂?inUse 迥カ諤?
[conn debugSetInUse:NO];
// 鬪瑚ッ<E7919A>シ喨nUse=YES 逧<>ソ樊磁荳榊コ碑ッ・陲ォ貂<EFBDAB><E8B282>?
// connectionsBefore = 1 (譌ァ霑樊<E99C91>?, connectionsAfter = 2 (譌ァ霑樊<E99C91>?+ 譁ー霑樊<E99C91>?
XCTAssertEqual(connectionsBefore, 1,
@"Should have 1 connection before (in-use protected)");
XCTAssertEqual(connectionsAfter, 2,
@"Should have 2 connections after (in-use connection NOT pruned, new connection added)");
}
// S.3 謇€譛芽ソ樊磁霑<E7A381><E99C91>?- 謇ケ驥乗ク<E4B997>
- (void)testIdleTimeout_AllExpired_BulkPruning {
[self.client resetPoolStatistics];
NSString *poolKey = @"127.0.0.1:11080:tcp";
// 蛻帛サコ 4 荳ェ霑樊磁<E6A88A>亥。ォ貊。豎<EFBDA1><E8B18E><EFBFBD>
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (NSInteger i = 0; i < 4; 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:@"FillPool"
timeout:15.0
error:&error];
XCTAssertNotNil(response);
[expectation fulfill];
});
}
[self waitForExpectations:expectations timeout:30.0];
// 遲牙セ<E78999>園譛芽ソ樊磁蠖定ソ?
[NSThread sleepForTimeInterval:1.0];
// 鬪瑚ッ∵ア<E288B5>蟾イ貊?
NSUInteger poolSizeBefore = [self.client connectionPoolCountForKey:poolKey];
XCTAssertGreaterThan(poolSizeBefore, 0, @"Pool should have connections");
// 蟆<>園譛芽ソ樊磁隶セ鄂ョ荳コ霑<EFBDBA><EFBFBD>?1 遘貞燕<E8B29E>?
NSArray<HttpdnsNWReusableConnection *> *connections = [self.client connectionsInPoolForKey:poolKey];
NSDate *expiredDate = [NSDate dateWithTimeIntervalSinceNow:-31.0];
for (HttpdnsNWReusableConnection *conn in connections) {
[conn debugSetLastUsedDate:expiredDate];
}
// 蜿題オキ譁ー隸キ豎ゑシ郁ァヲ蜿第音驥乗ク<E4B997><EFBFBD>?
NSError *errorNew = nil;
HttpdnsNWHTTPClientResponse *responseNew = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"AfterBulkExpiry"
timeout:15.0
error:&errorNew];
XCTAssertNotNil(responseNew, @"Request should succeed after bulk pruning");
XCTAssertEqual(responseNew.statusCode, 200);
// 遲牙セ<E78999>ス定ソ<E5AE9A>
[NSThread sleepForTimeInterval:0.5];
// 鬪瑚ッ<E7919A>シ壽ア<E5A3BD>荳ュ蜿ェ譛画眠霑樊磁<E6A88A>域園譛画立霑樊磁陲ォ貂<EFBDAB><EFBFBD><E7828A>
NSUInteger poolSizeAfter = [self.client connectionPoolCountForKey:poolKey];
XCTAssertEqual(poolSizeAfter, 1,
@"Pool should have only 1 connection (new one after bulk pruning)");
}
// S.4 霑<>悄蜷取ア<E58F96>迥カ諤<EFBDB6>ェ瑚ッ?
- (void)testIdleTimeout_PoolStateAfterExpiry_DirectVerification {
[self.client resetPoolStatistics];
NSString *poolKey = @"127.0.0.1:11080:tcp";
// 蛻帛サコ霑樊磁
NSError *error1 = nil;
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"CreateConnection"
timeout:15.0
error:&error1];
XCTAssertNotNil(response1);
[NSThread sleepForTimeInterval:0.5];
// 鬪瑚ッ∬ソ樊磁蝨ィ豎<EFBDA8>荳?
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey], 1,
@"Pool should have 1 connection");
// 隶セ鄂ョ霑樊磁荳コ霑<EFBDBA><E99C91>?
NSArray<HttpdnsNWReusableConnection *> *connections = [self.client connectionsInPoolForKey:poolKey];
HttpdnsNWReusableConnection *conn = connections.firstObject;
[conn debugSetLastUsedDate:[NSDate dateWithTimeIntervalSinceNow:-31.0]];
// 蜿題オキ隸キ豎ゑシ郁ァヲ蜿第ク<E7ACAC><EFBFBD><E7828A>
NSError *error2 = nil;
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"TriggerPrune"
timeout:15.0
error:&error2];
XCTAssertNotNil(response2);
[NSThread sleepForTimeInterval:0.5];
// 逶エ謗・鬪瑚ッ∵ア<E288B5>迥カ諤<EFBDB6>シ夊ソ<E5A48A>悄霑樊磁蟾イ陲ォ遘サ髯、<E9ABAF>梧眠霑樊磁蟾イ蜉<EFBDB2>蜈?
NSUInteger poolSizeAfter = [self.client connectionPoolCountForKey:poolKey];
XCTAssertEqual(poolSizeAfter, 1,
@"Pool should have 1 connection (expired removed, new added)");
// 鬪瑚ッ∫サ溯ョ。<EFBDAE><EFBFBD>蟒コ莠<EFBDBA>眠霑樊磁<E6A88A>域立霑樊磁霑<E7A381>悄荳榊庄螟咲畑<E592B2><E79591>
XCTAssertGreaterThanOrEqual(self.client.connectionCreationCount, 1,
@"Should have created at least 1 new connection");
}
// S.5 蠢ォ騾溯ソ<E6BAAF>悄豬玖ッ包シ域裏髴€遲牙セ<E78999>シ? 貍皮、コ譛€菴ウ螳櫁キ?
- (void)testIdleTimeout_FastExpiry_NoWaiting {
[self.client resetPoolStatistics];
NSString *poolKey = @"127.0.0.1:11080:tcp";
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
// 隨ャ荳€荳ェ隸キ豎ゑシ壼<EFBDBC>蟒コ霑樊磁
NSError *error1 = nil;
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"FastTest1"
timeout:15.0
error:&error1];
XCTAssertNotNil(response1);
XCTAssertEqual(response1.statusCode, 200);
XCTAssertEqual(self.client.connectionCreationCount, 1, @"Should create 1 connection");
[NSThread sleepForTimeInterval:0.5];
// 菴ソ逕ィ DEBUG 霎<>勧蜃ス謨ー讓。諡<EFBDA1> 31 遘定ソ<E5AE9A><EFBFBD>域裏髴€螳樣刔遲牙セ<E78999>シ?
NSArray<HttpdnsNWReusableConnection *> *connections = [self.client connectionsInPoolForKey:poolKey];
XCTAssertEqual(connections.count, 1);
HttpdnsNWReusableConnection *conn = connections.firstObject;
NSDate *expiredDate = [NSDate dateWithTimeIntervalSinceNow:-31.0];
[conn debugSetLastUsedDate:expiredDate];
// 隨ャ莠御クェ隸キ豎ゑシ壼コ碑ッ・譽€豬句芦霑<E88AA6>悄蟷カ蛻帛サコ譁ー霑樊磁
[self.client resetPoolStatistics];
NSError *error2 = nil;
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"FastTest2"
timeout:15.0
error:&error2];
XCTAssertNotNil(response2);
XCTAssertEqual(response2.statusCode, 200);
// 鬪瑚ッ<E7919A>シ壼<EFBDBC>蟒コ莠<EFBDBA>眠霑樊磁<E6A88A>€碁撼螟咲畑霑<E79591>悄逧<E68284><EFBFBD>
XCTAssertEqual(self.client.connectionCreationCount, 1,
@"Should create new connection (expired connection not reused)");
XCTAssertEqual(self.client.connectionReuseCount, 0,
@"Should not reuse expired connection");
CFAbsoluteTime elapsed = CFAbsoluteTimeGetCurrent() - startTime;
// 蜈ウ髞ョ鬪瑚ッ<E7919A>シ壽オ玖ッ募コ碑ッ・蠕亥ソォ螳梧<E89EB3><E6A2A7><EFBFBD>< 5 遘抵シ会シ瑚€碁撼遲牙セ<E78999> 30+ 遘?
XCTAssertLessThan(elapsed, 5.0,
@"Fast expiry test should complete quickly (%.1fs) without 30s wait", elapsed);
}
@end