mirror of
https://gitee.com/IrisVega/frp.git
synced 2024-11-01 22:31:29 +08:00
tcp multiplexing over http connect tunnel
This commit is contained in:
parent
0b9124d4fd
commit
1db091b381
49
README.md
49
README.md
@ -752,7 +752,7 @@ proxy_protocol_version = v2
|
|||||||
|
|
||||||
You can enable Proxy Protocol support in nginx to expose user's real IP in HTTP header `X-Real-IP`, and then read `X-Real-IP` header in your web service for the real IP.
|
You can enable Proxy Protocol support in nginx to expose user's real IP in HTTP header `X-Real-IP`, and then read `X-Real-IP` header in your web service for the real IP.
|
||||||
|
|
||||||
### Require HTTP Basic auth (password) for web services
|
### Require HTTP Basic Auth (Password) for Web Services
|
||||||
|
|
||||||
Anyone who can guess your tunnel URL can access your local web server unless you protect it with a password.
|
Anyone who can guess your tunnel URL can access your local web server unless you protect it with a password.
|
||||||
|
|
||||||
@ -772,7 +772,7 @@ http_pwd = abc
|
|||||||
|
|
||||||
Visit `http://test.example.com` in the browser and now you are prompted to enter the username and password.
|
Visit `http://test.example.com` in the browser and now you are prompted to enter the username and password.
|
||||||
|
|
||||||
### Custom subdomain names
|
### Custom Subdomain Names
|
||||||
|
|
||||||
It is convenient to use `subdomain` configure for http and https types when many people share one frps server.
|
It is convenient to use `subdomain` configure for http and https types when many people share one frps server.
|
||||||
|
|
||||||
@ -795,7 +795,7 @@ Now you can visit your web service on `test.frps.com`.
|
|||||||
|
|
||||||
Note that if `subdomain_host` is not empty, `custom_domains` should not be the subdomain of `subdomain_host`.
|
Note that if `subdomain_host` is not empty, `custom_domains` should not be the subdomain of `subdomain_host`.
|
||||||
|
|
||||||
### URL routing
|
### URL Routing
|
||||||
|
|
||||||
frp supports forwarding HTTP requests to different backend web services by url routing.
|
frp supports forwarding HTTP requests to different backend web services by url routing.
|
||||||
|
|
||||||
@ -818,6 +818,49 @@ locations = /news,/about
|
|||||||
|
|
||||||
HTTP requests with URL prefix `/news` or `/about` will be forwarded to **web02** and other requests to **web01**.
|
HTTP requests with URL prefix `/news` or `/about` will be forwarded to **web02** and other requests to **web01**.
|
||||||
|
|
||||||
|
### TCP Multiplexing
|
||||||
|
|
||||||
|
frp supports receiving TCP sockets directed to different proxies on a single port on frps, similar to `vhost_http_port` and `vhost_https_port`.
|
||||||
|
|
||||||
|
The only supported TCP multiplexing method available at the moment is `httpconnect` - HTTP CONNECT tunnel.
|
||||||
|
|
||||||
|
When setting `tcpmux_httpconnect_port` to anything other than 0 in frps under `[common]`, frps will listen on this port for HTTP CONNECT requests.
|
||||||
|
|
||||||
|
The host of the HTTP CONNECT request will be used to match the proxy in frps. Proxy hosts can be configured in frpc by configuring `custom_domain` and / or `subdomain` under `type = tcpmux` proxies, when `multiplexer = httpconnect`.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# frps.ini
|
||||||
|
[common]
|
||||||
|
bind_port = 7000
|
||||||
|
tcpmux_httpconnect_port = 1337
|
||||||
|
```
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# frpc.ini
|
||||||
|
[common]
|
||||||
|
server_addr = x.x.x.x
|
||||||
|
server_port = 7000
|
||||||
|
|
||||||
|
[proxy1]
|
||||||
|
type = tcpmux
|
||||||
|
multiplexer = httpconnect
|
||||||
|
custom_domains = test1
|
||||||
|
|
||||||
|
[proxy2]
|
||||||
|
type = tcpmux
|
||||||
|
multiplexer = httpconnect
|
||||||
|
custom_domains = test2
|
||||||
|
```
|
||||||
|
|
||||||
|
In the above configuration - frps can be contacted on port 1337 with a HTTP CONNECT header such as:
|
||||||
|
|
||||||
|
```
|
||||||
|
CONNECT test1 HTTP/1.1\r\n\r\n
|
||||||
|
```
|
||||||
|
and the connection will be routed to `proxy1`.
|
||||||
|
|
||||||
### Connecting to frps via HTTP PROXY
|
### Connecting to frps via HTTP PROXY
|
||||||
|
|
||||||
frpc can connect to frps using HTTP proxy if you set OS environment variable `HTTP_PROXY`, or if `http_proxy` is set in frpc.ini file.
|
frpc can connect to frps using HTTP proxy if you set OS environment variable `HTTP_PROXY`, or if `http_proxy` is set in frpc.ini file.
|
||||||
|
@ -72,6 +72,11 @@ func NewProxy(ctx context.Context, pxyConf config.ProxyConf, clientCfg config.Cl
|
|||||||
BaseProxy: &baseProxy,
|
BaseProxy: &baseProxy,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
|
case *config.TcpMuxProxyConf:
|
||||||
|
pxy = &TcpMuxProxy{
|
||||||
|
BaseProxy: &baseProxy,
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
case *config.UdpProxyConf:
|
case *config.UdpProxyConf:
|
||||||
pxy = &UdpProxy{
|
pxy = &UdpProxy{
|
||||||
BaseProxy: &baseProxy,
|
BaseProxy: &baseProxy,
|
||||||
@ -141,6 +146,35 @@ func (pxy *TcpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
|
|||||||
conn, []byte(pxy.clientCfg.Token), m)
|
conn, []byte(pxy.clientCfg.Token), m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TCP Multiplexer
|
||||||
|
type TcpMuxProxy struct {
|
||||||
|
*BaseProxy
|
||||||
|
|
||||||
|
cfg *config.TcpMuxProxyConf
|
||||||
|
proxyPlugin plugin.Plugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pxy *TcpMuxProxy) Run() (err error) {
|
||||||
|
if pxy.cfg.Plugin != "" {
|
||||||
|
pxy.proxyPlugin, err = plugin.Create(pxy.cfg.Plugin, pxy.cfg.PluginParams)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pxy *TcpMuxProxy) Close() {
|
||||||
|
if pxy.proxyPlugin != nil {
|
||||||
|
pxy.proxyPlugin.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pxy *TcpMuxProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
|
||||||
|
HandleTcpWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, pxy.limiter,
|
||||||
|
conn, []byte(pxy.clientCfg.Token), m)
|
||||||
|
}
|
||||||
|
|
||||||
// HTTP
|
// HTTP
|
||||||
type HttpProxy struct {
|
type HttpProxy struct {
|
||||||
*BaseProxy
|
*BaseProxy
|
||||||
|
@ -66,6 +66,7 @@ var (
|
|||||||
hostHeaderRewrite string
|
hostHeaderRewrite string
|
||||||
role string
|
role string
|
||||||
sk string
|
sk string
|
||||||
|
multiplexer string
|
||||||
serverName string
|
serverName string
|
||||||
bindAddr string
|
bindAddr string
|
||||||
bindPort int
|
bindPort int
|
||||||
|
91
cmd/frpc/sub/tcpmux.go
Normal file
91
cmd/frpc/sub/tcpmux.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
// Copyright 2020 guylewin, guy@lewin.co.il
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package sub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/models/config"
|
||||||
|
"github.com/fatedier/frp/models/consts"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
tcpMuxCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
|
||||||
|
tcpMuxCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
|
||||||
|
tcpMuxCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp or websocket")
|
||||||
|
tcpMuxCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
|
||||||
|
tcpMuxCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
|
||||||
|
tcpMuxCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
|
||||||
|
tcpMuxCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
|
||||||
|
tcpMuxCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console")
|
||||||
|
|
||||||
|
tcpMuxCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
|
||||||
|
tcpMuxCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
|
||||||
|
tcpMuxCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
|
||||||
|
tcpMuxCmd.PersistentFlags().StringVarP(&customDomains, "custom_domain", "d", "", "custom domain")
|
||||||
|
tcpMuxCmd.PersistentFlags().StringVarP(&subDomain, "sd", "", "", "sub domain")
|
||||||
|
tcpMuxCmd.PersistentFlags().StringVarP(&multiplexer, "mux", "", "", "multiplexer")
|
||||||
|
tcpMuxCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||||
|
tcpMuxCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||||
|
|
||||||
|
rootCmd.AddCommand(tcpMuxCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tcpMuxCmd = &cobra.Command{
|
||||||
|
Use: "tcpmux",
|
||||||
|
Short: "Run frpc with a single tcpmux proxy",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, "")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &config.TcpMuxProxyConf{}
|
||||||
|
var prefix string
|
||||||
|
if user != "" {
|
||||||
|
prefix = user + "."
|
||||||
|
}
|
||||||
|
cfg.ProxyName = prefix + proxyName
|
||||||
|
cfg.ProxyType = consts.TcpMuxProxy
|
||||||
|
cfg.LocalIp = localIp
|
||||||
|
cfg.LocalPort = localPort
|
||||||
|
cfg.CustomDomains = strings.Split(customDomains, ",")
|
||||||
|
cfg.SubDomain = subDomain
|
||||||
|
cfg.Multiplexer = multiplexer
|
||||||
|
cfg.UseEncryption = useEncryption
|
||||||
|
cfg.UseCompression = useCompression
|
||||||
|
|
||||||
|
err = cfg.CheckForCli()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyConfs := map[string]config.ProxyConf{
|
||||||
|
cfg.ProxyName: cfg,
|
||||||
|
}
|
||||||
|
err = startService(clientCfg, proxyConfs, nil, "")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
@ -34,6 +34,7 @@ var (
|
|||||||
func init() {
|
func init() {
|
||||||
proxyConfTypeMap = make(map[string]reflect.Type)
|
proxyConfTypeMap = make(map[string]reflect.Type)
|
||||||
proxyConfTypeMap[consts.TcpProxy] = reflect.TypeOf(TcpProxyConf{})
|
proxyConfTypeMap[consts.TcpProxy] = reflect.TypeOf(TcpProxyConf{})
|
||||||
|
proxyConfTypeMap[consts.TcpMuxProxy] = reflect.TypeOf(TcpMuxProxyConf{})
|
||||||
proxyConfTypeMap[consts.UdpProxy] = reflect.TypeOf(UdpProxyConf{})
|
proxyConfTypeMap[consts.UdpProxy] = reflect.TypeOf(UdpProxyConf{})
|
||||||
proxyConfTypeMap[consts.HttpProxy] = reflect.TypeOf(HttpProxyConf{})
|
proxyConfTypeMap[consts.HttpProxy] = reflect.TypeOf(HttpProxyConf{})
|
||||||
proxyConfTypeMap[consts.HttpsProxy] = reflect.TypeOf(HttpsProxyConf{})
|
proxyConfTypeMap[consts.HttpsProxy] = reflect.TypeOf(HttpsProxyConf{})
|
||||||
@ -574,6 +575,84 @@ func (cfg *TcpProxyConf) CheckForCli() (err error) {
|
|||||||
|
|
||||||
func (cfg *TcpProxyConf) CheckForSvr(serverCfg ServerCommonConf) error { return nil }
|
func (cfg *TcpProxyConf) CheckForSvr(serverCfg ServerCommonConf) error { return nil }
|
||||||
|
|
||||||
|
// TCP Multiplexer
|
||||||
|
type TcpMuxProxyConf struct {
|
||||||
|
BaseProxyConf
|
||||||
|
DomainConf
|
||||||
|
|
||||||
|
Multiplexer string `json:"multiplexer"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *TcpMuxProxyConf) Compare(cmp ProxyConf) bool {
|
||||||
|
cmpConf, ok := cmp.(*TcpMuxProxyConf)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||||
|
!cfg.DomainConf.compare(&cmpConf.DomainConf) ||
|
||||||
|
cfg.Multiplexer != cmpConf.Multiplexer {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *TcpMuxProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||||
|
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||||
|
cfg.DomainConf.UnmarshalFromMsg(pMsg)
|
||||||
|
cfg.Multiplexer = pMsg.Multiplexer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *TcpMuxProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||||
|
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = cfg.DomainConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.Multiplexer = section["multiplexer"]
|
||||||
|
if cfg.Multiplexer != consts.HttpConnectTcpMultiplexer {
|
||||||
|
return fmt.Errorf("parse conf error: proxy [%s] incorrect multiplexer [%s]", name, cfg.Multiplexer)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *TcpMuxProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||||
|
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||||
|
cfg.DomainConf.MarshalToMsg(pMsg)
|
||||||
|
pMsg.Multiplexer = cfg.Multiplexer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *TcpMuxProxyConf) CheckForCli() (err error) {
|
||||||
|
if err = cfg.BaseProxyConf.checkForCli(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = cfg.DomainConf.checkForCli(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if cfg.Multiplexer != consts.HttpConnectTcpMultiplexer {
|
||||||
|
return fmt.Errorf("parse conf error: incorrect multiplexer [%s]", cfg.Multiplexer)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *TcpMuxProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) {
|
||||||
|
if cfg.Multiplexer != consts.HttpConnectTcpMultiplexer {
|
||||||
|
return fmt.Errorf("proxy [%s] incorrect multiplexer [%s]", cfg.ProxyName, cfg.Multiplexer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Multiplexer == consts.HttpConnectTcpMultiplexer && serverCfg.TcpMuxHttpConnectPort == 0 {
|
||||||
|
return fmt.Errorf("proxy [%s] type [tcpmux] with multiplexer [httpconnect] requires tcpmux_httpconnect_port configuration", cfg.ProxyName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = cfg.DomainConf.checkForSvr(serverCfg); err != nil {
|
||||||
|
err = fmt.Errorf("proxy [%s] domain conf check error: %v", cfg.ProxyName, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// UDP
|
// UDP
|
||||||
type UdpProxyConf struct {
|
type UdpProxyConf struct {
|
||||||
BaseProxyConf
|
BaseProxyConf
|
||||||
|
@ -59,6 +59,12 @@ type ServerCommonConf struct {
|
|||||||
// requests. By default, this value is 0.
|
// requests. By default, this value is 0.
|
||||||
VhostHttpsPort int `json:"vhost_https_port"`
|
VhostHttpsPort int `json:"vhost_https_port"`
|
||||||
|
|
||||||
|
// TcpMuxHttpConnectPort specifies the port that the server listens for TCP
|
||||||
|
// HTTP CONNECT requests. If the value is 0, the server will not multiplex TCP
|
||||||
|
// requests on one single port. If it's not - it will listen on this value for
|
||||||
|
// HTTP CONNECT requests. By default, this value is 0.
|
||||||
|
TcpMuxHttpConnectPort int `json:"tcpmux_httpconnect_port"`
|
||||||
|
|
||||||
// VhostHttpTimeout specifies the response header timeout for the Vhost
|
// VhostHttpTimeout specifies the response header timeout for the Vhost
|
||||||
// HTTP server, in seconds. By default, this value is 60.
|
// HTTP server, in seconds. By default, this value is 60.
|
||||||
VhostHttpTimeout int64 `json:"vhost_http_timeout"`
|
VhostHttpTimeout int64 `json:"vhost_http_timeout"`
|
||||||
@ -155,6 +161,7 @@ func GetDefaultServerConf() ServerCommonConf {
|
|||||||
ProxyBindAddr: "0.0.0.0",
|
ProxyBindAddr: "0.0.0.0",
|
||||||
VhostHttpPort: 0,
|
VhostHttpPort: 0,
|
||||||
VhostHttpsPort: 0,
|
VhostHttpsPort: 0,
|
||||||
|
TcpMuxHttpConnectPort: 0,
|
||||||
VhostHttpTimeout: 60,
|
VhostHttpTimeout: 60,
|
||||||
DashboardAddr: "0.0.0.0",
|
DashboardAddr: "0.0.0.0",
|
||||||
DashboardPort: 0,
|
DashboardPort: 0,
|
||||||
@ -259,6 +266,17 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error
|
|||||||
cfg.VhostHttpsPort = 0
|
cfg.VhostHttpsPort = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tmpStr, ok = conf.Get("common", "tcpmux_httpconnect_port"); ok {
|
||||||
|
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||||
|
err = fmt.Errorf("Parse conf error: invalid tcpmux_httpconnect_port")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
cfg.TcpMuxHttpConnectPort = int(v)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cfg.TcpMuxHttpConnectPort = 0
|
||||||
|
}
|
||||||
|
|
||||||
if tmpStr, ok = conf.Get("common", "vhost_http_timeout"); ok {
|
if tmpStr, ok = conf.Get("common", "vhost_http_timeout"); ok {
|
||||||
v, errRet := strconv.ParseInt(tmpStr, 10, 64)
|
v, errRet := strconv.ParseInt(tmpStr, 10, 64)
|
||||||
if errRet != nil || v < 0 {
|
if errRet != nil || v < 0 {
|
||||||
|
@ -25,6 +25,7 @@ var (
|
|||||||
// proxy type
|
// proxy type
|
||||||
TcpProxy string = "tcp"
|
TcpProxy string = "tcp"
|
||||||
UdpProxy string = "udp"
|
UdpProxy string = "udp"
|
||||||
|
TcpMuxProxy string = "tcpmux"
|
||||||
HttpProxy string = "http"
|
HttpProxy string = "http"
|
||||||
HttpsProxy string = "https"
|
HttpsProxy string = "https"
|
||||||
StcpProxy string = "stcp"
|
StcpProxy string = "stcp"
|
||||||
@ -33,4 +34,7 @@ var (
|
|||||||
// authentication method
|
// authentication method
|
||||||
TokenAuthMethod string = "token"
|
TokenAuthMethod string = "token"
|
||||||
OidcAuthMethod string = "oidc"
|
OidcAuthMethod string = "oidc"
|
||||||
|
|
||||||
|
// tcp multiplexer
|
||||||
|
HttpConnectTcpMultiplexer string = "httpconnect"
|
||||||
)
|
)
|
||||||
|
@ -107,6 +107,9 @@ type NewProxy struct {
|
|||||||
|
|
||||||
// stcp
|
// stcp
|
||||||
Sk string `json:"sk"`
|
Sk string `json:"sk"`
|
||||||
|
|
||||||
|
// tcpmux
|
||||||
|
Multiplexer string `json:"multiplexer"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NewProxyResp struct {
|
type NewProxyResp struct {
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/fatedier/frp/models/nathole"
|
"github.com/fatedier/frp/models/nathole"
|
||||||
"github.com/fatedier/frp/server/group"
|
"github.com/fatedier/frp/server/group"
|
||||||
"github.com/fatedier/frp/server/ports"
|
"github.com/fatedier/frp/server/ports"
|
||||||
|
"github.com/fatedier/frp/utils/tcpmux"
|
||||||
"github.com/fatedier/frp/utils/vhost"
|
"github.com/fatedier/frp/utils/vhost"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -46,4 +47,7 @@ type ResourceController struct {
|
|||||||
|
|
||||||
// Controller for nat hole connections
|
// Controller for nat hole connections
|
||||||
NatHoleController *nathole.NatHoleController
|
NatHoleController *nathole.NatHoleController
|
||||||
|
|
||||||
|
// TcpMux HTTP CONNECT multiplexer
|
||||||
|
TcpMuxHttpConnectMuxer *tcpmux.HttpConnectTcpMuxer
|
||||||
}
|
}
|
||||||
|
@ -95,6 +95,12 @@ type TcpOutConf struct {
|
|||||||
RemotePort int `json:"remote_port"`
|
RemotePort int `json:"remote_port"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TcpMuxOutConf struct {
|
||||||
|
BaseOutConf
|
||||||
|
config.DomainConf
|
||||||
|
Multiplexer string `json:"multiplexer"`
|
||||||
|
}
|
||||||
|
|
||||||
type UdpOutConf struct {
|
type UdpOutConf struct {
|
||||||
BaseOutConf
|
BaseOutConf
|
||||||
RemotePort int `json:"remote_port"`
|
RemotePort int `json:"remote_port"`
|
||||||
@ -124,6 +130,8 @@ func getConfByType(proxyType string) interface{} {
|
|||||||
switch proxyType {
|
switch proxyType {
|
||||||
case consts.TcpProxy:
|
case consts.TcpProxy:
|
||||||
return &TcpOutConf{}
|
return &TcpOutConf{}
|
||||||
|
case consts.TcpMuxProxy:
|
||||||
|
return &TcpMuxOutConf{}
|
||||||
case consts.UdpProxy:
|
case consts.UdpProxy:
|
||||||
return &UdpOutConf{}
|
return &UdpOutConf{}
|
||||||
case consts.HttpProxy:
|
case consts.HttpProxy:
|
||||||
|
@ -177,6 +177,11 @@ func NewProxy(ctx context.Context, runId string, rc *controller.ResourceControll
|
|||||||
BaseProxy: &basePxy,
|
BaseProxy: &basePxy,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
|
case *config.TcpMuxProxyConf:
|
||||||
|
pxy = &TcpMuxProxy{
|
||||||
|
BaseProxy: &basePxy,
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
case *config.HttpProxyConf:
|
case *config.HttpProxyConf:
|
||||||
pxy = &HttpProxy{
|
pxy = &HttpProxy{
|
||||||
BaseProxy: &basePxy,
|
BaseProxy: &basePxy,
|
||||||
|
95
server/proxy/tcpmux.go
Normal file
95
server/proxy/tcpmux.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
// Copyright 2020 guylewin, guy@lewin.co.il
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/models/config"
|
||||||
|
"github.com/fatedier/frp/models/consts"
|
||||||
|
"github.com/fatedier/frp/utils/util"
|
||||||
|
"github.com/fatedier/frp/utils/vhost"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TcpMuxProxy struct {
|
||||||
|
*BaseProxy
|
||||||
|
cfg *config.TcpMuxProxyConf
|
||||||
|
|
||||||
|
realPort int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pxy *TcpMuxProxy) httpConnectListen(domain string, addrs []string) ([]string, error) {
|
||||||
|
routeConfig := &vhost.VhostRouteConfig{
|
||||||
|
Domain: domain,
|
||||||
|
}
|
||||||
|
l, err := pxy.rc.TcpMuxHttpConnectMuxer.Listen(pxy.ctx, routeConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pxy.xl.Info("tcpmux httpconnect multiplexer listens for host [%s]", routeConfig.Domain)
|
||||||
|
pxy.listeners = append(pxy.listeners, l)
|
||||||
|
return append(addrs, util.CanonicalAddr(routeConfig.Domain, pxy.serverCfg.TcpMuxHttpConnectPort)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pxy *TcpMuxProxy) httpConnectRun() (remoteAddr string, err error) {
|
||||||
|
addrs := make([]string, 0)
|
||||||
|
for _, domain := range pxy.cfg.CustomDomains {
|
||||||
|
if domain == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs, err = pxy.httpConnectListen(domain, addrs)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pxy.cfg.SubDomain != "" {
|
||||||
|
addrs, err = pxy.httpConnectListen(pxy.cfg.SubDomain+"."+pxy.serverCfg.SubDomainHost, addrs)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pxy.startListenHandler(pxy, HandleUserTcpConnection)
|
||||||
|
remoteAddr = strings.Join(addrs, ",")
|
||||||
|
return remoteAddr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pxy *TcpMuxProxy) Run() (remoteAddr string, err error) {
|
||||||
|
switch pxy.cfg.Multiplexer {
|
||||||
|
case consts.HttpConnectTcpMultiplexer:
|
||||||
|
remoteAddr, err = pxy.httpConnectRun()
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("unknown multiplexer [%s]", pxy.cfg.Multiplexer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
pxy.Close()
|
||||||
|
}
|
||||||
|
return remoteAddr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pxy *TcpMuxProxy) GetConf() config.ProxyConf {
|
||||||
|
return pxy.cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pxy *TcpMuxProxy) Close() {
|
||||||
|
pxy.BaseProxy.Close()
|
||||||
|
if pxy.cfg.Group == "" {
|
||||||
|
pxy.rc.TcpPortManager.Release(pxy.realPort)
|
||||||
|
}
|
||||||
|
}
|
@ -42,6 +42,7 @@ import (
|
|||||||
"github.com/fatedier/frp/server/stats"
|
"github.com/fatedier/frp/server/stats"
|
||||||
"github.com/fatedier/frp/utils/log"
|
"github.com/fatedier/frp/utils/log"
|
||||||
frpNet "github.com/fatedier/frp/utils/net"
|
frpNet "github.com/fatedier/frp/utils/net"
|
||||||
|
"github.com/fatedier/frp/utils/tcpmux"
|
||||||
"github.com/fatedier/frp/utils/util"
|
"github.com/fatedier/frp/utils/util"
|
||||||
"github.com/fatedier/frp/utils/version"
|
"github.com/fatedier/frp/utils/version"
|
||||||
"github.com/fatedier/frp/utils/vhost"
|
"github.com/fatedier/frp/utils/vhost"
|
||||||
@ -53,6 +54,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
connReadTimeout time.Duration = 10 * time.Second
|
connReadTimeout time.Duration = 10 * time.Second
|
||||||
|
vhostReadWriteTimeout time.Duration = 30 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
// Server service
|
// Server service
|
||||||
@ -212,7 +214,7 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
svr.rc.VhostHttpsMuxer, err = vhost.NewHttpsMuxer(l, 30*time.Second)
|
svr.rc.VhostHttpsMuxer, err = vhost.NewHttpsMuxer(l, vhostReadWriteTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Create vhost httpsMuxer error, %v", err)
|
err = fmt.Errorf("Create vhost httpsMuxer error, %v", err)
|
||||||
return
|
return
|
||||||
@ -220,6 +222,23 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
|
|||||||
log.Info("https service listen on %s:%d", cfg.ProxyBindAddr, cfg.VhostHttpsPort)
|
log.Info("https service listen on %s:%d", cfg.ProxyBindAddr, cfg.VhostHttpsPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create tcpmux httpconnect multiplexer.
|
||||||
|
if cfg.TcpMuxHttpConnectPort > 0 {
|
||||||
|
var l net.Listener
|
||||||
|
l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", cfg.ProxyBindAddr, cfg.TcpMuxHttpConnectPort))
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Create server listener error, %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
svr.rc.TcpMuxHttpConnectMuxer, err = tcpmux.NewHttpConnectTcpMuxer(l, vhostReadWriteTimeout)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Create vhost tcpMuxer error, %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info("tcpmux httpconnect multiplexer listen on %s:%d", cfg.ProxyBindAddr, cfg.TcpMuxHttpConnectPort)
|
||||||
|
}
|
||||||
|
|
||||||
// frp tls listener
|
// frp tls listener
|
||||||
svr.tlsListener = svr.muxer.Listen(1, 1, func(data []byte) bool {
|
svr.tlsListener = svr.muxer.Listen(1, 1, func(data []byte) bool {
|
||||||
return int(data[0]) == frpNet.FRP_TLS_HEAD_BYTE
|
return int(data[0]) == frpNet.FRP_TLS_HEAD_BYTE
|
||||||
|
@ -126,6 +126,13 @@ custom_domains = test6.frp.com
|
|||||||
host_header_rewrite = test6.frp.com
|
host_header_rewrite = test6.frp.com
|
||||||
header_X-From-Where = frp
|
header_X-From-Where = frp
|
||||||
|
|
||||||
|
[tcpmuxhttpconnect]
|
||||||
|
type = tcpmux
|
||||||
|
multiplexer = httpconnect
|
||||||
|
local_ip = 127.0.0.1
|
||||||
|
local_port = 10701
|
||||||
|
custom_domains = tunnel1
|
||||||
|
|
||||||
[wildcard_http]
|
[wildcard_http]
|
||||||
type = http
|
type = http
|
||||||
local_ip = 127.0.0.1
|
local_ip = 127.0.0.1
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
bind_addr = 0.0.0.0
|
bind_addr = 0.0.0.0
|
||||||
bind_port = 10700
|
bind_port = 10700
|
||||||
vhost_http_port = 10804
|
vhost_http_port = 10804
|
||||||
|
tcpmux_httpconnect_port = 10806
|
||||||
log_level = trace
|
log_level = trace
|
||||||
token = 123456
|
token = 123456
|
||||||
allow_ports = 10000-20000,20002,30000-50000
|
allow_ports = 10000-20000,20002,30000-50000
|
||||||
|
@ -212,6 +212,17 @@ func TestHttp(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTcpMux(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
conn, err := gnet.DialTcpByProxy(fmt.Sprintf("http://%s:%d", "127.0.0.1", consts.TEST_TCP_MUX_FRP_PORT), "tunnel1")
|
||||||
|
if assert.NoError(err) {
|
||||||
|
res, err := util.SendTcpMsgByConn(conn, consts.TEST_TCP_ECHO_STR)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal(consts.TEST_TCP_ECHO_STR, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestWebSocket(t *testing.T) {
|
func TestWebSocket(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
@ -40,6 +40,8 @@ var (
|
|||||||
TEST_HTTP_FOO_STR string = "http foo string: " + TEST_STR
|
TEST_HTTP_FOO_STR string = "http foo string: " + TEST_STR
|
||||||
TEST_HTTP_BAR_STR string = "http bar string: " + TEST_STR
|
TEST_HTTP_BAR_STR string = "http bar string: " + TEST_STR
|
||||||
|
|
||||||
|
TEST_TCP_MUX_FRP_PORT int = 10806
|
||||||
|
|
||||||
TEST_STCP_FRP_PORT int = 10805
|
TEST_STCP_FRP_PORT int = 10805
|
||||||
TEST_STCP_EC_FRP_PORT int = 10905
|
TEST_STCP_EC_FRP_PORT int = 10905
|
||||||
TEST_STCP_ECHO_STR string = "stcp type:" + TEST_STR
|
TEST_STCP_ECHO_STR string = "stcp type:" + TEST_STR
|
||||||
|
68
utils/tcpmux/httpconnect.go
Normal file
68
utils/tcpmux/httpconnect.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// Copyright 2020 guylewin, guy@lewin.co.il
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package tcpmux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/utils/util"
|
||||||
|
"github.com/fatedier/frp/utils/vhost"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HttpConnectTcpMuxer struct {
|
||||||
|
*vhost.VhostMuxer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHttpConnectTcpMuxer(listener net.Listener, timeout time.Duration) (*HttpConnectTcpMuxer, error) {
|
||||||
|
mux, err := vhost.NewVhostMuxer(listener, getHostFromHttpConnect, nil, sendHttpOk, nil, timeout)
|
||||||
|
return &HttpConnectTcpMuxer{mux}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func readHttpConnectRequest(rd io.Reader) (host string, err error) {
|
||||||
|
bufioReader := bufio.NewReader(rd)
|
||||||
|
|
||||||
|
req, err := http.ReadRequest(bufioReader)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Method != "CONNECT" {
|
||||||
|
err = fmt.Errorf("connections to tcp vhost must be of method CONNECT")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
host = util.GetHostFromAddr(req.Host)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendHttpOk(c net.Conn) error {
|
||||||
|
return util.OkResponse().Write(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHostFromHttpConnect(c net.Conn) (_ net.Conn, _ map[string]string, err error) {
|
||||||
|
reqInfoMap := make(map[string]string, 0)
|
||||||
|
host, err := readHttpConnectRequest(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, reqInfoMap, err
|
||||||
|
}
|
||||||
|
reqInfoMap["Host"] = host
|
||||||
|
reqInfoMap["Scheme"] = "tcp"
|
||||||
|
return c, reqInfoMap, nil
|
||||||
|
}
|
44
utils/util/http.go
Normal file
44
utils/util/http.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2020 guylewin, guy@lewin.co.il
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func OkResponse() *http.Response {
|
||||||
|
header := make(http.Header)
|
||||||
|
|
||||||
|
res := &http.Response{
|
||||||
|
Status: "OK",
|
||||||
|
StatusCode: 200,
|
||||||
|
Proto: "HTTP/1.1",
|
||||||
|
ProtoMajor: 1,
|
||||||
|
ProtoMinor: 1,
|
||||||
|
Header: header,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHostFromAddr(addr string) (host string) {
|
||||||
|
strs := strings.Split(addr, ":")
|
||||||
|
if len(strs) > 1 {
|
||||||
|
host = strs[0]
|
||||||
|
} else {
|
||||||
|
host = addr
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
@ -26,6 +26,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
frpLog "github.com/fatedier/frp/utils/log"
|
frpLog "github.com/fatedier/frp/utils/log"
|
||||||
|
"github.com/fatedier/frp/utils/util"
|
||||||
|
|
||||||
"github.com/fatedier/golib/pool"
|
"github.com/fatedier/golib/pool"
|
||||||
)
|
)
|
||||||
@ -34,16 +35,6 @@ var (
|
|||||||
ErrNoDomain = errors.New("no such domain")
|
ErrNoDomain = errors.New("no such domain")
|
||||||
)
|
)
|
||||||
|
|
||||||
func getHostFromAddr(addr string) (host string) {
|
|
||||||
strs := strings.Split(addr, ":")
|
|
||||||
if len(strs) > 1 {
|
|
||||||
host = strs[0]
|
|
||||||
} else {
|
|
||||||
host = addr
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type HttpReverseProxyOptions struct {
|
type HttpReverseProxyOptions struct {
|
||||||
ResponseHeaderTimeoutS int64
|
ResponseHeaderTimeoutS int64
|
||||||
}
|
}
|
||||||
@ -67,7 +58,7 @@ func NewHttpReverseProxy(option HttpReverseProxyOptions, vhostRouter *VhostRoute
|
|||||||
Director: func(req *http.Request) {
|
Director: func(req *http.Request) {
|
||||||
req.URL.Scheme = "http"
|
req.URL.Scheme = "http"
|
||||||
url := req.Context().Value("url").(string)
|
url := req.Context().Value("url").(string)
|
||||||
oldHost := getHostFromAddr(req.Context().Value("host").(string))
|
oldHost := util.GetHostFromAddr(req.Context().Value("host").(string))
|
||||||
host := rp.GetRealHost(oldHost, url)
|
host := rp.GetRealHost(oldHost, url)
|
||||||
if host != "" {
|
if host != "" {
|
||||||
req.Host = host
|
req.Host = host
|
||||||
@ -84,7 +75,7 @@ func NewHttpReverseProxy(option HttpReverseProxyOptions, vhostRouter *VhostRoute
|
|||||||
DisableKeepAlives: true,
|
DisableKeepAlives: true,
|
||||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
url := ctx.Value("url").(string)
|
url := ctx.Value("url").(string)
|
||||||
host := getHostFromAddr(ctx.Value("host").(string))
|
host := util.GetHostFromAddr(ctx.Value("host").(string))
|
||||||
remote := ctx.Value("remote").(string)
|
remote := ctx.Value("remote").(string)
|
||||||
return rp.CreateConnection(host, url, remote)
|
return rp.CreateConnection(host, url, remote)
|
||||||
},
|
},
|
||||||
@ -187,7 +178,7 @@ func (rp *HttpReverseProxy) getVhost(domain string, location string) (vr *VhostR
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (rp *HttpReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
func (rp *HttpReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
domain := getHostFromAddr(req.Host)
|
domain := util.GetHostFromAddr(req.Host)
|
||||||
location := req.URL.Path
|
location := req.URL.Path
|
||||||
user, passwd, _ := req.BasicAuth()
|
user, passwd, _ := req.BasicAuth()
|
||||||
if !rp.CheckAuth(domain, location, user, passwd) {
|
if !rp.CheckAuth(domain, location, user, passwd) {
|
||||||
|
@ -48,7 +48,7 @@ type HttpsMuxer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewHttpsMuxer(listener net.Listener, timeout time.Duration) (*HttpsMuxer, error) {
|
func NewHttpsMuxer(listener net.Listener, timeout time.Duration) (*HttpsMuxer, error) {
|
||||||
mux, err := NewVhostMuxer(listener, GetHttpsHostname, nil, nil, timeout)
|
mux, err := NewVhostMuxer(listener, GetHttpsHostname, nil, nil, nil, timeout)
|
||||||
return &HttpsMuxer{mux}, err
|
return &HttpsMuxer{mux}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,22 +29,25 @@ import (
|
|||||||
type muxFunc func(net.Conn) (net.Conn, map[string]string, error)
|
type muxFunc func(net.Conn) (net.Conn, map[string]string, error)
|
||||||
type httpAuthFunc func(net.Conn, string, string, string) (bool, error)
|
type httpAuthFunc func(net.Conn, string, string, string) (bool, error)
|
||||||
type hostRewriteFunc func(net.Conn, string) (net.Conn, error)
|
type hostRewriteFunc func(net.Conn, string) (net.Conn, error)
|
||||||
|
type successFunc func(net.Conn) error
|
||||||
|
|
||||||
type VhostMuxer struct {
|
type VhostMuxer struct {
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
vhostFunc muxFunc
|
vhostFunc muxFunc
|
||||||
authFunc httpAuthFunc
|
authFunc httpAuthFunc
|
||||||
|
successFunc successFunc
|
||||||
rewriteFunc hostRewriteFunc
|
rewriteFunc hostRewriteFunc
|
||||||
registryRouter *VhostRouters
|
registryRouter *VhostRouters
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVhostMuxer(listener net.Listener, vhostFunc muxFunc, authFunc httpAuthFunc, rewriteFunc hostRewriteFunc, timeout time.Duration) (mux *VhostMuxer, err error) {
|
func NewVhostMuxer(listener net.Listener, vhostFunc muxFunc, authFunc httpAuthFunc, successFunc successFunc, rewriteFunc hostRewriteFunc, timeout time.Duration) (mux *VhostMuxer, err error) {
|
||||||
mux = &VhostMuxer{
|
mux = &VhostMuxer{
|
||||||
listener: listener,
|
listener: listener,
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
vhostFunc: vhostFunc,
|
vhostFunc: vhostFunc,
|
||||||
authFunc: authFunc,
|
authFunc: authFunc,
|
||||||
|
successFunc: successFunc,
|
||||||
rewriteFunc: rewriteFunc,
|
rewriteFunc: rewriteFunc,
|
||||||
registryRouter: NewVhostRouters(),
|
registryRouter: NewVhostRouters(),
|
||||||
}
|
}
|
||||||
@ -149,7 +152,15 @@ func (v *VhostMuxer) handle(c net.Conn) {
|
|||||||
c.Close()
|
c.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
xl := xlog.FromContextSafe(l.ctx)
|
xl := xlog.FromContextSafe(l.ctx)
|
||||||
|
if v.successFunc != nil {
|
||||||
|
if err := v.successFunc(c); err != nil {
|
||||||
|
xl.Info("success func failure on vhost connection: %v", err)
|
||||||
|
c.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if authFunc is exist and userName/password is set
|
// if authFunc is exist and userName/password is set
|
||||||
// then verify user access
|
// then verify user access
|
||||||
|
Loading…
Reference in New Issue
Block a user