1
0
Files
DiAL/openspec/specs/probe-engine/spec.md
lanyuanxiaoyao bce0f8e7a8 feat: 增强 HTTP checker 鲁棒性 — 严格配置校验、完整耗时、流式body、重定向与编码完善
启动期校验: 新增 validate.ts 对 HTTP config/expect/body rule/operator 全方位严格校验
执行语义: body 改为 Web Stream 流式超限中止,durationMs 覆盖完整执行
错误归属: status/header 失败不读 body,phase 分层 request/body,early duration skip body
重定向: 跟随前释放 body,POST/303 改 GET 清理 header,跨 origin 剥离敏感 header
编码: 支持 quoted charset,未知编码返回结构化解码错误
文档: README match→regex+durationMs,DEVELOPMENT 执行流程与错误归属
测试: +63 测试覆盖全部新增场景,325 pass 0 fail
规格: 同步 probe-config/probe-engine/expect-body-checkers 3 个 delta spec
2026-05-13 08:00:05 +08:00

13 KiB
Raw Blame History

Purpose

定义拨测调度引擎的行为:按 interval 分组定时、组内并发拨测、expect 结果校验和结果持久化。

Requirements

Requirement: 按 interval 分组调度

系统 SHALL 将拨测目标按 interval 值分组,每组使用独立的定时器进行调度。

Scenario: 相同 interval 的目标共享定时器

  • WHEN 多个 target 配置了相同的 interval如 30s
  • THEN 系统 SHALL 使用同一个 setInterval 定时器,每次 tick 并发拨测所有该组目标

Scenario: 不同 interval 的目标各自调度

  • WHEN target A 配置 15s intervaltarget B 配置 30s interval
  • THEN 系统 SHALL 创建两个独立定时器,分别按各自频率调度

Requirement: 组内并发拨测

系统 SHALL 在每次调度 tick 时并发执行同组内目标的检查,但实际同时运行的检查数 MUST 受全局 runtime.maxConcurrentChecks 限制。

Scenario: 同组目标并发执行

  • WHEN 调度器触发一次 tick该组有 3 个目标,且全局并发余量至少为 3
  • THEN 系统 SHALL 同时执行 3 个 checker而非顺序执行

Scenario: 单个目标失败不影响同组其他目标

  • WHEN 同组中某个目标的检查请求超时或失败
  • THEN 其他目标的检查 SHALL 正常完成并记录结果

Scenario: 全局并发限制生效

  • WHEN 调度器同时触发 10 个目标且 runtime.maxConcurrentChecks 为 3
  • THEN 系统 MUST 同时最多运行 3 个检查,其余检查等待并发槽位释放

Requirement: HTTP 拨测执行

系统 SHALL 对 type: http 的目标执行 HTTP 请求,支持 GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS 方法,并携带 http.headershttp.body。系统 SHALL 支持 http.ignoreSSL 配置跳过 SSL 证书校验,支持 http.maxRedirects 配置控制重定向行为。HTTP response body 读取 SHALL 受 http.maxBodyBytes 流式限制,重定向跟随 SHALL 释放被跟随响应的 body。

Scenario: 执行 GET 请求

  • WHEN 目标配置 method 为 GET
  • THEN 系统 SHALL 发送 GET 请求到目标 URL

Scenario: 执行 POST 请求带 body

  • WHEN 目标配置 method 为 POST 且指定了 body 和 Content-Type header
  • THEN 系统 SHALL 发送带指定 body 的 POST 请求

Scenario: 携带自定义 headers

  • WHEN 目标配置了 headers如 Authorization
  • THEN 系统 SHALL 在请求中包含所有配置的 headers

Scenario: HTTP body 读取上限

  • WHEN HTTP response body 超过该 target 的 maxBodyBytes
  • THEN 系统 MUST 停止继续读取 response body并记录 matched=falsefailure.kind="error"failure.phase="body" 的结构化输出超限错误

Scenario: HTTP body 大小等于上限

  • WHEN HTTP response body 的字节数等于该 target 的 maxBodyBytes
  • THEN 系统 SHALL 允许该 body 进入后续解码和 expect 校验

Scenario: HTTP body 上限为 0

  • WHEN HTTP target 配置 maxBodyBytes 为 0 且响应体非空
  • THEN 系统 SHALL 停止读取并记录 body 超限错误

Scenario: 忽略 SSL 证书校验

  • WHEN 目标配置 http.ignoreSSL: true 且目标 URL 为 HTTPS
  • THEN 系统 SHALL 跳过 SSL 证书校验,即使证书无效也正常完成请求

Scenario: 不忽略 SSL 证书校验

  • WHEN 目标未配置 http.ignoreSSL 或配置为 false,且目标 URL 使用自签名证书
  • THEN 系统 SHALL 因 SSL 证书校验失败而记录请求错误

Scenario: 默认不跟随重定向

  • WHEN 目标未配置 http.maxRedirects 或配置为 0且服务端返回 301/302
  • THEN 系统 SHALL 不跟随重定向,直接返回 301/302 的响应状态码和响应头

