3.2-HTTP协议
Create by fall on 28 Aug 2021 Recently revised in 01 Dec 2023
HTTP 协议
HTTP 协议是互联网上应用最为广泛的一种网络协议,是 TCP/IP 协议的应用层协议,定义了以下内容。
请求格式包括以下格式:请求行,请求头,空行,请求数据
响应也由四个部分组成,分别是:响应行,响应头,空行,响应体
请求头和响应头,下面统称为 首部字段
请求头、响应头都以
\r\n
结尾,并且最后一行加上一个额外的空行\r\n
请求行
请求行:请求方法 + 空格 + URL + 空格 + 版本号 + 回车换行
请求方法
请求方法有以下几种:
- GET:获取资源,用来请求访问已经被 URI 识别的资源(URI 是统一资源标识符,是 URL 父类),且不应该由副作用
- POST:用来传输实体的主体(get也可以实现,但一般不适用get)
- PUT:传输文件,put 方法自身不带验证机制,任何人都可以上传文件,存在安全性问题,都不实用该方法进行上传文件。
- PATCH:修改(put,替换
- HEAD:获得报文首部,同
get
请求,只是不返回报文主体部分 - DELETE:删除文件,同样不带验证机制,存在安全性问题。
- OPTIONS:询问指定的请求
URI
支持那些方法,post 请求时,浏览器会先发一次 options 请求,如果请求通过,则正式发送 post 请求 - TRACE:追踪路径,让Web服务器将之前的请求通信还回给客户端的方法。
- CONNECT:要在请求与代理服务器通信时建立隧道,实现隧道进行TCP通信
最常用的两个就是 get 、post
URL
URL 的构成
┌─────────────────────────────────────────────────────────────────────────────────────────────┐
│ href │
├────────┬──┬─────────────────────┬────────────────────────┬───────────────────────────┬──────┤
│protocol│ │ auth │ host │ path │ hash │
│ │ │ ├─────────────────┬──────┼──────────┬────────────────┤ │
│ │ │ │ hostname │ port │ pathname │ search │ │
│ 协议 │ │ 认证 │ 域名 │ 端口 │ 路径 ├─┬──────────────┤ 哈希 │
│ │ │ │ │ │ │ │ query │ │
" https: // user : pass @ sub.example.com : 8080 /p/a/t/h ? query=string #hash "
│ │ │ │ │ hostname │ port │ │ │ │
│ │ │ │ ├─────────────────┴──────┤ │ │ │
│protocol│ │ username │ password │ host │ │ │ │
├────────┴──┼──────────┴──────────┼────────────────────────┤ │ │ │
│ origin │ │ origin │ pathname │ search │ hash │
├───────────┴─────────────────────┴────────────────────────┴──────────┴────────────────┴──────┤
│ href │
└─────────────────────────────────────────────────────────────────────────────────────────────┘
版本号
就是当前使用的 HTTP 的版本,比如说 HTTP/2
get 和 post
特点
无论是 post
还是 get
请求,都是基于超文本传输协议。
·一般来讲,get
和 post
的特点
特点 | get | post |
---|---|---|
请求参数 | 参数通过URL进行传递,多个参数通过&连接 | 请求参数放在 request body中 |
请求缓存 | get 请求会被缓存 | 请求不会被缓存为历史记录 |
收藏为书签 | 可以 | 不可以 |
安全性 | 比较不安全 | 比较安全 |
浏览器回退 | 参数会被完整地保留在浏览器记录,且无害 | 参数不会被保留,会再次进行请求 |
编码方式 | get 请求只能进行URL编码 | 支持多种编码格式 |
参数的数据类型 | 只接受ASCII字符 | post 没有限制 |
常见误区
- 请求参数长度限制?
HTTP 协议中没有对 URL 长度进行限制,限制是不同的浏览器和服务器有不同规范带来的限制。
限制长度有两方面的考虑,第一,防止恶意传入过多数据对服务器的响应造成压力。将高速服务器 Content-Length 是一个很大的数,却只发送一点数据,让服务器傻等。哪怕有超时设置,还会因为故意的访问超时让服务器挂掉。
因此无论是 post 还是 get,浏览器都会给相应的 URL 长度限制
get
请求不能有 request body 传输数据?
get
可以带 request body 但是不一定能保证被接收到。如果使用 get 时,在 request body
藏了数据,不同的服务器处理方式是不同的,有的服务器会帮你读出数据,而另一些服务器会直接进行忽略。
post
、get
请求都是由 HTTP 协议制定的,使用哪个 method 与应用层的数据如何进行传输,没有相互关系。
HTTP
协议没有要求如果是 post 就将数据放在 body 中,也没有要求如果是 get,统一将数据放在 URL 中。
HTML
中的约定是将这些数据放在相应的位置,但是又不是所有的请求都依赖于 HTML
。
post
比get
安全性更高?
安全性是相对的,通过get
提交的数据都会显示在 URL 上, 页面的 URL 会在历史记录里面,她人查看历史记录,会看到提交的记录,post
不会,get
提交数据可能会造成CSRF攻击。
get
请求时产生一个TCP数据包,post
产生两个数据包?
对于 get
请求而言,浏览器会把 http header
一起发送出去,服务器返回 200
OK
对于 post
而言,浏览器先发送 Header
,服务器先响应 100
,表示 continue
浏览器再发送 data
,服务器响应 200
,但是这种情况虽然 post
会请求两次,body
是紧跟 header
进行发送的,根本不存在等待服务器响应。
导致 post
连续发送两次请求可能原因是 post
可能传输大量的数据,如果服务器没有处理 100
,而是其他的情况,可能导致浪费大量带宽。
一个请求的一生
浏览器中的 HTTP 请求从发起到结束一共经历如下八个阶段:构建请求、查找缓存、准备 IP 和端口、等待 TCP 队列、建立 TCP 连接、发起 HTTP 请求、服务器处理请求、服务器返回请求和断开连接;
- 构建请求:浏览器构建请求行,构建好后,准备发起网络请求;
- 查找缓存:在真正发起请求前浏览器会查询缓存中是否有请求资源副本,有则拦截请求,返回资源副本,否则进入网络请求;
- 准备 IP 地址和端口:HTTP 网络请求需要和服务器建立 TCP 连接,而建立 TCP 连接需要准备 IP 地址和端口号,浏览器需要请求 DNS 返回域名对应的 IP,同时会缓存域名解析结果,供下次查询使用;
- 等待 TCP 队列:Chrome 机制,同一个域名同时最多只能建立 6 个 TCP 连接;
- 建立 TCP 连接:TCP 通过“三次握手”建立连接,传输数据,“四次挥手”断开连接;
- 发送 HTTP 请求:建立 TCP 连接后,浏览器就可以和服务器进行 HTTP 数据传输了,首先会向服务器发送请求行,然后以请求头形式发送一些其他信息,如果是 POST 请求还会发送请求体;
- 服务器处理请求:首先服务器会返回响应行,随后,服务器向浏览器发送响应头和响应体。通常服务器返回数据,就要关闭 TCP 连接,如果请求头或者响应头有 Connection:keep-alive TCP 保持打开状态;
首部字段
比如说下面简单的请求头
Host: www.someschool.edu
Connection: close
User-agent: Mozilla/5.0
Accept-language: fr
Host
表示请求的路径,虽然 URL 已经指明了请求对象的路径了,但这个首部行提供的信息是 Web 代理高速缓存
所需要的。
Connection
示浏览器告诉服务器,使用的是非持久链接,告诉服务器,发送完相应对象后就关闭连接。
User-agent
表示浏览器类型是 Mozilla/5.0
。
Accept-language
表示浏览器想要得到对象的语法版本。
通用标头
通用标头这类标头,可以出现在请求标头和响应标头中
Date
顾名思义,就是用来表示时间,比如:Date:Wed, 21 Oct 2015 07:28:00 GMT
Cache-Control
虽然请求头和响应头都有,但有共有部分,也有独有部分
Cache-Control 的特性分为四大类,可缓存性,过期,重新验证并重新加载,其他特性
对于请求头来说:
请求指令共有部分:
Cache-Control: max-age=<seconds> // 缓存存储的最大周期,超过这个时间被认为是过期
Cache-Control: no-cache // 客户端缓存资源,但是否缓存需要协商缓存进行验证。
Cache-Control: no-store // 不使用任何缓存
Cache-Control: no-transform // 不得对资源进行转换或者转变,不允许代理修改 HTTP 头
Cache-Control: stale-if-error=<seconds> // 表示当有特定响应错误的话,旧的还能再用用(500,502,503,504),其不属于标准文档,使用前请检查兼容性。
独有部分:
Cache-Control: max-stale[=<seconds>] // 表明客户端愿意接收一个已经过期的资源
Cache-Control: min-fresh=<seconds> // 客户端想要一个指定秒数内的最新状态响应
Cache-Control: only-if-cached // 客户端只接受已缓存的响应,如果已经有了缓存,就会重用缓存。
对于响应头来说:
响应指令共有部分:
// 都在上面,向上看↑
独有部分:
Cache-Control: must-revalidate // 一旦过期,缓存不能用该资源,必须先向服务器进行缓存确认,
Cache-Control: max-age=604800, must-revalidate // 通常会一起使用
Cache-Control: proxy-revalidate // 和 must-revalidate 相同,但是只适用于代理服务器
Cache-Control: public // 可以被任何对象(发送请求的客户端,代理服务器)进行缓存
Cache-Control: private // 只能被单个用户进行缓存,不能作为共享缓存(不能被代理服务器缓存)
Cache-Control: s-maxage=<seconds> // 到期时间,会覆盖 max-age 或者 Expires 头,仅适用于共享缓存(各个代理),私有缓存会忽略它
了解更多可以点击这里
如果想了解浏览器缓存可见
浏览器缓存机制
这篇文章。
Connection
表示事务(三次握手,四次挥手)完成后的链接状态
Connection: keep-alive
Connection: close
实体标头
描述正文内容,Content-Length
、Content-language
、ContentEncoding
是实体头。
Content-Length
表示实体主体的大小,以字节为单位,发送到接收方。- 比如:
Content-Length: 1076
- 比如:
Content-Language
表示客户端或者服务器端的能接受的语言- 比如:
Content-Language:en-US
- 比如:
Content-Encoding
表示的压缩媒体类型时使用的格式。- 比如:
Content-Encoding: gzip
响应头 - 比如:
Accept-Encoding: gzip, deflate, br
请求头
- 比如:
请求标头
GET /home.html HTTP/1.1
Host: developer.mozilla.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://developer.mozilla.org/testpage.html
Connection: keep-alive
Upgrade-Insecure-Requests: 1
If-Modified-Since: Mon, 18 Jul 2016 02:36:04 GMT
If-None-Match: "c561c68d0ba92bbeb8b0fff2a9199f722e3a621a"
Cache-Control: max-age=0
上面的请求标头是 MDN 官网的例子,接下来逐个讲解
Host
Host 表明了服务器的域名,可选的服务器监听的 TCP 端口号,如果没有给定端口号,会使用默认端口,默认端口为 80
User-Agent
表明了用户代理,显而易见,使用的是 Mozilla/5.0 火狐内核
Accept、Accept-language、Accept-Encoding 都是内容协商标头,详情在下一小节:内容协商
Referer
告诉服务器该网页是从哪个页面链接过来的
Upgrade-Insecure-Requests
表示客户端优先选择加密及带有身份验证的响应。
If-Modified-Since
条件请求,只有在给定日期之后对内容进行修改,服务器才会返回资源,状态码为 200,如果开始以来没有修改过,会返回 304,并且没有响应体。
就是,浏览器会把资源的最后修改日期(Last-modified)发给你,你收到这个消息,然后保存,再次请求的时候,会把最后修改日期(IF-Modified-Since)发给服务器,问你在这个日期后有没有改变资源,如果没有,就啥都不返回。
If-None-Match
和 If-Modified-Since 逻辑类似,只不过传递的是经过 hash 算法计算过的值,服务器传递过来的是 ETag,作为唯一识别码
If-Modified-Since 和 If-None-Match 都属于协商缓存内容,浏览器缓存详情见 3.1 :-)
内容协商
客户端和服务器端进行响应的交涉,提供给客户端合适的资源,一般以响应资源的语言,字符集,编码方式等作为判断的标准。
- 服务器驱动协商:服务器根据首部字段自动处理
- 客户端驱动协商:由客户端来进行内容的协商
- 透明协商,服务器和客户端各自进行内容协商
Accept:表示能够理解的 MIME 类型
MIME:Multipurpose Internet Mail Extensions,用来描述内容类型,包含文本,图像,音频,视频及其他应用程序专用的数据。
比如说下面的类型
- 文本文件:
text/html
、text/plain
、text/css
- 图片文件:
image/jpeg
、image/gif
、image/png
- 应用程序二进制文件:
application/octet-stream
、application/zip
Accept 除了 添加MIME 之外,还可以添加权重
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
- 逗号 , 用于分割不同的 MIME 类型
- 分号 ; 用于分割类型和权限
- 上面的数据表明 application/xml 的权重为 0.9 并且 */*;q=0.8 的权重为 0.8
- 权重默认是 1
- 权重高的在前,低的在后
Accept-Charset
规定服务器处理表单数据所接受的字符集,指定了数据集,服务器必须支持这些字符集,从而得以正确解释表单中的数据。
该属性的默认值是 unknown
,表示表单的字符集与包含表单的文档的字符集相同。
常用的字符集: UTF-8 - Unicode 字符编码 ; ISO-8859-1 - 拉丁字母表的字符编码;GBK
Accept-Language
接受的语言类型 Accept-Language: en-US,en;q=0.5
响应标头
服务器传给浏览器的内容中,响应标头,就是在响应行后面,响应
200 OK
Access-Control-Allow-Origin: *
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Mon, 18 Jul 2016 16:06:00 GMT
Etag: "c561c68d0ba92bbeb8b0f612a9199f722e3a621a"
Keep-Alive: timeout=5, max=997
Last-Modified: Mon, 18 Jul 2016 02:36:04 GMT
Server: Apache
Set-Cookie: mykey=myvalue; expires=Mon, 17-Jul-2017 16:06:00 GMT; Max-Age=31449600; Path=/; secure
Transfer-Encoding: chunked
Vary: Cookie, Accept-Encoding
x-frame-options: DENY
第一行的内容是服务器状态码,200,表示成功
详情可以查看参考章节,服务器状态码
Access-Control-Allow-Origin
指定一个源,告诉浏览器允许该来源进行资源访问
*
通配符表示允许任何资源访问资源
也可以自定义指定资源
Access-Control-Allow-Origin: https://mozilla.org
Vary: Origin
// 表示允许源 https://mozilla.org 的代码访问资源
// 当指定具体域名时,Vary 用来表明,资源会根据 Origin 的不同,有所变化,看本小节的那么多示例,也说明可以有不同的值
Keep-Alive
表示 Connection 非持续连接的存活时间
Connection: Keep-Alive
Keep-Alive: timeout=5, max=997
timeout 表示空闲连接必须保持打开状态的最短时间,此处表示最大超时时间是 5s
max 关闭连接之前,可以再次连接上发送的最大请求数,最大连接请求是 997 个
Server
服务器标头包含有关原始服务器用来处理请求的软件的信息。
Server: Apache/2.4.1 (Unix)
避免使用过于冗长和详细的 Server 值,因为它们可能会泄露内部实施细节,这可能会使攻击者容易地发现并利用已知的安全漏洞。上面的写法是不正确的。包含了太多信息。
Set-Cookie
Cookie、Set-Cookie、Content-Disposition 不属于 HTTP 1.1
Set-Cookie 是服务端传递的响应头,表明要设置 Cookie,有过期时间的选择
补上:详情可以查看 3.4 章的内容
Transfer-Encoding
表明传输时采用的编码方式
Transfer-Encoding: chunked
// chunked 好像是分块传输什么的
X-Frame-Options
属于 HTTP 响应首部
注:HTTP首部是支持扩展的,所以在浏览器上也会出现各种非标准的首部字段
其他首部字段
非 HTTP/1.1 首部字段
Cookie、Set-Cookie 和 Content-Disposition 等在其他 RFC 中定义的首部字段,这些非正式的首部字段统一归纳在 RFC4229 HTTP Header Field Registrations 中。
HTTP 首部将定义成缓存代理和非缓存代理的行为分为两类。
- End-to-end(端到端) 标头必须发送给最终接受者(请求的服务器,或者是相应的客户端)。中间代理必须传输未经修改的标头,并且缓存必须存储这些信息
- Hop-by-hop(逐跳) 首部只对单次转发有效,会因通过缓存或代理而不再转发。
除了 Connection、Keep-Alive、Proxy-Authenticate、Proxy-Authorization、Trailer、TE、Transfer-Encoding、Upgrade 其余皆为端到端