「 面试三板斧 」之 HTTP (上)

脑洞前端

共 10103字,需浏览 21分钟

 ·

2020-12-31 21:27

前言

HTTP 也是前端面试中常见的考察点, 了解这部分内容十分有必要。

常见的问题有:

  • 为什么说 HTTPS 更安全
  • HTTP 1/2 有什么区别
  • 304 是什么
  • option 请求是什么,每次都会发吗
  • 描述一下密钥交换过程
  • ...

这类问题都是 HTTP 相关的问题,十分常见。

下面我们就整体的回顾一下, 内容较多, 分成上下两部分。

今天的主要内容包括:

  • HTTP 是什么
  • HTTP 的前世今生
  • HTTP 的基础特性
  • 基于 HTTP 的组件系统
  • HTTP 报文组成
  • HTTP 状态码
  • GET 和 Post的区别
  •  优化 options 请求
  • HTTPS
  • HTTP/2 及 HTTP/3
  • HTTPS 及其工作流程
  • 为何不所有的网站都使用HTTPS

HTTP 是什么

全称:超文本传输协议(HyperText Transfer Protocol)

概念:HTTP 是一种能够获取像 HTML、图片等网络资源的通讯协议。

它是在 web 上进行数据交换的基础,是一种 client-server 协议。

HTTP 在因特网的角色:充当一个信使的角色,干的就是一个跑腿的活,在客户端和服务端之间传递信息,但我们又不能缺少它。

HTTP 协议是「 应用层 」协议,是与前端开发最息息相关的协议。

平时我们遇到的 HTTP 请求HTTP 缓存Cookies跨域等其实都跟 HTTP 息息相关。

HTTP 的前世今生

HTTP(HyperText Transfer Protocol)是万维网(World Wide Web)的基础协议。

Tim Berners-Lee 博士和他的团队在1989-1991年间创造出它。

在 1991 年发布了 HTTP 0.9 版,在 1996 年发布 1.0 版,1997 年是 1.1 版,1.1 版也是到今天为止传输最广泛的版本。

2015 年发布了 2.0 版,其极大的优化了 HTTP/1.1 的性能和安全性,而 2018 年发布的 3.0 版,继续优化 HTTP/2,激进地使用 UDP 取代 TCP 协议。

目前,HTTP/3 在 2019 年 9 月 26 日 被 Chrome,Firefox,和 Cloudflare 支持。

HTTP 0.9

单行协议,请求由单行指令构成。以唯一可用的方法 GET 开头。后面跟的是目标资源的路径

GET /mypage.html 响应:只包括响应文档本身

这是一个非常简单的HTML页面 没有响应头,只传输 HTML 文件

没有状态码

HTTP 1.0

RFC 1945 提出了 HTTP1.0,构建更好可拓展性

协议版本信息会随着每个请求发送。

响应状态码

引入了 HTTP 头的概念,无论是请求还是拓展,允许传输元数据。使协议变得灵活,更加具有拓展性

Content-Type 请求头,具备了传输除纯文本 HTML 文件以外其他类型文档的能力。

在响应中,Content-Type 标头告诉客户端实际返回的内容的内容类型

媒体类型是一种标准。用来表示文档、文件或者字节流的性质和格式。

浏览器通常使用 MIME (Multipurpose Internet Mail Extensions )类型来确定如何处理 URL,因此 Web 服务器在响应头中配置正确的 MIME 类型会非常的重要。

如果配置不正确,可能会导致网站无法正常的工作。

MIME 的组成结构非常简单: 由类型与子类型两个字符串中间用’/‘分隔而组成。

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

同时为了约定请求的数据和响应数据的压缩方式、支持语言、字符集等,还提出了以下的 Header:

1.压缩方式:

发送端:Content-Encoding(服务端告知客户端,服务器对实体的主体部分的编码方式)接收端:Accept-Encoding(用户代理支持的编码方式)

  • gzip: 当今最流行的压缩格式;
  • deflate: 另外一种著名的压缩格式, 一种专门为 HTTP 发明的压缩算法。

2.支持语言

Content-Language 和 Accept-Language(用户代理支持的自然语言集)

3.字符集

发送端:Content-Type 中,以 charset 属性指定。接收端:Accept-Charset(用户代理支持的字符集)。

// 发送端
Content-Encoding: gzip
Content-Language: zh-CN, zh, en
Content-Type: text/html; charset=utf-8

// 接收端
Accept-Encoding: gzip
Accept-Language: zh-CN, zh, en
Accept-Charset: charset=utf-8

虽然 HTTP1.0 在 HTTP 0.9 的基础上改进了很多,但还是存在这不少的缺点.

HTTP/1.0 版的主要缺点

  1. 每个 TCP 连接只能发送一个请求。发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接。

  2. TCP 连接的新建成本很高,因为需要客户端和服务器三次握手,并且开始时发送速率较慢(slow start)。

HTTP 最早期的模型,也是 HTTP/1.0 的默认模型,是短连接

每一个 HTTP 请求都由它自己独立的连接完成;

这意味着发起每一个 HTTP 请求之前, 都会有一次 TCP 握手,而且是连续不断的。

HTTP 1.1

HTTP/1.1 在1997年1月以 RFC 2068 文件发布。

HTTP 1.1 消除了大量歧义内容并引入了多项技术

1. 连接可以复用

2. 长连接:connection: keep-alive。

HTTP 1.1 支持长连接(PersistentConnection),在一个 TCP 连接上可以传送多个 HTTP 请求和响应,减少了建立和关闭连接的消耗和延迟。

在 HTTP1.1 中默认开启 Connection:keep-alive,一定程度上弥补了 HTTP1.0 每次请求都要创建连接的缺点。

3. 增加了管道化技术

允许在第一个应答被完全发送完成之前就发送第二个请求,以降低通信延迟

复用同一个 TCP 连接期间,即便是通过管道同时发送了多个请求,服务端也是按请求的顺序依次给出响应的;

而客户端在未收到之前所发出所有请求的响应之前,将会阻塞后面的请求(排队等待),这称为队头堵塞(Head-of-line blocking)。

4. 支持响应分块

分块编码传输:Transfer-Encoding: chunked

Content-length 声明本次响应的数据长度。

keep-alive 连接可以先后传送多个响应,因此用 Content-length 来区分数据包是属于哪一个响应。

使用 Content-Length 字段的前提条件是,服务器发送响应之前,必须知道响应的数据长度。

对于一些很耗时的动态操作来说,这意味着,服务器要等到所有操作完成,才能发送数据,显然这样的效率不高。

更好的处理方法是,产生一块数据,就发送一块,采用”流模式”(Stream)取代”缓存模式”(Buffer)。

因此,HTTP 1.1 规定可以不使用 Content-Length 字段,而使用”分块传输编码”(Chunked Transfer Encoding)。

只要请求或响应的头信息有 Transfer-Encoding: chunked 字段,就表明 body 将可能由数量未定的多个数据块组成。

每个数据块之前会有一行包含一个 16 进制数值,表示这个块的长度;

最后一个大小为 0 的块,就表示本次响应的数据发送完了。

5. 引入额外的缓存控制机制。

在 HTTP1.0 中主要使用 header 里的 If-Modified-Since,Expires 等来做为缓存判断的标准.

HTTP1.1 则引入了更多的缓存控制策略例, 如 Entity tag, If-None-Match,Cache-Control 等更多可供选择的缓存头控制缓存策略

6. Host 头

不同的域名配置同一个 IP 地址的服务器。

Host 是 HTTP 1.1 协议中新增的一个请求头,主要用来实现虚拟主机技术

虚拟主机(virtual hosting)即共享主机(shared web hosting),可以利用虚拟技术把一台完整的服务器分成若干个主机,因此可以在单一主机上运行多个网站或服务。

举个栗子,有一台 ip 地址为 61.135.169.125 的服务器,在这台服务器上部署着谷歌、百度、淘宝的网站。

为什么我们访问 https://www.google.com 时,看到的是 Google 的首页, 而不是百度或者淘宝的首页?

原因就是: Host 请求头决定着访问哪个虚拟主机

HTTP 的基础特性

可拓展协议。

HTTP 1.0 出现的 HTTP headers 让协议拓展变得更加的容易。

只要服务端和客户端就 headers 达成语义一致,新功能就可以被轻松的加入进来。

HTTP 是无状态的、有会话的。

