eventsource
Advanced tools
Comparing version 0.2.3 to 1.0.0
var EventSource = require('..') | ||
var es = new EventSource('http://localhost:8080/sse'); | ||
var es = new EventSource('http://localhost:8080/sse') | ||
es.addEventListener('server-time', function (e) { | ||
console.log(e.data); | ||
}); | ||
console.log(e.data) | ||
}) |
@@ -1,2 +0,2 @@ | ||
var express = require('express'); | ||
var express = require('express') | ||
var serveStatic = require('serve-static') | ||
@@ -8,9 +8,10 @@ var SSE = require('sse') | ||
server = app.listen(8080, function (err) { | ||
if(err) throw err; | ||
console.log("server ready on http://localhost:8080") | ||
}); | ||
var server = app.listen(8080, function (err) { | ||
if (err) throw err | ||
console.log('server ready on http://localhost:8080') | ||
}) | ||
var sse = new SSE(server) | ||
sse.on('connection', function (connection) { | ||
console.log('new connection'); | ||
console.log('new connection') | ||
var pusher = setInterval(function () { | ||
@@ -21,8 +22,8 @@ connection.send({ | ||
}) | ||
}, 1000); | ||
}, 1000) | ||
connection.on('close', function () { | ||
console.log('lost connection'); | ||
clearInterval(pusher); | ||
}); | ||
console.log('lost connection') | ||
clearInterval(pusher) | ||
}) | ||
}) |
@@ -0,1 +1,8 @@ | ||
# [1.0.0](https://github.com/EventSource/eventsource/compare/v0.2.3...v1.0.0) | ||
* Add missing `removeEventListener`-method. ([#51](https://github.com/EventSource/eventsource/pull/51) Yucheng Tu / Espen Hovlandsdal) | ||
* Fix EventSource reconnecting on non-200 responses. ([af84476](https://github.com/EventSource/eventsource/commit/af84476b519a01e61b8c80727261df52ae40022c) Espen Hovlandsdal) | ||
* Add ability to customize https options. ([#53](https://github.com/EventSource/eventsource/pull/53) Rafael Alfaro) | ||
* Add readyState constants to EventSource instances. ([#66](https://github.com/EventSource/eventsource/pull/66) Espen Hovlandsdal) | ||
# [0.2.3](https://github.com/EventSource/eventsource/compare/v0.2.2...v0.2.3) | ||
@@ -2,0 +9,0 @@ |
@@ -1,2 +0,2 @@ | ||
window.EventSourcePolyfill = require('./eventsource'); | ||
window.EventSourcePolyfill = require('./eventsource') | ||
window.EventSource = window.EventSource || window.EventSourcePolyfill |
var original = require('original') | ||
, parse = require('url').parse | ||
, events = require('events') | ||
, https = require('https') | ||
, http = require('http') | ||
, util = require('util'); | ||
var parse = require('url').parse | ||
var events = require('events') | ||
var https = require('https') | ||
var http = require('http') | ||
var util = require('util') | ||
var httpsOptions = [ | ||
'pfx', 'key', 'passphrase', 'cert', 'ca', 'ciphers', | ||
'rejectUnauthorized', 'secureProtocol', 'servername' | ||
] | ||
/** | ||
@@ -15,23 +20,23 @@ * Creates a new EventSource object | ||
**/ | ||
function EventSource(url, eventSourceInitDict) { | ||
var readyState = EventSource.CONNECTING; | ||
function EventSource (url, eventSourceInitDict) { | ||
var readyState = EventSource.CONNECTING | ||
Object.defineProperty(this, 'readyState', { | ||
get: function () { | ||
return readyState; | ||
return readyState | ||
} | ||
}); | ||
}) | ||
Object.defineProperty(this, 'url', { | ||
get: function () { | ||
return url; | ||
return url | ||
} | ||
}); | ||
}) | ||
var self = this; | ||
self.reconnectInterval = 1000; | ||
var self = this | ||
self.reconnectInterval = 1000 | ||
function onConnectionClosed() { | ||
if (readyState === EventSource.CLOSED) return; | ||
readyState = EventSource.CONNECTING; | ||
_emit('error', new Event('error')); | ||
function onConnectionClosed () { | ||
if (readyState === EventSource.CLOSED) return | ||
readyState = EventSource.CONNECTING | ||
_emit('error', new Event('error')) | ||
@@ -41,37 +46,36 @@ // The url may have been changed by a temporary | ||
if (reconnectUrl) { | ||
url = reconnectUrl; | ||
reconnectUrl = null; | ||
url = reconnectUrl | ||
reconnectUrl = null | ||
} | ||
setTimeout(function () { | ||
if (readyState !== EventSource.CONNECTING) { | ||
return; | ||
return | ||
} | ||
connect(); | ||
}, self.reconnectInterval); | ||
connect() | ||
}, self.reconnectInterval) | ||
} | ||
var req; | ||
var lastEventId = ''; | ||
var req | ||
var lastEventId = '' | ||
if (eventSourceInitDict && eventSourceInitDict.headers && eventSourceInitDict.headers['Last-Event-ID']) { | ||
lastEventId = eventSourceInitDict.headers['Last-Event-ID']; | ||
delete eventSourceInitDict.headers['Last-Event-ID']; | ||
lastEventId = eventSourceInitDict.headers['Last-Event-ID'] | ||
delete eventSourceInitDict.headers['Last-Event-ID'] | ||
} | ||
var discardTrailingNewline = false | ||
, data = '' | ||
, eventName = ''; | ||
var data = '' | ||
var eventName = '' | ||
var reconnectUrl = null; | ||
var reconnectUrl = null | ||
function connect() { | ||
var options = 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; | ||
function connect () { | ||
var options = 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) { | ||
for (var i in eventSourceInitDict.headers) { | ||
var header = eventSourceInitDict.headers[i]; | ||
var header = eventSourceInitDict.headers[i] | ||
if (header) { | ||
options.headers[i] = header; | ||
options.headers[i] = header | ||
} | ||
@@ -81,3 +85,5 @@ } | ||
options.rejectUnauthorized = !(eventSourceInitDict && eventSourceInitDict.rejectUnauthorized == false); | ||
// Legacy: this should be specified as `eventSourceInitDict.https.rejectUnauthorized`, | ||
// but for now exists as a backwards-compatibility layer | ||
options.rejectUnauthorized = !(eventSourceInitDict && !eventSourceInitDict.rejectUnauthorized) | ||
@@ -87,75 +93,89 @@ // If specify http proxy, make the request to sent to the proxy server, | ||
if (eventSourceInitDict && eventSourceInitDict.proxy) { | ||
var proxy = parse(eventSourceInitDict.proxy); | ||
options.path = url; | ||
options.headers.Host = options.host; | ||
options.hostname = proxy.hostname; | ||
options.host = proxy.host; | ||
options.port = proxy.port; | ||
var proxy = parse(eventSourceInitDict.proxy) | ||
options.path = url | ||
options.headers.Host = options.host | ||
options.hostname = proxy.hostname | ||
options.host = proxy.host | ||
options.port = proxy.port | ||
} | ||
// If https options are specified, merge them into the request options | ||
if (eventSourceInitDict && eventSourceInitDict.https) { | ||
for (var optName in eventSourceInitDict.https) { | ||
if (httpsOptions.indexOf(optName) === -1) { | ||
continue | ||
} | ||
var option = eventSourceInitDict.https[optName] | ||
if (option !== undefined) { | ||
options[optName] = option | ||
} | ||
} | ||
} | ||
req = (isSecure ? https : http).request(options, function (res) { | ||
// Handle HTTP redirects | ||
if (res.statusCode == 301 || res.statusCode == 307) { | ||
if (res.statusCode === 301 || res.statusCode === 307) { | ||
if (!res.headers.location) { | ||
// Server sent redirect response without Location header. | ||
_emit('error', new Event('error', {status: res.statusCode})); | ||
return; | ||
_emit('error', new Event('error', {status: res.statusCode})) | ||
return | ||
} | ||
if (res.statusCode == 307) reconnectUrl = url; | ||
url = res.headers.location; | ||
process.nextTick(connect); | ||
return; | ||
if (res.statusCode === 307) reconnectUrl = url | ||
url = res.headers.location | ||
process.nextTick(connect) | ||
return | ||
} | ||
if (res.statusCode !== 200) { | ||
_emit('error', new Event('error', {status: res.statusCode})); | ||
if (res.statusCode == 204) return self.close(); | ||
return | ||
_emit('error', new Event('error', {status: res.statusCode})) | ||
return self.close() | ||
} | ||
readyState = EventSource.OPEN; | ||
res.on('close', function() { | ||
res.removeAllListeners('close'); | ||
res.removeAllListeners('end'); | ||
onConnectionClosed(); | ||
}); | ||
readyState = EventSource.OPEN | ||
res.on('close', function () { | ||
res.removeAllListeners('close') | ||
res.removeAllListeners('end') | ||
onConnectionClosed() | ||
}) | ||
res.on('end', function() { | ||
res.removeAllListeners('close'); | ||
res.removeAllListeners('end'); | ||
onConnectionClosed(); | ||
}); | ||
_emit('open', new Event('open')); | ||
res.on('end', function () { | ||
res.removeAllListeners('close') | ||
res.removeAllListeners('end') | ||
onConnectionClosed() | ||
}) | ||
_emit('open', new Event('open')) | ||
// text/event-stream parser adapted from webkit's | ||
// Source/WebCore/page/EventSource.cpp | ||
var buf = ''; | ||
var buf = '' | ||
res.on('data', function (chunk) { | ||
buf += chunk; | ||
buf += chunk | ||
var pos = 0 | ||
, length = buf.length; | ||
var length = buf.length | ||
while (pos < length) { | ||
if (discardTrailingNewline) { | ||
if (buf[pos] === '\n') { | ||
++pos; | ||
++pos | ||
} | ||
discardTrailingNewline = false; | ||
discardTrailingNewline = false | ||
} | ||
var lineLength = -1 | ||
, fieldLength = -1 | ||
, c; | ||
var fieldLength = -1 | ||
var c | ||
for (var i = pos; lineLength < 0 && i < length; ++i) { | ||
c = buf[i]; | ||
c = buf[i] | ||
if (c === ':') { | ||
if (fieldLength < 0) { | ||
fieldLength = i - pos; | ||
fieldLength = i - pos | ||
} | ||
} else if (c === '\r') { | ||
discardTrailingNewline = true; | ||
lineLength = i - pos; | ||
discardTrailingNewline = true | ||
lineLength = i - pos | ||
} else if (c === '\n') { | ||
lineLength = i - pos; | ||
lineLength = i - pos | ||
} | ||
@@ -165,28 +185,28 @@ } | ||
if (lineLength < 0) { | ||
break; | ||
break | ||
} | ||
parseEventStreamLine(buf, pos, fieldLength, lineLength); | ||
parseEventStreamLine(buf, pos, fieldLength, lineLength) | ||
pos += lineLength + 1; | ||
pos += lineLength + 1 | ||
} | ||
if (pos === length) { | ||
buf = ''; | ||
buf = '' | ||
} else if (pos > 0) { | ||
buf = buf.slice(pos); | ||
buf = buf.slice(pos) | ||
} | ||
}); | ||
}); | ||
}) | ||
}) | ||
req.on('error', onConnectionClosed); | ||
if (req.setNoDelay) req.setNoDelay(true); | ||
req.end(); | ||
req.on('error', onConnectionClosed) | ||
if (req.setNoDelay) req.setNoDelay(true) | ||
req.end() | ||
} | ||
connect(); | ||
connect() | ||
function _emit() { | ||
function _emit () { | ||
if (self.listeners(arguments[0]).length > 0) { | ||
self.emit.apply(self, arguments); | ||
self.emit.apply(self, arguments) | ||
} | ||
@@ -196,12 +216,12 @@ } | ||
this.close = function () { | ||
if (readyState == EventSource.CLOSED) return; | ||
readyState = EventSource.CLOSED; | ||
if (req.abort) req.abort(); | ||
if (req.xhr && req.xhr.abort) req.xhr.abort(); | ||
}; | ||
if (readyState === EventSource.CLOSED) return | ||
readyState = EventSource.CLOSED | ||
if (req.abort) req.abort() | ||
if (req.xhr && req.xhr.abort) req.xhr.abort() | ||
} | ||
function parseEventStreamLine(buf, pos, fieldLength, lineLength) { | ||
function parseEventStreamLine (buf, pos, fieldLength, lineLength) { | ||
if (lineLength === 0) { | ||
if (data.length > 0) { | ||
var type = eventName || 'message'; | ||
var type = eventName || 'message' | ||
_emit(type, new MessageEvent(type, { | ||
@@ -211,32 +231,33 @@ data: data.slice(0, -1), // remove trailing newline | ||
origin: original(url) | ||
})); | ||
data = ''; | ||
})) | ||
data = '' | ||
} | ||
eventName = void 0; | ||
eventName = void 0 | ||
} else if (fieldLength > 0) { | ||
var noValue = fieldLength < 0 | ||
, step = 0 | ||
, field = buf.slice(pos, pos + (noValue ? lineLength : fieldLength)); | ||
var step = 0 | ||
var field = buf.slice(pos, pos + (noValue ? lineLength : fieldLength)) | ||
if (noValue) { | ||
step = lineLength; | ||
step = lineLength | ||
} else if (buf[pos + fieldLength + 1] !== ' ') { | ||
step = fieldLength + 1; | ||
step = fieldLength + 1 | ||
} else { | ||
step = fieldLength + 2; | ||
step = fieldLength + 2 | ||
} | ||
pos += step; | ||
pos += step | ||
var valueLength = lineLength - step | ||
, value = buf.slice(pos, pos + valueLength); | ||
var value = buf.slice(pos, pos + valueLength) | ||
if (field === 'data') { | ||
data += value + '\n'; | ||
data += value + '\n' | ||
} else if (field === 'event') { | ||
eventName = value; | ||
eventName = value | ||
} else if (field === 'id') { | ||
lastEventId = value; | ||
lastEventId = value | ||
} else if (field === 'retry') { | ||
var retry = parseInt(value, 10); | ||
var retry = parseInt(value, 10) | ||
if (!Number.isNaN(retry)) { | ||
self.reconnectInterval = retry; | ||
self.reconnectInterval = retry | ||
} | ||
@@ -248,5 +269,5 @@ } | ||
module.exports = EventSource; | ||
module.exports = EventSource | ||
util.inherits(EventSource, events.EventEmitter); | ||
util.inherits(EventSource, events.EventEmitter) | ||
EventSource.prototype.constructor = EventSource; // make stacktraces readable | ||
@@ -262,5 +283,5 @@ | ||
*/ | ||
get: function get() { | ||
var listener = this.listeners(method)[0]; | ||
return listener ? (listener._listener ? listener._listener : listener) : undefined; | ||
get: function get () { | ||
var listener = this.listeners(method)[0] | ||
return listener ? (listener._listener ? listener._listener : listener) : undefined | ||
}, | ||
@@ -275,8 +296,8 @@ | ||
*/ | ||
set: function set(listener) { | ||
this.removeAllListeners(method); | ||
this.addEventListener(method, listener); | ||
set: function set (listener) { | ||
this.removeAllListeners(method) | ||
this.addEventListener(method, listener) | ||
} | ||
}); | ||
}); | ||
}) | ||
}) | ||
@@ -286,10 +307,14 @@ /** | ||
*/ | ||
Object.defineProperty(EventSource, 'CONNECTING', { enumerable: true, value: 0}); | ||
Object.defineProperty(EventSource, 'OPEN', { enumerable: true, value: 1}); | ||
Object.defineProperty(EventSource, 'CLOSED', { enumerable: true, value: 2}); | ||
Object.defineProperty(EventSource, 'CONNECTING', {enumerable: true, value: 0}) | ||
Object.defineProperty(EventSource, 'OPEN', {enumerable: true, value: 1}) | ||
Object.defineProperty(EventSource, 'CLOSED', {enumerable: true, value: 2}) | ||
EventSource.prototype.CONNECTING = 0 | ||
EventSource.prototype.OPEN = 1 | ||
EventSource.prototype.CLOSED = 2 | ||
/** | ||
* Emulates the W3C Browser based WebSocket interface using addEventListener. | ||
* | ||
* @param {String} method Listen for an event | ||
* @param {String} type A string representing the event type to listen out for | ||
* @param {Function} listener callback | ||
@@ -300,11 +325,27 @@ * @see https://developer.mozilla.org/en/DOM/element.addEventListener | ||
*/ | ||
EventSource.prototype.addEventListener = function addEventListener(method, listener) { | ||
EventSource.prototype.addEventListener = function addEventListener (type, listener) { | ||
if (typeof listener === 'function') { | ||
// store a reference so we can return the original function again | ||
listener._listener = listener; | ||
this.on(method, listener); | ||
listener._listener = listener | ||
this.on(type, listener) | ||
} | ||
}; | ||
} | ||
/** | ||
* Emulates the W3C Browser based WebSocket interface using removeEventListener. | ||
* | ||
* @param {String} type A string representing the event type to remove | ||
* @param {Function} listener callback | ||
* @see https://developer.mozilla.org/en/DOM/element.removeEventListener | ||
* @see http://dev.w3.org/html5/websockets/#the-websocket-interface | ||
* @api public | ||
*/ | ||
EventSource.prototype.removeEventListener = function removeEventListener (type, listener) { | ||
if (typeof listener === 'function') { | ||
listener._listener = undefined | ||
this.removeListener(type, listener) | ||
} | ||
} | ||
/** | ||
* W3C Event | ||
@@ -315,8 +356,8 @@ * | ||
*/ | ||
function Event(type, optionalProperties) { | ||
Object.defineProperty(this, 'type', { writable: false, value: type, enumerable: true }); | ||
function Event (type, optionalProperties) { | ||
Object.defineProperty(this, 'type', { writable: false, value: type, enumerable: true }) | ||
if (optionalProperties) { | ||
for (var f in optionalProperties) { | ||
if (optionalProperties.hasOwnProperty(f)) { | ||
Object.defineProperty(this, f, { writable: false, value: optionalProperties[f], enumerable: true }); | ||
Object.defineProperty(this, f, { writable: false, value: optionalProperties[f], enumerable: true }) | ||
} | ||
@@ -333,9 +374,9 @@ } | ||
*/ | ||
function MessageEvent(type, eventInitDict) { | ||
Object.defineProperty(this, 'type', { writable: false, value: type, enumerable: true }); | ||
function MessageEvent (type, eventInitDict) { | ||
Object.defineProperty(this, 'type', { writable: false, value: type, enumerable: true }) | ||
for (var f in eventInitDict) { | ||
if (eventInitDict.hasOwnProperty(f)) { | ||
Object.defineProperty(this, f, { writable: false, value: eventInitDict[f], enumerable: true }); | ||
Object.defineProperty(this, f, { writable: false, value: eventInitDict[f], enumerable: true }) | ||
} | ||
} | ||
} |
{ | ||
"name": "eventsource", | ||
"version": "0.2.3", | ||
"version": "1.0.0", | ||
"description": "W3C compliant EventSource client for Node.js and browser (polyfill)", | ||
@@ -35,17 +35,25 @@ "keywords": [ | ||
"mocha": "^3.2.0", | ||
"nyc": "^10.2.0", | ||
"serve-static": "^1.10.2", | ||
"sse": "^0.0.6", | ||
"standard": "^10.0.2", | ||
"webpack": "^2.4.1" | ||
}, | ||
"scripts": { | ||
"test": "mocha --reporter spec", | ||
"test": "mocha --reporter spec && standard", | ||
"polyfill": "webpack lib/eventsource-polyfill.js example/eventsource-polyfill.js", | ||
"postpublish": "git push && git push --tags" | ||
"postpublish": "git push && git push --tags", | ||
"coverage": "nyc --reporter=html --reporter=text _mocha --reporter spec" | ||
}, | ||
"engines": { | ||
"node": ">=0.8.0" | ||
"node": ">=0.12.0" | ||
}, | ||
"dependencies": { | ||
"original": "^1.0.0" | ||
}, | ||
"standard": { | ||
"ignore": [ | ||
"example/eventsource-polyfill.js" | ||
] | ||
} | ||
} |
@@ -18,3 +18,3 @@ # EventSource [![Build Status](https://secure.travis-ci.org/EventSource/eventsource.svg)](http://travis-ci.org/EventSource/eventsource) [![NPM Downloads](https://img.shields.io/npm/dm/eventsource.svg?style=flat-square)](http://npm-stat.com/charts.html?package=eventsource&from=2015-09-01) [![Dependencies](https://david-dm.org/EventSource/eventsource.svg)](https://david-dm.org/EventSource/eventsource) | ||
open http://localhost:8080 # Browser client - both native and polyfill | ||
curl http://localhost:8080/sse # Enjoy the simplicity of SSE) | ||
curl http://localhost:8080/sse # Enjoy the simplicity of SSE | ||
@@ -56,6 +56,6 @@ ## Browser Polyfill | ||
By default, https requests that cannot be authorized will cause connection to fail and an exception | ||
to be emitted. You can override this behaviour: | ||
to be emitted. You can override this behaviour, along with other https options: | ||
```javascript | ||
var eventSourceInitDict = {rejectUnauthorized: false}; | ||
var eventSourceInitDict = {https: {rejectUnauthorized: false}}; | ||
var es = new EventSource(url, eventSourceInitDict); | ||
@@ -85,3 +85,8 @@ ``` | ||
```javascript | ||
var es = new EventSource(url, { proxy: 'http://your.proxy.com' }); | ||
var es = new EventSource(url, {proxy: 'http://your.proxy.com'}); | ||
``` | ||
## License | ||
MIT-licensed. See LICENSE |
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
568526
31
8947
0
90
7