超文本传输协议 -- HTTP/1.1(一)

超文本传输协议 -- HTTP/1.1(一)

  • 声明
  • 备忘录状态
  • 摘要
  • 1. 介绍
    • 1.1 目标
    • 1.2 需求
    • 1.3 术语
    • 1.4 概述
  • 2. 符号约定和一般语法
    • 2.1 扩展的巴科斯范式
    • 2.2 基本规则
  • 3. 协议参数
    • 3.1 HTTP版本
    • 3.2 统一资源标识符(URI)
      • 3.2.1 一般语法
      • 3.2.2 Http URL
      • 3.2.3 URI对比
    • 3.3 日期/时间格式
      • 3.3.1 全日期格式
      • 3.3.2 增量秒
    • 3.4 字符集
      • 3.4.1 丢失字符集
    • 3.5 内容编码
    • 3.6 传输编码
      • 3.6.1 分块传输码
    • 3.7 媒体类型
      • 3.7.1 规范及默认文本
      • 3.7.2 多实体类型(Multipart Types)
    • 3.8 产品令牌(Product Tokens)
    • 3.9 权重(Quality Values)
    • 3.10 语言标签(Language Tags)
    • 3.11 实体标签(Entity Tags)
    • 3.12 范围单元(Range Units)
  • 4. HTTP消息
    • 4.1 消息类型
    • 4.2 消息头
    • 4.3 消息体(message-body)
    • 4.4 消息长度
    • 4.5 通用头字段(general-header fields)
  • 5. 请求
    • 5.1 请求行
      • 5.1.1 方法
      • 5.1.2 请求URI
    • 5.2 请求标识的资源
    • 5.3 请求头字段
  • 6. 响应
    • 6.1 状态行
      • 6.1.1 状态码(Status-Code)和原因短语(Reason-Phrase)
    • 6.2 响应头字段
  • 7. 实体
    • 7.1 实体头字段
    • 7.2 实体体
      • 7.2.1 类型
      • 7.2.2 实体长度
  • 8. 连接
    • 8.1 持久化连接
      • 8.1.1 目的
      • 8.1.2 概述
        • 8.1.2.1 协商
        • 8.1.2.2 管道
      • 8.1.3 代理服务器
      • 8.1.4 实际考虑(Practical Considerations)
    • 8.2 消息传输需求
      • 8.2.1 持久化连接和流控制
      • 8.2.2 监视错误状态消息的连接
      • 8.2.3 100(继续)状态的使用
      • 8.2.4 服务器过早关闭连接时的客户端行为
  • 9. 方法定义
    • 9.1 安全和幂等方法
      • 9.1.1 安全方法
      • 9.1.2 幂等方法
    • 9.2 OPTIONS
    • 9.3 GET
    • 9.4 HEAD
    • 9.5 POST
    • 9.6 PUT
    • 9.7 DELETE
    • 9.8 TRACE
    • 9.9 CONNECT
  • 10. 状态码定义
    • 10.1 1xx 信息
      • 10.1.1 100 Continue
      • 10.1.2 101 协议切换
    • 10.2 2xx 成功状态码
      • 10.2.1 200 OK
      • 10.2.2 201 已创建
      • 10.2.3 202 已接受
      • 10.2.4 203 非权威信息
      • 10.2.5 204 无内容
      • 10.2.6 205 重置内容
      • 10.2.7 206 局部内容
    • 10.3 3xx 重定向
      • 10.3.1 300 多项选择
      • 10.3.2 301 永久移动
      • 10.3.3 302 找到
      • 10.3.4 303 参考其它
      • 10.3.5 304 无修改
      • 10.3.6 305 使用代理
      • 10.3.7 306 未使用
      • 10.3.8 307 临时重定向
    • 10.4 4xx 客户端错误
      • 10.4.1 400 错误请求(Bad Request)
      • 10.4.2 401 未授权(Unauthorized)
      • 10.4.3 402 需要支付(Payment Required)
      • 10.4.4 403 禁止(Forbidden)
      • 10.4.5 404 找不到(Not Found)
      • 10.4.6 405 方法不允许(Method Not Allowed)
      • 10.4.7 406 不可接受(Not Acceptable)
      • 10.4.8 407 需要代理授权(Proxy Authentication Required)
      • 10.4.9 408 请求超时(Request Timeout)
      • 10.4.10 409 冲突(Conflict)
      • 10.4.11 410 消失(Gone)
      • 10.4.12 411 长度需要(Length Required)
      • 10.4.13 412 前提条件失败(Precondition Failed)
      • 10.4.14 413 请求体太大
      • 10.4.15 414 Request-URI 太长
      • 10.4.16 415 不支持的媒体类型(Unsupported Media Type)
      • 10.4.17 416 请求区间不满足
      • 10.4.18 417
    • 10.5 5xx 服务器错误
      • 10.5.1 500 内部服务错误
      • 10.5.2 501 未实现
      • 10.5.3 502 网关错误
      • 10.5.4 503 服务不可用
      • 10.5.5 504 网关超时
      • 10.5.6 505 不支持的HTTP版本
  • 11. 访问认证
  • 12. 内容协商(Content Negotiation)
    • 12.1 服务器驱动协商
    • 12.2 代理驱动协商
    • 12.3 透明协商

声明

本文章(以下内容)仅为对HTTP/1.1协议的英文版备忘录1进行的中文翻译,仅供参考。

备忘录状态

本文档为互联网社区指定了一种互联网标准追踪协议,并请求讨论和改善的建议。有关本协议的标准化声明和状态,请参考《互联网官方协议标准》(STD1)的最新版本。本备忘录不限制分发。

摘要

HTTP协议是一个有关分布式、协作和超媒体信息系统的应用级协议。在为超文本的使用之前,它是一个一般的、无状态的协议,通过对其请求方法、错误码和头信息2的扩展,可以被应用于多个任务,比如命名服务器和分布式对象管理系统。HTTP协议的一个特点就是对数据表述的分类和传递,允许系统可以独立于被传输的数据来进行构建。

HTTP自1990年以来一直由万维网全球信息倡议组织使用。定义此协议的规范被称作“HTTP/1.1”,它同时也是对RFC3 20684的更新。

1. 介绍

1.1 目标

HTTP协议是一个有关分布式、协作和超媒体信息系统的应用级协议。自1990年以来一直由万维网全球信息倡议组织使用。HTTP协议的第一个版本,被称作“HTTP/0.9”,是一个用于跨互联网原始数据传输的简单协议。被RFC 19455定义的HTTP/1.0,通过使消息成为类MIME6消息的格式对协议进行了改善,包括数据传输的元信息和请求/响应语义中的修饰符。然而,HTTP/1.0协议并没有充分地考虑到分层代理、缓存、持久化连接需求和虚拟主机的影响。另外,没有完全实现“HTTP/1.0”的应用的繁殖也迫使协议版本的改变成为必须,来使正在通信的应用去确定它们互相之间的真正能力。

本规范以“HTTP/1.1”定义了本协议。它包含了更多的严格的需求来确保协议特性的可靠实现。

实际的信息系统需要比简单的检索系统更多的功能,包括搜索、前端更新和注解。HTTP允许一系列开放式的能够表明请求目标的方法和头信息。它建立在被统一资源定位符(URI)提供的参考规则之上,作为定位(URL)或者名称(URN),来表明被应用的方法所代表的资源。消息以一种类似于被MIME(Multipurpose Internet Mail Extensions)定义的Internet Mail使用的格式进行传递。

HTTP也会为了在用户代理和代理网关与其他网络系统之间的通信而被当做一般的协议来使用,包括SMTP、NNTP、FTP、Gopher和WAIS协议支持的系统。用这种方式,HTTP允许基本的超媒体能够访问各种各样的系统上的可用资源。

1.2 需求

本文档中的关键字“MUST”,“MUST NOT”,“REQUIRED”,“SHALL”,“SHALL NOT”,“SHOULD”,“SHOULD NOT”,“RECOMMENDED”,“MAY”和“OPTIONAL”的含义都与RFC 21197中描述的一致。

一个协议的实现如果不满足一个或者多个MUST或者REQUIRED级别的需求是不允许(not compliant)的。如果实现了协议所有的MUST、REQUIRED和SHOULD级别的需求,那么这个实现就被称之为“无条件允许”(“unconditionally compliant”)。一个只满足了MUST级别的需求而不完全满足SHOULD级别需求的实现被称之为“条件允许(conditionally compliant)”。

1.3 术语

本规范使用了一些术语来引用HTTP通信中的参与者和对象所代表的的角色。

  • connection
    为了通信目标的两个程序之间建立的传输层虚拟电路。
  • message
    HTTP通信的基本单元,由8位字节的结构化序列组成,它们通过connection进行传输,并且满足于第四章定义的语法。
  • request
    HTTP请求消息。定义于第五章。
  • response
    HTTP响应消息。定义于第六章。
  • resource
    网络数据对象或者URI标识的服务。定义于3.2节。资源可以有多个不同的表述方式(例如多种语言、数据格式、大小、分辨率)或者在其它方面的不同之处。
  • entity
    作为请求和响应的负载,被传输的信息。实体由实体头(entity-header)的域的元信息和实体体(entity-body)的内容组成。在第七章有更多的描述。
  • representation
    数据表示方式。包含一个从属于内容协商的响应的entity,在第12章有所描述。一个特殊的响应状态可能存在多种不同的表述。
  • content-negotiation
    内容协商。当服务一个请求时,选择合适的表述的机制,在第12章有所描述。任何响应实体的表述都能协商,包括错误响应。
  • variant
    变体。在任何给定的时刻,一个资源都有一个或者多个跟它有关的表述。每一个这样的表述都被称之为一个"variant"。使用术语"variant"并不一定认为这个资源从属于内容协商。
  • client
    客户端。为了发送请求而建立connection的程序。
  • user agent
    用户代理。初始化请求的client。它们通常是浏览器、编辑器、网络爬虫,或者其它的用户端工具。
  • server
    服务器。为了通过发送响应来服务请求而建立连接的应用程序。每一个给定的程序都能够既是客户端也是服务器。我们只会为了特殊的连接来使用这些术语去引用程序表现的角色,而不是程序的一般功能。同样地,任何一个服务器都可能会扮演原始服务器、代理服务器、网关或者通道的角色,它们通过请求的性质来改变行为。
  • origin server
    给定资源存在或者被创建的服务器。
  • proxy
    代理。为其他客户端发送请求,同时扮演服务端和客户端的中间人的程序。请求在其内部被处理,或者经过转化,然后转发给其他服务器。代理服务器必须实现本规范制定的服务端和客户端需求。一个“透明的代理服务器”不会在需要代理授权和认证之前修改请求和响应。一个“不透明的代理服务器”会为了给用户代理增加额外的服务而修改请求和响应,比如组注释服务、媒体类型转化、简化协议或者匿名用户过滤。除非明确地规定透明或者不透明,否则HTTP代理需求适用于这两种代理类型。
  • gateway
    网关。为其它服务器扮演中间人角色的服务器。不像代理服务器那样,网关在接收请求时就好像是请求资源所在的原始服务器,发送请求的客户端可能不会意识到它正在和一台网关进行通信。
  • tunnel
    隧道。在两个连接之间扮演一个盲目的接替者的中间人程序。一旦激活,隧道不会被认为是HTTP通信中的一部分,尽管它可能已经被一个HTTP请求初始化了。当接替的连接两端都关闭之后,隧道就不存在了。
  • cache
    缓存。程序的本地响应消息存储和控制其消息存储、检索和删除的子系统。缓存系统存储可缓存的响应是为了在以后同等的请求中能够减少响应时间和网络带宽消耗。任何客户端和服务端都可能包含缓存子系统,然而作为隧道的服务器不能使用缓存。
  • cacheable
    可缓存的。如果缓存系统被允许存储一份响应消息的复制品,为了能够在响应后续请求中使用它,那么这个响应就是可缓存的。第13章定义了决定HTTP响应缓存能力的规则。尽管资源是可缓存的,但是对于一个特殊的请求来说,缓存系统能否使用缓存的复制品可能还有一些额外的约束。
  • first-hand
    响应如果是直接从服务器过来的,并且没有必要的延迟,那么它就是第一手的,可能会通过一个或者多个代理。如果响应的有效性直接通过了源服务器的检查,那么它也是第一手的。
  • explicit expiration time
    当没有明显的到期时间的时候,缓存会直接分配一个到期时间。
  • age
    响应的年龄是它由源服务器发送或使用源服务器成功验证后的时间。
  • freshness lifetime
    生命周期。响应的产生时间开始直到到期时间结束的时间长度。
  • fresh
    响应如果还没有超过它的生命周期,那么它就是新鲜的。
  • stale
    响应如果超过了它的生命周期,那么它就是腐朽的。
  • semantically transparent
    语义上的透明。当缓存的使用不影响请求客户机和源服务器(除非是为了提高性能)时,缓存对特定响应的行为表现为“语义透明”。当缓存是语义透明的,客户端就会接收到与它已经接收到的请求被源服务器直接处理的完全一样的响应。
  • validator
    协议元素(比如实体标签和最后修改时间),被用来找出缓存项是否是某个实体的同等复制。
  • upstream/downstream
    上游和下游描述了一个消息的流向。所有消息都是从上游流向下游。
  • inbound/outbound
    入站和出站指的是请求消息和响应消息的路径:“入站”意味着“向源服务器旅行”,而“出站”表示“向用户代理旅行”。

1.4 概述

HTTP是一个请求/响应协议。客户端用一个请求方法、URI和协议版本,随后跟着一个包含了请求修饰符、客户端信息和可能的请求体内容的类MIME格式的消息的形式通过与服务端的连接向服务端发送请求。服务端用一个状态行作为响应,这个状态行里面包含了消息的协议版本和成功或者失败的响应码,状态行后面还紧跟了一个类MIME格式的消息,它包含了服务端信息、实体元信息和可能的实体内容。HTTP和MIME的关系在附录19.4有详细描述。

大多数HTTP通信都是被用户代理初始化的,它们由一个应用于源服务器上的资源的请求组成。在最简单的案例中,通过用户代理(UA)和源服务器(O)的单一连接就能够完成。

   request chain ---------------------------------->
UA ------------------------------------------------> O
   <---------------------------------- response chain

当在请求/响应链中出现了一个或者多个中间服务的时候,就会发生一个更加复杂的情况。有三种普通形式的中间服务:代理、网关和隧道。代理是指请求转发代理,用URI的绝对形式接收请求,重写部分或者全部消息,然后转发格式化后的消息给URI定位的服务端。网关是一个请求接收代理,扮演了其它服务端的上层服务的角色,如果有必要,会将请求转换为符合底层服务协议的格式。隧道是在不会改变消息的两个连接之间扮演的中继点的角色,当通信需要通过一个中间服务(比如防火墙)尽管这个中间服务不明白消息的内容的时候就会用到隧道。

   request chain ---------------------------------->
UA --------->A------------>B----------->C----------> O
   <---------------------------------- response chain

上面的图形展示了在用户代理和源服务器之间的三个中间服务。整个传输链中的请求和响应消息会通过四个分离的连接。这种区别是重要的,因为一些HTTP通信选项可能只适用于临近的、非渠道邻居的连接,或者传输链的端点,又或者遍布于整个传输链的所有连接。尽管图标是线性的,但是其中的每个服务器都可能在同一时间忙于多种通信服务。例如,B服务器可能正在接收来自于A以外的其它服务器的请求,或者正在转发给除C以外的其它的服务端,与此同时,还正在处理A服务的请求。

非渠道通信的每一部分都可能用一个缓存来处理请求。缓存的影响就是如果传输链中的一个中间服务缓存了适用于某一请求的响应,那么请求/响应链就可能会被缩短。下面的案例说明了如果B缓存了来自于O(通过C)的早期响应(没有被UA或者A缓存)的结果链。

   request chain --------->
UA --------->A------------>B----------->C----------> O
   <-------- response chain

不是所有响应都是可以缓存的,有些请求可能包含对缓存行为有特殊要求的修饰符。第13节定义了缓存行为和可缓存响应的HTTP需求。

事实上,在万维网上,有多种多样的缓存和代理的架构及配置正在被实验和部署。这些系统包含了用来节约跨洋带宽的国家级代理缓存系统、广播或多路广播缓存入口系统、CD-ROM分布式子集缓存组织等等。HTTP系统被用在高带宽的企业内部网上,通过低功率的无线链接和间歇性连接的PDA进行访问。HTTP/1.1的目标就是为了支持已经部署的系统的广泛的配置多样性,同时引进协议概念来构建满足具有高可靠性、失败之后的可靠标识需求的web应用。

HTTP通信通常建立在TCP/IP连接之上。默认启用的是TCP80端口,但是也可以启用其它的端口。这并不会妨碍HTTP实现于其它互联网协议或者别的网络通信之上。HTTP只是假定了一个可靠的连接,任何能提供这种保证的协议都可以被使用。关于HTTP/1.1的请求和响应在协议传输数据单元上的映射的讨论超出了本规范的范围。

在HTTP/1.0中,多数的实现为每一个请求和响应的交换都使用了一个新的连接。在HTTP/1.1中,一个连接可能会被用于一个或者多个请求和响应的交换,尽管这些连接可能会因为各种各样的原因被关闭。

2. 符号约定和一般语法

2.1 扩展的巴科斯范式

本文档中指定的所有的机制都使用了散文和类似于RFC 822中使用的Backus-Naur Form (BNF)8的形式进行了描述。实现者需要对这种符号形式有一定的熟悉以便更好地理解本文档。扩展的巴科斯范式包含了以下的结构:

  • name = definition
    规则的名称就是一个它自己的简单的名称(不包含任何的“<”和“>”封闭式标签),同时被它定义的“=”符号所分割。空格只有在用来表明多行规则定义的连续行缩进下才有意义。某些基本规则是用大写表示的,例如SP(空格), LWS(linear white space ,连续空格), HT(horizontal-tab,制表符), CRLF(换行、回车), DIGIT(数字), ALPHA(字母)等等。在任何时候,如果尖括号“<>”有助于识别规则名称的使用,就会在定义中使用它。
  • “literal”
    被引号引用的文本。除非特殊说明,文本的内容不区分大小写。
  • rule1 | rule2
    被符号“|”分割的元素是多选一的。例如“yes | no”表明接受“yes”或者“no”。
    -(rule1 rule2)
    被括号封闭的元素被当作单个的元素。因此,“(elem (foo | bar) elem)”允许被当作“elem foo elem”和“elem bar elem”。
  • *rule
    元素前面的字符“*”表明重复。完整的格式“*element”表明元素“element”出现至少n次,最多m次。默认值是0到无穷大,所以“*(element)”表示任意数量的元素,包括0个;“1*element”需要至少一个元素;“1*2element”允许1个或者2个。
  • [rule]
    中括号表明元素是可选的。“[foo bar]”同等于“*1(foo bar)”。
  • N rule
    指定重复数量:“element”同等于“*element”;实际上就是元素“element”出现的确切次数。因此“2DIGIT”就是2个数字,“3ALPHA”就是3个字母。
  • # rule
    “#”结构的用法和“*”是相似的,用来定义元素的列表。完整格式“#element”表明至少出现n次和最多出现m次元素,每个元素都被一个或者多个逗号“,”加上可选的连续空格(LWS)所分割。这样来定义列表的通用格式是非常容易的,例如:*LWS element *( *LWS "," *LWS element ))可以被表示为1#element。这种结构不管被用在什么地方,空元素都是允许的,但是不会被用来统计元素的数量。也就是说,“(element),,(element)”是允许的,但是只被当作出现了2次该元素。因此,在必须出现一个元素的地方,那么这个元素必须是非空的。默认值是0到无穷大,所以“#(element)”表示任意数量的元素,包括0个;“1#element”需要至少一个元素;“1#2element”允许1个或者2个。
  • ; comment
    出现在规则文本右边一段距离的分号直到该行的末尾的中间的内容是注释。这是一种将注释和规范并行出现的简单方法。
  • implied *LWS
    本规范描述的语法是基于单词的。除非特殊说明,在两个邻近的单词或者邻近单词和分割符之间的连续空格不会改变对域的解释。在任意两个标记(下面内容定义的“标记(token)”)之间必须出现一个分隔符,否则它们就会被当作单个单词。

2.2 基本规则

以下贯穿了整篇规范的规则描述了基本的解析结构。US-ASCⅡ码字符集定义于ANSI X3.4-19869

       OCTET          = 
       CHAR           = 
       UPALPHA        = 
       LOALPHA        = 
       ALPHA          = UPALPHA | LOALPHA
       DIGIT          = 
       CTL            = 
       CR             = 
       LF             = 
       SP             = 
       HT             = 
       <">            = 

具体解释:

       CRLF           = CR LF

HTTP/1.1为所有的协议都定义了CRLF顺序的行末(实体内容除外)标志。实体内容的行末标志由它自己关联的媒体类型所定义,参见3.7节的描述。

       LWS            = [CRLF] 1*( SP | HT )

HTTP/1.1头信息中域的值可以折叠到其它行中,如果连续行是以空格或者制表符开头的。所有线性空格,包括折叠空格,都和SP的语义是相同的。在解释字段值或将消息转发到下游之前,接收者可以使用单个SP替换任何线性空格。

       TEXT           = 

TEXT的值只能用于描述性的域内容或者不打算被消息解析器解释的值。*TEXT中的单词只有当根据RFC 2047规则编码时,才能够使用除ISO-8859-1字符集以外的字符。

CRLF只有作为头域后续部分的时候,才能够定义在TEXT中。我们希望折叠的线性空格在TEXT值被解析之前能够被一个SP所代替。

       HEX            = "A" | "B" | "C" | "D" | "E" | "F"
                      | "a" | "b" | "c" | "d" | "e" | "f" | DIGIT

十六进制数字字符被用于多个协议元素。

       token          = 1*
       separators     = "(" | ")" | "<" | ">" | "@"
                      | "," | ";" | ":" | "\" | <">
                      | "/" | "[" | "]" | "?" | "="
                      | "{" | "}" | SP | HT

许多HTTP/1.1头信息中域的值都是由被LWS或者其它字符分割的单词组成。这些特殊的字符必须用引号引起来,然后用一个参数值来使用(参考3.6节的定义)。

       comment        = "(" *( ctext | quoted-pair | comment ) ")"
       ctext          = 

注释能够用括号将注释文本括起来包含在某些HTTP头信息域中。注释只被允许在包含“comment”的域中作为域值的一部分被定义。在所有其它域中,括号也被认为是域值的一部分。

       quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )
       qdtext         = >

被双引号引用的文本会被当作一个单词。

       quoted-pair    = "\" CHAR

反斜杠(“\”)只有在作为引用字符(被引号引用的字符)或注释结构时,才可能被当作单字符引用机制使用。


3. 协议参数

3.1 HTTP版本

HTTP使用“.”(主版本.次版本)的数字模式来表示协议的版本。协议版本策略是假定允许消息发送者能够表明消息的格式和理解进一步通信的能力,而不是通信带来的特性。不影响通信行为或只是添加到扩展域值的额外的消息组件不会改变协议的版本号。当增加不会改变一般消息解析算法的特性,而这些特性可能影响到消息语法并暗示消息发送者额外的能力时,""的数字就会增加。当协议内消息格式发生变化时,“”的数字就会增加。参考RFC 214510获得完整解释。

一个HTTP消息的版本是通过消息第一行的域“HTTP-Version”来表示的。

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

注意,主版本号和次版本号必须被当作单独的数字对待,每个版本号都可能高于单个数字。因此,HTTP/2.4版本低于HTTP/2.13版本,反过来又低于HTTP/12.3版本。以0开头的版本号必须被消息接收者忽略,不能发送。

发送包含HTTP/1.1的“HTTP-Version”的请求或者响应消息的应用必须至少有条件地适应本规范。至少有条件地适应本规范的应用应该在它们的消息中使用HTTP/1.1的“HTTP-Version”,对于任何不兼容HTTP/1.0的消息都必须这样做。关于何时发送指定的HTTP-Version值的详情,参考RFC 214510

对于至少满足最低条件规范的应用的HTTP版本应该是最高的协议版本。

代理和网关应该小心地转发不同协议版本应用的消息。一旦协议版本指定了发送者的协议版本能力,代理或者网关不能用高于实际版本的版本指示器发送消息。如果接收到一个高协议版本的请求,必须要么降低请求版本等级,要么返回错误响应,要么切换到隧道行为(往下游传递,不作处理)。

自RFC 2068出版以来,由于HTTP/1.0代理发现的互操作性问题,缓存代理必须、网关可能、隧道不能将协议版本升级为它们支持的最高级版本。代理或者网关对于这种请求的响应必须使用和请求相等的主版本号协议。

注意:HTTP协议版本的转换可能牵涉到相关协议规定的必须或者禁止的头信息域的改变。

3.2 统一资源标识符(URI)

URI有很多众所周知的名称:WWW地址、通用文档定位符、通用资源定位符和统一资源定位符11与URN12的结合。对于HTTP而言,URI只是通过名称、地址和其它特性来标识资源的简单的格式化的字符串。

3.2.1 一般语法

HTTP中的URI可以用绝对值的形式或者跟一些已知的基本的URI相关的形式来表述,这取决于它们所使用的内容。通过绝对的URI总是以一个带冒号的模式名称开头这样的事实来区分这两种表现形式。有关URL语法和语义的限制信息,参考"Uniform Resource Identifiers (URI): Generic Syntax and Semantics," RFC 239613。本规范采用了来自RFC 2396规范中"URI-reference"、“absoluteURI”、 “relativeURI”、“port”、“host”、“abs_path”、 "rel_path"和"authority"的定义。

HTTP协议不会替换URI长度的任何优先限制。服务器必须能够处理它们服务的任何资源的URI,应该能够处理它们提供的基于GET形式的无限长度的URI。如果URI的长度比服务器能够处理(参考10.4.15节)的长度还要长,那么服务器应该返回414(请求URI太长)状态码。

      Note: Servers ought to be cautious about depending on URI lengths
      above 255 bytes, because some older client or proxy
      implementations might not properly support these lengths.

3.2.2 Http URL

“http”模式通过HTTP协议定位网络资源。本节为http URL定义指定模式的语法和语义。

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

如果端口为空或者不指定,则假定为80端口。以上的语义就是:服务器上的被标识的资源通过监听指定主机和端口上的TCP连接被定位,请求资源的URI用的是绝对路径(abs_path,参考5.1.2节)。无论何时,只要有可能,都应该避免在URL中使用IP地址。如果URL中没有提供绝对路径(abs_path),而这又是用于请求资源的URI的时候,则必须使用“/”替代。如果代理接收到的主机名称是一个不完全限定的域名,则代理可能将自己的域名添加到接收到的主机名称上面。如果代理接收到的是一个完全限定的域名,就不能更改收到的主机名称。

3.2.3 URI对比

当比较2个URI是否匹配时,客户端应该用大小写敏感的全路径的逐位八位比较,除了以下情况:

        - A port that is empty or not given is equivalent to the default
          port for that URI-reference;
        - Comparisons of host names MUST be case-insensitive;
        - Comparisons of scheme names MUST be case-insensitive;
        - An empty abs_path is equivalent to an abs_path of "/".

除“保留”和“不安全”集合中的字符(请参阅RFC 239613)外,其他字符都等效于它们的"% HEX HEX"编码(意思时可用它们的16进制方式表示)。
例如,以下的URI都是相等的:

      http://abc.com:80/~smith/home.html
      http://ABC.com/%7Esmith/home.html
      http://ABC.com:/%7esmith/home.html

3.3 日期/时间格式

3.3.1 全日期格式

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(对RFC 822的更新)定义的固定长度的子集。第二种格式是通用的,但是它是基于已经废弃的RFC 850定义的,而且还缺少4位长度的年份表示。HTTP/1.1的客户端和服务端在解析日期时必须要能够接收全部的三种格式(为了兼容HTTP/1.0),尽管它们必须只能生成RFC 1123的日期格式来表示头信息域中的HTTP-data值。更多信息请参考19.3节。

      Note: Recipients of date values are encouraged to be robust in
      accepting date values that may have been sent by non-HTTP
      applications, as is sometimes the case when retrieving or posting
      messages via proxies/gateways to SMTP or NNTP.

