2014-06-09 13:23:32 +04:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"errors"
|
2014-10-28 12:58:37 +03:00
|
|
|
"fmt"
|
2014-10-29 19:01:19 +03:00
|
|
|
"github.com/siddontang/go/arena"
|
2014-10-28 12:58:37 +03:00
|
|
|
"io"
|
2014-06-09 13:23:32 +04:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2014-10-23 09:34:27 +04:00
|
|
|
errLineFormat = errors.New("bad response line format")
|
2014-06-09 13:23:32 +04:00
|
|
|
)
|
|
|
|
|
2014-07-11 06:43:39 +04:00
|
|
|
func ReadLine(rb *bufio.Reader) ([]byte, error) {
|
2014-06-09 13:23:32 +04:00
|
|
|
p, err := rb.ReadSlice('\n')
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
i := len(p) - 2
|
|
|
|
if i < 0 || p[i] != '\r' {
|
|
|
|
return nil, errLineFormat
|
|
|
|
}
|
2014-10-28 12:58:37 +03:00
|
|
|
|
2014-06-09 13:23:32 +04:00
|
|
|
return p[:i], nil
|
|
|
|
}
|
2014-10-28 12:58:37 +03:00
|
|
|
|
2014-10-29 19:01:19 +03:00
|
|
|
func readBytes(br *bufio.Reader, a *arena.Arena) (bytes []byte, err error) {
|
2014-10-28 12:58:37 +03:00
|
|
|
size, err := readLong(br)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if size == -1 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
if size < 0 {
|
|
|
|
return nil, errors.New("Invalid size: " + fmt.Sprint("%d", size))
|
|
|
|
}
|
|
|
|
|
2014-10-29 19:01:19 +03:00
|
|
|
buf := a.Make(int(size) + 2)
|
2014-10-28 12:58:37 +03:00
|
|
|
if _, err = io.ReadFull(br, buf); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if buf[len(buf)-2] != '\r' && buf[len(buf)-1] != '\n' {
|
|
|
|
return nil, errors.New("bad bulk string format")
|
|
|
|
}
|
|
|
|
|
|
|
|
bytes = buf[0 : len(buf)-2]
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func readLong(in *bufio.Reader) (result int64, err error) {
|
|
|
|
read, err := in.ReadByte()
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
var sign int
|
|
|
|
if read == '-' {
|
|
|
|
read, err = in.ReadByte()
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
sign = -1
|
|
|
|
} else {
|
|
|
|
sign = 1
|
|
|
|
}
|
|
|
|
var number int64
|
|
|
|
for number = 0; err == nil; read, err = in.ReadByte() {
|
|
|
|
if read == '\r' {
|
|
|
|
read, err = in.ReadByte()
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
if read == '\n' {
|
|
|
|
return number * int64(sign), nil
|
|
|
|
} else {
|
|
|
|
return -1, errors.New("Bad line ending")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
value := read - '0'
|
|
|
|
if value >= 0 && value < 10 {
|
|
|
|
number *= 10
|
|
|
|
number += int64(value)
|
|
|
|
} else {
|
|
|
|
return -1, errors.New("Invalid digit")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1, err
|
|
|
|
}
|
2014-10-29 04:11:22 +03:00
|
|
|
|
2014-10-29 19:01:19 +03:00
|
|
|
func ReadRequest(in *bufio.Reader, a *arena.Arena) ([][]byte, error) {
|
2014-10-29 04:11:22 +03:00
|
|
|
code, err := in.ReadByte()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if code != '*' {
|
|
|
|
return nil, errReadRequest
|
|
|
|
}
|
|
|
|
|
|
|
|
var nparams int64
|
|
|
|
if nparams, err = readLong(in); err != nil {
|
|
|
|
return nil, err
|
|
|
|
} else if nparams <= 0 {
|
|
|
|
return nil, errReadRequest
|
|
|
|
}
|
|
|
|
|
|
|
|
req := make([][]byte, nparams)
|
|
|
|
for i := range req {
|
|
|
|
if code, err = in.ReadByte(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
} else if code != '$' {
|
|
|
|
return nil, errReadRequest
|
|
|
|
}
|
|
|
|
|
2014-10-29 19:01:19 +03:00
|
|
|
if req[i], err = readBytes(in, a); err != nil {
|
2014-10-29 04:11:22 +03:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return req, nil
|
|
|
|
}
|