private async Task HandleRpcAsync()

in src/StreamJsonRpc/JsonRpc.cs [2417:2574]


        private async Task HandleRpcAsync(JsonRpcMessage rpc)
        {
            Requires.NotNull(rpc, nameof(rpc));
            OutstandingCallData? data = null;
            try
            {
                if (rpc is JsonRpcRequest request)
                {
                    if (this.TraceSource.Switch.ShouldTrace(TraceEventType.Information))
                    {
                        if (request.IsResponseExpected)
                        {
                            this.TraceSource.TraceEvent(TraceEventType.Information, (int)TraceEvents.RequestReceived, "Received request \"{0}\" for method \"{1}\".", request.RequestId, request.Method);
                        }
                        else
                        {
                            this.TraceSource.TraceEvent(TraceEventType.Information, (int)TraceEvents.RequestReceived, "Received notification for method \"{0}\".", request.Method);
                        }
                    }

                    // We can't accept a request that requires a response if we can't write.
                    Verify.Operation(!request.IsResponseExpected || this.MessageHandler.CanWrite, Resources.StreamMustBeWriteable);

                    JsonRpcMessage result;
                    lock (this.syncObject)
                    {
                        if (this.requestsInDispatchCount++ == 0)
                        {
                            this.dispatchCompletionSource.Reset();
                        }
                    }

                    try
                    {
                        result = await this.DispatchIncomingRequestAsync(request).ConfigureAwait(false);
                    }
                    finally
                    {
                        lock (this.syncObject)
                        {
                            if (--this.requestsInDispatchCount == 0)
                            {
                                this.dispatchCompletionSource.Set();
                            }
                        }
                    }

                    if (request.IsResponseExpected && !this.IsDisposed)
                    {
                        bool responseSent = false;
                        try
                        {
                            await this.SendAsync(result, this.DisconnectedToken).ConfigureAwait(false);
                            responseSent = true;
                        }
                        catch (OperationCanceledException) when (this.DisconnectedToken.IsCancellationRequested)
                        {
                        }
                        catch (ObjectDisposedException) when (this.IsDisposed)
                        {
                        }
#pragma warning disable CA1031 // Do not catch general exception types
                        catch (Exception exception)
#pragma warning restore CA1031 // Do not catch general exception types
                        {
                            // Some exceptions are fatal. If we aren't already disconnected, try sending an apology to the client.
                            if (!this.DisconnectedToken.IsCancellationRequested)
                            {
                                result = this.CreateErrorForResponseTransmissionFailure(request, exception);
                                await this.SendAsync(result, this.DisconnectedToken).ConfigureAwait(false);
                                responseSent = true;
                            }
                        }

                        if (responseSent)
                        {
                            this.OnResponseSent(result);
                        }
                    }
                }
                else if (rpc is IJsonRpcMessageWithId resultOrError)
                {
                    this.OnResponseReceived(rpc);
                    JsonRpcResult? result = resultOrError as JsonRpcResult;
                    JsonRpcError? error = resultOrError as JsonRpcError;

                    lock (this.dispatcherMapLock)
                    {
                        if (this.resultDispatcherMap.TryGetValue(resultOrError.RequestId, out data))
                        {
                            this.resultDispatcherMap.Remove(resultOrError.RequestId);
                        }
                    }

                    if (this.TraceSource.Switch.ShouldTrace(TraceEventType.Information))
                    {
                        if (result is not null)
                        {
                            this.TraceSource.TraceEvent(TraceEventType.Information, (int)TraceEvents.ReceivedResult, "Received result for request \"{0}\".", result.RequestId);
                        }
                        else if (error?.Error is object)
                        {
                            this.TraceSource.TraceEvent(TraceEventType.Warning, (int)TraceEvents.ReceivedError, "Received error response for request {0}: {1} \"{2}\": ", error.RequestId, error.Error.Code, error.Error.Message);
                        }
                    }

                    if (data is object)
                    {
                        if (data.ExpectedResultType is not null && rpc is JsonRpcResult resultMessage)
                        {
                            resultMessage.SetExpectedResultType(data.ExpectedResultType);
                        }
                        else if (rpc is JsonRpcError errorMessage && errorMessage.Error is not null)
                        {
                            Type? errorType = this.GetErrorDetailsDataType(errorMessage);
                            if (errorType is not null)
                            {
                                errorMessage.Error.SetExpectedDataType(errorType);
                            }
                        }

                        // Complete the caller's request with the response asynchronously so it doesn't delay handling of other JsonRpc messages.
                        await TaskScheduler.Default.SwitchTo(alwaysYield: true);
                        data.CompletionHandler(rpc);
                        data = null; // avoid invoking again if we throw later
                    }
                    else
                    {
                        // Unexpected "response" to no request we have a record of. Raise disconnected event.
                        this.OnJsonRpcDisconnected(new JsonRpcDisconnectedEventArgs(
                            Resources.UnexpectedResponseWithNoMatchingRequest,
                            DisconnectedReason.RemoteProtocolViolation));
                    }
                }
                else
                {
                    // Not a request or result/error. Raise disconnected event.
                    this.OnJsonRpcDisconnected(new JsonRpcDisconnectedEventArgs(
                        Resources.UnrecognizedIncomingJsonRpc,
                        DisconnectedReason.ParseError));
                }
            }
#pragma warning disable CA1031 // Do not catch general exception types
            catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
            {
                var eventArgs = new JsonRpcDisconnectedEventArgs(
                    string.Format(CultureInfo.CurrentCulture, Resources.UnexpectedErrorProcessingJsonRpc, ex.Message),
                    DisconnectedReason.ParseError,
                    ex);

                // Fatal error. Raise disconnected event.
                this.OnJsonRpcDisconnected(eventArgs);

                // If we extracted this callback from the collection already, take care to complete it to avoid hanging our client.
                data?.CompletionHandler(null);
            }
        }