diff --git a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpRequestRunnable.java b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpRequestRunnable.java index 2edb4ceb2e..cc0ca4c20d 100644 --- a/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpRequestRunnable.java +++ b/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpRequestRunnable.java @@ -172,7 +172,7 @@ HttpRequest createHttpRequest() throws IOException { jsonFactory.createJsonParser(requestBody).parse(tokenRequest); jsonHttpContent = new JsonHttpContent(jsonFactory, tokenRequest) - .setMediaType((new HttpMediaType("application/json"))); + .setMediaType((new HttpMediaType("application/json; charset=utf-8"))); } else { // Force underlying HTTP lib to set Content-Length header to avoid 411s. // See EmptyContent.java. diff --git a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpRequestRunnableTest.java b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpRequestRunnableTest.java index f68b11feba..4e825d679d 100644 --- a/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpRequestRunnableTest.java +++ b/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpRequestRunnableTest.java @@ -36,6 +36,8 @@ import com.google.longrunning.ListOperationsRequest; import com.google.protobuf.Empty; import com.google.protobuf.Field; +import com.google.protobuf.util.JsonFormat; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; @@ -177,4 +179,67 @@ public void testRequestUrlUnnormalizedPatch() throws IOException { Truth.assertThat(httpRequest.getRequestMethod()).isEqualTo("POST"); Truth.assertThat(httpRequest.getHeaders().get("X-HTTP-Method-Override")).isEqualTo("PATCH"); } + + /* + We use a separate RequestFormatter because formatting the body requests is what sets the charset to be UTF-8. + The other tests above do not have a set a body request and instead send an EmptyContent (null Type/ CharSet) + */ + @Test + public void testUnicodeValuesInBody() throws IOException { + HttpRequestFormatter bodyRequestFormatter = + ProtoMessageRequestFormatter.newBuilder() + .setPath( + "/name/{name=*}", + request -> { + Map fields = new HashMap<>(); + ProtoRestSerializer serializer = ProtoRestSerializer.create(); + serializer.putPathParam(fields, "name", request.getName()); + return fields; + }) + .setQueryParamsExtractor(request -> new HashMap<>()) + .setRequestBodyExtractor( + request -> + ProtoRestSerializer.create().toBody("*", request.toBuilder().build(), true)) + .build(); + + Field bodyRequestMessage = + Field.newBuilder() + .setName("feline ☺ → ←") + .setNumber(2) + .setDefaultValue("bird ☺ → ←") + .setJsonName("mouse ☺ → ←") + .setTypeUrl("small ☺ → ←") + .build(); + + ApiMethodDescriptor methodDescriptor = + ApiMethodDescriptor.newBuilder() + .setFullMethodName("house.cat.get") + .setHttpMethod("PUT") + .setRequestFormatter(bodyRequestFormatter) + .setResponseParser(responseParser) + .build(); + + HttpRequestRunnable httpRequestRunnable = + new HttpRequestRunnable<>( + bodyRequestMessage, + methodDescriptor, + "www.googleapis.com/animals/v1/projects", + HttpJsonCallOptions.newBuilder().build(), + new MockHttpTransport(), + HttpJsonMetadata.newBuilder().build(), + (result) -> {}); + + HttpRequest httpRequest = httpRequestRunnable.createHttpRequest(); + Truth.assertThat(httpRequest.getContent().getType()) + .isEqualTo("application/json; charset=utf-8"); + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { + // writeTo() uses the Charset when writing to the stream + httpRequest.getContent().writeTo(byteArrayOutputStream); + String output = byteArrayOutputStream.toString(); + Field.Builder expectedBuilder = Field.newBuilder(); + JsonFormat.parser().merge(output, expectedBuilder); + Field result = expectedBuilder.build(); + Truth.assertThat(result).isEqualTo(bodyRequestMessage); + } + } }