From ee244a653749a584e9fcfbfa7ccbbd5d5821a2b1 Mon Sep 17 00:00:00 2001 From: Tomas Camin Date: Fri, 23 Feb 2024 08:58:35 +0100 Subject: [PATCH 1/6] Rework XcodeGen specification --- Example/SPM/project.yml | 6 ++-- Example/project.yml | 73 +++++++++++++++++++++++++++++++++-------- Scripts/build_lib.rb | 4 +-- 3 files changed, 65 insertions(+), 18 deletions(-) diff --git a/Example/SPM/project.yml b/Example/SPM/project.yml index 896e078f..cc0eac48 100644 --- a/Example/SPM/project.yml +++ b/Example/SPM/project.yml @@ -21,7 +21,7 @@ targets: settings: base: INFOPLIST_FILE: "../SBTUITestTunnel/SBTUITestTunnel-Info.plist" - CODE_SIGN_ENTITLEMENTS: "../SBTUITestTunnel_Example.entitlements" + CODE_SIGN_ENTITLEMENTS: "../SBTUITestTunnel_Example.entitlements" UITests: type: bundle.ui-testing platform: iOS @@ -33,7 +33,7 @@ targets: INFOPLIST_FILE: "../SBTUITestTunnel_Tests/Info.plist" schemes: - SBTUITestTunnel_Example: + SBTUITestTunnel: build: targets: App: [all] @@ -51,7 +51,9 @@ schemes: UITests: [all] run: executable: App + debugEnabled: false buildConfiguration: Debug test: targets: - UITests + debugEnabled: false diff --git a/Example/project.yml b/Example/project.yml index 29198e66..f6b89806 100644 --- a/Example/project.yml +++ b/Example/project.yml @@ -9,7 +9,6 @@ fileGroups: - SBTUITestTunnel_Example.entitlements targets: - SBTUITestTunnel_Example: type: application platform: iOS @@ -25,12 +24,6 @@ targets: - sdk: UIKit.framework - sdk: Foundation.framework - sdk: CoreGraphics.framework - scheme: - gatherCoverageData: true - testTargets: - - SBTUITestTunnel_Tests - coverageTargets: - - SBTUITestTunnel_Example SBTUITestTunnel_Tests: type: bundle.ui-testing @@ -43,9 +36,6 @@ targets: - SBTUITestTunnel_Tests dependencies: - target: SBTUITestTunnel_Example - scheme: - testTargets: - - SBTUITestTunnel_Tests SBTUITestTunnel_TestsNoSwizzling: type: bundle.ui-testing @@ -53,12 +43,67 @@ targets: deploymentTarget: "12.2" settings: base: - GCC_PREPROCESSOR_DEFINITIONS: ["DISABLE_UITUNNEL_SWIZZLING=1", "$(inherited)"] + GCC_PREPROCESSOR_DEFINITIONS: + ["DISABLE_UITUNNEL_SWIZZLING=1", "$(inherited)"] INFOPLIST_FILE: "SBTUITestTunnel_TestsNoSwizzling/Info.plist" sources: - SBTUITestTunnel_TestsNoSwizzling dependencies: - target: SBTUITestTunnel_Example - scheme: - testTargets: - - SBTUITestTunnel_TestsNoSwizzling \ No newline at end of file + +schemes: + SBTUITestTunnel: + build: + targets: + SBTUITestTunnel_Example: [run, test, profile, analyze, archive] + config: Debug + profile: + config: Debug + test: + targets: + - SBTUITestTunnel_Tests + config: Debug + gatherCoverageData: true + disableMainThreadChecker: true + language: en + region: EN + + SBTUITestTunnel_Tests: + run: + debugEnabled: false + build: + targets: + SBTUITestTunnel_Example: [run, test, profile, analyze, archive] + config: Debug + profile: + config: Debug + test: + targets: + - SBTUITestTunnel_Tests + config: Debug + gatherCoverageData: true + disableMainThreadChecker: true + language: en + region: EN + management: + isShown: false + + SBTUITestTunnel_NoSwizzlingTests: + run: + debugEnabled: false + build: + targets: + SBTUITestTunnel_Example: [run, test, profile, analyze, archive] + config: Debug + profile: + config: Debug + test: + targets: + - SBTUITestTunnel_TestsNoSwizzling + config: Debug + gatherCoverageData: true + disableMainThreadChecker: true + language: en + region: EN + management: + isShown: false diff --git a/Scripts/build_lib.rb b/Scripts/build_lib.rb index fc049bd5..8f4bd0e7 100755 --- a/Scripts/build_lib.rb +++ b/Scripts/build_lib.rb @@ -2,9 +2,9 @@ require "fileutils" module Build - EXAMPLE_APP_SCHEME = "SBTUITestTunnel_Example" + EXAMPLE_APP_SCHEME = "SBTUITestTunnel" UITESTS_SCHEME = "SBTUITestTunnel_Tests" - UITESTS_NOSWIZZ_SCHEME = "SBTUITestTunnel_TestsNoSwizzling" + UITESTS_NOSWIZZ_SCHEME = "SBTUITestTunnel_NoSwizzlingTests" def self.run_build(project_path) puts "⏳ Building app..." From e924e508c75fb5255f5bc23ad767bf7b60c1ea2f Mon Sep 17 00:00:00 2001 From: Tomas Camin Date: Fri, 23 Feb 2024 08:58:54 +0100 Subject: [PATCH 2/6] Disable debugger This should speedup test execution and reliability --- Example/project.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Example/project.yml b/Example/project.yml index f6b89806..f35f7915 100644 --- a/Example/project.yml +++ b/Example/project.yml @@ -83,6 +83,7 @@ schemes: config: Debug gatherCoverageData: true disableMainThreadChecker: true + debugEnabled: false language: en region: EN management: @@ -103,6 +104,7 @@ schemes: config: Debug gatherCoverageData: true disableMainThreadChecker: true + debugEnabled: false language: en region: EN management: From e5c0f7f4bd343627e55aecc39b76c39b32c8b444 Mon Sep 17 00:00:00 2001 From: Tomas Camin Date: Wed, 21 Feb 2024 09:25:51 +0100 Subject: [PATCH 3/6] Replace URLProtocol property setting --- .../SBTUITestTunnel_Tests/MonitorTests.swift | 36 +++++++++++-- .../NSURLRequest+HTTPBodyFix.m | 17 ++++--- .../SBTRequestPropertyStorage.m | 51 +++++++++++++++++++ .../include/SBTRequestPropertyStorage.h | 22 ++++++++ .../include/SBTUITestTunnelCommon.h | 1 + .../private/NSURLSession+HTTPBodyFix.m | 10 ++-- .../private/SBTProxyURLProtocol.m | 12 ++--- 7 files changed, 127 insertions(+), 22 deletions(-) create mode 100644 Sources/SBTUITestTunnelCommon/SBTRequestPropertyStorage.m create mode 100644 Sources/SBTUITestTunnelCommon/include/SBTRequestPropertyStorage.h diff --git a/Example/SBTUITestTunnel_Tests/MonitorTests.swift b/Example/SBTUITestTunnel_Tests/MonitorTests.swift index 64c93228..6a73894f 100644 --- a/Example/SBTUITestTunnel_Tests/MonitorTests.swift +++ b/Example/SBTUITestTunnel_Tests/MonitorTests.swift @@ -205,11 +205,41 @@ class MonitorTests: XCTestCase { XCTAssert(app.stubRequestsRemoveAll()) XCTAssert(app.monitorRequestRemoveAll()) } - + func testMonitorPostRequestWithHTTPBody() { app.monitorRequests(matching: SBTRequestMatch(url: "httpbin.org", method: "POST")) + + let smallBody = String(repeating: "a", count: 100) + + _ = request.dataTaskNetwork(urlString: "https://httpbin.org/post", httpMethod: "POST", httpBody: smallBody) + let requests = app.monitoredRequestsFlushAll() + XCTAssertEqual(requests.count, 1) + print(requests.map(\.debugDescription)) + + for request in requests { + guard let httpBody = request.request?.httpBody else { + XCTFail("Missing http body") + continue + } + + XCTAssertEqual(String(data: httpBody, encoding: .utf8), smallBody) + + XCTAssert((request.responseString()!).contains("httpbin.org")) + XCTAssert(request.timestamp > 0.0) + XCTAssert(request.requestTime > 0.0) + } + + XCTAssert(app.stubRequestsRemoveAll()) + XCTAssert(app.monitorRequestRemoveAll()) + } + + + func testMonitorPostRequestWithHTTPLargeBody() { + app.monitorRequests(matching: SBTRequestMatch(url: "httpbin.org", method: "POST")) + + let largeBody = String(repeating: "a", count: 20000) - _ = request.dataTaskNetwork(urlString: "https://httpbin.org/post", httpMethod: "POST", httpBody: "¶m5=val5¶m6=val6") + _ = request.dataTaskNetwork(urlString: "https://httpbin.org/post", httpMethod: "POST", httpBody: largeBody) let requests = app.monitoredRequestsFlushAll() XCTAssertEqual(requests.count, 1) print(requests.map(\.debugDescription)) @@ -220,7 +250,7 @@ class MonitorTests: XCTestCase { continue } - XCTAssertEqual(String(data: httpBody, encoding: .utf8), "¶m5=val5¶m6=val6") + XCTAssertEqual(String(data: httpBody, encoding: .utf8), largeBody) XCTAssert((request.responseString()!).contains("httpbin.org")) XCTAssert(request.timestamp > 0.0) diff --git a/Sources/SBTUITestTunnelCommon/NSURLRequest+HTTPBodyFix.m b/Sources/SBTUITestTunnelCommon/NSURLRequest+HTTPBodyFix.m index 1fd6da96..2c880ddb 100644 --- a/Sources/SBTUITestTunnelCommon/NSURLRequest+HTTPBodyFix.m +++ b/Sources/SBTUITestTunnelCommon/NSURLRequest+HTTPBodyFix.m @@ -17,6 +17,7 @@ #import "include/NSURLRequest+HTTPBodyFix.h" #import "include/SBTUITestTunnel.h" #import "include/SBTSwizzleHelpers.h" +#import "include/SBTRequestPropertyStorage.h" @implementation NSURLRequest (HTTPBodyFix) @@ -37,12 +38,12 @@ @implementation NSURLRequest (HTTPBodyFix) - (NSData *)sbt_uploadHTTPBody { - return [NSURLProtocol propertyForKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:self]; + return [SBTRequestPropertyStorage propertyForKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:self]; } - (BOOL)sbt_isUploadTaskRequest { - return ([NSURLProtocol propertyForKey:SBTUITunneledNSURLProtocolIsUploadTaskKey inRequest:self] != nil); + return ([SBTRequestPropertyStorage propertyForKey:SBTUITunneledNSURLProtocolIsUploadTaskKey inRequest:self] != nil); } - (void)sbt_markUploadTaskRequest @@ -50,7 +51,7 @@ - (void)sbt_markUploadTaskRequest NSAssert([self isKindOfClass:[NSMutableURLRequest class]], @"Attempted to mark an immutable request as an upload"); if ([self isKindOfClass:[NSMutableURLRequest class]]) { - [NSURLProtocol setProperty:@YES forKey:SBTUITunneledNSURLProtocolIsUploadTaskKey inRequest:(NSMutableURLRequest *)self]; + [SBTRequestPropertyStorage setProperty:@YES forKey:SBTUITunneledNSURLProtocolIsUploadTaskKey inRequest:(NSMutableURLRequest *)self]; } } @@ -81,7 +82,7 @@ - (NSData *)swz_HTTPBody NSData *ret = [self swz_HTTPBody]; - return ret ?: [NSURLProtocol propertyForKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:self]; + return ret ?: [SBTRequestPropertyStorage propertyForKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:self]; } - (id)swz_copyWithZone:(NSZone *)zone @@ -89,9 +90,9 @@ - (id)swz_copyWithZone:(NSZone *)zone NSURLRequest *ret = [self swz_copyWithZone:zone]; if ([ret isKindOfClass:[NSMutableURLRequest class]]) { - NSData *body = [NSURLProtocol propertyForKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:self]; + NSData *body = [SBTRequestPropertyStorage propertyForKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:self]; if (body) { - [NSURLProtocol setProperty:body forKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:(NSMutableURLRequest *)ret]; + [SBTRequestPropertyStorage setProperty:body forKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:(NSMutableURLRequest *)ret]; } } @@ -103,9 +104,9 @@ - (id)swz_mutableCopyWithZone:(NSZone *)zone NSMutableURLRequest *ret = [self swz_mutableCopyWithZone:zone]; if ([ret isKindOfClass:[NSMutableURLRequest class]]) { - NSData *body = [NSURLProtocol propertyForKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:self]; + NSData *body = [SBTRequestPropertyStorage propertyForKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:self]; if (body) { - [NSURLProtocol setProperty:body forKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:(NSMutableURLRequest *)ret]; + [SBTRequestPropertyStorage setProperty:body forKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:(NSMutableURLRequest *)ret]; } } diff --git a/Sources/SBTUITestTunnelCommon/SBTRequestPropertyStorage.m b/Sources/SBTUITestTunnelCommon/SBTRequestPropertyStorage.m new file mode 100644 index 00000000..167cdd30 --- /dev/null +++ b/Sources/SBTUITestTunnelCommon/SBTRequestPropertyStorage.m @@ -0,0 +1,51 @@ +// +// SBTRequestProperty.m +// SBTUITestTunnelCommon +// +// Created by tomas on 20/02/24. +// + +#import "include/SBTRequestPropertyStorage.h" + +@implementation SBTRequestPropertyStorage + +static NSMutableDictionary *storage; +static dispatch_queue_t queue; + ++ (void)initialize +{ + if (self == [SBTRequestPropertyStorage class]) { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + storage = [[NSMutableDictionary alloc] init]; + queue = dispatch_queue_create("com.subito.sbtuitesttunnel.storage.queue", DISPATCH_QUEUE_SERIAL); + }); + } +} + ++ (void)setProperty:(id)property forKey:(nonnull NSString *)key inRequest:(nonnull NSMutableURLRequest *)request +{ + if ([property isKindOfClass:[NSData class]] && ((NSData *)property).length > 16834) { + NSString *uuid = [[NSUUID UUID] UUIDString]; + dispatch_sync(queue, ^{ + [storage setObject:property forKey:uuid]; + [NSURLProtocol setProperty:uuid forKey:key inRequest:request]; + }); + } else { + [NSURLProtocol setProperty:property forKey:key inRequest:request]; + } +} + ++ (id)propertyForKey:(NSString *)key inRequest:(NSURLRequest *)request; +{ + __block id result = nil; + + dispatch_sync(queue, ^{ + id property = [NSURLProtocol propertyForKey:key inRequest:request]; + result = [storage objectForKey:property] ?: property; + }); + + return result; +} + +@end diff --git a/Sources/SBTUITestTunnelCommon/include/SBTRequestPropertyStorage.h b/Sources/SBTUITestTunnelCommon/include/SBTRequestPropertyStorage.h new file mode 100644 index 00000000..e231f36b --- /dev/null +++ b/Sources/SBTUITestTunnelCommon/include/SBTRequestPropertyStorage.h @@ -0,0 +1,22 @@ +// +// SBTRequestPropertyStorage.h +// Pods +// +// Created by tomas on 20/02/24. +// + +/// This class serves as a wrapper for NSURLProtocol to handle proxy property storage, addressing a limitation of +/// NSURLProtocol which restricts property size to 2^14 bytes. This class stores properties in a separate in memory +/// storage assigning a unique uuid. This uuid is passed to the underlying NSURLProtocol and is used to retrieve the +/// property from the storage when needed. Properties are stored in an NSDictionary with the uuid as the key and the +/// property as the value. If this proves to be excessively optimistic memory wise an on disk storage can be implemented +/// in the future. + +@import Foundation; + +@interface SBTRequestPropertyStorage : NSObject + ++ (void)setProperty:(nonnull id)property forKey:(nonnull NSString *)key inRequest:(nonnull NSMutableURLRequest *)request; ++ (nullable id)propertyForKey:(nonnull NSString *)key inRequest:(nonnull NSURLRequest *)request; + +@end diff --git a/Sources/SBTUITestTunnelCommon/include/SBTUITestTunnelCommon.h b/Sources/SBTUITestTunnelCommon/include/SBTUITestTunnelCommon.h index 1cabe499..61097f87 100644 --- a/Sources/SBTUITestTunnelCommon/include/SBTUITestTunnelCommon.h +++ b/Sources/SBTUITestTunnelCommon/include/SBTUITestTunnelCommon.h @@ -24,6 +24,7 @@ #import "SBTUITestTunnel.h" #import "SBTIPCTunnel.h" #import "SBTActiveStub.h" +#import "SBTRequestPropertyStorage.h" #import "NSURLRequest+HTTPBodyFix.h" #ifdef SPM diff --git a/Sources/SBTUITestTunnelServer/private/NSURLSession+HTTPBodyFix.m b/Sources/SBTUITestTunnelServer/private/NSURLSession+HTTPBodyFix.m index 34cf0844..7b0d09ba 100644 --- a/Sources/SBTUITestTunnelServer/private/NSURLSession+HTTPBodyFix.m +++ b/Sources/SBTUITestTunnelServer/private/NSURLSession+HTTPBodyFix.m @@ -26,7 +26,7 @@ - (NSURLSessionUploadTask *)swz_uploadTaskWithRequest:(NSURLRequest *)request fr NSURLRequest *requestWithoutBody = [request sbt_copyWithoutBody]; if ([requestWithoutBody isKindOfClass:[NSMutableURLRequest class]] && bodyData) { - [NSURLProtocol setProperty:bodyData forKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:(NSMutableURLRequest *)requestWithoutBody]; + [SBTRequestPropertyStorage setProperty:bodyData forKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:(NSMutableURLRequest *)requestWithoutBody]; // mark this as an upload request so future code knows to find the body via NSURLProtocol instead [requestWithoutBody sbt_markUploadTaskRequest]; @@ -43,7 +43,7 @@ - (NSURLSessionUploadTask *)swz_uploadTaskWithRequest:(NSURLRequest *)request fr if ([requestWithoutBody isKindOfClass:[NSMutableURLRequest class]]) { NSData *bodyData = [NSData dataWithContentsOfURL:fileURL]; if (bodyData) { - [NSURLProtocol setProperty:bodyData forKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:(NSMutableURLRequest *)requestWithoutBody]; + [SBTRequestPropertyStorage setProperty:bodyData forKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:(NSMutableURLRequest *)requestWithoutBody]; // mark this as an upload request so future code knows to find the body via NSURLProtocol instead [requestWithoutBody sbt_markUploadTaskRequest]; @@ -59,7 +59,7 @@ - (NSURLSessionUploadTask *)swz_uploadTaskWithRequest:(NSURLRequest *)request fr NSURLRequest *requestWithoutBody = [request sbt_copyWithoutBody]; if ([requestWithoutBody isKindOfClass:[NSMutableURLRequest class]] && bodyData) { - [NSURLProtocol setProperty:bodyData forKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:(NSMutableURLRequest *)requestWithoutBody]; + [SBTRequestPropertyStorage setProperty:bodyData forKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:(NSMutableURLRequest *)requestWithoutBody]; // mark this as an upload request so future code knows to find the body via NSURLProtocol instead [requestWithoutBody sbt_markUploadTaskRequest]; @@ -76,7 +76,7 @@ - (NSURLSessionUploadTask *)swz_uploadTaskWithRequest:(NSURLRequest *)request fr - (NSURLSessionDataTask *)swz_dataTaskWithRequest:(NSURLRequest *)request { if ([request isKindOfClass:[NSMutableURLRequest class]] && request.HTTPBody) { - [NSURLProtocol setProperty:request.HTTPBody forKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:(NSMutableURLRequest *)request]; + [SBTRequestPropertyStorage setProperty:request.HTTPBody forKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:(NSMutableURLRequest *)request]; } return [self swz_dataTaskWithRequest:request]; @@ -85,7 +85,7 @@ - (NSURLSessionDataTask *)swz_dataTaskWithRequest:(NSURLRequest *)request - (NSURLSessionDataTask *)swz_dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler { if ([request isKindOfClass:[NSMutableURLRequest class]] && request.HTTPBody) { - [NSURLProtocol setProperty:request.HTTPBody forKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:(NSMutableURLRequest *)request]; + [SBTRequestPropertyStorage setProperty:request.HTTPBody forKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:(NSMutableURLRequest *)request]; } return [self swz_dataTaskWithRequest:request completionHandler:completionHandler]; diff --git a/Sources/SBTUITestTunnelServer/private/SBTProxyURLProtocol.m b/Sources/SBTUITestTunnelServer/private/SBTProxyURLProtocol.m index 603a12c8..783ea2a2 100644 --- a/Sources/SBTUITestTunnelServer/private/SBTProxyURLProtocol.m +++ b/Sources/SBTUITestTunnelServer/private/SBTProxyURLProtocol.m @@ -362,7 +362,7 @@ + (BOOL)canInitWithRequest:(NSURLRequest *)request // values for the allHTTPHeaderFields property in one of these callse. For this reason // we postpone matching the request headers after startLoading is called. - if ([NSURLProtocol propertyForKey:SBTProxyURLProtocolHandledKey inRequest:request]) { + if ([SBTRequestPropertyStorage propertyForKey:SBTProxyURLProtocolHandledKey inRequest:request]) { return NO; } @@ -449,7 +449,7 @@ - (void)startLoading NSMutableURLRequest *redirectionRequest = [NSMutableURLRequest requestWithURL:redirectionUrl]; [NSURLProtocol removePropertyForKey:SBTProxyURLProtocolHandledKey inRequest:redirectionRequest]; - if (![NSURLProtocol propertyForKey:SBTProxyURLOriginalRequestKey inRequest:redirectionRequest]) { + if (![SBTRequestPropertyStorage propertyForKey:SBTProxyURLOriginalRequestKey inRequest:redirectionRequest]) { // don't handle double (or more) redirects [[self class] associateOriginalRequest:request withRequest:redirectionRequest]; } @@ -481,7 +481,7 @@ - (void)startLoading NSLog(@"[SBTUITestTunnel] Throttling/monitoring/chaning cookies/stubbing headers %@ request: %@\n\nMatching rule:\n%@", [self.request HTTPMethod], [self.request URL], requestMatch1 ?: requestMatch2 ?: requestMatch3 ?: requestMatch4 ?: requestMatch5); NSMutableURLRequest *newRequest = [self.request mutableCopy]; - [NSURLProtocol setProperty:@YES forKey:SBTProxyURLProtocolHandledKey inRequest:newRequest]; + [SBTRequestPropertyStorage setProperty:@YES forKey:SBTProxyURLProtocolHandledKey inRequest:newRequest]; NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil]; @@ -628,7 +628,7 @@ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPer } [NSURLProtocol removePropertyForKey:SBTProxyURLProtocolHandledKey inRequest:mRequest]; - if (![NSURLProtocol propertyForKey:SBTProxyURLOriginalRequestKey inRequest:mRequest]) { + if (![SBTRequestPropertyStorage propertyForKey:SBTProxyURLOriginalRequestKey inRequest:mRequest]) { // don't handle double (or more) redirects [[self class] associateOriginalRequest:self.request withRequest:mRequest]; } @@ -814,7 +814,7 @@ - (NSDictionary *)blockCookieRuleFromMatchingRules:(NSArray *)ma /// Finds the original request in NSURLProtocol and deserializes it + (NSURLRequest *)originalRequestFor:(NSURLRequest*)request { - NSData *serializedOriginal = [NSURLProtocol propertyForKey:SBTProxyURLOriginalRequestKey inRequest:request]; + NSData *serializedOriginal = [SBTRequestPropertyStorage propertyForKey:SBTProxyURLOriginalRequestKey inRequest:request]; NSURLRequest *originalRequest = nil; if (serializedOriginal) { @@ -837,7 +837,7 @@ + (void)associateOriginalRequest:(NSURLRequest *)original withRequest:(NSMutable error:&archiveError]; NSAssert(archiveError == nil, @"Error archiving NSURLRequest for NSURLProtocol"); - [NSURLProtocol setProperty:serializedOriginal forKey:SBTProxyURLOriginalRequestKey inRequest:request]; + [SBTRequestPropertyStorage setProperty:serializedOriginal forKey:SBTProxyURLOriginalRequestKey inRequest:request]; } @end From b13036a022fdb9a6a545b793d61edeaafddab3ad Mon Sep 17 00:00:00 2001 From: Ahmed Osama Date: Wed, 20 Mar 2024 09:48:16 +0200 Subject: [PATCH 4/6] Encode stored large body with the requests for sending --- .../SBTMonitoredNetworkRequest.m | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Sources/SBTUITestTunnelCommon/SBTMonitoredNetworkRequest.m b/Sources/SBTUITestTunnelCommon/SBTMonitoredNetworkRequest.m index 6b161e5b..f198fe7e 100644 --- a/Sources/SBTUITestTunnelCommon/SBTMonitoredNetworkRequest.m +++ b/Sources/SBTUITestTunnelCommon/SBTMonitoredNetworkRequest.m @@ -44,8 +44,15 @@ - (void)encodeWithCoder:(NSCoder *)encoder { [encoder encodeDouble:self.timestamp forKey:NSStringFromSelector(@selector(timestamp))]; [encoder encodeDouble:self.requestTime forKey:NSStringFromSelector(@selector(requestTime))]; - [encoder encodeObject:self.request forKey:NSStringFromSelector(@selector(request))]; - [encoder encodeObject:self.originalRequest forKey:NSStringFromSelector(@selector(originalRequest))]; + + NSMutableURLRequest *fixedRequest = [self.request mutableCopy]; + fixedRequest.HTTPBody = [SBTRequestPropertyStorage propertyForKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:self.request]; + [encoder encodeObject:fixedRequest forKey:NSStringFromSelector(@selector(request))]; + + NSMutableURLRequest *fixedOriginalRequest = [self.originalRequest mutableCopy]; + fixedOriginalRequest.HTTPBody = [SBTRequestPropertyStorage propertyForKey:SBTUITunneledNSURLProtocolHTTPBodyKey inRequest:self.originalRequest]; + [encoder encodeObject:fixedOriginalRequest forKey:NSStringFromSelector(@selector(originalRequest))]; + [encoder encodeObject:self.response forKey:NSStringFromSelector(@selector(response))]; [encoder encodeObject:self.responseData forKey:NSStringFromSelector(@selector(responseData))]; [encoder encodeBool:self.isStubbed forKey:NSStringFromSelector(@selector(isStubbed))]; From e6f801445c9d0cf24659e0b967e922784716ed99 Mon Sep 17 00:00:00 2001 From: Ahmed Osama Date: Wed, 20 Mar 2024 10:04:22 +0200 Subject: [PATCH 5/6] Import headers --- Sources/SBTUITestTunnelCommon/SBTMonitoredNetworkRequest.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/SBTUITestTunnelCommon/SBTMonitoredNetworkRequest.m b/Sources/SBTUITestTunnelCommon/SBTMonitoredNetworkRequest.m index f198fe7e..7b90d1bd 100644 --- a/Sources/SBTUITestTunnelCommon/SBTMonitoredNetworkRequest.m +++ b/Sources/SBTUITestTunnelCommon/SBTMonitoredNetworkRequest.m @@ -16,6 +16,8 @@ #import "include/SBTMonitoredNetworkRequest.h" #import "include/SBTRequestMatch.h" +#import "include/SBTRequestPropertyStorage.h" +#import "include/SBTUITestTunnel.h" #import "private/NSData+gzip.h" @implementation SBTMonitoredNetworkRequest : NSObject From 7db92b2a61a37f4ec02e55fbc66d8a71658f19df Mon Sep 17 00:00:00 2001 From: Tomas Camin Date: Tue, 26 Mar 2024 14:19:02 +0100 Subject: [PATCH 6/6] Add test --- .../SBTTableViewController.swift | 6 ++ .../DownloadUploadTests.swift | 67 +++++++++++++------ .../SBTUITestTunnel_Tests/MonitorTests.swift | 2 +- 3 files changed, 55 insertions(+), 20 deletions(-) diff --git a/Example/SBTUITestTunnel/SBTTableViewController.swift b/Example/SBTUITestTunnel/SBTTableViewController.swift index ad73a671..9ceee7d1 100644 --- a/Example/SBTUITestTunnel/SBTTableViewController.swift +++ b/Example/SBTUITestTunnel/SBTTableViewController.swift @@ -46,6 +46,7 @@ class SBTTableViewController: UITableViewController { private let testList: [BaseTest] = [NetworkTest(testSelector: #selector(executeDataTaskRequest)), NetworkTest(testSelector: #selector(executeDataTaskRequest2)), NetworkTest(testSelector: #selector(executeDataTaskRequest3)), + NetworkTest(testSelector: #selector(executePostDataTaskRequestWithLargeHTTPBody)), NetworkTest(testSelector: #selector(executeUploadDataTaskRequest)), NetworkTest(testSelector: #selector(executeUploadDataTaskRequest2)), NetworkTest(testSelector: #selector(executeBackgroundUploadDataTaskRequest)), @@ -348,6 +349,11 @@ extension SBTTableViewController { @objc func executeDataTaskRequest3() { dataTaskNetwork(urlString: "https://httpbin.org/get?param1=val1¶m2=val2", httpMethod: "GET", httpBody: nil, delay: 0.0, shouldPushResult: false) } + + @objc func executePostDataTaskRequestWithLargeHTTPBody() { + let largeBody = String(repeating: "a", count: 20000) + dataTaskNetwork(urlString: "https://httpbin.org/post", httpMethod: "POST", httpBody: largeBody) + } @objc func executeUploadDataTaskRequest() { let data = "This is a test".data(using: .utf8) diff --git a/Example/SBTUITestTunnel_Tests/DownloadUploadTests.swift b/Example/SBTUITestTunnel_Tests/DownloadUploadTests.swift index af428839..50c9c2bd 100644 --- a/Example/SBTUITestTunnel_Tests/DownloadUploadTests.swift +++ b/Example/SBTUITestTunnel_Tests/DownloadUploadTests.swift @@ -21,62 +21,91 @@ import XCTest class DownloadUploadTests: XCTestCase { override func setUp() { super.setUp() - + app.launchTunnel(withOptions: [SBTUITunneledApplicationLaunchOptionResetFilesystem]) - + expectation(for: NSPredicate(format: "count > 0"), evaluatedWith: app.tables) waitForExpectations(timeout: 15.0, handler: nil) - + Thread.sleep(forTimeInterval: 1.0) } - + func testSingleDownload() { let randomString = ProcessInfo.processInfo.globallyUniqueString - + let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) let testFilePath = paths.first!.appending("/test_file_a.txt") - + if FileManager.default.fileExists(atPath: testFilePath) { try! FileManager.default.removeItem(atPath: testFilePath) } - + try! (randomString.data(using: .utf8))?.write(to: URL(fileURLWithPath: testFilePath)) - + app.uploadItem(atPath: testFilePath, toPath: "test_file_b.txt", relativeTo: .documentDirectory) - + let uploadData = app.downloadItems(fromPath: "test_file_b.txt", relativeTo: .documentDirectory)?.first! - + let uploadedString = String(data: uploadData!, encoding: .utf8) - + XCTAssertTrue(randomString == uploadedString) } - + func testMultipleDownload() { let randomString = ProcessInfo.processInfo.globallyUniqueString - + let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) let testFilePath = paths.first!.appending("/test_file_a.txt") - + if FileManager.default.fileExists(atPath: testFilePath) { try! FileManager.default.removeItem(atPath: testFilePath) } - + try! (randomString.data(using: .utf8))?.write(to: URL(fileURLWithPath: testFilePath)) - + app.uploadItem(atPath: testFilePath, toPath: "test_file_1.txt", relativeTo: .documentDirectory) app.uploadItem(atPath: testFilePath, toPath: "test_file_2.txt", relativeTo: .documentDirectory) app.uploadItem(atPath: testFilePath, toPath: "test_file_3.txt", relativeTo: .documentDirectory) - + if let uploadDatas = app.downloadItems(fromPath: "test_file_*.txt", relativeTo: .documentDirectory) { XCTAssertEqual(uploadDatas.count, 3) - + for uploadData in uploadDatas { let uploadedString = String(data: uploadData, encoding: .utf8) - + XCTAssertTrue(randomString == uploadedString) } } else { XCTFail("No upload data received") } } + + func testMonitorPostRequestWithHTTPLargeBodyInAppProcess() { + let largeBody = String(repeating: "a", count: 20000) + let matchingRequest = SBTRequestMatch(url: "httpbin.org", method: "POST") + app.monitorRequests(matching: matchingRequest) + + XCTAssertTrue(app.tables.firstMatch.staticTexts["executePostDataTaskRequestWithLargeHTTPBody"].waitForExistence(timeout: 5)) + app.tables.firstMatch.staticTexts["executePostDataTaskRequestWithLargeHTTPBody"].tap() + + XCTAssertTrue(app.waitForMonitoredRequests(matching: matchingRequest, timeout: 10)) + let requests = app.monitoredRequestsFlushAll() + XCTAssertEqual(requests.count, 1) + + for request in requests { + guard let httpBody = request.request?.httpBody else { + XCTFail("Missing http body") + continue + } + + XCTAssertEqual(String(data: httpBody, encoding: .utf8), largeBody) + + XCTAssert((request.responseString()!).contains("httpbin.org")) + XCTAssert(request.timestamp > 0.0) + XCTAssert(request.requestTime > 0.0) + } + + XCTAssert(app.stubRequestsRemoveAll()) + XCTAssert(app.monitorRequestRemoveAll()) + } } diff --git a/Example/SBTUITestTunnel_Tests/MonitorTests.swift b/Example/SBTUITestTunnel_Tests/MonitorTests.swift index 6a73894f..e44eeaa6 100644 --- a/Example/SBTUITestTunnel_Tests/MonitorTests.swift +++ b/Example/SBTUITestTunnel_Tests/MonitorTests.swift @@ -265,7 +265,7 @@ class MonitorTests: XCTestCase { app.monitorRequests(matching: SBTRequestMatch(url: "httpbin.org")) let start = Date() - DispatchQueue.global(qos: .userInteractive).asyncAfter(deadline: .now() + 2.5) { [weak self] in + DispatchQueue.global(qos: .userInteractive).asyncAfter(deadline: .now() + 1.0) { [weak self] in _ = self?.request.dataTaskNetwork(urlString: "https://httpbin.org/get?param1=val1¶m2=val2", httpMethod: "GET", httpBody: nil, delay: 0.0) }