Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Internal connections only #186

Merged
merged 5 commits into from
Oct 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 55 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,73 @@

<!-- TODO: remove beta in README.md and CONTRIBUTING.md -->

# Ionic Web View
# Ionic Web View for Cordova

The Web View plugin for Cordova that is specialized for Ionic apps.
A Web View plugin for Cordova, focused on providing the highest performance experience for Ionic apps (but can be used with any Cordova app).

This is for `cordova-plugin-ionic-webview` @ `2.x`, which uses the latest and greatest features and may not work with all apps. See [Requirements](#requirements) and [Migrating to 2.x](#migrating-to-2x).
This plugin defaults to using WKWebView on iOS and the latest evergreen webview on Android. Additionally, this plugin makes it easy to use HTML5 style routing
that web developers expect for building single-page apps.

Note: This repo and its documentation are for `cordova-plugin-ionic-webview` @ `2.x`, which uses the new features that may not work with all apps. See [Requirements](#requirements) and [Migrating to 2.x](#migrating-to-2x).

:book: **Documentation**: [https://beta.ionicframework.com/docs/building/webview][ionic-webview-docs]

:mega: **Support/Questions?** Please see our [Support Page][ionic-support] for general support questions. The issues on GitHub should be reserved for bug reports and feature requests.

:sparkling_heart: **Want to contribute?** Please see [CONTRIBUTING.md](https://github.com/ionic-team/cordova-plugin-ionic-webview/blob/master/CONTRIBUTING.md).

### Requirements
## Configuration

This plugin has several configuration options that can be set in `config.xml`. Important: some configuration options should be adjusted for production apps, especially `WKPort`:

### iOS and Android Preferences

Preferences available for both iOS and Android platforms

#### WKPort

```xml
<preference name="WKPort" value="8080" />
```

The default port the server will listen on. _You should change this to a random port number!_

### iOS Preferences

Preferences only available for iOS platform

#### WKSuspendInBackground

```xml
<preference name="WKSuspendInBackground" value="false" />
```

Whether to try to keep the server running when the app is backgrounded. Note: the server will likely be suspended by the OS after a few minutes. In particular, long-lived background tasks are not allowed on iOS outside of select audio and geolocation tasks.

#### WKBind

```xml
<preference name="WKBind" value="localhost" />
```

The hostname the server will bind to. There aren't a lot of other valid options, but some prefer binding to "127.0.0.1"

#### WKInternalConnectionsOnly (New in 2.2.0)

```xml
<preference name="WKInternalConnectionsOnly" value="true" />
```

Whether to restrict access to this server to the app itself. Previous versions of this plugin did not restrict access to the app itself. In 2.2.0 and above,
the plugin now restricts access to only the app itself.


## Plugin Requirements

* **iOS**: iOS 10+ and `cordova-ios` 4+
* **Android**: Android 5.0+ and `cordova-android` 6.4+
* **Android**: Android 4.4+ and `cordova-android` 6.4+

### Migrating to 2.x
## Migrating to 2.x

1. Remove and re-add the Web View plugin:

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cordova-plugin-ionic-webview",
"version": "2.1.4",
"version": "2.2.0",
"description": "The official Ionic's WKWebView Engine Plugin",
"main": "index.js",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
under the License.
-->

<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:rim="http://www.blackberry.com/ns/widgets" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-ionic-webview" version="2.1.4">
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:rim="http://www.blackberry.com/ns/widgets" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-ionic-webview" version="2.2.0">
<name>cordova-plugin-ionic-webview</name>
<description>The official Ionic's WKWebView Engine Plugin</description>
<license>Apache-2.0</license>
Expand Down
69 changes: 67 additions & 2 deletions src/ios/CDVWKWebViewEngine.m
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ @interface CDVWKWebViewEngine ()
@property (nonatomic, weak) id <WKScriptMessageHandler> weakScriptMessageHandler;
@property (nonatomic, strong) GCDWebServer *webServer;
@property (nonatomic, readwrite) CGRect frame;
@property (nonatomic, strong) NSString *userAgentCreds;
@property (nonatomic, assign) BOOL internalConnectionsOnly;
@property (nonatomic, readwrite) NSString *CDV_LOCAL_SERVER;
@end

Expand Down Expand Up @@ -259,6 +261,8 @@ - (void)pluginInitialize
{
// viewController would be available now. we attempt to set all possible delegates to it, by default
NSDictionary* settings = self.commandDelegate.settings;
self.internalConnectionsOnly = [settings cordovaBoolSettingForKey:@"WKInternalConnectionsOnly" defaultValue:YES];

[self initWebServer];

self.uiDelegate = [[CDVWKWebViewUIDelegate alloc] initWithTitle:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"]];
Expand Down Expand Up @@ -319,6 +323,9 @@ - (void)pluginInitialize
if (IsAtLeastiOSVersion(@"9.0") && [self.viewController isKindOfClass:[CDVViewController class]]) {
wkWebView.customUserAgent = ((CDVViewController*) self.viewController).userAgent;
}
if (self.internalConnectionsOnly) {
wkWebView.customUserAgent = [NSString stringWithFormat:@"%@/%@",wkWebView.customUserAgent, [self getUserAgentCredentials]];
}

if ([self.viewController conformsToProtocol:@protocol(WKUIDelegate)]) {
wkWebView.UIDelegate = (id <WKUIDelegate>)self.viewController;
Expand Down Expand Up @@ -376,6 +383,21 @@ - (void) keyboardDisplayDoesNotRequireUserAction {
}
}

- (NSString*)getUserAgentCredentials {
if (self.userAgentCreds == nil) {
self.userAgentCreds = [self generateRandomString:32];
}
return self.userAgentCreds;
}

- (NSString*)generateRandomString:(int)num {
NSMutableString* string = [NSMutableString stringWithCapacity:num];
for (int i = 0; i < num; i++) {
[string appendFormat:@"%C", (unichar)('a' + arc4random_uniform(26))];
}
return string;
}

- (void)onReset
{
[self addURLObserver];
Expand Down Expand Up @@ -799,7 +821,11 @@ -(void)setServerPath:(NSString *) path
}

__block NSString* serverUrl = self.CDV_LOCAL_SERVER;
[self.webServer addGETHandlerForBasePath:@"/" directoryPath:path indexFilename:((CDVViewController *)self.viewController).startPage cacheAge:0 allowRangeRequests:YES];
if (self.internalConnectionsOnly) {
[self internalConnectionsGetHandlerForPath:path];
} else {
[self.webServer addGETHandlerForBasePath:@"/" directoryPath:path indexFilename:((CDVViewController *)self.viewController).startPage cacheAge:0 allowRangeRequests:YES];
}
[self.webServer addHandlerForMethod:@"GET" pathRegex:@"_file_/" requestClass:GCDWebServerFileRequest.class asyncProcessBlock:^(__kindof GCDWebServerRequest * _Nonnull request, GCDWebServerCompletionBlock _Nonnull completionBlock) {
NSString *urlToRemove = [serverUrl stringByAppendingString:@"/_file_"];
NSString *absUrl = [[[request URL] absoluteString] stringByReplacingOccurrencesOfString:urlToRemove withString:@""];
Expand All @@ -810,7 +836,7 @@ -(void)setServerPath:(NSString *) path
}
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:absUrl]) {
GCDWebServerResponse* response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NotFound];;
GCDWebServerResponse* response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NotFound];
completionBlock(response);
} else {
GCDWebServerFileResponse *response = [GCDWebServerFileResponse responseWithFile:absUrl byteRange:request.byteRange];
Expand All @@ -823,6 +849,45 @@ -(void)setServerPath:(NSString *) path
}
}

-(void) internalConnectionsGetHandlerForPath:(NSString*)directoryPath {
__weak CDVWKWebViewEngine * weakSelf = self;
[self.webServer addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
if (![requestMethod isEqualToString:@"GET"]) {
return nil;
}
return [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
}
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
GCDWebServerResponse* response = nil;
NSString* userAgent = [request.headers objectForKey:@"User-Agent"];
if ([userAgent containsString:[weakSelf getUserAgentCredentials]]) {
NSString* filePath = [directoryPath stringByAppendingPathComponent:[request.path substringFromIndex:1]];
NSString* fileType = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL] fileType];
if (fileType) {
if ([fileType isEqualToString:NSFileTypeDirectory]) {
NSString* indexPath = [filePath stringByAppendingPathComponent:((CDVViewController *)weakSelf.viewController).startPage];
NSString* indexType = [[[NSFileManager defaultManager] attributesOfItemAtPath:indexPath error:NULL] fileType];
if ([indexType isEqualToString:NSFileTypeRegular]) {
response = [GCDWebServerFileResponse responseWithFile:indexPath];
}
} else if ([fileType isEqualToString:NSFileTypeRegular]) {
response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange];
[response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"];
}
}
if (response) {
response.cacheControlMaxAge = 0;
} else {
response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NotFound];
}
[response setValue:@"*" forAdditionalHeader:@"Access-Control-Allow-Origin"];
} else {
response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Unauthorized];
}
return response;
}];
}

-(void)persistServerBasePath:(CDVInvokedUrlCommand*)command
{
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
Expand Down