Skip to content

Commit 3c4cb63

Browse files
committed
Idle Timeout #103
1 parent 83a74af commit 3c4cb63

File tree

4 files changed

+73
-6
lines changed

4 files changed

+73
-6
lines changed

src/main/java/com/enonic/lib/http/client/HttpClientFactory.java

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@
55
import java.net.InetSocketAddress;
66
import java.net.PasswordAuthentication;
77
import java.net.ProxySelector;
8+
import java.net.URI;
89
import java.net.http.HttpClient;
910
import java.nio.CharBuffer;
1011
import java.time.Duration;
12+
import java.util.HashMap;
13+
import java.util.Map;
1114
import java.util.concurrent.ConcurrentHashMap;
1215
import java.util.concurrent.ConcurrentMap;
1316
import java.util.concurrent.Executor;
@@ -26,18 +29,33 @@
2629

2730
class HttpClientFactory
2831
{
32+
private static final long IDLE_TIMEOUT_MS = Long.getLong( "com.enonic.lib.http.client.idle.timeout", 30000 );
33+
2934
private static final long DEFAULT_CONNECT_TIMEOUT_MS = 10_000;
3035

3136
private static final int DEFAULT_PROXY_PORT = 8080;
3237

33-
private static final ConcurrentMap<String, HttpClient> CACHE = new ConcurrentHashMap<>();
38+
private static final ConcurrentMap<String, HttpClientWrapper> CACHE = new ConcurrentHashMap<>();
3439

3540
private static final Executor SHARED_WORKERS_EXECUTOR = Executors.newCachedThreadPool( new SharedWorkerThreadFactory() );
3641

3742
private HttpClientFactory()
3843
{
3944
}
4045

46+
private static class HttpClientWrapper
47+
{
48+
final HttpClient client;
49+
50+
final Map<String, Long> lastAccess;
51+
52+
public HttpClientWrapper( final HttpClient client, Map<String, Long> lastAccess )
53+
{
54+
this.client = client;
55+
this.lastAccess = lastAccess;
56+
}
57+
}
58+
4159
static class ClientParams
4260
{
4361
final Duration connectTimeout;
@@ -212,9 +230,54 @@ static void clearCache()
212230
CACHE.clear();
213231
}
214232

215-
static HttpClient getHttpClient( ClientParams params )
233+
static HttpClient getHttpClient( final ClientParams params, final URI uri )
216234
{
217-
return CACHE.computeIfAbsent( cacheKey( params ), c -> createClient( params ) );
235+
return CACHE.compute( cacheKey( params ), ( key, old ) -> {
236+
if ( IDLE_TIMEOUT_MS <= 0 )
237+
{
238+
return old != null ? old : new HttpClientWrapper( createClient( params ), null );
239+
}
240+
final String keyForRequest = keyForRequest( uri, params.proxy );
241+
final long currentTimeMillis = System.currentTimeMillis();
242+
243+
final Long lastAccess = old != null ? old.lastAccess.get( keyForRequest ) : null;
244+
245+
if ( old != null && ( lastAccess == null || lastAccess > currentTimeMillis - IDLE_TIMEOUT_MS ) )
246+
{
247+
old.lastAccess.put( keyForRequest, currentTimeMillis );
248+
return old;
249+
}
250+
else
251+
{
252+
return new HttpClientWrapper( createClient( params ), new HashMap<>( Map.of( keyForRequest, currentTimeMillis ) ) );
253+
}
254+
} ).client;
255+
}
256+
257+
private static String keyForRequest( final URI uri, final InetSocketAddress proxy )
258+
{
259+
final boolean isSecure = Utils.isSecure( uri );
260+
final String host = uri.getHost();
261+
final int port = uri.getPort() == -1 ? ( isSecure ? 443 : 80 ) : uri.getPort();
262+
263+
final StringBuilder keyBuilder = new StringBuilder( isSecure ? "S:" : "C:" );
264+
265+
if ( proxy == null )
266+
{
267+
keyBuilder.append( "H:" ).append( host ).append( ":" ).append( port );
268+
}
269+
else
270+
{
271+
if ( isSecure )
272+
{
273+
keyBuilder.append( "T:H:" ).append( host ).append( ":" ).append( port ).append( ";" );
274+
}
275+
final String proxyHost = proxy.getHostString();
276+
final int proxyPort = proxy.getPort();
277+
278+
keyBuilder.append( "P:" ).append( proxyHost ).append( ":" ).append( proxyPort );
279+
}
280+
return keyBuilder.toString();
218281
}
219282

220283
private static String cacheKey( final ClientParams params )

src/main/java/com/enonic/lib/http/client/HttpRequestHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ private HttpResponse<Supplier<ByteSource>> executeRequest()
9595
.build() );
9696

9797
final HttpClient client = HttpClientFactory.getHttpClient( HttpClientFactory.params()
98-
.disableHttp2( !url.startsWith( "https://" ) )
98+
.disableHttp2( !Utils.isSecure( request.uri() ) )
9999
.connectTimeout( connectionTimeout )
100100
.authUser( authUser )
101101
.authPassword( authPassword )
@@ -106,7 +106,7 @@ private HttpResponse<Supplier<ByteSource>> executeRequest()
106106
.followRedirects( followRedirects )
107107
.certificates( certificates )
108108
.clientCertificate( clientCertificate )
109-
.build() );
109+
.build(), request.uri() );
110110

111111
return client.send( request, mapToFullyReadByteSource(
112112
MoreBodySubscribers.withReadTimeout( HttpResponse.BodySubscribers.ofInputStream(),

src/main/java/com/enonic/lib/http/client/Utils.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ private Utils()
3131
{
3232
}
3333

34+
public static boolean isSecure( final URI uri )
35+
{
36+
return uri.getScheme().equalsIgnoreCase( "https" );
37+
}
38+
3439
public static long getContentLength( final HttpHeaders headers )
3540
{
3641
try

src/test/java/com/enonic/lib/http/client/HttpRequestHandlerTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import java.util.concurrent.TimeUnit;
66

77
import org.junit.After;
8-
import org.junit.Ignore;
98
import org.junit.Test;
109
import org.mockito.Mockito;
1110

0 commit comments

Comments
 (0)