Commit 5b7c9e08 authored by jrw's avatar jrw Committed by Commit bot

Updated remoting.xhr API to use promises.

Removed access to the native XHR object used by the API.

This is a larger change than one might expect for two reasons: First,
because the native XHR object only allows the response content to be
retrieved while the onreadystatechange handler is executing, and
second, because the unit test for dns_blackhole_checker.js relied on
synchronous semantics which cannot be duplicated with promises because
when a promise is resolved, its "then" handlers are not called until
the next event cycle.

Review URL: https://codereview.chromium.org/1003433002

Cr-Commit-Position: refs/heads/master@{#321608}
parent c29919ce
...@@ -105,11 +105,11 @@ remoting.AppRemoting.prototype.start = function(connector, token) { ...@@ -105,11 +105,11 @@ remoting.AppRemoting.prototype.start = function(connector, token) {
/** @type {remoting.AppRemoting} */ /** @type {remoting.AppRemoting} */
var that = this; var that = this;
/** @param {XMLHttpRequest} xhr */ /** @param {!remoting.Xhr.Response} xhrResponse */
var parseAppHostResponse = function(xhr) { var parseAppHostResponse = function(xhrResponse) {
if (xhr.status == 200) { if (xhrResponse.status == 200) {
var response = /** @type {remoting.AppRemoting.AppHostResponse} */ var response = /** @type {remoting.AppRemoting.AppHostResponse} */
(base.jsonParseSafe(xhr.responseText)); (base.jsonParseSafe(xhrResponse.getText()));
if (response && if (response &&
response.status && response.status &&
response.status == 'done' && response.status == 'done' &&
...@@ -157,16 +157,16 @@ remoting.AppRemoting.prototype.start = function(connector, token) { ...@@ -157,16 +157,16 @@ remoting.AppRemoting.prototype.start = function(connector, token) {
// TODO(garykac) Start using remoting.Error.fromHttpStatus once it has // TODO(garykac) Start using remoting.Error.fromHttpStatus once it has
// been updated to properly report 'unknown' errors (rather than // been updated to properly report 'unknown' errors (rather than
// reporting them as AUTHENTICATION_FAILED). // reporting them as AUTHENTICATION_FAILED).
if (xhr.status == 0) { if (xhrResponse.status == 0) {
that.handleError(new remoting.Error( that.handleError(new remoting.Error(
remoting.Error.Tag.NETWORK_FAILURE)); remoting.Error.Tag.NETWORK_FAILURE));
} else if (xhr.status == 401) { } else if (xhrResponse.status == 401) {
that.handleError(new remoting.Error( that.handleError(new remoting.Error(
remoting.Error.Tag.AUTHENTICATION_FAILED)); remoting.Error.Tag.AUTHENTICATION_FAILED));
} else if (xhr.status == 403) { } else if (xhrResponse.status == 403) {
that.handleError(new remoting.Error( that.handleError(new remoting.Error(
remoting.Error.Tag.APP_NOT_AUTHORIZED)); remoting.Error.Tag.APP_NOT_AUTHORIZED));
} else if (xhr.status == 502 || xhr.status == 503) { } else if (xhrResponse.status == 502 || xhrResponse.status == 503) {
that.handleError(new remoting.Error( that.handleError(new remoting.Error(
remoting.Error.Tag.SERVICE_UNAVAILABLE)); remoting.Error.Tag.SERVICE_UNAVAILABLE));
} else { } else {
...@@ -175,12 +175,11 @@ remoting.AppRemoting.prototype.start = function(connector, token) { ...@@ -175,12 +175,11 @@ remoting.AppRemoting.prototype.start = function(connector, token) {
} }
}; };
remoting.xhr.start({ new remoting.Xhr({
method: 'POST', method: 'POST',
url: that.runApplicationUrl(), url: that.runApplicationUrl(),
onDone: parseAppHostResponse,
oauthToken: token oauthToken: token
}); }).start().then(parseAppHostResponse);
}; };
/** /**
......
...@@ -127,21 +127,19 @@ function onToken(token) { ...@@ -127,21 +127,19 @@ function onToken(token) {
'/applications/' + remoting.settings.getAppRemotingApplicationId() + '/applications/' + remoting.settings.getAppRemotingApplicationId() +
'/hosts/' + hostId + '/hosts/' + hostId +
'/reportIssue'; '/reportIssue';
/** @param {XMLHttpRequest} xhr */ var onDone = function(/** !remoting.Xhr.Response */ response) {
var onDone = function(xhr) { if (response.status >= 200 && response.status < 300) {
if (xhr.status >= 200 && xhr.status < 300) {
getUserInfo(); getUserInfo();
} else { } else {
showError(); showError();
} }
}; };
remoting.xhr.start({ new remoting.Xhr({
method: 'POST', method: 'POST',
url: uri, url: uri,
onDone: onDone,
jsonContent: body, jsonContent: body,
oauthToken: token oauthToken: token
}); }).start().then(onDone);
} else { } else {
getUserInfo(); getUserInfo();
} }
......
...@@ -38,7 +38,7 @@ remoting.DnsBlackholeChecker = function(signalStrategy) { ...@@ -38,7 +38,7 @@ remoting.DnsBlackholeChecker = function(signalStrategy) {
/** @private */ /** @private */
this.blackholeState_ = BlackholeState.PENDING; this.blackholeState_ = BlackholeState.PENDING;
/** @private {?XMLHttpRequest} */ /** @private {?remoting.Xhr} */
this.xhr_ = null; this.xhr_ = null;
}; };
...@@ -85,11 +85,11 @@ remoting.DnsBlackholeChecker.prototype.connect = function(server, ...@@ -85,11 +85,11 @@ remoting.DnsBlackholeChecker.prototype.connect = function(server,
this.signalStrategy_.connect(server, username, authToken); this.signalStrategy_.connect(server, username, authToken);
this.xhr_ = remoting.xhr.start({ this.xhr_ = new remoting.Xhr({
method: 'GET', method: 'GET',
url: remoting.DnsBlackholeChecker.URL_TO_REQUEST_, url: remoting.DnsBlackholeChecker.URL_TO_REQUEST_
onDone: this.onHttpRequestDone_.bind(this)
}); });
this.xhr_.start().then(this.onHttpRequestDone_.bind(this));
}; };
remoting.DnsBlackholeChecker.prototype.getState = function() { remoting.DnsBlackholeChecker.prototype.getState = function() {
...@@ -153,12 +153,12 @@ remoting.DnsBlackholeChecker.prototype.onWrappedSignalStrategyStateChanged_ = ...@@ -153,12 +153,12 @@ remoting.DnsBlackholeChecker.prototype.onWrappedSignalStrategyStateChanged_ =
}; };
/** /**
* @param {XMLHttpRequest} xhr * @param {!remoting.Xhr.Response} response
* @private * @private
*/ */
remoting.DnsBlackholeChecker.prototype.onHttpRequestDone_ = function(xhr) { remoting.DnsBlackholeChecker.prototype.onHttpRequestDone_ = function(response) {
this.xhr_ = null; this.xhr_ = null;
if (xhr.status >= 200 && xhr.status <= 299) { if (response.status >= 200 && response.status <= 299) {
console.log("DNS blackhole check succeeded."); console.log("DNS blackhole check succeeded.");
this.blackholeState_ = BlackholeState.OPEN; this.blackholeState_ = BlackholeState.OPEN;
if (this.signalStrategy_.getState() == if (this.signalStrategy_.getState() ==
...@@ -166,14 +166,15 @@ remoting.DnsBlackholeChecker.prototype.onHttpRequestDone_ = function(xhr) { ...@@ -166,14 +166,15 @@ remoting.DnsBlackholeChecker.prototype.onHttpRequestDone_ = function(xhr) {
this.setState_(remoting.SignalStrategy.State.CONNECTED); this.setState_(remoting.SignalStrategy.State.CONNECTED);
} }
} else { } else {
console.error("DNS blackhole check failed: " + xhr.status + " " + console.error("DNS blackhole check failed: " + response.status + " " +
xhr.statusText + ". Response URL: " + xhr.responseURL + response.statusText + ". Response URL: " +
". Response Text: " + xhr.responseText); response.url + ". Response Text: " +
response.getText());
this.blackholeState_ = BlackholeState.BLOCKED; this.blackholeState_ = BlackholeState.BLOCKED;
base.dispose(this.signalStrategy_); base.dispose(this.signalStrategy_);
this.setState_(remoting.SignalStrategy.State.FAILED); this.setState_(remoting.SignalStrategy.State.FAILED);
} }
} };
/** /**
* @param {remoting.SignalStrategy.State} newState * @param {remoting.SignalStrategy.State} newState
......
...@@ -23,13 +23,15 @@ var checker = null; ...@@ -23,13 +23,15 @@ var checker = null;
/** @type {remoting.MockSignalStrategy} */ /** @type {remoting.MockSignalStrategy} */
var signalStrategy = null; var signalStrategy = null;
var fakeXhrs;
/** @type {sinon.FakeXhr} */
var fakeXhr = null;
QUnit.module('dns_blackhole_checker', { QUnit.module('dns_blackhole_checker', {
beforeEach: function(assert) { beforeEach: function(assert) {
fakeXhrs = [];
sinon.useFakeXMLHttpRequest().onCreate = function(xhr) { sinon.useFakeXMLHttpRequest().onCreate = function(xhr) {
fakeXhrs.push(xhr); QUnit.equal(fakeXhr, null, 'exactly one XHR is issued');
fakeXhr = xhr;
}; };
onStateChange = sinon.spy(); onStateChange = sinon.spy();
...@@ -46,9 +48,8 @@ QUnit.module('dns_blackhole_checker', { ...@@ -46,9 +48,8 @@ QUnit.module('dns_blackhole_checker', {
sinon.assert.calledWith(signalStrategy.connect, 'server', 'username', sinon.assert.calledWith(signalStrategy.connect, 'server', 'username',
'authToken'); 'authToken');
assert.equal(fakeXhrs.length, 1, 'exactly one XHR is issued');
assert.equal( assert.equal(
fakeXhrs[0].url, remoting.DnsBlackholeChecker.URL_TO_REQUEST_, fakeXhr.url, remoting.DnsBlackholeChecker.URL_TO_REQUEST_,
'the correct URL is requested'); 'the correct URL is requested');
}, },
afterEach: function() { afterEach: function() {
...@@ -59,112 +60,136 @@ QUnit.module('dns_blackhole_checker', { ...@@ -59,112 +60,136 @@ QUnit.module('dns_blackhole_checker', {
onStateChange = null; onStateChange = null;
onIncomingStanzaCallback = null; onIncomingStanzaCallback = null;
checker = null; checker = null;
}, fakeXhr = null;
}
}); });
QUnit.test('success', QUnit.test('success',
function(assert) { function(assert) {
fakeXhrs[0].respond(200); function checkState(state) {
sinon.assert.notCalled(onStateChange);
[
remoting.SignalStrategy.State.CONNECTING,
remoting.SignalStrategy.State.HANDSHAKE,
remoting.SignalStrategy.State.CONNECTED
].forEach(function(state) {
signalStrategy.setStateForTesting(state); signalStrategy.setStateForTesting(state);
sinon.assert.calledWith(onStateChange, state); sinon.assert.calledWith(onStateChange, state);
assert.equal(checker.getState(), state); assert.equal(checker.getState(), state);
}
return base.SpyPromise.run(function() {
fakeXhr.respond(200);
}).then(function() {
sinon.assert.notCalled(onStateChange);
checkState(remoting.SignalStrategy.State.CONNECTING);
checkState(remoting.SignalStrategy.State.HANDSHAKE);
checkState(remoting.SignalStrategy.State.CONNECTED);
}); });
} });
);
QUnit.test('http response after connected', QUnit.test('http response after connected',
function(assert) { function(assert) {
[ function checkState(state) {
remoting.SignalStrategy.State.CONNECTING,
remoting.SignalStrategy.State.HANDSHAKE,
].forEach(function(state) {
signalStrategy.setStateForTesting(state); signalStrategy.setStateForTesting(state);
sinon.assert.calledWith(onStateChange, state); sinon.assert.calledWith(onStateChange, state);
assert.equal(checker.getState(), state); assert.equal(checker.getState(), state);
}); }
checkState(remoting.SignalStrategy.State.CONNECTING);
checkState(remoting.SignalStrategy.State.HANDSHAKE);
onStateChange.reset(); onStateChange.reset();
// Verify that DnsBlackholeChecker stays in HANDSHAKE state even if the // Verify that DnsBlackholeChecker stays in HANDSHAKE state even if the
// signal strategy has connected. // signal strategy has connected.
signalStrategy.setStateForTesting(remoting.SignalStrategy.State.CONNECTED); return base.SpyPromise.run(function() {
sinon.assert.notCalled(onStateChange); signalStrategy.setStateForTesting(
remoting.SignalStrategy.State.CONNECTED);
}).then(function() {
sinon.assert.notCalled(onStateChange);
assert.equal(checker.getState(), remoting.SignalStrategy.State.HANDSHAKE); assert.equal(checker.getState(), remoting.SignalStrategy.State.HANDSHAKE);
// Verify that DnsBlackholeChecker goes to CONNECTED state after the // Verify that DnsBlackholeChecker goes to CONNECTED state after the
// the HTTP request has succeeded. // the HTTP request has succeeded.
fakeXhrs[0].respond(200); return base.SpyPromise.run(function() {
sinon.assert.calledWith(onStateChange, fakeXhr.respond(200);
remoting.SignalStrategy.State.CONNECTED); });
} }).then(function() {
); sinon.assert.calledWith(onStateChange,
remoting.SignalStrategy.State.CONNECTED);
});
});
QUnit.test('connect failed', QUnit.test('connect failed',
function(assert) { function(assert) {
fakeXhrs[0].respond(200); function checkState(state) {
sinon.assert.notCalled(onStateChange);
[
remoting.SignalStrategy.State.CONNECTING,
remoting.SignalStrategy.State.FAILED
].forEach(function(state) {
signalStrategy.setStateForTesting(state); signalStrategy.setStateForTesting(state);
sinon.assert.calledWith(onStateChange, state); sinon.assert.calledWith(onStateChange, state);
};
return base.SpyPromise.run(function() {
fakeXhr.respond(200);
}).then(function() {
sinon.assert.notCalled(onStateChange);
checkState(remoting.SignalStrategy.State.CONNECTING);
checkState(remoting.SignalStrategy.State.FAILED);
}); });
} });
);
QUnit.test('blocked', QUnit.test('blocked',
function(assert) { function(assert) {
fakeXhrs[0].respond(400); function checkState(state) {
sinon.assert.calledWith(onStateChange,
remoting.SignalStrategy.State.FAILED);
assert.equal(checker.getError().getTag(), assert.equal(checker.getError().getTag(),
remoting.Error.Tag.NOT_AUTHORIZED); remoting.Error.Tag.NOT_AUTHORIZED);
onStateChange.reset(); onStateChange.reset();
[
remoting.SignalStrategy.State.CONNECTING,
remoting.SignalStrategy.State.HANDSHAKE,
remoting.SignalStrategy.State.CONNECTED
].forEach(function(state) {
signalStrategy.setStateForTesting(state); signalStrategy.setStateForTesting(state);
sinon.assert.notCalled(onStateChange); sinon.assert.notCalled(onStateChange);
assert.equal(checker.getState(), remoting.SignalStrategy.State.FAILED); assert.equal(checker.getState(),
checker.getState(),
remoting.SignalStrategy.State.FAILED,
'checker state is still FAILED');
};
return base.SpyPromise.run(function() {
fakeXhr.respond(400);
}).then(function() {
sinon.assert.calledWith(
onStateChange, remoting.SignalStrategy.State.FAILED);
assert.equal(
checker.getError().getTag(),
remoting.Error.Tag.NOT_AUTHORIZED,
'checker error is NOT_AUTHORIZED');
checkState(remoting.SignalStrategy.State.CONNECTING);
checkState(remoting.SignalStrategy.State.HANDSHAKE);
checkState(remoting.SignalStrategy.State.FAILED);
}); });
} });
);
QUnit.test('blocked after connected', QUnit.test('blocked after connected',
function(assert) { function(assert) {
[ function checkState(state) {
remoting.SignalStrategy.State.CONNECTING,
remoting.SignalStrategy.State.HANDSHAKE,
].forEach(function(state) {
signalStrategy.setStateForTesting(state); signalStrategy.setStateForTesting(state);
sinon.assert.calledWith(onStateChange, state); sinon.assert.calledWith(onStateChange, state);
assert.equal(checker.getState(), state); assert.equal(checker.getState(), state);
}); };
checkState(remoting.SignalStrategy.State.CONNECTING);
checkState(remoting.SignalStrategy.State.HANDSHAKE);
onStateChange.reset(); onStateChange.reset();
// Verify that DnsBlackholeChecker stays in HANDSHAKE state even if the // Verify that DnsBlackholeChecker stays in HANDSHAKE state even
// signal strategy has connected. // if the signal strategy has connected.
signalStrategy.setStateForTesting(remoting.SignalStrategy.State.CONNECTED); return base.SpyPromise.run(function() {
sinon.assert.notCalled(onStateChange); signalStrategy.setStateForTesting(
remoting.SignalStrategy.State.CONNECTED);
}).then(function() {
sinon.assert.notCalled(onStateChange);
assert.equal(checker.getState(), remoting.SignalStrategy.State.HANDSHAKE); assert.equal(checker.getState(), remoting.SignalStrategy.State.HANDSHAKE);
// Verify that DnsBlackholeChecker goes to FAILED state after it gets the // Verify that DnsBlackholeChecker goes to FAILED state after it
// blocked HTTP response. // gets the blocked HTTP response.
fakeXhrs[0].respond(400); return base.SpyPromise.run(function() {
sinon.assert.calledWith(onStateChange, fakeXhr.respond(400);
remoting.SignalStrategy.State.FAILED); });
}).then(function() {
sinon.assert.calledWith(onStateChange,
remoting.SignalStrategy.State.FAILED);
assert.ok(checker.getError().hasTag(remoting.Error.Tag.NOT_AUTHORIZED)); assert.ok(checker.getError().hasTag(remoting.Error.Tag.NOT_AUTHORIZED));
});
} }
); );
......
...@@ -234,14 +234,14 @@ remoting.HostController.prototype.start = function(hostPin, consent, onDone, ...@@ -234,14 +234,14 @@ remoting.HostController.prototype.start = function(hostPin, consent, onDone,
* @param {string} hostName * @param {string} hostName
* @param {string} publicKey * @param {string} publicKey
* @param {string} privateKey * @param {string} privateKey
* @param {XMLHttpRequest} xhr * @param {!remoting.Xhr.Response} response
*/ */
function onRegistered( function onRegistered(
hostName, publicKey, privateKey, xhr) { hostName, publicKey, privateKey, response) {
var success = (xhr.status == 200); var success = (response.status == 200);
if (success) { if (success) {
var result = base.jsonParseSafe(xhr.responseText); var result = base.jsonParseSafe(response.getText());
if ('data' in result && 'authorizationCode' in result['data']) { if ('data' in result && 'authorizationCode' in result['data']) {
that.hostDaemonFacade_.getCredentialsFromAuthCode( that.hostDaemonFacade_.getCredentialsFromAuthCode(
result['data']['authorizationCode'], result['data']['authorizationCode'],
...@@ -260,8 +260,8 @@ remoting.HostController.prototype.start = function(hostPin, consent, onDone, ...@@ -260,8 +260,8 @@ remoting.HostController.prototype.start = function(hostPin, consent, onDone,
}); });
} }
} else { } else {
console.log('Failed to register the host. Status: ' + xhr.status + console.log('Failed to register the host. Status: ' + response.status +
' response: ' + xhr.responseText); ' response: ' + response.getText());
onError(new remoting.Error(remoting.Error.Tag.REGISTRATION_FAILED)); onError(new remoting.Error(remoting.Error.Tag.REGISTRATION_FAILED));
} }
} }
...@@ -281,16 +281,15 @@ remoting.HostController.prototype.start = function(hostPin, consent, onDone, ...@@ -281,16 +281,15 @@ remoting.HostController.prototype.start = function(hostPin, consent, onDone,
publicKey: publicKey publicKey: publicKey
} }; } };
remoting.xhr.start({ new remoting.Xhr({
method: 'POST', method: 'POST',
url: remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts', url: remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts',
urlParams: { urlParams: {
hostClientId: hostClientId hostClientId: hostClientId
}, },
onDone: onRegistered.bind(null, hostName, publicKey, privateKey),
jsonContent: newHostDetails, jsonContent: newHostDetails,
oauthToken: oauthToken oauthToken: oauthToken
}); }).start().then(onRegistered.bind(null, hostName, publicKey, privateKey));
} }
/** /**
......
...@@ -28,17 +28,16 @@ remoting.HostListApiImpl = function() { ...@@ -28,17 +28,16 @@ remoting.HostListApiImpl = function() {
* @param {function(!remoting.Error):void} onError * @param {function(!remoting.Error):void} onError
*/ */
remoting.HostListApiImpl.prototype.get = function(onDone, onError) { remoting.HostListApiImpl.prototype.get = function(onDone, onError) {
/** @type {function(XMLHttpRequest):void} */ /** @type {function(!remoting.Xhr.Response):void} */
var parseHostListResponse = var parseHostListResponse =
this.parseHostListResponse_.bind(this, onDone, onError); this.parseHostListResponse_.bind(this, onDone, onError);
/** @param {string} token */ /** @param {string} token */
var onToken = function(token) { var onToken = function(token) {
remoting.xhr.start({ new remoting.Xhr({
method: 'GET', method: 'GET',
url: remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts', url: remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts',
onDone: parseHostListResponse,
oauthToken: token oauthToken: token
}); }).start().then(parseHostListResponse);
}; };
remoting.identity.getToken().then(onToken, remoting.Error.handler(onError)); remoting.identity.getToken().then(onToken, remoting.Error.handler(onError));
}; };
...@@ -63,13 +62,12 @@ remoting.HostListApiImpl.prototype.put = ...@@ -63,13 +62,12 @@ remoting.HostListApiImpl.prototype.put =
'publicKey': hostPublicKey 'publicKey': hostPublicKey
} }
}; };
remoting.xhr.start({ new remoting.Xhr({
method: 'PUT', method: 'PUT',
url: remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts/' + hostId, url: remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts/' + hostId,
onDone: remoting.xhr.defaultResponse(onDone, onError),
jsonContent: newHostDetails, jsonContent: newHostDetails,
oauthToken: token oauthToken: token
}); }).start().then(remoting.Xhr.defaultResponse(onDone, onError));
}; };
remoting.identity.getToken().then(onToken, remoting.Error.handler(onError)); remoting.identity.getToken().then(onToken, remoting.Error.handler(onError));
}; };
...@@ -84,13 +82,12 @@ remoting.HostListApiImpl.prototype.put = ...@@ -84,13 +82,12 @@ remoting.HostListApiImpl.prototype.put =
remoting.HostListApiImpl.prototype.remove = function(hostId, onDone, onError) { remoting.HostListApiImpl.prototype.remove = function(hostId, onDone, onError) {
/** @param {string} token */ /** @param {string} token */
var onToken = function(token) { var onToken = function(token) {
remoting.xhr.start({ new remoting.Xhr({
method: 'DELETE', method: 'DELETE',
url: remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts/' + hostId, url: remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts/' + hostId,
onDone: remoting.xhr.defaultResponse(onDone, onError,
[remoting.Error.Tag.NOT_FOUND]),
oauthToken: token oauthToken: token
}); }).start().then(remoting.Xhr.defaultResponse(
onDone, onError, [remoting.Error.Tag.NOT_FOUND]));
}; };
remoting.identity.getToken().then(onToken, remoting.Error.handler(onError)); remoting.identity.getToken().then(onToken, remoting.Error.handler(onError));
}; };
...@@ -102,19 +99,19 @@ remoting.HostListApiImpl.prototype.remove = function(hostId, onDone, onError) { ...@@ -102,19 +99,19 @@ remoting.HostListApiImpl.prototype.remove = function(hostId, onDone, onError) {
* *
* @param {function(Array<remoting.Host>):void} onDone * @param {function(Array<remoting.Host>):void} onDone
* @param {function(!remoting.Error):void} onError * @param {function(!remoting.Error):void} onError
* @param {XMLHttpRequest} xhr * @param {!remoting.Xhr.Response} response
* @private * @private
*/ */
remoting.HostListApiImpl.prototype.parseHostListResponse_ = remoting.HostListApiImpl.prototype.parseHostListResponse_ =
function(onDone, onError, xhr) { function(onDone, onError, response) {
if (xhr.status == 200) { if (response.status == 200) {
var response = /** @type {{data: {items: Array}}} */ var obj = /** @type {{data: {items: Array}}} */
(base.jsonParseSafe(xhr.responseText)); (base.jsonParseSafe(response.getText()));
if (!response || !response.data) { if (!obj || !obj.data) {
console.error('Invalid "hosts" response from server.'); console.error('Invalid "hosts" response from server.');
onError(remoting.Error.unexpected()); onError(remoting.Error.unexpected());
} else { } else {
var items = response.data.items || []; var items = obj.data.items || [];
var hosts = items.map( var hosts = items.map(
function(/** Object */ item) { function(/** Object */ item) {
var host = new remoting.Host(); var host = new remoting.Host();
...@@ -134,7 +131,7 @@ remoting.HostListApiImpl.prototype.parseHostListResponse_ = ...@@ -134,7 +131,7 @@ remoting.HostListApiImpl.prototype.parseHostListResponse_ =
onDone(hosts); onDone(hosts);
} }
} else { } else {
onError(remoting.Error.fromHttpStatus(xhr.status)); onError(remoting.Error.fromHttpStatus(response.status));
} }
}; };
...@@ -142,4 +139,3 @@ remoting.HostListApiImpl.prototype.parseHostListResponse_ = ...@@ -142,4 +139,3 @@ remoting.HostListApiImpl.prototype.parseHostListResponse_ =
remoting.hostListApi = new remoting.HostListApiImpl(); remoting.hostListApi = new remoting.HostListApiImpl();
})(); })();
...@@ -54,8 +54,8 @@ remoting.It2MeConnectFlow.prototype.connect_ = function(accessCode) { ...@@ -54,8 +54,8 @@ remoting.It2MeConnectFlow.prototype.connect_ = function(accessCode) {
return remoting.identity.getToken(); return remoting.identity.getToken();
}).then(function(/** string */ token) { }).then(function(/** string */ token) {
return that.getHostInfo_(token); return that.getHostInfo_(token);
}).then(function(/** XMLHttpRequest */ xhr) { }).then(function(/** !remoting.Xhr.Response */ response) {
return that.onHostInfo_(xhr); return that.onHostInfo_(response);
}).then(function(/** remoting.Host */ host) { }).then(function(/** remoting.Host */ host) {
that.sessionConnector_.connect( that.sessionConnector_.connect(
remoting.DesktopConnectedView.Mode.IT2ME, remoting.DesktopConnectedView.Mode.IT2ME,
...@@ -88,33 +88,31 @@ remoting.It2MeConnectFlow.prototype.verifyAccessCode_ = function(accessCode) { ...@@ -88,33 +88,31 @@ remoting.It2MeConnectFlow.prototype.verifyAccessCode_ = function(accessCode) {
* Continues an IT2Me connection once an access token has been obtained. * Continues an IT2Me connection once an access token has been obtained.
* *
* @param {string} token An OAuth2 access token. * @param {string} token An OAuth2 access token.
* @return {Promise<XMLHttpRequest>} * @return {Promise<!remoting.Xhr.Response>}
* @private * @private
*/ */
remoting.It2MeConnectFlow.prototype.getHostInfo_ = function(token) { remoting.It2MeConnectFlow.prototype.getHostInfo_ = function(token) {
var that = this; var that = this;
return new Promise(function(resolve) { return new remoting.Xhr({
remoting.xhr.start({ method: 'GET',
method: 'GET', url: remoting.settings.DIRECTORY_API_BASE_URL + '/support-hosts/' +
url: remoting.settings.DIRECTORY_API_BASE_URL + '/support-hosts/' + encodeURIComponent(that.hostId_),
encodeURIComponent(that.hostId_), oauthToken: token
onDone: resolve, }).start();
oauthToken: token
});
});
}; };
/** /**
* Continues an IT2Me connection once the host JID has been looked up. * Continues an IT2Me connection once the host JID has been looked up.
* *
* @param {XMLHttpRequest} xhr The server response to the support-hosts query. * @param {!remoting.Xhr.Response} xhrResponse The server response to the
* support-hosts query.
* @return {!Promise<!remoting.Host>} Rejects on error. * @return {!Promise<!remoting.Host>} Rejects on error.
* @private * @private
*/ */
remoting.It2MeConnectFlow.prototype.onHostInfo_ = function(xhr) { remoting.It2MeConnectFlow.prototype.onHostInfo_ = function(xhrResponse) {
if (xhr.status == 200) { if (xhrResponse.status == 200) {
var response = /** @type {{data: {jabberId: string, publicKey: string}}} */ var response = /** @type {{data: {jabberId: string, publicKey: string}}} */
(base.jsonParseSafe(xhr.responseText)); (base.jsonParseSafe(xhrResponse.getText()));
if (response && response.data && if (response && response.data &&
response.data.jabberId && response.data.publicKey) { response.data.jabberId && response.data.publicKey) {
var host = new remoting.Host(); var host = new remoting.Host();
...@@ -128,11 +126,12 @@ remoting.It2MeConnectFlow.prototype.onHostInfo_ = function(xhr) { ...@@ -128,11 +126,12 @@ remoting.It2MeConnectFlow.prototype.onHostInfo_ = function(xhr) {
return Promise.reject(remoting.Error.unexpected()); return Promise.reject(remoting.Error.unexpected());
} }
} else { } else {
return Promise.reject(translateSupportHostsError(xhr.status)); return Promise.reject(translateSupportHostsError(xhrResponse.status));
} }
}; };
/** /**
* TODO(jrw): Replace with remoting.Error.fromHttpStatus.
* @param {number} error An HTTP error code returned by the support-hosts * @param {number} error An HTTP error code returned by the support-hosts
* endpoint. * endpoint.
* @return {remoting.Error} The equivalent remoting.Error code. * @return {remoting.Error} The equivalent remoting.Error code.
......
...@@ -250,7 +250,7 @@ remoting.OAuth2.prototype.onTokens_ = ...@@ -250,7 +250,7 @@ remoting.OAuth2.prototype.onTokens_ =
remoting.OAuth2.prototype.getAuthorizationCode = function(onDone) { remoting.OAuth2.prototype.getAuthorizationCode = function(onDone) {
var xsrf_token = base.generateXsrfToken(); var xsrf_token = base.generateXsrfToken();
var GET_CODE_URL = this.getOAuth2AuthEndpoint_() + '?' + var GET_CODE_URL = this.getOAuth2AuthEndpoint_() + '?' +
remoting.xhr.urlencodeParamHash({ remoting.Xhr.urlencodeParamHash({
'client_id': this.getClientId_(), 'client_id': this.getClientId_(),
'redirect_uri': this.getRedirectUri_(), 'redirect_uri': this.getRedirectUri_(),
'scope': this.SCOPE_, 'scope': this.SCOPE_,
......
...@@ -75,37 +75,36 @@ remoting.OAuth2ApiImpl.prototype.interpretXhrStatus_ = ...@@ -75,37 +75,36 @@ remoting.OAuth2ApiImpl.prototype.interpretXhrStatus_ =
*/ */
remoting.OAuth2ApiImpl.prototype.refreshAccessToken = function( remoting.OAuth2ApiImpl.prototype.refreshAccessToken = function(
onDone, onError, clientId, clientSecret, refreshToken) { onDone, onError, clientId, clientSecret, refreshToken) {
/** @param {XMLHttpRequest} xhr */ /** @param {!remoting.Xhr.Response} response */
var onResponse = function(xhr) { var onResponse = function(response) {
if (xhr.status == 200) { if (response.status == 200) {
try { try {
// Don't use base.jsonParseSafe here unless you also include base.js, // Don't use base.jsonParseSafe here unless you also include base.js,
// otherwise this won't work from the OAuth trampoline. // otherwise this won't work from the OAuth trampoline.
// TODO(jamiewalch): Fix this once we're no longer using the trampoline. // TODO(jamiewalch): Fix this once we're no longer using the trampoline.
var tokens = JSON.parse(xhr.responseText); var tokens = JSON.parse(response.getText());
onDone(tokens['access_token'], tokens['expires_in']); onDone(tokens['access_token'], tokens['expires_in']);
} catch (/** @type {Error} */ err) { } catch (/** @type {Error} */ err) {
console.error('Invalid "token" response from server:', err); console.error('Invalid "token" response from server:', err);
onError(remoting.Error.unexpected()); onError(remoting.Error.unexpected());
} }
} else { } else {
console.error('Failed to refresh token. Status: ' + xhr.status + console.error('Failed to refresh token. Status: ' + response.status +
' response: ' + xhr.responseText); ' response: ' + response.getText());
onError(fromHttpStatus(xhr.status)); onError(remoting.Error.fromHttpStatus(response.status));
} }
}; };
remoting.xhr.start({ new remoting.Xhr({
method: 'POST', method: 'POST',
url: this.getOAuth2TokenEndpoint_(), url: this.getOAuth2TokenEndpoint_(),
onDone: onResponse,
formContent: { formContent: {
'client_id': clientId, 'client_id': clientId,
'client_secret': clientSecret, 'client_secret': clientSecret,
'refresh_token': refreshToken, 'refresh_token': refreshToken,
'grant_type': 'refresh_token' 'grant_type': 'refresh_token'
} }
}); }).start().then(onResponse);
}; };
/** /**
...@@ -124,14 +123,14 @@ remoting.OAuth2ApiImpl.prototype.refreshAccessToken = function( ...@@ -124,14 +123,14 @@ remoting.OAuth2ApiImpl.prototype.refreshAccessToken = function(
*/ */
remoting.OAuth2ApiImpl.prototype.exchangeCodeForTokens = function( remoting.OAuth2ApiImpl.prototype.exchangeCodeForTokens = function(
onDone, onError, clientId, clientSecret, code, redirectUri) { onDone, onError, clientId, clientSecret, code, redirectUri) {
/** @param {XMLHttpRequest} xhr */ /** @param {!remoting.Xhr.Response} response */
var onResponse = function(xhr) { var onResponse = function(response) {
if (xhr.status == 200) { if (response.status == 200) {
try { try {
// Don't use base.jsonParseSafe here unless you also include base.js, // Don't use base.jsonParseSafe here unless you also include base.js,
// otherwise this won't work from the OAuth trampoline. // otherwise this won't work from the OAuth trampoline.
// TODO(jamiewalch): Fix this once we're no longer using the trampoline. // TODO(jamiewalch): Fix this once we're no longer using the trampoline.
var tokens = JSON.parse(xhr.responseText); var tokens = JSON.parse(response.getText());
onDone(tokens['refresh_token'], onDone(tokens['refresh_token'],
tokens['access_token'], tokens['expires_in']); tokens['access_token'], tokens['expires_in']);
} catch (/** @type {Error} */ err) { } catch (/** @type {Error} */ err) {
...@@ -139,16 +138,15 @@ remoting.OAuth2ApiImpl.prototype.exchangeCodeForTokens = function( ...@@ -139,16 +138,15 @@ remoting.OAuth2ApiImpl.prototype.exchangeCodeForTokens = function(
onError(remoting.Error.unexpected()); onError(remoting.Error.unexpected());
} }
} else { } else {
console.error('Failed to exchange code for token. Status: ' + xhr.status + console.error('Failed to exchange code for token. Status: ' +
' response: ' + xhr.responseText); response.status + ' response: ' + response.getText());
onError(fromHttpStatus(xhr.status)); onError(remoting.Error.fromHttpStatus(response.status));
} }
}; };
remoting.xhr.start({ new remoting.Xhr({
method: 'POST', method: 'POST',
url: this.getOAuth2TokenEndpoint_(), url: this.getOAuth2TokenEndpoint_(),
onDone: onResponse,
formContent: { formContent: {
'client_id': clientId, 'client_id': clientId,
'client_secret': clientSecret, 'client_secret': clientSecret,
...@@ -156,7 +154,7 @@ remoting.OAuth2ApiImpl.prototype.exchangeCodeForTokens = function( ...@@ -156,7 +154,7 @@ remoting.OAuth2ApiImpl.prototype.exchangeCodeForTokens = function(
'code': code, 'code': code,
'grant_type': 'authorization_code' 'grant_type': 'authorization_code'
} }
}); }).start().then(onResponse);
}; };
/** /**
...@@ -170,28 +168,27 @@ remoting.OAuth2ApiImpl.prototype.exchangeCodeForTokens = function( ...@@ -170,28 +168,27 @@ remoting.OAuth2ApiImpl.prototype.exchangeCodeForTokens = function(
* @return {void} Nothing. * @return {void} Nothing.
*/ */
remoting.OAuth2ApiImpl.prototype.getEmail = function(onDone, onError, token) { remoting.OAuth2ApiImpl.prototype.getEmail = function(onDone, onError, token) {
/** @param {XMLHttpRequest} xhr */ /** @param {!remoting.Xhr.Response} response */
var onResponse = function(xhr) { var onResponse = function(response) {
if (xhr.status == 200) { if (response.status == 200) {
try { try {
var result = JSON.parse(xhr.responseText); var result = JSON.parse(response.getText());
onDone(result['email']); onDone(result['email']);
} catch (/** @type {Error} */ err) { } catch (/** @type {Error} */ err) {
console.error('Invalid "userinfo" response from server:', err); console.error('Invalid "userinfo" response from server:', err);
onError(remoting.Error.unexpected()); onError(remoting.Error.unexpected());
} }
} else { } else {
console.error('Failed to get email. Status: ' + xhr.status + console.error('Failed to get email. Status: ' + response.status +
' response: ' + xhr.responseText); ' response: ' + response.getText());
onError(fromHttpStatus(xhr.status)); onError(remoting.Error.fromHttpStatus(response.status));
} }
}; };
remoting.xhr.start({ new remoting.Xhr({
method: 'GET', method: 'GET',
url: this.getOAuth2ApiUserInfoEndpoint_(), url: this.getOAuth2ApiUserInfoEndpoint_(),
onDone: onResponse,
oauthToken: token oauthToken: token
}); }).start().then(onResponse);
}; };
/** /**
...@@ -206,28 +203,27 @@ remoting.OAuth2ApiImpl.prototype.getEmail = function(onDone, onError, token) { ...@@ -206,28 +203,27 @@ remoting.OAuth2ApiImpl.prototype.getEmail = function(onDone, onError, token) {
*/ */
remoting.OAuth2ApiImpl.prototype.getUserInfo = remoting.OAuth2ApiImpl.prototype.getUserInfo =
function(onDone, onError, token) { function(onDone, onError, token) {
/** @param {XMLHttpRequest} xhr */ /** @param {!remoting.Xhr.Response} response */
var onResponse = function(xhr) { var onResponse = function(response) {
if (xhr.status == 200) { if (response.status == 200) {
try { try {
var result = JSON.parse(xhr.responseText); var result = JSON.parse(response.getText());
onDone(result['email'], result['name']); onDone(result['email'], result['name']);
} catch (/** @type {Error} */ err) { } catch (/** @type {Error} */ err) {
console.error('Invalid "userinfo" response from server:', err); console.error('Invalid "userinfo" response from server:', err);
onError(remoting.Error.unexpected()); onError(remoting.Error.unexpected());
} }
} else { } else {
console.error('Failed to get user info. Status: ' + xhr.status + console.error('Failed to get user info. Status: ' + response.status +
' response: ' + xhr.responseText); ' response: ' + response.getText());
onError(fromHttpStatus(xhr.status)); onError(remoting.Error.fromHttpStatus(response.status));
} }
}; };
remoting.xhr.start({ new remoting.Xhr({
method: 'GET', method: 'GET',
url: this.getOAuth2ApiUserInfoEndpoint_(), url: this.getOAuth2ApiUserInfoEndpoint_(),
onDone: onResponse,
oauthToken: token oauthToken: token
}); }).start().then(onResponse);
}; };
/** @returns {!remoting.Error} */ /** @returns {!remoting.Error} */
......
...@@ -75,6 +75,9 @@ remoting.SessionConnectorImpl = function(clientContainer, onConnected, onError, ...@@ -75,6 +75,9 @@ remoting.SessionConnectorImpl = function(clientContainer, onConnected, onError,
remoting.SessionConnectorImpl.prototype.resetConnection_ = function() { remoting.SessionConnectorImpl.prototype.resetConnection_ = function() {
this.closeSession(); this.closeSession();
// It's OK to initialize these member variables here because the
// constructor calls this method.
/** @private {remoting.Host} */ /** @private {remoting.Host} */
this.host_ = null; this.host_ = null;
...@@ -87,8 +90,6 @@ remoting.SessionConnectorImpl.prototype.resetConnection_ = function() { ...@@ -87,8 +90,6 @@ remoting.SessionConnectorImpl.prototype.resetConnection_ = function() {
/** @private {remoting.ClientSession} */ /** @private {remoting.ClientSession} */
this.clientSession_ = null; this.clientSession_ = null;
/** @private {XMLHttpRequest} */
this.pendingXhr_ = null;
/** @private {remoting.CredentialsProvider} */ /** @private {remoting.CredentialsProvider} */
this.credentialsProvider_ = null; this.credentialsProvider_ = null;
......
...@@ -132,7 +132,7 @@ remoting.ThirdPartyTokenFetcher.prototype.parseRedirectUrl_ = ...@@ -132,7 +132,7 @@ remoting.ThirdPartyTokenFetcher.prototype.parseRedirectUrl_ =
* @private * @private
*/ */
remoting.ThirdPartyTokenFetcher.prototype.getFullTokenUrl_ = function() { remoting.ThirdPartyTokenFetcher.prototype.getFullTokenUrl_ = function() {
return this.tokenUrl_ + '?' + remoting.xhr.urlencodeParamHash({ return this.tokenUrl_ + '?' + remoting.Xhr.urlencodeParamHash({
'redirect_uri': this.redirectUri_, 'redirect_uri': this.redirectUri_,
'scope': this.tokenScope_, 'scope': this.tokenScope_,
'client_id': this.hostPublicKey_, 'client_id': this.hostPublicKey_,
......
This diff is collapsed.
This diff is collapsed.
...@@ -100,4 +100,4 @@ QUnit.module = function(desc, opt_args) {}; ...@@ -100,4 +100,4 @@ QUnit.module = function(desc, opt_args) {};
* @param {string} desc * @param {string} desc
* @param {function(QUnit.Assert)} f * @param {function(QUnit.Assert)} f
*/ */
QUnit.test = function(desc, f) {}; QUnit.test = function(desc, f) {};
\ No newline at end of file
...@@ -126,3 +126,36 @@ sinon.TestStub.prototype.onFirstCall = function() {}; ...@@ -126,3 +126,36 @@ sinon.TestStub.prototype.onFirstCall = function() {};
/** @returns {Object} */ /** @returns {Object} */
sinon.createStubInstance = function (/** * */ constructor) {}; sinon.createStubInstance = function (/** * */ constructor) {};
/** @return {sinon.FakeXhr} */
sinon.useFakeXMLHttpRequest = function() {};
/** @interface */
sinon.FakeXhr = function() {};
/** @type {string} */
sinon.FakeXhr.prototype.method;
/** @type {string} */
sinon.FakeXhr.prototype.url;
/** @type {boolean} */
sinon.FakeXhr.prototype.withCredentials;
/** @type {?string} */
sinon.FakeXhr.prototype.requestBody;
/** @type {!Object<string,string>} */
sinon.FakeXhr.prototype.requestHeaders;
/**
* @param {number} status
* @param {!Object<string,string>} headers
* @param {?string} content
*/
sinon.FakeXhr.prototype.respond;
/**
* @type {?function(!sinon.FakeXhr)}
*/
sinon.FakeXhr.prototype.onCreate;
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment