15
15
16
16
import java .io .ByteArrayInputStream ;
17
17
import java .io .IOException ;
18
+ import java .lang .ref .SoftReference ;
18
19
import java .net .URI ;
19
20
import java .net .URISyntaxException ;
20
21
import java .net .http .HttpClient ;
39
40
import java .util .Set ;
40
41
import java .util .TreeSet ;
41
42
import java .util .concurrent .CompletableFuture ;
43
+ import java .util .concurrent .ConcurrentHashMap ;
42
44
import java .util .function .Function ;
43
45
import java .util .stream .Collectors ;
44
46
import feign .AsyncClient ;
@@ -54,6 +56,8 @@ public class Http2Client implements Client, AsyncClient<Object> {
54
56
55
57
private final HttpClient client ;
56
58
59
+ private final Map <Integer , SoftReference <HttpClient >> clients = new ConcurrentHashMap <>();
60
+
57
61
/**
58
62
* Creates the new Http2Client using following defaults:
59
63
* <ul>
@@ -140,15 +144,24 @@ private HttpClient getOrCreateClient(Options options) {
140
144
if (doesClientConfigurationDiffer (options )) {
141
145
// create a new client from the existing one - but with connectTimeout and followRedirect
142
146
// settings from options
143
- java .net .http .HttpClient .Builder builder = newClientBuilder (options )
144
- .sslContext (client .sslContext ())
145
- .sslParameters (client .sslParameters ())
146
- .version (client .version ());
147
- client .authenticator ().ifPresent (builder ::authenticator );
148
- client .cookieHandler ().ifPresent (builder ::cookieHandler );
149
- client .executor ().ifPresent (builder ::executor );
150
- client .proxy ().ifPresent (builder ::proxy );
151
- return builder .build ();
147
+ final int clientKey = createClientKey (options );
148
+
149
+ SoftReference <HttpClient > requestScopedSoftReference = clients .get (clientKey );
150
+ HttpClient requestScoped = requestScopedSoftReference == null ? null : requestScopedSoftReference .get ();
151
+
152
+ if (requestScoped == null ) {
153
+ java .net .http .HttpClient .Builder builder = newClientBuilder (options )
154
+ .sslContext (client .sslContext ())
155
+ .sslParameters (client .sslParameters ())
156
+ .version (client .version ());
157
+ client .authenticator ().ifPresent (builder ::authenticator );
158
+ client .cookieHandler ().ifPresent (builder ::cookieHandler );
159
+ client .executor ().ifPresent (builder ::executor );
160
+ client .proxy ().ifPresent (builder ::proxy );
161
+ requestScoped = builder .build ();
162
+ clients .put (clientKey , new SoftReference <>(requestScoped ));
163
+ }
164
+ return requestScoped ;
152
165
}
153
166
return client ;
154
167
}
@@ -162,6 +175,20 @@ private boolean doesClientConfigurationDiffer(Options options) {
162
175
.orElse (true );
163
176
}
164
177
178
+ /**
179
+ * Creates integer key that represents {@link Options} settings based
180
+ * on {@link Http2Client#doesClientConfigurationDiffer(Options)} method
181
+ * @param options value
182
+ * @return integer key
183
+ */
184
+ public int createClientKey (feign .Request .Options options ) {
185
+ int key = options .connectTimeoutMillis ();
186
+ if (options .isFollowRedirects ()) {
187
+ key |= 1 << 31 ; // connectTimeoutMillis always positive, so we can use first sign bit for isFollowRedirects flag
188
+ }
189
+ return key ;
190
+ }
191
+
165
192
private static java .net .http .HttpClient .Builder newClientBuilder (Options options ) {
166
193
return HttpClient
167
194
.newBuilder ()
0 commit comments