private ProcessSchedulerDelegate BuildRequestDelegate(IRequestDescriptor <IHandlerDescriptor?> descriptors, Request request, RequestInvocationHandle handle) { return(async(cancellationToken) => { try { var result = await InvokeRequestAsync(cancellationToken).ConfigureAwait(false); _outputHandler.Send(result.Value); } finally { handle.Dispose(); } }); async Task <ErrorResponse> InvokeRequestAsync(CancellationToken cancellationToken) { using var timeoutCts = new CancellationTokenSource(_requestTimeout); using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(handle.CancellationTokenSource.Token, timeoutCts.Token, cancellationToken); using var timer = _logger.TimeDebug("Processing request {Method}:{Id}", request.Method, request.Id); try { var result = await _requestRouter.RouteRequest(descriptors, request, combinedCts.Token).ConfigureAwait(false); return(result); } catch (OperationCanceledException) { if (timeoutCts.IsCancellationRequested) { _logger.LogTrace("Request {Method}:{Id} was cancelled, due to timeout", request.Method, request.Id); return(new RequestCancelled(request.Id, request.Method)); } _logger.LogTrace("Request {Method}:{Id} was cancelled", request.Method, request.Id); return(new RequestCancelled(request.Id, request.Method)); } catch (RpcErrorException e) { _logger.LogCritical(Events.UnhandledRequest, e, "Failed to handle request {Method}:{Id}", request.Method, request.Id); return(new RpcError( request.Id, request.Method, new ErrorMessage(e.Code, e.Message, e.Error))); } catch (Exception e) { _logger.LogCritical(Events.UnhandledRequest, e, "Failed to handle request {Method}:{Id}. Unhandled exception", request.Method, request.Id); return(new InternalError(request.Id, request.Method, e.ToString())); } } }
private void HandleRequest(string request) { JToken payload; try { payload = JToken.Parse(request); } catch { _outputHandler.Send(new ParseError()); return; } if (!_receiver.IsValid(payload)) { _outputHandler.Send(new InvalidRequest()); return; } var(requests, hasResponse) = _receiver.GetRequests(payload); if (hasResponse) { foreach (var response in requests.Where(x => x.IsResponse).Select(x => x.Response)) { var id = response.Id is string s?long.Parse(s) : response.Id is long l ? l : -1; if (id < 0) { continue; } var tcs = _responseRouter.GetRequest(id); if (tcs is null) { continue; } if (response is ServerResponse serverResponse) { tcs.SetResult(serverResponse.Result); } else if (response is ServerError serverError) { tcs.SetException(new JsonRpcException(serverError)); } } return; } foreach (var item in requests) { if (item.IsRequest) { var descriptor = _requestRouter.GetDescriptor(item.Request); if (descriptor is null) { continue; } var type = _requestProcessIdentifier.Identify(descriptor); _requestRouter.StartRequest(item.Request.Id); _scheduler.Add( type, item.Request.Method, async() => { try { var result = await _requestRouter.RouteRequest(descriptor, item.Request, CancellationToken.None); if (result.IsError && result.Error is RequestCancelled) { return; } _outputHandler.Send(result.Value); } catch (Exception e) { _logger.LogCritical(Events.UnhandledRequest, e, "Unhandled exception executing request {Method}@{Id}", item.Request.Method, item.Request.Id); // TODO: Should we rethrow or swallow? // If an exception happens... the whole system could be in a bad state, hence this throwing currently. throw; } } ); } if (item.IsNotification) { var descriptor = _requestRouter.GetDescriptor(item.Notification); if (descriptor is null) { continue; } // We need to special case cancellation so that we can cancel any request that is currently in flight. if (descriptor.Method == JsonRpcNames.CancelRequest) { var cancelParams = item.Notification.Params?.ToObject <CancelParams>(); if (cancelParams == null) { continue; } _requestRouter.CancelRequest(cancelParams.Id); continue; } var type = _requestProcessIdentifier.Identify(descriptor); _scheduler.Add( type, item.Notification.Method, DoNotification(descriptor, item.Notification) ); } if (item.IsError) { // TODO: _outputHandler.Send(item.Error); } } Func <Task> DoNotification(IHandlerDescriptor descriptor, Notification notification) { return(async() => { try { await _requestRouter.RouteNotification(descriptor, notification, CancellationToken.None); } catch (Exception e) { _logger.LogCritical(Events.UnhandledNotification, e, "Unhandled exception executing notification {Method}", notification.Method); // TODO: Should we rethrow or swallow? // If an exception happens... the whole system could be in a bad state, hence this throwing currently. throw; } }); } }
private SchedulerDelegate RouteRequest( IRequestDescriptor <IHandlerDescriptor?> descriptor, Request request, RequestInvocationHandle handle) { var cts = handle.CancellationTokenSource; return((contentModifiedToken, scheduler) => Observable.Create <ErrorResponse>( observer => { // ITS A RACE! var sub = Observable.Amb( contentModifiedToken.Select( _ => { _logger.LogTrace( "Request {Id} was abandoned due to content be modified", request.Id ); return new ErrorResponse( new ContentModified(request.Id, request.Method) ); } ), Observable.Timer(_options.RequestTimeout, scheduler).Select( _ => new ErrorResponse(new RequestCancelled(request.Id, request.Method)) ), Observable.FromAsync( async ct => { using var timer = _logger.TimeDebug( "Processing request {Method} {ResponseId}", request.Method, request.Id ); ct.Register(cts.Cancel); // ObservableToToken(contentModifiedToken).Register(cts.Cancel); try { var result = await _requestRouter.RouteRequest( descriptor, request, cts.Token ).ConfigureAwait(false); return result; } catch (OperationCanceledException) { _logger.LogTrace("Request {Id} was cancelled", request.Id); return new RequestCancelled(request.Id, request.Method); } catch (RpcErrorException e) { _logger.LogCritical( Events.UnhandledRequest, e, "Failed to handle request {Method} {RequestId}", request.Method, request.Id ); return new RpcError( request.Id, request.Method, new ErrorMessage(e.Code, e.Message, e.Error) ); } catch (Exception e) { _logger.LogCritical( Events.UnhandledRequest, e, "Failed to handle request {Method} {RequestId}", request.Method, request.Id ); return new InternalError(request.Id, request.Method, e.ToString()); } } ) ) .Subscribe(observer); return new CompositeDisposable(sub, handle); } ) .Select( response => { _outputHandler.Send(response.Value); return Unit.Default; } )); }
private void HandleRequest(string request) { JToken payload; try { payload = JToken.Parse(request); } catch { _outputHandler.Send(new ParseError()); return; } if (!_reciever.IsValid(payload)) { _outputHandler.Send(new InvalidRequest()); return; } var(requests, hasResponse) = _reciever.GetRequests(payload); if (hasResponse) { foreach (var response in requests.Where(x => x.IsResponse).Select(x => x.Response)) { var id = response.Id is string s?long.Parse(s) : response.Id is long l ? l : -1; if (id < 0) { continue; } var tcs = _responseRouter.GetRequest(id); if (tcs is null) { continue; } if (response is ServerResponse serverResponse) { tcs.SetResult(serverResponse.Result); } else if (response is ServerError serverError) { tcs.SetException(new Exception(JsonConvert.SerializeObject(serverError.Error))); } } return; } foreach (var item in requests) { if (item.IsRequest) { var descriptor = _requestRouter.GetDescriptor(item.Request); if (descriptor is null) { continue; } var type = _requestProcessIdentifier.Identify(descriptor); _scheduler.Add( type, item.Request.Method, async() => { try { var result = await _requestRouter.RouteRequest(descriptor, item.Request); _outputHandler.Send(result.Value); } catch (Exception e) { _logger.LogCritical(Events.UnhandledRequest, e, "Unhandled exception executing request {Method}@{Id}", item.Request.Method, item.Request.Id); // TODO: Should we rethrow or swallow? // If an exception happens... the whole system could be in a bad state, hence this throwing currently. throw; } } ); } else if (item.IsNotification) { var descriptor = _requestRouter.GetDescriptor(item.Notification); if (descriptor is null) { continue; } var type = _requestProcessIdentifier.Identify(descriptor); _scheduler.Add( type, item.Notification.Method, async() => { try { await _requestRouter.RouteNotification(descriptor, item.Notification); } catch (Exception e) { _logger.LogCritical(Events.UnhandledNotification, e, "Unhandled exception executing notification {Method}", item.Notification.Method); // TODO: Should we rethrow or swallow? // If an exception happens... the whole system could be in a bad state, hence this throwing currently. throw; } } ); } else if (item.IsError) { // TODO: _outputHandler.Send(item.Error); } } }