Scenario: 配置跟随重定向

  • WHEN 目标配置 http.maxRedirects: 5 且服务端返回重定向
  • THEN 系统 SHALL 跟随重定向,最多跟随 5 次,并在跟随前释放当前重定向响应的 body

Scenario: 超过最大重定向次数

  • WHEN 目标配置 http.maxRedirects: 1 且服务端连续返回两次重定向
  • THEN 系统 SHALL 只跟随第一次重定向,并返回第二次重定向响应的状态码和响应头

Scenario: POST 重定向改 GET

  • WHEN POST 请求遇到 301/302 或任意方法请求遇到 303且系统决定按 GET 跟随重定向
  • THEN 系统 SHALL 移除请求 body并清理 content-type、content-length 等 body 相关 headers 后发起后续 GET 请求

Scenario: 跨 origin 重定向敏感 header

  • WHEN HTTP 请求跟随重定向到不同 origin
  • THEN 系统 SHALL NOT 将 authorization、cookie 等敏感 headers 转发到新的 origin

Scenario: 响应体编码自动检测

  • WHEN HTTP 响应的 Content-Type header 包含 charset=gbkcharset="gbk"
  • THEN 系统 SHALL 使用 GBK 编码解码响应体,而非硬编码 UTF-8

Scenario: 响应体编码回退 UTF-8

  • WHEN HTTP 响应的 Content-Type header 未指定 charset
  • THEN 系统 SHALL 使用 UTF-8 编码解码响应体

Scenario: 响应体编码不支持

  • WHEN HTTP 响应的 Content-Type header 指定了当前运行时不支持的 charset
  • THEN 系统 SHALL 记录 matched=falsefailure.kind="error"failure.phase="body" 的解码失败结果

Requirement: 请求超时控制

系统 SHALL 对每次 checker 执行实施超时控制,超时时间使用目标配置的 timeout 值。

Scenario: HTTP 请求超时

  • WHEN HTTP 请求在 timeout 时间内未收到响应
  • THEN 系统 SHALL 中止该请求,记录为失败并标注超时错误

Scenario: command 执行超时

  • WHEN command 进程在 timeout 时间内未退出
  • THEN 系统 MUST 终止该子进程,记录为失败并标注超时错误

Scenario: 请求在超时前完成

  • WHEN checker 在超时前完成执行
  • THEN 系统 SHALL 正常记录执行结果并进入 expect 校验

Requirement: expect 校验

系统 SHALL 在 checker 执行完成后根据目标类型的 expect 配置校验观测结果,校验结果和首个失败原因记入 check result。HTTP checker 的 durationMs SHALL 表示完整 checker 执行耗时,包括重定向、响应体读取、响应体解码和 expect 校验。

Scenario: HTTP 默认状态码

  • WHEN HTTP target 未配置 expect.status
  • THEN 系统 SHALL 按默认 status: [200] 校验响应状态码

Scenario: 校验 HTTP 状态码精确值

  • WHEN HTTP target 配置了 expect.status: [200, 201]
  • THEN 系统 SHALL 检查响应状态码是否在列表中,将匹配结果记录到 matched 字段

Scenario: 校验 HTTP 状态码范围模式

  • WHEN HTTP target 配置了 expect.status: ["2xx"]
  • THEN 系统 SHALL 检查响应状态码是否在 200-299 范围内

Scenario: 校验 HTTP 状态码混合模式

  • WHEN HTTP target 配置了 expect.status: ["2xx", 301],且响应状态码为 204
  • THEN 系统 SHALL 判定状态码匹配204 属于 2xx 范围)

Scenario: 校验 HTTP 响应头

  • WHEN HTTP target 配置了 expect.headers: {"Content-Type": {contains: "application/json"}}
  • THEN 系统 SHALL 检查响应头是否符合指定规则,全部匹配时继续后续阶段

Scenario: 校验 HTTP 响应体

  • WHEN HTTP target 配置了有序 expect.body 规则数组
  • THEN 系统 SHALL 按数组顺序执行 body 规则,任一失败立即记录 failure 并停止后续规则

Scenario: 校验 HTTP 完整耗时阈值

  • WHEN 目标配置了 expect.maxDurationMs,且 HTTP checker 完整执行含重定向、body 读取、解码和 expect后的 durationMs 超过阈值
  • THEN 系统 SHALL 判定 duration 不匹配,记录完整 durationMs 和 duration failure

Scenario: HTTP body 前耗时已超阈值

  • WHEN HTTP target 配置了 body 校验和 expect.maxDurationMs,且进入 body 读取前的已耗时已超过阈值
  • THEN 系统 SHALL 直接返回 duration failure且 MUST NOT 读取 response body

Scenario: HTTP body 失败优先于后续 duration 检查

  • WHEN HTTP target 配置了 body 校验和 expect.maxDurationMsbody 阶段存在失败,且完整执行后 duration 也超过阈值
  • THEN 系统 SHALL 返回 body 阶段的失败首个失败为准durationMs SHALL 记录完整耗时

Scenario: HTTP 慢响应体计入耗时

  • WHEN HTTP target 配置了 body 校验和 expect.maxDurationMs,且响应头很快返回但响应体读取导致完整执行耗时超过阈值
  • THEN 系统 SHALL 判定 duration 不匹配并记录完整 durationMs

Scenario: 多条 expect 规则

  • WHEN 目标同时配置状态、duration、元数据和内容规则
  • THEN 系统 SHALL 所有规则全部通过时 matched 为 true任一不通过则为 false 并记录首个失败原因

Scenario: command 默认 exitCode

  • WHEN command target 未配置 expect.exitCode
  • THEN 系统 SHALL 按默认 exitCode: [0] 校验命令退出码

Scenario: 校验 command stdout

  • WHEN command target 配置了有序 expect.stdout 规则数组
  • THEN 系统 SHALL 按数组顺序执行 stdout 规则,任一失败立即记录 failure 并停止后续规则

Requirement: Body 校验按需解析

系统 SHALL 仅在 HTTP target 配置了 body 校验,且 status、headers 阶段均通过,并且进入 body 前未确定 duration 已失败时才读取并解析响应体避免不必要的读取和解析开销。HTTP target 未配置 body 校验时,系统 SHALL NOT 读取 response body。

Scenario: status 失败时不读取 body

  • WHEN HTTP target 的 status 阶段不匹配
  • THEN 系统 SHALL 立即返回 matched=false且 MUST NOT 读取 response body

Scenario: headers 失败时不读取 body

  • WHEN HTTP target 的 status 阶段匹配但 headers 阶段不匹配
  • THEN 系统 SHALL 立即返回 matched=false且 MUST NOT 读取 response body

Scenario: 进入 body 前 duration 已失败时不读取 body

  • WHEN HTTP target 已配置 expect.maxDurationMs,且进入 body 读取前的已耗时已经超过阈值
  • THEN 系统 SHALL 返回 duration failure且 MUST NOT 读取 response body

Scenario: 仅配置 contains 时不解析 JSON

  • WHEN HTTP target 仅配置 body contains 规则而未配置 json/css/xpath 规则
  • THEN 系统 SHALL 不执行 JSON.parse 或 HTML/XML 解析

Scenario: 配置 json 时解析 JSON 失败

  • WHEN HTTP target 配置了 body json 规则但响应体不是合法 JSON
  • THEN 系统 SHALL 判定 matched 为 false并记录 json 规则对应的 failure.path

Requirement: HTTP 运行期错误归属

HTTP checker SHALL 将运行期失败归属到实际失败阶段。请求、网络、TLS 和 timeout 错误 SHALL 记录为 request 阶段错误body 超限、响应体解码失败、响应内容解析失败 SHALL 记录为 body 阶段错误expect 不匹配 SHALL 记录为对应 mismatch 阶段。

Scenario: 请求错误归属 request

  • WHEN HTTP 请求因为网络、TLS 或 timeout 失败
  • THEN 系统 SHALL 记录 matched=falsefailure.kind="error"failure.phase="request"

Scenario: body 超限归属 body

  • WHEN HTTP response body 超过 maxBodyBytes
  • THEN 系统 SHALL 记录 failure.kind="error"failure.phase="body"failure.path="body"

Scenario: body 解析错误归属 body

  • WHEN HTTP response body 已读取但解码、JSON 解析、CSS 解析或 XPath 解析失败
  • THEN 系统 SHALL 记录 failure.phase="body",且 SHALL NOT 将该失败记录为 request 错误

Requirement: 拨测结果记录

系统 SHALL 在每次 checker 完成后,将结果写入 SQLite 数据存储,包含 target_id、timestamp、matched、duration_ms、status_detail、failure 字段。

Scenario: 成功检查结果记录

  • WHEN checker 成功执行且 expect 全部匹配
  • THEN 系统 SHALL 记录 matched=true、duration_ms、status_detailfailure 为 null

Scenario: 执行失败结果记录

  • WHEN checker 执行失败(网络错误、超时、命令启动失败、输出超限等)
  • THEN 系统 SHALL 记录 matched=false、failure.kind="error" 和具体错误信息

Scenario: expect 不匹配结果记录

  • WHEN checker 执行成功但 expect 不匹配
  • THEN 系统 SHALL 记录 matched=false、failure.kind="mismatch" 和具体不匹配信息

Requirement: runner 选择

系统 SHALL 根据 target.type 选择对应 runner 执行检查。

Scenario: 选择 HTTP runner

  • WHEN target.type 为 http
  • THEN 系统 SHALL 使用 HTTP runner 执行该目标

Scenario: 选择 command runner

  • WHEN target.type 为 command
  • THEN 系统 SHALL 使用 command runner 执行该目标