add live reloading support for peer config
This commit is contained in:
parent
c91e424cbe
commit
d9cce6332c
@ -173,55 +173,6 @@ func parseIP(s string, port string) (ips []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type peerConfig struct {
|
|
||||||
Strategy string `json:"strategy"`
|
|
||||||
Filters []string `json:"filters"`
|
|
||||||
MaxFails int `json:"max_fails"`
|
|
||||||
FailTimeout int `json:"fail_timeout"`
|
|
||||||
Nodes []string `json:"nodes"`
|
|
||||||
Bypass *bypass `json:"bypass"` // global bypass
|
|
||||||
}
|
|
||||||
|
|
||||||
type bypass struct {
|
|
||||||
Reverse bool `json:"reverse"`
|
|
||||||
Patterns []string `json:"patterns"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadPeerConfig(peer string) (config peerConfig, err error) {
|
|
||||||
if peer == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
content, err := ioutil.ReadFile(peer)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(content, &config)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *peerConfig) Validate() {
|
|
||||||
if cfg.MaxFails <= 0 {
|
|
||||||
cfg.MaxFails = 1
|
|
||||||
}
|
|
||||||
if cfg.FailTimeout <= 0 {
|
|
||||||
cfg.FailTimeout = 30 // seconds
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseStrategy(s string) gost.Strategy {
|
|
||||||
switch s {
|
|
||||||
case "random":
|
|
||||||
return &gost.RandomStrategy{}
|
|
||||||
case "fifo":
|
|
||||||
return &gost.FIFOStrategy{}
|
|
||||||
case "round":
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
return &gost.RoundStrategy{}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseBypass(s string) *gost.Bypass {
|
func parseBypass(s string) *gost.Bypass {
|
||||||
if s == "" {
|
if s == "" {
|
||||||
return nil
|
return nil
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
// _ "net/http/pprof"
|
// _ "net/http/pprof"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -102,65 +103,23 @@ func (r *route) initChain() (*gost.Chain, error) {
|
|||||||
ngroup.ID = gid
|
ngroup.ID = gid
|
||||||
gid++
|
gid++
|
||||||
|
|
||||||
// parse the base node
|
// parse the base nodes
|
||||||
nodes, err := parseChainNode(ns)
|
nodes, err := parseChainNode(ns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
nid := 1 // node ID
|
nid := 1 // node ID
|
||||||
|
|
||||||
for i := range nodes {
|
for i := range nodes {
|
||||||
nodes[i].ID = nid
|
nodes[i].ID = nid
|
||||||
nid++
|
nid++
|
||||||
}
|
}
|
||||||
ngroup.AddNode(nodes...)
|
ngroup.AddNode(nodes...)
|
||||||
|
|
||||||
// parse peer nodes if exists
|
go gost.PeriodReload(&peerConfig{
|
||||||
peerCfg, err := loadPeerConfig(nodes[0].Get("peer"))
|
group: ngroup,
|
||||||
if err != nil {
|
baseNodes: nodes,
|
||||||
log.Log(err)
|
}, nodes[0].Get("peer"))
|
||||||
}
|
|
||||||
peerCfg.Validate()
|
|
||||||
|
|
||||||
strategy := peerCfg.Strategy
|
|
||||||
// overwrite the strategry in the peer config if `strategy` param exists.
|
|
||||||
if s := nodes[0].Get("strategy"); s != "" {
|
|
||||||
strategy = s
|
|
||||||
}
|
|
||||||
ngroup.Options = append(ngroup.Options,
|
|
||||||
gost.WithFilter(&gost.FailFilter{
|
|
||||||
MaxFails: peerCfg.MaxFails,
|
|
||||||
FailTimeout: time.Duration(peerCfg.FailTimeout) * time.Second,
|
|
||||||
}),
|
|
||||||
gost.WithStrategy(parseStrategy(strategy)),
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, s := range peerCfg.Nodes {
|
|
||||||
nodes, err = parseChainNode(s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range nodes {
|
|
||||||
nodes[i].ID = nid
|
|
||||||
nid++
|
|
||||||
}
|
|
||||||
|
|
||||||
ngroup.AddNode(nodes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
var bypass *gost.Bypass
|
|
||||||
// global bypass
|
|
||||||
if peerCfg.Bypass != nil {
|
|
||||||
bypass = gost.NewBypassPatterns(peerCfg.Bypass.Reverse, peerCfg.Bypass.Patterns...)
|
|
||||||
}
|
|
||||||
nodes = ngroup.Nodes()
|
|
||||||
for i := range nodes {
|
|
||||||
if nodes[i].Bypass == nil {
|
|
||||||
nodes[i].Bypass = bypass // use global bypass if local bypass does not exist.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
chain.AddNodeGroup(ngroup)
|
chain.AddNodeGroup(ngroup)
|
||||||
}
|
}
|
||||||
|
163
cmd/gost/peer.go
Normal file
163
cmd/gost/peer.go
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ginuerzh/gost"
|
||||||
|
)
|
||||||
|
|
||||||
|
type peerConfig struct {
|
||||||
|
Strategy string `json:"strategy"`
|
||||||
|
MaxFails int `json:"max_fails"`
|
||||||
|
FailTimeout time.Duration `json:"fail_timeout"`
|
||||||
|
period time.Duration // the period for live reloading
|
||||||
|
Nodes []string `json:"nodes"`
|
||||||
|
group *gost.NodeGroup
|
||||||
|
baseNodes []gost.Node
|
||||||
|
}
|
||||||
|
|
||||||
|
type bypass struct {
|
||||||
|
Reverse bool `json:"reverse"`
|
||||||
|
Patterns []string `json:"patterns"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePeerConfig(cfg string, group *gost.NodeGroup, baseNodes []gost.Node) *peerConfig {
|
||||||
|
pc := &peerConfig{
|
||||||
|
group: group,
|
||||||
|
baseNodes: baseNodes,
|
||||||
|
}
|
||||||
|
go gost.PeriodReload(pc, cfg)
|
||||||
|
return pc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *peerConfig) Validate() {
|
||||||
|
if cfg.MaxFails <= 0 {
|
||||||
|
cfg.MaxFails = 1
|
||||||
|
}
|
||||||
|
if cfg.FailTimeout <= 0 {
|
||||||
|
cfg.FailTimeout = 30 // seconds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *peerConfig) Reload(r io.Reader) error {
|
||||||
|
if err := cfg.parse(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cfg.Validate()
|
||||||
|
|
||||||
|
group := cfg.group
|
||||||
|
strategy := cfg.Strategy
|
||||||
|
if len(cfg.baseNodes) > 0 {
|
||||||
|
// overwrite the strategry in the peer config if `strategy` param exists.
|
||||||
|
if s := cfg.baseNodes[0].Get("strategy"); s != "" {
|
||||||
|
strategy = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
group.Options = append([]gost.SelectOption{},
|
||||||
|
gost.WithFilter(&gost.FailFilter{
|
||||||
|
MaxFails: cfg.MaxFails,
|
||||||
|
FailTimeout: time.Duration(cfg.FailTimeout) * time.Second,
|
||||||
|
}),
|
||||||
|
gost.WithStrategy(parseStrategy(strategy)),
|
||||||
|
)
|
||||||
|
|
||||||
|
gNodes := cfg.baseNodes
|
||||||
|
nid := len(gNodes) + 1
|
||||||
|
for _, s := range cfg.Nodes {
|
||||||
|
nodes, err := parseChainNode(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range nodes {
|
||||||
|
nodes[i].ID = nid
|
||||||
|
nid++
|
||||||
|
}
|
||||||
|
|
||||||
|
gNodes = append(gNodes, nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
group.SetNodes(gNodes...)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *peerConfig) parse(r io.Reader) error {
|
||||||
|
data, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// compatible with JSON format
|
||||||
|
if err := json.NewDecoder(bytes.NewReader(data)).Decode(cfg); err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
split := func(line string) []string {
|
||||||
|
if line == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if n := strings.IndexByte(line, '#'); n >= 0 {
|
||||||
|
line = line[:n]
|
||||||
|
}
|
||||||
|
line = strings.Replace(line, "\t", " ", -1)
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
|
||||||
|
var ss []string
|
||||||
|
for _, s := range strings.Split(line, " ") {
|
||||||
|
if s = strings.TrimSpace(s); s != "" {
|
||||||
|
ss = append(ss, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ss
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.Nodes = nil
|
||||||
|
scanner := bufio.NewScanner(bytes.NewReader(data))
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
ss := split(line)
|
||||||
|
if len(ss) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ss[0] {
|
||||||
|
case "strategy":
|
||||||
|
cfg.Strategy = ss[1]
|
||||||
|
case "max_fails":
|
||||||
|
cfg.MaxFails, _ = strconv.Atoi(ss[1])
|
||||||
|
case "fail_timeout":
|
||||||
|
cfg.FailTimeout, _ = time.ParseDuration(ss[1])
|
||||||
|
case "reload":
|
||||||
|
cfg.period, _ = time.ParseDuration(ss[1])
|
||||||
|
case "peer":
|
||||||
|
cfg.Nodes = append(cfg.Nodes, ss[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return scanner.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *peerConfig) Period() time.Duration {
|
||||||
|
return cfg.period
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseStrategy(s string) gost.Strategy {
|
||||||
|
switch s {
|
||||||
|
case "random":
|
||||||
|
return &gost.RandomStrategy{}
|
||||||
|
case "fifo":
|
||||||
|
return &gost.FIFOStrategy{}
|
||||||
|
case "round":
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
return &gost.RoundStrategy{}
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"strategy": "round",
|
|
||||||
"max_fails": 3,
|
|
||||||
"fail_timeout": 30,
|
|
||||||
"nodes":[
|
|
||||||
"socks5://:1081",
|
|
||||||
"socks://:1082",
|
|
||||||
"socks4a://:1083"
|
|
||||||
],
|
|
||||||
"bypass":{
|
|
||||||
"reverse": false,
|
|
||||||
"patterns": [
|
|
||||||
"10.0.0.1",
|
|
||||||
"192.168.0.0/24",
|
|
||||||
"*.example.com"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
14
cmd/gost/peer.txt
Normal file
14
cmd/gost/peer.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# strategy for node selecting
|
||||||
|
strategy random
|
||||||
|
|
||||||
|
max_fails 1
|
||||||
|
|
||||||
|
fail_timeout 30s
|
||||||
|
|
||||||
|
# period for live reloading
|
||||||
|
reload 10s
|
||||||
|
|
||||||
|
# peers
|
||||||
|
peer http://:18080
|
||||||
|
peer socks://:11080
|
||||||
|
peer ss://chacha20:123456@:18338
|
7
node.go
7
node.go
@ -180,7 +180,7 @@ func NewNodeGroup(nodes ...Node) *NodeGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddNode adds node or node list into group
|
// AddNode appends node or node list into group node.
|
||||||
func (group *NodeGroup) AddNode(node ...Node) {
|
func (group *NodeGroup) AddNode(node ...Node) {
|
||||||
if group == nil {
|
if group == nil {
|
||||||
return
|
return
|
||||||
@ -188,6 +188,11 @@ func (group *NodeGroup) AddNode(node ...Node) {
|
|||||||
group.nodes = append(group.nodes, node...)
|
group.nodes = append(group.nodes, node...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetNodes replaces the group nodes to the specified nodes.
|
||||||
|
func (group *NodeGroup) SetNodes(nodes ...Node) {
|
||||||
|
group.nodes = nodes
|
||||||
|
}
|
||||||
|
|
||||||
// SetSelector sets node selector with options for the group.
|
// SetSelector sets node selector with options for the group.
|
||||||
func (group *NodeGroup) SetSelector(selector NodeSelector, opts ...SelectOption) {
|
func (group *NodeGroup) SetSelector(selector NodeSelector, opts ...SelectOption) {
|
||||||
if group == nil {
|
if group == nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user