Httpcomponents httpcore и httpclient HTTP_status 400

Я пытаюсь сделать:

  1. Отправить запрос от HttpClient (на основе HttpComponents HttpClient 4.5).
  2. Получите этот запрос в HttpServer (на основе HttpComponents HttpCore 4.4.1).
  3. HttpServer должен ответить на HttpClient с различными кодами HttpStatus и строковыми сущностями в качестве тела.

Проблема: Если HttpServer делает ответ с кодом состояния 200 (или любым другим, не проверенным), то он работает нормально и никаких исключений на стороне сервера. Но если сервер установил код состояния ответа 400, то на HttpServer произошла исключительная ситуация IOException. Описание на русском языке "Удаленный хост принудительно разорвал существующее подключение", на английском я думаю, что это "Клиент закрыл соединение". Простой: на статусе 200 нет проблем, на 400 - исключение на сервере.

Строка исключения:

java.io.IOException: Удаленный хост принудительно разорвал существующее подключение на sun.nio.ch.SocketDispatcher.read0(собственный метод) ~[na:1.7.0_51] на sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43) ~[na:1.7.0_51] в sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223) ~[na:1.7.0_51] в 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] по адресу org.apache.http.nio.reactor.ssl.SSLIOSession.isAppInputReady(SSLIOSession.java:503) ~[httpcore-nio-4.4.1.jar:4.4.1] в org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(AbstractIODispatch.java:122) ~[httpcore-nio-4.4.1.jar:4.4.1] в org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:164) [httpcore-nio-4.4.1.jar:4.4.1] в org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:339) [httpcore-nio-4.4.1.jar:4.4.1] в org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:317) [httpcore-nio-4.4.1.jar:4.4.1] по адресу org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:278) [httpcore-nio-4.4.1.jar:4.4.1] в org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:106) [httpcore-nio-4.4.1.jar:4.4.1] в org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:590) [httpcore-nio-4.4.1.jar:4.4.1] в java.lang.Thread.run(Thread.java:744) [па: 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-код Hander:

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);
        }
    }

2 ответа

Решение

Сброс соединения может быть вызван HTTPCLIENT-1655. Пожалуйста, попробуйте последний снимок 4.5.x и посмотрите, решит ли это проблему.

Я нашел 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;
    }

Моя проблема воспроизводится по этим кодам состояния.