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);
}
}