http: Only send connection:keep-alive if necessary
[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' || method === 'HEAD' || method === 'CONNECT') {
91     self.useChunkedEncodingByDefault = false;
92   } else {
93     self.useChunkedEncodingByDefault = true;
94   }
95
96   if (util.isArray(options.headers)) {
97     self._storeHeader(self.method + ' ' + self.path + ' HTTP/1.1\r\n',
98                       options.headers);
99   } else if (self.getHeader('expect')) {
100     self._storeHeader(self.method + ' ' + self.path + ' HTTP/1.1\r\n',
101                       self._renderHeaders());
102   }
103
104   if (self.socketPath) {
105     self._last = true;
106     self.shouldKeepAlive = false;
107     var conn = self.agent.createConnection({ path: self.socketPath });
108     self.onSocket(conn);
109   } else if (self.agent) {
110     // If there is an agent we should default to Connection:keep-alive,
111     // but only if the Agent will actually reuse the connection!
112     // If it's not a keepAlive agent, and the maxSockets==Infinity, then
113     // there's never a case where this socket will actually be reused
114     var agent = self.agent;
115     if (!agent.keepAlive && !Number.isFinite(agent.maxSockets)) {
116       self._last = true;
117       self.shouldKeepAlive = false;
118     } else {
119       self._last = false;
120       self.shouldKeepAlive = true;
121     }
122     self.agent.addRequest(self, options);
123   } else {
124     // No agent, default to Connection:close.
125     self._last = true;
126     self.shouldKeepAlive = false;
127     if (options.createConnection) {
128       var conn = options.createConnection(options);
129     } else {
130       debug('CLIENT use net.createConnection', options);
131       var conn = net.createConnection(options);
132     }
133     self.onSocket(conn);
134   }
135
136   self._deferToConnect(null, null, function() {
137     self._flush();
138     self = null;
139   });
140 }
141
142 util.inherits(ClientRequest, OutgoingMessage);
143
144 exports.ClientRequest = ClientRequest;
145
146 ClientRequest.prototype._finish = function() {
147   DTRACE_HTTP_CLIENT_REQUEST(this, this.connection);
148   COUNTER_HTTP_CLIENT_REQUEST();
149   OutgoingMessage.prototype._finish.call(this);
150 };
151
152 ClientRequest.prototype._implicitHeader = function() {
153   this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n',
154                     this._renderHeaders());
155 };
156
157 ClientRequest.prototype.abort = function() {
158   // If we're aborting, we don't care about any more response data.
159   if (this.res)
160     this.res._dump();
161   else
162     this.once('response', function(res) {
163       res._dump();
164     });
165
166   if (this.socket) {
167     // in-progress
168     this.socket.destroy();
169   } else {
170     // haven't been assigned a socket yet.
171     // this could be more efficient, it could
172     // remove itself from the pending requests
173     this._deferToConnect('destroy', []);
174   }
175 };
176
177
178 function createHangUpError() {
179   var error = new Error('socket hang up');
180   error.code = 'ECONNRESET';
181   return error;
182 }
183
184
185 function socketCloseListener() {
186   var socket = this;
187   var parser = socket.parser;
188   var req = socket._httpMessage;
189   debug('HTTP socket close');
190   req.emit('close');
191   if (req.res && req.res.readable) {
192     // Socket closed before we emitted 'end' below.
193     req.res.emit('aborted');
194     var res = req.res;
195     res.on('end', function() {
196       res.emit('close');
197     });
198     res.push(null);
199   } else if (!req.res && !req.socket._hadError) {
200     // This socket error fired before we started to
201     // receive a response. The error needs to
202     // fire on the request.
203     req.emit('error', createHangUpError());
204     req.socket._hadError = true;
205   }
206
207   // Too bad.  That output wasn't getting written.
208   // This is pretty terrible that it doesn't raise an error.
209   // Fixed better in v0.10
210   if (req.output)
211     req.output.length = 0;
212   if (req.outputEncodings)
213     req.outputEncodings.length = 0;
214
215   if (parser) {
216     parser.finish();
217     freeParser(parser, req);
218   }
219 }
220
221 function socketErrorListener(err) {
222   var socket = this;
223   var parser = socket.parser;
224   var req = socket._httpMessage;
225   debug('SOCKET ERROR:', err.message, err.stack);
226
227   if (req) {
228     req.emit('error', err);
229     // For Safety. Some additional errors might fire later on
230     // and we need to make sure we don't double-fire the error event.
231     req.socket._hadError = true;
232   }
233
234   if (parser) {
235     parser.finish();
236     freeParser(parser, req);
237   }
238   socket.destroy();
239 }
240
241 function socketOnEnd() {
242   var socket = this;
243   var req = this._httpMessage;
244   var parser = this.parser;
245
246   if (!req.res && !req.socket._hadError) {
247     // If we don't have a response then we know that the socket
248     // ended prematurely and we need to emit an error on the request.
249     req.emit('error', createHangUpError());
250     req.socket._hadError = true;
251   }
252   if (parser) {
253     parser.finish();
254     freeParser(parser, req);
255   }
256   socket.destroy();
257 }
258
259 function socketOnData(d) {
260   var socket = this;
261   var req = this._httpMessage;
262   var parser = this.parser;
263
264   assert(parser);
265
266   var ret = parser.execute(d);
267   if (ret instanceof Error) {
268     debug('parse error');
269     freeParser(parser, req);
270     socket.destroy();
271     req.emit('error', ret);
272     req.socket._hadError = true;
273   } else if (parser.incoming && parser.incoming.upgrade) {
274     // Upgrade or CONNECT
275     var bytesParsed = ret;
276     var res = parser.incoming;
277     req.res = res;
278
279     socket.removeListener('data', socketOnData);
280     socket.removeListener('end', socketOnEnd);
281     parser.finish();
282
283     var bodyHead = d.slice(bytesParsed, d.length);
284
285     var eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade';
286     if (EventEmitter.listenerCount(req, eventName) > 0) {
287       req.upgradeOrConnect = true;
288
289       // detach the socket
290       socket.emit('agentRemove');
291       socket.removeListener('close', socketCloseListener);
292       socket.removeListener('error', socketErrorListener);
293
294       // TODO(isaacs): Need a way to reset a stream to fresh state
295       // IE, not flowing, and not explicitly paused.
296       socket._readableState.flowing = null;
297
298       req.emit(eventName, res, socket, bodyHead);
299       req.emit('close');
300     } else {
301       // Got Upgrade header or CONNECT method, but have no handler.
302       socket.destroy();
303     }
304     freeParser(parser, req);
305   } else if (parser.incoming && parser.incoming.complete &&
306              // When the status code is 100 (Continue), the server will
307              // send a final response after this client sends a request
308              // body. So, we must not free the parser.
309              parser.incoming.statusCode !== 100) {
310     socket.removeListener('data', socketOnData);
311     socket.removeListener('end', socketOnEnd);
312     freeParser(parser, req);
313   }
314 }
315
316
317 // client
318 function parserOnIncomingClient(res, shouldKeepAlive) {
319   var socket = this.socket;
320   var req = socket._httpMessage;
321
322
323   // propogate "domain" setting...
324   if (req.domain && !res.domain) {
325     debug('setting "res.domain"');
326     res.domain = req.domain;
327   }
328
329   debug('AGENT incoming response!');
330
331   if (req.res) {
332     // We already have a response object, this means the server
333     // sent a double response.
334     socket.destroy();
335     return;
336   }
337   req.res = res;
338
339   // Responses to CONNECT request is handled as Upgrade.
340   if (req.method === 'CONNECT') {
341     res.upgrade = true;
342     return true; // skip body
343   }
344
345   // Responses to HEAD requests are crazy.
346   // HEAD responses aren't allowed to have an entity-body
347   // but *can* have a content-length which actually corresponds
348   // to the content-length of the entity-body had the request
349   // been a GET.
350   var isHeadResponse = req.method == 'HEAD';
351   debug('AGENT isHeadResponse', isHeadResponse);
352
353   if (res.statusCode == 100) {
354     // restart the parser, as this is a continue message.
355     delete req.res; // Clear res so that we don't hit double-responses.
356     req.emit('continue');
357     return true;
358   }
359
360   if (req.shouldKeepAlive && !shouldKeepAlive && !req.upgradeOrConnect) {
361     // Server MUST respond with Connection:keep-alive for us to enable it.
362     // If we've been upgraded (via WebSockets) we also shouldn't try to
363     // keep the connection open.
364     req.shouldKeepAlive = false;
365   }
366
367
368   DTRACE_HTTP_CLIENT_RESPONSE(socket, req);
369   COUNTER_HTTP_CLIENT_RESPONSE();
370   req.res = res;
371   res.req = req;
372
373   // add our listener first, so that we guarantee socket cleanup
374   res.on('end', responseOnEnd);
375   var handled = req.emit('response', res);
376
377   // If the user did not listen for the 'response' event, then they
378   // can't possibly read the data, so we ._dump() it into the void
379   // so that the socket doesn't hang there in a paused state.
380   if (!handled)
381     res._dump();
382
383   return isHeadResponse;
384 }
385
386 // client
387 function responseOnEnd() {
388   var res = this;
389   var req = res.req;
390   var socket = req.socket;
391
392   if (!req.shouldKeepAlive) {
393     if (socket.writable) {
394       debug('AGENT socket.destroySoon()');
395       socket.destroySoon();
396     }
397     assert(!socket.writable);
398   } else {
399     debug('AGENT socket keep-alive');
400     if (req.timeoutCb) {
401       socket.setTimeout(0, req.timeoutCb);
402       req.timeoutCb = null;
403     }
404     socket.removeListener('close', socketCloseListener);
405     socket.removeListener('error', socketErrorListener);
406     // Mark this socket as available, AFTER user-added end
407     // handlers have a chance to run.
408     process.nextTick(function() {
409       socket.emit('free');
410     });
411   }
412 }
413
414 ClientRequest.prototype.onSocket = function(socket) {
415   var req = this;
416
417   process.nextTick(function() {
418     var parser = parsers.alloc();
419     req.socket = socket;
420     req.connection = socket;
421     parser.reinitialize(HTTPParser.RESPONSE);
422     parser.socket = socket;
423     parser.incoming = null;
424     req.parser = parser;
425
426     socket.parser = parser;
427     socket._httpMessage = req;
428
429     // Setup "drain" propogation.
430     httpSocketSetup(socket);
431
432     // Propagate headers limit from request object to parser
433     if (util.isNumber(req.maxHeadersCount)) {
434       parser.maxHeaderPairs = req.maxHeadersCount << 1;
435     } else {
436       // Set default value because parser may be reused from FreeList
437       parser.maxHeaderPairs = 2000;
438     }
439
440     parser.onIncoming = parserOnIncomingClient;
441     socket.on('error', socketErrorListener);
442     socket.on('data', socketOnData);
443     socket.on('end', socketOnEnd);
444     socket.on('close', socketCloseListener);
445     req.emit('socket', socket);
446   });
447
448 };
449
450 ClientRequest.prototype._deferToConnect = function(method, arguments_, cb) {
451   // This function is for calls that need to happen once the socket is
452   // connected and writable. It's an important promisy thing for all the socket
453   // calls that happen either now (when a socket is assigned) or
454   // in the future (when a socket gets assigned out of the pool and is
455   // eventually writable).
456   var self = this;
457   var onSocket = function() {
458     if (self.socket.writable) {
459       if (method) {
460         self.socket[method].apply(self.socket, arguments_);
461       }
462       if (cb) { cb(); }
463     } else {
464       self.socket.once('connect', function() {
465         if (method) {
466           self.socket[method].apply(self.socket, arguments_);
467         }
468         if (cb) { cb(); }
469       });
470     }
471   }
472   if (!self.socket) {
473     self.once('socket', onSocket);
474   } else {
475     onSocket();
476   }
477 };
478
479 ClientRequest.prototype.setTimeout = function(msecs, callback) {
480   if (callback) this.once('timeout', callback);
481
482   var self = this;
483   function emitTimeout() {
484     self.emit('timeout');
485   }
486
487   if (this.socket && this.socket.writable) {
488     if (this.timeoutCb)
489       this.socket.setTimeout(0, this.timeoutCb);
490     this.timeoutCb = emitTimeout;
491     this.socket.setTimeout(msecs, emitTimeout);
492     return;
493   }
494
495   // Set timeoutCb so that it'll get cleaned up on request end
496   this.timeoutCb = emitTimeout;
497   if (this.socket) {
498     var sock = this.socket;
499     this.socket.once('connect', function() {
500       sock.setTimeout(msecs, emitTimeout);
501     });
502     return;
503   }
504
505   this.once('socket', function(sock) {
506     sock.setTimeout(msecs, emitTimeout);
507   });
508 };
509
510 ClientRequest.prototype.setNoDelay = function() {
511   this._deferToConnect('setNoDelay', arguments);
512 };
513 ClientRequest.prototype.setSocketKeepAlive = function() {
514   this._deferToConnect('setKeepAlive', arguments);
515 };
516
517 ClientRequest.prototype.clearTimeout = function(cb) {
518   this.setTimeout(0, cb);
519 };