【问题标题】:Httpcomponents httpcore and httpclient HTTP_status 400Httpcomponents httpcore 和 httpclient HTTP_status 400
【发布时间】:2015-11-17 19:00:35
【问题描述】:

我正在努力:

  1. 从 HttpClient 发送请求(基于 HttpComponents HttpClient 4.5)。
  2. 在 HttpServer 中接收该请求(基于 HttpComponents HttpCore 4.4.1)。
  3. HttpServer 必须以不同的 HttpStatus 代码和字符串实体作为正文响应 HttpClient。

问题:如果 HttpServer 使用状态码 200(或任何其他,未检查)做出回答,那么它工作正常,服务器端没有异常。但是如果服务器设置了应答状态码 400,那么 HttpServer 上已经发生了 IOException。俄语上的描述是“Удаленный хост принудительно разорвал существующее подключение”,英文我认为是“客户端关闭连接”。简单一:状态200没有问题,400服务器异常。

异常字符串:

java.io.IOException: Удаленный хост принудительно разорвал существующее подключение 在 sun.nio.ch.SocketDispatcher.read0(Native Method) ~[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] 在 sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:379) ~[na:1.7.0_51] 在 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) [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);
    }

网址处理程序代码:

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

【问题讨论】:

  • 如果响应不是 2xx 状态,您是否会使用消息内容?
  • 不,只有在服务器设置应答状态码400时才会出现这种情况。我用200、401、402代码进行了测试,并且可以正常工作。问题仅在 400 错误代码中。而且我必须添加一些信息:400 代码的问题只存在于非空实体中。如果服务器没有设置实体或设置空实体,那么它工作正常。
  • 你是否消费响应消息的内容?
  • 是的,我在客户端使用代码为 400 的响应消息的内容。实体在客户端正确接收。问题仅存在于服务器端的异常中。在我最初的帖子中,生成服务器答案的代码位于“URL Hander 代码”块中。
  • 你确定吗?在您的示例代码中没有证据表明,只有“一些带有响应的下一个操作”注释。客户端的连接重置通常发生在客户端认为底层连接无法安全重用时,在大多数情况下,如果响应消息仍然具有未使用的内容,则会发生这种情况

标签: java apache-httpclient-4.x apache-httpcomponents http-status-code-400 http-status-code-200


【解决方案1】:

连接重置很可能是由HTTPCLIENT-1655 引起的。请尝试最新的4.5.x snapshot,看看是否能解决问题。

【讨论】:

    【解决方案2】:

    我找到了带有代码的 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;
        }
    

    我的问题由此状态码重现。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-27
      • 1970-01-01
      • 2014-11-27
      相关资源
      最近更新 更多