Пример #1
0
        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;
                }
            }
        }
Пример #3
0
        /// <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
            });
        }
Пример #4
0
        /// <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);
            }
        }
Пример #7
0
 /// <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);
        }
Пример #10
0
        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);
                    }
                }
            }
        }
Пример #11
0
        /// <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);
            }
        }
Пример #12
0
        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);
        }
Пример #13
0
        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();
        }
Пример #14
0
        /// <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;
                }
            }
        }
Пример #15
0
        /// <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();
        }
Пример #16
0
        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);
        }
Пример #17
0
        /// <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;
            }
        }
Пример #18
0
        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);
        }
Пример #19
0
        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));
            }
        }
Пример #20
0
        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);
        }
Пример #22
0
        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));
        }
Пример #23
0
#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;
                }
            }
        }
Пример #24
0
        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)));
            }
        }
Пример #25
0
        /// <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);
                }
            }
        }
Пример #26
0
        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);
        }
Пример #28
0
        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;
            }
        }
Пример #30
0
        /// <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);
        }