websocket protocol

This commit is contained in:
FishFish 2018-08-03 16:38:45 +08:00
parent 629f2856b1
commit fd60e03efd
10 changed files with 211 additions and 14 deletions

View File

@ -41,6 +41,7 @@ var (
bindPort int bindPort int
bindUdpPort int bindUdpPort int
kcpBindPort int kcpBindPort int
websocketBindPort int
proxyBindAddr string proxyBindAddr string
vhostHttpPort int vhostHttpPort int
vhostHttpsPort int vhostHttpsPort int
@ -70,6 +71,7 @@ func init() {
rootCmd.PersistentFlags().IntVarP(&bindPort, "bind_port", "p", 7000, "bind port") rootCmd.PersistentFlags().IntVarP(&bindPort, "bind_port", "p", 7000, "bind port")
rootCmd.PersistentFlags().IntVarP(&bindUdpPort, "bind_udp_port", "", 0, "bind udp port") rootCmd.PersistentFlags().IntVarP(&bindUdpPort, "bind_udp_port", "", 0, "bind udp port")
rootCmd.PersistentFlags().IntVarP(&kcpBindPort, "kcp_bind_port", "", 0, "kcp bind udp port") rootCmd.PersistentFlags().IntVarP(&kcpBindPort, "kcp_bind_port", "", 0, "kcp bind udp port")
rootCmd.PersistentFlags().IntVarP(&websocketBindPort, "websocket_bind_port", "", 0, "websocket bind tcp port")
rootCmd.PersistentFlags().StringVarP(&proxyBindAddr, "proxy_bind_addr", "", "0.0.0.0", "proxy bind address") rootCmd.PersistentFlags().StringVarP(&proxyBindAddr, "proxy_bind_addr", "", "0.0.0.0", "proxy bind address")
rootCmd.PersistentFlags().IntVarP(&vhostHttpPort, "vhost_http_port", "", 0, "vhost http port") rootCmd.PersistentFlags().IntVarP(&vhostHttpPort, "vhost_http_port", "", 0, "vhost http port")
rootCmd.PersistentFlags().IntVarP(&vhostHttpsPort, "vhost_https_port", "", 0, "vhost https port") rootCmd.PersistentFlags().IntVarP(&vhostHttpsPort, "vhost_https_port", "", 0, "vhost https port")
@ -162,6 +164,7 @@ func parseServerCommonCfgFromCmd() (err error) {
g.GlbServerCfg.BindPort = bindPort g.GlbServerCfg.BindPort = bindPort
g.GlbServerCfg.BindUdpPort = bindUdpPort g.GlbServerCfg.BindUdpPort = bindUdpPort
g.GlbServerCfg.KcpBindPort = kcpBindPort g.GlbServerCfg.KcpBindPort = kcpBindPort
g.GlbServerCfg.WebsocketBindPort = websocketBindPort
g.GlbServerCfg.ProxyBindAddr = proxyBindAddr g.GlbServerCfg.ProxyBindAddr = proxyBindAddr
g.GlbServerCfg.VhostHttpPort = vhostHttpPort g.GlbServerCfg.VhostHttpPort = vhostHttpPort
g.GlbServerCfg.VhostHttpsPort = vhostHttpsPort g.GlbServerCfg.VhostHttpsPort = vhostHttpsPort

View File

@ -41,7 +41,7 @@ user = your_name
login_fail_exit = true login_fail_exit = true
# communication protocol used to connect to server # communication protocol used to connect to server
# now it supports tcp and kcp, default is tcp # now it supports tcp and kcp and websocket, default is tcp
protocol = tcp protocol = tcp
# specify a dns server, so frpc will use this instead of default one # specify a dns server, so frpc will use this instead of default one

View File

@ -12,6 +12,10 @@ bind_udp_port = 7001
# if not set, kcp is disabled in frps # if not set, kcp is disabled in frps
kcp_bind_port = 7000 kcp_bind_port = 7000
# tcp port used for websocket protocol, it can't be same with 'bind_port'
# if not set, websocket is disabled in frps
websocket_bind_port = 7002
# specify which address proxy will listen for, default value is same with bind_addr # specify which address proxy will listen for, default value is same with bind_addr
# proxy_bind_addr = 127.0.0.1 # proxy_bind_addr = 127.0.0.1

View File

@ -187,7 +187,7 @@ func UnmarshalClientConfFromIni(defaultCfg *ClientCommonConf, content string) (c
if tmpStr, ok = conf.Get("common", "protocol"); ok { if tmpStr, ok = conf.Get("common", "protocol"); ok {
// Now it only support tcp and kcp. // Now it only support tcp and kcp.
if tmpStr != "kcp" { if tmpStr != "kcp" && tmpStr != "websocket" {
tmpStr = "tcp" tmpStr = "tcp"
} }
cfg.Protocol = tmpStr cfg.Protocol = tmpStr

View File

@ -825,6 +825,55 @@ func (cfg *XtcpProxyConf) CheckForSvr() (err error) {
return return
} }
// WEBSOCKET
type WebsocketProxyConf struct {
BaseProxyConf
BindInfoConf
LocalSvrConf
}
func (cfg *WebsocketProxyConf) Compare(cmp ProxyConf) bool {
cmpConf, ok := cmp.(*WebsocketProxyConf)
if !ok {
return false
}
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
!cfg.BindInfoConf.compare(&cmpConf.BindInfoConf) ||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) {
return false
}
return true
}
func (cfg *WebsocketProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
cfg.BindInfoConf.UnmarshalFromMsg(pMsg)
}
func (cfg *WebsocketProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
return
}
if err = cfg.BindInfoConf.UnmarshalFromIni(prefix, name, section); err != nil {
return
}
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
return
}
return
}
func (cfg *WebsocketProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
cfg.BaseProxyConf.MarshalToMsg(pMsg)
cfg.BindInfoConf.MarshalToMsg(pMsg)
}
func (cfg *WebsocketProxyConf) CheckForCli() error { return nil }
func (cfg *WebsocketProxyConf) CheckForSvr() error { return nil }
func ParseRangeSection(name string, section ini.Section) (sections map[string]ini.Section, err error) { func ParseRangeSection(name string, section ini.Section) (sections map[string]ini.Section, err error) {
localPorts, errRet := util.ParseRangeNumbers(section["local_port"]) localPorts, errRet := util.ParseRangeNumbers(section["local_port"])
if errRet != nil { if errRet != nil {

View File

@ -45,6 +45,7 @@ type ServerCommonConf struct {
BindPort int `json:"bind_port"` BindPort int `json:"bind_port"`
BindUdpPort int `json:"bind_udp_port"` BindUdpPort int `json:"bind_udp_port"`
KcpBindPort int `json:"kcp_bind_port"` KcpBindPort int `json:"kcp_bind_port"`
WebsocketBindPort int `json:"websocket_bind_port"`
ProxyBindAddr string `json:"proxy_bind_addr"` ProxyBindAddr string `json:"proxy_bind_addr"`
// If VhostHttpPort equals 0, don't listen a public port for http protocol. // If VhostHttpPort equals 0, don't listen a public port for http protocol.
@ -153,6 +154,15 @@ func UnmarshalServerConfFromIni(defaultCfg *ServerCommonConf, content string) (c
} }
} }
if tmpStr, ok = conf.Get("common", "websocket_bind_port"); ok {
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
err = fmt.Errorf("Parse conf error: invalid kcp_bind_port")
return
} else {
cfg.WebsocketBindPort = int(v)
}
}
if tmpStr, ok = conf.Get("common", "proxy_bind_addr"); ok { if tmpStr, ok = conf.Get("common", "proxy_bind_addr"); ok {
cfg.ProxyBindAddr = tmpStr cfg.ProxyBindAddr = tmpStr
} else { } else {

View File

@ -29,4 +29,5 @@ var (
HttpsProxy string = "https" HttpsProxy string = "https"
StcpProxy string = "stcp" StcpProxy string = "stcp"
XtcpProxy string = "xtcp" XtcpProxy string = "xtcp"
WebsocketProxy string = "websocket"
) )

View File

@ -53,6 +53,9 @@ type Service struct {
// Accept connections using kcp // Accept connections using kcp
kcpListener frpNet.Listener kcpListener frpNet.Listener
// Accept connections using websocket
websocketListener frpNet.Listener
// For https proxies, route requests to different clients by hostname and other infomation // For https proxies, route requests to different clients by hostname and other infomation
VhostHttpsMuxer *vhost.HttpsMuxer VhostHttpsMuxer *vhost.HttpsMuxer
@ -137,6 +140,16 @@ func NewService() (svr *Service, err error) {
log.Info("frps kcp listen on udp %s:%d", cfg.BindAddr, cfg.KcpBindPort) log.Info("frps kcp listen on udp %s:%d", cfg.BindAddr, cfg.KcpBindPort)
} }
if cfg.WebsocketBindPort > 0 {
svr.websocketListener, err = frpNet.ListenWebsocket(cfg.BindAddr, cfg.WebsocketBindPort)
if err != nil {
err = fmt.Errorf("Listen on websocket address tcp [%s:%d] error: %v",
cfg.BindAddr, cfg.WebsocketBindPort, err)
return
}
log.Info("frps websocket listen on tcp %s:%d", cfg.BindAddr, cfg.WebsocketBindPort)
}
// Create http vhost muxer. // Create http vhost muxer.
if cfg.VhostHttpPort > 0 { if cfg.VhostHttpPort > 0 {
rp := vhost.NewHttpReverseProxy() rp := vhost.NewHttpReverseProxy()
@ -214,6 +227,9 @@ func (svr *Service) Run() {
if g.GlbServerCfg.KcpBindPort > 0 { if g.GlbServerCfg.KcpBindPort > 0 {
go svr.HandleListener(svr.kcpListener) go svr.HandleListener(svr.kcpListener)
} }
if g.GlbServerCfg.WebsocketBindPort > 0 {
go svr.HandleListener(svr.websocketListener)
}
svr.HandleListener(svr.listener) svr.HandleListener(svr.listener)
} }
@ -226,7 +242,6 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
log.Warn("Listener for incoming connections from client closed") log.Warn("Listener for incoming connections from client closed")
return return
} }
// Start a new goroutine for dealing connections. // Start a new goroutine for dealing connections.
go func(frpConn frpNet.Conn) { go func(frpConn frpNet.Conn) {
dealFn := func(conn frpNet.Conn) { dealFn := func(conn frpNet.Conn) {

View File

@ -132,6 +132,8 @@ func ConnectServerByProxy(proxyUrl string, protocol string, addr string) (c Conn
case "kcp": case "kcp":
// http proxy is not supported for kcp // http proxy is not supported for kcp
return ConnectServer(protocol, addr) return ConnectServer(protocol, addr)
case "websocket":
return ConnectWebsocketServer(addr)
default: default:
return nil, fmt.Errorf("unsupport protocol: %s", protocol) return nil, fmt.Errorf("unsupport protocol: %s", protocol)
} }

113
utils/net/websocket.go Normal file
View File

@ -0,0 +1,113 @@
package net
import (
"fmt"
"net"
"net/http"
"net/url"
"sync/atomic"
"time"
"github.com/fatedier/frp/utils/log"
"golang.org/x/net/websocket"
)
type WebsocketListener struct {
log.Logger
server *http.Server
httpMutex *http.ServeMux
connChan chan *WebsocketConn
closeFlag bool
}
func ListenWebsocket(bindAddr string, bindPort int) (l *WebsocketListener, err error) {
l = &WebsocketListener{
httpMutex: http.NewServeMux(),
connChan: make(chan *WebsocketConn),
Logger: log.NewPrefixLogger(""),
}
l.httpMutex.Handle("/", websocket.Handler(func(c *websocket.Conn) {
conn := NewWebScoketConn(c)
l.connChan <- conn
conn.waitClose()
}))
l.server = &http.Server{
Addr: fmt.Sprintf("%s:%d", bindAddr, bindPort),
Handler: l.httpMutex,
}
ch := make(chan struct{})
go func() {
close(ch)
err = l.server.ListenAndServe()
}()
<-ch
<-time.After(time.Millisecond)
return
}
func (p *WebsocketListener) Accept() (Conn, error) {
c := <-p.connChan
return c, nil
}
func (p *WebsocketListener) Close() error {
if !p.closeFlag {
p.closeFlag = true
p.server.Close()
}
return nil
}
type WebsocketConn struct {
net.Conn
log.Logger
closed int32
wait chan struct{}
}
func NewWebScoketConn(conn net.Conn) (c *WebsocketConn) {
c = &WebsocketConn{
Conn: conn,
Logger: log.NewPrefixLogger(""),
wait: make(chan struct{}),
}
return
}
func (p *WebsocketConn) Close() error {
if atomic.LoadInt32(&p.closed) == 1 {
return nil
}
close(p.wait)
return p.Conn.Close()
}
func (p *WebsocketConn) waitClose() {
<-p.wait
}
// ConnectWebsocketServer :
// addr: ws://domain:port
func ConnectWebsocketServer(addr string) (c Conn, err error) {
addr = "ws://" + addr
uri, err := url.Parse(addr)
if err != nil {
return
}
origin := "http://" + uri.Host
cfg, err := websocket.NewConfig(addr, origin)
if err != nil {
return
}
cfg.Dialer = &net.Dialer{
Timeout: time.Second * 10,
}
conn, err := websocket.DialConfig(cfg)
if err != nil {
return
}
c = NewWebScoketConn(conn)
return
}