mirror of https://github.com/grpc/grpc-dart.git
112 lines
3.2 KiB
Dart
112 lines
3.2 KiB
Dart
// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
import 'dart:async';
|
|
|
|
import '../shared/status.dart';
|
|
import 'call.dart';
|
|
|
|
/// Definition of a gRPC service method.
|
|
class ServiceMethod<Q, R> {
|
|
final String name;
|
|
|
|
final bool streamingRequest;
|
|
final bool streamingResponse;
|
|
|
|
final Q Function(List<int> request) requestDeserializer;
|
|
final List<int> Function(R response) responseSerializer;
|
|
|
|
final Function handler;
|
|
|
|
ServiceMethod(
|
|
this.name,
|
|
this.handler,
|
|
this.streamingRequest,
|
|
this.streamingResponse,
|
|
this.requestDeserializer,
|
|
this.responseSerializer);
|
|
|
|
StreamController<Q> createRequestStream(StreamSubscription incoming) =>
|
|
StreamController<Q>(
|
|
onListen: incoming.resume,
|
|
onPause: incoming.pause,
|
|
onResume: incoming.resume);
|
|
|
|
Q deserialize(List<int> data) => requestDeserializer(data);
|
|
|
|
List<int> serialize(dynamic response) => responseSerializer(response as R);
|
|
|
|
Stream<R> handle(ServiceCall call, Stream<Q> requests) {
|
|
if (streamingResponse) {
|
|
if (streamingRequest) {
|
|
return handler(call, requests);
|
|
} else {
|
|
return handler(call, _toSingleFuture(requests));
|
|
}
|
|
} else {
|
|
final response = streamingRequest
|
|
? handler(call, requests)
|
|
: handler(call, _toSingleFuture(requests));
|
|
return response.asStream();
|
|
}
|
|
}
|
|
|
|
Future<Q> _toSingleFuture(Stream<Q> stream) {
|
|
Q _ensureOnlyOneRequest(Q? previous, Q element) {
|
|
if (previous != null) {
|
|
throw GrpcError.unimplemented('More than one request received');
|
|
}
|
|
return element;
|
|
}
|
|
|
|
Q _ensureOneRequest(Q? value) {
|
|
if (value == null) throw GrpcError.unimplemented('No requests received');
|
|
return value;
|
|
}
|
|
|
|
final future =
|
|
stream.fold<Q?>(null, _ensureOnlyOneRequest).then(_ensureOneRequest);
|
|
// Make sure errors on the future aren't unhandled, but return the original
|
|
// future so the request handler can also get the error.
|
|
_awaitAndCatch(future);
|
|
return future;
|
|
}
|
|
|
|
void _awaitAndCatch<Q>(Future<Q> f) async {
|
|
try {
|
|
await f;
|
|
} catch (_) {}
|
|
}
|
|
}
|
|
|
|
/// Definition of a gRPC service.
|
|
abstract class Service {
|
|
final Map<String, ServiceMethod> _$methods = {};
|
|
|
|
String get $name;
|
|
|
|
void $addMethod(ServiceMethod method) {
|
|
_$methods[method.name] = method;
|
|
}
|
|
|
|
/// Client metadata handler.
|
|
///
|
|
/// Services can override this method to provide common handling of incoming
|
|
/// metadata from the client.
|
|
void $onMetadata(ServiceCall context) {}
|
|
|
|
ServiceMethod? $lookupMethod(String name) => _$methods[name];
|
|
}
|