3.1 HTTP 版本

HTTP 使用 “<主要>.<次要>” 的编号方案来表示协议的版本。协议的版本政策是为了让发送者表明消息的格式和理解进一步 HTTP 通信的能力,而不是通过该通信获得的功能。对于不影响通信行为或只添加到可扩展字段值的消息组件的添加,不会对版本号进行改变。当对协议所做的修改增加了不改变一般消息解析算法的特性,但可能增加了消息语义并意味着发送者的额外能力时,<次要>号会被增加。当协议内的消息格式发生变化时,<主要>号会被递增。更全面的解释见 RFC 2145 [36]。

HTTP 消息的版本由消息第一行中的 HTTP-Version 字段指示。

  1. HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT

请注意,主要数字和次要数字必须被视为独立的整数,并且每个数字的增量可能高于单个数字。因此,HTTP/2.4是一个比 HTTP/2.13 低的版本,而 HTTP/2.13 又比 HTTP/12.3 低。接收者必须忽略前导零,并且不得发送。

发送包含 HTTP-Version 为 “HTTP/1.1” 的请求或响应消息的应用程序必须至少有条件地符合本规范。至少有条件地符合本规范的应用程序应该在其消息中使用 “HTTP/1.1” 的 HTTP-Version,并且对于任何与 HTTP/1.0 不兼容的消息必须这样做。关于何时发送特定的 HTTP-Version 值的更多细节,见 RFC 2145 [36]。

一个应用程序的 HTTP 版本是该应用程序至少有条件地符合的最高 HTTP 版本。

代理和网关应用在转发与应用不同的协议版本的消息时需要小心。由于协议版本表示发件人的协议能力,代理/网关决不能发送一个版本指标大于其实际版本的消息。如果收到更高的版本请求,代理/网关必须降低请求的版本,或以错误回应,或切换到隧道行为。

由于自 RFC 2068 [33]发布以来发现的与 HTTP/1.0 代理的互操作性问题,缓存代理(必须)、网关(可能)和隧道(必须)不将请求升级到他们支持的最高版本。代理/网关对该请求的响应必须是与该请求相同的主要版本。

注意:在不同版本的 HTTP 之间进行转换,可能会涉及修改相关版本所要求或禁止的头字段。

3.2 统一资源标识符

URIs 有很多名字。WWW地址、通用文档标识符、通用资源标识符 [3],以及最后的统一资源定位器(URL)[4]和名称(URN)的组合 [20]。就 HTTP 而言,统一资源标识符是简单的格式化字符串,通过名称、位置或任何其他特征来识别一个资源。

3.2.1 一般语法

HTTP 中的 URI 可以以绝对形式表示,也可以相对于一些已知的基础 URI [11],这取决于它们的使用环境。这两种形式的区别在于,绝对 URI 总是以方案名称和冒号开始。关于 URL 语法和语义的确切信息,请参见 “统一资源标识符(URI)。通用语法和语义”,RFC 2396 [42](它取代了RFC 1738 [4] 和 RFC 1808 [11])。本规范采用了该规范中的 “URI-reference”、”absoluteURI”、”relativeURI”、”port”、”host”、”abs_path”、”rel_path” 和 “authority” 的定义。

HTTP 协议对 URI 的长度没有任何先验的限制。服务器必须能够处理它们所提供的任何资源的 URI,如果它们提供的基于 GET 的表格可以产生这样的 URI,则应该能够处理无限制长度的 URI。如果一个 URI 超过了服务器可以处理的长度,服务器应该返回 414(Request-URI Too Long)状态(见 10.4.15 节)。

注意:服务器在依赖超过 255 字节的 URI 长度时应该谨慎,因为一些旧的客户端或代理实现可能无法正确支持这些长度。

3.2.2 http URL

“http” 方案是用来通过 HTTP 协议定位网络资源的。本节定义了 http URLs 的特定方案语法和语义。

  1. http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]

