示例#1
0
        public void Instantiates_With_The_Given_Data(string filename, int placeInQueue)
        {
            var a = new PeerPlaceInQueueResponse(filename, placeInQueue);

            Assert.Equal(filename, a.Filename);
            Assert.Equal(placeInQueue, a.PlaceInQueue);
        }
示例#2
0
        public void Parse_Throws_MessageReadException_On_Missing_Data()
        {
            var msg = new MessageBuilder()
                .WriteCode(MessageCode.Peer.PlaceInQueueResponse)
                .Build();

            var ex = Record.Exception(() => PeerPlaceInQueueResponse.Parse(msg));

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

            var ex = Record.Exception(() => PeerPlaceInQueueResponse.Parse(msg));

            Assert.NotNull(ex);
            Assert.IsType<MessageException>(ex);
        }
示例#4
0
        public void Parse_Returns_Expected_Data(string filename, int placeInQueue)
        {
            var msg = new MessageBuilder()
                .WriteCode(MessageCode.Peer.PlaceInQueueResponse)
                .WriteString(filename)
                .WriteInteger(placeInQueue)
                .Build();

            var response = PeerPlaceInQueueResponse.Parse(msg);

            Assert.Equal(filename, response.Filename);
            Assert.Equal(placeInQueue, response.PlaceInQueue);
        }
示例#5
0
        public async Task GetDownloadPlaceInQueueAsync_Returns_Expected_Info(string username, string filename, int placeInQueue)
        {
            var result = new PeerPlaceInQueueResponse(filename, placeInQueue);

            var waiter = new Mock <IWaiter>();

            waiter.Setup(m => m.Wait <PeerPlaceInQueueResponse>(It.IsAny <WaitKey>(), null, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(result));
            waiter.Setup(m => m.Wait <GetPeerAddressResponse>(It.IsAny <WaitKey>(), null, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(new GetPeerAddressResponse(username, IPAddress.Parse("127.0.0.1"), 1)));

            var serverConn = new Mock <IMessageConnection>();

            serverConn.Setup(m => m.WriteAsync(It.IsAny <byte[]>(), It.IsAny <CancellationToken>()))
            .Returns(Task.CompletedTask);

            var conn = new Mock <IMessageConnection>();

            conn.Setup(m => m.WriteAsync(It.IsAny <byte[]>(), It.IsAny <CancellationToken>()))
            .Returns(Task.CompletedTask);

            var connManager = new Mock <IPeerConnectionManager>();

            connManager.Setup(m => m.GetOrAddMessageConnectionAsync(username, It.IsAny <IPAddress>(), It.IsAny <int>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(conn.Object));

            var s = new SoulseekClient("127.0.0.1", 1, waiter: waiter.Object, serverConnection: serverConn.Object, peerConnectionManager: connManager.Object);

            s.SetProperty("State", SoulseekClientStates.Connected | SoulseekClientStates.LoggedIn);

            var dict = new ConcurrentDictionary <int, Transfer>();

            dict.GetOrAdd(0, new Transfer(TransferDirection.Download, username, filename, 0));

            s.SetProperty("Downloads", dict);

            var place = await s.GetDownloadPlaceInQueueAsync(username, filename);

            Assert.Equal(placeInQueue, place);
        }
示例#6
0
        public async void HandleMessage(object sender, byte[] message)
        {
            var connection = (IMessageConnection)sender;
            var code       = new MessageReader <MessageCode.Peer>(message).ReadCode();

            Diagnostic.Debug($"Peer message received: {code} from {connection.Username} ({connection.IPAddress}:{connection.Port})");

            try
            {
                switch (code)
                {
                case MessageCode.Peer.SearchResponse:
                    var searchResponse = SearchResponseSlim.Parse(message);
                    if (SoulseekClient.Searches.TryGetValue(searchResponse.Token, out var search))
                    {
                        search.AddResponse(searchResponse);
                    }

                    break;

                case MessageCode.Peer.BrowseResponse:
                    var browseWaitKey = new WaitKey(MessageCode.Peer.BrowseResponse, connection.Username);
                    try
                    {
                        SoulseekClient.Waiter.Complete(browseWaitKey, BrowseResponse.Parse(message));
                    }
                    catch (Exception ex)
                    {
                        SoulseekClient.Waiter.Throw(browseWaitKey, new MessageReadException("The peer returned an invalid browse response.", ex));
                        throw;
                    }

                    break;

                case MessageCode.Peer.InfoResponse:
                    var infoResponse = UserInfoResponse.Parse(message);
                    SoulseekClient.Waiter.Complete(new WaitKey(MessageCode.Peer.InfoResponse, connection.Username), infoResponse);
                    break;

                case MessageCode.Peer.TransferResponse:
                    var transferResponse = TransferResponse.Parse(message);
                    Console.WriteLine($"Got response from {connection.Username}: {transferResponse.Token}");
                    SoulseekClient.Waiter.Complete(new WaitKey(MessageCode.Peer.TransferResponse, connection.Username, transferResponse.Token), transferResponse);
                    break;

                case MessageCode.Peer.QueueDownload:
                    // the end state here is to wait until there's actually a free slot, then send this request to the peer to
                    // let them know we are ready to start the actual transfer.
                    var queueDownloadRequest = QueueDownloadRequest.Parse(message);
                    var(queueAllowed, queueRejectionMessage) = SoulseekClient.Resolvers.QueueDownloadResponse(connection.Username, connection.IPAddress, connection.Port, queueDownloadRequest.Filename);

                    if (!queueAllowed)
                    {
                        await connection.WriteAsync(new QueueFailedResponse(queueDownloadRequest.Filename, queueRejectionMessage)).ConfigureAwait(false);
                    }

                    break;

                case MessageCode.Peer.TransferRequest:
                    var transferRequest = TransferRequest.Parse(message);

                    if (transferRequest.Direction == TransferDirection.Upload)
                    {
                        SoulseekClient.Waiter.Complete(new WaitKey(MessageCode.Peer.TransferRequest, connection.Username, transferRequest.Filename), transferRequest);
                    }
                    else
                    {
                        var(transferAllowed, transferRejectionMessage) = SoulseekClient.Resolvers.QueueDownloadResponse(connection.Username, connection.IPAddress, connection.Port, transferRequest.Filename);

                        if (!transferAllowed)
                        {
                            await connection.WriteAsync(new TransferResponse(transferRequest.Token, transferRejectionMessage)).ConfigureAwait(false);

                            await connection.WriteAsync(new QueueFailedResponse(transferRequest.Filename, transferRejectionMessage)).ConfigureAwait(false);
                        }
                        else
                        {
                            await connection.WriteAsync(new TransferResponse(transferRequest.Token, "Queued.")).ConfigureAwait(false);
                        }
                    }

                    break;

                case MessageCode.Peer.QueueFailed:
                    var queueFailedResponse = QueueFailedResponse.Parse(message);
                    SoulseekClient.Waiter.Throw(new WaitKey(MessageCode.Peer.TransferRequest, connection.Username, queueFailedResponse.Filename), new TransferRejectedException(queueFailedResponse.Message));
                    break;

                case MessageCode.Peer.PlaceInQueueResponse:
                    var placeInQueueResponse = PeerPlaceInQueueResponse.Parse(message);
                    SoulseekClient.Waiter.Complete(new WaitKey(MessageCode.Peer.PlaceInQueueResponse, connection.Username, placeInQueueResponse.Filename), placeInQueueResponse);
                    break;

                case MessageCode.Peer.UploadFailed:
                    var uploadFailedResponse = PeerUploadFailedResponse.Parse(message);
                    var msg = $"Download of {uploadFailedResponse.Filename} reported as failed by {connection.Username}.";

                    var download = SoulseekClient.Downloads.Values.FirstOrDefault(d => d.Username == connection.Username && d.Filename == uploadFailedResponse.Filename);
                    if (download != null)
                    {
                        SoulseekClient.Waiter.Throw(new WaitKey(MessageCode.Peer.TransferRequest, download.Username, download.Filename), new TransferException(msg));
                        SoulseekClient.Waiter.Throw(download.WaitKey, new TransferException(msg));
                    }

                    Diagnostic.Debug(msg);
                    break;

                case MessageCode.Peer.BrowseRequest:
                    var browseResponse = SoulseekClient.Resolvers.BrowseResponse(connection.Username, connection.IPAddress, connection.Port);
                    await connection.WriteAsync(browseResponse).ConfigureAwait(false);

                    break;

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