「 面试三板斧 」之 HTTP (上)
前言
HTTP 也是前端面试中常见的考察点, 了解这部分内容十分有必要。
常见的问题有:
为什么说 HTTPS 更安全
HTTP 1/2 有什么区别
304 是什么
option 请求是什么,每次都会发吗
描述一下密钥交换过程
...
这类问题都是 HTTP 相关的问题,十分常见。
下面我们就整体的回顾一下, 内容较多, 分成上下两部分。
今天的主要内容包括:
HTTP 是什么
HTTP 的前世今生
HTTP 的基础特性
基于 HTTP 的组件系统
HTTP 报文组成
HTTP 状态码
GET 和 Post的区别
优化 options 请求 HTTPS
HTTP/2 及 HTTP/3
HTTPS 及其工作流程
为何不所有的网站都使用HTTPS
全称:超文本传输协议
(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 版的主要缺点
每个 TCP 连接只能发送一个请求。发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接。
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 有两种类型的消息:
请求
即:由客户端发送用来触发一个服务器上的动作.
响应
即:来自服务器端的应答。
HTTP 消息由采用 ASCII 编码的多行文本构成的。
在 HTTP/1.1 以及更早的版本中,这些消息通过连接公开的发送。
在 HTTP2.0 中,消息被分到了多个 HTTP 帧中。
通过配置文件(用于代理服务器或者服务器),API(用于浏览器)或者其他接口提供 HTTP 消息。
典型的 HTTP 会话
建立连接
在客户端-服务器协议中,连接是由客户端发起建立的。
在 HTTP 中打开连接意味着在底层传输层启动连接,通常是 TCP。
使用 TCP 时,HTTP 服务器的默认端口号是 80,另外还有 8000 和 8080 也很常用.
发送客户端请求
服务器响应请求
HTTP 请求和响应
HTTP 请求和响应都包括起始行(start line)、请求头(HTTP Headers)、空行(empty line)以及 body 部分,如下图所示:
起始行。
请求的起始行:请求方法、请求 Path 和HTTP 版本号 响应的起始行:HTTP 版本号、响应状态码以及状态文本描述 下面详细说下请求 Path,请求路径(Path)有以下几种:
一个绝对路径,末尾跟上一个 ‘ ? ‘ 和查询字符串。
这是最常见的形式,称为 原始形式 (origin form)。
被 GET
,POST
,HEAD
和 OPTIONS
方法所使用:
POST / HTTP/1.1 GET /background.png HTTP/1.0 HEAD /test.html?query=alibaba HTTP/1.1 OPTIONS /anypage.html HTTP/1.0
一个完整的 URL
主要在使用 GET 方法连接到代理的时候使用:
GET http://developer.mozilla.org/en-US/docs/Web/HTTP/Messages HTTP/1.1
由域名和可选端口(以’:’为前缀)组成的 URL 的 authority component,称为 authority form。
仅在使用 CONNECT 建立 HTTP 隧道时才使用:
CONNECT developer.mozilla.org:80 HTTP/1.1
星号形式
(asterisk form)
一个简单的星号(‘*‘),配合 OPTIONS 方法使用,代表整个服务器:
OPTIONS * HTTP/1.1 Headers 请求头或者响应头。详见下面的首部。不区分大小写的字符串,紧跟着的冒号 (‘:’) 和一个结构取决于 header 的值
空行
。很多人容易忽略
Body
请求 Body 部分:
有些请求将数据发送到服务器以便更新数据:常见的的情况是 POST 请求(包含 HTML 表单数据)。
请求报文的 Body 一般为两类。
一类是通过 Content-Type
和 Content-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 主要是有副作用的,不幂等的情况。
技术上有以下的区分:
缓存:Get 请求能缓存,Post 请求不能
安全:Get 请求没有 Post 请求那么安全,因为请求都在 URL 中。且会被浏览器保存历史纪录。POST 放在请求体中,更加安全
限制:URL 有长度限制,会干预 Get 请求,这个是浏览器决定的
编码: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.如果你觉得本文的内容对你有帮助,就帮我转发一下吧。