/// <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.ServerSearchRequest) { Diagnostic.Debug($"Distributed message received: {code} from {connection.Username} ({connection.IPEndPoint}) (id: {connection.Id})"); } try { switch (code) { // if we are connected to a branch root, we get search requests with code DistributedServerSearchRequest. convert this // message to a normal DistributedSearchRequest before forwarding. not sure if this is correct, but it would match the // observed behavior. these messages may also be forwarded from the server message handler if we haven't connected to // a distributed parent in a timely manner. case MessageCode.Distributed.ServerSearchRequest: var serverSearchRequest = DistributedServerSearchRequest.FromByteArray(message); var forwardedMessage = new DistributedSearchRequest(serverSearchRequest.Username, serverSearchRequest.Token, serverSearchRequest.Query); SoulseekClient.DistributedConnectionManager.BroadcastMessageAsync(forwardedMessage.ToByteArray()).Forget(); await TrySendSearchResults(serverSearchRequest.Username, serverSearchRequest.Token, serverSearchRequest.Query).ConfigureAwait(false); break; // if we are connected to anyone other than a branch root, we should get search requests with code SearchRequest. // forward these requests as is. case MessageCode.Distributed.SearchRequest: var searchRequest = DistributedSearchRequest.FromByteArray(message); SoulseekClient.DistributedConnectionManager.BroadcastMessageAsync(message).Forget(); await TrySendSearchResults(searchRequest.Username, searchRequest.Token, searchRequest.Query).ConfigureAwait(false); break; case MessageCode.Distributed.Ping: Diagnostic.Debug($"PING?"); var pingResponse = new DistributedPingResponse(SoulseekClient.GetNextToken()); await connection.WriteAsync(pingResponse.ToByteArray()).ConfigureAwait(false); Diagnostic.Debug($"PONG!"); break; case MessageCode.Distributed.BranchLevel: var branchLevel = DistributedBranchLevel.FromByteArray(message); if ((connection.Username, connection.IPEndPoint) == SoulseekClient.DistributedConnectionManager.Parent) { SoulseekClient.DistributedConnectionManager.SetBranchLevel(branchLevel.Level); } break; case MessageCode.Distributed.BranchRoot: var branchRoot = DistributedBranchRoot.FromByteArray(message); if ((connection.Username, connection.IPEndPoint) == SoulseekClient.DistributedConnectionManager.Parent) { SoulseekClient.DistributedConnectionManager.SetBranchRoot(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); } }