定义一个协议的时候,会遇到这样的问题:协议的数据段之间分隔符是什么,字段之间可以用什么字符分隔,空白符可以连续多少个,所以需要一门元语言来准确描述这些规则。
例如,在一个HTTP请求中, HOST: 后面是否可以连续有多个空格?在计算机科学中,ABNF(augmented Backus–Naur form,巴科斯范式)是我们经常遇到的一种的规则定义语言,广泛存在于各种标准文档中。
ABNF规范
ABNF范式由一组派生规则组成:
rule = definition ; comment CRLF
-
rule
大小写不敏感的非终结符 -
definition
定义规则的一串符号 -
comment
用于注释 -
CRLF
回车换行,用于结束
最终值
最终值由一个或多个数值字符指定。
- 数值字符的组成:
%base + value
- base, 即基数:
%b 二进制
%d 十进制
%x 十六进制
- “.”表示串联
例如:
%d13.10 表示回车换行
操作符
- 空白符
空白字符被用来分隔定义中的各个元素.WSP 为单个空白符,LWSP 为包含换行符的零个或者多个空白字符
- 注释;
; comment
注释以 ; 开始,到行尾结束。
- 串联
Rule1 Rule2
规则由规则名的序列定义。
foo = %x61 ; a
bar = %x62 ; b
number = foo bar foo
则number表示aba
- 选择
Rule1 / Rule2
规则由被"/"分隔的规则序列定义。
fubar = fu / bar
选择 fu规则 或者 bar规则
- 增量选择
Rule1 =/ Rule2
增量选择用于向一个规则增加补充选择。
ruleset = alt1 / alt2 / alt3 / alt4 / alt5
等价于
ruleset = alt1 / alt2
ruleset =/ alt3
ruleset =/ alt4 / alt5
- 值范围%c##-##
%c##-##
数值范围可以通过使用连字符(“-”)来指定。
OCTAL = "0" / "1" / "2" / "3" / "4" / "5" / "6" / "7"
等价于
OCTAL = %x30-37
- 序列组合
(Rule1 Rule2)
被括号包围起来的一组规则,被视为单个元素。
group = a (b / c) d
表示匹配 “a b d” 或者 “a c d”。
- 不定量重复m*n
m*nRule
m: 元素的最小数量
n: 元素的最大数量,默认为无限大
- 定量重复n
nRule
Rule的数量为n
- 可选序列
[Rule]
表示可选元素。
核心规则
Rule | Formal definition | Meaning |
---|---|---|
ALPHA | %x41–5A / %x61–7A | Upper- and lower-case ASCII letters (A–Z, a–z) |
DIGIT | %x30–39 | Decimal digits (0–9) |
HEXDIG | DIGIT / “A” / “B” / “C” / “D” / “E” / “F” | Hexadecimal digits (0–9, A–F, a-f) |
DQUOTE | %x22 | Double quote |
SP | %x20 | Space |
HTAB | %x09 | Horizontal tab |
WSP | SP / HTAB | Space and horizontal tab |
LWSP | *(WSP / CRLF WSP) | Linear white space (past newline) |
VCHAR | %x21–7E | Visible (printing) characters |
CHAR | %x01–7F | Any ASCII character, excluding NUL |
OCTET | %x00–FF | 8 bits of data |
CTL | %x00–1F / %x7F | Controls |
CR | %x0D | Carriage return |
LF | %x0A | Linefeed |
CRLF | CR LF | Internet-standard newline |
BIT | “0” / “1” | Binary digit |
HTTP协议的描述
HTTP-message = start-line *( header-field 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
请求和响应都可以携带包体,定长包体的传输需要使用Content-Length头部指明包体长度,不定长包体需要指明Transfer-Encoding。含有Transfer-Encoding头部后Content-Length头部应该被忽略。
- Transfer-Encoding = 1#transfer-coding
- transfer-coding = "chunked" / "compress" /"deflate" / "gzip" / transfer-extension
- transfer-extension = token *( OWS ";" OWS transfer-parameter )
transfer-parameter = token BWS "=" BWS ( token / quoted-string )
分块传输编码:
Transfer-Encoding: chunked
- chunked-body = *chunk
last-chunk
trailer-part
CRLF
- chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
- chunk-size = 1*HEXDIG
- chunk-data = 1*OCTET
- last-chunk = 1*("0") [ chunk-ext ] CRLF
- trailer-part = *( header-field CRLF )
我们来看看一个具体的http报文,这些定义都对应于哪些内容。
GET /hello.txt HTTP/1.1
User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
Host: www.example.com
Accept-Language: en, mi
HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
ETag: "34aa387-d-1568eb00"
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain
Hello World! My payload includes a trailing CRLF.
request-line:
GET /hello.txt HTTP/1.1
status-line:
HTTP/1.1 200 OK
header-field:
Host: www.example.com
Server: Apache
Date: Mon, 27 Jul 2009 12:28:53 GMT
message-body:
Hello World! My payload includes a trailing CRLF.