diff --git a/example/hello-tls/receive.dart b/example/hello-tls/receive.dart index 690e96b..5e947af 100644 --- a/example/hello-tls/receive.dart +++ b/example/hello-tls/receive.dart @@ -1,5 +1,5 @@ -import "dart:io"; import "package:dart_amqp/dart_amqp.dart"; +import "package:universal_io/io.dart"; void main(List args) async { var useClientCert = false; diff --git a/example/hello-tls/send.dart b/example/hello-tls/send.dart index 7486827..15c54f3 100644 --- a/example/hello-tls/send.dart +++ b/example/hello-tls/send.dart @@ -1,6 +1,5 @@ -import "dart:io"; - import "package:dart_amqp/dart_amqp.dart"; +import "package:universal_io/io.dart"; void main(List args) async { var useClientCert = false; diff --git a/example/hello/receive.dart b/example/hello/receive.dart index 578bf10..5c1c63c 100644 --- a/example/hello/receive.dart +++ b/example/hello/receive.dart @@ -1,5 +1,5 @@ -import "dart:io"; import "package:dart_amqp/dart_amqp.dart"; +import "package:universal_io/io.dart"; void main() async { Client client = Client(); diff --git a/example/pubsub/receive_logs.dart b/example/pubsub/receive_logs.dart index c1c2352..d2472bd 100644 --- a/example/pubsub/receive_logs.dart +++ b/example/pubsub/receive_logs.dart @@ -1,5 +1,5 @@ -import "dart:io"; import "package:dart_amqp/dart_amqp.dart"; +import "package:universal_io/io.dart"; void main() async { Client client = Client(); diff --git a/example/routing/emit_log_direct.dart b/example/routing/emit_log_direct.dart index 4424611..49958a8 100644 --- a/example/routing/emit_log_direct.dart +++ b/example/routing/emit_log_direct.dart @@ -1,5 +1,5 @@ -import "dart:io"; import "package:dart_amqp/dart_amqp.dart"; +import "package:universal_io/io.dart"; void main(List args) async { if (args.length < 2 || !["info", "warning", "error"].contains(args[0])) { diff --git a/example/routing/receive_logs_direct.dart b/example/routing/receive_logs_direct.dart index 4191b77..7224f0d 100644 --- a/example/routing/receive_logs_direct.dart +++ b/example/routing/receive_logs_direct.dart @@ -1,5 +1,5 @@ -import "dart:io"; import "package:dart_amqp/dart_amqp.dart"; +import "package:universal_io/io.dart"; void main(List args) async { if (args.isEmpty || !args.every(["info", "warning", "error"].contains)) { diff --git a/example/rpc/rpc_client.dart b/example/rpc/rpc_client.dart index 295cb8c..b96fae2 100644 --- a/example/rpc/rpc_client.dart +++ b/example/rpc/rpc_client.dart @@ -1,6 +1,6 @@ -import "dart:io"; import "dart:async"; import "package:dart_amqp/dart_amqp.dart"; +import "package:universal_io/io.dart"; class FibonacciRpcClient { int _nextCorrelationId = 1; diff --git a/example/rpc/rpc_server.dart b/example/rpc/rpc_server.dart index 7ca8ce8..56e9939 100644 --- a/example/rpc/rpc_server.dart +++ b/example/rpc/rpc_server.dart @@ -1,5 +1,5 @@ -import "dart:io"; import "package:dart_amqp/dart_amqp.dart"; +import "package:universal_io/io.dart"; // Slow implementation of fib int fib(int n) { diff --git a/example/topics/emit_log_topic.dart b/example/topics/emit_log_topic.dart index be9684a..22458c0 100644 --- a/example/topics/emit_log_topic.dart +++ b/example/topics/emit_log_topic.dart @@ -1,5 +1,5 @@ -import "dart:io"; import "package:dart_amqp/dart_amqp.dart"; +import "package:universal_io/io.dart"; void main(List args) async { if (args.length < 2) { diff --git a/example/topics/receive_logs_topic.dart b/example/topics/receive_logs_topic.dart index 2f1e67f..ec3019c 100644 --- a/example/topics/receive_logs_topic.dart +++ b/example/topics/receive_logs_topic.dart @@ -1,5 +1,5 @@ -import "dart:io"; import "package:dart_amqp/dart_amqp.dart"; +import "package:universal_io/io.dart"; void main(List args) async { if (args.isEmpty) { diff --git a/example/workers/worker.dart b/example/workers/worker.dart index 73ce81c..71ae7cf 100644 --- a/example/workers/worker.dart +++ b/example/workers/worker.dart @@ -1,5 +1,5 @@ -import "dart:io"; import "package:dart_amqp/dart_amqp.dart"; +import "package:universal_io/io.dart"; void main() async { Client client = Client(); diff --git a/lib/src/client.dart b/lib/src/client.dart index e188f39..e34e4c1 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -1,20 +1,21 @@ library dart_amqp.client; import "dart:async"; -import "dart:io"; +import "package:dart_amqp/dart_amqp.dart"; +import "package:universal_io/io.dart"; import "dart:typed_data"; import "dart:collection"; import 'package:async/async.dart'; +import "package:web_socket_channel/web_socket_channel.dart"; // Internal lib dependencies import "logging.dart"; -import "exceptions.dart"; -import "enums.dart"; import "protocol.dart"; -import "authentication.dart"; -part "client/connection_settings.dart"; +part "client/base_connection_settings.dart"; +part "client/impl/connection_settings.dart"; +part "client/impl/websocket_connection_settings.dart"; // client interfaces part "client/client.dart"; diff --git a/lib/src/client/base_connection_settings.dart b/lib/src/client/base_connection_settings.dart new file mode 100644 index 0000000..b055e38 --- /dev/null +++ b/lib/src/client/base_connection_settings.dart @@ -0,0 +1,43 @@ +part of "../client.dart"; + +abstract class BaseConnectionSettings { + // The host to connect to + String? host; + + // The port to connect to + int? port; + + // The uri to connect to websocket. Use either host and url + Uri? uri; + + // The connection vhost that will be sent to the server + abstract String virtualHost; + + // The max number of reconnection attempts before declaring a connection as unusable + abstract int maxConnectionAttempts; + + // The time to wait before trying to reconnect + abstract Duration reconnectWaitTime; + + // Authentication provider + abstract Authenticator authProvider; + + // Protocol version + int amqpProtocolVersion = 0; + int amqpMajorVersion = 0; + int amqpMinorVersion = 9; + int amqpRevision = 1; + + // Tuning settings + abstract TuningSettings tuningSettings; + + // TLS settings (if TLS connection is required) + SecurityContext? tlsContext; + bool Function(X509Certificate)? onBadCertificate; + + // Connection identifier + String? connectionName; + + // The time to wait for socket connection to be established + Duration? connectTimeout; +} diff --git a/lib/src/client/client.dart b/lib/src/client/client.dart index d54be24..2e72471 100644 --- a/lib/src/client/client.dart +++ b/lib/src/client/client.dart @@ -1,11 +1,11 @@ part of "../client.dart"; abstract class Client { - factory Client({ConnectionSettings? settings}) => + factory Client({BaseConnectionSettings? settings}) => _ClientImpl(settings: settings); // Configuration options - ConnectionSettings get settings; + BaseConnectionSettings get settings; TuningSettings get tuningSettings; /// Check if a connection is currently in handshake state diff --git a/lib/src/client/impl/channel_impl.dart b/lib/src/client/impl/channel_impl.dart index bdfe944..ff09fe2 100644 --- a/lib/src/client/impl/channel_impl.dart +++ b/lib/src/client/impl/channel_impl.dart @@ -50,25 +50,24 @@ class _ChannelImpl implements Channel { _pendingOperationPayloads.add(this); // Transmit handshake - _frameWriter - ..writeProtocolHeader( - _client.settings.amqpProtocolVersion, - _client.settings.amqpMajorVersion, - _client.settings.amqpMinorVersion, - _client.settings.amqpRevision) - ..pipe(_client._socket!); + _frameWriter.writeProtocolHeader( + _client.settings.amqpProtocolVersion, + _client.settings.amqpMajorVersion, + _client.settings.amqpMinorVersion, + _client.settings.amqpRevision); + _setPipe(); } void writeHeartbeat() { - if (_channelClosed != null || _client._socket == null) { + if (_channelClosed != null || + (_client._socket == null && _client._webSocketChannel == null)) { return; // no-op } // Transmit heartbeat try { - _frameWriter - ..writeHeartbeat() - ..pipe(_client._socket!); + _frameWriter.writeHeartbeat(); + _setPipe(); } catch (_) { // An exception will be raised if we attempt to send a hearbeat // immediately after the connection to the server is lost. We can safely @@ -104,10 +103,10 @@ class _ChannelImpl implements Channel { _nextPublishSeqNo++; } - _frameWriter - ..writeMessage(channelId, message, - properties: properties, payloadContent: payloadContent) - ..pipe(_client._socket!); + _frameWriter.writeMessage(channelId, message, + properties: properties, payloadContent: payloadContent); + + _setPipe(); // If the noWait flag was specified, complete the future now. The broken // will raise any errors asynchronously via the channel or connection. @@ -116,6 +115,16 @@ class _ChannelImpl implements Channel { } } + void _setPipe() { + if (_client._webSocketChannel != null) { + _frameWriter.pipe(_client._webSocketChannel!.sink); + } else if (_client._socket != null) { + _frameWriter.pipe(_client._socket!); + } else { + throw FatalException("Couldn't set pipe"); + } + } + /// Implement the handshake flow specified by the AMQP spec by /// examining [serverFrame] and generating the appropriate response void _processHandshake(DecodedMessage serverMessage) { diff --git a/lib/src/client/impl/client_impl.dart b/lib/src/client/impl/client_impl.dart index 82fad59..7b29519 100644 --- a/lib/src/client/impl/client_impl.dart +++ b/lib/src/client/impl/client_impl.dart @@ -3,7 +3,7 @@ part of "../../client.dart"; class _ClientImpl implements Client { // Configuration options @override - late ConnectionSettings settings; + late BaseConnectionSettings settings; // Tuning settings @override @@ -12,6 +12,7 @@ class _ClientImpl implements Client { // The connection to the server int _connectionAttempt = 0; Socket? _socket; + WebSocketChannel? _webSocketChannel; // The list of open channels. Channel 0 is always reserved for signaling final Map _channels = {}; @@ -32,7 +33,7 @@ class _ClientImpl implements Client { // consecutive number of maxMissedHeartbeats (see tuningSettings). RestartableTimer? _heartbeatRecvTimer; - _ClientImpl({ConnectionSettings? settings}) { + _ClientImpl({BaseConnectionSettings? settings}) { // Use defaults if no settings specified this.settings = settings ?? ConnectionSettings(); } @@ -44,67 +45,115 @@ class _ClientImpl implements Client { Future _reconnect() { _connected ??= Completer(); - Future fs; - if (settings.tlsContext != null) { + Future? fs; + if (settings.uri != null) { + try { + connectionLogger.info( + "Trying to connect to ${settings.uri} [attempt ${_connectionAttempt + 1}/${settings.maxConnectionAttempts}]"); + WebSocketChannel webSocketChannel = + WebSocketChannel.connect(settings.uri!); + + webSocketChannel.ready.then( + (value) { + _webSocketChannel = webSocketChannel; + + // Bind processors and initiate handshake + _bindProcessorsAndInitHandshake(_webSocketChannel!.stream); + + // Allocate channel 0 for handshaking and transmit the AMQP header to bootstrap the handshake + _allocateChannelZero(); + }, + ).catchError((err, trace) { + _reconnectionOnError(); + }); + } catch (_) { + print("Can't connect to ${settings.uri}"); + } + } else if (settings.tlsContext != null && _isHostAndPortSet) { connectionLogger.info( "Trying to connect to ${settings.host}:${settings.port} using TLS [attempt ${_connectionAttempt + 1}/${settings.maxConnectionAttempts}]"); fs = SecureSocket.connect( settings.host, - settings.port, + settings.port!, timeout: settings.connectTimeout, context: settings.tlsContext, onBadCertificate: settings.onBadCertificate, ); - } else { + } else if (_isHostAndPortSet) { connectionLogger.info( "Trying to connect to ${settings.host}:${settings.port} [attempt ${_connectionAttempt + 1}/${settings.maxConnectionAttempts}]"); - fs = Socket.connect(settings.host, settings.port, + fs = Socket.connect(settings.host, settings.port!, timeout: settings.connectTimeout); + } else { + throw throw ArgumentError( + "Specify websocketUri to connect through websocket or set host and port to use default connection"); } - fs.then((Socket s) { + fs?.then((Socket s) { _socket = s; // Bind processors and initiate handshake - RawFrameParser(tuningSettings) - .transformer - .bind(_socket!) - .transform(AmqpMessageDecoder().transformer) - .listen(_handleMessage, - onError: _handleException, - onDone: () => - _handleException(const SocketException("Socket closed"))); + _bindProcessorsAndInitHandshake(_socket!); // Allocate channel 0 for handshaking and transmit the AMQP header to bootstrap the handshake - _channels.clear(); - _channels.putIfAbsent(0, () => _ChannelImpl(0, this)); + _allocateChannelZero(); }).catchError((err, trace) { - // Connection attempt completed with an error (probably protocol mismatch) - if (_connected!.isCompleted) { - return; - } + _reconnectionOnError(); + }); - if (++_connectionAttempt >= settings.maxConnectionAttempts) { - String errorMessage = - "Could not connect to ${settings.host}:${settings.port} after ${settings.maxConnectionAttempts} attempts. Giving up"; - connectionLogger.severe(errorMessage); - _connected!.completeError(ConnectionFailedException(errorMessage)); + return _connected!.future; + } + + void _bindProcessorsAndInitHandshake(Stream stream) { + RawFrameParser(tuningSettings) + .transformer + .bind(stream) + .transform(AmqpMessageDecoder().transformer) + .listen(_handleMessage, + onError: _handleException, + onDone: () => + _handleException(const SocketException("Socket closed"))); + } - // Clear _connected future so the client can invoke open() in the future - _connected = null; + void _allocateChannelZero() { + _channels.clear(); + _channels.putIfAbsent(0, () => _ChannelImpl(0, this)); + } + + bool get _isHostAndPortSet => settings.host != null && settings.port != null; + + Future _reconnectionOnError() async { + // Connection attempt completed with an error (probably protocol mismatch) + if (_connected!.isCompleted) { + return; + } + + if (++_connectionAttempt >= settings.maxConnectionAttempts) { + String errorMessage; + if (settings.uri != null) { + errorMessage = + "Could not connect to ${settings.uri} after ${settings.maxConnectionAttempts} attempts. Giving up"; } else { - // Retry after reconnectWaitTime ms - Timer(settings.reconnectWaitTime, _reconnect); + errorMessage = + "Could not connect to ${settings.host}:${settings.port} after ${settings.maxConnectionAttempts} attempts. Giving up"; } - }); + connectionLogger.severe(errorMessage); + _connected!.completeError(ConnectionFailedException(errorMessage)); - return _connected!.future; + // Clear _connected future so the client can invoke open() in the future + _connected = null; + } else { + // Retry after reconnectWaitTime ms + Timer(settings.reconnectWaitTime, _reconnect); + } } /// Check if a connection is currently in handshake state @override bool get handshaking => - _socket != null && _connected != null && !_connected!.isCompleted; + (_socket != null || _webSocketChannel != null) && + _connected != null && + !_connected!.isCompleted; void _handleMessage(DecodedMessage serverMessage) { try { @@ -283,7 +332,7 @@ class _ClientImpl implements Client { _heartbeatRecvTimer?.cancel(); _heartbeatRecvTimer = null; - if (_socket == null) { + if (_socket == null && _webSocketChannel == null) { return Future.value(); } @@ -298,11 +347,15 @@ class _ClientImpl implements Client { .toList() .reversed .map((_ChannelImpl channel) => channel.close())) - .then((_) => _socket!.flush()) - .then((_) => _socket!.close(), onError: (e) { + .then((_) => _socket?.flush()) + .then((_) { + _webSocketChannel?.sink.close(); + _socket?.close(); + }, onError: (e) { // Mute exception as the socket may be already closed }).whenComplete(() { - _socket!.destroy(); + _webSocketChannel = null; + _socket?.destroy(); _socket = null; _connected = null; if (closeErrorStream) { diff --git a/lib/src/client/connection_settings.dart b/lib/src/client/impl/connection_settings.dart similarity index 77% rename from lib/src/client/connection_settings.dart rename to lib/src/client/impl/connection_settings.dart index ae30ca7..302d34d 100644 --- a/lib/src/client/connection_settings.dart +++ b/lib/src/client/impl/connection_settings.dart @@ -1,41 +1,60 @@ -part of "../client.dart"; +part of "../../client.dart"; -class ConnectionSettings { +class ConnectionSettings implements BaseConnectionSettings{ // The host to connect to - String host; + @override + String? host; // The port to connect to - int port; + @override + int? port; + + // The uri to connect to websocket. Use either host and url + @override + Uri? uri; // The connection vhost that will be sent to the server + @override String virtualHost; // The max number of reconnection attempts before declaring a connection as unusable + @override int maxConnectionAttempts; // The time to wait before trying to reconnect + @override Duration reconnectWaitTime; // Authentication provider + @override Authenticator authProvider; // Protocol version + @override int amqpProtocolVersion = 0; + @override int amqpMajorVersion = 0; + @override int amqpMinorVersion = 9; + @override int amqpRevision = 1; // Tuning settings + @override TuningSettings tuningSettings; // TLS settings (if TLS connection is required) + @override SecurityContext? tlsContext; + @override bool Function(X509Certificate)? onBadCertificate; // Connection identifier + @override String? connectionName; // The time to wait for socket connection to be established + @override Duration? connectTimeout; ConnectionSettings({ diff --git a/lib/src/client/impl/websocket_connection_settings.dart b/lib/src/client/impl/websocket_connection_settings.dart new file mode 100644 index 0000000..6651d3a --- /dev/null +++ b/lib/src/client/impl/websocket_connection_settings.dart @@ -0,0 +1,72 @@ +part of "../../client.dart"; + +class WebsocketConnectionSettings implements BaseConnectionSettings{ + // The host to connect to + @override + String? host; + + // The port to connect to + @override + int? port; + + // The uri to connect to websocket. Use either host and url + @override + Uri? uri; + + // The connection vhost that will be sent to the server + @override + String virtualHost; + + // The max number of reconnection attempts before declaring a connection as unusable + @override + int maxConnectionAttempts; + + // The time to wait before trying to reconnect + @override + Duration reconnectWaitTime; + + // Authentication provider + @override + Authenticator authProvider; + + // Protocol version + @override + int amqpProtocolVersion = 0; + @override + int amqpMajorVersion = 0; + @override + int amqpMinorVersion = 9; + @override + int amqpRevision = 1; + + // Tuning settings + @override + TuningSettings tuningSettings; + + // TLS settings (if TLS connection is required) + @override + SecurityContext? tlsContext; + @override + bool Function(X509Certificate)? onBadCertificate; + + // Connection identifier + @override + String? connectionName; + + // The time to wait for socket connection to be established + @override + Duration? connectTimeout; + + WebsocketConnectionSettings({ + this.uri, + this.virtualHost = "/", + this.authProvider = const PlainAuthenticator("guest", "guest"), + this.maxConnectionAttempts = 1, + this.reconnectWaitTime = const Duration(milliseconds: 1500), + TuningSettings? tuningSettings, + this.tlsContext, + this.onBadCertificate, + this.connectionName, + this.connectTimeout, + }) : tuningSettings = tuningSettings ?? TuningSettings(); +} diff --git a/lib/src/protocol.dart b/lib/src/protocol.dart index 2ba2442..afd5713 100644 --- a/lib/src/protocol.dart +++ b/lib/src/protocol.dart @@ -13,6 +13,8 @@ import "dart:collection"; import "dart:math" as math; // Internal lib dependencies +import "package:dart_amqp/src/uint64_converter.dart"; + import "enums.dart"; import "exceptions.dart"; diff --git a/lib/src/protocol/io/raw_frame_parser.dart b/lib/src/protocol/io/raw_frame_parser.dart index a7aa9d7..c2f068a 100644 --- a/lib/src/protocol/io/raw_frame_parser.dart +++ b/lib/src/protocol/io/raw_frame_parser.dart @@ -11,7 +11,7 @@ class RawFrameParser { RawFrameParser(this.tuningSettings); - void handleData(List? chunk, EventSink sink) { + void handleData(dynamic chunk, EventSink sink) { try { // Append incoming chunk to input buffer if (chunk != null) { @@ -102,8 +102,8 @@ class RawFrameParser { sink.addError(error, stackTrace); } - StreamTransformer, RawFrame> get transformer => - StreamTransformer, RawFrame>.fromHandlers( + StreamTransformer get transformer => + StreamTransformer.fromHandlers( handleData: handleData, handleDone: handleDone, handleError: handleError); diff --git a/lib/src/protocol/stream/type_decoder.dart b/lib/src/protocol/stream/type_decoder.dart index 83e1898..65f31c4 100644 --- a/lib/src/protocol/stream/type_decoder.dart +++ b/lib/src/protocol/stream/type_decoder.dart @@ -44,7 +44,14 @@ class TypeDecoder { } int readUInt64() { - int val = _buffer.getUint64(_offset, endianess); + final int val; + const kIsWeb = bool.fromEnvironment('dart.library.js_util'); + if (kIsWeb) { + val = Uint64Converter.getUint64(_buffer, _offset); + } else { + val = _buffer.getUint64(_offset, endianess); + } + _offset += 8; return val; diff --git a/lib/src/protocol/stream/type_encoder.dart b/lib/src/protocol/stream/type_encoder.dart index 8126f77..c9fb1b7 100644 --- a/lib/src/protocol/stream/type_encoder.dart +++ b/lib/src/protocol/stream/type_encoder.dart @@ -52,9 +52,14 @@ class TypeEncoder { } void writeUInt64(int value) { - Uint8List buf = Uint8List(8); - ByteData.view(buf.buffer).setUint64(0, value, endianess); - _writer.addLast(buf); + const kIsWeb = bool.fromEnvironment('dart.library.js_util'); + if (kIsWeb) { + _writer.addLast(Uint64Converter.uint64ToUint8List(value)); + } else { + Uint8List buf = Uint8List(8); + ByteData.view(buf.buffer).setUint64(0, value, endianess); + _writer.addLast(buf); + } } writeFloat(double value) { diff --git a/lib/src/uint64_converter.dart b/lib/src/uint64_converter.dart new file mode 100644 index 0000000..4821c46 --- /dev/null +++ b/lib/src/uint64_converter.dart @@ -0,0 +1,24 @@ +import 'dart:typed_data'; + +class Uint64Converter { + const Uint64Converter._(); + + static Uint8List uint64ToUint8List(int value, [Endian endian = Endian.big]) { + ByteData byteData = ByteData(8); + setUint64(byteData, 0, value, endian); + return byteData.buffer.asUint8List(); + } + + static void setUint64(ByteData byteData, int byteOffset, int value, + [Endian endian = Endian.big]) { + byteData.setUint32(byteOffset, value >> 32, endian); + byteData.setUint32(byteOffset + 4, value & 0xFFFFFFFF, endian); + } + + static int getUint64(ByteData byteData, int byteOffset, + [Endian endian = Endian.big]) { + int high = byteData.getUint32(byteOffset, endian); + int low = byteData.getUint32(byteOffset + 4, endian); + return (high << 32) | low; + } +} diff --git a/pubspec.yaml b/pubspec.yaml index f80af32..abfc1d4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,6 +8,8 @@ environment: dependencies: async: ^2.10.0 logging: ^1.0.1 + universal_io: ^2.2.2 + web_socket_channel: ^3.0.0 dev_dependencies: test: ^1.17.5 mockito: ^5.0.9 diff --git a/test/lib/client_test.dart b/test/lib/client_test.dart index a412565..d5f00f3 100644 --- a/test/lib/client_test.dart +++ b/test/lib/client_test.dart @@ -1,7 +1,7 @@ library dart_amqp.test.client; import 'dart:async'; -import 'dart:io'; +import "package:universal_io/io.dart"; import "package:test/test.dart"; diff --git a/test/lib/exception_handling_test.dart b/test/lib/exception_handling_test.dart index 9e13e23..d834060 100644 --- a/test/lib/exception_handling_test.dart +++ b/test/lib/exception_handling_test.dart @@ -420,7 +420,7 @@ main({bool enableLogger = true}) { // ignore: unawaited_futures server.shutdown().then((_) async { - await server.listen(client.settings.host, client.settings.port); + await server.listen(client.settings.host!, client.settings.port!); generateHandshakeMessages(frameWriter, server); await client.connect(); client.errorListener((ex) => handleError(ex)); diff --git a/test/lib/mocks/mocks.dart b/test/lib/mocks/mocks.dart index 54653bf..c438676 100644 --- a/test/lib/mocks/mocks.dart +++ b/test/lib/mocks/mocks.dart @@ -1,7 +1,7 @@ library dart_amqp.tests.mocks; import "dart:typed_data"; -import "dart:io"; +import "package:universal_io/io.dart"; import "dart:async"; import "dart:convert"; diff --git a/tool/generate_bindings.dart b/tool/generate_bindings.dart index ccaad83..b858755 100644 --- a/tool/generate_bindings.dart +++ b/tool/generate_bindings.dart @@ -1,8 +1,8 @@ -import "dart:io"; import "dart:async"; import "package:xml/xml.dart" as xml; import "package:http/http.dart" as http; import "package:logging/logging.dart"; +import "package:universal_io/io.dart"; final Logger logger = Logger("tools");