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

Nelio Lucas 7 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 Lucas 7 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

Manssouri 3 years ago

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