microsoft / microsoft/vs-streamjsonrpc
AddRemoteRpcTarget doesn't work with methods returning IAsyncEnumerable
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 937
- Forks
- 178
- Avg merge
- 3d 1h
- Merged PRs (30d)
- 28
Description
When using AddRemoteRpcTarget to forward messages to another JsonRpc instance, calling methods that return an IAsyncEnumerable fails.
This is because JsonRpc.rpcTargetInfo.TryGetTargetMethod will always return a match for the special methods that are used to implement the async enumerable support.
This can easily be reproduced with the following code:
using Nerdbank.Streams;
using StreamJsonRpc;
using StreamJsonRpc.Protocol;
using System.Diagnostics;
var (clientStream, manInTheMiddleClientStream) = FullDuplexStream.CreatePair();
var (manInTheMiddleServerStream, serverStream) = FullDuplexStream.CreatePair();
JsonRpc clientRpc;
// client
{
var clientFormatter = new JsonMessageFormatter();
var clientHandler = new LengthHeaderMessageHandler(clientStream.UsePipe(), clientFormatter);
clientRpc = new JsonRpc(clientHandler);
clientRpc.TraceSource = new TraceSource("Client", SourceLevels.Verbose);
clientRpc.StartListening();
}
// man in the middle
{
var manInTheMiddleClientFormatter = new JsonMessageFormatter();
var manInTheMiddleClientHandler = new LengthHeaderMessageHandler(manInTheMiddleClientStream.UsePipe(), manInTheMiddleClientFormatter);
var manInTheMiddleClientRpc = new JsonRpc(manInTheMiddleClientHandler);
manInTheMiddleClientRpc.TraceSource = new TraceSource("ManInTheMiddleClient", SourceLevels.Verbose);
var manInTheMiddleServerFormatter = new JsonMessageFormatter();
var manInTheMiddleServerHandler = new LengthHeaderMessageHandler(manInTheMiddleServerStream.UsePipe(), manInTheMiddleServerFormatter);
var manInTheMiddleServerRpc = new JsonRpc(manInTheMiddleServerHandler);
manInTheMiddleServerRpc.TraceSource = new TraceSource("ManInTheMiddlServer", SourceLevels.Verbose);
manInTheMiddleClientRpc.AddRemoteRpcTarget(manInTheMiddleServerRpc);
manInTheMiddleServerRpc.AddRemoteRpcTarget(manInTheMiddleClientRpc);
manInTheMiddleClientRpc.StartListening();
manInTheMiddleServerRpc.StartListening();
}
// server
{
var serverFormatter = new JsonMessageFormatter();
var serverHandler = new LengthHeaderMessageHandler(serverStream.UsePipe(), serverFormatter);
var mserverRpc = new JsonRpc(serverHandler, new Server());
mserverRpc.TraceSource = new TraceSource("Server", SourceLevels.Verbose);
mserverRpc.StartListening();
}
// This one works
var result = await clientRpc.InvokeAsync<int>("DoSomethingAsync", 5);
// This doesn't work because JsonRpc.rpcTargetInfo.TryGetTargetMethod has a few hardcoded methods that are never passed to remote targets.
await foreach (var number in await clientRpc.InvokeAsync<IAsyncEnumerable<int>>("EnumerateAsync", 5))
{
Console.WriteLine(number);
}
Console.ReadKey();
public interface IServer
{
Task<int> DoSomethingAsync(int val);
IAsyncEnumerable<int> EnumerateAsync(int val);
}
public class Server : IServer
{
public Task<int> DoSomethingAsync(int val)
{
return Task.FromResult(val);
}
public async IAsyncEnumerable<int> EnumerateAsync(int val)
{
for (int i = 0; i < val; i++)
{
await Task.Yield();
yield return i;
}
}
}
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in JsonRpc.cs at rpcTargetInfo.TryGetTargetMethod, linked from the issue, and compare how normal calls and the special IAsyncEnumerable methods are resolved. Run the supplied client, middleman, and server reproduction, then verify that EnumerateAsync is forwarded through AddRemoteRpcTarget and yields all expected values.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- api
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100