2024-11-23 03:30:23 +00:00
package hodu
2024-11-12 13:59:37 +00:00
//import "bufio"
import "context"
import "crypto/tls"
2024-11-15 15:03:42 +00:00
import "encoding/json"
2024-11-17 05:57:56 +00:00
import "errors"
2024-11-12 13:59:37 +00:00
import "fmt"
import "io"
import "net"
2024-11-15 15:03:42 +00:00
import "net/http"
2024-11-12 13:59:37 +00:00
import "sync"
import "sync/atomic"
2024-11-13 14:14:43 +00:00
import "time"
2024-11-12 13:59:37 +00:00
//import "github.com/google/uuid"
import "google.golang.org/grpc"
import "google.golang.org/grpc/credentials/insecure"
const PTC_LIMIT = 8192
type PacketStreamClient grpc . BidiStreamingClient [ Packet , Packet ]
2024-11-13 14:14:43 +00:00
type ServerConnMap = map [ net . Addr ] * ServerConn
2024-11-12 13:59:37 +00:00
type ClientPeerConnMap = map [ uint32 ] * ClientPeerConn
type ClientRouteMap = map [ uint32 ] * ClientRoute
2024-11-19 17:47:58 +00:00
type ClientPeerCancelFuncMap = map [ uint32 ] context . CancelFunc
2024-11-12 13:59:37 +00:00
// --------------------------------------------------------------------
type ClientConfig struct {
2024-11-23 03:30:23 +00:00
ServerAddr string
PeerAddrs [ ] string
2024-11-12 13:59:37 +00:00
}
type Client struct {
2024-11-15 15:03:42 +00:00
ctx context . Context
ctx_cancel context . CancelFunc
tlscfg * tls . Config
2024-11-12 13:59:37 +00:00
2024-11-23 03:30:23 +00:00
ext_svcs [ ] Service
2024-11-15 15:03:42 +00:00
ctl * http . Server // control server
2024-11-12 13:59:37 +00:00
2024-11-15 15:03:42 +00:00
cts_mtx sync . Mutex
cts_map ServerConnMap
2024-11-12 13:59:37 +00:00
2024-11-15 15:03:42 +00:00
wg sync . WaitGroup
stop_req atomic . Bool
stop_chan chan bool
2024-11-23 05:49:04 +00:00
log Logger
2024-11-15 15:03:42 +00:00
}
2024-11-12 13:59:37 +00:00
type ClientPeerConn struct {
route * ClientRoute
conn_id uint32
2024-11-19 15:35:58 +00:00
conn * net . TCPConn
2024-11-12 13:59:37 +00:00
remot_conn_id uint32
addr string // peer address
2024-11-19 15:31:14 +00:00
2024-11-13 14:14:43 +00:00
stop_chan chan bool
2024-11-19 15:31:14 +00:00
stop_req atomic . Bool
server_peer_eof atomic . Bool
2024-11-12 13:59:37 +00:00
}
// client connection to server
type ServerConn struct {
2024-11-19 15:31:14 +00:00
cli * Client
cfg * ClientConfig
saddr * net . TCPAddr // server address that is connected to
2024-11-13 14:14:43 +00:00
2024-11-19 15:31:14 +00:00
conn * grpc . ClientConn // grpc connection to the server
hdc HoduClient
psc * GuardedPacketStreamClient // guarded grpc stream
2024-11-12 13:59:37 +00:00
2024-11-19 15:31:14 +00:00
s_seed Seed
c_seed Seed
route_mtx sync . Mutex
2024-11-13 14:14:43 +00:00
route_map ClientRouteMap
2024-11-19 15:31:14 +00:00
route_wg sync . WaitGroup
2024-11-12 13:59:37 +00:00
2024-11-19 15:31:14 +00:00
stop_req atomic . Bool
2024-11-13 14:14:43 +00:00
stop_chan chan bool
2024-11-12 13:59:37 +00:00
}
type ClientRoute struct {
cts * ServerConn
id uint32
peer_addr * net . TCPAddr
proto ROUTE_PROTO
2024-11-19 17:47:58 +00:00
ptc_mtx sync . Mutex
ptc_map ClientPeerConnMap
ptc_cancel_map ClientPeerCancelFuncMap
//ptc_limit int
//ptc_last_id uint32
2024-11-12 13:59:37 +00:00
ptc_wg sync . WaitGroup
2024-11-13 14:14:43 +00:00
stop_req atomic . Bool
stop_chan chan bool
2024-11-12 13:59:37 +00:00
}
2024-11-15 15:03:42 +00:00
type ClientCtlParamServer struct {
ServerAddr string ` json:"server-addr" `
PeerAddrs [ ] string ` json:"peer-addrs" `
}
2024-11-12 13:59:37 +00:00
2024-11-18 13:25:59 +00:00
type GuardedPacketStreamClient struct {
mtx sync . Mutex
//psc Hodu_PacketStreamClient
Hodu_PacketStreamClient
}
// ------------------------------------
func ( g * GuardedPacketStreamClient ) Send ( data * Packet ) error {
g . mtx . Lock ( )
defer g . mtx . Unlock ( )
//return g.psc.Send(data)
return g . Hodu_PacketStreamClient . Send ( data )
}
/ * func ( g * GuardedPacketStreamClient ) Recv ( ) ( * Packet , error ) {
return g . psc . Recv ( )
}
func ( g * GuardedPacketStreamClient ) Context ( ) context . Context {
return g . psc . Context ( )
} * /
2024-11-12 13:59:37 +00:00
// --------------------------------------------------------------------
func NewClientRoute ( cts * ServerConn , id uint32 , addr * net . TCPAddr , proto ROUTE_PROTO ) * ClientRoute {
var r ClientRoute
r . cts = cts
r . id = id
2024-11-19 17:47:58 +00:00
//r.ptc_limit = PTC_LIMIT
2024-11-12 13:59:37 +00:00
r . ptc_map = make ( ClientPeerConnMap )
2024-11-19 17:47:58 +00:00
r . ptc_cancel_map = make ( ClientPeerCancelFuncMap )
//r.ptc_last_id = 0
2024-11-12 13:59:37 +00:00
r . proto = proto
r . peer_addr = addr
2024-11-13 14:14:43 +00:00
r . stop_req . Store ( false )
2024-11-24 11:39:51 +00:00
r . stop_chan = make ( chan bool , 8 )
2024-11-12 13:59:37 +00:00
2024-11-23 05:49:04 +00:00
return & r
2024-11-12 13:59:37 +00:00
}
2024-11-16 15:47:15 +00:00
func ( r * ClientRoute ) RunTask ( wg * sync . WaitGroup ) {
2024-11-12 13:59:37 +00:00
// this task on the route object isn't actually necessary.
2024-11-13 14:14:43 +00:00
// most useful works are triggered by ReportEvent() and done by ConnectToPeer()
2024-11-16 15:47:15 +00:00
defer wg . Done ( )
2024-11-13 14:14:43 +00:00
main_loop :
for {
select {
case <- r . stop_chan :
break main_loop
}
}
2024-11-16 15:47:15 +00:00
r . ptc_wg . Wait ( ) // wait for all peer tasks are finished
2024-11-13 14:14:43 +00:00
fmt . Printf ( "*** End fo Client Roue Task\n" )
2024-11-12 13:59:37 +00:00
}
2024-11-13 14:14:43 +00:00
func ( r * ClientRoute ) ReqStop ( ) {
if r . stop_req . CompareAndSwap ( false , true ) {
var ptc * ClientPeerConn
for _ , ptc = range r . ptc_map {
ptc . ReqStop ( )
}
r . stop_chan <- true
}
2024-11-23 05:49:04 +00:00
fmt . Printf ( "*** Sent stop request to Route..\n" )
2024-11-12 13:59:37 +00:00
}
2024-11-19 17:47:58 +00:00
func ( r * ClientRoute ) ConnectToPeer ( pts_id uint32 , wg * sync . WaitGroup ) {
2024-11-12 13:59:37 +00:00
var err error
2024-11-13 14:14:43 +00:00
var conn net . Conn
2024-11-19 15:35:58 +00:00
var real_conn * net . TCPConn
2024-11-12 13:59:37 +00:00
var ptc * ClientPeerConn
2024-11-13 14:14:43 +00:00
var d net . Dialer
var ctx context . Context
2024-11-19 17:47:58 +00:00
var cancel context . CancelFunc
2024-11-19 15:35:58 +00:00
var ok bool
2024-11-13 14:14:43 +00:00
2024-11-19 17:47:58 +00:00
defer wg . Done ( )
2024-11-13 14:14:43 +00:00
// TODO: make timeuot value configurable
// TODO: fire the cancellation function upon stop request???
2024-11-19 17:47:58 +00:00
ctx , cancel = context . WithTimeout ( r . cts . cli . ctx , 10 * time . Second )
r . ptc_mtx . Lock ( )
r . ptc_cancel_map [ pts_id ] = cancel
r . ptc_mtx . Unlock ( )
2024-11-13 14:14:43 +00:00
d . LocalAddr = nil // TOOD: use this if local address is specified
2024-11-23 05:49:04 +00:00
conn , err = d . DialContext ( ctx , "tcp" , r . peer_addr . String ( ) )
2024-11-19 17:47:58 +00:00
r . ptc_mtx . Lock ( )
delete ( r . ptc_cancel_map , pts_id )
r . ptc_mtx . Unlock ( )
2024-11-12 13:59:37 +00:00
if err != nil {
2024-11-17 05:57:56 +00:00
// TODO: make send peer started failure mesage?
2024-11-12 13:59:37 +00:00
fmt . Printf ( "failed to connect to %s - %s\n" , r . peer_addr . String ( ) , err . Error ( ) )
2024-11-19 15:48:02 +00:00
goto peer_aborted
2024-11-12 13:59:37 +00:00
}
2024-11-19 15:35:58 +00:00
real_conn , ok = conn . ( * net . TCPConn )
if ! ok {
fmt . Printf ( "not tcp connection - %s\n" , err . Error ( ) )
2024-11-19 15:48:02 +00:00
goto peer_aborted
2024-11-19 15:35:58 +00:00
}
ptc , err = r . AddNewClientPeerConn ( real_conn , pts_id )
2024-11-12 13:59:37 +00:00
if err != nil {
// TODO: logging
2024-11-17 05:57:56 +00:00
// TODO: make send peer started failure mesage?
2024-11-12 13:59:37 +00:00
fmt . Printf ( "YYYYYYYY - %s\n" , err . Error ( ) )
2024-11-19 15:48:02 +00:00
goto peer_aborted
2024-11-12 13:59:37 +00:00
}
fmt . Printf ( "STARTED NEW SERVER PEER STAK\n" )
2024-11-17 05:57:56 +00:00
err = r . cts . psc . Send ( MakePeerStartedPacket ( r . id , ptc . conn_id ) )
if err != nil {
fmt . Printf ( "CLOSING NEW SERVER PEER STAK - %s\n" , err . Error ( ) )
2024-11-19 15:48:02 +00:00
goto peer_aborted
2024-11-17 05:57:56 +00:00
}
2024-11-12 13:59:37 +00:00
2024-11-19 17:47:58 +00:00
wg . Add ( 1 )
go ptc . RunTask ( wg )
2024-11-19 15:48:02 +00:00
return
peer_aborted :
if conn != nil {
conn . Close ( )
err = r . cts . psc . Send ( MakePeerAbortedPacket ( r . id , ptc . conn_id ) )
if err != nil {
// TODO: logging
}
}
2024-11-12 13:59:37 +00:00
}
2024-11-19 15:31:14 +00:00
func ( r * ClientRoute ) DisconnectFromPeer ( pts_id uint32 ) error {
var ptc * ClientPeerConn
2024-11-19 17:47:58 +00:00
var cancel context . CancelFunc
2024-11-19 15:31:14 +00:00
var ok bool
r . ptc_mtx . Lock ( )
2024-11-19 17:47:58 +00:00
cancel , ok = r . ptc_cancel_map [ pts_id ]
if ok {
fmt . Printf ( "~~~~~~~~~~~~~~~~ cancelling.....\n" )
cancel ( )
}
2024-11-19 15:31:14 +00:00
ptc , ok = r . ptc_map [ pts_id ]
if ! ok {
r . ptc_mtx . Unlock ( )
return fmt . Errorf ( "non-existent connection id - %u" , pts_id )
}
r . ptc_mtx . Unlock ( )
ptc . ReqStop ( )
return nil
}
func ( r * ClientRoute ) CloseWriteToPeer ( pts_id uint32 ) error {
var ptc * ClientPeerConn
var ok bool
r . ptc_mtx . Lock ( )
ptc , ok = r . ptc_map [ pts_id ]
if ! ok {
r . ptc_mtx . Unlock ( )
return fmt . Errorf ( "non-existent connection id - %u" , pts_id )
}
r . ptc_mtx . Unlock ( )
ptc . CloseWrite ( )
return nil
}
2024-11-12 13:59:37 +00:00
func ( r * ClientRoute ) ReportEvent ( pts_id uint32 , event_type PACKET_KIND , event_data [ ] byte ) error {
2024-11-19 15:31:14 +00:00
var err error
2024-11-12 13:59:37 +00:00
switch event_type {
case PACKET_KIND_PEER_STARTED :
2024-11-19 15:31:14 +00:00
fmt . Printf ( "GOT PEER STARTD . CONENCT TO CLIENT_SIDE PEER\n" )
2024-11-19 17:47:58 +00:00
r . ptc_wg . Add ( 1 )
go r . ConnectToPeer ( pts_id , & r . ptc_wg )
2024-11-12 13:59:37 +00:00
2024-11-19 15:48:02 +00:00
case PACKET_KIND_PEER_ABORTED :
fallthrough
2024-11-19 15:31:14 +00:00
case PACKET_KIND_PEER_STOPPED :
fmt . Printf ( "GOT PEER STOPPED . DISCONNECTION FROM CLIENT_SIDE PEER\n" )
err = r . DisconnectFromPeer ( pts_id )
if err != nil {
// TODO:
}
case PACKET_KIND_PEER_EOF :
fmt . Printf ( "GOT PEER EOF. REMEMBER EOF\n" )
err = r . CloseWriteToPeer ( pts_id )
if err != nil {
// TODO:
}
2024-11-17 05:57:56 +00:00
case PACKET_KIND_PEER_DATA :
var ptc * ClientPeerConn
var ok bool
var err error
ptc , ok = r . ptc_map [ pts_id ]
if ok {
_ , err = ptc . conn . Write ( event_data )
return err
} else {
}
2024-11-12 13:59:37 +00:00
// TODO: other types
}
return nil
}
// --------------------------------------------------------------------
2024-11-16 15:47:15 +00:00
func NewServerConn ( c * Client , addr * net . TCPAddr , cfg * ClientConfig ) * ServerConn {
var cts ServerConn
cts . cli = c
cts . route_map = make ( ClientRouteMap )
cts . saddr = addr
cts . cfg = cfg
cts . stop_req . Store ( false )
2024-11-24 11:39:51 +00:00
cts . stop_chan = make ( chan bool , 8 )
2024-11-16 15:47:15 +00:00
// the actual connection to the server is established in the main task function
// The cts.conn, cts.hdc, cts.psc fields are left unassigned here.
return & cts
}
2024-11-12 13:59:37 +00:00
func ( cts * ServerConn ) AddNewClientRoute ( route_id uint32 , addr * net . TCPAddr , proto ROUTE_PROTO ) ( * ClientRoute , error ) {
var r * ClientRoute
cts . route_mtx . Lock ( )
2024-11-13 14:14:43 +00:00
if cts . route_map [ route_id ] != nil {
2024-11-12 13:59:37 +00:00
cts . route_mtx . Unlock ( )
return nil , fmt . Errorf ( "existent route id - %d" , route_id )
}
r = NewClientRoute ( cts , route_id , addr , proto )
2024-11-13 14:14:43 +00:00
cts . route_map [ route_id ] = r
2024-11-12 13:59:37 +00:00
cts . route_mtx . Unlock ( )
2024-11-13 14:14:43 +00:00
fmt . Printf ( "added client route.... %d -> %d\n" , route_id , len ( cts . route_map ) )
2024-11-16 15:47:15 +00:00
cts . route_wg . Add ( 1 )
go r . RunTask ( & cts . route_wg )
2024-11-12 13:59:37 +00:00
return r , nil
}
2024-11-24 11:39:51 +00:00
func ( cts * ServerConn ) RemoveClientRoute ( route * ClientRoute ) error {
2024-11-12 13:59:37 +00:00
var r * ClientRoute
var ok bool
cts . route_mtx . Lock ( )
2024-11-24 11:39:51 +00:00
r , ok = cts . route_map [ route . id ]
2024-11-12 13:59:37 +00:00
if ( ! ok ) {
cts . route_mtx . Unlock ( )
2024-11-24 11:39:51 +00:00
return fmt . Errorf ( "non-existent route id - %d" , route . id )
2024-11-12 13:59:37 +00:00
}
2024-11-24 11:39:51 +00:00
if r != route {
cts . route_mtx . Unlock ( )
return fmt . Errorf ( "non-existent route id - %d" , route . id )
}
delete ( cts . route_map , route . id )
2024-11-12 13:59:37 +00:00
cts . route_mtx . Unlock ( )
2024-11-24 11:39:51 +00:00
r . ReqStop ( )
2024-11-23 05:49:04 +00:00
return nil
2024-11-12 13:59:37 +00:00
}
2024-11-24 11:39:51 +00:00
func ( cts * ServerConn ) RemoveClientRouteById ( route_id uint32 ) error {
2024-11-16 15:47:15 +00:00
var r * ClientRoute
2024-11-24 11:39:51 +00:00
var ok bool
2024-11-16 15:47:15 +00:00
cts . route_mtx . Lock ( )
2024-11-24 11:39:51 +00:00
r , ok = cts . route_map [ route_id ]
if ( ! ok ) {
cts . route_mtx . Unlock ( )
return fmt . Errorf ( "non-existent route id - %d" , route_id )
2024-11-16 15:47:15 +00:00
}
2024-11-24 11:39:51 +00:00
delete ( cts . route_map , route_id )
2024-11-16 15:47:15 +00:00
cts . route_mtx . Unlock ( )
2024-11-24 11:39:51 +00:00
r . ReqStop ( )
return nil
2024-11-16 15:47:15 +00:00
}
2024-11-12 13:59:37 +00:00
func ( cts * ServerConn ) AddClientRoutes ( peer_addrs [ ] string ) error {
var i int
var v string
var addr * net . TCPAddr
var proto ROUTE_PROTO
var r * ClientRoute
var err error
for i , v = range peer_addrs {
2024-11-23 05:49:04 +00:00
addr , err = net . ResolveTCPAddr ( NET_TYPE_TCP , v ) // Make this interruptable
2024-11-12 13:59:37 +00:00
if err != nil {
return fmt . Errorf ( "unable to resovle %s - %s" , v , err . Error ( ) )
}
if addr . IP . To4 ( ) != nil {
proto = ROUTE_PROTO_TCP4
} else {
proto = ROUTE_PROTO_TCP6
}
_ , err = cts . AddNewClientRoute ( uint32 ( i ) , addr , proto )
if err != nil {
2024-11-16 15:47:15 +00:00
return fmt . Errorf ( "unable to add client route for %s - %s" , addr , err . Error ( ) )
2024-11-12 13:59:37 +00:00
}
}
2024-11-24 11:39:51 +00:00
// TODO: mutex protection
2024-11-13 14:14:43 +00:00
for _ , r = range cts . route_map {
err = cts . psc . Send ( MakeRouteStartPacket ( r . id , r . proto , addr . String ( ) ) )
2024-11-12 13:59:37 +00:00
if err != nil {
2024-11-24 11:39:51 +00:00
// TODO: remove all routes???
2024-11-12 13:59:37 +00:00
return fmt . Errorf ( "unable to send route-start packet - %s" , err . Error ( ) )
}
}
2024-11-23 05:49:04 +00:00
return nil
2024-11-12 13:59:37 +00:00
}
2024-11-24 11:39:51 +00:00
func ( cts * ServerConn ) RemoveClientRoutes ( ) {
var r * ClientRoute
var id uint32
cts . route_mtx . Lock ( )
for _ , r = range cts . route_map {
r . ReqStop ( )
}
for id , r = range cts . route_map {
delete ( cts . route_map , id )
}
cts . route_map = make ( ClientRouteMap )
cts . route_mtx . Unlock ( )
// TODO: mutex protection?
for _ , r = range cts . route_map {
cts . psc . Send ( MakeRouteStopPacket ( r . id , r . proto , r . peer_addr . String ( ) ) )
}
}
2024-11-13 14:14:43 +00:00
func ( cts * ServerConn ) ReqStop ( ) {
if cts . stop_req . CompareAndSwap ( false , true ) {
var r * ClientRoute
2024-11-16 15:47:15 +00:00
cts . route_mtx . Lock ( )
2024-11-24 11:39:51 +00:00
for _ , r = range cts . route_map {
cts . psc . Send ( MakeRouteStopPacket ( r . id , r . proto , r . peer_addr . String ( ) ) ) // don't care about failure
2024-11-13 14:14:43 +00:00
r . ReqStop ( )
2024-11-12 13:59:37 +00:00
}
2024-11-16 15:47:15 +00:00
cts . route_mtx . Unlock ( )
2024-11-12 13:59:37 +00:00
2024-11-13 14:14:43 +00:00
cts . stop_chan <- true
2024-11-12 13:59:37 +00:00
}
}
2024-11-12 17:20:25 +00:00
2024-11-13 14:14:43 +00:00
func ( cts * ServerConn ) RunTask ( wg * sync . WaitGroup ) {
var conn * grpc . ClientConn = nil
var hdc HoduClient
var psc PacketStreamClient
2024-11-16 15:47:15 +00:00
var slpctx context . Context
2024-11-19 15:31:14 +00:00
var c_seed Seed
var s_seed * Seed
2024-11-12 13:59:37 +00:00
var err error
2024-11-15 15:03:42 +00:00
defer wg . Done ( ) // arrange to call at the end of this function
2024-11-12 17:20:25 +00:00
2024-11-12 13:59:37 +00:00
// TODO: HANDLE connection timeout..
// ctx, _/*cancel*/ := context.WithTimeout(context.Background(), time.Second)
2024-11-16 15:47:15 +00:00
start_over :
2024-11-24 11:39:51 +00:00
cts . cli . log . Write ( "" , LOG_INFO , "Connecting to server %s" , cts . saddr . String ( ) )
2024-11-13 14:14:43 +00:00
conn , err = grpc . NewClient ( cts . saddr . String ( ) , grpc . WithTransportCredentials ( insecure . NewCredentials ( ) ) )
2024-11-12 13:59:37 +00:00
if err != nil {
2024-11-24 11:39:51 +00:00
cts . cli . log . Write ( "" , LOG_ERROR , "Failed to connect to server %s - %s" , cts . saddr . String ( ) , err . Error ( ) )
2024-11-16 15:47:15 +00:00
goto reconnect_to_server
2024-11-12 13:59:37 +00:00
}
2024-11-13 14:14:43 +00:00
hdc = NewHoduClient ( conn )
2024-11-19 15:31:14 +00:00
// seed exchange is for furture expansion of the protocol
// there is nothing to do much about it for now.
c_seed . Version = HODU_VERSION
c_seed . Flags = 0
s_seed , err = hdc . GetSeed ( cts . cli . ctx , & c_seed )
if err != nil {
2024-11-24 11:39:51 +00:00
cts . cli . log . Write ( "" , LOG_ERROR , "Failed to get seed from server %s - %s" , cts . saddr . String ( ) , err . Error ( ) )
2024-11-19 15:31:14 +00:00
goto reconnect_to_server
}
cts . s_seed = * s_seed
cts . c_seed = c_seed
2024-11-16 15:47:15 +00:00
psc , err = hdc . PacketStream ( cts . cli . ctx )
2024-11-12 13:59:37 +00:00
if err != nil {
2024-11-24 11:39:51 +00:00
cts . cli . log . Write ( "" , LOG_ERROR , "Failed to get packet stream from server %s - %s" , cts . saddr . String ( ) , err . Error ( ) )
2024-11-16 15:47:15 +00:00
goto reconnect_to_server
2024-11-12 13:59:37 +00:00
}
2024-11-24 11:39:51 +00:00
cts . cli . log . Write ( "" , LOG_INFO , "Got packet stream from server %s" , cts . saddr . String ( ) )
2024-11-13 14:14:43 +00:00
cts . conn = conn
cts . hdc = hdc
2024-11-18 13:25:59 +00:00
//cts.psc = &GuardedPacketStreamClient{psc: psc}
cts . psc = & GuardedPacketStreamClient { Hodu_PacketStreamClient : psc }
2024-11-12 13:59:37 +00:00
2024-11-13 14:14:43 +00:00
// the connection structure to a server is ready.
// let's add routes to the client-side peers.
2024-11-23 03:30:23 +00:00
err = cts . AddClientRoutes ( cts . cfg . PeerAddrs )
2024-11-12 13:59:37 +00:00
if err != nil {
2024-11-24 11:39:51 +00:00
cts . cli . log . Write ( "" , LOG_INFO , "Failed to add routes to server %s for %v - %s" , cts . saddr . String ( ) , cts . cfg . PeerAddrs , err . Error ( ) )
2024-11-13 14:14:43 +00:00
goto done
2024-11-12 13:59:37 +00:00
}
2024-11-24 11:39:51 +00:00
2024-11-16 15:47:15 +00:00
fmt . Printf ( "[%v]\n" , cts . route_map )
2024-11-12 13:59:37 +00:00
for {
var pkt * Packet
select {
2024-11-13 14:14:43 +00:00
case <- cts . cli . ctx . Done ( ) :
2024-11-24 11:39:51 +00:00
fmt . Printf ( "context doine... error - %s\n" , cts . cli . ctx . Err ( ) . Error ( ) )
2024-11-16 15:47:15 +00:00
goto done
2024-11-13 14:14:43 +00:00
case <- cts . stop_chan :
2024-11-16 15:47:15 +00:00
goto done
2024-11-13 14:14:43 +00:00
2024-11-12 13:59:37 +00:00
default :
2024-11-17 05:57:56 +00:00
// no other case is ready. run the code below select.
2024-11-12 13:59:37 +00:00
// without the default case, the select construct would block
}
2024-11-13 14:14:43 +00:00
pkt , err = psc . Recv ( )
2024-11-12 13:59:37 +00:00
if err != nil {
2024-11-24 11:39:51 +00:00
if errors . Is ( err , io . EOF ) {
goto reconnect_to_server
} else {
cts . cli . log . Write ( "" , LOG_INFO , "Failed to receive packet form server %s - %s" , cts . saddr . String ( ) , err . Error ( ) )
goto reconnect_to_server
}
2024-11-12 13:59:37 +00:00
}
switch pkt . Kind {
case PACKET_KIND_ROUTE_STARTED :
2024-11-13 14:14:43 +00:00
// the server side managed to set up the route the client requested
2024-11-12 13:59:37 +00:00
var x * Packet_Route
var ok bool
x , ok = pkt . U . ( * Packet_Route )
if ok {
2024-11-23 05:49:04 +00:00
fmt . Printf ( "SERVER LISTENING ON %s\n" , x . Route . AddrStr )
2024-11-12 13:59:37 +00:00
err = cts . ReportEvent ( x . Route . RouteId , 0 , pkt . Kind , nil )
if err != nil {
// TODO:
} else {
2024-11-13 14:14:43 +00:00
// TODO:
2024-11-12 13:59:37 +00:00
}
} else {
// TODO: send invalid request... or simply keep quiet?
}
case PACKET_KIND_ROUTE_STOPPED :
var x * Packet_Route
var ok bool
x , ok = pkt . U . ( * Packet_Route )
if ok {
err = cts . ReportEvent ( x . Route . RouteId , 0 , pkt . Kind , nil )
if err != nil {
// TODO:
} else {
2024-11-13 14:14:43 +00:00
// TODO:
2024-11-12 13:59:37 +00:00
}
} else {
// TODO: send invalid request... or simply keep quiet?
}
case PACKET_KIND_PEER_STARTED :
// 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 {
err = cts . ReportEvent ( x . Peer . RouteId , x . Peer . PeerId , PACKET_KIND_PEER_STARTED , nil )
if err != nil {
// TODO:
} else {
2024-11-13 14:14:43 +00:00
// TODO:
2024-11-12 13:59:37 +00:00
}
} else {
// TODO
}
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 {
err = cts . ReportEvent ( x . Peer . RouteId , x . Peer . PeerId , PACKET_KIND_PEER_STOPPED , nil )
if err != nil {
// TODO:
} else {
2024-11-13 14:14:43 +00:00
// TODO:
2024-11-12 13:59:37 +00:00
}
} else {
// TODO
}
2024-11-19 15:31:14 +00:00
case PACKET_KIND_PEER_EOF :
var x * Packet_Peer
var ok bool
x , ok = pkt . U . ( * Packet_Peer )
if ok {
err = cts . ReportEvent ( x . Peer . RouteId , x . Peer . PeerId , PACKET_KIND_PEER_EOF , nil )
if err != nil {
// TODO:
} else {
// TODO:
}
} else {
// TODO
}
2024-11-12 13:59:37 +00:00
case PACKET_KIND_PEER_DATA :
// the connection from the client to a peer has been established
2024-11-24 11:39:51 +00:00
//fmt.Printf ("**** GOT PEER DATA\n")
2024-11-12 13:59:37 +00:00
var x * Packet_Data
var ok bool
x , ok = pkt . U . ( * Packet_Data )
if ok {
err = cts . ReportEvent ( x . Data . RouteId , x . Data . PeerId , PACKET_KIND_PEER_DATA , x . Data . Data )
if err != nil {
2024-11-17 05:57:56 +00:00
fmt . Printf ( "failed to report event - %s\n" , err . Error ( ) )
2024-11-12 13:59:37 +00:00
// TODO:
} else {
2024-11-13 14:14:43 +00:00
// TODO:
2024-11-12 13:59:37 +00:00
}
} else {
// TODO
}
}
}
2024-11-13 14:14:43 +00:00
done :
2024-11-24 11:39:51 +00:00
cts . cli . log . Write ( "" , LOG_INFO , "Disconnected from server %s" , cts . saddr . String ( ) )
2024-11-16 15:47:15 +00:00
cts . RemoveClientRoutes ( )
2024-11-24 11:39:51 +00:00
if conn != nil { conn . Close ( ) }
2024-11-16 15:47:15 +00:00
cts . route_wg . Wait ( ) // wait until all route tasks are finished
return
reconnect_to_server :
cts . RemoveClientRoutes ( )
2024-11-24 11:39:51 +00:00
if conn != nil { conn . Close ( ) }
// wait for 2 seconds
slpctx , _ = context . WithTimeout ( cts . cli . ctx , 2 * time . Second )
2024-11-16 15:47:15 +00:00
select {
case <- cts . cli . ctx . Done ( ) :
fmt . Printf ( "context doine... error - %s\n" , cts . cli . ctx . Err ( ) . Error ( ) )
goto done
case <- cts . stop_chan :
goto done
case <- slpctx . Done ( ) :
// do nothing
}
2024-11-24 11:39:51 +00:00
goto start_over // and reconnect
2024-11-13 14:14:43 +00:00
}
2024-11-12 17:20:25 +00:00
2024-11-13 14:14:43 +00:00
func ( cts * ServerConn ) ReportEvent ( route_id uint32 , pts_id uint32 , event_type PACKET_KIND , event_data [ ] byte ) error {
var r * ClientRoute
var ok bool
cts . route_mtx . Lock ( )
r , ok = cts . route_map [ route_id ]
if ( ! ok ) {
cts . route_mtx . Unlock ( )
return fmt . Errorf ( "non-existent route id - %d" , route_id )
}
cts . route_mtx . Unlock ( )
return r . ReportEvent ( pts_id , event_type , event_data )
}
// --------------------------------------------------------------------
2024-11-19 15:35:58 +00:00
func ( r * ClientRoute ) AddNewClientPeerConn ( c * net . TCPConn , pts_id uint32 ) ( * ClientPeerConn , error ) {
2024-11-13 14:14:43 +00:00
var ptc * ClientPeerConn
2024-11-19 15:31:14 +00:00
//var ok bool
//var start_id uint32
2024-11-13 14:14:43 +00:00
r . ptc_mtx . Lock ( )
defer r . ptc_mtx . Unlock ( )
2024-11-19 15:31:14 +00:00
/ *
2024-11-13 14:14:43 +00:00
if len ( r . ptc_map ) >= r . ptc_limit {
return nil , fmt . Errorf ( "peer-to-client connection table full" )
}
start_id = r . ptc_last_id
for {
_ , ok = r . ptc_map [ r . ptc_last_id ]
if ! ok {
break
}
r . ptc_last_id ++
if r . ptc_last_id == start_id {
// unlikely to happen but it cycled through the whole range.
return nil , fmt . Errorf ( "failed to assign peer-to-table connection id" )
}
}
ptc = NewClientPeerConn ( r , c , r . ptc_last_id )
2024-11-19 15:31:14 +00:00
* /
ptc = NewClientPeerConn ( r , c , pts_id )
2024-11-13 14:14:43 +00:00
r . ptc_map [ ptc . conn_id ] = ptc
2024-11-19 15:31:14 +00:00
//r.ptc_last_id++
2024-11-13 14:14:43 +00:00
return ptc , nil
}
2024-11-16 15:47:15 +00:00
2024-11-13 14:14:43 +00:00
// --------------------------------------------------------------------
2024-11-15 15:03:42 +00:00
2024-11-23 05:49:04 +00:00
func NewClient ( ctx context . Context , listen_on string , logger Logger , tlscfg * tls . Config ) * Client {
2024-11-13 14:14:43 +00:00
var c Client
c . ctx , c . ctx_cancel = context . WithCancel ( ctx )
c . tlscfg = tlscfg
2024-11-23 03:30:23 +00:00
c . ext_svcs = make ( [ ] Service , 0 , 1 )
2024-11-13 14:14:43 +00:00
c . cts_map = make ( ServerConnMap ) // TODO: make it configurable...
c . stop_req . Store ( false )
2024-11-24 11:39:51 +00:00
c . stop_chan = make ( chan bool , 8 )
2024-11-23 05:49:04 +00:00
c . log = logger
2024-11-13 14:14:43 +00:00
2024-11-15 15:03:42 +00:00
c . ctl = & http . Server {
Addr : listen_on ,
Handler : & c ,
}
2024-11-13 14:14:43 +00:00
return & c
}
func ( c * Client ) AddNewServerConn ( addr * net . TCPAddr , cfg * ClientConfig ) ( * ServerConn , error ) {
2024-11-16 15:47:15 +00:00
var cts * ServerConn
2024-11-13 14:14:43 +00:00
var ok bool
2024-11-16 15:47:15 +00:00
cts = NewServerConn ( c , addr , cfg )
2024-11-13 14:14:43 +00:00
c . cts_mtx . Lock ( )
defer c . cts_mtx . Unlock ( )
_ , ok = c . cts_map [ addr ]
if ok {
return nil , fmt . Errorf ( "existing server - %s" , addr . String ( ) )
}
2024-11-23 05:49:04 +00:00
c . cts_map [ addr ] = cts
fmt . Printf ( "ADD total servers %d\n" , len ( c . cts_map ) )
2024-11-16 15:47:15 +00:00
return cts , nil
2024-11-13 14:14:43 +00:00
}
func ( c * Client ) RemoveServerConn ( cts * ServerConn ) {
c . cts_mtx . Lock ( )
delete ( c . cts_map , cts . saddr )
2024-11-23 05:49:04 +00:00
fmt . Printf ( "REMOVE total servers %d\n" , len ( c . cts_map ) )
2024-11-13 14:14:43 +00:00
c . cts_mtx . Unlock ( )
2024-11-12 13:59:37 +00:00
}
func ( c * Client ) ReqStop ( ) {
if c . stop_req . CompareAndSwap ( false , true ) {
2024-11-13 14:14:43 +00:00
var cts * ServerConn
2024-11-15 15:03:42 +00:00
2024-11-24 11:39:51 +00:00
if ( c . ctl != nil ) {
c . ctl . Shutdown ( c . ctx ) // to break c.ctl.ListenAndServe()
}
2024-11-15 15:03:42 +00:00
2024-11-13 14:14:43 +00:00
for _ , cts = range c . cts_map {
cts . ReqStop ( )
}
2024-11-12 13:59:37 +00:00
// TODO: notify the server.. send term command???
2024-11-13 14:14:43 +00:00
c . stop_chan <- true
c . ctx_cancel ( )
2024-11-12 13:59:37 +00:00
}
2024-11-23 05:49:04 +00:00
fmt . Printf ( "*** Sent stop request to client..\n" )
2024-11-12 13:59:37 +00:00
}
2024-11-15 15:03:42 +00:00
func ( c * Client ) ServeHTTP ( w http . ResponseWriter , req * http . Request ) {
var err error
// command handler for the control channel
if req . URL . String ( ) == "/servers" {
switch req . Method {
case http . MethodGet :
goto bad_request // TODO:
case http . MethodPost :
var s ClientCtlParamServer
var cc ClientConfig
err = json . NewDecoder ( req . Body ) . Decode ( & s )
if err != nil {
fmt . Printf ( "failed to decode body - %s\n" , err . Error ( ) )
goto bad_request
}
2024-11-23 03:30:23 +00:00
cc . ServerAddr = s . ServerAddr
cc . PeerAddrs = s . PeerAddrs
c . StartService ( & cc ) // TODO: this can be blocking. do we have to resolve addresses before calling this? also not good because resolution succeed or fail at each attempt. however ok as ServeHTTP itself is in a goroutine?
2024-11-15 15:03:42 +00:00
w . WriteHeader ( http . StatusCreated )
case http . MethodPut :
goto bad_request // TODO:
case http . MethodDelete :
var cts * ServerConn
for _ , cts = range c . cts_map {
cts . ReqStop ( )
}
}
} else {
goto bad_request
}
fmt . Printf ( "[%s][%s][%s]\n" , req . RequestURI , req . URL . String ( ) , req . Method )
return
bad_request :
w . WriteHeader ( http . StatusBadRequest )
return
}
/ *
* POST GET PUT DELETE
* / servers - create new server list all servers bulk update delete all servers
* / servers / 1 - X get server 1 details update server 1 delete server 1
* / servers / 1 / xxx -
* /
2024-11-20 16:11:01 +00:00
func ( c * Client ) RunCtlTask ( wg * sync . WaitGroup ) {
2024-11-15 15:03:42 +00:00
var err error
2024-11-20 16:11:01 +00:00
defer wg . Done ( )
2024-11-15 15:03:42 +00:00
err = c . ctl . ListenAndServe ( )
2024-11-20 16:11:01 +00:00
if ! errors . Is ( err , http . ErrServerClosed ) {
2024-11-15 15:03:42 +00:00
fmt . Printf ( "------------http server error - %s\n" , err . Error ( ) )
} else {
fmt . Printf ( "********* http server ended\n" )
}
}
2024-11-23 03:30:23 +00:00
func ( c * Client ) StartCtlService ( ) {
c . wg . Add ( 1 )
go c . RunCtlTask ( & c . wg )
}
func ( c * Client ) RunTask ( wg * sync . WaitGroup ) {
// just a place holder to pacify the Service interface
// StartService() calls cts.RunTask() instead.
}
2024-11-13 14:14:43 +00:00
// naming convention:
// RunService - returns after having executed another go routine
// RunTask - supposed to be detached as a go routine
2024-11-23 03:30:23 +00:00
func ( c * Client ) StartService ( data interface { } ) {
2024-11-13 14:14:43 +00:00
var saddr * net . TCPAddr
var cts * ServerConn
var err error
2024-11-23 03:30:23 +00:00
var cfg * ClientConfig
var ok bool
cfg , ok = data . ( * ClientConfig )
if ! ok {
fmt . Printf ( "invalid configuration given" )
return
}
2024-11-13 14:14:43 +00:00
2024-11-23 03:30:23 +00:00
if len ( cfg . PeerAddrs ) < 0 || len ( cfg . PeerAddrs ) > int ( ^ uint16 ( 0 ) ) { // TODO: change this check... not really right...
2024-11-13 14:14:43 +00:00
fmt . Printf ( "no peer addresses or too many peer addresses" )
return
}
2024-11-23 05:49:04 +00:00
saddr , err = net . ResolveTCPAddr ( NET_TYPE_TCP , cfg . ServerAddr ) // TODO: make this interruptable...
2024-11-13 14:14:43 +00:00
if err != nil {
2024-11-23 03:30:23 +00:00
fmt . Printf ( "unable to resolve %s - %s" , cfg . ServerAddr , err . Error ( ) )
2024-11-13 14:14:43 +00:00
return
}
cts , err = c . AddNewServerConn ( saddr , cfg )
if err != nil {
2024-11-23 03:30:23 +00:00
fmt . Printf ( "unable to add server connection structure to %s - %s" , cfg . ServerAddr , err . Error ( ) )
2024-11-13 14:14:43 +00:00
return
}
c . wg . Add ( 1 )
go cts . RunTask ( & c . wg )
}
2024-11-23 03:30:23 +00:00
func ( c * Client ) StartExtService ( svc Service , data interface { } ) {
c . ext_svcs = append ( c . ext_svcs , svc )
c . wg . Add ( 1 )
go svc . RunTask ( & c . wg )
2024-11-13 14:14:43 +00:00
}
2024-11-12 17:20:25 +00:00
2024-11-23 03:30:23 +00:00
func ( c * Client ) StopServices ( ) {
var ext_svc Service
c . ReqStop ( )
for _ , ext_svc = range c . ext_svcs {
ext_svc . StopServices ( )
2024-11-12 17:20:25 +00:00
}
}
2024-11-23 03:30:23 +00:00
func ( c * Client ) WaitForTermination ( ) {
c . wg . Wait ( )
2024-11-12 13:59:37 +00:00
}
2024-11-23 05:49:04 +00:00
2024-11-23 11:13:07 +00:00
func ( c * Client ) WriteLog ( id string , level LogLevel , fmtstr string , args ... interface { } ) {
c . log . Write ( id , level , fmtstr , args ... )
2024-11-23 05:49:04 +00:00
}