如果端口为空或没有给出,则假定端口为80。其语义是,被识别的资源位于该主机的该端口上监听 TCP 连接的服务器,并且该资源的 Request-URI 是 abs_path(5.1.2 节)。应该尽可能避免在 URL 中使用 IP 地址(见 RFC 1900 [24])。如果 abs_path 在 URL 中不存在,当作为资源的 Request-URI 使用时,必须以 “/“ 的形式给出(第 5.1.2 节)。如果代理机构收到的主机名不是完全合格的域名,它可以将其域名添加到它收到的主机名中。如果代理机构收到一个完全合格的域名,代理机构不得改变主机名。

3.2.3 URI 比较

当比较两个URI以决定它们是否匹配时,客户端应该使用对整个 URI 的逐个八位字节进行大小写敏感的比较,但有这些例外:

  • 一个空的或未给出的端口相当于该URI-reference 的默认的端口。
    • 主机名的比较必须不区分大小写;
    • 方案名称的比较必须不区分大小写;
    • 空的 abs_path 相当于 “/“ 的 abs_path。

除了那些 “保留 “和 “不安全” 的字符集(见 RFC 2396 [42])等同于其 “%HEX” 编码。

例如,以下三个 URI 是等效的:
http://abc.com:80/~smith/home.html
http://ABC.com/%7Esmith/home.html
http://ABC.com:/%7esmith/home.html

3.3 Date/Time 格式

3.3.1 完整 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

第一种格式是首选的互联网标准,代表了 RFC 1123 [8](RFC 822 [9] 的更新)所定义的固定长度的子集。第二种格式是普遍使用的,但它是基于过时的 RFC 850 [12] 日期格式,并且缺少四位数的年份。解析日期值的HTTP/1.1 客户端和服务器必须接受所有三种格式(为了与 HTTP/1.0 兼容),尽管它们必须只生成 RFC 1123 格式,用于在头字段中表示 HTTP-date 值。更多信息见第 19.3 节

注意:我们鼓励日期值的接收者在接受可能由非 HTTP 应用程序发送的日期值时保持稳健,有时通过代理/网关检索或发布邮件到 SMTP 或 NNTP 时就是如此。

所有的 HTTP 日期/时间戳都必须以格林尼治标准时间(GMT)表示,没有例外。就 HTTP 而言,GMT 正好等于 UTC(协调世界时)。这在前两种格式中通过包含 “GMT” 作为时区的三个字母缩写来表示,并且在读取asctime 格式时必须假定。HTTP-date 是区分大小写的,除了语法中具体包括的 SP 之外,不得包括额外的LWS。

  1. HTTP-date = rfc1123-date | rfc850-date | asctime-date
  2. rfc1123-date = wkday "," SP date1 SP time SP "GMT"
  3. rfc850-date = weekday "," SP date2 SP time SP "GMT"
  4. asctime-date = wkday SP date3 SP time SP 4DIGIT
  5. date1 = 2DIGIT SP month SP 4DIGIT
  6. ; day month year (e.g., 02 Jun 1982)
  7. date2 = 2DIGIT "-" month "-" 2DIGIT
  8. ; day-month-year (e.g., 02-Jun-82)
  9. date3 = month SP ( 2DIGIT | ( SP 1DIGIT ))
  10. ; month day (e.g., Jun 2)
  11. time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
  12. ; 00:00:00 - 23:59:59
  13. wkday = "Mon" | "Tue" | "Wed"
  14. | "Thu" | "Fri" | "Sat" | "Sun"
  15. weekday = "Monday" | "Tuesday" | "Wednesday"
  16. | "Thursday" | "Friday" | "Saturday" | "Sunday"
  17. month = "Jan" | "Feb" | "Mar" | "Apr"
  18. | "May" | "Jun" | "Jul" | "Aug"
  19. | "Sep" | "Oct" | "Nov" | "Dec"

注意:HTTP 对日期/时间戳格式的要求只适用于它们在协议流中的使用。客户端和服务器不需要将这些格式用于用户展示、请求记录等。

3.3.2 增量秒

一些 HTTP 头字段允许将时间值指定为接收信息后的整数秒,用十进制表示。

  1. delta-seconds = 1*DIGIT

3.4 字符集

HTTP 使用与 MIME 相同的 “字符集” 的定义:

术语 “字符集” 在本文件中指的是与一个或多个表一起使用的方法,将一串八位字节转换成一串字符。请注意,在另一个方向上的无条件转换不是必需的,因为在一个给定的字符集中可能不是所有的字符都可用,而且一个字符集可能提供一个以上的八进制序列来代表一个特定的字符。该定义旨在允许各种字符编码,从简单的单表映射(如 US-ASCII)到复杂的表转换方法(如使用 ISO-2022 的技术)。然而,与 MIME 字符集名称相关的定义必须完全指定从八位数到字符的映射。特别是,不允许使用外部分析信息来确定确切的映射。

注意:”字符集” 这个术语的使用,更多的是指 “字符编码”。然而,由于 HTTP 和 MIME 共享同一个注册表,所以术语也必须共享。

HTTP 字符集是由不区分大小写的令牌来识别的。完整的令牌集由 IANA 字符集注册表 [19] 定义。

  1. charset = token

尽管HTTP允许任意 token 被用作字符集值,但任何在 IANA 字符集注册表 [19] 中具有预定义值的标记都必须代表该注册表所定义的字符集。应用程序应将其对字符集的使用限制在 IANA 注册表所定义的字符集。

实现者应了解 IETF 字符集要求 [38] [41]。

3.5 内容编码

内容编码值指示已应用于或可以应用于实体的编码转换。内容编码主要用于允许对文档进行压缩或以其他方式进行有用的转换,而不会丢失其底层媒体类型的身份并且不会丢失信息。通常,实体以编码形式存储,直接传输,并且仅由接收方解码。

  1. content-coding = token

所有的内容编码值是不区分大小写的。HTTP/1.1 在 Accept-Encoding(第 14.3 节)和 Content-Encoding(第 14.11 节)头字段中使用内容编码值。虽然该值描述了内容编码,但更重要的是,它表明需要什么解码机制来去除编码。

互联网号码分配机构(IANA)充当了内容编码值 token 的注册机构。最初,该注册表包含以下 token:

gzip
由文件压缩程序 “gzip”(GNU zip)产生的一种编码格式,如 RFC 1952 [25] 中所述。这种格式是 Lempel-Ziv编码(LZ77),有一个 32 位的 CRC。

compress
由常见的 UNIX 文件压缩程序 “compress” 产生的编码格式。这种格式是一种自适应的 Lempel-Ziv-Welch 编码(LZW)。

使用程序名称来识别编码格式是不可取的,不鼓励在未来的编码中使用。它们在这里的使用是代表了历史惯例,而不是好的设计。为了与以前的 HTTP 实现兼容,应用程序应该认为 “x-gzip” 和 “x-compress” 分别等同于 “gzip” 和 “compress”。

deflate
RFC 1950 [31] 中定义的 “zlib” 格式与 RFC 1951 [29] 中描述的 “deflate” 压缩机制相结合。

identity
默认(identity)编码;不使用任何转换。这种内容编码只在 Accept- Encoding 头中使用,而不应该在 Content-Encoding 头中使用。

3.6 传输编码

传输编码值用于指示已经、可以或可能需要应用于实体主体的编码转换,以确保通过网络的“安全传输”。 这与内容编码的不同之处在于,传输编码是消息的属性,而不是原始实体的属性。

  1. transfer-coding = "chunked" | transfer-extension
  2. transfer-extension = token *( ";" parameter )

参数采用属性/值对的形式。

  1. parameter = attribute "=" value
  2. attribute = token
  3. value = token | quoted-string

所有的传输编码值都是不区分大小写的。HTTP/1.1 在 TE 头字段(第 14.39 节)和 Transfer-Encoding 头字段(第 14.41 节)中使用传输编码值。

