Browse Source

Merge pull request #217 from fatedier/dev

bump version to 0.9.2
fatedier 8 years ago
parent
commit
edb97abf50

+ 15 - 7
Makefile.cross-compiles

@@ -3,15 +3,23 @@ export GO15VENDOREXPERIMENT := 1
 
 all: build
 
-build: gox app more
-
-gox:
-	go get github.com/mitchellh/gox
+build: app
 
 app:
-	gox -osarch "darwin/386 darwin/amd64 linux/386 linux/amd64 linux/arm windows/386 windows/amd64" ./src/...
-
-more:
+	env GOOS=darwin GOARCH=386 go build -o ./frpc_darwin_386 ./src/cmd/frpc
+	env GOOS=darwin GOARCH=386 go build -o ./frps_darwin_386 ./src/cmd/frps
+	env GOOS=darwin GOARCH=amd64 go build -o ./frpc_darwin_amd64 ./src/cmd/frpc
+	env GOOS=darwin GOARCH=amd64 go build -o ./frps_darwin_amd64 ./src/cmd/frps
+	env GOOS=linux GOARCH=386 go build -o ./frpc_linux_386 ./src/cmd/frpc
+	env GOOS=linux GOARCH=386 go build -o ./frps_linux_386 ./src/cmd/frps
+	env GOOS=linux GOARCH=amd64 go build -o ./frpc_linux_amd64 ./src/cmd/frpc
+	env GOOS=linux GOARCH=amd64 go build -o ./frps_linux_amd64 ./src/cmd/frps
+	env GOOS=linux GOARCH=arm go build -o ./frpc_linux_arm ./src/cmd/frpc
+	env GOOS=linux GOARCH=arm go build -o ./frps_linux_arm ./src/cmd/frps
+	env GOOS=windows GOARCH=386 go build -o ./frpc_windows_386.exe ./src/cmd/frpc
+	env GOOS=windows GOARCH=386 go build -o ./frps_windows_386.exe ./src/cmd/frps
+	env GOOS=windows GOARCH=amd64 go build -o ./frpc_windows_amd64.exe ./src/cmd/frpc
+	env GOOS=windows GOARCH=amd64 go build -o ./frps_windows_amd64.exe ./src/cmd/frps
 	env GOOS=linux GOARCH=mips64 go build -o ./frpc_linux_mips64 ./src/cmd/frpc
 	env GOOS=linux GOARCH=mips64 go build -o ./frps_linux_mips64 ./src/cmd/frps
 	env GOOS=linux GOARCH=mips64le go build -o ./frpc_linux_mips64le ./src/cmd/frpc

+ 2 - 2
README.md

@@ -219,7 +219,7 @@ Then visit `http://[server_addr]:7500` to see dashboard, default username and pa
 
 Client that want's to register must set a global `auth_token` equals to frps.ini.
 
-Note that time duration bewtween frpc and frps mustn't exceed 15 minutes because timestamp is used for authentication.
+Note that time duration between frpc and frps mustn't exceed 15 minutes because timestamp is used for authentication.
 
 Howerver, this timeout duration can be modified by setting `authentication_timeout` in frps's configure file. It's defalut value is 900, means 15 minutes. If it is equals 0, then frps will not check authentication timeout.
 
@@ -452,7 +452,6 @@ http_proxy = http://user:pwd@192.168.1.128:8080
 
 ## Development Plan
 
-* Url router.
 * Log http request information in frps.
 * Direct reverse proxy, like haproxy.
 * Load balance to different service in frpc.
