Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jetty 8.1.1 Websocket client handshake

I'm using Jetty 8.1.1 Websocket client api.
I need to update the headers with ("Sec-WebSocket-Protocol", "xsCrossfire") and ("Authorization", "Basic TLVWQMZqRr2hasYnZoI=")

WebSocketClientFactory factory = new WebSocketClientFactory();
factory.start();
client = factory.newWebSocketClient();
client.getCookies().put("Sec-WebSocket-Protocol", "xsCrossfire");
client.getCookies().put("Authorization", "Basic TLVWQMZqRr2hasYnZoI=");
Future<Connection> conn = client.open(uri, (WebSocket) this);
System.out.printf("Connecting to : %s%n", uri);

request looks:

Host: iltlvl262:8000\r\n
Upgrade: websocket\r\n
Connection: Upgrade\r\n
Sec-WebSocket-Version: 13\r\n
Sec-WebSocket-Key: FHKTsICO2vqGCxXVwLkH4Q==\r\n
Cookie: Sec-WebSocket-Protocol=xsCrossfire\r\n
Cookie: Authorization="Basic TLVWQMZqRr2hasYnZoI="\r\n

expected request:

Host: iltlvl262:8000\r\n
Upgrade: websocket\r\n
Connection: Upgrade\r\n
Sec-WebSocket-Version: 13\r\n
Sec-WebSocket-Key: FHKTsICO2vqGCxXVwLkH4Q==\r\n
Sec-WebSocket-Protocol: xsCrossfire\r\n
Authorization: "Basic TLVWQMZqRr2hasYnZoI="\r\n

how do I implement handshake in version 8.1.1 correctly?

like image 593
Tomer.Epstein Avatar asked Mar 21 '23 19:03

Tomer.Epstein


1 Answers

Some good news and some bad news.

First, the Good news:

To set the Sec-WebSocket-Protocol header use the following.

client.setProtocol("xsCrossfire");

before you use client.open()

Next, the Bad news:

With Jetty 8.x you cannot set arbitrary non-websocket headers. This was due to how early experimental drafts of WebSocket were written. You simply were not allowed to set arbitrary headers per the early draft specs, so the implementation back in the Jetty 8.x days just didn't allow it.

However, with the finalization of RFC6455 (the official WebSocket spec) things changed, all of those changes were rolled into the Jetty 9.x codebase. Which is 100% RFC6455 compliant. (Note: Jetty 8 is 100% compliant to RFC6455 on the server side. Jetty 8 is also 100% compatible on the RFC6455 protocol use for both server and client. However, Jetty 8 is only partially compliant on the client side, from a features and API point of view.)

The decision with Jetty 7 and Jetty 8 was made to keep the old experimental drafts around for those early adopters and old browsers (Safari 5.x) that still used them. This decision prevented us from allowing behaviors that are specifically prevented in the old experimental drafts.

Starting with Jetty 9.x all old experimental drafts of websocket were dropped, leaving only RFC6455 to support, which allowed Jetty to open up more features that were previously disallowed. This includes arbitrary headers on the WebSocketClient.

Example of Jetty 9.1 WebSocket Client

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

import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;

public class ExampleClient
{
    public static class ExampleSocket extends WebSocketAdapter
    {
        @Override
        public void onWebSocketText(String message)
        {
            try
            {
                // echo the message
                getRemote().sendString(message);
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args)
    {
        try
        {
            new ExampleClient().demo();
        }
        catch (Throwable t)
        {
            t.printStackTrace(System.err);
        }
    }

    public void demo() throws Exception
    {
        WebSocketClient client = new WebSocketClient();
        try
        {
            client.start();
            ClientUpgradeRequest request = new ClientUpgradeRequest();
            request.setSubProtocols("xsCrossfire");
            request.setHeader("Authorization","Basic TLVWQMZqRr2hasYnZoI=");
            
            URI wsUri = URI.create("ws://iltlvl262:8000/echo");
            
            ExampleSocket socket = new ExampleSocket();
            Future<Session> future = client.connect(socket,wsUri,request);
            
            future.get(); // wait for connect
            
            socket.getRemote().sendString("hello"); // send message
        }
        finally
        {
            client.stop();
        }
    }
}

Also note, that starting with Jetty 9.1, even the javax.websocket (JSR-356) API is fully supported.

Same example using javax.websocket on Jetty 9.1

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.websocket.ClientEndpoint;
import javax.websocket.ContainerProvider;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;

public class ExampleClient
{
    @ClientEndpoint(subprotocols = { "xsCrossfire" }, 
                    configurator = ExampleClient.Configurator.class)
    public static class ExampleSocket
    {
        @OnMessage
        public String onMessage(String msg)
        {
            return msg; // echo
        }
    }

    public static class Configurator 
           extends javax.websocket.ClientEndpointConfig.Configurator
    {
        @Override
        public void beforeRequest(Map<String, List<String>> headers)
        {
            List<String> authvalues = new ArrayList<>();
            authvalues.add("Basic TLVWQMZqRr2hasYnZoI=");
            headers.put("Authorization", authvalues);
            super.beforeRequest(headers);
        }
    }

    public static void main(String[] args)
    {
        try
        {
            new ExampleClient().demo();
        }
        catch (Throwable t)
        {
            t.printStackTrace(System.err);
        }
    }

    public void demo() throws Exception
    {
        WebSocketContainer client = ContainerProvider.getWebSocketContainer();

        ExampleSocket socket = new ExampleSocket();
        URI wsUri = URI.create("ws://iltlvl262:8000/echo");

        Session session = client.connectToServer(socket,wsUri);
        session.getAsyncRemote().sendText("Hello");
    }
}
like image 78
Joakim Erdfelt Avatar answered Apr 02 '23 20:04

Joakim Erdfelt