[BUG] Private Network Requests Hang When Route Is Registered
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.
I can repro this. Note: this requires some server running on the localhost. Most likely the upstream issue.
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.
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 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.
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?
Unfortunately, I have this issue too with Chromium.
I have this issue too, quite weird behavior. My browser is Chromium. Wanting a workaround for this, anyone knows? 🙇♂️
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)
}));
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.
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
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 If you have a repro that we can run locally, please share. That would definitely help with fixing the issue.
@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("/");
@dgozman any sort of debugging I could do to help figure out this issue?
@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
Any resolution for this?
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.
Works for me as well 🎉
Same here!
Confirmed this is good now