Httpcomponents httpcore 和 httpclient HTTP_status 400
Httpcomponents httpcore and httpclient HTTP_status 400
我正在尝试做:
- 从 HttpClient 发送请求(基于 HttpComponents HttpClient 4.5)。
- 在 HttpServer 中接收该请求(基于 HttpComponents HttpCore 4.4.1)。
- HttpServer 必须使用不同的 HttpStatus 代码和字符串实体作为正文来响应 HttpClient。
问题:如果 HttpServer 使用状态码 200(或任何其他未检查的状态码)进行应答,那么它工作正常并且在服务器端没有异常。但是,如果服务器设置应答状态代码 400,则说明 HttpServer 上发生了 IOException。俄语的描述是“Удаленный хост принудительно разорвал существующее подключение”,英文我认为是"Client closed connection"。简单一:状态200没问题,状态400是服务器异常。
异常字符串:
java.io.IOException: Удаленный хост принудительно разорвал существующее подключение
at sun.nio.ch.SocketDispatcher.read0(Native Method) ~[na:1.7.0_51]
at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43) ~[na:1.7.0_51]
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223) ~[na:1.7.0_51]
at sun.nio.ch.IOUtil.read(IOUtil.java:197) ~[na:1.7.0_51]
at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:379) ~[na:1.7.0_51]
at org.apache.http.nio.reactor.ssl.SSLIOSession.receiveEncryptedData(SSLIOSession.java:449) ~[httpcore-nio-4.4.1.jar:4.4.1]
at org.apache.http.nio.reactor.ssl.SSLIOSession.isAppInputReady(SSLIOSession.java:503) ~[httpcore-nio-4.4.1.jar:4.4.1]
at org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(AbstractIODispatch.java:122) ~[httpcore-nio-4.4.1.jar:4.4.1]
at org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:164) [httpcore-nio-4.4.1.jar:4.4.1]
at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:339) [httpcore-nio-4.4.1.jar:4.4.1]
at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:317) [httpcore-nio-4.4.1.jar:4.4.1]
at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:278) [httpcore-nio-4.4.1.jar:4.4.1]
at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:106) [httpcore-nio-4.4.1.jar:4.4.1]
at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:590) [httpcore-nio-4.4.1.jar:4.4.1]
at java.lang.Thread.run(Thread.java:744) [na:1.7.0_51]
HttpServer 代码:
HttpProcessor httpproc = HttpProcessorBuilder.create()
.add(new ResponseDate())
.add(new ResponseServer("HTTP/1.1 WTX Server"))
.add(new ResponseContent())
.add(new ResponseConnControl()).build();
UriHttpAsyncRequestHandlerMapper reqistry = new UriHttpAsyncRequestHandlerMapper();
reqistry.register("*", new HttpServerURLHandler());
HttpAsyncService protocolHandler = new HttpServerConnectionsHandler(httpproc, reqistry);
try {
String keyStoreFile = Config.getString("HTTPServer.keyStoreFile");
String keyStoreFilePassword = Config.getString("HTTPServer.keyStoreFilePassword");
FileInputStream fin = new FileInputStream(keyStoreFile);
KeyStore keystore = KeyStore.getInstance("jks");
keystore.load(fin, keyStoreFilePassword.toCharArray());
KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmfactory.init(keystore, keyStoreFilePassword.toCharArray());
KeyManager[] keymanagers = kmfactory.getKeyManagers();
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(keymanagers, null, null);
NHttpConnectionFactory<DefaultNHttpServerConnection> connFactory = new SSLNHttpServerConnectionFactory(sslcontext, null, ConnectionConfig.DEFAULT);
IOEventDispatch ioEventDispatch = new DefaultHttpServerIODispatch(protocolHandler, connFactory);
IOReactorConfig config = IOReactorConfig.custom()
//.setIoThreadCount(10)
//.setSoTimeout(5000)
//.setConnectTimeout(4000)
//.setSoKeepAlive(true)
//.setSoReuseAddress(true)
//.setRcvBufSize(65535)
//.setTcpNoDelay(true)
.build();
ListeningIOReactor ioReactor = new DefaultListeningIOReactor(config);
ioReactor.listen(new InetSocketAddress(socketAddr, socketPort));
ioReactor.execute(ioEventDispatch);
} catch (Exception e) {
MDC.put(ApplicationInit.LOGGERVAR, ApplicationInit.LOGGERCTX.HTTPSERVER.toString());
logger.error("Error while creating HTTP Server instance.", e);
}
URL 处理程序代码:
public class HttpServerURLHandler implements HttpAsyncRequestHandler<HttpRequest> {
public static final Logger logger = LoggerFactory.getLogger(HttpServerURLHandler.class);
private BasicHttpResponse httpResponse = null;
public HttpServerURLHandler() {
super();
}
public HttpAsyncRequestConsumer<HttpRequest> processRequest(final HttpRequest request, final HttpContext context) {
return new BasicAsyncRequestConsumer();
}
public void handle(final HttpRequest httpRequest, final HttpAsyncExchange httpExchange, final HttpContext httpContext) throws HttpException, IOException {
String string1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ++++++++++++++++++++++++++++++++++++++++++++++++abcdefghijklmnopqrstuvwxyz";
string1 += "ABCDEFGHIJKLMNOPQRSTUVWXYZ++++++++++++++++++++++++++++++++++++++++++++++++abcdefghijklmnopqrstuvwxyz";
int httpCode = 400;
String httpCodeString = EnglishReasonPhraseCatalog.INSTANCE.getReason(httpCode, Locale.ENGLISH);
BasicHttpResponse httpResponse = new BasicHttpResponse(HttpVersion.HTTP_1_1, httpCode, httpCodeString);
NStringEntity answerEntity = new NStringEntity(stringXML, Consts.UTF_8);
httpResponse.setEntity(answerEntity);
httpExchange.submitResponse(new BasicAsyncResponseProducer(httpResponse));
}
}
客户代码:
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(20000)
.setConnectionRequestTimeout(20000)
.setSocketTimeout(20000)
.build();
SSLContext sslContext = null;
try {
TrustStrategy trustStrategy = new TrustStrategy() {
public boolean isTrusted(X509Certificate[] arg0, String arg1) {
return true;
}
};
sslContext = new SSLContextBuilder().loadTrustMaterial(null, trustStrategy).build();
} catch (Exception e) {
MDC.put(ApplicationInit.LOGGERVAR, ApplicationInit.LOGGERCTX.HTTPCLIENT.toString());
logger.error("Error while creating SSL context for making HTTP request", e);
}
CloseableHttpClient client = HttpClientBuilder.create()
.setDefaultRequestConfig(config)
.setSSLContext(sslContext)
.setSSLHostnameVerifier(new NoopHostnameVerifier())
.build();
String stringURL = "https://serverhost:port/";
try {
HttpPost post = new HttpPost(stringURL);
post.setEntity(httpClientRequest.getEntity());
CloseableHttpResponse httpResponse = client.execute(post);
// Consume entity code
HttpEntity responseEntity = httpResponse.getEntity();
String stringXMLAnswer = EntityUtils.toString(responseEntity);
EntityUtils.consume(responseEntity);
// Some next operations with responseEntity
} catch (Exception e) {
MDC.put(ApplicationInit.LOGGERVAR, ApplicationInit.LOGGERCTX.HTTPCLIENT.toString());
logger.error("Error while make request.", e);
} finally {
try {
// Closing connection
client.close();
} catch (Exception e) {
MDC.put(ApplicationInit.LOGGERVAR, ApplicationInit.LOGGERCTX.HTTPCLIENT.toString());
logger.error("Error while closing connection after making request", e);
}
}
我找到了 org.apache.http.protocol.ResponseConnControl 代码
if (status == HttpStatus.SC_BAD_REQUEST ||
status == HttpStatus.SC_REQUEST_TIMEOUT ||
status == HttpStatus.SC_LENGTH_REQUIRED ||
status == HttpStatus.SC_REQUEST_TOO_LONG ||
status == HttpStatus.SC_REQUEST_URI_TOO_LONG ||
status == HttpStatus.SC_SERVICE_UNAVAILABLE ||
status == HttpStatus.SC_NOT_IMPLEMENTED) {
response.setHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE);
return;
}
此状态代码重现了我的问题。
连接重置可能是由 HTTPCLIENT-1655. Please try the latest 4.5.x snapshot 引起的,看看是否能解决问题。
我正在尝试做:
- 从 HttpClient 发送请求(基于 HttpComponents HttpClient 4.5)。
- 在 HttpServer 中接收该请求(基于 HttpComponents HttpCore 4.4.1)。
- HttpServer 必须使用不同的 HttpStatus 代码和字符串实体作为正文来响应 HttpClient。
问题:如果 HttpServer 使用状态码 200(或任何其他未检查的状态码)进行应答,那么它工作正常并且在服务器端没有异常。但是,如果服务器设置应答状态代码 400,则说明 HttpServer 上发生了 IOException。俄语的描述是“Удаленный хост принудительно разорвал существующее подключение”,英文我认为是"Client closed connection"。简单一:状态200没问题,状态400是服务器异常。
异常字符串:
java.io.IOException: Удаленный хост принудительно разорвал существующее подключение at sun.nio.ch.SocketDispatcher.read0(Native Method) ~[na:1.7.0_51] at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43) ~[na:1.7.0_51] at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223) ~[na:1.7.0_51] at sun.nio.ch.IOUtil.read(IOUtil.java:197) ~[na:1.7.0_51] at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:379) ~[na:1.7.0_51] at org.apache.http.nio.reactor.ssl.SSLIOSession.receiveEncryptedData(SSLIOSession.java:449) ~[httpcore-nio-4.4.1.jar:4.4.1] at org.apache.http.nio.reactor.ssl.SSLIOSession.isAppInputReady(SSLIOSession.java:503) ~[httpcore-nio-4.4.1.jar:4.4.1] at org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(AbstractIODispatch.java:122) ~[httpcore-nio-4.4.1.jar:4.4.1] at org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:164) [httpcore-nio-4.4.1.jar:4.4.1] at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:339) [httpcore-nio-4.4.1.jar:4.4.1] at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:317) [httpcore-nio-4.4.1.jar:4.4.1] at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:278) [httpcore-nio-4.4.1.jar:4.4.1] at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:106) [httpcore-nio-4.4.1.jar:4.4.1] at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:590) [httpcore-nio-4.4.1.jar:4.4.1] at java.lang.Thread.run(Thread.java:744) [na:1.7.0_51]
HttpServer 代码:
HttpProcessor httpproc = HttpProcessorBuilder.create()
.add(new ResponseDate())
.add(new ResponseServer("HTTP/1.1 WTX Server"))
.add(new ResponseContent())
.add(new ResponseConnControl()).build();
UriHttpAsyncRequestHandlerMapper reqistry = new UriHttpAsyncRequestHandlerMapper();
reqistry.register("*", new HttpServerURLHandler());
HttpAsyncService protocolHandler = new HttpServerConnectionsHandler(httpproc, reqistry);
try {
String keyStoreFile = Config.getString("HTTPServer.keyStoreFile");
String keyStoreFilePassword = Config.getString("HTTPServer.keyStoreFilePassword");
FileInputStream fin = new FileInputStream(keyStoreFile);
KeyStore keystore = KeyStore.getInstance("jks");
keystore.load(fin, keyStoreFilePassword.toCharArray());
KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmfactory.init(keystore, keyStoreFilePassword.toCharArray());
KeyManager[] keymanagers = kmfactory.getKeyManagers();
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(keymanagers, null, null);
NHttpConnectionFactory<DefaultNHttpServerConnection> connFactory = new SSLNHttpServerConnectionFactory(sslcontext, null, ConnectionConfig.DEFAULT);
IOEventDispatch ioEventDispatch = new DefaultHttpServerIODispatch(protocolHandler, connFactory);
IOReactorConfig config = IOReactorConfig.custom()
//.setIoThreadCount(10)
//.setSoTimeout(5000)
//.setConnectTimeout(4000)
//.setSoKeepAlive(true)
//.setSoReuseAddress(true)
//.setRcvBufSize(65535)
//.setTcpNoDelay(true)
.build();
ListeningIOReactor ioReactor = new DefaultListeningIOReactor(config);
ioReactor.listen(new InetSocketAddress(socketAddr, socketPort));
ioReactor.execute(ioEventDispatch);
} catch (Exception e) {
MDC.put(ApplicationInit.LOGGERVAR, ApplicationInit.LOGGERCTX.HTTPSERVER.toString());
logger.error("Error while creating HTTP Server instance.", e);
}
URL 处理程序代码:
public class HttpServerURLHandler implements HttpAsyncRequestHandler<HttpRequest> {
public static final Logger logger = LoggerFactory.getLogger(HttpServerURLHandler.class);
private BasicHttpResponse httpResponse = null;
public HttpServerURLHandler() {
super();
}
public HttpAsyncRequestConsumer<HttpRequest> processRequest(final HttpRequest request, final HttpContext context) {
return new BasicAsyncRequestConsumer();
}
public void handle(final HttpRequest httpRequest, final HttpAsyncExchange httpExchange, final HttpContext httpContext) throws HttpException, IOException {
String string1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ++++++++++++++++++++++++++++++++++++++++++++++++abcdefghijklmnopqrstuvwxyz";
string1 += "ABCDEFGHIJKLMNOPQRSTUVWXYZ++++++++++++++++++++++++++++++++++++++++++++++++abcdefghijklmnopqrstuvwxyz";
int httpCode = 400;
String httpCodeString = EnglishReasonPhraseCatalog.INSTANCE.getReason(httpCode, Locale.ENGLISH);
BasicHttpResponse httpResponse = new BasicHttpResponse(HttpVersion.HTTP_1_1, httpCode, httpCodeString);
NStringEntity answerEntity = new NStringEntity(stringXML, Consts.UTF_8);
httpResponse.setEntity(answerEntity);
httpExchange.submitResponse(new BasicAsyncResponseProducer(httpResponse));
}
}
客户代码:
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(20000)
.setConnectionRequestTimeout(20000)
.setSocketTimeout(20000)
.build();
SSLContext sslContext = null;
try {
TrustStrategy trustStrategy = new TrustStrategy() {
public boolean isTrusted(X509Certificate[] arg0, String arg1) {
return true;
}
};
sslContext = new SSLContextBuilder().loadTrustMaterial(null, trustStrategy).build();
} catch (Exception e) {
MDC.put(ApplicationInit.LOGGERVAR, ApplicationInit.LOGGERCTX.HTTPCLIENT.toString());
logger.error("Error while creating SSL context for making HTTP request", e);
}
CloseableHttpClient client = HttpClientBuilder.create()
.setDefaultRequestConfig(config)
.setSSLContext(sslContext)
.setSSLHostnameVerifier(new NoopHostnameVerifier())
.build();
String stringURL = "https://serverhost:port/";
try {
HttpPost post = new HttpPost(stringURL);
post.setEntity(httpClientRequest.getEntity());
CloseableHttpResponse httpResponse = client.execute(post);
// Consume entity code
HttpEntity responseEntity = httpResponse.getEntity();
String stringXMLAnswer = EntityUtils.toString(responseEntity);
EntityUtils.consume(responseEntity);
// Some next operations with responseEntity
} catch (Exception e) {
MDC.put(ApplicationInit.LOGGERVAR, ApplicationInit.LOGGERCTX.HTTPCLIENT.toString());
logger.error("Error while make request.", e);
} finally {
try {
// Closing connection
client.close();
} catch (Exception e) {
MDC.put(ApplicationInit.LOGGERVAR, ApplicationInit.LOGGERCTX.HTTPCLIENT.toString());
logger.error("Error while closing connection after making request", e);
}
}
我找到了 org.apache.http.protocol.ResponseConnControl 代码
if (status == HttpStatus.SC_BAD_REQUEST ||
status == HttpStatus.SC_REQUEST_TIMEOUT ||
status == HttpStatus.SC_LENGTH_REQUIRED ||
status == HttpStatus.SC_REQUEST_TOO_LONG ||
status == HttpStatus.SC_REQUEST_URI_TOO_LONG ||
status == HttpStatus.SC_SERVICE_UNAVAILABLE ||
status == HttpStatus.SC_NOT_IMPLEMENTED) {
response.setHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE);
return;
}
此状态代码重现了我的问题。
连接重置可能是由 HTTPCLIENT-1655. Please try the latest 4.5.x snapshot 引起的,看看是否能解决问题。