Пример #1
0
        WebMessage LoadSessionResult(XmlRequest request)
        {
            WebMessage message = request.Result.GetRow <WebMessage>("Result");

            if (message.Error != WebError.None)
            {
                throw new XAuthException(message, request);
            }

            Trace.TraceInformation("{0}", message);
            Software = request.Result.GetRow <Software>("Software");
            Session  = request.Result.GetRow <SoftwareSession>("SoftwareSession");
            return(message);
        }
Пример #2
0
        /// <summary>Performs a leave group command.</summary>
        /// <param name="groupID">The group identifier.</param>
        /// <returns></returns>
        public WebMessage GroupLeave(long groupID)
        {
            lock (this)
            {
                var request = XmlRequest.Prepare(Server, "GroupLeave", $"groupID={groupID}");
                request.Headers["Session"] = Session.ID.ToString();
                WebMessage message = request.Post();
                if (message.Error != WebError.None)
                {
                    throw new XAuthException(message, request);
                }

                return(message);
            }
        }
Пример #3
0
        /// <summary>Performs a create group command.</summary>
        /// <param name="groupName">Name of the group.</param>
        /// <returns></returns>
        public XmlDeserializer GroupCreate(string groupName)
        {
            lock (this)
            {
                var request = XmlRequest.Prepare(Server, "GroupCreate", $"groupName={groupName}");
                request.Headers["Session"] = Session.ID.ToString();
                WebMessage message = request.Post();
                if (message.Error != WebError.None)
                {
                    throw new XAuthException(message, request);
                }

                return(request.Result);
            }
        }
Пример #4
0
        /// <summary>Performs a session check. A successful check extends the session.</summary>
        public void CheckSession()
        {
            Trace.TraceInformation("Checking session {0}", Session);
            var request = XmlRequest.Prepare(Server, "CheckSession");

            request.Headers["Session"] = Session.ID.ToString();
            WebMessage message = request.Post();

            if (message.Error != WebError.None)
            {
                throw new XAuthException(message, request);
            }

            message = LoadSessionResult(request);
            OnSessionUpdated(new EventArgs());
        }
Пример #5
0
        /// <summary>Gets a transaction key.</summary>
        /// <param name="url">The URL.</param>
        /// <returns></returns>
        public TransactionKey GetTransactionKey(string url)
        {
            lock (this)
            {
                string hash    = Base64.UrlChars.Encode(Hash.FromString(Hash.Type.SHA256, url));
                var    request = XmlRequest.Prepare(Server, "GetTransactionKey", $"urlHash={hash}");
                request.Headers["Session"] = Session.ID.ToString();
                WebMessage message = request.Post();
                if (message.Error != WebError.None)
                {
                    throw new XAuthException(message, request);
                }

                return(request.Result.GetRow <TransactionKey>("TransactionKey"));
            }
        }
Пример #6
0
        /// <summary>Creates a new account.</summary>
        /// <param name="user">The user.</param>
        /// <param name="email">The email.</param>
        /// <param name="firstname">The firstname.</param>
        /// <param name="lastname">The lastname.</param>
        /// <param name="birthday">The birthday.</param>
        /// <param name="gender">The gender.</param>
        /// <returns></returns>
        public WebMessage CreateAccount(string user, string email, string firstname, string lastname, DateTime birthday, Gender gender)
        {
            lock (this)
            {
                var request = XmlRequest.Prepare(Server, "CreateAccount", $"user={user}",
                                                 $"email={email}", $"firstname={firstname}", $"lastname={lastname}", $"gender={gender}",
                                                 $"birthday={birthday.Date}");
                WebMessage message = request.Post();
                if (message.Error != WebError.None)
                {
                    throw new XAuthException(message, request);
                }

                return(message);
            }
        }
Пример #7
0
        /// <summary>Closes the current session.</summary>
        /// <returns></returns>
        public WebMessage CloseSession()
        {
            lock (this)
            {
                if (Session.IsValid())
                {
                    var        request = XmlRequest.Prepare(Server, "CloseSession");
                    WebMessage message = request.Post();
                    if (message.Error != WebError.None)
                    {
                        throw new XAuthException(message, request);
                    }

                    return(message);
                }
                return(WebMessage.Create("CloseSession", "Session was already expired!", error: WebError.None));
            }
        }
