13.8 目录处理器
13.8.1目录处理器概述
除了普通的磁盘文件之外,还有另外一种情况就是请求被映射到目录。那么这时候服务器就必须确定如何进行响应,通常情况下有三种情况可供选择:
1 返回默认的索引页面。
大部分情况下这是默认的选择。通常默认页面会取名为default.html,index.html等名称。为了实现默认的索引页面,Apache中提供了mod_dir模块,它内部提供了DirectoryIndex指令用于指定默认的索引页面。
2. 发送目录中的所有文件的列表。
如果没有找到DirectoryIndex指令规定的文件,那么服务器就会生成目录中所有的文件的列表。这个由模块mod_autoindex实现。实现的原理很简单:服务器会查看目录中的所有的文件,然后为每个文件生成一行数据。
3. 返回错误页面响应消息
一些用户会对一些隐私的目录进行访问控制。如果用户访问到这些目录,此时,服务器会返回特定的错误页面给客户端以进行错误提示。
我们分别了解Apache是如何处理这三种处理器的。
13.8.2 返回默认页面处理器
mod_dir实质上并不是真正的内容处理器模块,不过它影响内容的生成方式。为了指定目录中的默认页面,mod_dir模块中提供了DirectoryIndex指令,比如:
DirectoryIndex index.html
这个指令会告诉服务器在目录中查找名为index.html的文件,并将其返回。
DirectoryIndex index.html default.html
这个指令告诉服务器在目录中首先查找名为index.html的文件,如果没有找到,再查找default.html文件。DirectoryIndex指令允许指定多个
DirectoryIndex index.html /indexes/nofile.html
这个指令告诉服务器在目录中首先查找名为index.html的文件,如果没有找到,再去DocumentRoot之下的/indexes目录中查找nofile.html。
DirectoryIndex /cgi-bin/index.pl
这个指令高速服务器直接调用/cgi-bin/目录下的index.pl脚本进行执行。
DirectoryIndex指令可以出现在全局作用域中、虚拟主机、.htaccess文件、或者在<Directory>容器中使用。将<Directory>用于<Location>和<Files>中并不合理,因此不允许在那些地方使用它们。
如果你拥有多个能够作为默认文件使用的文件,那么就应该将最常用的文件列在最初的位置。这是因为服务器会根据文件所列的次序来对他们进行搜索,服务器在找所要的文件之前搜索的文件越多,它运行的速度就越慢。
mod_dir模块并不负责读取和生成返回给客户端的内容,而仅仅负责默认页面的设置处理。除了DirectoryIndex指令,mod_dir还支持另外一个指令DirectorySlash,DirectorySlash指令决定mod_dir是否通过在请求的URL结尾补上"/"使其重定向到其所指向的目录。
代表性的,如果用户请求一个资源并且结尾没有"/",若该资源是指向一个目录,mod_dir将通过在请求的URL结尾补上"/"使其重定向到其所指向的目录。但是在原始请求中就必须加上结尾的"/"有以下好处:
(1)用户必须使用规范的URL来请求资源。
(2)mod_autoindex将会正确工作。因为它并不会补全路径,所以将会指向错误的路径。
(3)DirectoryIndex将只评估有"/"结尾的目录。
如果你不希望这个自动重定向的功能生效,并且不在乎上述影响,你可以关闭它:
DirecotyrSlash OFF
Mod_dir中使用dir_config_rec记录目录配置信息:
typedef struct dir_config_struct {
apr_array_header_t *index_names;
slash_cfg do_slash;
} dir_config_rec;
index_names记录所有的出现在DirectoryIndex指令后面的索引页面名称。而do_slash则记录是否开启了DirectorySlash指令。
对于磁盘上的静态文件,默认是由default_handler处理器完成,不过如果客户端请求的是目录,并且目录中使用DirectoryIndex设置了默认的索引页面,则在请求处理之前,必须将默认索引页面和请求目录共同构成实际的文件路径。这一步必须在调用处理器之前完成。
Apache提供了fixups挂钩用以完成调用处理器之前所有的必须完成的“修补”工作。事实上对于mod_dir而言,将目录和索引文件组合成磁盘文件路径的工作也正好可以在该阶段完成,因此mod_dir模块中实现了fixups挂钩:
static void register_hooks(apr_pool_t *p)
{
ap_hook_fixups(fixup_dir,NULL,NULL,APR_HOOK_LAST);
}
module AP_MODULE_DECLARE_DATA dir_module = {
……
register_hooks /* register hooks */
};
fixup_dir函数的实现如下:
static int fixup_dir(request_rec *r)
{
dir_config_rec *d;
char *dummy_ptr[1];
char **names_ptr;
int num_names;
int error_notfound = 0;
if (r->finfo.filetype != APR_DIR) {
return DECLINED;
}
if (!r->handler) {
r->handler = DIR_MAGIC_TYPE;
}
if (r->path_info && *r->path_info) {
return DECLINED;
}
(1)、mod_dir只能处理目录,不能直接处理文件。文件直接调用default_handler进行处理。
d = (dir_config_rec *)ap_get_module_config(r->per_dir_config,
&dir_module);
/* Redirect requests that are not '/' terminated */
if (r->uri[0] == '/0' || r->uri[strlen(r->uri) - 1] != '/')
{
char *ifile;
if (!d->do_slash) {
return DECLINED;
}
/* Only redirect non-get requests if we have no note to warn
* that this browser cannot handle redirs on non-GET requests
* (such as Microsoft's WebFolders).
*/
if ((r->method_number != M_GET)
&& apr_table_get(r->subprocess_env, "redirect-carefully")) {
return DECLINED;
}
if (r->args != NULL) {
ifile = apr_pstrcat(r->pool, ap_escape_uri(r->pool, r->uri),
"/", "?", r->args, NULL);
}
else {
ifile = apr_pstrcat(r->pool, ap_escape_uri(r->pool, r->uri),
"/", NULL);
}
apr_table_setn(r->headers_out, "Location",
ap_construct_url(r->pool, ifile, r));
return HTTP_MOVED_PERMANENTLY;
}
if (strcmp(r->handler, DIR_MAGIC_TYPE)) {
return DECLINED;
}
if (d->index_names) {
names_ptr = (char **)d->index_names->elts;
num_names = d->index_names->nelts;
}
else {
dummy_ptr[0] = AP_DEFAULT_INDEX;
names_ptr = dummy_ptr;
num_names = 1;
}
for (; num_names; ++names_ptr, --num_names) {
/* XXX: Is this name_ptr considered escaped yet, or not??? */
char *name_ptr = *names_ptr;
request_rec *rr;
/* Once upon a time args were handled _after_ the successful redirect.
* But that redirect might then _refuse_ the given r->args, creating
* a nasty tangle. It seems safer to consider the r->args while we
* determine if name_ptr is our viable index, and therefore set them
* up correctly on redirect.
*/
if (r->args != NULL) {
name_ptr = apr_pstrcat(r->pool, name_ptr, "?", r->args, NULL);
}
rr = ap_sub_req_lookup_uri(name_ptr, r, NULL);
/* The sub request lookup is very liberal, and the core map_to_storage
* handler will almost always result in HTTP_OK as /foo/index.html
* may be /foo with PATH_INFO="/index.html", or even / with
* PATH_INFO="/foo/index.html". To get around this we insist that the
* the index be a regular filetype.
*
* Another reason is that the core handler also makes the assumption
* that if r->finfo is still NULL by the time it gets called, the
* file does not exist.
*/
if (rr->status == HTTP_OK
&& ( (rr->handler && !strcmp(rr->handler, "proxy-server"))
|| rr->finfo.filetype == APR_REG)) {
ap_internal_fast_redirect(rr, r);
return OK;
}
/* If the request returned a redirect, propagate it to the client */
if (ap_is_HTTP_REDIRECT(rr->status)
|| (rr->status == HTTP_NOT_ACCEPTABLE && num_names == 1)
|| (rr->status == HTTP_UNAUTHORIZED && num_names == 1)) {
apr_pool_join(r->pool, rr->pool);
error_notfound = rr->status;
r->notes = apr_table_overlay(r->pool, r->notes, rr->notes);
r->headers_out = apr_table_overlay(r->pool, r->headers_out,
rr->headers_out);
r->err_headers_out = apr_table_overlay(r->pool, r->err_headers_out,
rr->err_headers_out);
return error_notfound;
}
/* If the request returned something other than 404 (or 200),
* it means the module encountered some sort of problem. To be
* secure, we should return the error, rather than allow autoindex
* to create a (possibly unsafe) directory index.
*
* So we store the error, and if none of the listed files
* exist, we return the last error response we got, instead
* of a directory listing.
*/
if (rr->status && rr->status != HTTP_NOT_FOUND
&& rr->status != HTTP_OK) {
error_notfound = rr->status;
}
ap_destroy_sub_req(rr);
}
if (error_notfound) {
return error_notfound;
}
/* nothing for us to do, pass on through */
return DECLINED;
}
13.8.3 返回文件列表
如果没有找到DirectoryIndex指令规定的文件,那么服务器就会生成目录中所有文件的列表。通常这由mod_autoindex文件完成。服务器会查看请求目录中的所有的文件并为每一个文件在相应中生成一条数据。当然mod_autoindex提供了非常多的指令,通过这些指令,你可以对列表的格式进行各种控制。
13.8.4 返回错误响应
在任何时候如果发生错误的时候,Apache都会返回对应的错误响应消息。
13.9 mod_info
在13.5章中我们已经讨论了默认过滤器的处理细节,这种过滤器所针对的是实实在在的在磁盘中存在的静态HTML文件。因此它的主要任务就是读取对应的文件,并将其发送给客户端。本章中我们描述另外一种处理器的处理细节。alias_handler,info_handler以及status_handler等都属于这种处理器,他们并不处理实际的文件。而是针对特定的URL进行特定的处理。比如info_handler处理的是http://your.host.dom/server-info格式的URL,而status_handler则是处理http://your.host.dom/server-status。
对于这种URL,Apache将会首先在配置文件中搜索,如果搜索到对应的针对该 URI的配置,那么他将直接调用对应的处理器进行处理。通常情况下处理过程并不会去读取磁盘中对应的文件,因为文件并不存在。处理器要做的就是生成特定的响应并返回给客户端