Migrate grpc to null safety (#432)

This commit is contained in:
Ivan Inozemtsev 2021-01-29 12:40:52 +01:00 committed by GitHub
parent d3f0ec7f37
commit 1e1831a187
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
73 changed files with 1121 additions and 676 deletions

View File

@ -11,12 +11,12 @@ on:
jobs: jobs:
# Check code formating and static analysis on a single OS (linux) # Check code formating and static analysis on a single OS (linux)
# against Dart stable and dev. # against Dart dev.
analyze: analyze:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
sdk: [stable, dev] sdk: [dev]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: cedx/setup-dart@v2 - uses: cedx/setup-dart@v2
@ -43,7 +43,7 @@ jobs:
# Run tests on a matrix consisting of three dimensions: # Run tests on a matrix consisting of three dimensions:
# 1. OS: mac, windows, linux # 1. OS: mac, windows, linux
# 2. release channel: stable, dev # 2. release channel: dev
# 3. TODO: Dart execution mode: native, web # 3. TODO: Dart execution mode: native, web
test: test:
needs: analyze needs: analyze
@ -51,7 +51,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, macos-latest, windows-latest]
sdk: [stable, dev] sdk: [dev]
platform: [vm, chrome] platform: [vm, chrome]
exclude: exclude:
# We only run Chrome tests on Linux. No need to run them # We only run Chrome tests on Linux. No need to run them

View File

@ -1,3 +1,7 @@
## 3.0.0-nullsafety.0
* Migrate library and tests to null safety.
## 2.9.0 ## 2.9.0
* Added support for compression/decompression, which can be configured through * Added support for compression/decompression, which can be configured through

View File

@ -9,7 +9,7 @@ dependencies:
async: ^2.2.0 async: ^2.2.0
grpc: grpc:
path: ../../ path: ../../
protobuf: ^1.0.1 protobuf: ^2.0.0-nullsafety
dev_dependencies: dev_dependencies:
test: ^1.6.4 test: ^1.6.4

View File

@ -8,8 +8,7 @@ environment:
dependencies: dependencies:
grpc: grpc:
path: ../../ path: ../../
protobuf: ^1.0.1 protobuf: ^2.0.0-nullsafety
dev_dependencies: dev_dependencies:
build_runner: ^1.5.2 build_runner: ^1.5.2
build_web_compilers: ^2.1.1

View File

@ -9,4 +9,4 @@ dependencies:
async: ^2.2.0 async: ^2.2.0
grpc: grpc:
path: ../../ path: ../../
protobuf: ^1.0.1 protobuf: ^2.0.0-nullsafety

View File

@ -9,7 +9,7 @@ dependencies:
async: ^2.2.0 async: ^2.2.0
grpc: grpc:
path: ../../ path: ../../
protobuf: ^1.0.1 protobuf: ^2.0.0-nullsafety
dev_dependencies: dev_dependencies:
test: ^1.6.0 test: ^1.6.0

View File

@ -9,4 +9,4 @@ dependencies:
async: ^2.2.0 async: ^2.2.0
grpc: grpc:
path: ../../ path: ../../
protobuf: ^1.0.1 protobuf: ^2.0.0-nullsafety

View File

@ -11,7 +11,7 @@ dependencies:
collection: ^1.14.11 collection: ^1.14.11
grpc: grpc:
path: ../ path: ../
protobuf: ^1.0.1 protobuf: ^2.0.0-nullsafety
dev_dependencies: dev_dependencies:
test: ^1.6.4 test: ^1.6.4

View File

