沈阳工程学院毕业论文 第3章 Apache体系结构
(3)HTTP请求体
请求保温读取后,所有的请求相关信息都保存到请求数据结构request_rec中。该结构是一个复杂的数据结构,其中包含了HTTP请求的各个方面。该结构在整个请求处理过程中一直存在,直到请求处理结束。
对于读取的报文还有一个重要的任务就是读取的报文进行输入过滤器处理。所有的输入处理过滤器组成输入过滤器表,在请求报文从网络中读取之后,它就直接进入了过滤器链表中,然后每个过滤器对其进行处理,并将其传送给下一个过滤器,直到最后一个过滤器。
在所有的过滤器处理完毕后,我们得到的就是一个最终的处理后的请求报文。此时请求处理模块HTTP_REQUEST将对请求做进一步处理。
3.4.4 请求处理
对于HTTP报文,Apache调用ap_process_request函数对请求进行实质的处理。Apache中的请求处理包括三个大的阶段:请求解析阶段、安全处理阶段和请求准备阶段。
1.请求解析阶段 (1)URL字符转义
该阶段是一个必须的阶段,如果请求是一个代理请求或请求结构的parsed_uri.path没有赋值,那么该阶段将不会被处理。
(2)从URL中剔除/../和/./字符 URL中所有的/../和/./字符串都在这一阶段调用ap_getparents()函数并剔除,该阶段能够确保将URL提交后续阶段处理时候是一个绝对的路径URL。这个阶段是不能省略的,必须被执行。
(3)首次读取URL相关配置信息 一旦第二步处理完毕后,只包含绝对路径的URL就生成完毕了,此时Apache将调用ap_location_walk从配置系统中查找与该URL关联的配置信息。在请求处理的后续阶段中,比如用户授权认证,权限控制等都需要依赖与读取的配置信息。如果当前请求是一个内部重定向请求或子请求,那么该请求的配置信息可能有很大一部分或全部继承自父请求。
URL关联的配置信息需要两次,此处一次读取。在对URL进行转换映射后,它可能被转换为一个完全不同的URL,此时还需要再次读取一次配置信息。完整配置信息是两次读取信息的最终叠加。
(4)URL名称转换(translate_name)
该阶段主要用于对URL尽心转换。比如Alias指令将某个URL映射到另一个特定的URL中;而mod_write模块则用于对URL的完全重写。
(5)map_to_storage
如果在translate_name阶段,请求的URL最终转换本次的磁盘路径,map_ to_ storage将用来确定特定的资源是否在磁盘上存在。
(6)二次URL相关配置文件读取
- 10 -
沈阳工程学院毕业论文 第3章 Apache体系结构
在进行了名称转换之后,我们需要再次读取它新的URL关联的配置信息。 (7)header_parser
该阶段用来检查HTTP请求头,目前该阶段已经很少使用。 2.安全处理阶段 (1)access_checker
该阶段主要是对客户的访问做一些基础性的限制工作。 (2)check_user_id
该阶段主要是检查用户的身份权限,具体就是检查用户是否存在,或者检查用户的密码是否正确。
(3)auth_check
该阶段主要检查用户是否有访问该资源的权限。 3.请求准备阶段 (1)type_checker
在权限检查完毕之后,说明可以响应用户的请求了,这时,Apache开始处理客户请求的资源,并返回给客户。
(2)fixups
这是请求处理的最后一个阶段。该阶段之后就是生成返回给客户端的相应内容。
3.4.5 内容生成
当所有的请求在请求处理阶段处理完毕后,它就调用适当的处理器生成相应内容。如果没有找到对应的处理器,那么默认处理器将被调用。
与请求数据一旦读取就被压入输入过滤器链表一样,生成的这些内容一旦生成,就将被压入到输出过滤器链表进行输出处理。输出过滤器链表中的每一个输出过滤器都会对生成的数据进行处理,直到最后一个输出过滤器——网络输出过滤器,它将数据直接写入网络,然后返回给客户端。
3.5 主程序main
3.5.1 主程序概要
Apache的主函数main()位于${Apache}/server目录下,它的主要功能则着重于预处理,主要包括以下几个方面。
(1)读取Apache的配置文件
(2)检查启动Apache的指令行参数 (3)虚拟主机的设置
3.5.2 主程序细节
- 11 -
沈阳工程学院毕业论文 第3章 Apache体系结构
Apache主程序实现代码如下所示:
int main(int argc, const char * const argv[])
{
char c;
int configtestonly = 0;
const char *confname = SERVER_CONFIG_FILE; const char *def_server_root = HTTPD_ROOT; const char *temp_error_log = NULL; process_rec *process; server_rec *server_conf; apr_pool_t *pglobal; apr_pool_t *pconf;
apr_pool_t *plog; /* Pool of log streams, reset _after_ each read of conf */ apr_pool_t *ptemp; /* Pool for temporary config stuff, reset often */ apr_pool_t *pcommands; /* Pool for -D, -C and -c switches */ apr_getopt_t *opt; apr_status_t rv; module **mod; const char *optarg;
APR_OPTIONAL_FN_TYPE(ap_signal_server) *signal_server; AP_MONCONTROL(0); /* turn off profiling of startup */ AP_MONCONTROL主要用于打开和关闭代码剖析。代码剖析用于检查代码的运行情况,从而可以找到程序执行的瓶颈,通常用于调试阶段。 apr_app_initialize(&argc, &argv, NULL);
任何一个应用程序如果要使用APR库进行二次开发,那么首先必须为完成的任务是APR库的初始化。apr_app_initialize 负责对APR库进行必要的初始化工作,代码如下:
process = create_process(argc, argv); pglobal = process->pool; pconf = process->pconf;
ap_server_argv0 = process->short_name;
由于Apache通常是通过指令行进行启动的,因此指令行的相关信息(指令数目arge及指令行字符串argv)都非常重要。对Apache而言,指令信息不仅主程序要使用,而且在一些子进程程序中也要使用,因此这就存在指令行信息传递问题。
Apache把所有的指令行相关信息都包装在process_rec结构中,代码如下: struct process_rec { apr_pool_t *pool; apr_pool_t *pconf; int argc;
const char * const *argv;
- 12 -
沈阳工程学院毕业论文 第3章 Apache体系结构
const char *short_name; };
short_name是应用程序的缩略名称。pool和pconf则分别是全局内存池和配置相关内存池。
创建一个process_rec结构体可通过create_process函数完成。 Apache中的指令分为两大类:指令行中的指令及配置文件中的指令。而对于指令中的指令有包括两种:读取配置文件之前必须处理的指令和读取配置文件后必须处理的指令。前者是防止指令行中的指令被覆盖,而后者则相反。一旦命令行被解析完毕后,这两种特殊的指令将被保存起来,以便在合适的时候执行。如果不保存,这些指令将会丢失。保存可以使用如下数组数据结构:
ap_setup_prelinked_modules(process); apr_pool_create(&pcommands, pglobal); apr_pool_tag(pcommands, \
ap_server_pre_read_config = apr_array_make(pcommands, 1, sizeof(char *)); ap_server_post_read_config = apr_array_make(pcommands, 1, sizeof(char *)); ap_server_config_defines = apr_array_make(pcommands, 1, sizeof(char *)); ap_setup_prelinked_modules用于将所有预链接的模块加入到加载模块链接表中。模块只有加入到加载模块链表中才能称之为活动模块,然后才能被Apache核心调用,否则该模块仍处于非活动状态。
error = ap_run_rewrite_args(process); if(error){ ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_EMERG, 0,
NULL,”%S :%S”, ap_server_argv0, error);
destroy_and_exit_args(process, 1); }
rewrite_args挂钩主要是为MPM模块设置的,它允许MPM对命令行中的传入参数进行重写。对于一些MPM(如Event MPM、work MPM),它们须将命令行中传入的参数转换为MPM内部的参数。通过调用ap_run_rewrite_args,使得每一个模块都有这种机会进行命令行重写。
当httpd启动的时候,它可能会带有一系列的命令行参数。完整的httpd命令如下:
httpd [-d serverroot] [-f config] [-C directive] [-c directive] [-D parameter] [-e level] [-E file] [-k start|restart|graceful|stop|graceful-stop] [-R directory] [-h] [-l] [-L] [-S] [-t] [-v] [-V] [-X] [-M]
在Windows系统中,还可以使用如下参数: Httpd [-k install|config|uninstall] [-n name] [-w] 各个参数处理如下:
apr_getopt_init(&opt, pcommands, process->argc, process->argv);
while ((rv = apr_getopt(opt, AP_SERVER_BASEARGS, &c, &optarg)) == APR_SUCCESS) { char **new;
- 13 -
沈阳工程学院毕业论文 第3章 Apache体系结构
switch (c) { case 'c':
new = (char **)apr_array_push(ap_server_post_read_config); *new = apr_pstrdup(pcommands, optarg); break; case ?C?:
new = (char **)apr_array_push(ap_server_pre_read_config); *new = apr_pstrdup(pcommands, optarg); break;
-C选项指定了再读取配置文件之前必须先处理directive的配置指令。而-c指令则指定了再读取配置文件之后,才能再处理directive的配置命令。-c中的指令保存到ap_server_post_read_config数组中,而-C中的指令则保存到ap_server_ pre_read_config数组中。
case 'd':
def_server_root = optarg; break;
-d serverroot选项蒋ServerRoot指令设置初始值为serverroot。它可以将配置文件中的ServerRoot指令所覆盖。其默认值为/uer/loca/apache2。
case 'D':
new = (char **)apr_array_push(ap_server_config_defines); *new = apr_pstrdup(pcommands, optarg); break;
-D parameter选项用于设置参数parameter,它配合配置文件中的
case 'e':
if (strcasecmp(optarg, \
ap_default_loglevel = APLOG_EMERG; } else if (strcasecmp(optarg, \
ap_default_loglevel = APLOG_ALERT;} else if (strcasecmp(optarg, \
ap_default_loglevel = APLOG_CRIT;} else if (strncasecmp(optarg, \ ap_default_loglevel = APLOG_ERR;} else if (strncasecmp(optarg, \ ap_default_loglevel = APLOG_WARNING;}
else if (strcasecmp(optarg, \ ap_default_loglevel = APLOG_NOTICE;} else if (strcasecmp(optarg, \
ap_default_loglevel = APLOG_INFO;}
else if (strcasecmp(optarg, \ ap_default_loglevel = APLOG_DEBUG;} else { usage(process); } break;
-e level选项在服务器启动时,设置日志的LogLevel为level。它用于在启动时,临时提高出错信息的详细程度,以帮助排错。
- 14 -