-
Notifications
You must be signed in to change notification settings - Fork 0
/
redisRequest.go
153 lines (128 loc) · 3.41 KB
/
redisRequest.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"log"
"strings"
)
type RedisRequestType int
const (
REQUEST_UNSUPPORTED RedisRequestType = iota
REQUEST_REDIS_GET
REQUEST_REDIS_MGET
REQUEST_REDIS_SET
REQUEST_REDIS_COMMAND
REQUEST_REDIS_INFO
REQUEST_REDIS_PING
)
type requestProperties struct {
name string
}
var RequestTypeDesc = [...]requestProperties{
REQUEST_UNSUPPORTED: {name: "REQUEST_UNKNOWN"},
REQUEST_REDIS_GET: {name: "GET"},
REQUEST_REDIS_MGET: {name: "MGET"},
REQUEST_REDIS_SET: {name: "SET"},
REQUEST_REDIS_COMMAND: {name: "COMMAND"},
REQUEST_REDIS_INFO: {name: "INFO"},
REQUEST_REDIS_PING: {name: "PING"},
}
// Helper to map a protocol string to its internal request type
type requestStringMapper struct {
m map[string]RedisRequestType
}
func newRequestStringMapper() requestStringMapper {
return requestStringMapper{m: make(map[string]RedisRequestType)}
}
func (m *requestStringMapper) add(name string, id RedisRequestType) {
m.m[strings.ToUpper(name)] = id
return
}
func (m *requestStringMapper) get(request string) RedisRequestType {
t, ok := m.m[strings.ToUpper(request)]
if ok != true {
t = REQUEST_UNSUPPORTED
}
return t
}
var gRM requestStringMapper = newRequestStringMapper()
func init() {
for i, v := range RequestTypeDesc {
log.Println("Adding ", v, RedisRequestType(i))
gRM.add(v.name, RedisRequestType(i))
}
}
func GetRequestTypeFromString(r string) RedisRequestType {
return gRM.get(r)
}
type RedisRequest struct {
Name string
requestType RedisRequestType
Args [][]byte
}
func readArgument(r *bufio.Reader) ([]byte, error) {
line, err := r.ReadString('\n')
if err != nil {
return nil, err
}
var length int
if _, err = fmt.Sscanf(line, "$%d\r\n", &length); err != nil {
return nil, fmt.Errorf("invalid length for argument in %s", line)
}
// we know the length of the argument. Just read it.
data, err := ioutil.ReadAll(io.LimitReader(r, int64(length)))
if err != nil {
return nil, err
}
if len(data) != length {
return nil, fmt.Errorf("Expected length %d, received %d : data'%s'", length, len(data), data)
}
// Now check for trailing CR
if b, err := r.ReadByte(); err != nil || b != '\r' {
return nil, fmt.Errorf("Expected \\r, %s", err.Error())
}
// And LF
if b, err := r.ReadByte(); err != nil || b != '\n' {
return nil, fmt.Errorf("Expected \\n, %s", err.Error())
}
return data, nil
}
func parseRequest(r *bufio.Reader) (*RedisRequest, error) {
line, err := r.ReadString('\n')
if err != nil {
return nil, err
}
if len(line) == 0 {
return nil, fmt.Errorf("Empty line")
}
var argsCount int
if _, err := fmt.Sscanf(line, "*%d\r\n", &argsCount); err != nil {
return nil, fmt.Errorf("invalid number of arguments in %s", line)
}
// All next lines are pairs of:
//$<argument length> CR LF
//<argument data> CR LF
// first argument is a command name, so just convert
firstArg, err := readArgument(r)
if err != nil {
return nil, err
}
args := make([][]byte, argsCount-1)
for i := 0; i < argsCount-1; i += 1 {
if args[i], err = readArgument(r); err != nil {
return nil, err
}
}
var requestType RedisRequestType = GetRequestTypeFromString(string(firstArg))
if requestType == REQUEST_UNSUPPORTED {
return nil, fmt.Errorf("Invalid or unsupported request")
}
req := &RedisRequest{
requestType: requestType,
Name: strings.ToUpper(string(firstArg)),
Args: args,
}
return req, nil
}