Skip to content

Malformed application/x-www-form-urlencoded and multipart/form-data request bodies result in 500 instead of 400 #57827

Open

Description

@pschaub

Describe the bug

In Quarkus REST, some malformed form request bodies result in 500 Internal Server Error instead of 400 Bad Request:

  1. An application/x-www-form-urlencoded body with an invalid percent-encoding (e.g. name=%zz). FormEncodedDataDefinition passes the value to URLUtils.decode(), which throws an IllegalArgumentException that is not handled. The same invalid encoding in the query string correctly results in a 400.
  2. A multipart/form-data body that ends without the closing boundary. MultiPartParserDefinition throws IOException("Connection terminated parsing multipart request") when the input ends before the parser is complete. The request is then treated like a client disconnect: a 500 is returned and only a DEBUG message saying that "the client likely terminated the connection" is logged, although the body was sent completely. Other malformed multipart bodies already result in a 400 (MalformedMessageException).

Expected behavior

400 Bad Request.

Actual behavior

500 Internal Server Error. For case 1:

ERROR [io.quarkus.vertx.http.runtime.QuarkusErrorHandler] (executor-thread-2) HTTP Request to /echo/form failed, error id: ...: java.lang.IllegalArgumentException: Failed to decode URL %zz to UTF-8
	at org.jboss.resteasy.reactive.common.util.URLUtils.failedToDecodeURL(URLUtils.java:221)
	at org.jboss.resteasy.reactive.common.util.URLUtils.decode(URLUtils.java:155)
	at org.jboss.resteasy.reactive.common.util.URLUtils.decode(URLUtils.java:81)
	at org.jboss.resteasy.reactive.server.core.multipart.FormEncodedDataDefinition$FormEncodedDataParser.decodeParameterValue(FormEncodedDataDefinition.java:208)

For case 2 nothing is logged at the default log level.

How to Reproduce?

  1. Create a project:
    mvn io.quarkus.platform:quarkus-maven-plugin:3.40.1.1:create \
        -DprojectGroupId=org.acme -DprojectArtifactId=rest-repro \
        -Dextensions=rest -DnoCode
    cd rest-repro
  2. Add src/main/java/org/acme/EchoResource.java:
    package org.acme;
    
    import jakarta.ws.rs.Consumes;
    import jakarta.ws.rs.FormParam;
    import jakarta.ws.rs.POST;
    import jakarta.ws.rs.Path;
    import jakarta.ws.rs.core.MediaType;
    
    import org.jboss.resteasy.reactive.RestForm;
    
    @Path("/echo")
    public class EchoResource {
    
        @POST
        @Path("/form")
        @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
        public String form(@FormParam("name") String name) {
            return name;
        }
    
        @POST
        @Path("/multipart")
        @Consumes(MediaType.MULTIPART_FORM_DATA)
        public String multipart(@RestForm String name) {
            return name;
        }
    }
  3. Run ./mvnw quarkus:dev.
  4. Send requests:
    curl -s -o /dev/null -w "%{http_code}\n" -X POST localhost:8080/echo/form -H 'Content-Type: application/x-www-form-urlencoded' -d 'name=foo'   # 200
    curl -s -o /dev/null -w "%{http_code}\n" -X POST localhost:8080/echo/form -H 'Content-Type: application/x-www-form-urlencoded' -d 'name=%zz'   # 500
    
    printf -- '--xyz\r\nContent-Disposition: form-data; name="name"\r\n\r\nfoo\r\n--xyz--\r\n' | curl -s -o /dev/null -w "%{http_code}\n" -X POST localhost:8080/echo/multipart -H 'Content-Type: multipart/form-data; boundary=xyz' --data-binary @-   # 200
    printf -- '--xyz\r\nContent-Disposition: form-data; name="name"\r\n\r\nfoo' | curl -s -o /dev/null -w "%{http_code}\n" -X POST localhost:8080/echo/multipart -H 'Content-Type: multipart/form-data; boundary=xyz' --data-binary @-   # 500

The same result with 4.0.0.Beta1 instead of 3.40.1.1 in step 1.

Output of uname -a or ver

Linux 6.18.54.1-microsoft-standard-WSL2 x86_64 GNU/Linux

Output of java -version

OpenJDK 21.0.12.1 (Temurin-21.0.12.1+1)

Quarkus version or git rev

3.40.1.1, 4.0.0.Beta1, main (5e98984)

Build tool (ie. output of mvnw --version or gradlew --version)

Apache Maven 3.9.9

Additional information

Related to #52767, which fixed the empty multipart body case. I'll open a PR with a fix and tests.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions