2011-03-10 00:54:52 -08:00
|
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
|
|
//
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
// copy of this software and associated documentation files (the
|
|
|
|
// "Software"), to deal in the Software without restriction, including
|
|
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
|
|
// persons to whom the Software is furnished to do so, subject to the
|
|
|
|
// following conditions:
|
|
|
|
//
|
|
|
|
// The above copyright notice and this permission notice shall be included
|
|
|
|
// in all copies or substantial portions of the Software.
|
|
|
|
//
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
|
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
2010-10-12 08:04:09 +11:00
|
|
|
var util = require('util');
|
2010-11-29 07:41:08 -08:00
|
|
|
var net = require('net');
|
2012-06-12 21:53:08 +02:00
|
|
|
var Stream = require('stream');
|
2012-05-16 16:27:34 +02:00
|
|
|
var url = require('url');
|
2011-01-20 02:41:16 -08:00
|
|
|
var EventEmitter = require('events').EventEmitter;
|
2010-11-29 07:41:08 -08:00
|
|
|
var HTTPParser = process.binding('http_parser').HTTPParser;
|
2011-01-27 16:59:28 -08:00
|
|
|
var assert = require('assert').ok;
|
2010-03-19 21:49:00 -07:00
|
|
|
|
2010-05-26 17:59:55 -07:00
|
|
|
var debug;
|
2011-01-03 15:27:57 -08:00
|
|
|
if (process.env.NODE_DEBUG && /http/.test(process.env.NODE_DEBUG)) {
|
2010-12-01 18:07:20 -08:00
|
|
|
debug = function(x) { console.error('HTTP: %s', x); };
|
2010-05-26 17:59:55 -07:00
|
|
|
} else {
|
2010-12-01 18:07:20 -08:00
|
|
|
debug = function() { };
|
2010-03-19 21:49:00 -07:00
|
|
|
}
|
|
|
|
|
2013-04-11 15:00:45 -07:00
|
|
|
var incoming = require('_http_incoming');
|
|
|
|
var IncomingMessage = exports.IncomingMessage = incoming.IncomingMessage;
|
2013-01-07 20:33:56 -08:00
|
|
|
|
2010-03-19 19:22:04 -07:00
|
|
|
|
2013-04-11 15:15:41 -07:00
|
|
|
var common = require('_http_common');
|
|
|
|
var parsers = exports.parsers = common.parsers;
|
|
|
|
var freeParser = common.freeParser;
|
2010-03-15 13:48:03 -07:00
|
|
|
|
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
var CRLF = '\r\n';
|
2009-12-07 16:18:43 -08:00
|
|
|
var STATUS_CODES = exports.STATUS_CODES = {
|
2009-06-30 18:49:56 -04:00
|
|
|
100 : 'Continue',
|
|
|
|
101 : 'Switching Protocols',
|
2010-05-23 22:48:50 +02:00
|
|
|
102 : 'Processing', // RFC 2518, obsoleted by RFC 4918
|
2009-06-30 18:49:56 -04:00
|
|
|
200 : 'OK',
|
|
|
|
201 : 'Created',
|
|
|
|
202 : 'Accepted',
|
|
|
|
203 : 'Non-Authoritative Information',
|
|
|
|
204 : 'No Content',
|
|
|
|
205 : 'Reset Content',
|
|
|
|
206 : 'Partial Content',
|
2010-05-23 22:48:50 +02:00
|
|
|
207 : 'Multi-Status', // RFC 4918
|
2009-06-30 18:49:56 -04:00
|
|
|
300 : 'Multiple Choices',
|
|
|
|
301 : 'Moved Permanently',
|
|
|
|
302 : 'Moved Temporarily',
|
|
|
|
303 : 'See Other',
|
|
|
|
304 : 'Not Modified',
|
|
|
|
305 : 'Use Proxy',
|
2010-05-23 22:48:50 +02:00
|
|
|
307 : 'Temporary Redirect',
|
2009-06-30 18:49:56 -04:00
|
|
|
400 : 'Bad Request',
|
|
|
|
401 : 'Unauthorized',
|
|
|
|
402 : 'Payment Required',
|
|
|
|
403 : 'Forbidden',
|
|
|
|
404 : 'Not Found',
|
|
|
|
405 : 'Method Not Allowed',
|
|
|
|
406 : 'Not Acceptable',
|
|
|
|
407 : 'Proxy Authentication Required',
|
|
|
|
408 : 'Request Time-out',
|
|
|
|
409 : 'Conflict',
|
|
|
|
410 : 'Gone',
|
|
|
|
411 : 'Length Required',
|
|
|
|
412 : 'Precondition Failed',
|
|
|
|
413 : 'Request Entity Too Large',
|
|
|
|
414 : 'Request-URI Too Large',
|
|
|
|
415 : 'Unsupported Media Type',
|
2010-05-23 22:48:50 +02:00
|
|
|
416 : 'Requested Range Not Satisfiable',
|
|
|
|
417 : 'Expectation Failed',
|
|
|
|
418 : 'I\'m a teapot', // RFC 2324
|
|
|
|
422 : 'Unprocessable Entity', // RFC 4918
|
|
|
|
423 : 'Locked', // RFC 4918
|
|
|
|
424 : 'Failed Dependency', // RFC 4918
|
|
|
|
425 : 'Unordered Collection', // RFC 4918
|
|
|
|
426 : 'Upgrade Required', // RFC 2817
|
2011-12-18 16:13:51 -08:00
|
|
|
428 : 'Precondition Required', // RFC 6585
|
|
|
|
429 : 'Too Many Requests', // RFC 6585
|
|
|
|
431 : 'Request Header Fields Too Large',// RFC 6585
|
2009-06-30 18:49:56 -04:00
|
|
|
500 : 'Internal Server Error',
|
|
|
|
501 : 'Not Implemented',
|
|
|
|
502 : 'Bad Gateway',
|
|
|
|
503 : 'Service Unavailable',
|
|
|
|
504 : 'Gateway Time-out',
|
2013-02-26 15:17:26 +01:00
|
|
|
505 : 'HTTP Version Not Supported',
|
2010-05-23 22:48:50 +02:00
|
|
|
506 : 'Variant Also Negotiates', // RFC 2295
|
|
|
|
507 : 'Insufficient Storage', // RFC 4918
|
|
|
|
509 : 'Bandwidth Limit Exceeded',
|
2011-12-18 16:13:51 -08:00
|
|
|
510 : 'Not Extended', // RFC 2774
|
|
|
|
511 : 'Network Authentication Required' // RFC 6585
|
2009-06-30 18:49:56 -04:00
|
|
|
};
|
2009-05-11 19:08:29 +02:00
|
|
|
|
2010-11-29 07:41:08 -08:00
|
|
|
|
2010-03-23 21:31:44 -07:00
|
|
|
var connectionExpression = /Connection/i;
|
|
|
|
var transferEncodingExpression = /Transfer-Encoding/i;
|
|
|
|
var closeExpression = /close/i;
|
|
|
|
var chunkExpression = /chunk/i;
|
|
|
|
var contentLengthExpression = /Content-Length/i;
|
2012-02-15 07:38:24 +11:00
|
|
|
var dateExpression = /Date/i;
|
2010-09-10 11:56:35 +10:00
|
|
|
var expectExpression = /Expect/i;
|
|
|
|
var continueExpression = /100-continue/i;
|
2009-05-11 18:54:52 +02:00
|
|
|
|
2012-02-15 07:38:24 +11:00
|
|
|
var dateCache;
|
|
|
|
function utcDate() {
|
2012-02-18 15:01:35 -08:00
|
|
|
if (!dateCache) {
|
2012-02-15 07:38:24 +11:00
|
|
|
var d = new Date();
|
|
|
|
dateCache = d.toUTCString();
|
2012-02-18 15:01:35 -08:00
|
|
|
setTimeout(function() {
|
2012-02-15 07:38:24 +11:00
|
|
|
dateCache = undefined;
|
|
|
|
}, 1000 - d.getMilliseconds());
|
|
|
|
}
|
|
|
|
return dateCache;
|
|
|
|
}
|
|
|
|
|
2009-06-26 18:29:57 +02:00
|
|
|
|
2011-01-20 02:41:16 -08:00
|
|
|
function OutgoingMessage() {
|
2012-06-12 21:53:08 +02:00
|
|
|
Stream.call(this);
|
2010-03-03 13:06:19 -08:00
|
|
|
|
2009-07-14 18:31:50 +02:00
|
|
|
this.output = [];
|
2009-09-13 12:38:59 +02:00
|
|
|
this.outputEncodings = [];
|
2009-06-26 18:29:57 +02:00
|
|
|
|
2010-10-11 01:22:38 -07:00
|
|
|
this.writable = true;
|
|
|
|
|
2010-05-26 17:59:55 -07:00
|
|
|
this._last = false;
|
2010-03-23 21:31:44 -07:00
|
|
|
this.chunkedEncoding = false;
|
|
|
|
this.shouldKeepAlive = true;
|
|
|
|
this.useChunkedEncodingByDefault = true;
|
2012-02-15 07:38:24 +11:00
|
|
|
this.sendDate = false;
|
2009-07-14 11:59:13 +02:00
|
|
|
|
2010-04-21 15:15:21 -07:00
|
|
|
this._hasBody = true;
|
2010-09-09 16:29:35 +10:00
|
|
|
this._trailer = '';
|
2010-04-21 15:15:21 -07:00
|
|
|
|
2009-07-14 18:31:50 +02:00
|
|
|
this.finished = false;
|
2013-02-22 10:35:07 -08:00
|
|
|
this._hangupClose = false;
|
2013-03-09 18:46:39 -08:00
|
|
|
|
|
|
|
this.socket = null;
|
|
|
|
this.connection = null;
|
2009-07-16 10:59:40 +02:00
|
|
|
}
|
2012-06-12 21:53:08 +02:00
|
|
|
util.inherits(OutgoingMessage, Stream);
|
2010-11-29 07:41:08 -08:00
|
|
|
|
|
|
|
|
2009-11-05 00:02:15 +01:00
|
|
|
exports.OutgoingMessage = OutgoingMessage;
|
2009-07-14 11:59:13 +02:00
|
|
|
|
2010-10-11 16:36:12 -07:00
|
|
|
|
http: More useful setTimeout API on server
This adds the following to HTTP:
* server.setTimeout(msecs, callback)
Sets all new connections to time out after the specified time, at
which point it emits 'timeout' on the server, passing the socket as an
argument.
In this way, timeouts can be handled in one place consistently.
* req.setTimeout(), res.setTimeout()
Essentially an alias to req/res.socket.setTimeout(), but without
having to delve into a "buried" object. Adds a listener on the
req/res object, but not on the socket.
* server.timeout
Number of milliseconds before incoming connections time out.
(Default=1000*60*2, as before.)
Furthermore, if the user sets up their own timeout listener on either
the server, the request, or the response, then the default behavior
(destroying the socket) is suppressed.
Fix #3460
2013-03-03 23:29:22 -08:00
|
|
|
OutgoingMessage.prototype.setTimeout = function(msecs, callback) {
|
|
|
|
if (callback)
|
|
|
|
this.on('timeout', callback);
|
2013-03-09 18:46:39 -08:00
|
|
|
if (!this.socket) {
|
|
|
|
this.once('socket', function(socket) {
|
|
|
|
socket.setTimeout(msecs);
|
|
|
|
});
|
|
|
|
} else
|
|
|
|
this.socket.setTimeout(msecs);
|
http: More useful setTimeout API on server
This adds the following to HTTP:
* server.setTimeout(msecs, callback)
Sets all new connections to time out after the specified time, at
which point it emits 'timeout' on the server, passing the socket as an
argument.
In this way, timeouts can be handled in one place consistently.
* req.setTimeout(), res.setTimeout()
Essentially an alias to req/res.socket.setTimeout(), but without
having to delve into a "buried" object. Adds a listener on the
req/res object, but not on the socket.
* server.timeout
Number of milliseconds before incoming connections time out.
(Default=1000*60*2, as before.)
Furthermore, if the user sets up their own timeout listener on either
the server, the request, or the response, then the default behavior
(destroying the socket) is suppressed.
Fix #3460
2013-03-03 23:29:22 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
OutgoingMessage.prototype.destroy = function(error) {
|
2010-10-11 16:36:12 -07:00
|
|
|
this.socket.destroy(error);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2010-05-26 17:59:55 -07:00
|
|
|
// This abstract either writing directly to the socket or buffering it.
|
2010-12-01 18:07:20 -08:00
|
|
|
OutgoingMessage.prototype._send = function(data, encoding) {
|
2010-05-27 20:28:12 -07:00
|
|
|
// This is a shameful hack to get the headers and first body chunk onto
|
|
|
|
// the same packet. Future versions of Node are going to take care of
|
|
|
|
// this at a lower level and in a more general way.
|
|
|
|
if (!this._headerSent) {
|
|
|
|
if (typeof data === 'string') {
|
|
|
|
data = this._header + data;
|
|
|
|
} else {
|
|
|
|
this.output.unshift(this._header);
|
|
|
|
this.outputEncodings.unshift('ascii');
|
|
|
|
}
|
|
|
|
this._headerSent = true;
|
|
|
|
}
|
2010-10-11 16:00:36 -07:00
|
|
|
return this._writeRaw(data, encoding);
|
2010-10-06 23:05:23 -04:00
|
|
|
};
|
2010-05-27 20:28:12 -07:00
|
|
|
|
2010-11-29 07:41:08 -08:00
|
|
|
|
2010-09-10 11:56:35 +10:00
|
|
|
OutgoingMessage.prototype._writeRaw = function(data, encoding) {
|
2012-05-15 22:05:33 +02:00
|
|
|
if (data.length === 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-01-20 10:29:54 -08:00
|
|
|
if (this.connection &&
|
|
|
|
this.connection._httpMessage === this &&
|
http: Raise hangup error on destroyed socket write
Prior to v0.10, Node ignored ECONNRESET errors in many situations.
There *are* valid cases in which ECONNRESET should be ignored as a
normal part of the TCP dance, but in many others, it's a very relevant
signal that must be heeded with care.
Exacerbating this problem, if the OutgoingMessage does not have a
req.connection._handle, it assumes that it is in the process of
connecting, and thus buffers writes up in an array.
The problem happens when you reuse a socket between two requests, and it
is destroyed abruptly in between them. The writes will be buffered,
because the socket has no handle, but it's not ever going to GET a
handle, because it's not connecting, it's destroyed.
The proper fix is to treat ECONNRESET correctly. However, this is a
behavior/semantics change, and cannot land in a stable branch.
Fix #4775
2013-02-14 08:57:32 -08:00
|
|
|
this.connection.writable &&
|
|
|
|
!this.connection.destroyed) {
|
2010-05-27 20:28:12 -07:00
|
|
|
// There might be pending data in the this.output buffer.
|
|
|
|
while (this.output.length) {
|
|
|
|
if (!this.connection.writable) {
|
|
|
|
this._buffer(data, encoding);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
var c = this.output.shift();
|
|
|
|
var e = this.outputEncodings.shift();
|
|
|
|
this.connection.write(c, e);
|
|
|
|
}
|
|
|
|
|
2010-05-26 17:59:55 -07:00
|
|
|
// Directly write to socket.
|
|
|
|
return this.connection.write(data, encoding);
|
http: Raise hangup error on destroyed socket write
Prior to v0.10, Node ignored ECONNRESET errors in many situations.
There *are* valid cases in which ECONNRESET should be ignored as a
normal part of the TCP dance, but in many others, it's a very relevant
signal that must be heeded with care.
Exacerbating this problem, if the OutgoingMessage does not have a
req.connection._handle, it assumes that it is in the process of
connecting, and thus buffers writes up in an array.
The problem happens when you reuse a socket between two requests, and it
is destroyed abruptly in between them. The writes will be buffered,
because the socket has no handle, but it's not ever going to GET a
handle, because it's not connecting, it's destroyed.
The proper fix is to treat ECONNRESET correctly. However, this is a
behavior/semantics change, and cannot land in a stable branch.
Fix #4775
2013-02-14 08:57:32 -08:00
|
|
|
} else if (this.connection && this.connection.destroyed) {
|
|
|
|
// The socket was destroyed. If we're still trying to write to it,
|
2013-04-01 15:46:51 -07:00
|
|
|
// then we haven't gotten the 'close' event yet.
|
2013-02-22 10:35:07 -08:00
|
|
|
return false;
|
2010-05-26 17:59:55 -07:00
|
|
|
} else {
|
http: Raise hangup error on destroyed socket write
Prior to v0.10, Node ignored ECONNRESET errors in many situations.
There *are* valid cases in which ECONNRESET should be ignored as a
normal part of the TCP dance, but in many others, it's a very relevant
signal that must be heeded with care.
Exacerbating this problem, if the OutgoingMessage does not have a
req.connection._handle, it assumes that it is in the process of
connecting, and thus buffers writes up in an array.
The problem happens when you reuse a socket between two requests, and it
is destroyed abruptly in between them. The writes will be buffered,
because the socket has no handle, but it's not ever going to GET a
handle, because it's not connecting, it's destroyed.
The proper fix is to treat ECONNRESET correctly. However, this is a
behavior/semantics change, and cannot land in a stable branch.
Fix #4775
2013-02-14 08:57:32 -08:00
|
|
|
// buffer, as long as we're not destroyed.
|
2010-05-27 20:28:12 -07:00
|
|
|
this._buffer(data, encoding);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
2010-05-26 17:59:55 -07:00
|
|
|
|
2010-11-29 07:41:08 -08:00
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
OutgoingMessage.prototype._buffer = function(data, encoding) {
|
2010-05-27 20:28:12 -07:00
|
|
|
if (data.length === 0) return;
|
2010-05-26 17:59:55 -07:00
|
|
|
|
2010-05-27 20:28:12 -07:00
|
|
|
var length = this.output.length;
|
2009-09-14 16:25:41 +02:00
|
|
|
|
2010-05-27 20:28:12 -07:00
|
|
|
if (length === 0 || typeof data != 'string') {
|
2009-09-14 16:25:41 +02:00
|
|
|
this.output.push(data);
|
|
|
|
this.outputEncodings.push(encoding);
|
2010-05-27 20:28:12 -07:00
|
|
|
return false;
|
|
|
|
}
|
2009-09-14 16:25:41 +02:00
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
var lastEncoding = this.outputEncodings[length - 1];
|
|
|
|
var lastData = this.output[length - 1];
|
2010-05-27 20:28:12 -07:00
|
|
|
|
2011-02-05 16:31:27 -08:00
|
|
|
if ((encoding && lastEncoding === encoding) ||
|
2010-05-27 20:28:12 -07:00
|
|
|
(!encoding && data.constructor === lastData.constructor)) {
|
2010-12-01 18:07:20 -08:00
|
|
|
this.output[length - 1] = lastData + data;
|
2010-05-26 17:59:55 -07:00
|
|
|
return false;
|
2009-09-14 16:25:41 +02:00
|
|
|
}
|
2010-05-27 20:28:12 -07:00
|
|
|
|
|
|
|
this.output.push(data);
|
|
|
|
this.outputEncodings.push(encoding);
|
|
|
|
|
|
|
|
return false;
|
2009-07-14 18:31:50 +02:00
|
|
|
};
|
2009-07-14 11:59:13 +02:00
|
|
|
|
2010-05-26 17:59:55 -07:00
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
OutgoingMessage.prototype._storeHeader = function(firstLine, headers) {
|
|
|
|
// firstLine in the case of request is: 'GET /index.html HTTP/1.1\r\n'
|
|
|
|
// in the case of response it is: 'HTTP/1.1 200 OK\r\n'
|
2012-12-27 11:41:29 -08:00
|
|
|
var state = {
|
|
|
|
sentConnectionHeader: false,
|
|
|
|
sentContentLengthHeader: false,
|
|
|
|
sentTransferEncodingHeader: false,
|
|
|
|
sentDateHeader: false,
|
|
|
|
sentExpect: false,
|
|
|
|
messageHeader: firstLine
|
|
|
|
};
|
|
|
|
|
2009-08-23 12:20:25 +02:00
|
|
|
var field, value;
|
2010-09-15 09:52:23 -05:00
|
|
|
var self = this;
|
|
|
|
|
2010-04-12 11:57:24 -05:00
|
|
|
if (headers) {
|
|
|
|
var keys = Object.keys(headers);
|
2010-07-14 14:29:28 -07:00
|
|
|
var isArray = (Array.isArray(headers));
|
2010-12-04 15:20:34 -08:00
|
|
|
var field, value;
|
2010-09-15 09:52:23 -05:00
|
|
|
|
2010-10-04 10:50:35 -07:00
|
|
|
for (var i = 0, l = keys.length; i < l; i++) {
|
2010-04-12 11:57:24 -05:00
|
|
|
var key = keys[i];
|
|
|
|
if (isArray) {
|
|
|
|
field = headers[key][0];
|
|
|
|
value = headers[key][1];
|
|
|
|
} else {
|
|
|
|
field = key;
|
|
|
|
value = headers[key];
|
|
|
|
}
|
2009-08-26 22:03:19 +02:00
|
|
|
|
2010-09-15 09:52:23 -05:00
|
|
|
if (Array.isArray(value)) {
|
2010-10-04 10:50:35 -07:00
|
|
|
for (var j = 0; j < value.length; j++) {
|
2012-12-27 11:41:29 -08:00
|
|
|
storeHeader(this, state, field, value[j]);
|
2010-08-01 22:22:20 -07:00
|
|
|
}
|
2010-09-15 09:52:23 -05:00
|
|
|
} else {
|
2012-12-27 11:41:29 -08:00
|
|
|
storeHeader(this, state, field, value);
|
2010-04-12 11:57:24 -05:00
|
|
|
}
|
2009-07-14 18:31:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-15 07:38:24 +11:00
|
|
|
// Date header
|
2012-12-27 11:41:29 -08:00
|
|
|
if (this.sendDate == true && state.sentDateHeader == false) {
|
|
|
|
state.messageHeader += 'Date: ' + utcDate() + CRLF;
|
2012-02-15 07:38:24 +11:00
|
|
|
}
|
|
|
|
|
2013-01-23 13:47:29 +01:00
|
|
|
// Force the connection to close when the response is a 204 No Content or
|
|
|
|
// a 304 Not Modified and the user has set a "Transfer-Encoding: chunked"
|
|
|
|
// header.
|
2013-01-23 01:42:16 +01:00
|
|
|
//
|
2013-01-23 13:47:29 +01:00
|
|
|
// RFC 2616 mandates that 204 and 304 responses MUST NOT have a body but
|
|
|
|
// node.js used to send out a zero chunk anyway to accommodate clients
|
|
|
|
// that don't have special handling for those responses.
|
2013-01-23 01:42:16 +01:00
|
|
|
//
|
|
|
|
// It was pointed out that this might confuse reverse proxies to the point
|
|
|
|
// of creating security liabilities, so suppress the zero chunk and force
|
|
|
|
// the connection to close.
|
2013-01-23 13:47:29 +01:00
|
|
|
var statusCode = this.statusCode;
|
|
|
|
if ((statusCode == 204 || statusCode === 304) &&
|
|
|
|
this.chunkedEncoding === true) {
|
|
|
|
debug(statusCode + ' response should not use chunked encoding,' +
|
|
|
|
' closing connection.');
|
2013-01-23 01:42:16 +01:00
|
|
|
this.chunkedEncoding = false;
|
|
|
|
this.shouldKeepAlive = false;
|
|
|
|
}
|
|
|
|
|
2009-08-26 22:03:19 +02:00
|
|
|
// keep-alive logic
|
2012-12-27 11:41:29 -08:00
|
|
|
if (state.sentConnectionHeader === false) {
|
2012-02-18 15:01:35 -08:00
|
|
|
var shouldSendKeepAlive = this.shouldKeepAlive &&
|
2012-12-27 11:41:29 -08:00
|
|
|
(state.sentContentLengthHeader ||
|
2012-02-18 15:01:35 -08:00
|
|
|
this.useChunkedEncodingByDefault ||
|
|
|
|
this.agent);
|
|
|
|
if (shouldSendKeepAlive) {
|
2012-12-27 11:41:29 -08:00
|
|
|
state.messageHeader += 'Connection: keep-alive\r\n';
|
2009-06-26 18:29:57 +02:00
|
|
|
} else {
|
2010-05-26 17:59:55 -07:00
|
|
|
this._last = true;
|
2012-12-27 11:41:29 -08:00
|
|
|
state.messageHeader += 'Connection: close\r\n';
|
2009-06-26 18:29:57 +02:00
|
|
|
}
|
2009-07-14 18:31:50 +02:00
|
|
|
}
|
|
|
|
|
2012-12-27 11:41:29 -08:00
|
|
|
if (state.sentContentLengthHeader == false &&
|
|
|
|
state.sentTransferEncodingHeader == false) {
|
2010-04-21 15:15:21 -07:00
|
|
|
if (this._hasBody) {
|
|
|
|
if (this.useChunkedEncodingByDefault) {
|
2012-12-27 11:41:29 -08:00
|
|
|
state.messageHeader += 'Transfer-Encoding: chunked\r\n';
|
2010-04-21 15:15:21 -07:00
|
|
|
this.chunkedEncoding = true;
|
|
|
|
} else {
|
2010-05-26 17:59:55 -07:00
|
|
|
this._last = true;
|
2010-04-21 15:15:21 -07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Make sure we don't end the 0\r\n\r\n at the end of the message.
|
|
|
|
this.chunkedEncoding = false;
|
2009-12-23 15:48:14 -05:00
|
|
|
}
|
2009-07-14 18:31:50 +02:00
|
|
|
}
|
|
|
|
|
2012-12-27 11:41:29 -08:00
|
|
|
this._header = state.messageHeader + CRLF;
|
2010-05-27 20:28:12 -07:00
|
|
|
this._headerSent = false;
|
2010-11-29 07:41:08 -08:00
|
|
|
|
2010-09-10 11:56:35 +10:00
|
|
|
// wait until the first body chunk, or close(), is sent to flush,
|
|
|
|
// UNLESS we're sending Expect: 100-continue.
|
2012-12-27 11:41:29 -08:00
|
|
|
if (state.sentExpect) this._send('');
|
2009-07-14 18:31:50 +02:00
|
|
|
};
|
|
|
|
|
2012-12-27 11:41:29 -08:00
|
|
|
function storeHeader(self, state, field, value) {
|
|
|
|
// Protect against response splitting. The if statement is there to
|
|
|
|
// minimize the performance impact in the common case.
|
|
|
|
if (/[\r\n]/.test(value))
|
|
|
|
value = value.replace(/[\r\n]+[ \t]*/g, '');
|
|
|
|
|
|
|
|
state.messageHeader += field + ': ' + value + CRLF;
|
|
|
|
|
|
|
|
if (connectionExpression.test(field)) {
|
|
|
|
state.sentConnectionHeader = true;
|
|
|
|
if (closeExpression.test(value)) {
|
|
|
|
self._last = true;
|
|
|
|
} else {
|
|
|
|
self.shouldKeepAlive = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (transferEncodingExpression.test(field)) {
|
|
|
|
state.sentTransferEncodingHeader = true;
|
|
|
|
if (chunkExpression.test(value)) self.chunkedEncoding = true;
|
|
|
|
|
|
|
|
} else if (contentLengthExpression.test(field)) {
|
|
|
|
state.sentContentLengthHeader = true;
|
|
|
|
} else if (dateExpression.test(field)) {
|
|
|
|
state.sentDateHeader = true;
|
|
|
|
} else if (expectExpression.test(field)) {
|
|
|
|
state.sentExpect = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-29 07:41:08 -08:00
|
|
|
|
2011-02-10 02:18:13 -08:00
|
|
|
OutgoingMessage.prototype.setHeader = function(name, value) {
|
|
|
|
if (arguments.length < 2) {
|
2012-02-18 15:01:35 -08:00
|
|
|
throw new Error('`name` and `value` are required for setHeader().');
|
2011-02-10 02:18:13 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (this._header) {
|
2012-02-18 15:01:35 -08:00
|
|
|
throw new Error('Can\'t set headers after they are sent.');
|
2011-02-10 02:18:13 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
var key = name.toLowerCase();
|
2011-02-10 16:29:34 -08:00
|
|
|
this._headers = this._headers || {};
|
|
|
|
this._headerNames = this._headerNames || {};
|
2011-02-10 02:18:13 -08:00
|
|
|
this._headers[key] = value;
|
|
|
|
this._headerNames[key] = name;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
OutgoingMessage.prototype.getHeader = function(name) {
|
|
|
|
if (arguments.length < 1) {
|
2012-02-18 15:01:35 -08:00
|
|
|
throw new Error('`name` is required for getHeader().');
|
2011-02-10 02:18:13 -08:00
|
|
|
}
|
|
|
|
|
2011-02-10 16:29:34 -08:00
|
|
|
if (!this._headers) return;
|
|
|
|
|
2011-02-10 02:18:13 -08:00
|
|
|
var key = name.toLowerCase();
|
|
|
|
return this._headers[key];
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
OutgoingMessage.prototype.removeHeader = function(name) {
|
|
|
|
if (arguments.length < 1) {
|
2012-02-18 15:01:35 -08:00
|
|
|
throw new Error('`name` is required for removeHeader().');
|
2011-02-10 02:18:13 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (this._header) {
|
2012-02-18 15:01:35 -08:00
|
|
|
throw new Error('Can\'t remove headers after they are sent.');
|
2011-02-10 02:18:13 -08:00
|
|
|
}
|
|
|
|
|
2011-02-10 16:29:34 -08:00
|
|
|
if (!this._headers) return;
|
|
|
|
|
2011-02-10 02:18:13 -08:00
|
|
|
var key = name.toLowerCase();
|
|
|
|
delete this._headers[key];
|
|
|
|
delete this._headerNames[key];
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
OutgoingMessage.prototype._renderHeaders = function() {
|
|
|
|
if (this._header) {
|
2012-02-18 15:01:35 -08:00
|
|
|
throw new Error('Can\'t render headers after they are sent to the client.');
|
2011-02-10 02:18:13 -08:00
|
|
|
}
|
2011-02-10 16:29:34 -08:00
|
|
|
|
|
|
|
if (!this._headers) return {};
|
|
|
|
|
2011-02-10 02:18:13 -08:00
|
|
|
var headers = {};
|
|
|
|
var keys = Object.keys(this._headers);
|
|
|
|
for (var i = 0, l = keys.length; i < l; i++) {
|
|
|
|
var key = keys[i];
|
|
|
|
headers[this._headerNames[key]] = this._headers[key];
|
|
|
|
}
|
|
|
|
return headers;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2012-09-18 18:26:08 +02:00
|
|
|
Object.defineProperty(OutgoingMessage.prototype, 'headersSent', {
|
|
|
|
configurable: true,
|
|
|
|
enumerable: true,
|
2012-10-12 11:44:02 -07:00
|
|
|
get: function() { return !!this._header; }
|
2012-09-18 18:26:08 +02:00
|
|
|
});
|
|
|
|
|
2011-02-10 02:18:13 -08:00
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
OutgoingMessage.prototype.write = function(chunk, encoding) {
|
2010-05-26 17:59:55 -07:00
|
|
|
if (!this._header) {
|
2011-02-10 02:18:13 -08:00
|
|
|
this._implicitHeader();
|
2010-03-14 12:36:45 +09:00
|
|
|
}
|
|
|
|
|
2010-04-21 15:15:21 -07:00
|
|
|
if (!this._hasBody) {
|
2012-06-21 10:04:21 -07:00
|
|
|
debug('This type of response MUST NOT have a body. ' +
|
|
|
|
'Ignoring write() calls.');
|
2010-08-12 10:06:52 -07:00
|
|
|
return true;
|
2010-04-21 15:15:21 -07:00
|
|
|
}
|
|
|
|
|
2011-11-28 09:36:54 -08:00
|
|
|
if (typeof chunk !== 'string' && !Buffer.isBuffer(chunk)) {
|
|
|
|
throw new TypeError('first argument must be a string or Buffer');
|
2010-05-04 18:28:49 -07:00
|
|
|
}
|
|
|
|
|
2010-05-26 17:59:55 -07:00
|
|
|
if (chunk.length === 0) return false;
|
|
|
|
|
|
|
|
var len, ret;
|
2010-03-23 21:31:44 -07:00
|
|
|
if (this.chunkedEncoding) {
|
2013-04-08 08:46:51 -07:00
|
|
|
if (typeof(chunk) === 'string' &&
|
|
|
|
encoding !== 'hex' &&
|
|
|
|
encoding !== 'base64' &&
|
|
|
|
encoding !== 'binary') {
|
2010-05-26 17:59:55 -07:00
|
|
|
len = Buffer.byteLength(chunk, encoding);
|
2011-10-31 00:49:20 +09:00
|
|
|
chunk = len.toString(16) + CRLF + chunk + CRLF;
|
2010-05-26 17:59:55 -07:00
|
|
|
ret = this._send(chunk, encoding);
|
|
|
|
} else {
|
2013-04-08 08:46:51 -07:00
|
|
|
// buffer, or a non-toString-friendly encoding
|
2010-05-26 17:59:55 -07:00
|
|
|
len = chunk.length;
|
|
|
|
this._send(len.toString(16) + CRLF);
|
2013-04-08 08:46:51 -07:00
|
|
|
this._send(chunk, encoding);
|
2010-05-26 17:59:55 -07:00
|
|
|
ret = this._send(CRLF);
|
2010-03-19 19:22:04 -07:00
|
|
|
}
|
2009-07-14 18:31:50 +02:00
|
|
|
} else {
|
2010-05-26 17:59:55 -07:00
|
|
|
ret = this._send(chunk, encoding);
|
2009-07-14 18:31:50 +02:00
|
|
|
}
|
2009-06-26 18:29:57 +02:00
|
|
|
|
2010-05-26 17:59:55 -07:00
|
|
|
debug('write ret = ' + ret);
|
|
|
|
return ret;
|
2009-07-14 18:31:50 +02:00
|
|
|
};
|
|
|
|
|
2010-09-09 16:29:35 +10:00
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
OutgoingMessage.prototype.addTrailers = function(headers) {
|
|
|
|
this._trailer = '';
|
2010-09-09 16:29:35 +10:00
|
|
|
var keys = Object.keys(headers);
|
|
|
|
var isArray = (Array.isArray(headers));
|
2010-12-04 15:20:34 -08:00
|
|
|
var field, value;
|
2010-09-09 16:29:35 +10:00
|
|
|
for (var i = 0, l = keys.length; i < l; i++) {
|
|
|
|
var key = keys[i];
|
|
|
|
if (isArray) {
|
|
|
|
field = headers[key][0];
|
|
|
|
value = headers[key][1];
|
|
|
|
} else {
|
|
|
|
field = key;
|
|
|
|
value = headers[key];
|
|
|
|
}
|
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
this._trailer += field + ': ' + value + CRLF;
|
2010-09-09 16:29:35 +10:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2012-12-19 12:35:07 +01:00
|
|
|
var zero_chunk_buf = new Buffer('\r\n0\r\n');
|
|
|
|
var crlf_buf = new Buffer('\r\n');
|
|
|
|
|
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
OutgoingMessage.prototype.end = function(data, encoding) {
|
2011-07-29 23:49:48 +09:00
|
|
|
if (this.finished) {
|
|
|
|
return false;
|
|
|
|
}
|
2011-02-10 02:18:13 -08:00
|
|
|
if (!this._header) {
|
|
|
|
this._implicitHeader();
|
|
|
|
}
|
|
|
|
|
2011-07-07 21:04:27 -07:00
|
|
|
if (data && !this._hasBody) {
|
2012-06-21 10:04:21 -07:00
|
|
|
debug('This type of response MUST NOT have a body. ' +
|
|
|
|
'Ignoring data passed to end().');
|
2011-07-07 21:04:27 -07:00
|
|
|
data = false;
|
|
|
|
}
|
|
|
|
|
2010-05-26 17:59:55 -07:00
|
|
|
var ret;
|
|
|
|
|
2010-11-29 07:41:08 -08:00
|
|
|
var hot = this._headerSent === false &&
|
2012-12-19 12:35:07 +01:00
|
|
|
(data && data.length > 0) &&
|
2010-11-29 07:41:08 -08:00
|
|
|
this.output.length === 0 &&
|
2011-01-24 01:13:59 +03:30
|
|
|
this.connection &&
|
2010-11-29 07:41:08 -08:00
|
|
|
this.connection.writable &&
|
2011-01-20 02:41:16 -08:00
|
|
|
this.connection._httpMessage === this;
|
2010-05-27 20:28:12 -07:00
|
|
|
|
2013-03-14 07:48:18 -07:00
|
|
|
// The benefits of the hot-path optimization below start to fall
|
|
|
|
// off when the buffer size gets up near 128KB, because the cost
|
|
|
|
// of the copy is more than the cost of the extra write() call.
|
|
|
|
// Switch to the write/end method at that point. Heuristics and
|
|
|
|
// magic numbers are awful, but slow http responses are worse.
|
|
|
|
if (hot && Buffer.isBuffer(data) && data.length > 120 * 1024)
|
|
|
|
hot = false;
|
|
|
|
|
2010-05-27 20:28:12 -07:00
|
|
|
if (hot) {
|
|
|
|
// Hot path. They're doing
|
|
|
|
// res.writeHead();
|
|
|
|
// res.end(blah);
|
|
|
|
// HACKY.
|
2011-07-07 21:04:27 -07:00
|
|
|
|
2012-12-19 12:35:07 +01:00
|
|
|
if (typeof data === 'string') {
|
|
|
|
if (this.chunkedEncoding) {
|
|
|
|
var l = Buffer.byteLength(data, encoding).toString(16);
|
|
|
|
ret = this.connection.write(this._header + l + CRLF +
|
|
|
|
data + '\r\n0\r\n' +
|
|
|
|
this._trailer + '\r\n', encoding);
|
|
|
|
} else {
|
|
|
|
ret = this.connection.write(this._header + data, encoding);
|
|
|
|
}
|
|
|
|
} else if (Buffer.isBuffer(data)) {
|
|
|
|
if (this.chunkedEncoding) {
|
|
|
|
var chunk_size = data.length.toString(16);
|
|
|
|
|
|
|
|
// Skip expensive Buffer.byteLength() calls; only ISO-8859-1 characters
|
|
|
|
// are allowed in HTTP headers. Therefore:
|
|
|
|
//
|
|
|
|
// this._header.length == Buffer.byteLength(this._header.length)
|
|
|
|
// this._trailer.length == Buffer.byteLength(this._trailer.length)
|
|
|
|
//
|
|
|
|
var header_len = this._header.length;
|
|
|
|
var chunk_size_len = chunk_size.length;
|
|
|
|
var data_len = data.length;
|
|
|
|
var trailer_len = this._trailer.length;
|
|
|
|
|
2012-12-21 16:51:43 +00:00
|
|
|
var len = header_len +
|
|
|
|
chunk_size_len +
|
|
|
|
2 + // '\r\n'.length
|
|
|
|
data_len +
|
|
|
|
5 + // '\r\n0\r\n'.length
|
|
|
|
trailer_len +
|
|
|
|
2; // '\r\n'.length
|
2012-12-19 12:35:07 +01:00
|
|
|
|
|
|
|
var buf = new Buffer(len);
|
|
|
|
var off = 0;
|
|
|
|
|
|
|
|
buf.write(this._header, off, header_len, 'ascii');
|
|
|
|
off += header_len;
|
|
|
|
|
|
|
|
buf.write(chunk_size, off, chunk_size_len, 'ascii');
|
|
|
|
off += chunk_size_len;
|
|
|
|
|
|
|
|
crlf_buf.copy(buf, off);
|
|
|
|
off += 2;
|
|
|
|
|
|
|
|
data.copy(buf, off);
|
|
|
|
off += data_len;
|
|
|
|
|
|
|
|
zero_chunk_buf.copy(buf, off);
|
|
|
|
off += 5;
|
|
|
|
|
|
|
|
if (trailer_len > 0) {
|
|
|
|
buf.write(this._trailer, off, trailer_len, 'ascii');
|
|
|
|
off += trailer_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
crlf_buf.copy(buf, off);
|
|
|
|
|
|
|
|
ret = this.connection.write(buf);
|
|
|
|
} else {
|
|
|
|
var header_len = this._header.length;
|
|
|
|
var buf = new Buffer(header_len + data.length);
|
|
|
|
buf.write(this._header, 0, header_len, 'ascii');
|
|
|
|
data.copy(buf, header_len);
|
|
|
|
ret = this.connection.write(buf);
|
|
|
|
}
|
2010-05-27 20:28:12 -07:00
|
|
|
} else {
|
2012-12-19 12:35:07 +01:00
|
|
|
throw new TypeError('first argument must be a string or Buffer');
|
2010-05-27 20:28:12 -07:00
|
|
|
}
|
|
|
|
this._headerSent = true;
|
|
|
|
|
|
|
|
} else if (data) {
|
|
|
|
// Normal body write.
|
2010-05-26 17:59:55 -07:00
|
|
|
ret = this.write(data, encoding);
|
|
|
|
}
|
|
|
|
|
2010-05-27 20:28:12 -07:00
|
|
|
if (!hot) {
|
|
|
|
if (this.chunkedEncoding) {
|
2010-09-09 16:29:35 +10:00
|
|
|
ret = this._send('0\r\n' + this._trailer + '\r\n'); // Last chunk.
|
2010-09-30 17:12:56 -07:00
|
|
|
} else {
|
2010-05-27 20:28:12 -07:00
|
|
|
// Force a flush, HACK.
|
|
|
|
ret = this._send('');
|
|
|
|
}
|
2010-05-26 17:59:55 -07:00
|
|
|
}
|
|
|
|
|
2010-05-27 20:28:12 -07:00
|
|
|
this.finished = true;
|
|
|
|
|
2010-05-26 17:59:55 -07:00
|
|
|
// There is the first message on the outgoing queue, and we've sent
|
|
|
|
// everything to the socket.
|
2011-10-04 20:51:34 +02:00
|
|
|
debug('outgoing message end.');
|
2011-01-20 02:41:16 -08:00
|
|
|
if (this.output.length === 0 && this.connection._httpMessage === this) {
|
|
|
|
this._finish();
|
2010-05-26 17:59:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2009-07-14 18:31:50 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2011-01-20 02:41:16 -08:00
|
|
|
OutgoingMessage.prototype._finish = function() {
|
2011-02-09 18:50:26 -08:00
|
|
|
assert(this.connection);
|
|
|
|
if (this instanceof ServerResponse) {
|
|
|
|
DTRACE_HTTP_SERVER_RESPONSE(this.connection);
|
2012-11-21 00:27:22 +01:00
|
|
|
COUNTER_HTTP_SERVER_RESPONSE();
|
2011-02-09 18:50:26 -08:00
|
|
|
} else {
|
|
|
|
assert(this instanceof ClientRequest);
|
|
|
|
DTRACE_HTTP_CLIENT_REQUEST(this, this.connection);
|
2012-11-21 00:27:22 +01:00
|
|
|
COUNTER_HTTP_CLIENT_REQUEST();
|
2011-02-09 18:50:26 -08:00
|
|
|
}
|
2012-10-03 17:40:14 -07:00
|
|
|
this.emit('finish');
|
2011-01-20 02:41:16 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
OutgoingMessage.prototype._flush = function() {
|
|
|
|
// This logic is probably a bit confusing. Let me explain a bit:
|
|
|
|
//
|
|
|
|
// In both HTTP servers and clients it is possible to queue up several
|
|
|
|
// outgoing messages. This is easiest to imagine in the case of a client.
|
|
|
|
// Take the following situation:
|
|
|
|
//
|
|
|
|
// req1 = client.request('GET', '/');
|
|
|
|
// req2 = client.request('POST', '/');
|
|
|
|
//
|
|
|
|
// When the user does
|
|
|
|
//
|
|
|
|
// req2.write('hello world\n');
|
|
|
|
//
|
|
|
|
// it's possible that the first request has not been completely flushed to
|
|
|
|
// the socket yet. Thus the outgoing messages need to be prepared to queue
|
|
|
|
// up data internally before sending it on further to the socket's queue.
|
|
|
|
//
|
|
|
|
// This function, outgoingFlush(), is called by both the Server and Client
|
|
|
|
// to attempt to flush any pending messages out to the socket.
|
|
|
|
|
|
|
|
if (!this.socket) return;
|
|
|
|
|
|
|
|
var ret;
|
2011-07-26 00:21:52 +02:00
|
|
|
while (this.output.length) {
|
2011-10-04 20:51:34 +02:00
|
|
|
|
2011-01-20 02:41:16 -08:00
|
|
|
if (!this.socket.writable) return; // XXX Necessary?
|
|
|
|
|
|
|
|
var data = this.output.shift();
|
|
|
|
var encoding = this.outputEncodings.shift();
|
|
|
|
|
|
|
|
ret = this.socket.write(data, encoding);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.finished) {
|
|
|
|
// This is a queue to the server or client to bring in the next this.
|
|
|
|
this._finish();
|
|
|
|
} else if (ret) {
|
2011-02-19 16:00:05 -08:00
|
|
|
// This is necessary to prevent https from breaking
|
2011-01-20 02:41:16 -08:00
|
|
|
this.emit('drain');
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
function ServerResponse(req) {
|
2011-01-20 02:41:16 -08:00
|
|
|
OutgoingMessage.call(this);
|
2009-07-14 18:31:50 +02:00
|
|
|
|
2010-06-02 14:46:29 -07:00
|
|
|
if (req.method === 'HEAD') this._hasBody = false;
|
|
|
|
|
2012-02-15 07:38:24 +11:00
|
|
|
this.sendDate = true;
|
|
|
|
|
2009-12-05 08:37:46 +01:00
|
|
|
if (req.httpVersionMajor < 1 || req.httpVersionMinor < 1) {
|
2012-09-08 21:43:55 +02:00
|
|
|
this.useChunkedEncodingByDefault = chunkExpression.test(req.headers.te);
|
2010-03-23 21:31:44 -07:00
|
|
|
this.shouldKeepAlive = false;
|
2009-12-05 08:37:46 +01:00
|
|
|
}
|
2009-07-16 10:59:40 +02:00
|
|
|
}
|
2010-10-12 08:04:09 +11:00
|
|
|
util.inherits(ServerResponse, OutgoingMessage);
|
2010-11-29 07:41:08 -08:00
|
|
|
|
|
|
|
|
2009-11-04 12:37:52 +01:00
|
|
|
exports.ServerResponse = ServerResponse;
|
2009-07-14 18:31:50 +02:00
|
|
|
|
2011-02-10 02:18:13 -08:00
|
|
|
ServerResponse.prototype.statusCode = 200;
|
2010-11-29 07:41:08 -08:00
|
|
|
|
2012-01-05 20:50:03 +09:00
|
|
|
function onServerResponseClose() {
|
2013-01-14 16:35:03 +01:00
|
|
|
// EventEmitter.emit makes a copy of the 'close' listeners array before
|
|
|
|
// calling the listeners. detachSocket() unregisters onServerResponseClose
|
|
|
|
// but if detachSocket() is called, directly or indirectly, by a 'close'
|
|
|
|
// listener, onServerResponseClose is still in that copy of the listeners
|
|
|
|
// array. That is, in the example below, b still gets called even though
|
|
|
|
// it's been removed by a:
|
|
|
|
//
|
|
|
|
// var obj = new events.EventEmitter;
|
|
|
|
// obj.on('event', a);
|
|
|
|
// obj.on('event', b);
|
|
|
|
// function a() { obj.removeListener('event', b) }
|
|
|
|
// function b() { throw "BAM!" }
|
|
|
|
// obj.emit('event'); // throws
|
|
|
|
//
|
|
|
|
// Ergo, we need to deal with stale 'close' events and handle the case
|
|
|
|
// where the ServerResponse object has already been deconstructed.
|
|
|
|
// Fortunately, that requires only a single if check. :-)
|
|
|
|
if (this._httpMessage) this._httpMessage.emit('close');
|
2012-01-05 20:50:03 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
ServerResponse.prototype.assignSocket = function(socket) {
|
|
|
|
assert(!socket._httpMessage);
|
|
|
|
socket._httpMessage = this;
|
|
|
|
socket.on('close', onServerResponseClose);
|
|
|
|
this.socket = socket;
|
|
|
|
this.connection = socket;
|
2013-03-09 18:46:39 -08:00
|
|
|
this.emit('socket', socket);
|
2012-01-05 20:50:03 +09:00
|
|
|
this._flush();
|
|
|
|
};
|
|
|
|
|
|
|
|
ServerResponse.prototype.detachSocket = function(socket) {
|
|
|
|
assert(socket._httpMessage == this);
|
|
|
|
socket.removeListener('close', onServerResponseClose);
|
|
|
|
socket._httpMessage = null;
|
|
|
|
this.socket = this.connection = null;
|
|
|
|
};
|
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
ServerResponse.prototype.writeContinue = function() {
|
|
|
|
this._writeRaw('HTTP/1.1 100 Continue' + CRLF + CRLF, 'ascii');
|
2010-11-29 07:41:08 -08:00
|
|
|
this._sent100 = true;
|
2010-10-06 23:05:23 -04:00
|
|
|
};
|
2010-02-21 16:53:20 -08:00
|
|
|
|
2011-02-10 02:18:13 -08:00
|
|
|
ServerResponse.prototype._implicitHeader = function() {
|
2011-02-10 16:29:34 -08:00
|
|
|
this.writeHead(this.statusCode);
|
2011-02-10 02:18:13 -08:00
|
|
|
};
|
2010-11-29 07:41:08 -08:00
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
ServerResponse.prototype.writeHead = function(statusCode) {
|
2010-02-21 16:53:20 -08:00
|
|
|
var reasonPhrase, headers, headerIndex;
|
|
|
|
|
|
|
|
if (typeof arguments[1] == 'string') {
|
|
|
|
reasonPhrase = arguments[1];
|
|
|
|
headerIndex = 2;
|
|
|
|
} else {
|
2010-12-01 18:07:20 -08:00
|
|
|
reasonPhrase = STATUS_CODES[statusCode] || 'unknown';
|
2010-02-21 16:53:20 -08:00
|
|
|
headerIndex = 1;
|
|
|
|
}
|
2011-07-20 22:42:10 -07:00
|
|
|
this.statusCode = statusCode;
|
2010-02-21 16:53:20 -08:00
|
|
|
|
2011-02-10 16:29:34 -08:00
|
|
|
var obj = arguments[headerIndex];
|
|
|
|
|
|
|
|
if (obj && this._headers) {
|
|
|
|
// Slow-case: when progressive API and header fields are passed.
|
|
|
|
headers = this._renderHeaders();
|
|
|
|
|
|
|
|
if (Array.isArray(obj)) {
|
|
|
|
// handle array case
|
|
|
|
// TODO: remove when array is no longer accepted
|
|
|
|
var field;
|
|
|
|
for (var i = 0, len = obj.length; i < len; ++i) {
|
|
|
|
field = obj[i][0];
|
2012-12-27 09:13:58 -08:00
|
|
|
if (headers[field] !== undefined) {
|
2011-02-10 16:29:34 -08:00
|
|
|
obj.push([field, headers[field]]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
headers = obj;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// handle object case
|
2011-10-07 03:07:45 +02:00
|
|
|
var keys = Object.keys(obj);
|
|
|
|
for (var i = 0; i < keys.length; i++) {
|
|
|
|
var k = keys[i];
|
2011-02-10 16:29:34 -08:00
|
|
|
if (k) headers[k] = obj[k];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (this._headers) {
|
|
|
|
// only progressive api is used
|
|
|
|
headers = this._renderHeaders();
|
2010-02-21 16:53:20 -08:00
|
|
|
} else {
|
2011-02-10 16:29:34 -08:00
|
|
|
// only writeHead() called
|
|
|
|
headers = obj;
|
2010-02-21 16:53:20 -08:00
|
|
|
}
|
|
|
|
|
2011-07-05 21:54:06 +02:00
|
|
|
var statusLine = 'HTTP/1.1 ' + statusCode.toString() + ' ' +
|
|
|
|
reasonPhrase + CRLF;
|
2010-04-21 15:15:21 -07:00
|
|
|
|
2010-11-29 07:41:08 -08:00
|
|
|
if (statusCode === 204 || statusCode === 304 ||
|
|
|
|
(100 <= statusCode && statusCode <= 199)) {
|
2010-04-21 15:15:21 -07:00
|
|
|
// RFC 2616, 10.2.5:
|
|
|
|
// The 204 response MUST NOT include a message-body, and thus is always
|
|
|
|
// terminated by the first empty line after the header fields.
|
|
|
|
// RFC 2616, 10.3.5:
|
|
|
|
// The 304 response MUST NOT contain a message-body, and thus is always
|
|
|
|
// terminated by the first empty line after the header fields.
|
2010-09-10 11:56:35 +10:00
|
|
|
// RFC 2616, 10.1 Informational 1xx:
|
|
|
|
// This class of status code indicates a provisional response,
|
|
|
|
// consisting only of the Status-Line and optional headers, and is
|
|
|
|
// terminated by an empty line.
|
2010-04-21 15:15:21 -07:00
|
|
|
this._hasBody = false;
|
|
|
|
}
|
|
|
|
|
2010-09-10 11:56:35 +10:00
|
|
|
// don't keep alive connections where the client expects 100 Continue
|
|
|
|
// but we sent a final status; they may put extra bytes on the wire.
|
2012-12-12 22:24:17 -08:00
|
|
|
if (this._expect_continue && !this._sent100) {
|
2010-11-29 07:41:08 -08:00
|
|
|
this.shouldKeepAlive = false;
|
2010-09-10 11:56:35 +10:00
|
|
|
}
|
2010-04-21 15:15:21 -07:00
|
|
|
|
2010-05-26 17:59:55 -07:00
|
|
|
this._storeHeader(statusLine, headers);
|
2009-07-14 18:31:50 +02:00
|
|
|
};
|
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
ServerResponse.prototype.writeHeader = function() {
|
2010-05-12 12:15:58 -07:00
|
|
|
this.writeHead.apply(this, arguments);
|
|
|
|
};
|
2009-07-14 18:31:50 +02:00
|
|
|
|
2010-11-29 07:41:08 -08:00
|
|
|
|
2013-04-11 14:47:15 -07:00
|
|
|
var agent = require('_http_agent');
|
2011-10-04 20:51:34 +02:00
|
|
|
|
2013-04-11 14:47:15 -07:00
|
|
|
var Agent = exports.Agent = agent.Agent;
|
|
|
|
var globalAgent = exports.globalAgent = agent.globalAgent;
|
2011-07-26 00:15:15 +02:00
|
|
|
|
2011-10-14 17:10:49 -07:00
|
|
|
|
2011-10-04 20:51:34 +02:00
|
|
|
function ClientRequest(options, cb) {
|
|
|
|
var self = this;
|
|
|
|
OutgoingMessage.call(self);
|
|
|
|
|
2012-02-20 20:32:10 +01:00
|
|
|
self.agent = options.agent === undefined ? globalAgent : options.agent;
|
|
|
|
|
|
|
|
var defaultPort = options.defaultPort || 80;
|
|
|
|
|
|
|
|
var port = options.port || defaultPort;
|
|
|
|
var host = options.hostname || options.host || 'localhost';
|
2011-07-26 00:15:15 +02:00
|
|
|
|
2011-10-04 20:51:34 +02:00
|
|
|
if (options.setHost === undefined) {
|
2012-02-20 20:32:10 +01:00
|
|
|
var setHost = true;
|
2011-10-04 20:51:34 +02:00
|
|
|
}
|
2011-07-26 00:15:15 +02:00
|
|
|
|
2011-10-04 20:51:34 +02:00
|
|
|
self.socketPath = options.socketPath;
|
|
|
|
|
|
|
|
var method = self.method = (options.method || 'GET').toUpperCase();
|
|
|
|
self.path = options.path || '/';
|
|
|
|
if (cb) {
|
2012-04-27 01:02:10 +02:00
|
|
|
self.once('response', cb);
|
2011-10-04 20:51:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!Array.isArray(options.headers)) {
|
2011-02-10 02:18:13 -08:00
|
|
|
if (options.headers) {
|
2011-10-04 20:51:34 +02:00
|
|
|
var keys = Object.keys(options.headers);
|
2011-02-10 02:18:13 -08:00
|
|
|
for (var i = 0, l = keys.length; i < l; i++) {
|
|
|
|
var key = keys[i];
|
2011-10-04 20:51:34 +02:00
|
|
|
self.setHeader(key, options.headers[key]);
|
2011-02-10 02:18:13 -08:00
|
|
|
}
|
|
|
|
}
|
2012-02-20 20:32:10 +01:00
|
|
|
if (host && !this.getHeader('host') && setHost) {
|
|
|
|
var hostHeader = host;
|
|
|
|
if (port && +port !== defaultPort) {
|
|
|
|
hostHeader += ':' + port;
|
2011-02-23 14:46:35 -08:00
|
|
|
}
|
2011-10-19 16:28:44 -07:00
|
|
|
this.setHeader('Host', hostHeader);
|
2011-02-10 02:18:13 -08:00
|
|
|
}
|
2011-01-20 02:41:16 -08:00
|
|
|
}
|
2009-07-14 18:31:50 +02:00
|
|
|
|
2011-10-14 17:10:49 -07:00
|
|
|
if (options.auth && !this.getHeader('Authorization')) {
|
|
|
|
//basic auth
|
|
|
|
this.setHeader('Authorization', 'Basic ' +
|
|
|
|
new Buffer(options.auth).toString('base64'));
|
|
|
|
}
|
|
|
|
|
2012-01-09 03:51:06 +01:00
|
|
|
if (method === 'GET' || method === 'HEAD' || method === 'CONNECT') {
|
2011-10-04 20:51:34 +02:00
|
|
|
self.useChunkedEncodingByDefault = false;
|
2009-07-31 18:34:27 +02:00
|
|
|
} else {
|
2011-10-04 20:51:34 +02:00
|
|
|
self.useChunkedEncodingByDefault = true;
|
2009-07-31 18:34:27 +02:00
|
|
|
}
|
2011-01-20 02:41:16 -08:00
|
|
|
|
2011-10-04 20:51:34 +02:00
|
|
|
if (Array.isArray(options.headers)) {
|
2011-10-19 16:28:44 -07:00
|
|
|
self._storeHeader(self.method + ' ' + self.path + ' HTTP/1.1\r\n',
|
|
|
|
options.headers);
|
2011-10-04 20:51:34 +02:00
|
|
|
} else if (self.getHeader('expect')) {
|
2011-10-19 16:28:44 -07:00
|
|
|
self._storeHeader(self.method + ' ' + self.path + ' HTTP/1.1\r\n',
|
|
|
|
self._renderHeaders());
|
2011-10-04 20:51:34 +02:00
|
|
|
}
|
|
|
|
if (self.socketPath) {
|
|
|
|
self._last = true;
|
|
|
|
self.shouldKeepAlive = false;
|
|
|
|
if (options.createConnection) {
|
2012-07-20 20:51:02 +04:00
|
|
|
self.onSocket(options.createConnection(self.socketPath));
|
2011-10-04 20:51:34 +02:00
|
|
|
} else {
|
|
|
|
self.onSocket(net.createConnection(self.socketPath));
|
|
|
|
}
|
|
|
|
} else if (self.agent) {
|
|
|
|
// If there is an agent we should default to Connection:keep-alive.
|
|
|
|
self._last = false;
|
|
|
|
self.shouldKeepAlive = true;
|
2012-02-23 17:37:49 +02:00
|
|
|
self.agent.addRequest(self, host, port, options.localAddress);
|
2011-10-04 20:51:34 +02:00
|
|
|
} else {
|
|
|
|
// No agent, default to Connection:close.
|
|
|
|
self._last = true;
|
|
|
|
self.shouldKeepAlive = false;
|
|
|
|
if (options.createConnection) {
|
2012-02-23 17:37:49 +02:00
|
|
|
options.port = port;
|
|
|
|
options.host = host;
|
2012-07-20 20:51:02 +04:00
|
|
|
var conn = options.createConnection(options);
|
2011-10-04 20:51:34 +02:00
|
|
|
} else {
|
2012-02-23 17:37:49 +02:00
|
|
|
var conn = net.createConnection({
|
|
|
|
port: port,
|
|
|
|
host: host,
|
|
|
|
localAddress: options.localAddress
|
|
|
|
});
|
2011-10-04 20:51:34 +02:00
|
|
|
}
|
2012-02-18 15:01:35 -08:00
|
|
|
self.onSocket(conn);
|
2011-07-26 00:21:52 +02:00
|
|
|
}
|
2011-07-26 00:15:15 +02:00
|
|
|
|
2011-10-19 16:28:44 -07:00
|
|
|
self._deferToConnect(null, null, function() {
|
2011-10-04 20:51:34 +02:00
|
|
|
self._flush();
|
2012-05-03 10:20:20 -07:00
|
|
|
self = null;
|
2011-10-19 16:28:44 -07:00
|
|
|
});
|
2011-10-04 20:51:34 +02:00
|
|
|
|
2009-07-16 10:59:40 +02:00
|
|
|
}
|
2010-10-12 08:04:09 +11:00
|
|
|
util.inherits(ClientRequest, OutgoingMessage);
|
2010-11-29 07:41:08 -08:00
|
|
|
|
2009-11-04 12:37:52 +01:00
|
|
|
exports.ClientRequest = ClientRequest;
|
2009-07-14 18:31:50 +02:00
|
|
|
|
2011-02-10 02:18:13 -08:00
|
|
|
ClientRequest.prototype._implicitHeader = function() {
|
2012-02-18 15:01:35 -08:00
|
|
|
this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n',
|
|
|
|
this._renderHeaders());
|
2011-10-04 20:51:34 +02:00
|
|
|
};
|
2010-11-29 07:41:08 -08:00
|
|
|
|
2011-02-04 15:14:58 -08:00
|
|
|
ClientRequest.prototype.abort = function() {
|
2011-10-04 20:51:34 +02:00
|
|
|
if (this.socket) {
|
2011-07-26 00:21:52 +02:00
|
|
|
// in-progress
|
2011-10-04 20:51:34 +02:00
|
|
|
this.socket.destroy();
|
|
|
|
} else {
|
|
|
|
// haven't been assigned a socket yet.
|
|
|
|
// this could be more efficient, it could
|
|
|
|
// remove itself from the pending requests
|
|
|
|
this._deferToConnect('destroy', []);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-10-12 17:31:32 -07:00
|
|
|
|
|
|
|
function createHangUpError() {
|
|
|
|
var error = new Error('socket hang up');
|
|
|
|
error.code = 'ECONNRESET';
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2012-05-15 14:19:46 -07:00
|
|
|
|
2012-05-10 17:55:28 -07:00
|
|
|
function socketCloseListener() {
|
|
|
|
var socket = this;
|
|
|
|
var parser = socket.parser;
|
|
|
|
var req = socket._httpMessage;
|
|
|
|
debug('HTTP socket close');
|
|
|
|
req.emit('close');
|
|
|
|
if (req.res && req.res.readable) {
|
2012-05-15 14:19:46 -07:00
|
|
|
// Socket closed before we emitted 'end' below.
|
2012-05-10 17:55:28 -07:00
|
|
|
req.res.emit('aborted');
|
2012-05-15 14:19:46 -07:00
|
|
|
var res = req.res;
|
2012-12-12 22:24:17 -08:00
|
|
|
res.on('end', function() {
|
2012-05-15 14:19:46 -07:00
|
|
|
res.emit('close');
|
|
|
|
});
|
2013-01-07 20:23:07 -08:00
|
|
|
res.push(null);
|
2012-05-10 17:55:28 -07:00
|
|
|
} else if (!req.res && !req._hadError) {
|
|
|
|
// This socket error fired before we started to
|
|
|
|
// receive a response. The error needs to
|
|
|
|
// fire on the request.
|
|
|
|
req.emit('error', createHangUpError());
|
http: Raise hangup error on destroyed socket write
Prior to v0.10, Node ignored ECONNRESET errors in many situations.
There *are* valid cases in which ECONNRESET should be ignored as a
normal part of the TCP dance, but in many others, it's a very relevant
signal that must be heeded with care.
Exacerbating this problem, if the OutgoingMessage does not have a
req.connection._handle, it assumes that it is in the process of
connecting, and thus buffers writes up in an array.
The problem happens when you reuse a socket between two requests, and it
is destroyed abruptly in between them. The writes will be buffered,
because the socket has no handle, but it's not ever going to GET a
handle, because it's not connecting, it's destroyed.
The proper fix is to treat ECONNRESET correctly. However, this is a
behavior/semantics change, and cannot land in a stable branch.
Fix #4775
2013-02-14 08:57:32 -08:00
|
|
|
req._hadError = true;
|
2012-05-10 17:55:28 -07:00
|
|
|
}
|
|
|
|
|
http: Raise hangup error on destroyed socket write
Prior to v0.10, Node ignored ECONNRESET errors in many situations.
There *are* valid cases in which ECONNRESET should be ignored as a
normal part of the TCP dance, but in many others, it's a very relevant
signal that must be heeded with care.
Exacerbating this problem, if the OutgoingMessage does not have a
req.connection._handle, it assumes that it is in the process of
connecting, and thus buffers writes up in an array.
The problem happens when you reuse a socket between two requests, and it
is destroyed abruptly in between them. The writes will be buffered,
because the socket has no handle, but it's not ever going to GET a
handle, because it's not connecting, it's destroyed.
The proper fix is to treat ECONNRESET correctly. However, this is a
behavior/semantics change, and cannot land in a stable branch.
Fix #4775
2013-02-14 08:57:32 -08:00
|
|
|
// Too bad. That output wasn't getting written.
|
|
|
|
// This is pretty terrible that it doesn't raise an error.
|
|
|
|
// Fixed better in v0.10
|
|
|
|
if (req.output)
|
|
|
|
req.output.length = 0;
|
|
|
|
if (req.outputEncodings)
|
|
|
|
req.outputEncodings.length = 0;
|
|
|
|
|
2012-05-10 17:55:28 -07:00
|
|
|
if (parser) {
|
|
|
|
parser.finish();
|
|
|
|
freeParser(parser, req);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function socketErrorListener(err) {
|
|
|
|
var socket = this;
|
|
|
|
var parser = socket.parser;
|
|
|
|
var req = socket._httpMessage;
|
|
|
|
debug('HTTP SOCKET ERROR: ' + err.message + '\n' + err.stack);
|
2012-05-15 14:19:46 -07:00
|
|
|
|
2012-05-10 17:55:28 -07:00
|
|
|
if (req) {
|
|
|
|
req.emit('error', err);
|
|
|
|
// For Safety. Some additional errors might fire later on
|
|
|
|
// and we need to make sure we don't double-fire the error event.
|
|
|
|
req._hadError = true;
|
|
|
|
}
|
2012-05-15 14:19:46 -07:00
|
|
|
|
2012-05-10 17:55:28 -07:00
|
|
|
if (parser) {
|
|
|
|
parser.finish();
|
|
|
|
freeParser(parser, req);
|
|
|
|
}
|
|
|
|
socket.destroy();
|
|
|
|
}
|
|
|
|
|
2012-05-15 14:19:46 -07:00
|
|
|
function socketOnEnd() {
|
|
|
|
var socket = this;
|
|
|
|
var req = this._httpMessage;
|
|
|
|
var parser = this.parser;
|
2012-05-15 11:37:34 -07:00
|
|
|
|
2012-05-15 14:19:46 -07:00
|
|
|
if (!req.res) {
|
|
|
|
// If we don't have a response then we know that the socket
|
|
|
|
// ended prematurely and we need to emit an error on the request.
|
|
|
|
req.emit('error', createHangUpError());
|
|
|
|
req._hadError = true;
|
|
|
|
}
|
|
|
|
if (parser) {
|
|
|
|
parser.finish();
|
|
|
|
freeParser(parser, req);
|
|
|
|
}
|
|
|
|
socket.destroy();
|
|
|
|
}
|
2012-05-10 17:55:28 -07:00
|
|
|
|
2012-05-15 14:19:46 -07:00
|
|
|
function socketOnData(d, start, end) {
|
|
|
|
var socket = this;
|
|
|
|
var req = this._httpMessage;
|
|
|
|
var parser = this.parser;
|
|
|
|
|
|
|
|
var ret = parser.execute(d, start, end - start);
|
|
|
|
if (ret instanceof Error) {
|
|
|
|
debug('parse error');
|
|
|
|
freeParser(parser, req);
|
2012-08-06 20:42:13 -04:00
|
|
|
socket.destroy();
|
|
|
|
req.emit('error', ret);
|
|
|
|
req._hadError = true;
|
2012-05-15 14:19:46 -07:00
|
|
|
} else if (parser.incoming && parser.incoming.upgrade) {
|
|
|
|
// Upgrade or CONNECT
|
|
|
|
var bytesParsed = ret;
|
|
|
|
var res = parser.incoming;
|
|
|
|
req.res = res;
|
|
|
|
|
|
|
|
socket.ondata = null;
|
|
|
|
socket.onend = null;
|
|
|
|
parser.finish();
|
|
|
|
|
|
|
|
// This is start + byteParsed
|
|
|
|
var bodyHead = d.slice(start + bytesParsed, end);
|
|
|
|
|
|
|
|
var eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade';
|
2013-02-14 00:48:11 -08:00
|
|
|
if (EventEmitter.listenerCount(req, eventName) > 0) {
|
2012-05-15 14:19:46 -07:00
|
|
|
req.upgradeOrConnect = true;
|
|
|
|
|
|
|
|
// detach the socket
|
|
|
|
socket.emit('agentRemove');
|
|
|
|
socket.removeListener('close', socketCloseListener);
|
|
|
|
socket.removeListener('error', socketErrorListener);
|
|
|
|
|
|
|
|
req.emit(eventName, res, socket, bodyHead);
|
|
|
|
req.emit('close');
|
|
|
|
} else {
|
|
|
|
// Got Upgrade header or CONNECT method, but have no handler.
|
|
|
|
socket.destroy();
|
2012-05-10 17:55:28 -07:00
|
|
|
}
|
2012-05-15 14:19:46 -07:00
|
|
|
freeParser(parser, req);
|
|
|
|
} else if (parser.incoming && parser.incoming.complete &&
|
|
|
|
// When the status code is 100 (Continue), the server will
|
|
|
|
// send a final response after this client sends a request
|
|
|
|
// body. So, we must not free the parser.
|
|
|
|
parser.incoming.statusCode !== 100) {
|
|
|
|
freeParser(parser, req);
|
2012-05-10 17:55:28 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-15 14:19:46 -07:00
|
|
|
|
2012-12-12 22:24:17 -08:00
|
|
|
// client
|
2012-05-10 17:55:28 -07:00
|
|
|
function parserOnIncomingClient(res, shouldKeepAlive) {
|
|
|
|
var parser = this;
|
|
|
|
var socket = this.socket;
|
|
|
|
var req = socket._httpMessage;
|
|
|
|
|
2012-12-12 22:24:17 -08:00
|
|
|
|
2012-09-24 11:30:26 -07:00
|
|
|
// propogate "domain" setting...
|
|
|
|
if (req.domain && !res.domain) {
|
|
|
|
debug('setting "res.domain"');
|
|
|
|
res.domain = req.domain;
|
|
|
|
}
|
|
|
|
|
2012-05-10 17:55:28 -07:00
|
|
|
debug('AGENT incoming response!');
|
|
|
|
|
|
|
|
if (req.res) {
|
|
|
|
// We already have a response object, this means the server
|
|
|
|
// sent a double response.
|
|
|
|
socket.destroy();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
req.res = res;
|
|
|
|
|
2012-05-15 14:19:46 -07:00
|
|
|
// Responses to CONNECT request is handled as Upgrade.
|
|
|
|
if (req.method === 'CONNECT') {
|
|
|
|
res.upgrade = true;
|
|
|
|
return true; // skip body
|
|
|
|
}
|
|
|
|
|
2012-05-10 17:55:28 -07:00
|
|
|
// Responses to HEAD requests are crazy.
|
|
|
|
// HEAD responses aren't allowed to have an entity-body
|
|
|
|
// but *can* have a content-length which actually corresponds
|
|
|
|
// to the content-length of the entity-body had the request
|
|
|
|
// been a GET.
|
|
|
|
var isHeadResponse = req.method == 'HEAD';
|
|
|
|
debug('AGENT isHeadResponse ' + isHeadResponse);
|
|
|
|
|
|
|
|
if (res.statusCode == 100) {
|
|
|
|
// restart the parser, as this is a continue message.
|
|
|
|
delete req.res; // Clear res so that we don't hit double-responses.
|
|
|
|
req.emit('continue');
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-05-15 14:19:46 -07:00
|
|
|
if (req.shouldKeepAlive && !shouldKeepAlive && !req.upgradeOrConnect) {
|
2012-05-10 17:55:28 -07:00
|
|
|
// Server MUST respond with Connection:keep-alive for us to enable it.
|
|
|
|
// If we've been upgraded (via WebSockets) we also shouldn't try to
|
|
|
|
// keep the connection open.
|
|
|
|
req.shouldKeepAlive = false;
|
|
|
|
}
|
|
|
|
|
2012-05-15 14:19:46 -07:00
|
|
|
|
2012-05-10 17:55:28 -07:00
|
|
|
DTRACE_HTTP_CLIENT_RESPONSE(socket, req);
|
2012-11-21 00:27:22 +01:00
|
|
|
COUNTER_HTTP_CLIENT_RESPONSE();
|
2012-05-10 17:55:28 -07:00
|
|
|
req.res = res;
|
|
|
|
res.req = req;
|
2013-04-01 17:19:05 -07:00
|
|
|
|
|
|
|
// add our listener first, so that we guarantee socket cleanup
|
2012-05-10 17:55:28 -07:00
|
|
|
res.on('end', responseOnEnd);
|
2013-04-01 17:19:05 -07:00
|
|
|
var handled = req.emit('response', res);
|
2012-05-10 17:55:28 -07:00
|
|
|
|
2012-12-12 22:24:17 -08:00
|
|
|
// If the user did not listen for the 'response' event, then they
|
2012-12-21 16:48:32 +00:00
|
|
|
// can't possibly read the data, so we ._dump() it into the void
|
2012-12-12 22:24:17 -08:00
|
|
|
// so that the socket doesn't hang there in a paused state.
|
|
|
|
if (!handled)
|
2012-12-18 18:49:09 -08:00
|
|
|
res._dump();
|
2012-12-12 22:24:17 -08:00
|
|
|
|
2012-05-10 17:55:28 -07:00
|
|
|
return isHeadResponse;
|
|
|
|
}
|
|
|
|
|
2012-12-12 22:24:17 -08:00
|
|
|
// client
|
2012-05-15 14:19:46 -07:00
|
|
|
function responseOnEnd() {
|
|
|
|
var res = this;
|
|
|
|
var req = res.req;
|
|
|
|
var socket = req.socket;
|
2012-05-10 17:55:28 -07:00
|
|
|
|
2012-05-15 14:19:46 -07:00
|
|
|
if (!req.shouldKeepAlive) {
|
|
|
|
if (socket.writable) {
|
|
|
|
debug('AGENT socket.destroySoon()');
|
|
|
|
socket.destroySoon();
|
2012-05-10 17:55:28 -07:00
|
|
|
}
|
2012-05-15 14:19:46 -07:00
|
|
|
assert(!socket.writable);
|
|
|
|
} else {
|
|
|
|
debug('AGENT socket keep-alive');
|
2012-09-09 14:32:40 +02:00
|
|
|
if (req.timeoutCb) {
|
|
|
|
socket.setTimeout(0, req.timeoutCb);
|
|
|
|
req.timeoutCb = null;
|
|
|
|
}
|
2012-05-15 14:19:46 -07:00
|
|
|
socket.removeListener('close', socketCloseListener);
|
|
|
|
socket.removeListener('error', socketErrorListener);
|
2013-04-01 17:19:05 -07:00
|
|
|
// Mark this socket as available, AFTER user-added end
|
|
|
|
// handlers have a chance to run.
|
|
|
|
process.nextTick(function() {
|
|
|
|
socket.emit('free');
|
|
|
|
});
|
2012-05-10 17:55:28 -07:00
|
|
|
}
|
|
|
|
}
|
2011-10-12 17:31:32 -07:00
|
|
|
|
2011-10-04 20:51:34 +02:00
|
|
|
ClientRequest.prototype.onSocket = function(socket) {
|
|
|
|
var req = this;
|
2012-05-10 17:55:28 -07:00
|
|
|
|
2011-10-19 16:28:44 -07:00
|
|
|
process.nextTick(function() {
|
2011-10-04 20:51:34 +02:00
|
|
|
var parser = parsers.alloc();
|
|
|
|
req.socket = socket;
|
|
|
|
req.connection = socket;
|
2012-05-10 17:55:28 -07:00
|
|
|
parser.reinitialize(HTTPParser.RESPONSE);
|
2012-05-15 14:19:46 -07:00
|
|
|
parser.socket = socket;
|
2011-10-04 20:51:34 +02:00
|
|
|
parser.incoming = null;
|
|
|
|
req.parser = parser;
|
|
|
|
|
2012-05-15 14:19:46 -07:00
|
|
|
socket.parser = parser;
|
|
|
|
socket._httpMessage = req;
|
|
|
|
|
|
|
|
// Setup "drain" propogation.
|
|
|
|
httpSocketSetup(socket);
|
|
|
|
|
2012-01-16 01:45:31 +06:00
|
|
|
// Propagate headers limit from request object to parser
|
2012-01-16 16:11:29 +06:00
|
|
|
if (typeof req.maxHeadersCount === 'number') {
|
|
|
|
parser.maxHeaderPairs = req.maxHeadersCount << 1;
|
|
|
|
} else {
|
|
|
|
// Set default value because parser may be reused from FreeList
|
|
|
|
parser.maxHeaderPairs = 2000;
|
2012-01-16 01:45:31 +06:00
|
|
|
}
|
|
|
|
|
2012-05-15 14:19:46 -07:00
|
|
|
socket.on('error', socketErrorListener);
|
2012-05-10 17:55:28 -07:00
|
|
|
socket.ondata = socketOnData;
|
|
|
|
socket.onend = socketOnEnd;
|
|
|
|
socket.on('close', socketCloseListener);
|
|
|
|
parser.onIncoming = parserOnIncomingClient;
|
2011-10-04 20:51:34 +02:00
|
|
|
req.emit('socket', socket);
|
|
|
|
});
|
2012-05-15 14:19:46 -07:00
|
|
|
|
2011-10-04 20:51:34 +02:00
|
|
|
};
|
2012-05-02 12:13:54 -07:00
|
|
|
|
2011-11-03 01:20:54 +01:00
|
|
|
ClientRequest.prototype._deferToConnect = function(method, arguments_, cb) {
|
2011-10-04 20:51:34 +02:00
|
|
|
// This function is for calls that need to happen once the socket is
|
|
|
|
// connected and writable. It's an important promisy thing for all the socket
|
|
|
|
// calls that happen either now (when a socket is assigned) or
|
|
|
|
// in the future (when a socket gets assigned out of the pool and is
|
|
|
|
// eventually writable).
|
|
|
|
var self = this;
|
|
|
|
var onSocket = function() {
|
|
|
|
if (self.socket.writable) {
|
|
|
|
if (method) {
|
2011-11-03 01:20:54 +01:00
|
|
|
self.socket[method].apply(self.socket, arguments_);
|
2011-10-04 20:51:34 +02:00
|
|
|
}
|
|
|
|
if (cb) { cb(); }
|
|
|
|
} else {
|
2012-01-12 14:16:03 +09:00
|
|
|
self.socket.once('connect', function() {
|
2011-10-04 20:51:34 +02:00
|
|
|
if (method) {
|
2011-11-03 01:20:54 +01:00
|
|
|
self.socket[method].apply(self.socket, arguments_);
|
2011-10-04 20:51:34 +02:00
|
|
|
}
|
|
|
|
if (cb) { cb(); }
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!self.socket) {
|
|
|
|
self.once('socket', onSocket);
|
2011-02-04 15:14:58 -08:00
|
|
|
} else {
|
2011-10-04 20:51:34 +02:00
|
|
|
onSocket();
|
|
|
|
}
|
|
|
|
};
|
2012-05-02 12:13:54 -07:00
|
|
|
|
|
|
|
ClientRequest.prototype.setTimeout = function(msecs, callback) {
|
|
|
|
if (callback) this.once('timeout', callback);
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
function emitTimeout() {
|
|
|
|
self.emit('timeout');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.socket && this.socket.writable) {
|
2012-09-09 14:32:40 +02:00
|
|
|
if (this.timeoutCb)
|
|
|
|
this.socket.setTimeout(0, this.timeoutCb);
|
|
|
|
this.timeoutCb = emitTimeout;
|
2012-05-02 12:13:54 -07:00
|
|
|
this.socket.setTimeout(msecs, emitTimeout);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-03-04 22:44:56 +02:00
|
|
|
// Set timeoutCb so that it'll get cleaned up on request end
|
|
|
|
this.timeoutCb = emitTimeout;
|
2012-05-02 12:13:54 -07:00
|
|
|
if (this.socket) {
|
2013-03-04 22:44:56 +02:00
|
|
|
var sock = this.socket;
|
2012-05-03 10:39:16 -07:00
|
|
|
this.socket.once('connect', function() {
|
2013-03-04 22:44:56 +02:00
|
|
|
sock.setTimeout(msecs, emitTimeout);
|
2012-05-02 12:13:54 -07:00
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-05-03 10:39:16 -07:00
|
|
|
this.once('socket', function(sock) {
|
2013-03-04 22:44:56 +02:00
|
|
|
sock.setTimeout(msecs, emitTimeout);
|
2012-05-02 12:13:54 -07:00
|
|
|
});
|
2011-10-04 20:51:34 +02:00
|
|
|
};
|
2012-05-02 12:13:54 -07:00
|
|
|
|
2011-10-04 20:51:34 +02:00
|
|
|
ClientRequest.prototype.setNoDelay = function() {
|
|
|
|
this._deferToConnect('setNoDelay', arguments);
|
|
|
|
};
|
|
|
|
ClientRequest.prototype.setSocketKeepAlive = function() {
|
|
|
|
this._deferToConnect('setKeepAlive', arguments);
|
|
|
|
};
|
|
|
|
|
2012-05-04 17:14:09 -07:00
|
|
|
ClientRequest.prototype.clearTimeout = function(cb) {
|
|
|
|
this.setTimeout(0, cb);
|
2012-04-08 20:54:53 +05:30
|
|
|
};
|
2011-10-04 20:51:34 +02:00
|
|
|
|
|
|
|
exports.request = function(options, cb) {
|
2012-05-16 16:27:34 +02:00
|
|
|
if (typeof options === 'string') {
|
|
|
|
options = url.parse(options);
|
2013-04-12 15:54:31 +02:00
|
|
|
} else if (options && options.path) {
|
|
|
|
options = util._extend({}, options);
|
|
|
|
options.path = encodeURI(options.path);
|
|
|
|
// encodeURI() doesn't escape quotes while url.parse() does. Fix up.
|
|
|
|
options.path = options.path.replace(/'/g, '%27');
|
2012-05-16 16:27:34 +02:00
|
|
|
}
|
|
|
|
|
2011-10-14 17:10:49 -07:00
|
|
|
if (options.protocol && options.protocol !== 'http:') {
|
|
|
|
throw new Error('Protocol:' + options.protocol + ' not supported.');
|
|
|
|
}
|
|
|
|
|
2011-10-04 20:51:34 +02:00
|
|
|
return new ClientRequest(options, cb);
|
2011-02-04 15:14:58 -08:00
|
|
|
};
|
|
|
|
|
2011-10-04 20:51:34 +02:00
|
|
|
exports.get = function(options, cb) {
|
|
|
|
var req = exports.request(options, cb);
|
|
|
|
req.end();
|
|
|
|
return req;
|
|
|
|
};
|
2011-02-04 15:14:58 -08:00
|
|
|
|
2012-01-24 15:49:32 +01:00
|
|
|
|
|
|
|
function ondrain() {
|
|
|
|
if (this._httpMessage) this._httpMessage.emit('drain');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
function httpSocketSetup(socket) {
|
2012-01-24 15:49:32 +01:00
|
|
|
socket.removeListener('drain', ondrain);
|
|
|
|
socket.on('drain', ondrain);
|
2009-07-14 18:31:50 +02:00
|
|
|
}
|
2009-05-18 19:33:05 +02:00
|
|
|
|
2009-07-16 10:59:40 +02:00
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
function Server(requestListener) {
|
2010-08-27 13:58:52 -07:00
|
|
|
if (!(this instanceof Server)) return new Server(requestListener);
|
2010-10-25 22:04:39 -07:00
|
|
|
net.Server.call(this, { allowHalfOpen: true });
|
2010-05-02 11:01:06 +10:00
|
|
|
|
2010-10-25 22:04:39 -07:00
|
|
|
if (requestListener) {
|
2010-12-01 18:07:20 -08:00
|
|
|
this.addListener('request', requestListener);
|
2010-04-30 23:35:51 +10:00
|
|
|
}
|
2010-05-02 11:01:06 +10:00
|
|
|
|
2011-02-04 15:14:58 -08:00
|
|
|
// Similar option to this. Too lazy to write my own docs.
|
|
|
|
// http://www.squid-cache.org/Doc/config/half_closed_clients/
|
|
|
|
// http://wiki.squid-cache.org/SquidFaq/InnerWorkings#What_is_a_half-closed_filedescriptor.3F
|
|
|
|
this.httpAllowHalfOpen = false;
|
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
this.addListener('connection', connectionListener);
|
2012-10-08 01:22:44 +02:00
|
|
|
|
|
|
|
this.addListener('clientError', function(err, conn) {
|
|
|
|
conn.destroy(err);
|
|
|
|
});
|
http: More useful setTimeout API on server
This adds the following to HTTP:
* server.setTimeout(msecs, callback)
Sets all new connections to time out after the specified time, at
which point it emits 'timeout' on the server, passing the socket as an
argument.
In this way, timeouts can be handled in one place consistently.
* req.setTimeout(), res.setTimeout()
Essentially an alias to req/res.socket.setTimeout(), but without
having to delve into a "buried" object. Adds a listener on the
req/res object, but not on the socket.
* server.timeout
Number of milliseconds before incoming connections time out.
(Default=1000*60*2, as before.)
Furthermore, if the user sets up their own timeout listener on either
the server, the request, or the response, then the default behavior
(destroying the socket) is suppressed.
Fix #3460
2013-03-03 23:29:22 -08:00
|
|
|
|
|
|
|
this.timeout = 2 * 60 * 1000;
|
2010-03-19 19:22:04 -07:00
|
|
|
}
|
2010-10-12 08:04:09 +11:00
|
|
|
util.inherits(Server, net.Server);
|
2010-03-19 19:22:04 -07:00
|
|
|
|
2010-11-29 07:41:08 -08:00
|
|
|
|
http: More useful setTimeout API on server
This adds the following to HTTP:
* server.setTimeout(msecs, callback)
Sets all new connections to time out after the specified time, at
which point it emits 'timeout' on the server, passing the socket as an
argument.
In this way, timeouts can be handled in one place consistently.
* req.setTimeout(), res.setTimeout()
Essentially an alias to req/res.socket.setTimeout(), but without
having to delve into a "buried" object. Adds a listener on the
req/res object, but not on the socket.
* server.timeout
Number of milliseconds before incoming connections time out.
(Default=1000*60*2, as before.)
Furthermore, if the user sets up their own timeout listener on either
the server, the request, or the response, then the default behavior
(destroying the socket) is suppressed.
Fix #3460
2013-03-03 23:29:22 -08:00
|
|
|
Server.prototype.setTimeout = function(msecs, callback) {
|
|
|
|
this.timeout = msecs;
|
|
|
|
if (callback)
|
|
|
|
this.on('timeout', callback);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2010-03-19 19:22:04 -07:00
|
|
|
exports.Server = Server;
|
|
|
|
|
2010-11-29 07:41:08 -08:00
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
exports.createServer = function(requestListener) {
|
2010-03-19 19:22:04 -07:00
|
|
|
return new Server(requestListener);
|
2009-07-14 18:31:50 +02:00
|
|
|
};
|
2009-05-18 19:33:05 +02:00
|
|
|
|
2010-11-29 07:41:08 -08:00
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
function connectionListener(socket) {
|
2010-03-19 19:22:04 -07:00
|
|
|
var self = this;
|
2011-01-20 02:41:16 -08:00
|
|
|
var outgoing = [];
|
2011-02-04 15:14:58 -08:00
|
|
|
var incoming = [];
|
|
|
|
|
|
|
|
function abortIncoming() {
|
|
|
|
while (incoming.length) {
|
|
|
|
var req = incoming.shift();
|
|
|
|
req.emit('aborted');
|
2011-07-05 15:42:32 -07:00
|
|
|
req.emit('close');
|
2011-02-04 15:14:58 -08:00
|
|
|
}
|
|
|
|
// abort socket._httpMessage ?
|
|
|
|
}
|
2010-05-26 17:59:55 -07:00
|
|
|
|
2012-01-09 03:51:06 +01:00
|
|
|
function serverSocketCloseListener() {
|
|
|
|
debug('server socket close');
|
2012-05-04 10:40:27 -07:00
|
|
|
// mark this parser as reusable
|
2013-02-25 09:06:44 -08:00
|
|
|
if (this.parser)
|
|
|
|
freeParser(this.parser);
|
2012-01-09 03:51:06 +01:00
|
|
|
|
|
|
|
abortIncoming();
|
|
|
|
}
|
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
debug('SERVER new http connection');
|
2010-05-26 17:59:55 -07:00
|
|
|
|
|
|
|
httpSocketSetup(socket);
|
2009-05-18 19:33:05 +02:00
|
|
|
|
http: More useful setTimeout API on server
This adds the following to HTTP:
* server.setTimeout(msecs, callback)
Sets all new connections to time out after the specified time, at
which point it emits 'timeout' on the server, passing the socket as an
argument.
In this way, timeouts can be handled in one place consistently.
* req.setTimeout(), res.setTimeout()
Essentially an alias to req/res.socket.setTimeout(), but without
having to delve into a "buried" object. Adds a listener on the
req/res object, but not on the socket.
* server.timeout
Number of milliseconds before incoming connections time out.
(Default=1000*60*2, as before.)
Furthermore, if the user sets up their own timeout listener on either
the server, the request, or the response, then the default behavior
(destroying the socket) is suppressed.
Fix #3460
2013-03-03 23:29:22 -08:00
|
|
|
// If the user has added a listener to the server,
|
|
|
|
// request, or response, then it's their responsibility.
|
|
|
|
// otherwise, destroy on timeout by default
|
|
|
|
if (self.timeout)
|
|
|
|
socket.setTimeout(self.timeout);
|
|
|
|
socket.on('timeout', function() {
|
|
|
|
var req = socket.parser && socket.parser.incoming;
|
2013-03-10 20:10:19 +09:00
|
|
|
var reqTimeout = req && !req.complete && req.emit('timeout', socket);
|
http: More useful setTimeout API on server
This adds the following to HTTP:
* server.setTimeout(msecs, callback)
Sets all new connections to time out after the specified time, at
which point it emits 'timeout' on the server, passing the socket as an
argument.
In this way, timeouts can be handled in one place consistently.
* req.setTimeout(), res.setTimeout()
Essentially an alias to req/res.socket.setTimeout(), but without
having to delve into a "buried" object. Adds a listener on the
req/res object, but not on the socket.
* server.timeout
Number of milliseconds before incoming connections time out.
(Default=1000*60*2, as before.)
Furthermore, if the user sets up their own timeout listener on either
the server, the request, or the response, then the default behavior
(destroying the socket) is suppressed.
Fix #3460
2013-03-03 23:29:22 -08:00
|
|
|
var res = socket._httpMessage;
|
|
|
|
var resTimeout = res && res.emit('timeout', socket);
|
|
|
|
var serverTimeout = self.emit('timeout', socket);
|
|
|
|
|
|
|
|
if (!reqTimeout && !resTimeout && !serverTimeout)
|
|
|
|
socket.destroy();
|
2010-05-12 11:42:01 -07:00
|
|
|
});
|
|
|
|
|
2010-04-13 02:27:32 +10:00
|
|
|
var parser = parsers.alloc();
|
2011-09-27 17:07:56 +02:00
|
|
|
parser.reinitialize(HTTPParser.REQUEST);
|
2010-04-13 02:27:32 +10:00
|
|
|
parser.socket = socket;
|
2012-05-15 14:19:46 -07:00
|
|
|
socket.parser = parser;
|
2011-01-20 15:55:02 -08:00
|
|
|
parser.incoming = null;
|
2010-03-19 19:22:04 -07:00
|
|
|
|
2012-01-16 01:45:31 +06:00
|
|
|
// Propagate headers limit from server instance to parser
|
2012-01-16 16:11:29 +06:00
|
|
|
if (typeof this.maxHeadersCount === 'number') {
|
|
|
|
parser.maxHeaderPairs = this.maxHeadersCount << 1;
|
|
|
|
} else {
|
|
|
|
// Set default value because parser may be reused from FreeList
|
|
|
|
parser.maxHeaderPairs = 2000;
|
2012-01-16 01:45:31 +06:00
|
|
|
}
|
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
socket.addListener('error', function(e) {
|
2012-10-08 01:22:44 +02:00
|
|
|
self.emit('clientError', e, this);
|
2010-04-22 17:22:03 -07:00
|
|
|
});
|
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
socket.ondata = function(d, start, end) {
|
2010-05-20 15:21:40 -07:00
|
|
|
var ret = parser.execute(d, start, end - start);
|
|
|
|
if (ret instanceof Error) {
|
2010-12-01 18:07:20 -08:00
|
|
|
debug('parse error');
|
2010-05-20 15:21:40 -07:00
|
|
|
socket.destroy(ret);
|
|
|
|
} else if (parser.incoming && parser.incoming.upgrade) {
|
2012-01-09 03:51:06 +01:00
|
|
|
// Upgrade or CONNECT
|
2010-05-20 15:21:40 -07:00
|
|
|
var bytesParsed = ret;
|
2012-01-09 03:51:06 +01:00
|
|
|
var req = parser.incoming;
|
|
|
|
|
2010-04-14 03:52:15 -07:00
|
|
|
socket.ondata = null;
|
|
|
|
socket.onend = null;
|
2012-01-09 03:51:06 +01:00
|
|
|
socket.removeListener('close', serverSocketCloseListener);
|
|
|
|
parser.finish();
|
2012-05-04 10:40:27 -07:00
|
|
|
freeParser(parser, req);
|
2010-05-02 11:01:06 +10:00
|
|
|
|
2011-08-08 17:08:38 -07:00
|
|
|
// This is start + byteParsed
|
2012-01-09 03:51:06 +01:00
|
|
|
var bodyHead = d.slice(start + bytesParsed, end);
|
2010-05-02 11:01:06 +10:00
|
|
|
|
2012-01-09 03:51:06 +01:00
|
|
|
var eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade';
|
2013-02-14 00:48:11 -08:00
|
|
|
if (EventEmitter.listenerCount(self, eventName) > 0) {
|
2012-01-09 03:51:06 +01:00
|
|
|
self.emit(eventName, req, req.socket, bodyHead);
|
2010-04-30 23:33:11 +10:00
|
|
|
} else {
|
2012-01-09 03:51:06 +01:00
|
|
|
// Got upgrade header or CONNECT method, but have no handler.
|
2010-05-03 11:23:36 -07:00
|
|
|
socket.destroy();
|
2010-04-30 23:33:11 +10:00
|
|
|
}
|
2010-04-14 03:52:15 -07:00
|
|
|
}
|
2010-03-19 19:22:04 -07:00
|
|
|
};
|
|
|
|
|
2010-12-01 18:07:20 -08:00
|
|
|
socket.onend = function() {
|
2011-02-04 15:14:58 -08:00
|
|
|
var ret = parser.finish();
|
2010-05-20 15:21:40 -07:00
|
|
|
|
2011-02-04 15:14:58 -08:00
|
|
|
if (ret instanceof Error) {
|
|
|
|
debug('parse error');
|
|
|
|
socket.destroy(ret);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!self.httpAllowHalfOpen) {
|
|
|
|
abortIncoming();
|
2011-02-15 16:45:54 -08:00
|
|
|
if (socket.writable) socket.end();
|
2011-02-04 15:14:58 -08:00
|
|
|
} else if (outgoing.length) {
|
2011-01-20 02:41:16 -08:00
|
|
|
outgoing[outgoing.length - 1]._last = true;
|
|
|
|
} else if (socket._httpMessage) {
|
|
|
|
socket._httpMessage._last = true;
|
2010-05-26 17:59:55 -07:00
|
|
|
} else {
|
2011-02-15 16:45:54 -08:00
|
|
|
if (socket.writable) socket.end();
|
2010-05-26 17:59:55 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-01-09 03:51:06 +01:00
|
|
|
socket.addListener('close', serverSocketCloseListener);
|
2009-11-21 16:27:02 +01:00
|
|
|
|
2010-03-19 19:22:04 -07:00
|
|
|
// The following callback is issued after the headers have been read on a
|
|
|
|
// new message. In this callback we setup the response object and pass it
|
|
|
|
// to the user.
|
2010-12-01 18:07:20 -08:00
|
|
|
parser.onIncoming = function(req, shouldKeepAlive) {
|
2011-02-04 15:14:58 -08:00
|
|
|
incoming.push(req);
|
|
|
|
|
2009-12-05 08:37:46 +01:00
|
|
|
var res = new ServerResponse(req);
|
2012-12-12 22:24:17 -08:00
|
|
|
|
2010-03-23 21:31:44 -07:00
|
|
|
res.shouldKeepAlive = shouldKeepAlive;
|
2011-01-24 17:50:10 -08:00
|
|
|
DTRACE_HTTP_SERVER_REQUEST(req, socket);
|
2012-11-21 00:27:22 +01:00
|
|
|
COUNTER_HTTP_SERVER_REQUEST();
|
2011-01-20 02:41:16 -08:00
|
|
|
|
|
|
|
if (socket._httpMessage) {
|
|
|
|
// There are already pending outgoing res, append.
|
|
|
|
outgoing.push(res);
|
|
|
|
} else {
|
|
|
|
res.assignSocket(socket);
|
|
|
|
}
|
|
|
|
|
|
|
|
// When we're finished writing the response, check if this is the last
|
|
|
|
// respose, if so destroy the socket.
|
2012-10-03 17:40:14 -07:00
|
|
|
res.on('finish', function() {
|
2011-05-20 15:39:48 -07:00
|
|
|
// Usually the first incoming element should be our request. it may
|
|
|
|
// be that in the case abortIncoming() was called that the incoming
|
|
|
|
// array will be empty.
|
|
|
|
assert(incoming.length == 0 || incoming[0] === req);
|
|
|
|
|
2011-02-04 15:14:58 -08:00
|
|
|
incoming.shift();
|
|
|
|
|
2012-12-12 22:24:17 -08:00
|
|
|
// if the user never called req.read(), and didn't pipe() or
|
2012-12-21 16:48:32 +00:00
|
|
|
// .resume() or .on('data'), then we call req._dump() so that the
|
2012-12-12 22:24:17 -08:00
|
|
|
// bytes will be pulled off the wire.
|
|
|
|
if (!req._consuming)
|
2012-12-18 18:49:09 -08:00
|
|
|
req._dump();
|
2012-12-12 22:24:17 -08:00
|
|
|
|
2011-01-20 10:29:54 -08:00
|
|
|
res.detachSocket(socket);
|
|
|
|
|
2011-01-20 02:41:16 -08:00
|
|
|
if (res._last) {
|
|
|
|
socket.destroySoon();
|
|
|
|
} else {
|
|
|
|
// start sending the next message
|
|
|
|
var m = outgoing.shift();
|
|
|
|
if (m) {
|
|
|
|
m.assignSocket(socket);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2009-08-26 22:03:19 +02:00
|
|
|
|
2012-12-27 09:13:58 -08:00
|
|
|
if (req.headers.expect !== undefined &&
|
2010-11-29 07:41:08 -08:00
|
|
|
(req.httpVersionMajor == 1 && req.httpVersionMinor == 1) &&
|
|
|
|
continueExpression.test(req.headers['expect'])) {
|
|
|
|
res._expect_continue = true;
|
2013-02-14 00:48:11 -08:00
|
|
|
if (EventEmitter.listenerCount(self, 'checkContinue') > 0) {
|
2010-12-01 18:07:20 -08:00
|
|
|
self.emit('checkContinue', req, res);
|
2010-11-29 07:41:08 -08:00
|
|
|
} else {
|
|
|
|
res.writeContinue();
|
2010-09-10 11:56:35 +10:00
|
|
|
self.emit('request', req, res);
|
2010-11-29 07:41:08 -08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self.emit('request', req, res);
|
2010-09-10 11:56:35 +10:00
|
|
|
}
|
2010-05-25 19:24:30 -07:00
|
|
|
return false; // Not a HEAD response. (Not even a response!)
|
2010-03-19 19:22:04 -07:00
|
|
|
};
|
2009-07-14 18:31:50 +02:00
|
|
|
}
|
2011-01-02 01:13:56 -08:00
|
|
|
exports._connectionListener = connectionListener;
|
2009-06-26 12:51:27 +02:00
|
|
|
|
2011-10-04 20:51:34 +02:00
|
|
|
// Legacy Interface
|
2009-06-26 18:30:55 +02:00
|
|
|
|
2011-10-04 20:51:34 +02:00
|
|
|
function Client(port, host) {
|
2012-01-28 23:13:42 -05:00
|
|
|
if (!(this instanceof Client)) return new Client(port, host);
|
2012-06-12 21:53:08 +02:00
|
|
|
EventEmitter.call(this);
|
|
|
|
|
2011-10-04 20:51:34 +02:00
|
|
|
host = host || 'localhost';
|
|
|
|
port = port || 80;
|
|
|
|
this.host = host;
|
|
|
|
this.port = port;
|
|
|
|
this.agent = new Agent({ host: host, port: port, maxSockets: 1 });
|
2011-01-20 02:41:16 -08:00
|
|
|
}
|
2011-10-04 20:51:34 +02:00
|
|
|
util.inherits(Client, EventEmitter);
|
|
|
|
Client.prototype.request = function(method, path, headers) {
|
2011-01-20 02:41:16 -08:00
|
|
|
var self = this;
|
2011-10-04 20:51:34 +02:00
|
|
|
var options = {};
|
|
|
|
options.host = self.host;
|
|
|
|
options.port = self.port;
|
|
|
|
if (method[0] === '/') {
|
|
|
|
headers = path;
|
|
|
|
path = method;
|
|
|
|
method = 'GET';
|
|
|
|
}
|
|
|
|
options.method = method;
|
|
|
|
options.path = path;
|
|
|
|
options.headers = headers;
|
|
|
|
options.agent = self.agent;
|
|
|
|
var c = new ClientRequest(options);
|
|
|
|
c.on('error', function(e) {
|
|
|
|
self.emit('error', e);
|
2011-07-26 00:21:52 +02:00
|
|
|
});
|
2012-02-18 15:01:35 -08:00
|
|
|
// The old Client interface emitted 'end' on socket end.
|
2011-10-04 20:51:34 +02:00
|
|
|
// This doesn't map to how we want things to operate in the future
|
|
|
|
// but it will get removed when we remove this legacy interface.
|
|
|
|
c.on('socket', function(s) {
|
|
|
|
s.on('end', function() {
|
2012-10-11 15:53:11 -07:00
|
|
|
if (self._decoder) {
|
|
|
|
var ret = self._decoder.end();
|
|
|
|
if (ret)
|
|
|
|
self.emit('data', ret);
|
|
|
|
}
|
2011-10-04 20:51:34 +02:00
|
|
|
self.emit('end');
|
2011-07-26 00:21:52 +02:00
|
|
|
});
|
2011-10-04 20:51:34 +02:00
|
|
|
});
|
2010-03-19 19:22:04 -07:00
|
|
|
return c;
|
2010-05-26 17:59:55 -07:00
|
|
|
};
|
|
|
|
|
2012-06-21 11:42:33 -07:00
|
|
|
exports.Client = util.deprecate(Client,
|
|
|
|
'http.Client will be removed soon. Do not use it.');
|
2012-01-28 23:13:42 -05:00
|
|
|
|
2012-06-21 11:42:33 -07:00
|
|
|
exports.createClient = util.deprecate(function(port, host) {
|
2011-10-04 20:51:34 +02:00
|
|
|
return new Client(port, host);
|
2012-06-21 11:42:33 -07:00
|
|
|
}, 'http.createClient is deprecated. Use `http.request` instead.');
|