add new file

This commit is contained in:
he liu
2022-01-23 17:30:38 +08:00
parent 05b2e55f39
commit c482967f8c
125 changed files with 9688 additions and 0 deletions

129
component/client/client.go Normal file
View File

@@ -0,0 +1,129 @@
package client
import (
"ehang.io/nps/core/action"
"ehang.io/nps/core/handler"
"ehang.io/nps/core/process"
"ehang.io/nps/core/rule"
"ehang.io/nps/lib/enet"
"ehang.io/nps/lib/logger"
"go.uber.org/zap"
"io"
"net"
"net/http"
"sync"
"sync/atomic"
"time"
)
type Client struct {
controlLn net.Listener
dataLn net.Listener
lastPongTime time.Time
mux *http.ServeMux
ticker *time.Ticker
closeCh chan struct{}
closed int32
wg sync.WaitGroup
}
func NewClient(controlLn, dataLn net.Listener) *Client {
return &Client{
controlLn: controlLn,
dataLn: dataLn,
mux: &http.ServeMux{},
ticker: time.NewTicker(time.Second * 5),
closeCh: make(chan struct{}, 0),
}
}
func (c *Client) ping(writer http.ResponseWriter, request *http.Request) {
c.lastPongTime = time.Now()
_, err := io.WriteString(writer, "pong")
if err != nil {
logger.Warn("write pong error", zap.Error(err))
return
}
logger.Debug("write pong success")
}
func (c *Client) Run() {
c.mux.HandleFunc("/ping", c.ping)
c.wg.Add(3)
go c.handleControlConn()
go c.handleDataConn()
go c.checkPing()
c.wg.Wait()
}
func (c *Client) HasPong() bool {
return time.Now().Sub(c.lastPongTime).Seconds() < 10
}
func (c *Client) checkPing() {
for {
select {
case <-c.ticker.C:
if !c.lastPongTime.IsZero() && time.Now().Sub(c.lastPongTime).Seconds() > 15 && c.controlLn != nil {
logger.Debug("close connection", zap.Time("lastPongTime", c.lastPongTime), zap.Time("now", time.Now()))
_ = c.controlLn.Close()
}
case <-c.closeCh:
c.wg.Done()
return
}
}
}
func (c *Client) handleDataConn() {
h := &handler.DefaultHandler{}
ac := &action.LocalAction{}
err := ac.Init()
if err != nil {
logger.Warn("init action failed", zap.Error(err))
return
}
appPr := &process.PbAppProcessor{}
_ = appPr.Init(ac)
h.AddRule(&rule.Rule{Handler: h, Process: appPr, Action: ac})
pingPr := &process.PbPingProcessor{}
_ = appPr.Init(ac)
h.AddRule(&rule.Rule{Handler: h, Process: pingPr, Action: ac})
var conn net.Conn
for {
conn, err = c.dataLn.Accept()
if err != nil {
logger.Error("accept connection failed", zap.Error(err))
break
}
go func(conn net.Conn) {
_, err = h.HandleConn(nil, enet.NewReaderConn(conn))
if err != nil {
logger.Warn("process failed", zap.Error(err))
return
}
}(conn)
}
c.wg.Done()
c.Close()
}
func (c *Client) handleControlConn() {
err := http.Serve(c.controlLn, c.mux)
if err != nil {
logger.Error("http error", zap.Error(err))
}
c.wg.Done()
c.Close()
}
func (c *Client) Close() {
if atomic.CompareAndSwapInt32(&c.closed, 0, 1) {
c.closeCh <- struct{}{}
c.ticker.Stop()
_ = c.controlLn.Close()
_ = c.dataLn.Close()
}
}

69
component/client/conn.go Normal file
View File

@@ -0,0 +1,69 @@
package client
import (
"crypto/tls"
"ehang.io/nps/lib/pb"
"ehang.io/nps/transport"
"github.com/lucas-clemente/quic-go"
"github.com/pkg/errors"
"io"
"net"
"time"
)
type TunnelCreator interface {
NewMux(bridgeAddr string, message *pb.ConnRequest, config *tls.Config) (net.Listener, error)
}
type BaseTunnelCreator struct{}
func (bc BaseTunnelCreator) handshake(npcInfo *pb.ConnRequest, rw io.ReadWriteCloser) error {
_, err := pb.WriteMessage(rw, npcInfo)
if err != nil {
return errors.Wrap(err, "write handshake message")
}
var resp pb.NpcResponse
_, err = pb.ReadMessage(rw, &resp)
if err != nil || !resp.Success {
return errors.Wrap(err, resp.Message)
}
return nil
}
type TcpTunnelCreator struct{ BaseTunnelCreator }
func (tc TcpTunnelCreator) NewMux(bridgeAddr string, message *pb.ConnRequest, config *tls.Config) (net.Listener, error) {
conn, err := tls.Dial("tcp", bridgeAddr, config)
if err != nil {
return nil, err
}
if err := tc.handshake(message, conn); err != nil {
return nil, err
}
server := transport.NewYaMux(conn, nil)
return server, server.Server()
}
type QUICTunnelCreator struct{ BaseTunnelCreator }
func (tc QUICTunnelCreator) NewMux(bridgeAddr string, message *pb.ConnRequest, config *tls.Config) (net.Listener, error) {
session, err := quic.DialAddr(bridgeAddr, config, &quic.Config{
MaxIncomingStreams: 1000000,
MaxIncomingUniStreams: 1000000,
MaxIdleTimeout: time.Minute,
KeepAlive: true,
})
if err != nil {
return nil, err
}
stream, err := session.OpenStream()
if err != nil {
return nil, err
}
err = tc.handshake(message, stream)
if err != nil {
return nil, err
}
server := transport.NewQUIC(session)
return server, server.Server()
}

44
component/client/npc.go Normal file
View File

@@ -0,0 +1,44 @@
package client
import (
"crypto/tls"
"ehang.io/nps/lib/cert"
"ehang.io/nps/lib/logger"
"ehang.io/nps/lib/pb"
uuid "github.com/satori/go.uuid"
"go.uber.org/zap"
)
// StartNpc is used to connect to bridge
// proto is quic or tcp
// tlsConfig must contain a npc cert
func StartNpc(proto string, bridgeAddr string, tlsConfig *tls.Config) error {
id, err := cert.GetCertSnFromConfig(tlsConfig)
if err != nil {
return err
}
var creator TunnelCreator
if proto == "quic" {
creator = QUICTunnelCreator{}
} else {
creator = TcpTunnelCreator{}
}
connId := uuid.NewV1().String()
retry:
logger.Info("start connecting to bridge")
controlLn, err := creator.NewMux(bridgeAddr,
&pb.ConnRequest{Id: id, ConnType: &pb.ConnRequest_NpcInfo{NpcInfo: &pb.NpcInfo{TunnelId: connId, IsControlTunnel: true}}}, tlsConfig)
if err != nil {
logger.Error("new control connection error", zap.Error(err))
goto retry
}
dataLn, err := creator.NewMux(bridgeAddr,
&pb.ConnRequest{Id: id, ConnType: &pb.ConnRequest_NpcInfo{NpcInfo: &pb.NpcInfo{TunnelId: connId, IsControlTunnel: false}}}, tlsConfig)
if err != nil {
logger.Error("new data connection error", zap.Error(err))
goto retry
}
c := NewClient(controlLn, dataLn)
c.Run()
goto retry
}