0

I can reach this website with firefox, edge, chrome. But not with the following (I obfuscated the actual company address just in case that there may be a security problem with it).

    SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
    ClientConnector clientConnector = new ClientConnector();
    clientConnector.setSslContextFactory(sslContextFactory);

    HttpClient httpClient = new HttpClient(new HttpClientTransportDynamic(clientConnector));
    httpClient.start();
    ContentResponse res = httpClient.GET("https://some.safe.company.server.de");

The website requires TLS1.3. I am using Jetty 10.0.8 with Java JDK 16.0.2, so the connection should be possible, right? I get the following stack trace (again with obfuscated address):

java.util.concurrent.ExecutionException: java.io.EOFException: HttpConnectionOverHTTP@7845debe::DecryptedEndPoint@6fc8d160[{l=/192.168.145.211:59933,r=some.safe.company.server.de/129.247.33.85:443,OPEN,fill=-,flush=-,to=29960/30000}]
    at org.eclipse.jetty.client.util.FutureResponseListener.getResult(FutureResponseListener.java:113)
    at org.eclipse.jetty.client.util.FutureResponseListener.get(FutureResponseListener.java:96)
    at org.eclipse.jetty.client.HttpRequest.send(HttpRequest.java:769)
    at org.eclipse.jetty.client.HttpClient.GET(HttpClient.java:351)
    at org.eclipse.jetty.client.HttpClient.GET(HttpClient.java:336)
    >>>> httpClient.GET(...
Caused by: java.io.EOFException: HttpConnectionOverHTTP@7845debe::DecryptedEndPoint@6fc8d160[{l=/192.168.145.211:59933,r=some.safe.company.server.de/129.247.33.85:443,OPEN,fill=-,flush=-,to=29960/30000}]
    at org.eclipse.jetty.client.http.HttpReceiverOverHTTP.earlyEOF(HttpReceiverOverHTTP.java:395)
    at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:1605)
    at org.eclipse.jetty.client.http.HttpReceiverOverHTTP.shutdown(HttpReceiverOverHTTP.java:281)
    at org.eclipse.jetty.client.http.HttpReceiverOverHTTP.process(HttpReceiverOverHTTP.java:197)
    at org.eclipse.jetty.client.http.HttpReceiverOverHTTP.receive(HttpReceiverOverHTTP.java:91)
    at org.eclipse.jetty.client.http.HttpChannelOverHTTP.receive(HttpChannelOverHTTP.java:91)
    at org.eclipse.jetty.client.http.HttpConnectionOverHTTP.onFillable(HttpConnectionOverHTTP.java:194)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:319)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
    at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.onFillable(SslConnection.java:530)
    at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:379)
    at org.eclipse.jetty.io.ssl.SslConnection$2.succeeded(SslConnection.java:146)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
    at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
    at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:412)
    at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:381)
    at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:268)
    at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.lambda$new$0(AdaptiveExecutionStrategy.java:138)
    at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:407)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:894)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1038)
    at java.base/java.lang.Thread.run(Thread.java:831)
user3726374
  • 583
  • 1
  • 4
  • 24
  • Remove `new HttpClientTransportDynamic(clientConnector)` and try again, if it works then you have a server that isn't following TLS/1.3 properly with regards to ALPN. – Joakim Erdfelt Mar 08 '22 at 09:56

1 Answers1

0

The HttpReceiverOverHTTP.shutdown() in your stacktrace shows that the remote side closed the connection before sending all of the response data, which results in the HttpReceiverOverHTTP.earlyEOF().

Some servers and web applications just have bad behaviors.
Not all servers behave like that, just some bad ones.

There is nothing you can do on the client side to recover from this.
You need to understand what the server is doing.

We've had one other report that is similar to your situation at https://github.com/eclipse/jetty.project/issues/6701

Here's an example of using the high level HttpClient with a dynamic connector supporting both HTTP/2 and HTTP/1.1, and forced to only use TLSv1.3. This example connects to https://api.github.com/ which implements TLS and HTTP/1.1 properly (even HTTP/2 properly).

Found at the jetty-project/embedded-jetty-cookbook repository as ClientWithDynamicConnection.java

If you don't want HTTP/2 support, then just remove the entire HttpClientTransportDynamic layer, including the ClientConnector and stick with HTTP/1.1 (you'll still need the SslContextFactory.Client tho)

package org.eclipse.jetty.cookbook;

import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
import org.eclipse.jetty.client.http.HttpClientConnectionFactory;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.http.ClientConnectionFactoryOverHTTP2;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory;

/**
 * Example of using a high level HttpClient to connect to a server that
 * supports both HTTP/2 and HTTP/1.1, using TLSv1.3 only.
 */
public class ClientWithDynamicConnection
{
    public static void main(String[] args) throws Exception
    {
        SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
        sslContextFactory.setIncludeProtocols("TLSv1.3");
        ClientConnector clientConnector = new ClientConnector();
        clientConnector.setSslContextFactory(sslContextFactory);

        ClientConnectionFactory.Info h1 = HttpClientConnectionFactory.HTTP11;
        HTTP2Client http2Client = new HTTP2Client(clientConnector);
        ClientConnectionFactory.Info h2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
        HttpClientTransportDynamic dynamicTransport = new HttpClientTransportDynamic(clientConnector, h1, h2);

        HttpClient httpClient = new HttpClient(dynamicTransport);
        try
        {
            httpClient.start();
            // To see the SslContextFactory configuration, dump the client
            System.out.printf("Dump of client: %s%n", httpClient.dump());
            ContentResponse res = httpClient.GET("https://api.github.com/zen");
            System.out.printf("response status: %d%n", res.getStatus());
            res.getHeaders().forEach((field) ->
            {
                System.out.printf("response header [%s]: %s%n", field.getName(), field.getValue());
            });
            System.out.printf("response body: %s%n", res.getContentAsString());
        }
        finally
        {
            httpClient.stop();
        }
    }
}
Joakim Erdfelt
  • 46,896
  • 7
  • 86
  • 136
  • Thanks for the example. I tried it, but I still get the same exception. – user3726374 Mar 08 '22 at 13:21
  • I get your point and I can very well imagine that it is some bad server setup. But on the other hand it seems to be somehow possible to reach the server because browsers do work reliably. Do you know of any jetty setting to get a response on something like best effort basis? – user3726374 Mar 09 '22 at 08:46
  • Browsers are by design more forgiving of partial / incomplete data. Most http clients for APIs (eg: REST) are the opposite, harsh when it comes to incomplete/bad data (just like Jetty HttpClient is) – Joakim Erdfelt Mar 09 '22 at 09:12