@ -20,25 +20,26 @@ import 'package:googleapis_auth/auth.dart' as auth;
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import '../client/call.dart'; import '../client/call.dart';
import '../shared/status.dart';
import 'rsa.dart'; import 'rsa.dart';
const _tokenExpirationThreshold = Duration(seconds: 30); const _tokenExpirationThreshold = Duration(seconds: 30);
abstract class BaseAuthenticator { abstract class BaseAuthenticator {
auth.AccessToken _accessToken; auth.AccessToken? _accessToken;
String _lastUri; late String _lastUri;
var _lastUriSet = false;
Future<void> authenticate(Map<String, String> metadata, String uri) async { Future<void> authenticate(Map<String, String> metadata, String uri) async {
if (uri == null) { if (_accessToken == null ||
throw GrpcError.unauthenticated('Credentials require secure transport.'); _accessToken!.hasExpired ||
} !_lastUriSet ||
if (_accessToken == null || _accessToken.hasExpired || uri != _lastUri) { uri != _lastUri) {
await obtainAccessCredentials(uri); await obtainAccessCredentials(uri);
_lastUri = uri; _lastUri = uri;
_lastUriSet = true;
} }
final auth = '${_accessToken.type} ${_accessToken.data}'; final auth = '${_accessToken!.type} ${_accessToken!.data}';
metadata['authorization'] = auth; metadata['authorization'] = auth;
if (_tokenExpiresSoon) { if (_tokenExpiresSoon) {
@ -47,7 +48,7 @@ abstract class BaseAuthenticator {
} }
} }
bool get _tokenExpiresSoon => _accessToken.expiry bool get _tokenExpiresSoon => _accessToken!.expiry
.subtract(_tokenExpirationThreshold) .subtract(_tokenExpirationThreshold)
.isBefore(DateTime.now().toUtc()); .isBefore(DateTime.now().toUtc());
@ -57,7 +58,7 @@ abstract class BaseAuthenticator {
} }
abstract class HttpBasedAuthenticator extends BaseAuthenticator { abstract class HttpBasedAuthenticator extends BaseAuthenticator {
Future<void> _call; Future<void>? _call;
Future<void> obtainAccessCredentials(String uri) { Future<void> obtainAccessCredentials(String uri) {
if (_call == null) { if (_call == null) {
@ -68,7 +69,7 @@ abstract class HttpBasedAuthenticator extends BaseAuthenticator {
authClient.close(); authClient.close();
}); });
} }
return _call; return _call!;
} }
Future<auth.AccessCredentials> obtainCredentialsWithClient( Future<auth.AccessCredentials> obtainCredentialsWithClient(
@ -77,18 +78,21 @@ abstract class HttpBasedAuthenticator extends BaseAuthenticator {
class JwtServiceAccountAuthenticator extends BaseAuthenticator { class JwtServiceAccountAuthenticator extends BaseAuthenticator {
auth.ServiceAccountCredentials _serviceAccountCredentials; auth.ServiceAccountCredentials _serviceAccountCredentials;
String _projectId; String? _projectId;
String _keyId; String? _keyId;
JwtServiceAccountAuthenticator(String serviceAccountJson) { JwtServiceAccountAuthenticator.fromJson(
final serviceAccount = jsonDecode(serviceAccountJson); Map<String, dynamic> serviceAccountJson)
_serviceAccountCredentials = : _serviceAccountCredentials =
auth.ServiceAccountCredentials.fromJson(serviceAccount); auth.ServiceAccountCredentials.fromJson(serviceAccountJson),
_projectId = serviceAccount['project_id']; _projectId = serviceAccountJson['project_id'],
_keyId = serviceAccount['private_key_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 { Future<void> obtainAccessCredentials(String uri) async {
_accessToken = _jwtTokenFor(_serviceAccountCredentials, _keyId, uri); _accessToken = _jwtTokenFor(_serviceAccountCredentials, _keyId, uri);
@ -97,8 +101,8 @@ class JwtServiceAccountAuthenticator extends BaseAuthenticator {
// TODO(jakobr): Expose in googleapis_auth. // TODO(jakobr): Expose in googleapis_auth.
auth.AccessToken _jwtTokenFor( auth.AccessToken _jwtTokenFor(
auth.ServiceAccountCredentials credentials, String keyId, String uri, auth.ServiceAccountCredentials credentials, String? keyId, String uri,
{String user, List<String> scopes}) { {String? user, List<String>? scopes}) {
// Subtracting 20 seconds from current timestamp to allow for clock skew among // Subtracting 20 seconds from current timestamp to allow for clock skew among
// servers. // servers.
final timestamp = final timestamp =

View File

@ -13,18 +13,22 @@ class ComputeEngineAuthenticator extends HttpBasedAuthenticator {
} }
class ServiceAccountAuthenticator extends HttpBasedAuthenticator { class ServiceAccountAuthenticator extends HttpBasedAuthenticator {
auth.ServiceAccountCredentials _serviceAccountCredentials; late auth.ServiceAccountCredentials _serviceAccountCredentials;
final List<String> _scopes; final List<String> _scopes;
String _projectId; String? _projectId;
ServiceAccountAuthenticator(String serviceAccountJson, this._scopes) { ServiceAccountAuthenticator.fromJson(
final serviceAccount = jsonDecode(serviceAccountJson); Map<String, dynamic> serviceAccountJson, this._scopes)
_serviceAccountCredentials = : _serviceAccountCredentials =
auth.ServiceAccountCredentials.fromJson(serviceAccount); auth.ServiceAccountCredentials.fromJson(serviceAccountJson),
_projectId = serviceAccount['project_id']; _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( Future<auth.AccessCredentials> obtainCredentialsWithClient(
http.Client client, String uri) => http.Client client, String uri) =>
@ -35,7 +39,7 @@ class ServiceAccountAuthenticator extends HttpBasedAuthenticator {
class _CredentialsRefreshingAuthenticator extends HttpBasedAuthenticator { class _CredentialsRefreshingAuthenticator extends HttpBasedAuthenticator {
final auth.ClientId _clientId; final auth.ClientId _clientId;
auth.AccessCredentials _accessCredentials; auth.AccessCredentials _accessCredentials;
final String _quotaProject; final String? _quotaProject;
_CredentialsRefreshingAuthenticator( _CredentialsRefreshingAuthenticator(
this._clientId, this._clientId,
@ -47,7 +51,7 @@ class _CredentialsRefreshingAuthenticator extends HttpBasedAuthenticator {
await super.authenticate(metadata, uri); await super.authenticate(metadata, uri);
if (_quotaProject != null) { if (_quotaProject != null) {
// https://cloud.google.com/apis/docs/system-parameters#definitions // 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( Future<HttpBasedAuthenticator> applicationDefaultCredentialsAuthenticator(
List<String> scopes, List<String> scopes,
) async { ) async {
File credFile; File? credFile;
String fileSource; String? fileSource;
// If env var specifies a file to load credentials from we'll do that. // If env var specifies a file to load credentials from we'll do that.
final credsEnv = Platform.environment['GOOGLE_APPLICATION_CREDENTIALS']; final credsEnv = Platform.environment['GOOGLE_APPLICATION_CREDENTIALS'];
if (credsEnv != null && credsEnv.isNotEmpty) { if (credsEnv != null && credsEnv.isNotEmpty) {
@ -97,10 +101,10 @@ Future<HttpBasedAuthenticator> applicationDefaultCredentialsAuthenticator(
// Attempt to use file created by `gcloud auth application-default login` // Attempt to use file created by `gcloud auth application-default login`
File gcloudAdcFile; File gcloudAdcFile;
if (Platform.isWindows) { 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')); .resolve('gcloud/application_default_credentials.json'));
} else { } else {
gcloudAdcFile = File.fromUri(Uri.directory(Platform.environment['HOME']) gcloudAdcFile = File.fromUri(Uri.directory(Platform.environment['HOME']!)
.resolve('.config/gcloud/application_default_credentials.json')); .resolve('.config/gcloud/application_default_credentials.json'));
} }
// Only try to load from gcloudAdcFile if it exists. // Only try to load from gcloudAdcFile if it exists.

View File

@ -99,7 +99,7 @@ class ASN1Parser {
static const SEQUENCE_TAG = 0x30; static const SEQUENCE_TAG = 0x30;
static ASN1Object parse(Uint8List bytes) { static ASN1Object parse(Uint8List bytes) {
invalidFormat(String msg) { Never invalidFormat(String msg) {
throw new ArgumentError("Invalid DER encoding: $msg"); throw new ArgumentError("Invalid DER encoding: $msg");
} }
@ -185,8 +185,6 @@ class ASN1Parser {
invalidFormat( invalidFormat(
'Unexpected tag $tag at offset ${offset - 1} (end: $end).'); 'Unexpected tag $tag at offset ${offset - 1} (end: $end).');
} }
// Unreachable.
return null;
} }
final obj = decodeObject(); final obj = decodeObject();

View File

@ -38,6 +38,7 @@ const _reservedHeaders = [
'grpc-encoding', 'grpc-encoding',
'user-agent', 'user-agent',
]; ];
const _statusDetailsHeader = 'grpc-status-details-bin';
/// Provides per-RPC metadata. /// Provides per-RPC metadata.
/// ///
@ -55,9 +56,9 @@ typedef FutureOr<void> MetadataProvider(
/// Runtime options for an RPC. /// Runtime options for an RPC.
class CallOptions { class CallOptions {
final Map<String, String> metadata; final Map<String, String> metadata;
final Duration timeout; final Duration? timeout;
final List<MetadataProvider> metadataProviders; final List<MetadataProvider> metadataProviders;
final Codec compression; final Codec? compression;
CallOptions._( CallOptions._(
this.metadata, this.metadata,
@ -73,10 +74,10 @@ class CallOptions {
/// invoked in order for every RPC, and can modify the outgoing metadata /// invoked in order for every RPC, and can modify the outgoing metadata
/// (including metadata provided by previous providers). /// (including metadata provided by previous providers).
factory CallOptions({ factory CallOptions({
Map<String, String> metadata, Map<String, String>? metadata,
Duration timeout, Duration? timeout,
List<MetadataProvider> providers, List<MetadataProvider>? providers,
Codec compression, Codec? compression,
}) { }) {
return CallOptions._( return CallOptions._(
Map.unmodifiable(metadata ?? {}), Map.unmodifiable(metadata ?? {}),
@ -89,7 +90,7 @@ class CallOptions {
factory CallOptions.from(Iterable<CallOptions> options) => factory CallOptions.from(Iterable<CallOptions> options) =>
options.fold(CallOptions(), (p, o) => p.mergedWith(o)); options.fold(CallOptions(), (p, o) => p.mergedWith(o));
CallOptions mergedWith(CallOptions other) { CallOptions mergedWith(CallOptions? other) {
if (other == null) return this; if (other == null) return this;
final mergedMetadata = Map.from(metadata)..addAll(other.metadata); final mergedMetadata = Map.from(metadata)..addAll(other.metadata);
final mergedTimeout = other.timeout ?? timeout; 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 /// For this to work correctly, a proxy server must be set up that
/// understands the query parameter and can unpack/send the original /// understands the query parameter and can unpack/send the original
/// list of headers to the server endpoint. /// list of headers to the server endpoint.
final bool bypassCorsPreflight; final bool? bypassCorsPreflight;
/// Whether to send credentials along with the XHR. /// Whether to send credentials along with the XHR.
/// ///
/// This may be required for proxying or wherever the server /// This may be required for proxying or wherever the server
/// needs to otherwise inspect client cookies for that domain. /// 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. // 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, List<MetadataProvider> metadataProviders,
{this.bypassCorsPreflight, this.withCredentials}) {this.bypassCorsPreflight, this.withCredentials})
: super._(metadata, timeout, metadataProviders, null); : super._(metadata, timeout, metadataProviders, null);
@ -136,11 +137,11 @@ class WebCallOptions extends CallOptions {
/// metadata [providers] of [CallOptions], [bypassCorsPreflight] and /// metadata [providers] of [CallOptions], [bypassCorsPreflight] and
/// [withCredentials] for CORS request. /// [withCredentials] for CORS request.
factory WebCallOptions( factory WebCallOptions(
{Map<String, String> metadata, {Map<String, String>? metadata,
Duration timeout, Duration? timeout,
List<MetadataProvider> providers, List<MetadataProvider>? providers,
bool bypassCorsPreflight, bool? bypassCorsPreflight,
bool withCredentials}) { bool? withCredentials}) {
return WebCallOptions._(Map.unmodifiable(metadata ?? {}), timeout, return WebCallOptions._(Map.unmodifiable(metadata ?? {}), timeout,
List.unmodifiable(providers ?? []), List.unmodifiable(providers ?? []),
bypassCorsPreflight: bypassCorsPreflight ?? false, bypassCorsPreflight: bypassCorsPreflight ?? false,
@ -148,7 +149,7 @@ class WebCallOptions extends CallOptions {
} }
@override @override
CallOptions mergedWith(CallOptions other) { CallOptions mergedWith(CallOptions? other) {
if (other == null) return this; if (other == null) return this;
final mergedMetadata = Map.from(metadata)..addAll(other.metadata); final mergedMetadata = Map.from(metadata)..addAll(other.metadata);
@ -163,11 +164,9 @@ class WebCallOptions extends CallOptions {
withCredentials: withCredentials); withCredentials: withCredentials);
} }
final otherOptions = other as WebCallOptions;
final mergedBypassCorsPreflight = final mergedBypassCorsPreflight =
otherOptions.bypassCorsPreflight ?? bypassCorsPreflight; other.bypassCorsPreflight ?? bypassCorsPreflight;
final mergedWithCredentials = final mergedWithCredentials = other.withCredentials ?? withCredentials;
otherOptions.withCredentials ?? withCredentials;
return WebCallOptions._(Map.unmodifiable(mergedMetadata), mergedTimeout, return WebCallOptions._(Map.unmodifiable(mergedMetadata), mergedTimeout,
List.unmodifiable(mergedProviders), List.unmodifiable(mergedProviders),
bypassCorsPreflight: mergedBypassCorsPreflight, bypassCorsPreflight: mergedBypassCorsPreflight,
@ -185,28 +184,28 @@ class ClientCall<Q, R> implements Response {
final _trailers = Completer<Map<String, String>>(); final _trailers = Completer<Map<String, String>>();
bool _hasReceivedResponses = false; bool _hasReceivedResponses = false;
Map<String, String> _headerMetadata; late Map<String, String> _headerMetadata;
GrpcTransportStream _stream; GrpcTransportStream? _stream;
StreamController<R> _responses; final _responses = StreamController<R>();
StreamSubscription<List<int>> _requestSubscription; StreamSubscription<List<int>>? _requestSubscription;
StreamSubscription<GrpcMessage> _responseSubscription; StreamSubscription<GrpcMessage>? _responseSubscription;
bool isCancelled = false; bool isCancelled = false;
Timer _timeoutTimer; Timer? _timeoutTimer;
final TimelineTask _requestTimeline; final TimelineTask? _requestTimeline;
TimelineTask _responseTimeline; TimelineTask? _responseTimeline;
ClientCall(this._method, this._requests, this.options, ClientCall(this._method, this._requests, this.options,
[this._requestTimeline]) { [this._requestTimeline]) {
_requestTimeline?.start('gRPC Request: ${_method.path}', arguments: { _requestTimeline?.start('gRPC Request: ${_method.path}', arguments: {
'method': _method.path, 'method': _method.path,
'timeout': options?.timeout?.toString(), 'timeout': options.timeout?.toString(),
}); });
_responses = StreamController(onListen: _onResponseListen); _responses.onListen = _onResponseListen;
if (options.timeout != null) { 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); final metadata = Map<String, String>.from(options.metadata);
Future.forEach( Future.forEach(
options.metadataProviders, options.metadataProviders,
(provider) => provider(metadata, (MetadataProvider provider) => provider(metadata,
'${connection.scheme}://${connection.authority}${audiencePath(_method)}')) '${connection.scheme}://${connection.authority}${audiencePath(_method)}'))
.then((_) => _sendRequest(connection, _sanitizeMetadata(metadata))) .then((_) => _sendRequest(connection, _sanitizeMetadata(metadata)))
.catchError(_onMetadataProviderError); .catchError(_onMetadataProviderError);
@ -263,8 +262,9 @@ class ClientCall<Q, R> implements Response {
} }
void _sendRequest(ClientConnection connection, Map<String, String> metadata) { void _sendRequest(ClientConnection connection, Map<String, String> metadata) {
late final GrpcTransportStream stream;
try { try {
_stream = connection.makeRequest( stream = connection.makeRequest(
_method.path, _method.path,
options.timeout, options.timeout,
metadata, metadata,
@ -287,16 +287,17 @@ class ClientCall<Q, R> implements Response {
return _method.requestSerializer(data); return _method.requestSerializer(data);
}) })
.handleError(_onRequestError) .handleError(_onRequestError)
.listen(_stream.outgoingMessages.add, .listen(stream.outgoingMessages.add,
onError: _stream.outgoingMessages.addError, onError: stream.outgoingMessages.addError,
onDone: _stream.outgoingMessages.close, onDone: stream.outgoingMessages.close,
cancelOnError: true); cancelOnError: true);
_stream = stream;
// The response stream might have been listened to before _stream was ready, // The response stream might have been listened to before _stream was ready,
// so try setting up the subscription here as well. // so try setting up the subscription here as well.
_onResponseListen(); _onResponseListen();
} }
void _finishTimelineWithError(GrpcError error, TimelineTask timeline) { void _finishTimelineWithError(GrpcError error, TimelineTask? timeline) {
timeline?.finish(arguments: { timeline?.finish(arguments: {
'error': error.toString(), 'error': error.toString(),
}); });
@ -315,28 +316,31 @@ class ClientCall<Q, R> implements Response {
if (_stream != null && if (_stream != null &&
_responses.hasListener && _responses.hasListener &&
_responseSubscription == null) { _responseSubscription == null) {
_responseSubscription = _stream.incomingMessages.listen(_onResponseData, // ignore: cancel_subscriptions
final subscription = _stream!.incomingMessages.listen(_onResponseData,
onError: _onResponseError, onError: _onResponseError,
onDone: _onResponseDone, onDone: _onResponseDone,
cancelOnError: true); cancelOnError: true);
if (_responses.isPaused) { if (_responses.isPaused) {
_responseSubscription.pause(); subscription.pause();
} }
_responses.onPause = _responseSubscription.pause; _responses.onPause = subscription.pause;
_responses.onResume = _responseSubscription.resume; _responses.onResume = subscription.resume;
_responses.onCancel = cancel; _responses.onCancel = cancel;
_responseSubscription = subscription;
} }
} }
/// Emit an error response to the user, and tear down this call. /// 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); _finishTimelineWithError(error, _responseTimeline);
_responses.addError(error, stackTrace); _responses.addError(error, stackTrace);
_timeoutTimer?.cancel(); _timeoutTimer?.cancel();
_requestSubscription?.cancel(); _requestSubscription?.cancel();
_responseSubscription.cancel(); _responseSubscription!.cancel();
_responses.close(); _responses.close();
_stream.terminate(); _stream!.terminate();
} }
/// If there's an error status then process it as a response error /// 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'); final statusCode = int.parse(status ?? '0');
if (statusCode != 0) { if (statusCode != 0) {
final message = metadata['grpc-message'] == null final messageMetadata = metadata['grpc-message'];
? null final message =
: Uri.decodeFull(metadata['grpc-message']); messageMetadata == null ? null : Uri.decodeFull(messageMetadata);
final statusDetails = metadata[_statusDetailsHeader];
_responseError(GrpcError.custom( _responseError(GrpcError.custom(
statusCode, statusCode,
message, message,
decodeStatusDetails(metadata['grpc-status-details-bin']), statusDetails == null
)); ? const <GeneratedMessage>[]
: decodeStatusDetails(statusDetails)));
} }
} }
@ -444,7 +450,7 @@ class ClientCall<Q, R> implements Response {
_responseTimeline?.finish(); _responseTimeline?.finish();
_timeoutTimer?.cancel(); _timeoutTimer?.cancel();
_responses.close(); _responses.close();
_responseSubscription.cancel(); _responseSubscription!.cancel();
} }
/// Error handler for the requests stream. Something went wrong while trying /// Error handler for the requests stream. Something went wrong while trying
@ -461,7 +467,7 @@ class ClientCall<Q, R> implements Response {
_responses.close(); _responses.close();
_requestSubscription?.cancel(); _requestSubscription?.cancel();
_responseSubscription?.cancel(); _responseSubscription?.cancel();
_stream.terminate(); _stream!.terminate();
} }
Stream<R> get response => _responses.stream; Stream<R> get response => _responses.stream;
@ -492,10 +498,10 @@ class ClientCall<Q, R> implements Response {
_stream?.terminate(); _stream?.terminate();
final futures = <Future>[]; final futures = <Future>[];
if (_requestSubscription != null) { if (_requestSubscription != null) {
futures.add(_requestSubscription.cancel()); futures.add(_requestSubscription!.cancel());
} }
if (_responseSubscription != null) { if (_responseSubscription != null) {
futures.add(_responseSubscription.cancel()); futures.add(_responseSubscription!.cancel());
} }
await Future.wait(futures); await Future.wait(futures);
} }

View File

@ -44,8 +44,8 @@ abstract class ClientChannel {
/// Auxiliary base class implementing much of ClientChannel. /// Auxiliary base class implementing much of ClientChannel.
abstract class ClientChannelBase implements ClientChannel { abstract class ClientChannelBase implements ClientChannel {
// TODO(jakobr): Multiple connections, load balancing. // TODO(jakobr): Multiple connections, load balancing.
ClientConnection _connection; late ClientConnection _connection;
var _connected = false;
bool _isShutdown = false; bool _isShutdown = false;
ClientChannelBase(); ClientChannelBase();
@ -54,13 +54,17 @@ abstract class ClientChannelBase implements ClientChannel {
Future<void> shutdown() async { Future<void> shutdown() async {
if (_isShutdown) return; if (_isShutdown) return;
_isShutdown = true; _isShutdown = true;
if (_connection != null) await _connection.shutdown(); if (_connected) {
await _connection.shutdown();
}
} }
@override @override
Future<void> terminate() async { Future<void> terminate() async {
_isShutdown = true; _isShutdown = true;
if (_connection != null) await _connection.terminate(); if (_connected) {
await _connection.terminate();
}
} }
ClientConnection createConnection(); ClientConnection createConnection();
@ -70,7 +74,11 @@ abstract class ClientChannelBase implements ClientChannel {
/// The connection may be shared between multiple RPCs. /// The connection may be shared between multiple RPCs.
Future<ClientConnection> getConnection() async { Future<ClientConnection> getConnection() async {
if (_isShutdown) throw GrpcError.unavailable('Channel shutting down.'); if (_isShutdown) throw GrpcError.unavailable('Channel shutting down.');
return _connection ??= createConnection(); if (!_connected) {
_connection = createConnection();
_connected = true;
}
return _connection;
} }
@override @override

View File

@ -27,7 +27,7 @@ class Client {
/// Interceptors will be applied in direct order before making a request. /// Interceptors will be applied in direct order before making a request.
Client(this._channel, Client(this._channel,
{CallOptions options, Iterable<ClientInterceptor> interceptors}) {CallOptions? options, Iterable<ClientInterceptor>? interceptors})
: _options = options ?? CallOptions(), : _options = options ?? CallOptions(),
_interceptors = List.unmodifiable(interceptors ?? Iterable.empty()); _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>( ClientCall<Q, R> $createCall<Q, R>(
ClientMethod<Q, R> method, Stream<Q> requests, ClientMethod<Q, R> method, Stream<Q> requests,
{CallOptions options}) { {CallOptions? options}) {
return _channel.createCall(method, requests, _options.mergedWith(options)); return _channel.createCall(method, requests, _options.mergedWith(options));
} }
ResponseFuture<R> $createUnaryCall<Q, R>(ClientMethod<Q, R> method, Q request, ResponseFuture<R> $createUnaryCall<Q, R>(ClientMethod<Q, R> method, Q request,
{CallOptions options}) { {CallOptions? options}) {
ClientUnaryInvoker<Q, R> invoker = (method, request, options) => ClientUnaryInvoker<Q, R> invoker = (method, request, options) =>
ResponseFuture<R>( ResponseFuture<R>(
_channel.createCall<Q, R>(method, Stream.value(request), options)); _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>( ResponseStream<R> $createStreamingCall<Q, R>(
ClientMethod<Q, R> method, Stream<Q> requests, ClientMethod<Q, R> method, Stream<Q> requests,
{CallOptions options}) { {CallOptions? options}) {
ClientStreamingInvoker<Q, R> invoker = (method, request, options) => ClientStreamingInvoker<Q, R> invoker = (method, request, options) =>
ResponseStream<R>(_channel.createCall<Q, R>(method, requests, options)); ResponseStream<R>(_channel.createCall<Q, R>(method, requests, options));

View File

@ -46,21 +46,21 @@ class ResponseFuture<R> extends DelegatingFuture<R>
with _ResponseMixin<dynamic, R> { with _ResponseMixin<dynamic, R> {
final ClientCall<dynamic, R> _call; final ClientCall<dynamic, R> _call;
static R _ensureOnlyOneResponse<R>(R previous, R element) { static R _ensureOnlyOneResponse<R>(R? previous, R element) {
if (previous != null) { if (previous != null) {
throw GrpcError.unimplemented('More than one response received'); throw GrpcError.unimplemented('More than one response received');
} }
return element; return element;
} }
static R _ensureOneResponse<R>(R value) { static R _ensureOneResponse<R>(R? value) {
if (value == null) throw GrpcError.unimplemented('No responses received'); if (value == null) throw GrpcError.unimplemented('No responses received');
return value; return value;
} }
ResponseFuture(this._call) ResponseFuture(this._call)
: super(_call.response : super(_call.response
.fold(null, _ensureOnlyOneResponse) .fold<R?>(null, _ensureOnlyOneResponse)
.then(_ensureOneResponse)); .then(_ensureOneResponse));
} }

View File

@ -42,9 +42,9 @@ abstract class ClientConnection {
void dispatchCall(ClientCall call); void dispatchCall(ClientCall call);
/// Start a request for [path] with [metadata]. /// 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, Map<String, String> metadata, ErrorHandler onRequestFailure,
{CallOptions callOptions}); {required CallOptions callOptions});
/// Shuts down this connection. /// Shuts down this connection.
/// ///

View File

@ -44,19 +44,19 @@ class Http2ClientConnection implements connection.ClientConnection {
connection.ConnectionState _state = ConnectionState.idle; connection.ConnectionState _state = ConnectionState.idle;
@visibleForTesting @visibleForTesting
void Function(Http2ClientConnection connection) onStateChanged; void Function(Http2ClientConnection connection)? onStateChanged;
final _pendingCalls = <ClientCall>[]; final _pendingCalls = <ClientCall>[];
final ClientTransportConnector _transportConnector; final ClientTransportConnector _transportConnector;
ClientTransportConnection _transportConnection; ClientTransportConnection? _transportConnection;
/// Used for idle and reconnect timeout, depending on [_state]. /// 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. /// Used for making sure a single connection is not kept alive too long.
final Stopwatch _connectionLifeTimer = Stopwatch(); final Stopwatch _connectionLifeTimer = Stopwatch();
Duration _currentReconnectDelay; Duration? _currentReconnectDelay;
Http2ClientConnection(Object host, int port, this.options) Http2ClientConnection(Object host, int port, this.options)
: _transportConnector = _SocketTransportConnector(host, port, options); : _transportConnector = _SocketTransportConnector(host, port, options);
@ -96,7 +96,7 @@ class Http2ClientConnection implements connection.ClientConnection {
return; return;
} }
_setState(ConnectionState.connecting); _setState(ConnectionState.connecting);
connectTransport().then((transport) async { connectTransport().then<void>((transport) async {
_currentReconnectDelay = null; _currentReconnectDelay = null;
_transportConnection = transport; _transportConnection = transport;
_connectionLifeTimer _connectionLifeTimer
@ -119,11 +119,11 @@ class Http2ClientConnection implements connection.ClientConnection {
/// ///
/// Assumes [_transportConnection] is not `null`. /// Assumes [_transportConnection] is not `null`.
void _refreshConnectionIfUnhealthy() { void _refreshConnectionIfUnhealthy() {
final bool isHealthy = _transportConnection.isOpen; final bool isHealthy = _transportConnection!.isOpen;
final bool shouldRefresh = final bool shouldRefresh =
_connectionLifeTimer.elapsed > options.connectionTimeout; _connectionLifeTimer.elapsed > options.connectionTimeout;
if (shouldRefresh) { if (shouldRefresh) {
_transportConnection.finish(); _transportConnection!.finish();
} }
if (!isHealthy || shouldRefresh) { if (!isHealthy || shouldRefresh) {
_abandonConnection(); _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, Map<String, String> metadata, ErrorHandler onRequestFailure,
{CallOptions callOptions}) { {CallOptions? callOptions}) {
final compressionCodec = callOptions?.compression; final compressionCodec = callOptions?.compression;
final headers = createCallHeaders( final headers = createCallHeaders(
credentials.isSecure, credentials.isSecure,
@ -165,7 +165,7 @@ class Http2ClientConnection implements connection.ClientConnection {
(callOptions?.metadata ?? const {})['grpc-accept-encoding'] ?? (callOptions?.metadata ?? const {})['grpc-accept-encoding'] ??
options.codecRegistry?.supportedEncodings, options.codecRegistry?.supportedEncodings,
); );
final stream = _transportConnection.makeRequest(headers); final stream = _transportConnection!.makeRequest(headers);
return Http2TransportStream( return Http2TransportStream(
stream, stream,
onRequestFailure, onRequestFailure,
@ -208,9 +208,7 @@ class Http2ClientConnection implements connection.ClientConnection {
void _setState(ConnectionState state) { void _setState(ConnectionState state) {
_state = state; _state = state;
if (onStateChanged != null) { onStateChanged?.call(this);
onStateChanged(this);
}
} }
void _handleIdleTimeout() { void _handleIdleTimeout() {
@ -218,7 +216,7 @@ class Http2ClientConnection implements connection.ClientConnection {
_cancelTimer(); _cancelTimer();
_transportConnection _transportConnection
?.finish() ?.finish()
?.catchError((_) => {}); // TODO(jakobr): Log error. .catchError((_) {}); // TODO(jakobr): Log error.
_transportConnection = null; _transportConnection = null;
_setState(ConnectionState.idle); _setState(ConnectionState.idle);
} }
@ -233,7 +231,7 @@ class Http2ClientConnection implements connection.ClientConnection {
_cancelTimer(); _cancelTimer();
} else { } else {
if (options.idleTimeout != null) { 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. // We have pending RPCs. Reconnect after backoff delay.
_setState(ConnectionState.transientFailure); _setState(ConnectionState.transientFailure);
_currentReconnectDelay = options.backoffStrategy(_currentReconnectDelay); _currentReconnectDelay = options.backoffStrategy(_currentReconnectDelay);
_timer = Timer(_currentReconnectDelay, _handleReconnect); _timer = Timer(_currentReconnectDelay!, _handleReconnect);
} }
static List<Header> createCallHeaders( static List<Header> createCallHeaders(
bool useTls, bool useTls,
String authority, String authority,
String path, String path,
Duration timeout, Duration? timeout,
Map<String, String> metadata, Map<String, String>? metadata,
Codec compressionCodec, { Codec? compressionCodec, {
String userAgent, String? userAgent,
String grpcAcceptEncodings, String? grpcAcceptEncodings,
}) { }) {
final headers = [ final headers = [
_methodPost, _methodPost,
@ -317,12 +315,14 @@ class Http2ClientConnection implements connection.ClientConnection {
} }
class _SocketTransportConnector implements ClientTransportConnector { class _SocketTransportConnector implements ClientTransportConnector {
/// Either [InternetAddress] or [String].
final Object _host; final Object _host;
final int _port; final int _port;
final ChannelOptions _options; 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 @override
Future<ClientTransportConnection> connect() async { Future<ClientTransportConnection> connect() async {
@ -349,19 +349,22 @@ class _SocketTransportConnector implements ClientTransportConnector {
} }
@override @override
String get authority => String get authority {
_options.credentials.authority ?? final host =
(_port == 443 ? _host : "$_host:$_port"); _host is String ? _host as String : (_host as InternetAddress).host;
return _options.credentials.authority ??
(_port == 443 ? host : "$host:$_port");
}
@override @override
Future get done { Future get done {
assert(_socket != null); ArgumentError.checkNotNull(_socket);
return _socket.done; return _socket.done;
} }
@override @override
void shutdown() { void shutdown() {
assert(_socket != null); ArgumentError.checkNotNull(_socket);
_socket.destroy(); _socket.destroy();
} }

View File

@ -3,10 +3,10 @@ import 'common.dart';
import 'method.dart'; import 'method.dart';
typedef ClientUnaryInvoker<Q, R> = ResponseFuture<R> Function( 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( 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. /// ClientInterceptors intercepts client calls before they are executed.
/// ///

View File

@ -26,7 +26,7 @@ const defaultIdleTimeout = Duration(minutes: 5);
const defaultConnectionTimeOut = Duration(minutes: 50); const defaultConnectionTimeOut = Duration(minutes: 50);
const defaultUserAgent = 'dart-grpc/2.0.0'; 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 // Backoff algorithm from https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md
const _initialBackoff = Duration(seconds: 1); const _initialBackoff = Duration(seconds: 1);
@ -35,7 +35,7 @@ const _multiplier = 1.6;
const _jitter = 0.2; const _jitter = 0.2;
final _random = Random(); final _random = Random();
Duration defaultBackoffStrategy(Duration lastBackoff) { Duration defaultBackoffStrategy(Duration? lastBackoff) {
if (lastBackoff == null) return _initialBackoff; if (lastBackoff == null) return _initialBackoff;
final jitter = _random.nextDouble() * 2 * _jitter - _jitter; final jitter = _random.nextDouble() * 2 * _jitter - _jitter;
final nextBackoff = lastBackoff * (_multiplier + jitter); final nextBackoff = lastBackoff * (_multiplier + jitter);
@ -45,8 +45,8 @@ Duration defaultBackoffStrategy(Duration lastBackoff) {
/// Options controlling how connections are made on a [ClientChannel]. /// Options controlling how connections are made on a [ClientChannel].
class ChannelOptions { class ChannelOptions {
final ChannelCredentials credentials; final ChannelCredentials credentials;
final Duration idleTimeout; final Duration? idleTimeout;
final CodecRegistry codecRegistry; final CodecRegistry? codecRegistry;
/// The maximum time a single connection will be used for new requests. /// The maximum time a single connection will be used for new requests.
final Duration connectionTimeout; final Duration connectionTimeout;

View File

@ -29,17 +29,24 @@ class QueryParameter implements Comparable<QueryParameter> {
String get value => values.first; String get value => values.first;
/// Creates an instance by wrapping the single value in a [List]. /// Creates an instance by wrapping the single value in a [List].
QueryParameter(this.key, String value) QueryParameter(this.key, String value) : values = [value] {
: assert(key != null && key.trim().isNotEmpty), ArgumentError.checkNotNull(key);
values = [value]; if (key.trim().isEmpty) {
throw ArgumentError(key);
}
}
/// Creates an instance from a [List] of values. /// Creates an instance from a [List] of values.
/// ///
/// This is not the default constructor since the single-value case is the /// This is not the default constructor since the single-value case is the
/// most common. /// most common.
QueryParameter.multi(this.key, List<String> values) 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. /// Returns the escaped value of the param as will appear in a URL.
@override @override

View File

@ -32,32 +32,32 @@ bool allowBadCertificates(X509Certificate certificate, String host) => true;
/// Options controlling TLS security settings on a [ClientChannel]. /// Options controlling TLS security settings on a [ClientChannel].
class ChannelCredentials { class ChannelCredentials {
final bool isSecure; final bool isSecure;
final String authority; final String? authority;
final List<int> _certificateBytes; final List<int>? _certificateBytes;
final String _certificatePassword; final String? _certificatePassword;
final BadCertificateHandler onBadCertificate; final BadCertificateHandler? onBadCertificate;
const ChannelCredentials._(this.isSecure, this._certificateBytes, const ChannelCredentials._(this.isSecure, this._certificateBytes,
this._certificatePassword, this.authority, this.onBadCertificate); this._certificatePassword, this.authority, this.onBadCertificate);
/// Disable TLS. RPCs are sent in clear text. /// Disable TLS. RPCs are sent in clear text.
const ChannelCredentials.insecure({String authority}) const ChannelCredentials.insecure({String? authority})
: this._(false, null, null, authority, null); : this._(false, null, null, authority, null);
/// Enable TLS and optionally specify the [certificates] to trust. If /// Enable TLS and optionally specify the [certificates] to trust. If
/// [certificates] is not provided, the default trust store is used. /// [certificates] is not provided, the default trust store is used.
const ChannelCredentials.secure( const ChannelCredentials.secure(
{List<int> certificates, {List<int>? certificates,
String password, String? password,
String authority, String? authority,
BadCertificateHandler onBadCertificate}) BadCertificateHandler? onBadCertificate})
: this._(true, certificates, password, authority, onBadCertificate); : this._(true, certificates, password, authority, onBadCertificate);
SecurityContext get securityContext { SecurityContext? get securityContext {
if (!isSecure) return null; if (!isSecure) return null;
if (_certificateBytes != null) { if (_certificateBytes != null) {
return createSecurityContext(false) return createSecurityContext(false)
..setTrustedCertificatesBytes(_certificateBytes, ..setTrustedCertificatesBytes(_certificateBytes!,
password: _certificatePassword); password: _certificatePassword);
} }
final context = new SecurityContext(withTrustedRoots: true); final context = new SecurityContext(withTrustedRoots: true);

View File

@ -15,9 +15,10 @@
import 'dart:async'; import 'dart:async';
import 'package:grpc/grpc.dart';
import 'package:http2/transport.dart'; import 'package:http2/transport.dart';
import '../../shared/codec.dart';
import '../../shared/codec_registry.dart';
import '../../shared/message.dart'; import '../../shared/message.dart';
import '../../shared/streams.dart'; import '../../shared/streams.dart';
import 'transport.dart'; import 'transport.dart';
@ -33,8 +34,8 @@ class Http2TransportStream extends GrpcTransportStream {
Http2TransportStream( Http2TransportStream(
this._transportStream, this._transportStream,
this._onError, this._onError,
CodecRegistry codecRegistry, CodecRegistry? codecRegistry,
Codec compression, Codec? compression,
) : incomingMessages = _transportStream.incomingMessages ) : incomingMessages = _transportStream.incomingMessages
.transform(GrpcHttpDecoder()) .transform(GrpcHttpDecoder())
.transform(grpcDecompressor(codecRegistry: codecRegistry)) { .transform(grpcDecompressor(codecRegistry: codecRegistry)) {

View File

@ -47,10 +47,10 @@ class _GrpcWebConversionSink extends ChunkedConversionSink<ByteBuffer> {
final _dataHeader = Uint8List(4); final _dataHeader = Uint8List(4);
_GrpcWebParseState _state = _GrpcWebParseState.Init; _GrpcWebParseState _state = _GrpcWebParseState.Init;
int _chunkOffset; var _chunkOffset = 0;
int _frameType; int? _frameType;
int _dataOffset = 0; var _dataOffset = 0;
Uint8List _data; Uint8List? _data;
_GrpcWebConversionSink(this._out); _GrpcWebConversionSink(this._out);
@ -87,16 +87,16 @@ class _GrpcWebConversionSink extends ChunkedConversionSink<ByteBuffer> {
} }
void _parseMessage(List<int> chunkData) { void _parseMessage(List<int> chunkData) {
final dataRemaining = _data.lengthInBytes - _dataOffset; final dataRemaining = _data!.lengthInBytes - _dataOffset;
if (dataRemaining > 0) { if (dataRemaining > 0) {
final chunkRemaining = chunkData.length - _chunkOffset; final chunkRemaining = chunkData.length - _chunkOffset;
final toCopy = min(dataRemaining, chunkRemaining); final toCopy = min(dataRemaining, chunkRemaining);
_data.setRange( _data!
_dataOffset, _dataOffset + toCopy, chunkData, _chunkOffset); .setRange(_dataOffset, _dataOffset + toCopy, chunkData, _chunkOffset);
_dataOffset += toCopy; _dataOffset += toCopy;
_chunkOffset += toCopy; _chunkOffset += toCopy;
} }
if (_dataOffset == _data.lengthInBytes) { if (_dataOffset == _data!.lengthInBytes) {
_finishMessage(); _finishMessage();
} }
} }
@ -104,10 +104,10 @@ class _GrpcWebConversionSink extends ChunkedConversionSink<ByteBuffer> {
void _finishMessage() { void _finishMessage() {
switch (_frameType) { switch (_frameType) {
case frameTypeData: case frameTypeData:
_out.add(GrpcData(_data, isCompressed: false)); _out.add(GrpcData(_data!, isCompressed: false));
break; break;
case frameTypeTrailers: case frameTypeTrailers:
final stringData = String.fromCharCodes(_data); final stringData = String.fromCharCodes(_data!);
final headers = _parseHttp1Headers(stringData); final headers = _parseHttp1Headers(stringData);
_out.add(GrpcMetadata(headers)); _out.add(GrpcMetadata(headers));
break; break;
@ -119,7 +119,7 @@ class _GrpcWebConversionSink extends ChunkedConversionSink<ByteBuffer> {
Map<String, String> _parseHttp1Headers(String stringData) { Map<String, String> _parseHttp1Headers(String stringData) {
final trimmed = stringData.trim(); final trimmed = stringData.trim();
final chunks = trimmed == '' ? [] : trimmed.split('\r\n'); final chunks = trimmed == '' ? <String>[] : trimmed.split('\r\n');
final headers = <String, String>{}; final headers = <String, String>{};
for (final chunk in chunks) { for (final chunk in chunks) {
final pos = chunk.indexOf(':'); final pos = chunk.indexOf(':');

View File

@ -51,7 +51,8 @@ class XhrTransportStream implements GrpcTransportStream {
@override @override
StreamSink<List<int>> get outgoingMessages => _outgoingMessages.sink; StreamSink<List<int>> get outgoingMessages => _outgoingMessages.sink;
XhrTransportStream(this._request, {onError, onDone}) XhrTransportStream(this._request,
{required ErrorHandler onError, required onDone})
: _onError = onError, : _onError = onError,
_onDone = onDone { _onDone = onDone {
_outgoingMessages.stream _outgoingMessages.stream
@ -170,7 +171,7 @@ class XhrClientConnection extends ClientConnection {
void _initializeRequest(HttpRequest request, Map<String, String> metadata) { void _initializeRequest(HttpRequest request, Map<String, String> metadata) {
for (final header in metadata.keys) { 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 // Overriding the mimetype allows us to stream and parse the data
request.overrideMimeType('text/plain; charset=x-user-defined'); request.overrideMimeType('text/plain; charset=x-user-defined');
@ -181,9 +182,9 @@ class XhrClientConnection extends ClientConnection {
HttpRequest createHttpRequest() => HttpRequest(); HttpRequest createHttpRequest() => HttpRequest();
@override @override
GrpcTransportStream makeRequest(String path, Duration timeout, GrpcTransportStream makeRequest(String path, Duration? timeout,
Map<String, String> metadata, ErrorHandler onError, Map<String, String> metadata, ErrorHandler onError,
{CallOptions callOptions}) { {CallOptions? callOptions}) {
// gRPC-web headers. // gRPC-web headers.
if (_getContentTypeHeader(metadata) == null) { if (_getContentTypeHeader(metadata) == null) {
metadata['Content-Type'] = 'application/grpc-web+proto'; metadata['Content-Type'] = 'application/grpc-web+proto';
@ -231,7 +232,7 @@ class XhrClientConnection extends ClientConnection {
Future<void> shutdown() async {} 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) { for (var entry in metadata.entries) {
if (entry.key.toLowerCase() == _contentTypeKey.toLowerCase()) { if (entry.key.toLowerCase() == _contentTypeKey.toLowerCase()) {
return entry; return entry;

View File

@ -2,7 +2,7 @@
// Generated code. Do not modify. // Generated code. Do not modify.
// source: google/protobuf/any.proto // 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 // 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; import 'dart:core' as $core;
@ -37,7 +37,19 @@ class Any extends $pb.GeneratedMessage with $mixin.AnyMixin {
..hasRequiredFields = false; ..hasRequiredFields = false;
Any._() : super(); 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, factory Any.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); 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. ' @$core.Deprecated('Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version') 'Will be removed in next major version')
Any copyWith(void Function(Any) updates) => super.copyWith( Any copyWith(void Function(Any) updates) =>
(message) => updates(message as Any)); // ignore: deprecated_member_use super.copyWith((message) => updates(message as Any))
as Any; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static Any create() => Any._(); static Any create() => Any._();
@ -61,7 +74,7 @@ class Any extends $pb.GeneratedMessage with $mixin.AnyMixin {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static Any getDefault() => static Any getDefault() =>
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Any>(create); _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Any>(create);
static Any _defaultInstance; static Any? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.String get typeUrl => $_getSZ(0); $core.String get typeUrl => $_getSZ(0);

View File

@ -2,5 +2,5 @@
// Generated code. Do not modify. // Generated code. Do not modify.
// source: google/protobuf/any.proto // 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 // 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

View File

@ -2,9 +2,11 @@
// Generated code. Do not modify. // Generated code. Do not modify.
// source: google/protobuf/any.proto // 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 // 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 { const Any$json = const {
'1': 'Any', '1': 'Any',
'2': const [ '2': const [

View File

@ -2,7 +2,7 @@
// Generated code. Do not modify. // Generated code. Do not modify.
// source: google/protobuf/duration.proto // 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 // 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; import 'dart:core' as $core;
@ -38,7 +38,19 @@ class Duration extends $pb.GeneratedMessage with $mixin.DurationMixin {
..hasRequiredFields = false; ..hasRequiredFields = false;
Duration._() : super(); 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, factory Duration.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -53,8 +65,8 @@ class Duration extends $pb.GeneratedMessage with $mixin.DurationMixin {
'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version') 'Will be removed in next major version')
Duration copyWith(void Function(Duration) updates) => Duration copyWith(void Function(Duration) updates) =>
super.copyWith((message) => super.copyWith((message) => updates(message as Duration))
updates(message as Duration)); // ignore: deprecated_member_use as Duration; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static Duration create() => Duration._(); static Duration create() => Duration._();
@ -63,7 +75,7 @@ class Duration extends $pb.GeneratedMessage with $mixin.DurationMixin {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static Duration getDefault() => static Duration getDefault() =>
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Duration>(create); _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Duration>(create);
static Duration _defaultInstance; static Duration? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$fixnum.Int64 get seconds => $_getI64(0); $fixnum.Int64 get seconds => $_getI64(0);

View File

@ -2,5 +2,5 @@
// Generated code. Do not modify. // Generated code. Do not modify.
// source: google/protobuf/duration.proto // 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 // 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

View File

@ -2,9 +2,11 @@
// Generated code. Do not modify. // Generated code. Do not modify.
// source: google/protobuf/duration.proto // 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 // 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 { const Duration$json = const {
'1': 'Duration', '1': 'Duration',
'2': const [ '2': const [

View File

@ -2,7 +2,7 @@
// Generated code. Do not modify. // Generated code. Do not modify.
// source: google/rpc/code.proto // 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: 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; import 'dart:core' as $core;

View File

@ -2,7 +2,7 @@
// Generated code. Do not modify. // Generated code. Do not modify.
// source: google/rpc/code.proto // 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: 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 // ignore_for_file: UNDEFINED_SHOWN_NAME
@ -115,7 +115,7 @@ class Code extends $pb.ProtobufEnum {
static final $core.Map<$core.int, Code> _byValue = static final $core.Map<$core.int, Code> _byValue =
$pb.ProtobufEnum.initByValue(values); $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); const Code._($core.int v, $core.String n) : super(v, n);
} }

View File

@ -2,9 +2,11 @@
// Generated code. Do not modify. // Generated code. Do not modify.
// source: google/rpc/code.proto // 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: 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 { const Code$json = const {
'1': 'Code', '1': 'Code',
'2': const [ '2': const [

View File

@ -2,7 +2,7 @@
// Generated code. Do not modify. // Generated code. Do not modify.
// source: google/rpc/error_details.proto // 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 // 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; import 'dart:core' as $core;
@ -30,7 +30,15 @@ class RetryInfo extends $pb.GeneratedMessage {
..hasRequiredFields = false; ..hasRequiredFields = false;
RetryInfo._() : super(); 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, factory RetryInfo.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -45,8 +53,8 @@ class RetryInfo extends $pb.GeneratedMessage {
'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version') 'Will be removed in next major version')
RetryInfo copyWith(void Function(RetryInfo) updates) => RetryInfo copyWith(void Function(RetryInfo) updates) =>
super.copyWith((message) => super.copyWith((message) => updates(message as RetryInfo))
updates(message as RetryInfo)); // ignore: deprecated_member_use as RetryInfo; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static RetryInfo create() => RetryInfo._(); static RetryInfo create() => RetryInfo._();
@ -55,7 +63,7 @@ class RetryInfo extends $pb.GeneratedMessage {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static RetryInfo getDefault() => static RetryInfo getDefault() =>
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RetryInfo>(create); _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RetryInfo>(create);
static RetryInfo _defaultInstance; static RetryInfo? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$1.Duration get retryDelay => $_getN(0); $1.Duration get retryDelay => $_getN(0);
@ -95,7 +103,19 @@ class DebugInfo extends $pb.GeneratedMessage {
..hasRequiredFields = false; ..hasRequiredFields = false;
DebugInfo._() : super(); 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, factory DebugInfo.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -110,8 +130,8 @@ class DebugInfo extends $pb.GeneratedMessage {
'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version') 'Will be removed in next major version')
DebugInfo copyWith(void Function(DebugInfo) updates) => DebugInfo copyWith(void Function(DebugInfo) updates) =>
super.copyWith((message) => super.copyWith((message) => updates(message as DebugInfo))
updates(message as DebugInfo)); // ignore: deprecated_member_use as DebugInfo; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static DebugInfo create() => DebugInfo._(); static DebugInfo create() => DebugInfo._();
@ -120,7 +140,7 @@ class DebugInfo extends $pb.GeneratedMessage {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static DebugInfo getDefault() => static DebugInfo getDefault() =>
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<DebugInfo>(create); _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<DebugInfo>(create);
static DebugInfo _defaultInstance; static DebugInfo? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.List<$core.String> get stackEntries => $_getList(0); $core.List<$core.String> get stackEntries => $_getList(0);
@ -161,7 +181,19 @@ class QuotaFailure_Violation extends $pb.GeneratedMessage {
..hasRequiredFields = false; ..hasRequiredFields = false;
QuotaFailure_Violation._() : super(); 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, factory QuotaFailure_Violation.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -178,8 +210,8 @@ class QuotaFailure_Violation extends $pb.GeneratedMessage {
'Will be removed in next major version') 'Will be removed in next major version')
QuotaFailure_Violation copyWith( QuotaFailure_Violation copyWith(
void Function(QuotaFailure_Violation) updates) => void Function(QuotaFailure_Violation) updates) =>
super.copyWith((message) => updates( super.copyWith((message) => updates(message as QuotaFailure_Violation))
message as QuotaFailure_Violation)); // ignore: deprecated_member_use as QuotaFailure_Violation; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static QuotaFailure_Violation create() => QuotaFailure_Violation._(); static QuotaFailure_Violation create() => QuotaFailure_Violation._();
@ -189,7 +221,7 @@ class QuotaFailure_Violation extends $pb.GeneratedMessage {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static QuotaFailure_Violation getDefault() => _defaultInstance ??= static QuotaFailure_Violation getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<QuotaFailure_Violation>(create); $pb.GeneratedMessage.$_defaultFor<QuotaFailure_Violation>(create);
static QuotaFailure_Violation _defaultInstance; static QuotaFailure_Violation? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.String get subject => $_getSZ(0); $core.String get subject => $_getSZ(0);
@ -236,7 +268,15 @@ class QuotaFailure extends $pb.GeneratedMessage {
..hasRequiredFields = false; ..hasRequiredFields = false;
QuotaFailure._() : super(); 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, factory QuotaFailure.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -251,8 +291,8 @@ class QuotaFailure extends $pb.GeneratedMessage {
'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version') 'Will be removed in next major version')
QuotaFailure copyWith(void Function(QuotaFailure) updates) => QuotaFailure copyWith(void Function(QuotaFailure) updates) =>
super.copyWith((message) => super.copyWith((message) => updates(message as QuotaFailure))
updates(message as QuotaFailure)); // ignore: deprecated_member_use as QuotaFailure; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static QuotaFailure create() => QuotaFailure._(); static QuotaFailure create() => QuotaFailure._();
@ -262,7 +302,7 @@ class QuotaFailure extends $pb.GeneratedMessage {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static QuotaFailure getDefault() => _defaultInstance ??= static QuotaFailure getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<QuotaFailure>(create); $pb.GeneratedMessage.$_defaultFor<QuotaFailure>(create);
static QuotaFailure _defaultInstance; static QuotaFailure? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.List<QuotaFailure_Violation> get violations => $_getList(0); $core.List<QuotaFailure_Violation> get violations => $_getList(0);
@ -300,7 +340,23 @@ class ErrorInfo extends $pb.GeneratedMessage {
..hasRequiredFields = false; ..hasRequiredFields = false;
ErrorInfo._() : super(); 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, factory ErrorInfo.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -315,8 +371,8 @@ class ErrorInfo extends $pb.GeneratedMessage {
'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version') 'Will be removed in next major version')
ErrorInfo copyWith(void Function(ErrorInfo) updates) => ErrorInfo copyWith(void Function(ErrorInfo) updates) =>
super.copyWith((message) => super.copyWith((message) => updates(message as ErrorInfo))
updates(message as ErrorInfo)); // ignore: deprecated_member_use as ErrorInfo; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static ErrorInfo create() => ErrorInfo._(); static ErrorInfo create() => ErrorInfo._();
@ -325,7 +381,7 @@ class ErrorInfo extends $pb.GeneratedMessage {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static ErrorInfo getDefault() => static ErrorInfo getDefault() =>
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ErrorInfo>(create); _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ErrorInfo>(create);
static ErrorInfo _defaultInstance; static ErrorInfo? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.String get reason => $_getSZ(0); $core.String get reason => $_getSZ(0);
@ -383,7 +439,23 @@ class PreconditionFailure_Violation extends $pb.GeneratedMessage {
..hasRequiredFields = false; ..hasRequiredFields = false;
PreconditionFailure_Violation._() : super(); 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, factory PreconditionFailure_Violation.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -400,8 +472,9 @@ class PreconditionFailure_Violation extends $pb.GeneratedMessage {
'Will be removed in next major version') 'Will be removed in next major version')
PreconditionFailure_Violation copyWith( PreconditionFailure_Violation copyWith(
void Function(PreconditionFailure_Violation) updates) => void Function(PreconditionFailure_Violation) updates) =>
super.copyWith((message) => updates(message super.copyWith(
as PreconditionFailure_Violation)); // ignore: deprecated_member_use (message) => updates(message as PreconditionFailure_Violation))
as PreconditionFailure_Violation; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static PreconditionFailure_Violation create() => static PreconditionFailure_Violation create() =>
@ -412,7 +485,7 @@ class PreconditionFailure_Violation extends $pb.GeneratedMessage {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static PreconditionFailure_Violation getDefault() => _defaultInstance ??= static PreconditionFailure_Violation getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<PreconditionFailure_Violation>(create); $pb.GeneratedMessage.$_defaultFor<PreconditionFailure_Violation>(create);
static PreconditionFailure_Violation _defaultInstance; static PreconditionFailure_Violation? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.String get type => $_getSZ(0); $core.String get type => $_getSZ(0);
@ -471,7 +544,15 @@ class PreconditionFailure extends $pb.GeneratedMessage {
..hasRequiredFields = false; ..hasRequiredFields = false;
PreconditionFailure._() : super(); 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, factory PreconditionFailure.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -486,8 +567,8 @@ class PreconditionFailure extends $pb.GeneratedMessage {
'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version') 'Will be removed in next major version')
PreconditionFailure copyWith(void Function(PreconditionFailure) updates) => PreconditionFailure copyWith(void Function(PreconditionFailure) updates) =>
super.copyWith((message) => updates( super.copyWith((message) => updates(message as PreconditionFailure))
message as PreconditionFailure)); // ignore: deprecated_member_use as PreconditionFailure; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static PreconditionFailure create() => PreconditionFailure._(); static PreconditionFailure create() => PreconditionFailure._();
@ -497,7 +578,7 @@ class PreconditionFailure extends $pb.GeneratedMessage {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static PreconditionFailure getDefault() => _defaultInstance ??= static PreconditionFailure getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<PreconditionFailure>(create); $pb.GeneratedMessage.$_defaultFor<PreconditionFailure>(create);
static PreconditionFailure _defaultInstance; static PreconditionFailure? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.List<PreconditionFailure_Violation> get violations => $_getList(0); $core.List<PreconditionFailure_Violation> get violations => $_getList(0);
@ -526,7 +607,19 @@ class BadRequest_FieldViolation extends $pb.GeneratedMessage {
..hasRequiredFields = false; ..hasRequiredFields = false;
BadRequest_FieldViolation._() : super(); 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, factory BadRequest_FieldViolation.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -543,8 +636,8 @@ class BadRequest_FieldViolation extends $pb.GeneratedMessage {
'Will be removed in next major version') 'Will be removed in next major version')
BadRequest_FieldViolation copyWith( BadRequest_FieldViolation copyWith(
void Function(BadRequest_FieldViolation) updates) => void Function(BadRequest_FieldViolation) updates) =>
super.copyWith((message) => updates(message super.copyWith((message) => updates(message as BadRequest_FieldViolation))
as BadRequest_FieldViolation)); // ignore: deprecated_member_use as BadRequest_FieldViolation; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static BadRequest_FieldViolation create() => BadRequest_FieldViolation._(); static BadRequest_FieldViolation create() => BadRequest_FieldViolation._();
@ -554,7 +647,7 @@ class BadRequest_FieldViolation extends $pb.GeneratedMessage {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static BadRequest_FieldViolation getDefault() => _defaultInstance ??= static BadRequest_FieldViolation getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<BadRequest_FieldViolation>(create); $pb.GeneratedMessage.$_defaultFor<BadRequest_FieldViolation>(create);
static BadRequest_FieldViolation _defaultInstance; static BadRequest_FieldViolation? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.String get field_1 => $_getSZ(0); $core.String get field_1 => $_getSZ(0);
@ -601,7 +694,15 @@ class BadRequest extends $pb.GeneratedMessage {
..hasRequiredFields = false; ..hasRequiredFields = false;
BadRequest._() : super(); 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, factory BadRequest.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -616,8 +717,8 @@ class BadRequest extends $pb.GeneratedMessage {
'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version') 'Will be removed in next major version')
BadRequest copyWith(void Function(BadRequest) updates) => BadRequest copyWith(void Function(BadRequest) updates) =>
super.copyWith((message) => super.copyWith((message) => updates(message as BadRequest))
updates(message as BadRequest)); // ignore: deprecated_member_use as BadRequest; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static BadRequest create() => BadRequest._(); static BadRequest create() => BadRequest._();
@ -626,7 +727,7 @@ class BadRequest extends $pb.GeneratedMessage {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static BadRequest getDefault() => _defaultInstance ??= static BadRequest getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<BadRequest>(create); $pb.GeneratedMessage.$_defaultFor<BadRequest>(create);
static BadRequest _defaultInstance; static BadRequest? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.List<BadRequest_FieldViolation> get fieldViolations => $_getList(0); $core.List<BadRequest_FieldViolation> get fieldViolations => $_getList(0);
@ -655,7 +756,19 @@ class RequestInfo extends $pb.GeneratedMessage {
..hasRequiredFields = false; ..hasRequiredFields = false;
RequestInfo._() : super(); 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, factory RequestInfo.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -670,8 +783,8 @@ class RequestInfo extends $pb.GeneratedMessage {
'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version') 'Will be removed in next major version')
RequestInfo copyWith(void Function(RequestInfo) updates) => RequestInfo copyWith(void Function(RequestInfo) updates) =>
super.copyWith((message) => super.copyWith((message) => updates(message as RequestInfo))
updates(message as RequestInfo)); // ignore: deprecated_member_use as RequestInfo; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static RequestInfo create() => RequestInfo._(); static RequestInfo create() => RequestInfo._();
@ -680,7 +793,7 @@ class RequestInfo extends $pb.GeneratedMessage {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static RequestInfo getDefault() => _defaultInstance ??= static RequestInfo getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<RequestInfo>(create); $pb.GeneratedMessage.$_defaultFor<RequestInfo>(create);
static RequestInfo _defaultInstance; static RequestInfo? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.String get requestId => $_getSZ(0); $core.String get requestId => $_getSZ(0);
@ -737,7 +850,27 @@ class ResourceInfo extends $pb.GeneratedMessage {
..hasRequiredFields = false; ..hasRequiredFields = false;
ResourceInfo._() : super(); 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, factory ResourceInfo.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -752,8 +885,8 @@ class ResourceInfo extends $pb.GeneratedMessage {
'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version') 'Will be removed in next major version')
ResourceInfo copyWith(void Function(ResourceInfo) updates) => ResourceInfo copyWith(void Function(ResourceInfo) updates) =>
super.copyWith((message) => super.copyWith((message) => updates(message as ResourceInfo))
updates(message as ResourceInfo)); // ignore: deprecated_member_use as ResourceInfo; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static ResourceInfo create() => ResourceInfo._(); static ResourceInfo create() => ResourceInfo._();
@ -763,7 +896,7 @@ class ResourceInfo extends $pb.GeneratedMessage {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static ResourceInfo getDefault() => _defaultInstance ??= static ResourceInfo getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<ResourceInfo>(create); $pb.GeneratedMessage.$_defaultFor<ResourceInfo>(create);
static ResourceInfo _defaultInstance; static ResourceInfo? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.String get resourceType => $_getSZ(0); $core.String get resourceType => $_getSZ(0);
@ -837,7 +970,19 @@ class Help_Link extends $pb.GeneratedMessage {
..hasRequiredFields = false; ..hasRequiredFields = false;
Help_Link._() : super(); 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, factory Help_Link.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -852,8 +997,8 @@ class Help_Link extends $pb.GeneratedMessage {
'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version') 'Will be removed in next major version')
Help_Link copyWith(void Function(Help_Link) updates) => Help_Link copyWith(void Function(Help_Link) updates) =>
super.copyWith((message) => super.copyWith((message) => updates(message as Help_Link))
updates(message as Help_Link)); // ignore: deprecated_member_use as Help_Link; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static Help_Link create() => Help_Link._(); static Help_Link create() => Help_Link._();
@ -862,7 +1007,7 @@ class Help_Link extends $pb.GeneratedMessage {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static Help_Link getDefault() => static Help_Link getDefault() =>
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Help_Link>(create); _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Help_Link>(create);
static Help_Link _defaultInstance; static Help_Link? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.String get description => $_getSZ(0); $core.String get description => $_getSZ(0);
@ -909,7 +1054,15 @@ class Help extends $pb.GeneratedMessage {
..hasRequiredFields = false; ..hasRequiredFields = false;
Help._() : super(); 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, factory Help.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -923,8 +1076,9 @@ class Help extends $pb.GeneratedMessage {
@$core.Deprecated('Using this can add significant overhead to your binary. ' @$core.Deprecated('Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version') 'Will be removed in next major version')
Help copyWith(void Function(Help) updates) => super.copyWith( Help copyWith(void Function(Help) updates) =>
(message) => updates(message as Help)); // ignore: deprecated_member_use super.copyWith((message) => updates(message as Help))
as Help; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static Help create() => Help._(); static Help create() => Help._();
@ -933,7 +1087,7 @@ class Help extends $pb.GeneratedMessage {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static Help getDefault() => static Help getDefault() =>
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Help>(create); _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Help>(create);
static Help _defaultInstance; static Help? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.List<Help_Link> get links => $_getList(0); $core.List<Help_Link> get links => $_getList(0);
@ -962,7 +1116,19 @@ class LocalizedMessage extends $pb.GeneratedMessage {
..hasRequiredFields = false; ..hasRequiredFields = false;
LocalizedMessage._() : super(); 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, factory LocalizedMessage.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -977,8 +1143,8 @@ class LocalizedMessage extends $pb.GeneratedMessage {
'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version') 'Will be removed in next major version')
LocalizedMessage copyWith(void Function(LocalizedMessage) updates) => LocalizedMessage copyWith(void Function(LocalizedMessage) updates) =>
super.copyWith((message) => updates( super.copyWith((message) => updates(message as LocalizedMessage))
message as LocalizedMessage)); // ignore: deprecated_member_use as LocalizedMessage; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static LocalizedMessage create() => LocalizedMessage._(); static LocalizedMessage create() => LocalizedMessage._();
@ -988,7 +1154,7 @@ class LocalizedMessage extends $pb.GeneratedMessage {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static LocalizedMessage getDefault() => _defaultInstance ??= static LocalizedMessage getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<LocalizedMessage>(create); $pb.GeneratedMessage.$_defaultFor<LocalizedMessage>(create);
static LocalizedMessage _defaultInstance; static LocalizedMessage? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.String get locale => $_getSZ(0); $core.String get locale => $_getSZ(0);

View File

@ -2,5 +2,5 @@
// Generated code. Do not modify. // Generated code. Do not modify.
// source: google/rpc/error_details.proto // 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 // 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

View File

@ -2,9 +2,11 @@
// Generated code. Do not modify. // Generated code. Do not modify.
// source: google/rpc/error_details.proto // 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 // 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 { const RetryInfo$json = const {
'1': 'RetryInfo', '1': 'RetryInfo',
'2': const [ '2': const [

View File

@ -2,7 +2,7 @@
// Generated code. Do not modify. // Generated code. Do not modify.
// source: google/rpc/status.proto // 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 // 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; import 'dart:core' as $core;
@ -40,7 +40,23 @@ class Status extends $pb.GeneratedMessage {
..hasRequiredFields = false; ..hasRequiredFields = false;
Status._() : super(); 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, factory Status.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -54,8 +70,9 @@ class Status extends $pb.GeneratedMessage {
@$core.Deprecated('Using this can add significant overhead to your binary. ' @$core.Deprecated('Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version') 'Will be removed in next major version')
Status copyWith(void Function(Status) updates) => super.copyWith( Status copyWith(void Function(Status) updates) =>
(message) => updates(message as Status)); // ignore: deprecated_member_use super.copyWith((message) => updates(message as Status))
as Status; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static Status create() => Status._(); static Status create() => Status._();
@ -64,7 +81,7 @@ class Status extends $pb.GeneratedMessage {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static Status getDefault() => static Status getDefault() =>
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Status>(create); _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Status>(create);
static Status _defaultInstance; static Status? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.int get code => $_getIZ(0); $core.int get code => $_getIZ(0);

View File

@ -2,5 +2,5 @@
// Generated code. Do not modify. // Generated code. Do not modify.
// source: google/rpc/status.proto // 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 // 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

View File

@ -2,9 +2,11 @@
// Generated code. Do not modify. // Generated code. Do not modify.
// source: google/rpc/status.proto // 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 // 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 { const Status$json = const {
'1': 'Status', '1': 'Status',
'2': const [ '2': const [

View File

@ -19,19 +19,19 @@
/// ability to set custom metadata on the header/trailer sent to the client. /// ability to set custom metadata on the header/trailer sent to the client.
abstract class ServiceCall { abstract class ServiceCall {
/// Custom metadata from the client. /// 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 /// 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 /// have been sent, either when [sendHeaders] is called, or when the first
/// response message is sent. /// 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. /// 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 /// Deadline for this call. If the call is still active after this time, then
/// the client or server may cancel it. /// the client or server may cancel it.
DateTime get deadline; DateTime? get deadline;
/// Returns [true] if the [deadline] has been exceeded. /// Returns [true] if the [deadline] has been exceeded.
bool get isTimedOut; bool get isTimedOut;
@ -50,5 +50,5 @@ abstract class ServiceCall {
/// ///
/// The call will be closed after calling this method, and no further /// The call will be closed after calling this method, and no further
/// responses can be sent. /// responses can be sent.
void sendTrailers({int status, String message}); void sendTrailers({int? status, String? message});
} }

View File

@ -31,32 +31,33 @@ import 'service.dart';
/// Handles an incoming gRPC call. /// Handles an incoming gRPC call.
class ServerHandler_ extends ServiceCall { class ServerHandler_ extends ServiceCall {
final ServerTransportStream _stream; final ServerTransportStream _stream;
final Service Function(String service) _serviceLookup; final Service? Function(String service) _serviceLookup;
final List<Interceptor> _interceptors; final List<Interceptor> _interceptors;
final CodecRegistry _codecRegistry; final CodecRegistry? _codecRegistry;
StreamSubscription<GrpcMessage> _incomingSubscription; // ignore: cancel_subscriptions
StreamSubscription<GrpcMessage>? _incomingSubscription;
Service _service; late Service _service;
ServiceMethod _descriptor; late ServiceMethod _descriptor;
Map<String, String> _clientMetadata; Map<String, String>? _clientMetadata;
Codec _callEncodingCodec; Codec? _callEncodingCodec;
StreamController _requests; StreamController? _requests;
bool _hasReceivedRequest = false; bool _hasReceivedRequest = false;
Stream _responses; late Stream _responses;
StreamSubscription _responseSubscription; StreamSubscription? _responseSubscription;
bool _headersSent = false; bool _headersSent = false;
Map<String, String> _customHeaders = {}; Map<String, String>? _customHeaders = {};
Map<String, String> _customTrailers = {}; Map<String, String>? _customTrailers = {};
DateTime _deadline; DateTime? _deadline;
bool _isCanceled = false; bool _isCanceled = false;
bool _isTimedOut = false; bool _isTimedOut = false;
Timer _timeoutTimer; Timer? _timeoutTimer;
ServerHandler_( ServerHandler_(
this._serviceLookup, this._serviceLookup,
@ -65,17 +66,17 @@ class ServerHandler_ extends ServiceCall {
this._codecRegistry, this._codecRegistry,
); );
DateTime get deadline => _deadline; DateTime? get deadline => _deadline;
bool get isCanceled => _isCanceled; bool get isCanceled => _isCanceled;
bool get isTimedOut => _isTimedOut; 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() { void handle() {
_stream.onTerminated = (_) => cancel(); _stream.onTerminated = (_) => cancel();
@ -95,23 +96,21 @@ class ServerHandler_ extends ServiceCall {
/// We need the catchError() handler here, since otherwise the error would /// We need the catchError() handler here, since otherwise the error would
/// be an unhandled exception. /// be an unhandled exception.
void _cancelResponseSubscription() { void _cancelResponseSubscription() {
_responseSubscription?.cancel()?.catchError((_) {}); _responseSubscription?.cancel().catchError((_) {});
} }
// -- Idle state, incoming data -- // -- Idle state, incoming data --
void _onDataIdle(GrpcMessage message) async { void _onDataIdle(GrpcMessage headerMessage) async {
if (message is! GrpcMetadata) { if (headerMessage is! GrpcMetadata) {
_sendError(GrpcError.unimplemented('Expected header frame')); _sendError(GrpcError.unimplemented('Expected header frame'));
_sinkIncoming(); _sinkIncoming();
return; return;
} }
_incomingSubscription.pause(); _incomingSubscription!.pause();
final headerMessage = message
as GrpcMetadata; // TODO(jakobr): Cast should not be necessary here.
_clientMetadata = headerMessage.metadata; _clientMetadata = headerMessage.metadata;
final path = _clientMetadata[':path']; final path = _clientMetadata![':path']!;
final pathSegments = path.split('/'); final pathSegments = path.split('/');
if (pathSegments.length < 3) { if (pathSegments.length < 3) {
_sendError(GrpcError.unimplemented('Invalid path')); _sendError(GrpcError.unimplemented('Invalid path'));
@ -122,19 +121,21 @@ class ServerHandler_ extends ServiceCall {
final methodName = pathSegments[2]; final methodName = pathSegments[2];
if (_codecRegistry != null) { if (_codecRegistry != null) {
final acceptedEncodings = final acceptedEncodings =
clientMetadata['grpc-accept-encoding']?.split(',') ?? []; clientMetadata!['grpc-accept-encoding']?.split(',') ?? [];
_callEncodingCodec = acceptedEncodings _callEncodingCodec = acceptedEncodings
.map(_codecRegistry.lookup) .map(_codecRegistry!.lookup)
.firstWhere((c) => c != null, orElse: () => null); .firstWhere((c) => c != null, orElse: () => null);
} }
_service = _serviceLookup(serviceName); final service = _serviceLookup(serviceName);
_descriptor = _service?.$lookupMethod(methodName); final descriptor = service?.$lookupMethod(methodName);
if (_descriptor == null) { if (descriptor == null) {
_sendError(GrpcError.unimplemented('Path $path not found')); _sendError(GrpcError.unimplemented('Path $path not found'));
_sinkIncoming(); _sinkIncoming();
return; return;
} }
_service = service!;
_descriptor = descriptor;
final error = await _applyInterceptors(); final error = await _applyInterceptors();
if (error != null) { if (error != null) {
@ -146,7 +147,7 @@ class ServerHandler_ extends ServiceCall {
_startStreamingRequest(); _startStreamingRequest();
} }
GrpcError _onMetadata() { GrpcError? _onMetadata() {
try { try {
_service.$onMetadata(this); _service.$onMetadata(this);
} on GrpcError catch (error) { } on GrpcError catch (error) {
@ -158,7 +159,7 @@ class ServerHandler_ extends ServiceCall {
return null; return null;
} }
Future<GrpcError> _applyInterceptors() async { Future<GrpcError?> _applyInterceptors() async {
try { try {
for (final interceptor in _interceptors) { for (final interceptor in _interceptors) {
final error = await interceptor(this, this._descriptor); final error = await interceptor(this, this._descriptor);
@ -174,13 +175,14 @@ class ServerHandler_ extends ServiceCall {
} }
void _startStreamingRequest() { void _startStreamingRequest() {
_requests = _descriptor.createRequestStream(_incomingSubscription); final requests = _descriptor.createRequestStream(_incomingSubscription!);
_incomingSubscription.onData(_onDataActive); _requests = requests;
_incomingSubscription!.onData(_onDataActive);
final error = _onMetadata(); final error = _onMetadata();
if (error != null) { if (error != null) {
if (!_requests.isClosed) { if (!requests.isClosed) {
_requests requests
..addError(error) ..addError(error)
..close(); ..close();
} }
@ -190,16 +192,16 @@ class ServerHandler_ extends ServiceCall {
return; return;
} }
_responses = _descriptor.handle(this, _requests.stream); _responses = _descriptor.handle(this, requests.stream);
_responseSubscription = _responses.listen(_onResponse, _responseSubscription = _responses.listen(_onResponse,
onError: _onResponseError, onError: _onResponseError,
onDone: _onResponseDone, onDone: _onResponseDone,
cancelOnError: true); cancelOnError: true);
_incomingSubscription.onData(_onDataActive); _incomingSubscription!.onData(_onDataActive);
_incomingSubscription.onDone(_onDoneExpected); _incomingSubscription!.onDone(_onDoneExpected);
final timeout = fromTimeoutString(_clientMetadata['grpc-timeout']); final timeout = fromTimeoutString(_clientMetadata!['grpc-timeout']);
if (timeout != null) { if (timeout != null) {
_deadline = DateTime.now().add(timeout); _deadline = DateTime.now().add(timeout);
_timeoutTimer = Timer(timeout, _onTimedOut); _timeoutTimer = Timer(timeout, _onTimedOut);
@ -212,8 +214,8 @@ class ServerHandler_ extends ServiceCall {
_isCanceled = true; _isCanceled = true;
final error = GrpcError.deadlineExceeded('Deadline exceeded'); final error = GrpcError.deadlineExceeded('Deadline exceeded');
_sendError(error); _sendError(error);
if (!_requests.isClosed) { if (!_requests!.isClosed) {
_requests _requests!
..addError(error) ..addError(error)
..close(); ..close();
} }
@ -225,7 +227,7 @@ class ServerHandler_ extends ServiceCall {
if (message is! GrpcData) { if (message is! GrpcData) {
final error = GrpcError.unimplemented('Expected request'); final error = GrpcError.unimplemented('Expected request');
_sendError(error); _sendError(error);
_requests _requests!
..addError(error) ..addError(error)
..close(); ..close();
return; return;
@ -234,14 +236,13 @@ class ServerHandler_ extends ServiceCall {
if (_hasReceivedRequest && !_descriptor.streamingRequest) { if (_hasReceivedRequest && !_descriptor.streamingRequest) {
final error = GrpcError.unimplemented('Too many requests'); final error = GrpcError.unimplemented('Too many requests');
_sendError(error); _sendError(error);
_requests _requests!
..addError(error) ..addError(error)
..close(); ..close();
return; return;
} }
// TODO(jakobr): Cast should not be necessary here. final data = message;
final data = message as GrpcData;
var request; var request;
try { try {
request = _descriptor.deserialize(data.data); request = _descriptor.deserialize(data.data);
@ -249,12 +250,12 @@ class ServerHandler_ extends ServiceCall {
final grpcError = final grpcError =
GrpcError.internal('Error deserializing request: $error'); GrpcError.internal('Error deserializing request: $error');
_sendError(grpcError); _sendError(grpcError);
_requests _requests!
..addError(grpcError) ..addError(grpcError)
..close(); ..close();
return; return;
} }
_requests.add(request); _requests!.add(request);
_hasReceivedRequest = true; _hasReceivedRequest = true;
} }
@ -269,9 +270,9 @@ class ServerHandler_ extends ServiceCall {
_stream.sendData(frame(bytes, _callEncodingCodec)); _stream.sendData(frame(bytes, _callEncodingCodec));
} catch (error) { } catch (error) {
final grpcError = GrpcError.internal('Error sending response: $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. // If we can, alert the handler that things are going wrong.
_requests _requests!
..addError(grpcError) ..addError(grpcError)
..close(); ..close();
} }
@ -295,17 +296,17 @@ class ServerHandler_ extends ServiceCall {
void sendHeaders() { void sendHeaders() {
if (_headersSent) throw GrpcError.internal('Headers already sent'); 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? // TODO(jakobr): Should come from package:http2?
final outgoingHeadersMap = <String, String>{ final outgoingHeadersMap = <String, String>{
':status': '200', ':status': '200',
'content-type': 'application/grpc', 'content-type': 'application/grpc',
if (_callEncodingCodec != null) if (_callEncodingCodec != null)
'grpc-encoding': _callEncodingCodec.encodingName, 'grpc-encoding': _callEncodingCodec!.encodingName,
}; };
outgoingHeadersMap.addAll(_customHeaders); outgoingHeadersMap.addAll(_customHeaders!);
_customHeaders = null; _customHeaders = null;
final outgoingHeaders = <Header>[]; final outgoingHeaders = <Header>[];
@ -315,7 +316,7 @@ class ServerHandler_ extends ServiceCall {
_headersSent = true; _headersSent = true;
} }
void sendTrailers({int status = 0, String message}) { void sendTrailers({int? status = 0, String? message}) {
_timeoutTimer?.cancel(); _timeoutTimer?.cancel();
final outgoingTrailersMap = <String, String>{}; final outgoingTrailersMap = <String, String>{};
@ -324,13 +325,13 @@ class ServerHandler_ extends ServiceCall {
outgoingTrailersMap[':status'] = '200'; outgoingTrailersMap[':status'] = '200';
outgoingTrailersMap['content-type'] = 'application/grpc'; outgoingTrailersMap['content-type'] = 'application/grpc';
_customHeaders..remove(':status')..remove('content-type'); _customHeaders!..remove(':status')..remove('content-type');
outgoingTrailersMap.addAll(_customHeaders); outgoingTrailersMap.addAll(_customHeaders!);
_customHeaders = null; _customHeaders = null;
_headersSent = true; _headersSent = true;
} }
_customTrailers..remove(':status')..remove('content-type'); _customTrailers!..remove(':status')..remove('content-type');
outgoingTrailersMap.addAll(_customTrailers); outgoingTrailersMap.addAll(_customTrailers!);
_customTrailers = null; _customTrailers = null;
outgoingTrailersMap['grpc-status'] = status.toString(); outgoingTrailersMap['grpc-status'] = status.toString();
if (message != null) { if (message != null) {
@ -354,11 +355,11 @@ class ServerHandler_ extends ServiceCall {
// client, so we treat it as such. // client, so we treat it as such.
_timeoutTimer?.cancel(); _timeoutTimer?.cancel();
_isCanceled = true; _isCanceled = true;
if (_requests != null && !_requests.isClosed) { if (_requests != null && !_requests!.isClosed) {
_requests.addError(GrpcError.cancelled('Cancelled')); _requests!.addError(GrpcError.cancelled('Cancelled'));
} }
_cancelResponseSubscription(); _cancelResponseSubscription();
_incomingSubscription.cancel(); _incomingSubscription!.cancel();
_stream.terminate(); _stream.terminate();
} }
@ -371,20 +372,20 @@ class ServerHandler_ extends ServiceCall {
if (!(_hasReceivedRequest || _descriptor.streamingRequest)) { if (!(_hasReceivedRequest || _descriptor.streamingRequest)) {
final error = GrpcError.unimplemented('No request received'); final error = GrpcError.unimplemented('No request received');
_sendError(error); _sendError(error);
_requests.addError(error); _requests!.addError(error);
} }
_onDone(); _onDone();
} }
void _onDone() { void _onDone() {
_requests?.close(); _requests?.close();
_incomingSubscription.cancel(); _incomingSubscription!.cancel();
} }
/// Sink incoming requests. This is used when an error has already been /// 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. /// reported, but we still need to consume the request stream from the client.
void _sinkIncoming() { void _sinkIncoming() {
_incomingSubscription _incomingSubscription!
..onData((_) {}) ..onData((_) {})
..onDone(_onDone); ..onDone(_onDone);
} }
@ -405,6 +406,6 @@ class ServerHandler extends ServerHandler_ {
Service Function(String service) serviceLookup, Service Function(String service) serviceLookup,
stream, [ stream, [
List<Interceptor> interceptors = const <Interceptor>[], List<Interceptor> interceptors = const <Interceptor>[],
CodecRegistry codecRegistry, CodecRegistry? codecRegistry,
]) : super(serviceLookup, stream, interceptors, codecRegistry); ]) : super(serviceLookup, stream, interceptors, codecRegistry);
} }

View File

@ -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 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 throws [Exception], [GrpcError.internal] with exception.toString() will be returned.
/// If the interceptor returns null, the corresponding [ServiceMethod] of [Service] will be called. /// 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); ServiceCall call, ServiceMethod method);

View File

@ -16,10 +16,10 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:grpc/grpc.dart';
import 'package:http2/transport.dart'; import 'package:http2/transport.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import '../shared/codec_registry.dart';
import '../shared/security.dart'; import '../shared/security.dart';
import 'handler.dart'; import 'handler.dart';
import 'interceptor.dart'; import 'interceptor.dart';
@ -33,7 +33,7 @@ abstract class ServerCredentials {
/// Creates [SecurityContext] from these credentials if possible. /// Creates [SecurityContext] from these credentials if possible.
/// Otherwise returns [null]. /// Otherwise returns [null].
SecurityContext get securityContext; SecurityContext? get securityContext;
} }
/// Set of credentials that only allows local TCP connections. /// Set of credentials that only allows local TCP connections.
@ -42,14 +42,14 @@ class ServerLocalCredentials extends ServerCredentials {
bool validateClient(Socket socket) => socket.remoteAddress.isLoopback; bool validateClient(Socket socket) => socket.remoteAddress.isLoopback;
@override @override
SecurityContext get securityContext => null; SecurityContext? get securityContext => null;
} }
class ServerTlsCredentials extends ServerCredentials { class ServerTlsCredentials extends ServerCredentials {
final List<int> certificate; final List<int>? certificate;
final String certificatePassword; final String? certificatePassword;
final List<int> privateKey; final List<int>? privateKey;
final String privateKeyPassword; final String? privateKeyPassword;
/// TLS credentials for a [Server]. /// TLS credentials for a [Server].
/// ///
@ -64,10 +64,10 @@ class ServerTlsCredentials extends ServerCredentials {
SecurityContext get securityContext { SecurityContext get securityContext {
final context = createSecurityContext(true); final context = createSecurityContext(true);
if (privateKey != null) { if (privateKey != null) {
context.usePrivateKeyBytes(privateKey, password: privateKeyPassword); context.usePrivateKeyBytes(privateKey!, password: privateKeyPassword);
} }
if (certificate != null) { if (certificate != null) {
context.useCertificateChainBytes(certificate, context.useCertificateChainBytes(certificate!,
password: certificatePassword); password: certificatePassword);
} }
return context; return context;
@ -84,7 +84,7 @@ class ServerTlsCredentials extends ServerCredentials {
class ConnectionServer { class ConnectionServer {
final Map<String, Service> _services = {}; final Map<String, Service> _services = {};
final List<Interceptor> _interceptors; final List<Interceptor> _interceptors;
final CodecRegistry _codecRegistry; final CodecRegistry? _codecRegistry;
final _connections = <ServerTransportConnection>[]; final _connections = <ServerTransportConnection>[];
@ -92,7 +92,7 @@ class ConnectionServer {
ConnectionServer( ConnectionServer(
List<Service> services, [ List<Service> services, [
List<Interceptor> interceptors = const <Interceptor>[], List<Interceptor> interceptors = const <Interceptor>[],
CodecRegistry codecRegistry, CodecRegistry? codecRegistry,
]) : _codecRegistry = codecRegistry, ]) : _codecRegistry = codecRegistry,
_interceptors = interceptors { _interceptors = interceptors {
for (final service in services) { 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 { Future<void> serveConnection(ServerTransportConnection connection) async {
_connections.add(connection); _connections.add(connection);
ServerHandler_ handler; ServerHandler_? handler;
// TODO(jakobr): Set active state handlers, close connection after idle // TODO(jakobr): Set active state handlers, close connection after idle
// timeout. // timeout.
connection.incomingStreams.listen((stream) { connection.incomingStreams.listen((stream) {
@ -134,39 +134,39 @@ class ConnectionServer {
/// ///
/// Listens for incoming RPCs, dispatching them to the right [Service] handler. /// Listens for incoming RPCs, dispatching them to the right [Service] handler.
class Server extends ConnectionServer { class Server extends ConnectionServer {
ServerSocket _insecureServer; ServerSocket? _insecureServer;
SecureServerSocket _secureServer; SecureServerSocket? _secureServer;
/// Create a server for the given [services]. /// Create a server for the given [services].
Server( Server(
List<Service> services, [ List<Service> services, [
List<Interceptor> interceptors = const <Interceptor>[], List<Interceptor> interceptors = const <Interceptor>[],
CodecRegistry codecRegistry, CodecRegistry? codecRegistry,
]) : super(services, interceptors, codecRegistry); ]) : super(services, interceptors, codecRegistry);
/// The port that the server is listening on, or `null` if the server is not /// The port that the server is listening on, or `null` if the server is not
/// active. /// active.
int get port { int? get port {
if (_secureServer != null) return _secureServer.port; if (_secureServer != null) return _secureServer!.port;
if (_insecureServer != null) return _insecureServer.port; if (_insecureServer != null) return _insecureServer!.port;
return null; return null;
} }
Service lookupService(String service) => _services[service]; Service? lookupService(String service) => _services[service];
/// Starts the [Server] with the given options. /// Starts the [Server] with the given options.
/// [address] can be either a [String] or an [InternetAddress], in the latter /// [address] can be either a [String] or an [InternetAddress], in the latter
/// case it can be a Unix Domain Socket address. /// case it can be a Unix Domain Socket address.
Future<void> serve( Future<void> serve(
{dynamic address, {dynamic address,
int port, int? port,
ServerCredentials security, ServerCredentials? security,
ServerSettings http2ServerSettings, ServerSettings? http2ServerSettings,
int backlog: 0, int backlog: 0,
bool v6Only: false, bool v6Only: false,
bool shared: false}) async { bool shared: false}) async {
// TODO(dart-lang/grpc-dart#9): Handle HTTP/1.1 upgrade to h2c, if allowed. // 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; final securityContext = security?.securityContext;
if (securityContext != null) { if (securityContext != null) {
_secureServer = await SecureServerSocket.bind( _secureServer = await SecureServerSocket.bind(
@ -183,7 +183,7 @@ class Server extends ConnectionServer {
); );
server = _insecureServer; server = _insecureServer;
} }
server.listen((socket) { server!.listen((socket) {
// Don't wait for io buffers to fill up before sending requests. // Don't wait for io buffers to fill up before sending requests.
if (socket.address.type != InternetAddressType.unix) { if (socket.address.type != InternetAddressType.unix) {
socket.setOption(SocketOption.tcpNoDelay, true); socket.setOption(SocketOption.tcpNoDelay, true);
@ -213,10 +213,10 @@ class Server extends ConnectionServer {
Future<void> shutdown() async { Future<void> shutdown() async {
final done = _connections.map((connection) => connection.finish()).toList(); final done = _connections.map((connection) => connection.finish()).toList();
if (_insecureServer != null) { if (_insecureServer != null) {
done.add(_insecureServer.close()); done.add(_insecureServer!.close());
} }
if (_secureServer != null) { if (_secureServer != null) {
done.add(_secureServer.close()); done.add(_secureServer!.close());
} }
await Future.wait(done); await Future.wait(done);
_insecureServer = null; _insecureServer = null;

View File

@ -56,36 +56,39 @@ class ServiceMethod<Q, R> {
return handler(call, _toSingleFuture(requests)); return handler(call, _toSingleFuture(requests));
} }
} else { } else {
Future<R> response; final response = streamingRequest
if (streamingRequest) { ? handler(call, requests)
response = handler(call, requests); : handler(call, _toSingleFuture(requests));
} else {
response = handler(call, _toSingleFuture(requests));
}
return response.asStream(); return response.asStream();
} }
} }
Future<Q> _toSingleFuture(Stream<Q> stream) { Future<Q> _toSingleFuture(Stream<Q> stream) {
Q _ensureOnlyOneRequest(Q previous, Q element) { Q _ensureOnlyOneRequest(Q? previous, Q element) {
if (previous != null) { if (previous != null) {
throw GrpcError.unimplemented('More than one request received'); throw GrpcError.unimplemented('More than one request received');
} }
return element; return element;
} }
Q _ensureOneRequest(Q value) { Q _ensureOneRequest(Q? value) {
if (value == null) throw GrpcError.unimplemented('No requests received'); if (value == null) throw GrpcError.unimplemented('No requests received');
return value; return value;
} }
final future = 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 // Make sure errors on the future aren't unhandled, but return the original
// future so the request handler can also get the error. // future so the request handler can also get the error.
future.catchError((_) {}); _awaitAndCatch(future);
return future; return future;
} }
void _awaitAndCatch<Q>(Future<Q> f) async {
try {
await f;
} catch (_) {}
}
} }
/// Definition of a gRPC service. /// Definition of a gRPC service.
@ -104,5 +107,5 @@ abstract class Service {
/// metadata from the client. /// metadata from the client.
void $onMetadata(ServiceCall context) {} void $onMetadata(ServiceCall context) {}
ServiceMethod $lookupMethod(String name) => _$methods[name]; ServiceMethod? $lookupMethod(String name) => _$methods[name];
} }

View File

@ -36,7 +36,7 @@ class IdentityCodec implements Codec {
const IdentityCodec(); const IdentityCodec();
@override @override
String get encodingName => "identity"; final encodingName = 'identity';
@override @override
List<int> compress(List<int> data) { List<int> compress(List<int> data) {
@ -54,11 +54,11 @@ class GzipCodec implements Codec {
const GzipCodec(); const GzipCodec();
@override @override
String get encodingName => "gzip"; final encodingName = "gzip";
@override @override
List<int> compress(List<int> data) { List<int> compress(List<int> data) {
return GZipEncoder().encode(data); return GZipEncoder().encode(data)!;
} }
@override @override

View File

@ -18,8 +18,7 @@ import 'codec.dart';
/// Encloses classes related to the compression and decompression of messages. /// Encloses classes related to the compression and decompression of messages.
class CodecRegistry { class CodecRegistry {
CodecRegistry({List<Codec> codecs = const [IdentityCodec()]}) CodecRegistry({List<Codec> codecs = const [IdentityCodec()]})
: assert(codecs != null), : _codecs = {for (var codec in codecs) codec.encodingName: codec},
_codecs = Map.fromIterable(codecs, key: (c) => c.encodingName),
_supportedEncodings = codecs.map((c) { _supportedEncodings = codecs.map((c) {
if (c.encodingName.contains(',')) { if (c.encodingName.contains(',')) {
throw ArgumentError.value(c.encodingName, 'codecs', throw ArgumentError.value(c.encodingName, 'codecs',
@ -33,16 +32,14 @@ class CodecRegistry {
} }
} }
factory CodecRegistry.empty() { factory CodecRegistry.empty() => CodecRegistry(codecs: []);
return CodecRegistry(codecs: []);
}
/// Key refers to the `encodingName` param from the [Codec]. /// Key refers to the `encodingName` param from the [Codec].
final Map<String, Codec> _codecs; final Map<String, Codec> _codecs;
final String _supportedEncodings; final String _supportedEncodings;
Codec lookup(String codecName) { Codec? lookup(String codecName) {
return _codecs[codecName]; return _codecs[codecName];
} }

View File

@ -33,32 +33,36 @@ class GrpcMetadata extends GrpcMessage {
class GrpcData extends GrpcMessage { class GrpcData extends GrpcMessage {
final List<int> data; final List<int> data;
final bool isCompressed; final bool isCompressed;
GrpcData(this.data, {this.isCompressed}) : assert(data != null); GrpcData(this.data, {required this.isCompressed}) {
ArgumentError.checkNotNull(data);
}
@override @override
String toString() => 'gRPC Data (${data.length} bytes)'; String toString() => 'gRPC Data (${data.length} bytes)';
} }
class GrpcMessageSink extends Sink<GrpcMessage> { class GrpcMessageSink extends Sink<GrpcMessage> {
GrpcMessage message; late final GrpcMessage message;
bool _messageReceived = false;
@override @override
void add(GrpcMessage data) { void add(GrpcMessage data) {
if (message != null) { if (_messageReceived) {
throw StateError('Too many messages received!'); throw StateError('Too many messages received!');
} }
message = data; message = data;
_messageReceived = true;
} }
@override @override
void close() { void close() {
if (message == null) { if (!_messageReceived) {
throw StateError('No messages received!'); throw StateError('No messages received!');
} }
} }
} }
List<int> frame(List<int> rawPayload, [Codec codec]) { List<int> frame(List<int> rawPayload, [Codec? codec]) {
final compressedPayload = final compressedPayload =
codec == null ? rawPayload : codec.compress(rawPayload); codec == null ? rawPayload : codec.compress(rawPayload);
final payloadLength = compressedPayload.length; final payloadLength = compressedPayload.length;
@ -71,9 +75,9 @@ List<int> frame(List<int> rawPayload, [Codec codec]) {
} }
StreamTransformer<GrpcMessage, GrpcMessage> grpcDecompressor({ StreamTransformer<GrpcMessage, GrpcMessage> grpcDecompressor({
CodecRegistry codecRegistry, CodecRegistry? codecRegistry,
}) { }) {
Codec codec; Codec? codec;
return StreamTransformer<GrpcMessage, GrpcMessage>.fromHandlers( return StreamTransformer<GrpcMessage, GrpcMessage>.fromHandlers(
handleData: (GrpcMessage value, EventSink<GrpcMessage> sink) { handleData: (GrpcMessage value, EventSink<GrpcMessage> sink) {
if (value is GrpcData && value.isCompressed) { if (value is GrpcData && value.isCompressed) {
@ -83,12 +87,12 @@ StreamTransformer<GrpcMessage, GrpcMessage> grpcDecompressor({
); );
return; return;
} }
sink.add(GrpcData(codec.decompress(value.data), isCompressed: false)); sink.add(GrpcData(codec!.decompress(value.data), isCompressed: false));
return; return;
} }
if (value is GrpcMetadata && value.metadata.containsKey('grpc-encoding')) { 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); sink.add(value);
}); });

View File

@ -16,12 +16,12 @@
import 'dart:developer'; import 'dart:developer';
typedef TimelineTask TimelineTaskFactory( typedef TimelineTask TimelineTaskFactory(
{String filterKey, TimelineTask parent}); {String? filterKey, TimelineTask? parent});
TimelineTaskFactory timelineTaskFactory = _defaultTimelineTaskFactory; TimelineTaskFactory timelineTaskFactory = _defaultTimelineTaskFactory;
TimelineTask _defaultTimelineTaskFactory( TimelineTask _defaultTimelineTaskFactory(
{String filterKey, TimelineTask parent}) => {String? filterKey, TimelineTask? parent}) =>
TimelineTask(filterKey: filterKey, parent: parent); TimelineTask(filterKey: filterKey, parent: parent);
const String clientTimelineFilterKey = 'grpc/client'; const String clientTimelineFilterKey = 'grpc/client';

View File

@ -125,9 +125,9 @@ class StatusCode {
class GrpcError implements Exception { class GrpcError implements Exception {
final int code; final int code;
final String codeName; final String codeName;
final String message; final String? message;
final List<GeneratedMessage> details; final Object? rawResponse;
final Object rawResponse; final List<GeneratedMessage>? details;
/// Custom error code. /// Custom error code.
GrpcError.custom(this.code, [this.message, this.details, this.rawResponse]) GrpcError.custom(this.code, [this.message, this.details, this.rawResponse])

View File

@ -58,7 +58,7 @@ class _GrpcMessageConversionSink extends ChunkedConversionSink<StreamMessage> {
final Sink<GrpcMessage> _out; final Sink<GrpcMessage> _out;
final _dataHeader = Uint8List(5); final _dataHeader = Uint8List(5);
Uint8List _data; Uint8List? _data;
int _dataOffset = 0; int _dataOffset = 0;
_GrpcMessageConversionSink(this._out); _GrpcMessageConversionSink(this._out);
@ -87,17 +87,17 @@ class _GrpcMessageConversionSink extends ChunkedConversionSink<StreamMessage> {
} }
if (_data != null) { if (_data != null) {
// Reading data. // Reading data.
final dataRemaining = _data.lengthInBytes - _dataOffset; final dataRemaining = _data!.lengthInBytes - _dataOffset;
if (dataRemaining > 0) { if (dataRemaining > 0) {
final chunkRemaining = chunkLength - chunkReadOffset; final chunkRemaining = chunkLength - chunkReadOffset;
final toCopy = min(dataRemaining, chunkRemaining); final toCopy = min(dataRemaining, chunkRemaining);
_data.setRange( _data!.setRange(
_dataOffset, _dataOffset + toCopy, chunkData, chunkReadOffset); _dataOffset, _dataOffset + toCopy, chunkData, chunkReadOffset);
_dataOffset += toCopy; _dataOffset += toCopy;
chunkReadOffset += toCopy; chunkReadOffset += toCopy;
} }
if (_dataOffset == _data.lengthInBytes) { if (_dataOffset == _data!.lengthInBytes) {
_out.add(GrpcData(_data, _out.add(GrpcData(_data!,
isCompressed: _dataHeader.buffer.asByteData().getUint8(0) != 0)); isCompressed: _dataHeader.buffer.asByteData().getUint8(0) != 0));
_data = null; _data = null;
_dataOffset = 0; _dataOffset = 0;

View File

@ -17,7 +17,6 @@
// Mostly inspired by grpc-java implementation. // Mostly inspired by grpc-java implementation.
// TODO(jakobr): Modify to match grpc/core implementation instead. // TODO(jakobr): Modify to match grpc/core implementation instead.
String toTimeoutString(Duration duration) { String toTimeoutString(Duration duration) {
if (duration == null) return null;
const cutoff = 100000; const cutoff = 100000;
final timeout = duration.inMicroseconds; final timeout = duration.inMicroseconds;
if (timeout < 0) { if (timeout < 0) {
@ -38,7 +37,7 @@ String toTimeoutString(Duration duration) {
/// Convert [timeout] from grpc-timeout header string format to [Duration]. /// Convert [timeout] from grpc-timeout header string format to [Duration].
/// Returns [null] if [timeout] is not correctly formatted. /// Returns [null] if [timeout] is not correctly formatted.
Duration fromTimeoutString(String timeout) { Duration? fromTimeoutString(String? timeout) {
if (timeout == null) return null; if (timeout == null) return null;
if (timeout.length < 2) return null; if (timeout.length < 2) return null;
final value = int.tryParse(timeout.substring(0, timeout.length - 1)); final value = int.tryParse(timeout.substring(0, timeout.length - 1));

View File

@ -1,28 +1,42 @@
name: grpc name: grpc
description: Dart implementation of gRPC, a high performance, open-source universal RPC framework. 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 homepage: https://github.com/dart-lang/grpc-dart
environment: environment:
sdk: '>=2.8.0 <3.0.0' sdk: '>=2.12.0-0 <3.0.0'
dependencies: dependencies:
archive: ^2.0.13 archive: ^3.0.0-nullsafety
async: ^2.2.0 async: ^2.2.0
crypto: ^2.1.4 crypto: ^3.0.0-nullsafety
fixnum: ^0.10.11 fixnum: ^1.0.0-nullsafety
googleapis_auth: ^0.2.7 googleapis_auth: ^0.2.7
meta: ^1.1.6 meta: ^1.1.6
http: ^0.12.0 http: ^0.12.0
http2: ^1.0.0 http2: ^2.0.0-nullsafety
protobuf: ^1.0.1 protobuf: ^2.0.0-nullsafety.1
dev_dependencies: dev_dependencies:
build_runner: ^1.5.2 build_runner: ^1.11.0
build_test: ^0.10.8 build_test: ^1.3.4
build_web_compilers: ^2.1.1 mockito: ^5.0.0-nullsafety.5
mockito: ^4.1.0 test: ^1.16.0-nullsafety.16
test: ^1.6.4
stream_channel: ^2.0.0 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

View File

@ -16,7 +16,7 @@ class TestClient extends grpc.Client {
(List<int> value) => value[0]); (List<int> value) => value[0]);
TestClient(ClientChannel channel) : super(channel); 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), return $createStreamingCall(_$stream, Stream.value(request),
options: options); options: options);
} }
@ -56,7 +56,7 @@ main() async {
final channel = FixedConnectionClientChannel(Http2ClientConnection( final channel = FixedConnectionClientChannel(Http2ClientConnection(
address, address,
server.port, server.port!,
grpc.ChannelOptions( grpc.ChannelOptions(
idleTimeout: Duration(minutes: 1), idleTimeout: Duration(minutes: 1),
// Short delay to test that it will time out. // Short delay to test that it will time out.
@ -84,7 +84,7 @@ main() async {
final channel = FixedConnectionClientChannel(Http2ClientConnection( final channel = FixedConnectionClientChannel(Http2ClientConnection(
address, address,
server.port, server.port!,
grpc.ChannelOptions(credentials: grpc.ChannelCredentials.insecure()))); grpc.ChannelOptions(credentials: grpc.ChannelCredentials.insecure())));
final states = <grpc.ConnectionState>[]; final states = <grpc.ConnectionState>[];
channel.clientConnection.onStateChanged = channel.clientConnection.onStateChanged =

View File

@ -31,7 +31,7 @@ import '../src/utils.dart';
void main() { void main() {
const dummyValue = 0; const dummyValue = 0;
ClientHarness harness; late ClientHarness harness;
setUp(() { setUp(() {
harness = ClientHarness()..setUp(); harness = ClientHarness()..setUp();
@ -399,8 +399,8 @@ void main() {
test('Connection errors are reported', () async { test('Connection errors are reported', () async {
final connectionStates = <ConnectionState>[]; final connectionStates = <ConnectionState>[];
harness.connection.connectionError = 'Connection error'; harness.connection!.connectionError = 'Connection error';
harness.connection.onStateChanged = (connection) { harness.connection!.onStateChanged = (connection) {
final state = connection.state; final state = connection.state;
connectionStates.add(state); connectionStates.add(state);
}; };
@ -418,7 +418,7 @@ void main() {
test('Connections time out if idle', () async { test('Connections time out if idle', () async {
final done = Completer(); final done = Completer();
final connectionStates = <ConnectionState>[]; final connectionStates = <ConnectionState>[];
harness.connection.onStateChanged = (connection) { harness.connection!.onStateChanged = (connection) {
final state = connection.state; final state = connection.state;
connectionStates.add(state); connectionStates.add(state);
if (state == ConnectionState.idle) done.complete(); if (state == ConnectionState.idle) done.complete();
@ -464,7 +464,7 @@ void main() {
credentials: ChannelCredentials.insecure(authority: 'myauthority.com')); credentials: ChannelCredentials.insecure(authority: 'myauthority.com'));
expect(Http2ClientConnection('localhost', 8080, channelOptions).authority, expect(Http2ClientConnection('localhost', 8080, channelOptions).authority,
'myauthority.com'); 'myauthority.com');
expect(Http2ClientConnection('localhost', null, channelOptions).authority, expect(Http2ClientConnection('localhost', 443, channelOptions).authority,
'myauthority.com'); 'myauthority.com');
}); });
@ -485,12 +485,6 @@ void main() {
expect(decodedDetails.length, 0); 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', () { test('parseGeneratedMessage should parse out a valid Any type', () {
final status = Status.fromBuffer(base64Url.decode( final status = Status.fromBuffer(base64Url.decode(
'CAMSEGFtb3VudCB0b28gc21hbGwafgopdHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUucnBjLkJhZFJlcXVlc3QSUQpPCgZhbW91bnQSRVRoZSByZXF1aXJlZCBjdXJyZW5jeSBjb252ZXJzaW9uIHdvdWxkIHJlc3VsdCBpbiBhIHplcm8gdmFsdWUgcGF5bWVudA==')); 'CAMSEGFtb3VudCB0b28gc21hbGwafgopdHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUucnBjLkJhZFJlcXVlc3QSUQpPCgZhbW91bnQSRVRoZSByZXF1aXJlZCBjdXJyZW5jeSBjb252ZXJzaW9uIHdvdWxkIHJlc3VsdCBpbiBhIHplcm8gdmFsdWUgcGF5bWVudA=='));

View File

@ -26,7 +26,7 @@ import '../src/utils.dart';
void main() { void main() {
const dummyValue = 0; const dummyValue = 0;
ClientTransportConnectorHarness harness; late ClientTransportConnectorHarness harness;
setUp(() { setUp(() {
harness = ClientTransportConnectorHarness()..setUp(); harness = ClientTransportConnectorHarness()..setUp();
@ -349,8 +349,8 @@ void main() {
test('Connection errors are reported', () async { test('Connection errors are reported', () async {
final connectionStates = <ConnectionState>[]; final connectionStates = <ConnectionState>[];
harness.connection.connectionError = 'Connection error'; harness.connection!.connectionError = 'Connection error';
harness.connection.onStateChanged = (connection) { harness.connection!.onStateChanged = (connection) {
final state = connection.state; final state = connection.state;
connectionStates.add(state); connectionStates.add(state);
}; };
@ -368,7 +368,7 @@ void main() {
test('Connections time out if idle', () async { test('Connections time out if idle', () async {
final done = Completer(); final done = Completer();
final connectionStates = <ConnectionState>[]; final connectionStates = <ConnectionState>[];
harness.connection.onStateChanged = (connection) { harness.connection!.onStateChanged = (connection) {
final state = connection.state; final state = connection.state;
connectionStates.add(state); connectionStates.add(state);
if (state == ConnectionState.idle) done.complete(); if (state == ConnectionState.idle) done.complete();
@ -414,7 +414,7 @@ void main() {
credentials: ChannelCredentials.insecure(authority: 'myauthority.com')); credentials: ChannelCredentials.insecure(authority: 'myauthority.com'));
expect(Http2ClientConnection('localhost', 8080, channelOptions).authority, expect(Http2ClientConnection('localhost', 8080, channelOptions).authority,
'myauthority.com'); 'myauthority.com');
expect(Http2ClientConnection('localhost', null, channelOptions).authority, expect(Http2ClientConnection('localhost', 443, channelOptions).authority,
'myauthority.com'); 'myauthority.com');
}); });
} }

View File

@ -18,16 +18,22 @@ import 'dart:async';
import 'dart:html'; import 'dart:html';
import 'package:async/async.dart';
import 'package:grpc/src/client/call.dart'; import 'package:grpc/src/client/call.dart';
import 'package:grpc/src/client/transport/xhr_transport.dart'; import 'package:grpc/src/client/transport/xhr_transport.dart';
import 'package:grpc/src/shared/message.dart'; import 'package:grpc/src/shared/message.dart';
import 'package:grpc/src/shared/status.dart'; import 'package:grpc/src/shared/status.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:stream_transform/stream_transform.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
final readyStateChangeEvent =
Event('readystatechange', canBubble: false, cancelable: false);
final progressEvent = ProgressEvent('onloadstart');
class MockHttpRequest extends Mock implements HttpRequest { class MockHttpRequest extends Mock implements HttpRequest {
MockHttpRequest({int code}) : status = code ?? 200; MockHttpRequest({int? code}) : status = code ?? 200;
// ignore: close_sinks // ignore: close_sinks
StreamController<Event> readyStateChangeController = StreamController<Event> readyStateChangeController =
StreamController<Event>(); StreamController<Event>();
@ -46,14 +52,20 @@ class MockHttpRequest extends Mock implements HttpRequest {
@override @override
final int status; 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 { class MockXhrClientConnection extends XhrClientConnection {
MockXhrClientConnection({int code}) MockXhrClientConnection({int? code})
: _statusCode = code ?? 200, : _statusCode = code ?? 200,
super(Uri.parse('test:8080')); super(Uri.parse('test:8080'));
MockHttpRequest latestRequest; late MockHttpRequest latestRequest;
final int _statusCode; final int _statusCode;
@override @override
@ -199,28 +211,40 @@ void main() {
}); });
test('Stream handles headers properly', () async { test('Stream handles headers properly', () async {
final metadata = <String, String>{ final responseHeaders = {
'parameter_1': 'value_1', 'parameter_1': 'value_1',
'parameter_2': 'value_2' 'parameter_2': 'value_2'
}; };
final transport = MockXhrClientConnection(); final transport = MockXhrClientConnection();
final stream = transport.makeRequest('test_path', Duration(seconds: 10), final stream = transport.makeRequest('test_path', Duration(seconds: 10), {},
metadata, (error, _) => fail(error.toString())); (error, _) => fail(error.toString()));
stream.incomingMessages.listen((message) { when(transport.latestRequest.responseHeaders).thenReturn(responseHeaders);
expect(message, TypeMatcher<GrpcMetadata>()); when(transport.latestRequest.getResponseHeader('Content-Type'))
if (message is GrpcMetadata) { .thenReturn('application/grpc+proto');
message.metadata.forEach((key, value) { when(transport.latestRequest.response)
expect(value, metadata[key]); .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 { 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_1': 'value_1',
'trailer_2': 'value_2' 'trailer_2': 'value_2'
}; };
@ -228,31 +252,37 @@ void main() {
final connection = MockXhrClientConnection(); final connection = MockXhrClientConnection();
final stream = connection.makeRequest('test_path', Duration(seconds: 10), 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}') .map((e) => '${e.key}:${e.value}')
.join('\r\n') .join('\r\n')
.codeUnits); .codeUnits);
encodedTrailers[0] = 0x80; // Mark this frame as trailers. encodedTrailers[0] = 0x80; // Mark this frame as trailers.
final encodedString = String.fromCharCodes(encodedTrailers); 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')) when(connection.latestRequest.getResponseHeader('Content-Type'))
.thenReturn('application/grpc+proto'); .thenReturn('application/grpc+proto');
when(connection.latestRequest.responseHeaders).thenReturn({}); when(connection.latestRequest.responseHeaders).thenReturn({});
when(connection.latestRequest.readyState)
.thenReturn(HttpRequest.HEADERS_RECEIVED);
when(connection.latestRequest.response).thenReturn(encodedString); 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 { test('Stream handles empty trailers properly', () async {
@ -265,24 +295,32 @@ void main() {
encoded[0] = 0x80; // Mark this frame as trailers. encoded[0] = 0x80; // Mark this frame as trailers.
final encodedString = String.fromCharCodes(encoded); 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')) when(connection.latestRequest.getResponseHeader('Content-Type'))
.thenReturn('application/grpc+proto'); .thenReturn('application/grpc+proto');
when(connection.latestRequest.responseHeaders).thenReturn({}); when(connection.latestRequest.responseHeaders).thenReturn({});
when(connection.latestRequest.readyState)
.thenReturn(HttpRequest.HEADERS_RECEIVED);
when(connection.latestRequest.response).thenReturn(encodedString); 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 { test('Stream deserializes data properly', () async {
final metadata = <String, String>{ final requestHeaders = <String, String>{
'parameter_1': 'value_1', 'parameter_1': 'value_1',
'parameter_2': 'value_2' 'parameter_2': 'value_2'
}; };
@ -290,57 +328,50 @@ void main() {
final connection = MockXhrClientConnection(); final connection = MockXhrClientConnection();
final stream = connection.makeRequest('test_path', Duration(seconds: 10), 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 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')) when(connection.latestRequest.getResponseHeader('Content-Type'))
.thenReturn('application/grpc+proto'); .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) when(connection.latestRequest.readyState)
.thenReturn(HttpRequest.HEADERS_RECEIVED); .thenAnswer((_) => readyStates.removeAt(0));
when(connection.latestRequest.response).thenReturn(encodedString); connection.latestRequest.readyStateChangeController
connection.latestRequest.readyStateChangeController.add(null); .add(readyStateChangeEvent);
connection.latestRequest.progressController.add(null); 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 { 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 connection = MockXhrClientConnection(code: 400);
final errorStream = StreamController<GrpcError>(); final errors = <GrpcError>[];
connection.makeRequest('test_path', Duration(seconds: 10), metadata, // The incoming messages stream never completes when there's an error, so
(e, _) => errorStream.add(e)); // using completer.
const errorDetails = "error details"; final errorReceived = Completer<void>();
int count = 0; connection.makeRequest('test_path', Duration(seconds: 10), {}, (e, _) {
errorReceived.complete();
errorStream.stream.listen((error) { errors.add(e);
expect(
error,
TypeMatcher<GrpcError>()
.having((e) => e.rawResponse, 'rawResponse', errorDetails));
count++;
if (count == 2) {
errorStream.close();
}
}); });
const errorDetails = "error details";
when(connection.latestRequest.getResponseHeader('Content-Type')) when(connection.latestRequest.getResponseHeader('Content-Type'))
.thenReturn('application/grpc+proto'); .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.readyState).thenReturn(HttpRequest.DONE);
when(connection.latestRequest.responseText).thenReturn(errorDetails); 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 { test('Stream recieves multiple messages', () async {
@ -358,40 +389,44 @@ void main() {
List<int>.filled(10, 224), List<int>.filled(10, 224),
List<int>.filled(5, 124) List<int>.filled(5, 124)
]; ];
final encoded = data.map((d) => frame(d)); final encodedStrings =
final encodedStrings = encoded.map((e) => String.fromCharCodes(e)).toList(); data.map((d) => String.fromCharCodes(frame(d))).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));
when(connection.latestRequest.getResponseHeader('Content-Type')) when(connection.latestRequest.getResponseHeader('Content-Type'))
.thenReturn('application/grpc+proto'); .thenReturn('application/grpc+proto');
when(connection.latestRequest.responseHeaders).thenReturn(metadata); when(connection.latestRequest.responseHeaders).thenReturn(metadata);
when(connection.latestRequest.readyState) when(connection.latestRequest.readyState)
.thenReturn(HttpRequest.HEADERS_RECEIVED); .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 // At first invocation the response should be the the first message, after
when(connection.latestRequest.response) // that first + last messages.
.thenAnswer((_) => encodedStrings[0] + encodedStrings[1]); var first = true;
connection.latestRequest.progressController.add(null); 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);
}); });
} }

View File

@ -25,7 +25,7 @@ import 'src/server_utils.dart';
void main() { void main() {
const dummyValue = 17; const dummyValue = 17;
ConnectionServerHarness harness; late ConnectionServerHarness harness;
setUp(() { setUp(() {
harness = ConnectionServerHarness()..setUp(); harness = ConnectionServerHarness()..setUp();
@ -257,7 +257,7 @@ void main() {
harness harness
..service.unaryHandler = methodHandler ..service.unaryHandler = methodHandler
..fromServer.stream.listen(expectAsync1((_) {}, count: 0), ..fromServer.stream.listen(expectAsync1((_) {}, count: 0),
onError: expectAsync1((error) { onError: expectAsync1((dynamic error) {
expect(error, 'TERMINATED'); expect(error, 'TERMINATED');
}, count: 1), }, count: 1),
onDone: expectAsync0(() {}, count: 1)) onDone: expectAsync0(() {}, count: 1))

View File

@ -11,7 +11,7 @@ import 'package:grpc/grpc_web.dart';
import 'src/generated/echo.pbgrpc.dart'; import 'src/generated/echo.pbgrpc.dart';
void main() { void main() {
GrpcWebServer server; late GrpcWebServer server;
setUpAll(() async { setUpAll(() async {
server = await GrpcWebServer.start(); server = await GrpcWebServer.start();

View File

@ -13,6 +13,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
@TestOn('vm') @TestOn('vm')
import 'dart:io'; import 'dart:io';
import 'package:grpc/src/client/transport/http2_credentials.dart'; import 'package:grpc/src/client/transport/http2_credentials.dart';

View File

@ -16,7 +16,7 @@ class TestClient extends Client {
(int value) => [value], (List<int> value) => value[0]); (int value) => [value], (List<int> value) => value[0]);
TestClient(api.ClientChannel channel) : super(channel); 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), return $createStreamingCall(_$stream, Stream.value(request),
options: options); options: options);
} }
@ -66,7 +66,7 @@ main() async {
final channel = FixedConnectionClientChannel(Http2ClientConnection( final channel = FixedConnectionClientChannel(Http2ClientConnection(
address, address,
server.port, server.port!,
ChannelOptions(credentials: ChannelCredentials.insecure()), ChannelOptions(credentials: ChannelCredentials.insecure()),
)); ));
final testClient = TestClient(channel); final testClient = TestClient(channel);
@ -83,7 +83,7 @@ main() async {
final channel = FixedConnectionClientChannel(Http2ClientConnection( final channel = FixedConnectionClientChannel(Http2ClientConnection(
address, address,
server.port, server.port!,
ChannelOptions( ChannelOptions(
credentials: ChannelCredentials.insecure(), credentials: ChannelCredentials.insecure(),
codecRegistry: CodecRegistry(codecs: const [GzipCodec()]), codecRegistry: CodecRegistry(codecs: const [GzipCodec()]),
@ -111,7 +111,7 @@ main() async {
final channel = FixedConnectionClientChannel(Http2ClientConnection( final channel = FixedConnectionClientChannel(Http2ClientConnection(
address, address,
server.port, server.port!,
ChannelOptions( ChannelOptions(
credentials: ChannelCredentials.secure( credentials: ChannelCredentials.secure(
certificates: File('test/data/localhost.crt').readAsBytesSync(), certificates: File('test/data/localhost.crt').readAsBytesSync(),
@ -129,7 +129,7 @@ main() async {
final channel = FixedConnectionClientChannel(Http2ClientConnection( final channel = FixedConnectionClientChannel(Http2ClientConnection(
'localhost', 'localhost',
server.port, server.port!,
ChannelOptions(credentials: ChannelCredentials.insecure()), ChannelOptions(credentials: ChannelCredentials.insecure()),
)); ));
final testClient = TestClient(channel); final testClient = TestClient(channel);
@ -145,7 +145,7 @@ main() async {
final channel = FixedConnectionClientChannel(Http2ClientConnection( final channel = FixedConnectionClientChannel(Http2ClientConnection(
'localhost', 'localhost',
server.port, server.port!,
ChannelOptions(credentials: ChannelCredentials.insecure()), ChannelOptions(credentials: ChannelCredentials.insecure()),
)); ));
final testClient = TestClient(channel); final testClient = TestClient(channel);

View File

@ -14,7 +14,7 @@ class TestClient extends grpc.Client {
TestClient(grpc.ClientChannel channel) : super(channel); TestClient(grpc.ClientChannel channel) : super(channel);
grpc.ResponseStream<int> infiniteStream(int request, grpc.ResponseStream<int> infiniteStream(int request,
{grpc.CallOptions options}) { {grpc.CallOptions? options}) {
return $createStreamingCall(_$infiniteStream, Stream.value(request), return $createStreamingCall(_$infiniteStream, Stream.value(request),
options: options); options: options);
} }
@ -24,12 +24,13 @@ class TestService extends grpc.Service {
String get $name => 'test.TestService'; String get $name => 'test.TestService';
final void Function() finallyCallback; final void Function() finallyCallback;
TestService({this.finallyCallback}) { TestService({required this.finallyCallback}) {
$addMethod(grpc.ServiceMethod<int, int>('infiniteStream', infiniteStream, $addMethod(grpc.ServiceMethod<int, int>('infiniteStream', infiniteStream,
false, true, (List<int> value) => value[0], (int value) => [value])); 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; int count = await request;
try { try {
while (true) { while (true) {
@ -50,7 +51,8 @@ class ClientData {
final InternetAddress address; final InternetAddress address;
final int port; final int port;
final SendPort sendPort; final SendPort sendPort;
ClientData({this.address, this.port, this.sendPort}); ClientData(
{required this.address, required this.port, required this.sendPort});
} }
void client(clientData) async { void client(clientData) async {
@ -73,7 +75,7 @@ main() async {
"the client interrupting the connection does not crash the server", "the client interrupting the connection does not crash the server",
(address) async { (address) async {
// interrrupt the connect of client, the server does not crash. // interrrupt the connect of client, the server does not crash.
grpc.Server server; late grpc.Server server;
server = grpc.Server([ server = grpc.Server([
TestService( TestService(
finallyCallback: expectAsync0(() { finallyCallback: expectAsync0(() {
@ -86,7 +88,7 @@ main() async {
client, client,
ClientData( ClientData(
address: address, address: address,
port: server.port, port: server.port!,
sendPort: receivePort.sendPort)); sendPort: receivePort.sendPort));
receivePort.listen(expectAsync1((e) { receivePort.listen(expectAsync1((e) {
expect(e, isA<grpc.GrpcError>()); expect(e, isA<grpc.GrpcError>());

View File

@ -25,7 +25,7 @@ import 'src/server_utils.dart';
void main() { void main() {
const dummyValue = 17; const dummyValue = 17;
ServerHarness harness; late ServerHarness harness;
setUp(() { setUp(() {
harness = ServerHarness()..setUp(); harness = ServerHarness()..setUp();
@ -271,7 +271,7 @@ void main() {
harness harness
..service.unaryHandler = methodHandler ..service.unaryHandler = methodHandler
..fromServer.stream.listen(expectAsync1((_) {}, count: 0), ..fromServer.stream.listen(expectAsync1((_) {}, count: 0),
onError: expectAsync1((error) { onError: expectAsync1((dynamic error) {
expect(error, 'TERMINATED'); expect(error, 'TERMINATED');
}, count: 1), }, count: 1),
onDone: expectAsync0(() {}, count: 1)) onDone: expectAsync0(() {}, count: 1))

View File

@ -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/client/http2_connection.dart';
import 'package:grpc/src/shared/message.dart'; import 'package:grpc/src/shared/message.dart';
import 'package:http2/transport.dart'; import 'package:http2/transport.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'client_utils.mocks.dart';
import 'utils.dart'; import 'utils.dart';
class MockTransport extends Mock implements ClientTransportConnection {} @GenerateMocks([ClientTransportConnection, ClientTransportStream])
class MockStream extends Mock implements ClientTransportStream {}
class FakeConnection extends Http2ClientConnection { class FakeConnection extends Http2ClientConnection {
final ClientTransportConnection transport; 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 { class FakeChannelOptions implements ChannelOptions {
ChannelCredentials credentials = const ChannelCredentials.secure(); ChannelCredentials credentials = const ChannelCredentials.secure();
@ -97,16 +96,16 @@ class FakeClientConnectorChannel extends ClientTransportConnectorChannel {
typedef ServerMessageHandler = void Function(StreamMessage message); typedef ServerMessageHandler = void Function(StreamMessage message);
class TestClient extends Client { class TestClient extends Client {
ClientMethod<int, int> _$unary; late ClientMethod<int, int> _$unary;
ClientMethod<int, int> _$clientStreaming; late ClientMethod<int, int> _$clientStreaming;
ClientMethod<int, int> _$serverStreaming; late ClientMethod<int, int> _$serverStreaming;
ClientMethod<int, int> _$bidirectional; late ClientMethod<int, int> _$bidirectional;
final int Function(List<int> value) decode; final int Function(List<int> value) decode;
TestClient(base.ClientChannel channel, TestClient(base.ClientChannel channel,
{CallOptions options, {CallOptions? options,
Iterable<ClientInterceptor> interceptors, Iterable<ClientInterceptor>? interceptors,
this.decode: mockDecode}) this.decode: mockDecode})
: super(channel, options: options, interceptors: interceptors) { : super(channel, options: options, interceptors: interceptors) {
_$unary = ClientMethod<int, int>('/Test/Unary', mockEncode, decode); _$unary = ClientMethod<int, int>('/Test/Unary', mockEncode, decode);
@ -118,34 +117,34 @@ class TestClient extends Client {
ClientMethod<int, int>('/Test/Bidirectional', mockEncode, decode); 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); return $createUnaryCall(_$unary, request, options: options);
} }
ResponseFuture<int> clientStreaming(Stream<int> request, ResponseFuture<int> clientStreaming(Stream<int> request,
{CallOptions options}) { {CallOptions? options}) {
return $createStreamingCall(_$clientStreaming, request, options: options) return $createStreamingCall(_$clientStreaming, request, options: options)
.single; .single;
} }
ResponseStream<int> serverStreaming(int request, {CallOptions options}) { ResponseStream<int> serverStreaming(int request, {CallOptions? options}) {
return $createStreamingCall(_$serverStreaming, Stream.value(request), return $createStreamingCall(_$serverStreaming, Stream.value(request),
options: options); options: options);
} }
ResponseStream<int> bidirectional(Stream<int> request, ResponseStream<int> bidirectional(Stream<int> request,
{CallOptions options}) { {CallOptions? options}) {
return $createStreamingCall(_$bidirectional, request, options: options); return $createStreamingCall(_$bidirectional, request, options: options);
} }
} }
class ClientHarness extends _Harness { class ClientHarness extends _Harness {
FakeConnection connection; FakeConnection? connection;
@override @override
FakeChannel createChannel() { FakeChannel createChannel() {
connection = FakeConnection('test', transport, channelOptions); connection = FakeConnection('test', transport, channelOptions);
return FakeChannel('test', connection, channelOptions); return FakeChannel('test', connection!, channelOptions);
} }
@override @override
@ -153,14 +152,14 @@ class ClientHarness extends _Harness {
} }
class ClientTransportConnectorHarness extends _Harness { class ClientTransportConnectorHarness extends _Harness {
FakeClientTransportConnection connection; FakeClientTransportConnection? connection;
ClientTransportConnector connector; late ClientTransportConnector connector;
@override @override
FakeClientConnectorChannel createChannel() { FakeClientConnectorChannel createChannel() {
connector = FakeClientTransportConnector(transport); connector = FakeClientTransportConnector(transport);
connection = FakeClientTransportConnection(connector, channelOptions); connection = FakeClientTransportConnection(connector, channelOptions);
return FakeClientConnectorChannel(connector, connection, channelOptions); return FakeClientConnectorChannel(connector, connection!, channelOptions);
} }
@override @override
@ -187,27 +186,27 @@ class FakeClientTransportConnector extends ClientTransportConnector {
} }
abstract class _Harness { abstract class _Harness {
MockTransport transport; late MockClientTransportConnection transport;
base.ClientChannel channel; late base.ClientChannel channel;
FakeChannelOptions channelOptions; late FakeChannelOptions channelOptions;
MockStream stream; late MockClientTransportStream stream;
StreamController<StreamMessage> fromClient; late StreamController<StreamMessage> fromClient;
StreamController<StreamMessage> toClient; late StreamController<StreamMessage> toClient;
Iterable<ClientInterceptor> interceptors; Iterable<ClientInterceptor>? interceptors;
TestClient client; late TestClient client;
base.ClientChannel createChannel(); base.ClientChannel createChannel();
String get expectedAuthority; String get expectedAuthority;
void setUp() { void setUp() {
transport = MockTransport(); transport = MockClientTransportConnection();
channelOptions = FakeChannelOptions(); channelOptions = FakeChannelOptions();
channel = createChannel(); channel = createChannel();
stream = MockStream(); stream = MockClientTransportStream();
fromClient = StreamController(); fromClient = StreamController();
toClient = StreamController(); toClient = StreamController();
when(transport.makeRequest(any, endStream: anyNamed('endStream'))) when(transport.makeRequest(any, endStream: anyNamed('endStream')))
@ -216,6 +215,8 @@ abstract class _Harness {
when(transport.isOpen).thenReturn(true); when(transport.isOpen).thenReturn(true);
when(stream.outgoingMessages).thenReturn(fromClient.sink); when(stream.outgoingMessages).thenReturn(fromClient.sink);
when(stream.incomingMessages).thenAnswer((_) => toClient.stream); when(stream.incomingMessages).thenAnswer((_) => toClient.stream);
when(stream.terminate()).thenReturn(null);
when(transport.finish()).thenAnswer((_) async {});
client = TestClient(channel, interceptors: interceptors); client = TestClient(channel, interceptors: interceptors);
} }
@ -246,13 +247,13 @@ abstract class _Harness {
} }
Future<void> runTest( Future<void> runTest(
{Future clientCall, {Future? clientCall,
dynamic expectedResult, dynamic expectedResult,
String expectedPath, String? expectedPath,
Duration expectedTimeout, Duration? expectedTimeout,
Map<String, String> expectedCustomHeaders, Map<String, String>? expectedCustomHeaders,
List<MessageHandler> serverHandlers = const [], List<MessageHandler> serverHandlers = const [],
Function doneHandler, void Function()? doneHandler,
bool expectDone = true}) async { bool expectDone = true}) async {
int serverHandlerIndex = 0; int serverHandlerIndex = 0;
void handleServerMessage(StreamMessage message) { void handleServerMessage(StreamMessage message) {
@ -261,7 +262,7 @@ abstract class _Harness {
final clientSubscription = fromClient.stream.listen( final clientSubscription = fromClient.stream.listen(
expectAsync1(handleServerMessage, count: serverHandlers.length), expectAsync1(handleServerMessage, count: serverHandlers.length),
onError: expectAsync1((_) {}, count: 0), onError: expectAsync1((dynamic _) {}, count: 0),
onDone: expectAsync0(doneHandler ?? () {}, count: expectDone ? 1 : 0)); onDone: expectAsync0(doneHandler ?? () {}, count: expectDone ? 1 : 0));
final result = await clientCall; final result = await clientCall;
@ -276,13 +277,14 @@ abstract class _Harness {
MapEntry(utf8.decode(header.name), utf8.decode(header.value)))), MapEntry(utf8.decode(header.name), utf8.decode(header.value)))),
path: expectedPath, path: expectedPath,
authority: expectedAuthority, authority: expectedAuthority,
timeout: toTimeoutString(expectedTimeout), timeout:
expectedTimeout == null ? null : toTimeoutString(expectedTimeout),
customHeaders: expectedCustomHeaders); customHeaders: expectedCustomHeaders);
await clientSubscription.cancel(); await clientSubscription.cancel();
} }
Future<void> expectThrows(Future future, dynamic exception) async { Future<void> expectThrows(Future? future, dynamic exception) async {
try { try {
await future; await future;
fail('Did not throw'); fail('Did not throw');
@ -293,11 +295,11 @@ abstract class _Harness {
} }
Future<void> runFailureTest( Future<void> runFailureTest(
{Future clientCall, {Future? clientCall,
dynamic expectedException, dynamic expectedException,
String expectedPath, String? expectedPath,
Duration expectedTimeout, Duration? expectedTimeout,
Map<String, String> expectedCustomHeaders, Map<String, String>? expectedCustomHeaders,
List<MessageHandler> serverHandlers = const [], List<MessageHandler> serverHandlers = const [],
bool expectDone = true}) async { bool expectDone = true}) async {
return runTest( return runTest(

View File

@ -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}));
}

View File

@ -2,7 +2,7 @@
// Generated code. Do not modify. // Generated code. Do not modify.
// source: echo.proto // 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 // 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; import 'dart:core' as $core;
@ -27,7 +27,15 @@ class EchoRequest extends $pb.GeneratedMessage {
..hasRequiredFields = false; ..hasRequiredFields = false;
EchoRequest._() : super(); 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, factory EchoRequest.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -42,8 +50,8 @@ class EchoRequest extends $pb.GeneratedMessage {
'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version') 'Will be removed in next major version')
EchoRequest copyWith(void Function(EchoRequest) updates) => EchoRequest copyWith(void Function(EchoRequest) updates) =>
super.copyWith((message) => super.copyWith((message) => updates(message as EchoRequest))
updates(message as EchoRequest)); // ignore: deprecated_member_use as EchoRequest; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static EchoRequest create() => EchoRequest._(); static EchoRequest create() => EchoRequest._();
@ -52,7 +60,7 @@ class EchoRequest extends $pb.GeneratedMessage {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static EchoRequest getDefault() => _defaultInstance ??= static EchoRequest getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<EchoRequest>(create); $pb.GeneratedMessage.$_defaultFor<EchoRequest>(create);
static EchoRequest _defaultInstance; static EchoRequest? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.String get message => $_getSZ(0); $core.String get message => $_getSZ(0);
@ -85,7 +93,15 @@ class EchoResponse extends $pb.GeneratedMessage {
..hasRequiredFields = false; ..hasRequiredFields = false;
EchoResponse._() : super(); 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, factory EchoResponse.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -100,8 +116,8 @@ class EchoResponse extends $pb.GeneratedMessage {
'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version') 'Will be removed in next major version')
EchoResponse copyWith(void Function(EchoResponse) updates) => EchoResponse copyWith(void Function(EchoResponse) updates) =>
super.copyWith((message) => super.copyWith((message) => updates(message as EchoResponse))
updates(message as EchoResponse)); // ignore: deprecated_member_use as EchoResponse; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static EchoResponse create() => EchoResponse._(); static EchoResponse create() => EchoResponse._();
@ -111,7 +127,7 @@ class EchoResponse extends $pb.GeneratedMessage {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static EchoResponse getDefault() => _defaultInstance ??= static EchoResponse getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<EchoResponse>(create); $pb.GeneratedMessage.$_defaultFor<EchoResponse>(create);
static EchoResponse _defaultInstance; static EchoResponse? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.String get message => $_getSZ(0); $core.String get message => $_getSZ(0);
@ -156,7 +172,23 @@ class ServerStreamingEchoRequest extends $pb.GeneratedMessage {
..hasRequiredFields = false; ..hasRequiredFields = false;
ServerStreamingEchoRequest._() : super(); 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, factory ServerStreamingEchoRequest.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -173,8 +205,9 @@ class ServerStreamingEchoRequest extends $pb.GeneratedMessage {
'Will be removed in next major version') 'Will be removed in next major version')
ServerStreamingEchoRequest copyWith( ServerStreamingEchoRequest copyWith(
void Function(ServerStreamingEchoRequest) updates) => void Function(ServerStreamingEchoRequest) updates) =>
super.copyWith((message) => updates(message super.copyWith(
as ServerStreamingEchoRequest)); // ignore: deprecated_member_use (message) => updates(message as ServerStreamingEchoRequest))
as ServerStreamingEchoRequest; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static ServerStreamingEchoRequest create() => ServerStreamingEchoRequest._(); static ServerStreamingEchoRequest create() => ServerStreamingEchoRequest._();
@ -184,7 +217,7 @@ class ServerStreamingEchoRequest extends $pb.GeneratedMessage {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static ServerStreamingEchoRequest getDefault() => _defaultInstance ??= static ServerStreamingEchoRequest getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<ServerStreamingEchoRequest>(create); $pb.GeneratedMessage.$_defaultFor<ServerStreamingEchoRequest>(create);
static ServerStreamingEchoRequest _defaultInstance; static ServerStreamingEchoRequest? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.String get message => $_getSZ(0); $core.String get message => $_getSZ(0);
@ -241,7 +274,15 @@ class ServerStreamingEchoResponse extends $pb.GeneratedMessage {
..hasRequiredFields = false; ..hasRequiredFields = false;
ServerStreamingEchoResponse._() : super(); 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, factory ServerStreamingEchoResponse.fromBuffer($core.List<$core.int> i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(i, r); create()..mergeFromBuffer(i, r);
@ -258,8 +299,9 @@ class ServerStreamingEchoResponse extends $pb.GeneratedMessage {
'Will be removed in next major version') 'Will be removed in next major version')
ServerStreamingEchoResponse copyWith( ServerStreamingEchoResponse copyWith(
void Function(ServerStreamingEchoResponse) updates) => void Function(ServerStreamingEchoResponse) updates) =>
super.copyWith((message) => updates(message super.copyWith(
as ServerStreamingEchoResponse)); // ignore: deprecated_member_use (message) => updates(message as ServerStreamingEchoResponse))
as ServerStreamingEchoResponse; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i; $pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static ServerStreamingEchoResponse create() => static ServerStreamingEchoResponse create() =>
@ -270,7 +312,7 @@ class ServerStreamingEchoResponse extends $pb.GeneratedMessage {
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static ServerStreamingEchoResponse getDefault() => _defaultInstance ??= static ServerStreamingEchoResponse getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<ServerStreamingEchoResponse>(create); $pb.GeneratedMessage.$_defaultFor<ServerStreamingEchoResponse>(create);
static ServerStreamingEchoResponse _defaultInstance; static ServerStreamingEchoResponse? _defaultInstance;
@$pb.TagNumber(1) @$pb.TagNumber(1)
$core.String get message => $_getSZ(0); $core.String get message => $_getSZ(0);

View File

@ -2,5 +2,5 @@
// Generated code. Do not modify. // Generated code. Do not modify.
// source: echo.proto // 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 // 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

View File

@ -2,7 +2,7 @@
// Generated code. Do not modify. // Generated code. Do not modify.
// source: echo.proto // 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 // 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; import 'dart:async' as $async;
@ -26,18 +26,18 @@ class EchoServiceClient extends $grpc.Client {
$0.ServerStreamingEchoResponse.fromBuffer(value)); $0.ServerStreamingEchoResponse.fromBuffer(value));
EchoServiceClient($grpc.ClientChannel channel, EchoServiceClient($grpc.ClientChannel channel,
{$grpc.CallOptions options, {$grpc.CallOptions? options,
$core.Iterable<$grpc.ClientInterceptor> interceptors}) $core.Iterable<$grpc.ClientInterceptor>? interceptors})
: super(channel, options: options, interceptors: interceptors); : super(channel, options: options, interceptors: interceptors);
$grpc.ResponseFuture<$0.EchoResponse> echo($0.EchoRequest request, $grpc.ResponseFuture<$0.EchoResponse> echo($0.EchoRequest request,
{$grpc.CallOptions options}) { {$grpc.CallOptions? options}) {
return $createUnaryCall(_$echo, request, options: options); return $createUnaryCall(_$echo, request, options: options);
} }
$grpc.ResponseStream<$0.ServerStreamingEchoResponse> serverStreamingEcho( $grpc.ResponseStream<$0.ServerStreamingEchoResponse> serverStreamingEcho(
$0.ServerStreamingEchoRequest request, $0.ServerStreamingEchoRequest request,
{$grpc.CallOptions options}) { {$grpc.CallOptions? options}) {
return $createStreamingCall( return $createStreamingCall(
_$serverStreamingEcho, $async.Stream.fromIterable([request]), _$serverStreamingEcho, $async.Stream.fromIterable([request]),
options: options); options: options);

View File

@ -2,9 +2,11 @@
// Generated code. Do not modify. // Generated code. Do not modify.
// source: echo.proto // 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 // 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 { const EchoRequest$json = const {
'1': 'EchoRequest', '1': 'EchoRequest',
'2': const [ '2': const [

View File

@ -27,12 +27,12 @@ class TestService extends Service {
@override @override
String get $name => 'Test'; String get $name => 'Test';
Future<int> Function(ServiceCall call, Future<int> request) unaryHandler; Future<int> Function(ServiceCall call, Future<int> request)? unaryHandler;
Future<int> Function(ServiceCall call, Stream<int> request) Future<int> Function(ServiceCall call, Stream<int> request)?
clientStreamingHandler; clientStreamingHandler;
Stream<int> Function(ServiceCall call, Future<int> request) Stream<int> Function(ServiceCall call, Future<int> request)?
serverStreamingHandler; serverStreamingHandler;
Stream<int> Function(ServiceCall call, Stream<int> request) Stream<int> Function(ServiceCall call, Stream<int> request)?
bidirectionalHandler; bidirectionalHandler;
TestService() { TestService() {
@ -53,40 +53,40 @@ class TestService extends Service {
if (unaryHandler == null) { if (unaryHandler == null) {
fail('Should not invoke Unary'); fail('Should not invoke Unary');
} }
return unaryHandler(call, request); return unaryHandler!(call, request);
} }
Future<int> _clientStreaming(ServiceCall call, Stream<int> request) { Future<int> _clientStreaming(ServiceCall call, Stream<int> request) {
if (clientStreamingHandler == null) { if (clientStreamingHandler == null) {
fail('Should not invoke ClientStreaming'); fail('Should not invoke ClientStreaming');
} }
return clientStreamingHandler(call, request); return clientStreamingHandler!(call, request);
} }
Stream<int> _serverStreaming(ServiceCall call, Future<int> request) { Stream<int> _serverStreaming(ServiceCall call, Future<int> request) {
if (serverStreamingHandler == null) { if (serverStreamingHandler == null) {
fail('Should not invoke ServerStreaming'); fail('Should not invoke ServerStreaming');
} }
return serverStreamingHandler(call, request); return serverStreamingHandler!(call, request);
} }
Stream<int> _bidirectional(ServiceCall call, Stream<int> request) { Stream<int> _bidirectional(ServiceCall call, Stream<int> request) {
if (bidirectionalHandler == null) { if (bidirectionalHandler == null) {
fail('Should not invoke Bidirectional'); fail('Should not invoke Bidirectional');
} }
return bidirectionalHandler(call, request); return bidirectionalHandler!(call, request);
} }
} }
class TestInterceptor { class TestInterceptor {
Interceptor handler; Interceptor? handler;
FutureOr<GrpcError> call(ServiceCall call, ServiceMethod method) { FutureOr<GrpcError?> call(ServiceCall call, ServiceMethod method) {
if (handler == null) { if (handler == null) {
return null; return null;
} }
return handler(call, method); return handler!(call, method);
} }
} }
@ -112,7 +112,8 @@ class TestServerStream extends ServerTransportStream {
bool get canPush => true; bool get canPush => true;
@override @override
ServerTransportStream push(List<Header> requestHeaders) => null; ServerTransportStream push(List<Header> requestHeaders) =>
throw 'unimplemented';
} }
class ServerHarness extends _Harness { class ServerHarness extends _Harness {
@ -144,7 +145,7 @@ abstract class _Harness {
final fromServer = StreamController<StreamMessage>(); final fromServer = StreamController<StreamMessage>();
final service = TestService(); final service = TestService();
final interceptor = TestInterceptor(); final interceptor = TestInterceptor();
ConnectionServer _server; ConnectionServer? _server;
ConnectionServer createServer(); ConnectionServer createServer();
@ -168,7 +169,7 @@ abstract class _Harness {
fromServer.stream.listen( fromServer.stream.listen(
expectAsync1(handleMessages, count: handlers.length), expectAsync1(handleMessages, count: handlers.length),
onError: expectAsync1((_) {}, count: 0), onError: expectAsync1((dynamic _) {}, count: 0),
onDone: expectAsync0(() {}, count: 1)); onDone: expectAsync0(() {}, count: 1));
} }
@ -185,8 +186,8 @@ abstract class _Harness {
void sendRequestHeader(String path, void sendRequestHeader(String path,
{String authority = 'test', {String authority = 'test',
Map<String, String> metadata, Map<String, String>? metadata,
Duration timeout}) { Duration? timeout}) {
final headers = Http2ClientConnection.createCallHeaders( final headers = Http2ClientConnection.createCallHeaders(
true, authority, path, timeout, metadata, null, true, authority, path, timeout, metadata, null,
userAgent: 'dart-grpc/1.0.0 test'); userAgent: 'dart-grpc/1.0.0 test');

View File

@ -31,10 +31,10 @@ Map<String, String> headersToMap(List<Header> headers) =>
key: (h) => ascii.decode(h.name), value: (h) => ascii.decode(h.value)); key: (h) => ascii.decode(h.name), value: (h) => ascii.decode(h.value));
void validateRequestHeaders(Map<String, String> headers, void validateRequestHeaders(Map<String, String> headers,
{String path, {String? path,
String authority = 'test', String authority = 'test',
String timeout, String? timeout,
Map<String, String> customHeaders}) { Map<String, String>? customHeaders}) {
expect(headers[':method'], 'POST'); expect(headers[':method'], 'POST');
expect(headers[':scheme'], 'https'); expect(headers[':scheme'], 'https');
if (path != null) { if (path != null) {
@ -54,7 +54,7 @@ void validateRequestHeaders(Map<String, String> headers,
void validateResponseHeaders(Map<String, String> headers, void validateResponseHeaders(Map<String, String> headers,
{int status = 200, {int status = 200,
bool allowTrailers = false, bool allowTrailers = false,
Map<String, String> customHeaders}) { Map<String, String>? customHeaders}) {
expect(headers[':status'], '200'); expect(headers[':status'], '200');
expect(headers['content-type'], startsWith('application/grpc')); expect(headers['content-type'], startsWith('application/grpc'));
if (!allowTrailers) { if (!allowTrailers) {
@ -67,7 +67,7 @@ void validateResponseHeaders(Map<String, String> headers,
} }
void validateResponseTrailers(Map<String, String> trailers, 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'); expect(trailers['grpc-status'], '$status');
if (message != null) { if (message != null) {
expect(trailers['grpc-message'], message); expect(trailers['grpc-message'], message);
@ -84,7 +84,7 @@ GrpcMetadata validateMetadataMessage(StreamMessage message,
final decoded = GrpcHttpDecoder().convert(message); final decoded = GrpcHttpDecoder().convert(message);
expect(decoded, TypeMatcher<GrpcMetadata>()); expect(decoded, TypeMatcher<GrpcMetadata>());
return decoded; return decoded as GrpcMetadata;
} }
GrpcData validateDataMessage(StreamMessage message, {bool endStream = false}) { GrpcData validateDataMessage(StreamMessage message, {bool endStream = false}) {
@ -93,7 +93,7 @@ GrpcData validateDataMessage(StreamMessage message, {bool endStream = false}) {
final decoded = GrpcHttpDecoder().convert(message); final decoded = GrpcHttpDecoder().convert(message);
expect(decoded, TypeMatcher<GrpcData>()); expect(decoded, TypeMatcher<GrpcData>());
return decoded; return decoded as GrpcData;
} }
void Function(StreamMessage message) headerValidator() { void Function(StreamMessage message) headerValidator() {

View File

@ -22,8 +22,8 @@ import 'package:grpc/grpc.dart';
void main() { void main() {
group('GrpcHttpDecoder', () { group('GrpcHttpDecoder', () {
StreamController<StreamMessage> input; late StreamController<StreamMessage> input;
Stream<GrpcMessage> output; late Stream<GrpcMessage> output;
setUp(() { setUp(() {
input = StreamController(); input = StreamController();
@ -51,11 +51,12 @@ void main() {
} }
expect(converted[0], TypeMatcher<GrpcMetadata>()); expect(converted[0], TypeMatcher<GrpcMetadata>());
verify(converted[1], [48, 49, 50, 51, 52, 53, 54, 55, 56, 57]); verify(
verify(converted[2], [97, 98, 99, 100]); converted[1] as GrpcData, [48, 49, 50, 51, 52, 53, 54, 55, 56, 57]);
verify(converted[3], [65]); verify(converted[2] as GrpcData, [97, 98, 99, 100]);
verify(converted[4], [48, 49, 50, 51]); verify(converted[3] as GrpcData, [65]);
verify(converted[5], List.filled(256, 90)); 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', test('throws error if input is closed while receiving data header',

View File

@ -33,7 +33,7 @@ class TestClient extends Client {
path, (int value) => [value], (List<int> value) => value[0]); path, (int value) => [value], (List<int> value) => value[0]);
TestClient(api.ClientChannel channel) : super(channel); 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]), return $createStreamingCall(_$stream, Stream.fromIterable([request]),
options: options); options: options);
} }
@ -69,12 +69,12 @@ class FakeTimelineTask extends Fake implements TimelineTask {
static final List<Map> events = []; static final List<Map> events = [];
static int _idCount = 0; static int _idCount = 0;
final String filterKey; final String? filterKey;
final TimelineTask parent; final TimelineTask? parent;
final int id = _idCount++; final int id = _idCount++;
int _startFinishCount = 0; int _startFinishCount = 0;
factory FakeTimelineTask({TimelineTask parent, String filterKey}) { factory FakeTimelineTask({TimelineTask? parent, String? filterKey}) {
final task = FakeTimelineTask._(parent: parent, filterKey: filterKey); final task = FakeTimelineTask._(parent: parent, filterKey: filterKey);
tasks.add(task); tasks.add(task);
return task; return task;
@ -84,7 +84,7 @@ class FakeTimelineTask extends Fake implements TimelineTask {
bool get isComplete => _startFinishCount == 0; bool get isComplete => _startFinishCount == 0;
void start(String name, {Map arguments}) { void start(String name, {Map? arguments}) {
events.add({ events.add({
'id': id, 'id': id,
'ph': 'b', 'ph': 'b',
@ -98,7 +98,7 @@ class FakeTimelineTask extends Fake implements TimelineTask {
++_startFinishCount; ++_startFinishCount;
} }
void instant(String name, {Map arguments}) { void instant(String name, {Map? arguments}) {
events.add({ events.add({
'id': id, 'id': id,
'ph': 'i', 'ph': 'i',
@ -110,7 +110,7 @@ class FakeTimelineTask extends Fake implements TimelineTask {
}); });
} }
void finish({Map arguments}) { void finish({Map? arguments}) {
events.add({ events.add({
'id': id, 'id': id,
'ph': 'e', '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); FakeTimelineTask(filterKey: filterKey, parent: parent);
testee() async { testee() async {
@ -134,7 +135,7 @@ testee() async {
timelineTaskFactory = fakeTimelineTaskFactory; timelineTaskFactory = fakeTimelineTaskFactory;
final channel = FixedConnectionClientChannel(Http2ClientConnection( final channel = FixedConnectionClientChannel(Http2ClientConnection(
'localhost', 'localhost',
server.port, server.port!,
ChannelOptions(credentials: ChannelCredentials.insecure()), ChannelOptions(credentials: ChannelCredentials.insecure()),
)); ));
final testClient = TestClient(channel); final testClient = TestClient(channel);

View File

@ -28,7 +28,6 @@ void main() {
group('Unit:', () { group('Unit:', () {
test('Timeouts are converted correctly to header string', () { test('Timeouts are converted correctly to header string', () {
expect(toTimeoutString(null), isNull);
expect(toTimeoutString(Duration(microseconds: -1)), '1n'); expect(toTimeoutString(Duration(microseconds: -1)), '1n');
expect(toTimeoutString(Duration(microseconds: 0)), '0u'); expect(toTimeoutString(Duration(microseconds: 0)), '0u');
expect(toTimeoutString(Duration(microseconds: 107)), '107u'); expect(toTimeoutString(Duration(microseconds: 107)), '107u');
@ -61,7 +60,7 @@ void main() {
}); });
group('Client:', () { group('Client:', () {
ClientHarness harness; late ClientHarness harness;
setUp(() { setUp(() {
harness = ClientHarness()..setUp(); harness = ClientHarness()..setUp();
@ -101,7 +100,7 @@ void main() {
}); });
group('Server:', () { group('Server:', () {
ServerHarness harness; late ServerHarness harness;
setUp(() { setUp(() {
harness = ServerHarness()..setUp(); harness = ServerHarness()..setUp();
@ -112,6 +111,7 @@ void main() {
}); });
test('Calls time out if deadline is exceeded', () async { test('Calls time out if deadline is exceeded', () async {
final handlerFinished = Completer<void>();
Future<int> methodHandler(ServiceCall call, Future<int> request) async { Future<int> methodHandler(ServiceCall call, Future<int> request) async {
try { try {
expect(call.isTimedOut, isFalse); expect(call.isTimedOut, isFalse);
@ -126,6 +126,8 @@ void main() {
fail('Did not throw'); fail('Did not throw');
} catch (error, stack) { } catch (error, stack) {
registerException(error, stack); registerException(error, stack);
} finally {
handlerFinished.complete();
} }
return dummyValue; return dummyValue;
} }
@ -134,7 +136,7 @@ void main() {
..service.unaryHandler = methodHandler ..service.unaryHandler = methodHandler
..expectErrorResponse(StatusCode.deadlineExceeded, 'Deadline exceeded') ..expectErrorResponse(StatusCode.deadlineExceeded, 'Deadline exceeded')
..sendRequestHeader('/Test/Unary', timeout: Duration(microseconds: 1)); ..sendRequestHeader('/Test/Unary', timeout: Duration(microseconds: 1));
await harness.fromServer.done; await Future.wait([handlerFinished.future, harness.fromServer.done]);
}); });
}, testOn: 'vm'); }, testOn: 'vm');
} }