@@ -497,3 +496,4 @@ Donate money by [paypal](https://www.paypal.me/fatedier) to my account **fatedie
 * [Damon Zhao](https://github.com/se77en)
 * [Manfred Touron](https://github.com/moul)
 * [xuebing1110](https://github.com/xuebing1110)
+* [Anbitioner](https://github.com/bingtianbaihua)

+ 1 - 1
README_zh.md

@@ -469,7 +469,6 @@ http_proxy = http://user:pwd@192.168.1.128:8080
 
 计划在后续版本中加入的功能与优化,排名不分先后,如果有其他功能建议欢迎在 [issues](https://github.com/fatedier/frp/issues) 中反馈。
 
-* 支持 url 路由转发。
 * frps 记录 http 请求日志。
 * frps 支持直接反向代理,类似 haproxy。
 * frpc 支持负载均衡到后端不同服务。
@@ -516,3 +515,4 @@ frp 交流群:606194980 (QQ 群号)
 * [Damon Zhao](https://github.com/se77en)
 * [Manfred Touron](https://github.com/moul)
 * [xuebing1110](https://github.com/xuebing1110)
+* [Anbitioner](https://github.com/bingtianbaihua)

+ 4 - 0
conf/frpc.ini

@@ -22,6 +22,10 @@ auth_token = 123
 # for privilege mode
 privilege_token = 12345678
 
+# heartbeat configure, it's not recommended to modify the default value
+# the default value of heartbeat_interval is 10 and heartbeat_timeout is 30
+# heartbeat_interval = 10
+# heartbeat_timeout = 30
 
 # ssh is the proxy name same as server's configuration
 [ssh]

+ 4 - 0
conf/frps.ini

@@ -30,6 +30,10 @@ log_max_days = 3
 privilege_mode = true
 privilege_token = 12345678
 
+# heartbeat configure, it's not recommended to modify the default value
+# the default value of heartbeat_timeout is 30
+# heartbeat_timeout = 30
+
 # only allow frpc to bind ports you list, if you set nothing, there won't be any limit
 privilege_allow_ports = 2000-3000,3001,3003,4000-50000
 

+ 15 - 3
src/cmd/frpc/control.go

@@ -55,15 +55,24 @@ func msgReader(cli *client.ProxyClient, c *conn.Conn, msgSendChan chan interface
 	var heartbeatTimeout bool = false
 	timer := time.AfterFunc(time.Duration(client.HeartBeatTimeout)*time.Second, func() {
 		heartbeatTimeout = true
-		c.Close()
+		if c != nil {
+			c.Close()
+		}
+		if cli != nil {
+			// if it's not udp type, nothing will happen
+			cli.CloseUdpTunnel()
+			cli.SetCloseFlag(true)
+		}
 		log.Error("ProxyName [%s], heartbeatRes from frps timeout", cli.Name)
 	})
 	defer timer.Stop()
 
 	for {
 		buf, err := c.ReadLine()
-		if err == io.EOF || c == nil || c.IsClosed() {
+		if err == io.EOF || c.IsClosed() {
+			timer.Stop()
 			c.Close()
+			cli.SetCloseFlag(true)
 			log.Warn("ProxyName [%s], frps close this control conn!", cli.Name)
 			var delayTime time.Duration = 1
 
@@ -76,11 +85,14 @@ func msgReader(cli *client.ProxyClient, c *conn.Conn, msgSendChan chan interface
 					msgSendChan = make(chan interface{}, 1024)
 					go heartbeatSender(c, msgSendChan)
 					go msgSender(cli, c, msgSendChan)
+					cli.SetCloseFlag(false)
 					break
 				}
 
-				if delayTime < 60 {
+				if delayTime < 30 {
 					delayTime = delayTime * 2
+				} else {
+					delayTime = 30
 				}
 				time.Sleep(delayTime * time.Second)
 			}

+ 3 - 1
src/cmd/frps/control.go

@@ -85,7 +85,9 @@ func controlWorker(c *conn.Conn) {
 			return
 		}
 	} else {
-		closeFlag = false
+		if ret == 0 {
+			closeFlag = false
+		}
 		return
 	}
 

+ 28 - 2
src/models/client/client.go

@@ -39,6 +39,9 @@ type ProxyClient struct {
 
 	udpTunnel *conn.Conn
 	once      sync.Once
+	closeFlag bool
+
+	mutex sync.RWMutex
 }
 
 // if proxy type is udp, keep a tcp connection for transferring udp packages
@@ -48,7 +51,7 @@ func (pc *ProxyClient) StartUdpTunnelOnce(addr string, port int64) {
 		var c *conn.Conn
 		udpProcessor := NewUdpProcesser(nil, pc.LocalIp, pc.LocalPort)
 		for {
-			if pc.udpTunnel == nil || pc.udpTunnel.IsClosed() {
+			if !pc.IsClosed() && (pc.udpTunnel == nil || pc.udpTunnel.IsClosed()) {
 				if HttpProxy == "" {
 					c, err = conn.ConnectServer(fmt.Sprintf("%s:%d", addr, port))
 				} else {
@@ -59,7 +62,7 @@ func (pc *ProxyClient) StartUdpTunnelOnce(addr string, port int64) {
 					time.Sleep(10 * time.Second)
 					continue
 				}
-				log.Info("ProxyName [%s], udp tunnel reconnect to server [%s:%d] success", pc.Name, addr, port)
+				log.Info("ProxyName [%s], udp tunnel connect to server [%s:%d] success", pc.Name, addr, port)
 
 				nowTime := time.Now().Unix()
 				req := &msg.ControlReq{
@@ -82,8 +85,11 @@ func (pc *ProxyClient) StartUdpTunnelOnce(addr string, port int64) {
 					time.Sleep(1 * time.Second)
 					continue
 				}
+				pc.mutex.Lock()
 				pc.udpTunnel = c
 				udpProcessor.UpdateTcpConn(pc.udpTunnel)
+				pc.mutex.Unlock()
+
 				udpProcessor.Run()
 			}
 			time.Sleep(1 * time.Second)
@@ -91,6 +97,14 @@ func (pc *ProxyClient) StartUdpTunnelOnce(addr string, port int64) {
 	})
 }
 
+func (pc *ProxyClient) CloseUdpTunnel() {
+	pc.mutex.RLock()
+	defer pc.mutex.RUnlock()
+	if pc.udpTunnel != nil {
+		pc.udpTunnel.Close()
+	}
+}
+
 func (pc *ProxyClient) GetLocalConn() (c *conn.Conn, err error) {
 	c, err = conn.ConnectServer(fmt.Sprintf("%s:%d", pc.LocalIp, pc.LocalPort))
 	if err != nil {
@@ -158,3 +172,15 @@ func (pc *ProxyClient) StartTunnel(serverAddr string, serverPort int64) (err err
 
 	return nil
 }
+
+func (pc *ProxyClient) SetCloseFlag(closeFlag bool) {
+	pc.mutex.Lock()
+	defer pc.mutex.Unlock()
+	pc.closeFlag = closeFlag
+}
+
+func (pc *ProxyClient) IsClosed() bool {
+	pc.mutex.RLock()
+	defer pc.mutex.RUnlock()
+	return pc.closeFlag
+}

+ 30 - 2
src/models/client/config.go

@@ -33,8 +33,8 @@ var (
 	LogLevel          string = "info"
 	LogMaxDays        int64  = 3
 	PrivilegeToken    string = ""
-	HeartBeatInterval int64  = 20
-	HeartBeatTimeout  int64  = 90
+	HeartBeatInterval int64  = 10
+	HeartBeatTimeout  int64  = 30
 )
 
 var ProxyClients map[string]*ProxyClient = make(map[string]*ProxyClient)
@@ -98,6 +98,34 @@ func LoadConf(confFile string) (err error) {
 		authToken = tmpStr
 	}
 
+	tmpStr, ok = conf.Get("common", "heartbeat_timeout")
+	if ok {
+		v, err := strconv.ParseInt(tmpStr, 10, 64)
+		if err != nil {
+			return fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect")
+		} else {
+			HeartBeatTimeout = v
+		}
+	}
+
+	tmpStr, ok = conf.Get("common", "heartbeat_interval")
+	if ok {
+		v, err := strconv.ParseInt(tmpStr, 10, 64)
+		if err != nil {
+			return fmt.Errorf("Parse conf error: heartbeat_interval is incorrect")
+		} else {
+			HeartBeatInterval = v
+		}
+	}
+
+	if HeartBeatInterval <= 0 {
+		return fmt.Errorf("Parse conf error: heartbeat_interval is incorrect")
+	}
+
+	if HeartBeatTimeout < HeartBeatInterval {
+		return fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect, heartbeat_timeout is less than heartbeat_interval")
+	}
+
 	// proxies
 	for name, section := range conf {
 		if name != "common" {

+ 13 - 2
src/models/server/config.go

@@ -51,7 +51,7 @@ var (
 	// if PrivilegeAllowPorts is not nil, tcp proxies which remote port exist in this map can be connected
 	PrivilegeAllowPorts map[int64]struct{}
 	MaxPoolCount        int64 = 100
-	HeartBeatTimeout    int64 = 90
+	HeartBeatTimeout    int64 = 30
 	UserConnTimeout     int64 = 10
 
 	VhostHttpMuxer    *vhost.HttpMuxer
@@ -237,6 +237,16 @@ func loadCommonConf(confFile string) error {
 	if ok {
 		SubDomainHost = strings.ToLower(strings.TrimSpace(SubDomainHost))
 	}
+
+	tmpStr, ok = conf.Get("common", "heartbeat_timeout")
+	if ok {
+		v, err := strconv.ParseInt(tmpStr, 10, 64)
+		if err != nil {
+			return fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect")
+		} else {
+			HeartBeatTimeout = v
+		}
+	}
 	return nil
 }
 
@@ -395,7 +405,9 @@ func CreateProxy(s *ProxyServer) error {
 		if oldServer.Status == consts.Working {
 			return fmt.Errorf("this proxy is already working now")
 		}
+		oldServer.Lock()
 		oldServer.Release()
+		oldServer.Unlock()
 		if oldServer.PrivilegeMode {
 			delete(ProxyServers, s.Name)
 		}
@@ -403,7 +415,6 @@ func CreateProxy(s *ProxyServer) error {
 	ProxyServers[s.Name] = s
 	metric.SetProxyInfo(s.Name, s.Type, s.BindAddr, s.UseEncryption, s.UseGzip,
 		s.PrivilegeMode, s.CustomDomains, s.Locations, s.ListenPort)
-	s.Init()
 	return nil
 }
 

+ 23 - 8
src/models/server/server.go

@@ -83,6 +83,8 @@ func NewProxyServerFromCtlMsg(req *msg.ControlReq) (p *ProxyServer) {
 	p.HostHeaderRewrite = req.HostHeaderRewrite
 	p.HttpUserName = req.HttpUserName
 	p.HttpPassWord = req.HttpPassWord
+
+	p.Init()
 	return
 }
 
@@ -276,10 +278,14 @@ func (p *ProxyServer) Start(c *conn.Conn) (err error) {
 }
 
 func (p *ProxyServer) Close() {
+	p.Lock()
+	defer p.Unlock()
+
+	oldStatus := p.Status
 	p.Release()
 
 	// if the proxy created by PrivilegeMode, delete it when closed
-	if p.PrivilegeMode {
+	if p.PrivilegeMode && oldStatus != consts.Closed {
 		// NOTE: this will take the global ProxyServerMap's lock
 		// if we only want to release resources, use Release() instead
 		DeleteProxy(p.Name)
@@ -287,9 +293,6 @@ func (p *ProxyServer) Close() {
 }
 
 func (p *ProxyServer) Release() {
-	p.Lock()
-	defer p.Unlock()
-
 	if p.Status != consts.Closed {
 		p.Status = consts.Closed
 		for _, l := range p.listeners {
@@ -297,10 +300,22 @@ func (p *ProxyServer) Release() {
 				l.Close()
 			}
 		}
-		close(p.ctlMsgChan)
-		close(p.workConnChan)
-		close(p.udpSenderChan)
-		close(p.closeChan)
+		if p.ctlMsgChan != nil {
+			close(p.ctlMsgChan)
+			p.ctlMsgChan = nil
+		}
+		if p.workConnChan != nil {
+			close(p.workConnChan)
+			p.workConnChan = nil
+		}
+		if p.udpSenderChan != nil {
+			close(p.udpSenderChan)
+			p.udpSenderChan = nil
+		}
+		if p.closeChan != nil {
+			close(p.closeChan)
+			p.closeChan = nil
+		}
 		if p.CtlConn != nil {
 			p.CtlConn.Close()
 		}

+ 1 - 1
src/utils/version/version.go

@@ -19,7 +19,7 @@ import (
 	"strings"
 )
 
-var version string = "0.9.1"
+var version string = "0.9.2"
 
 func Full() string {
 	return version