/// <summary>
        /// Retrieve a static route.
        /// </summary>
        /// <param name="method">The HTTP method.</param>
        /// <param name="path">URL path.</param>
        /// <returns>StaticRoute if the route exists, otherwise null.</returns>
        public StaticRoute Get(HttpMethod method, string path)
        {
            if (String.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException(nameof(path));
            }

            path = path.ToLower();
            if (!path.StartsWith("/"))
            {
                path = "/" + path;
            }
            if (!path.EndsWith("/"))
            {
                path = path + "/";
            }

            lock (_Lock)
            {
                StaticRoute curr = _Routes.FirstOrDefault(i => i.Method == method && i.Path == path);
                if (curr == null || curr == default(StaticRoute))
                {
                    return(null);
                }
                else
                {
                    return(curr);
                }
            }
        }
        private void Add(StaticRoute route)
        {
            if (route == null)
            {
                throw new ArgumentNullException(nameof(route));
            }

            route.Path = route.Path.ToLower();
            if (!route.Path.StartsWith("/"))
            {
                route.Path = "/" + route.Path;
            }
            if (!route.Path.EndsWith("/"))
            {
                route.Path = route.Path + "/";
            }

            if (Exists(route.Method, route.Path))
            {
                return;
            }

            lock (_Lock)
            {
                _Routes.Add(route);
            }
        }
        public StaticRoute Get(string verb, string path)
        {
            if (String.IsNullOrEmpty(verb))
            {
                throw new ArgumentNullException(nameof(verb));
            }
            if (String.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException(nameof(path));
            }

            verb = verb.ToLower();
            path = path.ToLower();
            if (!path.StartsWith("/"))
            {
                path = "/" + path;
            }
            if (!path.EndsWith("/"))
            {
                path = path + "/";
            }

            lock (RouteLock)
            {
                StaticRoute curr = Routes.FirstOrDefault(i => i.Verb == verb && i.Path == path);
                if (curr == null || curr == default(StaticRoute))
                {
                    return(null);
                }
                else
                {
                    return(curr);
                }
            }
        }
        public void Remove(string verb, string path)
        {
            if (String.IsNullOrEmpty(verb))
            {
                throw new ArgumentNullException(nameof(verb));
            }
            if (String.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException(nameof(path));
            }

            StaticRoute r = Get(verb, path);

            if (r == null || r == default(StaticRoute))
            {
                return;
            }
            else
            {
                lock (RouteLock)
                {
                    Routes.Remove(r);
                }

                return;
            }
        }
        /// <summary>
        /// Match a request method and URL to a handler method.
        /// </summary>
        /// <param name="method">The HTTP method.</param>
        /// <param name="path">URL path.</param>
        /// <param name="route">Matching route.</param>
        /// <returns>Method to invoke.</returns>
        public Func <HttpContext, Task> Match(HttpMethod method, string path, out StaticRoute route)
        {
            route = null;
            if (String.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException(nameof(path));
            }

            path = path.ToLower();
            if (!path.StartsWith("/"))
            {
                path = "/" + path;
            }
            if (!path.EndsWith("/"))
            {
                path = path + "/";
            }

            lock (_Lock)
            {
                StaticRoute curr = _Routes.FirstOrDefault(i => i.Method == method && i.Path == path);
                if (curr == null || curr == default(StaticRoute))
                {
                    return(null);
                }
                else
                {
                    route = curr;
                    return(curr.Handler);
                }
            }
        }
        private void Remove(StaticRoute route)
        {
            if (route == null)
            {
                throw new ArgumentNullException(nameof(route));
            }

            lock (_Lock)
            {
                _Routes.Remove(route);
            }

            return;
        }
        /// <summary>
        /// Add a route.
        /// </summary>
        /// <param name="method">The HTTP method.</param>
        /// <param name="path">URL path, i.e. /path/to/resource.</param>
        /// <param name="handler">Method to invoke.</param>
        public void Add(HttpMethod method, string path, Func <HttpRequest, HttpResponse> handler)
        {
            if (String.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException(nameof(path));
            }
            if (handler == null)
            {
                throw new ArgumentNullException(nameof(handler));
            }

            StaticRoute r = new StaticRoute(method, path, handler);

            Add(r);
        }
        /// <summary>
        /// Add a route.
        /// </summary>
        /// <param name="method">The HTTP method.</param>
        /// <param name="path">URL path, i.e. /path/to/resource.</param>
        /// <param name="handler">Method to invoke.</param>
        /// <param name="guid">Globally-unique identifier.</param>
        /// <param name="metadata">User-supplied metadata.</param>
        public void Add(HttpMethod method, string path, Func <HttpContext, Task> handler, string guid = null, object metadata = null)
        {
            if (String.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException(nameof(path));
            }
            if (handler == null)
            {
                throw new ArgumentNullException(nameof(handler));
            }

            StaticRoute r = new StaticRoute(method, path, handler, guid, metadata);

            Add(r);
        }
        /// <summary>
        /// Remove a route.
        /// </summary>
        /// <param name="method">The HTTP method.</param>
        /// <param name="path">URL path.</param>
        public void Remove(HttpMethod method, string path)
        {
            if (String.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException(nameof(path));
            }

            StaticRoute r = Get(method, path);

            if (r == null || r == default(StaticRoute))
            {
                return;
            }
            else
            {
                lock (_Lock)
                {
                    _Routes.Remove(r);
                }

                return;
            }
        }
Beispiel #10
0
        private async Task AcceptConnections(CancellationToken token)
        {
            try
            {
                #region Process-Requests

                while (_HttpListener.IsListening)
                {
                    if (_RequestCount >= _Settings.IO.MaxRequests)
                    {
                        await Task.Delay(100, token).ConfigureAwait(false);

                        continue;
                    }

                    HttpListenerContext listenerCtx = await _HttpListener.GetContextAsync().ConfigureAwait(false);

                    Interlocked.Increment(ref _RequestCount);
                    HttpContext ctx = null;

                    Task unawaited = Task.Run(async() =>
                    {
                        DateTime startTime = DateTime.Now;

                        try
                        {
                            #region Build-Context

                            Events.HandleConnectionReceived(this, new ConnectionEventArgs(
                                                                listenerCtx.Request.RemoteEndPoint.Address.ToString(),
                                                                listenerCtx.Request.RemoteEndPoint.Port));

                            ctx = new HttpContext(listenerCtx, _Settings, Events);

                            Events.HandleRequestReceived(this, new RequestEventArgs(ctx));

                            if (_Settings.Debug.Requests)
                            {
                                Events.Logger?.Invoke(
                                    _Header + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " " +
                                    ctx.Request.Method.ToString() + " " + ctx.Request.Url.RawWithoutQuery);
                            }

                            Statistics.IncrementRequestCounter(ctx.Request.Method);
                            Statistics.IncrementReceivedPayloadBytes(ctx.Request.ContentLength);

                            #endregion

                            #region Check-Access-Control

                            if (!_Settings.AccessControl.Permit(ctx.Request.Source.IpAddress))
                            {
                                Events.HandleRequestDenied(this, new RequestEventArgs(ctx));

                                if (_Settings.Debug.AccessControl)
                                {
                                    Events.Logger?.Invoke(_Header + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " denied due to access control");
                                }

                                listenerCtx.Response.StatusCode = 403;
                                listenerCtx.Response.Close();
                                return;
                            }

                            #endregion

                            #region Process-Preflight-Requests

                            if (ctx.Request.Method == HttpMethod.OPTIONS)
                            {
                                if (_Routes.Preflight != null)
                                {
                                    if (_Settings.Debug.Routing)
                                    {
                                        Events.Logger?.Invoke(
                                            _Header + "preflight route for " + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " " +
                                            ctx.Request.Method.ToString() + " " + ctx.Request.Url.RawWithoutQuery);
                                    }

                                    await _Routes.Preflight(ctx).ConfigureAwait(false);
                                    return;
                                }
                            }

                            #endregion

                            #region Pre-Routing-Handler

                            bool terminate = false;
                            if (_Routes.PreRouting != null)
                            {
                                terminate = await _Routes.PreRouting(ctx).ConfigureAwait(false);
                                if (terminate)
                                {
                                    if (_Settings.Debug.Routing)
                                    {
                                        Events.Logger?.Invoke(
                                            _Header + "prerouting terminated connection for " + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " " +
                                            ctx.Request.Method.ToString() + " " + ctx.Request.Url.RawWithoutQuery);
                                    }

                                    return;
                                }
                            }

                            #endregion

                            #region Content-Routes

                            if (ctx.Request.Method == HttpMethod.GET || ctx.Request.Method == HttpMethod.HEAD)
                            {
                                ContentRoute cr = null;
                                if (_Routes.Content.Match(ctx.Request.Url.RawWithoutQuery, out cr))
                                {
                                    if (_Settings.Debug.Routing)
                                    {
                                        Events.Logger?.Invoke(
                                            _Header + "content route for " + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " " +
                                            ctx.Request.Method.ToString() + " " + ctx.Request.Url.RawWithoutQuery);
                                    }

                                    ctx.RouteType = RouteTypeEnum.Content;
                                    ctx.Route     = cr;
                                    await _Routes.ContentHandler.Process(ctx, token).ConfigureAwait(false);
                                    return;
                                }
                            }

                            #endregion

                            #region Static-Routes

                            StaticRoute sr = null;
                            Func <HttpContext, Task> handler = _Routes.Static.Match(ctx.Request.Method, ctx.Request.Url.RawWithoutQuery, out sr);
                            if (handler != null)
                            {
                                if (_Settings.Debug.Routing)
                                {
                                    Events.Logger?.Invoke(
                                        _Header + "static route for " + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " " +
                                        ctx.Request.Method.ToString() + " " + ctx.Request.Url.RawWithoutQuery);
                                }

                                ctx.RouteType = RouteTypeEnum.Static;
                                ctx.Route     = sr;
                                await handler(ctx).ConfigureAwait(false);
                                return;
                            }

                            #endregion

                            #region Parameter-Routes

                            ParameterRoute pr = null;
                            Dictionary <string, string> parameters = null;
                            handler = _Routes.Parameter.Match(ctx.Request.Method, ctx.Request.Url.RawWithoutQuery, out parameters, out pr);
                            if (handler != null)
                            {
                                ctx.Request.Url.Parameters = new Dictionary <string, string>(parameters);

                                if (_Settings.Debug.Routing)
                                {
                                    Events.Logger?.Invoke(
                                        _Header + "parameter route for " + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " " +
                                        ctx.Request.Method.ToString() + " " + ctx.Request.Url.RawWithoutQuery);
                                }

                                ctx.RouteType = RouteTypeEnum.Parameter;
                                ctx.Route     = pr;
                                await handler(ctx).ConfigureAwait(false);
                                return;
                            }

                            #endregion

                            #region Dynamic-Routes

                            DynamicRoute dr = null;
                            handler         = _Routes.Dynamic.Match(ctx.Request.Method, ctx.Request.Url.RawWithoutQuery, out dr);
                            if (handler != null)
                            {
                                if (_Settings.Debug.Routing)
                                {
                                    Events.Logger?.Invoke(
                                        _Header + "dynamic route for " + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " " +
                                        ctx.Request.Method.ToString() + " " + ctx.Request.Url.RawWithoutQuery);
                                }

                                ctx.RouteType = RouteTypeEnum.Dynamic;
                                ctx.Route     = dr;
                                await handler(ctx).ConfigureAwait(false);
                                return;
                            }

                            #endregion

                            #region Default-Route

                            if (_Routes.Default != null)
                            {
                                if (_Settings.Debug.Routing)
                                {
                                    Events.Logger?.Invoke(
                                        _Header + "default route for " + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " " +
                                        ctx.Request.Method.ToString() + " " + ctx.Request.Url.RawWithoutQuery);
                                }

                                ctx.RouteType = RouteTypeEnum.Default;
                                await _Routes.Default(ctx).ConfigureAwait(false);
                                return;
                            }
                            else
                            {
                                if (_Settings.Debug.Routing)
                                {
                                    Events.Logger?.Invoke(
                                        _Header + "default route not found for " + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " " +
                                        ctx.Request.Method.ToString() + " " + ctx.Request.Url.RawWithoutQuery);
                                }

                                ctx.Response.StatusCode  = 404;
                                ctx.Response.ContentType = Pages.Default404Page.ContentType;
                                await ctx.Response.Send(Pages.Default404Page.Content).ConfigureAwait(false);
                                return;
                            }

                            #endregion
                        }
                        catch (Exception eInner)
                        {
                            ctx.Response.StatusCode  = 500;
                            ctx.Response.ContentType = Pages.Default500Page.ContentType;
                            await ctx.Response.Send(Pages.Default500Page.Content).ConfigureAwait(false);
                            Events.HandleExceptionEncountered(this, new ExceptionEventArgs(ctx, eInner));
                        }
                        finally
                        {
                            Interlocked.Decrement(ref _RequestCount);

                            if (ctx != null && ctx.Response != null && ctx.Response.ResponseSent)
                            {
                                Events.HandleResponseSent(this, new ResponseEventArgs(ctx, TotalMsFrom(startTime)));
                                Statistics.IncrementSentPayloadBytes(ctx.Response.ContentLength);
                            }
                        }
                    }, token);
                }

                #endregion
            }
            catch (TaskCanceledException)
            {
            }
            catch (OperationCanceledException)
            {
            }
            catch (HttpListenerException)
            {
            }
            catch (Exception e)
            {
                Events.HandleExceptionEncountered(this, new ExceptionEventArgs(null, e));
            }
            finally
            {
                Events.HandleServerStopped(this, EventArgs.Empty);
            }
        }