JSON-RPC 路由

SBC 最强大的功能是 JSON-RPC 动态路由:每个入局 SIP INVITE 都可以调用外部 HTTP API 来决定呼叫处理方式。

1. 工作原理

SIP INVITE 到达
     │
     ▼
SBC Inspector
     │
     ├── 提取匹配字段 (Caller, Callee, Direction...)
     ├── 匹配 Rule 列表
     │     │
     │     ▼ 命中规则
     ├── 渲染请求模板 (Jinja2)
     ├── HTTP POST 到上游 API
     │     │
     │     ▼ 收到响应
     ├── 解析响应 JSON
     ├── 应用号码改写 (caller_rewrite, callee_rewrite)
     ├── 注入/删除 Header
     └── 返回路由决策 (forward / reject / busy)

2. 配置文件

config/sbc/sbc_jsonrpc.toml:

[[rules]]
name = "route-by-api"
description = "通过 API 路由所有入局呼叫"

[rules.match]
logic = "all"
conditions = [
  { field = "Direction", operator = "Equals", value = "inbound" }
]

[rules.upstream]
url = "http://10.0.0.50:3000/api/sbc/route"
method = "POST"
timeout_ms = 2000
retries = 1

[rules.upstream.headers]
Authorization = "Bearer my-api-key"
Content-Type = "application/json"

[rules.upstream.body_template]
# Jinja2 模板
body = '''
{
  "caller": "{{ caller }}",
  "callee": "{{ callee }}",
  "call_id": "{{ call_id }}",
  "from_host": "{{ from_host }}",
  "direction": "{{ direction }}"
}
'''

[rules.response]
# 从响应 JSON 提取路由决策
action_field = "action"           # "forward" / "reject" / "busy"
trunk_field = "trunk"             # 转发到的中继名称
caller_rewrite_field = "caller"   # 改写后的主叫
callee_rewrite_field = "callee"   # 改写后的被叫
reject_code_field = "sip_code"    # 拒绝时的 SIP 状态码

[[rules.response.header_injections]]
action = "add"
name = "X-Route-Source"
value = "sbc-jsonrpc"

3. 匹配条件

3.1 可匹配字段

字段说明示例
Caller完整主叫 URIsip:1000@10.0.0.1
Callee完整被叫 URIsip:9000@10.0.0.1
CallerUser主叫号码1000
CalleeUser被叫号码9000
CallerHost主叫域名/IP10.0.0.1
CalleeHost被叫域名/IP10.0.0.1
Direction方向inbound / outbound
CallIdSIP Call-IDabc@10.0.0.1
UserAgentSIP User-AgentLinphone/4.0
Header自定义 Header任意 Header 名

3.2 操作符

操作符说明
Equals精确匹配
NotEquals不等于
Contains包含
StartsWith前缀匹配
EndsWith后缀匹配
Regex正则匹配
ExistsHeader 存在
NotExistsHeader 不存在

3.3 逻辑组合

[rules.match]
logic = "all"  # all = 全部满足, any = 任一满足
conditions = [
  { field = "Direction", operator = "Equals", value = "inbound" },
  { field = "CalleeUser", operator = "StartsWith", value = "9" },
]

4. 上游 API 契约

4.1 请求(SBC → 上游)

SBC 发送 HTTP POST,Body 由模板渲染。

4.2 响应(上游 → SBC)

{
  "action": "forward",
  "trunk": "carrier-a",
  "callee": "861012345678",
  "caller": "057112345678",
  "headers": {
    "X-Custom": "value"
  }
}

action 可选值:

值说明
forward转发到指定中继
reject拒绝(可指定 SIP 状态码)
busy返回 486 Busy

4.3 主叫覆写与非法 URI(0.5+)

  • 响应中的 caller 字段会覆写出站腿的主叫身份——外部系统可按通话重新盖 CLI(租户级主叫池、合规改写)。
  • 响应中的非法目标 URI 现在直接使呼叫失败并给出明确错误,而不是回退到非预期路由:畸形的决策按路由错误处理,不再被静默忽略。

5. 模板变量

Jinja2 模板可用变量:

变量说明
{{ caller }}主叫号码
{{ callee }}被叫号码
{{ call_id }}SIP Call-ID
{{ from_host }}来源 IP
{{ to_host }}目标 IP
{{ direction }}方向
{{ user_agent }}User-Agent

6. 控制台操作

6.1 JSON-RPC 配置管理

SBC → 路由 → JSON-RPC 配置:Web UI 编辑和保存 sbc_jsonrpc.toml。

6.2 规则模拟

SBC → 路由 → 模拟:输入 Caller/Callee 等字段,测试匹配哪条规则,预览发送到上游 API 的请求。

6.3 路由模拟

SBC → 路由 → 路由模拟:完整模拟一个 INVITE 的路由决策过程(含 JSON-RPC 调用)。