Skip to content

Commit e795bd6

Browse files
fix[backend](soar_commands): send done and close ws after single command result
1 parent 2536a4b commit e795bd6

1 file changed

Lines changed: 31 additions & 32 deletions

File tree

backend/modules/soar/handler/command_ws.go

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -154,42 +154,41 @@ func (h *CommandWSHandler) CommandStream(c *gin.Context) {
154154
}
155155
resultCh, errCh := h.agentClient.ProcessCommandStream(ctx, cmd)
156156

157-
for {
158-
select {
159-
case result, ok := <-resultCh:
160-
if !ok {
161-
_ = wsjson.Write(ctx, conn, wsMessage{Type: "done"})
162-
_ = conn.Close(websocket.StatusNormalClosure, "stream complete")
163-
return
164-
}
165-
output := result.GetResult()
166-
if h.variableUC != nil {
167-
if masked, mErr := h.variableUC.MaskSecrets(output); mErr != nil {
168-
_ = catcher.Error("CommandStream: mask secrets", mErr, nil)
169-
} else {
170-
output = masked
171-
}
172-
}
173-
msg := wsMessage{Type: "output", Data: output}
174-
if writeErr := wsjson.Write(ctx, conn, msg); writeErr != nil {
175-
cancel()
176-
return
177-
}
178-
179-
case grpcErr, ok := <-errCh:
180-
if !ok {
181-
return
182-
}
183-
if grpcErr != nil {
184-
_ = catcher.Error("CommandStream: grpc error", grpcErr, nil)
185-
errMsg, _ := json.Marshal(wsMessage{Type: "error", Message: grpcErr.Error()})
186-
_ = conn.Write(ctx, websocket.MessageText, errMsg)
187-
_ = conn.Close(websocket.StatusInternalError, "grpc error")
157+
// ponytail: one command → one CommandResult (see agent-manager ProcessCommand).
158+
// Server never closes the stream, so we don't wait for !ok — we finalize on the first result.
159+
select {
160+
case result, ok := <-resultCh:
161+
if !ok {
162+
_ = conn.Close(websocket.StatusNormalClosure, "stream complete")
163+
return
164+
}
165+
output := result.GetResult()
166+
if h.variableUC != nil {
167+
if masked, mErr := h.variableUC.MaskSecrets(output); mErr != nil {
168+
_ = catcher.Error("CommandStream: mask secrets", mErr, nil)
169+
} else {
170+
output = masked
188171
}
172+
}
173+
if writeErr := wsjson.Write(ctx, conn, wsMessage{Type: "output", Data: output}); writeErr != nil {
174+
_ = catcher.Error("CommandStream: write output", writeErr, nil)
175+
cancel()
189176
return
177+
}
178+
if doneErr := wsjson.Write(ctx, conn, wsMessage{Type: "done"}); doneErr != nil {
179+
_ = catcher.Error("CommandStream: write done", doneErr, nil)
180+
}
181+
_ = conn.Close(websocket.StatusNormalClosure, "command complete")
190182

191-
case <-ctx.Done():
183+
case grpcErr, ok := <-errCh:
184+
if !ok || grpcErr == nil {
192185
return
193186
}
187+
_ = catcher.Error("CommandStream: grpc error", grpcErr, nil)
188+
errMsg, _ := json.Marshal(wsMessage{Type: "error", Message: grpcErr.Error()})
189+
_ = conn.Write(ctx, websocket.MessageText, errMsg)
190+
_ = conn.Close(websocket.StatusInternalError, "grpc error")
191+
192+
case <-ctx.Done():
194193
}
195194
}

0 commit comments

Comments
 (0)