ef15ae10a39da5891deadf800f7acf3157053411
[platform/upstream/nodejs.git] / lib / _http_client.js
1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 var util = require('util');
23 var net = require('net');
24 var EventEmitter = require('events').EventEmitter;
25 var HTTPParser = process.binding('http_parser').HTTPParser;
26 var assert = require('assert').ok;
27
28 var common = require('_http_common');
29
30 var httpSocketSetup = common.httpSocketSetup;
31 var parsers = common.parsers;
32 var freeParser = common.freeParser;
33 var debug = common.debug;
34
35 var IncomingMessage = require('_http_incoming').IncomingMessage;
36 var OutgoingMessage = require('_http_outgoing').OutgoingMessage;
37
38 var agent = require('_http_agent');
39 var globalAgent = agent.globalAgent;
40
41
42 function ClientRequest(options, cb) {
43   var self = this;
44   OutgoingMessage.call(self);
45
46   options = util._extend({}, options);
47
48   self.agent = util.isUndefined(options.agent) ? globalAgent : options.agent;
49
50   var defaultPort = options.defaultPort || self.agent.defaultPort;
51
52   var port = options.port = options.port || defaultPort;
53   var host = options.host = options.hostname || options.host || 'localhost';
54
55   if (util.isUndefined(options.setHost)) {
56     var setHost = true;
57   }
58
59   self.socketPath = options.socketPath;
60
61   var method = self.method = (options.method || 'GET').toUpperCase();
62   self.path = options.path || '/';
63   if (cb) {
64     self.once('response', cb);
65   }
66
67   if (!util.isArray(options.headers)) {
68     if (options.headers) {
69       var keys = Object.keys(options.headers);
70       for (var i = 0, l = keys.length; i < l; i++) {
71         var key = keys[i];
72         self.setHeader(key, options.headers[key]);
73       }
74     }
75     if (host && !this.getHeader('host') && setHost) {
76       var hostHeader = host;
77       if (port && +port !== defaultPort) {
78         hostHeader += ':' + port;
79       }
80       this.setHeader('Host', hostHeader);
81     }
82   }
83
84   if (options.auth && !this.getHeader('Authorization')) {
85     //basic auth
86     this.setHeader('Authorization', 'Basic ' +
87                    new Buffer(options.auth).toString('base64'));
88   }
89
90   if (method === 'GET' ||
91       method === 'HEAD' ||
92       method === 'DELETE' ||
93       method === 'CONNECT') {
94     self.useChunkedEncodingByDefault = false;
95   } else {
96     self.useChunkedEncodingByDefault = true;
97   }
98
99   if (util.isArray(options.headers)) {
100     self._storeHeader(self.method + ' ' + self.path + ' HTTP/1.1\r\n',
101                       options.headers);
102   } else if (self.getHeader('expect')) {
103     self._storeHeader(self.method + ' ' + self.path + ' HTTP/1.1\r\n',
104                       self._renderHeaders());
105   }
106
107   if (self.socketPath) {
108     self._last = true;
109     self.shouldKeepAlive = false;
110     var conn = self.agent.createConnection({ path: self.socketPath });
111     self.onSocket(conn);
112   } else if (self.agent) {
113     // If there is an agent we should default to Connection:keep-alive,
114     // but only if the Agent will actually reuse the connection!
115     // If it's not a keepAlive agent, and the maxSockets==Infinity, then
116     // there's never a case where this socket will actually be reused
117     var agent = self.agent;
118     if (!agent.keepAlive && !Number.isFinite(agent.maxSockets)) {
119       self._last = true;
120       self.shouldKeepAlive = false;
121     } else {
122       self._last = false;
123       self.shouldKeepAlive = true;
124     }
125     self.agent.addRequest(self, options);
126   } else {
127     // No agent, default to Connection:close.
128     self._last = true;
129     self.shouldKeepAlive = false;
130     if (options.createConnection) {
131       var conn = options.createConnection(options);
132     } else {
133       debug('CLIENT use net.createConnection', options);
134       var conn = net.createConnection(options);
135     }
136     self.onSocket(conn);
137   }
138
139   self._deferToConnect(null, null, function() {
140     self._flush();
141     self = null;
142   });
143 }
144
145 util.inherits(ClientRequest, OutgoingMessage);
146
147 exports.ClientRequest = ClientRequest;
148
149 ClientRequest.prototype._finish = function() {
150   DTRACE_HTTP_CLIENT_REQUEST(this, this.connection);
151   COUNTER_HTTP_CLIENT_REQUEST();
152   OutgoingMessage.prototype._finish.call(this);
153 };
154
155 ClientRequest.prototype._implicitHeader = function() {
156   this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n',
157                     this._renderHeaders());
158 };
159
160 ClientRequest.prototype.abort = function() {
161   // If we're aborting, we don't care about any more response data.
162   if (this.res)
163     this.res._dump();
164   else
165     this.once('response', function(res) {
166       res._dump();
167     });
168
169   if (this.socket) {
170     // in-progress
171     this.socket.destroy();
172   } else {
173     // haven't been assigned a socket yet.
174     // this could be more efficient, it could
175     // remove itself from the pending requests
176     this._deferToConnect('destroy', []);
177   }
178 };
179
180
181 function createHangUpError() {
182   var error = new Error('socket hang up');
183   error.code = 'ECONNRESET';
184   return error;
185 }
186
187
188 function socketCloseListener() {
189   var socket = this;
190   var req = socket._httpMessage;
191   debug('HTTP socket close');
192
193   // Pull through final chunk, if anything is buffered.
194   // the ondata function will handle it properly, and this
195   // is a no-op if no final chunk remains.
196   socket.read();
197
198   // NOTE: Its important to get parser here, because it could be freed by
199   // the `socketOnData`.
200   var parser = socket.parser;
201   req.emit('close');
202   if (req.res && req.res.readable) {
203     // Socket closed before we emitted 'end' below.
204     req.res.emit('aborted');
205     var res = req.res;
206     res.on('end', function() {
207       res.emit('close');
208     });
209     res.push(null);
210   } else if (!req.res && !req.socket._hadError) {
211     // This socket error fired before we started to
212     // receive a response. The error needs to
213     // fire on the request.
214     req.emit('error', createHangUpError());
215     req.socket._hadError = true;
216   }
217
218   // Too bad.  That output wasn't getting written.
219   // This is pretty terrible that it doesn't raise an error.
220   // Fixed better in v0.10
221   if (req.output)
222     req.output.length = 0;
223   if (req.outputEncodings)
224     req.outputEncodings.length = 0;
225
226   if (parser) {
227     parser.finish();
228     freeParser(parser, req);
229   }
230 }
231
232 function socketErrorListener(err) {
233   var socket = this;
234   var parser = socket.parser;
235   var req = socket._httpMessage;
236   debug('SOCKET ERROR:', err.message, err.stack);
237
238   if (req) {
239     req.emit('error', err);
240     // For Safety. Some additional errors might fire later on
241     // and we need to make sure we don't double-fire the error event.
242     req.socket._hadError = true;
243   }
244
245   if (parser) {
246     parser.finish();
247     freeParser(parser, req);
248   }
249   socket.destroy();
250 }
251
252 function socketOnEnd() {
253   var socket = this;
254   var req = this._httpMessage;
255   var parser = this.parser;
256
257   if (!req.res && !req.socket._hadError) {
258     // If we don't have a response then we know that the socket
259     // ended prematurely and we need to emit an error on the request.
260     req.emit('error', createHangUpError());
261     req.socket._hadError = true;
262   }
263   if (parser) {
264     parser.finish();
265     freeParser(parser, req);
266   }
267   socket.destroy();
268 }
269
270 function socketOnData(d) {
271   var socket = this;
272   var req = this._httpMessage;
273   var parser = this.parser;
274
275   assert(parser && parser.socket === socket);
276
277   var ret = parser.execute(d);
278   if (ret instanceof Error) {
279     debug('parse error');
280     freeParser(parser, req);
281     socket.destroy();
282     req.emit('error', ret);
283     req.socket._hadError = true;
284   } else if (parser.incoming && parser.incoming.upgrade) {
285     // Upgrade or CONNECT
286     var bytesParsed = ret;
287     var res = parser.incoming;
288     req.res = res;
289
290     socket.removeListener('data', socketOnData);
291     socket.removeListener('end', socketOnEnd);
292     parser.finish();
293
294     var bodyHead = d.slice(bytesParsed, d.length);
295
296     var eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade';
297     if (EventEmitter.listenerCount(req, eventName) > 0) {
298       req.upgradeOrConnect = true;
299
300       // detach the socket
301       socket.emit('agentRemove');
302       socket.removeListener('close', socketCloseListener);
303       socket.removeListener('error', socketErrorListener);
304
305       // TODO(isaacs): Need a way to reset a stream to fresh state
306       // IE, not flowing, and not explicitly paused.
307       socket._readableState.flowing = null;
308
309       req.emit(eventName, res, socket, bodyHead);
310       req.emit('close');
311     } else {
312       // Got Upgrade header or CONNECT method, but have no handler.
313       socket.destroy();
314     }
315     freeParser(parser, req);
316   } else if (parser.incoming && parser.incoming.complete &&
317              // When the status code is 100 (Continue), the server will
318              // send a final response after this client sends a request
319              // body. So, we must not free the parser.
320              parser.incoming.statusCode !== 100) {
321     socket.removeListener('data', socketOnData);
322     socket.removeListener('end', socketOnEnd);
323     freeParser(parser, req);
324   }
325 }
326
327
328 // client
329 function parserOnIncomingClient(res, shouldKeepAlive) {
330   var socket = this.socket;
331   var req = socket._httpMessage;
332
333
334   // propogate "domain" setting...
335   if (req.domain && !res.domain) {
336     debug('setting "res.domain"');
337     res.domain = req.domain;
338   }
339
340   debug('AGENT incoming response!');
341
342   if (req.res) {
343     // We already have a response object, this means the server
344     // sent a double response.
345     socket.destroy();
346     return;
347   }
348   req.res = res;
349
350   // Responses to CONNECT request is handled as Upgrade.
351   if (req.method === 'CONNECT') {
352     res.upgrade = true;
353     return true; // skip body
354   }
355
356   // Responses to HEAD requests are crazy.
357   // HEAD responses aren't allowed to have an entity-body
358   // but *can* have a content-length which actually corresponds
359   // to the content-length of the entity-body had the request
360   // been a GET.
361   var isHeadResponse = req.method == 'HEAD';
362   debug('AGENT isHeadResponse', isHeadResponse);
363
364   if (res.statusCode == 100) {
365     // restart the parser, as this is a continue message.
366     delete req.res; // Clear res so that we don't hit double-responses.
367     req.emit('continue');
368     return true;
369   }
370
371   if (req.shouldKeepAlive && !shouldKeepAlive && !req.upgradeOrConnect) {
372     // Server MUST respond with Connection:keep-alive for us to enable it.
373     // If we've been upgraded (via WebSockets) we also shouldn't try to
374     // keep the connection open.
375     req.shouldKeepAlive = false;
376   }
377
378
379   DTRACE_HTTP_CLIENT_RESPONSE(socket, req);
380   COUNTER_HTTP_CLIENT_RESPONSE();
381   req.res = res;
382   res.req = req;
383
384   // add our listener first, so that we guarantee socket cleanup
385   res.on('end', responseOnEnd);
386   var handled = req.emit('response', res);
387
388   // If the user did not listen for the 'response' event, then they
389   // can't possibly read the data, so we ._dump() it into the void
390   // so that the socket doesn't hang there in a paused state.
391   if (!handled)
392     res._dump();
393
394   return isHeadResponse;
395 }
396
397 // client
398 function responseOnEnd() {
399   var res = this;
400   var req = res.req;
401   var socket = req.socket;
402
403   if (!req.shouldKeepAlive) {
404     if (socket.writable) {
405       debug('AGENT socket.destroySoon()');
406       socket.destroySoon();
407     }
408     assert(!socket.writable);
409   } else {
410     debug('AGENT socket keep-alive');
411     if (req.timeoutCb) {
412       socket.setTimeout(0, req.timeoutCb);
413       req.timeoutCb = null;
414     }
415     socket.removeListener('close', socketCloseListener);
416     socket.removeListener('error', socketErrorListener);
417     // Mark this socket as available, AFTER user-added end
418     // handlers have a chance to run.
419     process.nextTick(function() {
420       socket.emit('free');
421     });
422   }
423 }
424
425 function tickOnSocket(req, socket) {
426   var parser = parsers.alloc();
427   req.socket = socket;
428   req.connection = socket;
429   parser.reinitialize(HTTPParser.RESPONSE);
430   parser.socket = socket;
431   parser.incoming = null;
432   req.parser = parser;
433
434   socket.parser = parser;
435   socket._httpMessage = req;
436
437   // Setup "drain" propogation.
438   httpSocketSetup(socket);
439
440   // Propagate headers limit from request object to parser
441   if (util.isNumber(req.maxHeadersCount)) {
442     parser.maxHeaderPairs = req.maxHeadersCount << 1;
443   } else {
444     // Set default value because parser may be reused from FreeList
445     parser.maxHeaderPairs = 2000;
446   }
447
448   parser.onIncoming = parserOnIncomingClient;
449   socket.on('error', socketErrorListener);
450   socket.on('data', socketOnData);
451   socket.on('end', socketOnEnd);
452   socket.on('close', socketCloseListener);
453   req.emit('socket', socket);
454 }
455
456 ClientRequest.prototype.onSocket = function(socket) {
457   var req = this;
458
459   process.nextTick(function() {
460     // If a domain was added to the request, attach it to the socket.
461     if (req.domain)
462       socket._handle.addAsyncListener(req.domain._listener);
463     tickOnSocket(req, socket);
464   });
465
466 };
467
468 ClientRequest.prototype._deferToConnect = function(method, arguments_, cb) {
469   // This function is for calls that need to happen once the socket is
470   // connected and writable. It's an important promisy thing for all the socket
471   // calls that happen either now (when a socket is assigned) or
472   // in the future (when a socket gets assigned out of the pool and is
473   // eventually writable).
474   var self = this;
475   var onSocket = function() {
476     if (self.socket.writable) {
477       if (method) {
478         self.socket[method].apply(self.socket, arguments_);
479       }
480       if (cb) { cb(); }
481     } else {
482       self.socket.once('connect', function() {
483         if (method) {
484           self.socket[method].apply(self.socket, arguments_);
485         }
486         if (cb) { cb(); }
487       });
488     }
489   }
490   if (!self.socket) {
491     self.once('socket', onSocket);
492   } else {
493     onSocket();
494   }
495 };
496
497 ClientRequest.prototype.setTimeout = function(msecs, callback) {
498   if (callback) this.once('timeout', callback);
499
500   var self = this;
501   function emitTimeout() {
502     self.emit('timeout');
503   }
504
505   if (this.socket && this.socket.writable) {
506     if (this.timeoutCb)
507       this.socket.setTimeout(0, this.timeoutCb);
508     this.timeoutCb = emitTimeout;
509     this.socket.setTimeout(msecs, emitTimeout);
510     return;
511   }
512
513   // Set timeoutCb so that it'll get cleaned up on request end
514   this.timeoutCb = emitTimeout;
515   if (this.socket) {
516     var sock = this.socket;
517     this.socket.once('connect', function() {
518       sock.setTimeout(msecs, emitTimeout);
519     });
520     return;
521   }
522
523   this.once('socket', function(sock) {
524     sock.setTimeout(msecs, emitTimeout);
525   });
526 };
527
528 ClientRequest.prototype.setNoDelay = function() {
529   this._deferToConnect('setNoDelay', arguments);
530 };
531 ClientRequest.prototype.setSocketKeepAlive = function() {
532   this._deferToConnect('setKeepAlive', arguments);
533 };
534
535 ClientRequest.prototype.clearTimeout = function(cb) {
536   this.setTimeout(0, cb);
537 };