|
|
- var Stream = require('stream').Stream,
- util = require('util'),
- driver = require('websocket-driver'),
- Headers = require('websocket-driver/lib/websocket/driver/headers'),
- API = require('./websocket/api'),
- EventTarget = require('./websocket/api/event_target'),
- Event = require('./websocket/api/event');
-
- var EventSource = function(request, response, options) {
- this.writable = true;
- options = options || {};
-
- this._stream = response.socket;
- this._ping = options.ping || this.DEFAULT_PING;
- this._retry = options.retry || this.DEFAULT_RETRY;
-
- var scheme = driver.isSecureRequest(request) ? 'https:' : 'http:';
- this.url = scheme + '//' + request.headers.host + request.url;
- this.lastEventId = request.headers['last-event-id'] || '';
- this.readyState = API.CONNECTING;
-
- var headers = new Headers(),
- self = this;
-
- if (options.headers) {
- for (var key in options.headers) headers.set(key, options.headers[key]);
- }
-
- if (!this._stream || !this._stream.writable) return;
- process.nextTick(function() { self._open() });
-
- this._stream.setTimeout(0);
- this._stream.setNoDelay(true);
-
- var handshake = 'HTTP/1.1 200 OK\r\n' +
- 'Content-Type: text/event-stream\r\n' +
- 'Cache-Control: no-cache, no-store\r\n' +
- 'Connection: close\r\n' +
- headers.toString() +
- '\r\n' +
- 'retry: ' + Math.floor(this._retry * 1000) + '\r\n\r\n';
-
- this._write(handshake);
-
- this._stream.on('drain', function() { self.emit('drain') });
-
- if (this._ping)
- this._pingTimer = setInterval(function() { self.ping() }, this._ping * 1000);
-
- ['error', 'end'].forEach(function(event) {
- self._stream.on(event, function() { self.close() });
- });
- };
- util.inherits(EventSource, Stream);
-
- EventSource.isEventSource = function(request) {
- if (request.method !== 'GET') return false;
- var accept = (request.headers.accept || '').split(/\s*,\s*/);
- return accept.indexOf('text/event-stream') >= 0;
- };
-
- var instance = {
- DEFAULT_PING: 10,
- DEFAULT_RETRY: 5,
-
- _write: function(chunk) {
- if (!this.writable) return false;
- try {
- return this._stream.write(chunk, 'utf8');
- } catch (e) {
- return false;
- }
- },
-
- _open: function() {
- if (this.readyState !== API.CONNECTING) return;
-
- this.readyState = API.OPEN;
-
- var event = new Event('open');
- event.initEvent('open', false, false);
- this.dispatchEvent(event);
- },
-
- write: function(message) {
- return this.send(message);
- },
-
- end: function(message) {
- if (message !== undefined) this.write(message);
- this.close();
- },
-
- send: function(message, options) {
- if (this.readyState > API.OPEN) return false;
-
- message = String(message).replace(/(\r\n|\r|\n)/g, '$1data: ');
- options = options || {};
-
- var frame = '';
- if (options.event) frame += 'event: ' + options.event + '\r\n';
- if (options.id) frame += 'id: ' + options.id + '\r\n';
- frame += 'data: ' + message + '\r\n\r\n';
-
- return this._write(frame);
- },
-
- ping: function() {
- return this._write(':\r\n\r\n');
- },
-
- close: function() {
- if (this.readyState > API.OPEN) return false;
-
- this.readyState = API.CLOSED;
- this.writable = false;
- if (this._pingTimer) clearInterval(this._pingTimer);
- if (this._stream) this._stream.end();
-
- var event = new Event('close');
- event.initEvent('close', false, false);
- this.dispatchEvent(event);
-
- return true;
- }
- };
-
- for (var method in instance) EventSource.prototype[method] = instance[method];
- for (var key in EventTarget) EventSource.prototype[key] = EventTarget[key];
-
- module.exports = EventSource;
|