redis/request.go

335 lines
6.1 KiB
Go

package redis
import (
"errors"
"fmt"
"io"
"strconv"
"github.com/togoio/redisgoproxy/bufreader"
)
var Nil = errors.New("(nil)")
//------------------------------------------------------------------------------
func ParseReq(rd *bufreader.Reader) ([]string, error) {
line, err := rd.ReadLine('\n')
if err != nil {
return nil, err
}
if line[0] != '*' {
return []string{string(line)}, nil
}
args := make([]string, 0)
for {
line, err = rd.ReadLine('\n')
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
if line[0] != '$' {
return nil, fmt.Errorf("Expected '$', but got %q of %q.", line, rd.Bytes())
}
line, err = rd.ReadLine('\n')
args = append(args, string(line))
}
return args, nil
}
//------------------------------------------------------------------------------
func PackReq(args []string) []byte {
buf := make([]byte, 0, 1024)
buf = append(buf, '*')
buf = strconv.AppendUint(buf, uint64(len(args)), 10)
buf = append(buf, '\r', '\n')
for _, arg := range args {
buf = append(buf, '$')
buf = strconv.AppendUint(buf, uint64(len(arg)), 10)
buf = append(buf, '\r', '\n')
buf = append(buf, []byte(arg)...)
buf = append(buf, '\r', '\n')
}
return buf
}
//------------------------------------------------------------------------------
type Req interface {
Req() []byte
ParseReply(*bufreader.Reader)
SetErr(error)
Err() error
}
//------------------------------------------------------------------------------
type BaseReq struct {
args []string
err error
}
func NewBaseReq(args ...string) *BaseReq {
return &BaseReq{
args: args,
}
}
func (r *BaseReq) Req() []byte {
return PackReq(r.args)
}
func (r *BaseReq) SetErr(err error) {
r.err = err
}
func (r *BaseReq) Err() error {
return r.err
}
//------------------------------------------------------------------------------
type StatusReq struct {
*BaseReq
val string
}
func NewStatusReq(args ...string) *StatusReq {
return &StatusReq{
BaseReq: NewBaseReq(args...),
}
}
func (r *StatusReq) ParseReply(rd *bufreader.Reader) {
var line []byte
line, r.err = rd.ReadLine('\n')
if r.err != nil {
return
}
if line[0] == '-' {
r.err = errors.New(string(line[1:]))
return
} else if line[0] != '+' {
r.err = fmt.Errorf("Expected '+', but got %q of %q.", line, rd.Bytes())
return
}
r.val = string(line[1:])
}
func (r *StatusReq) Reply() (string, error) {
return r.val, r.err
}
//------------------------------------------------------------------------------
type IntReq struct {
*BaseReq
val int64
}
func NewIntReq(args ...string) *IntReq {
return &IntReq{
BaseReq: NewBaseReq(args...),
}
}
func (r *IntReq) ParseReply(rd *bufreader.Reader) {
var line []byte
line, r.err = rd.ReadLine('\n')
if r.err != nil {
return
}
if line[0] == '-' {
r.err = errors.New(string(line[1:]))
return
} else if line[0] != ':' {
r.err = fmt.Errorf("Expected ':', but got %q of %q.", line, rd.Bytes())
return
}
r.val, r.err = strconv.ParseInt(string(line[1:]), 10, 64)
}
func (r *IntReq) Reply() (int64, error) {
return r.val, r.err
}
//------------------------------------------------------------------------------
type BoolReq struct {
*BaseReq
val bool
}
func NewBoolReq(args ...string) *BoolReq {
return &BoolReq{
BaseReq: NewBaseReq(args...),
}
}
func (r *BoolReq) ParseReply(rd *bufreader.Reader) {
var line []byte
line, r.err = rd.ReadLine('\n')
if r.err != nil {
return
}
if line[0] == '-' {
r.err = errors.New(string(line[1:]))
return
} else if line[0] != ':' {
r.err = fmt.Errorf("Expected ':', but got %q of %q.", line, rd.Bytes())
return
}
r.val = line[1] == '1'
}
func (r *BoolReq) Reply() (bool, error) {
return r.val, r.err
}
//------------------------------------------------------------------------------
type BulkReq struct {
*BaseReq
val string
}
func NewBulkReq(args ...string) *BulkReq {
return &BulkReq{
BaseReq: NewBaseReq(args...),
}
}
func (r *BulkReq) ParseReply(rd *bufreader.Reader) {
var line []byte
line, r.err = rd.ReadLine('\n')
if r.err != nil {
return
}
if line[0] == '-' {
r.err = errors.New(string(line[1:]))
return
} else if line[0] != '$' {
r.err = fmt.Errorf("Expected '$', but got %q of %q.", line, rd.Bytes())
return
}
if len(line) >= 3 && line[1] == '-' && line[2] == '1' {
r.err = Nil
return
}
line, r.err = rd.ReadLine('\n')
if r.err != nil {
return
}
r.val = string(line)
}
func (r *BulkReq) Reply() (string, error) {
return r.val, r.err
}
//------------------------------------------------------------------------------
type MultiBulkReq struct {
*BaseReq
val []interface{}
}
func NewMultiBulkReq(args ...string) *MultiBulkReq {
return &MultiBulkReq{
BaseReq: NewBaseReq(args...),
}
}
func (r *MultiBulkReq) ParseReply(rd *bufreader.Reader) {
var line []byte
line, r.err = rd.ReadLine('\n')
if r.err != nil {
return
}
if line[0] == '-' {
r.err = errors.New(string(line[1:]))
return
} else if line[0] != '*' {
r.err = fmt.Errorf("Expected '*', but got line %q of %q.", line, rd.Bytes())
return
}
val := make([]interface{}, 0)
if len(line) >= 2 && line[1] == '0' {
r.val = val
return
} else if len(line) >= 3 && line[1] == '-' && line[2] == '1' {
r.err = Nil
return
}
line, r.err = rd.ReadLine('\n')
if r.err != nil {
return
}
for {
if line[0] == ':' {
var n int64
n, r.err = strconv.ParseInt(string(line[1:]), 10, 64)
if r.err != nil {
return
}
val = append(val, n)
} else if line[0] == '$' {
if len(line) >= 2 && line[1] == '0' {
val = append(val, "")
} else if len(line) >= 3 && line[1] == '-' && line[2] == '1' {
val = append(val, nil)
} else {
line, r.err = rd.ReadLine('\n')
if r.err != nil {
return
}
val = append(val, string(line))
}
} else {
r.err = fmt.Errorf("Expected '$', but got line %q of %q.", line, rd.Bytes())
return
}
line, r.err = rd.ReadLine('\n')
if r.err == io.EOF {
r.err = nil
break
}
// Check for start of another reply.
if line[0] == '*' {
rd.UnreadLine('\n')
break
}
}
r.val = val
}
func (r *MultiBulkReq) Reply() ([]interface{}, error) {
return r.val, r.err
}