![JAR search and dependency download from the Maven repository](/logo.png)
com.codeminders.socketio.server.transport.websocket.WebsocketTransportConnection Maven / Gradle / Ivy
/**
* The MIT License
* Copyright (c) 2015 Alexander Sova ([email protected])
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.codeminders.socketio.server.transport.websocket;
import com.codeminders.socketio.common.ConnectionState;
import com.codeminders.socketio.common.DisconnectReason;
import com.codeminders.socketio.common.SocketIOException;
import com.codeminders.socketio.protocol.BinaryPacket;
import com.codeminders.socketio.protocol.EngineIOPacket;
import com.codeminders.socketio.protocol.EngineIOProtocol;
import com.codeminders.socketio.protocol.SocketIOPacket;
import com.codeminders.socketio.server.*;
import com.codeminders.socketio.server.transport.AbstractTransportConnection;
import com.google.common.io.ByteStreams;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpoint;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @author Alexander Sova ([email protected])
* @author Alex Saveliev ([email protected])
*/
@ServerEndpoint(value="/socket.io/", configurator = WebsocketConfigurator.class)
public final class WebsocketTransportConnection extends AbstractTransportConnection
{
private static final Logger LOGGER = Logger.getLogger(WebsocketTransportConnection.class.getName());
private javax.websocket.Session remote_endpoint;
public WebsocketTransportConnection() {
super(WebsocketTransportProvider.websocket);
}
public WebsocketTransportConnection(Transport transport)
{
super(transport);
}
@Override
protected void init()
{
getSession().setTimeout(getConfig().getTimeout(Config.DEFAULT_PING_TIMEOUT));
if (LOGGER.isLoggable(Level.FINE))
LOGGER.fine(getConfig().getNamespace() + " WebSocket configuration:" +
" timeout=" + getSession().getTimeout());
}
@OnOpen
public void onOpen(javax.websocket.Session session, EndpointConfig config) throws Exception
{
remote_endpoint = session;
setupSession(session);
init(new ServletBasedConfig(
ServletConfigHolder.getInstance().getConfig(),
getTransport().getType().toString()));
session.setMaxBinaryMessageBufferSize(getConfig().getBufferSize());
session.setMaxIdleTimeout(getConfig().getMaxIdle());
session.setMaxTextMessageBufferSize(getConfig().getInt(Config.MAX_TEXT_MESSAGE_SIZE, 32000));
if(getSession().getConnectionState() == ConnectionState.CONNECTING)
{
try
{
send(EngineIOProtocol.createHandshakePacket(getSession().getSessionId(),
new String[]{},
getConfig().getPingInterval(Config.DEFAULT_PING_INTERVAL),
getConfig().getTimeout(Config.DEFAULT_PING_TIMEOUT)));
getSession().onConnect(this);
}
catch (SocketIOException e)
{
LOGGER.log(Level.SEVERE, "Cannot connect", e);
getSession().setDisconnectReason(DisconnectReason.CONNECT_FAILED);
abort();
}
}
}
@OnClose
public void onClose(javax.websocket.Session session, CloseReason closeReason)
{
if(LOGGER.isLoggable(Level.FINE))
LOGGER.log(Level.FINE, "Session[" + getSession().getSessionId() + "]:" +
" websocket closed. " + closeReason.toString());
//If close is unexpected then try to guess the reason based on closeCode, otherwise the reason is already set
if(getSession().getConnectionState() != ConnectionState.CLOSING)
getSession().setDisconnectReason(fromCloseCode(closeReason.getCloseCode().getCode()));
getSession().setDisconnectMessage(closeReason.getReasonPhrase());
getSession().onShutdown();
}
@OnMessage
public void onMessage(String text)
{
if (LOGGER.isLoggable(Level.FINE))
LOGGER.fine("Session[" + getSession().getSessionId() + "]: text received: " + text);
getSession().resetTimeout();
try
{
getSession().onPacket(EngineIOProtocol.decode(text), this);
}
catch (SocketIOProtocolException e)
{
if(LOGGER.isLoggable(Level.WARNING))
LOGGER.log(Level.WARNING, "Invalid packet received", e);
}
}
@OnMessage
public void onMessage(InputStream is)
{
if (LOGGER.isLoggable(Level.FINE))
LOGGER.fine("Session[" + getSession().getSessionId() + "]: binary received");
getSession().resetTimeout();
try
{
getSession().onPacket(EngineIOProtocol.decode(is), this);
}
catch (SocketIOProtocolException e)
{
if(LOGGER.isLoggable(Level.WARNING))
LOGGER.log(Level.WARNING, "Problem processing binary received", e);
}
}
@OnError
public void onError(javax.websocket.Session session, Throwable error) {
// TODO implement
// One reason might be when you are refreshing web page causing connection to be dropped
}
@Override
public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException
{
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unexpected request on upgraded WebSocket connection");
}
@Override
public void abort()
{
getSession().clearTimeout();
if (remote_endpoint != null)
{
disconnectEndpoint();
remote_endpoint = null;
}
}
@Override
public void send(EngineIOPacket packet) throws SocketIOException
{
sendString(EngineIOProtocol.encode(packet));
}
@Override
public void send(SocketIOPacket packet) throws SocketIOException
{
send(EngineIOProtocol.createMessagePacket(packet.encode()));
if(packet instanceof BinaryPacket)
{
Collection attachments = ((BinaryPacket) packet).getAttachments();
for (InputStream is : attachments)
{
ByteArrayOutputStream os = new ByteArrayOutputStream();
try
{
os.write(EngineIOPacket.Type.MESSAGE.value());
ByteStreams.copy(is, os);
}
catch (IOException e)
{
if(LOGGER.isLoggable(Level.WARNING))
LOGGER.log(Level.SEVERE, "Cannot load binary object to send it to the socket", e);
}
sendBinary(os.toByteArray());
}
}
}
protected void sendString(String data) throws SocketIOException
{
if (LOGGER.isLoggable(Level.FINE))
LOGGER.log(Level.FINE, "Session[" + getSession().getSessionId() + "]: send text: " + data);
try
{
remote_endpoint.getBasicRemote().sendText(data);
}
catch (IOException e)
{
disconnectEndpoint();
throw new SocketIOException(e);
}
}
//TODO: implement streaming. right now it is all in memory.
//TODO: read and send in chunks using sendPartialBytes()
protected void sendBinary(byte[] data) throws SocketIOException
{
if (LOGGER.isLoggable(Level.FINE))
LOGGER.log(Level.FINE, "Session[" + getSession().getSessionId() + "]: send binary");
try
{
remote_endpoint.getBasicRemote().sendBinary(ByteBuffer.wrap(data));
}
catch (IOException e)
{
disconnectEndpoint();
throw new SocketIOException(e);
}
}
private void disconnectEndpoint()
{
try
{
remote_endpoint.close();
}
catch (IOException ex)
{
// ignore
}
}
/**
* @link https://tools.ietf.org/html/rfc6455#section-11.7
*/
private DisconnectReason fromCloseCode(int code)
{
switch (code) {
case 1000:
return DisconnectReason.CLOSED; // Normal Closure
case 1001:
return DisconnectReason.CLIENT_GONE; // Going Away
default:
return DisconnectReason.ERROR;
}
}
/**
* @param session websocket session
* @return session id extracted from handshake request's parameter
*/
private String getSessionId(javax.websocket.Session session)
{
HandshakeRequest handshake = (HandshakeRequest)
session.getUserProperties().get(HandshakeRequest.class.getName());
if (handshake == null) {
return null;
}
List values = handshake.getParameterMap().get(EngineIOProtocol.SESSION_ID);
if (values == null || values.isEmpty()) {
return null;
}
return values.get(0);
}
private HttpSession getHttpSession(javax.websocket.Session session)
{
HandshakeRequest handshake = (HandshakeRequest)
session.getUserProperties().get(HandshakeRequest.class.getName());
if (handshake == null)
{
return null;
}
if (!(handshake.getHttpSession() instanceof HttpSession))
{
return null;
}
return (HttpSession) handshake.getHttpSession();
}
/**
* Initializes socket.io session
* @param session
* @throws Exception
*/
private void setupSession(javax.websocket.Session session) throws Exception
{
String sessionId = getSessionId(session);
com.codeminders.socketio.server.Session sess = null;
if (sessionId != null) {
sess = SocketIOManager.getInstance().getSession(sessionId);
}
if (sess == null) {
HttpSession httpSession = getHttpSession(session);
sess = SocketIOManager.getInstance().createSession(httpSession);
}
setSession(sess);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy