手写 HTTP 请求
本实验在 VM 中运行
使用 python3 -m http.server 启动一个小型 Web 服务器,并且不使用浏览器,
而是通过 nc 亲手键入请求。HTTP 是文本协议,所以可以这样做。
目标
手工构造请求行、请求头和空行,亲自接收 200、301、404、501 响应,
并与 curl -v 显示的内容进行对照。
为什么重要
HTTP 是 TCP 之上的文本:一行请求、几行请求头和一个空行——仅此而已。
亲手构造过这一结构的人,在代理替换请求头、Host 错误导致出现意外站点,
或重定向循环时,会知道应该检查什么。能读懂 curl -v 中的 > 和 <,
就掌握了 API 调试的一半。
步骤
- 创建
/root/http/www/index.html和/root/http/www/docs/guide.txt,让/root/http/www在127.0.0.1:8080由python3 -m http.server提供服务(日志写入/root/http/server.log)。 - 使用
nc手工编写并发送GET / HTTP/1.1请求,将完整响应保存到/root/http/get.txt。 - 请求不存在的路径
/missing,将 404 响应保存到/root/http/notfound.txt。 - 将
HEAD /请求的响应保存到/root/http/head.txt。响应应包含请求头,但没有正文。 - 请求末尾不带斜杠的
/docs,将包含 301 和Location请求头的响应保存到/root/http/redirect.txt。 - 使用
curl -v获取/docs/guide.txt,将同时包含请求头和响应头的输出保存到/root/http/curl.txt。 - 使用
curl -o /dev/null -w '%{http_code}'获取/、/docs、/nope的状态码,分三行保存到/root/http/codes.txt,每行采用경로=코드格式。 - 在
/root/http/report.md中写入status_ok=、status_redirect=、status_missing=三行,并说明请求的三个部分(请求行、请求头、空行)分别是什么。
参考
- 服务器:
setsid python3 -m http.server 8080 --bind 127.0.0.1 --directory /root/http/www > /root/http/server.log 2>&1 < /dev/null &。 - 编写请求:
printf 'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n' | nc -q 1 127.0.0.1 8080。每行以\r\n结束,请求头后的空行表示请求结束。 curl -v用>标记发送的内容,用<标记接收的内容。- 常见错误:遗漏最后的空行。服务器会一直等待请求结束。
一个小型站点
创建 /root/http/www/index.html 和 /root/http/www/docs/guide.txt,让 /root/http/www 在 127.0.0.1:8080 由 python3 -m http.server 提供服务(日志写入 /root/http/server.log)。
index.html 中只需写一行 <h1> 标签。使用 --directory /root/http/www --bind 127.0.0.1 8080 启动服务器,并将日志写入 /root/http/server.log。ss -ltn 中应能看到 8080。
手工发送 GET
使用 nc 手工编写并发送 GET / HTTP/1.1 请求,将完整响应保存到 /root/http/get.txt。
printf 'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n' | nc -q 1 127.0.0.1 8080 > /root/http/get.txt。响应依次由状态行、若干响应头、空行和正文组成。应能看到 200 OK、Content-type 和 <h1>。
请求不存在的资源
请求不存在的路径 /missing,将 404 响应保存到 /root/http/notfound.txt。
使用与第 2 步相同的请求,只把路径改为 /missing。状态行应为 404。这表示服务器仍在运行,只是资源不存在——TCP 连接已经成功。
HEAD
将 HEAD / 请求的响应保存到 /root/http/head.txt。响应应包含请求头,但没有正文。
只需将方法改为 HEAD。响应仍会包含 Content-Length,但不会包含正文。它适用于只想知道大文件大小的场景,也可用于健康检查。
301 与 Location
请求末尾不带斜杠的 /docs,将包含 301 和 Location 请求头的响应保存到 /root/http/redirect.txt。
请求路径 /docs(不带斜杠)。因为它是目录,服务器会以 301 和 Location: /docs/ 回答“请到那边去”。浏览器和 curl -L 会跟随这个重定向。
使用 curl -v 读取交互过程
使用 curl -v 获取 /docs/guide.txt,将同时包含请求头和响应头的输出保存到 /root/http/curl.txt。
curl -sv http://127.0.0.1:8080/docs/guide.txt > /root/http/curl.txt 2>&1。-v 的输出位于标准错误,因此需要 2>&1。应能看到 > GET /docs/guide.txt、< HTTP/1.0 200 OK 和 < Content-type: text/plain。
只获取状态码
使用 curl -o /dev/null -w '%{http_code}' 获取 /、/docs、/nope 的状态码,分三行保存到 /root/http/codes.txt,每行采用 경로=코드 格式。
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/docs 会输出 301(因为没有添加 -L,所以不会跟随重定向)。三行应为:/=200、/docs=301、/nope=404。
总结所学内容
在 /root/http/report.md 中写入 status_ok=、status_redirect=、status_missing= 三行,并说明请求的三个部分(请求行、请求头、空行)分别是什么。
这三个值就是第 7 步得到的状态码。正文必须说明为什么需要 Host 请求头,以及空行表示什么。还要检查服务器日志(/root/http/server.log)是否记录了此前的所有请求。