Creating a WebSocket with Java and AngularJS

In order to create a nice website that is able to update the data itself, there are several options. Probably the easiest one is to make a HTTP request after a predefined period. It does work quite well, but the problem is that HTTP is a request-response protocol, meaning that after initating the connection, sending the request to the server and receiving a response, the connection is automatically closed. Hence, the overhead of opening and closing a connection becomes significant when the process of is repeated several times.
This is the place where WebSockets come in handy. It is a protocol based on TCP that enables the client and the server to send several messages through the same socket. The socket is opened just once and several messages can be exchanged before closing it.

As it can be seen from the picture below, WebSocket is 10 times faster than HTTP requests when exchanging 10000 messages and this kind of boost is most probably noticed by the enduser.
WebSocket vs HTTP: performance
1)REST vs WebSocket Comparison and Benchmarks
How to create a WebSocket that on request echoes the initial message back? First of all, the WebSocket API has to be included to your project 2)WebSocket Server API. The second step would be to create an endpoint as shown in the code example below. The main idea of the code is that on initialization of the WebSocket by a client, a new threadpool is created and the session is stored as an instance variable. After receiving the first message from the client, the same message is sent back to the client after every 2 seconds.

package com.myproject.servlet.endpoint;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@ServerEndpoint("/endpoint")
public class OverviewEndpoint {

    private Session session;
    private ScheduledExecutorService executorService;

    @OnOpen
    public void handleConnect(Session session){
        this.session = session;
        this.executorService = new ScheduledThreadPoolExecutor(1);
    }

    @OnClose
    public void handleClose(){
        this.executorService.shutdown();
    }

    @OnMessage
    public void handleMessage(String message){
        executorService.scheduleAtFixedRate(() -> send(message), 0, 2, TimeUnit.SECONDS);
    }

    private void send(String msg) {
        try{
            this.session.getBasicRemote().sendText(msg);
        } catch (IOException e){
            e.printStackTrace();
        }
    }

}

The first task on the client side is to install angular-websocket library.


angular.module("myApp.socket", [])
.factory('socketFactory', [
	function(){
		var websocket = undefined;

		var service = {};

		service.start = function(endpoint, callback){
			var url = endpoint;
			websocket = new WebSocket(url);

			websocket.onopen = function(){
				websocket.send(sessionId);
			}

			websocket.onclose = function() {};

			websocket.onmessage = function(evt){
				callback(evt);
			}
		}

		service.getWebSocket = function(){
			return websocket;
		}

		return service;
	}
])

The code above defines a socket factory that can be reused for several websocket just by assigning the invoking it with the correct endpoint.


		$scope.function = start() {
			var socket = socketFactory;
			socket.start('ws://www.example.com/socketendpoint', function(data){
				console.log(data);
			});

			$scope.websocket = socket.getWebSocket();
		}

		$scope.$on('$destroy', function(){
            $scope.websocket.close();
        })

Using the factory is quite trivial. Just invoke socketFactory.start method with the correct endpoint and callback function and this should be it. However, it would be clever to store the websocket object itself as a local variable, because when changing the URL of the webpage, the websocket isn’t closed by default and therefore, it needs to be explicitly closed when the current $scope is destroyed.

References   [ + ]

Setting up a permanent reverse SSH tunnel

Reverse SSH is simple yet powerful tool that enables to access devices behind NAT in a convenient way. For example, it is needed to be able to access an IoT device or a PC at your home or workplace. Usually the problem is that the firewall blocks incoming connections to a traditional SSH port 22 and the IP address of the device that you wish to access, changes over the time.
Imagine the following scenario:
reverse ssh
We have two PCs that are behind NAT and a standard server that has a public IP. Our target is to be able to SSH from PC B to PC A.

