blob: ab153f3a8078746329d612f95823c3cf272e5ec3 [file] [log] [blame]
<!--
Copyright 2016 The LUCI Authors. All rights reserved.
Use of this source code is governed under the Apache License, Version 2.0
that can be found in the LICENSE file.
-->
<link rel="import" href="../bower_components/iron-ajax/iron-request.html">
<!--
The `rpc-call` is a single RPC call. Produced by <rpc-client>.
-->
<dom-module id="rpc-client">
<script>
'use strict';
Polymer({
is: 'rpc-call',
properties: {
/**
* if true, use HTTP instead of HTTPS.
* If null or undefined (default), determined automatically:
* - If host equals current host and current protocol is http, then
* false.
* - otherwise true.
*/
insecure: {
type: Boolean,
readOnly: true
},
/**
* pRPC server host, defaults to current document host.
*/
host: {
type: String,
readOnly: true
},
/**
* Full service name, including package name.
*/
service: {
type: String,
readOnly: true
},
/**
* Service method name.
*/
method: {
type: String,
readOnly: true
},
/**
* Request object.
*/
request: {
type: Object,
readOnly: true
},
/**
* Request timeout in milliseconds.
*/
timeout: {
type: Number,
readOnly: true
},
/**
* OAuth 2.0 access token.
*/
accessToken: {
type: String,
readOnly: true
},
/**
* A promise that resolves when the response comes back, or rejects
* if there is an error.
*
* @type {Promise}
*/
completes: {
type: Object,
readOnly: true,
notify: true,
value: function() {
return new Promise(function(resolve, reject) {
this._resolveCompletes = resolve;
this._rejectCompletes = reject;
}.bind(this));
}
},
/**
* Response object.
*/
response: {
type: Object,
notify: true,
readOnly: true
},
/**
* Response code.
* @type {luci.rpc.Code}
*/
code: {
type: Number,
readOnly: true,
},
/**
* Response error
* @type {Error|luci.rpc.GrpcError}
*/
error: {
type: Object,
notify: true,
readOnly: true
},
_ironRequest: Object
},
_generateUrl: function() {
var insecure = this.insecure;
var host = this.host;
var service = this.service;
var method = this.method;
if (!host) {
throw Error('no host');
}
if (!service) {
throw Error('no service');
}
if (!method) {
throw Error('no method');
}
if (!host || !service || !method) {
return '';
}
var protocol = 'https:';
if (insecure === true) {
protocol = 'http:';
} else if (insecure == null && host === document.location.host) {
protocol = document.location.protocol;
}
return protocol + '//' + host + '/prpc/' + service + '/' + method;
},
toRequestOptions: function() {
var headers = {};
if (this.request != null) {
headers['content-type'] = 'application/json'
}
if (this.timeout) {
headers['x-prpc-timeout'] = this.timeout + 'm';
}
if (this.accessToken) {
headers['authorization'] = 'Bearer ' + this.accessToken;
}
return {
url: this._generateUrl(),
method: 'POST',
headers: headers,
body: this.request,
handleAs: 'json',
jsonPrefix: ')]}\'',
timeout: this.timeout
};
},
send: function(options) {
if (this.xhr) {
throw Error('Already sent');
}
this._setInsecure(options.insecure);
this._setHost(options.host);
this._setService(options.service);
this._setMethod(options.method);
this._setRequest(options.request);
this._setTimeout(options.timeout);
this._setAccessToken(options.accessToken);
this._ironRequest = document.createElement('iron-request');
this._ironRequest.send(this.toRequestOptions());
this.xhr = this._ironRequest.xhr;
this._ironRequest.completes
.then(this._done.bind(this, null))
.catch(this._done.bind(this));
},
abort: function() {
if (this.xhr) {
// This will cause a 'Request aborted' error to be sent via reject.
this.xhr.abort();
}
},
_done: function(error) {
try {
var status = this.xhr.status;
if (error && typeof status !== 'number') {
// We didn't receive the response.
throw error;
}
var codeHeader = this.xhr.getResponseHeader('X-Prpc-Grpc-Code');
if (!codeHeader) {
throw new luci.rpc.HttpError( status,
'Invalid response: no X-Prpc-Grpc-Code response header');
}
try {
this._setCode(parseInt(codeHeader, 10));
if (this.code == null || isNaN(this.code)) {
throw Error('code is not defined');
}
} catch (e) {
throw new luci.rpc.HttpError( status,
'Invalid X-Prpc-Grpc-Code response header "' + codeHeader +
'": ' + e
);
}
if (this.code !== luci.rpc.Code.OK) {
throw new luci.rpc.GrpcError(this.code, this.xhr.responseText);
}
if (this._ironRequest.response == null) {
throw Error('could not parse response');
}
this._setResponse(this._ironRequest.response);
this._setError(null);
this._resolveCompletes(this);
} catch (e) {
this._setResponse(null);
this._setError(e);
this._rejectCompletes(e);
}
}
});
</script>
</dom-module>