private async Task HandleRpcAsync(string json) { JsonRpcMessage rpc; try { rpc = JsonRpcMessage.FromJson(json, this.MessageJsonDeserializerSettings); } catch (JsonException exception) { var e = new JsonRpcDisconnectedEventArgs(string.Format(CultureInfo.CurrentCulture, Resources.FailureDeserializingJsonRpc, json, exception.Message), DisconnectedReason.ParseError, json, exception); // Fatal error. Raise disconnected event. this.OnJsonRpcDisconnected(e); return; } if (rpc.IsRequest) { // We can't accept a request that requires a response if we can't write. Verify.Operation(rpc.IsNotification || this.MessageHandler.CanWrite, Resources.StreamMustBeWriteable); if (rpc.IsNotification && rpc.Method == CancelRequestSpecialMethod) { this.HandleCancellationNotification(rpc); return; } JsonRpcMessage result = await this.DispatchIncomingRequestAsync(rpc, this.JsonSerializer).ConfigureAwait(false); if (!rpc.IsNotification) { try { await this.TransmitAsync(result, this.disposeCts.Token).ConfigureAwait(false); } catch (OperationCanceledException) { } catch (ObjectDisposedException) { } catch (Exception exception) { var e = new JsonRpcDisconnectedEventArgs(string.Format(CultureInfo.CurrentCulture, Resources.ErrorWritingJsonRpcResult, exception.GetType().Name, exception.Message), DisconnectedReason.StreamError, exception); // Fatal error. Raise disconnected event. this.OnJsonRpcDisconnected(e); } } return; } if (rpc.IsResponse) { OutstandingCallData data = null; lock (this.dispatcherMapLock) { int id = (int)rpc.Id; if (this.resultDispatcherMap.TryGetValue(id, out data)) { this.resultDispatcherMap.Remove(id); } } if (data != null) { data.CompletionHandler(rpc); } return; } // Not a request or return. Raise disconnected event. this.OnJsonRpcDisconnected(new JsonRpcDisconnectedEventArgs( string.Format(CultureInfo.CurrentCulture, Resources.UnrecognizedIncomingJsonRpc, json), DisconnectedReason.ParseError, json)); }
/// <summary> /// Invokes the specified RPC method /// </summary> /// <typeparam name="ReturnType">RPC method return type</typeparam> /// <param name="id">An identifier established by the Client that MUST contain a String, Number, or NULL value if included. /// If it is not included it is assumed to be a notification.</param> /// <param name="targetName">Name of the method to invoke.</param> /// <param name="arguments">Arguments to pass to the invoked method. If null, no arguments are passed.</param> /// <param name="cancellationToken">The token whose cancellation should signal the server to stop processing this request.</param> /// <returns>A task whose result is the deserialized response from the JSON-RPC server.</returns> protected virtual async Task <ReturnType> InvokeCoreAsync <ReturnType>(int?id, string targetName, IReadOnlyList <object> arguments, CancellationToken cancellationToken) { Requires.NotNullOrEmpty(targetName, nameof(targetName)); Verify.NotDisposed(this); cancellationToken.ThrowIfCancellationRequested(); arguments = arguments ?? EmptyObjectArray; JsonRpcMessage request = JsonRpcMessage.CreateRequest(id, targetName, arguments, this.JsonSerializer); using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, this.disposeCts.Token)) { if (id == null) { await this.TransmitAsync(request, cts.Token).ConfigureAwait(false); return(default(ReturnType)); } Verify.Operation(this.readLinesTask != null, Resources.InvalidBeforeListenHasStarted); var tcs = new TaskCompletionSource <ReturnType>(); Action <JsonRpcMessage> dispatcher = (response) => { lock (this.dispatcherMapLock) { this.resultDispatcherMap.Remove(id.Value); } try { if (response == null) { tcs.TrySetCanceled(); } else if (response.IsError) { tcs.TrySetException(CreateExceptionFromRpcError(response, targetName)); } else { tcs.TrySetResult(response.GetResult <ReturnType>(this.JsonSerializer)); } } catch (Exception ex) { tcs.TrySetException(ex); } }; var callData = new OutstandingCallData(tcs, dispatcher); lock (this.dispatcherMapLock) { this.resultDispatcherMap.Add(id.Value, callData); } await this.TransmitAsync(request, cts.Token).ConfigureAwait(false); // Arrange for sending a cancellation message if canceled while we're waiting for a response. using (cancellationToken.Register(this.cancelPendingOutboundRequestAction, id.Value, useSynchronizationContext: false)) { // This task will be completed when the Response object comes back from the other end of the pipe return(await tcs.Task.ConfigureAwait(false)); } } }