Example #1
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);
        }
Example #2
0
        /// <summary>
        /// Write rpc result struct data to remote side.
        /// </summary>
        /// <param name="context">The context of the http.</param>
        /// <param name="result">The result data to write</param>
        /// <param name="cancellationToken">The cancellation which can cancel this method</param>
        /// <returns>Void</returns>
        private async Task WriteRpcResultAsync(IJsonRpcHttpContext context, byte[] result, CancellationToken cancellationToken = default)
        {
            context.SetResponseHeader("Server", "JsonRpcLite");
            context.SetResponseHeader("Access-Control-Allow-Origin", "*");
            context.SetResponseStatusCode((int)HttpStatusCode.OK);
            context.SetResponseContentType("application/json");
            if (result != null)
            {
                var outputData = await GetOutputDataAsync(context, result, cancellationToken).ConfigureAwait(false);

                context.SetResponseContentLength(outputData.Length);
                var outputStream = context.GetOutputStream();
                await outputStream.WriteAsync(outputData, cancellationToken).ConfigureAwait(false);

                await outputStream.FlushAsync(cancellationToken);
            }
            if (Logger.DebugMode)
            {
                if (result != null)
                {
                    var resultString = Encoding.UTF8.GetString(result);
                    Logger.WriteDebug($"Response data sent:{resultString}");
                }
            }
        }
Example #3
0
        /// <summary>
        /// Get the compressed(or not) output data according to request.
        /// </summary>
        /// <param name="context">The http context.</param>
        /// <param name="data">The data to output.</param>
        /// <param name="cancellationToken">The cancellation token which will cancel this method.</param>
        /// <returns>The compressed or not output data.</returns>
        private async Task <byte[]> GetOutputDataAsync(IJsonRpcHttpContext context, byte[] data, CancellationToken cancellationToken = default)
        {
            var outputData     = data;
            var acceptEncoding = context.GetRequestHeader("Accept-Encoding");

            if (acceptEncoding != null && acceptEncoding.Contains("gzip"))
            {
                context.SetResponseHeader("Content-Encoding", "gzip");
                await using var memoryStream = new MemoryStream();
                await using var outputStream = new GZipStream(memoryStream, CompressionMode.Compress);
                await outputStream.WriteAsync(outputData, cancellationToken).ConfigureAwait(false);

                await outputStream.FlushAsync(cancellationToken).ConfigureAwait(false);

                outputData = memoryStream.ToArray();
            }
            else if (acceptEncoding != null && acceptEncoding.Contains("deflate"))
            {
                context.SetResponseHeader("Content-Encoding", "deflate");
                await using var memoryStream = new MemoryStream();
                await using var outputStream = new DeflateStream(memoryStream, CompressionMode.Compress);
                await outputStream.WriteAsync(outputData, cancellationToken).ConfigureAwait(false);

                await outputStream.FlushAsync(cancellationToken).ConfigureAwait(false);

                outputData = memoryStream.ToArray();
            }
            return(outputData);
        }
Example #4
0
 /// <summary>
 /// Write http exception back to the client.
 /// </summary>
 /// <param name="context">The http context</param>
 /// <param name="exception">The exception to write back.</param>
 /// <param name="cancellationToken">The cancel token which will cancel this method.</param>
 /// <returns>Void</returns>
 private async Task WriteHttpExceptionAsync(IJsonRpcHttpContext context, HttpException exception, CancellationToken cancellationToken = default)
 {
     try
     {
         await WriteHttpResultAsync(context, exception.ErrorCode, exception.Message, cancellationToken).ConfigureAwait(false);
     }
     catch (Exception ex)
     {
         Logger.WriteWarning($"Write http exception back to client error:{ex}");
     }
 }
Example #5
0
 /// <summary>
 /// Write smd data back to the client.
 /// </summary>
 /// <param name="context">The http context</param>
 /// <param name="smdData">The smd data to write back.</param>
 /// <param name="cancellationToken">The cancellation token which will cancel this method.</param>
 /// <returns>Void</returns>
 private async Task WriteSmdDataAsync(IJsonRpcHttpContext context, byte[] smdData, CancellationToken cancellationToken = default)
 {
     try
     {
         await WriteRpcResultAsync(context, smdData, cancellationToken).ConfigureAwait(false);
     }
     catch (Exception ex)
     {
         Logger.WriteWarning($"Write smd data back to client error:{ex}");
     }
 }
Example #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}");
     }
 }
