eventsource
Advanced tools
Comparing version 0.0.10 to 0.1.0
@@ -0,1 +1,6 @@ | ||
# [0.1.0](https://github.com/aslakhellesoy/eventsource-node/compare/v0.0.10...0.1.0) | ||
* Bugfix: High CPU usage by replacing Jison with port of WebKit's parser. ([#25](https://github.com/aslakhellesoy/eventsource-node/issues/25), [#32](https://github.com/aslakhellesoy/eventsource-node/pull/32), [#18](https://github.com/aslakhellesoy/eventsource-node/issues/18) qqueue) | ||
* Reformatted all code to 2 spaces. | ||
# [0.0.10](https://github.com/aslakhellesoy/eventsource-node/compare/v0.0.9...v0.0.10) | ||
@@ -8,7 +13,7 @@ | ||
* Bugfix: old "last-event-id" used on reconnect ([#27](https://github.com/aslakhellesoy/eventsource-node/pull/27) Aslak Hellesøy) | ||
* Bugfix: old "last-event-id" used on reconnect ([#27](https://github.com/aslakhellesoy/eventsource-node/pull/27) Aslak Hellesøy) | ||
# [0.0.8](https://github.com/aslakhellesoy/eventsource-node/compare/v0.0.7...v0.0.8) | ||
* Bugfix: EventSource still reconnected when closed ([#24](https://github.com/aslakhellesoy/eventsource-node/pull/24) FrozenCow) | ||
* Bugfix: EventSource still reconnected when closed ([#24](https://github.com/aslakhellesoy/eventsource-node/pull/24) FrozenCow) | ||
* Allow unauthorized HTTPS connections by setting `rejectUnauthorized` to false. (Aslak Hellesøy) | ||
@@ -18,3 +23,3 @@ | ||
* Explicitly raise an error when server returns http 403 and dont continue ([#20](https://github.com/aslakhellesoy/eventsource-node/pull/20) Scott Moak) | ||
* Explicitly raise an error when server returns http 403 and don't continue ([#20](https://github.com/aslakhellesoy/eventsource-node/pull/20) Scott Moak) | ||
* Added ability to send custom http headers to server ([#21](https://github.com/aslakhellesoy/eventsource-node/pull/21), [#9](https://github.com/aslakhellesoy/eventsource-node/issues/9) Scott Moak) | ||
@@ -21,0 +26,0 @@ * Fix Unicode support to cope with Javascript Unicode size limitations ([#23](https://github.com/aslakhellesoy/eventsource-node/pull/23), [#22](https://github.com/aslakhellesoy/eventsource-node/issues/22) Devon Adkisson) |
var http = require('http') | ||
, https =require('https') | ||
, https = require('https') | ||
, util = require('util') | ||
, events = require('events') | ||
, eventstream = require('./eventstream'); | ||
, events = require('events'); | ||
function isPlainObject(obj) { | ||
return Object.getPrototypeOf(obj) === Object.prototype; | ||
return Object.getPrototypeOf(obj) === Object.prototype; | ||
} | ||
@@ -19,138 +18,208 @@ | ||
function EventSource(url, eventSourceInitDict) { | ||
var readyState = EventSource.CONNECTING; | ||
Object.defineProperty(this, 'readyState', { | ||
get: function() { | ||
return readyState; | ||
} | ||
}); | ||
var readyState = EventSource.CONNECTING; | ||
Object.defineProperty(this, 'readyState', { | ||
get: function () { | ||
return readyState; | ||
} | ||
}); | ||
Object.defineProperty(this, 'url', { | ||
get: function() { | ||
return url; | ||
} | ||
}); | ||
Object.defineProperty(this, 'url', { | ||
get: function () { | ||
return url; | ||
} | ||
}); | ||
var self = this; | ||
self.reconnectInterval = 1000; | ||
var connectPending = false; | ||
var self = this; | ||
self.reconnectInterval = 1000; | ||
var connectPending = false; | ||
function onConnectionClosed(err) { | ||
if (connectPending || readyState === EventSource.CLOSED) return; | ||
connectPending = true; | ||
readyState = EventSource.CONNECTING; | ||
_emit('error', new Event('error')); | ||
function onConnectionClosed() { | ||
if (connectPending || readyState === EventSource.CLOSED) return; | ||
connectPending = true; | ||
readyState = EventSource.CONNECTING; | ||
_emit('error', new Event('error')); | ||
// The url may have been changed by a temporary | ||
// redirect. If that's the case, revert it now. | ||
if (reconnectUrl) { | ||
url = reconnectUrl; | ||
reconnectUrl = null; | ||
} | ||
setTimeout(function() { | ||
if (readyState !== EventSource.CONNECTING) { | ||
return; | ||
} | ||
connect(); | ||
}, self.reconnectInterval); | ||
// The url may have been changed by a temporary | ||
// redirect. If that's the case, revert it now. | ||
if (reconnectUrl) { | ||
url = reconnectUrl; | ||
reconnectUrl = null; | ||
} | ||
setTimeout(function () { | ||
if (readyState !== EventSource.CONNECTING) { | ||
return; | ||
} | ||
connect(); | ||
}, self.reconnectInterval); | ||
} | ||
var req; | ||
var lastEventId = ''; | ||
if (eventSourceInitDict && eventSourceInitDict.headers && isPlainObject(eventSourceInitDict.headers) && eventSourceInitDict.headers['Last-Event-ID']) { | ||
lastEventId = eventSourceInitDict.headers['Last-Event-ID']; | ||
delete eventSourceInitDict.headers['Last-Event-ID']; | ||
var req; | ||
var lastEventId = ''; | ||
if (eventSourceInitDict && eventSourceInitDict.headers && isPlainObject(eventSourceInitDict.headers) && eventSourceInitDict.headers['Last-Event-ID']) { | ||
lastEventId = eventSourceInitDict.headers['Last-Event-ID']; | ||
delete eventSourceInitDict.headers['Last-Event-ID']; | ||
} | ||
var discardTrailingNewline = false | ||
, data = '' | ||
, eventName = ''; | ||
var reconnectUrl = null; | ||
function connect() { | ||
connectPending = false; | ||
var options = require('url').parse(url); | ||
var isSecure = options.protocol == 'https:'; | ||
options.headers = { 'Cache-Control': 'no-cache', 'Accept': 'text/event-stream' }; | ||
if (lastEventId) options.headers['Last-Event-ID'] = lastEventId; | ||
if (eventSourceInitDict && eventSourceInitDict.headers && isPlainObject(eventSourceInitDict.headers)) { | ||
for (var i in eventSourceInitDict.headers) { | ||
var header = eventSourceInitDict.headers[i]; | ||
options.headers[i] = header; | ||
} | ||
} | ||
var reconnectUrl = null; | ||
options.rejectUnauthorized = !(eventSourceInitDict && eventSourceInitDict.rejectUnauthorized == false); | ||
function connect() { | ||
connectPending = false; | ||
var options = require('url').parse(url); | ||
var isSecure = options.protocol == 'https:'; | ||
options.headers = { 'Cache-Control': 'no-cache', 'Accept': 'text/event-stream' }; | ||
if (lastEventId) options.headers['Last-Event-ID'] = lastEventId; | ||
if (eventSourceInitDict && eventSourceInitDict.headers && isPlainObject(eventSourceInitDict.headers)) { | ||
for (var i in eventSourceInitDict.headers) { | ||
var header = eventSourceInitDict.headers[i]; | ||
options.headers[i] = header; | ||
} | ||
req = (isSecure ? https : http).request(options, function (res) { | ||
// Handle HTTP redirects | ||
if (res.statusCode == 301 || res.statusCode == 307) { | ||
if (!res.headers.location) { | ||
// Server sent redirect response without Location header. | ||
_emit('error', new Event('error')); | ||
return; | ||
} | ||
options.rejectUnauthorized = true; | ||
if(eventSourceInitDict && eventSourceInitDict.rejectUnauthorized == false) { | ||
options.rejectUnauthorized = false; | ||
} | ||
if (res.statusCode == 307) reconnectUrl = url; | ||
url = res.headers.location; | ||
process.nextTick(connect); | ||
return; | ||
} | ||
req = (isSecure ? https : http).request(options, function(res) { | ||
// Handle HTTP redirects | ||
if (res.statusCode == 301 || res.statusCode == 307) { | ||
if (!res.headers.location) { | ||
// Server sent redirect response without Location header. | ||
_emit('error', new Event('error')); | ||
return; | ||
} | ||
if (res.statusCode == 307) reconnectUrl = url; | ||
url = res.headers.location; | ||
process.nextTick(connect); | ||
return; | ||
if (res.statusCode == 204) return self.close(); | ||
if (res.statusCode == 403) { | ||
// 'Access denied' | ||
_emit('error', new Event('error')); | ||
return self.close(); | ||
} | ||
readyState = EventSource.OPEN; | ||
res.on('close', onConnectionClosed); | ||
res.on('end', onConnectionClosed); | ||
_emit('open', new Event('open')); | ||
// text/event-stream parser adapted from webkit's | ||
// Source/WebCore/page/EventSource.cpp | ||
var buf = ''; | ||
res.on('data', function (chunk) { | ||
buf += chunk; | ||
var pos = 0 | ||
, length = buf.length; | ||
while (pos < length) { | ||
if (discardTrailingNewline) { | ||
if (buf[pos] === '\n') { | ||
++pos; | ||
} | ||
discardTrailingNewline = false; | ||
} | ||
if (res.statusCode == 204) return self.close(); | ||
var lineLength = -1 | ||
, fieldLength = -1 | ||
, c; | ||
if (res.statusCode == 403) { | ||
// 'Access denied' | ||
_emit('error', new Event('error')); | ||
return self.close(); | ||
for (var i = pos; lineLength < 0 && i < length; ++i) { | ||
c = buf[i]; | ||
if (c === ':') { | ||
if (fieldLength < 0) { | ||
fieldLength = i - pos; | ||
} | ||
} else if (c === '\r') { | ||
discardTrailingNewline = true; | ||
lineLength = i - pos; | ||
} else if (c === '\n') { | ||
lineLength = i - pos; | ||
} | ||
} | ||
readyState = EventSource.OPEN; | ||
res.on('close', onConnectionClosed); | ||
res.on('end', onConnectionClosed); | ||
_emit('open', new Event('open')); | ||
if (lineLength < 0) { | ||
break; | ||
} | ||
var buf = ''; | ||
res.on('data', function (chunk) { | ||
buf = buf + chunk; | ||
var messages = buf.match(/(.|\r\n|\n|\r)*(\n\n|\r\r|\r\n\r\n)/); | ||
if (!messages) return; | ||
messages = messages[0]; | ||
buf = buf.slice(messages.length); | ||
try { | ||
messages = eventstream.parse(messages); | ||
if (!messages) return; | ||
messages.forEach(function(message) { | ||
var data = message.data.replace(/\n$/, ''); | ||
if (data == '' || (message.event != null && message.event == '')) { | ||
return; | ||
} | ||
if (message.id) lastEventId = message.id; | ||
var type = message.event || 'message'; | ||
_emit(type, new MessageEvent('message', { | ||
data: data, | ||
lastEventId: lastEventId | ||
})); | ||
}); | ||
} catch(e) { | ||
_emit('error', new Event('error')); | ||
} | ||
}); | ||
}); | ||
parseEventStreamLine(buf, pos, fieldLength, lineLength); | ||
req.on('error', onConnectionClosed); | ||
req.setNoDelay(true); | ||
req.end(); | ||
} | ||
connect(); | ||
pos += lineLength + 1; | ||
} | ||
function _emit() { | ||
if(self.listeners(arguments[0]).length > 0) { | ||
self.emit.apply(self, arguments); | ||
if (pos === length) { | ||
buf = ''; | ||
} else if (pos > 0) { | ||
buf = buf.slice(pos); | ||
} | ||
}); | ||
}); | ||
req.on('error', onConnectionClosed); | ||
req.setNoDelay(true); | ||
req.end(); | ||
} | ||
connect(); | ||
function _emit() { | ||
if (self.listeners(arguments[0]).length > 0) { | ||
self.emit.apply(self, arguments); | ||
} | ||
} | ||
this.close = function() { | ||
if (readyState == EventSource.CLOSED) return; | ||
readyState = EventSource.CLOSED; | ||
req.abort(); | ||
this.close = function () { | ||
if (readyState == EventSource.CLOSED) return; | ||
readyState = EventSource.CLOSED; | ||
req.abort(); | ||
}; | ||
function parseEventStreamLine(buf, pos, fieldLength, lineLength) { | ||
if (lineLength === 0) { | ||
if (data.length > 0) { | ||
var type = eventName || 'message'; | ||
_emit(type, new MessageEvent('message', { | ||
data: data.slice(0, -1), // remove trailing newline | ||
lastEventId: lastEventId | ||
})); | ||
data = ''; | ||
} | ||
eventName = void 0; | ||
} else if (fieldLength > 0) { | ||
var noValue = fieldLength < 0 | ||
, step = 0 | ||
, field = buf.slice(pos, pos + (noValue ? lineLength : fieldLength)); | ||
if (noValue) { | ||
step = lineLength; | ||
} else if (buf[pos + fieldLength + 1] !== ' ') { | ||
step = fieldLength + 1; | ||
} else { | ||
step = fieldLength + 2; | ||
} | ||
pos += step; | ||
var valueLength = lineLength - step | ||
, value = buf.slice(pos, pos + valueLength); | ||
if (field === 'data') { | ||
if (value.length > 0) { | ||
data += value + '\n'; | ||
} | ||
} else if (field === 'event') { | ||
eventName = value; | ||
} else if (field === 'id') { | ||
lastEventId = value; | ||
} else if (field === 'retry') { | ||
var retry = parseInt(value, 10); | ||
if (!Number.isNaN(retry)) { | ||
self.reconnectInterval = retry; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
@@ -163,27 +232,27 @@ | ||
['open', 'error', 'message'].forEach(function(method) { | ||
Object.defineProperty(EventSource.prototype, 'on' + method, { | ||
/** | ||
* Returns the current listener | ||
* | ||
* @return {Mixed} the set function or undefined | ||
* @api private | ||
*/ | ||
get: function get() { | ||
var listener = this.listeners(method)[0]; | ||
return listener ? (listener._listener ? listener._listener : listener) : undefined; | ||
}, | ||
['open', 'error', 'message'].forEach(function (method) { | ||
Object.defineProperty(EventSource.prototype, 'on' + method, { | ||
/** | ||
* Returns the current listener | ||
* | ||
* @return {Mixed} the set function or undefined | ||
* @api private | ||
*/ | ||
get: function get() { | ||
var listener = this.listeners(method)[0]; | ||
return listener ? (listener._listener ? listener._listener : listener) : undefined; | ||
}, | ||
/** | ||
* Start listening for events | ||
* | ||
* @param {Function} listener the listener | ||
* @return {Mixed} the set function or undefined | ||
* @api private | ||
*/ | ||
set: function set(listener) { | ||
this.removeAllListeners(method); | ||
this.addEventListener(method, listener); | ||
} | ||
}); | ||
/** | ||
* Start listening for events | ||
* | ||
* @param {Function} listener the listener | ||
* @return {Mixed} the set function or undefined | ||
* @api private | ||
*/ | ||
set: function set(listener) { | ||
this.removeAllListeners(method); | ||
this.addEventListener(method, listener); | ||
} | ||
}); | ||
}); | ||
@@ -198,3 +267,2 @@ | ||
/** | ||
@@ -204,3 +272,3 @@ * Emulates the W3C Browser based WebSocket interface using addEventListener. | ||
* @param {String} method Listen for an event | ||
* @param {Function} event callback | ||
* @param {Function} listener callback | ||
* @see https://developer.mozilla.org/en/DOM/element.addEventListener | ||
@@ -211,7 +279,7 @@ * @see http://dev.w3.org/html5/websockets/#the-websocket-interface | ||
EventSource.prototype.addEventListener = function addEventListener(method, listener) { | ||
if (typeof listener === 'function') { | ||
// store a reference so we can return the original function again | ||
listener._listener = listener; | ||
this.on(method, listener); | ||
} | ||
if (typeof listener === 'function') { | ||
// store a reference so we can return the original function again | ||
listener._listener = listener; | ||
this.on(method, listener); | ||
} | ||
}; | ||
@@ -226,3 +294,3 @@ | ||
function Event(type) { | ||
Object.defineProperty(this, 'type', { writable: false, value: type }); | ||
Object.defineProperty(this, 'type', { writable: false, value: type }); | ||
} | ||
@@ -237,9 +305,9 @@ | ||
function MessageEvent(type, eventInitDict) { | ||
Object.defineProperty(this, 'type', { writable: false, value: type }); | ||
for(var f in eventInitDict) { | ||
if(eventInitDict.hasOwnProperty(f)) { | ||
Object.defineProperty(this, f, { writable: false, value: eventInitDict[f] }); | ||
} | ||
Object.defineProperty(this, 'type', { writable: false, value: type }); | ||
for (var f in eventInitDict) { | ||
if (eventInitDict.hasOwnProperty(f)) { | ||
Object.defineProperty(this, f, { writable: false, value: eventInitDict[f] }); | ||
} | ||
} | ||
} | ||
{ | ||
"name": "eventsource", | ||
"version": "0.0.10", | ||
"version": "0.1.0", | ||
"description": "W3C compliant EventSource client for Node.js", | ||
@@ -20,3 +20,5 @@ "keywords": [ | ||
"Devon Adkisson", | ||
"FrozenCow <frozencow@gmail.com>" | ||
"FrozenCow <frozencow@gmail.com>", | ||
"mbieser <29erpilot@gmail.com>", | ||
"qqueue <queue@hakase.org>" | ||
], | ||
@@ -41,7 +43,6 @@ "repository": { | ||
"devDependencies": { | ||
"mocha": "~1.9.0", | ||
"jison": "~0.4.4" | ||
"mocha": "~1.17.1" | ||
}, | ||
"scripts": { | ||
"test": "make run-tests" | ||
"test": "./node_modules/.bin/mocha --reporter spec" | ||
}, | ||
@@ -48,0 +49,0 @@ "engines": { |
@@ -9,843 +9,872 @@ var EventSource = require('../lib/eventsource') | ||
var servers = 0; | ||
process.on('exit', function() { | ||
if(servers != 0) { | ||
console.error("************ Didn't kill all servers - there is still %d running.", servers); | ||
} | ||
process.on('exit', function () { | ||
if (servers != 0) { | ||
console.error("************ Didn't kill all servers - there is still %d running.", servers); | ||
} | ||
}); | ||
function createServer(chunks, callback, onreq, secure) { | ||
var options = {}; | ||
var isSecure = onreq === true || secure === true; | ||
if (isSecure) { | ||
options = { | ||
key: fs.readFileSync(__dirname + '/key.pem'), | ||
cert: fs.readFileSync(__dirname + '/certificate.pem') | ||
}; | ||
} | ||
var responses = []; | ||
function open(req, res) { | ||
if (typeof onreq == 'function' && onreq(req, res) === true) return; | ||
res.writeHead(200, {'Content-Type': 'text/event-stream'}); | ||
chunks.forEach(function(chunk) { res.write(chunk); }); | ||
res.write(':'); // send a dummy comment to ensure that the head is flushed | ||
responses.push(res); | ||
} | ||
function close(closed) { | ||
responses.forEach(function(res) { | ||
res.end(); | ||
}); | ||
server.on('close', closed); | ||
server.close(); | ||
} | ||
var server; | ||
if (isSecure) server = https.createServer(options, open); | ||
else server = http.createServer(open); | ||
server.listen(_port, function onOpen() { | ||
servers++; | ||
callback(_port++, function(err) { | ||
servers--; | ||
close(err); | ||
}); | ||
var options = {}; | ||
var isSecure = onreq === true || secure === true; | ||
if (isSecure) { | ||
options = { | ||
key: fs.readFileSync(__dirname + '/key.pem'), | ||
cert: fs.readFileSync(__dirname + '/certificate.pem') | ||
}; | ||
} | ||
var responses = []; | ||
function open(req, res) { | ||
if (typeof onreq == 'function' && onreq(req, res) === true) return; | ||
res.writeHead(200, {'Content-Type': 'text/event-stream'}); | ||
chunks.forEach(function (chunk) { | ||
res.write(chunk); | ||
}); | ||
}; | ||
res.write(':'); // send a dummy comment to ensure that the head is flushed | ||
responses.push(res); | ||
} | ||
describe('Parser', function() { | ||
it('parses multibyte characters', function(done) { | ||
createServer(["id: 1\ndata: €豆腐\n\n"], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = function(m) { | ||
assert.equal("€豆腐", m.data); | ||
es.close(); | ||
close(done); | ||
}; | ||
}); | ||
function close(closed) { | ||
responses.forEach(function (res) { | ||
res.end(); | ||
}); | ||
server.on('close', closed); | ||
server.close(); | ||
} | ||
it('raises error when it fails to parse', function(done) { | ||
createServer(["\n\n\n\nid: 1\ndata: hello world\n\n"], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = function(m) { | ||
assert.equal("hello world", m.data); | ||
es.close(); | ||
close(done); | ||
}; | ||
es.onerror = function(e) { | ||
es.close(); | ||
close(done); | ||
}; | ||
}); | ||
var server; | ||
if (isSecure) server = https.createServer(options, open); | ||
else server = http.createServer(open); | ||
server.listen(_port, function onOpen() { | ||
servers++; | ||
callback(_port++, function (err) { | ||
servers--; | ||
close(err); | ||
}); | ||
}); | ||
} | ||
it('parses one one-line message in one chunk', function(done) { | ||
createServer(["data: Hello\n\n"], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = function(m) { | ||
assert.equal("Hello", m.data); | ||
es.close(); | ||
close(done); | ||
}; | ||
}); | ||
describe('Parser', function () { | ||
it('parses multibyte characters', function (done) { | ||
createServer(["id: 1\ndata: €豆腐\n\n"], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = function (m) { | ||
assert.equal("€豆腐", m.data); | ||
es.close(); | ||
close(done); | ||
}; | ||
}); | ||
}); | ||
it('parses one one-line message in two chunks', function(done) { | ||
createServer(["data: Hel", "lo\n\n"], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = function(m) { | ||
assert.equal("Hello", m.data); | ||
es.close(); | ||
close(done); | ||
}; | ||
}); | ||
it('parses empty lines with multibyte characters', function (done) { | ||
createServer(["\n\n\n\nid: 1\ndata: 我現在都看實況不玩遊戲\n\n"], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = function (m) { | ||
assert.equal("我現在都看實況不玩遊戲", m.data); | ||
es.close(); | ||
close(done); | ||
}; | ||
}); | ||
}); | ||
it('parses two one-line messages in one chunk', function(done) { | ||
createServer(["data: Hello\n\n", "data: World\n\n"], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = first; | ||
it('parses one one-line message in one chunk', function (done) { | ||
createServer(["data: Hello\n\n"], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = function (m) { | ||
assert.equal("Hello", m.data); | ||
es.close(); | ||
close(done); | ||
}; | ||
}); | ||
}); | ||
function first(m) { | ||
assert.equal("Hello", m.data); | ||
es.onmessage = second; | ||
} | ||
it('parses one one-line message in two chunks', function (done) { | ||
createServer(["data: Hel", "lo\n\n"], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = function (m) { | ||
assert.equal("Hello", m.data); | ||
es.close(); | ||
close(done); | ||
}; | ||
}); | ||
}); | ||
function second(m) { | ||
assert.equal("World", m.data); | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
it('parses two one-line messages in one chunk', function (done) { | ||
createServer(["data: Hello\n\n", "data: World\n\n"], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = first; | ||
function first(m) { | ||
assert.equal("Hello", m.data); | ||
es.onmessage = second; | ||
} | ||
function second(m) { | ||
assert.equal("World", m.data); | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
}); | ||
it('parses one two-line message in one chunk', function(done) { | ||
createServer(["data: Hello\ndata:World\n\n"], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = function(m) { | ||
assert.equal("Hello\nWorld", m.data); | ||
es.close(); | ||
close(done); | ||
}; | ||
}); | ||
it('parses one two-line message in one chunk', function (done) { | ||
createServer(["data: Hello\ndata:World\n\n"], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = function (m) { | ||
assert.equal("Hello\nWorld", m.data); | ||
es.close(); | ||
close(done); | ||
}; | ||
}); | ||
}); | ||
it('parses really chopped up unicode data', function(done) { | ||
var chopped = "data: Aslak\n\ndata: Hellesøy\n\n".split(""); | ||
createServer(chopped, function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = first; | ||
it('parses really chopped up unicode data', function (done) { | ||
var chopped = "data: Aslak\n\ndata: Hellesøy\n\n".split(""); | ||
createServer(chopped, function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = first; | ||
function first(m) { | ||
assert.equal("Aslak", m.data); | ||
es.onmessage = second; | ||
} | ||
function first(m) { | ||
assert.equal("Aslak", m.data); | ||
es.onmessage = second; | ||
} | ||
function second(m) { | ||
assert.equal("Hellesøy", m.data); | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
function second(m) { | ||
assert.equal("Hellesøy", m.data); | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
}); | ||
it('accepts CRLF as separator', function(done) { | ||
var chopped = "data: Aslak\r\n\r\ndata: Hellesøy\r\n\r\n".split(""); | ||
createServer(chopped, function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = first; | ||
it('accepts CRLF as separator', function (done) { | ||
var chopped = "data: Aslak\r\n\r\ndata: Hellesøy\r\n\r\n".split(""); | ||
createServer(chopped, function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = first; | ||
function first(m) { | ||
assert.equal("Aslak", m.data); | ||
es.onmessage = second; | ||
} | ||
function first(m) { | ||
assert.equal("Aslak", m.data); | ||
es.onmessage = second; | ||
} | ||
function second(m) { | ||
assert.equal("Hellesøy", m.data); | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
function second(m) { | ||
assert.equal("Hellesøy", m.data); | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
}); | ||
it('accepts CR as separator', function(done) { | ||
var chopped = "data: Aslak\r\rdata: Hellesøy\r\r".split(""); | ||
createServer(chopped, function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = first; | ||
it('accepts CR as separator', function (done) { | ||
var chopped = "data: Aslak\r\rdata: Hellesøy\r\r".split(""); | ||
createServer(chopped, function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = first; | ||
function first(m) { | ||
assert.equal("Aslak", m.data); | ||
es.onmessage = second; | ||
} | ||
function first(m) { | ||
assert.equal("Aslak", m.data); | ||
es.onmessage = second; | ||
} | ||
function second(m) { | ||
assert.equal("Hellesøy", m.data); | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
function second(m) { | ||
assert.equal("Hellesøy", m.data); | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
}); | ||
it('delivers message with explicit event', function(done) { | ||
createServer(["event: greeting\ndata: Hello\n\n"], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.addEventListener('greeting', function(m) { | ||
assert.equal("Hello", m.data); | ||
es.close(); | ||
close(done); | ||
}); | ||
}); | ||
it('delivers message with explicit event', function (done) { | ||
createServer(["event: greeting\ndata: Hello\n\n"], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.addEventListener('greeting', function (m) { | ||
assert.equal("Hello", m.data); | ||
es.close(); | ||
close(done); | ||
}); | ||
}); | ||
}); | ||
it('ignores comments', function(done) { | ||
createServer(["data: Hello\n\n:nothing to see here\n\ndata: World\n\n"], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = first; | ||
it('ignores comments', function (done) { | ||
createServer(["data: Hello\n\n:nothing to see here\n\ndata: World\n\n"], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = first; | ||
function first(m) { | ||
assert.equal("Hello", m.data); | ||
es.onmessage = second; | ||
} | ||
function first(m) { | ||
assert.equal("Hello", m.data); | ||
es.onmessage = second; | ||
} | ||
function second(m) { | ||
assert.equal("World", m.data); | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
function second(m) { | ||
assert.equal("World", m.data); | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
}); | ||
it('ignores empty comments', function(done) { | ||
createServer(["data: Hello\n\n:\n\ndata: World\n\n"], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = first; | ||
it('ignores empty comments', function (done) { | ||
createServer(["data: Hello\n\n:\n\ndata: World\n\n"], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = first; | ||
function first(m) { | ||
assert.equal("Hello", m.data); | ||
es.onmessage = second; | ||
} | ||
function first(m) { | ||
assert.equal("Hello", m.data); | ||
es.onmessage = second; | ||
} | ||
function second(m) { | ||
assert.equal("World", m.data); | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
function second(m) { | ||
assert.equal("World", m.data); | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
}); | ||
it('causes entire event to be ignored for empty data fields', function(done) { | ||
createServer(["data:\n\ndata: Hello\n\n"], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
var originalEmit = es.emit; | ||
es.emit = function(event) { | ||
assert.ok(event === 'message' || event === 'newListener'); | ||
return originalEmit.apply(this, arguments); | ||
} | ||
es.onmessage = function(m) { | ||
assert.equal('Hello', m.data); | ||
es.close(); | ||
close(done); | ||
}; | ||
}); | ||
it('causes entire event to be ignored for empty data fields', function (done) { | ||
createServer(["data:\n\ndata: Hello\n\n"], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
var originalEmit = es.emit; | ||
es.emit = function (event) { | ||
assert.ok(event === 'message' || event === 'newListener'); | ||
return originalEmit.apply(this, arguments); | ||
}; | ||
es.onmessage = function (m) { | ||
assert.equal('Hello', m.data); | ||
es.close(); | ||
close(done); | ||
}; | ||
}); | ||
}); | ||
it('causes entire event to be ignored for empty event field', function(done) { | ||
createServer(["event:\n\ndata: Hello\n\n"], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
var originalEmit = es.emit; | ||
es.emit = function(event) { | ||
assert.ok(event === 'message' || event === 'newListener'); | ||
return originalEmit.apply(this, arguments); | ||
} | ||
es.onmessage = function(m) { | ||
assert.equal('Hello', m.data); | ||
es.close(); | ||
close(done); | ||
}; | ||
}); | ||
it('causes entire event to be ignored for empty event field', function (done) { | ||
createServer(["event:\n\ndata: Hello\n\n"], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
var originalEmit = es.emit; | ||
es.emit = function (event) { | ||
assert.ok(event === 'message' || event === 'newListener'); | ||
return originalEmit.apply(this, arguments); | ||
}; | ||
es.onmessage = function (m) { | ||
assert.equal('Hello', m.data); | ||
es.close(); | ||
close(done); | ||
}; | ||
}); | ||
}); | ||
var longMessage = "data: " + new Array(100000).join('a') + "\n\n"; | ||
it('parses relatively huge messages efficiently', function (done) { | ||
this.timeout(1000); | ||
createServer([longMessage], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = function () { | ||
es.close(); | ||
close(done); | ||
}; | ||
}); | ||
}); | ||
}); | ||
describe('HTTP Request', function() { | ||
it('passes cache-control: no-cache to server', function(done) { | ||
var headers; | ||
createServer([], function(port, close) { | ||
var url = 'http://localhost:' + port; | ||
var es = new EventSource(url); | ||
es.onopen = function() { | ||
assert.equal('no-cache', headers['cache-control']); | ||
es.close(); | ||
close(done); | ||
}; | ||
}, function(req) { headers = req.headers; }); | ||
describe('HTTP Request', function () { | ||
it('passes cache-control: no-cache to server', function (done) { | ||
var headers; | ||
createServer([], function (port, close) { | ||
var url = 'http://localhost:' + port; | ||
var es = new EventSource(url); | ||
es.onopen = function () { | ||
assert.equal('no-cache', headers['cache-control']); | ||
es.close(); | ||
close(done); | ||
}; | ||
}, function (req) { | ||
headers = req.headers; | ||
}); | ||
}); | ||
it('sets headers by user', function(done) { | ||
var headers = { | ||
'User-Agent': 'test', | ||
'Cookie': 'test=test', | ||
'Last-Event-ID': '99' | ||
it('sets headers by user', function (done) { | ||
var headers = { | ||
'User-Agent': 'test', | ||
'Cookie': 'test=test', | ||
'Last-Event-ID': '99' | ||
}; | ||
createServer([], | ||
function (port, close) { | ||
var url = 'http://localhost:' + port; | ||
var es = new EventSource(url, {headers: headers}); | ||
es.onopen = function () { | ||
es.close(); | ||
close(done); | ||
}; | ||
createServer([], | ||
function(port, close) { | ||
var url = 'http://localhost:' + port; | ||
var es = new EventSource(url, {headers: headers}); | ||
es.onopen = function() { | ||
es.close(); | ||
close(done); | ||
}; | ||
}, | ||
function(req, res) { | ||
assert.equal(req.headers['user-agent'], headers['User-Agent']); | ||
assert.equal(req.headers['cookie'], headers['Cookie']); | ||
assert.equal(req.headers['last-event-id'], '99'); // Shouldn't be sent | ||
res.writeHead(200); | ||
res.end(); | ||
return true; | ||
}); | ||
}); | ||
}, | ||
function (req, res) { | ||
assert.equal(req.headers['user-agent'], headers['User-Agent']); | ||
assert.equal(req.headers['cookie'], headers['Cookie']); | ||
assert.equal(req.headers['last-event-id'], '99'); // Shouldn't be sent | ||
res.writeHead(200); | ||
res.end(); | ||
return true; | ||
}); | ||
}); | ||
it('follows http 301 redirect', function(done) { | ||
var url = 'http://localhost:' + _port; | ||
var redirectSuffix = '/foobar'; | ||
var clientRequestedRedirectUrl = false; | ||
createServer([], | ||
function(port, close) { | ||
var es = new EventSource(url); | ||
es.onopen = function() { | ||
assert.ok(clientRequestedRedirectUrl); | ||
assert.equal(url + redirectSuffix, es.url); | ||
es.close(); | ||
close(done); | ||
}; | ||
}, | ||
function(req, res) { | ||
if (req.url != '/') { | ||
clientRequestedRedirectUrl = req.url == redirectSuffix; | ||
return false; | ||
} | ||
res.writeHead(301, 'Moved Permanently', { | ||
'Connection': 'Close', | ||
'Location': url + redirectSuffix | ||
}); | ||
res.end(); | ||
return true; | ||
}); | ||
}); | ||
it('follows http 301 redirect', function (done) { | ||
var url = 'http://localhost:' + _port; | ||
var redirectSuffix = '/foobar'; | ||
var clientRequestedRedirectUrl = false; | ||
createServer([], | ||
function (port, close) { | ||
var es = new EventSource(url); | ||
es.onopen = function () { | ||
assert.ok(clientRequestedRedirectUrl); | ||
assert.equal(url + redirectSuffix, es.url); | ||
es.close(); | ||
close(done); | ||
}; | ||
}, | ||
function (req, res) { | ||
if (req.url != '/') { | ||
clientRequestedRedirectUrl = req.url == redirectSuffix; | ||
return false; | ||
} | ||
res.writeHead(301, 'Moved Permanently', { | ||
'Connection': 'Close', | ||
'Location': url + redirectSuffix | ||
}); | ||
res.end(); | ||
return true; | ||
}); | ||
}); | ||
it('causes error event when response is 403', function(done) { | ||
createServer(["id: 1\ndata: hello world\n\n"], | ||
function(port, close) { | ||
var url = 'http://localhost:' + port; | ||
var es = new EventSource(url); | ||
es.onerror = function() { | ||
assert.ok(true, 'got error'); | ||
es.close(); | ||
close(done); | ||
}; | ||
}, | ||
function(req, res) { | ||
res.writeHead(403, {'Content-Type': 'text/html'}); | ||
res.end(); | ||
}); | ||
}); | ||
it('causes error event when response is 403', function (done) { | ||
createServer(["id: 1\ndata: hello world\n\n"], | ||
function (port, close) { | ||
var url = 'http://localhost:' + port; | ||
var es = new EventSource(url); | ||
es.onerror = function () { | ||
assert.ok(true, 'got error'); | ||
es.close(); | ||
close(done); | ||
}; | ||
}, | ||
function (req, res) { | ||
res.writeHead(403, {'Content-Type': 'text/html'}); | ||
res.end(); | ||
}); | ||
}); | ||
it('causes error event when response is 301 with missing location', function(done) { | ||
createServer([], | ||
function(port, close) { | ||
var url = 'http://localhost:' + port; | ||
var es = new EventSource(url); | ||
es.onerror = function() { | ||
es.close(); | ||
close(done); | ||
}; | ||
}, | ||
function(req, res) { | ||
if (req.url != '/') return false; | ||
res.writeHead(301, 'Moved Permanently', { | ||
'Connection': 'Close' | ||
}); | ||
res.end(); | ||
return true; | ||
}); | ||
}); | ||
it('causes error event when response is 301 with missing location', function (done) { | ||
createServer([], | ||
function (port, close) { | ||
var url = 'http://localhost:' + port; | ||
var es = new EventSource(url); | ||
es.onerror = function () { | ||
es.close(); | ||
close(done); | ||
}; | ||
}, | ||
function (req, res) { | ||
if (req.url != '/') return false; | ||
res.writeHead(301, 'Moved Permanently', { | ||
'Connection': 'Close' | ||
}); | ||
res.end(); | ||
return true; | ||
}); | ||
}); | ||
it('follows http 307 redirect', function(done) { | ||
var url = 'http://localhost:' + _port; | ||
var redirectSuffix = '/foobar'; | ||
var clientRequestedRedirectUrl = false; | ||
createServer([], | ||
function(port, close) { | ||
var es = new EventSource(url); | ||
es.onopen = function() { | ||
assert.ok(clientRequestedRedirectUrl); | ||
assert.equal(url + redirectSuffix, es.url); | ||
es.close(); | ||
close(done); | ||
}; | ||
}, | ||
function(req, res) { | ||
if (req.url != '/') { | ||
clientRequestedRedirectUrl = req.url == redirectSuffix; | ||
return false; | ||
} | ||
res.writeHead(307, 'Temporary Redirect', { | ||
'Connection': 'Close', | ||
'Location': url + redirectSuffix | ||
}); | ||
res.end(); | ||
return true; | ||
}); | ||
}); | ||
it('follows http 307 redirect', function (done) { | ||
var url = 'http://localhost:' + _port; | ||
var redirectSuffix = '/foobar'; | ||
var clientRequestedRedirectUrl = false; | ||
createServer([], | ||
function (port, close) { | ||
var es = new EventSource(url); | ||
es.onopen = function () { | ||
assert.ok(clientRequestedRedirectUrl); | ||
assert.equal(url + redirectSuffix, es.url); | ||
es.close(); | ||
close(done); | ||
}; | ||
}, | ||
function (req, res) { | ||
if (req.url != '/') { | ||
clientRequestedRedirectUrl = req.url == redirectSuffix; | ||
return false; | ||
} | ||
res.writeHead(307, 'Temporary Redirect', { | ||
'Connection': 'Close', | ||
'Location': url + redirectSuffix | ||
}); | ||
res.end(); | ||
return true; | ||
}); | ||
}); | ||
it('causes error event for 307 response with with missing location', function(done) { | ||
createServer([], | ||
function(port, close) { | ||
var url = 'http://localhost:' + port; | ||
var es = new EventSource(url); | ||
es.onerror = function() { | ||
es.close(); | ||
close(done); | ||
}; | ||
}, | ||
function(req, res) { | ||
if (req.url != '/') return false; | ||
res.writeHead(307, 'Temporary Redirect', { | ||
'Connection': 'Close' | ||
}); | ||
res.end(); | ||
return true; | ||
}); | ||
}); | ||
it('causes error event for 307 response with with missing location', function (done) { | ||
createServer([], | ||
function (port, close) { | ||
var url = 'http://localhost:' + port; | ||
var es = new EventSource(url); | ||
es.onerror = function () { | ||
es.close(); | ||
close(done); | ||
}; | ||
}, | ||
function (req, res) { | ||
if (req.url != '/') return false; | ||
res.writeHead(307, 'Temporary Redirect', { | ||
'Connection': 'Close' | ||
}); | ||
res.end(); | ||
return true; | ||
}); | ||
}); | ||
}); | ||
describe('HTTPS Support', function() { | ||
it('uses https for https urls', function(done) { | ||
var chopped = "data: Aslak\n\ndata: Hellesøy\n\n".split(""); | ||
createServer(chopped, function(port, close) { | ||
var es = new EventSource('https://localhost:' + port, {rejectUnauthorized: false}); | ||
es.onerror = function(err) { | ||
if(!err) err = new Error("Didn't expect error here"); | ||
done(err); | ||
}; | ||
es.onmessage = first; | ||
describe('HTTPS Support', function () { | ||
it('uses https for https urls', function (done) { | ||
var chopped = "data: Aslak\n\ndata: Hellesøy\n\n".split(""); | ||
createServer(chopped, function (port, close) { | ||
var es = new EventSource('https://localhost:' + port, {rejectUnauthorized: false}); | ||
function first(m) { | ||
assert.equal("Aslak", m.data); | ||
es.onmessage = second; | ||
} | ||
es.onerror = function (err) { | ||
if (!err) err = new Error("Didn't expect error here"); | ||
done(err); | ||
}; | ||
es.onmessage = first; | ||
function second(m) { | ||
assert.equal("Hellesøy", m.data); | ||
es.close(); | ||
close(done); | ||
} | ||
}, true); | ||
}); | ||
function first(m) { | ||
assert.equal("Aslak", m.data); | ||
es.onmessage = second; | ||
} | ||
if(!process.version.match(/^v0\.[12345678]\./)) { | ||
// This functionality relies on Node 0.10.x | ||
it('rejects unauthorized https by default', function(done) { | ||
var chopped = "data: Aslak\n\ndata: Hellesøy\n\n".split(""); | ||
createServer(chopped, function(port, close) { | ||
var es = new EventSource('https://localhost:' + port); | ||
es.onerror = function(err) { | ||
// Expected | ||
close(done); | ||
}; | ||
es.onmessage = function(err) { | ||
close(function() { | ||
done(new Error("Didn't expect any messages")); | ||
}); | ||
}; | ||
}, true); | ||
}); | ||
} | ||
}); | ||
function second(m) { | ||
assert.equal("Hellesøy", m.data); | ||
es.close(); | ||
close(done); | ||
} | ||
}, true); | ||
}); | ||
describe('Reconnection', function() { | ||
it('is attempted when server is down', function(done) { | ||
var es = new EventSource('http://localhost:' + _port); | ||
es.reconnectInterval = 0; | ||
var theClose = null; | ||
if (!process.version.match(/^v0\.[12345678]\./)) { | ||
// This functionality relies on Node 0.10.x | ||
it('rejects unauthorized https by default', function (done) { | ||
var chopped = "data: Aslak\n\ndata: Hellesøy\n\n".split(""); | ||
createServer(chopped, function (port, close) { | ||
var es = new EventSource('https://localhost:' + port); | ||
es.onerror = function(source) { | ||
es.onerror = null; | ||
createServer(["data: Hello\n\n"], function(port, close) { | ||
theClose = close; | ||
}); | ||
es.onerror = function (err) { | ||
// Expected | ||
close(done); | ||
}; | ||
es.onmessage = function(m) { | ||
assert.equal("Hello", m.data); | ||
es.close(); | ||
theClose(done); | ||
es.onmessage = function (err) { | ||
close(function () { | ||
done(new Error("Didn't expect any messages")); | ||
}); | ||
}; | ||
}, true); | ||
}); | ||
} | ||
}); | ||
it('is attempted when server goes down after connection', function(done) { | ||
createServer(["data: Hello\n\n"], function(port, closeFirstServer) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.reconnectInterval = 0; | ||
describe('Reconnection', function () { | ||
it('is attempted when server is down', function (done) { | ||
var es = new EventSource('http://localhost:' + _port); | ||
es.reconnectInterval = 0; | ||
var theClose = null; | ||
es.onmessage = function(m) { | ||
assert.equal("Hello", m.data); | ||
closeFirstServer(function() { | ||
_port = port; | ||
createServer(["data: World\n\n"], function(port, closeSecondServer) { | ||
es.onmessage = second; | ||
es.onerror = function () { | ||
es.onerror = null; | ||
createServer(["data: Hello\n\n"], function (port, close) { | ||
theClose = close; | ||
}); | ||
}; | ||
function second(m) { | ||
assert.equal("World", m.data); | ||
es.close(); | ||
closeSecondServer(done); | ||
} | ||
}); | ||
}); | ||
}; | ||
es.onmessage = function (m) { | ||
assert.equal("Hello", m.data); | ||
es.close(); | ||
theClose(done); | ||
}; | ||
}); | ||
it('is attempted when server goes down after connection', function (done) { | ||
createServer(["data: Hello\n\n"], function (port, closeFirstServer) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.reconnectInterval = 0; | ||
es.onmessage = function (m) { | ||
assert.equal("Hello", m.data); | ||
closeFirstServer(function () { | ||
_port = port; | ||
createServer(["data: World\n\n"], function (port, closeSecondServer) { | ||
es.onmessage = second; | ||
function second(m) { | ||
assert.equal("World", m.data); | ||
es.close(); | ||
closeSecondServer(done); | ||
} | ||
}); | ||
}); | ||
}; | ||
}); | ||
}); | ||
it('is stopped when server goes down and eventsource is being closed', function(done) { | ||
createServer(["data: Hello\n\n"], function(port, closeFirstServer) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.reconnectInterval = 0; | ||
it('is stopped when server goes down and eventsource is being closed', function (done) { | ||
createServer(["data: Hello\n\n"], function (port, closeFirstServer) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.reconnectInterval = 0; | ||
es.onmessage = function(m) { | ||
assert.equal("Hello", m.data); | ||
closeFirstServer(function() { | ||
// The server has closed down. es.onerror should now get called, | ||
// because es's remote connection was dropped. | ||
}); | ||
}; | ||
es.onmessage = function (m) { | ||
assert.equal("Hello", m.data); | ||
closeFirstServer(function () { | ||
// The server has closed down. es.onerror should now get called, | ||
// because es's remote connection was dropped. | ||
}); | ||
}; | ||
es.onerror = function(source) { | ||
// We received an error because the remote connection was closed. | ||
// We close es, so we do not want es to reconnect. | ||
es.close(); | ||
es.onerror = function () { | ||
_port = port; | ||
createServer(["data: World\n\n"], function(port, closeSecondServer) { | ||
es.onmessage = second; | ||
// We received an error because the remote connection was closed. | ||
// We close es, so we do not want es to reconnect. | ||
es.close(); | ||
function second(m) { | ||
// We received a message even though we closed es. | ||
assert.ok(false); | ||
} | ||
_port = port; | ||
createServer(["data: World\n\n"], function (port, closeSecondServer) { | ||
es.onmessage = second; | ||
setTimeout(function() { | ||
// We have not received any message within 100ms, we can | ||
// presume this works correctly. | ||
closeSecondServer(done); | ||
},100); | ||
}); | ||
}; | ||
function second() { | ||
// We received a message even though we closed es. | ||
assert.ok(false); | ||
} | ||
setTimeout(function () { | ||
// We have not received any message within 100ms, we can | ||
// presume this works correctly. | ||
closeSecondServer(done); | ||
}, 100); | ||
}); | ||
}; | ||
}); | ||
}); | ||
it('is not attempted when server responds with HTTP 204', function(done) { | ||
createServer(["data: Hello\n\n"], function(port, closeFirstServer) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.reconnectInterval = 0; | ||
it('is not attempted when server responds with HTTP 204', function (done) { | ||
createServer(["data: Hello\n\n"], function (port, closeFirstServer) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.reconnectInterval = 0; | ||
es.onmessage = function(m) { | ||
assert.equal("Hello", m.data); | ||
closeFirstServer(function() { | ||
_port = port; | ||
createServer([], function(port, closeSecondServer) { | ||
// this will be verified by the readyState | ||
// going from CONNECTING to CLOSED, | ||
// along with the tests verifying that the | ||
// state is CONNECTING when a server closes. | ||
// it's next to impossible to write a fail-safe | ||
// test for this, though. | ||
var ival = setInterval(function() { | ||
if (es.readyState == EventSource.CLOSED) { | ||
clearInterval(ival); | ||
closeSecondServer(done); | ||
} | ||
}, 5); | ||
}, function(req, res) { res.writeHead(204); res.end(); return true; }); | ||
}); | ||
}; | ||
es.onmessage = function (m) { | ||
assert.equal("Hello", m.data); | ||
closeFirstServer(function () { | ||
_port = port; | ||
createServer([], function (port, closeSecondServer) { | ||
// this will be verified by the readyState | ||
// going from CONNECTING to CLOSED, | ||
// along with the tests verifying that the | ||
// state is CONNECTING when a server closes. | ||
// it's next to impossible to write a fail-safe | ||
// test for this, though. | ||
var ival = setInterval(function () { | ||
if (es.readyState == EventSource.CLOSED) { | ||
clearInterval(ival); | ||
closeSecondServer(done); | ||
} | ||
}, 5); | ||
}, function (req, res) { | ||
res.writeHead(204); | ||
res.end(); | ||
return true; | ||
}); | ||
}); | ||
}; | ||
}); | ||
}); | ||
it('sends Last-Event-ID http header when it has previously been passed in an event from the server', function(done) { | ||
createServer(['id: 10\ndata: Hello\n\n'], function(port, closeFirstServer) { | ||
var headers = null; | ||
var es = new EventSource('http://localhost:' + port); | ||
es.reconnectInterval = 0; | ||
it('sends Last-Event-ID http header when it has previously been passed in an event from the server', function (done) { | ||
createServer(['id: 10\ndata: Hello\n\n'], function (port, closeFirstServer) { | ||
var headers = null; | ||
var es = new EventSource('http://localhost:' + port); | ||
es.reconnectInterval = 0; | ||
es.onmessage = function(m) { | ||
closeFirstServer(function() { | ||
_port = port; | ||
createServer([], function(port, close) { | ||
es.onopen = function() { | ||
assert.equal('10', headers['last-event-id']); | ||
es.close(); | ||
close(done); | ||
}; | ||
}, function(req) { headers = req.headers; }); | ||
}); | ||
es.onmessage = function () { | ||
closeFirstServer(function () { | ||
_port = port; | ||
createServer([], function (port, close) { | ||
es.onopen = function () { | ||
assert.equal('10', headers['last-event-id']); | ||
es.close(); | ||
close(done); | ||
}; | ||
}, function (req) { | ||
headers = req.headers; | ||
}); | ||
}); | ||
}; | ||
}); | ||
}); | ||
it('sends correct Last-Event-ID http header when an initial Last-Event-ID header was specified in the constructor', function(done) { | ||
createServer(['id: 10\ndata: Hello\n\n'], function(port, closeFirstServer) { | ||
var headers = null; | ||
var es = new EventSource('http://localhost:' + port, {headers: {'Last-Event-ID': '9'}}); | ||
es.reconnectInterval = 0; | ||
it('sends correct Last-Event-ID http header when an initial Last-Event-ID header was specified in the constructor', function (done) { | ||
createServer(['id: 10\ndata: Hello\n\n'], function (port, closeFirstServer) { | ||
var headers = null; | ||
var es = new EventSource('http://localhost:' + port, {headers: {'Last-Event-ID': '9'}}); | ||
es.reconnectInterval = 0; | ||
es.onmessage = function(m) { | ||
closeFirstServer(function() { | ||
_port = port; | ||
createServer([], function(port, close) { | ||
es.onopen = function() { | ||
assert.equal('10', headers['last-event-id']); | ||
es.close(); | ||
close(done); | ||
}; | ||
}, function(req) { headers = req.headers; }); | ||
}); | ||
es.onmessage = function () { | ||
closeFirstServer(function () { | ||
_port = port; | ||
createServer([], function (port, close) { | ||
es.onopen = function () { | ||
assert.equal('10', headers['last-event-id']); | ||
es.close(); | ||
close(done); | ||
}; | ||
}, function (req) { | ||
headers = req.headers; | ||
}); | ||
}); | ||
}; | ||
}); | ||
}); | ||
it('does not send Last-Event-ID http header when it has not been previously sent by the server', function(done) { | ||
createServer(['data: Hello\n\n'], function(port, closeFirstServer) { | ||
var headers = null; | ||
var es = new EventSource('http://localhost:' + port); | ||
es.reconnectInterval = 0; | ||
it('does not send Last-Event-ID http header when it has not been previously sent by the server', function (done) { | ||
createServer(['data: Hello\n\n'], function (port, closeFirstServer) { | ||
var headers = null; | ||
var es = new EventSource('http://localhost:' + port); | ||
es.reconnectInterval = 0; | ||
es.onmessage = function(m) { | ||
closeFirstServer(function() { | ||
_port = port; | ||
createServer([], function(port, close) { | ||
es.onopen = function() { | ||
assert.equal('undefined', typeof headers['last-event-id']); | ||
es.close(); | ||
close(done); | ||
}; | ||
}, function(req) { headers = req.headers; }); | ||
}); | ||
es.onmessage = function () { | ||
closeFirstServer(function () { | ||
_port = port; | ||
createServer([], function (port, close) { | ||
es.onopen = function () { | ||
assert.equal('undefined', typeof headers['last-event-id']); | ||
es.close(); | ||
close(done); | ||
}; | ||
}, function (req) { | ||
headers = req.headers; | ||
}); | ||
}); | ||
}; | ||
}); | ||
}); | ||
it('is attempted after http 301 redirect uses new url', function(done) { | ||
var headers; | ||
var url = 'http://localhost:' + _port; | ||
var redirectSuffix = '/foobar'; | ||
createServer(['data: Hello\n\n'], | ||
function(port, closeFirstServer) { | ||
var es = new EventSource(url); | ||
es.reconnectInterval = 0; | ||
it('is attempted after http 301 redirect uses new url', function (done) { | ||
var url = 'http://localhost:' + _port; | ||
var redirectSuffix = '/foobar'; | ||
createServer(['data: Hello\n\n'], | ||
function (port, closeFirstServer) { | ||
var es = new EventSource(url); | ||
es.reconnectInterval = 0; | ||
es.onmessage = function(m) { | ||
closeFirstServer(function() { | ||
_port = port; | ||
createServer([], function(port, closeSecondServer) { | ||
es.onopen = function() { | ||
assert.equal(url + redirectSuffix, es.url); | ||
es.close(); | ||
closeSecondServer(done); | ||
}; | ||
}, function(req, res) { assert.equal(redirectSuffix, req.url); }); | ||
}); | ||
}; | ||
}, | ||
function(req, res) { | ||
if (req.url != '/') return false; | ||
res.writeHead(301, 'Moved Permanently', { | ||
'Connection': 'Close', | ||
'Location': url + redirectSuffix | ||
}); | ||
res.end(); | ||
return true; | ||
es.onmessage = function () { | ||
closeFirstServer(function () { | ||
_port = port; | ||
createServer([], function (port, closeSecondServer) { | ||
es.onopen = function () { | ||
assert.equal(url + redirectSuffix, es.url); | ||
es.close(); | ||
closeSecondServer(done); | ||
}; | ||
}, function (req) { | ||
assert.equal(redirectSuffix, req.url); | ||
}); | ||
}); | ||
}); | ||
}; | ||
}, | ||
function (req, res) { | ||
if (req.url != '/') return false; | ||
res.writeHead(301, 'Moved Permanently', { | ||
'Connection': 'Close', | ||
'Location': url + redirectSuffix | ||
}); | ||
res.end(); | ||
return true; | ||
}); | ||
}); | ||
it('is attempted after http 307 redirect uses original url', function(done) { | ||
var headers; | ||
var url = 'http://localhost:' + _port; | ||
var redirectSuffix = '/foobar'; | ||
createServer(['data: Hello\n\n'], | ||
function(port, closeFirstServer) { | ||
var es = new EventSource(url); | ||
es.reconnectInterval = 0; | ||
it('is attempted after http 307 redirect uses original url', function (done) { | ||
var url = 'http://localhost:' + _port; | ||
var redirectSuffix = '/foobar'; | ||
createServer(['data: Hello\n\n'], | ||
function (port, closeFirstServer) { | ||
var es = new EventSource(url); | ||
es.reconnectInterval = 0; | ||
es.onmessage = function(m) { | ||
closeFirstServer(function() { | ||
--_port; | ||
createServer([], function(port, closeSecondServer) { | ||
es.onopen = function() { | ||
assert.equal(url, es.url); | ||
es.close(); | ||
closeSecondServer(done); | ||
}; | ||
}, function(req, res) { assert.equal('/', req.url); }); | ||
}); | ||
}; | ||
}, | ||
function(req, res) { | ||
if (req.url != '/') return false; | ||
res.writeHead(307, 'Temporary Redirect', { | ||
'Connection': 'Close', | ||
'Location': url + redirectSuffix | ||
}); | ||
res.end(); | ||
return true; | ||
es.onmessage = function (m) { | ||
closeFirstServer(function () { | ||
--_port; | ||
createServer([], function (port, closeSecondServer) { | ||
es.onopen = function () { | ||
assert.equal(url, es.url); | ||
es.close(); | ||
closeSecondServer(done); | ||
}; | ||
}, function (req) { | ||
assert.equal('/', req.url); | ||
}); | ||
}); | ||
}); | ||
}; | ||
}, | ||
function (req, res) { | ||
if (req.url != '/') return false; | ||
res.writeHead(307, 'Temporary Redirect', { | ||
'Connection': 'Close', | ||
'Location': url + redirectSuffix | ||
}); | ||
res.end(); | ||
return true; | ||
}); | ||
}); | ||
}); | ||
describe('readyState', function() { | ||
it('has CONNECTING constant', function(done) { | ||
assert.equal(0, EventSource.CONNECTING); | ||
done(); | ||
}); | ||
describe('readyState', function () { | ||
it('has CONNECTING constant', function (done) { | ||
assert.equal(0, EventSource.CONNECTING); | ||
done(); | ||
}); | ||
it('has OPEN constant', function(done) { | ||
assert.equal(1, EventSource.OPEN); | ||
done(); | ||
}); | ||
it('has OPEN constant', function (done) { | ||
assert.equal(1, EventSource.OPEN); | ||
done(); | ||
}); | ||
it('has CLOSED constant', function(done) { | ||
assert.equal(2, EventSource.CLOSED); | ||
done(); | ||
}); | ||
it('has CLOSED constant', function (done) { | ||
assert.equal(2, EventSource.CLOSED); | ||
done(); | ||
}); | ||
it('is CONNECTING before connection has been established', function(done) { | ||
createServer([], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
assert.equal(EventSource.CONNECTING, es.readyState); | ||
es.onopen = function() { | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
it('is CONNECTING before connection has been established', function (done) { | ||
createServer([], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
assert.equal(EventSource.CONNECTING, es.readyState); | ||
es.onopen = function () { | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
}); | ||
it('is CONNECTING when server has closed the connection', function(done) { | ||
createServer(["data: Hello\n\n"], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.reconnectInterval = 0; | ||
it('is CONNECTING when server has closed the connection', function (done) { | ||
createServer(["data: Hello\n\n"], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.reconnectInterval = 0; | ||
es.onmessage = function(m) { | ||
assert.equal("Hello", m.data); | ||
close(function() { | ||
setTimeout(function(){ | ||
assert.equal(EventSource.CONNECTING, es.readyState); | ||
done(); | ||
}, 0); | ||
}); | ||
}; | ||
es.onmessage = function (m) { | ||
assert.equal("Hello", m.data); | ||
close(function () { | ||
setTimeout(function () { | ||
assert.equal(EventSource.CONNECTING, es.readyState); | ||
done(); | ||
}, 0); | ||
}); | ||
}; | ||
}); | ||
}); | ||
it('is OPEN when connection has been established', function(done) { | ||
createServer([], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onopen = function() { | ||
assert.equal(EventSource.OPEN, es.readyState); | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
it('is OPEN when connection has been established', function (done) { | ||
createServer([], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onopen = function () { | ||
assert.equal(EventSource.OPEN, es.readyState); | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
}); | ||
it('is CLOSED after connection has been closed', function(done) { | ||
createServer([], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onopen = function() { | ||
es.close(); | ||
assert.equal(EventSource.CLOSED, es.readyState); | ||
close(done); | ||
}; | ||
}); | ||
it('is CLOSED after connection has been closed', function (done) { | ||
createServer([], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onopen = function () { | ||
es.close(); | ||
assert.equal(EventSource.CLOSED, es.readyState); | ||
close(done); | ||
}; | ||
}); | ||
}); | ||
}); | ||
describe('Properties', function() { | ||
it('url exposes original request url', function(done) { | ||
createServer([], function(port, close) { | ||
var url = 'http://localhost:' + port; | ||
var es = new EventSource(url); | ||
es.onopen = function() { | ||
assert.equal(url, es.url); | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
describe('Properties', function () { | ||
it('url exposes original request url', function (done) { | ||
createServer([], function (port, close) { | ||
var url = 'http://localhost:' + port; | ||
var es = new EventSource(url); | ||
es.onopen = function () { | ||
assert.equal(url, es.url); | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
}); | ||
}); | ||
describe('Events', function() { | ||
it('calls onopen when connection is established', function(done) { | ||
createServer([], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onopen = function(event) { | ||
assert.equal(event.type, 'open'); | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
describe('Events', function () { | ||
it('calls onopen when connection is established', function (done) { | ||
createServer([], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onopen = function (event) { | ||
assert.equal(event.type, 'open'); | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
}); | ||
it('emits open event when connection is established', function(done) { | ||
createServer([], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.addEventListener('open', function(event) { | ||
assert.equal(event.type, 'open'); | ||
es.close(); | ||
close(done); | ||
}); | ||
}); | ||
it('emits open event when connection is established', function (done) { | ||
createServer([], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.addEventListener('open', function (event) { | ||
assert.equal(event.type, 'open'); | ||
es.close(); | ||
close(done); | ||
}); | ||
}); | ||
}); | ||
it('does not emit error when connection is closed by client', function(done) { | ||
createServer([], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.addEventListener('open', function() { | ||
es.close(); | ||
setTimeout(function() { | ||
close(done); | ||
}, 50); | ||
}); | ||
es.addEventListener('error', function() { | ||
throw new Error('error should not be emitted'); | ||
}); | ||
}); | ||
it('does not emit error when connection is closed by client', function (done) { | ||
createServer([], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.addEventListener('open', function () { | ||
es.close(); | ||
setTimeout(function () { | ||
close(done); | ||
}, 50); | ||
}); | ||
es.addEventListener('error', function () { | ||
throw new Error('error should not be emitted'); | ||
}); | ||
}); | ||
}); | ||
it('populates message\'s lastEventId correctly when the last event has an associated id', function(done) { | ||
createServer(["id: 123\ndata: sample_data\n\n"], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = function(m) { | ||
assert.equal(m.lastEventId, "123"); | ||
es.close(); | ||
close(done); | ||
}; | ||
}); | ||
it('populates message\'s lastEventId correctly when the last event has an associated id', function (done) { | ||
createServer(["id: 123\ndata: sample_data\n\n"], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = function (m) { | ||
assert.equal(m.lastEventId, "123"); | ||
es.close(); | ||
close(done); | ||
}; | ||
}); | ||
it('populates message\'s lastEventId correctly when the last event doesn\'t have an associated id', function(done) { | ||
createServer(["id: 123\ndata: Hello\n\n", "data: World\n\n"], function(port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = first; | ||
}); | ||
function first(m) { | ||
es.onmessage = second; | ||
} | ||
it('populates message\'s lastEventId correctly when the last event doesn\'t have an associated id', function (done) { | ||
createServer(["id: 123\ndata: Hello\n\n", "data: World\n\n"], function (port, close) { | ||
var es = new EventSource('http://localhost:' + port); | ||
es.onmessage = first; | ||
function second(m) { | ||
assert.equal(m.data, "World"); | ||
assert.equal(m.lastEventId, "123"); //expect to get back the previous event id | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
}); | ||
function first() { | ||
es.onmessage = second; | ||
} | ||
function second(m) { | ||
assert.equal(m.data, "World"); | ||
assert.equal(m.lastEventId, "123"); //expect to get back the previous event id | ||
es.close(); | ||
close(done); | ||
} | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
1
1
44393
10
1058