apache/cordova

ERR_CONNECTION_REFUSED despite access origin=* and correct content-secutiy-policy

Closed this issue · 2 comments

Bug Report

Problem

No matter what I do, I get net::ERR_CONNECTION_REFUSED when making a fetch() call to a domain running on http://localhost:8002

What is expected to happen?

fetch('http://localhost:8002') .then(response => { return response.text() }).then(response => alert(response)); should alert hello world

What does actually happen?

I get an error Failed to load resource: net::ERR_CONNECTION_REFUSED

Information

Command or Code

index.html

<!DOCTYPE html>
<!--
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.
-->
<html>
    <head>
        <meta charset="utf-8">
        <!--
        Customize this policy to fit your own app's needs. For more guidance, please refer to the docs:
            https://cordova.apache.org/docs/en/latest/
        Some notes:
            * https://ssl.gstatic.com is required only on Android and is needed for TalkBack to function properly
            * Disables use of inline scripts in order to mitigate risk of XSS vulnerabilities. To change this:
                * Enable inline JS: add 'unsafe-inline' to default-src
        -->
        <meta http-equiv="Content-Security-Policy" content="default-src 'self' http://localhost:8002 data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">


        <meta name="format-detection" content="telephone=no">
        <meta name="msapplication-tap-highlight" content="no">
        <meta name="viewport" content="initial-scale=1, width=device-width, viewport-fit=cover">
        <meta name="color-scheme" content="light dark">
        <link rel="stylesheet" href="css/index.css">
        <title>Hello World</title>
    </head>
    <body>
        <div class="app">
            <h1>Apache Cordova</h1>
            <div id="deviceready" class="blink">
                <p class="event listening">Connecting to Device</p>
                <p class="event received">Device is Ready</p>
            </div>
        </div>
        <script src="cordova.js"></script>
        <script src="js/index.js"></script>
    </body>
</html>

index.js

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

// Wait for the deviceready event before using any of Cordova's device APIs.
// See https://cordova.apache.org/docs/en/latest/cordova/events/events.html#deviceready
document.addEventListener('deviceready', onDeviceReady, false);

function onDeviceReady() {
    // Cordova is now initialized. Have fun!

    console.log('Running cordova-' + cordova.platformId + '@' + cordova.version);
    document.getElementById('deviceready').classList.add('ready');
    fetch('http://localhost:8002')
    .then(response => {
        return response.text()
    }).then(response => alert(response));
}

config.xml

<?xml version='1.0' encoding='utf-8'?>
<!-- <widget id="io.cordova.hellocordova" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0" xmlns:android="http://schemas.android.com/apk/res/android" > -->
<widget id="com.onegini.ExampleAppCordova" version="8.1.0" xmlns="http://www.w3.org/ns/widgets" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
    <name>HelloCordova</name>
    <description>Sample Apache Cordova App</description>
    <author email="dev@cordova.apache.org" href="https://cordova.apache.org">
        Apache Cordova Team
    </author>
    <content src="index.html" />
    <allow-intent href="http://*/*" />
    <allow-intent href="https://*/*" />
    <plugin name="cordova-plugin-whitelist" spec="1" />
    <access origin="*" />

    <!--  -->
    <platform name="android">
        <access origin="*" />
        <allow-intent href="http://*/*" />
        <allow-intent href="https://*/*" />
        <preference name="android-minSdkVersion" value="23" />
        <!-- Necessary to make sure that the deep app linking works properly -->
        <preference name="AndroidLaunchMode" value="singleTask" />
        <!-- <preference name="hostname" value="localhost" />
        <preference name="AndroidInsecureFileModeEnabled" value="true" /> -->
        <edit-config file="AndroidManifest.xml" target="/manifest" mode="merge">
            <manifest xmlns:tools="http://schemas.android.com/tools" />
        </edit-config>
        <edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application">
            <application android:usesCleartextTraffic="true" />
        </edit-config>

        <plugin name="cordova-plugin-whitelist" spec="1" />
        <!-- <resource-file src="google-services.json" target="app/google-services.json" /> -->
    </platform>

    <!--  -->


</widget>

Environment, Platform, Device

package.json

{
  "name": "io.cordova.hellocordova",
  "displayName": "HelloCordova",
  "version": "1.0.0",
  "description": "A sample Apache Cordova application that responds to the deviceready event.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "ecosystem:cordova"
  ],
  "author": "Apache Cordova Team",
  "license": "Apache-2.0",
  "devDependencies": {
    "cordova-android": "^10.1.2",
    "cordova-browser": "^6.0.0",
    "cordova-plugin-whitelist": "1"
  },
  "cordova": {
    "platforms": [
      "browser",
      "android"
    ],
    "plugins": {
      "cordova-plugin-whitelist": {}
    }
  }
}
  • The command I'm using: cordova run android
    It returns CONNECTION_REFUSED for cordova_plugins.js as well, it's not really the issue as far as I know but I feel like it might give insight.

Version information

  • Output from cordova -version: 11.0.0
  • Operating System: Ubuntu 20.04.5
  • No other frameworks
  • The only plugin I'm using would be cordova-plugin-whitelist but as I understand it's been integrated

Checklist

  • I searched for existing GitHub issues
  • I updated all Cordova tooling to most recent version
  • I included all the necessary information above

Things I've Tried

  • Running cordova prepare android followed by cordova build android followed by cordova run android
  • Replacing localhost with my private IP address and placing the API at the doc root
  • Running on browser platform which functions as it should and alerts "hello world" without any connection refused errors
  • Using the following two lines inside the android platform config <preference name="hostname" value="localhost" /> <preference name="AndroidInsecureFileModeEnabled" value="true" />
  • Several different cordova-android versions

I ended up fixing this, part of the problem was that because the app was launched with https, I couldn't access localhost as it was only capable of http so I installed an ssl cert and also used https://10.0.0.2 which on android emulators directs to the hosts doc root and it seems to be working now.

Just for context, Android emulators are full virtual machines with their own virtual network stack. Therefore localhost will resolve to itself, the virtual machine, rather than the host machine. Physical devices will work the same way.

Additionally, if you're using schemes via <preference name="hostname" value="localhost" /> then the webview will (in simple terms) intercept http://localhost or (https://localhost if scheme is set to https) and remap urls to load from the local file system. It somewhat behaves like a virtual webserver that only the process itself can have access to.

That is why changing the url to use a private IP https://10.0.0.2 works. On mac/linux, you can look up your private ip using the ifconfig command, most home networks uses a 192.168.x.x ip scheme