每当一个传输编码被应用于一个消息体时,传输编码集必须包括 “chunked”,除非该消息被关闭连接而终止。当 “分块” 传输编码被使用时,它必须是应用于消息体的最后一个传输编码。分块的 “传输编码不能在一个消息体上应用超过一次。这些规则允许接收者确定消息的传输长度(第 4.4 节)。

传输编码类似于 MIME [7] 的 Content-Transfer-Encoding 值,旨在通过 7 位传输服务实现二进制数据的安全传输。 然而,安全传输对 8 位清洁传输协议有不同的关注。 在 HTTP 中,消息体唯一不安全的特性是难以确定准确的正文长度(第 7.2.2 节),或者希望通过共享传输加密数据。

互联网号码分配机构(IANA)充当转移编码值令牌的注册机构。最初,该注册表包含以下令牌。”chunked”(第 3.6.1 节)、”identity”(第 3.6.2 节)、”gzip”(第 3.5 节)、”compress”(第 3.5 节)、和 “deflate”(第 3.5 节)。

新的传输编码值 token 应该以与新的内容编码值 token 相同的方式注册(第 3.5 节)。

一个服务器如果收到一个带有它不理解的传输编码的实体,应该返回 501(未实现),并关闭连接。服务器不得向 HTTP/1.0 客户端发送传输编码。

3.6.1 分块传输编码

分块编码修改了消息的主体,以便将其作为一系列的分块来传输,每个分块都有自己的大小指示器,后面是一个包含实体头字段的可选 trailer。这允许动态产生的内容与必要的信息一起传输,以便接收者验证其是否收到完整的消息。

  1. Chunked-Body = *chunk
  2. last-chunk
  3. trailer
  4. CRLF
  5. chunk = chunk-size [ chunk-extension ] CRLF
  6. chunk-data CRLF
  7. chunk-size = 1*HEX
  8. last-chunk = 1*("0") [ chunk-extension ] CRLF
  9. chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
  10. chunk-ext-name = token
  11. chunk-ext-val = token | quoted-string
  12. chunk-data = chunk-size(OCTET)
  13. trailer = *(entity-header CRLF)s

chunk-size 字段是一串十六进制数字,表示该块的大小。分块编码由任何大小为零的分块结束,后面是 trailer,由一个空行结束。

trailer 允许发送者在消息的末尾包括额外的 HTTP 头。trailer 头字段可以用来指示哪些头字段被包括在结尾中(见第 14.40 节)。

在响应中使用分块传输编码的服务器不得对任何标头字段使用 trailer,除非至少满足以下一项:

a) 该请求包括一个 TE 头字段,表明在响应的传输编码中可以接受 “trailer”,如第 14.39 节所述;或者。
b) 服务器是响应的源服务器,trailer 字段完全由可选的元数据组成,并且接收者可以在没有收到这些元数据的情况下使用该消息(以源服务器可以接受的方式)。 换句话说,源服务器愿意接受这样的可能性,即 trailer 字段可能在通往客户端的路上被默默地丢弃。

这一要求防止了当消息被 HTTP/1.1(或更高版本)代理接收并转发到 HTTP/1.0 接收者时出现互操作性失败。它避免了这样一种情况,即遵守协议就必须在代理上设置一个可能是无限的缓冲区。

附录 19.4.6 中介绍了一个解码 Chunked-Body 的例子过程。

所有 HTTP/1.1 应用程序必须能够接收和解码 “分块” 传输编码,并且必须忽略他们不理解的分块扩展。

3.7 媒体类型

HTTP 在 Content-Type(第14.17节)和 Accept(第14.1节)头字段中使用互联网媒体类型 [17],以便提供开放和可扩展的数据类型和类型协商。

  1. media-type = type "/" subtype *( ";" parameter )
  2. type = token
  3. subtype = token

参数可以以属性/值对的形式跟随类型/子类型(如 3.6 节所定义)。

类型、子类型和参数属性名称是不区分大小写的。参数值可能区分大小写,也可能不区分,这取决于参数名称的语义。在类型和子类型之间,以及属性和它的值之间,决不能使用线性空白(LWS)。一个参数的存在或不存在可能对媒体类型的处理有意义,这取决于它在媒体类型注册表中的定义。

