Пример #1
0
        /// <summary>
        /// Invoke remote method and get the result.
        /// </summary>
        /// <typeparam name="T">The return type.</typeparam>
        /// <param name="serviceName">The name of the service</param>
        /// <param name="methodName">The method name to call.</param>
        /// <param name="args">The parameters of the method.</param>
        /// <param name="cancellationToken">The cancellation token which can cancel this method.</param>
        /// <returns>The result value.</returns>
        public async Task <T> InvokeAsync <T>(string serviceName, string methodName, object[] args, CancellationToken cancellationToken = default)
        {
            var id          = Interlocked.Increment(ref _requestId);
            var request     = new JsonRpcRequest(id, methodName, new JsonRpcRequestParameter(RequestParameterType.Object, args));
            var requestData = await JsonRpcCodec.EncodeRequestsAsync(new[] { request }, cancellationToken).ConfigureAwait(false);

            var responseData = await ProcessAsync(serviceName, requestData, cancellationToken).ConfigureAwait(false);

            var responses = await JsonRpcCodec.DecodeResponsesAsync(responseData, cancellationToken).ConfigureAwait(false);

            if (responses.Length > 0)
            {
                var response   = responses[0];
                var responseId = Convert.ToInt32(response.Id);
                if (responseId != id)
                {
                    throw new InvalidOperationException("Response id is not matched.");
                }
                if (response.Result is RpcException exception)
                {
                    throw exception;
                }

                var resultString = (string)response.Result;
                using var utf8StringData = Utf8StringData.Get(resultString);
                return(await JsonSerializer.DeserializeAsync <T>(utf8StringData.Stream, JsonRpcConvertSettings.SerializerOptions, cancellationToken).ConfigureAwait(false));
            }

            throw new InvalidOperationException("Fail to get invoke result from server.");
        }
Пример #2
0
        /// <summary>
        /// Process a byte[] request which contains the json data.
        /// </summary>
        /// <param name="serviceName">The name of the service.</param>
        /// <param name="requestData">The request data</param>
        /// <param name="cancellationToken">The cancellation token which can cancel this method.</param>
        /// <returns>The response data.</returns>
        public async Task <byte[]> ProcessAsync(string serviceName, byte[] requestData, CancellationToken cancellationToken)
        {
            if (_router == null)
            {
                throw new NullReferenceException("The router is null");
            }
            if (Logger.DebugMode)
            {
                var requestString = Encoding.UTF8.GetString(requestData);
                Logger.WriteDebug($"Receive request data:{requestString}");
            }

            await using var requestStream = new MemoryStream(requestData);
            var requests = await JsonRpcCodec.DecodeRequestsAsync(requestStream, cancellationToken).ConfigureAwait(false);

            var responses = await _router.DispatchRequestsAsync(serviceName, requests, cancellationToken).ConfigureAwait(false);

            var responseData = await JsonRpcCodec.EncodeResponsesAsync(responses, cancellationToken).ConfigureAwait(false);

            if (Logger.DebugMode)
            {
                var responseString = Encoding.UTF8.GetString(responseData);
                Logger.WriteDebug($"Response data sent:{responseString}");
            }
            return(responseData);
        }
