HTTP协议理解与应用
2013-10-25 12:13:06 阿炯

超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议,HTTP是万维网的数据通信的基础。设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法,通过HTTP或HTTPS协议请求的资源由统一资源标识符(Uniform Resource Identifiers,URI)来标识。HTTP的发展是由蒂姆·伯纳斯-李于1989年在欧洲核子研究组织(CERN)所发起。

HTTP的标准制定由万维网协会(World Wide Web Consortium,W3C)和互联网工程任务组(Internet Engineering Task Force,IETF)进行协调,最终发布了一系列的RFC,其中最著名的是1999年6月公布的 RFC 2616,定义了HTTP协议中现今广泛使用的一个版本--HTTP 1.1。


HTTP的几个重要概念

1、连接:Connection

一个传输层的实际环流,它是建立在两个相互通讯的应用程序之间。在http1.1,request和reponse头中都有可能出现一个connection的头,此header的 含义是当client和server通信时对于长链接如何进行处理。

在http1.1中,client和server都是默认对方支持长链接的,如果client使用http1.1协议,但又不希望使用长链接,则需要在header中指明connection的值为close;如果server方也不想支持长链接,则在response中也需要明确说明connection的值为close。 不论request还是response的header中包 含了值为close的connection,都表明当前正在使用的tcp链接在当天请求处理完毕后会被断掉。以后client再进行新 的请求时就必须创建新的tcp链接了。

2、消息:Message

HTTP通讯的基本单位,包括一个结构化的八元组序列并通过连接传输。

3、请求:Request

一个从客户端到服务器的请求信息包括应用于资源的方法、资源的标识符和协议的版本号。

4、响应:Response

一个从服务器返回的信息包括HTTP协议的版本号、请求的状态(例如“成功”或“没找到”)和文档的MIME类 型。

5、资源:Resource

由URI标识的网络数据对象或服务。

6、实体:Entity

数据资源或来自服务资源的回映的一种特殊表示方法,它可能被包围在一个请求或响应信 息中。一个实体包括实体头信息和实体的本身内容。

7、客户机:Client

一个为发送请求目的而建立连接的应用程序。

8、用户代理:UserAgent

初始化一个请求的客户机。它们是浏览器、编辑器或其它用户工具。

9、服务器:Server

一个接受连接并对请求返回信息的应用程序。

10、源服务器:Originserver

是一个给定资源可以在其上驻留或被创建的服务器。

11、代理:Proxy

一个中间程序,它可以充当一个服务器,也可以充当一个客户机,为其它客户机建立请求。请求是通过可能的翻译在内部或经过传递到其它的服务器中。一个代理在发送请求信息之前,必须解释并且如果可能重写它。代理经常作为通过防火墙的客户机端的门户,代理还可以作为一个帮助应用来通过协议处 理没有被用户代理完成的请求。

12、网关:Gateway

一个作为其它服务器中间媒介的服务器。与代理不同的是,网关接受请求就好象对被请求的资源来说它就是源服务器;发出请求的客户机并没有意识到它在同网关打交道。网关经常作为通过防火墙的服务器端的门户,网关还可以作为一个协议翻译器以便存取那 些存储在非HTTP系统中的资源。

13、通道:Tunnel

是作为两个连接中继的中介程序。一旦激活,通道便被认为不属于HTTP通讯,尽管通道可能是被一个HTTP请求初始化的。当被中继的连接两端关闭时,通道便消失。当一个门户(Portal)必须存在或中介(Intermediary)不能解释中继的通讯时通道被经常使用。

14、缓存:Cache

反应信息的局域或本地存储。


Request & Response

Request格式


<request-line> 比如:GET /api/index.json HTTP/1.1
<headers> 比如:Accept: */*; User-Agent: Mozilla/4.0;……
<blank line>
[<request-body>] 比如:id=1&timestamp=xxxxxx

Response格式

<status-line> 比如:HTTP/1.1 200 OK
<headers> 比如:Content-Type: application/json;……
<blank line>
[<response-body>] 比如:{"id":1,"username":"testuser"}
Status Code

http的状态码有将近60个,这里主要记录一些常见的非正常情况下产生的状态码,在平常应用中或多或少会碰到,有助于我们去理解和发现问题。

206 - 断点下载时用到,客户端请求了一部分内容,服务器成功把这部分内容返回给它,这时候就是用这个状态。

301 - 永久跳转,原地址不存在了,url被指向到另一个地址。这个主要是搜索引擎相关,影响爬虫的检索行为。

302 - 临时跳转,服务器会返回一个新的url给客户端,客户端可以继续访问这个url来获取内容。

304 - 资源没有改变,客户端可以使用本地缓存的内容,常见于静态内容访问。

413 - 请求实体太大。常见的情况是上传大文件,但超出了服务器(比如nginx)限制。或者请求头或请求体超出了后端的server(比如tomcat)的设置比如当前域名下cookie太多,超出了请求头限制

416 - 跟断点续传有关,客户端请求的范围超出了服务器上文件大小。

500 - 服务器内部错误,不能返回正常的结果。比如最常见的应用抛出空指针异常未进行处理。

502 - 网关错误。常见的情况是反向代理后端的服务器(比如resin或tomcat)没有启动。

503 - 服务不可用。比如服务器负载太高或者服务器已经停止服务。

504 - 网关超时。比如请求时长超出了服务器的响应时间限制。

Headers

http headers分为请求头(Request Header)和响应头(Response Header)两类。下面是我们经常用到的一些header.

1.缓存控制

在互联网站的应用中,缓存几乎无处不在,在基于http的服务中,我们也可以对一些不常改变的内容在客户端进行缓存,这样在多次访问中可以复用缓存内容,加快访问速度,提升用户体验。http的协议里规定了一些用于缓存控制的http消息头:

Cache-Control(HTTP/1.1)/Pragma(HTTP/1.0):指示客户端是否进行缓存以及缓存的时间是多长。默认值是private,也就是把内容缓存在用户私有空间。比如:Cache-Control:max-age=86400,must-revalidate,这是告诉客户端所请求的资源缓存一天max-age单位是秒,相对时间,过期之后必须进行重新检验。

Expires:指定客户端如果不强制刷新的话在多长时间里可以不向服务器发请求,直接读本地缓存。

注意:
优先级:Cache-Control > Expires;
详细参数说明:http://condor.depaul.edu/dmumaugh/readings/handouts/SE435/HTTP/node24.html
不同浏览器的不同行为刷新,后退,地址栏回车等在实现上可能有差异;
Last-Modified/If-Modified-Since:Last-Modified是服务器端返回给客户端的资源最后修改时间戳,这样,客户端在下次请求时比如强制刷新会带上If-Modified-Since参数来校验资源是否有更新,没有更新的话服务器就返回304状态码,客户端直接取本地缓存的资源。这个时候只有请求开销,没有网络传输开销。注意:时间戳必须是格林威治(GMT)时间,比如:Last-Modified:Sat, 19 Oct 2013 09:20:15 GMT

ETag/If-None-Match:ETag是根据文件属性通过一定算法生成的资源标识,也是用来确定客户端请求的资源是否有更新。如果服务器返回了一个ETag值给客户端,那么下次客户端请求时会带上If-None-Match参数来校验资源是否更新,没有更新的就返回304状态码。效果基本等同于Last-Modified

注意:ETag需要计算,对于计算资源紧张的服务器来说是一种消耗,所以有些网站直接不使用ETag;如果服务器在负载均衡后面,同一个资源的请求可能分发到不同的后端机器上,由于ETag的计算依赖于文件属性,不同机器上内容相同的文件可能生成的ETag不同,这样就可能使本来内容没变的文件通过ETag校验失败。这里有两种解决方案:一是etag计算不依赖于本地机器,比如直接算文件内容的md5值;二是在负载均衡器上把相同的url请求分发到同一台后端机器。在我们的实际业务场景下,http的缓存具有非常大的用途,下面列举一些:

充分利用客户端的资源,比如一些客户端需要频繁访问的静态文件,像LOGO,广告图等,完全可以缓存在客户端本地。这样可以减少网络请求,加快客户端展示,还能减少服务器请求的压力。

我们的一些静态内容,比如新闻,博客等,在被搜索引擎爬虫抓取的时候,通过控制缓存参数,就可以减少爬虫的抓取频率,减少不必要的资源浪费。

如果我们的静态资源使用了CDN,那么设置了http缓存就可以在CDN节点上保存一份文件,减少CDN的回源次数,减少网络延时和源站服务器压力。


2.断点请求

Accept-Ranges:服务端支持断点下载时会返回这个响应头给客户端,当客户端知道这个以后就可以发送断点请求了。

Content-Length:响应信息的长度,告诉客户端当前请求返回了多少数据。这里要注意一下,用head方法提交请求时不会返回具体数据,但是这个Content-Length会返回完整数据的大小。

Range/Content-Range:客户端请求时提交名为Range的header,告诉服务器自己要请求哪部分的数据。比如:Range: bytes=0-1023表示请求第0到1023个字节.然后服务器返回这1024个字节的内容给客户端,响应头中会带上Content-Range。即:Content-Range: bytes 0-1023/4096,这个4096就是文件总大小。客户端下次请求可以从第1024个字节处开始,Range: bytes=1024-xxxx

断点续传和多线程下载的实现原理

HTTP协议的GET方法,支持只请求某个资源的某一部分;
206 Partial Content 部分内容响应;
Range 请求的资源范围;
Content-Range 响应的资源范围;
在连接断开重连时,客户端 只请求该资源未下载的部分,而不是重新请求整个资源,来实现断点续传。

分块请求资源实例:
Eg1:Range: bytes=306302- :请求这个资源从306302个字节到末尾的部分;
Eg2:Content-Range: bytes 306302-604047/604048:响应中指示携带的是该资 源的第306302-604047的字节,该资源共604048个字节;

客户端通过并发的请求相同资源的不同片段,来实现对某个资源的并发分块下载,从而达到快速下载的目的。

多线程下载的原理:
下载工具开启多个发出HTTP请求的线程;
每个http请求只请求资源文件的一部分:Content-Range: bytes 20000-40000/47000;
合并每个线程下载的文件。


3.编码

Accept-Encoding/Content-Encoding:前者是客户端支持接收的消息编码类型。默认是identity,可选值有gzip,compress等。后者是服务器端响应信息的内容编码类型,常用的就是压缩。压缩的好处显而易见,可以大大减少网络传输的开销,相对于服务器端压缩产生的cpu消耗,网络传输的减少显然更实在。常见形式:Content-Encoding: gzip,deflate,compress.通常我们对html,js,css,xml,json之类的响应结果可以进行压缩传输。

Transfer-Encoding:response header.响应消息的传输编码类型,规定了网络传输的形式。一般都是下面这种形式:Transfer-Encoding: chunked。当服务器产生动态内容,不知道响应信息的具体长度时,可以通过这个指定分块进行传输,处理多少数据就返回多少数据,这样不用等到数据都准备好了一次性返回。结合上面的内容编码,比如gzip,可以分块压缩并进行传输。另外,请注意,在使用这种编码传输时,我们是看不到Content-Length的,因为内容还没有完全生成。

4.其他

X-Forward-For:request header. 用来标识用户的真实ip,特别是通过代理(正向或反向)访问服务器或是服务器在负载均衡设备后面的情况。格式:X-forward-For: client,proxy1,proxy2,…最左边的是最接近客户端的ip。

User-Agent:request header.服务器用来识别客户端基本信息的请求头。一般这个在识别搜索爬虫的时候有用,某些场景下也可以用这个来做一些客户端的统计。

Referer:request header.客户端访问服务器时,这个Referer来指定请求来源,比如是从哪个网站链接过来的,我们在一些统计中会经常用到这个。另外,还有一个重要的用途就是在需要资源防盗链的场景中来过滤非法的请求来源但是,这个referer是客户端可以伪造的。

Location:response header.在301/302状态码的响应头中,都会带上这个Location头,来指示客户端用新的地址去访问需要的资源。

Connection:request/response header.在http/1.1中,客户端和服务端默认都是保持连接的,也就是Connection: keep-alive.如果任何一方不想保持连接,都可以把这个值设置为close.默认情况下,客户端和服务端会保持一个长连接,这样客户端就可以用这个连接发送多次http请求,减少频繁创建连接带来的消耗。对于这个参数,在服务端可能要做更多的设置,比如连接keep-alive的时间,服务器内核的一些网络参数设置(针对tcp)。

Session和Cookie

http请求是无状态的请求,但是在我们的互联网应用中,经常需要标识用户状态信息来完成一些交互性的操作,比如用户认证要记录用户登录状态,购物车应用要记住用户选择的商品,广告投放应用要记录用户的历史浏览行为等等。这里就会用到session和cookie了。

session:是指http请求-响应的过程中客户端与服务器端的交互状态,这些信息被保存在服务器端,比如内存,数据库等。每个session都有一个唯一标识,由服务器生成,这个标识也要在客户端进行保存,这样客户端在下次请求时可以带上这个标识,方便服务器判断客户端的状态。

客户端对session的支持:

通过cookie保存session id,在请求时发送给服务器。

通过url的参数携带session id与服务器通信。

通过表单的隐藏字段携带session id与服务器通信。

session共享的问题:

在分布式应用中,我们的http server一般都架在反向代理或是负载均衡设备后面,这就会面临一个session共享的问题。也就是同一个用户的多个请求可能被分发到多个不同的机器,如果我们把session保存在机器本地内存中的话,就无法在多个机器间共享用户的session。这个问题,一般来说,我们可以有两种方式来解决:

把session存放到分布式的内存(eg:memcached)或是集中式存储中(eg:database)。

在反向代理或负载均衡设备上把相同用户的请求分发到同一台机器这里要处理好机器宕机后请求重新分配的问题。

cookie:在客户端保持状态化信息,每个cookie内容都属于特定的域(domain)和路径(path),出于安全考虑,不同域或路径下的cookie不能共享。

会话cookie:没有指定过期时间,保存在内存,浏览器关闭后就失效。

持久cookie:指定了过期时间,保存在浏览器本地。

详细内容可以参考:http://en.wikipedia.org/wiki/HTTP_cookie

需要注意的是cookie会存在一些安全方面的问题。

推荐两个很好的http调试工具:fiddler(windows)和charles(mac)有http代理功能,对于不是基于浏览器的http应用(比如mobile app),可以用这两个工具来监控http请求。

http协议之请求方法,下述Get与Post方法

1、Get是用来从服务器上获得数据,而Post是用来向服务器上传递数据。 

2、Get将表单中数据的按照variable=value的形式,添加到action所指向的URL后面,并且两者使用“?”连接,而各个变量之间使用“&”连接;Post是将表单中的数据放在form的数据体中,按照变量和值相对应的方式,传递到action所指向URL。 

3、Get是不安全的,因为在传输过程,数据被放在请求的URL中,而如今现有的很多服务器、代理服务器或者用户代理都会将请求URL记录到日志文件中,然后放在某个地方,这样就可能会有一些隐私的信息被第三方看到。另外,用户也可以在浏览器上直接看到提交的数据,一些系统内部消息将会一同显示在用户面前。Post的所有操作对用户来说都是不可见的。 

4、Get传输的数据量小,这主要是因为受URL长度限制;而Post可以传输大量的数据,所以在上传文件只能使用Post当然还有一个原因,将在后面的提到。 

5、Get限制Form表单的数据集的值必须为ASCII字符;而Post支持整个ISO10646字符集,默认是用ISO-8859-1编码 

6、Get是Form的默认方法。

HTTP的底层是TCP/IP。所以GET和POST的底层也是TCP/IP,也就是说,GET/POST都是TCP链接,GET和POST能做的事情是一样一样的。你要给GET加上request body,给POST带上URL参数,技术上是完全行的通的。

但是,业界有不成文的规定,大多数浏览器通常都会限制url长度在2K个字节,而大多数服务器最多处理64K大小的URL。超过的部分,恕不处理。如果你用GET服务,在request body偷偷藏了数据,不同服务器的处理方式也是不同的,有些服务器会帮你卸货,读出数据,有些服务器直接忽略,所以,虽然GET可以带request body,也不能保证一定能被接收到。

所以HTTP协议建议GET请求参数只放在URL,URL参数有长度限制;而POST请求参数放在request body,这样就没有长度限制。

GET和POST还有一个重大区别,简单的说:
GET产生一个TCP数据包;POST产生两个TCP数据包。

对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200返回数据;而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok返回数据。也就是说,GET只需要通信一次,而POST需要两次,第一次,先去和服务器打个招呼“嗨,我等下要发送一批数据来,你们打开门迎接我”,然后再回头把数据送过去。因为POST需要两步,时间上消耗的要多一点,看起来GET比POST更有效。下面是它们的一些特点:

1. GET与POST都有自己的语义,不能随便混用。

2. 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。

3. 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。


完整的请求方法

HTTP/1.1协议中共定义了八种方法(也叫“动作”)来以不同方式操作指定的资源,上一节已经介绍了最主要也是最重要的两种:

GET
向指定的资源发出“显示”请求。使用GET方法应该只用在读取数据,而不应当被用于产生“副作用”的操作中,例如在Web Application中。其中一个原因是GET可能会被网络蜘蛛等随意访问。参见安全方法

HEAD
与GET方法一样,都是向服务器发出指定资源的请求。只不过服务器将不传回资源的本文部分。它的好处在于,使用这个方法可以在不必传输全部内容的情况下,就可以获取其中“关于该资源的信息”(元信息或称元数据)。

POST
向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有。

PUT
向指定资源位置上传其最新内容。

DELETE
请求服务器删除Request-URI所标识的资源。

TRACE
回显服务器收到的请求,主要用于测试或诊断。

OPTIONS
这个方法可使服务器传回该资源所支持的所有HTTP请求方法。用'*'来代替资源名称,向Web服务器发送OPTIONS请求,可以测试服务器功能是否正常运作。

CONNECT
HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的链接(经由非加密的HTTP代理服务器)。方法名称是区分大小写的。当某个请求所针对的资源不支持对应的请求方法的时候,服务器应当返回状态码405(Method Not Allowed),当服务器不认识或者不支持对应的请求方法的时候,应当返回状态码501(Not Implemented)。

HTTP服务器至少应该实现GET和HEAD方法,其他方法都是可选的。当然所有的方法支持的实现都应当匹配下述的方法各自的语义定义。此外,除了上述方法,特定的HTTP服务器还能够扩展自定义的方法。例如:

PATCH(由 RFC 5789 指定的方法)
用于将局部修改应用到资源。


HTTP连接优化

现在网络建设使带宽极大的提升,影响HTTP性能主要是延迟。

浏览器阻塞(HOL blocking):浏览器对于同一个域名,同时只能有 4 个连接(根据浏览器内核不同可能有所差异),超过浏览器最大连接数限制,后续请求就会被阻塞。

DNS 查询(DNS Lookup):浏览器需要知道目标服务器的 IP 才能建立连接。将域名解析为 IP 的这个系统就是 DNS,这个通常可以利用DNS缓存结果来达到减少这个时间的目的。

建立连接(Initial connection):HTTP 基于 TCP 协议,浏览器最快也要经过三次握手建立TCP连接才能捎带 HTTP 请求报文,但是这些连接无法复用会导致每次请求都经历三次握手和慢启动。三次握手在高延迟的场景下影响较明显,慢启动则对文件类大请求影响较大。

https通信过程

1、什么是https

HTTPS(全称:Hypertext Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安 全基础是SSL,https所用的端口号是443

见下图:


2、https的实现原理

有两种基本的加解密算法类型:

1)对称加密:密钥只有一个,加密解密为同一个密码,且加解密速度 快,典型的对称加密算法有DES、AES等;

2)非对称加密:密钥成对出现(且根据公钥无法推知私钥,根据私钥也无法推知公钥),加密解密使用不同密钥(公钥加密需要私钥解密,私钥加密需要公钥解密),相对对称加密速度较慢,典型的非对称加密算法有RSA、DSA等。

下面看一下https的通信过程:


https通信的优点:
1)客户端产生的密钥只有客户端和服务器端能得到;
2)加密的数据只有客户端和服务器端才能得到明文;
3)客户端到服务端的通信是安全的。


http代理


1、http代理服务器

代理服务器英文全称是Proxy Server,其功能就是代理网络用户去取得网络信息。形象的说:它是网络信息的中转站。

代理服务器是介于浏览器和Web服务器之间的一台服务器,有了它之后,浏览器不是直接到Web服务器去取回网页而是向代理服务器发出请求,Request信号会先送到代理服务器,由代理服务器来取回浏览器所需要的信息并传送给你的浏览器。大部分代理服务器都具有缓冲的功能,就好象一个大的Cache,它有很大的存储空间,它不断将新取得数据储存到它本机的存储器上,如果浏览器所请求的数据在它本机 的存储器上已经存在而且是最新的,那么它就不重新从Web服务器取数据, 而直接将存储器上的数据传送给用户的浏览器,这样就能显著提高浏览速度和效率。

更重要的是:Proxy Server(代理服务器)是Internet链路级网关所提供的一种重要的安全功能,它的工作主要在开放系统互联(OSI)模型的对话层。

2、http代理服务器的主要功能

主要功能如下:
1)突破自身IP访问 限制,访问国外站点。如:教育网、169网等网络用户可以通过代理访问国 外网站;

2)访问一些单位或团体内部资源,如某大学FTP(前提是该代理地址在该资源的允许访问范围之内),使用教育网内地址段免费代理服务器,就可以用于对教育 网开放的各类FTP下 载上传,以及各类资料查询共享等服务;

3)突破中国电信的IP封 锁:中国电信用户有很多网站是被限制访问的,这种限制是人为的,不同Serve对地址的封锁是不同的。所以不能访问时可以换一个国 外的代理 服务器试试;

4)提高访问速度:通常代理服务器都设置一个较大的硬盘缓冲区,当有外界的信息通过时,同时也将其保存 到缓冲区中,当其他用户再访问相同的信息时, 则直接由缓冲区中取出信 息,传给用户,以提高访问速度;

5)隐藏真实IP:上 网者也可以通过这种方法隐藏自己的IP,免受攻击。

3、http代理图示

http代理的图示见下图:


对于客户端浏览器而言,http代理服务器相当于服务器。而对于Web服务器 而言,http代理服务器又担当了客户端的角色。


HTTP 协议的演变和设计思路


HTTP 协议是互联网的基础协议,也是网页开发的必备知识,最新版本 HTTP/2 更是让它成为技术热点。

一、HTTP/0.9

HTTP 是基于 TCP/IP 协议的应用层协议。它不涉及数据包(packet)传输,主要规定了客户端和服务器之间的通信格式,默认使用80端口。最早版本是1991年发布的0.9版。该版本极其简单,只有一个命令GET。
GET /index.html

上面命令表示,TCP 连接(connection)建立后,客户端向服务器请求(request)网页index.html。

协议规定,服务器只能回应HTML格式的字符串,不能回应别的格式。    
<html>
  <body>Hello World</body>
</html>

服务器发送完毕,就关闭TCP连接。

二、HTTP/1.0

2.1 简介

1996年5月,HTTP/1.0 版本发布,内容大大增加。

首先,任何格式的内容都可以发送。这使得互联网不仅可以传输文字,还能传输图像、视频、二进制文件。这为互联网的大发展奠定了基础。

其次,除了GET命令,还引入了POST命令和HEAD命令,丰富了浏览器与服务器的互动手段。

再次,HTTP请求和回应的格式也变了。除了数据部分,每次通信都必须包括头信息(HTTP header),用来描述一些元数据。

其他的新增功能还包括状态码(status code)、多字符集支持、多部分发送(multi-part type)、权限(authorization)、缓存(cache)、内容编码(content encoding)等。

2.2 请求格式

下面是一个1.0版的HTTP请求的例子。
GET / HTTP/1.0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5)
Accept: */*

