From 27c860bd8d7cabad014c038644f2117348414f62 Mon Sep 17 00:00:00 2001 From: Alexander Sova Date: Tue, 15 Dec 2015 17:28:49 -0800 Subject: [PATCH 01/85] Jetty 9.x websockets support Socket.IO EVENT packet ("emit" call) support --- core/pom.xml | 14 +++- .../socketio/server/DefaultSession.java | 33 ++++++++ .../glines/socketio/server/SocketIOFrame.java | 2 + .../socketio/server/SocketIOInbound.java | 8 ++ .../socketio/server/SocketIOOutbound.java | 11 +++ .../socketio/server/SocketIOSession.java | 1 + extension/gwt/pom.xml | 2 +- extension/jetty/pom.xml | 12 ++- .../JettyContinuationTransportHandler.java | 22 +++++- .../jetty/JettyWebSocketTransport.java | 58 +++++++------- .../jetty/JettyWebSocketTransportHandler.java | 79 +++++++++++++++---- extension/jre/pom.xml | 2 +- extension/pom.xml | 4 +- pom.xml | 40 ++++++---- samples/broadcast/pom.xml | 2 +- .../broadcast/BroadcastSocketServlet.java | 5 ++ .../src/test/java/BroadcastServer.java | 4 +- samples/chat-gwt/pom.xml | 2 +- .../sample/gwtchat/GWTChatSocketServlet.java | 5 ++ .../chat-gwt/src/test/java/GWTChatServer.java | 4 +- samples/chat/pom.xml | 2 +- .../sample/chat/ChatSocketServlet.java | 5 ++ samples/chat/src/test/java/ChatServer.java | 8 +- samples/echo/pom.xml | 2 +- .../sample/echo/EchoSocketServlet.java | 5 ++ samples/echo/src/test/java/EchoServer.java | 4 +- samples/eventbus/pom.xml | 2 +- .../sample/eventbus/EventBusServlet.java | 5 ++ samples/pom.xml | 2 +- 29 files changed, 256 insertions(+), 89 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 1ed748a..7b87ecb 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -3,11 +3,23 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.7 + 1.7 + + + + com.glines.socketio socketio - 0.2.1 + 0.2.2 .. diff --git a/core/src/main/java/com/glines/socketio/server/DefaultSession.java b/core/src/main/java/com/glines/socketio/server/DefaultSession.java index 9bd639f..67b0c2f 100644 --- a/core/src/main/java/com/glines/socketio/server/DefaultSession.java +++ b/core/src/main/java/com/glines/socketio/server/DefaultSession.java @@ -27,6 +27,7 @@ import com.glines.socketio.common.ConnectionState; import com.glines.socketio.common.DisconnectReason; import com.glines.socketio.common.SocketIOException; +import com.glines.socketio.util.JSON; import java.util.Map; import java.util.concurrent.*; @@ -199,6 +200,7 @@ public void onMessage(SocketIOFrame message) { onPing(message.getData()); case HEARTBEAT: // Ignore this message type as they are only intended to be from server to client. + //TODO: socket.io client echoes heartbeat message back startHeartbeatTimer(); break; case CLOSE: @@ -212,6 +214,23 @@ public void onMessage(SocketIOFrame message) { LOGGER.log(Level.FINE, "Session[" + sessionId + "]: onMessage: " + message.getData()); onMessage(message.getData()); break; + case EVENT: + if (LOGGER.isLoggable(Level.FINE)) + LOGGER.log(Level.FINE, "Session[" + sessionId + "]: onEvent: " + message.getData()); + try { + Map json = (Map)JSON.parse(message.getData()); + String name = json.get("name").toString(); + Object args = json.get("args"); + if(args.getClass().isArray()) { + for(Object o: (Object[])args) + onEvent(name, o.toString()); + } else + onEvent(name, JSON.toString(args)); + + } catch(ClassCastException | NullPointerException e) { + LOGGER.log(Level.WARNING, "Invalid payload format: ", e); + } + break; default: // Ignore unknown message types break; @@ -229,6 +248,7 @@ public void onPing(String data) { } } + //TODO: this is never called. need to inverstigate @Override public void onPong(String data) { clearTimeoutTimer(); @@ -316,6 +336,19 @@ public void onMessage(String message) { } } + @Override + public void onEvent(String name, String args) { + if (inbound != null) { + try { + inbound.onEvent(name, args); + } catch (Throwable e) { + if (LOGGER.isLoggable(Level.WARNING)) + LOGGER.log(Level.WARNING, "Session[" + sessionId + "]: Exception thrown by SocketIOInbound.onEvent()", e); + } + } + } + + @Override public void onDisconnect(DisconnectReason reason) { if (LOGGER.isLoggable(Level.FINE)) diff --git a/core/src/main/java/com/glines/socketio/server/SocketIOFrame.java b/core/src/main/java/com/glines/socketio/server/SocketIOFrame.java index 7f5bd2e..061b1b5 100644 --- a/core/src/main/java/com/glines/socketio/server/SocketIOFrame.java +++ b/core/src/main/java/com/glines/socketio/server/SocketIOFrame.java @@ -79,6 +79,8 @@ public static FrameType fromInt(int val) { } } + //TODO: Why we need these if we have FrameType enum? need to refactor + //TODO: for now FrameType.EVENT will always be JSON_MESSAGE_TYPE public static final int TEXT_MESSAGE_TYPE = 0; public static final int JSON_MESSAGE_TYPE = 1; diff --git a/core/src/main/java/com/glines/socketio/server/SocketIOInbound.java b/core/src/main/java/com/glines/socketio/server/SocketIOInbound.java index 62275ea..ff0e455 100644 --- a/core/src/main/java/com/glines/socketio/server/SocketIOInbound.java +++ b/core/src/main/java/com/glines/socketio/server/SocketIOInbound.java @@ -52,4 +52,12 @@ public interface SocketIOInbound { * @param message */ void onMessage(int messageType, String message); + + /** + * Called one per arriving event. + * + * @param name event name + * @param args event payload + */ + void onEvent(String name, String args); } diff --git a/core/src/main/java/com/glines/socketio/server/SocketIOOutbound.java b/core/src/main/java/com/glines/socketio/server/SocketIOOutbound.java index 6ca17bc..d9204f5 100644 --- a/core/src/main/java/com/glines/socketio/server/SocketIOOutbound.java +++ b/core/src/main/java/com/glines/socketio/server/SocketIOOutbound.java @@ -65,4 +65,15 @@ public interface SocketIOOutbound { * @throws SocketIOException */ void sendMessage(int messageType, String message) throws SocketIOException; + + /** + * Send a named event. + * + * @param name + * @param args + * @throws IllegalStateException if the socket is not CONNECTED. + * @throws SocketIOException + */ + void emitEvent(String name, String args) throws SocketIOException; + } diff --git a/core/src/main/java/com/glines/socketio/server/SocketIOSession.java b/core/src/main/java/com/glines/socketio/server/SocketIOSession.java index 39aa9db..ab000f4 100644 --- a/core/src/main/java/com/glines/socketio/server/SocketIOSession.java +++ b/core/src/main/java/com/glines/socketio/server/SocketIOSession.java @@ -66,6 +66,7 @@ interface SessionTask { void onPing(String data); void onPong(String data); void onClose(String data); + void onEvent(String name, String args); /** * Schedule a task (e.g. timeout timer) diff --git a/extension/gwt/pom.xml b/extension/gwt/pom.xml index 1b69cbb..3fb3a44 100644 --- a/extension/gwt/pom.xml +++ b/extension/gwt/pom.xml @@ -7,7 +7,7 @@ com.glines.socketio.extension socketio-extension - 0.2.1 + 0.2.2 .. diff --git a/extension/jetty/pom.xml b/extension/jetty/pom.xml index a794f23..b3f7b75 100644 --- a/extension/jetty/pom.xml +++ b/extension/jetty/pom.xml @@ -7,7 +7,7 @@ com.glines.socketio.extension socketio-extension - 0.2.1 + 0.2.2 .. @@ -40,10 +40,16 @@ provided - org.eclipse.jetty - jetty-websocket + org.eclipse.jetty.websocket + websocket-api provided + + org.eclipse.jetty.websocket + websocket-server + provided + + diff --git a/extension/jetty/src/main/java/com/glines/socketio/server/transport/jetty/JettyContinuationTransportHandler.java b/extension/jetty/src/main/java/com/glines/socketio/server/transport/jetty/JettyContinuationTransportHandler.java index ae5bb9d..ab5d5e9 100644 --- a/extension/jetty/src/main/java/com/glines/socketio/server/transport/jetty/JettyContinuationTransportHandler.java +++ b/extension/jetty/src/main/java/com/glines/socketio/server/transport/jetty/JettyContinuationTransportHandler.java @@ -31,11 +31,12 @@ import com.glines.socketio.server.*; import com.glines.socketio.server.transport.DataHandler; import com.glines.socketio.util.IO; +import com.glines.socketio.util.JSON; import com.glines.socketio.util.URI; import org.eclipse.jetty.continuation.Continuation; import org.eclipse.jetty.continuation.ContinuationListener; import org.eclipse.jetty.continuation.ContinuationSupport; -import org.eclipse.jetty.server.AbstractHttpConnection; +import org.eclipse.jetty.server.HttpConnection; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -43,6 +44,7 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.HashMap; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -192,6 +194,22 @@ public void sendMessage(int messageType, String message) throws SocketIOExceptio } } + @Override + public void emitEvent(String name, String args) throws SocketIOException { + //TODO: code duplication + if (is_open && getSession().getConnectionState() == ConnectionState.CONNECTED) { + HashMap map = new HashMap(); + map.put("name", name); + map.put("args", new Object[] { args }); + sendMessage(new SocketIOFrame(SocketIOFrame.FrameType.EVENT, + SocketIOFrame.JSON_MESSAGE_TYPE, + JSON.toString(map))); + } else { + throw new SocketIOClosedException(); + } + + } + @Override public void handle(HttpServletRequest request, HttpServletResponse response, SocketIOSession session) throws IOException { if ("GET".equals(request.getMethod())) { @@ -387,7 +405,7 @@ public boolean onMessage(String message) { * This must be called within the context of an active HTTP request. */ private static ConnectionTimeoutPreventer newTimeoutPreventor() { - AbstractHttpConnection httpConnection = AbstractHttpConnection.getCurrentConnection(); + HttpConnection httpConnection = HttpConnection.getCurrentConnection(); if (httpConnection == null) { LOGGER.log(Level.FINE, "No HttpConnection boundto local thread: " + Thread.currentThread().getName()); return new ConnectionTimeoutPreventer() { diff --git a/extension/jetty/src/main/java/com/glines/socketio/server/transport/jetty/JettyWebSocketTransport.java b/extension/jetty/src/main/java/com/glines/socketio/server/transport/jetty/JettyWebSocketTransport.java index 9f89039..3e1d361 100644 --- a/extension/jetty/src/main/java/com/glines/socketio/server/transport/jetty/JettyWebSocketTransport.java +++ b/extension/jetty/src/main/java/com/glines/socketio/server/transport/jetty/JettyWebSocketTransport.java @@ -32,36 +32,28 @@ import javax.servlet.http.HttpServletResponse; import com.glines.socketio.server.*; -import org.eclipse.jetty.websocket.WebSocket; -import org.eclipse.jetty.websocket.WebSocketFactory; -import org.eclipse.jetty.websocket.WebSocketFactory.Acceptor; + +import org.eclipse.jetty.websocket.server.WebSocketServerFactory; +import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest; +import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse; +import org.eclipse.jetty.websocket.servlet.WebSocketCreator; public final class JettyWebSocketTransport extends AbstractTransport { private static final Logger LOGGER = Logger.getLogger(JettyWebSocketTransport.class.getName()); - private final WebSocketFactory wsFactory = new WebSocketFactory(new Acceptor() { - @Override - public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean checkOrigin(HttpServletRequest arg0, String arg1) { - throw new UnsupportedOperationException(); - } - }); + private final WebSocketServerFactory wsFactory = new WebSocketServerFactory(); @Override public void init() throws TransportInitializationException { - wsFactory.setMaxTextMessageSize(getConfig().getInt(SocketIOServlet.MAX_TEXT_MESSAGE_SIZE, 32000)); - wsFactory.setBufferSize(getConfig().getBufferSize()); - wsFactory.setMaxIdleTime(getConfig().getMaxIdle()); + wsFactory.getPolicy().setMaxTextMessageSize(getConfig().getInt(SocketIOServlet.MAX_TEXT_MESSAGE_SIZE, 32000)); + wsFactory.getPolicy().setInputBufferSize(getConfig().getBufferSize()); + wsFactory.getPolicy().setIdleTimeout(getConfig().getMaxIdle()); if (LOGGER.isLoggable(Level.FINE)) LOGGER.fine(getType() + " configuration:\n" + - " - bufferSize=" + wsFactory.getBufferSize() + "\n" + - " - maxIdle=" + wsFactory.getMaxIdleTime()); + " - bufferSize=" + wsFactory.getPolicy().getInputBufferSize() + "\n" + + " - maxIdle=" + wsFactory.getPolicy().getIdleTimeout()); } @Override @@ -89,18 +81,18 @@ public void handle(HttpServletRequest request, } if ("GET".equals(request.getMethod()) && sessionId != null - && (transport.equals("websocket") || transport.equals("flashsocket"))) { + && ("websocket".equals(transport) || "flashsocket".equals(transport))) { boolean hixie = request.getHeader("Sec-WebSocket-Key1") != null; String protocol = request.getHeader(hixie ? "Sec-WebSocket-Protocol" : "WebSocket-Protocol"); if (protocol == null) protocol = request.getHeader("Sec-WebSocket-Protocol"); - String host = request.getHeader("Host"); - String origin = request.getHeader("Origin"); - if (origin == null) { - origin = host; - } +// String host = request.getHeader("Host"); +// String origin = request.getHeader("Origin"); +// if (origin == null) { +// origin = host; +// } SocketIOInbound inbound = inboundFactory.getInbound(request); if (inbound == null) { @@ -110,21 +102,27 @@ public void handle(HttpServletRequest request, response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); } else { SocketIOSession session; - TransportHandler handler; + final TransportHandler handler; session = sessionFactory.getSession(sessionId); if (session == null) { session = sessionFactory.createSession(inbound, sessionId); - handler = newHandler(WebSocket.class, session); + handler = newHandler(JettyWebSocketTransportHandler.class, session); handler.init(getConfig()); //handler.onConnect(); } else { handler = session.getTransportHandler(); } - wsFactory.upgrade(request, response, WebSocket.class.cast(handler), protocol); - handler.sendMessage(new SocketIOFrame(SocketIOFrame.FrameType.CONNECT, SocketIOFrame.TEXT_MESSAGE_TYPE, "")); - handler.onConnect(); + wsFactory.acceptWebSocket(new WebSocketCreator() { + @Override + public Object createWebSocket(ServletUpgradeRequest servletUpgradeRequest, ServletUpgradeResponse servletUpgradeResponse) { + return handler; + } + }, request, response); + +// handler.sendMessage(new SocketIOFrame(SocketIOFrame.FrameType.CONNECT, SocketIOFrame.TEXT_MESSAGE_TYPE, "")); +// handler.onConnect(); } } else { response.sendError(HttpServletResponse.SC_BAD_REQUEST, this + " transport error: Invalid request"); diff --git a/extension/jetty/src/main/java/com/glines/socketio/server/transport/jetty/JettyWebSocketTransportHandler.java b/extension/jetty/src/main/java/com/glines/socketio/server/transport/jetty/JettyWebSocketTransportHandler.java index e5577e8..b827508 100644 --- a/extension/jetty/src/main/java/com/glines/socketio/server/transport/jetty/JettyWebSocketTransportHandler.java +++ b/extension/jetty/src/main/java/com/glines/socketio/server/transport/jetty/JettyWebSocketTransportHandler.java @@ -29,13 +29,21 @@ import com.glines.socketio.common.DisconnectReason; import com.glines.socketio.common.SocketIOException; import com.glines.socketio.server.*; -import org.eclipse.jetty.websocket.WebSocket; + +import com.glines.socketio.util.JSON; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.WebSocket; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.util.HashMap; import java.util.List; +import java.util.Observable; import java.util.logging.Level; import java.util.logging.Logger; @@ -43,14 +51,15 @@ * @author Mathieu Carbou */ @Handle({TransportType.WEB_SOCKET, TransportType.FLASH_SOCKET}) -public final class JettyWebSocketTransportHandler extends AbstractTransportHandler implements WebSocket.OnTextMessage, WebSocket.OnBinaryMessage { +@WebSocket +public final class JettyWebSocketTransportHandler extends AbstractTransportHandler { private static final long DEFAULT_HEARTBEAT_DELAY = SocketIOConfig.DEFAULT_MAX_IDLE / 2; private static final long DEFAULT_HEARTBEAT_TIMEOUT = 10 * 1000; private static final Logger LOGGER = Logger.getLogger(JettyWebSocketTransportHandler.class.getName()); - private Connection outbound; + private Session outbound; @Override protected void init() { @@ -62,18 +71,25 @@ protected void init() { " - heartbeatTimeout=" + getSession().getTimeout()); } - @Override - public void onOpen(Connection connection) { - this.outbound = connection; + @OnWebSocketConnect + public void onWebSocketConnect(Session session) { + outbound = session; + try { + sendMessage(new SocketIOFrame(SocketIOFrame.FrameType.CONNECT, SocketIOFrame.TEXT_MESSAGE_TYPE, "")); + } catch (SocketIOException e) { + LOGGER.log(Level.SEVERE, "Cannot connect", e); + } + onConnect(); + } - @Override - public void onClose(int closeCode, String message) { + @OnWebSocketClose + public void onWebSocketClose(int closeCode, String message) { getSession().onShutdown(); } - @Override - public void onMessage(String message) { + @OnWebSocketMessage + public void onWebSocketText(String message) { getSession().startHeartbeatTimer(); List messages = SocketIOFrame.parse(message); for (SocketIOFrame msg : messages) { @@ -81,10 +97,10 @@ public void onMessage(String message) { } } - @Override - public void onMessage(byte[] data, int offset, int length) { + @OnWebSocketMessage + public void onWebSocketBinary(byte[] data, int offset, int length) { try { - onMessage(new String(data, offset, length, "UTF-8")); + onWebSocketText(new String(data, offset, length, "UTF-8")); } catch (UnsupportedEncodingException e) { // Do nothing for now. } @@ -93,7 +109,11 @@ public void onMessage(byte[] data, int offset, int length) { @Override public void disconnect() { getSession().onDisconnect(DisconnectReason.DISCONNECT); - outbound.disconnect(); + try { + outbound.disconnect(); + } catch (IOException e) { + //TODO: report? + } } @Override @@ -112,9 +132,13 @@ public void sendMessage(SocketIOFrame frame) throws SocketIOException { if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Session[" + getSession().getSessionId() + "]: sendMessage: [" + frame.getFrameType() + "]: " + frame.getData()); try { - outbound.sendMessage(frame.encode()); + outbound.getRemote().sendString(frame.encode()); } catch (IOException e) { - outbound.disconnect(); + try { + outbound.disconnect(); + } catch(IOException ex) { + //TODO: report? + } throw new SocketIOException(e); } } else { @@ -141,6 +165,23 @@ public void sendMessage(int messageType, String message) } } + @Override + public void emitEvent(String name, String args) + throws SocketIOException { + + if (outbound.isOpen() && getSession().getConnectionState() == ConnectionState.CONNECTED) { + HashMap map = new HashMap(); + map.put("name", name); + map.put("args", new Object[] { args }); + sendMessage(new SocketIOFrame(SocketIOFrame.FrameType.EVENT, + SocketIOFrame.JSON_MESSAGE_TYPE, + JSON.toString(map))); + } else { + throw new SocketIOClosedException(); + } + + } + @Override public void handle(HttpServletRequest request, HttpServletResponse response, SocketIOSession session) throws IOException { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unexpected request on upgraded WebSocket connection"); @@ -149,7 +190,11 @@ public void handle(HttpServletRequest request, HttpServletResponse response, Soc @Override public void abort() { if (outbound != null) { - outbound.disconnect(); + try { + outbound.disconnect(); + } catch (IOException e) { + //TODO: report? + } outbound = null; } getSession().onShutdown(); diff --git a/extension/jre/pom.xml b/extension/jre/pom.xml index 262107d..1cc7dd4 100644 --- a/extension/jre/pom.xml +++ b/extension/jre/pom.xml @@ -7,7 +7,7 @@ com.glines.socketio.extension socketio-extension - 0.2.1 + 0.2.2 .. diff --git a/extension/pom.xml b/extension/pom.xml index 73b2ec9..8a8d2f0 100644 --- a/extension/pom.xml +++ b/extension/pom.xml @@ -7,7 +7,7 @@ com.glines.socketio socketio - 0.2.1 + 0.2.2 .. @@ -20,7 +20,7 @@ jetty - jre + gwt diff --git a/pom.xml b/pom.xml index 6dc94bb..ca11061 100755 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.glines.socketio socketio - 0.2.1 + 0.2.2 pom Socket.IO Java @@ -16,6 +16,7 @@ 1.6 UTF-8 + 9.2.14.v20151106 @@ -342,28 +343,34 @@ org.eclipse.jetty jetty-client - 8.1.3.v20120416 + ${jetty-version} org.eclipse.jetty jetty-continuation - 8.1.3.v20120416 + ${jetty-version} org.eclipse.jetty - jetty-websocket - 8.1.3.v20120416 + jetty-server + ${jetty-version} - org.eclipse.jetty - jetty-server - 8.1.3.v20120416 + org.eclipse.jetty.websocket + websocket-api + ${jetty-version} + + org.eclipse.jetty.websocket + websocket-server + ${jetty-version} + + org.eclipse.jetty.aggregate jetty-all - 8.1.3.v20120416 + ${jetty-version} org.testatoo.container @@ -478,13 +485,14 @@ - - com.mycila.maven-license-plugin - maven-license-plugin - -
https://github.com/tadglines/Socket.IO-Java/raw/master/licenses/MIT
-
-
+ + + + + + + +
diff --git a/samples/broadcast/pom.xml b/samples/broadcast/pom.xml index 0d5af2f..95325c2 100644 --- a/samples/broadcast/pom.xml +++ b/samples/broadcast/pom.xml @@ -7,7 +7,7 @@ com.glines.socketio.sample socketio-sample - 0.2.1 + 0.2.2 .. diff --git a/samples/broadcast/src/main/java/com/glines/socketio/sample/broadcast/BroadcastSocketServlet.java b/samples/broadcast/src/main/java/com/glines/socketio/sample/broadcast/BroadcastSocketServlet.java index c14aab6..55ea4b2 100644 --- a/samples/broadcast/src/main/java/com/glines/socketio/sample/broadcast/BroadcastSocketServlet.java +++ b/samples/broadcast/src/main/java/com/glines/socketio/sample/broadcast/BroadcastSocketServlet.java @@ -58,6 +58,11 @@ public void onMessage(int messageType, String message) { broadcast(messageType, message); } + @Override + public void onEvent(String name, String args) { + + } + private void broadcast(int messageType, String message) { for (BroadcastConnection c : connections) { if (c != this) { diff --git a/samples/broadcast/src/test/java/BroadcastServer.java b/samples/broadcast/src/test/java/BroadcastServer.java index 5c7ffcf..9a68c2c 100644 --- a/samples/broadcast/src/test/java/BroadcastServer.java +++ b/samples/broadcast/src/test/java/BroadcastServer.java @@ -25,7 +25,7 @@ import com.glines.socketio.sample.broadcast.BroadcastSocketServlet; import com.glines.socketio.server.SocketIOConfig; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.nio.SelectChannelConnector; +import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; @@ -49,7 +49,7 @@ public static void main(String[] args) throws Exception { } Server server = new Server(); - SelectChannelConnector connector = new SelectChannelConnector(); + ServerConnector connector = new ServerConnector(server); connector.setHost(host); connector.setPort(port); diff --git a/samples/chat-gwt/pom.xml b/samples/chat-gwt/pom.xml index 587a6ab..49d8daf 100644 --- a/samples/chat-gwt/pom.xml +++ b/samples/chat-gwt/pom.xml @@ -7,7 +7,7 @@ com.glines.socketio.sample socketio-sample - 0.2.1 + 0.2.2 .. diff --git a/samples/chat-gwt/src/main/java/com/glines/socketio/sample/gwtchat/GWTChatSocketServlet.java b/samples/chat-gwt/src/main/java/com/glines/socketio/sample/gwtchat/GWTChatSocketServlet.java index ed5cf34..06d8160 100644 --- a/samples/chat-gwt/src/main/java/com/glines/socketio/sample/gwtchat/GWTChatSocketServlet.java +++ b/samples/chat-gwt/src/main/java/com/glines/socketio/sample/gwtchat/GWTChatSocketServlet.java @@ -117,6 +117,11 @@ public void onMessage(int messageType, String message) { } } + @Override + public void onEvent(String name, String args) { + + } + private void broadcast(int messageType, String message) { for (GWTChatConnection c : connections) { if (c != this) { diff --git a/samples/chat-gwt/src/test/java/GWTChatServer.java b/samples/chat-gwt/src/test/java/GWTChatServer.java index b9c23c8..468f7ae 100644 --- a/samples/chat-gwt/src/test/java/GWTChatServer.java +++ b/samples/chat-gwt/src/test/java/GWTChatServer.java @@ -26,7 +26,7 @@ import com.glines.socketio.sample.gwtchat.GWTChatSocketServlet; import com.glines.socketio.server.transport.FlashSocketTransport; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.nio.SelectChannelConnector; +import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; @@ -85,7 +85,7 @@ public static void main(String[] args) throws Exception { } Server server = new Server(); - SelectChannelConnector connector = new SelectChannelConnector(); + ServerConnector connector = new ServerConnector(server); connector.setHost(host); connector.setPort(port); diff --git a/samples/chat/pom.xml b/samples/chat/pom.xml index e4c26d9..3927d2e 100644 --- a/samples/chat/pom.xml +++ b/samples/chat/pom.xml @@ -7,7 +7,7 @@ com.glines.socketio.sample socketio-sample - 0.2.1 + 0.2.2 .. diff --git a/samples/chat/src/main/java/com/glines/socketio/sample/chat/ChatSocketServlet.java b/samples/chat/src/main/java/com/glines/socketio/sample/chat/ChatSocketServlet.java index b289f1c..6a2f4a2 100644 --- a/samples/chat/src/main/java/com/glines/socketio/sample/chat/ChatSocketServlet.java +++ b/samples/chat/src/main/java/com/glines/socketio/sample/chat/ChatSocketServlet.java @@ -140,6 +140,11 @@ public void onMessage(int messageType, String message) { } } + @Override + public void onEvent(String name, String args) { + + } + private void broadcast(int messageType, String message) { for (ChatConnection c : connections) { if (c != this) { diff --git a/samples/chat/src/test/java/ChatServer.java b/samples/chat/src/test/java/ChatServer.java index 94ea1e2..dfb92b1 100644 --- a/samples/chat/src/test/java/ChatServer.java +++ b/samples/chat/src/test/java/ChatServer.java @@ -34,7 +34,7 @@ import com.glines.socketio.sample.chat.ChatSocketServlet; import com.glines.socketio.server.SocketIOConfig; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.nio.SelectChannelConnector; +import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; @@ -49,7 +49,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) path = path.substring(req.getContextPath().length()); if ("/json.js".equals(path)) { resp.setContentType("text/javascript"); - InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/glines/socketio/examples/chat/json.js"); + InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/glines/socketio/sample/chat/json.js"); OutputStream os = resp.getOutputStream(); byte[] data = new byte[8192]; int nread = 0; @@ -61,7 +61,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) } } else if ("/chat.html".equals(path)) { resp.setContentType("text/html"); - InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/glines/socketio/examples/chat/chat.html"); + InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/glines/socketio/sample/chat/chat.html"); OutputStream os = resp.getOutputStream(); byte[] data = new byte[8192]; int nread = 0; @@ -94,7 +94,7 @@ public static void main(String[] args) throws Exception { } Server server = new Server(); - SelectChannelConnector connector = new SelectChannelConnector(); + ServerConnector connector = new ServerConnector(server); connector.setHost(host); connector.setPort(port); diff --git a/samples/echo/pom.xml b/samples/echo/pom.xml index fa571b6..32136c7 100644 --- a/samples/echo/pom.xml +++ b/samples/echo/pom.xml @@ -7,7 +7,7 @@ com.glines.socketio.sample socketio-sample - 0.2.1 + 0.2.2 .. diff --git a/samples/echo/src/main/java/com/glines/socketio/sample/echo/EchoSocketServlet.java b/samples/echo/src/main/java/com/glines/socketio/sample/echo/EchoSocketServlet.java index 54c2dea..8ba8876 100644 --- a/samples/echo/src/main/java/com/glines/socketio/sample/echo/EchoSocketServlet.java +++ b/samples/echo/src/main/java/com/glines/socketio/sample/echo/EchoSocketServlet.java @@ -57,6 +57,11 @@ public void onMessage(int messageType, String message) { outbound.disconnect(); } } + + @Override + public void onEvent(String name, String args) { + + } } @Override diff --git a/samples/echo/src/test/java/EchoServer.java b/samples/echo/src/test/java/EchoServer.java index 126849c..d361d62 100644 --- a/samples/echo/src/test/java/EchoServer.java +++ b/samples/echo/src/test/java/EchoServer.java @@ -25,7 +25,7 @@ import com.glines.socketio.sample.echo.EchoSocketServlet; import com.glines.socketio.server.SocketIOConfig; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.nio.SelectChannelConnector; +import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; @@ -49,7 +49,7 @@ public static void main(String[] args) throws Exception { } Server server = new Server(); - SelectChannelConnector connector = new SelectChannelConnector(); + ServerConnector connector = new ServerConnector(server); connector.setHost(host); connector.setPort(port); diff --git a/samples/eventbus/pom.xml b/samples/eventbus/pom.xml index 3941e01..e12649c 100644 --- a/samples/eventbus/pom.xml +++ b/samples/eventbus/pom.xml @@ -7,7 +7,7 @@ com.glines.socketio.sample socketio-sample - 0.2.1 + 0.2.2 .. diff --git a/samples/eventbus/src/main/java/com/glines/socketio/sample/eventbus/EventBusServlet.java b/samples/eventbus/src/main/java/com/glines/socketio/sample/eventbus/EventBusServlet.java index 5a17759..470f912 100644 --- a/samples/eventbus/src/main/java/com/glines/socketio/sample/eventbus/EventBusServlet.java +++ b/samples/eventbus/src/main/java/com/glines/socketio/sample/eventbus/EventBusServlet.java @@ -169,6 +169,11 @@ public void onMessage(int messageType, String message) { } } + @Override + public void onEvent(String name, String args) { + + } + void close() { if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, this + " closing."); diff --git a/samples/pom.xml b/samples/pom.xml index e4b3a4b..684a5d2 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -7,7 +7,7 @@ com.glines.socketio socketio - 0.2.1 + 0.2.2 .. From 5676d66ea1f636986a44434e4f92825f42f21925 Mon Sep 17 00:00:00 2001 From: Alexander Sova Date: Thu, 17 Dec 2015 01:46:48 -0800 Subject: [PATCH 02/85] making default heartbeat interval shorter than heartbeat timeout --- .../java/com/glines/socketio/server/SocketIOConfig.java | 3 +++ .../transport/jetty/JettyWebSocketTransportHandler.java | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/glines/socketio/server/SocketIOConfig.java b/core/src/main/java/com/glines/socketio/server/SocketIOConfig.java index b4e882b..d3f6d21 100644 --- a/core/src/main/java/com/glines/socketio/server/SocketIOConfig.java +++ b/core/src/main/java/com/glines/socketio/server/SocketIOConfig.java @@ -41,6 +41,9 @@ public interface SocketIOConfig { int DEFAULT_BUFFER_SIZE = 8192; int DEFAULT_MAX_IDLE = 300 * 1000; + int DEFAULT_HEARTBEAT_INTERVAL = 25 * 1000; // 25s + int DEFAULT_HEARTBEAT_TIMEOUT = 60 * 1000; // 60s + long getHeartbeatDelay(long def); long getHeartbeatTimeout(long def); long getTimeout(long def); diff --git a/extension/jetty/src/main/java/com/glines/socketio/server/transport/jetty/JettyWebSocketTransportHandler.java b/extension/jetty/src/main/java/com/glines/socketio/server/transport/jetty/JettyWebSocketTransportHandler.java index b827508..e09b55b 100644 --- a/extension/jetty/src/main/java/com/glines/socketio/server/transport/jetty/JettyWebSocketTransportHandler.java +++ b/extension/jetty/src/main/java/com/glines/socketio/server/transport/jetty/JettyWebSocketTransportHandler.java @@ -54,8 +54,8 @@ @WebSocket public final class JettyWebSocketTransportHandler extends AbstractTransportHandler { - private static final long DEFAULT_HEARTBEAT_DELAY = SocketIOConfig.DEFAULT_MAX_IDLE / 2; - private static final long DEFAULT_HEARTBEAT_TIMEOUT = 10 * 1000; +// private static final long DEFAULT_HEARTBEAT_DELAY = SocketIOConfig.DEFAULT_MAX_IDLE / 2; +// private static final long DEFAULT_HEARTBEAT_TIMEOUT = 10 * 1000; private static final Logger LOGGER = Logger.getLogger(JettyWebSocketTransportHandler.class.getName()); @@ -63,8 +63,8 @@ public final class JettyWebSocketTransportHandler extends AbstractTransportHandl @Override protected void init() { - getSession().setHeartbeat(getConfig().getHeartbeatDelay(DEFAULT_HEARTBEAT_DELAY)); - getSession().setTimeout(getConfig().getHeartbeatTimeout(DEFAULT_HEARTBEAT_TIMEOUT)); + getSession().setHeartbeat(getConfig().getHeartbeatDelay(SocketIOConfig.DEFAULT_HEARTBEAT_INTERVAL)); + getSession().setTimeout(getConfig().getHeartbeatTimeout(SocketIOConfig.DEFAULT_HEARTBEAT_TIMEOUT)); if (LOGGER.isLoggable(Level.FINE)) LOGGER.fine(getConfig().getNamespace() + " transport handler configuration:\n" + " - heartbeatDelay=" + getSession().getHeartbeat() + "\n" + From 20fc8ed4061bcda351560ca9e1610b1cc7a23b5f Mon Sep 17 00:00:00 2001 From: Alexander Sova Date: Sat, 26 Dec 2015 01:41:29 -0800 Subject: [PATCH 03/85] Major refactoring removed of unused transports (htmlfile, xhr-multipart), renamed bunch of classes and interfaces removed all extensions except Jetty got rid of dynamic transport classes discovery based on reflection and config files it's only compiles now, not tested at all --- .gitignore | 2 +- TODO | 7 - core/pom.xml | 4 +- .../socketio/common/ConnectionState.java | 2 +- .../socketio/common/DisconnectReason.java | 2 +- .../socketio/common/SocketIOException.java | 6 +- .../socketio/server/AbstractTransport.java} | 39 +- .../server/AbstractTransportConnection.java} | 5 +- .../socketio/server/DefaultSession.java | 30 +- .../server/ServletBasedSocketIOConfig.java | 40 +- .../socketio/server/SessionManager.java | 2 +- .../server/SocketIOClosedException.java | 6 +- .../socketio/server/SocketIOConfig.java | 8 +- .../socketio/server/SocketIOFrame.java | 2 +- .../socketio/server/SocketIOInbound.java | 4 +- .../socketio/server/SocketIOOutbound.java | 6 +- .../server/SocketIOProtocolException.java | 15 + .../socketio/server/SocketIOServlet.java | 120 +- .../socketio/server/SocketIOSession.java | 12 +- .../server/SocketIOSessionManager.java | 2 +- .../socketio/server/Transport.java | 15 +- .../socketio/server/TransportConnection.java} | 17 +- .../socketio/server/TransportProvider.java | 27 + .../socketio/server/TransportType.java | 3 +- .../server/UnsupportedTransportException.java | 11 + .../transport/AbstractHttpTransport.java | 87 +- .../transport/AbstractTransportProvider.java | 111 + .../transport/FlashSocketTransport.java | 44 +- .../transport/JSONPPollingTransport.java | 91 + .../server/transport/XHRPollingTransport.java | 72 + .../socketio/util/DefaultLoader.java | 2 +- .../socketio/util/IO.java | 2 +- .../socketio/util/JSON.java | 2 +- .../socketio/util/JdkOverLog4j.java | 2 +- .../socketio/util/Loader.java | 2 +- .../socketio/util/ServiceClassLoader.java | 2 +- .../socketio/util/URI.java | 2 +- .../socketio/util/Web.java | 2 +- .../glines/socketio/annotation/Handle.java | 39 - .../client/common/SocketIOConnection.java | 83 - .../common/SocketIOConnectionListener.java | 36 - .../socketio/server/AbstractTransport.java | 75 - .../AnnotationTransportHandlerProvider.java | 108 - .../server/ClasspathTransportDiscovery.java | 72 - .../server/ConnectableTransportHandler.java | 39 - .../socketio/server/TransportDiscovery.java | 32 - .../server/TransportHandlerProvider.java | 40 - .../TransportInitializationException.java | 45 - .../server/transport/DataHandler.java | 51 - .../server/transport/HTMLFileDataHandler.java | 96 - .../server/transport/HTMLFileTransport.java | 40 - .../transport/JSONPPollingDataHandler.java | 104 - .../transport/JSONPPollingTransport.java | 40 - .../transport/XHRMultipartDataHandler.java | 108 - .../transport/XHRMultipartTransport.java | 40 - .../transport/XHRPollingDataHandler.java | 89 - .../server/transport/XHRPollingTransport.java | 40 - .../com.glines.socketio.server.Transport | 5 - .../socketio/WebSocketMain.swf | Bin .../com/codeminders/socketio/socket.io.js | 4144 +++++++++++++++++ .../com/glines/socketio/socket.io.js | 3871 --------------- extension/gwt/pom.xml | 32 - .../gwt/GWTSocketIOConnectionFactory.java | 43 - .../client/gwt/GWTSocketIOConnectionImpl.java | 160 - .../com/glines/socketio/Common.gwt.xml | 30 - .../com/glines/socketio/client/Client.gwt.xml | 35 - extension/jetty/pom.xml | 6 +- .../jetty/ConnectionTimeoutPreventer.java | 2 +- ...JettyContinuationTransportConnection.java} | 239 +- .../transport/jetty/JettySocketIOServlet.java | 18 + .../jetty/JettyTransportProvider.java | 50 + .../jetty/JettyWebSocketTransport.java | 69 +- .../JettyWebSocketTransportConnection.java} | 57 +- .../transport/jetty/TransportBuffer.java | 2 +- ...ConnectionTimeoutPreventerDataHandler.java | 84 - .../com.glines.socketio.server.Transport | 1 - ...om.glines.socketio.server.TransportHandler | 2 - extension/jre/pom.xml | 31 - .../client/jre/SocketIOConnectionImpl.java | 84 - .../client/jre/SocketIOConnectionXHRBase.java | 380 -- extension/pom.xml | 8 +- pom.xml | 194 +- samples/broadcast/pom.xml | 37 - .../broadcast/BroadcastSocketServlet.java | 84 - .../src/main/resources/logging.properties | 4 - .../broadcast/src/main/webapp/WEB-INF/web.xml | 42 - .../src/test/java/BroadcastServer.java | 69 - .../src/test/java/StartSampleBroadcast.java | 38 - samples/chat-gwt/pom.xml | 84 - .../sample/gwtchat/GWTChatSocketServlet.java | 143 - .../sample/gwtchat/client/GWTChatClient.java | 183 - .../socketio/sample/gwtchat/GWTChat.gwt.xml | 34 - .../chat-gwt/src/main/webapp/WEB-INF/web.xml | 46 - samples/chat-gwt/src/main/webapp/gwtchat.html | 68 - .../chat-gwt/src/test/java/GWTChatServer.java | 109 - .../src/test/java/StartSampleChatGWT.java | 38 - samples/chat/pom.xml | 9 +- .../sample/chat/ChatSocketServlet.java | 22 +- .../com.codeminders.socketio.server.Transport | 1 + ...inders.socketio.server.TransportConnection | 2 + samples/chat/src/main/resources/log4j.xml | 2 +- .../webapp/META-INF/context.xml | 0 .../{ => resources}/webapp/WEB-INF/web.xml | 2 +- .../src/main/{ => resources}/webapp/chat.html | 7 +- .../src/main/{ => resources}/webapp/json.js | 0 samples/chat/src/test/java/ChatServer.java | 18 +- samples/echo/pom.xml | 37 - .../sample/echo/EchoSocketServlet.java | 72 - samples/echo/src/main/webapp/WEB-INF/web.xml | 42 - samples/echo/src/test/java/EchoServer.java | 69 - .../echo/src/test/java/StartSampleEcho.java | 38 - samples/eventbus/pom.xml | 55 - .../sample/eventbus/EventBusServlet.java | 278 -- samples/eventbus/src/main/resources/log4j.xml | 83 - .../eventbus/src/main/webapp/WEB-INF/web.xml | 50 - samples/eventbus/src/main/webapp/index.html | 211 - .../src/main/webapp/jquery-1.4.4.min.js | 167 - .../src/main/webapp/jquery.json-2.2.min.js | 31 - .../src/test/java/StartSampleEventBus.java | 38 - samples/pom.xml | 10 +- 120 files changed, 5068 insertions(+), 8687 deletions(-) delete mode 100644 TODO rename core/src/main/java/com/{glines => codeminders}/socketio/common/ConnectionState.java (97%) rename core/src/main/java/com/{glines => codeminders}/socketio/common/DisconnectReason.java (98%) rename core/src/main/java/com/{glines => codeminders}/socketio/common/SocketIOException.java (92%) rename core/src/main/java/com/{glines/socketio/server/transport/AbstractDataHandler.java => codeminders/socketio/server/AbstractTransport.java} (61%) rename core/src/main/java/com/{glines/socketio/server/AbstractTransportHandler.java => codeminders/socketio/server/AbstractTransportConnection.java} (93%) rename core/src/main/java/com/{glines => codeminders}/socketio/server/DefaultSession.java (94%) rename core/src/main/java/com/{glines => codeminders}/socketio/server/ServletBasedSocketIOConfig.java (74%) rename core/src/main/java/com/{glines => codeminders}/socketio/server/SessionManager.java (97%) rename core/src/main/java/com/{glines => codeminders}/socketio/server/SocketIOClosedException.java (94%) rename core/src/main/java/com/{glines => codeminders}/socketio/server/SocketIOConfig.java (88%) rename core/src/main/java/com/{glines => codeminders}/socketio/server/SocketIOFrame.java (99%) rename core/src/main/java/com/{glines => codeminders}/socketio/server/SocketIOInbound.java (95%) rename core/src/main/java/com/{glines => codeminders}/socketio/server/SocketIOOutbound.java (94%) create mode 100644 core/src/main/java/com/codeminders/socketio/server/SocketIOProtocolException.java rename core/src/main/java/com/{glines => codeminders}/socketio/server/SocketIOServlet.java (50%) rename core/src/main/java/com/{glines => codeminders}/socketio/server/SocketIOSession.java (89%) rename core/src/main/java/com/{glines => codeminders}/socketio/server/SocketIOSessionManager.java (98%) rename core/src/main/java/com/{glines => codeminders}/socketio/server/Transport.java (86%) rename core/src/main/java/com/{glines/socketio/server/TransportHandler.java => codeminders/socketio/server/TransportConnection.java} (81%) create mode 100644 core/src/main/java/com/codeminders/socketio/server/TransportProvider.java rename core/src/main/java/com/{glines => codeminders}/socketio/server/TransportType.java (96%) create mode 100644 core/src/main/java/com/codeminders/socketio/server/UnsupportedTransportException.java rename core/src/main/java/com/{glines => codeminders}/socketio/server/transport/AbstractHttpTransport.java (61%) create mode 100644 core/src/main/java/com/codeminders/socketio/server/transport/AbstractTransportProvider.java rename core/src/main/java/com/{glines => codeminders}/socketio/server/transport/FlashSocketTransport.java (91%) create mode 100644 core/src/main/java/com/codeminders/socketio/server/transport/JSONPPollingTransport.java create mode 100644 core/src/main/java/com/codeminders/socketio/server/transport/XHRPollingTransport.java rename core/src/main/java/com/{glines => codeminders}/socketio/util/DefaultLoader.java (98%) rename core/src/main/java/com/{glines => codeminders}/socketio/util/IO.java (98%) rename core/src/main/java/com/{glines => codeminders}/socketio/util/JSON.java (99%) rename core/src/main/java/com/{glines => codeminders}/socketio/util/JdkOverLog4j.java (99%) rename core/src/main/java/com/{glines => codeminders}/socketio/util/Loader.java (97%) rename core/src/main/java/com/{glines => codeminders}/socketio/util/ServiceClassLoader.java (99%) rename core/src/main/java/com/{glines => codeminders}/socketio/util/URI.java (99%) rename core/src/main/java/com/{glines => codeminders}/socketio/util/Web.java (98%) delete mode 100644 core/src/main/java/com/glines/socketio/annotation/Handle.java delete mode 100644 core/src/main/java/com/glines/socketio/client/common/SocketIOConnection.java delete mode 100644 core/src/main/java/com/glines/socketio/client/common/SocketIOConnectionListener.java delete mode 100644 core/src/main/java/com/glines/socketio/server/AbstractTransport.java delete mode 100644 core/src/main/java/com/glines/socketio/server/AnnotationTransportHandlerProvider.java delete mode 100644 core/src/main/java/com/glines/socketio/server/ClasspathTransportDiscovery.java delete mode 100644 core/src/main/java/com/glines/socketio/server/ConnectableTransportHandler.java delete mode 100644 core/src/main/java/com/glines/socketio/server/TransportDiscovery.java delete mode 100644 core/src/main/java/com/glines/socketio/server/TransportHandlerProvider.java delete mode 100644 core/src/main/java/com/glines/socketio/server/TransportInitializationException.java delete mode 100644 core/src/main/java/com/glines/socketio/server/transport/DataHandler.java delete mode 100644 core/src/main/java/com/glines/socketio/server/transport/HTMLFileDataHandler.java delete mode 100644 core/src/main/java/com/glines/socketio/server/transport/HTMLFileTransport.java delete mode 100644 core/src/main/java/com/glines/socketio/server/transport/JSONPPollingDataHandler.java delete mode 100644 core/src/main/java/com/glines/socketio/server/transport/JSONPPollingTransport.java delete mode 100644 core/src/main/java/com/glines/socketio/server/transport/XHRMultipartDataHandler.java delete mode 100644 core/src/main/java/com/glines/socketio/server/transport/XHRMultipartTransport.java delete mode 100644 core/src/main/java/com/glines/socketio/server/transport/XHRPollingDataHandler.java delete mode 100644 core/src/main/java/com/glines/socketio/server/transport/XHRPollingTransport.java delete mode 100644 core/src/main/resources/META-INF/services/com.glines.socketio.server.Transport rename core/src/main/resources/com/{glines => codeminders}/socketio/WebSocketMain.swf (100%) create mode 100644 core/src/main/resources/com/codeminders/socketio/socket.io.js delete mode 100644 core/src/main/resources/com/glines/socketio/socket.io.js delete mode 100644 extension/gwt/pom.xml delete mode 100644 extension/gwt/src/main/java/com/glines/socketio/client/gwt/GWTSocketIOConnectionFactory.java delete mode 100644 extension/gwt/src/main/java/com/glines/socketio/client/gwt/GWTSocketIOConnectionImpl.java delete mode 100644 extension/gwt/src/main/resources/com/glines/socketio/Common.gwt.xml delete mode 100644 extension/gwt/src/main/resources/com/glines/socketio/client/Client.gwt.xml rename extension/jetty/src/main/java/com/{glines => codeminders}/socketio/server/transport/jetty/ConnectionTimeoutPreventer.java (95%) rename extension/jetty/src/main/java/com/{glines/socketio/server/transport/jetty/JettyContinuationTransportHandler.java => codeminders/socketio/server/transport/jetty/JettyContinuationTransportConnection.java} (68%) create mode 100644 extension/jetty/src/main/java/com/codeminders/socketio/server/transport/jetty/JettySocketIOServlet.java create mode 100644 extension/jetty/src/main/java/com/codeminders/socketio/server/transport/jetty/JettyTransportProvider.java rename extension/jetty/src/main/java/com/{glines => codeminders}/socketio/server/transport/jetty/JettyWebSocketTransport.java (76%) rename extension/jetty/src/main/java/com/{glines/socketio/server/transport/jetty/JettyWebSocketTransportHandler.java => codeminders/socketio/server/transport/jetty/JettyWebSocketTransportConnection.java} (86%) rename extension/jetty/src/main/java/com/{glines => codeminders}/socketio/server/transport/jetty/TransportBuffer.java (98%) delete mode 100644 extension/jetty/src/main/java/com/glines/socketio/server/transport/jetty/ConnectionTimeoutPreventerDataHandler.java delete mode 100644 extension/jetty/src/main/resources/META-INF/services/com.glines.socketio.server.Transport delete mode 100644 extension/jetty/src/main/resources/META-INF/services/com.glines.socketio.server.TransportHandler delete mode 100644 extension/jre/pom.xml delete mode 100644 extension/jre/src/main/java/com/glines/socketio/client/jre/SocketIOConnectionImpl.java delete mode 100644 extension/jre/src/main/java/com/glines/socketio/client/jre/SocketIOConnectionXHRBase.java delete mode 100644 samples/broadcast/pom.xml delete mode 100644 samples/broadcast/src/main/java/com/glines/socketio/sample/broadcast/BroadcastSocketServlet.java delete mode 100644 samples/broadcast/src/main/resources/logging.properties delete mode 100644 samples/broadcast/src/main/webapp/WEB-INF/web.xml delete mode 100644 samples/broadcast/src/test/java/BroadcastServer.java delete mode 100644 samples/broadcast/src/test/java/StartSampleBroadcast.java delete mode 100644 samples/chat-gwt/pom.xml delete mode 100644 samples/chat-gwt/src/main/java/com/glines/socketio/sample/gwtchat/GWTChatSocketServlet.java delete mode 100644 samples/chat-gwt/src/main/java/com/glines/socketio/sample/gwtchat/client/GWTChatClient.java delete mode 100644 samples/chat-gwt/src/main/resources/com/glines/socketio/sample/gwtchat/GWTChat.gwt.xml delete mode 100644 samples/chat-gwt/src/main/webapp/WEB-INF/web.xml delete mode 100644 samples/chat-gwt/src/main/webapp/gwtchat.html delete mode 100644 samples/chat-gwt/src/test/java/GWTChatServer.java delete mode 100644 samples/chat-gwt/src/test/java/StartSampleChatGWT.java rename samples/chat/src/main/java/com/{glines => codeminders}/socketio/sample/chat/ChatSocketServlet.java (93%) create mode 100644 samples/chat/src/main/resources/META-INF/services/com.codeminders.socketio.server.Transport create mode 100644 samples/chat/src/main/resources/META-INF/services/com.codeminders.socketio.server.TransportConnection rename samples/chat/src/main/{ => resources}/webapp/META-INF/context.xml (100%) rename samples/chat/src/main/{ => resources}/webapp/WEB-INF/web.xml (95%) rename samples/chat/src/main/{ => resources}/webapp/chat.html (97%) rename samples/chat/src/main/{ => resources}/webapp/json.js (100%) delete mode 100644 samples/echo/pom.xml delete mode 100644 samples/echo/src/main/java/com/glines/socketio/sample/echo/EchoSocketServlet.java delete mode 100644 samples/echo/src/main/webapp/WEB-INF/web.xml delete mode 100644 samples/echo/src/test/java/EchoServer.java delete mode 100644 samples/echo/src/test/java/StartSampleEcho.java delete mode 100644 samples/eventbus/pom.xml delete mode 100644 samples/eventbus/src/main/java/com/glines/socketio/sample/eventbus/EventBusServlet.java delete mode 100644 samples/eventbus/src/main/resources/log4j.xml delete mode 100644 samples/eventbus/src/main/webapp/WEB-INF/web.xml delete mode 100644 samples/eventbus/src/main/webapp/index.html delete mode 100644 samples/eventbus/src/main/webapp/jquery-1.4.4.min.js delete mode 100644 samples/eventbus/src/main/webapp/jquery.json-2.2.min.js delete mode 100644 samples/eventbus/src/test/java/StartSampleEventBus.java diff --git a/.gitignore b/.gitignore index 49134a8..eaf3d7b 100644 --- a/.gitignore +++ b/.gitignore @@ -20,5 +20,5 @@ dist war build.properties target -com.glines.socketio.sample.gwtchat.GWTChat +com.codeminders.socketio.sample.gwtchat.GWTChat samples/chat-gwt/src/main/gwt-unitCache/ \ No newline at end of file diff --git a/TODO b/TODO deleted file mode 100644 index e6b6aae..0000000 --- a/TODO +++ /dev/null @@ -1,7 +0,0 @@ -1. Maven pom.xml - -2. Add unit tests - -3. Add embedded containers to run sample webapps - -4. Split project in modules (core, samples, gwt, ...) diff --git a/core/pom.xml b/core/pom.xml index 7b87ecb..e2d2257 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -17,9 +17,9 @@ - com.glines.socketio + com.codeminders.socketio socketio - 0.2.2 + 0.3 .. diff --git a/core/src/main/java/com/glines/socketio/common/ConnectionState.java b/core/src/main/java/com/codeminders/socketio/common/ConnectionState.java similarity index 97% rename from core/src/main/java/com/glines/socketio/common/ConnectionState.java rename to core/src/main/java/com/codeminders/socketio/common/ConnectionState.java index 301e224..ff1cf9f 100644 --- a/core/src/main/java/com/glines/socketio/common/ConnectionState.java +++ b/core/src/main/java/com/codeminders/socketio/common/ConnectionState.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.common; +package com.codeminders.socketio.common; public enum ConnectionState { UNKNOWN(-1), diff --git a/core/src/main/java/com/glines/socketio/common/DisconnectReason.java b/core/src/main/java/com/codeminders/socketio/common/DisconnectReason.java similarity index 98% rename from core/src/main/java/com/glines/socketio/common/DisconnectReason.java rename to core/src/main/java/com/codeminders/socketio/common/DisconnectReason.java index 20de289..eef2ffb 100644 --- a/core/src/main/java/com/glines/socketio/common/DisconnectReason.java +++ b/core/src/main/java/com/codeminders/socketio/common/DisconnectReason.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.common; +package com.codeminders.socketio.common; public enum DisconnectReason { UNKNOWN(-1), diff --git a/core/src/main/java/com/glines/socketio/common/SocketIOException.java b/core/src/main/java/com/codeminders/socketio/common/SocketIOException.java similarity index 92% rename from core/src/main/java/com/glines/socketio/common/SocketIOException.java rename to core/src/main/java/com/codeminders/socketio/common/SocketIOException.java index 694dcf9..66b6e5f 100644 --- a/core/src/main/java/com/glines/socketio/common/SocketIOException.java +++ b/core/src/main/java/com/codeminders/socketio/common/SocketIOException.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.common; +package com.codeminders.socketio.common; import java.io.IOException; @@ -41,7 +41,5 @@ public SocketIOException(Throwable cause) { super(cause); } - public SocketIOException(String message, Throwable cause) { - super(message, cause); - } + public SocketIOException(String message, Throwable cause) { super(message, cause); } } diff --git a/core/src/main/java/com/glines/socketio/server/transport/AbstractDataHandler.java b/core/src/main/java/com/codeminders/socketio/server/AbstractTransport.java similarity index 61% rename from core/src/main/java/com/glines/socketio/server/transport/AbstractDataHandler.java rename to core/src/main/java/com/codeminders/socketio/server/AbstractTransport.java index e7d5bcb..479f46b 100644 --- a/core/src/main/java/com/glines/socketio/server/transport/AbstractDataHandler.java +++ b/core/src/main/java/com/codeminders/socketio/server/AbstractTransport.java @@ -22,28 +22,51 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.server.transport; +package com.codeminders.socketio.server; -import com.glines.socketio.server.SocketIOConfig; +import javax.servlet.ServletConfig; /** + * @author Alexander Sova * @author Mathieu Carbou */ -abstract class AbstractDataHandler implements DataHandler { - +public abstract class AbstractTransport implements Transport +{ private SocketIOConfig config; @Override - public final void init(SocketIOConfig config) { - this.config = config; + public void destroy() + { + } + + @Override + public final void init(ServletConfig config) + { + this.config = new ServletBasedSocketIOConfig(config, getType().toString()); init(); } - protected final SocketIOConfig getConfig() { + protected final SocketIOConfig getConfig() + { return config; } - protected void init() { + protected void init() + { + } + + protected final TransportConnection createConnection(SocketIOSession session) + { + TransportConnection connection = createConnection(); + connection.setSession(session); + connection.init(getConfig()); + return connection; + } + + @Override + public String toString() + { + return getType().toString(); } } diff --git a/core/src/main/java/com/glines/socketio/server/AbstractTransportHandler.java b/core/src/main/java/com/codeminders/socketio/server/AbstractTransportConnection.java similarity index 93% rename from core/src/main/java/com/glines/socketio/server/AbstractTransportHandler.java rename to core/src/main/java/com/codeminders/socketio/server/AbstractTransportConnection.java index 0fe091d..0bf9af3 100644 --- a/core/src/main/java/com/glines/socketio/server/AbstractTransportHandler.java +++ b/core/src/main/java/com/codeminders/socketio/server/AbstractTransportConnection.java @@ -22,12 +22,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.server; +package com.codeminders.socketio.server; /** * @author Mathieu Carbou */ -public abstract class AbstractTransportHandler implements TransportHandler { +public abstract class AbstractTransportConnection implements TransportConnection +{ private SocketIOConfig config; private SocketIOSession session; diff --git a/core/src/main/java/com/glines/socketio/server/DefaultSession.java b/core/src/main/java/com/codeminders/socketio/server/DefaultSession.java similarity index 94% rename from core/src/main/java/com/glines/socketio/server/DefaultSession.java rename to core/src/main/java/com/codeminders/socketio/server/DefaultSession.java index 67b0c2f..2e28740 100644 --- a/core/src/main/java/com/glines/socketio/server/DefaultSession.java +++ b/core/src/main/java/com/codeminders/socketio/server/DefaultSession.java @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.server; +package com.codeminders.socketio.server; -import com.glines.socketio.common.ConnectionState; -import com.glines.socketio.common.DisconnectReason; -import com.glines.socketio.common.SocketIOException; -import com.glines.socketio.util.JSON; +import com.codeminders.socketio.common.SocketIOException; +import com.codeminders.socketio.util.JSON; +import com.codeminders.socketio.common.ConnectionState; +import com.codeminders.socketio.common.DisconnectReason; import java.util.Map; import java.util.concurrent.*; @@ -49,7 +49,7 @@ class DefaultSession implements SocketIOSession { private final Map attributes = new ConcurrentHashMap(); private SocketIOInbound inbound; - private TransportHandler handler; + private TransportConnection handler; private ConnectionState state = ConnectionState.CONNECTING; private long hbDelay; private SessionTask hbDelayTask; @@ -90,7 +90,7 @@ public SocketIOInbound getInbound() { } @Override - public TransportHandler getTransportHandler() { + public TransportConnection getTransportHandler() { return handler; } @@ -218,7 +218,7 @@ public void onMessage(SocketIOFrame message) { if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Session[" + sessionId + "]: onEvent: " + message.getData()); try { - Map json = (Map)JSON.parse(message.getData()); + Map json = (Map) JSON.parse(message.getData()); String name = json.get("name").toString(); Object args = json.get("args"); if(args.getClass().isArray()) { @@ -297,30 +297,30 @@ public boolean cancel() { } @Override - public void onConnect(TransportHandler handler) { - if (handler == null) { + public void onConnect(TransportConnection connection) { + if (connection == null) { state = ConnectionState.CLOSED; inbound = null; socketIOSessionManager.socketIOSessions.remove(sessionId); } else if (this.handler == null) { - this.handler = handler; + this.handler = connection; if (inbound == null) { state = ConnectionState.CLOSED; - handler.abort(); + connection.abort(); } else { try { state = ConnectionState.CONNECTED; - inbound.onConnect(handler); + inbound.onConnect(connection); startHeartbeatTimer(); } catch (Throwable e) { if (LOGGER.isLoggable(Level.WARNING)) LOGGER.log(Level.WARNING, "Session[" + sessionId + "]: Exception thrown by SocketIOInbound.onConnect()", e); state = ConnectionState.CLOSED; - handler.abort(); + connection.abort(); } } } else { - handler.abort(); + connection.abort(); } } diff --git a/core/src/main/java/com/glines/socketio/server/ServletBasedSocketIOConfig.java b/core/src/main/java/com/codeminders/socketio/server/ServletBasedSocketIOConfig.java similarity index 74% rename from core/src/main/java/com/glines/socketio/server/ServletBasedSocketIOConfig.java rename to core/src/main/java/com/codeminders/socketio/server/ServletBasedSocketIOConfig.java index 7672617..70cee11 100644 --- a/core/src/main/java/com/glines/socketio/server/ServletBasedSocketIOConfig.java +++ b/core/src/main/java/com/codeminders/socketio/server/ServletBasedSocketIOConfig.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.server; +package com.codeminders.socketio.server; import javax.servlet.ServletConfig; import java.util.Collection; @@ -34,10 +34,9 @@ /** * @author Mathieu Carbou */ -public final class ServletBasedSocketIOConfig implements SocketIOConfig { - +public final class ServletBasedSocketIOConfig implements SocketIOConfig +{ private static final Logger LOGGER = Logger.getLogger(ServletBasedSocketIOConfig.class.getName()); - private static final String KEY_TRANSPORT = SocketIOConfig.class.getName() + ".TRANSPORTS"; private final ServletConfig config; private final String namespace; @@ -72,31 +71,6 @@ public int getMaxIdle() { return getInt(PARAM_MAX_IDLE, DEFAULT_MAX_IDLE); } - @Override - public void addTransport(Transport transport) { - getTransportMap().put(transport.getType(), transport); - } - - @Override - public Collection getTransports() { - return getTransportMap().values(); - } - - @Override - public Transport getTransport(TransportType type) { - return getTransportMap().get(type); - } - - @Override - public void removeTransport(TransportType type) { - getTransportMap().remove(type); - } - - @Override - public Transport getWebSocketTransport() { - return getTransport(TransportType.WEB_SOCKET); - } - @Override public int getInt(String param, int def) { String v = getString(param); @@ -133,12 +107,4 @@ public String getString(String param, String def) { return v == null ? def : v; } - @SuppressWarnings({"unchecked"}) - private Map getTransportMap() { - Map transports = (Map) config.getServletContext().getAttribute(KEY_TRANSPORT); - if (transports == null) - config.getServletContext().setAttribute(KEY_TRANSPORT, transports = new ConcurrentHashMap()); - return transports; - } - } diff --git a/core/src/main/java/com/glines/socketio/server/SessionManager.java b/core/src/main/java/com/codeminders/socketio/server/SessionManager.java similarity index 97% rename from core/src/main/java/com/glines/socketio/server/SessionManager.java rename to core/src/main/java/com/codeminders/socketio/server/SessionManager.java index 52c4ebe..4fb1931 100644 --- a/core/src/main/java/com/glines/socketio/server/SessionManager.java +++ b/core/src/main/java/com/codeminders/socketio/server/SessionManager.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.server; +package com.codeminders.socketio.server; /** * @author Mathieu Carbou (mathieu.carbou@gmail.com) diff --git a/core/src/main/java/com/glines/socketio/server/SocketIOClosedException.java b/core/src/main/java/com/codeminders/socketio/server/SocketIOClosedException.java similarity index 94% rename from core/src/main/java/com/glines/socketio/server/SocketIOClosedException.java rename to core/src/main/java/com/codeminders/socketio/server/SocketIOClosedException.java index 3e14dce..5abb70f 100644 --- a/core/src/main/java/com/glines/socketio/server/SocketIOClosedException.java +++ b/core/src/main/java/com/codeminders/socketio/server/SocketIOClosedException.java @@ -22,15 +22,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.server; +package com.codeminders.socketio.server; -import com.glines.socketio.common.SocketIOException; +import com.codeminders.socketio.common.SocketIOException; public class SocketIOClosedException extends SocketIOException { private static final long serialVersionUID = 1L; public SocketIOClosedException() { - super(); + } public SocketIOClosedException(String message) { diff --git a/core/src/main/java/com/glines/socketio/server/SocketIOConfig.java b/core/src/main/java/com/codeminders/socketio/server/SocketIOConfig.java similarity index 88% rename from core/src/main/java/com/glines/socketio/server/SocketIOConfig.java rename to core/src/main/java/com/codeminders/socketio/server/SocketIOConfig.java index d3f6d21..2da879f 100644 --- a/core/src/main/java/com/glines/socketio/server/SocketIOConfig.java +++ b/core/src/main/java/com/codeminders/socketio/server/SocketIOConfig.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.server; +package com.codeminders.socketio.server; import java.util.Collection; @@ -50,12 +50,6 @@ public interface SocketIOConfig { int getBufferSize(); int getMaxIdle(); - void addTransport(Transport transport); - Collection getTransports(); - Transport getTransport(TransportType type); - void removeTransport(TransportType type); - Transport getWebSocketTransport(); - String getString(String key); String getString(String key, String def); int getInt(String key, int def); diff --git a/core/src/main/java/com/glines/socketio/server/SocketIOFrame.java b/core/src/main/java/com/codeminders/socketio/server/SocketIOFrame.java similarity index 99% rename from core/src/main/java/com/glines/socketio/server/SocketIOFrame.java rename to core/src/main/java/com/codeminders/socketio/server/SocketIOFrame.java index 061b1b5..59e38f8 100644 --- a/core/src/main/java/com/glines/socketio/server/SocketIOFrame.java +++ b/core/src/main/java/com/codeminders/socketio/server/SocketIOFrame.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.server; +package com.codeminders.socketio.server; import java.util.ArrayList; import java.util.List; diff --git a/core/src/main/java/com/glines/socketio/server/SocketIOInbound.java b/core/src/main/java/com/codeminders/socketio/server/SocketIOInbound.java similarity index 95% rename from core/src/main/java/com/glines/socketio/server/SocketIOInbound.java rename to core/src/main/java/com/codeminders/socketio/server/SocketIOInbound.java index ff0e455..0b3069f 100644 --- a/core/src/main/java/com/glines/socketio/server/SocketIOInbound.java +++ b/core/src/main/java/com/codeminders/socketio/server/SocketIOInbound.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.server; +package com.codeminders.socketio.server; -import com.glines.socketio.common.DisconnectReason; +import com.codeminders.socketio.common.DisconnectReason; public interface SocketIOInbound { diff --git a/core/src/main/java/com/glines/socketio/server/SocketIOOutbound.java b/core/src/main/java/com/codeminders/socketio/server/SocketIOOutbound.java similarity index 94% rename from core/src/main/java/com/glines/socketio/server/SocketIOOutbound.java rename to core/src/main/java/com/codeminders/socketio/server/SocketIOOutbound.java index d9204f5..ff4cf5a 100644 --- a/core/src/main/java/com/glines/socketio/server/SocketIOOutbound.java +++ b/core/src/main/java/com/codeminders/socketio/server/SocketIOOutbound.java @@ -22,10 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.server; +package com.codeminders.socketio.server; -import com.glines.socketio.common.ConnectionState; -import com.glines.socketio.common.SocketIOException; +import com.codeminders.socketio.common.ConnectionState; +import com.codeminders.socketio.common.SocketIOException; public interface SocketIOOutbound { /** diff --git a/core/src/main/java/com/codeminders/socketio/server/SocketIOProtocolException.java b/core/src/main/java/com/codeminders/socketio/server/SocketIOProtocolException.java new file mode 100644 index 0000000..709d171 --- /dev/null +++ b/core/src/main/java/com/codeminders/socketio/server/SocketIOProtocolException.java @@ -0,0 +1,15 @@ +package com.codeminders.socketio.server; + +/** + * @author Alexander Sova + */ +public class SocketIOProtocolException extends Exception { + + public SocketIOProtocolException(String message) { + super(message); + } + + public SocketIOProtocolException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/core/src/main/java/com/glines/socketio/server/SocketIOServlet.java b/core/src/main/java/com/codeminders/socketio/server/SocketIOServlet.java similarity index 50% rename from core/src/main/java/com/glines/socketio/server/SocketIOServlet.java rename to core/src/main/java/com/codeminders/socketio/server/SocketIOServlet.java index b4727c3..4865a13 100644 --- a/core/src/main/java/com/glines/socketio/server/SocketIOServlet.java +++ b/core/src/main/java/com/codeminders/socketio/server/SocketIOServlet.java @@ -22,9 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.server; +package com.codeminders.socketio.server; -import com.glines.socketio.util.IO; +import com.codeminders.socketio.server.transport.AbstractTransportProvider; +import com.codeminders.socketio.util.IO; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -42,62 +43,41 @@ public abstract class SocketIOServlet extends HttpServlet { private static final long serialVersionUID = 2L; private final SocketIOSessionManager sessionManager = new SocketIOSessionManager(); - private final TransportHandlerProvider transportHandlerProvider = new AnnotationTransportHandlerProvider(); - private SocketIOConfig config; + private TransportProvider transportProvider; - public final static String DEFAULT_HEARTBEAT_TIMEOUT = "defaultHeartbeatTimeout"; - public final static String DEFAULT_TIMEOUT = "defaultTimeout"; + public final static String MAX_TEXT_MESSAGE_SIZE = "maxTextMessageSize"; - public final static String MAX_TEXT_MESSAGE_SIZE = "maxTextMessageSize"; + public void setTransportProvider(TransportProvider transportProvider) { + assert (transportProvider != null); + this.transportProvider = transportProvider; + } @Override - public void init() throws ServletException { - config = new ServletBasedSocketIOConfig(getServletConfig(), "socketio"); - - // lazy load available transport handlers - transportHandlerProvider.init(); - if (LOGGER.isLoggable(Level.INFO)) - LOGGER.log(Level.INFO, "Transport handlers loaded: " + transportHandlerProvider.listAll()); - - // lazily load available transports - TransportDiscovery transportDiscovery = new ClasspathTransportDiscovery(); - for (Transport transport : transportDiscovery.discover()) { - if (transportHandlerProvider.isSupported(transport.getType())) { - transport.setTransportHandlerProvider(transportHandlerProvider); - config.addTransport(transport); - } else { - LOGGER.log(Level.WARNING, "Transport " + transport.getType() + " ignored since not supported by any TransportHandler"); - } - } - // initialize them - for (Transport t : config.getTransports()) { - try { - t.init(getServletConfig()); - } catch (TransportInitializationException e) { - config.removeTransport(t.getType()); - LOGGER.log(Level.WARNING, "Transport " + t.getType() + " disabled. Initialization failed: " + e.getMessage()); - } - } + public void init() throws ServletException + { if (LOGGER.isLoggable(Level.INFO)) - LOGGER.log(Level.INFO, "Transports loaded: " + config.getTransports()); + LOGGER.log(Level.INFO, "Transports: " + transportProvider.getTransports()); } @Override - public void destroy() { - for (Transport t : config.getTransports()) { - t.destroy(); - } + public void destroy() + { + transportProvider.destroy(); super.destroy(); } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException + { serve(req, resp); } @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + protected void doPost(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException + { serve(req, resp); } @@ -107,8 +87,10 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S */ protected abstract SocketIOInbound doSocketIOConnect(HttpServletRequest request); - private void serve(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - + private void serve(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException + { + //TODO: Need to remove this check. Transport is not defined in path anymore String path = request.getPathInfo(); if (path == null || path.length() == 0 || "/".equals(path)) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing SocketIO transport"); @@ -119,42 +101,38 @@ private void serve(HttpServletRequest request, HttpServletResponse response) thr if ("GET".equals(request.getMethod()) && "socket.io.js".equals(parts[0])) { response.setContentType("text/javascript"); - InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/glines/socketio/socket.io.js"); + InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/codeminders/socketio/socket.io.js"); OutputStream os = response.getOutputStream(); IO.copy(is, os); - return; - } else if ("GET".equals(request.getMethod()) && "WebSocketMain.swf".equals(parts[0])) { + } + else + if ("GET".equals(request.getMethod()) && "WebSocketMain.swf".equals(parts[0])) { response.setContentType("application/x-shockwave-flash"); - InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/glines/socketio/WebSocketMain.swf"); + InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/codeminders/socketio/WebSocketMain.swf"); OutputStream os = response.getOutputStream(); IO.copy(is, os); - return; - } else if (parts.length <= 2) { // handshake - OutputStream os = response.getOutputStream(); - - // Format: sessionId : heartbeat : timeout - String body = request.getSession().getId().toString() + - ":" + (config.getString(DEFAULT_HEARTBEAT_TIMEOUT) == null ? "15000" : config.getString(DEFAULT_HEARTBEAT_TIMEOUT)) + - ":" + (config.getString(DEFAULT_TIMEOUT) == null? "10000" : config.getString(DEFAULT_TIMEOUT)) + ":"; - - String transports = ""; // websocket,flashsocket,xhr-polling,jsonp-polling,htmlfile - for (Transport transport : config.getTransports()) { - if (!transports.isEmpty()) - transports += ","; - transports += transport.getType().toString(); - } - body += transports; - - os.write(body.getBytes()); - return; - } else { - Transport transport = config.getTransport(TransportType.from(parts[1])); - if (transport == null) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unknown SocketIO transport: " + parts[0]); + } + else + { + assert (transportProvider != null); + + Transport transport = null; + try + { + transport = transportProvider.getTransport(request); + } catch (UnsupportedTransportException | SocketIOProtocolException e) + { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Cannot find appropriate transport"); + + if(LOGGER.isLoggable(Level.WARNING)) + LOGGER.log(Level.WARNING, "Cannot find appropriate transport", e); return; } + if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, "Handling request from " + request.getRemoteHost() + ":" + request.getRemotePort() + " with transport: " + transport.getType()); + LOGGER.log(Level.FINE, "Handling request from " + + request.getRemoteHost() + ":" + request.getRemotePort() + + " with transport: " + transport.getType()); transport.handle(request, response, new Transport.InboundFactory() { @Override diff --git a/core/src/main/java/com/glines/socketio/server/SocketIOSession.java b/core/src/main/java/com/codeminders/socketio/server/SocketIOSession.java similarity index 89% rename from core/src/main/java/com/glines/socketio/server/SocketIOSession.java rename to core/src/main/java/com/codeminders/socketio/server/SocketIOSession.java index ab000f4..e5914a7 100644 --- a/core/src/main/java/com/glines/socketio/server/SocketIOSession.java +++ b/core/src/main/java/com/codeminders/socketio/server/SocketIOSession.java @@ -22,10 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.server; +package com.codeminders.socketio.server; -import com.glines.socketio.common.ConnectionState; -import com.glines.socketio.common.DisconnectReason; +import com.codeminders.socketio.common.ConnectionState; +import com.codeminders.socketio.common.DisconnectReason; public interface SocketIOSession { void setAttribute(String key, Object val); @@ -44,7 +44,7 @@ interface SessionTask { SocketIOInbound getInbound(); - TransportHandler getTransportHandler(); + TransportConnection getTransportHandler(); void setHeartbeat(long delay); long getHeartbeat(); @@ -77,9 +77,9 @@ interface SessionTask { SessionTask scheduleTask(Runnable task, long delay); /** - * @param handler The handler or null if the connection failed. + * @param connection The connection or null if the connection failed. */ - void onConnect(TransportHandler handler); + void onConnect(TransportConnection connection); /** * Pass message through to contained SocketIOInbound diff --git a/core/src/main/java/com/glines/socketio/server/SocketIOSessionManager.java b/core/src/main/java/com/codeminders/socketio/server/SocketIOSessionManager.java similarity index 98% rename from core/src/main/java/com/glines/socketio/server/SocketIOSessionManager.java rename to core/src/main/java/com/codeminders/socketio/server/SocketIOSessionManager.java index 37ce4ce..a8e3230 100644 --- a/core/src/main/java/com/glines/socketio/server/SocketIOSessionManager.java +++ b/core/src/main/java/com/codeminders/socketio/server/SocketIOSessionManager.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.server; +package com.codeminders.socketio.server; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; diff --git a/core/src/main/java/com/glines/socketio/server/Transport.java b/core/src/main/java/com/codeminders/socketio/server/Transport.java similarity index 86% rename from core/src/main/java/com/glines/socketio/server/Transport.java rename to core/src/main/java/com/codeminders/socketio/server/Transport.java index 5d9ea07..d6378cc 100644 --- a/core/src/main/java/com/glines/socketio/server/Transport.java +++ b/core/src/main/java/com/codeminders/socketio/server/Transport.java @@ -22,16 +22,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.server; +package com.codeminders.socketio.server; import javax.servlet.ServletConfig; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -public interface Transport { - - interface InboundFactory { +public interface Transport +{ + interface InboundFactory + { SocketIOInbound getInbound(HttpServletRequest request); } @@ -40,9 +41,7 @@ interface InboundFactory { */ TransportType getType(); - void setTransportHandlerProvider(TransportHandlerProvider transportHandlerProvider); - - void init(ServletConfig config) throws TransportInitializationException; + void init(ServletConfig config); void destroy(); @@ -50,4 +49,6 @@ void handle(HttpServletRequest request, HttpServletResponse response, Transport.InboundFactory inboundFactory, SessionManager sessionFactory) throws IOException; + + TransportConnection createConnection(); } diff --git a/core/src/main/java/com/glines/socketio/server/TransportHandler.java b/core/src/main/java/com/codeminders/socketio/server/TransportConnection.java similarity index 81% rename from core/src/main/java/com/glines/socketio/server/TransportHandler.java rename to core/src/main/java/com/codeminders/socketio/server/TransportConnection.java index 319b542..6868f47 100644 --- a/core/src/main/java/com/glines/socketio/server/TransportHandler.java +++ b/core/src/main/java/com/codeminders/socketio/server/TransportConnection.java @@ -22,21 +22,27 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.server; +package com.codeminders.socketio.server; -import com.glines.socketio.common.SocketIOException; +import com.codeminders.socketio.common.SocketIOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** -* @author Mathieu Carbou + * @author Mathieu Carbou + * @author Alexander Sova */ -public interface TransportHandler extends SocketIOOutbound { +public interface TransportConnection extends SocketIOOutbound +{ + void init(SocketIOConfig config); void setSession(SocketIOSession session); - + Transport getTransport(); + + void connect(HttpServletRequest request, HttpServletResponse response) throws IOException, SocketIOProtocolException; + void handle(HttpServletRequest request, HttpServletResponse response, SocketIOSession session) throws IOException; void sendMessage(SocketIOFrame message) throws SocketIOException; void disconnectWhenEmpty(); @@ -47,6 +53,7 @@ public interface TransportHandler extends SocketIOOutbound { */ void abort(); + //TODO: do we need two 'connect' callbacks? void onConnect(); } diff --git a/core/src/main/java/com/codeminders/socketio/server/TransportProvider.java b/core/src/main/java/com/codeminders/socketio/server/TransportProvider.java new file mode 100644 index 0000000..68a5dd4 --- /dev/null +++ b/core/src/main/java/com/codeminders/socketio/server/TransportProvider.java @@ -0,0 +1,27 @@ +package com.codeminders.socketio.server; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletRequest; +import java.util.Collection; + +/** + * @author Alexander Sova + */ +public interface TransportProvider { + + /* + Creates all the transports + */ + void init(ServletConfig config); + void destroy(); + + /* + Finds appropriate Transport class based on the rules defined at + https://github.com/socketio/engine.io-protocol#transports + */ + Transport getTransport(ServletRequest request) + throws UnsupportedTransportException, SocketIOProtocolException; + + Transport getTransport(TransportType type); + Collection getTransports(); +} diff --git a/core/src/main/java/com/glines/socketio/server/TransportType.java b/core/src/main/java/com/codeminders/socketio/server/TransportType.java similarity index 96% rename from core/src/main/java/com/glines/socketio/server/TransportType.java rename to core/src/main/java/com/codeminders/socketio/server/TransportType.java index 2f5dfb5..dba6ccb 100644 --- a/core/src/main/java/com/glines/socketio/server/TransportType.java +++ b/core/src/main/java/com/codeminders/socketio/server/TransportType.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.server; +package com.codeminders.socketio.server; /** * @author Mathieu Carbou (mathieu.carbou@gmail.com) @@ -31,7 +31,6 @@ public enum TransportType { WEB_SOCKET("websocket"), FLASH_SOCKET("flashsocket"), - HTML_FILE("htmlfile"), JSONP_POLLING("jsonp-polling"), XHR_MULTIPART("xhr-multipart"), XHR_POLLING("xhr-polling"), diff --git a/core/src/main/java/com/codeminders/socketio/server/UnsupportedTransportException.java b/core/src/main/java/com/codeminders/socketio/server/UnsupportedTransportException.java new file mode 100644 index 0000000..de96116 --- /dev/null +++ b/core/src/main/java/com/codeminders/socketio/server/UnsupportedTransportException.java @@ -0,0 +1,11 @@ +package com.codeminders.socketio.server; + +/** + * @author Alexander Sova + */ +public class UnsupportedTransportException extends Exception { + + public UnsupportedTransportException(String name) { + super("Unsupported transport " + name); + } +} diff --git a/core/src/main/java/com/glines/socketio/server/transport/AbstractHttpTransport.java b/core/src/main/java/com/codeminders/socketio/server/transport/AbstractHttpTransport.java similarity index 61% rename from core/src/main/java/com/glines/socketio/server/transport/AbstractHttpTransport.java rename to core/src/main/java/com/codeminders/socketio/server/transport/AbstractHttpTransport.java index ce6068c..6fa2947 100644 --- a/core/src/main/java/com/glines/socketio/server/transport/AbstractHttpTransport.java +++ b/core/src/main/java/com/codeminders/socketio/server/transport/AbstractHttpTransport.java @@ -22,11 +22,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.server.transport; +package com.codeminders.socketio.server.transport; -import com.glines.socketio.server.*; -import com.glines.socketio.util.Web; +import com.codeminders.socketio.server.*; +import com.codeminders.socketio.util.Web; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @@ -41,35 +43,50 @@ public abstract class AbstractHttpTransport extends AbstractTransport { @Override public final void handle(HttpServletRequest request, HttpServletResponse response, - Transport.InboundFactory inboundFactory, - SessionManager sessionFactory) throws IOException { + InboundFactory inboundFactory, + SessionManager sessionFactory) throws IOException + { if (LOGGER.isLoggable(Level.FINE)) LOGGER.fine("Handling request " + request.getRequestURI() + " by " + getClass().getName()); SocketIOSession session = null; String sessionId = Web.extractSessionId(request); - if (sessionId != null && sessionId.length() > 0) { + if (sessionId != null && sessionId.length() > 0) + { session = sessionFactory.getSession(sessionId); } - if (session != null) { - TransportHandler handler = session.getTransportHandler(); - if (handler != null) { + if (session != null) + { + TransportConnection handler = session.getTransportHandler(); + if (handler != null) + { handler.handle(request, response, session); - } else { + } + else + { session.onShutdown(); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } - } else { - if ("GET".equals(request.getMethod())) { - session = connect(request, response, inboundFactory, - sessionFactory, sessionId); - if (session == null) { - response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); + } + else + { + if ("GET".equals(request.getMethod())) + { + try + { + session = connect(request, response, inboundFactory, + sessionFactory, sessionId); + } catch (SocketIOProtocolException e) + { + if(LOGGER.isLoggable(Level.WARNING)) + LOGGER.log(Level.WARNING, "Cannot initialize connection", e); } - } else { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); + if (session == null) + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); } + else + response.sendError(HttpServletResponse.SC_BAD_REQUEST); } } @@ -77,26 +94,32 @@ private SocketIOSession connect(HttpServletRequest request, HttpServletResponse response, InboundFactory inboundFactory, SessionManager sessionFactory, - String sessionId) throws IOException { + String sessionId) throws IOException, SocketIOProtocolException + { SocketIOInbound inbound = inboundFactory.getInbound(request); - if (inbound != null) { + if (inbound != null) + { if (sessionId == null) - sessionId = request.getSession().getId().toString(); + sessionId = request.getSession().getId(); + SocketIOSession session = sessionFactory.createSession(inbound, sessionId); - // get and init data handler - DataHandler dataHandler = newDataHandler(session); - dataHandler.init(getConfig()); - // get and init transport handler - TransportHandler transportHandler = newHandler(ConnectableTransportHandler.class, session); - ConnectableTransportHandler connectableTransportHandler = ConnectableTransportHandler.class.cast(transportHandler); - connectableTransportHandler.setDataHandler(dataHandler); - transportHandler.init(getConfig()); - // connect transport to session - connectableTransportHandler.connect(request, response); + + //TODO: this is wierd. need to refactor connection sequence + createConnection(session).connect(request, response); + connect(session, request, response); + return session; } + return null; } - protected abstract DataHandler newDataHandler(SocketIOSession session); + public abstract void startSend(SocketIOSession session, ServletResponse response) throws IOException; + + public abstract void writeData(SocketIOSession session, ServletResponse response, String data) throws IOException; + + public abstract void finishSend(SocketIOSession session, ServletResponse response) throws IOException; + + public abstract void connect(SocketIOSession session, ServletRequest request, ServletResponse response) + throws IOException, SocketIOProtocolException; } diff --git a/core/src/main/java/com/codeminders/socketio/server/transport/AbstractTransportProvider.java b/core/src/main/java/com/codeminders/socketio/server/transport/AbstractTransportProvider.java new file mode 100644 index 0000000..fb3bc81 --- /dev/null +++ b/core/src/main/java/com/codeminders/socketio/server/transport/AbstractTransportProvider.java @@ -0,0 +1,111 @@ +package com.codeminders.socketio.server.transport; + +import com.codeminders.socketio.server.*; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletRequest; +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * @author Alexander Sova + */ +public abstract class AbstractTransportProvider implements TransportProvider { + + private static final Logger LOGGER = Logger.getLogger(AbstractTransportProvider.class.getName()); + + protected Map transports = new HashMap<>(); + + /** + * Creates and initializes all available transports + */ + @Override + public void init(ServletConfig config) + { + transports.put(TransportType.XHR_POLLING, createXHTPollingTransport()); + transports.put(TransportType.JSONP_POLLING, createJSONPPollingTransport()); + + //TODO: should we allow server to work whithout websocket support? + //TODO: websocket is one of the "must support" transports for socket.io 1.0 servers + Transport transport = createWebSocketTransport(); + if(transport != null) + { + transports.put(TransportType.WEB_SOCKET, transport); + transport = createFlashSocketTransport(transport); + if(transport != null) + transports.put(TransportType.FLASH_SOCKET, transport); + } + + for(Transport t : transports.values()) + t.init(config); + } + + @Override + public void destroy() { + for (Transport t : getTransports()) { + t.destroy(); + } + } + + @Override + public Transport getTransport(ServletRequest request) + throws UnsupportedTransportException, SocketIOProtocolException + { + //TODO: check EIO version? I do not see it in the spec but it is in actual requests. Ignoring it for now. + String transportName = request.getParameter("transport"); + if(transportName == null) + throw new SocketIOProtocolException("Missing transport parameter"); + + TransportType type = TransportType.UNKNOWN; + + if("websocket".equals(transportName) || "flashsocket".equals(transportName)) + type = TransportType.from(transportName); + + if("polling".equals(transportName)) { + if(request.getParameter("j") != null) + type = TransportType.JSONP_POLLING; + else + type = TransportType.XHR_POLLING; + } + + Transport t = transports.get(type); + if(t == null) + throw new UnsupportedTransportException(transportName); + return t; + } + + @Override + public Transport getTransport(TransportType type) + { + return transports.get(type); + } + + @Override + public Collection getTransports() + { + return transports.values(); + } + + protected Transport createFlashSocketTransport(Transport delegate) + { + return null; + } + + protected Transport createXHTPollingTransport() + { + return null; + } + + protected Transport createJSONPPollingTransport() + { + return null; + } + + protected Transport createWebSocketTransport() + { + return null; + } + + +} diff --git a/core/src/main/java/com/glines/socketio/server/transport/FlashSocketTransport.java b/core/src/main/java/com/codeminders/socketio/server/transport/FlashSocketTransport.java similarity index 91% rename from core/src/main/java/com/glines/socketio/server/transport/FlashSocketTransport.java rename to core/src/main/java/com/codeminders/socketio/server/transport/FlashSocketTransport.java index 167bf4b..3e65a44 100644 --- a/core/src/main/java/com/glines/socketio/server/transport/FlashSocketTransport.java +++ b/core/src/main/java/com/codeminders/socketio/server/transport/FlashSocketTransport.java @@ -22,10 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.server.transport; +package com.codeminders.socketio.server.transport; -import com.glines.socketio.server.*; -import com.glines.socketio.util.IO; +import com.codeminders.socketio.server.*; +import com.codeminders.socketio.util.IO; +import org.apache.log4j.lf5.LogLevel; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -44,12 +45,13 @@ public class FlashSocketTransport extends AbstractTransport { - public static final String PARAM_FLASHPOLICY_DOMAIN = "flashPolicyDomain"; + public static final String PARAM_FLASHPOLICY_DOMAIN = "flashPolicyDomain"; public static final String PARAM_FLASHPOLICY_SERVER_HOST = "flashPolicyServerHost"; public static final String PARAM_FLASHPOLICY_SERVER_PORT = "flashPolicyServerPort"; - public static final String PARAM_FLASHPOLICY_PORTS = "flashPolicyPorts"; + public static final String PARAM_FLASHPOLICY_PORTS = "flashPolicyPorts"; private static final Logger LOGGER = Logger.getLogger(FlashSocketTransport.class.getName()); + private static final String FLASHFILE_NAME = "WebSocketMain.swf"; private static final String FLASHFILE_PATH = TransportType.FLASH_SOCKET + "/" + FLASHFILE_NAME; @@ -65,17 +67,25 @@ public class FlashSocketTransport extends AbstractTransport { private Transport delegate; @Override - public TransportType getType() { + public TransportType getType() + { return TransportType.FLASH_SOCKET; } + public FlashSocketTransport(Transport delegate) + { + this.delegate = delegate; + } + @Override - public void init() throws TransportInitializationException { + public void init() + { + assert (delegate != null); + this.flashPolicyDomain = getConfig().getString(PARAM_FLASHPOLICY_DOMAIN); this.flashPolicyPorts = getConfig().getString(PARAM_FLASHPOLICY_PORTS); this.flashPolicyServerHost = getConfig().getString(PARAM_FLASHPOLICY_SERVER_HOST); this.flashPolicyServerPort = getConfig().getInt(PARAM_FLASHPOLICY_SERVER_PORT, 843); - this.delegate = getConfig().getWebSocketTransport(); if (LOGGER.isLoggable(Level.FINE)) LOGGER.fine(getType() + " configuration:\n" + @@ -85,21 +95,19 @@ public void init() throws TransportInitializationException { " - flashPolicyServerPort=" + flashPolicyServerPort + "\n" + " - websocket delegate=" + (delegate == null ? "" : delegate.getClass().getName())); - if (delegate == null) - throw new TransportInitializationException("No WebSocket transport available for this transport: " + getClass().getName()); - if (flashPolicyServerHost != null && flashPolicyDomain != null && flashPolicyPorts != null) { try { startFlashPolicyServer(); } catch (IOException e) { - e.printStackTrace(); + LOGGER.log(Level.SEVERE, "Cannot start a policy server", e); // Ignore } } } @Override - public void destroy() { + public void destroy() + { stopFlashPolicyServer(); } @@ -126,7 +134,7 @@ public void handle(HttpServletRequest request, try { IO.copy(is, os); } catch (IOException e) { - LOGGER.log(Level.FINE, "Error writing " + FLASHFILE_NAME + ": " + e.getMessage(), e); + LOGGER.log(Level.WARNING, "Error writing " + FLASHFILE_NAME + ": " + e.getMessage(), e); } } } else { @@ -194,7 +202,8 @@ public void run() { }); } - private void stopFlashPolicyServer() { + private void stopFlashPolicyServer() + { if (flashPolicyServer != null) { try { flashPolicyServer.close(); @@ -217,4 +226,9 @@ private void stopFlashPolicyServer() { } } + @Override + public TransportConnection createConnection() + { + return delegate.createConnection(); + } } diff --git a/core/src/main/java/com/codeminders/socketio/server/transport/JSONPPollingTransport.java b/core/src/main/java/com/codeminders/socketio/server/transport/JSONPPollingTransport.java new file mode 100644 index 0000000..114aa28 --- /dev/null +++ b/core/src/main/java/com/codeminders/socketio/server/transport/JSONPPollingTransport.java @@ -0,0 +1,91 @@ +/** + * The MIT License + * Copyright (c) 2015 + * + * Contributors: Tad Glines, Ovea.com, Mycila.com + * + * 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. + * + * @author Alexander Sova + */ +package com.codeminders.socketio.server.transport; + +import com.codeminders.socketio.server.SocketIOFrame; +import com.codeminders.socketio.server.SocketIOProtocolException; +import com.codeminders.socketio.server.SocketIOSession; +import com.codeminders.socketio.server.TransportType; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import java.io.IOException; + +public abstract class JSONPPollingTransport extends AbstractHttpTransport +{ + private static final String EIO_PREFIX = "___eio"; + private static final String FRAME_ID = JSONPPollingTransport.class.getName() + ".FRAME_ID"; + + protected JSONPPollingTransport() { } + + @Override + public TransportType getType() + { + return TransportType.JSONP_POLLING; + } + + @Override + public void startSend(SocketIOSession session, ServletResponse response) throws IOException + { + response.setContentType("text/javascript; charset=UTF-8"); + } + + @Override + public void writeData(SocketIOSession session, ServletResponse response, String data) throws IOException + { + //TODO: encode data? + response.getOutputStream().print(EIO_PREFIX); + response.getOutputStream().print("[" + session.getAttribute(FRAME_ID) + "]('"); + response.getOutputStream().print(data); + response.getOutputStream().print("');"); + } + + @Override + public void finishSend(SocketIOSession session, ServletResponse response) throws IOException + { + response.flushBuffer(); + } + + @Override + public void connect(SocketIOSession session, ServletRequest request, ServletResponse response) + throws IOException, SocketIOProtocolException + { + try { + //TODO: Use string constant for request parameter name "j" + //TODO: Do we really need to enforce "j" to be an integer? + session.setAttribute(FRAME_ID, Integer.parseInt(request.getParameter("j"))); + } catch (NullPointerException | NumberFormatException e) { + throw new SocketIOProtocolException("Missing or invalid 'j' parameter. It suppose to be integer"); + } + + startSend(session, response); + + //TODO: check it! + writeData(session, response, SocketIOFrame.encode(SocketIOFrame.FrameType.CONNECT, session.getSessionId())); + + } +} diff --git a/core/src/main/java/com/codeminders/socketio/server/transport/XHRPollingTransport.java b/core/src/main/java/com/codeminders/socketio/server/transport/XHRPollingTransport.java new file mode 100644 index 0000000..54a572c --- /dev/null +++ b/core/src/main/java/com/codeminders/socketio/server/transport/XHRPollingTransport.java @@ -0,0 +1,72 @@ +/** + * The MIT License + * Copyright (c) 2010 Tad Glines + * + * Contributors: Ovea.com, Mycila.com + * + * 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; + +import com.codeminders.socketio.server.SocketIOFrame; +import com.codeminders.socketio.server.SocketIOProtocolException; +import com.codeminders.socketio.server.SocketIOSession; +import com.codeminders.socketio.server.TransportType; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import java.io.IOException; + +public abstract class XHRPollingTransport extends AbstractHttpTransport +{ + @Override + public TransportType getType() + { + return TransportType.XHR_POLLING; + } + + @Override + public void startSend(SocketIOSession session, ServletResponse response) throws IOException + { + response.setContentType("text/plain; charset=UTF-8"); + } + + @Override + public void writeData(SocketIOSession session, ServletResponse response, String data) throws IOException + { + response.getOutputStream().print(data); + response.flushBuffer(); + } + + @Override + public void finishSend(SocketIOSession session, ServletResponse response) throws IOException + { + + } + + @Override + public void connect(SocketIOSession session, ServletRequest request, ServletResponse response) throws IOException, SocketIOProtocolException + { + startSend(session, response); + + //TODO: check!!! + writeData(session, response, SocketIOFrame.encode(SocketIOFrame.FrameType.CONNECT, session.getSessionId())); + + } +} diff --git a/core/src/main/java/com/glines/socketio/util/DefaultLoader.java b/core/src/main/java/com/codeminders/socketio/util/DefaultLoader.java similarity index 98% rename from core/src/main/java/com/glines/socketio/util/DefaultLoader.java rename to core/src/main/java/com/codeminders/socketio/util/DefaultLoader.java index b774f76..5845b40 100644 --- a/core/src/main/java/com/glines/socketio/util/DefaultLoader.java +++ b/core/src/main/java/com/codeminders/socketio/util/DefaultLoader.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.util; +package com.codeminders.socketio.util; import java.io.IOException; import java.net.URL; diff --git a/core/src/main/java/com/glines/socketio/util/IO.java b/core/src/main/java/com/codeminders/socketio/util/IO.java similarity index 98% rename from core/src/main/java/com/glines/socketio/util/IO.java rename to core/src/main/java/com/codeminders/socketio/util/IO.java index cb18dd4..992a732 100644 --- a/core/src/main/java/com/glines/socketio/util/IO.java +++ b/core/src/main/java/com/codeminders/socketio/util/IO.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.util; +package com.codeminders.socketio.util; import java.io.*; diff --git a/core/src/main/java/com/glines/socketio/util/JSON.java b/core/src/main/java/com/codeminders/socketio/util/JSON.java similarity index 99% rename from core/src/main/java/com/glines/socketio/util/JSON.java rename to core/src/main/java/com/codeminders/socketio/util/JSON.java index 174f88c..06bca8c 100644 --- a/core/src/main/java/com/glines/socketio/util/JSON.java +++ b/core/src/main/java/com/codeminders/socketio/util/JSON.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.util; +package com.codeminders.socketio.util; import java.io.IOException; import java.io.Reader; diff --git a/core/src/main/java/com/glines/socketio/util/JdkOverLog4j.java b/core/src/main/java/com/codeminders/socketio/util/JdkOverLog4j.java similarity index 99% rename from core/src/main/java/com/glines/socketio/util/JdkOverLog4j.java rename to core/src/main/java/com/codeminders/socketio/util/JdkOverLog4j.java index 07f4e53..c0ee477 100644 --- a/core/src/main/java/com/glines/socketio/util/JdkOverLog4j.java +++ b/core/src/main/java/com/codeminders/socketio/util/JdkOverLog4j.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.util; +package com.codeminders.socketio.util; /** * @author Mathieu Carbou (mathieu.carbou@gmail.com) diff --git a/core/src/main/java/com/glines/socketio/util/Loader.java b/core/src/main/java/com/codeminders/socketio/util/Loader.java similarity index 97% rename from core/src/main/java/com/glines/socketio/util/Loader.java rename to core/src/main/java/com/codeminders/socketio/util/Loader.java index 03a63e8..0c4c3b8 100644 --- a/core/src/main/java/com/glines/socketio/util/Loader.java +++ b/core/src/main/java/com/codeminders/socketio/util/Loader.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.util; +package com.codeminders.socketio.util; import java.net.URL; import java.util.List; diff --git a/core/src/main/java/com/glines/socketio/util/ServiceClassLoader.java b/core/src/main/java/com/codeminders/socketio/util/ServiceClassLoader.java similarity index 99% rename from core/src/main/java/com/glines/socketio/util/ServiceClassLoader.java rename to core/src/main/java/com/codeminders/socketio/util/ServiceClassLoader.java index f7065df..f949d39 100644 --- a/core/src/main/java/com/glines/socketio/util/ServiceClassLoader.java +++ b/core/src/main/java/com/codeminders/socketio/util/ServiceClassLoader.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.util; +package com.codeminders.socketio.util; import java.io.BufferedReader; import java.io.IOException; diff --git a/core/src/main/java/com/glines/socketio/util/URI.java b/core/src/main/java/com/codeminders/socketio/util/URI.java similarity index 99% rename from core/src/main/java/com/glines/socketio/util/URI.java rename to core/src/main/java/com/codeminders/socketio/util/URI.java index 58da700..358d4b3 100644 --- a/core/src/main/java/com/glines/socketio/util/URI.java +++ b/core/src/main/java/com/codeminders/socketio/util/URI.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.util; +package com.codeminders.socketio.util; import java.io.UnsupportedEncodingException; diff --git a/core/src/main/java/com/glines/socketio/util/Web.java b/core/src/main/java/com/codeminders/socketio/util/Web.java similarity index 98% rename from core/src/main/java/com/glines/socketio/util/Web.java rename to core/src/main/java/com/codeminders/socketio/util/Web.java index 0dcf7a6..273013d 100644 --- a/core/src/main/java/com/glines/socketio/util/Web.java +++ b/core/src/main/java/com/codeminders/socketio/util/Web.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.util; +package com.codeminders.socketio.util; import javax.servlet.http.HttpServletRequest; import java.security.SecureRandom; diff --git a/core/src/main/java/com/glines/socketio/annotation/Handle.java b/core/src/main/java/com/glines/socketio/annotation/Handle.java deleted file mode 100644 index 48d1933..0000000 --- a/core/src/main/java/com/glines/socketio/annotation/Handle.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.annotation; - -import com.glines.socketio.server.TransportType; - -import java.lang.annotation.*; - -/** - * @author Mathieu Carbou (mathieu.carbou@gmail.com) - */ -@Target(ElementType.TYPE) -@Inherited -@Retention(RetentionPolicy.RUNTIME) -public @interface Handle { - TransportType[] value() default {}; -} diff --git a/core/src/main/java/com/glines/socketio/client/common/SocketIOConnection.java b/core/src/main/java/com/glines/socketio/client/common/SocketIOConnection.java deleted file mode 100644 index 8cc11ef..0000000 --- a/core/src/main/java/com/glines/socketio/client/common/SocketIOConnection.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.client.common; - -import com.glines.socketio.common.ConnectionState; -import com.glines.socketio.common.DisconnectReason; -import com.glines.socketio.common.SocketIOException; - -public interface SocketIOConnection { - interface Factory { - SocketIOConnection create(SocketIOConnectionListener listener, - String host, short port); - } - - /** - * Initiate a connection attempt. If the connection succeeds, then the - * {@link SocketIOConnectionListener#onConnect() onConnect} will be called. If the connection - * attempt fails, then - * {@link SocketIOConnectionListener#onDisconnect(DisconnectReason, String) onDisonnect} will - * be called. - * @throws IllegalStateException if the socket is not CLOSED. - */ - void connect(); - - /** - * Forcefully disconnect the connection, discarding any unsent messages. - * This does nothing if the connection is already CLOSED. - * This will abort an orderly close if one was initiated. - */ - void disconnect(); - - /** - * Initiate an orderly close of the connection. - * - * @throws IllegalStateException if the socket is not CONNECTED. - */ - void close(); - - /** - * Return the current socket connection state. - * @return - */ - ConnectionState getConnectionState(); - - /** - * Send a message. - * - * @param message - * @throws IllegalStateException if the socket is not CONNECTED. - */ - void sendMessage(String message) throws SocketIOException; - - /** - * Send a message. With a default priority of 0. - * - * @param message - * @throws IllegalStateException if the socket is not CONNECTED. - * @throws SocketIOException if the message type parser encode() failed. - */ - void sendMessage(int messageType, String message) throws SocketIOException; -} diff --git a/core/src/main/java/com/glines/socketio/client/common/SocketIOConnectionListener.java b/core/src/main/java/com/glines/socketio/client/common/SocketIOConnectionListener.java deleted file mode 100644 index 4f72ba7..0000000 --- a/core/src/main/java/com/glines/socketio/client/common/SocketIOConnectionListener.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.client.common; - -import com.glines.socketio.common.DisconnectReason; - -/** -* @author Mathieu Carbou (mathieu.carbou@gmail.com) -*/ -public interface SocketIOConnectionListener { - void onConnect(); - void onDisconnect(DisconnectReason reason, String errorMessage); - void onMessage(int messageType, String message); -} diff --git a/core/src/main/java/com/glines/socketio/server/AbstractTransport.java b/core/src/main/java/com/glines/socketio/server/AbstractTransport.java deleted file mode 100644 index daa1ec4..0000000 --- a/core/src/main/java/com/glines/socketio/server/AbstractTransport.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.server; - -import javax.servlet.ServletConfig; - -/** - * @author Mathieu Carbou - */ -public abstract class AbstractTransport implements Transport { - - private ServletConfig servletConfig; - private SocketIOConfig config; - private TransportHandlerProvider transportHandlerProvider; - - @Override - public void destroy() { - } - - @Override - public final void init(ServletConfig config) throws TransportInitializationException { - this.servletConfig = config; - this.config = new ServletBasedSocketIOConfig(servletConfig, getType().toString()); - init(); - } - - protected final ServletConfig getServletConfig() { - return servletConfig; - } - - protected final SocketIOConfig getConfig() { - return config; - } - - protected void init() throws TransportInitializationException { - } - - protected final TransportHandler newHandler(Class type, SocketIOSession session) { - TransportHandler handler = transportHandlerProvider.get(type, getType()); - handler.setSession(session); - return handler; - } - - @Override - public String toString() { - return getType().toString(); - } - - @Override - public final void setTransportHandlerProvider(TransportHandlerProvider transportHandlerProvider) { - this.transportHandlerProvider = transportHandlerProvider; - } -} diff --git a/core/src/main/java/com/glines/socketio/server/AnnotationTransportHandlerProvider.java b/core/src/main/java/com/glines/socketio/server/AnnotationTransportHandlerProvider.java deleted file mode 100644 index 5f7bf31..0000000 --- a/core/src/main/java/com/glines/socketio/server/AnnotationTransportHandlerProvider.java +++ /dev/null @@ -1,108 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.server; - -import com.glines.socketio.annotation.Handle; -import com.glines.socketio.util.DefaultLoader; -import com.glines.socketio.util.ServiceClassLoader; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.Collection; -import java.util.EnumMap; -import java.util.Iterator; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * @author Mathieu Carbou (mathieu.carbou@gmail.com) - */ -public final class AnnotationTransportHandlerProvider implements TransportHandlerProvider { - - private static final Logger LOGGER = Logger.getLogger(AnnotationTransportHandlerProvider.class.getName()); - - private final Map> handlerClasses = new EnumMap>(TransportType.class); - - @Override - public TransportHandler get(Class handlerType, TransportType transportType) { - Class handlerClass = handlerClasses.get(transportType); - if (handlerClass == null) - throw new IllegalArgumentException("No TransportHandler found for transport " + transportType); - if (!handlerType.isAssignableFrom(handlerClass)) - throw new IllegalArgumentException("TransportHandler " + handlerClass.getName() + " is not of required type " + handlerType.getName() + " for transport " + transportType); - return load(handlerClass); - } - - @Override - public boolean isSupported(TransportType type) { - return handlerClasses.containsKey(type); - } - - @Override - public void init() { - handlerClasses.clear(); - ServiceClassLoader serviceClassLoader = ServiceClassLoader.load( - TransportHandler.class, - new DefaultLoader(Thread.currentThread().getContextClassLoader())); - Iterator> it = serviceClassLoader.iterator(); - while (it.hasNext()) { - Class transportHandlerClass; - try { - transportHandlerClass = it.next(); - } catch (Throwable e) { - LOGGER.log(Level.INFO, "Unable to load transport hander class. Error: " + e.getMessage(), e); - continue; - } - // try to load it to see if it is available - if (load(transportHandlerClass) != null) { - Handle handle = transportHandlerClass.getAnnotation(Handle.class); - if (handle != null) { - for (TransportType type : handle.value()) { - handlerClasses.put(type, transportHandlerClass); - } - } - } - } - } - - private static TransportHandler load(Class c) { - try { - Constructor ctor = c.getConstructor(); - return ctor.newInstance(); - } catch (InvocationTargetException e) { - LOGGER.log(Level.INFO, "Unable to load transport handler class " + c.getName() + ". Error: " + e.getTargetException().getMessage(), e.getTargetException()); - } catch (Exception e) { - LOGGER.log(Level.INFO, "Unable to load transport handler class " + c.getName() + ". Error: " + e.getMessage(), e); - } - return null; - } - - @Override - public Map> listAll() { - return handlerClasses; - } - -} diff --git a/core/src/main/java/com/glines/socketio/server/ClasspathTransportDiscovery.java b/core/src/main/java/com/glines/socketio/server/ClasspathTransportDiscovery.java deleted file mode 100644 index 62113fc..0000000 --- a/core/src/main/java/com/glines/socketio/server/ClasspathTransportDiscovery.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.server; - -import com.glines.socketio.util.DefaultLoader; -import com.glines.socketio.util.ServiceClassLoader; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * @author Mathieu Carbou - */ -public final class ClasspathTransportDiscovery implements TransportDiscovery { - - private static final Logger LOGGER = Logger.getLogger(ClasspathTransportDiscovery.class.getName()); - - @Override - public Iterable discover() { - List transports = new LinkedList(); - // discover transports lazily - ServiceClassLoader serviceClassLoader = ServiceClassLoader.load( - Transport.class, - new DefaultLoader(Thread.currentThread().getContextClassLoader())); - Iterator> it = serviceClassLoader.iterator(); - while (it.hasNext()) { - Class transportClass; - try { - transportClass = it.next(); - } catch (Throwable e) { - LOGGER.log(Level.INFO, "Unable to load transport class: Error: " + e.getMessage(), e); - continue; - } - try { - Constructor ctor = transportClass.getConstructor(); - transports.add(ctor.newInstance()); - } catch (InvocationTargetException e) { - LOGGER.log(Level.INFO, "Unable to load transport class " + transportClass.getName() + ". Error: " + e.getTargetException().getMessage(), e.getTargetException()); - } catch (Throwable e) { - LOGGER.log(Level.INFO, "Unable to load transport class " + transportClass.getName() + ". Error: " + e.getMessage(), e); - } - } - return transports; - } -} diff --git a/core/src/main/java/com/glines/socketio/server/ConnectableTransportHandler.java b/core/src/main/java/com/glines/socketio/server/ConnectableTransportHandler.java deleted file mode 100644 index 14c1f94..0000000 --- a/core/src/main/java/com/glines/socketio/server/ConnectableTransportHandler.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.server; - -import com.glines.socketio.server.transport.DataHandler; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * @author Mathieu Carbou (mathieu.carbou@gmail.com) - */ -public interface ConnectableTransportHandler { - void connect(HttpServletRequest request, HttpServletResponse response) throws IOException; - void setDataHandler(DataHandler dataHandler); -} diff --git a/core/src/main/java/com/glines/socketio/server/TransportDiscovery.java b/core/src/main/java/com/glines/socketio/server/TransportDiscovery.java deleted file mode 100644 index 0dd13a6..0000000 --- a/core/src/main/java/com/glines/socketio/server/TransportDiscovery.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.server; - -/** - * @author Mathieu Carbou - */ -public interface TransportDiscovery { - Iterable discover(); -} diff --git a/core/src/main/java/com/glines/socketio/server/TransportHandlerProvider.java b/core/src/main/java/com/glines/socketio/server/TransportHandlerProvider.java deleted file mode 100644 index dbc0089..0000000 --- a/core/src/main/java/com/glines/socketio/server/TransportHandlerProvider.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.server; - -import java.util.Collection; -import java.util.Map; - -/** - * @author Mathieu Carbou (mathieu.carbou@gmail.com) - */ -public interface TransportHandlerProvider { - TransportHandler get(Class handlerType, TransportType transportType); - boolean isSupported(TransportType type); - - Map> listAll(); - - void init(); -} diff --git a/core/src/main/java/com/glines/socketio/server/TransportInitializationException.java b/core/src/main/java/com/glines/socketio/server/TransportInitializationException.java deleted file mode 100644 index 56a545b..0000000 --- a/core/src/main/java/com/glines/socketio/server/TransportInitializationException.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.server; - -/** - * @author Mathieu Carbou - */ -public class TransportInitializationException extends RuntimeException { - - private static final long serialVersionUID = -5971560873659475024L; - - public TransportInitializationException(Throwable cause) { - super(cause); - } - - public TransportInitializationException(String message) { - super(message); - } - - public TransportInitializationException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/core/src/main/java/com/glines/socketio/server/transport/DataHandler.java b/core/src/main/java/com/glines/socketio/server/transport/DataHandler.java deleted file mode 100644 index f6a4e80..0000000 --- a/core/src/main/java/com/glines/socketio/server/transport/DataHandler.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.server.transport; - -import com.glines.socketio.server.SocketIOConfig; - -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * @author Mathieu Carbou - */ -public interface DataHandler { - - void init(SocketIOConfig config); - - boolean isConnectionPersistent(); - - void onStartSend(HttpServletResponse response) throws IOException; - - void onWriteData(ServletResponse response, String data) throws IOException; - - void onFinishSend(ServletResponse response) throws IOException; - - void onConnect(HttpServletRequest request, HttpServletResponse response) throws IOException; - -} diff --git a/core/src/main/java/com/glines/socketio/server/transport/HTMLFileDataHandler.java b/core/src/main/java/com/glines/socketio/server/transport/HTMLFileDataHandler.java deleted file mode 100644 index 3666daf..0000000 --- a/core/src/main/java/com/glines/socketio/server/transport/HTMLFileDataHandler.java +++ /dev/null @@ -1,96 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.server.transport; - -import com.glines.socketio.server.SocketIOFrame; -import com.glines.socketio.server.SocketIOSession; -import com.glines.socketio.util.JSON; - -import javax.servlet.ServletOutputStream; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Arrays; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * @author Mathieu Carbou - */ -final class HTMLFileDataHandler extends AbstractDataHandler { - - private static final Logger LOGGER = Logger.getLogger(HTMLFileDataHandler.class.getName()); - private static final long DEFAULT_HEARTBEAT_DELAY = 15 * 1000; - - private final SocketIOSession session; - - private long hearbeat; - - HTMLFileDataHandler(SocketIOSession session) { - this.session = session; - } - - @Override - protected void init() { - this.hearbeat = getConfig().getHeartbeatDelay(DEFAULT_HEARTBEAT_DELAY); - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.fine(getConfig().getNamespace() + " data handler configuration:\n" + - " - heartbeatDelay=" + hearbeat); - } - - @Override - public boolean isConnectionPersistent() { - return true; - } - - @Override - public void onStartSend(HttpServletResponse response) throws IOException { - response.setContentType("text/html"); - response.setHeader("Connection", "keep-alive"); - response.setHeader("Transfer-Encoding", "chunked"); - char[] spaces = new char[244]; - Arrays.fill(spaces, ' '); - ServletOutputStream os = response.getOutputStream(); - os.print("" + new String(spaces)); - response.flushBuffer(); - } - - @Override - public void onWriteData(ServletResponse response, String data) throws IOException { - response.getOutputStream().print(""); - response.flushBuffer(); - } - - @Override - public void onFinishSend(ServletResponse response) throws IOException { - } - - @Override - public void onConnect(HttpServletRequest request, HttpServletResponse response) throws IOException { - onStartSend(response); - onWriteData(response, SocketIOFrame.encode(SocketIOFrame.FrameType.CONNECT, session.getSessionId())); - } -} diff --git a/core/src/main/java/com/glines/socketio/server/transport/HTMLFileTransport.java b/core/src/main/java/com/glines/socketio/server/transport/HTMLFileTransport.java deleted file mode 100644 index dd0b4c7..0000000 --- a/core/src/main/java/com/glines/socketio/server/transport/HTMLFileTransport.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.server.transport; - -import com.glines.socketio.server.SocketIOSession; -import com.glines.socketio.server.TransportType; - -public class HTMLFileTransport extends AbstractHttpTransport { - @Override - public TransportType getType() { - return TransportType.HTML_FILE; - } - - @Override - protected DataHandler newDataHandler(SocketIOSession session) { - return new HTMLFileDataHandler(session); - } -} diff --git a/core/src/main/java/com/glines/socketio/server/transport/JSONPPollingDataHandler.java b/core/src/main/java/com/glines/socketio/server/transport/JSONPPollingDataHandler.java deleted file mode 100644 index 64adc9f..0000000 --- a/core/src/main/java/com/glines/socketio/server/transport/JSONPPollingDataHandler.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.server.transport; - -import com.glines.socketio.server.SocketIOFrame; -import com.glines.socketio.server.SocketIOSession; - -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * @author Mathieu Carbou - */ -final class JSONPPollingDataHandler extends AbstractDataHandler { - - /** - * For non persistent connection transports, this is the amount of time to wait - * for messages before returning empty results. - */ - private static final long DEFAULT_TIMEOUT = 20 * 1000; - private static final String FRAME_ID = JSONPPollingDataHandler.class.getName() + "FRAME_ID"; - private static final Logger LOGGER = Logger.getLogger(JSONPPollingDataHandler.class.getName()); - - private final SocketIOSession session; - private long timeout; - - JSONPPollingDataHandler(SocketIOSession session) { - this.session = session; - } - - @Override - protected void init() { - this.timeout = getConfig().getTimeout(DEFAULT_TIMEOUT); - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.fine(getConfig().getNamespace() + " data handler configuration:\n" + - " - timeout=" + timeout); - } - - @Override - public boolean isConnectionPersistent() { - return false; - } - - @Override - public void onStartSend(HttpServletResponse response) throws IOException { - response.setContentType("text/javascript; charset=UTF-8"); - } - - @Override - public void onWriteData(ServletResponse response, String data) throws IOException { - response.getOutputStream().print("io.j[" + session.getAttribute(FRAME_ID) + "]('"); - response.getOutputStream().print(data); - response.getOutputStream().print("');"); - } - - @Override - public void onFinishSend(ServletResponse response) throws IOException { - response.flushBuffer(); - } - - @Override - public void onConnect(HttpServletRequest request, HttpServletResponse response) throws IOException { - String query = request.getQueryString(); - String[] parts = query.split("&"); - int frameId = 0; - for (String part : parts) { - if (part.startsWith("i=")) { - try { - frameId = Integer.parseInt(part.substring(2)); - break; - } catch (NumberFormatException e) { } - } - } - session.setAttribute(FRAME_ID, frameId); - onStartSend(response); - onWriteData(response, SocketIOFrame.encode(SocketIOFrame.FrameType.CONNECT, session.getSessionId())); - } -} diff --git a/core/src/main/java/com/glines/socketio/server/transport/JSONPPollingTransport.java b/core/src/main/java/com/glines/socketio/server/transport/JSONPPollingTransport.java deleted file mode 100644 index 47dedeb..0000000 --- a/core/src/main/java/com/glines/socketio/server/transport/JSONPPollingTransport.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.server.transport; - -import com.glines.socketio.server.SocketIOSession; -import com.glines.socketio.server.TransportType; - -public class JSONPPollingTransport extends AbstractHttpTransport { - @Override - public TransportType getType() { - return TransportType.JSONP_POLLING; - } - - @Override - protected DataHandler newDataHandler(SocketIOSession session) { - return new JSONPPollingDataHandler(session); - } -} diff --git a/core/src/main/java/com/glines/socketio/server/transport/XHRMultipartDataHandler.java b/core/src/main/java/com/glines/socketio/server/transport/XHRMultipartDataHandler.java deleted file mode 100644 index 1352521..0000000 --- a/core/src/main/java/com/glines/socketio/server/transport/XHRMultipartDataHandler.java +++ /dev/null @@ -1,108 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.server.transport; - -import com.glines.socketio.server.SocketIOFrame; -import com.glines.socketio.server.SocketIOSession; -import com.glines.socketio.util.Web; - -import javax.servlet.ServletOutputStream; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * @author Mathieu Carbou - */ -final class XHRMultipartDataHandler extends AbstractDataHandler { - - private static final Logger LOGGER = Logger.getLogger(XHRMultipartDataHandler.class.getName()); - private static final int MULTIPART_BOUNDARY_LENGTH = 20; - private static final long DEFAULT_HEARTBEAT_DELAY = 15 * 1000; - - private final SocketIOSession session; - - private long hearbeatDelay; - - private final String contentType; - private final String boundarySeperator; - - XHRMultipartDataHandler(SocketIOSession session) { - this.session = session; - String boundary = Web.generateRandomString(MULTIPART_BOUNDARY_LENGTH); - boundarySeperator = "--" + boundary; - contentType = "multipart/x-mixed-replace;boundary=\"" + boundary + "\""; - } - - @Override - protected void init() { - this.hearbeatDelay = getConfig().getHeartbeatDelay(DEFAULT_HEARTBEAT_DELAY); - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.fine(getConfig().getNamespace() + " data handler configuration:\n" + - " - heartbeatDelay=" + hearbeatDelay); - } - - @Override - public boolean isConnectionPersistent() { - return true; - } - - @Override - public void onStartSend(HttpServletResponse response) throws IOException { - response.setContentType(contentType); - response.setHeader("Connection", "keep-alive"); - ServletOutputStream os = response.getOutputStream(); - os.print(boundarySeperator); - response.flushBuffer(); - } - - @Override - public void onWriteData(ServletResponse response, String data) throws IOException { - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, "Session[" + session.getSessionId() + "]: writeData(START): " + data); - ServletOutputStream os = response.getOutputStream(); - os.println("Content-Type: text/plain; charset=utf-8"); - os.println(); - os.write(data.getBytes()); - os.println(); - os.println(boundarySeperator); - response.flushBuffer(); - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, "Session[" + session.getSessionId() + "]: writeData(END): " + data); - } - - @Override - public void onFinishSend(ServletResponse response) throws IOException { - } - - @Override - public void onConnect(HttpServletRequest request, HttpServletResponse response) throws IOException { - onStartSend(response); - onWriteData(response, SocketIOFrame.encode(SocketIOFrame.FrameType.CONNECT, session.getSessionId())); - } -} diff --git a/core/src/main/java/com/glines/socketio/server/transport/XHRMultipartTransport.java b/core/src/main/java/com/glines/socketio/server/transport/XHRMultipartTransport.java deleted file mode 100644 index 354166c..0000000 --- a/core/src/main/java/com/glines/socketio/server/transport/XHRMultipartTransport.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.server.transport; - -import com.glines.socketio.server.SocketIOSession; -import com.glines.socketio.server.TransportType; - -public class XHRMultipartTransport extends AbstractHttpTransport { - @Override - public TransportType getType() { - return TransportType.XHR_MULTIPART; - } - - @Override - protected DataHandler newDataHandler(SocketIOSession session) { - return new XHRMultipartDataHandler(session); - } -} diff --git a/core/src/main/java/com/glines/socketio/server/transport/XHRPollingDataHandler.java b/core/src/main/java/com/glines/socketio/server/transport/XHRPollingDataHandler.java deleted file mode 100644 index a457dd9..0000000 --- a/core/src/main/java/com/glines/socketio/server/transport/XHRPollingDataHandler.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.server.transport; - -import com.glines.socketio.server.SocketIOFrame; -import com.glines.socketio.server.SocketIOSession; - -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * @author Mathieu Carbou - */ -final class XHRPollingDataHandler extends AbstractDataHandler { - - /** - * For non persistent connection transports, this is the amount of time to wait - * for messages before returning empty results. - */ - private static final long DEFAULT_TIMEOUT = 20 * 1000; - private static final Logger LOGGER = Logger.getLogger(XHRPollingDataHandler.class.getName()); - - private final SocketIOSession session; - private long timeout; - - XHRPollingDataHandler(SocketIOSession session) { - this.session = session; - } - - @Override - protected void init() { - this.timeout = getConfig().getTimeout(DEFAULT_TIMEOUT); - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.fine(getConfig().getNamespace() + " data handler configuration:\n" + - " - timeout=" + timeout); - } - - @Override - public boolean isConnectionPersistent() { - return false; - } - - @Override - public void onStartSend(HttpServletResponse response) throws IOException { - response.setContentType("text/plain; charset=UTF-8"); - } - - @Override - public void onWriteData(ServletResponse response, String data) throws IOException { - response.getOutputStream().print(data); - response.flushBuffer(); - } - - @Override - public void onFinishSend(ServletResponse response) throws IOException { - } - - @Override - public void onConnect(HttpServletRequest request, HttpServletResponse response) throws IOException { - onStartSend(response); - onWriteData(response, SocketIOFrame.encode(SocketIOFrame.FrameType.CONNECT, session.getSessionId())); - } -} diff --git a/core/src/main/java/com/glines/socketio/server/transport/XHRPollingTransport.java b/core/src/main/java/com/glines/socketio/server/transport/XHRPollingTransport.java deleted file mode 100644 index 6021519..0000000 --- a/core/src/main/java/com/glines/socketio/server/transport/XHRPollingTransport.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.server.transport; - -import com.glines.socketio.server.SocketIOSession; -import com.glines.socketio.server.TransportType; - -public class XHRPollingTransport extends AbstractHttpTransport { - @Override - public TransportType getType() { - return TransportType.XHR_POLLING; - } - - @Override - protected DataHandler newDataHandler(SocketIOSession session) { - return new XHRPollingDataHandler(session); - } -} diff --git a/core/src/main/resources/META-INF/services/com.glines.socketio.server.Transport b/core/src/main/resources/META-INF/services/com.glines.socketio.server.Transport deleted file mode 100644 index aa6896e..0000000 --- a/core/src/main/resources/META-INF/services/com.glines.socketio.server.Transport +++ /dev/null @@ -1,5 +0,0 @@ -com.glines.socketio.server.transport.FlashSocketTransport -com.glines.socketio.server.transport.HTMLFileTransport -com.glines.socketio.server.transport.XHRMultipartTransport -com.glines.socketio.server.transport.XHRPollingTransport -com.glines.socketio.server.transport.JSONPPollingTransport diff --git a/core/src/main/resources/com/glines/socketio/WebSocketMain.swf b/core/src/main/resources/com/codeminders/socketio/WebSocketMain.swf similarity index 100% rename from core/src/main/resources/com/glines/socketio/WebSocketMain.swf rename to core/src/main/resources/com/codeminders/socketio/WebSocketMain.swf diff --git a/core/src/main/resources/com/codeminders/socketio/socket.io.js b/core/src/main/resources/com/codeminders/socketio/socket.io.js new file mode 100644 index 0000000..782db4c --- /dev/null +++ b/core/src/main/resources/com/codeminders/socketio/socket.io.js @@ -0,0 +1,4144 @@ +! function(e) { + if ("object" == typeof exports && "undefined" != typeof module) module.exports = e(); + else if ("function" == typeof define && define.amd) define([], e); + else { + var f; + "undefined" != typeof window ? f = window : "undefined" != typeof global ? f = global : "undefined" != typeof self && (f = self), f.io = e() + } +}(function() { + var define, module, exports; + return function e(t, n, r) { + function s(o, u) { + if (!n[o]) { + if (!t[o]) { + var a = typeof require == "function" && require; + if (!u && a) return a(o, !0); + if (i) return i(o, !0); + throw new Error("Cannot find module '" + o + "'") + } + var f = n[o] = { + exports: {} + }; + t[o][0].call(f.exports, function(e) { + var n = t[o][1][e]; + return s(n ? n : e) + }, f, f.exports, e, t, n, r) + } + return n[o].exports + } + var i = typeof require == "function" && require; + for (var o = 0; o < r.length; o++) s(r[o]); + return s + }({ + 1: [function(_dereq_, module, exports) { + module.exports = _dereq_("./lib/") + }, { + "./lib/": 2 + }], + 2: [function(_dereq_, module, exports) { + var url = _dereq_("./url"); + var parser = _dereq_("socket.io-parser"); + var Manager = _dereq_("./manager"); + var debug = _dereq_("debug")("socket.io-client"); + module.exports = exports = lookup; + var cache = exports.managers = {}; + + function lookup(uri, opts) { + if (typeof uri == "object") { + opts = uri; + uri = undefined + } + opts = opts || {}; + var parsed = url(uri); + var source = parsed.source; + var id = parsed.id; + var io; + if (opts.forceNew || opts["force new connection"] || false === opts.multiplex) { + debug("ignoring socket cache for %s", source); + io = Manager(source, opts) + } else { + if (!cache[id]) { + debug("new io instance for %s", source); + cache[id] = Manager(source, opts) + } + io = cache[id] + } + return io.socket(parsed.path) + } + exports.protocol = parser.protocol; + exports.connect = lookup; + exports.Manager = _dereq_("./manager"); + exports.Socket = _dereq_("./socket") + }, { + "./manager": 3, + "./socket": 5, + "./url": 6, + debug: 10, + "socket.io-parser": 44 + }], + 3: [function(_dereq_, module, exports) { + var url = _dereq_("./url"); + var eio = _dereq_("engine.io-client"); + var Socket = _dereq_("./socket"); + var Emitter = _dereq_("component-emitter"); + var parser = _dereq_("socket.io-parser"); + var on = _dereq_("./on"); + var bind = _dereq_("component-bind"); + var object = _dereq_("object-component"); + var debug = _dereq_("debug")("socket.io-client:manager"); + var indexOf = _dereq_("indexof"); + var Backoff = _dereq_("backo2"); + module.exports = Manager; + + function Manager(uri, opts) { + if (!(this instanceof Manager)) return new Manager(uri, opts); + if (uri && "object" == typeof uri) { + opts = uri; + uri = undefined + } + opts = opts || {}; + opts.path = opts.path || "/socket.io"; + this.nsps = {}; + this.subs = []; + this.opts = opts; + this.reconnection(opts.reconnection !== false); + this.reconnectionAttempts(opts.reconnectionAttempts || Infinity); + this.reconnectionDelay(opts.reconnectionDelay || 1e3); + this.reconnectionDelayMax(opts.reconnectionDelayMax || 5e3); + this.randomizationFactor(opts.randomizationFactor || .5); + this.backoff = new Backoff({ + min: this.reconnectionDelay(), + max: this.reconnectionDelayMax(), + jitter: this.randomizationFactor() + }); + this.timeout(null == opts.timeout ? 2e4 : opts.timeout); + this.readyState = "closed"; + this.uri = uri; + this.connected = []; + this.encoding = false; + this.packetBuffer = []; + this.encoder = new parser.Encoder; + this.decoder = new parser.Decoder; + this.autoConnect = opts.autoConnect !== false; + if (this.autoConnect) this.open() + } + Manager.prototype.emitAll = function() { + this.emit.apply(this, arguments); + for (var nsp in this.nsps) { + this.nsps[nsp].emit.apply(this.nsps[nsp], arguments) + } + }; + Manager.prototype.updateSocketIds = function() { + for (var nsp in this.nsps) { + this.nsps[nsp].id = this.engine.id + } + }; + Emitter(Manager.prototype); + Manager.prototype.reconnection = function(v) { + if (!arguments.length) return this._reconnection; + this._reconnection = !!v; + return this + }; + Manager.prototype.reconnectionAttempts = function(v) { + if (!arguments.length) return this._reconnectionAttempts; + this._reconnectionAttempts = v; + return this + }; + Manager.prototype.reconnectionDelay = function(v) { + if (!arguments.length) return this._reconnectionDelay; + this._reconnectionDelay = v; + this.backoff && this.backoff.setMin(v); + return this + }; + Manager.prototype.randomizationFactor = function(v) { + if (!arguments.length) return this._randomizationFactor; + this._randomizationFactor = v; + this.backoff && this.backoff.setJitter(v); + return this + }; + Manager.prototype.reconnectionDelayMax = function(v) { + if (!arguments.length) return this._reconnectionDelayMax; + this._reconnectionDelayMax = v; + this.backoff && this.backoff.setMax(v); + return this + }; + Manager.prototype.timeout = function(v) { + if (!arguments.length) return this._timeout; + this._timeout = v; + return this + }; + Manager.prototype.maybeReconnectOnOpen = function() { + if (!this.reconnecting && this._reconnection && this.backoff.attempts === 0) { + this.reconnect() + } + }; + Manager.prototype.open = Manager.prototype.connect = function(fn) { + debug("readyState %s", this.readyState); + if (~this.readyState.indexOf("open")) return this; + debug("opening %s", this.uri); + this.engine = eio(this.uri, this.opts); + var socket = this.engine; + var self = this; + this.readyState = "opening"; + this.skipReconnect = false; + var openSub = on(socket, "open", function() { + self.onopen(); + fn && fn() + }); + var errorSub = on(socket, "error", function(data) { + debug("connect_error"); + self.cleanup(); + self.readyState = "closed"; + self.emitAll("connect_error", data); + if (fn) { + var err = new Error("Connection error"); + err.data = data; + fn(err) + } else { + self.maybeReconnectOnOpen() + } + }); + if (false !== this._timeout) { + var timeout = this._timeout; + debug("connect attempt will timeout after %d", timeout); + var timer = setTimeout(function() { + debug("connect attempt timed out after %d", timeout); + openSub.destroy(); + socket.close(); + socket.emit("error", "timeout"); + self.emitAll("connect_timeout", timeout) + }, timeout); + this.subs.push({ + destroy: function() { + clearTimeout(timer) + } + }) + } + this.subs.push(openSub); + this.subs.push(errorSub); + return this + }; + Manager.prototype.onopen = function() { + debug("open"); + this.cleanup(); + this.readyState = "open"; + this.emit("open"); + var socket = this.engine; + this.subs.push(on(socket, "data", bind(this, "ondata"))); + this.subs.push(on(this.decoder, "decoded", bind(this, "ondecoded"))); + this.subs.push(on(socket, "error", bind(this, "onerror"))); + this.subs.push(on(socket, "close", bind(this, "onclose"))) + }; + Manager.prototype.ondata = function(data) { + this.decoder.add(data) + }; + Manager.prototype.ondecoded = function(packet) { + this.emit("packet", packet) + }; + Manager.prototype.onerror = function(err) { + debug("error", err); + this.emitAll("error", err) + }; + Manager.prototype.socket = function(nsp) { + var socket = this.nsps[nsp]; + if (!socket) { + socket = new Socket(this, nsp); + this.nsps[nsp] = socket; + var self = this; + socket.on("connect", function() { + socket.id = self.engine.id; + if (!~indexOf(self.connected, socket)) { + self.connected.push(socket) + } + }) + } + return socket + }; + Manager.prototype.destroy = function(socket) { + var index = indexOf(this.connected, socket); + if (~index) this.connected.splice(index, 1); + if (this.connected.length) return; + this.close() + }; + Manager.prototype.packet = function(packet) { + debug("writing packet %j", packet); + var self = this; + if (!self.encoding) { + self.encoding = true; + this.encoder.encode(packet, function(encodedPackets) { + for (var i = 0; i < encodedPackets.length; i++) { + self.engine.write(encodedPackets[i]) + } + self.encoding = false; + self.processPacketQueue() + }) + } else { + self.packetBuffer.push(packet) + } + }; + Manager.prototype.processPacketQueue = function() { + if (this.packetBuffer.length > 0 && !this.encoding) { + var pack = this.packetBuffer.shift(); + this.packet(pack) + } + }; + Manager.prototype.cleanup = function() { + var sub; + while (sub = this.subs.shift()) sub.destroy(); + this.packetBuffer = []; + this.encoding = false; + this.decoder.destroy() + }; + Manager.prototype.close = Manager.prototype.disconnect = function() { + this.skipReconnect = true; + this.backoff.reset(); + this.readyState = "closed"; + this.engine && this.engine.close() + }; + Manager.prototype.onclose = function(reason) { + debug("close"); + this.cleanup(); + this.backoff.reset(); + this.readyState = "closed"; + this.emit("close", reason); + if (this._reconnection && !this.skipReconnect) { + this.reconnect() + } + }; + Manager.prototype.reconnect = function() { + if (this.reconnecting || this.skipReconnect) return this; + var self = this; + if (this.backoff.attempts >= this._reconnectionAttempts) { + debug("reconnect failed"); + this.backoff.reset(); + this.emitAll("reconnect_failed"); + this.reconnecting = false + } else { + var delay = this.backoff.duration(); + debug("will wait %dms before reconnect attempt", delay); + this.reconnecting = true; + var timer = setTimeout(function() { + if (self.skipReconnect) return; + debug("attempting reconnect"); + self.emitAll("reconnect_attempt", self.backoff.attempts); + self.emitAll("reconnecting", self.backoff.attempts); + if (self.skipReconnect) return; + self.open(function(err) { + if (err) { + debug("reconnect attempt error"); + self.reconnecting = false; + self.reconnect(); + self.emitAll("reconnect_error", err.data) + } else { + debug("reconnect success"); + self.onreconnect() + } + }) + }, delay); + this.subs.push({ + destroy: function() { + clearTimeout(timer) + } + }) + } + }; + Manager.prototype.onreconnect = function() { + var attempt = this.backoff.attempts; + this.reconnecting = false; + this.backoff.reset(); + this.updateSocketIds(); + this.emitAll("reconnect", attempt) + } + }, { + "./on": 4, + "./socket": 5, + "./url": 6, + backo2: 7, + "component-bind": 8, + "component-emitter": 9, + debug: 10, + "engine.io-client": 11, + indexof: 40, + "object-component": 41, + "socket.io-parser": 44 + }], + 4: [function(_dereq_, module, exports) { + module.exports = on; + + function on(obj, ev, fn) { + obj.on(ev, fn); + return { + destroy: function() { + obj.removeListener(ev, fn) + } + } + } + }, {}], + 5: [function(_dereq_, module, exports) { + var parser = _dereq_("socket.io-parser"); + var Emitter = _dereq_("component-emitter"); + var toArray = _dereq_("to-array"); + var on = _dereq_("./on"); + var bind = _dereq_("component-bind"); + var debug = _dereq_("debug")("socket.io-client:socket"); + var hasBin = _dereq_("has-binary"); + module.exports = exports = Socket; + var events = { + connect: 1, + connect_error: 1, + connect_timeout: 1, + disconnect: 1, + error: 1, + reconnect: 1, + reconnect_attempt: 1, + reconnect_failed: 1, + reconnect_error: 1, + reconnecting: 1 + }; + var emit = Emitter.prototype.emit; + + function Socket(io, nsp) { + this.io = io; + this.nsp = nsp; + this.json = this; + this.ids = 0; + this.acks = {}; + if (this.io.autoConnect) this.open(); + this.receiveBuffer = []; + this.sendBuffer = []; + this.connected = false; + this.disconnected = true + } + Emitter(Socket.prototype); + Socket.prototype.subEvents = function() { + if (this.subs) return; + var io = this.io; + this.subs = [on(io, "open", bind(this, "onopen")), on(io, "packet", bind(this, "onpacket")), on(io, "close", bind(this, "onclose"))] + }; + Socket.prototype.open = Socket.prototype.connect = function() { + if (this.connected) return this; + this.subEvents(); + this.io.open(); + if ("open" == this.io.readyState) this.onopen(); + return this + }; + Socket.prototype.send = function() { + var args = toArray(arguments); + args.unshift("message"); + this.emit.apply(this, args); + return this + }; + Socket.prototype.emit = function(ev) { + if (events.hasOwnProperty(ev)) { + emit.apply(this, arguments); + return this + } + var args = toArray(arguments); + var parserType = parser.EVENT; + if (hasBin(args)) { + parserType = parser.BINARY_EVENT + } + var packet = { + type: parserType, + data: args + }; + if ("function" == typeof args[args.length - 1]) { + debug("emitting packet with ack id %d", this.ids); + this.acks[this.ids] = args.pop(); + packet.id = this.ids++ + } + if (this.connected) { + this.packet(packet) + } else { + this.sendBuffer.push(packet) + } + return this + }; + Socket.prototype.packet = function(packet) { + packet.nsp = this.nsp; + this.io.packet(packet) + }; + Socket.prototype.onopen = function() { + debug("transport is open - connecting"); + if ("/" != this.nsp) { + this.packet({ + type: parser.CONNECT + }) + } + }; + Socket.prototype.onclose = function(reason) { + debug("close (%s)", reason); + this.connected = false; + this.disconnected = true; + delete this.id; + this.emit("disconnect", reason) + }; + Socket.prototype.onpacket = function(packet) { + if (packet.nsp != this.nsp) return; + switch (packet.type) { + case parser.CONNECT: + this.onconnect(); + break; + case parser.EVENT: + this.onevent(packet); + break; + case parser.BINARY_EVENT: + this.onevent(packet); + break; + case parser.ACK: + this.onack(packet); + break; + case parser.BINARY_ACK: + this.onack(packet); + break; + case parser.DISCONNECT: + this.ondisconnect(); + break; + case parser.ERROR: + this.emit("error", packet.data); + break + } + }; + Socket.prototype.onevent = function(packet) { + var args = packet.data || []; + debug("emitting event %j", args); + if (null != packet.id) { + debug("attaching ack callback to event"); + args.push(this.ack(packet.id)) + } + if (this.connected) { + emit.apply(this, args) + } else { + this.receiveBuffer.push(args) + } + }; + Socket.prototype.ack = function(id) { + var self = this; + var sent = false; + return function() { + if (sent) return; + sent = true; + var args = toArray(arguments); + debug("sending ack %j", args); + var type = hasBin(args) ? parser.BINARY_ACK : parser.ACK; + self.packet({ + type: type, + id: id, + data: args + }) + } + }; + Socket.prototype.onack = function(packet) { + debug("calling ack %s with %j", packet.id, packet.data); + var fn = this.acks[packet.id]; + fn.apply(this, packet.data); + delete this.acks[packet.id] + }; + Socket.prototype.onconnect = function() { + this.connected = true; + this.disconnected = false; + this.emit("connect"); + this.emitBuffered() + }; + Socket.prototype.emitBuffered = function() { + var i; + for (i = 0; i < this.receiveBuffer.length; i++) { + emit.apply(this, this.receiveBuffer[i]) + } + this.receiveBuffer = []; + for (i = 0; i < this.sendBuffer.length; i++) { + this.packet(this.sendBuffer[i]) + } + this.sendBuffer = [] + }; + Socket.prototype.ondisconnect = function() { + debug("server disconnect (%s)", this.nsp); + this.destroy(); + this.onclose("io server disconnect") + }; + Socket.prototype.destroy = function() { + if (this.subs) { + for (var i = 0; i < this.subs.length; i++) { + this.subs[i].destroy() + } + this.subs = null + } + this.io.destroy(this) + }; + Socket.prototype.close = Socket.prototype.disconnect = function() { + if (this.connected) { + debug("performing disconnect (%s)", this.nsp); + this.packet({ + type: parser.DISCONNECT + }) + } + this.destroy(); + if (this.connected) { + this.onclose("io client disconnect") + } + return this + } + }, { + "./on": 4, + "component-bind": 8, + "component-emitter": 9, + debug: 10, + "has-binary": 36, + "socket.io-parser": 44, + "to-array": 48 + }], + 6: [function(_dereq_, module, exports) { + (function(global) { + var parseuri = _dereq_("parseuri"); + var debug = _dereq_("debug")("socket.io-client:url"); + module.exports = url; + + function url(uri, loc) { + var obj = uri; + var loc = loc || global.location; + if (null == uri) uri = loc.protocol + "//" + loc.host; + if ("string" == typeof uri) { + if ("/" == uri.charAt(0)) { + if ("/" == uri.charAt(1)) { + uri = loc.protocol + uri + } else { + uri = loc.hostname + uri + } + } + if (!/^(https?|wss?):\/\//.test(uri)) { + debug("protocol-less url %s", uri); + if ("undefined" != typeof loc) { + uri = loc.protocol + "//" + uri + } else { + uri = "https://" + uri + } + } + debug("parse %s", uri); + obj = parseuri(uri) + } + if (!obj.port) { + if (/^(http|ws)$/.test(obj.protocol)) { + obj.port = "80" + } else if (/^(http|ws)s$/.test(obj.protocol)) { + obj.port = "443" + } + } + obj.path = obj.path || "/"; + obj.id = obj.protocol + "://" + obj.host + ":" + obj.port; + obj.href = obj.protocol + "://" + obj.host + (loc && loc.port == obj.port ? "" : ":" + obj.port); + return obj + } + }).call(this, typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) + }, { + debug: 10, + parseuri: 42 + }], + 7: [function(_dereq_, module, exports) { + module.exports = Backoff; + + function Backoff(opts) { + opts = opts || {}; + this.ms = opts.min || 100; + this.max = opts.max || 1e4; + this.factor = opts.factor || 2; + this.jitter = opts.jitter > 0 && opts.jitter <= 1 ? opts.jitter : 0; + this.attempts = 0 + } + Backoff.prototype.duration = function() { + var ms = this.ms * Math.pow(this.factor, this.attempts++); + if (this.jitter) { + var rand = Math.random(); + var deviation = Math.floor(rand * this.jitter * ms); + ms = (Math.floor(rand * 10) & 1) == 0 ? ms - deviation : ms + deviation + } + return Math.min(ms, this.max) | 0 + }; + Backoff.prototype.reset = function() { + this.attempts = 0 + }; + Backoff.prototype.setMin = function(min) { + this.ms = min + }; + Backoff.prototype.setMax = function(max) { + this.max = max + }; + Backoff.prototype.setJitter = function(jitter) { + this.jitter = jitter + } + }, {}], + 8: [function(_dereq_, module, exports) { + var slice = [].slice; + module.exports = function(obj, fn) { + if ("string" == typeof fn) fn = obj[fn]; + if ("function" != typeof fn) throw new Error("bind() requires a function"); + var args = slice.call(arguments, 2); + return function() { + return fn.apply(obj, args.concat(slice.call(arguments))) + } + } + }, {}], + 9: [function(_dereq_, module, exports) { + module.exports = Emitter; + + function Emitter(obj) { + if (obj) return mixin(obj) + } + + function mixin(obj) { + for (var key in Emitter.prototype) { + obj[key] = Emitter.prototype[key] + } + return obj + } + Emitter.prototype.on = Emitter.prototype.addEventListener = function(event, fn) { + this._callbacks = this._callbacks || {}; + (this._callbacks[event] = this._callbacks[event] || []).push(fn); + return this + }; + Emitter.prototype.once = function(event, fn) { + var self = this; + this._callbacks = this._callbacks || {}; + + function on() { + self.off(event, on); + fn.apply(this, arguments) + } + on.fn = fn; + this.on(event, on); + return this + }; + Emitter.prototype.off = Emitter.prototype.removeListener = Emitter.prototype.removeAllListeners = Emitter.prototype.removeEventListener = function(event, fn) { + this._callbacks = this._callbacks || {}; + if (0 == arguments.length) { + this._callbacks = {}; + return this + } + var callbacks = this._callbacks[event]; + if (!callbacks) return this; + if (1 == arguments.length) { + delete this._callbacks[event]; + return this + } + var cb; + for (var i = 0; i < callbacks.length; i++) { + cb = callbacks[i]; + if (cb === fn || cb.fn === fn) { + callbacks.splice(i, 1); + break + } + } + return this + }; + Emitter.prototype.emit = function(event) { + this._callbacks = this._callbacks || {}; + var args = [].slice.call(arguments, 1), + callbacks = this._callbacks[event]; + if (callbacks) { + callbacks = callbacks.slice(0); + for (var i = 0, len = callbacks.length; i < len; ++i) { + callbacks[i].apply(this, args) + } + } + return this + }; + Emitter.prototype.listeners = function(event) { + this._callbacks = this._callbacks || {}; + return this._callbacks[event] || [] + }; + Emitter.prototype.hasListeners = function(event) { + return !!this.listeners(event).length + } + }, {}], + 10: [function(_dereq_, module, exports) { + module.exports = debug; + + function debug(name) { + if (!debug.enabled(name)) return function() {}; + return function(fmt) { + fmt = coerce(fmt); + var curr = new Date; + var ms = curr - (debug[name] || curr); + debug[name] = curr; + fmt = name + " " + fmt + " +" + debug.humanize(ms); + window.console && console.log && Function.prototype.apply.call(console.log, console, arguments) + } + } + debug.names = []; + debug.skips = []; + debug.enable = function(name) { + try { + localStorage.debug = name + } catch (e) {} + var split = (name || "").split(/[\s,]+/), + len = split.length; + for (var i = 0; i < len; i++) { + name = split[i].replace("*", ".*?"); + if (name[0] === "-") { + debug.skips.push(new RegExp("^" + name.substr(1) + "$")) + } else { + debug.names.push(new RegExp("^" + name + "$")) + } + } + }; + debug.disable = function() { + debug.enable("") + }; + debug.humanize = function(ms) { + var sec = 1e3, + min = 60 * 1e3, + hour = 60 * min; + if (ms >= hour) return (ms / hour).toFixed(1) + "h"; + if (ms >= min) return (ms / min).toFixed(1) + "m"; + if (ms >= sec) return (ms / sec | 0) + "s"; + return ms + "ms" + }; + debug.enabled = function(name) { + for (var i = 0, len = debug.skips.length; i < len; i++) { + if (debug.skips[i].test(name)) { + return false + } + } + for (var i = 0, len = debug.names.length; i < len; i++) { + if (debug.names[i].test(name)) { + return true + } + } + return false + }; + + function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val + } + try { + if (window.localStorage) debug.enable(localStorage.debug) + } catch (e) {} + }, {}], + 11: [function(_dereq_, module, exports) { + module.exports = _dereq_("./lib/") + }, { + "./lib/": 12 + }], + 12: [function(_dereq_, module, exports) { + module.exports = _dereq_("./socket"); + module.exports.parser = _dereq_("engine.io-parser") + }, { + "./socket": 13, + "engine.io-parser": 25 + }], + 13: [function(_dereq_, module, exports) { + (function(global) { + var transports = _dereq_("./transports"); + var Emitter = _dereq_("component-emitter"); + var debug = _dereq_("debug")("engine.io-client:socket"); + var index = _dereq_("indexof"); + var parser = _dereq_("engine.io-parser"); + var parseuri = _dereq_("parseuri"); + var parsejson = _dereq_("parsejson"); + var parseqs = _dereq_("parseqs"); + module.exports = Socket; + + function noop() {} + + function Socket(uri, opts) { + if (!(this instanceof Socket)) return new Socket(uri, opts); + opts = opts || {}; + if (uri && "object" == typeof uri) { + opts = uri; + uri = null + } + if (uri) { + uri = parseuri(uri); + opts.host = uri.host; + opts.secure = uri.protocol == "https" || uri.protocol == "wss"; + opts.port = uri.port; + if (uri.query) opts.query = uri.query + } + this.secure = null != opts.secure ? opts.secure : global.location && "https:" == location.protocol; + if (opts.host) { + var pieces = opts.host.split(":"); + opts.hostname = pieces.shift(); + if (pieces.length) { + opts.port = pieces.pop() + } else if (!opts.port) { + opts.port = this.secure ? "443" : "80" + } + } + this.agent = opts.agent || false; + this.hostname = opts.hostname || (global.location ? location.hostname : "localhost"); + this.port = opts.port || (global.location && location.port ? location.port : this.secure ? 443 : 80); + this.query = opts.query || {}; + if ("string" == typeof this.query) this.query = parseqs.decode(this.query); + this.upgrade = false !== opts.upgrade; + this.path = (opts.path || "/engine.io").replace(/\/$/, "") + "/"; + this.forceJSONP = !!opts.forceJSONP; + this.jsonp = false !== opts.jsonp; + this.forceBase64 = !!opts.forceBase64; + this.enablesXDR = !!opts.enablesXDR; + this.timestampParam = opts.timestampParam || "t"; + this.timestampRequests = opts.timestampRequests; + this.transports = opts.transports || ["polling", "websocket"]; + this.readyState = ""; + this.writeBuffer = []; + this.callbackBuffer = []; + this.policyPort = opts.policyPort || 843; + this.rememberUpgrade = opts.rememberUpgrade || false; + this.binaryType = null; + this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades; + this.pfx = opts.pfx || null; + this.key = opts.key || null; + this.passphrase = opts.passphrase || null; + this.cert = opts.cert || null; + this.ca = opts.ca || null; + this.ciphers = opts.ciphers || null; + this.rejectUnauthorized = opts.rejectUnauthorized || null; + this.open() + } + Socket.priorWebsocketSuccess = false; + Emitter(Socket.prototype); + Socket.protocol = parser.protocol; + Socket.Socket = Socket; + Socket.Transport = _dereq_("./transport"); + Socket.transports = _dereq_("./transports"); + Socket.parser = _dereq_("engine.io-parser"); + Socket.prototype.createTransport = function(name) { + debug('creating transport "%s"', name); + var query = clone(this.query); + query.EIO = parser.protocol; + query.transport = name; + if (this.id) query.sid = this.id; + var transport = new transports[name]({ + agent: this.agent, + hostname: this.hostname, + port: this.port, + secure: this.secure, + path: this.path, + query: query, + forceJSONP: this.forceJSONP, + jsonp: this.jsonp, + forceBase64: this.forceBase64, + enablesXDR: this.enablesXDR, + timestampRequests: this.timestampRequests, + timestampParam: this.timestampParam, + policyPort: this.policyPort, + socket: this, + pfx: this.pfx, + key: this.key, + passphrase: this.passphrase, + cert: this.cert, + ca: this.ca, + ciphers: this.ciphers, + rejectUnauthorized: this.rejectUnauthorized + }); + return transport + }; + + function clone(obj) { + var o = {}; + for (var i in obj) { + if (obj.hasOwnProperty(i)) { + o[i] = obj[i] + } + } + return o + } + Socket.prototype.open = function() { + var transport; + if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf("websocket") != -1) { + transport = "websocket" + } else if (0 == this.transports.length) { + var self = this; + setTimeout(function() { + self.emit("error", "No transports available") + }, 0); + return + } else { + transport = this.transports[0] + } + this.readyState = "opening"; + var transport; + try { + transport = this.createTransport(transport) + } catch (e) { + this.transports.shift(); + this.open(); + return + } + transport.open(); + this.setTransport(transport) + }; + Socket.prototype.setTransport = function(transport) { + debug("setting transport %s", transport.name); + var self = this; + if (this.transport) { + debug("clearing existing transport %s", this.transport.name); + this.transport.removeAllListeners() + } + this.transport = transport; + transport.on("drain", function() { + self.onDrain() + }).on("packet", function(packet) { + self.onPacket(packet) + }).on("error", function(e) { + self.onError(e) + }).on("close", function() { + self.onClose("transport close") + }) + }; + Socket.prototype.probe = function(name) { + debug('probing transport "%s"', name); + var transport = this.createTransport(name, { + probe: 1 + }), + failed = false, + self = this; + Socket.priorWebsocketSuccess = false; + + function onTransportOpen() { + if (self.onlyBinaryUpgrades) { + var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary; + failed = failed || upgradeLosesBinary + } + if (failed) return; + debug('probe transport "%s" opened', name); + transport.send([{ + type: "ping", + data: "probe" + }]); + transport.once("packet", function(msg) { + if (failed) return; + if ("pong" == msg.type && "probe" == msg.data) { + debug('probe transport "%s" pong', name); + self.upgrading = true; + self.emit("upgrading", transport); + if (!transport) return; + Socket.priorWebsocketSuccess = "websocket" == transport.name; + debug('pausing current transport "%s"', self.transport.name); + self.transport.pause(function() { + if (failed) return; + if ("closed" == self.readyState) return; + debug("changing transport and sending upgrade packet"); + cleanup(); + self.setTransport(transport); + transport.send([{ + type: "upgrade" + }]); + self.emit("upgrade", transport); + transport = null; + self.upgrading = false; + self.flush() + }) + } else { + debug('probe transport "%s" failed', name); + var err = new Error("probe error"); + err.transport = transport.name; + self.emit("upgradeError", err) + } + }) + } + + function freezeTransport() { + if (failed) return; + failed = true; + cleanup(); + transport.close(); + transport = null + } + + function onerror(err) { + var error = new Error("probe error: " + err); + error.transport = transport.name; + freezeTransport(); + debug('probe transport "%s" failed because of error: %s', name, err); + self.emit("upgradeError", error) + } + + function onTransportClose() { + onerror("transport closed") + } + + function onclose() { + onerror("socket closed") + } + + function onupgrade(to) { + if (transport && to.name != transport.name) { + debug('"%s" works - aborting "%s"', to.name, transport.name); + freezeTransport() + } + } + + function cleanup() { + transport.removeListener("open", onTransportOpen); + transport.removeListener("error", onerror); + transport.removeListener("close", onTransportClose); + self.removeListener("close", onclose); + self.removeListener("upgrading", onupgrade) + } + transport.once("open", onTransportOpen); + transport.once("error", onerror); + transport.once("close", onTransportClose); + this.once("close", onclose); + this.once("upgrading", onupgrade); + transport.open() + }; + Socket.prototype.onOpen = function() { + debug("socket open"); + this.readyState = "open"; + Socket.priorWebsocketSuccess = "websocket" == this.transport.name; + this.emit("open"); + this.flush(); + if ("open" == this.readyState && this.upgrade && this.transport.pause) { + debug("starting upgrade probes"); + for (var i = 0, l = this.upgrades.length; i < l; i++) { + this.probe(this.upgrades[i]) + } + } + }; + Socket.prototype.onPacket = function(packet) { + if ("opening" == this.readyState || "open" == this.readyState) { + debug('socket receive: type "%s", data "%s"', packet.type, packet.data); + this.emit("packet", packet); + this.emit("heartbeat"); + switch (packet.type) { + case "open": + this.onHandshake(parsejson(packet.data)); + break; + case "pong": + this.setPing(); + break; + case "error": + var err = new Error("server error"); + err.code = packet.data; + this.emit("error", err); + break; + case "message": + this.emit("data", packet.data); + this.emit("message", packet.data); + break + } + } else { + debug('packet received with socket readyState "%s"', this.readyState) + } + }; + Socket.prototype.onHandshake = function(data) { + this.emit("handshake", data); + this.id = data.sid; + this.transport.query.sid = data.sid; + this.upgrades = this.filterUpgrades(data.upgrades); + this.pingInterval = data.pingInterval; + this.pingTimeout = data.pingTimeout; + this.onOpen(); + if ("closed" == this.readyState) return; + this.setPing(); + this.removeListener("heartbeat", this.onHeartbeat); + this.on("heartbeat", this.onHeartbeat) + }; + Socket.prototype.onHeartbeat = function(timeout) { + clearTimeout(this.pingTimeoutTimer); + var self = this; + self.pingTimeoutTimer = setTimeout(function() { + if ("closed" == self.readyState) return; + self.onClose("ping timeout") + }, timeout || self.pingInterval + self.pingTimeout) + }; + Socket.prototype.setPing = function() { + var self = this; + clearTimeout(self.pingIntervalTimer); + self.pingIntervalTimer = setTimeout(function() { + debug("writing ping packet - expecting pong within %sms", self.pingTimeout); + self.ping(); + self.onHeartbeat(self.pingTimeout) + }, self.pingInterval) + }; + Socket.prototype.ping = function() { + this.sendPacket("ping") + }; + Socket.prototype.onDrain = function() { + for (var i = 0; i < this.prevBufferLen; i++) { + if (this.callbackBuffer[i]) { + this.callbackBuffer[i]() + } + } + this.writeBuffer.splice(0, this.prevBufferLen); + this.callbackBuffer.splice(0, this.prevBufferLen); + this.prevBufferLen = 0; + if (this.writeBuffer.length == 0) { + this.emit("drain") + } else { + this.flush() + } + }; + Socket.prototype.flush = function() { + if ("closed" != this.readyState && this.transport.writable && !this.upgrading && this.writeBuffer.length) { + debug("flushing %d packets in socket", this.writeBuffer.length); + this.transport.send(this.writeBuffer); + this.prevBufferLen = this.writeBuffer.length; + this.emit("flush") + } + }; + Socket.prototype.write = Socket.prototype.send = function(msg, fn) { + this.sendPacket("message", msg, fn); + return this + }; + Socket.prototype.sendPacket = function(type, data, fn) { + if ("closing" == this.readyState || "closed" == this.readyState) { + return + } + var packet = { + type: type, + data: data + }; + this.emit("packetCreate", packet); + this.writeBuffer.push(packet); + this.callbackBuffer.push(fn); + this.flush() + }; + Socket.prototype.close = function() { + if ("opening" == this.readyState || "open" == this.readyState) { + this.readyState = "closing"; + var self = this; + + function close() { + self.onClose("forced close"); + debug("socket closing - telling transport to close"); + self.transport.close() + } + + function cleanupAndClose() { + self.removeListener("upgrade", cleanupAndClose); + self.removeListener("upgradeError", cleanupAndClose); + close() + } + + function waitForUpgrade() { + self.once("upgrade", cleanupAndClose); + self.once("upgradeError", cleanupAndClose) + } + if (this.writeBuffer.length) { + this.once("drain", function() { + if (this.upgrading) { + waitForUpgrade() + } else { + close() + } + }) + } else if (this.upgrading) { + waitForUpgrade() + } else { + close() + } + } + return this + }; + Socket.prototype.onError = function(err) { + debug("socket error %j", err); + Socket.priorWebsocketSuccess = false; + this.emit("error", err); + this.onClose("transport error", err) + }; + Socket.prototype.onClose = function(reason, desc) { + if ("opening" == this.readyState || "open" == this.readyState || "closing" == this.readyState) { + debug('socket close with reason: "%s"', reason); + var self = this; + clearTimeout(this.pingIntervalTimer); + clearTimeout(this.pingTimeoutTimer); + setTimeout(function() { + self.writeBuffer = []; + self.callbackBuffer = []; + self.prevBufferLen = 0 + }, 0); + this.transport.removeAllListeners("close"); + this.transport.close(); + this.transport.removeAllListeners(); + this.readyState = "closed"; + this.id = null; + this.emit("close", reason, desc) + } + }; + Socket.prototype.filterUpgrades = function(upgrades) { + var filteredUpgrades = []; + for (var i = 0, j = upgrades.length; i < j; i++) { + if (~index(this.transports, upgrades[i])) filteredUpgrades.push(upgrades[i]) + } + return filteredUpgrades + } + }).call(this, typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) + }, { + "./transport": 14, + "./transports": 15, + "component-emitter": 9, + debug: 22, + "engine.io-parser": 25, + indexof: 40, + parsejson: 32, + parseqs: 33, + parseuri: 34 + }], + 14: [function(_dereq_, module, exports) { + var parser = _dereq_("engine.io-parser"); + var Emitter = _dereq_("component-emitter"); + module.exports = Transport; + + function Transport(opts) { + this.path = opts.path; + this.hostname = opts.hostname; + this.port = opts.port; + this.secure = opts.secure; + this.query = opts.query; + this.timestampParam = opts.timestampParam; + this.timestampRequests = opts.timestampRequests; + this.readyState = ""; + this.agent = opts.agent || false; + this.socket = opts.socket; + this.enablesXDR = opts.enablesXDR; + this.pfx = opts.pfx; + this.key = opts.key; + this.passphrase = opts.passphrase; + this.cert = opts.cert; + this.ca = opts.ca; + this.ciphers = opts.ciphers; + this.rejectUnauthorized = opts.rejectUnauthorized + } + Emitter(Transport.prototype); + Transport.timestamps = 0; + Transport.prototype.onError = function(msg, desc) { + var err = new Error(msg); + err.type = "TransportError"; + err.description = desc; + this.emit("error", err); + return this + }; + Transport.prototype.open = function() { + if ("closed" == this.readyState || "" == this.readyState) { + this.readyState = "opening"; + this.doOpen() + } + return this + }; + Transport.prototype.close = function() { + if ("opening" == this.readyState || "open" == this.readyState) { + this.doClose(); + this.onClose() + } + return this + }; + Transport.prototype.send = function(packets) { + if ("open" == this.readyState) { + this.write(packets) + } else { + throw new Error("Transport not open") + } + }; + Transport.prototype.onOpen = function() { + this.readyState = "open"; + this.writable = true; + this.emit("open") + }; + Transport.prototype.onData = function(data) { + var packet = parser.decodePacket(data, this.socket.binaryType); + this.onPacket(packet) + }; + Transport.prototype.onPacket = function(packet) { + this.emit("packet", packet) + }; + Transport.prototype.onClose = function() { + this.readyState = "closed"; + this.emit("close") + } + }, { + "component-emitter": 9, + "engine.io-parser": 25 + }], + 15: [function(_dereq_, module, exports) { + (function(global) { + var XMLHttpRequest = _dereq_("xmlhttprequest"); + var XHR = _dereq_("./polling-xhr"); + var JSONP = _dereq_("./polling-jsonp"); + var websocket = _dereq_("./websocket"); + exports.polling = polling; + exports.websocket = websocket; + + function polling(opts) { + var xhr; + var xd = false; + var xs = false; + var jsonp = false !== opts.jsonp; + if (global.location) { + var isSSL = "https:" == location.protocol; + var port = location.port; + if (!port) { + port = isSSL ? 443 : 80 + } + xd = opts.hostname != location.hostname || port != opts.port; + xs = opts.secure != isSSL + } + opts.xdomain = xd; + opts.xscheme = xs; + xhr = new XMLHttpRequest(opts); + if ("open" in xhr && !opts.forceJSONP) { + return new XHR(opts) + } else { + if (!jsonp) throw new Error("JSONP disabled"); + return new JSONP(opts) + } + } + }).call(this, typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) + }, { + "./polling-jsonp": 16, + "./polling-xhr": 17, + "./websocket": 19, + xmlhttprequest: 20 + }], + 16: [function(_dereq_, module, exports) { + (function(global) { + var Polling = _dereq_("./polling"); + var inherit = _dereq_("component-inherit"); + module.exports = JSONPPolling; + var rNewline = /\n/g; + var rEscapedNewline = /\\n/g; + var callbacks; + var index = 0; + + function empty() {} + + function JSONPPolling(opts) { + Polling.call(this, opts); + this.query = this.query || {}; + if (!callbacks) { + if (!global.___eio) global.___eio = []; + callbacks = global.___eio + } + this.index = callbacks.length; + var self = this; + callbacks.push(function(msg) { + self.onData(msg) + }); + this.query.j = this.index; + if (global.document && global.addEventListener) { + global.addEventListener("beforeunload", function() { + if (self.script) self.script.onerror = empty + }, false) + } + } + inherit(JSONPPolling, Polling); + JSONPPolling.prototype.supportsBinary = false; + JSONPPolling.prototype.doClose = function() { + if (this.script) { + this.script.parentNode.removeChild(this.script); + this.script = null + } + if (this.form) { + this.form.parentNode.removeChild(this.form); + this.form = null; + this.iframe = null + } + Polling.prototype.doClose.call(this) + }; + JSONPPolling.prototype.doPoll = function() { + var self = this; + var script = document.createElement("script"); + if (this.script) { + this.script.parentNode.removeChild(this.script); + this.script = null + } + script.async = true; + script.src = this.uri(); + script.onerror = function(e) { + self.onError("jsonp poll error", e) + }; + var insertAt = document.getElementsByTagName("script")[0]; + insertAt.parentNode.insertBefore(script, insertAt); + this.script = script; + var isUAgecko = "undefined" != typeof navigator && /gecko/i.test(navigator.userAgent); + if (isUAgecko) { + setTimeout(function() { + var iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + document.body.removeChild(iframe) + }, 100) + } + }; + JSONPPolling.prototype.doWrite = function(data, fn) { + var self = this; + if (!this.form) { + var form = document.createElement("form"); + var area = document.createElement("textarea"); + var id = this.iframeId = "eio_iframe_" + this.index; + var iframe; + form.className = "socketio"; + form.style.position = "absolute"; + form.style.top = "-1000px"; + form.style.left = "-1000px"; + form.target = id; + form.method = "POST"; + form.setAttribute("accept-charset", "utf-8"); + area.name = "d"; + form.appendChild(area); + document.body.appendChild(form); + this.form = form; + this.area = area + } + this.form.action = this.uri(); + + function complete() { + initIframe(); + fn() + } + + function initIframe() { + if (self.iframe) { + try { + self.form.removeChild(self.iframe) + } catch (e) { + self.onError("jsonp polling iframe removal error", e) + } + } + try { + var html = ' - - - diff --git a/samples/chat-gwt/src/test/java/GWTChatServer.java b/samples/chat-gwt/src/test/java/GWTChatServer.java deleted file mode 100644 index 468f7ae..0000000 --- a/samples/chat-gwt/src/test/java/GWTChatServer.java +++ /dev/null @@ -1,109 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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. - */ - -import com.glines.socketio.sample.gwtchat.GWTChatSocketServlet; -import com.glines.socketio.server.transport.FlashSocketTransport; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.servlet.DefaultServlet; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -public class GWTChatServer { - private static class StaticServlet extends HttpServlet { - private static final long serialVersionUID = -5523626683621615592L; - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { - String path = req.getRequestURI(); - path = path.substring(req.getContextPath().length()); - if ("/gwtchat.html".equals(path)) { - resp.setContentType("text/html"); - InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/glines/socketio/examples/gwtchat/gwtchat.html"); - OutputStream os = resp.getOutputStream(); - byte[] data = new byte[8192]; - int nread = 0; - while ((nread = is.read(data)) > 0) { - os.write(data, 0, nread); - if (nread < data.length) { - break; - } - } - } - } - } - - /** - * @param args - * @throws Exception - */ - public static void main(String[] args) throws Exception { - String war_dir = "./war"; - String host = "localhost"; - int port = 8080; - - if (args.length > 0) { - war_dir = args[0]; - } - - if (args.length > 1) { - host = args[1]; - } - - if (args.length > 2) { - port = Integer.parseInt(args[2]); - } - - Server server = new Server(); - ServerConnector connector = new ServerConnector(server); - connector.setHost(host); - connector.setPort(port); - - server.addConnector(connector); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setResourceBase(war_dir); - ServletHolder holder = new ServletHolder(new GWTChatSocketServlet()); - holder.setInitParameter(FlashSocketTransport.PARAM_FLASHPOLICY_SERVER_HOST, host); - holder.setInitParameter(FlashSocketTransport.PARAM_FLASHPOLICY_DOMAIN, host); - holder.setInitParameter(FlashSocketTransport.PARAM_FLASHPOLICY_PORTS, ""+ port); - context.addServlet(holder, "/socket.io/*"); - context.addServlet(new ServletHolder(new StaticServlet()), "/gwtchat.html"); - ServletHolder defaultServlet = new ServletHolder(new DefaultServlet()); - context.addServlet(defaultServlet, "/com.glines.socketio.examples.gwtchat.GWTChat/*"); - - server.setHandler(context); - server.start(); - } - -} diff --git a/samples/chat-gwt/src/test/java/StartSampleChatGWT.java b/samples/chat-gwt/src/test/java/StartSampleChatGWT.java deleted file mode 100644 index 2a1db67..0000000 --- a/samples/chat-gwt/src/test/java/StartSampleChatGWT.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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. - */ -import org.testatoo.container.ContainerRunner; - -/** - * Just run this class and go to http://127.0.0.1:8080/ - * - * @author Mathieu Carbou - */ -public final class StartSampleChatGWT { - public static void main(String... args) throws Exception { - // ContainerRunner could be directly run from a launcher with argument. - // See ContainerRunner --help foe help - ContainerRunner.main("-container", "jetty"); - } -} diff --git a/samples/chat/pom.xml b/samples/chat/pom.xml index 3927d2e..3dc3798 100644 --- a/samples/chat/pom.xml +++ b/samples/chat/pom.xml @@ -5,9 +5,9 @@ 4.0.0 - com.glines.socketio.sample + com.codeminders.socketio.sample socketio-sample - 0.2.2 + 0.3 .. @@ -19,11 +19,11 @@ - com.glines.socketio + com.codeminders.socketio socketio-core - com.glines.socketio.extension + com.codeminders.socketio.extension socketio-jetty @@ -50,6 +50,7 @@ servlet-api provided + diff --git a/samples/chat/src/main/java/com/glines/socketio/sample/chat/ChatSocketServlet.java b/samples/chat/src/main/java/com/codeminders/socketio/sample/chat/ChatSocketServlet.java similarity index 93% rename from samples/chat/src/main/java/com/glines/socketio/sample/chat/ChatSocketServlet.java rename to samples/chat/src/main/java/com/codeminders/socketio/sample/chat/ChatSocketServlet.java index 6a2f4a2..b608b9f 100644 --- a/samples/chat/src/main/java/com/glines/socketio/sample/chat/ChatSocketServlet.java +++ b/samples/chat/src/main/java/com/codeminders/socketio/sample/chat/ChatSocketServlet.java @@ -22,15 +22,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.glines.socketio.sample.chat; +package com.codeminders.socketio.sample.chat; + +import com.codeminders.socketio.server.SocketIOFrame; +import com.codeminders.socketio.server.SocketIOInbound; +import com.codeminders.socketio.server.transport.jetty.JettySocketIOServlet; +import com.codeminders.socketio.util.JdkOverLog4j; +import com.codeminders.socketio.common.DisconnectReason; +import com.codeminders.socketio.common.SocketIOException; +import com.codeminders.socketio.server.SocketIOOutbound; -import com.glines.socketio.common.DisconnectReason; -import com.glines.socketio.common.SocketIOException; -import com.glines.socketio.server.SocketIOFrame; -import com.glines.socketio.server.SocketIOInbound; -import com.glines.socketio.server.SocketIOOutbound; -import com.glines.socketio.server.SocketIOServlet; -import com.glines.socketio.util.JdkOverLog4j; import com.google.gson.Gson; import javax.servlet.ServletConfig; @@ -42,7 +43,8 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicInteger; -public class ChatSocketServlet extends SocketIOServlet { +public class ChatSocketServlet extends JettySocketIOServlet +{ private static final long serialVersionUID = 1L; private AtomicInteger ids = new AtomicInteger(1); private Queue connections = new ConcurrentLinkedQueue(); @@ -136,7 +138,7 @@ public void onMessage(int messageType, String message) { } else { broadcast(SocketIOFrame.JSON_MESSAGE_TYPE, new Gson().toJson( Collections.singletonMap("message", - new String[]{sessionId.toString(), (String) message}))); + new String[]{sessionId.toString(), message}))); } } diff --git a/samples/chat/src/main/resources/META-INF/services/com.codeminders.socketio.server.Transport b/samples/chat/src/main/resources/META-INF/services/com.codeminders.socketio.server.Transport new file mode 100644 index 0000000..0521ac4 --- /dev/null +++ b/samples/chat/src/main/resources/META-INF/services/com.codeminders.socketio.server.Transport @@ -0,0 +1 @@ +com.codeminders.socketio.server.transport.jetty.JettyWebSocketTransport diff --git a/samples/chat/src/main/resources/META-INF/services/com.codeminders.socketio.server.TransportConnection b/samples/chat/src/main/resources/META-INF/services/com.codeminders.socketio.server.TransportConnection new file mode 100644 index 0000000..b1947ff --- /dev/null +++ b/samples/chat/src/main/resources/META-INF/services/com.codeminders.socketio.server.TransportConnection @@ -0,0 +1,2 @@ +com.codeminders.socketio.server.transport.jetty.JettyWebSocketTransportConnection +com.codeminders.socketio.server.transport.jetty.JettyContinuationTransportConnection diff --git a/samples/chat/src/main/resources/log4j.xml b/samples/chat/src/main/resources/log4j.xml index d33d3d6..185a35a 100644 --- a/samples/chat/src/main/resources/log4j.xml +++ b/samples/chat/src/main/resources/log4j.xml @@ -63,7 +63,7 @@ - + diff --git a/samples/chat/src/main/webapp/META-INF/context.xml b/samples/chat/src/main/resources/webapp/META-INF/context.xml similarity index 100% rename from samples/chat/src/main/webapp/META-INF/context.xml rename to samples/chat/src/main/resources/webapp/META-INF/context.xml diff --git a/samples/chat/src/main/webapp/WEB-INF/web.xml b/samples/chat/src/main/resources/webapp/WEB-INF/web.xml similarity index 95% rename from samples/chat/src/main/webapp/WEB-INF/web.xml rename to samples/chat/src/main/resources/webapp/WEB-INF/web.xml index ff139b8..fd27fcd 100644 --- a/samples/chat/src/main/webapp/WEB-INF/web.xml +++ b/samples/chat/src/main/resources/webapp/WEB-INF/web.xml @@ -31,7 +31,7 @@ ChatSocketServlet - com.glines.socketio.sample.chat.ChatSocketServlet + com.codeminders.socketio.sample.chat.ChatSocketServlet diff --git a/samples/chat/src/main/webapp/chat.html b/samples/chat/src/main/resources/webapp/chat.html similarity index 97% rename from samples/chat/src/main/webapp/chat.html rename to samples/chat/src/main/resources/webapp/chat.html index b612e7a..73ac40f 100644 --- a/samples/chat/src/main/webapp/chat.html +++ b/samples/chat/src/main/resources/webapp/chat.html @@ -86,8 +86,11 @@ return 'UNKNOWN['+dr+']'; } } - - var socket = io.connect('http://127.0.0.1:8080'); + + //TODO: use origin host name + var socket = io('http://127.0.0.1:8080', { + transports: ['websocket'] + }); socket.on('connect', function () { document.getElementById('form').style.display='block'; diff --git a/samples/chat/src/main/webapp/json.js b/samples/chat/src/main/resources/webapp/json.js similarity index 100% rename from samples/chat/src/main/webapp/json.js rename to samples/chat/src/main/resources/webapp/json.js diff --git a/samples/chat/src/test/java/ChatServer.java b/samples/chat/src/test/java/ChatServer.java index dfb92b1..328afa6 100644 --- a/samples/chat/src/test/java/ChatServer.java +++ b/samples/chat/src/test/java/ChatServer.java @@ -31,16 +31,15 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import com.glines.socketio.sample.chat.ChatSocketServlet; -import com.glines.socketio.server.SocketIOConfig; +import com.codeminders.socketio.sample.chat.ChatSocketServlet; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; -import com.glines.socketio.server.transport.FlashSocketTransport; - public class ChatServer { + + //TODO: use Jetty DefaultServlet private static class StaticServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) @@ -49,7 +48,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) path = path.substring(req.getContextPath().length()); if ("/json.js".equals(path)) { resp.setContentType("text/javascript"); - InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/glines/socketio/sample/chat/json.js"); + InputStream is = this.getClass().getClassLoader().getResourceAsStream("webapp/json.js"); OutputStream os = resp.getOutputStream(); byte[] data = new byte[8192]; int nread = 0; @@ -61,7 +60,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) } } else if ("/chat.html".equals(path)) { resp.setContentType("text/html"); - InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/glines/socketio/sample/chat/chat.html"); + InputStream is = this.getClass().getClassLoader().getResourceAsStream("webapp/chat.html"); OutputStream os = resp.getOutputStream(); byte[] data = new byte[8192]; int nread = 0; @@ -102,11 +101,10 @@ public static void main(String[] args) throws Exception { ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); ServletHolder holder = new ServletHolder(new ChatSocketServlet()); - holder.setInitParameter(FlashSocketTransport.PARAM_FLASHPOLICY_SERVER_HOST, host); - holder.setInitParameter(FlashSocketTransport.PARAM_FLASHPOLICY_DOMAIN, host); - holder.setInitParameter(FlashSocketTransport.PARAM_FLASHPOLICY_PORTS, ""+ port); context.addServlet(holder, "/socket.io/*"); - context.addServlet(new ServletHolder(new StaticServlet()), "/*"); + + //TODO: use Jetty DefaultServlet + context.addServlet(new ServletHolder(new StaticServlet()), "/*"); server.setHandler(context); server.start(); diff --git a/samples/echo/pom.xml b/samples/echo/pom.xml deleted file mode 100644 index 32136c7..0000000 --- a/samples/echo/pom.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - 4.0.0 - - - com.glines.socketio.sample - socketio-sample - 0.2.2 - .. - - - socketio-sample-echo - war - - Socket.IO Sample Echo - ${project.name} - - - - com.glines.socketio - socketio-core - - - com.glines.socketio.extension - socketio-jetty - - - - org.mortbay.jetty - servlet-api - provided - - - - diff --git a/samples/echo/src/main/java/com/glines/socketio/sample/echo/EchoSocketServlet.java b/samples/echo/src/main/java/com/glines/socketio/sample/echo/EchoSocketServlet.java deleted file mode 100644 index 8ba8876..0000000 --- a/samples/echo/src/main/java/com/glines/socketio/sample/echo/EchoSocketServlet.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.sample.echo; - -import java.io.IOException; - -import javax.servlet.http.HttpServletRequest; - -import com.glines.socketio.common.DisconnectReason; -import com.glines.socketio.server.SocketIOInbound; -import com.glines.socketio.server.SocketIOOutbound; -import com.glines.socketio.server.SocketIOServlet; - -public class EchoSocketServlet extends SocketIOServlet { - private static final long serialVersionUID = 1L; - - private class EchoConnection implements SocketIOInbound { - private volatile SocketIOOutbound outbound = null; - - @Override - public void onConnect(SocketIOOutbound outbound) { - this.outbound = outbound; - } - - @Override - public void onDisconnect(DisconnectReason reason, String errorMessage) { - this.outbound = null; - } - - @Override - public void onMessage(int messageType, String message) { - try { - outbound.sendMessage(messageType, message); - } catch (IOException e) { - outbound.disconnect(); - } - } - - @Override - public void onEvent(String name, String args) { - - } - } - - @Override - protected SocketIOInbound doSocketIOConnect(HttpServletRequest request) { - return new EchoConnection(); - } - -} diff --git a/samples/echo/src/main/webapp/WEB-INF/web.xml b/samples/echo/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 915d762..0000000 --- a/samples/echo/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - EchoSocketServlet - com.glines.socketio.sample.echo.EchoSocketServlet - - - - EchoSocketServlet - /socket.io/* - - - diff --git a/samples/echo/src/test/java/EchoServer.java b/samples/echo/src/test/java/EchoServer.java deleted file mode 100644 index d361d62..0000000 --- a/samples/echo/src/test/java/EchoServer.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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. - */ -import com.glines.socketio.sample.echo.EchoSocketServlet; -import com.glines.socketio.server.SocketIOConfig; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; - -import com.glines.socketio.server.transport.FlashSocketTransport; - -public class EchoServer { - /** - * @param args - * @throws Exception - */ - public static void main(String[] args) throws Exception { - String host = "localhost"; - int port = 8080; - - if (args.length > 0) { - host = args[0]; - } - - if (args.length > 1) { - port = Integer.parseInt(args[1]); - } - - Server server = new Server(); - ServerConnector connector = new ServerConnector(server); - connector.setHost(host); - connector.setPort(port); - - server.addConnector(connector); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - ServletHolder holder = new ServletHolder(new EchoSocketServlet()); - holder.setInitParameter(FlashSocketTransport.PARAM_FLASHPOLICY_SERVER_HOST, host); - holder.setInitParameter(FlashSocketTransport.PARAM_FLASHPOLICY_DOMAIN, host); - holder.setInitParameter(FlashSocketTransport.PARAM_FLASHPOLICY_PORTS, ""+ port); - context.addServlet(holder, "/socket.io/*"); - - server.setHandler(context); - server.start(); - } - -} diff --git a/samples/echo/src/test/java/StartSampleEcho.java b/samples/echo/src/test/java/StartSampleEcho.java deleted file mode 100644 index 8011ae7..0000000 --- a/samples/echo/src/test/java/StartSampleEcho.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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. - */ -import org.testatoo.container.ContainerRunner; - -/** - * Just run this class and go to http://127.0.0.1:8080/ - * - * @author Mathieu Carbou - */ -public final class StartSampleEcho { - public static void main(String... args) throws Exception { - // ContainerRunner could be directly run from a launcher with argument. - // See ContainerRunner --help foe help - ContainerRunner.main("-container", "jetty"); - } -} diff --git a/samples/eventbus/pom.xml b/samples/eventbus/pom.xml deleted file mode 100644 index e12649c..0000000 --- a/samples/eventbus/pom.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - 4.0.0 - - - com.glines.socketio.sample - socketio-sample - 0.2.2 - .. - - - socketio-sample-eventbus - war - - Socket.IO Sample EventBus - ${project.name} - - - - com.glines.socketio - socketio-core - - - com.glines.socketio.extension - socketio-jetty - - - org.codehaus.jettison - jettison - - - - - log4j - log4j - - - org.slf4j - slf4j-log4j12 - - - org.slf4j - slf4j-api - - - - org.mortbay.jetty - servlet-api - provided - - - - diff --git a/samples/eventbus/src/main/java/com/glines/socketio/sample/eventbus/EventBusServlet.java b/samples/eventbus/src/main/java/com/glines/socketio/sample/eventbus/EventBusServlet.java deleted file mode 100644 index 470f912..0000000 --- a/samples/eventbus/src/main/java/com/glines/socketio/sample/eventbus/EventBusServlet.java +++ /dev/null @@ -1,278 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.glines.socketio.sample.eventbus; - -import com.glines.socketio.common.DisconnectReason; -import com.glines.socketio.common.SocketIOException; -import com.glines.socketio.server.SocketIOInbound; -import com.glines.socketio.server.SocketIOOutbound; -import com.glines.socketio.server.SocketIOServlet; -import com.glines.socketio.util.JdkOverLog4j; -import org.codehaus.jettison.json.JSONArray; -import org.codehaus.jettison.json.JSONException; -import org.codehaus.jettison.json.JSONObject; - -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class EventBusServlet extends SocketIOServlet { - - private static final Logger LOGGER = Logger.getLogger(EventBusServlet.class.getName()); - private static final long serialVersionUID = 5761989518185277878L; - - private final ConcurrentMap subscriptions = new ConcurrentHashMap(); - - @Override - public void init(ServletConfig config) throws ServletException { - JdkOverLog4j.install(); - super.init(config); - } - - @Override - protected SocketIOInbound doSocketIOConnect(HttpServletRequest request) { - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.fine("doSocketIOConnect : " + request.getRemoteHost() + ":" + request.getRemotePort()); - return new Endpoint(request.getSession().getId(), request.getRemoteHost(), request.getRemotePort()); - } - - private final class Endpoint implements SocketIOInbound { - - private final String remoteHost; - private final int remotePort; - private final String id; - - private volatile SocketIOOutbound outbound; - - private Endpoint(String id, String remoteHost, int remotePort) { - this.remoteHost = remoteHost; - this.remotePort = remotePort; - this.id = id; - } - - String getId() { - return id; - } - - @Override - public String toString() { - return "Endpoint " + id + " (" + remoteHost + ":" + remotePort + ")"; - } - - @Override - public int hashCode() { - return 31 * id.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Endpoint endpoint = (Endpoint) o; - return id.equals(endpoint.id); - } - - @Override - public void onConnect(SocketIOOutbound outbound) { - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.fine(this + " connected."); - this.outbound = outbound; - try { - send(new JSONObject().put("type", MessageType.ACK)); - } catch (JSONException e) { - throw new RuntimeException(e.getMessage(), e); - } - } - - @Override - public void onDisconnect(DisconnectReason reason, String errorMessage) { - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, this + " disconnected: reason=" + reason); - this.outbound = null; - for (Endpoints ee : subscriptions.values()) - ee.remove(this); - } - - @Override - public void onMessage(int messageType, String message) { - if (outbound == null) - throw new NullPointerException(); - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.fine(this + " received message: " + message); - try { - JSONArray array = new JSONArray(message); - for (int i = 0; i < array.length() && outbound != null; i++) { - JSONObject json = array.getJSONObject(i); - MessageType type = MessageType.valueOf(json.getInt("type")); - switch (type) { - case SUBSCRIBE: { - String topic = json.getString("topic"); - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, this + " subscribes to topic: " + topic); - subscriptions.putIfAbsent(topic, new Endpoints(topic)); - subscriptions.get(topic).add(this); - break; - } - case UNSUBSCRIBE: { - String topic = json.getString("topic"); - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, this + " unsubscribes from topic: " + topic); - Endpoints ee = subscriptions.get(topic); - if (ee != null) - ee.remove(this); - return; - } - case PUBLISH: { - String topic = json.getString("topic"); - String data = json.getString("data"); - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, this + " publishes to topic " + topic + " message: " + data); - Endpoints ee = subscriptions.get(topic); - if (ee != null) - ee.fire(topic, data); - break; - } - default: { - close(); - throw new IllegalArgumentException("Illegal message: " + message); - } - } - } - } catch (JSONException e) { - throw new RuntimeException(e.getMessage(), e); - } - } - - @Override - public void onEvent(String name, String args) { - - } - - void close() { - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, this + " closing."); - if (outbound != null) { - outbound.close(); - this.outbound = null; - } - } - - void send(JSONObject data) { - if (outbound != null) { - String str = data.toString(); - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.fine("Sending to " + this + " message: " + str); - try { - outbound.sendMessage(str); - } catch (SocketIOException e) { - LOGGER.log(Level.SEVERE, "Error sending message to " + this + " => disconnecting. Error: " + e.getMessage(), e); - close(); - } - } - } - } - - private final class Endpoints { - - final String topic; - final ConcurrentMap endpoints = new ConcurrentHashMap(); - - Endpoints(String topic) { - this.topic = topic; - } - - @Override - public String toString() { - return "Endpoints for " + topic + ": " + endpoints.size(); - } - - void add(Endpoint endpoint) { - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, "Subscribing " + endpoint + " to " + this); - Endpoint old = endpoints.get(endpoint.getId()); - if (old == null) { - endpoints.putIfAbsent(endpoint.getId(), endpoint); - } else { - endpoints.replace(endpoint.getId(), old, endpoint); - } - if (old != null) - old.close(); - } - - void remove(Endpoint endpoint) { - if (endpoints.remove(endpoint.getId(), endpoint)) { - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, endpoint + " unsubscribed from " + this); - if (endpoints.isEmpty()) { - subscriptions.remove(topic, this); - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, this + " removed from subscriptions."); - } - } - } - - void fire(String topic, String data) { - for (Endpoint endpoint : endpoints.values()) - try { - endpoint.send(new JSONObject().put("type", MessageType.PUBLISH).put("topic", topic).put("data", data)); - } catch (JSONException e) { - throw new RuntimeException(e.getMessage(), e); - } - } - } - - private static enum MessageType { - - ACK(4), - SUBSCRIBE(1), - UNSUBSCRIBE(2), - PUBLISH(3), - UNKNOWN(0); - - final int code; - - MessageType(int code) { - this.code = code; - } - - - static MessageType valueOf(int code) { - for (MessageType type : values()) - if (type.code == code) - return type; - return UNKNOWN; - } - - @Override - public String toString() { - return "" + code; - } - } - -} diff --git a/samples/eventbus/src/main/resources/log4j.xml b/samples/eventbus/src/main/resources/log4j.xml deleted file mode 100644 index d6530cf..0000000 --- a/samples/eventbus/src/main/resources/log4j.xml +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/samples/eventbus/src/main/webapp/WEB-INF/web.xml b/samples/eventbus/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 583648b..0000000 --- a/samples/eventbus/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - 20 - - - - EventBusServlet - com.glines.socketio.sample.eventbus.EventBusServlet - - - - EventBusServlet - /socket.io/* - - - - index.html - - - diff --git a/samples/eventbus/src/main/webapp/index.html b/samples/eventbus/src/main/webapp/index.html deleted file mode 100644 index f762407..0000000 --- a/samples/eventbus/src/main/webapp/index.html +++ /dev/null @@ -1,211 +0,0 @@ - - - - - EventBus - - - - - - - - - - -Topic: -Message: - -

In the Firebug console, you can execute:

-
TESTER.start();
-
TESTER.subscribe('myTopic');
-
TESTER.publish('myTopic', 'myMessage');
-
TESTER.stop();
-
-

-
-
diff --git a/samples/eventbus/src/main/webapp/jquery-1.4.4.min.js b/samples/eventbus/src/main/webapp/jquery-1.4.4.min.js
deleted file mode 100644
index 8f3ca2e..0000000
--- a/samples/eventbus/src/main/webapp/jquery-1.4.4.min.js
+++ /dev/null
@@ -1,167 +0,0 @@
-/*!
- * jQuery JavaScript Library v1.4.4
- * http://jquery.com/
- *
- * Copyright 2010, John Resig
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
- * Includes Sizzle.js
- * http://sizzlejs.com/
- * Copyright 2010, The Dojo Foundation
- * Released under the MIT, BSD, and GPL Licenses.
- *
- * Date: Thu Nov 11 19:04:53 2010 -0500
- */
-(function(E,B){function ka(a,b,d){if(d===B&&a.nodeType===1){d=a.getAttribute("data-"+b);if(typeof d==="string"){try{d=d==="true"?true:d==="false"?false:d==="null"?null:!c.isNaN(d)?parseFloat(d):Ja.test(d)?c.parseJSON(d):d}catch(e){}c.data(a,b,d)}else d=B}return d}function U(){return false}function ca(){return true}function la(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function Ka(a){var b,d,e,f,h,l,k,o,x,r,A,C=[];f=[];h=c.data(this,this.nodeType?"events":"__events__");if(typeof h==="function")h=
-h.events;if(!(a.liveFired===this||!h||!h.live||a.button&&a.type==="click")){if(a.namespace)A=RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)");a.liveFired=this;var J=h.live.slice(0);for(k=0;kd)break;a.currentTarget=f.elem;a.data=f.handleObj.data;a.handleObj=f.handleObj;A=f.handleObj.origHandler.apply(f.elem,arguments);if(A===false||a.isPropagationStopped()){d=f.level;if(A===false)b=false;if(a.isImmediatePropagationStopped())break}}return b}}function Y(a,b){return(a&&a!=="*"?a+".":"")+b.replace(La,
-"`").replace(Ma,"&")}function ma(a,b,d){if(c.isFunction(b))return c.grep(a,function(f,h){return!!b.call(f,h,f)===d});else if(b.nodeType)return c.grep(a,function(f){return f===b===d});else if(typeof b==="string"){var e=c.grep(a,function(f){return f.nodeType===1});if(Na.test(b))return c.filter(b,e,!d);else b=c.filter(b,e)}return c.grep(a,function(f){return c.inArray(f,b)>=0===d})}function na(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var e=c.data(a[d++]),f=c.data(this,
-e);if(e=e&&e.events){delete f.handle;f.events={};for(var h in e)for(var l in e[h])c.event.add(this,h,e[h][l],e[h][l].data)}}})}function Oa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function oa(a,b,d){var e=b==="width"?a.offsetWidth:a.offsetHeight;if(d==="border")return e;c.each(b==="width"?Pa:Qa,function(){d||(e-=parseFloat(c.css(a,"padding"+this))||0);if(d==="margin")e+=parseFloat(c.css(a,
-"margin"+this))||0;else e-=parseFloat(c.css(a,"border"+this+"Width"))||0});return e}function da(a,b,d,e){if(c.isArray(b)&&b.length)c.each(b,function(f,h){d||Ra.test(a)?e(a,h):da(a+"["+(typeof h==="object"||c.isArray(h)?f:"")+"]",h,d,e)});else if(!d&&b!=null&&typeof b==="object")c.isEmptyObject(b)?e(a,""):c.each(b,function(f,h){da(a+"["+f+"]",h,d,e)});else e(a,b)}function S(a,b){var d={};c.each(pa.concat.apply([],pa.slice(0,b)),function(){d[this]=a});return d}function qa(a){if(!ea[a]){var b=c("<"+
-a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d==="")d="block";ea[a]=d}return ea[a]}function fa(a){return c.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var t=E.document,c=function(){function a(){if(!b.isReady){try{t.documentElement.doScroll("left")}catch(j){setTimeout(a,1);return}b.ready()}}var b=function(j,s){return new b.fn.init(j,s)},d=E.jQuery,e=E.$,f,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,l=/\S/,k=/^\s+/,o=/\s+$/,x=/\W/,r=/\d/,A=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,
-C=/^[\],:{}\s]*$/,J=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,w=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,I=/(?:^|:|,)(?:\s*\[)+/g,L=/(webkit)[ \/]([\w.]+)/,g=/(opera)(?:.*version)?[ \/]([\w.]+)/,i=/(msie) ([\w.]+)/,n=/(mozilla)(?:.*? rv:([\w.]+))?/,m=navigator.userAgent,p=false,q=[],u,y=Object.prototype.toString,F=Object.prototype.hasOwnProperty,M=Array.prototype.push,N=Array.prototype.slice,O=String.prototype.trim,D=Array.prototype.indexOf,R={};b.fn=b.prototype={init:function(j,
-s){var v,z,H;if(!j)return this;if(j.nodeType){this.context=this[0]=j;this.length=1;return this}if(j==="body"&&!s&&t.body){this.context=t;this[0]=t.body;this.selector="body";this.length=1;return this}if(typeof j==="string")if((v=h.exec(j))&&(v[1]||!s))if(v[1]){H=s?s.ownerDocument||s:t;if(z=A.exec(j))if(b.isPlainObject(s)){j=[t.createElement(z[1])];b.fn.attr.call(j,s,true)}else j=[H.createElement(z[1])];else{z=b.buildFragment([v[1]],[H]);j=(z.cacheable?z.fragment.cloneNode(true):z.fragment).childNodes}return b.merge(this,
-j)}else{if((z=t.getElementById(v[2]))&&z.parentNode){if(z.id!==v[2])return f.find(j);this.length=1;this[0]=z}this.context=t;this.selector=j;return this}else if(!s&&!x.test(j)){this.selector=j;this.context=t;j=t.getElementsByTagName(j);return b.merge(this,j)}else return!s||s.jquery?(s||f).find(j):b(s).find(j);else if(b.isFunction(j))return f.ready(j);if(j.selector!==B){this.selector=j.selector;this.context=j.context}return b.makeArray(j,this)},selector:"",jquery:"1.4.4",length:0,size:function(){return this.length},
-toArray:function(){return N.call(this,0)},get:function(j){return j==null?this.toArray():j<0?this.slice(j)[0]:this[j]},pushStack:function(j,s,v){var z=b();b.isArray(j)?M.apply(z,j):b.merge(z,j);z.prevObject=this;z.context=this.context;if(s==="find")z.selector=this.selector+(this.selector?" ":"")+v;else if(s)z.selector=this.selector+"."+s+"("+v+")";return z},each:function(j,s){return b.each(this,j,s)},ready:function(j){b.bindReady();if(b.isReady)j.call(t,b);else q&&q.push(j);return this},eq:function(j){return j===
--1?this.slice(j):this.slice(j,+j+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(N.apply(this,arguments),"slice",N.call(arguments).join(","))},map:function(j){return this.pushStack(b.map(this,function(s,v){return j.call(s,v,s)}))},end:function(){return this.prevObject||b(null)},push:M,sort:[].sort,splice:[].splice};b.fn.init.prototype=b.fn;b.extend=b.fn.extend=function(){var j,s,v,z,H,G=arguments[0]||{},K=1,Q=arguments.length,ga=false;
-if(typeof G==="boolean"){ga=G;G=arguments[1]||{};K=2}if(typeof G!=="object"&&!b.isFunction(G))G={};if(Q===K){G=this;--K}for(;K0))if(q){var s=0,v=q;for(q=null;j=v[s++];)j.call(t,b);b.fn.trigger&&b(t).trigger("ready").unbind("ready")}}},bindReady:function(){if(!p){p=true;if(t.readyState==="complete")return setTimeout(b.ready,1);if(t.addEventListener){t.addEventListener("DOMContentLoaded",u,false);E.addEventListener("load",b.ready,false)}else if(t.attachEvent){t.attachEvent("onreadystatechange",u);E.attachEvent("onload",
-b.ready);var j=false;try{j=E.frameElement==null}catch(s){}t.documentElement.doScroll&&j&&a()}}},isFunction:function(j){return b.type(j)==="function"},isArray:Array.isArray||function(j){return b.type(j)==="array"},isWindow:function(j){return j&&typeof j==="object"&&"setInterval"in j},isNaN:function(j){return j==null||!r.test(j)||isNaN(j)},type:function(j){return j==null?String(j):R[y.call(j)]||"object"},isPlainObject:function(j){if(!j||b.type(j)!=="object"||j.nodeType||b.isWindow(j))return false;if(j.constructor&&
-!F.call(j,"constructor")&&!F.call(j.constructor.prototype,"isPrototypeOf"))return false;for(var s in j);return s===B||F.call(j,s)},isEmptyObject:function(j){for(var s in j)return false;return true},error:function(j){throw j;},parseJSON:function(j){if(typeof j!=="string"||!j)return null;j=b.trim(j);if(C.test(j.replace(J,"@").replace(w,"]").replace(I,"")))return E.JSON&&E.JSON.parse?E.JSON.parse(j):(new Function("return "+j))();else b.error("Invalid JSON: "+j)},noop:function(){},globalEval:function(j){if(j&&
-l.test(j)){var s=t.getElementsByTagName("head")[0]||t.documentElement,v=t.createElement("script");v.type="text/javascript";if(b.support.scriptEval)v.appendChild(t.createTextNode(j));else v.text=j;s.insertBefore(v,s.firstChild);s.removeChild(v)}},nodeName:function(j,s){return j.nodeName&&j.nodeName.toUpperCase()===s.toUpperCase()},each:function(j,s,v){var z,H=0,G=j.length,K=G===B||b.isFunction(j);if(v)if(K)for(z in j){if(s.apply(j[z],v)===false)break}else for(;H
a";var f=d.getElementsByTagName("*"),h=d.getElementsByTagName("a")[0],l=t.createElement("select"), -k=l.appendChild(t.createElement("option"));if(!(!f||!f.length||!h)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(h.getAttribute("style")),hrefNormalized:h.getAttribute("href")==="/a",opacity:/^0.55$/.test(h.style.opacity),cssFloat:!!h.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:k.selected,deleteExpando:true,optDisabled:false,checkClone:false, -scriptEval:false,noCloneEvent:true,boxModel:null,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableHiddenOffsets:true};l.disabled=true;c.support.optDisabled=!k.disabled;b.type="text/javascript";try{b.appendChild(t.createTextNode("window."+e+"=1;"))}catch(o){}a.insertBefore(b,a.firstChild);if(E[e]){c.support.scriptEval=true;delete E[e]}try{delete b.test}catch(x){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function r(){c.support.noCloneEvent= -false;d.detachEvent("onclick",r)});d.cloneNode(true).fireEvent("onclick")}d=t.createElement("div");d.innerHTML="";a=t.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var r=t.createElement("div");r.style.width=r.style.paddingLeft="1px";t.body.appendChild(r);c.boxModel=c.support.boxModel=r.offsetWidth===2;if("zoom"in r.style){r.style.display="inline";r.style.zoom= -1;c.support.inlineBlockNeedsLayout=r.offsetWidth===2;r.style.display="";r.innerHTML="
";c.support.shrinkWrapBlocks=r.offsetWidth!==2}r.innerHTML="
t
";var A=r.getElementsByTagName("td");c.support.reliableHiddenOffsets=A[0].offsetHeight===0;A[0].style.display="";A[1].style.display="none";c.support.reliableHiddenOffsets=c.support.reliableHiddenOffsets&&A[0].offsetHeight===0;r.innerHTML="";t.body.removeChild(r).style.display= -"none"});a=function(r){var A=t.createElement("div");r="on"+r;var C=r in A;if(!C){A.setAttribute(r,"return;");C=typeof A[r]==="function"}return C};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=f=h=null}})();var ra={},Ja=/^(?:\{.*\}|\[.*\])$/;c.extend({cache:{},uuid:0,expando:"jQuery"+c.now(),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},data:function(a,b,d){if(c.acceptData(a)){a=a==E?ra:a;var e=a.nodeType,f=e?a[c.expando]:null,h= -c.cache;if(!(e&&!f&&typeof b==="string"&&d===B)){if(e)f||(a[c.expando]=f=++c.uuid);else h=a;if(typeof b==="object")if(e)h[f]=c.extend(h[f],b);else c.extend(h,b);else if(e&&!h[f])h[f]={};a=e?h[f]:h;if(d!==B)a[b]=d;return typeof b==="string"?a[b]:a}}},removeData:function(a,b){if(c.acceptData(a)){a=a==E?ra:a;var d=a.nodeType,e=d?a[c.expando]:a,f=c.cache,h=d?f[e]:e;if(b){if(h){delete h[b];d&&c.isEmptyObject(h)&&c.removeData(a)}}else if(d&&c.support.deleteExpando)delete a[c.expando];else if(a.removeAttribute)a.removeAttribute(c.expando); -else if(d)delete f[e];else for(var l in a)delete a[l]}},acceptData:function(a){if(a.nodeName){var b=c.noData[a.nodeName.toLowerCase()];if(b)return!(b===true||a.getAttribute("classid")!==b)}return true}});c.fn.extend({data:function(a,b){var d=null;if(typeof a==="undefined"){if(this.length){var e=this[0].attributes,f;d=c.data(this[0]);for(var h=0,l=e.length;h-1)return true;return false},val:function(a){if(!arguments.length){var b=this[0];if(b){if(c.nodeName(b,"option")){var d=b.attributes.value;return!d||d.specified?b.value:b.text}if(c.nodeName(b,"select")){var e=b.selectedIndex;d=[];var f=b.options;b=b.type==="select-one"; -if(e<0)return null;var h=b?e:0;for(e=b?e+1:f.length;h=0;else if(c.nodeName(this,"select")){var A=c.makeArray(r);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),A)>=0});if(!A.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true}, -attr:function(a,b,d,e){if(!a||a.nodeType===3||a.nodeType===8)return B;if(e&&b in c.attrFn)return c(a)[b](d);e=a.nodeType!==1||!c.isXMLDoc(a);var f=d!==B;b=e&&c.props[b]||b;var h=Ta.test(b);if((b in a||a[b]!==B)&&e&&!h){if(f){b==="type"&&Ua.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");if(d===null)a.nodeType===1&&a.removeAttribute(b);else a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&& -b.specified?b.value:Va.test(a.nodeName)||Wa.test(a.nodeName)&&a.href?0:B;return a[b]}if(!c.support.style&&e&&b==="style"){if(f)a.style.cssText=""+d;return a.style.cssText}f&&a.setAttribute(b,""+d);if(!a.attributes[b]&&a.hasAttribute&&!a.hasAttribute(b))return B;a=!c.support.hrefNormalized&&e&&h?a.getAttribute(b,2):a.getAttribute(b);return a===null?B:a}});var X=/\.(.*)$/,ia=/^(?:textarea|input|select)$/i,La=/\./g,Ma=/ /g,Xa=/[^\w\s.|`]/g,Ya=function(a){return a.replace(Xa,"\\$&")},ua={focusin:0,focusout:0}; -c.event={add:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(c.isWindow(a)&&a!==E&&!a.frameElement)a=E;if(d===false)d=U;else if(!d)return;var f,h;if(d.handler){f=d;d=f.handler}if(!d.guid)d.guid=c.guid++;if(h=c.data(a)){var l=a.nodeType?"events":"__events__",k=h[l],o=h.handle;if(typeof k==="function"){o=k.handle;k=k.events}else if(!k){a.nodeType||(h[l]=h=function(){});h.events=k={}}if(!o)h.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem, -arguments):B};o.elem=a;b=b.split(" ");for(var x=0,r;l=b[x++];){h=f?c.extend({},f):{handler:d,data:e};if(l.indexOf(".")>-1){r=l.split(".");l=r.shift();h.namespace=r.slice(0).sort().join(".")}else{r=[];h.namespace=""}h.type=l;if(!h.guid)h.guid=d.guid;var A=k[l],C=c.event.special[l]||{};if(!A){A=k[l]=[];if(!C.setup||C.setup.call(a,e,r,o)===false)if(a.addEventListener)a.addEventListener(l,o,false);else a.attachEvent&&a.attachEvent("on"+l,o)}if(C.add){C.add.call(a,h);if(!h.handler.guid)h.handler.guid= -d.guid}A.push(h);c.event.global[l]=true}a=null}}},global:{},remove:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(d===false)d=U;var f,h,l=0,k,o,x,r,A,C,J=a.nodeType?"events":"__events__",w=c.data(a),I=w&&w[J];if(w&&I){if(typeof I==="function"){w=I;I=I.events}if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(f in I)c.event.remove(a,f+b)}else{for(b=b.split(" ");f=b[l++];){r=f;k=f.indexOf(".")<0;o=[];if(!k){o=f.split(".");f=o.shift();x=RegExp("(^|\\.)"+ -c.map(o.slice(0).sort(),Ya).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(A=I[f])if(d){r=c.event.special[f]||{};for(h=e||0;h=0){a.type=f=f.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[f]&&c.each(c.cache,function(){this.events&&this.events[f]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType=== -8)return B;a.result=B;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(e=d.nodeType?c.data(d,"handle"):(c.data(d,"__events__")||{}).handle)&&e.apply(d,b);e=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+f]&&d["on"+f].apply(d,b)===false){a.result=false;a.preventDefault()}}catch(h){}if(!a.isPropagationStopped()&&e)c.event.trigger(a,b,e,true);else if(!a.isDefaultPrevented()){var l;e=a.target;var k=f.replace(X,""),o=c.nodeName(e,"a")&&k=== -"click",x=c.event.special[k]||{};if((!x._default||x._default.call(d,a)===false)&&!o&&!(e&&e.nodeName&&c.noData[e.nodeName.toLowerCase()])){try{if(e[k]){if(l=e["on"+k])e["on"+k]=null;c.event.triggered=true;e[k]()}}catch(r){}if(l)e["on"+k]=l;c.event.triggered=false}}},handle:function(a){var b,d,e,f;d=[];var h=c.makeArray(arguments);a=h[0]=c.event.fix(a||E.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){e=a.type.split(".");a.type=e.shift();d=e.slice(0).sort();e=RegExp("(^|\\.)"+ -d.join("\\.(?:.*\\.)?")+"(\\.|$)")}a.namespace=a.namespace||d.join(".");f=c.data(this,this.nodeType?"events":"__events__");if(typeof f==="function")f=f.events;d=(f||{})[a.type];if(f&&d){d=d.slice(0);f=0;for(var l=d.length;f-1?c.map(a.options,function(e){return e.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},Z=function(a,b){var d=a.target,e,f;if(!(!ia.test(d.nodeName)||d.readOnly)){e=c.data(d,"_change_data");f=xa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",f);if(!(e===B||f===e))if(e!=null||f){a.type="change";a.liveFired= -B;return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:Z,beforedeactivate:Z,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return Z.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return Z.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,"_change_data",xa(a))}},setup:function(){if(this.type=== -"file")return false;for(var a in V)c.event.add(this,a+".specialChange",V[a]);return ia.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return ia.test(this.nodeName)}};V=c.event.special.change.filters;V.focus=V.beforeactivate}t.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(e){e=c.event.fix(e);e.type=b;return c.event.trigger(e,null,e.target)}c.event.special[b]={setup:function(){ua[b]++===0&&t.addEventListener(a,d,true)},teardown:function(){--ua[b]=== -0&&t.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,e,f){if(typeof d==="object"){for(var h in d)this[b](h,e,d[h],f);return this}if(c.isFunction(e)||e===false){f=e;e=B}var l=b==="one"?c.proxy(f,function(o){c(this).unbind(o,l);return f.apply(this,arguments)}):f;if(d==="unload"&&b!=="one")this.one(d,e,f);else{h=0;for(var k=this.length;h0?this.bind(b,d,e):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});E.attachEvent&&!E.addEventListener&&c(E).bind("unload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}}); -(function(){function a(g,i,n,m,p,q){p=0;for(var u=m.length;p0){F=y;break}}y=y[g]}m[p]=F}}}var d=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,h=false,l=true;[0,0].sort(function(){l=false;return 0});var k=function(g,i,n,m){n=n||[];var p=i=i||t;if(i.nodeType!==1&&i.nodeType!==9)return[];if(!g||typeof g!=="string")return n;var q,u,y,F,M,N=true,O=k.isXML(i),D=[],R=g;do{d.exec("");if(q=d.exec(R)){R=q[3];D.push(q[1]);if(q[2]){F=q[3]; -break}}}while(q);if(D.length>1&&x.exec(g))if(D.length===2&&o.relative[D[0]])u=L(D[0]+D[1],i);else for(u=o.relative[D[0]]?[i]:k(D.shift(),i);D.length;){g=D.shift();if(o.relative[g])g+=D.shift();u=L(g,u)}else{if(!m&&D.length>1&&i.nodeType===9&&!O&&o.match.ID.test(D[0])&&!o.match.ID.test(D[D.length-1])){q=k.find(D.shift(),i,O);i=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]}if(i){q=m?{expr:D.pop(),set:C(m)}:k.find(D.pop(),D.length===1&&(D[0]==="~"||D[0]==="+")&&i.parentNode?i.parentNode:i,O);u=q.expr?k.filter(q.expr, -q.set):q.set;if(D.length>0)y=C(u);else N=false;for(;D.length;){q=M=D.pop();if(o.relative[M])q=D.pop();else M="";if(q==null)q=i;o.relative[M](y,q,O)}}else y=[]}y||(y=u);y||k.error(M||g);if(f.call(y)==="[object Array]")if(N)if(i&&i.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&k.contains(i,y[g])))n.push(u[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&n.push(u[g]);else n.push.apply(n,y);else C(y,n);if(F){k(F,p,n,m);k.uniqueSort(n)}return n};k.uniqueSort=function(g){if(w){h= -l;g.sort(w);if(h)for(var i=1;i0};k.find=function(g,i,n){var m;if(!g)return[];for(var p=0,q=o.order.length;p":function(g,i){var n,m=typeof i==="string",p=0,q=g.length;if(m&&!/\W/.test(i))for(i=i.toLowerCase();p=0))n||m.push(u);else if(n)i[q]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var i=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=i[1]+(i[2]||1)-0;g[3]=i[3]-0}g[0]=e++;return g},ATTR:function(g,i,n, -m,p,q){i=g[1].replace(/\\/g,"");if(!q&&o.attrMap[i])g[1]=o.attrMap[i];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,i,n,m,p){if(g[1]==="not")if((d.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,i);else{g=k.filter(g[3],i,n,true^p);n||m.push.apply(m,g);return false}else if(o.match.POS.test(g[0])||o.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled=== -true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,i,n){return!!k(n[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"=== -g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,i){return i===0},last:function(g,i,n,m){return i===m.length-1},even:function(g,i){return i%2===0},odd:function(g,i){return i%2===1},lt:function(g,i,n){return in[3]-0},nth:function(g,i,n){return n[3]- -0===i},eq:function(g,i,n){return n[3]-0===i}},filter:{PSEUDO:function(g,i,n,m){var p=i[1],q=o.filters[p];if(q)return q(g,n,i,m);else if(p==="contains")return(g.textContent||g.innerText||k.getText([g])||"").indexOf(i[3])>=0;else if(p==="not"){i=i[3];n=0;for(m=i.length;n=0}},ID:function(g,i){return g.nodeType===1&&g.getAttribute("id")===i},TAG:function(g,i){return i==="*"&&g.nodeType===1||g.nodeName.toLowerCase()=== -i},CLASS:function(g,i){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(i)>-1},ATTR:function(g,i){var n=i[1];n=o.attrHandle[n]?o.attrHandle[n](g):g[n]!=null?g[n]:g.getAttribute(n);var m=n+"",p=i[2],q=i[4];return n==null?p==="!=":p==="="?m===q:p==="*="?m.indexOf(q)>=0:p==="~="?(" "+m+" ").indexOf(q)>=0:!q?m&&n!==false:p==="!="?m!==q:p==="^="?m.indexOf(q)===0:p==="$="?m.substr(m.length-q.length)===q:p==="|="?m===q||m.substr(0,q.length+1)===q+"-":false},POS:function(g,i,n,m){var p=o.setFilters[i[2]]; -if(p)return p(g,n,i,m)}}},x=o.match.POS,r=function(g,i){return"\\"+(i-0+1)},A;for(A in o.match){o.match[A]=RegExp(o.match[A].source+/(?![^\[]*\])(?![^\(]*\))/.source);o.leftMatch[A]=RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[A].source.replace(/\\(\d+)/g,r))}var C=function(g,i){g=Array.prototype.slice.call(g,0);if(i){i.push.apply(i,g);return i}return g};try{Array.prototype.slice.call(t.documentElement.childNodes,0)}catch(J){C=function(g,i){var n=0,m=i||[];if(f.call(g)==="[object Array]")Array.prototype.push.apply(m, -g);else if(typeof g.length==="number")for(var p=g.length;n";n.insertBefore(g,n.firstChild);if(t.getElementById(i)){o.find.ID=function(m,p,q){if(typeof p.getElementById!=="undefined"&&!q)return(p=p.getElementById(m[1]))?p.id===m[1]||typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id").nodeValue===m[1]?[p]:B:[]};o.filter.ID=function(m,p){var q=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&q&&q.nodeValue===p}}n.removeChild(g); -n=g=null})();(function(){var g=t.createElement("div");g.appendChild(t.createComment(""));if(g.getElementsByTagName("*").length>0)o.find.TAG=function(i,n){var m=n.getElementsByTagName(i[1]);if(i[1]==="*"){for(var p=[],q=0;m[q];q++)m[q].nodeType===1&&p.push(m[q]);m=p}return m};g.innerHTML="";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")o.attrHandle.href=function(i){return i.getAttribute("href",2)};g=null})();t.querySelectorAll&& -function(){var g=k,i=t.createElement("div");i.innerHTML="

";if(!(i.querySelectorAll&&i.querySelectorAll(".TEST").length===0)){k=function(m,p,q,u){p=p||t;m=m.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!u&&!k.isXML(p))if(p.nodeType===9)try{return C(p.querySelectorAll(m),q)}catch(y){}else if(p.nodeType===1&&p.nodeName.toLowerCase()!=="object"){var F=p.getAttribute("id"),M=F||"__sizzle__";F||p.setAttribute("id",M);try{return C(p.querySelectorAll("#"+M+" "+m),q)}catch(N){}finally{F|| -p.removeAttribute("id")}}return g(m,p,q,u)};for(var n in g)k[n]=g[n];i=null}}();(function(){var g=t.documentElement,i=g.matchesSelector||g.mozMatchesSelector||g.webkitMatchesSelector||g.msMatchesSelector,n=false;try{i.call(t.documentElement,"[test!='']:sizzle")}catch(m){n=true}if(i)k.matchesSelector=function(p,q){q=q.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(p))try{if(n||!o.match.PSEUDO.test(q)&&!/!=/.test(q))return i.call(p,q)}catch(u){}return k(q,null,null,[p]).length>0}})();(function(){var g= -t.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){o.order.splice(1,0,"CLASS");o.find.CLASS=function(i,n,m){if(typeof n.getElementsByClassName!=="undefined"&&!m)return n.getElementsByClassName(i[1])};g=null}}})();k.contains=t.documentElement.contains?function(g,i){return g!==i&&(g.contains?g.contains(i):true)}:t.documentElement.compareDocumentPosition? -function(g,i){return!!(g.compareDocumentPosition(i)&16)}:function(){return false};k.isXML=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false};var L=function(g,i){for(var n,m=[],p="",q=i.nodeType?[i]:i;n=o.match.PSEUDO.exec(g);){p+=n[0];g=g.replace(o.match.PSEUDO,"")}g=o.relative[g]?g+"*":g;n=0;for(var u=q.length;n0)for(var h=d;h0},closest:function(a,b){var d=[],e,f,h=this[0];if(c.isArray(a)){var l,k={},o=1;if(h&&a.length){e=0;for(f=a.length;e-1:c(h).is(e))d.push({selector:l,elem:h,level:o})}h= -h.parentNode;o++}}return d}l=cb.test(a)?c(a,b||this.context):null;e=0;for(f=this.length;e-1:c.find.matchesSelector(h,a)){d.push(h);break}else{h=h.parentNode;if(!h||!h.ownerDocument||h===b)break}d=d.length>1?c.unique(d):d;return this.pushStack(d,"closest",a)},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var d=typeof a==="string"?c(a,b||this.context): -c.makeArray(a),e=c.merge(this.get(),d);return this.pushStack(!d[0]||!d[0].parentNode||d[0].parentNode.nodeType===11||!e[0]||!e[0].parentNode||e[0].parentNode.nodeType===11?e:c.unique(e))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a, -2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a, -b){c.fn[a]=function(d,e){var f=c.map(this,b,d);Za.test(a)||(e=d);if(e&&typeof e==="string")f=c.filter(e,f);f=this.length>1?c.unique(f):f;if((this.length>1||ab.test(e))&&$a.test(a))f=f.reverse();return this.pushStack(f,a,bb.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return b.length===1?c.find.matchesSelector(b[0],a)?[b[0]]:[]:c.find.matches(a,b)},dir:function(a,b,d){var e=[];for(a=a[b];a&&a.nodeType!==9&&(d===B||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&& -e.push(a);a=a[b]}return e},nth:function(a,b,d){b=b||1;for(var e=0;a;a=a[d])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var za=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,Aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Ba=/<([\w:]+)/,db=/\s]+\/)>/g,P={option:[1, -""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};P.optgroup=P.option;P.tbody=P.tfoot=P.colgroup=P.caption=P.thead;P.th=P.td;if(!c.support.htmlSerialize)P._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= -c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==B)return this.empty().append((this[0]&&this[0].ownerDocument||t).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, -wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, -prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, -this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,e;(e=this[d])!=null;d++)if(!a||c.filter(a,[e]).length){if(!b&&e.nodeType===1){c.cleanData(e.getElementsByTagName("*"));c.cleanData([e])}e.parentNode&&e.parentNode.removeChild(e)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); -return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,e=this.ownerDocument;if(!d){d=e.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(za,"").replace(fb,'="$1">').replace($,"")],e)[0]}else return this.cloneNode(true)});if(a===true){na(this,b);na(this.find("*"),b.find("*"))}return b},html:function(a){if(a===B)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(za,""):null; -else if(typeof a==="string"&&!Ca.test(a)&&(c.support.leadingWhitespace||!$.test(a))&&!P[(Ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Aa,"<$1>");try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?h.cloneNode(true):h)}k.length&&c.each(k,Oa)}return this}});c.buildFragment=function(a,b,d){var e,f,h;b=b&&b[0]?b[0].ownerDocument||b[0]:t;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===t&&!Ca.test(a[0])&&(c.support.checkClone||!Da.test(a[0]))){f=true;if(h=c.fragments[a[0]])if(h!==1)e=h}if(!e){e=b.createDocumentFragment();c.clean(a,b,e,d)}if(f)c.fragments[a[0]]=h?e:1;return{fragment:e,cacheable:f}};c.fragments={};c.each({appendTo:"append", -prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var e=[];d=c(d);var f=this.length===1&&this[0].parentNode;if(f&&f.nodeType===11&&f.childNodes.length===1&&d.length===1){d[b](this[0]);return this}else{f=0;for(var h=d.length;f0?this.clone(true):this).get();c(d[f])[b](l);e=e.concat(l)}return this.pushStack(e,a,d.selector)}}});c.extend({clean:function(a,b,d,e){b=b||t;if(typeof b.createElement==="undefined")b=b.ownerDocument|| -b[0]&&b[0].ownerDocument||t;for(var f=[],h=0,l;(l=a[h])!=null;h++){if(typeof l==="number")l+="";if(l){if(typeof l==="string"&&!eb.test(l))l=b.createTextNode(l);else if(typeof l==="string"){l=l.replace(Aa,"<$1>");var k=(Ba.exec(l)||["",""])[1].toLowerCase(),o=P[k]||P._default,x=o[0],r=b.createElement("div");for(r.innerHTML=o[1]+l+o[2];x--;)r=r.lastChild;if(!c.support.tbody){x=db.test(l);k=k==="table"&&!x?r.firstChild&&r.firstChild.childNodes:o[1]===""&&!x?r.childNodes:[];for(o=k.length- -1;o>=0;--o)c.nodeName(k[o],"tbody")&&!k[o].childNodes.length&&k[o].parentNode.removeChild(k[o])}!c.support.leadingWhitespace&&$.test(l)&&r.insertBefore(b.createTextNode($.exec(l)[0]),r.firstChild);l=r.childNodes}if(l.nodeType)f.push(l);else f=c.merge(f,l)}}if(d)for(h=0;f[h];h++)if(e&&c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script")))); -d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,l=0,k;(k=a[l])!=null;l++)if(!(k.nodeName&&c.noData[k.nodeName.toLowerCase()]))if(d=k[c.expando]){if((b=e[d])&&b.events)for(var o in b.events)f[o]?c.event.remove(k,o):c.removeEvent(k,o,b.handle);if(h)delete k[c.expando];else k.removeAttribute&&k.removeAttribute(c.expando);delete e[d]}}});var Ea=/alpha\([^)]*\)/i,gb=/opacity=([^)]*)/,hb=/-([a-z])/ig,ib=/([A-Z])/g,Fa=/^-?\d+(?:px)?$/i, -jb=/^-?\d/,kb={position:"absolute",visibility:"hidden",display:"block"},Pa=["Left","Right"],Qa=["Top","Bottom"],W,Ga,aa,lb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===B)return this;return c.access(this,a,b,true,function(d,e,f){return f!==B?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true, -zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),l=a.style,k=c.cssHooks[h];b=c.cssProps[h]||h;if(d!==B){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!k||!("set"in k)||(d=k.set(a,d))!==B)try{l[b]=d}catch(o){}}}else{if(k&&"get"in k&&(f=k.get(a,false,e))!==B)return f;return l[b]}}},css:function(a,b,d){var e,f=c.camelCase(b), -h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==B)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]=e[f]},camelCase:function(a){return a.replace(hb,lb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=oa(d,b,f);else c.swap(d,kb,function(){h=oa(d,b,f)});if(h<=0){h=W(d,b,b);if(h==="0px"&&aa)h=aa(d,b,b); -if(h!=null)return h===""||h==="auto"?"0px":h}if(h<0||h==null){h=d.style[b];return h===""||h==="auto"?"0px":h}return typeof h==="string"?h:h+"px"}},set:function(d,e){if(Fa.test(e)){e=parseFloat(e);if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return gb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f= -d.filter||"";d.filter=Ea.test(f)?f.replace(Ea,e):d.filter+" "+e}};if(t.defaultView&&t.defaultView.getComputedStyle)Ga=function(a,b,d){var e;d=d.replace(ib,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return B;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};if(t.documentElement.currentStyle)aa=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b],h=a.style;if(!Fa.test(f)&&jb.test(f)){d=h.left; -e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f===""?"auto":f};W=Ga||aa;if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var mb=c.now(),nb=/)<[^<]*)*<\/script>/gi, -ob=/^(?:select|textarea)/i,pb=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,qb=/^(?:GET|HEAD)$/,Ra=/\[\]$/,T=/\=\?(&|$)/,ja=/\?/,rb=/([?&])_=[^&]*/,sb=/^(\w+:)?\/\/([^\/?#]+)/,tb=/%20/g,ub=/#.*$/,Ha=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!=="string"&&Ha)return Ha.apply(this,arguments);else if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var f=a.slice(e,a.length);a=a.slice(0,e)}e="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b=== -"object"){b=c.param(b,c.ajaxSettings.traditional);e="POST"}var h=this;c.ajax({url:a,type:e,dataType:"html",data:b,complete:function(l,k){if(k==="success"||k==="notmodified")h.html(f?c("
").append(l.responseText.replace(nb,"")).find(f):l.responseText);d&&h.each(d,[l.responseText,k,l])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&& -!this.disabled&&(this.checked||ob.test(this.nodeName)||pb.test(this.type))}).map(function(a,b){var d=c(this).val();return d==null?null:c.isArray(d)?c.map(d,function(e){return{name:b.name,value:e}}):{name:b.name,value:d}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:e})}, -getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:e})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return new E.XMLHttpRequest},accepts:{xml:"application/xml, text/xml",html:"text/html", -script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},ajax:function(a){var b=c.extend(true,{},c.ajaxSettings,a),d,e,f,h=b.type.toUpperCase(),l=qb.test(h);b.url=b.url.replace(ub,"");b.context=a&&a.context!=null?a.context:b;if(b.data&&b.processData&&typeof b.data!=="string")b.data=c.param(b.data,b.traditional);if(b.dataType==="jsonp"){if(h==="GET")T.test(b.url)||(b.url+=(ja.test(b.url)?"&":"?")+(b.jsonp||"callback")+"=?");else if(!b.data|| -!T.test(b.data))b.data=(b.data?b.data+"&":"")+(b.jsonp||"callback")+"=?";b.dataType="json"}if(b.dataType==="json"&&(b.data&&T.test(b.data)||T.test(b.url))){d=b.jsonpCallback||"jsonp"+mb++;if(b.data)b.data=(b.data+"").replace(T,"="+d+"$1");b.url=b.url.replace(T,"="+d+"$1");b.dataType="script";var k=E[d];E[d]=function(m){if(c.isFunction(k))k(m);else{E[d]=B;try{delete E[d]}catch(p){}}f=m;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);r&&r.removeChild(A)}}if(b.dataType==="script"&&b.cache===null)b.cache= -false;if(b.cache===false&&l){var o=c.now(),x=b.url.replace(rb,"$1_="+o);b.url=x+(x===b.url?(ja.test(b.url)?"&":"?")+"_="+o:"")}if(b.data&&l)b.url+=(ja.test(b.url)?"&":"?")+b.data;b.global&&c.active++===0&&c.event.trigger("ajaxStart");o=(o=sb.exec(b.url))&&(o[1]&&o[1].toLowerCase()!==location.protocol||o[2].toLowerCase()!==location.host);if(b.dataType==="script"&&h==="GET"&&o){var r=t.getElementsByTagName("head")[0]||t.documentElement,A=t.createElement("script");if(b.scriptCharset)A.charset=b.scriptCharset; -A.src=b.url;if(!d){var C=false;A.onload=A.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);A.onload=A.onreadystatechange=null;r&&A.parentNode&&r.removeChild(A)}}}r.insertBefore(A,r.firstChild);return B}var J=false,w=b.xhr();if(w){b.username?w.open(h,b.url,b.async,b.username,b.password):w.open(h,b.url,b.async);try{if(b.data!=null&&!l||a&&a.contentType)w.setRequestHeader("Content-Type", -b.contentType);if(b.ifModified){c.lastModified[b.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[b.url]);c.etag[b.url]&&w.setRequestHeader("If-None-Match",c.etag[b.url])}o||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",b.dataType&&b.accepts[b.dataType]?b.accepts[b.dataType]+", */*; q=0.01":b.accepts._default)}catch(I){}if(b.beforeSend&&b.beforeSend.call(b.context,w,b)===false){b.global&&c.active--===1&&c.event.trigger("ajaxStop");w.abort();return false}b.global&& -c.triggerGlobal(b,"ajaxSend",[w,b]);var L=w.onreadystatechange=function(m){if(!w||w.readyState===0||m==="abort"){J||c.handleComplete(b,w,e,f);J=true;if(w)w.onreadystatechange=c.noop}else if(!J&&w&&(w.readyState===4||m==="timeout")){J=true;w.onreadystatechange=c.noop;e=m==="timeout"?"timeout":!c.httpSuccess(w)?"error":b.ifModified&&c.httpNotModified(w,b.url)?"notmodified":"success";var p;if(e==="success")try{f=c.httpData(w,b.dataType,b)}catch(q){e="parsererror";p=q}if(e==="success"||e==="notmodified")d|| -c.handleSuccess(b,w,e,f);else c.handleError(b,w,e,p);d||c.handleComplete(b,w,e,f);m==="timeout"&&w.abort();if(b.async)w=null}};try{var g=w.abort;w.abort=function(){w&&Function.prototype.call.call(g,w);L("abort")}}catch(i){}b.async&&b.timeout>0&&setTimeout(function(){w&&!J&&L("timeout")},b.timeout);try{w.send(l||b.data==null?null:b.data)}catch(n){c.handleError(b,w,null,n);c.handleComplete(b,w,e,f)}b.async||L();return w}},param:function(a,b){var d=[],e=function(h,l){l=c.isFunction(l)?l():l;d[d.length]= -encodeURIComponent(h)+"="+encodeURIComponent(l)};if(b===B)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){e(this.name,this.value)});else for(var f in a)da(f,a[f],b,e);return d.join("&").replace(tb,"+")}});c.extend({active:0,lastModified:{},etag:{},handleError:function(a,b,d,e){a.error&&a.error.call(a.context,b,d,e);a.global&&c.triggerGlobal(a,"ajaxError",[b,a,e])},handleSuccess:function(a,b,d,e){a.success&&a.success.call(a.context,e,d,b);a.global&&c.triggerGlobal(a,"ajaxSuccess", -[b,a])},handleComplete:function(a,b,d){a.complete&&a.complete.call(a.context,b,d);a.global&&c.triggerGlobal(a,"ajaxComplete",[b,a]);a.global&&c.active--===1&&c.event.trigger("ajaxStop")},triggerGlobal:function(a,b,d){(a.context&&a.context.url==null?c(a.context):c.event).trigger(b,d)},httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"), -e=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(e)c.etag[b]=e;return a.status===304},httpData:function(a,b,d){var e=a.getResponseHeader("content-type")||"",f=b==="xml"||!b&&e.indexOf("xml")>=0;a=f?a.responseXML:a.responseText;f&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&e.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&e.indexOf("javascript")>=0)c.globalEval(a);return a}}); -if(E.ActiveXObject)c.ajaxSettings.xhr=function(){if(E.location.protocol!=="file:")try{return new E.XMLHttpRequest}catch(a){}try{return new E.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}};c.support.ajax=!!c.ajaxSettings.xhr();var ea={},vb=/^(?:toggle|show|hide)$/,wb=/^([+\-]=)?([\d+.\-]+)(.*)$/,ba,pa=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b,d){if(a||a===0)return this.animate(S("show", -3),a,b,d);else{d=0;for(var e=this.length;d=0;e--)if(d[e].elem===this){b&&d[e](true);d.splice(e,1)}});b||this.dequeue();return this}});c.each({slideDown:S("show",1),slideUp:S("hide",1),slideToggle:S("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){c.fn[a]=function(d,e,f){return this.animate(b, -d,e,f)}});c.extend({speed:function(a,b,d){var e=a&&typeof a==="object"?c.extend({},a):{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};e.duration=c.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in c.fx.speeds?c.fx.speeds[e.duration]:c.fx.speeds._default;e.old=e.complete;e.complete=function(){e.queue!==false&&c(this).dequeue();c.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,d,e){return d+e*a},swing:function(a,b,d,e){return(-Math.cos(a* -Math.PI)/2+0.5)*e+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a=parseFloat(c.css(this.elem,this.prop));return a&&a>-1E4?a:0},custom:function(a,b,d){function e(l){return f.step(l)} -var f=this,h=c.fx;this.startTime=c.now();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;e.elem=this.elem;if(e()&&c.timers.push(e)&&!ba)ba=setInterval(h.tick,h.interval)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true; -this.custom(this.cur(),0)},step:function(a){var b=c.now(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var e in this.options.curAnim)if(this.options.curAnim[e]!==true)d=false;if(d){if(this.options.overflow!=null&&!c.support.shrinkWrapBlocks){var f=this.elem,h=this.options;c.each(["","X","Y"],function(k,o){f.style["overflow"+o]=h.overflow[k]})}this.options.hide&&c(this.elem).hide();if(this.options.hide|| -this.options.show)for(var l in this.options.curAnim)c.style(this.elem,l,this.options.orig[l]);this.options.complete.call(this.elem)}return false}else{a=b-this.startTime;this.state=a/this.options.duration;b=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||b](this.state,a,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a= -c.timers,b=0;b-1;e={};var x={};if(o)x=f.position();l=o?x.top:parseInt(l,10)||0;k=o?x.left:parseInt(k,10)||0;if(c.isFunction(b))b=b.call(a,d,h);if(b.top!=null)e.top=b.top-h.top+l;if(b.left!=null)e.left=b.left-h.left+k;"using"in b?b.using.call(a, -e):f.css(e)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),e=Ia.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.css(a,"marginTop"))||0;d.left-=parseFloat(c.css(a,"marginLeft"))||0;e.top+=parseFloat(c.css(b[0],"borderTopWidth"))||0;e.left+=parseFloat(c.css(b[0],"borderLeftWidth"))||0;return{top:d.top-e.top,left:d.left-e.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||t.body;a&&!Ia.test(a.nodeName)&& -c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(e){var f=this[0],h;if(!f)return null;if(e!==B)return this.each(function(){if(h=fa(this))h.scrollTo(!a?e:c(h).scrollLeft(),a?e:c(h).scrollTop());else this[d]=e});else return(h=fa(f))?"pageXOffset"in h?h[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&h.document.documentElement[d]||h.document.body[d]:f[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase(); -c.fn["inner"+b]=function(){return this[0]?parseFloat(c.css(this[0],d,"padding")):null};c.fn["outer"+b]=function(e){return this[0]?parseFloat(c.css(this[0],d,e?"margin":"border")):null};c.fn[d]=function(e){var f=this[0];if(!f)return e==null?null:this;if(c.isFunction(e))return this.each(function(l){var k=c(this);k[d](e.call(this,l,k[d]()))});if(c.isWindow(f))return f.document.compatMode==="CSS1Compat"&&f.document.documentElement["client"+b]||f.document.body["client"+b];else if(f.nodeType===9)return Math.max(f.documentElement["client"+ -b],f.body["scroll"+b],f.documentElement["scroll"+b],f.body["offset"+b],f.documentElement["offset"+b]);else if(e===B){f=c.css(f,d);var h=parseFloat(f);return c.isNaN(h)?f:h}else return this.css(d,typeof e==="string"?e:e+"px")}})})(window); diff --git a/samples/eventbus/src/main/webapp/jquery.json-2.2.min.js b/samples/eventbus/src/main/webapp/jquery.json-2.2.min.js deleted file mode 100644 index bad4a0a..0000000 --- a/samples/eventbus/src/main/webapp/jquery.json-2.2.min.js +++ /dev/null @@ -1,31 +0,0 @@ - -(function($){$.toJSON=function(o) -{if(typeof(JSON)=='object'&&JSON.stringify) -return JSON.stringify(o);var type=typeof(o);if(o===null) -return"null";if(type=="undefined") -return undefined;if(type=="number"||type=="boolean") -return o+"";if(type=="string") -return $.quoteString(o);if(type=='object') -{if(typeof o.toJSON=="function") -return $.toJSON(o.toJSON());if(o.constructor===Date) -{var month=o.getUTCMonth()+1;if(month<10)month='0'+month;var day=o.getUTCDate();if(day<10)day='0'+day;var year=o.getUTCFullYear();var hours=o.getUTCHours();if(hours<10)hours='0'+hours;var minutes=o.getUTCMinutes();if(minutes<10)minutes='0'+minutes;var seconds=o.getUTCSeconds();if(seconds<10)seconds='0'+seconds;var milli=o.getUTCMilliseconds();if(milli<100)milli='0'+milli;if(milli<10)milli='0'+milli;return'"'+year+'-'+month+'-'+day+'T'+ -hours+':'+minutes+':'+seconds+'.'+milli+'Z"';} -if(o.constructor===Array) -{var ret=[];for(var i=0;i4.0.0 - com.glines.socketio + com.codeminders.socketio socketio - 0.2.2 + 0.3 .. - com.glines.socketio.sample + com.codeminders.socketio.sample socketio-sample pom @@ -19,11 +19,7 @@ Socket.IO Java Samples - echo - broadcast - eventbus chat - chat-gwt From fe3c1cd5c6b26db4d43117c507aa10745a616cb4 Mon Sep 17 00:00:00 2001 From: Alexander Sova Date: Tue, 29 Dec 2015 16:21:31 -0800 Subject: [PATCH 04/85] Handshake and connection sequence are implemented PING/PONG exchange are implemented Websocket only for now --- TODO | 9 + .../socketio/server/AbstractTransport.java | 2 + .../server/AbstractTransportConnection.java | 8 + .../socketio/server/DefaultSession.java | 176 +++++++++++---- .../socketio/server/EngineIOPacket.java | 71 ++++++ .../socketio/server/EngineIOProtocol.java | 82 +++++++ .../server/ServletBasedSocketIOConfig.java | 52 +++-- .../socketio/server/SessionManager.java | 2 +- .../socketio/server/SocketIOConfig.java | 12 +- .../socketio/server/SocketIOFrame.java | 9 +- .../socketio/server/SocketIOInbound.java | 11 +- .../socketio/server/SocketIOPacket.java | 83 +++++++ .../socketio/server/SocketIOProtocol.java | 36 +++ .../socketio/server/SocketIOServlet.java | 75 +++--- .../socketio/server/SocketIOSession.java | 26 +-- .../server/SocketIOSessionManager.java | 47 +++- .../socketio/server/TransportConnection.java | 4 +- .../transport/AbstractHttpTransport.java | 89 ++++---- .../transport/AbstractTransportProvider.java | 4 +- .../transport/FlashSocketTransport.java | 54 +++-- .../transport/JSONPPollingTransport.java | 2 +- .../server/transport/XHRPollingTransport.java | 5 +- .../socketio/util/DefaultLoader.java | 86 ------- .../socketio/util/JdkOverLog4j.java | 7 - .../com/codeminders/socketio/util/Loader.java | 39 ---- .../socketio/util/ServiceClassLoader.java | 207 ----------------- .../com/codeminders/socketio/util/Web.java | 2 + .../JettyContinuationTransportConnection.java | 14 +- .../transport/jetty/JettySocketIOServlet.java | 11 +- .../jetty/JettyWebSocketTransport.java | 89 +++----- .../JettyWebSocketTransportConnection.java | 213 +++++++++++------- .../sample/chat/ChatSocketServlet.java | 37 ++- .../com.codeminders.socketio.server.Transport | 1 - ...inders.socketio.server.TransportConnection | 2 - .../chat/src/main/resources/webapp/chat.html | 15 +- 35 files changed, 870 insertions(+), 712 deletions(-) create mode 100644 TODO create mode 100644 core/src/main/java/com/codeminders/socketio/server/EngineIOPacket.java create mode 100644 core/src/main/java/com/codeminders/socketio/server/EngineIOProtocol.java create mode 100644 core/src/main/java/com/codeminders/socketio/server/SocketIOPacket.java create mode 100644 core/src/main/java/com/codeminders/socketio/server/SocketIOProtocol.java delete mode 100644 core/src/main/java/com/codeminders/socketio/util/DefaultLoader.java delete mode 100644 core/src/main/java/com/codeminders/socketio/util/Loader.java delete mode 100644 core/src/main/java/com/codeminders/socketio/util/ServiceClassLoader.java delete mode 100644 samples/chat/src/main/resources/META-INF/services/com.codeminders.socketio.server.Transport delete mode 100644 samples/chat/src/main/resources/META-INF/services/com.codeminders.socketio.server.TransportConnection diff --git a/TODO b/TODO new file mode 100644 index 0000000..4be25e5 --- /dev/null +++ b/TODO @@ -0,0 +1,9 @@ +* binary data in EngineIO +* CORS support for XHR Polling +* upgrade transport +* payload (multi-packets messages) +* use Guava instead of util.IO +* use com.fasterxml.jackson instead of util.JSON +* Socket.IO Multiplexing (subscription to namespace) +* Implement broadcasting +* handle b64 flag \ No newline at end of file diff --git a/core/src/main/java/com/codeminders/socketio/server/AbstractTransport.java b/core/src/main/java/com/codeminders/socketio/server/AbstractTransport.java index 479f46b..2d2ad54 100644 --- a/core/src/main/java/com/codeminders/socketio/server/AbstractTransport.java +++ b/core/src/main/java/com/codeminders/socketio/server/AbstractTransport.java @@ -24,6 +24,8 @@ */ package com.codeminders.socketio.server; +import com.codeminders.socketio.common.SocketIOException; + import javax.servlet.ServletConfig; /** diff --git a/core/src/main/java/com/codeminders/socketio/server/AbstractTransportConnection.java b/core/src/main/java/com/codeminders/socketio/server/AbstractTransportConnection.java index 0bf9af3..f77edd4 100644 --- a/core/src/main/java/com/codeminders/socketio/server/AbstractTransportConnection.java +++ b/core/src/main/java/com/codeminders/socketio/server/AbstractTransportConnection.java @@ -24,6 +24,8 @@ */ package com.codeminders.socketio.server; +import com.codeminders.socketio.common.SocketIOException; + /** * @author Mathieu Carbou */ @@ -58,4 +60,10 @@ protected void init() { @Override public void disconnectWhenEmpty() { } + + public void send(SocketIOPacket packet) throws SocketIOException + { + send(EngineIOProtocol.createMessagePacket(SocketIOProtocol.encode(packet))); + } + } diff --git a/core/src/main/java/com/codeminders/socketio/server/DefaultSession.java b/core/src/main/java/com/codeminders/socketio/server/DefaultSession.java index 2e28740..0c082b7 100644 --- a/core/src/main/java/com/codeminders/socketio/server/DefaultSession.java +++ b/core/src/main/java/com/codeminders/socketio/server/DefaultSession.java @@ -29,9 +29,9 @@ import com.codeminders.socketio.common.ConnectionState; import com.codeminders.socketio.common.DisconnectReason; +import java.util.Arrays; import java.util.Map; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; import java.util.logging.Logger; @@ -45,11 +45,13 @@ class DefaultSession implements SocketIOSession { private final SocketIOSessionManager socketIOSessionManager; private final String sessionId; - private final AtomicLong messageId = new AtomicLong(0); - private final Map attributes = new ConcurrentHashMap(); + private final Map attributes = new ConcurrentHashMap<>(); - private SocketIOInbound inbound; - private TransportConnection handler; + //TODO: rename to something more telling + //This is callback/listener interface set by library user + private SocketIOInbound inbound; + + private TransportConnection connection; private ConnectionState state = ConnectionState.CONNECTING; private long hbDelay; private SessionTask hbDelayTask; @@ -57,8 +59,9 @@ class DefaultSession implements SocketIOSession { private SessionTask timeoutTask; private boolean timedout; private String closeId; - - DefaultSession(SocketIOSessionManager socketIOSessionManager, SocketIOInbound inbound, String sessionId) { + + DefaultSession(SocketIOSessionManager socketIOSessionManager, SocketIOInbound inbound, String sessionId) + { this.socketIOSessionManager = socketIOSessionManager; this.inbound = inbound; this.sessionId = sessionId; @@ -90,8 +93,8 @@ public SocketIOInbound getInbound() { } @Override - public TransportConnection getTransportHandler() { - return handler; + public TransportConnection getConnection() { + return connection; } private void onTimeout() { @@ -101,7 +104,7 @@ private void onTimeout() { timedout = true; state = ConnectionState.CLOSED; onDisconnect(DisconnectReason.TIMEOUT); - handler.abort(); + connection.abort(); } } @@ -126,15 +129,16 @@ public void clearTimeoutTimer() { } } + //TODO: remove heartbeat from server. in SocketI.IO client is sending periodic pings private void sendHeartBeat() { if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Session[" + sessionId + "]: send heartbeat "); try { - handler.sendMessage(new SocketIOFrame(SocketIOFrame.FrameType.HEARTBEAT, 0, "")); + connection.sendMessage(new SocketIOFrame(SocketIOFrame.FrameType.HEARTBEAT, 0, "")); } catch (SocketIOException e) { if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, "handler.sendMessage failed: ", e); - handler.abort(); + LOGGER.log(Level.FINE, "connection.sendMessage failed: ", e); + connection.abort(); } startTimeoutTimer(); } @@ -185,14 +189,86 @@ public void startClose() { state = ConnectionState.CLOSING; closeId = "server"; try { - handler.sendMessage(new SocketIOFrame(SocketIOFrame.FrameType.CLOSE, 0, closeId)); + connection.sendMessage(new SocketIOFrame(SocketIOFrame.FrameType.CLOSE, 0, closeId)); } catch (SocketIOException e) { if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, "handler.sendMessage failed: ", e); - handler.abort(); + LOGGER.log(Level.FINE, "connection.sendMessage failed: ", e); + connection.abort(); + } + } + + @Override + public void onPacket(EngineIOPacket packet) + { + switch (packet.getType()) + { + case OPEN: + case PONG: + // ignore. OPEN and PONG are server -> client + return; + case MESSAGE: + startTimeoutTimer(); + try + { + onPacket(SocketIOProtocol.decode(packet.getData())); + } + catch (SocketIOProtocolException e) + { + if(LOGGER.isLoggable(Level.WARNING)) + LOGGER.log(Level.WARNING, "Invalid SIO packet: " + packet.getData(), e); + } + return; + + case PING: + startTimeoutTimer(); + onPing(packet.getData()); + return; + + case CLOSE: + startClose(); + return; + + default: + throw new UnsupportedOperationException("EIO Packet " + packet + " is not implemented yet"); + } } + @Override + public void onPacket(SocketIOPacket packet) + { + switch (packet.getType()) + { + case CONNECT: + // ignore. server -> client + return; + case EVENT: + Object json = JSON.parse(packet.getData()); + if(!(json instanceof Object[]) || ((Object[])json).length == 0) + { + if (LOGGER.isLoggable(Level.WARNING)) + LOGGER.log(Level.WARNING, "Invalid JSON in EVENT message packet: " + packet.getData()); + return; + } + + Object[] args = (Object[])json; + if(!(args[0] instanceof String)) + { + if (LOGGER.isLoggable(Level.WARNING)) + LOGGER.log(Level.WARNING, "Invalid JSON in EVENT message packet. First argument must be string: " + packet.getData()); + return; + } + onEvent(args[0].toString(), Arrays.copyOfRange(args, 1, args.length-1)); + + return; + + default: + throw new UnsupportedOperationException("SocketIO packet " + packet.getType() + " is not implemented yet"); + + } + } + + //TODO: remove. old method @Override public void onMessage(SocketIOFrame message) { switch (message.getFrameType()) { @@ -223,9 +299,9 @@ public void onMessage(SocketIOFrame message) { Object args = json.get("args"); if(args.getClass().isArray()) { for(Object o: (Object[])args) - onEvent(name, o.toString()); + onEvent(name, new String[] { o.toString() }); } else - onEvent(name, JSON.toString(args)); + onEvent(name, new String[] { JSON.toString(args) }); } catch(ClassCastException | NullPointerException e) { LOGGER.log(Level.WARNING, "Invalid payload format: ", e); @@ -238,20 +314,19 @@ public void onMessage(SocketIOFrame message) { } @Override - public void onPing(String data) { - try { - handler.sendMessage(new SocketIOFrame(SocketIOFrame.FrameType.CONNECT, 0, data)); - } catch (SocketIOException e) { - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, "handler.sendMessage failed: ", e); - handler.abort(); + public void onPing(String data) + { + try + { + connection.send(EngineIOProtocol.createPongPacket(data)); } - } + catch (SocketIOException e) + { + if (LOGGER.isLoggable(Level.FINE)) + LOGGER.log(Level.FINE, "connection.send failed: ", e); - //TODO: this is never called. need to inverstigate - @Override - public void onPong(String data) { - clearTimeoutTimer(); + connection.abort(); + } } @Override @@ -260,27 +335,27 @@ public void onClose(String data) { if (closeId != null && closeId.equals(data)) { state = ConnectionState.CLOSED; onDisconnect(DisconnectReason.CLOSED); - handler.abort(); + connection.abort(); } else { try { - handler.sendMessage(new SocketIOFrame(SocketIOFrame.FrameType.CLOSE, 0, data)); + connection.sendMessage(new SocketIOFrame(SocketIOFrame.FrameType.CLOSE, 0, data)); } catch (SocketIOException e) { if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, "handler.sendMessage failed: ", e); - handler.abort(); + LOGGER.log(Level.FINE, "connection.sendMessage failed: ", e); + connection.abort(); } } } else { state = ConnectionState.CLOSING; try { - handler.sendMessage(new SocketIOFrame(SocketIOFrame.FrameType.CLOSE, 0, data)); - handler.disconnectWhenEmpty(); + connection.sendMessage(new SocketIOFrame(SocketIOFrame.FrameType.CLOSE, 0, data)); + connection.disconnectWhenEmpty(); if ("client".equals(data)) onDisconnect(DisconnectReason.CLOSED_REMOTELY); } catch (SocketIOException e) { if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, "handler.sendMessage failed: ", e); - handler.abort(); + LOGGER.log(Level.FINE, "connection.sendMessage failed: ", e); + connection.abort(); } } } @@ -302,8 +377,8 @@ public void onConnect(TransportConnection connection) { state = ConnectionState.CLOSED; inbound = null; socketIOSessionManager.socketIOSessions.remove(sessionId); - } else if (this.handler == null) { - this.handler = connection; + } else if (this.connection == null) { + this.connection = connection; if (inbound == null) { state = ConnectionState.CLOSED; connection.abort(); @@ -337,7 +412,7 @@ public void onMessage(String message) { } @Override - public void onEvent(String name, String args) { + public void onEvent(String name, Object[] args) { if (inbound != null) { try { inbound.onEvent(name, args); @@ -368,19 +443,22 @@ public void onDisconnect(DisconnectReason reason) { } @Override - public void onShutdown() { + public void onShutdown() + { if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Session[" + sessionId + "]: onShutdown"); - if (inbound != null) { - if (state == ConnectionState.CLOSING) { - if (closeId != null) { + + if (inbound != null) + { + if (state == ConnectionState.CLOSING) + { + if (closeId != null) onDisconnect(DisconnectReason.CLOSE_FAILED); - } else { + else onDisconnect(DisconnectReason.CLOSED_REMOTELY); - } - } else { - onDisconnect(DisconnectReason.ERROR); } + else + onDisconnect(DisconnectReason.ERROR); } socketIOSessionManager.socketIOSessions.remove(sessionId); } diff --git a/core/src/main/java/com/codeminders/socketio/server/EngineIOPacket.java b/core/src/main/java/com/codeminders/socketio/server/EngineIOPacket.java new file mode 100644 index 0000000..e8384fa --- /dev/null +++ b/core/src/main/java/com/codeminders/socketio/server/EngineIOPacket.java @@ -0,0 +1,71 @@ +package com.codeminders.socketio.server; + +/** + * @author Alexander Sova + */ +public class EngineIOPacket +{ + public enum Type + { + OPEN(0), + CLOSE(1), + PING(2), + PONG(3), + MESSAGE(4), + UPGRADE(5), + NOOP(6), + UNKNOWN(7); + + private int value; + + Type(int value) + { + this.value = value; + } + + public int value() + { + return value; + } + + public static Type fromInt(int i) + { + switch (i) + { + case 0: return OPEN; + case 1: return CLOSE; + case 2: return PING; + case 3: return PONG; + case 4: return MESSAGE; + case 5: return UPGRADE; + case 6: return NOOP; + default: + return UNKNOWN; + } + } + } + + private Type type; + private String data; + + public Type getType() + { + return type; + } + + public String getData() + { + return data; + } + + public EngineIOPacket(Type type, String data) + { + this.type = type; + this.data = data; + } + + public EngineIOPacket(Type type) + { + this(type, ""); + } +} diff --git a/core/src/main/java/com/codeminders/socketio/server/EngineIOProtocol.java b/core/src/main/java/com/codeminders/socketio/server/EngineIOProtocol.java new file mode 100644 index 0000000..b90c6aa --- /dev/null +++ b/core/src/main/java/com/codeminders/socketio/server/EngineIOProtocol.java @@ -0,0 +1,82 @@ +package com.codeminders.socketio.server; + +import com.codeminders.socketio.util.JSON; + +import java.util.HashMap; +import java.util.Map; + +/** + * Implementation of Engine.IO Protocol version 3 + * + * @author Alexander Sova + */ +public final class EngineIOProtocol +{ + public static final String SESSION_ID = "sid"; + public static final String TRANSPORT = "transport"; + public static final String JSONP_INDEX = "j"; + public static final String BASE64_FLAG = "b64"; + + private EngineIOProtocol() + { + } + + public static String encode(EngineIOPacket packet) + { + return String.valueOf(packet.getType().value()) + packet.getData(); + } + + //TODO: do we need separate exception for EIO protocol? + public static EngineIOPacket decode(String raw) + throws SocketIOProtocolException + { + assert (raw != null); + + if(raw.length() < 1) + throw new SocketIOProtocolException("Empty EIO packet"); + + try + { + return new EngineIOPacket( + EngineIOPacket.Type.fromInt(Integer.parseUnsignedInt(raw.substring(0, 1))), + raw.substring(1)); + } + catch (NumberFormatException e) + { + throw new SocketIOProtocolException("Invalid EIO packet type: " + raw); + } + } + + public static EngineIOPacket createHandshakePacket(String session_id, + String[] upgrades, + long ping_interval, + long ping_timeout) + { + Map map = new HashMap<>(); + map.put("sid", session_id); + map.put("upgrades", upgrades); + map.put("pingInterval", ping_interval); + map.put("pingTimeout", ping_timeout); + return new EngineIOPacket(EngineIOPacket.Type.OPEN, JSON.toString(map)); + } + + public static EngineIOPacket createOpenPacket() + { + return new EngineIOPacket(EngineIOPacket.Type.OPEN); + } + + public static EngineIOPacket createPingPacket(String data) + { + return new EngineIOPacket(EngineIOPacket.Type.PING, data); + } + + public static EngineIOPacket createPongPacket(String data) + { + return new EngineIOPacket(EngineIOPacket.Type.PONG, data); + } + + public static EngineIOPacket createMessagePacket(String data) + { + return new EngineIOPacket(EngineIOPacket.Type.MESSAGE, data); + } +} diff --git a/core/src/main/java/com/codeminders/socketio/server/ServletBasedSocketIOConfig.java b/core/src/main/java/com/codeminders/socketio/server/ServletBasedSocketIOConfig.java index 70cee11..64a86f1 100644 --- a/core/src/main/java/com/codeminders/socketio/server/ServletBasedSocketIOConfig.java +++ b/core/src/main/java/com/codeminders/socketio/server/ServletBasedSocketIOConfig.java @@ -1,19 +1,19 @@ /** * The MIT License * Copyright (c) 2010 Tad Glines - * + *

* Contributors: Ovea.com, Mycila.com - * + *

* 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 @@ -39,61 +39,78 @@ public final class ServletBasedSocketIOConfig implements SocketIOConfig private static final Logger LOGGER = Logger.getLogger(ServletBasedSocketIOConfig.class.getName()); private final ServletConfig config; - private final String namespace; + private final String namespace; - public ServletBasedSocketIOConfig(ServletConfig config, String namespace) { + public ServletBasedSocketIOConfig(ServletConfig config, String namespace) + { this.namespace = namespace; this.config = config; } @Override - public long getHeartbeatDelay(long def) { + public long getHeartbeatDelay(long def) + { return getLong(PARAM_HEARTBEAT_DELAY, def); } @Override - public long getHeartbeatTimeout(long def) { + public long getHeartbeatTimeout(long def) + { return getLong(PARAM_HEARTBEAT_TIMEOUT, def); } @Override - public long getTimeout(long def) { + public long getPingInterval(long def) + { + return getLong(PARAM_PING_INTERVAL, def); + } + + @Override + public long getTimeout(long def) + { return getLong(PARAM_TIMEOUT, def); } @Override - public int getBufferSize() { + public int getBufferSize() + { return getInt(PARAM_BUFFER_SIZE, DEFAULT_BUFFER_SIZE); } @Override - public int getMaxIdle() { + public int getMaxIdle() + { return getInt(PARAM_MAX_IDLE, DEFAULT_MAX_IDLE); } @Override - public int getInt(String param, int def) { + public int getInt(String param, int def) + { String v = getString(param); return v == null ? def : Integer.parseInt(v); } @Override - public long getLong(String param, long def) { + public long getLong(String param, long def) + { String v = getString(param); return v == null ? def : Long.parseLong(v); } @Override - public String getNamespace() { + public String getNamespace() + { return namespace; } @Override - public String getString(String param) { + public String getString(String param) + { if (LOGGER.isLoggable(Level.FINE)) LOGGER.fine("Getting InitParameter: " + namespace + "." + param); String v = config.getInitParameter(namespace + "." + param); - if (v == null) { + if (v == null) + { if (LOGGER.isLoggable(Level.FINE)) LOGGER.fine("Fallback to InitParameter: " + param); v = config.getInitParameter(param); @@ -102,7 +119,8 @@ public String getString(String param) { } @Override - public String getString(String param, String def) { + public String getString(String param, String def) + { String v = getString(param); return v == null ? def : v; } diff --git a/core/src/main/java/com/codeminders/socketio/server/SessionManager.java b/core/src/main/java/com/codeminders/socketio/server/SessionManager.java index 4fb1931..c1efc35 100644 --- a/core/src/main/java/com/codeminders/socketio/server/SessionManager.java +++ b/core/src/main/java/com/codeminders/socketio/server/SessionManager.java @@ -28,6 +28,6 @@ * @author Mathieu Carbou (mathieu.carbou@gmail.com) */ public interface SessionManager { - SocketIOSession createSession(SocketIOInbound inbound, String sessionId); + SocketIOSession createSession(SocketIOInbound inbound); SocketIOSession getSession(String sessionId); } diff --git a/core/src/main/java/com/codeminders/socketio/server/SocketIOConfig.java b/core/src/main/java/com/codeminders/socketio/server/SocketIOConfig.java index 2da879f..b0d81fc 100644 --- a/core/src/main/java/com/codeminders/socketio/server/SocketIOConfig.java +++ b/core/src/main/java/com/codeminders/socketio/server/SocketIOConfig.java @@ -31,8 +31,7 @@ */ public interface SocketIOConfig { - String PARAM_HEARTBEAT_DELAY = "heartbeat-delay"; - String PARAM_HEARTBEAT_TIMEOUT = "heartbeat-timeout"; + String PARAM_PING_INTERVAL = "pingInterval"; String PARAM_TIMEOUT = "timeout"; String PARAM_BUFFER_SIZE = "bufferSize"; @@ -41,11 +40,18 @@ public interface SocketIOConfig { int DEFAULT_BUFFER_SIZE = 8192; int DEFAULT_MAX_IDLE = 300 * 1000; + //TODO: retire heartbeat + String PARAM_HEARTBEAT_DELAY = "heartbeat-delay"; + String PARAM_HEARTBEAT_TIMEOUT = "heartbeat-timeout"; int DEFAULT_HEARTBEAT_INTERVAL = 25 * 1000; // 25s int DEFAULT_HEARTBEAT_TIMEOUT = 60 * 1000; // 60s - long getHeartbeatDelay(long def); long getHeartbeatTimeout(long def); + + int DEFAULT_PING_INTERVAL = 25 * 1000; // 25s + int DEFAULT_PING_TIMEOUT = 60 * 1000; // 60s + + long getPingInterval(long def); long getTimeout(long def); int getBufferSize(); int getMaxIdle(); diff --git a/core/src/main/java/com/codeminders/socketio/server/SocketIOFrame.java b/core/src/main/java/com/codeminders/socketio/server/SocketIOFrame.java index 59e38f8..6c7523b 100644 --- a/core/src/main/java/com/codeminders/socketio/server/SocketIOFrame.java +++ b/core/src/main/java/com/codeminders/socketio/server/SocketIOFrame.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.List; +//TODO: retire public class SocketIOFrame { public static final char SEPERATOR_CHAR = ':'; public static final char MESSAGE_SEPARATOR = '�'; // char code 65535 @@ -96,12 +97,12 @@ private static boolean isHexDigit(String str, int start, int end) { } public static List parse(String data) { - List messages = new ArrayList(); + List messages = new ArrayList<>(); // Parse the data and silently ignore any part that fails to parse properly. int messageEnd; int start = 0; - int end = 0; + int end; while (data.length() > start) { if (data.charAt(start) == MESSAGE_SEPARATOR) { start += 1; @@ -180,11 +181,11 @@ public SocketIOFrame(FrameType frameType, int messageType, String data) { public FrameType getFrameType() { return frameType; } - + public int getMessageType() { return messageType; } - + public String getData() { return data; } diff --git a/core/src/main/java/com/codeminders/socketio/server/SocketIOInbound.java b/core/src/main/java/com/codeminders/socketio/server/SocketIOInbound.java index 0b3069f..4187f4b 100644 --- a/core/src/main/java/com/codeminders/socketio/server/SocketIOInbound.java +++ b/core/src/main/java/com/codeminders/socketio/server/SocketIOInbound.java @@ -45,19 +45,12 @@ public interface SocketIOInbound { */ void onDisconnect(DisconnectReason reason, String errorMessage); - /** - * Called one per arriving message. - * - * @param messageType - * @param message - */ void onMessage(int messageType, String message); /** * Called one per arriving event. - * - * @param name event name + * @param name event name * @param args event payload */ - void onEvent(String name, String args); + void onEvent(String name, Object[] args); } diff --git a/core/src/main/java/com/codeminders/socketio/server/SocketIOPacket.java b/core/src/main/java/com/codeminders/socketio/server/SocketIOPacket.java new file mode 100644 index 0000000..08337a0 --- /dev/null +++ b/core/src/main/java/com/codeminders/socketio/server/SocketIOPacket.java @@ -0,0 +1,83 @@ +package com.codeminders.socketio.server; + +/** + * @author Alexander Sova + */ +public class SocketIOPacket +{ + public enum Type + { + CONNECT(0), + DISCONNECT(1), + EVENT(2), + ACK(3), + ERROR(4), + BINARY_EVENT(5), + BINARY_ACK(6), + UNKNOWN(7); + + private int value; + + Type(int value) + { + this.value = value; + } + + public int value() + { + return value; + } + + public static Type fromInt(int i) + { + switch (i) + { + case 0: return CONNECT; + case 1: return DISCONNECT; + case 2: return EVENT; + case 3: return ACK; + case 4: return ERROR; + case 5: return BINARY_EVENT; + case 6: return BINARY_ACK; + default: + return UNKNOWN; + } + } + } + + private Type type; + private String data; + private String namespace; + + public Type getType() + { + return type; + } + + public String getData() + { + return data; + } + + public String getNamespace() + { + return namespace; + } + + public SocketIOPacket(Type type) + { + this(type, "", "/"); + } + + public SocketIOPacket(Type type, String data) + { + this(type, data, "/"); + } + + public SocketIOPacket(Type type, String data, String namespace) + { + this.type = type; + this.data = data; + this.namespace = namespace; + } +} diff --git a/core/src/main/java/com/codeminders/socketio/server/SocketIOProtocol.java b/core/src/main/java/com/codeminders/socketio/server/SocketIOProtocol.java new file mode 100644 index 0000000..c618162 --- /dev/null +++ b/core/src/main/java/com/codeminders/socketio/server/SocketIOProtocol.java @@ -0,0 +1,36 @@ +package com.codeminders.socketio.server; + +/** + * @author Alexander Sova + */ +public final class SocketIOProtocol +{ + private SocketIOProtocol() + { + } + + public static String encode(SocketIOPacket packet) + { + return String.valueOf(packet.getType().value()) + packet.getData(); + } + + public static SocketIOPacket decode(String raw) + throws SocketIOProtocolException + { + assert (raw != null); + + if(raw.length() < 1) + throw new SocketIOProtocolException("Empty SocketIO packet"); + + try + { + return new SocketIOPacket( + SocketIOPacket.Type.fromInt(Integer.parseUnsignedInt(raw.substring(0, 1))), + raw.substring(1)); + } + catch (NumberFormatException e) + { + throw new SocketIOProtocolException("Invalid EIO packet type: " + raw); + } + } +} diff --git a/core/src/main/java/com/codeminders/socketio/server/SocketIOServlet.java b/core/src/main/java/com/codeminders/socketio/server/SocketIOServlet.java index 4865a13..d7aa524 100644 --- a/core/src/main/java/com/codeminders/socketio/server/SocketIOServlet.java +++ b/core/src/main/java/com/codeminders/socketio/server/SocketIOServlet.java @@ -1,19 +1,19 @@ /** * The MIT License * Copyright (c) 2010 Tad Glines - * + *

* Contributors: Ovea.com, Mycila.com - * + *

* 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 @@ -37,18 +37,20 @@ import java.util.logging.Level; import java.util.logging.Logger; -public abstract class SocketIOServlet extends HttpServlet { +public abstract class SocketIOServlet extends HttpServlet +{ - private static final Logger LOGGER = Logger.getLogger(SocketIOServlet.class.getName()); - private static final long serialVersionUID = 2L; + private static final Logger LOGGER = Logger.getLogger(SocketIOServlet.class.getName()); + private static final long serialVersionUID = 2L; private final SocketIOSessionManager sessionManager = new SocketIOSessionManager(); private TransportProvider transportProvider; - public final static String MAX_TEXT_MESSAGE_SIZE = "maxTextMessageSize"; + public final static String MAX_TEXT_MESSAGE_SIZE = "maxTextMessageSize"; - public void setTransportProvider(TransportProvider transportProvider) { + public void setTransportProvider(TransportProvider transportProvider) + { assert (transportProvider != null); this.transportProvider = transportProvider; } @@ -57,7 +59,14 @@ public void setTransportProvider(TransportProvider transportProvider) { public void init() throws ServletException { if (LOGGER.isLoggable(Level.INFO)) - LOGGER.log(Level.INFO, "Transports: " + transportProvider.getTransports()); + if (transportProvider != null) + { + LOGGER.log(Level.INFO, "Transports: " + transportProvider.getTransports()); + if(transportProvider.getTransports().size() == 0) + LOGGER.log(Level.INFO, "No transport defined. TransportProvider.init() should be called."); + } + else + LOGGER.log(Level.INFO, "No Transport Provider is set"); } @Override @@ -92,21 +101,20 @@ private void serve(HttpServletRequest request, HttpServletResponse response) { //TODO: Need to remove this check. Transport is not defined in path anymore String path = request.getPathInfo(); - if (path == null || path.length() == 0 || "/".equals(path)) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing SocketIO transport"); - return; - } + if (path.startsWith("/")) path = path.substring(1); String[] parts = path.split("/"); - if ("GET".equals(request.getMethod()) && "socket.io.js".equals(parts[0])) { + if ("GET".equals(request.getMethod()) && "socket.io.js".equals(parts[0])) + { response.setContentType("text/javascript"); InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/codeminders/socketio/socket.io.js"); OutputStream os = response.getOutputStream(); IO.copy(is, os); } else - if ("GET".equals(request.getMethod()) && "WebSocketMain.swf".equals(parts[0])) { + if ("GET".equals(request.getMethod()) && "WebSocketMain.swf".equals(parts[0])) + { response.setContentType("application/x-shockwave-flash"); InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/codeminders/socketio/WebSocketMain.swf"); OutputStream os = response.getOutputStream(); @@ -116,30 +124,31 @@ private void serve(HttpServletRequest request, HttpServletResponse response) { assert (transportProvider != null); - Transport transport = null; try { - transport = transportProvider.getTransport(request); - } catch (UnsupportedTransportException | SocketIOProtocolException e) + Transport transport = transportProvider.getTransport(request); + + if (LOGGER.isLoggable(Level.FINE)) + LOGGER.log(Level.FINE, "Handling request from " + + request.getRemoteHost() + ":" + request.getRemotePort() + + " with transport: " + transport.getType()); + + transport.handle(request, response, new Transport.InboundFactory() + { + @Override + public SocketIOInbound getInbound(HttpServletRequest request) + { + return SocketIOServlet.this.doSocketIOConnect(request); + } + }, sessionManager); + } + catch (UnsupportedTransportException | SocketIOProtocolException e) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Cannot find appropriate transport"); - if(LOGGER.isLoggable(Level.WARNING)) + if (LOGGER.isLoggable(Level.WARNING)) LOGGER.log(Level.WARNING, "Cannot find appropriate transport", e); - return; } - - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, "Handling request from " + - request.getRemoteHost() + ":" + request.getRemotePort() + - " with transport: " + transport.getType()); - - transport.handle(request, response, new Transport.InboundFactory() { - @Override - public SocketIOInbound getInbound(HttpServletRequest request) { - return SocketIOServlet.this.doSocketIOConnect(request); - } - }, sessionManager); } } diff --git a/core/src/main/java/com/codeminders/socketio/server/SocketIOSession.java b/core/src/main/java/com/codeminders/socketio/server/SocketIOSession.java index e5914a7..6f6fafb 100644 --- a/core/src/main/java/com/codeminders/socketio/server/SocketIOSession.java +++ b/core/src/main/java/com/codeminders/socketio/server/SocketIOSession.java @@ -44,8 +44,11 @@ interface SessionTask { SocketIOInbound getInbound(); - TransportConnection getTransportHandler(); - + TransportConnection getConnection(); + + //TODO: EngineIO docs says that server is suppose to send PING and + //TODO: client to respond with PONG but I'm seeing that client is sending PINGs, + //TODO: at least for websocket transport. Rigth now I will relay on a client sending pings void setHeartbeat(long delay); long getHeartbeat(); void setTimeout(long timeout); @@ -62,18 +65,15 @@ interface SessionTask { */ void startClose(); + void onPacket(EngineIOPacket packet); + void onPacket(SocketIOPacket packet); + void onMessage(SocketIOFrame message); void onPing(String data); - void onPong(String data); - void onClose(String data); - void onEvent(String name, String args); - /** - * Schedule a task (e.g. timeout timer) - * @param task The task to execute after specified delay. - * @param delay Delay in milliseconds. - * @return - */ + void onClose(String data); + void onEvent(String name, Object[] args); + SessionTask scheduleTask(Runnable task, long delay); /** @@ -88,10 +88,6 @@ interface SessionTask { */ void onMessage(String message); - /** - * Pass disconnect through to contained SocketIOInbound and update any internal state. - * @param reason - */ void onDisconnect(DisconnectReason reason); /** diff --git a/core/src/main/java/com/codeminders/socketio/server/SocketIOSessionManager.java b/core/src/main/java/com/codeminders/socketio/server/SocketIOSessionManager.java index a8e3230..7eab553 100644 --- a/core/src/main/java/com/codeminders/socketio/server/SocketIOSessionManager.java +++ b/core/src/main/java/com/codeminders/socketio/server/SocketIOSessionManager.java @@ -24,25 +24,52 @@ */ package com.codeminders.socketio.server; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.*; -public final class SocketIOSessionManager implements SessionManager { +public final class SocketIOSessionManager implements SessionManager +{ + private static final int SESSION_ID_LEN = 20; + private static final char[] symbols; - final ConcurrentMap socketIOSessions = new ConcurrentHashMap(); - final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); + static + { + StringBuilder sb = new StringBuilder(); + for (char ch = 'A'; ch <= 'Z'; ch++) + sb.append(ch); + for (char ch = 'a'; ch <= 'z'; ch++) + sb.append(ch); + symbols = sb.toString().toCharArray(); + } + + final ConcurrentMap socketIOSessions = new ConcurrentHashMap<>(); + final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); + + private String gennerateSessionId() + { + while(true) + { + StringBuilder sb = new StringBuilder(SESSION_ID_LEN); + for (int i = 0; i < SESSION_ID_LEN; i++) + sb.append(symbols[ThreadLocalRandom.current().nextInt(symbols.length)]); + + String id = sb.toString(); + if(socketIOSessions.get(id) == null) + return id; + } + + } @Override - public SocketIOSession createSession(SocketIOInbound inbound, String sessionId) { - DefaultSession impl = new DefaultSession(this, inbound, sessionId); + public SocketIOSession createSession(SocketIOInbound inbound) + { + DefaultSession impl = new DefaultSession(this, inbound, gennerateSessionId()); socketIOSessions.put(impl.getSessionId(), impl); return impl; } @Override - public SocketIOSession getSession(String sessionId) { + public SocketIOSession getSession(String sessionId) + { return socketIOSessions.get(sessionId); } } diff --git a/core/src/main/java/com/codeminders/socketio/server/TransportConnection.java b/core/src/main/java/com/codeminders/socketio/server/TransportConnection.java index 6868f47..845acae 100644 --- a/core/src/main/java/com/codeminders/socketio/server/TransportConnection.java +++ b/core/src/main/java/com/codeminders/socketio/server/TransportConnection.java @@ -44,6 +44,7 @@ public interface TransportConnection extends SocketIOOutbound void connect(HttpServletRequest request, HttpServletResponse response) throws IOException, SocketIOProtocolException; void handle(HttpServletRequest request, HttpServletResponse response, SocketIOSession session) throws IOException; + void sendMessage(SocketIOFrame message) throws SocketIOException; void disconnectWhenEmpty(); /** @@ -53,7 +54,6 @@ public interface TransportConnection extends SocketIOOutbound */ void abort(); - //TODO: do we need two 'connect' callbacks? - void onConnect(); + void send(EngineIOPacket packet) throws SocketIOException; } diff --git a/core/src/main/java/com/codeminders/socketio/server/transport/AbstractHttpTransport.java b/core/src/main/java/com/codeminders/socketio/server/transport/AbstractHttpTransport.java index 6fa2947..d850eb4 100644 --- a/core/src/main/java/com/codeminders/socketio/server/transport/AbstractHttpTransport.java +++ b/core/src/main/java/com/codeminders/socketio/server/transport/AbstractHttpTransport.java @@ -1,19 +1,19 @@ /** * The MIT License * Copyright (c) 2010 Tad Glines - * + *

* Contributors: Ovea.com, Mycila.com - * + *

* 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 @@ -25,7 +25,6 @@ package com.codeminders.socketio.server.transport; import com.codeminders.socketio.server.*; -import com.codeminders.socketio.util.Web; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; @@ -35,10 +34,11 @@ import java.util.logging.Level; import java.util.logging.Logger; -public abstract class AbstractHttpTransport extends AbstractTransport { +public abstract class AbstractHttpTransport extends AbstractTransport +{ - private static final Logger LOGGER = Logger.getLogger(AbstractHttpTransport.class.getName()); - public static final String SESSION_KEY = AbstractHttpTransport.class.getName() + ".Session"; + private static final Logger LOGGER = Logger.getLogger(AbstractHttpTransport.class.getName()); + public static final String SESSION_KEY = AbstractHttpTransport.class.getName() + ".Session"; @Override public final void handle(HttpServletRequest request, @@ -49,19 +49,18 @@ public final void handle(HttpServletRequest request, if (LOGGER.isLoggable(Level.FINE)) LOGGER.fine("Handling request " + request.getRequestURI() + " by " + getClass().getName()); + //TODO: redo session id handling. SocketIOSession session = null; - String sessionId = Web.extractSessionId(request); + String sessionId = request.getParameter(EngineIOProtocol.SESSION_ID); if (sessionId != null && sessionId.length() > 0) - { session = sessionFactory.getSession(sessionId); - } if (session != null) { - TransportConnection handler = session.getTransportHandler(); - if (handler != null) + TransportConnection connection = session.getConnection(); + if (connection != null) { - handler.handle(request, response, session); + connection.handle(request, response, session); } else { @@ -71,47 +70,35 @@ public final void handle(HttpServletRequest request, } else { - if ("GET".equals(request.getMethod())) + if (!"GET".equals(request.getMethod())) { - try - { - session = connect(request, response, inboundFactory, - sessionFactory, sessionId); - } catch (SocketIOProtocolException e) - { - if(LOGGER.isLoggable(Level.WARNING)) - LOGGER.log(Level.WARNING, "Cannot initialize connection", e); - } - if (session == null) - response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); + response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); + return; } - else - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - } - } - - private SocketIOSession connect(HttpServletRequest request, - HttpServletResponse response, - InboundFactory inboundFactory, - SessionManager sessionFactory, - String sessionId) throws IOException, SocketIOProtocolException - { - SocketIOInbound inbound = inboundFactory.getInbound(request); - if (inbound != null) - { - if (sessionId == null) - sessionId = request.getSession().getId(); - SocketIOSession session = sessionFactory.createSession(inbound, sessionId); - //TODO: this is wierd. need to refactor connection sequence - createConnection(session).connect(request, response); - connect(session, request, response); + try + { + SocketIOInbound inbound = inboundFactory.getInbound(request); + if (inbound == null) + { + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); + return; + } - return session; + session = sessionFactory.createSession(inbound); + //TODO: wierd sequence. need to refactor + createConnection(session).connect(request, response); + onConnect(session, request, response); + } + catch (SocketIOProtocolException e) + { + if (LOGGER.isLoggable(Level.WARNING)) + LOGGER.log(Level.WARNING, "Cannot initialize connection", e); + } + if (session == null) + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); } - - return null; } public abstract void startSend(SocketIOSession session, ServletResponse response) throws IOException; @@ -120,6 +107,6 @@ private SocketIOSession connect(HttpServletRequest request, public abstract void finishSend(SocketIOSession session, ServletResponse response) throws IOException; - public abstract void connect(SocketIOSession session, ServletRequest request, ServletResponse response) - throws IOException, SocketIOProtocolException; + public abstract void onConnect(SocketIOSession session, ServletRequest request, ServletResponse response) + throws IOException, SocketIOProtocolException; } diff --git a/core/src/main/java/com/codeminders/socketio/server/transport/AbstractTransportProvider.java b/core/src/main/java/com/codeminders/socketio/server/transport/AbstractTransportProvider.java index fb3bc81..ae28c1c 100644 --- a/core/src/main/java/com/codeminders/socketio/server/transport/AbstractTransportProvider.java +++ b/core/src/main/java/com/codeminders/socketio/server/transport/AbstractTransportProvider.java @@ -53,7 +53,7 @@ public Transport getTransport(ServletRequest request) throws UnsupportedTransportException, SocketIOProtocolException { //TODO: check EIO version? I do not see it in the spec but it is in actual requests. Ignoring it for now. - String transportName = request.getParameter("transport"); + String transportName = request.getParameter(EngineIOProtocol.TRANSPORT); if(transportName == null) throw new SocketIOProtocolException("Missing transport parameter"); @@ -63,7 +63,7 @@ public Transport getTransport(ServletRequest request) type = TransportType.from(transportName); if("polling".equals(transportName)) { - if(request.getParameter("j") != null) + if(request.getParameter(EngineIOProtocol.JSONP_INDEX) != null) type = TransportType.JSONP_POLLING; else type = TransportType.XHR_POLLING; diff --git a/core/src/main/java/com/codeminders/socketio/server/transport/FlashSocketTransport.java b/core/src/main/java/com/codeminders/socketio/server/transport/FlashSocketTransport.java index 3e65a44..92f9dd6 100644 --- a/core/src/main/java/com/codeminders/socketio/server/transport/FlashSocketTransport.java +++ b/core/src/main/java/com/codeminders/socketio/server/transport/FlashSocketTransport.java @@ -115,29 +115,38 @@ public void destroy() public void handle(HttpServletRequest request, HttpServletResponse response, Transport.InboundFactory inboundFactory, - SessionManager sessionFactory) throws IOException { - + SessionManager sessionFactory) throws IOException + { String path = request.getPathInfo(); - if (path == null || path.length() == 0 || "/".equals(path)) { + if (path == null || path.length() == 0 || "/".equals(path)) + { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid " + TransportType.FLASH_SOCKET + " transport request"); return; } if (path.startsWith("/")) path = path.substring(1); String[] parts = path.split("/"); - if ("GET".equals(request.getMethod()) && "flashsocket".equals(parts[1])) { - if (!FLASHFILE_PATH.equals(path)) { + if ("GET".equals(request.getMethod()) && "flashsocket".equals(parts[1])) + { + if (!FLASHFILE_PATH.equals(path)) + { delegate.handle(request, response, inboundFactory, sessionFactory); - } else { + } + else + { response.setContentType("application/x-shockwave-flash"); - InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/glines/socketio/" + FLASHFILE_NAME); + InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/codeminders/socketio/" + FLASHFILE_NAME); OutputStream os = response.getOutputStream(); - try { + try + { IO.copy(is, os); - } catch (IOException e) { + } catch (IOException e) + { LOGGER.log(Level.WARNING, "Error writing " + FLASHFILE_NAME + ": " + e.getMessage(), e); } } - } else { + } + else + { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid " + TransportType.FLASH_SOCKET + " transport request"); } } @@ -204,23 +213,32 @@ public void run() { private void stopFlashPolicyServer() { - if (flashPolicyServer != null) { - try { + if (flashPolicyServer != null) + { + try + { flashPolicyServer.close(); - } catch (IOException e) { + } catch (IOException e) + { // Ignore } } - if (policyAcceptorThread != null) { - try { + if (policyAcceptorThread != null) + { + try + { policyAcceptorThread.get(); - } catch (InterruptedException e) { + } catch (InterruptedException e) + { Thread.currentThread().interrupt(); throw new IllegalStateException(); - } catch (ExecutionException e) { + } catch (ExecutionException e) + { throw new IllegalStateException("Server thread threw an exception", e.getCause()); } - if (!policyAcceptorThread.isDone()) { + + if (!policyAcceptorThread.isDone()) + { throw new IllegalStateException("Server acceptor thread has not stopped."); } } diff --git a/core/src/main/java/com/codeminders/socketio/server/transport/JSONPPollingTransport.java b/core/src/main/java/com/codeminders/socketio/server/transport/JSONPPollingTransport.java index 114aa28..4515720 100644 --- a/core/src/main/java/com/codeminders/socketio/server/transport/JSONPPollingTransport.java +++ b/core/src/main/java/com/codeminders/socketio/server/transport/JSONPPollingTransport.java @@ -71,7 +71,7 @@ public void finishSend(SocketIOSession session, ServletResponse response) throws } @Override - public void connect(SocketIOSession session, ServletRequest request, ServletResponse response) + public void onConnect(SocketIOSession session, ServletRequest request, ServletResponse response) throws IOException, SocketIOProtocolException { try { diff --git a/core/src/main/java/com/codeminders/socketio/server/transport/XHRPollingTransport.java b/core/src/main/java/com/codeminders/socketio/server/transport/XHRPollingTransport.java index 54a572c..4439e1c 100644 --- a/core/src/main/java/com/codeminders/socketio/server/transport/XHRPollingTransport.java +++ b/core/src/main/java/com/codeminders/socketio/server/transport/XHRPollingTransport.java @@ -33,6 +33,7 @@ import javax.servlet.ServletResponse; import java.io.IOException; +//TODO: implement CORS public abstract class XHRPollingTransport extends AbstractHttpTransport { @Override @@ -61,12 +62,12 @@ public void finishSend(SocketIOSession session, ServletResponse response) throws } @Override - public void connect(SocketIOSession session, ServletRequest request, ServletResponse response) throws IOException, SocketIOProtocolException + public void onConnect(SocketIOSession session, ServletRequest request, ServletResponse response) throws IOException, SocketIOProtocolException { startSend(session, response); //TODO: check!!! - writeData(session, response, SocketIOFrame.encode(SocketIOFrame.FrameType.CONNECT, session.getSessionId())); +// writeData(session, response, SocketIOFrame.encode(SocketIOFrame.FrameType.CONNECT, session.getSessionId())); } } diff --git a/core/src/main/java/com/codeminders/socketio/util/DefaultLoader.java b/core/src/main/java/com/codeminders/socketio/util/DefaultLoader.java deleted file mode 100644 index 5845b40..0000000 --- a/core/src/main/java/com/codeminders/socketio/util/DefaultLoader.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.util; - -import java.io.IOException; -import java.net.URL; -import java.util.Enumeration; -import java.util.LinkedList; -import java.util.List; - -/** - * @author Mathieu Carbou (mathieu.carbou@gmail.com) - */ -public class DefaultLoader implements Loader { - - private final ClassLoader classLoader; - - public DefaultLoader() { - this(getDefaultClassLoader()); - } - - public DefaultLoader(ClassLoader classLoader) { - this.classLoader = classLoader; - } - - @Override - public Class loadClass(String className) { - try { - return classLoader.loadClass(className); - } catch (ClassNotFoundException e) { - throw new IllegalArgumentException("Unable to load class " + className, e); - } - } - - @Override - public URL getResource(String path) { - return classLoader.getResource(path); - } - - @Override - public List getResources(String path) { - List urls = new LinkedList(); - try { - Enumeration e = classLoader.getResources(path); - while (e.hasMoreElements()) - urls.add(e.nextElement()); - } catch (IOException ignored) { - } - return urls; - } - - private static ClassLoader getDefaultClassLoader() { - ClassLoader cl = null; - try { - cl = Thread.currentThread().getContextClassLoader(); - } - catch (Throwable ignored) { - } - if (cl == null) - cl = DefaultLoader.class.getClassLoader(); - return cl; - } - -} \ No newline at end of file diff --git a/core/src/main/java/com/codeminders/socketio/util/JdkOverLog4j.java b/core/src/main/java/com/codeminders/socketio/util/JdkOverLog4j.java index c0ee477..166d5a0 100644 --- a/core/src/main/java/com/codeminders/socketio/util/JdkOverLog4j.java +++ b/core/src/main/java/com/codeminders/socketio/util/JdkOverLog4j.java @@ -37,13 +37,6 @@ import java.util.logging.LogManager; import java.util.logging.LogRecord; -/** - * @author Mathieu Carbou - */ - -/** - * @author Mathieu Carbou - */ public final class JdkOverLog4j extends Handler { private static final Map LEVELS_JDK_TO_LOG4J = new HashMap() { diff --git a/core/src/main/java/com/codeminders/socketio/util/Loader.java b/core/src/main/java/com/codeminders/socketio/util/Loader.java deleted file mode 100644 index 0c4c3b8..0000000 --- a/core/src/main/java/com/codeminders/socketio/util/Loader.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.util; - -import java.net.URL; -import java.util.List; - -/** - * @author Mathieu Carbou (mathieu.carbou@gmail.com) - */ -public interface Loader { - Class loadClass(String className); - - URL getResource(String path); - - List getResources(String path); -} \ No newline at end of file diff --git a/core/src/main/java/com/codeminders/socketio/util/ServiceClassLoader.java b/core/src/main/java/com/codeminders/socketio/util/ServiceClassLoader.java deleted file mode 100644 index f949d39..0000000 --- a/core/src/main/java/com/codeminders/socketio/util/ServiceClassLoader.java +++ /dev/null @@ -1,207 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2010 Tad Glines - * - * Contributors: Ovea.com, Mycila.com - * - * 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.util; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URL; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.ServiceConfigurationError; - -/** - * @author Mathieu Carbou - * @param The type of the service to be loaded by this loader - */ -public final class ServiceClassLoader implements Iterable> { - - private static final String PREFIX = "META-INF/services/"; - private final Class service; - private final Loader loader; - private LinkedHashMap> providers = new LinkedHashMap>(); - private LazyIterator lookupIterator; - - public void reload() { - providers.clear(); - lookupIterator = new LazyIterator(service, loader); - } - - private ServiceClassLoader(Class svc, Loader loader) { - this.service = svc; - this.loader = loader; - reload(); - } - - private static void fail(Class service, String msg, Throwable cause) throws ServiceConfigurationError { - throw new ServiceConfigurationError(service.getName() + ": " + msg, cause); - } - - private static void fail(Class service, String msg) throws ServiceConfigurationError { - throw new ServiceConfigurationError(service.getName() + ": " + msg); - } - - private static void fail(Class service, URL u, int line, String msg) throws ServiceConfigurationError { - fail(service, u + ":" + line + ": " + msg); - } - - private int parseLine(Class service, URL u, BufferedReader r, int lc, List names) throws IOException, ServiceConfigurationError { - String ln = r.readLine(); - if (ln == null) return -1; - int ci = ln.indexOf('#'); - if (ci >= 0) ln = ln.substring(0, ci); - ln = ln.trim(); - int n = ln.length(); - if (n != 0) { - if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0)) - fail(service, u, lc, "Illegal configuration-file syntax"); - int cp = ln.codePointAt(0); - if (!Character.isJavaIdentifierStart(cp)) - fail(service, u, lc, "Illegal provider-class name: " + ln); - for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) { - cp = ln.codePointAt(i); - if (!Character.isJavaIdentifierPart(cp) && (cp != '.')) - fail(service, u, lc, "Illegal provider-class name: " + ln); - } - if (!providers.containsKey(ln) && !names.contains(ln)) - names.add(ln); - } - return lc + 1; - } - - private Iterator parse(Class service, URL u) throws ServiceConfigurationError { - InputStream in = null; - BufferedReader r = null; - ArrayList names = new ArrayList(); - try { - in = u.openStream(); - r = new BufferedReader(new InputStreamReader(in, "utf-8")); - int lc = 1; - while ((lc = parseLine(service, u, r, lc, names)) >= 0) ; - } catch (IOException x) { - fail(service, "Error reading configuration file", x); - } finally { - try { - if (r != null) r.close(); - if (in != null) in.close(); - } catch (IOException y) { - fail(service, "Error closing configuration file", y); - } - } - return names.iterator(); - } - - private class LazyIterator implements Iterator> { - final Class service; - final Loader loader; - Iterator configs = null; - Iterator pending = null; - String nextName = null; - - private LazyIterator(Class service, Loader loader) { - this.service = service; - this.loader = loader; - } - - public boolean hasNext() { - if (nextName != null) { - return true; - } - if (configs == null) { - String fullName = PREFIX + service.getName(); - configs = loader.getResources(fullName).iterator(); - } - while ((pending == null) || !pending.hasNext()) { - if (!configs.hasNext()) { - return false; - } - pending = parse(service, configs.next()); - } - nextName = pending.next(); - return true; - } - - @SuppressWarnings({"unchecked"}) - public Class next() { - if (!hasNext()) - throw new NoSuchElementException(); - String cn = nextName; - nextName = null; - try { - Class p = (Class) loader.loadClass(cn); - providers.put(cn, p); - return p; - } catch (RuntimeException x) { - fail(service, - "Provider " + cn + " could not be instantiated: " + x, - x); - } - throw new Error(); // This cannot happen - } - - public void remove() { - throw new UnsupportedOperationException(); - } - - } - - public Iterator> iterator() { - return new Iterator>() { - Iterator>> knownProviders = providers.entrySet().iterator(); - - public boolean hasNext() { - return knownProviders.hasNext() || lookupIterator.hasNext(); - } - - public Class next() { - if (knownProviders.hasNext()) - return knownProviders.next().getValue(); - return lookupIterator.next(); - } - - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } - - public static ServiceClassLoader load(Class service, Loader loader) { - return new ServiceClassLoader(service, loader); - } - - public static ServiceClassLoader load(Class service) { - return new ServiceClassLoader(service, new DefaultLoader()); - } - - public String toString() { - return "ServiceClassLoader[" + service.getName() + "]"; - } - -} \ No newline at end of file diff --git a/core/src/main/java/com/codeminders/socketio/util/Web.java b/core/src/main/java/com/codeminders/socketio/util/Web.java index 273013d..ffe1a9b 100644 --- a/core/src/main/java/com/codeminders/socketio/util/Web.java +++ b/core/src/main/java/com/codeminders/socketio/util/Web.java @@ -31,6 +31,8 @@ /** * @author Mathieu Carbou */ + +//TODO: remove this. public final class Web { private static final char[] BASE64_ALPHABET ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".toCharArray(); diff --git a/extension/jetty/src/main/java/com/codeminders/socketio/server/transport/jetty/JettyContinuationTransportConnection.java b/extension/jetty/src/main/java/com/codeminders/socketio/server/transport/jetty/JettyContinuationTransportConnection.java index 0b907b6..151e75e 100644 --- a/extension/jetty/src/main/java/com/codeminders/socketio/server/transport/jetty/JettyContinuationTransportConnection.java +++ b/extension/jetty/src/main/java/com/codeminders/socketio/server/transport/jetty/JettyContinuationTransportConnection.java @@ -344,8 +344,8 @@ public void connect(HttpServletRequest request, HttpServletResponse response) continuation = ContinuationSupport.getContinuation(request); continuation.addContinuationListener(this); - // do transport-specific actions on connect - transport.connect(getSession(), request, response); + // do transport-specific actions on onConnect + transport.onConnect(getSession(), request, response); is_open = true; getSession().onConnect(this); @@ -387,6 +387,12 @@ public boolean onMessage(String message) { getSession().onShutdown(); } + @Override + public void send(EngineIOPacket packet) throws SocketIOException + { + //TODO: implement + } + /** * This must be called within the context of an active HTTP request. */ @@ -444,8 +450,8 @@ public void connectionActive() { } @Override - public void onConnect() + public void send(SocketIOPacket packet) throws SocketIOException { - getSession().onConnect(this); + //TODO: implement } } diff --git a/extension/jetty/src/main/java/com/codeminders/socketio/server/transport/jetty/JettySocketIOServlet.java b/extension/jetty/src/main/java/com/codeminders/socketio/server/transport/jetty/JettySocketIOServlet.java index 5f6116d..d9a61d1 100644 --- a/extension/jetty/src/main/java/com/codeminders/socketio/server/transport/jetty/JettySocketIOServlet.java +++ b/extension/jetty/src/main/java/com/codeminders/socketio/server/transport/jetty/JettySocketIOServlet.java @@ -1,7 +1,9 @@ package com.codeminders.socketio.server.transport.jetty; import com.codeminders.socketio.server.SocketIOServlet; +import com.codeminders.socketio.server.TransportProvider; +import javax.servlet.ServletConfig; import javax.servlet.ServletException; /** @@ -10,9 +12,12 @@ public abstract class JettySocketIOServlet extends SocketIOServlet { @Override - public void init() throws ServletException + public void init(ServletConfig config) throws ServletException { - super.init(); - setTransportProvider(new JettyTransportProvider()); + TransportProvider transportProvider = new JettyTransportProvider(); + transportProvider.init(config); + setTransportProvider(transportProvider); + + super.init(config); } } diff --git a/extension/jetty/src/main/java/com/codeminders/socketio/server/transport/jetty/JettyWebSocketTransport.java b/extension/jetty/src/main/java/com/codeminders/socketio/server/transport/jetty/JettyWebSocketTransport.java index 1f3613c..5de23a6 100644 --- a/extension/jetty/src/main/java/com/codeminders/socketio/server/transport/jetty/JettyWebSocketTransport.java +++ b/extension/jetty/src/main/java/com/codeminders/socketio/server/transport/jetty/JettyWebSocketTransport.java @@ -67,63 +67,49 @@ public TransportType getType() public void handle(HttpServletRequest request, HttpServletResponse response, Transport.InboundFactory inboundFactory, - SessionManager sessionFactory) throws IOException { - - String sessionId = null; - String transport = null; - - String path = request.getPathInfo(); - if (path != null && path.length() > 0 && !"/".equals(path)) { - if (path.startsWith("/")) path = path.substring(1); - String[] parts = path.split("/"); - if (parts.length >= 3) { - transport = parts[1] == null || parts[1].length() == 0 || parts[1].equals("null") ? null : parts[1]; - sessionId = parts[2] == null || parts[2].length() == 0 || parts[2].equals("null") ? null : parts[2]; - } + SessionManager sessionFactory) throws IOException + { + + if(!"GET".equals(request.getMethod())) + { + response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, + "Only GET method is allowed for websocket transport"); + return; } - if ("GET".equals(request.getMethod()) && sessionId != null - && ("websocket".equals(transport) || "flashsocket".equals(transport))) { - boolean hixie = request.getHeader("Sec-WebSocket-Key1") != null; - - SocketIOInbound inbound = inboundFactory.getInbound(request); - if (inbound == null) - { - if (hixie) - response.setHeader("Connection", "close"); - - response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); - } - else - { - SocketIOSession session; - final TransportConnection connection; - session = sessionFactory.getSession(sessionId); - - if (session == null) - { - session = sessionFactory.createSession(inbound, sessionId); - connection = createConnection(session); - } - else - { - connection = session.getTransportHandler(); - } + SocketIOInbound inbound = inboundFactory.getInbound(request); + if (inbound == null) + { + //TODO: research this + if (request.getHeader("Sec-WebSocket-Key1") != null) + response.setHeader("Connection", "close"); - wsFactory.acceptWebSocket(new WebSocketCreator() { - @Override - public Object createWebSocket(ServletUpgradeRequest servletUpgradeRequest, - ServletUpgradeResponse servletUpgradeResponse) - { - return connection; - } - }, request, response); - } + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); + return; } - else + + final TransportConnection connection; + String sessionId = request.getParameter(EngineIOProtocol.SESSION_ID); + SocketIOSession session = null; + if(sessionId != null) + session = sessionFactory.getSession(sessionId); + + if(session == null) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST, this + " transport error: Invalid request"); + session = sessionFactory.createSession(inbound); + connection = createConnection(session); } + else + connection = session.getConnection(); + + wsFactory.acceptWebSocket(new WebSocketCreator() { + @Override + public Object createWebSocket(ServletUpgradeRequest servletUpgradeRequest, + ServletUpgradeResponse servletUpgradeResponse) + { + return connection; + } + }, request, response); } @Override @@ -131,5 +117,4 @@ public TransportConnection createConnection() { return new JettyWebSocketTransportConnection(this); } - } diff --git a/extension/jetty/src/main/java/com/codeminders/socketio/server/transport/jetty/JettyWebSocketTransportConnection.java b/extension/jetty/src/main/java/com/codeminders/socketio/server/transport/jetty/JettyWebSocketTransportConnection.java index 5698ee1..275c0fc 100644 --- a/extension/jetty/src/main/java/com/codeminders/socketio/server/transport/jetty/JettyWebSocketTransportConnection.java +++ b/extension/jetty/src/main/java/com/codeminders/socketio/server/transport/jetty/JettyWebSocketTransportConnection.java @@ -1,19 +1,19 @@ /** * The MIT License * Copyright (c) 2010 Tad Glines - * + *

* Contributors: Ovea.com, Mycila.com - * + *

* 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 @@ -47,15 +47,14 @@ /** * @author Alexander Sova - * @author Mathieu Carbou */ + @WebSocket public final class JettyWebSocketTransportConnection extends AbstractTransportConnection { - private static final Logger LOGGER = Logger.getLogger(JettyWebSocketTransportConnection.class.getName()); - private Session outbound; + private Session remote_endpoint; private Transport transport; public JettyWebSocketTransportConnection(Transport transport) @@ -64,121 +63,156 @@ public JettyWebSocketTransportConnection(Transport transport) } @Override - protected void init() { - getSession().setHeartbeat(getConfig().getHeartbeatDelay(SocketIOConfig.DEFAULT_HEARTBEAT_INTERVAL)); + protected void init() + { getSession().setTimeout(getConfig().getHeartbeatTimeout(SocketIOConfig.DEFAULT_HEARTBEAT_TIMEOUT)); + if (LOGGER.isLoggable(Level.FINE)) - LOGGER.fine(getConfig().getNamespace() + " transport handler configuration:\n" + - " - heartbeatDelay=" + getSession().getHeartbeat() + "\n" + - " - heartbeatTimeout=" + getSession().getTimeout()); + LOGGER.fine(getConfig().getNamespace() + " WebSocket Connection configuration:\n" + + " - timeout=" + getSession().getTimeout()); } @OnWebSocketConnect - public void onWebSocketConnect(Session session) { - outbound = session; - try { - sendMessage(new SocketIOFrame(SocketIOFrame.FrameType.CONNECT, SocketIOFrame.TEXT_MESSAGE_TYPE, "")); - } catch (SocketIOException e) { - LOGGER.log(Level.SEVERE, "Cannot connect", e); - } - onConnect(); + public void onWebSocketConnect(Session session) + { + remote_endpoint = session; + try + { + send(EngineIOProtocol.createHandshakePacket(getSession().getSessionId(), + new String[] {}, + getConfig().getPingInterval(SocketIOConfig.DEFAULT_PING_INTERVAL), + getConfig().getTimeout(SocketIOConfig.DEFAULT_PING_TIMEOUT))); + send(new SocketIOPacket(SocketIOPacket.Type.CONNECT)); + + } + catch (SocketIOException e) + { + LOGGER.log(Level.SEVERE, "Cannot onConnect", e); + getSession().onConnect(null); //TODO: use different callback if connection failed + } + getSession().onConnect(this); } @OnWebSocketClose - public void onWebSocketClose(int closeCode, String message) { + public void onWebSocketClose(int closeCode, String message) + { getSession().onShutdown(); } @OnWebSocketMessage - public void onWebSocketText(String message) { - getSession().startHeartbeatTimer(); - List messages = SocketIOFrame.parse(message); - for (SocketIOFrame msg : messages) { - getSession().onMessage(msg); + public void onWebSocketText(String text) + { + if(LOGGER.isLoggable(Level.FINE)) + LOGGER.fine("Packet received: " + text); + + //TODO: check if it is possible to get multiple packets in one transmission + //TODO: for now we expect single packet per call + getSession().startTimeoutTimer(); + + try + { + getSession().onPacket(EngineIOProtocol.decode(text)); + } + catch (SocketIOProtocolException e) + { + LOGGER.log(Level.WARNING, "Invalid EIO packet received", e); } } @OnWebSocketMessage - public void onWebSocketBinary(byte[] data, int offset, int length) { - try { + public void onWebSocketBinary(byte[] data, int offset, int length) + { + //TODO: use proper binary encoding/decoding defined in EIO + try + { onWebSocketText(new String(data, offset, length, "UTF-8")); - } catch (UnsupportedEncodingException e) { + } + catch (UnsupportedEncodingException e) + { // Do nothing for now. } } @Override - public void disconnect() { + public void disconnect() + { getSession().onDisconnect(DisconnectReason.DISCONNECT); - try { - outbound.disconnect(); - } catch (IOException e) { - //TODO: report? - } + disconnectEndpoint(); } @Override - public void close() { + public void close() + { getSession().startClose(); } @Override - public ConnectionState getConnectionState() { + public ConnectionState getConnectionState() + { return getSession().getConnectionState(); } @Override - public void sendMessage(SocketIOFrame frame) throws SocketIOException { - if (outbound.isOpen()) { + public void sendMessage(SocketIOFrame frame) throws SocketIOException + { + if (remote_endpoint.isOpen()) + { if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Session[" + getSession().getSessionId() + "]: sendMessage: [" + frame.getFrameType() + "]: " + frame.getData()); - try { - outbound.getRemote().sendString(frame.encode()); - } catch (IOException e) { - try { - outbound.disconnect(); - } catch(IOException ex) { - //TODO: report? - } + try + { + remote_endpoint.getRemote().sendString(frame.encode()); + } + catch (IOException e) + { + disconnectEndpoint(); throw new SocketIOException(e); } - } else { - throw new SocketIOClosedException(); } + else + throw new SocketIOClosedException(); } @Override - public void sendMessage(String message) throws SocketIOException { + public void sendMessage(String message) throws SocketIOException + { sendMessage(SocketIOFrame.TEXT_MESSAGE_TYPE, message); } @Override public void sendMessage(int messageType, String message) - throws SocketIOException { - if (outbound.isOpen() && getSession().getConnectionState() == ConnectionState.CONNECTED) { + throws SocketIOException + { + if (remote_endpoint.isOpen() && getSession().getConnectionState() == ConnectionState.CONNECTED) + { sendMessage(new SocketIOFrame( - messageType == SocketIOFrame.TEXT_MESSAGE_TYPE ? - SocketIOFrame.FrameType.MESSAGE : - SocketIOFrame.FrameType.JSON_MESSAGE, - messageType, message)); - } else { + messageType == SocketIOFrame.TEXT_MESSAGE_TYPE ? + SocketIOFrame.FrameType.MESSAGE : + SocketIOFrame.FrameType.JSON_MESSAGE, + messageType, message)); + } + else + { throw new SocketIOClosedException(); } } @Override public void emitEvent(String name, String args) - throws SocketIOException { - - if (outbound.isOpen() && getSession().getConnectionState() == ConnectionState.CONNECTED) { + throws SocketIOException + { + if (remote_endpoint.isOpen() && getSession().getConnectionState() == ConnectionState.CONNECTED) + { HashMap map = new HashMap(); map.put("name", name); - map.put("args", new Object[] { args }); + map.put("args", new Object[]{args}); sendMessage(new SocketIOFrame(SocketIOFrame.FrameType.EVENT, - SocketIOFrame.JSON_MESSAGE_TYPE, - JSON.toString(map))); - } else { + SocketIOFrame.JSON_MESSAGE_TYPE, + JSON.toString(map))); + } + else + { throw new SocketIOClosedException(); } @@ -197,30 +231,55 @@ public void connect(HttpServletRequest request, HttpServletResponse response) th } @Override - public void handle(HttpServletRequest request, HttpServletResponse response, SocketIOSession session) throws IOException { + public void handle(HttpServletRequest request, HttpServletResponse response, SocketIOSession session) throws IOException + { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unexpected request on upgraded WebSocket connection"); } @Override public void abort() { - if (outbound != null) + if (remote_endpoint != null) { - try - { - outbound.disconnect(); - } - catch (IOException e) - { - //ignore - } - outbound = null; + disconnectEndpoint(); + remote_endpoint = null; } getSession().onShutdown(); } @Override - public void onConnect() { - getSession().onConnect(this); + public void send(EngineIOPacket packet) throws SocketIOException + { + sendString(EngineIOProtocol.encode(packet)); + } + + private void sendString(String data) throws SocketIOException + { + if (!remote_endpoint.isOpen()) + throw new SocketIOClosedException(); + + if (LOGGER.isLoggable(Level.FINE)) + LOGGER.log(Level.FINE, + "Session[" + getSession().getSessionId() + "]: sendPacket: " + data); + try + { + remote_endpoint.getRemote().sendString(data); + } + catch (IOException e) + { + disconnectEndpoint(); + throw new SocketIOException(e); + } + } + + private void disconnectEndpoint() + { + try + { + remote_endpoint.disconnect(); + } + catch (IOException ex) { + // ignore + } } } diff --git a/samples/chat/src/main/java/com/codeminders/socketio/sample/chat/ChatSocketServlet.java b/samples/chat/src/main/java/com/codeminders/socketio/sample/chat/ChatSocketServlet.java index b608b9f..2d8fd89 100644 --- a/samples/chat/src/main/java/com/codeminders/socketio/sample/chat/ChatSocketServlet.java +++ b/samples/chat/src/main/java/com/codeminders/socketio/sample/chat/ChatSocketServlet.java @@ -42,9 +42,13 @@ import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; public class ChatSocketServlet extends JettySocketIOServlet { + private static final Logger LOGGER = Logger.getLogger(ChatSocketServlet.class.getName()); + private static final long serialVersionUID = 1L; private AtomicInteger ids = new AtomicInteger(1); private Queue connections = new ConcurrentLinkedQueue(); @@ -60,17 +64,24 @@ private class ChatConnection implements SocketIOInbound { private Integer sessionId = ids.getAndIncrement(); @Override - public void onConnect(SocketIOOutbound outbound) { + public void onConnect(SocketIOOutbound outbound) + { + LOGGER.fine("Client connected"); this.outbound = outbound; connections.offer(this); - try { - outbound.sendMessage(SocketIOFrame.JSON_MESSAGE_TYPE, new Gson().toJson( - Collections.singletonMap("welcome", "Welcome to Socket.IO Chat!"))); - } catch (SocketIOException e) { - outbound.disconnect(); - } - broadcast(SocketIOFrame.JSON_MESSAGE_TYPE, new Gson().toJson( - Collections.singletonMap("announcement", sessionId + " connected"))); +// try +// { +// outbound.s +//// outbound.sendMessage(SocketIOFrame.JSON_MESSAGE_TYPE, new Gson().toJson( +//// Collections.singletonMap("welcome", "Welcome to Socket.IO Chat!"))); +// } +// catch (SocketIOException e) +// { +// LOGGER.log(Level.SEVERE, "Cannot send a chat message", e); +// outbound.disconnect(); +// } +// broadcast(SocketIOFrame.JSON_MESSAGE_TYPE, new Gson().toJson( +// Collections.singletonMap("announcement", sessionId + " connected"))); } @Override @@ -81,6 +92,7 @@ public void onDisconnect(DisconnectReason reason, String errorMessage) { Collections.singletonMap("announcement", sessionId + " disconnected"))); } + @Override public void onMessage(int messageType, String message) { if (message.equals("/rclose")) { @@ -143,10 +155,13 @@ public void onMessage(int messageType, String message) { } @Override - public void onEvent(String name, String args) { - + public void onEvent(String name, Object[] args) + { + //TODO: log arguments? + LOGGER.fine("Got event: " + name); } + private void broadcast(int messageType, String message) { for (ChatConnection c : connections) { if (c != this) { diff --git a/samples/chat/src/main/resources/META-INF/services/com.codeminders.socketio.server.Transport b/samples/chat/src/main/resources/META-INF/services/com.codeminders.socketio.server.Transport deleted file mode 100644 index 0521ac4..0000000 --- a/samples/chat/src/main/resources/META-INF/services/com.codeminders.socketio.server.Transport +++ /dev/null @@ -1 +0,0 @@ -com.codeminders.socketio.server.transport.jetty.JettyWebSocketTransport diff --git a/samples/chat/src/main/resources/META-INF/services/com.codeminders.socketio.server.TransportConnection b/samples/chat/src/main/resources/META-INF/services/com.codeminders.socketio.server.TransportConnection deleted file mode 100644 index b1947ff..0000000 --- a/samples/chat/src/main/resources/META-INF/services/com.codeminders.socketio.server.TransportConnection +++ /dev/null @@ -1,2 +0,0 @@ -com.codeminders.socketio.server.transport.jetty.JettyWebSocketTransportConnection -com.codeminders.socketio.server.transport.jetty.JettyContinuationTransportConnection diff --git a/samples/chat/src/main/resources/webapp/chat.html b/samples/chat/src/main/resources/webapp/chat.html index 73ac40f..32e5827 100644 --- a/samples/chat/src/main/resources/webapp/chat.html +++ b/samples/chat/src/main/resources/webapp/chat.html @@ -48,12 +48,15 @@ else addLine('Server: ' + '' + JSON.stringify(obj) + ''); } - function send(){ + function send() + { var val = document.getElementById('text').value; if (val == '/lclose') { addLine('closing...'); socket.close(); - } else if (val == '/ldisconnect') { + } + else + if (val == '/ldisconnect') { addLine('disconnecting...'); socket.disconnect(); } else { @@ -63,7 +66,8 @@ document.getElementById('text').value = ''; } - function esc(msg){ + function esc(msg) + { return msg.replace(//g, '>'); } @@ -87,8 +91,7 @@ } } - //TODO: use origin host name - var socket = io('http://127.0.0.1:8080', { + var socket = io({ transports: ['websocket'] }); @@ -113,7 +116,7 @@

Sample chat client

-