網頁大小 14KB 與 15KB 的速度差異

Hacker News 上看到「Why your website should be under 14kB in size」這篇,對應的討論在「A 14kb page can load much faster than a 15kb page (endtimes.dev)」,在講網頁大小 14KB/15KB 的速度差異比 15KB/16KB 大很多的問題:

What is surprising is that a 14kB page can load much faster than a 15kB page — maybe 612ms faster — while the difference between a 15kB and a 16kB page is trivial.

原因是 TCP slow start 造成的:

This is because of the TCP slow start algorithm.

而網頁這邊 TCP slow start 目前大多數的實做都是 10 packets 後發動:

Most web servers TCP slow start algorithm starts by sending 10 TCP packets.

然後再組合 1500 bytes/packet 以及 overhead,就差不多是 14KB 了:

The maximum size of a TCP packet is 1500 bytes.

This this maximum is not set by the TCP specification, it comes from the ethernet standard

Each TCP packet uses 40 bytes in its header — 16 bytes for IP and an additional 24 bytes for TCP

That leaves 1460 bytes per TCP packet. 10 x 1460 = 14600 bytes or roughly 14kB!

然後 HTTP/3 也可以看到類似的設計 (出自「QUIC Loss Detection and Congestion Control」:

Sending multiple packets into the network without any delay between them creates a packet burst that might cause short-term congestion and losses. Implementations MUST either use pacing or limit such bursts to the initial congestion window, which is recommended to be the minimum of 10 * max_datagram_size and max(2* max_datagram_size, 14720)), where max_datagram_size is the current maximum size of a datagram for the connection, not including UDP or IP overhead.

算是一個小知識... 但對於現在肥滋滋的網頁效果來說就沒辦法了,而且考慮到大一點的網站會在一個 TCP 連線裡面可能會傳很多 request,其實早就超過 TCP slow start 的門檻了。

CloudFront 支援 HTTP/3

雖然 HTTP/3 還沒有進到 Standard Track,但看到 CloudFront 宣佈支援 HTTP/3 了:「New – HTTP/3 Support for Amazon CloudFront」。

只要在 CloudFront 的 console 上勾選起來就可以了:

看了看 RFC 9114: HTTP/3 文件裡的描述,client 可以試著建立 UDP 版本的 QUIC 連線,但要有機制在失敗時回去用 TCPHTTP/2 或是 HTTP/1.1

A client MAY attempt access to a resource with an "https" URI by resolving the host identifier to an IP address, establishing a QUIC connection to that address on the indicated port (including validation of the server certificate as described above), and sending an HTTP/3 request message targeting the URI to the server over that secured connection. Unless some other mechanism is used to select HTTP/3, the token "h3" is used in the Application-Layer Protocol Negotiation (ALPN; see [RFC7301]) extension during the TLS handshake.

Connectivity problems (e.g., blocking UDP) can result in a failure to establish a QUIC connection; clients SHOULD attempt to use TCP-based versions of HTTP in this case.

另外一條路是在 TCP 連線時透過 HTTP header 告訴瀏覽器升級:

An HTTP origin can advertise the availability of an equivalent HTTP/3 endpoint via the Alt-Svc HTTP response header field or the HTTP/2 ALTSVC frame ([ALTSVC]) using the "h3" ALPN token.

像是這樣:

Alt-Svc: h3=":50781"

然後 client 就可以跑上 HTTP/3:

On receipt of an Alt-Svc record indicating HTTP/3 support, a client MAY attempt to establish a QUIC connection to the indicated host and port; if this connection is successful, the client can send HTTP requests using the mapping described in this document.

另外在 FAQ 裡面有提到啟用 HTTP/3 是不另外計費的,就照著本來的 request 費用算:

Q. Is there a separate charge for enabling HTTP/3?

No, there is no separate charge for enabling HTTP/3 on Amazon CloudFront distributions. HTTP/3 requests will be charged at the request pricing rates as per your pricing plan.

先開起來玩看看...

HTTP 標準的翻新

HTTP 的標準之前都是用新的 RFC 補充與修正舊的標準,所以整體讀起來會比較累,對於開始了解 HTTP 的人會需要交叉讀才能理解。

而這次 RFC 9110~9114 算是一次性的把文件全部重新整理出來,可以看到蠻多人 (以及團體) 都有丟出來對應的看法,這邊丟這兩篇:「A New Definition of HTTP」與「HTTP RFCs have evolved: A Cloudflare view of HTTP usage trends」。

而這五個 RFC,從名稱列出來就可以看出來命名簡單粗暴,把核心概念先拆出來講,然後再講不同 protocol 的部份:

Cloudflare 這邊提供了一些資料,可以看到三個 protocol 使用率都算高,而目前最高的是 HTTP/2:

另外比較特別的是 Safari 在 HTTP/3 的趨勢居然有倒縮的情況:

然後 bot 的部份幾乎大家都支援 HTTP/2 了,目前還沒看到太多 HTTP/3 的蹤跡,倒是 LinkedIn 的 bot 有個奇怪的 adoption 然後全部 rollback 的情況,而最近又開始少量導入了:

