diff --git a/tools/conf/json/reader.go b/tools/conf/json/reader.go new file mode 100644 index 000000000..c6b1e4df9 --- /dev/null +++ b/tools/conf/json/reader.go @@ -0,0 +1,94 @@ +package json + +import ( + "io" +) + +type State byte + +const ( + StateContent State = 0 + StateEscape State = 1 + StateDoubleQuote State = 2 + StateSingleQuote State = 3 + StateComment State = 4 + StateSlash State = 5 + StateMultilineComment State = 6 + StateMultilineCommentStar State = 7 +) + +type Reader struct { + io.Reader + state State +} + +func (v *Reader) Read(b []byte) (int, error) { + n, err := v.Reader.Read(b) + if err != nil { + return n, err + } + p := b[:0] + for _, x := range b[:n] { + switch v.state { + case StateContent: + switch x { + case '"': + v.state = StateDoubleQuote + p = append(p, x) + case '\'': + v.state = StateSingleQuote + p = append(p, x) + case '\\': + v.state = StateEscape + case '#': + v.state = StateComment + case '/': + v.state = StateSlash + default: + p = append(p, x) + } + case StateEscape: + p = append(p, x) + v.state = StateContent + case StateDoubleQuote: + if x == '"' { + v.state = StateContent + } + p = append(p, x) + case StateSingleQuote: + if x == '\'' { + v.state = StateContent + } + p = append(p, x) + case StateComment: + if x == '\n' { + v.state = StateContent + } + case StateSlash: + switch x { + case '/': + v.state = StateComment + case '*': + v.state = StateMultilineComment + default: + p = append(p, '/', x) + } + case StateMultilineComment: + if x == '*' { + v.state = StateMultilineCommentStar + } + case StateMultilineCommentStar: + switch x { + case '/': + v.state = StateContent + case '*': + // Stay + default: + v.state = StateMultilineComment + } + default: + panic("Unknown state.") + } + } + return len(p), nil +} diff --git a/tools/conf/json/reader_test.go b/tools/conf/json/reader_test.go new file mode 100644 index 000000000..6cb4958b2 --- /dev/null +++ b/tools/conf/json/reader_test.go @@ -0,0 +1,47 @@ +package json_test + +import ( + "testing" + + "bytes" + "v2ray.com/core/testing/assert" + . "v2ray.com/core/tools/conf/json" +) + +func TestReader(t *testing.T) { + assert := assert.On(t) + + data := []struct { + input string + output string + }{ + { + ` +content #comment 1 +#comment 2 +content 2`, + ` +content content 2`}, + {`content`, `content`}, + {" ", " "}, + {`con/*abcd*/tent`, "content"}, + {` +text // adlkhdf /* +//comment adfkj +text 2*/`, ` +text text 2*`}, + {`"//"content`, `"//"content`}, + {`abcd'//'abcd`, `abcd'//'abcd`}, + } + + for _, testCase := range data { + reader := &Reader{ + Reader: bytes.NewReader([]byte(testCase.input)), + } + + actual := make([]byte, 1024) + n, err := reader.Read(actual) + assert.Error(err).IsNil() + assert.String(string(actual[:n])).Equals(testCase.output) + } +} diff --git a/tools/conf/v2ray.go b/tools/conf/v2ray.go index 93262f289..a702f9340 100644 --- a/tools/conf/v2ray.go +++ b/tools/conf/v2ray.go @@ -8,6 +8,7 @@ import ( "v2ray.com/core/common/errors" "v2ray.com/core/common/loader" v2net "v2ray.com/core/common/net" + json_reader "v2ray.com/core/tools/conf/json" ) var ( @@ -337,7 +338,9 @@ func (v *Config) Build() (*core.Config, error) { func init() { core.RegisterConfigLoader(core.ConfigFormat_JSON, func(input io.Reader) (*core.Config, error) { jsonConfig := &Config{} - decoder := json.NewDecoder(input) + decoder := json.NewDecoder(&json_reader.Reader{ + Reader: input, + }) err := decoder.Decode(jsonConfig) if err != nil { return nil, errors.Base(err).Message("Invalid V2Ray config.")