playwright icon indicating copy to clipboard operation
playwright copied to clipboard

[BUG] Private Network Requests Hang When Route Is Registered

Open citizensas opened this issue 3 years ago • 20 comments

Context:

  • Playwright Version: 1.19.0
  • Operating System: Mac
  • Node.js version: 14.17
  • Browser: Chromium

Code Snippet

import {test} from @playwright/test';
test('hangs requests to private network', async ({page}) => {
  page.route('**', route => route.continue()); // <-- this causes the bug
});

Describe the bug Let's say I have a website that fetches something from my local server. If I don't stub any network calls in my test, then all works like expected. But if I have a Route defined in my test, then the request to my local (private) network hangs. Even if I have a route that doesn't match any request throughout the test, it'll still fail.

await page.route('/a-url-that-is-never-called', route => route.continue());

I've created a repository that shows exactly what the issue is about.
https://github.com/citizensas/chromium-private-network-playwright

This is very similar to the #5952 issue.

citizensas avatar Feb 16 '22 08:02 citizensas

I can repro this. Note: this requires some server running on the localhost. Most likely the upstream issue.

dgozman avatar Feb 16 '22 18:02 dgozman

Scenario: some https site does fetch('localhost', { mode: 'no-cors' }), that hangs with interception enabled.

Investigation revealed the following protocol sequence:

  • (event) Fetch.requestPaused(id1, networkId1, GET)
  • (event) Network.requestWillBeSent(networkId1, GET)
  • (command) Fetch.continueRequest(id1)
  • (event) Network.requestWillBeSent(networkId2, OPTIONS)
  • (event) Fetch.requestPaused(id2, networkId2, OPTIONS)
  • (command) Fetch.fulfillRequest(id2)
  • (event) Fetch.requestPaused(id1, networkId1, GET)

So we get requestPaused twice for this request, because Chromium decides to restart it with cors after hitting private network.

Possible solutions:

  • retain requestWillBeSent (until loadingFinished/loadingFailed) and handle second requestPaused for any request;
  • same as above, but only for requests that had cors preflight;
  • introduce Network.willRestartRequest that will be issued instead of an extra requestWillBeSent, and treat it accordingly.

dgozman avatar Feb 16 '22 22:02 dgozman

That's a very good observation @dgozman. I tried to workaround this issue with your first possible solution, but unfortunately that didn't work.

retain requestWillBeSent (until loadingFinished/loadingFailed) and handle second requestPaused for any request;

The problem is that after the first Fetch.continueRequest(id1) command the request stays in pending state and no further event is fired for that requestId (i.e. loadingFinished, loadingFailed, or dataReceived).

I'd like to investigate understanding why the same scenario works when the interception is disabled and why it fails when it is enabled. Do you know where can I start looking in the code?

citizensas avatar Feb 18 '22 14:02 citizensas

@citizensas This is all implemented in crNetworkManager.ts file. We assume one-to-one mapping between requestWillBeSent and requestPaused which is not the case here. The proper solution will most likely be upstream in Chromium though, so I wouldn't try to patch it up in crNetworkManager right away.

dgozman avatar Feb 18 '22 15:02 dgozman

This appears to be happening to us too, Does anyone know if this is being worked on either here or within chromium? Are there other issues on this or other repos progressing this that I should follow instead?

trinode avatar May 27 '22 10:05 trinode

Unfortunately, I have this issue too with Chromium.

philipearnshaw avatar May 27 '22 12:05 philipearnshaw

I have this issue too, quite weird behavior. My browser is Chromium. Wanting a workaround for this, anyone knows? 🙇‍♂️

ducan-ne avatar Jul 15 '22 07:07 ducan-ne

I have this issue too, quite weird behavior. My browser is Chromium. Wanting a workaround for this, anyone knows? 🙇‍♂️

I have a workaround that I use. Let's say the issue happens when I try to send a request to some 127.0.0.1 on port 8080. I define this route to be intercepted and works for me. You can adjust to your specific URL if you want.

await page.route(
  (url) => url.host === '127.0.0.1:8080, 
  async (route, req) => route.fulfill({
    response: await page.request.fetch(req)
}));

citizensas avatar Jul 16 '22 11:07 citizensas

I've deployed an app for testing in private network with using valid SSL wildcard certificate and the request handling doesn't work at all with Chromium (with FF all is OK). In Inspector the request has status code 204, and in the server logs this request didn't receive any data and immediately closed the connection.

balalaiQA avatar Apr 07 '23 08:04 balalaiQA

I've tried @citizensas 's solution, but unfortunately I'm using a self-signed cert so the node process throws Error: route.fetch: unable to verify the first certificate when using that method. I am still seeing this issue in playwright 1.33.0

jondcallahan avatar May 09 '23 23:05 jondcallahan

Any updates on this issue? Just testing out Playwright and I believe hitting this as the symptoms/causes are the same and would completely block our usage of Playwright.

I did find this issue though and it is hard to tell which issue we are being hit by: https://github.com/microsoft/playwright/issues/10376

bigcakes avatar Nov 14 '23 15:11 bigcakes

@bigcakes If you have a repro that we can run locally, please share. That would definitely help with fixing the issue.

dgozman avatar Nov 14 '23 16:11 dgozman

@dgozman Unfortunately this is a private codebase and network that I am working in, but I was able to mostly workaround it in my PoC with this hacky bit of code:

    await page.route("**", async (route, req) => {
      route.fulfill({
        //Have to catch for requests after the page has disposed
        response: await page.request.fetch(req).catch(() => { return {} as APIResponse; }),
      });
    });
    await page.route("**/mySpecificEndpoint", async (route, req) => {
      route.fulfill({
        status: 500,
      });
    });

    await page.goto("/");

bigcakes avatar Nov 14 '23 16:11 bigcakes

@dgozman any sort of debugging I could do to help figure out this issue?

bigcakes avatar Nov 22 '23 15:11 bigcakes

@pavelfeldman @dgozman this will likely be a blocker for many corporations in using Playwright, is there anything I could debug on my side to help get to the bottom of this in Playwright? I am happy to, just need a point in the right direction inside Playwright on how/where to debug this issue as there is no feedback when using it that it is getting hung up

bigcakes avatar Dec 12 '23 15:12 bigcakes

Any resolution for this?

DannyOnfido avatar Mar 19 '24 14:03 DannyOnfido

Looks like this is fixed on the newer versions of Chromium. I installed the browser with the latest Playwright version and it works. If most of the affected users confirm that the issue is indeed fixed for them too, then we can close this issue.

citizensas avatar Apr 01 '24 17:04 citizensas

Works for me as well 🎉

aoj2 avatar May 03 '24 09:05 aoj2

Same here!

nhlatour avatar May 03 '24 13:05 nhlatour

Confirmed this is good now

bigcakes avatar May 03 '24 14:05 bigcakes