Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(130)

Side by Side Diff: remoting/webapp/xmpp_connection.js

Issue 514343002: XMPP implementation in JavaScript. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 'use strict';
6
7 /** @suppress {duplicate} */
8 var remoting = remoting || {};
9
10 /**
11 * Object used to connect to XMPP server.
12 *
13 * TODO(sergeyu): Chrome provides two APIs for TCP sockets: chrome.socket and
14 * chrome.sockets.tcp . chrome.socket is deprecated but it's still used here
15 * because TLS support in chrome.sockets.tcp is currently broken, see
16 * crbug.com/403076 .
17 *
18 * @param {function(remoting.XmppConnection.State):void} onStateChangedCallback
19 * Callback to call on state change.
20 * @param {function(Element):void} onIncomingStanzaCallback Callback to call to
21 * handle incoming messages.
22 * @constructor
23 */
24 remoting.XmppConnection =
25 function(onStateChangedCallback, onIncomingStanzaCallback) {
26 this.server_ = '';
kelvinp 2014/08/29 01:37:09 @private. Same for other private members and func
Sergey Ulanov 2014/08/29 23:40:30 Done.
27 this.port_ = 0;
28 this.onStateChangedCallback_ = onStateChangedCallback;
29 this.onIncomingStanzaCallback_ = onIncomingStanzaCallback;
30 this.socketId_ = -1;
31 this.state_ = remoting.XmppConnection.State.NOT_CONNECTED;
32 this.readPending_ = false;
33 this.sendPending_ = false;
34 this.starttlsPending_ = false;
35 /** @type {Array.<ArrayBuffer>} */
36 this.sendQueue_ = [];
37 this.jid_ = '';
38 this.error_ = remoting.Error.NONE;
39 }
40
41 /**
42 * @enum {number} XmppConnection states. Possible state transitions:
43 * NOT_CONNECTED -> CONNECTING (connect() called).
44 * CONNECTING -> HANDSHAKE (connected successfully).
45 * HANDSHAKE -> CONNECTED (connected successfully).
kelvinp 2014/08/29 01:37:09 Nit: HANDSHAKE -> CONNECTED (Authenticated success
Sergey Ulanov 2014/08/29 23:40:29 Done.
46 * CONNECTING -> FAILED (connection failed).
47 * HANDSHAKE -> FAILED (authentication failed).
48 * * -> CLOSED (dispose() called).
49 */
50 remoting.XmppConnection.State = {
51 NOT_CONNECTED: 0,
52 CONNECTING: 1,
53 HANDSHAKE: 2,
54 CONNECTED: 3,
55 FAILED: 4,
56 CLOSED: 5
57 };
58
59 /**
60 * @param {string} server
61 * @param {string} username
62 * @param {string} authToken
63 */
64 remoting.XmppConnection.prototype.connect =
65 function(server, username, authToken) {
66 if (this.state_ != remoting.XmppConnection.State.NOT_CONNECTED) {
67 console.error(
68 'remoting.XmppConnection.prototype.connect() can only be called once.');
69 return;
70 }
71
72 this.error_ = remoting.Error.NONE;
73 var hostnameAndPort = server.split(':', 2);
74 this.server_ = hostnameAndPort[0];
75 this.port_ =
76 (hostnameAndPort.length == 2) ? parseInt(hostnameAndPort[1], 10) : 5222;
77
78 // The server name is passed as to attribute in the <stream>. When connecting
79 // to talk.google.com it affects the certificate the server will use for TLS:
80 // talk.google.com uses gmail certificate when specified server is gmail.com
81 // or googlemail.com and google.com cert otherwise. In the same time it
82 // doesn't accept talk.google.com as target server. Here we use google.com
83 // server name when authenticating to talk.google.com. This ensures that the
84 // server will use google.com cert which will be accepted by the TLS
85 // implementation in Chrome (TLS API doesn't allow specifying domain other
86 // than the one that was passed to connect()).
87 var xmppServer = this.server_;
88 if (xmppServer == 'talk.google.com')
89 xmppServer = 'google.com';
90
91 /** @type {remoting.XmppLoginHandler} */
92 this.loginHandler_ =
93 new remoting.XmppLoginHandler(xmppServer, username, authToken,
kelvinp 2014/08/29 01:37:10 Instead of binding private functions and handing t
Sergey Ulanov 2014/08/29 23:40:30 This would create circular dependency between Xmpp
94 this.sendInternal_.bind(this),
95 this.startTls_.bind(this),
96 this.onHandshakeDone_.bind(this),
97 this.onIncomingStanzaCallback_,
98 this.onError_.bind(this));
99 chrome.socket.create("tcp", {}, this.onSocketCreated_.bind(this));
100 this.setState_(remoting.XmppConnection.State.CONNECTING);
101 }
102
103 /** @param {string} message */
104 remoting.XmppConnection.prototype.sendMessage = function(message) {
105 if (this.state_ != remoting.XmppConnection.State.CONNECTED) {
106 console.error(
107 'remoting.XmppConnection.prototype.sendMessage() is called when not ' +
108 'connected.');
109 return;
110 }
111
112 this.sendInternal_(message);
113 }
114
115 /** @return {remoting.XmppConnection.State} Current state */
116 remoting.XmppConnection.prototype.getState = function() {
117 return this.state_;
118 }
119
120 /** @return {remoting.Error} Error when in FAILED state. */
121 remoting.XmppConnection.prototype.getError = function() {
122 return this.error_;
123 }
124
125 /** @return {string} Current JID when in CONNECTED state. */
126 remoting.XmppConnection.prototype.getJid = function() {
127 return this.jid_;
128 }
129
130 remoting.XmppConnection.prototype.dispose = function() {
131 this.closeSocket_();
132 this.setState_(remoting.XmppConnection.State.CLOSED);
133 }
134
135 /** @param {chrome.socket.CreateInfo} createInfo */
136 remoting.XmppConnection.prototype.onSocketCreated_ = function(createInfo) {
137 // Check if connection was terminated.
138 if (this.state_ != remoting.XmppConnection.State.CONNECTING) {
139 chrome.socket.destroy(createInfo.socketId);
140 return;
141 }
142
143 this.socketId_ = createInfo.socketId;
144
145 chrome.socket.connect(this.socketId_,
146 this.server_,
147 this.port_,
148 this.onSocketConnected_.bind(this));
149 }
150
151 /** @param {number} result */
152 remoting.XmppConnection.prototype.onSocketConnected_ = function(result) {
153 // Check if connection was terminated.
154 if (this.state_ != remoting.XmppConnection.State.CONNECTING)
155 return;
156
157 if (result != 0) {
158 this.onError_(remoting.Error.NETWORK_FAILURE,
159 'Failed to connect to ' + this.server_ + ': ' + result);
160 return;
161 }
162
163 this.setState_(remoting.XmppConnection.State.HANDSHAKE);
164
165 this.tryRead_();
166 this.loginHandler_.start();
167 }
168
169 remoting.XmppConnection.prototype.tryRead_ = function() {
kelvinp 2014/08/29 01:37:10 tryRead will cause onRead to call, which will call
Sergey Ulanov 2014/08/29 23:40:30 chrome.socket.read() never calls the callback sync
170 base.debug.assert(!this.readPending_);
171 if ((this.state_ != remoting.XmppConnection.State.HANDSHAKE &&
172 this.state_ != remoting.XmppConnection.State.CONNECTED) ||
173 this.starttlsPending_) {
174 return;
175 }
176
177 this.readPending_ = true;
178 chrome.socket.read(this.socketId_, this.onRead_.bind(this));
179 }
180
181 /** @param {chrome.socket.ReadInfo} readInfo */
182 remoting.XmppConnection.prototype.onRead_ = function(readInfo) {
183 base.debug.assert(this.readPending_);
184 this.readPending_ = false;
185
186 if (readInfo.resultCode < 0) {
187 this.onError_(remoting.Error.NETWORK_FAILURE,
188 'Failed to receive from XMPP socket: ' + readInfo.resultCode);
189 return;
190 }
191
192 this.loginHandler_.onDataReceived(readInfo.data);
193 this.tryRead_();
194 }
195
196 /** @param {string} text */
197 remoting.XmppConnection.prototype.sendInternal_ = function(text) {
198 this.sendQueue_.push(base.encodeUtf8(text));
199 this.doSend_();
200 }
201
202 remoting.XmppConnection.prototype.doSend_ = function() {
203 if (this.sendPending_)
204 return;
205 if (this.sendQueue_.length == 0)
206 return;
207
208 var data = this.sendQueue_[0]
209 this.sendPending_ = true;
210 chrome.socket.write(this.socketId_, data, this.onWrite_.bind(this));
211 }
212
213 /** @param {chrome.socket.WriteInfo} writeInfo */
214 remoting.XmppConnection.prototype.onWrite_ = function(writeInfo) {
215 base.debug.assert(this.sendPending_);
216 this.sendPending_ = false;
217
218 if (writeInfo.bytesWritten < 0) {
219 console.error('TCP write failed with error ' + writeInfo.bytesWritten);
220 this.setState_(remoting.XmppConnection.State.FAILED);
221 return;
222 }
223
224 base.debug.assert(this.sendQueue_.length > 0);
225
226 var data = this.sendQueue_[0]
227 base.debug.assert(writeInfo.bytesWritten <= data.byteLength);
kelvinp 2014/08/29 01:37:10 It is strange that we assert that bytesWritten <=
Sergey Ulanov 2014/08/29 23:40:29 replaced >= with == in the line below.
228 if (writeInfo.bytesWritten >= data.byteLength) {
229 this.sendQueue_.shift();
230 } else {
231 this.sendQueue_[0] = data.slice(data.byteLength - writeInfo.bytesWritten);
232 }
233
234 this.doSend_();
235 }
236
237 remoting.XmppConnection.prototype.startTls_ = function() {
kelvinp 2014/08/29 01:37:10 This function is marked as private but it is not c
Sergey Ulanov 2014/08/29 23:40:30 No. It's internal detail of XmppConnection() and i
238 base.debug.assert(!this.readPending_);
239 base.debug.assert(!this.starttlsPending_);
240
241 this.starttlsPending_ = true;
242 chrome.socket.secure(
243 this.socketId_, {}, this.onTlsStarted_.bind(this));
244 }
245
246 /** @param {number} resultCode */
247 remoting.XmppConnection.prototype.onTlsStarted_ = function(resultCode) {
248 base.debug.assert(this.starttlsPending_);
249 this.starttlsPending_ = false;
250
251 if (resultCode < 0) {
252 this.onError_(remoting.Error.NETWORK_FAILURE,
253 'Failed to start TLS: ' + resultCode);
254 return;
255 }
256
257 this.tryRead_();
258 this.loginHandler_.onTlsStarted();
259 }
260
261 /** @param {string} jid */
262 remoting.XmppConnection.prototype.onHandshakeDone_ = function(jid) {
263 this.jid_ = jid;
264 this.setState_(remoting.XmppConnection.State.CONNECTED);
265 }
266
267 /**
268 * @param {remoting.Error} error
269 * @param {string} text
270 */
271 remoting.XmppConnection.prototype.onError_ = function(error, text) {
272 console.error(text);
273 this.error_ = error;
274 this.closeSocket_();
275 this.setState_(remoting.XmppConnection.State.FAILED);
276 }
277
278 remoting.XmppConnection.prototype.closeSocket_ = function() {
279 if (this.socketId_ != -1) {
280 chrome.socket.destroy(this.socketId_);
281 this.socketId_ = -1;
282 }
283 }
284
285 /** @param {remoting.XmppConnection.State} newState */
286 remoting.XmppConnection.prototype.setState_ = function(newState) {
287 if (this.state_ != newState) {
288 this.state_ = newState;
289 this.onStateChangedCallback_(this.state_);
290 }
291 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698