-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathprotocol.go
179 lines (161 loc) · 4.95 KB
/
protocol.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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package mc
// Deal with the protocol specification of Memcached.
// Error represents a MemCache error (including the status code). All function
// in mc return error values of this type, despite the functions using the plain
// error type. You can safely cast all error types returned by mc to *Error. If
// needed, we take an underlying error value (such as a network error) and wrap
// it in Error, storing the underlying value in WrappedError.
//
// error is used as the return typed instead of Error directly due to the
// limitation in Go where error(nil) != *Error(nil).
type Error struct {
Status uint16
Message string
WrappedError error
}
// Error returns a human readable reason for the error (part of the error
// interface).
func (err Error) Error() string {
return err.Message
}
// Errors mc may return. Some errors aren't represented here as the message is
// dynamically generated. Status Code however captures all possible values for
// Error.Status.
var (
ErrNotFound = &Error{StatusNotFound, "mc: not found", nil}
ErrKeyExists = &Error{StatusKeyExists, "mc: key exists", nil}
ErrValueTooLarge = &Error{StatusValueNotStored, "mc: value to large", nil}
ErrInvalidArgs = &Error{StatusInvalidArgs, "mc: invalid arguments", nil}
ErrValueNotStored = &Error{StatusValueNotStored, "mc: value not stored", nil}
ErrNonNumeric = &Error{StatusNonNumeric, "mc: incr/decr called on non-numeric value", nil}
ErrAuthRequired = &Error{StatusAuthRequired, "mc: authentication required", nil}
ErrAuthContinue = &Error{StatusAuthContinue, "mc: authentication continue (unsupported)", nil}
ErrUnknownCommand = &Error{StatusUnknownCommand, "mc: unknown command", nil}
ErrOutOfMemory = &Error{StatusOutOfMemory, "mc: out of memory", nil}
ErrUnknownError = &Error{StatusUnknownError, "mc: unknown error from server", nil}
)
// Status Codes that may be returned (usually as part of an Error).
const (
StatusOK = uint16(0)
StatusNotFound = uint16(1)
StatusKeyExists = uint16(2)
StatusValueTooLarge = uint16(3)
StatusInvalidArgs = uint16(4)
StatusValueNotStored = uint16(5)
StatusNonNumeric = uint16(6)
StatusAuthRequired = uint16(0x20)
StatusAuthContinue = uint16(0x21)
StatusUnknownCommand = uint16(0x81)
StatusOutOfMemory = uint16(0x82)
StatusAuthUnknown = uint16(0xffff)
StatusNetworkError = uint16(0xfff1)
StatusUnknownError = uint16(0xffff)
)
// newError takes a status from the server and creates a matching Error.
func newError(status uint16) error {
switch status {
case StatusOK:
return nil
case StatusNotFound:
return ErrNotFound
case StatusKeyExists:
return ErrKeyExists
case StatusValueTooLarge:
return ErrValueTooLarge
case StatusInvalidArgs:
return ErrInvalidArgs
case StatusValueNotStored:
return ErrValueNotStored
case StatusNonNumeric:
return ErrNonNumeric
case StatusAuthRequired:
return ErrAuthRequired
// we only support PLAIN auth, no mechanism that would make use of auth
// continue, so make it an error for now for completeness.
case StatusAuthContinue:
return ErrAuthContinue
case StatusUnknownCommand:
return ErrUnknownCommand
case StatusOutOfMemory:
return ErrOutOfMemory
}
return ErrUnknownError
}
// wrapError wraps an existing error in an Error value.
func wrapError(status uint16, err error) error {
return &Error{status, err.Error(), err}
}
type opCode uint8
// ops
const (
opGet opCode = opCode(iota)
opSet
opAdd
opReplace
opDelete
opIncrement
opDecrement
opQuit
opFlush
opGetQ
opNoop
opVersion
opGetK
opGetKQ
opAppend
opPrepend
opStat
opSetQ
opAddQ
opReplaceQ
opDeleteQ
opIncrementQ
opDecrementQ
opQuitQ
opFlushQ
opAppendQ
opPrependQ
opVerbosity // verbosity - not implemented in memcached (but other servers)
opTouch
opGAT
opGATQ
opGATK = opCode(0x23)
opGATKQ = opCode(0x24)
)
// Auth Ops
const (
opAuthList opCode = opCode(iota + 0x20)
opAuthStart
opAuthStep
)
// Magic Codes
type magicCode uint8
const (
magicSend magicCode = 0x80
magicRecv magicCode = 0x81
)
// Memcache header
type header struct {
Magic magicCode
Op opCode
KeyLen uint16
ExtraLen uint8
DataType uint8 // not used, memcached expects it to be 0x00.
ResvOrStatus uint16 // for request this field is reserved / unused, for
// response it indicates the status
BodyLen uint32
Opaque uint32 // copied back to you in response message (message id)
CAS uint64 // version really
}
// Main Memcache message structure
type msg struct {
header // [0..23]
iextras []interface{} // [24..(m-1)] Command specific extras (In)
// Idea of this is we can pass in pointers to values that should appear in the
// response extras in this field and the generic send/receive code can handle.
oextras []interface{} // [24..(m-1)] Command specifc extras (Out)
key string // [m..(n-1)] Key (as needed, length in header)
val string // [n..x] Value (as needed, length in header)
}
// Memcache stats
type McStats map[string]string