In order to create a reverse SSH connection, the following steps have to be executed:

  • create an SSH tunnel from PC A to myserver. After executing the command, we should have a port 25432 on myserver tied with port 22 on PC A
    ssh -fN -R 25432:localhost:22 myserver
    
  • SSH into myserver and test it out! We should be able to SSH from myserver to PC A by using the following command:
    ssh pcauser@localhost:25432
    

    However, assuming that the connection of PC A is not always working perfectly, we need to make sure that despite the instability of the connection, we will always have a working tunnel from PC A to myserver such that we would be able to access PC A from anywhere in the world. An option would be to write a script that checks whether the tunnel is up or not, but luckily there is already tool called autossh. Autossh monitors the tunnel and tries to recreate it in case of failures. Hence, we can be assured that even if the connection from PC A to the Internet is not stable, the tunnel is still kept alive as long as there exists a connection.

    Here is a sample script that takes advantage of autossh

    #!/bin/bash
    PORT="9006"
    HOST="0.0.0.0"
    SSH_OPTIONS=" -i /home/pi/.ssh/id_rsa -F /home/pi/.ssh/config "
    ENDPOINT="myserver"
    export AUTOSSH_LOGFILE="/var/log/autossh.log";
    
    autossh -M 20000 -f -N $SSH_OPTIONS $SSH_CONFIG_OPTIONS -R $HOST:$PORT:localhost:22 $ENDPOINT -oStrictHostKeyChecking=no -oLogLevel=info -oUserKnownHostsFile=/dev/null;
    

    It tries to create a permanent ssh tunnel to myserver. If the connection drops, then autossh continues until the connection is restored.
    In order to create the tunnel during startup, the very same script could be added to /etc/init.d and marked as an executable.

  • Now there should be a persistent tunnel from myserver to PC A. In order to connect from PC B to PC A, run the following command in PC B:
    ssh pcauser@myserver -p 9006
    
  • CORS with Java servlets

    CORS (Cross-origin resource sharing) is a mechanism that allows AJAX requests between different domains. It used to be the case that it wasn’t allowed to make a request from a website located at domain X to a server located at domain Y. However, for several reasons such a limitation turned out to be a problem for a lot of developers. Hence, it was a problem needing a solution.

    For example, I have Angular frontend running on port 9000 and a backend API server running on port 8000. Even though they might be available from the same domain name, they are still considered to be running on different domains because the ports don’t match. So I need to deal with CORS and the problems occurring with it.

    The main issue is that I want to add a sessionId header for authentication purposes and also validate the presence of the header in the server side using filters.
    Here is the initial code:

    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    public class InfoFilter implements Filter {
        public void destroy() {
        }
    
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            
            httpResponse.addHeader("Access-Control-Allow-Origin", "*");
            httpResponse.addHeader("Access-Control-Allow-Methods", "GET");
            httpResponse.addHeader("Access-Control-Max-Age", "3600");
            httpResponse.addHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, sessionId");
            httpResponse.addHeader("Access-Control-Allow-Credentials", "true");
    
            if(this.validateRequest(httpRequest)){
                chain.doFilter(httpRequest, httpResponse);
            } else {
                httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
            }
        }
    
        private boolean validateRequest(HttpServletRequest httpRequest) {
            // Validating request
        }
    
        public void init(FilterConfig config) throws ServletException {
    
        }
    
    }
    

    However, the solution doesn’t work because the server returns always 403, which means that the validation of a request in method validateRequest didn’t succeed. After some research I found out a quite good diagram explaining the lifespan of a CORS request.

    1)https://commons.wikimedia.org/wiki/File:Flowchart_showing_Simple_and_Preflight_XHR.svg

    As it can be seen from the figure, if the request has custom headers, then an extra request will be made to the server with HTTP OPTIONS method. This is also known as preflight request. In such case the server has to distinguish between the real (GET, POST etc.) and preflight request and avoid validating the preflight request because it has no custom headers. Therefore, if validateRequest method would check for a specific custom header, then it would return false and therefore the response for the HTTP OPTIONS would be 403, failing also the real request.

    The following depicts a possible solution to the problem.

    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * Created by kert on 5.12.15.
     */
    public class InfoFilter implements Filter {
        public void destroy() {
        }
    
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            HttpServletResponse httpResponse = (HttpServletResponse) response;
    
            httpResponse.addHeader("Access-Control-Allow-Origin", "*");
            httpResponse.addHeader("Access-Control-Allow-Methods", "GET");
            httpResponse.addHeader("Access-Control-Max-Age", "3600");
            httpResponse.addHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, sessionId");
            httpResponse.addHeader("Access-Control-Allow-Credentials", "true");
    
            if(httpRequest.getMethod().equalsIgnoreCase("OPTIONS")){
                httpResponse.setStatus(HttpServletResponse.SC_ACCEPTED);
            } else if(this.validateRequest(httpRequest)){
                chain.doFilter(httpRequest, httpResponse);
            } else {
                httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
            }
    
        }
    
        private boolean validateRequest(HttpServletRequest httpRequest) {
            // validate request
        }
    
        public void init(FilterConfig config) throws ServletException {
    
        }
    
    }
    

    References   [ + ]