网络知识
RFC 精读 · Application · STD 97

RFC 9110

HTTP Semantics —— 所有 HTTP 版本共用的方法、状态码、字段与路由语义。报文怎么在线上排,不在这一篇。

编号 / 标题
RFC 9110 · HTTP Semantics
发布
June 2022 · R. Fielding, Ed. · M. Nottingham, Ed. · J. Reschke, Ed.
状态
Internet Standard · STD 97
关系
Obsoletes 281872307231723272337235753876157694 · Updates 3864 · 无 Updated by
原文
rfc-editor HTML · 纯文本 · datatracker
配套
缓存 9111 · HTTP/1.1 9112 · HTTP/2 9113 · HTTP/3 RFC 9114
读这一篇的目标:能按原文区分 safe 与 idempotent(safe ≠ 无副作用,幂等 ≠ 安全),说出 Host 和 :authority 谁在什么版本里 obligatory,并能把状态码第一位当成类、把未识别码归到 x00。
§9.1 Overview · Table 4

标准化方法一览

   This specification defines a number of standardized methods that are
   commonly used in HTTP, as outlined by the following table.

   +=========+============================================+=========+
   | Method  | Description                                | Section |
   | Name    |                                            |         |
   +=========+============================================+=========+
   | GET     | Transfer a current representation of the   | 9.3.1   |
   |         | target resource.                           |         |
   +---------+--------------------------------------------+---------+
   | HEAD    | Same as GET, but do not transfer the       | 9.3.2   |
   |         | response content.                          |         |
   +---------+--------------------------------------------+---------+
   | POST    | Perform resource-specific processing on    | 9.3.3   |
   |         | the request content.                       |         |
   +---------+--------------------------------------------+---------+
   | PUT     | Replace all current representations of the | 9.3.4   |
   |         | target resource with the request content.  |         |
   +---------+--------------------------------------------+---------+
   | DELETE  | Remove all current representations of the  | 9.3.5   |
   |         | target resource.                           |         |
   +---------+--------------------------------------------+---------+
   | CONNECT | Establish a tunnel to the server           | 9.3.6   |
   |         | identified by the target resource.         |         |
   +---------+--------------------------------------------+---------+
   | OPTIONS | Describe the communication options for the | 9.3.7   |
   |         | target resource.                           |         |
   +---------+--------------------------------------------+---------+
   | TRACE   | Perform a message loop-back test along the | 9.3.8   |
   |         | path to the target resource.               |         |
   +---------+--------------------------------------------+---------+

                                Table 4

[……All general-purpose servers MUST support GET and HEAD]

   All general-purpose servers MUST support the methods GET and HEAD.
   All other methods are OPTIONAL.

   The set of methods allowed by a target resource can be listed in an
   Allow header field (Section 10.2.1).  However, the set of allowed
   methods can change dynamically.  An origin server that receives a
   request method that is unrecognized or not implemented SHOULD respond
   with the 501 (Not Implemented) status code.  An origin server that
   receives a request method that is recognized and implemented, but not
   allowed for the target resource, SHOULD respond with the 405 (Method
   Not Allowed) status code.

注解

9110 收的是所有 HTTP 版本共用的语义,不是 HTTP/1.1 报文格式(那是 RFC 9112)也不是 HTTP/2 帧(RFC 9113)。7230 的语义进入 9110,HTTP/1.1 消息语法进入 9112。方法名大小写敏感,登记方法按惯例全大写。

通用源服务器 MUST 实现 GET 和 HEAD。未知或未实现的方法,origin SHOULD 回 501;已实现但不允许用于该目标资源,SHOULD 回 405。发 405 时 MUST 带 Allow(§9.1 / §10.2.1 / §15.5.6)。

§9.2.1 Safe Methods

