WIP: moved logic code in test tools to separate package for better code architecture

This commit is contained in:
2025-05-15 18:09:38 +10:00
parent b645f2b6bd
commit 169e0539f6
7 changed files with 472 additions and 374 deletions

View File

@@ -18,185 +18,64 @@ package main
import (
"context"
"fmt"
"net"
"os/signal"
"time"
"golang.org/x/sys/unix"
"gitea.suyono.dev/suyono/netbounce/cmd/slicewriter"
"gitea.suyono.dev/suyono/netbounce/testlib/client"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
var gLimit *counter
func main() {
ctx, cancel := signal.NotifyContext(context.Background(), unix.SIGINT, unix.SIGTERM)
defer cancel()
parseFlags()
log.Debug().Caller().Msgf("Sending messages to %s server: %s", viper.GetString(PROTOCOL), viper.GetString(SERVER))
sendMessages(ctx)
}
const (
SERVER = "server"
PROTOCOL = "protocol"
UDP = "udp"
TCP = "tcp"
NAME = "name"
MESSAGE = "message"
SLEEP = "sleep"
READTIMEOUT = "read-timeout"
)
func sendMessages(ctx context.Context) {
switch viper.GetString(PROTOCOL) {
case "udp":
sendUDP(ctx)
case "tcp":
sendTCP(ctx)
default:
log.Fatal().Caller().Str(PROTOCOL, viper.GetString(PROTOCOL)).Msg("Unknown protocol")
}
}
func sendTCP(ctx context.Context) {
var (
conn net.Conn
err error
buf, b []byte
n int
)
if conn, err = net.Dial(TCP, viper.GetString(SERVER)); err != nil {
log.Fatal().Caller().Err(err).Msg("Failed to connect to server")
}
defer func() {
_ = conn.Close()
}()
go func() {
<-ctx.Done()
_ = conn.Close()
}()
buf = make([]byte, 4096)
for gLimit.isContinue(ctx) {
sb := slicewriter.NewSliceWriter(buf)
if _, err = fmt.Fprintf(sb, "client %s | %v | %s", viper.GetString(NAME), time.Now(), viper.GetString(MESSAGE)); err != nil {
log.Fatal().Caller().Err(err).Msg("Failed to build client message")
}
b = sb.Bytes()
if _, err = conn.Write(b); err != nil {
log.Fatal().Caller().Err(err).Str(PROTOCOL, TCP).Str(SERVER, viper.GetString(SERVER)).Msg("Failed to send client message")
}
if err = conn.SetReadDeadline(time.Now().Add(viper.GetDuration(READTIMEOUT))); err != nil {
log.Fatal().Caller().Err(err).Str(PROTOCOL, TCP).Str(SERVER, viper.GetString(SERVER)).Msg("Failed to send client message")
}
if n, err = conn.Read(buf); err != nil {
log.Fatal().Caller().Err(err).Str(PROTOCOL, TCP).Str(SERVER, viper.GetString(SERVER)).Msg("read from the server")
}
log.Info().Caller().Str(PROTOCOL, TCP).Str(SERVER, viper.GetString(SERVER)).Msgf("%s", buf[:n])
time.Sleep(viper.GetDuration(SLEEP))
}
}
func sendUDP(ctx context.Context) {
var (
addr, laddr *net.UDPAddr
conn *net.UDPConn
rAddr net.Addr
err error
buf, b []byte
n int
)
if addr, err = net.ResolveUDPAddr(UDP, viper.GetString(SERVER)); err != nil {
log.Fatal().Caller().Err(err).Str(SERVER, viper.GetString(SERVER)).Msg("udp resolve server address")
}
if laddr, err = net.ResolveUDPAddr(UDP, ""); err != nil {
log.Fatal().Caller().Err(err).Str(SERVER, viper.GetString(SERVER)).Msg("udp resolve local/self address")
}
log.Info().Str(SERVER, viper.GetString(SERVER)).Msgf("bound address %v", laddr)
// In Go, binding address and port for UDP use ListenUDP. Confusing!!
if conn, err = net.ListenUDP(UDP, laddr); err != nil {
log.Fatal().Caller().Err(err).Str(SERVER, viper.GetString(SERVER)).Msg("fail to bind local/self address")
}
defer func() {
_ = conn.Close()
}()
go func() {
<-ctx.Done()
_ = conn.Close()
}()
buf = make([]byte, 4096)
for gLimit.isContinue(ctx) {
sb := slicewriter.NewSliceWriter(buf)
if _, err = fmt.Fprintf(sb, "client %s | %v | %s", viper.GetString(NAME), time.Now(), viper.GetString(MESSAGE)); err != nil {
log.Fatal().Caller().Err(err).Msg("Failed to build client message")
}
b = sb.Bytes()
if _, err = conn.WriteTo(b, addr); err != nil {
log.Fatal().Caller().Err(err).Str(PROTOCOL, UDP).Str(SERVER, viper.GetString(SERVER)).Msg("Failed to send client message")
}
if err = conn.SetReadDeadline(time.Now().Add(viper.GetDuration(READTIMEOUT))); err != nil {
log.Error().Caller().Err(err).Str(PROTOCOL, UDP).Str(SERVER, viper.GetString(SERVER)).Msg("set read timeout on the socket")
}
if n, rAddr, err = conn.ReadFrom(b); err != nil {
log.Fatal().Caller().Err(err).Str(PROTOCOL, UDP).Str(SERVER, viper.GetString(SERVER)).Msg("read from server")
}
log.Info().Caller().Str(PROTOCOL, UDP).Str(SERVER, rAddr.String()).Msgf("%s", buf[:n])
}
log.Debug().Caller().Msgf("Sending messages to %s server: %s", viper.GetString(client.PROTOCOL), viper.GetString(client.SERVER))
// sendMessages(ctx)
client.SendMessages(ctx)
}
func parseFlags() {
pflag.String(NAME, "", "client name")
pflag.String(client.NAME, "", "client name")
pflag.IntP("number", "n", 5, "number of messages to send; default 5; set to 0 for infinite")
pflag.Duration(READTIMEOUT, 5*time.Second, "read timeout; default 5 seconds")
pflag.DurationP(SLEEP, "s", 10*time.Millisecond, "sleep time between requests; default 10ms")
pflag.StringP(MESSAGE, "m", "message from client", "message to send")
pflag.Duration(client.READTIMEOUT, 5*time.Second, "read timeout; default 5 seconds")
pflag.DurationP(client.SLEEP, "s", 10*time.Millisecond, "sleep time between requests; default 10ms")
pflag.StringP(client.MESSAGE, "m", "message from client", "message to send")
pflag.Bool("debug", false, "run in debug mode")
pflag.String(TCP, "", "tcp server address")
pflag.String(UDP, "", "udp server address")
pflag.String(client.TCP, "", "tcp server address")
pflag.String(client.UDP, "", "udp server address")
pflag.Parse()
_ = viper.BindPFlags(pflag.CommandLine)
gLimit = makeCounter(viper.GetInt("number"))
// gLimit = makeCounter(viper.GetInt("number"))
client.InitLimit()
if !viper.IsSet(NAME) {
if !viper.IsSet(client.NAME) {
log.Fatal().Caller().Msg("server name is required")
}
if viper.IsSet(TCP) && viper.IsSet(UDP) {
if viper.IsSet(client.TCP) && viper.IsSet(client.UDP) {
log.Fatal().Caller().Msg("cannot use tcp and udp at once")
}
if !viper.IsSet(TCP) && !viper.IsSet(UDP) {
if !viper.IsSet(client.TCP) && !viper.IsSet(client.UDP) {
log.Fatal().Caller().Msg("--tcp or --udp is required, use one of them not both.")
}
if viper.IsSet(TCP) {
viper.Set(PROTOCOL, TCP)
viper.Set(SERVER, viper.GetString(TCP))
if viper.IsSet(client.TCP) {
viper.Set(client.PROTOCOL, client.TCP)
viper.Set(client.SERVER, viper.GetString(client.TCP))
}
if viper.IsSet(UDP) {
viper.Set(PROTOCOL, UDP)
viper.Set(SERVER, viper.GetString(UDP))
if viper.IsSet(client.UDP) {
viper.Set(client.PROTOCOL, client.UDP)
viper.Set(client.SERVER, viper.GetString(client.UDP))
}
zerolog.SetGlobalLevel(zerolog.InfoLevel)
@@ -204,30 +83,3 @@ func parseFlags() {
zerolog.SetGlobalLevel(zerolog.DebugLevel)
}
}
type counter struct {
limit, tick int
}
func makeCounter(limit int) *counter {
if limit <= 0 {
log.Fatal().Msg("number must be > 0")
}
return &counter{limit: limit, tick: -1}
}
func (c *counter) isContinue(ctx context.Context) bool {
select {
case <-ctx.Done():
return false
default:
}
if c.limit == 0 {
return true
}
c.tick++
return c.tick < c.limit
}