Skip to content

Commit 4173022

Browse files
committed
InterceptingClientHttpRequest adapts to StreamingHttpOutputMessage
Issue: SPR-16582
1 parent 499128d commit 4173022

13 files changed

+118
-101
lines changed

spring-web/src/main/java/org/springframework/http/HttpInputMessage.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ public interface HttpInputMessage extends HttpMessage {
3434
/**
3535
* Return the body of the message as an input stream.
3636
* @return the input stream body (never {@code null})
37-
* @throws IOException in case of I/O Errors
37+
* @throws IOException in case of I/O errors
3838
*/
3939
InputStream getBody() throws IOException;
4040

spring-web/src/main/java/org/springframework/http/HttpOutputMessage.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ public interface HttpOutputMessage extends HttpMessage {
3434
/**
3535
* Return the body of the message as an output stream.
3636
* @return the output stream body (never {@code null})
37-
* @throws IOException in case of I/O Errors
37+
* @throws IOException in case of I/O errors
3838
*/
3939
OutputStream getBody() throws IOException;
4040

spring-web/src/main/java/org/springframework/http/StreamingHttpOutputMessage.java

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,24 +30,24 @@
3030
public interface StreamingHttpOutputMessage extends HttpOutputMessage {
3131

3232
/**
33-
* Set the streaming body for this message.
34-
* @param body the streaming body
33+
* Set the streaming body callback for this message.
34+
* @param body the streaming body callback
3535
*/
3636
void setBody(Body body);
3737

3838

3939
/**
40-
* Defines the contract for bodies that can be written directly to an {@link OutputStream}.
41-
* It is useful with HTTP client libraries that provide indirect access to an
42-
* {@link OutputStream} via a callback mechanism.
40+
* Defines the contract for bodies that can be written directly to an
41+
* {@link OutputStream}. Useful with HTTP client libraries that provide
42+
* indirect access to an {@link OutputStream} via a callback mechanism.
4343
*/
4444
@FunctionalInterface
4545
interface Body {
4646

4747
/**
4848
* Write this body to the given {@link OutputStream}.
4949
* @param outputStream the output stream to write to
50-
* @throws IOException in case of errors
50+
* @throws IOException in case of I/O errors
5151
*/
5252
void writeTo(OutputStream outputStream) throws IOException;
5353
}

spring-web/src/main/java/org/springframework/http/client/ClientHttpRequestInterceptor.java

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,8 +22,8 @@
2222

2323
/**
2424
* Intercepts client-side HTTP requests. Implementations of this interface can be
25-
* {@linkplain org.springframework.web.client.RestTemplate#setInterceptors(java.util.List)
26-
* registered} with the {@link org.springframework.web.client.RestTemplate RestTemplate},
25+
* {@linkplain org.springframework.web.client.RestTemplate#setInterceptors registered}
26+
* with the {@link org.springframework.web.client.RestTemplate RestTemplate},
2727
* as to modify the outgoing {@link ClientHttpRequest} and/or the incoming
2828
* {@link ClientHttpResponse}.
2929
*
@@ -40,7 +40,6 @@ public interface ClientHttpRequestInterceptor {
4040
* Intercept the given request, and return a response. The given
4141
* {@link ClientHttpRequestExecution} allows the interceptor to pass on the
4242
* request and response to the next entity in the chain.
43-
*
4443
* <p>A typical implementation of this method would follow the following pattern:
4544
* <ol>
4645
* <li>Examine the {@linkplain HttpRequest request} and body</li>
@@ -56,7 +55,6 @@ public interface ClientHttpRequestInterceptor {
5655
* </ul>
5756
* <li>Optionally wrap the response to filter HTTP attributes.</li>
5857
* </ol>
59-
*
6058
* @param request the request, containing method, URI, and headers
6159
* @param body the body of the request
6260
* @param execution the request execution

spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequestFactory.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -145,6 +145,7 @@ public void setReadTimeout(int timeout) {
145145
* Indicates whether this request factory should buffer the request body internally.
146146
* <p>Default is {@code true}. When sending large amounts of data via POST or PUT, it is
147147
* recommended to change this property to {@code false}, so as not to run out of memory.
148+
* @since 4.0
148149
*/
149150
public void setBufferRequestBody(boolean bufferRequestBody) {
150151
this.bufferRequestBody = bufferRequestBody;

spring-web/src/main/java/org/springframework/http/client/InterceptingClientHttpRequest.java

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,11 +20,11 @@
2020
import java.net.URI;
2121
import java.util.Iterator;
2222
import java.util.List;
23-
import java.util.Map;
2423

2524
import org.springframework.http.HttpHeaders;
2625
import org.springframework.http.HttpMethod;
2726
import org.springframework.http.HttpRequest;
27+
import org.springframework.http.StreamingHttpOutputMessage;
2828
import org.springframework.util.Assert;
2929
import org.springframework.util.StreamUtils;
3030

@@ -95,11 +95,15 @@ public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOExc
9595
HttpMethod method = request.getMethod();
9696
Assert.state(method != null, "No standard HTTP method");
9797
ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);
98-
for (Map.Entry<String, List<String>> entry : request.getHeaders().entrySet()) {
99-
delegate.getHeaders().addAll(entry.getKey(), entry.getValue());
100-
}
98+
request.getHeaders().forEach((key, value) -> delegate.getHeaders().addAll(key, value));
10199
if (body.length > 0) {
102-
StreamUtils.copy(body, delegate.getBody());
100+
if (delegate instanceof StreamingHttpOutputMessage) {
101+
StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) delegate;
102+
streamingOutputMessage.setBody(outputStream -> StreamUtils.copy(body, outputStream));
103+
}
104+
else {
105+
StreamUtils.copy(body, delegate.getBody());
106+
}
103107
}
104108
return delegate.execute();
105109
}

spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java

+10-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -208,23 +208,17 @@ public final void write(final T t, @Nullable MediaType contentType, HttpOutputMe
208208
addDefaultHeaders(headers, t, contentType);
209209

210210
if (outputMessage instanceof StreamingHttpOutputMessage) {
211-
StreamingHttpOutputMessage streamingOutputMessage =
212-
(StreamingHttpOutputMessage) outputMessage;
213-
streamingOutputMessage.setBody(new StreamingHttpOutputMessage.Body() {
211+
StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;
212+
streamingOutputMessage.setBody(outputStream -> writeInternal(t, new HttpOutputMessage() {
214213
@Override
215-
public void writeTo(final OutputStream outputStream) throws IOException {
216-
writeInternal(t, new HttpOutputMessage() {
217-
@Override
218-
public OutputStream getBody() throws IOException {
219-
return outputStream;
220-
}
221-
@Override
222-
public HttpHeaders getHeaders() {
223-
return headers;
224-
}
225-
});
214+
public OutputStream getBody() {
215+
return outputStream;
226216
}
227-
});
217+
@Override
218+
public HttpHeaders getHeaders() {
219+
return headers;
220+
}
221+
}));
228222
}
229223
else {
230224
writeInternal(t, outputMessage);

spring-web/src/main/java/org/springframework/http/converter/BufferedImageHttpMessageConverter.java

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -221,12 +221,7 @@ public void write(final BufferedImage image, @Nullable final MediaType contentTy
221221

222222
if (outputMessage instanceof StreamingHttpOutputMessage) {
223223
StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;
224-
streamingOutputMessage.setBody(new StreamingHttpOutputMessage.Body() {
225-
@Override
226-
public void writeTo(OutputStream outputStream) throws IOException {
227-
writeInternal(image, selectedContentType, outputStream);
228-
}
229-
});
224+
streamingOutputMessage.setBody(outputStream -> writeInternal(image, selectedContentType, outputStream));
230225
}
231226
else {
232227
writeInternal(image, selectedContentType, outputMessage.getBody());

spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java

+5-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -319,12 +319,7 @@ private void writeForm(MultiValueMap<String, String> form, @Nullable MediaType c
319319

320320
if (outputMessage instanceof StreamingHttpOutputMessage) {
321321
StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;
322-
streamingOutputMessage.setBody(new StreamingHttpOutputMessage.Body() {
323-
@Override
324-
public void writeTo(OutputStream outputStream) throws IOException {
325-
StreamUtils.copy(bytes, outputStream);
326-
}
327-
});
322+
streamingOutputMessage.setBody(outputStream -> StreamUtils.copy(bytes, outputStream));
328323
}
329324
else {
330325
StreamUtils.copy(bytes, outputMessage.getBody());
@@ -347,12 +342,9 @@ private void writeMultipart(final MultiValueMap<String, Object> parts,
347342

348343
if (outputMessage instanceof StreamingHttpOutputMessage) {
349344
StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;
350-
streamingOutputMessage.setBody(new StreamingHttpOutputMessage.Body() {
351-
@Override
352-
public void writeTo(OutputStream outputStream) throws IOException {
353-
writeParts(outputStream, parts, boundary);
354-
writeEnd(outputStream, boundary);
355-
}
345+
streamingOutputMessage.setBody(outputStream -> {
346+
writeParts(outputStream, parts, boundary);
347+
writeEnd(outputStream, boundary);
356348
});
357349
}
358350
else {

spring-web/src/test/java/org/springframework/http/client/AbstractHttpRequestFactoryTestCase.java

+15-20
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,8 +16,6 @@
1616

1717
package org.springframework.http.client;
1818

19-
import java.io.IOException;
20-
import java.io.OutputStream;
2119
import java.net.URI;
2220
import java.util.Arrays;
2321
import java.util.Locale;
@@ -69,6 +67,7 @@ public void status() throws Exception {
6967
ClientHttpRequest request = factory.createRequest(uri, HttpMethod.GET);
7068
assertEquals("Invalid HTTP method", HttpMethod.GET, request.getMethod());
7169
assertEquals("Invalid HTTP URI", uri, request.getURI());
70+
7271
ClientHttpResponse response = request.execute();
7372
try {
7473
assertEquals("Invalid status code", HttpStatus.NOT_FOUND, response.getStatusCode());
@@ -82,26 +81,23 @@ public void status() throws Exception {
8281
public void echo() throws Exception {
8382
ClientHttpRequest request = factory.createRequest(new URI(baseUrl + "/echo"), HttpMethod.PUT);
8483
assertEquals("Invalid HTTP method", HttpMethod.PUT, request.getMethod());
84+
8585
String headerName = "MyHeader";
8686
String headerValue1 = "value1";
8787
request.getHeaders().add(headerName, headerValue1);
8888
String headerValue2 = "value2";
8989
request.getHeaders().add(headerName, headerValue2);
9090
final byte[] body = "Hello World".getBytes("UTF-8");
9191
request.getHeaders().setContentLength(body.length);
92+
9293
if (request instanceof StreamingHttpOutputMessage) {
93-
StreamingHttpOutputMessage streamingRequest =
94-
(StreamingHttpOutputMessage) request;
95-
streamingRequest.setBody(new StreamingHttpOutputMessage.Body() {
96-
@Override
97-
public void writeTo(OutputStream outputStream) throws IOException {
98-
StreamUtils.copy(body, outputStream);
99-
}
100-
});
94+
StreamingHttpOutputMessage streamingRequest = (StreamingHttpOutputMessage) request;
95+
streamingRequest.setBody(outputStream -> StreamUtils.copy(body, outputStream));
10196
}
10297
else {
10398
StreamUtils.copy(body, request.getBody());
10499
}
100+
105101
ClientHttpResponse response = request.execute();
106102
try {
107103
assertEquals("Invalid status code", HttpStatus.OK, response.getStatusCode());
@@ -119,17 +115,14 @@ public void writeTo(OutputStream outputStream) throws IOException {
119115
@Test(expected = IllegalStateException.class)
120116
public void multipleWrites() throws Exception {
121117
ClientHttpRequest request = factory.createRequest(new URI(baseUrl + "/echo"), HttpMethod.POST);
118+
122119
final byte[] body = "Hello World".getBytes("UTF-8");
123120
if (request instanceof StreamingHttpOutputMessage) {
124-
StreamingHttpOutputMessage streamingRequest =
125-
(StreamingHttpOutputMessage) request;
126-
streamingRequest.setBody(new StreamingHttpOutputMessage.Body() {
127-
@Override
128-
public void writeTo(OutputStream outputStream) throws IOException {
129-
StreamUtils.copy(body, outputStream);
130-
outputStream.flush();
131-
outputStream.close();
132-
}
121+
StreamingHttpOutputMessage streamingRequest = (StreamingHttpOutputMessage) request;
122+
streamingRequest.setBody(outputStream -> {
123+
StreamUtils.copy(body, outputStream);
124+
outputStream.flush();
125+
outputStream.close();
133126
});
134127
}
135128
else {
@@ -143,9 +136,11 @@ public void writeTo(OutputStream outputStream) throws IOException {
143136
@Test(expected = UnsupportedOperationException.class)
144137
public void headersAfterExecute() throws Exception {
145138
ClientHttpRequest request = factory.createRequest(new URI(baseUrl + "/echo"), HttpMethod.POST);
139+
146140
request.getHeaders().add("MyHeader", "value");
147141
byte[] body = "Hello World".getBytes("UTF-8");
148142
FileCopyUtils.copy(body, request.getBody());
143+
149144
ClientHttpResponse response = request.execute();
150145
try {
151146
request.getHeaders().add("MyHeader", "value");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2002-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.http.client;
18+
19+
import org.junit.Test;
20+
21+
import org.springframework.http.HttpMethod;
22+
23+
/**
24+
* @author Juergen Hoeller
25+
*/
26+
public class InterceptingStreamingHttpComponentsTests extends AbstractHttpRequestFactoryTestCase {
27+
28+
@Override
29+
protected ClientHttpRequestFactory createRequestFactory() {
30+
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
31+
requestFactory.setBufferRequestBody(false);
32+
return new InterceptingClientHttpRequestFactory(requestFactory, null);
33+
}
34+
35+
@Override
36+
@Test
37+
public void httpMethods() throws Exception {
38+
assertHttpMethod("patch", HttpMethod.PATCH);
39+
}
40+
41+
}

0 commit comments

Comments
 (0)