Skip to content

Commit

Permalink
0.0.5
Browse files Browse the repository at this point in the history
1. 重构代码, 修复 smux
2. 提供 `client` 配置 `mode` ,可选 `http` 和 `websocket`, 默认为 `websocket`
3. 提供 `client` 配置 `smux`,可选 `true` 和 `false`, 默认为 `false`
  • Loading branch information
ooooo-youwillsee committed Aug 29, 2023
1 parent 68691b9 commit b81126c
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 150 deletions.
12 changes: 11 additions & 1 deletion RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,14 @@
## 0.0.3

1. 优化代码, WebSocket 抽象为 Conn
2. 增加多路复用, 配置 smux = "true"
2. 增加多路复用, 配置 smux = "true"

## 0.0.4

1. 优化错误信息

## 0.0.5

1. 重构代码, 修复 smux
2. 提供 `client` 配置 `mode` ,可选 `http``websocket`, 默认为 `websocket`
3. 提供 `client` 配置 `smux`,可选 `true``false`, 默认为 `false`
3 changes: 1 addition & 2 deletions cmd/http-tunnel-client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ func main() {
}

func StartClient(cc *tunnel.ClientConfig) {
server := tunnel.NewClient(cc.LocalAddr, cc.RemoteAddr, cc.TunnelAddr, cc.TunnelUrl,
tunnel.ClientWithToken(cc.Token), tunnel.ClientWithSMux(cc.Smux))
server := tunnel.NewClient(cc)
err := server.ListenAndServe()
if err != nil {
log.Error(err)
Expand Down
2 changes: 1 addition & 1 deletion cmd/http-tunnel-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func main() {
}

func startServer(sc *tunnel.ServerConfig) {
server := tunnel.NewServer(sc.Addr, sc.Url, tunnel.ServerWithToken(sc.Token))
server := tunnel.NewServer(sc)
err := server.ListenAndServe()
if err != nil {
log.Error(err)
Expand Down
21 changes: 18 additions & 3 deletions example/example.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,28 @@ var (
localAddr = ":8111"
remoteAddr = ":30001"
tunnelAddr = ":8112"
tunnelUrl = "/"
)

func main() {
server := tunnel.NewServer(tunnelAddr, "")
sc := &tunnel.ServerConfig{
TunnelAddr: tunnelAddr,
TunnelUrl: tunnelUrl,
Token: "",
}
server := tunnel.NewServer(sc)
go server.ListenAndServe()

client := tunnel.NewClient(localAddr, remoteAddr, tunnelAddr, "", tunnel.ClientWithSMux("true"))
cc := &tunnel.ClientConfig{
LocalAddr: localAddr,
RemoteAddr: remoteAddr,
TunnelAddr: tunnelAddr,
TunnelUrl: tunnelUrl,
Token: "",
IsSmux: true,
Mode: tunnel.CONNECT_WEBSOCKET,
}
client := tunnel.NewClient(cc)
go client.ListenAndServe()

go testServer(remoteAddr)
Expand All @@ -29,7 +44,7 @@ func testServer(addr string) {
mux := http.NewServeMux()
mux.HandleFunc("/", index)
mux.HandleFunc("/hello", hello)
log.Infoln("listen addr ", addr)
log.Infoln("listen test server addr ", addr)
err := http.ListenAndServe(addr, mux)
if err != nil {
log.Fatalln(err)
Expand Down
66 changes: 31 additions & 35 deletions tunnel/client.go
Original file line number Diff line number Diff line change
@@ -1,59 +1,38 @@
package tunnel

import (
"fmt"
log "github.com/sirupsen/logrus"
"github.com/xtaci/smux"
"net"
"net/http"
)

var (
CONNECT_HTTP ConnectMode = "http"
CONNECT_WEBSOCKET ConnectMode = "websocket"
)

type ConnectMode string

type Client struct {
localAddr string
remoteAddr string
tunnelAddr string
tunnelUrl string
token string
mode ConnectMode
// true or false, default is true
isSmux string
config *ClientConfig
// it is not nil if IsSmux is true
smuxSession *smux.Session
}

func NewClient(localAddr, remoteAddr, tunnelAddr, tunnelUrl string, options ...ClientOption) *Client {
if localAddr == "" || remoteAddr == "" || tunnelAddr == "" {
panic("localAddr or remoteAddr or tunnelAddr is empty")
}
if tunnelUrl == "" {
tunnelUrl = URL_CONNECT
}
func NewClient(cc *ClientConfig, options ...ClientOption) *Client {
c := &Client{
localAddr: localAddr,
remoteAddr: remoteAddr,
tunnelAddr: tunnelAddr,
tunnelUrl: tunnelUrl,
mode: CONNECT_WEBSOCKET,
config: cc,
}
for _, option := range options {
option(c)
}
log.Infof("NewClient localAddr[%s], remoteAddr[%s], tunnelAddr[%s], tunnelUrl[%s]", localAddr, remoteAddr, tunnelAddr, tunnelUrl)
log.Infof("NewClient config %s", c.config)
return c
}

func (c *Client) ListenAndServe() error {
l, err := net.Listen("tcp", c.localAddr)
l, err := net.Listen("tcp", c.config.LocalAddr)
if err != nil {
log.Errorf("listen localAddr %s, err: %v", c.localAddr, err)
log.Errorf("listen LocalAddr %s, err: %v", c.config.LocalAddr, err)
return err
}
defer l.Close()
log.Infof("listen localAddr %s", c.localAddr)
log.Infof("listen LocalAddr %s", c.config.LocalAddr)

for {
conn, err := l.Accept()
Expand All @@ -66,22 +45,25 @@ func (c *Client) ListenAndServe() error {
}

func (c *Client) handleConn(conn net.Conn) {
log.Infof("handle conn %s", conn.RemoteAddr())
defer conn.Close()
setTCPConnOptions(conn)

connectFn := func() net.Conn {
var tunnelConn net.Conn
switch c.mode {
switch c.config.Mode {
case CONNECT_HTTP:
tunnelConn = c.connectWithHTTP()
case CONNECT_WEBSOCKET:
tunnelConn = c.connectWithWebSocket()
default:
panic("mode is empty")
}
return tunnelConn
}

// support isSmux
if c.isSmux == "true" {
// support smux
if c.config.IsSmux {
if c.smuxSession == nil || c.smuxSession.IsClosed() {
tunnelConn := connectFn()
if tunnelConn == nil {
Expand All @@ -90,7 +72,7 @@ func (c *Client) handleConn(conn net.Conn) {
defer tunnelConn.Close()
session, err := smux.Client(tunnelConn, smux.DefaultConfig())
if err != nil {
log.Errorf("new smux client, err: %v", err)
log.Errorf("new IsSmux client, err: %v", err)
return
}
defer session.Close()
Expand All @@ -101,12 +83,26 @@ func (c *Client) handleConn(conn net.Conn) {
log.Errorf("mux open stream, err: %v", err)
return
}
if stream == nil {
log.Errorf("mux open stream is null")
return
}
copyDataOnConn(conn, stream)
return
}

// per connection
tunnelConn := connectFn()
if tunnelConn == nil {
return
}
defer tunnelConn.Close()
copyDataOnConn(conn, tunnelConn)
}

func (c *Client) setHeader(header *http.Header) {
header.Set(HEADER_MODE, string(c.config.Mode))
header.Set(HEADER_REMOTE_ADDR, c.config.RemoteAddr)
header.Set(HEADER_IS_SMUX, fmt.Sprint(c.config.IsSmux))
header.Set(HEADER_TOKEN, c.config.Token)
}
86 changes: 62 additions & 24 deletions tunnel/config.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
package tunnel

import (
"encoding/json"
"fmt"
"github.com/spf13/viper"
"strings"
)

const (
DEFAULT_TUNNEL_URL = "/"

CONFIG_COMMON = "common"
CONFIG_LOCAL_ADDR = "local_addr"
CONFIG_REMOTE_ADDR = "remote_addr"
CONFIG_TUNNEL_ADDR = "tunnel_addr"
CONFIG_TUNNEL_URL = "tunnel_url"
CONFIG_TOKEN = "Token"
CONFIG_SMUX = "IsSmux"
CONFIG_MODE = "Mode"
)

const (
CONNECT_HTTP ConnectMode = "http"
CONNECT_WEBSOCKET ConnectMode = "websocket"
)

type ConnectMode string

type ClientConfigs []*ClientConfig

type ClientConfig struct {
Expand All @@ -14,7 +34,8 @@ type ClientConfig struct {
TunnelAddr string
TunnelUrl string
Token string
Smux string
IsSmux bool
Mode ConnectMode
}

func NewClientConfigsFromFile(configFile string) *ClientConfigs {
Expand All @@ -25,32 +46,49 @@ func NewClientConfigsFromFile(configFile string) *ClientConfigs {

ccs := &ClientConfigs{}
// parse common
tunnelAddr := viper.GetString("common.tunnel_addr")
tunnelUrl := viper.GetString("common.tunnel_url")
tunnelAddr := viper.GetString(CONFIG_COMMON + "." + CONFIG_TUNNEL_ADDR)
tunnelUrl := viper.GetString(CONFIG_COMMON + "." + CONFIG_TUNNEL_URL)
isSmux := viper.GetBool(CONFIG_COMMON + "." + CONFIG_SMUX)
mode := viper.GetString(CONFIG_COMMON + "." + CONFIG_MODE)
if tunnelUrl == "" {
tunnelUrl = DEFAULT_TUNNEL_URL
}
if mode == "" {
mode = string(CONNECT_WEBSOCKET)
}

// parse special
for g, m := range viper.AllSettings() {
if g == "common" {
if g == CONFIG_COMMON {
continue
}
cc := &ClientConfig{}
cc.LocalAddr = getString(m, "local_addr", "")
cc.RemoteAddr = getString(m, "remote_addr", "")
cc.TunnelAddr = getString(m, "tunnel_addr", tunnelAddr)
cc.TunnelUrl = getString(m, "tunnel_url", tunnelUrl)
cc.Token = getString(m, "token", "")
cc.Smux = getString(m, "isSmux", "true")
cc.LocalAddr = getValue(m, CONFIG_LOCAL_ADDR, "")
cc.RemoteAddr = getValue(m, CONFIG_REMOTE_ADDR, "")
cc.TunnelAddr = getValue(m, CONFIG_TUNNEL_ADDR, tunnelAddr)
cc.TunnelUrl = getValue(m, CONFIG_TUNNEL_URL, tunnelUrl)
cc.Token = getValue(m, CONFIG_TOKEN, "")
cc.IsSmux = getValue(m, CONFIG_SMUX, isSmux)
cc.Mode = ConnectMode(getValue(m, CONFIG_MODE, mode))
if cc.LocalAddr == "" || cc.RemoteAddr == "" || cc.TunnelAddr == "" {
panic(fmt.Sprintf("group %s LocalAddr or RemoteAddr or TunnelAddr is empty", g))
}
*ccs = append(*ccs, cc)
}
return ccs
}

func (c *ClientConfig) String() string {
bytes, _ := json.Marshal(c)
return string(bytes)
}

type ServerConfigs []*ServerConfig

type ServerConfig struct {
Addr string
Url string
Token string
TunnelAddr string
TunnelUrl string
Token string
}

func NewServerConfigsFrom(configFile string) *ServerConfigs {
Expand All @@ -62,26 +100,26 @@ func NewServerConfigsFrom(configFile string) *ServerConfigs {
scs := &ServerConfigs{}
for g, m := range viper.AllSettings() {
sc := &ServerConfig{}
sc.Addr = getString(m, "tunnel_addr", "")
sc.Url = getString(m, "tunnel_url", "")
sc.Token = getString(m, "token", "")
if sc.Addr == "" {
panic(fmt.Sprintf("group %s config 'Addr' is empty", g))
sc.TunnelAddr = getValue(m, CONFIG_TUNNEL_ADDR, "")
sc.TunnelUrl = getValue(m, CONFIG_TUNNEL_URL, DEFAULT_TUNNEL_URL)
sc.Token = getValue(m, CONFIG_TOKEN, "")
if sc.TunnelAddr == "" {
panic(fmt.Sprintf("group %s TunnelAddr is empty", g))
}
*scs = append(*scs, sc)
}
return scs
}

func splitAddr(addr string) (string, string) {
split := strings.Split(addr, ":")
return split[0], split[1]
func (c *ServerConfig) String() string {
bytes, _ := json.Marshal(c)
return string(bytes)
}

func getString(m interface{}, key string, defaultValue string) string {
func getValue[T any](m interface{}, key string, defaultValue T) T {
mm := m.(map[string]interface{})
if v, ok := mm[key]; ok {
return v.(string)
return v.(T)
}
return defaultValue
}
10 changes: 5 additions & 5 deletions tunnel/connect_http.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@ import (

func (c *Client) connectWithHTTP() net.Conn {
// dial tunnel
tunnelConn, err := net.Dial("tcp", c.tunnelAddr)
tunnelConn, err := net.Dial("tcp", c.config.TunnelAddr)
if err != nil {
log.Errorf("dial tunnelAddr %s, err: %v", c.tunnelAddr, err)
log.Errorf("dial TunnelAddr %s, err: %v", c.config.TunnelAddr, err)
return nil
}
// send request
request, _ := http.NewRequest(http.MethodConnect, c.tunnelUrl, nil)
request.Host = c.tunnelAddr
request.Header.Set(HEADER_REMOTE_ADDR, c.remoteAddr)
request, _ := http.NewRequest(http.MethodConnect, c.config.TunnelUrl, nil)
request.Host = c.config.TunnelAddr
request.Header.Set("HOST", request.Host)
c.setHeader(&request.Header)
err = request.Write(tunnelConn)
if err != nil {
log.Error("send connect request ", err)
Expand Down
8 changes: 3 additions & 5 deletions tunnel/connect_websocket.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@ import (
func (c *Client) connectWithWebSocket() net.Conn {
wsurl := url.URL{
Scheme: "ws",
Host: c.tunnelAddr,
Path: c.tunnelUrl,
Host: c.config.TunnelAddr,
Path: c.config.TunnelUrl,
}
header := http.Header{}
header.Set(HEADER_REMOTE_ADDR, c.remoteAddr)
header.Set(HEADER_TOKEN, c.token)
header.Set(HEADER_IS_SMUX, c.isSmux)
c.setHeader(&header)
wsc, _, err := websocket.DefaultDialer.Dial(wsurl.String(), header)
if err != nil {
log.Errorf("dial websocket addr %s, err: %v", wsurl.String(), err)
Expand Down
Loading

0 comments on commit b81126c

Please sign in to comment.