HTTP协议详解
保留所有版权,请引用而不是转载本文(原文地址 https://yeecode.top/blog/25/ )。
1 简介
HTTP是Hyper Text Transfer Protocol(超文本传输协议)的缩写。HTTP协议用于从WWW服务器传输超文本到本地浏览器的传送协议。它可以使浏览器更加高效,使网络传输减少。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容首先显示(如文本先于图形)等。HTTP是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型。HTTP是一个无状态的协议。
1.1 工作流程
一次HTTP操作称为一个事务,其工作过程可分为四步:
- 首先客户机与服务器需要建立连接。只要单击某个超级链接,HTTP的工作开始。
- 建立连接后,客户机发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是MIME信息包括请求修饰符、客户机信息和可能的内容。
- 服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息包括服务器信息、实体信息和可能的内容。
- 客户端接收服务器所返回的信息通过浏览器显示在用户的显示屏上,然后客户机与服务器断开连接。 如果在以上过程中的某一步出现错误,那么产生错误的信息将返回到客户端,有显示屏输出。对于用户来说,这些过程是由HTTP自己完成的,用户只要用鼠标点击,等待信息显示就可以了。
1.2 HTTP协议特点
- 支持客户/服务器模式。支持基本认证和安全认证。
- 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
- 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。 HTTP 0.9和1.0使用非持续连接:限制每次连接只处理一个请求,服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。HTTP 1.1使用持续连接:不必为每个web对象创建一个新的连接,一个连接可以传送多个对象。
- 无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。
2 HTTP1.1的新机制
HTTP/1.1 版本发布,只比 1.0 版本晚了半年。它进一步完善了 HTTP 协议,一直用到了20年后的今天,直到现在还是最流行的版本。
2.1 持久连接
1.1 版的最大变化,就是引入了持久连接(persistent connection),即TCP连接默认不关闭,可以被多个请求复用,不用声明Connection: keep-alive。客户端和服务器发现对方一段时间没有活动,就可以主动关闭连接。不过,规范的做法是,客户端在最后一个请求时,发送Connection: close
,明确要求服务器关闭TCP连接。
2.2 管道机制
1.1 版还引入了管道机制(pipelining),即在同一个TCP连接里面,客户端可以同时发送多个请求。这样就进一步改进了HTTP协议的效率。
举例来说,客户端需要请求两个资源。以前的做法是,在同一个TCP连接里面,先发送A请求,然后等待服务器做出回应,收到后再发出B请求。管道机制则是允许浏览器同时发出A请求和B请求,但是服务器还是按照顺序,先回应A请求,完成后再回应B请求。
但是同一个TCP连接里面,所有的数据通信是按次序进行的。服务器只有处理完一个回应,才会进行下一个回应。要是前面的回应特别慢,后面就会有许多请求排队等着。这称为”队头堵塞”(Head-of-line blocking)。
为了避免这个问题,只有两种方法:一是减少请求数,二是同时多开持久连接。这导致了很多的网页优化技巧,比如合并脚本和样式表、将图片嵌入CSS代码、域名分片(domain sharding)等等。如果HTTP协议设计得更好一些,这些额外的工作是可以避免的。
2.3 Content-Length字段
一个TCP连接现在可以传送多个回应,势必就要有一种机制,区分数据包是属于哪一个回应的。这就是Content-length字段的作用,声明本次回应的数据长度。
Content-Length: 3295
上面代码告诉浏览器,本次回应的长度是3295个字节,后面的字节就属于下一个回应了。
在1.0版中,Content-Length字段不是必需的,因为浏览器发现服务器关闭了TCP连接,就表明收到的数据包已经全了。
使用Content-Length字段的前提条件是,服务器发送回应之前,必须知道回应的数据长度。对于一些很耗时的动态操作来说,这意味着,服务器要等到所有操作完成,才能发送数据,显然这样的效率不高。更好的处理方法是,产生一块数据,就发送一块,采用”流模式”(stream)取代”缓存模式”(buffer)。
因此,1.1版规定可以不使用Content-Length字段,而使用”分块传输编码”(chunked transfer encoding)。只要请求或回应的头信息有Transfer-Encoding字段,就表明回应将由数量未定的数据块组成。
Transfer-Encoding: chunked
每个非空的数据块之前,会有一个16进制的数值,表示这个块的长度。最后是一个大小为0的块,就表示本次回应的数据发送完了。
2.4 其他改进
- 1.1版还新增了许多动词方法:PUT、PATCH、HEAD、 OPTIONS、DELETE。
- 另外,客户端请求的头信息新增了Host字段,用来指定服务器的域名。有了Host字段,就可以将请求发往同一台服务器上的不同网站,为虚拟主机的兴起打下了基础。
接下来,我们详细介绍1.1版本的相关规范。
3 客户端请求
发出的请求具体格式如下:
示例:
发出的请求信息包括以下几个:
- HTTP请求行(request-line):用来说明请求类型,要访问的资源以及所使用的HTTP版本。例如
GET /images/logo.gif HTTP/1.1
,表示从/images 目录下请求logo.gif 这个文件。 GET方法传输内容,则放在请求行中。 - 首部(header):用来说明服务器要使用的附加信息。例如
Accept-Language: en
。请求头可以有多条,除Host外,都是可选的。 - 空行
- 可选的消息体(body): POST方法传递的信息,放在这里。
请求行和首部必须以
<CR><LF>
作为结尾(也就是,回车然后换行)。空行内必须只有<CR><LF>
而无其他空格。
3.1 请求行
例如:GET /pagead/js/r20160511/r20160513/expansion_embed.js HTTP/1.1
,
分别说明了请求方法、请求地址、HTTP协议版本号。
3.1.1 请求方法
- GET:用来请求指定的资源。它是目前网上最常用的方法。它不应该用于一些会造成副作用的操作中 。在网络应用中用它来提交动作是一种常见的错误用法。它所传输内容放在请求行里。
- HEAD: 类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头。
- POST:向指定的资源提交需要处理的数据。这些数据写在请求的消息体里。(POST请求)可以导致新资源的产生和已有资源的更新。
- PUT: 上传指定资源
- DELETE: 删除指定资源
- TRACE: 告诉服务器端返回收到的请求。客户端可以通过此方法察看在请求过程中中间服务器添加或者改变哪些内容。
- OPTIONS: 返回服务器(在指定URL上)支持的HTTP方法。通过请求“*”而不是指定的资源,这个方法可以用来检查网络服务器的功能。 回应方会在回应头的allow字段中返回支持的方法,如:allow :GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
- CONNECT: 将请求的连接转换成透明的TCP/IP通道,通常用来简化通过非加密的HTTP代理的SSL-加密通讯(HTTPS)。
Method名称是区分大小写的。当某个请求所针对的资源不支持对应的请求方法的时候,服务器应当返回状态码405(Method Not Allowed),当服务器不认识或者不支持对应的请求方法的时候,应当返回状态码501(Not Implemented)。
HTTP协议没有对传输的数据和URL长度进行限制,但特定浏览器和服务器对URL长度有限制, 因此对于GET提交时,传输数据就会受到URL长度的限制;由于POST操作不是通过URL传值,理论上数据长度不受限。
3.1.2 HTTP协议版本
- HTTP/1.0 这是第一个在通讯中指定版本号的 HTTP 协议版本,至今仍被广泛采用,特别是在代理服务器中。
- HTTP/1.1 当前版本。持久连接被默认采用,并能很好地配合代理服务器工作。还支持以管道方式在同时发送多个请求,以便降低线路负载,提高传输速度。 HTTP 1.1支持持久连接,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟。一个包含有许多图像的网页文件的多个请求和应答可以在一个连接中传输,但每个单独的网页文件的请求和应答仍然需要使用各自的连接。HTTP 1.1还允许客户端不用等待上一次请求结果返回,就可以发出下一次请求,但服务器端必须按照接收到客户端请求的先后顺序依次回送响应结果,以保证客户端能够区分出每次请求的响应内容,这样也显著地减少了整个下载过程所需要的时间。
3.1.3 URL
url的格式如下:
schema://host[:port#]/path/.../[;url-params][?query-string][#anchor]
有以下的组成部分:
- scheme:指定低层使用的协议,一般是http,如果强调安全的话可以是http
- host:HTTP服务器的IP地址或者域名
- port:HTTP服务器的默认端口是80,这种情况下端口号可以省略。如果使用了别的端口,必须指明
- path:访问资源的路径
- url-params:URL的参数
- query-string:发送给http服务器的数据
- anchor:锚
举例子:
3.2 请求首部
请求首部包含众多的可配置项:
- Accept: 指定客户端能够接收的内容类型 Accept: text/plain, text/html
- Accept-Charset: 浏览器可以接受的字符编码集。 Accept-Charset: iso-8859-5
- Accept-Encoding: 指定浏览器可以支持的web服务器返回内容压缩编码类型。 Accept-Encoding: compress, gzip
- Accept-Language: 浏览器可接受的语言 Accept-Language: en,zh
- Accept-Ranges: 可以请求网页实体的一个或者多个子范围字段 Accept-Ranges: bytes
- Authorization: HTTP授权的授权证书 Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
- Cache-Control: 指定请求和响应遵循的缓存机制 Cache-Control: no-cache
- Connection: 表示是否需要持久连接。(HTTP 1.1默认进行持久连接) Connection: close
- Cookie: HTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器。 Cookie: $Version=1; Skin=new;
- Content-Length: 请求的内容长度 Content-Length: 348
- Content-Type:请求的与实体对应的MIME信息 Content-Type: application/x-www-form-urlencoded 。还可以在尾部使用分号,添加参数,例如:Content-Type: text/html; charset=utf-8
- Date 请求发送的日期和时间 Date: Tue, 15 Nov 2010 08:12:31 GMT
- Expect请求的特定的服务器行为 Expect: 100-continue
- From 发出请求的用户的Email From: [email protected]
- Host 指定请求的服务器的域名和端口号 Host: www.zcmhi.com
- If-Match 只有请求内容与实体相匹配才有效 If-Match: “737060cd8c284d8af7ad3082f209582d”
- If-Modified-Since 如果请求的部分在指定时间之后被修改则请求成功,未被修改则返回304代码 If-Modified-Since: Sat, 29 Oct 2010 19:43:31 GMT
- If-None-Match 如果内容未改变返回304代码,参数为服务器先前发送的Etag,与服务器回应的Etag比较判断是否改变 If-None-Match: “737060cd8c284d8af7ad3082f209582d”
- If-Range 如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。参数也为Etag If-Range: “737060cd8c284d8af7ad3082f209582d”
- If-Unmodified-Since 只在实体在指定时间之后未被修改才请求成功 If-Unmodified-Since: Sat, 29 Oct 2010 19:43:31 GMT
- Max-Forwards 限制信息通过代理和网关传送的时间 Max-Forwards: 10
- Pragma 用来包含实现特定的指令 Pragma: no-cache
- Proxy-Authorization 连接到代理的授权证书 Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
- Range 只请求实体的一部分,指定范围 Range: bytes=500-999
- Referer 先前网页的地址,当前请求网页紧随其后,即来路
Referer: [http://www.zcmhi.com/archives/71.html](http://www.zcmhi.com/archives/71.html)
- TE 客户端愿意接受的传输编码,并通知服务器接受接受尾加头信息 TE: trailers,deflate;q=0.5
- Upgrade 向服务器指定某种传输协议以便服务器进行转换(如果支持) Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11
- User-Agent User-Agent的内容包含发出请求的用户信息 User-Agent: Mozilla/5.0 (Linux; X11)
- Via 通知中间网关或代理服务器地址,通信协议
Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)
- Warning 关于消息实体的警告信息 Warn: 199 Miscellaneous warning
4 服务器回应
与HTTP请求类似,HTTP响应也是由三个部分组成,分别是:状态行,回应首部,回应正文。
示例:
4.1 状态行
主要包括:HTTP版本号,状态码。
各个状态码的意义:
- 1xx 消息: 请求已被服务器接收,继续处理
- 2xx 成功: 请求已成功被服务器接收、理解、并接受
- 3xx 重定向: 需要后续操作才能完成这一请求
- 4xx 请求错误: 请求含有词法错误或者无法被执行
- 5xx 服务器错误: 服务器在处理某个正确请求时发生错误
一些常见的状态码:
- 400 Bad Request: 客户端请求有语法错误,不能被服务器所理解
- 401 Unauthorized: 请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用
- 403 Forbidden: 服务器收到请求,但是拒绝提供服务
- 404 Not Found: 请求资源不存在,eg:输入了错误的URL
- 500 Internal Server Error: 服务器发生不可预期的错误
- 503 Server Unavailable: 服务器当前不能处理客户端的请求,一段时间后可能恢复正常
4.2 回应首部
可选项也非常多:
- Accept-Ranges 表明服务器是否支持指定范围请求及哪种类型的分段请求 Accept-Ranges: bytes
- Age 从原始服务器到代理缓存形成的估算时间(以秒计,非负) Age: 12
- Allow 对某网络资源的有效的请求行为,不允许则返回405 Allow: GET, HEAD
- Cache-Control 告诉所有的缓存机制是否可以缓存及哪种类型 Cache-Control: no-cache
- Content-Encoding web服务器支持的返回内容压缩编码类型。 Content-Encoding: gzip
- Content-Language 响应体的语言 Content-Language: en,zh
- Content-Length 响应体的长度 Content-Length: 348
- Content-Location 请求资源可替代的备用的另一地址 Content-Location: /index.htm
- Content-MD5 返回资源的MD5校验值 Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ==
- Content-Range 在整个返回体中本部分的字节位置 Content-Range: bytes 21010-47021/47022
- Content-Type 返回内容的MIME类型 Content-Type: text/html; charset=utf-8
- Date 原始服务器消息发出的时间 Date: Tue, 15 Nov 2010 08:12:31 GMT
- ETag 请求变量的实体标签的当前值 ETag: “737060cd8c284d8af7ad3082f209582d”
- Expires 响应过期的日期和时间 Expires: Thu, 01 Dec 2010 16:00:00 GMT
- Last-Modified 请求资源的最后修改时间 Last-Modified: Tue, 15 Nov 2010 12:45:26 GMT
- Location 用来重定向接收方到非请求URL的位置来完成请求或标识新的资源
Location: [http://www.zcmhi.com/archives/94.html](http://www.zcmhi.com/archives/94.html)
- Pragma 包括实现特定的指令,它可应用到响应链上的任何接收方 Pragma: no-cache
- Proxy-Authenticate 它指出认证方案和可应用到代理的该URL上的参数 Proxy-Authenticate: Basic
- refresh 应用于重定向或一个新的资源被创造,在5秒之后重定向(由网景提出,被大部分浏览器支持)
Refresh: 5; url=http://www.zcmhi.com/archives/94.html
- Retry-After 如果实体暂时不可取,通知客户端在指定时间之后再次尝试 Retry-After: 120
- Server web服务器软件名称
Server: Apache/1.3.27 (Unix) (Red-Hat/Linux)
- Set-Cookie 设置Http Cookie
Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1
- Trailer 指出头域在分块传输编码的尾部存在
Trailer: Max-Forwards
- Transfer-Encoding 文件传输编码 Transfer-Encoding:chunked
- Vary 告诉下游代理是使用缓存响应还是从原始服务器请求 Vary: *
- Via 告知代理客户端响应是通过哪里发送的
Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)
- Warning 警告实体可能存在的问题
Warning: 199 Miscellaneous warning
- WWW-Authenticate 表明客户端请求实体应该使用的授权方案
WWW-Authenticate: Basic
4.3 回应正文
即这次服务器返回的内容信息,可能是压缩的。相关压缩协议等均在回应首部进行了说明。
5 HTTPS
HTTP在安全方面存在以下的缺点:
- 通信使用明文不对数据进行加密(内容容易被窃听)
- 不验证通信方身份(容易伪装)
- 无法确定报文完整性(内容易被篡改)
HTTPS比HTTP多了TLS/SSL
层,即HTTP是直接和TCP打交道。而加入新的一层后,HTTP先和TLS/SSL层打交道,然后TLS/SSL层在和TCP层打交道。这样就实现了加密。
5.1 TLS与SSL
TLS(Transport Layer Security 传输层安全)/SSL (Secure Sockets Layer安全套接层), 是介于传输层TCP和应用层HTTP之间的一层安全协议,TLS/SSL 具有身份验证、信息加密和完整性校验的功能,目的是保障客户端到服务端之间连接的安全性。
SSL(Secure Socket Layer,安全套接字层):1994年为 Netscape 所研发,SSL 协议位于 TCP/IP 协议与各种应用层协议之间,为数据通讯提供安全支持。
TLS(Transport Layer Security,传输层安全):其前身是 SSL,它最初的几个版本(SSL 1.0、SSL 2.0、SSL 3.0)由网景公司开发,1999年从 3.1 开始被 IETF 标准化并改名,发展至今已经有 TLS 1.0、TLS 1.1、TLS 1.2 三个版本。SSL3.0和TLS1.0由于存在安全漏洞,已经很少被使用到。TLS 1.3 改动会比较大,目前还在草案阶段,目前使用最广泛的是TLS 1.1、TLS 1.2。
因此两者功能上是一样的。
TLS/SSL发展历史:
- 1994年,NetScape公司设计了SSL协议(Secure Sockets Layer)的1.0版,但是未发布。
- 1995年,NetScape公司发布SSL 2.0版,很快发现有严重漏洞。
- 1996年,SSL 3.0版问世,得到大规模应用。
- 1999年,互联网标准化组织ISOC接替NetScape公司,发布了SSL的升级版TLS 1.0版。
- 2006年和2008年,TLS进行了两次升级,分别为TLS 1.1版和TLS 1.2版。最新的变动是2011年TLS 1.2的修订版。
5.2 证书产生
持有EV SSL证书的web网站浏览器地址栏处的背景是绿色的.点击后可以查看详情
5.3 工作过程
SSL/TSL握手的作用是身份验证和协商密钥。密钥交换是为了在不安全数据通道中产生一个只有通信双方知道的对称加密 Session Key。身份认证的目的是确保连接到拥有网站私钥的合法服务器。
TLS/SSL握手主要过程如下:
- ClientHello:在握手流程中,ClientHello是第一个请求,将客户端的参数传送给服务器。主要请求参数包含:
- 客户端支持的SSL/TLS协议版本列表,比如TLS 1.2;
- 客户端支持的加密套件(Support Ciphers)列表;
- 客户端生成的随机数(Client random),稍后用于生成对称加密密钥。
- SeverHello:服务端收到ClientHello请求后,确认双方加密套件、压缩方法等会话参数,并返回给客户端。SeverHello消息的结构与ClientHello类似,只是每个字段只包含一个选项。服务器的加密组件内容以及压缩方法等都是从接收到的客户端加密组件内筛选出来的。
- Certificate:服务器返回HTTPS数字证书报文。
- ServerKeyExchange:ServerKeyExchange包含了服务端为密钥交换的提供的数据。对于不同的协商算法套件会存在差异。某些加密套件不需要服务器返回发送ServerKeyExchange消息。
- ServerHelloDone:ServerHelloDone消息表明服务器已经将所有预计的握手消息发送完毕。在此之后,服务器会等待客户端发送消息。
- ClientKeyExchange:ClientKeyExchange包含客户端为密钥交换提供的数据。这个消息受协商的密码套件的影响,内容随着不同的协商密码套件而不同。
- ChangeCipherSpec:ChangeCipherSpec消息表明发送端已取得用以生成连接参数的足够信息,已生成加密密钥,并且将切换到加密模式。客户端和服务器在条件成熟时都会发送这个消息。注意:ChangeCipherSpec不属于握手消息,它是另一种协议,只有一条消息,作为它的子协议进行实现。
- Finished:Finished消息意味着握手已经完成。接下来,客户端与服务器进入加密通信,就完全是使用普通的HTTP协议,只不过用”会话密钥”加密内容。
因为HTTP本身存在的安全性问题,目前已不推荐使用。谷歌发布的Chrome浏览器从 68版本开始将非HTTPS网站被显著标记为“不安全(Not Secure)”。因此,及时将网站升级为HTTPS十分必要。
可以访问个人知乎阅读更多文章:易哥(https://www.zhihu.com/people/yeecode),欢迎关注。