mirror of https://github.com/grpc/grpc-web.git
feat: pass http status code to RpcError
This commit is contained in:
parent
b5ff5d303d
commit
b84dc9c1bc
|
@ -194,6 +194,27 @@ testSuite({
|
|||
assertEquals(3, error.code);
|
||||
},
|
||||
|
||||
async testRpcErrorWithHttpStatusCode() {
|
||||
const xhr = new XhrIo();
|
||||
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
|
||||
const methodDescriptor = createMethodDescriptor((bytes) => new MockReply());
|
||||
|
||||
const error = await new Promise((resolve, reject) => {
|
||||
client.rpcCall(
|
||||
'urlurl', new MockRequest(), /* metadata= */ {}, methodDescriptor,
|
||||
(error, response) => {
|
||||
assertNull(response);
|
||||
resolve(error);
|
||||
});
|
||||
// This decodes to "grpc-status: 3"
|
||||
xhr.simulateResponse(505, '', {'Content-Type': 'text/html'});
|
||||
});
|
||||
assertTrue(error instanceof RpcError);
|
||||
assert('metadata' in error);
|
||||
assert('httpStatusCode' in error.metadata);
|
||||
assertEquals(505, error.metadata.httpStatusCode);
|
||||
},
|
||||
|
||||
async testRpcDeserializationError() {
|
||||
const xhr = new XhrIo();
|
||||
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
|
||||
|
|
|
@ -136,9 +136,8 @@ class GrpcWebClientReadableStream {
|
|||
|
||||
const self = this;
|
||||
events.listen(this.xhr_, EventType.READY_STATE_CHANGE, function(e) {
|
||||
let contentType = self.xhr_.getStreamingResponseHeader('Content-Type');
|
||||
if (!contentType) return;
|
||||
contentType = contentType.toLowerCase();
|
||||
const contentType = (self.xhr_.getStreamingResponseHeader('Content-Type') || '').toLowerCase();
|
||||
if (!contentType.startsWith('application/grpc')) return;
|
||||
|
||||
let byteSource;
|
||||
if (googString.startsWith(contentType, 'application/grpc-web-text')) {
|
||||
|
@ -152,11 +151,8 @@ class GrpcWebClientReadableStream {
|
|||
} else if (googString.startsWith(contentType, 'application/grpc')) {
|
||||
byteSource = new Uint8Array(
|
||||
/** @type {!ArrayBuffer} */ (self.xhr_.getResponse()));
|
||||
} else {
|
||||
self.handleError_(
|
||||
new RpcError(StatusCode.UNKNOWN, 'Unknown Content-type received.'));
|
||||
return;
|
||||
}
|
||||
|
||||
let messages = null;
|
||||
try {
|
||||
messages = self.parser_.parse(byteSource);
|
||||
|
@ -257,11 +253,14 @@ class GrpcWebClientReadableStream {
|
|||
return;
|
||||
}
|
||||
let errorMessage = ErrorCode.getDebugMessage(lastErrorCode);
|
||||
|
||||
const errorMetadata = /** @type {!StatusMetadata} */ ({});
|
||||
if (xhrStatusCode != -1) {
|
||||
errorMessage += ', http status code: ' + xhrStatusCode;
|
||||
errorMetadata['httpStatusCode'] = xhrStatusCode;
|
||||
}
|
||||
|
||||
self.handleError_(new RpcError(grpcStatusCode, errorMessage));
|
||||
self.handleError_(new RpcError(grpcStatusCode, errorMessage, errorMetadata));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
declare module "grpc-web" {
|
||||
|
||||
export interface Metadata { [s: string]: string; }
|
||||
export type StatusMetadata = Metadata & {
|
||||
httpStatusCode?: number
|
||||
};
|
||||
|
||||
export class AbstractClientBase {
|
||||
thenableCall<REQ, RESP> (
|
||||
|
@ -105,15 +108,15 @@ declare module "grpc-web" {
|
|||
}
|
||||
|
||||
export class RpcError extends Error {
|
||||
constructor(code: StatusCode, message: string, metadata: Metadata);
|
||||
constructor(code: StatusCode, message: string, metadata: StatusMetadata);
|
||||
code: StatusCode;
|
||||
metadata: Metadata;
|
||||
metadata: StatusMetadata;
|
||||
}
|
||||
|
||||
export interface Status {
|
||||
code: number;
|
||||
details: string;
|
||||
metadata?: Metadata;
|
||||
metadata?: StatusMetadata;
|
||||
}
|
||||
|
||||
export enum StatusCode {
|
||||
|
|
|
@ -118,6 +118,29 @@ describe('grpc-web generated code: promise-based client', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should receive error, on http error - Content-Type not matching application/grpc*', function(done) {
|
||||
const {EchoServicePromiseClient} = require(genCodePath);
|
||||
const {EchoRequest} = require(protoGenCodePath);
|
||||
MockXMLHttpRequest.onSend = function(xhr) {
|
||||
xhr.respond(
|
||||
505, {'Content-Type': 'text/html'});
|
||||
};
|
||||
var echoService = new EchoServicePromiseClient('MyHostname', null, null);
|
||||
var request = new EchoRequest();
|
||||
request.setMessage('aaa');
|
||||
|
||||
echoService.echo(request, {})
|
||||
.then((response) => {
|
||||
assert.fail('should not receive response');
|
||||
})
|
||||
.catch((error) => {
|
||||
assert('metadata' in error);
|
||||
assert('httpStatusCode' in error.metadata);
|
||||
assert.equal(505, error.metadata.httpStatusCode);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should receive error', function(done) {
|
||||
const {EchoServicePromiseClient} = require(genCodePath);
|
||||
const {EchoRequest} = require(protoGenCodePath);
|
||||
|
@ -613,6 +636,36 @@ describe('grpc-web generated code: callbacks tests', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should receive error, on http error - Content-Type not matching application/grpc*', function(done) {
|
||||
done = multiDone(done, 2);
|
||||
MockXMLHttpRequest.onSend = function(xhr) {
|
||||
xhr.respond(
|
||||
505, {'Content-Type': 'text/html'});
|
||||
};
|
||||
var call = echoService.echo(
|
||||
request, {},
|
||||
function(err, response) {
|
||||
if (response) {
|
||||
assert.fail('should not have received response with non-OK status');
|
||||
} else {
|
||||
assert('metadata' in err);
|
||||
assert('httpStatusCode' in err.metadata);
|
||||
assert.equal(505, err.metadata.httpStatusCode);
|
||||
}
|
||||
done();
|
||||
}
|
||||
);
|
||||
call.on('status', (status) => {
|
||||
assert('metadata' in status);
|
||||
assert('httpStatusCode' in status.metadata);
|
||||
assert.equal(505, status.metadata.httpStatusCode);
|
||||
done();
|
||||
});
|
||||
call.on('error', (error) => {
|
||||
assert.fail('error callback should not be called for unary calls');
|
||||
});
|
||||
});
|
||||
|
||||
it('should receive error, on http error', function(done) {
|
||||
done = multiDone(done, 2);
|
||||
MockXMLHttpRequest.onSend = function(xhr) {
|
||||
|
@ -802,6 +855,30 @@ describe('grpc-web generated code: callbacks tests', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should receive error, on http error (streaming) - Content-Type not matching application/grpc*', function(done) {
|
||||
done = multiDone(done, 2);
|
||||
MockXMLHttpRequest.onSend = function(xhr) {
|
||||
xhr.respond(
|
||||
505, {'Content-Type': 'text/html'});
|
||||
};
|
||||
var call = echoService.serverStreamingEcho(request, {});
|
||||
call.on('data', (response) => {
|
||||
assert.fail('should not receive data response');
|
||||
});
|
||||
call.on('status', (status) => {
|
||||
assert('metadata' in status);
|
||||
assert('httpStatusCode' in status.metadata);
|
||||
assert.equal(505, status.metadata.httpStatusCode);
|
||||
done();
|
||||
});
|
||||
call.on('error', (error) => {
|
||||
assert('metadata' in error);
|
||||
assert('httpStatusCode' in error.metadata);
|
||||
assert.equal(505, error.metadata.httpStatusCode);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should receive error, on http error (streaming)', function(done) {
|
||||
done = multiDone(done, 2);
|
||||
MockXMLHttpRequest.onSend = function(xhr) {
|
||||
|
|
Loading…
Reference in New Issue