forked from Shiloh/githaven
169 lines
3.9 KiB
Go
169 lines
3.9 KiB
Go
|
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
|
||
|
}
|