2424import feign .Util ;
2525import java .io .ByteArrayInputStream ;
2626import java .io .IOException ;
27+ import java .lang .ref .SoftReference ;
2728import java .net .URI ;
2829import java .net .URISyntaxException ;
2930import java .net .http .HttpClient ;
4849import java .util .Set ;
4950import java .util .TreeSet ;
5051import java .util .concurrent .CompletableFuture ;
52+ import java .util .concurrent .ConcurrentHashMap ;
5153import java .util .function .Function ;
5254import java .util .stream .Collectors ;
5355
5456public class Http2Client implements Client , AsyncClient <Object > {
5557
5658 private final HttpClient client ;
5759
60+ private final Map <Integer , SoftReference <HttpClient >> clients = new ConcurrentHashMap <>();
61+
5862 /**
5963 * Creates the new Http2Client using following defaults:
6064 *
@@ -139,16 +143,26 @@ private HttpClient getOrCreateClient(Options options) {
139143 if (doesClientConfigurationDiffer (options )) {
140144 // create a new client from the existing one - but with connectTimeout and followRedirect
141145 // settings from options
142- java .net .http .HttpClient .Builder builder =
143- 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 ();
146+ final int clientKey = createClientKey (options );
147+
148+ SoftReference <HttpClient > requestScopedSoftReference = clients .get (clientKey );
149+ HttpClient requestScoped =
150+ requestScopedSoftReference == null ? null : requestScopedSoftReference .get ();
151+
152+ if (requestScoped == null ) {
153+ java .net .http .HttpClient .Builder builder =
154+ newClientBuilder (options )
155+ .sslContext (client .sslContext ())
156+ .sslParameters (client .sslParameters ())
157+ .version (client .version ());
158+ client .authenticator ().ifPresent (builder ::authenticator );
159+ client .cookieHandler ().ifPresent (builder ::cookieHandler );
160+ client .executor ().ifPresent (builder ::executor );
161+ client .proxy ().ifPresent (builder ::proxy );
162+ requestScoped = builder .build ();
163+ clients .put (clientKey , new SoftReference <>(requestScoped ));
164+ }
165+ return requestScoped ;
152166 }
153167 return client ;
154168 }
@@ -163,6 +177,24 @@ private boolean doesClientConfigurationDiffer(Options options) {
163177 .orElse (true );
164178 }
165179
180+ /**
181+ * Creates integer key that represents {@link Options} settings based on {@link
182+ * Http2Client#doesClientConfigurationDiffer(Options)} method
183+ *
184+ * @param options value
185+ * @return integer key
186+ */
187+ public int createClientKey (feign .Request .Options options ) {
188+ int key = options .connectTimeoutMillis ();
189+ if (options .isFollowRedirects ()) {
190+ key |=
191+ 1
192+ << 31 ; // connectTimeoutMillis always positive, so we can use first sign bit for
193+ // isFollowRedirects flag
194+ }
195+ return key ;
196+ }
197+
166198 private static java .net .http .HttpClient .Builder newClientBuilder (Options options ) {
167199 return HttpClient .newBuilder ()
168200 .followRedirects (options .isFollowRedirects () ? Redirect .ALWAYS : Redirect .NEVER )
0 commit comments