mirror of https://github.com/grpc/grpc-web.git
GrpcWebClientReadableStream: keep falsy data (#1230)
This commit is contained in:
parent
b0ea9c7c45
commit
e11903b337
|
|
@ -123,10 +123,10 @@ class GrpcWebClientBase {
|
||||||
let unaryStatus;
|
let unaryStatus;
|
||||||
let unaryMsg;
|
let unaryMsg;
|
||||||
GrpcWebClientBase.setCallback_(
|
GrpcWebClientBase.setCallback_(
|
||||||
stream, (error, response, status, metadata) => {
|
stream, (error, response, status, metadata, unaryResponseReceived) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
} else if (response) {
|
} else if (unaryResponseReceived) {
|
||||||
unaryMsg = response;
|
unaryMsg = response;
|
||||||
} else if (status) {
|
} else if (status) {
|
||||||
unaryStatus = status;
|
unaryStatus = status;
|
||||||
|
|
@ -222,9 +222,16 @@ class GrpcWebClientBase {
|
||||||
* @static
|
* @static
|
||||||
* @template RESPONSE
|
* @template RESPONSE
|
||||||
* @param {!ClientReadableStream<RESPONSE>} stream
|
* @param {!ClientReadableStream<RESPONSE>} stream
|
||||||
* @param {function(?RpcError, ?RESPONSE, ?Status=, ?Object<string, string>=)|
|
* @param {function(?RpcError, ?RESPONSE, ?Status=, ?Object<string, string>=, ?boolean)|
|
||||||
* function(?RpcError,?RESPONSE)} callback
|
* function(?RpcError,?RESPONSE)} callback
|
||||||
* @param {boolean} useUnaryResponse
|
* @param {boolean} useUnaryResponse Pass true to have the client make
|
||||||
|
* multiple calls to the callback, using (error, response, status,
|
||||||
|
* metadata, unaryResponseReceived) arguments. One of error, status,
|
||||||
|
* metadata, or unaryResponseReceived will be truthy to indicate which piece
|
||||||
|
* of information the client is providing in that call. After the stream
|
||||||
|
* ends, it will call the callback an additional time with all falsy
|
||||||
|
* arguments. Pass false to have the client make one call to the callback
|
||||||
|
* using (error, response) arguments.
|
||||||
*/
|
*/
|
||||||
static setCallback_(stream, callback, useUnaryResponse) {
|
static setCallback_(stream, callback, useUnaryResponse) {
|
||||||
let isResponseReceived = false;
|
let isResponseReceived = false;
|
||||||
|
|
@ -272,11 +279,11 @@ class GrpcWebClientBase {
|
||||||
message: 'Incomplete response',
|
message: 'Incomplete response',
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
callback(null, responseReceived);
|
callback(null, responseReceived, null, null, /* unaryResponseReceived= */ true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (useUnaryResponse) {
|
if (useUnaryResponse) {
|
||||||
callback(null, null); // trigger unaryResponse
|
callback(null, null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,77 @@ testSuite({
|
||||||
assertElementsEquals(DEFAULT_UNARY_HEADER_VALUES, Object.values(headers));
|
assertElementsEquals(DEFAULT_UNARY_HEADER_VALUES, Object.values(headers));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async testRpcFalsyResponse_ForNonProtobufDescriptor() {
|
||||||
|
const xhr = new XhrIo();
|
||||||
|
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
|
||||||
|
const methodDescriptor = createMethodDescriptor((bytes) => {
|
||||||
|
assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await new Promise((resolve, reject) => {
|
||||||
|
client.rpcCall(
|
||||||
|
'url', new MockRequest(), /* metadata= */ {}, methodDescriptor,
|
||||||
|
(error, response) => {
|
||||||
|
assertNull(error);
|
||||||
|
resolve(response);
|
||||||
|
});
|
||||||
|
xhr.simulatePartialResponse(
|
||||||
|
googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
|
||||||
|
DEFAULT_RESPONSE_HEADERS);
|
||||||
|
xhr.simulateReadyStateChange(ReadyState.COMPLETE);
|
||||||
|
});
|
||||||
|
|
||||||
|
assertEquals(0, response);
|
||||||
|
const headers = /** @type {!Object} */ (xhr.getLastRequestHeaders());
|
||||||
|
assertElementsEquals(DEFAULT_UNARY_HEADERS, Object.keys(headers));
|
||||||
|
assertElementsEquals(DEFAULT_UNARY_HEADER_VALUES, Object.values(headers));
|
||||||
|
},
|
||||||
|
|
||||||
|
async testRpcResponseThenableCall() {
|
||||||
|
const xhr = new XhrIo();
|
||||||
|
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
|
||||||
|
const methodDescriptor = createMethodDescriptor((bytes) => {
|
||||||
|
assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
|
||||||
|
return new MockReply('value');
|
||||||
|
});
|
||||||
|
|
||||||
|
const responsePromise = client.thenableCall(
|
||||||
|
'url', new MockRequest(), /* metadata= */ {}, methodDescriptor);
|
||||||
|
xhr.simulatePartialResponse(
|
||||||
|
googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
|
||||||
|
DEFAULT_RESPONSE_HEADERS);
|
||||||
|
xhr.simulateReadyStateChange(ReadyState.COMPLETE);
|
||||||
|
const response = await responsePromise;
|
||||||
|
|
||||||
|
assertEquals('value', response.data);
|
||||||
|
const headers = /** @type {!Object} */ (xhr.getLastRequestHeaders());
|
||||||
|
assertElementsEquals(DEFAULT_UNARY_HEADERS, Object.keys(headers));
|
||||||
|
assertElementsEquals(DEFAULT_UNARY_HEADER_VALUES, Object.values(headers));
|
||||||
|
},
|
||||||
|
|
||||||
|
async testRpcFalsyResponseThenableCall_ForNonProtobufDescriptor() {
|
||||||
|
const xhr = new XhrIo();
|
||||||
|
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
|
||||||
|
const methodDescriptor = createMethodDescriptor((bytes) => {
|
||||||
|
assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
const responsePromise = client.thenableCall(
|
||||||
|
'url', new MockRequest(), /* metadata= */ {}, methodDescriptor);
|
||||||
|
xhr.simulatePartialResponse(
|
||||||
|
googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
|
||||||
|
DEFAULT_RESPONSE_HEADERS);
|
||||||
|
xhr.simulateReadyStateChange(ReadyState.COMPLETE);
|
||||||
|
const response = await responsePromise;
|
||||||
|
|
||||||
|
assertEquals(0, response);
|
||||||
|
const headers = /** @type {!Object} */ (xhr.getLastRequestHeaders());
|
||||||
|
assertElementsEquals(DEFAULT_UNARY_HEADERS, Object.keys(headers));
|
||||||
|
assertElementsEquals(DEFAULT_UNARY_HEADER_VALUES, Object.values(headers));
|
||||||
|
},
|
||||||
|
|
||||||
async testDeadline() {
|
async testDeadline() {
|
||||||
const xhr = new XhrIo();
|
const xhr = new XhrIo();
|
||||||
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
|
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
|
||||||
|
|
|
||||||
|
|
@ -170,16 +170,18 @@ class GrpcWebClientReadableStream {
|
||||||
if (FrameType.DATA in messages[i]) {
|
if (FrameType.DATA in messages[i]) {
|
||||||
const data = messages[i][FrameType.DATA];
|
const data = messages[i][FrameType.DATA];
|
||||||
if (data) {
|
if (data) {
|
||||||
|
let isResponseDeserialized = false;
|
||||||
let response;
|
let response;
|
||||||
try {
|
try {
|
||||||
response = self.responseDeserializeFn_(data);
|
response = self.responseDeserializeFn_(data);
|
||||||
|
isResponseDeserialized = true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
self.handleError_(new RpcError(
|
self.handleError_(new RpcError(
|
||||||
StatusCode.INTERNAL,
|
StatusCode.INTERNAL,
|
||||||
`Error when deserializing response data; error: ${err}` +
|
`Error when deserializing response data; error: ${err}` +
|
||||||
`, response: ${response}`));
|
`, response: ${response}`));
|
||||||
}
|
}
|
||||||
if (response) {
|
if (isResponseDeserialized) {
|
||||||
self.sendDataCallbacks_(response);
|
self.sendDataCallbacks_(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue