2024-11-23 12:30:23 +09:00
package hodu
2024-11-12 22:59:37 +09:00
2025-03-19 00:24:42 +09:00
import "container/list"
2024-11-12 22:59:37 +09:00
import "context"
import "crypto/tls"
2024-11-17 14:57:56 +09:00
import "errors"
2024-11-12 22:59:37 +09:00
import "fmt"
import "io"
2024-12-07 22:18:07 +09:00
import "log"
2024-11-12 22:59:37 +09:00
import "net"
2024-11-21 01:11:01 +09:00
import "net/http"
2024-12-05 18:24:42 +09:00
import "net/netip"
2025-08-19 20:20:18 +09:00
import "regexp"
2025-03-07 13:41:44 +09:00
import "slices"
2024-12-17 09:35:51 +09:00
import "strconv"
2025-06-21 22:01:24 +09:00
import "strings"
2024-11-12 22:59:37 +09:00
import "sync"
import "sync/atomic"
2024-12-27 14:43:44 +09:00
import "time"
2024-12-17 09:35:51 +09:00
import "unsafe"
2024-11-12 22:59:37 +09:00
2024-12-13 02:25:27 +09:00
import "golang.org/x/net/websocket"
2024-11-12 22:59:37 +09:00
import "google.golang.org/grpc"
2024-12-07 16:57:00 +09:00
import "google.golang.org/grpc/credentials"
2024-11-12 22:59:37 +09:00
import "google.golang.org/grpc/peer"
import "google.golang.org/grpc/stats"
2025-01-28 00:44:02 +09:00
import "github.com/prometheus/client_golang/prometheus"
import "github.com/prometheus/client_golang/prometheus/promhttp"
2024-11-12 22:59:37 +09:00
2024-12-08 12:13:36 +09:00
const PTS_LIMIT int = 16384
2024-12-08 17:25:59 +09:00
const CTS_LIMIT int = 16384
2024-11-12 22:59:37 +09:00
2024-12-16 15:19:01 +09:00
type PortId uint16
2024-12-26 00:20:44 +09:00
const PORT_ID_MARKER string = "_"
2025-01-15 01:19:50 +09:00
const HS_ID_CTL string = "ctl"
2025-08-19 20:20:18 +09:00
const HS_ID_RPX string = "rpx"
const HS_ID_PXY string = "pxy"
2025-01-15 01:19:50 +09:00
const HS_ID_WPX string = "wpx"
2024-12-16 15:19:01 +09:00
2025-03-07 13:41:44 +09:00
type ServerConnMapByAddr map [ net . Addr ] * ServerConn
type ServerConnMapByClientToken map [ string ] * ServerConn
type ServerConnMap map [ ConnId ] * ServerConn
type ServerRouteMap map [ RouteId ] * ServerRoute
2025-08-24 19:00:26 +09:00
type ServerRouteMapByPtcName map [ string ] * ServerRoute
2025-03-07 13:41:44 +09:00
type ServerPeerConnMap map [ PeerId ] * ServerPeerConn
type ServerSvcPortMap map [ PortId ] ConnRouteId
2024-12-09 22:41:23 +09:00
2025-08-12 02:50:10 +09:00
type ServerRptyMap map [ uint64 ] * ServerRpty
type ServerRptyMapByWs map [ * websocket . Conn ] * ServerRpty
2025-08-19 20:20:18 +09:00
type ServerRpxMap map [ uint64 ] * ServerRpx
2025-08-12 02:50:10 +09:00
2025-01-11 11:48:19 +09:00
type ServerWpxResponseTransformer func ( r * ServerRouteProxyInfo , resp * http . Response ) io . Reader
2025-01-12 17:30:13 +09:00
type ServerWpxForeignPortProxyMaker func ( wpx_type string , port_id string ) ( * ServerRouteProxyInfo , error )
2024-12-27 14:43:44 +09:00
2025-02-18 14:44:45 +09:00
type ServerConnNoticeHandler interface {
Handle ( cts * ServerConn , text string )
}
2025-01-28 02:35:11 +09:00
type ServerConfig struct {
RpcAddrs [ ] string
RpcTls * tls . Config
RpcMaxConns int
MaxPeers int
CtlAddrs [ ] string
CtlTls * tls . Config
CtlPrefix string
2025-02-01 00:06:05 +09:00
CtlAuth * HttpAuthConfig
2025-02-10 14:48:18 +09:00
CtlCors bool
2025-01-28 02:35:11 +09:00
2025-08-12 16:29:44 +09:00
RpxAddrs [ ] string
RpxTls * tls . Config
2025-08-19 20:20:18 +09:00
RpxClientTokenAttrName string
RpxClientTokenRegex * regexp . Regexp
RpxClientTokenSubmatchIndex int
2025-08-12 16:29:44 +09:00
2025-01-28 02:35:11 +09:00
PxyAddrs [ ] string
PxyTls * tls . Config
2025-08-24 14:36:10 +09:00
PxyTargetTls * tls . Config
2025-01-28 02:35:11 +09:00
WpxAddrs [ ] string
WpxTls * tls . Config
2025-01-28 01:29:21 +09:00
}
2025-03-10 19:56:14 +09:00
type ServerEventKind int
const (
2025-03-28 17:03:17 +09:00
SERVER_EVENT_CONN_STARTED = iota
2025-03-14 22:51:23 +09:00
SERVER_EVENT_CONN_UPDATED
2025-03-28 17:03:17 +09:00
SERVER_EVENT_CONN_STOPPED
SERVER_EVENT_ROUTE_STARTED
2025-03-14 22:51:23 +09:00
SERVER_EVENT_ROUTE_UPDATED
2025-03-28 17:03:17 +09:00
SERVER_EVENT_ROUTE_STOPPED
SERVER_EVENT_PEER_STARTED
2025-03-14 22:51:23 +09:00
SERVER_EVENT_PEER_UPDATED
2025-03-28 17:03:17 +09:00
SERVER_EVENT_PEER_STOPPED
2025-03-10 19:56:14 +09:00
)
2025-03-13 21:24:59 +09:00
type ServerEvent struct {
Kind ServerEventKind ` json:"type" `
Data interface { } ` json:"data" `
2025-03-10 19:56:14 +09:00
}
2025-03-12 12:08:56 +09:00
type ServerEventBulletin = Bulletin [ * ServerEvent ]
type ServerEventSubscription = BulletinSubscription [ * ServerEvent ]
2025-03-10 19:56:14 +09:00
2024-11-12 22:59:37 +09:00
type Server struct {
2025-01-28 00:44:02 +09:00
UnimplementedHoduServer
Named
2025-02-26 14:46:09 +09:00
Cfg * ServerConfig
2025-02-20 22:21:39 +09:00
Ctx context . Context
CtxCancel context . CancelFunc
2024-11-24 20:39:51 +09:00
2025-02-20 22:21:39 +09:00
wg sync . WaitGroup
stop_req atomic . Bool
stop_chan chan bool
2024-11-18 22:25:59 +09:00
2025-02-20 22:21:39 +09:00
ext_mtx sync . Mutex
ext_svcs [ ] Service
2025-03-11 21:12:05 +09:00
ext_closed bool
2024-12-03 11:52:46 +09:00
2025-08-12 16:29:44 +09:00
rpx_mux * http . ServeMux
rpx [ ] * http . Server // proxy server
rpx_addrs_mtx sync . Mutex
rpx_addrs * list . List // of net.Addr
2025-02-20 22:21:39 +09:00
pxy_mux * http . ServeMux
pxy [ ] * http . Server // proxy server
2025-03-31 23:40:45 +09:00
pxy_addrs_mtx sync . Mutex
pxy_addrs * list . List // of net.Addr
2024-12-12 21:09:16 +09:00
2025-02-20 22:21:39 +09:00
wpx_mux * http . ServeMux
wpx [ ] * http . Server // proxy server than handles http/https only
2025-03-31 23:40:45 +09:00
wpx_addrs_mtx sync . Mutex
wpx_addrs * list . List // of net.Addr
2024-12-26 00:20:44 +09:00
2025-02-20 22:21:39 +09:00
ctl_mux * http . ServeMux
ctl [ ] * http . Server // control server
2025-03-31 23:40:45 +09:00
ctl_addrs_mtx sync . Mutex
ctl_addrs * list . List // of net.Addr
2024-11-21 01:11:01 +09:00
2025-02-20 22:21:39 +09:00
rpc [ ] * net . TCPListener // main listener for grpc
rpc_wg sync . WaitGroup
rpc_svr * grpc . Server
2024-11-12 22:59:37 +09:00
2025-02-20 22:21:39 +09:00
cts_limit int
cts_next_id ConnId
cts_mtx sync . Mutex
cts_map ServerConnMap
cts_map_by_addr ServerConnMapByAddr
2025-02-22 10:08:57 +09:00
cts_map_by_token ServerConnMapByClientToken
2025-02-20 22:21:39 +09:00
cts_wg sync . WaitGroup
2024-11-12 22:59:37 +09:00
2025-03-19 00:24:42 +09:00
pts_limit int // global pts limit
pts_mtx sync . Mutex
pts_list * list . List
2025-03-22 14:01:42 +09:00
route_mtx sync . Mutex
route_list * list . List
2025-02-20 22:21:39 +09:00
log Logger
2025-03-21 12:53:16 +09:00
conn_notice_handlers [ ] ServerConnNoticeHandler
2024-11-20 00:31:14 +09:00
2025-02-20 22:21:39 +09:00
svc_port_mtx sync . Mutex
svc_port_map ServerSvcPortMap
2024-12-16 15:19:01 +09:00
2025-03-10 19:56:14 +09:00
bulletin * ServerEventBulletin
2025-01-28 00:44:02 +09:00
promreg * prometheus . Registry
2024-12-08 12:13:36 +09:00
stats struct {
conns atomic . Int64
routes atomic . Int64
peers atomic . Int64
2024-12-16 01:03:03 +09:00
ssh_proxy_sessions atomic . Int64
2025-08-08 19:24:52 +09:00
pty_sessions atomic . Int64
2025-08-19 20:20:18 +09:00
rpty_sessions atomic . Int64
2025-08-19 22:23:22 +09:00
rpx_sessions atomic . Int64
2024-12-08 12:13:36 +09:00
}
2024-12-28 18:48:29 +09:00
wpx_resp_tf ServerWpxResponseTransformer
2025-01-11 11:48:19 +09:00
wpx_foreign_port_proxy_maker ServerWpxForeignPortProxyMaker
2025-06-23 21:09:24 +09:00
2025-08-08 19:24:52 +09:00
pty_user string
pty_shell string
2025-06-23 21:09:24 +09:00
xterm_html string
2024-11-12 22:59:37 +09:00
}
2024-11-25 19:46:18 +09:00
// connection from client.
2024-11-12 22:59:37 +09:00
// client connect to the server, the server accept it, and makes a tunnel request
2024-11-25 19:46:18 +09:00
type ServerConn struct {
2025-02-18 14:44:45 +09:00
S * Server
2024-12-28 18:35:14 +09:00
Id ConnId
2025-02-19 17:17:53 +09:00
Sid string // for logging
2025-03-14 22:51:23 +09:00
Created time . Time
2025-03-13 21:24:59 +09:00
ClientToken Atom [ string ] // provided by client
2024-12-03 11:52:46 +09:00
2025-02-18 14:44:45 +09:00
RemoteAddr net . Addr // client address that created this structure
LocalAddr net . Addr // local address that the client is connected to
2024-12-13 23:19:26 +09:00
pss * GuardedPacketStreamServer
2024-11-12 22:59:37 +09:00
2024-12-13 23:19:26 +09:00
route_mtx sync . Mutex
route_map ServerRouteMap
2025-08-24 19:00:26 +09:00
route_map_by_ptc_name ServerRouteMapByPtcName
2024-12-13 23:19:26 +09:00
route_wg sync . WaitGroup
2024-11-12 22:59:37 +09:00
2025-03-23 13:22:53 +09:00
pts_mtx sync . Mutex
pts_list * list . List
2025-08-12 02:50:10 +09:00
rpty_next_id uint64
2025-08-10 17:23:01 +09:00
rpty_mtx sync . Mutex
2025-08-12 02:50:10 +09:00
rpty_map ServerRptyMap
rpty_map_by_ws ServerRptyMapByWs
2025-08-10 17:23:01 +09:00
2025-08-19 20:20:18 +09:00
rpx_next_id uint64
rpx_mtx sync . Mutex
rpx_map ServerRpxMap
2025-08-12 02:50:10 +09:00
wg sync . WaitGroup
stop_req atomic . Bool
stop_chan chan bool
2024-11-12 22:59:37 +09:00
}
type ServerRoute struct {
2024-12-28 18:35:14 +09:00
Cts * ServerConn
Id RouteId
2025-03-14 22:51:23 +09:00
Created time . Time
2024-12-28 18:35:14 +09:00
2025-03-22 14:01:42 +09:00
node_in_server * list . Element
2024-12-12 21:09:16 +09:00
svc_l * net . TCPListener
2024-12-28 18:35:14 +09:00
SvcAddr * net . TCPAddr // actual listening address
SvcReqAddr string
SvcPermNet netip . Prefix // network from which access is allowed
SvcOption RouteOption
2024-12-05 18:24:42 +09:00
2024-12-28 18:35:14 +09:00
PtcAddr string
PtcName string
2024-11-12 22:59:37 +09:00
pts_mtx sync . Mutex
pts_map ServerPeerConnMap
pts_limit int
2024-12-13 23:19:26 +09:00
pts_next_id PeerId
2024-11-20 00:31:14 +09:00
pts_wg sync . WaitGroup
stop_req atomic . Bool
2024-11-18 22:25:59 +09:00
}
2025-08-10 17:23:01 +09:00
type ServerRpty struct {
id uint64
ws * websocket . Conn
}
2025-08-19 20:20:18 +09:00
type ServerRpx struct {
id uint64
pr * io . PipeReader
pw * io . PipeWriter
br io . ReadCloser // body reader
start_chan chan [ ] byte
done_chan chan bool
br_chan chan bool
resp_status_code int
resp_error error
resp_done_chan chan bool
}
2024-11-18 22:25:59 +09:00
type GuardedPacketStreamServer struct {
mtx sync . Mutex
//pss Hodu_PacketStreamServer
Hodu_PacketStreamServer // let's embed it to avoid reimplement Recv() and Context()
2024-11-12 22:59:37 +09:00
}
// ------------------------------------
2024-11-18 22:25:59 +09:00
func ( g * GuardedPacketStreamServer ) Send ( data * Packet ) error {
// while Recv() on a stream is called from the same gorountine all the time,
// Send() is called from multiple places. let's guard it as grpc-go
// doesn't provide concurrency safety in this case.
// https://github.com/grpc/grpc-go/blob/master/Documentation/concurrency.md
g . mtx . Lock ( )
defer g . mtx . Unlock ( )
return g . Hodu_PacketStreamServer . Send ( data )
}
/ *
func ( g * GuardedPacketStreamServer ) Recv ( ) ( * Packet , error ) {
return g . pss . Recv ( )
}
func ( g * GuardedPacketStreamServer ) Context ( ) context . Context {
return g . pss . Context ( )
} * /
// ------------------------------------
2025-08-19 20:20:18 +09:00
func ( rpty * ServerRpty ) ReqStop ( ) {
rpty . ws . Close ( )
}
2025-08-20 02:04:06 +09:00
func ( rpx * ServerRpx ) ReqStop ( close_web bool ) {
2025-08-19 20:20:18 +09:00
rpx . done_chan <- true
rpx . pw . Close ( )
2025-08-20 02:04:06 +09:00
if close_web { rpx . br . Close ( ) }
2025-08-19 20:20:18 +09:00
}
// ------------------------------------
2024-12-14 02:39:27 +09:00
func NewServerRoute ( cts * ServerConn , id RouteId , option RouteOption , ptc_addr string , ptc_name string , svc_requested_addr string , svc_permitted_net string ) ( * ServerRoute , error ) {
2024-11-18 22:25:59 +09:00
var r ServerRoute
var l * net . TCPListener
2024-12-03 11:52:46 +09:00
var svcaddr * net . TCPAddr
2024-12-05 18:24:42 +09:00
var svcnet netip . Prefix
2024-11-18 22:25:59 +09:00
var err error
2024-12-05 18:24:42 +09:00
if svc_permitted_net != "" {
2024-12-12 21:09:16 +09:00
// parse the permitted network before creating a listener.
// the listener opened doesn't have to be closed when parsing fails.
2024-12-05 18:24:42 +09:00
svcnet , err = netip . ParsePrefix ( svc_permitted_net )
if err != nil {
return nil , err
}
}
2024-12-12 21:09:16 +09:00
l , svcaddr , err = cts . make_route_listener ( id , option , svc_requested_addr )
2025-01-14 16:47:14 +09:00
if err != nil { return nil , err }
2024-11-18 22:25:59 +09:00
2024-12-05 18:24:42 +09:00
if svc_permitted_net == "" {
if svcaddr . IP . To4 ( ) != nil {
2024-12-12 21:09:16 +09:00
svcnet = IPV4_PREFIX_ZERO
2024-12-05 18:24:42 +09:00
} else {
2024-12-12 21:09:16 +09:00
svcnet = IPV6_PREFIX_ZERO
2024-12-05 18:24:42 +09:00
}
}
2024-12-28 18:35:14 +09:00
r . Cts = cts
r . Id = id
2025-03-14 22:51:23 +09:00
r . Created = time . Now ( )
2024-12-12 21:09:16 +09:00
r . svc_l = l
2024-12-28 18:35:14 +09:00
r . SvcAddr = svcaddr
r . SvcReqAddr = svc_requested_addr
r . SvcPermNet = svcnet
r . SvcOption = option
2024-12-05 18:24:42 +09:00
2024-12-28 18:35:14 +09:00
r . PtcAddr = ptc_addr
r . PtcName = ptc_name
2024-11-18 22:25:59 +09:00
r . pts_limit = PTS_LIMIT
r . pts_map = make ( ServerPeerConnMap )
2025-01-07 23:59:39 +09:00
r . pts_next_id = 1
2024-11-18 22:25:59 +09:00
r . stop_req . Store ( false )
2024-11-23 14:49:04 +09:00
return & r , nil
2024-11-18 22:25:59 +09:00
}
2024-11-12 22:59:37 +09:00
func ( r * ServerRoute ) AddNewServerPeerConn ( c * net . TCPConn ) ( * ServerPeerConn , error ) {
var pts * ServerPeerConn
var ok bool
2024-12-09 22:41:23 +09:00
var start_id PeerId
2025-01-07 23:59:39 +09:00
var assigned_id PeerId
2024-11-12 22:59:37 +09:00
r . pts_mtx . Lock ( )
defer r . pts_mtx . Unlock ( )
if len ( r . pts_map ) >= r . pts_limit {
return nil , fmt . Errorf ( "peer-to-server connection table full" )
}
2024-12-13 23:19:26 +09:00
start_id = r . pts_next_id
2024-11-12 22:59:37 +09:00
for {
2024-12-13 23:19:26 +09:00
_ , ok = r . pts_map [ r . pts_next_id ]
2024-11-12 22:59:37 +09:00
if ! ok {
2025-01-07 23:59:39 +09:00
assigned_id = r . pts_next_id
r . pts_next_id ++
if r . pts_next_id == 0 { r . pts_next_id ++ }
2024-11-12 22:59:37 +09:00
break
}
2024-12-13 23:19:26 +09:00
r . pts_next_id ++
2025-01-07 23:59:39 +09:00
if r . pts_next_id == 0 { r . pts_next_id ++ }
2024-12-13 23:19:26 +09:00
if r . pts_next_id == start_id {
2024-11-12 22:59:37 +09:00
// unlikely to happen but it cycled through the whole range.
return nil , fmt . Errorf ( "failed to assign peer-to-server connection id" )
}
}
2025-01-07 23:59:39 +09:00
pts = NewServerPeerConn ( r , c , assigned_id )
2024-11-12 22:59:37 +09:00
r . pts_map [ pts . conn_id ] = pts
2025-02-18 14:44:45 +09:00
r . Cts . S . stats . peers . Add ( 1 )
2024-11-12 22:59:37 +09:00
2025-03-19 00:24:42 +09:00
r . Cts . S . pts_mtx . Lock ( )
pts . node_in_server = r . Cts . S . pts_list . PushBack ( pts )
r . Cts . S . pts_mtx . Unlock ( )
2025-03-23 13:22:53 +09:00
r . Cts . pts_mtx . Lock ( )
pts . node_in_conn = r . Cts . pts_list . PushBack ( pts )
r . Cts . pts_mtx . Unlock ( )
2024-11-12 22:59:37 +09:00
return pts , nil
}
func ( r * ServerRoute ) RemoveServerPeerConn ( pts * ServerPeerConn ) {
r . pts_mtx . Lock ( )
delete ( r . pts_map , pts . conn_id )
2025-02-18 14:44:45 +09:00
r . Cts . S . stats . peers . Add ( - 1 )
2024-11-12 22:59:37 +09:00
r . pts_mtx . Unlock ( )
2025-02-19 17:17:53 +09:00
r . Cts . S . log . Write ( r . Cts . Sid , LOG_DEBUG , "Removed server-side peer connection %s from route(%d)" , pts . conn . RemoteAddr ( ) . String ( ) , r . Id )
2024-11-12 22:59:37 +09:00
}
2024-11-18 22:25:59 +09:00
func ( r * ServerRoute ) RunTask ( wg * sync . WaitGroup ) {
2024-11-12 22:59:37 +09:00
var err error
var conn * net . TCPConn
var pts * ServerPeerConn
2024-12-05 18:24:42 +09:00
var raddr * net . TCPAddr
var iaddr netip . Addr
2024-11-12 22:59:37 +09:00
2024-11-18 22:25:59 +09:00
defer wg . Done ( )
2025-03-28 17:03:17 +09:00
r . Cts . S . FireRouteEvent ( SERVER_EVENT_ROUTE_STARTED , r )
2024-11-18 22:25:59 +09:00
2024-11-12 22:59:37 +09:00
for {
2024-12-12 21:09:16 +09:00
conn , err = r . svc_l . AcceptTCP ( ) // this call is blocking...
2024-11-12 22:59:37 +09:00
if err != nil {
2024-11-17 14:57:56 +09:00
if errors . Is ( err , net . ErrClosed ) {
2025-02-19 17:17:53 +09:00
r . Cts . S . log . Write ( r . Cts . Sid , LOG_INFO , "Server-side peer listener closed on route(%d)" , r . Id )
2024-11-17 14:57:56 +09:00
} else {
2025-02-19 17:17:53 +09:00
r . Cts . S . log . Write ( r . Cts . Sid , LOG_INFO , "Server-side peer listener error on route(%d) - %s" , r . Id , err . Error ( ) )
2024-11-17 14:57:56 +09:00
}
2024-11-12 22:59:37 +09:00
break
}
2024-12-05 18:24:42 +09:00
raddr = conn . RemoteAddr ( ) . ( * net . TCPAddr )
iaddr , _ = netip . AddrFromSlice ( raddr . IP )
2024-12-28 18:35:14 +09:00
if ! r . SvcPermNet . Contains ( iaddr ) {
2025-02-19 17:17:53 +09:00
r . Cts . S . log . Write ( r . Cts . Sid , LOG_DEBUG , "Rejected server-side peer %s to route(%d) - allowed range %v" , raddr . String ( ) , r . Id , r . SvcPermNet )
2024-12-05 18:24:42 +09:00
conn . Close ( )
}
2025-02-18 14:44:45 +09:00
if r . Cts . S . pts_limit > 0 && int ( r . Cts . S . stats . peers . Load ( ) ) >= r . Cts . S . pts_limit {
2025-02-19 17:17:53 +09:00
r . Cts . S . log . Write ( r . Cts . Sid , LOG_DEBUG , "Rejected server-side peer %s to route(%d) - allowed max %d" , raddr . String ( ) , r . Id , r . Cts . S . pts_limit )
2024-12-10 14:37:14 +09:00
conn . Close ( )
}
2024-11-12 22:59:37 +09:00
pts , err = r . AddNewServerPeerConn ( conn )
if err != nil {
2025-02-19 17:17:53 +09:00
r . Cts . S . log . Write ( r . Cts . Sid , LOG_ERROR , "Failed to add server-side peer %s to route(%d) - %s" , r . Id , raddr . String ( ) , r . Id , err . Error ( ) )
2024-11-12 22:59:37 +09:00
conn . Close ( )
} else {
2025-02-19 17:17:53 +09:00
r . Cts . S . log . Write ( r . Cts . Sid , LOG_DEBUG , "Added server-side peer %s to route(%d)" , raddr . String ( ) , r . Id )
2024-11-12 22:59:37 +09:00
r . pts_wg . Add ( 1 )
2024-11-18 22:25:59 +09:00
go pts . RunTask ( & r . pts_wg )
2024-11-12 22:59:37 +09:00
}
}
2024-11-13 23:14:43 +09:00
2024-11-24 20:39:51 +09:00
r . ReqStop ( )
2024-11-18 22:25:59 +09:00
r . pts_wg . Wait ( )
2025-02-19 17:17:53 +09:00
r . Cts . S . log . Write ( r . Cts . Sid , LOG_DEBUG , "All service-side peer handlers ended on route(%d)" , r . Id )
2024-11-24 20:39:51 +09:00
2024-12-28 18:35:14 +09:00
r . Cts . RemoveServerRoute ( r ) // final phase...
2025-03-13 21:24:59 +09:00
2025-03-22 14:01:42 +09:00
r . Cts . S . route_mtx . Lock ( )
r . Cts . S . route_list . Remove ( r . node_in_server )
r . node_in_server = nil
r . Cts . S . route_mtx . Unlock ( )
2025-03-28 17:03:17 +09:00
r . Cts . S . FireRouteEvent ( SERVER_EVENT_ROUTE_STOPPED , r )
2024-11-12 22:59:37 +09:00
}
2024-11-18 22:25:59 +09:00
func ( r * ServerRoute ) ReqStop ( ) {
if r . stop_req . CompareAndSwap ( false , true ) {
var pts * ServerPeerConn
2024-12-26 00:20:44 +09:00
r . pts_mtx . Lock ( )
for _ , pts = range r . pts_map { pts . ReqStop ( ) }
r . pts_mtx . Unlock ( )
2024-11-18 22:25:59 +09:00
2024-12-12 21:09:16 +09:00
r . svc_l . Close ( )
2024-11-18 22:25:59 +09:00
}
2024-11-12 22:59:37 +09:00
}
2025-02-15 19:44:48 +09:00
func ( r * ServerRoute ) ReqStopAllServerPeerConns ( ) {
var c * ServerPeerConn
r . pts_mtx . Lock ( )
for _ , c = range r . pts_map { c . ReqStop ( ) }
r . pts_mtx . Unlock ( )
}
func ( r * ServerRoute ) FindServerPeerConnById ( peer_id PeerId ) * ServerPeerConn {
var c * ServerPeerConn
var ok bool
r . pts_mtx . Lock ( )
defer r . pts_mtx . Unlock ( )
c , ok = r . pts_map [ peer_id ]
if ! ok { return nil }
return c
}
2025-03-08 11:34:05 +09:00
func ( r * ServerRoute ) ReportPacket ( pts_id PeerId , packet_type PACKET_KIND , event_data interface { } ) error {
2024-11-12 22:59:37 +09:00
var spc * ServerPeerConn
var ok bool
r . pts_mtx . Lock ( )
spc , ok = r . pts_map [ pts_id ]
if ! ok {
2024-11-23 14:49:04 +09:00
r . pts_mtx . Unlock ( )
2024-12-05 18:24:42 +09:00
return fmt . Errorf ( "non-existent peer id - %d" , pts_id )
2024-11-12 22:59:37 +09:00
}
2024-11-23 14:49:04 +09:00
r . pts_mtx . Unlock ( )
2024-11-12 22:59:37 +09:00
2025-03-08 11:34:05 +09:00
return spc . ReportPacket ( packet_type , event_data )
2024-11-12 22:59:37 +09:00
}
2025-08-10 17:23:01 +09:00
// ------------------------------------
2024-12-12 21:09:16 +09:00
func ( cts * ServerConn ) make_route_listener ( id RouteId , option RouteOption , svc_requested_addr string ) ( * net . TCPListener , * net . TCPAddr , error ) {
2024-11-12 22:59:37 +09:00
var l * net . TCPListener
2024-12-03 11:52:46 +09:00
var svcaddr * net . TCPAddr
2024-11-12 22:59:37 +09:00
var nw string
2024-12-16 15:19:01 +09:00
var prev_cri ConnRouteId
var ok bool
2025-03-31 23:40:45 +09:00
var is4 bool
2024-12-12 21:09:16 +09:00
var err error
if svc_requested_addr != "" {
var ap netip . AddrPort
2024-11-12 22:59:37 +09:00
2024-12-12 21:09:16 +09:00
ap , err = netip . ParseAddrPort ( svc_requested_addr )
if err != nil {
return nil , nil , fmt . Errorf ( "invalid service address %s - %s" , svc_requested_addr , err . Error ( ) )
}
2025-03-31 23:40:45 +09:00
if ( ap . Addr ( ) . Is4 ( ) ) { is4 = true }
2024-12-12 21:09:16 +09:00
svcaddr = & net . TCPAddr { IP : ap . Addr ( ) . AsSlice ( ) , Port : int ( ap . Port ( ) ) }
}
if option & RouteOption ( ROUTE_OPTION_TCP ) != 0 {
2025-03-31 23:40:45 +09:00
// go seems to use ipv4 for 0.0.0.0:XXX if ipv6 is enabled.
// i don't want the behavior.. I force tcp4 if the ip address given
// is ipv4 address
2025-01-14 16:47:14 +09:00
nw = "tcp"
2025-03-31 23:40:45 +09:00
if ( is4 ) { nw = "tcp4" }
2025-01-14 16:47:14 +09:00
if svcaddr == nil {
// port 0 for automatic assignment.
svcaddr = & net . TCPAddr { Port : 0 }
}
2024-12-12 21:09:16 +09:00
} else if option & RouteOption ( ROUTE_OPTION_TCP4 ) != 0 {
2025-01-14 16:47:14 +09:00
nw = "tcp4"
if svcaddr == nil {
svcaddr = & net . TCPAddr { IP : net . IPv4zero , Port : 0 }
}
2024-12-12 21:09:16 +09:00
} else if option & RouteOption ( ROUTE_OPTION_TCP6 ) != 0 {
2025-01-14 16:47:14 +09:00
nw = "tcp6"
if svcaddr == nil {
svcaddr = & net . TCPAddr { IP : net . IPv6zero , Port : 0 }
}
2024-12-12 21:09:16 +09:00
} else {
2025-01-16 01:26:58 +09:00
return nil , nil , fmt . Errorf ( "invalid route option value %d(%s)" , option , option . String ( ) )
2024-12-12 21:09:16 +09:00
}
2024-11-12 22:59:37 +09:00
2024-12-12 21:09:16 +09:00
l , err = net . ListenTCP ( nw , svcaddr ) // make the binding address configurable. support multiple binding addresses???
2025-01-14 16:47:14 +09:00
if err != nil { return nil , nil , err }
2024-11-12 22:59:37 +09:00
2024-12-12 21:09:16 +09:00
svcaddr = l . Addr ( ) . ( * net . TCPAddr )
2024-12-16 15:19:01 +09:00
2025-01-14 16:47:14 +09:00
// uniqueness by port id can be checked after listener creation,
// especially when automatic assignment is requested.
2025-02-18 14:44:45 +09:00
cts . S . svc_port_mtx . Lock ( )
prev_cri , ok = cts . S . svc_port_map [ PortId ( svcaddr . Port ) ]
2024-12-16 15:19:01 +09:00
if ok {
2025-02-18 14:44:45 +09:00
cts . S . svc_port_mtx . Unlock ( )
2025-02-19 17:17:53 +09:00
cts . S . log . Write ( cts . Sid , LOG_ERROR ,
2024-12-16 15:19:01 +09:00
"Route(%d,%d) on %s not unique by port number - existing route(%d,%d)" ,
2025-03-31 23:40:45 +09:00
cts . Id , id , svcaddr . String ( ) , prev_cri . conn_id , prev_cri . route_id )
2024-12-16 15:19:01 +09:00
l . Close ( )
2025-03-31 23:40:45 +09:00
return nil , nil , fmt . Errorf ( "port not unique" )
2024-12-16 15:19:01 +09:00
}
2025-02-18 14:44:45 +09:00
cts . S . svc_port_map [ PortId ( svcaddr . Port ) ] = ConnRouteId { conn_id : cts . Id , route_id : id }
cts . S . svc_port_mtx . Unlock ( )
2024-12-16 15:19:01 +09:00
2025-02-19 17:17:53 +09:00
cts . S . log . Write ( cts . Sid , LOG_DEBUG , "Route(%d,%d) listening on %s" , cts . Id , id , svcaddr . String ( ) )
2024-12-12 21:09:16 +09:00
return l , svcaddr , nil
2024-11-12 22:59:37 +09:00
}
2024-12-14 02:39:27 +09:00
func ( cts * ServerConn ) AddNewServerRoute ( route_id RouteId , proto RouteOption , ptc_addr string , ptc_name string , svc_requested_addr string , svc_permitted_net string ) ( * ServerRoute , error ) {
2024-11-12 22:59:37 +09:00
var r * ServerRoute
2025-08-24 19:00:26 +09:00
var ok bool
2024-11-12 22:59:37 +09:00
var err error
cts . route_mtx . Lock ( )
2025-08-24 19:00:26 +09:00
_ , ok = cts . route_map [ route_id ]
if ok {
2024-12-10 13:15:05 +09:00
// If this happens, something must be wrong between the server and the client
// most likely, it must be a logic error. the state must not go out of sync
// as the route_id and the peer_id are supposed to be the same between the client
// and the server.
2024-11-12 22:59:37 +09:00
cts . route_mtx . Unlock ( )
2024-11-25 19:46:18 +09:00
return nil , fmt . Errorf ( "existent route id - %d" , route_id )
2024-11-12 22:59:37 +09:00
}
2025-08-24 19:00:26 +09:00
if ptc_name != "" {
// ptc name can be empty. but if not empty, it must be unique
_ , ok = cts . route_map_by_ptc_name [ ptc_name ]
if ok {
cts . route_mtx . Unlock ( )
return nil , fmt . Errorf ( "existent ptc name %s for route %d" , ptc_name , route_id )
}
}
2024-12-14 02:39:27 +09:00
r , err = NewServerRoute ( cts , route_id , proto , ptc_addr , ptc_name , svc_requested_addr , svc_permitted_net )
2024-11-12 22:59:37 +09:00
if err != nil {
cts . route_mtx . Unlock ( )
return nil , err
}
2025-08-24 19:00:26 +09:00
2024-11-23 14:49:04 +09:00
cts . route_map [ route_id ] = r
2025-08-24 19:00:26 +09:00
if ptc_name != "" {
cts . route_map_by_ptc_name [ ptc_name ] = r
}
2025-02-18 14:44:45 +09:00
cts . S . stats . routes . Add ( 1 )
2024-11-12 22:59:37 +09:00
cts . route_mtx . Unlock ( )
2025-03-22 14:01:42 +09:00
cts . S . route_mtx . Lock ( )
r . node_in_server = cts . S . route_list . PushBack ( r )
cts . S . route_mtx . Unlock ( )
2025-03-13 21:24:59 +09:00
// Don't detached the cts task as a go-routine as this function
2024-11-18 22:25:59 +09:00
cts . route_wg . Add ( 1 )
go r . RunTask ( & cts . route_wg )
2024-11-12 22:59:37 +09:00
return r , nil
}
2024-11-25 19:46:18 +09:00
func ( cts * ServerConn ) RemoveServerRoute ( route * ServerRoute ) error {
2024-11-24 20:39:51 +09:00
var r * ServerRoute
var ok bool
cts . route_mtx . Lock ( )
2024-12-28 18:35:14 +09:00
r , ok = cts . route_map [ route . Id ]
2024-11-25 19:46:18 +09:00
if ! ok {
2024-11-24 20:39:51 +09:00
cts . route_mtx . Unlock ( )
2024-12-28 18:35:14 +09:00
return fmt . Errorf ( "non-existent route id - %d" , route . Id )
2024-11-24 20:39:51 +09:00
}
2024-11-25 19:46:18 +09:00
if r != route {
2024-11-24 20:39:51 +09:00
cts . route_mtx . Unlock ( )
2024-12-28 18:35:14 +09:00
return fmt . Errorf ( "non-existent route - %d" , route . Id )
2024-11-24 20:39:51 +09:00
}
2025-08-24 19:00:26 +09:00
if route . PtcName != "" {
delete ( cts . route_map_by_ptc_name , route . PtcName )
}
2024-12-28 18:35:14 +09:00
delete ( cts . route_map , route . Id )
2025-02-18 14:44:45 +09:00
cts . S . stats . routes . Add ( - 1 )
2024-11-24 20:39:51 +09:00
cts . route_mtx . Unlock ( )
2025-02-18 14:44:45 +09:00
cts . S . svc_port_mtx . Lock ( )
delete ( cts . S . svc_port_map , PortId ( route . SvcAddr . Port ) )
cts . S . svc_port_mtx . Unlock ( )
2024-12-16 15:19:01 +09:00
2024-11-24 20:39:51 +09:00
r . ReqStop ( )
return nil
}
2024-12-09 22:41:23 +09:00
func ( cts * ServerConn ) RemoveServerRouteById ( route_id RouteId ) ( * ServerRoute , error ) {
2024-11-12 22:59:37 +09:00
var r * ServerRoute
var ok bool
cts . route_mtx . Lock ( )
2024-11-18 22:25:59 +09:00
r , ok = cts . route_map [ route_id ]
2024-11-25 19:46:18 +09:00
if ! ok {
2024-11-12 22:59:37 +09:00
cts . route_mtx . Unlock ( )
2024-11-25 19:46:18 +09:00
return nil , fmt . Errorf ( "non-existent route id - %d" , route_id )
2024-11-12 22:59:37 +09:00
}
2025-08-24 19:00:26 +09:00
if r . PtcName != "" {
delete ( cts . route_map_by_ptc_name , r . PtcName )
}
2024-11-18 22:25:59 +09:00
delete ( cts . route_map , route_id )
2025-02-18 14:44:45 +09:00
cts . S . stats . routes . Add ( - 1 )
2024-11-12 22:59:37 +09:00
cts . route_mtx . Unlock ( )
2025-02-18 14:44:45 +09:00
cts . S . svc_port_mtx . Lock ( )
delete ( cts . S . svc_port_map , PortId ( r . SvcAddr . Port ) )
cts . S . svc_port_mtx . Unlock ( )
2024-12-16 15:19:01 +09:00
2024-11-24 20:39:51 +09:00
r . ReqStop ( )
return r , nil
2024-11-12 22:59:37 +09:00
}
2024-12-09 22:41:23 +09:00
func ( cts * ServerConn ) FindServerRouteById ( route_id RouteId ) * ServerRoute {
2024-12-08 23:16:43 +09:00
var r * ServerRoute
var ok bool
cts . route_mtx . Lock ( )
r , ok = cts . route_map [ route_id ]
if ! ok {
cts . route_mtx . Unlock ( )
return nil
}
cts . route_mtx . Unlock ( )
return r
}
2025-08-24 19:00:26 +09:00
func ( cts * ServerConn ) FindServerRouteByPtcName ( ptc_name string ) * ServerRoute {
var r * ServerRoute
var ok bool
cts . route_mtx . Lock ( )
r , ok = cts . route_map_by_ptc_name [ ptc_name ]
if ! ok {
cts . route_mtx . Unlock ( )
return nil
}
cts . route_mtx . Unlock ( )
return r
}
2025-02-15 19:44:48 +09:00
func ( cts * ServerConn ) FindServerPeerConnById ( route_id RouteId , peer_id PeerId ) * ServerPeerConn {
var r * ServerRoute
var ok bool
cts . route_mtx . Lock ( )
defer cts . route_mtx . Unlock ( )
r , ok = cts . route_map [ route_id ]
if ! ok { return nil }
return r . FindServerPeerConnById ( peer_id )
}
2024-12-08 23:16:43 +09:00
func ( cts * ServerConn ) ReqStopAllServerRoutes ( ) {
var r * ServerRoute
cts . route_mtx . Lock ( )
2024-12-26 00:20:44 +09:00
for _ , r = range cts . route_map { r . ReqStop ( ) }
cts . route_mtx . Unlock ( )
2024-12-08 23:16:43 +09:00
}
2025-08-19 20:20:18 +09:00
// Rpty
2025-08-10 17:23:01 +09:00
func ( cts * ServerConn ) StartRpty ( ws * websocket . Conn ) ( * ServerRpty , error ) {
var ok bool
var start_id uint64
var assigned_id uint64
var rpty * ServerRpty
var err error
cts . rpty_mtx . Lock ( )
start_id = cts . rpty_next_id
for {
_ , ok = cts . rpty_map [ cts . rpty_next_id ]
if ! ok {
assigned_id = cts . rpty_next_id
cts . rpty_next_id ++
if cts . rpty_next_id == 0 { cts . rpty_next_id ++ }
break
}
cts . rpty_next_id ++
if cts . rpty_next_id == 0 { cts . rpty_next_id ++ }
if cts . rpty_next_id == start_id {
cts . rpty_mtx . Unlock ( )
return nil , fmt . Errorf ( "unable to assign id" )
}
}
_ , ok = cts . rpty_map_by_ws [ ws ]
if ok {
cts . rpty_mtx . Unlock ( )
return nil , fmt . Errorf ( "connection already associated with rpty. possibly internal error" )
}
rpty = & ServerRpty {
id : assigned_id ,
ws : ws ,
}
cts . rpty_map [ assigned_id ] = rpty
cts . rpty_map_by_ws [ ws ] = rpty
cts . rpty_mtx . Unlock ( )
err = cts . pss . Send ( MakeRptyStartPacket ( assigned_id ) )
if err != nil {
cts . rpty_mtx . Lock ( )
delete ( cts . rpty_map , assigned_id )
delete ( cts . rpty_map_by_ws , ws )
cts . rpty_mtx . Unlock ( )
return nil , err
}
2025-08-19 20:20:18 +09:00
cts . S . stats . rpty_sessions . Add ( 1 )
2025-08-10 17:23:01 +09:00
return rpty , nil
2025-08-08 19:24:52 +09:00
}
2025-08-12 02:50:10 +09:00
func ( cts * ServerConn ) StopRpty ( ws * websocket . Conn ) error {
// called by the websocket handler.
var rpty * ServerRpty
var id uint64
var ok bool
var err error
cts . rpty_mtx . Lock ( )
rpty , ok = cts . rpty_map_by_ws [ ws ]
if ! ok {
2025-08-19 20:20:18 +09:00
cts . rpty_mtx . Unlock ( )
cts . S . log . Write ( cts . Sid , LOG_ERROR , "Unknown websocket connection for rpty - websocket %v" , ws . RemoteAddr ( ) )
return fmt . Errorf ( "unknown websocket connection for rpty - %v" , ws . RemoteAddr ( ) )
2025-08-12 02:50:10 +09:00
}
id = rpty . id
cts . rpty_mtx . Unlock ( )
// send the stop request to the client side
err = cts . pss . Send ( MakeRptyStopPacket ( id , "" ) )
if err != nil {
2025-08-29 10:44:05 +09:00
cts . S . log . Write ( cts . Sid , LOG_ERROR , "Failed to send %s(%d) for server %s websocket %v - %s" , PACKET_KIND_RPTY_STOP . String ( ) , id , cts . RemoteAddr , ws . RemoteAddr ( ) , err . Error ( ) )
2025-08-19 20:20:18 +09:00
// carry on
2025-08-12 02:50:10 +09:00
}
2025-08-19 20:20:18 +09:00
// delete the rpty entry from the maps as the websocket
// handler is ending
cts . rpty_mtx . Lock ( )
delete ( cts . rpty_map , id )
delete ( cts . rpty_map_by_ws , ws )
cts . rpty_mtx . Unlock ( )
cts . S . stats . rpty_sessions . Add ( - 1 )
cts . S . log . Write ( cts . Sid , LOG_INFO , "Stopped rpty(%d) for server %s websocket %vs" , id , cts . RemoteAddr , ws . RemoteAddr ( ) )
2025-08-12 02:50:10 +09:00
return nil
}
func ( cts * ServerConn ) StopRptyWsById ( id uint64 , msg string ) error {
2025-08-19 20:20:18 +09:00
// call this when the stop requested comes from the client.
2025-08-12 02:50:10 +09:00
// abort the websocket side.
var rpty * ServerRpty
var ok bool
cts . rpty_mtx . Lock ( )
rpty , ok = cts . rpty_map [ id ]
if ! ok {
2025-08-19 20:20:18 +09:00
cts . rpty_mtx . Unlock ( )
2025-08-12 02:50:10 +09:00
return fmt . Errorf ( "unknown rpty id %d" , id )
}
cts . rpty_mtx . Unlock ( )
2025-08-19 20:20:18 +09:00
rpty . ReqStop ( )
2025-08-12 02:50:10 +09:00
cts . S . log . Write ( cts . Sid , LOG_INFO , "Stopped rpty(%d) for %s - %s" , id , cts . RemoteAddr , msg )
return nil
}
func ( cts * ServerConn ) WriteRpty ( ws * websocket . Conn , data [ ] byte ) error {
var rpty * ServerRpty
var id uint64
var ok bool
var err error
cts . rpty_mtx . Lock ( )
rpty , ok = cts . rpty_map_by_ws [ ws ]
if ! ok {
2025-08-19 20:20:18 +09:00
cts . rpty_mtx . Unlock ( )
2025-08-12 02:50:10 +09:00
return fmt . Errorf ( "unknown ws connection for rpty - %v" , ws . RemoteAddr ( ) )
}
id = rpty . id
cts . rpty_mtx . Unlock ( )
err = cts . pss . Send ( MakeRptyDataPacket ( id , data ) )
if err != nil {
return fmt . Errorf ( "unable to send rpty data to client - %s" , err . Error ( ) )
}
return nil
}
func ( cts * ServerConn ) WriteRptySize ( ws * websocket . Conn , data [ ] byte ) error {
var rpty * ServerRpty
var id uint64
var ok bool
var err error
cts . rpty_mtx . Lock ( )
rpty , ok = cts . rpty_map_by_ws [ ws ]
if ! ok {
2025-08-19 20:20:18 +09:00
cts . rpty_mtx . Unlock ( )
2025-08-12 02:50:10 +09:00
return fmt . Errorf ( "unknown ws connection for rpty size - %v" , ws . RemoteAddr ( ) )
}
id = rpty . id
cts . rpty_mtx . Unlock ( )
err = cts . pss . Send ( MakeRptySizePacket ( id , data ) )
if err != nil {
return fmt . Errorf ( "unable to send rpty size to client - %s" , err . Error ( ) )
}
return nil
}
func ( cts * ServerConn ) ReadRptyAndWriteWs ( id uint64 , data [ ] byte ) error {
var ok bool
var rpty * ServerRpty
var err error
cts . rpty_mtx . Lock ( )
rpty , ok = cts . rpty_map [ id ]
if ! ok {
cts . rpty_mtx . Unlock ( )
return fmt . Errorf ( "unknown rpty id - %d" , id )
}
2025-08-19 20:20:18 +09:00
cts . rpty_mtx . Unlock ( )
2025-08-12 02:50:10 +09:00
err = send_ws_data_for_xterm ( rpty . ws , "iov" , string ( data ) )
if err != nil {
return fmt . Errorf ( "failed to write rpty data(%d) to ws - %s" , id , err . Error ( ) )
}
return nil
}
2025-08-19 20:20:18 +09:00
func ( cts * ServerConn ) HandleRptyEvent ( packet_type PACKET_KIND , evt * RptyEvent ) error {
switch packet_type {
case PACKET_KIND_RPTY_STOP :
// stop requested from the server
return cts . StopRptyWsById ( evt . Id , string ( evt . Data ) )
case PACKET_KIND_RPTY_DATA :
return cts . ReadRptyAndWriteWs ( evt . Id , evt . Data )
}
// ignore other packet types
return nil
}
// Rpx
func ( cts * ServerConn ) StartRpxWebById ( srpx * ServerRpx , id uint64 , data [ ] byte ) error {
// pass the initial response to code in server-rpx.go
srpx . start_chan <- data
return nil
}
func ( cts * ServerConn ) StopRpxWebById ( srpx * ServerRpx , id uint64 ) error {
2025-08-20 02:04:06 +09:00
cts . S . log . Write ( cts . Sid , LOG_DEBUG , "Requesting to stop rpx(%d)" , srpx . id )
srpx . ReqStop ( true )
cts . S . log . Write ( cts . Sid , LOG_DEBUG , "Requested to stop rpx(%d)" , srpx . id )
2025-08-19 20:20:18 +09:00
return nil
}
func ( cts * ServerConn ) WroteRpxWebById ( srpx * ServerRpx , id uint64 , data [ ] byte ) error {
var err error
_ , err = srpx . pw . Write ( data )
if err != nil {
cts . S . log . Write ( cts . Sid , LOG_ERROR , "Failed to write rpx data(%d) to rpx pipe - %s" , id , err . Error ( ) )
2025-08-20 02:04:06 +09:00
srpx . ReqStop ( true )
2025-08-19 20:20:18 +09:00
}
return err
}
func ( cts * ServerConn ) EofRpxWebById ( srpx * ServerRpx , id uint64 ) error {
2025-08-20 02:04:06 +09:00
srpx . ReqStop ( false )
2025-08-19 20:20:18 +09:00
return nil
}
func ( cts * ServerConn ) HandleRpxEvent ( packet_type PACKET_KIND , evt * RpxEvent ) error {
var ok bool
var rpx * ServerRpx
cts . rpx_mtx . Lock ( )
rpx , ok = cts . rpx_map [ evt . Id ]
if ! ok {
cts . rpx_mtx . Unlock ( )
return fmt . Errorf ( "unknown rpx id - %v" , evt . Id )
}
cts . rpx_mtx . Unlock ( )
switch packet_type {
case PACKET_KIND_RPX_START :
return cts . StartRpxWebById ( rpx , evt . Id , evt . Data )
case PACKET_KIND_RPX_STOP :
// stop requested from the server
return cts . StopRpxWebById ( rpx , evt . Id )
case PACKET_KIND_RPX_EOF :
return cts . EofRpxWebById ( rpx , evt . Id )
case PACKET_KIND_RPX_DATA :
return cts . WroteRpxWebById ( rpx , evt . Id , evt . Data )
}
// ignore other packet types
return nil
}
2025-08-12 02:50:10 +09:00
2025-08-19 20:20:18 +09:00
// Rpx
2025-03-08 11:34:05 +09:00
func ( cts * ServerConn ) ReportPacket ( route_id RouteId , pts_id PeerId , packet_type PACKET_KIND , event_data interface { } ) error {
2024-11-12 22:59:37 +09:00
var r * ServerRoute
var ok bool
2024-11-13 23:14:43 +09:00
2024-11-12 22:59:37 +09:00
cts . route_mtx . Lock ( )
2024-11-18 22:25:59 +09:00
r , ok = cts . route_map [ route_id ]
2024-12-27 14:43:44 +09:00
if ! ok {
2024-11-12 22:59:37 +09:00
cts . route_mtx . Unlock ( )
2024-11-25 19:46:18 +09:00
return fmt . Errorf ( "non-existent route id - %d" , route_id )
2024-11-12 22:59:37 +09:00
}
cts . route_mtx . Unlock ( )
2024-11-13 23:14:43 +09:00
2025-03-08 11:34:05 +09:00
return r . ReportPacket ( pts_id , packet_type , event_data )
2024-11-12 22:59:37 +09:00
}
2024-11-25 19:46:18 +09:00
func ( cts * ServerConn ) receive_from_stream ( wg * sync . WaitGroup ) {
2024-11-12 22:59:37 +09:00
var pkt * Packet
var err error
2024-11-13 23:14:43 +09:00
2024-11-20 00:31:14 +09:00
defer wg . Done ( )
2025-03-13 22:59:38 +09:00
for cts . stop_req . Load ( ) == false {
2024-11-18 22:25:59 +09:00
pkt , err = cts . pss . Recv ( )
2024-11-17 14:57:56 +09:00
if errors . Is ( err , io . EOF ) {
2025-02-19 17:17:53 +09:00
cts . S . log . Write ( cts . Sid , LOG_INFO , "RPC stream closed for client %s" , cts . RemoteAddr )
2024-11-18 22:25:59 +09:00
goto done
2024-11-12 22:59:37 +09:00
}
if err != nil {
2025-02-19 17:17:53 +09:00
cts . S . log . Write ( cts . Sid , LOG_ERROR , "RPC stream error for client %s - %s" , cts . RemoteAddr , err . Error ( ) )
2024-11-18 22:25:59 +09:00
goto done
2024-11-12 22:59:37 +09:00
}
switch pkt . Kind {
case PACKET_KIND_ROUTE_START :
var x * Packet_Route
var ok bool
x , ok = pkt . U . ( * Packet_Route )
if ok {
2024-11-25 19:46:18 +09:00
var r * ServerRoute
2024-11-24 20:39:51 +09:00
2024-12-14 02:39:27 +09:00
r , err = cts . AddNewServerRoute ( RouteId ( x . Route . RouteId ) , RouteOption ( x . Route . ServiceOption ) , x . Route . TargetAddrStr , x . Route . TargetName , x . Route . ServiceAddrStr , x . Route . ServiceNetStr )
2024-11-12 22:59:37 +09:00
if err != nil {
2025-02-19 17:17:53 +09:00
cts . S . log . Write ( cts . Sid , LOG_ERROR ,
2024-12-05 23:05:47 +09:00
"Failed to add route(%d,%s) for %s - %s" ,
2024-12-28 18:35:14 +09:00
x . Route . RouteId , x . Route . TargetAddrStr , cts . RemoteAddr , err . Error ( ) )
2024-12-05 23:05:47 +09:00
2024-12-14 02:39:27 +09:00
err = cts . pss . Send ( MakeRouteStoppedPacket ( RouteId ( x . Route . RouteId ) , RouteOption ( x . Route . ServiceOption ) , x . Route . TargetAddrStr , x . Route . TargetName , x . Route . ServiceAddrStr , x . Route . ServiceNetStr ) )
2024-12-05 23:05:47 +09:00
if err != nil {
2025-02-19 17:17:53 +09:00
cts . S . log . Write ( cts . Sid , LOG_ERROR ,
2025-08-29 10:44:05 +09:00
"Failed to send %s event(%d,%s,%v,%s) to client %s - %s" ,
PACKET_KIND_ROUTE_STOPPED . String ( ) , x . Route . RouteId , x . Route . TargetAddrStr , x . Route . ServiceOption , x . Route . ServiceNetStr , cts . RemoteAddr , err . Error ( ) )
2024-12-05 23:05:47 +09:00
goto done
} else {
2025-02-19 17:17:53 +09:00
cts . S . log . Write ( cts . Sid , LOG_DEBUG ,
2025-08-29 10:44:05 +09:00
"Sent %s event(%d,%s,%v,%s) to client %s" ,
PACKET_KIND_ROUTE_STOPPED . String ( ) , x . Route . RouteId , x . Route . TargetAddrStr , x . Route . ServiceOption , x . Route . ServiceNetStr , cts . RemoteAddr )
2024-12-05 23:05:47 +09:00
}
2024-11-12 22:59:37 +09:00
} else {
2025-02-19 17:17:53 +09:00
cts . S . log . Write ( cts . Sid , LOG_INFO ,
2025-08-24 19:00:26 +09:00
"Added route(%d,%s,%s,%v,%v,%s) for client %s to cts(%d)" ,
r . Id , r . PtcAddr , r . SvcAddr . String ( ) , r . SvcOption , r . SvcPermNet , r . PtcName , cts . RemoteAddr , cts . Id )
2024-12-28 18:35:14 +09:00
err = cts . pss . Send ( MakeRouteStartedPacket ( r . Id , r . SvcOption , r . SvcAddr . String ( ) , r . PtcName , r . SvcReqAddr , r . SvcPermNet . String ( ) ) )
2024-11-12 22:59:37 +09:00
if err != nil {
2024-11-24 20:39:51 +09:00
r . ReqStop ( )
2025-02-19 17:17:53 +09:00
cts . S . log . Write ( cts . Sid , LOG_ERROR ,
2025-08-29 10:44:05 +09:00
"Failed to send %s event(%d,%s,%s,%s%v,%v) to client %s - %s" ,
PACKET_KIND_ROUTE_STARTED . String ( ) , r . Id , r . PtcAddr , r . SvcAddr . String ( ) , r . SvcOption , r . SvcPermNet , cts . RemoteAddr , err . Error ( ) )
2024-11-24 20:39:51 +09:00
goto done
2025-08-29 10:44:05 +09:00
} else {
cts . S . log . Write ( cts . Sid , LOG_DEBUG ,
"Sent %s event(%d,%s,%s,%s%v,%v) to client %s" ,
PACKET_KIND_ROUTE_STARTED . String ( ) , r . Id , r . PtcAddr , r . SvcAddr . String ( ) , r . SvcOption , r . SvcPermNet , cts . RemoteAddr )
2024-11-12 22:59:37 +09:00
}
}
} else {
2025-02-19 17:17:53 +09:00
cts . S . log . Write ( cts . Sid , LOG_INFO , "Received invalid packet from %s" , cts . RemoteAddr )
2024-11-24 20:39:51 +09:00
// TODO: need to abort this client?
2024-11-12 22:59:37 +09:00
}
case PACKET_KIND_ROUTE_STOP :
var x * Packet_Route
var ok bool
x , ok = pkt . U . ( * Packet_Route )
if ok {
2024-11-25 19:46:18 +09:00
var r * ServerRoute
2024-11-24 20:39:51 +09:00
2024-12-09 22:41:23 +09:00
r , err = cts . RemoveServerRouteById ( RouteId ( x . Route . RouteId ) )
2024-11-12 22:59:37 +09:00
if err != nil {
2025-02-19 17:17:53 +09:00
cts . S . log . Write ( cts . Sid , LOG_ERROR ,
2024-12-05 23:05:47 +09:00
"Failed to delete route(%d,%s) for client %s - %s" ,
2024-12-28 18:35:14 +09:00
x . Route . RouteId , x . Route . TargetAddrStr , cts . RemoteAddr , err . Error ( ) )
2024-11-12 22:59:37 +09:00
} else {
2025-02-19 17:17:53 +09:00
cts . S . log . Write ( cts . Sid , LOG_ERROR ,
2024-12-05 18:24:42 +09:00
"Deleted route(%d,%s,%s,%v,%v) for client %s" ,
2024-12-28 18:35:14 +09:00
r . Id , r . PtcAddr , r . SvcAddr . String ( ) , r . SvcOption , r . SvcPermNet . String ( ) , cts . RemoteAddr )
err = cts . pss . Send ( MakeRouteStoppedPacket ( r . Id , r . SvcOption , r . PtcAddr , r . PtcName , r . SvcReqAddr , r . SvcPermNet . String ( ) ) )
2024-11-12 22:59:37 +09:00
if err != nil {
2024-11-24 20:39:51 +09:00
r . ReqStop ( )
2025-02-19 17:17:53 +09:00
cts . S . log . Write ( cts . Sid , LOG_ERROR ,
2025-08-29 10:44:05 +09:00
"Failed to send %s event(%d,%s,%s,%v.%v) to client %s - %s" ,
PACKET_KIND_ROUTE_STOPPED . String ( ) , r . Id , r . PtcAddr , r . SvcAddr . String ( ) , r . SvcOption , r . SvcPermNet . String ( ) , cts . RemoteAddr , err . Error ( ) )
2024-11-24 20:39:51 +09:00
goto done
2025-08-29 10:44:05 +09:00
} else {
cts . S . log . Write ( cts . Sid , LOG_DEBUG ,
"Sent %s event(%d,%s,%s,%v.%v) to client %s" ,
PACKET_KIND_ROUTE_STOPPED . String ( ) , r . Id , r . PtcAddr , r . SvcAddr . String ( ) , r . SvcOption , r . SvcPermNet . String ( ) , cts . RemoteAddr )
2024-11-12 22:59:37 +09:00
}
}
} else {
2025-02-19 17:17:53 +09:00
cts . S . log . Write ( cts . Sid , LOG_ERROR , "Invalid route_stop event from %s" , cts . RemoteAddr )
2024-11-12 22:59:37 +09:00
}
2024-11-13 23:14:43 +09:00
2024-11-12 22:59:37 +09:00
case PACKET_KIND_PEER_STARTED :
2025-08-10 17:23:01 +09:00
fallthrough
2024-11-20 00:48:02 +09:00
case PACKET_KIND_PEER_ABORTED :
2025-08-10 17:23:01 +09:00
fallthrough
2024-11-12 22:59:37 +09:00
case PACKET_KIND_PEER_STOPPED :
// the connection from the client to a peer has been established
var x * Packet_Peer
var ok bool
x , ok = pkt . U . ( * Packet_Peer )
if ok {
2025-08-10 17:23:01 +09:00
err = cts . ReportPacket ( RouteId ( x . Peer . RouteId ) , PeerId ( x . Peer . PeerId ) , pkt . Kind , x . Peer )
2024-11-12 22:59:37 +09:00
if err != nil {
2025-02-19 17:17:53 +09:00
cts . S . log . Write ( cts . Sid , LOG_ERROR ,
2025-08-10 17:23:01 +09:00
"Failed to handle %s event from %s for peer(%d,%d,%s,%s) - %s" ,
pkt . Kind . String ( ) , cts . RemoteAddr , x . Peer . RouteId , x . Peer . PeerId , x . Peer . LocalAddrStr , x . Peer . RemoteAddrStr , err . Error ( ) )
2024-11-12 22:59:37 +09:00
} else {
2025-02-19 17:17:53 +09:00
cts . S . log . Write ( cts . Sid , LOG_DEBUG ,
2025-08-10 17:23:01 +09:00
"Handled %s event from %s for peer(%d,%d,%s,%s)" ,
pkt . Kind . String ( ) , cts . RemoteAddr , x . Peer . RouteId , x . Peer . PeerId , x . Peer . LocalAddrStr , x . Peer . RemoteAddrStr )
2024-11-12 22:59:37 +09:00
}
} else {
2024-12-03 20:28:04 +09:00
// invalid event data
2025-08-10 17:23:01 +09:00
cts . S . log . Write ( cts . Sid , LOG_ERROR , "Invalid %s event from %s" , pkt . Kind . String ( ) , cts . RemoteAddr )
2024-11-12 22:59:37 +09:00
}
case PACKET_KIND_PEER_DATA :
// the connection from the client to a peer has been established
var x * Packet_Data
var ok bool
x , ok = pkt . U . ( * Packet_Data )
if ok {
2025-08-10 17:23:01 +09:00
err = cts . ReportPacket ( RouteId ( x . Data . RouteId ) , PeerId ( x . Data . PeerId ) , pkt . Kind , x . Data . Data )
2024-11-12 22:59:37 +09:00
if err != nil {
2025-02-19 17:17:53 +09:00
cts . S . log . Write ( cts . Sid , LOG_ERROR ,
2025-08-10 17:23:01 +09:00
"Failed to handle %s event from %s for peer(%d,%d) - %s" ,
pkt . Kind . String ( ) , cts . RemoteAddr , x . Data . RouteId , x . Data . PeerId , err . Error ( ) )
2024-11-12 22:59:37 +09:00
} else {
2025-02-19 17:17:53 +09:00
cts . S . log . Write ( cts . Sid , LOG_DEBUG ,
2025-08-10 17:23:01 +09:00
"Handled %s event from %s for peer(%d,%d)" ,
pkt . Kind . String ( ) , cts . RemoteAddr , x . Data . RouteId , x . Data . PeerId )
2024-11-12 22:59:37 +09:00
}
} else {
2024-12-03 20:28:04 +09:00
// invalid event data
2025-08-10 17:23:01 +09:00
cts . S . log . Write ( cts . Sid , LOG_ERROR , "Invalid %s event from %s" , pkt . Kind . String ( ) , cts . RemoteAddr )
2024-11-12 22:59:37 +09:00
}
2025-02-18 01:02:26 +09:00
2025-02-20 00:59:00 +09:00
case PACKET_KIND_CONN_DESC :
var x * Packet_Conn
var ok bool
x , ok = pkt . U . ( * Packet_Conn )
if ok {
2025-02-20 22:21:39 +09:00
if x . Conn . Token == "" {
2025-08-12 02:50:10 +09:00
cts . S . log . Write ( cts . Sid , LOG_ERROR , "Invalid %s packet from %s - blank token" , pkt . Kind . String ( ) , cts . RemoteAddr )
2025-03-04 01:16:46 +09:00
cts . pss . Send ( MakeConnErrorPacket ( 1 , "blank token refused" ) )
2025-02-20 22:21:39 +09:00
cts . ReqStop ( ) // TODO: is this desirable to disconnect?
2025-03-13 21:24:59 +09:00
} else if x . Conn . Token != cts . ClientToken . Get ( ) {
2025-02-20 22:21:39 +09:00
_ , err = strconv . ParseUint ( x . Conn . Token , 10 , int ( unsafe . Sizeof ( ConnId ( 0 ) ) * 8 ) )
2025-02-22 10:08:57 +09:00
if err == nil { // this is not != nil. this is to check if the token is numeric
2025-08-12 02:50:10 +09:00
cts . S . log . Write ( cts . Sid , LOG_ERROR , "Invalid %s packet from %s - numeric token '%s'" , pkt . Kind . String ( ) , cts . RemoteAddr , x . Conn . Token )
2025-03-04 01:16:46 +09:00
cts . pss . Send ( MakeConnErrorPacket ( 1 , "numeric token refused" ) )
2025-02-20 22:21:39 +09:00
cts . ReqStop ( ) // TODO: is this desirable to disconnect?
} else {
cts . S . cts_mtx . Lock ( )
_ , ok = cts . S . cts_map_by_token [ x . Conn . Token ]
if ok {
// error
cts . S . cts_mtx . Unlock ( )
2025-08-12 02:50:10 +09:00
cts . S . log . Write ( cts . Sid , LOG_ERROR , "Invalid %s packet from %s - duplicate token '%s'" , pkt . Kind . String ( ) , cts . RemoteAddr , x . Conn . Token )
2025-03-05 00:44:00 +09:00
cts . pss . Send ( MakeConnErrorPacket ( 1 , fmt . Sprintf ( "duplicate token refused - %s" , x . Conn . Token ) ) )
2025-02-20 22:21:39 +09:00
cts . ReqStop ( ) // TODO: is this desirable to disconnect?
} else {
2025-03-13 21:24:59 +09:00
if cts . ClientToken . Get ( ) != "" { delete ( cts . S . cts_map_by_token , cts . ClientToken . Get ( ) ) }
cts . ClientToken . Set ( x . Conn . Token )
2025-02-20 22:21:39 +09:00
cts . S . cts_map_by_token [ x . Conn . Token ] = cts
cts . S . cts_mtx . Unlock ( )
cts . S . log . Write ( cts . Sid , LOG_INFO , "client(%d) %s - token set to '%s'" , cts . Id , cts . RemoteAddr , x . Conn . Token )
2025-03-18 23:45:00 +09:00
2025-03-28 17:03:17 +09:00
cts . S . FireConnEvent ( SERVER_EVENT_CONN_UPDATED , cts )
2025-02-20 22:21:39 +09:00
}
}
}
2025-02-20 00:59:00 +09:00
} else {
2025-08-12 02:50:10 +09:00
cts . S . log . Write ( cts . Sid , LOG_ERROR , "Invalid %s packet from %s" , pkt . Kind . String ( ) , cts . RemoteAddr )
2025-02-20 00:59:00 +09:00
}
2025-02-18 01:02:26 +09:00
case PACKET_KIND_CONN_NOTICE :
// the connection from the client to a peer has been established
2025-03-04 01:16:46 +09:00
var x * Packet_ConnNoti
2025-02-18 01:02:26 +09:00
var ok bool
2025-03-04 01:16:46 +09:00
x , ok = pkt . U . ( * Packet_ConnNoti )
2025-02-18 01:02:26 +09:00
if ok {
2025-03-21 12:53:16 +09:00
if cts . S . conn_notice_handlers != nil {
var handler ServerConnNoticeHandler
for _ , handler = range cts . S . conn_notice_handlers {
handler . Handle ( cts , x . ConnNoti . Text )
}
2025-02-18 14:44:45 +09:00
}
2025-02-18 01:02:26 +09:00
} else {
2025-08-12 02:50:10 +09:00
cts . S . log . Write ( cts . Sid , LOG_ERROR , "Invalid %s packet from %s" , pkt . Kind . String ( ) , cts . RemoteAddr )
2025-02-18 01:02:26 +09:00
}
2025-08-08 19:24:52 +09:00
2025-08-12 02:50:10 +09:00
//case PACKET_KIND_RPTY_START: stop is never sent by the client to the server
case PACKET_KIND_RPTY_STOP :
2025-08-10 17:23:01 +09:00
fallthrough
2025-08-08 19:24:52 +09:00
case PACKET_KIND_RPTY_DATA :
2025-08-12 02:50:10 +09:00
var x * Packet_RptyEvt
var ok bool
x , ok = pkt . U . ( * Packet_RptyEvt )
if ok {
err = cts . HandleRptyEvent ( pkt . Kind , x . RptyEvt )
if err != nil {
cts . S . log . Write ( cts . Sid , LOG_ERROR , "Failed to handle %s event for rpty(%d) from %s - %s" , pkt . Kind . String ( ) , x . RptyEvt . Id , cts . RemoteAddr , err . Error ( ) )
2025-08-19 20:20:18 +09:00
} else {
2025-08-20 02:04:06 +09:00
cts . S . log . Write ( cts . Sid , LOG_DEBUG , "Handled %s event for rpty(%d) from %s" , pkt . Kind . String ( ) , x . RptyEvt . Id , cts . RemoteAddr )
2025-08-19 20:20:18 +09:00
}
} else {
cts . S . log . Write ( cts . Sid , LOG_ERROR , "Invalid %s packet from %s" , pkt . Kind . String ( ) , cts . RemoteAddr )
}
2025-08-12 02:50:10 +09:00
2025-08-19 20:20:18 +09:00
case PACKET_KIND_RPX_START : // the client sends the response header using START
fallthrough
case PACKET_KIND_RPX_STOP :
fallthrough
case PACKET_KIND_RPX_EOF :
fallthrough
case PACKET_KIND_RPX_DATA :
var x * Packet_RpxEvt
var ok bool
x , ok = pkt . U . ( * Packet_RpxEvt )
if ok {
err = cts . HandleRpxEvent ( pkt . Kind , x . RpxEvt )
if err != nil {
cts . S . log . Write ( cts . Sid , LOG_ERROR , "Failed to handle %s event for rpx(%d) from %s - %s" , pkt . Kind . String ( ) , x . RpxEvt . Id , cts . RemoteAddr , err . Error ( ) )
} else {
2025-08-20 02:04:06 +09:00
cts . S . log . Write ( cts . Sid , LOG_DEBUG , "Handled %s event for rpx(%d) from %s" , pkt . Kind . String ( ) , x . RpxEvt . Id , cts . RemoteAddr )
2025-08-19 20:20:18 +09:00
}
2025-08-12 02:50:10 +09:00
} else {
cts . S . log . Write ( cts . Sid , LOG_ERROR , "Invalid %s packet from %s" , pkt . Kind . String ( ) , cts . RemoteAddr )
}
2024-11-12 22:59:37 +09:00
}
2024-11-20 00:31:14 +09:00
}
2024-11-18 22:25:59 +09:00
done :
2025-08-12 02:50:10 +09:00
// arrange to break all rpty resources
cts . rpty_mtx . Lock ( )
if len ( cts . rpty_map ) > 0 {
var rpty * ServerRpty
for _ , rpty = range cts . rpty_map {
2025-08-19 20:20:18 +09:00
rpty . ReqStop ( )
2025-08-12 02:50:10 +09:00
}
}
cts . rpty_mtx . Unlock ( )
2025-08-19 20:20:18 +09:00
// arrange to break all rpx resources
cts . rpx_mtx . Lock ( )
if len ( cts . rpx_map ) > 0 {
var rpx * ServerRpx
for _ , rpx = range cts . rpx_map {
2025-08-20 02:04:06 +09:00
rpx . ReqStop ( false )
2025-08-19 20:20:18 +09:00
}
}
cts . rpx_mtx . Unlock ( )
2025-02-19 17:17:53 +09:00
cts . S . log . Write ( cts . Sid , LOG_INFO , "RPC stream receiver ended" )
2024-11-18 22:25:59 +09:00
}
2024-11-25 19:46:18 +09:00
func ( cts * ServerConn ) RunTask ( wg * sync . WaitGroup ) {
2024-11-18 22:25:59 +09:00
var strm * GuardedPacketStreamServer
var ctx context . Context
defer wg . Done ( )
2025-03-28 17:03:17 +09:00
cts . S . FireConnEvent ( SERVER_EVENT_CONN_STARTED , cts )
2024-11-18 22:25:59 +09:00
strm = cts . pss
ctx = strm . Context ( )
2024-11-20 00:31:14 +09:00
// it looks like the only proper way to interrupt the blocking Recv
// call on the grpc streaming server is exit from the service handler
// which is this function invoked from PacketStream().
// there is no cancel function or whatever that can interrupt it.
// so start the Recv() loop in a separte goroutine and let this
// function be the channel waiter only.
// increment on the wait group is for the caller to wait for
// these detached goroutines to finish.
2025-03-13 22:59:38 +09:00
wg . Add ( 1 )
go cts . receive_from_stream ( wg )
// Add(1) to cts.route_wg is mostly performed inside a goroutine
// cts.receive_from_stream() invoked just above.
// this can potentially cause a race condition where cts.route_wg
// is still zero when Wait() is called far below.
// Increment the wait count here before the loop begins
2025-03-13 21:24:59 +09:00
cts . route_wg . Add ( 1 )
2024-11-18 22:25:59 +09:00
2025-05-12 19:08:54 +09:00
// start the loop inside a goroutine so that route_wg counter
2025-08-19 20:20:18 +09:00
// is likely to be greater than 1 when Wait() is called.
2025-05-12 19:08:54 +09:00
go func ( ) {
waiting_loop :
for {
// exit if context is done or continue
select {
case <- ctx . Done ( ) : // the stream context is done
cts . S . log . Write ( cts . Sid , LOG_INFO , "RPC stream done - %s" , ctx . Err ( ) . Error ( ) )
break waiting_loop
case <- cts . stop_chan :
// get out of the loop to eventually to exit from
// this handler to let the main grpc server to
// close this specific client connection.
break waiting_loop
//default:
// no other case is ready.
// without the default case, the select construct would block
}
2024-11-18 22:25:59 +09:00
}
2025-05-12 19:08:54 +09:00
cts . ReqStop ( ) // just in case
cts . route_wg . Done ( )
} ( )
2024-11-18 22:25:59 +09:00
cts . route_wg . Wait ( )
2025-03-28 17:03:17 +09:00
cts . S . FireConnEvent ( SERVER_EVENT_CONN_STOPPED , cts )
2025-03-10 19:56:14 +09:00
// Don't detached the cts task as a go-routine as this function
2025-03-05 00:44:00 +09:00
cts . S . log . Write ( cts . Sid , LOG_INFO , "End of connection task" )
2024-11-18 22:25:59 +09:00
}
2024-11-25 19:46:18 +09:00
func ( cts * ServerConn ) ReqStop ( ) {
2024-11-18 22:25:59 +09:00
if cts . stop_req . CompareAndSwap ( false , true ) {
var r * ServerRoute
2025-08-19 20:20:18 +09:00
var rpty * ServerRpty
var srpx * ServerRpx
2024-11-18 22:25:59 +09:00
2024-12-26 00:20:44 +09:00
cts . route_mtx . Lock ( )
for _ , r = range cts . route_map { r . ReqStop ( ) }
cts . route_mtx . Unlock ( )
2024-11-18 22:25:59 +09:00
2025-08-19 20:20:18 +09:00
cts . rpty_mtx . Lock ( )
for _ , rpty = range cts . rpty_map { rpty . ReqStop ( ) }
cts . rpty_mtx . Unlock ( )
cts . rpx_mtx . Lock ( )
2025-08-20 02:04:06 +09:00
for _ , srpx = range cts . rpx_map { srpx . ReqStop ( true ) }
2025-08-19 20:20:18 +09:00
cts . rpx_mtx . Unlock ( )
2024-11-24 20:39:51 +09:00
// there is no good way to break a specific connection client to
// the grpc server. while the global grpc server is closed in
// ReqStop() for Server, the individuation connection is closed
// by returing from the grpc handler goroutine. See the comment
2024-11-25 19:46:18 +09:00
// RunTask() for ServerConn.
2024-11-18 22:25:59 +09:00
cts . stop_chan <- true
}
}
// --------------------------------------------------------------------
2024-11-25 19:46:18 +09:00
func ( s * Server ) GetSeed ( ctx context . Context , c_seed * Seed ) ( * Seed , error ) {
2024-11-20 00:31:14 +09:00
var s_seed Seed
// seed exchange is for furture expansion of the protocol
// there is nothing to do much about it for now.
2024-12-07 21:24:06 +09:00
s_seed . Version = HODU_RPC_VERSION
2024-11-20 00:31:14 +09:00
s_seed . Flags = 0
2024-11-25 19:46:18 +09:00
// we create no ServerConn structure associated with the connection
2024-11-20 00:31:14 +09:00
// at this phase for the server. it doesn't track the client version and
// features. we delegate protocol selection solely to the client.
return & s_seed , nil
}
2024-11-18 22:25:59 +09:00
func ( s * Server ) PacketStream ( strm Hodu_PacketStreamServer ) error {
var ctx context . Context
var p * peer . Peer
var ok bool
var err error
2024-11-25 19:46:18 +09:00
var cts * ServerConn
2024-11-18 22:25:59 +09:00
ctx = strm . Context ( )
p , ok = peer . FromContext ( ctx )
2024-11-25 19:46:18 +09:00
if ! ok {
2024-11-18 22:25:59 +09:00
return fmt . Errorf ( "failed to get peer from packet stream context" )
}
2025-03-18 23:37:46 +09:00
if s . stop_req . Load ( ) {
2025-03-26 23:44:41 +09:00
// this check should still suffer race condition
// becuase this function itself runs as a goroutine and is fired
// from the grpc server code without synchronizing with hodu.
2025-03-18 23:37:46 +09:00
return fmt . Errorf ( "new conneciton prohibited after stop - %s" , p . Addr . String ( ) )
}
2024-12-02 02:19:50 +09:00
cts , err = s . AddNewServerConn ( & p . Addr , & p . LocalAddr , strm )
2024-11-18 22:25:59 +09:00
if err != nil {
return fmt . Errorf ( "unable to add client %s - %s" , p . Addr . String ( ) , err . Error ( ) )
2024-11-12 22:59:37 +09:00
}
2024-11-18 22:25:59 +09:00
2025-03-18 23:37:46 +09:00
// Don't detach the cts task as a go-routine as this function
2024-11-18 22:25:59 +09:00
// is invoked as a go-routine by the grpc server.
s . cts_wg . Add ( 1 )
cts . RunTask ( & s . cts_wg )
return nil
2024-11-12 22:59:37 +09:00
}
// ------------------------------------
type ConnCatcher struct {
server * Server
}
func ( cc * ConnCatcher ) TagRPC ( ctx context . Context , info * stats . RPCTagInfo ) context . Context {
2024-11-25 19:46:18 +09:00
return ctx
2024-11-12 22:59:37 +09:00
}
func ( cc * ConnCatcher ) HandleRPC ( ctx context . Context , s stats . RPCStats ) {
}
func ( cc * ConnCatcher ) TagConn ( ctx context . Context , info * stats . ConnTagInfo ) context . Context {
2024-11-23 14:49:04 +09:00
return ctx
2024-11-25 19:46:18 +09:00
//return context.TODO()
2024-11-12 22:59:37 +09:00
}
func ( cc * ConnCatcher ) HandleConn ( ctx context . Context , cs stats . ConnStats ) {
var p * peer . Peer
var ok bool
var addr string
p , ok = peer . FromContext ( ctx )
2024-11-25 19:46:18 +09:00
if ! ok {
2024-11-12 22:59:37 +09:00
addr = ""
} else {
addr = p . Addr . String ( )
}
2024-12-03 20:28:04 +09:00
/ *
md , ok := metadata . FromIncomingContext ( ctx )
if ok {
} * /
2024-11-12 22:59:37 +09:00
switch cs . ( type ) {
case * stats . ConnBegin :
2024-12-03 20:28:04 +09:00
cc . server . log . Write ( "" , LOG_INFO , "Client connected - %s" , addr )
2024-11-12 22:59:37 +09:00
case * stats . ConnEnd :
2025-01-07 23:59:39 +09:00
var cts * ServerConn
var log_id string
cts , _ = cc . server . RemoveServerConnByAddr ( p . Addr )
2025-02-19 17:17:53 +09:00
if cts != nil { log_id = cts . Sid }
2025-01-07 23:59:39 +09:00
cc . server . log . Write ( log_id , LOG_INFO , "Client disconnected - %s" , addr )
2024-11-25 19:46:18 +09:00
}
2024-11-12 22:59:37 +09:00
}
2025-03-07 13:41:44 +09:00
// ------------------------------------
func ( m ServerPeerConnMap ) get_sorted_keys ( ) [ ] PeerId {
var ks [ ] PeerId
var peer * ServerPeerConn
ks = make ( [ ] PeerId , 0 , len ( m ) )
for _ , peer = range m {
ks = append ( ks , peer . conn_id )
}
slices . Sort ( ks )
return ks
}
func ( m ServerRouteMap ) get_sorted_keys ( ) [ ] RouteId {
var ks [ ] RouteId
var route * ServerRoute
ks = make ( [ ] RouteId , 0 , len ( m ) )
for _ , route = range m {
ks = append ( ks , route . Id )
}
slices . Sort ( ks )
return ks
}
func ( m ServerConnMap ) get_sorted_keys ( ) [ ] ConnId {
var ks [ ] ConnId
var cts * ServerConn
ks = make ( [ ] ConnId , 0 , len ( m ) )
for _ , cts = range m {
ks = append ( ks , cts . Id )
}
slices . Sort ( ks )
return ks
}
2024-12-03 20:28:04 +09:00
// ------------------------------------
2024-11-12 22:59:37 +09:00
type wrappedStream struct {
grpc . ServerStream
}
2024-12-03 20:28:04 +09:00
func ( w * wrappedStream ) RecvMsg ( msg interface { } ) error {
return w . ServerStream . RecvMsg ( msg )
2024-11-12 22:59:37 +09:00
}
2024-12-03 20:28:04 +09:00
func ( w * wrappedStream ) SendMsg ( msg interface { } ) error {
return w . ServerStream . SendMsg ( msg )
2024-11-12 22:59:37 +09:00
}
func newWrappedStream ( s grpc . ServerStream ) grpc . ServerStream {
return & wrappedStream { s }
}
2024-12-03 20:28:04 +09:00
func streamInterceptor ( srv interface { } , ss grpc . ServerStream , info * grpc . StreamServerInfo , handler grpc . StreamHandler ) error {
var err error
2024-11-12 22:59:37 +09:00
// authentication (token verification)
/ *
2024-12-03 20:28:04 +09:00
md , ok = metadata . FromIncomingContext ( ss . Context ( ) )
2024-11-12 22:59:37 +09:00
if ! ok {
return errMissingMetadata
}
if ! valid ( md [ "authorization" ] ) {
return errInvalidToken
}
* /
2024-12-03 20:28:04 +09:00
err = handler ( srv , newWrappedStream ( ss ) )
2024-11-12 22:59:37 +09:00
if err != nil {
2024-12-03 20:28:04 +09:00
// TODO: LOGGING
2024-11-12 22:59:37 +09:00
}
return err
}
2024-12-03 20:28:04 +09:00
func unaryInterceptor ( ctx context . Context , req interface { } , info * grpc . UnaryServerInfo , handler grpc . UnaryHandler ) ( interface { } , error ) {
var v interface { }
var err error
2024-11-12 22:59:37 +09:00
// authentication (token verification)
/ *
2024-12-03 20:28:04 +09:00
md , ok = metadata . FromIncomingContext ( ctx )
2024-11-12 22:59:37 +09:00
if ! ok {
return nil , errMissingMetadata
}
if ! valid ( md [ "authorization" ] ) {
// return nil, errInvalidToken
}
* /
2024-12-03 20:28:04 +09:00
v , err = handler ( ctx , req )
2024-11-12 22:59:37 +09:00
if err != nil {
2024-12-03 20:28:04 +09:00
//fmt.Printf("RPC failed with error: %v\n", err)
// TODO: Logging?
2024-11-12 22:59:37 +09:00
}
2024-11-21 01:11:01 +09:00
2024-12-03 20:28:04 +09:00
return v , err
2024-11-12 22:59:37 +09:00
}
2024-12-07 22:18:07 +09:00
2024-12-12 21:09:16 +09:00
type server_http_log_writer struct {
2024-12-07 22:18:07 +09:00
svr * Server
2025-08-19 20:20:18 +09:00
depth int
2024-12-07 22:18:07 +09:00
}
2024-12-12 21:09:16 +09:00
func ( hlw * server_http_log_writer ) Write ( p [ ] byte ) ( n int , err error ) {
2024-12-07 22:18:07 +09:00
// the standard http.Server always requires *log.Logger
// use this iowriter to create a logger to pass it to the http server.
2024-12-15 15:07:35 +09:00
// since this is another log write wrapper, give adjustment value
2025-08-19 20:20:18 +09:00
hlw . svr . log . WriteWithCallDepth ( "" , LOG_INFO , hlw . depth , string ( p ) )
2024-12-07 22:18:07 +09:00
return len ( p ) , nil
}
2024-12-27 14:43:44 +09:00
type ServerHttpHandler interface {
2025-03-29 13:29:02 +09:00
Identity ( ) string
2025-02-10 14:48:18 +09:00
Cors ( req * http . Request ) bool
2025-01-31 19:54:28 +09:00
Authenticate ( req * http . Request ) ( int , string )
2025-03-12 12:08:56 +09:00
ServeHTTP ( w http . ResponseWriter , req * http . Request ) ( int , error )
}
type ServerWebsocketHandler interface {
2025-03-29 13:29:02 +09:00
Identity ( ) string
2025-03-12 12:08:56 +09:00
ServeWebsocket ( ws * websocket . Conn ) ( int , error )
2024-12-27 14:43:44 +09:00
}
2025-02-26 14:28:41 +09:00
func ( s * Server ) WrapHttpHandler ( handler ServerHttpHandler ) http . Handler {
2024-12-27 14:43:44 +09:00
return http . HandlerFunc ( func ( w http . ResponseWriter , req * http . Request ) {
var status_code int
var err error
var start_time time . Time
var time_taken time . Duration
2025-01-11 11:48:19 +09:00
// this deferred function is to overcome the recovering implemenation
// from panic done in go's http server. in that implemenation, panic
// is isolated to a single gorountine. however, i want this program
// to exit immediately once a panic condition is caught. (e.g. nil
// pointer dererence)
defer func ( ) {
var err interface { } = recover ( )
if err != nil { dump_call_frame_and_exit ( s . log , req , err ) }
} ( )
2024-12-27 14:43:44 +09:00
start_time = time . Now ( )
2025-02-10 14:48:18 +09:00
if handler . Cors ( req ) {
w . Header ( ) . Set ( "Access-Control-Allow-Origin" , "*" )
w . Header ( ) . Set ( "Access-Control-Allow-Headers" , "*" )
2025-02-17 00:52:29 +09:00
w . Header ( ) . Set ( "Access-Control-Allow-Methods" , "*" )
2025-02-10 14:48:18 +09:00
}
if req . Method == http . MethodOptions {
status_code = WriteEmptyRespHeader ( w , http . StatusOK )
2025-01-31 21:20:10 +09:00
} else {
2025-04-07 15:24:02 +09:00
var realm string
2025-02-10 14:48:18 +09:00
status_code , realm = handler . Authenticate ( req )
if status_code == http . StatusUnauthorized {
if realm != "" {
w . Header ( ) . Set ( "WWW-Authenticate" , fmt . Sprintf ( "Basic Realm=\"%s\"" , realm ) )
}
WriteEmptyRespHeader ( w , status_code )
} else if status_code == http . StatusOK {
status_code , err = handler . ServeHTTP ( w , req )
} else {
WriteEmptyRespHeader ( w , status_code )
}
2025-01-28 23:50:28 +09:00
}
2025-08-19 20:20:18 +09:00
time_taken = time . Since ( start_time ) // time.Now().Sub(start_time)
2024-12-27 14:43:44 +09:00
if status_code > 0 {
2025-02-22 10:08:57 +09:00
if err != nil {
2025-08-19 20:20:18 +09:00
s . log . Write ( handler . Identity ( ) , LOG_INFO , "[%s] %s %s %d %.9f - %s" , req . RemoteAddr , req . Method , req . RequestURI , status_code , time_taken . Seconds ( ) , err . Error ( ) )
2025-02-22 10:08:57 +09:00
} else {
2025-08-19 20:20:18 +09:00
s . log . Write ( handler . Identity ( ) , LOG_INFO , "[%s] %s %s %d %.9f" , req . RemoteAddr , req . Method , req . RequestURI , status_code , time_taken . Seconds ( ) )
2024-12-27 14:43:44 +09:00
}
}
} )
}
2025-06-21 22:01:24 +09:00
func ( s * Server ) SafeWrapWebsocketHandler ( handler websocket . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , req * http . Request ) {
if ! strings . EqualFold ( req . Header . Get ( "Upgrade" ) , "websocket" ) ||
! strings . Contains ( strings . ToLower ( req . Header . Get ( "Connection" ) ) , "upgrade" ) {
var status_code int
status_code = WriteEmptyRespHeader ( w , http . StatusBadRequest )
2025-08-19 20:20:18 +09:00
s . log . Write ( "" , LOG_INFO , "[%s] %s %s %d[non-websocket]" , req . RemoteAddr , req . Method , req . RequestURI , status_code )
2025-06-21 22:01:24 +09:00
return
}
handler . ServeHTTP ( w , req )
} )
}
2025-03-12 12:08:56 +09:00
func ( s * Server ) WrapWebsocketHandler ( handler ServerWebsocketHandler ) websocket . Handler {
return websocket . Handler ( func ( ws * websocket . Conn ) {
var status_code int
var err error
var start_time time . Time
var time_taken time . Duration
var req * http . Request
req = ws . Request ( )
2025-08-19 20:20:18 +09:00
s . log . Write ( handler . Identity ( ) , LOG_INFO , "[%s] %s %s [ws]" , req . RemoteAddr , req . Method , req . RequestURI )
2025-03-12 12:08:56 +09:00
2025-03-12 12:57:46 +09:00
start_time = time . Now ( )
2025-03-12 12:08:56 +09:00
status_code , err = handler . ServeWebsocket ( ws )
2025-08-19 20:20:18 +09:00
time_taken = time . Since ( start_time ) // time.Now().Sub(start_time)
2025-03-12 12:08:56 +09:00
if status_code > 0 {
if err != nil {
2025-08-19 20:20:18 +09:00
s . log . Write ( handler . Identity ( ) , LOG_INFO , "[%s] %s %s [ws] %d %.9f - %s" , req . RemoteAddr , req . Method , req . RequestURI , status_code , time_taken . Seconds ( ) , err . Error ( ) )
2025-03-12 12:08:56 +09:00
} else {
2025-08-19 20:20:18 +09:00
s . log . Write ( handler . Identity ( ) , LOG_INFO , "[%s] %s %s [ws] %d %.9f" , req . RemoteAddr , req . Method , req . RequestURI , status_code , time_taken . Seconds ( ) )
2025-03-12 12:08:56 +09:00
}
}
} )
}
2025-01-28 02:35:11 +09:00
func NewServer ( ctx context . Context , name string , logger Logger , cfg * ServerConfig ) ( * Server , error ) {
2024-11-12 22:59:37 +09:00
var s Server
var l * net . TCPListener
2024-12-03 11:52:46 +09:00
var rpcaddr * net . TCPAddr
2024-11-12 22:59:37 +09:00
var addr string
2024-12-03 11:52:46 +09:00
var i int
2024-12-07 22:18:07 +09:00
var hs_log * log . Logger
2024-12-08 13:34:47 +09:00
var opts [ ] grpc . ServerOption
2024-12-27 14:43:44 +09:00
var err error
2024-11-12 22:59:37 +09:00
2025-01-28 02:35:11 +09:00
if len ( cfg . RpcAddrs ) <= 0 {
2024-11-24 20:39:51 +09:00
return nil , fmt . Errorf ( "no server addresses provided" )
2024-11-12 22:59:37 +09:00
}
2025-02-19 10:38:23 +09:00
s . Ctx , s . CtxCancel = context . WithCancel ( ctx )
2025-01-28 00:44:02 +09:00
s . name = name
2024-11-21 01:11:01 +09:00
s . log = logger
2024-11-12 22:59:37 +09:00
/* create the specified number of listeners */
2024-12-03 11:52:46 +09:00
s . rpc = make ( [ ] * net . TCPListener , 0 )
2025-01-28 02:35:11 +09:00
for _ , addr = range cfg . RpcAddrs {
2025-01-15 01:19:50 +09:00
var addr_class string
2024-11-12 22:59:37 +09:00
2025-01-15 01:19:50 +09:00
addr_class = TcpAddrStrClass ( addr )
rpcaddr , err = net . ResolveTCPAddr ( addr_class , addr ) // Make this interruptable???
if err != nil { goto oops }
l , err = net . ListenTCP ( addr_class , rpcaddr )
if err != nil { goto oops }
2024-11-12 22:59:37 +09:00
2024-12-03 11:52:46 +09:00
s . rpc = append ( s . rpc , l )
2024-11-12 22:59:37 +09:00
}
2025-02-26 14:46:09 +09:00
s . Cfg = cfg
2024-11-23 12:30:23 +09:00
s . ext_svcs = make ( [ ] Service , 0 , 1 )
2025-03-22 14:01:42 +09:00
s . route_list = list . New ( )
2025-03-26 23:44:41 +09:00
s . pts_list = list . New ( )
s . pts_limit = cfg . MaxPeers
2025-01-28 02:35:11 +09:00
s . cts_limit = cfg . RpcMaxConns
2025-01-07 23:59:39 +09:00
s . cts_next_id = 1
2024-12-03 00:55:19 +09:00
s . cts_map = make ( ServerConnMap )
s . cts_map_by_addr = make ( ServerConnMapByAddr )
2025-02-22 10:08:57 +09:00
s . cts_map_by_token = make ( ServerConnMapByClientToken )
2024-12-16 15:19:01 +09:00
s . svc_port_map = make ( ServerSvcPortMap )
2024-11-24 20:39:51 +09:00
s . stop_chan = make ( chan bool , 8 )
2024-11-12 22:59:37 +09:00
s . stop_req . Store ( false )
2025-03-12 12:08:56 +09:00
s . bulletin = NewBulletin [ * ServerEvent ] ( & s , 1024 )
2024-12-08 13:34:47 +09:00
2025-03-31 23:40:45 +09:00
s . ctl_addrs = list . New ( )
2025-08-12 16:29:44 +09:00
s . rpx_addrs = list . New ( )
2025-03-31 23:40:45 +09:00
s . pxy_addrs = list . New ( )
s . wpx_addrs = list . New ( )
2024-12-08 13:34:47 +09:00
opts = append ( opts , grpc . StatsHandler ( & ConnCatcher { server : & s } ) )
2025-02-26 14:46:09 +09:00
if s . Cfg . RpcTls != nil { opts = append ( opts , grpc . Creds ( credentials . NewTLS ( s . Cfg . RpcTls ) ) ) }
2024-12-08 13:34:47 +09:00
//opts = append(opts, grpc.UnaryInterceptor(unaryInterceptor))
//opts = append(opts, grpc.StreamInterceptor(streamInterceptor))
s . rpc_svr = grpc . NewServer ( opts ... )
2024-12-03 20:28:04 +09:00
RegisterHoduServer ( s . rpc_svr , & s )
2024-11-12 22:59:37 +09:00
2024-12-12 21:09:16 +09:00
// ---------------------------------------------------------
2024-12-01 21:47:11 +09:00
2025-08-19 20:20:18 +09:00
hs_log = log . New ( & server_http_log_writer { svr : & s , depth : + 2 } , "" , 0 )
2024-12-12 21:09:16 +09:00
// ---------------------------------------------------------
2024-12-01 21:47:11 +09:00
s . ctl_mux = http . NewServeMux ( )
2024-12-12 21:09:16 +09:00
2025-02-26 14:46:09 +09:00
s . ctl_mux . Handle ( s . Cfg . CtlPrefix + "/_ctl/server-conns" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_ctl_server_conns { ServerCtl { S : & s , Id : HS_ID_CTL } } ) )
2025-02-26 14:46:09 +09:00
s . ctl_mux . Handle ( s . Cfg . CtlPrefix + "/_ctl/server-conns/{conn_id}" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_ctl_server_conns_id { ServerCtl { S : & s , Id : HS_ID_CTL } } ) )
2025-02-26 14:46:09 +09:00
s . ctl_mux . Handle ( s . Cfg . CtlPrefix + "/_ctl/server-conns/{conn_id}/routes" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_ctl_server_conns_id_routes { ServerCtl { S : & s , Id : HS_ID_CTL } } ) )
2025-02-26 14:46:09 +09:00
s . ctl_mux . Handle ( s . Cfg . CtlPrefix + "/_ctl/server-conns/{conn_id}/routes/{route_id}" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_ctl_server_conns_id_routes_id { ServerCtl { S : & s , Id : HS_ID_CTL } } ) )
2025-02-26 14:46:09 +09:00
s . ctl_mux . Handle ( s . Cfg . CtlPrefix + "/_ctl/server-conns/{conn_id}/routes/{route_id}/peers" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_ctl_server_conns_id_routes_id_peers { ServerCtl { S : & s , Id : HS_ID_CTL } } ) )
2025-02-26 14:46:09 +09:00
s . ctl_mux . Handle ( s . Cfg . CtlPrefix + "/_ctl/server-conns/{conn_id}/routes/{route_id}/peers/{peer_id}" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_ctl_server_conns_id_routes_id_peers_id { ServerCtl { S : & s , Id : HS_ID_CTL } } ) )
2025-03-23 13:22:53 +09:00
s . ctl_mux . Handle ( s . Cfg . CtlPrefix + "/_ctl/server-conns/{conn_id}/peers" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_ctl_server_conns_id_peers { ServerCtl { S : & s , Id : HS_ID_CTL } } ) )
2025-03-22 14:01:42 +09:00
s . ctl_mux . Handle ( s . Cfg . CtlPrefix + "/_ctl/server-routes" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_ctl_server_routes { ServerCtl { S : & s , Id : HS_ID_CTL } } ) )
2025-03-19 00:24:42 +09:00
s . ctl_mux . Handle ( s . Cfg . CtlPrefix + "/_ctl/server-peers" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_ctl_server_peers { ServerCtl { S : & s , Id : HS_ID_CTL } } ) )
2025-02-26 14:46:09 +09:00
s . ctl_mux . Handle ( s . Cfg . CtlPrefix + "/_ctl/notices" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_ctl_notices { ServerCtl { S : & s , Id : HS_ID_CTL } } ) )
2025-02-26 14:46:09 +09:00
s . ctl_mux . Handle ( s . Cfg . CtlPrefix + "/_ctl/notices/{conn_id}" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_ctl_notices_id { ServerCtl { S : & s , Id : HS_ID_CTL } } ) )
2025-02-26 14:46:09 +09:00
s . ctl_mux . Handle ( s . Cfg . CtlPrefix + "/_ctl/stats" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_ctl_stats { ServerCtl { S : & s , Id : HS_ID_CTL } } ) )
2025-02-26 14:46:09 +09:00
s . ctl_mux . Handle ( s . Cfg . CtlPrefix + "/_ctl/token" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_ctl_token { ServerCtl { S : & s , Id : HS_ID_CTL } } ) )
2024-12-01 21:47:11 +09:00
2025-01-28 00:44:02 +09:00
// TODO: make this optional. add this endpoint only if it's enabled...
s . promreg = prometheus . NewRegistry ( )
s . promreg . MustRegister ( prometheus . NewGoCollector ( ) )
s . promreg . MustRegister ( NewServerCollector ( & s ) )
2025-02-26 14:46:09 +09:00
s . ctl_mux . Handle ( s . Cfg . CtlPrefix + "/_ctl/metrics" ,
2025-01-28 00:44:02 +09:00
promhttp . HandlerFor ( s . promreg , promhttp . HandlerOpts { EnableOpenMetrics : true } ) )
2025-03-12 12:08:56 +09:00
s . ctl_mux . Handle ( "/_ctl/events" ,
2025-06-21 22:01:24 +09:00
s . SafeWrapWebsocketHandler ( s . WrapWebsocketHandler ( & server_ctl_ws { ServerCtl { S : & s , Id : HS_ID_CTL } } ) ) )
2025-03-29 13:29:02 +09:00
2025-08-08 19:24:52 +09:00
s . ctl_mux . Handle ( "/_pty/ws" ,
s . SafeWrapWebsocketHandler ( s . WrapWebsocketHandler ( & server_pty_ws { S : & s , Id : HS_ID_CTL } ) ) )
s . ctl_mux . Handle ( "/_pty/xterm.js" ,
s . WrapHttpHandler ( & server_pty_xterm_file { ServerCtl : ServerCtl { S : & s , Id : HS_ID_CTL } , file : "xterm.js" } ) )
s . ctl_mux . Handle ( "/_pty/xterm.js/" ,
s . WrapHttpHandler ( & server_pty_xterm_file { ServerCtl : ServerCtl { S : & s , Id : HS_ID_CTL } , file : "_forbidden" } ) )
s . ctl_mux . Handle ( "/_pty/xterm-addon-fit.js" ,
s . WrapHttpHandler ( & server_pty_xterm_file { ServerCtl : ServerCtl { S : & s , Id : HS_ID_CTL } , file : "xterm-addon-fit.js" } ) )
s . ctl_mux . Handle ( "/_pty/xterm-addon-fit.js/" ,
s . WrapHttpHandler ( & server_pty_xterm_file { ServerCtl : ServerCtl { S : & s , Id : HS_ID_CTL } , file : "_forbidden" } ) )
s . ctl_mux . Handle ( "/_pty/xterm.css" ,
s . WrapHttpHandler ( & server_pty_xterm_file { ServerCtl : ServerCtl { S : & s , Id : HS_ID_CTL } , file : "xterm.css" } ) )
s . ctl_mux . Handle ( "/_pty/xterm.css/" ,
s . WrapHttpHandler ( & server_pty_xterm_file { ServerCtl : ServerCtl { S : & s , Id : HS_ID_CTL } , file : "_forbidden" } ) )
s . ctl_mux . Handle ( "/_pty/xterm.html" ,
2025-08-12 02:50:10 +09:00
s . WrapHttpHandler ( & server_pty_xterm_file { ServerCtl : ServerCtl { S : & s , Id : HS_ID_CTL } , file : "xterm.html" , mode : "pty" } ) )
2025-08-08 19:24:52 +09:00
s . ctl_mux . Handle ( "/_pty/xterm.html/" ,
s . WrapHttpHandler ( & server_pty_xterm_file { ServerCtl : ServerCtl { S : & s , Id : HS_ID_CTL } , file : "_forbidden" } ) )
s . ctl_mux . Handle ( "/_pty/" ,
s . WrapHttpHandler ( & server_pty_xterm_file { ServerCtl : ServerCtl { S : & s , Id : HS_ID_CTL } , file : "_redir:xterm.html" } ) )
2025-08-12 02:50:10 +09:00
s . ctl_mux . Handle ( "/_rpty/ws" ,
s . SafeWrapWebsocketHandler ( s . WrapWebsocketHandler ( & server_rpty_ws { S : & s , Id : HS_ID_CTL } ) ) )
s . ctl_mux . Handle ( "/_rpty/xterm.js" ,
s . WrapHttpHandler ( & server_pty_xterm_file { ServerCtl : ServerCtl { S : & s , Id : HS_ID_CTL } , file : "xterm.js" } ) )
s . ctl_mux . Handle ( "/_rpty/xterm.js/" ,
s . WrapHttpHandler ( & server_pty_xterm_file { ServerCtl : ServerCtl { S : & s , Id : HS_ID_CTL } , file : "_forbidden" } ) )
s . ctl_mux . Handle ( "/_rpty/xterm-addon-fit.js" ,
s . WrapHttpHandler ( & server_pty_xterm_file { ServerCtl : ServerCtl { S : & s , Id : HS_ID_CTL } , file : "xterm-addon-fit.js" } ) )
s . ctl_mux . Handle ( "/_rpty/xterm-addon-fit.js/" ,
s . WrapHttpHandler ( & server_pty_xterm_file { ServerCtl : ServerCtl { S : & s , Id : HS_ID_CTL } , file : "_forbidden" } ) )
s . ctl_mux . Handle ( "/_rpty/xterm.css" ,
s . WrapHttpHandler ( & server_pty_xterm_file { ServerCtl : ServerCtl { S : & s , Id : HS_ID_CTL } , file : "xterm.css" } ) )
s . ctl_mux . Handle ( "/_rpty/xterm.css/" ,
s . WrapHttpHandler ( & server_pty_xterm_file { ServerCtl : ServerCtl { S : & s , Id : HS_ID_CTL } , file : "_forbidden" } ) )
s . ctl_mux . Handle ( "/_rpty/xterm.html" ,
s . WrapHttpHandler ( & server_pty_xterm_file { ServerCtl : ServerCtl { S : & s , Id : HS_ID_CTL } , file : "xterm.html" , mode : "rpty" } ) )
s . ctl_mux . Handle ( "/_rpty/xterm.html/" ,
s . WrapHttpHandler ( & server_pty_xterm_file { ServerCtl : ServerCtl { S : & s , Id : HS_ID_CTL } , file : "_forbidden" } ) )
s . ctl_mux . Handle ( "/_rpty/" ,
s . WrapHttpHandler ( & server_pty_xterm_file { ServerCtl : ServerCtl { S : & s , Id : HS_ID_CTL } , file : "_redir:xterm.html" } ) )
2025-03-12 12:08:56 +09:00
2025-01-28 02:35:11 +09:00
s . ctl = make ( [ ] * http . Server , len ( cfg . CtlAddrs ) )
for i = 0 ; i < len ( cfg . CtlAddrs ) ; i ++ {
2024-12-03 11:52:46 +09:00
s . ctl [ i ] = & http . Server {
2025-01-28 02:35:11 +09:00
Addr : cfg . CtlAddrs [ i ] ,
2024-12-03 11:52:46 +09:00
Handler : s . ctl_mux ,
2025-08-19 20:20:18 +09:00
// race condition issues without cloning. the http package modifies some fields in the configuration object
2025-08-19 22:23:22 +09:00
TLSConfig : cfg . CtlTls . Clone ( ) ,
2024-12-07 22:18:07 +09:00
ErrorLog : hs_log ,
2024-12-03 11:52:46 +09:00
// TODO: more settings
}
2024-12-01 21:47:11 +09:00
}
2024-12-12 21:09:16 +09:00
// ---------------------------------------------------------
2024-12-13 02:25:27 +09:00
2025-08-12 16:29:44 +09:00
s . rpx_mux = http . NewServeMux ( ) // TODO: make /_init,_ssh,_ssh/ws,_http configurable...
s . rpx_mux . Handle ( "/" , s . WrapHttpHandler ( & server_rpx { S : & s , Id : HS_ID_RPX } ) )
s . rpx = make ( [ ] * http . Server , len ( cfg . RpxAddrs ) )
for i = 0 ; i < len ( cfg . RpxAddrs ) ; i ++ {
s . rpx [ i ] = & http . Server {
Addr : cfg . RpxAddrs [ i ] ,
Handler : s . rpx_mux ,
2025-08-19 20:20:18 +09:00
TLSConfig : cfg . RpxTls . Clone ( ) ,
2025-08-12 16:29:44 +09:00
ErrorLog : hs_log ,
// TODO: more settings
}
}
// ---------------------------------------------------------
2025-06-21 02:43:28 +09:00
s . pxy_mux = http . NewServeMux ( ) // TODO: make /_init,_ssh,_ssh/ws,_http configurable...
2025-06-24 00:58:46 +09:00
2025-03-10 19:56:14 +09:00
s . pxy_mux . Handle ( "/_ssh/{conn_id}/" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_PXY } , file : "_redirect" } ) )
2025-06-24 00:58:46 +09:00
2025-03-10 19:56:14 +09:00
s . pxy_mux . Handle ( "/_ssh/{conn_id}/{route_id}/" ,
2025-07-30 18:23:23 +09:00
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_PXY } , file : "_redir:xterm.html" } ) )
2025-06-24 00:58:46 +09:00
s . pxy_mux . Handle ( "/_ssh/{conn_id}/{route_id}/xterm.html" ,
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_PXY } , file : "xterm.html" } ) )
2025-07-30 18:23:23 +09:00
s . pxy_mux . Handle ( "/_ssh/{conn_id}/{route_id}/xterm.html/" ,
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_PXY } , file : "_forbidden" } ) )
2025-06-24 00:58:46 +09:00
s . pxy_mux . Handle ( "/_ssh/{conn_id}/{route_id}/xterm.css" ,
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_PXY } , file : "xterm.css" } ) )
2025-07-30 18:23:23 +09:00
s . pxy_mux . Handle ( "/_ssh/{conn_id}/{route_id}/xterm.css/" ,
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_PXY } , file : "_forbidden" } ) )
2025-06-24 00:58:46 +09:00
s . pxy_mux . Handle ( "/_ssh/{conn_id}/{route_id}/xterm.js" ,
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_PXY } , file : "xterm.js" } ) )
2025-07-30 18:23:23 +09:00
s . pxy_mux . Handle ( "/_ssh/{conn_id}/{route_id}/xterm.js/" ,
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_PXY } , file : "_forbidden" } ) )
2025-06-24 00:58:46 +09:00
s . pxy_mux . Handle ( "/_ssh/{conn_id}/{route_id}/xterm-addon-fit.js" ,
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_PXY } , file : "xterm-addon-fit.js" } ) )
2025-07-30 18:23:23 +09:00
s . pxy_mux . Handle ( "/_ssh/{conn_id}/{route_id}/xterm-addon-fit.js/" ,
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_PXY } , file : "_forbidden" } ) )
2025-06-24 00:58:46 +09:00
s . pxy_mux . Handle ( "/_ssh/{conn_id}/{route_id}/ws" ,
s . SafeWrapWebsocketHandler ( s . WrapWebsocketHandler ( & server_pxy_ssh_ws { S : & s , Id : HS_ID_PXY } ) ) )
s . pxy_mux . Handle ( "/_ssh/{conn_id}/{route_id}/session-info" ,
s . WrapHttpHandler ( & server_ctl_server_conns_id_routes_id { ServerCtl { S : & s , Id : HS_ID_PXY , NoAuth : true } } ) )
2024-12-27 16:52:27 +09:00
s . pxy_mux . Handle ( "/_ssh/" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_PXY } , file : "_forbidden" } ) )
2025-01-13 14:25:09 +09:00
s . pxy_mux . Handle ( "/favicon.ico" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_PXY } , file : "_forbidden" } ) )
2025-02-14 12:45:54 +09:00
s . pxy_mux . Handle ( "/favicon.ico/" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_PXY } , file : "_forbidden" } ) )
2024-12-27 16:52:27 +09:00
s . pxy_mux . Handle ( "/_http/{conn_id}/{route_id}/{trailer...}" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_pxy_http_main { server_pxy : server_pxy { S : & s , Id : HS_ID_PXY } , prefix : "/_http" } ) )
2024-12-12 21:09:16 +09:00
2025-01-28 02:35:11 +09:00
s . pxy = make ( [ ] * http . Server , len ( cfg . PxyAddrs ) )
2024-12-12 21:09:16 +09:00
2025-01-28 02:35:11 +09:00
for i = 0 ; i < len ( cfg . PxyAddrs ) ; i ++ {
2024-12-12 21:09:16 +09:00
s . pxy [ i ] = & http . Server {
2025-01-28 02:35:11 +09:00
Addr : cfg . PxyAddrs [ i ] ,
2024-12-12 21:09:16 +09:00
Handler : s . pxy_mux ,
2025-08-19 20:20:18 +09:00
TLSConfig : cfg . PxyTls . Clone ( ) ,
2024-12-12 21:09:16 +09:00
ErrorLog : hs_log ,
// TODO: more settings
}
}
2024-12-26 00:20:44 +09:00
// ---------------------------------------------------------
2025-06-21 02:43:28 +09:00
s . wpx_mux = http . NewServeMux ( ) // TODO: make /_init,_ssh,_ssh/ws,_http configurable...
2025-01-09 20:30:14 +09:00
2025-06-24 00:58:46 +09:00
s . wpx_mux . Handle ( "/_ssh/{port_id}/" ,
2025-07-30 18:23:23 +09:00
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_WPX } , file : "_redir:xterm.html" } ) )
2025-06-24 00:58:46 +09:00
s . wpx_mux . Handle ( "/_ssh/{port_id}/xterm.html" ,
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_WPX } , file : "xterm.html" } ) )
2025-07-30 18:23:23 +09:00
s . wpx_mux . Handle ( "/_ssh/{port_id}/xterm.html/" ,
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_WPX } , file : "_forbidden" } ) )
2025-06-24 00:58:46 +09:00
s . wpx_mux . Handle ( "/_ssh/{port_id}/xterm.css" ,
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_WPX } , file : "xterm.css" } ) )
2025-07-30 18:23:23 +09:00
s . wpx_mux . Handle ( "/_ssh/{port_id}/xterm.css/" ,
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_WPX } , file : "_forbidden" } ) )
2025-06-24 00:58:46 +09:00
s . wpx_mux . Handle ( "/_ssh/{port_id}/xterm.js" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_WPX } , file : "xterm.js" } ) )
2025-07-30 18:23:23 +09:00
s . wpx_mux . Handle ( "/_ssh/{port_id}/xterm.js/" ,
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_WPX } , file : "_forbidden" } ) )
2025-06-24 00:58:46 +09:00
s . wpx_mux . Handle ( "/_ssh/{port_id}/xterm-addon-fit.js" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_WPX } , file : "xterm-addon-fit.js" } ) )
2025-07-30 18:23:23 +09:00
s . wpx_mux . Handle ( "/_ssh/{port_id}/xterm-addon-fit.js/" ,
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_WPX } , file : "_forbidden" } ) )
2025-06-24 00:58:46 +09:00
s . wpx_mux . Handle ( "/_ssh/{port_id}/ws" ,
s . SafeWrapWebsocketHandler ( s . WrapWebsocketHandler ( & server_pxy_ssh_ws { S : & s , Id : HS_ID_WPX } ) ) )
s . wpx_mux . Handle ( "/_ssh/{port_id}/session-info" ,
s . WrapHttpHandler ( & server_ctl_server_conns_id_routes_id { ServerCtl { S : & s , Id : HS_ID_WPX , NoAuth : true } } ) )
2025-01-09 23:02:50 +09:00
s . wpx_mux . Handle ( "/_ssh/" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_pxy_xterm_file { server_pxy : server_pxy { S : & s , Id : HS_ID_WPX } , file : "_forbidden" } ) )
2025-01-09 20:30:14 +09:00
2025-06-24 00:58:46 +09:00
// http proxy by port id
2024-12-27 16:52:27 +09:00
s . wpx_mux . Handle ( "/{port_id}/{trailer...}" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_pxy_http_main { server_pxy : server_pxy { S : & s , Id : HS_ID_WPX } , prefix : PORT_ID_MARKER } ) )
2025-06-24 00:58:46 +09:00
2024-12-28 13:19:51 +09:00
s . wpx_mux . Handle ( "/" ,
2025-03-29 13:29:02 +09:00
s . WrapHttpHandler ( & server_pxy_http_wpx { server_pxy : server_pxy { S : & s , Id : HS_ID_WPX } } ) )
2024-12-26 00:20:44 +09:00
2025-01-28 02:35:11 +09:00
s . wpx = make ( [ ] * http . Server , len ( cfg . WpxAddrs ) )
2024-12-26 00:20:44 +09:00
2025-01-28 02:35:11 +09:00
for i = 0 ; i < len ( cfg . WpxAddrs ) ; i ++ {
2024-12-26 00:20:44 +09:00
s . wpx [ i ] = & http . Server {
2025-01-28 02:35:11 +09:00
Addr : cfg . WpxAddrs [ i ] ,
2024-12-26 00:20:44 +09:00
Handler : s . wpx_mux ,
2025-08-19 20:20:18 +09:00
TLSConfig : cfg . WpxTls . Clone ( ) ,
2024-12-26 00:20:44 +09:00
ErrorLog : hs_log ,
}
}
2024-12-12 21:09:16 +09:00
// ---------------------------------------------------------
2024-12-08 12:13:36 +09:00
s . stats . conns . Store ( 0 )
s . stats . routes . Store ( 0 )
s . stats . peers . Store ( 0 )
2024-12-16 01:03:03 +09:00
s . stats . ssh_proxy_sessions . Store ( 0 )
2025-08-08 19:24:52 +09:00
s . stats . pty_sessions . Store ( 0 )
2025-08-19 20:20:18 +09:00
s . stats . rpty_sessions . Store ( 0 )
2025-08-19 22:23:22 +09:00
s . stats . rpx_sessions . Store ( 0 )
2024-12-08 12:13:36 +09:00
2024-11-12 22:59:37 +09:00
return & s , nil
oops :
2024-12-27 14:43:44 +09:00
for _ , l = range s . rpc { l . Close ( ) }
2024-12-03 11:52:46 +09:00
s . rpc = make ( [ ] * net . TCPListener , 0 )
2024-11-12 22:59:37 +09:00
return nil , err
}
2024-12-27 14:43:44 +09:00
func ( s * Server ) SetWpxResponseTransformer ( tf ServerWpxResponseTransformer ) {
s . wpx_resp_tf = tf
}
2024-12-28 18:35:14 +09:00
func ( s * Server ) GetWpxResponseTransformer ( ) ServerWpxResponseTransformer {
return s . wpx_resp_tf
}
2025-01-11 11:48:19 +09:00
func ( s * Server ) SetWpxForeignPortProxyMaker ( pm ServerWpxForeignPortProxyMaker ) {
s . wpx_foreign_port_proxy_maker = pm
}
func ( s * Server ) GetWpxForeignPortProxyMaker ( ) ServerWpxForeignPortProxyMaker {
return s . wpx_foreign_port_proxy_maker
}
2024-12-28 18:48:29 +09:00
func ( s * Server ) SetXtermHtml ( html string ) {
s . xterm_html = html
}
func ( s * Server ) GetXtermHtml ( ) string {
return s . xterm_html
}
2025-08-08 19:24:52 +09:00
func ( s * Server ) SetPtyUser ( user string ) {
s . pty_user = user
2025-06-23 21:09:24 +09:00
}
2025-08-08 19:24:52 +09:00
func ( s * Server ) GetPtyUser ( ) string {
return s . pty_user
2025-06-23 21:09:24 +09:00
}
2025-08-08 19:24:52 +09:00
func ( s * Server ) SetPtyShell ( user string ) {
s . pty_shell = user
2025-06-23 21:09:24 +09:00
}
2025-08-08 19:24:52 +09:00
func ( s * Server ) GetPtyShell ( ) string {
return s . pty_shell
2025-06-23 21:09:24 +09:00
}
2024-11-18 22:25:59 +09:00
func ( s * Server ) run_grpc_server ( idx int , wg * sync . WaitGroup ) error {
2024-11-12 22:59:37 +09:00
var l * net . TCPListener
var err error
2024-11-23 14:49:04 +09:00
defer wg . Done ( )
2024-11-18 22:25:59 +09:00
2024-12-03 11:52:46 +09:00
l = s . rpc [ idx ]
2024-11-12 22:59:37 +09:00
// it seems to be safe to call a single grpc server on differnt listening sockets multiple times
2024-12-09 01:51:04 +09:00
s . log . Write ( "" , LOG_INFO , "Starting RPC server on %s" , l . Addr ( ) . String ( ) )
2024-12-03 20:28:04 +09:00
err = s . rpc_svr . Serve ( l )
2024-11-12 22:59:37 +09:00
if err != nil {
2024-11-21 01:11:01 +09:00
if errors . Is ( err , net . ErrClosed ) {
2024-12-09 01:51:04 +09:00
s . log . Write ( "" , LOG_INFO , "RPC server on %s closed" , l . Addr ( ) . String ( ) )
2024-11-21 01:11:01 +09:00
} else {
2024-12-03 20:28:04 +09:00
s . log . Write ( "" , LOG_ERROR , "Error from RPC server on %s - %s" , l . Addr ( ) . String ( ) , err . Error ( ) )
2024-11-21 01:11:01 +09:00
}
return err
2024-11-12 22:59:37 +09:00
}
return nil
}
2024-11-18 22:25:59 +09:00
func ( s * Server ) RunTask ( wg * sync . WaitGroup ) {
2024-11-12 22:59:37 +09:00
var idx int
2024-11-18 22:25:59 +09:00
defer wg . Done ( )
2024-11-13 02:20:25 +09:00
2024-12-03 11:52:46 +09:00
for idx , _ = range s . rpc {
s . rpc_wg . Add ( 1 )
go s . run_grpc_server ( idx , & s . rpc_wg )
2024-11-12 22:59:37 +09:00
}
2025-08-19 20:20:18 +09:00
// most work is done in separate goroutines (s.run_grp_server)
// this read on the stop channel serves as a placeholder to prevent the logic flow from
2024-11-24 20:39:51 +09:00
// descening down to s.ReqStop()
2025-08-19 20:20:18 +09:00
<- s . stop_chan
2024-11-24 20:39:51 +09:00
s . ReqStop ( )
2024-12-03 11:52:46 +09:00
s . rpc_wg . Wait ( )
2024-12-16 00:41:32 +09:00
s . log . Write ( "" , LOG_DEBUG , "All RPC listeners ended" )
2024-11-24 20:39:51 +09:00
2024-11-18 22:25:59 +09:00
s . cts_wg . Wait ( )
2024-12-16 00:41:32 +09:00
s . log . Write ( "" , LOG_DEBUG , "All CTS handlers ended" )
2024-11-21 01:11:01 +09:00
2024-11-20 00:31:14 +09:00
// stop the main grpc server after all the other tasks are finished.
2024-12-03 20:28:04 +09:00
s . rpc_svr . Stop ( )
2024-11-12 22:59:37 +09:00
}
2025-08-19 20:20:18 +09:00
func ( s * Server ) run_single_ctl_server ( i int , cs * http . Server , wg * sync . WaitGroup ) {
var l net . Listener
2024-11-21 01:11:01 +09:00
var err error
2025-08-19 20:20:18 +09:00
defer wg . Done ( )
s . log . Write ( "" , LOG_INFO , "Control channel[%d] started on %s" , i , cs . Addr )
if s . stop_req . Load ( ) == false {
// defeat hard-coded "tcp" in ListenAndServe() and ListenAndServeTLS()
// err = cs.ListenAndServe()
// err = cs.ListenAndServeTLS("", "")
l , err = net . Listen ( TcpAddrStrClass ( cs . Addr ) , cs . Addr )
if err == nil {
if s . stop_req . Load ( ) == false {
var node * list . Element
s . ctl_addrs_mtx . Lock ( )
node = s . ctl_addrs . PushBack ( l . Addr ( ) . ( * net . TCPAddr ) )
s . ctl_addrs_mtx . Unlock ( )
if s . Cfg . CtlTls == nil {
err = cs . Serve ( l )
} else {
err = cs . ServeTLS ( l , "" , "" ) // s.Cfg.CtlTls must provide a certificate and a key
}
s . ctl_addrs_mtx . Lock ( )
s . ctl_addrs . Remove ( node )
s . ctl_addrs_mtx . Unlock ( )
} else {
err = fmt . Errorf ( "stop requested" )
}
l . Close ( )
}
} else {
err = fmt . Errorf ( "stop requested" )
}
if errors . Is ( err , http . ErrServerClosed ) {
s . log . Write ( "" , LOG_INFO , "Control channel[%d] ended" , i )
} else {
s . log . Write ( "" , LOG_ERROR , "Control channel[%d] error - %s" , i , err . Error ( ) )
}
}
func ( s * Server ) RunCtlTask ( wg * sync . WaitGroup ) {
2024-12-03 11:52:46 +09:00
var ctl * http . Server
var idx int
var l_wg sync . WaitGroup
2024-11-21 01:11:01 +09:00
defer wg . Done ( )
2024-12-03 11:52:46 +09:00
for idx , ctl = range s . ctl {
l_wg . Add ( 1 )
2025-08-19 20:20:18 +09:00
go s . run_single_ctl_server ( idx , ctl , & l_wg ) ;
}
l_wg . Wait ( )
}
2024-12-07 23:03:23 +09:00
2025-08-19 20:20:18 +09:00
func ( s * Server ) run_single_rpx_server ( i int , cs * http . Server , wg * sync . WaitGroup ) {
var l net . Listener
var err error
2024-12-07 23:03:23 +09:00
2025-08-19 20:20:18 +09:00
defer wg . Done ( )
s . log . Write ( "" , LOG_INFO , "RPX channel[%d] started on %s" , i , s . Cfg . RpxAddrs [ i ] )
if s . stop_req . Load ( ) == false {
l , err = net . Listen ( TcpAddrStrClass ( cs . Addr ) , cs . Addr )
if err == nil {
2024-12-09 17:23:26 +09:00
if s . stop_req . Load ( ) == false {
2025-08-19 20:20:18 +09:00
var node * list . Element
2025-03-31 23:40:45 +09:00
2025-08-19 20:20:18 +09:00
s . rpx_addrs_mtx . Lock ( )
node = s . rpx_addrs . PushBack ( l . Addr ( ) . ( * net . TCPAddr ) )
s . rpx_addrs_mtx . Unlock ( )
if s . Cfg . RpxTls == nil { // TODO: change this
err = cs . Serve ( l )
} else {
err = cs . ServeTLS ( l , "" , "" ) // s.Cfg.RpxTls must provide a certificate and a key
2024-12-07 23:03:23 +09:00
}
2025-08-19 20:20:18 +09:00
s . rpx_addrs_mtx . Lock ( )
s . rpx_addrs . Remove ( node )
s . rpx_addrs_mtx . Unlock ( )
2024-12-09 17:23:26 +09:00
} else {
err = fmt . Errorf ( "stop requested" )
2024-12-06 00:52:33 +09:00
}
2025-08-19 20:20:18 +09:00
l . Close ( )
}
} else {
err = fmt . Errorf ( "stop requested" )
2024-11-21 01:11:01 +09:00
}
2025-08-19 20:20:18 +09:00
if errors . Is ( err , http . ErrServerClosed ) {
s . log . Write ( "" , LOG_INFO , "RPX channel[%d] ended" , i )
} else {
s . log . Write ( "" , LOG_ERROR , "RPX channel[%d] error - %s" , i , err . Error ( ) )
}
2025-08-19 22:23:22 +09:00
2024-11-21 01:11:01 +09:00
}
2025-08-12 16:29:44 +09:00
func ( s * Server ) RunRpxTask ( wg * sync . WaitGroup ) {
var rpx * http . Server
var idx int
var l_wg sync . WaitGroup
defer wg . Done ( )
for idx , rpx = range s . rpx {
l_wg . Add ( 1 )
2025-08-19 20:20:18 +09:00
go s . run_single_rpx_server ( idx , rpx , & l_wg )
}
l_wg . Wait ( )
}
2025-08-12 16:29:44 +09:00
2025-08-19 20:20:18 +09:00
func ( s * Server ) run_single_pxy_server ( i int , cs * http . Server , wg * sync . WaitGroup ) {
var l net . Listener
var err error
2025-08-12 16:29:44 +09:00
2025-08-19 20:20:18 +09:00
defer wg . Done ( )
2025-08-12 16:29:44 +09:00
2025-08-19 20:20:18 +09:00
s . log . Write ( "" , LOG_INFO , "Proxy channel[%d] started on %s" , i , s . Cfg . PxyAddrs [ i ] )
2025-08-12 16:29:44 +09:00
2025-08-19 20:20:18 +09:00
if s . stop_req . Load ( ) == false {
l , err = net . Listen ( TcpAddrStrClass ( cs . Addr ) , cs . Addr )
if err == nil {
if s . stop_req . Load ( ) == false {
var node * list . Element
2025-08-12 16:29:44 +09:00
2025-08-19 20:20:18 +09:00
s . pxy_addrs_mtx . Lock ( )
node = s . pxy_addrs . PushBack ( l . Addr ( ) . ( * net . TCPAddr ) )
s . pxy_addrs_mtx . Unlock ( )
if s . Cfg . PxyTls == nil { // TODO: change this
err = cs . Serve ( l )
} else {
err = cs . ServeTLS ( l , "" , "" ) // s.Cfg.PxyTls must provide a certificate and a key
2025-08-12 16:29:44 +09:00
}
2025-08-19 20:20:18 +09:00
s . pxy_addrs_mtx . Lock ( )
s . pxy_addrs . Remove ( node )
s . pxy_addrs_mtx . Unlock ( )
2025-08-12 16:29:44 +09:00
} else {
err = fmt . Errorf ( "stop requested" )
}
2025-08-19 20:20:18 +09:00
l . Close ( )
}
} else {
err = fmt . Errorf ( "stop requested" )
}
if errors . Is ( err , http . ErrServerClosed ) {
s . log . Write ( "" , LOG_INFO , "Proxy channel[%d] ended" , i )
} else {
s . log . Write ( "" , LOG_ERROR , "Proxy channel[%d] error - %s" , i , err . Error ( ) )
2025-08-12 16:29:44 +09:00
}
}
2024-12-12 21:09:16 +09:00
func ( s * Server ) RunPxyTask ( wg * sync . WaitGroup ) {
var pxy * http . Server
var idx int
var l_wg sync . WaitGroup
defer wg . Done ( )
for idx , pxy = range s . pxy {
l_wg . Add ( 1 )
2025-08-19 20:20:18 +09:00
go s . run_single_pxy_server ( idx , pxy , & l_wg ) ;
}
l_wg . Wait ( )
}
2024-12-12 21:09:16 +09:00
2025-08-19 20:20:18 +09:00
func ( s * Server ) run_single_wpx_server ( i int , cs * http . Server , wg * sync . WaitGroup ) {
var l net . Listener
var err error
2024-12-12 21:09:16 +09:00
2025-08-19 20:20:18 +09:00
defer wg . Done ( )
2025-03-31 23:40:45 +09:00
2025-08-19 20:20:18 +09:00
s . log . Write ( "" , LOG_INFO , "Wpx channel[%d] started on %s" , i , s . Cfg . WpxAddrs [ i ] )
2025-03-31 23:40:45 +09:00
2025-08-19 20:20:18 +09:00
if s . stop_req . Load ( ) == false {
l , err = net . Listen ( TcpAddrStrClass ( cs . Addr ) , cs . Addr )
if err == nil {
if s . stop_req . Load ( ) == false {
var node * list . Element
2025-03-31 23:40:45 +09:00
2025-08-19 20:20:18 +09:00
s . wpx_addrs_mtx . Lock ( )
node = s . wpx_addrs . PushBack ( l . Addr ( ) . ( * net . TCPAddr ) )
s . wpx_addrs_mtx . Unlock ( )
if s . Cfg . WpxTls == nil {
err = cs . Serve ( l )
} else {
err = cs . ServeTLS ( l , "" , "" ) // s.Cfg.WpxTls must provide a certificate and a key
2024-12-12 21:09:16 +09:00
}
2025-08-19 20:20:18 +09:00
s . wpx_addrs_mtx . Lock ( )
s . wpx_addrs . Remove ( node )
s . wpx_addrs_mtx . Unlock ( )
2024-12-12 21:09:16 +09:00
} else {
err = fmt . Errorf ( "stop requested" )
}
2025-08-19 20:20:18 +09:00
l . Close ( )
}
} else {
err = fmt . Errorf ( "stop requested" )
2024-12-12 21:09:16 +09:00
}
2025-08-19 20:20:18 +09:00
if errors . Is ( err , http . ErrServerClosed ) {
s . log . Write ( "" , LOG_INFO , "Wpx channel[%d] ended" , i )
} else {
s . log . Write ( "" , LOG_ERROR , "Wpx channel[%d] error - %s" , i , err . Error ( ) )
}
2024-12-12 21:09:16 +09:00
}
2024-12-26 00:20:44 +09:00
func ( s * Server ) RunWpxTask ( wg * sync . WaitGroup ) {
var wpx * http . Server
var idx int
var l_wg sync . WaitGroup
defer wg . Done ( )
for idx , wpx = range s . wpx {
l_wg . Add ( 1 )
2025-08-19 20:20:18 +09:00
go s . run_single_wpx_server ( idx , wpx , & l_wg )
2024-12-26 00:20:44 +09:00
}
l_wg . Wait ( )
}
2024-11-12 22:59:37 +09:00
func ( s * Server ) ReqStop ( ) {
if s . stop_req . CompareAndSwap ( false , true ) {
var l * net . TCPListener
2024-11-25 19:46:18 +09:00
var cts * ServerConn
2024-12-12 21:09:16 +09:00
var hs * http . Server
2025-03-12 12:08:56 +09:00
s . bulletin . Block ( )
2024-12-25 02:14:47 +09:00
// call cancellation function before anything else
// to break sub-tasks relying on this server context.
2025-03-12 12:57:46 +09:00
// for example, http.Client in server_pxy_http_main
2025-02-19 10:38:23 +09:00
s . CtxCancel ( )
2024-12-25 02:14:47 +09:00
2024-12-12 21:09:16 +09:00
for _ , hs = range s . ctl {
2025-02-19 10:38:23 +09:00
hs . Shutdown ( s . Ctx ) // to break s.ctl.Serve()
2024-12-12 21:09:16 +09:00
}
2024-11-12 22:59:37 +09:00
2025-08-12 16:29:44 +09:00
for _ , hs = range s . rpx {
hs . Shutdown ( s . Ctx ) // to break s.rpx.Serve()
}
2024-12-12 21:09:16 +09:00
for _ , hs = range s . pxy {
2025-02-19 10:38:23 +09:00
hs . Shutdown ( s . Ctx ) // to break s.pxy.Serve()
2024-11-24 20:39:51 +09:00
}
2024-12-26 00:20:44 +09:00
for _ , hs = range s . wpx {
2025-02-19 10:38:23 +09:00
hs . Shutdown ( s . Ctx ) // to break s.wpx.Serve()
2024-12-26 00:20:44 +09:00
}
2024-12-05 01:26:44 +09:00
//s.rpc_svr.GracefulStop()
//s.rpc_svr.Stop()
2024-12-03 11:52:46 +09:00
for _ , l = range s . rpc {
2024-11-12 22:59:37 +09:00
l . Close ( )
}
2024-12-26 00:20:44 +09:00
// request to stop connections from/to peer held in the cts structure
2024-12-03 20:28:04 +09:00
s . cts_mtx . Lock ( )
2024-12-26 00:20:44 +09:00
for _ , cts = range s . cts_map { cts . ReqStop ( ) }
2024-11-12 22:59:37 +09:00
s . cts_mtx . Unlock ( )
2024-11-24 20:39:51 +09:00
s . stop_chan <- true
2024-11-12 22:59:37 +09:00
}
}
2024-12-02 02:19:50 +09:00
func ( s * Server ) AddNewServerConn ( remote_addr * net . Addr , local_addr * net . Addr , pss Hodu_PacketStreamServer ) ( * ServerConn , error ) {
2024-11-25 19:46:18 +09:00
var cts ServerConn
2024-12-13 23:19:26 +09:00
var start_id ConnId
2025-01-07 23:59:39 +09:00
var assigned_id ConnId
2024-11-12 22:59:37 +09:00
var ok bool
2025-02-18 14:44:45 +09:00
cts . S = s
2025-03-14 22:51:23 +09:00
cts . Created = time . Now ( )
2024-11-18 22:25:59 +09:00
cts . route_map = make ( ServerRouteMap )
2025-08-24 19:00:26 +09:00
cts . route_map_by_ptc_name = make ( ServerRouteMapByPtcName )
2024-12-28 18:35:14 +09:00
cts . RemoteAddr = * remote_addr
cts . LocalAddr = * local_addr
2024-11-18 22:25:59 +09:00
cts . pss = & GuardedPacketStreamServer { Hodu_PacketStreamServer : pss }
2024-11-12 22:59:37 +09:00
cts . stop_req . Store ( false )
2024-11-24 20:39:51 +09:00
cts . stop_chan = make ( chan bool , 8 )
2025-03-23 13:22:53 +09:00
cts . pts_list = list . New ( )
2024-11-12 22:59:37 +09:00
2025-08-12 02:50:10 +09:00
cts . rpty_map = make ( ServerRptyMap )
cts . rpty_map_by_ws = make ( ServerRptyMapByWs )
2025-08-19 20:20:18 +09:00
cts . rpx_map = make ( ServerRpxMap )
2025-08-12 02:50:10 +09:00
2024-11-12 22:59:37 +09:00
s . cts_mtx . Lock ( )
2024-12-10 14:37:14 +09:00
if s . cts_limit > 0 && len ( s . cts_map ) >= s . cts_limit {
s . cts_mtx . Unlock ( )
2024-12-08 17:25:59 +09:00
return nil , fmt . Errorf ( "too many connections - %d" , s . cts_limit )
}
2024-12-13 23:19:26 +09:00
//start_id = rand.Uint64()
//start_id = ConnId(monotonic_time() / 1000)
start_id = s . cts_next_id
2024-12-03 00:55:19 +09:00
for {
2024-12-13 23:19:26 +09:00
_ , ok = s . cts_map [ s . cts_next_id ]
2025-01-07 23:59:39 +09:00
if ! ok {
assigned_id = s . cts_next_id
2025-01-08 17:32:40 +09:00
s . cts_next_id ++
2025-01-07 23:59:39 +09:00
if s . cts_next_id == 0 { s . cts_next_id ++ }
break
}
2024-12-13 23:19:26 +09:00
s . cts_next_id ++
2025-01-07 23:59:39 +09:00
if s . cts_next_id == 0 { s . cts_next_id ++ }
2024-12-13 23:19:26 +09:00
if s . cts_next_id == start_id {
s . cts_mtx . Unlock ( )
return nil , fmt . Errorf ( "unable to assign id" )
}
2024-12-03 00:55:19 +09:00
}
2025-01-07 23:59:39 +09:00
cts . Id = assigned_id
2025-02-19 17:17:53 +09:00
cts . Sid = fmt . Sprintf ( "%d" , cts . Id ) // id in string used for logging
2025-08-10 17:23:01 +09:00
cts . rpty_next_id = 1
2024-12-03 00:55:19 +09:00
2024-12-28 18:35:14 +09:00
_ , ok = s . cts_map_by_addr [ cts . RemoteAddr ]
2024-11-12 22:59:37 +09:00
if ok {
2024-12-10 14:37:14 +09:00
s . cts_mtx . Unlock ( )
2025-02-20 22:21:39 +09:00
return nil , fmt . Errorf ( "existing client address - %s" , cts . RemoteAddr . String ( ) )
}
2025-03-13 21:24:59 +09:00
if cts . ClientToken . Get ( ) != "" {
2025-02-20 22:21:39 +09:00
// this check is not needed as Token is never set at this phase
// however leave statements here for completeness
2025-03-13 21:24:59 +09:00
_ , ok = s . cts_map_by_token [ cts . ClientToken . Get ( ) ]
2025-02-20 22:21:39 +09:00
if ok {
s . cts_mtx . Unlock ( )
2025-03-13 21:24:59 +09:00
return nil , fmt . Errorf ( "existing client token - %s" , cts . ClientToken . Get ( ) )
2025-02-20 22:21:39 +09:00
}
2025-03-13 21:24:59 +09:00
s . cts_map_by_token [ cts . ClientToken . Get ( ) ] = & cts
2024-11-12 22:59:37 +09:00
}
2024-12-28 18:35:14 +09:00
s . cts_map_by_addr [ cts . RemoteAddr ] = & cts
2025-01-08 17:32:40 +09:00
s . cts_map [ cts . Id ] = & cts
2024-12-08 12:13:36 +09:00
s . stats . conns . Store ( int64 ( len ( s . cts_map ) ) )
2024-12-10 14:37:14 +09:00
s . cts_mtx . Unlock ( )
2025-02-19 17:17:53 +09:00
s . log . Write ( cts . Sid , LOG_DEBUG , "Added client connection from %s" , cts . RemoteAddr . String ( ) )
2024-11-12 22:59:37 +09:00
return & cts , nil
}
2024-12-02 09:46:10 +09:00
func ( s * Server ) ReqStopAllServerConns ( ) {
var cts * ServerConn
s . cts_mtx . Lock ( )
2024-12-26 00:20:44 +09:00
for _ , cts = range s . cts_map { cts . ReqStop ( ) }
s . cts_mtx . Unlock ( )
2024-12-02 09:46:10 +09:00
}
2024-12-03 00:55:19 +09:00
func ( s * Server ) RemoveServerConn ( cts * ServerConn ) error {
var conn * ServerConn
var ok bool
s . cts_mtx . Lock ( )
2024-12-28 18:35:14 +09:00
conn , ok = s . cts_map [ cts . Id ]
2024-12-03 00:55:19 +09:00
if ! ok {
s . cts_mtx . Unlock ( )
2024-12-28 18:35:14 +09:00
return fmt . Errorf ( "non-existent connection id - %d" , cts . Id )
2024-12-03 00:55:19 +09:00
}
if conn != cts {
s . cts_mtx . Unlock ( )
2024-12-28 18:35:14 +09:00
return fmt . Errorf ( "non-existent connection id - %d" , cts . Id )
2024-12-03 00:55:19 +09:00
}
2024-12-28 18:35:14 +09:00
delete ( s . cts_map , cts . Id )
delete ( s . cts_map_by_addr , cts . RemoteAddr )
2025-03-13 21:24:59 +09:00
if cts . ClientToken . Get ( ) != "" { delete ( s . cts_map_by_token , cts . ClientToken . Get ( ) ) }
2024-12-08 12:13:36 +09:00
s . stats . conns . Store ( int64 ( len ( s . cts_map ) ) )
2024-12-03 00:55:19 +09:00
s . cts_mtx . Unlock ( )
cts . ReqStop ( )
return nil
}
2025-01-07 23:59:39 +09:00
func ( s * Server ) RemoveServerConnByAddr ( addr net . Addr ) ( * ServerConn , error ) {
2024-12-03 00:55:19 +09:00
var cts * ServerConn
var ok bool
2024-11-12 22:59:37 +09:00
s . cts_mtx . Lock ( )
2024-12-03 00:55:19 +09:00
cts , ok = s . cts_map_by_addr [ addr ]
if ! ok {
s . cts_mtx . Unlock ( )
2025-01-07 23:59:39 +09:00
return nil , fmt . Errorf ( "non-existent connection address - %s" , addr . String ( ) )
2024-12-03 00:55:19 +09:00
}
2024-12-28 18:35:14 +09:00
delete ( s . cts_map , cts . Id )
delete ( s . cts_map_by_addr , cts . RemoteAddr )
2025-03-13 21:24:59 +09:00
if cts . ClientToken . Get ( ) != "" { delete ( s . cts_map_by_token , cts . ClientToken . Get ( ) ) }
2025-02-20 22:21:39 +09:00
s . stats . conns . Store ( int64 ( len ( s . cts_map ) ) )
s . cts_mtx . Unlock ( )
cts . ReqStop ( )
return cts , nil
}
2025-02-22 10:08:57 +09:00
func ( s * Server ) RemoveServerConnByClientToken ( token string ) ( * ServerConn , error ) {
2025-02-20 22:21:39 +09:00
var cts * ServerConn
var ok bool
s . cts_mtx . Lock ( )
cts , ok = s . cts_map_by_token [ token ]
if ! ok {
s . cts_mtx . Unlock ( )
return nil , fmt . Errorf ( "non-existent connection token - %s" , token )
}
delete ( s . cts_map , cts . Id )
delete ( s . cts_map_by_addr , cts . RemoteAddr )
2025-08-29 10:44:05 +09:00
delete ( s . cts_map_by_token , cts . ClientToken . Get ( ) ) // no emptiness check because an empty token is never found in the map
2024-12-08 12:13:36 +09:00
s . stats . conns . Store ( int64 ( len ( s . cts_map ) ) )
2024-11-12 22:59:37 +09:00
s . cts_mtx . Unlock ( )
2024-12-03 00:55:19 +09:00
cts . ReqStop ( )
2025-01-07 23:59:39 +09:00
return cts , nil
2024-11-12 22:59:37 +09:00
}
2024-12-09 22:41:23 +09:00
func ( s * Server ) FindServerConnById ( id ConnId ) * ServerConn {
2024-11-25 19:46:18 +09:00
var cts * ServerConn
2024-11-12 22:59:37 +09:00
var ok bool
s . cts_mtx . Lock ( )
defer s . cts_mtx . Unlock ( )
2024-12-03 00:55:19 +09:00
cts , ok = s . cts_map [ id ]
2025-02-20 22:21:39 +09:00
if ! ok { return nil }
2024-12-03 00:55:19 +09:00
return cts
2024-11-12 22:59:37 +09:00
}
2024-11-25 19:46:18 +09:00
func ( s * Server ) FindServerConnByAddr ( addr net . Addr ) * ServerConn {
var cts * ServerConn
2024-11-12 22:59:37 +09:00
var ok bool
s . cts_mtx . Lock ( )
defer s . cts_mtx . Unlock ( )
2024-12-03 00:55:19 +09:00
cts , ok = s . cts_map_by_addr [ addr ]
2025-02-20 22:21:39 +09:00
if ! ok { return nil }
return cts
}
2025-02-22 10:08:57 +09:00
func ( s * Server ) FindServerConnByClientToken ( token string ) * ServerConn {
2025-02-20 22:21:39 +09:00
var cts * ServerConn
var ok bool
s . cts_mtx . Lock ( )
defer s . cts_mtx . Unlock ( )
cts , ok = s . cts_map_by_token [ token ]
if ! ok { return nil }
2024-11-12 22:59:37 +09:00
return cts
}
2024-12-12 21:09:16 +09:00
func ( s * Server ) FindServerRouteById ( id ConnId , route_id RouteId ) * ServerRoute {
var cts * ServerConn
var ok bool
s . cts_mtx . Lock ( )
defer s . cts_mtx . Unlock ( )
cts , ok = s . cts_map [ id ]
2024-12-16 15:19:01 +09:00
if ! ok { return nil }
2024-12-12 21:09:16 +09:00
return cts . FindServerRouteById ( route_id )
}
2025-08-24 19:13:13 +09:00
func ( s * Server ) FindServerRouteByIdAndPtcName ( id ConnId , ptc_name string ) * ServerRoute {
var cts * ServerConn
var ok bool
s . cts_mtx . Lock ( )
defer s . cts_mtx . Unlock ( )
cts , ok = s . cts_map [ id ]
if ! ok { return nil }
return cts . FindServerRouteByPtcName ( ptc_name )
}
2025-08-24 19:00:26 +09:00
func ( s * Server ) FindServerRouteByClientTokenAndRouteId ( token string , route_id RouteId ) * ServerRoute {
var cts * ServerConn
var ok bool
s . cts_mtx . Lock ( )
defer s . cts_mtx . Unlock ( )
cts , ok = s . cts_map_by_token [ token ]
if ! ok { return nil }
return cts . FindServerRouteById ( route_id )
}
func ( s * Server ) FindServerRouteByClientTokenAndPtcName ( token string , ptc_name string ) * ServerRoute {
var cts * ServerConn
var ok bool
s . cts_mtx . Lock ( )
defer s . cts_mtx . Unlock ( )
cts , ok = s . cts_map_by_token [ token ]
if ! ok { return nil }
return cts . FindServerRouteByPtcName ( ptc_name )
}
2025-02-15 19:44:48 +09:00
func ( s * Server ) FindServerPeerConnById ( id ConnId , route_id RouteId , peer_id PeerId ) * ServerPeerConn {
var cts * ServerConn
var ok bool
s . cts_mtx . Lock ( )
defer s . cts_mtx . Unlock ( )
cts , ok = s . cts_map [ id ]
if ! ok { return nil }
return cts . FindServerPeerConnById ( route_id , peer_id )
}
2024-12-16 15:19:01 +09:00
func ( s * Server ) FindServerRouteByPortId ( port_id PortId ) * ServerRoute {
var cri ConnRouteId
var ok bool
s . svc_port_mtx . Lock ( )
defer s . svc_port_mtx . Unlock ( )
cri , ok = s . svc_port_map [ port_id ]
if ! ok { return nil }
return s . FindServerRouteById ( cri . conn_id , cri . route_id )
}
2025-02-15 19:44:48 +09:00
func ( s * Server ) FindServerPeerConnByPortId ( port_id PortId , peer_id PeerId ) * ServerPeerConn {
var cri ConnRouteId
var ok bool
s . svc_port_mtx . Lock ( )
defer s . svc_port_mtx . Unlock ( )
cri , ok = s . svc_port_map [ port_id ]
if ! ok { return nil }
return s . FindServerPeerConnById ( cri . conn_id , cri . route_id , peer_id )
}
func ( s * Server ) FindServerPeerConnByIdStr ( conn_id string , route_id string , peer_id string ) ( * ServerPeerConn , error ) {
var p * ServerPeerConn
var err error
if route_id == PORT_ID_MARKER {
var port_nid uint64
var peer_nid uint64
port_nid , err = strconv . ParseUint ( conn_id , 10 , int ( unsafe . Sizeof ( PortId ( 0 ) ) * 8 ) )
if err != nil { return nil , fmt . Errorf ( "invalid port id %s - %s" , conn_id , err . Error ( ) ) }
peer_nid , err = strconv . ParseUint ( peer_id , 10 , int ( unsafe . Sizeof ( PeerId ( 0 ) ) * 8 ) )
if err != nil { return nil , fmt . Errorf ( "invalid peer id %s - %s" , peer_id , err . Error ( ) ) }
p = s . FindServerPeerConnByPortId ( PortId ( port_nid ) , PeerId ( peer_nid ) )
if p == nil { return nil , fmt . Errorf ( "peer(%d,%d) not found" , port_nid , peer_nid ) }
} else {
var conn_nid uint64
var route_nid uint64
var peer_nid uint64
conn_nid , err = strconv . ParseUint ( conn_id , 10 , int ( unsafe . Sizeof ( ConnId ( 0 ) ) * 8 ) )
if err != nil { return nil , fmt . Errorf ( "invalid connection id %s - %s" , conn_id , err . Error ( ) ) }
route_nid , err = strconv . ParseUint ( route_id , 10 , int ( unsafe . Sizeof ( RouteId ( 0 ) ) * 8 ) )
if err != nil { return nil , fmt . Errorf ( "invalid route id %s - %s" , route_id , err . Error ( ) ) }
peer_nid , err = strconv . ParseUint ( peer_id , 10 , int ( unsafe . Sizeof ( PeerId ( 0 ) ) * 8 ) )
if err != nil { return nil , fmt . Errorf ( "invalid peer id %s - %s" , peer_id , err . Error ( ) ) }
p = s . FindServerPeerConnById ( ConnId ( conn_nid ) , RouteId ( route_nid ) , PeerId ( peer_nid ) )
if p == nil { return nil , fmt . Errorf ( "peer(%d,%d,%d) not found" , conn_nid , route_nid , peer_nid ) }
}
return p , nil
}
2024-12-17 09:35:51 +09:00
func ( s * Server ) FindServerRouteByIdStr ( conn_id string , route_id string ) ( * ServerRoute , error ) {
var r * ServerRoute
2024-12-26 00:20:44 +09:00
if route_id == PORT_ID_MARKER {
2024-12-17 09:35:51 +09:00
var port_nid uint64
2025-08-24 19:00:26 +09:00
var err error
2024-12-17 09:35:51 +09:00
port_nid , err = strconv . ParseUint ( conn_id , 10 , int ( unsafe . Sizeof ( PortId ( 0 ) ) * 8 ) )
2024-12-20 01:09:00 +09:00
if err != nil { return nil , fmt . Errorf ( "invalid port id %s - %s" , conn_id , err . Error ( ) ) }
2024-12-17 09:35:51 +09:00
r = s . FindServerRouteByPortId ( PortId ( port_nid ) )
2024-12-20 01:09:00 +09:00
if r == nil { return nil , fmt . Errorf ( "port(%d) not found" , port_nid ) }
2024-12-17 09:35:51 +09:00
} else {
var conn_nid uint64
var route_nid uint64
2025-08-24 19:00:26 +09:00
var err1 error
var err2 error
2024-12-17 09:35:51 +09:00
2025-08-24 19:00:26 +09:00
conn_nid , err1 = strconv . ParseUint ( conn_id , 10 , int ( unsafe . Sizeof ( ConnId ( 0 ) ) * 8 ) )
route_nid , err2 = strconv . ParseUint ( route_id , 10 , int ( unsafe . Sizeof ( RouteId ( 0 ) ) * 8 ) )
2024-12-17 09:35:51 +09:00
2025-08-24 19:13:13 +09:00
if err1 == nil && err2 == nil {
2025-08-24 19:00:26 +09:00
r = s . FindServerRouteById ( ConnId ( conn_nid ) , RouteId ( route_nid ) )
if r == nil { return nil , fmt . Errorf ( "route(%d,%d) not found" , conn_nid , route_nid ) }
2025-08-24 19:13:13 +09:00
} else if err2 == nil {
// route id is numeric while the conn id is not.
// if you know the client token but don't know the client-side peer name,
// this condition will be reached.
r = s . FindServerRouteByClientTokenAndRouteId ( conn_id , RouteId ( route_nid ) )
if r == nil { return nil , fmt . Errorf ( "route(%s,%d) not found" , conn_id , route_nid ) }
} else if err1 == nil {
// numeric route id and ptc name
r = s . FindServerRouteByIdAndPtcName ( ConnId ( conn_nid ) , route_id )
if r == nil { return nil , fmt . Errorf ( "route(%d,%s) not found" , conn_nid , route_id ) }
} else {
// if not numeric, attempt to use it as a token and ptc name
r = s . FindServerRouteByClientTokenAndPtcName ( conn_id , route_id )
if r == nil { return nil , fmt . Errorf ( "route(%s,%s) not found" , conn_id , route_id ) }
2025-08-24 19:00:26 +09:00
}
2024-12-17 09:35:51 +09:00
}
return r , nil
}
func ( s * Server ) FindServerConnByIdStr ( conn_id string ) ( * ServerConn , error ) {
var conn_nid uint64
var cts * ServerConn
var err error
conn_nid , err = strconv . ParseUint ( conn_id , 10 , int ( unsafe . Sizeof ( ConnId ( 0 ) ) * 8 ) )
2025-02-20 23:12:16 +09:00
if err != nil {
2025-03-07 21:12:21 +09:00
//return nil, fmt.Errorf("invalid connection id %s - %s", conn_id, err.Error())
2025-02-22 10:08:57 +09:00
cts = s . FindServerConnByClientToken ( conn_id ) // if not numeric, attempt to use it as a token
2025-02-20 23:12:16 +09:00
if cts == nil { return nil , fmt . Errorf ( "non-existent connection token '%s'" , conn_id ) }
} else {
cts = s . FindServerConnById ( ConnId ( conn_nid ) )
if cts == nil { return nil , fmt . Errorf ( "non-existent connection id %d" , conn_nid ) }
}
2024-12-17 09:35:51 +09:00
return cts , nil
}
2024-12-12 21:09:16 +09:00
2025-08-29 10:44:05 +09:00
func ( s * Server ) StartService ( data interface { } ) {
2025-03-12 12:08:56 +09:00
s . wg . Add ( 1 )
go s . bulletin . RunTask ( & s . wg )
2024-11-23 12:30:23 +09:00
s . wg . Add ( 1 )
go s . RunTask ( & s . wg )
}
2024-11-12 22:59:37 +09:00
2024-11-23 12:30:23 +09:00
func ( s * Server ) StartExtService ( svc Service , data interface { } ) {
2024-11-30 20:24:30 +09:00
s . ext_mtx . Lock ( )
2025-03-11 21:12:05 +09:00
if s . ext_closed {
// don't start it if it's already closed
s . ext_mtx . Unlock ( )
return
}
2024-11-23 12:30:23 +09:00
s . ext_svcs = append ( s . ext_svcs , svc )
2024-11-30 20:24:30 +09:00
s . ext_mtx . Unlock ( )
2024-11-23 12:30:23 +09:00
s . wg . Add ( 1 )
go svc . RunTask ( & s . wg )
}
2024-11-12 22:59:37 +09:00
2024-12-01 21:47:11 +09:00
func ( s * Server ) StartCtlService ( ) {
s . wg . Add ( 1 )
go s . RunCtlTask ( & s . wg )
}
2025-08-12 16:29:44 +09:00
func ( s * Server ) StartRpxService ( ) {
s . wg . Add ( 1 )
go s . RunRpxTask ( & s . wg )
}
2024-12-12 21:09:16 +09:00
func ( s * Server ) StartPxyService ( ) {
s . wg . Add ( 1 )
go s . RunPxyTask ( & s . wg )
}
2024-12-26 00:20:44 +09:00
func ( s * Server ) StartWpxService ( ) {
s . wg . Add ( 1 )
go s . RunWpxTask ( & s . wg )
}
2024-11-23 12:30:23 +09:00
func ( s * Server ) StopServices ( ) {
var ext_svc Service
s . ReqStop ( )
2025-03-12 12:08:56 +09:00
s . bulletin . ReqStop ( )
2025-03-11 21:12:05 +09:00
s . ext_mtx . Lock ( )
2024-11-23 12:30:23 +09:00
for _ , ext_svc = range s . ext_svcs {
ext_svc . StopServices ( )
2024-11-12 22:59:37 +09:00
}
2025-03-11 21:12:05 +09:00
s . ext_closed = true
s . ext_mtx . Unlock ( )
2024-11-23 12:30:23 +09:00
}
2024-11-12 22:59:37 +09:00
2024-12-14 14:04:33 +09:00
func ( s * Server ) FixServices ( ) {
2024-12-15 15:07:35 +09:00
s . log . Rotate ( )
2024-12-14 14:04:33 +09:00
}
2024-11-23 12:30:23 +09:00
func ( s * Server ) WaitForTermination ( ) {
2024-11-13 02:20:25 +09:00
s . wg . Wait ( )
2025-03-18 23:37:46 +09:00
s . log . Write ( "" , LOG_INFO , "End of service" )
2024-11-12 22:59:37 +09:00
}
2024-11-23 14:49:04 +09:00
2024-11-25 19:46:18 +09:00
func ( s * Server ) WriteLog ( id string , level LogLevel , fmtstr string , args ... interface { } ) {
2024-11-23 20:13:07 +09:00
s . log . Write ( id , level , fmtstr , args ... )
2024-11-23 14:49:04 +09:00
}
2025-01-13 15:20:36 +09:00
2025-03-21 12:53:16 +09:00
func ( s * Server ) SetConnNoticeHandlers ( handlers [ ] ServerConnNoticeHandler ) {
s . conn_notice_handlers = handlers
2025-02-18 14:44:45 +09:00
}
2025-03-31 23:40:45 +09:00
func ( s * Server ) GetFirstCtlAddr ( ) * net . TCPAddr {
var e * list . Element
s . ctl_addrs_mtx . Lock ( )
defer s . ctl_addrs_mtx . Unlock ( )
e = s . ctl_addrs . Front ( )
if e == nil { return nil }
return e . Value . ( * net . TCPAddr )
}
func ( s * Server ) GetFirstPxyAddr ( ) * net . TCPAddr {
var e * list . Element
s . pxy_addrs_mtx . Lock ( )
defer s . pxy_addrs_mtx . Unlock ( )
e = s . pxy_addrs . Front ( )
if e == nil { return nil }
return e . Value . ( * net . TCPAddr )
}
func ( s * Server ) GetFirstWpxAddr ( ) * net . TCPAddr {
var e * list . Element
s . wpx_addrs_mtx . Lock ( )
defer s . wpx_addrs_mtx . Unlock ( )
e = s . wpx_addrs . Front ( )
if e == nil { return nil }
return e . Value . ( * net . TCPAddr )
}
2025-01-15 01:19:50 +09:00
func ( s * Server ) AddCtlHandler ( path string , handler ServerHttpHandler ) {
2025-03-29 13:29:02 +09:00
// parked under /_ctl
2025-02-26 14:46:09 +09:00
s . ctl_mux . Handle ( s . Cfg . CtlPrefix + "/_ctl" + path , s . WrapHttpHandler ( handler ) )
2025-03-29 13:29:02 +09:00
}
func ( s * Server ) AddCtlRootHandler ( path string , handler ServerHttpHandler ) {
// parked at the root level. must avoid conflicting path
s . ctl_mux . Handle ( s . Cfg . CtlPrefix + path , s . WrapHttpHandler ( handler ) )
2025-01-15 01:19:50 +09:00
}
2025-01-28 00:44:02 +09:00
func ( s * Server ) AddCtlMetricsCollector ( col prometheus . Collector ) error {
return s . promreg . Register ( col )
}
func ( s * Server ) RemoveCtlMetricsCollector ( col prometheus . Collector ) bool {
return s . promreg . Unregister ( col )
}
2025-02-26 19:45:29 +09:00
func ( s * Server ) SendNotice ( id_str string , text string ) error {
var cts * ServerConn
var err error
if id_str != "" {
cts , err = s . FindServerConnByIdStr ( id_str ) // this function accepts connection id as well as the token.
if err != nil { return err }
err = cts . pss . Send ( MakeConnNoticePacket ( text ) )
if err != nil {
return fmt . Errorf ( "failed to send conn_notice text '%s' to %s - %s" , text , cts . RemoteAddr , err . Error ( ) )
}
} else {
s . cts_mtx . Lock ( )
// TODO: what if this loop takes too long? in that case,
// lock is held for long. think about how to handle this.
for _ , cts = range s . cts_map {
cts . pss . Send ( MakeConnNoticePacket ( text ) )
// let's not care about an error when broacasting a notice to all connections
}
s . cts_mtx . Unlock ( )
}
return nil
}
2025-03-28 17:03:17 +09:00
func ( s * Server ) FireConnEvent ( event_kind ServerEventKind , cts * ServerConn ) {
if event_kind == SERVER_EVENT_CONN_STOPPED {
s . bulletin . Enqueue (
& ServerEvent {
Kind : event_kind ,
Data : & json_out_server_conn_id {
CId : cts . Id ,
} ,
} ,
)
} else {
s . bulletin . Enqueue (
& ServerEvent {
Kind : event_kind ,
Data : & json_out_server_conn {
CId : cts . Id ,
ServerAddr : cts . LocalAddr . String ( ) ,
ClientAddr : cts . RemoteAddr . String ( ) ,
ClientToken : cts . ClientToken . Get ( ) ,
CreatedMilli : cts . Created . UnixMilli ( ) ,
} ,
} ,
)
}
}
func ( s * Server ) FireRouteEvent ( event_kind ServerEventKind , r * ServerRoute ) {
if event_kind == SERVER_EVENT_ROUTE_STOPPED {
s . bulletin . Enqueue (
& ServerEvent {
Kind : event_kind ,
Data : & json_out_server_route_id {
CId : r . Cts . Id ,
RId : r . Id ,
} ,
} ,
)
} else {
s . bulletin . Enqueue (
& ServerEvent {
Kind : event_kind ,
Data : & json_out_server_route {
CId : r . Cts . Id ,
RId : r . Id ,
ClientPeerAddr : r . PtcAddr ,
ClientPeerName : r . PtcName ,
ServerPeerSvcAddr : r . SvcAddr . String ( ) ,
ServerPeerSvcNet : r . SvcPermNet . String ( ) ,
ServerPeerOption : r . SvcOption . String ( ) ,
CreatedMilli : r . Created . UnixMilli ( ) ,
} ,
} ,
)
}
}
func ( s * Server ) FirePeerEvent ( event_kind ServerEventKind , pts * ServerPeerConn ) {
if event_kind == SERVER_EVENT_PEER_STOPPED {
pts . route . Cts . S . bulletin . Enqueue (
& ServerEvent {
Kind : event_kind ,
Data : & json_out_server_peer_id {
CId : pts . route . Cts . Id ,
RId : pts . route . Id ,
PId : pts . conn_id ,
} ,
} ,
)
} else {
s . bulletin . Enqueue (
& ServerEvent {
Kind : event_kind ,
Data : & json_out_server_peer {
CId : pts . route . Cts . Id ,
RId : pts . route . Id ,
PId : pts . conn_id ,
ServerPeerAddr : pts . conn . RemoteAddr ( ) . String ( ) ,
ServerLocalAddr : pts . conn . LocalAddr ( ) . String ( ) ,
ClientPeerAddr : pts . client_peer_raddr . Get ( ) ,
ClientLocalAddr : pts . client_peer_laddr . Get ( ) ,
CreatedMilli : pts . Created . UnixMilli ( ) ,
} ,
} ,
)
}
}