reorganized the source to place the resuable code under the hodu package and keep the command entry point in the main package under the cmd directory

This commit is contained in:
hyung-hwan 2024-11-23 12:30:23 +09:00
parent 9d7a843b4c
commit a78a0a4fc4
11 changed files with 353 additions and 304 deletions

View File

@ -2,4 +2,4 @@ all:
protoc --go_out=. --go_opt=paths=source_relative \ protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \ --go-grpc_out=. --go-grpc_opt=paths=source_relative \
hodu.proto hodu.proto
go build -x -o hodu go build -x -o hodu cmd/main.go

View File

@ -1,4 +1,4 @@
package main package hodu
import "fmt" import "fmt"
import "net" import "net"

161
client.go
View File

@ -1,22 +1,17 @@
package main package hodu
//import "bufio" //import "bufio"
import "context" import "context"
import "crypto/tls" import "crypto/tls"
import "crypto/x509"
import "encoding/json" import "encoding/json"
import "errors" import "errors"
import "fmt" import "fmt"
import "io" import "io"
import "log"
import "net" import "net"
import "net/http" import "net/http"
import "os"
import "os/signal"
import "sync" import "sync"
import "sync/atomic" import "sync/atomic"
import "syscall"
import "time" import "time"
//import "github.com/google/uuid" //import "github.com/google/uuid"
@ -34,8 +29,8 @@ type ClientPeerCancelFuncMap = map[uint32]context.CancelFunc
// -------------------------------------------------------------------- // --------------------------------------------------------------------
type ClientConfig struct { type ClientConfig struct {
server_addr string ServerAddr string
peer_addrs []string PeerAddrs []string
} }
type Client struct { type Client struct {
@ -43,6 +38,7 @@ type Client struct {
ctx_cancel context.CancelFunc ctx_cancel context.CancelFunc
tlscfg *tls.Config tlscfg *tls.Config
ext_svcs []Service
ctl *http.Server // control server ctl *http.Server // control server
cts_mtx sync.Mutex cts_mtx sync.Mutex
@ -467,7 +463,7 @@ fmt.Printf ("Connecting GRPC to [%s]\n", cts.saddr.String())
conn, err = grpc.NewClient(cts.saddr.String(), grpc.WithTransportCredentials(insecure.NewCredentials())) conn, err = grpc.NewClient(cts.saddr.String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil { if err != nil {
// TODO: logging // TODO: logging
fmt.Printf("ERROR: unable to make grpc client to %s - %s\n", cts.cfg.server_addr, err.Error()) fmt.Printf("ERROR: unable to make grpc client to %s - %s\n", cts.cfg.ServerAddr, err.Error())
goto reconnect_to_server goto reconnect_to_server
} }
@ -479,7 +475,7 @@ fmt.Printf ("Connecting GRPC to [%s]\n", cts.saddr.String())
c_seed.Flags = 0 c_seed.Flags = 0
s_seed, err = hdc.GetSeed(cts.cli.ctx, &c_seed) s_seed, err = hdc.GetSeed(cts.cli.ctx, &c_seed)
if err != nil { if err != nil {
fmt.Printf("ERROR: unable to get seed from %s - %s\n", cts.cfg.server_addr, err.Error()) fmt.Printf("ERROR: unable to get seed from %s - %s\n", cts.cfg.ServerAddr, err.Error())
goto reconnect_to_server goto reconnect_to_server
} }
cts.s_seed = *s_seed cts.s_seed = *s_seed
@ -498,7 +494,7 @@ fmt.Printf ("Connecting GRPC to [%s]\n", cts.saddr.String())
// the connection structure to a server is ready. // the connection structure to a server is ready.
// let's add routes to the client-side peers. // let's add routes to the client-side peers.
err = cts.AddClientRoutes(cts.cfg.peer_addrs) err = cts.AddClientRoutes(cts.cfg.PeerAddrs)
if err != nil { if err != nil {
fmt.Printf ("ERROR: unable to add routes to client-side peers - %s\n", err.Error()) fmt.Printf ("ERROR: unable to add routes to client-side peers - %s\n", err.Error())
goto done goto done
@ -721,6 +717,7 @@ func NewClient(ctx context.Context, listen_on string, tlscfg *tls.Config) *Clien
c.ctx, c.ctx_cancel = context.WithCancel(ctx) c.ctx, c.ctx_cancel = context.WithCancel(ctx)
c.tlscfg = tlscfg c.tlscfg = tlscfg
c.ext_svcs = make([]Service, 0, 1)
c.cts_map = make(ServerConnMap) // TODO: make it configurable... c.cts_map = make(ServerConnMap) // TODO: make it configurable...
c.stop_req.Store(false) c.stop_req.Store(false)
c.stop_chan = make(chan bool, 1) c.stop_chan = make(chan bool, 1)
@ -793,9 +790,9 @@ func (c *Client) ServeHTTP(w http.ResponseWriter, req *http.Request) {
fmt.Printf ("failed to decode body - %s\n", err.Error()) fmt.Printf ("failed to decode body - %s\n", err.Error())
goto bad_request goto bad_request
} }
cc.server_addr = s.ServerAddr cc.ServerAddr = s.ServerAddr
cc.peer_addrs = s.PeerAddrs cc.PeerAddrs = s.PeerAddrs
c.RunService(&cc) 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?
w.WriteHeader(http.StatusCreated) w.WriteHeader(http.StatusCreated)
case http.MethodPut: case http.MethodPut:
@ -837,28 +834,47 @@ func (c *Client) RunCtlTask(wg *sync.WaitGroup) {
} }
} }
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.
}
// naming convention: // naming convention:
// RunService - returns after having executed another go routine // RunService - returns after having executed another go routine
// RunTask - supposed to be detached as a go routine // RunTask - supposed to be detached as a go routine
func (c *Client) RunService(cfg *ClientConfig) { func (c *Client) StartService(data interface{}) {
var saddr *net.TCPAddr var saddr *net.TCPAddr
var cts *ServerConn var cts *ServerConn
var err error var err error
var cfg *ClientConfig
var ok bool
if len(cfg.peer_addrs) < 0 || len(cfg.peer_addrs) > int(^uint16(0)) { // TODO: change this check... not really right... cfg, ok = data.(*ClientConfig)
if !ok {
fmt.Printf("invalid configuration given")
return
}
if len(cfg.PeerAddrs) < 0 || len(cfg.PeerAddrs) > int(^uint16(0)) { // TODO: change this check... not really right...
fmt.Printf("no peer addresses or too many peer addresses") fmt.Printf("no peer addresses or too many peer addresses")
return return
} }
saddr, err = net.ResolveTCPAddr(NET_TYPE_TCP, cfg.server_addr) saddr, err = net.ResolveTCPAddr(NET_TYPE_TCP, cfg.ServerAddr)
if err != nil { if err != nil {
fmt.Printf("unable to resolve %s - %s", cfg.server_addr, err.Error()) fmt.Printf("unable to resolve %s - %s", cfg.ServerAddr, err.Error())
return return
} }
cts, err = c.AddNewServerConn(saddr, cfg) cts, err = c.AddNewServerConn(saddr, cfg)
if err != nil { if err != nil {
fmt.Printf("unable to add server connection structure to %s - %s", cfg.server_addr, err.Error()) fmt.Printf("unable to add server connection structure to %s - %s", cfg.ServerAddr, err.Error())
return return
} }
@ -866,99 +882,20 @@ func (c *Client) RunService(cfg *ClientConfig) {
go cts.RunTask(&c.wg) go cts.RunTask(&c.wg)
} }
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)
}
func (c *Client) StopServices() {
var ext_svc Service
c.ReqStop()
for _, ext_svc = range c.ext_svcs {
ext_svc.StopServices()
}
}
func (c *Client) WaitForTermination() { func (c *Client) WaitForTermination() {
fmt.Printf ("Waiting for task top stop\n")
// waiting for tasks to stop
c.wg.Wait() c.wg.Wait()
fmt.Printf ("XXXXXXXXXXXX Waiting for task top stop\n")
// TOOD: find a better way to stop the signal handling loop.
// above all the signal handler must not be with a single client,
// but with the whole app.
syscall.Kill(syscall.Getpid(), syscall.SIGTERM) // TODO: find a better to terminate the signal handler...
}
// --------------------------------------------------------------------
func (c *Client) handle_os_signals() {
var sighup_chan chan os.Signal
var sigterm_chan chan os.Signal
var sig os.Signal
defer c.wg.Done()
sighup_chan = make(chan os.Signal, 1)
sigterm_chan = make(chan os.Signal, 1)
signal.Notify(sighup_chan, syscall.SIGHUP)
signal.Notify(sigterm_chan, syscall.SIGTERM, os.Interrupt)
chan_loop:
for {
select {
case <-sighup_chan:
// TODO:
//s.RefreshConfig()
case sig = <-sigterm_chan:
// TODO: get timeout value from config
//c.Shutdown(fmt.Sprintf("termination by signal %s", sig), 3*time.Second)
c.ReqStop()
//log.Debugf("termination by signal %s", sig)
fmt.Printf("termination by signal %s\n", sig)
break chan_loop
}
}
fmt.Printf("end of signal handler\n")
}
// --------------------------------------------------------------------
const rootCert = `-----BEGIN CERTIFICATE-----
MIIB+TCCAZ+gAwIBAgIJAL05LKXo6PrrMAoGCCqGSM49BAMCMFkxCzAJBgNVBAYT
AkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn
aXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNTEyMDgxNDAxMTNa
Fw0yNTEyMDUxNDAxMTNaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0
YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMM
CWxvY2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHGaaHVod0hLOR4d
66xIrtS2TmEmjSFjt+DIEcb6sM9RTKS8TZcdBnEqq8YT7m2sKbV+TEq9Nn7d9pHz
pWG2heWjUDBOMB0GA1UdDgQWBBR0fqrecDJ44D/fiYJiOeBzfoqEijAfBgNVHSME
GDAWgBR0fqrecDJ44D/fiYJiOeBzfoqEijAMBgNVHRMEBTADAQH/MAoGCCqGSM49
BAMCA0gAMEUCIEKzVMF3JqjQjuM2rX7Rx8hancI5KJhwfeKu1xbyR7XaAiEA2UT7
1xOP035EcraRmWPe7tO0LpXgMxlh2VItpc2uc2w=
-----END CERTIFICATE-----
`
func client_main(listen_on string, server_addr string, peer_addrs []string) error {
var c *Client
var cert_pool *x509.CertPool
var tlscfg *tls.Config
var cc ClientConfig
cert_pool = x509.NewCertPool()
ok := cert_pool.AppendCertsFromPEM([]byte(rootCert))
if !ok {
log.Fatal("failed to parse root certificate")
}
tlscfg = &tls.Config{
RootCAs: cert_pool,
ServerName: "localhost",
InsecureSkipVerify: true,
}
c = NewClient(context.Background(), listen_on, tlscfg)
c.wg.Add(1)
go c.handle_os_signals()
c.wg.Add(1)
go c.RunCtlTask(&c.wg) // control channel task
cc.server_addr = server_addr
cc.peer_addrs = peer_addrs
c.RunService(&cc)
c.WaitForTermination()
return nil
} }

249
cmd/main.go Normal file
View File

@ -0,0 +1,249 @@
package main
import "context"
import "crypto/tls"
import "crypto/x509"
import "flag"
import "fmt"
import "hodu"
import "io"
import "log"
import "os"
import "os/signal"
import "strings"
import "sync"
import "syscall"
// --------------------------------------------------------------------
const rootKey = `-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIHg+g2unjA5BkDtXSN9ShN7kbPlbCcqcYdDu+QeV8XWuoAoGCCqGSM49
AwEHoUQDQgAEcZpodWh3SEs5Hh3rrEiu1LZOYSaNIWO34MgRxvqwz1FMpLxNlx0G
cSqrxhPubawptX5MSr02ft32kfOlYbaF5Q==
-----END EC PRIVATE KEY-----
`
const rootCert = `-----BEGIN CERTIFICATE-----
MIIB+TCCAZ+gAwIBAgIJAL05LKXo6PrrMAoGCCqGSM49BAMCMFkxCzAJBgNVBAYT
AkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn
aXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNTEyMDgxNDAxMTNa
Fw0yNTEyMDUxNDAxMTNaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0
YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMM
CWxvY2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHGaaHVod0hLOR4d
66xIrtS2TmEmjSFjt+DIEcb6sM9RTKS8TZcdBnEqq8YT7m2sKbV+TEq9Nn7d9pHz
pWG2heWjUDBOMB0GA1UdDgQWBBR0fqrecDJ44D/fiYJiOeBzfoqEijAfBgNVHSME
GDAWgBR0fqrecDJ44D/fiYJiOeBzfoqEijAMBgNVHRMEBTADAQH/MAoGCCqGSM49
BAMCA0gAMEUCIEKzVMF3JqjQjuM2rX7Rx8hancI5KJhwfeKu1xbyR7XaAiEA2UT7
1xOP035EcraRmWPe7tO0LpXgMxlh2VItpc2uc2w=
-----END CERTIFICATE-----
`
// --------------------------------------------------------------------
type serverLogger struct {
log *log.Logger
}
func (log* serverLogger) Write(level hodu.LogLevel, fmt string, args ...interface{}) {
log.log.Printf(fmt, args...)
}
// --------------------------------------------------------------------
type signal_handler struct {
svc hodu.Service
}
func (sh *signal_handler) RunTask(wg *sync.WaitGroup) {
var sighup_chan chan os.Signal
var sigterm_chan chan os.Signal
var sig os.Signal
defer wg.Done()
sighup_chan = make(chan os.Signal, 1)
sigterm_chan = make(chan os.Signal, 1)
signal.Notify(sighup_chan, syscall.SIGHUP)
signal.Notify(sigterm_chan, syscall.SIGTERM, os.Interrupt)
chan_loop:
for {
select {
case <-sighup_chan:
// TODO:
//svc.ReqReload()
case sig = <-sigterm_chan:
// TODO: get timeout value from config
//c.Shutdown(fmt.Sprintf("termination by signal %s", sig), 3*time.Second)
sh.svc.StopServices()
//log.Debugf("termination by signal %s", sig)
fmt.Printf("termination by signal %s\n", sig)
break chan_loop
}
}
//signal.Reset(syscall.SIGHUP)
//signal.Reset(syscall.SIGTERM)
signal.Stop(sighup_chan)
signal.Stop(sigterm_chan)
fmt.Printf("end of signal handler\n")
}
func (sh *signal_handler) StartService(data interface{}) {
// this isn't actually used standalone..
// if we are to implement it, it must use the wait group for signal handler itself
// however, this service is run through another service.
//
// sh.wg.Add(1)
// go sh.RunTask(&sh.wg)
}
func (sh *signal_handler) StopServices() {
syscall.Kill(syscall.Getpid(), syscall.SIGTERM) // TODO: find a better to terminate the signal handler...
}
func (sh *signal_handler) WaitForTermination() {
// not implemented. see the comment in StartServices()
// sh.wg.Wait()
}
func server_main(laddrs []string) error {
var s *hodu.Server
var err error
var sl serverLogger
var cert tls.Certificate
cert, err = tls.X509KeyPair([]byte(rootCert), []byte(rootKey))
if err != nil {
return fmt.Errorf("ERROR: failed to load key pair - %s\n", err)
}
sl.log = log.Default()
s, err = hodu.NewServer(laddrs, &sl, &tls.Config{Certificates: []tls.Certificate{cert}})
if err != nil {
return fmt.Errorf("ERROR: failed to create new server - %s", err.Error())
}
s.StartService(nil)
s.StartExtService(&signal_handler{svc:s}, nil)
s.WaitForTermination()
return nil
}
// --------------------------------------------------------------------
func client_main(listen_on string, server_addr string, peer_addrs []string) error {
var c *hodu.Client
var cert_pool *x509.CertPool
var tlscfg *tls.Config
var cc hodu.ClientConfig
cert_pool = x509.NewCertPool()
ok := cert_pool.AppendCertsFromPEM([]byte(rootCert))
if !ok {
log.Fatal("failed to parse root certificate")
}
tlscfg = &tls.Config{
RootCAs: cert_pool,
ServerName: "localhost",
InsecureSkipVerify: true,
}
c = hodu.NewClient(context.Background(), listen_on, tlscfg)
cc.ServerAddr = server_addr
cc.PeerAddrs = peer_addrs
c.StartService(&cc)
c.StartCtlService()
c.StartExtService(&signal_handler{svc:c}, nil)
c.WaitForTermination()
return nil
}
func main() {
var err error
var flgs *flag.FlagSet
if len(os.Args) < 2 {
goto wrong_usage
}
if strings.EqualFold(os.Args[1], "server") {
var la []string
la = make([]string, 0)
flgs = flag.NewFlagSet("", flag.ContinueOnError)
flgs.Func("listen-on", "specify a listening address", func(v string) error {
la = append(la, v)
return nil
})
flgs.SetOutput(io.Discard) // prevent usage output
err = flgs.Parse(os.Args[2:])
if err != nil {
fmt.Printf ("ERROR: %s\n", err.Error())
goto wrong_usage
}
if len(la) < 0 || flgs.NArg() > 0 {
goto wrong_usage
}
err = server_main(la)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: server error - %s\n", err.Error())
goto oops
}
} else if strings.EqualFold(os.Args[1], "client") {
var la []string
var sa []string
la = make([]string, 0)
sa = make([]string, 0)
flgs = flag.NewFlagSet("", flag.ContinueOnError)
flgs.Func("listen-on", "specify a control channel address", func(v string) error {
la = append(la, v)
return nil
})
flgs.Func("server", "specify a server address", func(v string) error {
sa = append(sa, v)
return nil
})
flgs.SetOutput(io.Discard)
err = flgs.Parse(os.Args[2:])
if err != nil {
fmt.Printf ("ERROR: %s\n", err.Error())
goto wrong_usage
}
if len(la) != 1 || len(sa) != 1 || flgs.NArg() < 1 {
goto wrong_usage
}
err = client_main(la[0], sa[0], flgs.Args())
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: client error - %s\n", err.Error())
goto oops
}
} else {
goto wrong_usage
}
os.Exit(0)
wrong_usage:
fmt.Fprintf(os.Stderr, "USAGE: %s server --listen-on=addr:port\n", os.Args[0])
fmt.Fprintf(os.Stderr, " %s client --listen-on=addr:port --server=addr:port peer-addr:peer-port\n", os.Args[0])
os.Exit(1)
oops:
os.Exit(1)
}

View File

@ -1,4 +1,4 @@
package main package hodu
import "time" import "time"

27
hodu.go Normal file
View File

@ -0,0 +1,27 @@
package hodu
import "sync"
const HODU_VERSION uint32 = 0x010000
type LogLevel int
const (
LOG_DEBUG LogLevel = iota + 1
LOG_ERROR
LOG_WARN
LOG_INFO
)
type Logger interface {
Write (level LogLevel, fmt string, args ...interface{})
}
type Service interface {
RunTask (wg *sync.WaitGroup) // blocking. run the actual task loop
StartService(data interface{}) // non-blocking. spin up a service. it may be invokded multiple times for multiple instances
StopServices() // non-blocking. send stop request to all services spun up
WaitForTermination() // blocking. must wait until all services are stopped
}
type ExtTask func(svc Service, wg *sync.WaitGroup)

View File

@ -1,6 +1,6 @@
syntax = "proto3"; syntax = "proto3";
option go_package = "./main"; option go_package = "./hodu";
//package hodu; // no idea if it's still important... //package hodu; // no idea if it's still important...

87
main.go
View File

@ -1,87 +0,0 @@
package main
import "flag"
import "fmt"
import "io"
import "os"
import "strings"
func main() {
var err error
var flgs *flag.FlagSet
if len(os.Args) < 2 {
goto wrong_usage
}
if strings.EqualFold(os.Args[1], "server") {
var la []string
la = make([]string, 0)
flgs = flag.NewFlagSet("", flag.ContinueOnError)
flgs.Func("listen-on", "specify a listening address", func(v string) error {
la = append(la, v)
return nil
})
flgs.SetOutput(io.Discard) // prevent usage output
err = flgs.Parse(os.Args[2:])
if err != nil {
fmt.Printf ("ERROR: %s\n", err.Error())
goto wrong_usage
}
if len(la) < 0 || flgs.NArg() > 0 {
goto wrong_usage
}
err = server_main(la)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: server error - %s\n", err.Error())
goto oops
}
} else if strings.EqualFold(os.Args[1], "client") {
var la []string
var sa []string
la = make([]string, 0)
sa = make([]string, 0)
flgs = flag.NewFlagSet("", flag.ContinueOnError)
flgs.Func("listen-on", "specify a control channel address", func(v string) error {
la = append(la, v)
return nil
})
flgs.Func("server", "specify a server address", func(v string) error {
sa = append(sa, v)
return nil
})
flgs.SetOutput(io.Discard)
err = flgs.Parse(os.Args[2:])
if err != nil {
fmt.Printf ("ERROR: %s\n", err.Error())
goto wrong_usage
}
if len(la) != 1 || len(sa) != 1 || flgs.NArg() < 1 {
goto wrong_usage
}
err = client_main(la[0], sa[0], flgs.Args())
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: client error - %s\n", err.Error())
goto oops
}
} else {
goto wrong_usage
}
os.Exit(0)
wrong_usage:
fmt.Fprintf(os.Stderr, "USAGE: %s server --listen-on=addr:port\n", os.Args[0])
fmt.Fprintf(os.Stderr, " %s client --listen-on=addr:port --server=addr:port peer-addr:peer-port\n", os.Args[0])
os.Exit(1)
oops:
os.Exit(1)
}

