Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ability to add remote RPC targets #319

Merged
merged 19 commits into from
Sep 6, 2019

Conversation

tinaschrepfer
Copy link
Member

@tinaschrepfer tinaschrepfer commented Aug 13, 2019

Implementation according to design review.

@tinaschrepfer tinaschrepfer requested a review from AArnott August 13, 2019 20:06
Copy link
Member

@AArnott AArnott left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've nit-picked at a few things, but I can't remember the design discussion we had so it would be helpful it you'd edit the PR description to give the high level objective, as well as xml doc comments to educate users (and reviewers). :)

@@ -577,6 +579,13 @@ public T Attach<T>(JsonRpcProxyOptions options)
return proxy;
}

public void Relay(Stream relaySendingStream, Stream relayReceivingStream, object relayTarget = null)
{
this.relayRpc = JsonRpc.Attach(relaySendingStream, relayReceivingStream, relayTarget);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting the field even if it was set previously sounds like it would be an error. Should we throw InvalidOperationException when it has already been set? (tip: add a test for this).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, I haven't gone back and polished the PR. Just wanted to get a read from you initially to see if this is the right approach.

{
this.relayRpc = JsonRpc.Attach(relaySendingStream, relayReceivingStream, relayTarget);

this.relayRpc.relayRpc = this;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to cause a circular loop between the two JsonRpc instances in which the original one is imperceptible from the one created here. Is that right (and good)?

protected async Task<TResult> InvokeCoreAsync<TResult>(JsonRpcRequest request, CancellationToken cancellationToken)
{
long id = default(long);
if (request.Id is long)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't dereference arguments without checking for null first.


protected async Task<TResult> InvokeCoreAsync<TResult>(JsonRpcRequest request, CancellationToken cancellationToken)
{
long id = default(long);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the event that request.Id is null, this will leave id=0, which is not the same thing as null. Being null implies a notification while 0 is a request that expects a response.
Also, in the general relaying configuration, you can't assume that the request.Id is even a number. It's allowed to be string, and some JSON-RPC servers do that. So if we're relaying messages from non-StreamJsonRpc sources, we'd need to properly handle all legal values for id.

return await this.InvokeCoreAsync<TResult>(request, cancellationToken).ConfigureAwait(false);
}

protected async Task<TResult> InvokeCoreAsync<TResult>(JsonRpcRequest request, CancellationToken cancellationToken)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this overload need to be protected or can it be private?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can probably be private. I originally had a subclass of JsonRpc, but found that was not necessary.

@@ -577,6 +579,13 @@ public T Attach<T>(JsonRpcProxyOptions options)
return proxy;
}

public void Relay(Stream relaySendingStream, Stream relayReceivingStream, object relayTarget = null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add detailed xml doc comments to this method, including a remarks section that explains the scenarios that it is meant to work with.

@tinaschrepfer tinaschrepfer changed the title Relay stream Implement ability to add remote RPC targets Aug 26, 2019
@@ -675,6 +681,18 @@ public void AddLocalRpcTarget(object target, JsonRpcTargetOptions options)
}
}

/// <summary>
/// Adds a remote rpc connection so calls can be forwarded to the remote target if local targets do not handle it.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You support remote being a fallback to local handlers. Do you need or want to support local being a fallback to remote? I'm guessing not. But just calling it out.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Local fallback would always take precedence. For example, if both local target and remote target has the method "Foo()", then the local one would be invoked before we even check for remote.

remoteTarget1.AllowModificationWhileListening = true;

var remoteTarget2 = JsonRpc.Attach(remoteClientStream2, remoteClientStream2, new LocalRelayTarget());
remoteTarget2.AllowModificationWhileListening = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason to be concerned that this is required? Generally, modifying the configuration after listening has started is a bad idea. Tests are sometimes used as samples (especially early on) so it's generally preferable to not use this property in a test unless you're explicitly testing that setting this property to true allows configuration changes.
The preferred pattern for fancy configurations is to use the constructor, add targets, then start listening explicitly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, in the case of LSP, the connection between devenv and LSP extension (via ServiceHub) has to be established first, and then devenv would invoke the ActivateAsync method via that RPC connection to activate the language server. The language server RPC can then be added as a remote target. So we have to allow for modifying the configuration after listening has started.

}

[Fact]
public async Task CanCancelOnRemoteTarget()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing cancellation is a good idea. I wonder if it's worth adding Progress forwarding tests once this merges with @milopezc's changes in #320.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll sync with @milopezc on this.

Copy link
Member

@AArnott AArnott left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few comments as discussed

@codecov-io
Copy link

codecov-io commented Aug 28, 2019

Codecov Report

Merging #319 into master will increase coverage by 0.34%.
The diff coverage is 94.7%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #319      +/-   ##
==========================================
+ Coverage   87.38%   87.72%   +0.34%     
==========================================
  Files          33       33              
  Lines        2164     2192      +28     
  Branches      410      424      +14     
==========================================
+ Hits         1891     1923      +32     
+ Misses        217      214       -3     
+ Partials       56       55       -1
Impacted Files Coverage Δ
src/StreamJsonRpc/Resources.Designer.cs 66.66% <0%> (-3.83%) ⬇️
src/StreamJsonRpc/JsonRpc.cs 92.05% <95.72%> (+0.26%) ⬆️
src/StreamJsonRpc/MessageHandlerBase.cs 97.61% <0%> (+2.38%) ⬆️
src/StreamJsonRpc/Reflection/MethodSignature.cs 66.66% <0%> (+5.55%) ⬆️
...treamJsonRpc/Exceptions/ConnectionLostException.cs 75% <0%> (+25%) ⬆️
src/StreamJsonRpc/Exceptions/RemoteRpcException.cs 66.66% <0%> (+33.33%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 03fc618...4ebebe1. Read the comment docs.

Copy link
Member

@AArnott AArnott left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks much better. All the issues I caught are new. :)

Copy link
Member

@AArnott AArnott left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

Can you add an md file under the doc folder that explains remote targets, and find a good place in our documentation to link to it so that the file is discoverable?

@tinaschrepfer tinaschrepfer merged commit 045c1d1 into microsoft:master Sep 6, 2019
@AArnott AArnott added this to the v2.2 milestone Jun 2, 2020
AArnott added a commit that referenced this pull request Jan 14, 2025
Activate GitHub Actions test reporting
AArnott added a commit that referenced this pull request Jan 14, 2025
AArnott added a commit that referenced this pull request Jan 14, 2025
Revert "Merge pull request #319 from AArnott/betterTestingLibTemplate"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants