Skip to content

Commit

Permalink
HTTPCORE-761: support ExtendedSocketOption (#473)
Browse files Browse the repository at this point in the history
  • Loading branch information
kkewwei authored and ok2c committed Aug 10, 2024
1 parent 9be6b1c commit e890aff
Show file tree
Hide file tree
Showing 10 changed files with 430 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.io.Closer;
import org.apache.hc.core5.io.ModalCloseable;
import org.apache.hc.core5.io.SocketSupport;
import org.apache.hc.core5.net.URIAuthority;
import org.apache.hc.core5.pool.ConnPoolControl;
import org.apache.hc.core5.pool.ManagedConnPool;
Expand Down Expand Up @@ -249,6 +250,15 @@ private HttpClientConnection createConnection(final Socket sock, final HttpHost
if (socketConfig.getSndBufSize() > 0) {
sock.setSendBufferSize(socketConfig.getSndBufSize());
}
if (this.socketConfig.getTcpKeepIdle() > 0) {
SocketSupport.setOption(sock, SocketSupport.TCP_KEEPIDLE, this.socketConfig.getTcpKeepIdle());
}
if (this.socketConfig.getTcpKeepInterval() > 0) {
SocketSupport.setOption(sock, SocketSupport.TCP_KEEPINTERVAL, this.socketConfig.getTcpKeepInterval());
}
if (this.socketConfig.getTcpKeepCount() > 0) {
SocketSupport.setOption(sock, SocketSupport.TCP_KEEPCOUNT, this.socketConfig.getTcpKeepCount());
}
final int linger = socketConfig.getSoLinger().toMillisecondsIntBound();
if (linger >= 0) {
sock.setSoLinger(true, linger);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.io.Closer;
import org.apache.hc.core5.io.ModalCloseable;
import org.apache.hc.core5.io.SocketSupport;
import org.apache.hc.core5.util.Args;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;
Expand Down Expand Up @@ -145,6 +146,15 @@ public void start() throws IOException {
if (this.socketConfig.getRcvBufSize() > 0) {
this.serverSocket.setReceiveBufferSize(this.socketConfig.getRcvBufSize());
}
if (this.socketConfig.getTcpKeepIdle() > 0) {
SocketSupport.setOption(this.serverSocket, SocketSupport.TCP_KEEPIDLE, this.socketConfig.getTcpKeepIdle());
}
if (this.socketConfig.getTcpKeepInterval() > 0) {
SocketSupport.setOption(this.serverSocket, SocketSupport.TCP_KEEPINTERVAL, this.socketConfig.getTcpKeepInterval());
}
if (this.socketConfig.getTcpKeepCount() > 0) {
SocketSupport.setOption(this.serverSocket, SocketSupport.TCP_KEEPCOUNT, this.socketConfig.getTcpKeepCount());
}
if (this.sslSetupHandler != null && this.serverSocket instanceof SSLServerSocket) {
final SSLServerSocket sslServerSocket = (SSLServerSocket) this.serverSocket;
final SSLParameters sslParameters = sslServerSocket.getSSLParameters();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.apache.hc.core5.http.io.HttpServerConnection;
import org.apache.hc.core5.http.io.SocketConfig;
import org.apache.hc.core5.io.Closer;
import org.apache.hc.core5.io.SocketSupport;

class RequestListener implements Runnable {

Expand Down Expand Up @@ -91,6 +92,15 @@ private HttpServerConnection createConnection(final Socket socket) throws IOExce
if (this.socketConfig.getSoLinger().toSeconds() >= 0) {
socket.setSoLinger(true, this.socketConfig.getSoLinger().toSecondsIntBound());
}
if (this.socketConfig.getTcpKeepIdle() > 0) {
SocketSupport.setOption(this.serverSocket, SocketSupport.TCP_KEEPIDLE, this.socketConfig.getTcpKeepIdle());
}
if (this.socketConfig.getTcpKeepInterval() > 0) {
SocketSupport.setOption(this.serverSocket, SocketSupport.TCP_KEEPINTERVAL, this.socketConfig.getTcpKeepInterval());
}
if (this.socketConfig.getTcpKeepCount() > 0) {
SocketSupport.setOption(this.serverSocket, SocketSupport.TCP_KEEPCOUNT, this.socketConfig.getTcpKeepCount());
}
if (!(socket instanceof SSLSocket) && sslSocketFactory != null) {
final SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(socket, null, -1, false);
sslSocket.setUseClientMode(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ public class SocketConfig {
private final int sndBufSize;
private final int rcvBufSize;
private final int backlogSize;
private final int tcpKeepIdle;
private final int tcpKeepInterval;
private final int tcpKeepCount;
private final SocketAddress socksProxyAddress;

SocketConfig(
Expand All @@ -67,6 +70,9 @@ public class SocketConfig {
final int sndBufSize,
final int rcvBufSize,
final int backlogSize,
final int tcpKeepIdle,
final int tcpKeepInterval,
final int tcpKeepCount,
final SocketAddress socksProxyAddress) {
super();
this.soTimeout = soTimeout;
Expand All @@ -77,6 +83,9 @@ public class SocketConfig {
this.sndBufSize = sndBufSize;
this.rcvBufSize = rcvBufSize;
this.backlogSize = backlogSize;
this.tcpKeepIdle = tcpKeepIdle;
this.tcpKeepInterval = tcpKeepInterval;
this.tcpKeepCount = tcpKeepCount;
this.socksProxyAddress = socksProxyAddress;
}

Expand Down Expand Up @@ -139,6 +148,30 @@ public int getBacklogSize() {
return backlogSize;
}

/**
* @see Builder#setTcpKeepIdle(int)
* @since 5.3
*/
public int getTcpKeepIdle() {
return this.tcpKeepIdle;
}

/**
* @see Builder#setTcpKeepInterval(int)
* @since 5.3
*/
public int getTcpKeepInterval() {
return this.tcpKeepInterval;
}

/**
* @see Builder#setTcpKeepCount(int)
* @since 5.3
*/
public int getTcpKeepCount() {
return this.tcpKeepCount;
}

/**
* @see Builder#setSocksProxyAddress(SocketAddress)
*/
Expand All @@ -157,6 +190,9 @@ public String toString() {
.append(", sndBufSize=").append(this.sndBufSize)
.append(", rcvBufSize=").append(this.rcvBufSize)
.append(", backlogSize=").append(this.backlogSize)
.append(", tcpKeepIdle=").append(this.tcpKeepIdle)
.append(", tcpKeepInterval=").append(this.tcpKeepInterval)
.append(", tcpKeepCount=").append(this.tcpKeepCount)
.append(", socksProxyAddress=").append(this.socksProxyAddress)
.append("]");
return builder.toString();
Expand All @@ -177,6 +213,9 @@ public static SocketConfig.Builder copy(final SocketConfig config) {
.setSndBufSize(config.getSndBufSize())
.setRcvBufSize(config.getRcvBufSize())
.setBacklogSize(config.getBacklogSize())
.setTcpKeepIdle(config.getTcpKeepIdle())
.setTcpKeepInterval(config.getTcpKeepInterval())
.setTcpKeepCount(config.getTcpKeepCount())
.setSocksProxyAddress(config.getSocksProxyAddress());
}

Expand All @@ -190,6 +229,9 @@ public static class Builder {
private int sndBufSize;
private int rcvBufSize;
private int backlogSize;
private int tcpKeepIdle;
private int tcpKeepInterval;
private int tcpKeepCount;
private SocketAddress socksProxyAddress;

Builder() {
Expand All @@ -201,6 +243,9 @@ public static class Builder {
this.sndBufSize = 0;
this.rcvBufSize = 0;
this.backlogSize = 0;
this.tcpKeepIdle = -1;
this.tcpKeepInterval = -1;
this.tcpKeepCount = -1;
this.socksProxyAddress = null;
}

Expand Down Expand Up @@ -340,6 +385,46 @@ public Builder setBacklogSize(final int backlogSize) {
return this;
}

/**
* Determines the time (in seconds) the connection needs to remain idle before TCP starts
* sending keepalive probes.
* <p>
* Default: {@code -1} (system default)
* </p>
* @return the time (in seconds) the connection needs to remain idle before TCP starts
* @since 5.3
*/
public Builder setTcpKeepIdle(final int tcpKeepIdle) {
this.tcpKeepIdle = tcpKeepIdle;
return this;
}

/**
* Determines the time (in seconds) between individual keepalive probes.
* <p>
* Default: {@code -1} (system default)
* </p>
* @return the time (in seconds) between individual keepalive probes.
* @since 5.3
*/
public Builder setTcpKeepInterval(final int tcpKeepInterval) {
this.tcpKeepInterval = tcpKeepInterval;
return this;
}

/**
* Determines the maximum number of keepalive probes TCP should send before dropping the connection.
* <p>
* Default: {@code -1} (system default)
* </p>
* @return the maximum number of keepalive probes TCP should send before dropping the connection.
* @since 5.3
*/
public Builder setTcpKeepCount(final int tcpKeepCount) {
this.tcpKeepCount = tcpKeepCount;
return this;
}

/**
* The address of the SOCKS proxy to use.
*/
Expand All @@ -354,6 +439,7 @@ public SocketConfig build() {
soReuseAddress,
soLinger != null ? soLinger : TimeValue.NEG_ONE_SECOND,
soKeepAlive, tcpNoDelay, sndBufSize, rcvBufSize, backlogSize,
tcpKeepIdle, tcpKeepInterval, tcpKeepCount,
socksProxyAddress);
}

Expand Down
77 changes: 77 additions & 0 deletions httpcore5/src/main/java/org/apache/hc/core5/io/SocketSupport.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/

package org.apache.hc.core5.io;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.SocketOption;

import org.apache.hc.core5.annotation.Internal;

/**
* @since 5.3
*/
@Internal
public class SocketSupport {

public static final String TCP_KEEPIDLE = "TCP_KEEPIDLE";
public static final String TCP_KEEPINTERVAL = "TCP_KEEPINTERVAL";
public static final String TCP_KEEPCOUNT = "TCP_KEEPCOUNT";

@SuppressWarnings("unchecked")
public static <T> SocketOption<T> getExtendedSocketOptionOrNull(final String fieldName) {
try {
final Class<?> extendedSocketOptionsClass = Class.forName("jdk.net.ExtendedSocketOptions");
final Field field = extendedSocketOptionsClass.getField(fieldName);
return (SocketOption<T>) field.get(null);
} catch (final Exception ignore) {
return null;
}
}

/**
* object can be ServerSocket or Socket
*/
public static <T> void setOption(final T object, final String fieldName, final T value) throws IOException {
try {
final Class<?> serverSocketClass = object.getClass();
final Method setOptionMethod = serverSocketClass.getMethod("setOption", SocketOption.class, Object.class);
final SocketOption<Integer> socketOption = getExtendedSocketOptionOrNull(fieldName);
if (socketOption == null) {
throw new UnsupportedOperationException("Extended socket option not supported: " + fieldName);
}
setOptionMethod.invoke(object, socketOption, value);
} catch (final UnsupportedOperationException e) {
throw e;
} catch (final Exception ex) {
throw new IOException("Failure setting extended socket option", ex);
}
}

}
Loading

0 comments on commit e890aff

Please sign in to comment.