예제 #1
0
        /// <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));
            }

            lock (_Lock)
            {
                ParameterRoute pr = new ParameterRoute(method, path, handler, guid, metadata);
                _Routes.Add(pr, handler);
            }
        }
예제 #2
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);
            }
        }
예제 #3
0
        /// <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="vals">Values extracted from the URL.</param>
        /// <param name="pr">Matching route.</param>
        /// <returns>True if match exists.</returns>
        public Func <HttpContext, Task> Match(HttpMethod method, string path, out Dictionary <string, string> vals, out ParameterRoute pr)
        {
            pr   = null;
            vals = null;
            if (String.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException(nameof(path));
            }

            string consolidatedPath = BuildConsolidatedPath(method, path);

            lock (_Lock)
            {
                foreach (KeyValuePair <ParameterRoute, Func <HttpContext, Task> > route in _Routes)
                {
                    if (_Matcher.Match(
                            consolidatedPath,
                            BuildConsolidatedPath(route.Key.Method, route.Key.Path),
                            out vals))
                    {
                        pr = route.Key;
                        return(route.Value);
                    }
                }
            }

            return(null);
        }