安全方法:客户端没请求改状态

   Request methods are considered "safe" if their defined semantics are
   essentially read-only; i.e., the client does not request, and does
   not expect, any state change on the origin server as a result of
   applying a safe method to a target resource.  Likewise, reasonable
   use of a safe method is not expected to cause any harm, loss of
   property, or unusual burden on the origin server.

   This definition of safe methods does not prevent an implementation
   from including behavior that is potentially harmful, that is not
   entirely read-only, or that causes side effects while invoking a safe
   method.  What is important, however, is that the client did not
   request that additional behavior and cannot be held accountable for
   it.  For example, most servers append request information to access
   log files at the completion of every response, regardless of the
   method, and that is considered safe even though the log storage might
   become full and cause the server to fail.  Likewise, a safe request
   initiated by selecting an advertisement on the Web will often have
   the side effect of charging an advertising account.

   Of the request methods defined by this specification, the GET, HEAD,
   OPTIONS, and TRACE methods are defined to be safe.

   The purpose of distinguishing between safe and unsafe methods is to
   allow automated retrieval processes (spiders) and cache performance
   optimization (pre-fetching) to work without fear of causing harm.  In
   addition, it allows a user agent to apply appropriate constraints on
   the automated use of unsafe methods when processing potentially
   untrusted content.

   A user agent SHOULD distinguish between safe and unsafe methods when
   presenting potential actions to a user, such that the user can be
   made aware of an unsafe action before it is requested.

   When a resource is constructed such that parameters within the target
   URI have the effect of selecting an action, it is the resource
   owner's responsibility to ensure that the action is consistent with
   the request method semantics.  For example, it is common for Web-
   based content editing software to use actions within query
   parameters, such as "page?do=delete".  If the purpose of such a
   resource is to perform an unsafe action, then the resource owner MUST
   disable or disallow that action when it is accessed using a safe
   request method.  Failure to do so will result in unfortunate side
   effects when automated processes perform a GET on every URI reference
   for the sake of link maintenance, pre-fetching, building a search
   index, etc.

注解

原文把 safe 定义成:方法的规定语义本质上是只读——客户端不请求、也不期望源服务器因这次请求而改状态。本规范里 GET、HEAD、OPTIONS、TRACE 是 safe。

易误解:safe ≠ 实现没有副作用。规范紧接着写:这个定义并不阻止实现去做有害、非只读、或带副作用的事(写访问日志、广告计费)。关键是客户端没有请求那些额外行为,也不能为此负责

区分 safe / unsafe 是为了让爬虫和预取敢于自动发请求。把删除做成 GET /page?do=delete 是资源所有者的责任事故。

§9.2.2 Idempotent Methods

幂等:多发一次和只发一次,意图效果相同

   A request method is considered "idempotent" if the intended effect on
   the server of multiple identical requests with that method is the
   same as the effect for a single such request.  Of the request methods
   defined by this specification, PUT, DELETE, and safe request methods
   are idempotent.

   Like the definition of safe, the idempotent property only applies to
   what has been requested by the user; a server is free to log each
   request separately, retain a revision control history, or implement
   other non-idempotent side effects for each idempotent request.

   Idempotent methods are distinguished because the request can be
   repeated automatically if a communication failure occurs before the
   client is able to read the server's response.  For example, if a
   client sends a PUT request and the underlying connection is closed
   before any response is received, then the client can establish a new
   connection and retry the idempotent request.  It knows that repeating
   the request will have the same intended effect, even if the original
   request succeeded, though the response might differ.

   A client SHOULD NOT automatically retry a request with a non-
   idempotent method unless it has some means to know that the request
   semantics are actually idempotent, regardless of the method, or some
   means to detect that the original request was never applied.

   For example, a user agent can repeat a POST request automatically if
   it knows (through design or configuration) that the request is safe
   for that resource.  Likewise, a user agent designed specifically to
   operate on a version control repository might be able to recover from
   partial failure conditions by checking the target resource
   revision(s) after a failed connection, reverting or fixing any
   changes that were partially applied, and then automatically retrying
   the requests that failed.

   Some clients take a riskier approach and attempt to guess when an
   automatic retry is possible.  For example, a client might
   automatically retry a POST request if the underlying transport
   connection closed before any part of a response is received,
   particularly if an idle persistent connection was used.

   A proxy MUST NOT automatically retry non-idempotent requests.  A
   client SHOULD NOT automatically retry a failed automatic retry.

注解

幂等看的是多次相同请求对服务器的意图效果是否等于一次。本规范里 PUT、DELETE,以及所有 safe 方法,都是幂等的。POST 默认不是。

易误解:幂等 ≠ 安全。PUT 幂等但不安全(它请求改状态)。safe 方法一定幂等,反过来不成立。

幂等也不等于实现没有日志、没有版本历史。和 safe 一样,只约束“用户请求了什么”。通信在读到响应前失败时,客户端可以自动重试幂等方法;意图效果相同,不保证响应相同。代理 MUST NOT 自动重试非幂等请求。

§18.2 Table 7

