add live reloading support for peer config

This commit is contained in:
ginuerzh 2018-11-12 16:28:22 +08:00
parent c91e424cbe
commit d9cce6332c
6 changed files with 189 additions and 115 deletions

View File

@ -173,55 +173,6 @@ func parseIP(s string, port string) (ips []string) {
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 {
if s == "" {
return nil

View File

@ -6,6 +6,7 @@ import (
"flag"
"fmt"
"net"
// _ "net/http/pprof"
"os"
"runtime"
@ -102,65 +103,23 @@ func (r *route) initChain() (*gost.Chain, error) {
ngroup.ID = gid
gid++
// parse the base node
// parse the base nodes
nodes, err := parseChainNode(ns)
if err != nil {
return nil, err
}
nid := 1 // node ID
for i := range nodes {
nodes[i].ID = nid
nid++
}
ngroup.AddNode(nodes...)
// parse peer nodes if exists
peerCfg, err := loadPeerConfig(nodes[0].Get("peer"))
if err != nil {
log.Log(err)
}
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.
}
}
go gost.PeriodReload(&peerConfig{
group: ngroup,
baseNodes: nodes,
}, nodes[0].Get("peer"))
chain.AddNodeGroup(ngroup)
}

163
cmd/gost/peer.go Normal file
View 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{}
}
}

View File

@ -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
View 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

View File

@ -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) {
if group == nil {
return
@ -188,6 +188,11 @@ func (group *NodeGroup) AddNode(node ...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.
func (group *NodeGroup) SetSelector(selector NodeSelector, opts ...SelectOption) {
if group == nil {