這次看起來淘汰了 (obsolete) 很多之前的文件,以後要引用得往這五份來引...

25Gbps 下 HTTPS 的效率

作者家裡拉了 25Gbps 的 Internet 後 (可以參考先前寫的「25Gbps 的家用 Internet」這篇),然後發現 Internet 上好像拉不動 25Gbps 的量,所以自己在家裡先測試了現在 HTTPS 的極限速度:「25 Gbit/s HTTP and HTTPS download speeds」。

Client 是 AMD 的 5600X,算是目前最新的世代;Server 則是 Intel 的 9900K,目前最新應該是 12 代;測試用 35GB 的檔案來測,然後使用 TCP BBR (這邊沒有特別講,目前 kernel 內建的還是 v1)。

在單條 HTTP 的情況下 curl + nginx 與 curl + caddy 都可以直接跑滿 (23.4Gbps),Gonet/http 會卡在 20Gbps 左右。

如果是多條 HTTP 的話都可以跑滿 23.4Gbps。

但到了 HTTPS 的情況下最快的是 Go + net/http,可以跑到 12Gbps;curl + nginx 剩下 8Gbps;接下來 curl + caddy 的部份只有 7.5Gbps,而 go + caddy 只有 7.2Gbps。

上到多條 HTTPS 的情況大家都可以跑滿 23.4Gbps,除了 go + caddy 只能跑到 21.6Gbps。

另外作者試著用 kTLS 把 TLS 的工作丟進 kernel,就不需要全部在 nginx 內處理,速度基本上沒有太大變化,主要是降低了 CPU loading:

In terms of download speeds, there is no difference with or without KTLS. But, enabling KTLS noticeably reduces CPU usage, from ≈10% to a steady 2%.

算是一個有趣的發現,如果目前的 HTTPS 想要在 25Gbps 上面單線直接跑滿,還需要再 tune 不少東西...

把 SQLite 的 VFS 掛上 WebTorrent 的 PoC Demo

Hacker News Daily 上看到「Static torrent website with peer-to-peer queries over BitTorrent on 2M records (boredcaveman.xyz)」這個討論,作者試著在網頁上跑 SQLite + VFS + WebTorrent

這好像是這陣子一連串的 combo 技累積出來的東西:

  • 首先當然是把 SQLite 丟到網頁上跑的「sql.js」,這個專案比較久了,2019 年有第一個 release;
  • 然後最近有人透過 HTTP Range (Byte serving) 實做 SQLite VFS 的「sql.js-httpvfs」,這樣就不需要一次下載整包 SQLite;

接下來就是文章作者把 HTTP Range 換掉,改用 BitTorrent 的 pieces 來處理,在網頁端的話就順勢拿 WebTorrent 來用,對於很熱門的網站來說還蠻有趣的設計,但也可以預期網頁的反應速度應該不會太快,偏 PoC...

自動偵測 DNS over HTTPS 或是 DNS over TLS 的 Discovery of Designated Resolvers (DDR)

看到 Cloudflare 宣佈支援 Discovery of Designated Resolvers (DDR):「Announcing experimental DDR in 1.1.1.1」。

看了一下這以後會是新的標準 (現在還在 draft),可以自動偵測現在用的 DNS resolver 是否支援 DNS over HTTPS 或是 DNS over TLS,如果有的話就 upgrade 到 DoH 或 DoT:

This document defines Discovery of Designated Resolvers (DDR), a mechanism for DNS clients to use DNS records to discover a resolver's encrypted DNS configuration.

目前的 spec 是去查 _dns.resolver.arpa,下面第一個是支援 DoH 的回應,第二個則是支援 DoT 的回應:

_dns.resolver.arpa  7200  IN SVCB 1 doh.example.net ( alpn=h2 dohpath=/dns-query{?dns} )
_dns.example.net  7200  IN SVCB 1 dot.example.net ( alpn=dot port=8530 )

實際上拿 1.1.1.1 測試,在 Ubuntu 20.04 下用 dig 可以看到一堆亂碼 XDDD

;; ANSWER SECTION:
_dns.resolver.arpa.     300     IN      TYPE64  \# 104 0001036F6E65036F6E65036F6E65036F6E6500000100060268320268 330003000201BB000400080101010101000001000600202606470047 00000000000000000011112606470047000000000000000000100100 0700112F646E732D71756572797B3F6E616D657D
_dns.resolver.arpa.     300     IN      TYPE64  \# 81 0002036F6E65036F6E65036F6E65036F6E65000001000403646F7400 03000203550004000801010101010000010006002026064700470000 00000000000000111126064700470000000000000000001001

改用 Docker 開了 Ubuntu 22.04 (jammy) 的 image 後,裡面的 dig 可以用 SVCB 查詢 (本來的 type64 也還是會動),另外也把內容解出來了:

