Browse Source

mconfig subcommand ready

vcptr 6 years ago
parent
commit
904db6bd61

+ 9 - 0
common/log/logger.go

@@ -119,6 +119,15 @@ func CreateStdoutLogWriter() WriterCreator {
 	}
 }
 
+// CreateStderrLogWriter returns a LogWriterCreator that creates LogWriter for stderr.
+func CreateStderrLogWriter() WriterCreator {
+	return func() Writer {
+		return &consoleLogWriter{
+			logger: log.New(os.Stderr, "", log.Ldate|log.Ltime),
+		}
+	}
+}
+
 // CreateFileLogWriter returns a LogWriterCreator that creates LogWriter for the given file.
 func CreateFileLogWriter(path string) (WriterCreator, error) {
 	file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)

+ 3 - 0
common/platform/ctlcmd/ctlcmd.go

@@ -39,6 +39,9 @@ func Run(args []string, input io.Reader) (buf.MultiBuffer, error) {
 		}
 		return nil, newError(msg).Base(err)
 	}
+	if !errBuffer.IsEmpty() {
+		newError("v2ctl > ", errBuffer.String()).AtInfo().WriteToLog()
+	}
 
 	return outBuffer.MultiBuffer, nil
 }

+ 10 - 1
infra/conf/serial/loader.go

@@ -38,7 +38,7 @@ func findOffset(b []byte, o int) *offset {
 	return &offset{line: line, char: char}
 }
 
