Wang1rrr opened a new issue, #4856:
URL: https://github.com/apache/rocketmq-dashboard/issues/4856

   ### Before Creating the Bug Report
   
   - [x] I have searched the [open 
issues](https://github.com/apache/rocketmq-dashboard/issues) of this repository 
and believe that this is not a duplicate.
   - [x] This is a defect in RocketMQ Studio, not a usage question and not a 
defect in another Apache RocketMQ repository.
   - [x] I can reproduce this on the current `rocketmq-studio` branch (the 
Studio trunk the pull request template points at), commit 
`4c697f07acde460e2344375cb1f82669f5b270fd`.
   
   ### Studio Version
   
   ```
   branch: rocketmq-studio
   git commit id: 4c697f07acde460e2344375cb1f82669f5b270fd
   deployed as: built from source
   ```
   
   ### Runtime Environment
   
   ```
   OS: Windows 11 (development box); the defect is in the Aliyun provider's 
message converter, so the host does not matter
   MySQL: not required - reproduced at the converter layer against a stubbed 
ListMessagesResponseBody
   browser (for UI issues): any; the affected console page is 
web/src/pages/instance/message.tsx
   ```
   
   ### Connected RocketMQ Cluster
   
   ```
   RocketMQ version: Alibaba Cloud RocketMQ 5.x instance (OpenAPI 
rocketmq20220801)
   access mode: cloud instance - AliyunInstanceProvider / AliyunConverters
   deployment: any Aliyun instance holding at least one message whose body is 
not valid UTF-8
   ```
   
   ### Describe the Bug
   
   `MessageRecordVO.bodyEncoding` has a published vocabulary of exactly two 
values plus "absent". `docs/api-spec.md:1335` documents the pair as
   
   ```
   | body        | string | UTF-8 文本或 Base64 编码后的消息体 |
   | bodyEncoding | string | 消息体编码,如 UTF-8 / BASE64     |
   ```
   
   and every other provider sticks to it:
   
   | producer | values emitted |
   | --- | --- |
   | `RocketMQMessageProvider.displayBody` 
(`provider/apache/RocketMQMessageProvider.java:881`) | `"UTF-8"` (`:897`), 
`"BASE64"` (`:901`), `null` for a null body (`:883`) |
   | `TencentInstanceProvider.toRecordVO` 
(`provider/tencent/TencentInstanceProvider.java:808`, `:828`) | `"UTF-8"`, 
`null` |
   | `AliyunConverters.toMessageRecord` 
(`provider/alibaba/AliyunConverters.java:240`) | `"UTF-8"`, **`"TEXT"`** |
   
   The Aliyun branch is
   
   ```java
   if (decodedBody != null) {
       builder.body(decodedBody).bodyEncoding("UTF-8");
   } else {
       builder.body(rawBody).bodyEncoding("TEXT");
   }
   ```
   
   `"TEXT"` appears nowhere else in the codebase and is not a value the spec 
defines. Worse, the branch that emits it puts the **raw Base64 string** into 
`body` while claiming it is text.
   
   The root cause is that `tryBase64Decode` (`:366`) collapses three different 
situations into one `null` return:
   
   1. the API returned no body at all (`raw == null || raw.isBlank()`);
   2. the body is Base64 of a **binary** payload, so the strict UTF-8 decoder 
reports a `CharacterCodingException`;
   3. the body is not Base64 in the first place (`IllegalArgumentException` 
from `Base64.getDecoder()`).
   
   Only case 3 is genuinely text. Case 2 is exactly the case `BASE64` exists 
for, and case 1 should carry no encoding at all - which is what both the Apache 
and the Tencent paths do for an absent body.
   
   #### Where it surfaces
   
   - **AI tool layer.** `MessageItem.from` 
(`ops/ai/tool/contract/message/MessageItem.java:46`) and `MessageQueryOutput` 
(`:76`) forward `getBodyEncoding()` verbatim, so `rmq.message.query` / 
`rmq.message.query_by_topic` / `rmq.message.query_by_offset` hand the model 
`{"body": "<base64>", "bodyEncoding": "TEXT"}`. The tool schema 
(`server/src/main/resources/tool-catalog/tools/message.yaml:86`, `:171`, 
`:254`) declares `bodyEncoding` as a bare `type: string` with no enum, so 
nothing rejects the value. An agent told the payload is text will quote the 
Base64 back to the operator as the message content instead of decoding it - and 
for an Aliyun instance it will do that for *every* binary message.
   - **rmqctl.** The generated catalogue advertises the same three fields 
(`rmqctl/internal/catalog/catalog_gen.go:389`, `:410`, `:419`), so the CLI 
prints `bodyEncoding: TEXT` next to a Base64 body.
   - **Console.** `web/src/pages/instance/message.tsx` never reads 
`bodyEncoding`; `formatBody` (`:140`) only tries `JSON.parse` and otherwise 
returns the string as-is, and the download at `:731` wraps that string in a 
`application/json` blob. So the Base64 is shown and downloaded as though it 
were the message text.
   - **Asymmetry across vendors.** The same binary message renders with 
`bodyEncoding: "BASE64"` on a self-managed cluster and `"TEXT"` on an Aliyun 
instance, so any consumer that switches on the documented values silently takes 
a different branch per vendor.
   
   #### Secondary defect in the same branch
   
   Case 1 also emits `"TEXT"`: a message with no body is published as `{"body": 
null, "bodyEncoding": "TEXT"}`, i.e. an encoding for a value that is not there. 
`AliyunInstanceProviderTest` already pins the neighbouring Tencent-style 
behaviour of `null`/`null` for a bodyless record.
   
   ### Steps to Reproduce
   
   1. Configure an Aliyun RocketMQ 5.x cloud instance in Studio.
   2. Send a message to it whose body is not valid UTF-8 (any 
protobuf/avro/binary payload; two bytes `0xFF 0xFE` are enough).
   3. Console -> Instance -> Message, query that topic and open the message 
detail; or call the `rmq.message.query` tool / `rmqctl message query` with 
`--include-body`.
   4. Inspect `bodyEncoding` next to `body`.
   
   Unit-level reproduction against the current trunk:
   
   ```java
   String encoded = Base64.getEncoder().encodeToString(new byte[] {(byte) 0xFF, 
(byte) 0xFE});
   MessageRecordVO record = AliyunConverters.toMessageRecord(
           
ListMessagesResponseBody.List.builder().messageId("msg-1").body(encoded).build());
   
   record.getBody();         // "//4="  - the Base64, not text
   record.getBodyEncoding(); // "TEXT"  - not a value the contract defines
   ```
   
   ### What Did You Expect to See?
   
   `bodyEncoding` restricted to the documented vocabulary:
   
   | body from the API | expected `body` | expected `bodyEncoding` |
   | --- | --- | --- |
   | Base64 of UTF-8 text | the decoded text | `UTF-8` |
   | Base64 of a binary payload | the Base64 string | `BASE64` |
   | a string that is not Base64 | that string | `UTF-8` |
   | absent / blank | `null` | `null` |
   
   which is precisely what `RocketMQMessageProvider.displayBody` already 
produces for a self-managed cluster.
   
   ### What Did You See Instead?
   
   `"TEXT"` for all three of the last-but-one, second and first rows: a binary 
payload labelled as text with its Base64 in `body`, and a bodyless message 
labelled as text with `null` in `body`.
   
   ### Additional Context
   
   Suggested fix, and the one in the pull request I am about to open: replace 
the three-way `null` in `tryBase64Decode` with a small value/encoding pair that 
keeps the three cases apart, and drop the undefined `"TEXT"` label. 
`AliyunInstanceProviderTest.queryMessagesShouldMapFieldsAndDecodeBase64BodyTest:544`
 currently asserts `"TEXT"` for a `"{}"` body - that fixture is case 3 (not 
Base64), so the assertion becomes `"UTF-8"` and the body assertion is unchanged.
   
   Follow-up worth its own change, deliberately not bundled here: 
`web/src/pages/instance/message.tsx` ignores `bodyEncoding` entirely, so a 
`BASE64` body from *any* provider is displayed and downloaded as raw Base64. 
Teaching `formatBody` to decode the `BASE64` case belongs in the web layer.
   
   ### Are You Willing to Submit a Pull Request?
   
   - [x] Yes, I am willing to submit a pull request.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to