/// <summary> /// Processes the hub's incoming method calls. /// </summary> protected override Task OnReceived(IRequest request, string connectionId, string data) { HubRequest hubRequest = _requestParser.Parse(data, _serializer); // Create the hub HubDescriptor descriptor = _manager.EnsureHub(hubRequest.Hub, _counters.ErrorsHubInvocationTotal, _counters.ErrorsHubInvocationPerSec, _counters.ErrorsAllTotal, _counters.ErrorsAllPerSec); IJsonValue[] parameterValues = hubRequest.ParameterValues; // Resolve the method MethodDescriptor methodDescriptor = _manager.GetHubMethod(descriptor.Name, hubRequest.Method, parameterValues); if (methodDescriptor == null) { // Empty (noop) method descriptor // Use: Forces the hub pipeline module to throw an error. This error is encapsulated in the HubDispatcher. // Encapsulating it in the HubDispatcher prevents the error from bubbling up to the transport level. // Specifically this allows us to return a faulted task (call .fail on client) and to not cause the // transport to unintentionally fail. IEnumerable <MethodDescriptor> availableMethods = _manager.GetHubMethods(descriptor.Name, m => m.Name == hubRequest.Method); methodDescriptor = new NullMethodDescriptor(descriptor, hubRequest.Method, availableMethods); } // Resolving the actual state object var tracker = new StateChangeTracker(hubRequest.State); var hub = CreateHub(request, descriptor, connectionId, tracker, throwIfFailedToCreate: true); return(InvokeHubPipeline(hub, parameterValues, methodDescriptor, hubRequest, tracker) .ContinueWithPreservedCulture(task => hub.Dispose(), TaskContinuationOptions.ExecuteSynchronously)); }
private Task InvokeHubPipeline(IHub hub, IJsonValue[] parameterValues, MethodDescriptor methodDescriptor, HubRequest hubRequest, StateChangeTracker tracker) { // TODO: Make adding parameters here pluggable? IValueProvider? ;) HubInvocationProgress progress = GetProgressInstance(methodDescriptor, value => SendProgressUpdate(hub.Context.ConnectionId, tracker, value, hubRequest), Trace); Task <object> piplineInvocation; try { var args = _binder.ResolveMethodParameters(methodDescriptor, parameterValues); // We need to add the IProgress<T> instance after resolving the method as the resolution // itself looks for overload matches based on the incoming arg values if (progress != null) { args = args.Concat(new[] { progress }).ToList(); } var context = new HubInvokerContext(hub, tracker, methodDescriptor, args); // Invoke the pipeline and save the task piplineInvocation = _pipelineInvoker.Invoke(context); } catch (Exception ex) { piplineInvocation = TaskAsyncHelper.FromError <object>(ex); } // Determine if we have a faulted task or not and handle it appropriately. return(piplineInvocation.ContinueWithPreservedCulture(task => { if (progress != null) { // Stop ability to send any more progress updates progress.SetComplete(); } if (task.IsFaulted) { return ProcessResponse(tracker, result: null, request: hubRequest, error: task.Exception); } else if (task.IsCanceled) { return ProcessResponse(tracker, result: null, request: hubRequest, error: new OperationCanceledException()); } else { return ProcessResponse(tracker, task.Result, hubRequest, error: null); } }) .FastUnwrap()); }
public HubRequest Parse(string data, JsonSerializer serializer) { var deserializedData = serializer.Parse <HubInvocation>(data); var request = new HubRequest(); request.Hub = deserializedData.Hub; request.Method = deserializedData.Method; request.Id = deserializedData.Id; request.State = GetState(deserializedData); request.ParameterValues = (deserializedData.Args != null) ? deserializedData.Args.Select(value => new JRawValue(value)).ToArray() : _emptyArgs; return(request); }
private Task ProcessResponse(StateChangeTracker tracker, object result, HubRequest request, Exception error) { var hubResult = new HubResponse { State = tracker.GetChanges(), Result = result, Id = request.Id, }; if (error != null) { _counters.ErrorsHubInvocationTotal.Increment(); _counters.ErrorsHubInvocationPerSec.Increment(); _counters.ErrorsAllTotal.Increment(); _counters.ErrorsAllPerSec.Increment(); var hubError = error.InnerException as HubException; if (_enableDetailedErrors || hubError != null) { var exception = error.InnerException ?? error; hubResult.StackTrace = _isDebuggingEnabled ? exception.StackTrace : null; hubResult.Error = exception.Message; if (hubError != null) { hubResult.IsHubException = true; hubResult.ErrorData = hubError.ErrorData; } } else { hubResult.Error = String.Format(CultureInfo.CurrentCulture, Resources.Error_HubInvocationFailed, request.Hub, request.Method); } } Trace.TraceVerbose("Sending hub invocation result to connection {0} using transport {1}", Transport.ConnectionId, Transport.GetType().Name); return(Transport.Send(hubResult)); }
private Task SendProgressUpdate(string connectionId, StateChangeTracker tracker, object value, HubRequest request) { var hubResult = new HubResponse { State = tracker.GetChanges(), Progress = new { I = request.Id, D = value }, // We prefix the ID here to ensure old clients treat this as a hub response // but fail to lookup a corresponding callback and thus no-op Id = "P|" + request.Id, }; return(Connection.Send(connectionId, hubResult)); }