public void Sends_Resolved_FolderContentsResponse(int token, string dirname)
        {
            var files = new List <File>()
            {
                new File(1, "1", 1, "1", 1, new List <FileAttribute>()
                {
                    new FileAttribute(FileAttributeType.BitDepth, 1)
                }),
                new File(2, "2", 2, "2", 1, new List <FileAttribute>()
                {
                    new FileAttribute(FileAttributeType.BitRate, 2)
                }),
            };

            var dir = new Directory(dirname, 2, files);

            var response = new FolderContentsResponse(token, dir);
            var options  = new SoulseekClientOptions(directoryContentsResponseResolver: (u, i, t, d) => Task.FromResult(dir));

            var(handler, mocks) = GetFixture(options: options);

            var msg = new FolderContentsRequest(token, dirname).ToByteArray();

            handler.HandleMessageRead(mocks.PeerConnection.Object, msg);

            mocks.PeerConnection.Verify(
                m => m.WriteAsync(It.Is <byte[]>(b => Encoding.UTF8.GetString(b) == Encoding.UTF8.GetString(response.ToByteArray())), null), Times.Once);
        }
Example #2
0
        public void Instantiates_With_Given_Data(int token, Directory dir)
        {
            var a = new FolderContentsResponse(token, dir);

            Assert.Equal(token, a.Token);
            Assert.Equal(dir, a.Directory);
        }
        public void Completes_Wait_For_FolderContentsResponse(string username, IPEndPoint endpoint, int token, string dirname)
        {
            var(handler, mocks) = GetFixture(username, endpoint);

            var msg = new FolderContentsResponse(token, new Directory(dirname)).ToByteArray();

            handler.HandleMessageRead(mocks.PeerConnection.Object, msg);

            mocks.Waiter.Verify(m => m.Complete(new WaitKey(MessageCode.Peer.FolderContentsResponse, username, token), It.IsAny <Directory>()), Times.Once);
        }
Example #4
0
        public void Parse_Throws_MessageException_On_Code_Mismatch()
        {
            var msg = new MessageBuilder()
                      .WriteCode(MessageCode.Peer.TransferResponse)
                      .Build();

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

            Assert.NotNull(ex);
            Assert.IsType <MessageException>(ex);
        }
Example #5
0
        public void Parse_Throws_MessageCompressionException_On_Uncompressed_Payload()
        {
            var msg = new MessageBuilder()
                      .WriteCode(MessageCode.Peer.FolderContentsResponse)
                      .WriteBytes(new byte[] { 0x0, 0x1, 0x2, 0x3 })
                      .Build();

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

            Assert.NotNull(ex);
            Assert.IsType <MessageCompressionException>(ex);
            Assert.IsType <ZStreamException>(ex.InnerException);
        }
Example #6
0
        public void Parse_Handles_A_Complete_Response(int token)
        {
            var dir = GetRandomDirectory(2);

            var builder = new MessageBuilder()
                          .WriteCode(MessageCode.Peer.FolderContentsResponse)
                          .WriteInteger(token)
                          .WriteString(dir.DirectoryName)
                          .WriteInteger(1);

            BuildDirectory(builder, dir);

            var msg = builder
                      .Compress()
                      .Build();

            FolderContentsResponse r = default;

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

            Assert.Null(ex);

            var d = r.Directory;

            Assert.Equal(dir.DirectoryName, d.DirectoryName);
            Assert.Equal(dir.FileCount, d.FileCount);

            var files    = d.Files.ToList();
            var msgFiles = d.Files.ToList();

            for (int j = 0; j < d.FileCount; j++)
            {
                Assert.Equal(files[j].Code, msgFiles[j].Code);
                Assert.Equal(files[j].Filename, msgFiles[j].Filename);
                Assert.Equal(files[j].Size, msgFiles[j].Size);
                Assert.Equal(files[j].Extension, msgFiles[j].Extension);
                Assert.Equal(files[j].AttributeCount, msgFiles[j].AttributeCount);

                var attr    = files[j].Attributes.ToList();
                var msgAttr = files[j].Attributes.ToList();

                for (int k = 0; k < msgFiles[j].AttributeCount; k++)
                {
                    Assert.Equal(attr[k].Type, msgAttr[k].Type);
                    Assert.Equal(attr[k].Value, msgAttr[k].Value);
                }
            }
        }
Example #7
0
        public void Parse_Throws_MessageReadException_On_Missing_Data(int token, string dirname)
        {
            var msg = new MessageBuilder()
                      .WriteCode(MessageCode.Peer.FolderContentsResponse)
                      .WriteInteger(token)
                      .WriteString(dirname)
                      .WriteInteger(1)
                      .WriteString(dirname)
                      .Compress() // count is missing
                      .Build();

            FolderContentsResponse r = default;
            var ex = Record.Exception(() => r = FolderContentsResponse.FromByteArray(msg));

            Assert.NotNull(ex);
            Assert.IsType <MessageReadException>(ex);
        }
