Esempio n. 1
0
        private async void ClientConnected(object sender, ClientConnectedEventArgs args)
        {
            DateTime startTime = DateTime.Now;

            #region Parse-IP-Port

            string ipPort = args.IpPort;
            string ip     = null;
            int    port   = 0;
            Common.ParseIpPort(ipPort, out ip, out port);
            HttpContext ctx = null;

            _Events.HandleConnectionReceived(this, new ConnectionEventArgs(ip, port));

            if (_Settings.Debug.Connections)
            {
                _Events.Logger?.Invoke(_Header + "connection from " + ip + ":" + port);
            }

            #endregion

            #region Process

            try
            {
                #region Retrieve-Headers

                StringBuilder sb = new StringBuilder();

                //                           123456789012345 6 7 8
                // minimum request 16 bytes: GET / HTTP/1.1\r\n\r\n
                int        preReadLen    = 18;
                ReadResult preReadResult = await _TcpServer.ReadWithTimeoutAsync(
                    _Settings.IO.ReadTimeoutMs,
                    args.IpPort,
                    preReadLen,
                    _Token).ConfigureAwait(false);

                if (preReadResult.Status != ReadResultStatus.Success ||
                    preReadResult.BytesRead != preReadLen ||
                    preReadResult.Data == null ||
                    preReadResult.Data.Length != preReadLen)
                {
                    return;
                }

                sb.Append(Encoding.ASCII.GetString(preReadResult.Data));

                bool retrievingHeaders = true;
                while (retrievingHeaders)
                {
                    if (sb.ToString().EndsWith("\r\n\r\n"))
                    {
                        // end of headers detected
                        retrievingHeaders = false;
                    }
                    else
                    {
                        ReadResult addlReadResult = await _TcpServer.ReadWithTimeoutAsync(
                            _Settings.IO.ReadTimeoutMs,
                            args.IpPort,
                            1,
                            _Token).ConfigureAwait(false);

                        if (addlReadResult.Status == ReadResultStatus.Success)
                        {
                            sb.Append(Encoding.ASCII.GetString(addlReadResult.Data));
                        }
                        else
                        {
                            return;
                        }
                    }
                }

                #endregion

                #region Build-Context

                ctx = new HttpContext(
                    ipPort,
                    _TcpServer.GetStream(ipPort),
                    sb.ToString(),
                    Events,
                    _Settings.Headers,
                    _Settings.IO.StreamBufferSize);

                _Statistics.IncrementRequestCounter(ctx.Request.Method);
                _Statistics.AddReceivedPayloadBytes(ctx.Request.ContentLength);

                _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.Full);
                }

                #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 + "request from " + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " denied due to access control");
                    }

                    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.Full);
                        }

                        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.Full);
                        }

                        return;
                    }
                }

                #endregion

                #region Content-Routes

                if (ctx.Request.Method == HttpMethod.GET || ctx.Request.Method == HttpMethod.HEAD)
                {
                    if (_Routes.Content.Exists(ctx.Request.Url.WithoutQuery))
                    {
                        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.Full);
                        }

                        await _Routes.ContentHandler.Process(ctx, _Token).ConfigureAwait(false);

                        return;
                    }
                }

                #endregion

                #region Static-Routes

                Func <HttpContext, Task> handler = _Routes.Static.Match(ctx.Request.Method, ctx.Request.Url.WithoutQuery);
                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.WithoutQuery);
                    }

                    await handler(ctx).ConfigureAwait(false);

                    return;
                }

                #endregion

                #region Parameter-Routes

                Dictionary <string, string> parameters = null;
                handler = _Routes.Parameter.Match(ctx.Request.Method, ctx.Request.Url.WithoutQuery, out parameters);
                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.WithoutQuery);
                    }

                    await handler(ctx).ConfigureAwait(false);

                    return;
                }

                #endregion

                #region Dynamic-Routes

                handler = _Routes.Dynamic.Match(ctx.Request.Method, ctx.Request.Url.WithoutQuery);
                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.Full);
                    }

                    await handler(ctx).ConfigureAwait(false);

                    return;
                }

                #endregion

                #region Default-Route

                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.Full);
                }

                if (_Routes.Default != null)
                {
                    await _Routes.Default(ctx).ConfigureAwait(false);

                    return;
                }
                else
                {
                    ctx.Response.StatusCode  = 404;
                    ctx.Response.ContentType = _Pages.Default404Page.ContentType;
                    await ctx.Response.SendAsync(_Pages.Default404Page.Content, _Token).ConfigureAwait(false);
                }

                #endregion
            }
            catch (TaskCanceledException)
            {
                return;
            }
            catch (ObjectDisposedException)
            {
                return;
            }
            catch (Exception e)
            {
                ctx.Response.StatusCode  = 500;
                ctx.Response.ContentType = _Pages.Default500Page.ContentType;
                await ctx.Response.SendAsync(_Pages.Default500Page.Content, _Token).ConfigureAwait(false);

                _Events.Logger?.Invoke(_Header + "exception: " + Environment.NewLine + SerializationHelper.SerializeJson(e, true));
                _Events.HandleException(this, new ExceptionEventArgs(ctx, e));
                return;
            }
            finally
            {
                _TcpServer.DisconnectClient(ipPort);

                if (ctx != null)
                {
                    double totalMs = TotalMsFrom(startTime);

                    _Events.HandleResponseSent(this, new ResponseEventArgs(ctx, totalMs));

                    if (_Settings.Debug.Responses)
                    {
                        _Events.Logger?.Invoke(
                            _Header + ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " " +
                            ctx.Request.Method.ToString() + " " + ctx.Request.Url.Full + ": " +
                            ctx.Response.StatusCode + " [" + totalMs + "ms]");
                    }

                    if (ctx.Response.ContentLength != null)
                    {
                        _Statistics.AddSentPayloadBytes(Convert.ToInt64(ctx.Response.ContentLength));
                    }
                }
            }

            #endregion
        }