中介 (Intermediaries) 示意圖
請求:
HTTP 請求訊息路由,由每個 Client 的 目標資源 (target resource)、
代理配置 或 進站連線的建立或重用來決定。
回應:
相關的回應路由,遵循相同的連接鏈回到 Client。
目錄
目標 URI (Target URI)
HTTP/1.1 通訊,由 使用者代理 (User Agent) 為達成某些目的而發起。
(HTTP/2 則不一定)
由 (2-1) 統一資源識別符 (URI) 已知,
目標資源 (target resource) 是由 URI 所辨識,
而 使用者代理 (User Agent) 送出的 URI,稱為 — — 目標 URI (target URI)。
由於 fragment 保留 是保留給 Client 端處理,
目標 URI (target URI),是 http(s) URI 去除 fragment 的部分,
http(s) URI =
"http(s):" "//" authority path-abempty [ "?" query ] [ "#" fragment ]
Target URI =
"http(s):" "//" authority path-abempty [ "?" query ]
訊息端點 — 發送者 (sender)、接收者 (recipient)
其中,術語 — — 訊息 (message) 發送/接收者,
與 Client、Server 無關!
請求時: 發送者為 Client ; 回應時: 發送者為 Server。
連線進站 (Connecting Inbound)
一旦確定了 目標 URI,
Client 需確定 — — 是否存在 快取/緩存 (Cache),
若存在快取,且能夠滿足請求,則通常優先導向至此。
就像媽媽先檢查冰箱,有沒有 雞蛋 (快取),
沒有的話,才去商店購買。
如果,快取不存在、或不滿足請求 (e.g., 過期),
則檢查 Client 是否有設置 代理伺服器 (Proxy)。
若 代理 (proxy) 有可用快取,則直接滿足 Client,
以 Varnish 代理快取 為例:
否則,代理 (proxy) 連線進站 至 目標資源的權威 (authority)
— — 源伺服器 (Origin Server),以發送 HTTP 請求:
Photo by Varnish.
請求方向 — 進站 (inbound)、出站 (outbound)
其中,進/出站,是指『請求』的方向!
而非 訊息 (Message),這是許多人的誤解處!
往 Origin Server 的方向 (>>),稱為 inbound,
往 User Agent 的方向 (<<),稱為 outbound。
方向是絕對的
請求目標 (Request Target)
一旦連線進站:
Client 將 從 目標 URI 拆分出一 請求目標 (request target),並發送訊息。
請求目標 (request-target),位於 HTTP 請求訊息 中的 起始行 (start-line),
提供 源伺服器 (Origin Server) 或 中介 (intermediary) 辨識資源,
對照上篇的請求訊息 (request message) 範例,
POST /?id=1 HTTP/1.1
Host: echo.paw.cloud
Content-Type: application/json; charset=utf-8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:53.0) Gecko/20100101 Firefox/53.0
Connection: close
Content-Length: 136
{
"status": "ok",
"extended": true,
"results": [
{"value": 0, "type": "int64"},
{"value": 1.0e+3, "type": "decimal"}
]
}
即此部分 :
/task?id=1
你可能覺得奇怪:
為什麼不好好的發送 目標 URI,
還要拆分出 請求目標 ?
沒錯,HTTP/1.1 的主架構師 — Dr. Roy Thomas Fielding 承認:
這是早期 HTTP 的假設錯誤『一 IP = 一網域』,而衍生的 設計債 。
補救方式是:
引進 Host 表頭欄位 — — 指名網域名稱,
並完善 請求目標 的相應機制。
請求目標 (request-target) 共有四種形式,
取決於使用的 請求方法 (Request Method) 與 代理 (proxy) 方式。
1. 原始形式 (origin-form)
原始形式 (origin-form),是最常見的形式:
origin-form = absolute-path [ "?" query ]
目標 URI (target URI):
Target URI =
"http(s):" "//" authority path-abempty [ "?" query ]
原始形式 (origin-form) 類似 path-abempty + [ "?" query ] ,
不同之處在於,原始形式 (origin-form) 使用的是 絕對路徑 (absolute-path):
不能為 空路徑,必須以 " / " 開始 !
例如訪問:
https://echo.paw.cloud/?id=1
原始形式 (origin-form)為:
/?id=1
如果把 query 拿掉呢?
https://echo.paw.cloud
原始形式 (origin-form)為:
/
記得要有 『 / 』!!
當 Client 直接 將訊息發送給 源伺服器 (origin server),
且不是使用 CONNECT 方法 或 伺服器整體的 OPTIONS 請求 (稍後會說明),
必須使用 原始形式 (origin-form) 做為 請求目標 (request-target)。
2. 絕對形式 (absolute-form)
絕對形式 (absolute-form),唯一 用於對 代理 (proxy) 時:
(注意! 與上方的 絕對路徑 (absolute-path) 不同)
absolute-form = absolute-URI
目標 URI (target URI):
Target URI =
"http(s):" "//" authority path-abempty [ "?" query ]
絕對形式 (absolute-form) 為:
"http(s):" "//" authority path-abempty [ "?" query ]
對喔,與 目標 URI 一樣 🙄,且 URI 中不包含 片段 (fragment)。
例如訪問:
https://example.com/?id=1#ffffffffff
絕對形式 (absolute-form) 的 請求目標 (request-target) 為:
https://example.com/?id=1
POST https://example.com/?id=1 HTTP/1.1
Host: example.com
Content-Type: application/json; charset=utf-8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:53.0) Gecko/20100101 Firefox/53.0
Connection: close
Content-Length: 136
{
"status": "ok",
"extended": true,
"results": [
{"value": 0, "type": "int64"},
{"value": 1.0e+3, "type": "decimal"}
]
}
當 Client 使用 代理 (proxy) 時,
且不是使用 CONNECT 請求 或 伺服器整體的 OPTIONS 請求 (稍後會說明),
必須使用 絕對形式 (absolute-form) 做為 請求目標 (request-target)。
那 代理 (proxy) 發送訊息至 源伺服器 (origin server) 時呢?
Ans: 透過 Host 表頭欄位,將 絕對形式 轉為 原始形式 (origin-form)。
檢測訊息時,若看到 絕對形式 (absolute-form) 請求目標,
則能合理懷疑,訊息以 代理 (proxy) 的方式傳遞。
權威形式 (authority-form),唯一 用於 CONNECT 請求方法 [也就是 隧道 (tunnel)]:
authority-form = authority
當 Client 欲藉由 CONNECT 方法,
與一或多個 代理 (proxy) 建立起 隧道 (tunnel),就必須使用 權威形式 (authority-form)。
目標 URI (target URI):
Target URI =
"http(s):" "//" authority path-abempty [ "?" query ]
權威形式 (authority-form) 則為:
authority
Yep,僅有 authority 的部分,例如:
(若非預設的 TCP 埠號 — 80 port,需記得補上!)
CONNECT www.example.com:87 HTTP/1.1
詳見 (5-2) 代理 (proxy)、隧道 (tunnel) 與 閘道 (gateway):
4. 星號形式 (asterisk-form)
星號形式 (asterisk-form),唯一 用於 非代理 (proxy),
且對 伺服器整體 (server-wide) 的 OPTIONS 請求 時:
(而非指名特定的單一資源)
asterisk-form = "*"
權威形式 (authority-form) 為:
*
YEP,別懷疑,
Client 必須發送 「*」,做為請求目標,例如:
OPTIONS * HTTP/1.1
假如 代理 (proxy) 收到了,
絕對形式 (absolute-form) 的 OPTIONS 請求方法,且路徑 (path) 與 查詢 (query) 皆為空,例如:
OPTIONS https://www.example.org:8001 HTTP/1.1
最後一個 (如果還有的話) 指向 源伺服器 (origin server) 的 代理 (proxy),
必須將請求轉發為 星號形式 (asterisk-form):
OPTIONS * HTTP/1.1
Host: www.example.org:8001