websocket protocol
This commit is contained in:
parent
629f2856b1
commit
fd60e03efd
@ -41,6 +41,7 @@ var (
|
||||
bindPort int
|
||||
bindUdpPort int
|
||||
kcpBindPort int
|
||||
websocketBindPort int
|
||||
proxyBindAddr string
|
||||
vhostHttpPort int
|
||||
vhostHttpsPort int
|
||||
@ -70,6 +71,7 @@ func init() {
|
||||
rootCmd.PersistentFlags().IntVarP(&bindPort, "bind_port", "p", 7000, "bind 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(&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().IntVarP(&vhostHttpPort, "vhost_http_port", "", 0, "vhost http 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.BindUdpPort = bindUdpPort
|
||||
g.GlbServerCfg.KcpBindPort = kcpBindPort
|
||||
g.GlbServerCfg.WebsocketBindPort = websocketBindPort
|
||||
g.GlbServerCfg.ProxyBindAddr = proxyBindAddr
|
||||
g.GlbServerCfg.VhostHttpPort = vhostHttpPort
|
||||
g.GlbServerCfg.VhostHttpsPort = vhostHttpsPort
|
||||
|
@ -41,7 +41,7 @@ user = your_name
|
||||
login_fail_exit = true
|
||||
|
||||
# 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
|
||||
|
||||
# specify a dns server, so frpc will use this instead of default one
|
||||
|
@ -12,6 +12,10 @@ bind_udp_port = 7001
|
||||
# if not set, kcp is disabled in frps
|
||||
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
|
||||
# proxy_bind_addr = 127.0.0.1
|
||||
|
||||
|
@ -187,7 +187,7 @@ func UnmarshalClientConfFromIni(defaultCfg *ClientCommonConf, content string) (c
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "protocol"); ok {
|
||||
// Now it only support tcp and kcp.
|
||||
if tmpStr != "kcp" {
|
||||
if tmpStr != "kcp" && tmpStr != "websocket" {
|
||||
tmpStr = "tcp"
|
||||
}
|
||||
cfg.Protocol = tmpStr
|
||||
|
@ -825,6 +825,55 @@ func (cfg *XtcpProxyConf) CheckForSvr() (err error) {
|
||||
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) {
|
||||
localPorts, errRet := util.ParseRangeNumbers(section["local_port"])
|
||||
if errRet != nil {
|
||||
|
@ -45,6 +45,7 @@ type ServerCommonConf struct {
|
||||
BindPort int `json:"bind_port"`
|
||||
BindUdpPort int `json:"bind_udp_port"`
|
||||
KcpBindPort int `json:"kcp_bind_port"`
|
||||
WebsocketBindPort int `json:"websocket_bind_port"`
|
||||
ProxyBindAddr string `json:"proxy_bind_addr"`
|
||||
|
||||
// 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 {
|
||||
cfg.ProxyBindAddr = tmpStr
|
||||
} else {
|
||||
|
@ -29,4 +29,5 @@ var (
|
||||
HttpsProxy string = "https"
|
||||
StcpProxy string = "stcp"
|
||||
XtcpProxy string = "xtcp"
|
||||
WebsocketProxy string = "websocket"
|
||||
)
|
||||
|
@ -53,6 +53,9 @@ type Service struct {
|
||||
// Accept connections using kcp
|
||||
kcpListener frpNet.Listener
|
||||
|
||||
// Accept connections using websocket
|
||||
websocketListener frpNet.Listener
|
||||
|
||||
// For https proxies, route requests to different clients by hostname and other infomation
|
||||
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)
|
||||
}
|
||||
|
||||
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.
|
||||
if cfg.VhostHttpPort > 0 {
|
||||
rp := vhost.NewHttpReverseProxy()
|
||||
@ -214,6 +227,9 @@ func (svr *Service) Run() {
|
||||
if g.GlbServerCfg.KcpBindPort > 0 {
|
||||
go svr.HandleListener(svr.kcpListener)
|
||||
}
|
||||
if g.GlbServerCfg.WebsocketBindPort > 0 {
|
||||
go svr.HandleListener(svr.websocketListener)
|
||||
}
|
||||
svr.HandleListener(svr.listener)
|
||||
|
||||
}
|
||||
@ -226,7 +242,6 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
|
||||
log.Warn("Listener for incoming connections from client closed")
|
||||
return
|
||||
}
|
||||
|
||||
// Start a new goroutine for dealing connections.
|
||||
go func(frpConn frpNet.Conn) {
|
||||
dealFn := func(conn frpNet.Conn) {
|
||||
|
@ -132,6 +132,8 @@ func ConnectServerByProxy(proxyUrl string, protocol string, addr string) (c Conn
|
||||
case "kcp":
|
||||
// http proxy is not supported for kcp
|
||||
return ConnectServer(protocol, addr)
|
||||
case "websocket":
|
||||
return ConnectWebsocketServer(addr)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupport protocol: %s", protocol)
|
||||
}
|
||||
|
113
utils/net/websocket.go
Normal file
113
utils/net/websocket.go
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user