Prhub

#54999 [Rust Frontend] Add support for TLS in render server

原始 PR 作者 zdtsw 合并时间 2026-09-04 05:56 文件变更 5 提交数 4 评论 18 代码增减 +215 / -64

执行摘要

为 Rust 渲染服务器添加 TLS 支持,并重构共享 SSL 参数以提升可维护性。

根据 PR body 的描述,动机是为了匹配 Python vllm launch render 的 TLS 接口,使 Rust 渲染服务器(vllm-rs render)能够支持 HTTPS 和 mTLS,从而允许用户在不丢失任何 TLS 选项的情况下,可以在 Python 和 Rust 实现之间切换。此前 Rust 渲染服务器仅支持明文 HTTP。

这是一个设计良好、完成度高的功能添加 PR。它解决了实际功能缺失问题,并通过重构提升了代码质量。建议精读,重点关注:

1) SslArgs 如何通过 clap 和 serde 实现共享;
2) render.rs 中如何利用现有的 TLS 基础设施接入新的服务器。自动化审查提出的两点建议(错误格式化和测试快照)是合理的改进点,可在后续 PR 中跟进。

讨论亮点

CodeRabbit 自动化审查提出了两点 P1 级别的建议:

  1. 错误格式化:在测试 render_args_reject_tls_without_certificate 和 render_args_reject_client_cert_verification_without_ca 中,直接使用 error.to_string() 断言错误信息不符合 Rust 项目的错误处理规范(应使用 thiserror-ext 的 AsReport)。作者未在此 PR 中修改,可能留待后续统一处理。
  2. 测试完整性:测试 render_args_build_config_with_tls 仅对 TlsConfig 的 cert_file 和 key_file 字段进行断言,而没有对 ca_certs、cert_reqs 和 ciphers 等字段进行快照测试,遗漏了潜在的回归。维护者 BugenZhao 在评审中对此未做直接回应,但采纳了对文档示例命令的修改建议。
    维护者决策:维护者 BugenZhao 识别出了代码重复问题,主动推送了一个提交将共享的 SSL 参数逻辑提取到 SslArgs 结构体中,这是一个重要的设计优化。他同时修正了 README 中示例命令的错误(vllm -> vllm-rs)。

实现拆解

  1. 定义共享 TLS 参数结构体:在 rust/src/cmd/src/cli/ssl.rs 中新建 SslArgs 结构体,统一定义了 --ssl-certfile、--ssl-keyfile、--ssl-ca-certs、--ssl-cert-reqs 和 --ssl-ciphers 这五个 CLI 标志,并提供了 tls_config() 方法,用于将参数转换为 TlsConfig 对象(若未设置任何参数则返回 None)。此结构体实现了 Args、Default、Deserialize 等 trait,以支持 clap 和 serde。
  2. 重构现有 CLI 参数:在 rust/src/cmd/src/cli.rs 中,将原本定义在 SharedRuntimeArgs 中的五个 ssl_* 字段替换为使用 #[command(flatten)] 注解的 SslArgs 共享结构体。同时,为 RenderArgs 也添加了同样的 ssl: SslArgs 字段。RenderArgs::into_config() 和 SharedRuntimeArgs::into_bootstrapped_config() 等方法中构建 TLS 配置的逻辑被简化为调用 self.ssl.tls_config()。
  3. 在渲染服务器中接入 TLS:修改 rust/src/server/src/render.rs。首先为 RenderConfig 结构体添加了 tls: Option<TlsConfig> 字段。在 RenderConfig::validate() 方法中增加了对 TLS 配置的校验(如果存在)。在核心的 serve_render 函数中,从配置中提取 tls,调用 tls::build_server_config() 构建 OpenSSL 上下文,并将 Listener 包装在 MaybeTlsListener 中,以支持明文或 TLS 模式。日志中也增加了协议(http/https)的显示。
  4. 补充测试与文档:在 rust/src/cmd/src/cli/tests.rs 中,将原有的 render_args_build_supported_config 测试重命名为 render_args_build_config_without_tls 并增加了对 config.tls.is_none() 的断言。同时,新增了三个测试用例,分别验证:提供 --ssl-certfile 和 --ssl-keyfile 时 TLS 配置正确生成;仅提供 --ssl-keyfile 时验证失败;以及在没有 --ssl-ca-certs 的情况下请求客户端证书验证(--ssl-cert-reqs 2)时验证失败。在 rust/README.md 中为渲染服务器添加了 HTTPS 使用示例。
文件 模块 状态 重要度
rust/src/cmd/src/cli/ssl.rs CLI 参数 added 7.7
rust/src/cmd/src/cli.rs CLI 参数 modified 7.35
rust/src/server/src/render.rs 渲染服务器 modified 6.59
rust/src/cmd/src/cli/tests.rs CLI 测试 modified 8.55
rust/README.md 项目文档 modified 2.69

关键符号

