Network📌HTTP.txt
超文本传输协议(HTTP)是一个用于传输超媒体文档(例如HTML)的应用层协议。
它是在Web上进行数据交换的基础,是一种client-server协议。
通过TCP或TLS(加密过的TCP连接)来发送,理论上来说可以借助任何可靠的传输协议。
HTTP是无状态协议,这意味着服务器不会在两个请求之间保留任何数据(状态)。
客户端与服务端之间通过交换一个个独立的消息(而非数据流)进行通信。
由客户端发出的消息被称作请求(request),由服务端发出的应答消息被称作响应(response)。
HTTP并不需要底层的传输协议是面向连接的,仅仅需要它是可靠的,或不会丢失消息(至少,某个情况下告知错误)。
在互联网两个最常用的传输协议中,TCP是可靠的而UDP不是。HTTP因此而依靠于TCP的标准,即面向连接的。
在客户端与服务器能够传递请求、响应之前,这两者间必须建立TCP连接,这个过程需要多次往返交互。
HTTP/1.0默认为每一对HTTP请求/响应都打开一个单独的TCP连接。
当需要接连发起多个请求时,工作效率相比于它们之间共享同一个TCP连接要低。
HTTP/2则通过在一个连接中复合多个消息,让这个连接始终活跃并更加高效。
HTTP定义了一组请求方法,以表明要对给定资源执行的操作。指示针对给定资源要执行的期望动作。
GET方法请求一个指定资源的表示形式,使用GET的请求应该只被用于获取数据。
HEAD方法请求一个与GET请求的响应相同的响应,但没有响应体。
POST方法用于将实体提交到指定的资源,通常导致在服务器上的状态变化或副作用。
PUT方法用请求有效载荷替换目标资源的所有当前表示。
DELETE方法删除指定的资源。
CONNECT方法建立一个到由目标资源标识的服务器的隧道。
OPTIONS方法用于描述目标资源的通信选项。
TRACE方法沿着到目标资源的路径执行一个消息环回测试。
PATCH方法用于对资源应用部分修改。
HTTP标头(header)允许客户端和服务器通过HTTP请求(request)或者响应(response)传递附加信息。
一个标头包括它的名称(不区分大小写),一个冒号(:),可选且会被忽略的空格,最后是它的值(例如 Allow: POST)。
自定专用消息头可通过'X-'前缀来添加;但是这种用法被IETF在2012年6月发布的RFC6648中明确弃用,原因是其会在非标准字段成为标准时造成不便。
根据不同的消息上下文,标头可以分为:
请求标头:包含请求的资源,或请求资源的客户端的更多信息。
响应标头:包含有关响应的额外信息,例如响应的位置或者提供响应的服务器。
表示标头:包含资源体的信息,例如其 MIME 类型或者应用的编码/压缩方案。
负载标头:包含与负载数据的形式无关的表示信息,包含内容长度和用于传输的编码。
HTTP缓存会存储与请求关联的响应,并将存储的响应复用于后续请求。
可复用性有几个优点。首先,由于不需要将请求传递到源服务器,因此客户端和缓存越近,响应速度就越快。最典型的例子是浏览器本身为浏览器请求存储缓存。
此外,当响应可复用时,源服务器不需要处理请求——因为它不需要解析和路由请求、根据cookie恢复会话、查询数据库以获取结果或渲染模板引擎。这减少了服务器上的负载。
私有缓存是绑定到特定客户端的缓存——通常是浏览器缓存。由于存储的响应不与其他客户端共享,因此私有缓存可以存储该用户的个性化响应。
共享缓存位于客户端和服务器之间,可以存储能在用户之间共享的响应。共享缓存可以进一步细分为代理缓存和托管缓存。
出于安全性,浏览器限制脚本内发起的跨源HTTP请求。
这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。
跨源资源共享标准新增了一组HTTP标头字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。
CORS预检请求用于检查服务器是否支持CORS协议,并且是否允许使用特定的方法和标头。
Access-Control-Allow-Origin: <origin> | *
origin参数指定了单一的源,告诉浏览器允许该源访问资源。
或者,对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符“*”,表示允许来自任意源的请求。
Access-Control-Expose-Headers: <header-name>[, <header-name>]*
在跨源访问时,XMLHttpRequest对象的getResponseHeader()方法只能拿到一些最基本的响应头,如果要访问其他头,则需要服务器设置本响应头。
Access-Control-Max-Age: <delta-seconds>
指定了preflight请求的结果能够被缓存多久。
Access-Control-Allow-Methods: <method>[, <method>]*
预检请求的响应。其指明了实际请求所允许使用的HTTP方法。
Access-Control-Allow-Headers: <header-name>[, <header-name>]*
用于预检请求的响应。其指明了实际请求中允许携带的首部字段。
Access-Control-Allow-Credentials: true
此时服务器不能将Access-Control-Allow-Xxx值设为通配符“*”。
Cookie是服务器发送到用户浏览器并保存在本地的一小块数据。浏览器会存储cookie并在下次向同一服务器再发起请求时携带并发送到服务器上。
通常,它用于告知服务端两个请求是否来自同一浏览器——如保持用户的登录状态。Cookie使基于无状态的HTTP协议记录稳定的状态信息成为了可能。
Cookie曾一度用于客户端数据的存储,因当时并没有其他合适的存储办法而作为唯一的存储手段,但现在推荐使用现代存储API。
由于服务器指定Cookie后,浏览器的每次请求都会携带Cookie数据,会带来额外的性能开销(尤其是在移动环境下)。
新的浏览器API已经允许开发者直接将数据存储到本地,如使用storage(localStorage和sessionStorage)或IndexedDB。
HTTPS是HTTP协议的加密版本。它使用SSL或TLS协议来加密客户端和服务器之间所有的通信。
设计目标主要有三个:
(1)数据保密性:保证数据内容在传输的过程中不会被第三方查看。
(2)数据完整性:及时发现被第三方篡改的传输内容。
(3)身份校验安全性:保证数据到达用户期望的目的地。
* 在TLS1.3中,废除了RSA和DH算法,使用了更加安全的ECDHE。
一次完整的https请求过程:
1. DNS查询
根据优先级从浏览器DNS缓存、操作系统的DNS缓存、hosts文件、电信运营商的本地DNS缓存、运营商的根DNS服务器获取IP。
2. 三次Handshake建立TCP连接
3. 密钥协商(以ECDHE四次Handshake为例)
(1) c->s(client-hello): 支持的密码套件列表、TLS版本号、随机数client-random
(2) s->c(server-hello): 选择的密码套件、确认TLS版本号、随机数server-random
s->c(certificate): 发送数字证书
s: 生成一对椭圆曲线公私钥spub和spri
s->c(server-key-exchange): 发送用RSA加密的spub
s->c(server-hello-done): 招呼完毕
(3) c: 校验证书
c: 生成一对椭圆曲线公私钥cpub和cpri
c->s(client-key-exchange): 发送用RSA加密的cpub
c->s(change-cipher-spec): 告诉服务端后续改用对称加密通信
c: cpri+spub算出预主密钥pre-master,client-random+server-random+pre-master生成主密钥
c->s(encrypted-handshake-message): 发送密钥加密后的Handshake数据摘要
(4) s->c: (change-cipher-spec): 告诉服务端后续改用对称加密通信
s: spri+cpub算出预主密钥pre-master,client-random+server-random+pre-master生成主密钥
s->c: (encrypted-handshake-message): 发送密钥加密后的Handshake数据摘要
4. 使用密钥进行加密数据传输
如果使用了RSA密钥协商算法,TLS完成四次Handshake后,才能进行应用数据传输;
而对于ECDHE算法,客户端可以不用等服务端的最后一次TLSHandshake,就可以提前发出加密的数据。
5. 四次Handshake断开TCP连接