-func LoadJSONConfig(reader io.Reader) (*core.Config, error) {
+func DecodeJSONConfig(reader io.Reader) (*conf.Config, error) {
 	jsonConfig := &conf.Config{}
 
 	jsonContent := bytes.NewBuffer(make([]byte, 0, 10240))
@@ -62,6 +62,15 @@ func LoadJSONConfig(reader io.Reader) (*core.Config, error) {
 		return nil, newError("failed to read config file").Base(err)
 	}
 
+	return jsonConfig, nil
+}
+
+func LoadJSONConfig(reader io.Reader) (*core.Config, error) {
+	jsonConfig, err := DecodeJSONConfig(reader)
+	if err != nil {
+		return nil, err
+	}
+
 	pbConfig, err := jsonConfig.Build()
 	if err != nil {
 		return nil, newError("failed to parse json config").Base(err)

+ 5 - 2
infra/conf/v2ray.go

@@ -327,7 +327,7 @@ func (c *Config) findOutboundTag(tag string) int {
 }
 
 // Override method accepts another Config overrides the current attribute
-func (c *Config) Override(o *Config) {
+func (c *Config) Override(o *Config, fn string) {
 
 	// only process the non-deprecated members
 
@@ -361,9 +361,10 @@ func (c *Config) Override(o *Config) {
 		if len(c.InboundConfigs) > 0 && len(o.InboundConfigs) == 1 {
 			if idx := c.findInboundTag(o.InboundConfigs[0].Tag); idx > -1 {
 				c.InboundConfigs[idx] = o.InboundConfigs[0]
-				newError("updated inbound with tag: ", o.InboundConfigs[0].Tag).AtInfo().WriteToLog()
+				newError("<", fn, "> updated inbound with tag: ", o.InboundConfigs[0].Tag).AtInfo().WriteToLog()
 			} else {
 				c.InboundConfigs = append(c.InboundConfigs, o.InboundConfigs[0])
+				newError("<", fn, "> appended inbound with tag: ", o.InboundConfigs[0].Tag).AtInfo().WriteToLog()
 			}
 		} else {
 			c.InboundConfigs = o.InboundConfigs
@@ -375,8 +376,10 @@ func (c *Config) Override(o *Config) {
 		if len(c.OutboundConfigs) > 0 && len(o.OutboundConfigs) == 1 {
 			if idx := c.findOutboundTag(o.OutboundConfigs[0].Tag); idx > -1 {
 				c.OutboundConfigs[idx] = o.OutboundConfigs[0]
+				newError("<", fn, "> updated outbound with tag: ", o.OutboundConfigs[0].Tag).AtInfo().WriteToLog()
 			} else {
 				c.OutboundConfigs = append(c.OutboundConfigs, o.OutboundConfigs[0])
+				newError("<", fn, "> updated outbound with tag: ", o.OutboundConfigs[0].Tag).AtInfo().WriteToLog()
 			}
 		} else {
 			c.OutboundConfigs = o.OutboundConfigs

+ 24 - 16
infra/control/fetch.go

@@ -5,6 +5,7 @@ import (
 	"net/url"
 	"os"
 	"strings"
+	"time"
 
 	"v2ray.com/core/common"
 	"v2ray.com/core/common/buf"
@@ -23,46 +24,53 @@ func (c *FetchCommand) Description() Description {
 	}
 }
 
-func (c *FetchCommand) isValidScheme(scheme string) bool {
-	scheme = strings.ToLower(scheme)
-	return scheme == "http" || scheme == "https"
-}
-
 func (c *FetchCommand) Execute(args []string) error {
 	if len(args) < 1 {
 		return newError("empty url")
 	}
-	target := args[0]
+	content, err := FetchHTTPContent(args[0])
+	if err != nil {
+		return newError("failed to read HTTP response").Base(err)
+	}
+
+	os.Stdout.Write(content)
+	return nil
+}
+
+func FetchHTTPContent(target string) ([]byte, error) {
+
 	parsedTarget, err := url.Parse(target)
 	if err != nil {
-		return newError("invalid URL: ", target).Base(err)
+		return nil, newError("invalid URL: ", target).Base(err)
 	}
-	if !c.isValidScheme(parsedTarget.Scheme) {
-		return newError("invalid scheme: ", parsedTarget.Scheme)
+
+	if s := strings.ToLower(parsedTarget.Scheme); s != "http" && s != "https" {
+		return nil, newError("invalid scheme: ", parsedTarget.Scheme)
 	}
 
-	client := &http.Client{}
+	client := &http.Client{
+		Timeout: 30 * time.Second,
+	}
 	resp, err := client.Do(&http.Request{
 		Method: "GET",
 		URL:    parsedTarget,
 		Close:  true,
 	})
 	if err != nil {
-		return newError("failed to dial to ", target).Base(err)
+		return nil, newError("failed to dial to ", target).Base(err)
 	}
+	defer resp.Body.Close()
 
 	if resp.StatusCode != 200 {
-		return newError("unexpected HTTP status code: ", resp.StatusCode)
+		return nil, newError("unexpected HTTP status code: ", resp.StatusCode)
 	}
 
 	content, err := buf.ReadAllToBytes(resp.Body)
 	if err != nil {
-		return newError("failed to read HTTP response").Base(err)
+		return nil, newError("failed to read HTTP response").Base(err)
 	}
 
-	os.Stdout.Write(content)
-
-	return nil
+	return content, nil
 }
 
 func init() {

+ 4 - 1
infra/control/main/main.go

@@ -5,7 +5,8 @@ import (
 	"fmt"
 	"os"
 
-	_ "v2ray.com/core/infra/conf/command"
+	commlog "v2ray.com/core/common/log"
+	// _ "v2ray.com/core/infra/conf/command"
 	"v2ray.com/core/infra/control"
 )
 
@@ -17,6 +18,8 @@ func getCommandName() string {
 }
 
 func main() {
+	// let the v2ctl prints log at stderr
+	commlog.RegisterHandler(commlog.NewLogger(commlog.CreateStderrLogWriter()))
 	name := getCommandName()
 	cmd := control.GetCommand(name)
 	if cmd == nil {

+ 78 - 0
infra/control/mconfig.go

@@ -0,0 +1,78 @@
+package control
+
+import (
+	"bytes"
+	"io"
+	"io/ioutil"
+	"os"
+	"strings"
+
+	"github.com/golang/protobuf/proto"
+	"v2ray.com/core/common"
+	"v2ray.com/core/infra/conf"
+	"v2ray.com/core/infra/conf/serial"
+)
+
+type MconfigCommand struct{}
+
+func (c *MconfigCommand) Name() string {
+	return "mconfig"
+}
+
+func (c *MconfigCommand) Description() Description {
+	return Description{
+		Short: "merge multiple json config",
+		Usage: []string{"v2ctl mconfig 1.json 2.json <url>.json"},
+	}
+}
+
+func (c *MconfigCommand) Execute(args []string) error {
+	if len(args) < 1 {
+		return newError("empty config list")
+	}
+
+	conf := &conf.Config{}
+	for _, arg := range args {
+		r, err := c.LoadArg(arg)
+		common.Must(err)
+		c, err := serial.DecodeJSONConfig(r)
+		common.Must(err)
+		conf.Override(c, arg)
+	}
+
+	pbConfig, err := conf.Build()
+	if err != nil {
+		return err
+	}
+
+	bytesConfig, err := proto.Marshal(pbConfig)
+	if err != nil {
+		return newError("failed to marshal proto config").Base(err)
+	}
+
+	if _, err := os.Stdout.Write(bytesConfig); err != nil {
+		return newError("failed to write proto config").Base(err)
+	}
+
+	return nil
+}
+
+func (c *MconfigCommand) LoadArg(arg string) (out io.Reader, err error) {
+
+	var data []byte
+	if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
+		data, err = FetchHTTPContent(arg)
+	} else {
+		data, err = ioutil.ReadFile(arg)
+	}
+
+	if err != nil {
+		return
+	}
+	out = bytes.NewBuffer(data)
+	return
+}
+
+func init() {
+	common.Must(RegisterCommand(&MconfigCommand{}))
+}