Merge branch 'upstream-master' into update-from-upstream

This commit is contained in:
Tsavo Knott 2025-01-03 12:31:33 -05:00
commit c9d628640c
60 changed files with 755 additions and 473 deletions

View File

@ -16,10 +16,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
sdk: [3.0.0, dev] sdk: [3.5, dev]
steps: steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 - uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94
with: with:
sdk: ${{ matrix.sdk }} sdk: ${{ matrix.sdk }}
- name: Report version - name: Report version
@ -60,7 +60,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, macos-latest, windows-latest]
sdk: [3.0.0, dev] sdk: [3.5, 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
@ -70,8 +70,8 @@ jobs:
- os: macos-latest - os: macos-latest
platform: chrome platform: chrome
steps: steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 - uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94
with: with:
sdk: ${{ matrix.sdk }} sdk: ${{ matrix.sdk }}
- name: Report version - name: Report version

13
.github/workflows/health.yaml vendored Normal file
View File

@ -0,0 +1,13 @@
name: Health
on:
pull_request:
branches: [ master ]
types: [opened, synchronize, reopened, labeled, unlabeled]
jobs:
health:
uses: dart-lang/ecosystem/.github/workflows/health.yaml@main
with:
checks: "changelog,do-not-submit,breaking,coverage,leaking"
ignore_coverage: "example/**,interop/**"
permissions:
pull-requests: write

17
.github/workflows/post_summaries.yaml vendored Normal file
View File

@ -0,0 +1,17 @@
name: Comment on the pull request
on:
# Trigger this workflow after the Health workflow completes. This workflow will have permissions to
# do things like create comments on the PR, even if the original workflow couldn't.
workflow_run:
workflows:
- Health
- Publish
types:
- completed
jobs:
upload:
uses: dart-lang/ecosystem/.github/workflows/post_summaries.yaml@main
permissions:
pull-requests: write

View File

@ -1,3 +1,24 @@
## 4.0.2-wip
* Internal optimization to client code.
* Small fixes, such as ports in testing and enabling `timeline_test.dart`.
* When the keep alive manager runs into a timeout, it will finish the transport instead of closing
the connection, as defined in the gRPC spec.
* Upgrade to `package:lints` version 5.0.0 and Dart SDK version 3.5.0.
* Upgrade `example/grpc-web` code.
## 4.0.1
* Fix header and trailing not completing if the call is terminated. Fixes [#727](https://github.com/grpc/grpc-dart/issues/727)
## 4.0.0
* Set compressed flag correctly for grpc-encoding = identity. Fixes [#669](https://github.com/grpc/grpc-dart/issues/669) (https://github.com/grpc/grpc-dart/pull/693)
* Remove generated status codes.
* Remove dependency on `package:archive`.
* Move `codec.dart`.
* Work around hang during Flutter hot restart by adding default case handler in _GrpcWebConversionSink.add.
## 3.2.4 ## 3.2.4
* Forward internal `GrpcError` on when throwing while sending a request. * Forward internal `GrpcError` on when throwing while sending a request.
@ -19,19 +40,19 @@
## 3.2.1 ## 3.2.1
* `package:http` now supports more versions: `>=0.13.0 <2.0.0`. * `package:http` now supports more versions: `>=0.13.0 <2.0.0`.
* `package:protobuf` new supports more versions: `>=2.0.0 <4.0.0`. * `package:protobuf` new supports more versions: `>=2.0.0 <4.0.0`.
## 3.2.0 ## 3.2.0
* `ChannelOptions` now exposes `connectTimeout`, which is used on the * `ChannelOptions` now exposes `connectTimeout`, which is used on the
socket connect. This is used to specify the maximum allowed time to wait socket connect. This is used to specify the maximum allowed time to wait
for a connection to be established. If `connectTime` is longer than the system for a connection to be established. If `connectTime` is longer than the system
level timeout duration, a timeout may occur sooner than specified in level timeout duration, a timeout may occur sooner than specified in
`connectTimeout`. On timeout, a `SocketException` is thrown. `connectTimeout`. On timeout, a `SocketException` is thrown.
* Require Dart 2.17 or greater. * Require Dart 2.17 or greater.
* Fix issue [#51](https://github.com/grpc/grpc-dart/issues/51), add support for custom error handling. * Fix issue [#51](https://github.com/grpc/grpc-dart/issues/51), add support for custom error handling.
* Expose client IP address to server * Expose client IP address to server
* Add a `channelShutdownHandler` argument to `ClientChannel` and the subclasses. * Add a `channelShutdownHandler` argument to `ClientChannel` and the subclasses.
This callback can be used to react to channel shutdown or termination. This callback can be used to react to channel shutdown or termination.
* Export the `Code` protobuf enum from the `grpc.dart` library. * Export the `Code` protobuf enum from the `grpc.dart` library.

View File

@ -1,14 +1,14 @@
The [Dart](https://www.dart.dev/) implementation of The [Dart](https://www.dart.dev/) implementation of
[gRPC](https://grpc.io/): A high performance, open source, general RPC framework that puts mobile and HTTP/2 first. [gRPC](https://grpc.io/): A high performance, open source, general RPC framework that puts mobile and HTTP/2 first.
[![CI status](https://github.com/grpc/grpc-dart/workflows/Dart/badge.svg)](https://github.com/grpc/grpc-dart/actions?query=workflow%3A%22Dart%22+branch%3Amaster) [![Dart](https://github.com/grpc/grpc-dart/actions/workflows/dart.yml/badge.svg)](https://github.com/grpc/grpc-dart/actions/workflows/dart.yml)
[![pub package](https://img.shields.io/pub/v/grpc.svg)](https://pub.dev/packages/grpc) [![pub package](https://img.shields.io/pub/v/grpc.svg)](https://pub.dev/packages/grpc)
## Learn more ## Learn more
- [Quick Start](https://grpc.io/docs/languages/dart/quickstart) - get an app running in minutes - [Quick Start](https://grpc.io/docs/languages/dart/quickstart) - get an app running in minutes
- [Examples](example) - [Examples](https://github.com/grpc/grpc-dart/tree/master/example)
- [API reference](https://grpc.io/docs/languages/dart/api) - [API reference](https://grpc.io/docs/languages/dart/api)
For complete documentation, see [Dart gRPC](https://grpc.io/docs/languages/dart). For complete documentation, see [Dart gRPC](https://grpc.io/docs/languages/dart).

View File

@ -9,13 +9,15 @@ analyzer:
linter: linter:
rules: rules:
- always_declare_return_types #true
- cancel_subscriptions always_declare_return_types: true
- close_sinks cancel_subscriptions: true
- directives_ordering close_sinks: true
- omit_local_variable_types directives_ordering: true
- prefer_final_locals omit_local_variable_types: true
- prefer_single_quotes prefer_final_locals: true
- test_types_in_equals prefer_single_quotes: true
- use_super_parameters test_types_in_equals: true
- prefer_relative_imports prefer_relative_imports: true
#false
unintended_html_in_doc_comment: false

View File

@ -1,7 +1,7 @@
Four code examples are available: Four code examples are available:
1. [helloworld](https://github.com/grpc/grpc-dart/tree/master/example/helloworld): 1. [helloworld](https://github.com/grpc/grpc-dart/tree/master/example/helloworld):
A demonstration of using the Dart gRPC library to perform unary RPs. A demonstration of using the Dart gRPC library to perform unary RPCs.
1. [googleapis](https://github.com/grpc/grpc-dart/tree/master/example/googleapis): 1. [googleapis](https://github.com/grpc/grpc-dart/tree/master/example/googleapis):
A demonstration of using the Dart gRPC library to communicate with Google APIs. A demonstration of using the Dart gRPC library to communicate with Google APIs.

View File

@ -1,3 +1,18 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'dart:async'; import 'dart:async';
import 'dart:html'; import 'dart:html';

View File

@ -3,7 +3,7 @@ description: Dart gRPC-Web sample client
publish_to: none publish_to: none
environment: environment:
sdk: '>=2.12.0 <3.0.0' sdk: ^3.5.0
dependencies: dependencies:
grpc: grpc:
@ -11,6 +11,6 @@ dependencies:
protobuf: ^3.0.0 protobuf: ^3.0.0
dev_dependencies: dev_dependencies:
build_runner: ^2.0.0 build_runner: ^2.4.13
build_web_compilers: '>3.0.0 <5.0.0' build_web_compilers: ^4.0.11
lints: ^2.0.0 lints: ^5.0.0

View File

@ -13,10 +13,10 @@
// 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.
/// Dart implementation of the gRPC helloworld.Greeter client.
import 'package:grpc/grpc.dart'; import 'package:grpc/grpc.dart';
import 'package:helloworld/src/generated/helloworld.pbgrpc.dart'; import 'package:helloworld/src/generated/helloworld.pbgrpc.dart';
/// Dart implementation of the gRPC helloworld.Greeter client.
Future<void> main(List<String> args) async { Future<void> main(List<String> args) async {
final channel = ClientChannel( final channel = ClientChannel(
'localhost', 'localhost',

View File

@ -13,10 +13,10 @@
// 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.
/// Dart implementation of the gRPC helloworld.Greeter server.
import 'package:grpc/grpc.dart'; import 'package:grpc/grpc.dart';
import 'package:helloworld/src/generated/helloworld.pbgrpc.dart'; import 'package:helloworld/src/generated/helloworld.pbgrpc.dart';
/// Dart implementation of the gRPC helloworld.Greeter server.
class GreeterService extends GreeterServiceBase { class GreeterService extends GreeterServiceBase {
@override @override
Future<HelloReply> sayHello(ServiceCall call, HelloRequest request) async { Future<HelloReply> sayHello(ServiceCall call, HelloRequest request) async {

View File

@ -13,12 +13,12 @@
// 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.
/// Dart implementation of the gRPC helloworld.Greeter client.
import 'dart:io'; import 'dart:io';
import 'package:grpc/grpc.dart'; import 'package:grpc/grpc.dart';
import 'package:helloworld/src/generated/helloworld.pbgrpc.dart'; import 'package:helloworld/src/generated/helloworld.pbgrpc.dart';
/// Dart implementation of the gRPC helloworld.Greeter client.
Future<void> main(List<String> args) async { Future<void> main(List<String> args) async {
final udsAddress = final udsAddress =
InternetAddress('localhost', type: InternetAddressType.unix); InternetAddress('localhost', type: InternetAddressType.unix);

View File

@ -13,12 +13,12 @@
// 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.
/// Dart implementation of the gRPC helloworld.Greeter server.
import 'dart:io'; import 'dart:io';
import 'package:grpc/grpc.dart'; import 'package:grpc/grpc.dart';
import 'package:helloworld/src/generated/helloworld.pbgrpc.dart'; import 'package:helloworld/src/generated/helloworld.pbgrpc.dart';
/// Dart implementation of the gRPC helloworld.Greeter server.
class GreeterService extends GreeterServiceBase { class GreeterService extends GreeterServiceBase {
@override @override
Future<HelloReply> sayHello(ServiceCall call, HelloRequest request) async { Future<HelloReply> sayHello(ServiceCall call, HelloRequest request) async {

View File

@ -208,7 +208,7 @@ class Tester {
final receivedBytes = response.payload.body.length; final receivedBytes = response.payload.body.length;
if (receivedBytes != 314159) { if (receivedBytes != 314159) {
throw 'Response payload mismatch. Expected 314159 bytes, ' throw 'Response payload mismatch. Expected 314159 bytes, '
'got ${receivedBytes}.'; 'got $receivedBytes.';
} }
} }
@ -869,7 +869,7 @@ class Tester {
final receivedBytes = response.payload.body.length; final receivedBytes = response.payload.body.length;
if (receivedBytes != 314159) { if (receivedBytes != 314159) {
throw 'Response payload mismatch. Expected 314159 bytes, ' throw 'Response payload mismatch. Expected 314159 bytes, '
'got ${receivedBytes}.'; 'got $receivedBytes.';
} }
return response; return response;
} }

View File

@ -13,6 +13,10 @@
// 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.
// ignore: dangling_library_doc_comments
/// Status detail types and error codes
export 'package:grpc/src/generated/google/rpc/error_details.pb.dart';
export 'src/auth/auth.dart' show BaseAuthenticator; export 'src/auth/auth.dart' show BaseAuthenticator;
export 'src/auth/auth_io.dart' export 'src/auth/auth_io.dart'
show show
@ -39,10 +43,6 @@ export 'src/client/options.dart'
export 'src/client/proxy.dart' show Proxy; export 'src/client/proxy.dart' show Proxy;
export 'src/client/transport/http2_credentials.dart' export 'src/client/transport/http2_credentials.dart'
show BadCertificateHandler, allowBadCertificates, ChannelCredentials; show BadCertificateHandler, allowBadCertificates, ChannelCredentials;
/// Status detail types and error codes
export 'src/generated/google/rpc/code.pbenum.dart';
export 'src/generated/google/rpc/error_details.pb.dart';
export 'src/server/call.dart' show ServiceCall; export 'src/server/call.dart' show ServiceCall;
export 'src/server/interceptor.dart' show Interceptor; export 'src/server/interceptor.dart' show Interceptor;
export 'src/server/server.dart' export 'src/server/server.dart'

View File

@ -16,7 +16,7 @@
/// Exports the minimum api to define server and client stubs. /// Exports the minimum api to define server and client stubs.
/// ///
/// Mainly intended to be imported by generated code. /// Mainly intended to be imported by generated code.
library service_api; library;
export 'src/client/call.dart' show CallOptions, MetadataProvider; export 'src/client/call.dart' show CallOptions, MetadataProvider;
export 'src/client/channel.dart' show ClientChannel; export 'src/client/channel.dart' show ClientChannel;

View File

@ -69,7 +69,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;
} }
} }

View File

@ -86,9 +86,9 @@ class CallOptions {
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.of(metadata)..addAll(other.metadata);
final mergedTimeout = other.timeout ?? timeout; final mergedTimeout = other.timeout ?? timeout;
final mergedProviders = List.from(metadataProviders) final mergedProviders = List.of(metadataProviders)
..addAll(other.metadataProviders); ..addAll(other.metadataProviders);
final mergedCompression = other.compression ?? compression; final mergedCompression = other.compression ?? compression;
return CallOptions._( return CallOptions._(
@ -146,9 +146,9 @@ class WebCallOptions extends CallOptions {
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.of(metadata)..addAll(other.metadata);
final mergedTimeout = other.timeout ?? timeout; final mergedTimeout = other.timeout ?? timeout;
final mergedProviders = List.from(metadataProviders) final mergedProviders = List.of(metadataProviders)
..addAll(other.metadataProviders); ..addAll(other.metadataProviders);
if (other is! WebCallOptions) { if (other is! WebCallOptions) {
@ -241,7 +241,7 @@ class ClientCall<Q, R> implements Response {
if (options.metadataProviders.isEmpty) { if (options.metadataProviders.isEmpty) {
_sendRequest(connection, _sanitizeMetadata(options.metadata)); _sendRequest(connection, _sanitizeMetadata(options.metadata));
} else { } else {
final metadata = Map<String, String>.from(options.metadata); final metadata = Map<String, String>.of(options.metadata);
Future.forEach( Future.forEach(
options.metadataProviders, options.metadataProviders,
(MetadataProvider provider) => provider(metadata, (MetadataProvider provider) => provider(metadata,
@ -483,6 +483,12 @@ class ClientCall<Q, R> implements Response {
if (_responseSubscription != null) { if (_responseSubscription != null) {
futures.add(_responseSubscription!.cancel()); futures.add(_responseSubscription!.cancel());
} }
if (!_headers.isCompleted) {
_headers.complete({});
}
if (!_trailers.isCompleted) {
_trailers.complete({});
}
await Future.wait(futures); await Future.wait(futures);
} }

View File

@ -113,7 +113,7 @@ class Http2ClientConnection implements connection.ClientConnection {
transport.ping(); transport.ping();
} }
}, },
onPingTimeout: () => shutdown(), onPingTimeout: () => transport.finish(),
); );
transport.onFrameReceived transport.onFrameReceived
.listen((_) => keepAliveManager?.onFrameReceived()); .listen((_) => keepAliveManager?.onFrameReceived());

View File

@ -58,7 +58,7 @@ class ChannelCredentials {
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 = SecurityContext(withTrustedRoots: true); final context = SecurityContext(withTrustedRoots: true);

View File

@ -132,6 +132,16 @@ class _GrpcWebConversionSink implements ChunkedConversionSink<ByteBuffer> {
void add(ByteBuffer chunk) { void add(ByteBuffer chunk) {
_chunkOffset = 0; _chunkOffset = 0;
final chunkData = chunk.asUint8List(); final chunkData = chunk.asUint8List();
// in flutter web, when a hot-restart is requested, the code can get stuck
// in the following loop if there is an active streaming call. the
// switch statement is invoked but doesn't match any of the cases. this
// presumably has something to do how enums work across isolates.
// possibly related to https://github.com/dart-lang/sdk/issues/35626.
//
// in any case, adding a default handler to the switch statement
// causes this loop to end in that case, allowing the old isolate
// to shut down and the hot-restart to work properly.
processingLoop:
while (_chunkOffset < chunk.lengthInBytes) { while (_chunkOffset < chunk.lengthInBytes) {
switch (_state) { switch (_state) {
case _GrpcWebParseState.init: case _GrpcWebParseState.init:
@ -143,6 +153,10 @@ class _GrpcWebConversionSink implements ChunkedConversionSink<ByteBuffer> {
case _GrpcWebParseState.message: case _GrpcWebParseState.message:
_parseMessage(chunkData); _parseMessage(chunkData);
break; break;
// ignore: unreachable_switch_default
default:
// only expected to be hit when hot-restarting, see above
break processingLoop;
} }
} }
_chunkOffset = 0; _chunkOffset = 0;

View File

@ -1,14 +0,0 @@
//
// Generated code. Do not modify.
// source: google/rpc/code.proto
//
// @dart = 2.12
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
// ignore_for_file: constant_identifier_names, library_prefixes
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
import 'dart:core' as $core;
export 'code.pbenum.dart';

View File

@ -1,79 +0,0 @@
//
// Generated code. Do not modify.
// source: google/rpc/code.proto
//
// @dart = 2.12
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
// ignore_for_file: constant_identifier_names, library_prefixes
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
import 'dart:core' as $core;
import 'package:protobuf/protobuf.dart' as $pb;
/// The canonical error codes for gRPC APIs.
///
///
/// Sometimes multiple error codes may apply. Services should return
/// the most specific error code that applies. For example, prefer
/// `OUT_OF_RANGE` over `FAILED_PRECONDITION` if both codes apply.
/// Similarly prefer `NOT_FOUND` or `ALREADY_EXISTS` over `FAILED_PRECONDITION`.
class Code extends $pb.ProtobufEnum {
static const Code OK = Code._(0, _omitEnumNames ? '' : 'OK');
static const Code CANCELLED = Code._(1, _omitEnumNames ? '' : 'CANCELLED');
static const Code UNKNOWN = Code._(2, _omitEnumNames ? '' : 'UNKNOWN');
static const Code INVALID_ARGUMENT =
Code._(3, _omitEnumNames ? '' : 'INVALID_ARGUMENT');
static const Code DEADLINE_EXCEEDED =
Code._(4, _omitEnumNames ? '' : 'DEADLINE_EXCEEDED');
static const Code NOT_FOUND = Code._(5, _omitEnumNames ? '' : 'NOT_FOUND');
static const Code ALREADY_EXISTS =
Code._(6, _omitEnumNames ? '' : 'ALREADY_EXISTS');
static const Code PERMISSION_DENIED =
Code._(7, _omitEnumNames ? '' : 'PERMISSION_DENIED');
static const Code UNAUTHENTICATED =
Code._(16, _omitEnumNames ? '' : 'UNAUTHENTICATED');
static const Code RESOURCE_EXHAUSTED =
Code._(8, _omitEnumNames ? '' : 'RESOURCE_EXHAUSTED');
static const Code FAILED_PRECONDITION =
Code._(9, _omitEnumNames ? '' : 'FAILED_PRECONDITION');
static const Code ABORTED = Code._(10, _omitEnumNames ? '' : 'ABORTED');
static const Code OUT_OF_RANGE =
Code._(11, _omitEnumNames ? '' : 'OUT_OF_RANGE');
static const Code UNIMPLEMENTED =
Code._(12, _omitEnumNames ? '' : 'UNIMPLEMENTED');
static const Code INTERNAL = Code._(13, _omitEnumNames ? '' : 'INTERNAL');
static const Code UNAVAILABLE =
Code._(14, _omitEnumNames ? '' : 'UNAVAILABLE');
static const Code DATA_LOSS = Code._(15, _omitEnumNames ? '' : 'DATA_LOSS');
static const $core.List<Code> values = <Code>[
OK,
CANCELLED,
UNKNOWN,
INVALID_ARGUMENT,
DEADLINE_EXCEEDED,
NOT_FOUND,
ALREADY_EXISTS,
PERMISSION_DENIED,
UNAUTHENTICATED,
RESOURCE_EXHAUSTED,
FAILED_PRECONDITION,
ABORTED,
OUT_OF_RANGE,
UNIMPLEMENTED,
INTERNAL,
UNAVAILABLE,
DATA_LOSS,
];
static final $core.Map<$core.int, Code> _byValue =
$pb.ProtobufEnum.initByValue(values);
static Code? valueOf($core.int value) => _byValue[value];
const Code._($core.int v, $core.String n) : super(v, n);
}
const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names');

View File

@ -1,47 +0,0 @@
//
// Generated code. Do not modify.
// source: google/rpc/code.proto
//
// @dart = 2.12
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
// ignore_for_file: constant_identifier_names, library_prefixes
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
import 'dart:convert' as $convert;
import 'dart:core' as $core;
import 'dart:typed_data' as $typed_data;
@$core.Deprecated('Use codeDescriptor instead')
const Code$json = {
'1': 'Code',
'2': [
{'1': 'OK', '2': 0},
{'1': 'CANCELLED', '2': 1},
{'1': 'UNKNOWN', '2': 2},
{'1': 'INVALID_ARGUMENT', '2': 3},
{'1': 'DEADLINE_EXCEEDED', '2': 4},
{'1': 'NOT_FOUND', '2': 5},
{'1': 'ALREADY_EXISTS', '2': 6},
{'1': 'PERMISSION_DENIED', '2': 7},
{'1': 'UNAUTHENTICATED', '2': 16},
{'1': 'RESOURCE_EXHAUSTED', '2': 8},
{'1': 'FAILED_PRECONDITION', '2': 9},
{'1': 'ABORTED', '2': 10},
{'1': 'OUT_OF_RANGE', '2': 11},
{'1': 'UNIMPLEMENTED', '2': 12},
{'1': 'INTERNAL', '2': 13},
{'1': 'UNAVAILABLE', '2': 14},
{'1': 'DATA_LOSS', '2': 15},
],
};
/// Descriptor for `Code`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List codeDescriptor = $convert.base64Decode(
'CgRDb2RlEgYKAk9LEAASDQoJQ0FOQ0VMTEVEEAESCwoHVU5LTk9XThACEhQKEElOVkFMSURfQV'
'JHVU1FTlQQAxIVChFERUFETElORV9FWENFRURFRBAEEg0KCU5PVF9GT1VORBAFEhIKDkFMUkVB'
'RFlfRVhJU1RTEAYSFQoRUEVSTUlTU0lPTl9ERU5JRUQQBxITCg9VTkFVVEhFTlRJQ0FURUQQEB'
'IWChJSRVNPVVJDRV9FWEhBVVNURUQQCBIXChNGQUlMRURfUFJFQ09ORElUSU9OEAkSCwoHQUJP'
'UlRFRBAKEhAKDE9VVF9PRl9SQU5HRRALEhEKDVVOSU1QTEVNRU5URUQQDBIMCghJTlRFUk5BTB'
'ANEg8KC1VOQVZBSUxBQkxFEA4SDQoJREFUQV9MT1NTEA8=');

View File

@ -1,186 +0,0 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
package google.rpc;
option go_package = "google.golang.org/genproto/googleapis/rpc/code;code";
option java_multiple_files = true;
option java_outer_classname = "CodeProto";
option java_package = "com.google.rpc";
option objc_class_prefix = "RPC";
// The canonical error codes for gRPC APIs.
//
//
// Sometimes multiple error codes may apply. Services should return
// the most specific error code that applies. For example, prefer
// `OUT_OF_RANGE` over `FAILED_PRECONDITION` if both codes apply.
// Similarly prefer `NOT_FOUND` or `ALREADY_EXISTS` over `FAILED_PRECONDITION`.
enum Code {
// Not an error; returned on success
//
// HTTP Mapping: 200 OK
OK = 0;
// The operation was cancelled, typically by the caller.
//
// HTTP Mapping: 499 Client Closed Request
CANCELLED = 1;
// Unknown error. For example, this error may be returned when
// a `Status` value received from another address space belongs to
// an error space that is not known in this address space. Also
// errors raised by APIs that do not return enough error information
// may be converted to this error.
//
// HTTP Mapping: 500 Internal Server Error
UNKNOWN = 2;
// The client specified an invalid argument. Note that this differs
// from `FAILED_PRECONDITION`. `INVALID_ARGUMENT` indicates arguments
// that are problematic regardless of the state of the system
// (e.g., a malformed file name).
//
// HTTP Mapping: 400 Bad Request
INVALID_ARGUMENT = 3;
// The deadline expired before the operation could complete. For operations
// that change the state of the system, this error may be returned
// even if the operation has completed successfully. For example, a
// successful response from a server could have been delayed long
// enough for the deadline to expire.
//
// HTTP Mapping: 504 Gateway Timeout
DEADLINE_EXCEEDED = 4;
// Some requested entity (e.g., file or directory) was not found.
//
// Note to server developers: if a request is denied for an entire class
// of users, such as gradual feature rollout or undocumented whitelist,
// `NOT_FOUND` may be used. If a request is denied for some users within
// a class of users, such as user-based access control, `PERMISSION_DENIED`
// must be used.
//
// HTTP Mapping: 404 Not Found
NOT_FOUND = 5;
// The entity that a client attempted to create (e.g., file or directory)
// already exists.
//
// HTTP Mapping: 409 Conflict
ALREADY_EXISTS = 6;
// The caller does not have permission to execute the specified
// operation. `PERMISSION_DENIED` must not be used for rejections
// caused by exhausting some resource (use `RESOURCE_EXHAUSTED`
// instead for those errors). `PERMISSION_DENIED` must not be
// used if the caller can not be identified (use `UNAUTHENTICATED`
// instead for those errors). This error code does not imply the
// request is valid or the requested entity exists or satisfies
// other pre-conditions.
//
// HTTP Mapping: 403 Forbidden
PERMISSION_DENIED = 7;
// The request does not have valid authentication credentials for the
// operation.
//
// HTTP Mapping: 401 Unauthorized
UNAUTHENTICATED = 16;
// Some resource has been exhausted, perhaps a per-user quota, or
// perhaps the entire file system is out of space.
//
// HTTP Mapping: 429 Too Many Requests
RESOURCE_EXHAUSTED = 8;
// The operation was rejected because the system is not in a state
// required for the operation's execution. For example, the directory
// to be deleted is non-empty, an rmdir operation is applied to
// a non-directory, etc.
//
// Service implementors can use the following guidelines to decide
// between `FAILED_PRECONDITION`, `ABORTED`, and `UNAVAILABLE`:
// (a) Use `UNAVAILABLE` if the client can retry just the failing call.
// (b) Use `ABORTED` if the client should retry at a higher level
// (e.g., when a client-specified test-and-set fails, indicating the
// client should restart a read-modify-write sequence).
// (c) Use `FAILED_PRECONDITION` if the client should not retry until
// the system state has been explicitly fixed. E.g., if an "rmdir"
// fails because the directory is non-empty, `FAILED_PRECONDITION`
// should be returned since the client should not retry unless
// the files are deleted from the directory.
//
// HTTP Mapping: 400 Bad Request
FAILED_PRECONDITION = 9;
// The operation was aborted, typically due to a concurrency issue such as
// a sequencer check failure or transaction abort.
//
// See the guidelines above for deciding between `FAILED_PRECONDITION`,
// `ABORTED`, and `UNAVAILABLE`.
//
// HTTP Mapping: 409 Conflict
ABORTED = 10;
// The operation was attempted past the valid range. E.g., seeking or
// reading past end-of-file.
//
// Unlike `INVALID_ARGUMENT`, this error indicates a problem that may
// be fixed if the system state changes. For example, a 32-bit file
// system will generate `INVALID_ARGUMENT` if asked to read at an
// offset that is not in the range [0,2^32-1], but it will generate
// `OUT_OF_RANGE` if asked to read from an offset past the current
// file size.
//
// There is a fair bit of overlap between `FAILED_PRECONDITION` and
// `OUT_OF_RANGE`. We recommend using `OUT_OF_RANGE` (the more specific
// error) when it applies so that callers who are iterating through
// a space can easily look for an `OUT_OF_RANGE` error to detect when
// they are done.
//
// HTTP Mapping: 400 Bad Request
OUT_OF_RANGE = 11;
// The operation is not implemented or is not supported/enabled in this
// service.
//
// HTTP Mapping: 501 Not Implemented
UNIMPLEMENTED = 12;
// Internal errors. This means that some invariants expected by the
// underlying system have been broken. This error code is reserved
// for serious errors.
//
// HTTP Mapping: 500 Internal Server Error
INTERNAL = 13;
// The service is currently unavailable. This is most likely a
// transient condition, which can be corrected by retrying with
// a backoff. Note that it is not always safe to retry
// non-idempotent operations.
//
// See the guidelines above for deciding between `FAILED_PRECONDITION`,
// `ABORTED`, and `UNAVAILABLE`.
//
// HTTP Mapping: 503 Service Unavailable
UNAVAILABLE = 14;
// Unrecoverable data loss or corruption.
//
// HTTP Mapping: 500 Internal Server Error
DATA_LOSS = 15;
}

View File

@ -170,7 +170,7 @@ class ServerHandler extends ServiceCall {
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);
} }

View File

@ -13,56 +13,6 @@
// 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.
import 'package:archive/archive.dart'; export 'codec/codec_all.dart';
export 'codec/codec_io.dart'
abstract class Codec { if (dart.library.js_interop) 'codec/codec_web.dart'; // package:web implementation
/// Returns the message encoding that this compressor uses.
///
/// This can be values such as "gzip", "deflate", "snappy", etc.
String get encodingName;
/// Wraps an existing output stream with a compressed output.
List<int> compress(List<int> data);
/// Wraps an existing output stream with a uncompressed input data.
List<int> decompress(List<int> data);
}
/// The "identity", or "none" codec.
///
/// This codec is special in that it can be used to explicitly disable Call
/// compression on a Channel that by default compresses.
class IdentityCodec implements Codec {
const IdentityCodec();
@override
final encodingName = 'identity';
@override
List<int> compress(List<int> data) {
return data;
}
@override
List<int> decompress(List<int> data) {
return data;
}
}
/// A gzip compressor and decompressor.
class GzipCodec implements Codec {
const GzipCodec();
@override
final encodingName = 'gzip';
@override
List<int> compress(List<int> data) {
return GZipEncoder().encode(data)!;
}
@override
List<int> decompress(List<int> data) {
return GZipDecoder().decodeBytes(data);
}
}

View File

@ -0,0 +1,48 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
abstract class Codec {
/// Returns the message encoding that this compressor uses.
///
/// This can be values such as "gzip", "deflate", "snappy", etc.
String get encodingName;
/// Wraps an existing output stream with a compressed output.
List<int> compress(List<int> data);
/// Wraps an existing output stream with a uncompressed input data.
List<int> decompress(List<int> data);
}
/// The "identity", or "none" codec.
///
/// This codec is special in that it can be used to explicitly disable Call
/// compression on a Channel that by default compresses.
class IdentityCodec implements Codec {
const IdentityCodec();
@override
final encodingName = 'identity';
@override
List<int> compress(List<int> data) {
return data;
}
@override
List<int> decompress(List<int> data) {
return data;
}
}

View File

@ -0,0 +1,36 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'dart:io';
import 'codec_all.dart';
/// A gzip compressor and decompressor.
class GzipCodec implements Codec {
const GzipCodec();
@override
final encodingName = 'gzip';
@override
List<int> compress(List<int> data) {
return gzip.encode(data);
}
@override
List<int> decompress(List<int> data) {
return gzip.decode(data);
}
}

View File

@ -0,0 +1,34 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'codec_all.dart';
/// A gzip compressor and decompressor.
class GzipCodec implements Codec {
const GzipCodec();
@override
final encodingName = 'gzip';
@override
List<int> compress(List<int> data) {
throw UnsupportedError('Gzip is not supported for grpc web');
}
@override
List<int> decompress(List<int> data) {
throw UnsupportedError('Gzip is not supported for grpc web');
}
}

View File

@ -68,7 +68,8 @@ List<int> frame(List<int> rawPayload, [Codec? codec]) {
final payloadLength = compressedPayload.length; final payloadLength = compressedPayload.length;
final bytes = Uint8List(payloadLength + 5); final bytes = Uint8List(payloadLength + 5);
final header = bytes.buffer.asByteData(0, 5); final header = bytes.buffer.asByteData(0, 5);
header.setUint8(0, codec == null ? 0 : 1); header.setUint8(
0, (codec == null || codec.encodingName == 'identity') ? 0 : 1);
header.setUint32(1, payloadLength); header.setUint32(1, payloadLength);
bytes.setRange(5, bytes.length, compressedPayload); bytes.setRange(5, bytes.length, compressedPayload);
return bytes; return bytes;

View File

@ -13,15 +13,16 @@
// 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.
// ignore_for_file: prefer_relative_imports
import 'dart:convert'; import 'dart:convert';
import 'package:grpc/src/generated/google/protobuf/any.pb.dart';
import 'package:grpc/src/generated/google/rpc/error_details.pb.dart';
import 'package:grpc/src/generated/google/rpc/status.pb.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:protobuf/protobuf.dart'; import 'package:protobuf/protobuf.dart';
import '../generated/google/protobuf/any.pb.dart';
import '../generated/google/rpc/code.pbenum.dart';
import '../generated/google/rpc/error_details.pb.dart';
import '../generated/google/rpc/status.pb.dart';
import 'io_bits/io_bits.dart' show HttpStatus; import 'io_bits/io_bits.dart' show HttpStatus;
class StatusCode { class StatusCode {
@ -149,6 +150,28 @@ class StatusCode {
static int fromHttpStatus(int status) { static int fromHttpStatus(int status) {
return _httpStatusToGrpcStatus[status] ?? StatusCode.unknown; return _httpStatusToGrpcStatus[status] ?? StatusCode.unknown;
} }
/// Creates a string from a gRPC status code.
static String? name(int status) => switch (status) {
ok => 'OK',
cancelled => 'CANCELLED',
unknown => 'UNKNOWN',
invalidArgument => 'INVALID_ARGUMENT',
deadlineExceeded => 'DEADLINE_EXCEEDED',
notFound => 'NOT_FOUND',
alreadyExists => 'ALREADY_EXISTS',
permissionDenied => 'PERMISSION_DENIED',
resourceExhausted => 'RESOURCE_EXHAUSTED',
failedPrecondition => 'FAILED_PRECONDITION',
aborted => 'ABORTED',
outOfRange => 'OUT_OF_RANGE',
unimplemented => 'UNIMPLEMENTED',
internal => 'INTERNAL',
unavailable => 'UNAVAILABLE',
dataLoss => 'DATA_LOSS',
unauthenticated => 'UNAUTHENTICATED',
int() => null,
};
} }
class GrpcError implements Exception { class GrpcError implements Exception {
@ -306,7 +329,8 @@ class GrpcError implements Exception {
code = StatusCode.unauthenticated; code = StatusCode.unauthenticated;
/// Given a status code, return the name /// Given a status code, return the name
String get codeName => (Code.valueOf(code) ?? Code.UNKNOWN).name; String get codeName =>
StatusCode.name(code) ?? StatusCode.name(StatusCode.unknown)!;
@override @override
bool operator ==(other) { bool operator ==(other) {
@ -328,6 +352,7 @@ class GrpcError implements Exception {
/// This list comes from `error_details.proto`. If any new error detail types are /// This list comes from `error_details.proto`. If any new error detail types are
/// added to the protbuf definition, this function should be updated accordingly to /// added to the protbuf definition, this function should be updated accordingly to
/// support them. /// support them.
@visibleForTesting
GeneratedMessage parseErrorDetailsFromAny(Any any) { GeneratedMessage parseErrorDetailsFromAny(Any any) {
switch (any.typeUrl) { switch (any.typeUrl) {
case 'type.googleapis.com/google.rpc.RetryInfo': case 'type.googleapis.com/google.rpc.RetryInfo':
@ -449,7 +474,7 @@ GrpcError? grpcErrorDetailsFromTrailers(Map<String, String> trailers) {
} }
Map<String, String> toCustomTrailers(Map<String, String> trailers) { Map<String, String> toCustomTrailers(Map<String, String> trailers) {
return Map.from(trailers) return Map.of(trailers)
..remove(':status') ..remove(':status')
..remove('content-type') ..remove('content-type')
..remove('grpc-status') ..remove('grpc-status')

View File

@ -1,10 +1,11 @@
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: 3.2.4 version: 4.0.2
repository: https://github.com/open-runtime/grpc-dart
repository: https://github.com/grpc/grpc-dart
environment: environment:
sdk: '>=3.3.0 <4.5.0' sdk: ^3.5.0
dependencies: dependencies:
archive: ^3.4.10 archive: ^3.4.10
@ -21,15 +22,20 @@ dependencies:
dev_dependencies: dev_dependencies:
build_runner: ^2.4.9 build_runner: ^2.4.9
build_test: ^2.2.2 build_test: ^2.2.2
lints: ">=2.0.0 <4.0.0" lints: ^5.0.0
mockito: ^5.4.4 mockito: ^5.4.4
path: ^1.9.0 path: ^1.9.0
test: ^1.25.3 test: ^1.25.3
stream_channel: ^2.1.2 stream_channel: ^2.1.2
stream_transform: ^2.1.0 stream_transform: ^2.1.0
vm_service: ">=11.6.0 <15.0.0" vm_service: ">=11.6.0 <16.0.0"
fake_async: ^1.3.1 fake_async: ^1.3.1
false_secrets: false_secrets:
- interop/server1.key - interop/server1.key
- test/data/localhost.key - test/data/localhost.key
topics:
- grpc
- rpc
- protocols

View File

@ -1,6 +1,23 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// TODO(dartbug.com/26057) currently Mac OS X seems to have some issues with // TODO(dartbug.com/26057) currently Mac OS X seems to have some issues with
// client certificates so we disable the test. // client certificates so we disable the test.
@TestOn('vm && !mac-os') @TestOn('vm && !mac-os')
library;
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';

View File

@ -1,4 +1,21 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
@TestOn('vm') @TestOn('vm')
library;
import 'dart:async'; import 'dart:async';
import 'package:grpc/grpc.dart' as grpc; import 'package:grpc/grpc.dart' as grpc;

View File

@ -1,7 +1,38 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'package:grpc/grpc.dart';
import 'package:grpc/src/client/call.dart'; import 'package:grpc/src/client/call.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../src/client_utils.dart';
void main() { void main() {
const dummyValue = 0;
const cancelDurationMillis = 300;
late ClientHarness harness;
setUp(() {
harness = ClientHarness()..setUp();
});
tearDown(() {
harness.tearDown();
});
test('WebCallOptions mergeWith CallOptions returns WebCallOptions', () { test('WebCallOptions mergeWith CallOptions returns WebCallOptions', () {
final options = final options =
WebCallOptions(bypassCorsPreflight: true, withCredentials: true); WebCallOptions(bypassCorsPreflight: true, withCredentials: true);
@ -13,4 +44,57 @@ void main() {
expect(mergedOptions.bypassCorsPreflight, true); expect(mergedOptions.bypassCorsPreflight, true);
expect(mergedOptions.withCredentials, true); expect(mergedOptions.withCredentials, true);
}); });
test(
'Cancelling a call correctly complete headers future',
() async {
final clientCall = harness.client.unary(dummyValue);
Future.delayed(
Duration(milliseconds: cancelDurationMillis),
).then((_) => clientCall.cancel());
expect(await clientCall.headers, isEmpty);
await expectLater(
clientCall,
throwsA(
isA<GrpcError>().having(
(e) => e.codeName,
'Test codename',
contains('CANCELLED'),
),
),
);
},
);
test(
'Cancelling a call correctly complete trailers futures',
() async {
final clientCall = harness.client.unary(dummyValue);
Future.delayed(
Duration(milliseconds: cancelDurationMillis),
).then((_) {
clientCall.cancel();
});
expect(
await clientCall.trailers,
isEmpty,
);
await expectLater(
clientCall,
throwsA(
isA<GrpcError>().having(
(e) => e.codeName,
'Test codename',
contains('CANCELLED'),
),
),
);
},
);
} }

View File

@ -1,3 +1,18 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'dart:async'; import 'dart:async';
import 'package:grpc/grpc.dart'; import 'package:grpc/grpc.dart';

View File

@ -1,3 +1,18 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'package:fake_async/fake_async.dart'; import 'package:fake_async/fake_async.dart';
import 'package:grpc/src/client/client_keepalive.dart'; import 'package:grpc/src/client/client_keepalive.dart';
import 'package:mockito/annotations.dart'; import 'package:mockito/annotations.dart';

View File

@ -1,4 +1,20 @@
// Mocks generated by Mockito 5.4.4 from annotations // Mocks generated by Mockito 5.4.4 from annotations
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Mocks generated by Mockito 5.4.1 from annotations
// in grpc/test/client_tests/client_keepalive_manager_test.dart. // in grpc/test/client_tests/client_keepalive_manager_test.dart.
// Do not manually edit this file. // Do not manually edit this file.

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('browser') @TestOn('browser')
library;
import 'dart:async'; import 'dart:async';
import 'dart:html'; import 'dart:html';
@ -64,7 +65,7 @@ class MockHttpRequest extends Mock implements HttpRequest {
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:0'));
late MockHttpRequest latestRequest; late MockHttpRequest latestRequest;
final int _statusCode; final int _statusCode;

View File

@ -13,12 +13,14 @@
// 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('!browser') @TestOn('!browser')
library;
import 'package:grpc/grpc.dart'; import 'package:grpc/grpc.dart';
import 'package:grpc/grpc_or_grpcweb.dart'; import 'package:grpc/grpc_or_grpcweb.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
const host = 'example.com'; const host = 'example.com';
const port = 8080; const port = 0;
void main() { void main() {
test('Channel on non-web uses gRPC ClientChannel with correct params', () { test('Channel on non-web uses gRPC ClientChannel with correct params', () {

View File

@ -13,13 +13,14 @@
// 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('browser') @TestOn('browser')
library;
import 'package:grpc/grpc_or_grpcweb.dart'; import 'package:grpc/grpc_or_grpcweb.dart';
import 'package:grpc/grpc_web.dart'; import 'package:grpc/grpc_web.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
const host = 'example.com'; const host = 'example.com';
const port = 8080; const port = 0;
void main() { void main() {
test('Channel on web uses GrpcWebClientChannel with correct URI', () { test('Channel on web uses GrpcWebClientChannel with correct URI', () {

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')
library;
import 'dart:async'; import 'dart:async';

View File

@ -0,0 +1,44 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
@TestOn('vm')
library;
import 'package:grpc/src/shared/codec.dart';
import 'package:grpc/src/shared/message.dart';
import 'package:test/test.dart';
void main() {
group('GRPC Compression Flag', () {
test('compression flag 0 with null codec', () {
final rawPayload = <int>[1, 2, 3, 4];
final Codec? codec = null;
final data = frame(rawPayload, codec);
expect(data[0], 0);
});
test('compression flag 0 with grpc-encoding identity', () {
final rawPayload = <int>[1, 2, 3, 4];
final Codec codec = IdentityCodec();
final data = frame(rawPayload, codec);
expect(data[0], 0);
});
test('compression flag 1 with grpc-encoding gzip', () {
final rawPayload = <int>[1, 2, 3, 4];
final Codec codec = GzipCodec();
final data = frame(rawPayload, codec);
expect(data[0], 1);
});
});
}

View File

@ -1,3 +1,18 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'dart:async'; import 'dart:async';
import 'dart:typed_data'; import 'dart:typed_data';

View File

@ -1,3 +1,18 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';

View File

@ -1,4 +1,21 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
@TestOn('browser') @TestOn('browser')
library;
import 'dart:async'; import 'dart:async';
import 'dart:math' as math; import 'dart:math' as math;

View File

@ -1,4 +1,21 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
@TestOn('vm') @TestOn('vm')
library;
import 'dart:async'; import 'dart:async';
import 'package:grpc/grpc.dart'; import 'package:grpc/grpc.dart';
@ -15,16 +32,20 @@ void main() {
late EchoServiceClient fakeClient; late EchoServiceClient fakeClient;
late FakeClientChannel fakeChannel; late FakeClientChannel fakeChannel;
late EchoServiceClient unresponsiveClient; late EchoServiceClient unresponsiveClient;
late ClientChannel unresponsiveChannel; late FakeClientChannel unresponsiveChannel;
final pingInterval = Duration(milliseconds: 10);
final timeout = Duration(milliseconds: 30);
final maxBadPings = 5;
setUp(() async { setUp(() async {
final serverOptions = ServerKeepAliveOptions( final serverOptions = ServerKeepAliveOptions(
maxBadPings: 5, maxBadPings: maxBadPings,
minIntervalBetweenPingsWithoutData: Duration(milliseconds: 10), minIntervalBetweenPingsWithoutData: Duration(milliseconds: 10),
); );
final clientOptions = ClientKeepAliveOptions( final clientOptions = ClientKeepAliveOptions(
pingInterval: Duration(milliseconds: 10), pingInterval: pingInterval,
timeout: Duration(milliseconds: 30), timeout: timeout,
permitWithoutCalls: true, permitWithoutCalls: true,
); );
@ -32,7 +53,7 @@ void main() {
services: [FakeEchoService()], services: [FakeEchoService()],
keepAliveOptions: serverOptions, keepAliveOptions: serverOptions,
); );
await server.serve(address: 'localhost', port: 8081); await server.serve(address: 'localhost', port: 0);
fakeChannel = FakeClientChannel( fakeChannel = FakeClientChannel(
'localhost', 'localhost',
port: server.port!, port: server.port!,
@ -62,7 +83,7 @@ void main() {
test('Server terminates connection after too many pings without data', test('Server terminates connection after too many pings without data',
() async { () async {
await fakeClient.echo(EchoRequest()); await fakeClient.echo(EchoRequest());
await Future.delayed(Duration(milliseconds: 300)); await Future.delayed(timeout * maxBadPings * 2);
await fakeClient.echo(EchoRequest()); await fakeClient.echo(EchoRequest());
// Check that the server closed the connection, the next request then has // Check that the server closed the connection, the next request then has
// to build a new one. // to build a new one.
@ -71,23 +92,27 @@ void main() {
test('Server doesnt terminate connection after pings, as data is sent', test('Server doesnt terminate connection after pings, as data is sent',
() async { () async {
final timer = Timer.periodic( for (var i = 0; i < 10; i++) {
Duration(milliseconds: 10), (timer) => fakeClient.echo(EchoRequest())); await fakeClient.echo(EchoRequest());
await Future.delayed(Duration(milliseconds: 200), () => timer.cancel()); await Future.delayed(timeout * 0.2);
}
// Wait for last request to be sent
await Future.delayed(Duration(milliseconds: 20));
// Check that the server never closed the connection // Check that the server never closed the connection
expect(fakeChannel.newConnectionCounter, 1); expect(fakeChannel.newConnectionCounter, 1);
}); });
test('Server doesnt ack the ping, making the client shutdown the connection', test('Server doesnt ack the ping, making the client shutdown the transport',
() async { () async {
//Send a first request, get a connection
await unresponsiveClient.echo(EchoRequest()); await unresponsiveClient.echo(EchoRequest());
await Future.delayed(Duration(milliseconds: 200)); expect(unresponsiveChannel.newConnectionCounter, 1);
await expectLater(
unresponsiveClient.echo(EchoRequest()), throwsA(isA<GrpcError>())); //Ping is not being acked on time
await Future.delayed(timeout * 2);
//A second request gets a new connection
await unresponsiveClient.echo(EchoRequest());
expect(unresponsiveChannel.newConnectionCounter, 2);
}); });
} }
@ -96,7 +121,7 @@ class FakeClientChannel extends ClientChannel {
FakeHttp2ClientConnection? fakeHttp2ClientConnection; FakeHttp2ClientConnection? fakeHttp2ClientConnection;
FakeClientChannel( FakeClientChannel(
super.host, { super.host, {
super.port = 443, super.port,
super.options = const ChannelOptions(), super.options = const ChannelOptions(),
super.channelShutdownHandler, super.channelShutdownHandler,
}); });
@ -125,20 +150,23 @@ class FakeHttp2ClientConnection extends Http2ClientConnection {
} }
/// A wrapper around a [FakeHttp2ClientConnection] /// A wrapper around a [FakeHttp2ClientConnection]
class UnresponsiveClientChannel extends ClientChannel { class UnresponsiveClientChannel extends FakeClientChannel {
UnresponsiveClientChannel( UnresponsiveClientChannel(
super.host, { super.host, {
super.port = 443, super.port,
super.options = const ChannelOptions(), super.options = const ChannelOptions(),
super.channelShutdownHandler, super.channelShutdownHandler,
}); });
@override @override
ClientConnection createConnection() => ClientConnection createConnection() {
UnresponsiveHttp2ClientConnection(host, port, options); fakeHttp2ClientConnection =
UnresponsiveHttp2ClientConnection(host, port, options);
return fakeHttp2ClientConnection!;
}
} }
class UnresponsiveHttp2ClientConnection extends Http2ClientConnection { class UnresponsiveHttp2ClientConnection extends FakeHttp2ClientConnection {
UnresponsiveHttp2ClientConnection(super.host, super.port, super.options); UnresponsiveHttp2ClientConnection(super.host, super.port, super.options);
@override @override
@ -172,8 +200,6 @@ class FakeEchoService extends EchoServiceBase {
@override @override
Stream<ServerStreamingEchoResponse> serverStreamingEcho( Stream<ServerStreamingEchoResponse> serverStreamingEcho(
ServiceCall call, ServerStreamingEchoRequest request) { ServiceCall call, ServerStreamingEchoRequest request) =>
// TODO: implement serverStreamingEcho throw UnsupportedError('Not used in this test');
throw UnimplementedError();
}
} }

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')
library;
import 'dart:io'; import 'dart:io';

View File

@ -1,4 +1,21 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
@TestOn('vm') @TestOn('vm')
library;
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
@ -16,13 +33,13 @@ void main() {
server = Server.create(services: [FakeEchoService()]); server = Server.create(services: [FakeEchoService()]);
await server.serve( await server.serve(
address: 'localhost', address: 'localhost',
port: 8888, port: 0,
security: ServerTlsCredentials( security: ServerTlsCredentials(
certificate: File('test/data/localhost.crt').readAsBytesSync(), certificate: File('test/data/localhost.crt').readAsBytesSync(),
privateKey: File('test/data/localhost.key').readAsBytesSync(), privateKey: File('test/data/localhost.key').readAsBytesSync(),
), ),
); );
final proxy = Proxy(host: 'localhost', port: 8080); final proxy = Proxy(host: 'localhost', port: 0);
final proxyCAName = '/CN=mitmproxy/O=mitmproxy'; final proxyCAName = '/CN=mitmproxy/O=mitmproxy';
fakeChannel = ClientChannel( fakeChannel = ClientChannel(

View File

@ -1,4 +1,21 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
@TestOn('vm') @TestOn('vm')
library;
import 'dart:async'; import 'dart:async';
import 'package:grpc/grpc.dart'; import 'package:grpc/grpc.dart';
@ -13,9 +30,9 @@ void main() {
setUp(() async { setUp(() async {
server = Server.create(services: [FakeEchoService()]); server = Server.create(services: [FakeEchoService()]);
await server.serve(address: 'localhost', port: 8888); await server.serve(address: 'localhost', port: 0);
final proxy = Proxy(host: 'localhost', port: 8080); final proxy = Proxy(host: 'localhost', port: 0);
fakeChannel = ClientChannel( fakeChannel = ClientChannel(
'localhost', 'localhost',

View File

@ -1,4 +1,21 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
@TestOn('vm') @TestOn('vm')
library;
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';

View File

@ -14,6 +14,8 @@
// limitations under the License. // limitations under the License.
@TestOn('vm') @TestOn('vm')
library;
import 'package:grpc/grpc.dart'; import 'package:grpc/grpc.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
@ -45,7 +47,7 @@ void main() {
server = Server.create( server = Server.create(
services: [EchoService()], services: [EchoService()],
); );
await server.serve(address: 'localhost', port: 8081); await server.serve(address: 'localhost', port: 0);
channel = ClientChannel( channel = ClientChannel(
'localhost', 'localhost',
port: server.port!, port: server.port!,

View File

@ -1,4 +1,21 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
@TestOn('vm') @TestOn('vm')
library;
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:isolate'; import 'dart:isolate';

View File

@ -1,3 +1,18 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'dart:async'; import 'dart:async';
import 'package:fake_async/fake_async.dart'; import 'package:fake_async/fake_async.dart';

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')
library;
import 'dart:async'; import 'dart:async';

View File

@ -1,3 +1,18 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'package:grpc/src/shared/codec.dart'; import 'package:grpc/src/shared/codec.dart';
import 'package:grpc/src/shared/codec_registry.dart'; import 'package:grpc/src/shared/codec_registry.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';

View File

@ -16,6 +16,8 @@
@TestOn('vm') @TestOn('vm')
@Skip( @Skip(
'Run only as `dart run --enable-vm-service --timeline-streams=Dart test/timeline_test.dart`') 'Run only as `dart run --enable-vm-service --timeline-streams=Dart test/timeline_test.dart`')
library;
import 'dart:async'; import 'dart:async';
import 'dart:developer' as dev; import 'dart:developer' as dev;

View File

@ -1,3 +1,18 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'dart:convert'; import 'dart:convert';
import 'package:grpc/grpc.dart'; import 'package:grpc/grpc.dart';
@ -5,7 +20,7 @@ import 'package:grpc/src/client/http2_connection.dart';
import 'package:http2/http2.dart'; import 'package:http2/http2.dart';
Future<void> main(List<String> args) async { Future<void> main(List<String> args) async {
final serverPort = 5678; final serverPort = 0;
final proxyPort = int.tryParse(args.first); final proxyPort = int.tryParse(args.first);
final proxy = final proxy =
@ -22,7 +37,7 @@ Future<void> main(List<String> args) async {
final incoming = final incoming =
proxy == null ? connector.socket : await connector.connectToProxy(proxy); proxy == null ? connector.socket : await connector.connectToProxy(proxy);
final uri = Uri.parse('http://localhost:8080'); final uri = Uri.parse('http://localhost:0');
final transport = final transport =
ClientTransportConnection.viaStreams(incoming, connector.socket); ClientTransportConnection.viaStreams(incoming, connector.socket);

View File

@ -1,3 +1,18 @@
// Copyright (c) 2024, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';