ProxymanApp / Proxyman

Modern. Native. Delightful Web Debugging Proxy for macOS, iOS, and Android ⚑️
https://proxyman.io
5.56k stars 185 forks source link

Debugging React Native on Android breaks when using Proxyman #1407

Open Justin-Credible opened 1 year ago

Justin-Credible commented 1 year ago

Description

When using Proxyman as a proxy for an Android device's traffic while running a React Native application in development mode, if React Native debugging is disabled, the app does not work. React will stop with the fatal error Failed to connect to debugger! Timeout while connecting to remote debugger.

When this occurs you can see the websocket traffic in Proxyman: localhost_10-20-2022-15-28-04.proxymanlog.zip

It appears that Proxyman is intercepting the websocket requests which I believe React Native uses for debugging. Something isn't quite working correctly though.

Screen Shot 2022-10-20 at 3 31 58 PM

Steps to Reproduce

  1. Setup Proxyman as a proxy for an Android emulated or physical device
  2. Launch a React Native app in development mode npx react-native start
  3. Ensure the Android device has 8081 forwarded to the local dev machine adb reverse tcp:8081 tcp:8081
  4. Run the app on the Android emulated/physical device
  5. Open the React Native dev menu adb shell input keyevent 82
  6. Enable debugging

Current Behavior

Application restarts in debugging mode, but immediately fails with: Failed to connect to debugger! Timeout while connecting to remote debugger.

Expected Behavior

Application restarts in debugging mode and works without any errors.

Environment

Additional notes

This does not affect iOS when using either the iOS simulator or a physical iOS device. In this case I've noticed that websocket traffic does not show up at all in Proxyman; perhaps this is why iOS doesn't have the same issue?

NghiaTranUIT commented 1 year ago

Thanks for the detailed report @Justin-Credible

This does not affect iOS when using either the iOS simulator or a physical iOS device. In this case I've noticed that websocket traffic does not show up at all in Proxyman; perhaps this is why iOS doesn't have the same issue?

You're right. Websocket on iOS doesn't respect the HTTP Proxy, so it won't go through Proxyman app.

On the other hand, Android does. Maybe it's the reason why your Android app (React Native) doesn't work. I'm going to investigate it now.

Maybe I should bypass all React Native debugging Websocket instead of intercepting it.

NghiaTranUIT commented 1 year ago

Hey @Justin-Credible I've spent hours investigating, but the npx react-native run-android is extremely unreliable 😒

I've followed the official guideline from React Native at https://reactnative.dev/docs/environment-setup

Without opening Proxyman, 9 out of 10, the command just returns an error. I have to remove the emulator and create a new one, it runs successfully Once, then breaks again.

Screen Shot 2022-10-25 at 14 39 38

Thus, I could not reliably reproduce the bug. I suppose that it's about the WS via Proxyman when you use adb reverse tcp:8081 tcp:8081.

If you don't mind, please try to remove all reverse, e.g adb reverse --remove-all, and try again, it might fix the problem.

Justin-Credible commented 1 year ago

Running adb reverse tcp:8081 tcp:8081 is required during development in most cases, as this is how React Native normally loads the application bundle. I tried it without using adb reverse by and setting the bundle location via React Native's debug menu to a local network IP/port (e.g. 192.168.1.20:8081), but as soon as ProxyMan was enabled as the proxy, React Native debugging broke. (as a side note I noticed that Charles Proxy does not exhibit this problem)

To work around the issue I made a Proxy auto-config script to tell Android to bypass the proxy for localhost only, while proxying all other hosts. This allowed me to work around the ProxyMan/websocket issue.

function FindProxyForURL(url, host) {
  if (host.indexOf('localhost') > -1) {
    return null;
  }

  return "PROXY 192.168.1.60:9090";
}
NghiaTranUIT commented 1 year ago

Thanks for the update. I will try again to reproduce and fix it this weekend πŸ‘

yoniholmes commented 1 year ago

Hi @Justin-Credible, thanks very much for raising this ticket and sharing your work around. Would you be able to elaborate on your Proxy auto-config solution? Where do you place this code / how is it run?

I'm facing a similar issue. My React Native app is successfully proxying all network traffic to Proxyman only what I believe are the metro bundler calls, /inspector, /message, /status are all resulting in Internal Error 999 and therefore the app returns an error saying that it cannot connect to the development server. ’adb reverse tcp:/8081 tcp:8081’ is not helping - hence would like to try to prevent these calls from proxying through Proxyman.

Many thanks to anyone in advance with any thoughts!

