HTTP 知识点总结(个人整理_彦超)

HTTP

结构及字段

HTTP报文结构

起始行 + 头部 + 空行 + 实体

起始行

 GET /home HTTP/1.1 方法 + 路径 + http版本。(请求报文)
 HTTP/1.1 200 OK http版本 + 状态码 + 原因。(响应报文)

头部


空行

用来区分开头部和实体。
如果说在头部中间故意加一个空行,那么空行后的内容全部被视为实体

实体

就是具体的数据了,也就是body部分。请求报文对应请求体, 响应报文对应响应体

GET 和 POST 有什么区别?

1、从缓存的角度,GET 请求会被浏览器主动缓存下来,留下历史记录,而 POST 默认不会。
2、从编码的角度,GET 只能进行 URL 编码,只能接收 ASCII 字符(encodeURI()编码),而 POST 没有限制。
3、从参数的角度,GET 一般放在 URL 中,因此不安全,POST 放在请求体中,更适合传输敏感信息。
4、从TCP的角度,GET 请求会把请求报文一次性发出去,而 POST 会分为两个 TCP 数据包,
   首先发 header 部分,如果服务器响应 100(continue), 然后发 body 部分。

ncodeURI()编码?

将所有非 ASCII 码字符和界定符转为十六进制字节值,然后在前面加个%。
如:空格被转义成了%20,三元被转义成了%E4%B8%89%E5%85%83。

HTTP 状态码

301、308是永久重定向;302、303、307是临时重定向。
204 No Content含义与 200 相同,但响应头后没有 body 数据。
304 Not Modified: 当协商缓存命中时会返回这个状态码
403 Forbidden: 这实际上并不是请求报文出错,而是服务器禁止访问
408 Request Timeout: 服务器等待了太长时间。
503 Service Unavailable: 表示服务器当前很忙,暂时无法响应服务。

HTTP 特性

优点:

	语义上的自由,只规定了基本格式,其他的各个部分都没有严格的语法限制
	传输形式的多样性,不仅仅可以传输文本,还能传输图片等
	HTTP 基于 TCP/IP
	请求-应答
	无状态

缺点:

	无状态 需要保存大量的上下文信息,会传输大量重复的信息
	明文传输 即协议里的报文(主要指的是头部)不使用二进制数据,而是文本形式。
	队头阻塞问题 当 http 开启长连接时,共用一个 TCP 连接,同一时刻只能处理一个请求,那么当前请求耗时过长的情况下,其它的请求只能处于阻塞状态

Accept字段

数据格式 + 压缩方式 + 支持语言 + 字符集

数据格式:

	HTTP从MIME type获取了一部分来标记报文 body 部分的数据类型反映在Content-Type字段
	前端想要收到特定类型的数据,也可以用Accept字段。

压缩方式:

	数据都是会进行编码压缩的,采取什么样的压缩方式就体现在了发送方的Content-Encoding字段上
	同样的,接收什么样的压缩方式体现在了接受方的Accept-Encoding字段上。
		gzip: 当今最流行的压缩格式
		deflate: 另外一种著名的压缩格式
		br: 一种专门为 HTTP 发明的压缩算法

支持语言:

	对于发送方而言,还有一个Content-Language字段,在需要实现国际化的方案当中,可以用来指定支持的语言,在接受方对应的字段为Accept-Language

字符集:

	在接收端对应为Accept-Charset,指定可以接受的字符集,而在发送端并没有对应的Content-Charset, 而是直接放在了Content-Type中,以charset属性指定
	// 发送端Content-Type: text/html; charset=utf-8
	// 接收端Accept-Charset: charset=utf-8

HTTP 如何处理大文件的传输

必须加上这样一个响应头:Accept-Ranges: none(告知客户端这边是支持范围请求)

单段数据
	HTTP/1.1 206 Partial Content
	Content-Length: 10
	Accept-Ranges: bytes=0-9
	Content-Range: bytes 0-9/100
多段数据
	HTTP/1.1 206 Partial Content
	Content-Type: multipart/byteranges; boundary=00000010101
	Content-Length: 189
	Connection: keep-alive
	Accept-Ranges: bytes
	其中Content-Type: multipart/byteranges;boundary=00000010101,它代表了信息量是这样的:
		请求一定是多段数据请求,响应体中的分隔符是 00000010101