可以看到,这个格式与0.9版有很大变化。

第一行是请求命令,必须在尾部添加协议版本(HTTP/1.0)。后面就是多行头信息,描述客户端的情况。

2.3 回应格式

服务器的回应如下:
HTTP/1.0 200 OK
Content-Type: text/plain
Content-Length: 137582
Expires: Thu, 05 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 5 August 1996 15:55:28 GMT
Server: Apache 0.84
 
<html>
  <body>Hello FreeOA</body>
</html>

回应的格式是"头信息 + 一个空行(\r\n) + 数据"。其中,第一行是"协议版本 + 状态码(status code) + 状态描述"。

2.4 Content-Type 字段

关于字符的编码,1.0版规定,头信息必须是 ASCII 码,后面的数据可以是任何格式。因此,服务器回应的时候,必须告诉客户端,数据是什么格式,这就是Content-Type字段的作用。下面是一些常见的Content-Type字段的值。
text/plain
text/html
text/css
image/jpeg
image/png
image/svg+xml
audio/mp4
video/mp4
application/javascript
application/pdf
application/zip
application/atom+xml

这些数据类型总称为MIME type,每个值包括一级类型和二级类型,之间用斜杠分隔。

除了预定义的类型,厂商也可以自定义类型。
application/vnd.debian.binary-package

