Establishng a WebSocket connection to traccar server using a java client in spring boot.

Nelio Lucas6 years ago

i've created a java websocket client using javax.websocket:javax.websocket-client-api:1.0 library.
iv attempted to establish a websocket connection to the traccar server but i keep getting the following error :

The HTTP response from the server [503] did not permit the HTTP upgrade to WebSocket

iv gone through numerous sources (one of which being this one (https://www.traccar.org/forums/topic/python-websockets-results-in-handshake-status-503/)) and it has come to my understanding that i should use cookie session authentication. However im not sure how to do so.

My current Endpoint implementation is the following

package ocsy.app.config;

import javax.websocket.*;
import java.net.URI;
import java.util.Arrays;
import java.util.Map;
import java.util.logging.Logger;

import static ocsy.app.utils.Utils.auth;

@ClientEndpoint
public class WebsocketClientEndpoint extends Endpoint {

    private Logger logger = Logger.getLogger(this.getClass().getName());
    Session session = null;
    
    
    public WebsocketClientEndpoint(String endpointURI) {
        //start connection
        javax.websocket.WebSocketContainer container = javax.websocket.ContainerProvider.getWebSocketContainer();
        try {
            ClientEndpointConfig.Configurator configurator = new ClientEndpointConfig.Configurator() {
                public void beforeRequest(Map headers) {
                    headers.put("Authorization", Arrays.asList(auth("admin","admin")));
                }
            };
            ClientEndpointConfig clientConfig = ClientEndpointConfig.Builder.create()
                    .configurator(configurator)
                    .build();

            container.connectToServer(this,clientConfig, new URI(endpointURI));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onOpen(Session session,EndpointConfig config) {
        logger.info("Client onOpen..." + session.getId());
  

        this.session = session;
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        logger.info("MyClientEndpoint log: " + message);
    }

    @Override
    public void onClose(Session session, CloseReason closeReason) {
        logger.info(String.format("Session %s close because of %s", session.getId(), closeReason));
    }


}

I instatiated the method as follows :

WebsocketClientEndpoint clientEndPoint = new WebsocketClientEndpoint("ws://ocsytracker.hopto.org:8082/api/socket");

I get the following error :

javax.websocket.DeploymentException: The HTTP response from the server [503] did not permit the HTTP upgrade to WebSocket
    at org.apache.tomcat.websocket.WsWebSocketContainer.connectToServerRecursive(WsWebSocketContainer.java:434)
    at org.apache.tomcat.websocket.WsWebSocketContainer.connectToServer(WsWebSocketContainer.java:194)
    at ocsy.app.config.WebsocketClientEndpoint.<init>(WebsocketClientEndpoint.java:40)
    at ocsy.app.controllers.MapController.startWebSocket(MapController.java:105)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
        ...
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

I'd very much appreciate anyones help on the matter. Thank you in advance

Nelio Lucas6 years ago

so i managed to solve this issue!

The problem was on the server side. all i did was update the AsyncSocketServlet class with the following code

/*
 * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.traccar.api;

import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
import org.traccar.Context;
import org.traccar.api.resource.SessionResource;
import org.traccar.helper.Log;
import org.traccar.model.User;

import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import java.lang.reflect.Method;
import java.sql.SQLException;


public class AsyncSocketServlet extends WebSocketServlet {

    public static final String AUTHORIZATION_HEADER = "Authorization";
    public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
    public static final String BASIC_REALM = "Basic realm=\"api\"";
    public static final String X_REQUESTED_WITH = "X-Requested-With";
    public static final String XML_HTTP_REQUEST = "XMLHttpRequest";
    private static final long ASYNC_TIMEOUT = 10 * 60 * 1000;

    @javax.ws.rs.core.Context
    private HttpServletRequest request;

    @javax.ws.rs.core.Context
    private ResourceInfo resourceInfo;

    @javax.ws.rs.core.Context
    private SecurityContext securityContext;

    protected long getUserId() {
        UserPrincipal principal = (UserPrincipal) securityContext.getUserPrincipal();
        if (principal != null) {
            return principal.getUserId();
        }
        return 0;
    }

    @Override
    public void configure(WebSocketServletFactory factory) {
        factory.getPolicy().setIdleTimeout(Context.getConfig().getLong("web.timeout", ASYNC_TIMEOUT));
        factory.setCreator(new WebSocketCreator() {
            @Override
            public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp) {

                auth(req);
                return new AsyncSocket(getUserId());

            }
        });
    }

    private void auth(ServletUpgradeRequest req) {

        request = req.getHttpServletRequest();

        if (req.getHttpServletRequest().getMethod().equals("OPTIONS")) {
            return;
        }

        SecurityContext securityContext = null;

        try {

            String authHeader = request.getHeader(AUTHORIZATION_HEADER);
            if (authHeader != null) {

                try {
                    String[] auth = SecurityRequestFilter.decodeBasicAuth(authHeader);
                    User user = Context.getPermissionsManager().login(auth[0], auth[1]);
                    if (user != null) {
                        Context.getStatisticsManager().registerRequest(user.getId());
                        securityContext = new UserSecurityContext(new UserPrincipal(user.getId()));
                    }
                } catch (SQLException e) {
                    throw new WebApplicationException(e);
                }

            } else if (req.getSession() != null) {

                Long userId = (Long) req.getSession().getAttribute(SessionResource.USER_ID_KEY);
                if (userId != null) {
                    Context.getPermissionsManager().checkUserEnabled(userId);
                    Context.getStatisticsManager().registerRequest(userId);
                    securityContext = new UserSecurityContext(new UserPrincipal(userId));
                }

            }

        } catch (SecurityException e) {
            Log.warning(e);
        }

        if (securityContext != null) {
            this.securityContext = securityContext;
        } else {
            Method method = resourceInfo.getResourceMethod();
            if (!method.isAnnotationPresent(PermitAll.class)) {
                Response.ResponseBuilder responseBuilder = Response.status(Response.Status.UNAUTHORIZED);
                if (!XML_HTTP_REQUEST.equals(request.getHeader(X_REQUESTED_WITH))) {
                    responseBuilder.header(WWW_AUTHENTICATE, BASIC_REALM);
                }
                throw new WebApplicationException(responseBuilder.build());
            }
        }

    }

}

the same code can be found here (https://github.com/traccar/traccar/blob/dcbace81a786e6b9157937c10eebed441a03dcfa/src/org/traccar/api/AsyncSocketServlet.java)

i hope this helps anyone facing the same issue

Manssouri2 years ago

Hello, is it possible to contact you for asking you some questions about traccar and websocket? thank you
My email [redacted]