HTTP
简介
HTTP协议是基于TCP/IP协议之上的应用层协议
超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP是万维网的数据通信的基础
。
HTTP的发展是由蒂姆·伯纳斯-李于1989年在欧洲核子研究组织(CERN)所发起。HTTP的标准制定由万维网协会(World Wide Web Consortium,W3C)和互联网工程任务组(Internet Engineering Task Force,IETF)进行协调,最终发布了一系列的RFC,其中最著名的是1999年6月公布的 RFC 2616,定义了HTTP协议中现今广泛使用的一个版本——HTTP 1.1。
2014年12月,互联网工程任务组(IETF)的Hypertext Transfer Protocol Bis(httpbis)工作小组将HTTP/2标准提议递交至IESG进行讨论,于2015年2月17日被批准。 HTTP/2标准于2015年5月以RFC 7540正式发表,取代HTTP 1.1成为HTTP的实现标准。
概述
HTTP是一个客户端终端(用户)和服务器端(网站)请求和应答的标准(TCP)。通过使用网页浏览器、网络爬虫或者其它的工具,客户端发起一个HTTP请求到服务器上指定端口(默认端口为80)。我们称这个客户端为用户代理程序(user agent)。应答的服务器上存储着一些资源,比如HTML文件和图像。我们称这个应答服务器为源服务器(origin server)。在用户代理和源服务器中间可能存在多个“中间层”,比如代理服务器、网关或者隧道(tunnel)。
尽管TCP/IP协议是互联网上最流行的应用,HTTP协议中,并没有规定必须使用它或它支持的层。事实上,HTTP可以在任何互联网协议上,或其他网络上实现。HTTP假定其下层协议提供可靠的传输。因此,任何能够提供这种保证的协议都可以被其使用。因此也就是其在TCP/IP协议族使用TCP作为其传输层。
通常,由HTTP客户端发起一个请求,创建一个到服务器指定端口(默认是80端口)的TCP连接。HTTP服务器则在那个端口监听客户端的请求。一旦收到请求,服务器会向客户端返回一个状态,比如”HTTP/1.1 200 OK”,以及返回的内容,如请求的文件、错误消息、或者其它信息。
工作原理
HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求报文,请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。服务器以一个状态行作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。
请求响应步骤
- 客户端连接到Web服务器
一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接。 - 发送HTTP请求
通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。 - 服务器接受请求并返回HTTP响应
Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。 - 释放TCP连接
若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;若connection 模式为keepalive
,则该连接会保持一段时间,在该时间内可以继续接收请求。 - 客户端浏览器解析HTML内容
客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。
报文结构
请求报文
起始行 + 请求头 + 空行 + 请求体
- 请求行:请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。例如,GET /index.html HTTP/1.1。
- 请求头:请求头部由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔。请求头部通知服务器有关于客户端请求的信息。
- 空行:最后一个请求头之后是一个空行,发送回车符和换行符,通知服务器以下不再有请求头。
- 请求体:浏览器端通过http协议发送给服务器的实体数据。
响应报文
状态行 + 响应头 + 响应体
- 状态行:格式:HTTP-Version Status-Code Reason-Phrase CRLF,HTTP-Version表示服务器HTTP协议的版本;Status-Code表示服务器发回的响应状态代码;Reason-Phrase表示状态代码的文本描述。
- 响应头:响应头用于描述服务器的基本信息,以及数据的描述,服务器通过这些数据的描述信息,可以通知客户端如何处理等一会儿它回送的数据。
- 响应体:响应体就是响应的消息体,如果是纯数据就是返回纯数据,如果请求的是HTML页面,那么返回的就是HTML代码,如果是JS就是JS代码。
URI
URI, 全称为(Uniform Resource Identifier), 也就是统一资源标识符,它的作用很简单,就是区分互联网上不同的资源。
但是,它并不是我们常说的网址
,网址指的是URL
,实际上URI
包含了URN
和URL
两个部分,由于 URL 过于普及,就默认将 URI 视为 URL 了。
超文本传输协议(HTTP)的统一资源定位符将从因特网获取信息的五个基本元素包括在一个简单的地址中:
- 传送协议。
- 层级URL标记符号(为[//],固定不变)
- 访问资源需要的凭证信息(可省略)
- 服务器。(通常为域名,有时为IP地址)
- 端口号。(以数字方式表示,若为HTTP的默认值“:80”可省略)
- 路径。(以“/”字符区别路径中的每一个目录名称)
- 查询。(GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)
- 片段。以“#”字符为起点
以http://www.luffycity.com:80/news/index.html?id=250&page=1 为例, 其中:
http,是协议
www.luffycity.com,是服务器
80,是服务器上的默认网络端口号,默认不显示
/news/index.html,是路径
?id=250&page=1,是查询
大多数网页浏览器不要求用户输入网页中“http://”的部分,因为绝大多数网页内容是超文本传输协议文件。同样,“80”是超文本传输协议文件的常用端口号,因此一般也不必写明。一般来说用户只要键入统一资源定位符的一部分(www.luffycity.com/news/index.html?id=250&page=1 )就可以了。
由于超文本传输协议允许服务器将浏览器重定向到另一个网页地址,因此许多服务器允许用户省略网页地址中的部分,比如 www。从技术上来说这样省略后的网页地址实际上是一个不同的网页地址,浏览器本身无法决定这个新地址是否通,服务器必须完成重定向的任务。
HTTP的特点和缺点
特点
- 灵活可扩展。主要体现在两方面:一个是语义上的自由,只规定了基本格式,比如空格分隔单词,换行分割字段,其他各个部分没有严格的语法限制;另一个是传输形式的多样性,不仅可以传输文本,还可以传输图片、视频等任意数据。
- 可靠传输。HTTP基于TCP/IP,因此把这一特性继承下来了。
- 请求-应答。客户端请求-服务器端应答或者服务器端请求-服务器端应答都行。
- 无状态。这里的状态指的是通信过程的上下文信息,而每次的HTTP请求都是独立、无关的,默认不需要保留状态信息。
缺点
- 无状态:这一点是优点还是缺点需要看场景:在长连接的场景中,需要保存大量的上下文信息,一面传输大量重复的信息,那么这时候无状态就是缺点了;但与此同时,另外一些应用只是为了获取一些数据,不需要保存连接上下文信息,无状态反而减少了网络开销,成为了HTTP的优点。
- 明文传输:协议里的报文(主要指的是头部)不适用二进制数据,而是采用文本形式。这当然为调试者提供了方便,但同时也让HTTP的报文暴露给外界,给攻击者提供了便利。
- 对头阻塞问题:当HTTP开启长连接时,共用一个TCP连接,同一时刻只能处理一个请求,那么当前请求耗时过长的情况下,其他请求只能处于阻塞状态,也就时著名的对头阻塞问题。
常见的请求方法
- GET:通常用来获取资源
- POST:提交数据
- HEAD:获取资源的元信息
- PUT:修改数据
- DELETE:删除资源
- CONNECT:建立连接隧道,用于代理服务器
- OPTIONS:列出可对资源实行的请求方法,用来跨域请求
- TRACE:追踪请求-响应的传输路径
GET和POST的区别
- GET请求会被浏览器主动缓存下来,留下历史记录,而POST不会
- GET请求只能进行URL编码,只能接受ASCII编码,POST没有限制
- GET请求的参数一般发在URL中,因此不安全,POST放在请求体中,更适合传输敏感信息
- GET请求是
幂等
的,而POST不是 - GET请求的参数是有长度限制的,而POST没有
- GET产生一个数据包,将请求报文一次性发送出去;POST产生两个数据包,首先发送header部分,如果服务器响应100 continue,浏览器再发送body部分(火狐浏览器除外,它只发送一次)
Content-Type与POST请求方法提交数据的关系
Content-Type | 提交的数据类型 |
---|---|
application/x-www/form-urlencoded | 表单数据 |
multipart/form-data | 表单文件上传 |
Application/json | 序列化JSON格式数据 |
Text/xml | XML数据 |
常见的请求头
Accept
- Accept:指定客户端能够接受的数据格式,例如text/plain
- Accept-CharSet:浏览器可以接受的字符编码集,例如chartset=utf-8
- Accept-Encoding:浏览器可以接受web服务器返回内容的压缩编码类型,例如g-zip
- Accept-Language:浏览器可以接受的语言,例如en,zh
缓存相关
- Date:请求发送的日期和时间,例如Thu Oct 01 2020 22:16:55 GMT+0800
- Cache-Control:指定的请求和响应遵循的缓存机制,例如max-age=3600
- If-Match:只有请求内容与实体匹配才有效
- If-Modified-Since:如果请求的部分在指定的时间后被修改则请求成功,未被修改则会返回304状态码
- If-None-Match:如果内容未改变返回304状态码,参数未服务器先前发送的Etag,与服务器回应的Etag比较是否改变
- If-Range:如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。参数也为Etag
- If-unModified-Since:只有实体在指定时间后未被修改才请求成功
其他
- Connection:表示是否需要持久连接(HTTP/1.1默认进行持久连接)例如close
- Cookie:HTTP发送请求时,会把保存在该域名下的所有cookie值一起发送到web服务器
- Content-Length:请求的内容长度,例如120
- Contetn-Type:请求的与实体对应的MIME信息,例如application/x-www-form-urlencoded
- Host:请求的服务器的域名和端口号,例如www.abc.com:80
- Pragma:用来包含实现特定的指令
- Proxy-Authorization:连接到代理的授权证书
- Range:只请求实体的一部分,指定范围,例如bytes=500-999
- Referer:先前网页的地址(如果是直接在url地址栏则没有这个字段)
- TE:客户端愿意接受的传输编码,并通知服务器接受接受尾加头信息
- Upgrade:向服务器指定某种传输协议以便服务器进行转换(如果支持)
- User-Agent:发出请求的用户信息
- Via:通知中间网关或代理服务器地址,通信协议
- Warning:关于消息实体的警告信息
常见的响应头
Content
- Content-Encoding:web服务器支持的返回内容压缩编码类型,例如gzip
- Content-Language:响应体的语言,例如en,zh
- Content-Length:响应体的长度,例如348
- Content-Location:请求资源可替代的备用的另一地址,例如/index.htm
- Content-MD5:返回资源的MD5校验值,例如Q2hlY2sgSW50ZWdyaXR5IQ==
- Content-Range:在整个返回体中本部分的子节位置,例如bytes 21010-47021/47022
- Content-Type:返回内容的MIME类型,例如text/html,charse=utf8
缓存相关
- Cache-Control:告诉所有的缓存机制是否可以缓存及哪种类型
- Date:原始服务器消息发出的时间
- Etag:请求变量的实体标签的当前值
- Expires:响应过期的日期和时间
- Last-Modified:请求资源的最后修改时间
其他
Accept-Ranges:表明服务器是否支持指定范围请求及哪种类型的分段请求
Allow:对某网络资源的有效的请求行为,不允许则返回405
Age:从原始服务器到代理缓存形成的估算时间(以秒计,非负)
Location:用来重定向接收方到非请求URL的位置来完成请求或标识新的资源
Pragma:包括实现特定的指令,它可应用到响应链上的任何接收方
Proxy-Authenticate:它指出认证方案和可应用到代理的该URL上的参数
refresh:应用于重定向或一个新的资源被创造,在5秒之后重定向(由网景提出,被大部分浏览器支持)
Retry-After:如果实体暂时不可取,通知客户端在指定时间之后再次尝试
Server:web服务器软件名称
Set-Cookie:设置Http Cookie
Trailer:指出头域在分块传输编码的尾部存在
Transfer-Encoding:文件传输编码
Vary:告诉下游代理是使用缓存响应还是从原始服务器请求
Warning:警告实体可能存在的问题
WWW-Authenticate:表明客户端请求实体应该使用的授权方案
状态码
1xx:表示目前是协议的中间状态
- 101 Switching Protocols 在
HTTP
升级为WebSocket
的时候,如果服务器同意变更,就会发送状态码 101
2xx:表示成功状态
- 200 OK 成功状态码,通常在响应体中有数据
- 204 No Content 含义与200相同,但响应头后没有body数据
- 206 Partial Content 成功状态码,响应体中有部分内容,使用场景为HTTP分块下载和断点续传
3xx:表示重定向
- 301 Moved Permanently 永久重定向,假如一个网站迁移到另一个地址了,以前的再也不用了,可以返回301,当下次访问的时候会直接访问重定向的那个地址。
- 302 Found 临时重定向,如果一个网站只是暂时不用了,那么返回302就行了
- 304 Not Modified 请求的资源未修改,当进行了协商缓存时会返回这个状态码
- 307 Temporary Redirect 临时重定向,和302含义类似,但是期望客户端保持请求方法不变向新的地址发出请求
4xx:表示请求错误
400 Bad Request 报文存在语法错误
401 Unauthorized 发送的请求需要有通过 HTTP 认证的认证信息
403 Forbidden 服务器禁止访问
404 Not Found 资源未找到
405 Method Not Allowed 请求方法不被服务器允许
408 Request Timeout 服务器等待太长时间
409 Conflict 多个请求发生冲突
429 Too Many Request 客户端发送的请求过多
5xx:服务器端发生错误
500 Internal Sever Error 服务器端在执行请求时发生了错误
502 Bad Geteway 服务器自身正常,但访问的时候出错了
503 Service Unavailable 服务器超载或维护,暂时无法响应服务
HTTP版本介绍
HTTP/1.0
HTTP 协议老的标准是HTTP/1.0,为了提高系统的效率,HTTP/1.0规定浏览器与服务器只保持短暂的连接,浏览器的每次请求都需要与服务器建立一个TCP连接,服务器完成请求处理后立即断开TCP连接,服务器不跟踪每个客户也不记录过去的请求。
HTTP/1.1
缓存处理
在HTTP/1.0中主要使用header里的If-Modified-Since
,Expires
来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Etag
,If-Unmodified-Since
, If-Match
, If-None-Match
等更多可供选择的缓存头来控制缓存策略。
带宽优化及网络连接的使用
HTTP/1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP/1.1则在请求头引入了Range
头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
错误通知的管理
在HTTP/1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
Host头处理
在HTTP/1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
长连接
HTTP/1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP/1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP/1.0每次请求都要创建连接的缺点。
HTTP/2
头部压缩
- 首先在服务器和客户端之间建立哈希表,将用到的字段存放在这张表中,那么在传输的时候对于之前出现过的值,只需要把索引传递给对方即可,对方拿到索引查表就行了。
- 其次对于整数和字符串进行哈夫曼编码,哈夫曼编码的原理就是先将所有出现的字符建立一个索引表,然后让出现次数多的字符对应的索引尽可能短,传输地时候也是传输这样地索引序列,可以达到非常高的压缩率。
多路复用
- HTTP队头阻塞
HTTP传输是基于请求-应答
的模式进行的,报文必须是一发一收,但值得注意的是,里面的任务被放在一个任务队列中串行执行,一旦队首的请求处理太慢,就会阻塞后面的请求处理。
- 二进制分帧
HTTP/2会将报文全部转换成二进制格式,全部传输01
串,方便及其解析。原来的Headers+Body
的报文格式被拆分成了一个个二进制的帧,用Headers帧存放头部字段,Data帧存放请求体数据。分帧之后,服务器看到的不再是一个个完整的HTTP请求报文,而是一堆乱序的二进制帧。这些二进制帧不存在先后关系,因此也不会排队等待,也就没有了HTTP队头阻塞问题。
通信双方都可以给对方发送二进制帧,这种二进制帧的双向传输的序列,也叫做流
。HTTP/2用流
来在一个TCP连接上进行多个数据帧的通信,这就是多路复用的概念。
服务端推送
在HTTP/2中,服务器已经不再是完全被动地接受请求,响应请求,它也能新建流
来给客户端发送消息,当TCP连接建立后,比如浏览器请求一个HTML文件,服务器就可以在返回HTML地基础上,将HTML中引用到的其他资源文件一起返回给客户端,减少客户端地等待。
HTTP/3
HTTP/2使用了多路复用,一般来说同一域名下只需要使用一个 TCP 连接。但当这个连接中出现了丢包的情况,那就会导致整个 TCP 都要开始等待重传,也就导致了后面的所有数据都被阻塞了。Google 基于 UDP 协议推出了一个的 QUIC 协议,并且使用在了 HTTP/3 上。
避免包阻塞 :多个流的数据包在TCP连接上传输时,若一个流中的数据包传输出现问题,TCP需要等待该包重传后,才能继续传输其它流的数据包。但在基于UDP的QUIC协议中,不同的流之间的数据传输真正实现了相互独立互不干扰,某个流的数据包在出问题需要重传时,并不会对其他流的数据包传输产生影响。
快速重启会话: 普通基于TCP的连接,是基于两端的IP和端口和协议来建立的。在网络切换场景,例如手机端切换了无线网,使用4G网络,会改变本身的ip,这就导致tcp连接必须重新创建。而QUIC协议使用特有的UUID来标记每一次连接,在网络环境发生变化的时候,只要UUID不变,就能不需要握手,继续传输数据。