/// <summary>
        /// Searches the specified <paramref name="hub">Hub</paramref> for the specified <paramref name="method"/>.
        /// </summary>
        /// <remarks>
        /// In the case that there are multiple overloads of the specified <paramref name="method"/>, the <paramref name="parameter">parameter set</paramref> helps determine exactly which instance of the overload should be resolved. 
        /// If there are multiple overloads found with the same number of matching paramters, none of the methods will be returned because it is not possible to determine which overload of the method was intended to be resolved.
        /// </remarks>
        /// <param name="hub">Hub to search for the specified <paramref name="method"/> on.</param>
        /// <param name="method">The method name to search for.</param>
        /// <param name="descriptor">If successful, the <see cref="MethodDescriptor"/> that was resolved.</param>
        /// <param name="parameters">The set of parameters that will be used to help locate a specific overload of the specified <paramref name="method"/>.</param>
        /// <returns>True if the method matching the name/parameter set is found on the hub, otherwise false.</returns>
        public bool TryGetMethod(HubDescriptor hub, string method, out MethodDescriptor descriptor, params IJsonValue[] parameters)
        {
            string hubMethodKey = BuildHubExecutableMethodCacheKey(hub, method, parameters);

            if (!_executableMethods.TryGetValue(hubMethodKey, out descriptor))
            {
                IEnumerable<MethodDescriptor> overloads;

                if (FetchMethodsFor(hub).TryGetValue(method, out overloads))
                {
                    var matches = overloads.Where(o => o.Matches(parameters)).ToList();

                    // If only one match is found, that is the "executable" version, otherwise none of the methods can be returned because we don't know which one was actually being targeted
                    descriptor = matches.Count == 1 ? matches[0] : null;
                }
                else
                {
                    descriptor = null;
                }

                // If an executable method was found, cache it for future lookups (NOTE: we don't cache null instances because it could be a surface area for DoS attack by supplying random method names to flood the cache)
                if (descriptor != null)
                {
                    _executableMethods.TryAdd(hubMethodKey, descriptor);
                }
            }

            return descriptor != null;
        }
        public bool TryGetMethod(HubDescriptor hub, string method, out MethodDescriptor descriptor, params JToken[] parameters)
        {
            IEnumerable<MethodDescriptor> overloads;

            if(FetchMethodsFor(hub).TryGetValue(method, out overloads))
            {
                var matches = overloads.Where(o => o.Matches(parameters)).ToList();
                if(matches.Count == 1)
                {
                    descriptor = matches.First();
                    return true;
                }
            }

            descriptor = null;
            return false;
        }
 /// <summary>
 /// Resolves method parameter values based on provided objects.
 /// </summary>
 /// <param name="method">Method descriptor.</param>
 /// <param name="values">List of values to resolve parameter values from.</param>
 /// <returns>Array of parameter values.</returns>
 public virtual object[] ResolveMethodParameters(MethodDescriptor method, params JToken[] values)
 {
     return method.Parameters.Zip(values, ResolveParameter).ToArray();
 }
Beispiel #4
0
 private static string GetMethodName(MethodDescriptor method)
 {
     return(Json.CamelCase(method.Name));
 }
Beispiel #5
0
 /// <summary>
 /// Resolves method parameter values based on provided objects.
 /// </summary>
 /// <param name="method">Method descriptor.</param>
 /// <param name="values">List of values to resolve parameter values from.</param>
 /// <returns>Array of parameter values.</returns>
 public virtual object[] ResolveMethodParameters(MethodDescriptor method, params IJsonValue[] values)
 {
     return(method.Parameters.Zip(values, ResolveParameter).ToArray());
 }
Beispiel #6
0
        /// <summary>
        /// Processes the hub's incoming method calls.
        /// </summary>
        protected override Task OnReceivedAsync(IRequest request, string connectionId, string data)
        {
            HubRequest hubRequest = _requestParser.Parse(data);

            // Create the hub
            HubDescriptor descriptor = _manager.EnsureHub(hubRequest.Hub);

            IJsonValue[] parameterValues = hubRequest.ParameterValues;

            // Resolve the method
            MethodDescriptor methodDescriptor = _manager.GetHubMethod(descriptor.Name, hubRequest.Method, parameterValues);

            if (methodDescriptor == null)
            {
                throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "'{0}' method could not be resolved.", hubRequest.Method));
            }

            // Resolving the actual state object
            var state = new TrackingDictionary(hubRequest.State);
            var hub   = CreateHub(request, descriptor, connectionId, state, throwIfFailedToCreate: true);

            Task resultTask;

            try
            {
                // Invoke the method
                object result     = methodDescriptor.Invoker.Invoke(hub, _binder.ResolveMethodParameters(methodDescriptor, parameterValues));
                Type   returnType = result != null?result.GetType() : methodDescriptor.ReturnType;

                if (typeof(Task).IsAssignableFrom(returnType))
                {
                    var task = (Task)result;
                    if (!returnType.IsGenericType)
                    {
                        return(task.ContinueWith(t => ProcessResponse(state, null, hubRequest, t.Exception))
                               .FastUnwrap());
                    }
                    else
                    {
                        // Get the <T> in Task<T>
                        Type resultType = returnType.GetGenericArguments().Single();

                        // Get the correct ContinueWith overload
                        var continueWith = TaskAsyncHelper.GetContinueWith(task.GetType());

                        var taskParameter       = Expression.Parameter(continueWith.Type);
                        var processResultMethod = typeof(HubDispatcher).GetMethod("ProcessTaskResult", BindingFlags.NonPublic | BindingFlags.Instance).MakeGenericMethod(resultType);

                        var body = Expression.Call(Expression.Constant(this),
                                                   processResultMethod,
                                                   Expression.Constant(state),
                                                   Expression.Constant(hubRequest),
                                                   taskParameter);

                        var lambda = Expression.Lambda(body, taskParameter);

                        var call = Expression.Call(Expression.Constant(task, continueWith.Type), continueWith.Method, lambda);
                        Func <Task <Task> > continueWithMethod = Expression.Lambda <Func <Task <Task> > >(call).Compile();
                        return(continueWithMethod.Invoke().FastUnwrap());
                    }
                }
                else
                {
                    resultTask = ProcessResponse(state, result, hubRequest, null);
                }
            }
            catch (TargetInvocationException e)
            {
                resultTask = ProcessResponse(state, null, hubRequest, e);
            }

            return(resultTask.Then(() => base.OnReceivedAsync(request, connectionId, data))
                   .Catch());
        }
Beispiel #7
0
        private Task InvokeHubPipeline(IRequest request, string connectionId, string data, HubRequest hubRequest, IJsonValue[] parameterValues, MethodDescriptor methodDescriptor, TrackingDictionary state, IHub hub)
        {
            var args    = _binder.ResolveMethodParameters(methodDescriptor, parameterValues);
            var context = new HubInvokerContext(hub, state, methodDescriptor, args);

            // Invoke the pipeline
            return(_pipelineInvoker.Invoke(context)
                   .ContinueWith(task =>
            {
                if (task.IsFaulted)
                {
                    return ProcessResponse(state, null, hubRequest, task.Exception);
                }
                else
                {
                    return ProcessResponse(state, task.Result, hubRequest, null);
                }
            })
                   .FastUnwrap());
        }