Apache內容協商


Apache HTTPD支援HTTP/1.1規範中描述的內容協商。它可以根據瀏覽器提供的媒體型別,語言,字元集和編碼偏好設定,選擇資源的最佳表示形式。它還實現了一些功能,可以更智慧地處理來自傳送不完整協商資訊的瀏覽器的請求。

內容協商由mod_negotiation模組提供,該模組預設編譯。

關於內容協商

資源可以以多種不同的表示形式提供。例如,它可能以不同語言或不同媒體型別或組合形式提供。選擇最合適選擇的一種方法是為使用者提供索引頁面,然後讓他們選擇。但是,伺服器通常可以自動選擇。這是可以做到的,因為瀏覽器可以在每個請求中傳送有關他們喜歡的表示形式的資訊。例如,如果可能的話,瀏覽器可以表明它希望看到法語資訊,否則會傳送英語資訊。瀏覽器通過請求中的標題指示其偏好設定。僅請求法語的表示,那麼瀏覽器將傳送法語內容資訊。

Accept-Language: fr

請注意,只有在可以選擇表示時才會應用此偏好設定,並且這些偏好設定因語言而異。

作為更複雜請求的範例,此瀏覽器已組態為接受法語和英語,但更喜歡法語,並接受各種媒體型別,更喜歡HTML而不是純文字或其他文字型別,並且優先選擇GIF或JPEG而不是其他媒體型別 ,但也允許任何其他媒體型別作為最後的手段:

Accept-Language: fr; q=1.0, en; q=0.5
Accept: text/html; q=1.0, text/*; q=0.8, image/gif; q=0.6, image/jpeg; q=0.6, image/*; q=0.5, */*; q=0.1

httpd支援「伺服器驅動的」內容協商,如HTTP/1.1規範中所定義。它完全支援AcceptAccept-LanguageAccept-CharsetAccept-Encoding請求檔頭。httpd還支援「透明」內容協商,這是RFC 2295RFC 2296中定義的實驗性協商協定。它不支援這些RFC中定義的「功能協商」。

資源是由URI(RFC 2396)標識的概念實體。像Apache HTTP Server這樣的HTTP伺服器提供對其名稱空間內資源表示的存取,每個表示形式為具有已定義媒體型別,字元集,編碼等的位元組序列。每個資源可以關聯在任何給定時間使用零個,一個或多個表示。如果有多個表示可用,則該資源被稱為可協商的,並且其每個表示被稱為變體。可轉讓資源的變體的變化方式稱為協商的維度。

httpd的協商

為了協商資源,需要向伺服器提供有關每個變體的資訊。這可以通過以下兩種方式之一完成:

  • 使用型別對映(即*.var檔案)明確命名包含變體的檔案,或
  • 使用「MultiViews」搜尋,伺服器執行隱式檔案名模式匹配,並從結果中進行選擇。

使用型別對映檔案
型別對映是與名為type-map的處理程式相關聯的文件(或者,對於與舊的httpd組態的向後相容性,MIME型別的應用程式/x-type-map)。請注意,要使用此功能,必須在組態中設定一個處理程式,將檔案字尾定義為type-map是最好的。

AddHandler type-map .var

在伺服器組態檔案中。

型別對映檔案應與其描述的資源具有相同的名稱,後跟擴充套件名.var。在下面顯示的範例中,資源名為foo,因此型別對映檔案名為foo.var

此檔案應包含每個可用變體的條目; 這些條目由連續的HTTP格式標題行組成。不同變體的條目由空行分隔。空白行在條目中是非法的。通常開始一個對映檔案,其中包含整個組合實體的條目(儘管這不是必需的,如果存在則將被忽略)。範例對映檔案如下所示。

此檔案中的URI相對於型別對映檔案的位置。通常,這些檔案將與型別對映檔案位於同一目錄中,但這不是必需的。可以為與對映檔案位於同一伺服器上的任何檔案提供絕對或相對URI。

URI: foo

URI: foo.en.html
Content-type: text/html
Content-language: en

URI: foo.fr.de.html
Content-type: text/html;charset=iso-8859-2
Content-language: fr, de