请注意,一些旧的 HTTP 应用程序不承认媒体类型参数。当向旧的 HTTP 应用程序发送数据时,实现应该只在该类型/子类型定义需要的时候使用媒体类型参数。

媒体类型值是在互联网指定号码管理机构(IANA [19])注册的。RFC 1590 [17] 中概述了媒体类型的注册过程。不鼓励使用未注册的媒体类型。

3.7.1 规范化和文本默认值

互联网媒体类型是以规范形式注册的。通过 HTTP 消息传输的实体在传输前必须以适当的规范形式表示,但 “文本” 类型除外,这在下一段中定义。

当采用规范形式时,”文本” 类型的媒体子类型使用 CRLF 作为文本换行符。HTTP 放宽了这一要求,并允许在整个实体体中使用 CR 或 LF 单独代表换行的文本媒体的传输,而这一做法是一致的。HTTP 应用程序必须接受CRLF、裸 CR 和裸 LF 作为通过 HTTP 接收的文本媒体中的换行符。此外,如果文本的字符集没有分别使用八位数 13 和 10 来表示 CR 和 LF,比如一些多字节的字符集,HTTP 允许使用该字符集定义的任何八位数序列来表示相当于 CR 和 LF 的换行。这种关于换行的灵活性只适用于实体主体中的文本媒体;在任何 HTTP 控制结构(如头域和多部分边界)中,裸露的 CR 或 LF 一定不能被替换为 CRLF。

如果实体主体使用 content-coding 进行编码,则底层数据在编码之前必须采用上面定义的形式。

“charset” 参数用于某些媒体类型,以定义数据的字符集(3.4 节)。当发送方没有提供明确的字符集参数时,”文本” 类型的媒体子类型在通过 HTTP 接收时被定义为有一个默认的字符集值 “ISO-8859-1”。除了 “ISO-8859-1” 或其子集以外的字符集的数据,必须用适当的字符集值来标示。关于兼容性问题,见第 3.4.1 节

3.7.2 Multipart Types

MIME 提供了一些 “multipart” 类型 — 在一个消息体中封装一个或多个实体。所有多部分类型共享一个共同的语法,如 RFC 2046 第 5.1.1 节 [40] 所定义的,并且必须包括一个边界参数作为媒体类型值的一部分。消息正文本身是一个协议元素,因此必须只使用 CRLF 来表示正文部分之间的换行。与 RFC 2046 不同的是,任何多部分消息的尾声必须是空的;HTTP 应用程序不得传输尾声(即使原始多部分包含尾声)。这些限制的存在是为了保持多部分消息体的自我限制性质,其中消息体的 “结束” 由结束的多部分边界表示。

一般来说,HTTP 对多部分消息体的处理与其他媒体类型没有什么不同:严格来说是有效载荷。一个例外是 “multipart/byteranges” 类型(附录 19.2),当它出现在 206(部分内容)响应中时,它将被一些 HTTP 缓存机制解释,如第 13.5.414.16 节所述。在所有其他情况下,HTTP 用户代理应该遵循与 MIME 用户代理在收到多部分类型时相同或类似的行为。在多部分信息体的每个主体部分中的 MIME 头字段对 HTTP 没有任何意义,除了它们的 MIME 语义所定义的意义。

一般来说,HTTP 用户代理应该遵循与 MIME 用户代理在收到多部分类型时相同或类似的行为。如果一个应用程序收到一个未被识别的多部分子类型,该应用程序必须将其视为等同于 “multipart/mixed”。

注意:”multipart/form-data” 类型已被专门定义为携带适合通过 POST 请求方法处理的表单数据,如 RFC 1867 [15] 所述。

3.8 Product Tokens

产品标记用于允许通信的应用程序通过软件名称和版本来识别自己。大多数使用产品标记的字段也允许列出构成应用程序重要部分的子产品,用空白分隔。按照惯例,产品是按照它们对识别应用程序的重要性来排列的。

  1. product = token ["/" product-version]
  2. product-version = token

