Prhub

#48973 [Bugfix] Accept RFC 2397 parameters in base64 data URLs

原始 PR 作者 thomas-fahrner-parasail 合并时间 2026-07-24 23:23 文件变更 2 提交数 4 评论 6 代码增减 +47 / -5

执行摘要

修复带 RFC 2397 参数的 base64 数据 URL 被拒绝的问题

PR body 指出 RFC 2397 明确允许在 mediatype 和 base64 标记间包含参数,而当前实现只识别第一个分号,导致合法数据 URL 被拒绝。客户端(如 charset 参数)无法发送多模态内容。

建议在遇到类似多模态输入问题时参考此 PR 的方法论:优先对齐 RFC 规范,同时在测试中覆盖常见偏差。该 PR 的解析改进和错误消息清晰化值得借鉴。

讨论亮点

review 中 DarkLight1337 询问 RFC 是否允许空白字符。cjackal 引用 RFC 指出不允许空白,且仅允许小写 base64。thomas-fahrner-parasail 补充了 WHATWG Fetch Standard 的宽松处理,但最终团队决定采用严格 RFC 语义以保持简单,thomas-fahrner-parasail 据此更新了实现与测试。

实现拆解

  1. 拆分数据 URL 头部:将 split(",", 1) 改为 partition(","),以正确检测缺失逗号的情况,并抛出 ValueError
  2. 识别 base64 标记:使用 rpartition(";") 从右向左查找 ;,确保 data:image/png;charset=utf-8;base64,... 中的 ;base64 被正确识别,中间参数被忽略。
  3. 清理媒体类型:使用 media_type.partition(";")[0] 去除媒体类型后的参数部分,避免将 image/png;charset=utf-8 等字符串传递给 load_base64
  4. 保留非 base64 路径:当 ; 不存在或 encoding != "base64" 时,仍抛出 NotImplementedError,保证不改写原有逻辑。
  5. 新增测试覆盖:在 test_connector.py 中添加 test_fetch_image_data_url_with_params 验证带参数 URL 的加载,以及 test_fetch_image_data_url_malformed 验证缺失逗号、非 base64、大小写不匹配等异常情况。
文件 模块 状态 重要度
vllm/multimodal/media/connector.py 多模态 modified 6.19
tests/multimodal/media/test_connector.py 测试 modified 5.73

关键符号

_load_data_url test_fetch_image_data_url_with_params test_fetch_image_data_url_malformed

关键源码片段

vllm/multimodal/media/connector.py core-logic

核心变更文件,修改了 `_load_data_url` 方法以支持 RFC 2397 参数并改进错误处理。

    def _load_data_url(
        self,
        url: str,
        media_io: MediaIO[_M],
    ) -> _M: # type: ignore[type-var]
        # Format per RFC 2397:
        # data:[<mediatype>][;<param>=<value>]*[;base64],<data>
        # 使用 partition 而不是 split,以检测缺少逗号的格式
        data_spec, sep, data = url[5:].partition(",")
        if not sep:
            msg = f"Invalid data URL {url[:32]!r}: missing ',' separator."
            raise ValueError(msg)
​
        # 使用 rpartition 取最后一个 ; 为 base64 标记,
        # 忽略中间可能存在的 RFC 2397 参数
        media_type, sep, encoding = data_spec.rpartition(";")
        if not sep or encoding != "base64":
            msg = "Only base64 data URLs are supported for now."
            raise NotImplementedError(msg)
​
        # 移除媒体类型中的参数部分(例如 charset=utf-8),
        # 避免将含参数的字符串传给 load_base64
        media_type = media_type.partition(";")[0]
        return media_io.load_base64(media_type, data)
tests/multimodal/media/test_connector.py test-coverage

新增测试覆盖带参数 base64 URL 和格式错误处理,确保修改的正确性和鲁棒性。

def test_fetch_image_data_url_malformed():
    # 测试多种不符合 RFC 2397 的 data URL 格式
    connector = MediaConnector()
​
    # 缺少逗号的分隔符
    with pytest.raises(ValueError, match="missing ','"):
        connector.fetch_image("data:image/png;base64")
​
    # 非 base64 的 data URL(普通百分号编码)
    with pytest.raises(NotImplementedError, match="base64"):
        connector.fetch_image("data:text/plain,hello")
​
    # ; 前面的 base64 标记缺少分号,被视为格式错误
    # "data:base64,..." 中 "base64" 位于媒体类型位置
    with pytest.raises(NotImplementedError, match="base64"):
        connector.fetch_image("data:base64,aGVsbG8=")
​
    # RFC 2397 规定 base64 标记必须为小写,无空格
    with pytest.raises(NotImplementedError, match="base64"):
        connector.fetch_image("data:image/png;BASE64,aGVsbG8=")
​
    with pytest.raises(NotImplementedError, match="base64"):
        connector.fetch_image("data:image/png; base64,aGVsbG8=")

评论区精华

是否应允许 base64 标记周围的空白字符? 设计

DarkLight1337 询问 RFC 是否允许额外空白字符,cjackal 引用 RFC 指出不允许,thomas-fahrner-parasail 补充了 WHATWG 的宽松处理,DarkLight1337 建议采用严格语法以保持简单。

结论:采用严格 RFC 语法:base64 标记必须为小写且无周围空白,不符合的均抛出 NotImplementedError。 · 已解决

风险与影响

本次变更仅影响多模态输入中的数据 URL 解析路径,非核心推理逻辑,风险较低。主要风险包括:边缘格式(如大小写、空格)可能被拒绝,但测试已明确覆盖这些场景,且严格语法符合 RFC 规范,避免了因接受非标准格式导致的潜在兼容性问题。没有引入新的依赖或性能开销。

影响范围仅限于通过 data: scheme 传递多模态内容的用户,之前被拒绝的带参数 base64 URL 现在可用。对系统性能无影响,对团队协作无影响,因为变更独立且测试充分。

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论