Example #7
0
        /// <summary>
        /// Write http message to remote side.
        /// </summary>
        /// <param name="context">The  http context.</param>
        /// <param name="statusCode">The status code to return</param>
        /// <param name="message">The message to write back.</param>
        /// <param name="cancellationToken">The cancellation token which will cancel this method.</param>
        /// <returns>Void</returns>
        private async Task WriteHttpResultAsync(IJsonRpcHttpContext context, int statusCode, string message, CancellationToken cancellationToken = default)
        {
            context.SetResponseHeader("Server", "JsonRpcLite");
            context.SetResponseHeader("Access-Control-Allow-Origin", "*");
            context.SetResponseStatusCode(statusCode);
            context.SetResponseContentType("text/html");
            var outputData = await GetOutputDataAsync(context, Encoding.UTF8.GetBytes(message), cancellationToken).ConfigureAwait(false);

            context.SetResponseContentLength(outputData.Length);
            var outputStream = context.GetOutputStream();
            await outputStream.WriteAsync(outputData, cancellationToken).ConfigureAwait(false);

            await outputStream.FlushAsync(cancellationToken);

            if (Logger.DebugMode)
            {
                Logger.WriteDebug($"Response data sent:{message}");
            }
        }
Example #8
0
        /// <summary>
        /// Handle connected request and return result.
        /// </summary>
        /// <param name="context">The http context to handle.</param>
        /// <param name="router">The router to dispatch the request data.</param>
        /// <param name="cancellationToken">The cancellation token which can cancel this method.</param>
        /// <returns>Void</returns>
        protected async Task HandleContextAsync(IJsonRpcHttpContext context, IJsonRpcRouter router, CancellationToken cancellationToken = default)
        {
            var httpMethod  = context.GetRequestHttpMethod();
            var requestPath = context.GetRequestPath();

            Logger.WriteVerbose($"Handle request [{httpMethod}]: {requestPath}");
            try
            {
                var serviceName = GetRpcServiceName(requestPath);
                if (string.IsNullOrEmpty(serviceName))
                {
                    Logger.WriteWarning($"Service for request: {requestPath} not found.");
                    throw new HttpException((int)HttpStatusCode.ServiceUnavailable, "Service does not exist.");
                }

                if (httpMethod == "get")
                {
                    var smdRequest = false;
                    var smdIndex   = serviceName.LastIndexOf(".smd", StringComparison.InvariantCultureIgnoreCase);
                    if (smdIndex != -1)
                    {
                        serviceName = serviceName.Substring(0, smdIndex);
                        smdRequest  = true;
                    }

                    if (!router.ServiceExists(serviceName))
                    {
                        Logger.WriteWarning($"Service for request: {requestPath} not found.");
                        throw new HttpException((int)HttpStatusCode.ServiceUnavailable, $"Service [{serviceName}] does not exist.");
                    }

                    if (SmdEnabled && smdRequest)
                    {
                        try
                        {
                            var smdData = await router.GetServiceSmdData(serviceName);
                            await WriteSmdDataAsync(context, smdData, cancellationToken).ConfigureAwait(false);
                        }
                        catch (Exception ex)
                        {
                            throw new HttpException((int)HttpStatusCode.InternalServerError, ex.Message);
                        }
                    }
                    else
                    {
                        throw new HttpException((int)HttpStatusCode.NotFound, $"Resource for {requestPath} does not exist.");
                    }
                }
                else if (httpMethod == "post")
                {
                    if (!router.ServiceExists(serviceName))
                    {
                        Logger.WriteWarning($"Service for request: {requestPath} not found.");
                        throw new ServerErrorException("Service does not exist.", $"Service [{serviceName}] does not exist.");
                    }

                    try
                    {
                        await DispatchAsync(context, router, serviceName, cancellationToken).ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        if (ex is RpcException)
                        {
                            throw;
                        }

                        throw new ServerErrorException("Internal server error.", ex.Message);
                    }
                }
                else
                {
                    throw new HttpException((int)HttpStatusCode.MethodNotAllowed, $"Invalid http-method:{httpMethod}");
                }
            }
            catch (Exception ex)
            {
                Logger.WriteError($"Handle request {requestPath} error: {ex.Message}");
                if (ex is HttpException httpException)
                {
                    await WriteHttpExceptionAsync(context, httpException, cancellationToken).ConfigureAwait(false);
                }
                else
                {
                    var response = new JsonRpcResponse();
                    if (ex is RpcException rpcException)
                    {
                        response.WriteResult(rpcException);
                    }
                    else
                    {
                        var serverError =
                            new InternalErrorException($"Handle request {requestPath} error: {ex.Message}");
                        response.WriteResult(serverError);
                    }

                    await WriteRpcResponsesAsync(context, new[] { response }, cancellationToken).ConfigureAwait(false);
                }
            }
            finally
            {
                context.Close();
            }
        }