1. 结语
http协议作为应用最为广泛的应用层网络协议还是很有必要扣扣协议的细节的,这篇结语我们就来扣下细节,有兴趣的可以看下没兴趣的也不会影响使用.本文基于使用最为广泛的HTTP 1.1协议,其使用的RFC文档为RFC2616
1.1. HTTP的地址格式
http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
协议和host不分大小写
1.2. HTTP消息
一个HTTP消息可能是request
或者response
消息,两种类型的消息都是由开始行(start-line),零个或多个header域,一个表示header域结束的空行(也就是,一个以CRLF为前缀的空行),一个可能为空的消息主体(message-body).一个合格的HTTP客户端不应该在消息头或者尾添加多余的CRLF,服务端也会忽略这些字符.
header的值不包括任何前导或后续的LWS(线性空白),线性空白可能会出现在域值(filed-value)的第一个非空白字符之前或最后一个非空白字符之后.前导或后续的LWS可能会被移除而不会改变域值的语意.任何出现在filed-content之间的LWS可能会被一个SP(空格)代替.header域的顺序不重要,但建议把常用的header放在前边(协议里这么说的).
1.2.1. Request消息
RFC2616中这样定义HTTP Request消息:
Request = Request-Line *(( general-header | request-header(跟本次请求相关的一些header) | entity-header ) CRLF)(跟本次请求相关的一些header) CRLF [ message-body ]
一个HTTP的request消息以一个Request-Line开始,从第二行开始是header,接下来是一个空行,表示header结束,最后是消息体.
请求行的定义如下:
//请求行的定义 Request-Line = Method SP Request-URL SP HTTP-Version CRLF //方法的定义 Method = "OPTIONS" | "GET" | "HEAD" |"POST" |"PUT" |"DELETE" |"TRACE" |"CONNECT" | extension-method //资源地址的定义 Request-URI ="*" | absoluteURI | abs_path | authotity(CONNECT)
Request消息中使用的header可以是general-header或者request-header,request-header.其中有一个比较特殊的就是Host,Host会与reuqest Uri一起来作为Request消息的接收者判断请求资源的条件,方法如下:
假如Request-URI是绝对地址(absoluteURI),这时请求里的主机存在于Request-URI里.任何出现在请求里Host头域值应当被忽略.
假如Request-URI不是绝对地址(absoluteURI),并且请求包括一个Host头域,则主机由该Host头域值决定.
假如由规则1或规则2定义的主机是一个无效的主机,则应当以一个400(错误请求)错误消息返回.
1.2.2. Response消息
响应消息跟请求消息几乎一模一样,定义如下:
Response = Status-Line *(( general-header | response-header | entity-header ) CRLF) CRLF [ message-body ]
除了header不使用request-header之外只有第一行不同--响应消息的第一行是状态行.
Status-Line的内容首先是协议的版本号,然后跟着返回码(http状态码),最后是解释的内容,它们之间各有一个空格分隔,行的末尾以一个回车换行符作为结束.定义如下:
Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase C
返回码
返回码是一个3位数,第一位定义的返回码的类别,总共有5个类别,它们是:
1xx
: 消息类型,表示请求已经收到,过程继续Informational2xx
: 成功类型,表示请求已经被接受且处理完成3xx
: 重新定向类型,表示接下来的操作需要重新定向到别处才可以完成4xx
: 客户端错误类型,表示请求有错误或不满足服务端要求5xx
: 服务端错误类型,表示服务端无法处理请求
RFC2616中给出了一系列返回码的扩展,但是那些只是示例.HTTP1.1不强制通信各方遵守这些扩展的返回码,通信各方在返回码的实现上只需要遵守以上边定义的这5种类别的定义,意思就是返回码的第一位要严格按照文档中所述的来,其他的随便定义,因此才有RESTful规范复用http返回码.任何人接收到一个不认识的返回码xyz都可以把它当做x00
来对待.
1.3. Header
HTTP中的Header一般用于描述一些请求或响应的元信息或行为.
RFC2616中定义了4种header类型,在通信各方都认可的情况下,请求头可以被扩展的(可信的扩展只能等到协议的版本更新),如果接收者收到了一个不认识的请求头,这个头将会被当做实体头。4种头类型如下:
1.3.1. 通用头(General Header Fields)
可用于request,也可用于response的头,但不可作为实体头,只能作为消息的头.
general-header = Cache-Control ; Section 14.9 | Connection ; Section 14.10 | Date ; Section 14.18 | Pragma ; Section 14.32 | Trailer ; Section 14.40 | Transfer-Encoding ; Section 14.41 | Upgrade ; Section 14.42 | Via ; Section 14.45 | Warning ; Section 14.46
1.3.2. 请求头(Request Header Fields)
被请求发起端用来描述或改变请求行为的头.
request-header = Accept ; Section 14.1 | Accept-Charset ; Section 14.2 | Accept-Encoding ; Section 14.3 | Accept-Language ; Section 14.4 | Authorization ; Section 14.8 | Expect ; Section 14.20 | From ; Section 14.22 | Host ; Section 14.23 | If-Match ; Section 14.24 | If-Modified-Since ; Section 14.25 | If-None-Match ; Section 14.26 | If-Range ; Section 14.27 | If-Unmodified-Since ; Section 14.28 | Max-Forwards ; Section 14.31 | Proxy-Authorization ; Section 14.34 | Range ; Section 14.35 | Referer ; Section 14.36 | TE ; Section 14.39 | User-Agent ; Section 14.43
1.3.3. 响应头(Response Header Fields)
被服务器用来对资源进行进一步的说明.
response-header = Accept-Ranges ; Section 14.5 | Age ; Section 14.6 | ETag ; Section 14.19 | Location ; Section 14.30 | Proxy-Authenticate ; Section 14.33 | Retry-After ; Section 14.37 | Server ; Section 14.38 | Vary ; Section 14.44 | WWW-Authenticate ; Section 14.47
1.3.4. 实体头(Entity Header Fields)
如果消息带有消息体,实体头用来作为元信息;如果没有消息体,就是为了描述请求的资源的信息.
entity-header = Allow ; Section 14.7 | Content-Encoding ; Section 14.11 | Content-Language ; Section 14.12 | Content-Length ; Section 14.13 | Content-Location ; Section 14.14 | Content-MD5 ; Section 14.15 | Content-Range ; Section 14.16 | Content-Type ; Section 14.17 | Expires ; Section 14.21 | Last-Modified ; Section 14.29 | extension-header
1.4. 消息体(Message Body)和实体主体(Entity Body)
如果有Transfer-Encoding
头,那么消息体解码完了就是实体主体,如果没有Transfer-Encoding
头,消息体就是实体主体.
message-body = entity-body | <entity-body encoded as per Transfer-Encoding>
在request消息中,消息头中含有Content-Length
或者Transfer-Encoding
表示会有一个消息体跟在后边,如果请求的方法不应该含有消息体(如OPTION
),那么request消息一定不能含有消息体,即使客户端发送过去,服务器也不会读取消息体.
在response消息中,是否存在消息体由请求方法和返回码来共同决定.像1xx,204,304不会带有消息体.
1.4.1. 消息体的长度
消息体长度的确定有一下几个规则,它们顺序执行:
所有不应该返回内容的Response消息都不应该带有任何的消息体,消息会在第一个空行就被认为是终止了.
如果消息头含有
Transfer-Encoding
,且它的值不是identity
,那么消息体的长度会使用chunked
方式解码来确定,直到连接终止.如果消息头中有
Content-Length
,那么它就代表了entity-length
和transfer-length
.如果同时含有Transfer-Encoding
,则entity-length
和transfer-length
可能不会相等,那么Content-Length
会被忽略.如果消息的媒体类型是
multipart/byteranges
,并且transfer-length
也没有指定,那么传输长度由这个媒体自己定义.通常是收发双发定义好了格式,HTTP1.1客户端请求里如果出现Range头域并且带有多个字节范围(byte-range)指示符,这就意味着客户端能解析multipart/byteranges
响应.如果是Response消息,也可以由服务器来断开连接作为消息体结束.
从消息体中得到实体主体,它的类型由两个header来定义:Content-Type
和Content-Encoding
(通常用来做压缩).如果有实体主体,则必须有Content-Type
;如果没有,接收方就需要猜测,猜不出来就是用application/octet-stream
,可以查看这个表格查找对应的Content-Type
1.5. HTTP连接
HTTP1.1的连接默认使用持续连接(persistent connection),持续连接指的是保持连接已满足多次请求响应.
有时客户端会需要在短时间内向服务端请求大量的相关的资源,如果不是持续连接那么每个资源都要建立一个新的连接,HTTP底层使用的是TCP,那么每次都要使用三次握手建立TCP连接,这将造成极大的资源浪费.
持续连接可以带来很多的好处:
使用更少的TCP连接,对通信各方的压力更小.
可以使用管道(pipeline)来传输信息,这样请求方不需要等待结果就可以发送下一条信息,对于单个的TCP连接使用更充分.
流量更小
顺序请求的延时更小
不需要重新建立TCP连接就可以传送error,关闭连接等信息.
HTTP1.1的服务器使用TCP的流量控制来控制HTTP的流量,HTTP1.1的客户端在收到服务器连接中发过来的error信息就要马上关闭此链接.
还没有评论,来说两句吧...