diff --git a/CordovaLib/Classes/Public/CDVURLSchemeHandler.h b/CordovaLib/Classes/Public/CDVURLSchemeHandler.h index 1cd84d09d6..304cc64987 100644 --- a/CordovaLib/Classes/Public/CDVURLSchemeHandler.h +++ b/CordovaLib/Classes/Public/CDVURLSchemeHandler.h @@ -25,6 +25,7 @@ @interface CDVURLSchemeHandler : NSObject @property (nonatomic, strong) CDVViewController* viewController; +@property (nonatomic) Boolean isRunning; - (instancetype)initWithVC:(CDVViewController *)controller; diff --git a/CordovaLib/Classes/Public/CDVURLSchemeHandler.m b/CordovaLib/Classes/Public/CDVURLSchemeHandler.m index 6c88ceb696..0fb1c91538 100644 --- a/CordovaLib/Classes/Public/CDVURLSchemeHandler.m +++ b/CordovaLib/Classes/Public/CDVURLSchemeHandler.m @@ -37,12 +37,70 @@ - (void)webView:(WKWebView *)webView startURLSchemeTask:(id )ur { NSString * startPath = [[NSBundle mainBundle] pathForResource:self.viewController.wwwFolderName ofType: nil]; NSURL * url = urlSchemeTask.request.URL; - NSString * stringToLoad = url.path; NSString * scheme = url.scheme; + self.isRunning = true; + Boolean loadFile = true; + NSDictionary * header = urlSchemeTask.request.allHTTPHeaderFields; + NSMutableString * stringToLoad = [NSMutableString string]; + [stringToLoad appendString:url.path]; + NSString * method = urlSchemeTask.request.HTTPMethod; + NSData * body = urlSchemeTask.request.HTTPBody; if ([scheme isEqualToString:self.viewController.appScheme]) { 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 && self.isRunning) { + NSLog(@"Proxy error: %@", error); + [urlSchemeTask didFailWithError:error]; + return; + } + + // 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]; + }); + }); + }; + } + + // Do not use urlSchemeTask if it has been closed in stopURLSchemeTask. Otherwise the app will crash. + if(self.isRunning) { + [urlSchemeTask didReceiveResponse:response]; + [urlSchemeTask didReceiveData:data]; + [urlSchemeTask didFinish]; + } + }] resume]; } else { if ([stringToLoad isEqualToString:@""] || [url.pathExtension isEqualToString:@""]) { startPath = [startPath stringByAppendingPathComponent:self.viewController.startPage]; @@ -52,37 +110,39 @@ - (void)webView:(WKWebView *)webView startURLSchemeTask:(id )ur } } - NSError * fileError = nil; - NSData * data = nil; - if ([self isMediaExtension:url.pathExtension]) { - data = [NSData dataWithContentsOfFile:startPath options:NSDataReadingMappedIfSafe error:&fileError]; - } - if (!data || fileError) { - 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) { + NSError * fileError = nil; + NSData * data = nil; + if ([self isMediaExtension:url.pathExtension]) { + data = [NSData dataWithContentsOfFile:startPath options:NSDataReadingMappedIfSafe error:&fileError]; + } + if (!data || fileError) { + 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]; + } - [urlSchemeTask didReceiveResponse:response]; - [urlSchemeTask didReceiveData:data]; - [urlSchemeTask didFinish]; + [urlSchemeTask didReceiveResponse:response]; + [urlSchemeTask didReceiveData:data]; + [urlSchemeTask didFinish]; + } } - (void)webView:(nonnull WKWebView *)webView stopURLSchemeTask:(nonnull id)urlSchemeTask { - + self.isRunning = false; } -(NSString *) getMimeType:(NSString *)fileExtension { diff --git a/CordovaLib/cordova.js b/CordovaLib/cordova.js index 51c783d0a1..8054199f8e 100644 --- a/CordovaLib/cordova.js +++ b/CordovaLib/cordova.js @@ -1799,6 +1799,18 @@ var WkWebKit = { return window.CDV_ASSETS_URL + path.replace('file://', '/_app_file_'); } return path; + }, + convertProxyUrl: function (path) { + if (!path || !window.CDV_ASSETS_URL) { + return path; + } + if (path.startsWith('http://')) { + return window.CDV_ASSETS_URL + '/_http_proxy_' + encodeURIComponent(path.replace('http://', '')); + } + if (path.startsWith('https://')) { + return window.CDV_ASSETS_URL + '/_https_proxy_' + encodeURIComponent(path.replace('https://', '')); + } + return path; } }; diff --git a/cordova-js-src/plugin/ios/wkwebkit.js b/cordova-js-src/plugin/ios/wkwebkit.js index 35c819cf14..2e1b265ab4 100644 --- a/cordova-js-src/plugin/ios/wkwebkit.js +++ b/cordova-js-src/plugin/ios/wkwebkit.js @@ -35,6 +35,17 @@ var WkWebKit = { if (path.startsWith('file://')) { return window.CDV_ASSETS_URL + path.replace('file://', '/_app_file_'); } + }, + convertProxyUrl: function (path) { + if (!path || !window.CDV_ASSETS_URL) { + return path; + } + if (path.startsWith('http://')) { + return window.CDV_ASSETS_URL + '/_http_proxy_' + encodeURIComponent(path.replace('http://', '')); + } + if (path.startsWith('https://')) { + return window.CDV_ASSETS_URL + '/_https_proxy_' + encodeURIComponent(path.replace('https://', '')); + } return path; } };