selectOneOf method

Future<ProtocolID?> selectOneOf(
  1. P2PStream stream,
  2. List<ProtocolID> protocolsToSelect
)

Selects one of the given protocols with the remote peer.

This method implements the initiator side of the multistream-select protocol. It will try each protocol in protocolsToSelect in order until the remote peer acknowledges one or indicates it does not support any of them.

Returns the selected ProtocolID or null if no protocol could be agreed upon.

Implementation

Future<ProtocolID?> selectOneOf(P2PStream<dynamic> stream, List<ProtocolID> protocolsToSelect) async {
  final startTime = DateTime.now();
  final streamId = stream.id();

  // Safely get peer ID - during negotiation, conn might not be available
  String peerInfo;
  try {
    peerInfo = stream.conn.remotePeer.toString();
  } catch (e) {
    peerInfo = 'unknown_peer';
  }

  try {
    // 1. Send our multistream protocol ID
    await _writeDelimited(stream, utf8.encode(protocolID));

    // 2. Read their multistream protocol ID
    final remoteProtoID = await _readNextToken(stream);

    if (remoteProtoID != protocolID) {

      await stream.reset();
      throw IncorrectVersionException(
          'Remote peer responded with wrong multistream version: $remoteProtoID, expected $protocolID');
    }

    // 3. Iterate through the protocols we want to select
    for (int i = 0; i < protocolsToSelect.length; i++) {
      final p = protocolsToSelect[i];
      final protocolStart = DateTime.now();

      await _writeDelimited(stream, utf8.encode(p));
      final response = await _readNextToken(stream);

      if (response == p) {
        // Protocol selected
        final totalDuration = DateTime.now().difference(startTime);

        return p;
      } else if (response == 'na') {
        // Protocol not available, try next
        continue;
      } else {
        // Unexpected response
        await stream.reset();
        throw IncorrectVersionException(
            'Remote peer sent unexpected response: "$response" when trying to negotiate protocol "$p"');
      }
    }

    // No protocol was selected
    final totalDuration = DateTime.now().difference(startTime);

    await stream.reset(); // Ensure stream is reset if no protocol is selected
    return null;
  } catch (e, st) {
    final totalDuration = DateTime.now().difference(startTime);

    await stream.reset();
    rethrow;
  }
}