后浪笔记一零二四

1. 基于ABNF语义定义的HTTP消息格式

  1. HTTP协议格式
1
2
3
4
5
6
请求:                            |   响应:
GET / HTTP/1.1  (request-line)   |   HTTP/1.1 200 OK  (status-line)
HOST: time.geekbang.org          |   Date: Tue,26 Mar 2019 02:39:11 GMT
                                 |   Content-Type: application/octet-stream
                                 |   Transfer-Encoding: chunked
                                 |   Connection: keep-alive

start-line: 分为request-line, status-line

header-field header-field

message-body

  1. ABNF (扩充巴科斯-瑙尔范式)操作符
  • 空白字符: 用来分隔定义中的各个元素
    • method SP request-target SP HTTP-version CRLF
  • 选择/: 表示多个规则都是可供选择的规则
    • start-line = request-line / status-line
  • 值范围%c##-## :
    • OCTAL = "0" / "1" / "2" / "3" / "4" / "5" / "6" / "7"OCTAL = %x30-37等价
  • 序列组合(): 将规则组合起来,视为单个元素
  • 不定量重复m*n
    • *元素表示零个或多个元素: *( header-field CRLF )
    • 1*元素表示一个或更多元素
    • 2*4元素表示两个至四个元素
  • 以逗号相隔的多个元素#:
    • #method可以表达put,get,post
  • 可选序列[]:
    • [ message-body ]
  1. ABNF (扩充巴科斯-瑙尔范式)核心规则
规则 形式定义 意义
VCHAR %x21-7E 可见(打印)字符
CTL %x00-1F / %x7F 控制字符
CHAR %x01-7F 任何7-位US-ASCII字符,不包括NUL (%x00)
OCTET %x00-FF 8位二进制,一个字节,表示二进制字节
ALPHA %x41-5A / %x61-7A 大写和小写ASCII字母(A-Z,a-z)
DIGIT %x30-39 数字(0-9)
HEXDIG DIGIT / “A” / “B” / “C” / “D” / “E” / “F” 十六进制数字(0-9, A-F, a-f)
DQUOTE %x22 双引号
SP %x20 空格
HTAB %x09 横向制表符
WSP SP / HTAB 空格或横向制表符
LWSP *(WSP / CRLF WSP) 直线空白(晚于换行)
CR %x0D 回车
LF %x0A 换行
CRLF CR LF 互联网标准换行
BIT “0” / “1” 二进制数字
  1. 基于ABNF描述的HTTP协议格式
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
HTTP-message = start-line *( header-filed CRLF ) CRLF [ message-body ]
  * start-line = request-line / status-line
    * request-line = method SP request-target SP HTTP-version CRLF
    * status-line = HTTP-version SP status-code SP reason-phrase CRLF
  * header-field = field-name ":" OWS field-value OWS
    * OWS = *( SP / HTAB )
    * field-name = token
    * field-value = *( field-content / obs-fold )
  * message-body = *OCTET

以下消息不能含有包体:
    HEAD方法请求对应的响应
    1xx204304对应的响应
    CONNECT方法对应的2xx响应

2. URI

2.1 使用ABNF来表述URI

URL: RFC1738 (1994.12), Uniform Resource Locator, 表示资源的位置,期望提供查找资源的方法。
URN: RFC2141 (1997.5), Uniform Resource Name, 期望为资源提供持久的、位置无关的标识方式,并允许简单地将多个命名空间映射到单个URN命名空间

  • 例如磁力链接magnet:?xt=urn:sha1:YNCKHTQC5C URI: RFC1630 (1994.6)、RFC3986 (2005.1,取代RFC2396和RFC2732),Uniform Resource Identifier,用以区分资源,是URL和URN的超集,用以取代URL和URN的概念。

URI格式:

* URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
* sheme = ALPHA * ( ALPHA / DIGIT / "+" / "-" / "." )
  * 例如: http, https, ftp, mailto, rtsp, file, telnet
* query = *( pchar / "/" / "?" )
* fragment = *( pchar / "/" / "?" )

hier-part = [ "//" ] authority path

* authority = [ userinfo "@" ] host [ ":" port ]
  * userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
  * host = IP-literal / IPv4address / reg-name
  * port = *DIGIT

* path = path-abempty/ path-absolute/ path-noscheme / path-rootless/ path-empty
  * path-abempty = *("/" segment)
    * 以/开头的路径或者空路径
  * path-absolute = "/" [ segment-nz *( "/" segment ) ]
    * 以/开头的路径,但不能以//开头
  * path-noscheme = segment-nz-nc *( "/" segment )
    * 以非:号开头的路径
  * path-rootless = segment-nz *( "/" segment )
    * 相对path-noscheme,增加允许以:号开头的路径
  * path-empty = 0<pchar>
    * 空路径

相对URI:

1
2
3
URI-reference = URI/relative-ref
* relative-ref = relative-part [ "?" query ] [ "#" fragment ]
  * relative-part = "//" authority path-abempty / path-absolute / path-noscheme / path-empty

2.2 对URI进行编码

  1. 为什么要进行URI编码
  • 传递数据中,如果存在用作分隔符的保留字怎么办?
  • 对可能产生歧义性的数据编码
    • 不在ASCII码范围内的字符
    • ASCII码中不可显示的字符
    • URI中规定的保留字符
      1
      2
      3
      4
      5
      6
      7
      8
      9
      
      reserved = gen-delims / sub-delims
        * gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
        * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
      
      unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
        * ALPHA: %41-%5A and %61-%7A
        * DIGIT: %30-%39
        * -:%2D  .:%2E  _:%5F
        * ~:%7E, 某些实现将其作为保留字符
      
    • 不安全字符 (传输环节中可能会被不正确处理),如空格、引号、尖括号等

示例: https://www.baidu.com/s?wd=?#! https://www.baidu.com/s?wd=极客 时间 https://www.baidu.com/s?wd=极客’?时间

  1. URI百分号编码
  • 百分号编码的方式
  • 非ASCII码字符(例如中文):建议先UTF8编码,再US-ASCII编码
  • 对URI合法字符,编码与不编码是等价的
    • 例如,“URI转换"既可以"URI%e8%bd%ac%e6%8d%a2”,也可以 “%55%52%49%e8%bd%ac%e6%8d%a2”

3. start line

3.1 请求行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
request-line = method SP request-target SP HTTP-version CRLF

* request-target = origin-form / absolute-form / authority-form / asterisk-form
  * origin-form = path-absolute [ "?" query ] 
    *origin server发起的请求,path为空时必须传递 /,不传域名
  * absolute-form = absolute-URI
    * 仅用于向正向代理proxy发起请求时,详见正向代理与隧道,传域名
    * absolute-URI = scheme ":" hier-part ["?" query]
  * authority-form = authority
    * 仅用于CONNECT方法(用于建立vpn隧道,它传域名,不传路径),例如CONNECT www.example.com:80 HTTP/1.1
  * asterisk-form = "*"
    * 仅用于OPTIONS方法。

* HTTP-version版本号发展历史:https://www.w3.org/Protocols/History.html
  * HTTP/0.9: 只支持GET方法,过时
  * HTTP/1.0RFC1945, 1996,常见使用于代理服务器(例如Nginx默认配置)
  * HTTP/1.1: RFC2616, 1999
  * HTTP/2.0: 2015.5正式发布
  1. 常见方法 (RFC7231)
  • GET: 主要的获取信息方法,大量的性能优化都针对该方法,幂等方法
  • HEAD:类似GET方法,但服务器不发送BODY,用以获取HEAD元数据,幂等方法
  • POST: 常用于提交HTML FROM表单、新增资源等
  • PUT:更新资源,带条件时是幂等方法
  • DELETE:删除资源,幂等方法
  • CONNECT:建立tunnel隧道
  • OPTIONS:显示服务器对访问资源支持的方法,幂等方法,该方法的响应头中的Allow字段定义访问该资源允许使用的方法。
  • TRACE: 回显服务器收到的请求,用于定位问题。有安全风险,所以在2007年nginx的0.5.17版本之后不再支持该方法了,直接返回405状态码。

幂等方法:调用一次和调用多次是一样的。幂等方法对于设计分布式系统的事务是有意义的。

  1. 用于文档管理的WEBDAV方法(RFC2518)
  • PROPFIND: 从服务器中检索以XML格式存储的属性,例如目录,目录的树状结构可以以XML格式进行存储
  • PROPPATCH: 在单个原子性动作中更改和删除资源的多个属性,比如同时修改文件的修改时间和文件名。
  • MKCOL: 创建集合或者目录
  • COPY: 将资源从一个URI复制到另一个URI
  • MOVE:将资源从一个URI移动到另一个URI
  • LOCK:锁定一个资源。WebDAV支持共享锁和互斥锁
  • UNLOCK:解除资源的锁定

3.1.1 Connect方法

  1. HTTP Tunnel隧道
  • 用于通过HTTP连接传输非HTTP协议格式的消息,常用于穿越防火墙
    • (d)之后,隧道就建立成功了
    • 建立隧道后,由于传输的并非HTTP消息,因此不再遵循请求/响应模式,已变为双向传输
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
       隧道位于客户端和网关之间                  普通的SSl连接
