Получение ошибки с 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