From d3e36da03ffdbc359c7da815ce32ae36e8cc7bab Mon Sep 17 00:00:00 2001 From: Jens Schumann Date: Fri, 18 Oct 2013 12:07:41 +0200 Subject: [PATCH 01/33] Comment 'disconnect detection' in order to prevent premature disconnects. --- server.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server.go b/server.go index c4a4131..818c9af 100644 --- a/server.go +++ b/server.go @@ -65,6 +65,7 @@ func (srv *Server) ServeClient(conn net.Conn) (err error) { clientChan := make(chan struct{}) // Read on `conn` in order to detect client disconnect + /* go func() { // Close chan in order to trigger eventual selects defer close(clientChan) @@ -74,6 +75,7 @@ func (srv *Server) ServeClient(conn net.Conn) (err error) { io.Copy(ioutil.Discard, conn) } }() + */ var clientAddr string From 7935a745a66fa78639942048233767a8da165cb0 Mon Sep 17 00:00:00 2001 From: Jens Schumann Date: Fri, 18 Oct 2013 12:10:08 +0200 Subject: [PATCH 02/33] Remove unused imports. --- server.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server.go b/server.go index 818c9af..b29c310 100644 --- a/server.go +++ b/server.go @@ -6,8 +6,8 @@ package redis import ( "fmt" - "io" - "io/ioutil" + // "io" + // "io/ioutil" "net" "reflect" ) @@ -75,7 +75,7 @@ func (srv *Server) ServeClient(conn net.Conn) (err error) { io.Copy(ioutil.Discard, conn) } }() - */ + */ var clientAddr string From 332083307aff2d7e0d29973c44165f3cf707def2 Mon Sep 17 00:00:00 2001 From: Jens Schumann Date: Sun, 20 Oct 2013 21:28:50 +0200 Subject: [PATCH 03/33] Add 'incr' and 'decr' methods. --- defaultHandler.go | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/defaultHandler.go b/defaultHandler.go index ac0fbbd..d5c071c 100644 --- a/defaultHandler.go +++ b/defaultHandler.go @@ -364,6 +364,40 @@ func (h *DefaultHandler) Monitor() (*MonitorReply, error) { return &MonitorReply{}, nil } +func (h *DefaultHandler) Incr(key string) (int, error) { + if h.Database == nil { + h.Database = NewDatabase(nil) + } + + lock <- true + + temp, _ := strconv.Atoi(string(h.values[key])) + temp = temp + 1 + h.values[key] = []byte(strconv.Itoa(temp)) + + <-lock + + return temp, nil +} + +var lock = make(chan bool, 1) + +func (h *DefaultHandler) Decr(key string) (int, error) { + if h.Database == nil { + h.Database = NewDatabase(nil) + } + + lock <- true + + temp, _ := strconv.Atoi(string(h.values[key])) + temp = temp - 1 + h.values[key] = []byte(strconv.Itoa(temp)) + + <-lock + + return temp, nil +} + func NewDefaultHandler() *DefaultHandler { db := NewDatabase(nil) ret := &DefaultHandler{ From d1efde2442c82a6f4daa60e6e154054781480765 Mon Sep 17 00:00:00 2001 From: Jens Schumann Date: Sun, 20 Oct 2013 21:31:37 +0200 Subject: [PATCH 04/33] Do some minor refactorings. --- defaultHandler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/defaultHandler.go b/defaultHandler.go index d5c071c..8b4e5d9 100644 --- a/defaultHandler.go +++ b/defaultHandler.go @@ -364,6 +364,8 @@ func (h *DefaultHandler) Monitor() (*MonitorReply, error) { return &MonitorReply{}, nil } +var lock = make(chan bool, 1) + func (h *DefaultHandler) Incr(key string) (int, error) { if h.Database == nil { h.Database = NewDatabase(nil) @@ -380,8 +382,6 @@ func (h *DefaultHandler) Incr(key string) (int, error) { return temp, nil } -var lock = make(chan bool, 1) - func (h *DefaultHandler) Decr(key string) (int, error) { if h.Database == nil { h.Database = NewDatabase(nil) From fed12c02d8540395ada2833f1da904adead668a9 Mon Sep 17 00:00:00 2001 From: Jens Schumann Date: Mon, 21 Oct 2013 16:18:35 +0200 Subject: [PATCH 05/33] Add 'expire' method. --- defaultHandler.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/defaultHandler.go b/defaultHandler.go index 8b4e5d9..7689a99 100644 --- a/defaultHandler.go +++ b/defaultHandler.go @@ -398,6 +398,20 @@ func (h *DefaultHandler) Decr(key string) (int, error) { return temp, nil } +func (h *DefaultHandler) Expire(key, after string) (error) { + if h.Database == nil { + h.Database = NewDatabase(nil) + } + + d, _ := strconv.Atoi(after) + + time.AfterFunc(time.Duration(d) * time.Second, func() { + h.Del(key) + }) + + return nil +} + func NewDefaultHandler() *DefaultHandler { db := NewDatabase(nil) ret := &DefaultHandler{ From 8a2e874487e5f455db0dd798a36eba52fe9b793e Mon Sep 17 00:00:00 2001 From: Jens Schumann Date: Mon, 21 Oct 2013 17:24:06 +0200 Subject: [PATCH 06/33] Add 'exists' method. --- defaultHandler.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/defaultHandler.go b/defaultHandler.go index 7689a99..4e0d69e 100644 --- a/defaultHandler.go +++ b/defaultHandler.go @@ -412,6 +412,19 @@ func (h *DefaultHandler) Expire(key, after string) (error) { return nil } +func (h *DefaultHandler) Exists(key string) (int, error) { + if h.Database == nil { + h.Database = NewDatabase(nil) + } + + _, exists := h.values[key] + if exists { + return 1, nil + } else { + return 0, nil + } +} + func NewDefaultHandler() *DefaultHandler { db := NewDatabase(nil) ret := &DefaultHandler{ From 7e66daf806e208861e4a5c87d36ca2c7aafb702f Mon Sep 17 00:00:00 2001 From: Jens Schumann Date: Wed, 23 Oct 2013 12:48:54 +0200 Subject: [PATCH 07/33] Add ordered set methods. --- defaultHandler.go | 106 ++++++++++++++++++++++++++++++++++++++++------ server.go | 18 ++++---- 2 files changed, 103 insertions(+), 21 deletions(-) diff --git a/defaultHandler.go b/defaultHandler.go index 4e0d69e..fb76997 100644 --- a/defaultHandler.go +++ b/defaultHandler.go @@ -8,10 +8,11 @@ import ( ) type ( - HashValue map[string][]byte - HashHash map[string]HashValue - HashSub map[string][]*ChannelWriter - HashBrStack map[string]*Stack + HashValue map[string][]byte + HashHash map[string]HashValue + HashSub map[string][]*ChannelWriter + HashBrStack map[string]*Stack + HashOrderedSet map[string]*OrderedSet ) type Database struct { @@ -23,15 +24,18 @@ type Database struct { brstack HashBrStack sub HashSub + + orderedSet HashOrderedSet } func NewDatabase(parent *Database) *Database { db := &Database{ - values: make(HashValue), - sub: make(HashSub), - brstack: make(HashBrStack), - children: map[int]*Database{}, - parent: parent, + values: make(HashValue), + sub: make(HashSub), + brstack: make(HashBrStack), + children: map[int]*Database{}, + parent: parent, + orderedSet: make(HashOrderedSet), } db.children[0] = db return db @@ -398,18 +402,18 @@ func (h *DefaultHandler) Decr(key string) (int, error) { return temp, nil } -func (h *DefaultHandler) Expire(key, after string) (error) { +func (h *DefaultHandler) Expire(key, after string) (int, error) { if h.Database == nil { h.Database = NewDatabase(nil) } d, _ := strconv.Atoi(after) - time.AfterFunc(time.Duration(d) * time.Second, func() { + time.AfterFunc(time.Duration(d)*time.Second, func() { h.Del(key) }) - return nil + return 1, nil } func (h *DefaultHandler) Exists(key string) (int, error) { @@ -425,6 +429,84 @@ func (h *DefaultHandler) Exists(key string) (int, error) { } } +func (h *DefaultHandler) Zadd(key string, score int, value []byte, values ...[]byte) (int, error) { + values = append([][]byte{value}, values...) + + if h.Database == nil { + h.Database = NewDatabase(nil) + } + + if _, exists := h.orderedSet[key]; !exists { + h.orderedSet[key] = NewOrderedSet() + } + + ctr := 0 + for _, v := range values { + ctr = ctr + h.orderedSet[key].Add(score, v) + } + + return ctr, nil +} + +func (h *DefaultHandler) Zrange(key string, min int, max int) ([][]byte, error) { + if h.Database == nil { + h.Database = NewDatabase(nil) + } + + if _, exists := h.orderedSet[key]; !exists { + return [][]byte{}, nil + } + + r := h.orderedSet[key].Range(min, max) + + return r, nil +} + +func (h *DefaultHandler) Zrangebyscore(key string, min int, max int) ([][]byte, error) { + if h.Database == nil { + h.Database = NewDatabase(nil) + } + + if _, exists := h.orderedSet[key]; !exists { + return [][]byte{}, nil + } + + r := h.orderedSet[key].RangeByScore(min, max) + + return r, nil +} + +func (h *DefaultHandler) Zrem(key string, value []byte, values ...[]byte) (int, error) { + values = append([][]byte{value}, values...) + + if h.Database == nil { + h.Database = NewDatabase(nil) + } + + if _, exists := h.orderedSet[key]; !exists { + return 0, nil + } + + ctr := 0 + for _, v := range values { + ctr += h.orderedSet[key].Rem(v) + } + + return ctr, nil +} + +func (h *DefaultHandler) Zremrangebyscore(key string, min int, max int) (int, error) { + if h.Database == nil { + h.Database = NewDatabase(nil) + } + + if _, exists := h.orderedSet[key]; !exists { + return 0, nil + } + + return h.orderedSet[key].RemRangeByScore(min, max), nil +} + func NewDefaultHandler() *DefaultHandler { db := NewDatabase(nil) ret := &DefaultHandler{ diff --git a/server.go b/server.go index b29c310..fe1bdb5 100644 --- a/server.go +++ b/server.go @@ -66,15 +66,15 @@ func (srv *Server) ServeClient(conn net.Conn) (err error) { // Read on `conn` in order to detect client disconnect /* - go func() { - // Close chan in order to trigger eventual selects - defer close(clientChan) - defer Debugf("Client disconnected") - // FIXME: move conn within the request. - if false { - io.Copy(ioutil.Discard, conn) - } - }() + go func() { + // Close chan in order to trigger eventual selects + defer close(clientChan) + defer Debugf("Client disconnected") + // FIXME: move conn within the request. + if false { + io.Copy(ioutil.Discard, conn) + } + }() */ var clientAddr string From b348418bfa9a211cc0f1595f4b2e2f08942a4f5d Mon Sep 17 00:00:00 2001 From: Jens Schumann Date: Wed, 23 Oct 2013 12:52:52 +0200 Subject: [PATCH 08/33] Add 'ordered_set.go' (forgotten in last commit). --- ordered_set.go | 160 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 ordered_set.go diff --git a/ordered_set.go b/ordered_set.go new file mode 100644 index 0000000..d3e362c --- /dev/null +++ b/ordered_set.go @@ -0,0 +1,160 @@ +/* + Warning: a very unsophisticated, i.e., unefficent ordered set implementation. + Should make use of binary search in the future, e.g., sort.Search. +*/ + +package redis + +type OrderedSet struct { + index map[string]bool + elements []orderedSetElement +} + +type orderedSetElement struct { + score int + value []byte +} + +func NewOrderedSet() *OrderedSet { + newSet := OrderedSet{ + index: map[string]bool{}, + elements: []orderedSetElement{}, + } + return &newSet +} + +func (self *OrderedSet) Add(score int, value []byte) int { + if _, ok := self.index[string(value)]; ok { + return 0 + } + self.index[string(value)] = true + + addIndex := 0 + for i, e := range self.elements { + if score < e.score { + addIndex = i + break + } else if i == len(self.elements)-1 { + addIndex = i + 1 + } + } + + newElement := orderedSetElement{ + score: score, + value: value, + } + + self.elements = append( + self.elements[:addIndex], append( + []orderedSetElement{newElement}, self.elements[addIndex:]...)...) + + return 1 +} + +func (self *OrderedSet) Range(lowerIndex, upperIndex int) [][]byte { + result := [][]byte{} + + lower, upper, ok := self.lowerAndUpperFromIndexes(lowerIndex, upperIndex) + if !ok { + return result + } + + for _, e := range self.elements[lower:upper] { + result = append(result, e.value) + } + + return result +} + +func (self *OrderedSet) RangeByScore(lowerScore, upperScore int) [][]byte { + result := [][]byte{} + + lower, upper, ok := self.lowerAndUpperFromScores(lowerScore, upperScore) + if !ok { + return result + } + + for _, e := range self.elements[lower:upper] { + result = append(result, e.value) + } + + return result +} + +func (self *OrderedSet) Rem(value []byte) int { + if _, ok := self.index[string(value)]; !ok { + return 0 + } + + delete(self.index, string(value)) + + remIndex := 0 + for i, e := range self.elements { + if string(e.value) == string(value) { + remIndex = i + break + } + } + + self.elements = append(self.elements[:remIndex], self.elements[remIndex+1:]...) + + return 1 +} + +func (self *OrderedSet) RemRangeByScore(lowerScore, upperScore int) int { + lower, upper, ok := self.lowerAndUpperFromScores(lowerScore, upperScore) + if !ok { + return 0 + } + + for _, e := range self.elements[lower:upper] { + delete(self.index, string(e.value)) + } + + length := len(self.elements) + + self.elements = append(self.elements[:lower], self.elements[upper:]...) + + return length - len(self.elements) +} + +func (self *OrderedSet) lowerAndUpperFromIndexes(lowerIndex, upperIndex int) (int, int, bool) { + lower := 0 + upper := 0 + + if lowerIndex < 0 { + lower = len(self.elements) + 1 + lowerIndex + } + + if upperIndex < 0 { + upper = len(self.elements) + 1 + upperIndex + } else if len(self.elements) <= upperIndex { + upper = len(self.elements) + } + + if len(self.elements) <= lower || upper < lower { + return 0, 0, false + } + + return lower, upper, true +} + +func (self *OrderedSet) lowerAndUpperFromScores(lowerScore, upperScore int) (int, int, bool) { + lower := 0 + upper := 0 + + for i, e := range self.elements { + if lowerScore >= e.score { + lower = i + 1 + } + if upperScore >= e.score { + upper = i + 1 + } + } + + if len(self.elements) <= lower || upper < lower { + return 0, 0, false + } + + return lower, upper, true +} \ No newline at end of file From 05499806ac6ab0dd7a28608850e4d97bf0a30e9a Mon Sep 17 00:00:00 2001 From: Dimitrij Denissenko Date: Mon, 3 Mar 2014 13:48:12 +0000 Subject: [PATCH 09/33] Allow pipelining --- parser.go | 5 +---- parser_test.go | 21 +++++++++++++++++++-- request.go | 2 -- server.go | 4 +++- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/parser.go b/parser.go index caaf49c..62789e2 100644 --- a/parser.go +++ b/parser.go @@ -8,8 +8,7 @@ import ( "strings" ) -func parseRequest(conn io.ReadCloser) (*Request, error) { - r := bufio.NewReader(conn) +func parseRequest(r *bufio.Reader) (*Request, error) { // first line of redis request should be: // *CRLF line, err := r.ReadString('\n') @@ -43,7 +42,6 @@ func parseRequest(conn io.ReadCloser) (*Request, error) { return &Request{ Name: strings.ToLower(string(firstArg)), Args: args, - Body: conn, }, nil } @@ -59,7 +57,6 @@ func parseRequest(conn io.ReadCloser) (*Request, error) { return &Request{ Name: strings.ToLower(string(fields[0])), Args: args, - Body: conn, }, nil } diff --git a/parser_test.go b/parser_test.go index 32a30ef..a47d04a 100644 --- a/parser_test.go +++ b/parser_test.go @@ -3,6 +3,7 @@ package redis import ( "bufio" "bytes" + "io" "io/ioutil" "strings" "testing" @@ -36,7 +37,7 @@ func TestParseBadRequests(t *testing.T) { "*2\r\n$3\r\ngEt\r\n$100\r\nx\r\n", } for _, v := range requests { - _, err := parseRequest(ioutil.NopCloser(strings.NewReader(v))) + _, err := parseRequest(bufio.NewReader(strings.NewReader(v))) if err == nil { t.Fatalf("Expected error for request [%s]", v) } @@ -55,7 +56,7 @@ func TestSucess(t *testing.T) { } for _, p := range expected { - request, err := parseRequest(ioutil.NopCloser(strings.NewReader(p.s))) + request, err := parseRequest(bufio.NewReader(strings.NewReader(p.s))) if err != nil { t.Fatalf("Un xxpected eror %s when parsting", err, p.s) } @@ -73,6 +74,22 @@ func TestSucess(t *testing.T) { } } +func TestPipielines(t *testing.T) { + chain := strings.Repeat("*2\r\n$3\r\ngEt\r\n$1\r\nx\r\n", 10) + reader := bufio.NewReader(strings.NewReader(chain)) + for i := 0; i < 10; i++ { + _, err := parseRequest(reader) + if err != nil { + t.Fatalf("Unexpected error %s when parsing", err) + } + } + _, err := parseRequest(reader) + if err != io.EOF { + t.Fatalf("Expected EOF but received %+v", err) + } + +} + func b(args ...string) [][]byte { arr := make([][]byte, len(args)) for i := 0; i < len(args); i += 1 { diff --git a/request.go b/request.go index 4888e69..7a295da 100644 --- a/request.go +++ b/request.go @@ -1,7 +1,6 @@ package redis import ( - "io" "strconv" ) @@ -10,7 +9,6 @@ type Request struct { Args [][]byte Host string ClientChan chan struct{} - Body io.ReadCloser } func (r *Request) HasArgument(index int) bool { diff --git a/server.go b/server.go index c4a4131..a82dfd0 100644 --- a/server.go +++ b/server.go @@ -5,6 +5,7 @@ package redis import ( + "bufio" "fmt" "io" "io/ioutil" @@ -88,8 +89,9 @@ func (srv *Server) ServeClient(conn net.Conn) (err error) { clientAddr = co.RemoteAddr().String() } + reader := bufio.NewReader(conn) for { - request, err := parseRequest(conn) + request, err := parseRequest(reader) if err != nil { return err } From c8aabfc330e2758125d04e10da08b2af12ca1106 Mon Sep 17 00:00:00 2001 From: David Koblas Date: Wed, 16 Jul 2014 06:35:25 -0700 Subject: [PATCH 10/33] Make Code public in the StatusReply --- auto.go | 2 +- defaultHandler.go | 2 +- reply.go | 6 +++--- reply_test.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/auto.go b/auto.go index f3bece3..08f1156 100644 --- a/auto.go +++ b/auto.go @@ -105,7 +105,7 @@ func (srv *Server) handlerFn(autoHandler interface{}, f *reflect.Value, checkers ret = result[0].Interface() return srv.createReply(request, ret) } - return &StatusReply{code: "OK"}, nil + return &StatusReply{Code: "OK"}, nil }, nil } diff --git a/defaultHandler.go b/defaultHandler.go index ac0fbbd..de6c413 100644 --- a/defaultHandler.go +++ b/defaultHandler.go @@ -290,7 +290,7 @@ func (h *DefaultHandler) Del(key string, keys ...string) (int, error) { } func (h *DefaultHandler) Ping() (*StatusReply, error) { - return &StatusReply{code: "PONG"}, nil + return &StatusReply{Code: "PONG"}, nil } func (h *DefaultHandler) Subscribe(channels ...[]byte) (*MultiChannelWriter, error) { diff --git a/reply.go b/reply.go index fa9485a..da9c0dd 100644 --- a/reply.go +++ b/reply.go @@ -11,11 +11,11 @@ import ( type ReplyWriter io.WriterTo type StatusReply struct { - code string + Code string } func (r *StatusReply) WriteTo(w io.Writer) (int64, error) { - n, err := w.Write([]byte("+" + r.code + "\r\n")) + n, err := w.Write([]byte("+" + r.Code + "\r\n")) return int64(n), err } @@ -93,7 +93,7 @@ func (r *MonitorReply) WriteTo(w io.Writer) (int64, error) { statusReply := &StatusReply{} totalBytes := int64(0) for line := range r.c { - statusReply.code = line + statusReply.Code = line if n, err := statusReply.WriteTo(w); err != nil { totalBytes += n return int64(totalBytes), err diff --git a/reply_test.go b/reply_test.go index e9dfff3..fc43fdf 100644 --- a/reply_test.go +++ b/reply_test.go @@ -12,7 +12,7 @@ func TestWriteStatus(t *testing.T) { reply ReplyWriter expected string }{ - {&StatusReply{code: "OK"}, "+OK\r\n"}, + {&StatusReply{Code: "OK"}, "+OK\r\n"}, {&IntegerReply{number: 42}, ":42\r\n"}, {&ErrorReply{code: "ERROR", message: "Something went wrong"}, "-ERROR Something went wrong\r\n"}, {&BulkReply{}, "$-1\r\n"}, From b65ad7f825a6a3a04955d466dbd534bac19e4b62 Mon Sep 17 00:00:00 2001 From: Harmen Date: Tue, 2 Sep 2014 17:29:12 +0200 Subject: [PATCH 11/33] Fix off-by-one in lowercase check. Removed a spurious println(). --- server.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server.go b/server.go index a82dfd0..a83771b 100644 --- a/server.go +++ b/server.go @@ -128,10 +128,9 @@ func NewServer(c *Config) (*Server, error) { rh := reflect.TypeOf(c.handler) for i := 0; i < rh.NumMethod(); i++ { method := rh.Method(i) - if method.Name[0] > 'a' && method.Name[0] < 'z' { + if method.Name[0] >= 'a' && method.Name[0] <= 'z' { continue } - println(method.Name) handlerFn, err := srv.createHandlerFn(c.handler, &method.Func) if err != nil { return nil, err From 1561785a0b69c6d342396cdf795ee29303782a7e Mon Sep 17 00:00:00 2001 From: Eric Tsanyen Date: Mon, 9 Nov 2015 11:40:42 +0800 Subject: [PATCH 12/33] fixed for go 1.5 --- parser.go | 5 +++-- server.go | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/parser.go b/parser.go index caaf49c..31ee74b 100644 --- a/parser.go +++ b/parser.go @@ -21,9 +21,10 @@ func parseRequest(conn io.ReadCloser) (*Request, error) { // Multiline request: if line[0] == '*' { - if _, err := fmt.Sscanf(line, "*%d\r", &argsCount); err != nil { + if _, err := fmt.Sscanf(line, "*%d\r\n", &argsCount); err != nil { return nil, malformed("*", line) } + fmt.Println(argsCount) // All next lines are pairs of: //$ CR LF // CR LF @@ -71,7 +72,7 @@ func readArgument(r *bufio.Reader) ([]byte, error) { return nil, malformed("$", line) } var argSize int - if _, err := fmt.Sscanf(line, "$%d\r", &argSize); err != nil { + if _, err := fmt.Sscanf(line, "$%d\r\n", &argSize); err != nil { return nil, malformed("$", line) } diff --git a/server.go b/server.go index c4a4131..b55eaa4 100644 --- a/server.go +++ b/server.go @@ -129,7 +129,6 @@ func NewServer(c *Config) (*Server, error) { if method.Name[0] > 'a' && method.Name[0] < 'z' { continue } - println(method.Name) handlerFn, err := srv.createHandlerFn(c.handler, &method.Func) if err != nil { return nil, err From 6545cdd391159ad6fa838b3e02e2f87fac03d0e0 Mon Sep 17 00:00:00 2001 From: Eric Tsanyen Date: Mon, 9 Nov 2015 11:48:23 +0800 Subject: [PATCH 13/33] tidy --- parser.go | 1 - 1 file changed, 1 deletion(-) diff --git a/parser.go b/parser.go index 31ee74b..2ebfeb7 100644 --- a/parser.go +++ b/parser.go @@ -24,7 +24,6 @@ func parseRequest(conn io.ReadCloser) (*Request, error) { if _, err := fmt.Sscanf(line, "*%d\r\n", &argsCount); err != nil { return nil, malformed("*", line) } - fmt.Println(argsCount) // All next lines are pairs of: //$ CR LF // CR LF From 6068ddcc41cf188ff0ba81feaa3ed155580df1bc Mon Sep 17 00:00:00 2001 From: Jonathan Harlap Date: Tue, 19 Apr 2016 20:42:33 -0400 Subject: [PATCH 14/33] Fix LRANGE with negative stop index --- defaultHandler.go | 6 ++++++ example/main.go | 1 + ordered_set.go | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/defaultHandler.go b/defaultHandler.go index 117e59e..f06d83b 100644 --- a/defaultHandler.go +++ b/defaultHandler.go @@ -130,6 +130,12 @@ func (h *DefaultHandler) Lrange(key string, start, stop int) ([][]byte, error) { } } + if stop < 0 { + if stop = h.brstack[key].Len() + stop; stop < 0 { + stop = 0 + } + } + var ret [][]byte for i := start; i <= stop; i++ { if val := h.brstack[key].GetIndex(i); val != nil { diff --git a/example/main.go b/example/main.go index 97cd01d..6698100 100644 --- a/example/main.go +++ b/example/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + redis "github.com/dotcloud/go-redis-server" ) diff --git a/ordered_set.go b/ordered_set.go index d3e362c..f8f76fa 100644 --- a/ordered_set.go +++ b/ordered_set.go @@ -157,4 +157,4 @@ func (self *OrderedSet) lowerAndUpperFromScores(lowerScore, upperScore int) (int } return lower, upper, true -} \ No newline at end of file +} From 880564cf2ee26cf47c154522343089bc56b776bb Mon Sep 17 00:00:00 2001 From: Jonathan Harlap Date: Wed, 20 Apr 2016 08:14:02 -0400 Subject: [PATCH 15/33] Add Server.Close() method --- server.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/server.go b/server.go index 8293048..ab0eb87 100644 --- a/server.go +++ b/server.go @@ -18,6 +18,7 @@ type Server struct { Addr string // TCP address to listen on, ":6389" if empty MonitorChans []chan string methods map[string]HandlerFn + listener net.Listener } func (srv *Server) ListenAndServe() error { @@ -34,9 +35,15 @@ func (srv *Server) ListenAndServe() error { if e != nil { return e } + srv.listener = l return srv.Serve(l) } +// Close shuts down the network port/socket +func (srv *Server) Close() error { + return srv.listener.Close() +} + // Serve accepts incoming connections on the Listener l, creating a // new service goroutine for each. The service goroutines read requests and // then call srv.Handler to reply to them. From c7a147cf6f83155f6f3d59368e118f976b9001fe Mon Sep 17 00:00:00 2001 From: Jonathan Harlap Date: Wed, 20 Apr 2016 09:15:54 -0400 Subject: [PATCH 16/33] Process requests one at a time Redis really does process each command atomically and sequentially, so this is accurate, and it also protects against racy behaviour involving maps. --- handler.go | 3 +++ server.go | 2 ++ 2 files changed, 5 insertions(+) diff --git a/handler.go b/handler.go index 959e520..37856f6 100644 --- a/handler.go +++ b/handler.go @@ -28,6 +28,9 @@ func (srv *Server) Register(name string, fn HandlerFn) { } func (srv *Server) Apply(r *Request) (ReplyWriter, error) { + srv.Lock() + defer srv.Unlock() + if srv == nil || srv.methods == nil { Debugf("The method map is uninitialized") return ErrMethodNotSupported, nil diff --git a/server.go b/server.go index ab0eb87..79b06fa 100644 --- a/server.go +++ b/server.go @@ -11,9 +11,11 @@ import ( // "io/ioutil" "net" "reflect" + "sync" ) type Server struct { + sync.Mutex Proto string Addr string // TCP address to listen on, ":6389" if empty MonitorChans []chan string From e7aa232715d20e1a81fa6a2434c5ec20adf4f765 Mon Sep 17 00:00:00 2001 From: Jonathan Harlap Date: Wed, 20 Apr 2016 14:20:41 -0400 Subject: [PATCH 17/33] Close server more safely --- server.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server.go b/server.go index 79b06fa..cf72c60 100644 --- a/server.go +++ b/server.go @@ -43,6 +43,9 @@ func (srv *Server) ListenAndServe() error { // Close shuts down the network port/socket func (srv *Server) Close() error { + if srv.listener == nil { + return nil + } return srv.listener.Close() } From 0afb481e54afaa631d5ce82e2e353f3e12f89b16 Mon Sep 17 00:00:00 2001 From: Jessica Woo Date: Wed, 11 May 2016 17:03:50 -0400 Subject: [PATCH 18/33] Have defaultHandler's DEL function check for brstack keys. Also fix typo in hvalues check. --- defaultHandler.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/defaultHandler.go b/defaultHandler.go index f06d83b..46912f0 100644 --- a/defaultHandler.go +++ b/defaultHandler.go @@ -291,10 +291,15 @@ func (h *DefaultHandler) Del(key string, keys ...string) (int, error) { delete(h.values, k) count++ } - if _, exists := h.hvalues[key]; exists { + if _, exists := h.hvalues[k]; exists { delete(h.hvalues, k) count++ } + + if _, exists := h.brstack[k]; exists { + delete(h.brstack, k) + count++ + } } return count, nil } From e3117f863df120af6dfc34486147181bd25dfe55 Mon Sep 17 00:00:00 2001 From: Jonathan Harlap Date: Fri, 20 May 2016 16:12:25 -0400 Subject: [PATCH 19/33] Replace ListenAndServe with Start Listener automatically starts when a new server is created, and port 0 will make the server use a random available port. Start should be called which will block until the server is closed, and Close() should always be called to clean up and release the port. --- auto_test.go | 1 + handler_test.go | 2 ++ server.go | 16 ++++++++++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/auto_test.go b/auto_test.go index f506af9..d3b7e20 100644 --- a/auto_test.go +++ b/auto_test.go @@ -129,6 +129,7 @@ func TestAutoHandler(t *testing.T) { if err != nil { t.Fatalf("Unexpected error: %s", err) } + defer srv.Close() expected := []struct { request *Request expected []string diff --git a/handler_test.go b/handler_test.go index 0730bca..4c7699c 100644 --- a/handler_test.go +++ b/handler_test.go @@ -9,6 +9,7 @@ func TestEmptyHandler(t *testing.T) { c := make(chan struct{}) defer close(c) srv := &Server{} + defer srv.Close() reply, err := srv.ApplyString(&Request{}) if err != nil { t.Fatalf("Unexpected error: %s", err) @@ -23,6 +24,7 @@ func TestCustomHandler(t *testing.T) { if err != nil { t.Fatal(err) } + defer srv.Close() srv.Register("GET", func(r *Request) (ReplyWriter, error) { return &BulkReply{value: []byte("42")}, nil }) diff --git a/server.go b/server.go index cf72c60..a691ad0 100644 --- a/server.go +++ b/server.go @@ -23,7 +23,7 @@ type Server struct { listener net.Listener } -func (srv *Server) ListenAndServe() error { +func (srv *Server) listen() error { addr := srv.Addr if srv.Proto == "" { srv.Proto = "tcp" @@ -38,7 +38,14 @@ func (srv *Server) ListenAndServe() error { return e } srv.listener = l - return srv.Serve(l) + + // if port was 0 and proto is tcp, the listener would use a random port + srv.Addr = l.Addr().String() + return nil +} + +func (srv *Server) Start() error { + return srv.Serve(srv.listener) } // Close shuts down the network port/socket @@ -151,5 +158,10 @@ func NewServer(c *Config) (*Server, error) { } srv.Register(method.Name, handlerFn) } + + err := srv.listen() + if err != nil { + return nil, err + } return srv, nil } From e06c83007cafeb4925cf5a2ef935cb6b61247a5a Mon Sep 17 00:00:00 2001 From: Tom Grennan Date: Thu, 31 Mar 2016 17:27:26 -0700 Subject: [PATCH 20/33] Fix error typos and quoted string formats "Malformed" not "Mailformed" and %q instead of '%s' Signed-off-by: Tom Grennan --- parser.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/parser.go b/parser.go index 3e590bf..1c161e6 100644 --- a/parser.go +++ b/parser.go @@ -97,16 +97,18 @@ func readArgument(r *bufio.Reader) ([]byte, error) { } func malformed(expected string, got string) error { - Debugf("Mailformed request:'%s does not match %s\\r\\n'", got, expected) - return fmt.Errorf("Mailformed request:'%s does not match %s\\r\\n'", got, expected) + Debugf("Malformed request: %q does not match %q\n", got, expected) + return fmt.Errorf("Malformed request: %q does not match %q\r\n", + got, expected) } func malformedLength(expected int, got int) error { return fmt.Errorf( - "Mailformed request: argument length '%d does not match %d\\r\\n'", + "Malformed request: argument length %d does not match %d\r\n", got, expected) } func malformedMissingCRLF() error { - return fmt.Errorf("Mailformed request: line should end with \\r\\n") + return fmt.Errorf("Malformed request: line should end with %q\r\n", + "\r\n") } From b726c7056398b6f28874cafaf0b54e2de39f0101 Mon Sep 17 00:00:00 2001 From: Tom Grennan Date: Tue, 5 Apr 2016 11:24:55 -0700 Subject: [PATCH 21/33] Print all debug to a configurable writer Signed-off-by: Tom Grennan --- auto.go | 4 ++-- debug.go | 5 ++++- defaultHandler.go | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/auto.go b/auto.go index 08f1156..0224c8a 100644 --- a/auto.go +++ b/auto.go @@ -148,13 +148,13 @@ func (srv *Server) createReply(r *Request, val interface{}) (ReplyWriter, error) case *MonitorReply: c := make(chan string) srv.MonitorChans = append(srv.MonitorChans, c) - println("len monitor: ", len(srv.MonitorChans)) + fmt.Println(Stderr, "len monitor: ", len(srv.MonitorChans)) v.c = c return v, nil case *ChannelWriter: return v, nil case *MultiChannelWriter: - println("New client") + fmt.Println(Stderr, "New client") for _, mcw := range v.Chans { mcw.clientChan = r.ClientChan } diff --git a/debug.go b/debug.go index 7fd252f..3e69d9c 100644 --- a/debug.go +++ b/debug.go @@ -2,11 +2,14 @@ package redis import ( "fmt" + "io" "os" "runtime" "strings" ) +var Stderr = io.Writer(os.Stderr) + // Debug function, if the debug flag is set, then display. Do nothing otherwise // If Docker is in damon mode, also send the debug info on the socket // Convenience debug function, courtesy of http://github.com/dotcloud/docker @@ -22,6 +25,6 @@ func Debugf(format string, a ...interface{}) { file = file[strings.LastIndex(file, "/")+1:] } - fmt.Fprintf(os.Stderr, fmt.Sprintf("[%d] [debug] %s:%d %s\n", os.Getpid(), file, line, format), a...) + fmt.Fprintf(Stderr, fmt.Sprintf("[%d] [debug] %s:%d %s\n", os.Getpid(), file, line, format), a...) } } diff --git a/defaultHandler.go b/defaultHandler.go index 46912f0..e5bf0a0 100644 --- a/defaultHandler.go +++ b/defaultHandler.go @@ -368,7 +368,7 @@ func (h *DefaultHandler) Select(key string) error { h.dbs[h.currentDb] = h.Database h.currentDb = index if _, exists := h.dbs[index]; !exists { - println("DB not exits, create ", index) + fmt.Println(Stderr, "DB not exits, create ", index) h.dbs[index] = NewDatabase(nil) } h.Database = h.dbs[index] From aa33f76823c965fb06e004df57b3a188bb21455e Mon Sep 17 00:00:00 2001 From: Tom Grennan Date: Tue, 5 Apr 2016 11:33:30 -0700 Subject: [PATCH 22/33] optimize empty DEBUG with a closure Signed-off-by: Tom Grennan --- debug.go | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/debug.go b/debug.go index 3e69d9c..9320531 100644 --- a/debug.go +++ b/debug.go @@ -10,21 +10,28 @@ import ( var Stderr = io.Writer(os.Stderr) -// Debug function, if the debug flag is set, then display. Do nothing otherwise -// If Docker is in damon mode, also send the debug info on the socket -// Convenience debug function, courtesy of http://github.com/dotcloud/docker -func Debugf(format string, a ...interface{}) { +// This closure is a no-op unless the DEBUG env is non empty. +var Debugf = func(format string, a ...interface{}) {} + +func init() { if os.Getenv("DEBUG") != "" { + Debugf = ActualDebugf + } - // Retrieve the stack infos - _, file, line, ok := runtime.Caller(1) - if !ok { - file = "" - line = -1 - } else { - file = file[strings.LastIndex(file, "/")+1:] - } +} - fmt.Fprintf(Stderr, fmt.Sprintf("[%d] [debug] %s:%d %s\n", os.Getpid(), file, line, format), a...) +// If Docker is in damon mode, also send the debug info on the socket +// Convenience debug function, courtesy of http://github.com/dotcloud/docker +func ActualDebugf(format string, a ...interface{}) { + // Retrieve the stack infos + _, file, line, ok := runtime.Caller(1) + if !ok { + file = "" + line = -1 + } else { + file = file[strings.LastIndex(file, "/")+1:] } + fmt.Fprintf(Stderr, "[%d] [debug] %s:%d ", os.Getpid(), file, line) + fmt.Fprintf(Stderr, format, a...) + fmt.Fprintln(Stderr) } From 1889e568cbaf19ead2e5257851c610699531a30a Mon Sep 17 00:00:00 2001 From: Tom Grennan Date: Wed, 6 Apr 2016 11:49:26 -0700 Subject: [PATCH 23/33] DefaultHandler.Hset: make hvalues if nil Signed-off-by: Tom Grennan --- defaultHandler.go | 3 +++ example/simple/main.go | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/defaultHandler.go b/defaultHandler.go index e5bf0a0..e64fca3 100644 --- a/defaultHandler.go +++ b/defaultHandler.go @@ -244,6 +244,9 @@ func (h *DefaultHandler) Hset(key, subkey string, value []byte) (int, error) { if h.Database == nil { h.Database = NewDatabase(nil) } + if h.hvalues == nil { + h.hvalues = make(HashHash) + } if _, exists := h.hvalues[key]; !exists { h.hvalues[key] = make(HashValue) ret = 1 diff --git a/example/simple/main.go b/example/simple/main.go index f0ade9c..f6928d4 100644 --- a/example/simple/main.go +++ b/example/simple/main.go @@ -1,7 +1,7 @@ package main import ( - redis "github.com/dotcloud/go-redis-server" + redis "github.com/platinasystems/go-redis-server" ) func main() { From fbe0fee401e05e354a8d2d4fda1b748bad44f335 Mon Sep 17 00:00:00 2001 From: Tom Grennan Date: Wed, 6 Apr 2016 22:19:05 -0700 Subject: [PATCH 24/33] add NewStatusReply Signed-off-by: Tom Grennan --- reply.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/reply.go b/reply.go index da9c0dd..1b1d042 100644 --- a/reply.go +++ b/reply.go @@ -14,6 +14,10 @@ type StatusReply struct { Code string } +func NewStatusReply(code string) *StatusReply { + return &StatusReply{code} +} + func (r *StatusReply) WriteTo(w io.Writer) (int64, error) { n, err := w.Write([]byte("+" + r.Code + "\r\n")) return int64(n), err From eaefa8e4665457f320feb45f36a94bcda4d9a5c0 Mon Sep 17 00:00:00 2001 From: Tom Grennan Date: Mon, 6 Jun 2016 18:55:46 -0700 Subject: [PATCH 25/33] example: fix import path to platinasystems fork Signed-off-by: Tom Grennan --- example/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/main.go b/example/main.go index 6698100..a86588c 100644 --- a/example/main.go +++ b/example/main.go @@ -3,7 +3,7 @@ package main import ( "fmt" - redis "github.com/dotcloud/go-redis-server" + redis "github.com/platinasystems/go-redis-server" ) type MyHandler struct { From 18bf73516e464fb4cee036f2948f42e038e81481 Mon Sep 17 00:00:00 2001 From: Tom Grennan Date: Mon, 6 Jun 2016 18:56:40 -0700 Subject: [PATCH 26/33] example, example/simple: s/ListenAndServe/Start/ Signed-off-by: Tom Grennan --- example/main.go | 2 +- example/simple/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example/main.go b/example/main.go index a86588c..197c1f7 100644 --- a/example/main.go +++ b/example/main.go @@ -46,7 +46,7 @@ func main() { if err := srv.RegisterFct("test2", Test2); err != nil { panic(err) } - if err := srv.ListenAndServe(); err != nil { + if err := srv.Start(); err != nil { panic(err) } } diff --git a/example/simple/main.go b/example/simple/main.go index f6928d4..0721cb5 100644 --- a/example/simple/main.go +++ b/example/simple/main.go @@ -9,7 +9,7 @@ func main() { if err != nil { panic(err) } - if err := server.ListenAndServe(); err != nil { + if err := server.Start(); err != nil { panic(err) } } From 5d7696c7c696ba9042cae4f982908aa4e7f2eeca Mon Sep 17 00:00:00 2001 From: Tom Grennan Date: Mon, 6 Jun 2016 19:03:50 -0700 Subject: [PATCH 27/33] README: -remove broken travis link Signed-off-by: Tom Grennan --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 8449b45..3586f20 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -[![Build Status](https://travis-ci.org/dotcloud/go-redis-server.png)](https://travis-ci.org/dotcloud/go-redis-server) - Redis server protocol library ============================= From 340ca981d298fd3df7d1f98a0bdc5852309efd0a Mon Sep 17 00:00:00 2001 From: Tom Grennan Date: Tue, 7 Jun 2016 11:48:05 -0700 Subject: [PATCH 28/33] remove print of "New client" on subscribe Signed-off-by: Tom Grennan --- auto.go | 1 - 1 file changed, 1 deletion(-) diff --git a/auto.go b/auto.go index 0224c8a..369d740 100644 --- a/auto.go +++ b/auto.go @@ -154,7 +154,6 @@ func (srv *Server) createReply(r *Request, val interface{}) (ReplyWriter, error) case *ChannelWriter: return v, nil case *MultiChannelWriter: - fmt.Println(Stderr, "New client") for _, mcw := range v.Chans { mcw.clientChan = r.ClientChan } From d92dd3019bc3f5f46ee52039587093a07a1596b5 Mon Sep 17 00:00:00 2001 From: Tom Grennan Date: Tue, 7 Jun 2016 11:50:43 -0700 Subject: [PATCH 29/33] don't overwrite addr of unix proto if non-empty Signed-off-by: Tom Grennan --- server.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/server.go b/server.go index a691ad0..4f1b4dc 100644 --- a/server.go +++ b/server.go @@ -16,8 +16,9 @@ import ( type Server struct { sync.Mutex - Proto string - Addr string // TCP address to listen on, ":6389" if empty + Proto string // default, "tcp" + Addr string // default, + // if Proto == unix then "/tmp/redis.sock" else ":6389" MonitorChans []chan string methods map[string]HandlerFn listener net.Listener @@ -28,10 +29,12 @@ func (srv *Server) listen() error { if srv.Proto == "" { srv.Proto = "tcp" } - if srv.Proto == "unix" && addr == "" { - addr = "/tmp/redis.sock" - } else if addr == "" { - addr = ":6389" + if addr == "" { + if srv.Proto == "unix" { + addr = "/tmp/redis.sock" + } else { + addr = ":6389" + } } l, e := net.Listen(srv.Proto, addr) if e != nil { From 4be445c946b0e03c14a7bd488dad2e436fbd0c81 Mon Sep 17 00:00:00 2001 From: Tom Grennan Date: Tue, 7 Jun 2016 11:52:07 -0700 Subject: [PATCH 30/33] retry listen Devices that are still in the ipv6 duplicated address detection will fail to bind so retry every 100ms for upto 3s. Signed-off-by: Tom Grennan --- server.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/server.go b/server.go index 4f1b4dc..9590952 100644 --- a/server.go +++ b/server.go @@ -7,6 +7,7 @@ package redis import ( "bufio" "fmt" + "time" // "io" // "io/ioutil" "net" @@ -36,14 +37,22 @@ func (srv *Server) listen() error { addr = ":6389" } } - l, e := net.Listen(srv.Proto, addr) - if e != nil { - return e + for i := 0; ; i++ { + l, e := net.Listen(srv.Proto, addr) + if e == nil { + srv.listener = l + break + } else if i < 30 { + // retry for devices that are still in ipv6 + // duplicate address detection + time.Sleep(100 * time.Millisecond) + } else { + return e + } } - srv.listener = l // if port was 0 and proto is tcp, the listener would use a random port - srv.Addr = l.Addr().String() + srv.Addr = srv.listener.Addr().String() return nil } From 9486d6a9f5f096674ba983771397a58eec3f5da5 Mon Sep 17 00:00:00 2001 From: Tom Grennan Date: Wed, 31 May 2017 11:39:01 -0700 Subject: [PATCH 31/33] go-redis-server: OK to Quit Signed-off-by: Tom Grennan --- defaultHandler.go | 4 ++++ server.go | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/defaultHandler.go b/defaultHandler.go index e64fca3..39dcf3c 100644 --- a/defaultHandler.go +++ b/defaultHandler.go @@ -311,6 +311,10 @@ func (h *DefaultHandler) Ping() (*StatusReply, error) { return &StatusReply{Code: "PONG"}, nil } +func (h *DefaultHandler) Quit() (*StatusReply, error) { + return nil, Quit +} + func (h *DefaultHandler) Subscribe(channels ...[]byte) (*MultiChannelWriter, error) { if h.Database == nil { h.Database = NewDatabase(nil) diff --git a/server.go b/server.go index 9590952..b05523d 100644 --- a/server.go +++ b/server.go @@ -6,6 +6,7 @@ package redis import ( "bufio" + "errors" "fmt" "time" // "io" @@ -15,6 +16,8 @@ import ( "sync" ) +var Quit = errors.New("QUIT") + type Server struct { sync.Mutex Proto string // default, "tcp" @@ -89,7 +92,11 @@ func (srv *Server) Serve(l net.Listener) error { func (srv *Server) ServeClient(conn net.Conn) (err error) { defer func() { if err != nil { - fmt.Fprintf(conn, "-%s\n", err) + if err == Quit { + conn.Write([]byte("+OK\r\n")) + } else { + fmt.Fprintf(conn, "-%s\n", err) + } } conn.Close() }() From 2aae595273b8256e45525653ba07c4e689180001 Mon Sep 17 00:00:00 2001 From: Tom Grennan Date: Tue, 6 Jun 2017 17:34:34 -0700 Subject: [PATCH 32/33] go-redis-server: expedite quit Signed-off-by: Tom Grennan --- defaultHandler.go | 4 ---- server.go | 13 +++++-------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/defaultHandler.go b/defaultHandler.go index 39dcf3c..e64fca3 100644 --- a/defaultHandler.go +++ b/defaultHandler.go @@ -311,10 +311,6 @@ func (h *DefaultHandler) Ping() (*StatusReply, error) { return &StatusReply{Code: "PONG"}, nil } -func (h *DefaultHandler) Quit() (*StatusReply, error) { - return nil, Quit -} - func (h *DefaultHandler) Subscribe(channels ...[]byte) (*MultiChannelWriter, error) { if h.Database == nil { h.Database = NewDatabase(nil) diff --git a/server.go b/server.go index b05523d..5f6774a 100644 --- a/server.go +++ b/server.go @@ -6,7 +6,6 @@ package redis import ( "bufio" - "errors" "fmt" "time" // "io" @@ -16,8 +15,6 @@ import ( "sync" ) -var Quit = errors.New("QUIT") - type Server struct { sync.Mutex Proto string // default, "tcp" @@ -92,11 +89,7 @@ func (srv *Server) Serve(l net.Listener) error { func (srv *Server) ServeClient(conn net.Conn) (err error) { defer func() { if err != nil { - if err == Quit { - conn.Write([]byte("+OK\r\n")) - } else { - fmt.Fprintf(conn, "-%s\n", err) - } + fmt.Fprintf(conn, "-%s\n", err) } conn.Close() }() @@ -135,6 +128,10 @@ func (srv *Server) ServeClient(conn net.Conn) (err error) { if err != nil { return err } + if request.Name == "quit" { + fmt.Fprintln(conn, "+OK") + break + } request.Host = clientAddr request.ClientChan = clientChan reply, err := srv.Apply(request) From fcb8fa742b73feb45085e0366e3faa5e681973c8 Mon Sep 17 00:00:00 2001 From: Kevin Paul Herbert Date: Tue, 30 Oct 2018 12:34:23 -0700 Subject: [PATCH 33/33] go.mod: Initial commit Signed-off-by: Kevin Paul Herbert --- go.mod | 1 + 1 file changed, 1 insertion(+) create mode 100644 go.mod diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..8a9d0f7 --- /dev/null +++ b/go.mod @@ -0,0 +1 @@ +module github.com/platinasystems/go-redis-server