HTTP报文长度管理:实际应用与最佳实践

HTTP报文长度管理:实际应用与最佳实践

背景

之前开发一个http代理程序,其主要功能是实现Windows系统中某个服务与外部服务器的通信。这个代理程序不仅仅负责简单的报文转发,还需要具备对通过它的报文进行信息过滤和修改的能力。

一次测试过程中,代理服务器还在接收处理服务端返回的数据,客户端却死掉了。排查后发现是服务端对请求内容没有限制,一次性返回了大量的数据。由于代理程序还没有完成接收、处理的过程,客户端的连接已经超时导致连接中断。

这个经历引发了我对以下几个问题的思考:

  1. 在http协议中,Content-Length是否存在长度限制?尽管我的代理程序能够处理大数据报文的接收,但它对客户端和服务端的通信造成了破坏。这个长度是否存在一个"最佳实践"的值?
  2. 在http报文从客户端发起到接收服务端返回的整个过程中,都存在哪些环节可能对http报文的长度进行修改?
  3. 典型的代理在遇到大数据量http报文时,采取了什么样的处理方式?

为了回答这些问题,我查阅了一些资料,并进行了一些实验。本文中讨论主要聚焦于http/1.x版本的协议。通过这些研究,希望能够为HTTP报文长度管理提供实际应用和最佳实践的指导。

http报文长度是多少

在客户端与服务端通过http协议进行通信时,涉及到多种文件类型,由Content-Type标头规定资源或数据的MIME类型,包括音频、视频、二进制文件等。在浏览器中,最常见的类型可能是text,其子类型通常为html。其他应用程序根据场景可能有不同的侧重。

在采用http协议通信过程中,客户端和服务端需要告知对方发送的数据有多少,这样对方才能够完整接收数据并做进一步的处理。

http协议提供了三种通知对端要读取内容长度的方式:

  1. Content-Length设置
    这是一种最明显的方式,直接将http报文内容部分的长度按字节数显式指定到Content-Length标头的值部分。接收端只需要读取指定长度就能够保证接收完整http报文。需要注意的是,在http协议标准中,并没有规定这个值的最大访问限制,也就是http内容部分可以是0到任意长度。

  2. 设置Transfer-Encoding为Chunk
    将报文分割成多块进行传输,在每一小块数据发送时告诉对端当前的数据块大小。当最后一块数据长度为0时,代表整体数据发送完毕。当服务端动态生成数据时,可能无法确定数据的总长度,或者根本不想等待数据全部生成完毕再发送,这时候可以设置Chunk的传输方式。Chunk的数据发送方式在一定程度上提高了http发送数据的效率。

  3. 短链接
    在这种情况下,Content-Length和Transfer-Encoding都无效。当对端关闭连接时,就意味着传输完毕。早期http/1.0时代,这种方式可能比较常见。在当前大部分http/1.1协议中,很少见到这种实现方式。

影响http报文长度的主要部分

以浏览器为例,在浏览器请求http响应的过程中,数据经过浏览器、代理服务器、负载均衡器、CDN、反向代理、web服务器等节点,这些节点可能对http报文长度进行修改。下图为浏览器请求过程中数据可能经过的网络节点。
在这里插入图片描述

浏览器

浏览器是http请求的发起客户端,同时负责处理服务端的响应报文。在这里,以Microsoft Edge浏览器为例,探讨浏览器对不同长度http报文的处理行为。

在这里插入图片描述

准备两台虚拟机,VM1和VM2,设置二者在同一个局域网。虚拟机VM2通过Apache的httpd对外提供web访问,而虚拟机VM1通过MS Edge访问web页面。首先,生成一个大小为50MB的测试HTML页面,将其放置在httpd的内容目录中。Edge访问该页面后,可以在稍等后显示该页面。接着,生成一个大小为100MB的测试HTML文件,同样放置在httpd的内容目录中。当Edge访问该页面时,等待一段时间后,Edge的tab页中会提示"Out of Memory"错误。

在这里插入图片描述

作为对比,生成一个大小为100MB的二进制文件,同样放置在httpd的内容目录中,并通过调整Content-Disposition响应标头,指示客户端以附件的形式下载文件并保存到本地。此时,浏览器同样采用http协议访问该文件,可以在瞬间完成下载。这与预期一致,就像浏览器可以下载超过几GB的大文件,而不会提示"Out of Memory"的错误。

这里的结论是,http协议中的Content-Length本身没有大小限制;而对于需要在浏览器中渲染显示的页面,它的Content-Length是有大小限制的。从资料来看,edge浏览器对于需要渲染的每个tab页存在一个size limit1。当超过这个size limit,当前tab页可能显示错误提示。即根据不同的业务需求或应用场景,应用程序可能会添加对Content-Length的限制。

缓存代理

对于企业组织来说,应用缓存代理能够直接实现两个效果:1. 降低出口带宽;2. 对用户访问内容实施过滤监控。

以Squid为例,
在这里插入图片描述

在Squid处理客户端的第一次请求时,通常会先向原始服务器发送请求以获取响应,然后缓存该响应并将其发送给客户端。在这个过程中,Squid会处理内容,不会直接将原始响应内容提供给客户端。

在这里插入图片描述

