Bounded incremental STOMP 1.0/1.1/1.2 codec and transport-independent client
git clone https://github.com/sundaysebasidian-byte/moonstomp.git
cd moonstomp
moon test # 单元 / 属性 / 对抗测试
moon build --target js # 构建 Node 用的 bridge
node tools/stomp-cli.cjs demo # 进程内 demo broker:发一条、收一条
node examples/demo/01-pubsub.cjs # 最后一行输出 PASS pubsub
moon run examples/versions --target js # 1.0 / 1.1 / 1.2 协商与边界moon add sundaysebasidian-byte/moonstomp@0.3.10.2.0 已发布到 Mooncakes。 Registry 实际版本、metadata 和发布 ZIP checksum 均已核对;全新 registry 消费者通过核心 API、多版本及模拟 TCP/TLS 验证。历史 0.1.2 仅支持 STOMP 1.2。精确发布 commit、CI、校验和与未验范围见0.2.0 回执。
let client = @stomp.Client::new()
let bytes = client.send(@stomp.connect("localhost"), 0) // 写给 socket
ignore(client.receive(b"CONNECTED\nversion:1.2\n\n\x00", 0))
ignore(client.send(@stomp.subscribe("sub-1", "/queue/jobs", ack="client-individual"), 0))
// 一个 MESSAGE 帧被 TCP 拆成两段到达
let first = client.receive(b"MESSAGE\nsubscription:sub-1\nmessage-id:m1\ndestination:/queue/jobs\nack:a1\ncontent-len", 0)
assert_eq(first.length(), 0) // 半包:还没有完整帧
let events = client.receive(b"gth:5\n\nhello\x00", 0)
match events[0] {
@stomp.Message(frame) => assert_eq(frame.body, b"hello")
_ => fail("expected a MESSAGE")
}
ignore(client.send(@stomp.acknowledge("a1"), 0))node tools/demo-broker.cjs --port 61613 &
node tools/stomp-cli.cjs subscribe --port 61613 --destination /queue/jobs --count 1 &
node tools/stomp-cli.cjs send --port 61613 --destination /queue/jobs --body hello
# sent destination=/queue/jobs bytes=5 receipt=confirmed| 场景 | 命令 |
|---|---|
| 发布 / 订阅(queue 与 topic) | node examples/demo/01-pubsub.cjs |
| receipt 可靠发送 | node examples/demo/02-reliable-send.cjs |
| 手动 ACK / NACK | node examples/demo/03-ack-nack.cjs |
| 事务 COMMIT / ABORT | node examples/demo/04-transaction.cjs |
| 心跳与断线检测 | node examples/demo/05-heartbeat.cjs |
| 1.0 / 1.1 / 1.2 协商 | node examples/demo/06-versions.cjs |
| 认证与结构化错误 | node examples/demo/07-auth-errors.cjs |
| 有界异步批量发送(仅未发布开发源码) | node examples/demo/08-async-send.cjs |
| 规范要求 | 支持 |
|---|---|
| 全部客户端帧:CONNECT / STOMP、SEND、SUBSCRIBE、UNSUBSCRIBE、ACK、NACK、BEGIN、COMMIT、ABORT、DISCONNECT | ✅ |
| 全部服务端帧:CONNECTED、MESSAGE、RECEIPT、ERROR | ✅ |
| 头部转义 \\ \n \c \r(1.2),CONNECT / CONNECTED 不转义;1.0 原样、1.1 无 \r | ✅ |
| content-length 与 NUL 结尾,正文可含二进制 NUL | ✅ |
| 增量(半包、粘包)解析,帧间 EOL | ✅ |
| 版本协商 accept-version / version,越界版本拒绝 | ✅ |
| 心跳协商 heart-beat 与超时检测 | ✅ |
| 订阅 ack 模式 auto / client / client-individual,按版本区分 ACK 头 | ✅ |
| receipt 跟踪、事务内的 SEND / ACK | ✅ |
| 行长、头数、正文、单次事件数上限(可配置 Limits) | ✅ |
| Broker | 已验证 |
|---|---|
| Apache Artemis 2.57.0(另以 go-stomp 为对端) | 1.0 / 1.1 / 1.2 收发、分段二进制正文、receipt、ACK / 累计 ACK / NACK、事务 SEND、双向心跳、真实 ERROR |
| Apache ActiveMQ Classic 6.3.2 | login/passcode 认证、TLS、三版本二进制收发、版本化 ACK、事务 SEND 提交 / 中止、COMMIT 前断线不重放 |
| 路径 | 内容 |
|---|---|
| frame.mbt headers.mbt decoder.mbt | 帧、头部转义、增量解码 |
| client.mbt heartbeat.mbt version.mbt api.mbt | 会话状态机、心跳、版本协商、帧构造函数 |
| bridge/ | MoonBit ↔ JS 边界 |
| transport/ | Node TCP / TLS 适配器与句柄 |
| tools/ | 命令行与 demo broker |
| examples/ | MoonBit 与 Node 示例 |
type Clientfn Client::receive(self : Client, chunk : Bytes, now : Int64) -> Array[ClientEvent] raise ProtocolErrortype Decodertype HeartbeatClockfn HeartbeatClock::new(plan : HeartbeatPlan, now : Int64, tolerance_ms? : Int64) -> HeartbeatClock raise ProtocolErrorfn Limits::new(max_line? : Int, max_headers? : Int, max_body? : Int, max_events? : Int) -> Limits raise ProtocolErrorfn acknowledge_message(version : Version, message : Frame, negative? : Bool, transaction? : String) -> Frame raise ProtocolErrorfn connect_versions(host : String, versions : Array[Version], heartbeat? : String, login? : String, passcode? : String) -> Frame raise ProtocolErrorfn send_message(destination : String, body : Bytes, transaction? : String, receipt? : String) -> Framefn subscribe_versioned(version : Version, destination : String, id? : String, ack? : String) -> Frame raise ProtocolErrorInstall
Download zipBounded incremental STOMP 1.0/1.1/1.2 codec and transport-independent client