acceptConnection method
Register connection as a new session and return it.
Returns null — after closing connection — when this host cannot take
it: it was disposed, it was stopped, or sessionId is already in use.
Closing is the point: the caller handed over a live socket, and a
refusal that left it open would leak it for the lifetime of the process.
sessionId overrides the generated identifier. Only pass one that is
unguessable — it is echoed to the client and used as a broadcast
identity, so a predictable id lets a peer impersonate another.
Implementation
S? acceptConnection(
TransportConnection connection, {
String? sessionId,
}) {
if (_state == SessionHostState.disposed) {
// Refused without an event: `dispose` closed `serverEvents`, so there
// is nobody left to tell. Closing the connection is the part that
// matters — the caller handed over a live socket.
unawaited(tryCatchIgnore(connection.close));
return null;
}
if (_state == SessionHostState.stopped) {
return _refuseConnection(
connection,
'the host is stopped',
);
}
if (_state == SessionHostState.idle) {
if (ownsTransport) {
// A host that owns its transport cannot be handed a connection before
// it has started: something is accepting on its behalf.
return _refuseConnection(connection, 'the host is not started');
}
// Embedded in another server: the connection is the start signal.
_markRunning();
}
final id = sessionId ?? generateSessionId();
if (_sessions.containsKey(id)) {
return _refuseConnection(
connection,
'session id $id is already in use',
);
}
final session = createSession(id: id, connection: connection);
_sessions[id] = session;
addServerEvent(
ServerEvent(
type: ServerEventType.clientConnected,
message: 'Client connected with session id: $id',
data: {
'clientId': id,
},
),
);
session.events.listen(
handleSessionEvent,
onDone: () {
handleSessionClosed(id);
},
onError: (dynamic error) => handleSessionError(id, error),
);
return session;
}