Justin-Credible commented 1 year ago

@yoniholmes

Sure, it looks like this:

  1. Create a proxy.pac file with the contents of the above post
  2. Change 192.168.1.60:9090 part of the script to be the IP/port of your Proxyman instance
  3. Place proxy.pac on a webserver and ensure it is served with a Content-Type: application/x-ns-proxy-autoconfig response header
  4. Edit the Android device's WiFi settings; set Proxy to Proxy Auto-Config
  5. Enter the full URL to the proxy.pac file and save the changes

From now on, the contents of the proxy.pac script will execute for each URL to determine if it should be passed through the proxy or not. The simple script above will ignore any requests to localhost which should be the metro bundler. Everything else will be proxied.

Hope this helps!

yoniholmes commented 1 year ago

Thanks! I’ll give it a try. I managed to point my wifi config to a server hosting the pac file but I didn’t serve it with the Content-Type: application/x-ns-proxy-autoconfig header. Thanks for coming back to me! 🀞

On Fri, 17 Mar 2023 at 21:17, Justin Unterreiner @.***> wrote:

@yoniholmes https://github.com/yoniholmes

Sure, it looks like this:

  1. Create a proxy.pac file with the contents of the above post https://github.com/ProxymanApp/Proxyman/issues/1407#issuecomment-1291126992
  2. Change 192.168.1.60:9090 part of the script to be the IP/port of your Proxyman instance
  3. Place proxy.pac on a webserver and ensure it is served with a Content-Type: application/x-ns-proxy-autoconfig response header
  4. Edit the Android device's WiFi settings; set Proxy to Proxy Auto-Config
  5. Enter the full URL to the proxy.pac file and save the changes

From now on, the contents of the proxy.pac script will execute for each URL to determine if it should be passed through the proxy or not. The simple script above will ignore any requests to localhost which should be the metro bundler. Everything else will be proxied.

Hope this helps!

β€” Reply to this email directly, view it on GitHub https://github.com/ProxymanApp/Proxyman/issues/1407#issuecomment-1474410933, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABNCDOJZK4ADLDNNYUSUDTW4TIGFANCNFSM6AAAAAARKTROMY . You are receiving this because you were mentioned.Message ID: @.***>

yoniholmes commented 1 year ago

