Merge pull request 'Merge connect and connectAwaitable' (#32) from refactor/connect into master

Reviewed-on: https://codeberg.org/moxxy/moxxmpp/pulls/32
This commit is contained in:
PapaTutuWawa 2023-03-12 16:31:21 +00:00
commit 324ef9ca29
7 changed files with 144 additions and 63 deletions

View File

@ -117,19 +117,19 @@ class _MyHomePageState extends State<MyHomePage> {
allowPlainAuth: true, allowPlainAuth: true,
), ),
); );
final result = await connection.connectAwaitable(); final result = await connection.connect(waitUntilLogin: true);
setState(() { setState(() {
connected = result.success; connected = result.isType<bool>() && result.get<bool>();
loading = false; loading = false;
}); });
if (result.error != null) { if (result.isType<XmppConnectionError>()) {
logger.severe(result.error); logger.severe(result.get<XmppConnectionError>());
if (context.mounted) { if (context.mounted) {
showDialog( showDialog(
context: context, context: context,
builder: (_) => AlertDialog( builder: (_) => AlertDialog(
title: const Text('Error'), title: const Text('Error'),
content: Text(result.error.toString()), content: Text(result.get<XmppConnectionError>().toString()),
), ),
); );
} }

View File

@ -29,7 +29,7 @@
useGoogleAPIs = false; useGoogleAPIs = false;
useGoogleTVAddOns = false; useGoogleTVAddOns = false;
}; };
pinnedJDK = pkgs.jdk; pinnedJDK = pkgs.jdk17;
pythonEnv = pkgs.python3.withPackages (ps: with ps; [ pythonEnv = pkgs.python3.withPackages (ps: with ps; [
pyyaml pyyaml

View File

@ -1,3 +1,7 @@
## 0.3.0
- **BREAKING**: Removed `connectAwaitable` and merged it with `connect`.
## 0.1.6+1 ## 0.1.6+1
- **FIX**: Fix LMC not working. - **FIX**: Fix LMC not working.

View File

@ -4,6 +4,7 @@ import 'package:meta/meta.dart';
import 'package:moxlib/moxlib.dart'; import 'package:moxlib/moxlib.dart';
import 'package:moxxmpp/src/awaiter.dart'; import 'package:moxxmpp/src/awaiter.dart';
import 'package:moxxmpp/src/buffer.dart'; import 'package:moxxmpp/src/buffer.dart';
import 'package:moxxmpp/src/connection_errors.dart';
import 'package:moxxmpp/src/connectivity.dart'; import 'package:moxxmpp/src/connectivity.dart';
import 'package:moxxmpp/src/errors.dart'; import 'package:moxxmpp/src/errors.dart';
import 'package:moxxmpp/src/events.dart'; import 'package:moxxmpp/src/events.dart';
@ -24,6 +25,7 @@ import 'package:moxxmpp/src/settings.dart';
import 'package:moxxmpp/src/socket.dart'; import 'package:moxxmpp/src/socket.dart';
import 'package:moxxmpp/src/stanza.dart'; import 'package:moxxmpp/src/stanza.dart';
import 'package:moxxmpp/src/stringxml.dart'; import 'package:moxxmpp/src/stringxml.dart';
import 'package:moxxmpp/src/types/result.dart';
import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart'; import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart';
import 'package:moxxmpp/src/xeps/xep_0198/negotiator.dart'; import 'package:moxxmpp/src/xeps/xep_0198/negotiator.dart';
import 'package:moxxmpp/src/xeps/xep_0198/xep_0198.dart'; import 'package:moxxmpp/src/xeps/xep_0198/xep_0198.dart';
@ -75,21 +77,6 @@ class StreamHeaderNonza extends XMLNode {
); );
} }
/// The result of an awaited connection.
class XmppConnectionResult {
const XmppConnectionResult(
this.success, {
this.error,
});
/// True if the connection was successful. False if it failed for any reason.
final bool success;
// If a connection attempt fails, i.e. success is false, then this indicates the
// reason the connection failed.
final XmppError? error;
}
/// This class is a connection to the server. /// This class is a connection to the server.
class XmppConnection { class XmppConnection {
XmppConnection( XmppConnection(
@ -180,7 +167,7 @@ class XmppConnection {
/// Completers for certain actions /// Completers for certain actions
// ignore: use_late_for_private_fields_and_variables // ignore: use_late_for_private_fields_and_variables
Completer<XmppConnectionResult>? _connectionCompleter; Completer<Result<bool, XmppConnectionError>>? _connectionCompleter;
/// Negotiators /// Negotiators
final Map<String, XmppFeatureNegotiatorBase> _featureNegotiators = {}; final Map<String, XmppFeatureNegotiatorBase> _featureNegotiators = {};
@ -198,6 +185,9 @@ class XmppConnection {
bool _isConnectionRunning = false; bool _isConnectionRunning = false;
final Lock _connectionRunningLock = Lock(); final Lock _connectionRunningLock = Lock();
/// Flag indicating whether reconnection should be enabled after a successful connection.
bool _enableReconnectOnSuccess = false;
/// Enters the critical section for accessing [XmppConnection._isConnectionRunning] /// Enters the critical section for accessing [XmppConnection._isConnectionRunning]
/// and does the following: /// and does the following:
/// - if _isConnectionRunning is false, set it to true and return false. /// - if _isConnectionRunning is false, set it to true and return false.
@ -404,9 +394,10 @@ class XmppConnection {
state: XmppConnectionState.error, state: XmppConnectionState.error,
); );
_connectionCompleter?.complete( _connectionCompleter?.complete(
XmppConnectionResult( Result(
false, StreamFailureError(
error: error, error,
),
), ),
); );
_connectionCompleter = null; _connectionCompleter = null;
@ -427,7 +418,14 @@ class XmppConnection {
// The error is recoverable // The error is recoverable
await _setConnectionState(XmppConnectionState.notConnected); await _setConnectionState(XmppConnectionState.notConnected);
await _reconnectionPolicy.onFailure();
if (await _reconnectionPolicy.getShouldReconnect()) {
await _reconnectionPolicy.onFailure();
} else {
_log.info(
'Not passing connection failure to reconnection policy as it indicates that we should not reconnect',
);
}
} }
/// Called whenever the socket creates an event /// Called whenever the socket creates an event
@ -859,8 +857,13 @@ class XmppConnection {
await _resetIsConnectionRunning(); await _resetIsConnectionRunning();
await _setConnectionState(XmppConnectionState.connected); await _setConnectionState(XmppConnectionState.connected);
// Enable reconnections
if (_enableReconnectOnSuccess) {
await _reconnectionPolicy.setShouldReconnect(true);
}
// Resolve the connection completion future // Resolve the connection completion future
_connectionCompleter?.complete(const XmppConnectionResult(true)); _connectionCompleter?.complete(const Result(true));
_connectionCompleter = null; _connectionCompleter = null;
// Tell consumers of the event stream that we're done with stream feature // Tell consumers of the event stream that we're done with stream feature
@ -1112,47 +1115,30 @@ class XmppConnection {
); );
} }
/// Like [connect] but the Future resolves when the resource binding is either done or Future<Result<bool, XmppConnectionError>> _connectImpl({
/// SASL has failed.
Future<XmppConnectionResult> connectAwaitable({
String? lastResource,
bool waitForConnection = false,
}) async {
_runPreConnectionAssertions();
await _resetIsConnectionRunning();
_connectionCompleter = Completer();
_log.finest('Calling connect() from connectAwaitable');
await connect(
lastResource: lastResource,
waitForConnection: waitForConnection,
shouldReconnect: false,
);
return _connectionCompleter!.future;
}
/// Start the connection process using the provided connection settings.
Future<void> connect({
String? lastResource, String? lastResource,
bool waitForConnection = false, bool waitForConnection = false,
bool shouldReconnect = true, bool shouldReconnect = true,
bool waitUntilLogin = false,
bool enableReconnectOnSuccess = true,
}) async { }) async {
if (_connectionState != XmppConnectionState.notConnected &&
_connectionState != XmppConnectionState.error) {
_log.fine(
'Cancelling this connection attempt as one appears to be already running.',
);
return;
}
_runPreConnectionAssertions(); _runPreConnectionAssertions();
await _resetIsConnectionRunning(); await _resetIsConnectionRunning();
if (waitUntilLogin) {
_log.finest('Setting up completer for awaiting completed login');
_connectionCompleter = Completer();
}
if (lastResource != null) { if (lastResource != null) {
setResource(lastResource); setResource(lastResource);
} }
_enableReconnectOnSuccess = enableReconnectOnSuccess;
if (shouldReconnect) { if (shouldReconnect) {
await _reconnectionPolicy.setShouldReconnect(true); await _reconnectionPolicy.setShouldReconnect(true);
} else {
await _reconnectionPolicy.setShouldReconnect(false);
} }
await _reconnectionPolicy.reset(); await _reconnectionPolicy.reset();
@ -1182,6 +1168,8 @@ class XmppConnection {
); );
if (!result) { if (!result) {
await handleError(NoConnectionError()); await handleError(NoConnectionError());
return Result(NoConnectionPossibleError());
} else { } else {
await _reconnectionPolicy.onSuccess(); await _reconnectionPolicy.onSuccess();
_log.fine('Preparing the internal state for a connection attempt'); _log.fine('Preparing the internal state for a connection attempt');
@ -1190,6 +1178,67 @@ class XmppConnection {
_updateRoutingState(RoutingState.negotiating); _updateRoutingState(RoutingState.negotiating);
_isAuthenticated = false; _isAuthenticated = false;
_sendStreamHeader(); _sendStreamHeader();
if (waitUntilLogin) {
return _connectionCompleter!.future;
} else {
return const Result(true);
}
}
}
/// Start the connection process using the provided connection settings.
///
/// If [lastResource] is set, then its value is used as the connection's resource.
/// Useful for stream resumption.
///
/// [shouldReconnect] indicates whether the reconnection attempts should be
/// automatically performed after a fatal failure of any kind occurs.
///
/// [waitForConnection] indicates whether the connection should wait for the "go"
/// signal from a registered connectivity manager.
///
/// If [waitUntilLogin] is set to true, the future will resolve when either
/// the connection has been successfully established (authentication included) or
/// a failure occured. If set to false, then the future will immediately resolve
/// to true.
///
/// [enableReconnectOnSuccess] indicates that automatic reconnection is to be
/// enabled once the connection has been successfully established.
Future<Result<bool, XmppConnectionError>> connect({
String? lastResource,
bool? shouldReconnect,
bool waitForConnection = false,
bool waitUntilLogin = false,
bool enableReconnectOnSuccess = true,
}) async {
if (_connectionState != XmppConnectionState.notConnected &&
_connectionState != XmppConnectionState.error) {
_log.fine(
'Cancelling this connection attempt as one appears to be already running.',
);
return Future.value(
Result(
ConnectionAlreadyRunningError(),
),
);
}
final result = _connectImpl(
lastResource: lastResource,
shouldReconnect: shouldReconnect ?? !waitUntilLogin,
waitForConnection: waitForConnection,
waitUntilLogin: waitUntilLogin,
enableReconnectOnSuccess: enableReconnectOnSuccess,
);
if (waitUntilLogin) {
return result;
} else {
return Future.value(
const Result(
true,
),
);
} }
} }
} }

