From a6727f990db7e8a6f6fb381ddbd7fca30b06942b Mon Sep 17 00:00:00 2001 From: Oleg Ozimok Date: Sun, 7 Apr 2019 21:03:47 +0300 Subject: [PATCH] add server mux --- example/mux/clone.go | 39 +++++++++++++++++++ example/mux/handler.go | 87 ++++++++++++++++++++++++++++++++++++++++++ redcon.go | 64 +++++++++++++++++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 example/mux/clone.go create mode 100644 example/mux/handler.go diff --git a/example/mux/clone.go b/example/mux/clone.go new file mode 100644 index 0000000..bc47d65 --- /dev/null +++ b/example/mux/clone.go @@ -0,0 +1,39 @@ +package main + +import ( + "log" + + "github.com/tidwall/redcon" +) + +var addr = ":6380" + +func main() { + log.Printf("started server at %s", addr) + + handler := NewHandler() + + mux := redcon.NewServeMux() + mux.HandleFunc("detach", handler.detach) + mux.HandleFunc("ping", handler.ping) + mux.HandleFunc("quit", handler.quit) + mux.HandleFunc("set", handler.set) + mux.HandleFunc("get", handler.get) + mux.HandleFunc("del", handler.delete) + + err := redcon.ListenAndServe(addr, + mux.ServeRESP, + func(conn redcon.Conn) bool { + // use this function to accept or deny the connection. + // log.Printf("accept: %s", conn.RemoteAddr()) + return true + }, + func(conn redcon.Conn, err error) { + // this is called when the connection has been closed + // log.Printf("closed: %s, err: %v", conn.RemoteAddr(), err) + }, + ) + if err != nil { + log.Fatal(err) + } +} diff --git a/example/mux/handler.go b/example/mux/handler.go new file mode 100644 index 0000000..dd5e616 --- /dev/null +++ b/example/mux/handler.go @@ -0,0 +1,87 @@ +package main + +import ( + "log" + "sync" + + "github.com/tidwall/redcon" +) + +type Handler struct { + itemsMux sync.RWMutex + items map[string][]byte +} + +func NewHandler() *Handler { + return &Handler{ + items: make(map[string][]byte), + } +} + +func (h *Handler) detach(conn redcon.Conn, cmd redcon.Command) { + detachedConn := conn.Detach() + log.Printf("connection has been detached") + go func(c redcon.DetachedConn) { + defer c.Close() + + c.WriteString("OK") + c.Flush() + }(detachedConn) +} + +func (h *Handler) ping(conn redcon.Conn, cmd redcon.Command) { + conn.WriteString("PONG") +} + +func (h *Handler) quit(conn redcon.Conn, cmd redcon.Command) { + conn.WriteString("OK") + conn.Close() +} + +func (h *Handler) set(conn redcon.Conn, cmd redcon.Command) { + if len(cmd.Args) != 3 { + conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") + return + } + + h.itemsMux.Lock() + h.items[string(cmd.Args[1])] = cmd.Args[2] + h.itemsMux.Unlock() + + conn.WriteString("OK") +} + +func (h *Handler) get(conn redcon.Conn, cmd redcon.Command) { + if len(cmd.Args) != 2 { + conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") + return + } + + h.itemsMux.RLock() + val, ok := h.items[string(cmd.Args[1])] + h.itemsMux.RUnlock() + + if !ok { + conn.WriteNull() + } else { + conn.WriteBulk(val) + } +} + +func (h *Handler) delete(conn redcon.Conn, cmd redcon.Command) { + if len(cmd.Args) != 2 { + conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") + return + } + + h.itemsMux.Lock() + _, ok := h.items[string(cmd.Args[1])] + delete(h.items, string(cmd.Args[1])) + h.itemsMux.Unlock() + + if !ok { + conn.WriteInt(0) + } else { + conn.WriteInt(1) + } +} diff --git a/redcon.go b/redcon.go index 3f7c1bb..b6e6ddf 100644 --- a/redcon.go +++ b/redcon.go @@ -7,6 +7,7 @@ import ( "errors" "io" "net" + "strings" "sync" ) @@ -900,3 +901,66 @@ func Parse(raw []byte) (Command, error) { return cmds[0], nil } + +// A Handler responds to an RESP request. +type Handler interface { + ServeRESP(conn Conn, cmd Command) +} + +// The HandlerFunc type is an adapter to allow the use of +// ordinary functions as RESP handlers. If f is a function +// with the appropriate signature, HandlerFunc(f) is a +// Handler that calls f. +type HandlerFunc func(conn Conn, cmd Command) + +// ServeRESP calls f(w, r) +func (f HandlerFunc) ServeRESP(conn Conn, cmd Command) { + f(conn, cmd) +} + +// ServeMux is an RESP command multiplexer. +type ServeMux struct { + handlers map[string]Handler +} + +// NewServeMux allocates and returns a new ServeMux. +func NewServeMux() *ServeMux { + return &ServeMux{ + handlers: make(map[string]Handler), + } +} + +// HandleFunc registers the handler function for the given command. +func (m *ServeMux) HandleFunc(command string, handler func(conn Conn, cmd Command)) { + if handler == nil { + panic("redcon: nil handler") + } + m.Handle(command, HandlerFunc(handler)) +} + +// Handle registers the handler for the given command. +// If a handler already exists for command, Handle panics. +func (m *ServeMux) Handle(command string, handler Handler) { + if command == "" { + panic("redcon: invalid command") + } + if handler == nil { + panic("redcon: nil handler") + } + if _, exist := m.handlers[command]; exist { + panic("redcon: multiple registrations for " + command) + } + + m.handlers[command] = handler +} + +// ServeRESP dispatches the command to the handler. +func (m *ServeMux) ServeRESP(conn Conn, cmd Command) { + command := strings.ToLower(string(cmd.Args[0])) + + if handler, ok := m.handlers[command]; ok { + handler.ServeRESP(conn, cmd) + } else { + conn.WriteError("ERR unknown command '" + command + "'") + } +}