feat(client): GrpcWebClientChannel on web targets via conditional import
Some checks failed
Security / Security check (push) Failing after 1s
Some checks failed
Security / Security check (push) Failing after 1s
Splits channel construction into two files selected at compile-time via `if (dart.library.html)`: channel_factory_io.dart — native ClientChannel (HTTP/2) channel_factory_web.dart — GrpcWebClientChannel.xhr (HTTP/1.1) HubClient._channel is now `ClientChannelBase` so both implementations satisfy the same field type. dart:html-only imports (`package:grpc/grpc_web.dart`) stay isolated from the native compile path. Pairs with the hub-side `fai_grpc_web` adapter crate (fai/platform 0d07892) — `tonic-web` on the same port as native gRPC, so one `fai serve` listener handles both surfaces. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
5604e5eaf3
commit
f481eee687
3 changed files with 49 additions and 13 deletions
21
lib/src/channel_factory_io.dart
Normal file
21
lib/src/channel_factory_io.dart
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Native channel factory — plain gRPC over HTTP/2.
|
||||||
|
// Selected on every non-web target (macOS, Linux, Windows, iOS,
|
||||||
|
// Android) via the conditional import in hub_client.dart.
|
||||||
|
|
||||||
|
import 'package:grpc/grpc.dart';
|
||||||
|
import 'package:grpc/grpc_connection_interface.dart';
|
||||||
|
|
||||||
|
import 'hub_client.dart' show HubEndpoint;
|
||||||
|
|
||||||
|
ClientChannelBase createChannel(HubEndpoint endpoint) {
|
||||||
|
return ClientChannel(
|
||||||
|
endpoint.host,
|
||||||
|
port: endpoint.port,
|
||||||
|
options: ChannelOptions(
|
||||||
|
credentials: endpoint.secure
|
||||||
|
? const ChannelCredentials.secure()
|
||||||
|
: const ChannelCredentials.insecure(),
|
||||||
|
idleTimeout: const Duration(minutes: 5),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
17
lib/src/channel_factory_web.dart
Normal file
17
lib/src/channel_factory_web.dart
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Web channel factory — gRPC-Web over HTTP/1.1.
|
||||||
|
// Selected on dart:html targets (Flutter web, Dart-for-web) via
|
||||||
|
// the conditional import in hub_client.dart. The hub must be
|
||||||
|
// running with `tonic-web` enabled — see fai_platform docs
|
||||||
|
// architecture/protocol-surfaces.md.
|
||||||
|
|
||||||
|
import 'package:grpc/grpc_connection_interface.dart';
|
||||||
|
import 'package:grpc/grpc_web.dart';
|
||||||
|
|
||||||
|
import 'hub_client.dart' show HubEndpoint;
|
||||||
|
|
||||||
|
ClientChannelBase createChannel(HubEndpoint endpoint) {
|
||||||
|
final scheme = endpoint.secure ? 'https' : 'http';
|
||||||
|
return GrpcWebClientChannel.xhr(
|
||||||
|
Uri.parse('$scheme://${endpoint.host}:${endpoint.port}'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -6,8 +6,10 @@
|
||||||
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:grpc/grpc.dart';
|
import 'package:grpc/grpc_connection_interface.dart';
|
||||||
|
|
||||||
|
import 'channel_factory_io.dart'
|
||||||
|
if (dart.library.html) 'channel_factory_web.dart' as channel_factory;
|
||||||
import 'generated/fai/v1/common.pb.dart' as pb_common;
|
import 'generated/fai/v1/common.pb.dart' as pb_common;
|
||||||
import 'generated/fai/v1/hub.pb.dart' as pb;
|
import 'generated/fai/v1/hub.pb.dart' as pb;
|
||||||
import 'generated/fai/v1/hub.pbgrpc.dart' as grpc_hub;
|
import 'generated/fai/v1/hub.pbgrpc.dart' as grpc_hub;
|
||||||
|
|
@ -78,9 +80,15 @@ typedef SubmitResponse = pb.SubmitResponse;
|
||||||
|
|
||||||
/// Typed client for the F∆I Hub gRPC surface. Construct once,
|
/// Typed client for the F∆I Hub gRPC surface. Construct once,
|
||||||
/// reuse for the process lifetime, call [close] on shutdown.
|
/// reuse for the process lifetime, call [close] on shutdown.
|
||||||
|
///
|
||||||
|
/// The channel type is picked at compile time:
|
||||||
|
/// * native targets → plain HTTP/2 gRPC via [ClientChannel].
|
||||||
|
/// * web targets → HTTP/1.1 gRPC-Web via [GrpcWebClientChannel].
|
||||||
|
/// Requires the hub to be running with the
|
||||||
|
/// `tonic-web` layer enabled.
|
||||||
class HubClient {
|
class HubClient {
|
||||||
final HubEndpoint endpoint;
|
final HubEndpoint endpoint;
|
||||||
final ClientChannel _channel;
|
final ClientChannelBase _channel;
|
||||||
final grpc_hub.HubClient _hub;
|
final grpc_hub.HubClient _hub;
|
||||||
final grpc_hub.HubAdminClient _admin;
|
final grpc_hub.HubAdminClient _admin;
|
||||||
|
|
||||||
|
|
@ -89,17 +97,7 @@ class HubClient {
|
||||||
_admin = grpc_hub.HubAdminClient(_channel);
|
_admin = grpc_hub.HubAdminClient(_channel);
|
||||||
|
|
||||||
factory HubClient({HubEndpoint endpoint = const HubEndpoint()}) {
|
factory HubClient({HubEndpoint endpoint = const HubEndpoint()}) {
|
||||||
final channel = ClientChannel(
|
return HubClient._(endpoint, channel_factory.createChannel(endpoint));
|
||||||
endpoint.host,
|
|
||||||
port: endpoint.port,
|
|
||||||
options: ChannelOptions(
|
|
||||||
credentials: endpoint.secure
|
|
||||||
? const ChannelCredentials.secure()
|
|
||||||
: const ChannelCredentials.insecure(),
|
|
||||||
idleTimeout: const Duration(minutes: 5),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return HubClient._(endpoint, channel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Liveness probe. Returns true when the hub responds with
|
/// Liveness probe. Returns true when the hub responds with
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue