diff --git a/examples/ping-pong/pp-server/main.go b/examples/ping-pong/pp-server/main.go index efdd4dc..47d66f0 100644 --- a/examples/ping-pong/pp-server/main.go +++ b/examples/ping-pong/pp-server/main.go @@ -5,35 +5,17 @@ import ( "net" "time" - "git.qowevisa.me/Qowevisa/tcpmachine/tcpcommand" "git.qowevisa.me/Qowevisa/tcpmachine/tcpserver" ) func main() { - bundle, err := tcpcommand.CreateCommandBundle([]tcpcommand.Command{ - { - Command: "PING", - Action: func(s []string, client net.Conn) { - fmt.Printf("todo..\n") - client.Write([]byte("PONG\n")) - }, - }, + server := tcpserver.CreateServer("127.0.0.1:10000") + server.On("PING", func(args []string, client net.Conn) { + client.Write([]byte("PONG\n")) }) + time.Sleep(time.Second) + err := server.StartServer() if err != nil { - panic(err) + fmt.Printf("Error: server.StartServer: %v\n", err) } - conf := tcpserver.GetDefaultConfig() - handler, errorChannel := tcpserver.CreateHandleClientFuncFromCommands(bundle, conf) - go func() { - for err := range errorChannel { - fmt.Printf("Error:1: %v\n", err) - } - }() - conf.HandleClientFunc = handler - server := tcpserver.CreateServer(conf) - go func() { - time.Sleep(time.Minute) - server.Exit <- true - }() - server.StartServer("127.0.0.1:10000") } diff --git a/tcpserver/options.go b/tcpserver/options.go new file mode 100644 index 0000000..147016b --- /dev/null +++ b/tcpserver/options.go @@ -0,0 +1,33 @@ +package tcpserver + +import "net" + +type ServerOption func(*ServerConfiguration) + +// WithMessageEndRune sets the MessageEndRune in the server configuration. +func WithMessageEndRune(r rune) ServerOption { + return func(conf *ServerConfiguration) { + conf.MessageEndRune = r + } +} + +// WithMessageSplitRune sets the MessageSplitRune in the server configuration. +func WithMessageSplitRune(r rune) ServerOption { + return func(conf *ServerConfiguration) { + conf.MessageSplitRune = r + } +} + +// WithErrorResolver sets a custom error resolver function. +func WithErrorResolver(resolver func(chan error)) ServerOption { + return func(conf *ServerConfiguration) { + conf.ErrorResolver = resolver + } +} + +// WithHandleClientFunc sets the HandleClientFunc in the server configuration. +func WithHandleClientFunc(handler func(client net.Conn)) ServerOption { + return func(conf *ServerConfiguration) { + conf.HandleClientFunc = handler + } +} diff --git a/tcpserver/server.go b/tcpserver/server.go index 668328d..9671f4b 100644 --- a/tcpserver/server.go +++ b/tcpserver/server.go @@ -23,6 +23,7 @@ type ServerConfiguration struct { func CreateHandleClientFuncFromCommands(bundle *tcpcommand.CommandBundle, conf ServerConfiguration) (func(client net.Conn), chan error) { clientErrors := make(chan error, 16) return func(client net.Conn) { + defer client.Close() connReader := bufio.NewReader(client) for { msg, err := connReader.ReadString(byte(conf.MessageEndRune)) @@ -45,17 +46,53 @@ func CreateHandleClientFuncFromCommands(bundle *tcpcommand.CommandBundle, conf S } type Server struct { + addr string HandleClientFunc func(client net.Conn) Exit chan bool // - ErrorsChannel chan error - ErrorResolver func(chan error) + MessageEndRune rune + MessageSplitRune rune + ErrorsChannel chan error + ErrorResolver func(chan error) + // + Commands []tcpcommand.Command +} + +func defaultHandleClientFunc(server *Server) func(net.Conn) { + return func(client net.Conn) { + defer client.Close() + connReader := bufio.NewReader(client) + for { + msg, err := connReader.ReadString(byte(server.MessageEndRune)) + if err != nil { + if errors.Is(err, io.EOF) { + break + } + server.ErrorsChannel <- err + } + msgWoNl := strings.Trim(msg, string(server.MessageEndRune)) + parts := strings.Split(msgWoNl, string(server.MessageSplitRune)) + commandFound := false + for _, cmd := range server.Commands { + if cmd.Command == parts[0] { + cmd.Action(parts[1:], client) + commandFound = true + break + } + } + if !commandFound { + server.ErrorsChannel <- fmt.Errorf("WARN: Command %s error: %w", parts[0], commandNotHandledError) + } + } + } } // HandleClientFunc is NOT created by this function // see: CreateHandleClientFuncFromCommands(bundle) func GetDefaultConfig() ServerConfiguration { return ServerConfiguration{ + MessageEndRune: '\n', + MessageSplitRune: ' ', ErrorResolver: func(c chan error) { for err := range c { log.Printf("DefConfig:Error: %v\n", err) @@ -64,33 +101,56 @@ func GetDefaultConfig() ServerConfiguration { } } -func CreateServer(conf ServerConfiguration) *Server { - return &Server{ - HandleClientFunc: conf.HandleClientFunc, - Exit: make(chan bool, 1), - // - ErrorsChannel: make(chan error, 8), - ErrorResolver: conf.ErrorResolver, +func CreateServer(addr string, options ...ServerOption) *Server { + conf := GetDefaultConfig() + + for _, opt := range options { + opt(&conf) } + var cmds []tcpcommand.Command + + server := &Server{ + addr: addr, + Exit: make(chan bool, 1), + HandleClientFunc: conf.HandleClientFunc, + // + MessageEndRune: conf.MessageEndRune, + MessageSplitRune: conf.MessageSplitRune, + ErrorsChannel: make(chan error, 8), + ErrorResolver: conf.ErrorResolver, + // + Commands: cmds, + } + if conf.HandleClientFunc == nil { + server.HandleClientFunc = defaultHandleClientFunc(server) + } + + return server } -func (s *Server) StartServer(address string) error { +func (s *Server) StartServer() error { if s.Exit == nil { - return fmt.Errorf("server's Exit channel is nil. Can't start server\n") + return fmt.Errorf("server's Exit channel is nil. Can't start server") } - listener, err := net.Listen("tcp", address) + if s.HandleClientFunc == nil { + return fmt.Errorf("server's HandleClientFunc is nil. Can't start server") + } + if s.ErrorsChannel == nil { + return fmt.Errorf("server's ErrorsChannel is nil. Can't start server") + } + if len(s.Commands) == 0 { + s.ErrorsChannel <- fmt.Errorf("WARN: len(s.Commands) == 0! Server is running without commands") + } + listener, err := net.Listen("tcp", s.addr) if err != nil { return fmt.Errorf("net.Listen: %w", err) } defer listener.Close() - if s.ErrorsChannel == nil { - return fmt.Errorf("server's ErrorsChannel is nil. Can't start server\n") - } if s.ErrorResolver == nil { - return fmt.Errorf("server's ErrorResolver is nil. Can't start server\n") + return fmt.Errorf("server's ErrorResolver is nil. Can't start server") } go s.ErrorResolver(s.ErrorsChannel) - log.Printf("Server started listening on %s\n", address) + log.Printf("Server started listening on %s\n", s.addr) loop: for { @@ -108,3 +168,19 @@ loop: } return nil } + +var commandDuplicateError = errors.New("Command already exists in server") +var commandNotHandledError = errors.New("Command was not handled") + +func (s *Server) On(command string, action func(args []string, client net.Conn)) error { + for _, cmd := range s.Commands { + if cmd.Command == command { + return fmt.Errorf("Failed addding command %s: %w ", command, commandDuplicateError) + } + } + s.Commands = append(s.Commands, tcpcommand.Command{ + Command: command, + Action: action, + }) + return nil +}