IANA 方法登记:Safe / Idempotent

                 | Method  | Safe | Idempotent | Section |
                 +=========+======+============+=========+
                 | CONNECT | no   | no         | 9.3.6   |
                 +---------+------+------------+---------+
                 | DELETE  | no   | yes        | 9.3.5   |
                 +---------+------+------------+---------+
                 | GET     | yes  | yes        | 9.3.1   |
                 +---------+------+------------+---------+
                 | HEAD    | yes  | yes        | 9.3.2   |
                 +---------+------+------------+---------+
                 | OPTIONS | yes  | yes        | 9.3.7   |
                 +---------+------+------------+---------+
                 | POST    | no   | no         | 9.3.3   |
                 +---------+------+------------+---------+
                 | PUT     | no   | yes        | 9.3.4   |
                 +---------+------+------------+---------+
                 | TRACE   | yes  | yes        | 9.3.8   |
                 +---------+------+------------+---------+
                 | *       | no   | no         | 18.2    |
                 +---------+------+------------+---------+

                                  Table 7

注解

对照表:GET / HEAD / OPTIONS / TRACE = safe 且 idempotent;PUT / DELETE = 不安全但幂等;POST / CONNECT = 两者都不是。通配符 * 不能当方法名。

§7.2 Host and :authority

谁带主机名

   The "Host" header field in a request provides the host and port
   information from the target URI, enabling the origin server to
   distinguish among resources while servicing requests for multiple
   host names.

   In HTTP/2 [HTTP/2] and HTTP/3 [HTTP/3], the Host header field is, in
   some cases, supplanted by the ":authority" pseudo-header field of a
   request's control data.

     Host = uri-host [ ":" port ] ; Section 4

   The target URI's authority information is critical for handling a
   request.  A user agent MUST generate a Host header field in a request
   unless it sends that information as an ":authority" pseudo-header
   field.  A user agent that sends Host SHOULD send it as the first
   field in the header section of a request.

   For example, a GET request to the origin server for
   <http://www.example.org/pub/WWW/> would begin with:

   GET /pub/WWW/ HTTP/1.1
   Host: www.example.org

注解

用户代理 MUST 生成 Host,除非它改用 :authority 伪首部送同一份权威信息。HTTP/2 与 HTTP/3 里,Host 在部分情形被 :authority 取代;两者表达的是同一件应用层路由信息。

HTTP/1.1 没有 Host 的请求是不合格的。HTTP/2 客户端直接发请求时 MUST 用 :authority(没有权威信息则 MUST NOT 生成它),且 Host 不得与 :authority 指向不同实体——细则在 RFC 9113 §8.3.1。

§9.3.1 / §9.3.3 / §9.3.4

GET、POST、PUT 的意图差在哪

   The GET method requests transfer of a current selected representation
   for the target resource.  A successful response reflects the quality
   of "sameness" identified by the target URI (Section 1.2.2 of [URI]).
   Hence, retrieving identifiable information via HTTP is usually
   performed by making a GET request on an identifier associated with
   the potential for providing that information in a 200 (OK) response.

[……节选 GET 其余段落,下接 POST]

   The POST method requests that the target resource process the
   representation enclosed in the request according to the resource's
   own specific semantics.  For example, POST is used for the following
   functions (among others):

   *  Providing a block of data, such as the fields entered into an HTML
      form, to a data-handling process;

   *  Posting a message to a bulletin board, newsgroup, mailing list,
      blog, or similar group of articles;

   *  Creating a new resource that has yet to be identified by the
      origin server; and

   *  Appending data to a resource's existing representation(s).

[……节选 POST 响应语义,下接 PUT]

   The PUT method requests that the state of the target resource be
   created or replaced with the state defined by the representation
   enclosed in the request message content.  A successful PUT of a given
   representation would suggest that a subsequent GET on that same
   target resource will result in an equivalent representation being
   sent in a 200 (OK) response.  However, there is no guarantee that
   such a state change will be observable, since the target resource
   might be acted upon by other user agents in parallel, or might be
   subject to dynamic processing by the origin server, before any
   subsequent GET is received.  A successful response only implies that
   the user agent's intent was achieved at the time of its processing by
   the origin server.

[……节选 PUT 约束与校验器,下接与 POST 的对照]

   The fundamental difference between the POST and PUT methods is
   highlighted by the different intent for the enclosed representation.
   The target resource in a POST request is intended to handle the
   enclosed representation according to the resource's own semantics,
   whereas the enclosed representation in a PUT request is defined as
   replacing the state of the target resource.  Hence, the intent of PUT
   is idempotent and visible to intermediaries, even though the exact
   effect is only known by the origin server.

注解

GET 请求传送目标资源当前选定的表示,不是“下载服务器上那个文件”。同一 URI 的“同一性”由资源自己定义。

POST 请目标资源按它自己的语义处理请求里的表示:提交表单、发帖、创建尚未命名的资源、追加数据,都是合法例子。由哪一个状态码描述结果,也由这次处理决定。