HTTP传输定长和不定长的数据

定长包体

	发送端在传输的时候一般会带上 Content-Length, 来指明包体的长度(如果长度小了就会截取,长了报错)

不定长包体 (长连接)

	设置 Transfer-Encoding: chunked 表示分块传输数据
	 效果:
		Content-Length 字段会被忽略
		基于**长连接**持续推送动态内容

HTTP1.1 如何解决 HTTP 的队头阻塞问题

HTTP 传输是基于请求-应答的模式进行的,报文必须是一发一收,但值得注意的是,里面的任务被放在一个任务队列中串行执行,一旦队首的请求处理太慢,就会阻塞后面请求的处理
并发连接
	对于一个域名允许分配多个长连接,那么相当于增加了任务队列,不至于一个队伍的任务阻塞其它所有任务。
域名分片
	一个域名可以并发 6 个长连接,所以就多分几个域名。且都指向同一个服务器

HTTP 代理

作为代理的服务器相当于一个中间人的角色,对于客户端而言,表现为服务器进行响应;而对于源服务器,表现为客户端发起请求,具有双重身份。

功能

负载均衡
	客户端的请求只会先到达代理服务器,后面到底有多少源服务器,IP 都是多少,客户端是不知道的。
	因此,这个代理服务器可以拿到这个请求之后,可以通过特定的算法分发给不同的源服务器,让各台源服务器的负载尽量平均。
保障安全
	利用心跳机制监控后台的服务器,一旦发现故障机就将其踢出集群。
	并且对于上下行的数据进行过滤,对非法 IP 限流,这些都是代理服务器的工作
缓存代理
	将内容缓存到代理服务器,使得客户端可以直接从代理服务器获得而不用到源服务器那里

相关头部字段

Via

	代理服务器通过Via字段来标明自己的身份
	客户端 -> 代理1 -> 代理2 -> 源服务器
	在源服务器收到请求后,会在请求头拿到这个字段:
	Via: proxy_server1, proxy_server2
	而源服务器响应时,最终在客户端会拿到这样的响应头:
	Via: proxy_server2, proxy_server1
	Via中代理的顺序即为在 HTTP 传输中报文传达的顺序。

X-Forwarded-For

	字面意思就是为谁转发, 它记录的是请求方的IP地址
	因为X-Forwarded-For记录的值会因为请求方的ip而变化,所以有代理协议
	 // PROXY + TCP4/TCP6 + 请求方地址 + 接收方地址 + 请求端口 + 接收端口
	 PROXY TCP4 0.0.0.1 0.0.0.2 1111 2222
	 GET / HTTP/1.1
	 ...

X-Real-IP

	是一种获取用户真实 IP 的字段,不管中间经过多少代理,这个字段始终记录最初的客户端的IP。

http缓存

Cache-Control

	private: 这种情况就是只有浏览器能缓存了,中间的代理服务器不能缓存。
	no-cache: 跳过当前的强缓存,发送HTTP请求,即直接进入协商缓存阶段。
	no-store:非常粗暴,不进行任何形式的缓存。
	s-maxage:针对代理服务器的缓存时间
	max-age: 过期时长来控制缓存
	(早期使用Expires: Wed, 22 Nov 2019 08:41:00 GMT,但是忽略了服务器时间和浏览器时间不一致的问题)

强缓存

	服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行比较缓存策略。

协商缓存(ETag> Last-Modified)

	将缓存信息中的Etag和Last-Modified通过请求发送给服务器,由服务器校验,返回304状态码时,浏览器直接使用缓存。
Last-Modified
	浏览器接收到后,如果再次请求,会在请求头中 携带 If-Modified-Since字段,
    这个字段的值也就是服务器传来的最后修改时间,拿到请求头中的
    If-Modified-Since的字段后,其实会和这个服务器中该资源的最后修改时间对比
ETag
	是服务器根据当前文件的内容,给文件生成的唯一标识,只要里面的内容有改动,这个值就会变。
    服务器通过响应头把这个值给浏览器。 浏览器接收到ETag的值,会在下次请求时,
    将这个值作为If-None-Match这个字段的内容,并放到请求头中,然后发给服务器。

对比

精准度
	ETag优于Last-Modified
		ETag 是按照内容给资源上标识,因此能准确感知资源的变化。
		Last-Modified 在一些特殊的情况并不能准确感知资源变化,主要有两种情况:
		  1、编辑了资源文件,但是文件内容并没有更改,这样也会造成缓存失效。
		  2、Last-Modified 能够感知的单位时间是秒,改变频率过快 Last-Modified 体现不出修改了。		  
性能
	Last-Modified优于ETag
		Last-Modified只是记录一个时间点
		Etag需要根据文件的具体内容生成哈希值。
缓存位置
	Memory Cache 内存中(又快又短)
	Disk Cache 磁盘中(又慢又长)
	Service Worker JS 运行在主线程之外(pwa应用)
	Push Cache (http/2应用)

总结

	首先通过 Cache-Control 验证强缓存是否可用
	如果强缓存可用,直接使用
	否则进入协商缓存,即发送 HTTP 请求,
	服务器通过请求头中的 
	
	If-Modified-Since(Last-Modified类型)
	If-None-Match(ETag类型)
	 
	条件请求字段检查资源是否更新
	若资源更新,返回资源和200状态码
	否则,返回304,告诉浏览器直接从缓存获取资源

输入URL到页面呈现发生了什么

网络层

网络请求

1. 构建请求
	例: GET / HTTP/1.1
2. 查找强缓存
	如果强缓存可用,直接使用
3. DNS解析
	将域名解析成ip地址
	特例: 一个域名已经解析过,浏览器会把解析的结果缓存下来,下次处理直接走缓存,不需要经过 DNS解析
4. 建立 TCP 连接(三次握手四次挥手)
	Chrome 在同一个域名下要求同时最多只能有 6 个 TCP 连接,超过 6 个的话剩下的请求就得等待。
	第一次握手:
		建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;
	
	第二次握手:
		服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),
		即SYN+ACK包,此时服务器进入SYN_RECV状态;
	
	第三次握手:
		客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),
		此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
	半连接队列和 SYN Flood 攻击:
		三次握手完成前,服务端的状态从CLOSED变为LISTEN, 同时在内部创建了两个队列:
			半连接队列:客户端发送SYN到服务端,服务端收到以后回复ACK和SYN,此时就是半连接队列。
			全连接队列:三次握手完成后,此时就是全连接队列。
		SYN Flood 攻击原理:
			属于 DoS/DDoS 攻击。客户端在短时间内伪造大量不存在的 IP 地址,并向服务端疯狂发送SYN
			服务端:处理大量的SYN包并返回对应ACK, 势必有大量连接处于SYN_RCVD状态,从而占满整个半连接队列
			服务端:服务端收不到客户端的ACK,不断重发数据,直到耗尽服务端的资源。
	如何应对 SYN Flood 攻击?
		增加 SYN 连接,也就是增加半连接队列的容量
		减少 SYN + ACK 重试次数,避免大量的超时重发。
		用 SYN Cookie 技术(也可以用 SYN Cookie 实现TFO(tcp快速打开))
5.发送 HTTP 请求
	请求行:请求方法 请求url http版本
	请求头:cache-control if-modified-since
	请求体:常见表单提交等

解析层

构建 DOM 树

标记化算法
	输入为HTML文本,输出为HTML标记
	 遇到<, 状态为  标记打开。
	 接收[a-z]的字符,会进入  标记名称状态。
	 遇到>,表示标记名称记录完成,这时候变为  数据状态。
建树算法
	DOM 树是一个以document为根节点的多叉树。因此解析器首先会创建一个document对象。
	标记生成器会把每个标记的信息发送给建树器。建树器接收到相应的标记时,会创建对应的 DOM 对象。
	创建这个DOM对象后会做两件事情:
	  将DOM对象加入 DOM 树中。
	  将对应标记压入存放开放(与闭合标签意思对应)元素的栈中。(父级子级自上而下的栈结构)
容错机制
	会将不严谨的写法在内部使用HTML Parser转换为标准语法
容样式计算
	css在解析时遵循 继承 和 层叠 
生成布局树
	遍历生成的 DOM 树节点,并把他们添加到布局树中。
	计算布局树节点的坐标位置。

