HTTP 是文本 — 并按顺序查每一层
一句话总结
HTTP请求是请求行、头衔、空行三个部分的文本。nc可以用手写。响应的状态代码表示“成功到什么层”——404是TCP或HTTP成功后的回复,refused表示服务器不存在。
为什么需要这个?
当浏览器和SDK隐藏HTTP时比较方便。但是代理会更改头信息,Host如果出现错误的、错误的网站,当重定向从A→B→A时,应该看到隐藏的内容。用手写过请求的人curl -v的>哇<我知道它是什么。那是API调试的一半。
而且网络诊断最终是按顺序拆分层。链接→地址→邻居→路径→名称→端口→防火墙→响应。每个层都有只看该层的命令。不按顺序随意输入任何命令都会被困在“一切正常,但不行”中。
怎么行动
请求是GET /docs/guide.txt HTTP/1.1一行,Host: localhost是一些行相同的头,还有空行。行末是CRLF。如果忽略空行是“头末”,服务器会以为会有更多的头,所以会等待。Host是HTTP/1.1的唯一必需头——因为一个服务器接收多个网站,所以用这个来选择哪个网站。Connection: close是指在响应后切断,如果没有的话,重新使用连接(keep-alive)。
回答是HTTP/1.0 200 OK状态行、标题行、空行、正文。2xx成功,3xx在其他地方(Location请按照),4xx请求方错误,5xx服务器方错误。HEAD只收到头衔,如果请求目录时不带斜杠,则指向带斜杠的地方,指向301。
如果把诊断顺序用命令写下来的话,就是这样。ip -br link(链接是否已上传)→ip -br addr(有地址吗)→ip neigh(邻居是否FAILED)→ip route get(不管去哪里)→getent hosts(名字能解开吗,dig将服务器单独设置为)→ss -ltn(端口是否打开)→nft list ruleset的counter(防火墙是否设置好)→curl -v(回答是什么)。前一步必须正确,后一步才有意义。
在现场相遇的样子
可以ping,但不能打开。 ping是ICMP,服务是TCP。防火墙根据协议不同而不同。相反,ping不能打开并不意味着断开连接——这个课程的最后一个练习在设计上阻止了ICMP。诊断是根据与实际服务相同的协议和端口进行的。
可以使用dig,但不能使用curl。/etc/hosts还有旧的地址。dig直接问服务器,curl先通过libc看hosts。两个名字有两个答案。
用状态代码区分层级
收到“不行”的报告时,可以根据返回的结果判断成功了多远。记住这个应对方法,调查范围就会立即缩小。
| 结果 | 走到了哪里 | 下次要去的地方 |
|---|---|---|
| 找不到名字 | 连DNS都通过不了 | getent hosts,dig,/etc/hosts |
| Connection refused | 甚至去了对方那里,那个端口却没有人 | ss -ltn服务是否恢复了 |
| 超时(没有回复) | 在某个地方安静地被抛弃 | 防火墙、安全组、路径 |
| TLS握手失败 | TCP成功 | 证书到期日·名称·中间证书 |
| 502 / 504 | 成功到代理,后面是问题 | 上游服务和它的超时 |
| 404 | 到HTTP为止全部成功 | 路径·Host头信息·路由规则 |
| 401 / 403 | 服务器识别我并拒绝 | 身份证件和权限 |
refused和timeout的区别特别有价值。 refused是对方主机活着,立即回答“没有那个端口”,所以网络路径是正常的,timeout是完全没有回复,很有可能在中间被丢弃。怀疑防火墙的地方是后者,而不是前者。
502和504也需要分开看。502连接到上游,但收到了奇怪的回复或连接中断,504上游没有在规定时间内回复。前者是上游死亡或重新启动时,后者是上游活着但速度慢时出现的。如果代理的超时时间比后端服务的处理时间短,即使是正常的请求也会变成504,所以将两个值同时匹配是基本。
最后,curl在测试时,必须创造与实际客户相同的条件。Host头信息、协议(HTTP/1.1或2)、以及代理环境变量。在服务器上curl因为相当多的无法成为银币但无法成为浏览器的状况正是这三个中的一种。
下次实习要做的事情
python3 -m http.server浮动nc手动制作GET·HEAD·404·301后收到后curl -v和对比。然后在综合实训中,在两个子网、路由器、dnsmasq、forward防火墙、掩码的VM上设置,并将按什么顺序查看“不打开”的报告留作报告。