这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
HTTP 是什么
全称:超文本传输协议(HyperText Transfer Protocol)。
概念:HTTP 是一种能够获取像 HTML、图片等网络资源的通讯协议(protocol)。它是在 web 上进行数据交换的基础,是一种 client-server 协议。
HTTP——因特网的多媒体信使 ——《HTTP权威指南》。HTTP 在因特网的角色:充当一个信使的角色,干的就是一个跑腿的活,在客户端和服务端之间传递信息,但我们又不能缺少它。HTTP 协议是应用层的协议,是与前端开发最息息相关的协议。平时我们遇到的 HTTP 请求、 HTTP 缓存、Cookies、跨域等其实都跟 HTTP 息息相关。
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 服务器和代理。
客户端:user-agent
浏览器,特殊比如是工程师使用的程序,以及 Web 开发人员调试应用程序。
Web服务端
由 Web Server 来服务并提供客户端所请求的文档。每一个发送到服务器的请求,都会被服务器处理并返回一个消息,也就是 response。
代理(Proxies)
在浏览器和服务器之间,有很多计算机和其他设备转发了 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)有以下几种:
1)一个绝对路径,末尾跟上一个 ' ? ' 和查询字符串。这是最常见的形式,称为 原始形式 (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
1.2.3.4.
2)一个完整的 URL。主要在使用 GET 方法连接到代理的时候使用。
复制
GET http://developer.mozilla.org/en-US/docs/Web/HTTP/Messages HTTP/1.1
1.
3)由域名和可选端口(以':'为前缀)组成的 URL 的 authority component,称为 authority form。仅在使用 CONNECT 建立 HTTP 隧道时才使用。
CONNECT developer.mozilla.org:80 HTTP/1.1
1.
4)星号形式 (asterisk form),一个简单的星号('*'),配合 OPTIONS 方法使用,代表整个服务器。
OPTIONS * HTTP/1.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"
1.2.
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--
1.2.3.4.5.6.7.8.
响应 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 中指定的的资源。
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 包)。
状态码
- 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 Headers
1.通用首部(General headers)同时适用于请求和响应消息,但与最终消息主体中传输的数据无关的消息头。如 Date。
2.请求首部(Request headers)包含更多有关要获取的资源或客户端本身信息的消息头。如 User-Agent。
3.响应首部(Response headers)包含有关响应的补充信息。
4.实体首部(Entity headers)含有关实体主体的更多信息,比如主体长(Content-Length)度或其 MIME 类型。如 Accept-Ranges。
详细的 Header 见 HTTP Headers 集合[2]。
HTTPS
HTTPS 也是通过 HTTP 协议进行传输信息,但是采用了 TLS 协议进行了加密。
对称加密和非对称加密
对称加密就是两边拥有相同的秘钥,两边都知道如何将密文加密解密。但是因为传输数据都是走的网络,如果将秘钥通过网络的方式传递的话,一旦秘钥被截获就没有加密的意义的。
非对称加密
公钥大家都知道,可以用公钥加密数据。但解密数据必须使用私钥,私钥掌握在颁发公钥的一方。首先服务端将公钥发布出去,那么客户端是知道公钥的。然后客户端创建一个秘钥,并使用公钥加密,发送给服务端。服务端接收到密文以后通过私钥解密出正确的秘钥。
TLS 握手过程
TLS 握手的过程采用的是非对称加密
- Client Hello: 客户端发送一个随机值(Random1)以及需要的协议和加密方式。
- Server Hello 以及Certificate: 服务端收到客户端的随机值,自己也产生一个随机值(Random2),并根据客户端需求的协议和加密方式来使用对应的方式,并且发送自己的证书(如果需要验证客户端证书需要说明)。
- Certificate Verify: 客户端收到服务端的证书并验证是否有效,验证通过会再生成一个随机值(Random3),通过服务端证书的公钥去加密这个随机值并发送给服务端,如果服务端需要验证客户端证书的话会附带证书。
- Server 生成 secret: 服务端收到加密过的随机值并使用私钥解密获得第三个随机值(Random3),这时候两端都拥有了三个随机值,可以通过这三个随机值按照之前约定的加密方式生成密钥,接下来的通信就可以通过该密钥来加密解密。
HTTP 缓存
强缓存
强缓存主要是由 Cache-control 和 Expires 两个 Header 决定的。
Expires 的值和头里面的 Date 属性的值来判断是否缓存还有效。Expires 是 Web 服务器响应消息头字段,在响应 http 请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。Expires 的一个缺点就是,返回的到期时间是服务器端的时间,这是一个绝对的时间,这样存在一个问题,如果客户端的时间与服务器的时间相差很大(比如时钟不同步,或者跨时区),那么误差就很大。
Cache-Control 指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。但是其设置的是一个相对时间。
指定过期时间:max-age 是距离请求发起的时间的秒数,比如下面指的是距离发起请求 31536000S 内都可以命中强缓存。
协商缓存
- If-Modified-Since——Last-Modified
Last-Modified 表示本地文件最后修改日期,浏览器会在 request header 加上 If-Modified-Since(上次返回的 Last-Modified 的值),询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来。
但是如果在本地打开缓存文件,就会造成 Last-Modified 被修改,所以在 HTTP / 1.1 出现了 ETag。
- If-none-match——ETags
Etag 就像一个指纹,资源变化都会导致 ETag 变化,跟最后修改时间没有关系,ETag 可以保证每一个资源是唯一的。If-None-Match 的 header 会将上次返回的 Etag 发送给服务器,询问该资源的 Etag 是否有更新,有变动就会发送新的资源回来。
If-none-match、ETags 优先级高于 If-Modified-Since、Last-Modified。
第一次请求:
第二次请求相同网页:
协商缓存,假如没有改动的话,返回 304 ,改动了返回 200 资源
- 200:强缓存Expires/Cache-Control 失效时,返回新的资源文件。
- 200(from cache): 强缓Expires/Cache-Control 两者都存在,未过期,Cache-Control 优先Expires 时,浏览器从本地获取资源成功。
- 304(Not Modified):协商缓存Last-modified/Etag 没有过期时,服务端返回状态码304。
现在的200(from cache)已经变成了 disk cache(磁盘缓存)和 memory cache(内存缓存)两种
revving 技术
上面提到 HTTP 缓存相关,但是很多有时候,我们希望上线之后需要更新线上资源。
web 开发者发明了一种被 Steve Souders 称之为 revving 的技术。不频繁更新的文件会使用特定的命名方式:在 URL 后面(通常是文件名后面)会加上版本号。
弊端:更新了版本号,所有引用这些的资源的地方的版本号都要改变。
web 开发者们通常会采用自动化构建工具在实际工作中完成这些琐碎的工作。当低频更新的资源(js/css)变动了,只用在高频变动的资源文件(html)里做入口的改动。
Cookies
HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。
创建 cookie
Set-Cookie 响应头部和 Cookie 请求头部。
复制
Set-Cookie: <cookie名>=<cookie值>
1.
会话期Cookie
会话期Cookie是最简单的 Cookie:浏览器关闭之后它会被自动删除,也就是说它仅在会话期内有效。会话期 Cookie 不需要指定过期时间(Expires)或者有效期(Max-Age)。需要注意的是,有些浏览器提供了会话恢复功能,这种情况下即使关闭了浏览器,会话期 Cookie 也会被保留下来,就好像浏览器从来没有关闭一样。
持久性Cookie
和关闭浏览器便失效的会话期 Cookie 不同,持久性 Cookie 可以指定一个特定的过期时间(Expires)或有效期(Max-Age)。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
1.
Cookie的Secure和HttpOnly 标记
标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端。
标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端。但即便设置了 Secure 标记,敏感信息也不应该通过 Cookie 传输,因为 Cookie 有其固有的不安全性,Secure 标记也无法提供确实的安全保障。
通过 JavaScript 的 Document.cookie API 是无法访问带有 HttpOnly 标记的 cookie。这么做是为了避免跨域脚本攻击(XSS)。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
1.
Cookie的作用域
Domain 和 Path 标识定义了 Cookie 的作用域:即 Cookie 应该发送给哪些 URL。
Domain 标识指定了哪些主机可以接受 Cookie。如果不指定,默认为当前的主机(不包含子域名)。如果指定了 Domain,则一般包含子域名。
例如,如果设置 Domain=mozilla.org,则 Cookie 也包含在子域名中(如developer.mozilla.org)。
Path 标识指定了主机下的哪些路径可以接受 Cookie(该 URL 路径必须存在于请求 URL 中)。以字符 %x2F ("/") 作为路径分隔符,子路径也会被匹配。
例如,设置 Path=/docs,则以下地址都会匹配:
/docs
/docs/Web/
/docs/Web/HTTP
1.2.3.
SameSite Cookies
SameSite Cookie 允许服务器要求某个 cookie 在跨站请求时不会被发送,从而可以阻止跨站请求伪造攻击。
None 浏览器会在同站请求、跨站请求下继续发送cookies,不区分大小写。【旧版本chrome 默认Chrome 80 版本之前】。
Strict 浏览器将只在访问相同站点时发送cookie。
Lax 将会为一些跨站子请求保留,如图片加载或者frames 的调用,但只有当用户从外部站点导航到URL 时才会发送。