revised package-level request and response classes
This commit is contained in:
parent
f5fdedea60
commit
0c2a6395e7
|
|
@ -28,7 +28,7 @@ import org.springframework.http.HttpHeaders;
|
|||
* @author Arjen Poutsma
|
||||
* @since 3.0.6
|
||||
*/
|
||||
public abstract class AbstractBufferingClientHttpRequest extends AbstractClientHttpRequest {
|
||||
abstract class AbstractBufferingClientHttpRequest extends AbstractClientHttpRequest {
|
||||
|
||||
private ByteArrayOutputStream bufferedOutput = new ByteArrayOutputStream();
|
||||
|
||||
|
|
|
|||
|
|
@ -30,12 +30,13 @@ import org.springframework.util.Assert;
|
|||
*/
|
||||
public abstract class AbstractClientHttpRequest implements ClientHttpRequest {
|
||||
|
||||
private boolean executed = false;
|
||||
|
||||
private final HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
private boolean executed = false;
|
||||
|
||||
|
||||
public final HttpHeaders getHeaders() {
|
||||
return executed ? HttpHeaders.readOnlyHttpHeaders(headers) : this.headers;
|
||||
return (this.executed ? HttpHeaders.readOnlyHttpHeaders(this.headers) : this.headers);
|
||||
}
|
||||
|
||||
public final OutputStream getBody() throws IOException {
|
||||
|
|
@ -43,14 +44,6 @@ public abstract class AbstractClientHttpRequest implements ClientHttpRequest {
|
|||
return getBodyInternal(this.headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract template method that returns the body.
|
||||
*
|
||||
* @param headers the HTTP headers
|
||||
* @return the body output stream
|
||||
*/
|
||||
protected abstract OutputStream getBodyInternal(HttpHeaders headers) throws IOException;
|
||||
|
||||
public final ClientHttpResponse execute() throws IOException {
|
||||
checkExecuted();
|
||||
ClientHttpResponse result = executeInternal(this.headers);
|
||||
|
|
@ -62,13 +55,19 @@ public abstract class AbstractClientHttpRequest implements ClientHttpRequest {
|
|||
Assert.state(!this.executed, "ClientHttpRequest already executed");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Abstract template method that returns the body.
|
||||
* @param headers the HTTP headers
|
||||
* @return the body output stream
|
||||
*/
|
||||
protected abstract OutputStream getBodyInternal(HttpHeaders headers) throws IOException;
|
||||
|
||||
/**
|
||||
* Abstract template method that writes the given headers and content to the HTTP request.
|
||||
*
|
||||
* @param headers the HTTP headers
|
||||
* @return the response object for the executed request
|
||||
*/
|
||||
protected abstract ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException;
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ public abstract class AbstractClientHttpRequestFactoryWrapper implements ClientH
|
|||
|
||||
private final ClientHttpRequestFactory requestFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a {@code AbstractClientHttpRequestFactoryWrapper} wrapping the given request factory.
|
||||
*
|
||||
* @param requestFactory the request factory to be wrapped
|
||||
*/
|
||||
protected AbstractClientHttpRequestFactoryWrapper(ClientHttpRequestFactory requestFactory) {
|
||||
|
|
@ -42,29 +42,27 @@ public abstract class AbstractClientHttpRequestFactoryWrapper implements ClientH
|
|||
this.requestFactory = requestFactory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>This implementation simply calls {@link #createRequest(URI, HttpMethod, ClientHttpRequestFactory)} with the
|
||||
* wrapped request factory provided to the {@linkplain #AbstractClientHttpRequestFactoryWrapper(ClientHttpRequestFactory)
|
||||
* constructor}.
|
||||
* This implementation simply calls {@link #createRequest(URI, HttpMethod, ClientHttpRequestFactory)}
|
||||
* with the wrapped request factory provided to the
|
||||
* {@linkplain #AbstractClientHttpRequestFactoryWrapper(ClientHttpRequestFactory) constructor}.
|
||||
*/
|
||||
public final ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
|
||||
return createRequest(uri, httpMethod, requestFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ClientHttpRequest} for the specified URI and HTTP method by using the passed on request factory.
|
||||
* Create a new {@link ClientHttpRequest} for the specified URI and HTTP method by using the
|
||||
* passed-on request factory.
|
||||
* <p>Called from {@link #createRequest(URI, HttpMethod)}.
|
||||
*
|
||||
* @param uri the URI to create a request for
|
||||
* @param httpMethod the HTTP method to execute
|
||||
* @param requestFactory the wrapped request factory
|
||||
* @return the created request
|
||||
* @throws IOException in case of I/O errors
|
||||
*/
|
||||
protected abstract ClientHttpRequest createRequest(URI uri,
|
||||
HttpMethod httpMethod,
|
||||
ClientHttpRequestFactory requestFactory) throws IOException;
|
||||
protected abstract ClientHttpRequest createRequest(
|
||||
URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) throws IOException;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ public class BufferingClientHttpRequestFactory extends AbstractClientHttpRequest
|
|||
throws IOException {
|
||||
ClientHttpRequest request = requestFactory.createRequest(uri, httpMethod);
|
||||
if (shouldBuffer(uri, httpMethod)) {
|
||||
return new BufferingClientHttpRequest(request);
|
||||
return new BufferingClientHttpRequestWrapper(request);
|
||||
}
|
||||
else {
|
||||
return request;
|
||||
|
|
|
|||
|
|
@ -31,29 +31,32 @@ import org.springframework.util.FileCopyUtils;
|
|||
* @author Arjen Poutsma
|
||||
* @since 3.1
|
||||
*/
|
||||
class BufferingClientHttpRequest extends AbstractBufferingClientHttpRequest {
|
||||
final class BufferingClientHttpRequestWrapper extends AbstractBufferingClientHttpRequest {
|
||||
|
||||
private final ClientHttpRequest request;
|
||||
|
||||
BufferingClientHttpRequest(ClientHttpRequest request) {
|
||||
|
||||
BufferingClientHttpRequestWrapper(ClientHttpRequest request) {
|
||||
Assert.notNull(request, "'request' must not be null");
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
|
||||
public HttpMethod getMethod() {
|
||||
return request.getMethod();
|
||||
return this.request.getMethod();
|
||||
}
|
||||
|
||||
public URI getURI() {
|
||||
return request.getURI();
|
||||
return this.request.getURI();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
|
||||
request.getHeaders().putAll(headers);
|
||||
OutputStream body = request.getBody();
|
||||
this.request.getHeaders().putAll(headers);
|
||||
OutputStream body = this.request.getBody();
|
||||
FileCopyUtils.copy(bufferedOutput, body);
|
||||
ClientHttpResponse response = request.execute();
|
||||
return new BufferingClientHttpResponse(response);
|
||||
ClientHttpResponse response = this.request.execute();
|
||||
return new BufferingClientHttpResponseWrapper(response);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -25,43 +25,45 @@ import org.springframework.http.HttpStatus;
|
|||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link ClientHttpResponse} that reads the request's body into memory, thus allowing for
|
||||
* multiple invocations of {@link #getBody()}.
|
||||
* Simple implementation of {@link ClientHttpResponse} that reads the request's body into memory,
|
||||
* thus allowing for multiple invocations of {@link #getBody()}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.1
|
||||
*/
|
||||
class BufferingClientHttpResponse implements ClientHttpResponse {
|
||||
final class BufferingClientHttpResponseWrapper implements ClientHttpResponse {
|
||||
|
||||
private final ClientHttpResponse response;
|
||||
|
||||
private byte[] body;
|
||||
|
||||
BufferingClientHttpResponse(ClientHttpResponse response) {
|
||||
|
||||
BufferingClientHttpResponseWrapper(ClientHttpResponse response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
|
||||
public HttpStatus getStatusCode() throws IOException {
|
||||
return response.getStatusCode();
|
||||
return this.response.getStatusCode();
|
||||
}
|
||||
|
||||
public String getStatusText() throws IOException {
|
||||
return response.getStatusText();
|
||||
return this.response.getStatusText();
|
||||
}
|
||||
|
||||
public HttpHeaders getHeaders() {
|
||||
return response.getHeaders();
|
||||
return this.response.getHeaders();
|
||||
}
|
||||
|
||||
public InputStream getBody() throws IOException {
|
||||
if (body == null) {
|
||||
body = FileCopyUtils.copyToByteArray(response.getBody());
|
||||
if (this.body == null) {
|
||||
this.body = FileCopyUtils.copyToByteArray(this.response.getBody());
|
||||
}
|
||||
return new ByteArrayInputStream(body);
|
||||
return new ByteArrayInputStream(this.body);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
response.close();
|
||||
this.response.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -60,7 +60,7 @@ public class CommonsClientHttpRequestFactory implements ClientHttpRequestFactory
|
|||
* {@link HttpClient} that uses a default {@link MultiThreadedHttpConnectionManager}.
|
||||
*/
|
||||
public CommonsClientHttpRequestFactory() {
|
||||
httpClient = new HttpClient(new MultiThreadedHttpConnectionManager());
|
||||
this.httpClient = new HttpClient(new MultiThreadedHttpConnectionManager());
|
||||
this.setReadTimeout(DEFAULT_READ_TIMEOUT_MILLISECONDS);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,11 +35,12 @@ import org.springframework.util.FileCopyUtils;
|
|||
* @since 3.0
|
||||
* @see SimpleClientHttpRequestFactory#createRequest(java.net.URI, HttpMethod)
|
||||
*/
|
||||
final class BufferingSimpleClientHttpRequest extends AbstractBufferingClientHttpRequest {
|
||||
final class SimpleBufferingClientHttpRequest extends AbstractBufferingClientHttpRequest {
|
||||
|
||||
private final HttpURLConnection connection;
|
||||
|
||||
BufferingSimpleClientHttpRequest(HttpURLConnection connection) {
|
||||
|
||||
SimpleBufferingClientHttpRequest(HttpURLConnection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
|
|
@ -69,9 +70,7 @@ final class BufferingSimpleClientHttpRequest extends AbstractBufferingClientHttp
|
|||
if (this.connection.getDoOutput()) {
|
||||
this.connection.setFixedLengthStreamingMode(bufferedOutput.length);
|
||||
}
|
||||
|
||||
this.connection.connect();
|
||||
|
||||
if (this.connection.getDoOutput()) {
|
||||
FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
|
||||
}
|
||||
|
|
@ -30,22 +30,24 @@ import org.springframework.util.Assert;
|
|||
* {@link ClientHttpRequestFactory} implementation that uses standard J2SE facilities.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.0
|
||||
* @see java.net.HttpURLConnection
|
||||
* @see CommonsClientHttpRequestFactory
|
||||
* @since 3.0
|
||||
*/
|
||||
public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory {
|
||||
|
||||
private static final int DEFAULT_CHUNK_SIZE = 4096;
|
||||
|
||||
|
||||
private Proxy proxy;
|
||||
|
||||
private boolean bufferRequestBody = true;
|
||||
|
||||
private int chunkSize = DEFAULT_CHUNK_SIZE;
|
||||
|
||||
|
||||
/**
|
||||
* Sets the {@link Proxy} to use for this request factory.
|
||||
* Set the {@link Proxy} to use for this request factory.
|
||||
*/
|
||||
public void setProxy(Proxy proxy) {
|
||||
this.proxy = proxy;
|
||||
|
|
@ -54,12 +56,11 @@ public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory
|
|||
/**
|
||||
* Indicates whether this request factory should buffer the {@linkplain ClientHttpRequest#getBody() request body}
|
||||
* internally.
|
||||
* <p>Default is {@code true}. When sending large amounts of data via POST or PUT, it is recommended to change this
|
||||
* property to {@code false}, so as not to run out of memory. This will result in a {@link ClientHttpRequest}
|
||||
* that either streams directly to the underlying {@link HttpURLConnection} (if the
|
||||
* {@link org.springframework.http.HttpHeaders#getContentLength() Content-Length} is known in advance), or that will
|
||||
* use "Chunked transfer encoding" (if the {@code Content-Length} is not known in advance).
|
||||
*
|
||||
* <p>Default is {@code true}. When sending large amounts of data via POST or PUT, it is recommended
|
||||
* to change this property to {@code false}, so as not to run out of memory. This will result in a
|
||||
* {@link ClientHttpRequest} that either streams directly to the underlying {@link HttpURLConnection}
|
||||
* (if the {@link org.springframework.http.HttpHeaders#getContentLength() Content-Length} is known in advance),
|
||||
* or that will use "Chunked transfer encoding" (if the {@code Content-Length} is not known in advance).
|
||||
* @see #setChunkSize(int)
|
||||
* @see HttpURLConnection#setFixedLengthStreamingMode(int)
|
||||
*/
|
||||
|
|
@ -72,43 +73,42 @@ public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory
|
|||
* <p>Note that this parameter is only used when {@link #setBufferRequestBody(boolean) bufferRequestBody} is set
|
||||
* to {@code false}, and the {@link org.springframework.http.HttpHeaders#getContentLength() Content-Length}
|
||||
* is not known in advance.
|
||||
*
|
||||
* @see #setBufferRequestBody(boolean)
|
||||
*/
|
||||
public void setChunkSize(int chunkSize) {
|
||||
this.chunkSize = chunkSize;
|
||||
}
|
||||
|
||||
|
||||
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
|
||||
HttpURLConnection connection = openConnection(uri.toURL(), proxy);
|
||||
HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
|
||||
prepareConnection(connection, httpMethod.name());
|
||||
if (bufferRequestBody) {
|
||||
return new BufferingSimpleClientHttpRequest(connection);
|
||||
if (this.bufferRequestBody) {
|
||||
return new SimpleBufferingClientHttpRequest(connection);
|
||||
}
|
||||
else {
|
||||
return new StreamingSimpleClientHttpRequest(connection, chunkSize);
|
||||
return new SimpleStreamingClientHttpRequest(connection, this.chunkSize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens and returns a connection to the given URL. <p>The default implementation uses the given {@linkplain
|
||||
* #setProxy(java.net.Proxy) proxy} - if any - to open a connection.
|
||||
*
|
||||
* Opens and returns a connection to the given URL.
|
||||
* <p>The default implementation uses the given {@linkplain #setProxy(java.net.Proxy) proxy} -
|
||||
* if any - to open a connection.
|
||||
* @param url the URL to open a connection to
|
||||
* @param proxy the proxy to use, may be {@code null}
|
||||
* @return the opened connection
|
||||
* @throws IOException in case of I/O errors
|
||||
*/
|
||||
protected HttpURLConnection openConnection(URL url, Proxy proxy) throws IOException {
|
||||
URLConnection urlConnection = proxy != null ? url.openConnection(proxy) : url.openConnection();
|
||||
URLConnection urlConnection = (proxy != null ? url.openConnection(proxy) : url.openConnection());
|
||||
Assert.isInstanceOf(HttpURLConnection.class, urlConnection);
|
||||
return (HttpURLConnection) urlConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method for preparing the given {@link HttpURLConnection}. <p>The default implementation prepares the
|
||||
* connection for input and output, and sets the HTTP method.
|
||||
*
|
||||
* Template method for preparing the given {@link HttpURLConnection}.
|
||||
* <p>The default implementation prepares the connection for input and output, and sets the HTTP method.
|
||||
* @param connection the connection to prepare
|
||||
* @param httpMethod the HTTP request method ({@code GET}, {@code POST}, etc.)
|
||||
* @throws IOException in case of I/O errors
|
||||
|
|
@ -130,4 +130,4 @@ public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory
|
|||
connection.setRequestMethod(httpMethod);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ import org.springframework.util.StringUtils;
|
|||
|
||||
/**
|
||||
* {@link ClientHttpResponse} implementation that uses standard J2SE facilities.
|
||||
* Obtained via {@link BufferingSimpleClientHttpRequest#execute()} and
|
||||
* {@link StreamingSimpleClientHttpRequest#execute()}.
|
||||
* Obtained via {@link SimpleBufferingClientHttpRequest#execute()} and
|
||||
* {@link SimpleStreamingClientHttpRequest#execute()}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.0
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import org.springframework.http.HttpMethod;
|
|||
* @since 3.0
|
||||
* @see SimpleClientHttpRequestFactory#createRequest(java.net.URI, HttpMethod)
|
||||
*/
|
||||
public class StreamingSimpleClientHttpRequest extends AbstractClientHttpRequest {
|
||||
final class SimpleStreamingClientHttpRequest extends AbstractClientHttpRequest {
|
||||
|
||||
private final HttpURLConnection connection;
|
||||
|
||||
|
|
@ -44,7 +44,8 @@ public class StreamingSimpleClientHttpRequest extends AbstractClientHttpRequest
|
|||
|
||||
private OutputStream body;
|
||||
|
||||
StreamingSimpleClientHttpRequest(HttpURLConnection connection, int chunkSize) {
|
||||
|
||||
SimpleStreamingClientHttpRequest(HttpURLConnection connection, int chunkSize) {
|
||||
this.connection = connection;
|
||||
this.chunkSize = chunkSize;
|
||||
}
|
||||
|
|
@ -64,13 +65,13 @@ public class StreamingSimpleClientHttpRequest extends AbstractClientHttpRequest
|
|||
|
||||
@Override
|
||||
protected OutputStream getBodyInternal(HttpHeaders headers) throws IOException {
|
||||
if (body == null) {
|
||||
if (this.body == null) {
|
||||
int contentLength = (int) headers.getContentLength();
|
||||
if (contentLength >= 0) {
|
||||
this.connection.setFixedLengthStreamingMode(contentLength);
|
||||
}
|
||||
else {
|
||||
this.connection.setChunkedStreamingMode(chunkSize);
|
||||
this.connection.setChunkedStreamingMode(this.chunkSize);
|
||||
}
|
||||
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
|
||||
String headerName = entry.getKey();
|
||||
|
|
@ -81,14 +82,14 @@ public class StreamingSimpleClientHttpRequest extends AbstractClientHttpRequest
|
|||
this.connection.connect();
|
||||
this.body = this.connection.getOutputStream();
|
||||
}
|
||||
return new NonClosingOutputStream(body);
|
||||
return new NonClosingOutputStream(this.body);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
|
||||
try {
|
||||
if (body != null) {
|
||||
body.close();
|
||||
if (this.body != null) {
|
||||
this.body.close();
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
|
|
@ -97,6 +98,7 @@ public class StreamingSimpleClientHttpRequest extends AbstractClientHttpRequest
|
|||
return new SimpleClientHttpResponse(this.connection);
|
||||
}
|
||||
|
||||
|
||||
private static class NonClosingOutputStream extends FilterOutputStream {
|
||||
|
||||
private NonClosingOutputStream(OutputStream out) {
|
||||
|
|
@ -108,5 +110,4 @@ public class StreamingSimpleClientHttpRequest extends AbstractClientHttpRequest
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue