身为开发人员除了应该对我们所写的项目需求要了解, 以及基本的语言知识, 对于 HTTP 协议也是应该了解一下的, 因为这些东西与我们是密不可分的, 每天都在和 HTTP 打交道然而却不知道它到底是什么? 这样说出去是不是很可悲? 简直可歌可泣有没有...
http 协议 https://baike.baidu.com/item/HTTP/243074 :HTTP 是一个简单的请求 - 响应协议, 它通常运行在 TCP 之上. 它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应. 请求和响应消息的头以 ASCII 码形式给出; 而消息内容则具有一个类似 MIME 的格式. 这个简单模型是早期 web 成功的有功之臣, 因为它使得开发和部署是那么的直截了当. 超文本传输协议 (HTTP) 是用于传输诸如 html 的超媒体文档的应用层协议. 它被设计用于 Web 浏览器和 Web 服务器之间的通信, 但它也可以用于其他目的. HTTP 遵循经典的客户端 - 服务端模型, 客户端打开一个连接以发出请求, 然后等待它收到服务器端响应. -- 百度百科
HTTP 是无状态协议, 意味着服务器不会在两个请求之间保留任何数据(状态). 在同一个连接中, 两个执行成功的请求之间是没有关系的. 这就带来了一个问题, 用户没有办法在同一个网站中进行连续的交互, 比如在一个电商网站里, 用户把某个商品加入到购物车, 切换一个页面后再次添加了商品, 这两次添加商品的请求之间没有关联, 浏览器无法知道用户最终选择了哪些商品. 而使用 HTTP 的头部扩展, HTTP Cookies 就可以解决这个问题. 把 Cookies 添加到头部中, 创建一个会话让每次请求都能共享相同的上下文信息, 达成相同的状态. 通过上述得出结论, http 特点是: 无状态, 无连接, 简单快速.
HTTP 交互流程
一个连接是由传输层来控制的, 这从根本上不属于 HTTP 的范围. HTTP 并不需要其底层的传输层协议是面向连接的, 只需要它是可靠的, 或不丢失消息的(至少返回错误). 在互联网中, 有两个最常用的传输层协议: TCP 是可靠的, 而 UDP 不是. 因此, HTTP 依赖于面向连接的 TCP 进行消息传递, 但连接并不是必须的.
TCP: 面向连接(如打电话要先拨号建立连接)
UDP: 是无连接的, 即发送数据之前不需要建立连接
关于 TCP 和 UDP 这里不做多余赘述, 如果想要深入了解两者之间的优缺点以及区别的话, 有时间再详细的介绍一下.
其实 HTTP 交互流程就是基于 TCP 连接进行消息传递的, 然而这个连接可有可无, 具体交互流程如下图:
结合上图详细说明经历的过程:
打开一个 TCP 连接: TCP 连接被用来发送一条或多条请求, 以及接受响应消息. 客户端可能打开一条新的连接, 或重用一个已经存在的连接, 或者也可能开几个新的 TCP 连接连向服务端
发送一个 HTTP 报文: HTTP 报文 (在 HTTP/2 之前) 是语义可读的. 在 HTTP/2 中, 这些简单的消息被封装在了帧中, 这使得报文不能被直接读取, 但是原理仍是相同的
读取服务端返回的报文信息, 服务器端接收到请求后, 进行处理, 然后将处理结果响应客户端(HTTP 协议)
关闭连接或者为后续请求重用连接, 关闭客户端和服务器端的连接(HTTP1.1 后不会立即关闭)
当 HTTP 流水线启动时, 后续请求都可以不用等待第一个请求的成功响应就被发送. 然而 HTTP 流水线已被证明很难在现有的网络中实现, 因为现有网络中有很多老旧的软件与现代版本的软件共存. 因此, HTTP 流水线已被在有多请求下表现得更稳健的 HTTP/2 的帧所取代.
HTTP 报文
在 Linux 系统下有一个 curl 指令可以通过这个命令来观测一下 HTTP 的请求过程.
curl -v https://segmentfault.com/
输入完之后回车就会看到下面这些信息:
图中>开始的是客服端发送给服务端的信息, 以<开始的为服务端返回给客户端的一些信息. 当客户端发起一个 Ajax 请求时, 浏览器会携带一些信息发送给服务端, HTTP 请求头提供了关于请求, 响应或者其他的发送实体的信息. 请求报文分为以下几个部分:
- General(请求行)
- Response Headers(请求头)
- Request Headers(响应头)
这三个部分分别承载了服务端以及客户端所需要的信息, 在浏览器中种 NetWork 中可以查看到其信息内容, 接下来就一一介绍一下:
General
这部分主要提供的是一些公用的请求头信息:
- Request URL: https://segmentfault.com/search?q=search
- Request Method: GET
- Status Code: 200
- Remote Address: 112.126.83.219:443
- Referrer Policy: no-referrer-when-downgrade
Request URL: 请求地址
Request Method: 请求方式
Status Code: 状态码
Remote Address: 请求的远程地址
Referrer Policy: 过滤 Referrer 报头内容
上述信息表明, 客户端向服务器发送一个 http 请求, 其请求地址为 https://segmentfault.com/search?q=search, 使用 GET 方式发起这个请求, 请求返回状态为 200, 连接的远程地址为 112.126.83.219:443, 过滤报头采用的是不传递 Referrer.
向前面几个应该都比较熟悉也通俗易懂, 当看到的这的时候心里有一些些的小疑惑 Remote Address 是什么? Referrer Policy 过滤报头的规则有哪些?
Remote Address
关于 Remote Address 远程连接地址, Remote Address 代表客户端的 IP, 但它的值不是由客户端提供的, 而是服务端根据客户端的 IP 指定的, 当你的浏览器访问某个网站时, 假设中间没有任何代理, 那么网站的 Web 服务器 (Nginx,Apache 等) 就会把 Remote Address 设为你的机器 IP, 如果你用了某个代理, 那么你的浏览器会先访问这个代理, 然后再由这个代理转发到网站, 这样 Web 服务器就会把 Remote Address 设为这台代理机器的 IP. 以至于后面的 443 端口, 也简单的看了一下, 443 端口即网页浏览端口, 主要是用于 HTTPS 服务, 是提供加密和通过安全端口传输的另一种 HTTP. 在一些对安全性要求较高的网站, 比如银行, 证券, 购物等, 都采用 HTTPS 服务, 这样在这些网站上的交换信息, 其他人抓包获取到的是加密数据, 保证了交易的安全性. 我也没有做深入的了解, 大概就是这个样子吧.
Referrer Policy
Referrer-Policy 的作用就是为了控制请求头中 referrer 的内容, 目前是一个候选标准, 不过已经有部分浏览器支持该标准. 目前 Referrer-Policy 只包含以下几种值:
值 | 解释 |
---|---|
no-referrer | 不显示 referrer 的任何信息在请求头中。 |
no-referrer-when-downgrade | 这是默认值。当从 https 网站跳转到 http 网站或者请求其资源时(安全降级 HTTPS→HTTP),不显示 referrer 的信息,其他情况(安全同级 HTTPS→HTTPS,或者 HTTP→HTTP)则在 referrer 中显示完整的源网站的 URL 信息。 |
same-origin | 表示浏览器只会显示 referrer 信息给同源网站,并且是完整的 URL 信息。所谓同源网站,是协议、域名、端口都相同的网站。 |
origin | 表示浏览器在 referrer 字段中只显示源网站的源地址(即协议、域名、端口),而不包括完整的路径。 |
strict-origin | 该策略更为安全些,和 origin 策略相似,只是不允许 referrer 信息显示在从 https 网站到 http 网站的请求中(安全降级)。 |
origin-when-cross-origin | 当发请求给同源网站时,浏览器会在 referrer 中显示完整的 URL 信息,发个非同源网站时,则只显示源地址(协议、域名、端口) |
strict-origin-when-cross-origin | 和 origin-when-cross-origin 相似,只是不允许 referrer 信息显示在从 https 网站到 http 网站的请求中(安全降级)。 |
unsaft-url | 浏览器总是会将完整的 URL 信息显示在 referrer 字段中,无论请求发给任何网站。 |
Referrer-Policy 值不是固定不变的, 而是可是通过程序手动设置, 一般都会不会去手动更改除非网页中不存在一些敏感信息, 那就默认使用 no-referrer-when-downgrade. 这里就多说了, 如果有兴趣的可以调研一下.
Response Headers
这部分存储的是响应头信息, 当服务端接受到请求, 并处理完成之后需要向客户端做出应答.
- cache-control: no-store, no-cache, must-revalidate
- content-encoding: gzip
- content-type: text/HTML; charset=UTF-8
- date: Fri, 28 Jun 2019 09:32:09 GMT
- expires: Thu, 19 Nov 1981 08:52:00 GMT
- pragma: no-cache
- status: 200
- strict-transport-security: max-age=15768000; preload
- x-hit: web1
cache-control: 响应输出到客户端后, 服务端通过该报文头告诉客户端如何控制响应内容的缓存
content-encoding: 文档编码 (Encode) 方法. 只有在解码之后才可以得到 Content-Type 头指定的内容类型
content-type: 文档类型
date: 当前的 GMT 时间, 可以用 setDateHeader 来设置
expires: 文档过期时间, 文档到期后则不再缓存
pragma: 设置消息头,(no-cache)强制清除缓存
status: 服务器响应状态码
strict-transport-security: 安全功能, 它告诉浏览器只能通过 HTTPS 访问当前资源, 而不是 HTTP
Request Headers
这部分承载的是请求头的信息, 当客户端向服务端发送请求时, 需要传递给服务端的信息内容.
- :authority: segmentfault.com
- :method: GET
- :path: /search?q=1
- :scheme: https
- accept: text/HTML,application/xhtml+xml,application/xml;
- accept-encoding: gzip, deflate, br
- accept-language: zh-CN,zh;q=0.9
- cookie: e23800c454aa573c0ccb16b52665ac26=1561712973
- referer: https://segmentfault.com/
- user-agent: Chrome/75.0.3770.100 Safari/537.
:authority: 请求权限(HTTP2.0)
:method: 请求方式(HTTP2.0)
:path: 请求地址(HTTP2.0)
:scheme: 请求协议(HTTP2.0)
accept: 指定客户端可以接受的内容类型, 比如文本, 图片, 应用等等, 内容的先后排序表示客户端接收的先后次序, 每种类型之间用逗号隔开.
accept-encoding: 客户端接收编码类型, 一些网络压缩格式: Accept-Encoding: gzip, deflate, sdch. 相对来说, deflate 是一种过时的压缩格式, 现在常用的是 gzip
accept-language: 客户端可以接受的语言类型, 参数值规范和 accept 的很像. 一般就接收中文和英文, 有其他语言需求自添加.
- const express = require('express');
- const cookieParser = require('cookie-parser');
- var App = express();
- App.use(cookieParser('sign'));
- App.get('/set', function(req, res) {
- res.cookie('name', 'TracyYu', {maxAge: 9999999, httpOnly: true, signed: true});
- res.send('cookie 设置成功');
- })
- App.get('/get', function(req, res) {
- console.log(req.signedCookies);
- res.send('success')
- })
- App.listen('3000', function() {
- console.log('3000 成功');
- })
来源: https://segmentfault.com/a/1190000019624321