internal TargetMethod( JsonRpcMessage request, JsonSerializer jsonSerializer, IEnumerable <MethodSignatureAndTarget> candidateMethodTargets) { Requires.NotNull(request, nameof(request)); Requires.NotNull(jsonSerializer, nameof(jsonSerializer)); Requires.NotNull(candidateMethodTargets, nameof(candidateMethodTargets)); this.request = request; var targetMethods = new Dictionary <MethodSignatureAndTarget, object[]>(); foreach (var method in candidateMethodTargets) { this.TryAddMethod(request, targetMethods, method, jsonSerializer); } KeyValuePair <MethodSignatureAndTarget, object[]> methodWithParameters = targetMethods.FirstOrDefault(); if (methodWithParameters.Key.Signature != null) { this.target = methodWithParameters.Key.Target; this.method = methodWithParameters.Key.Signature.MethodInfo; this.parameters = methodWithParameters.Value; this.AcceptsCancellationToken = methodWithParameters.Key.Signature.HasCancellationTokenParameter; } }
/// <summary> /// Reads a distinct and complete message from the transport, waiting for one if necessary. /// </summary> /// <param name="cancellationToken">A token to cancel the read request.</param> /// <returns>The received message, or <c>null</c> if the underlying transport ends before beginning another message.</returns> /// <exception cref="InvalidOperationException">Thrown when <see cref="CanRead"/> returns <c>false</c>.</exception> /// <exception cref="System.IO.EndOfStreamException">Thrown if the transport ends while reading a message.</exception> /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken"/> is canceled before a new message is received.</exception> /// <remarks> /// Implementations may assume this method is never called before any async result /// from a prior call to this method has completed. /// </remarks> public async ValueTask <JsonRpcMessage> ReadAsync(CancellationToken cancellationToken) { Verify.Operation(this.CanRead, "No receiving stream."); cancellationToken.ThrowIfCancellationRequested(); Verify.NotDisposed(this); using (var cts = CancellationTokenSource.CreateLinkedTokenSource(this.DisposalToken, cancellationToken)) { try { JsonRpcMessage result = await this.ReadCoreAsync(cts.Token).ConfigureAwait(false); return(result); } catch (InvalidOperationException ex) when(cancellationToken.IsCancellationRequested) { // PipeReader.ReadAsync can throw InvalidOperationException in a race where PipeReader.Complete() has been // called but we haven't noticed the CancellationToken was canceled yet. throw new OperationCanceledException("Reading failed during cancellation.", ex, cancellationToken); } catch (ObjectDisposedException) { // If already canceled, throw that instead of ObjectDisposedException. cancellationToken.ThrowIfCancellationRequested(); throw; } } }
/// <summary> /// Cancels an individual outbound pending request. /// </summary> /// <param name="state">The ID associated with the request to be canceled.</param> private void CancelPendingOutboundRequest(object state) { Task.Run(async delegate { try { Requires.NotNull(state, nameof(state)); object id = state; if (!this.disposed) { var cancellationMessage = JsonRpcMessage.CreateRequestWithNamedParameters(id: null, method: CancelRequestSpecialMethod, namedParameters: new { id = id }, parameterSerializer: DefaultJsonSerializer); await this.TransmitAsync(cancellationMessage, this.disposeCts.Token).ConfigureAwait(false); } } catch (OperationCanceledException) { } catch (ObjectDisposedException) { } #if DESKTOP catch (Exception ex) { Debug.Fail(ex.Message, ex.ToString()); } #else catch (Exception) { } #endif }); }
/// <inheritdoc/> protected override void Write(JsonRpcMessage content, CancellationToken cancellationToken) { Assumes.NotNull(this.Writer); try { // Write out the actual message content. this.formatter.Serialize(this.contentSequenceBuilder, content); ReadOnlySequence <byte> contentSequence = this.contentSequenceBuilder.AsReadOnlySequence; // Some formatters (e.g. MessagePackFormatter) needs the encoded form in order to produce JSON for tracing. // Other formatters (e.g. JsonMessageFormatter) would prefer to do its own tracing while it still has a JToken. // We only help the formatters that need the byte-encoded form here. The rest can do it themselves. if (this.Formatter is IJsonRpcFormatterTracingCallbacks tracer) { tracer.OnSerializationComplete(content, contentSequence); } // Now go back and fill in the header with the actual content length. Utilities.Write(this.Writer.GetSpan(sizeof(int)), checked ((int)contentSequence.Length)); this.Writer.Advance(sizeof(int)); contentSequence.CopyTo(this.Writer); if (JsonRpcEventSource.Instance.IsEnabled(System.Diagnostics.Tracing.EventLevel.Informational, System.Diagnostics.Tracing.EventKeywords.None)) { JsonRpcEventSource.Instance.TransmissionCompletedSize(contentSequence.Length); } } finally { this.contentSequenceBuilder.Reset(); } }
/// <summary> /// Writes a message to the transport and flushes. /// </summary> /// <param name="content">The message to write.</param> /// <param name="cancellationToken">A token to cancel the write request.</param> /// <returns>A task that represents the asynchronous operation.</returns> /// <exception cref="InvalidOperationException">Thrown when <see cref="CanWrite"/> returns <c>false</c>.</exception> /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken"/> is canceled before message transmission begins.</exception> /// <remarks> /// Implementations should expect this method to be invoked concurrently /// and use a queue to preserve message order as they are transmitted one at a time. /// </remarks> public async ValueTask WriteAsync(JsonRpcMessage content, CancellationToken cancellationToken) { Requires.NotNull(content, nameof(content)); Verify.Operation(this.CanWrite, "No sending stream."); cancellationToken.ThrowIfCancellationRequested(); Verify.NotDisposed(this); using (var cts = CancellationTokenSource.CreateLinkedTokenSource(this.DisposalToken, cancellationToken)) { try { using (await this.sendingSemaphore.EnterAsync(cts.Token).ConfigureAwait(false)) { cts.Token.ThrowIfCancellationRequested(); await this.WriteCoreAsync(content, cts.Token).ConfigureAwait(false); await this.FlushAsync(cts.Token).ConfigureAwait(false); } } catch (ObjectDisposedException) { // If already canceled, throw that instead of ObjectDisposedException. cancellationToken.ThrowIfCancellationRequested(); throw; } } }
private static object[] TryGetParameters(JsonRpcMessage request, MethodSignature method, HashSet <string> errors, JsonSerializer jsonSerializer) { if (!method.IsPublic) { errors.Add(string.Format(CultureInfo.CurrentCulture, Resources.MethodIsNotPublic, method)); return(null); } // The method name must match if (!string.Equals(method.Name, request.Method, StringComparison.Ordinal) && !string.Equals(method.Name, request.Method + ImpliedMethodNameAsyncSuffix, StringComparison.Ordinal)) { errors.Add(string.Format(CultureInfo.CurrentCulture, Resources.MethodNameCaseIsDifferent, method, request.Method)); return(null); } // ref and out parameters aren't supported. if (method.HasOutOrRefParameters) { errors.Add(string.Format(CultureInfo.CurrentCulture, Resources.MethodHasRefOrOutParameters, method)); return(null); } // The number of parameters must fall within required and total parameters. int paramCount = request.ParameterCount; if (paramCount < method.RequiredParamCount || paramCount > method.TotalParamCountExcludingCancellationToken) { string methodParameterCount; if (method.RequiredParamCount == method.TotalParamCountExcludingCancellationToken) { methodParameterCount = method.RequiredParamCount.ToString(CultureInfo.CurrentCulture); } else { methodParameterCount = string.Format(CultureInfo.CurrentCulture, "{0} - {1}", method.RequiredParamCount, method.TotalParamCountExcludingCancellationToken); } errors.Add(string.Format(CultureInfo.CurrentCulture, Resources.MethodParameterCountDoesNotMatch, method, methodParameterCount, request.ParameterCount)); return(null); } // Parameters must be compatible try { return(request.GetParameters(method.Parameters, jsonSerializer)); } catch (Exception exception) { errors.Add(string.Format(CultureInfo.CurrentCulture, Resources.MethodParametersNotCompatible, method, exception.Message)); return(null); } }
/// <inheritdoc/> void IJsonRpcMessageBufferManager.DeserializationComplete(JsonRpcMessage message) { if (message is object && this.bufferedMessage == message) { this.bufferedMessage.DeserializationComplete(message); this.bufferedMessage = null; this.contentSequenceBuilder.Reset(); } }
/// <inheritdoc/> public void Serialize(IBufferWriter <byte> contentBuffer, JsonRpcMessage message) { if (this.compress) { LZ4MessagePackSerializer.Typeless.Serialize(contentBuffer.AsStream(), message); } else { MessagePackSerializer.Typeless.Serialize(contentBuffer.AsStream(), message); } }
/// <inheritdoc/> public void Serialize(IBufferWriter <byte> contentBuffer, JsonRpcMessage message) { if (message is JsonRpcRequest request && request.Arguments != null && request.ArgumentsList == null && !(request.Arguments is IReadOnlyDictionary <string, object>)) { // This request contains named arguments, but not using a standard dictionary. Convert it to a dictionary so that // the parameters can be matched to the method we're invoking. request.Arguments = GetParamsObjectDictionary(request.Arguments); } MessagePackSerializer.Typeless.Serialize(contentBuffer.AsStream(), message, this.options); }
private async Task <JsonRpcMessage> DispatchIncomingRequestAsync(JsonRpcMessage request, JsonSerializer jsonSerializer) { if (this.callbackTarget == null) { string message = string.Format(CultureInfo.CurrentCulture, Resources.DroppingRequestDueToNoTargetObject, request.Method); return(JsonRpcMessage.CreateError(request.Id, JsonRpcErrorCode.NoCallbackObject, message)); } bool ctsAdded = false; try { var targetMethod = new TargetMethod(request, this.callbackTarget, jsonSerializer); if (!targetMethod.IsFound) { return(JsonRpcMessage.CreateError(request.Id, JsonRpcErrorCode.MethodNotFound, targetMethod.LookupErrorMessage)); } var cancellationToken = CancellationToken.None; if (targetMethod.AcceptsCancellationToken && !request.IsNotification) { var cts = new CancellationTokenSource(); cancellationToken = cts.Token; lock (this.dispatcherMapLock) { this.inboundCancellationSources.Add(request.Id, cts); ctsAdded = true; } } object result = targetMethod.Invoke(cancellationToken); if (!(result is Task)) { return(JsonRpcMessage.CreateResult(request.Id, result, this.JsonSerializer)); } return(await((Task)result).ContinueWith(this.handleInvocationTaskResultDelegate, request.Id, TaskScheduler.Default).ConfigureAwait(false)); } catch (Exception ex) { return(CreateError(request.Id, ex)); } finally { if (ctsAdded) { lock (this.dispatcherMapLock) { this.inboundCancellationSources.Remove(request.Id); } } } }
/// <inheritdoc /> protected override async ValueTask <JsonRpcMessage?> ReadCoreAsync(CancellationToken cancellationToken) { #if NETSTANDARD2_1_OR_GREATER ValueWebSocketReceiveResult result; #else WebSocketReceiveResult result; #endif do { #if NETSTANDARD2_1_OR_GREATER Memory <byte> memory = this.contentSequenceBuilder.GetMemory(this.sizeHint); result = await this.WebSocket.ReceiveAsync(memory, cancellationToken).ConfigureAwait(false); this.contentSequenceBuilder.Advance(result.Count); #else ArrayPool <byte> pool = ArrayPool <byte> .Shared; byte[] segment = pool.Rent(this.sizeHint); try { result = await this.WebSocket.ReceiveAsync(new ArraySegment <byte>(segment), cancellationToken).ConfigureAwait(false); this.contentSequenceBuilder.Write(segment.AsSpan(0, result.Count)); } finally { pool.Return(segment); } #endif if (result.MessageType == WebSocketMessageType.Close) { await this.WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closed as requested.", CancellationToken.None).ConfigureAwait(false); return(null); } }while (!result.EndOfMessage); if (this.contentSequenceBuilder.AsReadOnlySequence.Length > 0) { JsonRpcMessage message = this.Formatter.Deserialize(this.contentSequenceBuilder); this.bufferedMessage = message as IJsonRpcMessageBufferManager; if (this.bufferedMessage is null) { this.contentSequenceBuilder.Reset(); } return(message); } else { return(null); } }
public static JsonRpcMessage FromJson(JsonReader reader, JsonSerializerSettings settings) { JsonSerializer serializer = JsonSerializer.Create(settings); JsonRpcMessage result = serializer.Deserialize <JsonRpcMessage>(reader); if (result == null) { throw new JsonException(Resources.JsonRpcCannotBeNull); } return(result); }
private void HandleCancellationNotification(JsonRpcMessage rpc) { Requires.NotNull(rpc, nameof(rpc)); JToken id = rpc.Parameters.SelectToken("id"); CancellationTokenSource cts; lock (this.dispatcherMapLock) { this.inboundCancellationSources.TryGetValue(id, out cts); } cts?.Cancel(); }
/// <inheritdoc /> protected override async ValueTask WriteCoreAsync(JsonRpcMessage content, CancellationToken cancellationToken) { Requires.NotNull(content, nameof(content)); using (var contentSequenceBuilder = new Sequence <byte>()) { WebSocketMessageType messageType = this.Formatter is IJsonRpcMessageTextFormatter ? WebSocketMessageType.Text : WebSocketMessageType.Binary; this.Formatter.Serialize(contentSequenceBuilder, content); cancellationToken.ThrowIfCancellationRequested(); // Some formatters (e.g. MessagePackFormatter) needs the encoded form in order to produce JSON for tracing. // Other formatters (e.g. JsonMessageFormatter) would prefer to do its own tracing while it still has a JToken. // We only help the formatters that need the byte-encoded form here. The rest can do it themselves. if (this.Formatter is IJsonRpcFormatterTracingCallbacks tracer) { tracer.OnSerializationComplete(content, contentSequenceBuilder); } int bytesCopied = 0; ReadOnlySequence <byte> contentSequence = contentSequenceBuilder.AsReadOnlySequence; foreach (ReadOnlyMemory <byte> memory in contentSequence) { bool endOfMessage = bytesCopied + memory.Length == contentSequence.Length; #if NETCOREAPP2_1 await this.WebSocket.SendAsync(memory, messageType, endOfMessage, cancellationToken).ConfigureAwait(false); #else if (MemoryMarshal.TryGetArray(memory, out ArraySegment <byte> segment)) { await this.WebSocket.SendAsync(segment, messageType, endOfMessage, CancellationToken.None).ConfigureAwait(false); } else { byte[] array = ArrayPool <byte> .Shared.Rent(memory.Length); try { memory.CopyTo(array); await this.WebSocket.SendAsync(new ArraySegment <byte>(array, 0, memory.Length), messageType, endOfMessage, CancellationToken.None).ConfigureAwait(false); } finally { ArrayPool <byte> .Shared.Return(array); } } #endif bytesCopied += memory.Length; } } }
/// <inheritdoc/> protected override void Write(JsonRpcMessage content, CancellationToken cancellationToken) { if (this.prefixingWriter == null) { this.prefixingWriter = new PrefixingBufferWriter <byte>(this.Writer, sizeof(int)); } // Write out the actual message content, counting all written bytes. this.formatter.Serialize(this.prefixingWriter, content); // Now go back and fill in the header with the actual content length. Utilities.Write(this.prefixingWriter.Prefix.Span, checked ((int)this.prefixingWriter.Length)); this.prefixingWriter.Commit(); }
private bool TryAddMethod(JsonRpcMessage request, Dictionary <MethodSignatureAndTarget, object[]> targetMethods, MethodSignatureAndTarget method, JsonSerializer jsonSerializer) { Requires.NotNull(request, nameof(request)); Requires.NotNull(targetMethods, nameof(targetMethods)); Requires.NotNull(jsonSerializer, nameof(jsonSerializer)); object[] parameters = TryGetParameters(request, method.Signature, this.errorMessages, jsonSerializer, request.Method); if (parameters != null) { targetMethods.Add(method, parameters); return(true); } return(false); }
/// <summary> /// Writes a message to the transport and flushes. /// </summary> /// <param name="content">The message to write.</param> /// <param name="cancellationToken">A token to cancel the write request.</param> /// <returns>A task that represents the asynchronous operation.</returns> /// <exception cref="InvalidOperationException">Thrown when <see cref="CanWrite"/> returns <c>false</c>.</exception> /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken"/> is canceled before message transmission begins.</exception> /// <exception cref="ObjectDisposedException">Thrown if this instance is disposed before or during transmission.</exception> /// <remarks> /// Implementations should expect this method to be invoked concurrently /// and use a queue to preserve message order as they are transmitted one at a time. /// </remarks> public async ValueTask WriteAsync(JsonRpcMessage content, CancellationToken cancellationToken) { Requires.NotNull(content, nameof(content)); Verify.Operation(this.CanWrite, "No sending stream."); cancellationToken.ThrowIfCancellationRequested(); try { using (await this.sendingSemaphore.EnterAsync(cancellationToken).ConfigureAwait(false)) { this.SetState(MessageHandlerState.Writing); try { cancellationToken.ThrowIfCancellationRequested(); await this.WriteCoreAsync(content, cancellationToken).ConfigureAwait(false); // When flushing, do NOT honor the caller's CancellationToken since the writing is done // and we must not throw OperationCanceledException back at them as if we hadn't transmitted it. // But *do* cancel flushing if we're being disposed. try { await this.FlushAsync(this.DisposalToken).ConfigureAwait(false); } catch (OperationCanceledException ex) when(this.DisposalToken.IsCancellationRequested) { throw new ObjectDisposedException(this.GetType().FullName, ex); } } finally { lock (this.syncObject) { this.state &= ~MessageHandlerState.Writing; if (this.DisposalToken.IsCancellationRequested) { this.writingCompleted.Set(); } } } } } catch (ObjectDisposedException) { // If already canceled, throw that instead of ObjectDisposedException. cancellationToken.ThrowIfCancellationRequested(); throw; } }
private bool TryAddMethod(JsonRpcMessage request, Dictionary <MethodSignature, object[]> targetMethods, MethodInfo method, JsonSerializer jsonSerializer) { var methodSignature = new MethodSignature(method); if (!targetMethods.ContainsKey(methodSignature)) { object[] parameters = TryGetParameters(request, methodSignature, this.errorMessages, jsonSerializer); if (parameters != null) { targetMethods.Add(methodSignature, parameters); return(true); } } return(false); }
private static RemoteRpcException CreateExceptionFromRpcError(JsonRpcMessage response, string targetName) { Requires.NotNull(response, nameof(response)); Requires.Argument(response.IsError, nameof(response), Resources.ResponseIsNotError); switch (response.Error.Code) { case (int)JsonRpcErrorCode.MethodNotFound: return(new RemoteMethodNotFoundException(response.Error.Message, targetName)); case (int)JsonRpcErrorCode.NoCallbackObject: return(new RemoteTargetNotSetException(response.Error.Message)); default: return(new RemoteInvocationException(response.Error.Message, response.Error.ErrorStack, response.Error.ErrorCode)); } }
private static JsonRpcMessage CreateError(JToken id, Exception exception) { if (exception == null) { throw new ArgumentNullException(nameof(exception)); } if (exception is TargetInvocationException || (exception is AggregateException && exception.InnerException != null)) { // Never let the outer (TargetInvocationException) escape because the inner is the interesting one to the caller, the outer is due to // the fact we are using reflection. exception = exception.InnerException; } var data = new { stack = exception.StackTrace, code = exception.HResult.ToString(CultureInfo.InvariantCulture) }; return(JsonRpcMessage.CreateError(id, JsonRpcErrorCode.InvocationError, exception.Message, data)); }
/// <inheritdoc/> protected override void Write(JsonRpcMessage content, CancellationToken cancellationToken) { if (this.writerWrapper == null) { this.writerWrapper = new CountingWriterWrapper <byte>(this.Writer); } // Reserve a 4 byte header for the content length. Span <byte> lengthBuffer = this.Writer.GetSpan(sizeof(int)); this.Writer.Advance(4); // Write out the actual message content, counting all written bytes. this.writerWrapper.Reset(); this.formatter.Serialize(this.writerWrapper, content); // Now go back and fill in the header with the actual content length. Utilities.Write(lengthBuffer, this.writerWrapper.ElementCount); }
private JsonRpcMessage HandleInvocationTaskResult(JToken id, Task t) { if (t == null) { throw new ArgumentNullException(nameof(t)); } if (!t.IsCompleted) { throw new ArgumentException(Resources.TaskNotCompleted, nameof(t)); } if (t.IsFaulted) { return(CreateError(id, t.Exception)); } if (t.IsCanceled) { return(JsonRpcMessage.CreateError(id, JsonRpcErrorCode.InvocationError, Resources.TaskWasCancelled)); } object taskResult = null; Type taskType = t.GetType(); // If t is a Task<SomeType>, it will have Result property. // If t is just a Task, there is no Result property on it. if (!taskType.Equals(typeof(Task))) { #pragma warning disable VSTHRD002 // misfiring analyzer https://github.com/Microsoft/vs-threading/issues/60 #pragma warning disable VSTHRD102 // misfiring analyzer https://github.com/Microsoft/vs-threading/issues/60 const string ResultPropertyName = nameof(Task <int> .Result); #pragma warning restore VSTHRD002 #pragma warning restore VSTHRD102 // We can't really write direct code to deal with Task<T>, since we have no idea of T in this context, so we simply use reflection to // read the result at runtime. PropertyInfo resultProperty = taskType.GetTypeInfo().GetDeclaredProperty(ResultPropertyName); taskResult = resultProperty?.GetValue(t); } return(JsonRpcMessage.CreateResult(id, taskResult, this.JsonSerializer)); }
#pragma warning disable AvoidAsyncSuffix // Avoid Async suffix /// <inheritdoc /> protected override async ValueTask WriteCoreAsync(JsonRpcMessage content, CancellationToken cancellationToken) { Requires.NotNull(content, nameof(content)); using (var contentSequenceBuilder = new Sequence <byte>()) { WebSocketMessageType messageType = this.Formatter is IJsonRpcMessageTextFormatter ? WebSocketMessageType.Text : WebSocketMessageType.Binary; this.Formatter.Serialize(contentSequenceBuilder, content); cancellationToken.ThrowIfCancellationRequested(); int bytesCopied = 0; ReadOnlySequence <byte> contentSequence = contentSequenceBuilder.AsReadOnlySequence; foreach (ReadOnlyMemory <byte> memory in contentSequence) { bool endOfMessage = bytesCopied + memory.Length == contentSequence.Length; #if NETCOREAPP2_1 await this.WebSocket.SendAsync(memory, messageType, endOfMessage, cancellationToken).ConfigureAwait(false); #else if (MemoryMarshal.TryGetArray(memory, out ArraySegment <byte> segment)) { await this.WebSocket.SendAsync(segment, messageType, endOfMessage, CancellationToken.None).ConfigureAwait(false); } else { byte[] array = ArrayPool <byte> .Shared.Rent(memory.Length); try { memory.CopyTo(array); await this.WebSocket.SendAsync(new ArraySegment <byte>(array, 0, memory.Length), messageType, endOfMessage, CancellationToken.None).ConfigureAwait(false); } finally { ArrayPool <byte> .Shared.Return(array); } } #endif bytesCopied += memory.Length; } } }
internal TargetMethod(JsonRpcMessage request, object target, JsonSerializer jsonSerializer) { Requires.NotNull(request, nameof(request)); Requires.NotNull(target, nameof(target)); this.request = request; this.target = target; var targetMethods = new Dictionary <MethodSignature, object[]>(); for (TypeInfo t = target.GetType().GetTypeInfo(); t != null; t = t.BaseType?.GetTypeInfo()) { bool matchFound = false; foreach (MethodInfo method in t.GetDeclaredMethods(request.Method)) { matchFound |= TryAddMethod(request, targetMethods, method, jsonSerializer); } if (!matchFound && !request.Method.EndsWith(ImpliedMethodNameAsyncSuffix)) { foreach (MethodInfo method in t.GetDeclaredMethods(request.Method + ImpliedMethodNameAsyncSuffix)) { matchFound |= TryAddMethod(request, targetMethods, method, jsonSerializer); } } } if (targetMethods.Count == 1) { KeyValuePair <MethodSignature, object[]> methodWithParameters = targetMethods.First(); this.method = methodWithParameters.Key.MethodInfo; this.parameters = methodWithParameters.Value; this.AcceptsCancellationToken = methodWithParameters.Key.HasCancellationTokenParameter; } else if (targetMethods.Count > 1) { this.method = null; this.parameters = null; this.errorMessages.Add(string.Format(CultureInfo.CurrentCulture, Resources.MoreThanOneMethodFound, string.Join("; ", targetMethods.Keys))); } }
/// <summary> /// Writes a message to the transport and flushes. /// </summary> /// <param name="content">The message to write.</param> /// <param name="cancellationToken">A token to cancel the write request.</param> /// <returns>A task that represents the asynchronous operation.</returns> /// <exception cref="InvalidOperationException">Thrown when <see cref="CanWrite"/> returns <c>false</c>.</exception> /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken"/> is canceled before message transmission begins.</exception> /// <remarks> /// Implementations should expect this method to be invoked concurrently /// and use a queue to preserve message order as they are transmitted one at a time. /// </remarks> public async ValueTask WriteAsync(JsonRpcMessage content, CancellationToken cancellationToken) { Requires.NotNull(content, nameof(content)); Verify.Operation(this.CanWrite, "No sending stream."); cancellationToken.ThrowIfCancellationRequested(); bool shouldDispose = false; try { using (await this.sendingSemaphore.EnterAsync(cancellationToken).ConfigureAwait(false)) { this.SetState(MessageHandlerState.Writing); try { cancellationToken.ThrowIfCancellationRequested(); await this.WriteCoreAsync(content, cancellationToken).ConfigureAwait(false); await this.FlushAsync(cancellationToken).ConfigureAwait(false); } finally { shouldDispose = this.CheckIfDisposalAppropriate(MessageHandlerState.Writing); } } } catch (ObjectDisposedException) { // If already canceled, throw that instead of ObjectDisposedException. cancellationToken.ThrowIfCancellationRequested(); throw; } finally { if (shouldDispose) { this.Dispose(true); } } }
internal TargetMethod( JsonRpcMessage request, object target, JsonSerializer jsonSerializer, IReadOnlyDictionary <string, string> requestMethodToClrMethodMap) { Requires.NotNull(request, nameof(request)); Requires.NotNull(target, nameof(target)); Requires.NotNull(jsonSerializer, nameof(jsonSerializer)); Requires.NotNull(requestMethodToClrMethodMap, nameof(requestMethodToClrMethodMap)); this.request = request; this.target = target; if (requestMethodToClrMethodMap.TryGetValue(request.Method, out string clrMethodName)) { var targetMethods = new Dictionary <MethodSignature, object[]>(); for (TypeInfo t = target.GetType().GetTypeInfo(); t != null; t = t.BaseType?.GetTypeInfo()) { foreach (MethodInfo method in t.GetDeclaredMethods(clrMethodName)) { TryAddMethod(request, targetMethods, method, jsonSerializer, request.Method); } } if (targetMethods.Count == 1) { KeyValuePair <MethodSignature, object[]> methodWithParameters = targetMethods.First(); this.method = methodWithParameters.Key.MethodInfo; this.parameters = methodWithParameters.Value; this.AcceptsCancellationToken = methodWithParameters.Key.HasCancellationTokenParameter; } else if (targetMethods.Count > 1) { this.method = null; this.parameters = null; this.errorMessages.Add(string.Format(CultureInfo.CurrentCulture, Resources.MoreThanOneMethodFound, string.Join("; ", targetMethods.Keys))); } } }
/// <inheritdoc/> protected override async ValueTask <JsonRpcMessage> ReadCoreAsync(CancellationToken cancellationToken) { var readResult = await this.ReadAtLeastAsync(4, allowEmpty : true, cancellationToken).ConfigureAwait(false); if (readResult.Buffer.Length == 0) { return(null); } ReadOnlySequence <byte> lengthBuffer = readResult.Buffer.Slice(0, 4); int length = Utilities.ReadInt32BE(lengthBuffer); this.Reader.AdvanceTo(lengthBuffer.End); readResult = await this.ReadAtLeastAsync(length, allowEmpty : false, cancellationToken).ConfigureAwait(false); ReadOnlySequence <byte> content = readResult.Buffer.Slice(0, length); JsonRpcMessage message = this.formatter.Deserialize(content); this.Reader.AdvanceTo(content.End); return(message); }
private bool TryAddMethod(JsonRpcMessage request, Dictionary <MethodSignature, object[]> targetMethods, MethodInfo method, JsonSerializer jsonSerializer, string requestMethodName) { Requires.NotNull(request, nameof(request)); Requires.NotNull(targetMethods, nameof(targetMethods)); Requires.NotNull(method, nameof(method)); Requires.NotNull(jsonSerializer, nameof(jsonSerializer)); Requires.NotNullOrEmpty(requestMethodName, nameof(requestMethodName)); var methodSignature = new MethodSignature(method); if (!targetMethods.ContainsKey(methodSignature)) { object[] parameters = TryGetParameters(request, methodSignature, this.errorMessages, jsonSerializer, requestMethodName); if (parameters != null) { targetMethods.Add(methodSignature, parameters); return(true); } } return(false); }
/// <summary> /// Writes a message to the transport and flushes. /// </summary> /// <param name="content">The message to write.</param> /// <param name="cancellationToken">A token to cancel the write request.</param> /// <returns>A task that represents the asynchronous operation.</returns> /// <exception cref="InvalidOperationException">Thrown when <see cref="CanWrite"/> returns <c>false</c>.</exception> /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken"/> is canceled before message transmission begins.</exception> /// <remarks> /// Implementations should expect this method to be invoked concurrently /// and use a queue to preserve message order as they are transmitted one at a time. /// </remarks> public async ValueTask WriteAsync(JsonRpcMessage content, CancellationToken cancellationToken) { Requires.NotNull(content, nameof(content)); Verify.Operation(this.CanWrite, "No sending stream."); cancellationToken.ThrowIfCancellationRequested(); try { using (await this.sendingSemaphore.EnterAsync(cancellationToken).ConfigureAwait(false)) { this.SetState(MessageHandlerState.Writing); try { cancellationToken.ThrowIfCancellationRequested(); await this.WriteCoreAsync(content, cancellationToken).ConfigureAwait(false); await this.FlushAsync(cancellationToken).ConfigureAwait(false); } finally { lock (this.syncObject) { this.state &= ~MessageHandlerState.Writing; if (this.DisposalToken.IsCancellationRequested) { this.writingCompleted.Set(); } } } } } catch (ObjectDisposedException) { // If already canceled, throw that instead of ObjectDisposedException. cancellationToken.ThrowIfCancellationRequested(); throw; } }
/// <inheritdoc/> protected override async ValueTask <JsonRpcMessage?> ReadCoreAsync(CancellationToken cancellationToken) { Assumes.NotNull(this.Reader); ReadResult readResult = await this.ReadAtLeastAsync(4, allowEmpty : true, cancellationToken).ConfigureAwait(false); if (readResult.Buffer.Length == 0) { return(null); } ReadOnlySequence <byte> lengthBuffer = readResult.Buffer.Slice(0, 4); int length = Utilities.ReadInt32BE(lengthBuffer); this.Reader.AdvanceTo(lengthBuffer.End); JsonRpcMessage message = await this.DeserializeMessageAsync(length, cancellationToken).ConfigureAwait(false); if (JsonRpcEventSource.Instance.IsEnabled(System.Diagnostics.Tracing.EventLevel.Informational, System.Diagnostics.Tracing.EventKeywords.None)) { JsonRpcEventSource.Instance.HandlerReceived(length); } return(message); }