From da51eb8da3e4949f69497373434fd6817322d932 Mon Sep 17 00:00:00 2001 From: Alex Bledea Date: Mon, 8 Jul 2024 03:49:29 +0300 Subject: [PATCH] Refactor tcp (#749) * Refactor TCP server initialization This refactor uses the functional options pattern to extract away the optional TCP server configuration parameters. This: - improves overall readability, by moving away from global variables - makes it easier to configure the tcp server for tests * Use ticker instead of for loop for room deletion Go offers a ticker abstraction designed for performing tasks at a regular interval, and this change uses the ticker for tcp room deletion. It also cleans up the deletion goroutine gracefully. * Add local relay interaction diagram The diagram sketches out the interaction between clients and a local relay. * Add debug logs for room cleanup These would be useful for future development (e.g. adding a stopping mechanism for the TCP listener). --- src/tcp/assets/local_relay.png | Bin 0 -> 71288 bytes src/tcp/defaults.go | 9 ++++ src/tcp/options.go | 53 ++++++++++++++++++++ src/tcp/tcp.go | 87 +++++++++++++++++++++++---------- src/tcp/tcp_test.go | 4 +- 5 files changed, 126 insertions(+), 27 deletions(-) create mode 100644 src/tcp/assets/local_relay.png create mode 100644 src/tcp/defaults.go create mode 100644 src/tcp/options.go diff --git a/src/tcp/assets/local_relay.png b/src/tcp/assets/local_relay.png new file mode 100644 index 0000000000000000000000000000000000000000..b995e98d3022e07b633fb0fb662c59428a04d280 GIT binary patch literal 71288 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 a9g@h!`xh0IR2arV1G4|#$%bO(75OK{ZZL5G literal 0 HcmV?d00001 diff --git a/src/tcp/defaults.go b/src/tcp/defaults.go new file mode 100644 index 00000000..27e23c89 --- /dev/null +++ b/src/tcp/defaults.go @@ -0,0 +1,9 @@ +package tcp + +import "time" + +const ( + DEFAULT_LOG_LEVEL = "debug" + DEFAULT_ROOM_CLEANUP_INTERVAL = 10 * time.Minute + DEFAULT_ROOM_TTL = 3 * time.Hour +) diff --git a/src/tcp/options.go b/src/tcp/options.go new file mode 100644 index 00000000..7a923e97 --- /dev/null +++ b/src/tcp/options.go @@ -0,0 +1,53 @@ +package tcp + +import ( + "fmt" + "time" +) + +// TODO: maybe export from logger library? +var availableLogLevels = []string{"info", "error", "warn", "debug", "trace"} + +type serverOptsFunc func(s *server) error + +func WithBanner(banner ...string) serverOptsFunc { + return func(s *server) error { + if len(banner) > 0 { + s.banner = banner[0] + } + return nil + } +} + +func WithLogLevel(level string) serverOptsFunc { + return func(s *server) error { + if !containsSlice(availableLogLevels, level) { + return fmt.Errorf("invalid log level specified: %s", level) + } + s.debugLevel = level + return nil + } +} + +func WithRoomCleanupInterval(interval time.Duration) serverOptsFunc { + return func(s *server) error { + s.roomCleanupInterval = interval + return nil + } +} + +func WithRoomTTL(ttl time.Duration) serverOptsFunc { + return func(s *server) error { + s.roomTTL = ttl + return nil + } +} + +func containsSlice(s []string, e string) bool { + for _, ss := range s { + if e == ss { + return true + } + } + return false +} diff --git a/src/tcp/tcp.go b/src/tcp/tcp.go index 9d222836..c6bf67ed 100644 --- a/src/tcp/tcp.go +++ b/src/tcp/tcp.go @@ -23,6 +23,11 @@ type server struct { banner string password string rooms roomMap + + roomCleanupInterval time.Duration + roomTTL time.Duration + + stopRoomCleanup chan struct{} } type roomInfo struct { @@ -39,21 +44,36 @@ type roomMap struct { const pingRoom = "pinglkasjdlfjsaldjf" -var timeToRoomDeletion = 10 * time.Minute - -// Run starts a tcp listener, run async -func Run(debugLevel, host, port, password string, banner ...string) (err error) { +// newDefaultServer initializes a new server, with some default configuration options +func newDefaultServer() *server { s := new(server) + s.roomCleanupInterval = DEFAULT_ROOM_CLEANUP_INTERVAL + s.roomTTL = DEFAULT_ROOM_TTL + s.debugLevel = DEFAULT_LOG_LEVEL + s.stopRoomCleanup = make(chan struct{}) + return s +} + +// RunWithOptionsAsync asynchronously starts a TCP listener. +func RunWithOptionsAsync(host, port, password string, opts ...serverOptsFunc) error { + s := newDefaultServer() s.host = host s.port = port s.password = password - s.debugLevel = debugLevel - if len(banner) > 0 { - s.banner = banner[0] + for _, opt := range opts { + err := opt(s) + if err != nil { + return fmt.Errorf("could not apply optional configurations: %w", err) + } } return s.start() } +// Run starts a tcp listener, run async +func Run(debugLevel, host, port, password string, banner ...string) (err error) { + return RunWithOptionsAsync(host, port, password, WithBanner(banner...), WithLogLevel(debugLevel)) +} + func (s *server) start() (err error) { log.SetLevel(s.debugLevel) log.Debugf("starting with password '%s'", s.password) @@ -61,24 +81,8 @@ func (s *server) start() (err error) { s.rooms.rooms = make(map[string]roomInfo) s.rooms.Unlock() - // delete old rooms - go func() { - for { - time.Sleep(timeToRoomDeletion) - var roomsToDelete []string - s.rooms.Lock() - for room := range s.rooms.rooms { - if time.Since(s.rooms.rooms[room].opened) > 3*time.Hour { - roomsToDelete = append(roomsToDelete, room) - } - } - s.rooms.Unlock() - - for _, room := range roomsToDelete { - s.deleteRoom(room) - } - } - }() + go s.deleteOldRooms() + defer s.stopRoomDeletion() err = s.run() if err != nil { @@ -173,6 +177,39 @@ func (s *server) run() (err error) { } } +// deleteOldRooms checks for rooms at a regular interval and removes those that +// have exceeded their allocated TTL. +func (s *server) deleteOldRooms() { + ticker := time.NewTicker(s.roomCleanupInterval) + for { + select { + case <-ticker.C: + var roomsToDelete []string + s.rooms.Lock() + for room := range s.rooms.rooms { + if time.Since(s.rooms.rooms[room].opened) > s.roomTTL { + roomsToDelete = append(roomsToDelete, room) + } + } + s.rooms.Unlock() + + for _, room := range roomsToDelete { + s.deleteRoom(room) + log.Debugf("room cleaned up: %s", room) + } + case <-s.stopRoomCleanup: + ticker.Stop() + log.Debug("room cleanup stopped") + return + } + } +} + +func (s *server) stopRoomDeletion() { + log.Debug("stop room cleanup fired") + s.stopRoomCleanup <- struct{}{} +} + var weakKey = []byte{1, 2, 3} func (s *server) clientCommunication(port string, c *comm.Comm) (room string, err error) { diff --git a/src/tcp/tcp_test.go b/src/tcp/tcp_test.go index 769d999e..9c985994 100644 --- a/src/tcp/tcp_test.go +++ b/src/tcp/tcp_test.go @@ -23,8 +23,8 @@ func BenchmarkConnection(b *testing.B) { func TestTCP(t *testing.T) { log.SetLevel("error") - timeToRoomDeletion = 100 * time.Millisecond - go Run("debug", "127.0.0.1", "8381", "pass123", "8382") + timeToRoomDeletion := 100 * time.Millisecond + go RunWithOptionsAsync("127.0.0.1", "8381", "pass123", WithBanner("8382"), WithLogLevel("debug"), WithRoomTTL(timeToRoomDeletion)) time.Sleep(timeToRoomDeletion) err := PingServer("127.0.0.1:8381") assert.Nil(t, err)