Socket
Socket
Sign inDemoInstall

eventsource

Package Overview
Dependencies
0
Maintainers
1
Versions
35
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.0.6 to 0.0.7

8

History.md

@@ -0,1 +1,9 @@

# [0.0.7](https://github.com/aslakhellesoy/eventsource-node/compare/v0.0.6...v0.0.7)
* Explicitly raise an error when server returns http 403 and dont 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)
* 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)
* Graceful handling of parse errors ([#19](https://github.com/aslakhellesoy/eventsource-node/issues/19) Aslak Hellesøy)
* Switched from testing with Nodeunit to Mocha (Aslak Hellesøy)
# [0.0.6](https://github.com/aslakhellesoy/eventsource-node/compare/v0.0.5...v0.0.6)

@@ -2,0 +10,0 @@

42

lib/eventsource.js

@@ -7,2 +7,6 @@ var http = require('http')

function isPlainObject(obj) {
return Object.getPrototypeOf(obj) === Object.prototype;
}
/**

@@ -12,5 +16,6 @@ * Creates a new EventSource object

* @param {String} url the URL to which to connect
* @param {Object} headers headers to use
* @api public
**/
function EventSource(url) {
function EventSource(url, eventSourceInitDict) {
var readyState = EventSource.CONNECTING;

@@ -59,2 +64,8 @@ Object.defineProperty(this, 'readyState', {

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;
}
}

@@ -76,2 +87,7 @@ req = (isSecure ? https : http).request(options, function(res) {

if (res.statusCode == 403) {
_emit('error', 'Access denied');
return self.close();
}
readyState = EventSource.OPEN;

@@ -89,12 +105,16 @@ res.on('close', onConnectionClosed);

buf = buf.slice(messages.length);
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;
_emit(message.event || 'message', new MessageEvent(data));
});
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;
_emit(message.event || 'message', new MessageEvent(data));
});
} catch(e) {
_emit('error', e);
}
});

@@ -101,0 +121,0 @@ });

@@ -1,2 +0,74 @@

