Skip to content

Commit

Permalink
Fixed XHRtransport to more cleanly deal with the case where the clien…
Browse files Browse the repository at this point in the history
…t makes a second get attempt while one is already in progress.

Added ConnectionState to SocketIOConnection.
Fixed GWT impl so that host and port can be specified.
Modified GWT impl so that a failed connect can be distinguished from a regular disconnect.
  • Loading branch information
tadglines committed Oct 26, 2010
1 parent 61e6727 commit d022fc9
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 37 deletions.
42 changes: 40 additions & 2 deletions src/com/glines/socketio/client/SocketIOConnection.java
@@ -1,14 +1,52 @@
package com.glines.socketio.client;

public interface SocketIOConnection {
public enum ConnectionState {
CONNECTING,
OPEN,
CLOSING,
CLOSED;
}

interface SocketIOConnectionListener {
public abstract void onConnect();
public abstract void onDisconnect();

/**
* Called when the connection closes or if the initial connection attempt failed.
* If the initial connection attempt failed, then wasConnecting will be true.
*
* @param wasConnecting
*/
public abstract void onDisconnect(boolean wasConnecting);
public abstract void onMessage(String message);
}

/**
* 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(boolean) onDisonnect} will
* be called with a value of true.
* @throws IllegalStateException if the socket is not closed.
*/
void connect();

/**
* Initiate a disconnect. This does nothing if the socket is already disconnected or in the
* process of disconnecting.
*/
void disconnect();
boolean isOpen();

/**
* 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);
}
Expand Up @@ -9,7 +9,8 @@ public class GWTSocketIOConnectionFactory {
* @param port
* @return
*/
public static SocketIOConnection create(SocketIOConnection.SocketIOConnectionListener listener) {
return new GWTSocketIOConnectionImpl(listener);
public static SocketIOConnection create(SocketIOConnection.SocketIOConnectionListener listener,
String host, short port) {
return new GWTSocketIOConnectionImpl(listener, host, port);
}
}
55 changes: 39 additions & 16 deletions src/com/glines/socketio/client/gwt/GWTSocketIOConnectionImpl.java
Expand Up @@ -5,8 +5,8 @@

public class GWTSocketIOConnectionImpl implements SocketIOConnection {
private static final class SocketIOImpl extends JavaScriptObject {
public static native SocketIOImpl create(GWTSocketIOConnectionImpl impl) /*-{
var socket = new $wnd.io.Socket(null, {});
public static native SocketIOImpl create(GWTSocketIOConnectionImpl impl, String host, String port) /*-{
var socket = new $wnd.io.Socket(host, port != null ? {port: port} : {});
socket.on('connect', $entry(function() {
impl.@com.glines.socketio.client.gwt.GWTSocketIOConnectionImpl::onConnect()();
}));
Expand All @@ -19,7 +19,6 @@ public static native SocketIOImpl create(GWTSocketIOConnectionImpl impl) /*-{
return socket;
}-*/;

@SuppressWarnings("unused")
protected SocketIOImpl() {
}

Expand All @@ -28,49 +27,73 @@ protected SocketIOImpl() {
public native void disconnect() /*-{this.disconnect();}-*/;

public native void send(String data) /*-{this.send(data);}-*/;

public native boolean isConnecting() /*-{return this.connecting;}-*/;

public native boolean isConnected() /*-{return this.connected;}-*/;

public native boolean isDisconnecting() /*-{return this.disconnecting;}-*/;

public native boolean wasConnecting() /*-{return this.wasConnecting;}-*/;

public native boolean wasConnected() /*-{return this.wasConnected;}-*/;
}


private final SocketIOConnection.SocketIOConnectionListener listener;
private final String host;
private final String port;
private SocketIOImpl socket = null;
private ConnectionState state = ConnectionState.CLOSED;

GWTSocketIOConnectionImpl(SocketIOConnection.SocketIOConnectionListener listener) {
GWTSocketIOConnectionImpl(SocketIOConnection.SocketIOConnectionListener listener,
String host, short port) {
this.listener = listener;
this.host = host;
if (port > 0) {
this.port = "" + port;
} else {
this.port = null;
}
}

@Override
public void connect() {
if (socket != null) {
throw new IllegalStateException("Already connected");
if (socket == null) {
socket = SocketIOImpl.create(this, host, port);
}

if (ConnectionState.CLOSED != state) {
throw new IllegalStateException("Invalid connection state X " + state);
}
socket = SocketIOImpl.create(this);
state = ConnectionState.CONNECTING;
socket.connect();
}

@Override
public void disconnect() {
if (socket == null) {
throw new IllegalStateException("Not connected");
if (ConnectionState.OPEN == state) {
state = ConnectionState.CLOSING;
socket.disconnect();
}
socket.disconnect();
socket = null;
}

@Override
public boolean isOpen() {
return socket != null;
public ConnectionState getConnectionState() {
return state;
}

@Override
public void sendMessage(String message) {
if (socket == null) {
if (ConnectionState.OPEN != state) {
throw new IllegalStateException("Not connected");
}
socket.send(message);
}

@SuppressWarnings("unused")
private void onConnect() {
state = ConnectionState.OPEN;
try {
listener.onConnect();
} catch (Throwable t) {
Expand All @@ -80,9 +103,9 @@ private void onConnect() {

@SuppressWarnings("unused")
private void onDisconnect() {
socket = null;
state = ConnectionState.CLOSED;
try {
listener.onDisconnect();
listener.onDisconnect(socket.wasConnecting());
} catch (Throwable t) {
// Ignore
}
Expand Down
35 changes: 35 additions & 0 deletions src/com/glines/socketio/client/jre/SocketIOConnectionImpl.java
@@ -0,0 +1,35 @@
package com.glines.socketio.client.jre;

import com.glines.socketio.client.SocketIOConnection;

public class SocketIOConnectionImpl implements SocketIOConnection {

public SocketIOConnectionImpl(SocketIOConnection.SocketIOConnectionListener listener,
String host, short port) {

}

@Override
public void connect() {
// TODO Auto-generated method stub

}

@Override
public void disconnect() {
// TODO Auto-generated method stub

}

@Override
public ConnectionState getConnectionState() {
// TODO Auto-generated method stub
return null;
}

@Override
public void sendMessage(String message) {
// TODO Auto-generated method stub

}
}
46 changes: 38 additions & 8 deletions src/com/glines/socketio/server/transport/XHRTransport.java
Expand Up @@ -24,11 +24,13 @@
import com.glines.socketio.server.SocketIOSession.SessionTransportHandler;

public abstract class XHRTransport extends AbstractHttpTransport {
public static final String CONTINUATION_KEY = "com.glines.socketio.server.transport.XHRTransport.Continuation";
public static final String CONTINUATION_KEY =
"com.glines.socketio.server.transport.XHRTransport.Continuation";
private final int bufferSize;
private final int maxIdleTime;

protected abstract class XHRSessionHelper implements SessionTransportHandler, ContinuationListener {
protected abstract class XHRSessionHelper
implements SessionTransportHandler, ContinuationListener {
protected final SocketIOSession session;
private final TransportBuffer buffer = new TransportBuffer(bufferSize);
private boolean is_open = false;
Expand Down Expand Up @@ -67,9 +69,11 @@ public boolean isOpen() {
}

@Override
public void sendMessage(SocketIOMessage.Type type, String message) throws SocketIOException {
public void sendMessage(SocketIOMessage.Type type, String message)
throws SocketIOException {
if (is_open) {
System.out.println("Session["+session.getSessionId()+"]: sendMessage: [" + type + "]: " + message);
System.out.println("Session["+session.getSessionId()+"]: " +
"sendMessage: [" + type + "]: " + message);
if (continuation != null && continuation.isInitial()) {
List<String> messages = buffer.drainMessages();
messages.add(SocketIOMessage.encode(type, message));
Expand All @@ -87,7 +91,8 @@ public void sendMessage(SocketIOMessage.Type type, String message) throws Socket
}
} else {
String data = SocketIOMessage.encode(type, message);
if (continuation != null && continuation.isSuspended() && buffer.getAvailableBytes() < data.length()) {
if (continuation != null && continuation.isSuspended() &&
buffer.getAvailableBytes() < data.length()) {
continuation.resume();
}
if (buffer.putMessage(data, maxIdleTime) == false) {
Expand Down Expand Up @@ -123,10 +128,26 @@ public void handle(HttpServletRequest request,
response.sendError(HttpServletResponse.SC_NOT_FOUND);
} else {
Continuation cont = (Continuation)request.getAttribute(CONTINUATION_KEY);
if (cont != null && cont != continuation) {
return;
}
if (continuation != null) {
if (cont != continuation) {
if (cont != null) {
/*
* If the request continuation is non-null and doesn't match the
* active continuation then it's likely this is a result of an old
* continuation being resumes one last time.
* Just return and the continuation will be no more.
*/
return;
} else {
/*
* If the request has no continuation but there is an active
* continuation for the session then this request is probably due
* to the client making spurious requests.
* Return with no results.
*/
return;
}
}
List<String> messages = buffer.drainMessages();
if (messages.size() > 0) {
StringBuilder data = new StringBuilder();
Expand All @@ -145,6 +166,15 @@ public void handle(HttpServletRequest request,
continuation.suspend(response);
}
} else if (!isConnectionPersistant) {
if (cont != null) {
/*
* If the request continuation is not-null and and there is no
* active continuation then it's likely this is a result of an old
* continuation being resumes one last time.
* Just return and the continuation will be no more.
*/
return;
}
if (!buffer.isEmpty()) {
List<String> messages = buffer.drainMessages();
if (messages.size() > 0) {
Expand Down
28 changes: 19 additions & 9 deletions src/com/glines/socketio/socket.io.js
Expand Up @@ -9,6 +9,12 @@

this.io = {
version: '0.6',

setPath: function(path){
if (window.console && console.error) console.error('io.setPath will be removed. Please set the variable WEB_SOCKET_SWF_LOCATION pointing to WebSocketMain.swf');
this.path = /\/$/.test(path) ? path : path + '/';
WEB_SOCKET_SWF_LOCATION = path + 'flashsocket/WebSocketMain.swf';
}
};

if ('jQuery' in this) jQuery.io = this.io;
Expand Down Expand Up @@ -110,7 +116,7 @@ if (typeof window != 'undefined'){
};

Transport.prototype.disconnect = function(){
this.disconnecting = true;
this.base.disconnecting = true;
this.rawsend('~3~5~close');
this._disconnect();
};
Expand Down Expand Up @@ -227,15 +233,15 @@ if (typeof window != 'undefined'){
Transport.prototype._onConnect = function(){
this.connected = true;
this.connecting = false;
this.disconnecting = false;
this.base.disconnecting = false;
this.base._onConnect();
this._setTimeout();
};

Transport.prototype._onDisconnect = function(){
this.connecting = false;
this.connected = false;
this.disconnecting = false;
this.base.disconnecting = false;
this.sessionid = null;
this.base._onDisconnect();
};
Expand Down Expand Up @@ -306,7 +312,7 @@ if (typeof window != 'undefined'){
}
this._sendBuffer = [];
this._send(data);
} else if (this.disconnecting) {
} else if (this.base.disconnecting) {
this._disconnect();
}
};
Expand Down Expand Up @@ -848,6 +854,9 @@ JSONPPolling.xdomainCheck = function(){
this.options[i] = options[i];
this.connected = false;
this.connecting = false;
this.disconnecting = false;
this.wasConnected = false;
this.wasConnecting = false;
this._events = {};
this.transport = this.getTransport();
if (!this.transport && 'console' in window) console.error('No transport available');
Expand Down Expand Up @@ -901,7 +910,7 @@ JSONPPolling.xdomainCheck = function(){

Socket.prototype.send = function(data){
if (!this.transport || !this.transport.connected) return this._queue(data);
if (!this.transport.disconnecting) this.transport.send(data);
if (!this.disconnecting) this.transport.send(data);
return this;
};

Expand Down Expand Up @@ -962,11 +971,12 @@ JSONPPolling.xdomainCheck = function(){
};

Socket.prototype._onDisconnect = function(){
var wasConnected = this.connected;
this.wasConnected = this.connected;
this.wasConnecting = this.connecting;
this.connected = false;
this.connecting = false;
this._queueStack = [];
if (wasConnected) this.fire('disconnect');
this.fire('disconnect');
};

Socket.prototype.addListener = Socket.prototype.addEvent = Socket.prototype.addEventListener = Socket.prototype.on;
Expand Down Expand Up @@ -1792,10 +1802,10 @@ ASProxy.prototype =
try {
if (this.onmessage) {
var e;
if (window.MessageEvent) {
if (window.MessageEvent && !window.opera) {
e = document.createEvent("MessageEvent");
e.initMessageEvent("message", false, false, data, null, null, window, null);
} else { // IE
} else { // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes
e = {data: data};
}
this.onmessage(e);
Expand Down

0 comments on commit d022fc9

Please sign in to comment.