-
Notifications
You must be signed in to change notification settings - Fork 152
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
Conversation
Merge from main master
Merge from master
There was a problem hiding this 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). :)
src/StreamJsonRpc/JsonRpc.cs
Outdated
@@ -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); |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
src/StreamJsonRpc/JsonRpc.cs
Outdated
{ | ||
this.relayRpc = JsonRpc.Attach(relaySendingStream, relayReceivingStream, relayTarget); | ||
|
||
this.relayRpc.relayRpc = this; |
There was a problem hiding this comment.
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)?
src/StreamJsonRpc/JsonRpc.cs
Outdated
protected async Task<TResult> InvokeCoreAsync<TResult>(JsonRpcRequest request, CancellationToken cancellationToken) | ||
{ | ||
long id = default(long); | ||
if (request.Id is long) |
There was a problem hiding this comment.
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.
src/StreamJsonRpc/JsonRpc.cs
Outdated
|
||
protected async Task<TResult> InvokeCoreAsync<TResult>(JsonRpcRequest request, CancellationToken cancellationToken) | ||
{ | ||
long id = default(long); |
There was a problem hiding this comment.
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.
src/StreamJsonRpc/JsonRpc.cs
Outdated
return await this.InvokeCoreAsync<TResult>(request, cancellationToken).ConfigureAwait(false); | ||
} | ||
|
||
protected async Task<TResult> InvokeCoreAsync<TResult>(JsonRpcRequest request, CancellationToken cancellationToken) |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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.
src/StreamJsonRpc/JsonRpc.cs
Outdated
@@ -577,6 +579,13 @@ public T Attach<T>(JsonRpcProxyOptions options) | |||
return proxy; | |||
} | |||
|
|||
public void Relay(Stream relaySendingStream, Stream relayReceivingStream, object relayTarget = null) |
There was a problem hiding this comment.
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.
@@ -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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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.
There was a problem hiding this 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 Report
@@ 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
Continue to review full report at Codecov.
|
There was a problem hiding this 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. :)
…eamjsonrpc into relayStream # Conflicts: # src/StreamJsonRpc/JsonRpc.cs
There was a problem hiding this 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?
Activate GitHub Actions test reporting
Revert "Merge pull request #319 from AArnott/betterTestingLibTemplate"
Implementation according to design review.