//本文是《Apache源代碼全景分析》第二卷《體系結構和核心模塊》中的第八章《配置文件管理》的草稿部分中,主要描述Apache中的指令概念,在后續的章節中我們將繼續深入Apache中的配置文件的處理細節,包括Apache如何讀取命令行參數,如何讀取配置文件,如何執行配置文件中的指令以及如何存儲指令等等。
//本文可以任意轉載和閱讀,但是不允許出現在任何盈利性質的出版物和印刷品中,任何部分抄襲或者全局抄襲都將保留法律訴訟的權力。
//轉摘請保留上面的文字,并著名出處:http://blog.csdn.net/tingya
?
第七章?配置文件管理
Apache作為一個強大的非常靈活的Web服務器,配置文件功不可沒,通過修改和調整配置文件,用戶可以將Apache的功能發揮到極限。事實上,大部分的Apache管理員的工作就是調整Apache的配置文件,調整指令的參數。但是Apache的配置文件也是龐大的,指令的數目就夠令人望而生畏,而且還在不斷的擴充之中。盡管目前有很多的Apache的管理寶典之類的書指導用戶如何使用這些指令,但是大部分的系統管理員對配置文件的機制已經指令的內部運行并不能很好的理解。它們并不了解指令是如何對Apache產生效果的,因此指令的使用也僅僅是人云亦云,簡單模范而已。
古人云:知其然而知其所以然?;蛘哒f“授之以魚,不如授之以漁”。本章我們將詳細的對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
在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通??偸前掌髋渲?span lang="EN-US"><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表示針對指令位置的配置記錄,基于所遇到的指令位置的不同,該配置記錄可以是服務器配置記錄,也可以是目錄配置記錄。