comunicationLeap/node_modules/engine.io/lib/transports/polling.js
node_modules/engine.io/lib/transports/polling.jsRaw
1
2/**
3 * Module requirements.
4 */
5
6var Transport = require('../transport')
7 , parser = require('engine.io-parser')
8 , debug = require('debug')('engine:polling');
9
10/**
11 * Exports the constructor.
12 */
13
14module.exports = Polling;
15
16/**
17 * HTTP polling constructor.
18 *
19 * @api public.
20 */
21
22function Polling (req) {
23 Transport.call(this, req);
24}
25
26/**
27 * Inherits from Transport.
28 *
29 * @api public.
30 */
31
32Polling.prototype.__proto__ = Transport.prototype;
33
34/**
35 * Transport name
36 *
37 * @api public
38 */
39
40Polling.prototype.name = 'polling';
41
42/**
43 * Overrides onRequest.
44 *
45 * @param {http.ServerRequest}
46 * @api private
47 */
48
49Polling.prototype.onRequest = function (req) {
50 var res = req.res;
51
52 if ('GET' == req.method) {
53 this.onPollRequest(req, res);
54 } else if ('POST' == req.method) {
55 this.onDataRequest(req, res);
56 } else {
57 res.writeHead(500);
58 res.end();
59 }
60};
61
62/**
63 * The client sends a request awaiting for us to send data.
64 *
65 * @api private
66 */
67
68Polling.prototype.onPollRequest = function (req, res) {
69 if (this.req) {
70 debug('request overlap');
71 // assert: this.res, '.req and .res should be (un)set together'
72 this.onError('overlap from client');
73 res.writeHead(500);
74 return;
75 }
76
77 debug('setting request');
78
79 this.req = req;
80 this.res = res;
81
82 var self = this;
83
84 function onClose () {
85 self.onError('poll connection closed prematurely');
86 }
87
88 function cleanup () {
89 req.removeListener('close', onClose);
90 self.req = self.res = null;
91 }
92
93 req.cleanup = cleanup;
94 req.on('close', onClose);
95
96 this.writable = true;
97 this.emit('drain');
98
99 // if we're still writable but had a pending close, trigger an empty send
100 if (this.writable && this.shouldClose) {
101 debug('triggering empty send to append close packet');
102 this.send([{ type: 'noop' }]);
103 }
104};
105
106/**
107 * The client sends a request with data.
108 *
109 * @api private
110 */
111
112Polling.prototype.onDataRequest = function (req, res) {
113 if (this.dataReq) {
114 // assert: this.dataRes, '.dataReq and .dataRes should be (un)set together'
115 this.onError('data request overlap from client');
116 res.writeHead(500);
117 return;
118 }
119
120 var isBinary = 'application/octet-stream' == req.headers['content-type'];
121
122 this.dataReq = req;
123 this.dataRes = res;
124
125 var chunks = isBinary ? new Buffer(0) : '';
126 var self = this;
127
128 function cleanup () {
129 chunks = isBinary ? new Buffer(0) : '';
130 req.removeListener('data', onData);
131 req.removeListener('end', onEnd);
132 req.removeListener('close', onClose);
133 self.dataReq = self.dataRes = null;
134 }
135
136 function onClose () {
137 cleanup();
138 self.onError('data request connection closed prematurely');
139 }
140
141 function onData (data) {
142 var contentLength;
143 if (typeof data == 'string') {
144 chunks += data;
145 contentLength = Buffer.byteLength(chunks);
146 } else {
147 chunks = Buffer.concat([chunks, data]);
148 contentLength = chunks.length;
149 }
150
151 if (contentLength > self.maxHttpBufferSize) {
152 chunks = '';
153 req.connection.destroy();
154 }
155 }
156
157 function onEnd () {
158 self.onData(chunks);
159
160 var headers = {
161 // text/html is required instead of text/plain to avoid an
162 // unwanted download dialog on certain user-agents (GH-43)
163 'Content-Type': 'text/html',
164 'Content-Length': 2
165 };
166
167 // prevent XSS warnings on IE
168 // https://github.com/LearnBoost/socket.io/pull/1333
169 var ua = req.headers['user-agent'];
170 if (ua && (~ua.indexOf(';MSIE') || ~ua.indexOf('Trident/'))) {
171 headers['X-XSS-Protection'] = '0';
172 }
173
174 res.writeHead(200, self.headers(req, headers));
175 res.end('ok');
176 cleanup();
177 }
178
179 req.abort = cleanup;
180 req.on('close', onClose);
181 req.on('data', onData);
182 req.on('end', onEnd);
183 if (!isBinary) req.setEncoding('utf8');
184};
185
186/**
187 * Processes the incoming data payload.
188 *
189 * @param {String} encoded payload
190 * @api private
191 */
192
193Polling.prototype.onData = function (data) {
194 debug('received "%s"', data);
195 var self = this;
196 var callback = function(packet) {
197 if ('close' == packet.type) {
198 debug('got xhr close packet');
199 self.onClose();
200 return false;
201 }
202
203 self.onPacket(packet);
204 };
205
206 parser.decodePayload(data, callback);
207};
208
209/**
210 * Writes a packet payload.
211 *
212 * @param {Object} packet
213 * @api private
214 */
215
216Polling.prototype.send = function (packets) {
217 if (this.shouldClose) {
218 debug('appending close packet to payload');
219 packets.push({ type: 'close' });
220 this.shouldClose();
221 this.shouldClose = null;
222 }
223
224 var self = this;
225 parser.encodePayload(packets, this.supportsBinary, function(data) {
226 self.write(data);
227 });
228};
229
230/**
231 * Writes data as response to poll request.
232 *
233 * @param {String} data
234 * @api private
235 */
236
237Polling.prototype.write = function (data) {
238 debug('writing "%s"', data);
239 this.doWrite(data);
240 this.req.cleanup();
241 this.writable = false;
242};
243
244/**
245 * Closes the transport.
246 *
247 * @api private
248 */
249
250Polling.prototype.doClose = function (fn) {
251 debug('closing');
252
253 if (this.dataReq) {
254 // FIXME: should we do this?
255 debug('aborting ongoing data request');
256 this.dataReq.abort();
257 }
258
259 if (this.writable) {
260 debug('transport writable - closing right away');
261 this.send([{ type: 'close' }]);
262 fn();
263 } else {
264 debug('transport not writable - buffering orderly close');
265 this.shouldClose = fn;
266 }
267};