客户端<---------------------------------->正向代理(隧道端点)<------------------------->orders.joes-hardware.com
  |                                              |                                              |
  |(a)发送CONNECT请求                            |                                              |
  |CONNECT orders.joes-hardware.com:443 HTTP/1.0 |                                              |
  |User-agent: SuperBrowser4.2                   |                                              |
  |--------------------------------------------->|                                              |
  |                                              |(b)打开到端口443的TCP连接                     |
  |                                              |--------------------------------------------->|
  |                                              |<---------------------------------------------|
  | (d)返回HTTP连接就绪报文                      |                       (c)连接已建立          |
  |<---------------------------------------------|                                              |
  |(e)此时,建立起了原始数据的任意、             |                                              |
  |双向通信,直到连接关闭为止                     |                                              |
  |mdsnrt734tngfd/pof92piub5.                    | mdsnrt734tngfd/pof92piub5.                   |
  |lod9fuo8w34b4/;p-90[g9yk,8                    | lod9fuo8w34b4/;p-90[g9yk,8                   |
  |U|t6y6/ $!&9890G&*&98...                      | U|t6y6/ $!&9890G&*&98...       gal1304-*&hsgd|
  |--------------------------------------------->|---------------->--  --<----------------------|
  |                                              |                   \/                         |
  |                                gal1304-*&hsgd|                   /\                         |
  |<---------------------------------------------|<---------------<--  -->--------------------->|
  |                                              |                                              |
  1. HTTP Tunnel隧道的认证
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
客户端<-----客户端和网关之间的隧道------> 正向代理(隧道端口)<-----普通的SSL连接----------> orders.joes-hardware.com
  |(a)发送的CONNECT请求                           |                                                  |
  |CONNECT orders.joes-hardware.com:443 HTTP/1.0  |                                                  |
  |User-agent: SuperBrowser 4.2                   |                                                  |
  |---------------------------------------------->|                                                  |
  |(b)返回的认证请求                              |                                                  |
  |HTTP/1.0 407 Proxy authentication required     |                                                  |
  |Proxy-authenticate: Basic realm="wormhole"     |                                                  |
  |<----------------------------------------------|                                                  |
  |(c)发送带有适当认证信息的CONNECT请求           |                                                  |
  |CONNECT orders.joes-hardware.com:443 HTTP/1.0  |                                                  |
  |User-agent: SuperBrowser 4.2                   |                                                  |
  |Proxy-authorization: Basic YnJpYW4tdG90dHk6T3ch|                                                  |
  |---------------------------------------------->|(d)打开到端口443的TCP连接                         |
  |          (f) 返回HTTP连接就绪报文             |------------------------------------------------->|
  | HTTP/1.0 200 Connection established           |<-------------------------------------------------|
  |<----------------------------------------------|                   (e)连接已建立                  |

3.2 响应行

1
2
3
status-line = HTTP-version SP status-code SP reason-phrase CRLF
  * status-code = 3DIGIT
  * reason-phrase = *( HTAB / SP / VCHAR / obs-text )

响应码规范:RFC6585 (2012.4)、RFC7231 (2014.6) 这些规范可以指导服务器设计,但是并不是所有服务器都遵循响应码中的规范。

  1. 1xx: 请求已接收到,需要进一步处理才能完成,HTTP1.0不支持
  • 100 Continue:上传大文件前使用 客户端发起请求时携带Expect:100-continue头部, 然后服务器返回100状态码,表示已准备好大文件的接收。
  • 101 Switch Protocols: 协议升级使用 由客户端发起请求中携带Upgrade:头部触发,如升级websocket或者http/2.0, 如果服务器支持的话,就会返回101状态码。
  • 102 Processing: WebDav请求可能包含许多涉及文件操作的子请求,需要很长时间才能完成请求。 如果服务器等到这些子请求全部完成后才向客户端发送响应,就有可能导致客户端超时。 所以,服务器可以先发送102的状态码给客户端,表示这个请求已经收到了, 但是服务器可能需要很长时间才能处理完,当前并没有响应返回给你。
  1. 2xx: 成功处理请求
  • 200 OK: 成功返回响应
  • 201 Created: 有新资源在服务器端被成功创建
  • 202 Accepted: 服务器接收并开始处理请求,但请求未处理完成。 这样一个模糊的概念是有意如此设计,可以覆盖更多的场景。 例如异步、需要长时间处理的任务。
  • 203 Non-Authoritative Information: 当代理服务器修改了origin server的原始响应包体时(例如更换了HTML中的元素值), 代理服务器可以通过修改200为203的方式告知客户端这一事实,方便客户端为这一行为作出相应的处理。203响应可以被缓存。 203状态码并没有被广泛接收。
  • 204 No Content: 成功执行了请求且不携带响应包体,并暗示客户端无需更新当前的页面视图。
  • 205 Reset Content:成功执行了请求且不携带响应包体,同时指明客户端需要更新当前页面视图。
  • 206 Partial Content:使用range协议时返回部分响应内容时的响应码。
  • 207 Multi-Status: RFC4918,在WEBDAV协议中以XML返回多个资源的状态。
  • 208 Already Reported:RFC5842,为避免相同集合下资源在207响应码下重复上报,使用208可以使用父集合的响应码。
  1. 3xx: 重定向使用Location指向的资源或者缓存中的资源。在RFC2068中规定客户端重定向次数不应超过5次,以防止死循环。
  • 300 Multipart Choices: 资源有多种表述,通过300返回给客户端后由其自行选择访问哪一种表述。由于缺乏明确的细节,300很少使用。
  • 301 Moved Permanently: 资源永久性的重定向到另一个URI中。
  • 302 Found:资源临时的重定向到另一个URI中。
  • 303 See Other: 重定向到其他资源,常用于POST/PUT等方法的响应中,和302一致。
  • 304 Not Modified: 当客户端拥有可能过期的缓存时,会携带缓存的标识etag、时间等信息询问服务器缓存是否仍可复用,而304是告诉客户端可以复用缓存。
  • 307 Temporary Redirect:类似302,但明确重定向后请求方法必须与原请求方法相同,不得改变。
  • 308 Permanent Redirect:类似301,但明确重定向后请求方法必须与原请求方法相同,不得改变。
  1. 4xx: 客户端出现错误
  • 400 Bad Request: 服务器认为客户端出现了错误,但不能明确判断为以下哪种错误时使用此错误码。例如HTTP请求格式错误。
  • 401 Unauthorized: 用户认证信息缺失或者不正确,导致服务器无法处理请求。
  • 407 Proxy Authentication Required: 对需要经由代理的请求,认证信息未通过代理服务器的验证
  • 403 Forbidden:服务器理解请求的含义,但没有权限执行此请求
  • 404 Not Found: 服务器没有找到对应的资源
  • 410 Gone: 服务器没有找到对应的资源,且明确的知道该位置永久性找不到该资源。
  • 405 Method Not Allowd: 服务器不支持请求行中的method方法
  • 406 Not Acceptable: 对客户端指定的资源表述不存在(例如对语言或者编码有要求),服务器返回表述列表供客户端选择。
  • 408 Request Timeout: 服务器接收请求超时
  • 409 Conflict: 资源冲突,例如上传文件时目标位置已经存在版本更新的资源
  • 411 Length Required: 如果请求含有包体且未携带Content-Length头部,且不属于chunk类请求时,返回411。
  • 412 Procondition Failed: 复用缓存时传递的If-Unmodified-Since或If-None-Match头部不被满足
  • 413 Payload Too Large / Request Entity Too Large: 请求的包体超出服务器能处理的最大长度。
  • 414 URI Too Long: 请求的URI超出服务器能接受的最大长度
  • 415 Unsupported Media Type: 上传的文件类型不被服务器支持
  • 416 Range Not Satisfiable: 无法提供Range请求中指定的那段包体
  • 417 Expectation Failed: 对于Expect请求头部期待的情况无法满足时的响应码
  • 421 Misdirected Request: 服务器认为这个请求不该发给它,因为它没有能力处理。
  • 426 Upgrade Required: 服务器拒绝基于当前HTTP协议提供服务,通过Upgrade头部告知客户端必须升级协议才能继续处理。
  • 428 Precondition Required: 用户请求中缺失了条件类头部,例如If-Match
  • 429 Too Many Requests: 客户端发送请求的速率太快(一般使用503)
  • 431 Request Header Fields Too Large: 请求的HEADER头部大小超过限制(一般只做414错误码限制,431错误码太细了)
  • 451 Unavailable For Legal Reasons: RFC7725, 由于法律原因资源不可访问。
  1. 5xx: 服务器端出现错误
  • 500 Internal Server Error: 服务器内部错误,且不属于以下错误类型
  • 501 Not Implemented: 服务器不支持实现请求所需要的功能
  • 502 Bad Gateway: 代理服务器无法获取到合法响应
  • 503 Service Unavailable: 服务器资源尚未准备好处理当前请求,熔断降级限流时经常使用503
  • 504 Gateway Timeout: 代理服务器无法及时的从上游获得响应
  • 505 HTTP Version Not Supported:请求使用的HTTP协议版本不支持
  • 507 Insufficient Storage: 服务器没有足够的空间处理请求(把服务器内部的问题暴露给了客户端,有安全性问题,通常不会使用)
  • 508 Loop Detected: 访问资源时检测到循环
  • 511 Network Authentication Required: 代理服务器发现客户端需要进行身份验证才能获得网络访问权限,网吧场景中经常使用。

注意: 当客户端接收到了一个它不认识的响应码,不知道如何处理的时候,将使用这些响应码所在系列的第一个,也就是00系列的错误码进行处理。比如收到了555状态码,而客户端不认这个状态码,就会使用500来处理。

3.2.1 重定向

  1. 重定向的流程
  • 当浏览器接收到重定向响应码时,需要读取响应头部Location头部的值,获取到新的URI再跳转访问该页面
             Client                                       Server
               |                                            |
Initial request|------------------------------------------->|
               |GET /doc HTTP/1.1                           |
               |                                            |
               |<-------------------------------------------|Resource moved.
               |        HTTP/1.1 301 Moved Permanently      |New location returns
               |        Location: /doc_new                  |
               |                                            |
               |                                            |
Request to the |------------------------------------------->|
new location   |GET /doc_new HTTP/1.1                       |
               |                                            |
               |                                            |
               |<-------------------------------------------|Resource returned
               |                     HTTP/1.1 200 OK        |
  1. Location头部
1
2
# 对201响应码表示新创建的资源, 对301响应码表示重定向的目的地址
* Location = URI-reference
  1. 重定向循环
  • 服务器端在生成Location重定向URI时,在同一条路径上使用之前的URI,导致无限循环出现
  • Chrome浏览器会提示:ERR_TOO_MANY_REDIRECTS
             Client                                       Server
               |                                            |
Initial request|------------------------------------------->|
               |GET /doc HTTP/1.1                           |
               |                                            |
               |<-------------------------------------------|Resource moved.
               |        HTTP/1.1 301 Moved Permanently      |New location returns
               |        Location: /doc_new                  |
               |                                            |
               |                                            |
Request to the |------------------------------------------->|
new location   |GET /doc_new HTTP/1.1                       |
               |                                            |
               |                                            |
               |<-------------------------------------------|Resource Moved.
               |             HTTP/1.1 301 Moved Permanently |New location returns
               |             Location: /doc                 |

4. 请求头和响应头

4.1 Host头部

  • Host = uri-host[":" port]
    • HTTP/1.1规范要求,不传递Host头部则返回400错误响应码 因为上世纪90年代域名相对是比较少的,每一个服务器的IP地址仅对应一个域名,所以当用户 已经对你的服务器建立连接以后,是不需要考虑哪个域名对应的服务的。 但是,后来域名越来越多,而IP地址相对比较少,所以引入了Host头部。
    • uri-host:表示URI中的host部分

4.2 消息转发

  • Max-Forwards头部
    • 限制Proxy代理服务器的最大转发次数,仅对TRACE/OPTIONS方法有效
    • Max-Forwards = 1*DIGIT
  • Via头部: 指明经过的代理服务器名称及版本,例如Via: 1.0 th-openresty, 1.0 th-nginx
1
2
3
4
Via = 1*( received-protocol RWS received-by [ RWS comment ] )
  received-protocol = [ protocol-name "/" ] protocol-version
  received-by = ( uri-host [ ":" port ] ) / pseudonym
  pseudonym = token
  • Cache-Control:no-transform
    • 禁止代理服务器修改响应包体。

4.3 请求与响应的上下文

4.3.1 请求上下文

  1. User-Agent: 指明客户端的类型信息,服务器可以据此对资源的表述做抉择
1
2
3
4
User-Agent = product *(RWS product/comment)
  product = token ["/" product-version]
  RWS = 1*(SP/HTAB)
  comment = "(" token ")"

例如: User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:66.0) Gecko/20100101 Fireforx/66.0

  1. Referer 浏览器对来自某一页面的请求自动添加的头部
  • Referer = absolute-URI / partial-URI
  • 例如
    • Referer: https://developer.mozilla.org/zh_CN/docs/Web/HTTP/Headers/User-Agent
  • Referer不会被添加的场景
    • 来源页面采用的协议为表示本地文件的"file"或者"data"
    • 当前请求页面采用的是http协议,而来源页面采用的是https协议
  • 服务器常用Referer来做统计分析、缓存优化、防盗链等功能。
  1. From
  • 主要用户网络爬虫,告诉服务器如何通过邮件联系到爬虫的负责人
  • From = mailbox

4.3.2 响应上下文

  1. Server
  • 指明服务器上所用软件的信息,用于帮助客户端定位问题或者统计数据
  • Server = product*(RWS product/comment)
    • product = token ["/" product-version]
  • 例如: Server: nginx
  1. Allow: 告诉客户端,服务器上该URI对应的资源允许哪些方法的执行
  • Allow = #method
  • 例如:
    • Allow: GET,HEAD,PUT

4.4 内容协商与资源表述

  1. 内容协商的前提 该URI指向的资源有多种不同的表述,例如一份文档可以有不同语言的翻译、不同的媒体格式、可以针对不同的浏览器提供不同的压缩编码等。

  2. 内容协商的两种方式

  • Proactive 主动式内容协商: 指由客户端先在请求头部中提出需要的表述形式,而服务器根据这些请求头部提供特定的表述
                                                   Resource on the server
                                                   (with different available representation)
                                                   |--------------
                                                   | 法语|  德语 |-----< URLa
                                                   | text/html   |-----< URLb
                                                   | text/pdf    |-----< URLc
Client------1.Requests the resource----->URL-------| gzip压缩算法|-----< URLd
   ^        GET /URL HTTP/1.1                      | br压缩算法  |-----< URLe
   |        Accept: text/*                         ---------------
   |        Accept-Language: en                           |
   |        Accept-Encoding: br,gzip; q=0.8               |
   --------------------------------------------------------
         2.Returns the most appropriate representation of the resource.
         HTTP/1.1 200 OK
         Content-Location: /URLe
         Content-Type: text/html
         Content-Language: en
         Content-Encoding: br
  1. Reactive 响应式内容协商: 指服务器返回300 Multiple Choices或者406 Not Acceptable,由客户端 选择一种表述URI使用。 但是响应式内容协商它有个问题,因为RFC规范没有明确的告诉Client应该依据怎样的规则, 所以导致各大浏览器无法按照一个统一的策略,去选择合适的响应表述给用户,所以, 响应式内容协商相对是很少使用的。
                                                   Resource on the server
                                                   (with different available representation)
                                                   |--------------
                                                   | 法语|  德语 |-----< URLa
                                                   | text/html   |-----< URLb
                                                   | text/pdf    |-----< URLc
Client------1.Requests the resource----->URL-------| gzip压缩算法|-----< URLd
 ^|^        GET /URL HTTP/1.1                      | br压缩算法  |-----< URLe
 |||        Accept: text/*                         ----------|----        ^
 |||        Accept-Language: en                           |  |            |
 |||        Accept-Encoding: br,gzip; q=0.8               |  |            |
 ||-------------------------<------------------------------  |            |
 ||      2.Returns the list of appropriate representations.  |            |
 ||      HTTP/1.1 300 Multiple Choices                       |            |
 ||------------------------->--------------------------------|-------------
 |          3. Requests the specific representation.         |
 |          GET /URLe HTTP/1.1                               |
 |          Accept: text/*                                   |
 |          Accept-Language: en                              |
 |          Accept-Encoding: br,gzip; q=0.8                  |
 |--------------------------<--------------------------------|
       4.Returns the requested representation.
       HTTP/1.1 200 OK
       Content-Location: /URLe
       Content-Type: text/html
       Content-Language: en
       Content-Encoding: br
  1. 常见的协商要素
# 质量因子q: 内容的质量、优先级
# 媒体资源的MIME类型及质量因子
   Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
   Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,
   image/apng,*/*;q=0.8,application/signed-exchange;v=b3
# 字符编码:由于UTF-8格式广为使用,Accept-Charset已被废弃
      Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
# 内容编码:主要指压缩算法
      Accept-Encoding: gzip,deflate,br
# 表述语言
      Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
      Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
  1. 国际化与本地化
# internationalization (i18n,i和n之间有18个字符)
   指设计软件时,在不同的国家、地区可以不做逻辑实现层面的修改便能够以不同语言显示
# localization (l10n, l和n之间有10个字符)
   指内容协商时,根据请求中语言及区域信息,选择特定的语言作为资源表述
  1. 资源表述的元数据头部
# 媒体类型、编码
  content-type:text/html; charset=utf-8
# 内容编码
  content-encoding:gzip
# 语言
  Content-Language:de-DE,en-CA

5. 包体

5.1 两种传输HTTP包体的方式

  1. 发送HTTP消息时已能够确定包体的全部长度 使用Content-Length头部明确指明包体长度(HTTP1.1协议中就明确指定必须指定Content-Length头部),Content-Length = 1*DIGIT

使用Python来模拟:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#! /usr/bin/python
# -*- coding: utf-8 -*-
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ("127.0.0.1", 12345)
sock.bind(server_address)
sock.listen(100) #表示我们最多可以处理100个连接

while True:
  conn, client_address = sock.accept()
  try:
    data = conn.recv(4096) #接收4096字节的请求
    response = 'HTTP/1.1 200 OK\r\nContent-Length: 10\r\n\r\nHelloWorld'
    conn.send(response.encode())
  finally:
    conn.close()
  1. 发送HTTP消息时不能确定包体的全部长度 使用Transfer-Encoding头部指明使用Chunk传输方式,含Transfer-Encoding头部后Content-Length头部应被忽略。

优点: ① 基于长连接持续推送动态内容 ② 压缩体积较大的包体时,不必完全压缩完(计算出头部)再发送,可以边发送边压缩 ③ 传递必须在包体传输完才能计算出的Trailer头部

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 请求头的abnf
Transfer-Encoding = "chunked" / "compress" / "deflate" / "gzip" / transfer-extension

# 包体的abnf
chunked-body = *chunk
               last-chunk
               trailer-part
               CRLF

chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
   * chunk-size = 1*HEXDIG: 注意这里是16进制而不是10进制
   * chunk-data = 1*OCTET

# 用于表示chunk已经已经结束了
last-chunk = 1*("0")[chunk-ext]CRLF

trailer-part = *(header-field CRLF)

Trailer头部的传输:

  • TE头部:客户端在请求中声明是否接收Trailer头部 TE: trailers
  • Trailer头部:服务器告知接下来chunk包体后会传输哪些Trailer头部 Trailer: Date
  • 以下头部不允许出现在Trailer的值中: 用于信息分帧的首部(例如Transfer-Encoding和Content-Length) 用于路由用途的首部(例如Host) 请求修饰首部(例如控制类和条件类的,如Cache-Control,Max-Forwards,或者TE) 身份验证首部(例如Authorization或者Set-Cookie) Content-Encoding, Content-Type, Content-Range, 以及Trailer自身

5.2 用于上传/下载附件的Content-Disposition头部

1
2
3
4
5
6
7
* disposition-type = "inline" | "attachment" | disp-ext-type
  * inline: 指定包体时以inline内联的方式,作为页面的一部分展示
  * attachment: 指定浏览器将包体以附件的方式下载
     例如:  Content-Disposition: attachment
     例如: Content-Disposition: attachment; filename="filename.jpg"
  *multipart/form-data类型应答中,可以用于子消息体部分
Content-Disposition: form-data; name="fieldName"; filename="filename.jpg"

测试:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#! /usr/bin/python
# -*- coding: utf-8 -*-
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ("127.0.0.1", 12345)
sock.bind(server_address)
sock.listen(100) #表示我们最多可以处理100个连接

while True:
  conn, client_address = sock.accept()
  try:
    data = conn.recv(4096) #接收4096字节的请求
    response = 'HTTP/1.1 200 OK\r\nContent-Disposition: attachment; filename="filename.jpg"\r\nContent-Length: 10\r\n\r\nHelloWorld'
    conn.send(response.encode())
  finally:
    conn.close()

5.3 MIME

大小写不敏感,但通常是小写,例如: Content-type: text/plain; charset=“us-ascii”

1
2
3
4
5
6
7
8
# http://www.iana.org/assignments/media-types/media-types.xhtml
Content-Type: type / subtype *(; parameter)
  * type = discrete-type / composite-type
    * discrete-type(离散类型) = "text" / "image" / "audio" / "video" / "application" / extension-token
    * composite-type := "message" / "multipart" / extension-token
    * extension-token := ietf-token / x-token
  * subtype := extension-token / iana-token
  * parameter := attribute = value

5.4 form表单

  1. FORM表单常见控件有: ① Text Input Controls: 文本输入控件 ② Checkboxes Controls: 复选框控件 ③ Radio Box Controls: 单选按钮控件 ④ Select Box Controls: 下拉列表控件 ⑤ File Select boxes: 选取文件控件 ⑥ Clickable Buttons: 可点击的按钮控件 ⑦ Submit and Reset Button: 提交或者重置按钮控件

  2. FORM表单提交请求时的关键属性

  • action: 提交时发起HTTP请求的URI
  • method: 提交时发起HTTP请求的http方法 GET: 通过URI,将表单数据以URI参数的方式提交 POST: 将表单数据放在请求包体中提交
  • enctype: 在POST方法下,对表单内容在请求包体中的编码方式
    • application/x-www-form-urlencoded 数据被编码成以’&‘分隔的键-值对,同时以’=‘分隔键和值,字符以URL编码方式编码
    • multipart/form-data boundary分隔符 每部分表述皆为HTTP头部描述子包体,例如Content-Type last boundary结尾
  1. multipart(RFC1521)的abnf定义
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 头部定义
* Content-Type头部指明这是一个多表述包体
    Content-Type: multipart/form-data; boundary=---WebKitFormBoundaryRRJKeWfHPGrS4LKe
* Boundary分隔符的格式
    boundary := 0*69<bchars> bcharsnospace
        bchars := bcharsnospace / " "
        bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" / "+" / "_" / "," / "-" / "." / "/" / ":" / "=" / "?"

# 请求体
* multipart-body = 1*encapsulation close-delimiter
    * encapsulation = delimiter body-part CRLF
       * delimiter = "--" boundary CRLF
       * body-part = *(field) *(CRLF *text)
           * field = field-name ":" [field-value] CRLF
             例如:
                * content-disposition: form-data; name="xxxxxx"
                * content-type 头部指明该部分包体的类型
    * close-delimiter = "--" boundary "--" CRLF

附录:评估Web架构的七大关键属性

  1. HTTP协议应当在以下属性中取得可接受的均衡:

(1) 性能Performance: 影响高可用的关键因素
(2) 可伸缩性Scalability: 支持部署可以相互交互的大量组件
(3) 简单性Simplicity: 易理解、易实现、易验证
(4) 可见性Visiable: 对两个组件间的交互进行监视或者仲裁的能力。如缓存、分层设计等
(5) 可移植性Portability: 在不同的环境下运行的能力
(6) 可靠性Reliability: 出现部分故障时,对整理影响的程度
(7) 可修改性Modifiability: 对系统作出修改的难易程度,由可进化性、可定制性、可扩展性、可配置性、可重用性构成

  1. 架构性能:性能
  • 网络性能Network Performance
    • Throughput吞吐量:小于等于带宽bandwidth
    • Overhead开销:首次开销,每次开销
  • 用户感知到的性能User-perceived Performance
    • Latency延迟:发起请求到接收到响应的时间
    • Completion完成时间:完成一个应用动作所花费的时间
  • 网络效率Network Efficiency
    • 重用缓存、减少交互次数、数据传输距离更近、COD
  1. 架构属性:可修改性
  • 可进化性Evolvability: 一个组件独立升级而不影响其他组件
  • 可扩展性Extensibility:向系统添加功能,而不会影响到系统的其他部分
  • 可定制性Customizability:临时性、定制性地更改某一要素来提供服务,不对常规客户产生影响
  • 可配置性Configurability:应用部署后可通过修改配置提供新的功能
  • 可重用性Reusabilit:组件可以不做修改在其他应用在使用时
  1. 通过上面的架构属性可以推导出下面的REST架构:
                   http
                    /---->(-O){Proxy}($-)--http--(-O){Gateway}($-)--http--(-O)[OriginServer]
                  a/                     \      /
                  /                       \    /
                 /                         DNS
                /
{UserAgent}($-) ----b----------------------------------------------http---(-$)[OriginServer]
             |  \
            DNS  \
                 c\-------(-$){Proxy}------------------------------wais-------[DB]

ClientConnector: (O-)   Client+Cache: ($-)   ServerConnector: (-O)   ServerCache: (-$)
  1. 五种架构风格

在分布式的web架构中呢,主要有5类架构风格,那什么叫做架构风格呢?我们在日常编程中,可能会有习惯用法,

在高级语言面向对象编程中,可能会有设计模式,再往上呢,更大粒度的抽象,我们可以用到架构。

而架构风格呢,是指某一类架构它具有相同的或者说类似的一些约束,我们把它归纳为一类架构风格。

(1) 数据流风格Data-flow Syles

优点:简单性、可进化性、可扩展性、可配置性、可重用性

管道与过滤器Pipe And Filter, PF
  * PF: 每个Filter都有输入端和输出端,只能从输入端读取数据,处理后再从输出端产生数据
  * UPF: 统一接口的管道与过滤器Uniform Pipe And Filter, UPF
    在PF上增加了统一接口的约束,所有Filter过滤器必须具备同样的接口

Style         : PF      UPF       <br>
Derivation    :         PF        <br>
Net Perform   :         -         <br>
UP Perform    : ±       ±       <br>
Efficiency    :                   <br>
Scalability   :                   <br>
Simplicity    : +       ++        统一接口以后它的简单性有了很大的提升,它的每个模块呢,每个Filter,都可以独立去升级<br>
Evolvability  : +       +         <br>
Extensibility : +       +         我们再增加一个协议层,或者再增加一个模块,都是很容易的,所以扩展性非常好<br>
Customiz.     :                   <br>
Configur.     : +       ++        我们可以通过配置文件来增加我们的filter,或者说我们的协议层级<br>
Reusability   : +       ++        而在我们的可重用性上也是非常好的,每个filter我们可以独立拿出来以后,给新的架构去使用<br>
Visibility    :         +         <br>
Portability   :                   <br>
Reliability   :                   <br>

(2) 复制风格Replication Styles
优点:用户可察觉的性能、可扩展性,网络效率、可靠性都会得到提升

复制风格中主要有两类架构:
  * RR: 复制仓库Replicated Repository
        多个进程提供相同的服务,通过反向代理对外提供集中服务
        这种复制风格是非常常见的,比如mysql的冷热备份,我们很多的WebApplication,同时提供很多服务
  * 缓存: $
        RR的变体,通过复制请求的结果,为后续请求复用

Style         : RR      $       <br>
Derivation    :         RR      <br>
Net Perform   :                 <br>
UP Perform    : ++      +       对于用户可以察觉的网络性能,RR和$都有所提升<br>
Efficiency    :         +       直接减少了网络的传输流量,所以缓存架构在网络效率上会更高些<br>
Scalability   : +       +       可伸缩性,因为有很多可以复制的进程或者可以复制的数据,也会导致我们的Scalability性能更好<br>
Simplicity    :         +       在简单性上,缓存风格的表现也会更好些<br>
Evolvability  :                 <br>
Extensibility :                 <br>
Customiz.     :                 <br>
Configur.     :                 <br>
Reusability   :                 <br>
Visibility    :                 <br>
Portability   :                 <br>
Reliability   : +               因为有多个进程也就是说多个服务,所以RR的可靠性也会更好些<br>

(3) 分层风格Hierarchical Styles

优点:简单性、可进化性、可伸缩性

* CS: 客户端服务器Client-Server, CS
    由Client触发请求,Server监听到请求后产生响应,Client一直等待收到响应后,会话结束
* LS: 分层系统Layered Syste, LS
    每一层为其之上的层服务,并使用在其之上的层所提供的服务,例如TCP/IP
* LCS: 分层客户端服务器Layered Client-Server, LCS
    LS+CS,例如正向代理和反向代理,从空间上分为外部层和内部层
    正向代理,它自然会把客户端和我们的网络环境分离
    反向代理,它自然会把企业内网和企业外面的Internet网络分离
* CSS: 无状态、客户端服务器Client-Stateless-Server CSS
    基于CS,服务器上不允许有session state会话状态
    也就是说,我们不应该期待同一个客户端向服务器发来的请求,第二个请求必须依赖上一个请求中的传输的数据,才可以服务第二个请求。
    提升了可见性、可伸缩性、可靠性,但重复数据导致降低网络性能
    但是因为每个请求都需要携带完整的数据,所以就导致我们的网络性能下降,其实现在http2.0到http1.1之间的很多的升级都体现在这几个方面,
        比如我们http2.0中,不希望重复的传递http的头部,因为http的头部,特别是cookie,占用了大量的网络资源,
        就是因为http2.0,它已经做不到无状态了,所以它的重复数据相对比较少,网络性能更高,
        但是可见性、可伸缩性和可靠性上,http2.0就相对要差些。
* C$SS: 缓存、无状态、客户端服务器Client-Cache-Stateless-Server C$SS

* LC$SS: 分层、缓存、无状态、客户端服务器Layerd-Client-Cache-Stateless-Server,LC$SS

* RS: 远程会话Remote Session, RS
     Rest架构没有采用
     CS变体,服务器保存Application state应用状态
     比如,FTP,FTP每次发送一个请求的时候,需要知道之前你发出来的请求导致现在这个客户是在哪一层目录,
     它之前是发送了哪些端口等等,这样的一些信息,所以它其实是需要服务器来保存Application state的

* RDA: 远程数据访问Remote Data Access, RDA
     Rest架构没有采用
     CS变体,Application state应用状态同时分布在客户端和服务器
     巨大的数据集有可能通过迭代而减少
     比如,sql中的游标,我们在使用sql语言来查询数据的时候,因为我们查询的数据,可能会包含百万行的数据,
     那么我们就可以使用游标,反复的一页一页的去获取数据,也就是客户端保存了当前在第几页的状态,
     而服务器端也需要为游标保存相应的数据。它的好处是巨大的数据集有可能通过迭代而减少,但是它的简单性、
     可伸缩性是非常差的,所以传统的关系型数据库,很少能够支撑这种高可用性的,基于数据仓库的这种多个热备份的分布式架构,是很难达到的。

Style         : CS      LS      LCS      CSS      C$SS     LC$SS    RS     RDA<br>
Derivation    :                 CS+LS    CS       CSS+$    LCS+C$SS CS     CS <br>
Net Perform   :                          -        -        -                  <br>
UP Perform    :         -       -                 +        ±                 <br>
Efficiency    :                                   +        +        +      +  <br>
Scalability   : +       +       ++       ++       ++       +++      -      -  <br>
Simplicity    : +               +        +        +        ++       +      -  <br>
Evolvability  : +       +       ++       +        +        ++       +         <br>
Extensibility :                                                               <br>
Customiz.     :                                                               <br>
Configur.     :                                                               <br>
Reusability   :         +       +                          +                  <br>
Visibility    :                          +        +        +        -      +  <br>
Portability   :         +       +                          +                  <br>
Reliability   :                          +        +        +               -  <br>

(4) 移动代码风格Mobile Code Styles

优点:可移植性、可扩展性、网络效率

* VM: 虚拟机Virtual Machine, VM
    分离指令与实现

* REV: 远程求值Remote Evaluation,  REV
    基于CS的VM,将代码发送至服务器执行
    所以,Rest架构下,我们的Web环境下是没有办法使用REV架构的,因为客户端的代码可能充满了这样恶意的代码,风险是非常大的。

* COD: 按需代码Code on Demand, COD
    服务器在响应中发回处理代码,在客户端执行
    优秀的可伸展性和可配置性,提升用户可察觉性能的网络效率
    Javascript使用的就是COD模型

* LCODC$SS: 分层、按需代码、缓存、无状态、客户端服务器
            Layered-Code-on-Demand-Client-Cache-Stateless-Server, LCODC$SS
            LC$SS+COD   L[COD]C$SS

* MA: 移动代理Mobile Agent,MA
    相当于REV+COD

Style         :  VM     REV    COD     LCODC$SS   MA      <br>
Derivation    :         CS+VM  CS+VM   LC$SS+COD  REV+COD <br>
Net Perform   :                        -                  <br>
UP Perform    :                +       ++         +       <br>
Efficiency    :         +      +       ++         ++      <br>
Scalability   :         -      +       +4+                <br>
Simplicity    :  ±      ±      ±       +±+        ±  <br>
Evolvability  :                        ++                 <br>
Extensibility :  +      +      +       +          ++      <br>
Customiz.     :         +                         +       <br>
Configur.     :                +       +          +       <br>
Reusability   :                        +                  <br>
Visibility    :  -      -      -       ±          -      <br>
Portability   :  +      +              +          +       <br>
Reliability   :         -              +                  <br>

(5) 点对点风格Peer-to-Peer Styles(Rest架构没有使用,但是分布式网络中经常使用)

优点:可进化性、可重用性、可扩展性、可配置性

* EBI: Event-based Integration
   基于事件集成系统,如由类似Kafka这样的消息系统 + 分发订阅来消除耦合
   优秀的可重用性、可扩展性、可进化性
   缺乏可理解性
   由于消息广播等因素造成的消息风暴,可伸缩性差

* Chiron-2,C2
   参见论文《A Component-and Message-Based Architectural Style for GUI Software》
       相当于EBI+LCS,控制了消息的方向

* Distributed Objects, DO
   组件结对交互

* Brokered Distributed Objects, BDO
   引入名字解析组件来简化DO,例如CORBA

Style         : EBI         C2          DO             BDO     <br>
Derivation    :             EBI+LCS     CS+CS          DO+LCS  <br>
Net Perform   :                         -              -       <br>
UP Perform    :             -                          -       <br>
Efficiency    : +           +           +                      <br>
Scalability   : --                                             <br>
Simplicity    : ±           +                                 <br>
Evolvability  : +           ++          +              ++      <br>
Extensibility : +           +           +              +       <br>
Customiz.     :                                                <br>
Configur.     : +           +           +              +       <br>
Reusability   : +           ++          +              ++      <br>
Visibility    : -           ±           -              -      <br>
Portability   :             +                          +       <br>
Reliability   : -           ±           -                     <br>
  1. 风格演化
                               |
     +----------+--------------+-----------------+--------------+
     |          |              |                 |              |
replicated  separated       layered    programmable       uniform interface
     |          |              |                 |              |
   (RR)        (CS)----+--+   (LS)              (VM)           (U)
     |          |      |  |    |                 |              |
on-demand   stateless  |  +----+----mobile------>|           simple
     |          | intermediate |                 |           visible
    ($)        (CSS)   +--->--(LCS)             (COD)           |
      \         |              |                 |              |
    cacheable  reliable     shared           extensible     reusable
         \      |              |                 |              |
          ------|              |                 |              |
               (C$SS)-scalable>(LC$SS)-multiorg>(LCODC$SS)---->(REST)

本文发表于 0001-01-01,最后修改于 0001-01-01。

本站永久域名「 jiavvc.top 」,也可搜索「 后浪笔记一零二四 」找到我。


上一篇 « 下一篇 »

赞赏支持

请我吃鸡腿 =^_^=

i ysf

云闪付

i wechat

微信

推荐阅读

Big Image