2017-01-03 22:16:48 +01: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.
|
|
|
|
|
2014-11-22 16:59:48 +01:00
|
|
|
'use strict';
|
|
|
|
|
2015-09-17 00:45:29 +02:00
|
|
|
const EventEmitter = require('events');
|
2015-01-21 17:36:59 +01:00
|
|
|
const stream = require('stream');
|
|
|
|
const util = require('util');
|
2015-06-13 18:44:39 +02:00
|
|
|
const internalUtil = require('internal/util');
|
2017-12-31 00:27:56 +01:00
|
|
|
const {
|
2018-01-26 18:39:10 +01:00
|
|
|
isIP,
|
|
|
|
isIPv4,
|
|
|
|
isIPv6,
|
2017-12-31 00:27:56 +01:00
|
|
|
isLegalPort,
|
|
|
|
normalizedArgsSymbol,
|
|
|
|
makeSyncWrite
|
|
|
|
} = require('internal/net');
|
2015-01-21 17:36:59 +01:00
|
|
|
const assert = require('assert');
|
2017-08-19 00:37:35 +02:00
|
|
|
const {
|
|
|
|
UV_EADDRINUSE,
|
|
|
|
UV_EINVAL,
|
|
|
|
UV_EOF
|
|
|
|
} = process.binding('uv');
|
2014-02-26 15:03:59 +01:00
|
|
|
|
2017-10-07 16:50:42 +02:00
|
|
|
const { Buffer } = require('buffer');
|
2015-04-07 10:37:13 +02:00
|
|
|
const TTYWrap = process.binding('tty_wrap');
|
2018-03-18 08:35:47 +01:00
|
|
|
const { ShutdownWrap } = process.binding('stream_wrap');
|
2018-03-26 13:40:15 +02:00
|
|
|
const {
|
|
|
|
TCP,
|
|
|
|
TCPConnectWrap,
|
|
|
|
constants: TCPConstants
|
|
|
|
} = process.binding('tcp_wrap');
|
|
|
|
const {
|
|
|
|
Pipe,
|
|
|
|
PipeConnectWrap,
|
|
|
|
constants: PipeConstants
|
|
|
|
} = process.binding('pipe_wrap');
|
2018-02-11 22:35:59 +01:00
|
|
|
const {
|
|
|
|
newAsyncId,
|
|
|
|
defaultTriggerAsyncIdScope,
|
|
|
|
symbols: { async_id_symbol }
|
|
|
|
} = require('internal/async_hooks');
|
2018-03-18 08:35:47 +01:00
|
|
|
const {
|
|
|
|
createWriteWrap,
|
|
|
|
writevGeneric,
|
|
|
|
writeGeneric
|
|
|
|
} = require('internal/stream_base_commons');
|
2017-06-16 21:47:48 +02:00
|
|
|
const errors = require('internal/errors');
|
2018-02-27 14:55:32 +01:00
|
|
|
const {
|
2018-03-17 17:43:50 +01:00
|
|
|
ERR_INVALID_ADDRESS_FAMILY,
|
2018-02-27 14:55:32 +01:00
|
|
|
ERR_INVALID_ARG_TYPE,
|
|
|
|
ERR_INVALID_FD_TYPE,
|
|
|
|
ERR_INVALID_IP_ADDRESS,
|
|
|
|
ERR_INVALID_OPT_VALUE,
|
|
|
|
ERR_SERVER_ALREADY_LISTEN,
|
|
|
|
ERR_SERVER_NOT_RUNNING,
|
|
|
|
ERR_SOCKET_BAD_PORT,
|
|
|
|
ERR_SOCKET_CLOSED
|
|
|
|
} = errors.codes;
|
2018-06-20 23:27:09 +02:00
|
|
|
const { validateInt32 } = require('internal/validators');
|
2017-12-11 23:55:17 +01:00
|
|
|
const kLastWriteQueueSize = Symbol('lastWriteQueueSize');
|
|
|
|
|
2018-05-07 05:12:47 +02:00
|
|
|
// Lazy loaded to improve startup performance.
|
|
|
|
let cluster;
|
|
|
|
let dns;
|
2017-04-11 18:15:11 +02:00
|
|
|
|
2018-05-25 10:54:24 +02:00
|
|
|
const { errnoException, exceptionWithHostPort } = errors;
|
2011-08-01 00:58:10 +02:00
|
|
|
|
2017-12-16 19:05:38 +01:00
|
|
|
const {
|
|
|
|
kTimeout,
|
|
|
|
setUnrefTimeout,
|
2018-04-25 18:45:34 +02:00
|
|
|
validateTimerDuration
|
2017-12-16 19:05:38 +01:00
|
|
|
} = require('internal/timers');
|
2017-02-03 22:05:59 +01:00
|
|
|
|
2012-02-19 00:01:35 +01:00
|
|
|
function noop() {}
|
2011-10-07 19:00:48 +02:00
|
|
|
|
2017-11-20 17:18:40 +01:00
|
|
|
function createHandle(fd, is_server) {
|
2018-06-20 23:27:09 +02:00
|
|
|
validateInt32(fd, 'fd', 0);
|
2017-08-11 23:02:15 +02:00
|
|
|
const type = TTYWrap.guessHandleType(fd);
|
2017-11-20 17:18:40 +01:00
|
|
|
if (type === 'PIPE') {
|
|
|
|
return new Pipe(
|
|
|
|
is_server ? PipeConstants.SERVER : PipeConstants.SOCKET
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type === 'TCP') {
|
|
|
|
return new TCP(
|
|
|
|
is_server ? TCPConstants.SERVER : TCPConstants.SOCKET
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-02-27 14:55:32 +01:00
|
|
|
throw new ERR_INVALID_FD_TYPE(type);
|
2013-03-14 15:13:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-10 14:17:42 +01:00
|
|
|
function getNewAsyncId(handle) {
|
|
|
|
return (!handle || typeof handle.getAsyncId !== 'function') ?
|
2018-02-11 22:35:59 +01:00
|
|
|
newAsyncId() : handle.getAsyncId();
|
2017-03-10 14:17:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-21 17:36:59 +01:00
|
|
|
const debug = util.debuglog('net');
|
2011-06-16 21:11:05 +02:00
|
|
|
|
2011-07-19 22:49:42 +02:00
|
|
|
function isPipeName(s) {
|
2015-01-29 02:05:53 +01:00
|
|
|
return typeof s === 'string' && toNumber(s) === false;
|
2011-07-19 22:49:42 +02:00
|
|
|
}
|
|
|
|
|
2017-03-05 18:15:38 +01:00
|
|
|
function createServer(options, connectionListener) {
|
2014-09-24 05:08:35 +02:00
|
|
|
return new Server(options, connectionListener);
|
2017-03-05 18:15:38 +01:00
|
|
|
}
|
2011-06-17 14:27:02 +02:00
|
|
|
|
|
|
|
|
2012-01-09 02:18:39 +01:00
|
|
|
// Target API:
|
|
|
|
//
|
|
|
|
// var s = net.connect({port: 80, host: 'google.com'}, function() {
|
|
|
|
// ...
|
|
|
|
// });
|
|
|
|
//
|
|
|
|
// There are various forms:
|
|
|
|
//
|
|
|
|
// connect(options, [cb])
|
|
|
|
// connect(port, [host], [cb])
|
|
|
|
// connect(path, [cb]);
|
|
|
|
//
|
2017-06-05 14:05:36 +02:00
|
|
|
function connect(...args) {
|
2017-04-24 08:21:25 +02:00
|
|
|
var normalized = normalizeArgs(args);
|
|
|
|
var options = normalized[0];
|
2017-03-03 17:52:12 +01:00
|
|
|
debug('createConnection', normalized);
|
2017-04-24 08:21:25 +02:00
|
|
|
var socket = new Socket(options);
|
2016-08-31 02:00:41 +02:00
|
|
|
|
2017-03-03 17:52:12 +01:00
|
|
|
if (options.timeout) {
|
|
|
|
socket.setTimeout(options.timeout);
|
2016-08-31 02:00:41 +02:00
|
|
|
}
|
|
|
|
|
2017-05-05 16:02:14 +02:00
|
|
|
return Socket.prototype.connect.call(socket, normalized);
|
2017-03-05 18:15:38 +01:00
|
|
|
}
|
2011-07-19 22:49:42 +02:00
|
|
|
|
2012-01-09 02:18:39 +01:00
|
|
|
|
2017-03-03 17:52:12 +01:00
|
|
|
// Returns an array [options, cb], where options is an object,
|
2017-06-16 17:49:20 +02:00
|
|
|
// cb is either a function or null.
|
2017-03-03 17:52:12 +01:00
|
|
|
// Used to normalize arguments of Socket.prototype.connect() and
|
2017-06-16 17:49:20 +02:00
|
|
|
// Server.prototype.listen(). Possible combinations of parameters:
|
2017-03-03 17:52:12 +01:00
|
|
|
// (options[...][, cb])
|
|
|
|
// (path[...][, cb])
|
|
|
|
// ([port][, host][...][, cb])
|
|
|
|
// For Socket.prototype.connect(), the [...] part is ignored
|
|
|
|
// For Server.prototype.listen(), the [...] part is [, backlog]
|
|
|
|
// but will not be handled here (handled in listen())
|
|
|
|
function normalizeArgs(args) {
|
2017-05-18 20:19:21 +02:00
|
|
|
var arr;
|
|
|
|
|
2016-08-15 19:46:39 +02:00
|
|
|
if (args.length === 0) {
|
2017-05-18 20:19:21 +02:00
|
|
|
arr = [{}, null];
|
|
|
|
arr[normalizedArgsSymbol] = true;
|
|
|
|
return arr;
|
2017-03-03 17:52:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const arg0 = args[0];
|
|
|
|
var options = {};
|
|
|
|
if (typeof arg0 === 'object' && arg0 !== null) {
|
|
|
|
// (options[...][, cb])
|
|
|
|
options = arg0;
|
|
|
|
} else if (isPipeName(arg0)) {
|
|
|
|
// (path[...][, cb])
|
|
|
|
options.path = arg0;
|
2011-07-19 22:49:42 +02:00
|
|
|
} else {
|
2017-03-03 17:52:12 +01:00
|
|
|
// ([port][, host][...][, cb])
|
|
|
|
options.port = arg0;
|
2016-08-15 19:46:39 +02:00
|
|
|
if (args.length > 1 && typeof args[1] === 'string') {
|
2012-01-09 02:18:39 +01:00
|
|
|
options.host = args[1];
|
|
|
|
}
|
2011-07-19 22:49:42 +02:00
|
|
|
}
|
|
|
|
|
2012-01-09 02:18:39 +01:00
|
|
|
var cb = args[args.length - 1];
|
2016-08-12 18:22:22 +02:00
|
|
|
if (typeof cb !== 'function')
|
2017-05-18 20:19:21 +02:00
|
|
|
arr = [options, null];
|
2017-03-03 17:52:12 +01:00
|
|
|
else
|
2017-05-18 20:19:21 +02:00
|
|
|
arr = [options, cb];
|
|
|
|
|
|
|
|
arr[normalizedArgsSymbol] = true;
|
|
|
|
return arr;
|
2012-01-09 02:18:39 +01:00
|
|
|
}
|
|
|
|
|
2011-06-17 14:27:02 +02:00
|
|
|
|
2012-12-13 06:18:57 +01:00
|
|
|
// called when creating new Socket, or when re-using a closed Socket
|
2011-07-01 01:37:30 +02:00
|
|
|
function initSocketHandle(self) {
|
2017-05-06 14:20:52 +02:00
|
|
|
self._undestroy();
|
2015-07-03 00:26:21 +02:00
|
|
|
self._sockname = null;
|
2011-07-21 23:00:47 +02:00
|
|
|
|
|
|
|
// Handle creation may be deferred to bind() or connect() time.
|
|
|
|
if (self._handle) {
|
2012-04-27 04:42:10 +02:00
|
|
|
self._handle.owner = self;
|
2011-07-21 23:00:47 +02:00
|
|
|
self._handle.onread = onread;
|
2017-03-10 14:17:42 +01:00
|
|
|
self[async_id_symbol] = getNewAsyncId(self._handle);
|
2011-07-21 23:00:47 +02:00
|
|
|
}
|
2011-07-01 01:37:30 +02:00
|
|
|
}
|
2011-06-17 14:27:02 +02:00
|
|
|
|
2016-04-19 20:46:53 +02:00
|
|
|
|
2018-03-17 17:52:57 +01:00
|
|
|
const kBytesRead = Symbol('kBytesRead');
|
|
|
|
const kBytesWritten = Symbol('kBytesWritten');
|
2016-04-19 20:46:53 +02:00
|
|
|
|
|
|
|
|
2011-06-16 21:11:05 +02:00
|
|
|
function Socket(options) {
|
|
|
|
if (!(this instanceof Socket)) return new Socket(options);
|
|
|
|
|
2016-04-26 22:27:08 +02:00
|
|
|
this.connecting = false;
|
2017-03-10 14:17:42 +01:00
|
|
|
// Problem with this is that users can supply their own handle, that may not
|
|
|
|
// have _handle.getAsyncId(). In this case an[async_id_symbol] should
|
|
|
|
// probably be supplied by async_hooks.
|
|
|
|
this[async_id_symbol] = -1;
|
2013-06-13 21:47:31 +02:00
|
|
|
this._hadError = false;
|
2013-01-28 17:54:27 +01:00
|
|
|
this._handle = null;
|
2015-02-19 14:14:36 +01:00
|
|
|
this._parent = null;
|
2013-10-28 11:25:27 +01:00
|
|
|
this._host = null;
|
2017-12-11 23:55:17 +01:00
|
|
|
this[kLastWriteQueueSize] = 0;
|
2017-02-03 22:05:59 +01:00
|
|
|
this[kTimeout] = null;
|
2013-01-28 17:54:27 +01:00
|
|
|
|
2015-01-29 02:05:53 +01:00
|
|
|
if (typeof options === 'number')
|
2013-07-24 18:03:53 +02:00
|
|
|
options = { fd: options }; // Legacy interface.
|
2018-01-29 19:32:34 +01:00
|
|
|
else
|
|
|
|
options = util._extend({}, options);
|
|
|
|
|
2018-04-12 11:15:31 +02:00
|
|
|
options.readable = options.readable || false;
|
|
|
|
options.writable = options.writable || false;
|
2018-05-25 10:54:24 +02:00
|
|
|
const { allowHalfOpen } = options;
|
2018-04-03 18:51:30 +02:00
|
|
|
|
|
|
|
// Prevent the "no-half-open enforcer" from being inherited from `Duplex`.
|
|
|
|
options.allowHalfOpen = true;
|
2018-01-29 19:32:34 +01:00
|
|
|
// For backwards compat do not emit close on destroy.
|
|
|
|
options.emitClose = false;
|
2018-04-03 18:51:30 +02:00
|
|
|
stream.Duplex.call(this, options);
|
2012-07-17 15:16:23 +02:00
|
|
|
|
2018-04-03 18:51:30 +02:00
|
|
|
// Default to *not* allowing half open sockets.
|
|
|
|
this.allowHalfOpen = Boolean(allowHalfOpen);
|
2012-12-27 15:57:19 +01:00
|
|
|
|
2012-12-13 06:18:57 +01:00
|
|
|
if (options.handle) {
|
|
|
|
this._handle = options.handle; // private
|
2017-03-10 14:17:42 +01:00
|
|
|
this[async_id_symbol] = getNewAsyncId(this._handle);
|
2015-01-29 02:05:53 +01:00
|
|
|
} else if (options.fd !== undefined) {
|
2018-05-25 10:54:24 +02:00
|
|
|
const { fd } = options;
|
2018-06-20 23:02:03 +02:00
|
|
|
let err;
|
|
|
|
|
2017-12-31 00:27:56 +01:00
|
|
|
this._handle = createHandle(fd, false);
|
2018-06-20 23:02:03 +02:00
|
|
|
|
|
|
|
err = this._handle.open(fd);
|
|
|
|
if (err)
|
|
|
|
throw errnoException(err, 'open');
|
|
|
|
|
2017-03-10 14:17:42 +01:00
|
|
|
this[async_id_symbol] = this._handle.getAsyncId();
|
2017-04-16 20:29:35 +02:00
|
|
|
// options.fd can be string (since it is user-defined),
|
2017-02-23 00:46:32 +01:00
|
|
|
// so changing this to === would be semver-major
|
|
|
|
// See: https://github.com/nodejs/node/pull/11513
|
2017-04-16 20:29:35 +02:00
|
|
|
// eslint-disable-next-line eqeqeq
|
2017-12-31 00:27:56 +01:00
|
|
|
if ((fd == 1 || fd == 2) &&
|
2014-02-26 15:03:59 +01:00
|
|
|
(this._handle instanceof Pipe) &&
|
|
|
|
process.platform === 'win32') {
|
|
|
|
// Make stdout and stderr blocking on Windows
|
2018-06-20 23:02:03 +02:00
|
|
|
err = this._handle.setBlocking(true);
|
2014-02-26 15:03:59 +01:00
|
|
|
if (err)
|
|
|
|
throw errnoException(err, 'setBlocking');
|
2017-12-31 00:27:56 +01:00
|
|
|
|
|
|
|
this._writev = null;
|
|
|
|
this._write = makeSyncWrite(fd);
|
2018-03-17 17:52:57 +01:00
|
|
|
// makeSyncWrite adjusts this value like the original handle would, so
|
|
|
|
// we need to let it do that by turning it into a writable, own property.
|
|
|
|
Object.defineProperty(this._handle, 'bytesWritten', {
|
|
|
|
value: 0, writable: true
|
|
|
|
});
|
2014-02-26 15:03:59 +01:00
|
|
|
}
|
2011-09-12 23:59:51 +02:00
|
|
|
}
|
2012-07-17 15:16:23 +02:00
|
|
|
|
2012-12-13 06:18:57 +01:00
|
|
|
// shut down the socket when we're finished with it.
|
2018-02-06 23:00:15 +01:00
|
|
|
this.on('end', onReadableStreamEnd);
|
2012-12-13 06:18:57 +01:00
|
|
|
|
2012-07-17 15:16:23 +02:00
|
|
|
initSocketHandle(this);
|
2012-12-13 06:18:57 +01:00
|
|
|
|
2013-03-04 04:14:06 +01:00
|
|
|
this._pendingData = null;
|
|
|
|
this._pendingEncoding = '';
|
2012-12-13 06:18:57 +01:00
|
|
|
|
|
|
|
// handle strings directly
|
|
|
|
this._writableState.decodeStrings = false;
|
|
|
|
|
|
|
|
// if we have a handle, then start the flow of data into the
|
|
|
|
// buffer. if not, then this will happen when we connect
|
2014-10-18 03:45:40 +02:00
|
|
|
if (this._handle && options.readable !== false) {
|
|
|
|
if (options.pauseOnCreate) {
|
|
|
|
// stop the handle from reading and pause the stream
|
|
|
|
this._handle.reading = false;
|
|
|
|
this._handle.readStop();
|
2017-05-16 17:08:49 +02:00
|
|
|
this.readableFlowing = false;
|
2017-10-12 07:57:45 +02:00
|
|
|
} else if (!options.manualStart) {
|
2014-10-18 03:45:40 +02:00
|
|
|
this.read(0);
|
|
|
|
}
|
|
|
|
}
|
2016-02-16 21:09:31 +01:00
|
|
|
|
|
|
|
// Reserve properties
|
|
|
|
this.server = null;
|
|
|
|
this._server = null;
|
2016-04-19 20:46:53 +02:00
|
|
|
|
|
|
|
// Used after `.destroy()`
|
2018-03-17 17:52:57 +01:00
|
|
|
this[kBytesRead] = 0;
|
|
|
|
this[kBytesWritten] = 0;
|
2012-12-13 06:18:57 +01:00
|
|
|
}
|
|
|
|
util.inherits(Socket, stream.Duplex);
|
|
|
|
|
2017-02-03 22:05:59 +01:00
|
|
|
// Refresh existing timeouts.
|
2016-10-16 00:02:43 +02:00
|
|
|
Socket.prototype._unrefTimer = function _unrefTimer() {
|
2017-02-03 22:05:59 +01:00
|
|
|
for (var s = this; s !== null; s = s._parent) {
|
|
|
|
if (s[kTimeout])
|
2018-04-25 18:45:34 +02:00
|
|
|
s[kTimeout].refresh();
|
2017-02-03 22:05:59 +01:00
|
|
|
}
|
2015-02-19 14:14:36 +01:00
|
|
|
};
|
|
|
|
|
2017-11-22 18:41:00 +01:00
|
|
|
|
|
|
|
function shutdownSocket(self, callback) {
|
|
|
|
var req = new ShutdownWrap();
|
2018-02-07 01:36:20 +01:00
|
|
|
req.oncomplete = afterShutdown;
|
2017-11-22 18:41:00 +01:00
|
|
|
req.handle = self._handle;
|
2018-02-07 01:36:20 +01:00
|
|
|
req.callback = callback;
|
2017-11-22 18:41:00 +01:00
|
|
|
return self._handle.shutdown(req);
|
|
|
|
}
|
|
|
|
|
2012-12-13 06:18:57 +01:00
|
|
|
// the user has called .end(), and all the bytes have been
|
|
|
|
// sent out to the other side.
|
2018-02-07 01:36:20 +01:00
|
|
|
Socket.prototype._final = function(cb) {
|
|
|
|
// If still connecting - defer handling `_final` until 'connect' will happen
|
2016-04-26 22:27:08 +02:00
|
|
|
if (this.connecting) {
|
2018-02-07 01:36:20 +01:00
|
|
|
debug('_final: not yet connected');
|
|
|
|
return this.once('connect', () => this._final(cb));
|
2013-03-13 13:53:27 +01:00
|
|
|
}
|
|
|
|
|
2013-02-12 18:08:50 +01:00
|
|
|
if (!this.readable || this._readableState.ended) {
|
2018-02-07 01:36:20 +01:00
|
|
|
debug('_final: ended, destroy', this._readableState);
|
|
|
|
cb();
|
2012-12-13 06:18:57 +01:00
|
|
|
return this.destroy();
|
|
|
|
}
|
|
|
|
|
2018-02-07 01:36:20 +01:00
|
|
|
debug('_final: not ended, call shutdown()');
|
2012-12-13 06:18:57 +01:00
|
|
|
|
|
|
|
// otherwise, just shutdown, or destroy() if not possible
|
2018-02-07 01:36:20 +01:00
|
|
|
if (!this._handle || !this._handle.shutdown) {
|
|
|
|
cb();
|
2012-12-13 06:18:57 +01:00
|
|
|
return this.destroy();
|
2018-02-07 01:36:20 +01:00
|
|
|
}
|
2012-12-13 06:18:57 +01:00
|
|
|
|
2017-11-22 18:41:00 +01:00
|
|
|
var err = defaultTriggerAsyncIdScope(
|
2018-02-07 01:36:20 +01:00
|
|
|
this[async_id_symbol], shutdownSocket, this, cb
|
2017-11-22 18:41:00 +01:00
|
|
|
);
|
2012-12-13 06:18:57 +01:00
|
|
|
|
2013-07-18 23:18:50 +02:00
|
|
|
if (err)
|
2017-05-06 14:20:52 +02:00
|
|
|
return this.destroy(errnoException(err, 'shutdown'));
|
2018-02-07 01:36:20 +01:00
|
|
|
};
|
2012-12-13 06:18:57 +01:00
|
|
|
|
|
|
|
|
2018-02-08 04:59:10 +01:00
|
|
|
function afterShutdown(status, handle) {
|
2012-12-13 06:18:57 +01:00
|
|
|
var self = handle.owner;
|
|
|
|
|
|
|
|
debug('afterShutdown destroyed=%j', self.destroyed,
|
|
|
|
self._readableState);
|
|
|
|
|
2018-02-07 01:36:20 +01:00
|
|
|
this.callback();
|
|
|
|
|
2012-12-13 06:18:57 +01:00
|
|
|
// callback may come after call to destroy.
|
|
|
|
if (self.destroyed)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (self._readableState.ended) {
|
|
|
|
debug('readableState ended, destroying');
|
|
|
|
self.destroy();
|
|
|
|
}
|
2011-06-16 21:11:05 +02:00
|
|
|
}
|
|
|
|
|
2013-03-02 19:20:33 +01:00
|
|
|
// Provide a better error message when we call end() as a result
|
|
|
|
// of the other side sending a FIN. The standard 'write after end'
|
|
|
|
// is overly vague, and makes it seem like the user's code is to blame.
|
|
|
|
function writeAfterFIN(chunk, encoding, cb) {
|
2015-01-29 02:05:53 +01:00
|
|
|
if (typeof encoding === 'function') {
|
2013-03-02 19:20:33 +01:00
|
|
|
cb = encoding;
|
|
|
|
encoding = null;
|
|
|
|
}
|
|
|
|
|
2018-03-15 14:22:43 +01:00
|
|
|
// eslint-disable-next-line no-restricted-syntax
|
2013-03-02 20:50:33 +01:00
|
|
|
var er = new Error('This socket has been ended by the other party');
|
2013-03-02 19:20:33 +01:00
|
|
|
er.code = 'EPIPE';
|
|
|
|
// TODO: defer error events consistently everywhere, not just the cb
|
2016-02-14 16:27:17 +01:00
|
|
|
this.emit('error', er);
|
2015-01-29 02:05:53 +01:00
|
|
|
if (typeof cb === 'function') {
|
2018-03-05 12:20:48 +01:00
|
|
|
defaultTriggerAsyncIdScope(this[async_id_symbol], process.nextTick, cb, er);
|
2013-03-02 19:20:33 +01:00
|
|
|
}
|
2012-12-13 06:18:57 +01:00
|
|
|
}
|
2011-06-16 21:11:05 +02:00
|
|
|
|
|
|
|
Socket.prototype.setTimeout = function(msecs, callback) {
|
2018-06-08 06:42:27 +02:00
|
|
|
this.timeout = msecs;
|
2017-02-03 22:05:59 +01:00
|
|
|
// Type checking identical to timers.enroll()
|
2017-12-16 19:05:38 +01:00
|
|
|
msecs = validateTimerDuration(msecs);
|
2017-02-03 22:05:59 +01:00
|
|
|
|
2018-05-07 15:37:57 +02:00
|
|
|
// Attempt to clear an existing timer in both cases -
|
2017-02-03 22:05:59 +01:00
|
|
|
// even if it will be rescheduled we don't want to leak an existing timer.
|
|
|
|
clearTimeout(this[kTimeout]);
|
|
|
|
|
2014-12-16 23:17:28 +01:00
|
|
|
if (msecs === 0) {
|
|
|
|
if (callback) {
|
|
|
|
this.removeListener('timeout', callback);
|
|
|
|
}
|
|
|
|
} else {
|
2017-02-03 22:05:59 +01:00
|
|
|
this[kTimeout] = setUnrefTimeout(this._onTimeout.bind(this), msecs);
|
|
|
|
|
2011-06-16 21:11:05 +02:00
|
|
|
if (callback) {
|
|
|
|
this.once('timeout', callback);
|
|
|
|
}
|
|
|
|
}
|
2015-05-14 04:25:57 +02:00
|
|
|
return this;
|
2011-06-16 21:11:05 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2011-07-01 21:01:12 +02:00
|
|
|
Socket.prototype._onTimeout = function() {
|
2017-12-11 23:55:17 +01:00
|
|
|
const handle = this._handle;
|
|
|
|
const lastWriteQueueSize = this[kLastWriteQueueSize];
|
|
|
|
if (lastWriteQueueSize > 0 && handle) {
|
|
|
|
// `lastWriteQueueSize !== writeQueueSize` means there is
|
2017-10-25 15:26:20 +02:00
|
|
|
// an active write in progress, so we suppress the timeout.
|
2018-05-25 10:54:24 +02:00
|
|
|
const { writeQueueSize } = handle;
|
2017-12-11 23:55:17 +01:00
|
|
|
if (lastWriteQueueSize !== writeQueueSize) {
|
|
|
|
this[kLastWriteQueueSize] = writeQueueSize;
|
2017-10-25 15:26:20 +02:00
|
|
|
this._unrefTimer();
|
|
|
|
return;
|
|
|
|
}
|
2017-10-12 15:57:42 +02:00
|
|
|
}
|
2012-02-19 00:01:35 +01:00
|
|
|
debug('_onTimeout');
|
2011-07-01 21:01:12 +02:00
|
|
|
this.emit('timeout');
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2012-04-12 19:13:04 +02:00
|
|
|
Socket.prototype.setNoDelay = function(enable) {
|
2015-02-18 19:02:01 +01:00
|
|
|
if (!this._handle) {
|
|
|
|
this.once('connect',
|
2015-08-21 23:30:08 +02:00
|
|
|
enable ? this.setNoDelay : () => this.setNoDelay(enable));
|
2015-05-23 07:48:13 +02:00
|
|
|
return this;
|
2015-02-18 19:02:01 +01:00
|
|
|
}
|
|
|
|
|
2012-04-12 19:13:04 +02:00
|
|
|
// backwards compatibility: assume true when `enable` is omitted
|
2015-02-18 19:02:01 +01:00
|
|
|
if (this._handle.setNoDelay)
|
2015-01-29 02:05:53 +01:00
|
|
|
this._handle.setNoDelay(enable === undefined ? true : !!enable);
|
2015-05-23 07:48:13 +02:00
|
|
|
|
|
|
|
return this;
|
2011-06-17 14:27:02 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2011-06-30 23:53:22 +02:00
|
|
|
Socket.prototype.setKeepAlive = function(setting, msecs) {
|
2015-02-18 19:02:01 +01:00
|
|
|
if (!this._handle) {
|
2015-08-21 23:30:08 +02:00
|
|
|
this.once('connect', () => this.setKeepAlive(setting, msecs));
|
2015-05-23 07:48:13 +02:00
|
|
|
return this;
|
2015-02-18 19:02:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (this._handle.setKeepAlive)
|
2011-10-22 02:07:11 +02:00
|
|
|
this._handle.setKeepAlive(setting, ~~(msecs / 1000));
|
2015-05-23 07:48:13 +02:00
|
|
|
|
|
|
|
return this;
|
2011-06-30 23:53:22 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2011-07-16 00:06:10 +02:00
|
|
|
Socket.prototype.address = function() {
|
2014-04-13 19:19:14 +02:00
|
|
|
return this._getsockname();
|
2011-07-16 00:06:10 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2016-04-26 22:27:08 +02:00
|
|
|
Object.defineProperty(Socket.prototype, '_connecting', {
|
|
|
|
get: function() {
|
|
|
|
return this.connecting;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2011-06-17 14:27:02 +02:00
|
|
|
Object.defineProperty(Socket.prototype, 'readyState', {
|
|
|
|
get: function() {
|
2016-04-26 22:27:08 +02:00
|
|
|
if (this.connecting) {
|
2011-06-17 14:27:02 +02:00
|
|
|
return 'opening';
|
|
|
|
} else if (this.readable && this.writable) {
|
|
|
|
return 'open';
|
|
|
|
} else if (this.readable && !this.writable) {
|
|
|
|
return 'readOnly';
|
|
|
|
} else if (!this.readable && this.writable) {
|
|
|
|
return 'writeOnly';
|
|
|
|
} else {
|
|
|
|
return 'closed';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
Object.defineProperty(Socket.prototype, 'bufferSize', {
|
|
|
|
get: function() {
|
2011-12-28 07:13:57 +01:00
|
|
|
if (this._handle) {
|
2017-12-11 23:55:17 +01:00
|
|
|
return this[kLastWriteQueueSize] + this.writableLength;
|
2011-12-28 07:13:57 +01:00
|
|
|
}
|
2011-06-17 14:27:02 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2012-12-13 06:18:57 +01:00
|
|
|
// Just call handle.readStart until we have enough in the buffer
|
stream: There is no _read cb, there is only push
This makes it so that `stream.push(chunk)` is the only way to signal the
end of reading, removing the confusing disparity between the
callback-style _read method, and the fact that most real-world streams
do not have a 1:1 corollation between the "please give me data" event,
and the actual arrival of a chunk of data.
It is still possible, of course, to implement a `CallbackReadable` on
top of this. Simply provide a method like this as the callback:
function readCallback(er, chunk) {
if (er)
stream.emit('error', er);
else
stream.push(chunk);
}
However, *only* fs streams actually would behave in this way, so it
makes not a lot of sense to make TCP, TLS, HTTP, and all the rest have
to bend into this uncomfortable paradigm.
2013-03-01 00:32:32 +01:00
|
|
|
Socket.prototype._read = function(n) {
|
2012-12-13 06:18:57 +01:00
|
|
|
debug('_read');
|
stream: There is no _read cb, there is only push
This makes it so that `stream.push(chunk)` is the only way to signal the
end of reading, removing the confusing disparity between the
callback-style _read method, and the fact that most real-world streams
do not have a 1:1 corollation between the "please give me data" event,
and the actual arrival of a chunk of data.
It is still possible, of course, to implement a `CallbackReadable` on
top of this. Simply provide a method like this as the callback:
function readCallback(er, chunk) {
if (er)
stream.emit('error', er);
else
stream.push(chunk);
}
However, *only* fs streams actually would behave in this way, so it
makes not a lot of sense to make TCP, TLS, HTTP, and all the rest have
to bend into this uncomfortable paradigm.
2013-03-01 00:32:32 +01:00
|
|
|
|
2016-04-26 22:27:08 +02:00
|
|
|
if (this.connecting || !this._handle) {
|
2012-12-13 06:18:57 +01:00
|
|
|
debug('_read wait for connection');
|
2015-08-21 23:30:08 +02:00
|
|
|
this.once('connect', () => this._read(n));
|
stream: There is no _read cb, there is only push
This makes it so that `stream.push(chunk)` is the only way to signal the
end of reading, removing the confusing disparity between the
callback-style _read method, and the fact that most real-world streams
do not have a 1:1 corollation between the "please give me data" event,
and the actual arrival of a chunk of data.
It is still possible, of course, to implement a `CallbackReadable` on
top of this. Simply provide a method like this as the callback:
function readCallback(er, chunk) {
if (er)
stream.emit('error', er);
else
stream.push(chunk);
}
However, *only* fs streams actually would behave in this way, so it
makes not a lot of sense to make TCP, TLS, HTTP, and all the rest have
to bend into this uncomfortable paradigm.
2013-03-01 00:32:32 +01:00
|
|
|
} else if (!this._handle.reading) {
|
|
|
|
// not already reading, start the flow
|
2012-12-13 06:18:57 +01:00
|
|
|
debug('Socket._read readStart');
|
|
|
|
this._handle.reading = true;
|
2013-07-18 23:18:50 +02:00
|
|
|
var err = this._handle.readStart();
|
|
|
|
if (err)
|
2017-05-06 14:20:52 +02:00
|
|
|
this.destroy(errnoException(err, 'read'));
|
2011-08-12 01:41:53 +02:00
|
|
|
}
|
2011-06-16 21:11:05 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2018-02-11 03:08:21 +01:00
|
|
|
Socket.prototype.end = function(data, encoding, callback) {
|
|
|
|
stream.Duplex.prototype.end.call(this, data, encoding, callback);
|
2011-06-17 17:10:12 +02:00
|
|
|
DTRACE_NET_STREAM_END(this);
|
2017-06-05 21:35:43 +02:00
|
|
|
return this;
|
2011-06-16 21:11:05 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2018-02-06 23:00:15 +01:00
|
|
|
// Called when the 'end' event is emitted.
|
|
|
|
function onReadableStreamEnd() {
|
2018-03-08 22:47:55 +01:00
|
|
|
if (!this.allowHalfOpen) {
|
|
|
|
this.write = writeAfterFIN;
|
|
|
|
if (this.writable)
|
|
|
|
this.end();
|
|
|
|
}
|
2018-02-06 23:00:15 +01:00
|
|
|
maybeDestroy(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-05 08:43:29 +02:00
|
|
|
// Call whenever we set writable=false or readable=false
|
|
|
|
function maybeDestroy(socket) {
|
|
|
|
if (!socket.readable &&
|
|
|
|
!socket.writable &&
|
|
|
|
!socket.destroyed &&
|
2016-04-26 22:27:08 +02:00
|
|
|
!socket.connecting &&
|
2017-05-05 14:42:21 +02:00
|
|
|
!socket.writableLength) {
|
2013-06-05 08:43:29 +02:00
|
|
|
socket.destroy();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-06-16 21:11:05 +02:00
|
|
|
Socket.prototype.destroySoon = function() {
|
2012-12-13 06:18:57 +01:00
|
|
|
if (this.writable)
|
|
|
|
this.end();
|
2011-06-16 21:11:05 +02:00
|
|
|
|
2013-03-04 01:12:02 +01:00
|
|
|
if (this._writableState.finished)
|
2012-12-13 06:18:57 +01:00
|
|
|
this.destroy();
|
|
|
|
else
|
|
|
|
this.once('finish', this.destroy);
|
2011-07-02 09:18:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2012-03-20 08:21:38 +01:00
|
|
|
Socket.prototype._destroy = function(exception, cb) {
|
2012-12-13 06:18:57 +01:00
|
|
|
debug('destroy');
|
|
|
|
|
2016-04-26 22:27:08 +02:00
|
|
|
this.connecting = false;
|
2011-06-16 21:11:05 +02:00
|
|
|
|
|
|
|
this.readable = this.writable = false;
|
|
|
|
|
2017-02-03 22:05:59 +01:00
|
|
|
for (var s = this; s !== null; s = s._parent) {
|
|
|
|
clearTimeout(s[kTimeout]);
|
|
|
|
}
|
2011-06-16 21:11:05 +02:00
|
|
|
|
2011-08-23 11:31:20 +02:00
|
|
|
debug('close');
|
2011-07-21 23:00:47 +02:00
|
|
|
if (this._handle) {
|
2012-12-13 06:18:57 +01:00
|
|
|
if (this !== process.stderr)
|
|
|
|
debug('close handle');
|
2013-03-06 16:15:17 +01:00
|
|
|
var isException = exception ? true : false;
|
2018-03-17 17:52:57 +01:00
|
|
|
// `bytesRead` and `kBytesWritten` should be accessible after `.destroy()`
|
|
|
|
this[kBytesRead] = this._handle.bytesRead;
|
|
|
|
this[kBytesWritten] = this._handle.bytesWritten;
|
2016-04-19 20:46:53 +02:00
|
|
|
|
2016-03-23 09:14:29 +01:00
|
|
|
this._handle.close(() => {
|
2013-03-06 16:15:17 +01:00
|
|
|
debug('emit close');
|
2016-03-23 09:14:29 +01:00
|
|
|
this.emit('close', isException);
|
2013-03-06 16:15:17 +01:00
|
|
|
});
|
2011-10-07 19:00:48 +02:00
|
|
|
this._handle.onread = noop;
|
2011-07-22 00:47:28 +02:00
|
|
|
this._handle = null;
|
2015-07-03 00:26:21 +02:00
|
|
|
this._sockname = null;
|
2011-07-21 23:00:47 +02:00
|
|
|
}
|
2011-06-16 21:11:05 +02:00
|
|
|
|
2017-05-06 14:20:52 +02:00
|
|
|
cb(exception);
|
2012-05-29 13:05:49 +02:00
|
|
|
|
2016-02-16 21:09:31 +01:00
|
|
|
if (this._server) {
|
2012-11-21 00:27:22 +01:00
|
|
|
COUNTER_NET_SERVER_CONNECTION_CLOSE(this);
|
2012-12-13 06:18:57 +01:00
|
|
|
debug('has server');
|
2016-02-16 21:09:31 +01:00
|
|
|
this._server._connections--;
|
|
|
|
if (this._server._emitCloseIfDrained) {
|
|
|
|
this._server._emitCloseIfDrained();
|
2012-06-12 19:02:52 +02:00
|
|
|
}
|
2012-05-29 13:05:49 +02:00
|
|
|
}
|
2011-06-16 21:11:05 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2012-12-13 06:18:57 +01:00
|
|
|
// This function is called whenever the handle gets a
|
|
|
|
// buffer, or when there's an error reading.
|
2013-07-18 23:18:50 +02:00
|
|
|
function onread(nread, buffer) {
|
2011-06-16 21:11:05 +02:00
|
|
|
var handle = this;
|
2012-04-27 04:42:10 +02:00
|
|
|
var self = handle.owner;
|
2012-08-23 16:03:46 +02:00
|
|
|
assert(handle === self._handle, 'handle != self._handle');
|
2011-06-16 21:11:05 +02:00
|
|
|
|
2015-02-19 14:14:36 +01:00
|
|
|
self._unrefTimer();
|
2011-06-16 21:11:05 +02:00
|
|
|
|
2013-07-18 23:18:50 +02:00
|
|
|
debug('onread', nread);
|
2011-06-16 21:11:05 +02:00
|
|
|
|
2013-07-18 23:18:50 +02:00
|
|
|
if (nread > 0) {
|
2012-12-13 06:18:57 +01:00
|
|
|
debug('got data');
|
2011-06-16 21:11:05 +02:00
|
|
|
|
2012-12-13 06:18:57 +01:00
|
|
|
// read success.
|
|
|
|
// In theory (and in practice) calling readStop right now
|
|
|
|
// will prevent this from being called again until _read() gets
|
|
|
|
// called again.
|
|
|
|
|
2012-12-27 22:03:59 +01:00
|
|
|
// Optimization: emit the original buffer with end points
|
2013-07-26 04:33:15 +02:00
|
|
|
var ret = self.push(buffer);
|
2012-12-13 06:18:57 +01:00
|
|
|
|
2013-01-08 03:07:37 +01:00
|
|
|
if (handle.reading && !ret) {
|
2012-12-13 06:18:57 +01:00
|
|
|
handle.reading = false;
|
|
|
|
debug('readStop');
|
2013-07-18 23:18:50 +02:00
|
|
|
var err = handle.readStop();
|
|
|
|
if (err)
|
2017-05-06 14:20:52 +02:00
|
|
|
self.destroy(errnoException(err, 'read'));
|
2012-12-13 06:18:57 +01:00
|
|
|
}
|
2013-07-18 23:18:50 +02:00
|
|
|
return;
|
|
|
|
}
|
2011-07-21 10:20:01 +02:00
|
|
|
|
2013-07-18 23:18:50 +02:00
|
|
|
// if we didn't get any bytes, that doesn't necessarily mean EOF.
|
|
|
|
// wait for the next one.
|
|
|
|
if (nread === 0) {
|
|
|
|
debug('not any data, keep waiting');
|
|
|
|
return;
|
|
|
|
}
|
2011-06-17 17:10:12 +02:00
|
|
|
|
2013-07-18 23:18:50 +02:00
|
|
|
// Error, possibly EOF.
|
2017-08-19 00:37:35 +02:00
|
|
|
if (nread !== UV_EOF) {
|
2017-05-06 14:20:52 +02:00
|
|
|
return self.destroy(errnoException(nread, 'read'));
|
2013-07-18 23:18:50 +02:00
|
|
|
}
|
2011-06-17 13:36:16 +02:00
|
|
|
|
2013-07-18 23:18:50 +02:00
|
|
|
debug('EOF');
|
2012-12-13 06:18:57 +01:00
|
|
|
|
2016-10-12 22:39:37 +02:00
|
|
|
// push a null to signal the end of data.
|
|
|
|
// Do it before `maybeDestroy` for correct order of events:
|
|
|
|
// `end` -> `close`
|
|
|
|
self.push(null);
|
2018-02-06 23:00:15 +01:00
|
|
|
self.read(0);
|
2011-06-16 21:11:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-10-12 21:46:41 +02:00
|
|
|
Socket.prototype._getpeername = function() {
|
|
|
|
if (!this._peername) {
|
2015-03-10 21:48:19 +01:00
|
|
|
if (!this._handle || !this._handle.getpeername) {
|
|
|
|
return {};
|
|
|
|
}
|
2013-07-18 23:18:50 +02:00
|
|
|
var out = {};
|
|
|
|
var err = this._handle.getpeername(out);
|
|
|
|
if (err) return {}; // FIXME(bnoordhuis) Throw?
|
|
|
|
this._peername = out;
|
2011-10-12 21:46:41 +02:00
|
|
|
}
|
|
|
|
return this._peername;
|
|
|
|
};
|
|
|
|
|
2016-04-20 17:59:16 +02:00
|
|
|
function protoGetter(name, callback) {
|
|
|
|
Object.defineProperty(Socket.prototype, name, {
|
|
|
|
configurable: false,
|
|
|
|
enumerable: true,
|
|
|
|
get: callback
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
protoGetter('bytesRead', function bytesRead() {
|
2018-03-17 17:52:57 +01:00
|
|
|
return this._handle ? this._handle.bytesRead : this[kBytesRead];
|
2016-04-19 20:46:53 +02:00
|
|
|
});
|
2011-10-12 21:46:41 +02:00
|
|
|
|
2016-04-20 17:59:16 +02:00
|
|
|
protoGetter('remoteAddress', function remoteAddress() {
|
2011-10-12 21:46:41 +02:00
|
|
|
return this._getpeername().address;
|
|
|
|
});
|
|
|
|
|
2016-04-20 17:59:16 +02:00
|
|
|
protoGetter('remoteFamily', function remoteFamily() {
|
2014-07-16 16:04:34 +02:00
|
|
|
return this._getpeername().family;
|
|
|
|
});
|
2011-10-12 21:46:41 +02:00
|
|
|
|
2016-04-20 17:59:16 +02:00
|
|
|
protoGetter('remotePort', function remotePort() {
|
2011-10-12 21:46:41 +02:00
|
|
|
return this._getpeername().port;
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2013-01-01 03:01:42 +01:00
|
|
|
Socket.prototype._getsockname = function() {
|
|
|
|
if (!this._handle || !this._handle.getsockname) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
if (!this._sockname) {
|
2013-07-18 23:18:50 +02:00
|
|
|
var out = {};
|
|
|
|
var err = this._handle.getsockname(out);
|
|
|
|
if (err) return {}; // FIXME(bnoordhuis) Throw?
|
|
|
|
this._sockname = out;
|
2013-01-01 03:01:42 +01:00
|
|
|
}
|
|
|
|
return this._sockname;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2016-04-20 17:59:16 +02:00
|
|
|
protoGetter('localAddress', function localAddress() {
|
2013-01-01 03:01:42 +01:00
|
|
|
return this._getsockname().address;
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2016-04-20 17:59:16 +02:00
|
|
|
protoGetter('localPort', function localPort() {
|
2013-01-01 03:01:42 +01:00
|
|
|
return this._getsockname().port;
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2013-04-09 10:56:56 +02:00
|
|
|
Socket.prototype._writeGeneric = function(writev, data, encoding, cb) {
|
2011-07-02 09:18:36 +02:00
|
|
|
// If we are still connecting, then buffer this for later.
|
2012-12-13 06:18:57 +01:00
|
|
|
// The Writable logic will buffer up any more writes while
|
|
|
|
// waiting for this one to be done.
|
2016-04-26 22:27:08 +02:00
|
|
|
if (this.connecting) {
|
2013-03-04 04:14:06 +01:00
|
|
|
this._pendingData = data;
|
|
|
|
this._pendingEncoding = encoding;
|
2016-10-29 17:39:53 +02:00
|
|
|
this.once('connect', function connect() {
|
2013-04-09 10:56:56 +02:00
|
|
|
this._writeGeneric(writev, data, encoding, cb);
|
2012-12-13 06:18:57 +01:00
|
|
|
});
|
|
|
|
return;
|
2011-07-02 09:18:36 +02:00
|
|
|
}
|
2013-03-04 04:14:06 +01:00
|
|
|
this._pendingData = null;
|
|
|
|
this._pendingEncoding = '';
|
2011-07-02 09:18:36 +02:00
|
|
|
|
2012-03-20 08:21:38 +01:00
|
|
|
if (!this._handle) {
|
2018-02-27 14:55:32 +01:00
|
|
|
this.destroy(new ERR_SOCKET_CLOSED(), cb);
|
2012-03-20 08:21:38 +01:00
|
|
|
return false;
|
|
|
|
}
|
2011-12-22 03:32:27 +01:00
|
|
|
|
2018-05-09 23:17:56 +02:00
|
|
|
this._unrefTimer();
|
|
|
|
|
2018-03-18 08:35:47 +01:00
|
|
|
var req = createWriteWrap(this._handle, afterWrite);
|
|
|
|
if (writev)
|
2018-04-01 20:56:11 +02:00
|
|
|
writevGeneric(this, req, data, cb);
|
2018-03-18 08:35:47 +01:00
|
|
|
else
|
2018-04-01 20:56:11 +02:00
|
|
|
writeGeneric(this, req, data, encoding, cb);
|
|
|
|
if (req.async)
|
|
|
|
this[kLastWriteQueueSize] = req.bytes;
|
2011-06-16 21:11:05 +02:00
|
|
|
};
|
|
|
|
|
2013-04-09 10:56:56 +02:00
|
|
|
|
|
|
|
Socket.prototype._writev = function(chunks, cb) {
|
|
|
|
this._writeGeneric(true, chunks, '', cb);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Socket.prototype._write = function(data, encoding, cb) {
|
|
|
|
this._writeGeneric(false, data, encoding, cb);
|
|
|
|
};
|
|
|
|
|
2018-03-17 17:52:57 +01:00
|
|
|
|
|
|
|
// Legacy alias. Having this is probably being overly cautious, but it doesn't
|
|
|
|
// really hurt anyone either. This can probably be removed safely if desired.
|
|
|
|
protoGetter('_bytesDispatched', function _bytesDispatched() {
|
|
|
|
return this._handle ? this._handle.bytesWritten : this[kBytesWritten];
|
|
|
|
});
|
|
|
|
|
2016-04-20 17:59:16 +02:00
|
|
|
protoGetter('bytesWritten', function bytesWritten() {
|
2016-01-12 22:04:50 +01:00
|
|
|
var bytes = this._bytesDispatched;
|
|
|
|
const state = this._writableState;
|
|
|
|
const data = this._pendingData;
|
|
|
|
const encoding = this._pendingEncoding;
|
2012-05-08 20:19:38 +02:00
|
|
|
|
2015-10-10 00:51:42 +02:00
|
|
|
if (!state)
|
|
|
|
return undefined;
|
|
|
|
|
2017-05-16 17:08:49 +02:00
|
|
|
this.writableBuffer.forEach(function(el) {
|
2015-01-29 02:05:53 +01:00
|
|
|
if (el.chunk instanceof Buffer)
|
2013-04-10 12:21:41 +02:00
|
|
|
bytes += el.chunk.length;
|
|
|
|
else
|
|
|
|
bytes += Buffer.byteLength(el.chunk, el.encoding);
|
2012-12-13 06:18:57 +01:00
|
|
|
});
|
|
|
|
|
2017-07-13 20:36:44 +02:00
|
|
|
if (Array.isArray(data)) {
|
|
|
|
// was a writev, iterate over chunks to get total length
|
|
|
|
for (var i = 0; i < data.length; i++) {
|
|
|
|
const chunk = data[i];
|
|
|
|
|
|
|
|
if (data.allBuffers || chunk instanceof Buffer)
|
|
|
|
bytes += chunk.length;
|
|
|
|
else
|
|
|
|
bytes += Buffer.byteLength(chunk.chunk, chunk.encoding);
|
|
|
|
}
|
|
|
|
} else if (data) {
|
|
|
|
// Writes are either a string or a Buffer.
|
|
|
|
if (typeof data !== 'string')
|
2013-04-10 12:21:41 +02:00
|
|
|
bytes += data.length;
|
|
|
|
else
|
|
|
|
bytes += Buffer.byteLength(data, encoding);
|
2013-04-11 20:06:07 +02:00
|
|
|
}
|
2012-05-08 20:19:38 +02:00
|
|
|
|
|
|
|
return bytes;
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2018-02-08 04:59:10 +01:00
|
|
|
function afterWrite(status, handle, err) {
|
2012-04-27 04:42:10 +02:00
|
|
|
var self = handle.owner;
|
2012-12-13 06:18:57 +01:00
|
|
|
if (self !== process.stderr && self !== process.stdout)
|
2013-08-09 22:35:54 +02:00
|
|
|
debug('afterWrite', status);
|
2011-06-16 21:11:05 +02:00
|
|
|
|
2018-02-08 04:59:10 +01:00
|
|
|
if (this.async)
|
2017-12-11 23:55:17 +01:00
|
|
|
self[kLastWriteQueueSize] = 0;
|
|
|
|
|
2011-07-15 22:20:24 +02:00
|
|
|
// callback may come after call to destroy.
|
|
|
|
if (self.destroyed) {
|
2012-12-13 06:18:57 +01:00
|
|
|
debug('afterWrite destroyed');
|
2011-07-15 22:20:24 +02:00
|
|
|
return;
|
|
|
|
}
|
2011-12-12 22:45:39 +01:00
|
|
|
|
2013-07-18 23:18:50 +02:00
|
|
|
if (status < 0) {
|
2018-02-08 04:59:10 +01:00
|
|
|
var ex = errnoException(status, 'write', this.error);
|
2013-07-18 23:18:50 +02:00
|
|
|
debug('write failure', ex);
|
2018-04-01 20:56:11 +02:00
|
|
|
self.destroy(ex, this.callback);
|
2011-12-12 22:45:39 +01:00
|
|
|
return;
|
|
|
|
}
|
2011-06-16 21:11:05 +02:00
|
|
|
|
2015-02-19 14:14:36 +01:00
|
|
|
self._unrefTimer();
|
2011-11-03 20:34:23 +01:00
|
|
|
|
2012-12-13 06:18:57 +01:00
|
|
|
if (self !== process.stderr && self !== process.stdout)
|
|
|
|
debug('afterWrite call cb');
|
2011-06-17 17:10:12 +02:00
|
|
|
|
2018-04-01 20:56:11 +02:00
|
|
|
if (this.callback)
|
|
|
|
this.callback.call(undefined);
|
2011-06-16 21:11:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-08-30 19:13:04 +02:00
|
|
|
function checkBindError(err, port, handle) {
|
|
|
|
// EADDRINUSE may not be reported until we call listen() or connect().
|
|
|
|
// To complicate matters, a failed bind() followed by listen() or connect()
|
|
|
|
// will implicitly bind to a random port. Ergo, check that the socket is
|
|
|
|
// bound to the expected port before calling listen() or connect().
|
|
|
|
//
|
|
|
|
// FIXME(bnoordhuis) Doesn't work for pipe handles, they don't have a
|
|
|
|
// getsockname() method. Non-issue for now, the cluster module doesn't
|
|
|
|
// really support pipes anyway.
|
|
|
|
if (err === 0 && port > 0 && handle.getsockname) {
|
|
|
|
var out = {};
|
|
|
|
err = handle.getsockname(out);
|
|
|
|
if (err === 0 && port !== out.port) {
|
|
|
|
debug(`checkBindError, bound to ${out.port} instead of ${port}`);
|
|
|
|
err = UV_EADDRINUSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-05 18:15:38 +01:00
|
|
|
function internalConnect(
|
|
|
|
self, address, port, addressType, localAddress, localPort) {
|
2011-06-24 21:32:09 +02:00
|
|
|
// TODO return promise from Socket.prototype.connect which
|
|
|
|
// wraps _connectReq.
|
|
|
|
|
2017-10-06 18:52:56 +02:00
|
|
|
assert(self.connecting);
|
2011-06-24 21:32:09 +02:00
|
|
|
|
2013-07-18 23:18:50 +02:00
|
|
|
var err;
|
2014-02-18 02:30:12 +01:00
|
|
|
|
2015-01-04 00:26:26 +01:00
|
|
|
if (localAddress || localPort) {
|
|
|
|
if (addressType === 4) {
|
|
|
|
localAddress = localAddress || '0.0.0.0';
|
2017-04-11 16:40:16 +02:00
|
|
|
err = self._handle.bind(localAddress, localPort);
|
2015-01-04 00:26:26 +01:00
|
|
|
} else if (addressType === 6) {
|
|
|
|
localAddress = localAddress || '::';
|
2017-04-11 16:40:16 +02:00
|
|
|
err = self._handle.bind6(localAddress, localPort);
|
2015-01-04 00:26:26 +01:00
|
|
|
} else {
|
2018-03-15 14:22:36 +01:00
|
|
|
self.destroy(new ERR_INVALID_ADDRESS_FAMILY(addressType));
|
2014-02-18 02:30:12 +01:00
|
|
|
return;
|
2012-05-03 22:27:06 +02:00
|
|
|
}
|
2017-04-24 08:27:14 +02:00
|
|
|
debug('binding to localAddress: %s and localPort: %d (addressType: %d)',
|
|
|
|
localAddress, localPort, addressType);
|
2012-05-03 22:27:06 +02:00
|
|
|
|
2017-08-30 19:13:04 +02:00
|
|
|
err = checkBindError(err, localPort, self._handle);
|
2013-07-18 23:18:50 +02:00
|
|
|
if (err) {
|
2016-01-30 04:57:34 +01:00
|
|
|
const ex = exceptionWithHostPort(err, 'bind', localAddress, localPort);
|
2017-05-06 14:20:52 +02:00
|
|
|
self.destroy(ex);
|
2012-05-03 22:27:06 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-05 00:57:43 +02:00
|
|
|
if (addressType === 6 || addressType === 4) {
|
2016-01-30 04:57:34 +01:00
|
|
|
const req = new TCPConnectWrap();
|
2014-12-09 05:29:47 +01:00
|
|
|
req.oncomplete = afterConnect;
|
2014-12-03 04:16:32 +01:00
|
|
|
req.address = address;
|
2015-01-05 16:43:58 +01:00
|
|
|
req.port = port;
|
2015-11-20 22:16:55 +01:00
|
|
|
req.localAddress = localAddress;
|
|
|
|
req.localPort = localPort;
|
2013-09-05 00:57:43 +02:00
|
|
|
|
2015-01-04 00:26:26 +01:00
|
|
|
if (addressType === 4)
|
2013-09-05 00:57:43 +02:00
|
|
|
err = self._handle.connect(req, address, port);
|
2015-01-04 00:26:26 +01:00
|
|
|
else
|
|
|
|
err = self._handle.connect6(req, address, port);
|
2011-07-06 22:34:04 +02:00
|
|
|
} else {
|
2016-01-30 04:57:34 +01:00
|
|
|
const req = new PipeConnectWrap();
|
2014-12-03 04:16:32 +01:00
|
|
|
req.address = address;
|
2014-12-09 05:29:47 +01:00
|
|
|
req.oncomplete = afterConnect;
|
2017-11-22 18:41:00 +01:00
|
|
|
|
2013-07-18 23:18:50 +02:00
|
|
|
err = self._handle.connect(req, address, afterConnect);
|
2011-07-06 22:34:04 +02:00
|
|
|
}
|
2011-06-24 21:32:09 +02:00
|
|
|
|
2013-07-18 23:18:50 +02:00
|
|
|
if (err) {
|
2015-01-19 19:02:18 +01:00
|
|
|
var sockname = self._getsockname();
|
2014-12-03 04:16:32 +01:00
|
|
|
var details;
|
2015-01-19 19:02:18 +01:00
|
|
|
|
|
|
|
if (sockname) {
|
|
|
|
details = sockname.address + ':' + sockname.port;
|
2014-12-03 04:16:32 +01:00
|
|
|
}
|
2015-01-19 19:02:18 +01:00
|
|
|
|
2016-01-30 04:57:34 +01:00
|
|
|
const ex = exceptionWithHostPort(err, 'connect', address, port, details);
|
2017-05-06 14:20:52 +02:00
|
|
|
self.destroy(ex);
|
2011-06-24 21:32:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-06-05 14:05:36 +02:00
|
|
|
Socket.prototype.connect = function(...args) {
|
2017-05-05 16:02:14 +02:00
|
|
|
let normalized;
|
|
|
|
// If passed an array, it's treated as an array of arguments that have
|
|
|
|
// already been normalized (so we don't normalize more than once). This has
|
|
|
|
// been solved before in https://github.com/nodejs/node/pull/12342, but was
|
|
|
|
// reverted as it had unintended side effects.
|
2017-06-05 14:05:36 +02:00
|
|
|
if (Array.isArray(args[0]) && args[0][normalizedArgsSymbol]) {
|
|
|
|
normalized = args[0];
|
2017-05-05 16:02:14 +02:00
|
|
|
} else {
|
|
|
|
normalized = normalizeArgs(args);
|
|
|
|
}
|
2017-06-02 08:10:11 +02:00
|
|
|
var options = normalized[0];
|
|
|
|
var cb = normalized[1];
|
2017-03-09 04:36:15 +01:00
|
|
|
|
2013-03-02 19:20:33 +01:00
|
|
|
if (this.write !== Socket.prototype.write)
|
|
|
|
this.write = Socket.prototype.write;
|
|
|
|
|
2012-12-13 06:18:57 +01:00
|
|
|
if (this.destroyed) {
|
2017-05-06 14:20:52 +02:00
|
|
|
this._undestroy();
|
2012-12-13 06:18:57 +01:00
|
|
|
this._handle = null;
|
2015-03-10 21:48:19 +01:00
|
|
|
this._peername = null;
|
2015-07-03 00:26:21 +02:00
|
|
|
this._sockname = null;
|
2012-12-13 06:18:57 +01:00
|
|
|
}
|
|
|
|
|
2018-05-25 10:54:24 +02:00
|
|
|
const { path } = options;
|
2017-06-16 21:47:48 +02:00
|
|
|
var pipe = !!path;
|
|
|
|
debug('pipe', pipe, path);
|
2011-07-19 22:49:42 +02:00
|
|
|
|
2012-12-13 06:18:57 +01:00
|
|
|
if (!this._handle) {
|
2017-11-20 17:18:40 +01:00
|
|
|
this._handle = pipe ?
|
|
|
|
new Pipe(PipeConstants.SOCKET) :
|
|
|
|
new TCP(TCPConstants.SOCKET);
|
2011-07-01 01:37:30 +02:00
|
|
|
initSocketHandle(this);
|
|
|
|
}
|
2011-07-19 22:49:42 +02:00
|
|
|
|
2017-03-03 17:52:12 +01:00
|
|
|
if (cb !== null) {
|
2016-02-15 05:53:17 +01:00
|
|
|
this.once('connect', cb);
|
2011-06-21 16:53:02 +02:00
|
|
|
}
|
|
|
|
|
2015-02-19 14:14:36 +01:00
|
|
|
this._unrefTimer();
|
2011-06-16 21:11:05 +02:00
|
|
|
|
2016-04-26 22:27:08 +02:00
|
|
|
this.connecting = true;
|
2016-02-15 05:53:17 +01:00
|
|
|
this.writable = true;
|
2011-07-02 09:18:36 +02:00
|
|
|
|
2011-07-19 22:49:42 +02:00
|
|
|
if (pipe) {
|
2017-06-16 21:47:48 +02:00
|
|
|
if (typeof path !== 'string') {
|
2018-02-27 14:55:32 +01:00
|
|
|
throw new ERR_INVALID_ARG_TYPE('options.path', 'string', path);
|
2017-06-16 21:47:48 +02:00
|
|
|
}
|
2017-11-22 18:41:00 +01:00
|
|
|
defaultTriggerAsyncIdScope(
|
2018-01-05 15:03:10 +01:00
|
|
|
this[async_id_symbol], internalConnect, this, path
|
2017-11-22 18:41:00 +01:00
|
|
|
);
|
2012-01-09 02:18:39 +01:00
|
|
|
} else {
|
2016-02-15 05:53:17 +01:00
|
|
|
lookupAndConnect(this, options);
|
2011-06-24 21:32:09 +02:00
|
|
|
}
|
2016-02-15 05:53:17 +01:00
|
|
|
return this;
|
2017-05-05 16:02:14 +02:00
|
|
|
};
|
2011-06-16 21:11:05 +02:00
|
|
|
|
|
|
|
|
2015-04-22 23:46:21 +02:00
|
|
|
function lookupAndConnect(self, options) {
|
2018-05-25 10:54:24 +02:00
|
|
|
var { port, localAddress, localPort } = options;
|
2015-04-22 23:46:21 +02:00
|
|
|
var host = options.host || 'localhost';
|
|
|
|
|
2018-01-26 18:39:10 +01:00
|
|
|
if (localAddress && !isIP(localAddress)) {
|
2018-02-27 14:55:32 +01:00
|
|
|
throw new ERR_INVALID_IP_ADDRESS(localAddress);
|
2017-06-20 23:37:00 +02:00
|
|
|
}
|
2015-04-22 23:46:21 +02:00
|
|
|
|
2017-06-20 23:37:00 +02:00
|
|
|
if (localPort && typeof localPort !== 'number') {
|
2018-02-27 14:55:32 +01:00
|
|
|
throw new ERR_INVALID_ARG_TYPE('options.localPort', 'number', localPort);
|
2017-06-20 23:37:00 +02:00
|
|
|
}
|
2015-04-22 23:46:21 +02:00
|
|
|
|
|
|
|
if (typeof port !== 'undefined') {
|
2017-06-20 23:37:00 +02:00
|
|
|
if (typeof port !== 'number' && typeof port !== 'string') {
|
2018-02-27 14:55:32 +01:00
|
|
|
throw new ERR_INVALID_ARG_TYPE('options.port',
|
|
|
|
['number', 'string'], port);
|
2017-06-20 23:37:00 +02:00
|
|
|
}
|
|
|
|
if (!isLegalPort(port)) {
|
2018-02-27 14:55:32 +01:00
|
|
|
throw new ERR_SOCKET_BAD_PORT(port);
|
2017-06-20 23:37:00 +02:00
|
|
|
}
|
2015-04-22 23:46:21 +02:00
|
|
|
}
|
|
|
|
port |= 0;
|
|
|
|
|
|
|
|
// If host is an IP, skip performing a lookup
|
2018-01-26 18:39:10 +01:00
|
|
|
var addressType = isIP(host);
|
2015-04-22 23:46:21 +02:00
|
|
|
if (addressType) {
|
2018-03-05 12:20:48 +01:00
|
|
|
defaultTriggerAsyncIdScope(self[async_id_symbol], process.nextTick, () => {
|
2017-05-05 13:10:43 +02:00
|
|
|
if (self.connecting)
|
2017-11-22 18:41:00 +01:00
|
|
|
defaultTriggerAsyncIdScope(
|
|
|
|
self[async_id_symbol],
|
2018-01-05 15:03:10 +01:00
|
|
|
internalConnect,
|
|
|
|
self, host, port, addressType, localAddress, localPort
|
2017-11-22 18:41:00 +01:00
|
|
|
);
|
2017-05-05 13:10:43 +02:00
|
|
|
});
|
2015-04-22 23:46:21 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-04-23 00:05:29 +02:00
|
|
|
if (options.lookup && typeof options.lookup !== 'function')
|
2018-02-27 14:55:32 +01:00
|
|
|
throw new ERR_INVALID_ARG_TYPE('options.lookup',
|
|
|
|
'Function', options.lookup);
|
2015-04-23 00:05:29 +02:00
|
|
|
|
2018-05-07 05:12:47 +02:00
|
|
|
|
|
|
|
if (dns === undefined) dns = require('dns');
|
2015-04-22 23:46:21 +02:00
|
|
|
var dnsopts = {
|
|
|
|
family: options.family,
|
2016-04-01 22:59:09 +02:00
|
|
|
hints: options.hints || 0
|
2015-04-22 23:46:21 +02:00
|
|
|
};
|
|
|
|
|
2017-12-13 21:10:38 +01:00
|
|
|
if (process.platform !== 'win32' &&
|
|
|
|
dnsopts.family !== 4 &&
|
|
|
|
dnsopts.family !== 6 &&
|
|
|
|
dnsopts.hints === 0) {
|
2016-04-19 15:09:57 +02:00
|
|
|
dnsopts.hints = dns.ADDRCONFIG;
|
|
|
|
}
|
|
|
|
|
2017-04-11 17:31:54 +02:00
|
|
|
debug('connect: find host', host);
|
2015-06-25 14:29:01 +02:00
|
|
|
debug('connect: dns options', dnsopts);
|
2015-04-22 23:46:21 +02:00
|
|
|
self._host = host;
|
2015-04-23 00:05:29 +02:00
|
|
|
var lookup = options.lookup || dns.lookup;
|
2018-01-05 15:03:10 +01:00
|
|
|
defaultTriggerAsyncIdScope(self[async_id_symbol], function() {
|
2017-11-22 18:41:00 +01:00
|
|
|
lookup(host, dnsopts, function emitLookup(err, ip, addressType) {
|
|
|
|
self.emit('lookup', err, ip, addressType, host);
|
|
|
|
|
|
|
|
// It's possible we were destroyed while looking this up.
|
|
|
|
// XXX it would be great if we could cancel the promise returned by
|
|
|
|
// the look up.
|
|
|
|
if (!self.connecting) return;
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
// net.createConnection() creates a net.Socket object and
|
|
|
|
// immediately calls net.Socket.connect() on it (that's us).
|
|
|
|
// There are no event listeners registered yet so defer the
|
|
|
|
// error event to the next tick.
|
|
|
|
err.host = options.host;
|
|
|
|
err.port = options.port;
|
|
|
|
err.message = err.message + ' ' + options.host + ':' + options.port;
|
|
|
|
process.nextTick(connectErrorNT, self, err);
|
2018-03-17 17:43:50 +01:00
|
|
|
} else if (addressType !== 4 && addressType !== 6) {
|
|
|
|
err = new ERR_INVALID_ADDRESS_FAMILY(addressType);
|
|
|
|
err.host = options.host;
|
|
|
|
err.port = options.port;
|
|
|
|
err.message = err.message + ' ' + options.host + ':' + options.port;
|
|
|
|
process.nextTick(connectErrorNT, self, err);
|
2017-11-22 18:41:00 +01:00
|
|
|
} else {
|
|
|
|
self._unrefTimer();
|
|
|
|
defaultTriggerAsyncIdScope(
|
|
|
|
self[async_id_symbol],
|
2018-01-05 15:03:10 +01:00
|
|
|
internalConnect,
|
|
|
|
self, ip, port, addressType, localAddress, localPort
|
2017-11-22 18:41:00 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
2015-04-22 23:46:21 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-05-04 20:40:25 +02:00
|
|
|
function connectErrorNT(self, err) {
|
2017-05-06 14:20:52 +02:00
|
|
|
self.destroy(err);
|
2015-03-05 22:07:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-07-13 03:26:04 +02:00
|
|
|
Socket.prototype.ref = function() {
|
2015-02-18 19:02:01 +01:00
|
|
|
if (!this._handle) {
|
|
|
|
this.once('connect', this.ref);
|
2015-05-22 18:35:57 +02:00
|
|
|
return this;
|
2015-02-18 19:02:01 +01:00
|
|
|
}
|
|
|
|
|
2017-12-12 00:05:18 +01:00
|
|
|
if (typeof this._handle.ref === 'function') {
|
|
|
|
this._handle.ref();
|
|
|
|
}
|
2015-05-22 18:35:57 +02:00
|
|
|
|
|
|
|
return this;
|
2012-07-13 03:26:04 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Socket.prototype.unref = function() {
|
2015-02-18 19:02:01 +01:00
|
|
|
if (!this._handle) {
|
|
|
|
this.once('connect', this.unref);
|
2015-05-22 18:35:57 +02:00
|
|
|
return this;
|
2015-02-18 19:02:01 +01:00
|
|
|
}
|
|
|
|
|
2017-12-12 00:05:18 +01:00
|
|
|
if (typeof this._handle.unref === 'function') {
|
|
|
|
this._handle.unref();
|
|
|
|
}
|
2015-05-22 18:35:57 +02:00
|
|
|
|
|
|
|
return this;
|
2012-07-13 03:26:04 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2012-02-10 10:35:35 +01:00
|
|
|
function afterConnect(status, handle, req, readable, writable) {
|
2012-04-27 04:42:10 +02:00
|
|
|
var self = handle.owner;
|
2011-07-02 09:18:36 +02:00
|
|
|
|
2011-07-15 22:20:24 +02:00
|
|
|
// callback may come after call to destroy
|
|
|
|
if (self.destroyed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
stream_base: introduce StreamBase
StreamBase is an improved way to write C++ streams. The class itself is
for separting `StreamWrap` (with the methods like `.writeAsciiString`,
`.writeBuffer`, `.writev`, etc) from the `HandleWrap` class, making
possible to write abstract C++ streams that are not bound to any uv
socket.
The following methods are important part of the abstraction (which
mimics libuv's stream API):
* Events:
* `OnAlloc(size_t size, uv_buf_t*)`
* `OnRead(ssize_t nread, const uv_buf_t*, uv_handle_type pending)`
* `OnAfterWrite(WriteWrap*)`
* Wrappers:
* `DoShutdown(ShutdownWrap*)`
* `DoTryWrite(uv_buf_t** bufs, size_t* count)`
* `DoWrite(WriteWrap*, uv_buf_t*, size_t count, uv_stream_t* handle)`
* `Error()`
* `ClearError()`
The implementation should provide all of these methods, thus providing
the access to the underlying resource (be it uv handle, TLS socket, or
anything else).
A C++ stream may consume the input of another stream by replacing the
event callbacks and proxying the writes. This kind of API is actually
used now for the TLSWrap implementation, making it possible to wrap TLS
stream into another TLS stream. Thus legacy API calls are no longer
required in `_tls_wrap.js`.
PR-URL: https://github.com/iojs/io.js/pull/840
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
Reviewed-By: Chris Dickinson <christopher.s.dickinson@gmail.com>
2015-02-22 19:59:07 +01:00
|
|
|
// Update handle if it was wrapped
|
|
|
|
// TODO(indutny): assert that the handle is actually an ancestor of old one
|
|
|
|
handle = self._handle;
|
2011-07-22 11:11:02 +02:00
|
|
|
|
2011-10-05 00:08:18 +02:00
|
|
|
debug('afterConnect');
|
2011-07-22 11:11:02 +02:00
|
|
|
|
2017-10-06 18:52:56 +02:00
|
|
|
assert(self.connecting);
|
2016-04-26 22:27:08 +02:00
|
|
|
self.connecting = false;
|
2015-07-03 00:26:21 +02:00
|
|
|
self._sockname = null;
|
2011-06-17 17:10:12 +02:00
|
|
|
|
2017-02-23 00:46:32 +01:00
|
|
|
if (status === 0) {
|
2012-02-10 10:35:35 +01:00
|
|
|
self.readable = readable;
|
2018-06-12 17:29:57 +02:00
|
|
|
if (!self._writableState.ended)
|
|
|
|
self.writable = writable;
|
2015-02-19 14:14:36 +01:00
|
|
|
self._unrefTimer();
|
2011-07-02 09:18:36 +02:00
|
|
|
|
2012-02-26 20:13:08 +01:00
|
|
|
self.emit('connect');
|
2018-03-17 11:54:46 +01:00
|
|
|
self.emit('ready');
|
2012-02-26 20:13:08 +01:00
|
|
|
|
2012-12-13 06:18:57 +01:00
|
|
|
// start the first read, or get an immediate EOF.
|
|
|
|
// this doesn't actually consume any bytes, because len=0.
|
2014-08-19 23:38:55 +02:00
|
|
|
if (readable && !self.isPaused())
|
2012-12-13 06:18:57 +01:00
|
|
|
self.read(0);
|
|
|
|
|
2011-06-16 21:11:05 +02:00
|
|
|
} else {
|
2016-04-26 22:27:08 +02:00
|
|
|
self.connecting = false;
|
2014-12-03 04:16:32 +01:00
|
|
|
var details;
|
|
|
|
if (req.localAddress && req.localPort) {
|
2015-11-20 22:16:55 +01:00
|
|
|
details = req.localAddress + ':' + req.localPort;
|
2014-12-03 04:16:32 +01:00
|
|
|
}
|
2015-01-08 20:13:56 +01:00
|
|
|
var ex = exceptionWithHostPort(status,
|
|
|
|
'connect',
|
|
|
|
req.address,
|
|
|
|
req.port,
|
|
|
|
details);
|
2015-11-20 22:16:55 +01:00
|
|
|
if (details) {
|
|
|
|
ex.localAddress = req.localAddress;
|
|
|
|
ex.localPort = req.localPort;
|
|
|
|
}
|
2017-05-06 14:20:52 +02:00
|
|
|
self.destroy(ex);
|
2011-06-16 21:11:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-09-24 05:08:35 +02:00
|
|
|
function Server(options, connectionListener) {
|
|
|
|
if (!(this instanceof Server))
|
|
|
|
return new Server(options, connectionListener);
|
|
|
|
|
2015-09-17 00:45:29 +02:00
|
|
|
EventEmitter.call(this);
|
2011-06-16 21:11:05 +02:00
|
|
|
|
2014-09-24 05:08:35 +02:00
|
|
|
if (typeof options === 'function') {
|
|
|
|
connectionListener = options;
|
2011-06-16 21:11:05 +02:00
|
|
|
options = {};
|
2016-03-23 09:14:29 +01:00
|
|
|
this.on('connection', connectionListener);
|
2015-09-14 04:49:35 +02:00
|
|
|
} else if (options == null || typeof options === 'object') {
|
2014-09-24 05:08:35 +02:00
|
|
|
options = options || {};
|
2011-10-05 00:08:18 +02:00
|
|
|
|
2014-09-24 05:08:35 +02:00
|
|
|
if (typeof connectionListener === 'function') {
|
2016-03-23 09:14:29 +01:00
|
|
|
this.on('connection', connectionListener);
|
2011-06-16 21:11:05 +02:00
|
|
|
}
|
2015-09-14 04:49:35 +02:00
|
|
|
} else {
|
2018-02-27 14:55:32 +01:00
|
|
|
throw new ERR_INVALID_ARG_TYPE('options', 'Object', options);
|
2011-06-16 21:11:05 +02:00
|
|
|
}
|
|
|
|
|
2012-04-12 09:23:07 +02:00
|
|
|
this._connections = 0;
|
|
|
|
|
|
|
|
Object.defineProperty(this, 'connections', {
|
2016-03-23 09:14:29 +01:00
|
|
|
get: internalUtil.deprecate(() => {
|
2013-01-14 18:08:20 +01:00
|
|
|
|
2017-07-24 16:52:38 +02:00
|
|
|
if (this._usingWorkers) {
|
2012-04-12 09:23:07 +02:00
|
|
|
return null;
|
|
|
|
}
|
2016-03-23 09:14:29 +01:00
|
|
|
return this._connections;
|
2015-06-13 18:44:39 +02:00
|
|
|
}, 'Server.connections property is deprecated. ' +
|
2016-12-04 21:47:01 +01:00
|
|
|
'Use Server.getConnections method instead.', 'DEP0020'),
|
2017-01-12 08:20:54 +01:00
|
|
|
set: internalUtil.deprecate((val) => (this._connections = val),
|
2016-12-04 21:47:01 +01:00
|
|
|
'Server.connections property is deprecated.',
|
|
|
|
'DEP0020'),
|
2014-09-17 20:54:24 +02:00
|
|
|
configurable: true, enumerable: false
|
2012-04-12 09:23:07 +02:00
|
|
|
});
|
|
|
|
|
2017-03-10 14:17:42 +01:00
|
|
|
this[async_id_symbol] = -1;
|
2011-07-07 02:13:17 +02:00
|
|
|
this._handle = null;
|
2017-07-24 16:52:38 +02:00
|
|
|
this._usingWorkers = false;
|
|
|
|
this._workers = [];
|
2015-01-03 05:14:25 +01:00
|
|
|
this._unref = false;
|
2012-12-13 06:18:57 +01:00
|
|
|
|
|
|
|
this.allowHalfOpen = options.allowHalfOpen || false;
|
2014-10-18 03:45:40 +02:00
|
|
|
this.pauseOnConnect = !!options.pauseOnConnect;
|
2011-06-16 21:11:05 +02:00
|
|
|
}
|
2015-09-17 00:45:29 +02:00
|
|
|
util.inherits(Server, EventEmitter);
|
2011-06-16 21:11:05 +02:00
|
|
|
|
|
|
|
|
2012-04-18 15:56:14 +02:00
|
|
|
function toNumber(x) { return (x = Number(x)) >= 0 ? x : false; }
|
2011-06-16 21:11:05 +02:00
|
|
|
|
2017-03-11 05:25:39 +01:00
|
|
|
// Returns handle if it can be created, or error code if it can't
|
2016-01-12 22:04:50 +01:00
|
|
|
function createServerHandle(address, port, addressType, fd) {
|
2013-07-18 23:18:50 +02:00
|
|
|
var err = 0;
|
2011-10-12 11:56:29 +02:00
|
|
|
// assign handle in listen, and clean up if bind or listen fails
|
2011-12-01 22:24:28 +01:00
|
|
|
var handle;
|
|
|
|
|
2014-05-28 00:26:13 +02:00
|
|
|
var isTCP = false;
|
2015-01-29 02:05:53 +01:00
|
|
|
if (typeof fd === 'number' && fd >= 0) {
|
2013-03-14 15:13:58 +01:00
|
|
|
try {
|
2017-11-20 17:18:40 +01:00
|
|
|
handle = createHandle(fd, true);
|
2016-07-09 02:17:47 +02:00
|
|
|
} catch (e) {
|
2013-03-14 15:13:58 +01:00
|
|
|
// Not a fd we can listen on. This will trigger an error.
|
2017-04-11 17:31:54 +02:00
|
|
|
debug('listen invalid fd=%d:', fd, e.message);
|
2017-08-19 00:37:35 +02:00
|
|
|
return UV_EINVAL;
|
2012-06-12 19:02:52 +02:00
|
|
|
}
|
2018-06-20 23:02:03 +02:00
|
|
|
|
|
|
|
err = handle.open(fd);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2013-03-14 15:13:58 +01:00
|
|
|
handle.readable = true;
|
|
|
|
handle.writable = true;
|
2014-02-24 18:28:49 +01:00
|
|
|
assert(!address && !port);
|
2014-01-28 17:18:36 +01:00
|
|
|
} else if (port === -1 && addressType === -1) {
|
2017-11-20 17:18:40 +01:00
|
|
|
handle = new Pipe(PipeConstants.SERVER);
|
2011-12-01 22:24:28 +01:00
|
|
|
if (process.platform === 'win32') {
|
|
|
|
var instances = parseInt(process.env.NODE_PENDING_PIPE_INSTANCES);
|
2018-02-13 00:56:35 +01:00
|
|
|
if (!Number.isNaN(instances)) {
|
2011-12-01 22:24:28 +01:00
|
|
|
handle.setPendingInstances(instances);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2017-11-20 17:18:40 +01:00
|
|
|
handle = new TCP(TCPConstants.SERVER);
|
2014-05-28 00:26:13 +02:00
|
|
|
isTCP = true;
|
2011-12-01 22:24:28 +01:00
|
|
|
}
|
2011-10-12 11:56:29 +02:00
|
|
|
|
2014-05-28 00:26:13 +02:00
|
|
|
if (address || port || isTCP) {
|
2017-04-11 17:31:54 +02:00
|
|
|
debug('bind to', address || 'any');
|
2014-04-13 17:11:56 +02:00
|
|
|
if (!address) {
|
|
|
|
// Try binding to ipv6 first
|
|
|
|
err = handle.bind6('::', port);
|
|
|
|
if (err) {
|
|
|
|
handle.close();
|
|
|
|
// Fallback to ipv4
|
|
|
|
return createServerHandle('0.0.0.0', port);
|
|
|
|
}
|
|
|
|
} else if (addressType === 6) {
|
2013-07-18 23:18:50 +02:00
|
|
|
err = handle.bind6(address, port);
|
2011-10-12 11:56:29 +02:00
|
|
|
} else {
|
2013-07-18 23:18:50 +02:00
|
|
|
err = handle.bind(address, port);
|
2011-10-12 11:56:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-18 23:18:50 +02:00
|
|
|
if (err) {
|
2011-10-12 11:56:29 +02:00
|
|
|
handle.close();
|
2013-07-18 23:18:50 +02:00
|
|
|
return err;
|
2011-10-12 11:56:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return handle;
|
2016-01-15 09:53:11 +01:00
|
|
|
}
|
2011-10-12 11:56:29 +02:00
|
|
|
|
2017-03-11 05:25:39 +01:00
|
|
|
function setupListenHandle(address, port, addressType, backlog, fd) {
|
|
|
|
debug('setupListenHandle', address, port, addressType, backlog, fd);
|
2011-06-29 18:04:55 +02:00
|
|
|
|
2012-08-06 23:43:47 +02:00
|
|
|
// If there is not yet a handle, we need to create one and bind.
|
|
|
|
// In the case of a server sent via IPC, we don't need to do this.
|
2016-02-15 05:53:17 +01:00
|
|
|
if (this._handle) {
|
2017-03-11 05:25:39 +01:00
|
|
|
debug('setupListenHandle: have a handle already');
|
2015-01-21 13:38:44 +01:00
|
|
|
} else {
|
2017-03-11 05:25:39 +01:00
|
|
|
debug('setupListenHandle: create a handle');
|
2015-01-21 13:38:44 +01:00
|
|
|
|
|
|
|
var rval = null;
|
|
|
|
|
2017-03-11 05:25:39 +01:00
|
|
|
// Try to bind to the unspecified IPv6 address, see if IPv6 is available
|
2015-01-29 02:05:53 +01:00
|
|
|
if (!address && typeof fd !== 'number') {
|
2015-01-21 13:38:44 +01:00
|
|
|
rval = createServerHandle('::', port, 6, fd);
|
|
|
|
|
2015-01-29 02:05:53 +01:00
|
|
|
if (typeof rval === 'number') {
|
2015-01-21 13:38:44 +01:00
|
|
|
rval = null;
|
|
|
|
address = '0.0.0.0';
|
|
|
|
addressType = 4;
|
|
|
|
} else {
|
|
|
|
address = '::';
|
|
|
|
addressType = 6;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rval === null)
|
|
|
|
rval = createServerHandle(address, port, addressType, fd);
|
|
|
|
|
2015-01-29 02:05:53 +01:00
|
|
|
if (typeof rval === 'number') {
|
2015-01-08 20:13:56 +01:00
|
|
|
var error = exceptionWithHostPort(rval, 'listen', address, port);
|
2016-02-15 05:53:17 +01:00
|
|
|
process.nextTick(emitErrorNT, this, error);
|
2012-08-06 23:43:47 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-02-15 05:53:17 +01:00
|
|
|
this._handle = rval;
|
2012-08-06 23:43:47 +02:00
|
|
|
}
|
|
|
|
|
2017-03-10 14:17:42 +01:00
|
|
|
this[async_id_symbol] = getNewAsyncId(this._handle);
|
2016-02-15 05:53:17 +01:00
|
|
|
this._handle.onconnection = onconnection;
|
|
|
|
this._handle.owner = this;
|
2011-10-07 22:58:49 +02:00
|
|
|
|
2017-03-11 05:25:39 +01:00
|
|
|
// Use a backlog of 512 entries. We pass 511 to the listen() call because
|
|
|
|
// the kernel does: backlogsize = roundup_pow_of_two(backlogsize + 1);
|
|
|
|
// which will thus give us a backlog of 512 entries.
|
|
|
|
var err = this._handle.listen(backlog || 511);
|
2011-10-07 22:58:49 +02:00
|
|
|
|
2013-07-18 23:18:50 +02:00
|
|
|
if (err) {
|
2015-01-08 20:13:56 +01:00
|
|
|
var ex = exceptionWithHostPort(err, 'listen', address, port);
|
2016-02-15 05:53:17 +01:00
|
|
|
this._handle.close();
|
|
|
|
this._handle = null;
|
2018-03-05 12:20:48 +01:00
|
|
|
defaultTriggerAsyncIdScope(this[async_id_symbol],
|
|
|
|
process.nextTick,
|
|
|
|
emitErrorNT,
|
|
|
|
this,
|
|
|
|
ex);
|
2012-08-06 23:43:47 +02:00
|
|
|
return;
|
2011-06-24 21:32:09 +02:00
|
|
|
}
|
2011-10-07 22:58:49 +02:00
|
|
|
|
2012-04-12 09:23:07 +02:00
|
|
|
// generate connection key, this should be unique to the connection
|
|
|
|
this._connectionKey = addressType + ':' + address + ':' + port;
|
|
|
|
|
2015-01-03 05:14:25 +01:00
|
|
|
// unref the handle if the server was unref'ed prior to listening
|
|
|
|
if (this._unref)
|
|
|
|
this.unref();
|
|
|
|
|
2018-03-05 12:20:48 +01:00
|
|
|
defaultTriggerAsyncIdScope(this[async_id_symbol],
|
|
|
|
process.nextTick,
|
|
|
|
emitListeningNT,
|
|
|
|
this);
|
2017-03-11 05:25:39 +01:00
|
|
|
}
|
2011-06-24 21:32:09 +02:00
|
|
|
|
2017-03-11 05:25:39 +01:00
|
|
|
Server.prototype._listen2 = setupListenHandle; // legacy alias
|
2011-10-07 22:58:49 +02:00
|
|
|
|
2015-05-04 20:40:25 +02:00
|
|
|
function emitErrorNT(self, err) {
|
|
|
|
self.emit('error', err);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-05 22:07:27 +01:00
|
|
|
function emitListeningNT(self) {
|
|
|
|
// ensure handle hasn't closed
|
|
|
|
if (self._handle)
|
|
|
|
self.emit('listening');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-11 05:25:39 +01:00
|
|
|
function listenInCluster(server, address, port, addressType,
|
|
|
|
backlog, fd, exclusive) {
|
2014-08-22 22:51:53 +02:00
|
|
|
exclusive = !!exclusive;
|
|
|
|
|
2018-05-07 05:12:47 +02:00
|
|
|
if (cluster === undefined) cluster = require('cluster');
|
2012-07-10 12:06:13 +02:00
|
|
|
|
2014-08-22 22:51:53 +02:00
|
|
|
if (cluster.isMaster || exclusive) {
|
2017-03-11 05:25:39 +01:00
|
|
|
// Will create a new handle
|
|
|
|
// _listen2 sets up the listened handle, it is still named like this
|
|
|
|
// to avoid breaking code that wraps this method
|
|
|
|
server._listen2(address, port, addressType, backlog, fd);
|
2012-02-09 06:22:50 +01:00
|
|
|
return;
|
2012-07-14 09:38:00 +02:00
|
|
|
}
|
2012-02-09 06:22:50 +01:00
|
|
|
|
2017-03-11 05:25:39 +01:00
|
|
|
const serverQuery = {
|
2015-08-25 21:24:41 +02:00
|
|
|
address: address,
|
|
|
|
port: port,
|
|
|
|
addressType: addressType,
|
|
|
|
fd: fd,
|
|
|
|
flags: 0
|
2017-03-11 05:25:39 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
// Get the master's server handle, and listen on it
|
|
|
|
cluster._getServer(server, serverQuery, listenOnMasterHandle);
|
2013-07-28 12:19:34 +02:00
|
|
|
|
2017-03-11 05:25:39 +01:00
|
|
|
function listenOnMasterHandle(err, handle) {
|
2017-08-30 19:13:04 +02:00
|
|
|
err = checkBindError(err, port, handle);
|
2012-02-09 06:22:50 +01:00
|
|
|
|
2014-12-03 04:16:32 +01:00
|
|
|
if (err) {
|
2015-01-08 20:13:56 +01:00
|
|
|
var ex = exceptionWithHostPort(err, 'bind', address, port);
|
2017-03-11 05:25:39 +01:00
|
|
|
return server.emit('error', ex);
|
2014-12-03 04:16:32 +01:00
|
|
|
}
|
2013-07-28 12:19:34 +02:00
|
|
|
|
2017-03-11 05:25:39 +01:00
|
|
|
// Reuse master's server handle
|
|
|
|
server._handle = handle;
|
|
|
|
// _listen2 sets up the listened handle, it is still named like this
|
|
|
|
// to avoid breaking code that wraps this method
|
|
|
|
server._listen2(address, port, addressType, backlog, fd);
|
2013-07-28 12:19:34 +02:00
|
|
|
}
|
2012-07-14 09:38:00 +02:00
|
|
|
}
|
2011-10-12 11:56:29 +02:00
|
|
|
|
2012-08-06 23:43:47 +02:00
|
|
|
|
2017-06-05 14:05:36 +02:00
|
|
|
Server.prototype.listen = function(...args) {
|
2017-04-24 08:21:25 +02:00
|
|
|
var normalized = normalizeArgs(args);
|
2017-03-03 17:52:12 +01:00
|
|
|
var options = normalized[0];
|
2017-04-24 08:21:25 +02:00
|
|
|
var cb = normalized[1];
|
2011-06-16 21:11:05 +02:00
|
|
|
|
2017-05-22 12:35:22 +02:00
|
|
|
if (this._handle) {
|
2018-02-27 14:55:32 +01:00
|
|
|
throw new ERR_SERVER_ALREADY_LISTEN();
|
2017-05-22 12:35:22 +02:00
|
|
|
}
|
|
|
|
|
2018-05-21 14:08:12 +02:00
|
|
|
if (cb !== null) {
|
2016-08-12 18:22:22 +02:00
|
|
|
this.once('listening', cb);
|
2011-06-16 21:11:05 +02:00
|
|
|
}
|
2017-04-24 08:21:25 +02:00
|
|
|
var backlogFromArgs =
|
2017-03-03 17:52:12 +01:00
|
|
|
// (handle, backlog) or (path, backlog) or (port, backlog)
|
|
|
|
toNumber(args.length > 1 && args[1]) ||
|
|
|
|
toNumber(args.length > 2 && args[2]); // (port, host, backlog)
|
2011-06-16 21:11:05 +02:00
|
|
|
|
2016-08-12 18:22:22 +02:00
|
|
|
options = options._handle || options.handle || options;
|
2017-03-03 17:52:12 +01:00
|
|
|
// (handle[, backlog][, cb]) where handle is an object with a handle
|
2016-08-12 18:22:22 +02:00
|
|
|
if (options instanceof TCP) {
|
|
|
|
this._handle = options;
|
2017-03-10 14:17:42 +01:00
|
|
|
this[async_id_symbol] = this._handle.getAsyncId();
|
2017-03-11 05:25:39 +01:00
|
|
|
listenInCluster(this, null, -1, -1, backlogFromArgs);
|
2017-03-03 17:52:12 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
// (handle[, backlog][, cb]) where handle is an object with a fd
|
|
|
|
if (typeof options.fd === 'number' && options.fd >= 0) {
|
2017-03-11 05:25:39 +01:00
|
|
|
listenInCluster(this, null, null, null, backlogFromArgs, options.fd);
|
2017-03-03 17:52:12 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ([port][, host][, backlog][, cb]) where port is omitted,
|
2017-07-13 17:10:54 +02:00
|
|
|
// that is, listen(), listen(null), listen(cb), or listen(null, cb)
|
|
|
|
// or (options[, cb]) where options.port is explicitly set as undefined or
|
|
|
|
// null, bind to an arbitrary unused port
|
2017-03-03 17:52:12 +01:00
|
|
|
if (args.length === 0 || typeof args[0] === 'function' ||
|
2017-07-13 17:10:54 +02:00
|
|
|
(typeof options.port === 'undefined' && 'port' in options) ||
|
|
|
|
options.port === null) {
|
2017-03-03 17:52:12 +01:00
|
|
|
options.port = 0;
|
|
|
|
}
|
|
|
|
// ([port][, host][, backlog][, cb]) where port is specified
|
|
|
|
// or (options[, cb]) where options.port is specified
|
|
|
|
// or if options.port is normalized as 0 before
|
2017-04-24 08:21:25 +02:00
|
|
|
var backlog;
|
2017-03-03 17:52:12 +01:00
|
|
|
if (typeof options.port === 'number' || typeof options.port === 'string') {
|
|
|
|
if (!isLegalPort(options.port)) {
|
2018-02-27 14:55:32 +01:00
|
|
|
throw new ERR_SOCKET_BAD_PORT(options.port);
|
2017-03-03 17:52:12 +01:00
|
|
|
}
|
2017-04-24 08:21:25 +02:00
|
|
|
backlog = options.backlog || backlogFromArgs;
|
2017-03-03 17:52:12 +01:00
|
|
|
// start TCP server listening on host:port
|
|
|
|
if (options.host) {
|
|
|
|
lookupAndListen(this, options.port | 0, options.host, backlog,
|
|
|
|
options.exclusive);
|
|
|
|
} else { // Undefined host, listens on unspecified address
|
2017-03-11 05:25:39 +01:00
|
|
|
// Default addressType 4 will be used to search for master server
|
|
|
|
listenInCluster(this, null, options.port | 0, 4,
|
|
|
|
backlog, undefined, options.exclusive);
|
2016-08-12 18:22:22 +02:00
|
|
|
}
|
2017-03-03 17:52:12 +01:00
|
|
|
return this;
|
2011-06-16 21:11:05 +02:00
|
|
|
}
|
2014-08-22 22:51:53 +02:00
|
|
|
|
2017-03-03 17:52:12 +01:00
|
|
|
// (path[, backlog][, cb]) or (options[, cb])
|
|
|
|
// where path or options.path is a UNIX domain socket or Windows pipe
|
|
|
|
if (options.path && isPipeName(options.path)) {
|
2017-04-24 08:21:25 +02:00
|
|
|
var pipeName = this._pipeName = options.path;
|
|
|
|
backlog = options.backlog || backlogFromArgs;
|
2017-03-11 05:25:39 +01:00
|
|
|
listenInCluster(this, pipeName, -1, -1,
|
|
|
|
backlog, undefined, options.exclusive);
|
2018-03-08 00:09:24 +01:00
|
|
|
let mode = 0;
|
|
|
|
if (options.readableAll === true)
|
|
|
|
mode |= PipeConstants.UV_READABLE;
|
|
|
|
if (options.writableAll === true)
|
|
|
|
mode |= PipeConstants.UV_WRITABLE;
|
|
|
|
if (mode !== 0) {
|
|
|
|
const err = this._handle.fchmod(mode);
|
|
|
|
if (err) {
|
|
|
|
this._handle.close();
|
|
|
|
this._handle = null;
|
|
|
|
throw errnoException(err, 'uv_pipe_chmod');
|
|
|
|
}
|
|
|
|
}
|
2017-03-03 17:52:12 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2018-02-27 14:55:32 +01:00
|
|
|
throw new ERR_INVALID_OPT_VALUE('options', util.inspect(options));
|
2011-06-16 21:11:05 +02:00
|
|
|
};
|
|
|
|
|
2016-08-12 18:22:22 +02:00
|
|
|
function lookupAndListen(self, port, address, backlog, exclusive) {
|
2018-05-07 05:12:47 +02:00
|
|
|
if (dns === undefined) dns = require('dns');
|
2017-03-11 05:25:39 +01:00
|
|
|
dns.lookup(address, function doListen(err, ip, addressType) {
|
2016-08-12 18:22:22 +02:00
|
|
|
if (err) {
|
|
|
|
self.emit('error', err);
|
|
|
|
} else {
|
|
|
|
addressType = ip ? addressType : 4;
|
2017-03-11 05:25:39 +01:00
|
|
|
listenInCluster(self, ip, port, addressType,
|
|
|
|
backlog, undefined, exclusive);
|
2016-08-12 18:22:22 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-01-18 14:36:48 +01:00
|
|
|
Object.defineProperty(Server.prototype, 'listening', {
|
|
|
|
get: function() {
|
|
|
|
return !!this._handle;
|
|
|
|
},
|
|
|
|
configurable: true,
|
|
|
|
enumerable: true
|
|
|
|
});
|
|
|
|
|
2011-07-16 00:06:10 +02:00
|
|
|
Server.prototype.address = function() {
|
2011-11-01 23:42:45 +01:00
|
|
|
if (this._handle && this._handle.getsockname) {
|
2013-07-18 23:18:50 +02:00
|
|
|
var out = {};
|
2017-05-06 12:33:28 +02:00
|
|
|
var err = this._handle.getsockname(out);
|
|
|
|
if (err) {
|
|
|
|
throw errnoException(err, 'address');
|
|
|
|
}
|
2013-07-18 23:18:50 +02:00
|
|
|
return out;
|
2015-08-27 22:47:15 +02:00
|
|
|
} else if (this._pipeName) {
|
|
|
|
return this._pipeName;
|
2011-11-01 23:42:45 +01:00
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
2011-07-16 00:06:10 +02:00
|
|
|
};
|
2011-06-16 21:11:05 +02:00
|
|
|
|
2013-07-18 23:18:50 +02:00
|
|
|
function onconnection(err, clientHandle) {
|
2011-06-16 21:11:05 +02:00
|
|
|
var handle = this;
|
2012-04-27 04:42:10 +02:00
|
|
|
var self = handle.owner;
|
2011-06-16 21:11:05 +02:00
|
|
|
|
2011-10-05 00:08:18 +02:00
|
|
|
debug('onconnection');
|
2011-07-02 09:18:36 +02:00
|
|
|
|
2013-07-18 23:18:50 +02:00
|
|
|
if (err) {
|
|
|
|
self.emit('error', errnoException(err, 'accept'));
|
2011-08-10 23:59:21 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-04-12 09:23:07 +02:00
|
|
|
if (self.maxConnections && self._connections >= self.maxConnections) {
|
2011-07-14 22:18:28 +02:00
|
|
|
clientHandle.close();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-06-17 17:10:12 +02:00
|
|
|
var socket = new Socket({
|
|
|
|
handle: clientHandle,
|
2014-10-18 03:45:40 +02:00
|
|
|
allowHalfOpen: self.allowHalfOpen,
|
2018-04-12 11:15:31 +02:00
|
|
|
pauseOnCreate: self.pauseOnConnect,
|
|
|
|
readable: true,
|
|
|
|
writable: true
|
2011-06-17 17:10:12 +02:00
|
|
|
});
|
2011-06-16 21:11:05 +02:00
|
|
|
|
2012-04-12 09:23:07 +02:00
|
|
|
self._connections++;
|
2011-06-17 14:27:02 +02:00
|
|
|
socket.server = self;
|
2016-02-16 21:09:31 +01:00
|
|
|
socket._server = self;
|
2011-06-17 14:27:02 +02:00
|
|
|
|
2011-06-16 21:11:05 +02:00
|
|
|
DTRACE_NET_SERVER_CONNECTION(socket);
|
2012-11-21 00:27:22 +01:00
|
|
|
COUNTER_NET_SERVER_CONNECTION(socket);
|
2011-06-16 21:11:05 +02:00
|
|
|
self.emit('connection', socket);
|
|
|
|
}
|
|
|
|
|
2011-06-17 17:10:12 +02:00
|
|
|
|
2013-01-14 18:08:20 +01:00
|
|
|
Server.prototype.getConnections = function(cb) {
|
2017-03-10 14:17:42 +01:00
|
|
|
const self = this;
|
|
|
|
|
2013-01-14 18:08:20 +01:00
|
|
|
function end(err, connections) {
|
2018-03-05 12:20:48 +01:00
|
|
|
defaultTriggerAsyncIdScope(self[async_id_symbol],
|
|
|
|
process.nextTick,
|
|
|
|
cb,
|
|
|
|
err,
|
|
|
|
connections);
|
2013-01-14 18:08:20 +01:00
|
|
|
}
|
|
|
|
|
2017-07-24 16:52:38 +02:00
|
|
|
if (!this._usingWorkers) {
|
2017-06-07 21:49:00 +02:00
|
|
|
end(null, this._connections);
|
|
|
|
return this;
|
2013-01-14 18:08:20 +01:00
|
|
|
}
|
|
|
|
|
2017-07-24 16:52:38 +02:00
|
|
|
// Poll workers
|
|
|
|
var left = this._workers.length;
|
2016-01-12 22:04:50 +01:00
|
|
|
var total = this._connections;
|
2013-01-14 18:08:20 +01:00
|
|
|
|
|
|
|
function oncount(err, count) {
|
|
|
|
if (err) {
|
|
|
|
left = -1;
|
|
|
|
return end(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
total += count;
|
|
|
|
if (--left === 0) return end(null, total);
|
|
|
|
}
|
|
|
|
|
2017-07-24 16:52:38 +02:00
|
|
|
for (var n = 0; n < this._workers.length; n++) {
|
|
|
|
this._workers[n].getConnections(oncount);
|
2017-02-27 03:08:04 +01:00
|
|
|
}
|
2017-06-07 21:49:00 +02:00
|
|
|
|
|
|
|
return this;
|
2013-01-14 18:08:20 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2012-02-16 20:52:47 +01:00
|
|
|
Server.prototype.close = function(cb) {
|
2015-01-26 18:55:19 +01:00
|
|
|
if (typeof cb === 'function') {
|
2014-05-16 04:48:27 +02:00
|
|
|
if (!this._handle) {
|
2016-10-29 17:39:53 +02:00
|
|
|
this.once('close', function close() {
|
2018-02-27 14:55:32 +01:00
|
|
|
cb(new ERR_SERVER_NOT_RUNNING());
|
2014-05-16 04:48:27 +02:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.once('close', cb);
|
|
|
|
}
|
2011-07-07 02:13:17 +02:00
|
|
|
}
|
2011-07-22 01:23:50 +02:00
|
|
|
|
2014-05-16 04:48:27 +02:00
|
|
|
if (this._handle) {
|
|
|
|
this._handle.close();
|
|
|
|
this._handle = null;
|
2012-02-16 20:52:47 +01:00
|
|
|
}
|
2011-10-23 11:20:03 +02:00
|
|
|
|
2017-07-24 16:52:38 +02:00
|
|
|
if (this._usingWorkers) {
|
|
|
|
var left = this._workers.length;
|
|
|
|
const onWorkerClose = () => {
|
2017-04-11 11:39:25 +02:00
|
|
|
if (--left !== 0) return;
|
|
|
|
|
|
|
|
this._connections = 0;
|
|
|
|
this._emitCloseIfDrained();
|
|
|
|
};
|
2013-01-14 18:08:20 +01:00
|
|
|
|
|
|
|
// Increment connections to be sure that, even if all sockets will be closed
|
2017-07-24 16:52:38 +02:00
|
|
|
// during polling of workers, `close` event will be emitted only once.
|
2013-01-14 18:08:20 +01:00
|
|
|
this._connections++;
|
|
|
|
|
2017-07-24 16:52:38 +02:00
|
|
|
// Poll workers
|
|
|
|
for (var n = 0; n < this._workers.length; n++)
|
|
|
|
this._workers[n].close(onWorkerClose);
|
2013-01-14 18:08:20 +01:00
|
|
|
} else {
|
|
|
|
this._emitCloseIfDrained();
|
2012-04-12 09:23:07 +02:00
|
|
|
}
|
|
|
|
|
2011-10-23 11:20:03 +02:00
|
|
|
return this;
|
2011-07-25 09:01:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
Server.prototype._emitCloseIfDrained = function() {
|
2012-12-13 06:18:57 +01:00
|
|
|
debug('SERVER _emitCloseIfDrained');
|
2011-12-29 19:30:07 +01:00
|
|
|
|
2016-02-15 05:53:17 +01:00
|
|
|
if (this._handle || this._connections) {
|
2012-12-13 06:18:57 +01:00
|
|
|
debug('SERVER handle? %j connections? %d',
|
2016-02-15 05:53:17 +01:00
|
|
|
!!this._handle, this._connections);
|
2012-12-13 06:18:57 +01:00
|
|
|
return;
|
|
|
|
}
|
2011-12-29 19:30:07 +01:00
|
|
|
|
2018-03-05 12:20:48 +01:00
|
|
|
defaultTriggerAsyncIdScope(this[async_id_symbol],
|
|
|
|
process.nextTick,
|
|
|
|
emitCloseNT,
|
|
|
|
this);
|
2011-06-17 17:10:12 +02:00
|
|
|
};
|
2011-07-04 20:25:31 +02:00
|
|
|
|
2011-10-10 23:24:56 +02:00
|
|
|
|
2015-05-04 20:40:25 +02:00
|
|
|
function emitCloseNT(self) {
|
|
|
|
debug('SERVER: emit close');
|
|
|
|
self.emit('close');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-13 18:44:39 +02:00
|
|
|
Server.prototype.listenFD = internalUtil.deprecate(function(fd, type) {
|
2012-06-21 20:42:33 +02:00
|
|
|
return this.listen({ fd: fd });
|
2016-12-04 21:47:01 +01:00
|
|
|
}, 'Server.listenFD is deprecated. Use Server.listen({fd: <number>}) instead.',
|
|
|
|
'DEP0021');
|
2011-08-04 21:04:54 +02:00
|
|
|
|
2017-07-24 16:52:38 +02:00
|
|
|
Server.prototype._setupWorker = function(socketList) {
|
|
|
|
this._usingWorkers = true;
|
|
|
|
this._workers.push(socketList);
|
2017-09-29 11:29:58 +02:00
|
|
|
socketList.once('exit', (socketList) => {
|
|
|
|
const index = this._workers.indexOf(socketList);
|
|
|
|
this._workers.splice(index, 1);
|
|
|
|
});
|
2012-04-12 09:23:07 +02:00
|
|
|
};
|
|
|
|
|
2012-07-13 03:26:04 +02:00
|
|
|
Server.prototype.ref = function() {
|
2015-01-03 05:14:25 +01:00
|
|
|
this._unref = false;
|
|
|
|
|
2012-07-13 03:26:04 +02:00
|
|
|
if (this._handle)
|
|
|
|
this._handle.ref();
|
2015-05-22 18:35:57 +02:00
|
|
|
|
|
|
|
return this;
|
2012-07-13 03:26:04 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
Server.prototype.unref = function() {
|
2015-01-03 05:14:25 +01:00
|
|
|
this._unref = true;
|
|
|
|
|
2012-07-13 03:26:04 +02:00
|
|
|
if (this._handle)
|
|
|
|
this._handle.unref();
|
2015-05-22 18:35:57 +02:00
|
|
|
|
|
|
|
return this;
|
2012-07-13 03:26:04 +02:00
|
|
|
};
|
|
|
|
|
2017-03-05 18:15:38 +01:00
|
|
|
var _setSimultaneousAccepts;
|
2012-01-20 01:52:23 +01:00
|
|
|
|
|
|
|
if (process.platform === 'win32') {
|
|
|
|
var simultaneousAccepts;
|
|
|
|
|
2017-03-05 18:15:38 +01:00
|
|
|
_setSimultaneousAccepts = function(handle) {
|
2015-01-29 02:05:53 +01:00
|
|
|
if (handle === undefined) {
|
2012-01-20 01:52:23 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-01-29 02:05:53 +01:00
|
|
|
if (simultaneousAccepts === undefined) {
|
2012-01-20 01:52:23 +01:00
|
|
|
simultaneousAccepts = (process.env.NODE_MANY_ACCEPTS &&
|
2012-02-19 00:01:35 +01:00
|
|
|
process.env.NODE_MANY_ACCEPTS !== '0');
|
2012-01-20 01:52:23 +01:00
|
|
|
}
|
|
|
|
|
2012-02-19 00:01:35 +01:00
|
|
|
if (handle._simultaneousAccepts !== simultaneousAccepts) {
|
2012-01-20 01:52:23 +01:00
|
|
|
handle.setSimultaneousAccepts(simultaneousAccepts);
|
|
|
|
handle._simultaneousAccepts = simultaneousAccepts;
|
|
|
|
}
|
2012-02-19 00:01:35 +01:00
|
|
|
};
|
2012-01-20 01:52:23 +01:00
|
|
|
} else {
|
2018-05-21 14:08:12 +02:00
|
|
|
_setSimultaneousAccepts = function() {};
|
2012-01-20 01:52:23 +01:00
|
|
|
}
|
2017-03-05 18:15:38 +01:00
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
_createServerHandle: createServerHandle,
|
|
|
|
_normalizeArgs: normalizeArgs,
|
|
|
|
_setSimultaneousAccepts,
|
|
|
|
connect,
|
|
|
|
createConnection: connect,
|
|
|
|
createServer,
|
2018-01-26 18:39:10 +01:00
|
|
|
isIP: isIP,
|
|
|
|
isIPv4: isIPv4,
|
|
|
|
isIPv6: isIPv6,
|
2017-03-05 18:15:38 +01:00
|
|
|
Server,
|
|
|
|
Socket,
|
|
|
|
Stream: Socket, // Legacy naming
|
|
|
|
};
|