HTTP

简介

超文本传输协议(英语:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议[1]。HTTP是万维网的数据通信的基础。

设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。通过HTTP或者HTTPS协议请求的资源由统一资源标识符(Uniform Resource Identifiers,URI)来标识。

http 主要用于传输浏览器可以识别的超文本标记语言,除了可以传输文本,也可以用来传输声音,图片,视频等信息。

URL

URL:统一资源定位符,用来表示从互联网上得到的资源位置和访问这些资源的方法,通过URL能够知道通过什么协议,什么路径地址对资源进行访问。

URL 的格式如下

1
<协议>://<主机>:<端口>/<路径>

比如在浏览器访问http 协议的资源,都是从 http:// 开头,而如果要访问 **ftp ** 服务的资源 都是从 ftp:// 开头。

比如此URL http://www.sina.com.cn/aa/xx.html

协议是: https

主机是:/www.sina.com.cn

端口号没有是因为http 协议默认端口80,不需要指定。

路径是 /aa/xx.html 表示请求此资源,或执行某操作。

HTTP报文结构

http请求的客户端也就是浏览器,服务端一般是web服务器,它们能够互相的通信,能够知道互相发送信息的内置代表什么,那么需要客户端和服务端都实现HTTP协议的标准。它们都安装同一套逻辑发送数据和解析。

请求结构

通过浏览器查看网络请求:

可以看到一个http请求由几个部分组成。

  • 常规 基本请求信息,请求URL 请求方法方式等。
  • 请求标头 请求信息的头部,Accept ,Cache-Control 告诉请求服务端客户端需要的数据信息。
  • 请求体 请求的信息的结构体信息。
  • 响应标头 响应信息的头部,主要告诉客户端响应数据的相关信息,比如语言,数据类型。
  • 响应体 响应的数据,如果是html 将返回整个页面数据,如果是json将返回json数据。

基本上一个http请求 请求数据 ====> 请求头: 请求体 返回数据 ====> 响应头:响应体

请求头主要是告诉客户端,如果解析我请求的请求体数据,并且请求的数据是什么格式的,我希望你返回什么样的格式,返回哪部分的数据,服务器将根据请求信息中的请求头信息来获取客户端的一些信息来解析请求体数据。

而客户端拿到响应的数据的时候,只有先拿到响应头的数据,知道数据的contentType 等信息,才能解析响应体数据,完成对应的解析。

数据格式

通过wireshark 抓包工具,查看一个http请求的真实数据格式是什么样的。

找到对应的http请求,选择追踪流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
GET /index HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="102", "Google Chrome";v="102"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9

HTTP/1.1 200
Content-Type: text/html;charset=UTF-8
Content-Language: zh-CN
Transfer-Encoding: chunked
Keep-Alive: timeout=60
Connection: keep-alive

<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>......</title>
</head>
<body>
..................
</body>
</html>

  • 可以看到http 协议的数据 首先是请求头,然后是请求体 (上图是只有请求头没有请求体)。
  • 通过图2 可以看到 header 信息是由多个信息对组成,并且通过换行符分割。
  • 响应信息同样是先有响应的头信息,紧接着才有响应的数据。

http 请求一定有请求头和响应头 ,不一定有请求体和响应体

ABNF

ABNF ( Augmented Backus-Naur Form) 增强的巴克斯-瑙尔范式。

这是一种语法描述的语言,通过此来描述对应的语法规范。BNF是已经存在的一种描述语言符号,ABNF在BNF的基础上做了增强,通过此协议的定义语言可以清楚的了解一个协议的规范的格式。

基本介绍链接:https://www.jianshu.com/p/15efcb0c06c8#2-%E5%B7%B4%E7%A7%91%E6%96%AF%E8%8C%83%E5%BC%8Fbnf

ABNF 是在 RFC 5233 中增加的. https://www.rfc-editor.org/rfc/rfc5234

通过此RFC 5234 的文档可以知道ABNF的各个符号所代表的含义

​ 常见规则对照表

格式

最新的http/1.1 的协议 rfc 7230 https://www.rfc-editor.org/rfc/rfc7230

可以看到定义了http的相关的规范和细节。

对应一个消息格式,所有的http/1.1 都是这种格式。

start-line 开始行

不管是请求信息 和响应信息,行的开始需要先有开始行。

开始行的格式是

1
start-line     = request-line / status-line

这句话的意思是 开始行在客户端请求的时候是 request-line 服务端响应的时候是个status-line

request-line
1
2
request-line   = method SP request-target SP HTTP-version CRLF

请求行示例

GET /index HTTP/1.1

method 表示请求的方式,在http中 有GET ,POST ,DELETE ,OPTION 等等。

SP 表示必须有一个空格

request-target表示有请求的目录路径

SP 又有一个空格

HTTP-version 表示http的版本信息,比如HTTP/1.1

CRLF 表示末尾有一个换行

status-line
1
status-line = HTTP-version SP status-code SP reason-phrase CRLF
1
status-code    = 3DIGIT
1
reason-phrase  = *( HTAB / SP / VCHAR / obs-text )

状态行示例

HTTP/1.1 200 OK

状态行的格式:

HTTP-version : http 版本,比如 HTTP/1.1

SP 空格

status-code: 状态码 一个3位的正整数数字的描述码,比如 200 401 500

SP 空格

reason-phrase :主要提供跟状态码相关的文本描述信息,不是必须有的,客户端可以忽略此信息。

header-field 头部字段信息

1
2
3
4
5
6
7
8
9
10
11
12

header-field = field-name ":" OWS field-value OWS

field-name = token
field-value = *( field-content / obs-fold )
field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
field-vchar = VCHAR / obs-text

obs-text = %x80-FF

obs-fold = CRLF 1*( SP / HTAB )
; obsolete line folding
1
2
3
4
5
6
token          = 1*tchar

tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
/ "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
/ DIGIT / ALPHA
; any VCHAR, except delimiters

注意: ABNF中 ; 表示注释符号

头部字段信息 是由英文冒号隔开的 key value 形式。

field-name: 表示字符格式的字段名称,由tchar 定义格式。

OWS 表示在 field-value 前后可以有空格信息,也可以没有空格,不过一般习惯在上面增加一个空格

field-value: * 号表示后面的值可以为0 个或多个,也就是值可以为空。/ 表示或,也就是要么是 field-content 要么是 obs-fold

field-content: 1* (SP /HTAB) 大于等于1 个空格或tab键, 表示可以由一个或多个空值分割的 field-vchar

field-vchar: VCHAR 表示字符串 ,这里表示要么是字符串要么是 obs-txt

obs-fold: 表示一个换行 至少一个空格或tab符。

message-body

message-body 为可选项,用于承载该请求或响应的有效负载正文。在请求和响应的时候都可以有此部分数据。

请求方法

在request-line 的开始部分必须以请求方法开始,http不同的请求方法一般用于不同的用途。

  • GET 一般用于读取的操作,参数是拼接在URL的后面的,受浏览器的限制,数据的传输长度有限,并且地址栏出现中文会被浏览器自动 urlencode 编码
  • POST 一般用于添加,修改,删除的操作,请求参数放到请求体中
  • HEAD: 和GET请求相同,返回的数据中只返回响应头信息,不返回响应体信息,用于获取资源信息,但是不马上获取资源的场景。
  • OPTIONS: 获取服务器所支持的HTTP请求方法,还可以用来检查服务器的性能。比如 前端跨域请求发起之前,会发起一次预校验请求,确认服务器是否支持当前的跨域请求。
  • PUT: 对存在的资源进行覆盖
  • PATCH: 对部分资源修改,如果不存在就新增
  • DELETE: 删除指定的资源
  • TRACE: 请求服务器回显收到的请求信息,主要用于请求测试
  • CONNECT: 开启一个客户端与所请求资源之间的双向通道,可以用来创建隧道。

不过在上述的请求方法中,大部分可能是不会经常用到的,最常用的也就是 GET 和POST 请求。

状态码

在 响应的 status-line 中会返回一个数字类型的状态码。根据不同的状态码可以判断服务器对此请求的处理结果。

比如最常见的 200 ,表示当前的请求是成功的。如果出现 500 ,表示服务端出现了错误。如果出现了 400 表示客户端的请求格式有问题,客户端就需要检查请求格式。

信息响应:100 - 199

成功响应: 200 -299

重定向: 300 -399

客户端错误: 400 -499

服务器错误: 500 - 599

  • 100 Continue 客户端应当继续发送请求。这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝。客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应.服务器必须在请求完成后向客户端发送一个最终响应。比如在请求一个资源的时候,通过options 发送一个无请求体的数据,如果服务端通过header 信息就拒绝了,那么后续就没有发送带请求体的必要了,如果返回100 表示可以继续发送。

    跨域请求的options 请求返回的是 100

  • 200 OK 最常见的,成功了

  • 301 Moved Permanently 表示永久性的重定向,表示访问的资源已经不存在了,永久性的迁移到要重定向的路径上了

  • 302 SC_MOVED_PERMANENTLY 临时的重定向,请求的资源暂时性的移动到了 location 字段指定的URL上

  • 304 Not Modified 不用再次传输请求的内容,可直接利用客户端的缓存,客户端会缓存一些数据信息,当发送给服务端的时候,服务器的请求数据并没修改,认为客户端可以利用自身缓存的内容,就返回一个没有响应体的 信息,状态码标记为 304

  • 400 Bad Request 语法格式错误,服务端没办法解析

  • 401 Unauthorized 缺少身份验证,客户端请求一个需要身份验证的资源,但是没有传递身份信息的时候就返回此信息。一般未登录返回此状态。

  • 403 Forbidden 缺少授权信息,客户端没有被授权访问某资源。一般没权限返回此状态。

  • 404 Not Found 未找到,没有找到对应的资源

  • 405 Method Not Allowed 不允许使用此方法请求,比如后端只允许用POST 请求访问,却用GET 方式访问。

  • 406 Not Acceptable 服务端无法提供于Accept-Charset 以及 Accept-Language 指定的值匹配的响应。

  • 408 Request timeout 出现请求超时

  • 500 Internal Server Error 内部服务器错误

  • 501 Not Implemented 服务器对请求的方法不支持。

  • 502 Bad Gateway 网关返回的错误。比如 网关正常,但是服务无法请求,网关也会返回此错误。

  • 503 Service Unavailable 服务器出现超载的情况,暂时无法访问

  • 505 HTTP Version Not Supported 不支持的 HTTP 版本

http状态码查询: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status

头部字段

请求头字段

在请求数据的时候,客户端请求信息的消息。

  • User-Agent 浏览器的标识 浏览器请求时候带着一个字符串,服务端可以获取客户端的浏览器和操作系统版本. 举例: User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36

  • Host 请求的服务器的域名和端口号. 举例: host: www.baidu.com

  • Date 发送的时间

  • Referer 表示浏览器的来路页,表示请求所在页面是从哪个页面调转过来的。

  • Content-Type 请求体的类型,表示请求体的数据格式,服务端根据Content-Type 的不同,使用不同的方法解析请求体数据。比如 content-type: application/json 那么服务端将使用json的方式来解析请求体数据。

  • Content-Length 请求体的长度

  • Accept 能够接受的响应内容的类型,服务器将根据此Accept值返回对应的响应格式。 举例: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9

    accept 可以使用逗号分割表示可以接受多种格式,q 表示各个类型的权重,既优先返回那种格式。

  • Accept-Charset 客户端接受的字符集

  • Accept-Encoding 客户端接受的编码方式 举例: Accept-Encoding: gzip, deflate, br

  • Accept-Language 客户端接受的语言 举例: Accept-Language: zh-CN,zh;q=0.9

  • Range 请求数据的范围,表示字节的偏移量。 举例: range: 100-200

  • Origin 表示请求的来源,在跨域请求的时候需要传输此值,表示发起跨域请求的域名,后端会根据跨域请求的域名来源来判断是否允许跨域请求的访问。

  • Cookie cookie信息,在请求指定的域名的时候,会自动携带此域名对应的所有cookie信息,写入到此值内以传到后端。举例: Cookie: BIDUPSID=F1C95DEC4915B53992BF14540A761ADA; PSTM=1653478429; BAIDUID=58C0BEE9F757DA66CABEC0F28FA1719F:FG=1;

  • Connection 表示浏览器要优先连接的类型 举例: Connection: keep-alive 保持连接,表示使用长连接的方式,多个http请求将只发生一次tcp握手和分手。

  • Cache-Control 指定在这次请求/响应链中的所有缓存机制都必须遵守的指令。举例: Cache-Control: no-cache 表示不使用缓存

响应头字段

在响应数据的时候,返回服务器或资源的相关的信息。

  • Content-type 响应体的类型 举例:Content-Type: text/html;charset=UTF-8

  • Content-Language 响应体语言 举例: Content-Language: zh-CN

  • Transfer-Encoding 传输编码类型 举例: Transfer-Encoding: chunked

  • Content-Encoding 内容编码类型 举例:content-encoding: gzip

  • Content-Length 响应体的长度

  • Content-Disposition 一个让客户端下载文件并建议文件名的头部,在下载文件的时候,在后端设置下载文件名 举例: Content-Disposition: attachment;filename=”download.txt”

  • Accept-Ranges 服务器使用 HTTP 响应头 **Accept-Ranges** 标识自身支持范围请求 (partial requests)。字段的具体值用于定义范围请求的单位。

  • Content-Range 在 HTTP 协议中,响应首部 Content-Range 显示的是一个数据片段在整个文件中的位置。

  • Date 响应时间

  • Last-Modified 请求的对象的最后修改日期

  • Server 服务端的名称

  • Expire 响应过期时间

http-header 查询 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers

HTTP 数据请求

URL 传参

指直接将参数拼接到URL上的方式。

所有的请求方式都支持URL传参。

此种方式如果直接通过url可以携带所有参数,那么不用传请求体参数,当然也可以既使用请求体参数,也使用URL参数。

application/x-www-form-urlencoded

此种请求格式表示将form中的参数,使用urlencoded 的方式拼接成url 参数的格式放到请求体中传输。

使用postman 使用此种请求方式

  • 此种请求的content-type 是 application/x-www-form-urlencoded 表示告诉服务端如何解析请求体内的数据格式。
  • 请求体的数据为 通过 & 连接的多个参数,中文会被编码

application/json

请求体使用json数据传输,是一种比较常用的一种方式,可以传输比较复杂的数据结构。

  • contentType 等于application/json
  • 传输的请求体是json字符串,后端将会以json的方式解析

multipart/form-data

此种传参方式一般用来传输文件。

此种请求方式的 content-type 为 multipart/form-data;boundary=xx

注意每个请求的 boundary 的值是不同的。boundary 表示分界值。

请求体部分是一个比较复杂的数据结构。

rfc1521 中有详细的格式定义 https://www.rfc-editor.org/rfc/rfc1521#page-70

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# preamble 和 epilogue 被忽略,所以整体是由至少一个 encapsulation 组成,然后最后有一个epilogue 表示结束
multipart-body :=preamble 1*encapsulation close-delimiter epilogue


#每个值信息 由 delimiter body 信息 和换行符组成
encapsulation := delimiter body-part CRLF

# delimiter 是由 "--" 开头 加上contenttype中的 boundary 值,最后加上一个 换行符
delimiter := "--" boundary CRLF ;taken from Content-Type field.
; There must be no space
; between "--" and boundary.



preamble := discard-text ; to be ignored upon receipt. 值会被忽略


#关闭标记由 "--"开头 加 boundary 值 再加 "--" 加换行符结束
close-delimiter := "--" boundary "--" CRLF ; Again, no space 关闭标记

epilogue := discard-text ; to be ignored upon receipt. 值会被忽略


  1. multipart/form-data 的请求体结构是在请求的时候会生成一个boundary的分割值。

比如boundary = –7654232323232

请求体中的每个值的开头 和最终的标记结束部分都会用到 boundary 的值。

  1. 每个值的部分将是 —-7654232323232 开头

  2. 接着是一些描述信息,Content-Disposition 和值的名称,如果是文件会有文件的contenttype ,文件的名称等。

    //普通的值

    Content-Disposition: form-data; name=”key1”

    value1

    //文件数据

    Content-Disposition: form-data; name=”file”; filename=”……….xlsx”; filename*=UTF-8’’%E7%A9%BA%E6%96%87%E4%BB%B6.xlsx

    Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

  3. 最终将是一个结束符号,表示当前的 multipart/form-data 请求体已经完毕。

通过这些定义好的格式,后端将知道如何解析multipart/form-data 格式的请求数据。

HTTP 缓存

http 的GET 请求会对一些能够缓存的内容缓存到浏览器,减少再次向服务器请求。一般缓存的是静态资源。

浏览器的缓存分为2种类型,一种是缓存在内存里的称为 memory cache .一种是缓存在磁盘上的称为为 disk cache。通过浏览器的网络请求可以看到对应的请求是从缓存获取的。

对应浏览器是否要使用缓存和缓存的过期时间等,是由多个请求头和响应头共同判断和控制的。

缓存的响应头

pragma: 在http/1.0的时候,使用的缓存相关的头部信息,相当于 cache-control ,目前已经废弃,但是也兼容此属性。

expires: 缓存的过期时间。http/1.0的产物。

cache-control: 缓存策略,策略有

  • no-storage: 不缓存
  • public 允许用户,代理服务器缓存数据到本地
  • private 只允许用户缓存数据到本地
  • max-age 缓存的有效时间,单位是秒
  • no-cache 每次需要发送请求给服务器询问缓存是否有变化,再决定如何试用缓存

这3个选项是服务端高度客户端是否使用缓存和如何使用缓存 ,优先级是 pragma > cache-control > expires

last-modified: 资源的最后一次修改时间。

ETag 资源的唯一标识,根据资源的内容算出来的摘要值。

这2个选项的作用是客户度根据什么判断本地缓存的资源是否发生了变更 ,优先级是: Etag > Last-Modified

缓存的请求头

if-None-Match

如果上一次的响应头中有Etag的值,就会将Etag作为此请求头的值,服务器根据请求头的值和文件最新的摘要值做比较,如果一致就说明资源没有被修改,就返回 304 Not Modifed 告诉客户端还可以使用缓存。

如果不一致,表示服务器的文件被修改了,就会将最新的资源文件返回给客户端。

if-Modified-Since

如果上一次响应头中没有Etag的值,但是有Last-Modified,就会将Last-Modified 的值作为此请求头的值,服务器会根据服务器上的资源文件的最后修改时间和请求头的时间做比较,如果服务器的时间新就表示被修改了,就返回新的资源,如果一致就说明没有被修改,就返回 304。

使用时间来判断资源被修改的确定是:

​ 1. 因为这里的时间精确到秒的,所以一秒内重复修改了资源,可能客户端感知不到。

  1. 如果仅仅更新时间变化了,而内容其实一致,会导致网络重复传输。

缓存的流程

  • 浏览器发送GET 请求,首先查看本地是否有缓存,如果本地没有缓存就请求服务器,服务器给出响应
  • 根据服务器的cache-control 的值,决定是否将资源缓存到本地。
  • 浏览器发送GET 请求,如果本地有缓存,就检查本地缓存的响应头,如果响应头的cache-control 为no-cache,表示需要再请求下服务器判断缓存是否有效。依次根据是否有Etag 和Last-Modified 来请求服务器来判断是否需要刷新缓存。
  • 如果本地缓存的cache-control 不是 o-cache ,那么检查本地缓存是否过期,如果没过期直接使用,如果过期了那么需要根据Etag 和Last-Modified作为请求头请求服务器判断本地缓存是否需要刷新。

本地缓存 如果设置了每次使用检查是否修改,那么每次使用都还会请求服务器检查资源是否变更。如果本地缓存未设置没有使用请求服务器检查,在有效期内直接使用,过期了才向服务器检查内容是否发生改变。

网络代理服务器

代理服务器(Proxy Server)是一种重要的安全功能,它的工作主要在开放系统互联(OSI)模型的对话层,从而起到防火墙的作用。代理服务器大多被用来连接INTERNET(国际互联网)和INTRANET(局域网)。

代理服务器的主要作用是起到代理方和目标方的一个中转的作用。

一般可以利用代理服务器

  • 通过代理访问外网
  • 前端可以通过代理服务器解决跨域问题
  • 数据过滤,代理服务器对经手的数据包做些修改和过滤

当客户机访问目标服务器的时候,首先访问代理服务器,由代理服务器拿到要访问的数据,代理服务器以客户端的角色去访问目标服务器,当目标服务器响应数据后,给到代理服务器,代理服务器再讲数据返回给客户机。

代理的对象是客户端的代理为正向代理。代理的对象是服务器的代理为反向代理。

网络代理相关的头部字段

在一个网络请求经过多次网络代理后,那么 代理服务器将以客户端的角色请求真实服务器。

这时候服务器从请求信息上获取到的ip信息,将是代理服务器的ip,而不是真实ip。

所以反向代理服务器会在 http的请求头上添加一些请求信息。

  • X-Forwarded-For 追加请求的IP地址,每经过一次代理服务器,会将上一个IP地址信息追加到此值上

  • X-Real-IP 记录真实的IP地址信息,既第一个IP信息

  • X-Forwarded-Proto 表示客户端的真实协议,http 还是https

  • Via 追加每一个客户端主机名

除了这些信息,代理服务器还可以设置添加其他的信息。

只有添加了这些信息才能在服务器上获取真实的请求方的信息。