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

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.
Jamie 2014/08/29 02:14:08 I think it's implicit that it's an object. How abo
Sergey Ulanov 2014/08/29 23:40:29 Done.
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_ = '';
Jamie 2014/08/29 02:14:08 These should be declared as @private. I'm also a b
Sergey Ulanov 2014/08/29 23:40:28 Yes. jscompiler can infer type from assignment her
27 this.onStateChangedCallback_ = onStateChangedCallback;
28 this.onIncomingStanzaCallback_ = onIncomingStanzaCallback;
29 this.socketId_ = -1;
30 this.state_ = remoting.XmppConnection.State.NOT_CONNECTED;
31 this.readPending_ = false;
32 this.sendPending_ = false;
33 this.starttlsPending_ = false;
Jamie 2014/08/29 02:14:08 s/tls/Tls/ for consistency with the method name.
Sergey Ulanov 2014/08/29 23:40:28 Done.
34 /** @type {Array.<ArrayBuffer>} */
35 this.sendQueue_ = [];
36 this.jid_ = '';
37 this.error_ = remoting.Error.NONE;
38 }
Jamie 2014/08/29 02:14:09 Semi-colons after functions declared by assignment
Sergey Ulanov 2014/08/29 23:40:28 Done. Can emacs be integrated with clang-format fo
39
40 /**
41 * @enum {number} XmppConnection states. Possible state transitions:
42 * NOT_CONNECTED -> CONNECTING (connect() called).
43 * CONNECTING -> HANDSHAKE (connected successfully).
44 * HANDSHAKE -> CONNECTED (connected successfully).
45 * CONNECTING -> FAILED (connection failed).
46 * HANDSHAKE -> FAILED (authentication failed).
47 * * -> CLOSED (dispose() called).
48 */
49 remoting.XmppConnection.State = {
50 NOT_CONNECTED: 0,
51 CONNECTING: 1,
52 HANDSHAKE: 2,
53 CONNECTED: 3,
54 FAILED: 4,
55 CLOSED: 5
56 };
57
58 /**
59 * @param {string} server
60 * @param {string} username
61 * @param {string} authToken
62 */
63 remoting.XmppConnection.prototype.connect =
64 function(server, username, authToken) {
65 if (this.state_ != remoting.XmppConnection.State.NOT_CONNECTED) {
66 console.error(
67 'remoting.XmppConnection.prototype.connect() can only be called once.');
Jamie 2014/08/29 02:14:08 The log message will include the file name, so I t
Sergey Ulanov 2014/08/29 23:40:29 Replaced with assert()
68 return;
69 }
70
71 this.error_ = remoting.Error.NONE;
72 this.server_ = server;
73 /** @type {remoting.XmppLoginHandler} */
74 this.loginHandler_ =
75 new remoting.XmppLoginHandler(username, authToken,
76 this.sendInternal_.bind(this),
77 this.startTls_.bind(this),
78 this.onHandshakeDone_.bind(this),
79 this.onIncomingStanzaCallback_,
80 this.onError_.bind(this));
81 chrome.socket.create("tcp", {}, this.onSocketCreated_.bind(this));
82 this.setState_(remoting.XmppConnection.State.CONNECTING);
83 }
84
85 /** @param {string} message */
86 remoting.XmppConnection.prototype.sendMessage = function(message) {
87 if (this.state_ != remoting.XmppConnection.State.CONNECTED) {
88 console.error(
Jamie 2014/08/29 02:14:08 As above, the caller should have some indication t
Sergey Ulanov 2014/08/29 23:40:29 Done.
89 'remoting.XmppConnection.prototype.sendMessage() is called when not ' +
90 'connected.');
91 return;
92 }
93
94 this.sendInternal_(message);
95 }
96
97 /** @return {remoting.XmppConnection.State} Current state */
98 remoting.XmppConnection.prototype.getState = function() {
99 return this.state_;
100 }
101
102 /** @return {remoting.Error} Error when in FAILED state. */
103 remoting.XmppConnection.prototype.getError = function() {
104 return this.error_;
105 }
106
107 /** @return {string} Current JID when in CONNECTED state. */
108 remoting.XmppConnection.prototype.getJid = function() {
109 return this.jid_;
110 }
111
112 remoting.XmppConnection.prototype.dispose = function() {
Jamie 2014/08/29 02:14:09 This might be better named disconnect() for symmet
Sergey Ulanov 2014/08/29 23:40:29 Yes, the intent here is to follow the Disposable p
113 this.closeSocket_();
114 this.setState_(remoting.XmppConnection.State.CLOSED);
115 }
116
117 /** @param {chrome.socket.CreateInfo} createInfo */
Jamie 2014/08/29 02:14:08 @private for this and other methods ending in an u
Sergey Ulanov 2014/08/29 23:40:28 Done.
118 remoting.XmppConnection.prototype.onSocketCreated_ = function(createInfo) {
119 // Check if connection was terminated.
Jamie 2014/08/29 02:14:08 I think this can only happen if the caller calls d
Sergey Ulanov 2014/08/29 23:40:28 Replaced with "Check if connection was destroyed"
120 if (this.state_ != remoting.XmppConnection.State.CONNECTING) {
121 chrome.socket.destroy(createInfo.socketId);
122 return;
123 }
124
125 this.socketId_ = createInfo.socketId;
126
127 var hostnameAndPort = this.server_.split(':', 2);
128 var hostname = hostnameAndPort[0];
129 var port =
130 (hostnameAndPort.length == 2) ? parseInt(hostnameAndPort[1], 10) : 5222;
131 chrome.socket.connect(
132 this.socketId_, hostname, port, this.onSocketConnected_.bind(this));
133 }
134
135 /** @param {number} result */
136 remoting.XmppConnection.prototype.onSocketConnected_ = function(result) {
137 // Check if connection was terminated.
138 if (this.state_ != remoting.XmppConnection.State.CONNECTING)
139 return;
Jamie 2014/08/29 02:14:09 Braces for single-line ifs for consistency, please
Sergey Ulanov 2014/08/29 23:40:29 No. It should have been destroyed when dispose() w
140
141 if (result != 0) {
142 this.onError_(remoting.Error.NETWORK_FAILURE,
143 'Failed to connect to ' + this.server_ + ': ' + result);
144 return;
145 }
146
147 this.setState_(remoting.XmppConnection.State.HANDSHAKE);
148
149 this.tryRead_();
150 this.loginHandler_.start();
151 }
152
153 remoting.XmppConnection.prototype.tryRead_ = function() {
154 base.debug.assert(!this.readPending_);
155 if ((this.state_ != remoting.XmppConnection.State.HANDSHAKE &&
156 this.state_ != remoting.XmppConnection.State.CONNECTED) ||
157 this.starttlsPending_) {
158 return;
159 }
Jamie 2014/08/29 02:14:08 Why an assert for readPending, but a conditional f
Sergey Ulanov 2014/08/29 23:40:29 Done.
160
161 this.readPending_ = true;
162 chrome.socket.read(this.socketId_, this.onRead_.bind(this));
163 }
164
165 /** @param {chrome.socket.ReadInfo} readInfo */
166 remoting.XmppConnection.prototype.onRead_ = function(readInfo) {
167 base.debug.assert(this.readPending_);
168 this.readPending_ = false;
169
170 if (readInfo.resultCode < 0) {
171 this.onError_(remoting.Error.NETWORK_FAILURE,
172 'Failed to receive from XMPP socket: ' + readInfo.resultCode);
173 return;
174 }
175
176 this.loginHandler_.onDataReceived(readInfo.data);
Jamie 2014/08/29 02:14:09 The name of this class implies that it just handle
Sergey Ulanov 2014/08/29 23:40:29 Refactored this code so that LoginHandler is used
177 this.tryRead_();
178 }
179
180 /** @param {string} text */
181 remoting.XmppConnection.prototype.sendInternal_ = function(text) {
182 this.sendQueue_.push(base.encodeUtf8(text));
183 this.doSend_();
184 }
185
186 remoting.XmppConnection.prototype.doSend_ = function() {
187 if (this.sendPending_)
188 return;
189 if (this.sendQueue_.length == 0)
190 return;
191
192 var data = this.sendQueue_[0]
193 this.sendPending_ = true;
194 chrome.socket.write(this.socketId_, data, this.onWrite_.bind(this));
195 }
196
197 /** @param {chrome.socket.WriteInfo} writeInfo */
198 remoting.XmppConnection.prototype.onWrite_ = function(writeInfo) {
199 base.debug.assert(this.sendPending_);
200 this.sendPending_ = false;
201
202 if (writeInfo.bytesWritten < 0) {
203 console.error('TCP write failed with error ' + writeInfo.bytesWritten);
Jamie 2014/08/29 02:14:08 You should call onError here so that this.error is
Sergey Ulanov 2014/08/29 23:40:29 Done.
204 this.setState_(remoting.XmppConnection.State.FAILED);
205 return;
206 }
207
208 base.debug.assert(this.sendQueue_.length > 0);
209
210 var data = this.sendQueue_[0]
211 base.debug.assert(writeInfo.bytesWritten <= data.byteLength);
Jamie 2014/08/29 02:14:09 What is this assert guarding against? You're alrea
Sergey Ulanov 2014/08/29 23:40:29 Its to detect bugs that cause head of sendQueue_ t
212 if (writeInfo.bytesWritten >= data.byteLength) {
213 this.sendQueue_.shift();
214 } else {
215 this.sendQueue_[0] = data.slice(data.byteLength - writeInfo.bytesWritten);
216 }
217
218 this.doSend_();
219 }
220
221 remoting.XmppConnection.prototype.startTls_ = function() {
222 base.debug.assert(!this.readPending_);
223 base.debug.assert(!this.starttlsPending_);
224
225 this.starttlsPending_ = true;
226 chrome.socket.secure(
227 this.socketId_, {}, this.onTlsStarted_.bind(this));
228 }
229
230 /** @param {number} resultCode */
231 remoting.XmppConnection.prototype.onTlsStarted_ = function(resultCode) {
232 base.debug.assert(this.starttlsPending_);
233 this.starttlsPending_ = false;
234
235 if (resultCode < 0) {
236 this.onError_(remoting.Error.NETWORK_FAILURE,
237 'Failed to start TLS: ' + resultCode);
238 return;
239 }
240
241 this.tryRead_();
242 this.loginHandler_.onTlsStarted();
243 }
244
245 /** @param {string} jid */
246 remoting.XmppConnection.prototype.onHandshakeDone_ = function(jid) {
247 this.jid_ = jid;
248 this.setState_(remoting.XmppConnection.State.CONNECTED);
249 }
250
251 /**
252 * @param {remoting.Error} error
253 * @param {string} text
254 */
255 remoting.XmppConnection.prototype.onError_ = function(error, text) {
256 console.error(text);
257 this.error_ = error;
258 this.closeSocket_();
259 this.setState_(remoting.XmppConnection.State.FAILED);
260 }
261
262 remoting.XmppConnection.prototype.closeSocket_ = function() {
263 if (this.socketId_ != -1) {
264 chrome.socket.destroy(this.socketId_);
265 this.socketId_ = -1;
266 }
267 }
268
269 /** @param {remoting.XmppConnection.State} newState */
270 remoting.XmppConnection.prototype.setState_ = function(newState) {
271 if (this.state_ != newState) {
272 this.state_ = newState;
273 this.onStateChangedCallback_(this.state_);
274 }
275 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698