您的位置:首页 > 理论基础 > 计算机网络

apache(httpd-2.2.14) mod_ssl源码分析之三(mod_ssl处理流程分析)

2010-03-18 20:24 906 查看
由于过年家里出了些事情博客一直没有更新,再加上公司的Apache项目要推迟,好久没有写关于Apache的文章了,但是前几天张老师在我的博客文章留言让我很是感到荣幸,我想我会继续为Apache的探索做出自己的一份力。

在apache(httpd-2.2.14) mod_ssl源码分析之二中我们初步定位了mod_ssl的内部是怎样处理请求的,我将继续就mod_ssl的处理流程做更加详细的分析。

在apache(httpd-2.2.14) mod_ssl源码分析之二中我们看到了一个树形的处理流程图,其中最重要的就是它的分支点ssl_hook_pre_connection,这个挂钩的作用是:当Apache的socket接收到请求时,会为处理请求分配一个新的子进程,core核心模块中的Connection.c文件的ap_process_connection()会调用挂钩函数ap_hook_pre_connection,而mod_ssl刚好实现了该挂钩的具体实例来判断是否需要建立openssl连接,如果不需要则会按照http的处理流程在读取请求的时候只调用core_in这个过滤器对网络中的http明文数据进行读取,如果需要建立openssl的连接则会调用ssl_init_ssl_connection函数对ssl连接进行初始化操作,而在ssl_init_ssl_connection这个函数中最最重要的就是将mod_ssl的过滤器加入到Apache的过滤器列表中,filter_ctx->pOutputFilter = ap_add_output_filter(cssl_io_filter,filter_ctx, NULL, c)和ssl_io_input_add_filter(filter_ctx, c, ssl) 这两个函数也正是这个作用,在这个阶段完成以后,mod_ssl就可以休息一会,直到Http_core.c这个文件中的函数ap_process_http_connection(conn_rec *c)被调用才会让真正的处理开始,ap_process_http_connection(conn_rec *c)这个函数的作用是处理http请求连接,在这个函数内的函数ap_read_request(conn_rec *conn)才是真正的重头戏,在这个函数外有一个while语句来循环地读取socket接受到得请求,ap_read_request(conn_rec *conn)调用read_request_line(request_rec *r, apr_bucket_brigade *bb)函数...一层层的调用到一个叫做ap_rgetline_core(char **s, apr_size_t n,apr_size_t *read, request_rec *r, int fold, apr_bucket_brigade *bb)的函数。

在这个函数中存在一个for循环,该循环是读取多行请求的循环,换句话说就是一个请求的行数和该循环的执行次数是相同的,在for的内部有一个叫做ap_get_brigade(r->input_filters, bb, AP_MODE_GETLINE,APR_BLOCK_READ, 0)的函数,下面我们来细说这个函数。

在介绍这个函数之前我们要先理解一下什么是存储段组、过滤器以及存储段组和过滤器之间的关系。

存储段组:Apache从客户端接受或返回给客户端的数据称之为外部数据,为了存储外部数据,Apache引入了存储段和存储段组的概念,这是为了减少客户端数据的不确定性而引进的一种新的存储机制,其中存储段是Apache中保存外部数据的最基本单位,Apache内部所有的数据包括核心生成的数据、过滤器中传输的数据等等,他们都必须转换为存储段的形式,多个存储段又可以通过存储段组进行统一的管理,每一个存储段组中包含一个或多个存储段。

过滤器:我所理解的过滤器就是一个物品提炼与加工工厂,以提炼工厂为例,假如我们引进了一批海水要提取钠离子,首先第一步就是先要将海水提纯,接下来要烘干,提取粗盐,最终提取出钠离子,而这每一道工序都可以看做是一个过滤器,这些过滤器的职能是单一的,只有将它们所有的过滤器变成一个生产线才是最有意义的。如果我的工厂想要转变生产策略,想要提取氯离子而不是钠离子了,怎么样的变动才能使我这条生产线的花费最少并且更具灵活性呢,就是将每一个过滤器进行灵活的配置,用到哪个就安上哪个,不用的就将它去掉,这也就是Apache巧妙的构思所在,比如我们前面反复提到的ap_hook_pre_connection函数就是判断是否将mod_ssl过滤器设备加入到流水线的过程,如果不需要mod_ssl的过滤器ssl_filter,则会按照http的处理流程在读取请求的时候只调用core_in这个过滤器对网络中的http明文数据进行读取,如果需要建立openssl的连接则会调用ssl_init_ssl_connection函数对ssl连接进行初始化操作,而在ssl_init_ssl_connection这个函数中最最重要的就是将mod_ssl的过滤器加入到Apache的过滤器列表中,这样的判断使模块与模块间的耦合性降低,设计相当的巧妙!

过滤器的种类有很多:比如资源过滤器、内容过滤器、协议过滤器等等,详见张中庆老师的《Apache源代码全景分析第一卷:体系结构与核心模块》的第7章。

存储段组和过滤器之间的关系:正常情况下,存储段总是和过滤器关联在一起的,因此当存储段组在过滤器之间进行数据传递时,对于某一个过滤器而言,就是能够获取下一个过滤器的内容,只有获取存储段组的内容,才有可能进行过滤处理。

继续细说ap_get_brigade(r->input_filters, bb, AP_MODE_GETLINE,APR_BLOCK_READ, 0)这个函数,这个函数的作用是用于获取下一个过滤器的存储组的内容,函数只是在确实具有下一个过滤器的时候才直接调用过滤器本身所具有的输入过滤器函数,ap_get_brigade函数通常用于输入过滤器,在HTTP请求处理的时候,Apache核心并不会直接去读取网络中的请求数据,实际上它只是建立一个空的存储段组,然后将此存储段组传递给输入过滤器链表中的第一个过滤器,由该过滤器在存储段中填入实际的请求,然后第一个过滤器在做简单处理之后会继续调用ap_get_brigade函数,将处理过的存储段组传递给第二个过滤器,直到最后一个过滤器。

如果在ap_hook_pre_connection阶段确实要加入mod_ssl的过滤器ssl_filter,那么Apache第一个调用的就是ssl_filter过滤器,在该过滤器中调用ssl_io_filter_connect(inctx->filter_ctx))判断该ssl连接是否已经进行了握手,如果没有则调用SSL_accept(filter_ctx->pssl))函数进行握手操作,如果握手已经完毕则直接通过函数SSL_read(inctx->filter_ctx->pssl, buf + bytes, wanted - bytes)进行密文请求的读取,但是,无论是握手操作还是读取密文(解密)操作都会调用一个非常非常重要的函数,它就是static int bio_filter_in_read(BIO *bio, char *in, int inlen),在这个函数中还是调用ap_get_brigade函数,然后通过函数ap_get_brigade继续调用core_filter,在此过程中只用到了这两个过滤器,其中的ssl_filter过滤器就是用于openssl的握手和加密解密过程,而core_filter是过滤器链表中的最后一个过滤器,因此无法继续传递,只能从网络中读取请求数据,然后填充到传递过来的存储段组,最后返回到核心进行进一步处理。

待续...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: