javawebsocket

How to know the URL of a websocket client request?


I have a WebSocket server deployed on localhost in the user PC. Some website in foo.domain.com is calling the WebSocket using wss://localhost/sign. The websocket server processes the information and returns the data to the website.

Everything is working fine, but I want to know if I can read the URL of the website who is calling the WebSocket. I want to obtain foo.domain.com in the WebSocket server. Right now, I read a variable session.getRequestURI().getHost() and I always get 127.0.0.1. I think this is because the website code is running locally in Javascript in the same machine of the user and where is installed the WebSocket server. The WebSocket server is developed with Java and this is part of my code.

package com.foo.service;

import org.eclipse.jetty.websocket.core.exception.WebSocketTimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.swing.*;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.CountDownLatch;

@ServerEndpoint(value = "/sign", decoders = SignatureRequestDecoder.class, encoders = SignatureResponseEncoder.class)
public class SignEndpoint {
    // Some variables here
    
    @OnMessage
    public void onSignSocketRequest(Session session, SignatureRequest request) throws IOException, EncodeException {
        logger.info("Signature request received.");
        String test = session.getRequestURI().getHost();
        String test1 = session.getRequestURI().getPath();
        String test2 = session.getRequestURI().getFragment();
        String test3 = session.getRequestURI().getQuery();
        String test4 = session.getRequestURI().getScheme();
        logger.info("Test = " + test); // Prints 127.0.0.1
        logger.info("Test1 = " + test1); // Prints /sign
        logger.info("Test2 = " + test2); // Prints null
        logger.info("Test3 = " + test3); // Prints null
        logger.info("Test4 = " + test4); // Prints wss

        // More code here
    }

    // More methods here
}

I don't know if I can obtain the URL I want from this Session parameter or another way. If the client is a desktop app, then can't be a URL, and maybe what I want doesn't make sense.

I don't know if I can get the URL on the first protocol redirection of the WebSocket protocol from HTTP to WS.

EDITED

Ok, as suggested by Remy Lebeau I implemented a custom Configurator to get the Origin header and now I can read this origin from the WebSocket request.

The custom Configurator:

public class ServletAwareConfig extends ServerEndpointConfig.Configurator {
    @Override
    public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
        config.getUserProperties().put("Origin", request.getHeaders().get("Origin"));
    }
}  

I included the configurator in the @ServerEndpoint, I saved its value in @OnOpen and I read it in @OnMessage.

    @ServerEndpoint(value = "/sign", decoders = SignatureRequestDecoder.class, encoders = SignatureResponseEncoder.class,
                    configurator = ServletAwareConfig.class)
    public class SignEndpoint {
        private EndpointConfig endpointConfig;
    
        @OnOpen
        public void onSignSocketOpen(Session session, EndpointConfig endpointConfig) {
            this.endpointConfig = endpointConfig;  
            // More code here.
        }
    
        @OnMessage
        public void onSignSocketRequest(Session session, SignatureRequest request) {
            List<String> originHeader = (List<String>) endpointConfig.getUserProperties().get("Origin");
            // More code here.
        }
    }

Solution

  • The domain you are looking for should be in the Origin request header of the WebSocket handshake, per RFC 6455 Section 1.3:

    The |Origin| header field [RFC6454] is used to protect against unauthorized cross-origin use of a WebSocket server by scripts using the WebSocket API in a web browser. The server is informed of the script origin generating the WebSocket connection request. If the server does not wish to accept connections from this origin, it can choose to reject the connection by sending an appropriate HTTP error code. This header field is sent by browser clients; for non-browser clients, this header field may be sent if it makes sense in the context of those clients.

    However, the Session object in your OnMessage handler is a WebSocket session, not an HTTP session, which is probably why the URI is not what you are expecting.

    Try using an @OnOpen handler with a custom ServerEndpointConfig.Configurator to let your OnMessage handler access the HttpSession (or just the URI or headers) of the handshake.

    See Accessing ServletContext and HttpSession in @OnMessage of a JSR-356 @ServerEndpoint