11d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman/*
21d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg HartmanCopyright 2012 Google Inc.
31d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
41d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg HartmanLicensed under the Apache License, Version 2.0 (the "License");
51d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartmanyou may not use this file except in compliance with the License.
61d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg HartmanYou may obtain a copy of the License at
71d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
81d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman     http://www.apache.org/licenses/LICENSE-2.0
91d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
101d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg HartmanUnless required by applicable law or agreed to in writing, software
111d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartmandistributed under the License is distributed on an "AS IS" BASIS,
121d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg HartmanWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg HartmanSee the License for the specific language governing permissions and
141d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartmanlimitations under the License.
151d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
161d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg HartmanAuthor: Boris Smus (smus@chromium.org)
171d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman*/
181d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
191d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman(function(exports) {
201d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
211d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  // Define some local variables here.
221d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  var socket = chrome.socket || chrome.experimental.socket;
231d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  var dns = chrome.experimental.dns;
241d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
251d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  /**
261d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * Creates an instance of the client
271d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   *
281d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @param {String} host The remote host to connect to
291d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @param {Number} port The port to connect to at the remote host
301d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   */
311d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  function TcpClient(host, port, pollInterval) {
321d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    this.host = host;
331d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    this.port = port;
341d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    this.pollInterval = pollInterval || 15;
351d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
361d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    // Callback functions.
371d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    this.callbacks = {
381d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      connect: null,    // Called when socket is connected.
391d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      disconnect: null, // Called when socket is disconnected.
401d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      recvBuffer: null, // Called (as ArrayBuffer) when client receives data from server.
411d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      recvString: null, // Called (as string) when client receives data from server.
421d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      sent: null        // Called when client sends data to server.
431d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    };
441d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
451d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    // Socket.
461d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    this.socketId = null;
471d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    this.isConnected = false;
481d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
491d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    log('initialized tcp client');
501d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  }
511d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
521d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  /**
531d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * Connects to the TCP socket, and creates an open socket.
541d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   *
551d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @see http://developer.chrome.com/trunk/apps/socket.html#method-create
561d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @param {Function} callback The function to call on connection
571d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   */
581d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  TcpClient.prototype.connect = function(callback) {
591d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    // First resolve the hostname to an IP.
601d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    dns.resolve(this.host, function(result) {
611d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      this.addr = result.address;
621d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      socket.create('tcp', {}, this._onCreate.bind(this));
631d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
641d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      // Register connect callback.
651d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      this.callbacks.connect = callback;
661d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    }.bind(this));
671d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  };
681d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
691d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  /**
701d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * Sends an arraybuffer/view down the wire to the remote side
711d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   *
721d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @see http://developer.chrome.com/trunk/apps/socket.html#method-write
731d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @param {String} msg The arraybuffer/view to send
741d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @param {Function} callback The function to call when the message has sent
751d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   */
761d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  TcpClient.prototype.sendBuffer = function(buf, callback) {
771d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    if (buf.buffer) {
781d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman        buf = buf.buffer;
791d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    }
801d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
811d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    /*
821d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    // Debug
831d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    var bytes = [], u8 = new Uint8Array(buf);
841d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    for (var i = 0; i < u8.length; i++) {
851d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman        bytes.push(u8[i]);
861d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    }
871d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    log("sending bytes: " + (bytes.join(',')));
881d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    */
891d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
901d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    socket.write(this.socketId, buf, this._onWriteComplete.bind(this));
911d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
921d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    // Register sent callback.
931d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    this.callbacks.sent = callback;
941d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  };
951d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
961d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  /**
971d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * Sends a string down the wire to the remote side
981d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   *
991d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @see http://developer.chrome.com/trunk/apps/socket.html#method-write
1001d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @param {String} msg The string to send
1011d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @param {Function} callback The function to call when the message has sent
1021d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   */
1031d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  TcpClient.prototype.sendString = function(msg, callback) {
1041d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    /*
1051d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    // Debug
1061d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    log("sending string: " + msg);
1071d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    */
1081d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
1091d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    this._stringToArrayBuffer(msg, function(arrayBuffer) {
1101d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      socket.write(this.socketId, arrayBuffer, this._onWriteComplete.bind(this));
1111d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    }.bind(this));
1121d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
1131d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    // Register sent callback.
1141d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    this.callbacks.sent = callback;
1151d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  };
1161d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
1171d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  /**
1181d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * Sets the callback for when a message is received
1191d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   *
1201d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @param {Function} callback The function to call when a message has arrived
1211d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @param {String} type The callback argument type: "arraybuffer" or "string"
1221d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   */
1231d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  TcpClient.prototype.addResponseListener = function(callback, type) {
1241d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    if (typeof type === "undefined") {
1251d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman        type = "arraybuffer";
1261d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    }
1271d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    // Register received callback.
1281d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    if (type === "string") {
1291d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      this.callbacks.recvString = callback;
1301d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    } else {
1311d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      this.callbacks.recvBuffer = callback;
1321d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    }
1331d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  };
1341d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
1351d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  /**
1361d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * Sets the callback for when the socket disconnects
1371d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   *
1381d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @param {Function} callback The function to call when the socket disconnects
1391d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @param {String} type The callback argument type: "arraybuffer" or "string"
1401d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   */
1411d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  TcpClient.prototype.addDisconnectListener = function(callback) {
1421d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    // Register disconnect callback.
1431d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    this.callbacks.disconnect = callback;
1441d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  };
1451d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
1461d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  /**
1471d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * Disconnects from the remote side
1481d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   *
1491d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @see http://developer.chrome.com/trunk/apps/socket.html#method-disconnect
1501d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   */
1511d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  TcpClient.prototype.disconnect = function() {
1521d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    if (this.isConnected) {
1531d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      this.isConnected = false;
1541d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      socket.disconnect(this.socketId);
1551d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      if (this.callbacks.disconnect) {
1561d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman        this.callbacks.disconnect();
1571d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      }
1581d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      log('socket disconnected');
1591d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    }
1601d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  };
1611d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
1621d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  /**
1631d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * The callback function used for when we attempt to have Chrome
1641d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * create a socket. If the socket is successfully created
1651d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * we go ahead and connect to the remote side.
1661d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   *
1671d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @private
1681d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @see http://developer.chrome.com/trunk/apps/socket.html#method-connect
1691d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @param {Object} createInfo The socket details
1701d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   */
1711d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  TcpClient.prototype._onCreate = function(createInfo) {
1721d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    this.socketId = createInfo.socketId;
1731d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    if (this.socketId > 0) {
1741d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      socket.connect(this.socketId, this.addr, this.port, this._onConnectComplete.bind(this));
1751d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    } else {
1761d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      error('Unable to create socket');
1771d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    }
1781d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  };
1791d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
1801d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  /**
1811d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * The callback function used for when we attempt to have Chrome
1821d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * connect to the remote side. If a successful connection is
1831d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * made then polling starts to check for data to read
1841d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   *
1851d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @private
1861d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @param {Number} resultCode Indicates whether the connection was successful
1871d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   */
1881d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  TcpClient.prototype._onConnectComplete = function(resultCode) {
1891d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    // Start polling for reads.
1901d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    this.isConnected = true;
1911d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    setTimeout(this._periodicallyRead.bind(this), this.pollInterval);
1921d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
1931d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    if (this.callbacks.connect) {
1941d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      log('connect complete');
1951d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      this.callbacks.connect();
1961d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    }
1971d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    log('onConnectComplete');
1981d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  };
1991d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
2001d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  /**
2011d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * Checks for new data to read from the socket
2021d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   *
2031d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @see http://developer.chrome.com/trunk/apps/socket.html#method-read
2041d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   */
2051d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  TcpClient.prototype._periodicallyRead = function() {
2061d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    var that = this;
2071d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    socket.getInfo(this.socketId, function (info) {
2081d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      if (info.connected) {
2091d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman        setTimeout(that._periodicallyRead.bind(that), that.pollInterval);
2101d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman        socket.read(that.socketId, null, that._onDataRead.bind(that));
2111d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      } else if (that.isConnected) {
2121d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman        log('socket disconnect detected');
2131d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman        that.disconnect();
2141d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      }
2151d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   });
2161d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  };
2171d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
2181d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  /**
2191d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * Callback function for when data has been read from the socket.
2201d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * Converts the array buffer that is read in to a string
2211d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * and sends it on for further processing by passing it to
2221d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * the previously assigned callback function.
2231d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   *
2241d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @private
2251d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @see TcpClient.prototype.addResponseListener
2261d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @param {Object} readInfo The incoming message
2271d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   */
2281d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  TcpClient.prototype._onDataRead = function(readInfo) {
2291d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    // Call received callback if there's data in the response.
2301d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    if (readInfo.resultCode > 0) {
2311d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      log('onDataRead');
2321d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
2331d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      /*
2341d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      // Debug
2351d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      var bytes = [], u8 = new Uint8Array(readInfo.data);
2361d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      for (var i = 0; i < u8.length; i++) {
2371d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman          bytes.push(u8[i]);
2381d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      }
2391d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      log("received bytes: " + (bytes.join(',')));
2401d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      */
2411d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
2421d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      if (this.callbacks.recvBuffer) {
2431d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman        // Return raw ArrayBuffer directly.
2441d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman        this.callbacks.recvBuffer(readInfo.data);
2451d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      }
2461d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      if (this.callbacks.recvString) {
2471d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman        // Convert ArrayBuffer to string.
2481d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman        this._arrayBufferToString(readInfo.data, function(str) {
2491d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman          this.callbacks.recvString(str);
2501d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman        }.bind(this));
2511d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      }
2521d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
2531d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      // Trigger another read right away
2541d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      setTimeout(this._periodicallyRead.bind(this), 0);
2551d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    }
2561d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  };
2571d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
2581d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  /**
2591d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * Callback for when data has been successfully
2601d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * written to the socket.
2611d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   *
2621d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @private
2631d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @param {Object} writeInfo The outgoing message
2641d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   */
2651d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  TcpClient.prototype._onWriteComplete = function(writeInfo) {
2661d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    log('onWriteComplete');
2671d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    // Call sent callback.
2681d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    if (this.callbacks.sent) {
2691d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      this.callbacks.sent(writeInfo);
2701d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    }
2711d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  };
2721d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
2731d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  /**
2741d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * Converts an array buffer to a string
2751d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   *
2761d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @private
2771d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @param {ArrayBuffer} buf The buffer to convert
2781d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @param {Function} callback The function to call when conversion is complete
2791d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   */
2801d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  TcpClient.prototype._arrayBufferToString = function(buf, callback) {
2811d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    var bb = new Blob([new Uint8Array(buf)]);
2821d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    var f = new FileReader();
2831d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    f.onload = function(e) {
2841d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman      callback(e.target.result);
2851d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    };
2861d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    f.readAsText(bb);
2871d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  };
2881d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
2891d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  /**
2901d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * Converts a string to an array buffer
2911d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   *
2921d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @private
2931d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @param {String} str The string to convert
2941d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * @param {Function} callback The function to call when conversion is complete
2951d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   */
2961d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  TcpClient.prototype._stringToArrayBuffer = function(str, callback) {
2971d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    var bb = new Blob([str]);
2981d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    var f = new FileReader();
2991d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    f.onload = function(e) {
3001d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman        callback(e.target.result);
3011d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    };
3021d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    f.readAsArrayBuffer(bb);
3031d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  };
3041d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
3051d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  /**
3061d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * Wrapper function for logging
3071d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   */
3081d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  function log(msg) {
3091d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    console.log(msg);
3101d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  }
3111d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
3121d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  /**
3131d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   * Wrapper function for error logging
3141d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman   */
3151d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  function error(msg) {
3161d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman    console.error(msg);
3171d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  }
3181d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
3191d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman  exports.TcpClient = TcpClient;
3201d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman
3211d80c6a8d34f59543f7df1963c22d7efa292bcb0Greg Hartman})(window);
322