具体步骤如下:

  1. 客户端发起请求:当客户端发送第一次请求时,Squid会检查是否有缓存的响应可用。
  2. 向原始服务器请求:如果没有缓存的响应,Squid将向原始服务器发送请求,获取响应。
  3. 缓存响应:Squid会将原始响应缓存起来,以便将来的相似请求可以使用缓存而不必再次向原始服务器请求。
  4. 处理内容:Squid可能会对响应进行处理,例如压缩、筛选或修改,具体取决于Squid的配置和管理员的设置。
  5. 回复客户端:最后,Squid将处理过的响应发送给客户端。
  6. 客户端再次请求:将已缓存内容返回给客户端。

因此,Squid通常是先缓存响应,然后对其进行处理,最终将处理后的响应回复给客户端。这有助于提高客户端的访问效率,同时降低企业组织的带宽需求。伴随着管理需求,缓存代理可能根据管理员设置的规则删除或添加一些内容,导致缓存的响应与原始响应的长度不同。也可能对原始内容应用压缩算法以便更好地适应缓存或提高性能。这些都会导致缓存的响应与原始响应的长度不同。
所以说,缓存代理可能对http报文的长度进行修改。

负载均衡器

负载均衡器主要提供http数据包的路由,以提高资源利用率并降低服务响应延迟。在其工作过程中,可能涉及到对http报文进行内容压缩,以减小数据传输的长度。
所以说,负载均衡器可能对http报文的长度进行修改。

CDN

CDN主要优化不同地理位置的内容传递效率,其中包括对用户请求的内容进行压缩,以减少传输时间,提高传输效率。
所以说,CDN也可能对http报文的长度进行修改。

反向代理

通过使用反向代理,可以改变前后端报文通知对端结束的方式。

以Nginx为例,当Nginx作为反向代理时,通常会等待后端服务器完全生成响应后,再将整个响应传递给客户端。这是默认行为,适用于大多数情况。

然而,当后端服务器的生成速度慢,而Nginx希望尽早向客户端提供部分响应数据时,它可以使用分块传输编码(chunked transfer encoding)的方式。这种机制允许Nginx将响应分割成一系列数据块,每个块都包含一定量的数据,并立即传递给客户端。这样,客户端就可以尽早接收到部分响应数据,而不必等待整个响应完全生成。
Nginx可以根据配置和需要采用适当的策略,以保证在不同情况下能够提供最佳的性能和用户体验。

我进行了一个简单的实验。
在这里插入图片描述

VM1和VM2处于同一个局域网中,VM2中部署了Nginx和Web服务器。服务器通过Nginx反向代理提供Web浏览服务。在对前后端抓包的截图中,可以看出Nginx在获取到后端服务器的文件流反馈后,将数据内容以chunk的形式返回给Web浏览器。这实际上改变了HTTP内容的传输形式。

在这里插入图片描述

上图为浏览器向Nginx的请求及回复抓包截图。下图是Nginx作为反向代理对后端服务器的请求及回复内容。

在这里插入图片描述

由于Nginx无法知道后端服务器发送完成的时间或数据量,因此Nginx没有等待将所有的数据接收完成,而是在接收一部分后以chunk的形式将数据分块返回给浏览器。最终,后端服务器以短连接的方式告知Nginx数据接收完成。

这个实验展示了Nginx在反向代理过程中采用了分块传输编码的方式,改变了原有数据传输形式,以优化对慢速后端服务器的响应处理2

所以说,反向代理可能会对http报文的长度进行修改。同时,它也提供了一种在遇到大数据量http报文时的一种处理方式。

http报文长度错误处理

在和http报文长度打交道的时候,不可避免遇到报文长度错误的情况,这时候该如何处理呢?这里讨论一下Content-Length描述的长度与实际的报文长度不一致的情况。
当Content-Length描述的长度小于实际的报文长度时,接收端无法读取完整数据。反之,当Content-Length描述的长度大于实际的报文长度时,接收端在读取一部分数据后会等待对端继续发送数据。
在发生等待超时事件的时候,接收端可能会执行以下一种或多种行为:

触发超时回调/异常: 在许多编程语言中,超时通常会触发一个回调函数或引发一个特定的异常。开发者可以通过这个回调函数或异常处理机制来执行特定的操作,比如记录日志、重新尝试请求、或者向用户显示超时提示。

终止请求: 有些客户端库可能会自动终止超时的请求,关闭与服务器的连接,并将控制权返回给应用程序。这可以防止长时间的等待和阻塞,使应用程序能够继续执行其他任务。

用户提示: 如果客户端是一个用户界面应用程序,它可能会向用户显示超时提示,以便用户知道请求未成功,并可能提供重新尝试或其他操作的选项。

两个实际的例子:
当nginx作为反向代理收到服务端回复中出现Content-Length值大于实际内容长度的时候,nginx会出现超时错误。

2023/11/15 09:41:53 [error] 57228#57228: *1 upstream timed out (110: Connection timed out) while reading upstream, client: 172.27.240.1, server: localhost, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:8000/", host: "172.27.249.140:8080"

浏览器tab页处理大的网页(e.g 100MB)渲染,会提示"Out of Memory"。

再回到最初的问题,是否存在一个HTTP报文长度的最佳实践值呢?

这个没有确定的答案。即使是浏览器自己,它在处理不同内容的时候,都会有不同的行为。要回答这个问题,我们必须具体到应用和使用场景:考虑http报文可能经过的网络节点,结合各个网络节点的工作机制,在保证HTTP报文能够被接收完整并能够及时处理的情况下,决定HTTP报文的长度。

参考

  1. Is there any memory limit for Google Chrome browser
  1. nginx org website
  1. Max memory usage of a chrome process (tab) & how do I increase it?
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值