start static method
Future<Session>
start(
- String? realm,
- AbstractTransport transport, {
- String? authId,
- String? authRole,
- Map<
String, dynamic> ? authExtra, - List<
AbstractAuthentication> ? authMethods, - Duration? reconnect,
- WampE2eeProvider? e2eeProvider,
- SessionE2eeProviderResolver? e2eeProviderResolver,
Starting the session will also start the authentication process.
Implementation
static Future<Session> start(
String? realm,
AbstractTransport transport, {
String? authId,
String? authRole,
Map<String, dynamic>? authExtra,
List<AbstractAuthentication>? authMethods,
Duration? reconnect,
WampE2eeProvider? e2eeProvider,
SessionE2eeProviderResolver? e2eeProviderResolver,
}) async {
final session = Session(
realm,
transport,
e2eeProvider: e2eeProvider,
e2eeProviderResolver: e2eeProviderResolver,
);
final hello = Hello(realm, Details.forHello());
if (authId != null) {
hello.details.authid = authId;
}
if (authRole != null) {
hello.details.authrole = authRole;
}
if (authExtra != null) {
hello.details.authextra = authExtra;
}
if (authMethods != null && authMethods.isNotEmpty) {
await authMethods[0].hello(realm, hello.details);
hello.details.authmethods = authMethods
.map<String>((authMethod) => authMethod.getName())
.toList();
}
final welcomeCompleter = Completer<Session>();
AbstractAuthentication? challengedAuthMethod;
Future<void> cancelAuthentication() async {
if (authMethods == null) return;
await Future.wait(
authMethods.map((method) async {
try {
await method.cancelPendingChallenge();
} catch (_) {
// Authentication teardown is best-effort and must reach every method.
}
}),
);
}
session
._transportStreamSubscription = session._receiveSessionMessages().listen(
(message) {
final materialized = session._materializeTransportMessage(message);
if (materialized is Challenge) {
AbstractAuthentication? foundAuthMethod;
for (final authenticationMethod
in authMethods ?? const <AbstractAuthentication>[]) {
if (authenticationMethod.getName() == materialized.authMethod) {
foundAuthMethod = authenticationMethod;
break;
}
}
if (foundAuthMethod != null) {
challengedAuthMethod = foundAuthMethod;
try {
foundAuthMethod
.challenge(materialized.extra)
.then(
(authenticate) => session.authenticate(authenticate),
onError: (error) {
unawaited(cancelAuthentication());
if (!welcomeCompleter.isCompleted) {
welcomeCompleter.completeError(
Abort(
Error.authorizationFailed,
message: error.toString(),
),
);
}
session._transport.send(
Abort(
Error.authorizationFailed,
message: error.toString(),
),
);
session._transport.close();
},
);
} catch (exception) {
unawaited(cancelAuthentication());
try {
transport.close();
} catch (_) {
/* transport may already be closed */
}
welcomeCompleter.completeError(
Abort(Error.authorizationFailed, message: exception.toString()),
);
}
return;
}
final goodbye = Goodbye(
GoodbyeMessage('Authmethod $foundAuthMethod not supported'),
Goodbye.reasonGoodbyeAndOut,
);
session._transport.send(goodbye);
welcomeCompleter.completeError(goodbye);
return;
}
if (materialized is Welcome) {
if ((session.realm ?? materialized.details.realm) == null) {
welcomeCompleter.completeError(
Abort(
Error.authorizationFailed,
message:
'No realm specified! Neither by the client nor by the router',
),
);
return;
}
session._transportStreamSubscription.pause();
unawaited(
Future<void>(() async {
await challengedAuthMethod?.verifyFinal(
authId: materialized.details.authid,
authMethod: materialized.details.authmethod,
authExtra: materialized.details.authextra,
);
session.id = materialized.sessionId;
if (materialized.details.realm == null) {
if (_logger.level <= Level.INFO) {
_logger.info('Warning! No realm returned by the router');
}
} else {
session.realm = materialized.details.realm;
}
session.authId = materialized.details.authid;
session.authRole = materialized.details.authrole;
session.authMethod = materialized.details.authmethod;
session.authProvider = materialized.details.authprovider;
session.authExtra = materialized.details.authextra;
await session._initializeSessionE2eeProvider();
}).then(
(_) {
if (welcomeCompleter.isCompleted) {
unawaited(session._transportStreamSubscription.cancel());
return;
}
session._transportStreamSubscription.onData(
session._handleTransportMessage,
);
session._transportStreamSubscription.onDone(() {
unawaited(session._handleTransportClosed());
});
welcomeCompleter.complete(session);
session._transportStreamSubscription.resume();
},
onError: (error, stackTrace) {
unawaited(cancelAuthentication());
unawaited(session._transportStreamSubscription.cancel());
try {
transport.close();
} catch (_) {
/* transport may already be closed */
}
if (welcomeCompleter.isCompleted) {
return;
}
welcomeCompleter.completeError(error, stackTrace);
},
),
);
return;
}
if (materialized is Abort) {
unawaited(cancelAuthentication());
try {
transport.close();
} catch (_) {
/* transport may already be closed */
}
welcomeCompleter.completeError(materialized);
return;
}
if (materialized is Goodbye) {
unawaited(cancelAuthentication());
if (!welcomeCompleter.isCompleted) {
welcomeCompleter.completeError(materialized);
}
session._handleGoodbye(materialized);
return;
}
},
cancelOnError: true,
onError: (error, stackTrace) {
unawaited(cancelAuthentication());
_logger.warning(error);
if (!welcomeCompleter.isCompleted) {
welcomeCompleter.completeError(error, stackTrace);
}
unawaited(session._handleTransportClosed(error, stackTrace));
transport.close(error: error);
},
onDone: () {
unawaited(cancelAuthentication());
if (!welcomeCompleter.isCompleted) {
welcomeCompleter.completeError(
StateError('Transport closed before session welcome'),
);
}
unawaited(session._handleTransportClosed());
transport.close();
},
);
if (!transport.isReady) {
await transport.onReady;
}
transport.send(hello);
return welcomeCompleter.future;
}