JAVASCRIPT   62

eventsource.js

Guest on 21st August 2021 08:48:21 PM

  1. var Stream      = require('stream').Stream,
  2.     util        = require('util'),
  3.     driver      = require('websocket-driver'),
  4.     Headers     = require('websocket-driver/lib/websocket/driver/headers'),
  5.     API         = require('./websocket/api'),
  6.     EventTarget = require('./websocket/api/event_target'),
  7.     Event       = require('./websocket/api/event');
  8.  
  9. var EventSource = function(request, response, options) {
  10.   this.writable = true;
  11.   options = options || {};
  12.  
  13.   this._stream = response.socket;
  14.   this._ping   = options.ping  || this.DEFAULT_PING;
  15.   this._retry  = options.retry || this.DEFAULT_RETRY;
  16.  
  17.   var scheme       = driver.isSecureRequest(request) ? 'https:' : 'http:';
  18.   this.url         = scheme + '//' + request.headers.host + request.url;
  19.   this.lastEventId = request.headers['last-event-id'] || '';
  20.   this.readyState  = API.CONNECTING;
  21.  
  22.   var headers = new Headers(),
  23.       self    = this;
  24.  
  25.   if (options.headers) {
  26.     for (var key in options.headers) headers.set(key, options.headers[key]);
  27.   }
  28.  
  29.   if (!this._stream || !this._stream.writable) return;
  30.   process.nextTick(function() { self._open() });
  31.  
  32.   this._stream.setTimeout(0);
  33.   this._stream.setNoDelay(true);
  34.  
  35.   var handshake = 'HTTP/1.1 200 OK\r\n' +
  36.                   'Content-Type: text/event-stream\r\n' +
  37.                   'Cache-Control: no-cache, no-store\r\n' +
  38.                   'Connection: close\r\n' +
  39.                   headers.toString() +
  40.                   '\r\n' +
  41.                   'retry: ' + Math.floor(this._retry * 1000) + '\r\n\r\n';
  42.  
  43.   this._write(handshake);
  44.  
  45.   this._stream.on('drain', function() { self.emit('drain') });
  46.  
  47.   if (this._ping)
  48.     this._pingTimer = setInterval(function() { self.ping() }, this._ping * 1000);
  49.  
  50.   ['error', 'end'].forEach(function(event) {
  51.     self._stream.on(event, function() { self.close() });
  52.   });
  53. };
  54. util.inherits(EventSource, Stream);
  55.  
  56. EventSource.isEventSource = function(request) {
  57.   if (request.method !== 'GET') return false;
  58.   var accept = (request.headers.accept || '').split(/\s*,\s*/);
  59.   return accept.indexOf('text/event-stream') >= 0;
  60. };
  61.  
  62. var instance = {
  63.   DEFAULT_PING:   10,
  64.   DEFAULT_RETRY:  5,
  65.  
  66.   _write: function(chunk) {
  67.     if (!this.writable) return false;
  68.     try {
  69.       return this._stream.write(chunk, 'utf8');
  70.     } catch (e) {
  71.       return false;
  72.     }
  73.   },
  74.  
  75.   _open: function() {
  76.     if (this.readyState !== API.CONNECTING) return;
  77.  
  78.     this.readyState = API.OPEN;
  79.  
  80.     var event = new Event('open');
  81.     event.initEvent('open', false, false);
  82.     this.dispatchEvent(event);
  83.   },
  84.  
  85.   write: function(message) {
  86.     return this.send(message);
  87.   },
  88.  
  89.   end: function(message) {
  90.     if (message !== undefined) this.write(message);
  91.     this.close();
  92.   },
  93.  
  94.   send: function(message, options) {
  95.     if (this.readyState > API.OPEN) return false;
  96.  
  97.     message = String(message).replace(/(\r\n|\r|\n)/g, '$1data: ');
  98.     options = options || {};
  99.  
  100.     var frame = '';
  101.     if (options.event) frame += 'event: ' + options.event + '\r\n';
  102.     if (options.id)    frame += 'id: '    + options.id    + '\r\n';
  103.     frame += 'data: ' + message + '\r\n\r\n';
  104.  
  105.     return this._write(frame);
  106.   },
  107.  
  108.   ping: function() {
  109.     return this._write(':\r\n\r\n');
  110.   },
  111.  
  112.   close: function() {
  113.     if (this.readyState > API.OPEN) return false;
  114.  
  115.     this.readyState = API.CLOSED;
  116.     this.writable = false;
  117.     if (this._pingTimer) clearInterval(this._pingTimer);
  118.     if (this._stream) this._stream.end();
  119.  
  120.     var event = new Event('close');
  121.     event.initEvent('close', false, false);
  122.     this.dispatchEvent(event);
  123.  
  124.     return true;
  125.   }
  126. };
  127.  
  128. for (var method in instance) EventSource.prototype[method] = instance[method];
  129. for (var key in EventTarget) EventSource.prototype[key] = EventTarget[key];
  130.  
  131. module.exports = EventSource;

Raw Paste


Login or Register to edit or fork this paste. It's free.