Apache HttpClient 5.4.2配置自定义套接字工厂实现TLS/非TLS流量DSCP标记的问题咨询
Apache HttpClient 5.4.2配置自定义套接字工厂实现TLS/非TLS流量DSCP标记的问题咨询
大家好,我目前正在尝试给Apache HttpClient发送的数据包配置DSCP标记——也就是在IP头里设置流量类别(Traffic Class)或服务类型(Type-of-Service)字段。之前在旧版本的HttpClient里已经顺利实现了这个功能,但升级到最新版本后遇到了卡点,想请社区的大佬们帮忙看看。
旧版本可行方案(HttpClient 5.3.1 / Core 5.2.4)
在旧版本中,我通过自定义PlainConnectionSocketFactory和SSLConnectionSocketFactory的子类来实现DSCP标记,这两个类当时还未被弃用,代码运行完全正常:
HTTP流量的DSCP标记实现
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; } }
HTTPS流量的DSCP标记实现
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();
新版本遇到的问题(HttpClient 5.4.2 / Core 5.3.3)
升级到最新版本后,PlainConnectionSocketFactory和SSLConnectionSocketFactory都被弃用了,我需要改用新的API适配:
HTTPS流量的新方案(已成功)
我通过自定义DefaultClientTlsStrategy的子类实现了HTTPS流量的DSCP标记,目前运行正常:
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); } }
HTTP流量的新方案(未生效)
我尝试自定义ManagedHttpClientConnectionFactory来处理HTTP流量,但发现connection.getSocket()总是返回null,导致DSCP标记根本没被设置:
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();
现在的核心问题就是HTTP流量的DSCP标记怎么都加不上,想请教下大家,在新版本的HttpClient里,应该怎么正确配置HTTP流量的DSCP标记呢?
备注:内容来源于stack exchange,提问作者Venkat




