@crvouga/mockingbird-service-emqx (英语)
本地模拟器。 真正的API合同. 部分 嘲笑鸟。 。 。 .
国家模拟器 EMQX. 5 测试套房和本地开发的MQTT经纪人:一个MQTT 5
tCP和WebSocket的经纪人(/mqtt),EMQX的密码和JWT认证与JWT ACLs,
持续会话带有离线的 QoS 1 队列, 而两个 REST v5 端点只发布
客户需求(POST /api/v5/publish, (中文(简体) ), DELETE /api/v5/clients/{clientid}) (中文(简体) ). 它在测试中运行
在 OS 指定端口上的进程,没有安装经纪人、 Docker 或本地二进制和会话
到期时间跟随一个时钟 套房移动而不是等待.
- 行动覆盖( HTTP): 支助
- 没有EM Q经纪人作为甲骨文,所以行为跟随 MQTT 5.0 规格,则 EMQX文档 和EMQX 5.8.6 来源。 什麽 未结算者列于下 未核实。 。 。 .
安装
npm install -D @crvouga/mockingbird-service-emqx
仅限无害环境管理。 节点 22或 Bun 1.2. 无土生属地. 给它服务
npx mockingbird-emqx serve 或者说 createServer 从 ./server (节点和邦). createRuntime
(默认条目)是可移植的: 它服务于 REST API over fetch 和 MQTT 通过
runtime.attach没有听者.
使用量
将应用程序的 MQTT URL 指向 mqttUrl (或 wsUrl),其EMQX API来源于 url,并给它
您在此配置的 API 密钥、 MQTT 用户和 JWT 密钥 .
import { createServer } from "@crvouga/mockingbird-service-emqx/server"
import mqtt from "mqtt"
const emqx = await createServer({
settings: {
apiKeys: [{ key: "test-key", secret: "test-secret" }],
users: [{ username: "service", password: "service-password", superuser: true }],
jwt: { secret: "test-jwt-secret" }, // HS256 token in the CONNECT password
authorization: { noMatch: "deny" },
},
})
// emqx.url http://127.0.0.1:<port> REST API and /__admin
// emqx.mqttUrl mqtt://127.0.0.1:<port> MQTT 5 over TCP
// emqx.wsUrl ws://127.0.0.1:<port>/mqtt MQTT 5 over WebSocket
const client = await mqtt.connectAsync(emqx.mqttUrl, {
protocolVersion: 5,
clientId: "device-1",
clean: false,
properties: { sessionExpiryInterval: 600 },
username: "service",
password: "service-password",
})
await client.subscribeAsync("space:example", { qos: 1 })
// EMQX publishes no JavaScript SDK for its REST API, so the publisher calls it over HTTP.
await fetch(`${emqx.url}/api/v5/publish`, {
method: "POST",
headers: {
authorization: `Basic ${btoa("test-key:test-secret")}`,
"content-type": "application/json",
},
body: JSON.stringify({ topic: "space:example", payload: "hello", qos: 1, retain: false }),
})
// Test control: move time instead of waiting for a session to expire.
await fetch(`${emqx.url}/__admin/clock`, { method: "POST", body: '{"advance": "600s"}' })
await client.endAsync()
await emqx.close() // closes every listener and drops every client
npx mockingbird-emqx serve --port 8850 --mqtt-port 1883 \
--api-key test-key:test-secret --mqtt-user service:service-password \
--jwt-secret test-jwt-secret --no-match deny
每个 createServer() 是它自己拥有自己的港口的经纪人,其中两个没有分享任何东西。 下
邦顿, mqtt节点 WebSocket 传输没有启动( Bun) ws 没有
createWebSocketStream,所以连接到 wsUrl 与库的浏览器传输
(单位:千美元)new MqttClient((c) => browserStreamBuilder(c, options), options) 从 mqtt/lib/connect/ws, (中文(简体) ),
或用途 connectStream(runtime) 从 ./server 用于无套接字连接。 在平原的节点上
mqtt.connect("ws://…/mqtt") 工作时.
MQTT 数据
MQTT 5.0 在TCP和WebSocket上运行 /mqtt (副议定书) mqtt,还有 mqtt-v3, (中文(简体) ),
mqtt-v3.1.1, (中文(简体) ), mqtt-v5; 没有更新的升级被解答 400,正如 EM 所做的那样
fail_if_no_subprotocol) (中文(简体) ). 两种运输方式都达到相同的会话.
| 包装 | 行为 |
|---|---|
| 连接/连接 | 规格中的 Clean Start 和会话过期间隔 : clean: false 恢复存储会话( c)sessionPresent: true),一个干净的开始丢弃它. EMQ按发送的客户端间隔. CONNACK载有最大包尺寸1048576,Topic Alias 最大65535,接受最大订阅32,Wildcard 订阅可用 1,订阅识别符可用 1,以及此仿真器无法做到的: 最大QoS 1,保留可用 0,共享订阅可用 0. 空客户端 ID 被指定 16 个字符 . |
| 认证 | EM-Q的链:内置数据库users),然后是JWT认证器(英语:jwt) (中文(简体) ). 没有配置任何连接, 如在新鲜的EMQX上。 错误的密码是 CONNACK 0x86; 用来验证但已过期的标志( 或 已过期) exp),目前无效(nbf),失败 verify_claims 或有缺陷 acl 实值 0x86;不验证或认证人不承认的证书是 0x87。 。 。 . |
| 第二 CONNECT 有连接客户端 ID | 旧的连接得到DICONNECT 0x8E 会议结束;新的会议恢复会议,除非它要求有一个干净的开始. |
| 子公司/分公司 | 精确过滤器和 + 编号 : # 通配符,无本地,订阅标识符. 授予的QoS最多1分. 拒绝的过滤器是 SUBACK 0x87· ; $share/… 实值 0x9E。 。 。 . |
| PUBLISH / PUBACK 软件 | Qos 0和1. PUBACK为 0x00, (中文(简体) ), 0x10 或 0x87 被拒绝时( 信件被丢弃; 被拒绝的QoS 0 发布被悄悄地丢弃) 。 属性(内容类型,响应话题,关联数据,用户属性,有效载荷格式指标,消息过期间隔)被转发. 入门专题别名能用. |
| 离线 | 连接已去掉的会话会保留订阅并排队列信件(Qos 0 too, as mqtt.mqueue_store_qos0; 最多1 000起,最老的下降,因为 mqtt.max_mqueue_len) (中文(简体) ). 发送和未确认的信息在复刊时会遭到DUP的不满。 最多32个QoS 1信息正在飞行中(mqtt.max_inflight),如果客户端的"接收最大"更低. |
| 会话到期 | Session Expiry Interval 连接结束后秒,在模拟时钟上. 0 (或缺席)以连接结束会话. |
| JWT 过期 | 与 disconnectAfterExpire (EMQ) 连接的客户端被发送到 DICONNECT 0x87 当它的标志 exp 过关了 连接中断了 ACL否认一切. |
JWT ACL. (英语). 标志 acl 索赔(可配置名称)先问:
- 对象形式,
{"pub": [...], "sub": [...], "all": [...]}: 允许列出的主题 其它的都被否认了. - 名单表,
[{"permission": "allow" | "deny", "action": "pub" | "sub" | "all", "topic": "…", "qos"?: [0, 1], "retain"?: false}]: 第一个匹配规则决定;一个没有匹配规则的主题 归为authorization.noMatch(单位:千美元)allow默认, 如 EMQX 5; EMQX 6 更改了默认 改为deny) (中文(简体) ).
ACL 主题为 MQTT 过滤器 ${clientid} 财务报告和审定财务报表 ${username} 扩大; eq <topic> 比较
从字面上说 请求的过滤器按文字匹配, 所以规则 a/+ 涵盖a
订阅 a/# (EMQX emqx_topic:match/2) (中文(简体) ). 一个超级用户来自 users 跳过
授权。 与 authorization.denyAction: "disconnect" 拒绝操作结束
连接到 DISCONNECT 0x87 换句话说.
模拟器的模型 经纪人做什么 一个话题,没有其他的: space:example 是一个
主题级别。 将应用程序的逻辑名称映射到主题上是客户端的工作.
路线
每条路线都需要 Authorization: Basic <api key>:<secret>与 apiKeys 清空任意一对
被接受; 丢失的页眉总是被拒绝.
| 路线 | 行为 |
|---|---|
POST /api/v5/publish | 贾森 {topic, payload, qos?: 0|1|2, retain?, payload_encoding?: "plain"|"base64", properties?}。 。 。 . 200 {"id": "<32 hex>"} 当至少匹配一个会话( 离线的会话计数: 消息已排队); 202 {"reason_code": 16, "message": "no_matching_subscribers"} 那时没有人知道. 400 {"code": "BAD_REQUEST", "message"} 对于一个失败的人体 topic,非字符串 payload, (中文(简体) ), qos 超出范围、未知字段、无效的JSON); 400 {"reason_code": 144, "message": "topic_name_invalid"} 空题或带有通配符; 400 {"reason_code": 151, "message": "packet_too_large"} 超过 1 MB ; 415 当身体不是 application/json. QoS 2 接受并交付给 订阅的QoS. |
DELETE /api/v5/clients/{clientid} | 踢出客户端: 一个连接的客户端获得 DICONNECT 0x98 行政行动、届会、订阅和排行被丢弃(离线会话也是如此). 204,或 404 {"code": "CLIENTID_NOT_FOUND", "message": "Client ID not found"}。 。 。 . |
| 任何,无全权证书 | 401 {"code": "AUTHORIZATION_HEADER_ERROR", "message": "Support authorization: basic/bearer "}; 错误的密钥或密钥是 401 {"code": "BAD_API_KEY_OR_SECRET", "message": "Check api_key/api_secret"}; 无记名符号是 401 BAD_TOKEN全部携带 WWW-Authenticate: Basic Realm="emqx-dashboard"。 。 。 . |
管理员(超过标准合同)
| 路线 | 效果 |
|---|---|
GET /__admin/clients | 每届会议: clientId, (中文(简体) ), username, (中文(简体) ), connected, (中文(简体) ), transport, (中文(简体) ), sessionExpiryInterval, (中文(简体) ), connectedAt, (中文(简体) ), disconnectedAt, (中文(简体) ), expiresAt和计数 subscriptions, (中文(简体) ), queued 财务报告和审定财务报表 inflight 留言. |
GET /__admin/clients/:clientId | 一届会议,订阅日期 topics。 。 。 . |
GET /__admin/subscriptions?clientId= | 订阅: topicFilter,准予 qos,选项. |
GET /__admin/publishes | {total, publishes}: 以元数据形式出版的上千种topic, (中文(简体) ), qos, (中文(简体) ), bytes, (中文(简体) ), source: "mqtt" | "http" | "admin", (中文(简体) ), clientId, (中文(简体) ), matched, (中文(简体) ), at) (中文(简体) ). 有效载荷从不保存. |
POST /__admin/inject | {topic, payload | payloadBase64, qos?: 0 | 1}: 向主题的订阅者发送生字节,绕过REST schema和每个ACL. 用它来给消费者一个有效载荷 它必须拒绝。 答复 {matched}。 。 。 . |
POST /__admin/transport/cut | {clientId?}:一个客户端或所有客户端的网络断电. 现场连接因没有DICONNECT而死亡,新的CONNECT被放弃,没有得到答复. 会话未受影响,按自己的时间安排过期. |
POST /__admin/transport/restore | {clientId?}:结束停产. GET /__admin/transport 列出切出的内容. |
POST /__admin/clients/:clientId/disconnect | {reasonCode?}: 发送 DICONNECT( 默认) 0x98)并收紧. 和卖家的踢球不同,会话会留下来. |
GET 编号 : PUT /__admin/settings | {apiKeys?, users?, jwt?: {secret, from?, aclClaimName?, verifyClaims?, secretBase64Encoded?, disconnectAfterExpire?} | null, authorization?: {noMatch?, denyAction?}} 呼叫的命名空间。 重置运行时间创建的值的返回 . |
POST /__admin/clock | 标准 将时钟一并移走 : 会话经过间隔会被丢弃, 带有过期符的客户端被断开 . |
POST /__admin/reset检查站 | 标准 重置会降低每个连接和会话 。 恢复检查点会恢复会话、订阅和排队,并减少直播连接(检查点不能持有套接字). |
错误预设( E)POST /__admin/faults {"preset": "<name>", "count"?: n}· ; GET /__admin/faults/presets:
connect_bad_credentials (结束) 0x86, (中文(简体) ), connect_not_authorized (单位:千美元)0x87, (中文(简体) ),
connect_server_unavailable (单位:千美元)0x88, (中文(简体) ), publish_unavailable (REST发布答案) 503 {"reason_code": 131, "message": "failed_to_dispatch"}, (中文(简体) ), api_unauthorized (每一次紧急呼叫)
答题 401) (中文(简体) ). CONNECT是故障操作 MqttConnect,所以任何原因代码都可以是
强制 : {"operationId": "MqttConnect", "effect": "connack", "params": {"reasonCode": 151}}。 。 。 .
命名空间
平行工人通过命名空间孤立自己. HTTP : (韩语) x-mockingbird-namespace页:1
/__admin/ns/<name> aPI 来源或 API 密钥上的前缀
(单位:千美元)PUT /__admin/credentials {"credentials": {"<api key>": "<namespace>"}}. MQTT: WebSocket (英语)
網址 ws://…/__admin/ns/<name>/mqtt,或CONNECT用户名所按的同一证书地图,
然后,它的客户身份。 未映射的客户端使用默认的名称空间 .
未核实
取自EMQX 5.8.6 来源或未打开,未观察到在经纪人身上:
- 这个
message计划文本400(EMQ) 打印其配置库的错误; 模拟器 指纹{"kind": "validation_error", "path": "root.<field>", "reason": …}),基础差64 载荷和未知路径404。 。 。 . - EM 强制整数
payload以至其小数字符串;模拟者拒绝. - CONNACK接收最大值是EMQQ
min(client's, 32);模拟器总是发出32. - 这个
is_superuser符号索赔不读取.
故意不模拟
- MQTT 3.1和3.1.1:这种CONNECT以协议的回码被拒绝.
- QoS 2: CONNACK表示最大QoS 1,一个QoS 2的订阅获得QoS 1,一个QoS 2
发布是回答 DISCONNECT
0x9B.EMQX本身支持QoS 2. - 保留信件: CONNACK 表示保留可用 0, 保留的出版物被回答
分解系统
0x9A,以及retain: true记录显示400。 。 。 . - 将信件(收录于CONNECT,从未出版),共享订阅,增强 认证( AUTH ), 留守超时( PINGREQ 已回答; 使用已死亡的运算切换) 连接、 消息过期、 和外出主题别名 .
- 公用钥匙和JWKS代号、密码散列、以及核证人或授权人以外的 内置数据库和JWT.
- 集群、仪表板及其登录标志、规则引擎、桥梁、计费、TLS和 听众配置 .
API 密码
| 导出 | 仁爱 | 说明 |
|---|---|---|
createRuntime | 函数 | 服务合同的模拟者。 选项 : settings, (中文(简体) ), clock, (中文(简体) ), seed, (中文(简体) ), adminPrefix, (中文(简体) ), adminKey, (中文(简体) ), onLog, (中文(简体) ), sqlite添加 attach(transport, info?, namespace?) 为 MQTT 连接, webSocketPath(pathname), (中文(简体) ), endpoints,以及 stop()。 。 。 . |
EmqxAPI | 类 | 一个名字空间的经纪人: fetch(request) 用于rest API, broker 为MQTT, state, (中文(简体) ), reset()选项: sqlite, (中文(简体) ), now, (中文(简体) ), namespace, (中文(简体) ), settings, (中文(简体) ), connectFault。 。 。 . |
EMQX_PRESETS | 对象 | 每个命名的断层预设. |
EMQX_NAMESPACE | 字符串 | 服务名称, "emqx"。 。 。 . |
MQTT_CONNECT_OPERATION | 字符串 | "MqttConnect",操作id一个错误规则用于CONNECT. |
MQTT_WEBSOCKET_PATH | 字符串 | "/mqtt"。 。 。 . |
MAX_PACKET_SIZE | 编号 | 1048576,EMQX's mqtt.max_packet_size。 。 。 . |
DEFAULT_SETTINGS, (中文(简体) ), DEFAULT_JWT_AUTHENTICATOR | 对象 | 新鲜的EMQ:没有认证器, no_match = allow;和JWT验证器的默认值. |
apiKeyCredential | 函数 | 请求的 API 密钥( 如何将证书映射到命名空间 ) . |
parseAcl, (中文(简体) ), checkAcl | 函数 | 分析一个 acl 权利要求,并询问有关一个出版物或订阅者的情况(allow, (中文(简体) ), deny 或者说 nomatch) (中文(简体) ). |
ReasonCode | 对象 | MQTT 5 名下理性码. |
document, (中文(简体) ), operationIds, (中文(简体) ), supportedOperationIds | 数值 | REST合同及其操作代号. |
createServer, (中文(简体) ), connectStream, (中文(简体) ), serveTarget, (中文(简体) ), DEFAULT_PORT, (中文(简体) ), DEFAULT_MQTT_PORT (单位:千美元)./server(中文(简体) ) | 节点 | 服务HTTP、WebSocket和TCP{url, mqttUrl, mqttPort, wsUrl, runtime, close()}; 选项添加 port, (中文(简体) ), mqttPort, (中文(简体) ), host, (中文(简体) ), sweepMs; 正在处理的 MQTT 流 serve CLI目标;港口8850和1883. |
部分 嘲笑鸟。 。 。 .