public void Remove_DoesNotExist() { var manager = new ConnectionManager(); var peer = new Peer { Id = aId }; var a = new PeerConnection { RemotePeer = peer, Stream = Stream.Null }; Assert.IsFalse(manager.Remove(a)); Assert.IsFalse(manager.IsConnected(peer)); Assert.AreEqual(0, manager.Connections.Count()); Assert.IsNull(a.Stream); }
async Task <PeerConnection> DialAsync(Peer remote, MultiAddress addr, CancellationToken cancel) { // TODO: HACK: Currenty only the ipfs/p2p is supported. // short circuit to make life faster. if (addr.Protocols.Count != 3 || !(addr.Protocols[2].Name == "ipfs" || addr.Protocols[2].Name == "p2p")) { throw new Exception($"Cannnot dial; unknown protocol in '{addr}'."); } // Establish the transport stream. Stream stream = null; foreach (var protocol in addr.Protocols) { cancel.ThrowIfCancellationRequested(); if (TransportRegistry.Transports.TryGetValue(protocol.Name, out Func <IPeerTransport> transport)) { stream = await transport().ConnectAsync(addr, cancel); if (cancel.IsCancellationRequested) { stream?.Dispose(); continue; } break; } } if (stream == null) { throw new Exception("Missing a known transport protocol name."); } // Build the connection. remote.ConnectedAddress = addr; var connection = new PeerConnection { IsIncoming = false, LocalPeer = LocalPeer, // TODO: LocalAddress LocalPeerKey = LocalPeerKey, RemotePeer = remote, RemoteAddress = addr, Stream = stream }; return(connection); }
public void PeerDisconnectedEvent_ConnectionClose() { int gotEvent = 0; var manager = new ConnectionManager(); manager.PeerDisconnected += (s, e) => gotEvent += 1; var peerA = new Peer { Id = aId }; var a = new PeerConnection { RemotePeer = peerA, Stream = Stream.Null }; manager.Add(a); a.Dispose(); Assert.AreEqual(1, gotEvent); }
public void PeerDisconnectedEvent_RemovingPeer() { bool gotEvent = false; var manager = new ConnectionManager(); manager.PeerDisconnected += (s, e) => gotEvent = true; var peerA = new Peer { Id = aId }; var a = new PeerConnection { RemotePeer = peerA, Stream = Stream.Null }; manager.Add(a); manager.Remove(peerA.Id); Assert.IsTrue(gotEvent); }
public void Maintains_PeerConnectedAddress() { var address1 = "/ip4/127.0.0.1/tcp/4007"; var address2 = "/ip4/127.0.0.2/tcp/4007"; var manager = new ConnectionManager(); var peer = new Peer { Id = aId }; var a = new PeerConnection { RemotePeer = peer, RemoteAddress = address1, Stream = Stream.Null }; var b = new PeerConnection { RemotePeer = peer, RemoteAddress = address2, Stream = Stream.Null }; Assert.AreSame(a, manager.Add(a)); Assert.IsTrue(manager.IsConnected(peer)); Assert.AreEqual(1, manager.Connections.Count()); Assert.IsNotNull(a.Stream); Assert.AreEqual(address1, peer.ConnectedAddress); Assert.AreSame(b, manager.Add(b)); Assert.IsTrue(manager.IsConnected(peer)); Assert.AreEqual(2, manager.Connections.Count()); Assert.IsNotNull(a.Stream); Assert.IsNotNull(b.Stream); Assert.AreEqual(address1, peer.ConnectedAddress); Assert.IsTrue(manager.Remove(a)); Assert.IsTrue(manager.IsConnected(peer)); Assert.AreEqual(1, manager.Connections.Count()); Assert.IsNull(a.Stream); Assert.IsNotNull(b.Stream); Assert.AreEqual(address2, peer.ConnectedAddress); Assert.IsTrue(manager.Remove(b)); Assert.IsFalse(manager.IsConnected(peer)); Assert.AreEqual(0, manager.Connections.Count()); Assert.IsNull(a.Stream); Assert.IsNull(b.Stream); Assert.IsNull(peer.ConnectedAddress); }
public void Add_Duplicate_SameConnection() { var manager = new ConnectionManager(); var peer = new Peer { Id = aId }; var a = new PeerConnection { RemotePeer = peer, Stream = Stream.Null }; Assert.AreSame(a, manager.Add(a)); Assert.IsTrue(manager.IsConnected(peer)); Assert.AreEqual(1, manager.Connections.Count()); Assert.IsNotNull(a.Stream); Assert.AreSame(a, manager.Add(a)); Assert.IsTrue(manager.IsConnected(peer)); Assert.AreEqual(1, manager.Connections.Count()); Assert.IsNotNull(a.Stream); }
public void IsConnected_NotActive() { var manager = new ConnectionManager(); var peer = new Peer { Id = aId }; var connection = new PeerConnection { RemotePeer = peer, Stream = Stream.Null }; Assert.IsFalse(manager.IsConnected(peer)); manager.Add(connection); Assert.IsTrue(manager.IsConnected(peer)); Assert.AreEqual(1, manager.Connections.Count()); connection.Stream = null; Assert.IsFalse(manager.IsConnected(peer)); Assert.AreEqual(0, manager.Connections.Count()); }
public void Remove_PeerId() { var manager = new ConnectionManager(); var peer = new Peer { Id = aId }; var a = new PeerConnection { RemotePeer = peer, Stream = Stream.Null }; manager.Add(a); Assert.IsTrue(manager.IsConnected(peer)); Assert.AreEqual(1, manager.Connections.Count()); Assert.IsNotNull(a.Stream); Assert.IsTrue(manager.Remove(peer.Id)); Assert.IsFalse(manager.IsConnected(peer)); Assert.AreEqual(0, manager.Connections.Count()); Assert.IsNull(a.Stream); }
public void Stats() { var stream = new MemoryStream(); var connection = new PeerConnection { Stream = stream }; Assert.AreEqual(0, connection.BytesRead); Assert.AreEqual(0, connection.BytesWritten); var buffer = new byte[] { 1, 2, 3 }; connection.Stream.Write(buffer, 0, 3); Assert.AreEqual(0, connection.BytesRead); Assert.AreEqual(3, connection.BytesWritten); stream.Position = 0; connection.Stream.ReadByte(); connection.Stream.ReadByte(); Assert.AreEqual(2, connection.BytesRead); Assert.AreEqual(3, connection.BytesWritten); }
public void Disposing() { var closeCount = 0; var stream = new MemoryStream(); var connection = new PeerConnection { Stream = stream }; connection.Closed += (s, e) => { ++closeCount; }; Assert.IsNotNull(connection.Stream); connection.Dispose(); Assert.IsNull(connection.Stream); // Can be disposed multiple times. connection.Dispose(); Assert.AreEqual(1, closeCount); }
/// <summary> /// Adds a new connection. /// </summary> /// <param name="connection"> /// The <see cref="PeerConnection"/> to add. /// </param> /// <returns> /// The connection that should be used. /// </returns> /// <remarks> /// If a connection already exists to the peer, the specified /// <paramref name="connection"/> is closed and existing connection /// is returned. /// </remarks> public PeerConnection Add(PeerConnection connection) { if (connection == null) { throw new ArgumentNullException("connection"); } if (connection.RemotePeer == null) { throw new ArgumentNullException("connection.RemotePeer"); } if (connection.RemotePeer.Id == null) { throw new ArgumentNullException("connection.RemotePeer.Id"); } connections.AddOrUpdate( Key(connection.RemotePeer), (key) => new List <PeerConnection> { connection }, (key, conns) => { if (!conns.Contains(connection)) { conns.Add(connection); } return(conns); } ); if (connection.RemotePeer.ConnectedAddress == null) { connection.RemotePeer.ConnectedAddress = connection.RemoteAddress; } connection.Closed += (s, e) => Remove(e); return(connection); }
/// <summary> /// Remove a connection. /// </summary> /// <param name="connection"> /// The <see cref="PeerConnection"/> to remove. /// </param> /// <returns> /// <b>true</b> if the connection was removed; otherwise, <b>false</b>. /// </returns> /// <remarks> /// The <paramref name="connection"/> is removed from the list of /// connections and is closed. /// </remarks> public bool Remove(PeerConnection connection) { if (connection == null) { return(false); } if (!connections.TryGetValue(Key(connection.RemotePeer), out List <PeerConnection> originalConns)) { connection.Dispose(); return(false); } if (!originalConns.Contains(connection)) { connection.Dispose(); return(false); } var newConns = new List <PeerConnection>(); newConns.AddRange(originalConns.Where(c => c != connection)); connections.TryUpdate(Key(connection.RemotePeer), newConns, originalConns); connection.Dispose(); if (newConns.Count > 0) { var last = newConns.Last(); last.RemotePeer.ConnectedAddress = last.RemoteAddress; } else { connection.RemotePeer.ConnectedAddress = null; PeerDisconnected?.Invoke(this, connection.RemotePeer.Id); } return(true); }
/// <summary> /// Called when a remote peer is connecting to the local peer. /// </summary> /// <param name="stream"> /// The stream to the remote peer. /// </param> /// <param name="local"> /// The local peer's address. /// </param> /// <param name="remote"> /// The remote peer's address. /// </param> /// <remarks> /// Establishes the protocols of the connection. Any exception is simply /// logged as warning. /// </remarks> async void OnRemoteConnect(Stream stream, MultiAddress local, MultiAddress remote) { // If the remote is already trying to establish a connection, then we // can just refuse this one. if (!pendingRemoteConnections.TryAdd(remote, null)) { log.Debug($"Duplicate remote connection from {remote}"); stream.Dispose(); return; } try { log.Debug($"{LocalPeer.Id} got remote connection"); log.Debug("local " + local); log.Debug("remote " + remote); // TODO: Check the policies var connection = new PeerConnection { IsIncoming = true, LocalPeer = LocalPeer, LocalAddress = local, LocalPeerKey = LocalPeerKey, RemoteAddress = remote, Stream = stream }; // Are we communicating to a private network? if (NetworkProtector != null) { connection.Stream = await NetworkProtector.ProtectAsync(connection).ConfigureAwait(false); } // Mount the protocols. MountProtocols(connection); // Start the handshake // TODO: Isn't connection cancel token required. connection.ReadMessages(default(CancellationToken)); // Wait for security to be established. await connection.SecurityEstablished.Task.ConfigureAwait(false); // TODO: Maybe connection.LocalPeerKey = null; // Wait for the handshake to complete. var muxer = await connection.MuxerEstablished.Task; // Need details on the remote peer. Identify1 identify = null; lock (protocols) { identify = protocols.OfType <Identify1>().First(); } connection.RemotePeer = await identify.GetRemotePeer(connection, default(CancellationToken)).ConfigureAwait(false); connection.RemotePeer = RegisterPeer(connection.RemotePeer); connection.RemoteAddress = new MultiAddress($"{remote}/ipfs/{connection.RemotePeer.Id}"); var actual = Manager.Add(connection); if (actual == connection) { ConnectionEstablished?.Invoke(this, connection); } } catch (Exception e) { log.Warn("Remote connect failed", e); try { stream.Dispose(); } catch (Exception) { // eat it. } } finally { pendingRemoteConnections.TryRemove(remote, out object _); } }
/// <summary> /// Establish a duplex stream between the local and remote peer. /// </summary> /// <param name="remote"></param> /// <param name="addrs"></param> /// <param name="cancel"></param> /// <returns></returns> async Task <PeerConnection> Dial(Peer remote, IEnumerable <MultiAddress> addrs, CancellationToken cancel) { log.Debug($"Dialing {remote}"); if (remote == LocalPeer) { throw new Exception("Cannot dial self."); } // If no addresses, then ask peer routing. if (Router != null && addrs.Count() == 0) { var found = await Router.FindPeerAsync(remote.Id, cancel).ConfigureAwait(false); addrs = found.Addresses; remote.Addresses = addrs; } // Get the addresses we can use to dial the remote. var possibleAddresses = (await Task.WhenAll(addrs.Select(a => a.ResolveAsync(cancel))).ConfigureAwait(false)) .SelectMany(a => a) .Select(a => a.WithPeerId(remote.Id)) .Distinct() .ToArray(); // TODO: filter out self addresses and others. if (possibleAddresses.Length == 0) { throw new Exception($"{remote} has no known address."); } // Try the various addresses in parallel. The first one to complete wins. PeerConnection connection = null; try { using (var timeout = new CancellationTokenSource(TransportConnectionTimeout)) using (var cts = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancel)) { var attempts = possibleAddresses .Select(a => DialAsync(remote, a, cts.Token)); connection = await TaskHelper.WhenAnyResult(attempts, cts.Token).ConfigureAwait(false); cts.Cancel(); // stop other dialing tasks. } } catch (Exception e) { var attemped = string.Join(", ", possibleAddresses.Select(a => a.ToString())); log.Trace($"Cannot dial {attemped}"); throw new Exception($"Cannot dial {remote}.", e); } // Do the connection handshake. try { MountProtocols(connection); IEncryptionProtocol[] security = null; lock (protocols) { security = protocols.OfType <IEncryptionProtocol>().ToArray(); } await connection.InitiateAsync(security, cancel).ConfigureAwait(false); await connection.MuxerEstablished.Task.ConfigureAwait(false); Identify1 identify = null; lock (protocols) { identify = protocols.OfType <Identify1>().First(); } await identify.GetRemotePeer(connection, cancel).ConfigureAwait(false); } catch (Exception) { connection.Dispose(); throw; } var actual = Manager.Add(connection); if (actual == connection) { ConnectionEstablished?.Invoke(this, connection); } return(actual); }
public Task <Stream> ProtectAsync(PeerConnection connection, CancellationToken cancel = default(CancellationToken)) { Interlocked.Increment(ref Count); return(Task.FromResult(connection.Stream)); }
/// <summary> /// Is invoked by the <see cref="Swarm"/> when a peer is connected to. /// </summary> void Swarm_ConnectionEstablished(object sender, PeerConnection connection) { SetReachable(connection.RemotePeer); }