I managed to get this working. Steps:

  1. Created an express server, hosting a proxy.pac, returning with a Content-Type: application/x-ns-proxy-autoconfig header, as per the contents shared above, i.e.:

    function FindProxyForURL(url, host) {
    if (host.indexOf('localhost') > -1) {
    return null;
    }
    if (host.indexOf('10.0.2.2') > -1) {
    return null;
    }
    
    return "PROXY 192.168.1.60:9090"; // Replaced with my IP address 
    }
  2. Within my Android Emulator I disabled Mobile data, to ensure connectivity to the internet went solely through WiFi:
    • Settings > Network & Internet > Mobile network > Mobile data (switched it off)
  3. I then configured my Wifi Proxy to point to the pac file:
    • Settings > Network & Internet > AndroidWifi > Pencil icon > Proxy (set to 'Proxy Auto-Config') > Pac URL (set to http://[my IP Address]:8080), note I don't specify proxy.pac in the URL
  4. I then re-ran the 'Override Emulator' script from Proxyman (in Proxyman: Certificate > Install Certificate on Android > Emulators > Override Emulator

After this point all traffic from my Android was passing through Proxyman, and there were no issues with the Metro bundler failing to connect.

yoniholmes commented 1 year ago

So, while this worked for about a day - and what a glorious day it was - the setup later stopped working. I use a VPN so my IP address changed. But no amount of updating IP addresses have fixed the issue, so I can no longer inspect network traffic with Proxyman with my Android Emulator. I've spent at least a day trying to get it to work - but the Emulator consistently fails to attach to the metro bundler, both with and without the proxy.pac file in use.

Would be great if someone could share and insight or success with using Proxyman with Android. Many thanks in advance.

NghiaTranUIT commented 1 year ago

@yoniholmes do you have any current solution with other tools like Charles Proxy + React Native? Maybe I should look into it and make it easier πŸ€”

yoniholmes commented 1 year ago

Hi @NghiaTranUIT, thanks. I haven't tried Charles Proxy.

With React Native Debugger I can see most network calls. However, I cannot see network calls from web views (I can with Proxyman on iOS, and once on Android). Also, React Native Debugger doesn't let me modify requests or responses. I really like Scripting with Proxyman, and would love to use it to debug the Android specific bug I'm facing.

Only, as mentioned, I cannot get this to work. To be clear, Android can successfully pass traffic to Proxyman, which I can inspect. But the React Native app fails to connect to the metro bundler, which is a fatal error meaning I cannot test my app.

NghiaTranUIT commented 1 year ago

To intercept traffic from the Webview inside the Android app, you should check this doc: https://docs.proxyman.io/debug-devices/android-device#intercept-traffic-from-embedding-webview


Do you any sample React Native App that I can reproduce it?

@yoniholmes can I use https://reactnative.dev/ to create an app for testing?

yoniholmes commented 1 year ago

Re: Webview inpsection Actually this was working fine, I didn't need to make any further configuration changes

Re: Sample React Native App Yes that site should get you set up quite quickly. I don't have a sample app for you at the moment, and may not be able to set one up immediately.

yoniholmes commented 1 year ago

These are the errors I get. With Proxyman off, Android works fine.

IMG_6657

NghiaTranUIT commented 1 year ago

@yoniholmes Can you try to add this IP to the Bypass list in the Wifi?

You can find it at System Setting -> Wifi -> Details -> Proxy -> Bypass Proxy Setting

CleanShot 2023-04-05 at 17 09 54@2x

yoniholmes commented 1 year ago

Sure I'll try that and let you know.

NghiaTranUIT commented 1 year ago

Just wondering what Proxyman for macOS version you're using?

I've tried on the latest build, and it seems the Metro is still working fine πŸ€” I can update the App.js and hot-reload is still working.

Screenshot 2023-04-05 at 17 23 51

yoniholmes commented 1 year ago

Thanks for sharing. I'm using Proxyman 4.5.0 (45000).

Still failing for me after putting 10.0.2.2 into my wifi proxy bypass settings.

I'm not sure whether Expo might play any role – that port 19000 looks to be related to Expo, which is used in the React Native quick start. My project doesn't use Expo, so am not sure whether that's a factor.

I've tried both with and without VPN. Traffic from Android is all proxying to Proxyman, but the metro bundler 10.0.2.2:8081 calls all fail with code 999, Status 'Internal Error'

The message in the 'Response' panel in Proxyman says:

The operation couldn't be completed.
(NIOCore.ChannelError error O.) (code=0)
NghiaTranUIT commented 1 year ago

@yoniholmes I found a solution for React Native via Metro Bundler πŸ‘

Android Emulator

  1. Open Proxyman -> Certificate -> Install for Android -> Emulator -> Click on the "Revert the Proxy"
  2. Open Android Emulator -> Setting App -> Network -> Wifi -> Find a way to change the proxy
  3. Change the HTTP Proxy manually by using the Proxyman IP & Port
  4. Before saving, enter the localhost in the bypass Proxy List βœ…

CleanShot 2023-04-05 at 22 28 12 2@2x

  1. Done
  2. The Bridge Was shutdown warning is gone βœ…

Android Device

  1. Open Android Emulator -> Setting App -> Network -> Wifi -> Find a way to change the proxy
  2. Change the HTTP Proxy manually by using the Proxyman IP & Port
  3. Before saving, enter the your IP in the bypass Proxy List βœ… . In your case, it's 10.0.2.2
  4. Done
yoniholmes commented 1 year ago

Hi @NghiaTranUIT, yes! Thank you very much, I now have Android working with Proxyman. Truly appreciated, was stuck on this for a long time!

After making the manual WiFI proxy changes within Android (I'd switched off mobile data previously, so the only network I was using as the Emulator's WiFi network), along with the bypass settings (I added "localhost,10.0.2.2" to be sure, but I can test the difference later), then I could run my app, no metro bundler connectivity issues, and I could inspect network traffic from my app. πŸŽ‰

Without adding the res/xml/network_security_config.xml, the metro bundler would still connect, and I could see the domains that my app was calling, but I could not inspect them. After restoring the network_security_config.xml file, I could then run my Android app, no metro bundler issues, and I could inspect the calls being made from the app.

Thanks again & hope you enjoy the rest of your week!

NghiaTranUIT commented 1 year ago

Awesome, I will add my instruction to the React Native document page.

coofzilla commented 7 months ago

I was having a similar issue. Does the automated setup work with react-native ? Basically pressing this override emulator or do you have to manually add it in the actual settings of the emulator?

Also, do you have to add it to both settings?

Referencing this automated setup. image

Current setup:

image

but then I get the same like above:

image

I tried those steps; but, still facing the same issue :/

Got it fixed

image

Can someone enlighten me as to why that is needed ? πŸ€”

NghiaTranUIT commented 7 months ago

@coofzilla:

Does the automated setup work with react-native ?

Yes, it's.

Basically pressing this override emulator or do you have to manually add it in the actual settings of the emulator?

You only either use the Emulator or manual setting for Android devices. Don't need to use twice.

Also, do you have to add it to both settings?

If you mean the step 3 that you modify the network.xml, it's required.

coofzilla commented 7 months ago

@NghiaTranUIT Thank you for the response, the only issue I have now is, when I quit proxyman. I have to undo everything to get it to work normally again. Conversely, setting up, I have to redo everything I just undid such as setting up all the local host stuff. Is that intended?

NghiaTranUIT commented 7 months ago

Yes, you have to revert all proxy settings if you don't use Proxyman on your emulator.

Please click on the Revert Button at the end πŸ‘

Screenshot 2024-02-15 at 09 06 45

Proxyman uses the adb command line to modify your Android emulator.

MariuzM commented 2 months ago

I don’t know on Android with RN+Expo nothing works for me :(

image

And tried again

image image

Tried new emulator and just follow the steps for RN but nothing works

wijskinner commented 1 month ago

Really struggling here as well. Network traffic all going through but metro telling me to do one.

image image image
NghiaTranUIT commented 1 month ago

@wijskinner if you don't mind, can you share with me a sample Metro React Native project that I can reproduce?

Maybe the Getting Started project from Metro will work?

NghiaTranUIT commented 1 month ago

@wijskinner from what I know, React Native (Expo) is working fine with Proxyman, and Metro is deprecated, right?

On the React Native website, I don't see any Setup for Metro anymore.

wijskinner commented 1 month ago

@NghiaTranUIT Thanks for response. They have fairly recently switched to recommending expo as the default setup. Metro is just the bundler that react-native and expo uses under the hood to enable live bundling/reloading of the underlying javascript. There are a LOT of non expo react-native projects out there as back in the day there were a lot of restrictions on what you could do with a non ejected expo project. It is virtually impossible to switch to expo on a large codebase that didn't start with it.

With regards to metro - there isn't a setup as such. It is included with a normal react native setup. I'm guessing the difference between expo bundler and vanilla metro will be to do with how it handles ports and reverse proxying etc on android.

Setup for RN app without expo is still in the docs https://reactnative.dev/docs/getting-started-without-a-framework

wijskinner commented 3 weeks ago

@NghiaTranUIT any update on this?

NghiaTranUIT commented 2 weeks ago

@wijskinner not yet, let me investigate the RN Metro with Proxyman and get back to you πŸ‘

NghiaTranUIT commented 2 weeks ago

@wijskinner @Justin-Credible good news: Here is the solution πŸŽ‰

Please note that this issue is not Proxyman's bug. It also happens on Charles Proxy.

React Native - Metro - iOS app

Screenshot 2024-09-15 at 9 41 06β€―AM

React Native - Metro - Android Emulator

  1. Open Proxyman -> Tools menu -> Map Remote
  2. Add this config:
    • Rule: http://10.0.2.2:8081
    • Check on "Include all subpath"
    • Select ANY Method
    • Protocol: http
    • Host: 127.0.0.1
    • Port: 8081
    • Done

Screenshot 2024-09-15 at 11 25 53β€―AM

  1. Certificate menu -> Install for Android -> Emulator -> Click on Override Emulator -> It auto set the Proxy and install the certificate in 1 click
  2. Reload your Metro bundler
  3. Done: No Metro Bundler error βœ…

Proxyman decrypts HTTPS from React Native Android Metro Bundler


If you'd like to decrypt HTTPS on your Android Emulator, make sure to add the network.xml config to your Android project.

NghiaTranUIT commented 2 weeks ago

I will add some logic to automatically fix this issue in build v5.9.0, so it can work out of the box. No need to use Map Remote πŸ‘

NghiaTranUIT commented 2 weeks ago

cc @wijskinner @Justin-Credible @MariuzM @coofzilla @yoniholmes

If you don't mind, let's try this Beta build: https://download.proxyman.io/beta/Proxyman_5.8.0_Fix_React_native_with_metro_bundler_for_android.dmg

Changelog

Video

https://github.com/user-attachments/assets/de9d93b1-a13a-4dab-a44a-c273a3d468fc

wijskinner commented 5 days ago

Sorry just caught up here. Will try that build and report back. Thanks a bunch for looking at this.

wijskinner commented 5 days ago

Working fine over here. Thanks again. πŸ‘