]> git.wh0rd.org Git - chrome-ext/music-player-client.git/blob - js/tcp-client.js
ec61399b49ce12b1be0a3e15422a5f417b3da71f
[chrome-ext/music-player-client.git] / js / tcp-client.js
1 /*
2 Copyright 2012 Google Inc.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8      http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15
16 Author: Boris Smus (smus@chromium.org)
17 */
18
19 (function(exports) {
20
21   // Define some local variables here.
22   var socket = chrome.socket;
23
24   /**
25    * Creates an instance of the client
26    *
27    * @param {String} host The remote host to connect to
28    * @param {Number} port The port to connect to at the remote host
29    */
30   function TcpClient(host, port) {
31     this.host = host;
32     this.port = port;
33
34     // Callback functions.
35     this.callbacks = {
36       connect: null,    // Called when socket is connected.
37       disconnect: null, // Called when socket is disconnected.
38       recv: null,       // Called when client receives data from server.
39       sent: null        // Called when client sends data to server.
40     };
41
42     // Socket.
43     this.socketId = null;
44     this.isConnected = false;
45     this.pollerId = null;
46
47     log('initialized tcp client');
48   }
49
50   /**
51    * Connects to the TCP socket, and creates an open socket.
52    *
53    * @see http://developer.chrome.com/apps/socket.html#method-create
54    * @param {Function} callback The function to call on connection
55    */
56   TcpClient.prototype.connect = function(callback) {
57     socket.create('tcp', {}, this._onCreate.bind(this));
58
59     // Register connect callback.
60     this.callbacks.connect = callback;
61   };
62
63   /**
64    * Sends a message down the wire to the remote side
65    *
66    * @see http://developer.chrome.com/apps/socket.html#method-write
67    * @param {String} msg The message to send
68    * @param {Function} callback The function to call when the message has sent
69    */
70   TcpClient.prototype.sendMessage = function(msg, callback) {
71     this._stringToArrayBuffer(msg + '\n', function(arrayBuffer) {
72       socket.write(this.socketId, arrayBuffer, this._onWriteComplete.bind(this));
73     }.bind(this));
74
75     // Register sent callback.
76     this.callbacks.sent = callback;
77   };
78
79   /**
80    * Sets the callback for when a message is received
81    *
82    * @param {Function} callback The function to call when a message has arrived
83    */
84   TcpClient.prototype.addResponseListener = function(callback) {
85     // Register received callback.
86     this.callbacks.recv = callback;
87   };
88
89   /**
90    * Disconnects from the remote side
91    *
92    * @see http://developer.chrome.com/apps/socket.html#method-disconnect
93    */
94   TcpClient.prototype.disconnect = function() {
95     socket.disconnect(this.socketId);
96     if (this.isConnected) {
97       clearInterval(this.pollerId);
98       this.pollerId = null;
99       this.isConnected = false;
100     }
101   };
102
103   /**
104    * The callback function used for when we attempt to have Chrome
105    * create a socket. If the socket is successfully created
106    * we go ahead and connect to the remote side.
107    *
108    * @private
109    * @see http://developer.chrome.com/apps/socket.html#method-connect
110    * @param {Object} createInfo The socket details
111    */
112   TcpClient.prototype._onCreate = function(createInfo) {
113     this.socketId = createInfo.socketId;
114     if (this.socketId > 0) {
115       socket.connect(this.socketId, this.host, this.port, this._onConnectComplete.bind(this));
116     } else {
117       error('Unable to create socket');
118     }
119   };
120
121   /**
122    * The callback function used for when we attempt to have Chrome
123    * connect to the remote side. If a successful connection is
124    * made then polling starts to check for data to read
125    *
126    * @private
127    * @param {Number} resultCode Indicates whether the connection was successful
128    */
129   TcpClient.prototype._onConnectComplete = function(resultCode) {
130     log('resultCode: ' + resultCode);
131
132     // XXX: Can this ever be positive ?
133     this.isConnected = (resultCode >= 0);
134
135     // Start polling for reads.
136     clearInterval(this.pollerId);
137     if (this.isConnected) {
138       this.pollerId = setInterval(this.poll.bind(this), 500);
139     }
140
141     if (this.callbacks.connect) {
142       log('connect complete');
143       this.callbacks.connect(resultCode);
144     }
145     log('onConnectComplete');
146   };
147
148   /**
149    * Checks for new data to read from the socket
150    *
151    * @see http://developer.chrome.com/apps/socket.html#method-read
152    */
153   TcpClient.prototype.poll = function() {
154     socket.read(this.socketId, null, this._onDataRead.bind(this));
155   };
156
157   /**
158    * Callback function for when data has been read from the socket.
159    * Converts the array buffer that is read in to a string
160    * and sends it on for further processing by passing it to
161    * the previously assigned callback function.
162    *
163    * @private
164    * @see TcpClient.prototype.addResponseListener
165    * @param {Object} readInfo The incoming message
166    */
167   TcpClient.prototype._onDataRead = function(readInfo) {
168     // Call received callback if there's data in the response.
169     if (readInfo.resultCode > 0 && this.callbacks.recv) {
170       log('onDataRead');
171       // Convert ArrayBuffer to string.
172       this._arrayBufferToString(readInfo.data, function(str) {
173         this.callbacks.recv(str);
174       }.bind(this));
175     }
176   };
177
178   /**
179    * Callback for when data has been successfully
180    * written to the socket.
181    *
182    * @private
183    * @param {Object} writeInfo The outgoing message
184    */
185   TcpClient.prototype._onWriteComplete = function(writeInfo) {
186     log('onWriteComplete');
187     // Call sent callback.
188     if (this.callbacks.sent) {
189       this.callbacks.sent(writeInfo);
190     }
191   };
192
193   /**
194    * Converts an array buffer to a string
195    *
196    * @private
197    * @param {ArrayBuffer} buf The buffer to convert
198    * @param {Function} callback The function to call when conversion is complete
199    */
200   TcpClient.prototype._arrayBufferToString = function(buf, callback) {
201     var bb = new Blob([new Uint8Array(buf)]);
202     var f = new FileReader();
203     f.onload = function(e) {
204       callback(e.target.result);
205     };
206     f.readAsText(bb);
207   };
208
209   /**
210    * Converts a string to an array buffer
211    *
212    * @private
213    * @param {String} str The string to convert
214    * @param {Function} callback The function to call when conversion is complete
215    */
216   TcpClient.prototype._stringToArrayBuffer = function(str, callback) {
217     var bb = new Blob([str]);
218     var f = new FileReader();
219     f.onload = function(e) {
220         callback(e.target.result);
221     };
222     f.readAsArrayBuffer(bb);
223   };
224
225   /**
226    * Wrapper function for logging
227    */
228   function log(msg) {
229     //console.log('tcp-client:', msg);
230   }
231
232   /**
233    * Wrapper function for error logging
234    */
235   function error(msg) {
236     console.error('tcp-client:', msg);
237   }
238
239   exports.TcpClient = TcpClient;
240
241 })(window);