JS代码在V8中执行
	1、生成了 AST (类对象类型)
	2、将 AST 通过 V8 的解释器(也叫Ignition)转换为字节码
	3、在执行字节码的过程中,如果发现某一部分代码重复出现,那么 V8 将它记做热点代码(HotSpot),
	   然后将这么代码编译成机器码保存起来,这个用来编译的工具就是V8的编译器,代码执行的时间越久,那么执行效率会越来越高
  //通过解释器逐行解释代码,省去了一次性将全部的字节码都转换成机器码大大降低了内存的压力

建图层树

在绘制页面之前,还会对特定的节点进行分层,构建一棵图层树(Layer Tree)。

合成层

	优点:合成层的位图,会交由 GPU 合成,比 CPU 处理要快
	缺点:由于某些原因可能导致产生大量不在预期内的合成层,会导致层爆炸的现象
1. 显式合成
	一、 拥有层叠上下文的节点
		普通元素设置position不为static并且设置了z-index属性,会产生层叠上下文。
	    元素的 transform 值不是 none
	    元素的 filter 值不是 none
	二、需要剪裁的地方。
		比如一个div,你只给他设置 100 * 100 像素的大小,而你在里面放了非常多的文字,
		那么超出的文字部分就需要被剪裁。当然如果出现了滚动条,那么滚动条会被单独提升为一个图层。
2. 隐式合成
	如简单来说就是层叠等级低的节点被提升为单独的图层之后,那么所有层叠等级比它高的节点都会成为一个单独的图层。

生成绘制列表

	接下来渲染引擎会将图层的绘制拆分成一个个绘制指令,比如先画背景、再描绘边框......
	然后将这些指令按顺序组合成一个待绘制列表,相当于给后面的绘制操作做了一波计划。

生成图块和生成位图

	开始绘制操作由专门的线程来完成的,这个线程叫合成线程。当页面非常大的时候,要滑很长时间才能滑到底,
	合成线程就是将图层分块。这样可以大大加速页面的首屏展示。
	
	Chrome 采用了一个策略: 
	在首次合成图块时只采用一个低分辨率的图片,这样首屏展示的时候只是展示出低分辨率的图片,
	这个时候继续进行合成操作,当正常的图块内容绘制完毕后,会将当前低分辨率的图块内容替换。
	这也是 Chrome 底层优化首屏加载速度的一个手段。

显示器显示内容

	合成线程会生成一个绘制命令,即"DrawQuad"
		把页面内容绘制到内存,也就是生成了页面,然后把这部分内存发送给显卡。
		
		无论是 PC 显示器还是手机屏幕,都有一个固定的刷新频率,一般是 60 HZ,即 60 帧,也就是一秒更新 60 张图片
		一张图片停留的时间约为 16.7 ms。而每次更新的图片都来自显卡的前缓冲区。而显卡接收到浏览器进程传来的页面后,
		会合成相应的图像,并将图像保存到后缓冲区,然后系统自动将前缓冲区和后缓冲区对换位置,如此循环更新。
	
	  开启 GPU 加速页面渲染 :will-change: transform;	
	  原理 将 CPU 消耗高的渲染元素提升为一个新的合成层,才能开启 GPU 加速的
	// 请求头
	Cookie: a=xxx;b=xxx
	// 响应头
	Set-Cookie: a=xxx
	set-Cookie: b=xxx

Expires : 设置过期时间 ; Max-Age : 时间间隔

作用域

	Domain和path, 给 Cookie 绑定了域名和路径,在发送请求之前,发现域名或者路径和这两个属性不匹配,
	那么就不会带上 Cookie。值得注意的是,对于路径来说,/表示域名下的任意路径都允许使用 Cookie。

安全相关

	1、如果带上Secure,说明只能通过 HTTPS 传输 cookie。
	2、如果 cookie 字段带上HttpOnly,那么说明只能通过 HTTP 协议传输,不能通过 JS 访问,这也是预防 XSS 
	3、如果 cookie 字段带上SameSite属性,可以防御CSRF 攻击(可以设置是否禁止第三方请求携带Cookie)
			Strict:Scrict最为严格,完全禁止第三方Cookie,跨站点时,任何情况下都不会发送Cookie
			Lax:Lax规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。
			None:None网站可以选择显式关闭SameSite属性,将其设为None。

