websocket protocol
This commit is contained in:
parent
629f2856b1
commit
fd60e03efd
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -41,11 +41,12 @@ func InitServerCfg(cfg *ServerCommonConf) {
|
|||||||
|
|
||||||
// common config
|
// common config
|
||||||
type ServerCommonConf struct {
|
type ServerCommonConf struct {
|
||||||
BindAddr string `json:"bind_addr"`
|
BindAddr string `json:"bind_addr"`
|
||||||
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"`
|
||||||
ProxyBindAddr string `json:"proxy_bind_addr"`
|
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.
|
// If VhostHttpPort equals 0, don't listen a public port for http protocol.
|
||||||
VhostHttpPort int `json:"vhost_http_port"`
|
VhostHttpPort int `json:"vhost_http_port"`
|
||||||
@ -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 {
|
||||||
|
@ -23,10 +23,11 @@ var (
|
|||||||
Offline string = "offline"
|
Offline string = "offline"
|
||||||
|
|
||||||
// proxy type
|
// proxy type
|
||||||
TcpProxy string = "tcp"
|
TcpProxy string = "tcp"
|
||||||
UdpProxy string = "udp"
|
UdpProxy string = "udp"
|
||||||
HttpProxy string = "http"
|
HttpProxy string = "http"
|
||||||
HttpsProxy string = "https"
|
HttpsProxy string = "https"
|
||||||
StcpProxy string = "stcp"
|
StcpProxy string = "stcp"
|
||||||
XtcpProxy string = "xtcp"
|
XtcpProxy string = "xtcp"
|
||||||
|
WebsocketProxy string = "websocket"
|
||||||
)
|
)
|
||||||
|
@ -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) {
|
||||||
|
@ -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
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