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

"Content-Type: application/json" does not uses UTF-8 charset by default #352

Closed
yeDor opened this issue May 25, 2020 · 3 comments
Closed
Assignees
Labels
Milestone

Comments

@yeDor
Copy link

yeDor commented May 25, 2020

  • Framework version: 1.5
  • Implementations: Spring Boot

Scenario

A Lambda with responses of type json: "Content-Type: application/json"
The lambda is behind an API Gateway. /{proxy+}
Do a GET on the resource and get the object as JSON in UTF-8 charset according to https://tools.ietf.org/html/rfc4627#section-3

Example controller:

public class MyController {

 public static class UtfResponse {

    public String s = "öüäß фрыцшщ";
  }

  @GetMapping("/json")
  public ResponseEntity<UtfResponse> getJson() {
    return ResponseEntity.ok(new UtfResponse());
  }

  @GetMapping(value = "/json/utf8", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
  public ResponseEntity<UtfResponse> getUtfJson() {
    return ResponseEntity.ok(new UtfResponse());
  }
}

Expected behavior

Getting the objects with their content in UTF-8 regardless if "charset=UTF-8" specified or not, according to https://tools.ietf.org/html/rfc4627#section-3.

Actual behavior

  public void getJson() {
    InputStream inputStream = new AwsProxyRequestBuilder(BASE_URL + "/json", GET.name()).json().buildStream();
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    handle(inputStream, outputStream);

    AwsProxyResponse response = readResponse(outputStream);
    assertThat(response.getStatusCode()).isEqualTo(OK.value());
assertThat(response.getMultiValueHeaders().getFirst(CONTENT_TYPE)).isEqualTo(APPLICATION_JSON_VALUE);
    assertThat(response.getBody()).isEqualTo("{\"s\":\"öüäß фрыцшщ\"}");
  }

Test with "Content-Type":"application/json" fails with

Expected :{"s":"öüäß фрыцшщ"}
Actual :{"s":"öüä� ������"}

Test with "Content-Type":["application/json; charset=UTF-8"] works as expected:

  public void getJsonUtf() {
    InputStream inputStream = new AwsProxyRequestBuilder(BASE_URL + "/json/utf8", GET.name()).json().buildStream();
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    handle(inputStream, outputStream);

    AwsProxyResponse response = readResponse(outputStream);
    assertThat(response.getStatusCode()).isEqualTo(OK.value());
    assertThat(response.getMultiValueHeaders().getFirst(CONTENT_TYPE)).isEqualTo("application/json; charset=UTF-8");
    assertThat(response.getBody()).isEqualTo("{\"s\":\"öüäß фрыцшщ\"}");
  }

Spring MVC Test without "aws-serverless-java-container" works as expected:

  public void readJson() throws Exception {
    mockMvc.perform(get(BASE_URL + "/json"))
           .andExpect(status().isOk())
           .andExpect(content().contentType(APPLICATION_JSON))
           .andExpect(content().json("{\"s\":\"öüäß фрыцшщ\"}", true));
  }

Full log output

[2020-05-25 10:09:49,093] INFO [main] d.p.p.s.SenderStreamLambdaHandler - Output: {"statusCode":200,"multiValueHeaders":{"Cache-Control":["no-cache, no-store, max-age=0, must-revalidate"],"Content-Type":["application/json"],"Expires":["0"],"Pragma":["no-cache"],"Vary":["Origin","Access-Control-Request-Method","Access-Control-Request-Headers","Origin","Access-Control-Request-Method","Access-Control-Request-Headers"],"X-Content-Type-Options":["nosniff"],"X-Frame-Options":["DENY"],"X-XSS-Protection":["1; mode=block"]},"body":"{"s":"öüä� ������"}","isBase64Encoded":false}

Issue

com.amazonaws.serverless.proxy.model.ContainerConfig#getDefaultContentCharset is ISO-8859-1 regardless from MIME-type.
-> com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletResponse#flushBuffer
responseBody = new String(bodyOutputStream.toByteArray(), charset);
bodyOutputStream is in UTF-8 but responseBody is constructed with ISO-8859-1

Notes

MediaType.APPLICATION_JSON_UTF8_VALUE is Deprecated
as of 5.2 in favor of APPLICATION_JSON_VALUE since major browsers like Chrome now comply with the specification and interpret correctly UTF-8 special characters without requiring a charset=UTF-8 parameter

Possible workaround

Change the default charset in the constructor method of your Lambda handler class:
LambdaContainerHandler.getContainerConfig().setDefaultContentCharset("UTF-8");

@cruddasj
Copy link

Have just ran into the exact same issue (with character conversion happening in the handler despite UTF-8 being supplied in). @yeDor - just to confirm - the workaround you've outlined above works perfectly, but would be great to see this resolved in the library itself.

@sapessi
Copy link
Collaborator

sapessi commented Jun 11, 2020

This is probably related to #344 and likely a regression caused by #323. We'll fix it with the next patch release

@sapessi sapessi self-assigned this Jul 10, 2020
@sapessi sapessi added the bug label Jul 10, 2020
@sapessi sapessi added this to the Release 1.5.1 milestone Jul 10, 2020
sapessi added a commit that referenced this issue Jul 15, 2020
* fix: Adding pathParameters to v2 proxy event as reported in #358.

* fix: Address JSON content type issue reported in #352 and #344

* fix: Fixed bug caught by integration tests for #352

* fix: Fix struts tests for the changes made for #352

* test: Attempting to replicate the issue reported in #342

* test: Reverting exception test in Spring package since it's only available in Spring5, not Spring4

* fix: Sigh, forgot to remove the import for the class that doesn't exist from the previous commit

* fix: Addresses bug reported in query string parsing (#363) for HTTP API support where we have a query string key, followed by a value declarator (=), but then no value

* chore: Update GitHub issue and PR templates

* fix: Fixed issue reported by SpotBugs with the exception logging of the HTTP API query string parsing
@sapessi
Copy link
Collaborator

sapessi commented Jul 15, 2020

Release 1.5.1 is making its way to maven central and should be available later today. 🚀

@sapessi sapessi closed this as completed Jul 15, 2020
jogep pushed a commit to jogep/aws-serverless-java-container that referenced this issue Dec 8, 2020
* fix: Adding pathParameters to v2 proxy event as reported in aws#358.

* fix: Address JSON content type issue reported in aws#352 and aws#344

* fix: Fixed bug caught by integration tests for aws#352

* fix: Fix struts tests for the changes made for aws#352

* test: Attempting to replicate the issue reported in aws#342

* test: Reverting exception test in Spring package since it's only available in Spring5, not Spring4

* fix: Sigh, forgot to remove the import for the class that doesn't exist from the previous commit

* fix: Addresses bug reported in query string parsing (aws#363) for HTTP API support where we have a query string key, followed by a value declarator (=), but then no value

* chore: Update GitHub issue and PR templates

* fix: Fixed issue reported by SpotBugs with the exception logging of the HTTP API query string parsing
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants