http知识体系
001. HTTP 报文结构是怎样的?
对于 TCP 而言,在传输的时候分为两个部分:TCP头和数据部分。
而 HTTP 类似,也是header + body的结构,具体而言:
[mark]起始行 + 头部 + 空行 + 实体[/mark]
http 请求报文和响应报文是有一定区别,因此我们分开介绍。
起始行
对于请求报文来说,起始行类似下面这样:
[mark]GET /home HTTP/1.1[/mark]
也就是方法 + 路径 + http版本。
对于响应报文来说,起始行一般张这个样:
[mark]HTTP/1.1 200 OK[/mark]
响应报文的起始行也叫做状态行。由http版本、状态码和原因三部分组成。
在起始行中,每两个部分之间用空格隔开,最后一个部分后面应该接一个换行,严格遵循ABNF语法规范。
头部
展示一下请求头和响应头在报文中的位置:
不管是请求头还是响应头,其中的字段是相当多的,而且牵扯到http非常多的特性,这里就不一一列举的,重点看看这些头部字段的格式:
-
-
-
- 字段名不区分大小写
- 字段名不允许出现空格,不可以出现下划线
_ - 字段名后面必须紧接着
:
-
-
空行
用来区分开头部和实体。
问: 如果说在头部中间故意加一个空行会怎么样?
那么空行后的内容全部被视为实体。
实体
就是具体的数据了,也就是body部分。请求报文对应请求体, 响应报文对应响应体。
002. 如何理解 HTTP 的请求方法?
有哪些请求方法?
http/1.1规定了以下请求方法(注意,都是大写):
-
- GET: 通常用来获取资源
- HEAD: 获取资源的元信息
- POST: 提交数据,即上传数据
- PUT: 修改数据
- DELETE: 删除资源(几乎用不到)
- CONNECT: 建立连接隧道,用于代理服务器
GET 和 POST 有什么区别?
首先最直观的是语义上的区别。
而后又有这样一些具体的差别:
-
- 从缓存的角度,GET 请求会被浏览器主动缓存下来,留下历史记录,而 POST 默认不会。
- 从编码的角度,GET 只能进行 URL 编码,只能接收 ASCII 字符,而 POST 没有限制。
- 从参数的角度,GET 一般放在 URL 中,因此不安全,POST 放在请求体中,更适合传输敏感信息。
- 从幂等性的角度,
GET是幂等的,而POST不是。(幂等表示执行相同的操作,结果也是相同的)
003: 如何理解 URI?
URI, 全称为(Uniform Resource Identifier), 也就是统一资源标识符,它的作用很简单,就是区分互联网上不同的资源。
但是,它并不是我们常说的网址, 网址指的是URL, 实际上URI包含了URN和URL两个部分,由于 URL 过于普及,就默认将 URI 视为 URL 了。
URI 的结构
scheme 表示协议名,比如http, https, file等等。后面必须和://连在一起。
user:passwd@ 表示登录主机时的用户信息,不过很不安全,不推荐使用,也不常用。
host:port表示主机名和端口。
path表示请求路径,标记资源所在位置。
query表示查询参数,为key=val这种形式,多个键值对之间用&隔开。
fragment表示 URI 所定位的资源内的一个锚点,浏览器可以根据这个锚点跳转到对应的位置。
举个例子:
[mark]https://www.baidu.com/s?wd=HTTP&rsv_spt=1[/mark]
这个 URI 中,https即scheme部分,www.baidu.com为host:port部分(注意,http 和 https 的默认端口分别为80、443,[mark]baidu.com经过域名解析成对应的ip地址,实际上跳转的是公网ip地址[/mark]),/s为path部分,而wd=HTTP&rsv_spt=1就是query部分。
URI 编码
URI 只能使用ASCII, ASCII 之外的字符是不支持显示的,而且还有一部分符号是界定符,如果不加以处理就会导致解析出错。
因此,URI 引入了编码机制,将所有非 ASCII 码字符和界定符转为十六进制字节值,然后在前面加个%。
如,空格被转义成了%20
‘三元’被转义成了%E4%B8%89%E5%85%83。
004: 如何理解 HTTP 状态码?
RFC 规定 HTTP 的状态码为三位数,被分为五类:
-
- 1xx: 表示目前是协议处理的中间状态,还需要后续操作。
- 2xx: 表示成功状态。
- 3xx: 重定向状态,资源位置发生变动,需要重新请求。
- 4xx: 请求报文有误。
- 5xx: 服务器端发生错误。
1xx
101 Switching Protocols。在HTTP升级为WebSocket的时候,如果服务器同意变更,就会发送状态码 101。
2xx
200 OK是见得最多的成功状态码。通常在响应体中放有数据。
201(Created)作为HTTP POST请求的结果,已在服务器上成功创建了一个或多个新资源
204 No Content含义与 200 相同,但响应头后没有 body 数据。
206 Partial Content顾名思义,表示部分内容,它的使用场景为 HTTP 分块下载和断点续传,当然也会带上相应的响应头字段Content-Range。
3xx
301 Moved Permanently即永久重定向,对应着302 Found,即临时重定向。
比如你的网站从 HTTP 升级到了 HTTPS 了,以前的站点再也不用了,应当返回301,这个时候浏览器默认会做缓存优化,在第二次访问的时候自动访问重定向的那个地址。
而如果只是暂时不可用,那么直接返回302即可,和301不同的是,浏览器并不会做缓存优化。
304 Not Modified: 当协商缓存命中时会返回这个状态码。详见浏览器缓存
4xx
400 Bad Request: 请求有语法错误
403 Forbidden: 这实际上并不是请求报文出错,而是服务器禁止访问,原因有很多,比如法律禁止、信息敏感。
404 Not Found: 资源未找到,表示没在服务器上找到相应的资源。
405 Method Not Allowed: 请求方法不被服务器端允许。
5xx
500 Internal Server Error: 仅仅告诉你服务器出错了,出了啥错咱也不知道。
501 Not Implemented: 表示客户端请求的功能还不支持。
502 Bad Gateway: 服务器自身是正常的,但访问的时候出错了,啥错误咱也不知道。
503 Service Unavailable: 表示服务器当前很忙,暂时无法响应服务。
005: 简要概括一下 HTTP 的特点?HTTP 有哪些缺点?
HTTP 特点
HTTP 的特点概括如下:
[success]特点:无连接、无状态、灵活、简单快速[/success]
1.
- 无连接:每一次请求都要连接一次,请求结束就会断掉,不会保持连接
2.
- 无状态:每一次请求都是独立的,请求结束不会记录连接的任何信息(提起裤子就不认人的意思),减少了网络开销,这
是优点也是缺点,在需要长连接的场景中,需要保存大量的上下文信息,以免传输大量重复的信息,那么这时候无状态就是 http 的缺点了。
3.
- 灵活:通过http协议中头部的
Content-Type标记,可以传输任意数据类型的数据对象(文本、图片、视频等等),非常灵活
4.
- 简单快速:发送请求访问某个资源时,只需传送请求方法和URL就可以了,使用简单,正由于http协议简单,使得http服务器的程序规模小,因而通信速度很快
[success]缺点:无状态、不安全、明文传输、队头阻塞[/success]
1.
- 无状态:请求不会记录任何连接信息,没有记忆,就无法区分多个请求发起者身份是不是同一个客户端的,意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大
2.
- 不安全:
明文传输可能被窃听不安全,缺少身份认证也可能遭遇伪装,还有缺少报文完整性验证可能遭到篡改
3.
- 明文传输:报文(header部分)使用的是明文,直接将信息暴露给了外界,
WIFI陷阱就是复用明文传输的特点,诱导你连上热点,然后疯狂抓取你的流量,从而拿到你的敏感信息
4.
开启长连接(下面有讲)时,只建立一个TCP连接,同一时刻只能处理一个请求,那么当请求耗时过长时,其他请求就只能阻塞状态(如何解决下面有讲)
006: 对 Accept 系列字段了解多少?
对于Accept系列字段的介绍分为四个部分: 数据格式、压缩方式、支持语言和字符集。
数据格式
上一节谈到 HTTP 灵活的特性,它支持非常多的数据格式,那么这么多格式的数据一起到达客户端,客户端怎么知道它的格式呢?
HTTP 从MIME type取了一部分来标记报文 body 部分的数据类型,这些类型体现在Content-Type这个字段,当然这是针对于发送端而言,接收端想要收到特定类型的数据,也可以用Accept字段。
具体而言,这两个字段的取值可以分为下面几类:
text: text/html, text/plain, text/css 等
image: image/gif, image/jpeg, image/png 等
audio/video: audio/mpeg, video/mp4 等
application: application/json, application/javascript, application/pdf, application/octet-stream
压缩方式
当然一般这些数据都是会进行编码压缩的,采取什么样的压缩方式就体现在了发送方的Content-Encoding字段上, 同样的,接收什么样的压缩方式体现在了接受方的Accept-Encoding字段上。这个字段的取值有下面几种:
gzip: 当今最流行的压缩格式
deflate: 另外一种著名的压缩格式
br: 一种专门为 HTTP 发明的压缩算法
Content-Encoding: gzip
// 接收端
Accept-Encoding: gzip
支持语言
对于发送方而言,还有一个Content-Language字段,在需要实现国际化的方案当中,可以用来指定支持的语言,在接受方对应的字段为Accept-Language。如:
// 发送端
Content-Language: zh-CN, zh, en
// 接收端
Accept-Language: zh-CN, zh, en
字符集
最后是一个比较特殊的字段, 在接收端对应为Accept-Charset,指定可以接受的字符集,而在发送端并没有对应的Content-Charset, 而是直接放在了Content-Type中,以charset属性指定。如:
// 发送端
Content-Type: text/html; charset=utf-8
// 接收端
Accept-Charset: charset=utf-8
总结
007: 对于定长和不定长的数据,HTTP 是怎么传输的?
对于定长包体而言,发送端在传输的时候一般会带上 Content-Length, 来指明包体的长度。
不定长包体
上述是针对于定长包体,那么对于不定长包体而言是如何传输的呢?
这里就必须介绍另外一个 http 头部字段了:
Transfer-Encoding: chunked
复制代码
表示分块传输数据,设置这个字段后会自动产生两个效果:
Content-Length 字段会被忽略
基于长连接持续推送动态内容
008:什么是持久链接/长连接
http1.0协议采用的是"请求-应答"模式,当使用普通模式,每个请求/应答客户与服务器都要新建一个连接,完成之后立即断开连接(http协议为无连接的协议)
http1.1版本支持长连接,即请求头添加Connection: Keep-Alive,使用Keep-Alive模式(又称持久连接,连接复用)建立一个TCP连接后使客户端到服务端的连接持续有效,可以发送/接受多个http请求/响应,当出现对服务器的后续请求时,Keep-Alive功能避免了建立或者重新建立连接长连接优缺点
优点
减少CPU及内存的使用,因为不需要经常建立和关闭连接支持管道化的请求及响应模式减少网络堵塞,因为减少了TCP请求减少了后续请求的响应时间,因为不需要等待建立TCP、握手、挥手、关闭TCP的过程- 发生错误时,也
可在不关闭连接的情况下进行错误提示
缺点
一个长连接建立后,如果一直保持连接,对服务器来说是多么的浪费
还有就是可能造成队头堵塞
如何避免长连接资源浪费?
- 客户端请求头声明:
Connection: close,本次通信后就关闭连接
- 服务端配置:如Nginx,设置
keepalive_timeout设置长连接超时时间,keepalive_requests设置长连接请求次数上限
009什么是管线化(管道化)
http1.1在使用长连接的情况下,建立一个连接通道后,连接上消息的传递类似于
[info]请求1 -> 响应1 -> 请求2 -> 响应2 -> 请求3 -> 响应3[/info]
管理化连接的消息就变成了类似这样
[info]请求1 -> 请求2 -> 请求3 -> 响应1 -> 响应2 -> 响应3[/info]
管线化是在同一个TCP连接里发一个请求后不必等其回来就可以继续发请求出去,这可以减少整体的响应时间,但是服务器还是会按照请求的顺序响应请求,所以如果有许多请求,而前面的请求响应很慢,就产生一个著名的问题队头堵塞
010HTTP1.1 如何解决 HTTP 的队头阻塞问题?
http1.0协议采用的是请求-应答模式,报文必须是一发一收,就形成了一个先进先出的串行队列,没有轻重缓急的优先级,只有入队的先后顺序,排在最前面的请求最先处理,就导致如果队首的请求耗时过长,后面的请求就只能处于阻塞状态,这就是著名的队头阻塞
解决如下:
并发连接
因为一个域名允许分配多个长连接,就相当于增加了任务队列,不至于一个队列里的任务阻塞了其他全部任务。
现在的浏览器标准中一个域名并发连接可以有6~8个,记住是6~8个
域名分片
一个域名最多可以并发6~8个,那咱就多来几个域名
比如a.baidu.com,b.baidu.com,c.baidu.com,多准备几个二级域名,当我们访问baidu.com时,可以让不同的资源从不同的二域名中获取,而它们都指向同一台服务器
011说一下 HTTP 代理
常见的代理有两种:普通代理(中间人代理),隧道代理
普通代理(中间人代理)
如图:代理服务器相当于一个中间人,一直帮两边传递东西
它可以在中间可以帮我们过滤、缓存、负载均衡(多台服务器共用一台代理情况下)等一些处理
注意,实际场景中客户端和服务器之间可能有多个代理服务器
隧道代理
客户端通过CONNECT方法请求隧道代理创建一个可以到任意目标服务器和端口号的TCP连接,创建成功之后隧道代理只做请求和响应数据的转发,中间它不会做任何处理
为什么需要隧道代理呢?
https服务是需要网站有证书的,而代理服务器显然没有,所以浏览器和代理之间无法创建TLS,所以就有了隧道代理,它把浏览器的数据原样透传,这样就实现了通过中间代理和服务端进行TLS握手,然后进行加密传输
可能有人会问,那还要代理干嘛,直接请求服务器不是更好吗
代理服务器,到底有什么好处呢?
突破访问限制:如访问一些单位或集团内部资源,或用国外代理服务器(翻墙),就可以上国外网站看片等
安全性更高:上网者可以通过这种方式隐藏自己的IP,免受攻击。还可以对数据过滤,对非法IP限流等
负载均衡:客户端请求先到代理服务器,而代理服务器后面有多少源服务器,IP是多少,客户端是不知道的。因此,代理服务器收到请求后,通过特定的算法(随机算法、轮询、一致性hash、LUR(最近最少使用) 算法这里不细说了)把请求分发给不同的源服务器,让各个源服务器负载尽量均衡
缓存代理:将内容缓存到代理服务器
代理最常见的请求头
Via:
代理服务器需要标明自己的身份,在 HTTP 传输中留下自己的痕迹,怎么办呢?
通过Via字段来记录。举个例子,现在中间有两台代理服务器,在客户端发送请求后会经历这样一个过程:
[info]客户端 -> 代理1 -> 代理2 -> 源服务器[/info]
在源服务器收到请求后,会在请求头拿到这个字段:
[info]Via: proxy_server1, proxy_server2[/info]
而源服务器响应时,最终在客户端会拿到这样的响应头:
[info]Via: proxy_server2, proxy_server1[/info]
可以看到,Via中代理的顺序即为在 HTTP 传输中报文传达的顺序。
X-Forwarded-For
字面意思就是为谁转发, 它记录的是请求方的IP地址(注意,和Via区分开,X-Forwarded-For记录的是请求方这一个IP)。
[info]X-Forwarded-For: client,proxy1,proxy2[/info]
X-Real-IP
一般记录真实发出请求的客户端的IP,还有X-Forwarded-Host和X-Forwarded-Proto分别记录真实发出请求的客户端的域名和协议名
012正向代理和反向代理
正向代理
工作在客户端的代理为正向代理。使用正向代理的时候,需要在客户端配置需要使用的代理服务器,正向代理对服务端透明。比如抓包工具Fiddler、Charles以及访问一些外网网站的代理工具都是正向代理
正向代理通常用于
- 缓存
- 屏蔽某些不健康的网站
- 通过代理访问原本无法访问的网站
- 上网认证,对用户访问进行授权
反向代理
工作在服务端的代理称为反向代理。使用反向代理的时候,不需要在客户端进行设置,反向代理对客户端透明。如Nginx就是反向代理
反向代理通常用于:负载均衡、服务端缓存、流量隔离、日志
013HTTP 缓存及缓存代理
详情可看[link href=https://chun53.top/?p=242]浏览器缓存[/link]
缓存代理就是让代理服务器接管一部分的服务端的http缓存,客户端缓存过期之后就近到代理服务器的缓存中获取,代理缓存过期了才请求源服务器
注意响应头字段
- Cache-Control: 值有
public时,表示可以被所有终端缓存,包括代理服务器、CDN。值有private时,只能被终端浏览器缓存,CDN、代理等中继服务器都不可以缓存。
014HTTP 版本(1.0,1.1,2.0)
HTTP 1.0
任意数据类型都可以发送
有GET、POST、HEAD三种方法
无法复用TCP连接(长连接)
以header中的Last-Modified/If-Modified-Since和Expires作为缓存标识
HTTP 1.1
引入更多的请求方法类型PUT、PATCH、DELETE、OPTIONS
引入长连接,就是TCP连接默认不关闭,可以被多个请求复用,通过请求头connection:keep-alive设置
强化了缓存管理和控制Cache-Control、ETag/If-None-Match
引入管道连接机制,可以在同一TCP连接里,同时发送多个请求
缺点:主要是连接缓慢,服务器只能按顺序响应,如果某个请求花了很长时间,就会出现请求队头阻塞
HTTP 2.0
使用新的二进制协议,不再是纯文本,避免文本歧义,缩小了请求体积
多路复用,同域名下所有通信都是在单链接(双向数据流)完成,提高连接的复用率,在拥塞控制方面有更好的能力提升。
HTTP2报头压缩,可以使用HPACK进行头部压缩,HTTP1则不论什么请求都会发送。
参考