Пример #8
0
        /// <summary>Creates a new session.</summary>
        /// <param name="user">The user.</param>
        /// <param name="pass">The pass.</param>
        /// <returns></returns>
        public WebMessage CreateSession(string user, string pass)
        {
            lock (this)
            {
                var request = XmlRequest.Prepare(Server, "CreateSession");
                request.Credentials = new NetworkCredential(user, pass);
                WebMessage message = request.Post();
                if (message.Error != WebError.None)
                {
                    throw new XAuthException(message, request);
                }

                message = LoadSessionResult(request);
                timer?.Dispose();
                timer = new System.Threading.Timer(CheckSession, null, 1000 * 10, 1000 * 10);
                return(message);
            }
        }
Пример #9
0
 /// <summary>Sets a xml content.</summary>
 /// <param name="request">The request.</param>
 /// <param name="message">The message.</param>
 /// <param name="xdoc">The xdoc.</param>
 /// <param name="culture">The culture.</param>
 /// <returns>Returns a new RestContent instance.</returns>
 public static WebAnswer Xml(WebRequest request, WebMessage message, XDocument xdoc, CultureInfo culture = null)
 {
     byte[] data;
     using (var ms = new MemoryStream())
         using (var sw = new StreamWriter(ms, Encoding.GetEncoding(xdoc.Declaration.Encoding)))
         {
             xdoc.Save(sw);
             data = ms.ToArray();
         }
     return(new WebAnswer()
     {
         AllowCompression = request?.AllowCompression == true,
         Message = message,
         StatusCode = message.Code,
         ContentData = data,
         ContentType = "text/xml; charset=" + xdoc.Declaration.Encoding,
         ContentLanguage = (culture ?? Default).TwoLetterISOLanguageName,
     });
 }
Пример #10
0
        void GetStaticFileListing(WebData data)
        {
            string url     = Uri.UnescapeDataString(data.Request.DecodedUrl).Trim('/');
            string path    = FileSystem.Combine(StaticFilesPath, url);
            var    entries = new List <WebDirectoryEntry>();
            string root    = FileSystem.Combine(path, "..");

            if (FileSystem.IsRelative(root, StaticFilesPath))
            {
                FileSystemInfo info  = new DirectoryInfo(root);
                var            entry = new WebDirectoryEntry()
                {
                    DateTime = info.LastWriteTime,
                    Name     = "..",
                    Type     = WebDirectoryEntryType.Directory,
                    Link     = "/" + url + "/..",
                };
                entries.Add(entry);
            }
            if (FileSystem.IsRelative(path, StaticFilesPath))
            {
                foreach (string dir in Directory.GetDirectories(path))
                {
                    FileSystemInfo info  = new DirectoryInfo(dir);
                    var            entry = new WebDirectoryEntry()
                    {
                        DateTime = info.LastWriteTime,
                        Name     = info.Name,
                        Type     = WebDirectoryEntryType.Directory,
                        Link     = "/" + FileSystem.Combine('/', url, info.Name),
                    };
                    entries.Add(entry);
                }
                foreach (string file in Directory.GetFiles(path))
                {
                    var info  = new FileInfo(file);
                    var entry = new WebDirectoryEntry()
                    {
                        DateTime = info.LastWriteTime,
                        Size     = info.Length,
                        Name     = info.Name,
                        Type     = WebDirectoryEntryType.File,
                        Link     = "/" + FileSystem.Combine('/', url, info.Name),
                    };
                    entries.Add(entry);
                }
            }
            var pb = new HtmlPageBuilder(data.Request);

            pb.Content.CardOpenText($"File Listing:");
            pb.Content.ParagraphText($"{entries.Count} entries");
            pb.Content.TableOpen(new string[] { "Type", "Size", "Name" }, "table-striped table-responsive");
            foreach (WebDirectoryEntry entry in entries)
            {
                pb.Content.TableRowOpen();
                pb.Content.TableHtmlCell(Bootstrap4.GetBadge(entry.Type.ToString(), "badge-default"));
                pb.Content.TableCell(entry.Type == WebDirectoryEntryType.Directory ? string.Empty : entry.Size.FormatBinarySize());
                pb.Content.TableHtmlCell(Bootstrap4.GetLink(entry.Name, entry.Link));
                pb.Content.TableRowClose();
            }
            pb.Content.TableClose();
            pb.Content.CardClose();
            pb.Content.AddHtml("&nbsp;");
            data.Answer = pb.ToAnswer(WebMessage.Create("FileListing", "File listing retrieved."));
        }
Пример #11
0
 /// <summary>Initializes a new instance of the <see cref="WebServerException"/> class.</summary>
 /// <param name="message">The message.</param>
 public WebServerException(WebMessage message)
     : base(message.Content)
 {
     Error = message.Error;
     Code  = message.Code;
 }
Пример #12
0
 /// <summary>Adds a result message.</summary>
 /// <param name="message">The message.</param>
 public void AddMessage(WebMessage message)
 {
     message.ID  = ++messageCount;
     lastMessage = message;
     AddStruct(message);
 }
Пример #13
0
        /// <summary>Handles a client stage1 (preparations).</summary>
        /// <remarks>Performs the firewall checks and enters stage2.</remarks>
        internal void HandleClient(WebServerClient client)
        {
            System.Globalization.CultureInfo threadCulture = Thread.CurrentThread.CurrentCulture;
            int threadId            = Thread.CurrentThread.ManagedThreadId;
            WebResultBuilder result = null;

            try
            {
                // callback for connected client
                ClientConnected?.Invoke(this, new WebClientEventArgs(client));

                // do request handling
                int requestNumber = 0;
                if (PerformanceChecks)
                {
                    Trace.TraceInformation(
                        $"HandleClient [{threadId}] <cyan>{client.RemoteEndPoint}<default> ready to receive request. " +
                        $"Elapsed <cyan>{client.StopWatch.Elapsed.FormatTime()}<default>.");
                }
                while (client.IsConnected)
                {
                    result = null;
                    if (PerformanceChecks && requestNumber > 0)
                    {
                        Trace.TraceInformation(
                            $"HandleClient [{threadId}] <cyan>{client.RemoteEndPoint}<default> request <green>{requestNumber}<default> handling completed. " +
                            $"Elapsed <cyan>{client.StopWatch.Elapsed.FormatTime()}<default>.");
                    }

                    // read first request line
                    string firstLine = client.Reader.ReadLine();
                    client.StopWatch.Reset();
                    if (PerformanceChecks)
                    {
                        Trace.TraceInformation(
                            $"HandleClient [{threadId}] <cyan>{client.RemoteEndPoint}<default> start handling request <cyan>{++requestNumber}<default>. " +
                            $"Elapsed <cyan>{client.StopWatch.Elapsed.FormatTime()}<default>.");
                    }

                    // load request
                    var request = WebRequest.Load(this, firstLine, client);

                    // prepare web data object
                    var data = new WebData(request, client.StopWatch);
                    result = data.Result;

                    // update thread culture
                    Thread.CurrentThread.CurrentCulture = data.Request.Culture;

                    // handle request but change some default exceptions to web exceptions
                    try { HandleRequest(client, data); }
                    catch (ObjectDisposedException)
                    {
                        Trace.TraceInformation($"HandleClient [{threadId}] <red>{client.RemoteEndPoint}<default> Connection closed");
                    }
                    catch (InvalidOperationException ex) { throw new WebServerException(ex, WebError.InvalidOperation, 0, ex.Message); }
                    catch (ArgumentException ex) { throw new WebServerException(ex, WebError.InvalidParameters, 0, ex.Message); }
                }
            }
            catch (WebServerException ex)
            {
                Trace.TraceInformation(ex.ToString());
                if (result == null)
                {
                    result = new WebResultBuilder(this);
                }

                result.AddMessage(WebMessage.Create(ex));
                if (ex.Error == WebError.AuthenticationRequired || ex.Error == WebError.InvalidTransactionKey)
                {
                    result.Headers["WWW-Authenticate"] = $"Basic realm=\"{AssemblyVersionInfo.Program.Company} - {AssemblyVersionInfo.Program.Product}\"";
                }
                result.CloseAfterAnswer = true;
                client.SendAnswer(result.ToAnswer());
            }
            catch (SocketException)
            {
                Trace.TraceInformation($"HandleClient [{threadId}] <red>{client.RemoteEndPoint}<default> Connection closed");
                /*client closed connection*/
            }
            catch (EndOfStreamException)
            {
                /*client closed connection*/
                Trace.TraceInformation($"HandleClient [{threadId}] <red>{client.RemoteEndPoint}<default> Connection closed");
            }
            catch (Exception ex)
            {
                if (ex.InnerException is SocketException)
                {
                    Trace.TraceInformation($"HandleClient [{threadId}] <red>{client.RemoteEndPoint}<default> Connection closed");
                    return;
                }

                string supportCode = Base32.Safe.Encode(Environment.TickCount);
                Trace.TraceError("<red>Unhandled Internal Server Error<default> Code {1}\n{0}", ex.ToString(), supportCode);

                if (result == null)
                {
                    result = new WebResultBuilder(this);
                }

                result.AddMessage(ex.Source, WebError.InternalServerError, $"Internal Server Error\nUnexpected result on request.\nPlease contact support!\nSupport Code = {supportCode}");
                result.CloseAfterAnswer = true;
                client.SendAnswer(result.ToAnswer());
            }
            finally
            {
                while (client.IsConnected && client.Reader.Available == 0)
                {
                    Thread.Sleep(1);
                }

                client.Close();
                if (client != null)
                {
                    ClientDisconnected?.Invoke(this, new WebClientEventArgs(client));
                }

                // reset thread culture
                if (Thread.CurrentThread.CurrentCulture != threadCulture)
                {
                    Thread.CurrentThread.CurrentCulture = threadCulture;
                }
            }
        }
