예제 #1
0
 /// <summary>
 /// Start the engine and use given router to handle request.
 /// </summary>
 /// <param name="router">The router which will handle the request.</param>
 public override void Start(IJsonRpcRouter router)
 {
     _router   = router ?? throw new ArgumentNullException(nameof(router));
     _stopped  = false;
     _listener = new HttpListener();
     _listener.Prefixes.Add(_prefix);
     _listener.UnsafeConnectionNtlmAuthentication = true;
     _listener.IgnoreWriteExceptions = true;
     _listener.Start();
     Task.Factory.StartNew(async() =>
     {
         while (!_stopped)
         {
             try
             {
                 var context = await _listener.GetContextAsync().ConfigureAwait(false);
                 if (context.Request.IsWebSocketRequest)
                 {
                     var requestPath = string.Empty;
                     if (context.Request.Url != null)
                     {
                         requestPath = context.Request.Url.AbsolutePath;
                     }
                     var websocketContext = await context.AcceptWebSocketAsync("JsonRpcLite").ConfigureAwait(false);
                     ProcessWebSocketAsync(requestPath, _router, websocketContext.WebSocket);
                 }
                 else
                 {
                     context.Response.StatusCode = 400;
                     context.Response.Close();
                 }
             }
             catch (Exception ex)
             {
                 Logger.WriteError($"GetContext error:{ex.Format()}");
             }
         }
     }, TaskCreationOptions.LongRunning);
     Logger.WriteInfo("JsonRpc websocket server engine started.");
 }
예제 #2
0
 /// <summary>
 /// Start the engine and use given router to handle request.
 /// </summary>
 /// <param name="router">The router which will handle the request.</param>
 public abstract void Start(IJsonRpcRouter router);
예제 #3
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();
            }
        }
예제 #4
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.");
            }
        }
예제 #5
0
 /// <summary>
 /// Process 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>
 private async void ProcessWebSocketAsync(string requestPath, IJsonRpcRouter router, WebSocket socket)
 {
     await HandleWebSocketAsync(requestPath, router, socket).ConfigureAwait(false);
 }