Skip to content
Browse files

New infrastructure for AJAX/XHR/XDR plus tests

  • Loading branch information...
1 parent 25271b4 commit cd6106043f5ed1e498a3b6b90f9ea5f83d8a2f9a @majek majek committed
Showing with 245 additions and 3 deletions.
  1. +17 −0 lib/dom.js
  2. +23 −0 lib/eventemitter.js
  3. +2 −0 lib/index.js
  4. +132 −0 lib/utils2.js
  5. +57 −3 tests/html/src/domtests.coffee
  6. +14 −0 tests/server.js
View
17 lib/dom.js
@@ -47,6 +47,23 @@ utils.detachEvent = function(event, listener) {
}
};
+var on_unload = {};
+utils.unload_add = function(listener) {
+ var ref = utils.random_string(8);
+ on_unload[ref] = listener;
+ return ref;
+};
+utils.unload_del = function(ref) {
+ if (ref in on_unload)
+ delete on_unload[ref];
+};
+
+utils.attachEvent('unload', function() {
+ for(var k in on_unload) {
+ on_unload[k]();
+ };
+});
+
// Try to clear some headers, in order to save bandwidth. For
// reference see:
// http://blog.mibbit.com/?p=143
View
23 lib/eventemitter.js
@@ -0,0 +1,23 @@
+var EventEmitter = function(events) {
+ this.events = events || [];
+};
+EventEmitter.prototype.emit = function(type) {
+ var that = this;
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (!that.nuked && that['on'+type]) {
+ that['on'+type].apply(that, args);
+ }
+ if (utils.arrIndexOf(that.events, type) === -1) {
+ utils.log('Event ' + JSON.stringify(type) +
+ ' not listed ' + JSON.stringify(that.events) +
+ ' in ' + that);
+ }
+};
+
+EventEmitter.prototype.nuke = function(type) {
+ var that = this;
+ that.nuked = true;
+ for(var i=0; i<that.events.length; i++) {
+ delete that[that.events[i]];
+ }
+};
View
2 lib/index.js
@@ -4,7 +4,9 @@ SockJS = (function(){
var _window = window;
<!-- include lib/reventtarget.js -->
<!-- include lib/simpleevent.js -->
+<!-- include lib/eventemitter.js -->
<!-- include lib/utils.js -->
+<!-- include lib/utils2.js -->
<!-- include lib/dom.js -->
<!-- include lib/sockjs.js -->
<!-- include lib/trans-websocket.js -->
View
132 lib/utils2.js
@@ -0,0 +1,132 @@
+var XHRObject = utils.XHRObject = function(method, url, payload) {
+ var that = this;
+ utils.delay(function(){that._start(method, url, payload);});
+};
+
+XHRObject.prototype = new EventEmitter(['chunk', 'finish']);
+
+XHRObject.prototype._start = function(method, url, payload) {
+ var that = this;
+ if (_window.ActiveXObject) {
+ // IE caches POSTs
+ url += ((url.indexOf('?') === -1) ? '?' : '&') + 't='+(+new Date);
+ try {
+ that.xhr = new ActiveXObject('Microsoft.XMLHTTP');
+ } catch(x) {};
+ }
+ if (!that.xhr) {
+ that.xhr = new XMLHttpRequest();
+ }
+ that.unload_ref = utils.unload_add(function(){that._cleanup(true);});
+ try {
+ that.xhr.open(method, url, true);
+ } catch(e) {
+ // IE raises an exception on wrong port.
+ that.emit('finish', 0, '');
+ that._cleanup();
+ return;
+ };
+
+ if ('withCredentials' in that.xhr) {
+ that.xhr.withCredentials = 'true';
+ }
+
+ that.xhr.onreadystatechange = function() {
+ if (that.xhr) {
+ var x = that.xhr;
+ switch (x.readyState) {
+ case 3:
+ // IE doesn't like peeking into responseText or status on
+ // XHR and readystate=3
+ try {
+ var status = x.status;
+ var text = x.responseText;
+ that.emit('chunk', status, text);
+ } catch (x) {};
+ break;
+ case 4:
+ that.emit('finish', x.status, x.responseText);
+ that._cleanup(false);
+ break;
+ }
+ }
+ };
+ that.xhr.send(payload);
+};
+
+XHRObject.prototype._cleanup = function(abort) {
+ var that = this;
+ if (!that.xhr) return;
+ utils.unload_del(that.unload_ref);
+
+ // IE needs this field to be a function
+ that.xhr.onreadystatechange = function(){};
+
+ if (abort) {
+ try {
+ that.xhr.abort();
+ } catch(x) {};
+ }
+ that.unload_ref = that.xhr = null;
+};
+
+XHRObject.prototype.close = function() {
+ var that = this;
+ that.nuke();
+ that._cleanup(true);
+};
+
+
+var XDRObject = utils.XDRObject = function(method, url, payload) {
+ var that = this;
+ utils.delay(function(){that._start(method, url, payload);});
+};
+XDRObject.prototype = new EventEmitter(['chunk', 'finish']);
+XDRObject.prototype._start = function(method, url, payload) {
+ var that = this;
+ var xdr = new XDomainRequest();
+ // IE caches even POSTs
+ url += ((url.indexOf('?') === -1) ? '?' : '&') + 't='+(+new Date);
+
+ var onerror = xdr.ontimeout = xdr.onerror = function() {
+ that.emit('finish', 0, '');
+ that._cleanup(false);
+ };
+ xdr.onprogress = function() {
+ that.emit('chunk', 200, xdr.responseText);
+ };
+ xdr.onload = function() {
+ that.emit('finish', 200, xdr.responseText);
+ that._cleanup(false);
+ };
+ that.xdr = xdr;
+ that.unload_ref = utils.unload_add(function(){that._cleanup(true);});
+ try {
+ // Fails with AccessDenied if port number is bogus
+ that.xdr.open(method, url);
+ that.xdr.send(payload);
+ } catch(x) {
+ onerror();
+ }
+};
+
+XDRObject.prototype._cleanup = function(abort) {
+ var that = this;
+ if (!that.xdr) return;
+ utils.unload_del(that.unload_ref);
+
+ that.xdr.ontimeout = that.xdr.onerror = that.xdr.onprogress =
+ that.xdr.onload = null;
+ if (abort) {
+ try {
+ that.xdr.abort();
+ } catch(x) {};
+ }
+ that.unload_ref = that.xdr = null;
+};
+
+XDRObject.prototype.close = function() {
+ var that = this;
+ that.nuke();
+ that._cleanup(true);
+};
View
60 tests/html/src/domtests.coffee
@@ -89,10 +89,64 @@ else
start()
+ajax_simple_factory = (name) ->
+ asyncTest name + ' simple', ->
+ expect(2)
+ x = new u[name]('GET', '/simple.txt', null)
+ x.onfinish = (status, text) ->
+ equal(text.length, 2051)
+ equal(text.slice(-2), 'b\n')
+ start()
+
+ajax_streaming_factory = (name) ->
+ asyncTest name + ' streaming', ->
+ expect(3)
+ x = new u[name]('GET', '/streaming.txt', null)
+ chunkno = 0
+ x.onchunk = (status, text) ->
+ switch chunkno
+ when 0
+ equal(text.length, 2049)
+ equal(text.slice(-2), 'a\n')
+ chunkno += 1
+ x.onfinish = (status, text) ->
+ equal(text.slice(-4), 'a\nb\n')
+ start()
+
+
+test_wrong_url = (name, url, statuses) ->
+ expect(2)
+ x = new u[name]('GET', url, null)
+ x.onchunk = ->
+ fail(true)
+ x.onfinish = (status, text) ->
+ ok(u.arrIndexOf(statuses, status) isnt -1)
+ equal(text, '')
+ start()
+
+ajax_wrong_port_factory = (name) ->
+ for port in [25, 8999, 65300]
+ asyncTest name + ' wrong port ' + port, ->
+ test_wrong_url(name, 'http://localhost:'+port+'/streaming.txt', [0])
+
+ajax_simple_factory('XHRObject')
+if window.XDomainRequest
+ ajax_simple_factory('XDRObject')
+if not window.ActiveXObject
+ # Ajax streaming is not working in ie.
+ ajax_streaming_factory('XHRObject')
+if window.XDomainRequest
+ ajax_streaming_factory('XDRObject')
-# 1. data url
-# 2. wrong uri
-# 3. mass run - to verify mem leaks
+ajax_wrong_port_factory('XHRObject')
+if window.XDomainRequest
+ ajax_wrong_port_factory('XDRObject')
+asyncTest 'XHRObject wrong url', ->
+ # Opera responds with 0, all other browsers with 404
+ test_wrong_url('XHRObject', '/wrong_url_indeed.txt', [0, 404])
+if window.XDomainRequest
+ asyncTest 'XDRObject wrong url', ->
+ test_wrong_url('XDRObject', '/wrong_url_indeed.txt', [0])
View
14 tests/server.js
@@ -14,6 +14,20 @@ server.addListener('request', function(req, res) {
setTimeout(function() {
res.end('var a = 1;\n');
}, 500);
+ } else if ( /\/streaming.txt/.test(req.url) ) {
+ res.setHeader('content-type', 'text/plain');
+ res.setHeader('Access-Control-Allow-Origin', '*')
+
+ res.writeHead(200);
+ res.write(Array(2049).join('a') + '\n');
+ setTimeout(function() {
+ res.end('b\n');
+ }, 250);
+ } else if ( /\/simple.txt/.test(req.url) ) {
+ res.setHeader('content-type', 'text/plain');
+ res.setHeader('Access-Control-Allow-Origin', '*')
+ res.writeHead(200);
+ res.end(Array(2049).join('a') + '\nb\n');
} else if (req.url === '/config.js') {
res.setHeader('content-type', 'application/javascript');
res.writeHead(200);

0 comments on commit cd61060

Please sign in to comment.
Something went wrong with that request. Please try again.