所有HTTP日期/时间戳必须用格林威治标准时间(Greenwich Mean Time,GMT)表示,没有例外。对HTTP而言,GMT精确地等于UTC(Coordinated Universal Time,宇宙协调时)。这表明前2种格式已经纳入到用三个字母表示时区的"GMT"格式中了,当读取asctime时也必须假定这一点。HTTP-date是大小写敏感的,而且不能包含语法指定的SP之外的额外的线性空格。

       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"
                    | "Thursday" | "Friday" | "Saturday" | "Sunday"
       month        = "Jan" | "Feb" | "Mar" | "Apr"
                    | "May" | "Jun" | "Jul" | "Aug"
                    | "Sep" | "Oct" | "Nov" | "Dec"
      Note: HTTP requirements for the date/time stamp format apply only
      to their usage within the protocol stream. Clients and servers are
      not required to use these formats for user presentation, request
      logging, etc.

3.3.2 增量秒

一些HTTP头字段允许将时间值用接收消息后的整数秒数(decimal格式)表示。

       delta-seconds  = 1*DIGIT

3.4 字符集

HTTP使用了和MIME描述的相同的字符集术语的定义:
本文档中的术语“字符集”是指用一个或者多个表将一个八位序列的字节转换为一个字符串序列的方法。注意,在另一个方向上的无条件转换不是必需的,因为不是所有的字符都可以在给定的字符集中使用,并且一个字符集可以提供多个八位元序列来表示一个特定的字符。本定义假定允许多种字符编码,从简单的单表映射如US-ASCII到如ISO-2022技术的复杂表转换法。然而,与MIME字符集名称有关的定义必须完整地指定字节到字符映射的执行。特别地,不允许使用外部分析信息来确定准确的映射。

注意:术语“字符集”更常见地是作为“字符编码”来使用。然而,自从HTTP和MIME共享注册表之后,重要地是术语也被共享了。

HTTP字符集用不区分大小写的令牌标识。IANA字符集注册表14定义了令牌的全部集合。

       charset = token

尽管HTTP允许用一个专门的令牌来表示字符集,但是任何来自于IANA字符集注册表中预定义的令牌都必须能够代表注册表中定义的字符集。应用程序应该限制注册表中定义的字符集的使用。

实现者应该意识到IETF15的字符集需求。

3.4.1 丢失字符集

一些HTTP/1.0的软件一直把没有正确字符参数的Content-Type头信息域理解为“接收者应该猜”。希望战胜这种行为的发送者可以包含一个字符集参数在里面即使原字符集是ISO-8859-1,并且在知道不会困扰接收者的情况下更应该这样做。

不幸地是,一些古老的HTTP/1.0客户端没有用明确的字符参数来正确地处理这种情况。HTTP/1.1的接收者必须尊重发送者提供的字符标签;当最初显示文档的时候,提供了要去“猜”字符的这些用户代理必须使用它们支持的来自于Content-Type的字符集,而不是它们偏好的字符集。参考3.7.1节。

3.5 内容编码

内容编码值表明了已经或者能够用于实体的编码转换方式。内容编码主要用来压缩文档或者其它的不会丢失信息和潜在媒体类型一致性的有用的转换。通常,实体以编码形式存储,直接传输,并且仅由接收方解码。

       content-coding   = token

所有的content-coding内容编码的值是不区分大小写的。HTTP/1.1在Accept-Encoding(14.3节)和Content-Encoding(14.11节)中使用内容编码。尽管内容编码的值描述了内容编码,但更重要的是它指出了移出编码的解码机制。

IANA16在content-coding值令牌中扮演了一个注册表的角色。起初,注册表包含以下令牌:

  • gzip
    文件压缩项目“gzip”产生的一种内容编码格式,在RFC 1952中有所描述。这种格式是一种32位CRC的Lempel-Ziv(LZ77)编码格式。
  • compress
    通用UNIX文件压缩项目“compress”产生的内容编码格式。这种格式是一种可适应的Lempel-Ziv-Welch编码格式。

用项目名称来鉴别内容编码格式是不令人满意的,对于未来的编码格式也是不鼓励的。以上两种用项目名称命名编码格式的做法是历史实践的典型代表,不是一个好的设计。为了兼容HTTP早先的实现,应用程序应该考虑使用“x-gzip”和“x-compress”来分别替换“gzip”和“compress”。

  • deflate
    RFC 1951描述了在RFC 1950中定义的“zlib”格式与“deflate”压缩机制的结合。
  • identity
    默认编码格式。不需要任何转换。content-encoding只能用在Accept-Encoding头中,不应该在Content-Encoding头中使用。

新的content-encoding值令牌应该被注册;为了客户端和服务端之间的互操作性,内容编码算法需要一个新的规范,这个规范应该是公用的,能够适应独立的实现且符合本章定义的内容编码目标。

3.6 传输编码

传输编码被用来表明一种已经、能够或者可能需要适应一种实体的编码转换,以确保网络的安全传输。与内容编码不同的地方在于传输编码是消息而不是源实体的一个属性。

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

参数是属性/值对的形式。

       parameter               = attribute "=" value
       attribute               = token
       value                   = token | quoted-string

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

只要传输编码被用于消息体,编码集中必须包含“chunked”,除非消息因为连接断开而终止。当“chunked”传输编码被使用时,它必须是应用于消息体的最后一个编码。“chunked”传输编码不能在消息体中使用超过1次。这些规则能够使消息接收者确定消息的传输长度。

传输编码与MIME中的内容编码是相似的,其设计的目的在于在超过7位字节的传输服务上进行二进制数据的安全传输。然而,安全传输与净8位的传输协议有不同的关注点。在HTTP协议中,消息体中唯一的不安全因数就在于如何精确地确定消息体的长度,或者基于共享传输的数据加密需求。

互联网数字分配机构(IANA)扮演了一个传输编码注册表的角色。这个注册表包含了这些传输编码:chunked, identity, gzip, compress, deflate

新的传输编码应该以新的内容编码的注册方式注册。

接收到未知传输编码实体的服务端应该响应501(未实现),并且关闭连接。服务端不能发送传输编码给HTTP/1.0的客户端。

3.6.1 分块传输码

分块编码是为了将消息体进行分块传输,每一个部分都有它自己的长度指示器,尾部包含可选的实体头字段。这样可以允许动态生成的内容和消息接收者用来验证是否收到完整消息的必要的信息一起传输。

       Chunked-Body   = *chunk
                        last-chunk
                        trailer
                        CRLF
       chunk          = chunk-size [ chunk-extension ] CRLF
                        chunk-data CRLF
       chunk-size     = 1*HEX
       last-chunk     = 1*("0") [ chunk-extension ] CRLF
       chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
       chunk-ext-name = token
       chunk-ext-val  = token | quoted-string
       chunk-data     = chunk-size(OCTET)
       trailer        = *(entity-header CRLF)

“chunk-size”用16进制字符串表明了分块的大小。分块编码以一个任意的大小为0的分块结束,随后以空行结尾。

分块的尾部允许发送者添加额外的HTTP头字段。尾部头字段部分表明了在分块的尾部包含了哪些消息头字段(14.40节)。

使用分块响应的服务端不能在尾部中包含任何的头字段,除非满足以下任一条件:

  1. 请求中包含有指定了分块响应尾部是可接收的TE头字段。参见14.39节。
  2. 服务端是响应的源服务端,尾部头字段全部由可选的元数据组成,且接收者在没有接收到这些元数据的时候也能使用这个响应消息(某种意义上是可接收的响应)。换句话说,源服务端接受在消息传输中丢失尾部字段的异常情况。、

这个需求阻止了当消息被HTTP/1.1的接收者接受时的发生的互操作性失败的可能,即便随后转发给了HTTP/1.0的接收者。它避免了这样一种情况,即遵守协议可能需要在代理上有一个可能无限的缓冲区。

在附录19.4.6中提供了一个处理分块体的案例。

所有HTTP/1.1的应用必须能够接受和解码分块传输,并且必须忽略未知的分块扩展部分。

3.7 媒体类型

为了提供开放的、可扩展的数据类型和类型转让,HTTP在Content-Type和Accept头字段中使用了网络媒体类型(Internet Media Types)。

       media-type     = type "/" subtype *( ";" parameter )
       type           = token
       subtype        = token

在属性/值中的"type/subtype"后面可能跟随有参数。

“type”、“subtype”和“parameter”属性名不区分大小写。“parameter”是否区分大小写取决于其名称的语法。在“type”和“subtype”之间不能有连续空格。“parameter”是否存在对于media-type的处理都应该是有意义的,取决于媒体类型注册表中的定义。

注意,旧HTTP应用不能识别媒体类型参数。当给旧HTTP应用发送数据时,应该只有当“type/subtype”需要参数时才使用参数。

media-type值在IANA中注册。在RFC 1590中描述了media-type的注册过程。不建议使用未注册的media-type。

3.7.1 规范及默认文本

互联网媒体类型(media-type)注册在一个规范表中。除了“文本”类型,所有通过HTTP消息传输的实体(entity-body)必须在它传输之前就应该在规范表中注册,就像下面这段话定义的一样。

在规范表中,“文本”类型的媒体子类型(media-subtype)使用CRLF作为文本的换行符。HTTP放宽了这一需求,当实体以文本类型完全一致地完成之后,HTTP允许使用单独的CR或者LF作为作为换行符。在通过HTTP接受的文本媒体中,HTTP应用必须能够接受CRLF、CR和LF作为文本类型的换行符。另外,如果文本是用没有使用13或者10个字节分别代表CR和LF的字符集表示的,如同多字节字符集一样,HTTP允许该字符集使用任意长度的字节序列来表示CR和LF作为换行符。这个关于换行符的灵活性只适用于实体中的文本媒体。单独的CR或者LF不能替换HTTP控制结构(头域和多部边界(multiparty boundaries))中的CRLF。

如果实体使用了内容编码(content-coding),底层数据在被编码之前必须使用上述的格式。

“charset”参数使用一些媒体类型来定义数据的字符集。当通过HTTP接收时,如果消息发送者没有明确地指定字符集数,“text”类型的媒体子类型被定义为拥有一个默认的字符集值“ISO-8859-1”。使用ISO-8859-1或其子集以外的字符集表示的数据必须用一个合适的字符集值来标识。兼容性问题参考3.4.1节.

3.7.2 多实体类型(Multipart Types)

MIME提供了若干个“multipart”类型 - - 在单个的消息体中封装了一个或者多个实体。所有的多实体类型分享一个通用的语法,如同在RFC 2046中定义的那样,同时必须包含一个边界参数作为媒体类型值的一部分。消息体本身就是一个协议元素,因此必须只能使用CRLF作为各个实体之间的换行符。不像在RFC 2046中描述的那样,任何多实体消息的结尾必须是空的;HTTP应用不能传输多实体结尾部分(尽管源多实体消息包含一个结尾)。存在这些限制是为了保持多实体消息体的自定界特性,其中消息体的“结束”由结束的多实体边界表示。

通常,HTTP对待多实体消息体(multipart message-body)与其它媒体类型没有分别:严格地称为有效载荷(strictly as payload)。一个例子就是在206(局部内容)响应中出现的“multipart/byteranges”类型(附录19.2),这个响应能够被一些使用HTTP缓存机制(在13.5.4和14.16节中有描述)的应用解析。除此之外,HTTP用户代理应该遵循与MIME用户代理在接收多实体类型时相同或类似的行为。多实体消息体中每个实体中的MIME头域在被MIME语法定义之前对于HTTP来说都是没有任何意义的。

通常,HTTP用户代理应该遵循与MIME用户代理在接收多实体类型时相同或类似的行为。如果应用接收到一个不能识别的多实体子类型,应用必须把它当作“multipart/mixed”类型等同对待。

注意:“multipart/form-data”类型已经被明确指定用于为POST请求传递表单数据。参考RFC 1867。

3.8 产品令牌(Product Tokens)

产品令牌被用来允许正在通信的应用使用软件名称和版本来标识它们自己。使用产品令牌的大多数域也允许用空格分隔的形式列出应用重要组成部分的子产品。按照惯例,被列出的产品以它们的重要性排序来标识应用。

       product         = token ["/" product-version]
       product-version = token

例如:

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

产品令牌应该简明扼要。它们不能为了打广告或者其它非必须的信息来使用。尽管product-version可能出现任何的令牌字符,但这些字符应该只能用于版本标识符(例如,相同产品的连续版本应该只有product-version部分是不同的)。

3.9 权重(Quality Values)

HTTP内容协商(第12节)使用简短的“浮点数”表示各种可协商参数的相对重要性(“权重”)。权重被规范化为从0到1的真实的数字,0是最小值,1是最大值。如果一个参数的权重是0,那么对客户端来说,携带这个参数的内容是“不可接收的”。HTTP/1.1不能生成超过3位小数的权重值。用户配置的值应该也同样受这个限制。

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

“Quality values”用词不当,因为这些值仅仅代表了期望值的关联下降。

3.10 语言标签(Language Tags)

语言标签标识了一种说、写或者其它用于人与人之间信息交流传递的自然语言。计算机语言被明确地包含其中。HTTP在Accept-Language和Content-Language字段中使用语言标签。

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

标签中不允许出现空格,所有标签都是忽略大小写的。语言标签的命名空间由IANA进行管理。示例标签:

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

任何双字母主标签(例如:en)都是ISO-639语言的缩写,任何以双字母开头的子标签都是ISO-3166国家代码。(上示的最后3个标签是没有注册的标签;除了最后一个的所有其它标签都应该在未来被注册)。

3.11 实体标签(Entity Tags)

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

      entity-tag = [ weak ] opaque-tag
      weak       = "W/"
      opaque-tag = quoted-string

只有当来自于同一个资源的两个实体的字节相等,它们才可能共享一个“强实体标签”。

“弱实体标签”,以“W/”前缀表示,只有当来自于同一个资源的两个实体相等并且在语法上没有重大改变、能互相替换时,它们才可能共享这个标签。弱实体标签只能同于弱比较。

跨越一个独特资源的所有实体的所有版本的实体标签必须是唯一的。一个给定的实体标签可能被用于不同URI请求的实体。在请求不同URI的实体中相同的实体标签不代表这些请求实体也是相等的。

3.12 范围单元(Range Units)

HTTP/1.1允许客户端请求响应中只包含部分实体的响应。 HTTP/1.1在Range(14.35节)和Content-Range(14.16节)头字段中使用范围单元。一个实体可以根据不同的结构单元分解为子单元。

      range-unit       = bytes-unit | other-range-unit
      bytes-unit       = "bytes"
      other-range-unit = token

HTTP/1.1中定义的唯一的范围单元就是“bytes”。HTTP/1.1的实现应用可能忽略使用其它单元指定的范围。

HTTP/1.1已经被设计为允许不依赖于范围知识的应用实现。

4. HTTP消息

4.1 消息类型

HTTP消息由客户端发送给服务端的请求以及服务端发送给客户端的响应组成。

       HTTP-message   = Request | Response     ; HTTP/1.1 messages

请求和响应使用RFC 822的通用消息格式来传输实体(消息负载)。这两种消息格式都由起始行、0或者头字段(headers)、一个表明头字段部分结束的空行(CRLF前什么也没有的行)和一个可能的消息体组成。

        generic-message = start-line
                          *(message-header CRLF)
                          CRLF
                          [ message-body ]
        start-line      = Request-Line | Status-Line

为了健壮性,服务器应该忽略在期望是请求行(Request-Line)的地方接收到的空行。换句话说,如果服务端正在从消息的开始读取协议流,而又接收到了一个CRLF,那么就应该忽略这个CRLF。

某些有BUG的HTTP/1.0的客户端实现在POST请求之后生成了额外的CRLF。这里重申一下BNF明确禁止的内容:HTTP/1.1的客户端不能在请求之前或之后携带额外的CRLF。

4.2 消息头

HTTP请求头字段,包含了通用头、请求头、响应头和实体头,且都遵循RFC 822中3.1节给定的通用格式。每个头字段都由一个跟随冒号“:”的名称和字段值组成。字段名称是忽略大小写的。字段值前面可能出现多个连续空格,即使我们推荐使用单个空格。头字段可以通过在额外的行前面添加一个空格或者HT来扩展为多行。在生成HTTP结构时,应用应该遵循一个已知的或者指定的“普通格式”,因为可能存在一些应用不能够接收普通格式之前的任何东西。

       message-header = field-name ":" [ field-value ]
       field-name     = token
       field-value    = *( field-content | LWS )
       field-content  = 

"field-context"字段不包含任何的前置或后置线性空格:在字段值第一个非空字符之前或最后一个非空字符之后出现的线性空格。这样的前置或后置LWS(线性空格)可能在不改变字段值语义的前提下被移除。出现在field-content之间的任何LWS在字段值被解释或转发到下游之前可能被一个SP(空格)替换。

接收到的含有不同名称头字段的顺序并不重要。不过,以首先发送通用头(general-header)字段,然后是请求头(request-header)或响应头(response-header)字段,最后以实体头字段结尾的方式是一个“好的实践”。

当且仅当头字段的所有字段值是以逗号分隔的列表定义的,那么消息中可能会出现字段名称相同的多重消息头(multiple-header)。必须提供一个将多重消息头结合为单个“字段名称:字段值”对的可能性,在不改变消息语义的前提下,依次将这些名称相同的字段的值添加到转换后的字段的头部,以逗号分隔。因此,接收到的相同名称字段的顺序在解释结合的字段值的时候就显得重要了,并且代理在转发消息时,不能改变这些字段的顺序。

4.3 消息体(message-body)

HTTP消息的消息体(如果有的话)是用来搬运跟请求或响应有关的实体(entity-body)的。只有当提供了传输编码 (transfer-coding),如同在头字段Transfer-Encoding中表明的那样,消息体才不同于实体。

       message-body = entity-body
                    | 

Transfer-Encoding字段必须用来指定应用程序应用的任何传输编码来确保消息安全和合理地传输。传输编码是消息而不是实体的一个属性,因此可能被请求或响应链中的任意的应用程序添加或者删除(然而,3.6节对某些被使用的传输编码有另外的限制)。

请求和响应对消息中何时使用消息体(message-body)有不同的规则。

请求中出现消息体的信号就是请求的消息头中包含了Content-Length或Transfer-Encoding头字段。如果请求方法的规范不允许在请求中发送实体,那么请求中就不能包含消息体。服务端应该读取和转发任何请求中的消息体;如果请求方法不包含实体的定义语义,那么在处理请求时应该忽略这个消息体。

对响应消息来说,消息中是否包含消息体取决于请求方法和响应状态码(6.1.1节)两个条件。HEAD请求方法的所有响应都不能包含消息体,尽管实体头字段的出现可能让人相信它们这样做了。所有1xx(报告的)、204(无内容)和304(未修改)的响应不能包含消息体。所有其它的响应都可以包含一个消息体,尽管它们的长度可能为0。

4.4 消息长度

消息的传输长度就是消息中出现的消息体的长度;也就是,在任何的传输编码被应用之后。当消息中包含了消息体,消息体的长度由以下条件(按出现顺序)决定:

  1. 任何不能包含消息体的响应消息总是被头字段之后的第一个空行中断,不管消息中出现了怎样的实体头字段。
  2. 如果出现了Transfer-Encoding头字段且字段值是“identity”以外的其它值,那么传输长度由“chunked”传输编码的使用决定,除非消息由于连接的关闭而中断。
  3. 如果出现了Content-Length头字段,它的以八位字节代表的小数值即代表了实体长度也表示了传输长度。如果这两个长度是不同的,则Content-Length字段就不能发送。如果消息同时包含了Transfer-Encoding和Content-Length字段,则后者必须被忽略。
  4. 如果消息使用了“multipart/byteranges”媒体类型,且没有另外指定传输长度,那么这个自定界媒体类型决定了传输长度。除非接收者能够解析这种媒体类型,否则发送者不能使用这种媒体类型;来自HTTP/1.1客户端并且携带有多字节范围指示器的范围头的请求的出现意味着这个客户端可以解析“multipart/byterangers”响应。

范围头可能由不懂 “multipart/byterangers”媒体类型的HTTP/1.0的代理转发;在这种情况下,服务端必须使用本章第1、3和5节定义的方法来分隔消息。

  1. 服务端关闭了连接。(关闭连接不能表明请求体的结束,因为这样断绝了服务端发回响应的可能性。)

为了与HTTP/1.0兼容,包含消息体的HTTP/1.1的请求必须包含一个有效的Content-Length字段,除非服务端适用于HTTP/1.1。如果包含消息体的请求没有给定Content-Length字段,服务端不能决定消息的长度时应该响应400(请求错误),或者如果服务端坚持要接收一个有效的Content-Length字段的话,应该返回411(需求长度)。

所有接收到多个实体的HTTP/1.1的应用程序必须接受“chunked”传输编码,因此当消息长度不能确定时,优先对消息使用此机制。

消息不能同时包含Content-Length头字段和未标识的传输编码。如果消息包含未标识的传输编码,Content-Length必须被忽略。

有消息体的消息如果提供了Content-Length字段,它的字段值必须明确地满足消息体的8位字节的数量。当接收和检测到无效的长度时,HTTP/1.1的用户代理必须通知用户。

4.5 通用头字段(general-header fields)

存在一些普片适应请求和响应消息的头字段,但这些字段并不适应被转移的实体,它们只适应于被传输的消息。

       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

通用头字段只有在和协议版本的变化相结合时才能进行可靠地扩展。然而,如果新增的或者实验性质的头字段被通信中的所有部分都认为是通用头字段,那么它们可能被给定通用头字段的语义。不被识别的头字段被当作实体头字段。

5. 请求

客户端发送到服务端的请求的第一行包含:适应资源的请求方法、资源标识符和使用的协议版本。

        Request       = Request-Line              ; Section 5.1
                        *(( general-header        ; Section 4.5
                         | request-header         ; Section 5.3
                         | entity-header ) CRLF)  ; Section 7.1
                        CRLF
                        [ message-body ]          ; Section 4.3

5.1 请求行

请求行以方法令牌(method token)开始,随后是请求的URI和协议版本,以CRLF结束。它们以SP(空格)字符作为分隔符。除了最后结尾的CRLF字符序列外,不允许额外的CR或LF字符出现。

        Request-Line   = Method SP Request-URI SP HTTP-Version CRLF

5.1.1 方法

方法令牌表明了被请求URI标识的资源被处理的方法。方法是大小写敏感的。

       Method         = "OPTIONS"                ; Section 9.2
                      | "GET"                    ; Section 9.3
                      | "HEAD"                   ; Section 9.4
                      | "POST"                   ; Section 9.5
                      | "PUT"                    ; Section 9.6
                      | "DELETE"                 ; Section 9.7
                      | "TRACE"                  ; Section 9.8
                      | "CONNECT"                ; Section 9.9
                      | extension-method
       extension-method = token

被资源允许的方法列表能够在“允许”头字段(14.7节)中指定。响应的返回码总是会通知客户端当前方法是否被资源允许,因为被允许的方法集能够动态地改变。源服务端如果能识别这种方法而该方法又不被资源所允许,则服务端应该响应405(Method Not Allowed)的状态码;如果服务端不能识别这种方法,又或者没有这种方法的实现,则服务端应该响应501(Not Implemented)的状态码。GET和HEAD方法必须被所有常用目标服务端所支持。所有其它的方法都是可选的;然而,如果以上的方法都被实现了,它们必须实现在第9章中指定的相同的语义。

5.1.2 请求URI

请求URI就是一个统一资源标识符(Uniform Resource Identifier),标识了适应请求的资源。

       Request-URI    = "*" | absoluteURI | abs_path | authority

请求URI的四个选项都依赖于请求的性质。“*”意味着请求并不适应某一特定的资源,只适应服务端自己,并且只有当请求的方法不必适应某一资源的时候才允许这样做。看起来就像下面这样:

       OPTIONS * HTTP/1.1

当请求被转发给代理的时候,一个绝对URI是必须的。代理就是转发请求或者从有效的缓存中返回响应的服务。注意,代理可能会将请求转发给另一个代理,或者直接转发到绝对URI指定的服务端。为了避免请求循环,代理必须能够识别所有它所属的服务端名称,包括别名、本地变量和IP地址。一个可能的请求行的案例如下:

       GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1

为了在以后的HTTP版本的所有请求中传输绝对URI请求,所有HTTP/1.1的服务端必须能够接收绝对URI格式的请求,尽管HTTP/1.1的客户端只生成适应于代理的请求。

授权格式只在CONNECT请求方法中才能够使用。

最常用的请求URI的格式就是被用来标识服务端或者网关上的资源。在这种情况下,URI的绝对路径必须被当作请求URI来传输,同时URI的网络地址必须在Host头字段中被传输。例如,一个想要从源服务端里检索上述资源的客户端会建立一个在主机“www.w3.org”的80端口上的TCP连接,然后发送这些行:

       GET /pub/WWW/TheProject.html HTTP/1.1
       Host: www.w3.org

后面就是请求的剩余部分。注意,请求的绝对路径不能为空;如果原始URI就是空的,则必须给定一个“/”(服务端根路径)。

请求URI以3.2.1节中指定的格式进行传输。如果请求URI是用“% HEX HEX”编码进行编码的,源服务端为了对请求进行合适地解释必须对请求URI进行解码。服务端应该给无效的请求URI响应合适地状态码。

一个透明的代理在转发请求给下一个服务的时候,不能重写请求URI中的“abs_path”部分,除了以上提到的用“/”替换null路径。

注意:当源服务端为了保留的目的错误地使用了非保留的URI时,“不重写”规则阻止了代理改变请求的原意。实现者应该意识到HTTP/1.1之前的代理服务可能已经重写了请求URI。

5.2 请求标识的资源

网络请求标识的精确的资源是通过检查请求URI和Host头字段来决定的。

不允许资源因请求主机的不同而发生变化的源服务器在决定一个HTTP/1.1请求标识的资源的时候可能会忽略Host头字段的值。(但是可以参考19.6.1.1节中,有对HTTP/1.1对Host字段支持的另外的需求)

基于请求主机来区分资源的源服务器(有时根据虚拟主机或虚拟主机名称)必须使用以下规则来决定HTTP/1.1请求的请求资源:

  1. 如果请求URI是绝对路径,则主机就是请求URI的一部分。任何Host头字段的值都必须被忽略;
  2. 如果请求URI不是绝对路径,且请求包含Host头字段,则主机由Host头字段的值确定;
  3. 如果被规则1或规则2确定的主机是无效的,则必须响应一个400(错误请求)状态码;

缺少Host头字段的HTTP/1.0的请求接收者可能会尝试使用启发法(例如,对特定的主机的一些唯一的内容进行的URI的检查)来精确地定位被请求的资源。

5.3 请求头字段

请求头字段允许客户端向服务器传输跟请求和客户端相关的额外的信息。这些字段扮演了请求标识符的角色,与编程语言的方法调用的参数有着相同的语义。

       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

请求头字段只有在和协议版本的变化相结合时才能进行可靠地扩展。然而,如果新增的或者实验性质的头字段被通信中的所有部分都认为是请求头字段,那么它们可能被给定请求头字段的语义。不被识别的请求头字段被当作实体头字段。

6. 响应

在接收和解释请求消息后,服务器用HTTP响应消息进行响应。

       Response      = Status-Line               ; Section 6.1
                       *(( general-header        ; Section 4.5
                        | response-header        ; Section 6.2
                        | entity-header ) CRLF)  ; Section 7.1
                       CRLF
                       [ message-body ]          ; Section 7.2

6.1 状态行

响应消息的第一行就是状态行,由协议版本以及随后的状态码和原文短语组成,每个元素由SP字符(空格)分割。除了最后结尾的CRLF字符序列外,不允许额外的CR或LF字符出现。

       Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF

6.1.1 状态码(Status-Code)和原因短语(Reason-Phrase)

Status-Code是试图去理解和满足请求的三位整形数字的响应码。这些状态码全部都在第10章中定义。Reason-Phrase是打算给状态码给定一个简短的本意描述。Status-Code是给计算机用的,而Reason-Phrase是给人类用的。客户端不被要求去检查或者显示原因短语。

状态码的第一个数字定义了响应的类型。最后2个数字不扮演任何分类的角色。第一个数字有5个值:

  • 1xx:情报 - 已接收到请求,正在处理
  • 2xx:成功 - 行动已经被成功接收、理解和接受
  • 3xx:转发 - 为了完成请求,必须采取进一步的行动
  • 4xx:客户端错误 - 请求包含错误语法或不能实现
  • 5xx:服务器错误 - 服务器不能完成一个明显的有效的请求

以下显示了HTTP/1.1定义的状态码的个体值和其对应的原因描述。这些原因描述全部都只是建议性的 – 它们可能在不影响协议的前提下被本地等同的短语所替换。

      Status-Code    =
            "100"  ; Section 10.1.1: Continue
          | "101"  ; Section 10.1.2: Switching Protocols
          | "200"  ; Section 10.2.1: OK
          | "201"  ; Section 10.2.2: Created
          | "202"  ; Section 10.2.3: Accepted
          | "203"  ; Section 10.2.4: Non-Authoritative Information
          | "204"  ; Section 10.2.5: No Content
          | "205"  ; Section 10.2.6: Reset Content
          | "206"  ; Section 10.2.7: Partial Content
          | "300"  ; Section 10.3.1: Multiple Choices
          | "301"  ; Section 10.3.2: Moved Permanently
          | "302"  ; Section 10.3.3: Found
          | "303"  ; Section 10.3.4: See Other
          | "304"  ; Section 10.3.5: Not Modified
          | "305"  ; Section 10.3.6: Use Proxy
          | "307"  ; Section 10.3.8: Temporary Redirect
          | "400"  ; Section 10.4.1: Bad Request
          | "401"  ; Section 10.4.2: Unauthorized
          | "402"  ; Section 10.4.3: Payment Required
          | "403"  ; Section 10.4.4: Forbidden
          | "404"  ; Section 10.4.5: Not Found
          | "405"  ; Section 10.4.6: Method Not Allowed
          | "406"  ; Section 10.4.7: Not Acceptable
          | "407"  ; Section 10.4.8: Proxy Authentication Required
          | "408"  ; Section 10.4.9: Request Time-out
          | "409"  ; Section 10.4.10: Conflict
          | "410"  ; Section 10.4.11: Gone
          | "411"  ; Section 10.4.12: Length Required
          | "412"  ; Section 10.4.13: Precondition Failed
          | "413"  ; Section 10.4.14: Request Entity Too Large
          | "414"  ; Section 10.4.15: Request-URI Too Large
          | "415"  ; Section 10.4.16: Unsupported Media Type
          | "416"  ; Section 10.4.17: Requested range not satisfiable
          | "417"  ; Section 10.4.18: Expectation Failed
          | "500"  ; Section 10.5.1: Internal Server Error
          | "501"  ; Section 10.5.2: Not Implemented
          | "502"  ; Section 10.5.3: Bad Gateway
          | "503"  ; Section 10.5.4: Service Unavailable
          | "504"  ; Section 10.5.5: Gateway Time-out
          | "505"  ; Section 10.5.6: HTTP Version not supported
          | extension-code
      extension-code = 3DIGIT
      Reason-Phrase  = *

HTTP状态码是可扩展的。HTTP的应用程序不要求去理解所有已注册的状态码的含义,尽管理解这些是明显值得的。然而,应用程序必须理解状态码所代表的类型,即状态码第一个数字的含义,同时,不能识别的状态码都要被当作该类型的x00状态码处理,且不能缓存未识别的响应。例如,客户端接收到了一个431的未识别的状态码,那么就可以被安全地假定为客户端请求存在一些问题,并将响应视为接收到了一个400状态码。在这种情况下,用户代理应该提供给用户返回响应的实体,因为实体中可能包含对这些不常用状态码的人类可读的信息描述。

6.2 响应头字段

响应头字段允许服务器传输状态行不能替换的有关响应的额外的信息。这些头字段给定了有关服务器或者更深一层地有关访问请求URI标识的资源的信息。

       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

响应头字段只有在和协议版本的变化相结合时才能进行可靠地扩展。然而,如果新增的或者实验性质的头字段被通信中的所有部分都认为是响应头字段,那么它们可能被给定响应头字段的语义。不被识别的响应头字段被当作实体头字段。

7. 实体

请求或响应如果没有请求方法或者响应状态码的限制的话可能会传输一个实体。实体由实体头字段和实体体(entity-body)组成,尽管一些响应只会包含实体头字段。

在本章中,发送者和接收者既可能是客户端也可能是服务端,依赖于谁发送实体,谁接收实体。

7.1 实体头字段

实体头字段定义了有关实体体的元数据信息,如果没有提供实体体,则是有关于请求标识的资源的信息。部分元信息是可选的,而有些对于本规范的部分内容来说又是必须的。

       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
       extension-header = message-header

“extension-header”头字段允许在不改变协议的前提下定义额外的实体头字段,但这些字段不能假定为是被接收者所识别的。不能识别的头字段应该被接收者忽略,同时必须被透明的代理转发。

7.2 实体体

HTTP请求和响应中的任何实体都是由实体头字段定义的格式和编码发送的。

       entity-body    = *OCTET

只有当提供了消息体的时候,消息中才能够提供实体,参考4.3节中的描述。实体是通过对确保消息安全和合适传输的传输编码( Transfer-Encoding)进行解码而从消息体中获取的。

7.2.1 类型

当消息中包含实体的时候,实体的数据类型是通过Content-Type和Content-Encoding这两个头字段决定的。两者定义了一个双层的、有序的编码模型:

       entity-body := Content-Encoding( Content-Type( data ) )

Content-Type指定了潜在数据的媒体类型(media-type)。Content-Encoding可能被用来表明适用于数据的额外的内容编码,通常用于数据压缩的目的,这些编码是请求资源的一个属性。没有默认的编码。

任何包含实体的HTTP/1.1的消息都应该包含定义实体媒体类型的Content-Type头字段。只有当Content-Type头字段为空的时候(没有提供这个字段),消息接收者才可能假定通过检视实体内容和(或)标识资源的URI的扩展名来猜测实体的媒体类型。如果是未知的媒体类型,消息接受者应该当作“application/octet-stream”对待。

7.2.2 实体长度

消息的实体长度就是消息体在被任何传输编码应用之前的长度。4.4节定义了消息体是如何决定它的传输长度的。

8. 连接

8.1 持久化连接

8.1.1 目的

在持久化连接之前,对每一个URL都建立了一个独立的TCP连接,增加了HTTP服务器的负载,同时导致了网络拥塞。内联图片和其它相关数据的使用经常需要客户端向同一服务器在较短时间内发送多个请求。对这些性能问题和原型实现结果的分析是可用的17 18。实现经验和对真实HTTP/1.1实现的测试展示了好的结果19。另一种选择已经探索过了,例如,T/TCP20

持久化HTTP连接有许多优点:

  • 通过打开和关闭少量的TCP连接,路由器和主机(客户端、服务器、代理、网关、通道,或缓存系统)就节约了CPU时间,同时主机也节省了用于TCP协议控制块的内存。
  • HTTP请求和响应能够在连接上形成一个管道。管道允许一个客户端可以不需要等待响应而发送多个请求,允许使用单个TCP连接是非常高效的,有着很低的时间消耗。
  • 由于TCP持续连接导致的数据包的减少也会减少网络拥塞,同时给TCP充足的时间去决定网络拥塞的状态。
  • 后续请求的潜在因数减少了,因为不会把时间花费在TCP握手连接上了。
  • HTTP可以发展得更加地优雅,因为在错误被报告时,少去了关闭TCP连接的损耗。使用更高版本HTTP的客户端可以乐观地尝试新的特性,但是如果通信是建立在旧的服务器上的,在错误被报告以后,可以用旧的语法进行重试。

HTTP实现程序应该实现持久化连接。

8.1.2 概述

在HTTP/1.1和其之前版本的最大意义的不同就在于持久化连接是任何HTTP连接的默认行为。也就是说,除非另外指定,否则客户端应该假设服务器会保持持久化连接,即使服务器返回了错误响应之后。

持久化连接提供了一种机制,这种机制可以使客户端和服务器标记TCP连接的关闭。这个标记发生在连接头字段上。一旦某个关闭被标记了,客户端就不能在此连接上发送任何请求了。

8.1.2.1 协商

HTTP/1.1服务器可以假定HTTP/1.1客户端打算保持持久化连接,除非连接头包含请求发送过来的连接令牌“close”。如果服务器在发送响应之后选择立即关闭连接,那么它应该发送一个包含“close”连接令牌(connection-token)的连接头(Connection header)过去。

HTTP/1.1客户端期望连接保持开启,但是是否保持开启依赖于服务器的响应中是否包含“close”连接令牌的连接头。一旦客户端在发送请求后不想再保持连接,它应该发送一个包含“close”连接令牌的连接头。

如果客户端或服务器发送了一个包含“close”连接令牌的连接头,则当前请求就是此连接的最后一个请求。

客户端和服务器不应该假设低于HTTP/1.1版本的应用会保持持久化连接除非有明确指定。参考19.6.2节了解更多关于HTTP/1.0客户端向下兼容的信息。

为了保持持久化连接,连接中的所有消息必须有一个自定义的长度(不是由于连接关闭而定义的),如4.4节所述。

8.1.2.2 管道

支持持久化连接的客户端将请求“管道化”(发送多个不需要等待响应的请求)。服务器必须按照接收请求的顺序返回响应。

假定持久化连接并且在建立连接后立即管道化的客户端在第一次尝试管道化失败后应该准备重试连接。如果客户端做了这样的重试,在它知道连接是持久化之前不准管道化。如果服务器在发送所有对应的响应之前关闭了连接,客户端必须也要准备重新发送请求。

客户端不应该使用非幂等方法或非幂等方法的序列对请求管道化(参考9.1.2节)。否则,一个传输连接过早的中断能够导致不确定的结果。希望发送非幂等请求的客户端应该等待发送请求,除非它收到了上一个请求的响应码。

8.1.3 代理服务器

尤其重要的是代理服务器正确地实现了在14.10节中指定的连接头字段的属性。

代理服务器必须分别对其连接的客户端和源服务器(或其它的代理服务器)标记持久化连接。每个持久化连接只应用于一个传输连接。

代理服务器不准和HTTP/1.0的客户端建立HTTP/1.1的持久化连接(但是可以参考RFC 2068中许多HTTP/1.0客户端实现的Keep-Alive头问题的信息和讨论)。

8.1.4 实际考虑(Practical Considerations)

服务器都会设置一个超时时间,当超过超时时间后,它们就不会再保留非激活的连接了。代理服务器可能会设置一个比较高的值,因为有可能客户端会在同一个服务器上建立多个连接。持久化连接的使用对客户端或服务器的超时时间的长度(或超时时间的存在)没有要求。

设置了超时时间的客户端或服务器应该优雅地关闭连接。客户端和服务器两者都应该持续监控传输关闭的另一端,并且在合适的时候对其进行响应。如果客户端或服务器没有迅速地检测到另外一边的关闭,就能够导致网络资源不必要的耗尽。

客户端、服务器和代理可能在任何时候关闭传输连接。例如,一个客户端也许正在服务器决定关闭“闲置的”连接的同一时间发起一个新的请求。从服务器的角度来看,它正在关闭一个闲置的连接,但从客户端的角度来看,请求正在被处理。

这意味着客户端、服务器和代理必须能够从异步关闭事件中恢复。只要请求序列是幂等的(参见9.1.2节),客户端软件应该重启传输连接,并且在没有用户交互的情况下重新传输中断的请求序列。非幂等的方法或序列不准自动重试,尽管用户代理也许给人工操作提供了请求重试的选项。在语义上理解应用的用户代理的确认可以替换用户确认。如果请求的第二序列失败,则不应该重复自动重试。

如果有可能的话,服务器应该总是对每个连接都至少有一个响应。服务器不应该在响应的中途关闭连接,除非怀疑出现了网络或客户端故障。

使用持久化连接的客户端应该限制保留对给定服务器并立连接的数量。单个用户的客户端不应该对任何服务器或代理保留超过两个连接。代理应该对服务器或另外的代理最多保持2*N个连接,其中N是同时激活的用户数量。这些指导方针都是打算用来改善HTTP响应时间并避免出现网络拥塞的。

8.2 消息传输需求

8.2.1 持久化连接和流控制

HTTP/1.1服务器应该维持持久化连接并且使用TCP流控制机制来解决临时负载,而不是关闭连接期待客户端重试。后者使用的技术会加剧网络拥塞。

8.2.2 监视错误状态消息的连接

发送消息体的HTTP/1.1(或更高版本)的客户端在传输请求的时候应该监视连接中出现的错误状态。如果客户端监视到了错误状态,就应该立即终止传输消息体。如果消息体是用“chunked”编码(3.6节)发送的,0长度块和空尾部过早地被用来标识消息的结束。如果Content-Length头领先于消息体,则客户端必须关闭连接。

8.2.3 100(继续)状态的使用

100(继续)状态码的目的在于允许发送带消息体消息的客户端决定在客户端发送消息体之前服务器是否愿意接收请求(基于请求头)。在某些情况下,如果服务器没有查看消息体就拒绝了消息,那么客户端发送消息就会显得不合适或效率极低。

HTTP/1.1客户端需求:

  • 如果客户端在接收到100响应后才会发送消息体,那么它必须发送值为“100-continue”的Expect请求头。
  • 如果客户端不打算发送消息体,那么它禁止发送值为“100-continue”的Expect请求头。

由于存在使用旧协议的应用,HTTP/1.1允许存在发送了“Expect:100-continue”的客户端不会接收417(期望失败)或100状态码这样模棱两可的情况。因此,当客户端将这个请求头字段发送给源服务器(可能通过代理)时,即使它从没见过100响应码,也不应该在发送请求体之前等待无限长的时间。

HTTP/1.1源服务器需求:

  • 在接收到包含期望“100-continue”的Expect请求头字段的请求之后,源服务器必须要么响应100状态码同时继续读取输入流,要么响应一个最终的状态码。禁止源服务器在发送100响应码之前就开始等待请求体。如果源服务器用最终状态码进行了响应,那么它可能关闭传输连接或可能继续读取并抛弃请求的剩下部分。禁止源服务器在响应了最终状态码后仍然执行请求方法。
  • 如果请求消息不包含期望“100-continue”的Expect请求头字段,则源服务器不应该响应100状态码,如果这样的消息来自于HTTP/1.0(或更低版本)的客户端,则禁止响应100状态码。这个规则有个例外:为了兼容RFC 2068,服务器可能将100响应发送给不包含期望“100-continue”的Expect请求头字段的HTTP/1.1的PUT或POST请求。这个例外的目的就是最小化与未声明等待100状态码时间有关的任意客户端处理延迟,只应用于HTTP/1.1的请求,不适用于其它任一版本。
  • 如果源服务器已经收到相应请求的部分或全部请求体,那么它可能会省略100响应。
  • 一旦请求体被接收和处理完毕,发送了100响应码的源服务器最终必须发送一个最后的状态码,除非它过早地中断了传输连接。
  • 如果源服务器接收了一个不包含期望“100-continue”的Expect请求头字段的请求,而这个请求又包含了请求体,且服务器在读取传输连接中的整个请求体之前就响应了一个最终状态码,那么服务器不应该关闭连接直到它读取了整个请求,或者直到客户端关闭连接。否则,客户端可能会不可靠地接收响应消息。然而,本需求没有讨论保护服务器免受来自拒绝服务( denial-of-service)或严重损坏的客户端实现的攻击。

HTTP/1.1代理需求:

  • 如果代理接收到了一个包含“100-continue”期望值的Expect请求头字段的请求,同时代理要么知道下一跳服务器是用HTTP/1.1或更高版本编译的,或者不知道下一跳服务器的HTTP的版本,都必须转发请求,包含Expect头字段。
  • 如果代理知道服务器的版本是HTTP/1.0或者更低,则禁止转发这个请求,同时必须响应417(Expectation Failed)状态码。
  • 代理服务器应当维护一个缓存,以记录最近访问下一站点服务器的HTTP版本号。
  • 如果代理接收到的请求来自HTTP/1.0或更低版本的客户端,并且请求消息不包含“100-continue”期望值的Expect请求头字段,则禁止转发100响应。这点需求重写了在转发1xx响应时的一般规则(参考10.1节)。

8.2.4 服务器过早关闭连接时的客户端行为

如果HTTP/1.1客户端发送了一个包含请求体的请求,但是并不包含“100-continue”期望值的Expect请求头字段,同时如果客户端不是直接连接到源服务器,且如果客户端在接收到服务器任何响应之前就发现连接已经关闭了,则客户端应该重试请求。如果客户端进行了重试,就可以使用以下的“二进制指数补偿”算法来确保获取一个可靠的响应:

  1. 初始化一个到服务器的新连接;
  2. 发送请求头;
  3. 将变量R初始化为到服务器的估计往返时间(例如,基于建立连接所需的时间),或者初始化为常数5秒(如果往返时间不可用);
  4. 计算 T = R * ( 2 * * N),其中N是本次请求之前重试的次数;
  5. 要么等待来自服务器的错误消息,要么等待T秒(随便哪一个先到);
  6. 如果没有收到错误响应,等待T秒后就发送请求的请求体;
  7. 如果客户端发现连接过早地关闭了,则从第一步开始重试,直到请求被接受,接收到错误响应,或者用户焦躁地中止重试处理。

如果在任何时候都收到错误响应,如果还没有发送完请求消息,则客户端不应该再继续下去了,应该关闭连接。


9. 方法定义

下面定义了HTTP/1.1一般方法集。尽管这个集合可以被扩展,但是新增的方法不能假定对单独扩展的客户端和服务器共享相同的语义。

Host请求头字段必须存在于所有HTTP/1.1请求中。

9.1 安全和幂等方法

9.1.1 安全方法

实现者应该意识到软件在互联网交流中代表着用户,应该小心地让他们意识到他们可能采取的对他们自己或别人可能导致非本意的行动。

特别地,已有的惯例是GET和HEAD方法除了检索之外不应该具有采取额外行动的意义。这些方法应该被认为是“安全的”。这允许用户代理用一种特别的方式去表示其它的方法,例如POST、PUT和DELETE,以至于让用户意识到这样一个事实:可能采取了一个不安全的请求。

自然而然地,不能够确认服务器在执行GET请求时不会产生副作用;事实上,一些动态资源认为这是一种特性。这里最重要的区别就是用户并没有请求这样的副作用,所以不用对它们负责。

9.1.2 幂等方法

方法也可以有“幂等性”的属性,因为(除了错误和过期问题)多个同一请求的副作用和单个请求是一样的。GET、HEAD、PUT和DELETE方法共享这个属性。另外,这些方法的选项和跟踪也不应该有副作用,所以它们具有内在的幂等性。

然而,有可能一些请求序列不是幂等的,尽管序列中执行的所有方法是幂等的。(幂等的序列是指整个序列的单次执行产生的结果不会因为序列的全部或部分重复执行而改变。)例如,如果一个序列产生的结果依赖于一个值,而这个值随后被这个序列所修改,那么这个序列就不是幂等的。

从定义上讲,没有副作用的序列就是幂等的(在同一资源集上没有执行并行操作)。

9.2 OPTIONS

OPTIONS方法代表了对有关在被Request-URI标识的请求/响应链中可用通信选项信息的请求。这个方法允许客户端来决定这些与资源或服务器的能力有关的选项和/或需求,不包含暗示资源操作或启动资源检索。

这个方法的响应是不可缓存的。

如果OPTIONS请求包含一个实体(如同Content-Length或Transfer-Encoding的存在所表示的),那么媒体类型必须用字段Content-Type来表明。尽管本规范没有定义与这样的实体有关的任何用法,然而未来对HTTP的扩展可能会使用OPTIONS实体在服务器上制造更加详细的查询。不支持这种扩展的服务器可能会抛弃请求体。

如果Request-URI是一个星号(“*”),那么OPTIONS请求打算应用于一般服务器而不是特定的资源。因为服务器的通信选项通常依赖于资源,“*”请求只有在作为“ping”或“no-op”类型的方法时才有用;在允许客户端测试服务器的能力之前,什么用也没有。例如,可以用来测试HTTP/1.1代理的合法性。

如果Request-URI不是星号(“*”),OPTIONS请求只应用于当与资源通信时可用的选项。

200响应应该包含任何能够表明被服务器实现或应用于资源的可选特性的头字段,可能包含一些没有被规范定义的扩展字段。响应体,如果有的话,也应该包含有关通信选项的信息。本规范没有定义这种响应体的格式,但可能作为HTTP的扩展而被定义。内容协商被用来选择适当的响应格式。如果不包含响应体,则响应必须包含一个字段值为"0"的Content-Length字段。

在请求链中,Max-Forwards请求头字段被用来以特殊代理作为目标。当代理由于允许转发请求而接收到一个绝对URI上的OPTIONS请求时,它必须检查Max-Forwards字段。如果Max-Forwards字段值为"0",则代理禁止转发请求;反之,代理应该用自己的通信选项进行响应。如果Max-Forwards字段值是一个大于"0"的整数,则代理在转发请求时,必须减少该字段值。如果请求中没有提供Max-Forwards字段,则转发的请求中禁止包含Max-Forwards字段。

9.3 GET

GET方法就是检索被Request-URI标识的任何信息(以实体的形式)。如果Request-URI引用了一个数据生成进程,那么产生的数据会作为响应的实体被返回,而不是进程的资源文本,除非这个文本恰好是进程的输出。

如果请求消息中包含If-Modified-Since、If-Unmodified-Since、If-Match、If-None-Match、If-Range等头字段,那么GET方法的语义就变成了“有条件的GET方法”。有条件的GET方法要求实体只有在条件头字段描述的情况下才能被传输。有条件的GET方法打算通过在不需要多重请求或传输客户端已有数据的情况下刷新缓存的实体来减少不必要的网络使用。

如果请求消息中包含Range头字段,那么GET方法的语义就变成了“部分GET”。一个“部分”GET方法要求只传输实体的一部分,如同在14.35节描述的那样。“部分”GET方法打算通过在不需要传输客户端已有数据的情况下检索部分已完成的实体来减少不必要的网络使用。

当且仅当GET请求的响应满足HTTP缓存要求(见13节的描述)时,响应才是可缓存的。

使用表单时,请参阅第15.1.3节中的安全性注意事项。

9.4 HEAD

HEAD方法与GET方法是完全一样的,除了服务器禁止在响应中返回消息体。HEAD请求的响应头字段中的元信息应该和GET方法响应中的信息是完全一致的。这个方法可以用来获取有关本身没有传输实体的请求所隐含的实体元信息。这个方法经常被用来测试超文本链接的有效性、可访问性和最近的修改。

