Looking for the possibility to configure the custom socket factories to set traffic class or type-of-service octet in the IP header for packets sent from this Socket.
The below setup worked so far with below apache HttpClient versions
org.apache.httpcomponents.client5 5.3.1
org.apache.httpcomponents.core5 5.2.4
DSCP marking for HTTP
import java.io.IOException;
import java.net.Proxy;
import java.net.Socket;
import java.net.SocketException;
import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory;
import org.apache.hc.core5.http.protocol.HttpContext;
class CustomDscpPlainConnectionSocketFactory extends PlainConnectionSocketFactory {
private int dscpValue;
CustomDscpPlainConnectionSocketFactory(int dscpValue) {
this.dscpValue = dscpValue;
}
@Override
public Socket createSocket(final HttpContext context) throws IOException {
Socket socket = super.createSocket(context);
socket.setTrafficClass(dscpValue << 2);
return socket;
}
@Override
public Socket createSocket(final Proxy proxy, final HttpContext context) throws IOException {
Socket socket = super.createSocket(proxy, context);
socket.setTrafficClass(dscpValue << 2);
return socket;
}
}
DSCP marking for HTTPS
import java.io.IOException;
import java.net.Proxy;
import java.net.Socket;
import java.net.SocketException;
import javax.net.ssl.SSLContext;
import org.apache.hc.client5.http.ssl.HttpsSupport;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.core5.http.protocol.HttpContext;
class CustomDscpSSLConnectionSocketFactory extends SSLConnectionSocketFactory {
private int dscpValue;
public CustomDscpSSLConnectionSocketFactory(final int dscpValue,
final SSLContext sslContext,
final String[] supportedProtocols) {
super(sslContext.getSocketFactory(), supportedProtocols,
getCipherSuites().toArray(String[]::new),
HttpsSupport.getDefaultHostnameVerifier());
this.dscpValue = dscpValue;
}
public CustomDscpSSLConnectionSocketFactory(SSLContext sslContext) {
super(sslContext);
}
@Override
public Socket createSocket(HttpContext context) throws IOException {
Socket socket = super.createSocket(context);
socket.setTrafficClass(dscpValue << 2);
return socket;
}
@Override
public Socket createSocket(final Proxy proxy, final HttpContext context) throws IOException {
Socket socket = super.createSocket(proxy, context);
socket.setTrafficClass(dscpValue << 2);
return socket;
}
}
HttpClient
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
var plainSocketFactory = new CustomDscpPlainConnectionSocketFactory(20);
var sslConnSocketFactory = new CustomDscpSSLConnectionSocketFactory(20, sslContext, getProtocols())
PoolingHttpClientConnectionManager poolingmgr = new PoolingHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register(URIScheme.HTTP.id, plainSocketFactory)
.register(URIScheme.HTTPS.id, sslConnSocketFactory)
.build());
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create()
.setRedirectStrategy(DefaultRedirectStrategy.INSTANCE)
.setConnectionManager(poolingmgr);
CloseableHttpClient httpClient = httpClientBuilder.build();
Would like to achieve the similar setup with latest apache HTTP client update versions where PlainConnectionSocketFactory and SSLConnectionSocketFactory are deprecated.
org.apache.httpcomponents.client5 5.4.2
org.apache.httpcomponents.core5 5.3.3
DSCP marking for HTTPS which works
import java.net.Socket;
import java.net.SocketException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.client5.http.ssl.HttpsSupport;
import org.apache.hc.core5.reactor.ssl.SSLBufferMode;
public class CustomClientTlsStrategy extends DefaultClientTlsStrategy {
private int dscpValue;
public CustomClientTlsStrategy(final int dscpValue,
final SSLContext sslContext,
final String[] supportedProtocols) {
super(sslContext, supportedProtocols,
getCipherSuites().toArray(String[]::new),
SSLBufferMode.STATIC, HttpsSupport.getDefaultHostnameVerifier());
this.dscpValue = dscpValue;
}
protected void initializeSocket(final SSLSocket socket) {
socket.setTrafficClass(dscpValue << 2);
}
}
DSCP marking for HTTP doesn't work and may be the below setup is not valid for HTTP.
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import org.apache.hc.client5.http.impl.io.ManagedHttpClientConnectionFactory;
import org.apache.hc.client5.http.io.ManagedHttpClientConnection;
public class CustomManagedHttpClientConnectionFactory extends ManagedHttpClientConnectionFactory {
private int dscpValue;
public CustomManagedHttpClientConnectionFactory(int dscpValue) {
super();
this.dscpValue = dscpValue;
}
@Override
public ManagedHttpClientConnection createConnection(final Socket socket) throws IOException {
ManagedHttpClientConnection connection = super.createConnection(socket);
if (connection.getSocket() != null ) { // Socket here is always null and thus DSCP is never marked.
connection.getSocket().setTrafficClass(dscpValue << 2);
}
return connection;
}
}
HttpClient
CustomManagedHttpClientConnectionFactory customManagedHttpClientConnectionFactory = new CustomManagedHttpClientConnectionFactory(20));
var poolingmgrBuilder = PoolingHttpClientConnectionManagerBuilder.create();
poolingmgrBuilder.setTlsSocketStrategy(buildCustomTlsStrategy());
poolingmgrBuilder.setConnectionFactory(customManagedHttpClientConnectionFactory);
var poolingmgr = poolingmgrBuilder.build();
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create()
.setRedirectStrategy(DefaultRedirectStrategy.INSTANCE)
.setConnectionManager(poolingmgr);
CloseableHttpClient httpClient = httpClientBuilder.build();
One needs a custom HttpClientConnectionOperator
to accomplish that. In your case you many get away with a single extra lambda that creates a detached Socket
.
PoolingHttpClientConnectionManagerBuilder connectionManagerBuilder = new PoolingHttpClientConnectionManagerBuilder() {
@Override
protected HttpClientConnectionOperator createConnectionOperator(SchemePortResolver schemePortResolver, DnsResolver dnsResolver, TlsSocketStrategy tlsSocketStrategy) {
return new DefaultHttpClientConnectionOperator(
proxy -> {
Socket socket = proxy != null ? new Socket(proxy) : new Socket();
// Apply custom socket config here
return socket;
},
schemePortResolver,
dnsResolver,
RegistryBuilder.<TlsSocketStrategy>create()
.register(URIScheme.HTTPS.id, tlsSocketStrategy)
.build());
}
};
PoolingHttpClientConnectionManager connectionManager = connectionManagerBuilder.build();