nps/lib/pmux/pmux.go

167 lines
3.7 KiB
Go
Raw Normal View History

2019-03-05 01:23:18 +00:00
// This module is used for port reuse
// Distinguish client, web manager , HTTP and HTTPS according to the difference of protocol
2020-01-09 14:59:24 +00:00
package pmux
2019-03-05 01:23:18 +00:00
import (
"bufio"
"bytes"
"io"
"net"
2019-03-15 06:03:49 +00:00
"os"
2019-03-05 01:23:18 +00:00
"strconv"
"strings"
2019-03-07 10:07:53 +00:00
"time"
2019-08-10 03:15:25 +00:00
2020-01-08 13:57:14 +00:00
"ehang.io/nps/lib/common"
2019-08-10 03:15:25 +00:00
"github.com/astaxie/beego/logs"
"github.com/pkg/errors"
2019-03-05 01:23:18 +00:00
)
const (
2019-03-07 10:07:53 +00:00
HTTP_GET = 716984
HTTP_POST = 807983
HTTP_HEAD = 726965
HTTP_PUT = 808585
HTTP_DELETE = 686976
HTTP_CONNECT = 677978
HTTP_OPTIONS = 798084
HTTP_TRACE = 848265
CLIENT = 848384
ACCEPT_TIME_OUT = 10
2019-03-05 01:23:18 +00:00
)
type PortMux struct {
net.Listener
port int
isClose bool
managerHost string
clientConn chan *PortConn
httpConn chan *PortConn
httpsConn chan *PortConn
managerConn chan *PortConn
}
func NewPortMux(port int, managerHost string) *PortMux {
pMux := &PortMux{
managerHost: managerHost,
port: port,
clientConn: make(chan *PortConn),
httpConn: make(chan *PortConn),
httpsConn: make(chan *PortConn),
managerConn: make(chan *PortConn),
}
pMux.Start()
return pMux
}
func (pMux *PortMux) Start() error {
// Port multiplexing is based on TCP only
tcpAddr, err := net.ResolveTCPAddr("tcp", "0.0.0.0:"+strconv.Itoa(pMux.port))
if err != nil {
return err
}
pMux.Listener, err = net.ListenTCP("tcp", tcpAddr)
if err != nil {
2019-03-15 06:03:49 +00:00
logs.Error(err)
os.Exit(0)
2019-03-05 01:23:18 +00:00
}
go func() {
for {
conn, err := pMux.Listener.Accept()
if err != nil {
logs.Warn(err)
//close
pMux.Close()
}
go pMux.process(conn)
}
}()
return nil
}
func (pMux *PortMux) process(conn net.Conn) {
// Recognition according to different signs
// read 3 byte
buf := make([]byte, 3)
if n, err := io.ReadFull(conn, buf); err != nil || n != 3 {
return
}
var ch chan *PortConn
var rs []byte
var buffer bytes.Buffer
2019-12-07 12:44:32 +00:00
var readMore = false
2019-03-19 14:41:40 +00:00
switch common.BytesToNum(buf) {
2019-03-05 01:23:18 +00:00
case HTTP_CONNECT, HTTP_DELETE, HTTP_GET, HTTP_HEAD, HTTP_OPTIONS, HTTP_POST, HTTP_PUT, HTTP_TRACE: //http and manager
buffer.Reset()
r := bufio.NewReader(conn)
buffer.Write(buf)
for {
b, _, err := r.ReadLine()
if err != nil {
logs.Warn("read line error", err.Error())
conn.Close()
break
}
buffer.Write(b)
buffer.Write([]byte("\r\n"))
if strings.Index(string(b), "Host:") == 0 || strings.Index(string(b), "host:") == 0 {
// Remove host and space effects
str := strings.Replace(string(b), "Host:", "", -1)
str = strings.Replace(str, "host:", "", -1)
str = strings.TrimSpace(str)
// Determine whether it is the same as the manager domain name
2019-03-15 06:03:49 +00:00
if common.GetIpByAddr(str) == pMux.managerHost {
2019-03-05 01:23:18 +00:00
ch = pMux.managerConn
} else {
ch = pMux.httpConn
}
b, _ := r.Peek(r.Buffered())
buffer.Write(b)
rs = buffer.Bytes()
break
}
}
case CLIENT: // client connection
ch = pMux.clientConn
default: // https
2019-12-07 12:44:32 +00:00
readMore = true
2019-03-05 01:23:18 +00:00
ch = pMux.httpsConn
}
if len(rs) == 0 {
rs = buf
}
2019-03-07 10:07:53 +00:00
timer := time.NewTimer(ACCEPT_TIME_OUT)
select {
case <-timer.C:
2019-12-07 12:44:32 +00:00
case ch <- newPortConn(conn, rs, readMore):
2019-03-07 10:07:53 +00:00
}
2019-03-05 01:23:18 +00:00
}
func (pMux *PortMux) Close() error {
if pMux.isClose {
2020-01-09 14:59:24 +00:00
return errors.New("the port pmux has closed")
2019-03-05 01:23:18 +00:00
}
pMux.isClose = true
close(pMux.clientConn)
close(pMux.httpsConn)
close(pMux.httpConn)
close(pMux.managerConn)
return pMux.Listener.Close()
}
func (pMux *PortMux) GetClientListener() net.Listener {
return NewPortListener(pMux.clientConn, pMux.Listener.Addr())
}
func (pMux *PortMux) GetHttpListener() net.Listener {
return NewPortListener(pMux.httpConn, pMux.Listener.Addr())
}
func (pMux *PortMux) GetHttpsListener() net.Listener {
return NewPortListener(pMux.httpsConn, pMux.Listener.Addr())
}
func (pMux *PortMux) GetManagerListener() net.Listener {
return NewPortListener(pMux.managerConn, pMux.Listener.Addr())
}