//本文是《Apache源代码全景分析》第二卷《体系结构和核心模块》中的第八章《配置文件管理》的草稿部分中,主要描述Apache中的指令概念,在后续的章节中我们将继续深入Apache中的配置文件的处理细节,包括Apache如何读取命令行参数,如何读取配置文件,如何执行配置文件中的指令以及如何存储指令等等。
//本文可以任意转载和阅读,但是不允许出现在任何盈利性质的出版物和印刷品中,任何部分抄袭或者全局抄袭都将保留法律诉讼的权力。
//转摘请保留上面的文字,并著名出处:http://blog.csdn.net/tingya
第七章 配置文件管理
Apache作为一个强大的非常灵活的Web服务器,配置文件功不可没,通过修改和调整配置文件,用户可以将Apache的功能发挥到极限。事实上,大部分的Apache管理员的工作就是调整Apache的配置文件,调整指令的参数。但是Apache的配置文件也是庞大的,指令的数目就够令人望而生畏,而且还在不断的扩充之中。尽管目前有很多的Apache的管理宝典之类的书指导用户如何使用这些指令,但是大部分的系统管理员对配置文件的机制已经指令的内部运行并不能很好的理解。它们并不了解指令是如何对Apache产生效果的,因此指令的使用也仅仅是人云亦云,简单模范而已。
古人云:知其然而知其所以然。或者说“授之以鱼,不如授之以渔”。本章我们将详细的对Apache的配置文件进行了深入的剖析,同时我们将追踪配置指令的作用流程,从而明白配置文件是如何产生效果的,部分的内容我们可能需要放到下一章结合模块部分描述。
4.1 Apache配置系统
4.1.1配置系统概述
在第七章我们描述worker MPM的时候曾经提到过系统中每个进程所能产生的线程数目并不是任意的,而是通过指令ThreadsPerChild指定的,同时系统中所能产生的进程数目也不是无限的,这由指令ServerLimit指令指定,比如
ThreadsPerChild 25
ServerLimit 16
上面的指令指定每个进程所能产生的线程数目为25个,而进程的最大产生数目为16。
那么这些指令应该保存在哪儿?Apache是什么时候读取这些指定的?它是怎么读取的?读取之后这些指令保存在哪儿,怎么保存的?这些指令最终是如何影响Apache的行为的呢?这些都是Apache配置系统需要解决的问题.
从整体上描述Apache配置系统,它应该包含三个主要部分:
1)、配置文件。通常情况下,配置系统会指定一些固定的文件作为配置文件,比如目前最主要的配置文件就是httpd.conf。
2)、配置指令。配置系统必须能够决定各个指令的含义,这样配置系统才能够正确的对其进行解释和处理。配置正确的指令或者是默认的值,或者由管理员进行修改;而解释配置指令则由Apache的核心以及各个模块来处理。
3)、
本章我们先重点描述前两个部分,在模块章节中我们描述第三部分。
4.2 配置文件
Apache服务器的配置是通过文本格式的配置文件来实现的,在文本文件中包含逐条的配置指令,正是通过这些逐条的指令从而实现对Apache运行的方方面面进行控制。在Apache2.0中涉及到的配置文件包括下面的两种:
■ httpd.conf
httpd.conf是Apache中最重要的配置文件,通常位于$ServerRoot下的conf目录中。不过在一些特殊的发行版本中,可能并不是这个名字,比如在许多支持SSL的Apache二进制发行版本中都会将二进制文件命名为httdsd,与之对应,配置文件也相应的改名为httdsd.conf。不管名称如何,文件内部的指令都是一样的。Httpd.conf是默认的配置文件,一般情况下不建议对其进行修改,因此通常的建议是你重新拷贝一份,对该拷贝进行修改,因此这种情况下,你可以在指令行中使用-f参数来指定新的非默认的配置文件。
从Apache 1.3.13 起通过-f指令不仅可以指定配置文件,还可以指定配置目录,即,如果配置文件是一个目录,Apache会解析该目录及其子目录中的所有文件作为配置文件。一种可能的用途是,可以通过在这个目录中建立小的配置文件来设置虚拟主机,这样就可以简单地增加和删除虚拟主机,而不用修改其他任何文件,使类似操作的自动化容易了许多。
通常,在服务器启动的时候,该文件被读取处理一次,同时在每次重新启动的时候又会被处理一次,因此对配置文件的任何修改都要等待到服务器重启后才能生效。
■ .htaccess
httpd.conf文件通常用于控制全局的配置信息,但是有的时候Apache需要提供目录级别的控制,比如定制特定目录被访问或者被列表显示等等。尽管httpd.conf内部提供了相关的目录配置指令,但是如果需要控制的目录数目较大的话,httpd.conf无疑会急剧膨胀。因此Apache中提供了另外的一种目录级别的配置,就是.htaccess。通常情况下,.htaccess文件位于需要进行控制的目录之内,因此系统中可能存在多个.htaccess。每个.htaccesss文件都有能力为它所处的目录以及所有的子目录设置授权、目录索引、过滤器以及其余的各种相关指令。因为.htaccess文件总是包含在用户自己的共享目录文档中,因此用户完全可以建立、更新和修改自己的.htaccess文件,而不需要直接去修改httpd.conf文件,从而可以保证httpd.conf的安全性,你要知道,每个人都去修改httpd.conf的话,造成的问题,你可能甚至都无法预料。
■ access.conf和srm.conf[1]
在Apache1.3以前的版本中,除了httpd.conf和.htaccess之外,还有两个相关的配置文件,就是access.conf和srm.conf。access.conf用于配置服务器的访问权限,控制不同用户和计算机的访问限制,srm.conf是服务器的资源映射文件,告诉服务器各种文件的MIME类型,以及如何支持这些文件。这两个文件都是从NCSA服务器继承而来的,具体的文件可以通过httpd.conf中的AccessConfig和ResourceConfig指令进行指定。不过从Apache1.3开始,这两个文件就已经废弃不用了,因此在Apache2.0中你看不到这两个文件,不过如果你非要设置AccessConfig和ResourceConfig指令,那么你可以将它们设置为“/dev/null”。
除了这三个设置文件之外,Apache还使用mime.types文件用于标识不同文件 。其文件名由TypesConfig指定,缺省时是mime.types。对应的MIME类型, magic文件设置不同MIME类型文件的一些特殊标识,使得Apache 服务器从文档后缀不能判断出文件的MIME 类型时,能通过文件内容中的这些特殊标记来判断文档的MIME类型。
图4.1描述了各个配置文件在整个请求中的的位置。
图4.1 配置文件处理
从上图中我们可以看出四个配置文件的处理时机是不一样的:在Apache启动或者重新启动的时候三个文件httpd.conf,access.conf以及srm.conf都会被处理,而.htaccess只有在特定的HTTP请求到来的时候才有可能被处理。
图4.2 配置文件在整个Apache中的位置
4.3 指令相关概念
4.3.1指令概述
由于Apache只定义了一些配置的框架和配置段规则,因此Apache配置文件的结构通常很容易理解。每个可用的指令以及指令的参数并不是由Apache核心决定的,而是由模块完全决定并实现和控制。因此,一般情况下,Apache配置文件的结构可以使用如下的语法片段进行描述:
configuration ::= directive*
directive ::= section-directive | simple-directive
section-directive ::= section-open configuration section-close
section-close ::= “<”directive-name directive-argument*”>”
simle-directive ::= directive-name directive-argument*
directive-name ::= “directory” | “documentroot”|…
directive-argument ::= …
换句话说,一个Apache配置文件可能是一个空文件,或者是包含了一个或者多个配置的指令,每个指令包含指令名称以及指令所需要的参数。指令的名称唯一的标识该指令本身,指令参数差异性则很大,参数类型,参数的个数都不尽相同,下面的是一个指令片断:
……
ServerRoot “C:/Program Files/Apache Group/Apache2”
TimeOut 30
<Directory “C:/Program Files/Apache Group/Apache2/manual”>
Options Indexs
<Files *.html>
SetHandler type-map
</Files>
</Directory>
……
从上面的片断可以看出,指令只是用于控制Apache的简单的命令而已,Apache从配置文件中读取这些指令,然后执行相应的操作从而实现执行这些指令。通过使用指令,Apache管理员可以控制整个Web服务器的行为。由于Apache中提供了种类繁多的指令,这些指令使得的Apache是一个高度可配置的Web服务器。
Apache的指令可以分为两种:简单指令以及配置段指令。配置段指令都是被包含在”<…>”中的指令,比如<Directory>…</Directory>。配置段指令总是会包含其余的指令。
尽管从上面的语法我们可以看到Apache的指令的语法非常的复杂,但实际上却非常的简单。在Apache进行指令处理的时候,Apache将逐行的读取这些配置指令,如果某行不是空行(即不匹配正则表达式”^[/t]*$”),同时也不是一个注释行(不匹配正则表达式”^[/t]*#.*$”),那么Apache将该行的第一个单词视为指令字,后面的其余的单词全部算作参数。如果某个行以”/”结尾,则下一行视上一行的继续。
因此,Apache配置指令的规则可以概括如下:
对于配置文件中的指令,其规则如下:
■ 使用UNIX路径法则:在所有的路径中使用“/”代替DOS下的“/”作为路径的分隔符。
■ 所有的注释行以“#”开始,同时注释行必须在一行结束,如果一行注释容不下,下一行必须继续以“#”开始。
■ 配置文件对大小写不敏感。但建议对非关键字均小写,而关键字则使用匈牙利方法,比如ServerRoot、TypesConfig等等。
■ 每行只能配置一个参数,配置的基本格式为:
参数 参数值
■ 如果指令过长,不能够在一行中完整地放置,此时需要分割成为多行,各个行之间用 / 进行组合。如果使用 / 字符,那么在反斜杠和行的末尾不能存在任何内容和字符,包括空格或者水平制表符。
■ 系统将忽略配置文件中多余的空白字符。
4.3.2指令参数
4.3.2.1参数类型
从原则上讲指令的参数可以是任何的字符串,只要指令处理函数能够理解即可。不过对于一些常用的指令参数,Apache中有一些默认的规定。一般,指令名称后面可以跟一个或多个用空格分开的参数。如果参数中有空格,则必须用双引号括起来, 用方括号括起来的是可选的参数。如果一个参数可以取多个值,则各个可能的值用"|"分开。 应该原样输入的文字使用缺省的字体,而可变的必须按实际情况加以替换的会加强显示。 使用可变参数个数的指令以"..."结尾,以示最后一个参数可以重复。
指令的参数类型非常多,以下列出很常用的部分:
(1) URL
一个完整的包括类型、主机名和可选的路径名的统一资源引用名,如 http://www.example.com/path/to/file.html
(2) URL-path
即url中类型和主机名之后的部分,如/path/to/file.html. url-path是表示资源在网络空间而不是在文件系统中的位置。
(3) file-path
即文件在本地文件系统中相对于根目录的路径,如 /usr/local/apache/htdocs/path/to/file.html. 除非指定了其他的值,不以斜杠开头的file-path将被视为对ServerRoot的相对路径。
(4) directory-path
即目录在本地文件系统中相对于根目录的路径,如 /usr/local/apache/htdocs/path/to/.
(5) filename
即不带路径信息的文件名,如file.html.
(6) regex
正则表达式,是对文本匹配模式的描述。指令的定义中会说明应该使用什么regex.
(7) extension
一般是指filename中最后一个"."号后面的部分。 但是,Apache可以辨认多个文件后缀,如果filename含有多个".", 则第一个"."后面由每个"."分隔开的部分都是此文件的后缀。 比如filename,file.html.en有两个后缀:.html和.en. 在Apache指令中指定extension时,可以有也可以没有前导的".",而且不区分大小写。
(8) MIME-type
一种用一个主格式类型和一个副格式类型并用斜杠分隔的描述文件格式的方法,如 text/html,img/jpeg等等。
(9) env-variable
这是Apache配置进程中定义的环境变量的名称。 注意,它不一定与操作系统中的环境变量相同。比如:
SetEnvIf Referer "^http://www.example.com/" local_referal
SetEnvIf Referer "^$" local_referal
<Directory /web/images>
Order Deny,Allow
Deny from all
Allow from env=local_referal
</Directory>
上面的代码就使用了环境变量local_referal,使用的时候必须通过env=xxxx进行指定。
4.3.2.2参数默认值
如果该指令有默认值(即,如果你没有在配置中明确指定, 那么Apache网站服务器会设置一个特定的值,并认为它是你设置的),会在此处说明。 如果没有,则会指明是"None"。注意,此处的默认值并不一定与服务器发行版中默认的httpd.conf中该指令的取值相同。
4.3.2.3配置段指令
4.3.3指令上下文
4.3.3.1上下文介绍
配置文件中的各个指令的作用范围是不一样的,可以分为全局指令,局部指令以及条件指令。默认情况下,配置文件中的指令是作用于整个服务器的,比如上面的示例中的ServerRoot和TimeOut指令,它们的作用范围则是针对整个服务器而言,但并不是所有的指令都这样,有些指令只是针对某个特定的目录,文件或者URL地址,通常情况下,那么我们将这类指令称之为局部指令,这类指令总是嵌在相关的配置指令段中,比如<Directory>, <DirectoryMatch>, <Files>, <FilesMatch>, <Location>, 以及 <LocationMatch>,比如上面的示例片断中<Directory “C:/Program Files/Apache Group/Apache2/manual”>…</Directory>中的指令仅仅对目录C:/Program Files/Apache Group/Apache2/manual中的文件产生作用。
另外还有一些指令并不是针对某个目录的,而是在特定的条件下才会产生效果的,我们将它们称之为条件指令。
类似于<Director>的这类指令我们称之为容器指令或者称之为配置段指令。比如<IfDefine>,<IfModule>以及<IfVersion>等等。
局部指令和条件指令都是以<…>…</…>之间,我们将这两种指令称之为配置段指令。
一个指令它所能影响的范围以及它产生效果的条件,我们称之为指令的上下文,在用户使用任何一个核心指令之前,了解指令能够使用的上下文环境是一件非常重要的工作,换句话说,你必须能够知道指令的作用上下文或者指令的范围。
4.3.3.2主服务器上下文(Server Config)
如果指令的上下文是主服务器,那么它能够作用的范围将是配置文件中容器配置段之外的所有的范围,即可以出现在httpd.conf,srm.conf以及access.conf,但却不能出现在<VirtualHost>或者<Directory>配置指令片断中。该指令也不能出现在.htaccess文件中。
4.3.3.3局部上下文(Local Config)
局部上下文通常是指某个虚拟主机,某个目录,某个URI以及某个Location,这种上下文之间的关系可以用下图进行描述。
图4.3 配置指令上下文
从上图可以看出,局部上下文可以分为两大类,一种是直接通过局部配置段指定,另外一种则是通过.htaccess文件进行指定。
最常用的配置段是针对文件系统和网络空间特定位置的配置段。首先必须理解文件系统和网络空间这两个概念的区别,文件系统是指操作系统所看见的磁盘视图,比如,在Unix文件系统中,Apache会被默认安装到/usr/local/apache2 ,在Windows文件系统中,Apache会被默认安装到"C:/Program Files/Apache Group/Apache2"(注意:Apache始终用正斜杠而不是反斜杠作为路径的分隔符,即使是在Windows中)。相反,网络空间是网站被web服务器发送以及被客户在浏览器中所看到的视图。所以网络空间中的路径/dir/ 在Apache采用默认安装路径的情况下对应于Unix文件系统中的路径/usr/local/apache2/htdocs/dir/ 。由于网页可以从数据库或其他地方动态生成,因此,网络空间无须直接映射到文件系统。
<Directory>和<Files>指令与其相应的正则表达式版本(<DirectoryMatch>和<FilesMatch>)一起作用于文件系统的特定部分, <Directory>配置段中的指令作用于指定的文件系统目录及其所有子目录,.htaccess文件可以达到同样的效果。下例中,/var/web/dir1 及其所有子目录被允许进行目录索引。
<Directory /var/web/dir1>
Options +Indexes
</Directory>
<Files>配置段中包含的指令总是应用于特定的文件而无论这个文件实际存在于哪个目录,指定的文件可以是普通的文件名称,另外可以使用<FilesMatch>指定正则表达式,是正则表达式的文件名称。下例中的配置指令如果出现在配置文件的主服务器段,则会拒绝对位于任何目录下的private.html的访问。
<Files private.html>
Order allow,deny
Deny from all
</Files>
<Files>和<Directory>段的组合可以作用于文件系统中的特定文件。下例中的配置会拒绝对 /var/web/dir1/private.html ,/var/web/dir1/subdir2/private.html,/var/web/dir1/subdir3/private.html 等任何 /var/web/dir1/ 目录下private.html的访问。
<Directory /var/web/dir1>
<Files private.html>
Order allow,deny
Deny from all
</Files>
</Directory>
网络空间容器
<Location>指令与其相应的正则表达式版本(<LocationMatch>)一起作用于网络空间的特定部分。<Location>或者<LocationMatch>中包含的指令通常应用于特定的URL或者它的一部分,URL可以是普通的URL地址,也可以是正则表达式格式的URI。
如果指令的作用范围是仅仅限于.htaccess文件中,那么该指令的上下文应该是属于针对目录级别的。当Apache处理HTTP请求从而遍历文件系统的时候进行读取并将指令作用于对应的目录。该上下文也被细分为五种子上下文,这些上下文在httpd.conf中的AllowOverride指令允许的时候发生作用。
下例中的配置会拒绝对任何以"/private"开头的URL路径的访问,比如:http://yoursite.example.com/private、http://yoursite.example.com/private123、http://yoursite.example.com/private/dir/file.html 等所有以"/private"开头的URL路径。
<Location /private>
Order Allow,Deny
Deny from all
</Location>
<Location>指令与文件系统无关,下例演示了如何将特定的URL映射到Apache内部的处理器mod_status ,而并不要求文件系统中确实存在server-status文件。
<Location /server-status>
SetHandler server-status
</Location>
<Directory>和<Files>都提供了正则表达式版本的
<Directory>,<Files>, 和<Location>指令可以使用类似C标准库中的fnmatch的外壳通配符。符号"*"匹配任何字符串,"?"匹配任何单个的字符,"[seq]" 匹配seq序列中的任何字符,符号"/"不匹配为任何通配符所匹配,所以不能显式使用。
这些指令都有一个正则的配对指令,<DirectoryMatch>, <FilesMatch>和<LocationMatch>,可以使用与perl一致的正则表达式,以提供更复杂的匹配。但是还须注意下文配置的合并中有关使用正则表达式会如何作用于配置指令的内容。
下例使用非正则表达式的通配符来改变所有用户目录的配置:
<Directory /home/*/public_html>
Options Indexes
</Directory>
下例使用正则表达式一次性拒绝对多种图形文件的访问:
<FilesMatch /.(?i:gif|jpe?g|png)$>
Order allow,deny
Deny from all
</FilesMatch>
虚拟主机空间容器<VirtualHost>
包含的指令应用于特定的虚拟主机,这些虚拟主机之间通过唯一的IP地址和端口对进行区分。
.htaccess中的隐含上下文
除了上面的显式的指令上下文之外,上下文之间还包含了隐含的上下文关系,包括:
■ .htaccess文件上下文AuthConfig和Limit通常总是包含服务器配置<Directory>,<Files>和<Location>配置段。
■ .htaccess文件上下文Options,FileInfo以及Indexs总是包含所有的针对服务器的配置,即整个httpd.conf。
除此之外,<Location>和<Files>上下文中允许的指令与<Directory>中的指令一样对待。
为了明确的表示指令的作用上下文,Apache在http_config.h中定义了相关的常量来进行描述:
#define NOT_IN_VIRTUALHOST 0x01 /**< Forbidden in <Virtualhost> */
#define NOT_IN_LIMIT 0x02 /**< Forbidden in <Limit> */
#define NOT_IN_DIRECTORY 0x04 /**< Forbidden in <Directory> */
#define NOT_IN_LOCATION 0x08 /**< Forbidden in <Location> */
#define NOT_IN_FILES 0x10 /**< Forbidden in <Files> */
/** Forbidden in <Directory>/<Location>/<Files>*/
#define NOT_IN_DIR_LOC_FILE (NOT_IN_DIRECTORY|NOT_IN_LOCATION|NOT_IN_FILES)
/** Forbidden in <VirtualHost>/<Limit>/<Directory>/<Location>/<Files> */
#define GLOBAL_ONLY (NOT_IN_VIRTUALHOST|NOT_IN_LIMIT | NOT_IN_DIR_LOC_FILE)
同时还定义了函数ap_check_cmd_context用于检查指令是否出现在其应该出现的上下文。关于该该函数,我们在后面第五章详细描述。
4.3.3.4条件上下文
Apache中允许设定某些指令在特定的条件下才产生效果。这三种上下文主要是指<IfDefine>,<IfModule>以及<IfVersion>。
<IfDefine>容器中的指令只有在httpd命令行中设定了特定的参数后才有效。下例中,只有在服务器用 httpd -DClosedForNow 方式启动时,所有的请求才会被重定向到另一个站点:
<IfDefine ClosedForNow>
Redirect / http://otherserver.example.com/
</IfDefine>
<IfModule>容器很相似,但是其中的指令只有当服务器启用特定的模块时才有效(或是被静态地编译进了服务器,或是被动态装载进了服务器),注意,配置文件中该模块的装载指令LoadModule行必须在出现在此容器之前。这个容器应该仅用于你希望无论特定模块是否安装,配置文件都能正常运转的场合;而不应该用于容器中的指令在任何情况下都必须生效的场合,因为它会抑制类似模块没找到之类的有用出错信息。
下例中,MimeMagicFiles指令仅当mod_mime_magic模块启用时才有效。
<IfModule mod_mime_magic.c>
MimeMagicFile conf/magic
</IfModule>
<IfVersion>指令与<IfDefine>和<IfModule>很相似,但是其中的指令只有当正在执行的服务器版本与指定的版本要求相符时才有效。这个模块被设计用于测试套件、以及在一个存在多个不同httpd版本的大型网络中需要分别针对不同版本使用不同配置的情况。
<IfVersion >= 2.1>
# 仅在版本高于 2.1.0的时候才生效
</IfVersion>
<IfDefine>、<IfModule>、<IfVersion>都可以在条件前加一个"!"以实现条件的否定,而且都可以嵌套以实现更复杂的配置。
4.3.3.5上下文嵌套关系
Apache中各个上下文之间所能影响的范围并不是相同的,相反,它们之间有着严格的包含差异关系,通常各个上下文之间的包含关系从大到小可以用下图描述:
从上图中可以看出,包含范围大小依次为<VirtualHost>、<Directory>或者<Location>、<Files>以及<Limit>。因此各种上下文在嵌套中必须遵循下面的嵌套规则:
■ <Directory>配置段不允许出现在<Limit>,<Location>,<Files>或者其余的<Directory>配置段之间
■ <Location>配置段不允许出现在<Limit>,<Directory>,<Files>以及其余的<Location>配置段之间
■ <Files>配置段不允许出现在<Limit>,<Directory>,<Location>以及其余的<Files>配置段之间
■ <Directory>和<Location>配置段不允许出现在.htaccess文件中,但是<Files>则允许出现。
更加详细的嵌套关系可以用下表进行描述:
|
<VirtualHost> |
<Directory> |
<Location> |
<Files> |
<Limit> |
.htaccess |
<VirtualHost> |
╳ |
╳ |
╳ |
╳ |
╳ |
╳ |
<Directory> |
√ |
╳ |
╳ |
╳ |
╳ |
╳ |
<Location> |
√ |
╳ |
╳ |
╳ |
╳ |
╳ |
<Files> |
√ |
√ |
╳ |
╳ |
╳ |
√ |
<Limit> |
√ |
√ |
╳ |
√ |
╳ |
√ |
从语义上看,允许在<Directory>段中使用的指令当然也可以在<DirectoryMatch>、<Files>, <FilesMatch>、<Location>、 <LocationMatch>、<Proxy>和<ProxyMatch>段中使用,但是有几个例外:
■ AllowOverride指令只能出现在<Directory>段中;
■ Options中的FollowSymLinks和 SymLinksIfOwnerMatch只能出现在<Directory>段或者.htaccess文件中;
■ Options指令不能用于<Files>和<FilesMatch>段。
4.3.3.6上下文合并和继承
配置段会按非常特别的顺序依次生效,由于这会对配置指令的处理结果产生重大影响,理解它的流程尤为重要。另外,如果不同的配置段中同时出现相同的指令,那么这些指令之间也存在不同的产生作用的方式,包括完全覆盖,继承合并。覆盖的概念很好理解,继承合并遵循一定的规则,合并的顺序是:
<Directory>
(除了正则表达式)和.htaccess同时处理;(如果允许的话,.htaccess的设置会覆盖<Directory>
的设置)
<DirectoryMatch>
(和<Directory ~>
)
<Files>
和<FilesMatch>
同时处理;
<Location>
和<LocationMatch>
同时处理;
除了<Directory>
,每个组都按它们在配置文件中出现的顺序被依次处理,而<Directory>
组,会按字典顺序由短到长被依次处理。例如,<Directory /var/web/dir>
会先于<Directory /var/web/dir/subdir>
被处理。如果有多个指向同一个目录的<Directory>
段,则按它们在配置文件中的顺序被依次处理。用Include
指令包含进来的设置被视为按原样插入到Include
指令的位置。
位于<VirtualHost>
段中的配置段在外部相对应的段处理完毕以后再处理,这样就允许虚拟主机覆盖主服务器的设置。
后面的段覆盖前面的相应的段。
这是一个假设的演示合并顺序的例子。如果这些指令都起作用,则会按A > B > C > D > E的顺序依次生效。
<Location />
E
</Location>
<Files f.html>
D
</Files>
<VirtualHost *>
<Directory /a/b>
B
</Directory>
</VirtualHost>
<DirectoryMatch "^.*b$">
C
</DirectoryMatch>
<Directory /a/b>
A
</Directory>
在这个更具体的例子中,无论在<Directory>段中加了多少访问限制,由于<Location>段将会被最后处理,从而会允许不加限制的对服务器的访问,可见合并的顺序是很重要的,千万小心!
<Location />
Order deny,allow
Allow from all
</Location>
# Woops! This <Directory> section will have no effect
<Directory />
Order allow,deny
Allow from all
Deny from badguy.example.com
</Directory>
通常情况下各个指令完全独立,这意味着指令的次序并不重要,大多数指令都不会影响服务器怎样解释其余的指令,不过也有例外,有些指令相互之间还是具有依赖性的,比如LoadModule指令,当配置文件读取每一行指令的时候,都会尝试在当前已经加载到服务器的模块中找到该指令。如果你试图在加载实现指令的模块之前就使用指令,那么服务器将会输出错误信息,并且退出。
4.3.3.7指令位置
从前面的讨论中我们已经知道了指令上下文的概念。一个指令能够出现的位置即它能出现在哪些上下文环境中我们称之为指令位置控制。Apache中提供了指令位置字段的概念来控制一个指令所允许出现的上下文位置。位置字段主要用于控制各个指令在配置文件中允许出现的位置,包括三种:顶层位置,目录区和虚拟主机区。如果服务器发现一个指令不允许出现在出现的地方,比如LoadModule只允许出现在顶层位置,如果发现其在<Directory>中出现,服务器将报错,同时打印错误信息退出。另外位置字段还将控制指令是否允许在文件.htaccess中使用。
对于指令位置字段,Apache中提供了下面几个控制选项:
#define OR_NONE 0
#define OR_LIMIT 1
#define OR_OPTIONS 2
#define OR_FILEINFO 4
#define OR_AUTHCFG 8
#define OR_INDEXES 16
#define OR_UNSET 32
#define ACCESS_CONF 64
#define RSRC_CONF 128
#define EXEC_ON_READ 256
#define OR_ALL (OR_LIMIT|OR_OPTIONS|OR_FILEINFO|OR_AUTHCFG|OR_INDEXES)
对于上面的选项,Apache又分成两类:普通配置文件说明选项和.htaccess文件说明选项。
Ø ACCESS_CONF
该位置字段允许指令出现在Directory或者Location区间以内的*配置文件中,因此该选项通常用来对Apache中目录或者文件进行某些控制。
Ø RSRC_CONF
该选项允许指令出现在Directory或者Location区间以外的*配置文件中,当然也可以出现在VirtualHost区间中,因此该选项通用用来对Apache服务器或者虚拟主机的进行某些控制。
Ø EXEC_ON_READ
该选项是Apache2.0中新增加的。在Apache1.3中,对指令的处理是边读边执行的,而Apache2.0中并不是这样。Apache2.0首先预处理配置文件,将所有的配置指令读取到一个树型结构中,树的每个结点为ap_directive_t类型。我们称之为配置树。一旦建立配置树,Apache然后才会遍历并处理所有指令。通常情况下,这种处理方式会很好,因为延后处理使的可以控制模块之间的依赖性。比如,线程化的MPM需要在定义MaxClients指令之前就必须定义ThreadsPerChild指令。MPM不会强求管理员处理这种情况,即使在配置文件中ThreadsPerChild定义在MaxClients之后,Apache也会在分析之前在配置树中进行次序调整。
不过延后处理也不是完美无缺,有的时候可能导致问题。比如,Include通常用于在配置文件中读取另外一个配置文件。如果不能立即读取到配置文件,那么第二个配置文件中的配置指令将不可能生成到配置树中。解决的方法就是在读取到Include指令的时候取消滞后策略,而是立即执行该指令。EXEC_ON_READ选项可以强制服务器在将指令从配置文件中读取之后立即执行。
通过前面的分析,我们可以看出,如果某个指令可能会改变配置树,那么该指令就应该为EXEC_ON_READ,不过所谓的改变配置树不是指改变配置树的顺序,而是改变配置树的信息。如果想要改变配置树的次序,那么应该在预先配置阶段进行或者在处理配置指令时候完成这样的工作。
除了上面的三个用于对配置文件进行控制,Apache中还提供了八个选项用于控制.htaccess文件中指令。
Ø OR_NONE
该选项则不允许在.htaccess文件中使用任何指令,这是默认值。不过大多数指令都会对其进行修改。只有那些仅仅在*配置文件中才有效的指令以及那些仅仅有服务器管理员才可以使用的指令才会设置该选项。
Ø OR_LIMIT
只有那些可能涉及虚拟主机访问的指令才会设置该选项。在核心服务器上,Allow,Deny以及Order指令都会设置该选项。使用这个选项的指令允许放置在Directory和Location标签中,以及AllowOverride设置为Limit的.htaccess文件中。
Ø OR_OPTIONS
使用该选项的指令通常用来控制特定的目录设置。在标准的Apache发行版本中,Options指令和XbitHack指令都会使用该选项。使用该选项的指令必须置于Directory和Location以及AllowOverride设置为Options的.htaccess文件中。
Ø OR_FILEINFO
使用该选项的指令通常用于控制文档类型或者文档信息。设置该选项的指令包括SetHandler,ErrorDocument,DefaultType等等。这中类型的指令可以存在于Directory和Location标签以及AllowOverride设置为FileInfo的.htaccess文件中。
Ø OR_AUTHCFG
使用该选项的指令通常用语控制授权或者认证等信息。设置该选项的指令包括AuthUserFile,AuthName以及Require。这种指令可以存在于Directory和Location以及AllowOverride设置为AuthConfig的.htaccess文件中。
Ø OR_INDEXS
使用该选项的指令通常用于控制目录索引的输出。示例指令包括AddDescription,AddIcon,AddIconByEncoding。这种指令可以存在于Directory和Location以及AllowOverride设置为Indexs的.htaccess文件。
Ø OR_ALL
这个选项是前面所有选项的组合。使用该选项的指令可以存在于Directory和Location标签中,以及只要AllowOverride不为None的.htaccess文件中。
Ø OR_UNSET
这个特殊的值指出,这个目录没有设置重写。模块不应该使用值。核心会使用这个值来正确的控制继承。
Apache中使用req_override记录指令的位置字段,目前在配置文件中,对于<Directory>标签外部的部分,req_override状态为:
RSRC_CONF|OR_OPTIONS|OR_FILEINFO|OR_INDEXS
而在<Directory>标签内部的部分,状态为:
ACCESS_CONF|OR_LIMIT|OR_OPTIONS|OR_FILEINFO|OR_AUTHCFG|OR_INDEXS
而在.htaccess文件中,状态则由AllowOverride指令决定。
4.3.4指令参数类型
Apache中不同指令的参数差异很大,从没有参数到多个参数不等。为了能够准确的处理各种指令以及它的参数,Apache中使用指令参数类型来标识一个指令的参数。不同的指令参数类型指导指令处理程序如何处理指令的参数。Apache中提供了12种类型的指令,这些类型是与实际的配置文件中指令处理相一致的。每种指令都大同小异,唯一的区别就在于其处理的参数的数目以及在将指令传递给指令实现函数之前,服务器如何解释这些参数的方式。由于各个指令的参数不相同,为此也导致了指令的处理函数的格式不相同。
Apache中对于指令类型的定义是通过枚举类型cmd_how来实现的,cmd_how定义如下:
enum cmd_how {
RAW_ARGS,
TAKE1,
TAKE2,
ITERATE,
ITERATE2,
FLAG,
NO_ARGS,
TAKE12,
TAKE3,
TAKE23,
TAKE123,
TAKE13
};
对于所有的指令处理函数,其都将返回字符串。如果指令处理函数正确的处理了指令,那么函数返回NULL,否则应该返回错误提示信息。对于各种指令,服务器的处理方法如下:
Ø RAW_ARGS
该指令会通知Apache不要对传入的参数做任何的处理,只需要原封不动的传递给指令处理函数即可。使用这种指令会存在一定的风险,因为服务器不做任何的语法检查,因此难免会有错误成为“漏网之鱼”。
这种指令的处理函数通常如下所示:
const char * func(cmd_parms* parms , void* mconfig , char* args);
args是传入的参数,其只是简单的指令行内容,当然也包括指令在内,对于该参数func函数不做任何检查,直接使用。
Ø TAKE1
顾名思义,这种类型的指令“Take 1 argument”,其只允许传入一个参数。这种指令的处理函数通常如下所示:
const char * func(cmd_parms* parms , void* mconfig , const char* first);
first则是需要传入的第一个指令参数。
Ø ITERATE
该类型指令属于迭代类型。这种指令允许传入多个参数,不过一次只能处理一个,服务器必须遍历处理它们。每次遍历处理的过程又与TAKE1类型指令相同。因此这种指令的处理函数与TAKE1指令相同:
const char * func(cmd_parms* parms , void* mconfig , const char* first);
由于一次只能处理一个参数,因此如果指令具有N个参数的化,则func函数必须调用N次,每次将第N个参数传入给函数。
Ø TAKE2,TAKE12,ITERATE2
TAKE2类型必须向指令处理函数传入两个参数;而TAKE12可以接受一个或者两个参数。ITERATE2与ITERATE1类似,都属于参数迭代处理类型,不过ITERATE2要求至少传递两个参数给函数。不过,第二个参数能够使用多次,服务器会遍历它们,直到所有的参数都传递给处理函数。如果只向TAKE12传递一个参数,那么服务器将把第二个参数设置为NULL。这三种类型的指令处理函数原型如下:
const char *two_args_func(cmd_parms* parms , void* mconfig,const char* first,const char* second);
对于TAKE2类型而言,函数two_args_func只被调用一次,两个参数一次性传递给函数;对于TAKE12而言,如果只传递一个参数的话,第二个参数将被设置为NULL,如果传递两个参数的话,与TAKE2相同;对于ITERATE2而言,如果有N个参数的话,则处理函数至少将调用N-1次。
Ø TAKE3,TAKE23,TAKE13,TAKE123
这组指令最多都可以接受3个参数,如果参数超过三个,则处理函数将会报错。TAKE3意味着参数必须是三个;TAKE23则意味着至少两个参数,也可以为三个,不能少于两个或者多于三个。TAKE13则意味这可以接受一个参数或者三个参数,除此之外都是非法。TAKE123意味着可以接受一个,两个或者三个参数。这四种指令的处理函数原型如下:
const char *three_args_func(cmd_parms* parms , void* mconfig,const char* first,const char* second,const char* third);
Ø NO_ARGS
该类型的指令不接受任何的参数,其最常用的就是作为复杂指令的闭标签存在。比如<Directory>总是必须有</Directory>与之对应。尽管<Directory>通常需要一个参数来指定其所设置的目录,不过对于</Directory>则没有这个参数。因此其就是NO_ARGS指令类型。这种指令的处理函数通常如下所示:
const char *no_args_func(cmd_parms* parms , void* mconfig);
Ø FLAG
这种类型最简单,其只允许用来启动或者关闭的指令。与前面的几种类型中,服务器直接将配置指令后的参数传递给函数不同,这种指令不会直接传递参数,而是首先对参数进行处理,并将处理的结果true或者false作为进一步的参数传递给函数。这种指令的处理函数通常如下所示:
const char *flag_args_func(cmd_parms* parms , void* mconfig,int flag);
不管是什么指令,其对应的处理函数都是以两个参数开始:cmd_parms* parms和void* mconfig。cmd_parms结构用来存储处理配置指令时候所需要的辅助内容。在处理任何配置信息文件的时候,该结构都将被创建。其用于Apache核心传递各种参数给指令处理方法。关于cmd_parms的具体解释,我们在后面将给出。
另一个参数void* mconfig表示针对指令位置的配置记录,基于所遇到的指令位置的不同,该配置记录可以是服务器配置记录,也可以是目录配置记录。
[1] Apache最早是从NCSA HTTP服务器继承而来,在NCSA服务器中,httpd.conf、access.conf和srm.conf都被使用而且分工非常明确。Httpd.conf用以保存通用的服务器配置指令;access.conf则用于包含访问控制指令;srm.conf则包含资源配置指令。
但是Apache从来都没有严格的区分这三个文件的使用,即使从最早的Apache版本算起,Apache对待这三个配置文件也是一视同仁的,任何一个指令都可以包含在任何一个文件中。Apache中之所以继续保留种两个文件,完全是为了保证向后兼容性。不过这种兼容性经常因此误会,不知道的用户通常会很谨慎,防止指令放错在不该出现的文件中。因此Apache最终只能官方声明这两个文件作废。这就是为什么在Apache 2.0中你见不到它们的原因。
当然将httpd.conf的功能分割到多个文件中的想法却并不过时,而且有的时候甚至是必须的。为此Apache中提供了更通用的Include指令。具体的使用我们在后面的部分将详细描述。