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/README b/README deleted file mode 100644 index ef49c8e..0000000 --- a/README +++ /dev/null @@ -1,28 +0,0 @@ -Java backend integration for Socket.IO library (http://socket.io/) - -To use in a jetty webapp, grant access in your jetty xml file to the jetty server class org.eclipse.jetty.server.HttpConnection - - - - - - - - -org.eclipse.jetty.server.HttpConnection - -org.eclipse.jetty.continuation. - -org.eclipse.jetty.jndi. - -org.eclipse.jetty.plus.jaas. - -org.eclipse.jetty.websocket. - -org.eclipse.jetty.servlet.DefaultServlet - org.eclipse.jetty. - - - - - -More info: - -mvn eclipse:eclipse # for import the project in eclipse -mvn install -Dlicense.skip=true # to install the jars in your repo - -Lauch from eclipse the Start* sample classes to test it. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6ccfcb0 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +Java backend for `Socket.IO` library (http://socket.io/) + +Supports `Socket.IO` clients version 1.0+ +Requires JSR 356-compatible server (tested with Jetty 9 and Tomcat 8) + +Right now only websocket and XHR polling transports are implemented. + +Based on https://github.com/tadglines/Socket.IO-Java + +License: MIT + +## Websocket endpoint initialization in Jetty + +Default websocket endpoint configuration assumes it's located in the root context and accessible via `/socket.io/` path. + +When `Socket.IO` backend is integrated into webapp managed by Jetty there is no need to perform additional configuration because Jetty scans for `@ServerEndpoint` annotation and initializes websocket endpoint automatically. + +When Jetty server is embedded into your application, websocket endpoint is located in the root context ("/") and expected to be accessible via `/socket.io/` path (default configuration), then in order to initialize endpoint you should add the following code + +```java + WebSocketServerContainerInitializer. + configureContext(context). + addEndpoint(WebsocketTransportConnection.class); + +``` + +When Jetty server is embedded into your application, but websocket endpoint is either located not in the root context ("/") or expected to be accessible via path other than `/socket.io/`, then in order to initialize endpoint you should add the following code + +```java + ServerContainer serverContainer = WebSocketServerContainerInitializer. + configureContext(context); + serverContainer. + addEndpoint(new AnnotatedServerEndpointConfig(serverContainer, + WebsocketTransportConnection.class, + WebsocketTransportConnection.class.getAnnotation(ServerEndpoint.class), + null) { + @Override + public String getPath() { + return "/"; // context-relative path, "/bar" for context "/foo" and path "/foo/bar" + } + }); +``` +See example in [com.codeminders.socketio.sample.jetty.ChatServer](https://github.com/codeminders/socket.io-server-java/blob/master/samples/jetty/src/main/java/com/codeminders/socketio/sample/jetty/ChatServer.java) diff --git a/TODO b/TODO index e6b6aae..1adf1c1 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,2 @@ -1. Maven pom.xml - -2. Add unit tests - -3. Add embedded containers to run sample webapps - -4. Split project in modules (core, samples, gwt, ...) +* handle b64 flag +* JSONP polling transport (not sure how to test it) diff --git a/core/pom.xml b/core/pom.xml deleted file mode 100644 index 1ed748a..0000000 --- a/core/pom.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - 4.0.0 - - - com.glines.socketio - socketio - 0.2.1 - .. - - - socketio-core - jar - - Socket.IO Java Core - Core Socket.IO classes - - - - org.mortbay.jetty - servlet-api - provided - - - log4j - log4j - true - - - - 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/DefaultSession.java b/core/src/main/java/com/glines/socketio/server/DefaultSession.java deleted file mode 100644 index 9bd639f..0000000 --- a/core/src/main/java/com/glines/socketio/server/DefaultSession.java +++ /dev/null @@ -1,354 +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.common.ConnectionState; -import com.glines.socketio.common.DisconnectReason; -import com.glines.socketio.common.SocketIOException; - -import java.util.Map; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicLong; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * @author Mathieu Carbou (mathieu.carbou@gmail.com) - */ -class DefaultSession implements SocketIOSession { - - private static final int SESSION_ID_LENGTH = 20; - private static final Logger LOGGER = Logger.getLogger(DefaultSession.class.getName()); - - private final SocketIOSessionManager socketIOSessionManager; - private final String sessionId; - private final AtomicLong messageId = new AtomicLong(0); - private final Map attributes = new ConcurrentHashMap(); - - private SocketIOInbound inbound; - private TransportHandler handler; - private ConnectionState state = ConnectionState.CONNECTING; - private long hbDelay; - private SessionTask hbDelayTask; - private long timeout; - private SessionTask timeoutTask; - private boolean timedout; - private String closeId; - - DefaultSession(SocketIOSessionManager socketIOSessionManager, SocketIOInbound inbound, String sessionId) { - this.socketIOSessionManager = socketIOSessionManager; - this.inbound = inbound; - this.sessionId = sessionId; - } - - @Override - public void setAttribute(String key, Object val) { - attributes.put(key, val); - } - - @Override - public Object getAttribute(String key) { - return attributes.get(key); - } - - @Override - public String getSessionId() { - return sessionId; - } - - @Override - public ConnectionState getConnectionState() { - return state; - } - - @Override - public SocketIOInbound getInbound() { - return inbound; - } - - @Override - public TransportHandler getTransportHandler() { - return handler; - } - - private void onTimeout() { - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, "Session[" + sessionId + "]: onTimeout"); - if (!timedout) { - timedout = true; - state = ConnectionState.CLOSED; - onDisconnect(DisconnectReason.TIMEOUT); - handler.abort(); - } - } - - @Override - public void startTimeoutTimer() { - clearTimeoutTimer(); - if (!timedout && timeout > 0) { - timeoutTask = scheduleTask(new Runnable() { - @Override - public void run() { - DefaultSession.this.onTimeout(); - } - }, timeout); - } - } - - @Override - public void clearTimeoutTimer() { - if (timeoutTask != null) { - timeoutTask.cancel(); - timeoutTask = null; - } - } - - 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, "")); - } catch (SocketIOException e) { - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, "handler.sendMessage failed: ", e); - handler.abort(); - } - startTimeoutTimer(); - } - - @Override - public void startHeartbeatTimer() { - clearHeartbeatTimer(); - if (!timedout && hbDelay > 0) { - hbDelayTask = scheduleTask(new Runnable() { - @Override - public void run() { - sendHeartBeat(); - } - }, hbDelay); - } - } - - @Override - public void clearHeartbeatTimer() { - if (hbDelayTask != null) { - hbDelayTask.cancel(); - hbDelayTask = null; - } - } - - @Override - public void setHeartbeat(long delay) { - hbDelay = delay; - } - - @Override - public long getHeartbeat() { - return hbDelay; - } - - @Override - public void setTimeout(long timeout) { - this.timeout = timeout; - } - - @Override - public long getTimeout() { - return timeout; - } - - @Override - public void startClose() { - state = ConnectionState.CLOSING; - closeId = "server"; - try { - handler.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(); - } - } - - @Override - public void onMessage(SocketIOFrame message) { - switch (message.getFrameType()) { - case CONNECT: - onPing(message.getData()); - case HEARTBEAT: - // Ignore this message type as they are only intended to be from server to client. - startHeartbeatTimer(); - break; - case CLOSE: - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, "Session[" + sessionId + "]: onClose: " + message.getData()); - onClose(message.getData()); - break; - case MESSAGE: - case JSON_MESSAGE: - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, "Session[" + sessionId + "]: onMessage: " + message.getData()); - onMessage(message.getData()); - break; - default: - // Ignore unknown message types - break; - } - } - - @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(); - } - } - - @Override - public void onPong(String data) { - clearTimeoutTimer(); - } - - @Override - public void onClose(String data) { - if (state == ConnectionState.CLOSING) { - if (closeId != null && closeId.equals(data)) { - state = ConnectionState.CLOSED; - onDisconnect(DisconnectReason.CLOSED); - handler.abort(); - } else { - try { - handler.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(); - } - } - } else { - state = ConnectionState.CLOSING; - try { - handler.sendMessage(new SocketIOFrame(SocketIOFrame.FrameType.CLOSE, 0, data)); - handler.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(); - } - } - } - - @Override - public SessionTask scheduleTask(Runnable task, long delay) { - final Future future = socketIOSessionManager.executor.schedule(task, delay, TimeUnit.MILLISECONDS); - return new SessionTask() { - @Override - public boolean cancel() { - return future.cancel(false); - } - }; - } - - @Override - public void onConnect(TransportHandler handler) { - if (handler == null) { - state = ConnectionState.CLOSED; - inbound = null; - socketIOSessionManager.socketIOSessions.remove(sessionId); - } else if (this.handler == null) { - this.handler = handler; - if (inbound == null) { - state = ConnectionState.CLOSED; - handler.abort(); - } else { - try { - state = ConnectionState.CONNECTED; - inbound.onConnect(handler); - 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(); - } - } - } else { - handler.abort(); - } - } - - @Override - public void onMessage(String message) { - if (inbound != null) { - try { - inbound.onMessage(SocketIOFrame.TEXT_MESSAGE_TYPE, message); - } catch (Throwable e) { - if (LOGGER.isLoggable(Level.WARNING)) - LOGGER.log(Level.WARNING, "Session[" + sessionId + "]: Exception thrown by SocketIOInbound.onMessage()", e); - } - } - } - - @Override - public void onDisconnect(DisconnectReason reason) { - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.log(Level.FINE, "Session[" + sessionId + "]: onDisconnect: " + reason); - clearTimeoutTimer(); - clearHeartbeatTimer(); - if (inbound != null) { - state = ConnectionState.CLOSED; - try { - inbound.onDisconnect(reason, null); - } catch (Throwable e) { - if (LOGGER.isLoggable(Level.WARNING)) - LOGGER.log(Level.WARNING, "Session[" + sessionId + "]: Exception thrown by SocketIOInbound.onDisconnect()", e); - } - inbound = null; - } - } - - @Override - 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) { - onDisconnect(DisconnectReason.CLOSE_FAILED); - } else { - onDisconnect(DisconnectReason.CLOSED_REMOTELY); - } - } else { - onDisconnect(DisconnectReason.ERROR); - } - } - socketIOSessionManager.socketIOSessions.remove(sessionId); - } -} diff --git a/core/src/main/java/com/glines/socketio/server/ServletBasedSocketIOConfig.java b/core/src/main/java/com/glines/socketio/server/ServletBasedSocketIOConfig.java deleted file mode 100644 index 7672617..0000000 --- a/core/src/main/java/com/glines/socketio/server/ServletBasedSocketIOConfig.java +++ /dev/null @@ -1,144 +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; -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * @author Mathieu Carbou - */ -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; - - public ServletBasedSocketIOConfig(ServletConfig config, String namespace) { - this.namespace = namespace; - this.config = config; - } - - @Override - public long getHeartbeatDelay(long def) { - return getLong(PARAM_HEARTBEAT_DELAY, def); - } - - @Override - public long getHeartbeatTimeout(long def) { - return getLong(PARAM_HEARTBEAT_TIMEOUT, def); - } - - @Override - public long getTimeout(long def) { - return getLong(PARAM_TIMEOUT, def); - } - - @Override - public int getBufferSize() { - return getInt(PARAM_BUFFER_SIZE, DEFAULT_BUFFER_SIZE); - } - - @Override - 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); - return v == null ? def : Integer.parseInt(v); - } - - @Override - public long getLong(String param, long def) { - String v = getString(param); - return v == null ? def : Long.parseLong(v); - } - - @Override - public String getNamespace() { - return namespace; - } - - @Override - 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 (LOGGER.isLoggable(Level.FINE)) - LOGGER.fine("Fallback to InitParameter: " + param); - v = config.getInitParameter(param); - } - return v; - } - - @Override - public String getString(String param, String def) { - String v = getString(param); - 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/SocketIOFrame.java b/core/src/main/java/com/glines/socketio/server/SocketIOFrame.java deleted file mode 100644 index 7f5bd2e..0000000 --- a/core/src/main/java/com/glines/socketio/server/SocketIOFrame.java +++ /dev/null @@ -1,193 +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.ArrayList; -import java.util.List; - -public class SocketIOFrame { - public static final char SEPERATOR_CHAR = ':'; - public static final char MESSAGE_SEPARATOR = '�'; // char code 65535 - - public enum FrameType { - UNKNOWN(-1), - CLOSE(0), - CONNECT(1), - HEARTBEAT(2), - MESSAGE(3), - JSON_MESSAGE(4), - EVENT(5), - ACK(6), - ERROR(7), - NOOP(8); - - private int value; - - FrameType(int value) { - this.value = value; - } - - public int value() { - return value; - } - - public static FrameType fromInt(int val) { - switch (val) { - case 0: - return CLOSE; - case 1: - return CONNECT; - case 2: - return HEARTBEAT; - case 3: - return MESSAGE; - case 4: - return JSON_MESSAGE; - case 5: - return EVENT; - case 6: - return ACK; - case 7: - return ERROR; - case 8: - return NOOP; - default: - return UNKNOWN; - } - } - } - - public static final int TEXT_MESSAGE_TYPE = 0; - public static final int JSON_MESSAGE_TYPE = 1; - - private static boolean isHexDigit(String str, int start, int end) { - for (int i = start; i < end; i++) { - char c = str.charAt(i); - if (!Character.isDigit(c) && - c < 'A' && c > 'F' && c < 'a' && c > 'f') { - return false; - } - } - return true; - } - - public static List parse(String data) { - 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; - while (data.length() > start) { - if (data.charAt(start) == MESSAGE_SEPARATOR) { - start += 1; - end = data.indexOf(MESSAGE_SEPARATOR, start); - messageEnd = Integer.parseInt(data.substring(start, end)); - start = end + 1; - end = start + 1; - messageEnd += start; - } - else { - messageEnd = data.length(); - end = start + 1; - } - - if (!isHexDigit(data, start, end)) { - break; - } - - int ftype = Integer.parseInt(data.substring(start, end)); - - FrameType frameType = FrameType.fromInt(ftype); - if (frameType == FrameType.UNKNOWN) { - break; - } - - start = end + 1; - end = data.indexOf(SEPERATOR_CHAR, start); - - int messageId = 0; - if (end - start > 1) { - messageId = Integer.parseInt(data.substring(start + 1, end)); - } - - start = end + 1; - end = data.indexOf(SEPERATOR_CHAR, start); - - String endpoint = ""; - if (end - start > 1) { - endpoint = data.substring(start + 1, end); - } - - start = end + 1; - end = messageEnd; - - messages.add(new SocketIOFrame(frameType, - frameType == FrameType.MESSAGE ? TEXT_MESSAGE_TYPE : JSON_MESSAGE_TYPE, - data.substring(start, end))); - start = end; - } - - return messages; - } - - public static String encode(FrameType type, String data) { - StringBuilder str = new StringBuilder(data.length() + 16); - str.append(Integer.toHexString(type.value())); - str.append(SEPERATOR_CHAR); - //str.append("1"); // message id - str.append(SEPERATOR_CHAR); - //str.append(""); // endpoint - str.append(SEPERATOR_CHAR); - str.append(data); - return str.toString(); - } - - private final FrameType frameType; - private final int messageType; - private final String data; - - public SocketIOFrame(FrameType frameType, int messageType, String data) { - this.frameType = frameType; - this.messageType = messageType; - this.data = data; - } - - public FrameType getFrameType() { - return frameType; - } - - public int getMessageType() { - return messageType; - } - - public String getData() { - return data; - } - - public String encode() { - return encode(frameType, data); - } -} diff --git a/core/src/main/java/com/glines/socketio/server/SocketIOInbound.java b/core/src/main/java/com/glines/socketio/server/SocketIOInbound.java deleted file mode 100644 index 62275ea..0000000 --- a/core/src/main/java/com/glines/socketio/server/SocketIOInbound.java +++ /dev/null @@ -1,55 +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.common.DisconnectReason; - -public interface SocketIOInbound { - - /** - * Called when the connection is established. This will only ever be called once. - * - * @param outbound The SocketOutbound associated with the connection - */ - void onConnect(SocketIOOutbound outbound); - - /** - * Called when the socket connection is closed. This will only ever be called once. - * This method may be called instead of onConnect() if the connection handshake isn't - * completed successfully. - * - * @param reason The reason for the disconnect. - * @param errorMessage Possibly non null error message associated with the reason for disconnect. - */ - void onDisconnect(DisconnectReason reason, String errorMessage); - - /** - * Called one per arriving message. - * - * @param messageType - * @param message - */ - void onMessage(int messageType, String message); -} diff --git a/core/src/main/java/com/glines/socketio/server/SocketIOOutbound.java b/core/src/main/java/com/glines/socketio/server/SocketIOOutbound.java deleted file mode 100644 index 6ca17bc..0000000 --- a/core/src/main/java/com/glines/socketio/server/SocketIOOutbound.java +++ /dev/null @@ -1,68 +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.common.ConnectionState; -import com.glines.socketio.common.SocketIOException; - -public interface SocketIOOutbound { - /** - * Terminate the connection. This method may return before the connection disconnect - * completes. The onDisconnect() method of the associated SocketInbound will be called - * when the disconnect is completed. The onDisconnect() method may be called during the - * invocation of this method. - */ - void disconnect(); - - /** - * Initiate an orderly close of the connection. The state will be changed to CLOSING so no - * new messages can be sent, but messages may still arrive until the distant end has - * acknowledged the close. - */ - void close(); - - ConnectionState getConnectionState(); - - /** - * Send a message to the client. This method will block if the message will not fit in the - * outbound buffer. - * If the socket is closed, becomes closed, or times out, while trying to send the message, - * the SocketClosedException will be thrown. - * - * @param message The message to send - * @throws SocketIOException - */ - void sendMessage(String message) throws SocketIOException; - - /** - * Send a message. - * - * @param messageType - * @param message - * @throws IllegalStateException if the socket is not CONNECTED. - * @throws SocketIOException - */ - void sendMessage(int messageType, String message) throws SocketIOException; -} diff --git a/core/src/main/java/com/glines/socketio/server/SocketIOServlet.java b/core/src/main/java/com/glines/socketio/server/SocketIOServlet.java deleted file mode 100644 index b4727c3..0000000 --- a/core/src/main/java/com/glines/socketio/server/SocketIOServlet.java +++ /dev/null @@ -1,168 +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.IO; - -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; -import java.util.logging.Level; -import java.util.logging.Logger; - -public abstract class SocketIOServlet extends HttpServlet { - - private static final Logger LOGGER = Logger.getLogger(SocketIOServlet.class.getName()); - private static final long serialVersionUID = 2L; - - private final SocketIOSessionManager sessionManager = new SocketIOSessionManager(); - private final TransportHandlerProvider transportHandlerProvider = new AnnotationTransportHandlerProvider(); - - private SocketIOConfig config; - - public final static String DEFAULT_HEARTBEAT_TIMEOUT = "defaultHeartbeatTimeout"; - public final static String DEFAULT_TIMEOUT = "defaultTimeout"; - - public final static String MAX_TEXT_MESSAGE_SIZE = "maxTextMessageSize"; - - @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()); - } - } - if (LOGGER.isLoggable(Level.INFO)) - LOGGER.log(Level.INFO, "Transports loaded: " + config.getTransports()); - } - - @Override - public void destroy() { - for (Transport t : config.getTransports()) { - t.destroy(); - } - super.destroy(); - } - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - serve(req, resp); - } - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - serve(req, resp); - } - - /** - * Returns an instance of SocketIOInbound or null if the connection is to be denied. - * The value of cookies and protocols may be null. - */ - protected abstract SocketIOInbound doSocketIOConnect(HttpServletRequest request); - - private void serve(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - - 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])) { - response.setContentType("text/javascript"); - InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/glines/socketio/socket.io.js"); - OutputStream os = response.getOutputStream(); - IO.copy(is, os); - return; - } 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"); - 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]); - 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/glines/socketio/server/SocketIOSession.java b/core/src/main/java/com/glines/socketio/server/SocketIOSession.java deleted file mode 100644 index 39aa9db..0000000 --- a/core/src/main/java/com/glines/socketio/server/SocketIOSession.java +++ /dev/null @@ -1,101 +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.common.ConnectionState; -import com.glines.socketio.common.DisconnectReason; - -public interface SocketIOSession { - void setAttribute(String key, Object val); - Object getAttribute(String key); - - interface SessionTask { - /** - * @return True if task was or was already canceled, false if the task is executing or has executed. - */ - boolean cancel(); - } - - String getSessionId(); - - ConnectionState getConnectionState(); - - SocketIOInbound getInbound(); - - TransportHandler getTransportHandler(); - - void setHeartbeat(long delay); - long getHeartbeat(); - void setTimeout(long timeout); - long getTimeout(); - - void startTimeoutTimer(); - void clearTimeoutTimer(); - - void startHeartbeatTimer(); - void clearHeartbeatTimer(); - - /** - * Initiate close. - */ - void startClose(); - - void onMessage(SocketIOFrame message); - void onPing(String data); - void onPong(String data); - void onClose(String data); - - /** - * Schedule a task (e.g. timeout timer) - * @param task The task to execute after specified delay. - * @param delay Delay in milliseconds. - * @return - */ - SessionTask scheduleTask(Runnable task, long delay); - - /** - * @param handler The handler or null if the connection failed. - */ - void onConnect(TransportHandler handler); - - /** - * Pass message through to contained SocketIOInbound - * If a timeout timer is set, then it will be reset. - * @param message - */ - void onMessage(String message); - - /** - * Pass disconnect through to contained SocketIOInbound and update any internal state. - * @param reason - */ - void onDisconnect(DisconnectReason reason); - - /** - * Called by handler to report that it is done and the session can be cleaned up. - * If onDisconnect has not been called yet, then it will be called with DisconnectReason.ERROR. - */ - void onShutdown(); -} diff --git a/core/src/main/java/com/glines/socketio/server/transport/AbstractHttpTransport.java b/core/src/main/java/com/glines/socketio/server/transport/AbstractHttpTransport.java deleted file mode 100644 index ce6068c..0000000 --- a/core/src/main/java/com/glines/socketio/server/transport/AbstractHttpTransport.java +++ /dev/null @@ -1,102 +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.*; -import com.glines.socketio.util.Web; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - -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"; - - @Override - public final void handle(HttpServletRequest request, - HttpServletResponse response, - Transport.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) { - session = sessionFactory.getSession(sessionId); - } - - if (session != null) { - TransportHandler handler = session.getTransportHandler(); - if (handler != null) { - handler.handle(request, response, session); - } 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 { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - } - } - } - - private SocketIOSession connect(HttpServletRequest request, - HttpServletResponse response, - InboundFactory inboundFactory, - SessionManager sessionFactory, - String sessionId) throws IOException { - SocketIOInbound inbound = inboundFactory.getInbound(request); - if (inbound != null) { - if (sessionId == null) - sessionId = request.getSession().getId().toString(); - 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); - return session; - } - return null; - } - - protected abstract DataHandler newDataHandler(SocketIOSession session); -} diff --git a/core/src/main/java/com/glines/socketio/server/transport/FlashSocketTransport.java b/core/src/main/java/com/glines/socketio/server/transport/FlashSocketTransport.java deleted file mode 100644 index 167bf4b..0000000 --- a/core/src/main/java/com/glines/socketio/server/transport/FlashSocketTransport.java +++ /dev/null @@ -1,220 +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.*; -import com.glines.socketio.util.IO; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.*; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.nio.channels.ClosedChannelException; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.SocketChannel; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class FlashSocketTransport extends AbstractTransport { - - 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"; - - 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; - - private ServerSocketChannel flashPolicyServer; - private ExecutorService executor = Executors.newCachedThreadPool(); - private Future policyAcceptorThread; - - private int flashPolicyServerPort; - private String flashPolicyServerHost; - private String flashPolicyDomain; - private String flashPolicyPorts; - - private Transport delegate; - - @Override - public TransportType getType() { - return TransportType.FLASH_SOCKET; - } - - @Override - public void init() throws TransportInitializationException { - 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" + - " - flashPolicyDomain=" + flashPolicyDomain + "\n" + - " - flashPolicyPorts=" + flashPolicyPorts + "\n" + - " - flashPolicyServerHost=" + flashPolicyServerHost + "\n" + - " - 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(); - // Ignore - } - } - } - - @Override - public void destroy() { - stopFlashPolicyServer(); - } - - @Override - public void handle(HttpServletRequest request, - HttpServletResponse response, - Transport.InboundFactory inboundFactory, - SessionManager sessionFactory) throws IOException { - - String path = request.getPathInfo(); - 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)) { - delegate.handle(request, response, inboundFactory, sessionFactory); - } else { - response.setContentType("application/x-shockwave-flash"); - InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/glines/socketio/" + FLASHFILE_NAME); - OutputStream os = response.getOutputStream(); - try { - IO.copy(is, os); - } catch (IOException e) { - LOGGER.log(Level.FINE, "Error writing " + FLASHFILE_NAME + ": " + e.getMessage(), e); - } - } - } else { - response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid " + TransportType.FLASH_SOCKET + " transport request"); - } - } - - /** - * Starts this server, binding to the previously passed SocketAddress. - */ - public void startFlashPolicyServer() throws IOException { - final String POLICY_FILE_REQUEST = ""; - flashPolicyServer = ServerSocketChannel.open(); - flashPolicyServer.socket().setReuseAddress(true); - flashPolicyServer.socket().bind(new InetSocketAddress(flashPolicyServerHost, flashPolicyServerPort)); - flashPolicyServer.configureBlocking(true); - - // Spawn a new server acceptor thread, which must accept incoming - // connections indefinitely - until a ClosedChannelException is thrown. - policyAcceptorThread = executor.submit(new Runnable() { - @Override - public void run() { - try { - while (!Thread.currentThread().isInterrupted()) { - final SocketChannel serverSocket = flashPolicyServer.accept(); - executor.submit(new Runnable() { - @Override - public void run() { - try { - serverSocket.configureBlocking(true); - Socket s = serverSocket.socket(); - StringBuilder request = new StringBuilder(); - InputStreamReader in = new InputStreamReader(s.getInputStream()); - int c; - while ((c = in.read()) != 0 && request.length() <= POLICY_FILE_REQUEST.length()) { - request.append((char) c); - } - if (request.toString().equalsIgnoreCase(POLICY_FILE_REQUEST) || - flashPolicyDomain != null && flashPolicyPorts != null) { - PrintWriter out = new PrintWriter(s.getOutputStream()); - out.println(""); - out.write(0); - out.flush(); - } - serverSocket.close(); - } catch (IOException e) { - LOGGER.log(Level.FINE, "startFlashPolicyServer: " + e.getMessage(), e); - } finally { - try { - serverSocket.close(); - } catch (IOException e) { - // Ignore error on close. - } - } - } - }); - } - } catch (ClosedChannelException e) { - Thread.currentThread().interrupt(); - } catch (IOException e) { - Thread.currentThread().interrupt(); - throw new IllegalStateException("Server should not throw a misunderstood IOException", e); - } - } - }); - } - - private void stopFlashPolicyServer() { - if (flashPolicyServer != null) { - try { - flashPolicyServer.close(); - } catch (IOException e) { - // Ignore - } - } - if (policyAcceptorThread != null) { - try { - policyAcceptorThread.get(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new IllegalStateException(); - } catch (ExecutionException e) { - throw new IllegalStateException("Server thread threw an exception", e.getCause()); - } - if (!policyAcceptorThread.isDone()) { - throw new IllegalStateException("Server acceptor thread has not stopped."); - } - } - } - -} 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/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/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/util/DefaultLoader.java b/core/src/main/java/com/glines/socketio/util/DefaultLoader.java deleted file mode 100644 index b774f76..0000000 --- a/core/src/main/java/com/glines/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.glines.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/glines/socketio/util/IO.java b/core/src/main/java/com/glines/socketio/util/IO.java deleted file mode 100644 index cb18dd4..0000000 --- a/core/src/main/java/com/glines/socketio/util/IO.java +++ /dev/null @@ -1,65 +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.util; - -import java.io.*; - -/** - * @author Mathieu Carbou - */ -public final class IO { - private IO() { - } - - public static void copy(InputStream is, OutputStream os) throws IOException { - byte[] buffer = new byte[64 * 1024]; - int len; - while ((len = is.read(buffer)) >= 0) - os.write(buffer, 0, len); - } - - public static void copy(Reader in, Writer out) throws IOException { - char[] buffer = new char[64 * 1024]; - int len; - while ((len = in.read(buffer)) >= 0) - out.write(buffer, 0, len); - } - - public static String toString(InputStream in) throws IOException { - return toString(in, null); - } - - public static String toString(Reader in) throws IOException { - StringWriter writer = new StringWriter(); - copy(in, writer); - return writer.toString(); - } - - public static String toString(InputStream in, String encoding) throws IOException { - InputStreamReader reader = encoding == null ? new InputStreamReader(in) : new InputStreamReader(in, encoding); - return toString(reader); - } - -} diff --git a/core/src/main/java/com/glines/socketio/util/JSON.java b/core/src/main/java/com/glines/socketio/util/JSON.java deleted file mode 100644 index 174f88c..0000000 --- a/core/src/main/java/com/glines/socketio/util/JSON.java +++ /dev/null @@ -1,1116 +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.util; - -import java.io.IOException; -import java.io.Reader; -import java.lang.reflect.Array; -import java.util.*; - -public final class JSON { - - private static final int BUFFER = 1024; - - private JSON() { - } - - public static String toString(Object object) { - StringBuilder buffer = new StringBuilder(BUFFER); - append(buffer, object); - return buffer.toString(); - } - - - public static String toString(Map object) { - StringBuilder buffer = new StringBuilder(BUFFER); - appendMap(buffer, object); - return buffer.toString(); - } - - - public static String toString(Object[] array) { - StringBuilder buffer = new StringBuilder(BUFFER); - appendArray(buffer, array); - return buffer.toString(); - } - - - /** - * @param s String containing JSON object or array. - * @return A Map, Object array or primitive array parsed from the JSON. - */ - public static Object parse(String s) { - return parse(new StringSource(s), false); - } - - - /** - * @param s String containing JSON object or array. - * @param stripOuterComment If true, an outer comment around the JSON is ignored. - * @return A Map, Object array or primitive array parsed from the JSON. - */ - public static Object parse(String s, boolean stripOuterComment) { - return parse(new StringSource(s), stripOuterComment); - } - - - /** - * @param in Reader containing JSON object or array. - * @return A Map, Object array or primitive array parsed from the JSON. - * @throws java.io.IOException - - */ - public static Object parse(Reader in) throws IOException { - return parse(new ReaderSource(in), false); - } - - - /** - * @param in Reader containing JSON object or array. - * @param stripOuterComment If true, an outer comment around the JSON is ignored. - * @return A Map, Object array or primitive array parsed from the JSON. - * @throws java.io.IOException - - */ - public static Object parse(Reader in, boolean stripOuterComment) throws IOException { - return parse(new ReaderSource(in), stripOuterComment); - } - - - /** - * Append object as JSON to string buffer. - * - * @param buffer the buffer to append to - * @param object the object to append - */ - public static void append(Appendable buffer, Object object) { - try { - if (object == null) - buffer.append("null"); - else if (object instanceof Convertible) - appendJSON(buffer, (Convertible) object); - else if (object instanceof Generator) - appendJSON(buffer, (Generator) object); - else if (object instanceof Map) - appendMap(buffer, (Map) object); - else if (object instanceof Collection) - appendArray(buffer, (Collection) object); - else if (object.getClass().isArray()) - appendArray(buffer, object); - else if (object instanceof Number) - appendNumber(buffer, (Number) object); - else if (object instanceof Boolean) - appendBoolean(buffer, (Boolean) object); - else if (object instanceof Character) - appendString(buffer, object.toString()); - else if (object instanceof String) - appendString(buffer, (String) object); - else { - appendString(buffer, object.toString()); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - - public static void appendNull(Appendable buffer) { - try { - buffer.append("null"); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - - public static void appendJSON(final Appendable buffer, Convertible converter) { - ConvertableOutput out = new ConvertableOutput(buffer); - converter.toJSON(out); - out.complete(); - } - - - public static void appendJSON(Appendable buffer, Generator generator) { - generator.addJSON(buffer); - } - - - public static void appendMap(Appendable buffer, Map map) { - try { - if (map == null) { - appendNull(buffer); - return; - } - - buffer.append('{'); - Iterator iter = map.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = (Map.Entry) iter.next(); - quote(buffer, entry.getKey().toString()); - buffer.append(':'); - append(buffer, entry.getValue()); - if (iter.hasNext()) - buffer.append(','); - } - - buffer.append('}'); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - - public static void appendArray(Appendable buffer, Collection collection) { - try { - if (collection == null) { - appendNull(buffer); - return; - } - - buffer.append('['); - Iterator iter = collection.iterator(); - boolean first = true; - while (iter.hasNext()) { - if (!first) - buffer.append(','); - - first = false; - append(buffer, iter.next()); - } - - buffer.append(']'); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - - public static void appendArray(Appendable buffer, Object array) { - try { - if (array == null) { - appendNull(buffer); - return; - } - - buffer.append('['); - int length = Array.getLength(array); - - for (int i = 0; i < length; i++) { - if (i != 0) - buffer.append(','); - append(buffer, Array.get(array, i)); - } - - buffer.append(']'); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - - public static void appendBoolean(Appendable buffer, Boolean b) { - try { - if (b == null) { - appendNull(buffer); - return; - } - buffer.append(b ? "true" : "false"); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - - public static void appendNumber(Appendable buffer, Number number) { - try { - if (number == null) { - appendNull(buffer); - return; - } - buffer.append(String.valueOf(number)); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - - public static void appendString(Appendable buffer, String string) { - if (string == null) { - appendNull(buffer); - return; - } - quote(buffer, string); - } - - // Parsing utilities - - - protected static String toString(char[] buffer, int offset, int length) { - return new String(buffer, offset, length); - } - - - protected static Map newMap() { - return new HashMap(); - } - - - protected static Object[] newArray(int size) { - return new Object[size]; - } - - protected static Object convertTo(Class type, Map map) { - if (type != null && Convertible.class.isAssignableFrom(type)) { - try { - Convertible conv = (Convertible) type.newInstance(); - conv.fromJSON(map); - return conv; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - return map; - } - - - public static Object parse(Source source, boolean stripOuterComment) { - int comment_state = 0; // 0=no comment, 1="/", 2="/*", 3="/* *" -1="//" - if (!stripOuterComment) - return parse(source); - - int strip_state = 1; // 0=no strip, 1=wait for /*, 2= wait for */ - - Object o = null; - while (source.hasNext()) { - char c = source.peek(); - - // handle // or /* comment - if (comment_state == 1) { - switch (c) { - case '/': - comment_state = -1; - break; - case '*': - comment_state = 2; - if (strip_state == 1) { - comment_state = 0; - strip_state = 2; - } - } - } - // handle /* */ comment - else if (comment_state > 1) { - switch (c) { - case '*': - comment_state = 3; - break; - case '/': - if (comment_state == 3) { - comment_state = 0; - if (strip_state == 2) - return o; - } else - comment_state = 2; - break; - default: - comment_state = 2; - } - } - // handle // comment - else if (comment_state < 0) { - switch (c) { - case '\r': - case '\n': - comment_state = 0; - default: - break; - } - } - // handle unknown - else { - if (!Character.isWhitespace(c)) { - if (c == '/') - comment_state = 1; - else if (c == '*') - comment_state = 3; - else if (o == null) { - o = parse(source); - continue; - } - } - } - - source.next(); - } - - return o; - } - - - public static Object parse(Source source) { - int comment_state = 0; // 0=no comment, 1="/", 2="/*", 3="/* *" -1="//" - - while (source.hasNext()) { - char c = source.peek(); - - // handle // or /* comment - if (comment_state == 1) { - switch (c) { - case '/': - comment_state = -1; - break; - case '*': - comment_state = 2; - } - } - // handle /* */ comment - else if (comment_state > 1) { - switch (c) { - case '*': - comment_state = 3; - break; - case '/': - if (comment_state == 3) - comment_state = 0; - else - comment_state = 2; - break; - default: - comment_state = 2; - } - } - // handle // comment - else if (comment_state < 0) { - switch (c) { - case '\r': - case '\n': - comment_state = 0; - break; - default: - break; - } - } - // handle unknown - else { - switch (c) { - case '{': - return parseObject(source); - case '[': - return parseArray(source); - case '"': - return parseString(source); - case '-': - return parseNumber(source); - - case 'n': - complete("null", source); - return null; - case 't': - complete("true", source); - return Boolean.TRUE; - case 'f': - complete("false", source); - return Boolean.FALSE; - case 'u': - complete("undefined", source); - return null; - case 'N': - complete("NaN", source); - return null; - - case '/': - comment_state = 1; - break; - - default: - if (Character.isDigit(c)) - return parseNumber(source); - else if (Character.isWhitespace(c)) - break; - return handleUnknown(source, c); - } - } - source.next(); - } - - return null; - } - - - protected static Object handleUnknown(Source source, char c) { - throw new IllegalStateException("unknown char '" + c + "'(" + (int) c + ") in " + source); - } - - - private static Object parseObject(Source source) { - if (source.next() != '{') - throw new IllegalStateException(); - Map map = newMap(); - - char next = seekTo("\"}", source); - - while (source.hasNext()) { - if (next == '}') { - source.next(); - break; - } - - String name = parseString(source); - seekTo(':', source); - source.next(); - - Object value = parse(source); - map.put(name, value); - - seekTo(",}", source); - next = source.next(); - if (next == '}') - break; - else - next = seekTo("\"}", source); - } - - String classname = (String) map.get("class"); - if (classname != null) { - try { - Class c = Thread.currentThread().getContextClassLoader().loadClass(classname); - return convertTo(c, map); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } - } - return map; - } - - - protected static Object parseArray(Source source) { - if (source.next() != '[') - throw new IllegalStateException(); - - int size = 0; - ArrayList list = null; - Object item = null; - boolean coma = true; - - while (source.hasNext()) { - char c = source.peek(); - switch (c) { - case ']': - source.next(); - switch (size) { - case 0: - return newArray(0); - case 1: - Object array = newArray(1); - Array.set(array, 0, item); - return array; - default: - return list.toArray(newArray(list.size())); - } - - case ',': - if (coma) - throw new IllegalStateException(); - coma = true; - source.next(); - break; - - default: - if (Character.isWhitespace(c)) - source.next(); - else { - coma = false; - if (size++ == 0) - item = parse(source); - else if (list == null) { - list = new ArrayList(); - list.add(item); - item = parse(source); - list.add(item); - item = null; - } else { - item = parse(source); - list.add(item); - item = null; - } - } - } - - } - - throw new IllegalStateException("unexpected end of array"); - } - - - protected static String parseString(Source source) { - if (source.next() != '"') - throw new IllegalStateException(); - - boolean escape = false; - - StringBuilder b = null; - final char[] scratch = source.scratchBuffer(); - - if (scratch != null) { - int i = 0; - while (source.hasNext()) { - if (i >= scratch.length) { - // we have filled the scratch buffer, so we must - // use the StringBuffer for a large string - b = new StringBuilder(scratch.length * 2); - b.append(scratch, 0, i); - break; - } - - char c = source.next(); - - if (escape) { - escape = false; - switch (c) { - case '"': - scratch[i++] = '"'; - break; - case '\\': - scratch[i++] = '\\'; - break; - case '/': - scratch[i++] = '/'; - break; - case 'b': - scratch[i++] = '\b'; - break; - case 'f': - scratch[i++] = '\f'; - break; - case 'n': - scratch[i++] = '\n'; - break; - case 'r': - scratch[i++] = '\r'; - break; - case 't': - scratch[i++] = '\t'; - break; - case 'u': - char uc = (char) ((convertHexDigit((byte) source.next()) << 12) + (convertHexDigit((byte) source.next()) << 8) - + (convertHexDigit((byte) source.next()) << 4) + (convertHexDigit((byte) source.next()))); - scratch[i++] = uc; - break; - default: - scratch[i++] = c; - } - } else if (c == '\\') { - escape = true; - } else if (c == '\"') { - // Return string that fits within scratch buffer - return toString(scratch, 0, i); - } else - scratch[i++] = c; - } - - // Missing end quote, but return string anyway ? - if (b == null) - return toString(scratch, 0, i); - } else - b = new StringBuilder(BUFFER); - - // parse large string into string buffer - final StringBuilder builder = b; - while (source.hasNext()) { - char c = source.next(); - - if (escape) { - escape = false; - switch (c) { - case '"': - builder.append('"'); - break; - case '\\': - builder.append('\\'); - break; - case '/': - builder.append('/'); - break; - case 'b': - builder.append('\b'); - break; - case 'f': - builder.append('\f'); - break; - case 'n': - builder.append('\n'); - break; - case 'r': - builder.append('\r'); - break; - case 't': - builder.append('\t'); - break; - case 'u': - char uc = (char) ((convertHexDigit((byte) source.next()) << 12) + (convertHexDigit((byte) source.next()) << 8) - + (convertHexDigit((byte) source.next()) << 4) + (convertHexDigit((byte) source.next()))); - builder.append(uc); - break; - default: - builder.append(c); - } - } else if (c == '\\') { - escape = true; - } else if (c == '\"') - break; - else - builder.append(c); - } - return builder.toString(); - } - - - public static Number parseNumber(Source source) { - boolean minus = false; - long number = 0; - StringBuilder buffer = null; - - longLoop: - while (source.hasNext()) { - char c = source.peek(); - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - number = number * 10 + (c - '0'); - source.next(); - break; - - case '-': - case '+': - if (number != 0) - throw new IllegalStateException("bad number"); - minus = true; - source.next(); - break; - - case '.': - case 'e': - case 'E': - buffer = new StringBuilder(16); - if (minus) - buffer.append('-'); - buffer.append(number); - buffer.append(c); - source.next(); - break longLoop; - - default: - break longLoop; - } - } - - if (buffer == null) - return minus ? -1 * number : number; - - doubleLoop: - while (source.hasNext()) { - char c = source.peek(); - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '-': - case '.': - case '+': - case 'e': - case 'E': - buffer.append(c); - source.next(); - break; - - default: - break doubleLoop; - } - } - return new Double(buffer.toString()); - - } - - - protected static void seekTo(char seek, Source source) { - while (source.hasNext()) { - char c = source.peek(); - if (c == seek) - return; - - if (!Character.isWhitespace(c)) - throw new IllegalStateException("Unexpected '" + c + " while seeking '" + seek + "'"); - source.next(); - } - - throw new IllegalStateException("Expected '" + seek + "'"); - } - - - protected static char seekTo(String seek, Source source) { - while (source.hasNext()) { - char c = source.peek(); - if (seek.indexOf(c) >= 0) { - return c; - } - - if (!Character.isWhitespace(c)) - throw new IllegalStateException("Unexpected '" + c + "' while seeking one of '" + seek + "'"); - source.next(); - } - - throw new IllegalStateException("Expected one of '" + seek + "'"); - } - - - protected static void complete(String seek, Source source) { - int i = 0; - while (source.hasNext() && i < seek.length()) { - char c = source.next(); - if (c != seek.charAt(i++)) - throw new IllegalStateException("Unexpected '" + c + " while seeking \"" + seek + "\""); - } - - if (i < seek.length()) - throw new IllegalStateException("Expected \"" + seek + "\""); - } - - private static final class ConvertableOutput implements Output { - private final Appendable _buffer; - char c = '{'; - - private ConvertableOutput(Appendable buffer) { - _buffer = buffer; - } - - public void complete() { - try { - if (c == '{') - _buffer.append("{}"); - else if (c != 0) - _buffer.append("}"); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public void add(Object obj) { - if (c == 0) - throw new IllegalStateException(); - append(_buffer, obj); - c = 0; - } - - public void addClass(Class type) { - try { - if (c == 0) - throw new IllegalStateException(); - _buffer.append(c); - _buffer.append("\"class\":"); - append(_buffer, type.getName()); - c = ','; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public void add(String name, Object value) { - try { - if (c == 0) - throw new IllegalStateException(); - _buffer.append(c); - quote(_buffer, name); - _buffer.append(':'); - append(_buffer, value); - c = ','; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public void add(String name, double value) { - try { - if (c == 0) - throw new IllegalStateException(); - _buffer.append(c); - quote(_buffer, name); - _buffer.append(':'); - appendNumber(_buffer, value); - c = ','; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public void add(String name, long value) { - try { - if (c == 0) - throw new IllegalStateException(); - _buffer.append(c); - quote(_buffer, name); - _buffer.append(':'); - appendNumber(_buffer, value); - c = ','; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public void add(String name, boolean value) { - try { - if (c == 0) - throw new IllegalStateException(); - _buffer.append(c); - quote(_buffer, name); - _buffer.append(':'); - appendBoolean(_buffer, value ? Boolean.TRUE : Boolean.FALSE); - c = ','; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - - - public static interface Source { - boolean hasNext(); - - char next(); - - char peek(); - - char[] scratchBuffer(); - } - - - public static class StringSource implements Source { - private final String string; - private int index; - private char[] scratch; - - public StringSource(String s) { - string = s; - } - - public boolean hasNext() { - if (index < string.length()) - return true; - scratch = null; - return false; - } - - public char next() { - return string.charAt(index++); - } - - public char peek() { - return string.charAt(index); - } - - @Override - public String toString() { - return string.substring(0, index) + "|||" + string.substring(index); - } - - public char[] scratchBuffer() { - if (scratch == null) - scratch = new char[string.length()]; - return scratch; - } - } - - - public static class ReaderSource implements Source { - private Reader _reader; - private int _next = -1; - private char[] scratch; - - public ReaderSource(Reader r) { - _reader = r; - } - - public boolean hasNext() { - getNext(); - if (_next < 0) { - scratch = null; - return false; - } - return true; - } - - public char next() { - getNext(); - char c = (char) _next; - _next = -1; - return c; - } - - public char peek() { - getNext(); - return (char) _next; - } - - private void getNext() { - if (_next < 0) { - try { - _next = _reader.read(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - - public char[] scratchBuffer() { - if (scratch == null) - scratch = new char[1024]; - return scratch; - } - - } - - /** - * JSON Output class for use by {@link Convertible}. - */ - public static interface Output { - public void addClass(Class c); - - public void add(Object obj); - - public void add(String name, Object value); - - public void add(String name, double value); - - public void add(String name, long value); - - public void add(String name, boolean value); - } - - - /** - * JSON Convertible object. Object can implement this interface in a similar - * way to the {@link java.io.Externalizable} interface is used to allow classes to - * provide their own serialization mechanism. - *

- * A JSON.Convertible object may be written to a JSONObject or initialized - * from a Map of field names to values. - *

- * If the JSON is to be convertible back to an Object, then the method - * {@link Output#addClass(Class)} must be called from within toJSON() - */ - public static interface Convertible { - public void toJSON(Output out); - - public void fromJSON(Map object); - } - - - /** - * JSON Generator. A class that can add it's JSON representation directly to - * a StringBuffer. This is useful for object instances that are frequently - * converted and wish to avoid multiple Conversions - */ - public interface Generator { - public void addJSON(Appendable buffer); - } - - /** - * Quote a string into an Appendable. - * The characters ", \, \n, \r, \t, \f and \b are escaped - * - * @param buf The Appendable - * @param s The String to quote. - */ - private static void quote(Appendable buf, String s) { - try { - buf.append('"'); - - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - switch (c) { - case '"': - buf.append("\\\""); - continue; - case '\\': - buf.append("\\\\"); - continue; - case '\n': - buf.append("\\n"); - continue; - case '\r': - buf.append("\\r"); - continue; - case '\t': - buf.append("\\t"); - continue; - case '\f': - buf.append("\\f"); - continue; - case '\b': - buf.append("\\b"); - continue; - - default: - if (c < 0x10) { - buf.append("\\u000"); - buf.append(Integer.toString(c, 16)); - } else if (c <= 0x1f) { - buf.append("\\u00"); - buf.append(Integer.toString(c, 16)); - } else - buf.append(c); - } - } - - buf.append('"'); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /** - * @param b An ASCII encoded character 0-9 a-f A-F - * @return The byte value of the character 0-16. - */ - private static byte convertHexDigit(byte b) { - if ((b >= '0') && (b <= '9')) return (byte) (b - '0'); - if ((b >= 'a') && (b <= 'f')) return (byte) (b - 'a' + 10); - if ((b >= 'A') && (b <= 'F')) return (byte) (b - 'A' + 10); - return 0; - } - -} diff --git a/core/src/main/java/com/glines/socketio/util/JdkOverLog4j.java b/core/src/main/java/com/glines/socketio/util/JdkOverLog4j.java deleted file mode 100644 index 07f4e53..0000000 --- a/core/src/main/java/com/glines/socketio/util/JdkOverLog4j.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.util; - -/** - * @author Mathieu Carbou (mathieu.carbou@gmail.com) - */ - -import org.apache.log4j.Level; -import org.apache.log4j.Logger; - -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Handler; -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() { - private static final long serialVersionUID = 4627902405916164098L; - - { - put(java.util.logging.Level.OFF, Level.OFF); - put(java.util.logging.Level.SEVERE, Level.ERROR); - put(java.util.logging.Level.WARNING, Level.WARN); - put(java.util.logging.Level.INFO, Level.INFO); - put(java.util.logging.Level.CONFIG, Level.INFO); - put(java.util.logging.Level.FINE, Level.DEBUG); - put(java.util.logging.Level.FINER, Level.DEBUG); - put(java.util.logging.Level.FINEST, Level.DEBUG); - put(java.util.logging.Level.ALL, Level.ALL); - } - }; - - private static final Map LEVELS_LOG4J_TO_JDK = new HashMap() { - private static final long serialVersionUID = 1880156903738858787L; - - { - put(Level.OFF, java.util.logging.Level.OFF); - put(Level.FATAL, java.util.logging.Level.SEVERE); - put(Level.ERROR, java.util.logging.Level.SEVERE); - put(Level.WARN, java.util.logging.Level.WARNING); - put(Level.INFO, java.util.logging.Level.INFO); - put(Level.DEBUG, java.util.logging.Level.FINE); - put(Level.TRACE, java.util.logging.Level.FINE); - put(Level.ALL, java.util.logging.Level.ALL); - } - }; - - @Override - public void publish(LogRecord record) { - // normalize levels - Logger log4jLogger = Logger.getLogger(record.getLoggerName()); - Level log4jLevel = log4jLogger.getEffectiveLevel(); - java.util.logging.Logger jdkLogger = java.util.logging.Logger.getLogger(record.getLoggerName()); - java.util.logging.Level expectedJdkLevel = LEVELS_LOG4J_TO_JDK.get(log4jLevel); - if (expectedJdkLevel == null) - throw new AssertionError("Level not supported yet - have a bug !" + log4jLevel); - if (!expectedJdkLevel.equals(jdkLogger.getLevel())) { - jdkLogger.setLevel(expectedJdkLevel); - } - log4jLogger.log(record.getLoggerName(), LEVELS_JDK_TO_LOG4J.get(record.getLevel()), record.getMessage(), record.getThrown()); - } - - @Override - public void flush() { - } - - @Override - public void close() throws SecurityException { - } - - public static void install() { - LogManager.getLogManager().reset(); - LogManager.getLogManager().getLogger("").addHandler(new JdkOverLog4j()); - LogManager.getLogManager().getLogger("").setLevel(java.util.logging.Level.ALL); - } -} diff --git a/core/src/main/java/com/glines/socketio/util/Loader.java b/core/src/main/java/com/glines/socketio/util/Loader.java deleted file mode 100644 index 03a63e8..0000000 --- a/core/src/main/java/com/glines/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.glines.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/glines/socketio/util/ServiceClassLoader.java b/core/src/main/java/com/glines/socketio/util/ServiceClassLoader.java deleted file mode 100644 index f7065df..0000000 --- a/core/src/main/java/com/glines/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.glines.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/glines/socketio/util/URI.java b/core/src/main/java/com/glines/socketio/util/URI.java deleted file mode 100644 index 58da700..0000000 --- a/core/src/main/java/com/glines/socketio/util/URI.java +++ /dev/null @@ -1,122 +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.util; - -import java.io.UnsupportedEncodingException; - -public final class URI { - - private URI() { - } - - public static String decodePath(String path) { - return decodePath(path, "UTF-8"); - } - - /* Decode a URI path. - * @param path The path the encode - * @param buf StringBuilder to encode path into - */ - public static String decodePath(String path, String charset) { - if (path == null) - return null; - char[] chars = null; - int n = 0; - byte[] bytes = null; - int b = 0; - - int len = path.length(); - - for (int i = 0; i < len; i++) { - char c = path.charAt(i); - - if (c == '%' && (i + 2) < len) { - if (chars == null) { - chars = new char[len]; - bytes = new byte[len]; - path.getChars(0, i, chars, 0); - } - bytes[b++] = (byte) (0xff & parseInt(path, i + 1, 2, 16)); - i += 2; - continue; - } else if (bytes == null) { - n++; - continue; - } - - if (b > 0) { - String s; - try { - s = new String(bytes, 0, b, charset); - } catch (UnsupportedEncodingException e) { - s = new String(bytes, 0, b); - } - s.getChars(0, s.length(), chars, n); - n += s.length(); - b = 0; - } - - chars[n++] = c; - } - - if (chars == null) - return path; - - if (b > 0) { - String s; - try { - s = new String(bytes, 0, b, charset); - } catch (UnsupportedEncodingException e) { - s = new String(bytes, 0, b); - } - s.getChars(0, s.length(), chars, n); - n += s.length(); - } - - return new String(chars, 0, n); - } - - private static int parseInt(String s, int offset, int length, int base) throws NumberFormatException { - int value = 0; - if (length < 0) - length = s.length() - offset; - for (int i = 0; i < length; i++) { - char c = s.charAt(offset + i); - int digit = c - '0'; - if (digit < 0 || digit >= base || digit >= 10) { - digit = 10 + c - 'A'; - if (digit < 10 || digit >= base) - digit = 10 + c - 'a'; - } - if (digit < 0 || digit >= base) - throw new NumberFormatException(s.substring(offset, offset + length)); - value = value * base + digit; - } - return value; - } -} - - - diff --git a/core/src/main/java/com/glines/socketio/util/Web.java b/core/src/main/java/com/glines/socketio/util/Web.java deleted file mode 100644 index 0dcf7a6..0000000 --- a/core/src/main/java/com/glines/socketio/util/Web.java +++ /dev/null @@ -1,64 +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.util; - -import javax.servlet.http.HttpServletRequest; -import java.security.SecureRandom; -import java.util.Random; - -/** - * @author Mathieu Carbou - */ -public final class Web { - - private static final char[] BASE64_ALPHABET ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".toCharArray(); - private static Random random = new SecureRandom(); - - private Web() { - } - - public static String extractSessionId(HttpServletRequest request) { - 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) { - return parts[2] == null || parts[2].length() == 0 || parts[2].equals("null") ? null : parts[2]; - } - } - return null; - } - - public static String generateRandomString(int length) { - StringBuilder result = new StringBuilder(length); - byte[] bytes = new byte[length]; - random.nextBytes(bytes); - for (byte aByte : bytes) { - result.append(BASE64_ALPHABET[aByte & 0x3F]); - } - return result.toString(); - } - -} 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/glines/socketio/WebSocketMain.swf deleted file mode 100644 index 8174466..0000000 Binary files a/core/src/main/resources/com/glines/socketio/WebSocketMain.swf and /dev/null differ diff --git a/core/src/main/resources/com/glines/socketio/socket.io.js b/core/src/main/resources/com/glines/socketio/socket.io.js deleted file mode 100644 index d01e8da..0000000 --- a/core/src/main/resources/com/glines/socketio/socket.io.js +++ /dev/null @@ -1,3871 +0,0 @@ -/*! Socket.IO.js build:0.9.11, development. Copyright(c) 2011 LearnBoost MIT Licensed */ - -var io = ('undefined' === typeof module ? {} : module.exports); -(function() { - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, global) { - - /** - * IO namespace. - * - * @namespace - */ - - var io = exports; - - /** - * Socket.IO version - * - * @api public - */ - - io.version = '0.9.11'; - - /** - * Protocol implemented. - * - * @api public - */ - - io.protocol = 1; - - /** - * Available transports, these will be populated with the available transports - * - * @api public - */ - - io.transports = []; - - /** - * Keep track of jsonp callbacks. - * - * @api private - */ - - io.j = []; - - /** - * Keep track of our io.Sockets - * - * @api private - */ - io.sockets = {}; - - - /** - * Manages connections to hosts. - * - * @param {String} uri - * @Param {Boolean} force creation of new socket (defaults to false) - * @api public - */ - - io.connect = function (host, details) { - var uri = io.util.parseUri(host) - , uuri - , socket; - - if (global && global.location) { - uri.protocol = uri.protocol || global.location.protocol.slice(0, -1); - uri.host = uri.host || (global.document - ? global.document.domain : global.location.hostname); - uri.port = uri.port || global.location.port; - } - - uuri = io.util.uniqueUri(uri); - - var options = { - host: uri.host - , secure: 'https' == uri.protocol - , port: uri.port || ('https' == uri.protocol ? 443 : 80) - , query: uri.query || '' - }; - - io.util.merge(options, details); - - if (options['force new connection'] || !io.sockets[uuri]) { - socket = new io.Socket(options); - } - - if (!options['force new connection'] && socket) { - io.sockets[uuri] = socket; - } - - socket = socket || io.sockets[uuri]; - - // if path is different from '' or / - return socket.of(uri.path.length > 1 ? uri.path : ''); - }; - -})('object' === typeof module ? module.exports : (this.io = {}), this); -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, global) { - - /** - * Utilities namespace. - * - * @namespace - */ - - var util = exports.util = {}; - - /** - * Parses an URI - * - * @author Steven Levithan (MIT license) - * @api public - */ - - var re = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; - - var parts = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password', - 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', - 'anchor']; - - util.parseUri = function (str) { - var m = re.exec(str || '') - , uri = {} - , i = 14; - - while (i--) { - uri[parts[i]] = m[i] || ''; - } - - return uri; - }; - - /** - * Produces a unique url that identifies a Socket.IO connection. - * - * @param {Object} uri - * @api public - */ - - util.uniqueUri = function (uri) { - var protocol = uri.protocol - , host = uri.host - , port = uri.port; - - if ('document' in global) { - host = host || document.domain; - port = port || (protocol == 'https' - && document.location.protocol !== 'https:' ? 443 : document.location.port); - } else { - host = host || 'localhost'; - - if (!port && protocol == 'https') { - port = 443; - } - } - - return (protocol || 'http') + '://' + host + ':' + (port || 80); - }; - - /** - * Mergest 2 query strings in to once unique query string - * - * @param {String} base - * @param {String} addition - * @api public - */ - - util.query = function (base, addition) { - var query = util.chunkQuery(base || '') - , components = []; - - util.merge(query, util.chunkQuery(addition || '')); - for (var part in query) { - if (query.hasOwnProperty(part)) { - components.push(part + '=' + query[part]); - } - } - - return components.length ? '?' + components.join('&') : ''; - }; - - /** - * Transforms a querystring in to an object - * - * @param {String} qs - * @api public - */ - - util.chunkQuery = function (qs) { - var query = {} - , params = qs.split('&') - , i = 0 - , l = params.length - , kv; - - for (; i < l; ++i) { - kv = params[i].split('='); - if (kv[0]) { - query[kv[0]] = kv[1]; - } - } - - return query; - }; - - /** - * Executes the given function when the page is loaded. - * - * io.util.load(function () { console.log('page loaded'); }); - * - * @param {Function} fn - * @api public - */ - - var pageLoaded = false; - - util.load = function (fn) { - if ('document' in global && document.readyState === 'complete' || pageLoaded) { - return fn(); - } - - util.on(global, 'load', fn, false); - }; - - /** - * Adds an event. - * - * @api private - */ - - util.on = function (element, event, fn, capture) { - if (element.attachEvent) { - element.attachEvent('on' + event, fn); - } else if (element.addEventListener) { - element.addEventListener(event, fn, capture); - } - }; - - /** - * Generates the correct `XMLHttpRequest` for regular and cross domain requests. - * - * @param {Boolean} [xdomain] Create a request that can be used cross domain. - * @returns {XMLHttpRequest|false} If we can create a XMLHttpRequest. - * @api private - */ - - util.request = function (xdomain) { - - if (xdomain && 'undefined' != typeof XDomainRequest && !util.ua.hasCORS) { - return new XDomainRequest(); - } - - if ('undefined' != typeof XMLHttpRequest && (!xdomain || util.ua.hasCORS)) { - return new XMLHttpRequest(); - } - - if (!xdomain) { - try { - return new window[(['Active'].concat('Object').join('X'))]('Microsoft.XMLHTTP'); - } catch(e) { } - } - - return null; - }; - - /** - * XHR based transport constructor. - * - * @constructor - * @api public - */ - - /** - * Change the internal pageLoaded value. - */ - - if ('undefined' != typeof window) { - util.load(function () { - pageLoaded = true; - }); - } - - /** - * Defers a function to ensure a spinner is not displayed by the browser - * - * @param {Function} fn - * @api public - */ - - util.defer = function (fn) { - if (!util.ua.webkit || 'undefined' != typeof importScripts) { - return fn(); - } - - util.load(function () { - setTimeout(fn, 100); - }); - }; - - /** - * Merges two objects. - * - * @api public - */ - - util.merge = function merge (target, additional, deep, lastseen) { - var seen = lastseen || [] - , depth = typeof deep == 'undefined' ? 2 : deep - , prop; - - for (prop in additional) { - if (additional.hasOwnProperty(prop) && util.indexOf(seen, prop) < 0) { - if (typeof target[prop] !== 'object' || !depth) { - target[prop] = additional[prop]; - seen.push(additional[prop]); - } else { - util.merge(target[prop], additional[prop], depth - 1, seen); - } - } - } - - return target; - }; - - /** - * Merges prototypes from objects - * - * @api public - */ - - util.mixin = function (ctor, ctor2) { - util.merge(ctor.prototype, ctor2.prototype); - }; - - /** - * Shortcut for prototypical and static inheritance. - * - * @api private - */ - - util.inherit = function (ctor, ctor2) { - function f() {}; - f.prototype = ctor2.prototype; - ctor.prototype = new f; - }; - - /** - * Checks if the given object is an Array. - * - * io.util.isArray([]); // true - * io.util.isArray({}); // false - * - * @param Object obj - * @api public - */ - - util.isArray = Array.isArray || function (obj) { - return Object.prototype.toString.call(obj) === '[object Array]'; - }; - - /** - * Intersects values of two arrays into a third - * - * @api public - */ - - util.intersect = function (arr, arr2) { - var ret = [] - , longest = arr.length > arr2.length ? arr : arr2 - , shortest = arr.length > arr2.length ? arr2 : arr; - - for (var i = 0, l = shortest.length; i < l; i++) { - if (~util.indexOf(longest, shortest[i])) - ret.push(shortest[i]); - } - - return ret; - }; - - /** - * Array indexOf compatibility. - * - * @see bit.ly/a5Dxa2 - * @api public - */ - - util.indexOf = function (arr, o, i) { - - for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0; - i < j && arr[i] !== o; i++) {} - - return j <= i ? -1 : i; - }; - - /** - * Converts enumerables to array. - * - * @api public - */ - - util.toArray = function (enu) { - var arr = []; - - for (var i = 0, l = enu.length; i < l; i++) - arr.push(enu[i]); - - return arr; - }; - - /** - * UA / engines detection namespace. - * - * @namespace - */ - - util.ua = {}; - - /** - * Whether the UA supports CORS for XHR. - * - * @api public - */ - - util.ua.hasCORS = 'undefined' != typeof XMLHttpRequest && (function () { - try { - var a = new XMLHttpRequest(); - } catch (e) { - return false; - } - - return a.withCredentials != undefined; - })(); - - /** - * Detect webkit. - * - * @api public - */ - - util.ua.webkit = 'undefined' != typeof navigator - && /webkit/i.test(navigator.userAgent); - - /** - * Detect iPad/iPhone/iPod. - * - * @api public - */ - - util.ua.iDevice = 'undefined' != typeof navigator - && /iPad|iPhone|iPod/i.test(navigator.userAgent); - -})('undefined' != typeof io ? io : module.exports, this); -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io) { - - /** - * Expose constructor. - */ - - exports.EventEmitter = EventEmitter; - - /** - * Event emitter constructor. - * - * @api public. - */ - - function EventEmitter () {}; - - /** - * Adds a listener - * - * @api public - */ - - EventEmitter.prototype.on = function (name, fn) { - if (!this.$events) { - this.$events = {}; - } - - if (!this.$events[name]) { - this.$events[name] = fn; - } else if (io.util.isArray(this.$events[name])) { - this.$events[name].push(fn); - } else { - this.$events[name] = [this.$events[name], fn]; - } - - return this; - }; - - EventEmitter.prototype.addListener = EventEmitter.prototype.on; - - /** - * Adds a volatile listener. - * - * @api public - */ - - EventEmitter.prototype.once = function (name, fn) { - var self = this; - - function on () { - self.removeListener(name, on); - fn.apply(this, arguments); - }; - - on.listener = fn; - this.on(name, on); - - return this; - }; - - /** - * Removes a listener. - * - * @api public - */ - - EventEmitter.prototype.removeListener = function (name, fn) { - if (this.$events && this.$events[name]) { - var list = this.$events[name]; - - if (io.util.isArray(list)) { - var pos = -1; - - for (var i = 0, l = list.length; i < l; i++) { - if (list[i] === fn || (list[i].listener && list[i].listener === fn)) { - pos = i; - break; - } - } - - if (pos < 0) { - return this; - } - - list.splice(pos, 1); - - if (!list.length) { - delete this.$events[name]; - } - } else if (list === fn || (list.listener && list.listener === fn)) { - delete this.$events[name]; - } - } - - return this; - }; - - /** - * Removes all listeners for an event. - * - * @api public - */ - - EventEmitter.prototype.removeAllListeners = function (name) { - if (name === undefined) { - this.$events = {}; - return this; - } - - if (this.$events && this.$events[name]) { - this.$events[name] = null; - } - - return this; - }; - - /** - * Gets all listeners for a certain event. - * - * @api publci - */ - - EventEmitter.prototype.listeners = function (name) { - if (!this.$events) { - this.$events = {}; - } - - if (!this.$events[name]) { - this.$events[name] = []; - } - - if (!io.util.isArray(this.$events[name])) { - this.$events[name] = [this.$events[name]]; - } - - return this.$events[name]; - }; - - /** - * Emits an event. - * - * @api public - */ - - EventEmitter.prototype.emit = function (name) { - if (!this.$events) { - return false; - } - - var handler = this.$events[name]; - - if (!handler) { - return false; - } - - var args = Array.prototype.slice.call(arguments, 1); - - if ('function' == typeof handler) { - handler.apply(this, args); - } else if (io.util.isArray(handler)) { - var listeners = handler.slice(); - - for (var i = 0, l = listeners.length; i < l; i++) { - listeners[i].apply(this, args); - } - } else { - return false; - } - - return true; - }; - -})( - 'undefined' != typeof io ? io : module.exports - , 'undefined' != typeof io ? io : module.parent.exports -); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Based on JSON2 (http://www.JSON.org/js.html). - */ - -(function (exports, nativeJSON) { - "use strict"; - - // use native JSON if it's available - if (nativeJSON && nativeJSON.parse){ - return exports.JSON = { - parse: nativeJSON.parse - , stringify: nativeJSON.stringify - }; - } - - var JSON = exports.JSON = {}; - - function f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; - } - - function date(d, key) { - return isFinite(d.valueOf()) ? - d.getUTCFullYear() + '-' + - f(d.getUTCMonth() + 1) + '-' + - f(d.getUTCDate()) + 'T' + - f(d.getUTCHours()) + ':' + - f(d.getUTCMinutes()) + ':' + - f(d.getUTCSeconds()) + 'Z' : null; - }; - - var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - gap, - indent, - meta = { // table of character substitutions - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '"' : '\\"', - '\\': '\\\\' - }, - rep; - - - function quote(string) { - -// If the string contains no control characters, no quote characters, and no -// backslash characters, then we can safely slap some quotes around it. -// Otherwise we must also replace the offending characters with safe escape -// sequences. - - escapable.lastIndex = 0; - return escapable.test(string) ? '"' + string.replace(escapable, function (a) { - var c = meta[a]; - return typeof c === 'string' ? c : - '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }) + '"' : '"' + string + '"'; - } - - - function str(key, holder) { - -// Produce a string from holder[key]. - - var i, // The loop counter. - k, // The member key. - v, // The member value. - length, - mind = gap, - partial, - value = holder[key]; - -// If the value has a toJSON method, call it to obtain a replacement value. - - if (value instanceof Date) { - value = date(key); - } - -// If we were called with a replacer function, then call the replacer to -// obtain a replacement value. - - if (typeof rep === 'function') { - value = rep.call(holder, key, value); - } - -// What happens next depends on the value's type. - - switch (typeof value) { - case 'string': - return quote(value); - - case 'number': - -// JSON numbers must be finite. Encode non-finite numbers as null. - - return isFinite(value) ? String(value) : 'null'; - - case 'boolean': - case 'null': - -// If the value is a boolean or null, convert it to a string. Note: -// typeof null does not produce 'null'. The case is included here in -// the remote chance that this gets fixed someday. - - return String(value); - -// If the type is 'object', we might be dealing with an object or an array or -// null. - - case 'object': - -// Due to a specification blunder in ECMAScript, typeof null is 'object', -// so watch out for that case. - - if (!value) { - return 'null'; - } - -// Make an array to hold the partial results of stringifying this object value. - - gap += indent; - partial = []; - -// Is the value an array? - - if (Object.prototype.toString.apply(value) === '[object Array]') { - -// The value is an array. Stringify every element. Use null as a placeholder -// for non-JSON values. - - length = value.length; - for (i = 0; i < length; i += 1) { - partial[i] = str(i, value) || 'null'; - } - -// Join all of the elements together, separated with commas, and wrap them in -// brackets. - - v = partial.length === 0 ? '[]' : gap ? - '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : - '[' + partial.join(',') + ']'; - gap = mind; - return v; - } - -// If the replacer is an array, use it to select the members to be stringified. - - if (rep && typeof rep === 'object') { - length = rep.length; - for (i = 0; i < length; i += 1) { - if (typeof rep[i] === 'string') { - k = rep[i]; - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } else { - -// Otherwise, iterate through all of the keys in the object. - - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } - -// Join all of the member texts together, separated with commas, -// and wrap them in braces. - - v = partial.length === 0 ? '{}' : gap ? - '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : - '{' + partial.join(',') + '}'; - gap = mind; - return v; - } - } - -// If the JSON object does not yet have a stringify method, give it one. - - JSON.stringify = function (value, replacer, space) { - -// The stringify method takes a value and an optional replacer, and an optional -// space parameter, and returns a JSON text. The replacer can be a function -// that can replace values, or an array of strings that will select the keys. -// A default replacer method can be provided. Use of the space parameter can -// produce text that is more easily readable. - - var i; - gap = ''; - indent = ''; - -// If the space parameter is a number, make an indent string containing that -// many spaces. - - if (typeof space === 'number') { - for (i = 0; i < space; i += 1) { - indent += ' '; - } - -// If the space parameter is a string, it will be used as the indent string. - - } else if (typeof space === 'string') { - indent = space; - } - -// If there is a replacer, it must be a function or an array. -// Otherwise, throw an error. - - rep = replacer; - if (replacer && typeof replacer !== 'function' && - (typeof replacer !== 'object' || - typeof replacer.length !== 'number')) { - throw new Error('JSON.stringify'); - } - -// Make a fake root object containing our value under the key of ''. -// Return the result of stringifying the value. - - return str('', {'': value}); - }; - -// If the JSON object does not yet have a parse method, give it one. - - JSON.parse = function (text, reviver) { - // The parse method takes a text and an optional reviver function, and returns - // a JavaScript value if the text is a valid JSON text. - - var j; - - function walk(holder, key) { - - // The walk method is used to recursively walk the resulting structure so - // that modifications can be made. - - var k, v, value = holder[key]; - if (value && typeof value === 'object') { - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = walk(value, k); - if (v !== undefined) { - value[k] = v; - } else { - delete value[k]; - } - } - } - } - return reviver.call(holder, key, value); - } - - - // Parsing happens in four stages. In the first stage, we replace certain - // Unicode characters with escape sequences. JavaScript handles many characters - // incorrectly, either silently deleting them, or treating them as line endings. - - text = String(text); - cx.lastIndex = 0; - if (cx.test(text)) { - text = text.replace(cx, function (a) { - return '\\u' + - ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }); - } - - // In the second stage, we run the text against regular expressions that look - // for non-JSON patterns. We are especially concerned with '()' and 'new' - // because they can cause invocation, and '=' because it can cause mutation. - // But just to be safe, we want to reject all unexpected forms. - - // We split the second stage into 4 regexp operations in order to work around - // crippling inefficiencies in IE's and Safari's regexp engines. First we - // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we - // replace all simple value tokens with ']' characters. Third, we delete all - // open brackets that follow a colon or comma or that begin the text. Finally, - // we look to see that the remaining characters are only whitespace or ']' or - // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. - - if (/^[\],:{}\s]*$/ - .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') - .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') - .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { - - // In the third stage we use the eval function to compile the text into a - // JavaScript structure. The '{' operator is subject to a syntactic ambiguity - // in JavaScript: it can begin a block or an object literal. We wrap the text - // in parens to eliminate the ambiguity. - - j = eval('(' + text + ')'); - - // In the optional fourth stage, we recursively walk the new structure, passing - // each name/value pair to a reviver function for possible transformation. - - return typeof reviver === 'function' ? - walk({'': j}, '') : j; - } - - // If the text is not JSON parseable, then a SyntaxError is thrown. - - throw new SyntaxError('JSON.parse'); - }; - -})( - 'undefined' != typeof io ? io : module.exports - , typeof JSON !== 'undefined' ? JSON : undefined -); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io) { - - /** - * Parser namespace. - * - * @namespace - */ - - var parser = exports.parser = {}; - - /** - * Packet types. - */ - - var packets = parser.packets = [ - 'disconnect' - , 'connect' - , 'heartbeat' - , 'message' - , 'json' - , 'event' - , 'ack' - , 'error' - , 'noop' - ]; - - /** - * Errors reasons. - */ - - var reasons = parser.reasons = [ - 'transport not supported' - , 'client not handshaken' - , 'unauthorized' - ]; - - /** - * Errors advice. - */ - - var advice = parser.advice = [ - 'reconnect' - ]; - - /** - * Shortcuts. - */ - - var JSON = io.JSON - , indexOf = io.util.indexOf; - - /** - * Encodes a packet. - * - * @api private - */ - - parser.encodePacket = function (packet) { - var type = indexOf(packets, packet.type) - , id = packet.id || '' - , endpoint = packet.endpoint || '' - , ack = packet.ack - , data = null; - - switch (packet.type) { - case 'error': - var reason = packet.reason ? indexOf(reasons, packet.reason) : '' - , adv = packet.advice ? indexOf(advice, packet.advice) : ''; - - if (reason !== '' || adv !== '') - data = reason + (adv !== '' ? ('+' + adv) : ''); - - break; - - case 'message': - if (packet.data !== '') - data = packet.data; - break; - - case 'event': - var ev = { name: packet.name }; - - if (packet.args && packet.args.length) { - ev.args = packet.args; - } - - data = JSON.stringify(ev); - break; - - case 'json': - data = JSON.stringify(packet.data); - break; - - case 'connect': - if (packet.qs) - data = packet.qs; - break; - - case 'ack': - data = packet.ackId - + (packet.args && packet.args.length - ? '+' + JSON.stringify(packet.args) : ''); - break; - } - - // construct packet with required fragments - var encoded = [ - type - , id + (ack == 'data' ? '+' : '') - , endpoint - ]; - - // data fragment is optional - if (data !== null && data !== undefined) - encoded.push(data); - - return encoded.join(':'); - }; - - /** - * Encodes multiple messages (payload). - * - * @param {Array} messages - * @api private - */ - - parser.encodePayload = function (packets) { - var decoded = ''; - - if (packets.length == 1) - return packets[0]; - - for (var i = 0, l = packets.length; i < l; i++) { - var packet = packets[i]; - decoded += '\ufffd' + packet.length + '\ufffd' + packets[i]; - } - - return decoded; - }; - - /** - * Decodes a packet - * - * @api private - */ - - var regexp = /([^:]+):([0-9]+)?(\+)?:([^:]+)?:?([\s\S]*)?/; - - parser.decodePacket = function (data) { - var pieces = data.match(regexp); - - if (!pieces) return {}; - - var id = pieces[2] || '' - , data = pieces[5] || '' - , packet = { - type: packets[pieces[1]] - , endpoint: pieces[4] || '' - }; - - // whether we need to acknowledge the packet - if (id) { - packet.id = id; - if (pieces[3]) - packet.ack = 'data'; - else - packet.ack = true; - } - - // handle different packet types - switch (packet.type) { - case 'error': - var pieces = data.split('+'); - packet.reason = reasons[pieces[0]] || ''; - packet.advice = advice[pieces[1]] || ''; - break; - - case 'message': - packet.data = data || ''; - break; - - case 'event': - try { - var opts = JSON.parse(data); - packet.name = opts.name; - packet.args = opts.args; - } catch (e) { } - - packet.args = packet.args || []; - break; - - case 'json': - try { - packet.data = JSON.parse(data); - } catch (e) { } - break; - - case 'connect': - packet.qs = data || ''; - break; - - case 'ack': - var pieces = data.match(/^([0-9]+)(\+)?(.*)/); - if (pieces) { - packet.ackId = pieces[1]; - packet.args = []; - - if (pieces[3]) { - try { - packet.args = pieces[3] ? JSON.parse(pieces[3]) : []; - } catch (e) { } - } - } - break; - - case 'disconnect': - case 'heartbeat': - break; - }; - - return packet; - }; - - /** - * Decodes data payload. Detects multiple messages - * - * @return {Array} messages - * @api public - */ - - parser.decodePayload = function (data) { - // IE doesn't like data[i] for unicode chars, charAt works fine - if (data.charAt(0) == '\ufffd') { - var ret = []; - - for (var i = 1, length = ''; i < data.length; i++) { - if (data.charAt(i) == '\ufffd') { - ret.push(parser.decodePacket(data.substr(i + 1).substr(0, length))); - i += Number(length) + 1; - length = ''; - } else { - length += data.charAt(i); - } - } - - return ret; - } else { - return [parser.decodePacket(data)]; - } - }; - -})( - 'undefined' != typeof io ? io : module.exports - , 'undefined' != typeof io ? io : module.parent.exports -); -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io) { - - /** - * Expose constructor. - */ - - exports.Transport = Transport; - - /** - * This is the transport template for all supported transport methods. - * - * @constructor - * @api public - */ - - function Transport (socket, sessid) { - this.socket = socket; - this.sessid = sessid; - }; - - /** - * Apply EventEmitter mixin. - */ - - io.util.mixin(Transport, io.EventEmitter); - - - /** - * Indicates whether heartbeats is enabled for this transport - * - * @api private - */ - - Transport.prototype.heartbeats = function () { - return true; - }; - - /** - * Handles the response from the server. When a new response is received - * it will automatically update the timeout, decode the message and - * forwards the response to the onMessage function for further processing. - * - * @param {String} data Response from the server. - * @api private - */ - - Transport.prototype.onData = function (data) { - this.clearCloseTimeout(); - - // If the connection in currently open (or in a reopening state) reset the close - // timeout since we have just received data. This check is necessary so - // that we don't reset the timeout on an explicitly disconnected connection. - if (this.socket.connected || this.socket.connecting || this.socket.reconnecting) { - this.setCloseTimeout(); - } - - if (data !== '') { - // todo: we should only do decodePayload for xhr transports - var msgs = io.parser.decodePayload(data); - - if (msgs && msgs.length) { - for (var i = 0, l = msgs.length; i < l; i++) { - this.onPacket(msgs[i]); - } - } - } - - return this; - }; - - /** - * Handles packets. - * - * @api private - */ - - Transport.prototype.onPacket = function (packet) { - this.socket.setHeartbeatTimeout(); - - if (packet.type == 'heartbeat') { - return this.onHeartbeat(); - } - - if (packet.type == 'connect' && packet.endpoint == '') { - this.onConnect(); - } - - if (packet.type == 'error' && packet.advice == 'reconnect') { - this.isOpen = false; - } - - this.socket.onPacket(packet); - - return this; - }; - - /** - * Sets close timeout - * - * @api private - */ - - Transport.prototype.setCloseTimeout = function () { - if (!this.closeTimeout) { - var self = this; - - this.closeTimeout = setTimeout(function () { - self.onDisconnect(); - }, this.socket.closeTimeout); - } - }; - - /** - * Called when transport disconnects. - * - * @api private - */ - - Transport.prototype.onDisconnect = function () { - if (this.isOpen) this.close(); - this.clearTimeouts(); - this.socket.onDisconnect(); - return this; - }; - - /** - * Called when transport connects - * - * @api private - */ - - Transport.prototype.onConnect = function () { - this.socket.onConnect(); - return this; - }; - - /** - * Clears close timeout - * - * @api private - */ - - Transport.prototype.clearCloseTimeout = function () { - if (this.closeTimeout) { - clearTimeout(this.closeTimeout); - this.closeTimeout = null; - } - }; - - /** - * Clear timeouts - * - * @api private - */ - - Transport.prototype.clearTimeouts = function () { - this.clearCloseTimeout(); - - if (this.reopenTimeout) { - clearTimeout(this.reopenTimeout); - } - }; - - /** - * Sends a packet - * - * @param {Object} packet object. - * @api private - */ - - Transport.prototype.packet = function (packet) { - this.send(io.parser.encodePacket(packet)); - }; - - /** - * Send the received heartbeat message back to server. So the server - * knows we are still connected. - * - * @param {String} heartbeat Heartbeat response from the server. - * @api private - */ - - Transport.prototype.onHeartbeat = function (heartbeat) { - this.packet({ type: 'heartbeat' }); - }; - - /** - * Called when the transport opens. - * - * @api private - */ - - Transport.prototype.onOpen = function () { - this.isOpen = true; - this.clearCloseTimeout(); - this.socket.onOpen(); - }; - - /** - * Notifies the base when the connection with the Socket.IO server - * has been disconnected. - * - * @api private - */ - - Transport.prototype.onClose = function () { - var self = this; - - /* FIXME: reopen delay causing a infinit loop - this.reopenTimeout = setTimeout(function () { - self.open(); - }, this.socket.options['reopen delay']);*/ - - this.isOpen = false; - this.socket.onClose(); - this.onDisconnect(); - }; - - /** - * Generates a connection url based on the Socket.IO URL Protocol. - * See for more details. - * - * @returns {String} Connection url - * @api private - */ - - Transport.prototype.prepareUrl = function () { - var options = this.socket.options; - - return this.scheme() + '://' - + options.host + ':' + options.port + '/' - + options.resource + '/' + io.protocol - + '/' + this.name + '/' + this.sessid; - }; - - /** - * Checks if the transport is ready to start a connection. - * - * @param {Socket} socket The socket instance that needs a transport - * @param {Function} fn The callback - * @api private - */ - - Transport.prototype.ready = function (socket, fn) { - fn.call(this); - }; -})( - 'undefined' != typeof io ? io : module.exports - , 'undefined' != typeof io ? io : module.parent.exports -); -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io, global) { - - /** - * Expose constructor. - */ - - exports.Socket = Socket; - - /** - * Create a new `Socket.IO client` which can establish a persistent - * connection with a Socket.IO enabled server. - * - * @api public - */ - - function Socket (options) { - this.options = { - port: 80 - , secure: false - , document: 'document' in global ? document : false - , resource: 'socket.io' - , transports: io.transports - , 'connect timeout': 10000 - , 'try multiple transports': true - , 'reconnect': true - , 'reconnection delay': 500 - , 'reconnection limit': Infinity - , 'reopen delay': 3000 - , 'max reconnection attempts': 10 - , 'sync disconnect on unload': false - , 'auto connect': true - , 'flash policy port': 10843 - , 'manualFlush': false - }; - - io.util.merge(this.options, options); - - this.connected = false; - this.open = false; - this.connecting = false; - this.reconnecting = false; - this.namespaces = {}; - this.buffer = []; - this.doBuffer = false; - - if (this.options['sync disconnect on unload'] && - (!this.isXDomain() || io.util.ua.hasCORS)) { - var self = this; - io.util.on(global, 'beforeunload', function () { - self.disconnectSync(); - }, false); - } - - if (this.options['auto connect']) { - this.connect(); - } -}; - - /** - * Apply EventEmitter mixin. - */ - - io.util.mixin(Socket, io.EventEmitter); - - /** - * Returns a namespace listener/emitter for this socket - * - * @api public - */ - - Socket.prototype.of = function (name) { - if (!this.namespaces[name]) { - this.namespaces[name] = new io.SocketNamespace(this, name); - - if (name !== '') { - this.namespaces[name].packet({ type: 'connect' }); - } - } - - return this.namespaces[name]; - }; - - /** - * Emits the given event to the Socket and all namespaces - * - * @api private - */ - - Socket.prototype.publish = function () { - this.emit.apply(this, arguments); - - var nsp; - - for (var i in this.namespaces) { - if (this.namespaces.hasOwnProperty(i)) { - nsp = this.of(i); - nsp.$emit.apply(nsp, arguments); - } - } - }; - - /** - * Performs the handshake - * - * @api private - */ - - function empty () { }; - - Socket.prototype.handshake = function (fn) { - var self = this - , options = this.options; - - function complete (data) { - if (data instanceof Error) { - self.connecting = false; - self.onError(data.message); - } else { - fn.apply(null, data.split(':')); - } - }; - - var url = [ - 'http' + (options.secure ? 's' : '') + ':/' - , options.host + ':' + options.port - , options.resource - , io.protocol - , io.util.query(this.options.query, 't=' + +new Date) - ].join('/'); - - if (this.isXDomain() && !io.util.ua.hasCORS) { - var insertAt = document.getElementsByTagName('script')[0] - , script = document.createElement('script'); - - script.src = url + '&jsonp=' + io.j.length; - insertAt.parentNode.insertBefore(script, insertAt); - - io.j.push(function (data) { - complete(data); - script.parentNode.removeChild(script); - }); - } else { - var xhr = io.util.request(); - - xhr.open('GET', url, true); - if (this.isXDomain()) { - xhr.withCredentials = true; - } - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - xhr.onreadystatechange = empty; - - if (xhr.status == 200) { - complete(xhr.responseText); - } else if (xhr.status == 403) { - self.onError(xhr.responseText); - } else { - self.connecting = false; - !self.reconnecting && self.onError(xhr.responseText); - } - } - }; - xhr.send(null); - } - }; - - /** - * Find an available transport based on the options supplied in the constructor. - * - * @api private - */ - - Socket.prototype.getTransport = function (override) { - var transports = override || this.transports, match; - - for (var i = 0, transport; transport = transports[i]; i++) { - if (io.Transport[transport] - && io.Transport[transport].check(this) - && (!this.isXDomain() || io.Transport[transport].xdomainCheck(this))) { - return new io.Transport[transport](this, this.sessionid); - } - } - - return null; - }; - - /** - * Connects to the server. - * - * @param {Function} [fn] Callback. - * @returns {io.Socket} - * @api public - */ - - Socket.prototype.connect = function (fn) { - if (this.connecting) { - return this; - } - - var self = this; - self.connecting = true; - - this.handshake(function (sid, heartbeat, close, transports) { - self.sessionid = sid; - self.closeTimeout = close * 1000; - self.heartbeatTimeout = heartbeat * 1000; - if(!self.transports) - self.transports = self.origTransports = (transports ? io.util.intersect( - transports.split(',') - , self.options.transports - ) : self.options.transports); - - self.setHeartbeatTimeout(); - - function connect (transports){ - if (self.transport) self.transport.clearTimeouts(); - - self.transport = self.getTransport(transports); - if (!self.transport) return self.publish('connect_failed'); - - // once the transport is ready - self.transport.ready(self, function () { - self.connecting = true; - self.publish('connecting', self.transport.name); - self.transport.open(); - - if (self.options['connect timeout']) { - self.connectTimeoutTimer = setTimeout(function () { - if (!self.connected) { - self.connecting = false; - - if (self.options['try multiple transports']) { - var remaining = self.transports; - - while (remaining.length > 0 && remaining.splice(0,1)[0] != - self.transport.name) {} - - if (remaining.length){ - connect(remaining); - } else { - self.publish('connect_failed'); - } - } - } - }, self.options['connect timeout']); - } - }); - } - - connect(self.transports); - - self.once('connect', function (){ - clearTimeout(self.connectTimeoutTimer); - - fn && typeof fn == 'function' && fn(); - }); - }); - - return this; - }; - - /** - * Clears and sets a new heartbeat timeout using the value given by the - * server during the handshake. - * - * @api private - */ - - Socket.prototype.setHeartbeatTimeout = function () { - clearTimeout(this.heartbeatTimeoutTimer); - if(this.transport && !this.transport.heartbeats()) return; - - var self = this; - this.heartbeatTimeoutTimer = setTimeout(function () { - self.transport.onClose(); - }, this.heartbeatTimeout); - }; - - /** - * Sends a message. - * - * @param {Object} data packet. - * @returns {io.Socket} - * @api public - */ - - Socket.prototype.packet = function (data) { - if (this.connected && !this.doBuffer) { - this.transport.packet(data); - } else { - this.buffer.push(data); - } - - return this; - }; - - /** - * Sets buffer state - * - * @api private - */ - - Socket.prototype.setBuffer = function (v) { - this.doBuffer = v; - - if (!v && this.connected && this.buffer.length) { - if (!this.options['manualFlush']) { - this.flushBuffer(); - } - } - }; - - /** - * Flushes the buffer data over the wire. - * To be invoked manually when 'manualFlush' is set to true. - * - * @api public - */ - - Socket.prototype.flushBuffer = function() { - this.transport.payload(this.buffer); - this.buffer = []; - }; - - - /** - * Disconnect the established connect. - * - * @returns {io.Socket} - * @api public - */ - - Socket.prototype.disconnect = function () { - if (this.connected || this.connecting) { - if (this.open) { - this.of('').packet({ type: 'disconnect' }); - } - - // handle disconnection immediately - this.onDisconnect('booted'); - } - - return this; - }; - - /** - * Disconnects the socket with a sync XHR. - * - * @api private - */ - - Socket.prototype.disconnectSync = function () { - // ensure disconnection - var xhr = io.util.request(); - var uri = [ - 'http' + (this.options.secure ? 's' : '') + ':/' - , this.options.host + ':' + this.options.port - , this.options.resource - , io.protocol - , '' - , this.sessionid - ].join('/') + '/?disconnect=1'; - - xhr.open('GET', uri, false); - xhr.send(null); - - // handle disconnection immediately - this.onDisconnect('booted'); - }; - - /** - * Check if we need to use cross domain enabled transports. Cross domain would - * be a different port or different domain name. - * - * @returns {Boolean} - * @api private - */ - - Socket.prototype.isXDomain = function () { - - var port = global.location.port || - ('https:' == global.location.protocol ? 443 : 80); - - return this.options.host !== global.location.hostname - || this.options.port != port; - }; - - /** - * Called upon handshake. - * - * @api private - */ - - Socket.prototype.onConnect = function () { - if (!this.connected) { - this.connected = true; - this.connecting = false; - if (!this.doBuffer) { - // make sure to flush the buffer - this.setBuffer(false); - } - this.emit('connect'); - } - }; - - /** - * Called when the transport opens - * - * @api private - */ - - Socket.prototype.onOpen = function () { - this.open = true; - }; - - /** - * Called when the transport closes. - * - * @api private - */ - - Socket.prototype.onClose = function () { - this.open = false; - clearTimeout(this.heartbeatTimeoutTimer); - }; - - /** - * Called when the transport first opens a connection - * - * @param text - */ - - Socket.prototype.onPacket = function (packet) { - this.of(packet.endpoint).onPacket(packet); - }; - - /** - * Handles an error. - * - * @api private - */ - - Socket.prototype.onError = function (err) { - if (err && err.advice) { - if (err.advice === 'reconnect' && (this.connected || this.connecting)) { - this.disconnect(); - if (this.options.reconnect) { - this.reconnect(); - } - } - } - - this.publish('error', err && err.reason ? err.reason : err); - }; - - /** - * Called when the transport disconnects. - * - * @api private - */ - - Socket.prototype.onDisconnect = function (reason) { - var wasConnected = this.connected - , wasConnecting = this.connecting; - - this.connected = false; - this.connecting = false; - this.open = false; - - if (wasConnected || wasConnecting) { - this.transport.close(); - this.transport.clearTimeouts(); - if (wasConnected) { - this.publish('disconnect', reason); - - if ('booted' != reason && this.options.reconnect && !this.reconnecting) { - this.reconnect(); - } - } - } - }; - - /** - * Called upon reconnection. - * - * @api private - */ - - Socket.prototype.reconnect = function () { - this.reconnecting = true; - this.reconnectionAttempts = 0; - this.reconnectionDelay = this.options['reconnection delay']; - - var self = this - , maxAttempts = this.options['max reconnection attempts'] - , tryMultiple = this.options['try multiple transports'] - , limit = this.options['reconnection limit']; - - function reset () { - if (self.connected) { - for (var i in self.namespaces) { - if (self.namespaces.hasOwnProperty(i) && '' !== i) { - self.namespaces[i].packet({ type: 'connect' }); - } - } - self.publish('reconnect', self.transport.name, self.reconnectionAttempts); - } - - clearTimeout(self.reconnectionTimer); - - self.removeListener('connect_failed', maybeReconnect); - self.removeListener('connect', maybeReconnect); - - self.reconnecting = false; - - delete self.reconnectionAttempts; - delete self.reconnectionDelay; - delete self.reconnectionTimer; - delete self.redoTransports; - - self.options['try multiple transports'] = tryMultiple; - }; - - function maybeReconnect () { - if (!self.reconnecting) { - return; - } - - if (self.connected) { - return reset(); - }; - - if (self.connecting && self.reconnecting) { - return self.reconnectionTimer = setTimeout(maybeReconnect, 1000); - } - - if (self.reconnectionAttempts++ >= maxAttempts) { - if (!self.redoTransports) { - self.on('connect_failed', maybeReconnect); - self.options['try multiple transports'] = true; - self.transports = self.origTransports; - self.transport = self.getTransport(); - self.redoTransports = true; - self.connect(); - } else { - self.publish('reconnect_failed'); - reset(); - } - } else { - if (self.reconnectionDelay < limit) { - self.reconnectionDelay *= 2; // exponential back off - } - - self.connect(); - self.publish('reconnecting', self.reconnectionDelay, self.reconnectionAttempts); - self.reconnectionTimer = setTimeout(maybeReconnect, self.reconnectionDelay); - } - }; - - this.options['try multiple transports'] = false; - this.reconnectionTimer = setTimeout(maybeReconnect, this.reconnectionDelay); - - this.on('connect', maybeReconnect); - }; - -})( - 'undefined' != typeof io ? io : module.exports - , 'undefined' != typeof io ? io : module.parent.exports - , this -); -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io) { - - /** - * Expose constructor. - */ - - exports.SocketNamespace = SocketNamespace; - - /** - * Socket namespace constructor. - * - * @constructor - * @api public - */ - - function SocketNamespace (socket, name) { - this.socket = socket; - this.name = name || ''; - this.flags = {}; - this.json = new Flag(this, 'json'); - this.ackPackets = 0; - this.acks = {}; - }; - - /** - * Apply EventEmitter mixin. - */ - - io.util.mixin(SocketNamespace, io.EventEmitter); - - /** - * Copies emit since we override it - * - * @api private - */ - - SocketNamespace.prototype.$emit = io.EventEmitter.prototype.emit; - - /** - * Creates a new namespace, by proxying the request to the socket. This - * allows us to use the synax as we do on the server. - * - * @api public - */ - - SocketNamespace.prototype.of = function () { - return this.socket.of.apply(this.socket, arguments); - }; - - /** - * Sends a packet. - * - * @api private - */ - - SocketNamespace.prototype.packet = function (packet) { - packet.endpoint = this.name; - this.socket.packet(packet); - this.flags = {}; - return this; - }; - - /** - * Sends a message - * - * @api public - */ - - SocketNamespace.prototype.send = function (data, fn) { - var packet = { - type: this.flags.json ? 'json' : 'message' - , data: data - }; - - if ('function' == typeof fn) { - packet.id = ++this.ackPackets; - packet.ack = true; - this.acks[packet.id] = fn; - } - - return this.packet(packet); - }; - - /** - * Emits an event - * - * @api public - */ - - SocketNamespace.prototype.emit = function (name) { - var args = Array.prototype.slice.call(arguments, 1) - , lastArg = args[args.length - 1] - , packet = { - type: 'event' - , name: name - }; - - if ('function' == typeof lastArg) { - packet.id = ++this.ackPackets; - packet.ack = 'data'; - this.acks[packet.id] = lastArg; - args = args.slice(0, args.length - 1); - } - - packet.args = args; - - return this.packet(packet); - }; - - /** - * Disconnects the namespace - * - * @api private - */ - - SocketNamespace.prototype.disconnect = function () { - if (this.name === '') { - this.socket.disconnect(); - } else { - this.packet({ type: 'disconnect' }); - this.$emit('disconnect'); - } - - return this; - }; - - /** - * Handles a packet - * - * @api private - */ - - SocketNamespace.prototype.onPacket = function (packet) { - var self = this; - - function ack () { - self.packet({ - type: 'ack' - , args: io.util.toArray(arguments) - , ackId: packet.id - }); - }; - - switch (packet.type) { - case 'connect': - this.$emit('connect'); - break; - - case 'disconnect': - if (this.name === '') { - this.socket.onDisconnect(packet.reason || 'booted'); - } else { - this.$emit('disconnect', packet.reason); - } - break; - - case 'message': - case 'json': - var params = ['message', packet.data]; - - if (packet.ack == 'data') { - params.push(ack); - } else if (packet.ack) { - this.packet({ type: 'ack', ackId: packet.id }); - } - - this.$emit.apply(this, params); - break; - - case 'event': - var params = [packet.name].concat(packet.args); - - if (packet.ack == 'data') - params.push(ack); - - this.$emit.apply(this, params); - break; - - case 'ack': - if (this.acks[packet.ackId]) { - this.acks[packet.ackId].apply(this, packet.args); - delete this.acks[packet.ackId]; - } - break; - - case 'error': - if (packet.advice){ - this.socket.onError(packet); - } else { - if (packet.reason == 'unauthorized') { - this.$emit('connect_failed', packet.reason); - } else { - this.$emit('error', packet.reason); - } - } - break; - } - }; - - /** - * Flag interface. - * - * @api private - */ - - function Flag (nsp, name) { - this.namespace = nsp; - this.name = name; - }; - - /** - * Send a message - * - * @api public - */ - - Flag.prototype.send = function () { - this.namespace.flags[this.name] = true; - this.namespace.send.apply(this.namespace, arguments); - }; - - /** - * Emit an event - * - * @api public - */ - - Flag.prototype.emit = function () { - this.namespace.flags[this.name] = true; - this.namespace.emit.apply(this.namespace, arguments); - }; - -})( - 'undefined' != typeof io ? io : module.exports - , 'undefined' != typeof io ? io : module.parent.exports -); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io, global) { - - /** - * Expose constructor. - */ - - exports.websocket = WS; - - /** - * The WebSocket transport uses the HTML5 WebSocket API to establish an - * persistent connection with the Socket.IO server. This transport will also - * be inherited by the FlashSocket fallback as it provides a API compatible - * polyfill for the WebSockets. - * - * @constructor - * @extends {io.Transport} - * @api public - */ - - function WS (socket) { - io.Transport.apply(this, arguments); - }; - - /** - * Inherits from Transport. - */ - - io.util.inherit(WS, io.Transport); - - /** - * Transport name - * - * @api public - */ - - WS.prototype.name = 'websocket'; - - /** - * Initializes a new `WebSocket` connection with the Socket.IO server. We attach - * all the appropriate listeners to handle the responses from the server. - * - * @returns {Transport} - * @api public - */ - - WS.prototype.open = function () { - var query = io.util.query(this.socket.options.query) - , self = this - , Socket - - - if (!Socket) { - Socket = global.MozWebSocket || global.WebSocket; - } - - this.websocket = new Socket(this.prepareUrl() + query); - - this.websocket.onopen = function () { - self.onOpen(); - self.socket.setBuffer(false); - }; - this.websocket.onmessage = function (ev) { - self.onData(ev.data); - }; - this.websocket.onclose = function () { - self.onClose(); - self.socket.setBuffer(true); - }; - this.websocket.onerror = function (e) { - self.onError(e); - }; - - return this; - }; - - /** - * Send a message to the Socket.IO server. The message will automatically be - * encoded in the correct message format. - * - * @returns {Transport} - * @api public - */ - - // Do to a bug in the current IDevices browser, we need to wrap the send in a - // setTimeout, when they resume from sleeping the browser will crash if - // we don't allow the browser time to detect the socket has been closed - if (io.util.ua.iDevice) { - WS.prototype.send = function (data) { - var self = this; - setTimeout(function() { - self.websocket.send(data); - },0); - return this; - }; - } else { - WS.prototype.send = function (data) { - this.websocket.send(data); - return this; - }; - } - - /** - * Payload - * - * @api private - */ - - WS.prototype.payload = function (arr) { - for (var i = 0, l = arr.length; i < l; i++) { - this.packet(arr[i]); - } - return this; - }; - - /** - * Disconnect the established `WebSocket` connection. - * - * @returns {Transport} - * @api public - */ - - WS.prototype.close = function () { - this.websocket.close(); - return this; - }; - - /** - * Handle the errors that `WebSocket` might be giving when we - * are attempting to connect or send messages. - * - * @param {Error} e The error. - * @api private - */ - - WS.prototype.onError = function (e) { - this.socket.onError(e); - }; - - /** - * Returns the appropriate scheme for the URI generation. - * - * @api private - */ - WS.prototype.scheme = function () { - return this.socket.options.secure ? 'wss' : 'ws'; - }; - - /** - * Checks if the browser has support for native `WebSockets` and that - * it's not the polyfill created for the FlashSocket transport. - * - * @return {Boolean} - * @api public - */ - - WS.check = function () { - return ('WebSocket' in global && !('__addTask' in WebSocket)) - || 'MozWebSocket' in global; - }; - - /** - * Check if the `WebSocket` transport support cross domain communications. - * - * @returns {Boolean} - * @api public - */ - - WS.xdomainCheck = function () { - return true; - }; - - /** - * Add the transport to your public io.transports array. - * - * @api private - */ - - io.transports.push('websocket'); - -})( - 'undefined' != typeof io ? io.Transport : module.exports - , 'undefined' != typeof io ? io : module.parent.exports - , this -); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io) { - - /** - * Expose constructor. - */ - - exports.flashsocket = Flashsocket; - - /** - * The FlashSocket transport. This is a API wrapper for the HTML5 WebSocket - * specification. It uses a .swf file to communicate with the server. If you want - * to serve the .swf file from a other server than where the Socket.IO script is - * coming from you need to use the insecure version of the .swf. More information - * about this can be found on the github page. - * - * @constructor - * @extends {io.Transport.websocket} - * @api public - */ - - function Flashsocket () { - io.Transport.websocket.apply(this, arguments); - }; - - /** - * Inherits from Transport. - */ - - io.util.inherit(Flashsocket, io.Transport.websocket); - - /** - * Transport name - * - * @api public - */ - - Flashsocket.prototype.name = 'flashsocket'; - - /** - * Disconnect the established `FlashSocket` connection. This is done by adding a - * new task to the FlashSocket. The rest will be handled off by the `WebSocket` - * transport. - * - * @returns {Transport} - * @api public - */ - - Flashsocket.prototype.open = function () { - var self = this - , args = arguments; - - WebSocket.__addTask(function () { - io.Transport.websocket.prototype.open.apply(self, args); - }); - return this; - }; - - /** - * Sends a message to the Socket.IO server. This is done by adding a new - * task to the FlashSocket. The rest will be handled off by the `WebSocket` - * transport. - * - * @returns {Transport} - * @api public - */ - - Flashsocket.prototype.send = function () { - var self = this, args = arguments; - WebSocket.__addTask(function () { - io.Transport.websocket.prototype.send.apply(self, args); - }); - return this; - }; - - /** - * Disconnects the established `FlashSocket` connection. - * - * @returns {Transport} - * @api public - */ - - Flashsocket.prototype.close = function () { - WebSocket.__tasks.length = 0; - io.Transport.websocket.prototype.close.call(this); - return this; - }; - - /** - * The WebSocket fall back needs to append the flash container to the body - * element, so we need to make sure we have access to it. Or defer the call - * until we are sure there is a body element. - * - * @param {Socket} socket The socket instance that needs a transport - * @param {Function} fn The callback - * @api private - */ - - Flashsocket.prototype.ready = function (socket, fn) { - function init () { - var options = socket.options - , port = options['flash policy port'] - , path = [ - 'http' + (options.secure ? 's' : '') + ':/' - , options.host + ':' + options.port - , options.resource - , 'static/flashsocket' - , 'WebSocketMain' + (socket.isXDomain() ? 'Insecure' : '') + '.swf' - ]; - - // Only start downloading the swf file when the checked that this browser - // actually supports it - if (!Flashsocket.loaded) { - if (typeof WEB_SOCKET_SWF_LOCATION === 'undefined') { - // Set the correct file based on the XDomain settings - WEB_SOCKET_SWF_LOCATION = path.join('/'); - } - - if (port !== 843) { - WebSocket.loadFlashPolicyFile('xmlsocket://' + options.host + ':' + port); - } - - WebSocket.__initialize(); - Flashsocket.loaded = true; - } - - fn.call(self); - } - - var self = this; - if (document.body) return init(); - - io.util.load(init); - }; - - /** - * Check if the FlashSocket transport is supported as it requires that the Adobe - * Flash Player plug-in version `10.0.0` or greater is installed. And also check if - * the polyfill is correctly loaded. - * - * @returns {Boolean} - * @api public - */ - - Flashsocket.check = function () { - if ( - typeof WebSocket == 'undefined' - || !('__initialize' in WebSocket) || !swfobject - ) return false; - - return swfobject.getFlashPlayerVersion().major >= 10; - }; - - /** - * Check if the FlashSocket transport can be used as cross domain / cross origin - * transport. Because we can't see which type (secure or insecure) of .swf is used - * we will just return true. - * - * @returns {Boolean} - * @api public - */ - - Flashsocket.xdomainCheck = function () { - return true; - }; - - /** - * Disable AUTO_INITIALIZATION - */ - - if (typeof window != 'undefined') { - WEB_SOCKET_DISABLE_AUTO_INITIALIZATION = true; - } - - /** - * Add the transport to your public io.transports array. - * - * @api private - */ - - io.transports.push('flashsocket'); -})( - 'undefined' != typeof io ? io.Transport : module.exports - , 'undefined' != typeof io ? io : module.parent.exports -); -/* SWFObject v2.2 - is released under the MIT License -*/ -if ('undefined' != typeof window) { -var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O[(['Active'].concat('Object').join('X'))]!=D){try{var ad=new window[(['Active'].concat('Object').join('X'))](W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab -// License: New BSD License -// Reference: http://dev.w3.org/html5/websockets/ -// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol - -(function() { - - if ('undefined' == typeof window || window.WebSocket) return; - - var console = window.console; - if (!console || !console.log || !console.error) { - console = {log: function(){ }, error: function(){ }}; - } - - if (!swfobject.hasFlashPlayerVersion("10.0.0")) { - console.error("Flash Player >= 10.0.0 is required."); - return; - } - if (location.protocol == "file:") { - console.error( - "WARNING: web-socket-js doesn't work in file:///... URL " + - "unless you set Flash Security Settings properly. " + - "Open the page via Web server i.e. http://..."); - } - - /** - * This class represents a faux web socket. - * @param {string} url - * @param {array or string} protocols - * @param {string} proxyHost - * @param {int} proxyPort - * @param {string} headers - */ - WebSocket = function(url, protocols, proxyHost, proxyPort, headers) { - var self = this; - self.__id = WebSocket.__nextId++; - WebSocket.__instances[self.__id] = self; - self.readyState = WebSocket.CONNECTING; - self.bufferedAmount = 0; - self.__events = {}; - if (!protocols) { - protocols = []; - } else if (typeof protocols == "string") { - protocols = [protocols]; - } - // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc. - // Otherwise, when onopen fires immediately, onopen is called before it is set. - setTimeout(function() { - WebSocket.__addTask(function() { - WebSocket.__flash.create( - self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null); - }); - }, 0); - }; - - /** - * Send data to the web socket. - * @param {string} data The data to send to the socket. - * @return {boolean} True for success, false for failure. - */ - WebSocket.prototype.send = function(data) { - if (this.readyState == WebSocket.CONNECTING) { - throw "INVALID_STATE_ERR: Web Socket connection has not been established"; - } - // We use encodeURIComponent() here, because FABridge doesn't work if - // the argument includes some characters. We don't use escape() here - // because of this: - // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions - // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't - // preserve all Unicode characters either e.g. "\uffff" in Firefox. - // Note by wtritch: Hopefully this will not be necessary using ExternalInterface. Will require - // additional testing. - var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data)); - if (result < 0) { // success - return true; - } else { - this.bufferedAmount += result; - return false; - } - }; - - /** - * Close this web socket gracefully. - */ - WebSocket.prototype.close = function() { - if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) { - return; - } - this.readyState = WebSocket.CLOSING; - WebSocket.__flash.close(this.__id); - }; - - /** - * Implementation of {@link DOM 2 EventTarget Interface} - * - * @param {string} type - * @param {function} listener - * @param {boolean} useCapture - * @return void - */ - WebSocket.prototype.addEventListener = function(type, listener, useCapture) { - if (!(type in this.__events)) { - this.__events[type] = []; - } - this.__events[type].push(listener); - }; - - /** - * Implementation of {@link DOM 2 EventTarget Interface} - * - * @param {string} type - * @param {function} listener - * @param {boolean} useCapture - * @return void - */ - WebSocket.prototype.removeEventListener = function(type, listener, useCapture) { - if (!(type in this.__events)) return; - var events = this.__events[type]; - for (var i = events.length - 1; i >= 0; --i) { - if (events[i] === listener) { - events.splice(i, 1); - break; - } - } - }; - - /** - * Implementation of {@link DOM 2 EventTarget Interface} - * - * @param {Event} event - * @return void - */ - WebSocket.prototype.dispatchEvent = function(event) { - var events = this.__events[event.type] || []; - for (var i = 0; i < events.length; ++i) { - events[i](event); - } - var handler = this["on" + event.type]; - if (handler) handler(event); - }; - - /** - * Handles an event from Flash. - * @param {Object} flashEvent - */ - WebSocket.prototype.__handleEvent = function(flashEvent) { - if ("readyState" in flashEvent) { - this.readyState = flashEvent.readyState; - } - if ("protocol" in flashEvent) { - this.protocol = flashEvent.protocol; - } - - var jsEvent; - if (flashEvent.type == "open" || flashEvent.type == "error") { - jsEvent = this.__createSimpleEvent(flashEvent.type); - } else if (flashEvent.type == "close") { - // TODO implement jsEvent.wasClean - jsEvent = this.__createSimpleEvent("close"); - } else if (flashEvent.type == "message") { - var data = decodeURIComponent(flashEvent.message); - jsEvent = this.__createMessageEvent("message", data); - } else { - throw "unknown event type: " + flashEvent.type; - } - - this.dispatchEvent(jsEvent); - }; - - WebSocket.prototype.__createSimpleEvent = function(type) { - if (document.createEvent && window.Event) { - var event = document.createEvent("Event"); - event.initEvent(type, false, false); - return event; - } else { - return {type: type, bubbles: false, cancelable: false}; - } - }; - - WebSocket.prototype.__createMessageEvent = function(type, data) { - if (document.createEvent && window.MessageEvent && !window.opera) { - var event = document.createEvent("MessageEvent"); - event.initMessageEvent("message", false, false, data, null, null, window, null); - return event; - } else { - // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes. - return {type: type, data: data, bubbles: false, cancelable: false}; - } - }; - - /** - * Define the WebSocket readyState enumeration. - */ - WebSocket.CONNECTING = 0; - WebSocket.OPEN = 1; - WebSocket.CLOSING = 2; - WebSocket.CLOSED = 3; - - WebSocket.__flash = null; - WebSocket.__instances = {}; - WebSocket.__tasks = []; - WebSocket.__nextId = 0; - - /** - * Load a new flash security policy file. - * @param {string} url - */ - WebSocket.loadFlashPolicyFile = function(url){ - WebSocket.__addTask(function() { - WebSocket.__flash.loadManualPolicyFile(url); - }); - }; - - /** - * Loads WebSocketMain.swf and creates WebSocketMain object in Flash. - */ - WebSocket.__initialize = function() { - if (WebSocket.__flash) return; - - if (WebSocket.__swfLocation) { - // For backword compatibility. - window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation; - } - if (!window.WEB_SOCKET_SWF_LOCATION) { - console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf"); - return; - } - var container = document.createElement("div"); - container.id = "webSocketContainer"; - // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents - // Flash from loading at least in IE. So we move it out of the screen at (-100, -100). - // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash - // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is - // the best we can do as far as we know now. - container.style.position = "absolute"; - if (WebSocket.__isFlashLite()) { - container.style.left = "0px"; - container.style.top = "0px"; - } else { - container.style.left = "-100px"; - container.style.top = "-100px"; - } - var holder = document.createElement("div"); - holder.id = "webSocketFlash"; - container.appendChild(holder); - document.body.appendChild(container); - // See this article for hasPriority: - // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html - swfobject.embedSWF( - WEB_SOCKET_SWF_LOCATION, - "webSocketFlash", - "1" /* width */, - "1" /* height */, - "10.0.0" /* SWF version */, - null, - null, - {hasPriority: true, swliveconnect : true, allowScriptAccess: "always"}, - null, - function(e) { - if (!e.success) { - console.error("[WebSocket] swfobject.embedSWF failed"); - } - }); - }; - - /** - * Called by Flash to notify JS that it's fully loaded and ready - * for communication. - */ - WebSocket.__onFlashInitialized = function() { - // We need to set a timeout here to avoid round-trip calls - // to flash during the initialization process. - setTimeout(function() { - WebSocket.__flash = document.getElementById("webSocketFlash"); - WebSocket.__flash.setCallerUrl(location.href); - WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG); - for (var i = 0; i < WebSocket.__tasks.length; ++i) { - WebSocket.__tasks[i](); - } - WebSocket.__tasks = []; - }, 0); - }; - - /** - * Called by Flash to notify WebSockets events are fired. - */ - WebSocket.__onFlashEvent = function() { - setTimeout(function() { - try { - // Gets events using receiveEvents() instead of getting it from event object - // of Flash event. This is to make sure to keep message order. - // It seems sometimes Flash events don't arrive in the same order as they are sent. - var events = WebSocket.__flash.receiveEvents(); - for (var i = 0; i < events.length; ++i) { - WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]); - } - } catch (e) { - console.error(e); - } - }, 0); - return true; - }; - - // Called by Flash. - WebSocket.__log = function(message) { - console.log(decodeURIComponent(message)); - }; - - // Called by Flash. - WebSocket.__error = function(message) { - console.error(decodeURIComponent(message)); - }; - - WebSocket.__addTask = function(task) { - if (WebSocket.__flash) { - task(); - } else { - WebSocket.__tasks.push(task); - } - }; - - /** - * Test if the browser is running flash lite. - * @return {boolean} True if flash lite is running, false otherwise. - */ - WebSocket.__isFlashLite = function() { - if (!window.navigator || !window.navigator.mimeTypes) { - return false; - } - var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"]; - if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) { - return false; - } - return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false; - }; - - if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) { - if (window.addEventListener) { - window.addEventListener("load", function(){ - WebSocket.__initialize(); - }, false); - } else { - window.attachEvent("onload", function(){ - WebSocket.__initialize(); - }); - } - } - -})(); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io, global) { - - /** - * Expose constructor. - * - * @api public - */ - - exports.XHR = XHR; - - /** - * XHR constructor - * - * @costructor - * @api public - */ - - function XHR (socket) { - if (!socket) return; - - io.Transport.apply(this, arguments); - this.sendBuffer = []; - }; - - /** - * Inherits from Transport. - */ - - io.util.inherit(XHR, io.Transport); - - /** - * Establish a connection - * - * @returns {Transport} - * @api public - */ - - XHR.prototype.open = function () { - this.socket.setBuffer(false); - this.onOpen(); - this.get(); - - // we need to make sure the request succeeds since we have no indication - // whether the request opened or not until it succeeded. - this.setCloseTimeout(); - - return this; - }; - - /** - * Check if we need to send data to the Socket.IO server, if we have data in our - * buffer we encode it and forward it to the `post` method. - * - * @api private - */ - - XHR.prototype.payload = function (payload) { - var msgs = []; - - for (var i = 0, l = payload.length; i < l; i++) { - msgs.push(io.parser.encodePacket(payload[i])); - } - - this.send(io.parser.encodePayload(msgs)); - }; - - /** - * Send data to the Socket.IO server. - * - * @param data The message - * @returns {Transport} - * @api public - */ - - XHR.prototype.send = function (data) { - this.post(data); - return this; - }; - - /** - * Posts a encoded message to the Socket.IO server. - * - * @param {String} data A encoded message. - * @api private - */ - - function empty () { }; - - XHR.prototype.post = function (data) { - var self = this; - this.socket.setBuffer(true); - - function stateChange () { - if (this.readyState == 4) { - this.onreadystatechange = empty; - self.posting = false; - - if (this.status == 200){ - self.socket.setBuffer(false); - } else { - self.onClose(); - } - } - } - - function onload () { - this.onload = empty; - self.socket.setBuffer(false); - }; - - this.sendXHR = this.request('POST'); - - if (global.XDomainRequest && this.sendXHR instanceof XDomainRequest) { - this.sendXHR.onload = this.sendXHR.onerror = onload; - } else { - this.sendXHR.onreadystatechange = stateChange; - } - - this.sendXHR.send(data); - }; - - /** - * Disconnects the established `XHR` connection. - * - * @returns {Transport} - * @api public - */ - - XHR.prototype.close = function () { - this.onClose(); - return this; - }; - - /** - * Generates a configured XHR request - * - * @param {String} url The url that needs to be requested. - * @param {String} method The method the request should use. - * @returns {XMLHttpRequest} - * @api private - */ - - XHR.prototype.request = function (method) { - var req = io.util.request(this.socket.isXDomain()) - , query = io.util.query(this.socket.options.query, 't=' + +new Date); - - req.open(method || 'GET', this.prepareUrl() + query, true); - - if (method == 'POST') { - try { - if (req.setRequestHeader) { - req.setRequestHeader('Content-type', 'text/plain;charset=UTF-8'); - } else { - // XDomainRequest - req.contentType = 'text/plain'; - } - } catch (e) {} - } - - return req; - }; - - /** - * Returns the scheme to use for the transport URLs. - * - * @api private - */ - - XHR.prototype.scheme = function () { - return this.socket.options.secure ? 'https' : 'http'; - }; - - /** - * Check if the XHR transports are supported - * - * @param {Boolean} xdomain Check if we support cross domain requests. - * @returns {Boolean} - * @api public - */ - - XHR.check = function (socket, xdomain) { - try { - var request = io.util.request(xdomain), - usesXDomReq = (global.XDomainRequest && request instanceof XDomainRequest), - socketProtocol = (socket && socket.options && socket.options.secure ? 'https:' : 'http:'), - isXProtocol = (global.location && socketProtocol != global.location.protocol); - if (request && !(usesXDomReq && isXProtocol)) { - return true; - } - } catch(e) {} - - return false; - }; - - /** - * Check if the XHR transport supports cross domain requests. - * - * @returns {Boolean} - * @api public - */ - - XHR.xdomainCheck = function (socket) { - return XHR.check(socket, true); - }; - -})( - 'undefined' != typeof io ? io.Transport : module.exports - , 'undefined' != typeof io ? io : module.parent.exports - , this -); -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io) { - - /** - * Expose constructor. - */ - - exports.htmlfile = HTMLFile; - - /** - * The HTMLFile transport creates a `forever iframe` based transport - * for Internet Explorer. Regular forever iframe implementations will - * continuously trigger the browsers buzy indicators. If the forever iframe - * is created inside a `htmlfile` these indicators will not be trigged. - * - * @constructor - * @extends {io.Transport.XHR} - * @api public - */ - - function HTMLFile (socket) { - io.Transport.XHR.apply(this, arguments); - }; - - /** - * Inherits from XHR transport. - */ - - io.util.inherit(HTMLFile, io.Transport.XHR); - - /** - * Transport name - * - * @api public - */ - - HTMLFile.prototype.name = 'htmlfile'; - - /** - * Creates a new Ac...eX `htmlfile` with a forever loading iframe - * that can be used to listen to messages. Inside the generated - * `htmlfile` a reference will be made to the HTMLFile transport. - * - * @api private - */ - - HTMLFile.prototype.get = function () { - this.doc = new window[(['Active'].concat('Object').join('X'))]('htmlfile'); - this.doc.open(); - this.doc.write(''); - this.doc.close(); - this.doc.parentWindow.s = this; - - var iframeC = this.doc.createElement('div'); - iframeC.className = 'socketio'; - - this.doc.body.appendChild(iframeC); - this.iframe = this.doc.createElement('iframe'); - - iframeC.appendChild(this.iframe); - - var self = this - , query = io.util.query(this.socket.options.query, 't='+ +new Date); - - this.iframe.src = this.prepareUrl() + query; - - io.util.on(window, 'unload', function () { - self.destroy(); - }); - }; - - /** - * The Socket.IO server will write script tags inside the forever - * iframe, this function will be used as callback for the incoming - * information. - * - * @param {String} data The message - * @param {document} doc Reference to the context - * @api private - */ - - HTMLFile.prototype._ = function (data, doc) { - this.onData(data); - try { - var script = doc.getElementsByTagName('script')[0]; - script.parentNode.removeChild(script); - } catch (e) { } - }; - - /** - * Destroy the established connection, iframe and `htmlfile`. - * And calls the `CollectGarbage` function of Internet Explorer - * to release the memory. - * - * @api private - */ - - HTMLFile.prototype.destroy = function () { - if (this.iframe){ - try { - this.iframe.src = 'about:blank'; - } catch(e){} - - this.doc = null; - this.iframe.parentNode.removeChild(this.iframe); - this.iframe = null; - - CollectGarbage(); - } - }; - - /** - * Disconnects the established connection. - * - * @returns {Transport} Chaining. - * @api public - */ - - HTMLFile.prototype.close = function () { - this.destroy(); - return io.Transport.XHR.prototype.close.call(this); - }; - - /** - * Checks if the browser supports this transport. The browser - * must have an `Ac...eXObject` implementation. - * - * @return {Boolean} - * @api public - */ - - HTMLFile.check = function (socket) { - if (typeof window != "undefined" && (['Active'].concat('Object').join('X')) in window){ - try { - var a = new window[(['Active'].concat('Object').join('X'))]('htmlfile'); - return a && io.Transport.XHR.check(socket); - } catch(e){} - } - return false; - }; - - /** - * Check if cross domain requests are supported. - * - * @returns {Boolean} - * @api public - */ - - HTMLFile.xdomainCheck = function () { - // we can probably do handling for sub-domains, we should - // test that it's cross domain but a subdomain here - return false; - }; - - /** - * Add the transport to your public io.transports array. - * - * @api private - */ - - io.transports.push('htmlfile'); - -})( - 'undefined' != typeof io ? io.Transport : module.exports - , 'undefined' != typeof io ? io : module.parent.exports -); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io, global) { - - /** - * Expose constructor. - */ - - exports['xhr-polling'] = XHRPolling; - - /** - * The XHR-polling transport uses long polling XHR requests to create a - * "persistent" connection with the server. - * - * @constructor - * @api public - */ - - function XHRPolling () { - io.Transport.XHR.apply(this, arguments); - }; - - /** - * Inherits from XHR transport. - */ - - io.util.inherit(XHRPolling, io.Transport.XHR); - - /** - * Merge the properties from XHR transport - */ - - io.util.merge(XHRPolling, io.Transport.XHR); - - /** - * Transport name - * - * @api public - */ - - XHRPolling.prototype.name = 'xhr-polling'; - - /** - * Indicates whether heartbeats is enabled for this transport - * - * @api private - */ - - XHRPolling.prototype.heartbeats = function () { - return false; - }; - - /** - * Establish a connection, for iPhone and Android this will be done once the page - * is loaded. - * - * @returns {Transport} Chaining. - * @api public - */ - - XHRPolling.prototype.open = function () { - var self = this; - - io.Transport.XHR.prototype.open.call(self); - return false; - }; - - /** - * Starts a XHR request to wait for incoming messages. - * - * @api private - */ - - function empty () {}; - - XHRPolling.prototype.get = function () { - if (!this.isOpen) return; - - var self = this; - - function stateChange () { - if (this.readyState == 4) { - this.onreadystatechange = empty; - - if (this.status == 200) { - self.onData(this.responseText); - self.get(); - } else { - self.onClose(); - } - } - }; - - function onload () { - this.onload = empty; - this.onerror = empty; - self.retryCounter = 1; - self.onData(this.responseText); - self.get(); - }; - - function onerror () { - self.retryCounter ++; - if(!self.retryCounter || self.retryCounter > 3) { - self.onClose(); - } else { - self.get(); - } - }; - - this.xhr = this.request(); - - if (global.XDomainRequest && this.xhr instanceof XDomainRequest) { - this.xhr.onload = onload; - this.xhr.onerror = onerror; - } else { - this.xhr.onreadystatechange = stateChange; - } - - this.xhr.send(null); - }; - - /** - * Handle the unclean close behavior. - * - * @api private - */ - - XHRPolling.prototype.onClose = function () { - io.Transport.XHR.prototype.onClose.call(this); - - if (this.xhr) { - this.xhr.onreadystatechange = this.xhr.onload = this.xhr.onerror = empty; - try { - this.xhr.abort(); - } catch(e){} - this.xhr = null; - } - }; - - /** - * Webkit based browsers show a infinit spinner when you start a XHR request - * before the browsers onload event is called so we need to defer opening of - * the transport until the onload event is called. Wrapping the cb in our - * defer method solve this. - * - * @param {Socket} socket The socket instance that needs a transport - * @param {Function} fn The callback - * @api private - */ - - XHRPolling.prototype.ready = function (socket, fn) { - var self = this; - - io.util.defer(function () { - fn.call(self); - }); - }; - - /** - * Add the transport to your public io.transports array. - * - * @api private - */ - - io.transports.push('xhr-polling'); - -})( - 'undefined' != typeof io ? io.Transport : module.exports - , 'undefined' != typeof io ? io : module.parent.exports - , this -); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io, global) { - /** - * There is a way to hide the loading indicator in Firefox. If you create and - * remove a iframe it will stop showing the current loading indicator. - * Unfortunately we can't feature detect that and UA sniffing is evil. - * - * @api private - */ - - var indicator = global.document && "MozAppearance" in - global.document.documentElement.style; - - /** - * Expose constructor. - */ - - exports['jsonp-polling'] = JSONPPolling; - - /** - * The JSONP transport creates an persistent connection by dynamically - * inserting a script tag in the page. This script tag will receive the - * information of the Socket.IO server. When new information is received - * it creates a new script tag for the new data stream. - * - * @constructor - * @extends {io.Transport.xhr-polling} - * @api public - */ - - function JSONPPolling (socket) { - io.Transport['xhr-polling'].apply(this, arguments); - - this.index = io.j.length; - - var self = this; - - io.j.push(function (msg) { - self._(msg); - }); - }; - - /** - * Inherits from XHR polling transport. - */ - - io.util.inherit(JSONPPolling, io.Transport['xhr-polling']); - - /** - * Transport name - * - * @api public - */ - - JSONPPolling.prototype.name = 'jsonp-polling'; - - /** - * Posts a encoded message to the Socket.IO server using an iframe. - * The iframe is used because script tags can create POST based requests. - * The iframe is positioned outside of the view so the user does not - * notice it's existence. - * - * @param {String} data A encoded message. - * @api private - */ - - JSONPPolling.prototype.post = function (data) { - var self = this - , query = io.util.query( - this.socket.options.query - , 't='+ (+new Date) + '&i=' + this.index - ); - - if (!this.form) { - var form = document.createElement('form') - , area = document.createElement('textarea') - , id = this.iframeId = 'socketio_iframe_' + this.index - , iframe; - - form.className = 'socketio'; - form.style.position = 'absolute'; - form.style.top = '0px'; - form.style.left = '0px'; - form.style.display = 'none'; - 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.prepareUrl() + query; - - function complete () { - initIframe(); - self.socket.setBuffer(false); - }; - - function initIframe () { - if (self.iframe) { - self.form.removeChild(self.iframe); - } - - try { - // ie6 dynamic iframes with target="" support (thanks Chris Lambacher) - iframe = document.createElement(' - - - 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 b9c23c8..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.nio.SelectChannelConnector; -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(); - SelectChannelConnector connector = new SelectChannelConnector(); - 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 e4c26d9..97be71f 100644 --- a/samples/chat/pom.xml +++ b/samples/chat/pom.xml @@ -5,10 +5,9 @@ 4.0.0 - com.glines.socketio.sample + com.codeminders.socketio socketio-sample - 0.2.1 - .. + 1.0.10 socketio-sample-chat @@ -19,37 +18,23 @@ - com.glines.socketio - socketio-core - - - com.glines.socketio.extension - socketio-jetty - - - com.google.code.gson - gson - - - - - log4j - log4j - - - org.slf4j - slf4j-log4j12 - - - org.slf4j - slf4j-api - - - - org.mortbay.jetty - servlet-api - provided + com.codeminders.socketio + socket-io + 1.0.10 + + + + org.apache.maven.plugins + maven-war-plugin + 2.1.1 + + true + + + + + 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 new file mode 100644 index 0000000..250fa4b --- /dev/null +++ b/samples/chat/src/main/java/com/codeminders/socketio/sample/chat/ChatSocketServlet.java @@ -0,0 +1,198 @@ +/** + * The MIT License + * Copyright (c) 2015 Alexander Sova (bird@codeminders.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.sample.chat; + +import com.codeminders.socketio.common.DisconnectReason; +import com.codeminders.socketio.common.SocketIOException; +import com.codeminders.socketio.server.*; +import com.codeminders.socketio.server.transport.websocket.WebsocketIOServlet; +import com.google.common.io.ByteStreams; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class ChatSocketServlet extends WebsocketIOServlet +{ + private static final String ANNOUNCEMENT = "announcement"; // server to all connected clients + private static final String CHAT_MESSAGE = "chat message"; // broadcast to room + private static final String WELCOME = "welcome"; // single event sent by server to specific client + private static final String FORCE_DISCONNECT = "force disconnect"; // client requests server to disconnect + private static final String SERVER_BINARY = "server binary"; // client requests server to send a binary + private static final String CLIENT_BINARY = "client binary"; // client sends binary + + private static final Logger LOGGER = Logger.getLogger(ChatSocketServlet.class.getName()); + + private static final long serialVersionUID = 1L; + + @Override + @SuppressWarnings("unchecked") + public void init(ServletConfig config) throws ServletException + { + super.init(config); + + of("/chat").on(new ConnectionListener() + { + @Override + public void onConnect(final Socket socket) + { + try + { + socket.emit(WELCOME, "Welcome to Socket.IO Chat, " + socket.getId() + "!"); + + socket.join("room"); + } + catch (SocketIOException e) + { + e.printStackTrace(); + socket.disconnect(true); + } + + socket.on(new DisconnectListener() { + + @Override + public void onDisconnect(Socket socket, DisconnectReason reason, String errorMessage) + { + of("/chat").emit(ANNOUNCEMENT, socket.getSession().getSessionId() + " disconnected"); + } + }); + + socket.on(CHAT_MESSAGE, new EventListener() + { + @Override + public Object onEvent(String name, Object[] args, boolean ackRequested) + { + LOGGER.log(Level.FINE, "Received chat message: " + args[0]); + + try + { + socket.broadcast("room", CHAT_MESSAGE, socket.getId(), args[0]); + } + catch (SocketIOException e) + { + e.printStackTrace(); + } + + return "OK"; //this object will be sent back to the client in ACK packet + } + }); + + socket.on(FORCE_DISCONNECT, new EventListener() + { + @Override + public Object onEvent(String name, Object[] args, boolean ackRequested) + { + socket.disconnect(false); + return null; + } + }); + + socket.on(CLIENT_BINARY, new EventListener() + { + @Override + public Object onEvent(String name, Object[] args, boolean ackRequested) + { + Map map = (Map)args[0]; + InputStream is = (InputStream) map.get("buffer"); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + try + { + ByteStreams.copy(is, os); + byte []array = os.toByteArray(); + String s = "["; + for (byte b : array) + s += " " + b; + s += " ]"; + LOGGER.log(Level.FINE, "Binary received: " + s); + } + catch (IOException e) + { + e.printStackTrace(); + } + + return "OK"; + } + }); + + socket.on(SERVER_BINARY, new EventListener() + { + @Override + public Object onEvent(String name, Object[] args, boolean ackRequested) + { + try + { + socket.emit(SERVER_BINARY, + new ByteArrayInputStream(new byte[] {1, 2, 3, 4}), + new ACKListener() + { + @Override + public void onACK(Object[] args) + { + System.out.println("ACK received: " + args[0]); + } + }); + } + catch (SocketIOException e) + { + socket.disconnect(true); + } + + return null; + } + }); + } + }); + +// Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(new Runnable() +// { +// @Override +// public void run() +// { +// try +// { +// of("/chat").in("room").emit("time", new Date().toString()); +// } +// catch (SocketIOException e) +// { +// e.printStackTrace(); +// } +// } +// }, 0, 20, TimeUnit.SECONDS); + + +// of("/news").on(new ConnectionListener() +// { +// @Override +// public void onConnect(Socket socket) +// { +// socket.on(); +// } +// }); + } +} 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 deleted file mode 100644 index b289f1c..0000000 --- a/samples/chat/src/main/java/com/glines/socketio/sample/chat/ChatSocketServlet.java +++ /dev/null @@ -1,161 +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.chat; - -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; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.util.Collections; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicInteger; - -public class ChatSocketServlet extends SocketIOServlet { - private static final long serialVersionUID = 1L; - private AtomicInteger ids = new AtomicInteger(1); - private Queue connections = new ConcurrentLinkedQueue(); - - @Override - public void init(ServletConfig config) throws ServletException { - JdkOverLog4j.install(); - super.init(config); - } - - private class ChatConnection implements SocketIOInbound { - private volatile SocketIOOutbound outbound = null; - private Integer sessionId = ids.getAndIncrement(); - - @Override - public void onConnect(SocketIOOutbound outbound) { - 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"))); - } - - @Override - public void onDisconnect(DisconnectReason reason, String errorMessage) { - this.outbound = null; - connections.remove(this); - broadcast(SocketIOFrame.JSON_MESSAGE_TYPE, new Gson().toJson( - Collections.singletonMap("announcement", sessionId + " disconnected"))); - } - - @Override - public void onMessage(int messageType, String message) { - if (message.equals("/rclose")) { - outbound.close(); - } else if (message.equals("/rdisconnect")) { - outbound.disconnect(); - } else if (message.startsWith("/sleep")) { - int sleepTime = 1; - String parts[] = message.split("\\s+"); - if (parts.length == 2) { - sleepTime = Integer.parseInt(parts[1]); - } - try { - Thread.sleep(sleepTime * 1000); - } catch (InterruptedException e) { - // Ignore - } - try { - outbound.sendMessage(SocketIOFrame.JSON_MESSAGE_TYPE, new Gson().toJson( - Collections.singletonMap("message", "Slept for " + sleepTime + " seconds."))); - } catch (SocketIOException e) { - outbound.disconnect(); - } - } else if (message.startsWith("/burst")) { - int burstNum = 10; - String parts[] = message.split("\\s+"); - if (parts.length == 2) { - burstNum = Integer.parseInt(parts[1]); - } - try { - for (int i = 0; i < burstNum; i++) { - outbound.sendMessage(SocketIOFrame.JSON_MESSAGE_TYPE, new Gson().toJson( - Collections.singletonMap("message", new String[]{"Server", "Hi " + i + - "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" + - "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" + - "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" + - "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" + - "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" + - "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" + - "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" + - "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" - }))); -// outbound.sendMessage(SocketIOFrame.JSON_MESSAGE_TYPE, JSON.toString( -// Collections.singletonMap("say","Hi " + i))); - try { - Thread.sleep(250); - } catch (InterruptedException e) { - // Do nothing - } - } - } catch (Exception e) { -// } catch (SocketIOException e) { -// outbound.disconnect(); - } - } else { - broadcast(SocketIOFrame.JSON_MESSAGE_TYPE, new Gson().toJson( - Collections.singletonMap("message", - new String[]{sessionId.toString(), (String) message}))); - } - } - - private void broadcast(int messageType, String message) { - for (ChatConnection c : connections) { - if (c != this) { - try { - c.outbound.sendMessage(messageType, message); - } catch (IOException e) { - c.outbound.disconnect(); - } - } - } - } - } - - @Override - protected SocketIOInbound doSocketIOConnect(HttpServletRequest request) { - return new ChatConnection(); - } - -} 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/WEB-INF/web.xml b/samples/chat/src/main/webapp/WEB-INF/web.xml index ff139b8..fd27fcd 100644 --- a/samples/chat/src/main/webapp/WEB-INF/web.xml +++ b/samples/chat/src/main/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/webapp/chat.html index b612e7a..48e544c 100644 --- a/samples/chat/src/main/webapp/chat.html +++ b/samples/chat/src/main/webapp/chat.html @@ -35,73 +35,102 @@

Sample chat client

@@ -110,7 +139,7 @@

Sample chat client

-