1. HTTP常见方法
- GET: 通常用于请求服务器发送某些资源
- HEAD: 请求资源的头部信息,并且这些头部与 HTTP GET 方法请求时返回的一致。该请求方法的一个使用场景是在下载一个大文件前先获取其大小再决定是否要下载,以此可以节约带宽资源。
- OPTIONS: 用于获取目的资源所支持的通信选项,它返回的response如下:
HTTP/1.1 200 OK
Allow: OPTIONS, GET, HEAD, POST
Cache-Control: max-age=604800
Date: Thu, 13 Oct 2016 11:45:00 GMT
Expires: Thu, 20 Oct 2016 11:45:00 GMT
Server: EOS (lax004/2813)
x-ec-custom-error: 1
Content-Length: 0
- POST: 发送数据给服务器
- PUT: 用于新增资源或者使用请求中的有效payload替换目标资源的表现形式
- DELETE: 用于删除指定的资源
- PATCH: 用于对资源进行部分修改
- CONNECT: HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
- TRACE: 回显服务器收到的请求,主要用于测试或诊断
PUT和POST方法的区别在于,PUT方法是幂等的,即连续调用一次或者多次的效果相同(无副作用),而POST方法是非幂等的。
CONNECT方法用于创建代理服务器到目标服务器之间的TCP连接。我们可以在浏览器中配置一个代理服务器,之后浏览器在发送所有的HTTP请求前,会先通过CONNECT方法建立起代理服务器到目标Server间的TCP通道,之后浏览器所有的HTTP请求都发送给这台代理服务器,代理服务器收到请求后转发给目标Server并将收到的响应原封不动的返回给浏览器。因此,只有当浏览器配置为使用代理服务器时才会用到CONNECT方法,平时我们进行网页开发时是用不到的。当我们想要访问的资源客户端无法直接访问只能通过代理服务器访问时,此时我们就可以使用HTTP代理技术,例如VPN就是一种常见的HTTP代理技术。
PATCH和PUT的区别:使用PUT方法更新一个资源时,我们必须将整个资源传递给Server,然后Server会将旧对象完全覆盖。而是用PATCH方法可以直接传递变化了的资源属性,达到传输数据更少的目的。
2. HTTP协议报文
2.1 请求报文
HTTP协议的请求报文分为4部分:
- 请求行:由HTTP方法,请求URL和使用的HTTP协议版本组成,例如
GET /books HTTP/1.1
。 - 请求头部:由一个个键值对组成。
- 空行:由回车符和换行符组成。
- 请求实体:客户端发送的数据。
例如一个GET请求的报文如下:
GET /books/?sex=man&name=Professional HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)
Gecko/20050225 Firefox/1.0.1
Connection: Keep-Alive
- 第1行对应着请求行:
method
为GET
,request-URL
为/books/?sex=man&name=Professional
,HTTP version
为HTTP/1.1
。 - 第2行~第5行对应着请求头部,头部由一个个键值对组成。例如
HOST
表示请求的目标主机,User-Agent
显示客户端使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。Connection: Keep-Alive
决定当前的事务完成后,是否会关闭网络连接。如果该值是keep-alive
,网络连接就是持久的,不会关闭,使得对同一个服务器的请求可以继续在该连接上完成。如果是close
,则表示当前事务完成后,服务器应该关闭这个HTTP连接。另外, - 这个GET请求的请求体为空。
在头部字段中设置
Connection:keep-alive
和Keep-Alive: timeout=60
,表明连接建立之后,空闲时间超过60秒就会失效。
而一个POST请求的报文如下:
POST / HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)
Gecko/20050225 Firefox/1.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 40
Connection: Keep-Alive
sex=man&name=Professional
POST请求包含了一个请求实体,为sex=man&name=Professional
。头部字段Content-Type: application/x-www-form-urlencoded
表示请求实体中包含的是form类型的信息,其他可选值包括application/json
(表示json数据),multipart/form-data
(表示上传文件)等等。
2.2 响应报文
响应报文也由4部分组成:
- 响应行:由协议版本,状态码和状态码的原因短语组成,例如
HTTP/1.1 200 OK
。 - 响应头:由一个个键值对组成。
- 空行:由回车符和换行符组成。
- 响应体:服务器响应的数据。
下面是一个HTTP响应的例子:
HTTP/1.1 200 OK
Server:Apache Tomcat/5.0.12
Date:Mon,6Oct2003 13:23:42 GMT
Content-Length:112
<html>...
3. 常见首部
3.1 请求首部
下面是客户端向服务器发送请求报文时一些常见的首部:
Accept
Accept表示客户端或者代理能够处理的媒体类型。客户端期望的资源类型服务器可能没有,所以客户端会期望多种类型,并且设置优先级,服务器根据优先级寻找相应的资源返回给客户端。例如:
# 注意:逗号分割类型,分号分割属性
Accept: audio/*; q=0.2, audio/basic
其中q的取值范围是(0-1],其具体值并没有意义,它仅用来排序优先级,如果没有q,默认q=1,也就是最高优先级。上面表示客户端希望服务器优先返回audio/basic类型的资源,如果没有,就随便其它什么格式的audio资源都可以。
类似的,
Accept-Encoding
,Accept-Language
和Accept-Charset
分别表示优先可处理的编码格式,自然语言和字符集。
If-Match/If-None-Match
If-Match/If-None-Match用于和响应首部中的ETag进行比较。当且仅当Server对于If-Match/If-None-Match和Etag的比较符合预期,Server才会处理这个HTTP请求并返回对应的响应。
ETAG是一个响应首部,HTTP仅仅规定它是一个以引号开头结尾的值,并没有规定它的具体语义。
客户端常常使用If-Match
用于HTTP乐观锁。所谓HTTP乐观锁,是指客户端先GET这个资源得到ETag的值(此时这个ETAG的语义就是版本号),然后发起一个PUT|PATCH请求时将If-Match
的值设置为ETAG的值,如果服务器资源的最新ETAG满足If-Match
设置的值,请求就会被执行。如果不满足,说明资源被并发修改了,就需要返回412 Precondition failed 的错误。客户端可以选择放弃或者重试整个过程。
而If-None-Match
可以用于缓存控制。它的原理是这样的,当浏览器请求服务器的某项资源(A)时, 服务器根据A算出一个哈希值(3f80f-1b6-3e1cb03b)并通过 ETag 返回给浏览器,浏览器把"3f80f-1b6-3e1cb03b" 和 A 同时缓存在本地,当下次再次向服务器请求A时,会通过类似If-None-Match: "3f80f-1b6-3e1cb03b"
的请求头把ETag发送给服务器,服务器再次计算A的哈希值(即计算最新的Etag值)并和浏览器发送的If-None-Match
值做比较,如果发现相同直接浏览器返回一个304 Not Modified响应,如果发现不相等则触发了If-None-Match
语义,服务器就把A返回给浏览器(即状态码为200)。这样通过控制浏览器端的缓存,可以节省服务器的带宽,因为服务器不需要每次都把全量数据返回给客户端。
我们也可以通过
If-Modified-Since
请求首部来控制缓存。服务器响应首部中包含一个Last-Modified
值,每一次客户端发送HTTP请求时,将上一次收到的Last-Modified
值作为If-Modified-Since
的值发送给服务器,服务器根据请求中If-Modified-Since
的值来返回最新的资源或304 Not Modified。Last-Modified
的问题在于它的精度为秒级别,不适合适合更新频繁的敏感资源。
Range
Range表示客户端告知服务器返回文件的哪一部分。支持断点续传的服务器必须处理Range头。
例如:
Range: bytes=200-1000, 2000-6576, 19000-
If-Range
在断点续传时,为确保连续2个请求之间服务器资源本身没有发生变化,需要If-Range头带上ETag的资源版本号。服务器资源根据这个版本号来判定资源是否改变了。如果没变,就返回206 Partial Content将部分资源返回。如果资源变了,那就相当于一个普通的GET请求,返回200 OK和整个资源内容。
Authorization
对于某些需要特殊权限才能访问的资源,客户端在请求里通过Authorization
首部提供认证信息。
例如:
# value = base64(user_name:password)
Authorization: Basic YWRtaW46YWRtaW4xMjM=
Host
Host表示请求资源所在服务器。
User-Agent
User-Agent携带当前的客户端信息,一般包含浏览器、浏览器内核和操作系统的版本型号信息。
3.2 响应首部
Content-Type
Content-Type代表响应内容的媒体类型和编码格式,是对请求报文中Accept头和Accept-Charset头的统一应答。*
Content-Type: text/html; charset=utf8
Etag
ETag一般携带的是资源的版本号。协议没有具体规定版本号是什么。它可以是资源的md5校验码,也可以是uuid,甚至可以是自增的数字,也可以是资源的修改时间。它的匹配方式是相等/不相等(即If-Match和If-None-Match)。因为服务器需要维护版本号,取决的版本号是什么,这可能是一个存储和计算的负担。
Last-Modified携带的资源的修改时间。它的匹配方式是大于/小于(If-Modified-Since和If-Unmodified-Since)。如果是静态资源文件,一般就是操作系统记录的文件修改时间。
Expires是服务器告知客户端资源的过期时间。客户端缓存的资源在这个时间之后自动过期,而不需要非得向服务器确认一下是不是304 Not Modified才认为没过期。
Location
服务器向客户端发送302跳转的时候,总会携带Location头信息,它的值为目标URL。
例如:
HTTP/1.1 302 Temporary Redirect
Location: https://www-temp.example.org/
WWW-Authenticate
WWW-Authenticate是401 Unauthorized错误码返回时必须携带的头,该头会携带一个问题Challenge给客户端,告知客户端需要携带这个问题的答案来请求服务器才可以继续访问目标资源。这种问题Challenge可以自定义,比较常见的是Basic认证。
例如:
WWW-Authenticate: Basic realm=xxx
Basic指代base64加密算法(不安全),realm指代认证范围/场合/情景名称。
Server
用于返回服务器相关的软件信息,来告知客户端当前的HTTP服务是由某某软件提供的,可以看成是一种软件广告。 RFC协议里对这个头信息做了警告:暴露出服务器信息可能会导致黑客更易于攻击你的服务,建议谨慎使用。
Retry-After
服务器升级时,来自客户端的请求会直接给予503(Service Unavailable)错误,通过在返回头里面加入Retry-After字段告知客户端何时服务可以恢复正常访问。Retry-After的头可以是HTTP-Date,也可以是整数,表示多少秒后服务可以恢复正常访问。浏览器在拿到这个值之后可以考虑增加一个定时器在未来的某个时间进行重试。
Age
表示资源缓存的年龄,也就是资源自缓存以来到现在已经过去了多少时间,单位是秒。
3.3 通用首部
Connection
Connection决定当前的事务完成后,是否会关闭网络连接。如果该值是keep-alive
,网络连接就是持久的,不会关闭,使得对同一个服务器的请求可以继续在该连接上完成。如果是close
,则表示当前事务完成后,服务器应该关闭这个HTTP连接。
Connection: close
HTTP协议自从1.1版本后,Connection属性默认值是
keep-alive
。
Cache-Control
Cache-Control既可以用于请求,也可以用于响应。在请求和响应的取值不一样,分别代表了不同的意思。
- no-cache 如果no-cache没有指定值,那就表示不允许缓存。对于请求来说,服务器不得使用缓存内容直接返回。对于响应来说,客户端不得缓存响应的资源内容。
- no-store 告知对方不要持久化请求/响应数据到其它地方,这种信息是敏感的,要保持它的易失性。告知对方记在心里(memory)就行,别写在纸上(disk)。
- no-transform 告知对方不要转换数据。比如客户端上传了raw图像数据,服务器一般都会选择性压缩图像数据进行存储。no-transform告知对方保留原始数据信息,不要进行任何转换。告知对方不要乱动我发过来的东西。
- only-if-cached 用于请求头,告知服务器只要那些已经缓存的内容,不要去reload。如果没有缓存内容就返回504 Gateway Timeout错误。表示客户端不想太麻烦服务器,有就给,没就算了。
- max-age 用于请求头。限制缓存内容的年龄,如果超过max-age年龄的,需要服务器去reload内容资源。这叫客户端的年龄歧视。
- max-stale 用于请求头。客户端允许服务器返回缓存已过期的资源内容,但是限定了最大过期时间。表示客户端虽然很宽容,那是也是有限度的。
- min-fresh 用于请求头。客户端限制服务器不要那些即将过期的资源内容。就好比我们去超市买牛奶,如果牛奶快过期了虽然还在保质期内咱们也就不会考虑。
- public 用于响应头。表示所有内容都将被缓存(客户端和代理服务器都可缓存)。
- private 用于响应头。表示内容只缓存到私有缓存中(仅客户端可以缓存,代理服务器不可缓存)。
Transfer-Encoding
Transfer-Encoding表示报文主体的传输编码格式。尽管这个取值理论上可以有很多,但是当前的 HTTP 规范里实际上只定义了一种传输取值——chunked。
当HTTP对Body进行分块传送时,需要增加头部信息Transfer-Encoding: chunked
才可以进行分块传送。
如果一个HTTP消息(请求消息或应答消息)的Transfer-Encoding消息头的值为chunked,那么,消息体由数量未定的块组成,并以最后一个大小为0的块为结束。
每一个非空的块都以该块包含数据的字节数(字节数以十六进制表示)开始,跟随一个CRLF (回车及换行),然后是数据本身,最后块CRLF结束。在一些实现中,块大小和CRLF之间填充有白空格(0x20)。
最后一块是单行,由块大小(0),一些可选的填充白空格,以及CRLF。最后一块不再包含任何数据,但是可以发送可选的尾部,包括消息头字段。消息最后以CRLF结尾。
一个示例响应如下:
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
25
This is the data in the first chunk
1A
and this is the second one
0
4. 常见HTTP状态码
2xx 成功
- 200 OK,表示从客户端发来的请求在服务器端被正确处理
- 201 Created 请求已经被实现,而且有一个新的资源已经依据请求的需要而建立
- 202 Accepted 请求已接受,但是还没执行,不保证完成请求
- 204 No content,表示请求成功,但响应报文不含实体的主体部分
3xx 重定向
- 301 moved permanently,永久性重定向,表示资源已被分配了新的 URL
- 302 found,临时性重定向,表示资源临时被分配了新的 URL
- 303 see other,表示资源存在着另一个 URL,应使用 GET方法获取资源
- 304 not modified,表示服务器允许访问资源,但因发生请求未满足条件的情况
- 307 temporary redirect,临时重定向,和302含义相同
4xx 客户端错误
- 400 bad request,请求报文存在语法错误
- 401 unauthorized,表示发送的请求需要有通过 HTTP 认证的认证信息
- 403 forbidden,表示对请求资源的访问被服务器拒绝
- 404 not found,表示在服务器上没有找到请求的资源
- 408 Request timeout, 客户端请求超时
5XX 服务器错误
- 500 internal sever error,表示服务器端在执行请求时发生了错误
- 501 Not Implemented 请求超出服务器能力范围,例如服务器不支持当前请求所需要的某个功能,或者请求是服务器不支持的某个方法
- 503 service unavailable,表明服务器暂时处于超负载或正在停机维护,无法处理请求
- 505 http version not supported 服务器不支持,或者拒绝支持在请求中
5. HTTP持久连接
我们知道 HTTP 协议采用“请求-应答”模式,当使用普通模式,即非 Keep-Alive 模式时,每个请求/应答客户和服务器都要新建一个连接,完成之后立即断开连接(HTTP 协议为无连接的协议);当使用 Keep-Alive 模式(又称持久连接、连接重用)时,Keep-Alive 功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive 功能避免了建立或者重新建立连接。
在 HTTP 1.1 版本中,默认情况下所有连接都被保持,如果请求首部加入Connection: close
才关闭。目前大部分浏览器都使用 HTTP 1.1 协议,也就是说默认都会发起 Keep-Alive 的连接请求了,所以是否能完成一个完整的 Keep-Alive 连接就看服务器设置情况。
-
HTTP 长连接不可能一直保持,例如
Keep-Alive: timeout=5, max=100
,表示这个TCP通道可以保持5秒,max=100,表示这个长连接最多接收100次请求就断开。 -
HTTP 是一个无状态协议,这意味着每个请求都是独立的,Keep-Alive 没能改变这个结果。另外,Keep-Alive也不能保证客户端和服务器之间的连接一定是活跃的,在 HTTP1.1 版本中也如此。唯一能保证的就是当连接被关闭时你能得到一个通知,所以不应该让程序依赖于 Keep-Alive 的保持连接特性,否则会有意想不到的后果。
-
使用长连接之后,客户端、服务端怎么知道本次传输结束呢?两部分:1. 判断传输数据是否达到了Content-Length 指示的大小;2. 动态生成的文件没有 Content-Length ,它是分块传输(chunked),这时候就要根据 chunked 编码来判断,chunked 编码的数据在最后有一个空 chunked 块,表明本次传输数据结束。
网友评论