摘要
- RFC 3391 把多个 MIME 消息切成带编号和长度的分块,再交错排列,使图片可以在根文档引用它的文字前后就近到达。
- 邻近只是生产者安排的发送顺序,不是接收端状态回执;格式看不见排版顺序、缓存上限、释放时点,也不能保证所有未完消息最终闭合。
设想一台扫描仪一页页读入文件,另一端的打印机希望边收边印。扫描仪只有做到某一页时,才知道这一页会引用哪些图片。若先把根文档完整发完,打印机可能要等很久才拿到图片;若把所有图片放在根文档之前,扫描仪又得提前知道全部内容,打印机也得长期保存尚未使用的资源。
问题不在图片或文字本身,而在复合对象如何排成一条字节流。multipart/related 用边界串隔开各个 body part,每一部分整体顺序排列。一个引用与它指向的部分,可能相隔很多字节。
2002 年 12 月发布的 Informational RFC 3391,把这段距离变成可调度对象。它定义 application/vnd.pwg-multiplexed:每个组件仍表示为 MIME 消息,但消息可以切成若干分块,不同消息的分块能够交错。生产者写到一处图片引用时,可以暂时停下根消息,把图片消息插到引用之前或之后,再继续根消息。
RFC 对“对象”和“表示”分得很清楚。复合对象、根组件和被引用组件属于抽象层;传输实体以及其中的消息,是这些抽象的字节表示。重组完成后的消息,应当与同一组件在 multipart/related 中对应的 body part 逐字节一致。新格式没有另造一套图片语义,而是重新安排既有消息的到达节奏。
每个普通分块以一行头部开始:CHK、消息编号、负载长度,以及 MORE 或 LAST。长度字段告诉接收端接下来读取多少字节,不再依赖 multipart 边界串。MORE 表示同一消息还有后续,LAST 表示这条消息在本块结束。不同编号可以穿插,但同一消息的片段顺序不能颠倒。
整条流还有两个特殊边界。第一个分块必须承载根消息的全部或一部分;它甚至可以是零长度,于是根消息真正的首字节仍可在后续出现。实体最后用 CHK 0 0 LAST 收尾,编号 0 专供这个终止分块使用。
看到终止标记,不等于此前所有承诺都已兑现。如果总终止块早于任何消息的最后一块,RFC 直接把接收端行为留为未定义。消息编号通常唯一,但并非绝对禁止复用;同一编号的前一条消息必须先以 LAST 结束,下一条才能开始。相邻两个分块也可以编号相同,零长度负载同样合法。这些余地照顾了写流程序,却没有降低重组规则的精确度。
Content-Type 上还必须带 type 参数,声明根消息的媒体类型。接收端无需先拆开根消息,就能知道复合对象的大类。若声明值与根消息自己的 Content-Type 不一致,行为未定义。组件之间可沿用 MHTML 中 Content-ID、Content-Location 与 URL 的关联方式;RFC 3391 并不负责定义关系本身。
第一类打印案例展示了这种交错的吸引力。生产者连续生成许多页面,遇到某张图片的第一次引用时,就把图片消息放在引用前不远处。打印机读到引用时,资源已经在手,不必等完整根文档结束。这里改善的是引用与资源之间的字节距离。
扫描案例可以把图片放在引用前,也可以紧随引用之后。如果先发引用,接收端先知道图片应该出现在哪里;随后到来的图片便可直接进入相应位置。生产端与消费端都不必攒齐整份作业才开始行动。
然而第三个案例把真正的未知暴露出来:排版由接收端决定。两张图片可能横向并列,文字也可能绕图。生产者看得见引用字符,却看不见对方页面宽度、字体度量、缓存容量和渲染策略。
若把多张并排图片按条带交错发送,内存足够时似乎很顺滑;一旦缓存耗尽,接收端留下的却是多幅图片彼此穿插的残片,哪一幅都难以完整输出。RFC 因而建议此时不要交错图片。格式提供能力,不代表每次使用都正确。
绕图文字更能说明边界。生产者若过早切断文字块,接收端可能要先保存整张图片,或提前结束文字环绕;若切得太晚,接收端又可能积压太多文字,甚至把图片放得比预期更后。文件建议宁可多发一些文字,因为文本通常比图片省存储。这是一条面向概率的策略,不是对接收端实际状态的观测。
IESG 注释因此放在 RFC 开头,构成使用前提:只有生产者充分了解消费者能力与限制时,这种媒体类型才合适。不同消费者可能在不同时间需要不同组件,而这条单向表示没有任何机制去发现具体需求。若消费者能力未知,应考虑 BEEP 之类能够双向通信的替代方案。
两种机制的差别是控制权。交错实体由生产者预测何时推送图片;双向协议允许消费者遇到引用后主动索取,甚至按自身排版请求图片条带。主动索取可能引入等待,预推送可能猜错。RFC 没有把一边描述成无条件胜者,而是标明速度与可见性之间的交换。
显示行为也不归分块头部决定。消费者若同时理解容器与根类型,会在复合对象上下文中展示组件,此时组件内的 Content-Disposition 对显示可能多余甚至误导,应被忽略。若只理解容器、不理解根类型,它可以隐藏整个实体,也可以把各消息当作 multipart/mixed 的附件处理。若连容器都不认识,就只能把实体视作不透明对象。
安全风险正是调度自由的背面。故障或恶意生产者可以让一条消息的两个分块相隔极多字节,甚至永远不给闭合块;可以同时打开大量消息;可以写下永远不会到达的引用;也可以先塞入许多被引用资源,让接收端长期不知道何时能释放。
这些输入不一定靠伪造单个分块。编号可以合规,长度可以准确,MORE 也可以在局部成立。危险来自许多局部承诺叠加后形成的全局存储义务。解析器证明“这一块读对了”,不能顺便证明“整条流占用有界”。
用 Lu Heng 的“最小初始规范”观察,RFC 3391 的共同层其实很克制:规定消息如何编号、如何计长、如何续接、根从哪里开始、实体在哪里结束。它没有统一接收端的排版与内存政策,因而本地实现仍可继续演化。
“运行代码优先”则要求把文件权威停在文件能够证明的位置。媒体类型完成注册,不等于真实打印机不会停顿;收到 LAST,不等于被引用图片已经正确显示。要回答这些问题,必须保存原始字节、引用图、缓存遥测与最终渲染结果。
RFC 3391 的历史价值,不是让生产者洞察接收端,而是把“靠近”做成一种明确的字节安排。图片可以紧挨引用到达,发送端仍然隔着一面看不透的玻璃。顺序得到了控制,需求并没有因此变得可见。
来源
会员简报
档案背景详情
使用相应会员等级登录,即可解锁完整简报与来源注释。
仅限 Strategic Circle
Strategic Circle
所有读者均可浏览。加入并登录后可解锁档案简报。
加入 Strategic Circle仅限 Leadership Alliance
Leadership Alliance
符合条件的 IP 资产所有者和管理层可登录查看 Leadership Alliance 简报。
加入 Leadership Alliance
