Migrate to ES6 classes

This commit is contained in:
Stanley Cheung 2020-06-17 10:58:32 -07:00 committed by Stanley Cheung
parent 8c96b8cee3
commit 670326124a
7 changed files with 1249 additions and 1263 deletions

View File

@ -7,53 +7,60 @@ goog.module.declareLegacyNamespace();
/**
* The collection of runtime options for a new RPC call.
* @param {!Object<string, !Object>=} options
* @constructor
* @unrestricted
*/
const CallOptions = function(options) {
class CallOptions {
/**
* @param {!Object<string, !Object>=} options
*/
constructor(options) {
/**
* @const {!Object<string, !Object>}
* @private
*/
this.properties_ = options || {};
};
}
/**
* Add a new CallOption or override an existing one.
*
* @param {string} name name of the CallOption that should be added/overridden.
* @param {string} name name of the CallOption that should be
* added/overridden.
* @param {VALUE} value value of the CallOption
* @template VALUE
*/
CallOptions.prototype.setOption = function(name, value) {
setOption(name, value) {
this.properties_[name] = value;
};
}
/**
* Get the value of one CallOption.
*
* @param {string} name name of the CallOption.
* @return {!Object} value of the CallOption. If name doesn't exist, will return
* 'undefined'.
* @return {!Object} value of the CallOption. If name doesn't exist, will
* return 'undefined'.
*/
CallOptions.prototype.get = function(name) {
get(name) {
return this.properties_[name];
};
}
/**
* Remove a CallOption.
*
* @param {string} name name of the CallOption that shoud be removed.
*/
CallOptions.prototype.removeOption = function(name) {
removeOption(name) {
delete this.properties_[name];
};
}
/**
* @return {!Array<string>}
*/
CallOptions.prototype.getKeys = function() {
getKeys() {
return Object.keys(this.properties_);
};
}
}
exports = CallOptions;

View File

