Initial commit
[yaffs-website] / node_modules / faye-websocket / lib / faye / websocket / api.js
1 var Stream      = require('stream').Stream,
2     util        = require('util'),
3     driver      = require('websocket-driver'),
4     EventTarget = require('./api/event_target'),
5     Event       = require('./api/event');
6
7 var API = function(options) {
8   options = options || {};
9   driver.validateOptions(options, ['headers', 'extensions', 'maxLength', 'ping', 'proxy', 'tls', 'ca']);
10
11   this.readable = this.writable = true;
12
13   var headers = options.headers;
14   if (headers) {
15     for (var name in headers) this._driver.setHeader(name, headers[name]);
16   }
17
18   var extensions = options.extensions;
19   if (extensions) {
20     [].concat(extensions).forEach(this._driver.addExtension, this._driver);
21   }
22
23   this._ping          = options.ping;
24   this._pingId        = 0;
25   this.readyState     = API.CONNECTING;
26   this.bufferedAmount = 0;
27   this.protocol       = '';
28   this.url            = this._driver.url;
29   this.version        = this._driver.version;
30
31   var self = this;
32
33   this._driver.on('open',    function(e) { self._open() });
34   this._driver.on('message', function(e) { self._receiveMessage(e.data) });
35   this._driver.on('close',   function(e) { self._beginClose(e.reason, e.code) });
36
37   this._driver.on('error', function(error) {
38     self._emitError(error.message);
39   });
40   this.on('error', function() {});
41
42   this._driver.messages.on('drain', function() {
43     self.emit('drain');
44   });
45
46   if (this._ping)
47     this._pingTimer = setInterval(function() {
48       self._pingId += 1;
49       self.ping(self._pingId.toString());
50     }, this._ping * 1000);
51
52   this._configureStream();
53
54   if (!this._proxy) {
55     this._stream.pipe(this._driver.io);
56     this._driver.io.pipe(this._stream);
57   }
58 };
59 util.inherits(API, Stream);
60
61 API.CONNECTING = 0;
62 API.OPEN       = 1;
63 API.CLOSING    = 2;
64 API.CLOSED     = 3;
65
66 var instance = {
67   write: function(data) {
68     return this.send(data);
69   },
70
71   end: function(data) {
72     if (data !== undefined) this.send(data);
73     this.close();
74   },
75
76   pause: function() {
77     return this._driver.messages.pause();
78   },
79
80   resume: function() {
81     return this._driver.messages.resume();
82   },
83
84   send: function(data) {
85     if (this.readyState > API.OPEN) return false;
86     if (!(data instanceof Buffer)) data = String(data);
87     return this._driver.messages.write(data);
88   },
89
90   ping: function(message, callback) {
91     if (this.readyState > API.OPEN) return false;
92     return this._driver.ping(message, callback);
93   },
94
95   close: function(code, reason) {
96     if (code === undefined) code = 1000;
97     if (reason === undefined) reason = '';
98
99     if (code !== 1000 && (code < 3000 || code > 4999))
100       throw new Error("Failed to execute 'close' on WebSocket: " +
101                       "The code must be either 1000, or between 3000 and 4999. " +
102                       code + " is neither.");
103
104     if (this.readyState !== API.CLOSED) this.readyState = API.CLOSING;
105     this._driver.close(reason, code);
106   },
107
108   _configureStream: function() {
109     var self = this;
110
111     this._stream.setTimeout(0);
112     this._stream.setNoDelay(true);
113
114     ['close', 'end'].forEach(function(event) {
115       this._stream.on(event, function() { self._finalizeClose() });
116     }, this);
117
118     this._stream.on('error', function(error) {
119       self._emitError('Network error: ' + self.url + ': ' + error.message);
120       self._finalizeClose();
121     });
122   },
123
124  _open: function() {
125     if (this.readyState !== API.CONNECTING) return;
126
127     this.readyState = API.OPEN;
128     this.protocol = this._driver.protocol || '';
129
130     var event = new Event('open');
131     event.initEvent('open', false, false);
132     this.dispatchEvent(event);
133   },
134
135   _receiveMessage: function(data) {
136     if (this.readyState > API.OPEN) return false;
137
138     if (this.readable) this.emit('data', data);
139
140     var event = new Event('message', {data: data});
141     event.initEvent('message', false, false);
142     this.dispatchEvent(event);
143   },
144
145   _emitError: function(message) {
146     if (this.readyState >= API.CLOSING) return;
147
148     var event = new Event('error', {message: message});
149     event.initEvent('error', false, false);
150     this.dispatchEvent(event);
151   },
152
153   _beginClose: function(reason, code) {
154     if (this.readyState === API.CLOSED) return;
155     this.readyState = API.CLOSING;
156     this._closeParams = [reason, code];
157
158     if (this._stream) {
159       this._stream.end();
160       if (!this._stream.readable) this._finalizeClose();
161     }
162   },
163
164   _finalizeClose: function() {
165     if (this.readyState === API.CLOSED) return;
166     this.readyState = API.CLOSED;
167
168     if (this._pingTimer) clearInterval(this._pingTimer);
169     if (this._stream) this._stream.end();
170
171     if (this.readable) this.emit('end');
172     this.readable = this.writable = false;
173
174     var reason = this._closeParams ? this._closeParams[0] : '',
175         code   = this._closeParams ? this._closeParams[1] : 1006;
176
177     var event = new Event('close', {code: code, reason: reason});
178     event.initEvent('close', false, false);
179     this.dispatchEvent(event);
180   }
181 };
182
183 for (var method in instance) API.prototype[method] = instance[method];
184 for (var key in EventTarget) API.prototype[key] = EventTarget[key];
185
186 module.exports = API;