public WebServerRequestContext(HttpListenerContext httpContext, IPAddress remoteEndpoint)
        {
            this.httpContext = httpContext;

            QueryString = QueryStringValuesCollection.FromNameValueCollection(httpContext.Request.QueryString);
            Headers     = QueryStringValuesCollection.FromNameValueCollection(httpContext.Request.Headers);

            RemoteEndpoint = remoteEndpoint;
            ForwardedUri   = Headers.GetStringOrDefault("X-Forwarded-Uri", null);

            HttpMethod = new HttpMethod(httpContext.Request.HttpMethod);
        }
        public async void Handle()
        {
            WebServerRequestContext ctx = new WebServerRequestContext(_httpContext, RemoteEndpoint);

            var httpContext = _httpContext;

            httpContext.Response.AppendHeader("Access-Control-Allow-Origin", "*");

            var props = new Dictionary <string, object>()
            {
                ["method"] = _httpContext.Request.HttpMethod,
                ["path"]   = _httpContext.Request.Url.AbsolutePath,
            };

            using (MappedDiagnosticsLogicalContext.SetScoped("client_id", ClientId))
                using (MappedDiagnosticsLogicalContext.SetScoped("remote_ip", RemoteEndpoint)) {
                    try {
                        // Parse request
                        props.Add("query", QueryStringValuesCollection.FromNameValueCollection(_httpContext.Request.QueryString).GetAsDictionary());

                        props.Add("content_type", httpContext.Request.ContentType);
                        props.Add("content_length", httpContext.Request.ContentLength64);
                        if (httpContext.Request.ContentType != null)
                        {
                            props.Add("content", await ParseKnownTypes(httpContext, ctx));
                        }

                        var match = MatchRoutes(ctx.Path, ctx.HttpMethod);
                        if (match.RouteMatch == null)
                        {
                            Logger.Trace()
                            .Message($"[{ClientId}] Not found HTTP request - {_httpContext.Request.HttpMethod} {_httpContext.Request.Url.AbsolutePath}")
                            .Properties(props)
                            .Property("status_code", 404)
                            .Write();

                            httpContext.Response.StatusCode = 404;
                            httpContext.Response.OutputStream.Close();
                            return;
                        }

                        var ep = match.Endpoint;
                        ctx.Params = match.RouteMatch.Params;

                        Logger.Trace()
                        .Message($"[{ClientId}] New HTTP request - {_httpContext.Request.HttpMethod} {_httpContext.Request.Url.AbsolutePath}")
                        .Properties(props)
                        .Write();

                        Stopwatch timer = new Stopwatch();
                        timer.Start();
                        var response = await ep.Callback(ctx);

                        ProcessingTime = timer.ElapsedMilliseconds;

                        if (response == null)
                        {
                            httpContext.Response.StatusCode      = 200;
                            httpContext.Response.ContentLength64 = 0;
                        }
                        else
                        {
                            foreach (string responseHeader in response._headers)
                            {
                                httpContext.Response.Headers.Add(responseHeader, response._headers[responseHeader]);
                            }

                            await response.WriteToResponse(this, httpContext.Response);
                        }
                    }
                    catch (UnauthorizedException) {
                        Logger.Info()
                        .Message($"[{ClientId}] Unauthorized HTTP request - {_httpContext.Request.HttpMethod} {_httpContext.Request.Url.PathAndQuery}")
                        .Properties(props)
                        .Property("status_code", 401)
                        .Write();

                        httpContext.Response.StatusCode = 401;
                    }
                    catch (Exception e) {
                        Logger.Error()
                        .Message($"[{ClientId}] Error during handling HTTP request - {_httpContext.Request.HttpMethod} {_httpContext.Request.Url.PathAndQuery}")
                        .Properties(props)
                        .Property("status_code", 500)
                        .Exception(e)
                        .Write();

                        httpContext.Response.StatusCode = 500;
                    }

                    try {
                        httpContext.Response.OutputStream.Close();
                    }
                    catch { // ignored
                    }
                }
        }