Just wanted to bring this to people's attention. I've employed the
websocket transport in our app for the first time and it ran fine on
Chrome and IE9, but failed on Firefox (both 6.2 and 7.0). I find that
when our app tries to return a large amount of data (100K or more) it
causes Firefox to close the MozWebSocket object. I then see a failure
reported in the cometd library as a /meta/connect failure.
I've chatted with Simone on IRC and it seems this is probably a
Firefox bug, not a cometd or jetty issue. Wireshark shows correct
network traffic to/from jetty, it all goes pear shaped when the data
hits Firefox.
Cometd 2.4.0beta2
Jetty 7.5.1
Firefox 7.0
about:config shows network.websocket.max-message-size = 16000000
I've built a test app that reproduced this behaviour, it simply
connects to a cometd server, handshakes, subscribes to a response
channel for the response data, then sends a request for some data.
The AbstractService-derived class in the java app server receives the
request, and generates an amount of data, and delivers it to the
response channel. When the amount of data returned is too large, I
see Firefox fail before any cometd handlers are called to handle the
received websocket message.
I've seen this behaviour on both my Linux laptop (fedora 15) and
Windows 7.
Here comes two files of code.. the cometdrawtest.html, a raw test of
cometd without my app's framework around it, and GenerateDataService,
an AbstractService that's set up in the server side framework. I have
to pass some handshake ext data in because our server authenticates
handshakes, please ignore that, it's irrelevant to this test.
cometdrawtest.html:
<!DOCTYPE html>
<html>
<head>
<title>Cometd Raw Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../resources/dojo/dojo/
dojo.js"></script>
<script type="text/javascript">
dojo.require("dojox.cometd");
var hHandshakeListener;
dojo.ready(function() {
dojox.cometd.websocketEnabled = true;
dojox.cometd.configure( {
url: '
http://ourserver.com:8080/winesdemo/cometd',
logLevel: 'debug'
});
hHandshakeListener = dojox.cometd.addListener("/meta/handshake",
function(data) {
console.log("/meta/handshake data: ", data);
if (data.successful === false) {
console.error("Handshake error! ", data);
console.dir(data);
} else {
console.debug("Handshake OK!");
// We'll need to know when our subscribe is complete, so we may
publish safely
dojox.cometd.addListener("/meta/subscribe", function(data) {
console.log("/meta/subscribe listener saw data: ", data);
if (data.successful === true && data.subscription === '/
generateddata/receive') {
// We're ready to go, enable the buttons.
dojo.query('button').forEach(function(b) { dojo.attr(b,
'disabled', false) });
}
});
// When we publish, the response will come back via this
channel.
dojox.cometd.subscribe('/generateddata/receive', function(data)
{
console.log("receive got: ", data);
});
}
});
dojox.cometd.handshake();
});
function request(amount) {
dojox.cometd.publish('/generateddata/go', { amount: amount });
}
function requestTenK() {
console.log("Publishing request for 10K");
request(10000);
}
function requestAThousandK() {
console.log("Publishing request for 1000K");
request(1000000);
}
function requestAmount() {
var am = parseInt(dojo.byId('amountField').value);
console.log("Publishing request for " + am);
request(am);
}
</script>
</head>
<body>
<p>This is a simple test page to request the cometd server send us
various amounts of data. The buttons are disabled until handshake and
subscription are seen to succeed. I find that 10000 (10K) is safe,
but 1000000 (1000K) is not safe.</p>
This works easily: <button type="button" onClick="requestTenK()"
disabled>Request 10K</button>
This does not work: <button type="button"
onClick="requestAThousandK()" disabled>Request 1000K</button>
<br/>These seem to be the limits: 67519 is fine, 67520 is not.
<button type="button" onClick="request(67519)" disabled>Request 67519</
button> <button type="button" onClick="request(67520)"
disabled>Request 67520</button>
Or type a value in here and click 'Request Amount': <input
id="amountField"/>
<button type="button" onClick="requestAmount()" disabled>Request
Amount</button>
</body>
</html>
GenerateDataService.java:
package ltd.yavin.liveauction.bayeux;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import javax.servlet.ServletContext;
import org.apache.commons.lang.StringUtils;
import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.ServerSession;
import org.cometd.server.AbstractService;
public class GenerateDataService extends AbstractService {
private static final Logger logger =
Logger.getLogger(GenerateDataService.class.getName());
public GenerateDataService(BayeuxServer bayeux)
{
super(bayeux, "generator");
addService("/generateddata/go", "_ctl");
}
public void destroy(ServletContext servletContext) {
}
@SuppressWarnings("unchecked")
public void _ctl(ServerSession remote, Map<String, Object> data) {
Object amountObj = data.get("amount");
long amount = 10000;
if (amountObj instanceof Long) {
amount = (Long)amountObj;
} else {
logger.severe("Unknown type of amount argument: " + amountObj +
" (" + amountObj.getClass().getName() + ")");
}
String generatedData = StringUtils.repeat("*", (int)amount);
Map<String, Object> returnObject = new HashMap<String, Object>();
returnObject.put("data", generatedData);
logger.info("Returning " + amount + " characters of data.");
remote.deliver(getServerSession(), "/generateddata/receive",
returnObject, null);
}
}