Exemplo n.º 1
0
 /// <summary>
 /// Async implementation of signal
 /// </summary>
 /// <param name="signaler">Signaler used to signal</param>
 /// <param name="input">Parameters passed from signaler</param>
 public async Task SignalAsync(ISignaler signaler, Node input)
 {
     var result = new Node();
     await signaler.ScopeAsync("slots.result", result, async() =>
     {
         var whitelist = GetWhitelist(input);
         await signaler.ScopeAsync("whitelist", whitelist.Vocabulary, async() =>
         {
             await signaler.SignalAsync("eval", whitelist.Lambda.Clone());
         });
         input.Clear();
         input.Value = result.Value;
         input.AddRange(result.Children.ToList());
     });
 }
 /// <summary>
 /// [mssql.transaction.create] slot for creating a new MS SQL database transaction.
 /// </summary>
 /// <param name="signaler">Signaler used to raise the signal.</param>
 /// <param name="input">Arguments to your slot.</param>
 /// <returns>An awaitable task.</returns>
 public async Task SignalAsync(ISignaler signaler, Node input)
 {
     await signaler.ScopeAsync(
         "mssql.transaction",
         new Transaction(signaler, signaler.Peek <SqlConnectionWrapper>("mssql.connect").Connection),
         async() => await signaler.SignalAsync("eval", input));
 }
Exemplo n.º 3
0
        /// <summary>
        /// Implementation of slot.
        /// </summary>
        /// <param name="signaler">Signaler used to raise the signal.</param>
        /// <param name="input">Arguments to slot.</param>
        /// <returns>An awaitable task.</returns>
        public async Task SignalAsync(ISignaler signaler, Node input)
        {
            // Making sure we're able to handle returned values and nodes from slot implementation.
            var result = new Node();
            await signaler.ScopeAsync("slots.result", result, async() =>
            {
                // Loading file and converting its content to lambda.
                var filename    = input.GetEx <string>();
                var hyperlambda = await _service.LoadAsync(
                    PathResolver.CombinePaths(
                        _rootResolver.RootFolder,
                        filename));
                var lambda = new Parser(hyperlambda).Lambda();

                // Preparing arguments, if there are any.
                if (input.Children.Any())
                {
                    lambda.Insert(0, new Node(".arguments", null, input.Children.ToList()));
                }
                lambda.Insert(0, new Node(".filename", filename));

                // Evaluating lambda of slot.
                await signaler.SignalAsync("eval", lambda);

                // Clearing Children collection, since it might contain input parameters.
                input.Clear();

                /*
                 * Simply setting value and children to "slots.result" stack object, since its value
                 * was initially set to its old value during startup of method.
                 */
                input.Value = result.Value;
                input.AddRange(result.Children.ToList());
            });
        }
Exemplo n.º 4
0
 /// <summary>
 /// Implementation of signal
 /// </summary>
 /// <param name="signaler">Signaler used to signal</param>
 /// <param name="input">Parameters passed from signaler</param>
 /// <returns>An awaiatble task.</returns>
 public async Task SignalAsync(ISignaler signaler, Node input)
 {
     var arguments = GetArguments(input);
     await signaler.ScopeAsync(arguments.Name, arguments.Value, async() =>
     {
         await signaler.SignalAsync("eval", arguments.Lambda);
     });
 }
Exemplo n.º 5
0
        /// <summary>
        /// Slot implementation.
        /// </summary>
        /// <param name="signaler">Signaler that raised signal.</param>
        /// <param name="input">Arguments to slot.</param>
        /// <result>Arguments to slot.</result>
        public async Task SignalAsync(ISignaler signaler, Node input)
        {
            // Making sure we're able to handle returned values and nodes from slot implementation.
            var result = new Node();
            await signaler.ScopeAsync("slots.result", result, async() =>
            {
                // Evaluating lambda of slot, making sure we temporary clear any existing [whitelist] declarations.
                var lambda = GetLambda(signaler, input);
                await signaler.ScopeAsync("whitelist", null, async() =>
                {
                    await signaler.SignalAsync("eval", lambda);
                });

                input.Clear();
                input.Value = result.Value;
                input.AddRange(result.Children.ToList());
            });
        }
Exemplo n.º 6
0
        /// <summary>
        /// Handles the signal for the class.
        /// </summary>
        /// <param name="signaler">Signaler used to signal the slot.</param>
        /// <param name="input">Root node for invocation.</param>
        /// <returns>An awaitable task.</returns>
        public async Task SignalAsync(ISignaler signaler, Node input)
        {
            using (var connection = new MySqlConnectionWrapper(GetConnectionString(input)))
            {
                await signaler.ScopeAsync(
                    "mysql.connect",
                    connection,
                    async() => await signaler.SignalAsync("eval", input));

                input.Value = null;
            }
        }
