diff options
author | sergeyu <sergeyu@chromium.org> | 2014-09-04 18:17:38 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-09-05 01:19:13 +0000 |
commit | a55e1da00db331b8199a6a396fae7fc7a0ad715c (patch) | |
tree | 0506d91f978665851660046ab4394c8380ef4b8b /remoting | |
parent | e3f27656a03bda92753dfdb1cf9ef7243d366e94 (diff) | |
download | chromium_src-a55e1da00db331b8199a6a396fae7fc7a0ad715c.zip chromium_src-a55e1da00db331b8199a6a396fae7fc7a0ad715c.tar.gz chromium_src-a55e1da00db331b8199a6a396fae7fc7a0ad715c.tar.bz2 |
Reduce number of roundtrips required in XMPP handshake
Previously XMPP handshake implemented in XmppLoginHandler required
7 roundtrips (excluding TLS handshake). With this change it will be
sending each message as soon as possible without waiting response to
the previous message. This effectivelly reduces number of required
roundtrips to 3.
BUG=274652
Review URL: https://codereview.chromium.org/534853002
Cr-Commit-Position: refs/heads/master@{#293389}
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/webapp/unittests/xmpp_login_handler_unittest.js | 94 | ||||
-rw-r--r-- | remoting/webapp/xmpp_login_handler.js | 127 |
2 files changed, 112 insertions, 109 deletions
diff --git a/remoting/webapp/unittests/xmpp_login_handler_unittest.js b/remoting/webapp/unittests/xmpp_login_handler_unittest.js index 339ceba..8a74652 100644 --- a/remoting/webapp/unittests/xmpp_login_handler_unittest.js +++ b/remoting/webapp/unittests/xmpp_login_handler_unittest.js @@ -38,20 +38,23 @@ function handshakeBase() { sinon.assert.calledWith( sendMessage, '<stream:stream to="google.com" version="1.0" xmlns="jabber:client" ' + - 'xmlns:stream="http://etherx.jabber.org/streams">'); + 'xmlns:stream="http://etherx.jabber.org/streams">' + + '<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>'); sendMessage.reset(); loginHandler.onDataReceived(base.encodeUtf8( '<stream:stream from="google.com" id="78A87C70559EF28A" version="1.0" ' + - 'xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client">' + - '<stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls">' + - '<required/></starttls><mechanisms ' + - 'xmlns="urn:ietf:params:xml:ns:xmpp-sasl">' + - '<mechanism>X-OAUTH2</mechanism><mechanism>X-GOOGLE-TOKEN</mechanism>' + - '</mechanisms></stream:features>')); - sinon.assert.calledWith( - sendMessage, '<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>'); - sendMessage.reset(); + 'xmlns:stream="http://etherx.jabber.org/streams"' + + 'xmlns="jabber:client">' + + '<stream:features>' + + '<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls">' + + '<required/>' + + '</starttls>' + + '<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">' + + '<mechanism>X-OAUTH2</mechanism>' + + '<mechanism>X-GOOGLE-TOKEN</mechanism>' + + '</mechanisms>' + + '</stream:features>')); loginHandler.onDataReceived( base.encodeUtf8('<proceed xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>')); @@ -59,29 +62,30 @@ function handshakeBase() { startTls.reset(); loginHandler.onTlsStarted(); - sinon.assert.calledWith( - sendMessage, - '<stream:stream to="google.com" version="1.0" xmlns="jabber:client" ' + - 'xmlns:stream="http://etherx.jabber.org/streams">'); - sendMessage.reset(); - - loginHandler.onDataReceived(base.encodeUtf8( - '<stream:stream from="google.com" id="DCDDE5171CB2154A" version="1.0" ' + - 'xmlns:stream="http://etherx.jabber.org/streams" ' + - 'xmlns="jabber:client"><stream:features>' + - '<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">' + - '<mechanism>X-OAUTH2</mechanism><mechanism>X-GOOGLE-TOKEN</mechanism>' + - '<mechanism>PLAIN</mechanism></mechanisms></stream:features>')); var cookie = window.btoa("\0" + testUsername + "\0" + testToken); sinon.assert.calledWith( sendMessage, + '<stream:stream to="google.com" version="1.0" xmlns="jabber:client" ' + + 'xmlns:stream="http://etherx.jabber.org/streams">' + '<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="X-OAUTH2" ' + 'auth:service="oauth2" auth:allow-generated-jid="true" ' + 'auth:client-uses-full-bind-result="true" ' + 'auth:allow-non-google-login="true" ' + 'xmlns:auth="http://www.google.com/talk/protocol/auth">' + cookie + - '</auth>'); + '</auth>'); sendMessage.reset(); + + loginHandler.onDataReceived(base.encodeUtf8( + '<stream:stream from="google.com" id="DCDDE5171CB2154A" version="1.0" ' + + 'xmlns:stream="http://etherx.jabber.org/streams" ' + + 'xmlns="jabber:client">' + + '<stream:features>' + + '<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">' + + '<mechanism>X-OAUTH2</mechanism>' + + '<mechanism>X-GOOGLE-TOKEN</mechanism>' + + '<mechanism>PLAIN</mechanism>' + + '</mechanisms>' + + '</stream:features>')); } test('should authenticate', function() { @@ -92,32 +96,32 @@ test('should authenticate', function() { sinon.assert.calledWith( sendMessage, '<stream:stream to="google.com" version="1.0" xmlns="jabber:client" ' + - 'xmlns:stream="http://etherx.jabber.org/streams">'); + 'xmlns:stream="http://etherx.jabber.org/streams">' + + '<iq type="set" id="0">' + + '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">' + + '<resource>chromoting</resource>' + + '</bind>' + + '</iq>' + + '<iq type="set" id="1">' + + '<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>' + + '</iq>'); sendMessage.reset(); loginHandler.onDataReceived(base.encodeUtf8( '<stream:stream from="google.com" id="104FA10576E2AA80" version="1.0" ' + - 'xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client">' + - '<stream:features><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/>' + - '<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>' + - '</stream:features>')); - sinon.assert.calledWith( - sendMessage, - '<iq type="set" id="0"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">' + - '<resource>chromoting</resource></bind></iq>'); - sendMessage.reset(); - - loginHandler.onDataReceived( - base.encodeUtf8('<iq id="0" type="result">' + - '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>' + - testUsername + '/chromoting52B4920E</jid></bind></iq>')); - sinon.assert.calledWith( - sendMessage, - '<iq type="set" id="1"><session ' + - 'xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>'); - sendMessage.reset(); + 'xmlns:stream="http://etherx.jabber.org/streams" ' + + 'xmlns="jabber:client">' + + '<stream:features>' + + '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/>' + + '<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>' + + '</stream:features>' + + '<iq id="0" type="result">' + + '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">' + + '<jid>' + testUsername + '/chromoting52B4920E</jid>' + + '</bind>' + + '</iq>' + + '<iq type="result" id="1"/>')); - loginHandler.onDataReceived(base.encodeUtf8('<iq type="result" id="1"/>')); sinon.assert.calledWith(onHandshakeDone); }); diff --git a/remoting/webapp/xmpp_login_handler.js b/remoting/webapp/xmpp_login_handler.js index bddc48e..f2034f4 100644 --- a/remoting/webapp/xmpp_login_handler.js +++ b/remoting/webapp/xmpp_login_handler.js @@ -66,53 +66,53 @@ remoting.XmppLoginHandler = function(server, * * Following messages are sent/received in each state: * INIT - * client -> server: Stream header - * START_SENT - * client <- server: Stream header with list of supported features which - * should include starttls. - * client -> server: <starttls> - * STARTTLS_SENT - * client <- server: <proceed> + * client -> server: Stream header + * client -> server: <starttls> + * WAIT_STREAM_HEADER + * client <- server: Stream header with list of supported features which + * should include starttls. + * WAIT_STARTTLS_RESPONSE + * client <- server: <proceed> * STARTING_TLS * TLS handshake * client -> server: Stream header - * START_SENT_AFTER_TLS + * client -> server: <auth> message with the OAuth2 token. + * WAIT_STREAM_HEADER_AFTER_TLS * client <- server: Stream header with list of supported authentication * methods which is expected to include X-OAUTH2 - * client -> server: <auth> message with the OAuth2 token. - * AUTH_SENT + * WAIT_AUTH_RESULT * client <- server: <success> or <failure> * client -> server: Stream header - * AUTH_ACCEPTED + * client -> server: <bind> + * client -> server: <iq><session/></iq> to start the session + * WAIT_STREAM_HEADER_AFTER_AUTH * client <- server: Stream header with list of features that should * include <bind>. - * client -> server: <bind> - * BIND_SENT + * WAIT_BIND_RESULT * client <- server: <bind> result with JID. - * client -> server: <iq><session/></iq> to start the session - * SESSION_IQ_SENT - * client <- server: iq result + * WAIT_SESSION_IQ_RESULT + * client <- server: result for <iq><session/></iq> * DONE * * @enum {number} */ remoting.XmppLoginHandler.State = { INIT: 0, - START_SENT: 1, - STARTTLS_SENT: 2, + WAIT_STREAM_HEADER: 1, + WAIT_STARTTLS_RESPONSE: 2, STARTING_TLS: 3, - START_SENT_AFTER_TLS: 4, - AUTH_SENT: 5, - AUTH_ACCEPTED: 6, - BIND_SENT: 7, - SESSION_IQ_SENT: 8, + WAIT_STREAM_HEADER_AFTER_TLS: 4, + WAIT_AUTH_RESULT: 5, + WAIT_STREAM_HEADER_AFTER_AUTH: 6, + WAIT_BIND_RESULT: 7, + WAIT_SESSION_IQ_RESULT: 8, DONE: 9, ERROR: 10 }; remoting.XmppLoginHandler.prototype.start = function() { - this.state_ = remoting.XmppLoginHandler.State.START_SENT; - this.startStream_(); + this.state_ = remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER; + this.startStream_('<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>'); } /** @param {ArrayBuffer} data */ @@ -130,17 +130,15 @@ remoting.XmppLoginHandler.prototype.onDataReceived = function(data) { */ remoting.XmppLoginHandler.prototype.onStanza_ = function(stanza) { switch (this.state_) { - case remoting.XmppLoginHandler.State.START_SENT: + case remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER: if (stanza.querySelector('features>starttls')) { - this.sendMessageCallback_( - '<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>'); - this.state_ = remoting.XmppLoginHandler.State.STARTTLS_SENT; + this.state_ = remoting.XmppLoginHandler.State.WAIT_STARTTLS_RESPONSE; } else { this.onError_(remoting.Error.UNEXPECTED, "Server doesn't support TLS."); } break; - case remoting.XmppLoginHandler.State.STARTTLS_SENT: + case remoting.XmppLoginHandler.State.WAIT_STARTTLS_RESPONSE: if (stanza.localName == "proceed") { this.state_ = remoting.XmppLoginHandler.State.STARTING_TLS; this.startTlsCallback_(); @@ -151,7 +149,7 @@ remoting.XmppLoginHandler.prototype.onStanza_ = function(stanza) { } break; - case remoting.XmppLoginHandler.State.START_SENT_AFTER_TLS: + case remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER_AFTER_TLS: var mechanisms = Array.prototype.map.call( stanza.querySelectorAll('features>mechanisms>mechanism'), /** @param {Element} m */ @@ -162,24 +160,23 @@ remoting.XmppLoginHandler.prototype.onStanza_ = function(stanza) { return; } - var cookie = window.btoa("\0" + this.username_ + "\0" + this.authToken_); + this.state_ = remoting.XmppLoginHandler.State.WAIT_AUTH_RESULT; - this.state_ = remoting.XmppLoginHandler.State.AUTH_SENT; - this.sendMessageCallback_( - '<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" ' + - 'mechanism="X-OAUTH2" auth:service="oauth2" ' + - 'auth:allow-generated-jid="true" ' + - 'auth:client-uses-full-bind-result="true" ' + - 'auth:allow-non-google-login="true" ' + - 'xmlns:auth="http://www.google.com/talk/protocol/auth">' + - cookie + - '</auth>'); break; - case remoting.XmppLoginHandler.State.AUTH_SENT: + case remoting.XmppLoginHandler.State.WAIT_AUTH_RESULT: if (stanza.localName == 'success') { - this.state_ = remoting.XmppLoginHandler.State.AUTH_ACCEPTED; - this.startStream_(); + this.state_ = + remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER_AFTER_AUTH; + this.startStream_( + '<iq type="set" id="0">' + + '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">' + + '<resource>chromoting</resource>'+ + '</bind>' + + '</iq>' + + '<iq type="set" id="1">' + + '<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>' + + '</iq>'); } else { this.onError_(remoting.Error.AUTHENTICATION_FAILED, 'Failed to authenticate: ' + @@ -187,22 +184,16 @@ remoting.XmppLoginHandler.prototype.onStanza_ = function(stanza) { } break; - case remoting.XmppLoginHandler.State.AUTH_ACCEPTED: + case remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER_AFTER_AUTH: if (stanza.querySelector('features>bind')) { - this.sendMessageCallback_( - '<iq type="set" id="0">' + - '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">' + - '<resource>chromoting</resource>'+ - '</bind>' + - '</iq>'); - this.state_ = remoting.XmppLoginHandler.State.BIND_SENT; + this.state_ = remoting.XmppLoginHandler.State.WAIT_BIND_RESULT; } else { this.onError_(remoting.Error.UNEXPECTED, "Server doesn't support bind after authentication."); } break; - case remoting.XmppLoginHandler.State.BIND_SENT: + case remoting.XmppLoginHandler.State.WAIT_BIND_RESULT: var jidElement = stanza.querySelector('iq>bind>jid'); if (stanza.getAttribute('id') != '0' || stanza.getAttribute('type') != 'result' || !jidElement) { @@ -212,14 +203,10 @@ remoting.XmppLoginHandler.prototype.onStanza_ = function(stanza) { return; } this.jid_ = jidElement.textContent; - this.sendMessageCallback_( - '<iq type="set" id="1">' + - '<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>' + - '</iq>'); - this.state_ = remoting.XmppLoginHandler.State.SESSION_IQ_SENT; + this.state_ = remoting.XmppLoginHandler.State.WAIT_SESSION_IQ_RESULT; break; - case remoting.XmppLoginHandler.State.SESSION_IQ_SENT: + case remoting.XmppLoginHandler.State.WAIT_SESSION_IQ_RESULT: if (stanza.getAttribute('id') != '1' || stanza.getAttribute('type') != 'result') { this.onError_(remoting.Error.UNEXPECTED, @@ -240,8 +227,18 @@ remoting.XmppLoginHandler.prototype.onStanza_ = function(stanza) { remoting.XmppLoginHandler.prototype.onTlsStarted = function() { base.debug.assert(this.state_ == remoting.XmppLoginHandler.State.STARTING_TLS); - this.state_ = remoting.XmppLoginHandler.State.START_SENT_AFTER_TLS; - this.startStream_(); + this.state_ = remoting.XmppLoginHandler.State.WAIT_STREAM_HEADER_AFTER_TLS; + var cookie = window.btoa("\0" + this.username_ + "\0" + this.authToken_); + + this.startStream_( + '<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" ' + + 'mechanism="X-OAUTH2" auth:service="oauth2" ' + + 'auth:allow-generated-jid="true" ' + + 'auth:client-uses-full-bind-result="true" ' + + 'auth:allow-non-google-login="true" ' + + 'xmlns:auth="http://www.google.com/talk/protocol/auth">' + + cookie + + '</auth>'); }; /** @@ -253,12 +250,14 @@ remoting.XmppLoginHandler.prototype.onParserError_ = function(text) { } /** + * @param {string} firstMessage Message to send after stream header. * @private */ -remoting.XmppLoginHandler.prototype.startStream_ = function() { +remoting.XmppLoginHandler.prototype.startStream_ = function(firstMessage) { this.sendMessageCallback_('<stream:stream to="' + this.server_ + '" version="1.0" xmlns="jabber:client" ' + - 'xmlns:stream="http://etherx.jabber.org/streams">'); + 'xmlns:stream="http://etherx.jabber.org/streams">' + + firstMessage); this.streamParser_ = new remoting.XmppStreamParser(); this.streamParser_.setCallbacks(this.onStanza_.bind(this), this.onParserError_.bind(this)); |