Source: vertx/net.js

/*
 * Copyright 2011-2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

if (typeof __vertxload === 'string') {
  throw "Use require() to load Vert.x API modules";
}

/**
 * The <code>vertx/net</code> module provides network client and server
 * functions and classes.
 * @exports vertx/net
 */
var net = {};
var streams = require('vertx/streams');
var tcp_support = require('vertx/tcp_support');
var ssl_support = require('vertx/ssl_support');
var helpers = require("vertx/helpers.js");
var Buffer = require('vertx/buffer');


/**
 * Creates a {@linkcode module:vertx/net.NetServer|NetServer}.
 * @returns {module:vertx/net.NetServer} A new server instance
 */
net.createNetServer = function() {
  return new net.NetServer();
};

/**
 * Creates a {@linkcode module:vertx/net.NetClient|NetClient}.
 * @returns {module:vertx/net.NetClient} A new client instance
 */
net.createNetClient = function() {
  return new net.NetClient();
};

/**
 * A <code>ListenHandler</code> is a {@linkcode Handler} that is
 * notified when a call to {@linkcode module:vertx/net.NetServer#listen}
 * has been initiated and the underlying socket has been bound.
 * @typedef {function} ListenHandler
 */

/**
 * A <code>ConnectHandler</code> is a {@linkcode Handler} that accepts a
 * {@linkcode module:vertx/net.NetSocket} as it's parameter.
 * @typedef {function} ConnectHandler
 * @param {module:vertx/net.NetSocket} netSocket The raw socket
 */

/**
 * Represents a TCP or SSL server.  
 * @constructor
 * @augments module:vertx/ssl_support~SSLSupport
 * @augments module:vertx/ssl_support~ServerSSLSupport
 * @augments module:vertx/tcp_support~TCPSupport
 * @augments module:vertx/tcp_support~ServerTCPSupport
 */
net.NetServer = function() {
  var that = this;
  var jserver = __jvertx.createNetServer();

  tcp_support.TCPSupport.call(this, jserver);
  tcp_support.ServerTCPSupport.call(this, jserver);

  ssl_support.SSLSupport.call(this, jserver);
  ssl_support.ServerSSLSupport.call(this, jserver);

  /**
   * Supply a connect handler for this server. The server can only have at most
   * one connect handler at any one time.  As the server accepts TCP or SSL
   * connections it creates an instance of {@linkcode module:vertx/net.NetSocket}
   * and passes it to the connect handler.
   *
   * @param {ConnectHandler} handler the connection handler
   * @returns {module:vertx/net.NetServer} this
   */
  this.connectHandler = function(handler) {
    jserver.connectHandler(function(result) {
      handler(new net.NetSocket(result));
    });
    return that;
  };

  /**
   * Tell the server to start listening on all available interfaces and
   * <code>port</code>. Be aware this is an async operation and the server may
   * not bound on return of the method.
   *
   * @param {number} port The network port to listen on
   * @param {string} [host] The hostname or IP address to listen on
   * @param {ListenHandler} [handler] The listen handler
   * @returns {module:vertx/net.NetServer} this
   */
  this.listen = function() {
    var args = Array.prototype.slice.call(arguments);
    var handler = helpers.getArgValue('function', args);
    var host = helpers.getArgValue('string', args);
    var port = helpers.getArgValue('number', args);
    if (handler !== null) {
      handler = helpers.adaptAsyncResultHandler(handler);
    }
    if (host === null) {
      host = "0.0.0.0";
    }
    jserver.listen(port, host, handler);
    return that;
  };

  /**
   * Close the server. This will close any open connections.
   * @param {Handler} [handler] the handler to call when the close operation has completed
   */
  this.close = function(handler) {
    if (handler === undefined) {
      jserver.close();
    } else {
      jserver.close(helpers.adaptAsyncResultHandler(handler));
    }
  };

  /**
   * The actual port the server is listening on. This is useful if you bound
   * the server specifying 0 as port number signifying an ephemeral port.
   * @returns {number} The port number the server is listening on
   */
  this.port = function() {
    return jserver.port();
  };

  /**
   * The host name
   * @returns {string} the host
   */
  this.host = function() {
    return jserver.host();
  };
};

