计网复习07-应用层之HTTP报文格式

HTTP报文概述

HTTP概述在HTTP版本发展与Google的暗中助力 中已经说得差不多了,这篇文章来专门讲一下HTTP的报文格式

计网复习07-应用层之HTTP报文格式

HTTP 协议的请求报文和响应报文的结构基本相同,由三大部分组成:

  • 起始行(start line):描述请求或响应的基本信息;
  • 头部字段集合(header):使用 key-value 形式更详细地说明报文;
  • 消息正文(entity):实际传输的数据,它不一定是纯文本,可以是图片、视频等二进制数据。

这其中前两部分起始行和头部字段经常又合称为“请求头”或“响应头”,消息正文又称为“实体”,但与“header”对应,很多时候就直接称为“body”。

HTTP报文是面向文本的,因此header都是一些ASCII码串,可以很容易地用肉眼阅读,不用借助程序解析也能够看懂。(所以中文通常要经过urlencode)

HTTP 协议规定报文必须有 header,但可以没有 body,而且在 header 之后必须要有一个“空行”,也就是“CRLF”,十六进制的“0D0A”

请求行&状态行

请求行

计网复习07-应用层之HTTP报文格式

请求行是请求报文中的第一行,请求行由三部分构成:

  1. 请求方法:是一个动词,如 GET/POST,表示对资源的操作。事实上,请求方法的语义并没有被严格遵守,直到RESTful API的广泛应用,才使得请求方法的语义变得重要了起来。
方法(操作) 意义
OPTION 请求一些选项的信息
GET 请求读取由 URL所标志的信息
HEAD 请求读取由 URL所标志的信息的首部
POST 给服务器添加信息(例如,注释)
PUT 在指明的 URL下存储一个文档
DELETE 删除指明的 URL所标志的资源
TRACE 用来进行环回测试的请求报文
CONNECT 用于代理服务器

关于请求方法,我们通常谈到幂等性,相关内容可以参考Http请求的幂等性

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

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

状态行

状态行是响应报文中的第一行,与请求行的结构类似。

计网复习07-应用层之HTTP报文格式

状态行包括三项内容:

  1. 版本号:表示报文使用的 HTTP 协议版本。
  2. 状态码:一个三位数,用代码的形式表示处理的结果。我在SpringBoot开发RESTful API中列举了常用的HTTP状态码。
  3. 短语:解释状态码的简单短语,帮助人理解原因。对于常见的状态码200,后面跟的短语可能就是OK

头部字段(首部行)

请求行或状态行再加上头部字段集合就构成了 HTTP 报文里完整的请求头或响应头。请求报文和响应报文中的头部字段的格式都是一样的,只是字段可能有所区别。

头部字段是 key-value 的形式,key 和 value 之间用“: ”分隔,最后用 CRLF 换行表示字段结束。比如在“Host: 127.0.0.1”这一行里 key 就是“Host”,value 就是“127.0.0.1”

使用头字段需要注意下面几点:

  • 字段名不区分大小写,例如“Host”也可以写成“host”,但首字母大写的可读性更好;
  • 字段名里不允许出现空格,可以使用连字符“-”,但不能使用下划线“_”。例如,“test-name”是合法的字段名,而“test name”``“test_name”是不正确的字段名;
  • 字段名后面必须紧接着“:”,不能有空格,而“:”后的字段值前可以有多个空格
  • 字段的顺序是没有意义的,可以任意排列不影响语义;
  • 字段原则上不能重复,除非这个字段本身的语义允许,例如 Set-Cookie。

Content-Type&Content-Encoding

Content-Type:标识body的内容类型,取值通过“MIME type”标准确定,MIME 把数据分成了八大类,每个大类下再细分出多个子类,形式是“type/subtype”的字符串,巧得很,刚好也符合了 HTTP 明文的特点,所以能够很容易地纳入 HTTP 头字段里。

这里简单列举一下在 HTTP 里经常遇到的几个类别:

  • text:即文本格式的可读数据,我们最熟悉的应该就是 text/html 了,表示超文本文档,此外还有纯文本 text/plain、样式表 text/css 等。
  • image:即图像文件,有 image/gifimage/jpegimage/png 等。
  • audio/video:音频和视频数据,例如 audio/mpegvideo/mp4 等。
  • application:数据格式不固定,可能是文本也可能是二进制,必须由上层应用程序来解释。常见的有 application/jsonapplication/javascriptapplication/pdf 等,另外,如果实在是不知道数据是什么类型,像刚才说的“黑盒”,就会是 application/octet-stream,即不透明的二进制数据。

需要注意的是以x-开头的类型是我们自定义的类型。使用*代表任意类型。

