Skip to content

Commit

Permalink
feat(ios): add http(s) proxy/scheme
Browse files Browse the repository at this point in the history
This adds a new URLSchemeHandler which can proxy http(s) requests to external servers.
This is useful for some CORS issues and webview bugs that affect the use of cookies in CORS requests via XHR and fetch.
For that reason cookies will be synced between proxied requests on the native layer and the webview.
  • Loading branch information
NiklasMerz committed Oct 1, 2019
1 parent 0eb8a37 commit 6a391dd
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 19 deletions.
93 changes: 74 additions & 19 deletions src/ios/IONAssetHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,68 @@ - (instancetype)initWithBasePath:(NSString *)basePath andScheme:(NSString *)sche

- (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask
{
Boolean loadFile = true;
NSString * startPath = @"";
NSURL * url = urlSchemeTask.request.URL;
NSString * stringToLoad = url.path;
NSDictionary * header = urlSchemeTask.request.allHTTPHeaderFields;
NSMutableString * stringToLoad = [NSMutableString string];
[stringToLoad appendString:url.path];
NSString * scheme = url.scheme;
NSString * method = urlSchemeTask.request.HTTPMethod;
NSData * body = urlSchemeTask.request.HTTPBody;
NSData * data;
NSInteger statusCode;

if ([scheme isEqualToString:self.scheme]) {
if ([stringToLoad hasPrefix:@"/_app_file_"]) {
startPath = [stringToLoad stringByReplacingOccurrencesOfString:@"/_app_file_" withString:@""];
} else if ([stringToLoad hasPrefix:@"/_http_proxy_"]||[stringToLoad hasPrefix:@"/_https_proxy_"]) {
if(url.query) {
[stringToLoad appendString:@"?"];
[stringToLoad appendString:url.query];
}
loadFile = false;
startPath = [stringToLoad stringByReplacingOccurrencesOfString:@"/_http_proxy_" withString:@"http://"];
startPath = [startPath stringByReplacingOccurrencesOfString:@"/_https_proxy_" withString:@"https://"];
NSURL * requestUrl = [NSURL URLWithString:startPath];
WKWebsiteDataStore* dataStore = [WKWebsiteDataStore defaultDataStore];
WKHTTPCookieStore* cookieStore = dataStore.httpCookieStore;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setHTTPMethod:method];
[request setURL:requestUrl];
if (body) {
[request setHTTPBody:body];
}
[request setAllHTTPHeaderFields:header];
[request setHTTPShouldHandleCookies:YES];

[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if(error) {
NSLog(@"Proxy error: %@", error);
}

// set cookies to WKWebView
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
if(httpResponse) {
NSArray* cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:[httpResponse allHeaderFields] forURL:response.URL];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:cookies forURL:httpResponse.URL mainDocumentURL:nil];
cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];

for (NSHTTPCookie* c in cookies)
{
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
//running in background thread is necessary because setCookie otherwise fails
dispatch_async(dispatch_get_main_queue(), ^(void){
[cookieStore setCookie:c completionHandler:nil];
});
});
};
}

[urlSchemeTask didReceiveResponse:response];
[urlSchemeTask didReceiveData:data];
[urlSchemeTask didFinish];
}] resume];
} else {
startPath = self.basePath;
if ([stringToLoad isEqualToString:@""] || [url.pathExtension isEqualToString:@""]) {
Expand All @@ -37,25 +91,26 @@ - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)ur
}
}

NSData * data = [[NSData alloc] initWithContentsOfFile:startPath];
NSInteger statusCode = 200;
if (!data) {
statusCode = 404;
}
NSURL * localUrl = [NSURL URLWithString:url.absoluteString];
NSString * mimeType = [self getMimeType:url.pathExtension];
id response = nil;
if (data && [self isMediaExtension:url.pathExtension]) {
response = [[NSURLResponse alloc] initWithURL:localUrl MIMEType:mimeType expectedContentLength:data.length textEncodingName:nil];
} else {
NSDictionary * headers = @{ @"Content-Type" : mimeType, @"Cache-Control": @"no-cache"};
response = [[NSHTTPURLResponse alloc] initWithURL:localUrl statusCode:statusCode HTTPVersion:nil headerFields:headers];
if(loadFile) {
data = [[NSData alloc] initWithContentsOfFile:startPath];
statusCode = 200;
if (!data) {
statusCode = 404;
}
NSURL * localUrl = [NSURL URLWithString:url.absoluteString];
NSString * mimeType = [self getMimeType:url.pathExtension];
id response = nil;
if (data && [self isMediaExtension:url.pathExtension]) {
response = [[NSURLResponse alloc] initWithURL:localUrl MIMEType:mimeType expectedContentLength:data.length textEncodingName:nil];
} else {
NSDictionary * headers = @{ @"Content-Type" : mimeType, @"Cache-Control": @"no-cache"};
response = [[NSHTTPURLResponse alloc] initWithURL:localUrl statusCode:statusCode HTTPVersion:nil headerFields:headers];
}

[urlSchemeTask didReceiveResponse:response];
[urlSchemeTask didReceiveData:data];
[urlSchemeTask didFinish];
}

[urlSchemeTask didReceiveResponse:response];
[urlSchemeTask didReceiveData:data];
[urlSchemeTask didFinish];

}

- (void)webView:(nonnull WKWebView *)webView stopURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask
Expand Down
6 changes: 6 additions & 0 deletions src/www/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ var WebView = {
if (url.startsWith('file://')) {
return window.WEBVIEW_SERVER_URL + url.replace('file://', '/_app_file_');
}
if (url.startsWith('http://')) {
return window.WEBVIEW_SERVER_URL + url.replace('http://', '/_http_proxy_');
}
if (url.startsWith('https://')) {
return window.WEBVIEW_SERVER_URL + url.replace('https://', '/_https_proxy_');
}
if (url.startsWith('content://')) {
return window.WEBVIEW_SERVER_URL + url.replace('content:/', '/_app_content_');
}
Expand Down

0 comments on commit 6a391dd

Please sign in to comment.