vert-x3 / vertx-web

HTTP web applications for Vert.x
Apache License 2.0
1.11k stars 531 forks source link

Lose charset in application/x-www-form-urlencoded? #2385

Open snpcp opened 1 year ago

snpcp commented 1 year ago

I use sendForm to request, I set a header Content-Type: application/x-www-form-urlencoded; charset=UTF-8, but I found actual request Content-Type is application/x-www-form-urlencoded , it's not should be.

Vertx override my header in here of HttpContext#handleCreateRequest: multipartForm.headers().forEach(header -> { requestOptions.putHeader(header.getKey(), header.getValue()); });

Version

vertx-core:4.3.5 vertx-web-client:4.3.5 JVM:1.8

Steps to reproduce

  1. set Content-Type: application/x-www-form-urlencoded; charset=UTF-8 to header.
  2. call sendForm
  3. look message in tcpdump.
m1nhtu99-hoan9 commented 1 year ago

I don't think it's a bug, but just an implicit behaviour:

HttpRequestImpl::sendForm method would set the MultipartForm parts' charset to UTF-8 already. See: src/main/java/io/vertx/ext/web/client/impl/HttpRequestImpl.java.

If you call the sendForm method with the second argument of any other charsets different to UTF-8, the charset specification from "content-type" header would be ignored.

I tried it out by adding this test case to WebClientTest:

@Test
public void testWwwFormUrlEncodedWithCharset() throws Exception {
  this.server.requestHandler(req -> {
    req.body()
      .onComplete(onSuccess(body -> {
        MultiMap headers = req.headers();
        System.out.println("body = " + body);
        try {
          if (!headers.contains("content-type")) {
            throw new AssertionError("'content-type' header missing.");
          }
          String contentTypeHeaderValue = headers.get("content-type");
          if (!contentTypeHeaderValue.equals("application/x-www-form-urlencoded; charset=UTF-8")) {
            throw new AssertionError(String.format("Unexpected 'content-type' header value: '%s'.", contentTypeHeaderValue));
          }
        } finally {
          req.response().end();
        }
      }));

  });
  startServer();
  MultiMap form = MultiMap.caseInsensitiveMultiMap();
  form.add("param1", "param1_value");
  HttpRequest<Buffer> builder = webClient.post("/somepath");
  builder.putHeader("content-type", "application/x-www-form-urlencoded; charset=UTF-8");
  builder.sendForm(form, StandardCharsets.UTF_16.toString()).onComplete(onSuccess(resp -> complete()));
  await();
}

Test result:

May 09, 2023 3:35:52 AM io.vertx.test.core.AsyncTestBase
INFO: Starting test: WebClientTest#testWwwFormUrlEncodedWithCharset
body = �� p a r a m 1�� =�� p a r a m 1 _ v a l u e
May 09, 2023 3:35:53 AM io.vertx.core.impl.ContextBase
SEVERE: Unhandled exception
java.lang.AssertionError: Unexpected 'content-type' header value: 'application/x-www-form-urlencoded'.
    at io.vertx.ext.web.client.WebClientTest.lambda$testWwwFormUrlEncodedWithCharset$122(WebClientTest.java:1341)
    at io.vertx.test.core.AsyncTestBase.lambda$onSuccess$3(AsyncTestBase.java:684)
    at io.vertx.core.impl.future.FutureImpl$3.onSuccess(FutureImpl.java:141)
    <...trimmed...>

And also, the test result for builder.sendForm(form) without the second argument for charset:

May 09, 2023 3:44:26 AM io.vertx.test.core.AsyncTestBase
INFO: Starting test: WebClientTest#testWwwFormUrlEncodedWithCharset
body = param1=param1_value
May 09, 2023 3:44:26 AM io.vertx.core.impl.ContextBase
SEVERE: Unhandled exception
java.lang.AssertionError: Unexpected 'content-type' header value: 'application/x-www-form-urlencoded'.
    at io.vertx.ext.web.client.WebClientTest.lambda$testWwwFormUrlEncodedWithCharset$122(WebClientTest.java:1341)
    at io.vertx.test.core.AsyncTestBase.lambda$onSuccess$3(AsyncTestBase.java:684)
    at io.vertx.core.impl.future.FutureImpl$3.onSuccess(FutureImpl.java:141)
    at io.vertx.core.impl.future.FutureBase.lambda$emitSuccess$0(FutureBase.java:54)
        <...trimmed...>