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

[Bug]: Upgrade to Playwright 1.44 Causes Incomplete multipart/form-data Request Body #30788

Closed
barnabas-nagy-ni opened this issue May 14, 2024 · 9 comments · Fixed by #30863
Closed
Assignees
Labels

Comments

@barnabas-nagy-ni
Copy link

Version

1.44

Steps to reproduce

We've been using Playwright for our end-to-end tests for a while now, and our tests have been working fine up to version 1.43.1. But after we upgraded to Playwright 1.44, our file upload tests started acting up.

The main issue is when we send the file to the server, the multipart/form-data request body is incomplete. The WebKitFormBoundary isn't closing properly, and the file content isn't getting added to the request body. This issue is causing our tests to fail because the server is unable to parse the incomplete request body.
image
Here's the code we're using to send the request:

public static async sendFile(page: Page, method: string, url: string, fileName: string, fileContent: any): Promise<Response> {
        return page.evaluate(async (parameters: RequestParameters) => {
            const url = parameters.url;
            const method = parameters.method;
            const fileName = parameters.fileName!;
            const fileContent = parameters.fileContent;
            const formData = new FormData();
            const newFile = new File([fileContent], fileName);
            formData.append('file', newFile, fileName);
            return window
                .fetch(url, { method, credentials: 'include', body: formData })
                .then(async response => {
                    if (response.status < 200 || response.status > 299) {
                        const error = `Error calling ${method} ${url}: ${response.status}`;
                        console.error(error);
                        throw new Error(error);
                    }

                    return response.text().then(text => {
                        const json = text ? JSON.parse(text) : text;
                        return { status: response.status, body: json };
                    });
                });
        }, { method, url, fileName, fileContent });
    }

I did manage to find a workaround by using the new context request post method:

        await context.request.post({
            multipart: formData
        });

But we also have other tests where we add the file using the setInputFiles method:

await page.setInputFiles('.file-input', `./tests-file`);

This method automatically sends the file to the server, but the request body has the same issue. I couldn't find a workaround for this one. And since everything was working fine until the new version, I thought I'd file a bug.

Expected behavior

The playwright tests should generate the multipart/form-data request body correctly.

Actual behavior

The WebKitFormBoundary isn't closing properly, and the file content isn't getting added to the multipart/form-data request body.

Additional context

The UI is written in Angular. The backend, which processes the file, is written in Elixir.

Environment

Binaries:
    Node: 20.12.2
    npm: 10.5.0
  IDEs:
    VSCode: 1.88.1
  npmPackages:
    @playwright/test: ~1.44.0 => 1.44.0
@yury-s
Copy link
Member

yury-s commented May 14, 2024

You have two snippets, one of them uses web platform's fetch and FormData, it should not be affected by playwright, it might be a change caused by newer browser version. As far as I understand, the second example works fine when you say multipart: formData which is expected usage. Can you provide full reproduction example where the form data is not sent properly?

@barnabas-nagy-ni
Copy link
Author

barnabas-nagy-ni commented May 15, 2024

I apologize for any confusion. I provided two code snippets because we're dealing with two different types of file upload tests. The first one generates a file and sends it directly to the server programmatically using window.fetch. The second one uses page.setInputFiles to add the file to the Angular UI, which then sends the file to the server. Both of these methods generate a multipart: formData request. However, if I upgrade the Playwright version to 1.44, both tests fail as previously described. I managed to find a workaround for the first method using context.request.post. However, the second method still fails. I couldn't find a workaround for this because the file upload is entirely handled by the browser/Playwright.

I can confirm that if I use the same browser version in the same environment with Playwright 1.43.1, the tests pass. However, if I upgrade the Playwright version to 1.44, the tests fail due to an incomplete request body, as previously described.

After some investigation, we found that the cause of the issue is that the new version of playwright is somehow incompatible with our opentelemetry tracer setup.

I created a minimal reproduction environment, please find it attached. Please also find additional information in the README.md
Playwright-bug.zip

NOTE: The provided test will also fail with Playwright version 1.43.1 because the tracer isn't set up properly, but the file will be sent to the server successfully.

@servohatred
Copy link

servohatred commented May 16, 2024

This is also happening to me. Tried the exact same request with postman and axios (it was a 200 on both), using formdata but it doesnt work on playwright (Using the same formdata on both but I get a 500 with playwright). Not sure if playwright somehow is malforming the request.
image

