tcp-client: do not create multiple read pollers
[chrome-ext/music-player-client.git] / js / tcp-client.js
CommitLineData
50dafc4c
MF
1/*
2Copyright 2012 Google Inc.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15
16Author: 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;
e7d6adb4 45 this.pollerId = null;
50dafc4c
MF
46
47 log('initialized tcp client');
48 }
49
50 /**
51 * Connects to the TCP socket, and creates an open socket.
52 *
d4a26e73 53 * @see http://developer.chrome.com/apps/socket.html#method-create
50dafc4c
MF
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 *
d4a26e73 66 * @see http://developer.chrome.com/apps/socket.html#method-write
50dafc4c
MF
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 *
d4a26e73 92 * @see http://developer.chrome.com/apps/socket.html#method-disconnect
50dafc4c
MF
93 */
94 TcpClient.prototype.disconnect = function() {
95 socket.disconnect(this.socketId);
e7d6adb4
MF
96 clearInterval(this.pollerId);
97 this.pollerId = null;
50dafc4c
MF
98 this.isConnected = false;
99 };
100
101 /**
102 * The callback function used for when we attempt to have Chrome
103 * create a socket. If the socket is successfully created
104 * we go ahead and connect to the remote side.
105 *
106 * @private
d4a26e73 107 * @see http://developer.chrome.com/apps/socket.html#method-connect
50dafc4c
MF
108 * @param {Object} createInfo The socket details
109 */
110 TcpClient.prototype._onCreate = function(createInfo) {
111 this.socketId = createInfo.socketId;
112 if (this.socketId > 0) {
113 socket.connect(this.socketId, this.host, this.port, this._onConnectComplete.bind(this));
114 this.isConnected = true;
115 } else {
116 error('Unable to create socket');
117 }
118 };
119
120 /**
121 * The callback function used for when we attempt to have Chrome
122 * connect to the remote side. If a successful connection is
123 * made then polling starts to check for data to read
124 *
125 * @private
126 * @param {Number} resultCode Indicates whether the connection was successful
127 */
128 TcpClient.prototype._onConnectComplete = function(resultCode) {
129 // Start polling for reads.
e7d6adb4
MF
130 clearInterval(this.pollerId);
131 this.pollerId = setInterval(this.poll.bind(this), 500);
50dafc4c
MF
132
133 if (this.callbacks.connect) {
134 log('connect complete');
135 this.callbacks.connect();
136 }
137 log('onConnectComplete');
138 };
139
140 /**
141 * Checks for new data to read from the socket
142 *
d4a26e73 143 * @see http://developer.chrome.com/apps/socket.html#method-read
50dafc4c 144 */
0a3a435e 145 TcpClient.prototype.poll = function() {
50dafc4c
MF
146 socket.read(this.socketId, null, this._onDataRead.bind(this));
147 };
148
149 /**
150 * Callback function for when data has been read from the socket.
151 * Converts the array buffer that is read in to a string
152 * and sends it on for further processing by passing it to
153 * the previously assigned callback function.
154 *
155 * @private
156 * @see TcpClient.prototype.addResponseListener
157 * @param {Object} readInfo The incoming message
158 */
159 TcpClient.prototype._onDataRead = function(readInfo) {
160 // Call received callback if there's data in the response.
161 if (readInfo.resultCode > 0 && this.callbacks.recv) {
162 log('onDataRead');
163 // Convert ArrayBuffer to string.
164 this._arrayBufferToString(readInfo.data, function(str) {
165 this.callbacks.recv(str);
166 }.bind(this));
167 }
168 };
169
170 /**
171 * Callback for when data has been successfully
172 * written to the socket.
173 *
174 * @private
175 * @param {Object} writeInfo The outgoing message
176 */
177 TcpClient.prototype._onWriteComplete = function(writeInfo) {
178 log('onWriteComplete');
179 // Call sent callback.
180 if (this.callbacks.sent) {
181 this.callbacks.sent(writeInfo);
182 }
183 };
184
185 /**
186 * Converts an array buffer to a string
187 *
188 * @private
189 * @param {ArrayBuffer} buf The buffer to convert
190 * @param {Function} callback The function to call when conversion is complete
191 */
192 TcpClient.prototype._arrayBufferToString = function(buf, callback) {
193 var bb = new Blob([new Uint8Array(buf)]);
194 var f = new FileReader();
195 f.onload = function(e) {
196 callback(e.target.result);
197 };
198 f.readAsText(bb);
199 };
200
201 /**
202 * Converts a string to an array buffer
203 *
204 * @private
205 * @param {String} str The string to convert
206 * @param {Function} callback The function to call when conversion is complete
207 */
208 TcpClient.prototype._stringToArrayBuffer = function(str, callback) {
209 var bb = new Blob([str]);
210 var f = new FileReader();
211 f.onload = function(e) {
212 callback(e.target.result);
213 };
214 f.readAsArrayBuffer(bb);
215 };
216
217 /**
218 * Wrapper function for logging
219 */
220 function log(msg) {
221 //console.log('tcp-client: ', msg);
222 }
223
224 /**
225 * Wrapper function for error logging
226 */
227 function error(msg) {
228 console.error('tcp-client: ', msg);
229 }
230
231 exports.TcpClient = TcpClient;
232
233})(window);