Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
3.9 KiB
168 lines
3.9 KiB
package gomemcached |
|
|
|
import ( |
|
"bytes" |
|
"encoding/binary" |
|
"fmt" |
|
"io" |
|
"io/ioutil" |
|
"strings" |
|
) |
|
|
|
type TapConnectFlag uint32 |
|
|
|
// Tap connect option flags |
|
const ( |
|
BACKFILL = TapConnectFlag(0x01) |
|
DUMP = TapConnectFlag(0x02) |
|
LIST_VBUCKETS = TapConnectFlag(0x04) |
|
TAKEOVER_VBUCKETS = TapConnectFlag(0x08) |
|
SUPPORT_ACK = TapConnectFlag(0x10) |
|
REQUEST_KEYS_ONLY = TapConnectFlag(0x20) |
|
CHECKPOINT = TapConnectFlag(0x40) |
|
REGISTERED_CLIENT = TapConnectFlag(0x80) |
|
FIX_FLAG_BYTEORDER = TapConnectFlag(0x100) |
|
) |
|
|
|
// Tap opaque event subtypes |
|
const ( |
|
TAP_OPAQUE_ENABLE_AUTO_NACK = 0 |
|
TAP_OPAQUE_INITIAL_VBUCKET_STREAM = 1 |
|
TAP_OPAQUE_ENABLE_CHECKPOINT_SYNC = 2 |
|
TAP_OPAQUE_CLOSE_TAP_STREAM = 7 |
|
TAP_OPAQUE_CLOSE_BACKFILL = 8 |
|
) |
|
|
|
// Tap item flags |
|
const ( |
|
TAP_ACK = 1 |
|
TAP_NO_VALUE = 2 |
|
TAP_FLAG_NETWORK_BYTE_ORDER = 4 |
|
) |
|
|
|
// TapConnectFlagNames for TapConnectFlag |
|
var TapConnectFlagNames = map[TapConnectFlag]string{ |
|
BACKFILL: "BACKFILL", |
|
DUMP: "DUMP", |
|
LIST_VBUCKETS: "LIST_VBUCKETS", |
|
TAKEOVER_VBUCKETS: "TAKEOVER_VBUCKETS", |
|
SUPPORT_ACK: "SUPPORT_ACK", |
|
REQUEST_KEYS_ONLY: "REQUEST_KEYS_ONLY", |
|
CHECKPOINT: "CHECKPOINT", |
|
REGISTERED_CLIENT: "REGISTERED_CLIENT", |
|
FIX_FLAG_BYTEORDER: "FIX_FLAG_BYTEORDER", |
|
} |
|
|
|
// TapItemParser is a function to parse a single tap extra. |
|
type TapItemParser func(io.Reader) (interface{}, error) |
|
|
|
// TapParseUint64 is a function to parse a single tap uint64. |
|
func TapParseUint64(r io.Reader) (interface{}, error) { |
|
var rv uint64 |
|
err := binary.Read(r, binary.BigEndian, &rv) |
|
return rv, err |
|
} |
|
|
|
// TapParseUint16 is a function to parse a single tap uint16. |
|
func TapParseUint16(r io.Reader) (interface{}, error) { |
|
var rv uint16 |
|
err := binary.Read(r, binary.BigEndian, &rv) |
|
return rv, err |
|
} |
|
|
|
// TapParseBool is a function to parse a single tap boolean. |
|
func TapParseBool(r io.Reader) (interface{}, error) { |
|
return true, nil |
|
} |
|
|
|
// TapParseVBList parses a list of vBucket numbers as []uint16. |
|
func TapParseVBList(r io.Reader) (interface{}, error) { |
|
num, err := TapParseUint16(r) |
|
if err != nil { |
|
return nil, err |
|
} |
|
n := int(num.(uint16)) |
|
|
|
rv := make([]uint16, n) |
|
for i := 0; i < n; i++ { |
|
x, err := TapParseUint16(r) |
|
if err != nil { |
|
return nil, err |
|
} |
|
rv[i] = x.(uint16) |
|
} |
|
|
|
return rv, err |
|
} |
|
|
|
// TapFlagParsers parser functions for TAP fields. |
|
var TapFlagParsers = map[TapConnectFlag]TapItemParser{ |
|
BACKFILL: TapParseUint64, |
|
LIST_VBUCKETS: TapParseVBList, |
|
} |
|
|
|
// SplitFlags will split the ORed flags into the individual bit flags. |
|
func (f TapConnectFlag) SplitFlags() []TapConnectFlag { |
|
rv := []TapConnectFlag{} |
|
for i := uint32(1); f != 0; i = i << 1 { |
|
if uint32(f)&i == i { |
|
rv = append(rv, TapConnectFlag(i)) |
|
} |
|
f = TapConnectFlag(uint32(f) & (^i)) |
|
} |
|
return rv |
|
} |
|
|
|
func (f TapConnectFlag) String() string { |
|
parts := []string{} |
|
for _, x := range f.SplitFlags() { |
|
p := TapConnectFlagNames[x] |
|
if p == "" { |
|
p = fmt.Sprintf("0x%x", int(x)) |
|
} |
|
parts = append(parts, p) |
|
} |
|
return strings.Join(parts, "|") |
|
} |
|
|
|
type TapConnect struct { |
|
Flags map[TapConnectFlag]interface{} |
|
RemainingBody []byte |
|
Name string |
|
} |
|
|
|
// ParseTapCommands parse the tap request into the interesting bits we may |
|
// need to do something with. |
|
func (req *MCRequest) ParseTapCommands() (TapConnect, error) { |
|
rv := TapConnect{ |
|
Flags: map[TapConnectFlag]interface{}{}, |
|
Name: string(req.Key), |
|
} |
|
|
|
if len(req.Extras) < 4 { |
|
return rv, fmt.Errorf("not enough extra bytes: %x", req.Extras) |
|
} |
|
|
|
flags := TapConnectFlag(binary.BigEndian.Uint32(req.Extras)) |
|
|
|
r := bytes.NewReader(req.Body) |
|
|
|
for _, f := range flags.SplitFlags() { |
|
fun := TapFlagParsers[f] |
|
if fun == nil { |
|
fun = TapParseBool |
|
} |
|
|
|
val, err := fun(r) |
|
if err != nil { |
|
return rv, err |
|
} |
|
|
|
rv.Flags[f] = val |
|
} |
|
|
|
var err error |
|
rv.RemainingBody, err = ioutil.ReadAll(r) |
|
|
|
return rv, err |
|
}
|
|
|