从某种意义上讲,HEAD请求的响应是可缓存的,因为响应中所包含的信息可能会被用来更新同一资源下先前已被缓存的实体。如果新字段的值表明了已缓存的实体不同于当前实体(如同Content-Length、Content-MD5、ETag、Last-Modified等字段内容的改变所示,那么缓存必须把缓存条目视为已过期。

9.5 POST

POST请求用于源服务器接收请求中封装的实体作为请求行中被Request-URI标识的资源的新从属。POST方法被设计成允许用一个统一的方法来包含以下功能:

  • 对现存资源的注释;
  • 邮寄一个消息到公告牌、新闻组、邮件列表或相似的物品组;
  • 提供一块数据,如提交一个表单的结果,到数据处理程序;
  • 通过附加的操作来扩展数据库;

POST方法执行的真正功能是由服务器决定的,并且通常都依赖于Request-URI。传输的实体从属于该URI,就像文件从属于包含它的目录、新闻文章从属于发布它的新闻组或记录从属于数据库一样。

POST方法执行的行为也许不会产生一个能够被URI标识的资源。假若这样,200(OK)或204(无内容)才是合适的响应状态,这依赖于响应中是否包含一个描述处理结果的实体。

如果源服务器中已经产生了一个资源,那么响应状态就应该是201(created),并且包含一个描述了请求状态和引用了这个新资源的实体,还包含一个Location头(参见14.30节);

这个方法的响应是不可缓存的,除非响应中包含合适的Cache-Control或者Expires头字段。然而,303状态的响应能够被用来导向用户代理来检索已缓存的资源。

POST请求必须遵守在8.2节列出的消息传输需求。

参考15.1.3节了解安全性注意事项。

9.6 PUT

PUT方法要求封装的实体要储存在提供的Request-URI下面。如果Request-URI引用了一个已存在的资源,则封装的实体应该被认为是驻留在服务器端资源的一个更新的版本。如果Request-URI指向的资源不存在,并且该URI能够被请求中的用户代理定义为一个新的资源,则源服务器就能够用这个URI来创建这个新的资源。如果新的资源被创建了,那么源服务器必须通过201(created)响应来通知用户代理。如果已存在资源被更新了,则应该要么200(OK)要么204(Not Content)响应码被响应来表明请求的处理完成。如果该URI标识的资源无法被创建或更新,则应该响应一个能够反映出问题本质的合适的错误响应。实体的接收者禁止忽略任何不懂或没有实现的Content-*(e.g. Content-Range)头字段,并且必须返回501(没有实现)响应。

如果请求来自于缓存并且Request-URI标识了一个或多个现存的已缓存的实体,那么这些实体应该被当作已过期。

此方法的响应不可缓存。

POST和PUT请求的根本区别就在于Request-URI所反映的不用的含义。POST请求中URI所标识的是会处理封装实体的资源,这个资源可能是一个数据接收处理进程、指向其它协议的网关、或者接受注释的单独实体。相比之下,PUT请求中URI所标识的是请求中封装的实体 - - 用户代理知道假定的是何种URI并且服务器禁止试图将这个请求应用到其它资源上面。如果服务器想要将这个请求应用到不同的URI上面,那么它必须返回301(Moved Permanently)响应,随后用户代理在关于是否重定向该请求时,会做出自己的决定。

单一资源可能会被不同的URI所标识。例如,一篇文章可能有一个标识“当前版本”的URI,它与标识每个特定版本的URI是分开的。在这种情况下,对一般URI的PUT请求可能会导致源服务器定义其他几个URI。

HTTP/1.1没有定义一个PUT方法是如何影响源服务器的状态的。

PUT请求必须遵守在8.2节列出的消息传输需求。

除非对一个实体头有其它的特殊指定,否则PUT请求中的实体头应该应用于被PUT方法创建或修改的资源。

9.7 DELETE

DELETE方法要求源服务器删除被Request-URI标识的资源。这个方法可能会被源服务器上的人工或其它手段重写。客户端不能保证删除操作被成功执行,尽管来自源服务器的响应码能够表明操作已经成功完成了。但是,服务器不应该表示成功,除非在给出响应时,它打算删除资源或将其移动到无法访问的位置。

如果成功的响应包含一个描述状态的实体,则响应码应该是200(OK);如果操作还未执行,则响应码应该是202(已接受);如果操作已经被执行,但响应中不包含实体,则响应码应该是204(无内容)。

如果请求来自于缓存并且Request-URI标识了一个或多个现存的已缓存的实体,那么这些实体应该被当作已过期。

此方法的响应不可缓存。

9.8 TRACE

TRACE方法被用来调用请求消息的远程应用层回环。请求的最终接收者应该将消息作为200(OK)响应的实体返回给客户端。最终的接收者是源服务器、第一个代理或者接收到Max-Forwards字段值是0的请求(参考14.31节)的网关中的一个。

TRACE请求禁止包含实体。

TRACE请求允许客户端发现在请求的另一端被接收的何种消息以及使用这个数据来测试或诊断信息。Via头字段(参考14.45节)的值特别值得注意,因为它充当了请求链中的追踪器的角色。客户端使用Max-Forwards头字段来限制请求链的长度,这个字段在测试代理无限转发消息时是有用的。

如果请求有效,响应应该在实体中包含整个请求消息,且Content-Type字段值为“message/http”。

此方法的响应不可缓存。

9.9 CONNECT

本规范保留名称为“CONNECT”的方法。此方法能够和代理一起使用,使代理能够动态地切换为一个通道(tunnel )。

10. 状态码定义

以下描述了每个响应码,包括它适用的方法以及响应中需要的元信息的描述。

10.1 1xx 信息

这类状态码表示临时响应,只以状态行和可选的头字段组成,以空行结束。这类状态码没有必须的头字段。由于HTTP/1.0没有定义任何1XX类型的状态码,服务器禁止向HTTP/1.0客户端发送这类状态码,除非处于实验性质的条件下。

客户端必须准备在常规响应之前接受一个或多个1xx状态响应,尽管客户端不期望收到100(continue)状态信息。不期望的1XX状态响应可能会被用户代理忽略。

代理必须转发1XX响应,除非代理和客户端之间的连接被中断,或者代理自己需要生成1XX响应。(例如,如果代理在转发的请求中添加了“Expect: 100-continue”字段,那么它就不需要转发对应的100响应了。)

10.1.1 100 Continue

客户端应该继续它的请求。这个临时响应是用来通知客户端请求的初始部分已经被接收了,并且也没有被服务器拒绝。客户端应该继续发送请求的剩余部分,或者是如果请求已经完成了,就忽略这个响应。服务器在请求完成之后必须发送一个最终的响应。参考8.2.3节以了解此状态码使用和处理的详细讨论。

10.1.2 101 协议切换

为了改变连接中使用的应用协议,通过升级信息头字段(14.42节),服务器明白并且愿意遵从客户端请求。在中止101响应的空行之后,服务器就会立即切换到被响应中的升级头字段定义的协议。

协议只有在切换后是有利的才应该这样做。例如,切换到新版本的协议对使用老版本的协议来说是有利的,并且当传递使用这个特性的资源的时候,切换到实时的、同步的的协议也是有利的。

10.2 2xx 成功状态码

这类状态码表示客户端请求已经被成功地接收、解析和接受。

10.2.1 200 OK

请求已经成功。响应返回的信息依赖于请求使用的方法,例如:

  • GET请求。响应中包含请求资源对应的实体。
  • HEAD请求。响应中包含对应请求资源的实体头字段,不包含任何消息体。
  • POST请求。响应中包含实体描述或请求处理结果的响应。
  • TRACE请求。响应中包含终端服务器接收到的请求消息。

10.2.2 201 已创建

请求已经完成并且创建了新的资源。新创建的资源可以由响应实体中返回的URI引用,Location头字段为资源提供最特定的URI。响应应该包含一个含有资源特性和定位列表的实体,用户或用户代理能够从这个列表中选择最合适的一个。实体的格式是由Content-Type头字段给定的媒体类型指定的。源服务器必须在返回201响应之前就创建好资源。如果行动不能立即完成,服务器应该用202(已接受)响应替换。

201响应可能包含一个ETag响应头字段,该字段表示刚刚创建的请求变量的实体标签的当前值。

10.2.3 202 已接受

请求已经被接受并处理,但处理还未完成。请求最终可能不会执行,因为当处理真正发生的时候,它可能会被禁用。没有从这种异步操作中重发状态码的工具。

202响应是有意地不负责任的。它的目的就在于允许服务器为了某些其它的处理(可能是每天只跑一次的批量处理)而接受这个请求,而不需要用户代理和服务器一直保持连接直到处理完成。与此响应一起返回的实体应该包括请求当前状态的指示,以及指向状态监视器的指针,或者用户预计何时可以完成请求的一些估计。

10.2.4 203 非权威信息

实体头中返回的元信息不是源服务器中可用的最终集,但确是本地或第三方复制过来的集合。提供的集合可能是原始版本的子集或超集。例如,包含有关可能导致原服务器已知的元数据的超集的资源的本地注解信息。这个响应码的使用不是必须的,只有当200(OK)响应的时候才适用。

10.2.5 204 无内容

服务器已经完成了请求但是不必返回响应体,并且可能会返回已更新的元信息。响应可能以实体头的形式返回新的或已更新的元信息,这些头信息应该与请求变量相关。

如果客户端是用户代理,那么它不应该改变导致请求被发送的文档视图。这个响应主要打算是允许输入执行一些操作,而这些操作并不会导致用户代理已激活的文档视图的改变,尽管任何新的或已更新的元信息都应该应用到用户代理已激活视图的当前文档。

204响应禁止包含消息体,因此总是以头字段后的第一个空行中止。

10.2.6 205 重置内容

服务器已经完成了请求并且用户代理应该重置发送请求的文档视图。这个响应主要打算是用来通过用户输入来执行一些操作,在输入之前会清空表单,以至于用户可以轻松地发起另一个操作。这个响应禁止包含实体。

10.2.7 206 局部内容

服务器已经完成了请求资源的GET请求的一部分。请求必须包含一个Range头字段(14.35节)来表明期望的范围,并且可能包含一个If-Range头字段(14.27节)来使请求条件化。

响应必须包含以下头字段:

  • 一个能够表明响应包含范围的Content-Range头字段,或者一个包含每个部分Content-Range的multipart/byteranges Content-Type。如果响应提供了Content-Length头字段,则它的值必须要匹配传输的消息体的字节长度。
  • 日期
  • ETag和(或)Content-Location。对于相同的响应,如果这个头要放在200(OK)响应中的话,需要包含这2个字段。
  • Expires、Cache-Control,、和(或)Vary。对于相同请求变量,如果字段值不同于之前已发送的任何响应的话,需要包含这几个字段。

如果206响应是一个使用了强缓存验证器(13.3.3节)的If-Range请求的结果,则响应不应该包含其它的实体头。而如果响应是一个使用了弱缓存验证器的If-Range请求的结果,则响应禁止包含其它的实体头。这项规则防止了已缓存的实体体和已更新的实体头之间的矛盾。否则,响应就必须包含所有的实体头字段,这些字段都会随着200响应返回给同一个请求。

如果ETag或Last-Modified头字段没有精确匹配,则缓存系统禁止用之前已缓存的内容和206响应结合,参考13.5.4节。

不支持Range和Content-Range头字段的缓存系统禁止缓存206响应。

10.3 3xx 重定向

这类状态码表示用户代理需要采取更进一步的行动来完成请求。当且仅当第二个请求使用的方法是GET或HEAD时,需要的行动才会在没有用户交互的情况下被用户代理完成。客户端应该检测无限重定向循环,因为这样循环中的每一个重定向都会生成网络堵塞。

注意:本规范的之前版本建议最大5次重定向,内容开发人员应该意识到可能存在实现了这个固定限制的客户端。

10.3.1 300 多项选择

请求的资源对应于资源表示集中的任何一个,其中的每一个都提供它自己的地址和代理驱动协商信息,以至于用户或用户代理可以选择一个优先的资源并重定向请求到它的地址。

除非它是一个HEAD请求,否则响应就应该包含一个实体,这个实体涵盖了所有资源特性和地址的集合,用户或用户代理可以从中选择最合适的一个。实体的格式是由Content-Type头字段给定的媒体类型决定的。根据用户代理的格式和能力,会自动地选择最合适的选项。然而,本规范没有为这项自动选择功能提供任何标准。

如果服务器有一个更好的选择,那么它就应该将那个资源特定的URI包含在Location字段中;用户代理会使用这个Location字段值自动重定向。除非其它说明,否则这个响应是可缓存的。

10.3.2 301 永久移动

请求的资源已经被分配了一个新的永久URI,并且任何未来对这个资源的引用都应该使用返回URI中的一个。只要有可能,有链接编辑能力的客户端应当自动地修正Request-URI引用到服务器返回的一个或多个新的链接上。除非其它说明,否则这个响应是可缓存的。

新的永久URI应该包含在响应中的Location字段。除非请求方法是HEAD,否则响应的实体应该包含一个简短的带有新URI超链接的超文本注释。

如果接收了一个非GET或HEAD请求的301响应,则用户代理禁止自动重定向请求,除非用户授权,因为这样做可能改变请求发生时所处的条件。

注意:当接收到301响应并自动重定向POST请求时,存在一些HTTP/1.0的用户代理会将其变成一个GET请求。

10.3.3 302 找到

请求的资源临时处于不同的URI。因为重定向有时会发生改变,因此客户端应该继续使用Request-URI来发送未来的请求。这个响应只有当Cache-Control或Expires头字段有特殊说明时才可缓存。

响应中的Location字段应该提供临时URI的地址。除非请求方法是HEAD,否则响应的实体应该包含一个简短的带有新URI超链接的超文本注释。

如果接收了一个非GET或HEAD请求的302响应,则用户代理禁止自动重定向请求,除非用户授权,因为这样做可能改变请求发生时所处的条件。

注意:RFC 1945 和 RFC 2068 指出,客户端不允许改变重定向请求中的方法。然而,大多数已存在的用户代理实现将302当做303响应,对Location字段值执行GET请求而无视原始请求方法。服务器增加的303和307状态码就是希望能够明确地阐述客户端被期望的行为。

10.3.4 303 参考其它

请求的响应能够在不同的URI下找到,并且通过对资源的GET请求还应该可以恢复。这个方法存在的主要目的是允许已激活的POST脚本输出重定向用户代理到已选择的资源上。新URI并不是原始请求资源的替换引用。303响应禁止缓存,但是第二个请求(重定向)的响应是可以缓存的。

响应中的Location字段应该给出资源不同的URI。除非请求方法是HEAD,否则响应的实体应该包含一个简短的带有新URI超链接的超文本注释。

注意:许多HTTP/1.1版本之前的用户代理并不理解303响应。与这样的客户端互动是让人担忧的,但是可以用302响应替换,因为大多数用户代理对这里描述的303作出了302响应。

10.3.5 304 无修改

如果客户端执行了一个条件性的GET请求并且允许访问,但是文档并没有被修改,服务器就应该用这个响应码响应。304响应禁止包含消息体,因此总是以头字段后的空行中止。

本响应必须包含以下头字段:

  • Date。除非满足14.18.1节需要的条件才可省略。

如果无时钟服务器遵守这些规则,并且代理和客户端将它们的Date添加到了任何一个接收到的响应中,除了已经被RFC 2068指定的一个响应(参考14.19节),缓存将会正确地操作。

  • ETag,和(或) Content-Location。如果对相同的请求响应了200。
  • Expired,Cache-Control,和(或) Vary。对于相同的变量,响应了不同的字段值。

如果条件性GET请求使用了强缓存验证器(参考13.3.3节),则响应不应该再包含其它的实体头字段。否则(例如,条件性GET请求使用了弱验证器),响应禁止包含其它的实体头字段。这样做防止了已缓存的实体体和已更新的实体头之间的矛盾。

如果304响应表明了实体不能实时缓存,则缓存系统必须忽略响应并且无条件地重复请求。

如果缓存系统使用已接收到的304响应来更新缓存入口,则缓存系统必须更新入口来反映出响应中给出的任何新字段值。

10.3.6 305 使用代理

请求的资源必须要通过Location字段给定的代理进行访问。Location字段指定了代理的URI,并期望接收者通过代理重复这个单独的请求。305响应必须只能由源服务器生成。

注意:RFC 2068 并不清楚305是打算用来重定向单个请求并且只能由源服务器生成。缺少对这些局限的观察会产生重大的安全影响。

10.3.7 306 未使用

306状态码曾用于本规范之前版本,现在已不再使用,但是状态码保留。

10.3.8 307 临时重定向

请求的资源临时驻留在另一个URI之下。因为重定向有时会发生改变,客户端应该继续使用Request-URI发送未来的请求。响应只有在Cache-Control或Expires头字段指明的情况下才可缓存。

响应中的Location字段应该给定临时URI的地址。除非请求方法是HEAD,否则响应的实体应该包含一个简短的带有新URI超链接的超文本注释,因为许多HTTP/1.1之前的用户代理并不理解307状态码。因此,注释应该包含用户用此URI重复源请求的必要信息。

如果接收了一个非GET或HEAD请求的307响应,则用户代理禁止自动重定向请求,除非用户授权,因为这样做可能改变请求发生时所处的条件。

10.4 4xx 客户端错误

4xx类型的状态码被设计用于客户端似乎有错误的情况。除了响应HEAD请求外,服务器应该包含一个实体,其中包含错误情况的解释,以及它是临时的还是永久的条件。这些状态码适用于任何请求方法。用户代理应该向用户显示任何包含的实体。

如果客户端正在发送数据,使用TCP实现的源服务器应该小心地确保在自己关闭输入连接之前,客户端承认接收到了包含响应的数据包。如果客户端在关闭连接后仍向服务器发送数据,则服务器的TCP栈就会向客户端发送一个重置包,这个包会在客户端未确认的输入缓冲被HTTP应用读取和解释之前,抹除它们。

10.4.1 400 错误请求(Bad Request)

由于有缺陷的语法,请求不能够被服务器理解。客户端不应该在不修正的情况下重复此请求。

10.4.2 401 未授权(Unauthorized)

请求需要用户授权。响应必须包含 WWW-Authenticate 头字段(14.47节),这个字段包含一个适用于请求资源的认证。客户端可能会用一个合适的 Authorization 头字段(14.8节)重复这个请求。如果请求已经包含了 Authorization 证书,那么401响应就表明了这些证书的授权被拒绝了。如果401响应包含之前响应的同样的认证,并且用户代理已经尝试了至少一次授权,那么用户应该得到响应中给定的实体,因为实体中可能包含相关的诊断信息。HTTP访问授权解释于"HTTP Authentication: Basic and Digest Access Authentication"21

10.4.3 402 需要支付(Payment Required)

此状态码为以后保留。

10.4.4 403 禁止(Forbidden)

服务器理解请求,但是拒绝满足它。授权也没用,请求不应该重发。如果请求方法不是HEAD,并且服务器想要公布为啥请求不能满足,那么就应该在响应实体中描述拒绝的原因。如果服务器不想让客户端知道原因,那么可以用404状态码替换。

10.4.5 404 找不到(Not Found)

服务器找不到任何匹配Request-URI的资源。没有迹象表明这种情况是暂时的还是永久性的。通过一些内部可配机制,服务器了解到老资源已经永久失效并且没有转发地址,那么就应该响应410(消失)状态码。这个状态码通常用于服务器不希望明确地暴露为啥请求被拒绝或无其它响应可用的时候。

10.4.6 405 方法不允许(Method Not Allowed)

请求行中指定的方法不允许用于Request-URI标识的资源。响应必须包含一个Allow头字段,该字段包含一个适用于请求资源的可用方法列表。

10.4.7 406 不可接受(Not Acceptable)

请求标识的资源只能生成响应实体,根据请求中发送的accept头,这些实体具有不可接受的内容特征。

除非是HEAD请求,响应实体中应该包含可用实体特征和定位的列表,用户或者用户代理可以从这个列表中选择最合适的一个。Content-Type头字段中的媒体类型指定了实体的格式。根据用户代理的格式和能力,会自动的选择最合适的选项。然而,本规范没有给这种自动选择提供任何标准。

注意:根据请求中发送的Accept头字段,HTTP/1.1允许服务器返回不可接受的响应。在某些情况下,这甚至可能是发送406响应更好的方式。鼓励用户代理检查响应的头信息来确定响应是不是可接受的。

如果响应是不可接受的,用户代理应该临时性地暂停继续接受数据并且咨询用户如何进行下一步操作。

10.4.8 407 需要代理授权(Proxy Authentication Required)

本响应码与401很相似,但是它表明了客户端必须首先获取代理的授权。代理必须返回一个Proxy-Authenticate头字段(14.33节),这个字段包含了有关请求资源的对代理可用的认证。客户端可以用一个合适的Proxy-Authorization头字段重复这个请求。HTTP访问授权解释于"HTTP Authentication: Basic and Digest Access Authentication"21

10.4.9 408 请求超时(Request Timeout)

客户端不会在服务器准备好等待的时间内产生请求。客户端可以在随后不添加任何修改的情况下重复此请求。

10.4.10 409 冲突(Conflict)

由于与资源的当前状态冲突,请求无法完成。此状态码只有在预计用户能够解决冲突并重新提交请求的情况下才允许使用。响应体应该包含足够的信息以便用户能够认识到冲突来源。理想情况下,响应实体会包含足够的信息以便用户或用户代理解决这个问题;然而,这也许不太可能并且也不是必须的。

冲突最有可能发生在PUT请求的响应之中。例如,如果使用了版本控制,并且PUT实体中对资源的修改与更早的(第三方)请求做出的修改冲突,则服务器可以用409响应来表示无法完成请求。在这种情况下,响应实体很可能包含一个这两种版本间的差别列表,这个列表使用响应中Content-Type头字段定义的格式。

10.4.11 410 消失(Gone)

请求的资源在服务器不再可用并且转发地址未知。这种情况可以被永久考虑。具有链接编辑能力的客户端应该在获得用户同意之后删除Request-URI的引用。如果服务器不知道或没有能力决定这种情况是不是永久的,则状态码404应该用来替换使用。除非特殊说明,否则响应是可缓存的。

410响应主要打算用来帮助维持网站的任务,通过通知接受者请求的资源是有意的不可用的并且服务器主人期望到此资源的远程链接都被移除了。这种情况在限时、促销服务和在服务端失效的属于个体的资源来说是很普遍的。没有必要将所有永久过期的资源都标记为“gone”或者对标记保持任意长的时间 - - 这留给服务器主人自己考虑。

10.4.12 411 长度需要(Length Required)

服务端拒绝接受没有定义Content-Length的请求。客户端有可能在添加了包含请求消息中消息体长度的Content-Length头字段后重复此请求。

10.4.13 412 前提条件失败(Precondition Failed)

请求头字段给定的一个或多个前提条件在服务端验证的时候评价为false。此响应码允许客户端将前提条件放置在当前资源的元信息(头字段数据)中,并因此阻止了请求方法应用于除此资源以外的其它资源。

10.4.14 413 请求体太大

服务端拒绝处理请求,因为请求体比服务端想要的或能够处理的要大。服务端可能会关闭连接来阻止客户端继续发送请求。

如果这种情况是临时的,服务端在响应中应该包含一个Retry-After头字段来表明这是临时的并且客户端在这之后可以尝试重试。

10.4.15 414 Request-URI 太长

服务器拒绝服务请求,因为Request-URI的长度大于服务器期望解释的长度。这种罕见的情况只可能在以下几种情况下出现:

  • 客户端错误地使用了长查询信息将POST请求转换为GET请求;
  • 客户端已经降级为URI重定向“黑洞”(例如,重定向URI的前缀指定它自己的后缀);
  • 服务器受到攻击。客户端试图利用使用固定长度缓存来读取或操作Request-URI的服务器提供的安全漏洞。

10.4.16 415 不支持的媒体类型(Unsupported Media Type)

服务器拒绝服务该请求,因为请求实体的格式不受请求方法所请求的资源支持。

10.4.17 416 请求区间不满足

如果一个请求包含Range请求头(14.35节),并且这个字段中没有区间值匹配到请求资源的当前区间,同时请求中不包含If-Range请求头字段,那么服务器就应该用这个响应码返回响应。(对于字节区间,这意味着所有字节区间的第一个字节的长度都大于所有选择资源的当前长度。)

当此状态码是为byte-range请求返回的,则响应应该包含一个Content-Range头字段,该字段指定了所选资源(14.16节)的当前长度。此响应禁止使用multipart/byteranges的content- type。

10.4.18 417

此服务器无法满足Expect请求头字段(见第14.20节)中给出的期望,或者,如果服务器是代理,则服务器有明确的证据表明下一跳服务器无法满足请求。

10.5 5xx 服务器错误

以数字5开头的响应码表明服务器意识到自己发生了错误或者没有能力完成请求。除了响应HEAD请求之外,服务器响应中应该包含一个含有对错误详情的解释以及是否是临时还是永久情况的实体。用户代理应该将包含的任何实体内容展示给用户。这些响应码适用于所有的请求方法。

10.5.1 500 内部服务错误

服务器遇到一个阻止它完成请求的非预期的错误。

10.5.2 501 未实现

服务器不支持需要完成该请求的方法。对于服务器无法识别以及不能支持它适应于任何资源的方法,这是最合适的响应。

10.5.3 502 网关错误

作为网关或代理的服务器为了完成请求访问上游服务器时,收到了无效的响应。

10.5.4 503 服务不可用

由于服务器的临时过载或维护原因,当前服务器不能处理请求。它的含义就是也许过一段时间,这种临时情况可能会得到缓和。如果知道具体延迟时间,延迟的长度就应该在Retry-After头字段中表明。如果没有给定Retry-After头字段,则客户端应该像处理500响应一样对待此响应。

注意:503状态码的存在不是暗示当服务器过载的时候必须使用它。有些服务器可能会希望简单地拒绝连接。

10.5.5 504 网关超时

作为网关或者代理的服务器,在完成请求时没有及时地收到它需要访问的来自URI(例如:HTTP,FTP,LDAP)指定的上游服务器或一些其他辅助服务器(例如:DNS)的响应。

注意:小心实现者:一些已部署的代理被已知当DNS寻址超时时会返回400或500响应。

10.5.6 505 不支持的HTTP版本

服务器不支持或拒绝支持请求消息中使用的HTTP协议版本。除了使用此错误消息外, 服务器还表明了它不能或不愿意使用与客户端相同主版本号的协议来完成请求,参考3.1节的描述。响应中应该包含一个描述了为何版本不支持以及服务器支持哪些版本的实体。

11. 访问认证

HTTP提供了几种可选的应答(challenge-response)认证机制,服务器通过这些机制来认证客户端请求,客户端通过这些机制来提供认证信息。访问认证的一般框架,以及“basic”和“digest”认证的规范,都已经在“HTTP Authentication: Basic and Digest Access Authentication”21中指定。本规范从前规范中采取了“challenge”和“credentials”的定义。

12. 内容协商(Content Negotiation)

大多数HTTP响应都包含实体,实体中含有被人类用户解释的信息。自然而然地,向用户提供对应请求“最有效的”实体是极好的。但是不幸的是,对服务器和缓存来说,不是所有的用户对于“什么是最好的”都有相同的偏好,也不是所有的用户代理都有能力传递所有的实体类型。鉴于以上原因,HTTP为“内容协商”制定了几种机制 - - 当存在多种表述可用时,选择最好的代表的处理机制。

注意:这并不称为“格式协商”,因为替代表示可能是相同的媒体类型,但使用该类型的不同功能,使用不同的语言等。

任何包含实体的响应可能都从属于协商,包括错误响应。

存在有两种可能使用HTTP的内容协商:服务器驱动和代理驱动协商。这两种类型的协商是相互垂直的,因此它们可能分开或联合使用。联合使用的一种方法,被称为透明协商,当缓存使用源服务器提供的代理驱动协商信息以便为后续请求提供服务器驱动的协商时发生。

12.1 服务器驱动协商

如果响应中最好的表示的选择是由服务器上的算法决定的,就称之为服务器驱动协商。选择是基于响应中的可用的表示的(选择的度量基于它可变的内容,例如:语言,内容编码等等),和请求消息中独特的头字段或其它附属信息(例如客户端的网络地址)的内容。

当从可用表示中选择最好的表示的算法难以对用户代理描述时,或者服务器想要在第一次响应时发送给客户端“最好的猜测”(希望避免随后请求的来回延迟,如果这个“最好的猜测”对用户来说足够好),那么服务器驱动协商是有利的。为了改善服务器的“猜测”,用户代理可能会为这类响应包含一些描述了它的偏好的请求头,例如:Accept,Accept-Language,Accept-Encoding,etc。

服务器驱动协商劣势:

  1. 对于任何给定的用户,服务器不可能精确地决定什么才是“最好的”,因为这需要对用户代理的能力和响应的预计用法(例如,用户想要在屏幕上浏览或者在纸上打印?)两者的完整的认识。
  2. 在每个请求中描述用户代理的能力是非常低效的(考虑到只有一小部分响应具有多个表示形式),并且是对用户隐私的潜在侵害。
  3. 使源服务器的实现和生成对请求的响应算法的复杂化。
  4. 它可能限制公共缓存对多个用户请求使用相同响应的能力。

HTTP/1.1 包含了以下通过描述用户代理能力和用户偏好的请求头字段来启用服务器驱动协商:Accept(14.1节),Accept-Charset(14.2节),Accept-Encoding(14.3节),Accept-Language(14.4节)和User-Agent(14.43节)。然而,源服务器不受这些维度的限制,并且可以根据请求的任何方面更改响应,包括请求头字段外部的信息或扩展头字段中未被本规范定义的信息。

Vary头字段能够被用来表示服务器用来选择从属于服务器驱动协商的表述的参数。参考13.6节Vary头字段在缓存中的使用,参考14.44节Vary头字段在服务器中的使用。

12.2 代理驱动协商

使用代理驱动协商,用户代理从源服务器接收到第一手响应后就开始为响应选择最佳表述。选择是基于响应中可用表述的列表,包括处于头字段和初次响应实体体中的列表,这里的每一个表述都被它自己的URI唯一标识。选择可能是自动执行的(如果用户代理有能力这样做)或者用户手动地从菜单(也可能是超文本)中选择。

用户驱动协商在这些情况下是有利的:

  1. 响应的改变超出了常用维度(如类型、语言、编码);
  2. 源服务器通过检测请求不能决定用户代理的能力;
  3. 通常当公共缓存被用来分散服务器负载和减少网络使用;

代理驱动的协商的缺点是需要第二个请求才能获得最佳替代表示。第二个请求只有使用缓存才有效。此外,本规范没有定义任何支持自动选择的机制,不过它也并没有阻止任何类似的机制作为HTTP/1.1的扩展被开发和使用。

12.3 透明协商

透明协商是服务器驱动协商和用户代理协商两者的结合。当缓存是以响应中可用表述的列表的形式提供的(如同在代理驱动协商一样),并且变化的维度被缓存完全理解,那么缓存就能够为在同一资源上的随后请求中代表源服务器的利益执行服务器驱动协商。

透明协商的优点是分发原始服务器所需的协商工作,并且当缓存能够正确猜测正确响应时,还可以消除代理驱动协商的第二个请求延迟。

本规范没有为透明协商定义任何机制,尽管这样,它也没有阻止任何类似的机制作为HTTP/1.1的扩展被开发和使用。


  1. Hypertext Transfer Protocol – HTTP/1.1 ↩︎

  2. Masinter, L., Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0) , RFC 2324, 1 April 1998. ↩︎

  3. Request For Comments(RFC),是一系列以编号排定的文件。文件收集了有关互联网相关信息,以及UNIX和互联网社区的软件文件。由Internet Society(ISOC)赞助发行。 ↩︎

  4. Fielding, R., Gettys, J., Mogul, J., Frystyk, H. and T. Berners-Lee, Hypertext Transfer Protocol – HTTP/1.1 , RFC 2068, January 1997. ↩︎

  5. Berners-Lee, T., Fielding, R. and H. Frystyk, Hypertext Transfer Protocol – HTTP/1.0 , RFC 1945, May 1996. ↩︎

  6. MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型。它是一个互联网标准,扩展了电子邮件标准,使其能够支持多种类型的互联网文件。 ↩︎

  7. Bradner, S., Key words for use in RFCs to Indicate Requirement Levels, BCP 14, RFC 2119, March 1997. ↩︎

  8. 一种用来描述给定语言的形式化符号。 ↩︎

  9. US-ASCII. Coded Character Set - 7-Bit American Standard Code for Information Interchange. Standard ANSI X3.4-1986, ANSI, 1986. ↩︎

  10. Mogul, J., Fielding, R., Gettys, J. and H. Frystyk, Use and Interpretation of HTTP Verion Numbers, RFC 2145, May 1997. [jg639]. ↩︎ ↩︎

  11. Berners-Lee, T., Masinter, L. and M. McCahill, Uniform Resource Locators (URL) , RFC 1738, December 1994. ↩︎

  12. Sollins, K. and L. Masinter, Functional Requirements for Uniform Resource Names, RFC 1737, December 1994. ↩︎

  13. Berners-Lee, T., Fielding, R. and L. Masinter, Uniform Resource Identifiers (URI): Generic Syntax and Semantics, RFC 2396, August 1998. ↩︎ ↩︎

  14. Reynolds, J. and J. Postel, Assigned Numbers, STD 2, RFC 1700, October 1994. ↩︎

  15. Yergeau, F., UTF-8, a transformation format of Unicode and ISO-10646, RFC 2279, January 1998. ↩︎

  16. Internet Assigned Numbers Authority,一个分配和维护在互联网技术标准(或者称为协议)中的唯一编码和数值系统。 ↩︎

  17. Venkata N. Padmanabhan, and Jeffrey C. Mogul. Improving HTTP Latency , Computer Networks and ISDN Systems, v. 28, pp. 25-35, Dec. 1995. Slightly revised version of paper in Proc. 2nd International WWW Conference '94: Mosaic and the Web, Oct. 1994, which is available at http://www.ncsa.uiuc.edu/SDG/IT94/Proceedings/DDay/mogul/HTTPLatency.html. ↩︎

  18. S. Spero, Analysis of HTTP Performance Problems, http://sunsite.unc.edu/mdma-release/http-prob.html. ↩︎

  19. Nielsen, H.F., Gettys, J., Baird-Smith, A., Prud’hommeaux, E., Lie, H., and C. Lilley. Network Performance Effects of HTTP/1.1, CSS1, and PNG, Proceedings of ACM SIGCOMM '97, Cannes France, September 1997.[jg642]. ↩︎

  20. Joe Touch, John Heidemann, and Katia Obraczka. Analysis of HTTP Performance , , ISI Research Report ISI/RR-98-463, (original report dated Aug. 1996), USC/Information Sciences Institute, August 1998. ↩︎

  21. Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., Leach, P., Luotonen, A., Sink, E. and L. Stewart, HTTP Authentication: Basic and Digest Access Authentication , RFC 2617, June 1999. ↩︎ ↩︎ ↩︎

你可能感兴趣的:(Java)