Пример #3
0
        /// <summary>
        /// Invoke remote method without result.
        /// </summary>
        /// <param name="serviceName">The name of the service</param>
        /// <param name="methodName">The method name to call.</param>
        /// <param name="cancellationToken">The cancellation token which can cancel this method.</param>
        /// <param name="args">The parameters of the method.</param>
        /// <returns>Void</returns>
        public async Task VoidInvokeAsync(string serviceName, string methodName, CancellationToken cancellationToken, params object[] args)
        {
            var id          = Interlocked.Increment(ref _requestId);
            var request     = new JsonRpcRequest(id, methodName, new JsonRpcRequestParameter(RequestParameterType.Object, args));
            var requestData = await JsonRpcCodec.EncodeRequestsAsync(new[] { request }, cancellationToken).ConfigureAwait(false);

            var responseData = await ProcessAsync(serviceName, requestData, cancellationToken).ConfigureAwait(false);

            var responses = await JsonRpcCodec.DecodeResponsesAsync(responseData, cancellationToken).ConfigureAwait(false);

            if (responses.Length > 0)
            {
                var response   = responses[0];
                var responseId = Convert.ToInt32(response.Id);
                if (responseId != id)
                {
                    throw new InvalidOperationException("Response id is not matched.");
                }
                if (response.Result is RpcException exception)
                {
                    throw exception;
                }
                var resultString = (string)response.Result;
                if (resultString != "null")
                {
                    throw new InvalidOperationException("The result from server is not [null]");
                }
            }
            else
            {
                throw new InvalidOperationException("Fail to get invoke result from server.");
            }
        }
Пример #4
0
        /// <summary>
        /// Dispatch request to specified service.
        /// </summary>
        /// <param name="context">The HttpListenerContext</param>
        /// <param name="router">The router to handle the rpc request</param>
        /// <param name="serviceName">The name of the service</param>
        /// <param name="cancellationToken">The cancellation token which can cancel this method</param>
        /// <returns>Void</returns>
        private async Task DispatchAsync(IJsonRpcHttpContext context, IJsonRpcRouter router, string serviceName, CancellationToken cancellationToken = default)
        {
            var dataLength  = (int)context.GetRequestContentLength();
            var requestData = ArrayPool <byte> .Shared.Rent(dataLength);

            JsonRpcRequest[] requests;
            try
            {
                var inputStream = context.GetInputStream();
                await ReadRequestDataAsync(inputStream, requestData, dataLength, cancellationToken).ConfigureAwait(false);

                if (Logger.DebugMode)
                {
                    var requestString = Encoding.UTF8.GetString(requestData);
                    Logger.WriteDebug($"Receive request data:{requestString}");
                }

                requests = await JsonRpcCodec.DecodeRequestsAsync(requestData, cancellationToken, dataLength).ConfigureAwait(false);
            }
            finally
            {
                ArrayPool <byte> .Shared.Return(requestData);
            }

            var responses = await router.DispatchRequestsAsync(serviceName, requests, cancellationToken).ConfigureAwait(false);

            await WriteRpcResponsesAsync(context, responses, cancellationToken).ConfigureAwait(false);
        }
        /// <summary>
        /// Dispatch request to different services.
        /// </summary>
        /// <param name="service">The service to handle the context</param>
        /// <param name="context">The http context</param>
        /// <returns>Void</returns>
        private async Task DispatchCall(JsonRpcService service, WebSocketContext context)
        {
            var webSocket = context.WebSocket;

            while (true)
            {
                if (webSocket.CloseStatus != null)
                {
                    throw new WebSocketException((int)webSocket.CloseStatus, webSocket.CloseStatusDescription);
                }

                JsonRpcRequest[] requests;
                await using (var requestStream = await GetRequestStreamAsync(webSocket).ConfigureAwait(false))
                {
                    var requestData = ArrayPool <byte> .Shared.Rent((int)requestStream.Length);

                    try
                    {
                        await ReadRequestDataAsync(requestStream, requestData).ConfigureAwait(false);

                        if (Logger.DebugMode)
                        {
                            var requestString = Encoding.UTF8.GetString(requestData);
                            Logger.WriteDebug($"Receive request data:{requestString}");
                        }
                        requests = await JsonRpcCodec.DecodeRequestsAsync(requestData).ConfigureAwait(false);
                    }
                    finally
                    {
                        ArrayPool <byte> .Shared.Return(requestData);
                    }
                }

                if (requests.Length == 1)
                {
                    var request  = requests[0];
                    var response = await GetResponseAsync(service, request).ConfigureAwait(false);
                    await WriteResponse(context, response).ConfigureAwait(false);
                }
                else
                {
                    //batch call.
                    var responseList = new List <JsonRpcResponse>();
                    foreach (var request in requests)
                    {
                        var response = await GetResponseAsync(service, request).ConfigureAwait(false);

                        if (response != null)
                        {
                            responseList.Add(response);
                        }
                    }

                    if (responseList.Count > 0)
                    {
                        await WriteResponses(context, responseList.ToArray()).ConfigureAwait(false);
                    }
                }
            }
        }
