Skip to content

Content-Disposition without quoted parameters fails #4565

Closed

Description

@bart-vmware

When the parameters in the Content-Disposition HTTP header field are not surrounded by double quotes, the request is rejected by the server with error 500. According to https://datatracker.ietf.org/doc/html/rfc5987#section-3.2.2, unquoted values should be permitted. The example request below originates from HttpClient, which is part of the .NET libraries. HttpClient uses unquoted values, unless quoting is required due to the presence of special characters.

While it is possible to use custom code to manually patch up the request (see below), this prevents us from using auto-generated client libraries based on an OpenAPI spec file.

POST https://api.example.com/v3/packages/e2c923a3-26d8-449a-acef-201052915e7f/upload HTTP/1.1
Host: api.example.com
Authorization: Bearer XXX
Content-Type: multipart/form-data; boundary=a63df4c1-ad7c-4c18-b393-1cf5d2fccca4
Content-Length: 3791

--a63df4c1-ad7c-4c18-b393-1cf5d2fccca4
Content-Type: application/octet-stream
Content-Disposition: form-data; name=bits; filename=PushTestApp.zip; filename*=utf-8''PushTestApp.zip
Content-Length: 3540

***BINARY DATA***
--a63df4c1-ad7c-4c18-b393-1cf5d2fccca4--
HTTP/1.1 500 Internal Server Error
Content-Length: 144
Content-Type: text/html
Date: Tue, 16 Sep 2025 07:44:26 GMT
X-Vcap-Request-Id: fa305c34-a213-47e9-67f8-52970547ccfd

<html>
<head><title>500 Internal Server Error</title></head>
<body>
<center><h1>500 Internal Server Error</h1></center>

</body>
</html>

The following C# code can be used to manually add the quotes:

var content = new MultipartFormDataContent
{
    {
        new StreamContent(stream)
        {
            Headers =
            {
                ContentType = new("application/octet-stream")
            }
        },
        //"bits", "PushTestApp.zip"           /* original, does not work */
        "\"bits\"", "\"PushTestApp.zip\""     /* manual patch-up, but breaks filename* value */
    }
};

The code above results in the following header value, where the filename* value has now become incorrect (because there are quotes inside the filename value).

Content-Disposition: form-data; name="bits"; filename="PushTestApp.zip"; filename*=utf-8''%22PushTestApp.zip%22

To compensate, additional custom code is required after construction:

content.First().Headers.ContentDisposition!.FileNameStar = "PushTestApp.zip"; // without quotes

which results in:

Content-Disposition: form-data; name="bits"; filename="PushTestApp.zip"; filename*=utf-8''PushTestApp.zip

Alternatively, the filename* parameter can be removed after construction:

content.First().Headers.ContentDisposition!.FileNameStar = null;

which results in:

Content-Disposition: form-data; name="bits"; filename="PushTestApp.zip"

This is all very inconvenient. We're fighting against the framework defaults. It's not reasonable to expect auto-generated client libraries perform similar hacks. So please adapt the server to allow unquoted values.

Activity

  1. philippthun commented on Apr 30, 2026

    @philippthun
    Member

    This issue has been resolved by the Rack upgrade from 2.2.x to 3.2.4 in #4707 (merged 2025-12-19), which was unblocked by the Thin removal in #4670.

    Rack 3.1 completely rewrote the Content-Disposition parameter parser with explicit handling for unquoted values. At lib/rack/multipart/parser.rb#L436-L447:

    else
      if i = disposition.index(';')
        # Parameter value unquoted (which may be invalid), value ends at semicolon
        value = disposition.slice!(0, i)
      else
        # If no ending semicolon, assume remainder of line is value and stop
        # parsing
        disposition.strip!
        value = disposition
        disposition = ''
      end
    end

    This handles the exact case from this issue - name=bits; filename=file.zip (unquoted) is now parsed correctly alongside the standard quoted form.

    Upstream fix: rack/rack#2077 / rack/rack#2076 (first released in Rack 3.1.0, 2024-06-11)

    Available since: CAPI release 1.224.0 (2026-01-08)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions