ledisdb/server/client_resp.go

358 lines
6.5 KiB
Go
Raw Normal View History

package server
import (
"bufio"
"errors"
2015-02-03 09:15:11 +03:00
"fmt"
"io"
"net"
"os"
"runtime"
"strconv"
2015-05-03 06:19:31 +03:00
"syscall"
2014-10-30 06:11:45 +03:00
"time"
2015-05-04 17:42:28 +03:00
"github.com/siddontang/go/hack"
"github.com/siddontang/go/log"
"github.com/siddontang/go/num"
"github.com/siddontang/goredis"
"github.com/siddontang/ledisdb/ledis"
)
var errReadRequest = errors.New("invalid request protocol")
var errClientQuit = errors.New("remote client quit")
type respClient struct {
2014-08-25 10:18:23 +04:00
*client
conn net.Conn
2014-10-29 19:01:19 +03:00
2015-03-23 14:50:37 +03:00
respReader *goredis.RespReader
activeQuit bool
}
type respWriter struct {
buff *bufio.Writer
}
func (app *App) addRespClient(c *respClient) {
app.rcm.Lock()
app.rcs[c] = struct{}{}
app.rcm.Unlock()
}
func (app *App) delRespClient(c *respClient) {
app.rcm.Lock()
delete(app.rcs, c)
app.rcm.Unlock()
}
func (app *App) closeAllRespClients() {
app.rcm.Lock()
for c := range app.rcs {
c.conn.Close()
}
app.rcm.Unlock()
}
func (app *App) respClientNum() int {
app.rcm.Lock()
n := len(app.rcs)
app.rcm.Unlock()
return n
}
func newClientRESP(conn net.Conn, app *App) {
c := new(respClient)
2014-08-25 10:18:23 +04:00
c.client = newClient(app)
c.conn = conn
c.activeQuit = false
2014-10-25 10:48:52 +04:00
if tcpConn, ok := conn.(*net.TCPConn); ok {
tcpConn.SetReadBuffer(app.cfg.ConnReadBufferSize)
tcpConn.SetWriteBuffer(app.cfg.ConnWriteBufferSize)
}
2014-10-28 12:58:37 +03:00
2015-03-23 14:50:37 +03:00
br := bufio.NewReaderSize(conn, app.cfg.ConnReadBufferSize)
c.respReader = goredis.NewRespReader(br)
2014-10-25 10:48:52 +04:00
c.resp = newWriterRESP(conn, app.cfg.ConnWriteBufferSize)
2014-08-25 10:18:23 +04:00
c.remoteAddr = conn.RemoteAddr().String()
app.connWait.Add(1)
app.addRespClient(c)
go c.run()
}
func (c *respClient) run() {
defer func() {
if e := recover(); e != nil {
buf := make([]byte, 4096)
n := runtime.Stack(buf, false)
buf = buf[0:n]
2015-01-12 05:32:03 +03:00
log.Fatalf("client run panic %s:%v", buf, e)
}
c.client.close()
c.conn.Close()
2014-08-25 10:18:23 +04:00
// if c.tx != nil {
// c.tx.Rollback()
// c.tx = nil
// }
2014-09-27 16:11:36 +04:00
c.app.removeSlave(c.client, c.activeQuit)
c.app.delRespClient(c)
c.app.connWait.Done()
}()
select {
case <-c.app.quit:
//check app closed
return
default:
break
}
2014-10-30 06:11:45 +03:00
kc := time.Duration(c.app.cfg.ConnKeepaliveInterval) * time.Second
for {
2014-10-30 06:11:45 +03:00
if kc > 0 {
c.conn.SetReadDeadline(time.Now().Add(kc))
}
2015-03-23 14:50:37 +03:00
c.cmd = ""
c.args = nil
reqData, err := c.respReader.ParseRequest()
if err == nil {
err = c.handleRequest(reqData)
}
2014-10-30 04:03:58 +03:00
if err != nil {
return
}
}
}
func (c *respClient) handleRequest(reqData [][]byte) error {
if len(reqData) == 0 {
2014-08-25 10:18:23 +04:00
c.cmd = ""
c.args = reqData[0:0]
} else {
2014-10-30 07:48:52 +03:00
c.cmd = hack.String(lowerSlice(reqData[0]))
2014-08-25 10:18:23 +04:00
c.args = reqData[1:]
}
2015-03-22 03:39:20 +03:00
if c.cmd == "xselect" {
err := c.handleXSelectCmd()
if err != nil {
c.resp.writeError(err)
c.resp.flush()
return nil
}
}
2014-08-25 10:18:23 +04:00
if c.cmd == "quit" {
c.activeQuit = true
2014-08-25 10:18:23 +04:00
c.resp.writeStatus(OK)
c.resp.flush()
2014-08-04 07:06:28 +04:00
c.conn.Close()
2015-05-03 06:19:31 +03:00
return errClientQuit
} else if c.cmd == "shutdown" {
c.conn.Close()
// send kill signal
p, _ := os.FindProcess(os.Getpid())
p.Signal(syscall.SIGTERM)
p.Signal(os.Interrupt)
2015-05-03 06:19:31 +03:00
return errClientQuit
2014-08-04 07:06:28 +04:00
}
2014-08-25 10:18:23 +04:00
c.perform()
2014-08-01 05:38:08 +04:00
return nil
}
2015-03-22 03:39:20 +03:00
// XSELECT db THEN command
func (c *respClient) handleXSelectCmd() error {
if len(c.args) <= 2 {
// invalid command format
2015-03-22 03:39:20 +03:00
return fmt.Errorf("invalid format for XSELECT, must XSELECT db THEN your command")
}
if hack.String(upperSlice(c.args[1])) != "THEN" {
// invalid command format, just resturn here
2015-03-22 03:39:20 +03:00
return fmt.Errorf("invalid format for XSELECT, must XSELECT db THEN your command")
}
index, err := strconv.Atoi(hack.String(c.args[0]))
if err != nil {
2015-03-22 03:39:20 +03:00
return fmt.Errorf("invalid db for XSELECT, err %v", err)
}
db, err := c.app.ldb.Select(index)
if err != nil {
2015-03-22 03:39:20 +03:00
return fmt.Errorf("invalid db for XSELECT, err %v", err)
}
c.db = db
2014-10-29 19:01:19 +03:00
c.cmd = hack.String(lowerSlice(c.args[2]))
c.args = c.args[3:]
2014-10-29 19:01:19 +03:00
return nil
}
// response writer
2014-10-25 10:48:52 +04:00
func newWriterRESP(conn net.Conn, size int) *respWriter {
w := new(respWriter)
2014-10-25 10:48:52 +04:00
w.buff = bufio.NewWriterSize(conn, size)
return w
}
func (w *respWriter) writeError(err error) {
2015-03-23 14:50:37 +03:00
w.buff.Write(hack.Slice("-"))
if err != nil {
w.buff.WriteByte(' ')
2014-09-24 08:34:21 +04:00
w.buff.Write(hack.Slice(err.Error()))
}
w.buff.Write(Delims)
}
func (w *respWriter) writeStatus(status string) {
w.buff.WriteByte('+')
2014-09-24 08:34:21 +04:00
w.buff.Write(hack.Slice(status))
w.buff.Write(Delims)
}
func (w *respWriter) writeInteger(n int64) {
w.buff.WriteByte(':')
2014-09-24 09:29:27 +04:00
w.buff.Write(num.FormatInt64ToSlice(n))
w.buff.Write(Delims)
}
func (w *respWriter) writeBulk(b []byte) {
w.buff.WriteByte('$')
if b == nil {
w.buff.Write(NullBulk)
} else {
2014-09-24 08:34:21 +04:00
w.buff.Write(hack.Slice(strconv.Itoa(len(b))))
w.buff.Write(Delims)
w.buff.Write(b)
}
w.buff.Write(Delims)
}
func (w *respWriter) writeArray(lst []interface{}) {
w.buff.WriteByte('*')
if lst == nil {
w.buff.Write(NullArray)
w.buff.Write(Delims)
} else {
2014-09-24 08:34:21 +04:00
w.buff.Write(hack.Slice(strconv.Itoa(len(lst))))
w.buff.Write(Delims)
for i := 0; i < len(lst); i++ {
switch v := lst[i].(type) {
case []interface{}:
w.writeArray(v)
2014-08-26 19:21:45 +04:00
case [][]byte:
w.writeSliceArray(v)
case []byte:
w.writeBulk(v)
case nil:
w.writeBulk(nil)
case int64:
w.writeInteger(v)
default:
2015-02-03 09:15:11 +03:00
panic(fmt.Sprintf("invalid array type %T %v", lst[i], v))
}
}
}
}
func (w *respWriter) writeSliceArray(lst [][]byte) {
w.buff.WriteByte('*')
if lst == nil {
w.buff.Write(NullArray)
w.buff.Write(Delims)
} else {
2014-09-24 08:34:21 +04:00
w.buff.Write(hack.Slice(strconv.Itoa(len(lst))))
w.buff.Write(Delims)
for i := 0; i < len(lst); i++ {
w.writeBulk(lst[i])
}
}
}
func (w *respWriter) writeFVPairArray(lst []ledis.FVPair) {
w.buff.WriteByte('*')
if lst == nil {
w.buff.Write(NullArray)
w.buff.Write(Delims)
} else {
2014-09-24 08:34:21 +04:00
w.buff.Write(hack.Slice(strconv.Itoa(len(lst) * 2)))
w.buff.Write(Delims)
for i := 0; i < len(lst); i++ {
w.writeBulk(lst[i].Field)
w.writeBulk(lst[i].Value)
}
}
}
func (w *respWriter) writeScorePairArray(lst []ledis.ScorePair, withScores bool) {
w.buff.WriteByte('*')
if lst == nil {
w.buff.Write(NullArray)
w.buff.Write(Delims)
} else {
if withScores {
2014-09-24 08:34:21 +04:00
w.buff.Write(hack.Slice(strconv.Itoa(len(lst) * 2)))
w.buff.Write(Delims)
} else {
2014-09-24 08:34:21 +04:00
w.buff.Write(hack.Slice(strconv.Itoa(len(lst))))
w.buff.Write(Delims)
}
for i := 0; i < len(lst); i++ {
w.writeBulk(lst[i].Member)
if withScores {
2014-09-24 09:29:27 +04:00
w.writeBulk(num.FormatInt64ToSlice(lst[i].Score))
}
}
}
}
func (w *respWriter) writeBulkFrom(n int64, rb io.Reader) {
w.buff.WriteByte('$')
2014-09-24 08:34:21 +04:00
w.buff.Write(hack.Slice(strconv.FormatInt(n, 10)))
w.buff.Write(Delims)
io.Copy(w.buff, rb)
w.buff.Write(Delims)
}
func (w *respWriter) flush() {
w.buff.Flush()
}