132 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			132 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
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;
 |