本文主要讨论 HTTP/1.1 的文本报文格式。HTTP/2 和 HTTP/3 使用不同的二进制分帧机制,不在本文范围内。

HTTP/1.1 消息可以分为请求和响应:请求由客户端发送给服务器,响应由服务器返回给客户端。两者都由起始行、若干字段、一个空行和可选的消息体组成。请求的起始行是请求行,响应的起始行是状态行。

请求行

请求行由三部分构成:

  1. 请求方法:是一个动词,如 GET/POST,表示对资源的操作;
  2. 请求目标:通常是一个 URI,标记了请求方法要操作的资源;
  3. 版本号:表示报文使用的 HTTP 协议版本。

这三个部分通常使用空格来分隔,最后要用 CRLF 换行表示结束。

状态行

比起请求行来说,状态行要简单一些,同样也是由三部分构成:

  1. 版本号:表示报文使用的 HTTP 协议版本;
  2. 状态码:用三位数代码的形式表示处理的结果,比如 200 是成功,500 是服务器错误;
  3. 原因:作为数字状态码补充,是更详细的解释文字,帮助人理解原因。

字段

请求行或状态行再加上头部字段集合就构成了 HTTP 报文里完整的请求头或响应头。头部字段都是键值对,key 和 value 之间用“:”分隔,最后用 CRLF 换行表示字段结束。

HTTP 字段很灵活。标准定义了 Host、Content-Length 等常用字段,也允许扩展字段。一个字段可以出现在哪类消息、是否允许重复,以及具体语义,都应以该字段的定义为准,不宜简单套用固定的四分类。

Host

它属于请求字段,只能出现在请求头里,也是唯一一个 HTTP/1.1 规范里要求必须出现的字段。如果请求头里没有 Host,那这就是一个错误的报文。

User-Agent

User-Agent是请求字段,只出现在请求头里,用于表明客户端的类型。不过大家都相互伪装,导致这个字段非常混乱。

Date

Date字段是一个通用字段,但通常出现在响应头里,表示 HTTP 报文创建的时间,客户端可以使用这个时间再搭配其他字段决定缓存策略。

Server

Server字段是响应字段,只能出现在响应头里。它告诉客户端当前正在提供 Web 服务的软件名称和版本号。

Content-Length

Content-Length 表示消息体的长度,单位是字节。不过,没有 Content-Length 并不等于一定使用 chunked。

在 HTTP/1.1 中,消息体边界要按一组有优先级的规则判断:有些响应根据请求方法或状态码本来就没有消息体;使用 Transfer-Encoding 且最终编码为 chunked 时,接收方通过读取各个 chunk 判断结束;也可能由 Content-Length 给出长度。请求既没有 Transfer-Encoding 也没有 Content-Length 时,消息体长度通常为零;部分响应还可以由连接关闭来界定消息体。

总结

  • 本文讨论的 HTTP/1.1 消息可以理解为“起始行 + 字段 + 空行 + 可选的消息体”;
  • HTTP 报文可以没有 body,但必须要有 header,而且 header 后也必须要有空行;
  • 请求头由“请求行+头部字段”构成,响应头由“状态行+头部字段”构成;
  • 请求行有三部分:请求方法,请求目标和版本号;
  • 状态行也有三部分:版本号,状态码和原因字符串;

参考资料