1
votes

I'm writing a WebSocket client in a java application, using the Jetty 9.4.18 libraries.

I'm pretty new to WebSockets, so I started testing using the two sample classes from the Jetty documentation, connecting to echo.websocket.org

The test runs fine when I connect without SSL, but if fails when the connection is done to wss://echo.websocket.org

I always get the same exception:

java.io.EOFException: HttpConnectionOverHTTP@50371e9d::DecryptedEndPoint@6dc65fc2{echo.websocket.org/174.129.224.73:443<->/192.168.1.34:60521,OPEN,fill=-,flush=C,to=226/0}
    at org.eclipse.jetty.client.http.HttpReceiverOverHTTP.earlyEOF(HttpReceiverOverHTTP.java:338)
    at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:1551)
    at org.eclipse.jetty.client.http.HttpReceiverOverHTTP.shutdown(HttpReceiverOverHTTP.java:209)
    at org.eclipse.jetty.client.http.HttpReceiverOverHTTP.process(HttpReceiverOverHTTP.java:147)
    at org.eclipse.jetty.client.http.HttpReceiverOverHTTP.receive(HttpReceiverOverHTTP.java:73)
    at org.eclipse.jetty.client.http.HttpChannelOverHTTP.receive(HttpChannelOverHTTP.java:133)
    at org.eclipse.jetty.client.http.HttpConnectionOverHTTP.onFillable(HttpConnectionOverHTTP.java:155)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:305)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
    at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.onFillable(SslConnection.java:411)
    at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:305)
    at org.eclipse.jetty.io.ssl.SslConnection$2.succeeded(SslConnection.java:159)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
    at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:765)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:683)
    at java.lang.Thread.run(Thread.java:748)

It looks like the server closes without answering to the handshake request.

I am aware of SslContextFactory, but my understanding is that it should be used only if you need your own TrustStore or KeyStore, or for other special cases.

Note also that after some failed attempt I downloaded another websocket implementation from https://github.com/TooTallNate/Java-WebSocket, and it works fine with both ws and wss, without setting anything specific for SSL. However for this project I'm bind to use Jetty.

The code I'm using is exactly the sample from Jetty documentation at https://www.eclipse.org/jetty/documentation/9.4.x/jetty-websocket-client-api.html

the only change I did was to add an onError method to SimpleEchoSocket, that dumps the full exception stack.

Am I missing something?

Thanks in advance!

1

1 Answers

1
votes

Unfortunately, websocket.org (and Kaazing host/proxy) has a bunch of TLS issues at this point in time, so using their public servers are not sane choice right now.

Here's a different demo, also using TLS and WebSocket, against a stackexchange server with a proper and sane TLS/SSL implementation.

This was written against Jetty 9.4.18.v20190429

package org.eclipse.jetty.demo;

import java.net.URI;
import java.util.concurrent.Future;

import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.client.WebSocketClient;

@WebSocket
public class SecureClientSocket
{
    private static final Logger LOG = Log.getLogger(SecureClientSocket.class);

    public static void main(String[] args)
    {
        String url = "wss://qa.sockets.stackexchange.com/";

        SslContextFactory ssl = new SslContextFactory.Client();
        ssl.setEndpointIdentificationAlgorithm("HTTPS");
        HttpClient http = new HttpClient(ssl);
        WebSocketClient client = new WebSocketClient(http);
        try
        {
            http.start();
            client.start();
            SecureClientSocket socket = new SecureClientSocket();
            Future<Session> fut = client.connect(socket, URI.create(url));
            Session session = fut.get();
            session.getRemote().sendString("Hello");
            session.getRemote().sendString("155-questions-active");
        }
        catch (Throwable t)
        {
            LOG.warn(t);
        }
        finally
        {
            stop(http);
            stop(client);
        }
    }

    private static void stop(LifeCycle lifeCycle)
    {
        try
        {
            lifeCycle.stop();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    @OnWebSocketConnect
    public void onConnect(Session sess)
    {
        LOG.info("onConnect({})", sess);
    }

    @OnWebSocketClose
    public void onClose(int statusCode, String reason)
    {
        LOG.info("onClose({}, {})", statusCode, reason);
    }

    @OnWebSocketError
    public void onError(Throwable cause)
    {
        LOG.warn(cause);
    }

    @OnWebSocketMessage
    public void onMessage(String msg)
    {
        LOG.info("onMessage() - {}", msg);
    }
}