缺点

	1、容量缺陷。Cookie 的体积上限只有4KB,只能用来存储少量的信息。
	2、性能缺陷。Cookie 紧跟域名,不管域名下面的某一个地址需不需要这个 Cookie ,请求都会携带上完整的 Cookie
	3、安全缺陷 CSRF和XSS 攻击

CORS 如何指定多个域名?
总是设置 Vary: Origin,避免 CDN 缓存破坏 CORS 配置
如果请求头不带有 Origin,证明未跨域,则不作任何处理
如果请求头带有 Origin,证明浏览器访问跨域,根据 Origin 设置相应的 Access-Control-Allow-Origin:

SSL/TLS 协议

HTTPS = HTTP + SSL/TLS。
传统RAS即"公钥加密算法"

TLS 1.2

	(哈希摘要 + 私钥加密 + 公钥解密)
	所有的随机数都是通过 ECDHE算法 生成,基于椭圆曲线离散对数

step 1: Client Hello

	客户端生成的随机数 Random1 + 客户端支持的加密套件(Support Ciphers)+ SSL版本 等信息

step 2: Server Hello

	服务端确定一份加密套件 + 服务端的server证书 + 服务端生成的随机数 Random2

step 3: Client 验证证书,生成secret

	验证服务端传来的证书和签名
	用证书中取出公钥加密,再生成一个随机数 Random3,发送给服务器;
	此时客户端和服务端都拥有 Random1 + Random2 + Random3 三个数通过一个伪随机数函数来计算出最终的secret开始对称加密

step4: Server 生成 secret

	拿到 Random1 + Random2 + Random3用和客户端同样的伪随机数函数生成最后的secret,开始对称加密


RSA身份验证的隐患

	身份验证和密钥协商是TLS的基础功能,要求的前提是合法的服务器掌握着对应的私钥。
	但RSA算法无法确保服务器身份的合法性,因为公钥并不包含服务器的信息,存在安全隐患:

其他加密

散列函数Hash

	常见的有 MD5、SHA1、SHA256,该类函数特点是函数单向不可逆、对输入非常敏感、输出长度固定,
	针对数据的任何修改都会改变散列函数的结果,用于防止信息篡改并验证数据的完整性;

对称加密

	常见的有 AES-CBC、DES、3DES、AES-GCM等,相同的密钥可以用于信息的加密和解密,
	掌握密钥才能获取信息,能够防止信息窃听,通信方式是1对1;(密码本)

非对称加密

	RSA 算法,密钥成对出现,一般称为公钥(公开)和私钥(保密),公钥加密的信息只能私钥解开,私钥加密的信息只能公钥解开。
	因此掌握公钥的不同客户端之间不能互相解密信息,只能和掌握私钥的服务器进行加密通信,
	服务器可以实现1对多的通信,客户端也可以用来验证掌握私钥的服务器身份。

网络安全

XSS攻击

反射型XSS
反射型 XSS 漏洞常见于通过 URL 传递参数的功能,如网站搜索、跳转等
	攻击者构造出特殊的 URL,其中包含恶意代码。
	用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
方法:url的查询参数进行转义后再输出到页面 `res.send(`${encodeURIComponent(req.query.type)}`)`
DOM 型 XSS
前端 JavaScript 代码不够严谨,把不可信的内容插入到了页面
	用户浏览器执行了恶意代码。
	攻击者构造出特殊数据,其中包含恶意代码。
方法:使用 encodeURIComponent 来转义,或者进行编码
存储型XSS
恶意脚本永久存储在目标服务器上。当浏览器请求数据时,脚本从服务器传回并执行,
	攻击者将恶意代码提交到目标网站的数据库中。
	用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
方法:前端数据传递给服务器之前,先转义/过滤(防范不了抓包修改数据的情况)
	 后端设置HTTP-only Cookie: 禁止 JavaScript 读取某些敏感 Cookie
	 前端设置限定不能包含特殊字符或者仅能输入数字等

CSRF攻击

跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。
利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。

方法:1、使用Token(主流) 2、添加验证码 3、cookie 字段带上SameSite属性

你可能感兴趣的:(HTTP 知识点总结(个人整理_彦超))