/* Jison generated parser */
/* parser generated by jison 0.4.4 */
/*
Returns a Parser object of the following structure:
Parser: {
yy: {}
}
Parser.prototype: {
yy: {},
trace: function(),
symbols_: {associative list: name ==> number},
terminals_: {associative list: number ==> name},
productions_: [...],
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$),
table: [...],
defaultActions: {...},
parseError: function(str, hash),
parse: function(input),
lexer: {
EOF: 1,
parseError: function(str, hash),
setInput: function(input),
input: function(),
unput: function(str),
more: function(),
less: function(n),
pastInput: function(),
upcomingInput: function(),
showPosition: function(),
test_match: function(regex_match_array, rule_index),
next: function(),
lex: function(),
begin: function(condition),
popState: function(),
_currentRules: function(),
topState: function(),
pushState: function(condition),
options: {
ranges: boolean (optional: true ==> token location info will include a .range[] member)
flex: boolean (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match)
backtrack_lexer: boolean (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code)
},
performAction: function(yy, yy_, $avoiding_name_collisions, YY_START),
rules: [...],
conditions: {associative list: name ==> set},
}
}
token location info (@$, _$, etc.): {
first_line: n,
last_line: n,
first_column: n,
last_column: n,
range: [start_number, end_number] (where the numbers are indexes into the input string, regular zero-based)
}
the parseError function receives a 'hash' object with these members for lexer and parser errors: {
text: (matched text)
token: (the produced terminal token, if any)
line: (yylineno)
}
while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: {
loc: (yylloc)
expected: (string describing the set of expected tokens)
recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error)
}
*/
var eventstream = (function(){

@@ -8,3 +80,4 @@ var parser = {trace: function trace() { },

productions_: [0,[3,2],[4,3],[4,2],[6,2],[6,1],[8,1],[8,1],[10,3],[10,2],[9,4],[9,3],[9,2],[12,1],[12,2],[13,1],[13,2],[14,1],[14,1],[14,1],[15,1],[15,1]],
performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
/* this == yyval */

@@ -59,6 +132,10 @@ var $0 = $$.length - 1;

parseError: function parseError(str, hash) {
throw new Error(str);
if (hash.recoverable) {
this.trace(str);
} else {
throw new Error(str);
}
},
parse: function parse(input) {
var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
this.lexer.setInput(input);

@@ -68,9 +145,13 @@ this.lexer.yy = this.yy;

this.yy.parser = this;
if (typeof this.lexer.yylloc == "undefined")
if (typeof this.lexer.yylloc == 'undefined') {
this.lexer.yylloc = {};
}
var yyloc = this.lexer.yylloc;
lstack.push(yyloc);
var ranges = this.lexer.options && this.lexer.options.ranges;
if (typeof this.yy.parseError === "function")
if (typeof this.yy.parseError === 'function') {
this.parseError = this.yy.parseError;
} else {
this.parseError = Object.getPrototypeOf(this).parseError;
}
function popStack(n) {

@@ -83,4 +164,4 @@ stack.length = stack.length - 2 * n;

var token;
token = self.lexer.lex() || 1;
if (typeof token !== "number") {
token = self.lexer.lex() || EOF;
if (typeof token !== 'number') {
token = self.symbols_[token] || token;

@@ -96,3 +177,3 @@ }

} else {
if (symbol === null || typeof symbol == "undefined") {
if (symbol === null || typeof symbol == 'undefined') {
symbol = lex();

@@ -102,20 +183,25 @@ }

}
if (typeof action === "undefined" || !action.length || !action[0]) {
var errStr = "";
if (!recovering) {
if (typeof action === 'undefined' || !action.length || !action[0]) {
var errStr = '';
expected = [];
for (p in table[state])
if (this.terminals_[p] && p > 2) {
expected.push("'" + this.terminals_[p] + "'");
for (p in table[state]) {
if (this.terminals_[p] && p > TERROR) {
expected.push('\'' + this.terminals_[p] + '\'');
}
}
if (this.lexer.showPosition) {
errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'";
errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + this.lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\'';
} else {
errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'");
errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'');
}
this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
this.parseError(errStr, {
text: this.lexer.match,
token: this.terminals_[symbol] || symbol,
line: this.lexer.yylineno,
loc: yyloc,
expected: expected
});
}
}
if (action[0] instanceof Array && action.length > 1) {
throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol);
throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
}

@@ -134,4 +220,5 @@ switch (action[0]) {

yyloc = this.lexer.yylloc;
if (recovering > 0)
if (recovering > 0) {
recovering--;
}
} else {

@@ -145,8 +232,16 @@ symbol = preErrorSymbol;

yyval.$ = vstack[vstack.length - len];
yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column};
yyval._$ = {
first_line: lstack[lstack.length - (len || 1)].first_line,
last_line: lstack[lstack.length - 1].last_line,
first_column: lstack[lstack.length - (len || 1)].first_column,
last_column: lstack[lstack.length - 1].last_column
};
if (ranges) {
yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]];
yyval._$.range = [
lstack[lstack.length - (len || 1)].range[0],
lstack[lstack.length - 1].range[1]
];
}
r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
if (typeof r !== "undefined") {
if (typeof r !== 'undefined') {
return r;

@@ -170,7 +265,9 @@ }

return true;
}
};
/* Jison generated lexer */
}};
/* generated by jison-lex 0.2.0 */
var lexer = (function(){
var lexer = ({EOF:1,
var lexer = {
EOF:1,
parseError:function parseError(str, hash) {

@@ -183,13 +280,24 @@ if (this.yy.parser) {

},
// resets the lexer, sets new input
setInput:function (input) {
this._input = input;
this._more = this._less = this.done = false;
this._more = this._backtrack = this.done = false;
this.yylineno = this.yyleng = 0;
this.yytext = this.matched = this.match = '';
this.conditionStack = ['INITIAL'];
this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
if (this.options.ranges) this.yylloc.range = [0,0];
this.yylloc = {
first_line: 1,
first_column: 0,
last_line: 1,
last_column: 0
};
if (this.options.ranges) {
this.yylloc.range = [0,0];
}
this.offset = 0;
return this;
},
// consumes and returns one char from the input
input:function () {

@@ -209,3 +317,5 @@ var ch = this._input[0];

}
if (this.options.ranges) this.yylloc.range[1]++;
if (this.options.ranges) {
this.yylloc.range[1]++;
}

@@ -215,2 +325,4 @@ this._input = this._input.slice(1);

},
// unshifts one char (or a string) into the input
unput:function (ch) {

@@ -221,19 +333,23 @@ var len = ch.length;

this._input = ch + this._input;
this.yytext = this.yytext.substr(0, this.yytext.length-len-1);
this.yytext = this.yytext.substr(0, this.yytext.length - len - 1);
//this.yyleng -= len;
this.offset -= len;
var oldLines = this.match.split(/(?:\r\n?|\n)/g);
this.match = this.match.substr(0, this.match.length-1);
this.matched = this.matched.substr(0, this.matched.length-1);
this.match = this.match.substr(0, this.match.length - 1);
this.matched = this.matched.substr(0, this.matched.length - 1);
if (lines.length-1) this.yylineno -= lines.length-1;
if (lines.length - 1) {
this.yylineno -= lines.length - 1;
}
var r = this.yylloc.range;
this.yylloc = {first_line: this.yylloc.first_line,
last_line: this.yylineno+1,
first_column: this.yylloc.first_column,
last_column: lines ?
(lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length:
this.yylloc = {
first_line: this.yylloc.first_line,
last_line: this.yylineno + 1,
first_column: this.yylloc.first_column,
last_column: lines ?
(lines.length === oldLines.length ? this.yylloc.first_column : 0)
+ oldLines[oldLines.length - lines.length].length - lines[0].length :
this.yylloc.first_column - len
};
};

@@ -243,4 +359,7 @@ if (this.options.ranges) {

}
this.yyleng = this.yytext.length;
return this;
},
// When called from action, caches matched text and appends it on next action
more:function () {

@@ -250,5 +369,24 @@ this._more = true;

},
// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
reject:function () {
if (this.options.backtrack_lexer) {
this._backtrack = true;
} else {
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
text: "",
token: null,
line: this.yylineno
});
}
return this;
},
// retain first n characters of the match
less:function (n) {
this.unput(this.match.slice(n));
},
// displays already matched input, i.e. for error messages
pastInput:function () {

@@ -258,2 +396,4 @@ var past = this.matched.substr(0, this.matched.length - this.match.length);

},
// displays upcoming input, i.e. for error messages
upcomingInput:function () {

@@ -264,9 +404,91 @@ var next = this.match;

}
return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
return (next.substr(0,20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
},
// displays the character position where the lexing error occurred, i.e. for error messages
showPosition:function () {
var pre = this.pastInput();
var c = new Array(pre.length + 1).join("-");
return pre + this.upcomingInput() + "\n" + c+"^";
return pre + this.upcomingInput() + "\n" + c + "^";
},
// test the lexed token: return FALSE when not a match, otherwise return token
test_match:function (match, indexed_rule) {
var token,
lines,
backup;
if (this.options.backtrack_lexer) {
// save context
backup = {
yylineno: this.yylineno,
yylloc: {
first_line: this.yylloc.first_line,
last_line: this.last_line,
first_column: this.yylloc.first_column,
last_column: this.yylloc.last_column
},
yytext: this.yytext,
match: this.match,
matches: this.matches,
matched: this.matched,
yyleng: this.yyleng,
offset: this.offset,
_more: this._more,
_input: this._input,
yy: this.yy,
conditionStack: this.conditionStack.slice(0),
done: this.done
};
if (this.options.ranges) {
backup.yylloc.range = this.yylloc.range.slice(0);
}
}
lines = match[0].match(/(?:\r\n?|\n).*/g);
if (lines) {
this.yylineno += lines.length;
}
this.yylloc = {
first_line: this.yylloc.last_line,
last_line: this.yylineno + 1,
first_column: this.yylloc.last_column,
last_column: lines ?
lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length :
this.yylloc.last_column + match[0].length
};
this.yytext += match[0];
this.match += match[0];
this.matches = match;
this.yyleng = this.yytext.length;
if (this.options.ranges) {
this.yylloc.range = [this.offset, this.offset += this.yyleng];
}
this._more = false;
this._backtrack = false;
this._input = this._input.slice(match[0].length);
this.matched += match[0];
token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]);
if (this.done && this._input) {
this.done = false;
}
if (token) {
if (this.options.backtrack_lexer) {
delete backup;
}
return token;
} else if (this._backtrack) {
// recover context
for (var k in backup) {
this[k] = backup[k];
}
return false; // rule action called reject() implying the next rule should be tested instead.
}
if (this.options.backtrack_lexer) {
delete backup;
}
return false;
},
// return next match in input
next:function () {

@@ -276,3 +498,5 @@ if (this.done) {

}
if (!this._input) this.done = true;
if (!this._input) {
this.done = true;
}

@@ -282,5 +506,3 @@ var token,

tempMatch,
index,
col,
lines;
index;
if (!this._more) {

@@ -291,3 +513,3 @@ this.yytext = '';

var rules = this._currentRules();
for (var i=0;i < rules.length; i++) {
for (var i = 0; i < rules.length; i++) {
tempMatch = this._input.match(this.rules[rules[i]]);

@@ -297,26 +519,25 @@ if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {

index = i;
if (!this.options.flex) break;
if (this.options.backtrack_lexer) {
token = this.test_match(tempMatch, rules[i]);
if (token !== false) {
return token;
} else if (this._backtrack) {
match = false;
continue; // rule action called reject() implying a rule MISmatch.
} else {
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
return false;
}
} else if (!this.options.flex) {
break;
}
}
}
if (match) {
lines = match[0].match(/(?:\r\n?|\n).*/g);
if (lines) this.yylineno += lines.length;
this.yylloc = {first_line: this.yylloc.last_line,
last_line: this.yylineno+1,
first_column: this.yylloc.last_column,
last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length};
this.yytext += match[0];
this.match += match[0];
this.matches = match;
this.yyleng = this.yytext.length;
if (this.options.ranges) {
this.yylloc.range = [this.offset, this.offset += this.yyleng];
token = this.test_match(match, rules[index]);
if (token !== false) {
return token;
}
this._more = false;
this._input = this._input.slice(match[0].length);
this.matched += match[0];
token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]);
if (this.done && this._input) this.done = false;
if (token) return token;
else return;
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
return false;
}

@@ -326,9 +547,14 @@ if (this._input === "") {

} else {
return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
{text: "", token: null, line: this.yylineno});
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
text: "",
token: null,
line: this.yylineno
});
}
},
// return next match that has a token
lex:function lex() {
var r = this.next();
if (typeof r !== 'undefined') {
if (r) {
return r;

@@ -339,21 +565,50 @@ } else {

},
// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
begin:function begin(condition) {
this.conditionStack.push(condition);
},
// pop the previously active lexer condition state off the condition stack
popState:function popState() {
return this.conditionStack.pop();
var n = this.conditionStack.length - 1;
if (n > 0) {
return this.conditionStack.pop();
} else {
return this.conditionStack[0];
}
},
// produce the lexer rule set which is active for the currently active lexer condition state
_currentRules:function _currentRules() {
return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
} else {
return this.conditions["INITIAL"].rules;
}
},
topState:function () {
return this.conditionStack[this.conditionStack.length-2];
// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
topState:function topState(n) {
n = this.conditionStack.length - 1 - Math.abs(n || 0);
if (n >= 0) {
return this.conditionStack[n];
} else {
return "INITIAL";
}
},
pushState:function begin(condition) {
// alias for begin(condition)
pushState:function pushState(condition) {
this.begin(condition);
}});
lexer.options = {};
lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
},
var YYSTATE=YY_START
// return the number of states currently on the stack
stateStackSize:function stateStackSize() {
return this.conditionStack.length;
},
options: {},
performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
var YYSTATE=YY_START;
switch($avoiding_name_collisions) {

@@ -375,28 +630,32 @@ case 0:/* skip leading byte order mark */

}
},
rules: [/^(?:^\uFEFF)/,/^(?:\u0020)/,/^(?::)/,/^(?:(\u000D\u000A|\u000D|\u000A))/,/^(?:[\u0000-\u0009\u000B-\u000C\u000E-\u0019\u0021-\u0039\u003B-\uFFFFF])/,/^(?:$)/,/^(?:.)/],
conditions: {"INITIAL":{"rules":[0,1,2,3,4,5,6],"inclusive":true}}
};
lexer.rules = [/^(?:^\uFEFF)/,/^(?:\u0020)/,/^(?::)/,/^(?:(\u000D\u000A|\u000D|\u000A))/,/^(?:[\u0000-\u0009\u000B-\u000C\u000E-\u0019\u0021-\u0039\u003B-\u10FFFF])/,/^(?:$)/,/^(?:.)/];
lexer.conditions = {"INITIAL":{"rules":[0,1,2,3,4,5,6],"inclusive":true}};
return lexer;})()
return lexer;
})();
parser.lexer = lexer;
function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
function Parser () {
this.yy = {};
}
Parser.prototype = parser;parser.Parser = Parser;
return new Parser;
})();
if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
exports.parser = eventstream;
exports.Parser = eventstream.Parser;
exports.parse = function () { return eventstream.parse.apply(eventstream, arguments); }
exports.parse = function () { return eventstream.parse.apply(eventstream, arguments); };
exports.main = function commonjsMain(args) {
if (!args[1])
throw new Error('Usage: '+args[0]+' FILE');
var source, cwd;
if (typeof process !== 'undefined') {
source = require('fs').readFileSync(require('path').resolve(args[1]), "utf8");
} else {
source = require("file").path(require("file").cwd()).join(args[1]).read({charset: "utf-8"});
if (!args[1]) {
console.log('Usage: '+args[0]+' FILE');
process.exit(1);
}
var source = require('fs').readFileSync(require('path').normalize(args[1]), "utf8");
return exports.parser.parse(source);
}
};
if (typeof module !== 'undefined' && require.main === module) {
exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args);
exports.main(process.argv.slice(1));
}
}
{
"name": "eventsource",
"version": "0.0.6",
"description": "EventSource client for Node.js",
"keywords": [ "eventsource", "http", "streaming", "sse" ],
"homepage": "http://github.com/aslakhellesoy/eventsource-node",
"author": "Aslak Hellesøy <aslak.hellesoy@gmail.com>",
"contributors": [
"Aslak Hellesøy <aslak.hellesoy@gmail.com>",
"Einar Otto Stangvik <einaros+gh@gmail.com>",
"Dan North <tastapod@gmail.com>"
],
"repository": {
"type": "git",
"url": "git://github.com/aslakhellesoy/eventsource-node.git"
},
"bugs": {
"url": "http://github.com/aslakhellesoy/eventsource-node/issues"
},
"directories": {
"lib" : "./lib"
},
"main": "./lib/eventsource",
"licenses": [
{
"type": "MIT",
"url": "http://github.com/aslakhellesoy/eventsource-node/raw/master/LICENSE"
}
],
"devDependencies": {
"nodeunit" : "0.6.x",
"dox" : "0.1.x",
"jison": "0.3.x"
},
"scripts": {
"test": "make run-tests"
},
"engines": {
"node" : ">=0.6.0"
"name": "eventsource",
"version": "0.0.7",
"description": "W3C compliant EventSource client for Node.js",
"keywords": [
"eventsource",
"http",
"streaming",
"sse"
],
"homepage": "http://github.com/aslakhellesoy/eventsource-node",
"author": "Aslak Hellesøy <aslak.hellesoy@gmail.com>",
"contributors": [
"Aslak Hellesøy <aslak.hellesoy@gmail.com>",
"Einar Otto Stangvik <einaros+gh@gmail.com>",
"Dan North <tastapod@gmail.com>",
"Scott Moak <scott.moak@mybrainoncode.com>",
"William Wicks",
"Devon Adkisson"
],
"repository": {
"type": "git",
"url": "git://github.com/aslakhellesoy/eventsource-node.git"
},
"bugs": {
"url": "http://github.com/aslakhellesoy/eventsource-node/issues"
},
"directories": {
"lib": "./lib"
},
"main": "./lib/eventsource",
"licenses": [
{
"type": "MIT",
"url": "http://github.com/aslakhellesoy/eventsource-node/raw/master/LICENSE"
}
],
"devDependencies": {
"mocha": "~1.9.0",
"jison": "~0.4.4"
},
"scripts": {
"test": "make run-tests"
},
"engines": {
"node": ">=0.6.0"
}
}

@@ -16,3 +16,3 @@ [![Build Status](https://secure.travis-ci.org/aslakhellesoy/eventsource-node.png)](http://travis-ci.org/aslakhellesoy/eventsource-node)

es = new EventSource('http://googlecodesamples.com/html5/sse/sse.php');
var es = new EventSource('http://googlecodesamples.com/html5/sse/sse.php');
es.onmessage = function(e) {

@@ -31,1 +31,13 @@ console.log(e.data);

See https://github.com/einaros/sse-example
## Extensions to the W3C API
### Setting HTTP request headers
You can define custom HTTP headers for the initial HTTP request. This can be useful for e.g. sending cookies.
This is done by assigning a `header` attribute to the optional `eventSourceInitDict` argument:
```javascript
var eventSourceInitDict = {headers: {'Cookie': 'test=test'}};
var es = new EventSource(url, eventSourceInitDict);
```

@@ -1,5 +0,6 @@

var EventSource = require('eventsource')
var EventSource = require('../lib/eventsource')
, http = require('http')
, https = require('https')
, fs = require('fs');
, fs = require('fs')
, assert = require('assert');

@@ -39,42 +40,57 @@ var port = 20000;

exports['Messages'] = {
setUp: function(done) {
describe('Parser', function() {
beforeEach(function(done) {
port++;
done();
},
});
'issue-18': function(test) {
createServer(["id: 1\ndata: hello world\n\n"], function(close) {
it('parses multibyte characters', function(done) {
createServer(["id: 1\ndata: €豆腐\n\n"], function(close) {
var es = new EventSource('http://localhost:' + port);
es.onmessage = function(m) {
test.equal("hello world", m.data);
assert.equal("€豆腐", m.data);
es.close();
close(test.done);
close(done);
};
});
},
});
'one one-line message in one chunk': function(test) {
it('raises error when it fails to parse', function(done) {
createServer(["\n\n\n\nid: 1\ndata: hello world\n\n"], function(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);
};
});
});
it('parses one one-line message in one chunk', function(done) {
createServer(["data: Hello\n\n"], function(close) {
var es = new EventSource('http://localhost:' + port);
es.onmessage = function(m) {
test.equal("Hello", m.data);
assert.equal("Hello", m.data);
es.close();
close(test.done);
close(done);
};
});
},
});
'one one-line message in two chunks': function(test) {
it('parses one one-line message in two chunks', function(done) {
createServer(["data: Hel", "lo\n\n"], function(close) {
var es = new EventSource('http://localhost:' + port);
es.onmessage = function(m) {
test.equal("Hello", m.data);
assert.equal("Hello", m.data);
es.close();
close(test.done);
close(done);
};
});
},
});
'two one-line messages in one chunk': function(test) {
it('parses two one-line messages in one chunk', function(done) {
createServer(["data: Hello\n\n", "data: World\n\n"], function(close) {

@@ -85,3 +101,3 @@ var es = new EventSource('http://localhost:' + port);

function first(m) {
test.equal("Hello", m.data);
assert.equal("Hello", m.data);
es.onmessage = second;

@@ -91,21 +107,21 @@ }

function second(m) {
test.equal("World", m.data);
assert.equal("World", m.data);
es.close();
close(test.done);
close(done);
}
});
},
});
'one two-line message in one chunk': function(test) {
it('parses one two-line message in one chunk', function(done) {
createServer(["data: Hello\ndata:World\n\n"], function(close) {
var es = new EventSource('http://localhost:' + port);
es.onmessage = function(m) {
test.equal("Hello\nWorld", m.data);
assert.equal("Hello\nWorld", m.data);
es.close();
close(test.done);
close(done);
};
});
},
});
'really chopped up unicode data': function(test) {
it('parses really chopped up unicode data', function(done) {
var chopped = "data: Aslak\n\ndata: Hellesøy\n\n".split("");

@@ -117,3 +133,3 @@ createServer(chopped, function(close) {

function first(m) {
test.equal("Aslak", m.data);
assert.equal("Aslak", m.data);
es.onmessage = second;

@@ -123,10 +139,10 @@ }

function second(m) {
test.equal("Hellesøy", m.data);
assert.equal("Hellesøy", m.data);
es.close();
close(test.done);
close(done);
}
});
},
});
'accepts CRLF as separator': function(test) {
it('accepts CRLF as separator', function(done) {
var chopped = "data: Aslak\r\n\r\ndata: Hellesøy\r\n\r\n".split("");

@@ -138,3 +154,3 @@ createServer(chopped, function(close) {

function first(m) {
test.equal("Aslak", m.data);
assert.equal("Aslak", m.data);
es.onmessage = second;

@@ -144,10 +160,10 @@ }

function second(m) {
test.equal("Hellesøy", m.data);
assert.equal("Hellesøy", m.data);
es.close();
close(test.done);
close(done);
}
});
},
});
'accepts CR as separator': function(test) {
it('accepts CR as separator', function(done) {
var chopped = "data: Aslak\r\rdata: Hellesøy\r\r".split("");

@@ -159,3 +175,3 @@ createServer(chopped, function(close) {

function first(m) {
test.equal("Aslak", m.data);
assert.equal("Aslak", m.data);
es.onmessage = second;

@@ -165,21 +181,21 @@ }

function second(m) {
test.equal("Hellesøy", m.data);
assert.equal("Hellesøy", m.data);
es.close();
close(test.done);
close(done);
}
});
},
});
'delivers message with explicit event': function(test) {
it('delivers message with explicit event', function(done) {
createServer(["event: greeting\ndata: Hello\n\n"], function(close) {
var es = new EventSource('http://localhost:' + port);
es.addEventListener('greeting', function(m) {
test.equal("Hello", m.data);
assert.equal("Hello", m.data);
es.close();
close(test.done);
close(done);
});
});
},
});
'comments are ignored': function(test) {
it('ignores comments', function(done) {
createServer(["data: Hello\n\n:nothing to see here\n\ndata: World\n\n"], function(close) {

@@ -190,3 +206,3 @@ var es = new EventSource('http://localhost:' + port);

function first(m) {
test.equal("Hello", m.data);
assert.equal("Hello", m.data);
es.onmessage = second;

@@ -196,10 +212,10 @@ }

function second(m) {
test.equal("World", m.data);
assert.equal("World", m.data);
es.close();
close(test.done);
close(done);
}
});
},
});
'empty comments are ignored': function(test) {
it('ignores empty comments', function(done) {
createServer(["data: Hello\n\n:\n\ndata: World\n\n"], function(close) {

@@ -210,3 +226,3 @@ var es = new EventSource('http://localhost:' + port);

function first(m) {
test.equal("Hello", m.data);
assert.equal("Hello", m.data);
es.onmessage = second;

@@ -216,10 +232,10 @@ }

function second(m) {
test.equal("World", m.data);
assert.equal("World", m.data);
es.close();
close(test.done);
close(done);
}
});
},
});
'empty data field causes entire event to be ignored': function(test) {
it('causes entire event to be ignored for empty data fields', function(done) {
createServer(["data:\n\ndata: Hello\n\n"], function(close) {

@@ -229,14 +245,14 @@ var es = new EventSource('http://localhost:' + port);

es.emit = function(event) {
test.ok(event === 'message' || event === 'newListener');
assert.ok(event === 'message' || event === 'newListener');
return originalEmit.apply(this, arguments);
}
es.onmessage = function(m) {
test.equal('Hello', m.data);
assert.equal('Hello', m.data);
es.close();
close(test.done);
close(done);
};
});
},
});
'empty event field causes entire event to be ignored': function(test) {
it('causes entire event to be ignored for empty event field', function(done) {
createServer(["event:\n\ndata: Hello\n\n"], function(close) {

@@ -246,21 +262,21 @@ var es = new EventSource('http://localhost:' + port);

es.emit = function(event) {
test.ok(event === 'message' || event === 'newListener');
assert.ok(event === 'message' || event === 'newListener');
return originalEmit.apply(this, arguments);
}
es.onmessage = function(m) {
test.equal('Hello', m.data);
assert.equal('Hello', m.data);
es.close();
close(test.done);
close(done);
};
});
},
};
});
});
exports['HTTP Request'] = {
setUp: function(done) {
describe('HTTP Request', function() {
beforeEach(function(done) {
port++;
done();
},
});
'passes cache-control: no-cache to server': function(test) {
it('passes cache-control: no-cache to server', function(done) {
var headers;

@@ -271,10 +287,33 @@ createServer([], function(close) {

es.onopen = function() {
test.equal('no-cache', headers['cache-control']);
assert.equal('no-cache', headers['cache-control']);
es.close();
close(test.done);
close(done);
};
}, function(req) { headers = req.headers; });
},
});
'follows http 301 redirect': function(test) {
it('sets headers by user', function(done) {
var url = 'http://localhost:' + port;
var headers = {
'User-Agent': 'test',
'Cookie': 'test=test'
};
createServer([],
function(close) {
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']);
res.writeHead(200);
res.end();
return true;
});
});
it('follows http 301 redirect', function(done) {
var headers;

@@ -288,6 +327,6 @@ var url = 'http://localhost:' + port;

es.onopen = function() {
test.ok(clientRequestedRedirectUrl);
test.equal(url + redirectSuffix, es.url);
assert.ok(clientRequestedRedirectUrl);
assert.equal(url + redirectSuffix, es.url);
es.close();
close(test.done);
close(done);
};

@@ -307,7 +346,25 @@ },

});
},
});
'http 301 with missing location causes error event': function(test) {
it('causes error event when response is 403', function(done) {
var headers;
var url = 'http://localhost:' + port;
createServer(["id: 1\ndata: hello world\n\n"],
function(close) {
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) {
var headers;
var url = 'http://localhost:' + port;
createServer([],

@@ -318,3 +375,3 @@ function(close) {

es.close();
close(test.done);
close(done);
};

@@ -330,5 +387,5 @@ },

});
},
});
'follows http 307 redirect': function(test) {
it('follows http 307 redirect', function(done) {
var headers;

@@ -342,6 +399,6 @@ var url = 'http://localhost:' + port;

es.onopen = function() {
test.ok(clientRequestedRedirectUrl);
test.equal(url + redirectSuffix, es.url);
assert.ok(clientRequestedRedirectUrl);
assert.equal(url + redirectSuffix, es.url);
es.close();
close(test.done);
close(done);
};

@@ -361,5 +418,5 @@ },

});
},
});
'http 307 with missing location causes error event': function(test) {
it('causes error event for 307 response with with missing location', function(done) {
var headers;

@@ -372,3 +429,3 @@ var url = 'http://localhost:' + port;

es.close();
close(test.done);
close(done);
};

@@ -383,14 +440,13 @@ },

return true;
}
);
}
};
});
});
});
exports['HTTPS Support'] = {
setUp: function(done) {
describe('HTTPS Support', function() {
beforeEach(function(done) {
port++;
done();
},
});
'uses https for https urls': function(test) {
it('uses https for https urls', function(done) {
var chopped = "data: Aslak\n\ndata: Hellesøy\n\n".split("");

@@ -402,3 +458,3 @@ createServer(chopped, function(close) {

function first(m) {
test.equal("Aslak", m.data);
assert.equal("Aslak", m.data);
es.onmessage = second;

@@ -408,17 +464,17 @@ }

function second(m) {
test.equal("Hellesøy", m.data);
assert.equal("Hellesøy", m.data);
es.close();
close(test.done);
close(done);
}
}, true);
},
};
});
});
exports['Reconnect'] = {
setUp: function(done) {
describe('Reconnection', function() {
beforeEach(function(done) {
port++;
done();
},
});
'when server is down': function(test) {
it('is attempted when server is down', function(done) {
var es = new EventSource('http://localhost:' + port);

@@ -436,9 +492,9 @@ es.reconnectInterval = 0;

es.onmessage = function(m) {
test.equal("Hello", m.data);
assert.equal("Hello", m.data);
es.close();
theClose(test.done);
theClose(done);
};
},
});
'when server goes down after connection': function(test) {
it('is attempted when server goes down after connection', function(done) {
createServer(["data: Hello\n\n"], function(closeFirstServer) {

@@ -449,3 +505,3 @@ var es = new EventSource('http://localhost:' + port);

es.onmessage = function(m) {
test.equal("Hello", m.data);
assert.equal("Hello", m.data);
closeFirstServer(function() {

@@ -456,5 +512,5 @@ createServer(["data: World\n\n"], function(closeSecondServer) {

function second(m) {
test.equal("World", m.data);
assert.equal("World", m.data);
es.close();
closeSecondServer(test.done);
closeSecondServer(done);
}

@@ -465,5 +521,5 @@ });

});
},
});
'stop reconnecting when server responds with HTTP 204': function(test) {
it('is not attempted when server responds with HTTP 204', function(done) {
createServer(["data: Hello\n\n"], function(closeFirstServer) {

@@ -474,3 +530,3 @@ var es = new EventSource('http://localhost:' + port);

es.onmessage = function(m) {
test.equal("Hello", m.data);
assert.equal("Hello", m.data);
closeFirstServer(function() {

@@ -487,3 +543,3 @@ createServer([], function(closeSecondServer) {

clearInterval(ival);
closeSecondServer(test.done);
closeSecondServer(done);
}

@@ -495,5 +551,5 @@ }, 5);

});
},
});
'send Last-Event-ID http header when id has previously been passed in an event from the server': function(test) {
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(closeFirstServer) {

@@ -508,5 +564,5 @@ var headers = null;

es.onopen = function() {
test.equal('10', headers['last-event-id']);
assert.equal('10', headers['last-event-id']);
es.close();
close(test.done);
close(done);
};

@@ -517,5 +573,5 @@ }, function(req) { headers = req.headers; });

});
},
});
'does not send Last-Event-ID http header when id has not been previously sent by the server': function(test) {
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(closeFirstServer) {

@@ -530,5 +586,5 @@ var headers = null;

es.onopen = function() {
test.equal('undefined', typeof headers['last-event-id']);
assert.equal('undefined', typeof headers['last-event-id']);
es.close();
close(test.done);
close(done);
};

@@ -539,5 +595,5 @@ }, function(req) { headers = req.headers; });

});
},
});
'reconnect after http 301 redirect uses new url': function(test) {
it('is attempted after http 301 redirect uses new url', function(done) {
var headers;

@@ -555,7 +611,7 @@ var url = 'http://localhost:' + port;

es.onopen = function() {
test.equal(url + redirectSuffix, es.url);
assert.equal(url + redirectSuffix, es.url);
es.close();
closeSecondServer(test.done);
closeSecondServer(done);
};
}, function(req, res) { test.equal(redirectSuffix, req.url); });
}, function(req, res) { assert.equal(redirectSuffix, req.url); });
});

@@ -573,5 +629,5 @@ };

});
},
});
'reconnect after http 307 redirect uses original url': function(test) {
it('is attempted after http 307 redirect uses original url', function(done) {
var headers;

@@ -589,7 +645,7 @@ var url = 'http://localhost:' + port;

es.onopen = function() {
test.equal(url, es.url);
assert.equal(url, es.url);
es.close();
closeSecondServer(test.done);
closeSecondServer(done);
};
}, function(req, res) { test.equal('/', req.url); });
}, function(req, res) { assert.equal('/', req.url); });
});

@@ -607,38 +663,38 @@ };

});
},
};
});
});
exports['readyState'] = {
setUp: function(done) {
describe('readyState', function() {
beforeEach(function(done) {
port++;
done();
},
});
'has CONNECTING constant': function(test) {
test.equal(0, EventSource.CONNECTING);
test.done();
},
it('has CONNECTING constant', function(done) {
assert.equal(0, EventSource.CONNECTING);
done();
});
'has OPEN constant': function(test) {
test.equal(1, EventSource.OPEN);
test.done();
},
it('has OPEN constant', function(done) {
assert.equal(1, EventSource.OPEN);
done();
});
'has CLOSED constant': function(test) {
test.equal(2, EventSource.CLOSED);
test.done();
},
it('has CLOSED constant', function(done) {
assert.equal(2, EventSource.CLOSED);
done();
});
'readyState is CONNECTING before connection has been established': function(test) {
it('is CONNECTING before connection has been established', function(done) {
createServer([], function(close) {
var es = new EventSource('http://localhost:' + port);
test.equal(EventSource.CONNECTING, es.readyState);
assert.equal(EventSource.CONNECTING, es.readyState);
es.onopen = function() {
es.close();
close(test.done);
close(done);
}
});
},
});
'readyState is CONNECTING when server has closed the connection': function(test) {
it('is CONNECTING when server has closed the connection', function(done) {
createServer(["data: Hello\n\n"], function(closeFirstServer) {

@@ -649,8 +705,8 @@ var es = new EventSource('http://localhost:' + port);

es.onmessage = function(m) {
test.equal("Hello", m.data);
assert.equal("Hello", m.data);
closeFirstServer(function() {
createServer([], function(closeSecondServer) {
test.equal(EventSource.CONNECTING, es.readyState);
assert.equal(EventSource.CONNECTING, es.readyState);
es.close();
closeSecondServer(test.done);
closeSecondServer(done);
});

@@ -660,16 +716,16 @@ });

});
},
});
'readyState is OPEN when connection has been established': function(test) {
it('is OPEN when connection has been established', function(done) {
createServer([], function(close) {
var es = new EventSource('http://localhost:' + port);
es.onopen = function() {
test.equal(EventSource.OPEN, es.readyState);
assert.equal(EventSource.OPEN, es.readyState);
es.close();
close(test.done);
close(done);
}
});
},
});
'readyState is CLOSED after connection has been closed': function(test) {
it('is CLOSED after connection has been closed', function(done) {
createServer([], function(close) {

@@ -679,16 +735,16 @@ var es = new EventSource('http://localhost:' + port);

es.close();
test.equal(EventSource.CLOSED, es.readyState);
close(test.done);
assert.equal(EventSource.CLOSED, es.readyState);
close(done);
};
});
},
};
});
});
exports['Properties'] = {
setUp: function(done) {
describe('Properties', function() {
beforeEach(function(done) {
port++;
done();
},
});
'url exposes original request url': function(test) {
it('url exposes original request url', function(done) {
createServer([], function(close) {

@@ -698,17 +754,17 @@ var url = 'http://localhost:' + port;

es.onopen = function() {
test.equal(url, es.url);
assert.equal(url, es.url);
es.close();
close(test.done);
close(done);
}
});
},
};
});
});
exports['Events'] = {
setUp: function(done) {
describe('Events', function() {
beforeEach(function(done) {
port++;
done();
},
});
'calls onopen when connection is established': function(test) {
it('calls onopen when connection is established', function(done) {
createServer([], function(close) {

@@ -718,8 +774,8 @@ var es = new EventSource('http://localhost:' + port);

es.close();
close(test.done);
close(done);
}
});
},
});
'emits open event when connection is established': function(test) {
it('emits open event when connection is established', function(done) {
createServer([], function(close) {

@@ -729,8 +785,8 @@ var es = new EventSource('http://localhost:' + port);

es.close();
close(test.done);
close(done);
});
});
},
});
'does not emit error when connection is closed by client': function(test) {
it('does not emit error when connection is closed by client', function(done) {
createServer([], function(close) {

@@ -741,3 +797,3 @@ var es = new EventSource('http://localhost:' + port);

setTimeout(function() {
close(test.done);
close(done);
}, 50);

@@ -749,3 +805,3 @@ });

});
},
};
});
});

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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc