256 lines
11 KiB
Objective-C
256 lines
11 KiB
Objective-C
//
|
|
// DemoHttpdnsScenario.m
|
|
// NewHttpDNSTestDemo
|
|
//
|
|
// @author Created by Claude Code on 2025-10-23
|
|
//
|
|
|
|
#import "DemoHttpdnsScenario.h"
|
|
#import "DemoConfigLoader.h"
|
|
|
|
@interface DemoHttpdnsScenarioConfig ()
|
|
@end
|
|
|
|
@implementation DemoHttpdnsScenarioConfig
|
|
|
|
- (instancetype)init {
|
|
if (self = [super init]) {
|
|
NSString *serviceDomain = [DemoConfigLoader shared].serviceDomain;
|
|
_host = serviceDomain.length > 0 ? serviceDomain : @"www.new.com";
|
|
_ipType = HttpdnsQueryIPTypeBoth;
|
|
// _httpsEnabled = YES;
|
|
// _persistentCacheEnabled = YES;
|
|
// _reuseExpiredIPEnabled = YES;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (id)copyWithZone:(NSZone *)zone {
|
|
DemoHttpdnsScenarioConfig *cfg = [[[self class] allocWithZone:zone] init];
|
|
cfg.host = self.host;
|
|
cfg.ipType = self.ipType;
|
|
// cfg.httpsEnabled = self.httpsEnabled;
|
|
// cfg.persistentCacheEnabled = self.persistentCacheEnabled;
|
|
// cfg.reuseExpiredIPEnabled = self.reuseExpiredIPEnabled;
|
|
return cfg;
|
|
}
|
|
|
|
@end
|
|
|
|
@interface DemoHttpdnsScenario ()
|
|
|
|
@property (nonatomic, strong) HttpdnsEdgeService *service;
|
|
@property (nonatomic, strong) DemoHttpdnsScenarioConfig *config;
|
|
@property (nonatomic, strong) NSMutableString *logBuffer;
|
|
@property (nonatomic, strong) dispatch_queue_t logQueue;
|
|
|
|
@end
|
|
|
|
@implementation DemoHttpdnsScenario
|
|
|
|
- (instancetype)initWithDelegate:(id<DemoHttpdnsScenarioDelegate>)delegate {
|
|
if (self = [super init]) {
|
|
_delegate = delegate;
|
|
_model = [[DemoResolveModel alloc] init];
|
|
_config = [[DemoHttpdnsScenarioConfig alloc] init];
|
|
_logBuffer = [NSMutableString string];
|
|
_logQueue = dispatch_queue_create("com.new.httpdns.demo.log", DISPATCH_QUEUE_SERIAL);
|
|
[self buildService];
|
|
[self applyConfig:_config];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)buildService {
|
|
DemoConfigLoader *cfg = [DemoConfigLoader shared];
|
|
if (cfg.hasValidConfig) {
|
|
self.service = [[HttpdnsEdgeService alloc] initWithAppId:cfg.appId apiUrl:cfg.apiUrl signSecret:cfg.signSecret];
|
|
[self log:[NSString stringWithFormat:@"Init HttpdnsEdgeService success!"]];
|
|
[self log:[NSString stringWithFormat:@"== Config details =="]];
|
|
[self log:[NSString stringWithFormat:@"appId: %@", cfg.appId]];
|
|
[self log:[NSString stringWithFormat:@"apiUrl: %@", cfg.apiUrl]];
|
|
[self log:[NSString stringWithFormat:@"serviceDomain: %@", cfg.serviceDomain]];
|
|
[self log:[NSString stringWithFormat:@"signSecret length: %tu", cfg.signSecret.length]];
|
|
} else {
|
|
[self log:@"Init HttpdnsEdgeService failed! Missing required fields."];
|
|
}
|
|
}
|
|
|
|
- (void)applyConfig:(DemoHttpdnsScenarioConfig *)config {
|
|
self.config = [config copy];
|
|
self.model.host = self.config.host;
|
|
self.model.ipType = self.config.ipType;
|
|
[self log:[NSString stringWithFormat:@"Apply UI config: host=%@, ipType=%ld", self.config.host, (long)self.config.ipType]];
|
|
}
|
|
|
|
- (void)resolve {
|
|
NSString *queryHost = [self currentHost];
|
|
HttpdnsQueryIPType ipType = self.config.ipType;
|
|
NSTimeInterval startMs = [[NSDate date] timeIntervalSince1970] * 1000.0;
|
|
|
|
[self log:[NSString stringWithFormat:@"--- Start Resolve Request ---"]];
|
|
[self log:[NSString stringWithFormat:@"Target Host: %@", queryHost]];
|
|
|
|
if (ipType == HttpdnsQueryIPTypeBoth) {
|
|
[self log:@"Query Type: BOTH (mapped to concurrent A & AAAA)"];
|
|
dispatch_group_t group = dispatch_group_create();
|
|
|
|
__block HttpdnsEdgeResolveResult *resA = nil;
|
|
__block NSError *errA = nil;
|
|
__block HttpdnsEdgeResolveResult *resAAAA = nil;
|
|
__block NSError *errAAAA = nil;
|
|
|
|
dispatch_group_enter(group);
|
|
[self.service resolveHost:queryHost queryType:@"A" completion:^(HttpdnsEdgeResolveResult * _Nullable result, NSError * _Nullable error) {
|
|
resA = result;
|
|
errA = error;
|
|
dispatch_group_leave(group);
|
|
}];
|
|
|
|
dispatch_group_enter(group);
|
|
[self.service resolveHost:queryHost queryType:@"AAAA" completion:^(HttpdnsEdgeResolveResult * _Nullable result, NSError * _Nullable error) {
|
|
resAAAA = result;
|
|
errAAAA = error;
|
|
dispatch_group_leave(group);
|
|
}];
|
|
|
|
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
|
|
[self log:@"--- Edge Service Callback Triggered (BOTH) ---"];
|
|
HttpdnsEdgeResolveResult *merged = [[HttpdnsEdgeResolveResult alloc] init];
|
|
merged.ipv4s = resA ? resA.ipv4s : @[];
|
|
merged.ipv6s = resAAAA ? resAAAA.ipv6s : @[];
|
|
merged.ttl = 0;
|
|
if (resA && resAAAA) {
|
|
merged.ttl = MIN(resA.ttl, resAAAA.ttl);
|
|
} else if (resA) {
|
|
merged.ttl = resA.ttl;
|
|
} else if (resAAAA) {
|
|
merged.ttl = resAAAA.ttl;
|
|
}
|
|
if (resA.requestId) merged.requestId = resA.requestId;
|
|
else if (resAAAA.requestId) merged.requestId = resAAAA.requestId;
|
|
|
|
NSError *finalErr = nil;
|
|
if (errA && errAAAA) {
|
|
finalErr = errA;
|
|
}
|
|
|
|
[self handleEdgeResult:merged error:finalErr host:queryHost ipType:ipType start:startMs];
|
|
});
|
|
|
|
} else {
|
|
NSString *qtype = (ipType == HttpdnsQueryIPTypeIpv6) ? @"AAAA" : @"A";
|
|
[self log:[NSString stringWithFormat:@"Query Type: %@", qtype]];
|
|
[self.service resolveHost:queryHost queryType:qtype completion:^(HttpdnsEdgeResolveResult * _Nullable result, NSError * _Nullable error) {
|
|
[self log:[NSString stringWithFormat:@"--- Edge Service Callback Triggered ---"]];
|
|
[self handleEdgeResult:result error:error host:queryHost ipType:ipType start:startMs];
|
|
}];
|
|
}
|
|
}
|
|
|
|
- (NSString *)logSnapshot {
|
|
__block NSString *snapshot = @"";
|
|
dispatch_sync(self.logQueue, ^{
|
|
snapshot = [self.logBuffer copy];
|
|
});
|
|
return snapshot;
|
|
}
|
|
|
|
- (NSString *)currentHost {
|
|
return self.config.host.length > 0 ? self.config.host : @"demo.cloudxdr.com";
|
|
}
|
|
|
|
- (void)handleEdgeResult:(HttpdnsEdgeResolveResult *)result error:(NSError *)error host:(NSString *)host ipType:(HttpdnsQueryIPType)ipType start:(NSTimeInterval)startMs {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
self.model.host = host;
|
|
self.model.ipType = ipType;
|
|
|
|
NSTimeInterval endMs = [[NSDate date] timeIntervalSince1970] * 1000.0;
|
|
self.model.elapsedMs = endMs - startMs;
|
|
|
|
if (error) {
|
|
self.model.ipv4s = @[];
|
|
self.model.ipv6s = @[];
|
|
self.model.ttlV4 = 0;
|
|
self.model.ttlV6 = 0;
|
|
[self log:[NSString stringWithFormat:@"Edge Resolve Failed!"]];
|
|
[self log:[NSString stringWithFormat:@"Error domain: %@", error.domain]];
|
|
[self log:[NSString stringWithFormat:@"Error code: %ld", (long)error.code]];
|
|
[self log:[NSString stringWithFormat:@"Error description: %@", error.localizedDescription]];
|
|
if (error.userInfo) {
|
|
[self log:[NSString stringWithFormat:@"Error user info: %@", error.userInfo]];
|
|
}
|
|
} else {
|
|
self.model.ipv4s = result.ipv4s ?: @[];
|
|
self.model.ipv6s = result.ipv6s ?: @[];
|
|
self.model.ttlV4 = result.ttl;
|
|
self.model.ttlV6 = result.ttl;
|
|
self.model.businessRequestResult = @"Waiting for request...";
|
|
[self log:[NSString stringWithFormat:@"Edge Resolve Success!"]];
|
|
[self log:[NSString stringWithFormat:@"Host: %@", host]];
|
|
[self log:[NSString stringWithFormat:@"IPv4s: %@", result.ipv4s]];
|
|
[self log:[NSString stringWithFormat:@"IPv6s: %@", result.ipv6s]];
|
|
[self log:[NSString stringWithFormat:@"TTL: %ld", (long)result.ttl]];
|
|
[self log:[NSString stringWithFormat:@"Request ID: %@", result.requestId]];
|
|
|
|
// Fire off a business request to demonstrate real usage if we have an IP
|
|
if (result.ipv4s.count > 0 || result.ipv6s.count > 0) {
|
|
[self log:@"\n--- Start Business Request Demo ---"];
|
|
// Construct a URL for the business domain
|
|
NSURL *businessURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://%@", host]];
|
|
[self log:[NSString stringWithFormat:@"Requesting URL: %@", businessURL.absoluteString]];
|
|
|
|
[self.service requestURL:businessURL method:@"GET" headers:nil body:nil completion:^(NSData * _Nullable data, NSHTTPURLResponse * _Nullable response, NSError * _Nullable reqError) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if (reqError) {
|
|
NSString *err = [NSString stringWithFormat:@"Failed: %@", reqError.localizedDescription];
|
|
[self log:[NSString stringWithFormat:@"Business Request %@", err]];
|
|
self.model.businessRequestResult = err;
|
|
} else {
|
|
[self log:[NSString stringWithFormat:@"Business Request Success! Status Code: %ld", (long)response.statusCode]];
|
|
NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
if (responseString.length > 200) {
|
|
responseString = [[responseString substringToIndex:200] stringByAppendingString:@"... (truncated)"];
|
|
}
|
|
[self log:[NSString stringWithFormat:@"Response Body:\n%@", responseString]];
|
|
self.model.businessRequestResult = [NSString stringWithFormat:@"HTTP %ld\n%@", (long)response.statusCode, responseString];
|
|
}
|
|
|
|
id<DemoHttpdnsScenarioDelegate> bDelegate = self.delegate;
|
|
if (bDelegate != nil) {
|
|
[bDelegate scenario:self didUpdateModel:self.model];
|
|
}
|
|
});
|
|
}];
|
|
}
|
|
}
|
|
|
|
id<DemoHttpdnsScenarioDelegate> delegate = self.delegate;
|
|
if (delegate != nil) {
|
|
[delegate scenario:self didUpdateModel:self.model];
|
|
}
|
|
});
|
|
}
|
|
|
|
// logger & ttl delegates removed since EdgeService lacks these protocol hooks in this SDK version
|
|
|
|
- (void)log:(NSString *)logStr {
|
|
if (logStr.length == 0) {
|
|
return;
|
|
}
|
|
NSString *line = [NSString stringWithFormat:@"%@ %@\n", [NSDate date], logStr];
|
|
dispatch_async(self.logQueue, ^{
|
|
[self.logBuffer appendString:line];
|
|
id<DemoHttpdnsScenarioDelegate> delegate = self.delegate;
|
|
if (delegate != nil) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[delegate scenario:self didAppendLogLine:line];
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
|
|
@end
|