Пример #14
0
 /// <summary>Adds a result message.</summary>
 /// <param name="method">The method.</param>
 /// <param name="message">The message.</param>
 /// <param name="args">The arguments.</param>
 public void AddMessage(WebServerMethod method, string message, params object[] args)
 {
     AddMessage(WebMessage.Create(method, string.Format(message, args)));
 }
Пример #15
0
 /// <summary>Adds a result message.</summary>
 /// <param name="source">The source.</param>
 /// <param name="message">The message.</param>
 /// <param name="args">The arguments.</param>
 public void AddMessage(string source, string message, params object[] args)
 {
     AddMessage(WebMessage.Create(source, string.Format(message, args)));
 }
Пример #16
0
 /// <summary>Adds a result message.</summary>
 /// <param name="source">The source.</param>
 /// <param name="error">The error.</param>
 /// <param name="code">The code.</param>
 /// <param name="message">The message.</param>
 /// <param name="args">The arguments.</param>
 public void AddMessage(string source, WebError error, HttpStatusCode code, string message, params object[] args)
 {
     AddMessage(WebMessage.Create(source, string.Format(message, args), error: error, code: code));
 }
Пример #17
0
 /// <summary>Adds a result message.</summary>
 /// <param name="method">The method.</param>
 /// <param name="error">The error.</param>
 /// <param name="code">The code.</param>
 /// <param name="message">The message.</param>
 /// <param name="args">The arguments.</param>
 public void AddMessage(WebServerMethod method, WebError error, HttpStatusCode code, string message, params object[] args)
 {
     AddMessage(WebMessage.Create(method, string.Format(message, args), error: error, code: code));
 }
Пример #18
0
        void ExplainFunction(WebData data, WebServerMethod function)
        {
            var html = new HtmlPageBuilder(data.Request);
            {
                string   path  = string.Empty;
                string[] parts = function.FullPaths.First().Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
                int      last  = parts.Length - 1;
                for (int n = 0; n < parts.Length; n++)
                {
                    path += "/" + parts[n];
                    html.Breadcrump.Add(new WebLink()
                    {
                        Text = parts[n], Link = (n != last) ? $"/Explain?functions={path}" : $"/Explain?function={path}"
                    });
                }
            }

            Bootstrap4        content  = html.Content;
            WebServerAuthType authType = function.PageAttribute?.AuthType ?? WebServerAuthType.None;
            {
                string link = function.FullPaths.First();
                var    head = new Bootstrap4();
                if (authType != WebServerAuthType.None)
                {
                    // head.DivOpen(Bootstrap4.Item.float_right);
                    head.DivOpen(Bootstrap4.Item.float_right);
                    AddBadges(head, function.PageAttribute);
                    head.DivClose(Bootstrap4.Item.float_right);

                    // head.AddHtml("<br/>");
                }
                head.AddHtml("<h2>");
                head.AddHtml(function.Method.Name.SplitCamelCase().Join("&nbsp;"));
                if (function.Parameters.Length > 0)
                {
                    head.AddHtml(" (");
                    head.AddHtml(function.ParameterString());
                    head.AddHtml(")");
                }
                head.AddHtml("</h2>");

                head.DivOpen(Bootstrap4.Item.float_right);
                head.Link("html", link + ".html", "btn btn-sm btn-outline-primary");
                head.Link("json", link + ".json", "btn btn-sm btn-outline-primary");
                head.Link("xml", link + ".xml", "btn btn-sm btn-outline-primary");
                head.Link("plain", link + ".txt", "btn btn-sm btn-outline-primary");
                head.DivClose(Bootstrap4.Item.float_right);

                head.AddHtml(function.Method.DeclaringType.AssemblyQualifiedName);
                content.CardOpen(head.ToString());
            }
            XNetDocItem doc = documentation.GetMethod(function.Method);

            DocumentHtml(content, doc, function.IsAction ? "Generic action" : function.Method.ToString());
            content.ListGroupOpen();
            int i = 0;

            foreach (ParameterInfo parameter in function.Parameters)
            {
                if (parameter.ParameterType == typeof(WebData))
                {
                    continue;
                }

                ParameterHtml(content, i++, parameter, doc);
            }
            content.ListGroupClose();
            content.CardClose();
            content.AddHtml("&nbsp;");
            var message = WebMessage.Create("Explain " + function.Name, string.Format("Explain function {0}", function));

            data.Answer = html.ToAnswer(message);
        }
Пример #19
0
        void ExplainFunctionList(WebData data)
        {
            var html = new HtmlPageBuilder(data.Request);

            IEnumerable <KeyValuePair <string, WebServerMethod> > paths = data.Server.RegisteredPaths;

            if (data.Request.Parameters.TryGetValue("functions", out string functions))
            {
                string   path  = string.Empty;
                string[] parts = functions.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
                for (int n = 0; n < parts.Length; n++)
                {
                    path += "/" + parts[n];
                    html.Breadcrump.Add(new WebLink()
                    {
                        Text = parts[n], Link = $"/Explain?functions={path}"
                    });
                }
                paths = paths.Where(p => p.Key.StartsWith(functions));
            }

            Bootstrap4 content = html.Content;

            content.ListGroupOpen();
            int i = 0;

            ILookup <WebServerMethod, string> lookup = paths.ToLookup(p => p.Value, p => p.Key);

            foreach (IGrouping <WebServerMethod, string> item in lookup)
            {
                WebServerMethod function = item.Key;

                // if (item.Key == "/") continue;
                content.ListGroupItemOpen(i++ % 2 == 0 ? " list-group-item-info" : null);
                XNetDocItem doc = documentation.GetMethod(function.Method);

                content.AddHtml("<div style=\"width:100%\">");
                content.DivOpen(Bootstrap4.Item.row);
                WebServerAuthType authType = function.PageAttribute?.AuthType ?? WebServerAuthType.None;
                if (authType != WebServerAuthType.None)
                {
                    content.DivOpen(Bootstrap4.Item.col, "col-12 col-sm-auto flex-sm-last");
                    AddBadges(content, function.PageAttribute);
                    content.DivClose(Bootstrap4.Item.col);
                }
                content.DivOpen(Bootstrap4.Item.col);
                content.AddHtml("<h4>");
                content.AddHtml(function.Method.Name.SplitCamelCase().Join(" "));
                if (function.Parameters.Length > 0)
                {
                    content.AddHtml(" (");
                    content.AddHtml(function.ParameterString());
                    content.AddHtml(")");
                }
                content.AddHtml("</h4>");
                content.DivClose(Bootstrap4.Item.col);
                content.DivClose(Bootstrap4.Item.row);

                foreach (string path in item)
                {
                    content.DivOpen(Bootstrap4.Item.row);
                    content.DivOpen(Bootstrap4.Item.col);
                    content.Link(path, $"Explain?function={path}");
                    content.DivClose(Bootstrap4.Item.col);

                    content.DivOpen(Bootstrap4.Item.col, "col-12 col-sm-auto");
                    content.Link("html", path + ".html", "btn btn-sm btn-outline-primary");
                    content.Link("json", path + ".json", "btn btn-sm btn-outline-primary");
                    content.Link("xml", path + ".xml", "btn btn-sm btn-outline-primary");
                    content.Link("plain", path + ".txt", "btn btn-sm btn-outline-primary");
                    content.DivClose(Bootstrap4.Item.col);
                    content.DivClose(Bootstrap4.Item.row);
                }

                if (doc?.Summary != null)
                {
                    content.DivOpen(Bootstrap4.Item.row);
                    content.DivOpen(Bootstrap4.Item.col);
                    content.AddHtml("<strong>Description:</strong>");
                    int cdata = doc.Summary.IndexOf("<![CDATA[");
                    if (cdata > -1)
                    {
                        content.Text(doc.Summary.Substring(0, cdata));
                        content.AddHtml("<br/><code>");
                        string code     = doc.Summary.Substring(cdata + 9);
                        int    cdataEnd = code.IndexOf("]]>");
                        content.AddHtml(code.Substring(0, cdata));
                        content.AddHtml("</code>");
                        content.Text(doc.Summary.Substring(cdata + cdataEnd + 9 + 3));
                    }
                    else
                    {
                        content.Text(doc.Summary);
                    }
                    content.DivClose(Bootstrap4.Item.col);
                    content.DivClose(Bootstrap4.Item.row);
                }
                content.AddHtml("</div>");
                content.ListGroupItemClose();
            }
            content.ListGroupClose();
            content.AddHtml("&nbsp;");
            var message = WebMessage.Create("Explain", functions == null ? "Explain functions." : $"Explain {functions} functions.");

            data.Answer = html.ToAnswer(message);
        }
Пример #20
0
        /// <summary>Builds the template.</summary>
        /// <param name="data">The data.</param>
        /// <returns>Returns true on success, false otherwise.</returns>
        public bool Render(WebData data)
        {
            if (data.Server != server)
            {
                throw new ArgumentOutOfRangeException(nameof(data.Server));
            }

            { // need reload ?
                DateTime lastChanged = FileSystem.GetLastWriteTimeUtc(FileName);
                if (lastChanged != LastChanged)
                {
                    Reload();
                }
            }

            // do auth and load user session (if any)
            data.Result.SkipMainObject = true;
            data.Result.TransmitLayout = false;

            // call functions
            IDictionary <string, string> templateParameters = data.Request.Parameters;
            var tables = new Set <string>();
            var parameterDescription = new List <WebTemplateParameter>();

            for (int i = 0; i < functions.Length; i++)
            {
                Func function = functions[i];
                parameterDescription.AddRange(function.ParameterDescriptions);
                if (function.NeededParameters.Count > 0)
                {
                    // foreach neededparameters, any parameter is not at tmeplate parameters -> continue
                    if (function.NeededParameters.Where(n => !templateParameters.ContainsKey(n)).Any())
                    {
                        continue;
                    }
                }
                var functionParameters = new Dictionary <string, string>();
                foreach (System.Reflection.ParameterInfo methodParameter in function.Method.Parameters)
                {
                    // lookup function parameter name from function section at template
                    if (!function.Parameters.TryGetValue(methodParameter.Name, out string templateParameterName))
                    {
                        continue;
                    }

                    // parameter name at template could be loaded
                    if (!templateParameters.TryGetValue(templateParameterName, out string parameterValue))
                    {
                        if (!methodParameter.IsOptional)
                        {
                            // no value given and is not optional
                            throw new WebServerException(WebError.InvalidParameters, $"Template error: Missing {methodParameter.Name} is not for function {function} is not set. Define {templateParameterName} at template call!");
                        }
                        continue;
                    }
                    functionParameters[methodParameter.Name] = parameterValue;
                }
                data.Request.Parameters = new ReadOnlyDictionary <string, string>(functionParameters);

                // invoke method
                data.Method = function.Method;
                data.Server.CallMethod(data);
            }

            Stopwatch renderWatch = server.PerformanceChecks ? Stopwatch.StartNew() : null;

            // replace content
            byte[] result = staticData ?? BuildStaticData(content);

            if (renderWatch != null)
            {
                Trace.TraceInformation("Template static data generation took {0}", renderWatch.Elapsed.FormatTime());
            }

            // render data
            {
                data.Result.Type = WebResultType.Json;
                data.Result.AddStructs(parameterDescription);
                WebAnswer answer = data.Result.ToAnswer();
                result = result.ReplaceFirst(Tag, ScriptStart, answer.ContentData, ScriptEnd);
            }

            // set result
            data.Result = null;

            WebMessage message;

            if (data.Method != null)
            {
                message = WebMessage.Create(data.Method, $"Template call <cyan>{data.Request}<default> succeeded.");
            }
            else
            {
                message = WebMessage.Create($"Static {data.Request.PlainUrl}", $"Template call <cyan>{data.Request}<default> succeeded.");
            }

            data.Answer = WebAnswer.Raw(data.Request, message, result, "text/html");
            return(true);
        }