TIL:无需 curl,利用 Bash /dev/tcp 即可发起 HTTP 请求
Hacker News 摘要原标题:TIL: You can make HTTP requests without curl using Bash /dev/TCP
在极简的容器镜像中,通常没有预装 curl 或 wget。当需要测试容器间网络连通性,例如对某个服务的健康检查接口 GET /health 进行简单测试时,可以利用 Bash 内置的 /dev/tcp 功能来发起 HTTP 请求。
实现方法
Bash 能够自行开启 TCP 套接字,通过手动编写简单的 HTTP 请求来实现通信。整个过程不需要除了 Shell 以外的任何工具。
1. 建立连接:使用命令 exec 3<>/dev/tcp/service/8642。这里的 service 是目标主机名或 IP 地址,8642 是端口号。该命令会打开一个双向的 TCP 套接字,并将其分配给文件描述符 3。
2. 发送请求:通过 printf 将 HTTP 请求行和请求头写入该描述符。例如:printf 'GET /health HTTP/1.1\r\nHost: service\r\nConnection: close\r\n\r\n' >&3。
3. 获取响应:使用 cat <&3 命令读取响应内容。这将打印出完整的 HTTP 响应,包括状态行、响应头和消息主体。
如果需要添加额外的请求头(例如 Authorization: Bearer 身份验证令牌),只需在结尾的空行之前插入以 \r\n 结尾的字符串即可。
核心原理
/dev/tcp 并不是文件系统上真实存在的设备文件。如果你尝试执行 ls /dev/tcp,系统会报错。这实际上是 Bash 内部处理的一种重定向机制。
当 Bash 检测到这种特殊的路径格式时,它会根据 Bash 手册中的定义,尝试打开对应的 TCP 套接字。Bash 会自动完成 DNS 查询和 connect(2) 系统调用。通过 exec 3<>,用户可以获得一个可以像读写普通文件一样操作的文件描述符。
局限性与注意事项
虽然这个技巧在调试时非常有用,但它存在以下几个重要的限制:
• 非标准客户端:它不是一个完整的 HTTP 客户端。它不会自动处理 HTTP 重定向、分块传输响应、数据压缩、请求重试或 TLS 加密。
• 连接关闭头的重要性:在请求中包含 Connection: close 非常关键。按照 HTTP/1.1 的默认设置,服务器在响应后会保持连接开启。如果不显式要求关闭连接,cat <&3 命令将会永远等待不会结束。发送此请求头可以让服务器在发送完响应后关闭连接,从而让 cat 命令读取到文件结束符并正常退出。或者也可以使用 timeout 命令来限制执行时间。
• 不支持 HTTPS:由于 /dev/tcp 建立的是原始套接字,它只能处理明文 HTTP。如果需要访问 https 地址,通常需要 openssl s_client 这种工具,但在受限环境中往往也无法使用。
• Bash 特有功能:这不是 POSIX 标准。在 dash(Debian 系统默认的 /bin/sh)或 zsh 中无法使用。因此,必须明确在 bash 环境下运行。
• 编译选项:该功能依赖于 Bash 编译时的 --enable-net-redirections 选项。虽然大多数现代发行版都默认开启此项,但在一些极旧或特殊定制的系统中可能被禁用。
尽管在日常开发中 curl 仍然是更好的选择,但在无法安装任何新软件包的精简容器中,这个 Bash 技巧可以作为一种快速有效的连通性检查方案。
原文:https://mareksuppa.com/til/bash-dev-tcp-http-without-curl/