Revert "Merge branch 'merge_upstream_main_into_aot_monorepo_compat'"

This reverts commit 8c497fae08, reversing
changes made to 00b634fde4.
This commit is contained in:
Tsavo Knott 2025-01-03 14:30:29 -05:00
parent 8c497fae08
commit 97f8aee043
77 changed files with 695 additions and 934 deletions

View File

@ -15,10 +15,10 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
sdk: [3.5, dev]
sdk: [3.5.0, dev]
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3
with:
sdk: ${{ matrix.sdk }}
- name: Report version
@ -59,7 +59,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
sdk: [3.5, dev]
sdk: [3.0.0, dev]
platform: [vm, chrome]
exclude:
# We only run Chrome tests on Linux. No need to run them
@ -69,8 +69,8 @@ jobs:
- os: macos-latest
platform: chrome
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3
with:
sdk: ${{ matrix.sdk }}
- name: Report version

View File

@ -1,13 +0,0 @@
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

View File

@ -1,17 +0,0 @@
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,25 +1,3 @@
## 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.
* Update xhr transport to migrate off legacy JS/HTML apis.
## 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
* Forward internal `GrpcError` on when throwing while sending a request.
@ -41,19 +19,19 @@
## 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`.
## 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
for a connection to be established. If `connectTime` is longer than the system
level timeout duration, a timeout may occur sooner than specified in
`connectTimeout`. On timeout, a `SocketException` is thrown.
* Require Dart 2.17 or greater.
* 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.
This callback can be used to react to channel shutdown or termination.
* Export the `Code` protobuf enum from the `grpc.dart` library.

View File

