?
本文檔使用 php中文網(wǎng)手冊 發(fā)布
在HTTP協(xié)議提供了一個特殊機制,使已建立的連接升級到一個新的,不兼容,協(xié)議。本指南介紹了它的工作原理,并提供了其使用場景的示例。
此機制始終由客戶端啟動(有一個例外:服務器可能需要升級到TLS),并且服務器可能接受或拒絕切換到新協(xié)議。這使得使用常用協(xié)議(如HTTP / 1.1)啟動連接成為可能,然后請求連接切換到HTTP / 2或甚至到WebSocket。
協(xié)議升級總是由客戶請求; 沒有為服務器提供請求協(xié)議更改的機制。當客戶機希望升級到新的協(xié)議,它通過發(fā)送任何類型的正常請求發(fā)送到服務器(這樣做GET
,POST
等)。但是,該請求需要專門配置以包含升級請求。
特別是,請求需要這兩個額外的頭文件:
Connection: Upgrade
該Connection
頭被設(shè)置為"Upgrade"
以表示的升級要求。Upgrade:
protocols
所述Upgrade
標頭指定的一個或多個以逗號分隔的協(xié)議名稱,首選項的順序。
根據(jù)請求的協(xié)議,可能需要其他頭文件; 例如,WebSocket升級允許額外的頭部配置關(guān)于WebSocket連接的細節(jié),并在打開連接時提供一定程度的安全性。有關(guān)更多詳細信息,請參閱升級到WebSocket連接。
服務器可拒絕升級-在這種情況下,它僅僅是忽略了"Upgrade"
頭并發(fā)送回一個普通的響應("200 OK"
如果它可以提供所請求的資源時,30x
如果它要執(zhí)行重定向,狀態(tài)碼40x
或50x
一個如果不能提供請求的資源) - 或接受升級。在這種情況下,它會發(fā)送一個"101 Switching Protocols"
帶有升級標頭的指定所選協(xié)議的標頭。
在發(fā)送101
狀態(tài)碼之后,如果新協(xié)議要求它發(fā)生新的協(xié)議的最終握手,則服務器"Upgrade"
按照新的協(xié)議規(guī)則發(fā)送原始請求(包括標頭的請求)所請求的答案。
的101
狀態(tài)代碼被發(fā)送作為對包括一個請求的響應"Upgrade"
報頭以信號通知請求的接收方愿意升級到所期望的協(xié)議之一。如果"101 Switching Protocols"
狀態(tài)碼被返回,則標題還必須包含描述所選協(xié)議的標題Connection
和Upgrade
標題。請參閱此機制的常見用法中的示例,以了解有關(guān)這種機制的更多信息。
雖然您可以使用協(xié)議升級機制將HTTP / 1.1連接升級到HTTP / 2,但您不能以其他方式。實際上,HTTP / 2不再支持101狀態(tài)碼,因為HTTP / 2沒有升級機制。
這里我們看一下Upgrade
頭部最常見的用例。
由于其廣泛的支持,使用HTTP / 1.1啟動連接的標準過程是,然后請求升級到HTTP / 2。這樣,即使服務器不支持HTTP / 2,仍然可以正常工作。但是,只能升級到不安全(明文)HTTP / 2連接。這是通過使用目標協(xié)議名稱來完成的h2c
,該名稱代表“HTTP / 2 Cleartext”。這也需要指定HTTP2-Settings
標題字段。
GET / HTTP/1.1Host: destination.server.ext Connection: Upgrade, HTTP2-Settings Upgrade: h2c HTTP2-Settings: base64EncodedSettings
這里base64EncodedSettings
是一個HTTP / 2 "SETTINGS"
幀的有效載荷,它已經(jīng)被base64url編碼,所有的尾隨"="
(等于)字符被刪除,以便將其安全地包含在這個文本標題格式中。
所述base64url格式是不一樣的標準Base64編碼。這與標準的Base64差不多,但不完全相同。唯一的區(qū)別:為了確保所得到的字符串是在URL和文件名使用安全的字母表中的第62和第63個字符從改變"+"
和"/"
到"-"
(負)和"_"
(下劃線),分別。
如果服務器由于某種原因無法切換到HTTP / 2,那么在正常處理請求后,它將回復標準的HTTP / 1回復。因此,如果請求是獲取事實上存在的網(wǎng)頁,那么您將"HTTP/1.1 200 OK"
在網(wǎng)頁的其余部分之后獲得標準響應。如果服務器能夠切換到HTTP / 2,HTTP/1.1 101 Switching Protocols"
則會發(fā)送一個“ 響應” ,如下所示:
HTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: h2c[standard HTTP/2 server connection preface, etc.]
在HTTP / 1.1頭部和指示頭部結(jié)尾的空白行之后,服務器將立即包含服務器連接前言,從一個SETTINGS
幀開始。
到目前為止,升級HTTP連接最常見的用例是使用WebSocket,它通常通過升級HTTP或HTTPS連接來實現(xiàn)。請記住,如果您使用WebSocket API或任何執(zhí)行WebSocket的庫打開新連接,則大部分或所有這些都是為您完成的。例如,打開WebSocket連接非常簡單:
webSocket = new WebSocket("ws://destination.server.ext", "optionalProtocol");
該WebSocket()
構(gòu)造函數(shù)創(chuàng)建一個初始的HTTP / 1.1連接,然后處理握手和升級過程中的所有工作。
您也可以使用"wss://"
URL方案打開安全的WebSocket連接。
如果您需要從頭開始創(chuàng)建WebSocket連接,則必須自己處理握手過程。在創(chuàng)建初始HTTP / 1.1會話之后,您需要通過添加標準請求Upgrade
和Connection
頭部來請求升級,如下所示:
Connection: Upgrade Upgrade: websocket
WebSocket升級過程涉及以下頭文件。除了頭文件Upgrade
和Connection
頭文件,其余的文件通常都是可選的,或者在瀏覽器和服務器互相通話時為您處理。
Sec-WebSocket-Extensions
指定要求服務器使用的一個或多個協(xié)議級WebSocket擴展。Sec-WebSocket-Extension
允許在請求中使用多個頭部; 結(jié)果與您在一個這樣的標題中包含所有列出的擴展名相同。
Sec-WebSocket-Extensions: extensions
extensions
用逗號分隔的請求(或同意支持)的擴展名列表。這些應該從IANA WebSocket擴展名注冊表中選擇。帶參數(shù)的擴展使用分號描述。
例如:
Sec-WebSocket-Extensions: superspeed, colormode; depth=16
Sec-WebSocket-Key
向服務器提供信息,以確認客戶端有權(quán)請求升級到WebSocket。當不安全(HTTP)客戶希望升級時,可以使用此頭文件,以提供一定程度的防范濫用保護。使用WebSocket規(guī)范中定義的算法計算密鑰的值,因此不提供安全性。相反,它有助于防止非WebSocket客戶端無意中或通過濫用請求WebSocket連接。從本質(zhì)上說,這個關(guān)鍵只是確認“是的,我真的打算打開一個WebSocket連接?!?/p>
這個標題是由選擇使用它的客戶端自動添加的; 它不能使用該XMLHttpRequest.setRequestHeader()
方法添加。
Sec-WebSocket-Key: key
key
此請求升級的關(guān)鍵。如果客戶希望這樣做,則客戶端會添加該服務器,并且服務器會在響應中包含自己的密鑰,客戶端會在向您提供升級響應之前驗證該密鑰。
服務器的響應Sec-WebSocket-Accept
頭將有一個基于指定的值計算出來的值key
。
Sec-WebSocket-Protocol
該Sec-WebSocket-Protocol
頭指定要使用,按優(yōu)先順序的一個或多個的WebSocket協(xié)議。服務器支持的第一個服務器將被選中,并由服務器在Sec-WebSocket-Protocol
響應中包含的標題中返回。您也可以在標題中多次使用它,結(jié)果與在單個頭中使用逗號分隔的子協(xié)議標識符列表相同。
Sec-WebSocket-Protocol: subprotocols
subprotocols
按照優(yōu)先順序,按逗號分隔的子協(xié)議名稱列表。子協(xié)議可以從IANA WebSocket子協(xié)議名稱注冊中選擇,也可以是客戶和服務器共同理解的定制名稱。
Sec-WebSocket-Version
指定客戶端希望使用的WebSocket協(xié)議版本,以便服務器可以確認該版本是否受支持。
Sec-WebSocket-Version: version
version
客戶端希望在與服務器通信時使用的WebSocket協(xié)議版本。此號碼應為IANA WebSocket版本號注冊表中列出的最新版本。WebSocket協(xié)議的最新最終版本是版本13。
如果服務器無法使用指定版本的WebSocket協(xié)議進行通信,它將響應一個錯誤(如需要升級426),在其標題中包含一個包含Sec-WebSocket-Version
受支持協(xié)議版本的逗號分隔列表的標題。如果服務器確實支持所請求的協(xié)議版本,Sec-WebSocket-Version
則響應中不會包含標題。
Sec-WebSocket-Version: supportedVersions
supportedVersions
服務器支持的WebSocket協(xié)議版本的逗號分隔列表。
服務器的響應可能包括這些。
Sec-WebSocket-Accept
在服務器愿意啟動WebSocket連接時,在打開握手過程期間包含在服務器的響應消息中。它在repsonse頭文件中只會出現(xiàn)一次。
Sec-WebSocket-Accept: hash
hash
如果Sec-WebSocket-Key
提供了頭文件,則通過獲取該密鑰的值來計算此頭文件的值,并將字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”連接到該字符串,并取該連接字符串的SHA-1散列值,結(jié)果在一個20字節(jié)的值。然后該值被base64編碼以獲得該屬性的值。
您也可以將HTTP / 1.1連接升級到TLS / 1.0。這樣做的主要優(yōu)點是可以避免在服務器上使用從“http://”到“https://”的URL重定向,并且可以在虛擬主機上輕松使用TLS。但是,這可能會導致代理服務器出現(xiàn)問題。
升級HTTP連接以使用TLS將Upgrade
標記與標記一起使用"TLS/1.0"
。如果交換機成功完成,原始請求(包括其中Upgrade
)將按照正常完成,但在TLS連接上完成。
對TLS的請求可以選擇性地或強制性地進行。
要升級到TLS(也就是說,如果升級到TLS失敗,允許連接繼續(xù)以明文方式),只需按預期方式使用Upgrade
和Connection
標題。例如,給出原始請求:
GET http://destination.server.ext/secretpage.html HTTP/1.1Host: destination.server.ext Upgrade: TLS/1.0Connection: Upgrade
如果服務器不支持TLS升級,或者無法在當時升級到TLS,則它會使用標準的HTTP / 1.1響應進行響應,例如:
HTTP/1.1 200 OK Date: Thu, 17 Aug 2017 21:07:44 GMT Server: Apache Last-Modified: Thu, 17 Aug 2017 08:30:15 GMT Content-Type: text/html; charset=utf-8Content-Length: 31374<html> ...</html>
如果服務器確實支持TLS升級并希望允許升級,它將使用"101 Switching Protocols"
響應代碼進行響應,如下所示:
HTTP/1.1 101 Switching Protocols Upgrade: TLS/1.0, HTTP/1.1
一旦TLS握手完成,原始請求將被正常響應。
要請求強制升級到TLS(即升級失敗并在升級失敗時連接失?。?,您的第一個請求必須是一個OPTIONS
請求,如下所示:
OPTIONS * HTTP/1.1Host: destination.server.ext Upgrade: TLS/1.0Connection: Upgrade
如果升級到TLS成功,服務器將"101 Switching Protocols"
按照上一節(jié)所述進行響應。如果升級失敗,HTTP / 1.1連接將失敗。
這與客戶端啟動的升級大致相同,通過將Upgrade
頭添加到任何消息來請求可選的升級。然而,強制升級的工作方式稍有不同,因為它會通過回復收到的帶有426
狀態(tài)碼的消息來請求升級,如下所示:
HTTP/1.1 426 Upgrade Required Upgrade: TLS/1.1, HTTP/1.1Connection: Upgrade<html>... Human-readable HTML page describing why the upgrade is required and what to do if this text is seen ...</html>
如果接收到"426 Upgrade Required"
響應的客戶端愿意且能夠升級到TLS,則應該啟動上面在客戶端啟動的升級到TLS的過程中所述的相同過程。
WebSocket API
HTTP
規(guī)格和RFC:
RFC 2616
RFC 6455
RFC 2817
RFC 7540
在MDN上編輯此頁面