Httpcomponents httpcore 和 httpclient HTTP_status 400

Httpcomponents httpcore and httpclient HTTP_status 400

我正在尝试做:

  1. 从 HttpClient 发送请求(基于 HttpComponents HttpClient 4.5)。
  2. 在 HttpServer 中接收该请求(基于 HttpComponents HttpCore 4.4.1)。
  3. 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 引起的,看看是否能解决问题。