Http协议
一、介绍
1、HTTP是什么
超文本传输协议HTTP:这是一种最基本的客户机/服务器的访问协议;浏览器向服务器发送请求,而服务器回应相应的网页。
http(超文本传输协议)是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式,HTTP1.1版本中给出一种持续连接的机制,绝大多数的Web开发,都是构建在HTTP协议之上的Web应用。
2、为什么需要它
HTTP 协议(Hypertext Transfer Protocol)已经成为互联网上最常用的应用层协议,然而其本身只是用于传输超文本的网络协议,不会提供任何安全上的保证,使用明文在互联网上传输数据包使得窃听和中间人攻击成为可能,通过 HTTP 传输密码其实与在互联网上裸奔也差不多。
网景(Netscape)在 1994 年设计了 HTTPS 协议,使用安全套接字层(Secure Sockets Layer,SSL)保证数据传输的安全,随着传输层安全协议(Transport Layer Security,TLS)的发展,目前我们已经使用 TLS 取代了废弃的 SSL 协议,不过仍然使用 SSL 证书一词。
HTTPS 是对 HTTP 协议的扩展,我们可以使用它在互联网上安全地传输数据,然而 HTTPS 请求的发起方第一次从接收方获取响应需要经过 4.5 倍的往返延迟(Round-Trip Time,RTT)。
后面将详细介绍请求发起和响应的过程,分析为什么 HTTPS 协议需要通过 4.5-RTT 的时间获得服务提供方的响应:
TCP 协议 — 通信双方通过三次握手建立 TCP 连接;
TLS 协议 — 通信双方通过四次握手建立 TLS 连接;
HTTP 协议 — 客户端向服务端发送请求,服务端发回响应;
这里的分析建立在特定版本的协议实现以及常见场景上,随着网络技术的发展,我们能够减少需要的网络通信次数,本文会在对应章节中提到一些常见的优化方案。
HTTP 协议作为应用层协议,它需要底层的传输层协议为其提供基本的数据传输功能,HTTP 协议一般都会使用 TCP 协议作为底层协议。为了阻止错误的建立历史连接,TCP 协议通信的双方会通过三次握手建立 TCP 连接。
二、它的整体框架分析
三、报文分析
1、HTTP请求报文
HTTP请求报文由4部分组成(请求行+请求头+空行+请求体)
①是请求方法,GET和POST是最常见的HTTP方法,除此以外还包括DELETE、HEAD、OPTIONS、PUT、TRACE。不过,当前的大多数浏览器只支持GET和POST,Spring 3.0提供了一个HiddenHttpMethodFilter,允许你通过“_method”的表单参数指定这些特殊的HTTP方法(实际上还是通过POST提交表单)。服务端配置了HiddenHttpMethodFilter后,Spring会根据_method参数指定的值模拟出相应的HTTP方法,这样,就可以使用这些HTTP方法对处理方法进行映射了。
②为请求对应的URL地址,它和报文头的Host属性组成完整的请求URL,③是协议名称及版本号。
④是HTTP的报文头,报文头包含若干个属性,格式为“属性名:属性值”,服务端据此获取客户端的信息。
⑤是报文体,它将一个页面表单中的组件值通过param1=value1¶m2=value2的键值对形式编码成一个格式化串,它承载多个请求参数的数据。不但报文体可以传递请求参数,请求URL也可以通过类似于“/chapter15/user.html? param1=value1¶m2=value2”的方式传递请求参数。
对照上面的请求报文,我们把它进一步分解,你可以看到一幅更详细的结构图:
(1)请求行
请求行:请求行由三个标记组成:请求方法、请求URI和HTTP版本,它们用空格分隔。
例如:GET /index.html HTTP/1.1
HTTP规范定义了8种可能的请求方法:
GET 检索URI中标识资源的一个简单请求;
HEAD 与GET方法相同,服务器只返回状态行和头标,并不返回请求文档;
POST 服务器接受被写入客户端输出流中的数据的请求;
PUT 服务器保存请求数据作为指定URI新内容的请求;
DELETE 服务器删除URI中命名的资源的请求;
OPTIONS 关于服务器支持的请求方法信息的请求;
TRACE Web服务器反馈Http请求和其头标的请求;
CONNECT 已文档化但当前未实现的一个方法,预留做隧道处理。
(2)请求头
1. Accept:告诉WEB服务器自己接受什么介质类型,*/* 表示任何类型,type/* 表示该类型下的所有子类型,type/sub-type。
2. Accept-Charset: 浏览器申明自己接收的字符集
Accept-Encoding: 浏览器申明自己接收的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法 (gzip,deflate)
Accept-Language::浏览器申明自己接收的语言语言跟字符集的区别:中文是语言,中文有多种字符集,比如big5,gb2312,gbk等等。
3. Accept-Ranges:WEB服务器表明自己是否接受获取其某个实体的一部分(比如文件的一部分)的请求。bytes:表示接受,none:表示不接受。
4. Age:当代理服务器用自己缓存的实体去响应请求时,用该头部表明该实体从产生到现在经过多长时间了。
5. Authorization:当客户端接收到来自WEB服务器的 WWW-Authenticate 响应时,该头部来回应自己的身份验证信息给WEB服务器。
6. Cache-Control:请求:no-cache(不要缓存的实体,要求现在从WEB服务器去取)
max-age:(只接受 Age 值小于 max-age 值,并且没有过期的对象)
max-stale:(可以接受过去的对象,但是过期时间必须小于
max-stale 值)
min-fresh:(接受其新鲜生命期大于其当前 Age 跟 min-fresh 值之和的 缓存对象)
响应:public(可以用 Cached 内容回应任何用户)
private(只能用缓存内容回应先前请求该内容的那个用户)
no-cache(可以缓存,但是只有在跟WEB服务器验证了其有效后,才能返回给客户端)
max-age:(本响应包含的对象的过期时间)
ALL: no-store(不允许缓存)
7. Connection:请求:close(告诉WEB服务器或者代理服务器,在完成本次请求的响应 后,断开连接,不要等待本次连接的后续请求了)。
keepalive(告诉WEB服务器或者代理服务器,在完成本次请求的响应后,保持连接,等待本次连接的后续请求)。
响应:close(连接已经关闭)。
keepalive(连接保持着,在等待本次连接的后续请求)。
Keep-Alive:如果浏览器请求保持连接,则该头部表明希望 WEB 服务器保持连接多长时间(秒)。
例如:Keep-Alive:300
8. Content-Encoding:WEB服务器表明自己使用了什么压缩方法(gzip,deflate)压缩响应中的对象。
例如:Content-Encoding:gzip
Content-Language:WEB 服务器告诉浏览器自己响应的对象的语言。
Content-Length: WEB 服务器告诉浏览器自己响应的对象的长度。
例如:Content-Length: 26012
Content-Range: WEB 服务器表明该响应包含的部分对象为整个对象的哪个部分。
例如:Content-Range: bytes 21010-47021/47022
Content-Type: WEB 服务器告诉浏览器自己响应的对象的类型。
例如:Content-Type:application/xml
9. ETag:就是一个对象(比如URL)的标志值,就一个对象而言,比如一个html文件,如果被修改了,其Etag也会别修改,所以,ETag 的作用跟 Last-Modified的作用差不多,主要供WEB服务器判断一个对象是否改变了。比如前一次请求某个 html 文件时,
获得了其 ETag,当这次又请求这个文件时,浏览器就会把先前获得的 ETag 值发送给WEB服务器,然后WEB 服务器会把这个 ETag 跟该文件的当前 ETag 进行对比,然后就知道这个文件 有没有改变了。
10. Expired:WEB服务器表明该实体将在什么时候过期,对于过期了的对象,只有在跟WEB服务器验证了其有效性后,才能用来响应客户请求。 是 HTTP/1.0 的头部。
例如:Expires:Sat, 23 May 2009 10:02:12 GMT
11. Host:客户端指定自己想访问的WEB服务器的域名/IP 地址和端口号。
例如:Host:rss.sina.com.cn
12. If-Match:如果对象的 ETag 没有改变,其实也就意味著对象没有改变,才执行请求的动作。
If-None-Match:如果对象的 ETag 改变了,其实也就意味著对象也改变了,才执行请求的动作。
13. If-Modified-Since:如果请求的对象在该头部指定的时间之后修改了,才执行请求的动作(比如返回对象),否则返回代码304,告诉浏览器该对象没有修改。
例如:If-Modified-Since:Thu, 10 Apr 2008 09:14:42 GMT
If-Unmodified-Since:如果请求的对象在该头部指定的时间之后没修改过,才执行请求的动作(比如返回对象)。
14. If-Range:浏览器告诉 WEB 服务器,如果我请求的对象没有改变,就把我缺少的部分给我,如果对象改变了,就把整个对象给我。 浏览器通过发送请求对象的ETag 或者 自己所知道的最后修改时间给 WEB 服务器,让其判断对象是否改变了。 总是跟 Range 头部一起使用。
15. Last-Modified:WEB 服务器认为对象的最后修改时间,比如文件的最后修改时间, 动态页面的最后产生时间等等。
例如:Last-Modified:Tue, 06 May 2008 02:42:43 GMT
16. Location:WEB 服务器告诉浏览器,试图访问的对象已经被移到别的位置了,到该头部指定的位置去取。
例如:Location:http://i0.sinaimg.cn/dy/deco/2008/0528/sinahome_0803_ws_005_text_0.gif
17. Pramga:主要使用 Pramga: no-cache,相当于 Cache-Control: no-cache。
例如:Pragma:no-cache
18. Proxy-Authenticate: 代理服务器响应浏览器,要求其提供代理身份验证信息。
Proxy-Authorization:浏览器响应代理服务器的身份验证请求,提供自己的身份信息。
19. Range:浏览器(比如 Flashget 多线程下载时)告诉 WEB 服务器自己想取对象的哪部分。
例如:Range: bytes=1173546-
20. Referer:浏览器向 WEB 服务器表明自己是从哪个 网页/URL 获得/点击 当前请求中的网址/URL。
例如:Referer:http://www.sina.com/
21. Server: WEB 服务器表明自己是什么软件及版本等信息。
例如:Server:Apache/2.0.61 (Unix)
22. User-Agent: 浏览器表明自己的身份(是哪种浏览器)。
例如:User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14
23. Transfer-Encoding: WEB 服务器表明自己对本响应消息体(不是消息体里面的对象)作了怎样的编码,比如是否分块(chunked)。
例如:Transfer-Encoding: chunked
24. Vary: WEB服务器用该头部的内容告诉 Cache 服务器,在什么条件下才能用本响应所返回的对象响应后续的请求。
假如源WEB服务器在接到第一个请求消息时,其响应消息的头部为: Content-Encoding: gzip; Vary: Content-Encoding 那么 Cache 服务器会分析后续 请求消息的头部,检查其 Accept-Encoding,是否跟先前响应的 Vary 头部值一致,即是否使用相同的内容编码方法,这样就可以防止 Cache 服务器用自己Cache 里面压缩后的实体响应给不具备解压能力的浏览器。
例如:Vary:Accept-Encoding
25. Via: 列出从客户端到 OCS 或者相反方向的响应经过了哪些代理服务器,他们用 什么协议(和版本)发送的请求。
当客户端请求到达第一个代理服务器时,该服务器会在自己发出的请求里面 添加 Via 头部,并填上自己的相关信息,当下一个代理服务器 收到第一个代理 服务器的请求时,会在自己发出的请求里面复制前一个代理服务器的请求的Via头部,并把自己的相关信息加到后面, 以此类推,当 OCS 收到最后一个代理服 务器的请求时,检查 Via 头部,就知道该请求所经过的路由。
例如:Via:1.0 236-81.D07071953.sina.com.cn:80 (squid/2.6.STABLE13)
HTTP 请求消息头部实例:
Host:rss.sina.com.cn
User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14
Accept:text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language:zh-cn,zh;q=0.5
Accept-Encoding:gzip,deflate
Accept-Charset:gb2312,utf-8;q=0.7,*;q=0.7
Keep-Alive:300
Connection:keep-alive
Cookie:userId=C5bYpXrimdmsiQmsBPnE1Vn8ZQmdWSm3WRlEB3vRwTnRtW <-- Cookie
If-Modified-Since:Sun, 01 Jun 2008 12:05:30 GMT
Cache-Control:max-age=0
(3)空行
空行:最后一个请求头标之后是一个空行,发送回车符和退行,通知服务器以下不再有头标。
(4)请求体
2、HTTP响应报文
HTTP的响应报文也由4部分组成(响应行+响应头+空行+响应体)。
①报文协议及版本;
②状态码及状态描述;
③响应报文头,也是由多个属性组成;
④响应报文体,即我们真正要的“干货”。
(1)响应行
响应行:HTTP版本、响应代码和响应描述三个标记组成
HTTP版本:向客户端指明其可理解的最高版本。
响应代码:3位的数字代码,指出请求的成功或失败,如果失败则指出原因。
响应描述:为响应代码的可读性解释。
例如:HTTP/1.1 200 OK
HTTP响应码:
1xx 消息,一般是告诉客户端,请求已经收到了,正在处理,别急...
2xx 处理成功,一般表示:请求收悉、我明白你要的、请求已受理、已经处理完成等信息.
3xx 重定向到其它地方。它让客户端再发起一个请求以完成整个处理。
4xx 处理发生错误,责任在客户端,如客户端的请求一个不存在的资源,客户端未被授权,禁止访问等。
5xx 处理发生错误,责任在服务端,如服务端抛出异常,路由出错,HTTP版本不支持等。
100 继续 101 分组交换协 200 OK 201 被创建 202 被采纳 203 非授权信息 204 无内容 205 重置内容 206 部分内容
300 多选项 301 永久地传送 302 找到 303 参见其他 304 未改动 305 使用代理 307 暂时重定向
400 错误请求 401 未授权 402 要求付费 403 禁止 404 未找到 405 不允许的方法 406 不被采纳 407 要求代理授权408 请求超时 409 冲突 410 过期的 411 要求的长度 412 前提不成立 413 请求实例太大 414 请求URI太大 415 不支持的媒体类型
416 无法满足的请求范围 417 失败的预期 500 内部服务器错误 501 未被使用 502 网关错误 503 不可用的服务 504 网关超时 505 HTTP版本未被支持
(2)响应头
响应头标:像请求头标一样,它们指出服务器的功能,标识出响应数据的细节。
(3)响应行
空行:最后一个响应头标之后是一个空行,发送回车符和退行,表明服务器以下不再有头标。
(4)响应体
响应数据:HTML文档和图像等,也就是HTML本身。
3、HTTPS报文
CA(Certificate Authority)是负责管理和签发证书的第三方权威机构,是所有行业和公众都信任的、认可的。
CA证书,就是CA颁发的证书,可用于验证网站是否可信(针对HTTPS)、验证某文件是否可信(是否被篡改)等,也可以用一个证书来证明另一个证书是真实可信,最顶级的证书称为根证书。除了根证书(自己证明自己是可靠),其它证书都要依靠上一级的证书,来证明自己。
HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。具体是如何进行加密,解密,验证的,且看下图,下面的称为一次握手。
(1)Tcp三次握手
(2)TLS
TLS 的作用是在可靠的 TCP 协议上构建安全的传输通道,其本身是不提供可靠性保障的,我们还是需要下层可靠的传输层协议。在通信双方建立可靠的 TCP 连接之后,我们就需要通过 TLS 握手交换双方的密钥了,在这里我们将介绍 TLS 1.2 的连接建立过程
TLS 握手的关键在于利用通信双方生成的随机字符串和服务端的公钥生成一个双方经过协商后的密钥,通信的双方可以使用这个对称的密钥加密消息防止中间人的监听和攻击,保证通信的安全。
在 TLS 1.2 中,我们需要 2-RTT 才能建立 TLS 连接,但是 TLS 1.3 通过优化协议,将两次往返延迟降低至一次,大幅度减少建立 TLS 连接所需要的时间,让客户端可以在 1-RTT 之后就能向服务端传输应用层数据。
在已经建立好 TCP 和 TLS 通道上传输数据是比较简单的事情,HTTP 协议可以直接利用下层建立的可靠的、安全的通道传输数据。客户端通过 TCP 的套接字接口向服务端写入数据,服务端在接收到数据、进行处理后通过相同的途径返回。因为整个过程需要客户端发送请求以及服务端返回响应,所以耗时是 1-RTT。
(3)HTTP 请求和响应
HTTP 协议的数据交换只会消耗 1-RTT,当客户端和服务端仅处理一次 HTTP 请求时,从 HTTP 协议本身我们已经无法进行优化。不过随着请求的数量逐渐增加,HTTP/2 就可以复用已经建立的 TCP 连接减少 TCP 和 TLS 握手带来的额外开销。
当客户端想要通过 HTTPS 请求访问服务端时,整个过程需要经过 7 次握手并消耗 9 倍的延迟。如果客户端和服务端因为物理距离上的限制,RTT 约为 40ms 时,第一次请求需要 ~180ms;不过如果我们想要访问美国的服务器,RTT 约为 200ms 时,这时 HTTPS 请求的耗时为 ~900ms,这就是一个比较高的耗时了。我们来总结一下 HTTPS 协议需要 9 倍时延才能完成通信的原因:
TCP 协议需要通过三次握手建立 TCP 连接保证通信的可靠性(1.5-RTT);
TLS 协议会在 TCP 协议之上通过四次握手建立 TLS 连接保证通信的安全性(2-RTT);
HTTP 协议会在 TCP 和 TLS 上通过一次往返发送请求并接收响应(1-RTT);
需要注意的是,本文对往返延时的计算都基于特定的场景以及特定的协议版本,网络协议的版本在不断更新和演进,过去忽略的问题最开始都会通过补丁的方式更新,但是最后仍然会需要从底层完成重写。
HTTP/3 就是一个这样的例子,它会使用基于 UDP 的 QUIC 协议进行握手,将 TCP 和 TLS 的握手过程结合起来,把 7 次握手减少到了 3 次握手,直接建立了可靠并且安全的传输通道,将原本 ~900ms 的耗时降低至 ~500ms。
(4)HTTP 协议与 TCP/IP 协议的关系
HTTP 的长连接和短连接本质上是 TCP 长连接和短连接。HTTP 属于应用层协议,在传输层使用 TCP 协议,在网络层使用 IP 协议。IP 协议主要解决网络路由和寻址问题,TCP 协议主要解决如何在 IP 层之上可靠的传递数据包,使在网络上的另一端收到发端发出的所有包,并且顺序与发出顺序一致。TCP 有可靠,面向连接的特点。
(5)如何理解HTTP协议是无状态的
HTTP 协议是无状态的,指的是协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。也就是说,打开一个服务器上的网页和你之前打开这个服务器上的网页之间没有任何联系。HTTP 是一个无状态的面向连接的协议,无状态不代表 HTTP 不能保持 TCP 连接,更不能代表 HTTP 使用的是 UDP 协议(无连接)。
(6)什么是长连接、短连接?
短连接
连接->传输数据->关闭连接
HTTP是无状态的,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。
也可以这样说:短连接是指Socket连接后发送后接收完数据后马上断开连接。
长连接
连接->传输数据->保持连接 -> 传输数据-> 。。。->关闭连接。
长连接指建立Socket连接后不管是否使用都保持连接。
在 HTTP/1.0 中,默认使用的是短连接。也就是说,浏览器和服务器每进行一次 HTTP 操作,就建立一次连接,但任务结束就中断连接。如果客户端浏览器访问的某个 HTML 或其他类型的 Web 页中包含有其他的 Web 资源,如JavaScript 文件、图像文件、CSS 文件等;当浏览器每遇到这样一个 Web 资源,就会建立一个 HTTP 会话。
但从 HTTP/1.1 起,默认使用长连接,用以保持连接特性。使用长连接的 HTTP 协议,会在响应头有加入这行代码:
Connection:keep-alive
在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。Keep-Alive 不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如 Apache )中设定这个时间。实现长连接要客户端和服务端都支持长连接。
HTTP 协议的长连接和短连接,实质上是 TCP 协议的长连接和短连接。