;; ANSWER SECTION:
_dns.resolver.arpa.     300     IN      SVCB    1 one.one.one.one. alpn="h2,h3" port=443 ipv4hint=1.1.1.1,1.0.0.1 ipv6hint=2606:4700:4700::1111,2606:4700:4700::1001 key7="/dns-query{?name}"
_dns.resolver.arpa.     300     IN      SVCB    2 one.one.one.one. alpn="dot" port=853 ipv4hint=1.1.1.1,1.0.0.1 ipv6hint=2606:4700:4700::1111,2606:4700:4700::1001

這樣就不需要寫死在系統內,但要注意這樣還是會有 attacker 擋住讓你 upgrade 的問題...

AWS 增加 CloudFront 的 AWS-managed prefix list 讓管理者使用

看到 AWS 公告提供 CloudFront 的 origin subnet 資訊 (AWS-managed prefix list) 讓管理者可以用:「Amazon VPC now supports an AWS-managed prefix list for Amazon CloudFront」。

以往會自己去「AWS IP address ranges」這邊提供的 JSON 檔案定時撈出來再丟到 managed prefix list 裡面,這次的功能等於是 AWS 自己管理這個 prefix list 讓管理者使用。

馬上想的到的用途就是 HTTP/HTTPS port 了,只開放給 CloudFront 的伺服器存取:

Starting today, you can use the AWS managed prefix list for Amazon CloudFront to limit the inbound HTTP/HTTPS traffic to your origins from only the IP addresses that belong to CloudFront’s origin-facing servers. CloudFront keeps the managed prefix list up-to-date with the IP addresses of CloudFront’s origin-facing servers, so you no longer have to maintain a prefix list yourself.

要注意的是這不應該當作唯一的 ACL 手段,因為其他人也可以建立 CloudFront distribution 來穿透打進你的 origin server。

另外有個比較特別的地方,這個 prefix list 的權重很重,使用他會算 55 條 rule 的量,在 security group 內很容易撞到 60 條的限制,在 route table 裡面則是直接撞到 50 條的限制;不過這兩個限制都可以跟 AWS 申請調昇:

The Amazon CloudFront managed prefix list weight is unique in how it affects Amazon VPC quotas:

  • It counts as 55 rules in a security group. The default quota is 60 rules, leaving room for only 5 additional rules in a security group. You can request a quota increase for this quota.
  • It counts as 55 routes in a route table. The default quota is 50 routes, so you must request a quota increase before you can add the prefix list to a route table.

如果 HTTP 一條,HTTPS 也一條,那就會算 110 rules 了,有暴力的感覺...

Mitmproxy 7 支援 TLS over TCP 的分析了

Hacker News 首頁上看到 Mitmproxy 7 的消息:「Mitmproxy 7」。

比較重要的功能應該就是可以針對任意的 TLS 連線攔截分析了:

不過像是 STARTTLS 這類先在 plaintext 溝通,然後送出指令進入 TLS 的方式,目前就還沒支援:

Opportunistic TLS (STARTTLS) is not supported yet, but regular TCP-over-TLS just works!

另外是可以分析 WebSocket 內的傳輸資料:

應該是跑個 pip install -U mitmproxy 就可以升級了... (如果先前是用 pip 安裝的話)

HP 印表機的 Port 與 Prometheus...

Twitter 上看到這個,HP 印表機的 Port 9100 跟 Prometheus 撞到,再加上 mistype,於是就出事了:

找了一下 HP 的文件,「HP Jetdirect Print Servers - HP Jetdirect Port Numbers for TCP/IP (UDP) Connections」:

9100 TCP port is used for printing. Port numbers 9101 and 9102 are for parallel ports 2 and 3 on the three-port HP Jetdirect external print servers.

翻了一下「Service Name and Transport Protocol Port Number Registry」這邊,看起來 HP 在很久前就登記了 9100/tcp 與 9100/udp...

不過這沒有誰對誰錯的問題,只是很好笑:Printer 在收到不認識的指令時會直接當做 text 印出來,加上 Prometheus 的 HTTP request 打進去...

QUIC 成為標準,從 RFC 8999 到 RFC 9002

前幾天的新聞了,這兩天 FastlyCloudflare 也都發文章出來了,QUIC 成為標準:「QUIC is now RFC 9000」、「QUIC Version 1 is live on Cloudflare」。

主要是這兩家都發稿宣傳他們的平台都支援 QUIC 了,接下來可以等一些測試報告,看看在 web 這種已經有不少複雜的 workaround 機制下,TCP BBR 環境的 HTTP/2 跟 QUIC 環境會有多少差異... 記得 QUIC 也是 BBR-based 的演算法。

在 QUIC 下的 https 協定會走 443/udp,如果防火牆是預設阻擋所有連線,然後逐條開放的話,需要另外開這組設定。

另外就是等 nginx 支援了,在「NGINX QUIC Preview」這邊有些資料,然後「">nginx-quic: log」裡面可以看到東西,裡面不少 commit 只是跟 nginx 本家同步而已,不過還是可以看到一些跟 QUIC 有關的...