超文本传输协议(HyperText Transfer Protocol)是一种无状态的,以请求/应答方式运行的协议
请求行由请求
Method,URL字段和HTTP Version三部分构成, 总的来说请求就是定义了本次请求的请求方式, 请求的地址, 以及所遵循的HTTP协议版本例如: GET /example.html HTTP/1.1 (CRLF) HTTP协议的方法有:GET: 请求获取Request-URI所标识的资源POST: 在Request-URI所标识的资源后增加新的数据HEAD: 请求获取由Request-URI所标识的资源的响应消息报头PUT: 请求服务器存储或修改一个资源,并用Request-URI作为其标识DELETE: 请求服务器删除Request-URI所标识的资源TRACE: 请求服务器回送收到的请求信息,主要用于测试或诊断CONNEC: 保留将来使用OPTIONS: 请求查询服务器的性能,或者查询与资源相关的选项和需求
消息报头由一系列的键值对组成,允许客户端向服务器端发送一些附加信息或者客户端自身的信息,主要包括:
只有在发送
POST请求时才会有请求正文,GET方法并没有请求正文。
HTTP响应也由三部分组成,包括状态行,消息报头,响应正文。
响应行也由三部分组成,包括HTTP协议的版本,状态码,以及对状态码的文本描述。例如:
HTTP/1.1 200 OK (CRLF)1xx:指示信息--表示请求已接收,继续处理 2xx:成功--表示请求已被成功接收、理解、接受 3xx:重定向--要完成请求必须进行更进一步的操作 4xx:客户端错误--请求有语法错误或请求无法实现 5xx:服务器端错误--服务器未能实现合法的请求
如果没有设置http缓存,浏览器发送请求但是返回的数据和上次的一样,返回状态码:304 Not Modified;如果设置了http缓存,如果命中强缓存会返回200 OK (from disk cache,from memory cache等);如果命中协商缓存,会返回304 Not Modified
请求报文和响应报文都会使用的首部字段。
实体首部字段是包含在请求报文和响应报文中的实体部分所使用的首部。
http缓存机制主要在http响应头中设定,响应头中相关字段为Expires、Cache-Control、Last-Modified、If-Modified-Since、Etag。
不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的Network选项中可以看到该请求返回200的状态码,并且size显示from disk cache或from memory cache两种(灰色表示缓存)。
区别:Expires 是http1.0的产物,Cache-Control是http1.1的产物 两者同时存在的话,Cache-Control优先级高于Expires Expires其实是过时的产物,现阶段它的存在只是一种兼容性的写法
app.use('/star', function (req, res,) {
res.setHeader('Cache-Control', 'max-age=3')
var data = { code: new Date().getTime() }
res.json(data)
})设置了/star接口为强缓存,第一次请求star,返回200,然后在3秒内不管请求多少次star,都是从缓存里面读数据,如果三秒后请求,服务器收到请求后判断,获取到的数据和上一次获取的数据对比没有更新(即请求头If-None-Match和返回的响应头etag一致),就会返回304状态码和新的response header通知浏览器从缓存中读取资源;如果对比后发现数据更新了,就会返回200状态码并且从请求响应里读取数据
协商缓存使用Cache-Control的no-cache和no-store实现
向服务器发送请求,服务器接收到这个请求,会根据这个请求的request header的一些参数如:If-None-Match,来判断是否命中协商缓存,如果服务器接收到这个请求的请求头有If-None-Match,就会判断这个If-None-Match是否和这次请求返回的Etag一致,如果一致就代表命中协商缓存,就会返回304状态码和带上新的response header通知浏览器从缓存中读取资源
app.use('/tag', function (req, res,) {
/**
* 协商缓存,每次都会发请求判断资源是否更新,
* 如果资源更新了,就返回200状态码,如果没更新就返回304状态码
*/
// 设置协商缓存
res.setHeader('Cache-Control', 'no-cache')
// 获取请求头的if-none-match
console.log(req.headers['if-none-match'])
var data = { code: new Date().getFullYear() }
res.json(data)
// 设置了Cache-Control:no-cache后,手动设置status状态码不管用。
// res.status(200).json(data)
})上面是node设置了/tag接口的协商缓存,浏览器第一次请求/tag时,会在请求头if-none-match带上上次响应头的Etag(因为是第一次请求,因此获取到的if-none-match为undefined),node收到这个请求后,判断if-none-match,发现是undefined,即代表是第一次请求,因此就返回200状态码以及根据返回的data数据生成的唯一标识给响应头Etag;
浏览器第二次请求/tag时,会把上次请求的/tag时的响应头Etag,设置在请求头if-none-match上,node服务器收到请求后,判断请求头的if-none-match,发现这个if-none-match和这次返回的data生成的唯一标识一样(因为data都是{code:2021},因此生成的标识也一样),则命中协商缓存,返回304状态码和新的response header通知浏览器从缓存中读取资源。
app.use('/tag', function (req, res,) {
/**
* 协商缓存,每次都会发请求判断资源是否更新,
* 如果资源更新了,就返回200状态码,如果没更新就返回304状态码
*/
// 设置协商缓存
res.setHeader('Cache-Control', 'no-cache')
// 获取请求头的if-none-match
console.log(req.headers['if-none-match'])
var data = { code: new Date().getTime() }
res.json(data)
// 设置了Cache-Control:no-cache后,手动设置status状态码不管用。
// res.status(200).json(data)
})这里虽然设置了协商缓存,但是因为每次返回的数据都不一样(每次请求的时间戳都不一样),因此每次请求都不会取缓存里的数据。node每次都会返回200状态码并且从请求响应里读取数据。
app.use('/link', function (req, res,) {
res.setHeader('Cache-Control', 'no-store')
var data = { code: new Date().getFullYear() }
res.json(data)
})app.use('/link', function (req, res,) {
res.setHeader('Cache-Control', 'no-store')
var data = { code: new Date().getTime() }
res.json(data)
})设置了no-store后,所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存。每次请求不管数据是不是最新的,都从服务器重新拿数据。
共同点:都会从客户端缓存中读取资源; 不同点:强缓存不会发请求,协商缓存会发请求。 协商缓存既然都要发起请求了,为什么还要判断资源有没有更新,直接将请求返回的数据进行读取不就完事了吗?当然不是。 服务器端判断资源有没有更新,如果服务器端判断到返回是数据和上次的不一样,即更新了,服务端就返回200状态码和新数据给客户端,并通知客户端使用新数据;客户端接收到后,就用服务端返回的新数据; 如果服务器端判断到返回是数据和上次的一样,即没有更新,就不会返回数据,返回一个304状态码通知客户端直接从缓存里面拿之前的处理好的数据即可,这样的好处是节省了两个环节的时间,一个是服务端不用把新数据重新返回给客户端,直接返回304状态码就可以了(返回一个状态码总比返回一堆数据的传输时间来的快。),另一个是客户端收到304后,知道了资源没有更新,不用花时间处理新数据,而是直接从缓存里面读取之前处理好的数据即可。
HTTP 1.0协议中的。简而言之,就是告诉浏览器在约定的这个时间前,可以直接从缓存中获取资源(representations),而无需跑到服务器去获取。
另:Expires因为是对时间设定的,且时间是Greenwich Mean Time (GMT),而不是本地时间,所以对时间要求较高。
HTTP1.1协议中的,因为有了它,所以可以忽略上面提到的Expires。因为Cache-Control相对于Expires更加具体,细致。且就算同时设置了Cache-Control和Expires,Cache-Control的优先级也高于Expires。
下面就来看看,Cache-Control响应头中常用字段的具体含义:
max-age:用来设置资源(representations)可以被缓存多长时间,单位为秒;设置了max-age后,会在缓存时间内不发请求,从客户端缓存里读取数据,图片对应的是 memory cache、css等资源是 disk cache,且状态码是:304 Not Modified;如果客户端禁用了缓存(即开启Disable Cache),则max-age会失效,照常发请求。
s-maxage:和max-age是一样的,不过它只针对代理服务器缓存而言;
public:指示响应可被任何缓存区缓存;
private:只能针对个人用户,而不能被代理服务器缓存;
no-cache:强制客户端直接向服务器发送请求,也就是说每次请求都必须向服务器发送。服务器接收到请求,然后判断资源是否变更,是则返回新内容,否则返回304,未变更。这个很容易让人产生误解,使人误以为是响应不被缓存。实际上Cache-Control: no-cache是会被缓存的,只不过每次在向客户端(浏览器)提供响应数据时,缓存都要向服务器评估缓存响应的有效性。
no-store:禁止一切缓存(这个才是响应不被缓存的意思)。
Last-Modified和If-Modified-Since表示响应资源在服务器最后修改时间。
与Etag相比,不足为:
Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话(比如日志文件,一秒钟内有十个人访问,就会修改十次这个文件),它将不能准确标注文件的修改时间;
如果某些文件内容并没有变化,但Last-Modified却改变了,也会导致无法使用缓存(如果Etag和Last-Modified两种都使用的话,虽然文件没更新etag就不会更新,但是最后修改时间更新了即Last-Modified更新了,所以结果还是不会触发缓存,除非不使用Last-Modified,只是用Etag的方式设置缓存。);
app.get('/http1', function (req, res) {
// Last-Modified只有是数字,或者数字字符串缓存才会起作用
// 如果Last-Modified是非数字的话,不会起到缓存作用
res.setHeader('Last-Modified', new Date().getTime())
res.json({ code: new Date().getFullYear() })
})
// 每次请求/http1,都会改变响应头的Last-Modified,但是内容却都是一样的,会造成缓存无法使用。有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形。
然而,Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。
Etag/lastModified过程如下:
最早版本是1991年发布的0.9版。该版本极其简单,只有一个命令GET。
GET /index.html
上面命令表示,TCP 连接(connection)建立后,客户端向服务器请求(request)网页index.html。
协议规定,服务器只能回应HTML格式的字符串,不能回应别的格式。
<html> <body>Hello World</body> </html>
服务器发送完毕,就关闭TCP连接。
1996年5月,HTTP/1.0 版本发布,内容大大增加。
任何格式的内容都可以发送。这使得互联网不仅可以传输文字,还能传输图像、视频、二进制文件。这为互联网的大发展奠定了基础。
除了GET命令,还引入了POST命令和HEAD命令
HTTP请求和回应的格式也变了。除了数据部分,每次通信都必须包括头信息(HTTP header),用来描述一些元数据。
其他的新增功能还包括状态码(status code)、多字符集支持、多部分发送(multi-part type)、权限(authorization)、缓存(只有If-Modified-Since 和 Expires)、内容编码(content encoding)等。
HTTP/1.0 版的主要缺点是,每个TCP连接只能发送一个请求。发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接。
http1.0,在浏览器请求一个包含有许多图像的网页,如:www.aaa.com/index.html,首先和www.aaa.com建立tcp连接,拿到了index.html后就断开连接,拿到index.html后发现里面还有很多图片,继续建立tcp连接获取图片,如果有十个图片就会进行十次tcp连接过程
为了解决这个问题,有些浏览器在请求时,用了一个非标准的Connection字段。
Connection: keep-alive
这个字段要求服务器不要关闭TCP连接,以便其他请求复用。服务器同样回应这个字段。
Connection: keep-alive
一个可以复用的TCP连接就建立了,直到客户端或服务器主动关闭连接。但是,这不是标准字段,不同实现的行为可能不一致,因此不是根本的解决办法。
http1.1是目前最为主流的http协议版本,从1999年发布至今,仍是主流的http协议版本。
HTTP/1.1 最大的变化就是引入了持久连接(persistent connection),在HTTP/1.1中默认开启 Connection: keep-alive,即TCP连接默认不关闭,可以被多个请求复用。
一个包含有许多图像的网页文件中并没有包含真正的图像数据内容,而只是指明了这些图像的URL地址,当WEB浏览器访问这个网页文件时,浏览器首先要发出针对该网页文件的请求,当浏览器解析WEB服务器返回的该网页文档中的HTML内容时,发现其中的图像标签后,浏览器将根据标签中的src属性所指定的URL地址再次向服务器发出下载图像数据的请求。显然,访问一个包含有许多图像的网页文件的整个过程包含了多次请求和响应,每次请求和响应都需要建立一个单独的连接,每次连接只是传输一个文档和图像,上一次和下一次请求完全分离。即使图像文件都很小,但是客户端和服务器端每次建立和关闭连接却是一个相对比较费时的过程,并且会严重影响客户机和服务器的性能
同一个 tcp 连接,http1.1 允许一次发送多个 http1.1 请求,也就是说,不必等前一个响应收到,就可以发送下一个请求,这样就解决了 http1.0 的客户端的队首阻塞。但是,http1.1 规定,服务器端的响应的发送要根据请求被接收的顺序排队,也就是说,先接收到的请求的响应也要先发送。这样造成的问题是,如果最先收到的请求的处理时间长的话,响应生成也慢,就会阻塞已经生成了的响应的发送。也会造成队首阻塞。可见,http1.1 的队首阻塞发生在服务器端。
HTTP/1.1 版还引入了管道机制(pipelining),即在同一个TCP连接里面,客户端可以同时发送多个请求。这样就进一步改进了HTTP协议的效率。即在同一个TCP连接里面,客户端可以同时发送多个请求。这样就进一步改进了HTTP协议的效率。举例来说,客户端需要请求两个资源。以前的做法是,在同一个TCP连接里面,先发送A请求,然后等待服务器做出回应,收到后再发出B请求。管道机制则是允许浏览器同时发出A请求和B请求,但是服务器还是按照顺序,先回应A请求,完成后再回应B请求。
HTTP/1.0 使用 Pragma:no-cache + Last-Modified/If-Modified-Since来作为缓存判断的标准;
HTTP/1.1 引入了更多的缓存控制策略:Cache-Control、Etag/If-None-Match等。
1.1版还新增了许多动词方法:PUT、PATCH、HEAD、 OPTIONS、DELETE。
支持断点续传,通过使用请求头中的 Range 来实现。
另外,客户端请求的头信息新增了Host字段,用来指定服务器的域名。
Host: www.example.com
有了Host字段,就可以将请求发往同一台服务器上的不同网站,为虚拟主机的兴起打下了基础。
虽然HTTP/1.1版允许复用TCP连接,但是同一个TCP连接里面,所有的数据通信是按次序进行的。服务器只有处理完一个回应,才会进行下一个回应。要是前面的回应特别慢,后面就会有许多请求排队等着。这称为"队头堵塞"(Head-of-line blocking)。
为了避免这个问题,只有两种方法:一是减少请求数,二是同时多开持久连接(就等于原本只有一个队列,全部请求都会排队,一个阻塞了后面的就都阻塞了,这时候就多开几个队列,不用耗在这一个队列里白等。)。这导致了很多的网页优化技巧,比如合并脚本和样式表、将图片嵌入CSS代码、域名分片(domain sharding)等等。如果HTTP协议设计得更好一些,这些额外的工作是可以避免的。
HTTP 1.1版本会出现 「User-Agent、Cookie、Accept、Server、Range」 等字段可能会占用几百甚至几千字节,而 Body 却经常只有几十字节,所以导致头部偏重。
HTTP 2.0 使用 HPACK 算法进行压缩。
HTTP 1.x 中,如果想并发多个请求,必须使用多个 TCP 链接,且浏览器为了控制资源,还会对单个域名有 6-8个的TCP链接请求限制。
HTTP2中:
Stream ID,流标识符,有了它,接收方就能从乱序的二进制帧中选择ID相同的帧,按照顺序组装成请求/响应报文。浏览器发送一个请求,服务器主动向浏览器推送与这个请求相关的资源,这样浏览器就不用发起后续请求。
HTTP 是明文传输协议,HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 协议安全。
HTTPS比HTTP更加安全,对搜索引擎更友好,利于SEO,谷歌、百度优先索引HTTPS网页。
HTTPS标准端口443,HTTP标准端口80。
HTTPS需要用到SSL证书,而HTTP不用。
http://www.blogjava.net/zjusuyong/articles/304788.html
https://blog.csdn.net/shadow_zed/article/details/82534283
https://www.jianshu.com/p/3e2afe089e11
https://www.yuque.com/u1339897/kb/pt2nba
http://www.ruanyifeng.com/blog/2016/08/http.html
HTTP 1.1与HTTP 1.0的比较:https://blog.csdn.net/hejingyuan6/article/details/50365641
http://caibaojian.com/http-cache-code.html
https://www.dazhuanlan.com/2020/02/03/5e379909e56c4/
https://juejin.cn/post/6844903618890465294#heading-10