diff --git a/.classpath b/.classpath index 002455a..18252f6 100644 --- a/.classpath +++ b/.classpath @@ -5,7 +5,10 @@ - + + + + diff --git a/.gitignore b/.gitignore index d1b29c6..8dfa31b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ bin/ jar/ +.idea +*.iml diff --git a/README.markdown b/README.markdown index 674bee0..43add8b 100644 --- a/README.markdown +++ b/README.markdown @@ -20,10 +20,24 @@ Features: __Status:__ Connecting with Websocket is production ready. XHR is in beta. + ## How to use Using socket.io-java-client is quite simple. But lets see: +Checkout and compile the project: + +``` bash +git clone git://github.com/Gottox/socket.io-java-client.git +cd socket.io-java-client +ant jar +mv jar/socketio.jar /path/to/your/libs/project +``` + +If you're using ant, change your build.xml to include socketio.jar. If you're eclipse, add the jar to your project buildpath. + +Afterwards, you'll be able to use this library: + ``` java SocketIO socket = new SocketIO("http://127.0.0.1:3001/"); diff --git a/build.xml b/build.xml index 9c5a887..cd5186a 100644 --- a/build.xml +++ b/build.xml @@ -17,8 +17,11 @@ - - + + + + + @@ -28,16 +31,6 @@ - - - - - - - - - - @@ -51,14 +44,6 @@ - - - - - - - - @@ -115,7 +100,7 @@ - + diff --git a/examples/logback.xml b/examples/logback.xml new file mode 100644 index 0000000..a04713e --- /dev/null +++ b/examples/logback.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm} %-5level %logger{36} - %msg%n + + + + + + + + + + \ No newline at end of file diff --git a/examples/perfomance/MessageReceiver.java b/examples/perfomance/MessageReceiver.java new file mode 100644 index 0000000..f3dfbb3 --- /dev/null +++ b/examples/perfomance/MessageReceiver.java @@ -0,0 +1,85 @@ +package perfomance; + +import io.socket.*; +import org.json.JSONException; + +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Keesun Baik + */ +public class MessageReceiver { + + int connectionCount = 0; + int messageCount = 0; + + public static void main(String[] args) throws MalformedURLException, JSONException, InterruptedException { + MessageReceiver client = new MessageReceiver(); + List sockets = new ArrayList(); + + for(int i = 0 ; i < 10 ; i++) { + SocketIO socket = new SocketIO(); + CallBack callback = new CallBack(socket, client); + socket.connect("http://10.96.250.207:19191/", callback); + sockets.add(socket); + System.out.printf("[%d] sockets tried to connect.\n", sockets.size()); + if(i%100 == 0) { + Thread.sleep(500l); + } + } + + while (true) { + Thread.sleep(1000l); + System.out.printf("[%d] connections, got [%d] messages.\n", client.getConnectionCount(), client.getMessageCount()); + } + } + + public int getConnectionCount() { + return connectionCount; + } + + public int getMessageCount() { + return messageCount; + } + + public static class CallBack extends DeafultIoCallback { + + MessageReceiver client; + + public CallBack(SocketIO socket, MessageReceiver sampleClient) { + super(socket); + this.client = sampleClient; + } + + @Override + public void onConnect() { + this.client.plusConnectionCount(); + } + + @Override + public void on(String event, IOAcknowledge ack, Object... args) { + this.client.plusMessageCount(); + } + + @Override + public void onDisconnect() { + this.client.minusConnectionCount(); + } + + } + + private synchronized void plusMessageCount() { + this.messageCount++; + } + + private synchronized void minusConnectionCount() { + this.connectionCount--; + } + + private synchronized void plusConnectionCount() { + this.connectionCount++; + } + +} diff --git a/examples/perfomance/MessageSender.java b/examples/perfomance/MessageSender.java new file mode 100644 index 0000000..267da92 --- /dev/null +++ b/examples/perfomance/MessageSender.java @@ -0,0 +1,28 @@ +package perfomance; + +import io.socket.DeafultIoCallback; +import io.socket.SocketIO; +import org.json.JSONException; +import org.json.JSONObject; + +import java.net.MalformedURLException; + +/** + * @author Keesun Baik + */ +public class MessageSender { + + public static void main(String[] args) throws MalformedURLException, JSONException, InterruptedException { + int sendingMessageCount = 0; + SocketIO socket = new SocketIO(); + socket.connect("http://10.96.250.207:19191/", new DeafultIoCallback(socket)); + while (true) { + JSONObject message = new JSONObject(); + message.put("hello", "socket.io"); + socket.emit("send", message); + sendingMessageCount++; + System.out.printf("sent [%d] messages\n", sendingMessageCount); + Thread.sleep(10000l); + } + } +} diff --git a/examples/perfomance/compileReceiver.sh b/examples/perfomance/compileReceiver.sh new file mode 100644 index 0000000..4841192 --- /dev/null +++ b/examples/perfomance/compileReceiver.sh @@ -0,0 +1 @@ +javac -cp ~/apps/socket.io-java-client/jar/socketio.jar:~/apps/socket.io-java-client/libs/weberknecht-0.1.1.jar:~/apps/socket.io-java-client/libs/json-org.jar ./MessageReceiver.java \ No newline at end of file diff --git a/examples/perfomance/compileSender.sh b/examples/perfomance/compileSender.sh new file mode 100644 index 0000000..547b8f4 --- /dev/null +++ b/examples/perfomance/compileSender.sh @@ -0,0 +1 @@ +javac -cp ~/apps/socket.io-java-client/jar/socketio.jar:~/apps/socket.io-java-client/libs/weberknecht-0.1.1.jar:~/apps/socket.io-java-client/libs/json-org.jar ./MessageSender.java \ No newline at end of file diff --git a/examples/perfomance/runReceiver.sh b/examples/perfomance/runReceiver.sh new file mode 100644 index 0000000..3fb4e04 --- /dev/null +++ b/examples/perfomance/runReceiver.sh @@ -0,0 +1 @@ +java -Xms30G -Xmx30G -cp ~/apps/socket.io-java-client/jar/socketio.jar:~/apps/socket.io-java-client/libs/weberknecht-0.1.1.jar:~/apps/socket.io-java-client/libs/json-org.jar:. MessageReceiver \ No newline at end of file diff --git a/examples/perfomance/runSender.sh b/examples/perfomance/runSender.sh new file mode 100644 index 0000000..d46332e --- /dev/null +++ b/examples/perfomance/runSender.sh @@ -0,0 +1 @@ +java -Xms30G -Xmx30G -cp ~/apps/socket.io-java-client/jar/socketio.jar:~/apps/socket.io-java-client/libs/weberknecht-0.1.1.jar:~/apps/socket.io-java-client/libs/json-org.jar:. MessageSender \ No newline at end of file diff --git a/libs/WebSocket.jar b/libs/WebSocket.jar new file mode 100644 index 0000000..ef36379 Binary files /dev/null and b/libs/WebSocket.jar differ diff --git a/libs/logback-classic-1.0.6.jar b/libs/logback-classic-1.0.6.jar new file mode 100644 index 0000000..140b334 Binary files /dev/null and b/libs/logback-classic-1.0.6.jar differ diff --git a/libs/logback-core-1.0.6.jar b/libs/logback-core-1.0.6.jar new file mode 100644 index 0000000..4522494 Binary files /dev/null and b/libs/logback-core-1.0.6.jar differ diff --git a/libs/slf4j-api-1.6.6.jar b/libs/slf4j-api-1.6.6.jar new file mode 100644 index 0000000..4c03fa6 Binary files /dev/null and b/libs/slf4j-api-1.6.6.jar differ diff --git a/libs/weberknecht-0.1.1.jar b/libs/weberknecht-0.1.1.jar deleted file mode 100644 index ad94960..0000000 Binary files a/libs/weberknecht-0.1.1.jar and /dev/null differ diff --git a/src/io/socket/DeafultIoCallback.java b/src/io/socket/DeafultIoCallback.java new file mode 100644 index 0000000..1648254 --- /dev/null +++ b/src/io/socket/DeafultIoCallback.java @@ -0,0 +1,43 @@ +package io.socket; + +import org.json.JSONObject; + +/** + * @author Keesun Baik + */ +public class DeafultIoCallback implements IOCallback { + + protected SocketIO socket; + + public DeafultIoCallback() { + } + + public DeafultIoCallback(SocketIO socket) { + this.socket = socket; + } + + @Override + public void onDisconnect() { + } + + @Override + public void onConnect() { + } + + @Override + public void onMessage(String data, IOAcknowledge ack) { + } + + @Override + public void onMessage(JSONObject json, IOAcknowledge ack) { + } + + @Override + public void on(String event, IOAcknowledge ack, Object... args) { + } + + @Override + public void onError(SocketIOException socketIOException) { + } + +} diff --git a/src/io/socket/IOConnection.java b/src/io/socket/IOConnection.java index 403203d..e7dc52c 100644 --- a/src/io/socket/IOConnection.java +++ b/src/io/socket/IOConnection.java @@ -8,36 +8,32 @@ */ package io.socket; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSocketFactory; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; +import java.util.*; import java.util.Map.Entry; -import java.util.Properties; -import java.util.Scanner; -import java.util.Timer; -import java.util.TimerTask; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.logging.Logger; import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - /** * The Class IOConnection. */ class IOConnection implements IOCallback { /** Debug logger */ - static final Logger logger = Logger.getLogger("io.socket"); + static final Logger logger = LoggerFactory.getLogger(IOConnection.class); public static final String FRAME_DELIMITER = "\ufffd"; @@ -66,8 +62,7 @@ class IOConnection implements IOCallback { public static final String SOCKET_IO_1 = "/socket.io/1/"; /** The SSL socket factory for HTTPS connections */ - private static SSLSocketFactory sslSocketFactory = (SSLSocketFactory) SSLSocketFactory - .getDefault(); + private static SSLContext sslContext = null; /** All available connections. */ private static HashMap> connections = new HashMap>(); @@ -148,7 +143,6 @@ private class HearbeatTimeoutTask extends TimerTask { */ @Override public void run() { - setState(STATE_INVALID); error(new SocketIOException( "Timeout Error. No heartbeat from server within life time of the socket. closing.", lastException)); @@ -206,10 +200,19 @@ public void run() { /** * Set the socket factory used for SSL connections. * - * @param socketFactory + * @param sslContext + */ + public static void setSslContext(SSLContext sslContext) { + IOConnection.sslContext = sslContext; + } + + /** + * Get the socket factory used for SSL connections. + * + * @return socketFactory */ - public static void setDefaultSSLSocketFactory(SSLSocketFactory socketFactory) { - sslSocketFactory = socketFactory; + public static SSLContext getSslContext() { + return sslContext; } /** @@ -227,9 +230,11 @@ static public IOConnection register(String origin, SocketIO socket) { list = new LinkedList(); connections.put(origin, list); } else { - for (IOConnection connection : list) { - if (connection.register(socket)) - return connection; + synchronized (list) { + for (IOConnection connection : list) { + if (connection.register(socket)) + return connection; + } } } @@ -246,7 +251,7 @@ static public IOConnection register(String origin, SocketIO socket) { * @return true, if successfully registered on this transport, otherwise * false. */ - public boolean register(SocketIO socket) { + public synchronized boolean register(SocketIO socket) { String namespace = socket.getNamespace(); if (sockets.containsKey(namespace)) return false; @@ -265,7 +270,7 @@ public boolean register(SocketIO socket) { * @param socket * the socket to be shut down */ - public void unregister(SocketIO socket) { + public synchronized void unregister(SocketIO socket) { sendPlain("0::" + socket.getNamespace()); sockets.remove(socket.getNamespace()); socket.getCallback().onDisconnect(); @@ -289,7 +294,7 @@ private void handshake() { connection = url.openConnection(); if (connection instanceof HttpsURLConnection) { ((HttpsURLConnection) connection) - .setSSLSocketFactory(sslSocketFactory); + .setSSLSocketFactory(sslContext.getSocketFactory()); } connection.setConnectTimeout(connectTimeout); connection.setReadTimeout(connectTimeout); @@ -316,7 +321,7 @@ private void handshake() { /** * Connect transport. */ - private void connectTransport() { + private synchronized void connectTransport() { if (getState() == STATE_INVALID) return; setState(STATE_CONNECTING); @@ -409,19 +414,19 @@ private IOConnection(String url, SocketIO socket) { /** * Cleanup. IOConnection is not usable after this calling this. */ - private void cleanup() { + private synchronized void cleanup() { setState(STATE_INVALID); if (transport != null) transport.disconnect(); sockets.clear(); synchronized (connections) { List con = connections.get(urlStr); - if (con.size() > 1) + if (con != null && con.size() > 1) con.remove(this); else connections.remove(urlStr); } - logger.info("Cleanup"); + logger.debug("Cleanup"); backgroundTimer.cancel(); } @@ -444,19 +449,17 @@ private void error(SocketIOException e) { * @param text * the Text to be send. */ - private void sendPlain(String text) { - synchronized (outputBuffer) { - if (getState() == STATE_READY) - try { - logger.info("> " + text); - transport.send(text); - } catch (Exception e) { - logger.info("IOEx: saving"); - outputBuffer.add(text); - } - else { + private synchronized void sendPlain(String text) { + if (getState() == STATE_READY) + try { + logger.debug("> " + text); + transport.send(text); + } catch (Exception e) { + logger.debug("IOEx: saving"); outputBuffer.add(text); } + else { + outputBuffer.add(text); } } @@ -472,13 +475,15 @@ private void invalidateTransport() { /** * Reset timeout. */ - private void resetTimeout() { + private synchronized void resetTimeout() { if (heartbeatTimeoutTask != null) { heartbeatTimeoutTask.cancel(); } - heartbeatTimeoutTask = new HearbeatTimeoutTask(); - backgroundTimer.schedule(heartbeatTimeoutTask, closingTimeout - + heartbeatTimeout); + if(getState() != STATE_INVALID) { + heartbeatTimeoutTask = new HearbeatTimeoutTask(); + backgroundTimer.schedule(heartbeatTimeoutTask, closingTimeout + + heartbeatTimeout); + } } /** @@ -506,38 +511,36 @@ private IOCallback findCallback(IOMessage message) throws SocketIOException { * * {@link IOTransport} calls this when a connection is established. */ - public void transportConnected() { + public synchronized void transportConnected() { setState(STATE_READY); if (reconnectTask != null) { reconnectTask.cancel(); reconnectTask = null; } resetTimeout(); - synchronized (outputBuffer) { - if (transport.canSendBulk()) { - ConcurrentLinkedQueue outputBuffer = this.outputBuffer; - this.outputBuffer = new ConcurrentLinkedQueue(); - try { - // DEBUG - String[] texts = outputBuffer - .toArray(new String[outputBuffer.size()]); - logger.info("Bulk start:"); - for (String text : texts) { - logger.info("> " + text); - } - logger.info("Bulk end"); - // DEBUG END - transport.sendBulk(texts); - } catch (IOException e) { - this.outputBuffer = outputBuffer; + if (transport.canSendBulk()) { + ConcurrentLinkedQueue outputBuffer = this.outputBuffer; + this.outputBuffer = new ConcurrentLinkedQueue(); + try { + // DEBUG + String[] texts = outputBuffer.toArray(new String[outputBuffer + .size()]); + logger.debug("Bulk start:"); + for (String text : texts) { + logger.debug("> {}", text); } - } else { - String text; - while ((text = outputBuffer.poll()) != null) - sendPlain(text); + logger.debug("Bulk end"); + // DEBUG END + transport.sendBulk(texts); + } catch (IOException e) { + this.outputBuffer = outputBuffer; } - this.keepAliveInQueue = false; + } else { + String text; + while ((text = outputBuffer.poll()) != null) + sendPlain(text); } + this.keepAliveInQueue = false; } /** @@ -602,7 +605,7 @@ public void transportData(String text) { * the text */ public void transportMessage(String text) { - logger.info("< " + text); + logger.debug("< {}", text); IOMessage message; try { message = new IOMessage(text); @@ -668,7 +671,7 @@ public void transportMessage(String text) { + "Message was: " + message.toString(), e)); } } catch (JSONException e) { - logger.warning("Malformated JSON received"); + logger.warn("Malformated JSON received"); } break; case IOMessage.TYPE_EVENT: @@ -694,7 +697,7 @@ public void transportMessage(String text) { + "Message was: " + message.toString(), e)); } } catch (JSONException e) { - logger.warning("Malformated JSON received"); + logger.warn("Malformated JSON received"); } break; @@ -705,7 +708,7 @@ public void transportMessage(String text) { int id = Integer.parseInt(data[0]); IOAcknowledge ack = acknowledge.get(id); if (ack == null) - logger.warning("Received unknown ack packet"); + logger.warn("Received unknown ack packet"); else { JSONArray array = new JSONArray(data[1]); Object[] args = new Object[array.length()]; @@ -715,9 +718,9 @@ public void transportMessage(String text) { ack.ack(args); } } catch (NumberFormatException e) { - logger.warning("Received malformated Acknowledge! This is potentially filling up the acknowledges!"); + logger.warn("Received malformated Acknowledge! This is potentially filling up the acknowledges!"); } catch (JSONException e) { - logger.warning("Received malformated Acknowledge data!"); + logger.warn("Received malformated Acknowledge data!"); } } else if (data.length == 1) { sendPlain("6:::" + data[0]); @@ -738,7 +741,7 @@ public void transportMessage(String text) { case IOMessage.TYPE_NOOP: break; default: - logger.warning("Unkown type received" + message.getType()); + logger.warn("Unkown type received {}", message.getType()); break; } } @@ -747,17 +750,15 @@ public void transportMessage(String text) { * forces a reconnect. This had become useful on some android devices which * do not shut down TCP-connections when switching from HSDPA to Wifi */ - public void reconnect() { - synchronized (this) { - if (getState() != STATE_INVALID) { - invalidateTransport(); - setState(STATE_INTERRUPTED); - if (reconnectTask != null) { - reconnectTask.cancel(); - } - reconnectTask = new ReconnectTask(); - backgroundTimer.schedule(reconnectTask, 1000); + public synchronized void reconnect() { + if (getState() != STATE_INVALID) { + invalidateTransport(); + setState(STATE_INTERRUPTED); + if (reconnectTask != null) { + reconnectTask.cancel(); } + reconnectTask = new ReconnectTask(); + backgroundTimer.schedule(reconnectTask, 1000); } } @@ -873,7 +874,7 @@ public IOTransport getTransport() { public void onDisconnect() { SocketIO socket = sockets.get(""); if (socket != null) - socket.getCallback().onConnect(); + socket.getCallback().onDisconnect(); } @Override diff --git a/src/io/socket/SocketIO.java b/src/io/socket/SocketIO.java index b3c7f42..7ecbaa3 100644 --- a/src/io/socket/SocketIO.java +++ b/src/io/socket/SocketIO.java @@ -8,10 +8,14 @@ */ package io.socket; +import org.json.JSONObject; + +import javax.net.ssl.SSLSocketFactory; import java.net.MalformedURLException; import java.net.URL; import java.util.Properties; +import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import org.json.JSONObject; @@ -127,8 +131,8 @@ public SocketIO(final URL url) { * Set the socket factory used for SSL connections. * @param socketFactory */ - public static void setDefaultSSLSocketFactory(SSLSocketFactory socketFactory) { - IOConnection.setDefaultSSLSocketFactory(socketFactory); + public static void setDefaultSSLSocketFactory(SSLContext sslContext) { + IOConnection.setSslContext(sslContext); } /** @@ -198,6 +202,8 @@ else if (this.url == null) * @return true if connecting has been initiated, false if not */ private boolean setAndConnect(URL url, IOCallback callback) { + if(this.connection != null) + throw new RuntimeException("You can connect your SocketIO instance only once. Use a fresh instance instead."); if ((this.url != null && url != null) || (this.callback != null && callback != null)) return false; diff --git a/src/io/socket/WebsocketTransport.java b/src/io/socket/WebsocketTransport.java index 7ee667b..8eee840 100644 --- a/src/io/socket/WebsocketTransport.java +++ b/src/io/socket/WebsocketTransport.java @@ -1,144 +1,100 @@ -/* - * socket.io-java-client WebsocketTransport.java - * - * Copyright (c) 2012, Enno Boland - * socket.io-java-client is a implementation of the socket.io protocol in Java. - * - * See LICENSE file for more information - */ package io.socket; +import org.java_websocket.client.DefaultSSLWebSocketClientFactory; +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.handshake.ServerHandshake; + +import javax.net.ssl.SSLContext; import java.io.IOException; import java.net.URI; import java.net.URL; import java.util.regex.Pattern; -import de.roderick.weberknecht.WebSocketConnection; -import de.roderick.weberknecht.WebSocketEventHandler; -import de.roderick.weberknecht.WebSocketException; -import de.roderick.weberknecht.WebSocketMessage; - -/** - * The Class WebsocketTransport. - */ -class WebsocketTransport implements IOTransport, WebSocketEventHandler { - - WebSocketConnection websocket; - - /** Pattern used to replace http:// by ws:// respectively https:// by wss:// */ - private final static Pattern PATTERN_HTTP = Pattern.compile("^http"); - - /** The String to identify this Transport */ - public static final String TRANSPORT_NAME = "websocket"; - - /** The IOConnection of this transport. */ - private IOConnection connection; - - /** - * Creates a new Transport for the given url an {@link IOConnection}. - * - * @param url the url - * @param connection the connection - * @return the iO transport - */ - public static IOTransport create(URL url, IOConnection connection) { - URI uri = URI.create( - PATTERN_HTTP.matcher(url.toString()).replaceFirst("ws") - + IOConnection.SOCKET_IO_1 + TRANSPORT_NAME - + "/" + connection.getSessionId()); - - return new WebsocketTransport(uri, connection); - } - - /** - * Instantiates a new websocket transport. - * - * @param uri the uri - * @param connection the connection - * @throws WebSocketException - */ - public WebsocketTransport(URI uri, IOConnection connection) { - try { - websocket = new WebSocketConnection(uri); - } catch (WebSocketException e) { - connection.transportError(e); - return; - } - this.connection = connection; - websocket.setEventHandler(this); - } - - /* (non-Javadoc) - * @see io.socket.IOTransport#disconnect() - */ - @Override - public void disconnect() { - try { - websocket.close(); - } catch (Exception e) { - connection.transportError(e); - } - } - - /* (non-Javadoc) - * @see io.socket.IOTransport#canSendBulk() - */ - @Override - public boolean canSendBulk() { - return false; - } - - /* (non-Javadoc) - * @see io.socket.IOTransport#sendBulk(java.lang.String[]) - */ - @Override - public void sendBulk(String[] texts) throws IOException { - throw new RuntimeException("Cannot send Bulk!"); - } - - /* (non-Javadoc) - * @see io.socket.IOTransport#invalidate() - */ - @Override - public void invalidate() { - connection = null; - } - - @Override - public void onClose() { - if(connection != null) - connection.transportDisconnected(); - } - - @Override - public void onMessage(WebSocketMessage arg0) { - if(connection != null) - connection.transportMessage(arg0.getText()); - } - - @Override - public void onOpen() { - if(connection != null) - connection.transportConnected(); - } - - @Override - public void connect() { - try { - websocket.connect(); - } catch (WebSocketException e) { - connection.transportError(e); - } - } - - @Override - public void send(String text) throws Exception { - websocket.send(text); - } - - @Override - public String getName() { - return TRANSPORT_NAME; - } -} +class WebsocketTransport extends WebSocketClient implements IOTransport { + private final static Pattern PATTERN_HTTP = Pattern.compile("^http"); + public static final String TRANSPORT_NAME = "websocket"; + private IOConnection connection; + public static IOTransport create(URL url, IOConnection connection) { + URI uri = URI.create( + PATTERN_HTTP.matcher(url.toString()).replaceFirst("ws") + + IOConnection.SOCKET_IO_1 + TRANSPORT_NAME + + "/" + connection.getSessionId()); + + return new WebsocketTransport(uri, connection); + } + + public WebsocketTransport(URI uri, IOConnection connection) { + super(uri); + this.connection = connection; + SSLContext context = IOConnection.getSslContext(); + if("wss".equals(uri.getScheme()) && context != null) { + this.setWebSocketFactory(new DefaultSSLWebSocketClientFactory(context)); + } + } + + /* (non-Javadoc) + * @see io.socket.IOTransport#disconnect() + */ + @Override + public void disconnect() { + try { + this.close(); + } catch (Exception e) { + connection.transportError(e); + } + } + + /* (non-Javadoc) + * @see io.socket.IOTransport#canSendBulk() + */ + @Override + public boolean canSendBulk() { + return false; + } + + /* (non-Javadoc) + * @see io.socket.IOTransport#sendBulk(java.lang.String[]) + */ + @Override + public void sendBulk(String[] texts) throws IOException { + throw new RuntimeException("Cannot send Bulk!"); + } + + /* (non-Javadoc) + * @see io.socket.IOTransport#invalidate() + */ + @Override + public void invalidate() { + connection = null; + } + + @Override + public void onClose(int code, String reason, boolean remote) { + if(connection != null) + connection.transportDisconnected(); + } + + @Override + public void onMessage(String text) { + if(connection != null) + connection.transportMessage(text); + } + + @Override + public void onOpen(ServerHandshake handshakedata) { + if(connection != null) + connection.transportConnected(); + } + + @Override + public String getName() { + return TRANSPORT_NAME; + } + + @Override + public void onError(Exception ex) { + // TODO Auto-generated method stub + + } +} \ No newline at end of file diff --git a/src/io/socket/XhrTransport.java b/src/io/socket/XhrTransport.java index 94413e6..56f57fb 100644 --- a/src/io/socket/XhrTransport.java +++ b/src/io/socket/XhrTransport.java @@ -8,11 +8,7 @@ */ package io.socket; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; +import java.io.*; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; @@ -20,6 +16,9 @@ import java.util.Iterator; import java.util.concurrent.ConcurrentLinkedQueue; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; + /** * The Class XhrTransport. */ @@ -76,6 +75,10 @@ public void run() { URL url = new URL(XhrTransport.this.url.toString() + "?t=" + System.currentTimeMillis()); urlConnection = (HttpURLConnection) url.openConnection(); + SSLContext context = IOConnection.getSslContext(); + if(urlConnection instanceof HttpsURLConnection && context != null) { + ((HttpsURLConnection)urlConnection).setSSLSocketFactory(context.getSocketFactory()); + } if (!queue.isEmpty()) { urlConnection.setDoOutput(true); OutputStream output = urlConnection.getOutputStream(); diff --git a/tests/io/socket/AbstractTestSocketIO.java b/tests/io/socket/AbstractTestSocketIO.java index b52b110..8e1f6dd 100644 --- a/tests/io/socket/AbstractTestSocketIO.java +++ b/tests/io/socket/AbstractTestSocketIO.java @@ -36,7 +36,7 @@ public abstract class AbstractTestSocketIO implements IOCallback { private static final String REQUEST_ACKNOWLEDGE = "requestAcknowledge"; /** The Constant to the node executable */ - private final static String NODE = "node"; + private final static String NODE = "/usr/local/bin/node"; /** The port of this test, randomly choosed */ private int port = -1; @@ -98,7 +98,7 @@ public void setUp() throws Exception { node = Runtime.getRuntime().exec( new String[] { NODE, "./tests/io/socket/testutils/socketio.js", "" + getPort(), transport }); - proxy = new MutateProxy(getPort()+1, getPort()); + proxy = new MutateProxy(getPort() + 1, getPort()); proxy.start(); stdoutThread = new Thread("stdoutThread") { @@ -190,7 +190,8 @@ public void tearDown() throws Exception { */ void doConnect() throws Exception { // Setting up socket connection - socket = new SocketIO("http://127.0.0.1:" + getProxyPort() + "/main", this); + socket = new SocketIO("http://127.0.0.1:" + getProxyPort() + "/main", + this); assertEquals("onConnect", takeEvent()); assertEquals(transport, socket.getTransport()); } @@ -245,12 +246,12 @@ public void emitAndOn() throws Exception { socket.emit("echo"); assertEquals("Test String", "on", takeEvent()); - + String str = "TESTSTRING"; socket.emit("echo", str); assertEquals("Test String", "on", takeEvent()); assertEquals(str, takeArg()); - + JSONObject obj = new JSONObject("{'foo':'bar'}"); socket.emit("echo", obj); assertEquals("Test JSON", "on", takeEvent()); @@ -291,16 +292,16 @@ public void emitAndMessage() throws Exception { */ @Test(timeout = TIMEOUT) public void namespaces() throws Exception { - SocketIO ns1 = new SocketIO("http://127.0.0.1:" + getProxyPort() + "/ns1", - this); + SocketIO ns1 = new SocketIO("http://127.0.0.1:" + getProxyPort() + + "/ns1", this); assertEquals("onConnect", takeEvent()); doConnect(); ns1.disconnect(); assertEquals("onDisconnect", takeEvent()); - SocketIO ns2 = new SocketIO("http://127.0.0.1:" + getProxyPort() + "/ns2", - this); + SocketIO ns2 = new SocketIO("http://127.0.0.1:" + getProxyPort() + + "/ns2", this); assertEquals("onConnect", takeEvent()); assertEquals("onMessage_string", takeEvent()); assertEquals("ns2", takeArg()); @@ -311,8 +312,8 @@ public void namespaces() throws Exception { assertEquals("onMessage_string", takeEvent()); assertEquals("TESTSTRING", takeArg()); - SocketIO ns2_2 = new SocketIO("http://127.0.0.1:" + getProxyPort() + "/ns2", - this); + SocketIO ns2_2 = new SocketIO("http://127.0.0.1:" + getProxyPort() + + "/ns2", this); assertEquals("onConnect", takeEvent()); assertEquals("onMessage_string", takeEvent()); @@ -357,14 +358,34 @@ public void ack(Object... args) { }, "TESTSTRING"); assertEquals("ack", takeEvent()); assertEquals("TESTSTRING", takeArg()); - + socket.emit(REQUEST_ACKNOWLEDGE, "TESTSTRING"); assertEquals("on", takeEvent()); - assertEquals("TESTSTRING", takeArg()); + assertEquals("TESTSTRING", takeArg()); assertEquals("ACKNOWLEDGE:TESTSTRING", takeLine()); doClose(); } + @Test(timeout = TIMEOUT) + public void reconnectInvalidated() throws Exception { + doConnect(); + socket.disconnect(); + try { + socket.connect(this); + fail("reconnecting an invalidated socket should fail"); + } catch (RuntimeException ex) { + } + } + + @Test(timeout = TIMEOUT) + public void sendUtf8() throws Exception { + doConnect(); + socket.emit("fooo", "\uD83C\uDF84"); + socket.emit("fooo", "🎄"); + assertEquals("on", takeEvent()); + doClose(); + } + // END TESTS /** @@ -470,7 +491,7 @@ public void onMessage(JSONObject json, IOAcknowledge ack) { @Override public void on(String event, IOAcknowledge ack, Object... args) { events.add("on"); - if(event.equals(REQUEST_ACKNOWLEDGE)) { + if (event.equals(REQUEST_ACKNOWLEDGE)) { ack.ack(args); } this.args.addAll(Arrays.asList(args)); @@ -497,9 +518,8 @@ public int getPort() { port = 2048 + (int) (Math.random() * 10000) * 2; return port; } - + public int getProxyPort() { return getPort() + (proxy == null ? 0 : 1); } - }