Curl 是一个常见的命令行工具,能力非常强大,在大家的工作中很常用,但是完整读过 curl 的 manual 的应该不多。其实 curl manual 是一个学习 http 协议的很好的材料,这篇文章总结从 curl manual 可以学习到的一些有趣知识点。
--alt-svc
: 让 curl 支持解析alt-svc
, alt-svc 是什么呢,可以参考[这篇文章](HTTP Alternative Services 介绍 | JerryQu 的小站) ([(这篇文章有点老了,对于浏览器的支持那块不太对, 可以参考这里,实际大部分浏览器都已经支持了](Alt-Svc - HTTP | MDN)),简单来说alt-svc
有点类似实现于浏览器层的针对 dns 的 302,比如访问www.a.com
, 服务器发现当前压力太大了,可以让浏览器把对www.a.com
的所有请求都发送到www.b.com
, 这个信息就可以通过alt-svc
来返回。这样做的好处是比 改 cdn 快,又不影响浏览器上层的行为。--aws-sigv4
:aws
的 v4 鉴权。没想到吧,某一天云厂商的 api 会影响如此深远,甚至一些几十年历史的工具都会对他特殊支持。--digest
: 支持 HTTP摘要认证 这种认证方式是对 basic 认证的一种增强。我们知道 http basic 认证是很不安全的,用户的账号密码会明文传输(会 base64,其实和明文没啥区别)摘要认证的作用对 basic 认证做了增强,其中关键的点在于:1. 传输的是一个和账号密码有关的 md5 值,而不是账号密码,这样不容易泄漏;2. 这个 md5 值是根据访问的访问、URI、时间 (nonce、cnonce) 是相关的,这样可以避免重放攻击--dns-interface
--dns-ipv4-addr
--dns-ipv6-addr
: 让 curl 指定 source 端口、Ip 来发送 dns 请求,这样做的目的是什么呢。dns 支持根据不同的 source ip 返回不同的结果,一个简单的例子是:我有一个域名绑定了两个地址,一个国内地址,一个海外地址,我希望海外的 dns 请求返回海外地址,国内的 dns 请求返回国内地址。所以我们有时候在 curl 或者使用 dig 之类的工具发送 dns 请求的时候会希望设置 source ip 来影响 dns 服务器返回的结果。--doh-xxx
: doh 即 DNS over HTTPS 的相关配置,这种 dns 方式会更安全,使用加密的HTTPS协议进行DNS解析请求,避免原始DNS协议中用户的DNS解析请求被窃听或者修改的问题(例如中间人攻击)来达到保护用户隐私的目的,现在已经有很多 dns 服务商支持了这种功能。--egd-file
: 要了解 egd-file 是啥玩意需要先学习一下 linux 的 /dev/random /dev/urandom 设备的原理,可以参考 /dev/random 和 /dev/urandom 的原理,简单的说 /dev/random 的原理是收集 linux 中的一些随机事件(熵)作为随机源,而这些设备产生的随机数是 linux 系统中很多应用(在 curl 中主要是用来 seed SSL connection)的重要依赖。而 egd-file 则是 /dev/random 的一种替代,用于比如在一些没有 /dev/random 设备的场景。--etag-compare
--etag-save
: ETag - HTTP | MDNETa
HTTP响应头是资源的特定版本的标识符, 这个标识符让缓存更高效。简单的说就是 某个 http 资源 etag 是 'a', 那么在下一次访问的时候可以设置If-None-Match:"a"
头,那么如果 这个 http 资源没有变化,etag 还是 'a', 服务器会返回 304 未修改状态。对应到 curl 的这两个参数上面,--etag-save
会把 etag 保存到一个文件里面,而--etag-compare
会从文件里面读取 etag 然后设置到If-None-Match
头。--expect100-timeout
: 这个参数和 header 100 Continue 有关,100 Continue header 的作用是允许 client 像 server 发送大的数据包的时候先和 server 做一个协商, 当 server 允许 client 发送的时候 client 再继续发送内容,这样可以避免 client 端浪费资源开销。-G
,--get
: --data 等参数表述的内容默认都是通过 POST 发送,设置这个参数则使用 GET 发送。和这个参数相关的是一个有趣的面试题:"GET 和 POST 有什么区别"?实际上 restful 协议只是一种非常弱的约定
, 在实际的 HTTP 语义/规范上来讲,GET 和 POST 完全没有区别。--happy-eyeballs-timeout-ms
: 你有没有想过,当一个域名有 ipv4 和 ipv6 双栈地址当时候,curl 或者浏览器是怎么访问的?happy-eyeballs 算法是一个解决这个问题的简单方法:即优先用 ipv6,等几百 ms 后发现不行,再试 ipv4 地址,这个参数就是用来控制等待时间的,具体可以参考这篇文章:IPV6 & Happy Eyeballs - 云 社区 - 腾讯云--haproxy-protocol
: 让 curl 在 connect 的时候传递HAProxy PROXY protocol v1 header
, 这个协议的介绍可以参考这里:proxy protocol介绍及nginx配置 - 简书--hsts <file name>
: hsts 的解释可以参考这里:HTTP严格传输安全 - 维基百科 这种协议的作用是强制客户端(如浏览器)使用HTTPS与服务器建立连接(即使输入的是 http 地址),不过这种协议需要依赖第一个请求的 hsts 返回,curl 的配置则是设置一个HSTS cache file-k, --insecure
:这是一个比较常用的参数,用于让 curl 对 server 的 tls 证书不做校验-4, --ipv4; -6, --ipv6
: 让 curl 使用 ipv4 或者 ipv6 地址访问,一个小例子: ➜ curl -v -4 www.qq.com Trying 221.198.70.47:80... ➜ curl -v -6 www.qq.com Trying 2402:4e00:1900:1400:0:9227:71e8:2ccc:80...--keepalive-time
: 这是 curl 设置的是 tcp 的 keepalive 选项 即TCP_KEEPIDLE
; 关于 tcp 的 keepalive 和 http 的 keepalive 区别也是一个常见的面试题,具体可以参考前面的链接,简单来说就是 tcp 的 keepalive 是内核实现用于 tcp 连接保活(没有主动关闭的前提下定期探测看是否存活),默认是小时级别(7200s),而 http1.0 的 keepalive 是 tcp 连接复用,降低创建销毁成本,是应用层实现,一般是秒级别 (比如 10s );另外一个相关的问题:为什么 curl 提供的选项只有 tcp keepalive 没有 http keepalive 选项呢?实际上 curl 默认就是会 reuse connection的,至于Keep-Alive - HTTP Header)只是 client 对于 server 的一个 hint,当然也可以设置,具体 server 是否要 keep alive 和 keep 多久取决于 server 端的处理。--libcurl <file>
: 和一些高级的 http client 类似,curl 实际上也支持把一个 http 请求直接转化为代码,使用这个选项就会转化为 使用 libcurl 的 c 代码。-L, --location
: 如果返回是 30x curl 会自动访问 redirect 的地址,另一个相关的参数是--max-redirs
规定了最多能 redirect 的次数。关于 20x 和 30x 的区别有一个非常好的图(如下)--negotiate
: 使用 Spnego 认证,一种由微软提出的使用GSS-API接口的认证模式--no-npn
--no-alpn
: 不使用 npn/alpn 即 Next Protocol Negotiation,npn 现在的版本叫 ALPN 是一种在 tls 握手时顺便进行协议协商的 tls 扩展,具体可以参考这篇文章: 谈谈 HTTP/2 的协议协商机制 | JerryQu 的小站--no-sessionid
: sessionid 是一种 tls TLS/SSL 会话复用 机制,默认 curl 会使用这种机制,有助于复用 session 提高 https 传输(握手)性能;设置这个选项可以关闭默认的行为。--ntlm-wb
--ntlm
: ntlm 是 Microsoft 设计的一种鉴权协议。--preproxy
-p, --proxytunnel
:proxy 相关的选项很多,而且我也比较感兴趣,后面单开一篇重点分析。--tcp-fastopen
: 我们知道 tcp 一个被诟病的问题是建立连接比较耗时,需要三次握手,我们已经做了很多方法来减少建立 tcp 连接的次数,比如 内核层 keepalive,http 的 keepalive;那么有没有办法直接让 tcp 建立连接本身变得更快呢,一个办法就是 TCP Fastopen, 他是怎么运作的可以参考 wiki 或者这两篇文章:TCP 的那些事 | TCP Fast Open_CoderAndClimber的博客-CSDN博客_fastopen 和 Linux的TCP实现之:三次握手 | Zorro’s Linux Book; 这里我也简单概括一下,就是在客户端非第一次建立连接的时候可以带一个 cookieid 标识连接身份,这样服务端在握手完成之前就可以发送数据,这样能减少一次 RTT(根据测试数据,TFO可以减少15%的HTTP传输延迟,全页面的下载时间平均节省10%,最高可达40%)--tcp-nodelay
: 操作TCP_NODELAY
选项,这个选项默认就是打开的,理解这个选项先要了解一下 纳格Nagle算法 ,简单的说,这个算法早期是为了节约网络带宽,小包合并发送,和实际上在现代网络环境中,Nagel 算法一般是被关闭的,尤其是在 HTTP2 中,这也是为什么 curl 里 默认 --tcp-nodelay 也是 true-w, --write-out <format>
: format 是一个可以使用 curl 生成的一些变量的模板,比如这个例子,我只想看 curl 命令返回的 http_code: ➜ curl -s -o /dev/null -w "%{http_code}" http://www.qq.com/ 302%`--version
: 输出版本,这个选项的重点在于还输出了 curl 支持的Features
, 我们逐个来看一下 Feature解释alt-svc见上面的 alt-svc 参数AsynchDNS异步 dns 解析,为什么 dns 都能异步?实际上这还是一个常见需求,比如 curl 的域名比较多,或者一个爬虫场景,dns 解析可能是瓶颈,这时候异步 dns 的帮助是很大的brotli支持 Brotli压缩算法 类似的 feature 还有 libz/zstdgsasl使用 libgsasl 支持 sasl 加密算法GSS-API支持 GSS-APIHSTS参考上面的--hsts
选项HTTP2/HTTP3支持 HTTP2/HTTP3HTTPS-proxy支持 HTTPS-proxyIDN支持 IDN, 什么是 IDN以及 IDN 带来的风险,可以参考这篇文章IPv6支持 IPv6Kerberos支持 Kerberos V5 鉴权,类似的 feature 还有 NTLM/SPNEGO/TLS-SRP/SSPIMultiSSLmultiple TLS backend,这里的 backend 指的是如: bearssl, gnutls, gskit, mbedtls, mesalink, nss, openssl, rustls, schannel,secure-transport, wolfssl 的具体实现PSL支持 Public Suffix List, 我们常见的 '.com', '.cn' 都是属于 psl,实际上 psl 的列表已经非常大,它的使用场景有例如 Firefox 用它在地址栏高亮 URL 的关键部分。PSL 更重要的用途是用在浏览器同源策略上:例如通过 document.cookie 设置 cookie,或者通过 document.domain 设置当前 domain,都不允许将 domain 设置为 PSL 中的记录,curl 的使用场景也是这个 cookie 相关的设置。