@ -1,14 +1,14 @@
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.
[![Dart](https://github.com/grpc/grpc-dart/actions/workflows/dart.yml/badge.svg)](https://github.com/grpc/grpc-dart/actions/workflows/dart.yml)
[![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)
[![pub package](https://img.shields.io/pub/v/grpc.svg)](https://pub.dev/packages/grpc)
## Learn more
- [Quick Start](https://grpc.io/docs/languages/dart/quickstart) - get an app running in minutes
- [Examples](https://github.com/grpc/grpc-dart/tree/master/example)
- [Examples](example)
- [API reference](https://grpc.io/docs/languages/dart/api)
For complete documentation, see [Dart gRPC](https://grpc.io/docs/languages/dart).

View File

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

View File

@ -1,7 +1,7 @@
Four code examples are available:
1. [helloworld](https://github.com/grpc/grpc-dart/tree/master/example/helloworld):
A demonstration of using the Dart gRPC library to perform unary RPCs.
A demonstration of using the Dart gRPC library to perform unary RPs.
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.

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

View File

@ -0,0 +1,4 @@
# melos_managed_dependency_overrides: grpc
dependency_overrides:
grpc:
path: ../..

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

View File

@ -1,18 +1,3 @@
// 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:html';

View File

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

View File

@ -0,0 +1,4 @@
# melos_managed_dependency_overrides: grpc
dependency_overrides:
grpc:
path: ../..

View File

@ -13,10 +13,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
/// Dart implementation of the gRPC helloworld.Greeter client.
import 'package:grpc/grpc.dart';
import 'package:helloworld/src/generated/helloworld.pbgrpc.dart';
/// Dart implementation of the gRPC helloworld.Greeter client.
Future<void> main(List<String> args) async {
final channel = ClientChannel(
'localhost',

View File

@ -13,10 +13,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
/// Dart implementation of the gRPC helloworld.Greeter server.
import 'package:grpc/grpc.dart';
import 'package:helloworld/src/generated/helloworld.pbgrpc.dart';
/// Dart implementation of the gRPC helloworld.Greeter server.
class GreeterService extends GreeterServiceBase {
@override
Future<HelloReply> sayHello(ServiceCall call, HelloRequest request) async {

View File

@ -13,12 +13,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
/// Dart implementation of the gRPC helloworld.Greeter client.
import 'dart:io';
import 'package:grpc/grpc.dart';
import 'package:helloworld/src/generated/helloworld.pbgrpc.dart';
/// Dart implementation of the gRPC helloworld.Greeter client.
Future<void> main(List<String> args) async {
final udsAddress =
InternetAddress('localhost', type: InternetAddressType.unix);

View File

@ -13,12 +13,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
/// Dart implementation of the gRPC helloworld.Greeter server.
import 'dart:io';
import 'package:grpc/grpc.dart';
import 'package:helloworld/src/generated/helloworld.pbgrpc.dart';
/// Dart implementation of the gRPC helloworld.Greeter server.
class GreeterService extends GreeterServiceBase {
@override
Future<HelloReply> sayHello(ServiceCall call, HelloRequest request) async {

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

View File

@ -0,0 +1,4 @@
# melos_managed_dependency_overrides: grpc
dependency_overrides:
grpc:
path: ../..

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

View File

@ -0,0 +1,4 @@
# melos_managed_dependency_overrides: grpc
dependency_overrides:
grpc:
path: ../..

View File

@ -0,0 +1,4 @@
# melos_managed_dependency_overrides: grpc
dependency_overrides:
grpc:
path: ../..

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

18
grpc.iml Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/example" />
<excludeFolder url="file://$MODULE_DIR$/interop" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

16
interop/interop.iml Normal file
View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

View File

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

View File

@ -3,15 +3,15 @@ description: Dart gRPC interoperability test suite.
publish_to: none
environment:
sdk: ^3.0.0
sdk: '>=3.0.0 <4.0.0'
dependencies:
args: ^2.0.0
async: ^2.2.0
collection: ^1.14.11
args: ^2.5.0
async: ^2.11.0
collection: ^1.18.0
grpc:
path: ../
protobuf: ^3.0.0
protobuf: ^3.1.0
dev_dependencies:
test: ^1.16.0
test: ^1.25.3

View File

@ -0,0 +1,4 @@
# melos_managed_dependency_overrides: grpc
dependency_overrides:
grpc:
path: ..

View File

@ -13,10 +13,6 @@
// See the License for the specific language governing permissions and
// 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_io.dart'
show
@ -43,6 +39,10 @@ export 'src/client/options.dart'
export 'src/client/proxy.dart' show Proxy;
export 'src/client/transport/http2_credentials.dart'
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/interceptor.dart' show Interceptor;
export 'src/server/server.dart'

View File

@ -14,7 +14,7 @@
// limitations under the License.
import 'src/client/grpc_or_grpcweb_channel_grpc.dart'
if (dart.library.js_interop) 'src/client/grpc_or_grpcweb_channel_web.dart';
if (dart.library.html) 'src/client/grpc_or_grpcweb_channel_web.dart';
import 'src/client/http2_channel.dart';
import 'src/client/options.dart';

View File

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

View File

@ -69,7 +69,7 @@ class _CredentialsRefreshingAuthenticator extends HttpBasedAuthenticator {
await super.authenticate(metadata, uri);
if (_quotaProject != null) {
// https://cloud.google.com/apis/docs/system-parameters#definitions
metadata['X-Goog-User-Project'] = _quotaProject;
metadata['X-Goog-User-Project'] = _quotaProject!;
}
}

View File

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

View File

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

View File

@ -58,7 +58,7 @@ class ChannelCredentials {
if (!isSecure) return null;
if (_certificateBytes != null) {
return createSecurityContext(false)
..setTrustedCertificatesBytes(_certificateBytes,
..setTrustedCertificatesBytes(_certificateBytes!,
password: _certificatePassword);
}
final context = SecurityContext(withTrustedRoots: true);

View File

@ -132,16 +132,6 @@ class _GrpcWebConversionSink implements ChunkedConversionSink<ByteBuffer> {
void add(ByteBuffer chunk) {
_chunkOffset = 0;
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) {
switch (_state) {
case _GrpcWebParseState.init:
@ -153,10 +143,6 @@ class _GrpcWebConversionSink implements ChunkedConversionSink<ByteBuffer> {
case _GrpcWebParseState.message:
_parseMessage(chunkData);
break;
// ignore: unreachable_switch_default
default:
// only expected to be hit when hot-restarting, see above
break processingLoop;
}
}
_chunkOffset = 0;

View File

@ -14,11 +14,10 @@
// limitations under the License.
import 'dart:async';
import 'dart:js_interop';
import 'dart:html';
import 'dart:typed_data';
import 'package:meta/meta.dart';
import 'package:web/web.dart';
import '../../client/call.dart';
import '../../shared/message.dart';
@ -31,7 +30,7 @@ import 'web_streams.dart';
const _contentTypeKey = 'Content-Type';
class XhrTransportStream implements GrpcTransportStream {
final IXMLHttpRequest _request;
final HttpRequest _request;
final ErrorHandler _onError;
final Function(XhrTransportStream stream) _onDone;
bool _headersReceived = false;
@ -50,20 +49,19 @@ class XhrTransportStream implements GrpcTransportStream {
{required ErrorHandler onError, required onDone})
: _onError = onError,
_onDone = onDone {
_outgoingMessages.stream.map(frame).listen(
(data) => _request.send(Uint8List.fromList(data).toJS),
cancelOnError: true,
onError: _onError);
_outgoingMessages.stream
.map(frame)
.listen((data) => _request.send(data), cancelOnError: true);
_request.onReadyStateChange.listen((_) {
_request.onReadyStateChange.listen((data) {
if (_incomingProcessor.isClosed) {
return;
}
switch (_request.readyState) {
case XMLHttpRequest.HEADERS_RECEIVED:
case HttpRequest.HEADERS_RECEIVED:
_onHeadersReceived();
break;
case XMLHttpRequest.DONE:
case HttpRequest.DONE:
_onRequestDone();
_close();
break;
@ -83,11 +81,13 @@ class XhrTransportStream implements GrpcTransportStream {
if (_incomingProcessor.isClosed) {
return;
}
final responseText = _request.responseText;
// Use response over responseText as most browsers don't support
// using responseText during an onProgress event.
final responseString = _request.response as String;
final bytes = Uint8List.fromList(
responseText.substring(_requestBytesRead).codeUnits)
responseString.substring(_requestBytesRead).codeUnits)
.buffer;
_requestBytesRead = responseText.length;
_requestBytesRead = responseString.length;
_incomingProcessor.add(bytes);
});
@ -122,11 +122,9 @@ class XhrTransportStream implements GrpcTransportStream {
if (!_headersReceived && !_validateResponseState()) {
return;
}
if (_request.status != 200) {
if (_request.response == null) {
_onError(
GrpcError.unavailable(
'Request failed with status: ${_request.status}',
null,
GrpcError.unavailable('XhrConnection request null response', null,
_request.responseText),
StackTrace.current);
return;
@ -146,110 +144,6 @@ class XhrTransportStream implements GrpcTransportStream {
}
}
// XMLHttpRequest is an extension type and can't be extended or implemented.
// This interface is used to allow for mocking XMLHttpRequest in tests of
// XhrClientConnection.
@visibleForTesting
abstract interface class IXMLHttpRequest {
Stream<Event> get onReadyStateChange;
Stream<ProgressEvent> get onProgress;
Stream<ProgressEvent> get onError;
int get readyState;
JSAny? get response;
String get responseText;
Map<String, String> get responseHeaders;
int get status;
set responseType(String responseType);
set withCredentials(bool withCredentials);
void abort();
void open(
String method,
String url, [
// external default is true
bool async = true,
String? username,
String? password,
]);
void overrideMimeType(String mimeType);
void send([JSAny? body]);
void setRequestHeader(String header, String value);
// This method should only be used in production code.
XMLHttpRequest toXMLHttpRequest();
}
// IXMLHttpRequest that delegates to a real XMLHttpRequest.
class XMLHttpRequestImpl implements IXMLHttpRequest {
final XMLHttpRequest _xhr = XMLHttpRequest();
XMLHttpRequestImpl();
@override
Stream<Event> get onReadyStateChange => _xhr.onReadyStateChange;
@override
Stream<ProgressEvent> get onProgress => _xhr.onProgress;
@override
Stream<ProgressEvent> get onError => _xhr.onError;
@override
int get readyState => _xhr.readyState;
@override
Map<String, String> get responseHeaders => _xhr.responseHeaders;
@override
JSAny? get response => _xhr.response;
@override
String get responseText => _xhr.responseText;
@override
int get status => _xhr.status;
@override
set responseType(String responseType) {
_xhr.responseType = responseType;
}
@override
set withCredentials(bool withCredentials) {
_xhr.withCredentials = withCredentials;
}
@override
void abort() {
_xhr.abort();
}
@override
void open(
String method,
String url, [
bool async = true,
String? username,
String? password,
]) {
_xhr.open(method, url, async, username, password);
}
@override
void overrideMimeType(String mimeType) {
_xhr.overrideMimeType(mimeType);
}
@override
void setRequestHeader(String header, String value) {
_xhr.setRequestHeader(header, value);
}
@override
void send([JSAny? body]) {
_xhr.send(body);
}
@override
XMLHttpRequest toXMLHttpRequest() {
return _xhr;
}
}
class XhrClientConnection implements ClientConnection {
final Uri uri;
@ -259,20 +153,20 @@ class XhrClientConnection implements ClientConnection {
@override
String get authority => uri.authority;
@override
String get scheme => uri.scheme;
void _initializeRequest(
IXMLHttpRequest request, Map<String, String> metadata) {
metadata.forEach(request.setRequestHeader);
void _initializeRequest(HttpRequest request, Map<String, String> metadata) {
for (final header in metadata.keys) {
request.setRequestHeader(header, metadata[header]!);
}
// Overriding the mimetype allows us to stream and parse the data
request.overrideMimeType('text/plain; charset=x-user-defined');
request.responseType = 'text';
}
@visibleForTesting
IXMLHttpRequest createHttpRequest() => XMLHttpRequestImpl();
HttpRequest createHttpRequest() => HttpRequest();
@override
GrpcTransportStream makeRequest(String path, Duration? timeout,
@ -300,16 +194,11 @@ class XhrClientConnection implements ClientConnection {
_initializeRequest(request, metadata);
final transportStream =
_createXhrTransportStream(request, onError, _removeStream);
XhrTransportStream(request, onError: onError, onDone: _removeStream);
_requests.add(transportStream);
return transportStream;
}
XhrTransportStream _createXhrTransportStream(IXMLHttpRequest request,
ErrorHandler onError, void Function(XhrTransportStream stream) onDone) {
return XhrTransportStream(request, onError: onError, onDone: onDone);
}
void _removeStream(XhrTransportStream stream) {
_requests.remove(stream);
}

View File

@ -0,0 +1,14 @@
//
// 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

@ -0,0 +1,79 @@
//
// 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

@ -0,0 +1,47 @@
//
// 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

@ -0,0 +1,186 @@
// 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 =
clientMetadata!['grpc-accept-encoding']?.split(',') ?? [];
_callEncodingCodec = acceptedEncodings
.map(_codecRegistry.lookup)
.map(_codecRegistry!.lookup)
.firstWhere((c) => c != null, orElse: () => null);
}

View File

@ -13,6 +13,56 @@
// See the License for the specific language governing permissions and
// limitations under the License.
export 'codec/codec_all.dart';
export 'codec/codec_io.dart'
if (dart.library.js_interop) 'codec/codec_web.dart'; // package:web implementation
import 'package:archive/archive.dart';
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;
}
}
/// 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

@ -1,48 +0,0 @@
// 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

@ -1,36 +0,0 @@
// 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

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

View File

@ -13,16 +13,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// ignore_for_file: prefer_relative_imports
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: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;
class StatusCode {
@ -150,28 +149,6 @@ class StatusCode {
static int fromHttpStatus(int status) {
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 {
@ -329,8 +306,7 @@ class GrpcError implements Exception {
code = StatusCode.unauthenticated;
/// Given a status code, return the name
String get codeName =>
StatusCode.name(code) ?? StatusCode.name(StatusCode.unknown)!;
String get codeName => (Code.valueOf(code) ?? Code.UNKNOWN).name;
@override
bool operator ==(other) {
@ -352,7 +328,6 @@ class GrpcError implements Exception {
/// 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
/// support them.
@visibleForTesting
GeneratedMessage parseErrorDetailsFromAny(Any any) {
switch (any.typeUrl) {
case 'type.googleapis.com/google.rpc.RetryInfo':
@ -474,7 +449,7 @@ GrpcError? grpcErrorDetailsFromTrailers(Map<String, String> trailers) {
}
Map<String, String> toCustomTrailers(Map<String, String> trailers) {
return Map.of(trailers)
return Map.from(trailers)
..remove(':status')
..remove('content-type')
..remove('grpc-status')

View File

@ -1,41 +1,35 @@
name: grpc
description: Dart implementation of gRPC, a high performance, open-source universal RPC framework.
version: 4.0.2-wip
version: 3.2.4
repository: https://github.com/grpc/grpc-dart
environment:
sdk: ^3.5.0
sdk: '>=3.3.0 <4.5.0'
dependencies:
archive: ^3.4.10
async: ^2.11.0
crypto: ^3.0.3
fixnum: ^1.1.0
googleapis_auth: ^1.6.0
meta: ^1.3.0
meta: ">=1.11.0 <2.0.0"
http: '>=0.13.0 <2.0.0'
http2: ^2.3.0
protobuf: '>=2.0.0 <4.0.0'
clock: ^1.1.1
web: ^1.1.0
dev_dependencies:
build_runner: ^2.4.9
build_test: ^2.2.2
lints: ^5.0.0
lints: ">=2.0.0 <4.0.0"
mockito: ^5.4.4
path: ^1.9.0
test: ^1.25.3
stream_channel: ^2.1.2
stream_transform: ^2.1.0
vm_service: ">=11.6.0 <16.0.0"
vm_service: ">=11.6.0 <15.0.0"
fake_async: ^1.3.1
false_secrets:
- interop/server1.key
- test/data/localhost.key
topics:
- grpc
- rpc
- protocols

View File

@ -1,23 +1,6 @@
// 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
// client certificates so we disable the test.
@TestOn('vm && !mac-os')
library;
import 'dart:async';
import 'dart:io';

View File

@ -1,21 +1,4 @@
// 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 'dart:async';
import 'package:grpc/grpc.dart' as grpc;

View File

@ -1,38 +1,7 @@
// 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:test/test.dart';
import '../src/client_utils.dart';
void main() {
const dummyValue = 0;
const cancelDurationMillis = 300;
late ClientHarness harness;
setUp(() {
harness = ClientHarness()..setUp();
});
tearDown(() {
harness.tearDown();
});
test('WebCallOptions mergeWith CallOptions returns WebCallOptions', () {
final options =
WebCallOptions(bypassCorsPreflight: true, withCredentials: true);
@ -44,57 +13,4 @@ void main() {
expect(mergedOptions.bypassCorsPreflight, 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,18 +1,3 @@
// 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 'package:grpc/grpc.dart';

View File

@ -1,18 +1,3 @@
// 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:grpc/src/client/client_keepalive.dart';
import 'package:mockito/annotations.dart';

View File

@ -1,24 +1,7 @@
// 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
// Mocks generated by Mockito 5.4.4 from annotations
// in grpc/test/client_tests/client_keepalive_manager_test.dart.
// Do not manually edit this file.
// @dart=2.19
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:mockito/mockito.dart' as _i1;
@ -28,6 +11,8 @@ import 'client_keepalive_manager_test.dart' as _i2;
// ignore_for_file: avoid_redundant_argument_values
// ignore_for_file: avoid_setters_without_getters
// ignore_for_file: comment_references
// ignore_for_file: deprecated_member_use
// ignore_for_file: deprecated_member_use_from_same_package
// ignore_for_file: implementation_imports
// ignore_for_file: invalid_use_of_visible_for_testing_member
// ignore_for_file: prefer_const_constructors
@ -47,6 +32,7 @@ class MockPinger extends _i1.Mock implements _i2.Pinger {
),
returnValueForMissingStub: null,
);
@override
void onPingTimeout() => super.noSuchMethod(
Invocation.method(

View File

@ -13,11 +13,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
@TestOn('browser')
library;
import 'dart:async';
import 'dart:js_interop';
import 'dart:typed_data';
import 'dart:html';
import 'package:async/async.dart';
import 'package:grpc/src/client/call.dart';
import 'package:grpc/src/client/transport/xhr_transport.dart';
@ -26,13 +25,12 @@ import 'package:grpc/src/shared/status.dart';
import 'package:mockito/mockito.dart';
import 'package:stream_transform/stream_transform.dart';
import 'package:test/test.dart';
import 'package:web/web.dart';
final readyStateChangeEvent =
Event('readystatechange', EventInit(bubbles: false, cancelable: false));
Event('readystatechange', canBubble: false, cancelable: false);
final progressEvent = ProgressEvent('onloadstart');
class MockHttpRequest extends Mock implements IXMLHttpRequest {
class MockHttpRequest extends Mock implements HttpRequest {
MockHttpRequest({int? code}) : status = code ?? 200;
// ignore: close_sinks
StreamController<Event> readyStateChangeController =
@ -53,10 +51,6 @@ class MockHttpRequest extends Mock implements IXMLHttpRequest {
@override
final int status;
@override
String get responseText =>
super.noSuchMethod(Invocation.getter(#responseText), returnValue: '');
@override
int get readyState =>
super.noSuchMethod(Invocation.getter(#readyState), returnValue: -1);
@ -70,13 +64,13 @@ class MockHttpRequest extends Mock implements IXMLHttpRequest {
class MockXhrClientConnection extends XhrClientConnection {
MockXhrClientConnection({int? code})
: _statusCode = code ?? 200,
super(Uri.parse('test:0'));
super(Uri.parse('test:8080'));
late MockHttpRequest latestRequest;
final int _statusCode;
@override
IXMLHttpRequest createHttpRequest() {
HttpRequest createHttpRequest() {
final request = MockHttpRequest(code: _statusCode);
latestRequest = request;
return request;
@ -213,8 +207,8 @@ void main() {
await stream.terminate();
final expectedData = frame(data);
verify(
connection.latestRequest.send(Uint8List.fromList(expectedData).toJS));
expect(verify(connection.latestRequest.send(captureAny)).captured.single,
expectedData);
});
test('Stream handles headers properly', () async {
@ -230,13 +224,14 @@ void main() {
(error, _) => fail(error.toString()));
when(transport.latestRequest.responseHeaders).thenReturn(responseHeaders);
when(transport.latestRequest.responseText)
when(transport.latestRequest.response)
.thenReturn(String.fromCharCodes(frame(<int>[])));
// Set expectation for request readyState and generate two readyStateChange
// events, so that incomingMessages stream completes.
final readyStates = [XMLHttpRequest.HEADERS_RECEIVED, XMLHttpRequest.DONE];
when(transport.latestRequest.readyState).thenReturnInOrder(readyStates);
final readyStates = [HttpRequest.HEADERS_RECEIVED, HttpRequest.DONE];
when(transport.latestRequest.readyState)
.thenAnswer((_) => readyStates.removeAt(0));
transport.latestRequest.readyStateChangeController
.add(readyStateChangeEvent);
transport.latestRequest.readyStateChangeController
@ -271,12 +266,13 @@ void main() {
final encodedString = String.fromCharCodes(encodedTrailers);
when(connection.latestRequest.responseHeaders).thenReturn(requestHeaders);
when(connection.latestRequest.responseText).thenReturn(encodedString);
when(connection.latestRequest.response).thenReturn(encodedString);
// Set expectation for request readyState and generate events so that
// incomingMessages stream completes.
when(connection.latestRequest.readyState).thenReturnInOrder(
[XMLHttpRequest.HEADERS_RECEIVED, XMLHttpRequest.DONE]);
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);
@ -306,11 +302,13 @@ void main() {
final encodedString = String.fromCharCodes(encoded);
when(connection.latestRequest.responseHeaders).thenReturn(requestHeaders);
when(connection.latestRequest.responseText).thenReturn(encodedString);
when(connection.latestRequest.response).thenReturn(encodedString);
// Set expectation for request readyState and generate events so that
// incomingMessages stream completes.
when(connection.latestRequest.readyState).thenReturnInOrder(
[XMLHttpRequest.HEADERS_RECEIVED, XMLHttpRequest.DONE]);
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);
@ -338,13 +336,14 @@ void main() {
requestHeaders, (error, _) => fail(error.toString()));
final data = List<int>.filled(10, 224);
when(connection.latestRequest.responseHeaders).thenReturn(requestHeaders);
when(connection.latestRequest.responseText)
when(connection.latestRequest.response)
.thenReturn(String.fromCharCodes(frame(data)));
// Set expectation for request readyState and generate events, so that
// incomingMessages stream completes.
when(connection.latestRequest.readyState).thenReturnInOrder(
[XMLHttpRequest.HEADERS_RECEIVED, XMLHttpRequest.DONE]);
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);
@ -369,7 +368,7 @@ void main() {
const errorDetails = 'error details';
when(connection.latestRequest.responseHeaders)
.thenReturn({'content-type': 'application/grpc+proto'});
when(connection.latestRequest.readyState).thenReturn(XMLHttpRequest.DONE);
when(connection.latestRequest.readyState).thenReturn(HttpRequest.DONE);
when(connection.latestRequest.responseText).thenReturn(errorDetails);
connection.latestRequest.readyStateChangeController
.add(readyStateChangeEvent);
@ -398,12 +397,12 @@ void main() {
when(connection.latestRequest.responseHeaders).thenReturn(metadata);
when(connection.latestRequest.readyState)
.thenReturn(XMLHttpRequest.HEADERS_RECEIVED);
.thenReturn(HttpRequest.HEADERS_RECEIVED);
// At first invocation the response should be the the first message, after
// that first + last messages.
var first = true;
when(connection.latestRequest.responseText).thenAnswer((_) {
when(connection.latestRequest.response).thenAnswer((_) {
if (first) {
first = false;
return encodedStrings[0];
@ -411,7 +410,7 @@ void main() {
return encodedStrings[0] + encodedStrings[1];
});
final readyStates = [XMLHttpRequest.HEADERS_RECEIVED, XMLHttpRequest.DONE];
final readyStates = [HttpRequest.HEADERS_RECEIVED, HttpRequest.DONE];
when(connection.latestRequest.readyState)
.thenAnswer((_) => readyStates.removeAt(0));

View File

@ -13,14 +13,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
@TestOn('!browser')
library;
import 'package:grpc/grpc.dart';
import 'package:grpc/grpc_or_grpcweb.dart';
import 'package:test/test.dart';
const host = 'example.com';
const port = 0;
const port = 8080;
void main() {
test('Channel on non-web uses gRPC ClientChannel with correct params', () {

View File

@ -13,14 +13,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
@TestOn('browser')
library;
import 'package:grpc/grpc_or_grpcweb.dart';
import 'package:grpc/grpc_web.dart';
import 'package:test/test.dart';
const host = 'example.com';
const port = 0;
const port = 8080;
void main() {
test('Channel on web uses GrpcWebClientChannel with correct URI', () {

View File

@ -13,7 +13,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
@TestOn('vm')
library;
import 'dart:async';
@ -290,7 +289,7 @@ void main() {
}
GrpcError? interceptor(call, method) {
if (method.name == 'Unary') {
if (method.identifier == 'Unary') {
return null;
}
return GrpcError.unauthenticated('Request is unauthenticated');
@ -312,7 +311,7 @@ void main() {
group('returns error if interceptor blocks request', () {
GrpcError? interceptor(call, method) {
if (method.name == 'Unary') {
if (method.identifier == 'Unary') {
return GrpcError.unauthenticated('Request is unauthenticated');
}
return null;

View File

@ -1,44 +0,0 @@
// 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,18 +1,3 @@
// 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:typed_data';

View File

@ -1,18 +1,3 @@
// 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:convert';
import 'dart:io';

View File

@ -1,21 +1,4 @@
// 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')
library;
import 'dart:async';
import 'dart:math' as math;

View File

@ -1,21 +1,4 @@
// 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 'dart:async';
import 'package:grpc/grpc.dart';
@ -32,20 +15,16 @@ void main() {
late EchoServiceClient fakeClient;
late FakeClientChannel fakeChannel;
late EchoServiceClient unresponsiveClient;
late FakeClientChannel unresponsiveChannel;
final pingInterval = Duration(milliseconds: 10);
final timeout = Duration(milliseconds: 30);
final maxBadPings = 5;
late ClientChannel unresponsiveChannel;
setUp(() async {
final serverOptions = ServerKeepAliveOptions(
maxBadPings: maxBadPings,
maxBadPings: 5,
minIntervalBetweenPingsWithoutData: Duration(milliseconds: 10),
);
final clientOptions = ClientKeepAliveOptions(
pingInterval: pingInterval,
timeout: timeout,
pingInterval: Duration(milliseconds: 10),
timeout: Duration(milliseconds: 30),
permitWithoutCalls: true,
);
@ -53,7 +32,7 @@ void main() {
services: [FakeEchoService()],
keepAliveOptions: serverOptions,
);
await server.serve(address: 'localhost', port: 0);
await server.serve(address: 'localhost', port: 8081);
fakeChannel = FakeClientChannel(
'localhost',
port: server.port!,
@ -83,7 +62,7 @@ void main() {
test('Server terminates connection after too many pings without data',
() async {
await fakeClient.echo(EchoRequest());
await Future.delayed(timeout * maxBadPings * 2);
await Future.delayed(Duration(milliseconds: 300));
await fakeClient.echo(EchoRequest());
// Check that the server closed the connection, the next request then has
// to build a new one.
@ -92,27 +71,23 @@ void main() {
test('Server doesnt terminate connection after pings, as data is sent',
() async {
for (var i = 0; i < 10; i++) {
await fakeClient.echo(EchoRequest());
await Future.delayed(timeout * 0.2);
}
final timer = Timer.periodic(
Duration(milliseconds: 10), (timer) => fakeClient.echo(EchoRequest()));
await Future.delayed(Duration(milliseconds: 200), () => timer.cancel());
// Wait for last request to be sent
await Future.delayed(Duration(milliseconds: 20));
// Check that the server never closed the connection
expect(fakeChannel.newConnectionCounter, 1);
});
test('Server doesnt ack the ping, making the client shutdown the transport',
test('Server doesnt ack the ping, making the client shutdown the connection',
() async {
//Send a first request, get a connection
await unresponsiveClient.echo(EchoRequest());
expect(unresponsiveChannel.newConnectionCounter, 1);
//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);
await Future.delayed(Duration(milliseconds: 200));
await expectLater(
unresponsiveClient.echo(EchoRequest()), throwsA(isA<GrpcError>()));
});
}
@ -121,7 +96,7 @@ class FakeClientChannel extends ClientChannel {
FakeHttp2ClientConnection? fakeHttp2ClientConnection;
FakeClientChannel(
super.host, {
super.port,
super.port = 443,
super.options = const ChannelOptions(),
super.channelShutdownHandler,
});
@ -150,23 +125,20 @@ class FakeHttp2ClientConnection extends Http2ClientConnection {
}
/// A wrapper around a [FakeHttp2ClientConnection]
class UnresponsiveClientChannel extends FakeClientChannel {
class UnresponsiveClientChannel extends ClientChannel {
UnresponsiveClientChannel(
super.host, {
super.port,
super.port = 443,
super.options = const ChannelOptions(),
super.channelShutdownHandler,
});
@override
ClientConnection createConnection() {
fakeHttp2ClientConnection =
UnresponsiveHttp2ClientConnection(host, port, options);
return fakeHttp2ClientConnection!;
}
ClientConnection createConnection() =>
UnresponsiveHttp2ClientConnection(host, port, options);
}
class UnresponsiveHttp2ClientConnection extends FakeHttp2ClientConnection {
class UnresponsiveHttp2ClientConnection extends Http2ClientConnection {
UnresponsiveHttp2ClientConnection(super.host, super.port, super.options);
@override
@ -200,6 +172,8 @@ class FakeEchoService extends EchoServiceBase {
@override
Stream<ServerStreamingEchoResponse> serverStreamingEcho(
ServiceCall call, ServerStreamingEchoRequest request) =>
throw UnsupportedError('Not used in this test');
ServiceCall call, ServerStreamingEchoRequest request) {
// TODO: implement serverStreamingEcho
throw UnimplementedError();
}
}

View File

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

View File

@ -1,21 +1,4 @@
// 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 'dart:async';
import 'dart:io';
@ -33,13 +16,13 @@ void main() {
server = Server.create(services: [FakeEchoService()]);
await server.serve(
address: 'localhost',
port: 0,
port: 8888,
security: ServerTlsCredentials(
certificate: File('test/data/localhost.crt').readAsBytesSync(),
privateKey: File('test/data/localhost.key').readAsBytesSync(),
),
);
final proxy = Proxy(host: 'localhost', port: 0);
final proxy = Proxy(host: 'localhost', port: 8080);
final proxyCAName = '/CN=mitmproxy/O=mitmproxy';
fakeChannel = ClientChannel(

View File

@ -1,21 +1,4 @@
// 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 'dart:async';
import 'package:grpc/grpc.dart';
@ -30,9 +13,9 @@ void main() {
setUp(() async {
server = Server.create(services: [FakeEchoService()]);
await server.serve(address: 'localhost', port: 0);
await server.serve(address: 'localhost', port: 8888);
final proxy = Proxy(host: 'localhost', port: 0);
final proxy = Proxy(host: 'localhost', port: 8080);
fakeChannel = ClientChannel(
'localhost',

View File

@ -1,21 +1,4 @@
// 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 'dart:async';
import 'dart:io';

View File

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

View File

@ -1,21 +1,4 @@
// 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 'dart:async';
import 'dart:io';
import 'dart:isolate';

View File

@ -1,18 +1,3 @@
// 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 'package:fake_async/fake_async.dart';

View File

@ -13,7 +13,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
@TestOn('vm')
library;
import 'dart:async';
@ -304,7 +303,7 @@ void main() {
}
GrpcError? interceptor(call, method) {
if (method.name == 'Unary') {
if (method.identifier == 'Unary') {
return null;
}
return GrpcError.unauthenticated('Request is unauthenticated');
@ -325,8 +324,8 @@ void main() {
});
group('returns error if interceptor blocks request', () {
GrpcError? interceptor(ServiceCall call, ServiceMethod method) {
if (method.name == 'Unary') {
GrpcError? interceptor(call, method) {
if (method.identifier == 'Unary') {
return GrpcError.unauthenticated('Request is unauthenticated');
}
return null;

View File

@ -1,18 +1,3 @@
// 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_registry.dart';
import 'package:test/test.dart';

View File

@ -1,9 +1,7 @@
// Mocks generated by Mockito 5.4.1 from annotations
// Mocks generated by Mockito 5.4.4 from annotations
// in grpc/test/src/client_utils.dart.
// Do not manually edit this file.
// @dart=2.19
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i3;
@ -15,6 +13,8 @@ import 'package:mockito/mockito.dart' as _i1;
// ignore_for_file: avoid_redundant_argument_values
// ignore_for_file: avoid_setters_without_getters
// ignore_for_file: comment_references
// ignore_for_file: deprecated_member_use
// ignore_for_file: deprecated_member_use_from_same_package
// ignore_for_file: implementation_imports
// ignore_for_file: invalid_use_of_visible_for_testing_member
// ignore_for_file: prefer_const_constructors
@ -57,6 +57,7 @@ class MockClientTransportConnection extends _i1.Mock
Invocation.getter(#isOpen),
returnValue: false,
) as bool);
@override
set onActiveStateChanged(_i2.ActiveStateHandler? callback) =>
super.noSuchMethod(
@ -66,21 +67,25 @@ class MockClientTransportConnection extends _i1.Mock
),
returnValueForMissingStub: null,
);
@override
_i3.Future<void> get onInitialPeerSettingsReceived => (super.noSuchMethod(
Invocation.getter(#onInitialPeerSettingsReceived),
returnValue: _i3.Future<void>.value(),
) as _i3.Future<void>);
@override
_i3.Stream<int> get onPingReceived => (super.noSuchMethod(
Invocation.getter(#onPingReceived),
returnValue: _i3.Stream<int>.empty(),
) as _i3.Stream<int>);
@override
_i3.Stream<void> get onFrameReceived => (super.noSuchMethod(
Invocation.getter(#onFrameReceived),
returnValue: _i3.Stream<void>.empty(),
) as _i3.Stream<void>);
@override
_i2.ClientTransportStream makeRequest(
List<_i4.Header>? headers, {
@ -101,6 +106,7 @@ class MockClientTransportConnection extends _i1.Mock
),
),
) as _i2.ClientTransportStream);
@override
_i3.Future<dynamic> ping() => (super.noSuchMethod(
Invocation.method(
@ -109,6 +115,7 @@ class MockClientTransportConnection extends _i1.Mock
),
returnValue: _i3.Future<dynamic>.value(),
) as _i3.Future<dynamic>);
@override
_i3.Future<dynamic> finish() => (super.noSuchMethod(
Invocation.method(
@ -117,6 +124,7 @@ class MockClientTransportConnection extends _i1.Mock
),
returnValue: _i3.Future<dynamic>.value(),
) as _i3.Future<dynamic>);
@override
_i3.Future<dynamic> terminate([int? errorCode]) => (super.noSuchMethod(
Invocation.method(
@ -141,16 +149,19 @@ class MockClientTransportStream extends _i1.Mock
Invocation.getter(#peerPushes),
returnValue: _i3.Stream<_i2.TransportStreamPush>.empty(),
) as _i3.Stream<_i2.TransportStreamPush>);
@override
int get id => (super.noSuchMethod(
Invocation.getter(#id),
returnValue: 0,
) as int);
@override
_i3.Stream<_i2.StreamMessage> get incomingMessages => (super.noSuchMethod(
Invocation.getter(#incomingMessages),
returnValue: _i3.Stream<_i2.StreamMessage>.empty(),
) as _i3.Stream<_i2.StreamMessage>);
@override
_i3.StreamSink<_i2.StreamMessage> get outgoingMessages => (super.noSuchMethod(
Invocation.getter(#outgoingMessages),
@ -159,6 +170,7 @@ class MockClientTransportStream extends _i1.Mock
Invocation.getter(#outgoingMessages),
),
) as _i3.StreamSink<_i2.StreamMessage>);
@override
set onTerminated(void Function(int?)? value) => super.noSuchMethod(
Invocation.setter(
@ -167,6 +179,7 @@ class MockClientTransportStream extends _i1.Mock
),
returnValueForMissingStub: null,
);
@override
void terminate() => super.noSuchMethod(
Invocation.method(
@ -175,6 +188,7 @@ class MockClientTransportStream extends _i1.Mock
),
returnValueForMissingStub: null,
);
@override
void sendHeaders(
List<_i4.Header>? headers, {
@ -188,6 +202,7 @@ class MockClientTransportStream extends _i1.Mock
),
returnValueForMissingStub: null,
);
@override
void sendData(
List<int>? bytes, {

View File

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

View File

@ -1,18 +1,3 @@
// 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 'package:grpc/grpc.dart';
@ -20,7 +5,7 @@ import 'package:grpc/src/client/http2_connection.dart';
import 'package:http2/http2.dart';
Future<void> main(List<String> args) async {
final serverPort = 0;
final serverPort = 5678;
final proxyPort = int.tryParse(args.first);
final proxy =
@ -37,7 +22,7 @@ Future<void> main(List<String> args) async {
final incoming =
proxy == null ? connector.socket : await connector.connectToProxy(proxy);
final uri = Uri.parse('http://localhost:0');
final uri = Uri.parse('http://localhost:8080');
final transport =
ClientTransportConnection.viaStreams(incoming, connector.socket);

View File

@ -1,18 +1,3 @@
// 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:io';