mirror of https://github.com/grpc/grpc-dart.git
Improve interfaces (#163)
ChannelCredentials is now a http2-only-thing ClientCall now asks the Transport about the authority. The Xhr client-channel now takes a Uri.
This commit is contained in:
parent
afe0aea7f5
commit
edc0c19073
|
@ -19,8 +19,7 @@ import 'package:grpc_web/app.dart';
|
|||
import 'package:grpc_web/src/generated/echo.pbgrpc.dart';
|
||||
|
||||
void main() {
|
||||
final channel = new GrpcWebClientChannel.xhr('http://localhost',
|
||||
port: 8080);
|
||||
final channel = new GrpcWebClientChannel.xhr(Uri.parse('http://localhost:8080'));
|
||||
final service = EchoServiceClient(channel);
|
||||
final app = EchoApp(service);
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ class Tester {
|
|||
if (_useTestCA) {
|
||||
trustedRoot = new File('ca.pem').readAsBytesSync();
|
||||
}
|
||||
credentials = new Http2ChannelCredentials.secure(
|
||||
credentials = new ChannelCredentials.secure(
|
||||
certificates: trustedRoot, authority: serverHostOverride);
|
||||
} else {
|
||||
credentials = const ChannelCredentials.insecure();
|
||||
|
@ -472,8 +472,7 @@ class Tester {
|
|||
responses.map((response) => response.payload.body.length).toList();
|
||||
|
||||
if (!new ListEquality().equals(responseLengths, expectedResponses)) {
|
||||
throw 'Incorrect response lengths received (${responseLengths.join(
|
||||
', ')} != ${expectedResponses.join(', ')})';
|
||||
throw 'Incorrect response lengths received (${responseLengths.join(', ')} != ${expectedResponses.join(', ')})';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -586,8 +585,7 @@ class Tester {
|
|||
requests.add(index);
|
||||
await for (final response in responses) {
|
||||
if (index >= expectedResponses.length) {
|
||||
throw 'Received too many responses. $index > ${expectedResponses
|
||||
.length}.';
|
||||
throw 'Received too many responses. $index > ${expectedResponses.length}.';
|
||||
}
|
||||
if (response.payload.body.length != expectedResponses[index]) {
|
||||
throw 'Response mismatch for response $index: '
|
||||
|
|
|
@ -32,14 +32,15 @@ export 'src/client/options.dart'
|
|||
defaultIdleTimeout,
|
||||
BackoffStrategy,
|
||||
defaultBackoffStrategy,
|
||||
ChannelCredentials,
|
||||
ChannelOptions,
|
||||
MetadataProvider,
|
||||
CallOptions;
|
||||
|
||||
// TODO(sigurdm): Get rid of Http2ChannelCredentials.
|
||||
export 'src/client/transport/http2_credentials.dart'
|
||||
show BadCertificateHandler, allowBadCertificates, Http2ChannelCredentials;
|
||||
show
|
||||
BadCertificateHandler,
|
||||
allowBadCertificates,
|
||||
ChannelCredentials,
|
||||
ChannelOptions;
|
||||
|
||||
export 'src/server/call.dart' show ServiceCall;
|
||||
export 'src/server/handler.dart' show ServerHandler;
|
||||
|
|
|
@ -31,7 +31,6 @@ export 'src/client/options.dart'
|
|||
defaultIdleTimeout,
|
||||
BackoffStrategy,
|
||||
defaultBackoffStrategy,
|
||||
ChannelCredentials,
|
||||
ChannelOptions,
|
||||
MetadataProvider,
|
||||
CallOptions;
|
||||
|
|
|
@ -59,8 +59,6 @@ class ClientCall<Q, R> implements Response {
|
|||
}
|
||||
}
|
||||
|
||||
String get path => _method.path;
|
||||
|
||||
void onConnectionError(error) {
|
||||
_terminateWithError(new GrpcError.unavailable('Error connecting: $error'));
|
||||
}
|
||||
|
@ -91,16 +89,10 @@ class ClientCall<Q, R> implements Response {
|
|||
_sendRequest(connection, _sanitizeMetadata(options.metadata));
|
||||
} else {
|
||||
final metadata = new Map<String, String>.from(options.metadata);
|
||||
String audience;
|
||||
if (connection.options.credentials.isSecure) {
|
||||
final port = connection.port != 443 ? ':${connection.port}' : '';
|
||||
final lastSlashPos = path.lastIndexOf('/');
|
||||
final audiencePath =
|
||||
lastSlashPos == -1 ? path : path.substring(0, lastSlashPos);
|
||||
audience = 'https://${connection.authority}$port$audiencePath';
|
||||
}
|
||||
Future.forEach(options.metadataProviders,
|
||||
(provider) => provider(metadata, audience))
|
||||
Future.forEach(
|
||||
options.metadataProviders,
|
||||
(provider) => provider(
|
||||
metadata, "${connection.authority}${audiencePath(_method)}"))
|
||||
.then((_) => _sendRequest(connection, _sanitizeMetadata(metadata)))
|
||||
.catchError(_onMetadataProviderError);
|
||||
}
|
||||
|
@ -113,7 +105,7 @@ class ClientCall<Q, R> implements Response {
|
|||
void _sendRequest(ClientConnection connection, Map<String, String> metadata) {
|
||||
try {
|
||||
_stream = connection.makeRequest(
|
||||
path, options.timeout, metadata, _onRequestError);
|
||||
_method.path, options.timeout, metadata, _onRequestError);
|
||||
} catch (e) {
|
||||
_terminateWithError(new GrpcError.unavailable('Error making call: $e'));
|
||||
return;
|
||||
|
|
|
@ -21,59 +21,58 @@ import 'call.dart';
|
|||
import 'connection.dart';
|
||||
import 'method.dart';
|
||||
import 'options.dart';
|
||||
import 'transport/transport.dart';
|
||||
|
||||
typedef ConnectTransport = Future<Transport> Function(
|
||||
String host, int port, ChannelOptions options);
|
||||
|
||||
/// A channel to a virtual RPC endpoint.
|
||||
///
|
||||
/// For each RPC, the channel picks a [ClientConnection] to dispatch the call.
|
||||
/// RPCs on the same channel may be sent to different connections, depending on
|
||||
/// load balancing settings.
|
||||
abstract class ClientChannel {
|
||||
final String host;
|
||||
final int port;
|
||||
final ChannelOptions options;
|
||||
final ConnectTransport connectTransport;
|
||||
/// Shuts down this channel.
|
||||
///
|
||||
/// No further RPCs can be made on this channel. RPCs already in progress will
|
||||
/// be allowed to complete.
|
||||
Future<void> shutdown();
|
||||
|
||||
/// Terminates this channel.
|
||||
///
|
||||
/// RPCs already in progress will be terminated. No further RPCs can be made
|
||||
/// on this channel.
|
||||
Future<void> terminate();
|
||||
|
||||
/// Initiates a new RPC on this connection.
|
||||
ClientCall<Q, R> createCall<Q, R>(
|
||||
ClientMethod<Q, R> method, Stream<Q> requests, CallOptions options);
|
||||
}
|
||||
|
||||
/// Auxiliary base class implementing much of ClientChannel.
|
||||
abstract class ClientChannelBase implements ClientChannel {
|
||||
// TODO(jakobr): Multiple connections, load balancing.
|
||||
ClientConnection _connection;
|
||||
|
||||
bool _isShutdown = false;
|
||||
|
||||
ClientChannel(this.host, this.connectTransport,
|
||||
{this.port = 443, this.options = const ChannelOptions()});
|
||||
ClientChannelBase();
|
||||
|
||||
/// Shuts down this channel.
|
||||
///
|
||||
/// No further RPCs can be made on this channel. RPCs already in progress will
|
||||
/// be allowed to complete.
|
||||
@override
|
||||
Future<void> shutdown() async {
|
||||
if (_isShutdown) return;
|
||||
_isShutdown = true;
|
||||
if (_connection != null) await _connection.shutdown();
|
||||
}
|
||||
|
||||
/// Terminates this channel.
|
||||
///
|
||||
/// RPCs already in progress will be terminated. No further RPCs can be made
|
||||
/// on this channel.
|
||||
@override
|
||||
Future<void> terminate() async {
|
||||
_isShutdown = true;
|
||||
if (_connection != null) await _connection.terminate();
|
||||
}
|
||||
|
||||
ClientConnection createConnection();
|
||||
|
||||
/// Returns a connection to this [Channel]'s RPC endpoint.
|
||||
///
|
||||
/// The connection may be shared between multiple RPCs.
|
||||
Future<ClientConnection> getConnection() async {
|
||||
if (_isShutdown) throw new GrpcError.unavailable('Channel shutting down.');
|
||||
return _connection ??=
|
||||
new ClientConnection(host, port, options, connectTransport);
|
||||
return _connection ??= createConnection();
|
||||
}
|
||||
|
||||
/// Initiates a new RPC on this connection.
|
||||
ClientCall<Q, R> createCall<Q, R>(
|
||||
ClientMethod<Q, R> method, Stream<Q> requests, CallOptions options) {
|
||||
final call = new ClientCall(method, requests, options);
|
||||
|
|
|
@ -39,14 +39,9 @@ enum ConnectionState {
|
|||
shutdown
|
||||
}
|
||||
|
||||
/// A connection to a single RPC endpoint.
|
||||
///
|
||||
/// RPCs made on a connection are always sent to the same endpoint.
|
||||
class ClientConnection {
|
||||
final String host;
|
||||
final int port;
|
||||
final ChannelOptions options;
|
||||
final ConnectTransport connectTransport;
|
||||
final Future<Transport> Function() _connectTransport;
|
||||
|
||||
ConnectionState _state = ConnectionState.idle;
|
||||
void Function(ClientConnection connection) onStateChanged;
|
||||
|
@ -57,20 +52,19 @@ class ClientConnection {
|
|||
/// Used for idle and reconnect timeout, depending on [_state].
|
||||
Timer _timer;
|
||||
Duration _currentReconnectDelay;
|
||||
String get authority => _transport.authority;
|
||||
|
||||
ClientConnection(this.host, this.port, this.options, this.connectTransport);
|
||||
ClientConnection(this.options, this._connectTransport);
|
||||
|
||||
ConnectionState get state => _state;
|
||||
|
||||
String get authority => options.credentials.authority ?? host;
|
||||
|
||||
void _connect() {
|
||||
if (_state != ConnectionState.idle &&
|
||||
_state != ConnectionState.transientFailure) {
|
||||
return;
|
||||
}
|
||||
_setState(ConnectionState.connecting);
|
||||
connectTransport(host, port, options).then((transport) {
|
||||
_connectTransport().then((transport) {
|
||||
_currentReconnectDelay = null;
|
||||
_transport = transport;
|
||||
_transport.onActiveStateChanged = _handleActiveStateChanged;
|
||||
|
|
|
@ -14,21 +14,35 @@
|
|||
// limitations under the License.
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import 'channel.dart' as channel;
|
||||
import 'options.dart';
|
||||
import 'channel.dart';
|
||||
import 'connection.dart';
|
||||
import 'transport/http2_credentials.dart';
|
||||
import 'transport/http2_transport.dart';
|
||||
import 'transport/transport.dart';
|
||||
|
||||
@visibleForTesting
|
||||
Future<Transport> connectTransport(
|
||||
String host, int port, ChannelOptions options) async {
|
||||
return Http2Transport(host, port, options)..connect();
|
||||
}
|
||||
/// A channel to a virtual gRPC endpoint.
|
||||
///
|
||||
/// For each RPC, the channel picks a [ClientConnection] to dispatch the call.
|
||||
/// RPCs on the same channel may be sent to different connections, depending on
|
||||
/// load balancing settings.
|
||||
class ClientChannel extends ClientChannelBase {
|
||||
final String host;
|
||||
final int port;
|
||||
final ChannelOptions options;
|
||||
|
||||
class ClientChannel extends channel.ClientChannel {
|
||||
ClientChannel(String host,
|
||||
{int port = 443, ChannelOptions options = const ChannelOptions()})
|
||||
: super(host, connectTransport, port: port, options: options);
|
||||
ClientChannel(this.host,
|
||||
{this.port = 443, this.options = const ChannelOptions()})
|
||||
: super();
|
||||
|
||||
Future<Transport> _connectTransport() async {
|
||||
final result = Http2Transport(host, port, options.credentials);
|
||||
await result.connect();
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
ClientConnection createConnection() {
|
||||
return ClientConnection(options, _connectTransport);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,3 +21,11 @@ class ClientMethod<Q, R> {
|
|||
|
||||
ClientMethod(this.path, this.requestSerializer, this.responseDeserializer);
|
||||
}
|
||||
|
||||
// TODO(sigurdm): Find out why we do this.
|
||||
String audiencePath(ClientMethod method) {
|
||||
final lastSlashPos = method.path.lastIndexOf('/');
|
||||
return lastSlashPos == -1
|
||||
? method.path
|
||||
: method.path.substring(0, lastSlashPos);
|
||||
}
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
const defaultIdleTimeout = const Duration(minutes: 5);
|
||||
|
||||
typedef Duration BackoffStrategy(Duration lastBackoff);
|
||||
|
@ -36,29 +34,15 @@ Duration defaultBackoffStrategy(Duration lastBackoff) {
|
|||
return nextBackoff < _maxBackoff ? nextBackoff : _maxBackoff;
|
||||
}
|
||||
|
||||
/// Options controlling TLS security settings on a [ClientChannel].
|
||||
class ChannelCredentials {
|
||||
final bool isSecure;
|
||||
final String authority;
|
||||
|
||||
@visibleForOverriding
|
||||
const ChannelCredentials(this.isSecure, this.authority);
|
||||
|
||||
/// Disable TLS. RPCs are sent in clear text.
|
||||
const ChannelCredentials.insecure() : this(false, null);
|
||||
}
|
||||
|
||||
/// Options controlling how connections are made on a [ClientChannel].
|
||||
class ChannelOptions {
|
||||
final ChannelCredentials credentials;
|
||||
final Duration idleTimeout;
|
||||
final BackoffStrategy backoffStrategy;
|
||||
|
||||
const ChannelOptions({
|
||||
ChannelCredentials credentials,
|
||||
this.idleTimeout = defaultIdleTimeout,
|
||||
this.backoffStrategy = defaultBackoffStrategy,
|
||||
}) : this.credentials = credentials ?? const ChannelCredentials.insecure();
|
||||
});
|
||||
}
|
||||
|
||||
/// Provides per-RPC metadata.
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
import 'dart:io';
|
||||
|
||||
import '../../shared/security.dart';
|
||||
import '../options.dart';
|
||||
import '../options.dart' as options;
|
||||
|
||||
/// Handler for checking certificates that fail validation. If this handler
|
||||
/// returns `true`, the bad certificate is allowed, and the TLS handshake can
|
||||
|
@ -30,24 +30,38 @@ typedef bool BadCertificateHandler(X509Certificate certificate, String host);
|
|||
/// certificates, etc.
|
||||
bool allowBadCertificates(X509Certificate certificate, String host) => true;
|
||||
|
||||
class Http2ChannelCredentials extends ChannelCredentials {
|
||||
class git ci ChannelOptions extends options.ChannelOptions {
|
||||
final ChannelCredentials credentials;
|
||||
|
||||
const ChannelOptions({
|
||||
this.credentials,
|
||||
Duration idleTimeout = options.defaultIdleTimeout,
|
||||
options.BackoffStrategy backoffStrategy = options.defaultBackoffStrategy,
|
||||
}) : super(idleTimeout: idleTimeout, backoffStrategy: backoffStrategy);
|
||||
}
|
||||
|
||||
class ChannelCredentials {
|
||||
final bool isSecure;
|
||||
final String authority;
|
||||
|
||||
final List<int> _certificateBytes;
|
||||
final String _certificatePassword;
|
||||
final BadCertificateHandler onBadCertificate;
|
||||
|
||||
const Http2ChannelCredentials._(bool isSecure, String authority,
|
||||
this._certificateBytes, this._certificatePassword, this.onBadCertificate)
|
||||
: super(isSecure, authority);
|
||||
const ChannelCredentials._(this.isSecure, this.authority,
|
||||
this._certificateBytes, this._certificatePassword, this.onBadCertificate);
|
||||
|
||||
/// Enable TLS and optionally specify the [certificates] to trust. If
|
||||
/// [certificates] is not provided, the default trust store is used.
|
||||
const Http2ChannelCredentials.secure(
|
||||
const ChannelCredentials.secure(
|
||||
{List<int> certificates,
|
||||
String password,
|
||||
String authority,
|
||||
BadCertificateHandler onBadCertificate})
|
||||
: this._(true, authority, certificates, password, onBadCertificate);
|
||||
|
||||
const ChannelCredentials.insecure() : this._(false, null, null, null, null);
|
||||
|
||||
SecurityContext get securityContext {
|
||||
if (!isSecure) return null;
|
||||
if (_certificateBytes != null) {
|
||||
|
|
|
@ -24,9 +24,7 @@ import '../../shared/message.dart';
|
|||
import '../../shared/streams.dart';
|
||||
import '../../shared/timeout.dart';
|
||||
|
||||
import '../options.dart';
|
||||
|
||||
import 'http2_credentials.dart';
|
||||
import 'http2_credentials.dart' as http2_credentials;
|
||||
import 'transport.dart';
|
||||
|
||||
class Http2TransportStream extends GrpcTransportStream {
|
||||
|
@ -79,14 +77,14 @@ class Http2Transport extends Transport {
|
|||
|
||||
final String host;
|
||||
final int port;
|
||||
final ChannelOptions options;
|
||||
final http2_credentials.ChannelCredentials credentials;
|
||||
|
||||
@visibleForTesting
|
||||
ClientTransportConnection transportConnection;
|
||||
|
||||
Http2Transport(this.host, this.port, this.options);
|
||||
Http2Transport(this.host, this.port, this.credentials);
|
||||
|
||||
String get authority => options.credentials.authority ?? host;
|
||||
String get authority => credentials.authority ?? "$host:$port";
|
||||
|
||||
static List<Header> createCallHeaders(bool useTls, String authority,
|
||||
String path, Duration timeout, Map<String, String> metadata) {
|
||||
|
@ -115,15 +113,12 @@ class Http2Transport extends Transport {
|
|||
Future<void> connect() async {
|
||||
var socket = await Socket.connect(host, port);
|
||||
|
||||
final credentials = options.credentials;
|
||||
if (credentials is Http2ChannelCredentials) {
|
||||
final securityContext = credentials.securityContext;
|
||||
if (securityContext != null) {
|
||||
socket = await SecureSocket.secure(socket,
|
||||
host: authority,
|
||||
context: securityContext,
|
||||
onBadCertificate: _validateBadCertificate);
|
||||
}
|
||||
final securityContext = credentials.securityContext;
|
||||
if (securityContext != null) {
|
||||
socket = await SecureSocket.secure(socket,
|
||||
host: authority,
|
||||
context: securityContext,
|
||||
onBadCertificate: _validateBadCertificate);
|
||||
}
|
||||
socket.done.then(_handleSocketClosed);
|
||||
transportConnection = ClientTransportConnection.viaSocket(socket);
|
||||
|
@ -133,7 +128,7 @@ class Http2Transport extends Transport {
|
|||
GrpcTransportStream makeRequest(String path, Duration timeout,
|
||||
Map<String, String> metadata, ErrorHandler onError) {
|
||||
final headers = createCallHeaders(
|
||||
options.credentials.isSecure, authority, path, timeout, metadata);
|
||||
credentials.isSecure, authority, path, timeout, metadata);
|
||||
final stream = transportConnection.makeRequest(headers);
|
||||
return new Http2TransportStream(stream, onError);
|
||||
}
|
||||
|
@ -149,14 +144,11 @@ class Http2Transport extends Transport {
|
|||
}
|
||||
|
||||
bool _validateBadCertificate(X509Certificate certificate) {
|
||||
final credentials = options.credentials;
|
||||
if (credentials is Http2ChannelCredentials) {
|
||||
final validator = credentials.onBadCertificate;
|
||||
final credentials = this.credentials;
|
||||
final validator = credentials.onBadCertificate;
|
||||
|
||||
if (validator == null) return false;
|
||||
return validator(certificate, authority);
|
||||
}
|
||||
return false;
|
||||
if (validator == null) return false;
|
||||
return validator(certificate, authority);
|
||||
}
|
||||
|
||||
void _handleSocketClosed(_) {
|
||||
|
|
|
@ -32,6 +32,7 @@ abstract class Transport {
|
|||
ActiveStateHandler onActiveStateChanged;
|
||||
SocketClosedHandler onSocketClosed;
|
||||
|
||||
String get authority;
|
||||
Future<void> connect();
|
||||
GrpcTransportStream makeRequest(String path, Duration timeout,
|
||||
Map<String, String> metadata, ErrorHandler onRequestFailure);
|
||||
|
|
|
@ -130,12 +130,13 @@ class XhrTransportStream implements GrpcTransportStream {
|
|||
}
|
||||
|
||||
class XhrTransport extends Transport {
|
||||
final String host;
|
||||
final int port;
|
||||
final Uri uri;
|
||||
|
||||
HttpRequest _request;
|
||||
|
||||
XhrTransport(this.host, this.port);
|
||||
XhrTransport(this.uri);
|
||||
|
||||
String get authority => uri.authority;
|
||||
|
||||
@override
|
||||
Future<void> connect() async {}
|
||||
|
@ -160,7 +161,7 @@ class XhrTransport extends Transport {
|
|||
GrpcTransportStream makeRequest(String path, Duration timeout,
|
||||
Map<String, String> metadata, ErrorHandler onError) {
|
||||
_request = HttpRequest();
|
||||
_request.open('POST', '${host}:${port}${path}');
|
||||
_request.open('POST', uri.resolve(path).toString());
|
||||
|
||||
initializeRequest(_request, metadata);
|
||||
|
||||
|
|
|
@ -15,20 +15,28 @@
|
|||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:grpc/src/client/options.dart';
|
||||
import 'package:grpc/src/client/transport/transport.dart';
|
||||
import 'package:grpc/src/client/transport/xhr_transport.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import 'channel.dart';
|
||||
import 'connection.dart';
|
||||
import 'options.dart';
|
||||
import 'transport/transport.dart';
|
||||
import 'transport/xhr_transport.dart';
|
||||
|
||||
@visibleForTesting
|
||||
Future<Transport> connectXhrTransport(
|
||||
String host, int port, ChannelOptions _) async {
|
||||
return XhrTransport(host, port)..connect();
|
||||
}
|
||||
/// A channel to a grpc-web endpoint.
|
||||
class GrpcWebClientChannel extends ClientChannelBase {
|
||||
final Uri uri;
|
||||
ChannelOptions options;
|
||||
|
||||
class GrpcWebClientChannel extends ClientChannel {
|
||||
GrpcWebClientChannel.xhr(String host, {int port = 443})
|
||||
: super(host, connectXhrTransport, port: port);
|
||||
GrpcWebClientChannel.xhr(this.uri, {this.options: const ChannelOptions()})
|
||||
: super();
|
||||
|
||||
Future<Transport> _connectXhrTransport() async {
|
||||
final result = XhrTransport(uri);
|
||||
await result.connect();
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
ClientConnection createConnection() {
|
||||
return ClientConnection(options, _connectXhrTransport);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,8 +39,8 @@ class MockHttp2Transport extends Http2Transport {
|
|||
StreamController<StreamMessage> fromClient;
|
||||
StreamController<StreamMessage> toClient;
|
||||
|
||||
MockHttp2Transport(String host, int port, ChannelOptions options)
|
||||
: super(host, port, options);
|
||||
MockHttp2Transport(String host, int port, ChannelCredentials credentials)
|
||||
: super(host, port, credentials);
|
||||
|
||||
@override
|
||||
Future<void> connect() async {
|
||||
|
@ -71,10 +71,7 @@ class MockHttp2Transport extends Http2Transport {
|
|||
|
||||
void main() {
|
||||
final MockHttp2Transport transport = new MockHttp2Transport(
|
||||
'host',
|
||||
9999,
|
||||
ChannelOptions(
|
||||
credentials: new Http2ChannelCredentials.secure(authority: 'test')));
|
||||
'host', 9999, ChannelCredentials.secure(authority: 'test'));
|
||||
|
||||
setUp(() {
|
||||
transport.connect();
|
||||
|
@ -98,7 +95,8 @@ void main() {
|
|||
timeout: toTimeoutString(Duration(seconds: 10)));
|
||||
};
|
||||
|
||||
transport.makeRequest('test_path', Duration(seconds: 10), metadata);
|
||||
transport.makeRequest('test_path', Duration(seconds: 10), metadata,
|
||||
(error) => fail(error.toString()));
|
||||
});
|
||||
|
||||
test('Sent data converted to StreamMessages properly', () async {
|
||||
|
@ -107,8 +105,8 @@ void main() {
|
|||
"parameter_2": "value_2"
|
||||
};
|
||||
|
||||
final stream =
|
||||
transport.makeRequest('test_path', Duration(seconds: 10), metadata);
|
||||
final stream = transport.makeRequest('test_path', Duration(seconds: 10),
|
||||
metadata, (error) => fail(error.toString()));
|
||||
|
||||
transport.fromClient.stream.listen((message) {
|
||||
final dataMessage = validateDataMessage(message);
|
||||
|
@ -124,8 +122,8 @@ void main() {
|
|||
"parameter_2": "value_2"
|
||||
};
|
||||
|
||||
final stream =
|
||||
transport.makeRequest('test_path', Duration(seconds: 10), metadata);
|
||||
final stream = transport.makeRequest('test_path', Duration(seconds: 10),
|
||||
metadata, (error) => fail(error.toString()));
|
||||
|
||||
stream.incomingMessages.listen((message) {
|
||||
expect(message, TypeMatcher<GrpcMetadata>());
|
||||
|
@ -146,8 +144,8 @@ void main() {
|
|||
"parameter_2": "value_2"
|
||||
};
|
||||
|
||||
final stream =
|
||||
transport.makeRequest('test_path', Duration(seconds: 10), metadata);
|
||||
final stream = transport.makeRequest('test_path', Duration(seconds: 10),
|
||||
metadata, (error) => fail(error.toString()));
|
||||
final data = List<int>.filled(10, 0);
|
||||
stream.incomingMessages.listen((message) {
|
||||
expect(message, TypeMatcher<GrpcData>());
|
||||
|
|
|
@ -28,24 +28,25 @@ import 'package:test/test.dart';
|
|||
class MockHttpRequest extends Mock implements HttpRequest {}
|
||||
|
||||
class MockXhrTransport extends XhrTransport {
|
||||
StreamController<Event> readyStateChangeStream = StreamController<Event>();
|
||||
StreamController<ProgressEvent> progressStream =
|
||||
final StreamController<Event> readyStateChangeStream =
|
||||
StreamController<Event>();
|
||||
final StreamController<ProgressEvent> progressStream =
|
||||
StreamController<ProgressEvent>();
|
||||
|
||||
MockHttpRequest mockRequest;
|
||||
|
||||
MockXhrTransport(this.mockRequest) : super('test', 8080) {}
|
||||
MockXhrTransport(this.mockRequest) : super(Uri.parse('test:8080'));
|
||||
|
||||
@override
|
||||
GrpcTransportStream makeRequest(
|
||||
String path, Duration timeout, Map<String, String> metadata) {
|
||||
GrpcTransportStream makeRequest(String path, Duration timeout,
|
||||
Map<String, String> metadata, ErrorHandler onError) {
|
||||
when(mockRequest.onReadyStateChange)
|
||||
.thenAnswer((_) => readyStateChangeStream.stream);
|
||||
when(mockRequest.onProgress).thenAnswer((_) => progressStream.stream);
|
||||
|
||||
initializeRequest(mockRequest, metadata);
|
||||
|
||||
return XhrTransportStream(mockRequest);
|
||||
return XhrTransportStream(mockRequest, onError);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -65,7 +66,8 @@ void main() {
|
|||
final mockRequest = MockHttpRequest();
|
||||
final transport = MockXhrTransport(mockRequest);
|
||||
|
||||
transport.makeRequest('path', Duration(seconds: 10), metadata);
|
||||
transport.makeRequest('path', Duration(seconds: 10), metadata,
|
||||
(error) => fail(error.toString()));
|
||||
|
||||
verify(mockRequest.setRequestHeader(
|
||||
'Content-Type', 'application/grpc-web+proto'));
|
||||
|
@ -84,8 +86,8 @@ void main() {
|
|||
final mockRequest = MockHttpRequest();
|
||||
final transport = MockXhrTransport(mockRequest);
|
||||
|
||||
final stream =
|
||||
transport.makeRequest('path', Duration(seconds: 10), metadata);
|
||||
final stream = transport.makeRequest('path', Duration(seconds: 10),
|
||||
metadata, (error) => fail(error.toString()));
|
||||
|
||||
final data = List.filled(10, 0);
|
||||
stream.outgoingMessages.add(data);
|
||||
|
@ -104,8 +106,8 @@ void main() {
|
|||
final mockRequest = MockHttpRequest();
|
||||
final transport = MockXhrTransport(mockRequest);
|
||||
|
||||
final stream =
|
||||
transport.makeRequest('test_path', Duration(seconds: 10), metadata);
|
||||
final stream = transport.makeRequest('test_path', Duration(seconds: 10),
|
||||
metadata, (error) => fail(error.toString()));
|
||||
|
||||
stream.incomingMessages.listen((message) {
|
||||
expect(message, TypeMatcher<GrpcMetadata>());
|
||||
|
@ -126,8 +128,8 @@ void main() {
|
|||
final mockRequest = MockHttpRequest();
|
||||
final transport = MockXhrTransport(mockRequest);
|
||||
|
||||
final stream =
|
||||
transport.makeRequest('test_path', Duration(seconds: 10), metadata);
|
||||
final stream = transport.makeRequest('test_path', Duration(seconds: 10),
|
||||
metadata, (error) => fail(error.toString()));
|
||||
final data = List<int>.filled(10, 224);
|
||||
final encoded = frame(data);
|
||||
final encodedString = String.fromCharCodes(encoded);
|
||||
|
@ -164,8 +166,8 @@ void main() {
|
|||
final mockRequest = MockHttpRequest();
|
||||
final transport = MockXhrTransport(mockRequest);
|
||||
|
||||
final stream =
|
||||
transport.makeRequest('test_path', Duration(seconds: 10), metadata);
|
||||
final stream = transport.makeRequest('test_path', Duration(seconds: 10),
|
||||
metadata, (error) => fail(error.toString()));
|
||||
|
||||
final data = <List<int>>[
|
||||
List<int>.filled(10, 224),
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:grpc/grpc.dart';
|
||||
import 'package:grpc/src/client/transport/http2_credentials.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
const isTlsException = const TypeMatcher<TlsException>();
|
||||
|
@ -28,14 +28,14 @@ void main() {
|
|||
await new File('test/data/certstore.p12').readAsBytes();
|
||||
|
||||
final missingPassword =
|
||||
new Http2ChannelCredentials.secure(certificates: certificates);
|
||||
ChannelCredentials.secure(certificates: certificates);
|
||||
expect(() => missingPassword.securityContext, throwsA(isTlsException));
|
||||
|
||||
final wrongPassword = new Http2ChannelCredentials.secure(
|
||||
final wrongPassword = ChannelCredentials.secure(
|
||||
certificates: certificates, password: 'wrong');
|
||||
expect(() => wrongPassword.securityContext, throwsA(isTlsException));
|
||||
|
||||
final correctPassword = new Http2ChannelCredentials.secure(
|
||||
final correctPassword = ChannelCredentials.secure(
|
||||
certificates: certificates, password: 'correct');
|
||||
expect(correctPassword.securityContext, isNotNull);
|
||||
});
|
||||
|
|
|
@ -15,13 +15,19 @@
|
|||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:grpc/src/client/call.dart';
|
||||
import 'package:grpc/src/client/channel.dart';
|
||||
import 'package:grpc/src/client/channel.dart';
|
||||
import 'package:grpc/src/client/client.dart';
|
||||
import 'package:grpc/src/client/common.dart';
|
||||
import 'package:grpc/src/client/connection.dart';
|
||||
import 'package:grpc/src/client/method.dart';
|
||||
import 'package:grpc/src/client/options.dart';
|
||||
import 'package:grpc/src/shared/message.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
||||
import 'package:grpc/src/client/transport/transport.dart';
|
||||
import 'package:grpc/src/client/channel.dart' show ConnectTransport;
|
||||
import 'package:grpc/grpc.dart';
|
||||
|
||||
import 'utils.dart';
|
||||
|
||||
|
@ -42,13 +48,13 @@ class FakeConnection extends ClientConnection {
|
|||
var connectionError;
|
||||
|
||||
FakeConnection._(String host, Transport transport, ChannelOptions options,
|
||||
ConnectTransport connectTransport)
|
||||
: super(host, 443, options, connectTransport);
|
||||
Future<Transport> Function() connectTransport)
|
||||
: super(options, connectTransport);
|
||||
|
||||
factory FakeConnection(
|
||||
String host, Transport transport, ChannelOptions options) {
|
||||
FakeConnection f;
|
||||
f = FakeConnection._(host, transport, options, (_, _1, _2) async {
|
||||
f = FakeConnection._(host, transport, options, () async {
|
||||
if (f.connectionError != null) throw f.connectionError;
|
||||
return transport;
|
||||
});
|
||||
|
@ -59,20 +65,18 @@ class FakeConnection extends ClientConnection {
|
|||
Duration testBackoff(Duration lastBackoff) => const Duration(milliseconds: 1);
|
||||
|
||||
class FakeChannelOptions implements ChannelOptions {
|
||||
ChannelCredentials credentials = const Http2ChannelCredentials.secure();
|
||||
Duration idleTimeout = const Duration(seconds: 1);
|
||||
BackoffStrategy backoffStrategy = testBackoff;
|
||||
}
|
||||
|
||||
class FakeChannel extends ClientChannel {
|
||||
class FakeChannel extends ClientChannelBase {
|
||||
final ClientConnection connection;
|
||||
final FakeChannelOptions options;
|
||||
|
||||
FakeChannel(String host, this.connection, this.options)
|
||||
: super(host, options: options);
|
||||
FakeChannel(String host, this.connection, this.options);
|
||||
|
||||
@override
|
||||
Future<ClientConnection> getConnection() async => connection;
|
||||
ClientConnection createConnection() => connection;
|
||||
}
|
||||
|
||||
class TestClient extends Client {
|
||||
|
@ -134,7 +138,7 @@ class ClientHarness {
|
|||
stream = new MockStream();
|
||||
fromClient = new StreamController();
|
||||
toClient = new StreamController();
|
||||
when(transport.makeRequest(any, any, any)).thenReturn(stream);
|
||||
when(transport.makeRequest(any, any, any, any)).thenReturn(stream);
|
||||
when(transport.onActiveStateChanged = captureAny).thenReturn(null);
|
||||
when(stream.outgoingMessages).thenReturn(fromClient.sink);
|
||||
when(stream.incomingMessages).thenAnswer((_) => toClient.stream);
|
||||
|
@ -191,9 +195,9 @@ class ClientHarness {
|
|||
expect(result, expectedResult);
|
||||
}
|
||||
|
||||
final capturedParameters =
|
||||
verify(transport.makeRequest(captureAny, captureAny, captureAny))
|
||||
.captured;
|
||||
final capturedParameters = verify(transport.makeRequest(
|
||||
captureAny, captureAny, captureAny, captureAny))
|
||||
.captured;
|
||||
if (expectedPath != null) {
|
||||
expect(capturedParameters[0], expectedPath);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue