capacitor: bug: Android web view encapsulate 302 redirection response

Bug Report

Capacitor Version

πŸ’Š   Capacitor Doctor  πŸ’Š

Latest Dependencies:

  @capacitor/cli: 2.4.6
  @capacitor/core: 2.4.6
  @capacitor/android: 2.4.6
  @capacitor/electron: 2.4.6
  @capacitor/ios: 2.4.6

Installed Dependencies:

  @capacitor/cli 2.4.2
  @capacitor/android 2.4.2
  @capacitor/core 2.4.2
  @capacitor/ios 2.4.2
  @capacitor/electron not installed

Platform(s)

Only on android

Current Behavior

When the android web view navigates to a URL that supposed to returns a 302 redirection response to a different domain, a 200 response was received with the HTML content. So that the web view does not perform any URL redirection. Instead the window URL remain unchanged. Assets (eg. images, CSS, static JS files) with relative URLs in the HTML reference the original window URL rather than the redirected URL. A broken HTML page is shown as a result of missing assets required by the HTML.

Expected Behavior

When navigating to URL which returns a 302 response, the web view should perform the redirection instead of it being resolved by Capacitor. The web view window location should be updated to the redirected location and the assets with relative URL in the HTML should be resolved with the correct URL path

Code Reproduction

Navigating to any URL which returns a 302 redirection response to a different domain produce this issue.

I made a repo to reproduce the issue here: https://github.com/mrkelvinli/capacitor-android-issue-4240-reproduction

Other Technical Details

npm --version output: 6.14.10

node --version output: v14.15.4

pod --version output (iOS issues only):

Additional Context

I did some investigation on this in @capacitor/android

When the web view made a request to the serverUrl domain, Capacitor will proxy the network request from the web view into WebViewLocalServer. If the request in shouldInterceptRequest is pass into handleProxyRequest(), that means it has a PathHandler associated to the hostname of the request. If the request also has text/html in the HTTP accept header, then the HttpURLConnection object which performs the network request will follow redirects by default (as HttpURLConnection::setFollowRedirects() is true by default). Thus, all successfully request will result in a 200 (status code is from the PathHandler associated to the request hostname) response and redirection will be resolved within HttpURLConnection instead of the web view.

Solution I propose

diff --git a/node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java b/node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java
index d632065..bc10831 100755
--- a/node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java
+++ b/node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java
@@ -311,6 +311,28 @@ public class WebViewLocalServer {
           conn.setRequestMethod(method);
           conn.setReadTimeout(30 * 1000);
           conn.setConnectTimeout(30 * 1000);
+
+          /**
+           * This should be the final step in the setup process of the HttpURLConnection object.
+           *
+           * Initiate the connection with follow redirects disable. It allows us
+           * identifying any redirection response and avoid intercepting such request.
+           * This is because returning a 200 response when a redirection response (ie. 301 or 302)
+           * is expected could result in issues with resolving relative URLs for static assets
+           * in the HTML.
+           */
+          conn.setInstanceFollowRedirects(false);
+          conn.connect();
+          int status = conn.getResponseCode();
+          if (
+            status == HttpURLConnection.HTTP_MOVED_TEMP ||
+              status == HttpURLConnection.HTTP_MOVED_PERM ||
+              status == HttpURLConnection.HTTP_SEE_OTHER
+          ) {
+            // Return null so that this request will not be intercepted.
+            return null;
+          }
+
           String cookie = conn.getHeaderField("Set-Cookie");
           if (cookie != null) {
             CookieManager.getInstance().setCookie(url, cookie);

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 18
  • Comments: 21

Commits related to this issue

Most upvoted comments

@mrkelvinli, you are a lifesaver! Been really stuck on this for some days; your patch works fine. Hope soon to be merged.

We are facing this issue now to when integration with third party provider.

Your patch made my day! Thanks a lot!

I’m having the same issue, but some URLs the app can redirect, some cannot. So strange 😦

It’s a working method. Thank you! But why I can see no pull request?