Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 } | |
| OLD | NEW |