Bug 59659

Summary: WsSession leaks (is not GC) if error occurs during the closing process
Product: Tomcat 8 Reporter: Jan Mika <janmika7>
Component: WebSocketAssignee: Tomcat Developers Mailing List <dev>
Status: RESOLVED FIXED    
Severity: normal    
Priority: P2    
Version: 8.5.2   
Target Milestone: ----   
Hardware: PC   
OS: All   

Description Jan Mika 2016-06-03 07:32:48 UTC
Tested on tomee 7.0.0. using tomcat-websocket.jar version 8.5.2.
I found it during the long-term using of WebSockets. After several page switches and re-openings some WsSessions  were not  removed even after the client session finished. It was difficult to reproduce. But here is how to do it more predictably:

Open the HTML page defined below and several time click reload current page. If error occurs on Java side (it means that wsError occurs) the WsSession will not be removed from GC. I tested it mostly with Firefox but it work also on IE 11 etc. JDK 1.7.0_51 64 bit was used.



HTML code:

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>WebSocket Test</title>
	<script>
		var ws = null;
		var i=0;
		var myVar = setInterval(myTimer, 10);
		function myTimer() {
			if(ws) return;
			ws = new WebSocket("ws://localhost:8080/WsMemoryLeak/ws");
			console.log("reconnecting " + i++);
            ws.onopen = function() {
               //ws.send("Message to send");
               console.log("onocpen " + i++);
               ws.close();
               ws = null;
            };
				
            ws.onmessage = function (evt) { 
               var received_msg = evt.data;
               console.log("onmessage " + evt.data);
            };
				
            ws.onclose = function(){
           	 console.log("onclose");
            };

		}
		window.onbeforeunload = function(){
			if(ws) ws.close();
		}
	</script>
</head>
<body>
	<h2>WebSocket test</h2>
</body>

Java Code:

import java.io.IOException;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicInteger;

import javax.websocket.MessageHandler;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import org.apache.tomcat.websocket.WsSession;


@ServerEndpoint(value = "/ws")
public class MyWebsocket {
    private static final AtomicInteger count  = new AtomicInteger (0);
    @OnOpen
    public void wsOpen(Session session){
        WsSession s = (WsSession) session;
        int c = count.incrementAndGet();
        System.out.println("WS Opened " + c);
    }
    
    @OnError
    public void wsError(Session session, Throwable t){
        System.out.println("WS Error ");
        try {
            WsSession s = (WsSession) session;
            s.close();
        } catch (IOException e) {}
    }
    
    @OnClose
    public void wsClosed(Session session){
        int c = count.decrementAndGet();
        System.out.println("WS Closed " + c);
        try {
            session.close();
        } catch (IOException e) {}
    }
}
Comment 1 Mark Thomas 2016-06-06 16:30:00 UTC
Thanks for the report. This was introduced in the connector refactoring so 8.0.x and earlier are not affected.

It has been fixed in 9.0.x for 9.0.0.M7 onwards and 8.5.x for 8.5.3 onwards.