請注意,即使啟用了Multiviews,型別對映檔案也將優先於檔案名的擴充套件名。如果變體具有不同的源品質,則可以通過媒體型別的「qs」引數指示,如此圖片(可用作JPEG,GIF或ASCII藝術):

URI: foo

URI: foo.jpeg
Content-type: image/jpeg; qs=0.8

URI: foo.gif
Content-type: image/gif; qs=0.5

URI: foo.txt
Content-type: text/plain; qs=0.01

qs值可以在0.0001.000的範圍內變化。請注意,永遠不會選擇qs值為0.000的任何變體。沒有’qs’引數值的變數的因子為1.0qs引數表示此變體與其他可用變體相比的相對「qs」,與用戶端的能力無關。例如,如果JPEG檔案試圖表示照片,則該檔案通常具有比ASCII檔案更高的源品質。但是,如果表示的資源是原始ASCII藝術,那麼ASCII表示將具有比JPEG表示更高的源品質。因此,qs值特定於給定變體,具體取決於它所代表的資源的性質。

Multiviews
MultiViews是一個每個目錄選項,它可以在httpd.conf中的<Directory><Location><Files>部分中使用Options指令設定,或者在.htaccess檔案中設定(如果正確設定了AllowOverride)。請注意,選項全部不設定MultiViews; 必須通過名字指定它。

MultiViews的效果如下:如果伺服器收到/some/dir/foo的請求,如果/some/dir啟用了MultiViews,並且/some/dir/foo不存在,那麼伺服器會讀取尋找的目錄名為foo.*的檔案,有效地偽造了一個型別對映,該對映命名所有這些檔案,為它們分配相同的媒體型別和內容編碼,如果用戶端通過名稱請求其中一個檔案。然後,它會根據客戶的要求選擇最佳匹配。

如果伺服器嘗試索引目錄,MultiViews也可能適用於搜尋DirectoryIndex指令指定的檔案。如果組態檔案指定:

DirectoryIndex index

然後,如果兩者都存在,伺服器將在index.htmlindex.html3之間進行選擇。如果兩者都不存在,並且存在index.cgi伺服器將執行index.cgi

如果在讀取目錄時找到的其中一個檔案沒有mod_mime識別的擴充套件名來指定其CharsetContent-TypeLanguageEncoding,則結果取決於MultiViewsMatch指令的設定。此指令確定處理程式,過濾器和其他擴充套件型別是否可以參與MultiViews協商。

協商方法

在httpd從型別對映檔案或目錄中的檔案名獲得給定資源的變體列表之後,它會呼叫兩種方法之一來決定要返回的「最佳」變體(如果有)。為了使用httpd的內容協商功能,沒有必要知道協商實際發生的任何細節。

有兩種協商方法:

  • 伺服器驅動協商 - 在正常情況下使用與httpd演算法的伺服器驅動的協商。httpd演算法在下面更詳細地解釋。當使用該演算法時,httpd有時可以「擺弄」特定維度的品質因數以獲得更好的結果。httpd可以改變品質因素的方式在下面更詳細地解釋。

  • 透明內容協商 - 當瀏覽器通過RFC 2295中定義的機制專門請求時,使用透明內容協商。此協商方法使瀏覽器完全控制決定「最佳」變體,因此結果取決於瀏覽器使用的特定演算法。作為透明協商過程的一部分,瀏覽器可以要求httpd執行RFC 2296中定義的「遠端變數選擇演算法」。

httpd協商演算法
httpd可以使用以下演算法選擇「最佳」變體(如果有)返回瀏覽器。該演算法不是可組態的。它的運作方式如下:

第1步 - 首先,對於協商的每個維度,檢查相應的Accept *檔頭欄位並為每個變體分配品質。如果任何維度的Accept *檔頭暗示此變體不可接受,則將其刪除。如果沒有變體,請轉到第4步。
第2步 - 通過消除過程選擇「最佳」變體。按順序應用以下每個測試。在每次測試中未選擇的任何變體都被消除。每次測試後,如果只剩下一個變數,請將其選為最佳匹配,然後繼續執行第3步。如果仍有多個變數,請繼續進行下一個測試。

  • 將Accept檔頭中的品質因子與此變體媒體型別的源品質因子相乘,並選擇具有最高值的變體。
  • 選擇具有最高語言品質因子的變體。
  • 使用Accept-Language檔頭中的語言順序(如果存在)或LanguagePriority指令中的語言順序(如果存在),選擇具有最佳語言匹配的變體。
  • 選擇具有最高「級別」媒體引數的變體(用於提供text/html媒體型別的版本)。
  • 選擇具有最佳字元集媒體引數的變體,如Accept-Charset標題行所示。除非明確排除,否則使用Charset ISO-8859-1。假定具有text/*媒體型別但未與特定字元集明確關聯的變體在ISO-8859-1中。
  • 選擇那些具有非ISO-8859-1關聯字元集媒體引數的變體。如果沒有此類變體,請選擇所有變體。
  • 選擇具有最佳編碼的變體。如果存在具有使用者代理可接受的編碼的變體,請僅選擇這些變體。否則,如果存在編碼和非編碼變體的混合,請僅選擇未編碼的變體。如果編碼所有變體或未編碼所有變體,請選擇所有變體。
  • 選擇內容長度最小的變體。
  • 選擇剩餘的第一個變體。這將是型別對映檔案中列出的第一個,或者從目錄中讀取變體時,使用ASCII程式碼順序排序時檔案名首先出現的變數。
  • 該演算法現在選擇了一個「最佳」變體,因此將其作為響應返回。HTTP響應頭Vary設定為指示協商的維度(瀏覽器和快取可以在快取資源時使用此資訊)。
  • 到達此處意味著未選擇任何變體(因為瀏覽器不接受任何變體)。返回406狀態(表示「無法接受的表示」),其響應正文由列出可用變體的HTML文件組成。還要設定HTTP Vary檔頭以指示方差的維度。

擺弄品質值

httpd有時會改變上面對httpd協商演算法的嚴格解釋所期望的品質值。這是為了從不傳送完整或準確資訊的瀏覽器的演算法中獲得更好的結果。一些最流行的瀏覽器傳送Accept頭資訊,否則會導致在許多情況下選擇錯誤的變體。如果瀏覽器傳送完整且正確的資訊,則不會應用這些小提琴。

媒體型別和萬用字元
Accept:request檔頭指示媒體型別的偏好設定。它還可以包括「萬用字元」媒體型別,例如image/**/*,其中*匹配任何字串。所以請求包括:

Accept: image/*, */*

表示任何以image/開頭的型別都是可以接受的,就像任何其他型別一樣。除了可以處理的顯式型別之外,一些瀏覽器還會定期傳送萬用字元。例如:

Accept: text/html, text/plain, image/gif, image/jpeg, */*

這樣做的目的是表明明確列出哪些型別是首選的,但如果有不同的表示,那也沒關係。使用顯式品質值,瀏覽器真正想要的是:

Accept: text/html, text/plain, image/gif, image/jpeg, */*; q=0.01

顯式型別沒有品質因子,因此它們預設為1.0(最高)的偏好。萬用字元*/*的優先順序為0.01,因此只有在沒有變數與明確列出的型別匹配時才會返回其他型別。

如果Accept:檔頭根本不包含q因子,httpd將*/*」的q值設定為0.01以模擬所需的行為。它還將格式為type/*的萬用字元的q值設定為0.02(因此這些優先於匹配*/*的匹配。如果Accept:檔頭上的任何媒體型別包含aq因子,則這些特殊值不是應用,所以來自瀏覽器的請求傳送顯式資訊以按預期開始工作。

語言協商異常

在httpd 2.0中新增了一些異常,在協商演算法中新增了一些異常,以便在語言協商找不到匹配時允許優雅的回退。

當用戶端請求伺服器上的頁面,但伺服器找不到與瀏覽器傳送的Accept語言匹配的單個頁面時,伺服器將向用戶端返回「No Acceptable Variant」或「Multiple Choices」響應。為了避免這些錯誤訊息,可以將httpd組態為在這些情況下忽略Accept語言,並提供一個未明確匹配用戶端請求的文件。ForceLanguagePriority指令可用於覆蓋這些錯誤訊息中的一個或兩個,並以LanguagePriority指令的形式替換伺服器判斷。

