diff --git a/lib/src/server/handler.dart b/lib/src/server/handler.dart index 798c8af..6b7cf4e 100644 --- a/lib/src/server/handler.dart +++ b/lib/src/server/handler.dart @@ -328,7 +328,8 @@ class ServerHandler_ extends ServiceCall { } @override - void sendTrailers({int? status = 0, String? message}) { + void sendTrailers( + {int? status = 0, String? message, Map? errorTrailers}) { _timeoutTimer?.cancel(); final outgoingTrailersMap = {}; @@ -354,6 +355,9 @@ class ServerHandler_ extends ServiceCall { outgoingTrailersMap['grpc-message'] = Uri.encodeFull(message).replaceAll('%20', ' '); } + if (errorTrailers != null) { + outgoingTrailersMap.addAll(errorTrailers); + } final outgoingTrailers =
[]; outgoingTrailersMap.forEach((key, value) => @@ -407,7 +411,11 @@ class ServerHandler_ extends ServiceCall { } void _sendError(GrpcError error) { - sendTrailers(status: error.code, message: error.message); + sendTrailers( + status: error.code, + message: error.message, + errorTrailers: error.trailers, + ); } void cancel() { diff --git a/test/round_trip_test.dart b/test/round_trip_test.dart index 9557d2d..2ba7c43 100644 --- a/test/round_trip_test.dart +++ b/test/round_trip_test.dart @@ -50,6 +50,22 @@ class TestServiceWithOnMetadataException extends TestService { } } +class TestServiceWithGrpcError extends TestService { + @override + Stream stream(ServiceCall call, Future request) async* { + throw GrpcError.custom( + StatusCode.internal, + 'This error should contain trailers', + null, + null, + { + 'key1': 'value1', + 'key2': 'value2', + }, + ); + } +} + class FixedConnectionClientChannel extends ClientChannelBase { final Http2ClientConnection clientConnection; List states = []; @@ -155,4 +171,30 @@ Future main() async { await channel.shutdown(); await server.shutdown(); }); + + test('trailers on server GrpcError', () async { + final server = Server([TestServiceWithGrpcError()]); + await server.serve(address: 'localhost', port: 0); + + final channel = FixedConnectionClientChannel(Http2ClientConnection( + 'localhost', + server.port!, + ChannelOptions(credentials: ChannelCredentials.insecure()), + )); + final testClient = TestClient(channel); + await expectLater( + testClient.stream(TestService.requestFiniteStream).toList(), + throwsA(predicate((e) { + final trailers = e.trailers; + if (trailers == null || trailers.length != 2) return false; + final entries = trailers.entries.toList(); + final isOk = entries[0].key == 'key1' && + entries[0].value == 'value1' && + entries[1].key == 'key2' && + entries[1].value == 'value2'; + return isOk; + })), + ); + await server.shutdown(); + }); }