/lib/net.js
JavaScript | 1219 lines | 850 code | 220 blank | 149 comment | 222 complexity | 3faa68f12c163262f3166d24c0343b76 MD5 | raw file
1var sys = require("sys"); 2var fs = require("fs"); 3var events = require("events"); 4var dns = require('dns'); 5 6var kMinPoolSpace = 128; 7var kPoolSize = 40*1024; 8 9var debugLevel = parseInt(process.env.NODE_DEBUG, 16); 10function debug () { 11 if (debugLevel & 0x2) sys.error.apply(this, arguments); 12} 13var binding = process.binding('net'); 14 15// Note about Buffer interface: 16// I'm attempting to do the simplest possible interface to abstracting raw 17// memory allocation. This might turn out to be too simple - it seems that 18// I always use a buffer.used member to keep track of how much I've filled. 19// Perhaps giving the Buffer a file-like interface with a head (which would 20// represent buffer.used) that can be seeked around would be easier. I'm not 21// yet convinced that every use-case can be fit into that abstraction, so 22// waiting to implement it until I get more experience with this. 23var Buffer = require('buffer').Buffer; 24var FreeList = require('freelist').FreeList; 25 26var IOWatcher = process.IOWatcher; 27var assert = process.assert; 28 29var socket = binding.socket; 30var bind = binding.bind; 31var connect = binding.connect; 32var listen = binding.listen; 33var accept = binding.accept; 34var close = binding.close; 35var shutdown = binding.shutdown; 36var read = binding.read; 37var write = binding.write; 38var toRead = binding.toRead; 39var setNoDelay = binding.setNoDelay; 40var setKeepAlive= binding.setKeepAlive; 41var socketError = binding.socketError; 42var getsockname = binding.getsockname; 43var errnoException = binding.errnoException; 44var sendMsg = binding.sendMsg; 45var recvMsg = binding.recvMsg; 46var EINPROGRESS = binding.EINPROGRESS; 47var ENOENT = binding.ENOENT; 48var EMFILE = binding.EMFILE; 49var END_OF_FILE = 42; 50 51// Do we have openssl crypto? 52try { 53 var SecureContext = process.binding('crypto').SecureContext; 54 var SecureStream = process.binding('crypto').SecureStream; 55 var have_crypto = true; 56} catch (e) { 57 var have_crypto = false; 58} 59 60// IDLE TIMEOUTS 61// 62// Because often many sockets will have the same idle timeout we will not 63// use one timeout watcher per socket. It is too much overhead. Instead 64// we'll use a single watcher for all sockets with the same timeout value 65// and a linked list. This technique is described in the libev manual: 66// http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#Be_smart_about_timeouts 67 68 69var timeout = new (function () { 70 // Object containing all lists, timers 71 // key = time in milliseconds 72 // value = list 73 var lists = {}; 74 75 // show the most idle socket 76 function peek (list) { 77 if (list._idlePrev == list) return null; 78 return list._idlePrev; 79 } 80 81 82 // remove the most idle socket from the list 83 function shift (list) { 84 var first = list._idlePrev; 85 remove(first); 86 return first; 87 } 88 89 90 // remove a socket from its list 91 function remove (socket) { 92 socket._idleNext._idlePrev = socket._idlePrev; 93 socket._idlePrev._idleNext = socket._idleNext; 94 } 95 96 97 // remove a socket from its list and place at the end. 98 function append (list, socket) { 99 remove(socket); 100 socket._idleNext = list._idleNext; 101 socket._idleNext._idlePrev = socket; 102 socket._idlePrev = list 103 list._idleNext = socket; 104 } 105 106 107 function normalize (msecs) { 108 if (!msecs || msecs <= 0) return 0; 109 // round up to one sec 110 if (msecs < 1000) return 1000; 111 // round down to nearest second. 112 return msecs - (msecs % 1000); 113 } 114 115 // the main function - creates lists on demand and the watchers associated 116 // with them. 117 function insert (socket, msecs) { 118 socket._idleStart = new Date(); 119 socket._idleTimeout = msecs; 120 121 if (!msecs) return; 122 123 var list; 124 125 if (lists[msecs]) { 126 list = lists[msecs]; 127 } else { 128 list = new process.Timer(); 129 list._idleNext = list; 130 list._idlePrev = list; 131 132 lists[msecs] = list; 133 134 list.callback = function () { 135 debug('timeout callback ' + msecs); 136 // TODO - don't stop and start the watcher all the time. 137 // just set its repeat 138 var now = new Date(); 139 debug("now: " + now); 140 var first; 141 while (first = peek(list)) { 142 var diff = now - first._idleStart; 143 if (diff < msecs) { 144 list.again(msecs - diff); 145 debug(msecs + ' list wait because diff is ' + diff); 146 return; 147 } else { 148 remove(first); 149 assert(first != peek(list)); 150 first.emit('timeout'); 151 } 152 } 153 debug(msecs + ' list empty'); 154 assert(list._idleNext == list); // list is empty 155 list.stop(); 156 }; 157 } 158 159 if (list._idleNext == list) { 160 // if empty (re)start the timer 161 list.again(msecs); 162 } 163 164 append(list, socket); 165 assert(list._idleNext != list); // list is not empty 166 } 167 168 169 var unenroll = this.unenroll = function (socket) { 170 if (socket._idleNext) { 171 socket._idleNext._idlePrev = socket._idlePrev; 172 socket._idlePrev._idleNext = socket._idleNext; 173 174 var list = lists[socket._idleTimeout]; 175 // if empty then stop the watcher 176 //debug('unenroll'); 177 if (list && list._idlePrev == list) { 178 //debug('unenroll: list empty'); 179 list.stop(); 180 } 181 } 182 }; 183 184 185 // Does not start the time, just sets up the members needed. 186 this.enroll = function (socket, msecs) { 187 // if this socket was already in a list somewhere 188 // then we should unenroll it from that 189 if (socket._idleNext) unenroll(socket); 190 191 socket._idleTimeout = msecs; 192 socket._idleNext = socket; 193 socket._idlePrev = socket; 194 }; 195 196 // call this whenever the socket is active (not idle) 197 // it will reset its timeout. 198 this.active = function (socket) { 199 var msecs = socket._idleTimeout; 200 if (msecs) { 201 var list = lists[msecs]; 202 if (socket._idleNext == socket) { 203 insert(socket, msecs); 204 } else { 205 // inline append 206 socket._idleStart = new Date(); 207 socket._idleNext._idlePrev = socket._idlePrev; 208 socket._idlePrev._idleNext = socket._idleNext; 209 socket._idleNext = list._idleNext; 210 socket._idleNext._idlePrev = socket; 211 socket._idlePrev = list 212 list._idleNext = socket; 213 } 214 } 215 }; 216})(); 217 218var ioWatchers = new FreeList("iowatcher", 100, function () { 219 return new IOWatcher(); 220}); 221 222 223// waitingForFDs stores servers which have experienced EMFILE. 224// When a file descriptor becomes available through closeFD() 225// a server from waitingForFDs is started. 226 227var waitingForFDs = []; 228 229function closeFD(fd) { 230 close(fd); 231 232 // Try to recover from EMFILE 233 234 var server, serverFD; 235 while (server = waitingForFDs.shift()) { 236 serverFD = parseInt(server.fd); 237 if (serverFD && serverFD > 0) { 238 server.watcher.set(serverFD, true, false); 239 server.watcher.start(); 240 return; 241 } 242 } 243} 244 245 246// Allocated on demand. 247var pool = null; 248function allocNewPool () { 249 pool = new Buffer(kPoolSize); 250 pool.used = 0; 251} 252 253var securePool = null; 254function allocNewSecurePool () { 255 securePool = new Buffer(40*1024); 256} 257 258var emptyBuffer = null; 259function allocEmptyBuffer () { 260 emptyBuffer = new Buffer(1); 261 emptyBuffer.sent = 0; 262 emptyBuffer.length = 0; 263} 264 265function _doFlush () { 266 var socket = this.socket; 267 // Stream becomes writeable on connect() but don't flush if there's 268 // nothing actually to write 269 if (socket.flush()) { 270 if (socket._events && socket._events['drain']) socket.emit("drain"); 271 if (socket.ondrain) socket.ondrain(); // Optimization 272 } 273} 274 275function setImplmentationMethods (self) { 276 function noData(buf, off, len) { 277 return !buf || 278 (off != undefined && off >= buf.length) || 279 (len == 0); 280 }; 281 282 if (self.type == 'unix') { 283 self._writeImpl = function(buf, off, len, fd, flags) { 284 // Detect and disallow zero-byte writes wth an attached file 285 // descriptor. This is an implementation limitation of sendmsg(2). 286 if (fd && noData(buf, off, len)) { 287 throw new Error('File descriptors can only be written with data'); 288 } 289 290 return sendMsg(self.fd, buf, off, len, fd, flags); 291 }; 292 293 self._readImpl = function(buf, off, len, calledByIOWatcher) { 294 var bytesRead = recvMsg(self.fd, buf, off, len); 295 296 // Do not emit this in the same stack, otherwise we risk corrupting our 297 // buffer pool which is full of read data, but has not had had its 298 // pointers updated just yet. 299 // 300 // Save off recvMsg.fd in a closure so that, when we emit it later, we're 301 // emitting the same value that we see now. Otherwise, we can end up 302 // calling emit() after recvMsg() has been called again and end up 303 // emitting null (or another FD). 304 if (recvMsg.fd !== null) { 305 (function () { 306 var fd = recvMsg.fd; 307 process.nextTick(function() { 308 self.emit('fd', fd); 309 }); 310 })(); 311 } 312 313 return bytesRead; 314 }; 315 } else { 316 self._writeImpl = function(buf, off, len, fd, flags) { 317 // XXX: TLS support requires that 0-byte writes get processed 318 // by the kernel for some reason. Otherwise, we'd just 319 // fast-path return here. 320 321 // Drop 'fd' and 'flags' as these are not supported by the write(2) 322 // system call 323 return write(self.fd, buf, off, len); 324 }; 325 326 self._readImpl = function(buf, off, len, calledByIOWatcher) { 327 return read(self.fd, buf, off, len); 328 }; 329 } 330 331 self._shutdownImpl = function() { 332 shutdown(self.fd, 'write') 333 }; 334 335 if (self.secure) { 336 var oldWrite = self._writeImpl; 337 self._writeImpl = function(buf, off, len, fd, flags) { 338 assert(buf); 339 assert(self.secure); 340 341 var bytesWritten = self.secureStream.writeInject(buf, off, len); 342 343 if (!securePool) { 344 allocNewSecurePool(); 345 } 346 347 var secureLen = self.secureStream.writeExtract( 348 securePool, 0, securePool.length 349 ); 350 351 if (secureLen == -1) { 352 // Check our read again for secure handshake 353 self._readWatcher.callback(); 354 } else { 355 oldWrite(securePool, 0, secureLen, fd, flags); 356 } 357 358 if (!self.secureEstablished && self.secureStream.isInitFinished()) { 359 self.secureEstablished = true; 360 361 if (self._events && self._events['secure']) { 362 self.emit('secure'); 363 } 364 } 365 366 return bytesWritten; 367 }; 368 369 var oldRead = self._readImpl; 370 self._readImpl = function(buf, off, len, calledByIOWatcher) { 371 assert(self.secure); 372 373 var bytesRead = 0; 374 var secureBytesRead = null; 375 376 if (!securePool) { 377 allocNewSecurePool(); 378 } 379 380 if (calledByIOWatcher) { 381 secureBytesRead = oldRead(securePool, 0, securePool.length); 382 self.secureStream.readInject(securePool, 0, secureBytesRead); 383 } 384 385 var chunkBytes; 386 do { 387 chunkBytes = self.secureStream.readExtract( 388 pool, 389 pool.used + bytesRead, 390 pool.length - pool.used - bytesRead 391 ); 392 393 bytesRead += chunkBytes; 394 } while ((chunkBytes > 0) && (pool.used + bytesRead < pool.length)); 395 396 if (bytesRead == 0 && !calledByIOWatcher) { 397 return -1; 398 } 399 400 if (self.secureStream.readPending()) { 401 process.nextTick(function () { 402 if(self._readWatcher) 403 self._readWatcher.callback(); 404 }); 405 } 406 407 if (!self.secureEstablished) { 408 if (self.secureStream.isInitFinished()) { 409 self.secureEstablished = true; 410 if (self._events && self._events['secure']) { 411 self.emit('secure'); 412 } 413 } 414 } 415 416 if (calledByIOWatcher && secureBytesRead === null && !self.server) { 417 // Client needs to write as part of handshake 418 self._writeWatcher.start(); 419 return -1; 420 } 421 422 if (bytesRead == 0 && secureBytesRead > 0) { 423 // Deal with SSL handshake 424 if (self.server) { 425 self._checkForSecureHandshake(); 426 } else { 427 if (self.secureEstablised) { 428 self.flush(); 429 } else { 430 self._checkForSecureHandshake(); 431 } 432 } 433 434 return -1; 435 } 436 437 return bytesRead; 438 }; 439 440 var oldShutdown = self._shutdownImpl; 441 self._shutdownImpl = function() { 442 self.secureStream.shutdown(); 443 444 if (!securePool) { 445 allocNewSecurePool(); 446 } 447 448 var secureLen = self.secureStream.writeExtract( 449 securePool, 0, securePool.length 450 ); 451 452 try { 453 oldWrite(securePool, 0, secureLen); 454 } catch (e) { } 455 456 oldShutdown(); 457 }; 458 } 459}; 460 461function initStream (self) { 462 self._readWatcher = ioWatchers.alloc(); 463 self._readWatcher.callback = function () { 464 // If this is the first recv (pool doesn't exist) or we've used up 465 // most of the pool, allocate a new one. 466 if (!pool || pool.length - pool.used < kMinPoolSpace) { 467 // discard the old pool. Can't add to the free list because 468 // users might have refernces to slices on it. 469 pool = null; 470 allocNewPool(); 471 } 472 473 //debug('pool.used ' + pool.used); 474 var bytesRead; 475 476 try { 477 bytesRead = self._readImpl(pool, pool.used, pool.length - pool.used, (arguments.length > 0)); 478 } catch (e) { 479 self.destroy(e); 480 return; 481 } 482 483 // Note that some _readImpl() implementations return -1 bytes 484 // read as an indication not to do any processing on the result 485 // (but not an error). 486 487 if (bytesRead === 0) { 488 self.readable = false; 489 self._readWatcher.stop(); 490 491 if (!self.writable) self.destroy(); 492 // Note: 'close' not emitted until nextTick. 493 494 if (self._events && self._events['end']) self.emit('end'); 495 if (self.onend) self.onend(); 496 } else if (bytesRead > 0) { 497 498 timeout.active(self); 499 500 var start = pool.used; 501 var end = pool.used + bytesRead; 502 pool.used += bytesRead; 503 504 if (self._decoder) { 505 // emit String 506 var string = self._decoder.write(pool.slice(start, end)); 507 if (string.length) self.emit('data', string); 508 } else { 509 // emit buffer 510 if (self._events && self._events['data']) { 511 // emit a slice 512 self.emit('data', pool.slice(start, end)); 513 } 514 } 515 516 // Optimization: emit the original buffer with end points 517 if (self.ondata) self.ondata(pool, start, end); 518 } 519 }; 520 self.readable = false; 521 522 // Queue of buffers and string that need to be written to socket. 523 self._writeQueue = []; 524 self._writeQueueEncoding = []; 525 self._writeQueueFD = []; 526 527 self._writeWatcher = ioWatchers.alloc(); 528 self._writeWatcher.socket = self; 529 self._writeWatcher.callback = _doFlush; 530 self.writable = false; 531} 532 533function Stream (fd, type) { 534 events.EventEmitter.call(this); 535 536 this.fd = null; 537 this.type = null; 538 this.secure = false; 539 540 if (parseInt(fd) >= 0) { 541 this.open(fd, type); 542 } else { 543 setImplmentationMethods(this); 544 } 545}; 546sys.inherits(Stream, events.EventEmitter); 547exports.Stream = Stream; 548 549Stream.prototype.setSecure = function(credentials) { 550 if (!have_crypto) { 551 throw new Error('node.js not compiled with openssl crypto support.'); 552 } 553 var crypto= require("crypto"); 554 this.secure = true; 555 this.secureEstablished = false; 556 // If no credentials given, create a new one for just this Stream 557 if (!credentials) { 558 this.credentials = crypto.createCredentials(); 559 } else { 560 this.credentials = credentials; 561 } 562 if (!this.server) { 563 // For clients, we will always have either a given ca list or the default one; 564 this.credentials.shouldVerify = true; 565 } 566 this.secureStream = new SecureStream(this.credentials.context, this.server ? 1 : 0, this.credentials.shouldVerify ? 1 : 0); 567 568 setImplmentationMethods(this); 569 570 if (!this.server) { 571 // If client, trigger handshake 572 this._checkForSecureHandshake(); 573 } 574} 575 576 577Stream.prototype.verifyPeer = function() { 578 if (!this.secure) { 579 throw new Error('Stream is not a secure stream.'); 580 } 581 return this.secureStream.verifyPeer(this.credentials.context); 582} 583 584 585Stream.prototype._checkForSecureHandshake = function() { 586 if (!this.writable) { 587 return; 588 } 589 590 // Do an empty write to see if we need to write out as part of handshake 591 if (!emptyBuffer) allocEmptyBuffer(); 592 this.write(emptyBuffer); 593} 594 595 596Stream.prototype.getPeerCertificate = function(credentials) { 597 if (!this.secure) { 598 throw new Error('Stream is not a secure stream.'); 599 } 600 return this.secureStream.getPeerCertificate(); 601} 602 603 604Stream.prototype.getCipher = function() { 605 if (!this.secure) { 606 throw new Error('Stream is not a secure stream.'); 607 } 608 return this.secureStream.getCurrentCipher(); 609} 610 611 612Stream.prototype.open = function (fd, type) { 613 initStream(this); 614 615 this.fd = fd; 616 this.type = type || null; 617 this.readable = true; 618 619 setImplmentationMethods(this); 620 621 this._writeWatcher.set(this.fd, false, true); 622 this.writable = true; 623} 624 625 626exports.createConnection = function (port, host) { 627 var s = new Stream(); 628 s.connect(port, host); 629 return s; 630}; 631 632 633Object.defineProperty(Stream.prototype, 'readyState', { 634 get: function () { 635 if (this._connecting) { 636 return 'opening'; 637 } else if (this.readable && this.writable) { 638 assert(typeof this.fd == 'number'); 639 return 'open'; 640 } else if (this.readable && !this.writable){ 641 assert(typeof this.fd == 'number'); 642 return 'readOnly'; 643 } else if (!this.readable && this.writable){ 644 assert(typeof this.fd == 'number'); 645 return 'writeOnly'; 646 } else { 647 assert(typeof this.fd != 'number'); 648 return 'closed'; 649 } 650 } 651}); 652 653 654// Returns true if all the data was flushed to socket. Returns false if 655// something was queued. If data was queued, then the "drain" event will 656// signal when it has been finally flushed to socket. 657Stream.prototype.write = function (data, encoding, fd) { 658 if (this._writeQueue && this._writeQueue.length) { 659 // Slow. There is already a write queue, so let's append to it. 660 if (this._writeQueueLast() === END_OF_FILE) { 661 throw new Error('Stream.end() called already; cannot write.'); 662 } 663 664 if (typeof data == 'string' && 665 this._writeQueueEncoding[this._writeQueueEncoding.length-1] === encoding) { 666 // optimization - concat onto last 667 this._writeQueue[this._writeQueue.length-1] += data; 668 } else { 669 this._writeQueue.push(data); 670 this._writeQueueEncoding.push(encoding); 671 } 672 673 if (fd != undefined) { 674 this._writeQueueFD.push(fd); 675 } 676 677 return false; 678 } else { 679 // Fast. 680 // The most common case. There is no write queue. Just push the data 681 // directly to the socket. 682 return this._writeOut(data, encoding, fd); 683 } 684}; 685 686// Directly writes the data to socket. 687// 688// Steps: 689// 1. If it's a string, write it to the `pool`. (If not space remains 690// on the pool make a new one.) 691// 2. Write data to socket. Return true if flushed. 692// 3. Slice out remaining 693// 4. Unshift remaining onto _writeQueue. Return false. 694Stream.prototype._writeOut = function (data, encoding, fd) { 695 if (!this.writable) { 696 throw new Error('Stream is not writable'); 697 } 698 699 var buffer, off, len; 700 var bytesWritten, charsWritten; 701 var queuedData = false; 702 703 if (typeof data != 'string') { 704 // 'data' is a buffer, ignore 'encoding' 705 buffer = data; 706 off = 0; 707 len = data.length; 708 709 } else { 710 assert(typeof data == 'string') 711 712 if (!pool || pool.length - pool.used < kMinPoolSpace) { 713 pool = null; 714 allocNewPool(); 715 } 716 717 if (!encoding || encoding == 'utf8' || encoding == 'utf-8') { 718 // default to utf8 719 bytesWritten = pool.write(data, 'utf8', pool.used); 720 charsWritten = Buffer._charsWritten; 721 } else { 722 bytesWritten = pool.write(data, encoding, pool.used); 723 charsWritten = bytesWritten; 724 } 725 726 if (encoding && data.length > 0) { 727 assert(bytesWritten > 0); 728 } 729 730 buffer = pool; 731 len = bytesWritten; 732 off = pool.used; 733 734 pool.used += bytesWritten; 735 736 debug('wrote ' + bytesWritten + ' bytes to pool'); 737 738 if (charsWritten != data.length) { 739 //debug("couldn't fit " + (data.length - charsWritten) + " bytes into the pool\n"); 740 // Unshift whatever didn't fit onto the buffer 741 this._writeQueue.unshift(data.slice(charsWritten)); 742 this._writeQueueEncoding.unshift(encoding); 743 this._writeWatcher.start(); 744 queuedData = true; 745 } 746 } 747 748 try { 749 bytesWritten = this._writeImpl(buffer, off, len, fd, 0); 750 } catch (e) { 751 this.destroy(e); 752 return false; 753 } 754 755 debug('wrote ' + bytesWritten + ' to socket. [fd, off, len] = ' + JSON.stringify([this.fd, off, len]) + "\n"); 756 757 timeout.active(this); 758 759 if (bytesWritten == len) { 760 // awesome. sent to buffer. 761 if (buffer === pool) { 762 // If we're just writing from the pool then we can make a little 763 // optimization and save the space. 764 buffer.used -= len; 765 } 766 767 if (queuedData) { 768 return false; 769 } else { 770 return true; 771 } 772 } 773 774 // Didn't write the entire thing to buffer. 775 // Need to wait for the socket to become available before trying again. 776 this._writeWatcher.start(); 777 778 // Slice out the data left. 779 var leftOver = buffer.slice(off + bytesWritten, off + len); 780 leftOver.used = leftOver.length; // used the whole thing... 781 782 // sys.error('data.used = ' + data.used); 783 //if (!this._writeQueue) initWriteStream(this); 784 785 // data should be the next thing to write. 786 this._writeQueue.unshift(leftOver); 787 this._writeQueueEncoding.unshift(null); 788 789 // If didn't successfully write any bytes, enqueue our fd and try again 790 if (!bytesWritten) { 791 this._writeQueueFD.unshift(fd); 792 } 793 794 return false; 795} 796 797 798// Flushes the write buffer out. 799// Returns true if the entire buffer was flushed. 800Stream.prototype.flush = function () { 801 while (this._writeQueue && this._writeQueue.length) { 802 var data = this._writeQueue.shift(); 803 var encoding = this._writeQueueEncoding.shift(); 804 var fd = this._writeQueueFD.shift(); 805 806 if (data === END_OF_FILE) { 807 this._shutdown(); 808 return true; 809 } 810 811 var flushed = this._writeOut(data,encoding,fd); 812 if (!flushed) return false; 813 } 814 if (this._writeWatcher) this._writeWatcher.stop(); 815 return true; 816}; 817 818 819Stream.prototype.send = function () { 820 throw new Error('send renamed to write'); 821}; 822 823 824Stream.prototype._writeQueueLast = function () { 825 return this._writeQueue.length > 0 ? this._writeQueue[this._writeQueue.length-1] 826 : null; 827}; 828 829 830Stream.prototype.setEncoding = function (encoding) { 831 var StringDecoder = require("string_decoder").StringDecoder; // lazy load 832 this._decoder = new StringDecoder(encoding); 833}; 834 835 836function doConnect (socket, port, host) { 837 try { 838 connect(socket.fd, port, host); 839 } catch (e) { 840 socket.destroy(e); 841 return; 842 } 843 844 debug('connecting to ' + host + ' : ' + port); 845 846 // Don't start the read watcher until connection is established 847 socket._readWatcher.set(socket.fd, true, false); 848 849 // How to connect on POSIX: Wait for fd to become writable, then call 850 // socketError() if there isn't an error, we're connected. AFAIK this a 851 // platform independent way determining when a non-blocking connection 852 // is established, but I have only seen it documented in the Linux 853 // Manual Page connect(2) under the error code EINPROGRESS. 854 socket._writeWatcher.set(socket.fd, false, true); 855 socket._writeWatcher.start(); 856 socket._writeWatcher.callback = function () { 857 var errno = socketError(socket.fd); 858 if (errno == 0) { 859 // connection established 860 socket._connecting = false; 861 socket.resume(); 862 socket.readable = socket.writable = true; 863 socket._writeWatcher.callback = _doFlush; 864 try { 865 socket.emit('connect'); 866 } catch (e) { 867 socket.destroy(e); 868 return; 869 } 870 } else if (errno != EINPROGRESS) { 871 socket.destroy(errnoException(errno, 'connect')); 872 } 873 }; 874} 875 876function isPort (x) { return parseInt(x) >= 0; } 877 878 879// var stream = new Stream(); 880// stream.connect(80) - TCP connect to port 80 on the localhost 881// stream.connect(80, 'nodejs.org') - TCP connect to port 80 on nodejs.org 882// stream.connect('/tmp/socket') - UNIX connect to socket specified by path 883Stream.prototype.connect = function () { 884 var self = this; 885 initStream(self); 886 if (self.fd) throw new Error('Stream already opened'); 887 if (!self._readWatcher) throw new Error('No readWatcher'); 888 889 timeout.active(socket); 890 891 self._connecting = true; // set false in doConnect 892 893 if (isPort(arguments[0])) { 894 // TCP 895 var port = arguments[0]; 896 dns.lookup(arguments[1], function (err, ip, addressType) { 897 if (err) { 898 self.emit('error', err); 899 } else { 900 self.type = addressType == 4 ? 'tcp4' : 'tcp6'; 901 self.fd = socket(self.type); 902 doConnect(self, port, ip); 903 } 904 }); 905 } else { 906 // UNIX 907 self.fd = socket('unix'); 908 self.type = 'unix'; 909 910 setImplmentationMethods(this); 911 doConnect(self, arguments[0]); 912 } 913}; 914 915 916Stream.prototype.address = function () { 917 return getsockname(this.fd); 918}; 919 920 921Stream.prototype.setNoDelay = function (v) { 922 if ((this.type == 'tcp4')||(this.type == 'tcp6')) { 923 setNoDelay(this.fd, v); 924 } 925}; 926 927Stream.prototype.setKeepAlive = function (enable, time) { 928 if ((this.type == 'tcp4')||(this.type == 'tcp6')) { 929 var secondDelay = Math.ceil(time/1000); 930 setKeepAlive(this.fd, enable, secondDelay); 931 } 932}; 933 934Stream.prototype.setTimeout = function (msecs) { 935 if (msecs > 0) { 936 timeout.enroll(this, msecs); 937 if (this.fd) { timeout.active(this); } 938 } else if (msecs === 0) { 939 timeout.unenroll(this); 940 } 941}; 942 943 944Stream.prototype.pause = function () { 945 this._readWatcher.stop(); 946}; 947 948 949Stream.prototype.resume = function () { 950 if (this.fd === null) throw new Error('Cannot resume() closed Stream.'); 951 this._readWatcher.set(this.fd, true, false); 952 this._readWatcher.start(); 953}; 954 955 956var forceCloseWarning; 957 958Stream.prototype.forceClose = function (e) { 959 if (!forceCloseWarning) { 960 forceCloseWarning = "forceClose() has been renamed to destroy()"; 961 sys.error(forceCloseWarning); 962 } 963 return this.destroy(e); 964}; 965 966 967Stream.prototype.destroy = function (exception) { 968 // pool is shared between sockets, so don't need to free it here. 969 var self = this; 970 971 // TODO would like to set _writeQueue to null to avoid extra object alloc, 972 // but lots of code assumes this._writeQueue is always an array. 973 this._writeQueue = []; 974 975 this.readable = this.writable = false; 976 977 if (this._writeWatcher) { 978 this._writeWatcher.stop(); 979 ioWatchers.free(this._writeWatcher); 980 this._writeWatcher = null; 981 } 982 983 if (this._readWatcher) { 984 this._readWatcher.stop(); 985 ioWatchers.free(this._readWatcher); 986 this._readWatcher = null; 987 } 988 989 timeout.unenroll(this); 990 991 if (this.secure) { 992 this.secureStream.close(); 993 } 994 995 // FIXME Bug when this.fd == 0 996 if (typeof this.fd == 'number') { 997 closeFD(this.fd); 998 this.fd = null; 999 process.nextTick(function () { 1000 if (exception) self.emit('error', exception); 1001 self.emit('close', exception ? true : false); 1002 }); 1003 } 1004}; 1005 1006 1007Stream.prototype._shutdown = function () { 1008 if (!this.writable) { 1009 throw new Error('The connection is not writable'); 1010 } else { 1011 if (this.readable) { 1012 // readable and writable 1013 this.writable = false; 1014 1015 try { 1016 this._shutdownImpl(); 1017 } catch (e) { 1018 this.destroy(e); 1019 } 1020 } else { 1021 // writable but not readable 1022 this.destroy(); 1023 } 1024 } 1025}; 1026 1027var closeDepricationWarning; 1028 1029Stream.prototype.close = function (data, encoding) { 1030 if (!closeDepricationWarning) { 1031 closeDepricationWarning = "Notification: Stream.prototype.close has been renamed to end()"; 1032 sys.error(closeDepricationWarning); 1033 } 1034 return this.end(data, encoding); 1035}; 1036 1037Stream.prototype.end = function (data, encoding) { 1038 if (this.writable) { 1039 if (data) this.write(data, encoding); 1040 if (this._writeQueueLast() !== END_OF_FILE) { 1041 this._writeQueue.push(END_OF_FILE); 1042 this.flush(); 1043 } 1044 } 1045}; 1046 1047 1048function Server (listener) { 1049 events.EventEmitter.call(this); 1050 var self = this; 1051 1052 if (listener) { 1053 self.addListener('connection', listener); 1054 } 1055 1056 self.watcher = new IOWatcher(); 1057 self.watcher.host = self; 1058 self.watcher.callback = function () { 1059 while (self.fd) { 1060 try { 1061 var peerInfo = accept(self.fd); 1062 } catch (e) { 1063 if (e.errno == EMFILE) { 1064 waitingForFDs.push(self); 1065 self.watcher.stop(); 1066 return; 1067 } 1068 throw e; 1069 } 1070 if (!peerInfo) return; 1071 1072 var s = new Stream(peerInfo.fd, self.type); 1073 s.remoteAddress = peerInfo.address; 1074 s.remotePort = peerInfo.port; 1075 s.type = self.type; 1076 s.server = self; 1077 s.resume(); 1078 1079 self.emit('connection', s); 1080 1081 // The 'connect' event probably should be removed for server-side 1082 // sockets. It's redundant. 1083 try { 1084 s.emit('connect'); 1085 } catch (e) { 1086 s.destroy(e); 1087 return; 1088 } 1089 } 1090 }; 1091} 1092sys.inherits(Server, events.EventEmitter); 1093exports.Server = Server; 1094 1095 1096exports.createServer = function (listener) { 1097 return new Server(listener); 1098}; 1099 1100 1101// Listen on a UNIX socket 1102// server.listen("/tmp/socket"); 1103// 1104// Listen on port 8000, accept connections from INADDR_ANY. 1105// server.listen(8000); 1106// 1107// Listen on port 8000, accept connections to "192.168.1.2" 1108// server.listen(8000, "192.168.1.2"); 1109Server.prototype.listen = function () { 1110 var self = this; 1111 if (self.fd) throw new Error('Server already opened'); 1112 1113 var lastArg = arguments[arguments.length - 1]; 1114 if (typeof lastArg == 'function') { 1115 self.addListener('listening', lastArg); 1116 } 1117 1118 if (!isPort(arguments[0])) { 1119 // the first argument specifies a path 1120 self.fd = socket('unix'); 1121 self.type = 'unix'; 1122 var path = arguments[0]; 1123 self.path = path; 1124 // unlink sockfile if it exists 1125 fs.stat(path, function (err, r) { 1126 if (err) { 1127 if (err.errno == ENOENT) { 1128 bind(self.fd, path); 1129 self._doListen(); 1130 } else { 1131 throw r; 1132 } 1133 } else { 1134 if (!r.isSocket()) { 1135 throw new Error("Non-socket exists at " + path); 1136 } else { 1137 fs.unlink(path, function (err) { 1138 if (err) { 1139 throw err; 1140 } else { 1141 bind(self.fd, path); 1142 self._doListen(); 1143 } 1144 }); 1145 } 1146 } 1147 }); 1148 } else if (!arguments[1]) { 1149 // Don't bind(). OS will assign a port with INADDR_ANY. 1150 // The port can be found with server.address() 1151 self.type = 'tcp4'; 1152 self.fd = socket(self.type); 1153 bind(self.fd, arguments[0]); 1154 process.nextTick(function () { 1155 self._doListen(); 1156 }); 1157 } else { 1158 // the first argument is the port, the second an IP 1159 var port = arguments[0]; 1160 dns.lookup(arguments[1], function (err, ip, addressType) { 1161 if (err) { 1162 self.emit('error', err); 1163 } else { 1164 self.type = addressType == 4 ? 'tcp4' : 'tcp6'; 1165 self.fd = socket(self.type); 1166 bind(self.fd, port, ip); 1167 self._doListen(); 1168 } 1169 }); 1170 } 1171}; 1172 1173Server.prototype.listenFD = function (fd, type) { 1174 if (this.fd) { 1175 throw new Error('Server already opened'); 1176 } 1177 1178 this.fd = fd; 1179 this.type = type || null; 1180 this._startWatcher(); 1181}; 1182 1183Server.prototype._startWatcher = function () { 1184 this.watcher.set(this.fd, true, false); 1185 this.watcher.start(); 1186 this.emit("listening"); 1187}; 1188 1189Server.prototype._doListen = function () { 1190 listen(this.fd, 128); 1191 this._startWatcher(); 1192} 1193 1194 1195 1196Server.prototype.address = function () { 1197 return getsockname(this.fd); 1198}; 1199 1200 1201Server.prototype.close = function () { 1202 var self = this; 1203 if (!self.fd) throw new Error('Not running'); 1204 1205 self.watcher.stop(); 1206 1207 closeFD(self.fd); 1208 self.fd = null; 1209 1210 if (self.type === "unix") { 1211 fs.unlink(self.path, function () { 1212 self.emit("close"); 1213 }); 1214 } else { 1215 self.emit("close"); 1216 } 1217}; 1218 1219// vim:ts=2 sw=2