У меня есть сервер Tomcat, настроенный для HTTP1.1 и HTTP/2 на порту 8002, а также HTTPS на порту 8003, чтобы я мог протестировать его. Я пишу код Java, используя Apache HTTP Client 5.0, чтобы общаться по HTTP/2 с сервером и иметь проблемы. В конечном счете, я хочу иметь возможность отправить сообщение с содержимым на сервер с помощью HTTP/2, но у меня возникли проблемы, поэтому я урезал свой код до простого примера, основанного на одном из примеров, поставляемых Apache — H2TlsAlpnRequestExecutionExample.
В данном примере мы обращаемся к https://nghttp2.org с помощью запроса GET, и я могу заставить его работать. Переход на запрос POST также работает — хотя вы получаете ответ HTTP 405 (метод не разрешен), вызов был выполнен. Если я укажу точно такой же пример на свой сервер, то я смогу успешно ПОЛУЧИТЬ и ПОСТАВИТЬ к обычному порту HTTP/1.1 (8002), используя HTTP/2. Однако попытки GET или POST для HTTPS через порт 8003 терпят неудачу, хотя GET/POST с использованием HTTPv1.1 (а не HTTP/2) также работают с HTTPS/8003.
При включенной DEBUG я не вижу много информации об отладке из библиотек apache, но я вижу, что мои сообщения журнала о том, что моему сертификату доверяют, в обоих случаях, поэтому, очевидно, выполняется какое-то начальное согласование вызовов.
Это журнал для вызова HTTP2 к nghttp2.org (который, как я вижу, использует HTTP/2 из строки в журнале):
javax.net.ssl|DEBUG|01|main|2021-03-04 12:37:29.021 GMT|SSLCipher.java:438|jdk.tls.keyLimits: entry = AES/GCM/NoPadding KeyUpdate 2^37. AES/GCM/NOPADDING:KEYUPDATE = 137438953472
0 [main] DEBUG com.test.HTTP2Tester - Making requst to https://nghttp2.org:443
422 [main] INFO com.test.HTTP2Tester - Awaiting latch to countdown ....
javax.net.ssl|DEBUG|0E|requester-dispatch-1|2021-03-04 12:37:30.345 GMT|SSLCipher.java:1840|KeyLimit read side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
countdown value = 137438953472
javax.net.ssl|DEBUG|0E|requester-dispatch-1|2021-03-04 12:37:30.347 GMT|SSLCipher.java:1994|KeyLimit write side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
countdown value = 137438953472
725 [requester-dispatch-1] INFO com.test.HTTP2Tester - Trusting certificate from CN=nghttp2.org
javax.net.ssl|DEBUG|0E|requester-dispatch-1|2021-03-04 12:37:30.378 GMT|SSLCipher.java:1840|KeyLimit read side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
countdown value = 137438953472
javax.net.ssl|DEBUG|0E|requester-dispatch-1|2021-03-04 12:37:30.379 GMT|SSLCipher.java:1994|KeyLimit write side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
countdown value = 137438953472
942 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - 0 <<[type=4, flags=0, streamId=0, payoad=java.nio.HeapByteBuffer[pos=9 lim=33 cap=65545]]
943 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - 0 <<[type=4, flags=0, streamId=0, payoad=java.nio.HeapByteBuffer[pos=9 lim=33 cap=65545]]
958 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) >> :method: POST
958 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) >> :scheme: https
959 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) >> :authority: nghttp2.org:443
959 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) >> :path: /httpbin/ip
959 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) >> user-agent: Apache-HttpCore/5.0.2 (Java/11.0.10)
966 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - 0 <<[type=4, flags=1, streamId=0, payoad=null]
966 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - 0 <<[type=4, flags=1, streamId=0, payoad=null]
1168 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - 1 <<[type=1, flags=4, streamId=1, payoad=java.nio.HeapByteBuffer[pos=9 lim=258 cap=65545]]
1169 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - 1 <<[type=1, flags=4, streamId=1, payoad=java.nio.HeapByteBuffer[pos=9 lim=258 cap=65545]]
1170 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) << :status: 405
1170 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) << date: Thu, 04 Mar 2021 12:37:30 GMT
1172 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) << content-type: text/html
1173 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) << allow: GET, HEAD, OPTIONS
1173 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) << content-length: 178
1173 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) << access-control-allow-origin: *
1174 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) << access-control-allow-credentials: true
1175 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) << x-backend-header-rtt: 0.002696
1176 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) << strict-transport-security: max-age=31536000
1176 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) << server: nghttpx
1176 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) << via: 1.1 nghttpx
1177 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) << alt-svc: h3-29=":443"; ma=3600
1177 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) << x-frame-options: SAMEORIGIN
1177 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) << x-xss-protection: 1; mode=block
1178 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - nghttp2.org/139.162.123.134:443 (1) << x-content-type-options: nosniff
1186 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - 1 <<[type=0, flags=1, streamId=1, payoad=java.nio.HeapByteBuffer[pos=267 lim=445 cap=65545]]
1186 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - 1 <<[type=0, flags=1, streamId=1, payoad=java.nio.HeapByteBuffer[pos=267 lim=445 cap=65545]]
1188 [requester-dispatch-1] INFO com.test.HTTP2Tester - Complete : /httpbin/ip->405 HTTP/2.0
1189 [requester-dispatch-1] DEBUG com.test.HTTP2Tester - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>405 Method Not Allowed</title>
<h1>Method Not Allowed</h1>
<p>The method is not allowed for the requested URL.</p>
1189 [main] INFO com.test.HTTP2Tester - Awaiting latch to countdown .... done
Это журнал, когда я указываю на свой сервер. Вы можете видеть, что он говорит, что доверяет сертификату, поэтому он должен был подключиться и провести некоторые переговоры, но после этого ничего не происходит.
javax.net.ssl|DEBUG|01|main|2021-03-04 14:51:36.352 GMT|SSLCipher.java:438|jdk.tls.keyLimits: entry = AES/GCM/NoPadding KeyUpdate 2^37. AES/GCM/NOPADDING:KEYUPDATE = 137438953472
0 [main] DEBUG com.test.HTTP2Tester - Making requst to https://localhost:8003
54 [main] INFO com.test.HTTP2Tester - Awaiting latch to countdown ....
117 [requester-dispatch-1] INFO com.test.HTTP2Tester - Trusting certificate from EMAILADDRESS=john.fergus@......
javax.net.ssl|DEBUG|0E|requester-dispatch-1|2021-03-04 14:51:37.145 GMT|Alert.java:238|Received alert message (
"Alert": {
"level" : "warning",
"description": "close_notify"
}
)
182 [requester-dispatch-1] WARN com.test.HTTP2Tester - Failed : /VSFTestMT_Test/Test/Test_EchoHttpGet->org.apache.hc.core5.http.ConnectionClosedException: Connection is closed
183 [main] INFO com.test.HTTP2Tester - Awaiting latch to countdown .... done
Это код.
public void testWithHTTP2() throws Exception {
final String testName = "testWithHTTP2";
String content = "Code=99&Message=Goodbye";
String contentType = "application.x_www_form_urlencoded";
final SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
final X509Certificate cert = chain[0];
Log.info("Trusting certificate from "+cert.getSubjectDN());
return true;
}
})
.build();
final TlsStrategy tlsStrategy = ClientTlsStrategyBuilder.create()
.setSslContext(sslcontext)
// IMPORTANT uncomment the following method when running Java 9 or older
// in order for ALPN support to work and avoid the illegal reflective
// access operation warning
.setTlsDetailsFactory(new Factory<SSLEngine, TlsDetails>() {
@Override
public TlsDetails create(final SSLEngine sslEngine) {
return new TlsDetails(sslEngine.getSession(), sslEngine.getApplicationProtocol());
}
})
.build();
final H2Config h2Config = H2Config.custom()
.setPushEnabled(false)
.build();
HttpAsyncRequester requester = H2RequesterBootstrap.bootstrap()
.setH2Config(h2Config)
.setIOReactorConfig(IOReactorConfig.custom()
.setSoTimeout(Timeout.ofSeconds(5))
.build())
.setTlsStrategy(tlsStrategy)
.setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_2)
.setStreamListener(new H2StreamListener() {
//final FramePrinter framePrinter = new FramePrinter();
private void logFrameInfo(final String prefix, final RawFrame frame) {
Log.debug(prefix);
Log.debug(frame.toString());
}
private void logFramePayload(final String prefix, final RawFrame frame) {
Log.debug(prefix);
Log.debug(frame.getPayload());
}
@Override
public void onHeaderInput(final HttpConnection connection, final int streamId, final List<? extends Header> headers) {
for (int i = 0; i < headers.size(); i++) {
Log.debug(connection.getRemoteAddress() + " (" + streamId + ") << " + headers.get(i));
}
}
@Override
public void onHeaderOutput(final HttpConnection connection, final int streamId, final List<? extends Header> headers) {
for (int i = 0; i < headers.size(); i++) {
Log.debug(connection.getRemoteAddress() + " (" + streamId + ") >> " + headers.get(i));
}
}
@Override
public void onFrameInput(final HttpConnection connection, final int streamId, final RawFrame frame) {
if (Log.isDebugEnabled()) {
Log.debug(streamId + " <<" + frame);
}
if (Log.isDebugEnabled()) {
Log.debug(streamId + " <<" + frame);
}
}
@Override
public void onFrameOutput(final HttpConnection connection, final int streamId, final RawFrame frame) {
}
@Override
public void onInputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) {
}
@Override
public void onOutputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) {
}
})
.create();
CountDownLatch countDownLatch = new CountDownLatch(1);
requester.start();
// This tells me it trusts the cert, but get no further - just a "connection is closed" failure
final HttpHost target = new HttpHost("https", "localhost", 8003);
final String requestUri = "/VSFTestMT_Test/Test/Test_EchoHttpGet";
// This works fine ... get a 405 saying POST is not allowed.
// final HttpHost target = new HttpHost("https", "nghttp2.org", 443);
// final String requestUri = "/httpbin/ip";
Log.debug("Making requst to "+target);
final Future<AsyncClientEndpoint> future = requester.connect(target, Timeout.ofSeconds(5));
final AsyncClientEndpoint clientEndpoint = future.get();
final Future<Message<HttpResponse, String>> resultFuture1 = clientEndpoint.execute(
new BasicRequestProducer(
Method.POST, target, requestUri, new StringAsyncEntityProducer(content, ContentType.create(contentType))),
new BasicResponseConsumer<>(new StringAsyncEntityConsumer()),
new FutureCallback<Message<HttpResponse, String>>() {
@Override
public void completed(final Message<HttpResponse, String> message) {
clientEndpoint.releaseAndReuse();
final HttpResponse response = message.getHead();
final String body = message.getBody();
Log.info("Complete : "+requestUri + "->" + response.getCode() + " " + response.getVersion());
Log.debug(body);
countDownLatch.countDown();
}
@Override
public void failed(final Exception ex) {
clientEndpoint.releaseAndDiscard();
Log.warn("Failed : "+requestUri + "->" + ex);
countDownLatch.countDown();
}
@Override
public void cancelled() {
clientEndpoint.releaseAndDiscard();
Log.warn("Cancelled : "+requestUri );
countDownLatch.countDown();
}
});
Log.info("Awaiting latch to countdown ....");
countDownLatch.await();
Log.info("Awaiting latch to countdown .... done");
Message<HttpResponse, String> response = resultFuture1.get();
Assert.assertNotNull(response);
final HttpResponse response1 = response.getHead();
final String entity1 = response.getBody();
Assert.assertNotNull(response1);
Assert.assertEquals(200, response1.getCode());
assertTrue("Check we get Message Success back",response.getBody().contains("Success"));
assertTrue("Check we get Message Goodbye back",response.getBody().contains("Goodbye"));
Log.info("Shutting down I/O reactor");
requester.initiateShutdown();
}
У кого-нибудь есть идеи, как отладить проблему или решить ее? Я думаю, что моя установка сервера Tomcat в порядке. Есть ли что-нибудь или инструмент, который я могу использовать, чтобы проверить это? Могу ли я увидеть дополнительную информацию об отладке/журнале, чтобы узнать, почему соединение закрыто и что произошло, чтобы закрыть его?