mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-05-08 14:13:22 +00:00
Config: Support env XRAY_JSON_STRICT=true (#6053)
https://github.com/XTLS/Xray-core/pull/6053#issuecomment-4363840170 --------- Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
This commit is contained in:
@@ -17,6 +17,7 @@ const (
|
|||||||
UseFreedomSplice = "xray.buf.splice"
|
UseFreedomSplice = "xray.buf.splice"
|
||||||
UseVmessPadding = "xray.vmess.padding"
|
UseVmessPadding = "xray.vmess.padding"
|
||||||
UseCone = "xray.cone.disabled"
|
UseCone = "xray.cone.disabled"
|
||||||
|
UseStrictJSON = "xray.json.strict"
|
||||||
|
|
||||||
BufferSize = "xray.ray.buffer.size"
|
BufferSize = "xray.ray.buffer.size"
|
||||||
BrowserDialerAddress = "xray.browser.dialer"
|
BrowserDialerAddress = "xray.browser.dialer"
|
||||||
|
|||||||
@@ -5,12 +5,22 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
"github.com/xtls/xray-core/common/platform"
|
||||||
creflect "github.com/xtls/xray-core/common/reflect"
|
creflect "github.com/xtls/xray-core/common/reflect"
|
||||||
"github.com/xtls/xray-core/core"
|
"github.com/xtls/xray-core/core"
|
||||||
"github.com/xtls/xray-core/infra/conf"
|
"github.com/xtls/xray-core/infra/conf"
|
||||||
"github.com/xtls/xray-core/main/confloader"
|
"github.com/xtls/xray-core/main/confloader"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// UseStrictJSON, when true, makes JSON config decoders skip the custom
|
||||||
|
// comment-stripping reader and parse input as strict RFC 8259 JSON.
|
||||||
|
//
|
||||||
|
// Enabled by setting the env variable xray.json.strict=true (or its normalized
|
||||||
|
// form XRAY_JSON_STRICT=true). Default false preserves backward-compatible
|
||||||
|
// behavior for human-edited configs that may contain comments or other
|
||||||
|
// JSON5/JSONC syntax.
|
||||||
|
var UseStrictJSON = platform.NewEnvFlag(platform.UseStrictJSON).GetValue(func() string { return "" }) == "true"
|
||||||
|
|
||||||
func MergeConfigFromFiles(files []*core.ConfigSource) (string, error) {
|
func MergeConfigFromFiles(files []*core.ConfigSource) (string, error) {
|
||||||
c, err := mergeConfigs(files)
|
c, err := mergeConfigs(files)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -31,7 +41,11 @@ func mergeConfigs(files []*core.ConfigSource) (*conf.Config, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("failed to read config: ", file).Base(err)
|
return nil, errors.New("failed to read config: ", file).Base(err)
|
||||||
}
|
}
|
||||||
c, err := ReaderDecoderByFormat[file.Format](r)
|
decoder := ReaderDecoderByFormat[file.Format]
|
||||||
|
if file.Format == "json" && UseStrictJSON {
|
||||||
|
decoder = DecodeJSONConfigStrict
|
||||||
|
}
|
||||||
|
c, err := decoder(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("failed to decode config: ", file).Base(err)
|
return nil, errors.New("failed to decode config: ", file).Base(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ func findOffset(b []byte, o int) *offset {
|
|||||||
|
|
||||||
// DecodeJSONConfig reads from reader and decode the config into *conf.Config
|
// DecodeJSONConfig reads from reader and decode the config into *conf.Config
|
||||||
// syntax error could be detected.
|
// syntax error could be detected.
|
||||||
|
//
|
||||||
|
// Permissive: accepts JSON with Java/Python-style comments via json_reader.Reader.
|
||||||
|
// Used for local files and stdin where the config is human-edited.
|
||||||
func DecodeJSONConfig(reader io.Reader) (*conf.Config, error) {
|
func DecodeJSONConfig(reader io.Reader) (*conf.Config, error) {
|
||||||
jsonConfig := &conf.Config{}
|
jsonConfig := &conf.Config{}
|
||||||
|
|
||||||
@@ -69,6 +72,24 @@ func DecodeJSONConfig(reader io.Reader) (*conf.Config, error) {
|
|||||||
return jsonConfig, nil
|
return jsonConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DecodeJSONConfigStrict reads standard RFC 8259 JSON without comment-stripping.
|
||||||
|
// Used for remote sources (http/https/http+unix) where the payload is produced by
|
||||||
|
// automated systems and cannot contain JSON5/JSONC extensions. Avoids the
|
||||||
|
// byte-by-byte comment stripper and TeeReader, which are significant overhead on
|
||||||
|
// large configs.
|
||||||
|
func DecodeJSONConfigStrict(reader io.Reader) (*conf.Config, error) {
|
||||||
|
data, err := io.ReadAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("failed to read config file").Base(err)
|
||||||
|
}
|
||||||
|
jsonConfig := &conf.Config{}
|
||||||
|
if err := json.Unmarshal(data, jsonConfig); err != nil {
|
||||||
|
return nil, errors.New("failed to parse remote JSON config").Base(err)
|
||||||
|
}
|
||||||
|
return jsonConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func LoadJSONConfig(reader io.Reader) (*core.Config, error) {
|
func LoadJSONConfig(reader io.Reader) (*core.Config, error) {
|
||||||
jsonConfig, err := DecodeJSONConfig(reader)
|
jsonConfig, err := DecodeJSONConfig(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -41,6 +41,13 @@ func init() {
|
|||||||
}
|
}
|
||||||
return cf.Build()
|
return cf.Build()
|
||||||
case io.Reader:
|
case io.Reader:
|
||||||
|
if serial.UseStrictJSON {
|
||||||
|
cfg, err := serial.DecodeJSONConfigStrict(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cfg.Build()
|
||||||
|
}
|
||||||
return serial.LoadJSONConfig(v)
|
return serial.LoadJSONConfig(v)
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("unknown type")
|
return nil, errors.New("unknown type")
|
||||||
|
|||||||
Reference in New Issue
Block a user