PageRenderTime 54ms CodeModel.GetById 2ms app.highlight 43ms RepoModel.GetById 1ms app.codeStats 1ms

/lib/net.js

https://github.com/kevinxx/node_doc_zh_CN
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