HTTP

HTTP 訊息路由 (Message Routing)

中介 (Intermediaries) 示意圖
中介 (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) 發送/接收者,
ClientServer 無關!
請求時: 發送者為 Client ; 回應時: 發送者為 Server。
sender-and-recipient
 


 

連線進站 (Connecting Inbound)

一旦確定了 目標 URI
Client 需確定 — — 是否存在 快取/緩存 (Cache)
若存在快取,且能夠滿足請求,則通常優先導向至此。
 
就像媽媽先檢查冰箱,有沒有 雞蛋 (快取)
沒有的話,才去商店購買。

 
 
如果,快取不存在、或不滿足請求 (e.g., 過期),
則檢查 Client 是否有設置 代理伺服器 (Proxy)
 
代理 (proxy) 有可用快取,則直接滿足 Client,
以 Varnish 代理快取 為例:

 
否則,代理 (proxy) 連線進站 至 目標資源的權威 (authority)
— — 源伺服器 (Origin Server),以發送 HTTP 請求:

Photo by Varnish.
 
 

請求方向 — 進站 (inbound)、出站 (outbound)

其中,進/出站,是指『請求』的方向!
而非 訊息 (Message),這是許多人的誤解處!
simple-connection
 
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) 的方式傳遞。
 
 

3. 權威形式 (authority-form)

權威形式 (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):
tunnel-proxy
 
 

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

 
 

作者: 鄭中勝
喜愛音樂,但不知為何總在打程式 ? 期許能重新審視、整理自身所學,幫助有需要的人。

發表迴響