拦截与改写能力

本页列出 RelayCore 内置的流量改写能力,每项配一个能通过 relay-core rules validate 的 Rule JSON(也可直接 PUT /api/v1/rules)。字段形状与 规则 API 一致:active、stage、termination、带 type 的 filter,以及 actions 数组。动作字段包在 config 里。

Mock Response

用本地假响应替换上游响应。常用于前端开发、契约测试、错误模拟。在请求头阶段终止,不再访问上游。

{
  "id": "mock-health",
  "name": "Mock /v1/health",
  "active": true,
  "stage": "RequestHeaders",
  "priority": 100,
  "termination": "Stop",
  "filter": { "type": "Path", "config": { "mode": "Exact", "value": "/v1/health" } },
  "actions": [{
    "type": "MockResponse",
    "config": {
      "status": 200,
      "headers": { "content-type": "application/json" },
      "body": { "type": "Text", "value": "{\\"ok\\":true}" }
    }
  }]
}

Map Local

把 HTTP 请求替换成本地文件。常用于前端联调、离线调试。路径必须落在策略的 sandbox 根目录内。

{
  "id": "map-local-static",
  "name": "Serve /static from disk",
  "active": true,
  "stage": "RequestHeaders",
  "priority": 100,
  "termination": "Stop",
  "filter": { "type": "Path", "config": { "mode": "Prefix", "value": "/static/" } },
  "actions": [{
    "type": "MapLocal",
    "config": { "path": "/Users/me/projects/app/static", "content_type": null }
  }]
}

Map Remote

把请求改写到另一个地址。常用于把生产流量切到预发。

{
  "id": "map-remote-prod-to-staging",
  "name": "api.example.com -> staging",
  "active": true,
  "stage": "RequestHeaders",
  "priority": 100,
  "termination": "Stop",
  "filter": { "type": "Host", "config": { "mode": "Exact", "value": "api.example.com" } },
  "actions": [{
    "type": "MapRemote",
    "config": { "url": "https://api-staging.example.com", "preserve_host": false }
  }]
}

Redirect

返回 3xx 重定向。

{
  "id": "redirect-legacy",
  "name": "/old -> /new",
  "active": true,
  "stage": "RequestHeaders",
  "priority": 100,
  "termination": "Stop",
  "filter": { "type": "Path", "config": { "mode": "Exact", "value": "/old" } },
  "actions": [{
    "type": "Redirect",
    "config": { "location": "https://example.com/new", "status": 301 }
  }]
}

Header 改写

增删改请求/响应头。UpdateRequestHeader / UpdateResponseHeader 的 value 支持 {{previous}},引用改写前的值。

{
  "id": "strip-cookie",
  "name": "Strip Set-Cookie on /api/*",
  "active": true,
  "stage": "ResponseHeaders",
  "priority": 100,
  "termination": "Continue",
  "filter": { "type": "Path", "config": { "mode": "Prefix", "value": "/api/" } },
  "actions": [{ "type": "DeleteResponseHeader", "config": { "name": "set-cookie" } }]
}
{
  "id": "add-debug",
  "name": "Add X-Debug header",
  "active": true,
  "stage": "RequestHeaders",
  "priority": 100,
  "termination": "Continue",
  "filter": { "type": "Method", "config": { "mode": "Exact", "value": "POST" } },
  "actions": [{
    "type": "AddRequestHeader",
    "config": { "name": "x-debug", "value": "relay-core" }
  }]
}

Body 改写

支持整体替换、正则替换、JSONPath 修改。依赖 body 的规则必须放在 RequestBody / ResponseBody 阶段;超过检查预算的 body 会被跳过并打上原因,不会在截断前缀上匹配。

{
  "id": "jsonpath-set",
  "name": "Force user.role = admin",
  "active": true,
  "stage": "RequestBody",
  "priority": 100,
  "termination": "Continue",
  "filter": { "type": "Host", "config": { "mode": "Exact", "value": "api.example.com" } },
  "actions": [{
    "type": "TransformRequestBody",
    "config": {
      "transform": {
        "type": "JsonPathSet",
        "config": { "path": "$.user.role", "value": "\\"admin\\"" }
      }
    }
  }]
}
{
  "id": "regex-replace",
  "name": "Replace token in body",
  "active": true,
  "stage": "ResponseBody",
  "priority": 100,
  "termination": "Continue",
  "filter": { "type": "Path", "config": { "mode": "Exact", "value": "/login" } },
  "actions": [{
    "type": "TransformResponseBody",
    "config": {
      "transform": {
        "type": "RegexReplace",
        "config": { "pattern": "secret=([^&]+)", "replacement": "secret=REDACTED" }
      }
    }
  }]
}

拦截断点

暂停实时流量等待人工决定。用 HTTP POST /api/v1/intercepts 或 MCP set_intercept 创建断点;resume / resume_flow 放行或丢弃。没有 relay-core intercept 子命令。

curl -X POST http://127.0.0.1:8082/api/v1/intercepts \
  -H 'Content-Type: application/json' \
  -d '{
    "url_pattern": "api.example.com",
    "phase": "request"
  }'

断点命中时在 TUI Intercepts 面板可见,也可以通过 GET /api/v1/intercepts 查询。放行时可以携带修改。request_header_upserts / response_header_upserts 按名增改、保留其余头;request_headers / response_headers 会整表替换。

curl -X POST http://127.0.0.1:8082/api/v1/intercepts/<key>/resume \
  -H 'Content-Type: application/json' \
  -d '{
    "action": "continue",
    "request_header_upserts": { "x-test": "value" }
  }'

可修改字段:method、url、request_headers、request_header_upserts、request_body、status_code、response_headers、response_header_upserts、response_body、message_content(WebSocket)。

流量重放

把已捕获的 HTTP 请求再发一次。这次调用不能改头或改体。重放从本代理的监听端口进去,所以会记成一条新流量,当前规则和脚本会生效;代理没在运行时这次调用失败。仅支持 HTTP,不支持 WebSocket。

curl -X POST http://127.0.0.1:8082/api/v1/flows/<id>/replay

# TLS 校验失败时(仅调试)
curl -X POST 'http://127.0.0.1:8082/api/v1/flows/<id>/replay?accept_invalid_certs=true'

WebSocket 消息改写

WebSocket 流量有两个专用动作,阶段必须是 WebSocketMessage:

{
  "id": "ws-mock",
  "name": "Mock chat messages",
  "active": true,
  "stage": "WebSocketMessage",
  "priority": 100,
  "termination": "Stop",
  "filter": { "type": "Host", "config": { "mode": "Exact", "value": "chat.example.com" } },
  "actions": [{
    "type": "MockWebSocketMessage",
    "config": {
      "direction": "Incoming",
      "message": "{\\"type\\":\\"welcome\\",\\"user\\":\\"bot\\"}"
    }
  }]
}

也可以丢弃匹配到的消息:

{
  "id": "ws-drop-pings",
  "name": "Drop ping frames",
  "active": true,
  "stage": "WebSocketMessage",
  "priority": 100,
  "termination": "Stop",
  "filter": { "type": "All" },
  "actions": [{ "type": "DropWebSocketMessage" }]
}

L3/L4 Action

这三个动作只在 Connect 阶段有意义:

  • RedirectIp — 把连接拨到另一个 IP
  • ForwardPort — 转发到另一个 host:port(主机名会解析)
  • SetTtl — 会被接受并记一条警告,线路上的 TTL 不变