HTTP协议笔记

HTTP协议笔记

参考:
(建议精读)HTTP灵魂之问,巩固你的 HTTP 知识体系
《透视 HTTP 协议》——chrono

 

目录:

  • 1、说说你对HTTP的了解吧。
     1. HTTP状态码。
     2. HTTP请求头和响应头,其中包括cookie、跨域响应头、Accept相关字段、鉴权token等等。
     3. HTTP缓存。
     4. HTTP跨域。
     5. HTTPS。

  • 2、输入网址后到页面显示,浏览器发生了什么

  • 3、HTTP的优缺点

  • 4、HTTP1.1到2的一些版本更新。

  • 5、HTTP代理 / 缓存代理(具体看博客了里 20220817-CDN相关和HTTP代理 的另一篇文章)


 

1、如果面试官问你:说说你对HTTP的了解吧。如何回答?

这个我还真遇到过。。。那时候回答的乱七八糟的,就吭哧吭哧开始背看的那些文章,背到一半忘记了,面试官说:好可以了,下个问题吧。。。
哈哈哈哈哈现在回忆起来还是那么尴尬!虽然现在也还是没有特别理解,但是这道题好像可以比当年答得好了。


 
如果是现在我应该会这么回答:
HTTP协议是应用层协议,通常建立在传输层协议TCP之上,现在常用的HTTPS是HTTP在TCP协议建立后再进行SSL/TSL安全套接层连接的基础上建立的。
它有几个特点:包括灵活可拓展 可以自定义头字段实现功能、建立在TCP的基础上可靠传输、采用请求-应答的通信模式、无状态连接等。
http在我们的实际使用过程中主要需要注意以下几个方面:

  1. HTTP状态码。
  2. HTTP请求头和响应头,其中包括cookie、跨域响应头、Accept相关字段、鉴权token等等。
  3. HTTP缓存。
  4. HTTP跨域。
  5. 一个HTTP请求中的协议变化(不过这块我也了解不深)。
  6. 使用HTTP中的安全问题。

一、 HTTP状态码。

状态码大概分为五类,从1-5开头的三位数表示。

1xx: 表示目前是协议处理的中间状态,还需要后续操作。
1××类状态码属于提示信息,是协议处理的中间状态,实际能够用到的时候很少。
偶尔能够见到的是“101 Switching Protocols”。它的意思是客户端使用 Upgrade 头字段,要求在 HTTP 协议的基础上改成其他的协议继续通信,比如 WebSocket。而如果服务器也同意变更协议,就会发送状态码 101,但这之后的数据传输就不会再使用 HTTP 了。

2xx: 表示成功状态。
2××类状态码表示成功,常用的有 200、204、206;

3xx: 重定向状态,资源位置发生变动,需要重新请求。
3××类状态码表示重定向,常用的有 301、302、304;

4xx: 请求报文有误。
4××类状态码表示客户端错误,常用的有 400、403、404;

5xx: 服务器端发生错误。
5××类状态码表示服务器错误,常用的有 500、501、502、503。

2xx

200 OK是最常见的成功状态码,表示一切正常,服务器如客户端所期望的那样返回了处理结果,如果是非 HEAD 请求,通常在响应头后都会有 body 数据。
204 No Content是另一个很常见的成功状态码,它的含义与“200 OK”基本相同,但响应头后没有 body 数据。所以对于 Web 服务器来说,正确地区分 200 和 204 是很必要的。
206 Partial Content是 HTTP 分块下载或断点续传的基础,表示 body 里的数据不是资源的全部,而是其中的一部分。
状态码 206 通常还会伴随着头字段“Content-Range”,表示响应报文里 body 数据的具体范围,供客户端确认,例如“Content-Range: bytes 0-99/2000”,意思是此次获取的是总计 2000 个字节的前 100 个字节。

3xx

301 Moved Permanently俗称“永久重定向”,含义是此次请求的资源已经不存在了,需要改用新的 URI 再次访问。
与它类似的是“302 Found”,曾经的描述短语是“Moved Temporarily”,俗称“临时重定向”,意思是请求的资源还在,但需要暂时用另一个 URI 来访问。
301 和 302 都会在响应头里使用字段 Location 指明后续要跳转的 URI,最终的效果很相似,浏览器都会重定向到新的 URI。两者的根本区别在于语义,一个是“永久”,一个是“临时”,所以在场景、用法上差距很大。
比如,你的网站升级到了 HTTPS,原来的 HTTP 不打算用了,这就是“永久”的,所以要配置 301 跳转,把所有的 HTTP 流量都切换到 HTTPS。
再比如,今天夜里网站后台要系统维护,服务暂时不可用,这就属于“临时”的,可以配置成 302 跳转,把流量临时切换到一个静态通知页面,浏览器看到这个 302 就知道这只是暂时的情况,不会做缓存优化,第二天还会访问原来的地址。
304 Not Modified是一个比较有意思的状态码,它用于 If-Modified-Since 等条件请求,表示资源未修改,用于缓存控制。它不具有通常的跳转含义,但可以理解成“重定向已到缓存的文件”(即“缓存重定向”)。

4xx 客户端错误码

400 Bad Request是一个通用的错误码,表示请求报文有错误,但具体是数据格式错误、缺少请求头还是 URI 超长它没有明确说,只是一个笼统的错误,客户端看到 400 只会是“一头雾水”“不知所措”。所以,在开发 Web 应用时应当尽量避免给客户端返回 400,而是要用其他更有明确含义的状态码。
403 Forbidden实际上不是客户端的请求出错,而是表示服务器禁止访问资源。原因可能多种多样,例如信息敏感、法律禁止等,如果服务器友好一点,可以在 body 里详细说明拒绝请求的原因,不过现实中通常都是直接给一个“闭门羹”。
404 Not Found可能是我们最常看见也是最不愿意看到的一个状态码,它的原意是资源在本服务器上未找到,所以无法提供给客户端。但现在已经被“用滥了”,只要服务器“不高兴”就可以给出个 404,而我们也无从得知后面到底是真的未找到,还是有什么别的原因,某种程度上它比 403 还要令人讨厌。
405 Method Not Allowed:不允许使用某些方法操作资源,例如不允许 POST 只能 GET;

5xx 服务器端错误码

500 Internal Server Error与 400 类似,也是一个通用的错误码,服务器究竟发生了什么错误我们是不知道的。不过对于服务器来说这应该算是好事,通常不应该把服务器内部的详细信息,例如出错的函数调用栈告诉外界。虽然不利于调试,但能够防止黑客的窥探或者分析。
501 Not Implemented表示客户端请求的功能还不支持,这个错误码比 500 要“温和”一些,和“即将开业,敬请期待”的意思差不多,不过具体什么时候“开业”就不好说了。“502 Bad Gateway”通常是服务器作为网关或者代理时返回的错误码,表示服务器自身工作正常,访问后端服务器时发生了错误,但具体的错误原因也是不知道的。
503 Service Unavailable表示服务器当前很忙,暂时无法响应服务,我们上网时有时候遇到的“网络服务正忙,请稍后重试”的提示信息就是状态码 503。
503 是一个“临时”的状态,很可能过几秒钟后服务器就不那么忙了,可以继续提供服务,所以 503 响应报文里通常还会有一个“Retry-After”字段,指示客户端可以在多久以后再次尝试发送请求。

二、 HTTP请求头和响应头,其中包括cookie、跨域响应头、Accept相关字段、鉴权token等等。

一个个说哈。

数据类型有关:两个Accept 请求头字段和两个 Content 实体头字段,用于客户端和服务器进行“内容协商”

Accept: text/html,application/xml,image/webp,image/png

Accept-Encoding: gzip, deflate, br (如果没有表示不支持压缩

Content-Type: text/html; charset=utf-8 (返回字符集编码)
Content-Type: image/png

Content-Encoding: gzip (如果没有表示没有压缩

客户端用 Accept 头告诉服务器希望接收什么样的数据,同时在POST请求中也会用 Content 头说明发送了什么样的数据,服务端也会在响应头中添加Content响应头告诉客户端实际发送了什么数据。
HTTP协议笔记_第1张图片

语言类型的字段

Accept-Language
Content-Language: zh-CN

Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
“q”是权重的意思。最大值是 1,最小值是 0.01,默认值是 1,如果值是 0 就表示拒绝。具体的形式是在数据类型或语言代码后面加一个“;”,然后是“q=value”。
这里要提醒的是“;”的用法,在大多数编程语言里“;”的断句语气要强于“,”,而在 HTTP 的内容协商里却恰好反了过来,“;”的意义是小于“,”的。

服务器会在响应头里多加一个 Vary 字段,记录服务器在内容协商时参考的请求头字段。

Vary: Accept-Encoding,User-Agent,Accept

大文件传输有关的字段

分块传输
body数据长度未知的情况下可以使用chunked方式发送
响应头:Transfer-Encoding: chunked,和Content-Length响应头互斥。
注:流式下载的时候,服务端采用动态压缩的方式传输数据,所以采用分块编码,压缩一部分就发一部分,发送数据是动态生成的,总长度未知。

范围请求
比如视频播放拖动进度条实时获取文件的某一范围数据,就可以使用范围请求。它允许客户端在请求头里使用专用字段来表示只获取文件的一部分。
服务器必须在响应头里使用字段 “Accept-Ranges: bytes”明确告知客户端它支持范围请求.
请求头 Range 是 HTTP 范围请求的专用字段,格式是“bytes=x-y”,其中的 x 和 y 是以字节为单位的数据范围。
x、y 表示的是“偏移量”,范围必须从 0 计数,例如前 10 个字节表示为“0-9”,第二个 10 字节表示为“10-19”,而“0-10”实际上是前 11 个字节。

请求头
GET /16-2 HTTP/1.1
Host: www.chrono.com
Range: bytes=0-31

响应头

HTTP/1.1 206 Partial Content
Content-Length: 32
Accept-Ranges: bytes
Content-Range: bytes 0-31/96

服务器收到 Range 字段后,需要做四件事。
第一,它必须检查范围是否合法,比如文件只有 100 个字节,但请求“200-300”,这就是范围越界了。服务器就会返回状态码 416,意思是“你的范围请求有误,我无法处理,请再检查一下”。
第二,如果范围正确,服务器就可以根据 Range 头计算偏移量,读取文件的片段了,返回状态码“206 Partial Content”,和 200 的意思差不多,但表示 body 只是原数据的一部分。
第三,服务器要添加一个响应头字段Content-Range,告诉片段的实际偏移量和资源的总大小,格式是“bytes x-y/length”,与 Range 头区别在没有“=”,范围后多了总长度。例如,对于“0-10”的范围请求,值就是“bytes 0-10/100”。
最后剩下的就是发送数据了,直接把片段用 TCP 发给客户端,一个范围请求就算是处理完了。

注:range针对的是原数据的范围字段,但是服务器是会压缩了菜发送过来。

长连接

如果服务器支持长连接,它总会在响应报文里放一个Connection: keep-alive字段,告诉客户端它支持长连接的,接下来只要向服务器发送了第一次请求,后续的请求都会重复利用第一次打开的 TCP 连接,也就是长连接,在这个连接上收发数据。
如果长时间不关闭长连接也会有挤兑服务器资源的可能,因此服务端往往会自行设置自动关闭连接的策略。
请求头里发送Connection: close字段可以自动关闭连接。
响应头里传回Connection: close也代表自动关闭连接了。
HTTP协议笔记_第2张图片

拿 Nginx 来举例,它有两种方式:

  • 使用“keepalive_timeout”指令,设置长连接的超时时间,如果在一段时间内连接上没有任何数据收发就主动断开连接,避免空闲连接占用系统资源。

  • 使用“keepalive_requests”指令,设置长连接上可发送的最大请求次数。比如设置成 1000,那么当 Nginx 在这个连接上处理了 1000 个请求后,也会主动断开连接。

重定向和Location

301:永久重定向
302:暂时重定向
“重定向”实际上发送了两次 HTTP 请求,第一个请求返回了 302,然后第二个请求就被重定向到了“/index.html”
响应头Location: /index.html只有配合 301/302 状态码才有意义,它标记了服务器要求重定向的 URI。
浏览器收到 301/302 报文,会检查响应头里有没有“Location”。如果有,就从字段值里提取出 URI,发出新的 HTTP 请求,相当于自动替我们点击了这个链接。
HTTP协议笔记_第3张图片

cookie

聊一聊Cookie
CSRF/XSRF攻击与防御
Cookie 就是服务器委托浏览器存储在客户端里的一些数据,而这些数据通常都会记录用户的关键识别信息.
主要涉及到响应头字段 Set-Cookie 和请求头字段 Cookie。

当用户通过浏览器第一次访问服务器的时候,服务器肯定是不知道他的身份的。所以,就要创建一个独特的身份标识数据,格式是“key=value”,然后放进 Set-Cookie 字段里,随着响应报文一同发给浏览器。set-cookie可以设置多个,关键字使用;隔开。

浏览器收到响应报文,看到里面有 Set-Cookie,知道这是服务器给的身份标识,于是就保存起来,下次再请求的时候就自动把这个值放进 Cookie 字段里发给服务器。

因为第二次请求里面有了 Cookie 字段,服务器就知道这个用户不是新人,之前来过,就可以拿出 Cookie 里的值,识别出用户的身份,然后提供个性化的服务。
HTTP协议笔记_第4张图片

cookie属性

你对cookie了解多少?

  1. 过期时间
    Cookie 的有效期可以使用 Expires 和 Max-Age 两个属性来设置。
    “Expires”俗称“过期时间”,用的是绝对时间点,可以理解为“截止日期”(deadline)。
    “Max-Age”用的是相对时间,单位是秒,浏览器用收到报文的时间点再加上 Max-Age,就可以得到失效的绝对时间。
    Expires 和 Max-Age 可以同时出现,两者的失效时间可以一致,也可以不一致,但浏览器会优先采用 Max-Age 计算失效期。

  2. 作用域
    cookie的domain属性
    让浏览器仅发送给特定的服务器和 URI,避免被其他网站盗用。
    “Domain”和“Path”指定了 Cookie 所属的【服务器】域名和路径,浏览器在发送 Cookie 前会从请求服务器的 URI (URI 可以是js/html/img/css资源请求)中提取出 host 和 path 部分,对比 Cookie 的属性。如果不满足条件,就不会在请求头里发送 Cookie。
    如果一个域名’aaa.com/bbb’的客户端向域名’ccc.com/ddd’的服务器请求数据,响应中setcookie的cookie中默认属性domain=‘ccc.com’,path=‘/ddd’。以后客户端再向’ccc.com/'的服务器发送请求时,path符合就会自动带上这个cookie。
    domain可以用于同一个域名下的多网站做单点登陆。

  3. 安全性
    SameSite是什么
    SameSite cookie 的说明
    在 JS 脚本里可以用 document.cookie 来读写 Cookie 数据,这就带来了安全隐患,有可能会导致“跨站脚本”(XSS)攻击窃取数据。
    -HttpOnly会告诉浏览器,此 Cookie 只能通过浏览器 HTTP 协议传输,禁止其他方式访问,浏览器的 JS 引擎就会禁用 document.cookie 等一切相关的 API,脚本攻击也就无从谈起了。
    -SameSite可以防范“跨站请求伪造”(XSRF)攻击。

涉及到第一方cookie和第三方cookie。

  • 第一方cookie:由相同站点发送的 cookie。
  • 第三方cookie:由跨站请求发送的cookie。
    拿 “请求的目标URL(或者cookie的domain)” 和 “当前网站URL(也就是浏览器地址栏中的网址)” 这两者来进行比较从而判断是否形成跨站的。
    两者的ORIGIN的注册域相同则为相同站点,不同则构成跨站。所谓注册域,是指您可以购买或租用的域名,即公共后缀(public suffix)之下的一级,也称为顶级域名。=

设置成“SameSite=Strict”可以严格限定 Cookie 只能允许第一方cookie发送。这个规则过于严格,可能造成非常不好的用户体验。比如,当前网页有一个 GitHub 链接,用户点击跳转就不会带有 GitHub 的 cookie,跳转过去总是未登陆状态。

“SameSite=Lax”则略宽松一点,允许 第三方网站导航回第一方网站的时候发送请求携带cookie,允许GET/HEAD 等安全方法,但禁止 POST 跨站发送。

“SameSite=None”则不限制cookie的发送,只要domain/path符合就可以携带,但是为了安全性谷歌要求必须同时设置Secure属性(Cookie 只能通过 HTTPS 协议发送),否则无效。

一开始我没搞懂sameSite和domain的区别。
samesite:校验发送请求的客户端域名,domain校验服务器的域名
如果不指定samesite,可能会出现:用户进入安全网站获得cookie,再进入恶意网站,恶意网站获取到cookie后(携带第三方cookie)向cookie设置的domain域名发送恶意请求的情况(csrf)。

在旧版浏览器,如果SameSite属性没有设置,或者没有得到运行浏览器的支持,那么它的行为等同于None,Cookies会被包含在任何请求中——包括跨站请求。
但是,在Chrome 80+版本中,SameSite的默认属性是SameSite=Lax。换句话说,当 Cookie 没有设置 SameSite 属性时,将会视作 SameSite 属性被设置为Lax。如果想要指定 Cookies 在同站、跨站请求都被发送,那么需要明确指定 SameSite 为 None。具有 SameSite=None 的 Cookie 也必须标记为secure并通过HTTPS传送。
Chrome 也宣布,将在下个版本也就是Chrome 83版本,在访客模式下禁用第三方 Cookie,在2022年全面禁用第三方 Cookie,到时候,即使你能指定 SameSite 为 None 也没有意义,因为你已经无法写入第三方 Cookie 了。

-Secure,表示这个 Cookie 仅能用 HTTPS 协议加密传输,明文的 HTTP 协议会禁止发送。但 Cookie 本身不是加密的,浏览器里还是以明文的形式存在。

三、 HTTP缓存。

HTTP缓存机制
cache-control请求头/响应头有关,注意就算响应头设置了MAx-age但是请求头设置no-store的时候,浏览器仍然不会缓存,这是一个协商缓存字段,是双方的协商缓存策略一致时生效。
HTTP协议笔记_第5张图片
浏览器中“无缓存刷新”、控制台中“disable cache”的请求机制就是如此,在请求头中加上cache-control: no-cache。大家可以自己观察一下,当勾选了控制台上的disable cache,即使响应头中存在etag,仍然不会走缓存,而取消勾选之后才能看到304状态码。max-age:0也可以达到同样的效果,刷新缓存。
HTTP协议笔记_第6张图片

字段值 作用
max-age 在指定时间内,缓存服务器不再对资源的有效性进行确认,可以使用,以服务端发出文件的时间为基准时间
no-cache 防止从缓存中返回过期的资源,所以使用之前,需要和源服务器发起请求比对过期时间
no-store 不进行缓存,暗示请求报文中可能含有机密信息,不可缓存
must-revalidate 和 no-cache 相似的词,如果缓存不过期就可以继续使用,但过期了如果还想用就必须去服务器验证。
private 只有某个在通过缓存服务器的时候,得到缓存资源
public 所有的用户在通过缓存服务器的时候,都可以缓存这个资源。
强缓存
  • Cache-Control:当值设为max-age=300时,则代表在这个请求正确返回时间(浏览器也会记录下来)的5分钟内再次加载资源,就会命中强缓存。
  • Expires :(基本弃用了)response header里的过期时间,浏览器再次加载资源时,如果在这个过期时间内,则命中强缓存。
协商缓存
  • ETag和If-None-Match
    Etag是上一次加载资源时,服务器返回的response header,是对该资源的一种唯一标识
    只要资源有变化,Etag就会重新生成
    浏览器在下一次加载资源向服务器发送请求时,会将上一次返回的Etag值放到request header里的If-None-Match里
    服务器接受到If-None-Match的值后,会拿来跟该资源文件的Etag值做比较,如果相同,则表示资源文件没有发生改变,命中协商缓存。

每个服务器对实现Etag有不同的方式,Nginx的方式是匹配修改时间+文件长度
HTTP协议笔记_第7张图片

  • Last-Modified和If-Modified-Since
    Last-Modified是该资源文件最后一次更改时间,服务器会在response header里返回
    同时浏览器会将这个值保存起来,下一次发送请求时,放到request headr里的If-Modified-Since里
    服务器在接收到后也会做对比,如果相同则命中协商缓存

在精确度上,Etag要优于Last-Modified,Last-Modified的时间单位是秒,如果某个文件在1秒内变了多次,那么他们的Last-Modified其实并没有体现出>来修改,但是Etag每次都会改变确保了精度

在性能上,Etag要逊于Last-Modified,毕竟> Last-Modified只需要记录时间,而Etag需要服务器通过算法来计算出一个hash值。

在优先级上,服务器校验优先考虑Etag。

所以,两者互补

浏览器缓存过程:

  1. 浏览器第一次加载资源,服务器返回200,浏览器将资源文件从服务器上请求下载下来,并把response header及该请求的返回时间(要与Cache-Control和Expires对比)一并缓存;
  2. 下一次加载资源时,先比较当前时间和上一次返回200时的时间差,如果没有超过Cache-Control设置的max-age,则没有过期,命中强缓存,不发请求直接从本地缓存读取该文件(如果浏览器不支持HTTP1.1,则用Expires判断是否过期);
  3. 如果时间过期,服务器则查看header里的If-None-Match和If-Modified-Since ;
  4. 服务器优先根据Etag的值判断被请求的文件有没有做修改,Etag值一致则没有修改,命中协商缓存,返回304;如果不一致则有改动,直接返回新的资源文件带上新的Etag值并返回 200;
  5. 如果服务器收到的请求没有Etag值,则将If-Modified-Since和被请求文件的最后修改时间做比对,一致则命中协商缓存,返回304;不一致则返回新的last-modified和文件并返回 200;

四、 HTTP跨域。

[先看]跨源资源共享(CORS)
所有人都应该知道的跨域及CORS
什么是跨域请求以及实现跨域的方案
HTTP 跨域

跨域问题是由浏览器规定的同源策略引起的,所谓的同源是指,域名、协议、端口均为相同。

同源策略限制了一下行为:

  • Cookie、LocalStorage 和 IndexDB 无法读取。
  • DOM 和 JS 对象无法获取。
  • Ajax请求发送不出去。

因此现在的前后分离开发往往都是跨域通信的。

解决方案
  1. CORS
    CORS是一个W3C标准,全称是"跨域资源共享"。 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
    主要应用http headers Access-Control字段协商解决跨域问题。(安不安全和是否安全由前后协商决定,前后http请求头中表明可接受即认定非跨域。)

分为简单请求和复杂请求两种,简单请求是方法为:GET / HEAD / OPTION的请求,而且规定了可携带的安全请求头和内容类型(content-type),其余都是复杂请求,而且复杂请求在发送前必须发出预检请求检测服务器是否支持。

简单请求中,浏览器会自动为请求头加上Origin字段,而服务器也会根据Origin判断是否是自己支持的域名,返回的响应头带上Access-Control-Allow-Origin。

复杂请求中,预检请求是Option方法,请求头中会带上Origin指明发送主机域名、Access-Control-Request-Method指明即将发送的方法、Access-Control-Request-Headers指明方法中额外带上的请求头字段。
浏览器收到后会返回Access-Control-Allow-Origin说明可接受的域名、Access-Control-Allow-Methods说明服务器可接受方法、Access-Control-Allow-Header说明可接收的请求头、Access-Control-Max-Age说明预检请求的有效时间,在这时间内可以不用重复发起预检请求。

OPTIONS /doc 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
Origin: https://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

HTTP/1.1 204 No Content
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive

  1. JSONP
    JSONP仅能发起GET请求,它主要利用img / script 等标签可以跨域发起请求的特性。
    浏览器解析HTML代码时,原生具有src属性的标签,浏览器都赋予其HTTP请求的能力,而且不受跨域限制,使用src发送HTTP请求,服务器直接返回一段JS代码的函数调用,将服务器数据放在函数实参中,前端提前写好响应的函数准备回调,接收数据,实现跨域数据交互。

  2. 服务器发送请求
    使用nodejs代理服务器或者nginx反向代理。

允许携带身份凭证

服务器响应头:access-control-allow-credentials
客户端请求头设置:withCredentials。

  • 相关响应头
    如果服务端指定了具体的域名而非“*”,那么响应首部中的Vary 字段的值必须包含 Origin。这将告诉客户端:服务器对不同的源站返回不同的内容。
    Access-Control-expose-headers
    Access-Control-Max-Age
    Access-Control-Allow-Methods
    Access-Control-Allow-Headers
    access-control-allow-origin : 指定了允许访问该资源的外域 URI。允许来自哪个URL的请求,参考请求头中的Origin。
    例如,下面的字段值将允许来自 https://mozilla.org 的请求:
Access-Control-Allow-Origin: https://mozilla.org
Vary: Origin
  • 相关请求头
    Origin
    Access-Control-Request-Method
    Access-Control-Request-Headers

五、 HTTPS

HTTPS把 HTTP 下层的传输协议由 TCP/IP 换成了 SSL/TLS,由“HTTP over TCP/IP”变成了“HTTP over SSL/TLS”,让 HTTP 运行在了安全的 SSL/TLS 协议上,收发报文不再使用 Socket API,而是调用专门的安全接口。
HTTP协议笔记_第8张图片

浏览器和服务器在使用 TLS 建立连接时需要选择一组恰当的加密算法来实现安全通信,这些算法的组合被称为“密码套件”(cipher suite,也叫加密套件)。
HTTPS中使用到的是混合加密方式,非对称加密连接,对称加密会话。
非对称加密中涉及到公钥和私钥,公钥加密后只能用私钥解密,私钥加密后也只能用公钥解密。一般非对称加密的密钥很长,加解密非常耗时,因此客户端、服务端建立了连接后就会使用仅有十几位的会话密钥进行对称加密通话了.
HTTP协议笔记_第9张图片

而光有会话机密性仍然不够,还缺少完整性、身份验证。

黑客虽然拿不到会话密钥,无法破解密文,但可以通过窃听收集到足够多的密文,再尝试着修改、重组后发给网站。因为没有完整性保证,服务器只能“照单全收”,然后他就可以通过服务器的响应获取进一步的线索,最终就会破解出明文。
比如黑客截取到一段密文:AABBCCDD,然后他尝试修改:AABBCCD,AABBCC,AABBC,多试n遍之后意识到:这是一个{AA:BB,CC:DD}的对象,接下来,哪怕他并不知道BB是什么值,也可以把BB/DD换成自己的加密数据,再假如AABBCCDD代表着把id为BB的用户的钱转给id为DD的用户,这就出事故了。
另外,黑客也可以伪造身份发布公钥。如果你拿到了假的公钥,混合加密就完全失效了。你以为自己是在和“某宝”通信,实际上网线的另一端却是黑客,银行卡号、密码等敏感信息就在“安全”的通信过程中被窃取了。

身份验证引入了第三方的安全性证书颁发机构CA,用户向它申请一个安全证书,证书包含序列号、用途、颁发者、有效时间等等,把这些打成一个包再签名,完整地证明公钥关联的各种信息,形成“数字证书”。
同时浏览器也会支持向证书机构查询验证签名。
签名和验签操作和通信加密相反,把公钥私钥的用法反过来,之前是公钥加密、私钥解密,现在是私钥加密、公钥解密。
签名和公钥一样完全公开,任何人都可以获取。但这个签名只有用私钥对应的公钥才能解开,拿到摘要后,再比对原文验证完整性,就可以像签署文件一样证明消息确实是你发的。\

TLS的连接流程

TSL包含几个子协议,比如握手协议(Handshake Protocol)、变更密码规范协议(Change Cipher Spec Protocol)等。

TCP连接后,浏览器会先发送一个“Client Hello”的消息,里面有客户端的版本号、支持的密码套件,还有一个随机数(Client Random),用于后续生成会话密钥。

Handshake Protocol: Client Hello
    Version: TLS 1.2 (0x0303)
    Random: 1cbf803321fd2623408dfe…
    Cipher Suites (17 suites)
        Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
        Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)

服务器会返回一个“Server Hello”消息。把版本号对一下,也给出一个随机数(Server Random),然后从客户端的列表里选一个作为本次通信使用的密码套件,再用密码套件规定的加密方式和私钥加密生成数字签名,把这几样东西和证书一起发给浏览器。

浏览器获取到证书后从自己的证书信任链验证合法性,验证成功后使用公钥后解密数字签名,如果信息和证书上的一致,则连接成功。(因为私钥只存在于拥有证书的服务器手上,因此其他人若篡改签名,公钥绝对无法解密成功)

然后开始协商生成会话密钥,浏览器根据约定好的加密套件,生成一个会话密钥,然后使用公钥进行非对称算法加密(比如 RSA、ECDHE)给服务器发回去。

服务器拿到密文后用私钥解密,取出会话密钥。这样,双方就都拥有了“服务器随机数、浏览器随机数、会话密钥”三个随机数,然后再使用约定好的加密算法一阵加密,最终获得了最终的会话密钥。就实现了对称密钥的安全交换,后续就不再使用非对称加密,全都使用对称加密。
HTTP协议笔记_第10张图片

为了防止账号、密码被盗,有的时候(比如网上银行)还会使用 U 盾给用户颁发客户端证书,实现“双向认证”,这样会更加安全。

双向认证的流程也没有太多变化,只是在“Server Hello Done”之后,“Client Key Exchange”之前,客户端要发送“Client Certificate”消息,服务器收到后也把证书链走一遍,验证客户端的身份。

TLS连接耗时怎么优化

  1. 硬件加速,现有使用 SSL 加速服务器,用专门的服务器集群来彻底“卸载”TLS 握手时的加密解密计算。
  2. TLS1.3升级后大幅简化了握手过程,只需要1RTT。
  3. 服务器直接将证书链圈发给客户端,然后客户端收到后再验证,节省了客户端确认的时间。
  4. 会话复用:只要第一次连接成功后就使用一个sessionID记录下,就是客户端和服务器首次连接后各自保存一个会话的 ID 号,内存里存储主密钥和其他相关的信息。当客户端再次连接时发一个 ID 过来,服务器就在内存里找,找到就直接用主密钥恢复会话状态,跳过证书验证和密钥交换,只用一个消息往返就可以建立安全通信。

2、输入网址后到页面显示,浏览器发生了什么

  1. url其实也是发送一个请求,首先查询本地http缓存,看看有没有http缓存这个请求,没有的话就下一步。(http缓存)
  2. DNS解析,把url解析成ip地址。(上面的DNS解析过程)
  3. 对ip进行tcp三次握手连接建立连接通道。
  4. 现在的浏览器一般都是强制进行https链接的,接下来建立ssl/tls连接,双方确认公钥密钥、加密算法。(https)
  5. 发送http请求获取资源。
  6. 获取来的资源根据cache-control响应头决定要不要加入http缓存。
  7. 浏览器解析响应资源,html解析成Dom树,css解析成style tree,这两个是同步进行的,遇到js就会停下来使用js引擎解析,js解析会阻塞html和样式解析。
  8. 把Dom树和style树结合生成render tree。
  9. 接下来进行layout,进行节点位置信息计算。
  10. 最后遍历渲染树,进行绘制,页面就出现在了用户眼前。

3、HTTP的优缺点

优点:

  1. HTTP 以文本方式传输,简单、灵活和易于扩展;
    HTTP的基本报文格式就是’header+body’,头部信息用的也都是常见单词,降低了学习门槛。而且http里中的请求方法、状态码、请求头等等都可以根据开发者需要定制扩写。Http的灵活还体现在没有限制下层传输协议,除了TCP,还可以使用 SSL/TLS,甚至是基于 UDP 的 QUIC.

  2. HTTP 拥有成熟的软硬件环境,应用的非常广泛,是互联网的基础设施;

  3. HTTP 是无状态的,既是缺点也是优点:实现上会简单一些,减轻了服务器状态处理的负担。同时可以轻松实现集群化,扩展性能。缺点在于一些连续行为上可能会造成不便:比如购物 下单 结算 支付的一系列行为,服务器无法知道这是同一个用户的连续关联行为,因此如果需要也可以用 Cookie 技术来实现“有状态”;
    “无状态”表示服务器都是相同的,没有“状态”的差异,所以可以很容易地组成集群,让负载均衡把请求转发到任意一台服务器,不会因为状态不一致导致处理出错,使用“堆机器”的“笨办法”轻松实现高并发高可用。

这个所谓的“状态”应该怎么理解呢?“状态”其实就是客户端或者服务器里保存的一些数据或者标志,记录了通信过程中的一些变化信息。

你一定知道,TCP 协议是有状态的,一开始处于 CLOSED 状态,连接成功后是 ESTABLISHED 状态,断开连接后是 FIN-WAIT 状态,最后又是 CLOSED 状态。这些“状态”就需要 TCP 在内部用一些数据结构去维护,可以简单地想象成是个标志量,标记当前所处的状态,例如 0 是 CLOSED,2 是 ESTABLISHED 等等。

再来看 HTTP,那么对比一下 TCP 就看出来了,在整个协议里没有规定任何的“状态”,客户端和服务器永远是处在一种“无知”的状态。

建立连接前两者互不知情,每次收发的报文也都是互相独立的,没有任何的联系。收发报文也不会对客户端或服务器产生任何影响,连接后也不会要求保存任何信息。

  1. HTTP 是明文传输,数据完全肉眼可见,能够方便地研究分析,但也容易被窃听;
  2. HTTP 是不安全的,无法验证通信双方的身份,也不能判断报文是否被篡改;(身份认证和完整性校验有所欠缺)后面也出现了HTTPS改良安全性的问题。
  3. HTTP 的性能不算差,但不完全适应现在的互联网,还有很大的提升空间。
    因为HTTP使用了请求应答模式,就会造成队头阻塞的问题,当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一并被阻塞,会导致客户端迟迟收不到数据。

队头阻塞可以使用并发连接(每个客户端最多6个长连接)和域名分片(请求不同的域名,但其实不同的域名都指向同一个服务器,长连接的数量就上去了)来解决。

4、 HTTP1.1到2的一些版本更新。

HTTP2的特性解析
HTTP2的优化点主要在性能方面。

  1. 头部压缩
    报文 Header 一般携带的头字段都是固定的,它们多达几百字节甚至上千字节,但 Body 却经常只有几十字节(比如 GET 请求、204/301/304 响应),导致大部分带宽被消耗在了这些冗余度极高的字段上。
    所以HTTP2提出了头部压缩。HTTP2开发了专门的“HPACK”算法,客户端和服务器两端维护一个“字典”,用索引号表示重复的字符串,还釆用哈夫曼编码来压缩整数和字符串,可以达到 50%~90% 的高压缩率。

浏览器和服务器将共同维护一份 常见的头部名称与值 编成的字典,然后http采用特殊的算法压缩时,用索引号去替代字符串,http传送到目的地后再解压。

  1. 二进制分帧
    它把 TCP 协议的部分特性挪到了应用层,把原来的“Header+Body”的消息“打散”为数个小片的二进制“帧”(Frame),用“HEADERS”帧存放头数据、“DATA”帧存放实体数据。 HTTP/2 数据分帧后“Header+Body”的报文结构就完全消失了,协议看到的只是一个个的“碎片”。
    HTTP协议笔记_第11张图片

数据流:用于传输二进制帧提出的概念,它是二进制帧的双向传输序列,同一个消息往返的帧会分配一个唯一的流 ID。你可以把它想象成是一个虚拟的“数据流”,在里面流动的是一串有先后顺序的数据帧,这些数据帧按照次序组装起来就是 HTTP/1 里的请求报文和响应报文。
流(stream):已建立连接上的双向字节流。每个流都有一个唯一的整数标识符(1、2…N);
消息:与逻辑消息对应的完整的一系列数据帧。
帧(frame):HTTP2.0通信的最小单位,每个帧包含帧头部,至少也会标识出当前帧所属的流(stream id)。

  1. 多路复用
    http2的特性;多路复用怎么实现;和长链接的区别
    引入了数据流的概念,将http请求划分成以帧为单位传输,实现了同一个TCP连接上可以同时传输多个请求&响应,对端可以通过帧中的标识知道属于哪个请求。通过这个技术,可以避免 HTTP 旧版本中的队头阻塞问题,极大的提高传输性能。
    不同的流可以穿插传递,但是同一条流的达到顺序必须是有序的,比如 1 号流,流内的 帧必须有序。

与 HTTP/1“并发多个连接”不同,HTTP/2 的“多路复用”特性要求对一个域名(或者 IP)只用一个 TCP 连接,所有的数据都在这一个连接上传输,这样不仅节约了客户端、服务器和网络的资源,还可以把带宽跑满,让 TCP 充分“吃饱”。

  1. 服务端推送
    服务器不再是完全被动地响应请求,也可以新建“流”主动向客户端发送消息。比如,在浏览器刚请求 HTML 的时候就提前把可能会用到的 JS、CSS 文件发给客户端,减少等待的延迟。

HTTP/2 虽然使用“帧”“流”“多路复用”,没有了“队头阻塞”,但这些手段都是在应用层里,而在下层,也就是 TCP 协议里,还是会发生“队头阻塞”。
这是怎么回事呢?让我们从协议栈的角度来仔细看一下。在 HTTP/2 把多个“请求 - 响应”分解成流,交给 TCP 后,TCP 会再拆成更小的包依次发送(其实在 TCP 里应该叫 segment,也就是“段”)。
在网络良好的情况下,包可以很快送达目的地。但如果网络质量比较差,像手机上网的时候,就有可能会丢包。
而 TCP 为了保证可靠传输,有个特别的“丢包重传”机制,丢失的包必须要等待重新传输确认,其他的包即使已经收到了,也只能放在缓冲区里,上层的应用拿不出来,只能“干着急”。
于是Google就又发明了一个新的“QUIC”协议,让 HTTP 跑在 QUIC 上而不是 TCP 上。而这个“HTTP over QUIC”就是 HTTP 协议的下一个大版本,HTTP/3。它在 HTTP/2 的基础上又实现了质的飞跃,真正“完美”地解决了“队头阻塞”问题。
QUIC 选定了 UDP,在它之上把 TCP 的那一套连接管理、拥塞窗口、流量控制等“搬”了过来,“去其糟粕,取其精华”,打造出了一个全新的可靠传输协议,可以认为是“新时代的 TCP”。
HTTP协议笔记_第12张图片

5、HTTP代理 / 缓存代理(具体看博客了里 20220817-CDN相关和HTTP代理 的另一篇文章)

你可能感兴趣的:(计算机网络,http,笔记,网络协议)