/**
 * <p>
 * A TCP/SSL client.
 * </p>
 * <p>
 * Multiple connections to different servers can be made using the same
 * instance.  This client supports a configurable number of connection attempts
 * and a configurable delay between attempts.
 * </p>
 *
 * @constructor
 * @augments module:vertx/ssl_support~SSLSupport
 * @augments module:vertx/ssl_support~ClientSSLSupport
 * @augments module:vertx/tcp_support~TCPSupport
 */
net.NetClient = function() {
  var jclient = __jvertx.createNetClient();
  var that = this;
  tcp_support.TCPSupport.call(this, jclient);

  ssl_support.SSLSupport.call(this, jclient);
  ssl_support.ClientSSLSupport.call(this, jclient);

  /**
   * Attempt to open a connection to a server at the specific port and host.
   * The connect is done asynchronously and on success, a
   * {@linkcode module:vertx/net.NetSocket} instance is supplied via the
   * {@linkcode ConnectHandler} instance 
   *
   * @param {number} port The port to connect on
   * @param {string} [host] The hostname or IP address to connect to (default: localhost)
   * @param {ConnectHandler} [connectHandler] The handler receiving the connection
   * @return {module:vertx/net.NetClient} this
   */
  this.connect = function(arg0, arg1, arg2) {
    var port = arg0;
    var host;
    var handler;
    if (arg2 === undefined) {
      host = 'localhost';
      handler = arg1;
    } else {
      host = arg1;
      handler = arg2;
    }
    jclient.connect(port, host, helpers.adaptAsyncResultHandler(handler, function(result) {
      return new net.NetSocket(result);
    }));
    return that;
  };

  /**
   * Get or set the number of reconnection attempts. Default value is zero.
   * @param {number} [attempts] the number of reconnection attempts to make before failing
   * @returns {number|module:vertx/net.NetClient}
   */
  this.reconnectAttempts = function(attempts) {
    if (attempts === undefined) {
      return jclient.getReconnectAttempts();
    } else {
      jclient.setReconnectAttempts(attempts);
      return that;
    }
  };

  /**
   * Get or set the reconnection interval, in milliseconds.
   * @param {number} [interval] the number of milliseconds to wait before attempting a reconnection
   * @returns {number|module:vertx/net.NetClient}
   */
  this.reconnectInterval = function(interval) {
    if (interval === undefined) {
      return jclient.getReconnectInterval();
    } else {
      jclient.setReconnectInterval(interval);
      return that;
    }
  };
  
  /**
   * Get or set the connect timeout, in milliseconds.
   * @param {number} [timeout] the number of milliseconds to wait before timing out a connection
   * @returns {number|module:vertx/net.NetClient}
   */
  this.connectTimeout = function(timeout) {
    if (timeout === undefined) {
      return jclient.getConnectTimeout();
    } else {
      jclient.setConnectTimeout(timeout);
      return that;
    }
  };
  
  /**
   * Close the client.
   */
  this.close = function() {
    jclient.close();
  };
};

/**
 * <p>
 * A socket-like interface to a TCP/SSL connection on either the client or the
 * server side.
 * </p>
 * <p>
 * Instances of this class are created on the client side by a 
 * {@linkcode module:vertx/net.NetClient} when a connection to a server is
 * made, or on the server side by a {@linkcode vertx/net.NetServer}
 * when a server accepts a connection.
 * </p>
 * <p>
 * It implements both {@linkcode ReadStream} and 
 * {@linkcode WriteStream} so it can be used with 
 * {@linkcode module:vertx/pump~Pump} to pump data with flow control.
 * </p>
 * @constructor
 * @augments module:vertx/streams~ReadStream
 * @augments module:vertx/streams~WriteStream
 */
