Skip to content

Commit

Permalink
Fix #187: Enable to configure connection provider (#188)
Browse files Browse the repository at this point in the history
* Enable to configure connection provider
* Enable to configure Keep-Alive

(cherry picked from commit 7983b4a)
  • Loading branch information
banterCZ committed Jun 23, 2023
1 parent 4d10a5e commit 35ef083
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 1 deletion.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ The following options are available for the builder:
- `username` - proxy username
- `password` - proxy password
- `connectionTimeout` - connection timeout in milliseconds (default: 5000 ms)
- `maxIdleTime` - ConnectionProvider max idle time. (default: no max idle time)
- `maxLifeTime` - ConnectionProvider max life time. (default: no max life time)
- `keepAliveEnabled` - Keep-Alive probe feature flag (default: false)
- `keepAliveIdle` - Keep-Alive idle time
- `keepAliveInterval` - Keep-Alive retransmission interval time
- `keepAliveCount` - Keep-Alive retransmission limit
- `acceptInvalidSslCertificate` - whether invalid SSL certificate is accepted (default: false)
- `maxInMemorySize` - maximum in memory request size (default: 1048576 bytes)
- `httpBasicAuth` - HTTP basic authentication (default: disabled)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@
import io.getlime.core.rest.model.base.response.ObjectResponse;
import io.getlime.core.rest.model.base.response.Response;
import io.netty.channel.ChannelOption;
import io.netty.channel.epoll.EpollChannelOption;
import io.netty.channel.socket.nio.NioChannelOption;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.ssl.SslContext;
import jdk.net.ExtendedSocketOptions;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -44,6 +47,7 @@
import org.springframework.web.reactive.function.client.*;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;
import reactor.netty.tcp.SslProvider;
import reactor.netty.transport.ProxyProvider;
import reactor.netty.transport.logging.AdvancedByteBufFormat;
Expand Down Expand Up @@ -114,7 +118,7 @@ private void initializeWebClient() throws RestClientException {
}
final WebClient.Builder builder = WebClient.builder();
final SslContext sslContext = SslUtils.prepareSslContext(config);
HttpClient httpClient = HttpClient.create()
HttpClient httpClient = createHttpClient(config)
.wiretap(this.getClass().getCanonicalName(), LogLevel.TRACE, AdvancedByteBufFormat.TEXTUAL)
.followRedirect(config.isFollowRedirectEnabled());
if (sslContext != null) {
Expand All @@ -132,6 +136,10 @@ private void initializeWebClient() throws RestClientException {
ChannelOption.CONNECT_TIMEOUT_MILLIS,
config.getConnectionTimeout());
}
if (config.isKeepAliveEnabled()) {
httpClient = configureKeepAlive(httpClient, config);
}