Example #8
0
        public void ToByteArray_Returns_Expected_Data(int token, string dirname)
        {
            var list = new List <File>()
            {
                new File(1, "1", 1, ".1", 1, new List <FileAttribute>()
                {
                    new FileAttribute(FileAttributeType.BitDepth, 1)
                }),
                new File(2, "2", 2, ".2", 1, new List <FileAttribute>()
                {
                    new FileAttribute(FileAttributeType.BitRate, 2)
                }),
            };

            var dir = new Directory(dirname, 2, list);

            var r = new FolderContentsResponse(token, dir);

            var bytes = r.ToByteArray();

            var m = new MessageReader <MessageCode.Peer>(bytes);

            m.Decompress();

            Assert.Equal(MessageCode.Peer.FolderContentsResponse, m.ReadCode());
            Assert.Equal(token, m.ReadInteger());

            Assert.Equal(dirname, m.ReadString());
            Assert.Equal(1, m.ReadInteger());
            Assert.Equal(dirname, m.ReadString());
            Assert.Equal(dir.FileCount, m.ReadInteger());

            // file 1
            Assert.Equal(1, m.ReadByte());      // code
            Assert.Equal("1", m.ReadString());  // name
            Assert.Equal(1, m.ReadLong());      // length
            Assert.Equal(".1", m.ReadString()); // ext
            Assert.Equal(1, m.ReadInteger());   // attribute count

            Assert.Equal(FileAttributeType.BitDepth, (FileAttributeType)m.ReadInteger());
            Assert.Equal(1, m.ReadInteger());
        }
Example #9
0
        public void Parse_Returns_Empty_Response_Given_Empty_Message(int token, string dirname)
        {
            var msg = new MessageBuilder()
                      .WriteCode(MessageCode.Peer.FolderContentsResponse)
                      .WriteInteger(token)
                      .WriteString(dirname)
                      .WriteInteger(1)
                      .WriteString(dirname)
                      .WriteInteger(0)
                      .Compress()
                      .Build();

            FolderContentsResponse r = default;

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

            Assert.Null(ex);
            Assert.Equal(token, r.Token);
            Assert.Equal(dirname, r.Directory.DirectoryName);
            Assert.Equal(0, r.Directory.FileCount);
        }
Example #10
0
        public void Parse_Handles_Files_With_No_Attributes(int token, string dirname)
        {
            var msg = new MessageBuilder()
                      .WriteCode(MessageCode.Peer.FolderContentsResponse)
                      .WriteInteger(token)
                      .WriteString(dirname)
                      .WriteInteger(1)
                      .WriteString(dirname)
                      .WriteInteger(1)    // first directory file count
                      .WriteByte(0x0)     // file code
                      .WriteString("foo") // name
                      .WriteLong(12)      // size
                      .WriteString("bar") // extension
                      .WriteInteger(0)    // attribute count
                      .Compress()
                      .Build();

            FolderContentsResponse r = default;

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

            Assert.Null(ex);

            Assert.Equal(dirname, r.Directory.DirectoryName);
            Assert.Equal(1, r.Directory.FileCount);
            Assert.Single(r.Directory.Files);

            var f = r.Directory.Files.ToList();

            Assert.Equal(0x0, f[0].Code);
            Assert.Equal("foo", f[0].Filename);
            Assert.Equal(12, f[0].Size);
            Assert.Equal("bar", f[0].Extension);
            Assert.Equal(0, f[0].AttributeCount);
            Assert.Empty(f[0].Attributes);
        }
