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非常多的特性,这里就不一一列举的,重点看看这些头部字段的格式:

        1. 字段名不区分大小写
        2. 字段名不允许出现空格,不可以出现下划线_
        3. 字段名后面必须紧接着:

空行

用来区分开头部实体

问: 如果说在头部中间故意加一个空行会怎么样?

那么空行后的内容全部被视为实体。

实体

就是具体的数据了,也就是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 表示协议名,比如httphttpsfile等等。后面必须和://连在一起。

user:passwd@ 表示登录主机时的用户信息,不过很不安全,不推荐使用,也不常用。

host:port表示主机名和端口。

path表示请求路径,标记资源所在位置。

query表示查询参数,为key=val这种形式,多个键值对之间用&隔开。

fragment表示 URI 所定位的资源内的一个锚点,浏览器可以根据这个锚点跳转到对应的位置。

举个例子:

[mark]https://www.baidu.com/s?wd=HTTP&rsv_spt=1[/mark]

这个 URI 中,httpsscheme部分,www.baidu.comhost:port部分(注意,http 和 https 的默认端口分别为80、443,[mark]baidu.com经过域名解析成对应的ip地址,实际上跳转的是公网ip地址[/mark]),/spath部分,而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功能避免了建立或者重新建立连接

1.png

长连接优缺点

优点

  • 减少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 代理

常见的代理有两种:普通代理(中间人代理)隧道代理

普通代理(中间人代理)

1626169261669.jpg

如图:代理服务器相当于一个中间人,一直帮两边传递东西

它可以在中间可以帮我们过滤、缓存、负载均衡(多台服务器共用一台代理情况下)等一些处理

注意,实际场景中客户端和服务器之间可能有多个代理服务器

隧道代理

客户端通过CONNECT方法请求隧道代理创建一个可以到任意目标服务器和端口号的TCP连接,创建成功之后隧道代理只做请求和响应数据的转发,中间它不会做任何处理

1626191263809.jpg

为什么需要隧道代理呢?

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-HostX-Forwarded-Proto分别记录真实发出请求的客户端的域名协议名

012正向代理和反向代理

正向代理

工作在客户端的代理为正向代理。使用正向代理的时候,需要在客户端配置需要使用的代理服务器,正向代理对服务端透明。比如抓包工具Fiddler、Charles以及访问一些外网网站的代理工具都是正向代理

1626238781500.jpg

正向代理通常用于

  • 缓存
  • 屏蔽某些不健康的网站
  • 通过代理访问原本无法访问的网站
  • 上网认证,对用户访问进行授权

反向代理

工作在服务端的代理称为反向代理。使用反向代理的时候,不需要在客户端进行设置,反向代理对客户端透明。如Nginx就是反向代理

1626239283393.jpg

反向代理通常用于负载均衡服务端缓存流量隔离日志

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-SinceExpires作为缓存标识

HTTP 1.1

引入更多的请求方法类型PUTPATCHDELETEOPTIONS

引入长连接,就是TCP连接默认不关闭,可以被多个请求复用,通过请求头connection:keep-alive设置

强化了缓存管理和控制Cache-ControlETag/If-None-Match

引入管道连接机制,可以在同一TCP连接里,同时发送多个请求

缺点:主要是连接缓慢,服务器只能按顺序响应,如果某个请求花了很长时间,就会出现请求队头阻塞

HTTP 2.0

使用新的二进制协议,不再是纯文本,避免文本歧义,缩小了请求体积

多路复用,同域名下所有通信都是在单链接(双向数据流)完成,提高连接的复用率,在拥塞控制方面有更好的能力提升

HTTP2报头压缩,可以使用HPACK进行头部压缩,HTTP1则不论什么请求都会发送

参考

你应该知道的 https

巩固你的HTTP知识体系

http灵魂之问

《透视 HTTP 协议》——chrono

阅读剩余
THE END