上面的类型表明,发送的是Debian系统的二进制数据包。

MIME type还可以在尾部使用分号,添加参数。
Content-Type: text/html; charset=utf-8

上面的类型表明,发送的是网页,而且编码是UTF-8。客户端请求的时候,可以使用Accept字段声明自己可以接受哪些数据格式。
Accept: */*

上面代码中,客户端声明自己可以接受任何格式的数据。

MIME type不仅用在HTTP协议,还可以用在其他地方,比如HTML网页。
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<!-- 等同于 -->
<meta charset="utf-8" />

2.5 Content-Encoding 字段

由于发送的数据可以是任何格式,因此可以把数据压缩后再发送。Content-Encoding字段说明数据的压缩方法。
Content-Encoding: gzip
Content-Encoding: compress
Content-Encoding: deflate

客户端在请求时,用Accept-Encoding字段说明自己可以接受哪些压缩方法。
Accept-Encoding: gzip, deflate

2.6 缺点

HTTP/1.0 版的主要缺点是,每个TCP连接只能发送一个请求。发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接。

TCP连接的新建成本很高,因为需要客户端和服务器三次握手,并且开始时发送速率较慢(slow start)。所以,HTTP 1.0版本的性能比较差。随着网页加载的外部资源越来越多,这个问题就愈发突出了。

为了解决这个问题,有些浏览器在请求时,用了一个非标准的Connection字段。
Connection: keep-alive

这个字段要求服务器不要关闭TCP连接,以便其他请求复用。服务器同样回应这个字段。
Connection: keep-alive

一个可以复用的TCP连接就建立了,直到客户端或服务器主动关闭连接。但是,这不是标准字段,不同实现的行为可能不一致,因此不是根本的解决办法。

三、HTTP/1.1

1997年1月,HTTP/1.1 版本发布,只比 1.0 版本晚了半年。它进一步完善了 HTTP 协议,一直用到了20年后的今天,直到现在还是最流行的版本。

3.1 持久连接

1.1 版的最大变化,就是引入了持久连接(persistent connection),即TCP连接默认不关闭,可以被多个请求复用,不用声明Connection: keep-alive。

客户端和服务器发现对方一段时间没有活动,就可以主动关闭连接。不过,规范的做法是,客户端在最后一个请求时,发送Connection: close,明确要求服务器关闭TCP连接。
Connection: close

目前,对于同一个域名,大多数浏览器允许同时建立6个持久连接。

3.2 管道机制

1.1 版还引入了管道机制(pipelining),即在同一个TCP连接里面,客户端可以同时发送多个请求。这样就进一步改进了HTTP协议的效率。

举例来说,客户端需要请求两个资源。以前的做法是,在同一个TCP连接里面,先发送A请求,然后等待服务器做出回应,收到后再发出B请求。管道机制则是允许浏览器同时发出A请求和B请求,但是服务器还是按照顺序,先回应A请求,完成后再回应B请求。

3.3 Content-Length 字段

一个TCP连接现在可以传送多个回应,势必就要有一种机制,区分数据包是属于哪一个回应的。这就是Content-length字段的作用,声明本次回应的数据长度。
Content-Length: 3495

上面代码告诉浏览器,本次回应的长度是3495个字节,后面的字节就属于下一个回应了。在1.0版中,Content-Length字段不是必需的,因为浏览器发现服务器关闭了TCP连接,就表明收到的数据包已经全了。

3.4 分块传输编码

使用Content-Length字段的前提条件是,服务器发送回应之前,必须知道回应的数据长度。

对于一些很耗时的动态操作来说,这意味着,服务器要等到所有操作完成,才能发送数据,显然这样的效率不高。更好的处理方法是,产生一块数据,就发送一块,采用"流模式"(stream)取代"缓存模式"(buffer)。

因此,1.1版规定可以不使用Content-Length字段,而使用"分块传输编码"(chunked transfer encoding)。只要请求或回应的头信息有Transfer-Encoding字段,就表明回应将由数量未定的数据块组成。
Transfer-Encoding: chunked

每个非空的数据块之前,会有一个16进制的数值,表示这个块的长度。最后是一个大小为0的块,就表示本次回应的数据发送完了。下面是一个例子。
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
25
This is the data in the first chunk
1C
and this is the second one
3
con
8
sequence
0

3.5 其他功能

1.1版还新增了许多动词方法:PUT、PATCH、HEAD、 OPTIONS、DELETE。

另外,客户端请求的头信息新增了Host字段,用来指定服务器的域名。
Host: www.freeoa.net

有了Host字段,就可以将请求发往同一台服务器上的不同网站,为虚拟主机的兴起打下了基础。

3.6 缺点

虽然1.1版允许复用TCP连接,但是同一个TCP连接里面,所有的数据通信是按次序进行的。服务器只有处理完一个回应,才会进行下一个回应。要是前面的回应特别慢,后面就会有许多请求排队等着。这称为"队头堵塞"(Head-of-line blocking)。

为了避免这个问题,只有两种方法:一是减少请求数,二是同时多开持久连接。这导致了很多的网页优化技巧,比如合并脚本和样式表、将图片嵌入CSS代码、域名分片(domain sharding)等等。如果HTTP协议设计得更好一些,这些额外的工作是可以避免的。

HTTP/1.1相较于HTTP/1.0协议的区别主要体现在:
缓存处理
带宽优化及网络连接的使用
错误通知的管理
消息在网络中的发送
互联网地址的维护
安全性及完整性


3.7 区别

HTTP1.0最早在网页中使用是在1996年,那个时候只是使用一些较为简单的网页上和网络请求上,而HTTP1.1则在1999年才开始广泛应用于现在的各大浏览器网络请求中,同时HTTP1.1也是当前使用最为广泛的HTTP协议。 主要区别主要体现在:

缓存处理,在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。

带宽优化及网络连接的使用,HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。

错误通知的管理,在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。

Host头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。

长连接,HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。

HTTP1.0和1.1现存的一些问题

上面提到过的,HTTP1.x在传输数据时,每次都需要重新建立连接,无疑增加了大量的延迟时间,特别是在移动端更为突出。

HTTP1.x在传输数据时,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份,这在一定程度上无法保证数据的安全性。

HTTP1.x在使用时,header里携带的内容过大,在一定程度上增加了传输的成本,并且每次请求header基本不怎么变化,尤其在移动端增加用户流量。

虽然HTTP1.x支持了keep-alive,来弥补多次创建连接产生的延迟,但是keep-alive使用多了同样会给服务端带来大量的性能压力,并且对于单个文件被不断请求的服务(例如图片存放网站),keep-alive可能会极大的影响性能,因为它在文件被请求之后还保持了不必要的连接很长时间。



四、SPDY 协议

2009年,谷歌公开了自行研发的 SPDY 协议,主要解决 HTTP/1.1 效率不高的问题。这个协议在Chrome浏览器上证明可行以后,就被当作 HTTP/2 的基础,主要特性都在 HTTP/2 之中得到继承。
SPDY可以说是综合了HTTPS和HTTP两者有点于一体的传输协议,主要解决:

降低延迟,针对HTTP高延迟的问题,SPDY优雅的采取了多路复用(multiplexing)。多路复用通过多个请求stream共享一个tcp连接的方式,解决了HOL blocking的问题,降低了延迟同时提高了带宽的利用率。

请求优先级(request prioritization)。多路复用带来一个新的问题是,在连接共享的基础之上有可能会导致关键请求被阻塞。SPDY允许给每个request设置优先级,这样重要的请求就会优先得到响应。比如浏览器加载首页,首页的html内容应该优先展示,之后才是各种静态资源文件,脚本文件等加载,这样可以保证用户能第一时间看到网页内容。

header压缩。前面提到HTTP1.x的header很多时候都是重复多余的。选择合适的压缩算法可以减小包的大小和数量。

基于HTTPS的加密协议传输,大大提高了传输数据的可靠性。
服务端推送(server push),采用了SPDY的网页,例如我的网页有一个sytle.css的请求,在客户端收到sytle.css数据的同时,服务端会将sytle.js的文件推送给客户端,当客户端再次尝试获取sytle.js时就可以直接从缓存中获取到,不用再发请求了。SPDY构成图:


SPDY位于HTTP之下,TCP和SSL之上,这样可以轻松兼容老版本的HTTP协议(将HTTP1.x的内容封装成一种新的frame格式),同时可以使用已有的SSL功能。


五、HTTP/2

2015年,HTTP/2 发布。它不叫 HTTP/2.0,是因为标准委员会不打算再发布子版本了,下一个新版本将是 HTTP/3。

5.1 二进制协议

HTTP/1.1 版的头信息肯定是文本(ASCII编码),数据体可以是文本,也可以是二进制。HTTP/2 则是一个彻底的二进制协议,头信息和数据体都是二进制,并且统称为"帧"(frame):头信息帧和数据帧。

二进制协议的一个好处是,可以定义额外的帧。HTTP/2 定义了近十种帧,为将来的高级应用打好了基础。如果使用文本实现这种功能,解析数据将会变得非常麻烦,二进制解析则方便得多。

5.2 多工

HTTP/2 复用TCP连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应,这样就避免了"队头堵塞"。

举例来说,在一个TCP连接里面,服务器同时收到了A请求和B请求,于是先回应A请求,结果发现处理过程非常耗时,于是就发送A请求已经处理好的部分, 接着回应B请求,完成后,再发送A请求剩下的部分。

这样双向的、实时的通信,就叫做多工(Multiplexing)。

5.3 数据流

因为 HTTP/2 的数据包是不按顺序发送的,同一个连接里面连续的数据包,可能属于不同的回应。因此,必须要对数据包做标记,指出它属于哪个回应。

HTTP/2 将每个请求或回应的所有数据包,称为一个数据流(stream)。每个数据流都有一个独一无二的编号。数据包发送的时候,都必须标记数据流ID,用来区分它属于哪个数据流。另外还规定,客户端发出的数据流,ID一律为奇数,服务器发出的,ID为偶数。

数据流发送到一半的时候,客户端和服务器都可以发送信号(RST_STREAM帧),取消这个数据流。1.1版取消数据流的唯一方法,就是关闭TCP连接。这就是说,HTTP/2 可以取消某一次请求,同时保证TCP连接还打开着,可以被其他请求使用。客户端还可以指定数据流的优先级。优先级越高,服务器就会越早回应。

5.4 头信息压缩

HTTP 协议不带有状态,每次请求都必须附上所有信息。所以,请求的很多字段都是重复的,比如Cookie和User Agent,一模一样的内容,每次请求都必须附带,这会浪费很多带宽,也影响速度。

HTTP/2 对这一点做了优化,引入了头信息压缩机制(header compression)。一方面,头信息使用gzip或compress压缩后再发送;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。

5.5 服务器推送

HTTP/2 允许服务器未经请求,主动向客户端发送资源,这叫做服务器推送(server push)。

常见场景是客户端请求一个网页,这个网页里面包含很多静态资源。正常情况下,客户端必须收到网页后,解析HTML源码,发现有静态资源,再发出静态资源请求。其实,服务器可以预期到客户端请求网页后,很可能会再请求静态资源,所以就主动把这些静态资源随着网页一起发给客户端了。

5.6 综述

顾名思义有了HTTP1.x,那么HTTP2.0也就顺理成章的出现了。HTTP2.0可以说是SPDY的升级版(其实原本也是基于SPDY设计的),但是,HTTP2.0 跟 SPDY 仍有不同的地方,主要是以下两点:
HTTP2.0 支持明文 HTTP 传输,而 SPDY 强制使用 HTTPS
HTTP2.0 消息头的压缩算法采用 HPACK,而非 SPDY 采用的 DEFLATE

 
HTTP2.0的新特性

 新的二进制格式(Binary Format),HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。

多路复用(MultiPlexing),即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。多路复用原理图:


header压缩,如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。

服务端推送(server push),同SPDY一样,HTTP2.0也具有server push功能。目前,有大多数网站已经启用HTTP2.0。


关于HTTP2和HTTP1.x的区别大致可以看下图:


HTTP请求与响应常见表

1.HTTP请求--方法

GET

请求获取由Request-URI所表示的资源

POST

在Request-URI所标识的资源后附加新的数据

HEAD

请求获取由Request-URI所标识的资源的响应消息报头

PUT

请求服务器存储一个资源,并用Request-URI作为其标识

DELETE

请求服务器删除由Request-URI所标识的资源

TRACE

请求服务器回送收到的请求信息,主要用于测试或诊断

2.HTTP请求--常见响应码

状态码

描述

客户端请求成功

200

OK

客户端请求成功

206

Partial content

返回请求实体的部分内容

301

Moved Permanently

目标永久移动

302

found

找到目标目标临时被移动

304

Not Modified

目标没有修改

400

Bad Request

客户端请求有语法错误,不能被服务器所理解

403

Forbidden

服务器收到请求,但是拒绝提供服务

404

Not Found

请求资源不存在

413

Request Entity Too Large

请求实体太大

500

Internal Server Error

服务器发生不可预测的错误,导致无法完成客户端的请求

502

Bad Gateway

网关错误,有可能是网络不通或负载过大导致。

504

Gateway Timeout

连接超时



参考来源

HTTP,HTTP2.0,SPDY,HTTPS你应该知道的一些事