Initial commit
[yaffs-website] / node_modules / faye-websocket / lib / faye / eventsource.js
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;