在同一个连接中,两个执行成功的 HTTP 请求之间是没有关系的。

这就带来了一个问题,用户没有办法在同一个网站中进行连续的交互,比如在一个电商网站里,用户把某个商品加入到购物车,切换一个页面后再次添加了商品,这两次添加商品的请求之间没有关联,浏览器无法知道用户最终选择了哪些商品。

而使用 HTTP 的头部扩展,HTTP Cookies 就可以解决这个问题。把 Cookies 添加到头部中,创建一个会话让每次请求都能共享相同的上下文信息,达成相同的状态。

HTTP 与连接。通过 TCP,或者 TLS——加密的 TCP 连接来发送,理论上任何可靠的传输协议都可以使用。连接是传输层控制的,这从根本上来讲不是 HTTP 的范畴。

也就是说,HTTP 依赖于面向连接的 TCP 进行消息传递,但连接并不是必须的。

只需要它是可靠的,或不丢失消息的(至少返回错误)。

HTTP/1.0 默认为每一对 HTTP 请求/响应都打开一个单独的 TCP 连接。

当需要连续发起多个请求时,这种模式比多个请求共享同一个 TCP 链接更低效。

为此,HTTP 1.1 持久连接的概念,底层 TCP 连接可以通过 connection 头部实现。

但 HTTP 1.1 在连接上也是不完美的,后面我们会提到。

基于 HTTP 的组件系统

HTTP 的组件系统包括客户端、web 服务器和代理

1. 客户端:user-agent

浏览器,特殊比如是工程师使用的程序,以及 Web 开发人员调试应用程序。

2. Web服务端

由 Web Server 来服务并提供客户端所请求的文档。

每一个发送到服务器的请求,都会被服务器处理并返回一个消息,也就是 response。

3. 代理(Proxy)

在浏览器和服务器之间,有很多计算机和其他设备转发了 HTTP 消息。

它们可能出现在传输层、网络层和物理层上,对于 HTTP 应用层而言就是透明的 有如下的一些作用:

  • 缓存
  • 过滤(像防病毒扫描、家长控制)
  • 负载均衡
  • 认证(对不同的资源进行权限控制)
  • 日志管理

HTTP 报文组成

HTTP 有两种类型的消息:

  1. 请求

即:由客户端发送用来触发一个服务器上的动作.

  1. 响应

即:来自服务器端的应答。

HTTP 消息由采用 ASCII 编码的多行文本构成的。

在 HTTP/1.1 以及更早的版本中,这些消息通过连接公开的发送。

在 HTTP2.0 中,消息被分到了多个 HTTP 帧中。

通过配置文件(用于代理服务器或者服务器),API(用于浏览器)或者其他接口提供 HTTP 消息。

典型的 HTTP 会话

  1. 建立连接

在客户端-服务器协议中,连接是由客户端发起建立的。

在 HTTP 中打开连接意味着在底层传输层启动连接,通常是 TCP。

使用 TCP 时,HTTP 服务器的默认端口号是 80,另外还有 8000 和 8080 也很常用.

  1. 发送客户端请求

  2. 服务器响应请求

HTTP 请求和响应

HTTP 请求和响应都包括起始行(start line)、请求头(HTTP Headers)、空行(empty line)以及 body 部分,如下图所示:

  • 起始行。

请求的起始行:请求方法、请求 Path 和HTTP 版本号 响应的起始行:HTTP 版本号、响应状态码以及状态文本描述 下面详细说下请求 Path,请求路径(Path)有以下几种:

  1. 一个绝对路径,末尾跟上一个 ‘ ? ‘ 和查询字符串。

这是最常见的形式,称为 原始形式 (origin form)。

GETPOSTHEADOPTIONS 方法所使用:

POST / HTTP/1.1 GET /background.png HTTP/1.0 HEAD /test.html?query=alibaba HTTP/1.1 OPTIONS /anypage.html HTTP/1.0

  1. 一个完整的 URL

主要在使用 GET 方法连接到代理的时候使用:

GET http://developer.mozilla.org/en-US/docs/Web/HTTP/Messages HTTP/1.1

  1. 由域名和可选端口(以’:’为前缀)组成的 URL 的 authority component,称为 authority form。

仅在使用 CONNECT 建立 HTTP 隧道时才使用:

CONNECT developer.mozilla.org:80 HTTP/1.1

  1. 星号形式 (asterisk form)

一个简单的星号(‘*‘),配合 OPTIONS 方法使用,代表整个服务器:

OPTIONS * HTTP/1.1 Headers 请求头或者响应头。详见下面的首部。不区分大小写的字符串,紧跟着的冒号 (‘:’) 和一个结构取决于 header 的值

  1. 空行。很多人容易忽略

Body

请求 Body 部分:

有些请求将数据发送到服务器以便更新数据:常见的的情况是 POST 请求(包含 HTML 表单数据)。

请求报文的 Body 一般为两类。

一类是通过 Content-TypeContent-Length 定义的单文件 body。

另外一类是由多Body 组成,通常是和 HTML Form 联系在一起的。

两者的不同表现在于 Content-Type 的值。

1)Content-Type —— application/x-www-form-urlencoded

对于 application/x-www-form-urlencoded 格式的表单内容。

有以下特点:

I. 其中的数据会被编码成以&分隔的键值对

II. 字符以URL编码方式编码。

转换过程: {a: 1, b: 2} -> a=1&b=2 -> 如下(最终形式) "a%3D1%26b%3D2"

2)Content-Type —— multipart/form-data

请求头中的 Content-Type 字段会包含 boundary,且 boundary 的值有浏览器默认指定。

例:

Content-Type: multipart/form-data;boundary=----WebkitFormBoundaryRRJKeWfHPGrS4LKe。

数据会分为多个部分,每两个部分之间通过分隔符来分隔,每部分表述均有 HTTP 头部描述子包体,如Content-Type,在最后的分隔符会加上—表示结束。

Content-Disposition: form-data;name="data1";
Content-Type: text/plain
data1
----WebkitFormBoundaryRRJKeWfHPGrS4LKe
Content-Disposition: form-data;name="data2";
Content-Type: text/plain
data2
----WebkitFormBoundaryRRJKeWfHPGrS4LKe--

响应 Body 部分:

1)由已知长度的单个文件组成。该类型 body 由两个 header 定义:Content-Type 和 Content-Length

2)由未知长度的单个文件组成,通过将 Transfer-Encoding 设置为 chunked 来使用 chunks 编码。关于 Content-Length 在下面 HTTP 1.0 中会提到,这个是 HTTP 1.0 中新增的非常重要的头部。

方法

安全方法:HTTP 定义了一组被称为安全方法的方法。

GET 方法和 HEAD 方法都被认为是安全的,这意味着 GET 方法和 HEAD 方法都不会产生什么动作 —— HTTP 请求不会再服务端产生什么结果,但这并不意味着什么动作都没发生,其实这更多的是 web 开发者决定的.

  • GET:请求服务器发送某个资源

  • HEAD:跟 GET 方法类似,但服务器在响应中只返回了首部。不会返回实体的主体部分。

  • PUT:向服务器中写入文档。语义:用请求的主体部分来创建一个由所请求的 URL 命名的新文档

  • POST:用来向服务器中输入数据的。通常我们提交表单数据给服务器。【POST 用于向服务器发送数据,PUT 方法用于向服务器上的资源(例如文件)中存储数据】

  • TRACE:主要用于诊断。实现沿通向目标资源的路径的消息环回(loop-back)测试 ,提供了一种实用的 debug 机制。

  • OPTIONS:请求 WEB 服务器告知其支持的各种功能。可以询问服务器支持哪些方法。或者针对某些特殊资源支持哪些方法。

  • DELETE:请求服务器删除请求 URL 中指定的的资源

关于 options 请求

从很多资料我们可以了解到使用OPTIONS方法对服务器发起请求,可以检测服务器支持哪些 HTTP 方法。

但是这次我们并没有主动去发起 OPTIONS 请求,那OPTIONS请求为何会自动发起 ?

规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。

所以这个跨域请求触发了浏览器自动发起OPTIONS请求,看看此次跨域请求具体触发了哪些条件。

由于修改了Content-Type为application/json,触发了CORS预检请求。

优化OPTIONS请求:Access-Control-Max-Age 或者 避免触发

