From b90e9d7a1f1f098447c2be80b5082790049d449d Mon Sep 17 00:00:00 2001 From: DongHyukki Date: Fri, 25 Jul 2025 11:48:46 +0900 Subject: [PATCH] feat: accept empty content body in HttpJsonTranscodingService --- .../grpc/HttpJsonTranscodingService.java | 28 ++++++++++++------- .../it/grpc/HttpJsonTranscodingTest.java | 16 +++++++++-- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingService.java b/grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingService.java index 9ae93eb453d..b2df88d0395 100644 --- a/grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingService.java +++ b/grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingService.java @@ -742,19 +742,27 @@ private static HttpData convertToJson(ServiceRequestContext ctx, @Nullable private static JsonNode getBodyContent(AggregatedHttpRequest request) { - @Nullable - final MediaType contentType = request.contentType(); - if (contentType == null || !contentType.isJson()) { - if (request.content().isEmpty()) { - return null; + @Nullable final MediaType contentType = request.contentType(); + final HttpData bodyContent = request.content(); + final boolean hasBodyContent = !bodyContent.isEmpty(); + + if (contentType != null && contentType.isJson()) { + if (!hasBodyContent) { + return mapper.createObjectNode(); + } + + try { + return mapper.readTree(bodyContent.toStringUtf8()); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("Failed to parse JSON request.", e); } - throw new IllegalArgumentException("Missing or invalid content-type in JSON request."); } - try { - return mapper.readTree(request.contentUtf8()); - } catch (JsonProcessingException e) { - throw new IllegalArgumentException("Failed to parse JSON request.", e); + + if (hasBodyContent) { + throw new IllegalArgumentException("Missing or invalid content-type in JSON request."); } + + return null; } @VisibleForTesting diff --git a/grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTest.java b/grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTest.java index a1b7e57a54d..e181215b5bc 100644 --- a/grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTest.java +++ b/grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTest.java @@ -917,7 +917,7 @@ void shouldDenyNonJsonContentType() { } @Test - void shouldDenyEmptyJson() { + void shouldAcceptEmptyJson() { final String emptyJson = ""; final RequestHeaders headers = RequestHeaders.builder() .method(HttpMethod.POST) @@ -925,7 +925,19 @@ void shouldDenyEmptyJson() { .contentType(MediaType.JSON) .build(); final AggregatedHttpResponse response = webClient.execute(headers, emptyJson).aggregate().join(); - assertThat(response.status()).isEqualTo(HttpStatus.BAD_REQUEST); + assertThat(response.status()).isEqualTo(HttpStatus.OK); + } + + @Test + void shouldAcceptEmptyContentBody() { + final HttpData emptyContentBody = HttpData.empty(); + final RequestHeaders headers = RequestHeaders.builder() + .method(HttpMethod.POST) + .path("/v1/echo/response_body/repeated") + .contentType(MediaType.JSON) + .build(); + final AggregatedHttpResponse response = webClient.execute(headers, emptyContentBody).aggregate().join(); + assertThat(response.status()).isEqualTo(HttpStatus.OK); } @Test