]>
Commit | Line | Data |
---|---|---|
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 | if (this.socketId !== null) { | |
114 | socket.destroy(this.socketId); | |
115 | } | |
116 | this.socketId = createInfo.socketId; | |
117 | if (this.socketId > 0) { | |
118 | socket.connect(this.socketId, this.host, this.port, this._onConnectComplete.bind(this)); | |
119 | } else { | |
120 | error('Unable to create socket'); | |
121 | } | |
122 | }; | |
123 | ||
124 | /** | |
125 | * The callback function used for when we attempt to have Chrome | |
126 | * connect to the remote side. If a successful connection is | |
127 | * made then polling starts to check for data to read | |
128 | * | |
129 | * @private | |
130 | * @param {Number} resultCode Indicates whether the connection was successful | |
131 | */ | |
132 | TcpClient.prototype._onConnectComplete = function(resultCode) { | |
133 | log('resultCode: ' + resultCode); | |
134 | ||
135 | // XXX: Can this ever be positive ? | |
136 | this.isConnected = (resultCode >= 0); | |
137 | ||
138 | // Start polling for reads. | |
139 | clearInterval(this.pollerId); | |
140 | if (this.isConnected) { | |
141 | this.pollerId = setInterval(this.poll.bind(this), 500); | |
142 | } | |
143 | ||
144 | if (this.callbacks.connect) { | |
145 | log('connect complete'); | |
146 | this.callbacks.connect(resultCode); | |
147 | } | |
148 | log('onConnectComplete'); | |
149 | }; | |
150 | ||
151 | /** | |
152 | * Checks for new data to read from the socket | |
153 | * | |
154 | * @see http://developer.chrome.com/apps/socket.html#method-read | |
155 | */ | |
156 | TcpClient.prototype.poll = function() { | |
157 | socket.read(this.socketId, null, this._onDataRead.bind(this)); | |
158 | }; | |
159 | ||
160 | /** | |
161 | * Callback function for when data has been read from the socket. | |
162 | * Converts the array buffer that is read in to a string | |
163 | * and sends it on for further processing by passing it to | |
164 | * the previously assigned callback function. | |
165 | * | |
166 | * @private | |
167 | * @see TcpClient.prototype.addResponseListener | |
168 | * @param {Object} readInfo The incoming message | |
169 | */ | |
170 | TcpClient.prototype._onDataRead = function(readInfo) { | |
171 | // Call received callback if there's data in the response. | |
172 | if (readInfo.resultCode > 0 && this.callbacks.recv) { | |
173 | log('onDataRead'); | |
174 | // Convert ArrayBuffer to string. | |
175 | this._arrayBufferToString(readInfo.data, function(str) { | |
176 | this.callbacks.recv(str); | |
177 | }.bind(this)); | |
178 | } | |
179 | }; | |
180 | ||
181 | /** | |
182 | * Callback for when data has been successfully | |
183 | * written to the socket. | |
184 | * | |
185 | * @private | |
186 | * @param {Object} writeInfo The outgoing message | |
187 | */ | |
188 | TcpClient.prototype._onWriteComplete = function(writeInfo) { | |
189 | log('onWriteComplete'); | |
190 | // Call sent callback. | |
191 | if (this.callbacks.sent) { | |
192 | this.callbacks.sent(writeInfo); | |
193 | } | |
194 | }; | |
195 | ||
196 | /** | |
197 | * Converts an array buffer to a string | |
198 | * | |
199 | * @private | |
200 | * @param {ArrayBuffer} buf The buffer to convert | |
201 | * @param {Function} callback The function to call when conversion is complete | |
202 | */ | |
203 | TcpClient.prototype._arrayBufferToString = function(buf, callback) { | |
204 | var bb = new Blob([new Uint8Array(buf)]); | |
205 | var f = new FileReader(); | |
206 | f.onload = function(e) { | |
207 | callback(e.target.result); | |
208 | }; | |
209 | f.readAsText(bb); | |
210 | }; | |
211 | ||
212 | /** | |
213 | * Converts a string to an array buffer | |
214 | * | |
215 | * @private | |
216 | * @param {String} str The string to convert | |
217 | * @param {Function} callback The function to call when conversion is complete | |
218 | */ | |
219 | TcpClient.prototype._stringToArrayBuffer = function(str, callback) { | |
220 | var bb = new Blob([str]); | |
221 | var f = new FileReader(); | |
222 | f.onload = function(e) { | |
223 | callback(e.target.result); | |
224 | }; | |
225 | f.readAsArrayBuffer(bb); | |
226 | }; | |
227 | ||
228 | /** | |
229 | * Wrapper function for logging | |
230 | */ | |
231 | function log(msg) { | |
232 | //console.log('tcp-client:', msg); | |
233 | } | |
234 | ||
235 | /** | |
236 | * Wrapper function for error logging | |
237 | */ | |
238 | function error(msg) { | |
239 | console.error('tcp-client:', msg); | |
240 | } | |
241 | ||
242 | exports.TcpClient = TcpClient; | |
243 | ||
244 | })(window); |