HTTP是一种超文本传输协议,用于完成客户端和服务器端等等一系列的运作流程。而协议指的是规则的约定。可以说,Web 是建立在 HTTP 协议上进行通信的。
我相信大家也是这样,在学一门技术之前都会去了解一下它的历史,下面来看看 HTTP 的发展史。
HTTP 诞生于 1989 年 3 月。是一个名叫蒂姆伯纳斯-李的哥们儿提出的,最初设想的基本理念是:借助多文档之间相互关联形成的超文本,连成可相互参阅的 WWW(World Wide Web,万维网)。简称 Web。
HTTP 0.9 在 1990 年问世。那时候的 HTTP 还没有作为正式的标准被建立。
HTTP 1.0 在 1996 年 5 月 正式作为标准。该协议标准现在仍然被广泛使用在服务器端。
HTTP 1.1 在 1997 年 1 月公布为当前主流的 HTTP 协议版本。
HTTP 2.0 在 2012 年 3 月 征集建议。
HTTP 2.0 在 同年的 9 月份 发布了第一个草案。
HTTP 2.0 在 2014 年 11 月实现了标准化。
在理解 HTTP 之前,我们先简单的来了解一下 TCP/IP 协议族。一般使用的网络都是在 TCP/IP 协议的基础上运作的,而 HTTP 属于它内部的一个子集。
在计算机和网络设备进行互相通信时,双方都必须基于相同的方法。比如,如何探测到通信目标,是哪边先发起通信、用什么语言进行通信、怎样结束通信等等一些规则都是先要确定好的。不同的硬件、操作系统之间的通信,所有的这一切都需要一种规则。而这种规则称为协议
。
协议中包括:从电缆的规格到 IP 地址的选定方法、寻找异地用户的方法、双方建立通信的顺序,以及 Web 页面显示要处理的步骤,等等。将这些相关联的协议集合起来总称为 TCP/IP。
TCP/IP 重要的点就是分层。有以下4
层:应用层、传输层、网络层和数据链路层。
TCP四层.png
下面来介绍各层的作用。
应用层:应用层决定了向用户提供应用服务时通信的活动。比如,FTP(文件传输协议)和 DNS(域名解析系统)。HTTP 协议也在该层。
传输层:传输层对上层应用层,提供处于网络连接中的两台计算机之间的数据传送。该层有两个不同的协议:TCP 传输控制协议和 UDP 用户数据协议。
网络层:网络层用来处理在网络上的数据包。数据包是网络传输的最小数据单位。网络层的作用就是在多条路线中选出一条传输路线进行数据传输。
链路层:用来处理连接网络的硬件部分。包括什么操作系统、硬件的设备、什么路由器啊之类的等等,都属于该层。
TCP/IP 层次化的好处是:如果互联网由一个协议统一规划,某个地方需要改变设计时,就必须将所有部分整体替换掉。而分层之后只需要把变动的层替换掉。把各层之间的接口部分规划好之后,每层内部的设计就可以自由改动。比如,处于应用层上的应用可以只考虑分配给自己的任务,不用去考虑其他的问题。
TCP/IP 协议进行通信时,会通过分层顺序和对方进行通信。客户端从应用层往下走,服务器端则从链路层往上走。看下面的图。
1.3.1.jpg
首先客户端在应用层发出一个 HTTP 请求。
接着,在传输层接收到应用层的数据后进行分割,给每个报文打上标记序号以及端口号转发给网络层。
在网络层,添加通信目的地的 MAC 地址后转发给链路层。
接收端(也叫服务器端)的服务器在链路层接收到数据,按次序向上层发送,一直到应用层。传输到应用层才算真正接收到客户端发过来的 HTTP 请求。
在HTTP客户端向服务器端发送报文之前,需要用到 IP、TCP、DNS 这三个和 HTTP 密不可分的协议。
IP(Internet Protocol
)网络协议处于网络层。IP协议的作用是把各种数据包传送给对方。但要保证正确的传送给对方,其中两个重要的条件是 IP 地址和 MAC 地址。可以把它想象成你家的地址,或者说你的电话号码。
IP 地址指的是节点被分配到的地址,MAC 地址指的是网卡所属的固定地址。IP 地址可以跟 MAC 地址进行配对。IP 地址是可变的,MAC 地址是不可变的。
IP 和 IP地址别搞混了,IP是一种协议。而IP地址是则是每台计算机的标识
ARP 协议
IP 间的通信依赖 MAC 地址。在网络上通信的双方很少会在同一个局域网,一般都是经过多台计算机或者网络设备中专才能连接到对方。而在中转的过程中,会利用下一站中转设备的 MAC 地址进行搜索下一个中转目标。而这时,会用到ARP协议。ARP协议是一种用来解析地址的协议,通过通信方的 IP 地址就能反查出对应的 MAC 地址。
在到达通信目标前的中转过程中,计算机和路由器只能获取粗略的传输路线,这种机制叫做路由选择。
就跟你在淘宝上买东西是一样的道理。比如,你在淘宝网买了件衣服,快递公司会根据你的地址进行送货,在送货这个过程中,并不是直接送到你手里。而是经过各种什么杭州中转站然后又到深圳中转站,之后才送到你手里。
TCP 协议处于传输层,主要的作用是提供可靠的字节流服务。字节流服务指的是,为了方便传输,将大块的数据分割成以报文段为单位的数据包进行管理。而可靠性的传输服务指的是,能够把数据准确可靠的传给对方。
为了准确的将数据传送给对方,三次握手就出现了。下图展示这个过程。
1.4.1.jpg
第一次握手:客户端先发送一个带 SYN 标志的数据包给对方。
第二次握手:服务器端收到之后,回传一个带有 SYN/ACK 标志的数据包表示传达确认信息。
第三次握手:最后,客户端再传回一个带 ACK 标志的数据包,表示 “握手” 结束。
DNS 服务和 HTTP协议一样,处于应用层。它主要的作用是,将域名解析成 IP 地址。DNS 协议可以通过域名查找 IP 地址,也可以通过 IP 地址反查域名的服务。
下面展示每个协议和HTTP协议的关系。
URL指的是统一资源定位符,是访问Web网站需要输入的网站地址。例如,http://www.tutu.com
。
URI指的是统一资源标识符,全称为Uniform Resource Identifier
,它的作用是区分互联网中的不同资源。比如,HTML 文档、图像、视频片段、程序等等。而 URL 是 URI 的一个子集。
下图展示了 URI 的格式。
URI.jpg
协议名:http:
或https:
表示协议名称。不区分字母大小写,最后加上//
。
登录信息:user:pass@
表示获取服务器资源的用户和密码。但不推荐使用,因为不安全。
服务器地址:服务器地址有三种:
以域名的形式www.tutu.com
;
以 IPv4 192.168.0.1
地址名;
以[0:0:0:0:0:0:1]
这种方括号括起来的 IPv6 地址;
服务器端口号::8080
表示端口号。
文件路径:/html/index.html
表示服务器文件路径,资源的访问位置。
查询字符串:?userId=1
表示文件路径中的参数。?
后面以key=value
的形式。如果后面还需要加参数,用&
拼接。
片段标识符:#cn1
表示文件中的某个位置。就是平时的网页锚点定位。
HTTP 是一种无状态的协议,对发送过的请求/响应都不做持久化处理。
HTTP 1.1 中的所有连接都是默认开启的(keep-alive
)。通过请求/响应头部的Connection
字段可以查看是否开启持久化连接(后面会介绍该字段的值),而在 HTTP1.0中是默认关闭的(close
)。
它的特点是,不管是客户端还是服务器端,只要其中的一端没有提出断开连接,那么就会保持 TCP 连接。好处是,减少 TCP 连接的重复建立和断开连接造成的额外开销,减轻服务器压力。这样使得 HTTP 请求和响应速度更快结束,也提高页面的显示速度。
管线化是不用等待响应就可以发送下一个请求,也就是并行处理。不用一个接一个的等待响应,管线化比持久化连接还要更快。
HTTP 一共有两种报文:请求报文、响应报文。报文又分为报文头部和报文主体,报文主体是可选的。报文包含了以下三个部分。
起始行(start line
)有以下两种类型。
请求行:请求的方法、请求的 URL、HTTP的版本
响应行:HTTP 版本、状态码
头部字段(header
):一些头部信息,以key: value
的形式。
主体(body
):被发送的数据。
报文主体.jpg
这张图是以请求报文为例。
GET:获取服务器资源。
POST:提交信息给服务器。
PUT:传输文件。
HEAD:和 GET 方法一样。但是只返回响应头部。作用是确定 URL 的有效性和资源更新的时间。
DELETE:删除指定的资源。
OPTIONS:查询请求服务器指定的资源所支持的方法。
TRACE:用来确认连接过程中发生的一些操作。
CONNECT:建立连接渠道,用于代理服务器。
1xx
1XX
表示接收的请求正在处理。
2xx 成功
200 OK
:表示客户端发送的请求在服务器端被正常处理了。
204 No Content
:表示请求被处理成功,但没有资源可返回。
206 Partial Content
:表示客户端只获取文件的一部分内容,而服务器成功执行了这部分的GET
请求。响应报文中含Content-Range
指定部分的实体内容。
3xx 重定向
301 Moved Permanenty
:永久重定向。表示请求的资源已经被分配了新的 URL,以后就使用资源现在所指的 URL。
302 Found
:临时重定向。表示请求的资源被分配了新的 URL。
303 See Other
:表示请求的资源存着另一个 URL,应该用GET
方法获取请求的资源。
304 Not Modified
:表示请求已经找到,但不符合条件请求。协商缓存就会返回这个状态码。
307 Temporary Redirect
:临时重定向,和302
类似。但是补鞥呢改变请求方法。
当301、302、303响应状态码返回时,几乎所有浏览器都会将POST改为GET,并删除请求报文中的主体,之后请求会自动再次发送。301、302标准是禁止把POST改成GET的,但实际使用的时候大家都会这么做。
4xx 客户端错误
400 Bad Request
:表示请求报文中存在语法错误。
401 Unauthorized
:表示发送的请求要通过 HTTP 认证的认证消息。如果之前请求过一次,就表示用户认证失败。
403 Forbidden
:表示对请求资源的访问被服务器拒绝。
404 Not Found
:表示服务器上无法找到请求的资源。
5xx 服务器错误
500 Internal Serve Error
:表示服务器端在执行请求时发生错误。
503 Service Unavailable
:表示服务器暂处于超负荷或者正在进行停机维护。
HTTP进行通信时,除了客户端和服务器端这两个之外,还有一些用于通信数据转发的应用程序。例如代理、网关、隧道和缓存。
代理
代理是一种具有转发功能的应用程序,它存在于客户端和服务器端之间,相当于一个中间人。它将客户端发送过来的请求并转发给服务器端。当然,它也会将服务器端返回的响应转发给客户端。
代理服务器.jpg
每次通过代理服务器转发请求或响应时,头部都会出现Via
这个字段。
网关
网关是一种特殊的服务器,作为其他服务器的中间实体使用。用于将 HTTP 请求转化成其他协议通信。网关接收请求时就好像自己的资源的源服务器一样对请求做处理。
网关.jpg
隧道
隧道是可按要求建立一条和其他服务器的通信线路,到时候使用 SSL 加密进行通信。隧道的目的是保证客户端和服务器进行安全的通信。
隧道.jpg
缓存
缓存是指代理服务器或客户端本地磁盘中保存的资源副本。利用缓存可以减少向源服务器的访问,主要目的是减少网络带宽的流量和通信时间。
缓存服务器是代理服务器的一种,当代理转发从服务器返回的响应时,会保存一份资源的副本。缓存服务器的优点在于通过缓存可以避免多次从源服务器转发资源。因此客户端可就近从缓存服务器上获取资源,而源服务器也不必多次处理相同的请求。
缓存的有效期
每当源服务器上的资源更新时,如果还是用不变的缓存,那就会变成返回更新前的旧资源。
即使存在缓存,也会因为客户端的要求、缓存的有效期等等一些因素,向源服务器确认资源的有效性。如果缓存的资源已过期,缓存服务器会向源服务器上获取新的资源。
客户端缓存
这里的客户端缓存指的是浏览器中的缓存。浏览器缓存如果未过期,就不用向源服务器请求相同的资源,直接获取缓存在本地磁盘中的资源。当资源过期时,会向源服务器确认资源的有效性。如果缓存的资源过期,就会再次向源服务器发起资源请求。
内容协商机制是指客户端和服务器端就响应的资源内容进行互相协商,然后提供客户端最合适的资源。内容协商会以语言、字符集、编码方式等。
主要使用的请求头有:
Accept
Accept-Charset
Accept-Language
Content-Language
内容协商技术有下面三种类型。
服务器驱动协商(Server-driven Negotiation)
由服务器进行内容协商。
客户端启动协商(Agent-driven Negotiation)
由客户端进行内容协商。
透明协商 服务器驱动和客户端驱动的结合体,由服务器和客户端进行内容协商的一种方法。
HTTP 头字段定义成缓存代理和非缓存代理。分为两种类型。
端到端头部End-to-end
分在这个类别中的头部会转发给请求或响应对应的最终接收目标,而且必须保存在缓存生成的响应中,另外规定它必须被转发。
逐跳头部Hop-by-hop
分在这个类别中的头部只对单次转发有效,会因通过缓存或代理而不转发。在 HTTP 1.1 和之后的版本中,如果使用Hop-by-hop
头,就要提供 Connection
头字段。
除了下面 8 个头字段外,其他所有字段都属于端到端头部。
Connection
Keep-Alive
Proxy-Authenticate
Proxy-Authorization
Trailer
TE
Transfer-Encoding
Upgrade
下面列出请求/响应头都会出现的字段,这些字段都含有重要的信息。
Cache-Control
Cache-Control
表示资源的缓存操作,参数是可选的,如果存在多个参数的话,以,
分隔开。
请求头
在请求头中使用Cache-Control
字段时,它的值如下:
no-cache
:不使用强缓存,强制向源服务器再次验证缓存的资源是否过期(走协商缓存)。
no-store
:不使用任何缓存,每次都向源服务器获取最新的资源。
max-age
:以秒为单位。表示缓存的资源没有超过指定的时间,客户端就从缓存中获取资源。
min-fresh
:以秒为单位。要求代理服务器返回至少还没过指定时间的缓存资源。
max-stale
:即使是过期的资源,也照样接收。
only-if-cached
:告诉代理服务器,从缓存中获取资源(如果有)。
no-transform
:不能对资源进行转换,可以防止缓存或代理压缩图片等类似操作。
响应头
在响应头中使用Cache-Control
字段时,它的值如下:
public
:资源可被浏览器和代理服务器进行缓存。
private
:资源只可以被浏览器进行缓存。其他都不可以。
no-cache
:可以缓存,但每次使用前要向源服务器验证缓存资源是否过期。
s-maxage
:只提供给代理服务器,表示代理服务器中的资源过期时长,用s-maxage
后,会忽略max-age
和Expires
字段。
max-age
:以秒为单位。设置缓存时间,如果没超过该时间,不用向服务器请求资源。如果超过,就证明资源已过期。如果响应头出现Expires
字段,在 HTTP 1.1 中会有限处理max-age
,而 HTTP1.0 中则相反。
must-revalidate
:可以缓存,但必须向源服务器再次验证。如果请求失败,则返回504
状态码。该字段会忽略max-stale
。
proxy-revalidate
:要求缓存服务器对缓存的响应有效性进行确认。
no-transform
:不能对资源进行转换,可以防止缓存或代理压缩图片等类似操作。
Connection
Connection
字段决定当前 TCP连接 完成后,是否关闭连接。有以下两种。
keep-Alive
:持久化连接。
close
:TCP 连接完成后,立马关闭连接。
Date
Date
字段的值为GMT时间日期格式,表示 HTTP 报文创建的时间和日期。
Date: Tue, 13 Apr 2021 12:35:41 GMT
Pragma
Pragma
是用来向后兼容只支持 HTTP1.0 协议的缓存服务器。它的效果和Cache-Control
一样。
Pragma: no-cache
Upgrade
Upgrade
用于查看 HTTP 协议或者其他协议是否可以使用更高的版本进行通信。
Upgrade: HTTP/2.0 Connection: Upgrade <!-- 表示删除了Upgrade后再转发 -->
Via
Via
用于跟踪客户端和服务器端之间的请求和响应报文的传输路径,还可以避免请求循环的发生。
<!-- 经过单个代理服务器 --> Via: 1.0 gw.hackr.jp(Squid/3.1) <!-- 经过多个代理服务器 --> Via: 1.0 gw.hackr.jp(Squid/3.1), 1.1 al.example.com(Squid/2.7)
经过代理服务器 A 时,Via头部附加了 “1.0 gw.hackr.jp(Squid/3.1)” 这样的字符串值。行头 1.0 指的是接收请求的服务器上应用的 HTTP 协议版本。如果经过多个代理服务器的话,这些信息会后面追加。
Warning
Warning
字段告诉用户一些与缓存有关的警告。
Accept
Accept
请求头用来告诉服务器,客户端能处理的内容类型。下面列出几种媒体类型。
文本文件: text/html
、text/plain
、text/css
、application/xhtml+xml
、application/xml
等等。
图片文件: image/jpeg
、image/gif
、image/png
;
视频文件: video/mpeg
、video/quicktime
;
二进制文件:application/octet-stream
、application/zip
;
当值为*/*
,表示客户端可以是任意内容类型。当值为image/*
,用来代表任何其他图片类型。
如果想给显示的媒体类型增加优先级,通过用q=
表示权重值,用分号(;
)分隔开。权重值的范围是0~1
,可以精确到小数点后 3 位,1
是最大值。没有指定权重值时,默认权重是q=1.0
。
Accept: text/html, appliaction/json;q=0.9
Accept-Charset
Accept-Charset
请求头用来告诉服务器,客户端可处理的字符集类型。另外,可以一次性指定多种字符集。和Accept
一样,通过q
值来表示优先级。该头部应用于内容协商机制的服务器驱动协商。
Accept-Charset: iso-8859-1 Accept-Charset: iso-8859-1;q=0.5
Accept-Encoding
Accept-Encoding
请求头用来告诉服务器,客户端能理解的内容编码方式。可以一次性指定多种内容编码,有以下几种编码。
gzip:由文件压缩程序 gzip
生成的编码格式,使用 Lempel-Ziv
算法以及 32 位循环冗余验证。
compress:由 UNIX 文件压缩程序compress
生成的编码方式,采用Lempel-Ziv-Welch
算法。
deflate:组合使用 zlib
格式以及由deflate
压缩算法生成的编码方式。
indentity:不执行压缩或不会变化的默认编码格式。
Accept-Encoding: gzip, deflate
和Accept
一样,用q
值来设置优先级。还有使用星号(*
),表示指定任意的编码格式。
Accept-Language
Accept-Language
请求头用来告诉服务器,客户端可以理解的自然语言集(指中文和英文集),以及自然语言集的优先级。和Accept
一样的,可指定多个自然语言集。使用q
值设置优先级。
Accept-Language: zh-CN,zh;q=0.9;q=0.8
客户端在服务器有中文版的情况下,会请求返回中文版的响应,如果没有,则返回英文版。
Authorization
Authorization
用来告诉服务器,用户代理的认证信息(证书值)。通常会在服务器返回401
状态码响应后,把头部字段Authorization
添加到请求中。
Authorization: Basic dWVub3NlbjpwYXNzd29yZA==
Expect
Expect
用来告诉服务器,只有在满足这个条件的情况下才会处理请求。如果服务器不能满足客户端的要求,会返回417
状态码。目前只规定了100-continue
这个条件。
Expect: 100-continue
From
From
字段表示用户代理的用户的电子邮箱地址,目的是为了显示搜索引擎用户代理的负责人的电子邮箱联系方式。
From: info@hackr.jp
Host
Host
请求头指明了请求的资源所在的服务器主机名和端口号。如果服务器没设置主机名,会发送一个空值。
Host: www.tutu.com
If-Match
像这种If-xxxx
开头的请求头字段,都是条件请求。服务器接收到附带条件的请求后,只判断条件是真时才会执行请求。
If-Match
字段用于和服务器资源的ETag
值做对比时,ETag
值和If-Match
的值相等时才会处理该请求。否则返回412
状态码。通过用星号(*
)的方式,表示只要资源存在就处理请求,但服务器会忽略掉ETag
的值。
If-Match: "123456"
If-Modified-Since
If-Modified-Since
用于确定代理服务器或客户端的资源有效性。在指定的时间之后,请求的资源发生改变时,就处理请求。如果资源都没有改变,则返回304
状态码。
If-Modified-Since: Tue, 13 Apr 2021 12:35:41 GMT
If-None-Match
If-None-Match
和If-Match
相反,只有服务器资源的ETag
的值和If-None-Match
的值不一样时,才会处理该请求。在GET
和HEAD
请求方法中加入该字段可以获取最新的资源。
If-Range
If-Range
用于告诉服务器,如果If-Range
字段的值和请求资源的ETag
值或时间一样时,就会作为范围请求处理(Range
字段规定请求多少字节的数据)。否则,忽略范围请求,返回全部资源。
If-Range: "123456" Range: bytes=5001-10000
If-Unmodified-Since
If-Unmodified-Since
字段用来告诉服务器,只有当请求资源在指定的时间之后没有修改的情况下,才会处理请求。如果在指定的时间后发生了修改,则返回412
状态码。
If-Unmodified-Since: Tue, 13 Apr 2021 12:35:41 GMT
Proxy-Authorization
Proxy-Authorization
字段包含了用户代理提供给代理服务器用于身份验证的凭证。
Proxy-Authorization: Basic dGlwOjkpNLAGfFY5
Range
Range
请求头字段表示获取资源的哪一部分。服务器收到带有Range
字段的请求后,会在处理请求之后返回206
状态码。如果无法处理,则返回200
状态码并把全部资源返回。
Range: bytes=5001-10000 <!-- bytes=5001-10000为资源的字节 -->
Referer
Referer
字段表示请求的URL是从哪个 Web 页面发起的。服务器通过Referer
字段来标识访问来源,进行统计分析、日志记录和缓存优化。
Referer: www.tutu.com <!-- 请求是由www.tutu.com发起的 -->
TE
TE
表示客户端能够处理响应的传输编码方式以及优先级。
TE: gzip, deflate;q=0.5
User-Agent
User-Agent
字段用于把请求的浏览器和用户代理名称等信息传给服务器。
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36
Accept-Ranges
Accept-Ranges
表示服务器端能处理指定范围内的资源。有两个值:bytes
和none
。none
表示不能处理指定范围内的请求。
Age
Age
表示源服务器再多久前返回过资源。单位为秒。
Age: 3600
ETag
ETag
表示资源的特定标识符,服务器会给每份资源都分配对应的ETag
值。当资源发生变化时,ETag
值也会改变。例如,访问同一个 URL 的网站同时具备有中英文版,当切换中文时,会返回中文的资源(ETag: user-chi
),而切换到英文时,返回的是英文的资源(ETag: user-us
)。
强弱ETag
强ETag: 不管资源发生了什么变化都会改变其值。
ETag: "user-123456"
弱ETag: 用于资源是否相同,只有资源发生了根本改变,产生差异时才会改变ETag
值。字段值最开头会添加W/
的字符
ETag: W/"user-123456"
Location
Location
字段表示需要将页面重定向到某个地址,一般都是响应码为3xx
的响应才有用。
Location: www.baidu.com <!-- 重定向到www.baidu.com -->
Proxy-Authenticate
Proxy-Authenticate
字段表示获取代理服务器的资源要通过身份验证的方式。
Retry-After
Retry-After
字段表示客户端应该在多久之后再次请求。配合503
和3xx
状态码响应一起使用。
Retry-After: 120
Server
Server
字段表示处理请求的服务器所用到的软件相关信息。
Server: Apache/2.2.17
Vary
Vary
字段可以对缓存进行控制。从代理服务器收到源服务器返回含有Vary
指定项的响应后,如果要进行缓存,那么会对请求中含有Vary
指定头部字段的请求返回缓存。即使对相同资源发起请求,如果Vary
指定的头部字段不相同,就必须从源服务器重新获取资源。
Vary: Accept-Language
Allow
Allow
用于告诉客户端,资源所支持的 HTTP 方法,如果服务器收到不支持的 HTTP 方法时,会返回405
状态码作为响应。
Allow: GET, DELETE
Content-Encoding
Content-Encoding
字段表示服务器对实体的主体部分用的内容编码方式。内容编码在Accept-Encoding
中已经介绍过,一共有4
种。
Content-Encoding: gzip
Content-Language
Content-Language
字段表示实体主体使用的自然语言。
Content-Language: zh-CN
Content-Length
Content-Length
表示实体主体部分的大小(以字节为单位)。
Content-Length: 1500
Content-Location
Content-Location
字段表示要返回数据的地址。
Content-Location: https://www.tutu.com/index.html
Content-Range
Content-Range
字段表示的是一个数据片段在整个文件中的位置。
Content-Range: bytes 5001-10000/10000
Content-type
Content-type
字段表示实体主体中对象的媒体类型。
Content-type: text/html; charset=UTF-8
Expires
Expires
字段会把资源失效的日期告诉客户端。在这个日期后,资源就会过期。也就是说,在指定的日期内可以从浏览器缓存中获取资源。如果超过了这个日期,就必须向服务器发起资源请求。如果头部存在Cache-Control: max-age
时,会优先处理max-age
指令。
Expires: Tue, 13 Apr 2021 12:35:41 GMT
Last-Modified
Last-Modified
字段表示资源最后修改的时间。
Last-Modified: Tue, 13 Apr 2021 12:35:41 GMT
HTTP 缓存可分为强缓存和协商缓存,主要用于加快资源的获取速度,提高用户体验,减少网络连接,缓解服务器压力。
强缓存
对强缓存来说,浏览器会判断请求的资源是否在有效期内。如果是在有效期内,就直接从缓存中读取资源,不用向服务器发送资源请求。强缓存通过Expires
、Cache-Control
和Pragma
这三个头部字段设置。
Cache-Control
Cache-Control
头部字段在上面也详细介绍过在各端所具备的属性值。下面来列出最常见的几个值。
public
: 该资源可以被浏览器和代理服务器进行缓存。
private
: 该资源只可以被浏览器缓存,其他都不可以。
no-cache
: 不使用强缓存,强制向源服务器再次验证缓存的有效性。这个值表示走协商缓存。
no-store
: 不使用任何缓存,每次都向源服务器获取最新资源。
max-age
: 如果缓存的资源没有超过规定的时间,客户端就从缓存中获取资源。以秒为单位。
s-maxage
: 只适用于代理服务器,表示代理服务器中的资源过期时长,用了s-maxage
后,会忽略max-age
和Expires
字段。
Expires
Expires
字段的值是一个GMT
格式的时间日期,将资源失效的日期告诉客户端,客户端收到带有该字段的响应体后进行缓存。后续客户端发起相同的资源请求,会用Expires
的值和本地时间做对比,如果该请求的本地时间小于Expires
的值,就直接用缓存中的资源,不用向服务器发起请求。
Expires
的值会产生一个问题。如果修改了本地时间,就会导致客户端和服务器端的时间不一致,那么对于缓存过期的判断就无法和预期一样了。
Expires
在三者中优先级最低。
Pragma
Pragma
可以看上面的介绍,这里不过多讲解。
强缓存在 Chrome 中会返回200
状态码并且有两种情况。
memory cache
: 只要页面不关闭,就会从浏览器内存中获取资源。
disk cache
: 从磁盘中读取缓存资源。
强缓存.jpg
用了强缓存后,如果服务器端的资源更新了,客户端是不知道的,而且在过期之前都会用缓存中的资源。可以通过Ctrl
+ F5
强制刷新。
协商缓存
协商缓存是在用本地缓存之前,会向服务器发起一次GET
请求,验证浏览器保存在本地的资源是否过期。
last-modified和if-unmodified-sine
一般情况下是用请求资源的最近一次修改时间戳来判断。来举个例子:假设客户端向服务器端请求一个文件,为了让资源被再次请求时能通过协商缓存机制使用本地缓存。首次返回该资源的响应头中会包含一个last-modified
的字段,字段的值表示资源最后修改的时间。当刷新页面时,该资源使用的是协商缓存,浏览器无法确认本地缓存是否过期,然后向服务器发起一次GET
请求,进行缓存有效性的协商,本次请求的请求头中包含一个if-unmodified-since
字段,字段的值是上次响应头中的last-modified
字段的值。
last-modified的不足之处
last-modified
存在两个缺陷:
它只是根据资源最后的修改时间进行判断,虽然请求的文件资源进行了编辑,但内容并没有改变,时间也会更新。这就导致了协商缓存时关于有效性的判断验证失效,就要重新进行资源的请求。
由于文件资源修改的时间单位是秒,如果文件修改很频繁。比如,几百毫秒内改一次,就无法识别出该文件资源的更新。
ETag和if-none-match
为了弥补通过时间判断的不足,HTTP 1.1 加入了ETag
(实体标签)的头信息。
ETag
表示资源的特定标识符,类似文件指纹。作用上面也有提到,这里不过多讲解。
当响应头同时存在last-modified
和ETag
这两个字段时,会以ETag
为准。再次对该资源发起请求时,会将之前的响应头中ETag
的值当作这次请求中if-none-match
字段的值,发送给服务器进行缓存有效性验证。如果验证缓存有效,就返回304
状态码响应重定向到本地缓存。
ETag的不足之处
ETag
的出现并不是last-modified
的代替品,而是一种补充方案,它还是存在弊端的。
如果资源比较大,数量多而且修改频繁的话,那么生成ETag
的过程会影响到服务器的性能。
上面也讲了ETag
还分强ETag
和弱ETag
。
强ETag
值是根据资源内容进行生成,保证每个字节都相同。
弱ETag
值是根据资源的部分属性值来生成,生成速度快但是无法保证每个字节都相同。
如果浏览器走的是协商缓存,并且资源没发生改变,服务器端会返回304
状态响应码告诉浏览器获取本地缓存的资源即可。
1617632172962.jpg
HTTP协议主要的不足之处有以下几点。
通信使用明文,内容会被窃听
不验证通信方的身份,可能遭遇伪装
无法证明报文的完整性,可能已遭到篡改
HTTP 协议本身没有加密功能,所以无法做到对通信请求和响应内容进行加密。
由于 TCP/IP 协议的工作机制,通信内容在所有通信线路上都有可能遭到窥视。不管是哪个角落的服务器在跟客户端进行通信,通信的线路上的一些设备都不可能是个人物品。所以不排除在某个环节上遭到恶意窥视的行为。即使进行加密处理,也会被窥视到通信的内容。窃听相同端上的通信并不是难事,只要收集在网络上流动的数据包就行。可以通过抓包和嗅探器工具来收集数据包。
解决方案: 加密处理防止窃听
最常见的两种加密方式是通信加密和内容加密
通信加密
HTTP 协议中本身没有加密机制,但可以通过 SSL(Secure Socket Layer 安全套阶层) 或 TLS(Transport Layer Security 安全传输层协议) 的组合使用,加密HTTP的通信内容。用 SSL 建立安全通信线路后,就可以在这条线路上进行 HTTP 通信。和 SSL 组合使用的 HTTP 叫做 HTTPS (HTTP Secure 超文本传输安全协议) 或 HTTP over SSL。
内容加密
由于 HTTP 协议中没有加密机制,那么可以对传输的内容本身进行加密。也就是把 HTTP 报文中包含的内容进行加密处理。在这种情况下,客户端需要对 HTTP 报文主体(body
)进行加密处理后再发送请求。要做到内容的加密,前提是客户端和服务器端同时具有加密和解密的机制。主要应用在 Web 服务器中。该方式不同于 SSL 和 TLS 把整个通信线路加密处理,所以内容还是会有被篡改的可能。
HTTP 协议的请求和响应都不会对通信方进行确认。
任何人都可以发起请求
在 HTTP 协议通信时,由于不存在确定通信方的处理步骤,任何人都可以发起请求。服务器只要收到请求,不管是谁都会返回一个响应(仅限发送端的 IP 地址和端口号没被 Web 服务器设置限制访问的前提下)。也就是来者不拒。
有可能是伪装的服务器。
有可能是伪装的客户端。
无法确定正在通信的对方是否具备访问权限。因为某些 Web 服务器上保存有重要的信息,只想发给特定用户通信的权限。
无法判断请求是从哪来、出自谁手。
即使是无意义的请求也照样接收。无法阻止大量请求下的 Dos 攻击(Denial of Service,拒绝服务器攻击)。
解决方案:查明对方的证书
虽然使用 HTTP 协议无法确定通信方,但使用 SSL 可以。SSL 除了加密处理外,还用了一种证书的手段,用于确认通信方。证书是由值得信任的第三方机构颁发,用来证明服务器和客户端是真实存在的。
通过证书,以证明通信方就是意料中的服务器,对个人来说,减少了个人信息泄露的危险。另外,客户端持有证书即可完成个人身份的确认,也可用于对 Web 网站的认证环节。
收到的内容不完整
没有任何办法确认,发出去的请求或响应和接收到的请求或响应是前后相同的。有可能在中途被篡改成其他的内容,即使内容是真的被改了,接收方也不会知道。
解决方案:MD5 和 SHA-1
可以使用 MD5 和 SHA-1 等散列值校验方法,以及用来确认文件的数字签名方法(PGP签名)对内容进行加密。但是用这些方法也无法保证正确,因为 MD5 和 PGP 本身被修改的话,用户也不会知道。
HTTP加上加密处理和认证以及完整性保护机制就是HTTPS
HTTPS 不是应用层的一种新协议。只是 HTTP 通信接口部分用 SSL 和 TLS 协议代替而已。之前是 HTTP 和 TCP 进行通信,用了 SSL 之后,就变成了 HTTP 先和 SSL 通信,之后 SSL 和 TCP 通信。
HTTPS.png
使用 SSL 之后,HTTP 就有了 HTTPS 的加密、证书和完整保护性这些功能。
SSL 用的是公开密钥加密的处理方式。加密方法中的加密算法是公开的,密钥则是保密的。通过这种方式可以保持加密方法的安全性。
加密和解密都会用到密钥。没有密钥就没办法对密码解密,任何人只要有密钥就可以进行解密,如果密钥被攻击者获得,那么加密也就没有意义了。
对称加密
加密和解密同用一个密钥的方式叫做共享密钥加密,也称为对称密钥加密。也就是说,客户端和服务器端共用一个密钥对消息进行加密。客户端在发送请求时,会用密钥对消息加密。服务器收到后,再用密钥对消息进行解密。
缺点
对称加密虽然保证了消息保密性,但客户端和服务器端用的都是同一个密钥,如果说在传输的过程中出现了中间人或攻击者。密钥就有可能落到攻击者手中,这样就对消息加密就没有什么意义了。
非对称加密
非对称加密解决了对称加密的缺点。非对称加密用的是一对非对称的密钥。一把叫做私有密钥,另一把叫做公开密钥。私有密钥只能是自己所拥有,而公开密钥则是任何人都可以拿到。
当客户端发送消息前,使用公共密钥进行加密,而服务器收到消息后,使用私有密钥进行解密。
缺点
非对称加密需要在发送端在发送消息时,用公钥加密。但公钥是任何人都可以拿到,中间人也可以。中间人虽然不知道接收方的私钥是什么,但可以截获发送端的公钥,自己另外生成一把公钥或者篡改公钥,把公钥发给接收端。而且非对称加密处理起来比对称加密的方式更加复杂,这样就导致了效率变低。
混合加密机制
HTTPS 用的就是对称加密和非对称加密两者的混合加密。使用对称加密的好处是解密效率快,使用非对称加密的好处是在传输消息的过程中不会被破解。即使截获了数据,没有对应的私钥,也无法对消息进行破解。
摘要算法
数字摘要是采用 Hash 函数将需要加密的明文 “摘要” 成一串固定长度(128
位)的密文,这串密文又称为数字指纹,它有固定长度,而且不同的明文摘要成密文,其结果总是不同,而同样的明文摘要必须一致。数字摘要是 HTTPS 能确保数据完整性和防篡改的根本原因。
数字签名
数字签名是非对称加密和数字摘要两项技术的应用,它将摘要信息用发送者的私钥加密,和原文一起发给接收者。接收者只有用发送者的公钥才能解密被加密的摘要信息,然后用 Hash 函数对收到的原文产生一个摘要信息,与解密的摘要信息对比。如果一样,那就说明收到的信息是完整的。否则说明信息被修改过,因此数字签名能够验证信息的完整性。
签名是加密的校验码
数字签名是附加在报文上的特殊加密校验码。使用数字签名有以下两点好处。
签名能确定消息是由发送方签名并发过来的,因为别人假冒不了发送方的签名。
签名能确定消息的完整性,证明数据没有被篡改过。
数字签名的过程如下:明文 \-> hash运算 \-> 摘要 \-> 私钥加密 \-> 数字签名
数字证书
数字证书(CA)就像我们的身份证一样,信息都是唯一性的。它是属于可信任的一些第三方机构所有。证书包含了以下的信息。
证书的发布机构 CA
证书的有效期
公钥
证书所有者
签名
数字证书还包括对象的公钥,对象和所用签名算法的描述信息。所有人都可以创建一个数字证书,但并不是所有人都能获得签发权,从而为证书信息担保,并用它私有密钥签发证书。
首先是客户端向服务器端发起一个 HTTPS 请求。
服务器端返回公钥证书给客户端。
客户端收到公钥证书后,用证书的公钥验证数字签名,以确认服务器的公钥的真实性。
客户端用随机数生成器生成临时的会话密钥,然后用服务器的公钥对该会话密钥进行加密,发送给服务器端。
服务器收到后,用自己的密钥对会话密钥解密。
之后客户端和服务器端就开始了 HTTPS 通信。
HTTPS 用的是 SSL(Secure Socket Layer 安全套阶层) 和 TLS(Transport Layer Security 安全传输层协议)这两个协议。SSL 最开始是网景先倡导,后来网景凉了,就转移给了 IETF 的手里。IETF 以 SSL 3.0 为准,之后又定制了 TLS1.0、TLS1.1和 TLS1.2。TLS 是以 SSL 为原型开发的协议。有时候统一称该协议为 SSL。
凡事都具有两面性,不是说 HTTPS 安全就没有问题了。其实它还是存在一些问题的。在使用 SSL 时,它的处理速度会变慢。其原因有两种,一是通信慢,二是每次都进行加密通信,就导致消耗大量的 CPU 和内存资源,导致处理速度变慢。
除了和 TCP 连接、发送请求和响应之外,还要和 SSL 进行通信。
另外 SSL 要进行加密处理,在服务器和客户端都要进行加密和解密的运算处理。
要用 HTTPS 通信,购买证书是必不可少的。
当然可以用 SSL 加速(专用服务器)硬件来改善效率的问题。可以提高 SSL 的计算速度,分担负载。但只有在 SSL 处理时才发挥 SSL 加速器的效果。像一些非敏感的信息就用 HTTP 进行通信,对于敏感信息采用 HTTPS 通信,以节约资源。
HTTP 是以明文的方式进行传输,HTTPS 则是具有安全性的 SSL 加密传输协议。
HTTP 和 HTTPS 用的是两种不同的方式进行连接,端口号也不一样。前者是 80
,后者是 443
。
想要用 HTTPS 就得购买证书(CA),而免费的整数一般都很少,所以需要支付一定的费用。
HTTPS 对搜索引擎更友好,有利于 SEO ,优先索引 HTTPS 的网页。
HTTP 的连接简单,并且是无状态的。HTTPS 是由 SSL + HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 要安全。
HTTP 1.x主要有以下几个缺点:
HTTP 1.0 只允许在一个 TCP 连接上发送一个请求,HTTP 1.1中默认允许多个 TCP 连接。但是同一个 TCP 连接中,所有的数据通信都是按次序进行的,服务器一般是处理完一个响应之后,再继续处理下一个。这就造成了队首阻塞问题。
请求只能从客户端开始,客户端不可以接收除了响应之外的指令。
请求/响应头部不进行压缩就发送。头部信息越多延迟就越大。
发送冗长的头部,每次互相发送相同的头部导致资源浪费。
可随意选择数据压缩格式,非强制压缩发送。
SPDY 是由谷歌开发的基于 TCP 协议的应用层协议。目标是为了优化 HTTP 协议的性能,通过压缩、多路复用和优先级技术,缩短网页的加载时间并提高安全性。SPDY 协议的核心思想是尽量减少 TCP 的连接数。SPDY 并不是一种代替 HTTP 的协议,而是对 HTTP 协议的增强。
SPDY 没有改写 HTTP 协议,而是在 TCP/IP的应用层和传输层之间通过新加会话层的形式运作。同时,考虑到安全问题,SPDY 规定通信中使用 SSL。
SPDY.jpg
SPDY 以会话层的形式加入,控制对数据的流动,但还是采用 HTTP 建立通信。因此,可以照常使用 HTTP 的请求方法、Cookie 以及 HTTP 报文等等。
HTTP 2.0 可以说是 SPDY 的升级版(其实是基于 SPDY 设计的),但HTTP 2.0和 SPDY 还是存在一些不同的。主要有以下两点:
HTTP 2.0 支持明文传输,而 SPDY 强制使用 HTTP。
HTTP 2.0 消息头的压缩算法用的是 HPACK,而 SPDY 用的是 DEFLATE。
下面就简单的介绍一下 HTTP 2.0 新增的功能。由于HTTP 2.0 设计到的东西太多了,之后我会一篇文章单独讲讲 HTTP 2.0。
二进制分帧层:HTTP 2.0 性能增强的核心就是新增的二进制分帧层,HTTP 1.x是以换行符作为纯文本的分隔符,而 HTTP 2.0 把所有传输的信息分割成更小的消息和帧,并对它们采用二进制的格式编码。
多向请求和响应:HTTP 2.0 中心的二进制分帧层,将 HTTP 消息分解成独立的帧,交错发送。然后在另一个端根据流标识符和头部把它们重新组装。解决了 HTTP 1.x的队首阻塞问题。
请求优先级:把 HTTP 消息分解成多个独立的帧后,就可以通过优化这些帧的交错和传输顺序进一步性能优化。
服务器推送:服务器可以对一个客户端请求发送多个响应。服务器还可以向客户端推送资源而且无需客户端明确的请求。
头部压缩:在 HTTP 2.0 中,使用了 HPACK(HTTP2头部压缩算法)压缩格式对传输的头部进行编码,减少了头部的大小。并在两端维护了索引表,用于记录出现过的头部,之后在传输过程中就可以传输已经记录过的头部健名,对端收到数据后就可以通过键名找到对应的值。
如果想要了解更多 HTTP2.0 的知识可以看看《Web性能权威指南》这本书,里面讲得挺详细的。
网络架构模型除了TCP/IP模型之外,还有OSI模型。OSI模型实际上是多了三层。
OSI模型.png
也就是上面所说的多加了 SSL 和 SPDY 这两个协议(都处于应用层)。
而数据链路层细分的话有两层:
数据链路层: 在不可靠的物理链路上,提供可靠的数据传输服务。包括组帧、物理编址、流量控制、差错控制、接入控制等等。
物理层: 主要功能就是连接网络设备。
前面也说到,HTTP 是无状态的协议,它不会对之前发送过的请求和响应的状态进行管理。假设,客户端的用户发送一个请求,服务器端收到请求后想知道这个请求是哪个家伙发过来的,那么就要有一个状态进行管理。而Cookie
正是解决这类问题而出现。
从服务器端返回的响应头信息中有一个Set-Cookie
的字段信息,告诉客户端保存Cookie
。在下次客户端向服务器端发送请求时,客户端会自动在请求头信息中加入Cookie
的值发送出去。
服务器收到客户端发送过来的Cookie
之后,会检查到底是从哪个客户端发送过的请求,然后对比服务器上的记录,最后得到了之前的状态信息。
Set-Cookie
Set-Cookie
是属于响应头中的一个字段,它包含以下的值。
NAME=VALUE
: Cookie
的名称和值。
expires=DATE
: Cookie
的有效期。
path=PATH
: 把服务器上的文件目录作为Cookie
的使用对象,如果没有设置,默认是文档所在的文件目录。
domain=域名
: 作为Cookie
适用对象的域名,如果没有设置,默认是创建Cookie
的服务器的域名。
Secure
: 只有在 HTTPS 时才会发送Cookie
。
HttpOnly
: JavaScript 不能访问Cookie
。主要是为了防止跨站脚本攻击时Cookie
的信息窃取。
请求头中的Cookie字段
Cookie
是请求头中的一个字段,它包含服务器通过Set-Cookie
头部设置并存到客户端的值。如果接收多个Cookie
时,可以以多个Cookie
形式发送回去。
cookie.jpg
以上是我一个多月在学 HTTP 相关书籍做的一些笔记。哪里有不对的地方,请各位大佬多多指点!当然文章对你有所帮助,欢迎点赞加关注哦!
为什么阿里巴巴的程序员成长速度这么快?
进大厂也就这回事,工作2到3年后进大厂操作指南
阿里架构师【柏羲】带你揭秘架构项目实战与源码解读:微博+B站架构设计、JUC核心、Mybatis源码
如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
关注公众号 『 Java斗帝 』,不定期分享原创知识。
同时可以期待后续文章ing