4

I'm using this Websocket library with the example from here to create multiple services based on websockets.

The main server taking the requests is an Nginx server which forwards the requests acording to their origin (domain based), so I can know for sure the requests received are for websockets.

The problem however is here:

public void onOpen(WebSocket ws, ClientHandshake handshake) {
    System.out.println("-------------------------------");
    System.out.println(ws.getRemoteSocketAddress());
    System.out.println("-------------------------------");
}

getRemoteSocketAddress will always return the nginx server IP, something like /192.168.1.100:43556

My nginx configuration is:

server{
    listen 80;
    server_name we.mydomain.com;
    access_log /var/log/nginx/access-ws-debug.log;
    error_log /var/log/nginx/error-ws-debug.log;

    set_real_ip_from  192.168.1.0/24;
    real_ip_header X-Real-IP;
    real_ip_recursive on;

    location / {
       proxy_pass http://192.168.1.101:54321;

       proxy_http_version 1.1;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header Host $http_host; #host
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header Y-Real-IP $realip_remote_addr;

       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "Upgrade";

       proxy_connect_timeout 1d;
       proxy_send_timeout 1d;
       proxy_read_timeout 1d;
    }
}

Is there any problem with the nxing configuration? Or is there any other way to get the IP of the user behind the websocket?


Solved thanks to Mark's answer. The following code is not related to the library mentioned. It's the "classic" web implementation in jsp without any library.

I modified the handshake to look like this:

@Override
public void modifyHandshake(
        ServerEndpointConfig config,
        HandshakeRequest request,
        HandshakeResponse response
) {
    HttpSession httpSession = (HttpSession) request.getHttpSession();
    config.getUserProperties().put(HttpSession.class.getName(), httpSession);
    config.getUserProperties().put("Headers",request.getHeaders());
}

And to get the Headers, just invoking this in the OnOpen:

@OnOpen
public void onOpen(final Session session, EndpointConfig config){ //This happens when javascript opens socket session
    Object list = config.getUserProperties().get("Headers");
    System.out.println("-------------------------------------");
    System.out.println(list);
    System.out.println("-------------------------------------");
}

The returned format for the headers is like the following (using my phone):

{upgrade=[websocket], 
accept-language=[en-US,en;q=0.9,es;q=0.8], 
cookie=[JSESSIONID=0BAE99DFDE5DD81D20CFF04E4257D7FE], 
sec-websocket-extensions=[permessage-deflate; client_max_window_bits], 
origin=[https://mywebsite.com], 
x-forwarded-for=[1.2.3.4], 
sec-websocket-version=[13], 
pragma=[no-cache], 
x-real-ip=[1.2.3.4], 
y-real-ip=[1.2.3.4], 
host=[mywebsite.com], 
connection=[Upgrade], 
sec-websocket-key=[ZroOewrBGrg660x7ocI+nog==], 
cache-control=[no-cache], 
accept-encoding=[gzip, deflate, br], 
user-agent=[Mozilla/5.0 (Linux; Android 8.0.0; SM-N950F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36]}

And the IP is geo-localizable near my area.

Alpha2k
  • 2,212
  • 7
  • 38
  • 65

1 Answers1

2

It is not a trivial task to do, but your configuration looks OK you just have to get the original IP from the header you set (X-Real-IP).

import javax.websocket.*;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;

@ServerEndpoint(value = "/test", configurator = Test.TestConfiguratior.class)
public class Test extends Endpoint {

    @Override
    public void onOpen(Session session, EndpointConfig endpointConfig) {
        Object o = endpointConfig.getUserProperties().get(TestConfiguratior.HEADER_NAME);
        //o should be the string object you are looking for
    }

    @OnMessage
    public void onMessage(Session session, String msg) {
        //do what u want
    }

    public static class TestConfiguratior extends ServerEndpointConfig.Configurator {
        static String HEADER_NAME = "X-Real-IP";

        @Override
        public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
            String firstFoundHeader = request.getHeaders().get(HEADER_NAME.toLowerCase()).get(0);
            sec.getUserProperties().put(HEADER_NAME, firstFoundHeader);
        }
    }
}

TL.DR.: You can use the javax.websocket.EndpointConfig#getUserProperties map to put data in it and retrieve it later. Initially I thought that this can cause some concurrency problems while using this exact code, but I was not able to reproduce it. Even the documentation recommends to do so this way: https://javaee.github.io/tutorial/websocket010.html

Update: This is a JSR 356 example, not specific for the mentioned framework.

Hash
  • 4,647
  • 5
  • 21
  • 39
  • what concurrency problems could this cause? – Alpha2k Nov 13 '18 at 19:19
  • Basically `ServerEndpointConfig.getUserProperties()` is "global". If there are many concurrent incoming connections you can override this value before you read it. So the code is OK for having a proof of concept, but *not OK for production* in my opinion. If you do not want to use any CDI stuff you can experiment with `ThreadLocal` as well, but I would prefer to use some Context instead. – Hash Nov 13 '18 at 22:34
  • could you update your example with how would you implement it for production? That would be great once we hit production stage, will also help people looking for this in a future. Many thanks. – Alpha2k Nov 15 '18 at 20:48
  • Yeah, I'll do it tomorrow when I'll be in front of my machine. Are you in a CDI container? – Hash Nov 16 '18 at 11:17
  • Well I was trying to reproduce the concurrency problem but without luck.. maybe I remembered incorrectly. Even the JEE documentation, uses what I have recommended: https://javaee.github.io/tutorial/websocket010.html – Hash Nov 18 '18 at 10:44