Пример #6
0
 /// <summary>
 /// Write rpc responses back to the client.
 /// </summary>
 /// <param name="context">The http context</param>
 /// <param name="responses">The responses to write back.</param>
 /// <param name="cancellationToken">The cancel token which will cancel this method.</param>
 /// <returns>Void</returns>
 private async Task WriteRpcResponsesAsync(IJsonRpcHttpContext context, JsonRpcResponse[] responses, CancellationToken cancellationToken = default)
 {
     try
     {
         var resultData = await JsonRpcCodec.EncodeResponsesAsync(responses, cancellationToken).ConfigureAwait(false);
         await WriteRpcResultAsync(context, resultData, cancellationToken).ConfigureAwait(false);
     }
     catch (Exception ex)
     {
         Logger.WriteWarning($"Write rpc response back to client error:{ex}");
     }
 }
Пример #7
0
        /// <summary>
        /// Handle request request and get the response
        /// </summary>
        /// <param name="service">The service which will handle the request.</param>
        /// <param name="request">The request to handle.</param>
        /// <param name="cancellationToken">The cancellation token which can cancel this method.</param>
        /// <returns>The response for the request.</returns>
        private async Task <JsonRpcResponse> GetResponseAsync(IJsonRpcCallService service, JsonRpcRequest request, CancellationToken cancellationToken)
        {
            JsonRpcResponse response = null;

            try
            {
                var rpcCall = service[request.Method];
                if (rpcCall == null)
                {
                    throw new MethodNotFoundException($"Method: {request.Method} not found.");
                }

                object[] arguments;
                if (request.Params.Type == RequestParameterType.RawString)
                {
                    var paramString = (string)request.Params.Value;
                    arguments = await JsonRpcCodec.DecodeArgumentsAsync(paramString, rpcCall.Parameters, cancellationToken).ConfigureAwait(false);
                }
                else
                {
                    if (request.Params.Value is Array array)
                    {
                        arguments = array.Cast <object>().ToArray();
                    }
                    else
                    {
                        arguments = new[] { request.Params.Value };
                    }
                }

                //From here we got the response id.
                response = new JsonRpcResponse(request.Id);
                if (arguments.Length == rpcCall.Parameters.Count)
                {
                    try
                    {
                        var result = await rpcCall.Call(arguments).ConfigureAwait(false);

                        if (request.IsNotification)
                        {
                            return(null);
                        }
                        response.WriteResult(result);
                    }
                    catch (Exception ex)
                    {
                        var argumentString = new StringBuilder();
                        argumentString.Append(Environment.NewLine);
                        var index = 0;
                        foreach (var argument in arguments)
                        {
                            argumentString.AppendLine($"[{index}] {argument}");
                        }
                        argumentString.Append(Environment.NewLine);
                        response.WriteResult(new InternalErrorException($"Call method {rpcCall.Name} with args:{argumentString} error :{ex.Format()}"));
                    }
                }
                else
                {
                    throw new InvalidParamsException("Argument count is not matched");
                }
            }
            catch (Exception ex)
            {
                response ??= new JsonRpcResponse();
                if (ex is RpcException rpcException)
                {
                    response.WriteResult(rpcException);
                }
                else
                {
                    var serverError = new InternalErrorException($"Handle request {request} error: {ex.Format()}");
                    response.WriteResult(serverError);
                }
            }
            return(response);
        }
