]>
Commit | Line | Data |
---|---|---|
50dafc4c MF |
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; | |
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); | |
fac1d771 MF |
96 | if (this.isConnected) { |
97 | clearInterval(this.pollerId); | |
98 | this.pollerId = null; | |
99 | this.isConnected = false; | |
100 | } | |
50dafc4c MF |
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 | |
d4a26e73 | 109 | * @see http://developer.chrome.com/apps/socket.html#method-connect |
50dafc4c MF |
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)); | |
50dafc4c MF |
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) { | |
fac1d771 MF |
130 | log('resultCode: ' + resultCode); |
131 | ||
132 | // XXX: Can this ever be positive ? | |
133 | this.isConnected = (resultCode >= 0); | |
134 | ||
50dafc4c | 135 | // Start polling for reads. |
e7d6adb4 | 136 | clearInterval(this.pollerId); |
fac1d771 MF |
137 | if (this.isConnected) { |
138 | this.pollerId = setInterval(this.poll.bind(this), 500); | |
139 | } | |
50dafc4c MF |
140 | |
141 | if (this.callbacks.connect) { | |
142 | log('connect complete'); | |
fac1d771 | 143 | this.callbacks.connect(resultCode); |
50dafc4c MF |
144 | } |
145 | log('onConnectComplete'); | |
146 | }; | |
147 | ||
148 | /** | |
149 | * Checks for new data to read from the socket | |
150 | * | |
d4a26e73 | 151 | * @see http://developer.chrome.com/apps/socket.html#method-read |
50dafc4c | 152 | */ |
0a3a435e | 153 | TcpClient.prototype.poll = function() { |
50dafc4c MF |
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) { | |
fac1d771 | 229 | //console.log('tcp-client:', msg); |
50dafc4c MF |
230 | } |
231 | ||
232 | /** | |
233 | * Wrapper function for error logging | |
234 | */ | |
235 | function error(msg) { | |
fac1d771 | 236 | console.error('tcp-client:', msg); |
50dafc4c MF |
237 | } |
238 | ||
239 | exports.TcpClient = TcpClient; | |
240 | ||
241 | })(window); |