PUT 请求用附带表示创建或替换目标资源的状态。成功的 PUT 暗示随后 GET 会得到等价表示,但不保证——别人可能并行改,服务器也可能动态加工。

对照句是考点:POST 的目标负责“处理”表示;PUT 的表示被定义为“替换目标状态”。所以 PUT 的意图幂等,且对中间件可见。

§15 / §18.3 Table 8

三位数字与五类

   The status code of a response is a three-digit integer code that
   describes the result of the request and the semantics of the
   response, including whether the request was successful and what
   content is enclosed (if any).  All valid status codes are within the
   range of 100 to 599, inclusive.

   The first digit of the status code defines the class of response.
   The last two digits do not have any categorization role.  There are
   five values for the first digit:

   *  1xx (Informational): The request was received, continuing process

   *  2xx (Successful): The request was successfully received,
      understood, and accepted

   *  3xx (Redirection): Further action needs to be taken in order to
      complete the request

   *  4xx (Client Error): The request contains bad syntax or cannot be
      fulfilled

   *  5xx (Server Error): The server failed to fulfill an apparently
      valid request

[……下接可扩展性与非法值]

   HTTP status codes are extensible.  A client is not required to
   understand the meaning of all registered status codes, though such
   understanding is obviously desirable.  However, a client MUST
   understand the class of any status code, as indicated by the first
   digit, and treat an unrecognized status code as being equivalent to
   the x00 status code of that class.

   For example, if a client receives an unrecognized status code of 471,
   it can see from the first digit that there was something wrong with
   its request and treat the response as if it had received a 400 (Bad
   Request) status code.  The response message will usually contain a
   representation that explains the status.

   Values outside the range 100..599 are invalid.  Implementations often
   use three-digit integer values outside of that range (i.e., 600..999)
   for internal communication of non-HTTP status (e.g., library errors).
   A client that receives a response with an invalid status code SHOULD
   process the response as if it had a 5xx (Server Error) status code.

[……下接 IANA 登记表 Table 8]

            | Value | Description                   | Section |
            +=======+===============================+=========+
            | 100   | Continue                      | 15.2.1  |
            +-------+-------------------------------+---------+
            | 101   | Switching Protocols           | 15.2.2  |
            +-------+-------------------------------+---------+
            | 200   | OK                            | 15.3.1  |
            +-------+-------------------------------+---------+
            | 201   | Created                       | 15.3.2  |
            +-------+-------------------------------+---------+
            | 202   | Accepted                      | 15.3.3  |
            +-------+-------------------------------+---------+
            | 203   | Non-Authoritative Information | 15.3.4  |
            +-------+-------------------------------+---------+
            | 204   | No Content                    | 15.3.5  |
            +-------+-------------------------------+---------+
            | 205   | Reset Content                 | 15.3.6  |
            +-------+-------------------------------+---------+
            | 206   | Partial Content               | 15.3.7  |
            +-------+-------------------------------+---------+
            | 300   | Multiple Choices              | 15.4.1  |
            +-------+-------------------------------+---------+
            | 301   | Moved Permanently             | 15.4.2  |
            +-------+-------------------------------+---------+
            | 302   | Found                         | 15.4.3  |
            +-------+-------------------------------+---------+
            | 303   | See Other                     | 15.4.4  |
            +-------+-------------------------------+---------+
            | 304   | Not Modified                  | 15.4.5  |
            +-------+-------------------------------+---------+
            | 305   | Use Proxy                     | 15.4.6  |
            +-------+-------------------------------+---------+
            | 306   | (Unused)                      | 15.4.7  |
            +-------+-------------------------------+---------+
            | 307   | Temporary Redirect            | 15.4.8  |
            +-------+-------------------------------+---------+
            | 308   | Permanent Redirect            | 15.4.9  |
            +-------+-------------------------------+---------+
            | 400   | Bad Request                   | 15.5.1  |
            +-------+-------------------------------+---------+
            | 401   | Unauthorized                  | 15.5.2  |
            +-------+-------------------------------+---------+
            | 402   | Payment Required              | 15.5.3  |
            +-------+-------------------------------+---------+
            | 403   | Forbidden                     | 15.5.4  |
            +-------+-------------------------------+---------+
            | 404   | Not Found                     | 15.5.5  |
            +-------+-------------------------------+---------+
            | 405   | Method Not Allowed            | 15.5.6  |
            +-------+-------------------------------+---------+
            | 406   | Not Acceptable                | 15.5.7  |
            +-------+-------------------------------+---------+
            | 407   | Proxy Authentication Required | 15.5.8  |
            +-------+-------------------------------+---------+
            | 408   | Request Timeout               | 15.5.9  |
            +-------+-------------------------------+---------+
            | 409   | Conflict                      | 15.5.10 |
            +-------+-------------------------------+---------+
            | 410   | Gone                          | 15.5.11 |
            +-------+-------------------------------+---------+
            | 411   | Length Required               | 15.5.12 |
            +-------+-------------------------------+---------+
            | 412   | Precondition Failed           | 15.5.13 |
            +-------+-------------------------------+---------+
            | 413   | Content Too Large             | 15.5.14 |
            +-------+-------------------------------+---------+
            | 414   | URI Too Long                  | 15.5.15 |
            +-------+-------------------------------+---------+
            | 415   | Unsupported Media Type        | 15.5.16 |
            +-------+-------------------------------+---------+
            | 416   | Range Not Satisfiable         | 15.5.17 |
            +-------+-------------------------------+---------+
            | 417   | Expectation Failed            | 15.5.18 |
            +-------+-------------------------------+---------+
            | 418   | (Unused)                      | 15.5.19 |
            +-------+-------------------------------+---------+
            | 421   | Misdirected Request           | 15.5.20 |
            +-------+-------------------------------+---------+
            | 422   | Unprocessable Content         | 15.5.21 |
            +-------+-------------------------------+---------+
            | 426   | Upgrade Required              | 15.5.22 |
            +-------+-------------------------------+---------+
            | 500   | Internal Server Error         | 15.6.1  |
            +-------+-------------------------------+---------+
            | 501   | Not Implemented               | 15.6.2  |
            +-------+-------------------------------+---------+
            | 502   | Bad Gateway                   | 15.6.3  |
            +-------+-------------------------------+---------+
            | 503   | Service Unavailable           | 15.6.4  |
            +-------+-------------------------------+---------+
            | 504   | Gateway Timeout               | 15.6.5  |
            +-------+-------------------------------+---------+
            | 505   | HTTP Version Not Supported    | 15.6.6  |
            +-------+-------------------------------+---------+

                                  Table 8

