zoukankan      html  css  js  c++  java
  • Asp.Net构架(Http请求处理流程)

    Http请求处理流程概述

    当用户在浏览器输入一个URL地址后,浏览器会发送一个请求到服务器。这时候在服务器上第一个负责处理请求的是IIS。然后IIS再根据请求的URL扩展名将请求分发给不同的ISAPI处理,然后,IIS 接收返回的数据流,并重新返还给 HTTP.SYS,最后,HTTP.SYS 再将这些数据返回给客户端浏览器。

    ISAPI

    ISAPI是一个底层的WIN32 API,开发者可以使用这些接口深入到IIS,让IIS支持各种其他处理程序。ISAPI是一个桥接口,通常用于高层次的工具与IIS之间的接驳。例如Windows下的Apache与Tomcat就是构建于ISAPI之上。ISAPI是自定义Web请求处理中第一个IIS入口点。

    能够处理各种后缀名的应用程序,通常被称为ISAPI应用程序(NOTE:Internet Server Application Programe Interface,互联网服务器应用程序接口)。虽然这 ISAPI 听上去还挺气派,也算是“应用程序”呢,但仔细看看它的全称就明白了:它实际上只是一个接口,起到一个代理的作用,它的主要工作是映射所请求的页面(文件) 和与此后缀名相对应的实际的处理程序

    Http请求刚刚到达服务器的时候

    IIS依赖一个叫做 HTTP.SYS 的内置驱动程序来监听来自外部的 HTTP请求。在操作系统启动的时候,IIS首先在HTTP.SYS中注册自己的虚拟路径。

    实际上相当于告诉HTTP.SYS哪些URL是可以访问的,哪些是不可以访问的。为什么你访问不存在的文件会出现 404 错误呢?就是在这一步确定的。

    如果请求的是一个可访问的URL,HTTP.SYS会将这个请求交给 IIS 工作者进程。

    当服务器接收到一个 Http请求的时候,IIS 如何去处理这个请求,依据什么去处理呢?―― 根据文件的后缀名。对于html页面,txt文件,jpeg和gif图像的请求,IIS就自己处理了,当发现请求是Asp.net的资源时(如.aspx,.asmx,*.ashx),请求将传递到ASP.NET ISAPI扩展aspnet_isapi.dll。

    服务器获取所请求的页面的后缀名以后,接下来会在服务器端寻找可以处理这类后缀名的应用程序,如果IIS找不到可以处理此类文件的应用程序,并且这个文件也没有受到服务器端的保护(NOTE:一个受保护的例子就是 cs文件,一个不受保护的例子就是js脚本),那么IIS将直接把这个文件返还给客户端。

    aspnet_isapi.dll可以处理多种资源类型,包括Web服务和HTTP处理程序调用等。

    除了以外,ISAPI 还做一些其他的工作:

    • 从HTTP.SYS中获取当前的Httq请求信息,并且将这些信息保存到 HttpWorkerRequest 类中。
    • 在相互隔离的应用程序域AppDomain中加载HttpRuntime。
    • 调用 HttpRuntime的ProcessRequest方法。
    IIS与aspnet_isapi的处理逻辑:
    • 当请求到达时,IIS检查资源类型并调用aspnet_isapi扩展。如果启用了默认的进程模型。aspnet_isapi会将请求排队。并将请求分配给辅助进程、所有的请求数据都是通过异步I/O发送。
    • 收到请求后,ASP.NET辅助进程将通知aspnet_isapi,它将为请求服务(建立管道)。
    • 在辅助进程的上下文中执行请求。有时,辅助进程可能需要回调aspnet_isapi以完成请求,也就是需要说枚举服务器变量。这种情况下,辅助进程将使用同步管道,因为这样可以保持请求处理的逻辑顺序。
    • 完成后,响应被发送到打开了异步管道的aspnet_isapi。如果aspnet_isapi检测到辅助进程已取消,它将自动终止请求并释放所有相关的IIS资源。

    宿主环境(Hosting)与管道(Pipeline)

    从本质上讲,Asp.Net 主要是由一系列的类组成,这些类的主要目的就是将Http请求转变为对客户端的响应。

    当 Web.config文件的内容发生改变 或者 .aspx文件发生变动的时候,为了能够卸载运行在同一个进程中的应用程序(NOTE:卸载也是为了重新加载),Http请求被分放在相互隔离的应用程序域中(应用程序域就是 AppDomain)。

    • Asp.net处理管道的第一步是创建HttpWorkerRequest对象,它包含于当前请求有关的所有信息。
    • HttpWorkerRequest把请求传递给HttpRuntime类的静态ProcessRequest方法。HttpRuntime首先要做的事是创建HttpContext对象,并用HttpWorkerRequest进行初始化。
            /// <devdoc>
            ///    <para><SPAN>The method that drives
            ///       all ASP.NET web processing execution.</SPAN></para>
            /// </devdoc>
            [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
            public static void ProcessRequest(HttpWorkerRequest wr) {
                if (wr == null)
                    throw new ArgumentNullException("wr");
    
                if (HttpRuntime.UseIntegratedPipeline) {//判断是否为集成管道
                    throw new PlatformNotSupportedException(SR.GetString(SR.Method_Not_Supported_By_Iis_Integrated_Mode, "HttpRuntime.ProcessRequest"));
                }
    
                ProcessRequestNoDemand(wr);
            }
            internal static void ProcessRequestNoDemand(HttpWorkerRequest wr) {
                RequestQueue rq = _theRuntime._requestQueue;
    
                wr.UpdateInitialCounters();
    
                if (rq != null)  // could be null before first request
                    wr = rq.GetRequestToExecute(wr);
    
                if (wr != null) {
                    CalculateWaitTimeAndUpdatePerfCounter(wr);
                    wr.ResetStartTime();
                    ProcessRequestNow(wr);
                }
            }
            private void ProcessRequestInternal(HttpWorkerRequest wr) {
                // Count active requests
                Interlocked.Increment(ref _activeRequestCount);
    
                if (_disposingHttpRuntime) {//当请求数量超出程序处理速度,服务器返回503,服务器繁忙的响应
                    // Dev11 333176: An appdomain is unloaded before all requests are served, resulting in System.AppDomainUnloadedException during isapi completion callback
                    //
                    // HttpRuntim.Dispose could have already finished on a different thread when we had no active requests
                    // In this case we are about to start or already started unloading the appdomain so we will reject the request the safest way possible
                    try {
                        wr.SendStatus(503, "Server Too Busy");
                        wr.SendKnownResponseHeader(HttpWorkerRequest.HeaderContentType, "text/html; charset=utf-8");
                        byte[] body = Encoding.ASCII.GetBytes("<html><body>Server Too Busy</body></html>");
                        wr.SendResponseFromMemory(body, body.Length);
                        // this will flush synchronously because of HttpRuntime.ShutdownInProgress
                        wr.FlushResponse(true);
                        wr.EndOfRequest();
                    } finally {
                        Interlocked.Decrement(ref _activeRequestCount);
                    }
                    return;
                }
    
                // Construct the Context on HttpWorkerRequest, hook everything together
                HttpContext context;
    
                try {
                    context = new HttpContext(wr, false /* initResponseWriter */);
                } 
                catch {//HttpContext 组装错误,返回400错误
                    try {
                        // If we fail to create the context for any reason, send back a 400 to make sure
                        // the request is correctly closed (relates to VSUQFE3962)
                        wr.SendStatus(400, "Bad Request");
                        wr.SendKnownResponseHeader(HttpWorkerRequest.HeaderContentType, "text/html; charset=utf-8");
                        byte[] body = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>");
                        wr.SendResponseFromMemory(body, body.Length);
                        wr.FlushResponse(true);
                        wr.EndOfRequest();
                        return;
                    } finally {
                        Interlocked.Decrement(ref _activeRequestCount);
                    }
                }
    
                wr.SetEndOfSendNotification(_asyncEndOfSendCallback, context);
    
                HostingEnvironment.IncrementBusyCount();
    
                try {
                    // First request initialization
                    try {
                        EnsureFirstRequestInit(context);
                    }
                    catch {
                        // If we are handling a DEBUG request, ignore the FirstRequestInit exception.
                        // This allows the HttpDebugHandler to execute, and lets the debugger attach to
                        // the process (VSWhidbey 358135)
                        if (!context.Request.IsDebuggingRequest) {
                            throw;
                        }
                    }
    
                    // Init response writer (after we have config in first request init)
                    // no need for impersonation as it is handled in config system
                    context.Response.InitResponseWriter();
    
                    // Get application instance
                    IHttpHandler app = HttpApplicationFactory.GetApplicationInstance(context);
    
                    if (app == null)
                        throw new HttpException(SR.GetString(SR.Unable_create_app_object));
    
                    if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest, app.GetType().FullName, "Start");
    
                    if (app is IHttpAsyncHandler) {
                        // asynchronous handler
                        IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler)app;
                        context.AsyncAppHandler = asyncHandler;
                        asyncHandler.BeginProcessRequest(context, _handlerCompletionCallback, context);
                    }
                    else {
                        // synchronous handler
                        app.ProcessRequest(context);
                        FinishRequest(context.WorkerRequest, context, null);
                    }
                }
                catch (Exception e) {
                    context.Response.InitResponseWriter();
                    FinishRequest(wr, context, e);
                }
            }
    
    
    • 当Http请求进入 Asp.Net Runtime以后,它的管道由托管模块(NOTE:Managed Modules)和处理程序(NOTE:Handlers)组成,并且由管道来处理这个 Http请求。
    • 创建了HttpContext实例之后,HttpRuntime类就通过调用HttpApplicationFactory的静态GetApplicationInstance()方法,为该应用程序请求HttpApplication派生类的一个示例。GetApplicationInstance()方法要么创建一个HttpApplication类的一个新实例,要么从应用程序对象池中取出一个实例。
            internal static IHttpHandler GetApplicationInstance(HttpContext context) {
                if (_customApplication != null)
                    return _customApplication;
    
                // Check to see if it's a debug auto-attach request
                if (context.Request.IsDebuggingRequest)
                    return new HttpDebugHandler();
    
                _theApplicationFactory.EnsureInited();
    
                _theApplicationFactory.EnsureAppStartCalled(context);
    
                return _theApplicationFactory.GetNormalApplicationInstance(context);
            }
            private void EnsureInited() {
                if (!_inited) {
                    lock (this) {
                        if (!_inited) {
                            Init();
                            _inited = true;
                        }
                    }
                }
            }
            //
            // Initialization on first request
            //
    
            private void Init() {
                if (_customApplication != null)
                    return;
    
                try {
                    try {
                        _appFilename = GetApplicationFile();//获取global.asax 地址
    
                        CompileApplication();//编译应用程序
                    }
                    finally {
                        // Always set up global.asax file change notification, even if compilation
                        // failed.  This way, if the problem is fixed, the appdomain will be restarted.
                        SetupChangesMonitor();//启动变化监控
                    }
                }
                catch { // Protect against exception filters
                    throw;
                }
            }
    
            //
            //  Application instance management
            //
    
            private HttpApplication GetNormalApplicationInstance(HttpContext context) {
                HttpApplication app = null;
    
                if (!_freeList.TryTake(out app)) {
                    // If ran out of instances, create a new one
                    app = (HttpApplication)HttpRuntime.CreateNonPublicInstance(_theApplicationType);
    
                    using (new ApplicationImpersonationContext()) {
                        app.InitInternal(context, _state, _eventHandlerMethods);
                    }
                }
    
                if (AppSettings.UseTaskFriendlySynchronizationContext) {
                    // When this HttpApplication instance is no longer in use, recycle it.
                    app.ApplicationInstanceConsumersCounter = new CountdownTask(1); // representing required call to HttpApplication.ReleaseAppInstance
                    app.ApplicationInstanceConsumersCounter.Task.ContinueWith((_, o) => RecycleApplicationInstance((HttpApplication)o), app, TaskContinuationOptions.ExecuteSynchronously);
                }
                return app;
            }
    
    
    • 在创建完成HttpApplication实例之后,就对它进行初始化,并在初始化期间分配应用程序定义的所
      有模块。模块式实现IHttpModule接口的类,作用就是为了实现那经典的19个标准处理事件。
            private void CompileApplication() {
                // Get the Application Type and AppState from the global file
    
                _theApplicationType = BuildManager.GetGlobalAsaxType();
    
                BuildResultCompiledGlobalAsaxType result = BuildManager.GetGlobalAsaxBuildResult();
    
                if (result != null) {
    
                    // Even if global.asax was already compiled, we need to get the collections
                    // of application and session objects, since they are not persisted when
                    // global.asax is compiled.  Ideally, they would be, but since <object> tags
                    // are only there for ASP compat, it's not worth the trouble.
                    // Note that we only do this is the rare case where we know global.asax contains
                    // <object> tags, to avoid always paying the price (VSWhidbey 453101)
                    if (result.HasAppOrSessionObjects) {
                        GetAppStateByParsingGlobalAsax();
                    }
    
                    // Remember file dependencies
                    _fileDependencies = result.VirtualPathDependencies;
                }
    
                if (_state == null) {
                    _state = new HttpApplicationState();
                }
    
    
                // Prepare to hookup event handlers via reflection
    
                ReflectOnApplicationType();
            }
            private void ReflectOnApplicationType() {//这个方法为应用程序注册事件
                ArrayList handlers = new ArrayList();
                MethodInfo[] methods;
    
                Debug.Trace("PipelineRuntime", "ReflectOnApplicationType");
    
                // get this class methods
                methods = _theApplicationType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
                foreach (MethodInfo m in methods) {
                    if (ReflectOnMethodInfoIfItLooksLikeEventHandler(m))
                        handlers.Add(m);
                }
                
                // get base class private methods (GetMethods would not return those)
                Type baseType = _theApplicationType.BaseType;
                if (baseType != null && baseType != typeof(HttpApplication)) {
                    methods = baseType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
                    foreach (MethodInfo m in methods) {
                        if (m.IsPrivate && ReflectOnMethodInfoIfItLooksLikeEventHandler(m))
                            handlers.Add(m);
                    }
                }
    
                // remember as an array
                _eventHandlerMethods = new MethodInfo[handlers.Count];
                for (int i = 0; i < _eventHandlerMethods.Length; i++)
                    _eventHandlerMethods[i] = (MethodInfo)handlers[i];
            }
            private bool ReflectOnMethodInfoIfItLooksLikeEventHandler(MethodInfo m) {
                if (m.ReturnType != typeof(void))
                    return false;
    
                // has to have either no args or two args (object, eventargs)
                ParameterInfo[] parameters = m.GetParameters();
    
                switch (parameters.Length) {
                    case 0:
                        // ok
                        break;
                    case 2:
                        // param 0 must be object
                        if (parameters[0].ParameterType != typeof(System.Object))
                            return false;
                        // param 1 must be eventargs
                        if (parameters[1].ParameterType != typeof(System.EventArgs) &&
                            !parameters[1].ParameterType.IsSubclassOf(typeof(System.EventArgs)))
                            return false;
                        // ok
                        break;
    
                    default:
                        return false;
                }
    
                // check the name (has to have _ not as first or last char)
                String name = m.Name;
                int j = name.IndexOf('_');
                if (j <= 0 || j > name.Length-1)
                    return false;
    
                // special pseudo-events
                if (StringUtil.EqualsIgnoreCase(name, "Application_OnStart") ||
                    StringUtil.EqualsIgnoreCase(name, "Application_Start")) {
                    _onStartMethod = m;
                    _onStartParamCount = parameters.Length;
                }
                else if (StringUtil.EqualsIgnoreCase(name, "Application_OnEnd") ||
                         StringUtil.EqualsIgnoreCase(name, "Application_End")) {
                    _onEndMethod = m;
                    _onEndParamCount = parameters.Length;
                }
                else if (StringUtil.EqualsIgnoreCase(name, "Session_OnEnd") ||
                         StringUtil.EqualsIgnoreCase(name, "Session_End")) {
                    _sessionOnEndMethod = m;
                    _sessionOnEndParamCount = parameters.Length;
                }
    
                return true;
            }
            private void EnsureAppStartCalled(HttpContext context) {
                if (!_appOnStartCalled) {
                    lock (this) {
                        if (!_appOnStartCalled) {
                            using (new DisposableHttpContextWrapper(context)) {
                                // impersonation could be required (UNC share or app credentials)
    
                                WebBaseEvent.RaiseSystemEvent(this, WebEventCodes.ApplicationStart);
    
                                // fire outside of impersonation as HttpApplication logic takes
                                // care of impersonation by itself
                                FireApplicationOnStart(context);
                            }
    
                            _appOnStartCalled = true;
                        }
                    }
                }
            }
            //
            //  Application on_start 
            //
    
            private void FireApplicationOnStart(HttpContext context) {
                if (_onStartMethod != null) {
                    HttpApplication app = GetSpecialApplicationInstance();
    
                    app.ProcessSpecialRequest(
                                             context,
                                             _onStartMethod,
                                             _onStartParamCount,
                                             this, 
                                             EventArgs.Empty, 
                                             null);//执行前面注册的启动事件_onStartMethod
    
                    RecycleSpecialApplicationInstance(app);
                }
            }
            private HttpApplication GetSpecialApplicationInstance() {
                return GetSpecialApplicationInstance(IntPtr.Zero, null);
            }
            private HttpApplication GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) {
                HttpApplication app = null;
    
                if (!_specialFreeList.TryTake(out app)) {
                    //
                    //  Put the context on the thread, to make it available to anyone calling
                    //  HttpContext.Current from the HttpApplication constructor or module Init
                    //
                    using (new DisposableHttpContextWrapper(context)) {
                        // If ran out of instances, create a new one
                        app = (HttpApplication)HttpRuntime.CreateNonPublicInstance(_theApplicationType);
    
                        using (new ApplicationImpersonationContext()) {
                            app.InitSpecial(_state, _eventHandlerMethods, appContext, context);
                        }
                    }
                }
    
                return app;
            }
    
    
    • 在创建了模块之后,HttpRuntime类通过调用它的BeginProcessRequest方法,要求最新检索到的HttpApplication类对当前请求提供服务。然后,为当前请求找到合适的处理程序工厂。
      创建处理程序,传递当前HttpContext,一旦ProcessRequest方法返回,请求完成。
    本文参考文档
  • 相关阅读:
    2017 ACM-ICPC西安网赛B-Coin
    Codeforces389D(SummerTrainingDay01-J)
    Codeforces672D(SummerTrainingDay01-I)
    VS2017.滚动条选项
    VS.自动调试
    ffmpeg.mp4.格式资料
    vs2017.unresolved external symbol __imp__fprintf&__imp____iob_func
    vs2017."const char *"的实参与"char *"的形参不兼容_goto跳过类型声明
    vue项目开发时怎么解决跨域
    vue奇怪的知识点又增加了
  • 原文地址:https://www.cnblogs.com/Dewumu/p/12988276.html
Copyright © 2011-2022 走看看