可见一旦达到触发条件,跨域请求便会一直发送2次请求,这样增加的请求数是否可优化呢?答案是可以,OPTIONS预检请求的结果可以被缓存。

Access-Control-Max-Age这个响应首部表示 preflight request (预检请求)的返回结果(即 Access-Control-Allow-Methods 和Access-Control-Allow-Headers 提供的信息) 可以被缓存的最长时间,单位是秒。(MDN)

如果值为 -1,则表示禁用缓存,每一次请求都需要提供预检请求,即用OPTIONS请求进行检测。

在其他场景,比如跨域并且业务有自定义请求头的话就很难避免了。

现在使用的axios或者superagent等第三方ajax插件,如果出现CORS预检请求,可以看看默认配置或者二次封装是否规范。

GET 和 POST 的区别

首先要了解下副作用幂等的概念,副作用指的是: 对服务器端资源做修改

幂等, 指发送 M 和 N 次请求(两者不相同且都大于 1),服务器上资源的状态一致

应用场景上:

  • get 是无副作用的,幂等的。

  • post 主要是有副作用的,不幂等的情况。

技术上有以下的区分:

  1. 缓存:Get 请求能缓存,Post 请求不能

  2. 安全:Get 请求没有 Post 请求那么安全,因为请求都在 URL 中。且会被浏览器保存历史纪录。POST 放在请求体中,更加安全

  3. 限制:URL 有长度限制,会干预 Get 请求,这个是浏览器决定的

  4. 编码:GET 请求只能进行 URL 编码,只能接收 ASCII 字符,而 POST 没有限制。POST 支持更多的编码类型,而且不对数据类型做限制。

从 TCP 的角度,GET 请求会把请求报文一次性发出去,而 POST 会分为两个 TCP 数据包,首先发 header 部分,如果服务器响应 100(continue), 然后发 body 部分。(火狐浏览器除外,它的 POST 请求只发一个 TCP 包)

HTTP 状态码

100~199——信息性状态码

101 Switching Protocols。在HTTP升级为WebSocket的时候,如果服务器同意变更,就会发送状态码 101。

200~299——成功状态码

200 OK,表示从客户端发来的请求在服务器端被正确处理

204 No content,表示请求成功,但响应报文不含实体的主体部分

205 Reset Content,表示请求成功,但响应报文不含实体的主体部分,但是与 204 响应不同在于要求请求方重置内容

206 Partial Content,进行范围请求

300~399——重定向状态码

301 moved permanently,永久性重定向,表示资源已被分配了新的 URL

302 found,临时性重定向,表示资源临时被分配了新的 URL

303 see other,表示资源存在着另一个 URL,应使用 GET 方法获取资源

304 not modified,表示服务器允许访问资源,但因发生请求未满足条件的情况

307 temporary redirect,临时重定向,和302含义类似,但是期望客户端保持请求方法不变向新的地址发出请求

400~499——客户端错误状态码

400 bad request,请求报文存在语法错误

401 unauthorized,表示发送的请求需要有通过 HTTP 认证的认证信息

403 forbidden,表示对请求资源的访问被服务器拒绝

404 not found,表示在服务器上没有找到请求的资源

500~599——服务器错误状态码

500 internal sever error,表示服务器端在执行请求时发生了错误

501 Not Implemented,表示服务器不支持当前请求所需要的某个功能

503 service unavailable,表明服务器暂时处于超负载或正在停机维护,无法处理请求

HTTP 首部

HTTP Headers

1.通用首部(General headers)同时适用于请求和响应消息,但与最终消息主体中传输的数据无关的消息头。如 Date。

2.请求首部(Request headers)包含更多有关要获取的资源或客户端本身信息的消息头。

User-Agent

3.响应首部(Response headers)包含有关响应的补充信息

4.实体首部(Entity headers)含有关实体主体的更多信息,比如主体长(Content-Length)度或其 MIME 类型。如 Accept-Ranges。

详细的 Header 见 HTTP Headers 集合:

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers


未完待续, 见下篇。

爱心三连击

1.看到这里了就点个在看支持下吧,你的在看是我创作的动力。

2.关注公众号脑洞前端,获取更多前端硬核文章!加个星标,不错过每一条成长的机会。

3.如果你觉得本文的内容对你有帮助,就帮我转发一下吧。



浏览 59
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报