例子:

  1. User-Agent: CERN-LineMode/2.15 libwww/2.17b3
  2. Server: Apache/0.8.4

产品标记应该是简短的和有意义的。它们决不能用于广告或其他非必要的信息。尽管任何标记字符都可以出现在产品-版本中,但这个标记应该只用于版本标识(即,同一产品的连续版本应该只在产品值的产品-版本部分有差异)。

3.9 Quality Values

HTTP内容协商(第 12 节)使用简短的 “浮点” 数字来表示各种可协商参数的相对重要性(”权重”)。 权重被规范化为 0 到 1 范围内的一个实数,其中 0 是最小值,1 是最大值。如果一个参数的质量值为 0,那么对于客户端来说,带有这个参数的内容是 “不可接受的”。HTTP/1.1 应用程序不得在小数点后产生超过三位的数字。用户对这些值的配置也应以这种方式加以限制。

  1. qvalue = ( "0" [ "." 0*3DIGIT ] )
  2. | ( "1" [ "." 0*3("0") ] )

“Quality values” 是一个错误的说法,因为这些值只是代表所需质量的相对退化。

3.10 语言标签

语言标签是指人类为了向其他人类传递信息而说的、写的或以其他方式传达的自然语言。计算机语言被明确地排除在外。HTTP 在 Accept-Language 和 Content-Language 字段中使用语言标签。

HTTP 语言标签的语法和注册表与 RFC 1766 [1] 所定义的相同。简而言之,一个语言标签由 1 个或多个部分组成。一个主要的语言标签和一个可能是空的系列子标签。

  1. language-tag = primary-tag *( "-" subtag )
  2. primary-tag = 1*8ALPHA
  3. subtag = 1*8ALPHA

标签内不允许有空白,所有标签都不区分大小写。语言标签的名称空间由 IANA 管理。标签的例子包括:

  1. en, en-US, en-cockney, i-cherokee, x-pig-latin

其中任何两个字母的主标签是 ISO-639 语言缩写,任何两个字母的首字母副标签是 ISO-3166 国家代码。(上面的最后三个标签不是注册的标签;除了最后一个,其他都是将来可能被注册的标签的例子。)

3.11 实体标签

实体标签用于比较同一请求资源的两个或多个实体。HTTP/1.1 在 ETag(第 14.19 节)、If-Match(第 14.24节)、If-None-Match(第 14.26 节)和 If-Range(第 14.27 节)头字段中使用实体标签。关于它们如何作为缓存验证器被使用和比较的定义在 13.3.3 节。一个实体标签由一个不透明的带引号的字符串组成,前面可能有一个弱指标。

  1. entity-tag = [ weak ] opaque-tag
  2. weak = "W/"
  3. opaque-tag = quoted-string

一个资源的两个实体只有在八位数相等的情况下才可以共享一个 “强实体标签”。

由 “W/“ 前缀表示的 “弱实体标签”,只有在一个资源的两个实体是等价的,并且可以相互替换而不会对语义造成重大改变的情况下,才可以被这两个实体共享。弱实体标签只能被用于弱比较。

实体标签必须在与特定资源相关的所有实体的所有版本中是唯一的。一个给定的实体标签值可能被用于通过不同 URI 上的请求获得的实体。在不同 URI 上的请求所获得的实体中使用相同的实体标签值并不意味着这些实体的等同性。

3.12 范围单位

HTTP/1.1 允许客户端请求在响应中只包括部分(范围)的响应实体。HTTP/1.1 在 Range(第14.35节)和Content-Range(第14.16节)头域中使用范围单位。一个实体可以根据各种结构单元被分解成子范围。

  1. range-unit = bytes-unit | other-range-unit
  2. bytes-unit = "bytes"
  3. other-range-unit = token

HTTP/1.1 定义的唯一范围单位是 “byte”。HTTP/1.1 的实现可以忽略使用其他单位指定的范围。HTTP/1.1 被设计成允许不依赖范围知识的应用程序的实现。