记:curl 本地有响应输出,curl域名却没有响应输出问题排查
问题描述
业务反馈,调试服务时,访问域名没有响应输出,但是本地访问是有的,需要排查下为何curl 域名有问题。
结论
原因:服务端没有设置响应体大小Content-Length,设置Content-Length参数值后,curl域名正常。
修改后的代码(部分):
排查过程
1、查看进程监听地址是不是 0.0.0.0 ,如果是 127.0.0.1 或者是本地ip,那么只允许本地访问,通过域名是访问不了的
查看发现是正常监听的,排除这个原因
2、 curl -v 查看详细输出,看看两种方式有何不同之处
发现两者的状态码都是200,那证明请求是成功的;另外发现http版本不一致,尝试指定http1.0访问,域名也能正常显示了
把服务http版本改为http1.1后,再curl域名,发现还是没输出,还需要继续排查。
3、 tcpdump -i any -w captured_packets.pcap 抓包分析
可看出域名请求也是有响应体的,但是没正确返回到客户端;同时发现所有的响应头中都没有Content-Length参数,尝试在服务中设置这个参数后,curl域名请求正常返回输出。
原因分析
1、Content-Length是什么?
-
定义:
Content-Length
是 HTTP 响应头的一个参数,用于指示响应体的大小(以字节为单位),如果文本文件进行了gzip压缩,那么该值表示压缩后的文件大小。- 在响应头部设置,如 Content-Length: 1256
-
作用:
- 它可以帮助客户端准确地确定响应体的长度,从而正确地接收和处理响应数据。
- 客户端可以根据
Content-Length
的值来确保完整地接收响应体,而不会因为意外断开连接而丢失数据。
-
使用场景:
- 当服务器事先知道响应体的大小时,就可以在响应头中设置
Content-Length
。 - 这通常发生在服务器可以预知响应体大小的情况下,比如返回静态文件或者动态生成的内容大小确定的响应。
- 当服务器事先知道响应体的大小时,就可以在响应头中设置
-
与 Transfer-Encoding 的关系:
Content-Length
与Transfer-Encoding: chunked
是互斥的,服务器应该只设置其中一个。- 当服务器无法预知响应体大小时,可以使用 Transfer-Encoding: chunked 来代替
Content-Length
。
2、不设置Content-Length会怎样?
-
接收不完整的响应:
- 没有
Content-Length
头意味着客户端无法确定响应体的总大小。 - 在传输过程中可能由于网络问题或其他原因出现中断,导致客户端无法知道何时完整地接收到了响应。
- 这会导致客户端无法正确地解析和处理响应内容。
- 没有
-
错误的内容解析:
- 客户端无法提前知道响应体的大小,就无法正确地分配内存来存储和解析响应内容。
- 这可能会导致客户端尝试读取超出实际响应体大小的数据,造成内存溢出或其他错误。
-
响应体传输不确定性:
- 没有
Content-Length
头,客户端无法确定响应什么时候结束,需要一直等待直到连接关闭。 - 这会导致客户端的处理逻辑变得复杂,需要特殊处理响应体的接收。
- 没有
-
无法实现高效的缓存:
- 缓存机制依赖于
Content-Length
头来确定缓存对象的大小。 - 没有
Content-Length
头会降低缓存的命中率和效率。
- 缓存机制依赖于
3、为什么http1.0不设置Content-Length,客户端也可以正常接收响应体并输出?
因为HTTP/1.0 使用的是短连接(non-persistent connection)模式,每个请求/响应都是在一个新的连接上进行的,服务器在发送完整个响应体后,会主动关闭 TCP 连接,当客户端检测到连接关闭时,就知道响应体已经接收完毕,而不必依赖设置 Content-Length
头来知晓响应体是否发送完成。
注:服务器在发送完整个响应体后主动关闭 TCP 连接,是由Connection决定的,HTTP/1.0默认使用 Connection: close ,表示当前 HTTP 连接在响应完成后将被关闭。
而HTTP/1.1引入了持久连接(persistent connection)的概念,客户端和服务器可以在一个连接上传输多个请求和响应。为了处理这种情况,HTTP/1.1 要求服务器必须显式地告知客户端响应体的长度,以便客户端能够正确地处理后续的请求和响应;因此需要设置Content-Length或Transfer-Encoding参数。
注:HTTP/1.1中,默认 Connection: keep-alive ,表示客户端和服务器希望保持当前的 TCP 连接,以便后续可以复用该连接。