From 023d8184b2d8126292d81dc17ba4b2a3613f08a3 Mon Sep 17 00:00:00 2001 From: Sharang Gupta Date: Tue, 6 Oct 2026 21:18:57 +0100 Subject: [PATCH 1/2] Treat a null Request.Body as a request with no body `Request.create(..., (Request.Body) null, ...)` is documented as legal ("body ... can be null"), but the constructor stored the `null` and `body()`, `charset()`, `isBinary()` and `length()` dereferenced it, so any body-less `Request` built outside `RequestTemplate` threw a `NullPointerException` on first access. * The constructor now normalises a `null` body to `Request.Body.empty()`, the same default `RequestTemplate` already uses. `Body.empty()` yields exactly the documented contract: `body()` and `charset()` return `null`, `isBinary()` is `true` and `length()` is `0`, so clients that branch on `request.body() != null` keep working unchanged. * `toString()` now checks the body's bytes instead of the (no longer nullable) wrapper, so a body-less request still prints only the request line and headers. Fixes #1210 --- CHANGELOG.md | 7 +++++++ core/src/main/java/feign/Request.java | 6 +++--- core/src/test/java/feign/RequestTest.java | 24 +++++++++++++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f3098fba..cb310b7ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +### Version 13.17 + +* `Request` accepts a `null` `Request.Body` as "no body" instead of throwing a + `NullPointerException` from `body()`, `charset()`, `isBinary()` and `length()`. A body-less request + reports a `null` body and charset, is binary, has length 0, and `toString()` prints the request line + and headers without a body (#1210). + ### Version 13.16 * Path-style expansion repeats the entry name for each collection or array value inside a map, diff --git a/core/src/main/java/feign/Request.java b/core/src/main/java/feign/Request.java index 822518066..7cae186e7 100644 --- a/core/src/main/java/feign/Request.java +++ b/core/src/main/java/feign/Request.java @@ -172,7 +172,7 @@ public static Request create( * @param method of the request. * @param url for the request. * @param headers for the request. - * @param body for the request, optional. + * @param body for the request, optional. {@literal null} means the request has no body. * @param requestTemplate used to build the request. */ Request( @@ -184,7 +184,7 @@ public static Request create( this.httpMethod = checkNotNull(method, "httpMethod of %s", method.name()); this.url = checkNotNull(url, "url"); this.headers = checkNotNull(headers, "headers of %s %s", method, url); - this.body = body; + this.body = body != null ? body : Body.empty(); this.requestTemplate = requestTemplate; protocolVersion = ProtocolVersion.HTTP_1_1; } @@ -308,7 +308,7 @@ public String toString() { builder.append(field).append(": ").append(value).append('\n'); } } - if (body != null) { + if (body.asBytes() != null) { builder.append('\n').append(body.asString()); } return builder.toString(); diff --git a/core/src/test/java/feign/RequestTest.java b/core/src/test/java/feign/RequestTest.java index 9a40c1f52..d0a2b0437 100644 --- a/core/src/test/java/feign/RequestTest.java +++ b/core/src/test/java/feign/RequestTest.java @@ -19,6 +19,8 @@ import static org.assertj.core.api.Assertions.assertThat; import feign.Request.Body; +import feign.Request.HttpMethod; +import java.util.Collections; import org.junit.jupiter.api.Test; public class RequestTest { @@ -34,4 +36,26 @@ void stringBodyIsEncodedWithUtf8NotThePlatformDefault() { assertThat(body.isBinary()).isFalse(); assertThat(body.asString()).isEqualTo(content); } + + @Test + void requestCreatedWithNullBodyReportsNoBody() { + Request request = requestWithoutBody(); + + assertThat(request.body()).isNull(); + assertThat(request.charset()).isNull(); + assertThat(request.isBinary()).isTrue(); + assertThat(request.length()).isZero(); + } + + @Test + void requestWithoutBodyPrintsOnlyTheRequestLine() { + Request request = requestWithoutBody(); + + assertThat(request).hasToString("GET http://example.com HTTP/1.1\n"); + } + + private static Request requestWithoutBody() { + return Request.create( + HttpMethod.GET, "http://example.com", Collections.emptyMap(), (Body) null, null); + } } From 9e80a3bc3422b97893c4c0c66682f4255e82df61 Mon Sep 17 00:00:00 2001 From: Marvin Froeder Date: Wed, 7 Oct 2026 18:01:26 -0300 Subject: [PATCH 2/2] Check body() in Request.toString, clarify null body javadoc and cover template-built requests Signed-off-by: Marvin Froeder --- CHANGELOG.md | 8 ++--- core/src/main/java/feign/Request.java | 6 ++-- core/src/test/java/feign/RequestTest.java | 36 +++++++++++++++++++---- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb310b7ed..2139b2b2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,9 @@ ### Version 13.17 -* `Request` accepts a `null` `Request.Body` as "no body" instead of throwing a - `NullPointerException` from `body()`, `charset()`, `isBinary()` and `length()`. A body-less request - reports a `null` body and charset, is binary, has length 0, and `toString()` prints the request line - and headers without a body (#1210). +* `Request` now accepts a `null` `Request.Body` and treats it as "no body", instead of throwing a + `NullPointerException` from `body()`, `charset()`, `isBinary()` and `length()`. A request with no + body reports a `null` body and charset, is binary, and has length 0. Its `toString()` prints the + request line and headers without a body (#1210). ### Version 13.16 diff --git a/core/src/main/java/feign/Request.java b/core/src/main/java/feign/Request.java index 7cae186e7..e3ccb5e03 100644 --- a/core/src/main/java/feign/Request.java +++ b/core/src/main/java/feign/Request.java @@ -147,7 +147,7 @@ public static Request create( * @param httpMethod for the request. * @param url for the request. * @param headers to include. - * @param body of the request, can be {@literal null} + * @param body of the request, can be {@literal null}, meaning the request has no body. * @return a Request */ public static Request create( @@ -172,7 +172,7 @@ public static Request create( * @param method of the request. * @param url for the request. * @param headers for the request. - * @param body for the request, optional. {@literal null} means the request has no body. + * @param body for the request, optional. * @param requestTemplate used to build the request. */ Request( @@ -308,7 +308,7 @@ public String toString() { builder.append(field).append(": ").append(value).append('\n'); } } - if (body.asBytes() != null) { + if (body() != null) { builder.append('\n').append(body.asString()); } return builder.toString(); diff --git a/core/src/test/java/feign/RequestTest.java b/core/src/test/java/feign/RequestTest.java index d0a2b0437..5f0e87bca 100644 --- a/core/src/test/java/feign/RequestTest.java +++ b/core/src/test/java/feign/RequestTest.java @@ -38,8 +38,8 @@ void stringBodyIsEncodedWithUtf8NotThePlatformDefault() { } @Test - void requestCreatedWithNullBodyReportsNoBody() { - Request request = requestWithoutBody(); + void requestWithoutBodyHasNullBodyAndCharsetZeroLengthAndIsBinary() { + Request request = createGetRequestWithNullBody(); assertThat(request.body()).isNull(); assertThat(request.charset()).isNull(); @@ -48,13 +48,39 @@ void requestCreatedWithNullBodyReportsNoBody() { } @Test - void requestWithoutBodyPrintsOnlyTheRequestLine() { - Request request = requestWithoutBody(); + void toStringOfRequestWithoutBodyOmitsBodySection() { + Request request = createGetRequestWithNullBody(); assertThat(request).hasToString("GET http://example.com HTTP/1.1\n"); } - private static Request requestWithoutBody() { + @Test + void requestBuiltFromTemplateWithoutBodyOmitsBodyFromToString() { + Request request = + new RequestTemplate() + .method(HttpMethod.GET) + .target("http://example.com") + .resolve(Collections.emptyMap()) + .request(); + + assertThat(request).hasToString("GET http://example.com HTTP/1.1\n"); + } + + @Test + void binaryBodyWithoutCharsetPrintsBinaryDataInToString() { + Request request = + new RequestTemplate() + .method(HttpMethod.POST) + .target("http://example.com") + .body(new byte[] {1, 2, 3}, null) + .resolve(Collections.emptyMap()) + .request(); + + assertThat(request) + .hasToString("POST http://example.com HTTP/1.1\nContent-Length: 3\n\nBinary data"); + } + + private static Request createGetRequestWithNullBody() { return Request.create( HttpMethod.GET, "http://example.com", Collections.emptyMap(), (Body) null, null); }