View File

@ -1,4 +1,4 @@
package main package hodu
func MakeRouteStartPacket(route_id uint32, proto ROUTE_PROTO, addr string) *Packet { func MakeRouteStartPacket(route_id uint32, proto ROUTE_PROTO, addr string) *Packet {

View File

@ -1,4 +1,4 @@
package main package hodu
import "errors" import "errors"
import "fmt" import "fmt"
@ -9,10 +9,10 @@ import "sync/atomic"
import "time" import "time"
type ServerPeerConn struct { type ServerPeerConn struct {
route *ServerRoute route *ServerRoute
conn_id uint32 conn_id uint32
cts *ClientConn cts *ClientConn
conn *net.TCPConn conn *net.TCPConn
stop_chan chan bool stop_chan chan bool
stop_req atomic.Bool stop_req atomic.Bool

113
server.go
View File

@ -1,19 +1,15 @@
package main package hodu
import "context" import "context"
import "crypto/tls" import "crypto/tls"
import "errors" import "errors"
import "fmt" import "fmt"
import "io" import "io"
import "log"
import "math/rand" import "math/rand"
import "net" import "net"
import "net/http" import "net/http"
import "os"
import "os/signal"
import "sync" import "sync"
import "sync/atomic" import "sync/atomic"
import "syscall"
//import "time" //import "time"
import "google.golang.org/grpc" import "google.golang.org/grpc"
@ -31,9 +27,10 @@ type ServerRouteMap = map[uint32]*ServerRoute
type Server struct { type Server struct {
tlscfg *tls.Config tlscfg *tls.Config
wg sync.WaitGroup wg sync.WaitGroup
ext_svcs []Service
stop_req atomic.Bool stop_req atomic.Bool
ctl *http.Server // control server ctl *http.Server // control server
l []*net.TCPListener // main listener for grpc l []*net.TCPListener // main listener for grpc
l_wg sync.WaitGroup l_wg sync.WaitGroup
@ -503,38 +500,6 @@ func (cts *ClientConn) ReqStop() {
} }
} }
// ------------------------------------
func (s *Server) handle_os_signals() {
var sighup_chan chan os.Signal
var sigterm_chan chan os.Signal
var sig os.Signal
defer s.wg.Done()
sighup_chan = make(chan os.Signal, 1)
sigterm_chan = make(chan os.Signal, 1)
signal.Notify(sighup_chan, syscall.SIGHUP)
signal.Notify(sigterm_chan, syscall.SIGTERM, os.Interrupt)
chan_loop:
for {
select {
case <-sighup_chan:
// TODO:
//s.RefreshConfig()
case sig = <-sigterm_chan:
// TODO: get timeout value from config
//s.Shutdown(fmt.Sprintf("termination by signal %s", sig), 3*time.Second)
s.ReqStop()
//log.Debugf("termination by signal %s", sig)
fmt.Printf("termination by signal %s\n", sig)
break chan_loop
}
}
}
// -------------------------------------------------------------------- // --------------------------------------------------------------------
func (s *Server) GetSeed (ctx context.Context, c_seed *Seed) (*Seed, error) { func (s *Server) GetSeed (ctx context.Context, c_seed *Seed) (*Seed, error) {
@ -710,6 +675,7 @@ func NewServer(laddrs []string, logger Logger, tlscfg *tls.Config) (*Server, err
} }
s.tlscfg = tlscfg s.tlscfg = tlscfg
s.ext_svcs = make([]Service, 0, 1)
s.cts_map = make(ClientConnMap) // TODO: make it configurable... s.cts_map = make(ClientConnMap) // TODO: make it configurable...
s.stop_req.Store(false) s.stop_req.Store(false)
/* /*
@ -782,8 +748,6 @@ func (s *Server) RunTask(wg *sync.WaitGroup) {
// stop the main grpc server after all the other tasks are finished. // stop the main grpc server after all the other tasks are finished.
s.gs.Stop() s.gs.Stop()
syscall.Kill(syscall.Getpid(), syscall.SIGTERM)
} }
func (s *Server) RunCtlTask(wg *sync.WaitGroup) { func (s *Server) RunCtlTask(wg *sync.WaitGroup) {
@ -878,66 +842,25 @@ func (s *Server) FindClientConnByAddr (addr net.Addr) *ClientConn {
return cts return cts
} }
// -------------------------------------------------------------------- func (s *Server) StartService(cfg interface{}) {
s.wg.Add(1)
const serverKey = `-----BEGIN EC PARAMETERS----- go s.RunTask(&s.wg)
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIHg+g2unjA5BkDtXSN9ShN7kbPlbCcqcYdDu+QeV8XWuoAoGCCqGSM49
AwEHoUQDQgAEcZpodWh3SEs5Hh3rrEiu1LZOYSaNIWO34MgRxvqwz1FMpLxNlx0G
cSqrxhPubawptX5MSr02ft32kfOlYbaF5Q==
-----END EC PRIVATE KEY-----
`
const serverCert = `-----BEGIN CERTIFICATE-----
MIIB+TCCAZ+gAwIBAgIJAL05LKXo6PrrMAoGCCqGSM49BAMCMFkxCzAJBgNVBAYT
AkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn
aXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNTEyMDgxNDAxMTNa
Fw0yNTEyMDUxNDAxMTNaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0
YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMM
CWxvY2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHGaaHVod0hLOR4d
66xIrtS2TmEmjSFjt+DIEcb6sM9RTKS8TZcdBnEqq8YT7m2sKbV+TEq9Nn7d9pHz
pWG2heWjUDBOMB0GA1UdDgQWBBR0fqrecDJ44D/fiYJiOeBzfoqEijAfBgNVHSME
GDAWgBR0fqrecDJ44D/fiYJiOeBzfoqEijAMBgNVHRMEBTADAQH/MAoGCCqGSM49
BAMCA0gAMEUCIEKzVMF3JqjQjuM2rX7Rx8hancI5KJhwfeKu1xbyR7XaAiEA2UT7
1xOP035EcraRmWPe7tO0LpXgMxlh2VItpc2uc2w=
-----END CERTIFICATE-----
`
type serverLogger struct {
log *log.Logger
} }
func (s *Server) StartExtService(svc Service, data interface{}) {
func (log* serverLogger) Write(level LogLevel, fmt string, args ...interface{}) { s.ext_svcs = append(s.ext_svcs, svc)
log.log.Printf(fmt, args...) s.wg.Add(1)
go svc.RunTask(&s.wg)
} }
func server_main(laddrs []string) error { func (s *Server) StopServices() {
var s *Server var ext_svc Service
var err error s.ReqStop()
for _, ext_svc = range s.ext_svcs {
var sl serverLogger ext_svc.StopServices()
var cert tls.Certificate
cert, err = tls.X509KeyPair([]byte(serverCert), []byte(serverKey))
if err != nil {
return fmt.Errorf("ERROR: failed to load key pair - %s\n", err)
} }
}
sl.log = log.Default() func (s *Server) WaitForTermination() {
s, err = NewServer(laddrs, &sl, &tls.Config{Certificates: []tls.Certificate{cert}})
if err != nil {
return fmt.Errorf("ERROR: failed to create new server - %s", err.Error())
}
s.wg.Add(1)
go s.handle_os_signals()
s.wg.Add(1)
go s.RunTask(&s.wg) // this is blocking. ReqStop() will be called from a signal handler
s.wg.Wait() s.wg.Wait()
return nil
} }