net.NetSocket = function(jNetSocket) {
  var that = this;
  streams.ReadStream.call(this, jNetSocket);
  streams.WriteStream.call(this, jNetSocket);

  /**
   * <p>
   * When a NetSocket is created it automatically registers an event handler
   * with the event bus, the ID of that handler is given by
   * writeHandlerID.
   * </p>
   * <p>
   * Given this ID, a different event loop can send a buffer to that event
   * handler using the event bus and that buffer will be received by this
   * instance in its own event loop and written to the underlying connection.
   * This allows you to write data to other connections which are owned by
   * different event loops.
   * </p>
   */
  this.writeHandlerID = function() {
    return jNetSocket.writeHandlerID();
  };

  /**
   * Write a string or {@linkcode module:vertx/buffer~Buffer} to the socket.
   * @param {string|module:vertx/buffer~Buffer} chunk the data to write
   * @param {string} [encoding] the charset for the data (default: UTF-8)
   * @returns {module:vertx/net.NetSocket} this
   */
  this.write = function(arg0, arg1) {
    if (arg1 === undefined) {
      if (arg0 instanceof Buffer) {
        jNetSocket.write(arg0._to_java_buffer());
      } else {
        jNetSocket.write(arg0);
      }
    } else {
      jNetSocket.write(arg0, arg1);
    }
    return that;
  };

  /**
   * Tell the kernel to stream a file as specified by <code>filename</code>
   * directly from disk to the outgoing connection, bypassing userspace
   * altogether (where supported by the underlying operating system. This is a
   * very efficient way to stream files
   * @param {string} filename The path to the file
   * @param {function} [callback] A function to be called when the send
   * has completed or a failure has occurred.
   * @returns {module:vertx/net.NetSocket} this
   */
  this.sendFile = function(filename, callback) {
    jNetSocket.sendFile(filename, helpers.adaptAsyncResultHandler(callback));
    return that;
  };
  
  /**
   * Returns the remote address for this socket
   * @returns {Address} The remote address
   */
  this.remoteAddress = function() {
    return {
      'ipaddress': jNetSocket.remoteAddress().getAddress().getHostAddress(),
      'port': jNetSocket.remoteAddress().getPort()
    };
  };

  /**
   * Returns the local address for this socket
   * @returns {Address} The local address
   */
  this.localAddress = function() {
    return {
      'ipaddress': jNetSocket.localAddress().getAddress().getHostAddress(),
      'port': jNetSocket.localAddress().getPort()
    };
  };

  /**
   * Close this socket.
   */
  this.close = function() {
    jNetSocket.close();
  };

  /**
   * Set a {@linkcode Handler} to be called when this socket is closed
   * @param {Handler} handler The close handler to use
   */
  this.closeHandler = function(handler) {
    jNetSocket.closeHandler(handler);
    return that;
  };

  /**
   * Upgrade channel to use SSL/TLS. Be aware that for this to work SSL must
   * already be configured.
   *
   * @example
   * <pre><code>
   *     var server = vertx.createNetServer().
   *                        .keyStorePath('/path/to/your/keystore/server-keystore.jks')
   *                        .keyStorePassword('password');
   *     server.connectHandler(function(sock) {
   *       if (authCheck()) {
   *         sock.ssl();
   *       }
   *     }
   * </code></pre>
   *
   * @param {Handler} [handler] Called when the upgrade has been completed
   * @returns {module:vertx/net.NetSocket} this
   */
  this.ssl = function(handler) {
    if (!handler) {
      handler = function() {};
    }
    jNetSocket.ssl(handler);
    return that;
  };

  /**
   * Returns true if this socket is SSL/TLS encrypted.
   */
  this.isSSL = function() {
    return jNetSocket.isSsl();
  };
};

/**
 * @typedef {{}} Address
 * @property {string} ipaddress The IP address
 * @property {number} port The port for this address
 */

module.exports = net;