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-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
|
|
|
|
|
|
|
|
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;
|
2013-04-11 15:37:14 -07:00
|
|
|
var debug = common.debug;
|
|
|
|
var CRLF = common.CRLF;
|
|
|
|
var continueExpression = common.continueExpression;
|
|
|
|
var chunkExpression = common.chunkExpression;
|
2010-03-15 13:48:03 -07:00
|
|
|
|
|
|
|
|
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
|
|
|
|
2013-04-11 15:37:14 -07:00
|
|
|
var outgoing = require('_http_outgoing');
|
|
|
|
var OutgoingMessage = exports.OutgoingMessage = outgoing.OutgoingMessage;
|
2011-01-20 02:41:16 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
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.');
|