當沒有找到其他匹配時,伺服器還將嘗試匹配語言子集。例如,如果用戶端請求英語為en-GB語言的文件,則HTTP/1.1標準通常不允許伺服器將其與標記為en的文件進行匹配。(請注意,在Accept-Language標題中包含en-GB肯定是一個組態錯誤,因為讀者不太可能理解英國英語,但一般不懂英語。不幸的是,很多當前用戶端具有與此類似的預設組態。)但是,如果沒有其他語言匹配且伺服器即將返回「No Acceptable Variants」錯誤或回退到LanguagePriority,則伺服器將忽略子集規範並匹配en-GB相應檔案。隱式地,httpd會將父語言新增到用戶端可接受的語言列表中,並且品質非常低。但請注意,如果用戶端請求en-GB; q=0.9,fr; q=0.8,並且伺服器具有指定為「en」和「fr」的文件,則將返回「fr」文件。這對於保持對HTTP/1.1規範的遵從性以及與正確組態的用戶端有效地工作是必要的。

為了支援高階技術(例如cookie或特殊URL路徑)來確定使用者的首選語言,因為httpd 2.0.47 mod_negotiation識別環境變數prefer-language。如果它存在且包含適當的語言標記,則mod_negotiation將嘗試選擇匹配的變體。如果沒有這樣的變體,則適用正常的協商過程。

範例 -

SetEnvIf Cookie "language=(.+)" prefer-language=$1
Header append Vary cookie

透明內容協商的擴充套件

httpd擴充套件了透明內容協商協定(RFC 2295),如下所示。在變體列表中使用新的{encoding...}元素來標記僅具有特定內容編碼的變體。RVSA/1.0演算法(RFC 2296)的實現被擴充套件為識別列表中的編碼變體,並且根據Accept-Encoding請求頭,只要它們的編碼是可接受的,就將它們用作候選變體。在選擇最佳變體之前,RVSA/1.0實現不會將計算的品質因子舍入到5位小數。

關於超連結和命名約定的注釋

如果您正在使用語言協商,則可以在不同的命名約定之間進行選擇,因為檔案可以具有多個擴充套件名,並且擴充套件的順序通常無關緊要。

當具有該檔案的不同語言變體時,典型檔案具有MIME型別擴充套件(例如,html),可能是編碼擴充套件(例如,gz),當然還有語言擴充套件(例如,en)。

例子:

foo.en.html
foo.html.en
foo.en.html.gz

這裡有一些檔案名的例子以及有效和無效的超連結:

檔案名 有效的超連結 無效的超連結
foo.html.en foo,foo.html
foo.en.html foo foo.html
foo.html.en.gz foo,foo.html foo.gz,foo.html.gz
foo.en.html.gz foo foo.html,foo.html.gz,foo.gz
foo.gz.html.en foo,foo.gz,foo.gz.html foo.html
foo.html.gz.en foo,foo.html,foo.html.gz foo.gz

檢視上表,應該注意到在超連結(例如,foo)中始終可以使用沒有任何擴充套件名的名稱。優點是可以隱藏文件rsp的實際型別。檔案並可以在以後更改它,例如,從html更改為shtml或cgi而不更改任何超連結參照。

如果您想繼續在超連結中使用MIME型別(例如foo.html),則語言擴充套件(包括編碼擴充套件,如果有的話)必須位於MIME型別擴充套件的右側(例如,foo.html.en)。

關於快取的注意事項

當快取儲存表示時,它將其與請求URL相關聯。下次請求該URL時,快取可以使用儲存的表示。但是,如果資源在伺服器上可協商,則可能導致僅快取第一個請求的變體,並且後續快取命中可能返回錯誤的響應。為了防止這種情況,httpd通常將內容協商後返回的所有響應標記為HTTP/1.0用戶端不可快取。httpd還支援HTTP/1.1協定功能,以允許快取協商的響應。

對於來自HTTP/1.0相容用戶端(瀏覽器或快取)的請求,可以使用指令CacheNegotiatedDocs來快取需要協商的響應。該指令可以在伺服器組態或虛擬主機中給出,並且不帶引數。它對來自HTTP/1.1用戶端的請求沒有影響。

對於HTTP/1.1用戶端,httpd傳送Vary HTTP響應頭以指示響應的協商維度。快取可以使用此資訊來確定是否可以從本地副本提供後續請求。要鼓勵快取使用本地副本而不管協商維度,請設定force-no-vary環境變數。