mirror of https://github.com/grpc/grpc-dart.git
Use correct gRPC error codes. (#23)
Copied error code definitions from source grpc/grpc repo. Fixes #7.
This commit is contained in:
parent
a5e740c41a
commit
ed5e8fb43e
|
@ -10,6 +10,7 @@ import 'dart:io';
|
|||
import 'package:http2/transport.dart';
|
||||
|
||||
import 'shared.dart';
|
||||
import 'status.dart';
|
||||
import 'streams.dart';
|
||||
|
||||
const _reservedHeaders = const [
|
||||
|
@ -112,7 +113,7 @@ class ClientCall<Q, R> implements Response {
|
|||
_responses = new StreamController(onListen: _onResponseListen);
|
||||
_callSetup = _initiateCall().catchError((error) {
|
||||
_responses.addError(
|
||||
new GrpcError(1703, 'Error connecting: ${error.toString()}'));
|
||||
new GrpcError.unavailable('Error connecting: ${error.toString()}'));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -213,11 +214,13 @@ class ClientCall<Q, R> implements Response {
|
|||
void _onResponseData(GrpcMessage data) {
|
||||
if (data is GrpcData) {
|
||||
if (!_headers.isCompleted) {
|
||||
_responseError(new GrpcError(1217, 'Received data before headers'));
|
||||
_responseError(
|
||||
new GrpcError.unimplemented('Received data before headers'));
|
||||
return;
|
||||
}
|
||||
if (_trailers.isCompleted) {
|
||||
_responseError(new GrpcError(1218, 'Received data after trailers'));
|
||||
_responseError(
|
||||
new GrpcError.unimplemented('Received data after trailers'));
|
||||
return;
|
||||
}
|
||||
_responses.add(_method.responseDeserializer(data.data));
|
||||
|
@ -230,7 +233,8 @@ class ClientCall<Q, R> implements Response {
|
|||
return;
|
||||
}
|
||||
if (_trailers.isCompleted) {
|
||||
_responseError(new GrpcError(1219, 'Received multiple trailers'));
|
||||
_responseError(
|
||||
new GrpcError.unimplemented('Received multiple trailers'));
|
||||
return;
|
||||
}
|
||||
final metadata = data.metadata;
|
||||
|
@ -240,11 +244,11 @@ class ClientCall<Q, R> implements Response {
|
|||
final status = int.parse(metadata['grpc-status']);
|
||||
final message = metadata['grpc-message'];
|
||||
if (status != 0) {
|
||||
_responseError(new GrpcError(status, message, metadata));
|
||||
_responseError(new GrpcError.custom(status, message));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_responseError(new GrpcError(1220, 'Unexpected frame received'));
|
||||
_responseError(new GrpcError.unimplemented('Unexpected frame received'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,32 +259,33 @@ class ClientCall<Q, R> implements Response {
|
|||
_responseError(error);
|
||||
return;
|
||||
}
|
||||
_responseError(new GrpcError(1221, error.toString()));
|
||||
_responseError(new GrpcError.unknown(error.toString()));
|
||||
}
|
||||
|
||||
/// Handles closure of the response stream. Verifies that server has sent
|
||||
/// response messages and header/trailer metadata, as necessary.
|
||||
void _onResponseDone() {
|
||||
if (!_headers.isCompleted) {
|
||||
_responseError(new GrpcError(1223, 'Did not receive anything'));
|
||||
_responseError(new GrpcError.unavailable('Did not receive anything'));
|
||||
return;
|
||||
}
|
||||
if (!_trailers.isCompleted) {
|
||||
if (_hasReceivedResponses) {
|
||||
// Trailers are required after receiving data.
|
||||
_responseError(new GrpcError(1222, 'Missing trailers'));
|
||||
_responseError(new GrpcError.unavailable('Missing trailers'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Only received a header frame and no data frames, so the header
|
||||
// should contain "trailers" as well (Trailers-Only).
|
||||
_trailers.complete(_headerMetadata);
|
||||
final status = _headerMetadata['grpc-status'];
|
||||
// If status code is missing, we must treat it as '0'. As in 'success'.
|
||||
final statusCode = status != null ? int.parse(status) : 0;
|
||||
if (statusCode != 0) {
|
||||
final message = _headerMetadata['grpc-message'];
|
||||
_responseError(new GrpcError(statusCode, message, _headerMetadata));
|
||||
_responseError(new GrpcError.custom(statusCode, message));
|
||||
}
|
||||
// If status code is missing, we must treat it as '0'. As in 'success'.
|
||||
}
|
||||
_responses.close();
|
||||
_responseSubscription.cancel();
|
||||
|
@ -291,7 +296,7 @@ class ClientCall<Q, R> implements Response {
|
|||
/// error to the user code on the [_responses] stream.
|
||||
void _onRequestError(error) {
|
||||
if (error is! GrpcError) {
|
||||
error = new GrpcError(1217, error.toString());
|
||||
error = new GrpcError.unknown(error.toString());
|
||||
}
|
||||
|
||||
_responses.addError(error);
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'dart:io';
|
|||
|
||||
import 'package:http2/transport.dart';
|
||||
|
||||
import 'status.dart';
|
||||
import 'streams.dart';
|
||||
|
||||
/// Definition of a gRPC service method.
|
||||
|
@ -182,7 +183,7 @@ class ServerHandler {
|
|||
|
||||
void _onDataIdle(GrpcMessage message) {
|
||||
if (message is! GrpcMetadata) {
|
||||
_sendError(401, 'Expected header frame');
|
||||
_sendError(new GrpcError.unimplemented('Expected header frame'));
|
||||
return;
|
||||
}
|
||||
final headerMessage = message
|
||||
|
@ -190,14 +191,15 @@ class ServerHandler {
|
|||
_clientMetadata = headerMessage.metadata;
|
||||
final path = _clientMetadata[':path'].split('/');
|
||||
if (path.length < 3) {
|
||||
_sendError(404, 'Invalid path');
|
||||
_sendError(new GrpcError.unimplemented('Invalid path'));
|
||||
return;
|
||||
}
|
||||
final service = path[1];
|
||||
final method = path[2];
|
||||
_descriptor = _methodLookup(service, method);
|
||||
if (_descriptor == null) {
|
||||
_sendError(404, 'Path /$service/$method not found');
|
||||
_sendError(
|
||||
new GrpcError.unimplemented('Path /$service/$method not found'));
|
||||
return;
|
||||
}
|
||||
_startStreamingRequest();
|
||||
|
@ -239,29 +241,32 @@ class ServerHandler {
|
|||
|
||||
void _onDataActive(GrpcMessage message) {
|
||||
if (message is! GrpcData) {
|
||||
_sendError(711, 'Expected data frame');
|
||||
_sendError(new GrpcError.unimplemented('Expected data frame'));
|
||||
_requests
|
||||
..addError(new GrpcError(712, 'No request received'))
|
||||
..addError(new GrpcError.unimplemented('No request received'))
|
||||
..close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_hasReceivedRequest && !_descriptor.streamingRequest) {
|
||||
_sendError(712, 'Too many requests');
|
||||
final error = new GrpcError.unimplemented('Too many requests');
|
||||
_sendError(error);
|
||||
_requests
|
||||
..addError(new GrpcError(712, 'Too many requests'))
|
||||
..addError(error)
|
||||
..close();
|
||||
}
|
||||
|
||||
final data =
|
||||
message as GrpcData; // TODO(jakobr): Cast should not be necessary here.
|
||||
// TODO(jakobr): Cast should not be necessary here.
|
||||
final data = message as GrpcData;
|
||||
var request;
|
||||
try {
|
||||
request = _descriptor.requestDeserializer(data.data);
|
||||
} catch (error) {
|
||||
_sendError(730, 'Error deserializing request: $error');
|
||||
final grpcError =
|
||||
new GrpcError.internal('Error deserializing request: $error');
|
||||
_sendError(grpcError);
|
||||
_requests
|
||||
..addError(new GrpcError(730, 'Error deserializing request: $error'))
|
||||
..addError(grpcError)
|
||||
..close();
|
||||
return;
|
||||
}
|
||||
|
@ -283,7 +288,7 @@ class ServerHandler {
|
|||
if (!_requests.isClosed) {
|
||||
// If we can, alert the handler that things are going wrong.
|
||||
_requests
|
||||
.addError(new GrpcError(1001, 'Error sending response: $error'));
|
||||
.addError(new GrpcError.internal('Error sending response: $error'));
|
||||
_requests.close();
|
||||
}
|
||||
_incomingSubscription.cancel();
|
||||
|
@ -297,15 +302,14 @@ class ServerHandler {
|
|||
|
||||
void _onResponseError(error) {
|
||||
if (error is GrpcError) {
|
||||
// TODO(jakobr): error.metadata...
|
||||
_sendError(error.code, error.message);
|
||||
_sendError(error);
|
||||
} else {
|
||||
_sendError(107, error.toString());
|
||||
_sendError(new GrpcError.unknown(error.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
void _sendHeaders() {
|
||||
if (_headersSent) throw new GrpcError(1514, 'Headers already sent');
|
||||
if (_headersSent) throw new GrpcError.internal('Headers already sent');
|
||||
final headersMap = <String, String>{};
|
||||
headersMap.addAll(_customHeaders);
|
||||
_customHeaders = null;
|
||||
|
@ -354,24 +358,25 @@ class ServerHandler {
|
|||
// Exception from the incoming stream. Most likely a cancel request from the
|
||||
// client, so we treat it as such.
|
||||
_isCanceled = true;
|
||||
_requests.addError(new GrpcError(1001, 'Canceled'));
|
||||
_requests.addError(new GrpcError.cancelled('Cancelled'));
|
||||
_responseSubscription?.cancel();
|
||||
}
|
||||
|
||||
void _onDoneError() {
|
||||
_sendError(710, 'Request stream closed unexpectedly');
|
||||
_sendError(new GrpcError.unavailable('Request stream closed unexpectedly'));
|
||||
}
|
||||
|
||||
void _onDoneExpected() {
|
||||
if (!(_hasReceivedRequest || _descriptor.streamingRequest)) {
|
||||
_sendError(730, 'Expected request message');
|
||||
_requests.addError(new GrpcError(730, 'No request message received'));
|
||||
final error = new GrpcError.unimplemented('Expected request message');
|
||||
_sendError(error);
|
||||
_requests.addError(error);
|
||||
}
|
||||
_requests.close();
|
||||
_incomingSubscription.cancel();
|
||||
}
|
||||
|
||||
void _sendError(int status, String message) {
|
||||
_sendTrailers(status: status, message: message);
|
||||
void _sendError(GrpcError error) {
|
||||
_sendTrailers(status: error.code, message: error.message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
class StatusCode {
|
||||
/// The operation completed successfully.
|
||||
static final ok = 0;
|
||||
|
||||
/// The operation was cancelled (typically by the caller).
|
||||
static final cancelled = 1;
|
||||
|
||||
/// Unknown error. An example of where this error may be returned is if a
|
||||
/// Status value received from another address space belongs to an error-space
|
||||
/// that is not known in this address space. Also errors raised by APIs that
|
||||
/// do not return enough error information may be converted to this error.
|
||||
static final unknown = 2;
|
||||
|
||||
/// Client specified an invalid argument. Note that this differs from
|
||||
/// [failedPrecondition]. [invalidArgument] indicates arguments that are
|
||||
/// problematic regardless of the state of the system (e.g., a malformed file
|
||||
/// name).
|
||||
static final invalidArgument = 3;
|
||||
|
||||
/// Deadline expired before operation could complete. For operations that
|
||||
/// change the state of the system, this error may be returned even if the
|
||||
/// operation has completed successfully. For example, a successful response
|
||||
/// from a server could have been delayed long enough for the deadline to
|
||||
/// expire.
|
||||
static final deadlineExceeded = 4;
|
||||
|
||||
/// Some requested entity (e.g., file or directory) was not found.
|
||||
static final notFound = 5;
|
||||
|
||||
/// Some entity that we attempted to create (e.g., file or directory) already
|
||||
/// exists.
|
||||
static final alreadyExists = 6;
|
||||
|
||||
/// The caller does not have permission to execute the specified operation.
|
||||
/// [permissionDenied] must not be used for rejections caused by exhausting
|
||||
/// some resource (use [resourceExhausted] instead for those errors).
|
||||
/// [permissionDenied] must not be used if the caller cannot be identified
|
||||
/// (use [unauthenticated] instead for those errors).
|
||||
static final permissionDenied = 7;
|
||||
|
||||
/// Some resource has been exhausted, perhaps a per-user quota, or perhaps the
|
||||
/// entire file system is out of space.
|
||||
static final resourceExhausted = 8;
|
||||
|
||||
/// Operation was rejected because the system is not in a state required for
|
||||
/// the operation's execution. For example, directory to be deleted may be
|
||||
/// non-empty, an rmdir operation is applied to a non-directory, etc.
|
||||
///
|
||||
/// A litmus test that may help a service implementor in deciding between
|
||||
/// [failedPrecondition], [aborted], and [unavailable]:
|
||||
/// (a) Use [unavailable] if the client can retry just the failing call.
|
||||
/// (b) Use [aborted] if the client should retry at a higher-level (e.g.,
|
||||
/// restarting a read-modify-write sequence).
|
||||
/// (c) Use [failedPrecondition] if the client should not retry until the
|
||||
/// system state has been explicitly fixed. E.g., if an "rmdir" fails
|
||||
/// because the directory is non-empty, [failedPrecondition] should be
|
||||
/// returned since the client should not retry unless they have first
|
||||
/// fixed up the directory by deleting files from it.
|
||||
static final failedPrecondition = 9;
|
||||
|
||||
/// The operation was aborted, typically due to a concurrency issue like
|
||||
/// sequencer check failures, transaction aborts, etc.
|
||||
///
|
||||
/// See litmus test above for deciding between [failedPrecondition],
|
||||
/// [aborted], and [unavailable].
|
||||
static final aborted = 10;
|
||||
|
||||
/// Operation was attempted past the valid range. E.g., seeking or reading
|
||||
/// past end of file.
|
||||
///
|
||||
/// Unlike invalidArgument, this error indicates a problem that may be fixed
|
||||
/// if the system state changes. For example, a 32-bit file system will
|
||||
/// generate invalidArgument if asked to read at an offset that is not in the
|
||||
/// range [0,2^32-1], but it will generate [outOfRange] if asked to read from
|
||||
/// an offset past the current file size.
|
||||
///
|
||||
/// There is a fair bit of overlap between [failedPrecondition] and
|
||||
/// [outOfRange]. We recommend using [outOfRange] (the more specific error)
|
||||
/// when it applies so that callers who are iterating through a space can
|
||||
/// easily look for an [outOfRange] error to detect when they are done.
|
||||
static final outOfRange = 11;
|
||||
|
||||
/// Operation is not implemented or not supported/enabled in this service.
|
||||
static final unimplemented = 12;
|
||||
|
||||
/// Internal errors. Means some invariants expected by underlying system has
|
||||
/// been broken. If you see one of these errors, something is very broken.
|
||||
static final internal = 13;
|
||||
|
||||
/// The service is currently unavailable. This is a most likely a transient
|
||||
/// condition and may be corrected by retrying with a backoff.
|
||||
///
|
||||
/// See litmus test above for deciding between [failedPrecondition],
|
||||
/// [aborted], and [unavailable].
|
||||
static final unavailable = 14;
|
||||
|
||||
/// Unrecoverable data loss or corruption.
|
||||
static final dataLoss = 15;
|
||||
|
||||
/// The request does not have valid authentication credentials for the
|
||||
/// operation.
|
||||
static final unauthenticated = 16;
|
||||
}
|
||||
|
||||
class GrpcError {
|
||||
final int code;
|
||||
final String message;
|
||||
|
||||
/// Custom error code.
|
||||
GrpcError.custom(this.code, [this.message]);
|
||||
|
||||
/// The operation completed successfully.
|
||||
GrpcError.ok([this.message]) : code = StatusCode.ok;
|
||||
|
||||
/// The operation was cancelled (typically by the caller).
|
||||
GrpcError.cancelled([this.message]) : code = StatusCode.cancelled;
|
||||
|
||||
/// Unknown error. An example of where this error may be returned is if a
|
||||
/// Status value received from another address space belongs to an error-space
|
||||
/// that is not known in this address space. Also errors raised by APIs that
|
||||
/// do not return enough error information may be converted to this error.
|
||||
GrpcError.unknown([this.message]) : code = StatusCode.unknown;
|
||||
|
||||
/// Client specified an invalid argument. Note that this differs from
|
||||
/// [failedPrecondition]. [invalidArgument] indicates arguments that are
|
||||
/// problematic regardless of the state of the system (e.g., a malformed file
|
||||
/// name).
|
||||
GrpcError.invalidArgument([this.message]) : code = StatusCode.invalidArgument;
|
||||
|
||||
/// Deadline expired before operation could complete. For operations that
|
||||
/// change the state of the system, this error may be returned even if the
|
||||
/// operation has completed successfully. For example, a successful response
|
||||
/// from a server could have been delayed long enough for the deadline to
|
||||
/// expire.
|
||||
GrpcError.deadlineExceeded([this.message])
|
||||
: code = StatusCode.deadlineExceeded;
|
||||
|
||||
/// Some requested entity (e.g., file or directory) was not found.
|
||||
GrpcError.notFound([this.message]) : code = StatusCode.notFound;
|
||||
|
||||
/// Some entity that we attempted to create (e.g., file or directory) already
|
||||
/// exists.
|
||||
GrpcError.alreadyExists([this.message]) : code = StatusCode.alreadyExists;
|
||||
|
||||
/// The caller does not have permission to execute the specified operation.
|
||||
/// [permissionDenied] must not be used for rejections caused by exhausting
|
||||
/// some resource (use [resourceExhausted] instead for those errors).
|
||||
/// [permissionDenied] must not be used if the caller cannot be identified
|
||||
/// (use [unauthenticated] instead for those errors).
|
||||
GrpcError.permissionDenied([this.message])
|
||||
: code = StatusCode.permissionDenied;
|
||||
|
||||
/// Some resource has been exhausted, perhaps a per-user quota, or perhaps the
|
||||
/// entire file system is out of space.
|
||||
GrpcError.resourceExhausted([this.message])
|
||||
: code = StatusCode.resourceExhausted;
|
||||
|
||||
/// Operation was rejected because the system is not in a state required for
|
||||
/// the operation's execution. For example, directory to be deleted may be
|
||||
/// non-empty, an rmdir operation is applied to a non-directory, etc.
|
||||
///
|
||||
/// A litmus test that may help a service implementor in deciding between
|
||||
/// [failedPrecondition], [aborted], and [unavailable]:
|
||||
/// (a) Use [unavailable] if the client can retry just the failing call.
|
||||
/// (b) Use [aborted] if the client should retry at a higher-level (e.g.,
|
||||
/// restarting a read-modify-write sequence).
|
||||
/// (c) Use [failedPrecondition] if the client should not retry until the
|
||||
/// system state has been explicitly fixed. E.g., if an "rmdir" fails
|
||||
/// because the directory is non-empty, [failedPrecondition] should be
|
||||
/// returned since the client should not retry unless they have first
|
||||
/// fixed up the directory by deleting files from it.
|
||||
GrpcError.failedPrecondition([this.message])
|
||||
: code = StatusCode.failedPrecondition;
|
||||
|
||||
/// The operation was aborted, typically due to a concurrency issue like
|
||||
/// sequencer check failures, transaction aborts, etc.
|
||||
///
|
||||
/// See litmus test above for deciding between [failedPrecondition],
|
||||
/// [aborted], and [unavailable].
|
||||
GrpcError.aborted([this.message]) : code = StatusCode.aborted;
|
||||
|
||||
/// Operation was attempted past the valid range. E.g., seeking or reading
|
||||
/// past end of file.
|
||||
///
|
||||
/// Unlike invalidArgument, this error indicates a problem that may be fixed
|
||||
/// if the system state changes. For example, a 32-bit file system will
|
||||
/// generate invalidArgument if asked to read at an offset that is not in the
|
||||
/// range [0,2^32-1], but it will generate [outOfRange] if asked to read from
|
||||
/// an offset past the current file size.
|
||||
///
|
||||
/// There is a fair bit of overlap between [failedPrecondition] and
|
||||
/// [outOfRange]. We recommend using [outOfRange] (the more specific error)
|
||||
/// when it applies so that callers who are iterating through a space can
|
||||
/// easily look for an [outOfRange] error to detect when they are done.
|
||||
GrpcError.outOfRange([this.message]) : code = StatusCode.outOfRange;
|
||||
|
||||
/// Operation is not implemented or not supported/enabled in this service.
|
||||
GrpcError.unimplemented([this.message]) : code = StatusCode.unimplemented;
|
||||
|
||||
/// Internal errors. Means some invariants expected by underlying system has
|
||||
/// been broken. If you see one of these errors, something is very broken.
|
||||
GrpcError.internal([this.message]) : code = StatusCode.internal;
|
||||
|
||||
/// The service is currently unavailable. This is a most likely a transient
|
||||
/// condition and may be corrected by retrying with a backoff.
|
||||
///
|
||||
/// See litmus test above for deciding between [failedPrecondition],
|
||||
/// [aborted], and [unavailable].
|
||||
GrpcError.unavailable([this.message]) : code = StatusCode.unavailable;
|
||||
|
||||
/// Unrecoverable data loss or corruption.
|
||||
GrpcError.dataLoss([this.message]) : code = StatusCode.dataLoss;
|
||||
|
||||
/// The request does not have valid authentication credentials for the
|
||||
/// operation.
|
||||
GrpcError.unauthenticated([this.message]) : code = StatusCode.unauthenticated;
|
||||
|
||||
@override
|
||||
String toString() => 'gRPC Error ($code, $message)';
|
||||
}
|
|
@ -9,6 +9,8 @@ import 'dart:typed_data';
|
|||
|
||||
import 'package:http2/transport.dart';
|
||||
|
||||
import 'status.dart';
|
||||
|
||||
abstract class GrpcMessage {}
|
||||
|
||||
class GrpcMetadata extends GrpcMessage {
|
||||
|
@ -28,16 +30,6 @@ class GrpcData extends GrpcMessage {
|
|||
String toString() => 'gRPC Data (${data.length} bytes)';
|
||||
}
|
||||
|
||||
class GrpcError {
|
||||
final int code;
|
||||
final String message;
|
||||
final Map<String, String> metadata;
|
||||
GrpcError(this.code, this.message, [this.metadata = const {}]);
|
||||
|
||||
@override
|
||||
String toString() => 'gRPC Error ($code, $message, $metadata)';
|
||||
}
|
||||
|
||||
StreamTransformer<GrpcMessage, GrpcMessage> grpcDecompressor() =>
|
||||
new StreamTransformer<GrpcMessage, GrpcMessage>.fromHandlers(
|
||||
handleData: (GrpcMessage value, EventSink<GrpcMessage> sink) {
|
||||
|
@ -63,7 +55,7 @@ class GrpcHttpEncoder extends Converter<GrpcMessage, StreamMessage> {
|
|||
} else if (input is GrpcData) {
|
||||
return new DataStreamMessage(frame(input.data));
|
||||
}
|
||||
throw new GrpcError(99, 'Unexpected message type');
|
||||
throw new GrpcError.internal('Unexpected message type');
|
||||
}
|
||||
|
||||
static List<int> frame(List<int> payload) {
|
||||
|
@ -152,8 +144,7 @@ class _GrpcMessageConversionSink extends ChunkedConversionSink<StreamMessage> {
|
|||
if (_data != null || _dataOffset != 0) {
|
||||
// We were in the middle of receiving data, so receiving a header frame
|
||||
// is a violation of the gRPC protocol.
|
||||
throw new GrpcError(101,
|
||||
'Received header while reading ${_data == null ? 'header' : 'data (${_data.length} bytes)'} at offset $_dataOffset');
|
||||
throw new GrpcError.unimplemented('Received header while reading data');
|
||||
}
|
||||
final headers = <String, String>{};
|
||||
for (var header in chunk.headers) {
|
||||
|
@ -171,25 +162,26 @@ class _GrpcMessageConversionSink extends ChunkedConversionSink<StreamMessage> {
|
|||
@override
|
||||
void add(StreamMessage chunk) {
|
||||
if (_trailerReceived) {
|
||||
throw new GrpcError(102, 'Received data after trailer metadata');
|
||||
throw new GrpcError.unimplemented('Received data after trailer metadata');
|
||||
}
|
||||
if (chunk is DataStreamMessage) {
|
||||
if (!_headerReceived) {
|
||||
throw new GrpcError(103, 'Received data before header metadata');
|
||||
throw new GrpcError.unimplemented(
|
||||
'Received data before header metadata');
|
||||
}
|
||||
_addData(chunk);
|
||||
} else if (chunk is HeadersStreamMessage) {
|
||||
_addHeaders(chunk);
|
||||
} else {
|
||||
// No clue what this is.
|
||||
throw new GrpcError(104, 'Received unknown HTTP/2 frame type');
|
||||
throw new GrpcError.unimplemented('Received unknown HTTP/2 frame type');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void close() {
|
||||
if (_data != null || _dataOffset != 0) {
|
||||
throw new GrpcError(105, 'Closed in non-idle state');
|
||||
throw new GrpcError.unavailable('Closed in non-idle state');
|
||||
}
|
||||
_out.close();
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:http2/transport.dart';
|
|||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:grpc/src/streams.dart';
|
||||
import 'package:grpc/src/status.dart';
|
||||
|
||||
void main() {
|
||||
group('GrpcHttpDecoder', () {
|
||||
|
@ -26,7 +27,7 @@ void main() {
|
|||
await output.toList();
|
||||
fail('Did not throw exception');
|
||||
} on GrpcError catch (e) {
|
||||
expect(e.code, 103);
|
||||
expect(e.code, StatusCode.unimplemented);
|
||||
expect(e.message, 'Received data before header metadata');
|
||||
}
|
||||
});
|
||||
|
@ -69,7 +70,7 @@ void main() {
|
|||
await result;
|
||||
fail('Did not throw');
|
||||
} on GrpcError catch (e) {
|
||||
expect(e.code, 102);
|
||||
expect(e.code, StatusCode.unimplemented);
|
||||
expect(e.message, 'Received data after trailer metadata');
|
||||
}
|
||||
});
|
||||
|
@ -85,7 +86,7 @@ void main() {
|
|||
await result;
|
||||
fail('Did not throw');
|
||||
} on GrpcError catch (e) {
|
||||
expect(e.code, 105);
|
||||
expect(e.code, StatusCode.unavailable);
|
||||
expect(e.message, 'Closed in non-idle state');
|
||||
}
|
||||
});
|
||||
|
@ -100,7 +101,7 @@ void main() {
|
|||
await result;
|
||||
fail('Did not throw');
|
||||
} on GrpcError catch (e) {
|
||||
expect(e.code, 105);
|
||||
expect(e.code, StatusCode.unavailable);
|
||||
expect(e.message, 'Closed in non-idle state');
|
||||
}
|
||||
});
|
||||
|
@ -117,8 +118,8 @@ void main() {
|
|||
await result;
|
||||
fail('Did not throw');
|
||||
} on GrpcError catch (e) {
|
||||
expect(e.code, 101);
|
||||
expect(e.message, 'Received header while reading header at offset 4');
|
||||
expect(e.code, StatusCode.unimplemented);
|
||||
expect(e.message, 'Received header while reading data');
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -133,9 +134,8 @@ void main() {
|
|||
await result;
|
||||
fail('Did not throw');
|
||||
} on GrpcError catch (e) {
|
||||
expect(e.code, 101);
|
||||
expect(e.message,
|
||||
'Received header while reading data (2 bytes) at offset 1');
|
||||
expect(e.code, StatusCode.unimplemented);
|
||||
expect(e.message, 'Received header while reading data');
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue