public void Parse_Returns_Expected_Data(string username, int token, byte freeUploadSlots, int uploadSpeed, long queueLength) { var msg = new MessageBuilder() .Code(MessageCode.PeerSearchResponse) .WriteString(username) .WriteInteger(token) .WriteInteger(1) // file count .WriteByte(0x2) // code .WriteString("filename") // filename .WriteLong(3) // size .WriteString("ext") // extension .WriteInteger(1) // attribute count .WriteInteger((int)FileAttributeType.BitDepth) // attribute[0].type .WriteInteger(4) // attribute[0].value .WriteByte(freeUploadSlots) .WriteInteger(uploadSpeed) .WriteLong(queueLength) .WriteBytes(new byte[4]) // unknown 4 bytes .Compress() .Build(); var r = SearchResponseSlim.Parse(msg); Assert.Equal(username, r.Username); Assert.Equal(token, r.Token); Assert.Equal(1, r.FileCount); Assert.Equal(freeUploadSlots, r.FreeUploadSlots); Assert.Equal(uploadSpeed, r.UploadSpeed); Assert.Equal(queueLength, r.QueueLength); // assure the messagereader has been rewound to the start of the file list // position is equal to 4 byte username length + username + 4 byte token + 4 byte file count Assert.Equal(4 + username.Length + 4 + 4, r.MessageReader.Position); }
public void Parse_Throws_MessageException_On_Code_Mismatch() { var msg = new MessageBuilder() .Code(MessageCode.PeerBrowseRequest) .Build(); var ex = Record.Exception(() => SearchResponseSlim.Parse(msg)); Assert.NotNull(ex); Assert.IsType <MessageException>(ex); }
public void Parse_Throws_MessageReadException_On_Missing_Data() { var msg = new MessageBuilder() .Code(MessageCode.PeerSearchResponse) .WriteString("foo") .Compress() .Build(); var ex = Record.Exception(() => SearchResponseSlim.Parse(msg)); Assert.NotNull(ex); Assert.IsType <MessageReadException>(ex); }
public void Parse_Throws_MessageCompressionException_On_Uncompressed_Payload() { var msg = new MessageBuilder() .Code(MessageCode.PeerSearchResponse) .WriteBytes(new byte[] { 0x0, 0x1, 0x2, 0x3 }) .Build(); var ex = Record.Exception(() => SearchResponseSlim.Parse(msg)); Assert.NotNull(ex); Assert.IsType <MessageCompressionException>(ex); Assert.IsType <ZStreamException>(ex.InnerException); }
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); } }