Пример #8
0
        /// <summary>
        /// Handle request request and get the response
        /// </summary>
        /// <param name="service">The service which will handle the request.</param>
        /// <param name="request">The request to handle.</param>
        /// <returns>The response for the request.</returns>
        protected async Task <JsonRpcResponse> GetResponseAsync(JsonRpcService service, JsonRpcRequest request)
        {
            JsonRpcResponse response = null;

            try
            {
                var rpcCall = service.GetRpcCall(request.Method);
                if (rpcCall == null)
                {
                    throw new MethodNotFoundException($"Method: {request.Method} not found.");
                }
                var arguments = await JsonRpcCodec.DecodeArgumentsAsync(request.Params, rpcCall.Parameters).ConfigureAwait(false);

                //From here we got the response id.
                response = new JsonRpcResponse(request.Id);
                //The parser will add context into the args, so the final count is parameter count + 1.
                if (arguments.Length == rpcCall.Parameters.Count)
                {
                    try
                    {
                        var result = await rpcCall.Call(arguments).ConfigureAwait(false);

                        if (request.IsNotification)
                        {
                            return(null);
                        }
                        response.WriteResult(result);
                    }
                    catch (Exception ex)
                    {
                        var argumentString = new StringBuilder();
                        argumentString.Append(Environment.NewLine);
                        var index = 0;
                        foreach (var argument in arguments)
                        {
                            argumentString.AppendLine($"[{index}] {argument}");
                        }

                        argumentString.Append(Environment.NewLine);
                        Logger.WriteError($"Call method {rpcCall.Name} with args:{argumentString} error :{ex.Format()}");
                        response.WriteResult(new InternalErrorException());
                    }
                }
                else
                {
                    throw new InvalidParamsException("Argument count is not matched");
                }
            }
            catch (Exception ex)
            {
                response ??= new JsonRpcResponse();
                if (ex is RpcException rpcException)
                {
                    Logger.WriteError($"Handle request {request} error: {rpcException.Format()}");
                    response.WriteResult(rpcException);
                }
                else
                {
                    Logger.WriteError($"Handle request {request} error: {ex.Format()}");
                    var serverError = new InternalErrorException();
                    response.WriteResult(serverError);
                }
            }
            return(response);
        }
Пример #9
0
        /// <summary>
        /// Write a group of JsonRpcHttpResponse to remote side.
        /// </summary>
        /// <param name="context">The context of the http.</param>
        /// <param name="responses">A group of JsonRpcHttpResponses to write.</param>
        /// <returns>Void</returns>
        public async Task WriteResponses(object context, JsonRpcResponse[] responses)
        {
            var resultData = await JsonRpcCodec.EncodeResponsesAsync(responses).ConfigureAwait(false);

            await WriteResultAsync(context, resultData).ConfigureAwait(false);
        }
Пример #10
0
        /// <summary>
        /// Dispatch request to different services.
        /// </summary>
        /// <param name="service">The service to handle the context</param>
        /// <param name="context">The http context</param>
        /// <returns>Void</returns>
        private async Task DispatchCall(JsonRpcService service, HttpListenerContext context)
        {
            var httpMethod = context.Request.HttpMethod.ToLower();

            if (httpMethod != "post")
            {
                throw new ServerErrorException("Invalid http-method.", $"Invalid http-method:{httpMethod}");
            }

            Logger.WriteVerbose($"Handle request [{httpMethod}]: {context.Request.Url}");


            var requestData = ArrayPool <byte> .Shared.Rent((int)context.Request.ContentLength64);

            JsonRpcRequest[] requests;
            try
            {
                await ReadRequestDataAsync(context.Request.InputStream, requestData).ConfigureAwait(false);

                context.Request.InputStream.Close();
                if (Logger.DebugMode)
                {
                    var requestString = Encoding.UTF8.GetString(requestData);
                    Logger.WriteDebug($"Receive request data:{requestString}");
                }
                requests = await JsonRpcCodec.DecodeRequestsAsync(requestData).ConfigureAwait(false);
            }
            finally
            {
                ArrayPool <byte> .Shared.Return(requestData);
            }

            if (requests.Length == 1)
            {
                var request  = requests[0];
                var response = await GetResponseAsync(service, request).ConfigureAwait(false);
                await WriteResponse(context, response).ConfigureAwait(false);
            }
            else
            {
                //batch call.
                var responseList = new List <JsonRpcResponse>();
                foreach (var request in requests)
                {
                    var response = await GetResponseAsync(service, request).ConfigureAwait(false);

                    if (response != null)
                    {
                        responseList.Add(response);
                    }
                }

                if (responseList.Count > 0)
                {
                    await WriteResponses(context, responseList.ToArray()).ConfigureAwait(false);
                }
                else
                {
                    await WriteResultAsync(context).ConfigureAwait(false);
                }
            }
        }
