Files
waf-platform/EdgeHttpDNS/sdk/ios/AlicloudHttpDNSTests/Network/HttpdnsNWHTTPClient_BasicIntegrationTests.m
2026-02-28 18:55:33 +08:00

407 lines
19 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_BasicIntegrationTests.m
// TrustHttpDNSTests
//
// @author Created by Claude Code on 2025-11-01
// Copyright © 2025 trustapp.com. All rights reserved.
//
// åŸºç¡€é†æˆ<C3A6>æµè¯• - 包å<E280A6>«åŸºç¡€åŠŸèƒ½ (G) åŒè¿žæŽ¥å¤<C3A5>ç”?(J) 测试ç»?
// 测试总数�2 个(G:7 + J:5�
//
#import "HttpdnsNWHTTPClientTestBase.h"
@interface HttpdnsNWHTTPClient_BasicIntegrationTests : HttpdnsNWHTTPClientTestBase
@end
@implementation HttpdnsNWHTTPClient_BasicIntegrationTests
#pragma mark - G. 醿ˆ<C3A6>æµè¯•(真实ç½ç»œï¼‰
// G.1 HTTP GET 请求
- (void)testIntegration_HTTPGetRequest_RealNetwork {
XCTestExpectation *expectation = [self expectationWithDescription:@"HTTP GET request"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
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, @"Response should not be nil");
XCTAssertNil(error, @"Error should be nil, got: %@", error);
XCTAssertEqual(response.statusCode, 200, @"Status code should be 200");
XCTAssertNotNil(response.body, @"Body should not be nil");
XCTAssertGreaterThan(response.body.length, 0, @"Body should not be empty");
// 验è¯<C3A8>å“<C3A5>应包å<E280A6>« JSON
NSError *jsonError = nil;
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:response.body
options:0
error:&jsonError];
XCTAssertNotNil(jsonDict, @"Response should be valid JSON");
XCTAssertNil(jsonError, @"JSON parsing should succeed");
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:20.0];
}
// G.2 HTTPS GET 请求
- (void)testIntegration_HTTPSGetRequest_RealNetwork {
XCTestExpectation *expectation = [self expectationWithDescription:@"HTTPS GET request"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
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, @"Response should not be nil");
XCTAssertNil(error, @"Error should be nil, got: %@", error);
XCTAssertEqual(response.statusCode, 200, @"Status code should be 200");
XCTAssertNotNil(response.body, @"Body should not be nil");
// 验è¯<C3A8> TLS æˆ<C3A6>功建ç«
XCTAssertGreaterThan(response.body.length, 0, @"HTTPS body should not be empty");
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:20.0];
}
// G.3 HTTP 404 å“<C3A5>应
- (void)testIntegration_NotFound_Returns404 {
XCTestExpectation *expectation = [self expectationWithDescription:@"404 response"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/status/404"
userAgent:@"HttpdnsNWHTTPClient/1.0"
timeout:15.0
error:&error];
XCTAssertNotNil(response, @"Response should not be nil even for 404");
XCTAssertNil(error, @"Error should be nil for valid HTTP response");
XCTAssertEqual(response.statusCode, 404, @"Status code should be 404");
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:20.0];
}
// G.4 连接å¤<C3A5>用æµè¯•
- (void)testIntegration_ConnectionReuse_MultipleRequests {
XCTestExpectation *expectation = [self expectationWithDescription:@"Connection reuse"];
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:@"HttpdnsNWHTTPClient/1.0"
timeout:15.0
error:&error1];
XCTAssertNotNil(response1, @"First response should not be nil");
XCTAssertNil(error1, @"First request should succeed");
XCTAssertEqual(response1.statusCode, 200);
// ç«å<E280B9>³å<C2B3>起第二个请æ±ï¼Œåº”该å¤<C3A5>用连接
NSError *error2 = nil;
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"HttpdnsNWHTTPClient/1.0"
timeout:15.0
error:&error2];
XCTAssertNotNil(response2, @"Second response should not be nil");
XCTAssertNil(error2, @"Second request should succeed");
XCTAssertEqual(response2.statusCode, 200);
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:30.0];
}
// G.5 Chunked å“<C3A5>应处ç<E2809E>
- (void)testIntegration_ChunkedResponse_RealNetwork {
XCTestExpectation *expectation = [self expectationWithDescription:@"Chunked response"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError *error = nil;
// httpbin.org/stream-bytes 返回 chunked ç¼ç <C3A7>çš„å“<C3A5>åº?
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/stream-bytes/1024"
userAgent:@"HttpdnsNWHTTPClient/1.0"
timeout:15.0
error:&error];
XCTAssertNotNil(response, @"Response should not be nil");
XCTAssertNil(error, @"Error should be nil, got: %@", error);
XCTAssertEqual(response.statusCode, 200);
XCTAssertEqual(response.body.length, 1024, @"Should receive exactly 1024 bytes");
// 验è¯<C3A8> Transfer-Encoding å¤?
NSString *transferEncoding = response.headers[@"transfer-encoding"];
if (transferEncoding) {
XCTAssertTrue([transferEncoding containsString:@"chunked"], @"Should use chunked encoding");
}
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:20.0];
}
#pragma mark - é¢<C3A9>å¤çš„醿ˆ<C3A6>æµè¯?
// G.6 è¶…æ—¶æµè¯•(å<CB86>¯é€‰ï¼‰
- (void)testIntegration_RequestTimeout_ReturnsError {
XCTestExpectation *expectation = [self expectationWithDescription:@"Request timeout"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError *error = nil;
// httpbin.org/delay/10 会延è¿?10 ç§å“<C3A5>应,æˆä»¬è®¾ç½® 2 ç§’è¶…æ—?
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/delay/10"
userAgent:@"HttpdnsNWHTTPClient/1.0"
timeout:2.0
error:&error];
XCTAssertNil(response, @"Response should be nil on timeout");
XCTAssertNotNil(error, @"Error should be set on timeout");
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:5.0];
}
// G.7 多个ä¸<C3A4>å<EFBFBD>Œå¤´éƒ¨çš„请æ±?
- (void)testIntegration_CustomHeaders_Reflected {
XCTestExpectation *expectation = [self expectationWithDescription:@"Custom headers"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/headers"
userAgent:@"TestUserAgent/1.0"
timeout:15.0
error:&error];
XCTAssertNotNil(response);
XCTAssertEqual(response.statusCode, 200);
// è§£æž<C3A6> JSON å“<C3A5>应,验è¯<C3A8>æˆä»¬çš„ User-Agent 被å<C2AB>é€?
NSError *jsonError = nil;
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:response.body
options:0
error:&jsonError];
XCTAssertNotNil(jsonDict);
NSDictionary *headers = jsonDict[@"headers"];
XCTAssertTrue([headers[@"User-Agent"] containsString:@"TestUserAgent"], @"User-Agent should be sent");
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:20.0];
}
#pragma mark - J. 连接å¤<C3A5>用详细æµè¯•
// J.1 连接过期测试ï¼?1ç§å<E28099>Žåˆå»ºæ°è¿žæŽ¥ï¼‰
- (void)testConnectionReuse_Expiry31Seconds_NewConnectionCreated {
if (getenv("SKIP_SLOW_TESTS")) {
return;
}
XCTestExpectation *expectation = [self expectationWithDescription:@"Connection expiry"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
CFAbsoluteTime time1 = CFAbsoluteTimeGetCurrent();
NSError *error1 = nil;
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"First"
timeout:15.0
error:&error1];
CFAbsoluteTime elapsed1 = CFAbsoluteTimeGetCurrent() - time1;
XCTAssertTrue(response1 != nil || error1 != nil);
// 等待31ç§è®©è¿žæŽ¥è¿‡æœŸ
[NSThread sleepForTimeInterval:31.0];
// 第二个请æ±åº”该åˆå»ºæ°è¿žæŽ¥ï¼ˆå<CB86>¯èƒ½ç¨<C3A7>慢,å ä¸ºéœ€è¦<C3A8>建ç«è¿žæŽ¥ï¼‰
CFAbsoluteTime time2 = CFAbsoluteTimeGetCurrent();
NSError *error2 = nil;
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"Second"
timeout:15.0
error:&error2];
CFAbsoluteTime elapsed2 = CFAbsoluteTimeGetCurrent() - time2;
XCTAssertTrue(response2 != nil || error2 != nil);
// 注æ„<C3A6>:由于ç½ç»œæ³¢åŠ¨ï¼Œä¸<C3A4>能严格比较时间
// å<>ªéªŒè¯<C3A8>请æ±éƒ½æˆ<C3A6>功å<C5B8>³å<C2B3>¯
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:70.0];
}
// J.2 连接池容é‡<C3A9>é™<C3A9>制验è¯?
- (void)testConnectionReuse_TenRequests_OnlyFourConnectionsKept {
XCTestExpectation *expectation = [self expectationWithDescription:@"Pool size limit"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 连续10个请�
for (NSInteger i = 0; i < 10; i++) {
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"PoolSizeTest"
timeout:15.0
error:&error];
XCTAssertTrue(response != nil || error != nil);
}
// 等待所有连接归�
[NSThread sleepForTimeInterval:1.0];
// æ— æ³•ç´æŽ¥éªŒè¯<C3A8>池大å°<C3A5>ï¼Œä½†å¦æžœå®žçŽ°æ­£ç¡®ï¼Œæ± åº”è‡ªåŠ¨é™<C3A9>åˆ
// å<>Žç»­è¯·æ±åº”该ä»<C3A4>能正常工作
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"Verification"
timeout:15.0
error:&error];
XCTAssertTrue(response != nil || error != nil);
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:120.0];
}
// J.3 ä¸<C3A4>å<EFBFBD>Œè·¯å¾„å¤<C3A5>用连接
- (void)testConnectionReuse_DifferentPaths_SameConnection {
XCTestExpectation *expectation = [self expectationWithDescription:@"Different paths"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray<NSString *> *paths = @[@"/get", @"/headers", @"/user-agent", @"/uuid"];
NSMutableArray<NSNumber *> *times = [NSMutableArray array];
for (NSString *path in paths) {
CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
NSString *urlString = [NSString stringWithFormat:@"http://127.0.0.1:11080%@", path];
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:urlString
userAgent:@"PathTest"
timeout:15.0
error:&error];
CFAbsoluteTime elapsed = CFAbsoluteTimeGetCurrent() - start;
XCTAssertTrue(response != nil || error != nil);
[times addObject:@(elapsed)];
}
// 妿žœè¿žæŽ¥å¤<C3A5>用工作正常,å<C592>Žç»­è¯·æ±åº”该æ´å¿«ï¼ˆä½†ç½ç»œæ³¢åЍå<C2A8>¯èƒ½å½±å“<C3A5>)
// 至å°éªŒè¯<C3A8>所有请æ±éƒ½æˆ<C3A6>功
XCTAssertEqual(times.count, paths.count);
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:60.0];
}
// J.4 HTTP vs HTTPS 使用ä¸<C3A4>å<EFBFBD>Œè¿žæŽ¥
- (void)testConnectionReuse_HTTPvsHTTPS_DifferentPoolKeys {
XCTestExpectation *expectation = [self expectationWithDescription:@"HTTP vs HTTPS"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// HTTP 请求
NSError *httpError = nil;
HttpdnsNWHTTPClientResponse *httpResponse = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"HTTP"
timeout:15.0
error:&httpError];
XCTAssertTrue(httpResponse != nil || httpError != nil);
// HTTPS 请æ±ï¼ˆåº”该使用ä¸<C3A4>å<EFBFBD>Œçš„连接æ±?keyï¼?
NSError *httpsError = nil;
HttpdnsNWHTTPClientResponse *httpsResponse = [self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
userAgent:@"HTTPS"
timeout:15.0
error:&httpsError];
XCTAssertTrue(httpsResponse != nil || httpsError != nil);
// 两者都应该æˆ<C3A6>功,且ä¸<C3A4>会ç¸äºå¹²æ‰°
[expectation fulfill];
});
[self waitForExpectations:@[expectation] timeout:35.0];
}
// J.5 长连接ä¿<C3A4>æŒ<C3A6>æµè¯?
- (void)testConnectionReuse_TwentyRequestsOneSecondApart_ConnectionKeptAlive {
if (getenv("SKIP_SLOW_TESTS")) {
return;
}
XCTestExpectation *expectation = [self expectationWithDescription:@"Keep-alive"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSInteger successCount = 0;
NSMutableArray<NSNumber *> *requestTimes = [NSMutableArray array];
// 20个请æ±ï¼Œé—´éš”1ç§ï¼ˆç¬¬ä¸€ä¸ªè¯·æ±ç«å<E280B9>³æ‰§è¡Œï¼‰
for (NSInteger i = 0; i < 20; i++) {
// 除第一个请æ±å¤ï¼Œæ¯<C3A6>次请æ±å‰<C3A5>等待1ç§?
if (i > 0) {
[NSThread sleepForTimeInterval:1.0];
}
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
NSError *error = nil;
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
userAgent:@"KeepAlive"
timeout:10.0
error:&error];
CFAbsoluteTime elapsed = CFAbsoluteTimeGetCurrent() - startTime;
[requestTimes addObject:@(elapsed)];
if (response && (response.statusCode == 200 || response.statusCode == 503)) {
successCount++;
} else {
// 妿žœè¯·æ±å¤±è´¥ï¼Œæ<C592><C3A6>å‰<C3A5>退出以é<C2A5>¿å…<C3A5>è¶…æ—¶
break;
}
}
// 至å°å¤§éƒ¨åˆ†è¯·æ±åº”该æˆ<C3A6>åŠ?
XCTAssertGreaterThan(successCount, 15, @"Most requests should succeed with connection reuse");
// 验è¯<C3A8>连接å¤<C3A5>用:å<C5A1>Žç»­è¯·æ±åº”该æ´å¿«ï¼ˆå¦æžœä½¿ç”¨äº†keep-aliveï¼?
if (requestTimes.count >= 10) {
double firstRequestTime = [requestTimes[0] doubleValue];
double laterAvgTime = 0;
for (NSInteger i = 5; i < MIN(10, requestTimes.count); i++) {
laterAvgTime += [requestTimes[i] doubleValue];
}
laterAvgTime /= MIN(5, requestTimes.count - 5);
// å<>Žç»­è¯·æ±åº”该ä¸<C3A4>ä¼šæ˜Žæ˜¾æ´æ…¢ï¼ˆè¯´æ˜Žè¿žæŽ¥å¤<C3A5>用工作正常)
XCTAssertLessThanOrEqual(laterAvgTime, firstRequestTime * 2.0, @"Connection reuse should keep latency reasonable");
}
[expectation fulfill];
});
// 超时计算: 19ç§sleep + 20个请æ±Ã—~2ç§?= 59ç§ï¼Œè®¾ç½®50ç§ï¼ˆæ<CB86><C3A6>å‰<C3A5>退出机制ä¿<C3A4>è¯<C3A8>效率)
[self waitForExpectations:@[expectation] timeout:50.0];
}
@end