Browse Source

V5: Add TOML Support (rebased from 36ba06837f68db1e04fd090f36bf10d85cd5ced7)

秋のかえで 4 years ago
parent
commit
dde9463275

+ 1 - 0
go.mod

@@ -10,6 +10,7 @@ require (
 	github.com/jhump/protoreflect v1.8.2
 	github.com/jhump/protoreflect v1.8.2
 	github.com/lucas-clemente/quic-go v0.21.1
 	github.com/lucas-clemente/quic-go v0.21.1
 	github.com/miekg/dns v1.1.42
 	github.com/miekg/dns v1.1.42
+	github.com/pelletier/go-toml v1.8.1
 	github.com/pires/go-proxyproto v0.5.0
 	github.com/pires/go-proxyproto v0.5.0
 	github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c
 	github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c
 	github.com/stretchr/testify v1.7.0
 	github.com/stretchr/testify v1.7.0

+ 20 - 0
infra/conf/json/toml.go

@@ -0,0 +1,20 @@
+package json
+
+import (
+	"encoding/json"
+
+	"github.com/pelletier/go-toml"
+)
+
+// FromTOML convert toml to json
+func FromTOML(v []byte) ([]byte, error) {
+	m := make(map[string]interface{})
+	if err := toml.Unmarshal(v, &m); err != nil {
+		return nil, err
+	}
+	j, err := json.Marshal(m)
+	if err != nil {
+		return nil, err
+	}
+	return j, nil
+}

+ 113 - 0
infra/conf/json/toml_test.go

@@ -0,0 +1,113 @@
+package json
+
+import (
+	"encoding/json"
+	"testing"
+)
+
+func TestTOMLToJSON_V2Style(t *testing.T) {
+	input := `
+[log]
+loglevel = 'debug'
+
+[[inbounds]]
+port = 10800
+listen = '127.0.0.1'
+protocol = 'socks'
+
+[inbounds.settings]
+udp = true
+
+[[outbounds]]
+protocol = 'vmess'
+[[outbounds.settings.vnext]]
+port = 443
+address = 'example.com'
+
+[[outbounds.settings.vnext.users]]
+id = '98a15fa6-2eb1-edd5-50ea-cfc428aaab78'
+
+[outbounds.streamSettings]
+network = 'tcp'
+security = 'tls'
+`
+	expected := `
+{
+    "log": {
+        "loglevel": "debug"
+    },
+    "inbounds": [{
+        "port": 10800,
+        "listen": "127.0.0.1",
+        "protocol": "socks",
+        "settings": {
+            "udp": true
+        }
+    }],
+    "outbounds": [{
+        "protocol": "vmess",
+        "settings": {
+            "vnext": [{
+                "port": 443,
+                "address": "example.com",
+                "users": [{
+                    "id": "98a15fa6-2eb1-edd5-50ea-cfc428aaab78"
+                }]
+            }]
+        },
+        "streamSettings": {
+            "network": "tcp",
+            "security": "tls"
+        }
+    }]
+}
+`
+	bs, err := FromTOML([]byte(input))
+	if err != nil {
+		t.Error(err)
+	}
+	m := make(map[string]interface{})
+	json.Unmarshal(bs, &m)
+	assertResult(t, m, expected)
+}
+func TestTOMLToJSON_ValueTypes(t *testing.T) {
+	input := `
+boolean = [ true, false, true, false ]
+float = [ 3.14, 685_230.15 ]
+int = [ 123, 685_230 ]
+string = [ "哈哈", "Hello world", "newline newline2" ]
+date = [ "2018-02-17" ]
+datetime = [ "2018-02-17T15:02:31+08:00" ]
+mixed = [ true, false, 1, 0, "hello" ]
+1 = 0
+true = true
+str = "hello"
+
+[null]
+nodeName = "node"
+`
+	expected := `
+{
+    "boolean": [true, false, true, false],
+    "float": [3.14, 685230.15],
+    "int": [123, 685230],
+    "null": {
+        "nodeName": "node"
+    },
+    "string": ["哈哈", "Hello world",  "newline newline2"],
+    "date": ["2018-02-17"],
+    "datetime": ["2018-02-17T15:02:31+08:00"],
+    "mixed": [true,false,1,0,"hello"],
+    "1": 0,
+    "true": true,
+    "str": "hello"
+}
+`
+	bs, err := FromTOML([]byte(input))
+	if err != nil {
+		t.Error(err)
+	}
+	m := make(map[string]interface{})
+	json.Unmarshal(bs, &m)
+	assertResult(t, m, expected)
+}

+ 1 - 1
main/commands/all/api/shared_test.go

@@ -3,7 +3,7 @@ package api
 import (
 import (
 	"testing"
 	"testing"
 
 
-	statsService "v2ray.com/core/app/stats/command"
+	statsService "github.com/v2fly/v2ray-core/v4/app/stats/command"
 )
 )
 
 
 func TestEmptyResponese_0(t *testing.T) {
 func TestEmptyResponese_0(t *testing.T) {

+ 15 - 10
main/commands/all/convert.go

@@ -3,6 +3,7 @@ package all
 import (
 import (
 	"bytes"
 	"bytes"
 	"encoding/json"
 	"encoding/json"
+	"github.com/pelletier/go-toml"
 	"google.golang.org/protobuf/proto"
 	"google.golang.org/protobuf/proto"
 	"os"
 	"os"
 	"strings"
 	"strings"
@@ -24,12 +25,12 @@ Arguments:
 
 
 	-i, -input
 	-i, -input
 		Specify the input format.
 		Specify the input format.
-		Available values: "json", "yaml"
+		Available values: "json", "toml", "yaml"
 		Default: "json"
 		Default: "json"
 
 
 	-o, -output
 	-o, -output
 		Specify the output format
 		Specify the output format
-		Available values: "json", "yaml", "protobuf" / "pb"
+		Available values: "json", "toml", "yaml", "protobuf" / "pb"
 		Default: "json"
 		Default: "json"
 
 
 	-r
 	-r
@@ -38,16 +39,14 @@ Arguments:
 Examples:
 Examples:
 
 
 	{{.Exec}} {{.LongName}} -output=protobuf config.json           (1)
 	{{.Exec}} {{.LongName}} -output=protobuf config.json           (1)
-	{{.Exec}} {{.LongName}} -output=yaml config.json               (2)
-	{{.Exec}} {{.LongName}} -input=yaml config.yaml                (3)
-	{{.Exec}} {{.LongName}} "path/to/dir"                          (4)
-	{{.Exec}} {{.LongName}} -i yaml -o protobuf c1.yaml <url>.yaml (5)
+	{{.Exec}} {{.LongName}} -input=toml config.toml                (2)
+	{{.Exec}} {{.LongName}} "path/to/dir"                          (3)
+	{{.Exec}} {{.LongName}} -i yaml -o protobuf c1.yaml <url>.yaml (4)
 
 
 (1) Convert json to protobuf
 (1) Convert json to protobuf
-(2) Convert json to yaml
-(3) Convert yaml to json
-(4) Merge json files in dir
-(5) Merge yaml files and convert to protobuf
+(2) Convert toml to json
+(3) Merge json files in dir
+(4) Merge yaml files and convert to protobuf
 
 
 Use "{{.Exec}} help config-merge" for more information about merge.
 Use "{{.Exec}} help config-merge" for more information about merge.
 `,
 `,
@@ -64,6 +63,7 @@ var (
 )
 )
 var formatExtensions = map[string][]string{
 var formatExtensions = map[string][]string{
 	"json": {".json", ".jsonc"},
 	"json": {".json", ".jsonc"},
+	"toml": {".toml"},
 	"yaml": {".yaml", ".yml"},
 	"yaml": {".yaml", ".yml"},
 }
 }
 
 
@@ -97,6 +97,11 @@ func executeConvert(cmd *base.Command, args []string) {
 		if err != nil {
 		if err != nil {
 			base.Fatalf("failed to marshal json: %s", err)
 			base.Fatalf("failed to marshal json: %s", err)
 		}
 		}
+	case "toml":
+		out, err = toml.Marshal(m)
+		if err != nil {
+			base.Fatalf("failed to marshal json: %s", err)
+		}
 	case "yaml":
 	case "yaml":
 		out, err = yaml.Marshal(m)
 		out, err = yaml.Marshal(m)
 		if err != nil {
 		if err != nil {

+ 25 - 0
main/commands/all/convert_confs.go

@@ -23,6 +23,15 @@ func mergeConvertToMap(files []string, format string) map[string]interface{} {
 		if err != nil {
 		if err != nil {
 			base.Fatalf("failed to load json: %s", err)
 			base.Fatalf("failed to load json: %s", err)
 		}
 		}
+	case "toml":
+		bs, err := tomlsToJSONs(files)
+		if err != nil {
+			base.Fatalf("failed to convert toml to json: %s", err)
+		}
+		m, err = merge.BytesToMap(bs)
+		if err != nil {
+			base.Fatalf("failed to merge converted json: %s", err)
+		}
 	case "yaml":
 	case "yaml":
 		bs, err := yamlsToJSONs(files)
 		bs, err := yamlsToJSONs(files)
 		if err != nil {
 		if err != nil {
@@ -111,3 +120,19 @@ func yamlsToJSONs(files []string) ([][]byte, error) {
 	}
 	}
 	return jsons, nil
 	return jsons, nil
 }
 }
+
+func tomlsToJSONs(files []string) ([][]byte, error) {
+	jsons := make([][]byte, 0)
+	for _, file := range files {
+		bs, err := cmdarg.LoadArgToBytes(file)
+		if err != nil {
+			return nil, err
+		}
+		j, err := json.FromTOML(bs)
+		if err != nil {
+			return nil, err
+		}
+		jsons = append(jsons, j)
+	}
+	return jsons, nil
+}

+ 4 - 1
main/commands/all/format_doc.go

@@ -11,7 +11,10 @@ var docFormat = &base.Command{
 {{.Exec}} supports different config formats:
 {{.Exec}} supports different config formats:
 
 
 	* json (.json, .jsonc)
 	* json (.json, .jsonc)
-	  The default loader, multiple config files support.
+	  The default loader, multiple config files support.	
+
+	* toml (.toml)
+	  The toml loader, multiple config files support.
 
 
 	* yaml (.yml)
 	* yaml (.yml)
 	  The yaml loader, multiple config files support.
 	  The yaml loader, multiple config files support.

+ 1 - 1
main/commands/all/merge_doc.go

@@ -14,7 +14,7 @@ Merging of config files is applied in following commands:
 	{{.Exec}} test -c c1.yaml -c c2.yaml ...
 	{{.Exec}} test -c c1.yaml -c c2.yaml ...
 	{{.Exec}} convert c1.json dir1 ...
 	{{.Exec}} convert c1.json dir1 ...
 
 
-Support of yaml is implemented by converting yaml to json, 
+Support of toml and yaml is implemented by converting them to json, 
 both merge and load. So we take json as example here.
 both merge and load. So we take json as example here.
 
 
 Suppose we have 2 JSON files,
 Suppose we have 2 JSON files,

+ 3 - 0
main/distro/all/all.go

@@ -76,6 +76,9 @@ import (
 	// The following line loads JSON internally
 	// The following line loads JSON internally
 	_ "github.com/v2fly/v2ray-core/v4/main/json"
 	_ "github.com/v2fly/v2ray-core/v4/main/json"
 
 
+	// TOML config support.
+	_ "github.com/v2fly/v2ray-core/v4/main/toml"
+
 	// YAML config support.
 	// YAML config support.
 	_ "github.com/v2fly/v2ray-core/v4/main/yaml"
 	_ "github.com/v2fly/v2ray-core/v4/main/yaml"
 
 

+ 69 - 0
main/toml/toml.go

@@ -0,0 +1,69 @@
+package toml
+
+import (
+	"bytes"
+	"errors"
+	"io"
+	"io/ioutil"
+
+	"github.com/v2fly/v2ray-core/v4"
+	"github.com/v2fly/v2ray-core/v4/common"
+	"github.com/v2fly/v2ray-core/v4/common/cmdarg"
+	"github.com/v2fly/v2ray-core/v4/infra/conf/json"
+	"github.com/v2fly/v2ray-core/v4/infra/conf/merge"
+	"github.com/v2fly/v2ray-core/v4/infra/conf/serial"
+)
+
+func init() {
+	common.Must(core.RegisterConfigLoader(&core.ConfigFormat{
+		Name:      []string{"TOML"},
+		Extension: []string{".toml"},
+		Loader: func(input interface{}) (*core.Config, error) {
+			switch v := input.(type) {
+			case cmdarg.Arg:
+				bs, err := tomlsToJSONs(v)
+				if err != nil {
+					return nil, err
+				}
+				data, err := merge.BytesToJSON(bs)
+				if err != nil {
+					return nil, err
+				}
+				r := bytes.NewReader(data)
+				cf, err := serial.DecodeJSONConfig(r)
+				if err != nil {
+					return nil, err
+				}
+				return cf.Build()
+			case io.Reader:
+				bs, err := ioutil.ReadAll(v)
+				if err != nil {
+					return nil, err
+				}
+				bs, err = json.FromTOML(bs)
+				if err != nil {
+					return nil, err
+				}
+				return serial.LoadJSONConfig(bytes.NewBuffer(bs))
+			default:
+				return nil, errors.New("unknow type")
+			}
+		},
+	}))
+}
+
+func tomlsToJSONs(files []string) ([][]byte, error) {
+	jsons := make([][]byte, 0)
+	for _, file := range files {
+		bs, err := cmdarg.LoadArgToBytes(file)
+		if err != nil {
+			return nil, err
+		}
+		j, err := json.FromTOML(bs)
+		if err != nil {
+			return nil, err
+		}
+		jsons = append(jsons, j)
+	}
+	return jsons, nil
+}