Получение ошибки с httpclient5 с HTTP2 & SSL (java.io.IOException: существующее соединение было принудительно закрыто удаленным хостом)

Я пишу клиент, использующий бета-версию HttpClient 5.0 для запроса / загрузки защищенного URL/ ресурса в Tomcat, который поддерживает HTTP2. Программа как ниже. Он взят из примеров Apache httpclient 5 (код точно такой же, за исключением того, как контекст SSL создается с помощью loadTrustMaterial)

public class Http2TlsAlpnRequestExecutionExample {

    public final static void main(final String[] args) throws Exception {
        String trustStorePath = "C:\\cert\\keystore.jks";
        String trustStorePassword = "password";         
        SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(new File(trustStorePath), trustStorePassword.toCharArray()).build();

        // Create and start requester
        H2Config h2Config = H2Config.custom()
                .setPushEnabled(false)
                .build();

        final HttpAsyncRequester requester = H2RequesterBootstrap.bootstrap().setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_2)
                .setH2Config(h2Config)
                .setTlsStrategy(new H2ClientTlsStrategy(sslContext, new SSLSessionVerifier() {

                    public TlsDetails verify(final NamedEndpoint endpoint, final SSLEngine sslEngine) throws SSLException {
                        return null;
                    }

                }))
                .setStreamListener(new Http2StreamListener() {

                    public void onHeaderInput(final HttpConnection connection, final int streamId, final List<? extends Header> headers) {
                        for (int i = 0; i < headers.size(); i++) {
                            System.out.println(connection + " (" + streamId + ") << " + headers.get(i));
                        }
                    }

                    public void onHeaderOutput(final HttpConnection connection, final int streamId, final List<? extends Header> headers) {
                        for (int i = 0; i < headers.size(); i++) {
                            System.out.println(connection + " (" + streamId + ") >> " + headers.get(i));
                        }
                    }

                    public void onFrameInput(final HttpConnection connection, final int streamId, final RawFrame frame) {
                    }


                    public void onFrameOutput(final HttpConnection connection, final int streamId, final RawFrame frame) {
                    }

                    public void onInputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) {
                    }

                    public void onOutputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) {
                    }

                })
                .create();
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                System.out.println("HTTP requester shutting down");
                requester.shutdown(ShutdownType.GRACEFUL);
            }
        });
        requester.start();

        HttpHost target = new HttpHost("localhost", 1090, "https");
        String[] requestUris = new String[] {"/rest/rest/helloWorld"};

        final CountDownLatch latch = new CountDownLatch(requestUris.length);
        for (final String requestUri: requestUris) {
            final Future<AsyncClientEndpoint> future = requester.connect(target, Timeout.ofSeconds(5));
            final AsyncClientEndpoint clientEndpoint = future.get();
            clientEndpoint.execute(
                    new BasicRequestProducer("GET", target, requestUri),
                    new BasicResponseConsumer<String>(new StringAsyncEntityConsumer()),
                    new FutureCallback<Message<HttpResponse, String>>() {


                        public void completed(final Message<HttpResponse, String> message) {
                            clientEndpoint.releaseAndReuse();
                            HttpResponse response = message.getHead();
                            String body = message.getBody();
                            System.out.println(requestUri + "->" + response.getCode() + " " + response.getVersion());
                            System.out.println(body);
                            latch.countDown();
                        }


                        public void failed(final Exception ex) {
                            clientEndpoint.releaseAndDiscard();
                            System.out.println(requestUri + "->" + ex);
                            ex.printStackTrace();
                            latch.countDown();
                        }


                        public void cancelled() {
                            clientEndpoint.releaseAndDiscard();
                            System.out.println(requestUri + " cancelled");
                            latch.countDown();
                        }

                    });
        }

        latch.await();
        System.out.println("Shutting down I/O reactor");
        requester.initiateShutdown();
    }

}

Я получаю следующее исключение при запуске кода.

java.io.IOException: An existing connection was forcibly closed by the remote host
at sun.nio.ch.SocketDispatcher.read0(Native Method)
at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223)
at sun.nio.ch.IOUtil.read(IOUtil.java:197)
at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:380)
at org.apache.hc.core5.reactor.ssl.SSLIOSession.receiveEncryptedData(SSLIOSession.java:443)
at org.apache.hc.core5.reactor.ssl.SSLIOSession.isAppInputReady(SSLIOSession.java:498)
at org.apache.hc.core5.reactor.InternalDataChannel.onIOEvent(InternalDataChannel.java:112)
at org.apache.hc.core5.reactor.InternalChannel.handleIOEvent(InternalChannel.java:50)
at org.apache.hc.core5.reactor.SingleCoreIOReactor.processEvents(SingleCoreIOReactor.java:173)
at org.apache.hc.core5.reactor.SingleCoreIOReactor.doExecute(SingleCoreIOReactor.java:123)
at org.apache.hc.core5.reactor.AbstractSingleCoreIOReactor.execute(AbstractSingleCoreIOReactor.java:80)
at org.apache.hc.core5.reactor.IOReactorWorker.run(IOReactorWorker.java:44)
at java.lang.Thread.run(Thread.java:745)

Если я не устанавливаю установленную политику версий как HttpVersionPolicy.FORCE_HTTP_2 в H2RequesterBootstrap, она работает, но в этом случае она использует HTTP/1.1. Но я должен использовать только HTTP2.

Просто чтобы проверить, я попробовал это с клиентом Jetty HTTP2, и это сработало. Но я должен использовать только Apache HttpClient 5.0.

Не могли бы вы помочь, пожалуйста? Благодарю.

0 ответов

Я думаю, что сервер, к которому вы пытаетесь подключиться, не поддерживает http2, вам нужно настроить веб-приложение на прием запросов http2. Если вы используете apache tomcat, вам необходим tomcat 9.x с JDK9 или выше для поддержки http2. Проверьте здесь, чтобы настроить http2 для Apache Tomcat. Вам также необходимо установить библиотеки openssl и apr, которые поддерживают http2.

Перейдите по этой ссылке для установки библиотек apr и openssl

Другие вопросы по тегам