Exemplo n.º 7
0
        /// <summary>
        /// Slot implementation.
        /// </summary>
        /// <param name="signaler">Signaler that raised the signal.</param>
        /// <param name="input">Arguments to slot.</param>
        public async Task SignalAsync(ISignaler signaler, Node input)
        {
            var args = GetArgs(input);

            input.Value = await _cache.GetOrCreateAsync(args.Key, async() =>
            {
                var result = new Node();
                await signaler.ScopeAsync("slots.result", result, async() =>
                {
                    await signaler.SignalAsync("eval", args.Lambda.Clone());
                });
                return(result.GetEx <string>(), args.UtcExpires);
            });
Exemplo n.º 8
0
        /// <summary>
        /// Implementation of your slot.
        /// </summary>
        /// <param name="signaler">Signaler used to raise the signal.</param>
        /// <param name="input">Arguments to your slot.</param>
        /// <returns>An awaitable task.</returns>
        public async Task SignalAsync(ISignaler signaler, Node input)
        {
            using (var connection = new SqlConnectionWrapper(
                       Executor.GetConnectionString(
                           input,
                           "mssql",
                           "master",
                           _configuration)))
            {
                await signaler.ScopeAsync(
                    "mssql.connect",
                    connection,
                    async() => await signaler.SignalAsync("eval", input));

                input.Value = null;
            }
        }
        /// <summary>
        /// Slot implementation.
        /// </summary>
        /// <param name="signaler">Signaler that raised the signal.</param>
        /// <param name="input">Arguments to slot.</param>
        public async Task SignalAsync(ISignaler signaler, Node input)
        {
            var args = GetArgs(input);

            input.Value = await _cache.GetOrCreateAsync(args.Item1, async entry =>
            {
                var result = new Node();
                await signaler.ScopeAsync("slots.result", result, async() =>
                {
                    await signaler.SignalAsync("eval", args.Item2.Clone());
                });
                ConfigureCacheObject(entry, input);
                return(result.Value ?? result.Clone());
            });

            input.Clear();
        }
Exemplo n.º 10
0
        /// <summary>
        /// Implementation of slot.
        /// </summary>
        /// <param name="signaler">Signaler used to raise the signal.</param>
        /// <param name="input">Arguments to slot.</param>
        /// <returns>An awaitable task.</returns>
        public async Task SignalAsync(ISignaler signaler, Node input)
        {
            // Making sure we're able to handle returned values and nodes from slot implementation.
            var result = new Node();
            await signaler.ScopeAsync("slots.result", result, async() =>
            {
                // Loading file and converting its content to Hyperlambda.
                var filename    = input.GetEx <string>();
                var hyperlambda = await _service.LoadAsync(
                    PathResolver.CombinePaths(
                        _rootResolver.RootFolder,
                        filename));

                // Creating and parametrising our lambda object from argument + file's Hyperlambda content.
                var lambda = GetLambda(input, hyperlambda, filename);

                // Evaluating lambda of slot.
                await signaler.SignalAsync("eval", lambda);

                // Applying result.
                ApplyResult(input, result);
            });
        }
Exemplo n.º 11
0
        /*
         * Unrolls plugins referenced in specified content.
         */
        async Task <string> UnrollPluginsAsync(
            ISignaler signaler,
            string content,
            Node node,
            bool isMarkdown)
        {
            var result = new StringBuilder();
            var buffer = new StringBuilder();

            using (var reader = new StringReader(content))
            {
                var line = reader.ReadLine();
                while (line != null)
                {
                    // TODO: Implement true parsing, to allow for having plugins inside of e.g. attributes, etc.
                    if (line == "![[")
                    {
                        /*
                         * Hyperlambda content, reading until we find the end of it, parsing,
                         * evaluating, and adding results of evaluation into buffer.
                         *
                         * Making sure we first append content found so far in StringReader.
                         */
                        if (isMarkdown)
                        {
                            result.Append(Markdown.ToHtml(buffer.ToString()));
                        }
                        else
                        {
                            result.Append(buffer.ToString());
                        }
                        buffer.Clear();
                        line = reader.ReadLine(); // Discarding opening "![[" parts.
                        while (line != "]]!")
                        {
                            buffer.Append(line).Append("\r\n");
                            line = reader.ReadLine();
                        }

                        /*
                         * Notice, passing in all arguments as [.arguments] to all
                         * inline Hyperlambda plugin sections.
                         */
                        var lambda = new Parser(buffer.ToString()).Lambda();
                        lambda.Add(new Node(".arguments", null, node.Children.Select(x => x.Clone())));
                        buffer.Clear();
                        var evalResult = new Node();
                        await signaler.ScopeAsync(
                            "slots.result",
                            evalResult,
                            async() => await signaler.SignalAsync("wait.eval", lambda));

                        /*
                         * Assuming the plugin section returned some sort of inclusion,
                         * effectively being server side "document.write" logic.
                         */
                        result.Append(evalResult.Get <string>());
                    }
                    else
                    {
                        // Normal content.
                        buffer.Append(line).Append("\r\n");
                    }
                    line = reader.ReadLine();
                }
                if (isMarkdown)
                {
                    result.Append(Markdown.ToHtml(buffer.ToString()));
                }
                else
                {
                    result.Append(buffer.ToString());
                }
            }
            return(result.ToString());
        }
Exemplo n.º 12
0
        /// <summary>
        /// Handles the signal for the class.
        /// </summary>
        /// <param name="signaler">Signaler used to signal the slot.</param>
        /// <param name="input">Root node for invocation.</param>
        /// <returns>An awaitable task.</returns>
        public async Task SignalAsync(ISignaler signaler, Node input)
        {
            // Checking cache first.
            var key = input.GetEx <string>();

            if (!GetFromCache(key, input))
            {
                /*
                 * Making sure no more than max number of threads can
                 * execute code simultaneously. This is to avoid hundreds of
                 * simultaneous threads trying to access the database, and/or the same
                 * document at the same time, before cache has been validated,
                 * resulting in exhausting the server.
                 */
                await _semaphore.WaitAsync();

                try
                {
                    // Double checking, in case another thread was able to retrieve content first.
                    if (!GetFromCache(key, input))
                    {
                        // Evaluating [.lambda] to retrieve item to cache and return to caller.
                        var evalResult = new Node();
                        await signaler.ScopeAsync(
                            "slots.result",
                            evalResult,
                            async() => await signaler.SignalAsync("wait.eval", input.Children.First(x => x.Name == ".lambda")));

                        /*
                         * Checking to see if anything was returned at all,
                         * and if not, we don't store anything into our cache.
                         */
                        if (evalResult.Value != null || evalResult.Children.Any())
                        {
                            /*
                             * Storing into cache, notice we default cache time to 60 seconds, unless
                             * explicitly overridden as [seconds].
                             * We also clone result before adding to cache, to prevent later changes to propagate
                             * to cache value.
                             */
                            var clone = evalResult.Clone();
                            _memoryCache.Set(
                                key,
                                clone,
                                DateTimeOffset.Now.AddSeconds(
                                    input.Children.FirstOrDefault(x => x.Name == "seconds")?.GetEx <int>() ?? 60));

                            // Clearing children and value, and adding value fetched from lambda.
                            input.Clear();
                            input.Value = null;
                            input.AddRange(evalResult.Children.Select(x => x.Clone()));
                            input.Value = evalResult.Value;
                        }
                    }
                }
                finally
                {
                    _semaphore.Release();
                }
            }
        }
Exemplo n.º 13
0
        /// <inheritdoc/>
        public async Task <MagicResponse> ExecuteAsync(MagicRequest request)
        {
            // Sanity checking invocation
            if (string.IsNullOrEmpty(request.URL))
            {
                return new MagicResponse {
                           Result = 404
                }
            }
            ;

            // Making sure we never resolve to anything outside of "modules/" and "system/" folder.
            if (!request.URL.StartsWith("modules/") && !request.URL.StartsWith("system/"))
            {
                return new MagicResponse {
                           Result = 401
                }
            }
            ;

            // Figuring out file to execute, and doing some basic sanity checking.
            var path = Utilities.GetEndpointFilePath(_rootResolver, request.URL, request.Verb);

            if (!await _fileService.ExistsAsync(path))
            {
                return new MagicResponse {
                           Result = 404
                }
            }
            ;

            // Creating our lambda object by loading Hyperlambda file.
            var lambda = HyperlambdaParser.Parse(await _fileService.LoadAsync(path));

            // Applying interceptors.
            lambda = await ApplyInterceptors(lambda, request.URL);

            // Attaching arguments.
            _argumentsHandler.Attach(lambda, request.Query, request.Payload);

            // Invoking method responsible for actually executing lambda object.
            return(await ExecuteAsync(lambda, request));
        }

        #region [ -- Private helper methods -- ]

        /*
         * Applies interceptors to specified Node/Lambda object.
         */
        async Task <Node> ApplyInterceptors(Node result, string url)
        {
            // Checking to see if interceptors exists recursively upwards in folder hierarchy.
            var splits = url.Split(new char [] { '/' }, StringSplitOptions.RemoveEmptyEntries);

            // Stripping away last entity (filename) of invocation.
            var folders = splits.Take(splits.Length - 1);

            // Iterating as long as we have more entities in list of folders.
            while (true)
            {
                // Checking if "current-folder/interceptor.hl" file exists.
                var current = _rootResolver.AbsolutePath(string.Join("/", folders) + "/interceptor.hl");

                if (_fileService.Exists(current))
                {
                    result = await ApplyInterceptor(result, current);
                }

                // Checking if we're done, and at root folder, at which point we break while loop.
                if (!folders.Any())
                {
                    break; // We're done, no more interceptors!
                }
                // Traversing upwards in hierarchy to be able to nest interceptors upwards in hierarchy.
                folders = folders.Take(folders.Count() - 1);
            }

            // Returning result to caller.
            return(result);
        }

        /*
         * Applies the specified interceptor and returns the transformed Node/Lambda result.
         */
        async Task <Node> ApplyInterceptor(Node lambda, string interceptorFile)
        {
            // Getting interceptor lambda.
            var interceptNode = HyperlambdaParser.Parse(await _fileService.LoadAsync(interceptorFile));

            // Moving [.arguments] from endpoint lambda to the top of interceptor lambda if existing.
            var args = lambda
                       .Children
                       .Where(x =>
                              x.Name == ".arguments" ||
                              x.Name == ".description" ||
                              x.Name == ".type" ||
                              x.Name == "auth.ticket.verify" ||
                              x.Name.StartsWith("validators."));

            // Notice, reversing arguments nodes makes sure we apply arguments in order of appearance.
            foreach (var idx in args.Reverse().ToList())
            {
                interceptNode.Insert(0, idx); // Notice, will detach the argument from its original position!
            }

            // Moving endpoint Lambda to position before any [.interceptor] node found in interceptor lambda.
            foreach (var idxLambda in new Expression("**/.interceptor").Evaluate(interceptNode).ToList())
            {
                // Iterating through each node in current result and injecting before currently iterated [.lambda] node.
                foreach (var idx in lambda.Children)
                {
                    // This logic ensures we keep existing order without any fuzz.
                    // By cloning node we also support having multiple [.interceptor] nodes.
                    idxLambda.InsertBefore(idx.Clone());
                }

                // Removing currently iterated [.interceptor] node in interceptor lambda object.
                idxLambda.Parent.Remove(idxLambda);
            }

            // Returning interceptor Node/Lambda which is now the root of the execution Lambda object.
            return(interceptNode);
        }

        /*
         * Method responsible for actually executing lambda object after file has been loaded,
         * interceptors and arguments have been applied, and transforming result of invocation
         * to a MagicResponse.
         */
        async Task <MagicResponse> ExecuteAsync(Node lambda, MagicRequest request)
        {
            // Creating our result wrapper, wrapping whatever the endpoint wants to return to the client.
            var result   = new Node();
            var response = new MagicResponse();

            try
            {
                await _signaler.ScopeAsync("http.request", request, async() =>
                {
                    await _signaler.ScopeAsync("http.response", response, async() =>
                    {
                        await _signaler.ScopeAsync("slots.result", result, async() =>
                        {
                            await _signaler.SignalAsync("eval", lambda);
                        });
                    });
                });

                response.Content = GetReturnValue(response, result);
                return(response);
            }
            catch
            {
                if (result.Value is IDisposable disposable)
                {
                    disposable.Dispose();
                }
                if (response.Content is IDisposable disposable2 && !object.ReferenceEquals(response.Content, result.Value))
                {
                    disposable2.Dispose();
                }
                throw;
            }
        }

        /*
         * Creates a returned payload of some sort and returning to caller.
         */
        object GetReturnValue(MagicResponse httpResponse, Node lambda)
        {
            /*
             * An endpoint can return either a Node/Lambda hierarchy or a simple value.
             * First we check if endpoint returned a simple value, at which point we convert it to
             * a string. Notice, we're prioritising simple values, implying if return node has a
             * simple value, none of its children nodes will be returned.
             */
            if (lambda.Value != null)
            {
                // IDisposables (Streams e.g.) are automatically disposed by ASP.NET Core.
                if (lambda.Value is IDisposable || lambda.Value is byte[])
                {
                    return(lambda.Value);
                }

                return(lambda.Get <string>());
            }
            else if (lambda.Children.Any())
            {
                // Checking if we should return content as Hyperlambda.
                if (httpResponse.Headers.TryGetValue("Content-Type", out var val) && val == "application/x-hyperlambda")
                {
                    return(HyperlambdaGenerator.GetHyperlambda(lambda.Children));
                }

                // Defaulting to returning content as JSON by converting from Lambda to JSON.
                var convert = new Node();
                convert.AddRange(lambda.Children.ToList());
                _signaler.Signal(".lambda2json-raw", convert);
                return(convert.Value);
            }
            return(null); // No content
        }

        #endregion
    }
}