@servohatred
Copy link

servohatred commented May 16, 2024

Not sure if it has something to do with boundaries..... but most of the multipart/form-data objects require a boundary set up in the content-type header (also a calculated content-lenght header is required).

Content-Type: multipart/form-data; boundary="yet another boundary"

--yet another boundary
Content-Disposition: form-data; name="foo"

bar
--yet another boundary
Content-Disposition: form-data; name="baz"

quux
--yet another boundary
Content-Disposition: form-data; name="feels"
Content-Type: text/plain; charset=utf-8

Boundaries can look like this: ----WebKitFormBoundaryNAoDVXaXyNhjWxtO or like this --------------------------377218823638245114258521

If I try to run my request from postman it will not work when I do not set any of those headers. But then they have this hidden/self-calculated headers... if I enable both , my request works.
image
some other libraries like axios creates automatically a boundary and then creates the header for Content-Type, they basically infer all these values internally (if you do not pass any content-lenght/content-type. or undefined, axios infers the values (including boundaries) from the multipart object) so , even if I do not pass any header for content, my request is successfull in axios.

When I run the same request in playwright I get the same error I get in postman when I do not pass any header for content-length/content-type. Playwright is also using the FormData library from typescript but there is another one were all these concepts are included , this is a node library .

import FormData from 'form-data';

function postYourRequest(url, data){
    const formData = new FormData();
        formData.append("your", data.value);
        formData.append("your", data.file); //works with duplicated field names
    const contentLength = formData.getLengthSync();
    let response = await axios({
      method: "post",
      url: URL,
      data: formData,
      headers: {
        "Content-Length": `${contentLength}`,
        ...formData.getHeaders()  // this will return this { 'content-type': 'multipart/form-data; boundary=--------------------------377218823638245114258521'} which includes the boundary corresponding to the formdata
      },
}

Typescript global library for FormData only includes the append method but not all the others to calculate the content-length/type . Also the real issue : If we dont set up these headers playwright does nothing about that and the request will fail. I tried getting all these values from the other form-data library and setup the header in playwright but looks like there is another issue..... probably the header for Content-Type is getting malformed, if you include the boundary parameter in the content-type, a 50 digit boundary is required, if it has one character less it will fail(if the boundary is less than 50 digits , remaining digits should be filled with dashes) .

yury-s added a commit to yury-s/playwright that referenced this issue May 17, 2024
The bug was fixed in microsoft#30734.
This PR adds a test and updates interception logic to not send post data
when it is not modified.

Fixes microsoft#30788
yury-s added a commit to yury-s/playwright that referenced this issue May 17, 2024
The bug was fixed in microsoft#30734.
This PR adds a test and updates interception logic to not send post data
when it is not modified.

Fixes microsoft#30788
@yury-s yury-s self-assigned this May 17, 2024
@yury-s yury-s added the v1.45 label May 17, 2024
@yury-s
Copy link
Member

yury-s commented May 17, 2024

This is already fixed in @next version of playwright.

yury-s added a commit to yury-s/playwright that referenced this issue May 17, 2024
The bug was fixed in microsoft#30734.
This PR adds a test and updates interception logic to not send post data
when it is not modified.

Fixes microsoft#30788
@barnabas-nagy-ni
Copy link
Author

@yury-s, thank you for the fix. Could you please provide the release date for the new 1.45 version?

@yury-s
Copy link
Member

yury-s commented May 20, 2024

It will be published in the next few weeks, maybe sooner.

yury-s added a commit that referenced this issue May 20, 2024
The bug was fixed in #30734.
This PR adds a test and updates interception logic to not send post data
when it is not modified.

Fixes #30788
@ajssd
Copy link

ajssd commented Jun 25, 2024

@barnabas-nagy-ni @servohatred just wondering whether 1.45.0 fixed this issue for either of you. The 1.44.0 release introduced a problem for us running Playwright from a recorded HAR file (using routeFromHAR), when the HAR contains a multi-part form data POST. It seemed related to the boundaries, but I am not quite sure. I was hoping it was the same as your issue, and fixed in 1.45.0, but it was not.

@barnabas-nagy-ni
Copy link
Author

@ajssd for us it fixed the issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants