Example #1
0
        /// <summary>
        ///     Handles embedded messages from the server.
        /// </summary>
        /// <param name="message">The message.</param>
        public async void HandleEmbeddedMessage(byte[] message)
        {
            var code = MessageCode.Distributed.Unknown;

            try
            {
                var embeddedMessage = EmbeddedMessage.FromByteArray(message);
                code = embeddedMessage.DistributedCode;
                var distributedMessage = embeddedMessage.DistributedMessage;

                switch (code)
                {
                case MessageCode.Distributed.SearchRequest:
                    // receiving a SearchRequest/3 from the server as an embedded message indicates that we are
                    // operating as a branch root on the distributed network.
                    SoulseekClient.DistributedConnectionManager.PromoteToBranchRoot();

                    var searchRequest = DistributedSearchRequest.FromByteArray(distributedMessage);

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

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

                    break;

                default:
                    Diagnostic.Debug($"Unhandled embedded message: {code}; {message.Length} bytes");
                    break;
                }
            }
            catch (Exception ex)
            {
                Diagnostic.Warning($"Error handling embedded message: {code}; {ex.Message}", ex);
            }
        }
Example #2
0
        public void Parse_Throws_MessageReadException_On_Missing_Data()
        {
            var msg = new MessageBuilder()
                      .WriteCode(MessageCode.Server.EmbeddedMessage)
                      .Build();

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

            Assert.NotNull(ex);
            Assert.IsType <MessageReadException>(ex);
        }
Example #3
0
        public void Parse_Throws_MessageException_On_Code_Mismatch()
        {
            var msg = new MessageBuilder()
                      .WriteCode(MessageCode.Peer.BrowseRequest)
                      .Build();

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

            Assert.NotNull(ex);
            Assert.IsType <MessageException>(ex);
        }
Example #4
0
        internal void Parse_Returns_Expected_Data(MessageCode.Distributed code, byte[] message)
        {
            var msg = new MessageBuilder()
                      .WriteCode(MessageCode.Server.EmbeddedMessage)
                      .WriteByte((byte)code)
                      .WriteBytes(message)
                      .Build();

            var r = EmbeddedMessage.FromByteArray(msg);

            Assert.Equal(code, r.DistributedCode);
            Assert.Equal(message.Length + 1, BitConverter.ToInt32(r.DistributedMessage.Take(4).ToArray()));
            Assert.Equal((byte)code, r.DistributedMessage.Skip(4).Take(1).First());
            Assert.Equal(message, r.DistributedMessage.Skip(5));
        }
Example #5
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);
            }
        }