@ -47,17 +47,19 @@ const {StreamInterceptor, UnaryInterceptor} = goog.require('grpc.web.Interceptor
/**
* Base class for gRPC web client using the application/grpc-web wire format
* @param {?Object=} opt_options
* @constructor
* @implements {AbstractClientBase}
* @unrestricted
*/
const GrpcWebClientBase = function(opt_options) {
class GrpcWebClientBase {
/**
* @param {?Object=} opt_options
*/
constructor(opt_options) {
/**
* @const
* @private {string}
*/
this.format_ =
goog.getObjectByName('format', opt_options) || "text";
this.format_ = goog.getObjectByName('format', opt_options) || 'text';
/**
* @const
@ -87,15 +89,13 @@ const GrpcWebClientBase = function(opt_options) {
*/
this.unaryInterceptors_ =
goog.getObjectByName('unaryInterceptors', opt_options) || [];
};
}
/**
* @override
* @export
*/
GrpcWebClientBase.prototype.rpcCall = function(
method, requestMessage, metadata, methodDescriptor, callback) {
rpcCall(method, requestMessage, metadata, methodDescriptor, callback) {
methodDescriptor = AbstractClientBase.ensureMethodDescriptor(
method, requestMessage, MethodType.UNARY, methodDescriptor);
var hostname = AbstractClientBase.getHostname(method, methodDescriptor);
@ -106,15 +106,13 @@ GrpcWebClientBase.prototype.rpcCall = function(
this, methodDescriptor.createRequest(requestMessage, metadata)));
GrpcWebClientBase.setCallback_(stream, callback, false);
return stream;
};
}
/**
* @override
* @export
*/
GrpcWebClientBase.prototype.thenableCall = function(
method, requestMessage, metadata, methodDescriptor) {
thenableCall(method, requestMessage, metadata, methodDescriptor) {
methodDescriptor = AbstractClientBase.ensureMethodDescriptor(
method, requestMessage, MethodType.UNARY, methodDescriptor);
var hostname = AbstractClientBase.getHostname(method, methodDescriptor);
@ -143,7 +141,7 @@ GrpcWebClientBase.prototype.thenableCall = function(
var unaryResponse = /** @type {!Promise<?>} */ (invoker.call(
this, methodDescriptor.createRequest(requestMessage, metadata)));
return unaryResponse.then((response) => response.getResponseMessage());
};
}
/**
* @export
@ -156,19 +154,16 @@ GrpcWebClientBase.prototype.thenableCall = function(
* @return {!Promise<RESPONSE>}
* @template REQUEST, RESPONSE
*/
GrpcWebClientBase.prototype.unaryCall = function(
method, requestMessage, metadata, methodDescriptor) {
unaryCall(method, requestMessage, metadata, methodDescriptor) {
return /** @type {!Promise<RESPONSE>}*/ (
this.thenableCall(method, requestMessage, metadata, methodDescriptor));
};
}
/**
* @override
* @export
*/
GrpcWebClientBase.prototype.serverStreaming = function(
method, requestMessage, metadata, methodDescriptor) {
serverStreaming(method, requestMessage, metadata, methodDescriptor) {
methodDescriptor = AbstractClientBase.ensureMethodDescriptor(
method, requestMessage, MethodType.SERVER_STREAMING, methodDescriptor);
var hostname = AbstractClientBase.getHostname(method, methodDescriptor);
@ -177,8 +172,7 @@ GrpcWebClientBase.prototype.serverStreaming = function(
this.streamInterceptors_);
return /** @type {!ClientReadableStream<?>} */ (invoker.call(
this, methodDescriptor.createRequest(requestMessage, metadata)));
};
}
/**
* @private
@ -187,7 +181,7 @@ GrpcWebClientBase.prototype.serverStreaming = function(
* @param {string} hostname
* @return {!ClientReadableStream<RESPONSE>}
*/
GrpcWebClientBase.prototype.startStream_ = function(request, hostname) {
startStream_(request, hostname) {
var methodDescriptor = request.getMethodDescriptor();
var path = hostname + methodDescriptor.name;
@ -218,8 +212,7 @@ GrpcWebClientBase.prototype.startStream_ = function(request, hostname) {
}
xhr.send(path, 'POST', payload);
return stream;
};
}
/**
* @private
@ -230,7 +223,7 @@ GrpcWebClientBase.prototype.startStream_ = function(request, hostname) {
* function(?Error,?RESPONSE)} callback
* @param {boolean} useUnaryResponse
*/
GrpcWebClientBase.setCallback_ = function(stream, callback, useUnaryResponse) {
static setCallback_(stream, callback, useUnaryResponse) {
var responseReceived = null;
var errorEmitted = false;
@ -274,7 +267,7 @@ GrpcWebClientBase.setCallback_ = function(stream, callback, useUnaryResponse) {
callback(null, null); // trigger unaryResponse
}
});
};
}
/**
* Create a new XhrIo object
@ -282,9 +275,9 @@ GrpcWebClientBase.setCallback_ = function(stream, callback, useUnaryResponse) {
* @private
* @return {!XhrIo} The created XhrIo object
*/
GrpcWebClientBase.prototype.newXhr_ = function() {
newXhr_() {
return new XhrIo();
};
}
/**
* Encode the grpc-web request
@ -293,7 +286,7 @@ GrpcWebClientBase.prototype.newXhr_ = function() {
* @param {!Uint8Array} serialized The serialized proto payload
* @return {!Uint8Array} The application/grpc-web padded request
*/
GrpcWebClientBase.prototype.encodeRequest_ = function(serialized) {
encodeRequest_(serialized) {
var len = serialized.length;
var bytesArray = [0, 0, 0, 0];
var payload = new Uint8Array(5 + len);
@ -304,14 +297,14 @@ GrpcWebClientBase.prototype.encodeRequest_ = function(serialized) {
payload.set(new Uint8Array(bytesArray), 1);
payload.set(serialized, 5);
return payload;
};
}
/**
* @private
* @param {!XhrIo} xhr The xhr object
*/
GrpcWebClientBase.prototype.processHeaders_ = function(xhr) {
if (this.format_ == "text") {
processHeaders_(xhr) {
if (this.format_ == 'text') {
xhr.headers.set('Content-Type', 'application/grpc-web-text');
xhr.headers.set('Accept', 'application/grpc-web-text');
} else {
@ -332,7 +325,7 @@ GrpcWebClientBase.prototype.processHeaders_ = function(xhr) {
xhr.headers.set('grpc-timeout', timeout + 'm');
}
}
};
}
/**
* @private
@ -341,10 +334,10 @@ GrpcWebClientBase.prototype.processHeaders_ = function(xhr) {
* @param {!Object<string,string>} headerObject The xhr headers
* @return {string} The URI object or a string path with headers
*/
GrpcWebClientBase.setCorsOverride_ = function(method, headerObject) {
static setCorsOverride_(method, headerObject) {
return /** @type {string} */ (HttpCors.setHttpHeadersWithOverwriteParam(
method, HttpCors.HTTP_HEADERS_PARAM_NAME, headerObject));
};
}
/**
* @private
@ -357,12 +350,16 @@ GrpcWebClientBase.setCorsOverride_ = function(method, headerObject) {
* @return {function(!Request<REQUEST,RESPONSE>):
* (!Promise<RESPONSE>|!ClientReadableStream<RESPONSE>)}
*/
GrpcWebClientBase.runInterceptors_ = function(invoker, interceptors) {
static runInterceptors_(invoker, interceptors) {
let curInvoker = invoker;
interceptors.forEach((interceptor) => {
const lastInvoker = curInvoker;
curInvoker = (request) => interceptor.intercept(request, lastInvoker);
});
return curInvoker;
};
}
}
exports = GrpcWebClientBase;

View File

@ -29,16 +29,16 @@ const {StreamInterceptor} = goog.require('grpc.web.Interceptor');
goog.require('goog.testing.jsunit');
var REQUEST_BYTES = [1, 2, 3];
var FAKE_METHOD = "fake-method";
var PROTO_FIELD_VALUE = "meow";
var FAKE_METHOD = 'fake-method';
var PROTO_FIELD_VALUE = 'meow';
var EXPECTED_HEADERS;
var EXPECTED_HEADER_VALUES;
var EXPECTED_UNARY_HEADERS = ['Content-Type', 'Accept',
'X-User-Agent', 'X-Grpc-Web'];
var EXPECTED_UNARY_HEADER_VALUES = ['application/grpc-web-text',
'application/grpc-web-text',
'grpc-web-javascript/0.1',
'1'];
var EXPECTED_UNARY_HEADERS =
['Content-Type', 'Accept', 'X-User-Agent', 'X-Grpc-Web'];
var EXPECTED_UNARY_HEADER_VALUES = [
'application/grpc-web-text', 'application/grpc-web-text',
'grpc-web-javascript/0.1', '1'
];
var dataCallback;
@ -62,22 +62,23 @@ testSuite({
client.newXhr_ = function() {
return new MockXhr({
// This parses to [ { DATA: [4,5,6] }, { TRAILER: "a: b" } ]
response: googCrypt.encodeByteArray(new Uint8Array([
0, 0, 0, 0, 3, 4, 5, 6, 128, 0, 0, 0, 4, 97, 58, 32, 98
])),
response: googCrypt.encodeByteArray(new Uint8Array(
[0, 0, 0, 0, 3, 4, 5, 6, 128, 0, 0, 0, 4, 97, 58, 32, 98])),
});
};
expectUnaryHeaders();
client.rpcCall(FAKE_METHOD, {}, {}, {
client.rpcCall(
FAKE_METHOD, {}, {}, {
requestSerializeFn: function(request) {
return REQUEST_BYTES;
},
responseDeserializeFn: function(bytes) {
assertElementsEquals([4, 5, 6], [].slice.call(bytes));
return {"field1": PROTO_FIELD_VALUE};
return {'field1': PROTO_FIELD_VALUE};
}
}, function(error, response) {
},
function(error, response) {
assertNull(error);
assertEquals(PROTO_FIELD_VALUE, response['field1']);
});
@ -119,20 +120,23 @@ testSuite({
return new MockXhr({
// This decodes to "grpc-status: 3"
response: googCrypt.encodeByteArray(new Uint8Array([
128, 0, 0, 0, 14, 103, 114, 112, 99, 45, 115, 116, 97, 116, 117, 115, 58, 32, 51
128, 0, 0, 0, 14, 103, 114, 112, 99, 45, 115, 116, 97, 116, 117, 115,
58, 32, 51
])),
});
};
expectUnaryHeaders();
client.rpcCall(FAKE_METHOD, {}, {}, {
client.rpcCall(
FAKE_METHOD, {}, {}, {
requestSerializeFn: function(request) {
return REQUEST_BYTES;
},
responseDeserializeFn: function(bytes) {
return {};
}
}, function(error, response) {
},
function(error, response) {
assertNull(response);
assertEquals(3, error.code);
});
@ -144,28 +148,29 @@ testSuite({
client.newXhr_ = function() {
return new MockXhr({
// This parses to [ { DATA: [4,5,6] }, { TRAILER: "a: b" } ]
response: googCrypt.encodeByteArray(new Uint8Array([
0, 0, 0, 0, 3, 4, 5, 6, 128, 0, 0, 0, 4, 97, 58, 32, 98
])),
response: googCrypt.encodeByteArray(new Uint8Array(
[0, 0, 0, 0, 3, 4, 5, 6, 128, 0, 0, 0, 4, 97, 58, 32, 98])),
});
};
expectUnaryHeaders();
var call = client.rpcCall(FAKE_METHOD, {}, {}, {
var call = client.rpcCall(
FAKE_METHOD, {}, {}, {
requestSerializeFn: function(request) {
return REQUEST_BYTES;
},
responseDeserializeFn: function(bytes) {
assertElementsEquals([4, 5, 6], [].slice.call(bytes));
return {"field1": PROTO_FIELD_VALUE};
return {'field1': PROTO_FIELD_VALUE};
}
}, function(error, response) {
},
function(error, response) {
assertNull(error);
assertEquals(PROTO_FIELD_VALUE, response['field1']);
});
call.on('metadata', (metadata) => {
assertEquals(metadata['sample-initial-metadata-1'],
'sample-initial-metadata-val');
assertEquals(
metadata['sample-initial-metadata-1'], 'sample-initial-metadata-val');
});
dataCallback();
}
@ -180,104 +185,102 @@ function expectUnaryHeaders() {
}
/** @unrestricted */
class MockXhr {
/**
* @constructor
* @param {?Object} mockValues
* Mock XhrIO object to test the outgoing values
*/
function MockXhr(mockValues) {
constructor(mockValues) {
this.mockValues = mockValues;
this.headers = new Map();
}
/**
* @param {string} url
* @param {string=} opt_method
* @param {string=} opt_content
* @param {string=} opt_headers
*/
MockXhr.prototype.send = function(url, opt_method, opt_content, opt_headers) {
send(url, opt_method, opt_content, opt_headers) {
assertEquals(FAKE_METHOD, url);
assertEquals("POST", opt_method);
assertElementsEquals(googCrypt.encodeByteArray(new Uint8Array([0, 0, 0, 0, 3, 1, 2, 3])), opt_content);
assertEquals('POST', opt_method);
assertElementsEquals(
googCrypt.encodeByteArray(new Uint8Array([0, 0, 0, 0, 3, 1, 2, 3])),
opt_content);
assertElementsEquals(EXPECTED_HEADERS, this.headers.getKeys());
assertElementsEquals(EXPECTED_HEADER_VALUES, this.headers.getValues());
};
}
/**
* @param {boolean} withCredentials
*/
MockXhr.prototype.setWithCredentials = function(withCredentials) {
setWithCredentials(withCredentials) {
return;
};
}
/**
* @return {string} response
*/
MockXhr.prototype.getResponseText = function() {
getResponseText() {
return this.mockValues.response;
};
}
/**
* @param {string} key header key
* @return {string} content-type
*/
MockXhr.prototype.getStreamingResponseHeader = function(key) {
getStreamingResponseHeader(key) {
return 'application/grpc-web-text';
};
}
/**
* @return {string} response
*/
MockXhr.prototype.getResponseHeaders = function() {
getResponseHeaders() {
return {'sample-initial-metadata-1': 'sample-initial-metadata-val'};
};
}
/**
* @return {number} xhr state
*/
MockXhr.prototype.getReadyState = function() {
getReadyState() {
return 0;
};
}
/**
* @return {number} lastErrorCode
*/
MockXhr.prototype.getLastErrorCode = function() {
getLastErrorCode() {
return 0;
};
}
/**
* @return {string} lastError
*/
MockXhr.prototype.getLastError = function() {
getLastError() {
return 'server not responding';
};
}
/**
* @param {string} responseType
*/
MockXhr.prototype.setResponseType = function(responseType) {
setResponseType(responseType) {
return;
};
}
}
/**
* @constructor
* @implements {StreamInterceptor}
* @unrestricted
*/
const StreamResponseInterceptor = function() {};
class StreamResponseInterceptor {
constructor() {}
/** @override */
StreamResponseInterceptor.prototype.intercept = function(request, invoker) {
intercept(request, invoker) {
/**
* @implements {ClientReadableStream}
* @constructor
@ -309,4 +312,5 @@ StreamResponseInterceptor.prototype.intercept = function(request, invoker) {
};
return new InterceptedStream(invoker(request));
};
}
}

View File

@ -45,28 +45,27 @@ const {Status} = goog.require('grpc.web.Status');
const GRPC_STATUS = "grpc-status";
const GRPC_STATUS_MESSAGE = "grpc-message";
const GRPC_STATUS = 'grpc-status';
const GRPC_STATUS_MESSAGE = 'grpc-message';
/** @type {!Array<string>} */
const EXCLUDED_RESPONSE_HEADERS = [
'content-type',
GRPC_STATUS,
GRPC_STATUS_MESSAGE
];
const EXCLUDED_RESPONSE_HEADERS =
['content-type', GRPC_STATUS, GRPC_STATUS_MESSAGE];
/**
* A stream that the client can read from. Used for calls that are streaming
* from the server side.
*
* @template RESPONSE
* @constructor
* @implements {ClientReadableStream}
* @final
* @unrestricted
*/
class GrpcWebClientReadableStream {
/**
* @param {!GenericTransportInterface} genericTransportInterface The
* GenericTransportInterface
*/
const GrpcWebClientReadableStream = function(genericTransportInterface) {
constructor(genericTransportInterface) {
/**
* @const
* @private
@ -135,8 +134,7 @@ const GrpcWebClientReadableStream = function(genericTransportInterface) {
this.parser_ = new GrpcWebStreamParser();
var self = this;
events.listen(this.xhr_, EventType.READY_STATE_CHANGE,
function(e) {
events.listen(this.xhr_, EventType.READY_STATE_CHANGE, function(e) {
var contentType = self.xhr_.getStreamingResponseHeader('Content-Type');
if (!contentType) return;
contentType = contentType.toLowerCase();
@ -172,8 +170,8 @@ const GrpcWebClientReadableStream = function(genericTransportInterface) {
var trailerString = '';
for (var pos = 0; pos < messages[i][FrameType.TRAILER].length;
pos++) {
trailerString += String.fromCharCode(
messages[i][FrameType.TRAILER][pos]);
trailerString +=
String.fromCharCode(messages[i][FrameType.TRAILER][pos]);
}
var trailers = self.parseHttp1Headers_(trailerString);
var grpcStatusCode = StatusCode.OK;
@ -265,15 +263,13 @@ const GrpcWebClientReadableStream = function(genericTransportInterface) {
self.sendEndCallbacks_();
}
});
};
}
/**
* @override
* @export
*/
GrpcWebClientReadableStream.prototype.on = function(
eventType, callback) {
on(eventType, callback) {
// TODO(stanleycheung): change eventType to @enum type
if (eventType == 'data') {
this.onDataCallbacks_.push(callback);
@ -287,29 +283,25 @@ GrpcWebClientReadableStream.prototype.on = function(
this.onErrorCallbacks_.push(callback);
}
return this;
};
}
/**
* @private
* @param {!Array<function(?)>} callbacks the internal list of callbacks
* @param {function(?)} callback the callback to remove
*/
GrpcWebClientReadableStream.prototype.removeListenerFromCallbacks_ = function(
callbacks, callback) {
removeListenerFromCallbacks_(callbacks, callback) {
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
}
};
}
/**
* @export
* @override
*/
GrpcWebClientReadableStream.prototype.removeListener = function(
eventType, callback) {
removeListener(eventType, callback) {
if (eventType == 'data') {
this.removeListenerFromCallbacks_(this.onDataCallbacks_, callback);
} else if (eventType == 'status') {
@ -322,8 +314,7 @@ GrpcWebClientReadableStream.prototype.removeListener = function(
this.removeListenerFromCallbacks_(this.onErrorCallbacks_, callback);
}
return this;
};
}
/**
* Register a callbackl to parse the response
@ -331,21 +322,18 @@ GrpcWebClientReadableStream.prototype.removeListener = function(
* @param {function(?):!RESPONSE} responseDeserializeFn The deserialize
* function for the proto
*/
GrpcWebClientReadableStream.prototype.setResponseDeserializeFn =
function(responseDeserializeFn) {
setResponseDeserializeFn(responseDeserializeFn) {
this.responseDeserializeFn_ = responseDeserializeFn;
};
}
/**
* @override
* @export
*/
GrpcWebClientReadableStream.prototype.cancel = function() {
cancel() {
this.aborted_ = true;
this.xhr_.abort();
};
}
/**
* Parse HTTP headers
@ -354,72 +342,67 @@ GrpcWebClientReadableStream.prototype.cancel = function() {
* @param {string} str The raw http header string
* @return {!Object} The header:value pairs
*/
GrpcWebClientReadableStream.prototype.parseHttp1Headers_ =
function(str) {
var chunks = str.trim().split("\r\n");
parseHttp1Headers_(str) {
var chunks = str.trim().split('\r\n');
var headers = {};
for (var i = 0; i < chunks.length; i++) {
var pos = chunks[i].indexOf(":");
var pos = chunks[i].indexOf(':');
headers[chunks[i].substring(0, pos).trim()] =
chunks[i].substring(pos + 1).trim();
}
return headers;
};
}
/**
* @private
* @param {!RESPONSE} data The data to send back
*/
GrpcWebClientReadableStream.prototype.sendDataCallbacks_ = function(data) {
sendDataCallbacks_(data) {
for (var i = 0; i < this.onDataCallbacks_.length; i++) {
this.onDataCallbacks_[i](data);
}
};
}
/**
* @private
* @param {!Status} status The status to send back
*/
GrpcWebClientReadableStream.prototype.sendStatusCallbacks_ = function(status) {
sendStatusCallbacks_(status) {
for (var i = 0; i < this.onStatusCallbacks_.length; i++) {
this.onStatusCallbacks_[i](status);
}
};
}
/**
* @private
* @param {!Metadata} metadata The metadata to send back
*/
GrpcWebClientReadableStream.prototype.sendMetadataCallbacks_ =
function(metadata) {
sendMetadataCallbacks_(metadata) {
for (var i = 0; i < this.onMetadataCallbacks_.length; i++) {
this.onMetadataCallbacks_[i](metadata);
}
};
}
/**
* @private
* @param {?} error The error to send back
*/
GrpcWebClientReadableStream.prototype.sendErrorCallbacks_ = function(error) {
sendErrorCallbacks_(error) {
for (var i = 0; i < this.onErrorCallbacks_.length; i++) {
this.onErrorCallbacks_[i](error);
}
};
}
/**
* @private
*/
GrpcWebClientReadableStream.prototype.sendEndCallbacks_ = function() {
sendEndCallbacks_() {
for (var i = 0; i < this.onEndCallbacks_.length; i++) {
this.onEndCallbacks_[i]();
}
};
}
}
exports = GrpcWebClientReadableStream;

View File

@ -49,13 +49,11 @@ const asserts = goog.require('goog.asserts');
/**
* The default grpc-web stream parser.
*
* @constructor
* @struct
* @implements {StreamParser}
* @final
*/
const GrpcWebStreamParser = function() {
class GrpcWebStreamParser {
constructor() {
/**
* The current error message, if any.
* @private {?string}
@ -99,8 +97,8 @@ const GrpcWebStreamParser = function() {
this.countLengthBytes_ = 0;
/**
* Raw bytes of the current message. Uses Uint8Array by default. Falls back to
* native array when Uint8Array is unsupported.
* Raw bytes of the current message. Uses Uint8Array by default. Falls back
* to native array when Uint8Array is unsupported.
* @private {?Uint8Array|?Array<number>}
*/
this.messageBuffer_ = null;
@ -110,83 +108,38 @@ const GrpcWebStreamParser = function() {
* @private {number}
*/
this.countMessageBytes_ = 0;
};
const Parser = GrpcWebStreamParser;
/**
* The parser state.
* @private @enum {number}
*/
Parser.State_ = {
INIT: 0, // expecting the next frame byte
LENGTH: 1, // expecting 4 bytes of length
MESSAGE: 2, // expecting more message bytes
INVALID: 3
};
/**
* Possible frame byte
* @enum {number}
*/
GrpcWebStreamParser.FrameType = {
DATA: 0x00, // expecting a data frame
TRAILER: 0x80, // expecting a trailer frame
};
var FrameType = GrpcWebStreamParser.FrameType;
}
/**
* @override
*/
GrpcWebStreamParser.prototype.isInputValid = function() {
isInputValid() {
return this.state_ != Parser.State_.INVALID;
};
}
/**
* @override
*/
GrpcWebStreamParser.prototype.getErrorMessage = function() {
getErrorMessage() {
return this.errorMessage_;
};
/**
* @param {!Uint8Array|!Array<number>} inputBytes The current input buffer
* @param {number} pos The position in the current input that triggers the error
* @param {string} errorMsg Additional error message
* @throws {!Error} Throws an error indicating where the stream is broken
* @private
*/
Parser.prototype.error_ = function(inputBytes, pos, errorMsg) {
this.state_ = Parser.State_.INVALID;
this.errorMessage_ = 'The stream is broken @' + this.streamPos_ + '/' + pos +
'. ' +
'Error: ' + errorMsg + '. ' +
'With input:\n' + inputBytes;
throw new Error(this.errorMessage_);
};
}
/**
* Parse the new input.
*
* Note that there is no Parser state to indicate the end of a stream.
*
* @param {string|!ArrayBuffer|!Uint8Array|!Array<number>} input The input data
* @param {string|!ArrayBuffer|!Uint8Array|!Array<number>} input The input
* data
* @throws {!Error} Throws an error message if the input is invalid.
* @return {?Array<string|!Object>} any parsed objects (atomic messages)
* in an array, or null if more data needs be read to parse any new object.
* @override
*/
GrpcWebStreamParser.prototype.parse = function(input) {
asserts.assert(input instanceof Array || input instanceof ArrayBuffer || input instanceof Uint8Array);
parse(input) {
asserts.assert(
input instanceof Array || input instanceof ArrayBuffer ||
input instanceof Uint8Array);
var parser = this;
var inputBytes;
@ -216,7 +169,9 @@ GrpcWebStreamParser.prototype.parse = function(input) {
processMessageByte(inputBytes[pos]);
break;
}
default: { throw new Error('unexpected parser state: ' + parser.state_); }
default: {
throw new Error('unexpected parser state: ' + parser.state_);
}
}
parser.streamPos_++;
@ -285,6 +240,53 @@ GrpcWebStreamParser.prototype.parse = function(input) {
parser.result_.push(message);
parser.state_ = Parser.State_.INIT;
}
}
}
const Parser = GrpcWebStreamParser;
/**
* The parser state.
* @private @enum {number}
*/
Parser.State_ = {
INIT: 0, // expecting the next frame byte
LENGTH: 1, // expecting 4 bytes of length
MESSAGE: 2, // expecting more message bytes
INVALID: 3
};
/**
* Possible frame byte
* @enum {number}
*/
GrpcWebStreamParser.FrameType = {
DATA: 0x00, // expecting a data frame
TRAILER: 0x80, // expecting a trailer frame
};
var FrameType = GrpcWebStreamParser.FrameType;
/**
* @param {!Uint8Array|!Array<number>} inputBytes The current input buffer
* @param {number} pos The position in the current input that triggers the error
* @param {string} errorMsg Additional error message
* @throws {!Error} Throws an error indicating where the stream is broken
* @private
*/
Parser.prototype.error_ = function(inputBytes, pos, errorMsg) {
this.state_ = Parser.State_.INVALID;
this.errorMessage_ = 'The stream is broken @' + this.streamPos_ + '/' + pos +
'. ' +
'Error: ' + errorMsg + '. ' +
'With input:\n' + inputBytes;
throw new Error(this.errorMessage_);
};

View File

@ -13,10 +13,9 @@ const MethodType = goog.require('grpc.web.MethodType');
const Request = goog.require('grpc.web.Request');
const RequestInternal = goog.require('grpc.web.RequestInternal');
/** @template REQUEST, RESPONSE */
class MethodDescriptor {
/**
* @constructor
* @struct
* @template REQUEST, RESPONSE
* @param {string} name
* @param {!MethodType} methodType
* @param {function(new: REQUEST, ...)} requestType
@ -24,7 +23,7 @@ const RequestInternal = goog.require('grpc.web.RequestInternal');
* @param {function(REQUEST): ?} requestSerializeFn
* @param {function(?): RESPONSE} responseDeserializeFn
*/
const MethodDescriptor = function(
constructor(
name, methodType, requestType, responseType, requestSerializeFn,
responseDeserializeFn) {
/** @const */
@ -39,7 +38,7 @@ const MethodDescriptor = function(
this.requestSerializeFn = requestSerializeFn;
/** @const */
this.responseDeserializeFn = responseDeserializeFn;
};
}
/**
* @template REQUEST, RESPONSE
@ -48,9 +47,12 @@ const MethodDescriptor = function(
* @param {!CallOptions=} callOptions
* @return {!Request<REQUEST, RESPONSE>}
*/
MethodDescriptor.prototype.createRequest = function(
createRequest(
requestMessage, metadata = {}, callOptions = new CallOptions()) {
return new RequestInternal(requestMessage, this, metadata, callOptions);
};
}
}
exports = MethodDescriptor;

View File

@ -44,15 +44,17 @@ const {Status} = goog.require('grpc.web.Status');
/**
* A stream that the client can read from. Used for calls that are streaming
* from the server side.
*
* @template RESPONSE
* @constructor
* @implements {ClientReadableStream}
* @final
* @unrestricted
*/
class StreamBodyClientReadableStream {
/**
* @param {!GenericTransportInterface} genericTransportInterface The
* GenericTransportInterface
*/
const StreamBodyClientReadableStream = function(genericTransportInterface) {
constructor(genericTransportInterface) {
/**
* @const
* @private
@ -109,13 +111,12 @@ const StreamBodyClientReadableStream = function(genericTransportInterface) {
this.rpcStatusParseFn_ = null;
this.setStreamCallback_();
};
}
/**
* @private
*/
StreamBodyClientReadableStream.prototype.setStreamCallback_ = function() {
setStreamCallback_() {
// Add the callback to the underlying stream
var self = this;
this.xhrNodeReadableStream_.on('data', function(data) {
@ -167,14 +168,13 @@ StreamBodyClientReadableStream.prototype.setStreamCallback_ = function() {
message: ErrorCode.getDebugMessage(lastErrorCode)
});
});
};
}
/**
* @override
* @export
*/
StreamBodyClientReadableStream.prototype.on = function(
eventType, callback) {
on(eventType, callback) {
// TODO(stanleycheung): change eventType to @enum type
if (eventType == 'data') {
this.onDataCallbacks_.push(callback);
@ -186,29 +186,25 @@ StreamBodyClientReadableStream.prototype.on = function(
this.onErrorCallbacks_.push(callback);
}
return this;
};
}
/**
* @private
* @param {!Array<function(?)>} callbacks the internal list of callbacks
* @param {function(?)} callback the callback to remove
*/
StreamBodyClientReadableStream.prototype.removeListenerFromCallbacks_ = function(
callbacks, callback) {
removeListenerFromCallbacks_(callbacks, callback) {
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
}
};
}
/**
* @export
* @override
*/
StreamBodyClientReadableStream.prototype.removeListener = function(
eventType, callback) {
removeListener(eventType, callback) {
if (eventType == 'data') {
this.removeListenerFromCallbacks_(this.onDataCallbacks_, callback);
} else if (eventType == 'status') {
@ -219,8 +215,7 @@ StreamBodyClientReadableStream.prototype.removeListener = function(
this.removeListenerFromCallbacks_(this.onErrorCallbacks_, callback);
}
return this;
};
}
/**
* Register a callbackl to parse the response
@ -228,10 +223,9 @@ StreamBodyClientReadableStream.prototype.removeListener = function(
* @param {function(?): RESPONSE} responseDeserializeFn The deserialize
* function for the proto
*/
StreamBodyClientReadableStream.prototype.setResponseDeserializeFn =
function(responseDeserializeFn) {
setResponseDeserializeFn(responseDeserializeFn) {
this.responseDeserializeFn_ = responseDeserializeFn;
};
}
/**
* Register a function to parse RPC status response
@ -239,61 +233,58 @@ StreamBodyClientReadableStream.prototype.setResponseDeserializeFn =
* @param {function(?):!Status} rpcStatusParseFn A function to parse
* the RPC status response
*/
StreamBodyClientReadableStream.prototype.setRpcStatusParseFn = function(rpcStatusParseFn) {
setRpcStatusParseFn(rpcStatusParseFn) {
this.rpcStatusParseFn_ = rpcStatusParseFn;
};
}
/**
* @override
* @export
*/
StreamBodyClientReadableStream.prototype.cancel = function() {
cancel() {
this.xhr_.abort();
};
}
/**
* @private
* @param {!RESPONSE} data The data to send back
*/
StreamBodyClientReadableStream.prototype.sendDataCallbacks_ = function(data) {
sendDataCallbacks_(data) {
for (var i = 0; i < this.onDataCallbacks_.length; i++) {
this.onDataCallbacks_[i](data);
}
};
}
/**
* @private
* @param {!Status} status The status to send back
*/
StreamBodyClientReadableStream.prototype.sendStatusCallbacks_ = function(status) {
sendStatusCallbacks_(status) {
for (var i = 0; i < this.onStatusCallbacks_.length; i++) {
this.onStatusCallbacks_[i](status);
}
};
}
/**
* @private
* @param {?} error The error to send back
*/
StreamBodyClientReadableStream.prototype.sendErrorCallbacks_ = function(error) {
sendErrorCallbacks_(error) {
for (var i = 0; i < this.onErrorCallbacks_.length; i++) {
this.onErrorCallbacks_[i](error);
}
};
}
/**
* @private
*/
StreamBodyClientReadableStream.prototype.sendEndCallbacks_ = function() {
sendEndCallbacks_() {
for (var i = 0; i < this.onEndCallbacks_.length; i++) {
this.onEndCallbacks_[i]();
}
};
}
}
exports = StreamBodyClientReadableStream;