From 1513d055f5bb2ca9f96fdde30297980ae12d5242 Mon Sep 17 00:00:00 2001 From: wenyekui Date: Tue, 22 Jul 2014 11:31:56 +0800 Subject: [PATCH] http api of bit cmds --- server/app.go | 5 +- server/http/base.go | 26 +++++ server/http/cmd_bit.go | 228 +++++++++++++++++++++++++++++++++++++++++ server/http/handler.go | 101 ++++++++++++++++++ server/httpd.go | 22 ---- 5 files changed, 358 insertions(+), 24 deletions(-) create mode 100644 server/http/base.go create mode 100644 server/http/cmd_bit.go create mode 100644 server/http/handler.go delete mode 100644 server/httpd.go diff --git a/server/app.go b/server/app.go index fb3f45a..7fbcb65 100644 --- a/server/app.go +++ b/server/app.go @@ -3,6 +3,7 @@ package server import ( "fmt" "github.com/siddontang/ledisdb/ledis" + . "github.com/siddontang/ledisdb/server/http" "net" "net/http" "path" @@ -133,8 +134,8 @@ func (app *App) httpServe() { mux := http.NewServeMux() - mux.Handle("/ws", &wsHandler{app}) - mux.Handle("/", &cmdHandler{app}) + mux.Handle("/ws", &WsHandler{app.Ledis()}) + mux.Handle("/", &CmdHandler{app.Ledis()}) svr := http.Server{Handler: mux} svr.Serve(app.httpListener) diff --git a/server/http/base.go b/server/http/base.go new file mode 100644 index 0000000..4a05cb4 --- /dev/null +++ b/server/http/base.go @@ -0,0 +1,26 @@ +package http + +import ( + "errors" + "fmt" + "github.com/siddontang/ledisdb/ledis" + "strings" +) + +const ERR_ARGUMENT_FORMAT = "ERR wrong number of arguments for '%s' command" + +var ErrValue = errors.New("ERR value is not an integer or out of range") + +type commondFunc func(*ledis.DB, ...string) (interface{}, error) + +var regCmds = map[string]commondFunc{} + +func register(name string, f commondFunc) { + if _, ok := regCmds[strings.ToLower(name)]; ok { + panic(fmt.Sprintf("%s has been registered", name)) + } + regCmds[name] = f +} +func lookup(name string) commondFunc { + return regCmds[strings.ToLower(name)] +} diff --git a/server/http/cmd_bit.go b/server/http/cmd_bit.go new file mode 100644 index 0000000..999b3a4 --- /dev/null +++ b/server/http/cmd_bit.go @@ -0,0 +1,228 @@ +package http + +import ( + "fmt" + "github.com/siddontang/ledisdb/ledis" + "strconv" + "strings" +) + +func bgetCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "bget") + } + if v, err := db.BGet(ledis.Slice(args[0])); err != nil { + return nil, err + } else { + return v, nil + } +} + +func bdeleteCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "bdelete") + } + if n, err := db.BDelete(ledis.Slice(args[0])); err != nil { + return nil, err + } else { + return n, err + } +} + +func bsetbitCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 3 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "bsetbit") + } + key := ledis.Slice(args[0]) + offset, err := strconv.ParseInt(args[1], 10, 32) + if err != nil { + return nil, ErrValue + } + val, err := strconv.ParseUint(args[2], 10, 8) + if ori, err := db.BSetBit(key, int32(offset), uint8(val)); err != nil { + return nil, err + + } else { + return ori, nil + } +} + +func bgetbitCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "bgetbit") + } + key := ledis.Slice(args[0]) + offset, err := strconv.ParseInt(args[1], 10, 32) + if err != nil { + return nil, ErrValue + } + + if v, err := db.BGetBit(key, int32(offset)); err != nil { + return nil, err + } else { + return v, nil + } + + return nil, nil +} + +func bmsetbitCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) < 3 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "bmsetbit") + } + key := ledis.Slice(args[0]) + if len(args[1:])%2 != 0 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "bmsetbit") + } else { + args = args[1:] + } + pairs := make([]ledis.BitPair, len(args)/2) + for i := 0; i < len(pairs); i++ { + offset, err := strconv.ParseInt(args[i*2], 10, 32) + if err != nil { + return nil, err + } + val, err := strconv.ParseUint(args[i*2+1], 10, 8) + if err != nil { + return nil, err + } + pairs[i].Pos = int32(offset) + pairs[i].Val = uint8(val) + } + if place, err := db.BMSetBit(key, pairs...); err != nil { + return nil, err + } else { + return place, nil + } +} + +func bcountCommand(db *ledis.DB, args ...string) (interface{}, error) { + argCnt := len(args) + if argCnt > 3 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "bcount") + } + + var err error + var start, end int64 = 0, -1 + if argCnt > 1 { + if start, err = strconv.ParseInt(args[1], 10, 32); err != nil { + return nil, err + } + } + if argCnt > 2 { + if end, err = strconv.ParseInt(args[1], 10, 32); err != nil { + return nil, err + } + } + key := ledis.Slice(args[0]) + if cnt, err := db.BCount(key, int32(start), int32(end)); err != nil { + return nil, err + } else { + return cnt, nil + } +} + +func boptCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) < 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "bopt") + } + opDesc := strings.ToLower(args[0]) + dstKey := ledis.Slice(args[1]) + + var srcKeys = [][]byte{} + if len(args) >= 3 { + srcKeys = make([][]byte, len(args[2:])) + for i, arg := range args[2:] { + srcKeys[i] = ledis.Slice(arg) + } + } + + var op uint8 + switch opDesc { + case "and": + op = ledis.OPand + case "or": + op = ledis.OPor + case "xor": + op = ledis.OPxor + case "not": + op = ledis.OPnot + default: + return nil, fmt.Errorf("ERR invalid argument '%s' for 'bopt' command", opDesc) + } + if blen, err := db.BOperation(op, dstKey, srcKeys...); err != nil { + return nil, err + } else { + return blen, nil + } +} + +func bexpireCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "bexpire") + } + duration, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + return nil, err + } + key := ledis.Slice(args[0]) + if v, err := db.BExpire(key, duration); err != nil { + return nil, err + } else { + return v, err + } +} + +func bexpireatCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "bexpireat") + } + when, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + return nil, err + } + key := ledis.Slice(args[0]) + if v, err := db.BExpireAt(key, when); err != nil { + return nil, err + } else { + return v, nil + } +} + +func bttlCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "bttl") + } + key := ledis.Slice(args[0]) + if v, err := db.BTTL(key); err != nil { + return nil, err + } else { + return v, err + } +} + +func bpersistCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "bpersist") + } + key := ledis.Slice(args[0]) + if n, err := db.BPersist(key); err != nil { + return nil, err + } else { + return n, nil + } +} + +func init() { + register("bget", bgetCommand) + register("bdelete", bdeleteCommand) + register("bsetbit", bsetbitCommand) + register("bgetbit", bgetbitCommand) + register("bmsetbit", bmsetbitCommand) + register("bcount", bcountCommand) + register("bopt", boptCommand) + register("bexpire", bexpireCommand) + register("bexpireat", bexpireatCommand) + register("bttl", bttlCommand) + register("bpersist", bpersistCommand) +} diff --git a/server/http/handler.go b/server/http/handler.go new file mode 100644 index 0000000..34aa24f --- /dev/null +++ b/server/http/handler.go @@ -0,0 +1,101 @@ +package http + +import ( + "net/http" + //"github.com/siddontang/go-websocket/websocket" + "encoding/json" + "fmt" + "github.com/siddontang/go-log/log" + "github.com/siddontang/ledisdb/ledis" + "strconv" + "strings" +) + +type CmdHandler struct { + Ldb *ledis.Ledis +} + +func (h *CmdHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/" { + w.WriteHeader(http.StatusForbidden) + return + } + idx, cmd, args := h.parseReqPath(r.URL.Path) + cmdFunc := lookup(cmd) + if cmdFunc == nil { + h.cmdNotFound(cmd, w) + return + } + var db *ledis.DB + var err error + if db, err = h.Ldb.Select(idx); err != nil { + h.serverError(cmd, err, w) + return + } + result, err := cmdFunc(db, args...) + if err != nil { + h.serverError(cmd, err, w) + return + } + h.write(cmd, result, w) +} + +func (h *CmdHandler) parseReqPath(path string) (db int, cmd string, args []string) { + substrings := strings.Split(strings.TrimLeft(path, "/"), "/") + if len(substrings) == 1 { + return 0, substrings[0], substrings[1:] + } + db, err := strconv.Atoi(substrings[0]) + if err != nil { + // db = 0 + cmd = substrings[0] + args = substrings[1:] + } else { + cmd = substrings[1] + args = substrings[2:] + } + return +} +func (h *CmdHandler) cmdNotFound(cmd string, w http.ResponseWriter) { + result := [2]interface{}{ + false, + fmt.Sprintf("ERR unknown command '%s'", cmd), + } + h.write(cmd, result, w) +} + +func (h *CmdHandler) write(cmd string, result interface{}, w http.ResponseWriter) { + m := map[string]interface{}{ + cmd: result, + } + + buf, err := json.Marshal(&m) + if err != nil { + log.Error(err.Error()) + return + } + + w.Header().Set("Content-type", "application/json; charset=utf-8") + w.Header().Set("Content-Length", strconv.Itoa(len(buf))) + + _, err = w.Write(buf) + if err != nil { + log.Error(err.Error()) + } +} + +func (h *CmdHandler) serverError(cmd string, err error, w http.ResponseWriter) { + result := [2]interface{}{ + false, + fmt.Sprintf("ERR %s", err.Error()), + } + h.write(cmd, result, w) +} + +type WsHandler struct { + Ldb *ledis.Ledis +} + +func (h *WsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("ws handler")) +} diff --git a/server/httpd.go b/server/httpd.go deleted file mode 100644 index 29cf14a..0000000 --- a/server/httpd.go +++ /dev/null @@ -1,22 +0,0 @@ -package server - -import ( - "net/http" - //"github.com/siddontang/go-websocket/websocket" -) - -type cmdHandler struct { - app *App -} - -func (h *cmdHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("cmd handler")) -} - -type wsHandler struct { - app *App -} - -func (h *wsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("ws handler")) -}