ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Mosquitto 0.4.1 发布解读:修复新订阅场景下保留消息查找的主题匹配正则缺陷

Mosquitto 0.4.1 发布解读:修复新订阅场景下保留消息查找的主题匹配正则缺陷 Mosquitto 0.4.1 发布解读修复新订阅场景下保留消息查找的主题匹配正则缺陷【免费下载链接】mosquittoEclipse Mosquitto - An open source MQTT broker项目地址: https://gitcode.com/gh_mirrors/mos/mosquittoEclipse Mosquitto 0.4.1 是 2010-01-12 发布的一个纯缺陷修复bugfix版本其唯一改动是修复了客户端建立新订阅时用于查找应下发的保留消息retained message的正则表达式。版本发布公告 原文只有一句话但这一行改动背后牵涉 MQTT 代理最核心的机制之一——保留消息在新订阅时的即时投递以及早期 Mosquitto 用正则做主题匹配的历史实现。本文结合仓库中保留至今的旧正则文档、当时的 ChangeLog 记录与现代源码实现完整还原这次修复的技术背景、问题根源与演进脉络。一、发布背景一次聚焦的 bugfixChangeLog.txt 中对 0.4.1 的记录与发布公告完全一致0.4.1 - 20100112Fix regex used for finding retained messages to send on new subscription.结合 ChangeLog 上下文可以还原当时的版本节奏0.4 于 20100105 发布引入了与#通配符订阅支持、全非阻塞网络 I/O、$SYS消息统计等一批大改动一周后推出的 0.4.1 只包含上述这一项正则修复。也就是说这次修复直接服务于 0.4 新增的通配符订阅能力——0.4 之前代理不支持/#而新订阅时匹配并下发保留消息的逻辑必须与新的通配符语义对齐这正是 0.4.1 修补的薄弱点。二、修复对象新订阅时的保留消息投递MQTT 协议规定当客户端订阅某个主题过滤器时代理如果保存着匹配该过滤器的保留消息应立即把该消息以及主题作为正常发布消息推送给订阅者。0.4.1 修复的正则正是用于这一场景——在客户端建立新订阅时从保留消息库中找出所有应下发的消息。这一机制在现代版本中依然存在对应 src/retain.c 中的retain__queue()入口订阅建立后代理将订阅过滤器sub-topic_filter分词然后在保留消息树中查找匹配的分支把命中的保留消息逐条排入该客户端的外发队列db__message_insert_outgoing。其调用链为retain__queue()入口将过滤器用sub__topic_tokenise拆分为层级令牌retain__search()递归遍历保留消息分层结构对#、与精确层级分别处理retain__process()对每条命中的保留消息执行 ACL 检查、QoS 协商后投递。在现代实现中这个查找过程完全不依赖正则表达式而是基于按/分层 哈希子节点的数据结构遍历。而在 0.4.x 早期版本中同样的逻辑由一段按订阅过滤器动态生成的正则来完成——这正是 0.4.1 修复的对象。三、旧正则的完整结构与问题分析仓库 doc/historical/old-regex.txt 完整保留了这个被替换的旧正则及其逐行注释作者特意注明仅作存档复现。这是还原 0.4.1 修复内容的最直接证据。对于发布主题a/b/c旧实现生成的正则为^(?:(?:(a|\)(?!$))(?:(?:/(?:(b|\)(?!$)))(?:(?:/(?:c|\))|/#)?|/#)?|#)$从 doc/historical/old-regex.txt 的注释可以看出它的构造思路用^...$锚定整个主题串要求从字符串开头匹配到结尾每一级层级对应一个分支(a|\)(?!$)表示匹配字面层级a或单层通配但不能在行尾末尾层级允许c或也允许以/#结尾任何一级都可以提前用/#或直接#表示匹配剩余所有层级。结合正则结构可以推断这套旧方案存在几类明显弱点层级数硬编码。正则按Level 1 / Level 2 / Level 3逐层手工展开发布主题每多一层就需要在匹配逻辑里追加一层分支。主题层级较多时生成的正则要么不匹配、要么需要动态拼接出指数级组合难以维护且易出错。边界与嵌套组合易出漏洞。(?!$)负向前瞻用于禁止通配符停在行尾但/#、后紧跟/、foo/#与foo之间的边界语义必须逐条手工保证通配符层级增多后中间层是否也必须消耗一个实际层级#与相遇时谁优先等组合情况极易出现漏匹配或误匹配。0.4 刚引入通配符订阅这类边界在新订阅下发保留消息路径上正是最容易踩坑的地方。性能开销。每条发布主题 / 订阅过滤器都要先拼接再编译正则对代理的热路径消息路由是额外负担。0.4.1 的修复即针对此——修正后的正则或对匹配逻辑的调整保证了带通配符的订阅过滤器在检索保留消息时行为正确但具体补丁源码已不在当前仓库中其后续演进方向则在doc/historical/old-regex.txt的存档性质与后续版本源码中得到印证正则方案最终被彻底放弃。四、演进结果从正则到确定性字符匹配虽然 0.4.1 只修复了正则本身的缺陷但这段历史促成了 Mosquitto 后来对主题匹配机制的彻底重构。当前仓库中可以看到两条平行的现代实现均不再使用正则1. 发布路由侧——主题树遍历src/retain.c 的retain__search()用递归 哈希表子节点的方式实现#//精确三层语义。例如对#过滤器遍历当前节点的全部子分支并继续递归对则遍历子分支但只深入一层对精确层则HASH_FIND直接定位。retain__clean_empty_hierarchy()还会在保留消息被清除时反向回收空分支避免树结构膨胀。2. 匹配判定侧——字符级状态机libcommon/topic_common.c 中topic_matches_sub()及其公开接口mosquitto_topic_matches_sub2()libcommon/topic_common.c用逐字符扫描完成主题与过滤器的匹配遇到跳过直到下一个/遇到#则要求其必须是过滤器最后一个字符并匹配剩余全部内容同时显式校验foo、foo#、#foo、foo/#/bar等非法过滤器位置。同一文件中的mosquitto_sub_topic_check()在订阅建立阶段即拒绝这类非法过滤器libcommon/topic_common.c。这种确定性算法相比旧正则方案匹配语义完全由代码分支显式定义不再有正则边界歧义发布主题每一层只被扫描一次无正则编译与回溯开销非法过滤器在入口被拦截而非等到匹配时才暴露问题。五、正确性的持续保障测试与模糊测试正则缺陷的历史教训促使项目为主题匹配建立了严密的验证体系这也为读者提供了可自行复现的验证路径行为测试test/broker/03-pattern-matching.py 覆盖了含通配符的主题匹配在真实代理上的订阅与发布行为单元/模糊测试fuzzing 目录下的 libcommon_fuzz_topic_matching.cpp 将主题与过滤器组合作为模糊测试输入持续探测匹配逻辑的边界此外还有libcommon_fuzz_sub_topic_check2.cpp、libcommon_fuzz_pub_topic_check2.cpp分别对订阅过滤器校验与发布主题校验做模糊测试历史存档doc/historical/old-regex.txt 保留旧正则全文作为对比新旧实现差异的一手资料。六、结语Mosquitto 0.4.1 在项目历史上是一次体量极小但意义清晰的发布它修掉了 0.4 引入通配符订阅后、新订阅检索保留消息路径上的正则匹配缺陷。以今天的眼光看这次修复更像一个分水岭——正则方案在处理层级化、带通配符的 MQTT 主题时暴露出的边界与性能问题促使项目后续以分层数据结构 确定性字符匹配彻底取代了正则匹配并配套建立起测试与模糊测试防线。对想深入理解 MQTT 代理路由实现的读者而言沿着 doc/historical/old-regex.txt旧方案→ src/retain.c保留消息树→ libcommon/topic_common.c匹配算法这条线索阅读恰好可以完整走一遍这段演进史。【免费下载链接】mosquittoEclipse Mosquitto - An open source MQTT broker项目地址: https://gitcode.com/gh_mirrors/mos/mosquitto创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表