From 89d523b916c7d4a125c1c904994aba6deeed606a Mon Sep 17 00:00:00 2001 From: FailCake Date: Tue, 11 Jun 2019 14:16:56 +0100 Subject: [PATCH 1/7] Update WebViewLocalServer.java --- .../cordova/webview/WebViewLocalServer.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java b/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java index 0a5952d9..a602d261 100644 --- a/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java +++ b/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java @@ -53,6 +53,7 @@ public class WebViewLocalServer { public final static String httpsScheme = "https"; public final static String fileStart = "/_app_file_"; public final static String contentStart = "/_app_content_"; + public final static String proxyStart = "/_app_proxy_"; private final UriMatcher uriMatcher; private final AndroidProtocolHandler protocolHandler; @@ -219,6 +220,7 @@ public WebResourceResponse shouldInterceptRequest(Uri uri, WebResourceRequest re synchronized (uriMatcher) { handler = (PathHandler) uriMatcher.match(uri); } + if (handler == null) { return null; } @@ -231,6 +233,11 @@ public WebResourceResponse shouldInterceptRequest(Uri uri, WebResourceRequest re } } + private boolean isProxySource(Uri uri) { + String path = uri.getPath(); + return path.startsWith(proxyStart); + } + private boolean isLocalFile(Uri uri) { String path = uri.getPath(); if (path.startsWith(contentStart) || path.startsWith(fileStart)) { @@ -265,6 +272,16 @@ private WebResourceResponse handleLocalRequest(Uri uri, PathHandler handler, Web return createWebResourceResponse(mimeType, handler.getEncoding(), statusCode, handler.getReasonPhrase(), tempResponseHeaders, responseStream); } + + if (isProxySource(uri)) { + String fixedUri = uri.toString().replaceFirst("http://localhost/_app_proxy_/", ""); + + InputStream responseStream = new LollipopLazyInputStream(handler, Uri.parse(fixedUri)); + String mimeType = getMimeType(path, responseStream); + return createWebResourceResponse(mimeType, handler.getEncoding(), + handler.getStatusCode(), handler.getReasonPhrase(), handler.getResponseHeaders(), responseStream); + } + if (isLocalFile(uri)) { InputStream responseStream = new LollipopLazyInputStream(handler, uri); String mimeType = getMimeType(path, responseStream); From 622fba722cc89447cacbf79d739d1b9a4746a7c1 Mon Sep 17 00:00:00 2001 From: FailCake Date: Tue, 11 Jun 2019 14:22:03 +0100 Subject: [PATCH 2/7] Update WebViewLocalServer.java --- .../com/ionicframework/cordova/webview/WebViewLocalServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java b/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java index a602d261..af1f18b1 100644 --- a/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java +++ b/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java @@ -225,7 +225,7 @@ public WebResourceResponse shouldInterceptRequest(Uri uri, WebResourceRequest re return null; } - if (isLocalFile(uri) || uri.getAuthority().equals(this.authority)) { + if (isProxySource(uri) || isLocalFile(uri) || uri.getAuthority().equals(this.authority)) { Log.d("SERVER", "Handling local request: " + uri.toString()); return handleLocalRequest(uri, handler, request); } else { From 77d756105e33754a66730c58f59e047806ecee1f Mon Sep 17 00:00:00 2001 From: FailCake Date: Tue, 11 Jun 2019 14:40:24 +0100 Subject: [PATCH 3/7] Update WebViewLocalServer.java --- .../cordova/webview/WebViewLocalServer.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java b/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java index af1f18b1..722b5c4d 100644 --- a/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java +++ b/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java @@ -216,6 +216,15 @@ private static WebResourceResponse createWebResourceResponse(String mimeType, St * @return a response if the request URL had a matching handler, null if no handler was found. */ public WebResourceResponse shouldInterceptRequest(Uri uri, WebResourceRequest request) { + if (isProxySource(uri)) { + String fixedUri = uri.toString().replaceFirst("http://localhost/_app_proxy_/", ""); + + InputStream responseStream = new LollipopLazyInputStream(handler, Uri.parse(fixedUri)); + String mimeType = getMimeType(path, responseStream); + return createWebResourceResponse(mimeType, handler.getEncoding(), + handler.getStatusCode(), handler.getReasonPhrase(), handler.getResponseHeaders(), responseStream); + } + PathHandler handler; synchronized (uriMatcher) { handler = (PathHandler) uriMatcher.match(uri); @@ -225,7 +234,7 @@ public WebResourceResponse shouldInterceptRequest(Uri uri, WebResourceRequest re return null; } - if (isProxySource(uri) || isLocalFile(uri) || uri.getAuthority().equals(this.authority)) { + if (isLocalFile(uri) || uri.getAuthority().equals(this.authority)) { Log.d("SERVER", "Handling local request: " + uri.toString()); return handleLocalRequest(uri, handler, request); } else { @@ -272,16 +281,7 @@ private WebResourceResponse handleLocalRequest(Uri uri, PathHandler handler, Web return createWebResourceResponse(mimeType, handler.getEncoding(), statusCode, handler.getReasonPhrase(), tempResponseHeaders, responseStream); } - - if (isProxySource(uri)) { - String fixedUri = uri.toString().replaceFirst("http://localhost/_app_proxy_/", ""); - - InputStream responseStream = new LollipopLazyInputStream(handler, Uri.parse(fixedUri)); - String mimeType = getMimeType(path, responseStream); - return createWebResourceResponse(mimeType, handler.getEncoding(), - handler.getStatusCode(), handler.getReasonPhrase(), handler.getResponseHeaders(), responseStream); - } - + if (isLocalFile(uri)) { InputStream responseStream = new LollipopLazyInputStream(handler, uri); String mimeType = getMimeType(path, responseStream); From bb8d4864a6cfc319458cbd52ca0e9cc436efee17 Mon Sep 17 00:00:00 2001 From: Eduardo Fernandes Date: Tue, 11 Jun 2019 15:55:59 +0100 Subject: [PATCH 4/7] Trying to proxy the requests --- .../cordova/webview/WebViewLocalServer.java | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java b/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java index 722b5c4d..26ef9092 100644 --- a/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java +++ b/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java @@ -34,6 +34,10 @@ import java.util.Map; import java.util.UUID; +import java.net.URL; +import java.net.URLConnection; + + /** * Helper class meant to be used with the android.webkit.WebView class to enable hosting assets, * resources and other data on 'virtual' http(s):// URL. @@ -217,14 +221,22 @@ private static WebResourceResponse createWebResourceResponse(String mimeType, St */ public WebResourceResponse shouldInterceptRequest(Uri uri, WebResourceRequest request) { if (isProxySource(uri)) { - String fixedUri = uri.toString().replaceFirst("http://localhost/_app_proxy_/", ""); - - InputStream responseStream = new LollipopLazyInputStream(handler, Uri.parse(fixedUri)); - String mimeType = getMimeType(path, responseStream); - return createWebResourceResponse(mimeType, handler.getEncoding(), - handler.getStatusCode(), handler.getReasonPhrase(), handler.getResponseHeaders(), responseStream); + try { + String fixedUri = uri.toString().replaceFirst("http://localhost/_app_proxy_/", ""); + URL httpsUrl = new URL(fixedUri); + URLConnection connection = httpsUrl.openConnection(); + connection.setRequestProperty("Access-Control-Allow-Origin", "*"); + connection.setRequestProperty("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS"); + connection.setRequestProperty("Access-Control-Allow-Headers", "agent, user-data, Access-Control-Allow-Headers, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"); + + return new WebResourceResponse(connection.getContentType(), connection.getContentEncoding(), connection.getInputStream()); + } catch (Exception e) { + //an error occurred + return null; + } } + PathHandler handler; synchronized (uriMatcher) { handler = (PathHandler) uriMatcher.match(uri); From 5d8e5969e27f1870ebba242d3a2eceb0d58c0765 Mon Sep 17 00:00:00 2001 From: Eduardo Fernandes Date: Wed, 12 Jun 2019 11:58:23 +0100 Subject: [PATCH 5/7] Ability to proxy requests by using localhost --- .../cordova/webview/WebViewLocalServer.java | 64 ++++++++++++------- src/www/util.js | 3 + 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java b/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java index 26ef9092..36ef0481 100644 --- a/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java +++ b/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java @@ -28,16 +28,14 @@ import java.io.InputStream; import java.net.HttpURLConnection; import java.net.SocketTimeoutException; -import java.net.URL; -import java.net.URLConnection; import java.util.HashMap; import java.util.Map; import java.util.UUID; +import java.util.List; import java.net.URL; import java.net.URLConnection; - /** * Helper class meant to be used with the android.webkit.WebView class to enable hosting assets, * resources and other data on 'virtual' http(s):// URL. @@ -57,7 +55,7 @@ public class WebViewLocalServer { public final static String httpsScheme = "https"; public final static String fileStart = "/_app_file_"; public final static String contentStart = "/_app_content_"; - public final static String proxyStart = "/_app_proxy_"; + public final static String proxyStart = "/_local_proxy_"; private final UriMatcher uriMatcher; private final AndroidProtocolHandler protocolHandler; @@ -220,23 +218,6 @@ private static WebResourceResponse createWebResourceResponse(String mimeType, St * @return a response if the request URL had a matching handler, null if no handler was found. */ public WebResourceResponse shouldInterceptRequest(Uri uri, WebResourceRequest request) { - if (isProxySource(uri)) { - try { - String fixedUri = uri.toString().replaceFirst("http://localhost/_app_proxy_/", ""); - URL httpsUrl = new URL(fixedUri); - URLConnection connection = httpsUrl.openConnection(); - connection.setRequestProperty("Access-Control-Allow-Origin", "*"); - connection.setRequestProperty("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS"); - connection.setRequestProperty("Access-Control-Allow-Headers", "agent, user-data, Access-Control-Allow-Headers, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"); - - return new WebResourceResponse(connection.getContentType(), connection.getContentEncoding(), connection.getInputStream()); - } catch (Exception e) { - //an error occurred - return null; - } - } - - PathHandler handler; synchronized (uriMatcher) { handler = (PathHandler) uriMatcher.match(uri); @@ -248,13 +229,18 @@ public WebResourceResponse shouldInterceptRequest(Uri uri, WebResourceRequest re if (isLocalFile(uri) || uri.getAuthority().equals(this.authority)) { Log.d("SERVER", "Handling local request: " + uri.toString()); - return handleLocalRequest(uri, handler, request); + + if(isLocalProxySource(uri)) { + return handleLocalProxyRequest(uri, request); + } else { + return handleLocalRequest(uri, handler, request); + } } else { return handleProxyRequest(uri, handler); } } - private boolean isProxySource(Uri uri) { + private boolean isLocalProxySource(Uri uri) { String path = uri.getPath(); return path.startsWith(proxyStart); } @@ -267,6 +253,38 @@ private boolean isLocalFile(Uri uri) { return false; } + private WebResourceResponse handleLocalProxyRequest(Uri uri, WebResourceRequest request) { + String fixedUri = uri.toString().replaceFirst("http://localhost/_local_proxy_/", ""); // Fix the url by removing the proxy schema + + try { + URL httpsUrl = new URL(fixedUri); + URLConnection connection = httpsUrl.openConnection(); + HttpURLConnection httpConnection = (HttpURLConnection)connection; + + httpConnection.setRequestMethod(request.getMethod()); + for (Map.Entry entry : request.getRequestHeaders().entrySet()) { + httpConnection.setRequestProperty(entry.getKey(), entry.getValue()); + } + + httpConnection.connect(); + + // Pass them trough (Convert String,List to String,String) + Map headers = new HashMap(); + for (Map.Entry> entry : connection.getHeaderFields().entrySet()) { + headers.put(entry.getKey(), entry.getValue().get(0)); + } + + // Bypass CORS + headers.put("Access-Control-Allow-Origin", "*"); + headers.put("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS"); + headers.put("Access-Control-Allow-Headers", "agent, user-data, Access-Control-Allow-Headers, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"); + + return new WebResourceResponse(connection.getContentType(), connection.getContentEncoding(), httpConnection.getResponseCode(), httpConnection.getResponseMessage(), headers, connection.getInputStream()); + } catch (Exception e) { + //an error occurred + return null; + } + } private WebResourceResponse handleLocalRequest(Uri uri, PathHandler handler, WebResourceRequest request) { String path = uri.getPath(); diff --git a/src/www/util.js b/src/www/util.js index 4acf8851..10c86b5c 100644 --- a/src/www/util.js +++ b/src/www/util.js @@ -14,6 +14,9 @@ var WebView = { if (url.startsWith('content://')) { return window.WEBVIEW_SERVER_URL + url.replace('content:/', '/_app_content_'); } + if (url.startsWith('proxy://')) { + return window.WEBVIEW_SERVER_URL + url.replace('proxy:/', '/_local_proxy_'); + } return url; }, setServerBasePath: function(path) { From 39585c70cb03f0c76f4879d0a778e060db4c5cec Mon Sep 17 00:00:00 2001 From: Eduardo Fernandes Date: Wed, 12 Jun 2019 16:05:59 +0100 Subject: [PATCH 6/7] Attempting to fix range problems --- .../cordova/webview/WebViewLocalServer.java | 67 ++++++++++++++++--- 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java b/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java index 36ef0481..9cfcf4df 100644 --- a/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java +++ b/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java @@ -218,6 +218,10 @@ private static WebResourceResponse createWebResourceResponse(String mimeType, St * @return a response if the request URL had a matching handler, null if no handler was found. */ public WebResourceResponse shouldInterceptRequest(Uri uri, WebResourceRequest request) { + if(isLocalProxySource(uri)) { + return handleLocalProxyRequest(uri, request); + } + PathHandler handler; synchronized (uriMatcher) { handler = (PathHandler) uriMatcher.match(uri); @@ -260,26 +264,67 @@ private WebResourceResponse handleLocalProxyRequest(Uri uri, WebResourceRequest URL httpsUrl = new URL(fixedUri); URLConnection connection = httpsUrl.openConnection(); HttpURLConnection httpConnection = (HttpURLConnection)connection; + InputStream responseStream = connection.getInputStream(); - httpConnection.setRequestMethod(request.getMethod()); - for (Map.Entry entry : request.getRequestHeaders().entrySet()) { - httpConnection.setRequestProperty(entry.getKey(), entry.getValue()); - } - - httpConnection.connect(); - - // Pass them trough (Convert String,List to String,String) Map headers = new HashMap(); for (Map.Entry> entry : connection.getHeaderFields().entrySet()) { - headers.put(entry.getKey(), entry.getValue().get(0)); + String key = entry.getKey(); + headers.put(key, entry.getValue().get(0)); } + int code = httpConnection.getResponseCode(); + if(request != null && request.getRequestHeaders().get("Range") != null) { + String rangeString = request.getRequestHeaders().get("Range"); + int contentLength = 0; + + if(responseStream.available() <= 0){ + contentLength = Integer.parseInt(headers.get("Content-Length")); + } else { + contentLength = responseStream.available(); + } + + String[] parts = rangeString.split("="); + String[] streamParts = parts[1].split("-"); + String fromRange = streamParts[0]; + int range = contentLength - 1; + + headers.put("Accept-Ranges", "bytes"); + //headers.put("Content-Length", headers.get("Content-Length")); + headers.put("Content-Range", "bytes " + fromRange + "-" + range + "/" + contentLength); + + /* + int contentLength = Integer.parseInt(contentStr[1]); + String[] parts = rangeString.split("="); + String[] streamParts = parts[1].split("-"); + String fromRange = streamParts[0]; + int range = contentLength - 1; + + headers.put("Accept-Ranges", "bytes"); + headers.put("Content-Length", contentStr[1]); + headers.put("Content-Range", "bytes " + fromRange + "-" + range + "/" + contentLength);*/ + + code = 206; // Partial content being served + //String[] contentLength = request.getRequestHeaders().get("Content-Length").split(","); + + /*int currentRange = Integer.parseInt(rangeString.split("=")[1].replace("-", "")); + int totalRange = Integer.parseInt(contentLength[1].trim()); + + httpConnection.setRequestProperty("Range", rangeString); + //httpConnection.connect(); + + headers.put("Content-Length", contentLength[1].trim()); + headers.put("Content-Range", "bytes " + currentRange + "-" + (totalRange - 1) + "/" + totalRange);*/ + } + // Bypass CORS headers.put("Access-Control-Allow-Origin", "*"); headers.put("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS"); headers.put("Access-Control-Allow-Headers", "agent, user-data, Access-Control-Allow-Headers, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"); - - return new WebResourceResponse(connection.getContentType(), connection.getContentEncoding(), httpConnection.getResponseCode(), httpConnection.getResponseMessage(), headers, connection.getInputStream()); + headers.put("Content-Type", request.getRequestHeaders().get("Content-Type")); + + return new WebResourceResponse(connection.getContentType(), connection.getContentEncoding(), + code, httpConnection.getResponseMessage(), headers, responseStream); + } catch (Exception e) { //an error occurred return null; From d175c3533dba3e3d1259a1346efba1b279bbad57 Mon Sep 17 00:00:00 2001 From: Eduardo Fernandes Date: Thu, 13 Jun 2019 09:20:49 +0100 Subject: [PATCH 7/7] Fixed remaining errors, proxy more stable --- .../cordova/webview/WebViewLocalServer.java | 53 +++---------------- 1 file changed, 7 insertions(+), 46 deletions(-) diff --git a/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java b/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java index 9cfcf4df..6eef1c2f 100644 --- a/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java +++ b/src/android/com/ionicframework/cordova/webview/WebViewLocalServer.java @@ -218,10 +218,6 @@ private static WebResourceResponse createWebResourceResponse(String mimeType, St * @return a response if the request URL had a matching handler, null if no handler was found. */ public WebResourceResponse shouldInterceptRequest(Uri uri, WebResourceRequest request) { - if(isLocalProxySource(uri)) { - return handleLocalProxyRequest(uri, request); - } - PathHandler handler; synchronized (uriMatcher) { handler = (PathHandler) uriMatcher.match(uri); @@ -264,69 +260,34 @@ private WebResourceResponse handleLocalProxyRequest(Uri uri, WebResourceRequest URL httpsUrl = new URL(fixedUri); URLConnection connection = httpsUrl.openConnection(); HttpURLConnection httpConnection = (HttpURLConnection)connection; - InputStream responseStream = connection.getInputStream(); - - Map headers = new HashMap(); - for (Map.Entry> entry : connection.getHeaderFields().entrySet()) { - String key = entry.getKey(); - headers.put(key, entry.getValue().get(0)); - } - int code = httpConnection.getResponseCode(); + Map headers = new HashMap(); if(request != null && request.getRequestHeaders().get("Range") != null) { String rangeString = request.getRequestHeaders().get("Range"); - int contentLength = 0; - - if(responseStream.available() <= 0){ - contentLength = Integer.parseInt(headers.get("Content-Length")); - } else { - contentLength = responseStream.available(); - } + httpConnection.addRequestProperty("Range", rangeString); + + String contentHeader = connection.getHeaderFields().get("Content-Length").get(0); + int contentLength = Integer.parseInt(contentHeader); String[] parts = rangeString.split("="); String[] streamParts = parts[1].split("-"); String fromRange = streamParts[0]; int range = contentLength - 1; headers.put("Accept-Ranges", "bytes"); - //headers.put("Content-Length", headers.get("Content-Length")); + headers.put("Content-Length", contentHeader); headers.put("Content-Range", "bytes " + fromRange + "-" + range + "/" + contentLength); - - /* - int contentLength = Integer.parseInt(contentStr[1]); - String[] parts = rangeString.split("="); - String[] streamParts = parts[1].split("-"); - String fromRange = streamParts[0]; - int range = contentLength - 1; - - headers.put("Accept-Ranges", "bytes"); - headers.put("Content-Length", contentStr[1]); - headers.put("Content-Range", "bytes " + fromRange + "-" + range + "/" + contentLength);*/ - - code = 206; // Partial content being served - //String[] contentLength = request.getRequestHeaders().get("Content-Length").split(","); - - /*int currentRange = Integer.parseInt(rangeString.split("=")[1].replace("-", "")); - int totalRange = Integer.parseInt(contentLength[1].trim()); - - httpConnection.setRequestProperty("Range", rangeString); - //httpConnection.connect(); - - headers.put("Content-Length", contentLength[1].trim()); - headers.put("Content-Range", "bytes " + currentRange + "-" + (totalRange - 1) + "/" + totalRange);*/ } // Bypass CORS headers.put("Access-Control-Allow-Origin", "*"); headers.put("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS"); headers.put("Access-Control-Allow-Headers", "agent, user-data, Access-Control-Allow-Headers, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"); - headers.put("Content-Type", request.getRequestHeaders().get("Content-Type")); return new WebResourceResponse(connection.getContentType(), connection.getContentEncoding(), - code, httpConnection.getResponseMessage(), headers, responseStream); + httpConnection.getResponseCode(), httpConnection.getResponseMessage(), headers, httpConnection.getInputStream()); } catch (Exception e) { - //an error occurred return null; } }