Exemplo n.º 1
0
        public void Instantiates_With_The_Given_Data(string filename)
        {
            QueueDownloadRequest response = null;

            var ex = Record.Exception(() => response = new QueueDownloadRequest(filename));

            Assert.Null(ex);

            Assert.Equal(filename, response.Filename);
        }
Exemplo n.º 2
0
        public void Parse_Returns_Expected_Data(string filename)
        {
            var msg = new MessageBuilder()
                      .WriteCode(MessageCode.Peer.QueueDownload)
                      .WriteString(filename)
                      .Build();

            var response = QueueDownloadRequest.FromByteArray(msg);

            Assert.Equal(filename, response.Filename);
        }
Exemplo n.º 3
0
        public void Parse_Throws_MessageReadException_On_Missing_Data()
        {
            var msg = new MessageBuilder()
                      .WriteCode(MessageCode.Peer.QueueDownload)
                      .Build();

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

            Assert.NotNull(ex);
            Assert.IsType <MessageReadException>(ex);
        }
Exemplo n.º 4
0
        public void ToByteArray_Returns_Expected_Data(string filename)
        {
            var a   = new QueueDownloadRequest(filename);
            var msg = a.ToByteArray();

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

            Assert.Equal(MessageCode.Peer.QueueDownload, code);

            // length + code + filename len + filename
            Assert.Equal(4 + 4 + 4 + filename.Length, msg.Length);
            Assert.Equal(filename, reader.ReadString());
        }
Exemplo n.º 5
0
        public void Does_Not_Write_PlaceInQueueResponse_On_Successful_Enqueue_Via_QueueDownload_If_PlaceInQueueResponse_Is_Null(string username, IPEndPoint endpoint, string filename)
        {
            var options = new SoulseekClientOptions(
                enqueueDownloadAction: (u, f, i) => Task.CompletedTask,
                placeInQueueResponseResolver: (u, f, i) => Task.FromResult <int?>(null));

            var(handler, mocks) = GetFixture(username, endpoint, options);

            var message = new QueueDownloadRequest(filename).ToByteArray();

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

            mocks.PeerConnection
            .Verify(m => m.WriteAsync(It.IsAny <IOutgoingMessage>(), It.IsAny <CancellationToken?>()), Times.Never);
        }
Exemplo n.º 6
0
        public void Creates_Diagnostic_On_Failed_QueueDownload_Invocation_Via_QueueDownload(string username, IPEndPoint endpoint, string filename)
        {
            var           options  = new SoulseekClientOptions(enqueueDownloadAction: (u, f, i) => { throw new Exception(); });
            List <string> messages = new List <string>();

            var(handler, mocks) = GetFixture(username, endpoint, options);

            mocks.Diagnostic.Setup(m => m.Warning(It.IsAny <string>(), It.IsAny <Exception>()))
            .Callback <string, Exception>((msg, ex) => messages.Add(msg));

            var message = new QueueDownloadRequest(filename).ToByteArray();

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

            Assert.Contains(messages, m => m.IndexOf("Failed to invoke QueueDownload action", StringComparison.InvariantCultureIgnoreCase) > -1);
        }
Exemplo n.º 7
0
        public async Task <IActionResult> Enqueue([FromRoute, Required] string username, [FromBody] QueueDownloadRequest request)
        {
            try
            {
                var waitUntilEnqueue = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);
                var stream           = GetLocalFileStream(request.Filename, OutputDirectory);

                var cts = new CancellationTokenSource();

                var downloadTask = Client.DownloadAsync(username, request.Filename, () => stream, request.Size, 0, request.Token, new TransferOptions(disposeOutputStreamOnCompletion: true, stateChanged: (e) =>
                {
                    Tracker.AddOrUpdate(e, cts);

                    if (e.Transfer.State.HasFlag(TransferStates.Queued) || e.Transfer.State == TransferStates.Initializing)
                    {
                        waitUntilEnqueue.TrySetResult(true);
                    }
                }, progressUpdated: (e) => Tracker.AddOrUpdate(e, cts)), cts.Token);

                // wait until either the waitUntilEnqueue task completes because the download was successfully queued, or the
                // downloadTask throws due to an error prior to successfully queueing.
                var task = await Task.WhenAny(waitUntilEnqueue.Task, downloadTask);

                if (task == downloadTask && downloadTask.Exception is AggregateException)
                {
                    var rejected = downloadTask.Exception?.InnerExceptions.Where(e => e is TransferRejectedException) ?? Enumerable.Empty <Exception>();

                    if (rejected.Any())
                    {
                        return(StatusCode(403, rejected.First().Message));
                    }

                    return(StatusCode(500, downloadTask.Exception.Message));
                }

                // if it didn't throw, just return ok. the download will continue waiting in the background.
                return(StatusCode(201));
            }
            catch (Exception ex)
            {
                return(StatusCode(500, ex.Message));
            }
        }
        /// <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
                                       .UserInfoResolver(connection.Username, connection.IPEndPoint).ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        outgoingInfo = await new SoulseekClientOptions()
                                       .UserInfoResolver(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.SearchRequest:
                    var searchRequest = PeerSearchRequest.FromByteArray(message);

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

                    try
                    {
                        var peerSearchResponse = await SoulseekClient.Options.SearchResponseResolver(connection.Username, searchRequest.Token, SearchQuery.FromText(searchRequest.Query)).ConfigureAwait(false);

                        if (peerSearchResponse != null && peerSearchResponse.FileCount + peerSearchResponse.LockedFileCount > 0)
                        {
                            await connection.WriteAsync(peerSearchResponse.ToByteArray()).ConfigureAwait(false);
                        }
                    }
                    catch (Exception ex)
                    {
                        Diagnostic.Warning($"Error resolving search response for query '{searchRequest.Query}' requested by {connection.Username} with token {searchRequest.Token}: {ex.Message}", ex);
                    }

                    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.DirectoryContentsResolver(
                            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).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 = QueueDownloadRequest.FromByteArray(message);

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

                    if (queueRejected)
                    {
                        await connection.WriteAsync(new UploadDenied(queueDownloadRequest.Filename, queueRejectionMessage)).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.DownloadDictionary.IsEmpty && SoulseekClient.DownloadDictionary.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.
                            Diagnostic.Debug($"Rejecting unknown upload from {connection.Username} for {transferRequest.Filename} with token {transferRequest.Token}");
                            await connection.WriteAsync(new TransferResponse(transferRequest.Token, "Cancelled")).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)).ConfigureAwait(false);

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

                    break;

                case MessageCode.Peer.UploadDenied:
                    var uploadDeniedResponse = UploadDenied.FromByteArray(message);

                    Diagnostic.Debug($"Download of {uploadDeniedResponse.Filename} from {connection.Username} was denied: {uploadDeniedResponse.Message}");
                    SoulseekClient.Waiter.Throw(new WaitKey(MessageCode.Peer.TransferRequest, connection.Username, uploadDeniedResponse.Filename), new TransferRejectedException(uploadDeniedResponse.Message));

                    DownloadDenied?.Invoke(this, new DownloadDeniedEventArgs(connection.Username, uploadDeniedResponse.Filename, uploadDeniedResponse.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.DownloadDictionary.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);

                    DownloadFailed?.Invoke(this, new DownloadFailedEventArgs(connection.Username, uploadFailedResponse.Filename));
                    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);
            }
        }
Exemplo n.º 9
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);
            }
        }
Exemplo n.º 10
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.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.FromByteArray(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.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:
                    var outgoingInfo = await new ClientOptions()
                                       .UserInfoResponseResolver(connection.Username, connection.IPAddress, connection.Port).ConfigureAwait(false);

                    try
                    {
                        outgoingInfo = await SoulseekClient.Options
                                       .UserInfoResponseResolver(connection.Username, connection.IPAddress, connection.Port).ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        Diagnostic.Warning($"Failed to resolve UserInfoResponse: {ex.Message}", ex);
                    }

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

                    break;

                case MessageCode.Peer.BrowseRequest:
                    var browseResponse = await new ClientOptions()
                                         .BrowseResponseResolver(connection.Username, connection.IPAddress, connection.Port).ConfigureAwait(false);

                    try
                    {
                        browseResponse = await SoulseekClient.Options.BrowseResponseResolver(connection.Username, connection.IPAddress, connection.Port).ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        Diagnostic.Warning($"Failed to resolve BrowseResponse: {ex.Message}", ex);
                    }

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

                    break;

                case MessageCode.Peer.InfoResponse:
                    var incomingInfo = UserInfoResponse.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 = QueueDownloadRequest.FromByteArray(message);

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

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

                    break;

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

                    if (transferRequest.Direction == TransferDirection.Upload)
                    {
                        SoulseekClient.Waiter.Complete(new WaitKey(MessageCode.Peer.TransferRequest, connection.Username, transferRequest.Filename), transferRequest);
                    }
                    else
                    {
                        var(transferRejected, transferRejectionMessage) = await TryEnqueueDownloadAsync(connection.Username, connection.IPAddress, connection.Port, transferRequest.Filename).ConfigureAwait(false);

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

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

                    break;

                case MessageCode.Peer.QueueFailed:
                    var queueFailedResponse = QueueFailedResponse.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.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));
                        SoulseekClient.Waiter.Throw(download.WaitKey, new TransferException(msg));
                    }

                    Diagnostic.Debug(msg);
                    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);
            }
        }