mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-02 11:15:03 +03:00
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:
@@ -3,9 +3,10 @@ package runtime
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/util/json_util"
|
||||
)
|
||||
|
||||
// Per-endpoint caps on node-reported lists: each "{}" decodes into a whole struct, so the
|
||||
@@ -19,34 +20,7 @@ const (
|
||||
maxNodeMapValues = 1 << 20
|
||||
)
|
||||
|
||||
var errRemoteListTooLong = errors.New("remote list exceeds entry limit")
|
||||
|
||||
// decodeCappedList decodes a JSON array entry by entry and stops at the first entry
|
||||
// past maxEntries; null decodes to a nil slice.
|
||||
func decodeCappedList[T any](raw json.RawMessage, 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)", errRemoteListTooLong, maxEntries)
|
||||
}
|
||||
var item T
|
||||
if err := dec.Decode(&item); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, item)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
var errRemoteListTooLong = json_util.ErrListTooLong
|
||||
|
||||
// decodeCappedValues unmarshals raw into v after checking it holds at most maxValues
|
||||
// JSON values in all, nested ones included.
|
||||
|
||||
Reference in New Issue
Block a user