Example #11
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.Peer>(message).ReadCode();

            Diagnostic.Debug($"Peer message received: {code} from {connection.Username} ({connection.IPEndPoint}) (id: {connection.Id})");

            try
            {
                switch (code)
                {
                case MessageCode.Peer.SearchResponse:
                    var searchResponse = SearchResponseFactory.FromByteArray(message);

                    if (SoulseekClient.Searches.TryGetValue(searchResponse.Token, out var search))
                    {
                        search.TryAddResponse(searchResponse);
                    }

                    break;

                case MessageCode.Peer.BrowseResponse:
                    var browseWaitKey = new WaitKey(MessageCode.Peer.BrowseResponse, connection.Username);

                    try
                    {
                        SoulseekClient.Waiter.Complete(browseWaitKey, BrowseResponseFactory.FromByteArray(message));
                    }
                    catch (Exception ex)
                    {
                        SoulseekClient.Waiter.Throw(browseWaitKey, new MessageReadException("The peer returned an invalid browse response", ex));
                        throw;
                    }

                    break;

                case MessageCode.Peer.InfoRequest:
                    UserInfo outgoingInfo;

                    try
                    {
                        outgoingInfo = await SoulseekClient.Options
                                       .UserInfoResponseResolver(connection.Username, connection.IPEndPoint).ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        outgoingInfo = await new SoulseekClientOptions()
                                       .UserInfoResponseResolver(connection.Username, connection.IPEndPoint).ConfigureAwait(false);

                        Diagnostic.Warning($"Failed to resolve user info response: {ex.Message}", ex);
                    }

                    await connection.WriteAsync(outgoingInfo.ToByteArray()).ConfigureAwait(false);

                    break;

                case MessageCode.Peer.BrowseRequest:
                    BrowseResponse browseResponse;

                    try
                    {
                        browseResponse = await SoulseekClient.Options.BrowseResponseResolver(connection.Username, connection.IPEndPoint).ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        browseResponse = await new SoulseekClientOptions()
                                         .BrowseResponseResolver(connection.Username, connection.IPEndPoint).ConfigureAwait(false);

                        Diagnostic.Warning($"Failed to resolve browse response: {ex.Message}", ex);
                    }

                    await connection.WriteAsync(browseResponse.ToByteArray()).ConfigureAwait(false);

                    break;

                case MessageCode.Peer.FolderContentsRequest:
                    var       folderContentsRequest  = FolderContentsRequest.FromByteArray(message);
                    Directory outgoingFolderContents = null;

                    try
                    {
                        outgoingFolderContents = await SoulseekClient.Options.DirectoryContentsResponseResolver(
                            connection.Username,
                            connection.IPEndPoint,
                            folderContentsRequest.Token,
                            folderContentsRequest.DirectoryName).ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        Diagnostic.Warning($"Failed to resolve directory contents response: {ex.Message}", ex);
                    }

                    if (outgoingFolderContents != null)
                    {
                        var folderContentsResponseMessage = new FolderContentsResponse(folderContentsRequest.Token, outgoingFolderContents);

                        await connection.WriteAsync(folderContentsResponseMessage.ToByteArray()).ConfigureAwait(false);
                    }

                    break;

                case MessageCode.Peer.FolderContentsResponse:
                    var folderContentsResponse = FolderContentsResponse.FromByteArray(message);
                    SoulseekClient.Waiter.Complete(new WaitKey(MessageCode.Peer.FolderContentsResponse, connection.Username, folderContentsResponse.Token), folderContentsResponse.Directory);
                    break;

                case MessageCode.Peer.InfoResponse:
                    var incomingInfo = UserInfoResponseFactory.FromByteArray(message);
                    SoulseekClient.Waiter.Complete(new WaitKey(MessageCode.Peer.InfoResponse, connection.Username), incomingInfo);
                    break;

                case MessageCode.Peer.TransferResponse:
                    var transferResponse = TransferResponse.FromByteArray(message);
                    SoulseekClient.Waiter.Complete(new WaitKey(MessageCode.Peer.TransferResponse, connection.Username, transferResponse.Token), transferResponse);
                    break;

                case MessageCode.Peer.QueueDownload:
                    var queueDownloadRequest = EnqueueDownloadRequest.FromByteArray(message);

                    var(queueRejected, queueRejectionMessage) =
                        await TryEnqueueDownloadAsync(connection.Username, connection.IPEndPoint, queueDownloadRequest.Filename).ConfigureAwait(false);

                    if (queueRejected)
                    {
                        await connection.WriteAsync(new EnqueueFailedResponse(queueDownloadRequest.Filename, queueRejectionMessage).ToByteArray()).ConfigureAwait(false);
                    }
                    else
                    {
                        await TrySendPlaceInQueueAsync(connection, queueDownloadRequest.Filename).ConfigureAwait(false);
                    }

                    break;

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

                    if (transferRequest.Direction == TransferDirection.Upload)
                    {
                        if (!SoulseekClient.Downloads.IsEmpty && SoulseekClient.Downloads.Values.Any(d => d.Username == connection.Username && d.Filename == transferRequest.Filename))
                        {
                            SoulseekClient.Waiter.Complete(new WaitKey(MessageCode.Peer.TransferRequest, connection.Username, transferRequest.Filename), transferRequest);
                        }
                        else
                        {
                            // reject the transfer with an empty reason.  it was probably cancelled, but we can't be sure.
                            await connection.WriteAsync(new TransferResponse(transferRequest.Token, string.Empty).ToByteArray()).ConfigureAwait(false);
                        }
                    }
                    else
                    {
                        var(transferRejected, transferRejectionMessage) = await TryEnqueueDownloadAsync(connection.Username, connection.IPEndPoint, transferRequest.Filename).ConfigureAwait(false);

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

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

                    break;

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

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

                case MessageCode.Peer.PlaceInQueueRequest:
                    var placeInQueueRequest = PlaceInQueueRequest.FromByteArray(message);
                    await TrySendPlaceInQueueAsync(connection, placeInQueueRequest.Filename).ConfigureAwait(false);

                    break;

                case MessageCode.Peer.UploadFailed:
                    var uploadFailedResponse = UploadFailed.FromByteArray(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));
                    }

                    Diagnostic.Debug(msg);
                    break;

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