Skip to content

Commit 20c4177

Browse files
author
Arjen Poutsma
committed
Merge pull request spring-projects#350 from ok2c/httpclient_4_3
* httpclient_4_3: Post SPR-8804 optimizations: better use of HC 4.3 APIs
2 parents 80812d3 + 296e218 commit 20c4177

7 files changed

+197
-64
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
import org.apache.http.HttpEntityEnclosingRequest;
2828
import org.apache.http.HttpResponse;
2929
import org.apache.http.client.methods.HttpUriRequest;
30-
import org.apache.http.entity.ByteArrayEntity;
3130
import org.apache.http.nio.client.HttpAsyncClient;
31+
import org.apache.http.nio.entity.NByteArrayEntity;
3232
import org.apache.http.protocol.HttpContext;
3333

3434
import org.springframework.http.HttpHeaders;
@@ -79,7 +79,7 @@ protected Future<ClientHttpResponse> executeInternal(HttpHeaders headers,
7979
if (this.httpRequest instanceof HttpEntityEnclosingRequest) {
8080
HttpEntityEnclosingRequest entityEnclosingRequest =
8181
(HttpEntityEnclosingRequest) this.httpRequest;
82-
HttpEntity requestEntity = new ByteArrayEntity(bufferedOutput);
82+
HttpEntity requestEntity = new NByteArrayEntity(bufferedOutput);
8383
entityEnclosingRequest.setEntity(requestEntity);
8484
}
8585

@@ -117,14 +117,14 @@ public boolean isDone() {
117117
public ClientHttpResponse get()
118118
throws InterruptedException, ExecutionException {
119119
HttpResponse response = futureResponse.get();
120-
return new HttpComponentsClientHttpResponse(response);
120+
return new HttpComponentsAsyncClientHttpResponse(response);
121121
}
122122

123123
@Override
124124
public ClientHttpResponse get(long timeout, TimeUnit unit)
125125
throws InterruptedException, ExecutionException, TimeoutException {
126126
HttpResponse response = futureResponse.get(timeout, unit);
127-
return new HttpComponentsClientHttpResponse(response);
127+
return new HttpComponentsAsyncClientHttpResponse(response);
128128
}
129129

130130
}

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

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,16 @@
2020
import java.net.URI;
2121

2222
import org.apache.http.client.HttpClient;
23+
import org.apache.http.client.config.RequestConfig;
24+
import org.apache.http.client.methods.Configurable;
2325
import org.apache.http.client.methods.HttpUriRequest;
26+
import org.apache.http.client.protocol.HttpClientContext;
27+
import org.apache.http.impl.client.CloseableHttpClient;
28+
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
2429
import org.apache.http.impl.nio.client.HttpAsyncClients;
2530
import org.apache.http.nio.client.HttpAsyncClient;
2631
import org.apache.http.nio.reactor.IOReactorStatus;
32+
import org.apache.http.protocol.HttpContext;
2733
import org.springframework.beans.factory.InitializingBean;
2834
import org.springframework.http.HttpMethod;
2935
import org.springframework.util.Assert;
@@ -41,23 +47,23 @@ public class HttpComponentsAsyncClientHttpRequestFactory
4147
extends HttpComponentsClientHttpRequestFactory
4248
implements AsyncClientHttpRequestFactory, InitializingBean {
4349

44-
private HttpAsyncClient httpAsyncClient;
50+
private CloseableHttpAsyncClient httpAsyncClient;
4551

4652

4753
/**
4854
* Create a new instance of the {@code HttpComponentsAsyncClientHttpRequestFactory}
4955
* with a default {@link HttpAsyncClient} and {@link HttpClient}.
5056
*/
5157
public HttpComponentsAsyncClientHttpRequestFactory() {
52-
this(HttpAsyncClients.createDefault());
58+
this(HttpAsyncClients.createSystem());
5359
}
5460

5561
/**
5662
* Create a new instance of the {@code HttpComponentsAsyncClientHttpRequestFactory}
5763
* with the given {@link HttpAsyncClient} instance and a default {@link HttpClient}.
5864
* @param httpAsyncClient the HttpAsyncClient instance to use for this request factory
5965
*/
60-
public HttpComponentsAsyncClientHttpRequestFactory(HttpAsyncClient httpAsyncClient) {
66+
public HttpComponentsAsyncClientHttpRequestFactory(CloseableHttpAsyncClient httpAsyncClient) {
6167
super();
6268
Assert.notNull(httpAsyncClient, "'httpAsyncClient' must not be null");
6369
this.httpAsyncClient = httpAsyncClient;
@@ -69,8 +75,8 @@ public HttpComponentsAsyncClientHttpRequestFactory(HttpAsyncClient httpAsyncClie
6975
* @param httpClient the HttpClient instance to use for this request factory
7076
* @param httpAsyncClient the HttpAsyncClient instance to use for this request factory
7177
*/
72-
public HttpComponentsAsyncClientHttpRequestFactory(HttpClient httpClient,
73-
HttpAsyncClient httpAsyncClient) {
78+
public HttpComponentsAsyncClientHttpRequestFactory(CloseableHttpClient httpClient,
79+
CloseableHttpAsyncClient httpAsyncClient) {
7480
super(httpClient);
7581
Assert.notNull(httpAsyncClient, "'httpAsyncClient' must not be null");
7682
this.httpAsyncClient = httpAsyncClient;
@@ -80,15 +86,15 @@ public HttpComponentsAsyncClientHttpRequestFactory(HttpClient httpClient,
8086
* Set the {@code HttpClient} used for
8187
* {@linkplain #createAsyncRequest(java.net.URI, org.springframework.http.HttpMethod) asynchronous execution}.
8288
*/
83-
public void setHttpAsyncClient(HttpAsyncClient httpAsyncClient) {
89+
public void setHttpAsyncClient(CloseableHttpAsyncClient httpAsyncClient) {
8490
this.httpAsyncClient = httpAsyncClient;
8591
}
8692

8793
/**
8894
* Return the {@code HttpClient} used for
8995
* {@linkplain #createAsyncRequest(URI, HttpMethod) asynchronous execution}.
9096
*/
91-
public HttpAsyncClient getHttpAsyncClient() {
97+
public CloseableHttpAsyncClient getHttpAsyncClient() {
9298
return httpAsyncClient;
9399
}
94100

@@ -98,7 +104,7 @@ public void afterPropertiesSet() {
98104
}
99105

100106
private void startAsyncClient() {
101-
HttpAsyncClient asyncClient = getHttpAsyncClient();
107+
CloseableHttpAsyncClient asyncClient = getHttpAsyncClient();
102108
if (asyncClient.getStatus() != IOReactorStatus.ACTIVE) {
103109
asyncClient.start();
104110
}
@@ -111,8 +117,23 @@ public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod)
111117
startAsyncClient();
112118
HttpUriRequest httpRequest = createHttpUriRequest(httpMethod, uri);
113119
postProcessHttpRequest(httpRequest);
114-
return new HttpComponentsAsyncClientHttpRequest(asyncClient, httpRequest,
115-
createHttpContext(httpMethod, uri));
120+
HttpContext context = createHttpContext(httpMethod, uri);
121+
if (context == null) {
122+
context = HttpClientContext.create();
123+
}
124+
// Request configuration not set in the context
125+
if (context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) {
126+
// Use request configuration given by the user, when available
127+
RequestConfig config = null;
128+
if (httpRequest instanceof Configurable) {
129+
config = ((Configurable) httpRequest).getConfig();
130+
}
131+
if (config == null) {
132+
config = RequestConfig.DEFAULT;
133+
}
134+
context.setAttribute(HttpClientContext.REQUEST_CONFIG, config);
135+
}
136+
return new HttpComponentsAsyncClientHttpRequest(asyncClient, httpRequest, context);
116137
}
117138

118139
@Override
@@ -121,7 +142,7 @@ public void destroy() throws Exception {
121142
super.destroy();
122143
}
123144
finally {
124-
getHttpAsyncClient().shutdown();
145+
getHttpAsyncClient().close();
125146
}
126147
}
127148
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright 2002-2012 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.apache.http.Header;
20+
import org.apache.http.HttpEntity;
21+
import org.apache.http.HttpResponse;
22+
import org.apache.http.client.methods.CloseableHttpResponse;
23+
import org.apache.http.util.EntityUtils;
24+
import org.springframework.http.HttpHeaders;
25+
26+
import java.io.IOException;
27+
import java.io.InputStream;
28+
29+
/**
30+
* {@link ClientHttpResponse} implementation that uses
31+
* Apache HttpComponents HttpClient to execute requests.
32+
*
33+
* <p>Created via the {@link HttpComponentsAsyncClientHttpRequest}.
34+
*
35+
* @author Oleg Kalnichevski
36+
* @author Arjen Poutsma
37+
* @since 3.1
38+
* @see HttpComponentsAsyncClientHttpRequest#executeAsync()
39+
*/
40+
final class HttpComponentsAsyncClientHttpResponse extends AbstractClientHttpResponse {
41+
42+
private final HttpResponse httpResponse;
43+
44+
private HttpHeaders headers;
45+
46+
47+
HttpComponentsAsyncClientHttpResponse(HttpResponse httpResponse) {
48+
this.httpResponse = httpResponse;
49+
}
50+
51+
52+
@Override
53+
public int getRawStatusCode() throws IOException {
54+
return this.httpResponse.getStatusLine().getStatusCode();
55+
}
56+
57+
@Override
58+
public String getStatusText() throws IOException {
59+
return this.httpResponse.getStatusLine().getReasonPhrase();
60+
}
61+
62+
@Override
63+
public HttpHeaders getHeaders() {
64+
if (this.headers == null) {
65+
this.headers = new HttpHeaders();
66+
for (Header header : this.httpResponse.getAllHeaders()) {
67+
this.headers.add(header.getName(), header.getValue());
68+
}
69+
}
70+
return this.headers;
71+
}
72+
73+
@Override
74+
public InputStream getBody() throws IOException {
75+
HttpEntity entity = this.httpResponse.getEntity();
76+
return entity != null ? entity.getContent() : null;
77+
}
78+
79+
@Override
80+
public void close() {
81+
// HTTP responses returned by async HTTP client
82+
// are not bound to an active connection and
83+
// do not have to deallocate any resources
84+
}
85+
86+
}

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import java.util.List;
2222
import java.util.Map;
2323

24+
import org.apache.http.client.methods.CloseableHttpResponse;
25+
import org.apache.http.impl.client.CloseableHttpClient;
2426
import org.springframework.http.HttpHeaders;
2527
import org.springframework.http.HttpMethod;
2628

@@ -46,14 +48,14 @@
4648
*/
4749
final class HttpComponentsClientHttpRequest extends AbstractBufferingClientHttpRequest {
4850

49-
private final HttpClient httpClient;
51+
private final CloseableHttpClient httpClient;
5052

5153
private final HttpUriRequest httpRequest;
5254

5355
private final HttpContext httpContext;
5456

5557

56-
public HttpComponentsClientHttpRequest(HttpClient httpClient, HttpUriRequest httpRequest, HttpContext httpContext) {
58+
public HttpComponentsClientHttpRequest(CloseableHttpClient httpClient, HttpUriRequest httpRequest, HttpContext httpContext) {
5759
this.httpClient = httpClient;
5860
this.httpRequest = httpRequest;
5961
this.httpContext = httpContext;
@@ -81,7 +83,7 @@ protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] buffere
8183
HttpEntity requestEntity = new ByteArrayEntity(bufferedOutput);
8284
entityEnclosingRequest.setEntity(requestEntity);
8385
}
84-
HttpResponse httpResponse =
86+
CloseableHttpResponse httpResponse =
8587
this.httpClient.execute(this.httpRequest, this.httpContext);
8688
return new HttpComponentsClientHttpResponse(httpResponse);
8789
}

0 commit comments

Comments
 (0)