1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22'use strict'; 23 24const { 25 ArrayIsArray, 26 ArrayPrototypeIndexOf, 27 Boolean, 28 Error, 29 Number, 30 NumberIsNaN, 31 NumberParseInt, 32 ObjectDefineProperty, 33 ObjectSetPrototypeOf, 34 Symbol, 35} = primordials; 36 37const EventEmitter = require('events'); 38const stream = require('stream'); 39let debug = require('internal/util/debuglog').debuglog('net', (fn) => { 40 debug = fn; 41}); 42const { deprecate } = require('internal/util'); 43const { 44 isIP, 45 isIPv4, 46 isIPv6, 47 normalizedArgsSymbol, 48 makeSyncWrite 49} = require('internal/net'); 50const assert = require('internal/assert'); 51const { 52 UV_EADDRINUSE, 53 UV_EINVAL, 54 UV_ENOTCONN 55} = internalBinding('uv'); 56 57const { Buffer } = require('buffer'); 58const { guessHandleType } = internalBinding('util'); 59const { ShutdownWrap } = internalBinding('stream_wrap'); 60const { 61 TCP, 62 TCPConnectWrap, 63 constants: TCPConstants 64} = internalBinding('tcp_wrap'); 65const { 66 Pipe, 67 PipeConnectWrap, 68 constants: PipeConstants 69} = internalBinding('pipe_wrap'); 70const { 71 newAsyncId, 72 defaultTriggerAsyncIdScope, 73 symbols: { async_id_symbol, owner_symbol } 74} = require('internal/async_hooks'); 75const { 76 writevGeneric, 77 writeGeneric, 78 onStreamRead, 79 kAfterAsyncWrite, 80 kHandle, 81 kUpdateTimer, 82 setStreamTimeout, 83 kBuffer, 84 kBufferCb, 85 kBufferGen 86} = require('internal/stream_base_commons'); 87const { 88 codes: { 89 ERR_INVALID_ADDRESS_FAMILY, 90 ERR_INVALID_ARG_TYPE, 91 ERR_INVALID_ARG_VALUE, 92 ERR_INVALID_FD_TYPE, 93 ERR_INVALID_IP_ADDRESS, 94 ERR_INVALID_OPT_VALUE, 95 ERR_SERVER_ALREADY_LISTEN, 96 ERR_SERVER_NOT_RUNNING, 97 ERR_SOCKET_CLOSED 98 }, 99 errnoException, 100 exceptionWithHostPort, 101 uvExceptionWithHostPort 102} = require('internal/errors'); 103const { isUint8Array } = require('internal/util/types'); 104const { 105 validateInt32, 106 validatePort, 107 validateString 108} = require('internal/validators'); 109const kLastWriteQueueSize = Symbol('lastWriteQueueSize'); 110const { 111 DTRACE_NET_SERVER_CONNECTION, 112 DTRACE_NET_STREAM_END 113} = require('internal/dtrace'); 114 115// Lazy loaded to improve startup performance. 116let cluster; 117let dns; 118let BlockList; 119let SocketAddress; 120 121const { clearTimeout } = require('timers'); 122const { kTimeout } = require('internal/timers'); 123 124const DEFAULT_IPV4_ADDR = '0.0.0.0'; 125const DEFAULT_IPV6_ADDR = '::'; 126 127const isWindows = process.platform === 'win32'; 128 129const noop = () => {}; 130 131function getFlags(ipv6Only) { 132 return ipv6Only === true ? TCPConstants.UV_TCP_IPV6ONLY : 0; 133} 134 135function createHandle(fd, is_server) { 136 validateInt32(fd, 'fd', 0); 137 const type = guessHandleType(fd); 138 if (type === 'PIPE') { 139 return new Pipe( 140 is_server ? PipeConstants.SERVER : PipeConstants.SOCKET 141 ); 142 } 143 144 if (type === 'TCP') { 145 return new TCP( 146 is_server ? TCPConstants.SERVER : TCPConstants.SOCKET 147 ); 148 } 149 150 throw new ERR_INVALID_FD_TYPE(type); 151} 152 153 154function getNewAsyncId(handle) { 155 return (!handle || typeof handle.getAsyncId !== 'function') ? 156 newAsyncId() : handle.getAsyncId(); 157} 158 159 160function isPipeName(s) { 161 return typeof s === 'string' && toNumber(s) === false; 162} 163 164/** 165 * Creates a new TCP or IPC server 166 * @param {{ 167 * allowHalfOpen?: boolean; 168 * pauseOnConnect?: boolean; 169 * }} [options] 170 * @param {Function} [connectionListener] 171 * @returns {Server} 172 */ 173 174function createServer(options, connectionListener) { 175 return new Server(options, connectionListener); 176} 177 178 179// Target API: 180// 181// let s = net.connect({port: 80, host: 'google.com'}, function() { 182// ... 183// }); 184// 185// There are various forms: 186// 187// connect(options, [cb]) 188// connect(port, [host], [cb]) 189// connect(path, [cb]); 190// 191function connect(...args) { 192 const normalized = normalizeArgs(args); 193 const options = normalized[0]; 194 debug('createConnection', normalized); 195 const socket = new Socket(options); 196 197 if (options.timeout) { 198 socket.setTimeout(options.timeout); 199 } 200 201 return socket.connect(normalized); 202} 203 204 205// Returns an array [options, cb], where options is an object, 206// cb is either a function or null. 207// Used to normalize arguments of Socket.prototype.connect() and 208// Server.prototype.listen(). Possible combinations of parameters: 209// (options[...][, cb]) 210// (path[...][, cb]) 211// ([port][, host][...][, cb]) 212// For Socket.prototype.connect(), the [...] part is ignored 213// For Server.prototype.listen(), the [...] part is [, backlog] 214// but will not be handled here (handled in listen()) 215function normalizeArgs(args) { 216 let arr; 217 218 if (args.length === 0) { 219 arr = [{}, null]; 220 arr[normalizedArgsSymbol] = true; 221 return arr; 222 } 223 224 const arg0 = args[0]; 225 let options = {}; 226 if (typeof arg0 === 'object' && arg0 !== null) { 227 // (options[...][, cb]) 228 options = arg0; 229 } else if (isPipeName(arg0)) { 230 // (path[...][, cb]) 231 options.path = arg0; 232 } else { 233 // ([port][, host][...][, cb]) 234 options.port = arg0; 235 if (args.length > 1 && typeof args[1] === 'string') { 236 options.host = args[1]; 237 } 238 } 239 240 const cb = args[args.length - 1]; 241 if (typeof cb !== 'function') 242 arr = [options, null]; 243 else 244 arr = [options, cb]; 245 246 arr[normalizedArgsSymbol] = true; 247 return arr; 248} 249 250 251// Called when creating new Socket, or when re-using a closed Socket 252function initSocketHandle(self) { 253 self._undestroy(); 254 self._sockname = null; 255 256 // Handle creation may be deferred to bind() or connect() time. 257 if (self._handle) { 258 self._handle[owner_symbol] = self; 259 self._handle.onread = onStreamRead; 260 self[async_id_symbol] = getNewAsyncId(self._handle); 261 262 let userBuf = self[kBuffer]; 263 if (userBuf) { 264 const bufGen = self[kBufferGen]; 265 if (bufGen !== null) { 266 userBuf = bufGen(); 267 if (!isUint8Array(userBuf)) 268 return; 269 self[kBuffer] = userBuf; 270 } 271 self._handle.useUserBuffer(userBuf); 272 } 273 } 274} 275 276 277const kBytesRead = Symbol('kBytesRead'); 278const kBytesWritten = Symbol('kBytesWritten'); 279const kSetNoDelay = Symbol('kSetNoDelay'); 280 281function Socket(options) { 282 if (!(this instanceof Socket)) return new Socket(options); 283 284 this.connecting = false; 285 // Problem with this is that users can supply their own handle, that may not 286 // have _handle.getAsyncId(). In this case an[async_id_symbol] should 287 // probably be supplied by async_hooks. 288 this[async_id_symbol] = -1; 289 this._hadError = false; 290 this[kHandle] = null; 291 this._parent = null; 292 this._host = null; 293 this[kSetNoDelay] = false; 294 this[kLastWriteQueueSize] = 0; 295 this[kTimeout] = null; 296 this[kBuffer] = null; 297 this[kBufferCb] = null; 298 this[kBufferGen] = null; 299 300 if (typeof options === 'number') 301 options = { fd: options }; // Legacy interface. 302 else 303 options = { ...options }; 304 305 const { allowHalfOpen } = options; 306 307 // Prevent the "no-half-open enforcer" from being inherited from `Duplex`. 308 options.allowHalfOpen = true; 309 // For backwards compat do not emit close on destroy. 310 options.emitClose = false; 311 options.autoDestroy = false; 312 // Handle strings directly. 313 options.decodeStrings = false; 314 stream.Duplex.call(this, options); 315 316 // Default to *not* allowing half open sockets. 317 this.allowHalfOpen = Boolean(allowHalfOpen); 318 319 if (options.handle) { 320 this._handle = options.handle; // private 321 this[async_id_symbol] = getNewAsyncId(this._handle); 322 } else if (options.fd !== undefined) { 323 const { fd } = options; 324 let err; 325 326 // createHandle will throw ERR_INVALID_FD_TYPE if `fd` is not 327 // a valid `PIPE` or `TCP` descriptor 328 this._handle = createHandle(fd, false); 329 330 err = this._handle.open(fd); 331 332 // While difficult to fabricate, in some architectures 333 // `open` may return an error code for valid file descriptors 334 // which cannot be opened. This is difficult to test as most 335 // un-openable fds will throw on `createHandle` 336 if (err) 337 throw errnoException(err, 'open'); 338 339 this[async_id_symbol] = this._handle.getAsyncId(); 340 341 if ((fd === 1 || fd === 2) && 342 (this._handle instanceof Pipe) && isWindows) { 343 // Make stdout and stderr blocking on Windows 344 err = this._handle.setBlocking(true); 345 if (err) 346 throw errnoException(err, 'setBlocking'); 347 348 this._writev = null; 349 this._write = makeSyncWrite(fd); 350 // makeSyncWrite adjusts this value like the original handle would, so 351 // we need to let it do that by turning it into a writable, own 352 // property. 353 ObjectDefineProperty(this._handle, 'bytesWritten', { 354 value: 0, writable: true 355 }); 356 } 357 } 358 359 const onread = options.onread; 360 if (onread !== null && typeof onread === 'object' && 361 (isUint8Array(onread.buffer) || typeof onread.buffer === 'function') && 362 typeof onread.callback === 'function') { 363 if (typeof onread.buffer === 'function') { 364 this[kBuffer] = true; 365 this[kBufferGen] = onread.buffer; 366 } else { 367 this[kBuffer] = onread.buffer; 368 } 369 this[kBufferCb] = onread.callback; 370 } 371 372 // Shut down the socket when we're finished with it. 373 this.on('end', onReadableStreamEnd); 374 375 initSocketHandle(this); 376 377 this._pendingData = null; 378 this._pendingEncoding = ''; 379 380 // If we have a handle, then start the flow of data into the 381 // buffer. if not, then this will happen when we connect 382 if (this._handle && options.readable !== false) { 383 if (options.pauseOnCreate) { 384 // Stop the handle from reading and pause the stream 385 this._handle.reading = false; 386 this._handle.readStop(); 387 this.readableFlowing = false; 388 } else if (!options.manualStart) { 389 this.read(0); 390 } 391 } 392 393 // Reserve properties 394 this.server = null; 395 this._server = null; 396 397 // Used after `.destroy()` 398 this[kBytesRead] = 0; 399 this[kBytesWritten] = 0; 400} 401ObjectSetPrototypeOf(Socket.prototype, stream.Duplex.prototype); 402ObjectSetPrototypeOf(Socket, stream.Duplex); 403 404// Refresh existing timeouts. 405Socket.prototype._unrefTimer = function _unrefTimer() { 406 for (let s = this; s !== null; s = s._parent) { 407 if (s[kTimeout]) 408 s[kTimeout].refresh(); 409 } 410}; 411 412 413// The user has called .end(), and all the bytes have been 414// sent out to the other side. 415Socket.prototype._final = function(cb) { 416 // If still connecting - defer handling `_final` until 'connect' will happen 417 if (this.pending) { 418 debug('_final: not yet connected'); 419 return this.once('connect', () => this._final(cb)); 420 } 421 422 if (!this._handle) 423 return cb(); 424 425 debug('_final: not ended, call shutdown()'); 426 427 const req = new ShutdownWrap(); 428 req.oncomplete = afterShutdown; 429 req.handle = this._handle; 430 req.callback = cb; 431 const err = this._handle.shutdown(req); 432 433 if (err === 1 || err === UV_ENOTCONN) // synchronous finish 434 return afterShutdown.call(req, 0); 435 else if (err !== 0) 436 return this.destroy(errnoException(err, 'shutdown')); 437}; 438 439 440function afterShutdown(status) { 441 const self = this.handle[owner_symbol]; 442 443 debug('afterShutdown destroyed=%j', self.destroyed, 444 self._readableState); 445 446 this.callback(); 447 448 // Callback may come after call to destroy. 449 if (self.destroyed) 450 return; 451 452 if (!self.readable || self.readableEnded) { 453 debug('readableState ended, destroying'); 454 self.destroy(); 455 } 456} 457 458// Provide a better error message when we call end() as a result 459// of the other side sending a FIN. The standard 'write after end' 460// is overly vague, and makes it seem like the user's code is to blame. 461function writeAfterFIN(chunk, encoding, cb) { 462 if (typeof encoding === 'function') { 463 cb = encoding; 464 encoding = null; 465 } 466 467 // eslint-disable-next-line no-restricted-syntax 468 const er = new Error('This socket has been ended by the other party'); 469 er.code = 'EPIPE'; 470 process.nextTick(emitErrorNT, this, er); 471 if (typeof cb === 'function') { 472 defaultTriggerAsyncIdScope(this[async_id_symbol], process.nextTick, cb, er); 473 } 474 475 return false; 476} 477 478Socket.prototype.setTimeout = setStreamTimeout; 479 480 481Socket.prototype._onTimeout = function() { 482 const handle = this._handle; 483 const lastWriteQueueSize = this[kLastWriteQueueSize]; 484 if (lastWriteQueueSize > 0 && handle) { 485 // `lastWriteQueueSize !== writeQueueSize` means there is 486 // an active write in progress, so we suppress the timeout. 487 const { writeQueueSize } = handle; 488 if (lastWriteQueueSize !== writeQueueSize) { 489 this[kLastWriteQueueSize] = writeQueueSize; 490 this._unrefTimer(); 491 return; 492 } 493 } 494 debug('_onTimeout'); 495 this.emit('timeout'); 496}; 497 498 499Socket.prototype.setNoDelay = function(enable) { 500 if (!this._handle) { 501 this.once('connect', 502 enable ? this.setNoDelay : () => this.setNoDelay(enable)); 503 return this; 504 } 505 506 // Backwards compatibility: assume true when `enable` is omitted 507 const newValue = enable === undefined ? true : !!enable; 508 if (this._handle.setNoDelay && newValue !== this[kSetNoDelay]) { 509 this[kSetNoDelay] = newValue; 510 this._handle.setNoDelay(newValue); 511 } 512 513 return this; 514}; 515 516 517Socket.prototype.setKeepAlive = function(setting, msecs) { 518 if (!this._handle) { 519 this.once('connect', () => this.setKeepAlive(setting, msecs)); 520 return this; 521 } 522 523 if (this._handle.setKeepAlive) 524 this._handle.setKeepAlive(setting, ~~(msecs / 1000)); 525 526 return this; 527}; 528 529 530Socket.prototype.address = function() { 531 return this._getsockname(); 532}; 533 534 535ObjectDefineProperty(Socket.prototype, '_connecting', { 536 get: function() { 537 return this.connecting; 538 } 539}); 540 541ObjectDefineProperty(Socket.prototype, 'pending', { 542 get() { 543 return !this._handle || this.connecting; 544 }, 545 configurable: true 546}); 547 548 549ObjectDefineProperty(Socket.prototype, 'readyState', { 550 get: function() { 551 if (this.connecting) { 552 return 'opening'; 553 } else if (this.readable && this.writable) { 554 return 'open'; 555 } else if (this.readable && !this.writable) { 556 return 'readOnly'; 557 } else if (!this.readable && this.writable) { 558 return 'writeOnly'; 559 } 560 return 'closed'; 561 } 562}); 563 564 565ObjectDefineProperty(Socket.prototype, 'bufferSize', { 566 get: function() { 567 if (this._handle) { 568 return this.writableLength; 569 } 570 } 571}); 572 573ObjectDefineProperty(Socket.prototype, kUpdateTimer, { 574 get: function() { 575 return this._unrefTimer; 576 } 577}); 578 579 580function tryReadStart(socket) { 581 // Not already reading, start the flow 582 debug('Socket._handle.readStart'); 583 socket._handle.reading = true; 584 const err = socket._handle.readStart(); 585 if (err) 586 socket.destroy(errnoException(err, 'read')); 587} 588 589// Just call handle.readStart until we have enough in the buffer 590Socket.prototype._read = function(n) { 591 debug('_read'); 592 593 if (this.connecting || !this._handle) { 594 debug('_read wait for connection'); 595 this.once('connect', () => this._read(n)); 596 } else if (!this._handle.reading) { 597 tryReadStart(this); 598 } 599}; 600 601 602Socket.prototype.end = function(data, encoding, callback) { 603 stream.Duplex.prototype.end.call(this, 604 data, encoding, callback); 605 DTRACE_NET_STREAM_END(this); 606 return this; 607}; 608 609 610Socket.prototype.pause = function() { 611 if (this[kBuffer] && !this.connecting && this._handle && 612 this._handle.reading) { 613 this._handle.reading = false; 614 if (!this.destroyed) { 615 const err = this._handle.readStop(); 616 if (err) 617 this.destroy(errnoException(err, 'read')); 618 } 619 } 620 return stream.Duplex.prototype.pause.call(this); 621}; 622 623 624Socket.prototype.resume = function() { 625 if (this[kBuffer] && !this.connecting && this._handle && 626 !this._handle.reading) { 627 tryReadStart(this); 628 } 629 return stream.Duplex.prototype.resume.call(this); 630}; 631 632 633Socket.prototype.read = function(n) { 634 if (this[kBuffer] && !this.connecting && this._handle && 635 !this._handle.reading) { 636 tryReadStart(this); 637 } 638 return stream.Duplex.prototype.read.call(this, n); 639}; 640 641 642// Called when the 'end' event is emitted. 643function onReadableStreamEnd() { 644 if (!this.allowHalfOpen) { 645 this.write = writeAfterFIN; 646 if (this.writable) 647 this.end(); 648 else if (!this.writableLength) 649 this.destroy(); 650 } else if (!this.destroyed && !this.writable && !this.writableLength) 651 this.destroy(); 652} 653 654 655Socket.prototype.destroySoon = function() { 656 if (this.writable) 657 this.end(); 658 659 if (this.writableFinished) 660 this.destroy(); 661 else 662 this.once('finish', this.destroy); 663}; 664 665 666Socket.prototype._destroy = function(exception, cb) { 667 debug('destroy'); 668 669 this.connecting = false; 670 671 for (let s = this; s !== null; s = s._parent) { 672 clearTimeout(s[kTimeout]); 673 } 674 675 debug('close'); 676 if (this._handle) { 677 if (this !== process.stderr) 678 debug('close handle'); 679 const isException = exception ? true : false; 680 // `bytesRead` and `kBytesWritten` should be accessible after `.destroy()` 681 this[kBytesRead] = this._handle.bytesRead; 682 this[kBytesWritten] = this._handle.bytesWritten; 683 684 this._handle.close(() => { 685 debug('emit close'); 686 this.emit('close', isException); 687 }); 688 this._handle.onread = noop; 689 this._handle = null; 690 this._sockname = null; 691 cb(exception); 692 } else { 693 cb(exception); 694 process.nextTick(emitCloseNT, this); 695 } 696 697 if (this._server) { 698 debug('has server'); 699 this._server._connections--; 700 if (this._server._emitCloseIfDrained) { 701 this._server._emitCloseIfDrained(); 702 } 703 } 704}; 705 706Socket.prototype._getpeername = function() { 707 if (!this._peername) { 708 if (!this._handle || !this._handle.getpeername) { 709 return {}; 710 } 711 const out = {}; 712 const err = this._handle.getpeername(out); 713 if (err) return {}; // FIXME(bnoordhuis) Throw? 714 this._peername = out; 715 } 716 return this._peername; 717}; 718 719function protoGetter(name, callback) { 720 ObjectDefineProperty(Socket.prototype, name, { 721 configurable: false, 722 enumerable: true, 723 get: callback 724 }); 725} 726 727protoGetter('bytesRead', function bytesRead() { 728 return this._handle ? this._handle.bytesRead : this[kBytesRead]; 729}); 730 731protoGetter('remoteAddress', function remoteAddress() { 732 return this._getpeername().address; 733}); 734 735protoGetter('remoteFamily', function remoteFamily() { 736 return this._getpeername().family; 737}); 738 739protoGetter('remotePort', function remotePort() { 740 return this._getpeername().port; 741}); 742 743 744Socket.prototype._getsockname = function() { 745 if (!this._handle || !this._handle.getsockname) { 746 return {}; 747 } 748 if (!this._sockname) { 749 const out = {}; 750 const err = this._handle.getsockname(out); 751 if (err) return {}; // FIXME(bnoordhuis) Throw? 752 this._sockname = out; 753 } 754 return this._sockname; 755}; 756 757 758protoGetter('localAddress', function localAddress() { 759 return this._getsockname().address; 760}); 761 762 763protoGetter('localPort', function localPort() { 764 return this._getsockname().port; 765}); 766 767 768Socket.prototype[kAfterAsyncWrite] = function() { 769 this[kLastWriteQueueSize] = 0; 770}; 771 772Socket.prototype._writeGeneric = function(writev, data, encoding, cb) { 773 // If we are still connecting, then buffer this for later. 774 // The Writable logic will buffer up any more writes while 775 // waiting for this one to be done. 776 if (this.connecting) { 777 this._pendingData = data; 778 this._pendingEncoding = encoding; 779 this.once('connect', function connect() { 780 this._writeGeneric(writev, data, encoding, cb); 781 }); 782 return; 783 } 784 this._pendingData = null; 785 this._pendingEncoding = ''; 786 787 if (!this._handle) { 788 cb(new ERR_SOCKET_CLOSED()); 789 return false; 790 } 791 792 this._unrefTimer(); 793 794 let req; 795 if (writev) 796 req = writevGeneric(this, data, cb); 797 else 798 req = writeGeneric(this, data, encoding, cb); 799 if (req.async) 800 this[kLastWriteQueueSize] = req.bytes; 801}; 802 803 804Socket.prototype._writev = function(chunks, cb) { 805 this._writeGeneric(true, chunks, '', cb); 806}; 807 808 809Socket.prototype._write = function(data, encoding, cb) { 810 this._writeGeneric(false, data, encoding, cb); 811}; 812 813 814// Legacy alias. Having this is probably being overly cautious, but it doesn't 815// really hurt anyone either. This can probably be removed safely if desired. 816protoGetter('_bytesDispatched', function _bytesDispatched() { 817 return this._handle ? this._handle.bytesWritten : this[kBytesWritten]; 818}); 819 820protoGetter('bytesWritten', function bytesWritten() { 821 let bytes = this._bytesDispatched; 822 const data = this._pendingData; 823 const encoding = this._pendingEncoding; 824 const writableBuffer = this.writableBuffer; 825 826 if (!writableBuffer) 827 return undefined; 828 829 for (const el of writableBuffer) { 830 bytes += el.chunk instanceof Buffer ? 831 el.chunk.length : 832 Buffer.byteLength(el.chunk, el.encoding); 833 } 834 835 if (ArrayIsArray(data)) { 836 // Was a writev, iterate over chunks to get total length 837 for (let i = 0; i < data.length; i++) { 838 const chunk = data[i]; 839 840 if (data.allBuffers || chunk instanceof Buffer) 841 bytes += chunk.length; 842 else 843 bytes += Buffer.byteLength(chunk.chunk, chunk.encoding); 844 } 845 } else if (data) { 846 // Writes are either a string or a Buffer. 847 if (typeof data !== 'string') 848 bytes += data.length; 849 else 850 bytes += Buffer.byteLength(data, encoding); 851 } 852 853 return bytes; 854}); 855 856 857function checkBindError(err, port, handle) { 858 // EADDRINUSE may not be reported until we call listen() or connect(). 859 // To complicate matters, a failed bind() followed by listen() or connect() 860 // will implicitly bind to a random port. Ergo, check that the socket is 861 // bound to the expected port before calling listen() or connect(). 862 // 863 // FIXME(bnoordhuis) Doesn't work for pipe handles, they don't have a 864 // getsockname() method. Non-issue for now, the cluster module doesn't 865 // really support pipes anyway. 866 if (err === 0 && port > 0 && handle.getsockname) { 867 const out = {}; 868 err = handle.getsockname(out); 869 if (err === 0 && port !== out.port) { 870 debug(`checkBindError, bound to ${out.port} instead of ${port}`); 871 err = UV_EADDRINUSE; 872 } 873 } 874 return err; 875} 876 877 878function internalConnect( 879 self, address, port, addressType, localAddress, localPort, flags) { 880 // TODO return promise from Socket.prototype.connect which 881 // wraps _connectReq. 882 883 assert(self.connecting); 884 885 let err; 886 887 if (localAddress || localPort) { 888 if (addressType === 4) { 889 localAddress = localAddress || DEFAULT_IPV4_ADDR; 890 err = self._handle.bind(localAddress, localPort); 891 } else { // addressType === 6 892 localAddress = localAddress || DEFAULT_IPV6_ADDR; 893 err = self._handle.bind6(localAddress, localPort, flags); 894 } 895 debug('binding to localAddress: %s and localPort: %d (addressType: %d)', 896 localAddress, localPort, addressType); 897 898 err = checkBindError(err, localPort, self._handle); 899 if (err) { 900 const ex = exceptionWithHostPort(err, 'bind', localAddress, localPort); 901 self.destroy(ex); 902 return; 903 } 904 } 905 906 if (addressType === 6 || addressType === 4) { 907 const req = new TCPConnectWrap(); 908 req.oncomplete = afterConnect; 909 req.address = address; 910 req.port = port; 911 req.localAddress = localAddress; 912 req.localPort = localPort; 913 914 if (addressType === 4) 915 err = self._handle.connect(req, address, port); 916 else 917 err = self._handle.connect6(req, address, port); 918 } else { 919 const req = new PipeConnectWrap(); 920 req.address = address; 921 req.oncomplete = afterConnect; 922 923 err = self._handle.connect(req, address, afterConnect); 924 } 925 926 if (err) { 927 const sockname = self._getsockname(); 928 let details; 929 930 if (sockname) { 931 details = sockname.address + ':' + sockname.port; 932 } 933 934 const ex = exceptionWithHostPort(err, 'connect', address, port, details); 935 self.destroy(ex); 936 } 937} 938 939 940Socket.prototype.connect = function(...args) { 941 let normalized; 942 // If passed an array, it's treated as an array of arguments that have 943 // already been normalized (so we don't normalize more than once). This has 944 // been solved before in https://github.com/nodejs/node/pull/12342, but was 945 // reverted as it had unintended side effects. 946 if (ArrayIsArray(args[0]) && args[0][normalizedArgsSymbol]) { 947 normalized = args[0]; 948 } else { 949 normalized = normalizeArgs(args); 950 } 951 const options = normalized[0]; 952 const cb = normalized[1]; 953 954 if (this.write !== Socket.prototype.write) 955 this.write = Socket.prototype.write; 956 957 if (this.destroyed) { 958 this._handle = null; 959 this._peername = null; 960 this._sockname = null; 961 } 962 963 const { path } = options; 964 const pipe = !!path; 965 debug('pipe', pipe, path); 966 967 if (!this._handle) { 968 this._handle = pipe ? 969 new Pipe(PipeConstants.SOCKET) : 970 new TCP(TCPConstants.SOCKET); 971 initSocketHandle(this); 972 } 973 974 if (cb !== null) { 975 this.once('connect', cb); 976 } 977 978 this._unrefTimer(); 979 980 this.connecting = true; 981 this.writable = true; 982 983 if (pipe) { 984 validateString(path, 'options.path'); 985 defaultTriggerAsyncIdScope( 986 this[async_id_symbol], internalConnect, this, path 987 ); 988 } else { 989 lookupAndConnect(this, options); 990 } 991 return this; 992}; 993 994 995function lookupAndConnect(self, options) { 996 const { localAddress, localPort } = options; 997 const host = options.host || 'localhost'; 998 let { port } = options; 999 1000 if (localAddress && !isIP(localAddress)) { 1001 throw new ERR_INVALID_IP_ADDRESS(localAddress); 1002 } 1003 1004 if (localPort && typeof localPort !== 'number') { 1005 throw new ERR_INVALID_ARG_TYPE('options.localPort', 'number', localPort); 1006 } 1007 1008 if (typeof port !== 'undefined') { 1009 if (typeof port !== 'number' && typeof port !== 'string') { 1010 throw new ERR_INVALID_ARG_TYPE('options.port', 1011 ['number', 'string'], port); 1012 } 1013 validatePort(port); 1014 } 1015 port |= 0; 1016 1017 // If host is an IP, skip performing a lookup 1018 const addressType = isIP(host); 1019 if (addressType) { 1020 defaultTriggerAsyncIdScope(self[async_id_symbol], process.nextTick, () => { 1021 if (self.connecting) 1022 defaultTriggerAsyncIdScope( 1023 self[async_id_symbol], 1024 internalConnect, 1025 self, host, port, addressType, localAddress, localPort 1026 ); 1027 }); 1028 return; 1029 } 1030 1031 if (options.lookup && typeof options.lookup !== 'function') 1032 throw new ERR_INVALID_ARG_TYPE('options.lookup', 1033 'Function', options.lookup); 1034 1035 1036 if (dns === undefined) dns = require('dns'); 1037 const dnsopts = { 1038 family: options.family, 1039 hints: options.hints || 0 1040 }; 1041 1042 if (!isWindows && 1043 dnsopts.family !== 4 && 1044 dnsopts.family !== 6 && 1045 dnsopts.hints === 0) { 1046 dnsopts.hints = dns.ADDRCONFIG; 1047 } 1048 1049 debug('connect: find host', host); 1050 debug('connect: dns options', dnsopts); 1051 self._host = host; 1052 const lookup = options.lookup || dns.lookup; 1053 defaultTriggerAsyncIdScope(self[async_id_symbol], function() { 1054 lookup(host, dnsopts, function emitLookup(err, ip, addressType) { 1055 self.emit('lookup', err, ip, addressType, host); 1056 1057 // It's possible we were destroyed while looking this up. 1058 // XXX it would be great if we could cancel the promise returned by 1059 // the look up. 1060 if (!self.connecting) return; 1061 1062 if (err) { 1063 // net.createConnection() creates a net.Socket object and immediately 1064 // calls net.Socket.connect() on it (that's us). There are no event 1065 // listeners registered yet so defer the error event to the next tick. 1066 process.nextTick(connectErrorNT, self, err); 1067 } else if (!isIP(ip)) { 1068 err = new ERR_INVALID_IP_ADDRESS(ip); 1069 process.nextTick(connectErrorNT, self, err); 1070 } else if (addressType !== 4 && addressType !== 6) { 1071 err = new ERR_INVALID_ADDRESS_FAMILY(addressType, 1072 options.host, 1073 options.port); 1074 process.nextTick(connectErrorNT, self, err); 1075 } else { 1076 self._unrefTimer(); 1077 defaultTriggerAsyncIdScope( 1078 self[async_id_symbol], 1079 internalConnect, 1080 self, ip, port, addressType, localAddress, localPort 1081 ); 1082 } 1083 }); 1084 }); 1085} 1086 1087 1088function connectErrorNT(self, err) { 1089 self.destroy(err); 1090} 1091 1092 1093Socket.prototype.ref = function() { 1094 if (!this._handle) { 1095 this.once('connect', this.ref); 1096 return this; 1097 } 1098 1099 if (typeof this._handle.ref === 'function') { 1100 this._handle.ref(); 1101 } 1102 1103 return this; 1104}; 1105 1106 1107Socket.prototype.unref = function() { 1108 if (!this._handle) { 1109 this.once('connect', this.unref); 1110 return this; 1111 } 1112 1113 if (typeof this._handle.unref === 'function') { 1114 this._handle.unref(); 1115 } 1116 1117 return this; 1118}; 1119 1120 1121function afterConnect(status, handle, req, readable, writable) { 1122 const self = handle[owner_symbol]; 1123 1124 // Callback may come after call to destroy 1125 if (self.destroyed) { 1126 return; 1127 } 1128 1129 debug('afterConnect'); 1130 1131 assert(self.connecting); 1132 self.connecting = false; 1133 self._sockname = null; 1134 1135 if (status === 0) { 1136 if (self.readable && !readable) { 1137 self.push(null); 1138 self.read(); 1139 } 1140 if (self.writable && !writable) { 1141 self.end(); 1142 } 1143 self._unrefTimer(); 1144 1145 self.emit('connect'); 1146 self.emit('ready'); 1147 1148 // Start the first read, or get an immediate EOF. 1149 // this doesn't actually consume any bytes, because len=0. 1150 if (readable && !self.isPaused()) 1151 self.read(0); 1152 1153 } else { 1154 self.connecting = false; 1155 let details; 1156 if (req.localAddress && req.localPort) { 1157 details = req.localAddress + ':' + req.localPort; 1158 } 1159 const ex = exceptionWithHostPort(status, 1160 'connect', 1161 req.address, 1162 req.port, 1163 details); 1164 if (details) { 1165 ex.localAddress = req.localAddress; 1166 ex.localPort = req.localPort; 1167 } 1168 self.destroy(ex); 1169 } 1170} 1171 1172 1173function Server(options, connectionListener) { 1174 if (!(this instanceof Server)) 1175 return new Server(options, connectionListener); 1176 1177 EventEmitter.call(this); 1178 1179 if (typeof options === 'function') { 1180 connectionListener = options; 1181 options = {}; 1182 this.on('connection', connectionListener); 1183 } else if (options == null || typeof options === 'object') { 1184 options = { ...options }; 1185 1186 if (typeof connectionListener === 'function') { 1187 this.on('connection', connectionListener); 1188 } 1189 } else { 1190 throw new ERR_INVALID_ARG_TYPE('options', 'Object', options); 1191 } 1192 1193 this._connections = 0; 1194 1195 ObjectDefineProperty(this, 'connections', { 1196 get: deprecate(() => { 1197 1198 if (this._usingWorkers) { 1199 return null; 1200 } 1201 return this._connections; 1202 }, 'Server.connections property is deprecated. ' + 1203 'Use Server.getConnections method instead.', 'DEP0020'), 1204 set: deprecate((val) => (this._connections = val), 1205 'Server.connections property is deprecated.', 1206 'DEP0020'), 1207 configurable: true, enumerable: false 1208 }); 1209 1210 this[async_id_symbol] = -1; 1211 this._handle = null; 1212 this._usingWorkers = false; 1213 this._workers = []; 1214 this._unref = false; 1215 1216 this.allowHalfOpen = options.allowHalfOpen || false; 1217 this.pauseOnConnect = !!options.pauseOnConnect; 1218} 1219ObjectSetPrototypeOf(Server.prototype, EventEmitter.prototype); 1220ObjectSetPrototypeOf(Server, EventEmitter); 1221 1222 1223function toNumber(x) { return (x = Number(x)) >= 0 ? x : false; } 1224 1225// Returns handle if it can be created, or error code if it can't 1226function createServerHandle(address, port, addressType, fd, flags) { 1227 let err = 0; 1228 // Assign handle in listen, and clean up if bind or listen fails 1229 let handle; 1230 1231 let isTCP = false; 1232 if (typeof fd === 'number' && fd >= 0) { 1233 try { 1234 handle = createHandle(fd, true); 1235 } catch (e) { 1236 // Not a fd we can listen on. This will trigger an error. 1237 debug('listen invalid fd=%d:', fd, e.message); 1238 return UV_EINVAL; 1239 } 1240 1241 err = handle.open(fd); 1242 if (err) 1243 return err; 1244 1245 assert(!address && !port); 1246 } else if (port === -1 && addressType === -1) { 1247 handle = new Pipe(PipeConstants.SERVER); 1248 if (isWindows) { 1249 const instances = NumberParseInt(process.env.NODE_PENDING_PIPE_INSTANCES); 1250 if (!NumberIsNaN(instances)) { 1251 handle.setPendingInstances(instances); 1252 } 1253 } 1254 } else { 1255 handle = new TCP(TCPConstants.SERVER); 1256 isTCP = true; 1257 } 1258 1259 if (address || port || isTCP) { 1260 debug('bind to', address || 'any'); 1261 if (!address) { 1262 // Try binding to ipv6 first 1263 err = handle.bind6(DEFAULT_IPV6_ADDR, port, flags); 1264 if (err) { 1265 handle.close(); 1266 // Fallback to ipv4 1267 return createServerHandle(DEFAULT_IPV4_ADDR, port); 1268 } 1269 } else if (addressType === 6) { 1270 err = handle.bind6(address, port, flags); 1271 } else { 1272 err = handle.bind(address, port); 1273 } 1274 } 1275 1276 if (err) { 1277 handle.close(); 1278 return err; 1279 } 1280 1281 return handle; 1282} 1283 1284function setupListenHandle(address, port, addressType, backlog, fd, flags) { 1285 debug('setupListenHandle', address, port, addressType, backlog, fd); 1286 1287 // If there is not yet a handle, we need to create one and bind. 1288 // In the case of a server sent via IPC, we don't need to do this. 1289 if (this._handle) { 1290 debug('setupListenHandle: have a handle already'); 1291 } else { 1292 debug('setupListenHandle: create a handle'); 1293 1294 let rval = null; 1295 1296 // Try to bind to the unspecified IPv6 address, see if IPv6 is available 1297 if (!address && typeof fd !== 'number') { 1298 rval = createServerHandle(DEFAULT_IPV6_ADDR, port, 6, fd, flags); 1299 1300 if (typeof rval === 'number') { 1301 rval = null; 1302 address = DEFAULT_IPV4_ADDR; 1303 addressType = 4; 1304 } else { 1305 address = DEFAULT_IPV6_ADDR; 1306 addressType = 6; 1307 } 1308 } 1309 1310 if (rval === null) 1311 rval = createServerHandle(address, port, addressType, fd, flags); 1312 1313 if (typeof rval === 'number') { 1314 const error = uvExceptionWithHostPort(rval, 'listen', address, port); 1315 process.nextTick(emitErrorNT, this, error); 1316 return; 1317 } 1318 this._handle = rval; 1319 } 1320 1321 this[async_id_symbol] = getNewAsyncId(this._handle); 1322 this._handle.onconnection = onconnection; 1323 this._handle[owner_symbol] = this; 1324 1325 // Use a backlog of 512 entries. We pass 511 to the listen() call because 1326 // the kernel does: backlogsize = roundup_pow_of_two(backlogsize + 1); 1327 // which will thus give us a backlog of 512 entries. 1328 const err = this._handle.listen(backlog || 511); 1329 1330 if (err) { 1331 const ex = uvExceptionWithHostPort(err, 'listen', address, port); 1332 this._handle.close(); 1333 this._handle = null; 1334 defaultTriggerAsyncIdScope(this[async_id_symbol], 1335 process.nextTick, 1336 emitErrorNT, 1337 this, 1338 ex); 1339 return; 1340 } 1341 1342 // Generate connection key, this should be unique to the connection 1343 this._connectionKey = addressType + ':' + address + ':' + port; 1344 1345 // Unref the handle if the server was unref'ed prior to listening 1346 if (this._unref) 1347 this.unref(); 1348 1349 defaultTriggerAsyncIdScope(this[async_id_symbol], 1350 process.nextTick, 1351 emitListeningNT, 1352 this); 1353} 1354 1355Server.prototype._listen2 = setupListenHandle; // legacy alias 1356 1357function emitErrorNT(self, err) { 1358 self.emit('error', err); 1359} 1360 1361 1362function emitListeningNT(self) { 1363 // Ensure handle hasn't closed 1364 if (self._handle) 1365 self.emit('listening'); 1366} 1367 1368 1369function listenInCluster(server, address, port, addressType, 1370 backlog, fd, exclusive, flags) { 1371 exclusive = !!exclusive; 1372 1373 if (cluster === undefined) cluster = require('cluster'); 1374 1375 if (cluster.isMaster || exclusive) { 1376 // Will create a new handle 1377 // _listen2 sets up the listened handle, it is still named like this 1378 // to avoid breaking code that wraps this method 1379 server._listen2(address, port, addressType, backlog, fd, flags); 1380 return; 1381 } 1382 1383 const serverQuery = { 1384 address: address, 1385 port: port, 1386 addressType: addressType, 1387 fd: fd, 1388 flags, 1389 }; 1390 1391 // Get the master's server handle, and listen on it 1392 cluster._getServer(server, serverQuery, listenOnMasterHandle); 1393 1394 function listenOnMasterHandle(err, handle) { 1395 err = checkBindError(err, port, handle); 1396 1397 if (err) { 1398 const ex = exceptionWithHostPort(err, 'bind', address, port); 1399 return server.emit('error', ex); 1400 } 1401 1402 // Reuse master's server handle 1403 server._handle = handle; 1404 // _listen2 sets up the listened handle, it is still named like this 1405 // to avoid breaking code that wraps this method 1406 server._listen2(address, port, addressType, backlog, fd, flags); 1407 } 1408} 1409 1410 1411Server.prototype.listen = function(...args) { 1412 const normalized = normalizeArgs(args); 1413 let options = normalized[0]; 1414 const cb = normalized[1]; 1415 1416 if (this._handle) { 1417 throw new ERR_SERVER_ALREADY_LISTEN(); 1418 } 1419 1420 if (cb !== null) { 1421 this.once('listening', cb); 1422 } 1423 const backlogFromArgs = 1424 // (handle, backlog) or (path, backlog) or (port, backlog) 1425 toNumber(args.length > 1 && args[1]) || 1426 toNumber(args.length > 2 && args[2]); // (port, host, backlog) 1427 1428 options = options._handle || options.handle || options; 1429 const flags = getFlags(options.ipv6Only); 1430 // (handle[, backlog][, cb]) where handle is an object with a handle 1431 if (options instanceof TCP) { 1432 this._handle = options; 1433 this[async_id_symbol] = this._handle.getAsyncId(); 1434 listenInCluster(this, null, -1, -1, backlogFromArgs); 1435 return this; 1436 } 1437 // (handle[, backlog][, cb]) where handle is an object with a fd 1438 if (typeof options.fd === 'number' && options.fd >= 0) { 1439 listenInCluster(this, null, null, null, backlogFromArgs, options.fd); 1440 return this; 1441 } 1442 1443 // ([port][, host][, backlog][, cb]) where port is omitted, 1444 // that is, listen(), listen(null), listen(cb), or listen(null, cb) 1445 // or (options[, cb]) where options.port is explicitly set as undefined or 1446 // null, bind to an arbitrary unused port 1447 if (args.length === 0 || typeof args[0] === 'function' || 1448 (typeof options.port === 'undefined' && 'port' in options) || 1449 options.port === null) { 1450 options.port = 0; 1451 } 1452 // ([port][, host][, backlog][, cb]) where port is specified 1453 // or (options[, cb]) where options.port is specified 1454 // or if options.port is normalized as 0 before 1455 let backlog; 1456 if (typeof options.port === 'number' || typeof options.port === 'string') { 1457 validatePort(options.port, 'options.port'); 1458 backlog = options.backlog || backlogFromArgs; 1459 // start TCP server listening on host:port 1460 if (options.host) { 1461 lookupAndListen(this, options.port | 0, options.host, backlog, 1462 options.exclusive, flags); 1463 } else { // Undefined host, listens on unspecified address 1464 // Default addressType 4 will be used to search for master server 1465 listenInCluster(this, null, options.port | 0, 4, 1466 backlog, undefined, options.exclusive); 1467 } 1468 return this; 1469 } 1470 1471 // (path[, backlog][, cb]) or (options[, cb]) 1472 // where path or options.path is a UNIX domain socket or Windows pipe 1473 if (options.path && isPipeName(options.path)) { 1474 const pipeName = this._pipeName = options.path; 1475 backlog = options.backlog || backlogFromArgs; 1476 listenInCluster(this, pipeName, -1, -1, 1477 backlog, undefined, options.exclusive); 1478 1479 if (!this._handle) { 1480 // Failed and an error shall be emitted in the next tick. 1481 // Therefore, we directly return. 1482 return this; 1483 } 1484 1485 let mode = 0; 1486 if (options.readableAll === true) 1487 mode |= PipeConstants.UV_READABLE; 1488 if (options.writableAll === true) 1489 mode |= PipeConstants.UV_WRITABLE; 1490 if (mode !== 0) { 1491 const err = this._handle.fchmod(mode); 1492 if (err) { 1493 this._handle.close(); 1494 this._handle = null; 1495 throw errnoException(err, 'uv_pipe_chmod'); 1496 } 1497 } 1498 return this; 1499 } 1500 1501 if (!(('port' in options) || ('path' in options))) { 1502 throw new ERR_INVALID_ARG_VALUE('options', options, 1503 'must have the property "port" or "path"'); 1504 } 1505 1506 throw new ERR_INVALID_OPT_VALUE('options', options); 1507}; 1508 1509function lookupAndListen(self, port, address, backlog, exclusive, flags) { 1510 if (dns === undefined) dns = require('dns'); 1511 dns.lookup(address, function doListen(err, ip, addressType) { 1512 if (err) { 1513 self.emit('error', err); 1514 } else { 1515 addressType = ip ? addressType : 4; 1516 listenInCluster(self, ip, port, addressType, 1517 backlog, undefined, exclusive, flags); 1518 } 1519 }); 1520} 1521 1522ObjectDefineProperty(Server.prototype, 'listening', { 1523 get: function() { 1524 return !!this._handle; 1525 }, 1526 configurable: true, 1527 enumerable: true 1528}); 1529 1530Server.prototype.address = function() { 1531 if (this._handle && this._handle.getsockname) { 1532 const out = {}; 1533 const err = this._handle.getsockname(out); 1534 if (err) { 1535 throw errnoException(err, 'address'); 1536 } 1537 return out; 1538 } else if (this._pipeName) { 1539 return this._pipeName; 1540 } 1541 return null; 1542}; 1543 1544function onconnection(err, clientHandle) { 1545 const handle = this; 1546 const self = handle[owner_symbol]; 1547 1548 debug('onconnection'); 1549 1550 if (err) { 1551 self.emit('error', errnoException(err, 'accept')); 1552 return; 1553 } 1554 1555 if (self.maxConnections && self._connections >= self.maxConnections) { 1556 clientHandle.close(); 1557 return; 1558 } 1559 1560 const socket = new Socket({ 1561 handle: clientHandle, 1562 allowHalfOpen: self.allowHalfOpen, 1563 pauseOnCreate: self.pauseOnConnect, 1564 readable: true, 1565 writable: true 1566 }); 1567 1568 self._connections++; 1569 socket.server = self; 1570 socket._server = self; 1571 1572 DTRACE_NET_SERVER_CONNECTION(socket); 1573 self.emit('connection', socket); 1574} 1575 1576/** 1577 * Gets the number of concurrent connections on the server 1578 * @param {Function} cb 1579 * @returns {Server} 1580 */ 1581 1582Server.prototype.getConnections = function(cb) { 1583 const self = this; 1584 1585 function end(err, connections) { 1586 defaultTriggerAsyncIdScope(self[async_id_symbol], 1587 process.nextTick, 1588 cb, 1589 err, 1590 connections); 1591 } 1592 1593 if (!this._usingWorkers) { 1594 end(null, this._connections); 1595 return this; 1596 } 1597 1598 // Poll workers 1599 let left = this._workers.length; 1600 let total = this._connections; 1601 1602 function oncount(err, count) { 1603 if (err) { 1604 left = -1; 1605 return end(err); 1606 } 1607 1608 total += count; 1609 if (--left === 0) return end(null, total); 1610 } 1611 1612 for (let n = 0; n < this._workers.length; n++) { 1613 this._workers[n].getConnections(oncount); 1614 } 1615 1616 return this; 1617}; 1618 1619 1620Server.prototype.close = function(cb) { 1621 if (typeof cb === 'function') { 1622 if (!this._handle) { 1623 this.once('close', function close() { 1624 cb(new ERR_SERVER_NOT_RUNNING()); 1625 }); 1626 } else { 1627 this.once('close', cb); 1628 } 1629 } 1630 1631 if (this._handle) { 1632 this._handle.close(); 1633 this._handle = null; 1634 } 1635 1636 if (this._usingWorkers) { 1637 let left = this._workers.length; 1638 const onWorkerClose = () => { 1639 if (--left !== 0) return; 1640 1641 this._connections = 0; 1642 this._emitCloseIfDrained(); 1643 }; 1644 1645 // Increment connections to be sure that, even if all sockets will be closed 1646 // during polling of workers, `close` event will be emitted only once. 1647 this._connections++; 1648 1649 // Poll workers 1650 for (let n = 0; n < this._workers.length; n++) 1651 this._workers[n].close(onWorkerClose); 1652 } else { 1653 this._emitCloseIfDrained(); 1654 } 1655 1656 return this; 1657}; 1658 1659Server.prototype._emitCloseIfDrained = function() { 1660 debug('SERVER _emitCloseIfDrained'); 1661 1662 if (this._handle || this._connections) { 1663 debug('SERVER handle? %j connections? %d', 1664 !!this._handle, this._connections); 1665 return; 1666 } 1667 1668 defaultTriggerAsyncIdScope(this[async_id_symbol], 1669 process.nextTick, 1670 emitCloseNT, 1671 this); 1672}; 1673 1674 1675function emitCloseNT(self) { 1676 debug('SERVER: emit close'); 1677 self.emit('close'); 1678} 1679 1680 1681Server.prototype[EventEmitter.captureRejectionSymbol] = function( 1682 err, event, sock) { 1683 1684 switch (event) { 1685 case 'connection': 1686 sock.destroy(err); 1687 break; 1688 default: 1689 this.emit('error', err); 1690 } 1691}; 1692 1693 1694// Legacy alias on the C++ wrapper object. This is not public API, so we may 1695// want to runtime-deprecate it at some point. There's no hurry, though. 1696ObjectDefineProperty(TCP.prototype, 'owner', { 1697 get() { return this[owner_symbol]; }, 1698 set(v) { return this[owner_symbol] = v; } 1699}); 1700 1701ObjectDefineProperty(Socket.prototype, '_handle', { 1702 get() { return this[kHandle]; }, 1703 set(v) { return this[kHandle] = v; } 1704}); 1705 1706Server.prototype._setupWorker = function(socketList) { 1707 this._usingWorkers = true; 1708 this._workers.push(socketList); 1709 socketList.once('exit', (socketList) => { 1710 const index = ArrayPrototypeIndexOf(this._workers, socketList); 1711 this._workers.splice(index, 1); 1712 }); 1713}; 1714 1715Server.prototype.ref = function() { 1716 this._unref = false; 1717 1718 if (this._handle) 1719 this._handle.ref(); 1720 1721 return this; 1722}; 1723 1724Server.prototype.unref = function() { 1725 this._unref = true; 1726 1727 if (this._handle) 1728 this._handle.unref(); 1729 1730 return this; 1731}; 1732 1733let _setSimultaneousAccepts; 1734let warnSimultaneousAccepts = true; 1735 1736if (isWindows) { 1737 let simultaneousAccepts; 1738 1739 _setSimultaneousAccepts = function(handle) { 1740 if (warnSimultaneousAccepts) { 1741 process.emitWarning( 1742 'net._setSimultaneousAccepts() is deprecated and will be removed.', 1743 'DeprecationWarning', 'DEP0121'); 1744 warnSimultaneousAccepts = false; 1745 } 1746 if (handle === undefined) { 1747 return; 1748 } 1749 1750 if (simultaneousAccepts === undefined) { 1751 simultaneousAccepts = (process.env.NODE_MANY_ACCEPTS && 1752 process.env.NODE_MANY_ACCEPTS !== '0'); 1753 } 1754 1755 if (handle._simultaneousAccepts !== simultaneousAccepts) { 1756 handle.setSimultaneousAccepts(!!simultaneousAccepts); 1757 handle._simultaneousAccepts = simultaneousAccepts; 1758 } 1759 }; 1760} else { 1761 _setSimultaneousAccepts = function() { 1762 if (warnSimultaneousAccepts) { 1763 process.emitWarning( 1764 'net._setSimultaneousAccepts() is deprecated and will be removed.', 1765 'DeprecationWarning', 'DEP0121'); 1766 warnSimultaneousAccepts = false; 1767 } 1768 }; 1769} 1770 1771module.exports = { 1772 _createServerHandle: createServerHandle, 1773 _normalizeArgs: normalizeArgs, 1774 _setSimultaneousAccepts, 1775 get BlockList() { 1776 BlockList = BlockList ?? require('internal/blocklist').BlockList; 1777 return BlockList; 1778 }, 1779 get SocketAddress() { 1780 SocketAddress = 1781 SocketAddress ?? require('internal/socketaddress').SocketAddress; 1782 return SocketAddress; 1783 }, 1784 connect, 1785 createConnection: connect, 1786 createServer, 1787 isIP: isIP, 1788 isIPv4: isIPv4, 1789 isIPv6: isIPv6, 1790 Server, 1791 Socket, 1792 Stream: Socket, // Legacy naming 1793}; 1794