diff --git a/spring-web/src/main/java/org/springframework/http/StreamingHttpOutputMessage.java b/spring-web/src/main/java/org/springframework/http/StreamingHttpOutputMessage.java index 715fbae00c3..d6e43feacb1 100644 --- a/spring-web/src/main/java/org/springframework/http/StreamingHttpOutputMessage.java +++ b/spring-web/src/main/java/org/springframework/http/StreamingHttpOutputMessage.java @@ -20,10 +20,16 @@ import java.io.IOException; import java.io.OutputStream; /** - * Represents an HTTP output message that allows for setting a streaming body. - * Note that such messages typically do not support {@link #getBody()} access. + * Contract for {@code HttpOutputMessage} implementations to expose the ability + * to stream request body content by writing to an {@link OutputStream} from + * a callback. + * + *

The {@link #setBody(Body)} method provides the option to stream, and is + * mutually exclusive use of {@link #getBody()}, which instead returns an + * {@code OutputStream} that aggregates the request body before sending it. * * @author Arjen Poutsma + * @author Rossen Stoyanchev * @since 4.0 * @see #setBody */ @@ -31,15 +37,17 @@ public interface StreamingHttpOutputMessage extends HttpOutputMessage { /** * Set the streaming body callback for this message. + *

Note that this is mutually exclusive with {@link #getBody()}, which + * may instead aggregate the request body before sending it. * @param body the streaming body callback */ void setBody(Body body); /** - * Defines the contract for bodies that can be written directly to an - * {@link OutputStream}. Useful with HTTP client libraries that provide - * indirect access to an {@link OutputStream} via a callback mechanism. + * Contract to stream request body content to an {@link OutputStream}. + * In some HTTP client libraries this is only possible indirectly through a + * callback mechanism. */ @FunctionalInterface interface Body { diff --git a/spring-web/src/main/java/org/springframework/http/client/AbstractStreamingClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/AbstractStreamingClientHttpRequest.java index b2d82ad76fd..4b6d4f3a8d2 100644 --- a/spring-web/src/main/java/org/springframework/http/client/AbstractStreamingClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/AbstractStreamingClientHttpRequest.java @@ -20,17 +20,24 @@ import java.io.IOException; import java.io.OutputStream; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpOutputMessage; import org.springframework.http.StreamingHttpOutputMessage; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.FastByteArrayOutputStream; /** - * Abstract base for {@link ClientHttpRequest} that also implement - * {@link StreamingHttpOutputMessage}. Ensures that headers and - * body are not written multiple times. + * Extension of {@link AbstractClientHttpRequest} that adds the ability to stream + * request body content directly to the underlying HTTP client library through + * the {@link StreamingHttpOutputMessage} contract. + * + *

It is necessary to call {@link #setBody} and stream the request body through + * a callback for access to the {@code OutputStream}. The alternative to call + * {@link #getBody()} is also supported as a fallback, but that does not stream, + * and returns an aggregating {@code OutputStream} instead. * * @author Arjen Poutsma + * @author Rossen Stoyanchev * @since 6.1 */ abstract class AbstractStreamingClientHttpRequest extends AbstractClientHttpRequest @@ -43,6 +50,12 @@ abstract class AbstractStreamingClientHttpRequest extends AbstractClientHttpRequ private FastByteArrayOutputStream bodyStream; + /** + * Implements the {@link HttpOutputMessage} contract for request body content. + *

Note that this method does not result in streaming, and the returned + * {@code OutputStream} aggregates the full content in a byte[] before + * sending. To use streaming, call {@link #setBody} instead. + */ @Override protected final OutputStream getBodyInternal(HttpHeaders headers) { Assert.state(this.body == null, "Invoke either getBody or setBody; not both"); @@ -53,6 +66,10 @@ abstract class AbstractStreamingClientHttpRequest extends AbstractClientHttpRequ return this.bodyStream; } + /** + * Implements the {@link StreamingHttpOutputMessage} contract for writing + * request body by streaming directly to the underlying HTTP client. + */ @Override public final void setBody(Body body) { Assert.notNull(body, "Body must not be null"); @@ -73,12 +90,14 @@ abstract class AbstractStreamingClientHttpRequest extends AbstractClientHttpRequ /** - * Abstract template method that writes the given headers and content to the HTTP request. - * @param headers the HTTP headers + * Abstract method for concrete implementations to write the headers and + * {@link StreamingHttpOutputMessage.Body} to the HTTP request. + * @param headers the HTTP headers for the request * @param body the HTTP body, may be {@code null} if no body was {@linkplain #setBody(Body) set} * @return the response object for the executed request * @since 6.1 */ - protected abstract ClientHttpResponse executeInternal(HttpHeaders headers, @Nullable Body body) throws IOException; + protected abstract ClientHttpResponse executeInternal( + HttpHeaders headers, @Nullable Body body) throws IOException; } diff --git a/spring-web/src/main/java/org/springframework/web/client/RequestCallback.java b/spring-web/src/main/java/org/springframework/web/client/RequestCallback.java index ace09c94f97..22b2b4c189f 100644 --- a/spring-web/src/main/java/org/springframework/web/client/RequestCallback.java +++ b/spring-web/src/main/java/org/springframework/web/client/RequestCallback.java @@ -19,6 +19,7 @@ package org.springframework.web.client; import java.io.IOException; import java.lang.reflect.Type; +import org.springframework.http.HttpOutputMessage; import org.springframework.http.client.ClientHttpRequest; /** @@ -44,6 +45,12 @@ public interface RequestCallback { * Gets called by {@link RestTemplate#execute} with an opened {@code ClientHttpRequest}. * Does not need to care about closing the request or about handling errors: * this will all be handled by the {@code RestTemplate}. + *

Note: In order to stream request body content directly + * to the underlying HTTP library, implementations must check if the request + * is an implementation of {@link org.springframework.http.StreamingHttpOutputMessage}, + * and set the request body through it. Use of the {@link HttpOutputMessage#getBody()} + * is also supported, but results in full content aggregation prior to execution. + * All built-in request implementations support {@code StreamingHttpOutputMessage}. * @param request the active HTTP request * @throws IOException in case of I/O errors */