This is an automated email from the ASF dual-hosted git repository.
lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git
The following commit(s) were added to refs/heads/rocketmq-studio by this push:
new 7a1b8eeb4 fix(rmqctl): ignore non-positive --timeout instead of
failing every request (#4627)
7a1b8eeb4 is described below
commit 7a1b8eeb47ba3bc2e701ddffd49938de988e6c22
Author: Apulupie <[email protected]>
AuthorDate: Mon Sep 21 21:07:02 2026 +0800
fix(rmqctl): ignore non-positive --timeout instead of failing every request
(#4627)
`rmqctl/internal/studio/client.go` passed `target.Timeout` straight into
`context.WithTimeout`, so `--timeout 0s` — the usual spelling of "no client
timeout" — produced a deadline that had already expired and every call failed
with `context deadline exceeded` before the request left. `--timeout` is a
cobra `DurationVar` with no positivity check, `runtime.go` copies it into
`Target.Timeout` verbatim, and `error.go` maps the resulting timeout code to
"Increase --timeout", pointing the u [...]
`CallTool` now derives the context with `WithCancel` when the timeout is
not positive, making the two paths agree that a non-positive value means no
client-side deadline rather than falling back to a default. These were the only
two `context.WithTimeout` call sites in the client.
Fixes #4628
---
rmqctl/internal/studio/client.go | 10 +++++++++-
rmqctl/internal/studio/tools_test.go | 24 ++++++++++++++++++++++++
2 files changed, 33 insertions(+), 1 deletion(-)
diff --git a/rmqctl/internal/studio/client.go b/rmqctl/internal/studio/client.go
index 6eb24f376..04ec2c56d 100644
--- a/rmqctl/internal/studio/client.go
+++ b/rmqctl/internal/studio/client.go
@@ -114,7 +114,15 @@ func (c Client) request(ctx context.Context, target
Target, method string, path
}
reader = bytes.NewReader(payload)
}
- ctx, cancel := context.WithTimeout(ctx, target.Timeout)
+ // A non-positive timeout must not be passed to context.WithTimeout,
which would expire
+ // the context immediately and fail every request. Mirror
mcp_message.go and treat it as
+ // "no client-side timeout".
+ var cancel context.CancelFunc
+ if target.Timeout > 0 {
+ ctx, cancel = context.WithTimeout(ctx, target.Timeout)
+ } else {
+ ctx, cancel = context.WithCancel(ctx)
+ }
defer cancel()
requestURL, err := url.JoinPath(target.BaseURL(), path)
if err != nil {
diff --git a/rmqctl/internal/studio/tools_test.go
b/rmqctl/internal/studio/tools_test.go
index e7c535ffc..34aaf4169 100644
--- a/rmqctl/internal/studio/tools_test.go
+++ b/rmqctl/internal/studio/tools_test.go
@@ -63,6 +63,30 @@ func TestCallTool(t *testing.T) {
}
}
+func TestCallToolIgnoresNonPositiveTimeout(t *testing.T) {
+ server := httptest.NewServer(http.HandlerFunc(func(w
http.ResponseWriter, r *http.Request) {
+ _ = json.NewEncoder(w).Encode(map[string]any{
+ "code": 200, "message": "success",
+ "data": map[string]any{"items": []any{}},
+ })
+ }))
+ defer server.Close()
+
+ for name, timeout := range map[string]time.Duration{"zero": 0,
"negative": -time.Second} {
+ t.Run(name, func(t *testing.T) {
+ client := NewClient(server.Client())
+ result, err := client.CallTool(context.Background(),
Target{
+ Server: server.URL, InstanceID: "instance-dev",
+ Credential: Credential{AccessKey: "test-ak",
SecretKey: "test-sk"}, Timeout: timeout,
+ }, "rmq.topic.list", map[string]any{"instanceId":
"instance-dev"})
+ resultMap, ok := result.(map[string]any)
+ if err != nil || !ok || resultMap["items"] == nil {
+ t.Fatalf("result=%#v err=%v", result, err)
+ }
+ })
+ }
+}
+
func TestCallToolKeepsCatalogControlsInArguments(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w
http.ResponseWriter, r *http.Request) {
var request types.ToolCallRequest