is there a good way to generate curl
from java.net.http.HttpRequest
? It seems like there is no good way to get the body (if there is a body), the closest thing to it is bodyPublisher()
that returns Optional<BodyPublisher>
that has only long contentLength()
method.

- 1,078
- 1
- 11
- 28
1 Answers
Indeed, it's way trickier than it looks like.
The HttpRequest
of java.net.http
package is a generic object that can be used as request for any type of HttpClient<?>
, hence it doesn't know what the body is, and generically provides a body publisher (type unknown) to which subscribers of a specific type can subscribe in a custom way in order to get the content from it.
This is very different from HttpResponse<?>
, that you usually get from a HttpClient<?>
with a specific type (usually String
) and that can so trivially return you a String getBody()
.
If you don't want to use any third-party library (which already implement the below logic), this is the way you can extract the (String
) body from the publisher:
String requestBody = httpRequest.bodyPublisher().map(p -> {
HttpResponse.BodySubscriber<String> bodySubscriber = HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8);
StringSubscriber stringSubscriber = new StringSubscriber(bodySubscriber);
p.subscribe(stringSubscriber);
return bodySubscriber.getBody().toCompletableFuture().join();
}).orElse("");
... where httpRequest
is your java.net.http.HttpRequest
and StringSubscriber
is an implementation of Flow.Subscriber<ByteBuffer>
like follows:
class StringSubscriber implements Flow.Subscriber<ByteBuffer> {
private final HttpResponse.BodySubscriber<String> wrapped;
private StringSubscriber(HttpResponse.BodySubscriber<String> wrapped) {
this.wrapped = wrapped;
}
@Override
public void onSubscribe(Flow.Subscription subscription) {
wrapped.onSubscribe(subscription);
}
@Override
public void onNext(ByteBuffer item) {
wrapped.onNext(List.of(item));
}
@Override
public void onError(Throwable throwable) {
wrapped.onError(throwable);
}
@Override
public void onComplete() {
wrapped.onComplete();
}
}
Explanation:
If there is no BodyPublisher
in your request, you simply return an empty string (assuming that means no body, you can customize it to null
or whatever you want).
Else, if there is a BodyPublisher
in your request, then you will do the following:
- You will create a
BodySubscriber
of typeString
(meaning you will subscribe to a publisher that can provide aString
version of its body) - Create a new instance of
StringSubscriber
(your own implementation that I posted above) - Subscribe to the publisher with your subscriber, so that the publisher can call you back with the stream content.
- Get the future result (containing the body) when you're called back from the publisher (the call is asynchronous so you need to
.join()
it)

- 11,930
- 12
- 52
- 89
-
amazing, I'll try it later, thanks, I guess you don't know of any existing (maven?) implementation... – Roy Ash Feb 06 '23 at 14:06
-
1@RoyAsh not really, I think that Spring and ApacheHttp do that but I've written the code once and always use that ever since (it's not trivial for me to add new libraries in my code so I tend to avoid) – Matteo NNZ Feb 07 '23 at 09:27
-
does it work for all the publishers? no matter the underlying type of data? – Roy Ash Feb 07 '23 at 16:26
-
1Since the subscriber implementation is handling a buffer of bytes then I would say so – Matteo NNZ Feb 07 '23 at 21:21
-
another question, does it work even if I haven't sent the request yet and even if I have sent it? – Roy Ash Feb 08 '23 at 14:24
-
I would say so, else you wouldn't be able to use the same HttpRequest more than once for the same reason. But this is easily testable, no? – Matteo NNZ Feb 08 '23 at 16:02
-
Absolutely, I haven't tested it yet though, thanks for your help – Roy Ash Feb 08 '23 at 17:14