Пример #11
0
        /// <summary>
        /// Handle data from websocket and return result data to remote client.
        /// </summary>
        /// <param name="requestPath">The request path from the http request.</param>
        /// <param name="router">The router to handle the request data.</param>
        /// <param name="socket">The connected websocket.</param>
        /// <param name="cancellationToken">The cancellation token which can cancel this method</param>
        protected async Task HandleWebSocketAsync(string requestPath, IJsonRpcRouter router, WebSocket socket, CancellationToken cancellationToken = default)
        {
            try
            {
                var serviceName = GetRpcServiceName(requestPath);
                if (string.IsNullOrEmpty(serviceName) || !router.ServiceExists(serviceName))
                {
                    Logger.WriteWarning($"Service {serviceName} does not exist.");
                    throw new InvalidOperationException($"Service [{serviceName}] does not exist.");
                }

                byte[]       receiveBuffer = null;
                MemoryStream inputStream   = null;
                // While the WebSocket connection remains open run a simple loop that receives data and sends it back.
                while (socket.State == WebSocketState.Open)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    receiveBuffer ??= ArrayPool <byte> .Shared.Rent(1024);

                    inputStream ??= new MemoryStream();
                    var receiveResult = await socket.ReceiveAsync(receiveBuffer, cancellationToken).ConfigureAwait(false);

                    if (receiveResult.MessageType == WebSocketMessageType.Close)
                    {
                        await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", cancellationToken).ConfigureAwait(false);
                    }
                    else if (receiveResult.MessageType == WebSocketMessageType.Text)
                    {
                        await socket.CloseAsync(WebSocketCloseStatus.InvalidMessageType, "Cannot accept text frame", cancellationToken).ConfigureAwait(false);
                    }
                    else
                    {
                        await inputStream.WriteAsync(receiveBuffer, 0, receiveResult.Count, cancellationToken).ConfigureAwait(false);

                        //handle stream
                        if (receiveResult.EndOfMessage)
                        {
                            //Release resources.
                            var requestData = inputStream.ToArray();
                            await inputStream.DisposeAsync().ConfigureAwait(false);

                            inputStream = null;
                            ArrayPool <byte> .Shared.Return(receiveBuffer);

                            receiveBuffer = null;

                            if (Logger.DebugMode)
                            {
                                var requestString = Encoding.UTF8.GetString(requestData);
                                Logger.WriteDebug($"Receive request data: {requestString}");
                            }

                            var requests = await JsonRpcCodec.DecodeRequestsAsync(requestData, cancellationToken).ConfigureAwait(false);

                            var responses = await router.DispatchRequestsAsync(serviceName, requests, cancellationToken).ConfigureAwait(false);

                            var responseData = await JsonRpcCodec.EncodeResponsesAsync(responses, cancellationToken).ConfigureAwait(false);

                            await socket.SendAsync(responseData, WebSocketMessageType.Binary, true, cancellationToken).ConfigureAwait(false);

                            if (Logger.DebugMode)
                            {
                                var resultString = Encoding.UTF8.GetString(responseData);
                                Logger.WriteDebug($"Response data sent:{resultString}");
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.WriteError($"Handle request {requestPath} error: {ex.Message}");
            }
            finally
            {
                socket.Dispose();
                Logger.WriteVerbose("Remote websocket closed.");
            }
        }