mirror of https://github.com/grpc/grpc-dart.git
Migrate grpc to null safety (#432)
This commit is contained in:
parent
d3f0ec7f37
commit
1e1831a187
|
@ -11,12 +11,12 @@ on:
|
|||
|
||||
jobs:
|
||||
# Check code formating and static analysis on a single OS (linux)
|
||||
# against Dart stable and dev.
|
||||
# against Dart dev.
|
||||
analyze:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
sdk: [stable, dev]
|
||||
sdk: [dev]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: cedx/setup-dart@v2
|
||||
|
@ -43,7 +43,7 @@ jobs:
|
|||
|
||||
# Run tests on a matrix consisting of three dimensions:
|
||||
# 1. OS: mac, windows, linux
|
||||
# 2. release channel: stable, dev
|
||||
# 2. release channel: dev
|
||||
# 3. TODO: Dart execution mode: native, web
|
||||
test:
|
||||
needs: analyze
|
||||
|
@ -51,7 +51,7 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
sdk: [stable, dev]
|
||||
sdk: [dev]
|
||||
platform: [vm, chrome]
|
||||
exclude:
|
||||
# We only run Chrome tests on Linux. No need to run them
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
## 3.0.0-nullsafety.0
|
||||
|
||||
* Migrate library and tests to null safety.
|
||||
|
||||
## 2.9.0
|
||||
|
||||
* Added support for compression/decompression, which can be configured through
|
||||
|
|
|
@ -9,7 +9,7 @@ dependencies:
|
|||
async: ^2.2.0
|
||||
grpc:
|
||||
path: ../../
|
||||
protobuf: ^1.0.1
|
||||
protobuf: ^2.0.0-nullsafety
|
||||
|
||||
dev_dependencies:
|
||||
test: ^1.6.4
|
||||
|
|
|
@ -8,8 +8,7 @@ environment:
|
|||
dependencies:
|
||||
grpc:
|
||||
path: ../../
|
||||
protobuf: ^1.0.1
|
||||
protobuf: ^2.0.0-nullsafety
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^1.5.2
|
||||
build_web_compilers: ^2.1.1
|
|
@ -9,4 +9,4 @@ dependencies:
|
|||
async: ^2.2.0
|
||||
grpc:
|
||||
path: ../../
|
||||
protobuf: ^1.0.1
|
||||
protobuf: ^2.0.0-nullsafety
|
||||
|
|
|
@ -9,7 +9,7 @@ dependencies:
|
|||
async: ^2.2.0
|
||||
grpc:
|
||||
path: ../../
|
||||
protobuf: ^1.0.1
|
||||
protobuf: ^2.0.0-nullsafety
|
||||
|
||||
dev_dependencies:
|
||||
test: ^1.6.0
|
||||
|
|
|
@ -9,4 +9,4 @@ dependencies:
|
|||
async: ^2.2.0
|
||||
grpc:
|
||||
path: ../../
|
||||
protobuf: ^1.0.1
|
||||
protobuf: ^2.0.0-nullsafety
|
||||
|
|
|
@ -11,7 +11,7 @@ dependencies:
|
|||
collection: ^1.14.11
|
||||
grpc:
|
||||
path: ../
|
||||
protobuf: ^1.0.1
|
||||
protobuf: ^2.0.0-nullsafety
|
||||
|
||||
dev_dependencies:
|
||||
test: ^1.6.4
|
||||
|
|
|
@ -20,25 +20,26 @@ import 'package:googleapis_auth/auth.dart' as auth;
|
|||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../client/call.dart';
|
||||
import '../shared/status.dart';
|
||||
import 'rsa.dart';
|
||||
|
||||
const _tokenExpirationThreshold = Duration(seconds: 30);
|
||||
|
||||
abstract class BaseAuthenticator {
|
||||
auth.AccessToken _accessToken;
|
||||
String _lastUri;
|
||||
auth.AccessToken? _accessToken;
|
||||
late String _lastUri;
|
||||
var _lastUriSet = false;
|
||||
|
||||
Future<void> authenticate(Map<String, String> metadata, String uri) async {
|
||||
if (uri == null) {
|
||||
throw GrpcError.unauthenticated('Credentials require secure transport.');
|
||||
}
|
||||
if (_accessToken == null || _accessToken.hasExpired || uri != _lastUri) {
|
||||
if (_accessToken == null ||
|
||||
_accessToken!.hasExpired ||
|
||||
!_lastUriSet ||
|
||||
uri != _lastUri) {
|
||||
await obtainAccessCredentials(uri);
|
||||
_lastUri = uri;
|
||||
_lastUriSet = true;
|
||||
}
|
||||
|
||||
final auth = '${_accessToken.type} ${_accessToken.data}';
|
||||
final auth = '${_accessToken!.type} ${_accessToken!.data}';
|
||||
metadata['authorization'] = auth;
|
||||
|
||||
if (_tokenExpiresSoon) {
|
||||
|
@ -47,7 +48,7 @@ abstract class BaseAuthenticator {
|
|||
}
|
||||
}
|
||||
|
||||
bool get _tokenExpiresSoon => _accessToken.expiry
|
||||
bool get _tokenExpiresSoon => _accessToken!.expiry
|
||||
.subtract(_tokenExpirationThreshold)
|
||||
.isBefore(DateTime.now().toUtc());
|
||||
|
||||
|
@ -57,7 +58,7 @@ abstract class BaseAuthenticator {
|
|||
}
|
||||
|
||||
abstract class HttpBasedAuthenticator extends BaseAuthenticator {
|
||||
Future<void> _call;
|
||||
Future<void>? _call;
|
||||
|
||||
Future<void> obtainAccessCredentials(String uri) {
|
||||
if (_call == null) {
|
||||
|
@ -68,7 +69,7 @@ abstract class HttpBasedAuthenticator extends BaseAuthenticator {
|
|||
authClient.close();
|
||||
});
|
||||
}
|
||||
return _call;
|
||||
return _call!;
|
||||
}
|
||||
|
||||
Future<auth.AccessCredentials> obtainCredentialsWithClient(
|
||||
|
@ -77,18 +78,21 @@ abstract class HttpBasedAuthenticator extends BaseAuthenticator {
|
|||
|
||||
class JwtServiceAccountAuthenticator extends BaseAuthenticator {
|
||||
auth.ServiceAccountCredentials _serviceAccountCredentials;
|
||||
String _projectId;
|
||||
String _keyId;
|
||||
String? _projectId;
|
||||
String? _keyId;
|
||||
|
||||
JwtServiceAccountAuthenticator(String serviceAccountJson) {
|
||||
final serviceAccount = jsonDecode(serviceAccountJson);
|
||||
_serviceAccountCredentials =
|
||||
auth.ServiceAccountCredentials.fromJson(serviceAccount);
|
||||
_projectId = serviceAccount['project_id'];
|
||||
_keyId = serviceAccount['private_key_id'];
|
||||
}
|
||||
JwtServiceAccountAuthenticator.fromJson(
|
||||
Map<String, dynamic> serviceAccountJson)
|
||||
: _serviceAccountCredentials =
|
||||
auth.ServiceAccountCredentials.fromJson(serviceAccountJson),
|
||||
_projectId = serviceAccountJson['project_id'],
|
||||
_keyId = serviceAccountJson['private_key_id'];
|
||||
|
||||
String get projectId => _projectId;
|
||||
factory JwtServiceAccountAuthenticator(String serviceAccountJsonString) =>
|
||||
JwtServiceAccountAuthenticator.fromJson(
|
||||
jsonDecode(serviceAccountJsonString));
|
||||
|
||||
String? get projectId => _projectId;
|
||||
|
||||
Future<void> obtainAccessCredentials(String uri) async {
|
||||
_accessToken = _jwtTokenFor(_serviceAccountCredentials, _keyId, uri);
|
||||
|
@ -97,8 +101,8 @@ class JwtServiceAccountAuthenticator extends BaseAuthenticator {
|
|||
|
||||
// TODO(jakobr): Expose in googleapis_auth.
|
||||
auth.AccessToken _jwtTokenFor(
|
||||
auth.ServiceAccountCredentials credentials, String keyId, String uri,
|
||||
{String user, List<String> scopes}) {
|
||||
auth.ServiceAccountCredentials credentials, String? keyId, String uri,
|
||||
{String? user, List<String>? scopes}) {
|
||||
// Subtracting 20 seconds from current timestamp to allow for clock skew among
|
||||
// servers.
|
||||
final timestamp =
|
||||
|
|
|
@ -13,18 +13,22 @@ class ComputeEngineAuthenticator extends HttpBasedAuthenticator {
|
|||
}
|
||||
|
||||
class ServiceAccountAuthenticator extends HttpBasedAuthenticator {
|
||||
auth.ServiceAccountCredentials _serviceAccountCredentials;
|
||||
late auth.ServiceAccountCredentials _serviceAccountCredentials;
|
||||
final List<String> _scopes;
|
||||
String _projectId;
|
||||
String? _projectId;
|
||||
|
||||
ServiceAccountAuthenticator(String serviceAccountJson, this._scopes) {
|
||||
final serviceAccount = jsonDecode(serviceAccountJson);
|
||||
_serviceAccountCredentials =
|
||||
auth.ServiceAccountCredentials.fromJson(serviceAccount);
|
||||
_projectId = serviceAccount['project_id'];
|
||||
}
|
||||
ServiceAccountAuthenticator.fromJson(
|
||||
Map<String, dynamic> serviceAccountJson, this._scopes)
|
||||
: _serviceAccountCredentials =
|
||||
auth.ServiceAccountCredentials.fromJson(serviceAccountJson),
|
||||
_projectId = serviceAccountJson['project_id'];
|
||||
|
||||
String get projectId => _projectId;
|
||||
factory ServiceAccountAuthenticator(
|
||||
String serviceAccountJsonString, List<String> scopes) =>
|
||||
ServiceAccountAuthenticator.fromJson(
|
||||
jsonDecode(serviceAccountJsonString), scopes);
|
||||
|
||||
String? get projectId => _projectId;
|
||||
|
||||
Future<auth.AccessCredentials> obtainCredentialsWithClient(
|
||||
http.Client client, String uri) =>
|
||||
|
@ -35,7 +39,7 @@ class ServiceAccountAuthenticator extends HttpBasedAuthenticator {
|
|||
class _CredentialsRefreshingAuthenticator extends HttpBasedAuthenticator {
|
||||
final auth.ClientId _clientId;
|
||||
auth.AccessCredentials _accessCredentials;
|
||||
final String _quotaProject;
|
||||
final String? _quotaProject;
|
||||
|
||||
_CredentialsRefreshingAuthenticator(
|
||||
this._clientId,
|
||||
|
@ -47,7 +51,7 @@ class _CredentialsRefreshingAuthenticator extends HttpBasedAuthenticator {
|
|||
await super.authenticate(metadata, uri);
|
||||
if (_quotaProject != null) {
|
||||
// https://cloud.google.com/apis/docs/system-parameters#definitions
|
||||
metadata['X-Goog-User-Project'] = _quotaProject;
|
||||
metadata['X-Goog-User-Project'] = _quotaProject!;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,8 +87,8 @@ class _CredentialsRefreshingAuthenticator extends HttpBasedAuthenticator {
|
|||
Future<HttpBasedAuthenticator> applicationDefaultCredentialsAuthenticator(
|
||||
List<String> scopes,
|
||||
) async {
|
||||
File credFile;
|
||||
String fileSource;
|
||||
File? credFile;
|
||||
String? fileSource;
|
||||
// If env var specifies a file to load credentials from we'll do that.
|
||||
final credsEnv = Platform.environment['GOOGLE_APPLICATION_CREDENTIALS'];
|
||||
if (credsEnv != null && credsEnv.isNotEmpty) {
|
||||
|
@ -97,10 +101,10 @@ Future<HttpBasedAuthenticator> applicationDefaultCredentialsAuthenticator(
|
|||
// Attempt to use file created by `gcloud auth application-default login`
|
||||
File gcloudAdcFile;
|
||||
if (Platform.isWindows) {
|
||||
gcloudAdcFile = File.fromUri(Uri.directory(Platform.environment['APPDATA'])
|
||||
gcloudAdcFile = File.fromUri(Uri.directory(Platform.environment['APPDATA']!)
|
||||
.resolve('gcloud/application_default_credentials.json'));
|
||||
} else {
|
||||
gcloudAdcFile = File.fromUri(Uri.directory(Platform.environment['HOME'])
|
||||
gcloudAdcFile = File.fromUri(Uri.directory(Platform.environment['HOME']!)
|
||||
.resolve('.config/gcloud/application_default_credentials.json'));
|
||||
}
|
||||
// Only try to load from gcloudAdcFile if it exists.
|
||||
|
|
|
@ -99,7 +99,7 @@ class ASN1Parser {
|
|||
static const SEQUENCE_TAG = 0x30;
|
||||
|
||||
static ASN1Object parse(Uint8List bytes) {
|
||||
invalidFormat(String msg) {
|
||||
Never invalidFormat(String msg) {
|
||||
throw new ArgumentError("Invalid DER encoding: $msg");
|
||||
}
|
||||
|
||||
|
@ -185,8 +185,6 @@ class ASN1Parser {
|
|||
invalidFormat(
|
||||
'Unexpected tag $tag at offset ${offset - 1} (end: $end).');
|
||||
}
|
||||
// Unreachable.
|
||||
return null;
|
||||
}
|
||||
|
||||
final obj = decodeObject();
|
||||
|
|
|
@ -38,6 +38,7 @@ const _reservedHeaders = [
|
|||
'grpc-encoding',
|
||||
'user-agent',
|
||||
];
|
||||
const _statusDetailsHeader = 'grpc-status-details-bin';
|
||||
|
||||
/// Provides per-RPC metadata.
|
||||
///
|
||||
|
@ -55,9 +56,9 @@ typedef FutureOr<void> MetadataProvider(
|
|||
/// Runtime options for an RPC.
|
||||
class CallOptions {
|
||||
final Map<String, String> metadata;
|
||||
final Duration timeout;
|
||||
final Duration? timeout;
|
||||
final List<MetadataProvider> metadataProviders;
|
||||
final Codec compression;
|
||||
final Codec? compression;
|
||||
|
||||
CallOptions._(
|
||||
this.metadata,
|
||||
|
@ -73,10 +74,10 @@ class CallOptions {
|
|||
/// invoked in order for every RPC, and can modify the outgoing metadata
|
||||
/// (including metadata provided by previous providers).
|
||||
factory CallOptions({
|
||||
Map<String, String> metadata,
|
||||
Duration timeout,
|
||||
List<MetadataProvider> providers,
|
||||
Codec compression,
|
||||
Map<String, String>? metadata,
|
||||
Duration? timeout,
|
||||
List<MetadataProvider>? providers,
|
||||
Codec? compression,
|
||||
}) {
|
||||
return CallOptions._(
|
||||
Map.unmodifiable(metadata ?? {}),
|
||||
|
@ -89,7 +90,7 @@ class CallOptions {
|
|||
factory CallOptions.from(Iterable<CallOptions> options) =>
|
||||
options.fold(CallOptions(), (p, o) => p.mergedWith(o));
|
||||
|
||||
CallOptions mergedWith(CallOptions other) {
|
||||
CallOptions mergedWith(CallOptions? other) {
|
||||
if (other == null) return this;
|
||||
final mergedMetadata = Map.from(metadata)..addAll(other.metadata);
|
||||
final mergedTimeout = other.timeout ?? timeout;
|
||||
|
@ -116,16 +117,16 @@ class WebCallOptions extends CallOptions {
|
|||
/// For this to work correctly, a proxy server must be set up that
|
||||
/// understands the query parameter and can unpack/send the original
|
||||
/// list of headers to the server endpoint.
|
||||
final bool bypassCorsPreflight;
|
||||
final bool? bypassCorsPreflight;
|
||||
|
||||
/// Whether to send credentials along with the XHR.
|
||||
///
|
||||
/// This may be required for proxying or wherever the server
|
||||
/// needs to otherwise inspect client cookies for that domain.
|
||||
final bool withCredentials;
|
||||
final bool? withCredentials;
|
||||
// TODO(mightyvoice): add a list of extra QueryParameter for gRPC.
|
||||
|
||||
WebCallOptions._(Map<String, String> metadata, Duration timeout,
|
||||
WebCallOptions._(Map<String, String> metadata, Duration? timeout,
|
||||
List<MetadataProvider> metadataProviders,
|
||||
{this.bypassCorsPreflight, this.withCredentials})
|
||||
: super._(metadata, timeout, metadataProviders, null);
|
||||
|
@ -136,11 +137,11 @@ class WebCallOptions extends CallOptions {
|
|||
/// metadata [providers] of [CallOptions], [bypassCorsPreflight] and
|
||||
/// [withCredentials] for CORS request.
|
||||
factory WebCallOptions(
|
||||
{Map<String, String> metadata,
|
||||
Duration timeout,
|
||||
List<MetadataProvider> providers,
|
||||
bool bypassCorsPreflight,
|
||||
bool withCredentials}) {
|
||||
{Map<String, String>? metadata,
|
||||
Duration? timeout,
|
||||
List<MetadataProvider>? providers,
|
||||
bool? bypassCorsPreflight,
|
||||
bool? withCredentials}) {
|
||||
return WebCallOptions._(Map.unmodifiable(metadata ?? {}), timeout,
|
||||
List.unmodifiable(providers ?? []),
|
||||
bypassCorsPreflight: bypassCorsPreflight ?? false,
|
||||
|
@ -148,7 +149,7 @@ class WebCallOptions extends CallOptions {
|
|||
}
|
||||
|
||||
@override
|
||||
CallOptions mergedWith(CallOptions other) {
|
||||
CallOptions mergedWith(CallOptions? other) {
|
||||
if (other == null) return this;
|
||||
|
||||
final mergedMetadata = Map.from(metadata)..addAll(other.metadata);
|
||||
|
@ -163,11 +164,9 @@ class WebCallOptions extends CallOptions {
|
|||
withCredentials: withCredentials);
|
||||
}
|
||||
|
||||
final otherOptions = other as WebCallOptions;
|
||||
final mergedBypassCorsPreflight =
|
||||
otherOptions.bypassCorsPreflight ?? bypassCorsPreflight;
|
||||
final mergedWithCredentials =
|
||||
otherOptions.withCredentials ?? withCredentials;
|
||||
other.bypassCorsPreflight ?? bypassCorsPreflight;
|
||||
final mergedWithCredentials = other.withCredentials ?? withCredentials;
|
||||
return WebCallOptions._(Map.unmodifiable(mergedMetadata), mergedTimeout,
|
||||
List.unmodifiable(mergedProviders),
|
||||
bypassCorsPreflight: mergedBypassCorsPreflight,
|
||||
|
@ -185,28 +184,28 @@ class ClientCall<Q, R> implements Response {
|
|||
final _trailers = Completer<Map<String, String>>();
|
||||
bool _hasReceivedResponses = false;
|
||||
|
||||
Map<String, String> _headerMetadata;
|
||||
late Map<String, String> _headerMetadata;
|
||||
|
||||
GrpcTransportStream _stream;
|
||||
StreamController<R> _responses;
|
||||
StreamSubscription<List<int>> _requestSubscription;
|
||||
StreamSubscription<GrpcMessage> _responseSubscription;
|
||||
GrpcTransportStream? _stream;
|
||||
final _responses = StreamController<R>();
|
||||
StreamSubscription<List<int>>? _requestSubscription;
|
||||
StreamSubscription<GrpcMessage>? _responseSubscription;
|
||||
|
||||
bool isCancelled = false;
|
||||
Timer _timeoutTimer;
|
||||
Timer? _timeoutTimer;
|
||||
|
||||
final TimelineTask _requestTimeline;
|
||||
TimelineTask _responseTimeline;
|
||||
final TimelineTask? _requestTimeline;
|
||||
TimelineTask? _responseTimeline;
|
||||
|
||||
ClientCall(this._method, this._requests, this.options,
|
||||
[this._requestTimeline]) {
|
||||
_requestTimeline?.start('gRPC Request: ${_method.path}', arguments: {
|
||||
'method': _method.path,
|
||||
'timeout': options?.timeout?.toString(),
|
||||
'timeout': options.timeout?.toString(),
|
||||
});
|
||||
_responses = StreamController(onListen: _onResponseListen);
|
||||
_responses.onListen = _onResponseListen;
|
||||
if (options.timeout != null) {
|
||||
_timeoutTimer = Timer(options.timeout, _onTimedOut);
|
||||
_timeoutTimer = Timer(options.timeout!, _onTimedOut);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -251,7 +250,7 @@ class ClientCall<Q, R> implements Response {
|
|||
final metadata = Map<String, String>.from(options.metadata);
|
||||
Future.forEach(
|
||||
options.metadataProviders,
|
||||
(provider) => provider(metadata,
|
||||
(MetadataProvider provider) => provider(metadata,
|
||||
'${connection.scheme}://${connection.authority}${audiencePath(_method)}'))
|
||||
.then((_) => _sendRequest(connection, _sanitizeMetadata(metadata)))
|
||||
.catchError(_onMetadataProviderError);
|
||||
|
@ -263,8 +262,9 @@ class ClientCall<Q, R> implements Response {
|
|||
}
|
||||
|
||||
void _sendRequest(ClientConnection connection, Map<String, String> metadata) {
|
||||
late final GrpcTransportStream stream;
|
||||
try {
|
||||
_stream = connection.makeRequest(
|
||||
stream = connection.makeRequest(
|
||||
_method.path,
|
||||
options.timeout,
|
||||
metadata,
|
||||
|
@ -287,16 +287,17 @@ class ClientCall<Q, R> implements Response {
|
|||
return _method.requestSerializer(data);
|
||||
})
|
||||
.handleError(_onRequestError)
|
||||
.listen(_stream.outgoingMessages.add,
|
||||
onError: _stream.outgoingMessages.addError,
|
||||
onDone: _stream.outgoingMessages.close,
|
||||
.listen(stream.outgoingMessages.add,
|
||||
onError: stream.outgoingMessages.addError,
|
||||
onDone: stream.outgoingMessages.close,
|
||||
cancelOnError: true);
|
||||
_stream = stream;
|
||||
// The response stream might have been listened to before _stream was ready,
|
||||
// so try setting up the subscription here as well.
|
||||
_onResponseListen();
|
||||
}
|
||||
|
||||
void _finishTimelineWithError(GrpcError error, TimelineTask timeline) {
|
||||
void _finishTimelineWithError(GrpcError error, TimelineTask? timeline) {
|
||||
timeline?.finish(arguments: {
|
||||
'error': error.toString(),
|
||||
});
|
||||
|
@ -315,28 +316,31 @@ class ClientCall<Q, R> implements Response {
|
|||
if (_stream != null &&
|
||||
_responses.hasListener &&
|
||||
_responseSubscription == null) {
|
||||
_responseSubscription = _stream.incomingMessages.listen(_onResponseData,
|
||||
// ignore: cancel_subscriptions
|
||||
final subscription = _stream!.incomingMessages.listen(_onResponseData,
|
||||
onError: _onResponseError,
|
||||
onDone: _onResponseDone,
|
||||
cancelOnError: true);
|
||||
if (_responses.isPaused) {
|
||||
_responseSubscription.pause();
|
||||
subscription.pause();
|
||||
}
|
||||
_responses.onPause = _responseSubscription.pause;
|
||||
_responses.onResume = _responseSubscription.resume;
|
||||
_responses.onPause = subscription.pause;
|
||||
_responses.onResume = subscription.resume;
|
||||
_responses.onCancel = cancel;
|
||||
|
||||
_responseSubscription = subscription;
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit an error response to the user, and tear down this call.
|
||||
void _responseError(GrpcError error, [StackTrace stackTrace]) {
|
||||
void _responseError(GrpcError error, [StackTrace? stackTrace]) {
|
||||
_finishTimelineWithError(error, _responseTimeline);
|
||||
_responses.addError(error, stackTrace);
|
||||
_timeoutTimer?.cancel();
|
||||
_requestSubscription?.cancel();
|
||||
_responseSubscription.cancel();
|
||||
_responseSubscription!.cancel();
|
||||
_responses.close();
|
||||
_stream.terminate();
|
||||
_stream!.terminate();
|
||||
}
|
||||
|
||||
/// If there's an error status then process it as a response error
|
||||
|
@ -345,15 +349,17 @@ class ClientCall<Q, R> implements Response {
|
|||
final statusCode = int.parse(status ?? '0');
|
||||
|
||||
if (statusCode != 0) {
|
||||
final message = metadata['grpc-message'] == null
|
||||
? null
|
||||
: Uri.decodeFull(metadata['grpc-message']);
|
||||
final messageMetadata = metadata['grpc-message'];
|
||||
final message =
|
||||
messageMetadata == null ? null : Uri.decodeFull(messageMetadata);
|
||||
|
||||
final statusDetails = metadata[_statusDetailsHeader];
|
||||
_responseError(GrpcError.custom(
|
||||
statusCode,
|
||||
message,
|
||||
decodeStatusDetails(metadata['grpc-status-details-bin']),
|
||||
));
|
||||
statusCode,
|
||||
message,
|
||||
statusDetails == null
|
||||
? const <GeneratedMessage>[]
|
||||
: decodeStatusDetails(statusDetails)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -444,7 +450,7 @@ class ClientCall<Q, R> implements Response {
|
|||
_responseTimeline?.finish();
|
||||
_timeoutTimer?.cancel();
|
||||
_responses.close();
|
||||
_responseSubscription.cancel();
|
||||
_responseSubscription!.cancel();
|
||||
}
|
||||
|
||||
/// Error handler for the requests stream. Something went wrong while trying
|
||||
|
@ -461,7 +467,7 @@ class ClientCall<Q, R> implements Response {
|
|||
_responses.close();
|
||||
_requestSubscription?.cancel();
|
||||
_responseSubscription?.cancel();
|
||||
_stream.terminate();
|
||||
_stream!.terminate();
|
||||
}
|
||||
|
||||
Stream<R> get response => _responses.stream;
|
||||
|
@ -492,10 +498,10 @@ class ClientCall<Q, R> implements Response {
|
|||
_stream?.terminate();
|
||||
final futures = <Future>[];
|
||||
if (_requestSubscription != null) {
|
||||
futures.add(_requestSubscription.cancel());
|
||||
futures.add(_requestSubscription!.cancel());
|
||||
}
|
||||
if (_responseSubscription != null) {
|
||||
futures.add(_responseSubscription.cancel());
|
||||
futures.add(_responseSubscription!.cancel());
|
||||
}
|
||||
await Future.wait(futures);
|
||||
}
|
||||
|
|
|
@ -44,8 +44,8 @@ abstract class ClientChannel {
|
|||
/// Auxiliary base class implementing much of ClientChannel.
|
||||
abstract class ClientChannelBase implements ClientChannel {
|
||||
// TODO(jakobr): Multiple connections, load balancing.
|
||||
ClientConnection _connection;
|
||||
|
||||
late ClientConnection _connection;
|
||||
var _connected = false;
|
||||
bool _isShutdown = false;
|
||||
|
||||
ClientChannelBase();
|
||||
|
@ -54,13 +54,17 @@ abstract class ClientChannelBase implements ClientChannel {
|
|||
Future<void> shutdown() async {
|
||||
if (_isShutdown) return;
|
||||
_isShutdown = true;
|
||||
if (_connection != null) await _connection.shutdown();
|
||||
if (_connected) {
|
||||
await _connection.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> terminate() async {
|
||||
_isShutdown = true;
|
||||
if (_connection != null) await _connection.terminate();
|
||||
if (_connected) {
|
||||
await _connection.terminate();
|
||||
}
|
||||
}
|
||||
|
||||
ClientConnection createConnection();
|
||||
|
@ -70,7 +74,11 @@ abstract class ClientChannelBase implements ClientChannel {
|
|||
/// The connection may be shared between multiple RPCs.
|
||||
Future<ClientConnection> getConnection() async {
|
||||
if (_isShutdown) throw GrpcError.unavailable('Channel shutting down.');
|
||||
return _connection ??= createConnection();
|
||||
if (!_connected) {
|
||||
_connection = createConnection();
|
||||
_connected = true;
|
||||
}
|
||||
return _connection;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -27,7 +27,7 @@ class Client {
|
|||
|
||||
/// Interceptors will be applied in direct order before making a request.
|
||||
Client(this._channel,
|
||||
{CallOptions options, Iterable<ClientInterceptor> interceptors})
|
||||
{CallOptions? options, Iterable<ClientInterceptor>? interceptors})
|
||||
: _options = options ?? CallOptions(),
|
||||
_interceptors = List.unmodifiable(interceptors ?? Iterable.empty());
|
||||
|
||||
|
@ -39,12 +39,12 @@ regenerate these stubs using protobuf compiler plugin version 19.2.0 or newer.
|
|||
''')
|
||||
ClientCall<Q, R> $createCall<Q, R>(
|
||||
ClientMethod<Q, R> method, Stream<Q> requests,
|
||||
{CallOptions options}) {
|
||||
{CallOptions? options}) {
|
||||
return _channel.createCall(method, requests, _options.mergedWith(options));
|
||||
}
|
||||
|
||||
ResponseFuture<R> $createUnaryCall<Q, R>(ClientMethod<Q, R> method, Q request,
|
||||
{CallOptions options}) {
|
||||
{CallOptions? options}) {
|
||||
ClientUnaryInvoker<Q, R> invoker = (method, request, options) =>
|
||||
ResponseFuture<R>(
|
||||
_channel.createCall<Q, R>(method, Stream.value(request), options));
|
||||
|
@ -60,7 +60,7 @@ regenerate these stubs using protobuf compiler plugin version 19.2.0 or newer.
|
|||
|
||||
ResponseStream<R> $createStreamingCall<Q, R>(
|
||||
ClientMethod<Q, R> method, Stream<Q> requests,
|
||||
{CallOptions options}) {
|
||||
{CallOptions? options}) {
|
||||
ClientStreamingInvoker<Q, R> invoker = (method, request, options) =>
|
||||
ResponseStream<R>(_channel.createCall<Q, R>(method, requests, options));
|
||||
|
||||
|
|
|
@ -46,21 +46,21 @@ class ResponseFuture<R> extends DelegatingFuture<R>
|
|||
with _ResponseMixin<dynamic, R> {
|
||||
final ClientCall<dynamic, R> _call;
|
||||
|
||||
static R _ensureOnlyOneResponse<R>(R previous, R element) {
|
||||
static R _ensureOnlyOneResponse<R>(R? previous, R element) {
|
||||
if (previous != null) {
|
||||
throw GrpcError.unimplemented('More than one response received');
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
static R _ensureOneResponse<R>(R value) {
|
||||
static R _ensureOneResponse<R>(R? value) {
|
||||
if (value == null) throw GrpcError.unimplemented('No responses received');
|
||||
return value;
|
||||
}
|
||||
|
||||
ResponseFuture(this._call)
|
||||
: super(_call.response
|
||||
.fold(null, _ensureOnlyOneResponse)
|
||||
.fold<R?>(null, _ensureOnlyOneResponse)
|
||||
.then(_ensureOneResponse));
|
||||
}
|
||||
|
||||
|
|
|
@ -42,9 +42,9 @@ abstract class ClientConnection {
|
|||
void dispatchCall(ClientCall call);
|
||||
|
||||
/// Start a request for [path] with [metadata].
|
||||
GrpcTransportStream makeRequest(String path, Duration timeout,
|
||||
GrpcTransportStream makeRequest(String path, Duration? timeout,
|
||||
Map<String, String> metadata, ErrorHandler onRequestFailure,
|
||||
{CallOptions callOptions});
|
||||
{required CallOptions callOptions});
|
||||
|
||||
/// Shuts down this connection.
|
||||
///
|
||||
|
|
|
@ -44,19 +44,19 @@ class Http2ClientConnection implements connection.ClientConnection {
|
|||
connection.ConnectionState _state = ConnectionState.idle;
|
||||
|
||||
@visibleForTesting
|
||||
void Function(Http2ClientConnection connection) onStateChanged;
|
||||
void Function(Http2ClientConnection connection)? onStateChanged;
|
||||
final _pendingCalls = <ClientCall>[];
|
||||
|
||||
final ClientTransportConnector _transportConnector;
|
||||
ClientTransportConnection _transportConnection;
|
||||
ClientTransportConnection? _transportConnection;
|
||||
|
||||
/// Used for idle and reconnect timeout, depending on [_state].
|
||||
Timer _timer;
|
||||
Timer? _timer;
|
||||
|
||||
/// Used for making sure a single connection is not kept alive too long.
|
||||
final Stopwatch _connectionLifeTimer = Stopwatch();
|
||||
|
||||
Duration _currentReconnectDelay;
|
||||
Duration? _currentReconnectDelay;
|
||||
|
||||
Http2ClientConnection(Object host, int port, this.options)
|
||||
: _transportConnector = _SocketTransportConnector(host, port, options);
|
||||
|
@ -96,7 +96,7 @@ class Http2ClientConnection implements connection.ClientConnection {
|
|||
return;
|
||||
}
|
||||
_setState(ConnectionState.connecting);
|
||||
connectTransport().then((transport) async {
|
||||
connectTransport().then<void>((transport) async {
|
||||
_currentReconnectDelay = null;
|
||||
_transportConnection = transport;
|
||||
_connectionLifeTimer
|
||||
|
@ -119,11 +119,11 @@ class Http2ClientConnection implements connection.ClientConnection {
|
|||
///
|
||||
/// Assumes [_transportConnection] is not `null`.
|
||||
void _refreshConnectionIfUnhealthy() {
|
||||
final bool isHealthy = _transportConnection.isOpen;
|
||||
final bool isHealthy = _transportConnection!.isOpen;
|
||||
final bool shouldRefresh =
|
||||
_connectionLifeTimer.elapsed > options.connectionTimeout;
|
||||
if (shouldRefresh) {
|
||||
_transportConnection.finish();
|
||||
_transportConnection!.finish();
|
||||
}
|
||||
if (!isHealthy || shouldRefresh) {
|
||||
_abandonConnection();
|
||||
|
@ -149,9 +149,9 @@ class Http2ClientConnection implements connection.ClientConnection {
|
|||
}
|
||||
}
|
||||
|
||||
GrpcTransportStream makeRequest(String path, Duration timeout,
|
||||
GrpcTransportStream makeRequest(String path, Duration? timeout,
|
||||
Map<String, String> metadata, ErrorHandler onRequestFailure,
|
||||
{CallOptions callOptions}) {
|
||||
{CallOptions? callOptions}) {
|
||||
final compressionCodec = callOptions?.compression;
|
||||
final headers = createCallHeaders(
|
||||
credentials.isSecure,
|
||||
|
@ -165,7 +165,7 @@ class Http2ClientConnection implements connection.ClientConnection {
|
|||
(callOptions?.metadata ?? const {})['grpc-accept-encoding'] ??
|
||||
options.codecRegistry?.supportedEncodings,
|
||||
);
|
||||
final stream = _transportConnection.makeRequest(headers);
|
||||
final stream = _transportConnection!.makeRequest(headers);
|
||||
return Http2TransportStream(
|
||||
stream,
|
||||
onRequestFailure,
|
||||
|
@ -208,9 +208,7 @@ class Http2ClientConnection implements connection.ClientConnection {
|
|||
|
||||
void _setState(ConnectionState state) {
|
||||
_state = state;
|
||||
if (onStateChanged != null) {
|
||||
onStateChanged(this);
|
||||
}
|
||||
onStateChanged?.call(this);
|
||||
}
|
||||
|
||||
void _handleIdleTimeout() {
|
||||
|
@ -218,7 +216,7 @@ class Http2ClientConnection implements connection.ClientConnection {
|
|||
_cancelTimer();
|
||||
_transportConnection
|
||||
?.finish()
|
||||
?.catchError((_) => {}); // TODO(jakobr): Log error.
|
||||
.catchError((_) {}); // TODO(jakobr): Log error.
|
||||
_transportConnection = null;
|
||||
_setState(ConnectionState.idle);
|
||||
}
|
||||
|
@ -233,7 +231,7 @@ class Http2ClientConnection implements connection.ClientConnection {
|
|||
_cancelTimer();
|
||||
} else {
|
||||
if (options.idleTimeout != null) {
|
||||
_timer ??= Timer(options.idleTimeout, _handleIdleTimeout);
|
||||
_timer ??= Timer(options.idleTimeout!, _handleIdleTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -281,18 +279,18 @@ class Http2ClientConnection implements connection.ClientConnection {
|
|||
// We have pending RPCs. Reconnect after backoff delay.
|
||||
_setState(ConnectionState.transientFailure);
|
||||
_currentReconnectDelay = options.backoffStrategy(_currentReconnectDelay);
|
||||
_timer = Timer(_currentReconnectDelay, _handleReconnect);
|
||||
_timer = Timer(_currentReconnectDelay!, _handleReconnect);
|
||||
}
|
||||
|
||||
static List<Header> createCallHeaders(
|
||||
bool useTls,
|
||||
String authority,
|
||||
String path,
|
||||
Duration timeout,
|
||||
Map<String, String> metadata,
|
||||
Codec compressionCodec, {
|
||||
String userAgent,
|
||||
String grpcAcceptEncodings,
|
||||
Duration? timeout,
|
||||
Map<String, String>? metadata,
|
||||
Codec? compressionCodec, {
|
||||
String? userAgent,
|
||||
String? grpcAcceptEncodings,
|
||||
}) {
|
||||
final headers = [
|
||||
_methodPost,
|
||||
|
@ -317,12 +315,14 @@ class Http2ClientConnection implements connection.ClientConnection {
|
|||
}
|
||||
|
||||
class _SocketTransportConnector implements ClientTransportConnector {
|
||||
/// Either [InternetAddress] or [String].
|
||||
final Object _host;
|
||||
final int _port;
|
||||
final ChannelOptions _options;
|
||||
Socket _socket;
|
||||
late Socket _socket; // ignore: close_sinks
|
||||
|
||||
_SocketTransportConnector(this._host, this._port, this._options);
|
||||
_SocketTransportConnector(this._host, this._port, this._options)
|
||||
: assert(_host is InternetAddress || _host is String);
|
||||
|
||||
@override
|
||||
Future<ClientTransportConnection> connect() async {
|
||||
|
@ -349,19 +349,22 @@ class _SocketTransportConnector implements ClientTransportConnector {
|
|||
}
|
||||
|
||||
@override
|
||||
String get authority =>
|
||||
_options.credentials.authority ??
|
||||
(_port == 443 ? _host : "$_host:$_port");
|
||||
String get authority {
|
||||
final host =
|
||||
_host is String ? _host as String : (_host as InternetAddress).host;
|
||||
return _options.credentials.authority ??
|
||||
(_port == 443 ? host : "$host:$_port");
|
||||
}
|
||||
|
||||
@override
|
||||
Future get done {
|
||||
assert(_socket != null);
|
||||
ArgumentError.checkNotNull(_socket);
|
||||
return _socket.done;
|
||||
}
|
||||
|
||||
@override
|
||||
void shutdown() {
|
||||
assert(_socket != null);
|
||||
ArgumentError.checkNotNull(_socket);
|
||||
_socket.destroy();
|
||||
}
|
||||
|
||||
|
|
|
@ -3,10 +3,10 @@ import 'common.dart';
|
|||
import 'method.dart';
|
||||
|
||||
typedef ClientUnaryInvoker<Q, R> = ResponseFuture<R> Function(
|
||||
ClientMethod method, Q request, CallOptions options);
|
||||
ClientMethod<Q, R> method, Q request, CallOptions options);
|
||||
|
||||
typedef ClientStreamingInvoker<Q, R> = ResponseStream<R> Function(
|
||||
ClientMethod method, Stream<Q> requests, CallOptions options);
|
||||
ClientMethod<Q, R> method, Stream<Q> requests, CallOptions options);
|
||||
|
||||
/// ClientInterceptors intercepts client calls before they are executed.
|
||||
///
|
||||
|
|
|
@ -26,7 +26,7 @@ const defaultIdleTimeout = Duration(minutes: 5);
|
|||
const defaultConnectionTimeOut = Duration(minutes: 50);
|
||||
const defaultUserAgent = 'dart-grpc/2.0.0';
|
||||
|
||||
typedef Duration BackoffStrategy(Duration lastBackoff);
|
||||
typedef Duration BackoffStrategy(Duration? lastBackoff);
|
||||
|
||||
// Backoff algorithm from https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md
|
||||
const _initialBackoff = Duration(seconds: 1);
|
||||
|
@ -35,7 +35,7 @@ const _multiplier = 1.6;
|
|||
const _jitter = 0.2;
|
||||
final _random = Random();
|
||||
|
||||
Duration defaultBackoffStrategy(Duration lastBackoff) {
|
||||
Duration defaultBackoffStrategy(Duration? lastBackoff) {
|
||||
if (lastBackoff == null) return _initialBackoff;
|
||||
final jitter = _random.nextDouble() * 2 * _jitter - _jitter;
|
||||
final nextBackoff = lastBackoff * (_multiplier + jitter);
|
||||
|
@ -45,8 +45,8 @@ Duration defaultBackoffStrategy(Duration lastBackoff) {
|
|||
/// Options controlling how connections are made on a [ClientChannel].
|
||||
class ChannelOptions {
|
||||
final ChannelCredentials credentials;
|
||||
final Duration idleTimeout;
|
||||
final CodecRegistry codecRegistry;
|
||||
final Duration? idleTimeout;
|
||||
final CodecRegistry? codecRegistry;
|
||||
|
||||
/// The maximum time a single connection will be used for new requests.
|
||||
final Duration connectionTimeout;
|
||||
|
|
|
@ -29,17 +29,24 @@ class QueryParameter implements Comparable<QueryParameter> {
|
|||
String get value => values.first;
|
||||
|
||||
/// Creates an instance by wrapping the single value in a [List].
|
||||
QueryParameter(this.key, String value)
|
||||
: assert(key != null && key.trim().isNotEmpty),
|
||||
values = [value];
|
||||
QueryParameter(this.key, String value) : values = [value] {
|
||||
ArgumentError.checkNotNull(key);
|
||||
if (key.trim().isEmpty) {
|
||||
throw ArgumentError(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an instance from a [List] of values.
|
||||
///
|
||||
/// This is not the default constructor since the single-value case is the
|
||||
/// most common.
|
||||
QueryParameter.multi(this.key, List<String> values)
|
||||
: assert(key != null && key.trim().isNotEmpty),
|
||||
values = values..sort();
|
||||
: values = values..sort() {
|
||||
ArgumentError.checkNotNull(key);
|
||||
if (key.trim().isEmpty) {
|
||||
throw ArgumentError(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the escaped value of the param as will appear in a URL.
|
||||
@override
|
||||
|
|
|
@ -32,32 +32,32 @@ bool allowBadCertificates(X509Certificate certificate, String host) => true;
|
|||
/// Options controlling TLS security settings on a [ClientChannel].
|
||||
class ChannelCredentials {
|
||||
final bool isSecure;
|
||||
final String authority;
|
||||
final List<int> _certificateBytes;
|
||||
final String _certificatePassword;
|
||||
final BadCertificateHandler onBadCertificate;
|
||||
final String? authority;
|
||||
final List<int>? _certificateBytes;
|
||||
final String? _certificatePassword;
|
||||
final BadCertificateHandler? onBadCertificate;
|
||||
|
||||
const ChannelCredentials._(this.isSecure, this._certificateBytes,
|
||||
this._certificatePassword, this.authority, this.onBadCertificate);
|
||||
|
||||
/// Disable TLS. RPCs are sent in clear text.
|
||||
const ChannelCredentials.insecure({String authority})
|
||||
const ChannelCredentials.insecure({String? authority})
|
||||
: this._(false, null, null, authority, null);
|
||||
|
||||
/// Enable TLS and optionally specify the [certificates] to trust. If
|
||||
/// [certificates] is not provided, the default trust store is used.
|
||||
const ChannelCredentials.secure(
|
||||
{List<int> certificates,
|
||||
String password,
|
||||
String authority,
|
||||
BadCertificateHandler onBadCertificate})
|
||||
{List<int>? certificates,
|
||||
String? password,
|
||||
String? authority,
|
||||
BadCertificateHandler? onBadCertificate})
|
||||
: this._(true, certificates, password, authority, onBadCertificate);
|
||||
|
||||
SecurityContext get securityContext {
|
||||
SecurityContext? get securityContext {
|
||||
if (!isSecure) return null;
|
||||
if (_certificateBytes != null) {
|
||||
return createSecurityContext(false)
|
||||
..setTrustedCertificatesBytes(_certificateBytes,
|
||||
..setTrustedCertificatesBytes(_certificateBytes!,
|
||||
password: _certificatePassword);
|
||||
}
|
||||
final context = new SecurityContext(withTrustedRoots: true);
|
||||
|
|
|
@ -15,9 +15,10 @@
|
|||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:grpc/grpc.dart';
|
||||
import 'package:http2/transport.dart';
|
||||
|
||||
import '../../shared/codec.dart';
|
||||
import '../../shared/codec_registry.dart';
|
||||
import '../../shared/message.dart';
|
||||
import '../../shared/streams.dart';
|
||||
import 'transport.dart';
|
||||
|
@ -33,8 +34,8 @@ class Http2TransportStream extends GrpcTransportStream {
|
|||
Http2TransportStream(
|
||||
this._transportStream,
|
||||
this._onError,
|
||||
CodecRegistry codecRegistry,
|
||||
Codec compression,
|
||||
CodecRegistry? codecRegistry,
|
||||
Codec? compression,
|
||||
) : incomingMessages = _transportStream.incomingMessages
|
||||
.transform(GrpcHttpDecoder())
|
||||
.transform(grpcDecompressor(codecRegistry: codecRegistry)) {
|
||||
|
|
|
@ -47,10 +47,10 @@ class _GrpcWebConversionSink extends ChunkedConversionSink<ByteBuffer> {
|
|||
final _dataHeader = Uint8List(4);
|
||||
|
||||
_GrpcWebParseState _state = _GrpcWebParseState.Init;
|
||||
int _chunkOffset;
|
||||
int _frameType;
|
||||
int _dataOffset = 0;
|
||||
Uint8List _data;
|
||||
var _chunkOffset = 0;
|
||||
int? _frameType;
|
||||
var _dataOffset = 0;
|
||||
Uint8List? _data;
|
||||
|
||||
_GrpcWebConversionSink(this._out);
|
||||
|
||||
|
@ -87,16 +87,16 @@ class _GrpcWebConversionSink extends ChunkedConversionSink<ByteBuffer> {
|
|||
}
|
||||
|
||||
void _parseMessage(List<int> chunkData) {
|
||||
final dataRemaining = _data.lengthInBytes - _dataOffset;
|
||||
final dataRemaining = _data!.lengthInBytes - _dataOffset;
|
||||
if (dataRemaining > 0) {
|
||||
final chunkRemaining = chunkData.length - _chunkOffset;
|
||||
final toCopy = min(dataRemaining, chunkRemaining);
|
||||
_data.setRange(
|
||||
_dataOffset, _dataOffset + toCopy, chunkData, _chunkOffset);
|
||||
_data!
|
||||
.setRange(_dataOffset, _dataOffset + toCopy, chunkData, _chunkOffset);
|
||||
_dataOffset += toCopy;
|
||||
_chunkOffset += toCopy;
|
||||
}
|
||||
if (_dataOffset == _data.lengthInBytes) {
|
||||
if (_dataOffset == _data!.lengthInBytes) {
|
||||
_finishMessage();
|
||||
}
|
||||
}
|
||||
|
@ -104,10 +104,10 @@ class _GrpcWebConversionSink extends ChunkedConversionSink<ByteBuffer> {
|
|||
void _finishMessage() {
|
||||
switch (_frameType) {
|
||||
case frameTypeData:
|
||||
_out.add(GrpcData(_data, isCompressed: false));
|
||||
_out.add(GrpcData(_data!, isCompressed: false));
|
||||
break;
|
||||
case frameTypeTrailers:
|
||||
final stringData = String.fromCharCodes(_data);
|
||||
final stringData = String.fromCharCodes(_data!);
|
||||
final headers = _parseHttp1Headers(stringData);
|
||||
_out.add(GrpcMetadata(headers));
|
||||
break;
|
||||
|
@ -119,7 +119,7 @@ class _GrpcWebConversionSink extends ChunkedConversionSink<ByteBuffer> {
|
|||
|
||||
Map<String, String> _parseHttp1Headers(String stringData) {
|
||||
final trimmed = stringData.trim();
|
||||
final chunks = trimmed == '' ? [] : trimmed.split('\r\n');
|
||||
final chunks = trimmed == '' ? <String>[] : trimmed.split('\r\n');
|
||||
final headers = <String, String>{};
|
||||
for (final chunk in chunks) {
|
||||
final pos = chunk.indexOf(':');
|
||||
|
|
|
@ -51,7 +51,8 @@ class XhrTransportStream implements GrpcTransportStream {
|
|||
@override
|
||||
StreamSink<List<int>> get outgoingMessages => _outgoingMessages.sink;
|
||||
|
||||
XhrTransportStream(this._request, {onError, onDone})
|
||||
XhrTransportStream(this._request,
|
||||
{required ErrorHandler onError, required onDone})
|
||||
: _onError = onError,
|
||||
_onDone = onDone {
|
||||
_outgoingMessages.stream
|
||||
|
@ -170,7 +171,7 @@ class XhrClientConnection extends ClientConnection {
|
|||
|
||||
void _initializeRequest(HttpRequest request, Map<String, String> metadata) {
|
||||
for (final header in metadata.keys) {
|
||||
request.setRequestHeader(header, metadata[header]);
|
||||
request.setRequestHeader(header, metadata[header]!);
|
||||
}
|
||||
// Overriding the mimetype allows us to stream and parse the data
|
||||
request.overrideMimeType('text/plain; charset=x-user-defined');
|
||||
|
@ -181,9 +182,9 @@ class XhrClientConnection extends ClientConnection {
|
|||
HttpRequest createHttpRequest() => HttpRequest();
|
||||
|
||||
@override
|
||||
GrpcTransportStream makeRequest(String path, Duration timeout,
|
||||
GrpcTransportStream makeRequest(String path, Duration? timeout,
|
||||
Map<String, String> metadata, ErrorHandler onError,
|
||||
{CallOptions callOptions}) {
|
||||
{CallOptions? callOptions}) {
|
||||
// gRPC-web headers.
|
||||
if (_getContentTypeHeader(metadata) == null) {
|
||||
metadata['Content-Type'] = 'application/grpc-web+proto';
|
||||
|
@ -231,7 +232,7 @@ class XhrClientConnection extends ClientConnection {
|
|||
Future<void> shutdown() async {}
|
||||
}
|
||||
|
||||
MapEntry<String, String> _getContentTypeHeader(Map<String, String> metadata) {
|
||||
MapEntry<String, String>? _getContentTypeHeader(Map<String, String> metadata) {
|
||||
for (var entry in metadata.entries) {
|
||||
if (entry.key.toLowerCase() == _contentTypeKey.toLowerCase()) {
|
||||
return entry;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Generated code. Do not modify.
|
||||
// source: google/protobuf/any.proto
|
||||
//
|
||||
// @dart = 2.3
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
@ -37,7 +37,19 @@ class Any extends $pb.GeneratedMessage with $mixin.AnyMixin {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
Any._() : super();
|
||||
factory Any() => create();
|
||||
factory Any({
|
||||
$core.String? typeUrl,
|
||||
$core.List<$core.int>? value,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (typeUrl != null) {
|
||||
_result.typeUrl = typeUrl;
|
||||
}
|
||||
if (value != null) {
|
||||
_result.value = value;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory Any.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -51,8 +63,9 @@ class Any extends $pb.GeneratedMessage with $mixin.AnyMixin {
|
|||
@$core.Deprecated('Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
Any copyWith(void Function(Any) updates) => super.copyWith(
|
||||
(message) => updates(message as Any)); // ignore: deprecated_member_use
|
||||
Any copyWith(void Function(Any) updates) =>
|
||||
super.copyWith((message) => updates(message as Any))
|
||||
as Any; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static Any create() => Any._();
|
||||
|
@ -61,7 +74,7 @@ class Any extends $pb.GeneratedMessage with $mixin.AnyMixin {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static Any getDefault() =>
|
||||
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Any>(create);
|
||||
static Any _defaultInstance;
|
||||
static Any? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get typeUrl => $_getSZ(0);
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
// Generated code. Do not modify.
|
||||
// source: google/protobuf/any.proto
|
||||
//
|
||||
// @dart = 2.3
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
// Generated code. Do not modify.
|
||||
// source: google/protobuf/any.proto
|
||||
//
|
||||
// @dart = 2.3
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
||||
const Any$json = const {
|
||||
'1': 'Any',
|
||||
'2': const [
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Generated code. Do not modify.
|
||||
// source: google/protobuf/duration.proto
|
||||
//
|
||||
// @dart = 2.3
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
@ -38,7 +38,19 @@ class Duration extends $pb.GeneratedMessage with $mixin.DurationMixin {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
Duration._() : super();
|
||||
factory Duration() => create();
|
||||
factory Duration({
|
||||
$fixnum.Int64? seconds,
|
||||
$core.int? nanos,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (seconds != null) {
|
||||
_result.seconds = seconds;
|
||||
}
|
||||
if (nanos != null) {
|
||||
_result.nanos = nanos;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory Duration.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -53,8 +65,8 @@ class Duration extends $pb.GeneratedMessage with $mixin.DurationMixin {
|
|||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
Duration copyWith(void Function(Duration) updates) =>
|
||||
super.copyWith((message) =>
|
||||
updates(message as Duration)); // ignore: deprecated_member_use
|
||||
super.copyWith((message) => updates(message as Duration))
|
||||
as Duration; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static Duration create() => Duration._();
|
||||
|
@ -63,7 +75,7 @@ class Duration extends $pb.GeneratedMessage with $mixin.DurationMixin {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static Duration getDefault() =>
|
||||
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Duration>(create);
|
||||
static Duration _defaultInstance;
|
||||
static Duration? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$fixnum.Int64 get seconds => $_getI64(0);
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
// Generated code. Do not modify.
|
||||
// source: google/protobuf/duration.proto
|
||||
//
|
||||
// @dart = 2.3
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
// Generated code. Do not modify.
|
||||
// source: google/protobuf/duration.proto
|
||||
//
|
||||
// @dart = 2.3
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
||||
const Duration$json = const {
|
||||
'1': 'Duration',
|
||||
'2': const [
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Generated code. Do not modify.
|
||||
// source: google/rpc/code.proto
|
||||
//
|
||||
// @dart = 2.3
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Generated code. Do not modify.
|
||||
// source: google/rpc/code.proto
|
||||
//
|
||||
// @dart = 2.3
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
||||
// ignore_for_file: UNDEFINED_SHOWN_NAME
|
||||
|
@ -115,7 +115,7 @@ class Code extends $pb.ProtobufEnum {
|
|||
|
||||
static final $core.Map<$core.int, Code> _byValue =
|
||||
$pb.ProtobufEnum.initByValue(values);
|
||||
static Code valueOf($core.int value) => _byValue[value];
|
||||
static Code? valueOf($core.int value) => _byValue[value];
|
||||
|
||||
const Code._($core.int v, $core.String n) : super(v, n);
|
||||
}
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
// Generated code. Do not modify.
|
||||
// source: google/rpc/code.proto
|
||||
//
|
||||
// @dart = 2.3
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
||||
const Code$json = const {
|
||||
'1': 'Code',
|
||||
'2': const [
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Generated code. Do not modify.
|
||||
// source: google/rpc/error_details.proto
|
||||
//
|
||||
// @dart = 2.3
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
@ -30,7 +30,15 @@ class RetryInfo extends $pb.GeneratedMessage {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
RetryInfo._() : super();
|
||||
factory RetryInfo() => create();
|
||||
factory RetryInfo({
|
||||
$1.Duration? retryDelay,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (retryDelay != null) {
|
||||
_result.retryDelay = retryDelay;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory RetryInfo.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -45,8 +53,8 @@ class RetryInfo extends $pb.GeneratedMessage {
|
|||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
RetryInfo copyWith(void Function(RetryInfo) updates) =>
|
||||
super.copyWith((message) =>
|
||||
updates(message as RetryInfo)); // ignore: deprecated_member_use
|
||||
super.copyWith((message) => updates(message as RetryInfo))
|
||||
as RetryInfo; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static RetryInfo create() => RetryInfo._();
|
||||
|
@ -55,7 +63,7 @@ class RetryInfo extends $pb.GeneratedMessage {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static RetryInfo getDefault() =>
|
||||
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RetryInfo>(create);
|
||||
static RetryInfo _defaultInstance;
|
||||
static RetryInfo? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$1.Duration get retryDelay => $_getN(0);
|
||||
|
@ -95,7 +103,19 @@ class DebugInfo extends $pb.GeneratedMessage {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
DebugInfo._() : super();
|
||||
factory DebugInfo() => create();
|
||||
factory DebugInfo({
|
||||
$core.Iterable<$core.String>? stackEntries,
|
||||
$core.String? detail,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (stackEntries != null) {
|
||||
_result.stackEntries.addAll(stackEntries);
|
||||
}
|
||||
if (detail != null) {
|
||||
_result.detail = detail;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory DebugInfo.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -110,8 +130,8 @@ class DebugInfo extends $pb.GeneratedMessage {
|
|||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
DebugInfo copyWith(void Function(DebugInfo) updates) =>
|
||||
super.copyWith((message) =>
|
||||
updates(message as DebugInfo)); // ignore: deprecated_member_use
|
||||
super.copyWith((message) => updates(message as DebugInfo))
|
||||
as DebugInfo; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static DebugInfo create() => DebugInfo._();
|
||||
|
@ -120,7 +140,7 @@ class DebugInfo extends $pb.GeneratedMessage {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static DebugInfo getDefault() =>
|
||||
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<DebugInfo>(create);
|
||||
static DebugInfo _defaultInstance;
|
||||
static DebugInfo? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.List<$core.String> get stackEntries => $_getList(0);
|
||||
|
@ -161,7 +181,19 @@ class QuotaFailure_Violation extends $pb.GeneratedMessage {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
QuotaFailure_Violation._() : super();
|
||||
factory QuotaFailure_Violation() => create();
|
||||
factory QuotaFailure_Violation({
|
||||
$core.String? subject,
|
||||
$core.String? description,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (subject != null) {
|
||||
_result.subject = subject;
|
||||
}
|
||||
if (description != null) {
|
||||
_result.description = description;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory QuotaFailure_Violation.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -178,8 +210,8 @@ class QuotaFailure_Violation extends $pb.GeneratedMessage {
|
|||
'Will be removed in next major version')
|
||||
QuotaFailure_Violation copyWith(
|
||||
void Function(QuotaFailure_Violation) updates) =>
|
||||
super.copyWith((message) => updates(
|
||||
message as QuotaFailure_Violation)); // ignore: deprecated_member_use
|
||||
super.copyWith((message) => updates(message as QuotaFailure_Violation))
|
||||
as QuotaFailure_Violation; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static QuotaFailure_Violation create() => QuotaFailure_Violation._();
|
||||
|
@ -189,7 +221,7 @@ class QuotaFailure_Violation extends $pb.GeneratedMessage {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static QuotaFailure_Violation getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<QuotaFailure_Violation>(create);
|
||||
static QuotaFailure_Violation _defaultInstance;
|
||||
static QuotaFailure_Violation? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get subject => $_getSZ(0);
|
||||
|
@ -236,7 +268,15 @@ class QuotaFailure extends $pb.GeneratedMessage {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
QuotaFailure._() : super();
|
||||
factory QuotaFailure() => create();
|
||||
factory QuotaFailure({
|
||||
$core.Iterable<QuotaFailure_Violation>? violations,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (violations != null) {
|
||||
_result.violations.addAll(violations);
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory QuotaFailure.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -251,8 +291,8 @@ class QuotaFailure extends $pb.GeneratedMessage {
|
|||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
QuotaFailure copyWith(void Function(QuotaFailure) updates) =>
|
||||
super.copyWith((message) =>
|
||||
updates(message as QuotaFailure)); // ignore: deprecated_member_use
|
||||
super.copyWith((message) => updates(message as QuotaFailure))
|
||||
as QuotaFailure; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static QuotaFailure create() => QuotaFailure._();
|
||||
|
@ -262,7 +302,7 @@ class QuotaFailure extends $pb.GeneratedMessage {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static QuotaFailure getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<QuotaFailure>(create);
|
||||
static QuotaFailure _defaultInstance;
|
||||
static QuotaFailure? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.List<QuotaFailure_Violation> get violations => $_getList(0);
|
||||
|
@ -300,7 +340,23 @@ class ErrorInfo extends $pb.GeneratedMessage {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
ErrorInfo._() : super();
|
||||
factory ErrorInfo() => create();
|
||||
factory ErrorInfo({
|
||||
$core.String? reason,
|
||||
$core.String? domain,
|
||||
$core.Map<$core.String, $core.String>? metadata,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (reason != null) {
|
||||
_result.reason = reason;
|
||||
}
|
||||
if (domain != null) {
|
||||
_result.domain = domain;
|
||||
}
|
||||
if (metadata != null) {
|
||||
_result.metadata.addAll(metadata);
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory ErrorInfo.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -315,8 +371,8 @@ class ErrorInfo extends $pb.GeneratedMessage {
|
|||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
ErrorInfo copyWith(void Function(ErrorInfo) updates) =>
|
||||
super.copyWith((message) =>
|
||||
updates(message as ErrorInfo)); // ignore: deprecated_member_use
|
||||
super.copyWith((message) => updates(message as ErrorInfo))
|
||||
as ErrorInfo; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static ErrorInfo create() => ErrorInfo._();
|
||||
|
@ -325,7 +381,7 @@ class ErrorInfo extends $pb.GeneratedMessage {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static ErrorInfo getDefault() =>
|
||||
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ErrorInfo>(create);
|
||||
static ErrorInfo _defaultInstance;
|
||||
static ErrorInfo? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get reason => $_getSZ(0);
|
||||
|
@ -383,7 +439,23 @@ class PreconditionFailure_Violation extends $pb.GeneratedMessage {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
PreconditionFailure_Violation._() : super();
|
||||
factory PreconditionFailure_Violation() => create();
|
||||
factory PreconditionFailure_Violation({
|
||||
$core.String? type,
|
||||
$core.String? subject,
|
||||
$core.String? description,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (type != null) {
|
||||
_result.type = type;
|
||||
}
|
||||
if (subject != null) {
|
||||
_result.subject = subject;
|
||||
}
|
||||
if (description != null) {
|
||||
_result.description = description;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory PreconditionFailure_Violation.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -400,8 +472,9 @@ class PreconditionFailure_Violation extends $pb.GeneratedMessage {
|
|||
'Will be removed in next major version')
|
||||
PreconditionFailure_Violation copyWith(
|
||||
void Function(PreconditionFailure_Violation) updates) =>
|
||||
super.copyWith((message) => updates(message
|
||||
as PreconditionFailure_Violation)); // ignore: deprecated_member_use
|
||||
super.copyWith(
|
||||
(message) => updates(message as PreconditionFailure_Violation))
|
||||
as PreconditionFailure_Violation; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static PreconditionFailure_Violation create() =>
|
||||
|
@ -412,7 +485,7 @@ class PreconditionFailure_Violation extends $pb.GeneratedMessage {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static PreconditionFailure_Violation getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<PreconditionFailure_Violation>(create);
|
||||
static PreconditionFailure_Violation _defaultInstance;
|
||||
static PreconditionFailure_Violation? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get type => $_getSZ(0);
|
||||
|
@ -471,7 +544,15 @@ class PreconditionFailure extends $pb.GeneratedMessage {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
PreconditionFailure._() : super();
|
||||
factory PreconditionFailure() => create();
|
||||
factory PreconditionFailure({
|
||||
$core.Iterable<PreconditionFailure_Violation>? violations,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (violations != null) {
|
||||
_result.violations.addAll(violations);
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory PreconditionFailure.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -486,8 +567,8 @@ class PreconditionFailure extends $pb.GeneratedMessage {
|
|||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
PreconditionFailure copyWith(void Function(PreconditionFailure) updates) =>
|
||||
super.copyWith((message) => updates(
|
||||
message as PreconditionFailure)); // ignore: deprecated_member_use
|
||||
super.copyWith((message) => updates(message as PreconditionFailure))
|
||||
as PreconditionFailure; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static PreconditionFailure create() => PreconditionFailure._();
|
||||
|
@ -497,7 +578,7 @@ class PreconditionFailure extends $pb.GeneratedMessage {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static PreconditionFailure getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<PreconditionFailure>(create);
|
||||
static PreconditionFailure _defaultInstance;
|
||||
static PreconditionFailure? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.List<PreconditionFailure_Violation> get violations => $_getList(0);
|
||||
|
@ -526,7 +607,19 @@ class BadRequest_FieldViolation extends $pb.GeneratedMessage {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
BadRequest_FieldViolation._() : super();
|
||||
factory BadRequest_FieldViolation() => create();
|
||||
factory BadRequest_FieldViolation({
|
||||
$core.String? field_1,
|
||||
$core.String? description,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (field_1 != null) {
|
||||
_result.field_1 = field_1;
|
||||
}
|
||||
if (description != null) {
|
||||
_result.description = description;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory BadRequest_FieldViolation.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -543,8 +636,8 @@ class BadRequest_FieldViolation extends $pb.GeneratedMessage {
|
|||
'Will be removed in next major version')
|
||||
BadRequest_FieldViolation copyWith(
|
||||
void Function(BadRequest_FieldViolation) updates) =>
|
||||
super.copyWith((message) => updates(message
|
||||
as BadRequest_FieldViolation)); // ignore: deprecated_member_use
|
||||
super.copyWith((message) => updates(message as BadRequest_FieldViolation))
|
||||
as BadRequest_FieldViolation; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static BadRequest_FieldViolation create() => BadRequest_FieldViolation._();
|
||||
|
@ -554,7 +647,7 @@ class BadRequest_FieldViolation extends $pb.GeneratedMessage {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static BadRequest_FieldViolation getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<BadRequest_FieldViolation>(create);
|
||||
static BadRequest_FieldViolation _defaultInstance;
|
||||
static BadRequest_FieldViolation? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get field_1 => $_getSZ(0);
|
||||
|
@ -601,7 +694,15 @@ class BadRequest extends $pb.GeneratedMessage {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
BadRequest._() : super();
|
||||
factory BadRequest() => create();
|
||||
factory BadRequest({
|
||||
$core.Iterable<BadRequest_FieldViolation>? fieldViolations,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (fieldViolations != null) {
|
||||
_result.fieldViolations.addAll(fieldViolations);
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory BadRequest.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -616,8 +717,8 @@ class BadRequest extends $pb.GeneratedMessage {
|
|||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
BadRequest copyWith(void Function(BadRequest) updates) =>
|
||||
super.copyWith((message) =>
|
||||
updates(message as BadRequest)); // ignore: deprecated_member_use
|
||||
super.copyWith((message) => updates(message as BadRequest))
|
||||
as BadRequest; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static BadRequest create() => BadRequest._();
|
||||
|
@ -626,7 +727,7 @@ class BadRequest extends $pb.GeneratedMessage {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static BadRequest getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<BadRequest>(create);
|
||||
static BadRequest _defaultInstance;
|
||||
static BadRequest? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.List<BadRequest_FieldViolation> get fieldViolations => $_getList(0);
|
||||
|
@ -655,7 +756,19 @@ class RequestInfo extends $pb.GeneratedMessage {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
RequestInfo._() : super();
|
||||
factory RequestInfo() => create();
|
||||
factory RequestInfo({
|
||||
$core.String? requestId,
|
||||
$core.String? servingData,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (requestId != null) {
|
||||
_result.requestId = requestId;
|
||||
}
|
||||
if (servingData != null) {
|
||||
_result.servingData = servingData;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory RequestInfo.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -670,8 +783,8 @@ class RequestInfo extends $pb.GeneratedMessage {
|
|||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
RequestInfo copyWith(void Function(RequestInfo) updates) =>
|
||||
super.copyWith((message) =>
|
||||
updates(message as RequestInfo)); // ignore: deprecated_member_use
|
||||
super.copyWith((message) => updates(message as RequestInfo))
|
||||
as RequestInfo; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static RequestInfo create() => RequestInfo._();
|
||||
|
@ -680,7 +793,7 @@ class RequestInfo extends $pb.GeneratedMessage {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static RequestInfo getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<RequestInfo>(create);
|
||||
static RequestInfo _defaultInstance;
|
||||
static RequestInfo? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get requestId => $_getSZ(0);
|
||||
|
@ -737,7 +850,27 @@ class ResourceInfo extends $pb.GeneratedMessage {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
ResourceInfo._() : super();
|
||||
factory ResourceInfo() => create();
|
||||
factory ResourceInfo({
|
||||
$core.String? resourceType,
|
||||
$core.String? resourceName,
|
||||
$core.String? owner,
|
||||
$core.String? description,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (resourceType != null) {
|
||||
_result.resourceType = resourceType;
|
||||
}
|
||||
if (resourceName != null) {
|
||||
_result.resourceName = resourceName;
|
||||
}
|
||||
if (owner != null) {
|
||||
_result.owner = owner;
|
||||
}
|
||||
if (description != null) {
|
||||
_result.description = description;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory ResourceInfo.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -752,8 +885,8 @@ class ResourceInfo extends $pb.GeneratedMessage {
|
|||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
ResourceInfo copyWith(void Function(ResourceInfo) updates) =>
|
||||
super.copyWith((message) =>
|
||||
updates(message as ResourceInfo)); // ignore: deprecated_member_use
|
||||
super.copyWith((message) => updates(message as ResourceInfo))
|
||||
as ResourceInfo; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static ResourceInfo create() => ResourceInfo._();
|
||||
|
@ -763,7 +896,7 @@ class ResourceInfo extends $pb.GeneratedMessage {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static ResourceInfo getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<ResourceInfo>(create);
|
||||
static ResourceInfo _defaultInstance;
|
||||
static ResourceInfo? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get resourceType => $_getSZ(0);
|
||||
|
@ -837,7 +970,19 @@ class Help_Link extends $pb.GeneratedMessage {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
Help_Link._() : super();
|
||||
factory Help_Link() => create();
|
||||
factory Help_Link({
|
||||
$core.String? description,
|
||||
$core.String? url,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (description != null) {
|
||||
_result.description = description;
|
||||
}
|
||||
if (url != null) {
|
||||
_result.url = url;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory Help_Link.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -852,8 +997,8 @@ class Help_Link extends $pb.GeneratedMessage {
|
|||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
Help_Link copyWith(void Function(Help_Link) updates) =>
|
||||
super.copyWith((message) =>
|
||||
updates(message as Help_Link)); // ignore: deprecated_member_use
|
||||
super.copyWith((message) => updates(message as Help_Link))
|
||||
as Help_Link; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static Help_Link create() => Help_Link._();
|
||||
|
@ -862,7 +1007,7 @@ class Help_Link extends $pb.GeneratedMessage {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static Help_Link getDefault() =>
|
||||
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Help_Link>(create);
|
||||
static Help_Link _defaultInstance;
|
||||
static Help_Link? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get description => $_getSZ(0);
|
||||
|
@ -909,7 +1054,15 @@ class Help extends $pb.GeneratedMessage {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
Help._() : super();
|
||||
factory Help() => create();
|
||||
factory Help({
|
||||
$core.Iterable<Help_Link>? links,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (links != null) {
|
||||
_result.links.addAll(links);
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory Help.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -923,8 +1076,9 @@ class Help extends $pb.GeneratedMessage {
|
|||
@$core.Deprecated('Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
Help copyWith(void Function(Help) updates) => super.copyWith(
|
||||
(message) => updates(message as Help)); // ignore: deprecated_member_use
|
||||
Help copyWith(void Function(Help) updates) =>
|
||||
super.copyWith((message) => updates(message as Help))
|
||||
as Help; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static Help create() => Help._();
|
||||
|
@ -933,7 +1087,7 @@ class Help extends $pb.GeneratedMessage {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static Help getDefault() =>
|
||||
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Help>(create);
|
||||
static Help _defaultInstance;
|
||||
static Help? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.List<Help_Link> get links => $_getList(0);
|
||||
|
@ -962,7 +1116,19 @@ class LocalizedMessage extends $pb.GeneratedMessage {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
LocalizedMessage._() : super();
|
||||
factory LocalizedMessage() => create();
|
||||
factory LocalizedMessage({
|
||||
$core.String? locale,
|
||||
$core.String? message,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (locale != null) {
|
||||
_result.locale = locale;
|
||||
}
|
||||
if (message != null) {
|
||||
_result.message = message;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory LocalizedMessage.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -977,8 +1143,8 @@ class LocalizedMessage extends $pb.GeneratedMessage {
|
|||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
LocalizedMessage copyWith(void Function(LocalizedMessage) updates) =>
|
||||
super.copyWith((message) => updates(
|
||||
message as LocalizedMessage)); // ignore: deprecated_member_use
|
||||
super.copyWith((message) => updates(message as LocalizedMessage))
|
||||
as LocalizedMessage; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static LocalizedMessage create() => LocalizedMessage._();
|
||||
|
@ -988,7 +1154,7 @@ class LocalizedMessage extends $pb.GeneratedMessage {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static LocalizedMessage getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<LocalizedMessage>(create);
|
||||
static LocalizedMessage _defaultInstance;
|
||||
static LocalizedMessage? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get locale => $_getSZ(0);
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
// Generated code. Do not modify.
|
||||
// source: google/rpc/error_details.proto
|
||||
//
|
||||
// @dart = 2.3
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
// Generated code. Do not modify.
|
||||
// source: google/rpc/error_details.proto
|
||||
//
|
||||
// @dart = 2.3
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
||||
const RetryInfo$json = const {
|
||||
'1': 'RetryInfo',
|
||||
'2': const [
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Generated code. Do not modify.
|
||||
// source: google/rpc/status.proto
|
||||
//
|
||||
// @dart = 2.3
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
@ -40,7 +40,23 @@ class Status extends $pb.GeneratedMessage {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
Status._() : super();
|
||||
factory Status() => create();
|
||||
factory Status({
|
||||
$core.int? code,
|
||||
$core.String? message,
|
||||
$core.Iterable<$0.Any>? details,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (code != null) {
|
||||
_result.code = code;
|
||||
}
|
||||
if (message != null) {
|
||||
_result.message = message;
|
||||
}
|
||||
if (details != null) {
|
||||
_result.details.addAll(details);
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory Status.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -54,8 +70,9 @@ class Status extends $pb.GeneratedMessage {
|
|||
@$core.Deprecated('Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
Status copyWith(void Function(Status) updates) => super.copyWith(
|
||||
(message) => updates(message as Status)); // ignore: deprecated_member_use
|
||||
Status copyWith(void Function(Status) updates) =>
|
||||
super.copyWith((message) => updates(message as Status))
|
||||
as Status; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static Status create() => Status._();
|
||||
|
@ -64,7 +81,7 @@ class Status extends $pb.GeneratedMessage {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static Status getDefault() =>
|
||||
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Status>(create);
|
||||
static Status _defaultInstance;
|
||||
static Status? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.int get code => $_getIZ(0);
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
// Generated code. Do not modify.
|
||||
// source: google/rpc/status.proto
|
||||
//
|
||||
// @dart = 2.3
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
// Generated code. Do not modify.
|
||||
// source: google/rpc/status.proto
|
||||
//
|
||||
// @dart = 2.3
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
||||
const Status$json = const {
|
||||
'1': 'Status',
|
||||
'2': const [
|
||||
|
|
|
@ -19,19 +19,19 @@
|
|||
/// ability to set custom metadata on the header/trailer sent to the client.
|
||||
abstract class ServiceCall {
|
||||
/// Custom metadata from the client.
|
||||
Map<String, String> get clientMetadata;
|
||||
Map<String, String>? get clientMetadata;
|
||||
|
||||
/// Custom metadata to be sent to the client. Will be [null] once the headers
|
||||
/// have been sent, either when [sendHeaders] is called, or when the first
|
||||
/// response message is sent.
|
||||
Map<String, String> get headers;
|
||||
Map<String, String>? get headers;
|
||||
|
||||
/// Custom metadata to be sent to the client after all response messages.
|
||||
Map<String, String> get trailers;
|
||||
Map<String, String>? get trailers;
|
||||
|
||||
/// Deadline for this call. If the call is still active after this time, then
|
||||
/// the client or server may cancel it.
|
||||
DateTime get deadline;
|
||||
DateTime? get deadline;
|
||||
|
||||
/// Returns [true] if the [deadline] has been exceeded.
|
||||
bool get isTimedOut;
|
||||
|
@ -50,5 +50,5 @@ abstract class ServiceCall {
|
|||
///
|
||||
/// The call will be closed after calling this method, and no further
|
||||
/// responses can be sent.
|
||||
void sendTrailers({int status, String message});
|
||||
void sendTrailers({int? status, String? message});
|
||||
}
|
||||
|
|
|
@ -31,32 +31,33 @@ import 'service.dart';
|
|||
/// Handles an incoming gRPC call.
|
||||
class ServerHandler_ extends ServiceCall {
|
||||
final ServerTransportStream _stream;
|
||||
final Service Function(String service) _serviceLookup;
|
||||
final Service? Function(String service) _serviceLookup;
|
||||
final List<Interceptor> _interceptors;
|
||||
final CodecRegistry _codecRegistry;
|
||||
final CodecRegistry? _codecRegistry;
|
||||
|
||||
StreamSubscription<GrpcMessage> _incomingSubscription;
|
||||
// ignore: cancel_subscriptions
|
||||
StreamSubscription<GrpcMessage>? _incomingSubscription;
|
||||
|
||||
Service _service;
|
||||
ServiceMethod _descriptor;
|
||||
late Service _service;
|
||||
late ServiceMethod _descriptor;
|
||||
|
||||
Map<String, String> _clientMetadata;
|
||||
Codec _callEncodingCodec;
|
||||
Map<String, String>? _clientMetadata;
|
||||
Codec? _callEncodingCodec;
|
||||
|
||||
StreamController _requests;
|
||||
StreamController? _requests;
|
||||
bool _hasReceivedRequest = false;
|
||||
|
||||
Stream _responses;
|
||||
StreamSubscription _responseSubscription;
|
||||
late Stream _responses;
|
||||
StreamSubscription? _responseSubscription;
|
||||
bool _headersSent = false;
|
||||
|
||||
Map<String, String> _customHeaders = {};
|
||||
Map<String, String> _customTrailers = {};
|
||||
Map<String, String>? _customHeaders = {};
|
||||
Map<String, String>? _customTrailers = {};
|
||||
|
||||
DateTime _deadline;
|
||||
DateTime? _deadline;
|
||||
bool _isCanceled = false;
|
||||
bool _isTimedOut = false;
|
||||
Timer _timeoutTimer;
|
||||
Timer? _timeoutTimer;
|
||||
|
||||
ServerHandler_(
|
||||
this._serviceLookup,
|
||||
|
@ -65,17 +66,17 @@ class ServerHandler_ extends ServiceCall {
|
|||
this._codecRegistry,
|
||||
);
|
||||
|
||||
DateTime get deadline => _deadline;
|
||||
DateTime? get deadline => _deadline;
|
||||
|
||||
bool get isCanceled => _isCanceled;
|
||||
|
||||
bool get isTimedOut => _isTimedOut;
|
||||
|
||||
Map<String, String> get clientMetadata => _clientMetadata;
|
||||
Map<String, String>? get clientMetadata => _clientMetadata;
|
||||
|
||||
Map<String, String> get headers => _customHeaders;
|
||||
Map<String, String>? get headers => _customHeaders;
|
||||
|
||||
Map<String, String> get trailers => _customTrailers;
|
||||
Map<String, String>? get trailers => _customTrailers;
|
||||
|
||||
void handle() {
|
||||
_stream.onTerminated = (_) => cancel();
|
||||
|
@ -95,23 +96,21 @@ class ServerHandler_ extends ServiceCall {
|
|||
/// We need the catchError() handler here, since otherwise the error would
|
||||
/// be an unhandled exception.
|
||||
void _cancelResponseSubscription() {
|
||||
_responseSubscription?.cancel()?.catchError((_) {});
|
||||
_responseSubscription?.cancel().catchError((_) {});
|
||||
}
|
||||
|
||||
// -- Idle state, incoming data --
|
||||
|
||||
void _onDataIdle(GrpcMessage message) async {
|
||||
if (message is! GrpcMetadata) {
|
||||
void _onDataIdle(GrpcMessage headerMessage) async {
|
||||
if (headerMessage is! GrpcMetadata) {
|
||||
_sendError(GrpcError.unimplemented('Expected header frame'));
|
||||
_sinkIncoming();
|
||||
return;
|
||||
}
|
||||
_incomingSubscription.pause();
|
||||
_incomingSubscription!.pause();
|
||||
|
||||
final headerMessage = message
|
||||
as GrpcMetadata; // TODO(jakobr): Cast should not be necessary here.
|
||||
_clientMetadata = headerMessage.metadata;
|
||||
final path = _clientMetadata[':path'];
|
||||
final path = _clientMetadata![':path']!;
|
||||
final pathSegments = path.split('/');
|
||||
if (pathSegments.length < 3) {
|
||||
_sendError(GrpcError.unimplemented('Invalid path'));
|
||||
|
@ -122,19 +121,21 @@ class ServerHandler_ extends ServiceCall {
|
|||
final methodName = pathSegments[2];
|
||||
if (_codecRegistry != null) {
|
||||
final acceptedEncodings =
|
||||
clientMetadata['grpc-accept-encoding']?.split(',') ?? [];
|
||||
clientMetadata!['grpc-accept-encoding']?.split(',') ?? [];
|
||||
_callEncodingCodec = acceptedEncodings
|
||||
.map(_codecRegistry.lookup)
|
||||
.map(_codecRegistry!.lookup)
|
||||
.firstWhere((c) => c != null, orElse: () => null);
|
||||
}
|
||||
|
||||
_service = _serviceLookup(serviceName);
|
||||
_descriptor = _service?.$lookupMethod(methodName);
|
||||
if (_descriptor == null) {
|
||||
final service = _serviceLookup(serviceName);
|
||||
final descriptor = service?.$lookupMethod(methodName);
|
||||
if (descriptor == null) {
|
||||
_sendError(GrpcError.unimplemented('Path $path not found'));
|
||||
_sinkIncoming();
|
||||
return;
|
||||
}
|
||||
_service = service!;
|
||||
_descriptor = descriptor;
|
||||
|
||||
final error = await _applyInterceptors();
|
||||
if (error != null) {
|
||||
|
@ -146,7 +147,7 @@ class ServerHandler_ extends ServiceCall {
|
|||
_startStreamingRequest();
|
||||
}
|
||||
|
||||
GrpcError _onMetadata() {
|
||||
GrpcError? _onMetadata() {
|
||||
try {
|
||||
_service.$onMetadata(this);
|
||||
} on GrpcError catch (error) {
|
||||
|
@ -158,7 +159,7 @@ class ServerHandler_ extends ServiceCall {
|
|||
return null;
|
||||
}
|
||||
|
||||
Future<GrpcError> _applyInterceptors() async {
|
||||
Future<GrpcError?> _applyInterceptors() async {
|
||||
try {
|
||||
for (final interceptor in _interceptors) {
|
||||
final error = await interceptor(this, this._descriptor);
|
||||
|
@ -174,13 +175,14 @@ class ServerHandler_ extends ServiceCall {
|
|||
}
|
||||
|
||||
void _startStreamingRequest() {
|
||||
_requests = _descriptor.createRequestStream(_incomingSubscription);
|
||||
_incomingSubscription.onData(_onDataActive);
|
||||
final requests = _descriptor.createRequestStream(_incomingSubscription!);
|
||||
_requests = requests;
|
||||
_incomingSubscription!.onData(_onDataActive);
|
||||
|
||||
final error = _onMetadata();
|
||||
if (error != null) {
|
||||
if (!_requests.isClosed) {
|
||||
_requests
|
||||
if (!requests.isClosed) {
|
||||
requests
|
||||
..addError(error)
|
||||
..close();
|
||||
}
|
||||
|
@ -190,16 +192,16 @@ class ServerHandler_ extends ServiceCall {
|
|||
return;
|
||||
}
|
||||
|
||||
_responses = _descriptor.handle(this, _requests.stream);
|
||||
_responses = _descriptor.handle(this, requests.stream);
|
||||
|
||||
_responseSubscription = _responses.listen(_onResponse,
|
||||
onError: _onResponseError,
|
||||
onDone: _onResponseDone,
|
||||
cancelOnError: true);
|
||||
_incomingSubscription.onData(_onDataActive);
|
||||
_incomingSubscription.onDone(_onDoneExpected);
|
||||
_incomingSubscription!.onData(_onDataActive);
|
||||
_incomingSubscription!.onDone(_onDoneExpected);
|
||||
|
||||
final timeout = fromTimeoutString(_clientMetadata['grpc-timeout']);
|
||||
final timeout = fromTimeoutString(_clientMetadata!['grpc-timeout']);
|
||||
if (timeout != null) {
|
||||
_deadline = DateTime.now().add(timeout);
|
||||
_timeoutTimer = Timer(timeout, _onTimedOut);
|
||||
|
@ -212,8 +214,8 @@ class ServerHandler_ extends ServiceCall {
|
|||
_isCanceled = true;
|
||||
final error = GrpcError.deadlineExceeded('Deadline exceeded');
|
||||
_sendError(error);
|
||||
if (!_requests.isClosed) {
|
||||
_requests
|
||||
if (!_requests!.isClosed) {
|
||||
_requests!
|
||||
..addError(error)
|
||||
..close();
|
||||
}
|
||||
|
@ -225,7 +227,7 @@ class ServerHandler_ extends ServiceCall {
|
|||
if (message is! GrpcData) {
|
||||
final error = GrpcError.unimplemented('Expected request');
|
||||
_sendError(error);
|
||||
_requests
|
||||
_requests!
|
||||
..addError(error)
|
||||
..close();
|
||||
return;
|
||||
|
@ -234,14 +236,13 @@ class ServerHandler_ extends ServiceCall {
|
|||
if (_hasReceivedRequest && !_descriptor.streamingRequest) {
|
||||
final error = GrpcError.unimplemented('Too many requests');
|
||||
_sendError(error);
|
||||
_requests
|
||||
_requests!
|
||||
..addError(error)
|
||||
..close();
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(jakobr): Cast should not be necessary here.
|
||||
final data = message as GrpcData;
|
||||
final data = message;
|
||||
var request;
|
||||
try {
|
||||
request = _descriptor.deserialize(data.data);
|
||||
|
@ -249,12 +250,12 @@ class ServerHandler_ extends ServiceCall {
|
|||
final grpcError =
|
||||
GrpcError.internal('Error deserializing request: $error');
|
||||
_sendError(grpcError);
|
||||
_requests
|
||||
_requests!
|
||||
..addError(grpcError)
|
||||
..close();
|
||||
return;
|
||||
}
|
||||
_requests.add(request);
|
||||
_requests!.add(request);
|
||||
_hasReceivedRequest = true;
|
||||
}
|
||||
|
||||
|
@ -269,9 +270,9 @@ class ServerHandler_ extends ServiceCall {
|
|||
_stream.sendData(frame(bytes, _callEncodingCodec));
|
||||
} catch (error) {
|
||||
final grpcError = GrpcError.internal('Error sending response: $error');
|
||||
if (!_requests.isClosed) {
|
||||
if (!_requests!.isClosed) {
|
||||
// If we can, alert the handler that things are going wrong.
|
||||
_requests
|
||||
_requests!
|
||||
..addError(grpcError)
|
||||
..close();
|
||||
}
|
||||
|
@ -295,17 +296,17 @@ class ServerHandler_ extends ServiceCall {
|
|||
void sendHeaders() {
|
||||
if (_headersSent) throw GrpcError.internal('Headers already sent');
|
||||
|
||||
_customHeaders..remove(':status')..remove('content-type');
|
||||
_customHeaders!..remove(':status')..remove('content-type');
|
||||
|
||||
// TODO(jakobr): Should come from package:http2?
|
||||
final outgoingHeadersMap = <String, String>{
|
||||
':status': '200',
|
||||
'content-type': 'application/grpc',
|
||||
if (_callEncodingCodec != null)
|
||||
'grpc-encoding': _callEncodingCodec.encodingName,
|
||||
'grpc-encoding': _callEncodingCodec!.encodingName,
|
||||
};
|
||||
|
||||
outgoingHeadersMap.addAll(_customHeaders);
|
||||
outgoingHeadersMap.addAll(_customHeaders!);
|
||||
_customHeaders = null;
|
||||
|
||||
final outgoingHeaders = <Header>[];
|
||||
|
@ -315,7 +316,7 @@ class ServerHandler_ extends ServiceCall {
|
|||
_headersSent = true;
|
||||
}
|
||||
|
||||
void sendTrailers({int status = 0, String message}) {
|
||||
void sendTrailers({int? status = 0, String? message}) {
|
||||
_timeoutTimer?.cancel();
|
||||
|
||||
final outgoingTrailersMap = <String, String>{};
|
||||
|
@ -324,13 +325,13 @@ class ServerHandler_ extends ServiceCall {
|
|||
outgoingTrailersMap[':status'] = '200';
|
||||
outgoingTrailersMap['content-type'] = 'application/grpc';
|
||||
|
||||
_customHeaders..remove(':status')..remove('content-type');
|
||||
outgoingTrailersMap.addAll(_customHeaders);
|
||||
_customHeaders!..remove(':status')..remove('content-type');
|
||||
outgoingTrailersMap.addAll(_customHeaders!);
|
||||
_customHeaders = null;
|
||||
_headersSent = true;
|
||||
}
|
||||
_customTrailers..remove(':status')..remove('content-type');
|
||||
outgoingTrailersMap.addAll(_customTrailers);
|
||||
_customTrailers!..remove(':status')..remove('content-type');
|
||||
outgoingTrailersMap.addAll(_customTrailers!);
|
||||
_customTrailers = null;
|
||||
outgoingTrailersMap['grpc-status'] = status.toString();
|
||||
if (message != null) {
|
||||
|
@ -354,11 +355,11 @@ class ServerHandler_ extends ServiceCall {
|
|||
// client, so we treat it as such.
|
||||
_timeoutTimer?.cancel();
|
||||
_isCanceled = true;
|
||||
if (_requests != null && !_requests.isClosed) {
|
||||
_requests.addError(GrpcError.cancelled('Cancelled'));
|
||||
if (_requests != null && !_requests!.isClosed) {
|
||||
_requests!.addError(GrpcError.cancelled('Cancelled'));
|
||||
}
|
||||
_cancelResponseSubscription();
|
||||
_incomingSubscription.cancel();
|
||||
_incomingSubscription!.cancel();
|
||||
_stream.terminate();
|
||||
}
|
||||
|
||||
|
@ -371,20 +372,20 @@ class ServerHandler_ extends ServiceCall {
|
|||
if (!(_hasReceivedRequest || _descriptor.streamingRequest)) {
|
||||
final error = GrpcError.unimplemented('No request received');
|
||||
_sendError(error);
|
||||
_requests.addError(error);
|
||||
_requests!.addError(error);
|
||||
}
|
||||
_onDone();
|
||||
}
|
||||
|
||||
void _onDone() {
|
||||
_requests?.close();
|
||||
_incomingSubscription.cancel();
|
||||
_incomingSubscription!.cancel();
|
||||
}
|
||||
|
||||
/// Sink incoming requests. This is used when an error has already been
|
||||
/// reported, but we still need to consume the request stream from the client.
|
||||
void _sinkIncoming() {
|
||||
_incomingSubscription
|
||||
_incomingSubscription!
|
||||
..onData((_) {})
|
||||
..onDone(_onDone);
|
||||
}
|
||||
|
@ -405,6 +406,6 @@ class ServerHandler extends ServerHandler_ {
|
|||
Service Function(String service) serviceLookup,
|
||||
stream, [
|
||||
List<Interceptor> interceptors = const <Interceptor>[],
|
||||
CodecRegistry codecRegistry,
|
||||
CodecRegistry? codecRegistry,
|
||||
]) : super(serviceLookup, stream, interceptors, codecRegistry);
|
||||
}
|
||||
|
|
|
@ -10,5 +10,5 @@ import 'service.dart';
|
|||
/// If the interceptor returns a [GrpcError], the error will be returned as a response and [ServiceMethod] wouldn't be called.
|
||||
/// If the interceptor throws [Exception], [GrpcError.internal] with exception.toString() will be returned.
|
||||
/// If the interceptor returns null, the corresponding [ServiceMethod] of [Service] will be called.
|
||||
typedef Interceptor = FutureOr<GrpcError> Function(
|
||||
typedef Interceptor = FutureOr<GrpcError?> Function(
|
||||
ServiceCall call, ServiceMethod method);
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:grpc/grpc.dart';
|
||||
import 'package:http2/transport.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../shared/codec_registry.dart';
|
||||
import '../shared/security.dart';
|
||||
import 'handler.dart';
|
||||
import 'interceptor.dart';
|
||||
|
@ -33,7 +33,7 @@ abstract class ServerCredentials {
|
|||
|
||||
/// Creates [SecurityContext] from these credentials if possible.
|
||||
/// Otherwise returns [null].
|
||||
SecurityContext get securityContext;
|
||||
SecurityContext? get securityContext;
|
||||
}
|
||||
|
||||
/// Set of credentials that only allows local TCP connections.
|
||||
|
@ -42,14 +42,14 @@ class ServerLocalCredentials extends ServerCredentials {
|
|||
bool validateClient(Socket socket) => socket.remoteAddress.isLoopback;
|
||||
|
||||
@override
|
||||
SecurityContext get securityContext => null;
|
||||
SecurityContext? get securityContext => null;
|
||||
}
|
||||
|
||||
class ServerTlsCredentials extends ServerCredentials {
|
||||
final List<int> certificate;
|
||||
final String certificatePassword;
|
||||
final List<int> privateKey;
|
||||
final String privateKeyPassword;
|
||||
final List<int>? certificate;
|
||||
final String? certificatePassword;
|
||||
final List<int>? privateKey;
|
||||
final String? privateKeyPassword;
|
||||
|
||||
/// TLS credentials for a [Server].
|
||||
///
|
||||
|
@ -64,10 +64,10 @@ class ServerTlsCredentials extends ServerCredentials {
|
|||
SecurityContext get securityContext {
|
||||
final context = createSecurityContext(true);
|
||||
if (privateKey != null) {
|
||||
context.usePrivateKeyBytes(privateKey, password: privateKeyPassword);
|
||||
context.usePrivateKeyBytes(privateKey!, password: privateKeyPassword);
|
||||
}
|
||||
if (certificate != null) {
|
||||
context.useCertificateChainBytes(certificate,
|
||||
context.useCertificateChainBytes(certificate!,
|
||||
password: certificatePassword);
|
||||
}
|
||||
return context;
|
||||
|
@ -84,7 +84,7 @@ class ServerTlsCredentials extends ServerCredentials {
|
|||
class ConnectionServer {
|
||||
final Map<String, Service> _services = {};
|
||||
final List<Interceptor> _interceptors;
|
||||
final CodecRegistry _codecRegistry;
|
||||
final CodecRegistry? _codecRegistry;
|
||||
|
||||
final _connections = <ServerTransportConnection>[];
|
||||
|
||||
|
@ -92,7 +92,7 @@ class ConnectionServer {
|
|||
ConnectionServer(
|
||||
List<Service> services, [
|
||||
List<Interceptor> interceptors = const <Interceptor>[],
|
||||
CodecRegistry codecRegistry,
|
||||
CodecRegistry? codecRegistry,
|
||||
]) : _codecRegistry = codecRegistry,
|
||||
_interceptors = interceptors {
|
||||
for (final service in services) {
|
||||
|
@ -100,11 +100,11 @@ class ConnectionServer {
|
|||
}
|
||||
}
|
||||
|
||||
Service lookupService(String service) => _services[service];
|
||||
Service? lookupService(String service) => _services[service];
|
||||
|
||||
Future<void> serveConnection(ServerTransportConnection connection) async {
|
||||
_connections.add(connection);
|
||||
ServerHandler_ handler;
|
||||
ServerHandler_? handler;
|
||||
// TODO(jakobr): Set active state handlers, close connection after idle
|
||||
// timeout.
|
||||
connection.incomingStreams.listen((stream) {
|
||||
|
@ -134,39 +134,39 @@ class ConnectionServer {
|
|||
///
|
||||
/// Listens for incoming RPCs, dispatching them to the right [Service] handler.
|
||||
class Server extends ConnectionServer {
|
||||
ServerSocket _insecureServer;
|
||||
SecureServerSocket _secureServer;
|
||||
ServerSocket? _insecureServer;
|
||||
SecureServerSocket? _secureServer;
|
||||
|
||||
/// Create a server for the given [services].
|
||||
Server(
|
||||
List<Service> services, [
|
||||
List<Interceptor> interceptors = const <Interceptor>[],
|
||||
CodecRegistry codecRegistry,
|
||||
CodecRegistry? codecRegistry,
|
||||
]) : super(services, interceptors, codecRegistry);
|
||||
|
||||
/// The port that the server is listening on, or `null` if the server is not
|
||||
/// active.
|
||||
int get port {
|
||||
if (_secureServer != null) return _secureServer.port;
|
||||
if (_insecureServer != null) return _insecureServer.port;
|
||||
int? get port {
|
||||
if (_secureServer != null) return _secureServer!.port;
|
||||
if (_insecureServer != null) return _insecureServer!.port;
|
||||
return null;
|
||||
}
|
||||
|
||||
Service lookupService(String service) => _services[service];
|
||||
Service? lookupService(String service) => _services[service];
|
||||
|
||||
/// Starts the [Server] with the given options.
|
||||
/// [address] can be either a [String] or an [InternetAddress], in the latter
|
||||
/// case it can be a Unix Domain Socket address.
|
||||
Future<void> serve(
|
||||
{dynamic address,
|
||||
int port,
|
||||
ServerCredentials security,
|
||||
ServerSettings http2ServerSettings,
|
||||
int? port,
|
||||
ServerCredentials? security,
|
||||
ServerSettings? http2ServerSettings,
|
||||
int backlog: 0,
|
||||
bool v6Only: false,
|
||||
bool shared: false}) async {
|
||||
// TODO(dart-lang/grpc-dart#9): Handle HTTP/1.1 upgrade to h2c, if allowed.
|
||||
Stream<Socket> server;
|
||||
Stream<Socket>? server;
|
||||
final securityContext = security?.securityContext;
|
||||
if (securityContext != null) {
|
||||
_secureServer = await SecureServerSocket.bind(
|
||||
|
@ -183,7 +183,7 @@ class Server extends ConnectionServer {
|
|||
);
|
||||
server = _insecureServer;
|
||||
}
|
||||
server.listen((socket) {
|
||||
server!.listen((socket) {
|
||||
// Don't wait for io buffers to fill up before sending requests.
|
||||
if (socket.address.type != InternetAddressType.unix) {
|
||||
socket.setOption(SocketOption.tcpNoDelay, true);
|
||||
|
@ -213,10 +213,10 @@ class Server extends ConnectionServer {
|
|||
Future<void> shutdown() async {
|
||||
final done = _connections.map((connection) => connection.finish()).toList();
|
||||
if (_insecureServer != null) {
|
||||
done.add(_insecureServer.close());
|
||||
done.add(_insecureServer!.close());
|
||||
}
|
||||
if (_secureServer != null) {
|
||||
done.add(_secureServer.close());
|
||||
done.add(_secureServer!.close());
|
||||
}
|
||||
await Future.wait(done);
|
||||
_insecureServer = null;
|
||||
|
|
|
@ -56,36 +56,39 @@ class ServiceMethod<Q, R> {
|
|||
return handler(call, _toSingleFuture(requests));
|
||||
}
|
||||
} else {
|
||||
Future<R> response;
|
||||
if (streamingRequest) {
|
||||
response = handler(call, requests);
|
||||
} else {
|
||||
response = handler(call, _toSingleFuture(requests));
|
||||
}
|
||||
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) {
|
||||
Q _ensureOnlyOneRequest(Q? previous, Q element) {
|
||||
if (previous != null) {
|
||||
throw GrpcError.unimplemented('More than one request received');
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
Q _ensureOneRequest(Q value) {
|
||||
Q _ensureOneRequest(Q? value) {
|
||||
if (value == null) throw GrpcError.unimplemented('No requests received');
|
||||
return value;
|
||||
}
|
||||
|
||||
final future =
|
||||
stream.fold(null, _ensureOnlyOneRequest).then(_ensureOneRequest);
|
||||
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.
|
||||
future.catchError((_) {});
|
||||
_awaitAndCatch(future);
|
||||
return future;
|
||||
}
|
||||
|
||||
void _awaitAndCatch<Q>(Future<Q> f) async {
|
||||
try {
|
||||
await f;
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Definition of a gRPC service.
|
||||
|
@ -104,5 +107,5 @@ abstract class Service {
|
|||
/// metadata from the client.
|
||||
void $onMetadata(ServiceCall context) {}
|
||||
|
||||
ServiceMethod $lookupMethod(String name) => _$methods[name];
|
||||
ServiceMethod? $lookupMethod(String name) => _$methods[name];
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ class IdentityCodec implements Codec {
|
|||
const IdentityCodec();
|
||||
|
||||
@override
|
||||
String get encodingName => "identity";
|
||||
final encodingName = 'identity';
|
||||
|
||||
@override
|
||||
List<int> compress(List<int> data) {
|
||||
|
@ -54,11 +54,11 @@ class GzipCodec implements Codec {
|
|||
const GzipCodec();
|
||||
|
||||
@override
|
||||
String get encodingName => "gzip";
|
||||
final encodingName = "gzip";
|
||||
|
||||
@override
|
||||
List<int> compress(List<int> data) {
|
||||
return GZipEncoder().encode(data);
|
||||
return GZipEncoder().encode(data)!;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -18,8 +18,7 @@ import 'codec.dart';
|
|||
/// Encloses classes related to the compression and decompression of messages.
|
||||
class CodecRegistry {
|
||||
CodecRegistry({List<Codec> codecs = const [IdentityCodec()]})
|
||||
: assert(codecs != null),
|
||||
_codecs = Map.fromIterable(codecs, key: (c) => c.encodingName),
|
||||
: _codecs = {for (var codec in codecs) codec.encodingName: codec},
|
||||
_supportedEncodings = codecs.map((c) {
|
||||
if (c.encodingName.contains(',')) {
|
||||
throw ArgumentError.value(c.encodingName, 'codecs',
|
||||
|
@ -33,16 +32,14 @@ class CodecRegistry {
|
|||
}
|
||||
}
|
||||
|
||||
factory CodecRegistry.empty() {
|
||||
return CodecRegistry(codecs: []);
|
||||
}
|
||||
factory CodecRegistry.empty() => CodecRegistry(codecs: []);
|
||||
|
||||
/// Key refers to the `encodingName` param from the [Codec].
|
||||
final Map<String, Codec> _codecs;
|
||||
|
||||
final String _supportedEncodings;
|
||||
|
||||
Codec lookup(String codecName) {
|
||||
Codec? lookup(String codecName) {
|
||||
return _codecs[codecName];
|
||||
}
|
||||
|
||||
|
|
|
@ -33,32 +33,36 @@ class GrpcMetadata extends GrpcMessage {
|
|||
class GrpcData extends GrpcMessage {
|
||||
final List<int> data;
|
||||
final bool isCompressed;
|
||||
GrpcData(this.data, {this.isCompressed}) : assert(data != null);
|
||||
GrpcData(this.data, {required this.isCompressed}) {
|
||||
ArgumentError.checkNotNull(data);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'gRPC Data (${data.length} bytes)';
|
||||
}
|
||||
|
||||
class GrpcMessageSink extends Sink<GrpcMessage> {
|
||||
GrpcMessage message;
|
||||
late final GrpcMessage message;
|
||||
bool _messageReceived = false;
|
||||
|
||||
@override
|
||||
void add(GrpcMessage data) {
|
||||
if (message != null) {
|
||||
if (_messageReceived) {
|
||||
throw StateError('Too many messages received!');
|
||||
}
|
||||
message = data;
|
||||
_messageReceived = true;
|
||||
}
|
||||
|
||||
@override
|
||||
void close() {
|
||||
if (message == null) {
|
||||
if (!_messageReceived) {
|
||||
throw StateError('No messages received!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<int> frame(List<int> rawPayload, [Codec codec]) {
|
||||
List<int> frame(List<int> rawPayload, [Codec? codec]) {
|
||||
final compressedPayload =
|
||||
codec == null ? rawPayload : codec.compress(rawPayload);
|
||||
final payloadLength = compressedPayload.length;
|
||||
|
@ -71,9 +75,9 @@ List<int> frame(List<int> rawPayload, [Codec codec]) {
|
|||
}
|
||||
|
||||
StreamTransformer<GrpcMessage, GrpcMessage> grpcDecompressor({
|
||||
CodecRegistry codecRegistry,
|
||||
CodecRegistry? codecRegistry,
|
||||
}) {
|
||||
Codec codec;
|
||||
Codec? codec;
|
||||
return StreamTransformer<GrpcMessage, GrpcMessage>.fromHandlers(
|
||||
handleData: (GrpcMessage value, EventSink<GrpcMessage> sink) {
|
||||
if (value is GrpcData && value.isCompressed) {
|
||||
|
@ -83,12 +87,12 @@ StreamTransformer<GrpcMessage, GrpcMessage> grpcDecompressor({
|
|||
);
|
||||
return;
|
||||
}
|
||||
sink.add(GrpcData(codec.decompress(value.data), isCompressed: false));
|
||||
sink.add(GrpcData(codec!.decompress(value.data), isCompressed: false));
|
||||
return;
|
||||
}
|
||||
|
||||
if (value is GrpcMetadata && value.metadata.containsKey('grpc-encoding')) {
|
||||
codec = codecRegistry?.lookup(value.metadata['grpc-encoding']);
|
||||
codec = codecRegistry?.lookup(value.metadata['grpc-encoding']!);
|
||||
}
|
||||
sink.add(value);
|
||||
});
|
||||
|
|
|
@ -16,12 +16,12 @@
|
|||
import 'dart:developer';
|
||||
|
||||
typedef TimelineTask TimelineTaskFactory(
|
||||
{String filterKey, TimelineTask parent});
|
||||
{String? filterKey, TimelineTask? parent});
|
||||
|
||||
TimelineTaskFactory timelineTaskFactory = _defaultTimelineTaskFactory;
|
||||
|
||||
TimelineTask _defaultTimelineTaskFactory(
|
||||
{String filterKey, TimelineTask parent}) =>
|
||||
{String? filterKey, TimelineTask? parent}) =>
|
||||
TimelineTask(filterKey: filterKey, parent: parent);
|
||||
|
||||
const String clientTimelineFilterKey = 'grpc/client';
|
||||
|
|
|
@ -125,9 +125,9 @@ class StatusCode {
|
|||
class GrpcError implements Exception {
|
||||
final int code;
|
||||
final String codeName;
|
||||
final String message;
|
||||
final List<GeneratedMessage> details;
|
||||
final Object rawResponse;
|
||||
final String? message;
|
||||
final Object? rawResponse;
|
||||
final List<GeneratedMessage>? details;
|
||||
|
||||
/// Custom error code.
|
||||
GrpcError.custom(this.code, [this.message, this.details, this.rawResponse])
|
||||
|
|
|
@ -58,7 +58,7 @@ class _GrpcMessageConversionSink extends ChunkedConversionSink<StreamMessage> {
|
|||
final Sink<GrpcMessage> _out;
|
||||
|
||||
final _dataHeader = Uint8List(5);
|
||||
Uint8List _data;
|
||||
Uint8List? _data;
|
||||
int _dataOffset = 0;
|
||||
|
||||
_GrpcMessageConversionSink(this._out);
|
||||
|
@ -87,17 +87,17 @@ class _GrpcMessageConversionSink extends ChunkedConversionSink<StreamMessage> {
|
|||
}
|
||||
if (_data != null) {
|
||||
// Reading data.
|
||||
final dataRemaining = _data.lengthInBytes - _dataOffset;
|
||||
final dataRemaining = _data!.lengthInBytes - _dataOffset;
|
||||
if (dataRemaining > 0) {
|
||||
final chunkRemaining = chunkLength - chunkReadOffset;
|
||||
final toCopy = min(dataRemaining, chunkRemaining);
|
||||
_data.setRange(
|
||||
_data!.setRange(
|
||||
_dataOffset, _dataOffset + toCopy, chunkData, chunkReadOffset);
|
||||
_dataOffset += toCopy;
|
||||
chunkReadOffset += toCopy;
|
||||
}
|
||||
if (_dataOffset == _data.lengthInBytes) {
|
||||
_out.add(GrpcData(_data,
|
||||
if (_dataOffset == _data!.lengthInBytes) {
|
||||
_out.add(GrpcData(_data!,
|
||||
isCompressed: _dataHeader.buffer.asByteData().getUint8(0) != 0));
|
||||
_data = null;
|
||||
_dataOffset = 0;
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
// Mostly inspired by grpc-java implementation.
|
||||
// TODO(jakobr): Modify to match grpc/core implementation instead.
|
||||
String toTimeoutString(Duration duration) {
|
||||
if (duration == null) return null;
|
||||
const cutoff = 100000;
|
||||
final timeout = duration.inMicroseconds;
|
||||
if (timeout < 0) {
|
||||
|
@ -38,7 +37,7 @@ String toTimeoutString(Duration duration) {
|
|||
|
||||
/// Convert [timeout] from grpc-timeout header string format to [Duration].
|
||||
/// Returns [null] if [timeout] is not correctly formatted.
|
||||
Duration fromTimeoutString(String timeout) {
|
||||
Duration? fromTimeoutString(String? timeout) {
|
||||
if (timeout == null) return null;
|
||||
if (timeout.length < 2) return null;
|
||||
final value = int.tryParse(timeout.substring(0, timeout.length - 1));
|
||||
|
|
38
pubspec.yaml
38
pubspec.yaml
|
@ -1,28 +1,42 @@
|
|||
name: grpc
|
||||
description: Dart implementation of gRPC, a high performance, open-source universal RPC framework.
|
||||
|
||||
version: 2.9.0
|
||||
version: 3.0.0-nullsafety.0
|
||||
|
||||
homepage: https://github.com/dart-lang/grpc-dart
|
||||
|
||||
environment:
|
||||
sdk: '>=2.8.0 <3.0.0'
|
||||
sdk: '>=2.12.0-0 <3.0.0'
|
||||
|
||||
dependencies:
|
||||
archive: ^2.0.13
|
||||
archive: ^3.0.0-nullsafety
|
||||
async: ^2.2.0
|
||||
crypto: ^2.1.4
|
||||
fixnum: ^0.10.11
|
||||
crypto: ^3.0.0-nullsafety
|
||||
fixnum: ^1.0.0-nullsafety
|
||||
googleapis_auth: ^0.2.7
|
||||
meta: ^1.1.6
|
||||
http: ^0.12.0
|
||||
http2: ^1.0.0
|
||||
protobuf: ^1.0.1
|
||||
http2: ^2.0.0-nullsafety
|
||||
protobuf: ^2.0.0-nullsafety.1
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^1.5.2
|
||||
build_test: ^0.10.8
|
||||
build_web_compilers: ^2.1.1
|
||||
mockito: ^4.1.0
|
||||
test: ^1.6.4
|
||||
build_runner: ^1.11.0
|
||||
build_test: ^1.3.4
|
||||
mockito: ^5.0.0-nullsafety.5
|
||||
test: ^1.16.0-nullsafety.16
|
||||
stream_channel: ^2.0.0
|
||||
stream_transform: ^2.0.0-nullsafety
|
||||
|
||||
dependency_overrides:
|
||||
http:
|
||||
git:
|
||||
url: https://github.com/dart-lang/http.git
|
||||
ref: 3845753a54624b070828cb3eff7a6c2a4e046cfb
|
||||
googleapis_auth:
|
||||
git:
|
||||
url: https://github.com/dart-lang/googleapis_auth.git
|
||||
ref: 30c084b7650fbd3e52525a127e0e65fc528e85a8
|
||||
shelf:
|
||||
git:
|
||||
url: https://github.com/dart-lang/shelf.git
|
||||
ref: 2102572a6c27b1321823bb3e3723ddb5448e04ae
|
||||
|
|
|
@ -16,7 +16,7 @@ class TestClient extends grpc.Client {
|
|||
(List<int> value) => value[0]);
|
||||
|
||||
TestClient(ClientChannel channel) : super(channel);
|
||||
grpc.ResponseStream<int> stream(int request, {grpc.CallOptions options}) {
|
||||
grpc.ResponseStream<int> stream(int request, {grpc.CallOptions? options}) {
|
||||
return $createStreamingCall(_$stream, Stream.value(request),
|
||||
options: options);
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ main() async {
|
|||
|
||||
final channel = FixedConnectionClientChannel(Http2ClientConnection(
|
||||
address,
|
||||
server.port,
|
||||
server.port!,
|
||||
grpc.ChannelOptions(
|
||||
idleTimeout: Duration(minutes: 1),
|
||||
// Short delay to test that it will time out.
|
||||
|
@ -84,7 +84,7 @@ main() async {
|
|||
|
||||
final channel = FixedConnectionClientChannel(Http2ClientConnection(
|
||||
address,
|
||||
server.port,
|
||||
server.port!,
|
||||
grpc.ChannelOptions(credentials: grpc.ChannelCredentials.insecure())));
|
||||
final states = <grpc.ConnectionState>[];
|
||||
channel.clientConnection.onStateChanged =
|
||||
|
|
|
@ -31,7 +31,7 @@ import '../src/utils.dart';
|
|||
void main() {
|
||||
const dummyValue = 0;
|
||||
|
||||
ClientHarness harness;
|
||||
late ClientHarness harness;
|
||||
|
||||
setUp(() {
|
||||
harness = ClientHarness()..setUp();
|
||||
|
@ -399,8 +399,8 @@ void main() {
|
|||
|
||||
test('Connection errors are reported', () async {
|
||||
final connectionStates = <ConnectionState>[];
|
||||
harness.connection.connectionError = 'Connection error';
|
||||
harness.connection.onStateChanged = (connection) {
|
||||
harness.connection!.connectionError = 'Connection error';
|
||||
harness.connection!.onStateChanged = (connection) {
|
||||
final state = connection.state;
|
||||
connectionStates.add(state);
|
||||
};
|
||||
|
@ -418,7 +418,7 @@ void main() {
|
|||
test('Connections time out if idle', () async {
|
||||
final done = Completer();
|
||||
final connectionStates = <ConnectionState>[];
|
||||
harness.connection.onStateChanged = (connection) {
|
||||
harness.connection!.onStateChanged = (connection) {
|
||||
final state = connection.state;
|
||||
connectionStates.add(state);
|
||||
if (state == ConnectionState.idle) done.complete();
|
||||
|
@ -464,7 +464,7 @@ void main() {
|
|||
credentials: ChannelCredentials.insecure(authority: 'myauthority.com'));
|
||||
expect(Http2ClientConnection('localhost', 8080, channelOptions).authority,
|
||||
'myauthority.com');
|
||||
expect(Http2ClientConnection('localhost', null, channelOptions).authority,
|
||||
expect(Http2ClientConnection('localhost', 443, channelOptions).authority,
|
||||
'myauthority.com');
|
||||
});
|
||||
|
||||
|
@ -485,12 +485,6 @@ void main() {
|
|||
expect(decodedDetails.length, 0);
|
||||
});
|
||||
|
||||
test('decodeStatusDetails should handle a null input', () {
|
||||
final decodedDetails = decodeStatusDetails(null);
|
||||
expect(decodedDetails, isA<List<GeneratedMessage>>());
|
||||
expect(decodedDetails.length, 0);
|
||||
});
|
||||
|
||||
test('parseGeneratedMessage should parse out a valid Any type', () {
|
||||
final status = Status.fromBuffer(base64Url.decode(
|
||||
'CAMSEGFtb3VudCB0b28gc21hbGwafgopdHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUucnBjLkJhZFJlcXVlc3QSUQpPCgZhbW91bnQSRVRoZSByZXF1aXJlZCBjdXJyZW5jeSBjb252ZXJzaW9uIHdvdWxkIHJlc3VsdCBpbiBhIHplcm8gdmFsdWUgcGF5bWVudA=='));
|
||||
|
|
|
@ -26,7 +26,7 @@ import '../src/utils.dart';
|
|||
void main() {
|
||||
const dummyValue = 0;
|
||||
|
||||
ClientTransportConnectorHarness harness;
|
||||
late ClientTransportConnectorHarness harness;
|
||||
|
||||
setUp(() {
|
||||
harness = ClientTransportConnectorHarness()..setUp();
|
||||
|
@ -349,8 +349,8 @@ void main() {
|
|||
|
||||
test('Connection errors are reported', () async {
|
||||
final connectionStates = <ConnectionState>[];
|
||||
harness.connection.connectionError = 'Connection error';
|
||||
harness.connection.onStateChanged = (connection) {
|
||||
harness.connection!.connectionError = 'Connection error';
|
||||
harness.connection!.onStateChanged = (connection) {
|
||||
final state = connection.state;
|
||||
connectionStates.add(state);
|
||||
};
|
||||
|
@ -368,7 +368,7 @@ void main() {
|
|||
test('Connections time out if idle', () async {
|
||||
final done = Completer();
|
||||
final connectionStates = <ConnectionState>[];
|
||||
harness.connection.onStateChanged = (connection) {
|
||||
harness.connection!.onStateChanged = (connection) {
|
||||
final state = connection.state;
|
||||
connectionStates.add(state);
|
||||
if (state == ConnectionState.idle) done.complete();
|
||||
|
@ -414,7 +414,7 @@ void main() {
|
|||
credentials: ChannelCredentials.insecure(authority: 'myauthority.com'));
|
||||
expect(Http2ClientConnection('localhost', 8080, channelOptions).authority,
|
||||
'myauthority.com');
|
||||
expect(Http2ClientConnection('localhost', null, channelOptions).authority,
|
||||
expect(Http2ClientConnection('localhost', 443, channelOptions).authority,
|
||||
'myauthority.com');
|
||||
});
|
||||
}
|
||||
|
|
|
@ -18,16 +18,22 @@ import 'dart:async';
|
|||
|
||||
import 'dart:html';
|
||||
|
||||
import 'package:async/async.dart';
|
||||
import 'package:grpc/src/client/call.dart';
|
||||
import 'package:grpc/src/client/transport/xhr_transport.dart';
|
||||
import 'package:grpc/src/shared/message.dart';
|
||||
import 'package:grpc/src/shared/status.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_transform/stream_transform.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
final readyStateChangeEvent =
|
||||
Event('readystatechange', canBubble: false, cancelable: false);
|
||||
final progressEvent = ProgressEvent('onloadstart');
|
||||
|
||||
class MockHttpRequest extends Mock implements HttpRequest {
|
||||
MockHttpRequest({int code}) : status = code ?? 200;
|
||||
MockHttpRequest({int? code}) : status = code ?? 200;
|
||||
// ignore: close_sinks
|
||||
StreamController<Event> readyStateChangeController =
|
||||
StreamController<Event>();
|
||||
|
@ -46,14 +52,20 @@ class MockHttpRequest extends Mock implements HttpRequest {
|
|||
|
||||
@override
|
||||
final int status;
|
||||
|
||||
int get readyState => super.noSuchMethod(Invocation.getter(#readyState), -1);
|
||||
|
||||
@override
|
||||
Map<String, String> get responseHeaders => super
|
||||
.noSuchMethod(Invocation.getter(#responseHeaders), <String, String>{});
|
||||
}
|
||||
|
||||
class MockXhrClientConnection extends XhrClientConnection {
|
||||
MockXhrClientConnection({int code})
|
||||
MockXhrClientConnection({int? code})
|
||||
: _statusCode = code ?? 200,
|
||||
super(Uri.parse('test:8080'));
|
||||
|
||||
MockHttpRequest latestRequest;
|
||||
late MockHttpRequest latestRequest;
|
||||
final int _statusCode;
|
||||
|
||||
@override
|
||||
|
@ -199,28 +211,40 @@ void main() {
|
|||
});
|
||||
|
||||
test('Stream handles headers properly', () async {
|
||||
final metadata = <String, String>{
|
||||
final responseHeaders = {
|
||||
'parameter_1': 'value_1',
|
||||
'parameter_2': 'value_2'
|
||||
};
|
||||
|
||||
final transport = MockXhrClientConnection();
|
||||
|
||||
final stream = transport.makeRequest('test_path', Duration(seconds: 10),
|
||||
metadata, (error, _) => fail(error.toString()));
|
||||
final stream = transport.makeRequest('test_path', Duration(seconds: 10), {},
|
||||
(error, _) => fail(error.toString()));
|
||||
|
||||
stream.incomingMessages.listen((message) {
|
||||
expect(message, TypeMatcher<GrpcMetadata>());
|
||||
if (message is GrpcMetadata) {
|
||||
message.metadata.forEach((key, value) {
|
||||
expect(value, metadata[key]);
|
||||
});
|
||||
}
|
||||
});
|
||||
when(transport.latestRequest.responseHeaders).thenReturn(responseHeaders);
|
||||
when(transport.latestRequest.getResponseHeader('Content-Type'))
|
||||
.thenReturn('application/grpc+proto');
|
||||
when(transport.latestRequest.response)
|
||||
.thenReturn(String.fromCharCodes(frame(<int>[])));
|
||||
|
||||
// Set expectation for request readyState and generate two readyStateChange
|
||||
// events, so that incomingMessages stream completes.
|
||||
final readyStates = [HttpRequest.HEADERS_RECEIVED, HttpRequest.DONE];
|
||||
when(transport.latestRequest.readyState)
|
||||
.thenAnswer((_) => readyStates.removeAt(0));
|
||||
transport.latestRequest.readyStateChangeController
|
||||
.add(readyStateChangeEvent);
|
||||
transport.latestRequest.readyStateChangeController
|
||||
.add(readyStateChangeEvent);
|
||||
|
||||
// Should be only one metadata message with headers.
|
||||
final message = await stream.incomingMessages.single as GrpcMetadata;
|
||||
expect(message.metadata, responseHeaders);
|
||||
});
|
||||
|
||||
test('Stream handles trailers properly', () async {
|
||||
final trailers = <String, String>{
|
||||
final requestHeaders = {'parameter_1': 'value_1'};
|
||||
final responseTrailers = <String, String>{
|
||||
'trailer_1': 'value_1',
|
||||
'trailer_2': 'value_2'
|
||||
};
|
||||
|
@ -228,31 +252,37 @@ void main() {
|
|||
final connection = MockXhrClientConnection();
|
||||
|
||||
final stream = connection.makeRequest('test_path', Duration(seconds: 10),
|
||||
{}, (error, _) => fail(error.toString()));
|
||||
requestHeaders, (error, _) => fail(error.toString()));
|
||||
|
||||
final encodedTrailers = frame(trailers.entries
|
||||
final encodedTrailers = frame(responseTrailers.entries
|
||||
.map((e) => '${e.key}:${e.value}')
|
||||
.join('\r\n')
|
||||
.codeUnits);
|
||||
encodedTrailers[0] = 0x80; // Mark this frame as trailers.
|
||||
final encodedString = String.fromCharCodes(encodedTrailers);
|
||||
|
||||
stream.incomingMessages.listen((message) {
|
||||
expect(message, TypeMatcher<GrpcMetadata>());
|
||||
if (message is GrpcMetadata) {
|
||||
message.metadata.forEach((key, value) {
|
||||
expect(value, trailers[key]);
|
||||
});
|
||||
}
|
||||
});
|
||||
when(connection.latestRequest.getResponseHeader('Content-Type'))
|
||||
.thenReturn('application/grpc+proto');
|
||||
when(connection.latestRequest.responseHeaders).thenReturn({});
|
||||
when(connection.latestRequest.readyState)
|
||||
.thenReturn(HttpRequest.HEADERS_RECEIVED);
|
||||
when(connection.latestRequest.response).thenReturn(encodedString);
|
||||
connection.latestRequest.readyStateChangeController.add(null);
|
||||
connection.latestRequest.progressController.add(null);
|
||||
|
||||
// Set expectation for request readyState and generate events so that
|
||||
// incomingMessages stream completes.
|
||||
final readyStates = [HttpRequest.HEADERS_RECEIVED, HttpRequest.DONE];
|
||||
when(connection.latestRequest.readyState)
|
||||
.thenAnswer((_) => readyStates.removeAt(0));
|
||||
connection.latestRequest.readyStateChangeController
|
||||
.add(readyStateChangeEvent);
|
||||
connection.latestRequest.progressController.add(progressEvent);
|
||||
connection.latestRequest.readyStateChangeController
|
||||
.add(readyStateChangeEvent);
|
||||
|
||||
// Should be two metadata messages: headers and trailers.
|
||||
final messages =
|
||||
await stream.incomingMessages.whereType<GrpcMetadata>().toList();
|
||||
expect(messages.length, 2);
|
||||
expect(messages.first.metadata, isEmpty);
|
||||
expect(messages.last.metadata, responseTrailers);
|
||||
});
|
||||
|
||||
test('Stream handles empty trailers properly', () async {
|
||||
|
@ -265,24 +295,32 @@ void main() {
|
|||
encoded[0] = 0x80; // Mark this frame as trailers.
|
||||
final encodedString = String.fromCharCodes(encoded);
|
||||
|
||||
stream.incomingMessages.listen((message) {
|
||||
expect(message, TypeMatcher<GrpcMetadata>());
|
||||
if (message is GrpcMetadata) {
|
||||
message.metadata.isEmpty;
|
||||
}
|
||||
});
|
||||
when(connection.latestRequest.getResponseHeader('Content-Type'))
|
||||
.thenReturn('application/grpc+proto');
|
||||
when(connection.latestRequest.responseHeaders).thenReturn({});
|
||||
when(connection.latestRequest.readyState)
|
||||
.thenReturn(HttpRequest.HEADERS_RECEIVED);
|
||||
when(connection.latestRequest.response).thenReturn(encodedString);
|
||||
connection.latestRequest.readyStateChangeController.add(null);
|
||||
connection.latestRequest.progressController.add(null);
|
||||
|
||||
// Set expectation for request readyState and generate events so that
|
||||
// incomingMessages stream completes.
|
||||
final readyStates = [HttpRequest.HEADERS_RECEIVED, HttpRequest.DONE];
|
||||
when(connection.latestRequest.readyState)
|
||||
.thenAnswer((_) => readyStates.removeAt(0));
|
||||
connection.latestRequest.readyStateChangeController
|
||||
.add(readyStateChangeEvent);
|
||||
connection.latestRequest.progressController.add(progressEvent);
|
||||
connection.latestRequest.readyStateChangeController
|
||||
.add(readyStateChangeEvent);
|
||||
|
||||
// Should be two metadata messages: headers and trailers.
|
||||
final messages =
|
||||
await stream.incomingMessages.whereType<GrpcMetadata>().toList();
|
||||
expect(messages.length, 2);
|
||||
expect(messages.first.metadata, isEmpty);
|
||||
expect(messages.last.metadata, isEmpty);
|
||||
});
|
||||
|
||||
test('Stream deserializes data properly', () async {
|
||||
final metadata = <String, String>{
|
||||
final requestHeaders = <String, String>{
|
||||
'parameter_1': 'value_1',
|
||||
'parameter_2': 'value_2'
|
||||
};
|
||||
|
@ -290,57 +328,50 @@ void main() {
|
|||
final connection = MockXhrClientConnection();
|
||||
|
||||
final stream = connection.makeRequest('test_path', Duration(seconds: 10),
|
||||
metadata, (error, _) => fail(error.toString()));
|
||||
requestHeaders, (error, _) => fail(error.toString()));
|
||||
final data = List<int>.filled(10, 224);
|
||||
final encoded = frame(data);
|
||||
final encodedString = String.fromCharCodes(encoded);
|
||||
|
||||
stream.incomingMessages.listen(expectAsync1((message) {
|
||||
if (message is GrpcData) {
|
||||
expect(message.data, equals(data));
|
||||
}
|
||||
}, count: 2));
|
||||
|
||||
when(connection.latestRequest.getResponseHeader('Content-Type'))
|
||||
.thenReturn('application/grpc+proto');
|
||||
when(connection.latestRequest.responseHeaders).thenReturn(metadata);
|
||||
when(connection.latestRequest.responseHeaders).thenReturn({});
|
||||
when(connection.latestRequest.response)
|
||||
.thenReturn(String.fromCharCodes(frame(data)));
|
||||
|
||||
// Set expectation for request readyState and generate events, so that
|
||||
// incomingMessages stream completes.
|
||||
final readyStates = [HttpRequest.HEADERS_RECEIVED, HttpRequest.DONE];
|
||||
when(connection.latestRequest.readyState)
|
||||
.thenReturn(HttpRequest.HEADERS_RECEIVED);
|
||||
when(connection.latestRequest.response).thenReturn(encodedString);
|
||||
connection.latestRequest.readyStateChangeController.add(null);
|
||||
connection.latestRequest.progressController.add(null);
|
||||
.thenAnswer((_) => readyStates.removeAt(0));
|
||||
connection.latestRequest.readyStateChangeController
|
||||
.add(readyStateChangeEvent);
|
||||
connection.latestRequest.progressController.add(progressEvent);
|
||||
connection.latestRequest.readyStateChangeController
|
||||
.add(readyStateChangeEvent);
|
||||
|
||||
// Expect a single data message.
|
||||
final message = await stream.incomingMessages.whereType<GrpcData>().single;
|
||||
expect(message.data, data);
|
||||
});
|
||||
|
||||
test('GrpcError with error details in response', () async {
|
||||
final metadata = <String, String>{
|
||||
'parameter_1': 'value_1',
|
||||
'parameter_2': 'value_2'
|
||||
};
|
||||
|
||||
final connection = MockXhrClientConnection(code: 400);
|
||||
final errorStream = StreamController<GrpcError>();
|
||||
connection.makeRequest('test_path', Duration(seconds: 10), metadata,
|
||||
(e, _) => errorStream.add(e));
|
||||
const errorDetails = "error details";
|
||||
int count = 0;
|
||||
|
||||
errorStream.stream.listen((error) {
|
||||
expect(
|
||||
error,
|
||||
TypeMatcher<GrpcError>()
|
||||
.having((e) => e.rawResponse, 'rawResponse', errorDetails));
|
||||
count++;
|
||||
if (count == 2) {
|
||||
errorStream.close();
|
||||
}
|
||||
final errors = <GrpcError>[];
|
||||
// The incoming messages stream never completes when there's an error, so
|
||||
// using completer.
|
||||
final errorReceived = Completer<void>();
|
||||
connection.makeRequest('test_path', Duration(seconds: 10), {}, (e, _) {
|
||||
errorReceived.complete();
|
||||
errors.add(e);
|
||||
});
|
||||
|
||||
const errorDetails = "error details";
|
||||
when(connection.latestRequest.getResponseHeader('Content-Type'))
|
||||
.thenReturn('application/grpc+proto');
|
||||
when(connection.latestRequest.responseHeaders).thenReturn(metadata);
|
||||
when(connection.latestRequest.responseHeaders).thenReturn({});
|
||||
when(connection.latestRequest.readyState).thenReturn(HttpRequest.DONE);
|
||||
when(connection.latestRequest.responseText).thenReturn(errorDetails);
|
||||
connection.latestRequest.readyStateChangeController.add(null);
|
||||
connection.latestRequest.readyStateChangeController
|
||||
.add(readyStateChangeEvent);
|
||||
await errorReceived;
|
||||
expect(errors.single.rawResponse, errorDetails);
|
||||
});
|
||||
|
||||
test('Stream recieves multiple messages', () async {
|
||||
|
@ -358,40 +389,44 @@ void main() {
|
|||
List<int>.filled(10, 224),
|
||||
List<int>.filled(5, 124)
|
||||
];
|
||||
final encoded = data.map((d) => frame(d));
|
||||
final encodedStrings = encoded.map((e) => String.fromCharCodes(e)).toList();
|
||||
|
||||
final expectedMessages = <GrpcMessage>[
|
||||
GrpcMetadata(metadata),
|
||||
GrpcData(data[0]),
|
||||
GrpcData(data[1])
|
||||
];
|
||||
int i = 0;
|
||||
stream.incomingMessages.listen(expectAsync1((message) {
|
||||
final expectedMessage = expectedMessages[i];
|
||||
i++;
|
||||
expect(message.runtimeType, expectedMessage.runtimeType);
|
||||
if (message is GrpcMetadata) {
|
||||
expect(message.metadata, (expectedMessage as GrpcMetadata).metadata);
|
||||
} else if (message is GrpcData) {
|
||||
expect(message.data, (expectedMessage as GrpcData).data);
|
||||
}
|
||||
}, count: expectedMessages.length));
|
||||
final encodedStrings =
|
||||
data.map((d) => String.fromCharCodes(frame(d))).toList();
|
||||
|
||||
when(connection.latestRequest.getResponseHeader('Content-Type'))
|
||||
.thenReturn('application/grpc+proto');
|
||||
when(connection.latestRequest.responseHeaders).thenReturn(metadata);
|
||||
when(connection.latestRequest.readyState)
|
||||
.thenReturn(HttpRequest.HEADERS_RECEIVED);
|
||||
// At first - expected response is the first message
|
||||
when(connection.latestRequest.response)
|
||||
.thenAnswer((_) => encodedStrings[0]);
|
||||
connection.latestRequest.readyStateChangeController.add(null);
|
||||
connection.latestRequest.progressController.add(null);
|
||||
|
||||
// After the first call, expected response should now be both responses together
|
||||
when(connection.latestRequest.response)
|
||||
.thenAnswer((_) => encodedStrings[0] + encodedStrings[1]);
|
||||
connection.latestRequest.progressController.add(null);
|
||||
// At first invocation the response should be the the first message, after
|
||||
// that first + last messages.
|
||||
var first = true;
|
||||
when(connection.latestRequest.response).thenAnswer((_) {
|
||||
if (first) {
|
||||
first = false;
|
||||
return encodedStrings[0];
|
||||
}
|
||||
return encodedStrings[0] + encodedStrings[1];
|
||||
});
|
||||
|
||||
final readyStates = [HttpRequest.HEADERS_RECEIVED, HttpRequest.DONE];
|
||||
when(connection.latestRequest.readyState)
|
||||
.thenAnswer((_) => readyStates.removeAt(0));
|
||||
|
||||
final queue = StreamQueue(stream.incomingMessages);
|
||||
// Headers.
|
||||
connection.latestRequest.readyStateChangeController
|
||||
.add(readyStateChangeEvent);
|
||||
expect(((await queue.next) as GrpcMetadata).metadata, metadata);
|
||||
// Data 1.
|
||||
connection.latestRequest.progressController.add(progressEvent);
|
||||
expect(((await queue.next) as GrpcData).data, data[0]);
|
||||
// Data 2.
|
||||
connection.latestRequest.progressController.add(progressEvent);
|
||||
expect(((await queue.next) as GrpcData).data, data[1]);
|
||||
// Done.
|
||||
connection.latestRequest.readyStateChangeController
|
||||
.add(readyStateChangeEvent);
|
||||
expect(await queue.hasNext, isFalse);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ import 'src/server_utils.dart';
|
|||
void main() {
|
||||
const dummyValue = 17;
|
||||
|
||||
ConnectionServerHarness harness;
|
||||
late ConnectionServerHarness harness;
|
||||
|
||||
setUp(() {
|
||||
harness = ConnectionServerHarness()..setUp();
|
||||
|
@ -257,7 +257,7 @@ void main() {
|
|||
harness
|
||||
..service.unaryHandler = methodHandler
|
||||
..fromServer.stream.listen(expectAsync1((_) {}, count: 0),
|
||||
onError: expectAsync1((error) {
|
||||
onError: expectAsync1((dynamic error) {
|
||||
expect(error, 'TERMINATED');
|
||||
}, count: 1),
|
||||
onDone: expectAsync0(() {}, count: 1))
|
||||
|
|
|
@ -11,7 +11,7 @@ import 'package:grpc/grpc_web.dart';
|
|||
import 'src/generated/echo.pbgrpc.dart';
|
||||
|
||||
void main() {
|
||||
GrpcWebServer server;
|
||||
late GrpcWebServer server;
|
||||
|
||||
setUpAll(() async {
|
||||
server = await GrpcWebServer.start();
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
@TestOn('vm')
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:grpc/src/client/transport/http2_credentials.dart';
|
||||
|
|
|
@ -16,7 +16,7 @@ class TestClient extends Client {
|
|||
(int value) => [value], (List<int> value) => value[0]);
|
||||
|
||||
TestClient(api.ClientChannel channel) : super(channel);
|
||||
ResponseStream<int> stream(int request, {CallOptions options}) {
|
||||
ResponseStream<int> stream(int request, {CallOptions? options}) {
|
||||
return $createStreamingCall(_$stream, Stream.value(request),
|
||||
options: options);
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ main() async {
|
|||
|
||||
final channel = FixedConnectionClientChannel(Http2ClientConnection(
|
||||
address,
|
||||
server.port,
|
||||
server.port!,
|
||||
ChannelOptions(credentials: ChannelCredentials.insecure()),
|
||||
));
|
||||
final testClient = TestClient(channel);
|
||||
|
@ -83,7 +83,7 @@ main() async {
|
|||
|
||||
final channel = FixedConnectionClientChannel(Http2ClientConnection(
|
||||
address,
|
||||
server.port,
|
||||
server.port!,
|
||||
ChannelOptions(
|
||||
credentials: ChannelCredentials.insecure(),
|
||||
codecRegistry: CodecRegistry(codecs: const [GzipCodec()]),
|
||||
|
@ -111,7 +111,7 @@ main() async {
|
|||
|
||||
final channel = FixedConnectionClientChannel(Http2ClientConnection(
|
||||
address,
|
||||
server.port,
|
||||
server.port!,
|
||||
ChannelOptions(
|
||||
credentials: ChannelCredentials.secure(
|
||||
certificates: File('test/data/localhost.crt').readAsBytesSync(),
|
||||
|
@ -129,7 +129,7 @@ main() async {
|
|||
|
||||
final channel = FixedConnectionClientChannel(Http2ClientConnection(
|
||||
'localhost',
|
||||
server.port,
|
||||
server.port!,
|
||||
ChannelOptions(credentials: ChannelCredentials.insecure()),
|
||||
));
|
||||
final testClient = TestClient(channel);
|
||||
|
@ -145,7 +145,7 @@ main() async {
|
|||
|
||||
final channel = FixedConnectionClientChannel(Http2ClientConnection(
|
||||
'localhost',
|
||||
server.port,
|
||||
server.port!,
|
||||
ChannelOptions(credentials: ChannelCredentials.insecure()),
|
||||
));
|
||||
final testClient = TestClient(channel);
|
||||
|
|
|
@ -14,7 +14,7 @@ class TestClient extends grpc.Client {
|
|||
|
||||
TestClient(grpc.ClientChannel channel) : super(channel);
|
||||
grpc.ResponseStream<int> infiniteStream(int request,
|
||||
{grpc.CallOptions options}) {
|
||||
{grpc.CallOptions? options}) {
|
||||
return $createStreamingCall(_$infiniteStream, Stream.value(request),
|
||||
options: options);
|
||||
}
|
||||
|
@ -24,12 +24,13 @@ class TestService extends grpc.Service {
|
|||
String get $name => 'test.TestService';
|
||||
final void Function() finallyCallback;
|
||||
|
||||
TestService({this.finallyCallback}) {
|
||||
TestService({required this.finallyCallback}) {
|
||||
$addMethod(grpc.ServiceMethod<int, int>('infiniteStream', infiniteStream,
|
||||
false, true, (List<int> value) => value[0], (int value) => [value]));
|
||||
}
|
||||
|
||||
Stream<int> infiniteStream(grpc.ServiceCall call, Future request) async* {
|
||||
Stream<int> infiniteStream(
|
||||
grpc.ServiceCall call, Future<int> request) async* {
|
||||
int count = await request;
|
||||
try {
|
||||
while (true) {
|
||||
|
@ -50,7 +51,8 @@ class ClientData {
|
|||
final InternetAddress address;
|
||||
final int port;
|
||||
final SendPort sendPort;
|
||||
ClientData({this.address, this.port, this.sendPort});
|
||||
ClientData(
|
||||
{required this.address, required this.port, required this.sendPort});
|
||||
}
|
||||
|
||||
void client(clientData) async {
|
||||
|
@ -73,7 +75,7 @@ main() async {
|
|||
"the client interrupting the connection does not crash the server",
|
||||
(address) async {
|
||||
// interrrupt the connect of client, the server does not crash.
|
||||
grpc.Server server;
|
||||
late grpc.Server server;
|
||||
server = grpc.Server([
|
||||
TestService(
|
||||
finallyCallback: expectAsync0(() {
|
||||
|
@ -86,7 +88,7 @@ main() async {
|
|||
client,
|
||||
ClientData(
|
||||
address: address,
|
||||
port: server.port,
|
||||
port: server.port!,
|
||||
sendPort: receivePort.sendPort));
|
||||
receivePort.listen(expectAsync1((e) {
|
||||
expect(e, isA<grpc.GrpcError>());
|
||||
|
|
|
@ -25,7 +25,7 @@ import 'src/server_utils.dart';
|
|||
void main() {
|
||||
const dummyValue = 17;
|
||||
|
||||
ServerHarness harness;
|
||||
late ServerHarness harness;
|
||||
|
||||
setUp(() {
|
||||
harness = ServerHarness()..setUp();
|
||||
|
@ -271,7 +271,7 @@ void main() {
|
|||
harness
|
||||
..service.unaryHandler = methodHandler
|
||||
..fromServer.stream.listen(expectAsync1((_) {}, count: 0),
|
||||
onError: expectAsync1((error) {
|
||||
onError: expectAsync1((dynamic error) {
|
||||
expect(error, 'TERMINATED');
|
||||
}, count: 1),
|
||||
onDone: expectAsync0(() {}, count: 1))
|
||||
|
|
|
@ -21,15 +21,14 @@ import 'package:grpc/src/client/channel.dart' as base;
|
|||
import 'package:grpc/src/client/http2_connection.dart';
|
||||
import 'package:grpc/src/shared/message.dart';
|
||||
import 'package:http2/transport.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'client_utils.mocks.dart';
|
||||
import 'utils.dart';
|
||||
|
||||
class MockTransport extends Mock implements ClientTransportConnection {}
|
||||
|
||||
class MockStream extends Mock implements ClientTransportStream {}
|
||||
|
||||
@GenerateMocks([ClientTransportConnection, ClientTransportStream])
|
||||
class FakeConnection extends Http2ClientConnection {
|
||||
final ClientTransportConnection transport;
|
||||
|
||||
|
@ -60,7 +59,7 @@ class FakeClientTransportConnection extends Http2ClientConnection {
|
|||
}
|
||||
}
|
||||
|
||||
Duration testBackoff(Duration lastBackoff) => const Duration(milliseconds: 1);
|
||||
Duration testBackoff(Duration? lastBackoff) => const Duration(milliseconds: 1);
|
||||
|
||||
class FakeChannelOptions implements ChannelOptions {
|
||||
ChannelCredentials credentials = const ChannelCredentials.secure();
|
||||
|
@ -97,16 +96,16 @@ class FakeClientConnectorChannel extends ClientTransportConnectorChannel {
|
|||
typedef ServerMessageHandler = void Function(StreamMessage message);
|
||||
|
||||
class TestClient extends Client {
|
||||
ClientMethod<int, int> _$unary;
|
||||
ClientMethod<int, int> _$clientStreaming;
|
||||
ClientMethod<int, int> _$serverStreaming;
|
||||
ClientMethod<int, int> _$bidirectional;
|
||||
late ClientMethod<int, int> _$unary;
|
||||
late ClientMethod<int, int> _$clientStreaming;
|
||||
late ClientMethod<int, int> _$serverStreaming;
|
||||
late ClientMethod<int, int> _$bidirectional;
|
||||
|
||||
final int Function(List<int> value) decode;
|
||||
|
||||
TestClient(base.ClientChannel channel,
|
||||
{CallOptions options,
|
||||
Iterable<ClientInterceptor> interceptors,
|
||||
{CallOptions? options,
|
||||
Iterable<ClientInterceptor>? interceptors,
|
||||
this.decode: mockDecode})
|
||||
: super(channel, options: options, interceptors: interceptors) {
|
||||
_$unary = ClientMethod<int, int>('/Test/Unary', mockEncode, decode);
|
||||
|
@ -118,34 +117,34 @@ class TestClient extends Client {
|
|||
ClientMethod<int, int>('/Test/Bidirectional', mockEncode, decode);
|
||||
}
|
||||
|
||||
ResponseFuture<int> unary(int request, {CallOptions options}) {
|
||||
ResponseFuture<int> unary(int request, {CallOptions? options}) {
|
||||
return $createUnaryCall(_$unary, request, options: options);
|
||||
}
|
||||
|
||||
ResponseFuture<int> clientStreaming(Stream<int> request,
|
||||
{CallOptions options}) {
|
||||
{CallOptions? options}) {
|
||||
return $createStreamingCall(_$clientStreaming, request, options: options)
|
||||
.single;
|
||||
}
|
||||
|
||||
ResponseStream<int> serverStreaming(int request, {CallOptions options}) {
|
||||
ResponseStream<int> serverStreaming(int request, {CallOptions? options}) {
|
||||
return $createStreamingCall(_$serverStreaming, Stream.value(request),
|
||||
options: options);
|
||||
}
|
||||
|
||||
ResponseStream<int> bidirectional(Stream<int> request,
|
||||
{CallOptions options}) {
|
||||
{CallOptions? options}) {
|
||||
return $createStreamingCall(_$bidirectional, request, options: options);
|
||||
}
|
||||
}
|
||||
|
||||
class ClientHarness extends _Harness {
|
||||
FakeConnection connection;
|
||||
FakeConnection? connection;
|
||||
|
||||
@override
|
||||
FakeChannel createChannel() {
|
||||
connection = FakeConnection('test', transport, channelOptions);
|
||||
return FakeChannel('test', connection, channelOptions);
|
||||
return FakeChannel('test', connection!, channelOptions);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -153,14 +152,14 @@ class ClientHarness extends _Harness {
|
|||
}
|
||||
|
||||
class ClientTransportConnectorHarness extends _Harness {
|
||||
FakeClientTransportConnection connection;
|
||||
ClientTransportConnector connector;
|
||||
FakeClientTransportConnection? connection;
|
||||
late ClientTransportConnector connector;
|
||||
|
||||
@override
|
||||
FakeClientConnectorChannel createChannel() {
|
||||
connector = FakeClientTransportConnector(transport);
|
||||
connection = FakeClientTransportConnection(connector, channelOptions);
|
||||
return FakeClientConnectorChannel(connector, connection, channelOptions);
|
||||
return FakeClientConnectorChannel(connector, connection!, channelOptions);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -187,27 +186,27 @@ class FakeClientTransportConnector extends ClientTransportConnector {
|
|||
}
|
||||
|
||||
abstract class _Harness {
|
||||
MockTransport transport;
|
||||
base.ClientChannel channel;
|
||||
FakeChannelOptions channelOptions;
|
||||
MockStream stream;
|
||||
late MockClientTransportConnection transport;
|
||||
late base.ClientChannel channel;
|
||||
late FakeChannelOptions channelOptions;
|
||||
late MockClientTransportStream stream;
|
||||
|
||||
StreamController<StreamMessage> fromClient;
|
||||
StreamController<StreamMessage> toClient;
|
||||
late StreamController<StreamMessage> fromClient;
|
||||
late StreamController<StreamMessage> toClient;
|
||||
|
||||
Iterable<ClientInterceptor> interceptors;
|
||||
Iterable<ClientInterceptor>? interceptors;
|
||||
|
||||
TestClient client;
|
||||
late TestClient client;
|
||||
|
||||
base.ClientChannel createChannel();
|
||||
|
||||
String get expectedAuthority;
|
||||
|
||||
void setUp() {
|
||||
transport = MockTransport();
|
||||
transport = MockClientTransportConnection();
|
||||
channelOptions = FakeChannelOptions();
|
||||
channel = createChannel();
|
||||
stream = MockStream();
|
||||
stream = MockClientTransportStream();
|
||||
fromClient = StreamController();
|
||||
toClient = StreamController();
|
||||
when(transport.makeRequest(any, endStream: anyNamed('endStream')))
|
||||
|
@ -216,6 +215,8 @@ abstract class _Harness {
|
|||
when(transport.isOpen).thenReturn(true);
|
||||
when(stream.outgoingMessages).thenReturn(fromClient.sink);
|
||||
when(stream.incomingMessages).thenAnswer((_) => toClient.stream);
|
||||
when(stream.terminate()).thenReturn(null);
|
||||
when(transport.finish()).thenAnswer((_) async {});
|
||||
client = TestClient(channel, interceptors: interceptors);
|
||||
}
|
||||
|
||||
|
@ -246,13 +247,13 @@ abstract class _Harness {
|
|||
}
|
||||
|
||||
Future<void> runTest(
|
||||
{Future clientCall,
|
||||
{Future? clientCall,
|
||||
dynamic expectedResult,
|
||||
String expectedPath,
|
||||
Duration expectedTimeout,
|
||||
Map<String, String> expectedCustomHeaders,
|
||||
String? expectedPath,
|
||||
Duration? expectedTimeout,
|
||||
Map<String, String>? expectedCustomHeaders,
|
||||
List<MessageHandler> serverHandlers = const [],
|
||||
Function doneHandler,
|
||||
void Function()? doneHandler,
|
||||
bool expectDone = true}) async {
|
||||
int serverHandlerIndex = 0;
|
||||
void handleServerMessage(StreamMessage message) {
|
||||
|
@ -261,7 +262,7 @@ abstract class _Harness {
|
|||
|
||||
final clientSubscription = fromClient.stream.listen(
|
||||
expectAsync1(handleServerMessage, count: serverHandlers.length),
|
||||
onError: expectAsync1((_) {}, count: 0),
|
||||
onError: expectAsync1((dynamic _) {}, count: 0),
|
||||
onDone: expectAsync0(doneHandler ?? () {}, count: expectDone ? 1 : 0));
|
||||
|
||||
final result = await clientCall;
|
||||
|
@ -276,13 +277,14 @@ abstract class _Harness {
|
|||
MapEntry(utf8.decode(header.name), utf8.decode(header.value)))),
|
||||
path: expectedPath,
|
||||
authority: expectedAuthority,
|
||||
timeout: toTimeoutString(expectedTimeout),
|
||||
timeout:
|
||||
expectedTimeout == null ? null : toTimeoutString(expectedTimeout),
|
||||
customHeaders: expectedCustomHeaders);
|
||||
|
||||
await clientSubscription.cancel();
|
||||
}
|
||||
|
||||
Future<void> expectThrows(Future future, dynamic exception) async {
|
||||
Future<void> expectThrows(Future? future, dynamic exception) async {
|
||||
try {
|
||||
await future;
|
||||
fail('Did not throw');
|
||||
|
@ -293,11 +295,11 @@ abstract class _Harness {
|
|||
}
|
||||
|
||||
Future<void> runFailureTest(
|
||||
{Future clientCall,
|
||||
{Future? clientCall,
|
||||
dynamic expectedException,
|
||||
String expectedPath,
|
||||
Duration expectedTimeout,
|
||||
Map<String, String> expectedCustomHeaders,
|
||||
String? expectedPath,
|
||||
Duration? expectedTimeout,
|
||||
Map<String, String>? expectedCustomHeaders,
|
||||
List<MessageHandler> serverHandlers = const [],
|
||||
bool expectDone = true}) async {
|
||||
return runTest(
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
import 'dart:async' as _i3;
|
||||
|
||||
import 'package:http2/src/hpack/hpack.dart' as _i4;
|
||||
import 'package:http2/transport.dart' as _i2;
|
||||
import 'package:mockito/mockito.dart' as _i1;
|
||||
|
||||
// ignore_for_file: comment_references
|
||||
|
||||
// ignore_for_file: unnecessary_parenthesis
|
||||
|
||||
class _FakeClientTransportStream extends _i1.Fake
|
||||
implements _i2.ClientTransportStream {}
|
||||
|
||||
class _FakeStreamSink<S> extends _i1.Fake implements _i3.StreamSink<S> {}
|
||||
|
||||
/// A class which mocks [ClientTransportConnection].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockClientTransportConnection extends _i1.Mock
|
||||
implements _i2.ClientTransportConnection {
|
||||
MockClientTransportConnection() {
|
||||
_i1.throwOnMissingStub(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isOpen =>
|
||||
(super.noSuchMethod(Invocation.getter(#isOpen), false) as bool);
|
||||
@override
|
||||
set onActiveStateChanged(_i2.ActiveStateHandler? callback) =>
|
||||
super.noSuchMethod(Invocation.setter(#onActiveStateChanged, callback));
|
||||
@override
|
||||
_i3.Future<void> get onInitialPeerSettingsReceived => (super.noSuchMethod(
|
||||
Invocation.getter(#onInitialPeerSettingsReceived), Future.value(null))
|
||||
as _i3.Future<void>);
|
||||
@override
|
||||
_i2.ClientTransportStream makeRequest(List<_i4.Header>? headers,
|
||||
{bool? endStream = false}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#makeRequest, [headers], {#endStream: endStream}),
|
||||
_FakeClientTransportStream()) as _i2.ClientTransportStream);
|
||||
@override
|
||||
_i3.Future<dynamic> ping() =>
|
||||
(super.noSuchMethod(Invocation.method(#ping, []), Future.value(null))
|
||||
as _i3.Future<dynamic>);
|
||||
@override
|
||||
_i3.Future<dynamic> finish() =>
|
||||
(super.noSuchMethod(Invocation.method(#finish, []), Future.value(null))
|
||||
as _i3.Future<dynamic>);
|
||||
@override
|
||||
_i3.Future<dynamic> terminate() =>
|
||||
(super.noSuchMethod(Invocation.method(#terminate, []), Future.value(null))
|
||||
as _i3.Future<dynamic>);
|
||||
}
|
||||
|
||||
/// A class which mocks [ClientTransportStream].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockClientTransportStream extends _i1.Mock
|
||||
implements _i2.ClientTransportStream {
|
||||
MockClientTransportStream() {
|
||||
_i1.throwOnMissingStub(this);
|
||||
}
|
||||
|
||||
@override
|
||||
_i3.Stream<_i2.TransportStreamPush> get peerPushes => (super.noSuchMethod(
|
||||
Invocation.getter(#peerPushes),
|
||||
Stream<_i2.TransportStreamPush>.empty())
|
||||
as _i3.Stream<_i2.TransportStreamPush>);
|
||||
@override
|
||||
int get id => (super.noSuchMethod(Invocation.getter(#id), 0) as int);
|
||||
@override
|
||||
_i3.Stream<_i2.StreamMessage> get incomingMessages => (super.noSuchMethod(
|
||||
Invocation.getter(#incomingMessages),
|
||||
Stream<_i2.StreamMessage>.empty()) as _i3.Stream<_i2.StreamMessage>);
|
||||
@override
|
||||
_i3.StreamSink<_i2.StreamMessage> get outgoingMessages => (super.noSuchMethod(
|
||||
Invocation.getter(#outgoingMessages),
|
||||
_FakeStreamSink<_i2.StreamMessage>())
|
||||
as _i3.StreamSink<_i2.StreamMessage>);
|
||||
@override
|
||||
set onTerminated(void Function(int?)? value) =>
|
||||
super.noSuchMethod(Invocation.setter(#onTerminated, value));
|
||||
@override
|
||||
void sendHeaders(List<_i4.Header>? headers, {bool? endStream = false}) =>
|
||||
super.noSuchMethod(
|
||||
Invocation.method(#sendHeaders, [headers], {#endStream: endStream}));
|
||||
@override
|
||||
void sendData(List<int>? bytes, {bool? endStream = false}) =>
|
||||
super.noSuchMethod(
|
||||
Invocation.method(#sendData, [bytes], {#endStream: endStream}));
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
// Generated code. Do not modify.
|
||||
// source: echo.proto
|
||||
//
|
||||
// @dart = 2.3
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
@ -27,7 +27,15 @@ class EchoRequest extends $pb.GeneratedMessage {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
EchoRequest._() : super();
|
||||
factory EchoRequest() => create();
|
||||
factory EchoRequest({
|
||||
$core.String? message,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (message != null) {
|
||||
_result.message = message;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory EchoRequest.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -42,8 +50,8 @@ class EchoRequest extends $pb.GeneratedMessage {
|
|||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
EchoRequest copyWith(void Function(EchoRequest) updates) =>
|
||||
super.copyWith((message) =>
|
||||
updates(message as EchoRequest)); // ignore: deprecated_member_use
|
||||
super.copyWith((message) => updates(message as EchoRequest))
|
||||
as EchoRequest; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static EchoRequest create() => EchoRequest._();
|
||||
|
@ -52,7 +60,7 @@ class EchoRequest extends $pb.GeneratedMessage {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static EchoRequest getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<EchoRequest>(create);
|
||||
static EchoRequest _defaultInstance;
|
||||
static EchoRequest? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get message => $_getSZ(0);
|
||||
|
@ -85,7 +93,15 @@ class EchoResponse extends $pb.GeneratedMessage {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
EchoResponse._() : super();
|
||||
factory EchoResponse() => create();
|
||||
factory EchoResponse({
|
||||
$core.String? message,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (message != null) {
|
||||
_result.message = message;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory EchoResponse.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -100,8 +116,8 @@ class EchoResponse extends $pb.GeneratedMessage {
|
|||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
EchoResponse copyWith(void Function(EchoResponse) updates) =>
|
||||
super.copyWith((message) =>
|
||||
updates(message as EchoResponse)); // ignore: deprecated_member_use
|
||||
super.copyWith((message) => updates(message as EchoResponse))
|
||||
as EchoResponse; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static EchoResponse create() => EchoResponse._();
|
||||
|
@ -111,7 +127,7 @@ class EchoResponse extends $pb.GeneratedMessage {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static EchoResponse getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<EchoResponse>(create);
|
||||
static EchoResponse _defaultInstance;
|
||||
static EchoResponse? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get message => $_getSZ(0);
|
||||
|
@ -156,7 +172,23 @@ class ServerStreamingEchoRequest extends $pb.GeneratedMessage {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
ServerStreamingEchoRequest._() : super();
|
||||
factory ServerStreamingEchoRequest() => create();
|
||||
factory ServerStreamingEchoRequest({
|
||||
$core.String? message,
|
||||
$core.int? messageCount,
|
||||
$core.int? messageInterval,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (message != null) {
|
||||
_result.message = message;
|
||||
}
|
||||
if (messageCount != null) {
|
||||
_result.messageCount = messageCount;
|
||||
}
|
||||
if (messageInterval != null) {
|
||||
_result.messageInterval = messageInterval;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory ServerStreamingEchoRequest.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -173,8 +205,9 @@ class ServerStreamingEchoRequest extends $pb.GeneratedMessage {
|
|||
'Will be removed in next major version')
|
||||
ServerStreamingEchoRequest copyWith(
|
||||
void Function(ServerStreamingEchoRequest) updates) =>
|
||||
super.copyWith((message) => updates(message
|
||||
as ServerStreamingEchoRequest)); // ignore: deprecated_member_use
|
||||
super.copyWith(
|
||||
(message) => updates(message as ServerStreamingEchoRequest))
|
||||
as ServerStreamingEchoRequest; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static ServerStreamingEchoRequest create() => ServerStreamingEchoRequest._();
|
||||
|
@ -184,7 +217,7 @@ class ServerStreamingEchoRequest extends $pb.GeneratedMessage {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static ServerStreamingEchoRequest getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<ServerStreamingEchoRequest>(create);
|
||||
static ServerStreamingEchoRequest _defaultInstance;
|
||||
static ServerStreamingEchoRequest? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get message => $_getSZ(0);
|
||||
|
@ -241,7 +274,15 @@ class ServerStreamingEchoResponse extends $pb.GeneratedMessage {
|
|||
..hasRequiredFields = false;
|
||||
|
||||
ServerStreamingEchoResponse._() : super();
|
||||
factory ServerStreamingEchoResponse() => create();
|
||||
factory ServerStreamingEchoResponse({
|
||||
$core.String? message,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (message != null) {
|
||||
_result.message = message;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory ServerStreamingEchoResponse.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
|
@ -258,8 +299,9 @@ class ServerStreamingEchoResponse extends $pb.GeneratedMessage {
|
|||
'Will be removed in next major version')
|
||||
ServerStreamingEchoResponse copyWith(
|
||||
void Function(ServerStreamingEchoResponse) updates) =>
|
||||
super.copyWith((message) => updates(message
|
||||
as ServerStreamingEchoResponse)); // ignore: deprecated_member_use
|
||||
super.copyWith(
|
||||
(message) => updates(message as ServerStreamingEchoResponse))
|
||||
as ServerStreamingEchoResponse; // ignore: deprecated_member_use
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static ServerStreamingEchoResponse create() =>
|
||||
|
@ -270,7 +312,7 @@ class ServerStreamingEchoResponse extends $pb.GeneratedMessage {
|
|||
@$core.pragma('dart2js:noInline')
|
||||
static ServerStreamingEchoResponse getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<ServerStreamingEchoResponse>(create);
|
||||
static ServerStreamingEchoResponse _defaultInstance;
|
||||
static ServerStreamingEchoResponse? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get message => $_getSZ(0);
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
// Generated code. Do not modify.
|
||||
// source: echo.proto
|
||||
//
|
||||
// @dart = 2.3
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Generated code. Do not modify.
|
||||
// source: echo.proto
|
||||
//
|
||||
// @dart = 2.3
|
||||
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
||||
import 'dart:async' as $async;
|
||||
|
@ -26,18 +26,18 @@ class EchoServiceClient extends $grpc.Client {
|
|||
$0.ServerStreamingEchoResponse.fromBuffer(value));
|
||||
|
||||
EchoServiceClient($grpc.ClientChannel channel,
|
||||
{$grpc.CallOptions options,
|
||||
$core.Iterable<$grpc.ClientInterceptor> interceptors})
|
||||
{$grpc.CallOptions? options,
|
||||
$core.Iterable<$grpc.ClientInterceptor>? interceptors})
|
||||
: super(channel, options: options, interceptors: interceptors);
|
||||
|
||||
$grpc.ResponseFuture<$0.EchoResponse> echo($0.EchoRequest request,
|
||||
{$grpc.CallOptions options}) {
|
||||
{$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$echo, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseStream<$0.ServerStreamingEchoResponse> serverStreamingEcho(
|
||||
$0.ServerStreamingEchoRequest request,
|
||||
{$grpc.CallOptions options}) {
|
||||
{$grpc.CallOptions? options}) {
|
||||
return $createStreamingCall(
|
||||
_$serverStreamingEcho, $async.Stream.fromIterable([request]),
|
||||
options: options);
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
// Generated code. Do not modify.
|
||||
// source: echo.proto
|
||||
//
|
||||
// @dart = 2.3
|
||||
// @dart = 2.12
|
||||
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
||||
const EchoRequest$json = const {
|
||||
'1': 'EchoRequest',
|
||||
'2': const [
|
||||
|
|
|
@ -27,12 +27,12 @@ class TestService extends Service {
|
|||
@override
|
||||
String get $name => 'Test';
|
||||
|
||||
Future<int> Function(ServiceCall call, Future<int> request) unaryHandler;
|
||||
Future<int> Function(ServiceCall call, Stream<int> request)
|
||||
Future<int> Function(ServiceCall call, Future<int> request)? unaryHandler;
|
||||
Future<int> Function(ServiceCall call, Stream<int> request)?
|
||||
clientStreamingHandler;
|
||||
Stream<int> Function(ServiceCall call, Future<int> request)
|
||||
Stream<int> Function(ServiceCall call, Future<int> request)?
|
||||
serverStreamingHandler;
|
||||
Stream<int> Function(ServiceCall call, Stream<int> request)
|
||||
Stream<int> Function(ServiceCall call, Stream<int> request)?
|
||||
bidirectionalHandler;
|
||||
|
||||
TestService() {
|
||||
|
@ -53,40 +53,40 @@ class TestService extends Service {
|
|||
if (unaryHandler == null) {
|
||||
fail('Should not invoke Unary');
|
||||
}
|
||||
return unaryHandler(call, request);
|
||||
return unaryHandler!(call, request);
|
||||
}
|
||||
|
||||
Future<int> _clientStreaming(ServiceCall call, Stream<int> request) {
|
||||
if (clientStreamingHandler == null) {
|
||||
fail('Should not invoke ClientStreaming');
|
||||
}
|
||||
return clientStreamingHandler(call, request);
|
||||
return clientStreamingHandler!(call, request);
|
||||
}
|
||||
|
||||
Stream<int> _serverStreaming(ServiceCall call, Future<int> request) {
|
||||
if (serverStreamingHandler == null) {
|
||||
fail('Should not invoke ServerStreaming');
|
||||
}
|
||||
return serverStreamingHandler(call, request);
|
||||
return serverStreamingHandler!(call, request);
|
||||
}
|
||||
|
||||
Stream<int> _bidirectional(ServiceCall call, Stream<int> request) {
|
||||
if (bidirectionalHandler == null) {
|
||||
fail('Should not invoke Bidirectional');
|
||||
}
|
||||
return bidirectionalHandler(call, request);
|
||||
return bidirectionalHandler!(call, request);
|
||||
}
|
||||
}
|
||||
|
||||
class TestInterceptor {
|
||||
Interceptor handler;
|
||||
Interceptor? handler;
|
||||
|
||||
FutureOr<GrpcError> call(ServiceCall call, ServiceMethod method) {
|
||||
FutureOr<GrpcError?> call(ServiceCall call, ServiceMethod method) {
|
||||
if (handler == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return handler(call, method);
|
||||
return handler!(call, method);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,8 @@ class TestServerStream extends ServerTransportStream {
|
|||
bool get canPush => true;
|
||||
|
||||
@override
|
||||
ServerTransportStream push(List<Header> requestHeaders) => null;
|
||||
ServerTransportStream push(List<Header> requestHeaders) =>
|
||||
throw 'unimplemented';
|
||||
}
|
||||
|
||||
class ServerHarness extends _Harness {
|
||||
|
@ -144,7 +145,7 @@ abstract class _Harness {
|
|||
final fromServer = StreamController<StreamMessage>();
|
||||
final service = TestService();
|
||||
final interceptor = TestInterceptor();
|
||||
ConnectionServer _server;
|
||||
ConnectionServer? _server;
|
||||
|
||||
ConnectionServer createServer();
|
||||
|
||||
|
@ -168,7 +169,7 @@ abstract class _Harness {
|
|||
|
||||
fromServer.stream.listen(
|
||||
expectAsync1(handleMessages, count: handlers.length),
|
||||
onError: expectAsync1((_) {}, count: 0),
|
||||
onError: expectAsync1((dynamic _) {}, count: 0),
|
||||
onDone: expectAsync0(() {}, count: 1));
|
||||
}
|
||||
|
||||
|
@ -185,8 +186,8 @@ abstract class _Harness {
|
|||
|
||||
void sendRequestHeader(String path,
|
||||
{String authority = 'test',
|
||||
Map<String, String> metadata,
|
||||
Duration timeout}) {
|
||||
Map<String, String>? metadata,
|
||||
Duration? timeout}) {
|
||||
final headers = Http2ClientConnection.createCallHeaders(
|
||||
true, authority, path, timeout, metadata, null,
|
||||
userAgent: 'dart-grpc/1.0.0 test');
|
||||
|
|
|
@ -31,10 +31,10 @@ Map<String, String> headersToMap(List<Header> headers) =>
|
|||
key: (h) => ascii.decode(h.name), value: (h) => ascii.decode(h.value));
|
||||
|
||||
void validateRequestHeaders(Map<String, String> headers,
|
||||
{String path,
|
||||
{String? path,
|
||||
String authority = 'test',
|
||||
String timeout,
|
||||
Map<String, String> customHeaders}) {
|
||||
String? timeout,
|
||||
Map<String, String>? customHeaders}) {
|
||||
expect(headers[':method'], 'POST');
|
||||
expect(headers[':scheme'], 'https');
|
||||
if (path != null) {
|
||||
|
@ -54,7 +54,7 @@ void validateRequestHeaders(Map<String, String> headers,
|
|||
void validateResponseHeaders(Map<String, String> headers,
|
||||
{int status = 200,
|
||||
bool allowTrailers = false,
|
||||
Map<String, String> customHeaders}) {
|
||||
Map<String, String>? customHeaders}) {
|
||||
expect(headers[':status'], '200');
|
||||
expect(headers['content-type'], startsWith('application/grpc'));
|
||||
if (!allowTrailers) {
|
||||
|
@ -67,7 +67,7 @@ void validateResponseHeaders(Map<String, String> headers,
|
|||
}
|
||||
|
||||
void validateResponseTrailers(Map<String, String> trailers,
|
||||
{int status = 0, String message, Map<String, String> customTrailers}) {
|
||||
{int status = 0, String? message, Map<String, String>? customTrailers}) {
|
||||
expect(trailers['grpc-status'], '$status');
|
||||
if (message != null) {
|
||||
expect(trailers['grpc-message'], message);
|
||||
|
@ -84,7 +84,7 @@ GrpcMetadata validateMetadataMessage(StreamMessage message,
|
|||
|
||||
final decoded = GrpcHttpDecoder().convert(message);
|
||||
expect(decoded, TypeMatcher<GrpcMetadata>());
|
||||
return decoded;
|
||||
return decoded as GrpcMetadata;
|
||||
}
|
||||
|
||||
GrpcData validateDataMessage(StreamMessage message, {bool endStream = false}) {
|
||||
|
@ -93,7 +93,7 @@ GrpcData validateDataMessage(StreamMessage message, {bool endStream = false}) {
|
|||
|
||||
final decoded = GrpcHttpDecoder().convert(message);
|
||||
expect(decoded, TypeMatcher<GrpcData>());
|
||||
return decoded;
|
||||
return decoded as GrpcData;
|
||||
}
|
||||
|
||||
void Function(StreamMessage message) headerValidator() {
|
||||
|
|
|
@ -22,8 +22,8 @@ import 'package:grpc/grpc.dart';
|
|||
|
||||
void main() {
|
||||
group('GrpcHttpDecoder', () {
|
||||
StreamController<StreamMessage> input;
|
||||
Stream<GrpcMessage> output;
|
||||
late StreamController<StreamMessage> input;
|
||||
late Stream<GrpcMessage> output;
|
||||
|
||||
setUp(() {
|
||||
input = StreamController();
|
||||
|
@ -51,11 +51,12 @@ void main() {
|
|||
}
|
||||
|
||||
expect(converted[0], TypeMatcher<GrpcMetadata>());
|
||||
verify(converted[1], [48, 49, 50, 51, 52, 53, 54, 55, 56, 57]);
|
||||
verify(converted[2], [97, 98, 99, 100]);
|
||||
verify(converted[3], [65]);
|
||||
verify(converted[4], [48, 49, 50, 51]);
|
||||
verify(converted[5], List.filled(256, 90));
|
||||
verify(
|
||||
converted[1] as GrpcData, [48, 49, 50, 51, 52, 53, 54, 55, 56, 57]);
|
||||
verify(converted[2] as GrpcData, [97, 98, 99, 100]);
|
||||
verify(converted[3] as GrpcData, [65]);
|
||||
verify(converted[4] as GrpcData, [48, 49, 50, 51]);
|
||||
verify(converted[5] as GrpcData, List.filled(256, 90));
|
||||
});
|
||||
|
||||
test('throws error if input is closed while receiving data header',
|
||||
|
|
|
@ -33,7 +33,7 @@ class TestClient extends Client {
|
|||
path, (int value) => [value], (List<int> value) => value[0]);
|
||||
|
||||
TestClient(api.ClientChannel channel) : super(channel);
|
||||
ResponseStream<int> stream(int request, {CallOptions options}) {
|
||||
ResponseStream<int> stream(int request, {CallOptions? options}) {
|
||||
return $createStreamingCall(_$stream, Stream.fromIterable([request]),
|
||||
options: options);
|
||||
}
|
||||
|
@ -69,12 +69,12 @@ class FakeTimelineTask extends Fake implements TimelineTask {
|
|||
static final List<Map> events = [];
|
||||
static int _idCount = 0;
|
||||
|
||||
final String filterKey;
|
||||
final TimelineTask parent;
|
||||
final String? filterKey;
|
||||
final TimelineTask? parent;
|
||||
final int id = _idCount++;
|
||||
int _startFinishCount = 0;
|
||||
|
||||
factory FakeTimelineTask({TimelineTask parent, String filterKey}) {
|
||||
factory FakeTimelineTask({TimelineTask? parent, String? filterKey}) {
|
||||
final task = FakeTimelineTask._(parent: parent, filterKey: filterKey);
|
||||
tasks.add(task);
|
||||
return task;
|
||||
|
@ -84,7 +84,7 @@ class FakeTimelineTask extends Fake implements TimelineTask {
|
|||
|
||||
bool get isComplete => _startFinishCount == 0;
|
||||
|
||||
void start(String name, {Map arguments}) {
|
||||
void start(String name, {Map? arguments}) {
|
||||
events.add({
|
||||
'id': id,
|
||||
'ph': 'b',
|
||||
|
@ -98,7 +98,7 @@ class FakeTimelineTask extends Fake implements TimelineTask {
|
|||
++_startFinishCount;
|
||||
}
|
||||
|
||||
void instant(String name, {Map arguments}) {
|
||||
void instant(String name, {Map? arguments}) {
|
||||
events.add({
|
||||
'id': id,
|
||||
'ph': 'i',
|
||||
|
@ -110,7 +110,7 @@ class FakeTimelineTask extends Fake implements TimelineTask {
|
|||
});
|
||||
}
|
||||
|
||||
void finish({Map arguments}) {
|
||||
void finish({Map? arguments}) {
|
||||
events.add({
|
||||
'id': id,
|
||||
'ph': 'e',
|
||||
|
@ -124,7 +124,8 @@ class FakeTimelineTask extends Fake implements TimelineTask {
|
|||
}
|
||||
}
|
||||
|
||||
TimelineTask fakeTimelineTaskFactory({String filterKey, TimelineTask parent}) =>
|
||||
TimelineTask fakeTimelineTaskFactory(
|
||||
{String? filterKey, TimelineTask? parent}) =>
|
||||
FakeTimelineTask(filterKey: filterKey, parent: parent);
|
||||
|
||||
testee() async {
|
||||
|
@ -134,7 +135,7 @@ testee() async {
|
|||
timelineTaskFactory = fakeTimelineTaskFactory;
|
||||
final channel = FixedConnectionClientChannel(Http2ClientConnection(
|
||||
'localhost',
|
||||
server.port,
|
||||
server.port!,
|
||||
ChannelOptions(credentials: ChannelCredentials.insecure()),
|
||||
));
|
||||
final testClient = TestClient(channel);
|
||||
|
|
|
@ -28,7 +28,6 @@ void main() {
|
|||
|
||||
group('Unit:', () {
|
||||
test('Timeouts are converted correctly to header string', () {
|
||||
expect(toTimeoutString(null), isNull);
|
||||
expect(toTimeoutString(Duration(microseconds: -1)), '1n');
|
||||
expect(toTimeoutString(Duration(microseconds: 0)), '0u');
|
||||
expect(toTimeoutString(Duration(microseconds: 107)), '107u');
|
||||
|
@ -61,7 +60,7 @@ void main() {
|
|||
});
|
||||
|
||||
group('Client:', () {
|
||||
ClientHarness harness;
|
||||
late ClientHarness harness;
|
||||
|
||||
setUp(() {
|
||||
harness = ClientHarness()..setUp();
|
||||
|
@ -101,7 +100,7 @@ void main() {
|
|||
});
|
||||
|
||||
group('Server:', () {
|
||||
ServerHarness harness;
|
||||
late ServerHarness harness;
|
||||
|
||||
setUp(() {
|
||||
harness = ServerHarness()..setUp();
|
||||
|
@ -112,6 +111,7 @@ void main() {
|
|||
});
|
||||
|
||||
test('Calls time out if deadline is exceeded', () async {
|
||||
final handlerFinished = Completer<void>();
|
||||
Future<int> methodHandler(ServiceCall call, Future<int> request) async {
|
||||
try {
|
||||
expect(call.isTimedOut, isFalse);
|
||||
|
@ -126,6 +126,8 @@ void main() {
|
|||
fail('Did not throw');
|
||||
} catch (error, stack) {
|
||||
registerException(error, stack);
|
||||
} finally {
|
||||
handlerFinished.complete();
|
||||
}
|
||||
return dummyValue;
|
||||
}
|
||||
|
@ -134,7 +136,7 @@ void main() {
|
|||
..service.unaryHandler = methodHandler
|
||||
..expectErrorResponse(StatusCode.deadlineExceeded, 'Deadline exceeded')
|
||||
..sendRequestHeader('/Test/Unary', timeout: Duration(microseconds: 1));
|
||||
await harness.fromServer.done;
|
||||
await Future.wait([handlerFinished.future, harness.fromServer.done]);
|
||||
});
|
||||
}, testOn: 'vm');
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue