Exemple #1
0
        public void FromByteArray_Returns_Expected_Data(int level)
        {
            var msg = new MessageBuilder()
                      .WriteCode(MessageCode.Distributed.BranchLevel)
                      .WriteInteger(level)
                      .Build();

            var response = DistributedBranchLevel.FromByteArray(msg);

            Assert.Equal(level, response.Level);
        }
Exemple #2
0
        public void FromByteArray_Throws_MessageException_On_Code_Mismatch()
        {
            var msg = new MessageBuilder()
                      .WriteCode(MessageCode.Distributed.ChildDepth)
                      .WriteInteger(1)
                      .Build();

            var ex = Record.Exception(() => DistributedBranchLevel.FromByteArray(msg));

            Assert.NotNull(ex);
            Assert.IsType <MessageException>(ex);
        }
Exemple #3
0
        public void ToByteArray_Constructs_The_Correct_Message(int level)
        {
            var msg = new DistributedBranchLevel(level).ToByteArray();

            var reader = new MessageReader <MessageCode.Distributed>(msg);
            var code   = reader.ReadCode();

            Assert.Equal(MessageCode.Distributed.BranchLevel, code);
            Assert.Equal(4 + 1 + 4, msg.Length);

            Assert.Equal(level, reader.ReadInteger());
        }
Exemple #4
0
        public void HandleChildMessageRead_Produces_Debug_On_Unhandled_Message()
        {
            var(handler, mocks) = GetFixture();

            var conn = new Mock <IMessageConnection>();

            var message = new DistributedBranchLevel(1).ToByteArray();

            handler.HandleChildMessageRead(conn.Object, message);

            mocks.Diagnostic.Verify(m => m.Debug(It.Is <string>(s => s.ContainsInsensitive("unhandled distributed child message"))), Times.Once);
        }
Exemple #5
0
        public void HandleMessageWritten_Produces_Debug_On_Message(string username)
        {
            var(handler, mocks) = GetFixture();

            var conn = new Mock <IMessageConnection>();

            conn.Setup(m => m.Username)
            .Returns(username);

            var message = new DistributedBranchLevel(1).ToByteArray();

            handler.HandleMessageWritten(conn.Object, new MessageEventArgs(message));

            mocks.Diagnostic.Verify(m => m.Debug(It.Is <string>(s => s.ContainsInsensitive($"Distributed message sent: BranchLevel"))), Times.Once);
        }
Exemple #6
0
        /// <summary>
        ///     Handles incoming messages.
        /// </summary>
        /// <param name="sender">The <see cref="IMessageConnection"/> instance from which the message originated.</param>
        /// <param name="message">The message.</param>
        public async void HandleMessageRead(object sender, byte[] message)
        {
            var connection = (IMessageConnection)sender;
            var code       = new MessageReader <MessageCode.Distributed>(message).ReadCode();

            if (code != MessageCode.Distributed.SearchRequest && code != MessageCode.Distributed.EmbeddedMessage)
            {
                Diagnostic.Debug($"Distributed message received: {code} from {connection.Username} ({connection.IPEndPoint}) (id: {connection.Id})");
            }
            else if (SoulseekClient.Options.DeduplicateSearchRequests)
            {
                var current = Convert.ToBase64String(message);

                if (DeduplicationHash == current)
                {
                    return;
                }

                DeduplicationHash = current;
            }

            try
            {
                switch (code)
                {
                // if we are connected to a branch root, we will receive EmbeddedMessage/93.
                case MessageCode.Distributed.EmbeddedMessage:
                    var embeddedMessage = EmbeddedMessage.FromByteArray(message);

                    switch (embeddedMessage.DistributedCode)
                    {
                    // convert this message to a normal DistributedSearchRequest before forwarding.  this functionality is based
                    // on the observation that branch roots send embedded messages to children, while parents that are not a branch root
                    // send a plain SearchRequest/3.
                    case MessageCode.Distributed.SearchRequest:
                        var embeddedSearchRequest = DistributedSearchRequest.FromByteArray(embeddedMessage.DistributedMessage);

                        _ = SoulseekClient.DistributedConnectionManager.BroadcastMessageAsync(embeddedMessage.DistributedMessage).ConfigureAwait(false);

                        await SoulseekClient.SearchResponder.TryRespondAsync(embeddedSearchRequest.Username, embeddedSearchRequest.Token, embeddedSearchRequest.Query).ConfigureAwait(false);

                        break;

                    default:
                        Diagnostic.Debug($"Unhandled embedded message: {code} from {connection.Username} ({connection.IPEndPoint}); {message.Length} bytes");
                        break;
                    }

                    break;

                // if we are connected to anyone other than a branch root, we will receive SearchRequest/3.
                case MessageCode.Distributed.SearchRequest:
                    var searchRequest = DistributedSearchRequest.FromByteArray(message);

                    _ = SoulseekClient.DistributedConnectionManager.BroadcastMessageAsync(message).ConfigureAwait(false);

                    await SoulseekClient.SearchResponder.TryRespondAsync(searchRequest.Username, searchRequest.Token, searchRequest.Query).ConfigureAwait(false);

                    break;

                case MessageCode.Distributed.Ping:
                    var pingResponse = DistributedPingResponse.FromByteArray(message);
                    SoulseekClient.Waiter.Complete(new WaitKey(MessageCode.Distributed.Ping, connection.Username), pingResponse);

                    break;

                case MessageCode.Distributed.BranchLevel:
                    var branchLevel = DistributedBranchLevel.FromByteArray(message);

                    if ((connection.Username, connection.IPEndPoint) == SoulseekClient.DistributedConnectionManager.Parent)
                    {
                        SoulseekClient.DistributedConnectionManager.SetParentBranchLevel(branchLevel.Level);
                    }

                    break;

                case MessageCode.Distributed.BranchRoot:
                    var branchRoot = DistributedBranchRoot.FromByteArray(message);

                    if ((connection.Username, connection.IPEndPoint) == SoulseekClient.DistributedConnectionManager.Parent)
                    {
                        SoulseekClient.DistributedConnectionManager.SetParentBranchRoot(branchRoot.Username);
                    }

                    break;

                case MessageCode.Distributed.ChildDepth:
                    var childDepth = DistributedChildDepth.FromByteArray(message);
                    SoulseekClient.Waiter.Complete(new WaitKey(Constants.WaitKey.ChildDepthMessage, connection.Key), childDepth.Depth);
                    break;

                default:
                    Diagnostic.Debug($"Unhandled distributed message: {code} from {connection.Username} ({connection.IPEndPoint}); {message.Length} bytes");
                    break;
                }
            }
            catch (Exception ex)
            {
                Diagnostic.Warning($"Error handling distributed message: {code} from {connection.Username} ({connection.IPEndPoint}); {ex.Message}", ex);
            }
        }
Exemple #7
0
        public void Instantiates_With_The_Given_Data(int level)
        {
            var r = new DistributedBranchLevel(level);

            Assert.Equal(level, r.Level);
        }
Exemple #8
0
        /// <summary>
        ///     Handles incoming messages.
        /// </summary>
        /// <param name="sender">The <see cref="IMessageConnection"/> instance from which the message originated.</param>
        /// <param name="message">The message.</param>
        public async void HandleMessage(object sender, byte[] message)
        {
            var connection = (IMessageConnection)sender;
            var code       = new MessageReader <MessageCode.Distributed>(message).ReadCode();

            if (code != MessageCode.Distributed.SearchRequest)
            {
                Diagnostic.Debug($"Distributed message received: {code} from {connection.Username} ({connection.IPAddress}:{connection.Port})");
            }

            try
            {
                switch (code)
                {
                // some clients erroneously send code 93, which is a server code, in place of 3.
                case MessageCode.Distributed.ServerSearchRequest:
                case MessageCode.Distributed.SearchRequest:
                    var            searchRequest = DistributedSearchRequest.FromByteArray(message);
                    SearchResponse searchResponse;

                    SoulseekClient.Waiter.Complete(new WaitKey(Constants.WaitKey.SearchRequestMessage, connection.Context, connection.Key));
                    SoulseekClient.DistributedConnectionManager.BroadcastMessageAsync(message).Forget();

                    if (SoulseekClient.Options.SearchResponseResolver == default)
                    {
                        break;
                    }

                    try
                    {
                        searchResponse = await SoulseekClient.Options.SearchResponseResolver(searchRequest.Username, searchRequest.Token, searchRequest.Query).ConfigureAwait(false);

                        if (searchResponse != null && searchResponse.FileCount > 0)
                        {
                            var(ip, port) = await SoulseekClient.GetUserAddressAsync(searchRequest.Username).ConfigureAwait(false);

                            var peerConnection = await SoulseekClient.PeerConnectionManager.GetOrAddMessageConnectionAsync(searchRequest.Username, ip, port, CancellationToken.None).ConfigureAwait(false);

                            await peerConnection.WriteAsync(searchResponse.ToByteArray()).ConfigureAwait(false);
                        }
                    }
                    catch (Exception ex)
                    {
                        Diagnostic.Warning($"Error resolving search response for query '{searchRequest.Query}' requested by {searchRequest.Username} with token {searchRequest.Token}: {ex.Message}", ex);
                    }

                    break;

                case MessageCode.Distributed.Ping:
                    Diagnostic.Debug($"PING?");
                    var pingResponse = new PingResponse(SoulseekClient.GetNextToken());
                    await connection.WriteAsync(pingResponse.ToByteArray()).ConfigureAwait(false);

                    Diagnostic.Debug($"PONG!");
                    break;

                case MessageCode.Distributed.BranchLevel:
                    var branchLevel = DistributedBranchLevel.FromByteArray(message);

                    SoulseekClient.Waiter.Complete(new WaitKey(Constants.WaitKey.BranchLevelMessage, connection.Context, connection.Key), branchLevel.Level);

                    if ((connection.Username, connection.IPAddress, connection.Port) == SoulseekClient.DistributedConnectionManager.Parent)
                    {
                        SoulseekClient.DistributedConnectionManager.SetBranchLevel(branchLevel.Level);
                    }

                    break;

                case MessageCode.Distributed.BranchRoot:
                    var branchRoot = DistributedBranchRoot.FromByteArray(message);

                    SoulseekClient.Waiter.Complete(new WaitKey(Constants.WaitKey.BranchRootMessage, connection.Context, connection.Key), branchRoot.Username);

                    if ((connection.Username, connection.IPAddress, connection.Port) == SoulseekClient.DistributedConnectionManager.Parent)
                    {
                        SoulseekClient.DistributedConnectionManager.SetBranchRoot(branchRoot.Username);
                    }

                    break;

                case MessageCode.Distributed.ChildDepth:
                    var childDepth = DistributedChildDepth.FromByteArray(message);
                    SoulseekClient.Waiter.Complete(new WaitKey(Constants.WaitKey.ChildDepthMessage, connection.Key), childDepth.Depth);
                    break;

                default:
                    Diagnostic.Debug($"Unhandled distributed message: {code} from {connection.Username} ({connection.IPAddress}:{connection.Port}); {message.Length} bytes");
                    break;
                }
            }
            catch (Exception ex)
            {
                Diagnostic.Warning($"Error handling distributed message: {code} from {connection.Username} ({connection.IPAddress}:{connection.Port}); {ex.Message}", ex);
            }
        }
        /// <summary>
        ///     Handles incoming messages.
        /// </summary>
        /// <param name="sender">The <see cref="IMessageConnection"/> instance from which the message originated.</param>
        /// <param name="message">The message.</param>
        public async void HandleMessageRead(object sender, byte[] message)
        {
            var connection = (IMessageConnection)sender;
            var code       = new MessageReader <MessageCode.Distributed>(message).ReadCode();

            if (code != MessageCode.Distributed.SearchRequest && code != MessageCode.Distributed.ServerSearchRequest)
            {
                Diagnostic.Debug($"Distributed message received: {code} from {connection.Username} ({connection.IPEndPoint}) (id: {connection.Id})");
            }
            else if (SoulseekClient.Options.DeduplicateSearchRequests)
            {
                var current = Convert.ToBase64String(message);

                if (DeduplicationHash == current)
                {
                    return;
                }

                DeduplicationHash = current;
            }

            try
            {
                switch (code)
                {
                // if we are connected to a branch root, we get search requests with code DistributedServerSearchRequest.
                // convert this message to a normal DistributedSearchRequest before forwarding. not sure if this is correct,
                // but it would match the observed behavior. these messages may also be forwarded from the server message
                // handler if we haven't connected to a distributed parent in a timely manner.
                case MessageCode.Distributed.ServerSearchRequest:
                    var serverSearchRequest = DistributedServerSearchRequest.FromByteArray(message);

                    var forwardedSearchRequest = new DistributedSearchRequest(serverSearchRequest.Username, serverSearchRequest.Token, serverSearchRequest.Query);
                    _ = SoulseekClient.DistributedConnectionManager.BroadcastMessageAsync(forwardedSearchRequest.ToByteArray()).ConfigureAwait(false);

                    await TrySendSearchResults(serverSearchRequest.Username, serverSearchRequest.Token, serverSearchRequest.Query).ConfigureAwait(false);

                    break;

                // if we are connected to anyone other than a branch root, we should get search requests with code
                // SearchRequest. forward these requests as is.
                case MessageCode.Distributed.SearchRequest:
                    var searchRequest = DistributedSearchRequest.FromByteArray(message);

                    _ = SoulseekClient.DistributedConnectionManager.BroadcastMessageAsync(searchRequest.ToByteArray()).ConfigureAwait(false);

                    await TrySendSearchResults(searchRequest.Username, searchRequest.Token, searchRequest.Query).ConfigureAwait(false);

                    break;

                case MessageCode.Distributed.Ping:
                    var pingResponse = DistributedPingResponse.FromByteArray(message);
                    SoulseekClient.Waiter.Complete(new WaitKey(MessageCode.Distributed.Ping, connection.Username), pingResponse);

                    break;

                case MessageCode.Distributed.BranchLevel:
                    var branchLevel = DistributedBranchLevel.FromByteArray(message);

                    if ((connection.Username, connection.IPEndPoint) == SoulseekClient.DistributedConnectionManager.Parent)
                    {
                        SoulseekClient.DistributedConnectionManager.SetBranchLevel(branchLevel.Level);
                    }

                    break;

                case MessageCode.Distributed.BranchRoot:
                    var branchRoot = DistributedBranchRoot.FromByteArray(message);

                    if ((connection.Username, connection.IPEndPoint) == SoulseekClient.DistributedConnectionManager.Parent)
                    {
                        SoulseekClient.DistributedConnectionManager.SetBranchRoot(branchRoot.Username);
                    }

                    break;

                case MessageCode.Distributed.ChildDepth:
                    var childDepth = DistributedChildDepth.FromByteArray(message);
                    SoulseekClient.Waiter.Complete(new WaitKey(Constants.WaitKey.ChildDepthMessage, connection.Key), childDepth.Depth);
                    break;

                default:
                    Diagnostic.Debug($"Unhandled distributed message: {code} from {connection.Username} ({connection.IPEndPoint}); {message.Length} bytes");
                    break;
                }
            }
            catch (Exception ex)
            {
                Diagnostic.Warning($"Error handling distributed message: {code} from {connection.Username} ({connection.IPEndPoint}); {ex.Message}", ex);
            }
        }