2 符号习惯和一般语法
2.1 扩充的BNF(扩充的 巴科斯-诺尔范式)
本文档规定的所有机制都用两种方法描述:散文体(prose)和类似于RFC 822 的扩充Backus-Naur Form(BNF)。要理解本规范,使用者需熟悉符号表示法。扩充BNF结构如下:
名字(name)=定义(definition)
名字(name)就是代表规则的名字,规则名里不能包含“<”和“>”,通过等号把规则名和规则定义(definiation)分离开。空格只有在采用延续行缩进来指定跨度多于一行的规则定义的时候才有意义。某些基本规则(basic rules)使用大写字母包含在规则定义里, 如SP,LWS,HT,CRLF,DIGIT,ALPHA,等等。尖括号可以包含在规则定义里,只要它们的存在有利于区分规则名的使用。
“字面文本”(“literal”)
字面文本(literal text)两边用引号。除非声明,字面文本大小写不敏感(译注:如,HEX ="A" | "B" | "C" | "D" | "E" | "F" | "a" | "b" | "c" | "d" | "e" | "f" | DIGIT 里的A,B,C,D等等都是字面文本(literal text))。
规则1 | 规则2
由竖线(“|”)分开的元素是可选的,例如,“yes | no”表示yes或no都是可接受的。
(规则1 规则2)
围在括号里的多个元素视作一个元素。所以,“(elem (foo | bar) elem)”符合的字符串是“elem foo elem”和“elem bar elem”。 实际上是两个元素,但是用一个都足以表达了。
*规则
前面的字符“*”表示重复。完整的形式是“<n>*<m>元素”,表示元素至少出现<n>次,至多出现<m>次。默认值是0和无穷大,所以"*(元素)"允许任何数值,包括零;"1*元素"至少出现一次;"1*2element"允许出现一次或两次。
[规则]
方括号里是任选元素;“[foo bar]”相当于“*1(foo bar)”。选其中一个,且必须选一个。
N 规则
特殊的重复:“<n>(元素)”与“<n>*<n>(元素)”等价;就是说,(元素)正好出现<n>次。这样2DIGIT是一个两位数字,3ALPHA是一个由三个字符组成的字符串。
#规则
类似于“*”,结构“#”是用来定义一系列元素的。完整的形式是<n>#<m>元素,表示至少<n>个
元素,至多<m>个元素,(*表示重复出现多少次)元素之间被一个或多个逗号(“,”)以及可选的线性空白(LWS)隔
开了。这就使得表示列表这样的形式变得非常容易;像(*LWS element *(*LWS ","*LWS element))
就可以表示为1#element无论在哪里使用这个结构,空元素都是允许的,但是不计入元素出现的次数。换句话说 ,“(element ), , (element) ”是允许的,但是仅仅视为两个元素。因此,在至少需要一个元素的地方,必须存在至少一个非空元素。默认值是0和无穷大,这样,“#element”允许任意零个或多个元素;“1# element”需要至少一个;“1#2element”允许一个或两个元素。
注释(comment)
用分号引导注释。
隐含的*LWS
本规范所描述的语法是基于字(word-based)的。除非特别注明,线性空白(LWS)可以出现在任何两个相邻字之间(标记(token)或引用字符串(quoted-string)),以及相邻字和间隔符之间,但是这并没有改变对一个域的解释。任何两个标记(token)之间必须有至少一个分割符,否则将会被理解为只是一个标记。
2.2基本规则 (basic rule)
下面的规则贯穿于本规范的全文,此规则描述了基本的解析结构。US-ASCII(美国信息交换标准码)编码字符集是由ANSI X3.4-1986[21]定义的。
OCTET(字节) = <任意八比特的数据序列>
CHAR = <任意ASCII字符(ascii码值从 0到127的字节)>
UPALPHA = <任意大写字母"A"..."Z">
LOALPHA = <任意小写字母"a"..."z">
ALPHA = UPALPHA | LOALPHA
DIGIT = <任意数字0,1,...9>
CTL = <任意控制字符(ascii码值从0 到 31的字节)及删除键DEL(127>
CR = <US-ASCII CR, 回车(13)>
LF = <US-ASCII LF, 换行符(10)>
SP = <US-ASCII SP, 空格(32)>
HT = <US-ASCII HT, 水平制表 (9)>
<"> = <US-ASCII双引号(34)>
HTTP/1.1 将 CR LF 的序列定义为任何协议元素的行尾标志,但这个规定对实体主体
(endtity-body)除外(要求比较松的应用见附录19.3)。实体主体(entity-body)的行尾标志
是由其相应的媒体类型定义的,如3.7节所述。
CRLF = CR LF
HTTP/1.1 的消息头域值可以折叠成多行,但紧接着的被折叠行由空格(SP)或水平制表(HT)折叠标记开始。所有的线性空白(LWS)包括被折叠行的折叠标记(空格SP或水平制表键HT),具有同SP一样的语义。接收者在解析域值并且将消息转送到下游(downstream)之前可能会将任何线性空白(LWS)替换成单个SP(空格)。
LWS = [CRLF] 1*(SP | HT)
下面的TEXT规则仅仅适用于头域内容和值的描述,不会被消息解释器解析。TEXT里的字可以包含不仅仅是ISO-8859-1[22]里的字符集,也可以包含RFC 2047里规定的字符集。
TEXT = <除CTLs以外的任意OCTET,但包括LWS>
一个CRLF只有作为HTTP消息头域延续的一部分时才在TEXT定义里使用。
十六进制数字字符用在多个协议元素(protocol element)里。
HEX = "A" | "B" | "C" | "D" | "E" | "F"
| "a" | "b" | "c" | "d" | "e" | "f" | DIGIT
许多HTTP/1.1的消息头域值是由LWS或特殊字符分隔的字构成的。这些特殊字符必须先被包
含在引用字符串(quoted string)里之后才能用于参数值(如3.6节定义)里。
token (标记) = 1*<除CTLs与分割符以外的任意CHAR >
separators(分割符) = "(" | ")" | "<" | ">" | "@"
| "," | ";" | ":" | "\" | <">
| "/" | "[" | "]" | "?" | "="
| "{" | "}" | SP | HT
通过用圆括号括起来,注释(comment)可以包含在一些HTTP头域里。注释只能被包含在域
值定义里有“comment”的域里。在其他域里,圆括号被视作域值的一部分。
comment (注释)= "(" *(ctext | quoted-pair | comment )” )"
ctext = <除"(" 和 ")"以外的任意TEXT >
如果一个TEXT若被包含在双引号里,则当作一个字。
quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
qdtext = <any TEXT except <">>
斜划线("\")可以被作为单字符的引用机制,但是必须要在quoted-string和comment构造之
内。
quoted-pair = "\" CHAR
3 协议参数
3.1 HTTP版本
HTTP使用一个“<major>.<minor>”数字模式来指明协议的版本号。为了进一步的理解HTTP通信,协议的版本号指示了发送端指明消息的格式和能力,而不仅仅是通过双方通信而获得的通信特性。当消息元素的增加不会影响通信行为或扩展了域值时,协议版本是不需要修改的。当协议会因为添加一些特征而做了修改时,<minor>数字就会递增。这些修改不会影响通常的消息解析算法,但它会给消息添加额外的语意(semantic)并且会暗示发送者具有额外的能力。协议的消息格式发生变化时,<major>数字就会增加。HTTP消息的版本在HTTP-Version域被指明,HTTP-Version域在消息的第一行中。
注意major和minor数字必须被看成两个独立整数,每个整数都可以递增,并且可以增大到大于一位数的整数,如HTTP/2.4比HTTP/2.13低,而HTTP/2.4又比HTTP/12.3低。前导0必须被接收者忽略并且不能被发送者发送。
网关或代理服务器版本不能容忍用户端发送过来的高版本的协议的时候,就会触发一个错误。
3.2 通用资源标识符(URI)
URIs有许多名字已为人所知:WWW地址,通用文档标识符,通用资源标识符[3],以及后来的统一资源定位器(URL)[4]和统一资源名称(URN)[20]。就HTTP而言,通用资源标识符(URI)只是简单的格式化字符串---通过名称,位置,或其它特征---识别一个资源。
根据使用的背景,HTTP里的URIs可以表示成绝对(absoulute)形式或相对形式(相对URI基于根URI[11])。两种形式的区别是根据这样的事实:绝对URI总是以一个模式(scheme)名作为开头,其后是一个冒号。关于URL 更详尽的语法和含义请参看“统一资源标识符
(URI):一般语法和语义”,RFC 2396 [42](代替了RFCs 1738 [4]和RFC 1808 [11])。本规范采用了RFC 2396 里的” URIreference”, "absoluteURI" , "relativeURI" , "port" , "host" , "abs_path" , "rel_path",和"authority"的定义格式。
HTTP协议不对URI的长度作事先的限制,服务器必须能够处理任何他们提供资源的URI,并且应该能够处理无限长度的URIs,这种无效长度的URL可能会在客户端以基于GET方式的请求时产生。如果服务器不能处理太长的URI的时候,服务器应该返回414状态码(此状态码代表Request-URI太长)。
在HTTP协议里,http模式(http scheme)被用于定位网络资源(resourse)的位置。本节定义了http URLs这种特定模式(scheme)的语法和语义。
http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
如果端口为空或未给出,就假定为80。它的语义即:已识别的资源存放于正在监听tcp连接的那个端口的服务器上,并且请求资源的的Request-UR为绝对路径(5.1.2节)。无论什么可能的时候,URL 里使用IP 地址都是应该避免的(参看RFC 1900 [24])。如果绝对地址(abs_path)没有出现在URL 里,那么应该给出"/"。如果代理(proxy)收到一个主机(host)名,但是这个主机名不是全称域名(fully quanlified domain name),则代理应该把它的域名加到主机名上。如果代理(proxy)接收了一个全称域名,代理不能改变主机(host)名称。
3.2.3 URI 比较
当比较两个URI是否匹配时,客户应该对整个URI比较时应该区分大小写,并且一个字节一
个字节的比较。 但下面有些特殊情况:
- 一个为空或未给定的端口等同于URI-refernece(见RFC 2396)里的默认端口;
- 主机(host)名的比较必须不区分大小写;
- 模式(scheme)名的比较必须是不区分大小写的;
- 一个空绝对路径(abs_path)等同于"/"。
除了“保留(reserved)”和“不安全(unsafe)”字符集里的字符(参见RFC 2396[42]) ,其它字符和它们的"%HEXHEX"编码的效果一样。
例如,以下三个URI是等同的:
http://abc.com:80/~smith/home.html
http://ABC.com/%7Esmith/home.html
http://ABC.com:/%7esmith/home.html
3.3 日期/时间格式(Date/Time Formats)
3.3.1 完整日期 (Full Date)
HTTP应用曾经一直允许三种不同日期/时间格式:
Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
第一种格式是作为Internet 标准提出来的,它是一个国定长度的,由RFC 1123 [8](RFC822[9]的升级版本)定义的一个子集。第二种格式使用比较普遍,但是基于废弃的RFC 850[12]协议,并且没有年份。如果HTTP/1.1客户端和服务器要解析日期,他们必须能接收所有三种格式(为了兼容HTTP/1.0),但是它们只能用RFC 1123 里定义的日期格式来填充头域(header field)的值里用到HTTP-date的地方。
注:日期值的接收者被鼓励能可靠地接收来自于非HTTP应用程序发送的的日期值,例如有时
可以通过代理(proxy)/网关(gateway)向SMTP或NNTP获取或提交消息。
所有的HTTP日期/时间都必须以格林威治时间(GMT)表示。对HTTP而言,GMT完全等同于UTC(世界协调时间)。前两种日期/时间格式里包含“GMT”,它是时区的三个字面的简写,并且当读到一个asctime格式时必须先被假定是GMT时间。HTTP日期(HTTP-date)区分大小写,不能在此语法中除SP之外包含一个多余的LWS。
HTTP-date = rfc1123-date | rfc850-date | asctime-date
rfc1123-date = wkday "," SP date1 SP time SP "GMT"
rfc850-date = weekday "," SP date2 SP time SP "GMT"
asctime-date = wkday SP date3 SP time SP 4DIGIT
date1 = 2DIGIT SP month SP 4DIGIT
; day month year (e.g., 02 Jun 1982)
date2 = 2DIGIT "-" month "-" 2DIGIT
; day-month-year (e.g., 02-Jun-82)
date3 = month SP ( 2DIGIT | ( SP 1DIGIT ))
; month day (e.g., Jun 2)
time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
; 00:00:00 - 23:59:59
wkday = "Mon" | "Tue" | "Wed"
| "Thu" | "Fri" | "Sat" | "Sun"
weekday = "Monday" | "Tuesday" | "Wednesday"month = "Jan" | "Feb" | "Mar" | "Apr"
| "May" | "Jun" | "Jul" | "Aug"
| "Sep" | "Oct" | "Nov" | "Dec"
注意:HTTP对日期/时间格式的要求仅仅应用在协议流的使用。客户和服务器不必把这种格式应用于用户呈现(user presentation),请求记录日志,等等。.
3.3.2 Delta Seconds (秒间隔)
一些HTTP头域(header field)允许时间值以秒为单位,以十进制整数表示,此值代表消息接收后的时间。
delta-seconds = 1*DIGIT
3.4 字符集 (Character Sets)
HTTP使用术语“字符集”的定义,这和MIME中所描述的是一样.
本文档中的术语“字符集”涉及到一种方法,此方法是用单个或多个表将一个字节序列转换成一个字符序列(译注:从这里来看,这应该是一种映射关系,表保存了映射关系)。注意在反方向上无条件的转换是不成立的,因为并不是所有的字符都能在一个给定的字符集里得到,一个字符集里可能提供多个字节序列表征一个特定的字符。这个定义为的是允许不同种类的字符编码从单一简单表映射(如US-ASCII)到复杂表的转换方法,例如利用ISO-2022技术。然而,相关于MIME字符集名字的定义必须要充分指定从字节到字符的映射。特别是利用外部外围信息来精确确定映射是不允许的.
注:这里使用的术语“字符集”一般的被称作一种“字符编码”。不过既然HTTP和MIME在
同一机构注册,术语统一是很重要的。
HTTP字符集的标记(token)是用不区分大小写的。所有的标记由IANA字符集注册机构[19]定
义。
charset = token
尽管HTTP允许用任意标记(token)作为字符集(charset)值,但这个标记已经在IANA字符集注册机构注册过了,那么这个标记必须代表在该注册机构定义的字符集。对那些非IANA定义的字符集,应用程序应该限制使用。
HTTP协议的实现者应该注意IETF字符集的要求[38][41].
3.4.1 丢失字符集(Missing Charset)
一些HTTP/1.0 应用程序当他们解析Content-Type 头时,当发现没有字符集参数(charsetparameter,译注: Content-Type: text/plain; charset=UTF-8,此时charset=UTF-8就是字符集参数)可用时,这意味着接收者必须猜测实体主体(entity body)的字符集到底是什么。如果
发送者希望避免这种情况,他应该在Content-Type头域里包含一个字符集参数,即使字符集是ISO-8859-1的也应该指明,这样就不会让接收者产生混淆。
不幸的是,一些旧的HTTP/1.0客户端不能处理在Content-Type头域里明确指定的字符集参数。HTTP/1.1接收端必须要认真对待发送者提供的字符集;并且当用户代理(user agent,译注:如浏览器)开始呈现一个文档时,虽然用户代理可以猜测文档的字符集,但如果content-type头域里提供了字符集,并且用户代理也支持这种字符集的显示,不管用户代理是否愿意,它必须要利用这种字符集。参见3.7.1节。
3.5 内容编码(Content Codings)
内容编码(content coding)的值表示一种曾经或能被应用于一个实体的编码转换(encodingtransformation)。内容编码主要用于文档的压缩或其它有效的变换,但这种变换必须不能丢失文档的媒体类型的特性,并且不能丢失文档的信息(译注:就像有损压缩和无损压缩,前者不会丢失信息,后者会丢失信息)。实体经常被编码后保存,然后传送出去,并且在接收端被解码。
content-coding = token
所有内容编码(content-coding)的值是不区分大小写的。HTTP/1.1在接受译码 (Accept-Encoding,14.3 节)和内容译码(Content-Encoding)(14.11 节)头域里使用内容编码(content-coding)的值。尽管该值描述了内容编码,更重要的是它指出了一种解码机制,利用
这种机制对实体的编码进行解码。网络分配数字权威( (IANA)充当内容编码的值标记(token)注册机构。最初,注册表里包
含下列标记:
gzip(压缩程序)
一种由文件压缩程序"gzip"(GNU zip)产生的编码格式(在RFC 1952中描述)。这种编码格式是一种具有32位CRC的Lempel-Ziv编码(LZ77)。
compress(压缩)
一种由UNIX文件压缩程序"compress"产生的编码格式。这种编码格式是一种具有可适应性的Lempel-Ziv-Welch编码(LZW)。对于将来的编码,用程序名识表征编码格式是不可取。在这里用到他们是因为他们在历史的作用,虽然这样做并不好。为了同以前的HTTP 实现相兼容,应用程序应该将"x-gzip"和"xcompress"分别等同于"gzip"和"compress"。
deflate(缩小)
deflate编码是由RFC 1950 [31]定义的"zlib"编码格式与RFC 1951 [29]里描述的"deflate"压缩
机制的组合的产物。
identity(一致性)
Identity是缺省编码;指明这种编码表明不进行任何编码转换。这种内容编码仅被用于接受译码(Accept-Encoding)头域里,但不能被用在内容译码(Content-Encoding)头域里。.
新的内容编码的值标记(token)应该被注册;为了实现客户和服务器间的互操作性,实现新值的内容编码算法规范应该能公开利用并且能独立实现,并且与本节中被定义的内容编码目的
3.6 传输编码 (Transfer Codings)
3.6.1 块传输编码(Chunked Transfer Coding)
块编码(chunked encoding)改变消息主体使消息主体(message body)成块发送。每一个块有它自己的大小(size)指示器,在所有的块之后会紧接着一个可选的包含实体头域的尾部(trailer)。这种编码允许发送端能动态生成内容,并能携带能让接收端判断消息是否接收完整
的有用信息。
3.7 媒体类型(Media Type)
为了提供开放的,可扩展的数据类型和类型协商,HTTP在Content-Type(14.17节)实体头域和Accept请求头域里利用了网络媒体[17]类型。
media-type = type "/" subtype *( ";" parameter )
type = token
subtype = token
参数(parameter)以一种 属性/值(attribute/value)形式(如3.6节定义)跟随 类型/子类型(type/subtype)。
类型(type),子类型(subtype),和参数(parameter)里属性名称是大小写不敏感的。参数值有可能是大小写敏感的,也可能不是,这根据参数里属性名称的语意。线性空白(LWS)不能被用于类型(type)和子类型(subtype)之间,也不能用于参数的属性和值之间。参数的出现或不出现对处理媒体类型(media-type)可能会有帮助,这取决于它在媒体类型注册表里的定义。
注意一些旧的HTTP应用程序不能识别媒体类型的参数(parameter)。当向一个旧HTTP应用程序发送数据时,发送端只有在被type/subtype定义里需要时才使用类型参数(parameter)。
媒体类型(media-type)值需要被注册到网络数字分配权威(IANA[19])里。媒体类型的注册程序在RFC 1590[17]中大概描述。使用未经注册的媒体类型是不被鼓励的。
3.7.1 规范化和文本缺省 (Canonicalization and Text Defaults)
3.7.2 多部分类型(Multipart type)
3.8 产品标记 (product Tokens)
3.9 质量值(Quality Values)
3.10 语言标签 (Language Tags)
3.11 实体标签 (Entity Tags)
3.12 范围单位(Range Units)
省略的都是比较不常用的一些约定