fix(api): cap the client traffic list a master pushes by entry count

pushClientTraffics decoded the pushed list whole, and each "{}" became a
full traffic row, so a body under the size cap could cost the panel
close to a gigabyte. The list is now decoded entry by entry and refused
past 262144 entries. The capped decoder the node runtime already used
moves to json_util so both share it.
This commit is contained in:
MHSanaei
2026-10-31 14:25:21 +01:00
parent 44a502a407
commit fe5d704d67
5 changed files with 82 additions and 38 deletions
+38
View File
@@ -0,0 +1,38 @@
package json_util
import (
"bytes"
"encoding/json"
"errors"
"fmt"
)
// ErrListTooLong is returned by DecodeCappedList for a list past its entry limit.
var ErrListTooLong = errors.New("list exceeds entry limit")
// DecodeCappedList decodes a JSON array entry by entry and stops at the first entry past
// maxEntries: each "{}" becomes a whole T, so a byte cap alone does not bound the result.
func DecodeCappedList[T any](raw []byte, maxEntries int) ([]T, error) {
raw = bytes.TrimSpace(raw)
if len(raw) == 0 || bytes.Equal(raw, []byte("null")) {
return nil, nil
}
dec := json.NewDecoder(bytes.NewReader(raw))
if tok, err := dec.Token(); err != nil {
return nil, err
} else if delim, ok := tok.(json.Delim); !ok || delim != '[' {
return nil, fmt.Errorf("expected a JSON array, got %v", tok)
}
var out []T
for dec.More() {
if len(out) >= maxEntries {
return nil, fmt.Errorf("%w (%d)", ErrListTooLong, maxEntries)
}
var item T
if err := dec.Decode(&item); err != nil {
return nil, err
}
out = append(out, item)
}
return out, nil
}