Content-Encoding:告诉数据是用的什么编码格式,这样对方才能正确解压缩,还原出原始的数据。如果响应报文里没有 Content-Encoding 字段,就表示响应数据没有被压缩。常见的Content-Encoding
+ gzip:GNU zip 压缩格式,也是互联网上最流行的压缩格式;
+ deflate:zlib(deflate)压缩格式,流行程度仅次于 gzip;
+ br:一种专门为 HTTP 优化的新压缩算法(Brotli)。

Accept&Accept-Encoding

如果服务器一厢情愿发送特定的数据类型而客户端解析不了也不行,所以请求首部字段中通常会有Accept字段,代表客户端可理解的 MIME type,用,做分隔符列出多个类型。对应的,也存在Accept-Encoding字段,代表客户端支持的压缩格式。服务器通常要通过这两个字段决定响应的数据类型。

其他Accept-和Content-字段

Accept-Language代表客户端可理解的自然语言,Content-Language告诉客户端实体数据使用的实际语言类型。

Accept-Charset代表客户端可解析的字符集,响应报文并不会有Content-Charset,而是在Content-Type字段的数据类型后面用“charset=xxx”来表示。

不过现在的浏览器都支持多种字符集,通常不会发送 Accept-Charset,而服务器也不会发送 Content-Language,因为使用的语言完全可以由字符集推断出来,所以在请求头里一般只会有 Accept-Language 字段,响应头里只会有 Content-Type 字段。

内容协商的质量值

我们称以上Accept-*Content-*字段为客户端和服务端对传输的内容进行内容协商,在 HTTP 协议里用 Accept、Accept-Encoding、Accept-Language 等请求头字段进行内容协商的时候,还可以用一种特殊的“q”参数表示权重来设定优先级,这里的“q”是“quality factor”的意思。

权重的最大值是 1,最小值是 0.01,默认值是 1,如果值是 0 就表示拒绝。具体的形式是在数据类型或语言代码后面加一个;,然后是q=value

例如:

Accept: text/html,application/xml;q=0.9,*/*;q=0.8

它表示浏览器最希望使用的是 HTML 文件,权重是 1,其次是 XML 文件,权重是 0.9,最后是任意数据类型,权重是 0.8。服务器收到请求头后,就会计算权重,再根据自己的实际情况优先输出 HTML 或者 XML。

内容协商的结果

内容协商的过程是不透明的,每个 Web 服务器使用的算法都不一样。但有的时候,服务器会在响应头里多加一个Vary字段,记录服务器在内容协商时参考的请求头字段,给出一点信息,例如:

Vary: Accept-Encoding,User-Agent,Accept

这个 Vary 字段表示服务器依据了 Accept-EncodingUser-AgentAccept 这三个头字段,然后决定了发回的响应报文。

其他常见的头部字段

Host字段,它属于请求字段,只能出现在请求头里,它同时也是唯一一个 HTTP/1.1 规范里要求必须出现的字段,也就是说,如果请求头里没有 Host,那这就是一个错误的报文。Host 字段告诉服务器这个请求应该由哪个主机来处理,当一台计算机上托管了多个虚拟主机的时候,服务器端就需要用 Host 字段来选择,有点像是一个简单的“路由重定向”。

User-Agent请求字段,只出现在请求头里。它使用一个字符串来描述发起 HTTP 请求的客户端,服务器可以依据它来返回最合适此浏览器显示的页面。但由于历史的原因,User-Agent 非常混乱,每个浏览器都自称是“Mozilla”``“Chrome”``“Safari”,企图使用这个字段来互相“伪装”,导致 User-Agent 变得越来越长,最终变得毫无意义。

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

Server字段是响应字段,只能出现在响应头里。它告诉客户端当前正在提供 Web 服务的软件名称和版本号,例如nginx/1.**Server 字段也不是必须要出现的,因为这会把服务器的一部分信息暴露给外界,如果这个版本恰好存在 bug,那么黑客就有可能利用 bug 攻陷服务器。所以,有的网站响应头里要么没有这个字段,要么就给出一个完全无关的描述信息。

Content-Length,它表示报文里 body 的长度,也就是请求头或响应头空行后面数据的长度。服务器看到这个字段,就知道了后续有多少数据,可以直接接收。如果没有这个字段,那么 body 就是不定长的,需要使用 chunked 方式分段传输。

原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/%e8%ae%a1%e7%bd%91%e5%a4%8d%e4%b9%a007-%e5%ba%94%e7%94%a8%e5%b1%82%e4%b9%8bhttp%e6%8a%a5%e6%96%87%e6%a0%bc%e5%bc%8f/

发表评论

电子邮件地址不会被公开。