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

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

Issue 534853002: Reduce number of roundtrips required in XMPP handshake (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@webapp_tls
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
« no previous file with comments | « remoting/webapp/unittests/xmpp_login_handler_unittest.js ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 'use strict'; 5 'use strict';
6 6
7 /** @suppress {duplicate} */ 7 /** @suppress {duplicate} */
8 var remoting = remoting || {}; 8 var remoting = remoting || {};
9 9
10 /** 10 /**
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
59 /** @type {remoting.XmppStreamParser} @private */ 59 /** @type {remoting.XmppStreamParser} @private */
60 this.streamParser_ = null; 60 this.streamParser_ = null;
61 } 61 }
62 62
63 /** 63 /**
64 * States the handshake goes through. States are iterated from INIT to DONE 64 * States the handshake goes through. States are iterated from INIT to DONE
65 * sequentially, except for ERROR state which may be accepted at any point. 65 * sequentially, except for ERROR state which may be accepted at any point.
66 * 66 *
67 * Following messages are sent/received in each state: 67 * Following messages are sent/received in each state:
68 * INIT 68 * INIT
69 * client -> server: Stream header 69 * client -> server: Stream header
70 * START_SENT 70 * client -> server: <starttls>
71 * client <- server: Stream header with list of supported features which 71 * WAIT_STREAM_HEADER
72 * should include starttls. 72 * client <- server: Stream header with list of supported features which
73 * client -> server: <starttls> 73 * should include starttls.
74 * STARTTLS_SENT 74 * WAIT_STARTTLS_RESPONSE
75 * client <- server: <proceed> 75 * client <- server: <proceed>
76 * STARTING_TLS 76 * STARTING_TLS
77 * TLS handshake 77 * TLS handshake
78 * client -> server: Stream header 78 * client -> server: Stream header
79 * START_SENT_AFTER_TLS 79 * client -> server: <auth> message with the OAuth2 token.
80 * WAIT_STREAM_HEADER_AFTER_TLS
80 * client <- server: Stream header with list of supported authentication 81 * client <- server: Stream header with list of supported authentication
81 * methods which is expected to include X-OAUTH2 82 * methods which is expected to include X-OAUTH2
82 * client -> server: <auth> message with the OAuth2 token. 83 * WAIT_AUTH_RESULT
83 * AUTH_SENT
84 * client <- server: <success> or <failure> 84 * client <- server: <success> or <failure>
85 * client -> server: Stream header 85 * client -> server: Stream header
86 * AUTH_ACCEPTED 86 * client -> server: <bind>
87 * client -> server: <iq><session/></iq> to start the session
88 * WAIT_STREAM_HEADER_AFTER_AUTH
87 * client <- server: Stream header with list of features that should 89 * client <- server: Stream header with list of features that should
88 * include <bind>. 90 * include <bind>.
89 * client -> server: <bind> 91 * WAIT_BIND_RESULT
90 * BIND_SENT
91 * client <- server: <bind> result with JID. 92 * client <- server: <bind> result with JID.
92 * client -> server: <iq><session/></iq> to start the session 93 * WAIT_SESSION_IQ_RESULT
93 * SESSION_IQ_SENT 94 * client <- server: result for <iq><session/></iq>
94 * client <- server: iq result
95 * DONE 95 * DONE
96 * 96 *
97 * @enum {number} 97 * @enum {number}
98 */ 98 */
99 remoting.XmppLoginHandler.State = { 99 remoting.XmppLoginHandler.State = {
100 INIT: 0, 100 INIT: 0,
101 START_SENT: 1, 101 WAIT_STREAM_HEADER: 1,
102 STARTTLS_SENT: 2, 102 WAIT_STARTTLS_RESPONSE: 2,
103 STARTING_TLS: 3, 103 STARTING_TLS: 3,
104 START_SENT_AFTER_TLS: 4, 104 WAIT_STREAM_HEADER_AFTER_TLS: 4,
105 AUTH_SENT: 5, 105 WAIT_AUTH_RESULT: 5,
106 AUTH_ACCEPTED: 6, 106 WAIT_STREAM_HEADER_AFTER_AUTH: 6,
107 BIND_SENT: 7, 107 WAIT_BIND_RESULT: 7,
108 SESSION_IQ_SENT: 8, 108 WAIT_SESSION_IQ_RESULT: 8,
109 DONE: 9, 109 DONE: 9,
110 ERROR: 10 110 ERROR: 10
111 }; 111 };
112 112
113 remoting.XmppLoginHandler.prototype.start = function() { 113 remoting.XmppLoginHandler.prototype.start = function() {
114 this.state_ = remoting.XmppLoginHandler.State.START_SENT; 114 this.state_ = remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER;
115 this.startStream_(); 115 this.startStream_('<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>');
116 } 116 }
117 117
118 /** @param {ArrayBuffer} data */ 118 /** @param {ArrayBuffer} data */
119 remoting.XmppLoginHandler.prototype.onDataReceived = function(data) { 119 remoting.XmppLoginHandler.prototype.onDataReceived = function(data) {
120 base.debug.assert(this.state_ != remoting.XmppLoginHandler.State.INIT && 120 base.debug.assert(this.state_ != remoting.XmppLoginHandler.State.INIT &&
121 this.state_ != remoting.XmppLoginHandler.State.DONE && 121 this.state_ != remoting.XmppLoginHandler.State.DONE &&
122 this.state_ != remoting.XmppLoginHandler.State.ERROR); 122 this.state_ != remoting.XmppLoginHandler.State.ERROR);
123 123
124 this.streamParser_.appendData(data); 124 this.streamParser_.appendData(data);
125 } 125 }
126 126
127 /** 127 /**
128 * @param {Element} stanza 128 * @param {Element} stanza
129 * @private 129 * @private
130 */ 130 */
131 remoting.XmppLoginHandler.prototype.onStanza_ = function(stanza) { 131 remoting.XmppLoginHandler.prototype.onStanza_ = function(stanza) {
132 switch (this.state_) { 132 switch (this.state_) {
133 case remoting.XmppLoginHandler.State.START_SENT: 133 case remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER:
134 if (stanza.querySelector('features>starttls')) { 134 if (stanza.querySelector('features>starttls')) {
135 this.sendMessageCallback_( 135 this.state_ = remoting.XmppLoginHandler.State.WAIT_STARTTLS_RESPONSE;
136 '<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>');
137 this.state_ = remoting.XmppLoginHandler.State.STARTTLS_SENT;
138 } else { 136 } else {
139 this.onError_(remoting.Error.UNEXPECTED, "Server doesn't support TLS."); 137 this.onError_(remoting.Error.UNEXPECTED, "Server doesn't support TLS.");
140 } 138 }
141 break; 139 break;
142 140
143 case remoting.XmppLoginHandler.State.STARTTLS_SENT: 141 case remoting.XmppLoginHandler.State.WAIT_STARTTLS_RESPONSE:
144 if (stanza.localName == "proceed") { 142 if (stanza.localName == "proceed") {
145 this.state_ = remoting.XmppLoginHandler.State.STARTING_TLS; 143 this.state_ = remoting.XmppLoginHandler.State.STARTING_TLS;
146 this.startTlsCallback_(); 144 this.startTlsCallback_();
147 } else { 145 } else {
148 this.onError_(remoting.Error.UNEXPECTED, 146 this.onError_(remoting.Error.UNEXPECTED,
149 "Failed to start TLS: " + 147 "Failed to start TLS: " +
150 (new XMLSerializer().serializeToString(stanza))); 148 (new XMLSerializer().serializeToString(stanza)));
151 } 149 }
152 break; 150 break;
153 151
154 case remoting.XmppLoginHandler.State.START_SENT_AFTER_TLS: 152 case remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER_AFTER_TLS:
155 var mechanisms = Array.prototype.map.call( 153 var mechanisms = Array.prototype.map.call(
156 stanza.querySelectorAll('features>mechanisms>mechanism'), 154 stanza.querySelectorAll('features>mechanisms>mechanism'),
157 /** @param {Element} m */ 155 /** @param {Element} m */
158 function(m) { return m.textContent; }); 156 function(m) { return m.textContent; });
159 if (mechanisms.indexOf("X-OAUTH2")) { 157 if (mechanisms.indexOf("X-OAUTH2")) {
160 this.onError_(remoting.Error.UNEXPECTED, 158 this.onError_(remoting.Error.UNEXPECTED,
161 "OAuth2 is not supported by the server."); 159 "OAuth2 is not supported by the server.");
162 return; 160 return;
163 } 161 }
164 162
165 var cookie = window.btoa("\0" + this.username_ + "\0" + this.authToken_); 163 this.state_ = remoting.XmppLoginHandler.State.WAIT_AUTH_RESULT;
166 164
167 this.state_ = remoting.XmppLoginHandler.State.AUTH_SENT;
168 this.sendMessageCallback_(
169 '<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" ' +
170 'mechanism="X-OAUTH2" auth:service="oauth2" ' +
171 'auth:allow-generated-jid="true" ' +
172 'auth:client-uses-full-bind-result="true" ' +
173 'auth:allow-non-google-login="true" ' +
174 'xmlns:auth="http://www.google.com/talk/protocol/auth">' +
175 cookie +
176 '</auth>');
177 break; 165 break;
178 166
179 case remoting.XmppLoginHandler.State.AUTH_SENT: 167 case remoting.XmppLoginHandler.State.WAIT_AUTH_RESULT:
180 if (stanza.localName == 'success') { 168 if (stanza.localName == 'success') {
181 this.state_ = remoting.XmppLoginHandler.State.AUTH_ACCEPTED; 169 this.state_ =
182 this.startStream_(); 170 remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER_AFTER_AUTH;
171 this.startStream_(
172 '<iq type="set" id="0">' +
173 '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">' +
174 '<resource>chromoting</resource>'+
175 '</bind>' +
176 '</iq>' +
177 '<iq type="set" id="1">' +
178 '<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>' +
179 '</iq>');
183 } else { 180 } else {
184 this.onError_(remoting.Error.AUTHENTICATION_FAILED, 181 this.onError_(remoting.Error.AUTHENTICATION_FAILED,
185 'Failed to authenticate: ' + 182 'Failed to authenticate: ' +
186 (new XMLSerializer().serializeToString(stanza))); 183 (new XMLSerializer().serializeToString(stanza)));
187 } 184 }
188 break; 185 break;
189 186
190 case remoting.XmppLoginHandler.State.AUTH_ACCEPTED: 187 case remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER_AFTER_AUTH:
191 if (stanza.querySelector('features>bind')) { 188 if (stanza.querySelector('features>bind')) {
192 this.sendMessageCallback_( 189 this.state_ = remoting.XmppLoginHandler.State.WAIT_BIND_RESULT;
193 '<iq type="set" id="0">' +
194 '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">' +
195 '<resource>chromoting</resource>'+
196 '</bind>' +
197 '</iq>');
198 this.state_ = remoting.XmppLoginHandler.State.BIND_SENT;
199 } else { 190 } else {
200 this.onError_(remoting.Error.UNEXPECTED, 191 this.onError_(remoting.Error.UNEXPECTED,
201 "Server doesn't support bind after authentication."); 192 "Server doesn't support bind after authentication.");
202 } 193 }
203 break; 194 break;
204 195
205 case remoting.XmppLoginHandler.State.BIND_SENT: 196 case remoting.XmppLoginHandler.State.WAIT_BIND_RESULT:
206 var jidElement = stanza.querySelector('iq>bind>jid'); 197 var jidElement = stanza.querySelector('iq>bind>jid');
207 if (stanza.getAttribute('id') != '0' || 198 if (stanza.getAttribute('id') != '0' ||
208 stanza.getAttribute('type') != 'result' || !jidElement) { 199 stanza.getAttribute('type') != 'result' || !jidElement) {
209 this.onError_(remoting.Error.UNEXPECTED, 200 this.onError_(remoting.Error.UNEXPECTED,
210 'Received unexpected response to bind: ' + 201 'Received unexpected response to bind: ' +
211 (new XMLSerializer().serializeToString(stanza))); 202 (new XMLSerializer().serializeToString(stanza)));
212 return; 203 return;
213 } 204 }
214 this.jid_ = jidElement.textContent; 205 this.jid_ = jidElement.textContent;
215 this.sendMessageCallback_( 206 this.state_ = remoting.XmppLoginHandler.State.WAIT_SESSION_IQ_RESULT;
216 '<iq type="set" id="1">' +
217 '<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>' +
218 '</iq>');
219 this.state_ = remoting.XmppLoginHandler.State.SESSION_IQ_SENT;
220 break; 207 break;
221 208
222 case remoting.XmppLoginHandler.State.SESSION_IQ_SENT: 209 case remoting.XmppLoginHandler.State.WAIT_SESSION_IQ_RESULT:
223 if (stanza.getAttribute('id') != '1' || 210 if (stanza.getAttribute('id') != '1' ||
224 stanza.getAttribute('type') != 'result') { 211 stanza.getAttribute('type') != 'result') {
225 this.onError_(remoting.Error.UNEXPECTED, 212 this.onError_(remoting.Error.UNEXPECTED,
226 'Failed to start session: ' + 213 'Failed to start session: ' +
227 (new XMLSerializer().serializeToString(stanza))); 214 (new XMLSerializer().serializeToString(stanza)));
228 return; 215 return;
229 } 216 }
230 this.state_ = remoting.XmppLoginHandler.State.DONE; 217 this.state_ = remoting.XmppLoginHandler.State.DONE;
231 this.onHandshakeDoneCallback_(this.jid_, this.streamParser_); 218 this.onHandshakeDoneCallback_(this.jid_, this.streamParser_);
232 break; 219 break;
233 220
234 default: 221 default:
235 base.debug.assert(false); 222 base.debug.assert(false);
236 break; 223 break;
237 } 224 }
238 } 225 }
239 226
240 remoting.XmppLoginHandler.prototype.onTlsStarted = function() { 227 remoting.XmppLoginHandler.prototype.onTlsStarted = function() {
241 base.debug.assert(this.state_ == 228 base.debug.assert(this.state_ ==
242 remoting.XmppLoginHandler.State.STARTING_TLS); 229 remoting.XmppLoginHandler.State.STARTING_TLS);
243 this.state_ = remoting.XmppLoginHandler.State.START_SENT_AFTER_TLS; 230 this.state_ = remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER_AFTER_TLS;
244 this.startStream_(); 231 var cookie = window.btoa("\0" + this.username_ + "\0" + this.authToken_);
232
233 this.startStream_(
234 '<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" ' +
235 'mechanism="X-OAUTH2" auth:service="oauth2" ' +
236 'auth:allow-generated-jid="true" ' +
237 'auth:client-uses-full-bind-result="true" ' +
238 'auth:allow-non-google-login="true" ' +
239 'xmlns:auth="http://www.google.com/talk/protocol/auth">' +
240 cookie +
241 '</auth>');
245 }; 242 };
246 243
247 /** 244 /**
248 * @param {string} text 245 * @param {string} text
249 * @private 246 * @private
250 */ 247 */
251 remoting.XmppLoginHandler.prototype.onParserError_ = function(text) { 248 remoting.XmppLoginHandler.prototype.onParserError_ = function(text) {
252 this.onError_(remoting.Error.UNEXPECTED, text); 249 this.onError_(remoting.Error.UNEXPECTED, text);
253 } 250 }
254 251
255 /** 252 /**
253 * @param {string} firstMessage Message to send after stream header.
256 * @private 254 * @private
257 */ 255 */
258 remoting.XmppLoginHandler.prototype.startStream_ = function() { 256 remoting.XmppLoginHandler.prototype.startStream_ = function(firstMessage) {
259 this.sendMessageCallback_('<stream:stream to="' + this.server_ + 257 this.sendMessageCallback_('<stream:stream to="' + this.server_ +
260 '" version="1.0" xmlns="jabber:client" ' + 258 '" version="1.0" xmlns="jabber:client" ' +
261 'xmlns:stream="http://etherx.jabber.org/streams">'); 259 'xmlns:stream="http://etherx.jabber.org/streams">' +
260 firstMessage);
262 this.streamParser_ = new remoting.XmppStreamParser(); 261 this.streamParser_ = new remoting.XmppStreamParser();
263 this.streamParser_.setCallbacks(this.onStanza_.bind(this), 262 this.streamParser_.setCallbacks(this.onStanza_.bind(this),
264 this.onParserError_.bind(this)); 263 this.onParserError_.bind(this));
265 } 264 }
266 265
267 /** 266 /**
268 * @param {remoting.Error} error 267 * @param {remoting.Error} error
269 * @param {string} text 268 * @param {string} text
270 * @private 269 * @private
271 */ 270 */
272 remoting.XmppLoginHandler.prototype.onError_ = function(error, text) { 271 remoting.XmppLoginHandler.prototype.onError_ = function(error, text) {
273 if (this.state_ != remoting.XmppLoginHandler.State.ERROR) { 272 if (this.state_ != remoting.XmppLoginHandler.State.ERROR) {
274 this.onErrorCallback_(error, text); 273 this.onErrorCallback_(error, text);
275 this.state_ = remoting.XmppLoginHandler.State.ERROR; 274 this.state_ = remoting.XmppLoginHandler.State.ERROR;
276 } else { 275 } else {
277 console.error(text); 276 console.error(text);
278 } 277 }
279 } 278 }
OLDNEW
« no previous file with comments | « remoting/webapp/unittests/xmpp_login_handler_unittest.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698