Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(plugins): follow redirects when checking the webServer url #22035

Merged
merged 1 commit into from
Apr 5, 2023

Conversation

ghost91-
Copy link
Contributor

@ghost91- ghost91- commented Mar 28, 2023

Fixes #22144

This change implements following redirects when a 3xx HTTP status code is received, when probing the url of a webServer config.

Without this change (i.e., currently), playwright still tries to follow the redirect, but often fails, since it incorrectly uses the received Location header as new URL, instead of combining it with the URL from the previous request.

For example, if the URL is http://localhost:8080/some-path and a redirect with Location: /some-other-path is received, playwright incorrectly tries to query http://localhost:80/some-other-path, which of course fails, due to the incorrect port.

An alternative solution would be to not follow redirects at all when probing the URL, and just consider a 3xx status code as "server is available". This is actually what the documentation claims will happen right now, but it's incorrect. I chose this solution over not following the redirect, since it makes sense, in my opinion, and not following the redirect would require more code changes (httpRequest would need some kind of option that prevents it from following redirects). But if that solution would be preferred, I am happy to change the implementation.

Signed-off-by: Johannes Loher <johannes.loher@tngtech.com>
'playwright.config.ts': `
module.exports = {
webServer: {
command: 'node ${JSON.stringify(SIMPLE_SERVER_PATH)} ${server.PORT}',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
command: 'node ${JSON.stringify(SIMPLE_SERVER_PATH)} ${server.PORT}',
command: 'exit 1',

I think we can do this to make clear that we should never run the command, since reuseExistingServer is true.

Copy link
Contributor Author

@ghost91- ghost91- Mar 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could also be done for some of the other tests, e.g., should send Accept header or should be able to use an existing server when reuseExistingServer:true. Should I also add it for those tests? If so, should that be done in the same commit or in a separate one?

@@ -595,7 +595,7 @@ export default defineConfig({
- type: ?<[Object]|[Array]<[Object]>>
- `command` <[string]> Shell command to start. For example `npm run start`..
- `port` ?<[int]> The port that your http server is expected to appear on. It does wait until it accepts connections. Exactly one of `port` or `url` is required.
- `url` ?<[string]> The url on your http server that is expected to return a 2xx, 3xx, 400, 401, 402, or 403 status code when the server is ready to accept connections. Exactly one of `port` or `url` is required.
- `url` ?<[string]> The url on your http server that is expected to return a 2xx, 3xx, 400, 401, 402, or 403 status code when the server is ready to accept connections. Redirects (3xx status codes) are being followed and the new location is checked. Exactly one of `port` or `url` is required.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the simple formula that we currently document:

The url on your http server that is expected to return a
2xx, 3xx, 400, 401, 402, or 403 status code when the
server is ready to accept connections.

Does that not work? We can prevent probe from following the redirects if current behavior does not much the doc above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I mentioned that in the PR description: At the moment, the docs don’t match reality: redirects are attempted to be followed, but it’s buggy. I am fine with both not following redirects, or fixing redirects. But not following redirects is more work, because the underlying utility (httpRequest) does not support that yet. And I wanted to keep things as simple as possible. But if that’s the preferred solution, I’m also Ok with that. I’d probably need some guidance on how to add that „don’t follow redirects“ option to httpRequest.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/utils/network.ts#L78 we are using http.request for that, which should not follow redirects. Could you add a failing tests that surfaces the bug?

Copy link
Contributor Author

@ghost91- ghost91- Apr 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

http.request may not follow redirects, but there is dedicated code in httpRequest to do so: https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/utils/network.ts#L73

But this code is broken, which is the whole point of this PR. At the moment it fixes the broken redirect implementation. But I’m also happy to remove the redirect behavior, if that’s preferred.

Regarding a test case that fails, here is the simplest one I can think of:

test('should not follow redirects but accept them as "server is available"', async ({ runInlineTest, server }) => {
  server.setRedirect('/redirect', '/redirected-to');
  const result = await runInlineTest({
    'test.spec.ts': `
      import { test, expect } from '@playwright/test';
      test('dummy test', async () => {
        expect(true).toBe(true);
      });
    `,
    'playwright.config.ts': `
      module.exports = {
        webServer: {
          command: 'exit 1',
          url: 'http://localhost:${server.PORT}/redirect',
          reuseExistingServer: true,
        }
      };
    `,
  });
  expect(result.exitCode).toBe(0);
});

@@ -71,7 +71,7 @@ export function httpRequest(params: HTTPRequestParams, onResponse: (r: http.Inco
const requestCallback = (res: http.IncomingMessage) => {
const statusCode = res.statusCode || 0;
if (statusCode >= 300 && statusCode < 400 && res.headers.location)
httpRequest({ ...params, url: res.headers.location }, onResponse, onError);
httpRequest({ ...params, url: new URL.URL(res.headers.location, params.url).toString() }, onResponse, onError);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
httpRequest({ ...params, url: new URL.URL(res.headers.location, params.url).toString() }, onResponse, onError);
httpRequest({ ...params, url: new URL(res.headers.location, params.url).toString() }, onResponse, onError);

change this to that and import * as URL from 'url'; to import * as url from 'url';.

Clarified offline with Pavel that we want to continue to follow redirects.

Copy link
Member

@mxschmitt mxschmitt Apr 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually its fine, we used to do this before. Merging in since require('url').URL === URL.

@mxschmitt mxschmitt merged commit bd698ef into microsoft:main Apr 5, 2023
20 checks passed
github-actions bot added a commit that referenced this pull request Apr 5, 2023
mxschmitt pushed a commit that referenced this pull request Apr 5, 2023
…hecking the url (#22204)

This PR cherry-picks the following commits:

- bd698ef

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[BUG] WebServer only starting after timeout
3 participants