带你了解HTTP黑科技
这篇文章主要讲解了“带你了解HTTP黑科技”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“带你了解HTTP黑科技”吧!
HTTP 内容协商
什么是内容协商
在 HTTP 中,内容协商是一种用于在同一 URL 上提供资源的不同表示形式的机制。内容协商机制是指客户端和服务器端就响应的资源内容进行交涉,然后提供给客户端最为适合的资源。内容协商会以响应资源的语言、字符集、编码方式等作为判断的标准。
内容协商的种类
内容协商主要有以下3种类型:
服务器驱动协商(Server-driven Negotiation)
这种协商方式是由服务器端进行内容协商。服务器端会根据请求首部字段进行自动处理
客户端驱动协商(Agent-driven Negotiation)
这种协商方式是由客户端来进行内容协商。
透明协商(Transparent Negotiation)
是服务器驱动和客户端驱动的结合体,是由服务器端和客户端各自进行内容协商的一种方法。
内容协商的分类有很多种,主要的几种类型是 Accept、Accept-Charset、Accept-Encoding、Accept-Language、Content-Language。
一般来说,客户端用 Accept 头告诉服务器希望接收什么样的数据,而服务器用 Content 头告诉客户端实际发送了什么样的数据。
为什么需要内容协商
我们为什么需要内容协商呢?在回答这个问题前我们先来看一下 TCP 和 HTTP 的不同。
在 TCP / IP 协议栈里,传输数据基本上都是 header+body 的格式。但 TCP、UDP 因为是传输层的协议,它们不会关心 body 数据是什么,只要把数据发送到对方就算是完成了任务。
而 HTTP 协议则不同,它是应用层的协议,数据到达之后需要告诉应用程序这是什么数据。当然不告诉应用这是哪种类型的数据,应用也可以通过不断尝试来判断,但这种方式无疑十分低效,而且有很大几率会检查不出来文件类型。
所以鉴于此,浏览器和服务器需要就数据的传输达成一致,浏览器需要告诉服务器自己希望能够接收什么样的数据,需要什么样的压缩格式,什么语言,哪种字符集等;而服务器需要告诉客户端自己能够提供的服务是什么。
所以我们就引出了内容协商的几种概念,下面依次来进行探讨
内容协商标头
Accept
接受请求 HTTP 标头会通告客户端自己能够接受的 MIME 类型
那么什么是 MIME 类型呢?在回答这个问题前你应该先了解一下什么是 MIME
MIME: MIME (Multipurpose Internet Mail Extensions) 是描述消息内容类型的因特网标准。MIME 消息能包含文本、图像、音频、视频以及其他应用程序专用的数据。
也就是说,MIME 类型其实就是一系列消息内容类型的集合。那么 MIME 类型都有哪些呢?
文本文件: text/html、text/plain、text/css、application/xhtml+xml、application/xml
图片文件: image/jpeg、image/gif、image/png
视频文件: video/mpeg、video/quicktime
应用程序二进制文件: application/octet-stream、application/zip
比如,如果浏览器不支持 PNG 图片的显示,那 Accept 就不指定image/png,而指定可处理的 image/gif 和 image/jpeg 等图片类型。
一般 MIME 类型也会和 q 这个属性一起使用,q 是什么?q 表示的是权重,来看一个例子
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,**;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Connection: keep-alive Origin: https://foo.example
注意请求的标头 Origin ,它表明调用来自于 https://foo.example。让我们看看服务器是如何响应的
HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 00:23:53 GMT Server: Apache/2 Access-Control-Allow-Origin: * Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: application/xml […XML Data…]
服务端发送 Access-Control-Allow-Origin 作为响应。使用 Origin 标头和 Access-Control-Allow-Origin 展示了最简单的访问控制协议。在这个事例中,服务端使用 Access-Control-Allow-Origin 作为响应,也就说明该资源可以被任何域访问。
如果位于https://bar.other的资源所有者希望将对资源的访问限制为仅来自https://foo.example的请求,他们应该发送如下响应
Access-Control-Allow-Origin: https://foo.example
现在除了 https://foo.example 之外的任何域都无法以跨域方式访问到 https://bar.other 的资源。
预检请求
和上面探讨的简单请求不同,预检请求首先通过 OPTIONS 方法向另一个域上的资源发送 HTTP 请求,用来确定实际请求是否可以安全的发送。跨站点这样被预检,因为它们可能会影响用户数据。
下面是一个预检事例
const xhr = new XMLHttpRequest(); xhr.open('POST', 'https://bar.other/resources/post-here/'); xhr.setRequestHeader('X-PINGOTHER', 'pingpong'); xhr.setRequestHeader('Content-Type', 'application/xml'); xhr.onreadystatechange = handler; xhr.send('<person><name>Arun</name></person>');
上面的事例创建了一个 XML 请求体用来和 POST 请求一起发送。此外,设置了非标准请求头 X-PINGOTHER ,这个标头不是 HTTP/1.1 的一部分,但通常对 Web 程序很有用。由于请求的 Content-Type 使用 application/xml,并且设置了自定义标头,因此该请求被预检。如下图所示
如下所述,实际的 POST 请求不包含 Access-Control-Request- * 标头;只有 OPTIONS 请求才需要它们。
下面我们来看一下完整的客户端/服务器交互,首先是预检请求/响应
OPTIONS /resources/post-here/ HTTP/1.1 Host: bar.other User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,**;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Connection: keep-alive X-PINGOTHER: pingpong Content-Type: text/xml; charset=UTF-8 Referer: https://foo.example/examples/preflightInvocation.html Content-Length: 55 Origin: https://foo.example Pragma: no-cache Cache-Control: no-cache <person><name>Arun</name></person>
HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 01:15:40 GMT Server: Apache/2 Access-Control-Allow-Origin: https://foo.example Vary: Accept-Encoding, Origin Content-Encoding: gzip Content-Length: 235 Keep-Alive: timeout=2, max=99 Connection: Keep-Alive Content-Type: text/plain [Some GZIP'd payload]
正式响应中很多标头我们在之前的文章已经探讨过了,本篇不再做详细的介绍,读者可以参考
你还在为 HTTP 的这些概念头疼吗? 查阅
带凭证的请求
XMLHttpRequest 或 Fetch 和 CORS 最有趣的功能就是能够发出知道 HTTP Cookie 和 HTTP 身份验证的 凭证 请求。默认情况下,在跨站点 XMLHttpRequest 或 Fetch 调用中,浏览器将不发送凭据。调用 XMLHttpRequest对象或 Request 构造函数时必须设置一个特定的标志。
在下面这个例子中,最初从 http://foo.example 加载的内容对设置了 Cookies 的 http://bar.other 上的资源进行了简单的 GET 请求, foo.example 上可能的代码如下
const invocation = new XMLHttpRequest(); const url = 'http://bar.other/resources/credentialed-content/'; function callOtherDomain() { if (invocation) { invocation.open('GET', url, true); invocation.withCredentials = true; invocation.onreadystatechange = handler; invocation.send(); } }
第7行显示 XMLHttpRequest 上的标志,必须设置该标志才能使用 Cookie 进行调用。默认情况下,调用是不在使用 Cookie 的情况下进行的。由于这是一个简单的 GET 请求,因此不会进行预检,但是浏览器将拒绝任何没有 Access-Control-Allow-Credentials 的响应:标头为true,指的是响应不会返回 web 页面的内容。
上面的请求用下图可以表示
这是客户端和服务器之间的示例交换:
GET /resources/access-control-with-credentials/ HTTP/1.1 Host: bar.other User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Connection: keep-alive Referer: http://foo.example/examples/credential.html Origin: http://foo.example Cookie: pageAccess=2
HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 01:34:52 GMT Server: Apache/2 Access-Control-Allow-Origin: https://foo.example Access-Control-Allow-Credentials: true Cache-Control: no-cache Pragma: no-cache Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT Vary: Accept-Encoding, Origin Content-Encoding: gzip Content-Length: 106 Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Content-Type: text/plain [text/plain payload]
上面第10行包含指向http://bar.other 上的内容 Cookie,但是如果 bar.other 没有以 Access-Control-Allow-Credentials:true 响应(下面第五行),响应将被忽略,并且不能使用网站返回的内容。
请求凭证和通配符
当回应凭证请求时,服务器必须在 Access-Control-Allow-Credentials 中指定一个来源,而不能直接写* 通配符
因为上面示例代码中的请求标头包含 Cookie 标头,如果 Access-Control-Allow-Credentials 中是指定的通配符 * 的话,请求会失败。
注意上面示例中的 Set-Cookie 响应标头还设置了另外一个值,如果发生故障,将引发异常(取决于所使用的API)。
HTTP 响应标头
下面会列出一些服务器跨域共享规范定义的 HTTP 标头,上面简单概述了一下,现在一起来认识一下,主要会介绍下面这些
Access-Control-Allow-Origin
Access-Control-Allow-Credentials
Access-Control-Allow-Headers
Access-Control-Allow-Methods
Access-Control-Expose-Headers
Access-Control-Max-Age
Access-Control-Request-Headers
Access-Control-Request-Method
Origin
Access-Control-Allow-Origin
Access-Control-Allow-Origin 是 HTTP 响应标头,指示响应是否能够和给定的源共享资源。Access-Control-Allow-Origin 指定单个资源会告诉浏览器允许指定来源访问资源。对于没有凭据的请求 *通配符,告诉浏览器允许任何源访问资源。
例如,如果要允许源 https://mozilla.org 的代码访问资源,可以使用如下的指定方式
Access-Control-Allow-Origin: https://mozilla.org Vary: Origin
如果服务器指定单个来源而不是*通配符,则服务器还应在 Vary 响应标头中包含该来源。
Access-Control-Allow-Credentials
Access-Control-Allow-Credentials 是 HTTP 的响应标头,这个标头告诉浏览器,当包含凭证请求(Request.credentials)时是否将响应公开给前端 JavaScript 代码。
这时候你会问到 Request.credentials 是什么玩意?不要着急,来给你看一下,首先来看 Request 是什么玩意,
实际上,Request 是 Fetch API 的一类接口代表着资源请求。一般创建 Request 对象有两种方式
使用 Request() 构造函数创建一个 Request 对象
还可以通过 FetchEvent.request api 操作来创建
再来说下 Request.credentials 是什么意思,Request 接口的凭据只读属性指示在跨域请求的情况下,用户代理是否应从其他域发送 cookie。(其他 Request 对象的方法详见 https://developer.mozilla.org...)
当发送的是凭证模式的请求包含 (Request.credentials)时,如果 Access-Control-Allow-Credentials 值为 true,浏览器将仅向前端 JavaScript 代码公开响应。
Access-Control-Allow-Credentials: true
凭证一般包括 cookie、认证头和 TLS 客户端证书
当用作对预检请求响应的一部分时,这表明是否可以使用凭据发出实际请求。注意简单的 GET 请求不会进行预检。
可以参考一个实际的例子 https://www.jianshu.com/p/ea4...
Access-Control-Allow-Headers
Access-Control-Allow-Headers 是一个响应标头,这个标头用来响应预检请求,它发出实际请求时可以使用哪些HTTP标头。
示例
自定义标头
这是 Access-Control-Allow-Headers 标头的示例。它表明除了像 CROS 安全列出的请求标头外,对服务器的 CROS 请求还支持名为 X-Custom-Header 的自定义标头。
Access-Control-Allow-Headers: X-Custom-Header
多个标头
这个例子展示了 Access-Control-Allow-Headers 如何使用多个标头
Access-Control-Allow-Headers: X-Custom-Header, Upgrade-Insecure-Requests
绕过其他限制
尽管始终允许使用 CORS 安全列出的请求标头,并且通常不需要在 Access-Control-Allow-Headers 中列出这些标头,但是无论如何列出它们都将绕开适用的其他限制。
Access-Control-Allow-Headers: Accept
这里你可能会有疑问,哪些是 CORS 列出的安全标头?(别嫌累,就是这么麻烦)
有下面这些 Accep、Accept-Language、Content-Language、Content-Type ,当且仅当包含这些标头时,无需在 CORS 上下文中发送预检请求。
Access-Control-Allow-Methods
Access-Control-Allow-Methods 也是响应标头,它指定了哪些访问资源的方法可以使用预检请求。例如
Access-Control-Allow-Methods: POST, GET, OPTIONS Access-Control-Allow-Methods: *
Access-Control-Expose-Headers
Access-Control-Expose-Headers 响应标头表明哪些标头可以作为响应的一部分公开。默认情况下,仅公开6个CORS安全列出的响应标头,分别是
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
如果希望客户端能够访问其他标头,则必须使用 Access-Control-Expose-Headers 标头列出它们。下面是示例
要公开非 CORS 安全列出的请求标头,可以像如下这样指定
Access-Control-Expose-Headers: Content-Length
要另外公开自定义标头,例如 X-Kuma-Revision,可以指定多个标头,并用逗号分隔
Access-Control-Expose-Headers: Content-Length, X-Kuma-Revision
在不是凭证请求中,你还可以使用通配符
Access-Control-Expose-Headers: *
但是,这不会通配 Authorization 标头,因此如果需要公开它,则需要明确列出
Access-Control-Expose-Headers: *, Authorization
Access-Control-Max-Age
Access-Control-Max-Age 响应头表示预检请求的结果可以缓存多长时间,例如
Access-Control-Max-Age: 600
表示预检请求可以缓存10分钟
Access-Control-Request-Headers
浏览器在发出预检请求时使用 Access-Control-Request-Headers 请求标头,使服务器知道在发出实际请求时客户端可能发送的 HTTP 标头。
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
Access-Control-Request-Method
同样的,Access-Control-Request-Method 响应标头告诉服务器发出预检请求时将使用那种 HTTP 方法。此标头是必需的,因为预检请求始终是 OPTIONS,并且使用的方法与实际请求不同。
Access-Control-Request-Method: POST
Origin
Origin 请求标头表明匹配的来源,它不包含任何信息,仅仅包含服务器名称,它与 CORS 请求以及 POST 请求一起发送,它类似于 Referer 标头,但与此标头不同,它没有公开整个路径。例如
Origin: https://developer.mozilla.org
HTTP 条件请求
HTTP 具有条件请求的概念,通过比较资源更新生成的值与验证器的值进行比较,来确定资源是否进行过更新。这样的请求对于验证缓存的内容、条件请求、验证资源的完整性来说非常重要。
原则
HTTP 条件请求是根据特定标头的值执行不同的请求,这些标头定义了一个前提条件,如果前提条件匹配或不匹配,则请求的结果将有所不同。
对于 安全 的方法,像是 GET、用于请求文档的资源,仅当条件请求的条件满足时发回文档资源,所以,这种方式可以节约带宽。
什么是安全的方法,对于 HTTP 来说,安全的方法是不会改变服务器状态的方法,换句话说,如果方法只是只读操作,那么它肯定是安全的方法,比如说 GET 请求,它肯定是安全的方法,因为它只是请求资源。几种常见的方法肯定是安全的,它们是 GET、HEAD和 OPTIONS。所有安全的方法都是幂等的(这他妈幂等又是啥意思?)但不是所有幂等的方法都是安全的,例如 PUT 和 DELETE 都是幂等的,但不安全。
幂等性:如果相同的客户端发起一次或者多次 HTTP 请求会得到相同的结果,则说明 HTTP 是幂等的。(我们这次不深究幂等性)
对于 非安全 的方法,像是 PUT,只有原始文档与服务器上存储的资源相同时,才可以使用条件请求来传输文档。(PUT 方法通常用来传输文件,就像 FTP 协议的文件上传一样)
验证
所有的条件请求都会尝试检查服务器上存储的资源是否与某个特定版本的资源相匹配。为了满足这种情况,条件请求需要指示资源的版本。由于无法和整个文件逐个字符进行比较,因此需要把整个文件描绘成一个值,然后把此值和服务器上的资源进行比较,这种方式称为比较器,比较器有两个条件
文档的最后修改日期
一个不透明的字符串,用于唯一标识每个版本,称为实体标签或 Etag。
比较两个资源是否时相同的版本有些复杂,根据上下文,有两种相等性检查
当期望的是字节对字节进行比较时,例如在恢复下载时,使用强 Etag 进行验证
当用户代理需要比较两个资源是否具有相同的内容时,使用若 Etag 进行验证
HTTP 协议默认使用 强验证,它指定何时进行弱验证
强验证
强验证保证的是字节 级别的验证,严格的验证非常严格,可能在服务器级别难以保证,但是它能够保证任何时候都不会丢失数据,但这种验证丢失性能。
要使用 Last-Modified 很难实现强验证,通常,这是通过使用带有资源的 MD5 哈希值的 Etag 来完成的。
弱验证
弱验证不同于强验证,因为如果内容相等,它将认为文档的两个版本相同,例如,一个页面与另一个页面的不同之处仅在于页脚的日期不同,因此该页面被认为与其他页面相同。而使用强验证时则被认为这两个版本是不同的。构建一个若验证的 Etag 系统可能会非常复杂,因为这需要了解每个页面元素的重要性,但是对于优化缓存性能非常有用。
下面介绍一下 Etag 如何实现强弱验证。
Etag 响应头是特定版本的标识,它能够使缓存变得更高效并能够节省带宽,因为如果缓存内容未发生变更,Web 服务器则不需要重新发送完整的响应。除此之外,Etag 能够防止资源同时更新互相覆盖。
如果给定 URL 上的资源发生变更,必须生成一个新的 Etag 值,通过比较它们可以确定资源的两个表示形式是否相同。
Etag 值有两种,一种是强 Etag,一种是弱 Etag;
强 Etag 值,无论实体发生多么细微的变化都会改变其值,一般的表示如下
Etag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
弱 Etag 值,弱 Etag 值只用于提示资源是否相同。只有资源发生了根本改变,产生差异时才会改变 Etag 值。这时,会在字段值最开始处附加 W/。
Etag: W/"0815"
下面就来具体探讨一下条件请求的标头和 Etag 的关系
条件请求
条件请求主要包含的标头如下
If-Match
If-None-Match
If-Modified-Since
If-Unmodified-Since
If-Range
If-Match
对于 GET 和 POST 方法,服务器仅在与列出的 Etag(响应标头) 之一匹配时才返回请求的资源。这里又多了一个新词 Etag,我们稍后再说 Etag 的用法。对于像是 PUT 和其他非安全的方法,在这种情况下,它仅仅将上传资源。
下面是两种常见的案例
对于 GET 和 POST 方法,会结合使用 Range 标头,它可以确保新发送请求的范围与上一个请求的资源相同,如果不匹配的话,会返回 416 响应。
对于其他方法,特别是 PUT 方法,If-Match 可以防止丢失更新,服务器会比对 If-Match 的字段值和资源的 Etag 值,仅当两者一致时,才会执行请求。反之,则返回状态码 412 Precondition Failed 的响应。例如
If-Match: "bfc13a64729c4290ef5b2c2730249c88ca92d82d" If-Match: *
If-None-Match
条件请求,它与 If-Match 的作用相反,仅当 If-None-Match 的字段值与 Etag 值不一致时,可处理该请求。对于GET 和 HEAD ,仅当服务器没有与给定资源匹配的 Etag 时,服务器将返回 200 OK作为响应。对于其他方法,仅当最终现有资源的 Etag 与列出的任何值都不匹配时,才会处理请求。
当 GET 和 POST 发送的 If-None-Match与 Etag 匹配时,服务器会返回 304。
If-None-Match: "bfc13a64729c4290ef5b2c2730249c88ca92d82d" If-None-Match: W/"67ab43", "54ed21", "7892dd" If-None-Match: *
If-Modified-Since
If-Modified-Since 是 HTTP 条件请求的一部分,只有在给定日期之后,服务端修改了请求所需要的资源,才会返回 200 OK 的响应。如果在给定日期之后,服务端没有修改内容,响应会返回 304 并且不带任何响应体。If-Modified-Since 只能使用 GET 和 HEAD 请求。
If-Modified-Since 与 If-None-Match 结合使用时,它将被忽略,除非服务器不支持 If-None-Match。一般表示如下
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
注意:这是格林威治标准时间。 HTTP 日期始终以格林尼治标准时间表示,而不是本地时间。
If-Range
If-Range 也是条件请求,如果满足条件(If-Range 的值和 Etag 值或者更新的日期时间一致),则会发出范围请求,否则将会返回全部资源。它的一般表示如下
If-Range: Wed, 21 Oct 2015 07:28:00 GMT If-Range: bfc13a64729c4290ef5b2c2730249c88ca92d82d
If-Unmodified-Since
If-Unmodified-Since HTTP 请求标头也是一个条件请求,服务器只有在给定日期之后没有对其进行修改时,服务器才返回请求资源。如果在指定日期时间后发生了更新,则以状态码 412 Precondition Failed 作为响应返回。
If-Unmodified-Since: Wed, 21 Oct 2015 07:28:00 GMT
条件请求示例
缓存更新
条件请求最常见的示例就是更新缓存,如果缓存是空或没有缓存,则以200 OK的状态发送回请求的资源。如下图所示
客户端第一次发送请求没有,缓存为空并且没有条件请求,服务器在收到客户端请求后,设置验证器 Last-Modified 和 Etag 标签,并把这两个标签随着响应一起发送回客户端。
下一次客户端再发送相同的请求后,会直接从缓存中提取,只要缓存没有过期,就不会有任何新的请求到达服务器重新下载资源。但是,一旦缓存过期,客户端不会直接使用缓存的值,而是发出条件请求。 验证器的值用作 If-Modified-Since 和 If-Match标头的参数。
缓存过期后客户端重新发起请求,服务器收到请求后发现如果资源没有更改,服务器会发回 304 Not Modified响应,这使缓存再次刷新,并让客户端使用缓存的资源。 尽管有一个响应/请求往返消耗一些资源,但是这比再次通过有线传输整个资源更有效。
如果资源已经发生更改,则服务器仅使用新版本的资源返回 200 OK 响应,就像没有条件请求,并且客户端会重新使用新的资源,从这个角度来讲,缓存是条件请求的前置条件。
断点续传
HTTP 可以支持文件的部分下载,通过保留已获得的信息,此功能允许恢复先前的操作,从而节省带宽和时间。
支持断点续传的服务器通过发送 Accept-Ranges 标头广播此消息,一旦发生这种情况,客户端可以通过发送缺少范围的 Ranges 标头来恢复下载
这里你可能有疑问 Ranges 和 Content-Range是什么,来解释一下
Range
Range HTTP 请求标头指示服务器应返回文档指定部分的资源,可以一次请求一个 Range 来返回多个部分,服务器会将这些资源返回各个文档中。如果服务器成功返回,那么将返回 206 响应;如果 Range 范围无效,服务器返回416 Range Not Satisfiable错误;服务器还可以忽略 Range 标头,并且返回 200 作为响应。
Range: bytes=200-1000, 2000-6576, 19000-
还有一种表示是
Range: bytes=0-499, -500
它们分别表示请求前500个字节和最后500个字节,如果范围重叠,则服务器可能会拒绝该请求。
Content-Range
HTTP 的 Content-Range 响应标头是针对范围请求而设定的,返回响应时使用首部字段 Content-Range,能够告知客户端响应实体的哪部分是符合客户端请求的,字段以字节为单位。它的一般表示如下
Content-Range: bytes 200-1000/67589
上段代码表示从所有 67589 个字节中返回 200-1000 个字节的内容
那么上面的 Content-Range你也应该知道是什么意思了
断点续传的原理比较简单,但是这种方式存在潜在的问题:如果在两次下载资源的期间进行了资源更新,那么获得的范围将对应于资源的两个不同版本,并且最终文档将被破坏。
为了阻止这种情况的出现,就会使用条件请求。对于范围来说,有两种方法可以做到这一点。一种方法是使用 If-Modified-Since和If-Match,如果前提条件失败,服务器将返回错误;然后客户端从头开始重新下载。
即使此方法有效,当文档资源发生改变时,它也会添加额外的 响应/请求 交换。这会降低性能,并且 HTTP 具有特定的标头来避免这种情况 If-Range。
该解决方案效率更高,但灵活性稍差一些,因为在这种情况下只能使用一个 Etag。
通过乐观锁避免丢失更新
Web 应用程序中最普遍的操作是资源更新。这在任何文件系统或应用程序中都很常见,但是任何允许存储远程资源的应用程序都需要这种机制。
使用 put 方法,你可以实现这一点,客户端首先读取原始文件对其进行修改,然后把它们发送到服务器。
上面这种请求响应存在问题,一旦考虑到并发性,事情就会变得不准确。当客户端在本地修改资源打算重新发送之前,第二个客户端可以获取相同的资源并对资源进行修改操作,这样就会造成问题。当它们重新发送请求到服务器时,第一个客户端所做的修改将被第二次客户端的修改所覆盖,因为第二次客户端修改并不知道第一次客户端正在修改。资源提交并更新的一方不会传达给另外一方,所以要保留哪个客户的更改,将随着他们提交的速度而变化; 这取决于客户端,服务器的性能,甚至取决于人工在客户端编辑文档的性能。 例如下面这个流程
如果没有两个用户同时操作服务器,也就不存在这个问题。但是,现实情况是不可能只有单个用户出现的,所以为了规避或者避免这个问题,我们希望客户端资源在更新时进行提示或者修改被拒绝时收到通知。
条件请求允许实现乐观锁算法。这个概念是允许所有的客户端获取资源的副本,然后让他们在本地修改资源,并成功通过允许第一个客户端提交更新来控制并发,基于此服务端的后面版本的更新都将被拒绝。
这是使用 If-Match 或 If-Unmodified-Since标头实现的。如果 Etag 与原始文件不匹配,或者自获取以来已对文件进行了修改,则更改为拒绝更新,并显示412 Precondition Failed错误。
HTTP Cookies
HTTP 协议中的 Cookie 包括 Web Cookie 和浏览器 Cookie,它是服务器发送到 Web 浏览器的一小块数据。服务器发送到浏览器的 Cookie,浏览器会进行存储,并与下一个请求一起发送到服务器。通常,它用于判断两个请求是否来自于同一个浏览器,例如用户保持登录状态。
HTTP Cookie 机制是 HTTP 协议无状态的一种补充和改良
Cookie 主要用于下面三个目的
会话管理
登陆、购物车、游戏得分或者服务器应该记住的其他内容
个性化
用户偏好、主题或者其他设置
追踪
记录和分析用户行为
Cookie 曾经用于一般的客户端存储。虽然这是合法的,因为它们是在客户端上存储数据的唯一方法,但如今建议使用现代存储 API。Cookie 随每个请求一起发送,因此它们可能会降低性能(尤其是对于移动数据连接而言)。客户端存储的现代 API 是 Web 存储 API(localStorage 和 sessionStorage)和 IndexedDB。
创建 Cookie
当接收到客户端发出的 HTTP 请求时,服务器可以发送带有响应的 Set-Cookie 标头,Cookie 通常由浏览器存储,然后将 Cookie 与 HTTP 标头一同向服务器发出请求。可以指定到期日期或持续时间,之后将不再发送Cookie。此外,可以设置对特定域和路径的限制,从而限制 cookie 的发送位置。
Set-Cookie 和 Cookie 标头
Set-Cookie HTTP 响应标头将 cookie 从服务器发送到用户代理。下面是一个发送 Cookie 的例子
HTTP/2.0 200 OK Content-type: text/html Set-Cookie: yummy_cookie=choco Set-Cookie: tasty_cookie=strawberry [page content]
此标头告诉客户端存储 Cookie
现在,随着对服务器的每个新请求,浏览器将使用 Cookie 头将所有以前存储的 cookie 发送回服务器。
GET /sample_page.html HTTP/2.0 Host: www.example.org Cookie: yummy_cookie=choco; tasty_cookie=strawberry
Cookie 主要分为三类,它们是 会话Cookie、永久Cookie 和 Cookie的 Secure 和 HttpOnly 标记,下面依次来介绍一下
会话 Cookies
上面的示例创建的是会话 Cookie ,会话 Cookie 有个特征,客户端关闭时 Cookie 会删除,因为它没有指定Expires 或 Max-Age 指令。 这两个指令你看到这里应该比较熟悉了。
但是,Web 浏览器可能会使用会话还原,这会使大多数会话 Cookie 保持永久状态,就像从未关闭过浏览器一样
永久性 Cookies
永久性 Cookie 不会在客户端关闭时过期,而是在特定日期(Expires)或特定时间长度(Max-Age)外过期。例如
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
Cookie的 Secure 和 HttpOnly 标记
安全的 Cookie 需要经过 HTTPS 协议通过加密的方式发送到服务器。即使是安全的,也不应该将敏感信息存储在cookie 中,因为它们本质上是不安全的,并且此标志不能提供真正的保护。
HttpOnly 的作用
会话 cookie 中缺少 HttpOnly 属性会导致攻击者可以通过程序(JS脚本、Applet等)获取到用户的 cookie 信息,造成用户cookie 信息泄露,增加攻击者的跨站脚本攻击威胁。
HttpOnly 是微软对 cookie 做的扩展,该值指定 cookie 是否可通过客户端脚本访问。
如果在 Cookie 中没有设置 HttpOnly 属性为 true,可能导致 Cookie 被窃取。窃取的 Cookie 可以包含标识站点用户的敏感信息,如 ASP.NET 会话 ID 或 Forms 身份验证票证,攻击者可以重播窃取的 Cookie,以便伪装成用户或获取敏感信息,进行跨站脚本攻击等。
Cookie 的作用域
Domain 和 Path 标识定义了 Cookie 的作用域:即 Cookie 应该发送给哪些 URL。
Domain 标识指定了哪些主机可以接受 Cookie。如果不指定,默认为当前主机(不包含子域名)。如果指定了Domain,则一般包含子域名。
感谢各位的阅读,以上就是“带你了解HTTP黑科技”的内容了,经过本文的学习后,相信大家对带你了解HTTP黑科技这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341