注解

合法范围 100–599。只看第一位分类:1 继续、2 成功、3 要再动作、4 请求有问题、5 服务器没完成一个看起来合法的请求。后两位没有再分类的规范角色。

客户端 MUST 理解“类”,不认识的码按该类的 x00 处理:471 当 400。100–599 以外的值非法,收到后 SHOULD 当 5xx。实现内部用 600–999 当库错误,那不是 HTTP 状态码。

reason phrase 只是建议,可以换成本地语言或省略。一次请求可以有零个或多个 1xx 中间响应,然后恰好一个最终响应。

表里 418 仍登记为 Unused(不再是“我是茶壶”玩笑码的规范位置)。421 是 Misdirected Request,和前面 Host / 权威信息不匹配那节对应。

抓包对照

语义字段落在请求行 / 状态行和首部里。HTTP/1.1 明文(或 TLS 解密后)最容易对上这一篇;HTTP/2 over HTTPS 的常见路径是 ALPN h2,解密后再把伪首部映射回同一套方法与状态码。明文 TCP 上的 h2 只能靠 prior knowledge(RFC 9113 §3.3),不能靠 Upgrade。

HTTP/1.1 请求行: METHOD SP request-target SP HTTP/1.1 GET /pub/WWW/ HTTP/1.1 Host: www.example.org HTTP/1.1 状态行: HTTP/1.1 SP 3DIGIT SP [reason] HTTP/1.1 200 OK HTTP/1.1 404 Not Found Wireshark: http.request.method == "GET" http.response.code == 304 http.host == "www.example.org" HTTP/2 over HTTPS(常见路径 ALPN h2,解密后): http2.header.name == ":method" http2.header.name == ":authority" http2.header.name == ":status" tcpdump: tcp port 80 或 tls 解密后再跟应用数据

考点与易错点

  1. safe 指客户端不请求、不期望状态变化;实现仍可写日志、计费。safe ≠ 无副作用。
  2. 幂等指多次相同请求的意图效果等于一次。PUT / DELETE 幂等但不安全;POST 默认两者都不是。
  3. 幂等不是“可以随便重试 POST”。代理 MUST NOT 自动重试非幂等请求。
  4. GET 传送当前选定表示,不是读磁盘文件。POST 按资源自己的语义处理表示;PUT 替换目标资源状态。
  5. 用户代理必须送 Host,除非改送 :authority。HTTP/2 / HTTP/3 里后者在部分情形取代前者。
  6. 状态码 100–599;不认识的码按该类 x00;范围外非法,客户端 SHOULD 按 5xx 处理。reason phrase 可省略。