SslArgs::tls_config RenderArgs::into_config SharedRuntimeArgs::tls_config RenderConfig::validate serve_render

关键源码片段

rust/src/cmd/src/cli/ssl.rs core-logic

新增的核心文件,定义了用于 CLI 和 serde 的共享 TLS 参数结构体 `SslArgs` 及其构建逻辑。

// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright contributors to the vLLM project
​
//! TLS CLI options mirroring Python's uvicorn `ssl_*` arguments, shared by the
//! OpenAI frontend and the render server.
​
use clap::Args;
use serde::Deserialize;
use vllm_server::TlsConfig;
​
/// TLS options mirroring Python's uvicorn `ssl_*` arguments, shared by the
/// OpenAI frontend and the render server.
#[derive(Clone, Debug, Default, Args, PartialEq, Eq, Deserialize)]
pub struct SslArgs {
    /// The file path to the SSL key file. When omitted, the key is read from
    /// `--ssl-certfile` (combined PEM).
    #[arg(long)]
    #[serde(default)]
    pub ssl_keyfile: Option<String>,
​
    /// The file path to the SSL cert file. Enables TLS when set.
    #[arg(long)]
    #[serde(default)]
    pub ssl_certfile: Option<String>,
​
    /// The CA certificates file used to verify client certificates (mTLS).
    #[arg(long)]
    #[serde(default)]
    pub ssl_ca_certs: Option<String>,
​
    /// Whether a client certificate is required: 0 = none, 1 = optional,
    /// 2 = required (mirrors Python's `ssl.CERT_*`).
    #[arg(long, default_value_t = 0, value_parser = clap::value_parser!(i32).range(0..=2))]
    #[serde(default)]
    pub ssl_cert_reqs: i32,
​
    /// OpenSSL cipher string for HTTPS (TLS 1.2 and below).
    /// When unset, the linked OpenSSL's default suites are used.
    #[arg(long)]
    #[serde(default)]
    pub ssl_ciphers: Option<String>,
}
​
impl SslArgs {
    /// Build the TLS config: `Some` when any `ssl_*` argument is set, else
    /// `None` (plaintext). The combination is validated in [`TlsConfig::validate`].
    pub fn tls_config(&self) -> Option<TlsConfig> {
        // 判断是否有任何 TLS 参数被设置,以此决定是否启用 TLS。
        let tls_requested = self.ssl_certfile.is_some()
            || self.ssl_keyfile.is_some()
            || self.ssl_ca_certs.is_some()
            || self.ssl_cert_reqs != 0
            || self.ssl_ciphers.is_some();
        tls_requested.then(|| TlsConfig {
            cert_file: self.ssl_certfile.clone(),
            key_file: self.ssl_keyfile.clone(),
            ca_certs: self.ssl_ca_certs.clone(),
            cert_reqs: self.ssl_cert_reqs,
            ciphers: self.ssl_ciphers.clone(),
        })
    }
}
rust/src/cmd/src/cli.rs core-logic

主 CLI 定义文件,将新的共享 `SslArgs` 集成到 `RenderArgs` 和 `SharedRuntimeArgs` 中,并简化了 TLS 配置构建逻辑。

// ... ( 文件头部 )
mod ssl; // 新增模块声明
// ... ( 导入 )
use crate::cli::ssl::SslArgs;
​
// RenderArgs 结构体中新增了共享的 SslArgs
pub struct RenderArgs {
    // ... 其他字段
    /// TLS options for HTTPS/mTLS.
    #[command(flatten)]
    ssl: SslArgs,
}
​
impl RenderArgs {
    pub(super) fn into_config(self) -> RenderConfig {
        // 调用 SslArgs 的方法获取 TLS 配置
        let tls = self.ssl.tls_config();
        RenderConfig {
            // ... 其他字段赋值
            tls, // 将 TLS 配置传入 RenderConfig
        }
    }
}
​
// SharedRuntimeArgs 结构体中,原来的五个 ssl_* 字段被替换为:
pub struct SharedRuntimeArgs {
    // ... 其他字段
    /// TLS options for HTTPS/mTLS.
    #[command(flatten)]
    #[serde(default, flatten)]
    pub ssl: SslArgs, // 共享的 TLS 参数结构体
    // ...
}
​
impl SharedRuntimeArgs {
    // 在构建配置的方法中,使用 self.ssl.tls_config() 获取 TLS 配置
    fn into_bootstrapped_config(...) -> Config {
        // ...
        let tls = self.ssl.tls_config(); // 简化了构建逻辑
        // ...
    }
}
rust/src/server/src/render.rs dependency-wiring

渲染服务器的实现入口,将 TLS 配置实际接入服务器的启动、验证和监听流程。

use crate::{
    HttpListenerMode, TlsConfig,
    listener::{Listener, MaybeTlsListener},
    tls,
};
​
// RenderConfig 结构体新增了可选的 TLS 配置字段
pub struct RenderConfig {
    // ... 其他字段
    pub tls: Option<TlsConfig>,
}
​
impl RenderConfig {
    pub fn validate(&self) -> Result<()> {
        // ... 其他验证
        // 新增:对存在的 TLS 配置进行校验(如证书文件是否可读等)
        if let Some(tls) = &self.tls {
            tls.validate()?;
        }
        Ok(())
    }
}
​
pub async fn serve_render(config: RenderConfig, shutdown: CancellationToken) -> Result<()> {
    config.validate().context("invalid render server configuration")?;
    // 新增:构建 TLS 上下文
    let tls_config = config
        .tls
        .as_ref()
        .map(tls::build_server_config) // 复用已有的构建函数
        .transpose()
        .context("invalid TLS configuration")?;
    // ... 构建状态、绑定 TCP 监听器
    let listener = Listener::bind(&listener_mode).await?;
    // 新增:根据是否有 TLS 上下文,选择明文或 TLS 包装
    let scheme = if tls_config.is_some() { "https" } else { "http" };
    let listener = match tls_config {
        Some(context) => MaybeTlsListener::tls(listener, context), // 包装为 TLS 监听器
        None => MaybeTlsListener::plain(listener), // 保持明文
    };
    info!(
        %address,
        scheme, // 日志中记录协议类型
        model = %config.model,
        "starting engine-free Rust render server"
    );
    // ... 启动 axum 服务器
}

评论区精华

错误格式化不一致 style

Codex 审查指出测试中使用 `error.to_string()` 不符合项目 Rust 错误处理规范(AGENTS.md),应使用 `thiserror-ext` 提供的格式化方法。

结论:未在本次 PR 中解决,可能留待后续统一重构。 · 待处理

测试覆盖不完整 测试

Codex 审查指出 `render_args_build_config_with_tls` 测试只验证了 `cert_file` 和 `key_file`,而 `ca_certs`、`cert_reqs` 和 `ciphers` 字段未被覆盖。

结论:维护者未直接评论,但这是一个合理的测试改进建议。 · 待处理

文档示例命令错误 documentation

Codex 审查发现 README 中新增的 TLS 示例使用了 `vllm render`,但实际 Rust 命令应为 `vllm-rs render`。维护者 BugenZhao 提出了修改建议。

结论:维护者已将示例命令修正为 `vllm-rs`。 · 已解决

代码重构:提取共享 SslArgs 设计

维护者 BugenZhao 在评审后主动推送了一个提交,将 `SharedRuntimeArgs` 和 `RenderArgs` 中重复的 SSL 参数定义和构建逻辑提取到独立的 `SslArgs` 结构体中,提升了可维护性。

结论:重构被采纳,成为 PR 的一部分,优化了代码结构。 · 已解决

风险与影响

  1. 回归风险:对 SharedRuntimeArgs 的重构(将 ssl_* 字段替换为 SslArgs)影响了 vllm serve 和 vllm frontend 命令的参数解析和 JSON 反序列化。虽然测试中通过 expect_test 快照验证了结构变化,但这种涉及序列化格式的变更仍需谨慎,确保与 Python 监督进程的交互协议不变。从代码看,serde 的 flatten 属性应能保持 JSON 兼容性。
  2. 安全风险:引入了新的 TLS 配置路径。虽然核心的 tls::build_server_config 和 TlsConfig::validate 是复用的,但需确保 render 服务器的实现(在 render.rs 中)正确使用了构建好的上下文,没有配置错误。代码显示实现了标准流程(构建上下文 -> 包装监听器),风险较低。
  3. 兼容性风险:RenderArgs 的新增字段 ssl 会影响其 Debug 输出,但这是内部结构变化,不影响用户接口。关键在于确保 CLI 参数的变更(从独立的 --ssl-* 标志到嵌套在 SslArgs 中)不会破坏现有的调用脚本。由于 #[command(flatten)] 使得 CLI 参数在用户视角保持不变,此风险极低。
  1. 用户影响:直接惠及需要安全通信的 Rust 渲染服务器用户。他们现在可以使用与 Python 版本完全相同的 TLS 选项来配置 HTTPS 和客户端证书验证,实现了部署的灵活性和迁移的平滑性。
  2. 系统影响:增强了 Rust 前端(vllm-rs)作为独立部署组件的能力。渲染服务器不再局限于本地或受信任网络,可以安全地暴露在公网上,扩大了其使用场景。
  3. 团队影响:重构消除了 SharedRuntimeArgs 和 RenderArgs 之间关于 SSL 参数的重复定义,提升了代码的可维护性。未来对 TLS 参数的修改只需在 SslArgs 一处进行。同时,它展示了如何在 Rust 前端中系统地添加新特性(定义参数 -> 接入服务器逻辑 -> 补充测试文档)。
序列化格式变更 TLS 配置路径新增

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论