all: support for virtual host

This commit is contained in:
fatedier
2016-04-18 15:16:40 +08:00
parent 6874688e07
commit f650d3f330
11 changed files with 400 additions and 75 deletions

View File

@@ -31,6 +31,7 @@ type ProxyClient struct {
AuthToken string
LocalIp string
LocalPort int64
Type string
UseEncryption bool
}

View File

@@ -105,6 +105,16 @@ func LoadConf(confFile string) (err error) {
return fmt.Errorf("Parse ini file error: proxy [%s] local_port not found", proxyClient.Name)
}
// type
proxyClient.Type = "tcp"
typeStr, ok := section["type"]
if ok {
if typeStr != "tcp" && typeStr != "http" {
return fmt.Errorf("Parse ini file error: proxy [%s] type error", proxyClient.Name)
}
proxyClient.Type = typeStr
}
// use_encryption
proxyClient.UseEncryption = false
useEncryptionStr, ok := section["use_encryption"]

View File

@@ -17,19 +17,25 @@ package server
import (
"fmt"
"strconv"
"strings"
ini "github.com/vaughan0/go-ini"
"frp/utils/vhost"
)
// common config
var (
BindAddr string = "0.0.0.0"
BindPort int64 = 7000
VhostHttpPort int64 = 0 // if VhostHttpPort equals 0, do not listen a public port for http
LogFile string = "console"
LogWay string = "console" // console or file
LogLevel string = "info"
HeartBeatTimeout int64 = 90
UserConnTimeout int64 = 10
VhostMuxer *vhost.HttpMuxer
)
var ProxyServers map[string]*ProxyServer = make(map[string]*ProxyServer)
@@ -54,6 +60,13 @@ func LoadConf(confFile string) (err error) {
BindPort, _ = strconv.ParseInt(tmpStr, 10, 64)
}
tmpStr, ok = conf.Get("common", "vhost_http_port")
if ok {
VhostHttpPort, _ = strconv.ParseInt(tmpStr, 10, 64)
} else {
VhostHttpPort = 0
}
tmpStr, ok = conf.Get("common", "log_file")
if ok {
LogFile = tmpStr
@@ -73,26 +86,52 @@ func LoadConf(confFile string) (err error) {
for name, section := range conf {
if name != "common" {
proxyServer := &ProxyServer{}
proxyServer.CustomDomains = make([]string, 0)
proxyServer.Name = name
proxyServer.Type, ok = section["type"]
if ok {
if proxyServer.Type != "tcp" && proxyServer.Type != "http" {
return fmt.Errorf("Parse ini file error: proxy [%s] type error", proxyServer.Name)
}
} else {
proxyServer.Type = "tcp"
}
proxyServer.AuthToken, ok = section["auth_token"]
if !ok {
return fmt.Errorf("Parse ini file error: proxy [%s] no auth_token found", proxyServer.Name)
}
proxyServer.BindAddr, ok = section["bind_addr"]
if !ok {
proxyServer.BindAddr = "0.0.0.0"
}
portStr, ok := section["listen_port"]
if ok {
proxyServer.ListenPort, err = strconv.ParseInt(portStr, 10, 64)
if err != nil {
return fmt.Errorf("Parse ini file error: proxy [%s] listen_port error", proxyServer.Name)
// for tcp
if proxyServer.Type == "tcp" {
proxyServer.BindAddr, ok = section["bind_addr"]
if !ok {
proxyServer.BindAddr = "0.0.0.0"
}
portStr, ok := section["listen_port"]
if ok {
proxyServer.ListenPort, err = strconv.ParseInt(portStr, 10, 64)
if err != nil {
return fmt.Errorf("Parse ini file error: proxy [%s] listen_port error", proxyServer.Name)
}
} else {
return fmt.Errorf("Parse ini file error: proxy [%s] listen_port not found", proxyServer.Name)
}
} else if proxyServer.Type == "http" {
// for http
domainStr, ok := section["custom_domains"]
if ok {
var suffix string
if VhostHttpPort != 80 {
suffix = fmt.Sprintf(":%d", VhostHttpPort)
}
proxyServer.CustomDomains = strings.Split(domainStr, ",")
for i, domain := range proxyServer.CustomDomains {
proxyServer.CustomDomains[i] = strings.ToLower(strings.TrimSpace(domain)) + suffix
}
}
} else {
return fmt.Errorf("Parse ini file error: proxy [%s] listen_port not found", proxyServer.Name)
}
proxyServer.Init()

View File

@@ -24,15 +24,22 @@ import (
"frp/utils/log"
)
type Listener interface {
Accept() (*conn.Conn, error)
Close() error
}
type ProxyServer struct {
Name string
AuthToken string
UseEncryption bool
Type string
BindAddr string
ListenPort int64
Status int64
UseEncryption bool
CustomDomains []string
listener *conn.Listener // accept new connection from remote users
Status int64
listeners []Listener // accept new connection from remote users
ctlMsgChan chan int64 // every time accept a new user conn, put "1" to the channel
workConnChan chan *conn.Conn // get new work conns from control goroutine
userConnList *list.List // store user conns
@@ -44,6 +51,7 @@ func (p *ProxyServer) Init() {
p.workConnChan = make(chan *conn.Conn)
p.ctlMsgChan = make(chan int64)
p.userConnList = list.New()
p.listeners = make([]Listener, 0)
}
func (p *ProxyServer) Lock() {
@@ -57,57 +65,71 @@ func (p *ProxyServer) Unlock() {
// start listening for user conns
func (p *ProxyServer) Start() (err error) {
p.Init()
p.listener, err = conn.Listen(p.BindAddr, p.ListenPort)
if err != nil {
return err
if p.Type == "tcp" {
l, err := conn.Listen(p.BindAddr, p.ListenPort)
if err != nil {
return err
}
p.listeners = append(p.listeners, l)
} else if p.Type == "http" {
for _, domain := range p.CustomDomains {
l, err := VhostMuxer.Listen(domain)
if err != nil {
return err
}
p.listeners = append(p.listeners, l)
}
}
p.Status = consts.Working
// start a goroutine for listener to accept user connection
go func() {
for {
// block
// if listener is closed, err returned
c, err := p.listener.GetConn()
if err != nil {
log.Info("ProxyName [%s], listener is closed", p.Name)
return
}
log.Debug("ProxyName [%s], get one new user conn [%s]", p.Name, c.GetRemoteAddr())
// insert into list
p.Lock()
if p.Status != consts.Working {
log.Debug("ProxyName [%s] is not working, new user conn close", p.Name)
c.Close()
p.Unlock()
return
}
p.userConnList.PushBack(c)
p.Unlock()
// put msg to control conn
p.ctlMsgChan <- 1
// set timeout
time.AfterFunc(time.Duration(UserConnTimeout)*time.Second, func() {
p.Lock()
defer p.Unlock()
element := p.userConnList.Front()
if element == nil {
for _, listener := range p.listeners {
go func(l Listener) {
for {
// block
// if listener is closed, err returned
c, err := l.Accept()
if err != nil {
log.Info("ProxyName [%s], listener is closed", p.Name)
return
}
log.Debug("ProxyName [%s], get one new user conn [%s]", p.Name, c.GetRemoteAddr())
userConn := element.Value.(*conn.Conn)
if userConn == c {
log.Warn("ProxyName [%s], user conn [%s] timeout", p.Name, c.GetRemoteAddr())
// insert into list
p.Lock()
if p.Status != consts.Working {
log.Debug("ProxyName [%s] is not working, new user conn close", p.Name)
c.Close()
p.Unlock()
return
}
})
}
}()
p.userConnList.PushBack(c)
p.Unlock()
// start another goroutine for join two conns from client and user
// put msg to control conn
p.ctlMsgChan <- 1
// set timeout
time.AfterFunc(time.Duration(UserConnTimeout)*time.Second, func() {
p.Lock()
defer p.Unlock()
element := p.userConnList.Front()
if element == nil {
return
}
userConn := element.Value.(*conn.Conn)
if userConn == c {
log.Warn("ProxyName [%s], user conn [%s] timeout", p.Name, c.GetRemoteAddr())
userConn.Close()
}
})
}
}(listener)
}
// start another goroutine for join two conns from frpc and user
go func() {
for {
workConn, ok := <-p.workConnChan
@@ -149,8 +171,12 @@ func (p *ProxyServer) Close() {
p.Lock()
if p.Status != consts.Closed {
p.Status = consts.Closed
if p.listener != nil {
p.listener.Close()
if len(p.listeners) != 0 {
for _, l := range p.listeners {
if l != nil {
l.Close()
}
}
}
close(p.ctlMsgChan)
close(p.workConnChan)