mirror of https://github.com/ledisdb/ledisdb.git
246 lines
4.6 KiB
Go
246 lines
4.6 KiB
Go
|
package server
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"errors"
|
||
|
"github.com/siddontang/ledisdb/ledis"
|
||
|
"io"
|
||
|
"net"
|
||
|
"strconv"
|
||
|
)
|
||
|
|
||
|
type tcpContext struct {
|
||
|
conn net.Conn
|
||
|
}
|
||
|
|
||
|
type tcpWriter struct {
|
||
|
buff *bufio.Writer
|
||
|
}
|
||
|
|
||
|
type tcpReader struct {
|
||
|
buff *bufio.Reader
|
||
|
}
|
||
|
|
||
|
// tcp context
|
||
|
|
||
|
func newTcpContext(conn net.Conn) *tcpContext {
|
||
|
ctx := new(tcpContext)
|
||
|
ctx.conn = conn
|
||
|
return ctx
|
||
|
}
|
||
|
|
||
|
func (ctx *tcpContext) addr() string {
|
||
|
return ctx.conn.RemoteAddr().String()
|
||
|
}
|
||
|
|
||
|
func (ctx *tcpContext) release() {
|
||
|
if ctx.conn != nil {
|
||
|
ctx.conn.Close()
|
||
|
ctx.conn = nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// tcp reader
|
||
|
|
||
|
func newTcpReader(conn net.Conn) *tcpReader {
|
||
|
r := new(tcpReader)
|
||
|
r.buff = bufio.NewReaderSize(conn, 256)
|
||
|
return r
|
||
|
}
|
||
|
|
||
|
func (r *tcpReader) readLine() ([]byte, error) {
|
||
|
return ReadLine(r.buff)
|
||
|
}
|
||
|
|
||
|
//A client sends to the Redis server a RESP Array consisting of just Bulk Strings.
|
||
|
func (r *tcpReader) read() ([][]byte, error) {
|
||
|
l, err := r.readLine()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
} else if len(l) == 0 || l[0] != '*' {
|
||
|
return nil, errReadRequest
|
||
|
}
|
||
|
|
||
|
var nparams int
|
||
|
if nparams, err = strconv.Atoi(ledis.String(l[1:])); err != nil {
|
||
|
return nil, err
|
||
|
} else if nparams <= 0 {
|
||
|
return nil, errReadRequest
|
||
|
}
|
||
|
|
||
|
reqData := make([][]byte, 0, nparams)
|
||
|
var n int
|
||
|
for i := 0; i < nparams; i++ {
|
||
|
if l, err = r.readLine(); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if len(l) == 0 {
|
||
|
return nil, errReadRequest
|
||
|
} else if l[0] == '$' {
|
||
|
//handle resp string
|
||
|
if n, err = strconv.Atoi(ledis.String(l[1:])); err != nil {
|
||
|
return nil, err
|
||
|
} else if n == -1 {
|
||
|
reqData = append(reqData, nil)
|
||
|
} else {
|
||
|
buf := make([]byte, n)
|
||
|
if _, err = io.ReadFull(r.buff, buf); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if l, err = r.readLine(); err != nil {
|
||
|
return nil, err
|
||
|
} else if len(l) != 0 {
|
||
|
return nil, errors.New("bad bulk string format")
|
||
|
}
|
||
|
|
||
|
reqData = append(reqData, buf)
|
||
|
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
return nil, errReadRequest
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return reqData, nil
|
||
|
}
|
||
|
|
||
|
// tcp writer
|
||
|
|
||
|
func newTcpWriter(conn net.Conn) *tcpWriter {
|
||
|
w := new(tcpWriter)
|
||
|
w.buff = bufio.NewWriterSize(conn, 256)
|
||
|
return w
|
||
|
}
|
||
|
|
||
|
func (w *tcpWriter) writeError(err error) {
|
||
|
w.buff.Write(ledis.Slice("-ERR"))
|
||
|
if err != nil {
|
||
|
w.buff.WriteByte(' ')
|
||
|
w.buff.Write(ledis.Slice(err.Error()))
|
||
|
}
|
||
|
w.buff.Write(Delims)
|
||
|
}
|
||
|
|
||
|
func (w *tcpWriter) writeStatus(status string) {
|
||
|
w.buff.WriteByte('+')
|
||
|
w.buff.Write(ledis.Slice(status))
|
||
|
w.buff.Write(Delims)
|
||
|
}
|
||
|
|
||
|
func (w *tcpWriter) writeInteger(n int64) {
|
||
|
w.buff.WriteByte(':')
|
||
|
w.buff.Write(ledis.StrPutInt64(n))
|
||
|
w.buff.Write(Delims)
|
||
|
}
|
||
|
|
||
|
func (w *tcpWriter) writeBulk(b []byte) {
|
||
|
w.buff.WriteByte('$')
|
||
|
if b == nil {
|
||
|
w.buff.Write(NullBulk)
|
||
|
} else {
|
||
|
w.buff.Write(ledis.Slice(strconv.Itoa(len(b))))
|
||
|
w.buff.Write(Delims)
|
||
|
w.buff.Write(b)
|
||
|
}
|
||
|
|
||
|
w.buff.Write(Delims)
|
||
|
}
|
||
|
|
||
|
func (w *tcpWriter) writeArray(lst []interface{}) {
|
||
|
w.buff.WriteByte('*')
|
||
|
if lst == nil {
|
||
|
w.buff.Write(NullArray)
|
||
|
w.buff.Write(Delims)
|
||
|
} else {
|
||
|
w.buff.Write(ledis.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)
|
||
|
case []byte:
|
||
|
w.writeBulk(v)
|
||
|
case nil:
|
||
|
w.writeBulk(nil)
|
||
|
case int64:
|
||
|
w.writeInteger(v)
|
||
|
default:
|
||
|
panic("invalid array type")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (w *tcpWriter) writeSliceArray(lst [][]byte) {
|
||
|
w.buff.WriteByte('*')
|
||
|
if lst == nil {
|
||
|
w.buff.Write(NullArray)
|
||
|
w.buff.Write(Delims)
|
||
|
} else {
|
||
|
w.buff.Write(ledis.Slice(strconv.Itoa(len(lst))))
|
||
|
w.buff.Write(Delims)
|
||
|
|
||
|
for i := 0; i < len(lst); i++ {
|
||
|
w.writeBulk(lst[i])
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (w *tcpWriter) writeFVPairArray(lst []ledis.FVPair) {
|
||
|
w.buff.WriteByte('*')
|
||
|
if lst == nil {
|
||
|
w.buff.Write(NullArray)
|
||
|
w.buff.Write(Delims)
|
||
|
} else {
|
||
|
w.buff.Write(ledis.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 *tcpWriter) writeScorePairArray(lst []ledis.ScorePair, withScores bool) {
|
||
|
w.buff.WriteByte('*')
|
||
|
if lst == nil {
|
||
|
w.buff.Write(NullArray)
|
||
|
w.buff.Write(Delims)
|
||
|
} else {
|
||
|
if withScores {
|
||
|
w.buff.Write(ledis.Slice(strconv.Itoa(len(lst) * 2)))
|
||
|
w.buff.Write(Delims)
|
||
|
} else {
|
||
|
w.buff.Write(ledis.Slice(strconv.Itoa(len(lst))))
|
||
|
w.buff.Write(Delims)
|
||
|
|
||
|
}
|
||
|
|
||
|
for i := 0; i < len(lst); i++ {
|
||
|
w.writeBulk(lst[i].Member)
|
||
|
|
||
|
if withScores {
|
||
|
w.writeBulk(ledis.StrPutInt64(lst[i].Score))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (w *tcpWriter) writeBulkFrom(n int64, rb io.Reader) {
|
||
|
w.buff.WriteByte('$')
|
||
|
w.buff.Write(ledis.Slice(strconv.FormatInt(n, 10)))
|
||
|
w.buff.Write(Delims)
|
||
|
|
||
|
io.Copy(w.buff, rb)
|
||
|
w.buff.Write(Delims)
|
||
|
}
|
||
|
|
||
|
func (w *tcpWriter) flush() {
|
||
|
w.buff.Flush()
|
||
|
}
|