| RFC-0053:墓志铭 | |
|---|---|
| 状态 | 已接受 |
| 区域 |
|
| 说明 | 此提案的目标是允许服务器在关闭连接之前发送一条消息,指明关闭连接的原因。 |
| 作者 | |
| 提交日期(年-月-日) | 2018-07-19 |
| 审核日期(年-月-日) | 2018-10-04 |
“您的服务器位于此处”
摘要
此提案旨在允许服务器在关闭连接之前发送一条消息,指明连接关闭的原因。虽然规范中涵盖了墓志铭,但尚未实现。
设计初衷
目前,服务器无法以标准方式告知客户端连接已关闭的原因。这样一来,确保错误处理的责任就落到了开发者身上。开发者可以预见到这一点,并在消息中构建特殊的错误处理机制,也可以直接忽略错误处理(但这样可能会导致无法诊断的错误)。
一种使用情形是,服务器上的错误大多是致命的,发生错误时,与客户端的所有连接都会关闭。在这种情况下,开发者需要一种通用的错误报告机制,因为所有有效的方法调用都会因同一错误而终止。为每个方法声明潜在错误的替代方案会很麻烦且不方便。
此 FTP 的目标不是提供广泛的错误报告机制。具体而言,向连接的另一端传达大量详细信息(包括详细消息、进程状态或传播的原因)的能力不在本规范的范围内。
此 FTP 也无意定义一组通用错误代码。
设计
此提案修改了有线格式、源语言和第一类语言绑定。
Wire 格式
有线格式规范目前包含一个关于墓志铭的部分。相应部分将修订为如下内容:
Epitaph (Control Message Ordinal 0xFFFFFFFF)
An epitaph is a message with ordinal **0xFFFFFFFF**. A server may send an
epitaph as the last message prior to closing the connection, to provide an
indication of why the connection is being closed. No further messages may be
sent through the channel after the epitaph. Epitaphs are not sent from clients
to servers.
When a client receives an epitaph message, it can assume that it has received
the last message, and the channel is about to be closed. The contents of the
epitaph message explain the disposition of the channel.
The epitaph contains an error status. The error status of the epitaph is stored
in the reserved uint32 of the message header. The reserved word is treated as
being of type **zx_status_t**: negative numbers are reserved for system error
codes, positive numbers are reserved for application error codes, and ZX_OK is
used to indicate normal connection closure. The message is otherwise empty.
源语言
源语言规范目前包含一个关于墓志铭的部分。我们会及时更新。
一流的语言绑定
实现应考虑到以下事实:如果发送了 Epitaph 消息,则该消息应是关闭之前的最后一条消息;不同语言(例如,通过在 C/C++ 中传递错误代码、在 Rust 中传递 Result<t, e=""> 以及在 Dart 中传递异常)对错误的处理方式不同。</t,>
我们将向 C 绑定添加方法 fidl_epitaph_write(channel, zx_status_t),以及 fidl_epitaph_t 类型。
我们将向“原始绑定”部分中的 C 绑定添加以下文档:
fidl_epitaph_write
Declared in lib/fidl/epitaph.h, defined in epitaph.c.
This function sends an epitaph with the given error number down the given
channel. An epitaph is a special message, with ordinal 0xFFFFFFFF, which
contains an error code. The epitaph must be the last thing sent down the
channel before it is closed.
C 更改的 CL:https://fuchsia-review.googlesource.com/c/zircon/+/178250
我们将更改 C++ 绑定,以实现以下目的:
fidl::Binding 会在收到 Epitaph 时立即关闭通道。
开发者将能够使用 fidl::Binding::Close 关闭渠道
错误代码将传播到客户端使用 set_error_handler() 设置的错误处理程序。我们将添加一个新的 error_handler 变体,该变体接受一个闭包,该闭包接受一个表示错误代码的 int 变量,并移除现有的变体。未来的潜在工作包括提供“合理的默认”错误处理程序,但目前尚不清楚这会是什么。
来自此通道的任何待处理读取操作都将返回 ZX_ERR_PEER_CLOSED。
C++ 绑定的 CL:https://fuchsia-review.googlesource.com/c/garnet/+/177939
其他绑定需要更新,包括 Dart、Rust 和 Go。
文档和示例
文档将按照上一部分中的说明进行更新。
开发者指南
Epitaph 的目的是使服务器能够向客户端提供有关渠道处置和可能正在处理的请求的可操作信息。
本部分介绍了墓志铭的预期行为和用法。
墓志消息仅从服务器发送到客户端,绝不会反向发送。如果发送,则必须是服务器在关闭其通道端点之前发送给客户端的最后一条消息。
当客户端收到墓志铭消息时,必须立即关闭其通道端。不得尝试从可能由不合规的服务器实现发送的渠道读取任何其他消息。
如果客户端在未收到墓志铭的情况下观察到对等方已关闭,则必须像收到
ZX_ERR_PEER_CLOSED墓志铭一样继续执行;这两个状态在语义上是等效的。如果通道的关闭是协议达到指定成功结束状态的预期副作用,则服务器应发送
ZX_OK墓志铭。a. 示例:当客户端在表示单个数据库事务的接口上调用 Commit() 时,服务器应尝试应用所请求的更改。如果成功,服务器必须在关闭其通道端点之前发送
ZX_OK墓志铭。客户可能会合理地认为,ZX_OK墓志铭表明交易已成功提交。b. 反例:许多协议没有指定的成功结束状态;客户端希望能够连接到服务器并发出无限数量的请求,而不会观察到对等方关闭,直到客户端关闭其自己的通道端。在这些情况下,服务器关闭其通道端点构成异常结束状态,因此服务器绝不应发送
ZX_OK墓志铭。在因任何原因(协议达到其指定的成功结束状态除外)关闭通道的服务器端之前,服务器可能会发送非
ZX_OK墓志铭。我们建议采用以下惯例:a. 如果服务器因客户端向其发送格式错误的 FIDL 消息而关闭连接,则应发送
ZX_ERR_INVALID_ARGS墓志铭。b. 如果服务器因客户端发送的请求在其当前状态下无效而关闭连接,则应发送
ZX_ERR_BAD_STATE墓志铭。c. 如果客户端尝试通过服务发现机制连接到服务器时,服务器无法访问(例如无法启动),则该机制应发送
ZX_ERR_UNAVAILABLE墓志铭。(另请参阅此草图。)d. 如果服务器因并非由客户端执行的操作(例如关闭或内存不足)而无法继续提供协议服务,则不必发送任何墓志铭。如上所述,客户端会将此视为
ZX_ERR_PEER_CLOSED。e. 如果服务器遇到特定于应用的错误,则应发送应用定义的错误代码。例如,如果服务器控制着文件系统,而用户尝试执行不允许执行的写入操作,则服务器可能希望关闭连接并返回错误。
f. 该列表并不详尽。服务器可能会根据需要发送其他错误。与往常一样,建议 FIDL 作者清楚地记录其协议可能返回的错误,包括墓志铭。
向后兼容性
FIDL 文档目前指出,0x80000001 是哀悼消息的序号。我们将其更改为 0xFFFFFFFF,因为 0x80000001 正在被 IO 使用。 目前没有任何内容依赖于使用 0x80000001 的 Epitaph。否则,无需担心向后兼容性问题。
性能
不适用
安全
不适用。
测试
此功能的单元测试将添加到相应的 FIDL 绑定中。在每个受支持的 FIDL 绑定获得支持后,我们应扩充 FIDL 兼容性测试的集合。
缺点、替代方案和未知因素
我们曾考虑创建一个包含 Epitaph 事件的 System 接口,该接口将成为所有其他接口消息的父接口。仅凭墓志铭不足以证明需要进行如此大的更改。目前,实现这一目标还面临两个障碍。首先,派生类型目前无法使用,不过这种情况很快就会改变。接下来,由于此提案会更改运行时,而 FIDL 解析器 / 生成器依赖于运行时,因此引入系统消息并尝试在运行时中使用它会导致循环依赖。
此 FTP 导致的 API 变更不会阻止 Epitaph 支持移至未来的系统消息。
有人提出将一些墓志铭处理纳入源语言,从而允许将 zx_status 标志映射为 FIDL 定义的枚举。此问题已推迟到未来解决。
建议的实现存在竞态条件。如果一个线程在另一个线程关闭渠道的同时写入消息,则墓志铭可能会在另一个线程的消息之前写入,但在调用 zx_handle_close() 之前写入。替代方案包括锁定渠道或提供显式系统调用。我们先从线程不安全版本开始,以便进一步了解问题空间。