How do I properly close a websocket and and provide a clean, informative response to the client when an internal error occurs on my server? In my current case, the client must provide a parameter when it connects, and I am trying to handle incorrect or missing parameters received by OnOpen.
This example suggests I can just throw an exception in OnOpen, which will ultimately call OnError where I can close with a reason and message. It kinda works, but the client only receives an EOF, 1006, CLOSE_ABNORMAL.
Also, because I have found no other discussion, I can't tell what might be best practice.
I'm using the JSR-356 spec, as follows:
@ClientEndpoint
@ServerEndpoint(value="/ws/events/")
public class WebSocketEvents
{
private javax.websocket.Session session;
private long token;
@OnOpen
public void onWebSocketConnect(javax.websocket.Session session) throws BadRequestException
{
logger.info("WebSocket connection attempt: " + session);
this.session = session;
// this throws BadRequestException if null or invalid long
// with short detail message, e.g., "Missing parameter: token"
token = HTTP.getRequiredLongParameter(session, "token");
}
@OnMessage
public void onWebSocketText(String message)
{
logger.info("Received text message: " + message);
}
@OnClose
public void onWebSocketClose(CloseReason reason)
{
logger.info("WebSocket Closed: " + reason);
}
@OnError
public void onWebSocketError(Throwable t)
{
logger.info("WebSocket Error: ");
logger.debug(t, t);
if (!session.isOpen())
{
logger.info("Throwable in closed websocket:" + t, t);
return;
}
CloseCode reason = t instanceof BadRequestException ? CloseReason.CloseCodes.PROTOCOL_ERROR : CloseReason.CloseCodes.UNEXPECTED_CONDITION;
try
{
session.close(new CloseReason(reason, t.getMessage()));
}
catch (IOException e)
{
logger.warn(e, e);
}
}
}
Edit: The exception throwing per linked example seems weird, so now I am catching exception within OnOpen and immediately doing
session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "some text"));
Edit: This turned out to be correct, though a separate bug disguised it for a while.
Edit2: Clarification: HTTP
is my own static utility class. HTTP.getRequiredLongParameter()
gets query parameters from the client's initial request by using
session.getRequestParameterMap().get(name)
and does further processing.
OnOpen
is closing the session beforeOnError
is called? Are you seeing"Throwable in closed websocket"
being logged? – TonnageServerEndpoint
andClientEndpoint
at the same time? If you don't want to use a filter, you can also consider a custom configuration where you'll check if the parameter is here or not and what value it has – DrugiClientEndpoint
was combined withServerEndpoint
in the first example I found. Thanks for pointing out; removed now. – Lexy