4

Using the Tyrus reference implementation of Java's "JSR 356 - Java API for WebSocket", I cannot find a way to access the HTTP connection that was used for the Websocket upgrades. Thus, I cannot access the HTTP headers that the browser sent.

Is there a way to read the HTTP UserAgent header?

Casting a "Session" object to "TyrusSession" or similar would be acceptable, I have to do it to get the Remote Address anyway. Sending the UserAgent again as a message inside the Websocket connection would be my fallback solution.

lathspell
  • 3,040
  • 1
  • 30
  • 49
  • seems like a duplicate of http://stackoverflow.com/questions/28188172/how-to-access-client-hostname-http-headers-etc-from-a-java-websocket-server – Pavel Bucek Mar 10 '15 at 10:08
  • @PavelBucek: The question sounds similar indeed but the answer does not fit: using the ServerEndpointConfig.Configurator, I can only access the headers of the Websocket handshake, i.e.: `{connection=[Upgrade], host=[localhost:8025], origin=[localhost:8025], sec-websocket-key=[vi6GPNd4GIj2oRTcwzWKYA==], sec-websocket-version=[13], upgrade=[websocket]}` The request.getHttpSession() only works in a Java EE container and gives NULL in "normal" mode. – lathspell Mar 10 '15 at 15:23
  • 1
    User-Agent header (when sent) should be there - there is no other way how to get User-Agent (web socket protocol does not send info like that anywhere). – Pavel Bucek Mar 10 '15 at 15:33
  • 1
    Oh, indeed, it's just Java that does not send one. To make this question at least a bit useful, I'll post a solution with source code as answer. – lathspell Mar 10 '15 at 17:22

1 Answers1

1

WARNING: ServerEndpointConfig is shared among all endpoint instances and multiple upgrade requests can be done concurrently! See comments!

The endpoint gets a configurator:

import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(value = "/foo", configurator = MyServerEndpointConfigurator.class)
public class MyEndpoint {

    @OnOpen
    public void onOpen(Session session, EndpointConfig endpointConfig) throws Exception {
        String ip = ((TyrusSession) session).getRemoteAddr();
        String userAgent = (String) endpointConfig.getUserProperties().get("user-agent");
        ...
   }
}

The configurator looks like this:

import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;

public class MyServerEndpointConfigurator extends ServerEndpointConfig.Configurator {

    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
        if (request.getHeaders().containsKey("user-agent")) {
            sec.getUserProperties().put("user-agent", request.getHeaders().get("user-agent").get(0)); // lower-case!
        }
    }
}
lathspell
  • 3,040
  • 1
  • 30
  • 49
  • :-) this is similar to the answer I originally wrote, BUT this is not a good solution. ServerEndpointConfig is shared among all endpoint instances and multiple upgrade requests can be done concurrently - which would effectively rewrite the "user-agent" entry and endpoints won't have the correct information.. – Pavel Bucek Mar 10 '15 at 18:07
  • Then I better delete my answer. But I can't find the your solution in the other question or the Tyrus example you mentioned, though. In the example the ServerEndpoint.Config only sets some response headers for the websocket client, it does not pass any information to the ServerEndpoint, so how am I supposed to do it? – lathspell Mar 11 '15 at 14:15
  • that is a good question and unfortunately, I don't have acceptable answer :-/ can you please file an issue against WebSocket API? https://java.net/jira/browse/WEBSOCKET_SPEC – Pavel Bucek Mar 12 '15 at 11:02