final Duration responseTimeout = config.getResponseTimeout();
if (responseTimeout != null) {
logger.debug("Setting response timeout {}", responseTimeout);
Expand Down Expand Up @@ -180,6 +188,50 @@ private void initializeWebClient() throws RestClientException {
webClient = builder.baseUrl(config.getBaseUrl()).clientConnector(connector).build();
}

private static HttpClient configureKeepAlive(final HttpClient httpClient, final RestClientConfiguration config) throws RestClientException {
final Duration keepAliveIdle = config.getKeepAliveIdle();
final Duration keepAliveInterval = config.getKeepAliveInterval();
final Integer keepAliveCount = config.getKeepAliveCount();
logger.info("Configuring Keep-Alive, idle={}, interval={}, count={}", keepAliveIdle, keepAliveInterval, keepAliveCount);
if (keepAliveIdle == null || keepAliveInterval == null || keepAliveCount == null) {
throw new RestClientException("All Keep-Alive properties must be specified.");
}

final int keepIdleSeconds = Math.toIntExact(keepAliveIdle.toSeconds());
final int keepIntervalSeconds = Math.toIntExact(keepAliveInterval.toSeconds());

return httpClient.option(ChannelOption.SO_KEEPALIVE, true)
.option(NioChannelOption.of(ExtendedSocketOptions.TCP_KEEPIDLE), keepIdleSeconds)
.option(NioChannelOption.of(ExtendedSocketOptions.TCP_KEEPINTERVAL), keepIntervalSeconds)
.option(NioChannelOption.of(ExtendedSocketOptions.TCP_KEEPCOUNT), keepAliveCount)
.option(EpollChannelOption.TCP_KEEPIDLE, keepIdleSeconds)
.option(EpollChannelOption.TCP_KEEPINTVL, keepIntervalSeconds)
.option(EpollChannelOption.TCP_KEEPCNT, keepAliveCount);
}

/**
* Create HttpClient with default HttpConnectionProvider or custom one, if specified in the given config.
* @param config Config to create connection provider if specified.
* @return Http client.
*/
private static HttpClient createHttpClient(final RestClientConfiguration config) {
final Duration maxIdleTime = config.getMaxIdleTime();
final Duration maxLifeTime = config.getMaxLifeTime();
if (maxIdleTime != null || maxLifeTime != null) {
logger.info("Configuring custom connection provider, maxIdleTime={}, maxLifeTime={}", maxIdleTime, maxLifeTime);
final ConnectionProvider.Builder providerBuilder = ConnectionProvider.builder("custom");
if (maxIdleTime != null) {
providerBuilder.maxIdleTime(maxIdleTime);
}
if (maxLifeTime != null) {
providerBuilder.maxLifeTime(maxLifeTime);
}
return HttpClient.create(providerBuilder.build());
} else {
return HttpClient.create();
}
}

@Override
public <T> ResponseEntity<T> get(String path, ParameterizedTypeReference<T> responseType) throws RestClientException {
return get(path, null, null, responseType);
Expand Down Expand Up @@ -801,6 +853,66 @@ public Builder connectionTimeout(Integer connectionTimeout) {
return this;
}

/**
* Configure ConnectionProvider max idle time. {@code Null} means no max idle time.
* @param maxIdleTime Max idle time.
* @return Builder.
*/
public Builder maxIdleTime(final Duration maxIdleTime) {
config.setMaxIdleTime(maxIdleTime);
return this;
}

/**
* Configure ConnectionProvider max life time. {@code Null} means no max life time.
* @param maxLifeTime Max life time.
* @return Builder.
*/
public Builder maxLifeTime(Duration maxLifeTime) {
config.setMaxLifeTime(maxLifeTime);
return this;
}

/**
* Configure Keep-Alive probe.
* @param keepAliveEnabled Keep-Alive enabled.
* @return Builder.
*/
public Builder keepAliveEnabled(boolean keepAliveEnabled) {
config.setKeepAliveEnabled(keepAliveEnabled);
return this;
}

/**
* Configure Keep-Alive idle interval.
* @param keepAliveIdle Keep-Alive idle interval.
* @return Builder.
*/
public Builder keepAliveIdle(Duration keepAliveIdle) {
config.setKeepAliveIdle(keepAliveIdle);
return this;
}

/**
* Configure Keep-Alive retransmission interval.
* @param keepAliveInterval Keep-Alive retransmission interval.
* @return Builder.
*/
public Builder keepAliveInterval(Duration keepAliveInterval) {
config.setKeepAliveInterval(keepAliveInterval);
return this;
}

/**
* Configure Keep-Alive retransmission limit.
* @param keepAliveCount Keep-Alive retransmission limit.
* @return Builder.
*/
public Builder keepAliveCount(Integer keepAliveCount) {
config.setKeepAliveCount(keepAliveCount);
return this;
}

/**
* Configure whether invalid SSL certificate is accepted.
* @param acceptInvalidSslCertificate Whether invalid SSL certificate is accepted.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ public class RestClientConfiguration {
private Integer connectionTimeout = 5000;
private Duration responseTimeout;

private Duration maxIdleTime;
private Duration maxLifeTime;

private boolean keepAliveEnabled;
private Duration keepAliveIdle;
private Duration keepAliveInterval;
private Integer keepAliveCount;

// TLS certificate settings
private boolean acceptInvalidSslCertificate = false;
private Duration handshakeTimeout;
Expand Down Expand Up @@ -235,6 +243,108 @@ public void setConnectionTimeout(Integer connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}

/**
* Get max idle time.
*
* @return Max idle time.
*/
public Duration getMaxIdleTime() {
return maxIdleTime;
}

/**
* Set the options to use for configuring ConnectionProvider max idle time.
* {@code Null} means no max idle time.
*
* @param maxIdleTime Max idle time.
*/
public void setMaxIdleTime(Duration maxIdleTime) {
this.maxIdleTime = maxIdleTime;
}

/**
* Get max life time.
*
* @return Max life time.
*/
public Duration getMaxLifeTime() {
return maxLifeTime;
}

/**
* Set the options to use for configuring ConnectionProvider max life time.
* {@code Null} means no max life time.
*
* @param maxLifeTime Max life time.
*/
public void setMaxLifeTime(Duration maxLifeTime) {
this.maxLifeTime = maxLifeTime;
}

/**
* Return whether Keep-Alive is enabled.
* @return {@code True} if keep-alive enabled-
*/
public boolean isKeepAliveEnabled() {
return keepAliveEnabled;
}

/**
* Set whether Keep-Alive is enabled
* @param keepAliveEnabled Keep-Alive.
*/
public void setKeepAliveEnabled(boolean keepAliveEnabled) {
this.keepAliveEnabled = keepAliveEnabled;
}

/**
* Get Keep-Alive idle time.
* @return Keep-Alive idle time.
*/
public Duration getKeepAliveIdle() {
return keepAliveIdle;
}

/**
* Set Keep-Alive idle time.
* @param keepAliveIdle Keep-Alive idle time.
*/
public void setKeepAliveIdle(Duration keepAliveIdle) {
this.keepAliveIdle = keepAliveIdle;
}

/**
* Get Keep-Alive retransmission interval time.
* @return Keep-Alive retransmission interval time.
*/
public Duration getKeepAliveInterval() {
return keepAliveInterval;
}

/**
* Set Keep-Alive retransmission interval time.
* @param keepAliveInterval Keep-Alive retransmission interval time.
*/
public void setKeepAliveInterval(Duration keepAliveInterval) {
this.keepAliveInterval = keepAliveInterval;
}

/**
* Get Keep-Alive retransmission limit.
* @return Keep-Alive retransmission limit.
*/
public Integer getKeepAliveCount() {
return keepAliveCount;
}

/**
* Set Keep-Alive retransmission limit.
* @param keepAliveCount Keep-Alive retransmission limit.
*/
public void setKeepAliveCount(Integer keepAliveCount) {
this.keepAliveCount = keepAliveCount;
}

/**
* Get whether invalid SSL certificate is accepted.
* @return Whether invalid SSL certificate is accepted.
Expand Down

0 comments on commit 35ef083

Please sign in to comment.