View File

@ -0,0 +1,28 @@
import 'package:moxxmpp/src/errors.dart';
import 'package:moxxmpp/src/negotiators/negotiator.dart';
/// The reason a call to `XmppConnection.connect` failed.
abstract class XmppConnectionError {}
/// Returned by `XmppConnection.connect` when a connection is already active.
class ConnectionAlreadyRunningError extends XmppConnectionError {}
/// Returned by `XmppConnection.connect` when a negotiator returned an unrecoverable
/// error. Only returned when waitUntilLogin is true.
class NegotiatorReturnedError extends XmppConnectionError {
NegotiatorReturnedError(this.error);
/// The error returned by the negotiator.
final NegotiatorError error;
}
class StreamFailureError extends XmppConnectionError {
StreamFailureError(this.error);
/// The error that causes a connection failure.
final XmppError error;
}
/// Returned by `XmppConnection.connect` when no connection could
/// be established.
class NoConnectionPossibleError extends XmppConnectionError {}

View File

@ -1,6 +1,6 @@
name: moxxmpp name: moxxmpp
description: A pure-Dart XMPP library description: A pure-Dart XMPP library
version: 0.2.0 version: 0.3.0
homepage: https://codeberg.org/moxxy/moxxmpp homepage: https://codeberg.org/moxxy/moxxmpp
publish_to: https://git.polynom.me/api/packages/Moxxy/pub publish_to: https://git.polynom.me/api/packages/Moxxy/pub

View File

@ -160,13 +160,13 @@ void main() {
), ),
); );
final result = await manager.preprocessPublishOptions( // final result = await manager.preprocessPublishOptions(
'pubsub.server.example.org', // 'pubsub.server.example.org',
'example:node', // 'example:node',
PubSubPublishOptions( // PubSubPublishOptions(
maxItems: 'max', // maxItems: 'max',
), // ),
); // );
}); });
} }