mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	add other session providers (#5963)
This commit is contained in:
		
							
								
								
									
										19
									
								
								vendor/github.com/couchbase/gomemcached/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/couchbase/gomemcached/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
Copyright (c) 2013 Dustin Sallings
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
							
								
								
									
										1074
									
								
								vendor/github.com/couchbase/gomemcached/client/mc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1074
									
								
								vendor/github.com/couchbase/gomemcached/client/mc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										333
									
								
								vendor/github.com/couchbase/gomemcached/client/tap_feed.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										333
									
								
								vendor/github.com/couchbase/gomemcached/client/tap_feed.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,333 @@
 | 
			
		||||
package memcached
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math"
 | 
			
		||||
 | 
			
		||||
	"github.com/couchbase/gomemcached"
 | 
			
		||||
	"github.com/couchbase/goutils/logging"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TAP protocol docs: <http://www.couchbase.com/wiki/display/couchbase/TAP+Protocol>
 | 
			
		||||
 | 
			
		||||
// TapOpcode is the tap operation type (found in TapEvent)
 | 
			
		||||
type TapOpcode uint8
 | 
			
		||||
 | 
			
		||||
// Tap opcode values.
 | 
			
		||||
const (
 | 
			
		||||
	TapBeginBackfill = TapOpcode(iota)
 | 
			
		||||
	TapEndBackfill
 | 
			
		||||
	TapMutation
 | 
			
		||||
	TapDeletion
 | 
			
		||||
	TapCheckpointStart
 | 
			
		||||
	TapCheckpointEnd
 | 
			
		||||
	tapEndStream
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const tapMutationExtraLen = 16
 | 
			
		||||
 | 
			
		||||
var tapOpcodeNames map[TapOpcode]string
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	tapOpcodeNames = map[TapOpcode]string{
 | 
			
		||||
		TapBeginBackfill:   "BeginBackfill",
 | 
			
		||||
		TapEndBackfill:     "EndBackfill",
 | 
			
		||||
		TapMutation:        "Mutation",
 | 
			
		||||
		TapDeletion:        "Deletion",
 | 
			
		||||
		TapCheckpointStart: "TapCheckpointStart",
 | 
			
		||||
		TapCheckpointEnd:   "TapCheckpointEnd",
 | 
			
		||||
		tapEndStream:       "EndStream",
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (opcode TapOpcode) String() string {
 | 
			
		||||
	name := tapOpcodeNames[opcode]
 | 
			
		||||
	if name == "" {
 | 
			
		||||
		name = fmt.Sprintf("#%d", opcode)
 | 
			
		||||
	}
 | 
			
		||||
	return name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TapEvent is a TAP notification of an operation on the server.
 | 
			
		||||
type TapEvent struct {
 | 
			
		||||
	Opcode     TapOpcode // Type of event
 | 
			
		||||
	VBucket    uint16    // VBucket this event applies to
 | 
			
		||||
	Flags      uint32    // Item flags
 | 
			
		||||
	Expiry     uint32    // Item expiration time
 | 
			
		||||
	Key, Value []byte    // Item key/value
 | 
			
		||||
	Cas        uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makeTapEvent(req gomemcached.MCRequest) *TapEvent {
 | 
			
		||||
	event := TapEvent{
 | 
			
		||||
		VBucket: req.VBucket,
 | 
			
		||||
	}
 | 
			
		||||
	switch req.Opcode {
 | 
			
		||||
	case gomemcached.TAP_MUTATION:
 | 
			
		||||
		event.Opcode = TapMutation
 | 
			
		||||
		event.Key = req.Key
 | 
			
		||||
		event.Value = req.Body
 | 
			
		||||
		event.Cas = req.Cas
 | 
			
		||||
	case gomemcached.TAP_DELETE:
 | 
			
		||||
		event.Opcode = TapDeletion
 | 
			
		||||
		event.Key = req.Key
 | 
			
		||||
		event.Cas = req.Cas
 | 
			
		||||
	case gomemcached.TAP_CHECKPOINT_START:
 | 
			
		||||
		event.Opcode = TapCheckpointStart
 | 
			
		||||
	case gomemcached.TAP_CHECKPOINT_END:
 | 
			
		||||
		event.Opcode = TapCheckpointEnd
 | 
			
		||||
	case gomemcached.TAP_OPAQUE:
 | 
			
		||||
		if len(req.Extras) < 8+4 {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		switch op := int(binary.BigEndian.Uint32(req.Extras[8:])); op {
 | 
			
		||||
		case gomemcached.TAP_OPAQUE_INITIAL_VBUCKET_STREAM:
 | 
			
		||||
			event.Opcode = TapBeginBackfill
 | 
			
		||||
		case gomemcached.TAP_OPAQUE_CLOSE_BACKFILL:
 | 
			
		||||
			event.Opcode = TapEndBackfill
 | 
			
		||||
		case gomemcached.TAP_OPAQUE_CLOSE_TAP_STREAM:
 | 
			
		||||
			event.Opcode = tapEndStream
 | 
			
		||||
		case gomemcached.TAP_OPAQUE_ENABLE_AUTO_NACK:
 | 
			
		||||
			return nil
 | 
			
		||||
		case gomemcached.TAP_OPAQUE_ENABLE_CHECKPOINT_SYNC:
 | 
			
		||||
			return nil
 | 
			
		||||
		default:
 | 
			
		||||
			logging.Infof("TapFeed: Ignoring TAP_OPAQUE/%d", op)
 | 
			
		||||
			return nil // unknown opaque event
 | 
			
		||||
		}
 | 
			
		||||
	case gomemcached.NOOP:
 | 
			
		||||
		return nil // ignore
 | 
			
		||||
	default:
 | 
			
		||||
		logging.Infof("TapFeed: Ignoring %s", req.Opcode)
 | 
			
		||||
		return nil // unknown event
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(req.Extras) >= tapMutationExtraLen &&
 | 
			
		||||
		(event.Opcode == TapMutation || event.Opcode == TapDeletion) {
 | 
			
		||||
 | 
			
		||||
		event.Flags = binary.BigEndian.Uint32(req.Extras[8:])
 | 
			
		||||
		event.Expiry = binary.BigEndian.Uint32(req.Extras[12:])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &event
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (event TapEvent) String() string {
 | 
			
		||||
	switch event.Opcode {
 | 
			
		||||
	case TapBeginBackfill, TapEndBackfill, TapCheckpointStart, TapCheckpointEnd:
 | 
			
		||||
		return fmt.Sprintf("<TapEvent %s, vbucket=%d>",
 | 
			
		||||
			event.Opcode, event.VBucket)
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Sprintf("<TapEvent %s, key=%q (%d bytes) flags=%x, exp=%d>",
 | 
			
		||||
			event.Opcode, event.Key, len(event.Value),
 | 
			
		||||
			event.Flags, event.Expiry)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TapArguments are parameters for requesting a TAP feed.
 | 
			
		||||
//
 | 
			
		||||
// Call DefaultTapArguments to get a default one.
 | 
			
		||||
type TapArguments struct {
 | 
			
		||||
	// Timestamp of oldest item to send.
 | 
			
		||||
	//
 | 
			
		||||
	// Use TapNoBackfill to suppress all past items.
 | 
			
		||||
	Backfill uint64
 | 
			
		||||
	// If set, server will disconnect after sending existing items.
 | 
			
		||||
	Dump bool
 | 
			
		||||
	// The indices of the vbuckets to watch; empty/nil to watch all.
 | 
			
		||||
	VBuckets []uint16
 | 
			
		||||
	// Transfers ownership of vbuckets during cluster rebalance.
 | 
			
		||||
	Takeover bool
 | 
			
		||||
	// If true, server will wait for client ACK after every notification.
 | 
			
		||||
	SupportAck bool
 | 
			
		||||
	// If true, client doesn't want values so server shouldn't send them.
 | 
			
		||||
	KeysOnly bool
 | 
			
		||||
	// If true, client wants the server to send checkpoint events.
 | 
			
		||||
	Checkpoint bool
 | 
			
		||||
	// Optional identifier to use for this client, to allow reconnects
 | 
			
		||||
	ClientName string
 | 
			
		||||
	// Registers this client (by name) till explicitly deregistered.
 | 
			
		||||
	RegisteredClient bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Value for TapArguments.Backfill denoting that no past events at all
 | 
			
		||||
// should be sent.
 | 
			
		||||
const TapNoBackfill = math.MaxUint64
 | 
			
		||||
 | 
			
		||||
// DefaultTapArguments returns a default set of parameter values to
 | 
			
		||||
// pass to StartTapFeed.
 | 
			
		||||
func DefaultTapArguments() TapArguments {
 | 
			
		||||
	return TapArguments{
 | 
			
		||||
		Backfill: TapNoBackfill,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (args *TapArguments) flags() []byte {
 | 
			
		||||
	var flags gomemcached.TapConnectFlag
 | 
			
		||||
	if args.Backfill != 0 {
 | 
			
		||||
		flags |= gomemcached.BACKFILL
 | 
			
		||||
	}
 | 
			
		||||
	if args.Dump {
 | 
			
		||||
		flags |= gomemcached.DUMP
 | 
			
		||||
	}
 | 
			
		||||
	if len(args.VBuckets) > 0 {
 | 
			
		||||
		flags |= gomemcached.LIST_VBUCKETS
 | 
			
		||||
	}
 | 
			
		||||
	if args.Takeover {
 | 
			
		||||
		flags |= gomemcached.TAKEOVER_VBUCKETS
 | 
			
		||||
	}
 | 
			
		||||
	if args.SupportAck {
 | 
			
		||||
		flags |= gomemcached.SUPPORT_ACK
 | 
			
		||||
	}
 | 
			
		||||
	if args.KeysOnly {
 | 
			
		||||
		flags |= gomemcached.REQUEST_KEYS_ONLY
 | 
			
		||||
	}
 | 
			
		||||
	if args.Checkpoint {
 | 
			
		||||
		flags |= gomemcached.CHECKPOINT
 | 
			
		||||
	}
 | 
			
		||||
	if args.RegisteredClient {
 | 
			
		||||
		flags |= gomemcached.REGISTERED_CLIENT
 | 
			
		||||
	}
 | 
			
		||||
	encoded := make([]byte, 4)
 | 
			
		||||
	binary.BigEndian.PutUint32(encoded, uint32(flags))
 | 
			
		||||
	return encoded
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func must(err error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (args *TapArguments) bytes() (rv []byte) {
 | 
			
		||||
	buf := bytes.NewBuffer([]byte{})
 | 
			
		||||
 | 
			
		||||
	if args.Backfill > 0 {
 | 
			
		||||
		must(binary.Write(buf, binary.BigEndian, uint64(args.Backfill)))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(args.VBuckets) > 0 {
 | 
			
		||||
		must(binary.Write(buf, binary.BigEndian, uint16(len(args.VBuckets))))
 | 
			
		||||
		for i := 0; i < len(args.VBuckets); i++ {
 | 
			
		||||
			must(binary.Write(buf, binary.BigEndian, uint16(args.VBuckets[i])))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return buf.Bytes()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TapFeed represents a stream of events from a server.
 | 
			
		||||
type TapFeed struct {
 | 
			
		||||
	C      <-chan TapEvent
 | 
			
		||||
	Error  error
 | 
			
		||||
	closer chan bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StartTapFeed starts a TAP feed on a client connection.
 | 
			
		||||
//
 | 
			
		||||
// The events can be read from the returned channel.  The connection
 | 
			
		||||
// can no longer be used for other purposes; it's now reserved for
 | 
			
		||||
// receiving the TAP messages. To stop receiving events, close the
 | 
			
		||||
// client connection.
 | 
			
		||||
func (mc *Client) StartTapFeed(args TapArguments) (*TapFeed, error) {
 | 
			
		||||
	rq := &gomemcached.MCRequest{
 | 
			
		||||
		Opcode: gomemcached.TAP_CONNECT,
 | 
			
		||||
		Key:    []byte(args.ClientName),
 | 
			
		||||
		Extras: args.flags(),
 | 
			
		||||
		Body:   args.bytes()}
 | 
			
		||||
 | 
			
		||||
	err := mc.Transmit(rq)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ch := make(chan TapEvent)
 | 
			
		||||
	feed := &TapFeed{
 | 
			
		||||
		C:      ch,
 | 
			
		||||
		closer: make(chan bool),
 | 
			
		||||
	}
 | 
			
		||||
	go mc.runFeed(ch, feed)
 | 
			
		||||
	return feed, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TapRecvHook is called after every incoming tap packet is received.
 | 
			
		||||
var TapRecvHook func(*gomemcached.MCRequest, int, error)
 | 
			
		||||
 | 
			
		||||
// Internal goroutine that reads from the socket and writes events to
 | 
			
		||||
// the channel
 | 
			
		||||
func (mc *Client) runFeed(ch chan TapEvent, feed *TapFeed) {
 | 
			
		||||
	defer close(ch)
 | 
			
		||||
	var headerBuf [gomemcached.HDR_LEN]byte
 | 
			
		||||
loop:
 | 
			
		||||
	for {
 | 
			
		||||
		// Read the next request from the server.
 | 
			
		||||
		//
 | 
			
		||||
		//  (Can't call mc.Receive() because it reads a
 | 
			
		||||
		//  _response_ not a request.)
 | 
			
		||||
		var pkt gomemcached.MCRequest
 | 
			
		||||
		n, err := pkt.Receive(mc.conn, headerBuf[:])
 | 
			
		||||
		if TapRecvHook != nil {
 | 
			
		||||
			TapRecvHook(&pkt, n, err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if err != io.EOF {
 | 
			
		||||
				feed.Error = err
 | 
			
		||||
			}
 | 
			
		||||
			break loop
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		//logging.Infof("** TapFeed received %#v : %q", pkt, pkt.Body)
 | 
			
		||||
 | 
			
		||||
		if pkt.Opcode == gomemcached.TAP_CONNECT {
 | 
			
		||||
			// This is not an event from the server; it's
 | 
			
		||||
			// an error response to my connect request.
 | 
			
		||||
			feed.Error = fmt.Errorf("tap connection failed: %s", pkt.Body)
 | 
			
		||||
			break loop
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		event := makeTapEvent(pkt)
 | 
			
		||||
		if event != nil {
 | 
			
		||||
			if event.Opcode == tapEndStream {
 | 
			
		||||
				break loop
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			select {
 | 
			
		||||
			case ch <- *event:
 | 
			
		||||
			case <-feed.closer:
 | 
			
		||||
				break loop
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(pkt.Extras) >= 4 {
 | 
			
		||||
			reqFlags := binary.BigEndian.Uint16(pkt.Extras[2:])
 | 
			
		||||
			if reqFlags&gomemcached.TAP_ACK != 0 {
 | 
			
		||||
				if _, err := mc.sendAck(&pkt); err != nil {
 | 
			
		||||
					feed.Error = err
 | 
			
		||||
					break loop
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err := mc.Close(); err != nil {
 | 
			
		||||
		logging.Errorf("Error closing memcached client:  %v", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (mc *Client) sendAck(pkt *gomemcached.MCRequest) (int, error) {
 | 
			
		||||
	res := gomemcached.MCResponse{
 | 
			
		||||
		Opcode: pkt.Opcode,
 | 
			
		||||
		Opaque: pkt.Opaque,
 | 
			
		||||
		Status: gomemcached.SUCCESS,
 | 
			
		||||
	}
 | 
			
		||||
	return res.Transmit(mc.conn)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close terminates a TapFeed.
 | 
			
		||||
//
 | 
			
		||||
//  Call this if you stop using a TapFeed before its channel ends.
 | 
			
		||||
func (feed *TapFeed) Close() {
 | 
			
		||||
	close(feed.closer)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										67
									
								
								vendor/github.com/couchbase/gomemcached/client/transport.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								vendor/github.com/couchbase/gomemcached/client/transport.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
package memcached
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	"github.com/couchbase/gomemcached"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var errNoConn = errors.New("no connection")
 | 
			
		||||
 | 
			
		||||
// UnwrapMemcachedError converts memcached errors to normal responses.
 | 
			
		||||
//
 | 
			
		||||
// If the error is a memcached response, declare the error to be nil
 | 
			
		||||
// so a client can handle the status without worrying about whether it
 | 
			
		||||
// indicates success or failure.
 | 
			
		||||
func UnwrapMemcachedError(rv *gomemcached.MCResponse,
 | 
			
		||||
	err error) (*gomemcached.MCResponse, error) {
 | 
			
		||||
 | 
			
		||||
	if rv == err {
 | 
			
		||||
		return rv, nil
 | 
			
		||||
	}
 | 
			
		||||
	return rv, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReceiveHook is called after every packet is received (or attempted to be)
 | 
			
		||||
var ReceiveHook func(*gomemcached.MCResponse, int, error)
 | 
			
		||||
 | 
			
		||||
func getResponse(s io.Reader, hdrBytes []byte) (rv *gomemcached.MCResponse, n int, err error) {
 | 
			
		||||
	if s == nil {
 | 
			
		||||
		return nil, 0, errNoConn
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rv = &gomemcached.MCResponse{}
 | 
			
		||||
	n, err = rv.Receive(s, hdrBytes)
 | 
			
		||||
 | 
			
		||||
	if ReceiveHook != nil {
 | 
			
		||||
		ReceiveHook(rv, n, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err == nil && (rv.Status != gomemcached.SUCCESS && rv.Status != gomemcached.AUTH_CONTINUE) {
 | 
			
		||||
		err = rv
 | 
			
		||||
	}
 | 
			
		||||
	return rv, n, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TransmitHook is called after each packet is transmitted.
 | 
			
		||||
var TransmitHook func(*gomemcached.MCRequest, int, error)
 | 
			
		||||
 | 
			
		||||
func transmitRequest(o io.Writer, req *gomemcached.MCRequest) (int, error) {
 | 
			
		||||
	if o == nil {
 | 
			
		||||
		return 0, errNoConn
 | 
			
		||||
	}
 | 
			
		||||
	n, err := req.Transmit(o)
 | 
			
		||||
	if TransmitHook != nil {
 | 
			
		||||
		TransmitHook(req, n, err)
 | 
			
		||||
	}
 | 
			
		||||
	return n, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func transmitResponse(o io.Writer, res *gomemcached.MCResponse) (int, error) {
 | 
			
		||||
	if o == nil {
 | 
			
		||||
		return 0, errNoConn
 | 
			
		||||
	}
 | 
			
		||||
	n, err := res.Transmit(o)
 | 
			
		||||
	return n, err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1005
									
								
								vendor/github.com/couchbase/gomemcached/client/upr_feed.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1005
									
								
								vendor/github.com/couchbase/gomemcached/client/upr_feed.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										335
									
								
								vendor/github.com/couchbase/gomemcached/mc_constants.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										335
									
								
								vendor/github.com/couchbase/gomemcached/mc_constants.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,335 @@
 | 
			
		||||
// Package gomemcached is binary protocol packet formats and constants.
 | 
			
		||||
package gomemcached
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	REQ_MAGIC = 0x80
 | 
			
		||||
	RES_MAGIC = 0x81
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CommandCode for memcached packets.
 | 
			
		||||
type CommandCode uint8
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	GET        = CommandCode(0x00)
 | 
			
		||||
	SET        = CommandCode(0x01)
 | 
			
		||||
	ADD        = CommandCode(0x02)
 | 
			
		||||
	REPLACE    = CommandCode(0x03)
 | 
			
		||||
	DELETE     = CommandCode(0x04)
 | 
			
		||||
	INCREMENT  = CommandCode(0x05)
 | 
			
		||||
	DECREMENT  = CommandCode(0x06)
 | 
			
		||||
	QUIT       = CommandCode(0x07)
 | 
			
		||||
	FLUSH      = CommandCode(0x08)
 | 
			
		||||
	GETQ       = CommandCode(0x09)
 | 
			
		||||
	NOOP       = CommandCode(0x0a)
 | 
			
		||||
	VERSION    = CommandCode(0x0b)
 | 
			
		||||
	GETK       = CommandCode(0x0c)
 | 
			
		||||
	GETKQ      = CommandCode(0x0d)
 | 
			
		||||
	APPEND     = CommandCode(0x0e)
 | 
			
		||||
	PREPEND    = CommandCode(0x0f)
 | 
			
		||||
	STAT       = CommandCode(0x10)
 | 
			
		||||
	SETQ       = CommandCode(0x11)
 | 
			
		||||
	ADDQ       = CommandCode(0x12)
 | 
			
		||||
	REPLACEQ   = CommandCode(0x13)
 | 
			
		||||
	DELETEQ    = CommandCode(0x14)
 | 
			
		||||
	INCREMENTQ = CommandCode(0x15)
 | 
			
		||||
	DECREMENTQ = CommandCode(0x16)
 | 
			
		||||
	QUITQ      = CommandCode(0x17)
 | 
			
		||||
	FLUSHQ     = CommandCode(0x18)
 | 
			
		||||
	APPENDQ    = CommandCode(0x19)
 | 
			
		||||
	AUDIT      = CommandCode(0x27)
 | 
			
		||||
	PREPENDQ   = CommandCode(0x1a)
 | 
			
		||||
	GAT        = CommandCode(0x1d)
 | 
			
		||||
	HELLO      = CommandCode(0x1f)
 | 
			
		||||
	RGET       = CommandCode(0x30)
 | 
			
		||||
	RSET       = CommandCode(0x31)
 | 
			
		||||
	RSETQ      = CommandCode(0x32)
 | 
			
		||||
	RAPPEND    = CommandCode(0x33)
 | 
			
		||||
	RAPPENDQ   = CommandCode(0x34)
 | 
			
		||||
	RPREPEND   = CommandCode(0x35)
 | 
			
		||||
	RPREPENDQ  = CommandCode(0x36)
 | 
			
		||||
	RDELETE    = CommandCode(0x37)
 | 
			
		||||
	RDELETEQ   = CommandCode(0x38)
 | 
			
		||||
	RINCR      = CommandCode(0x39)
 | 
			
		||||
	RINCRQ     = CommandCode(0x3a)
 | 
			
		||||
	RDECR      = CommandCode(0x3b)
 | 
			
		||||
	RDECRQ     = CommandCode(0x3c)
 | 
			
		||||
 | 
			
		||||
	SASL_LIST_MECHS = CommandCode(0x20)
 | 
			
		||||
	SASL_AUTH       = CommandCode(0x21)
 | 
			
		||||
	SASL_STEP       = CommandCode(0x22)
 | 
			
		||||
 | 
			
		||||
	SET_VBUCKET = CommandCode(0x3d)
 | 
			
		||||
 | 
			
		||||
	TAP_CONNECT          = CommandCode(0x40) // Client-sent request to initiate Tap feed
 | 
			
		||||
	TAP_MUTATION         = CommandCode(0x41) // Notification of a SET/ADD/REPLACE/etc. on the server
 | 
			
		||||
	TAP_DELETE           = CommandCode(0x42) // Notification of a DELETE on the server
 | 
			
		||||
	TAP_FLUSH            = CommandCode(0x43) // Replicates a flush_all command
 | 
			
		||||
	TAP_OPAQUE           = CommandCode(0x44) // Opaque control data from the engine
 | 
			
		||||
	TAP_VBUCKET_SET      = CommandCode(0x45) // Sets state of vbucket in receiver (used in takeover)
 | 
			
		||||
	TAP_CHECKPOINT_START = CommandCode(0x46) // Notifies start of new checkpoint
 | 
			
		||||
	TAP_CHECKPOINT_END   = CommandCode(0x47) // Notifies end of checkpoint
 | 
			
		||||
 | 
			
		||||
	UPR_OPEN        = CommandCode(0x50) // Open a UPR connection with a name
 | 
			
		||||
	UPR_ADDSTREAM   = CommandCode(0x51) // Sent by ebucketMigrator to UPR Consumer
 | 
			
		||||
	UPR_CLOSESTREAM = CommandCode(0x52) // Sent by eBucketMigrator to UPR Consumer
 | 
			
		||||
	UPR_FAILOVERLOG = CommandCode(0x54) // Request failover logs
 | 
			
		||||
	UPR_STREAMREQ   = CommandCode(0x53) // Stream request from consumer to producer
 | 
			
		||||
	UPR_STREAMEND   = CommandCode(0x55) // Sent by producer when it has no more messages to stream
 | 
			
		||||
	UPR_SNAPSHOT    = CommandCode(0x56) // Start of a new snapshot
 | 
			
		||||
	UPR_MUTATION    = CommandCode(0x57) // Key mutation
 | 
			
		||||
	UPR_DELETION    = CommandCode(0x58) // Key deletion
 | 
			
		||||
	UPR_EXPIRATION  = CommandCode(0x59) // Key expiration
 | 
			
		||||
	UPR_FLUSH       = CommandCode(0x5a) // Delete all the data for a vbucket
 | 
			
		||||
	UPR_NOOP        = CommandCode(0x5c) // UPR NOOP
 | 
			
		||||
	UPR_BUFFERACK   = CommandCode(0x5d) // UPR Buffer Acknowledgement
 | 
			
		||||
	UPR_CONTROL     = CommandCode(0x5e) // Set flow control params
 | 
			
		||||
 | 
			
		||||
	SELECT_BUCKET = CommandCode(0x89) // Select bucket
 | 
			
		||||
 | 
			
		||||
	OBSERVE_SEQNO = CommandCode(0x91) // Sequence Number based Observe
 | 
			
		||||
	OBSERVE       = CommandCode(0x92)
 | 
			
		||||
 | 
			
		||||
	GET_META            = CommandCode(0xA0) // Get meta. returns with expiry, flags, cas etc
 | 
			
		||||
	SUBDOC_GET          = CommandCode(0xc5) // Get subdoc. Returns with xattrs
 | 
			
		||||
	SUBDOC_MULTI_LOOKUP = CommandCode(0xd0) // Multi lookup. Doc xattrs and meta.
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// command codes that are counted toward DCP control buffer
 | 
			
		||||
// when DCP clients receive DCP messages with these command codes, they need to provide acknowledgement
 | 
			
		||||
var BufferedCommandCodeMap = map[CommandCode]bool{
 | 
			
		||||
	SET_VBUCKET:    true,
 | 
			
		||||
	UPR_STREAMEND:  true,
 | 
			
		||||
	UPR_SNAPSHOT:   true,
 | 
			
		||||
	UPR_MUTATION:   true,
 | 
			
		||||
	UPR_DELETION:   true,
 | 
			
		||||
	UPR_EXPIRATION: true}
 | 
			
		||||
 | 
			
		||||
// Status field for memcached response.
 | 
			
		||||
type Status uint16
 | 
			
		||||
 | 
			
		||||
// Matches with protocol_binary.h as source of truth
 | 
			
		||||
const (
 | 
			
		||||
	SUCCESS         = Status(0x00)
 | 
			
		||||
	KEY_ENOENT      = Status(0x01)
 | 
			
		||||
	KEY_EEXISTS     = Status(0x02)
 | 
			
		||||
	E2BIG           = Status(0x03)
 | 
			
		||||
	EINVAL          = Status(0x04)
 | 
			
		||||
	NOT_STORED      = Status(0x05)
 | 
			
		||||
	DELTA_BADVAL    = Status(0x06)
 | 
			
		||||
	NOT_MY_VBUCKET  = Status(0x07)
 | 
			
		||||
	NO_BUCKET       = Status(0x08)
 | 
			
		||||
	LOCKED          = Status(0x09)
 | 
			
		||||
	AUTH_STALE      = Status(0x1f)
 | 
			
		||||
	AUTH_ERROR      = Status(0x20)
 | 
			
		||||
	AUTH_CONTINUE   = Status(0x21)
 | 
			
		||||
	ERANGE          = Status(0x22)
 | 
			
		||||
	ROLLBACK        = Status(0x23)
 | 
			
		||||
	EACCESS         = Status(0x24)
 | 
			
		||||
	NOT_INITIALIZED = Status(0x25)
 | 
			
		||||
	UNKNOWN_COMMAND = Status(0x81)
 | 
			
		||||
	ENOMEM          = Status(0x82)
 | 
			
		||||
	NOT_SUPPORTED   = Status(0x83)
 | 
			
		||||
	EINTERNAL       = Status(0x84)
 | 
			
		||||
	EBUSY           = Status(0x85)
 | 
			
		||||
	TMPFAIL         = Status(0x86)
 | 
			
		||||
 | 
			
		||||
	// SUBDOC
 | 
			
		||||
	SUBDOC_PATH_NOT_FOUND             = Status(0xc0)
 | 
			
		||||
	SUBDOC_BAD_MULTI                  = Status(0xcc)
 | 
			
		||||
	SUBDOC_MULTI_PATH_FAILURE_DELETED = Status(0xd3)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// for log redaction
 | 
			
		||||
const (
 | 
			
		||||
	UdTagBegin = "<ud>"
 | 
			
		||||
	UdTagEnd   = "</ud>"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var isFatal = map[Status]bool{
 | 
			
		||||
	DELTA_BADVAL:  true,
 | 
			
		||||
	NO_BUCKET:     true,
 | 
			
		||||
	AUTH_STALE:    true,
 | 
			
		||||
	AUTH_ERROR:    true,
 | 
			
		||||
	ERANGE:        true,
 | 
			
		||||
	ROLLBACK:      true,
 | 
			
		||||
	EACCESS:       true,
 | 
			
		||||
	ENOMEM:        true,
 | 
			
		||||
	NOT_SUPPORTED: true,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// the producer/consumer bit in dcp flags
 | 
			
		||||
var DCP_PRODUCER uint32 = 0x01
 | 
			
		||||
 | 
			
		||||
// the include XATTRS bit in dcp flags
 | 
			
		||||
var DCP_OPEN_INCLUDE_XATTRS uint32 = 0x04
 | 
			
		||||
 | 
			
		||||
// the include deletion time bit in dcp flags
 | 
			
		||||
var DCP_OPEN_INCLUDE_DELETE_TIMES uint32 = 0x20
 | 
			
		||||
 | 
			
		||||
// Datatype to Include XATTRS in SUBDOC GET
 | 
			
		||||
var SUBDOC_FLAG_XATTR uint8 = 0x04
 | 
			
		||||
 | 
			
		||||
// MCItem is an internal representation of an item.
 | 
			
		||||
type MCItem struct {
 | 
			
		||||
	Cas               uint64
 | 
			
		||||
	Flags, Expiration uint32
 | 
			
		||||
	Data              []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Number of bytes in a binary protocol header.
 | 
			
		||||
const HDR_LEN = 24
 | 
			
		||||
 | 
			
		||||
// Mapping of CommandCode -> name of command (not exhaustive)
 | 
			
		||||
var CommandNames map[CommandCode]string
 | 
			
		||||
 | 
			
		||||
// StatusNames human readable names for memcached response.
 | 
			
		||||
var StatusNames map[Status]string
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	CommandNames = make(map[CommandCode]string)
 | 
			
		||||
	CommandNames[GET] = "GET"
 | 
			
		||||
	CommandNames[SET] = "SET"
 | 
			
		||||
	CommandNames[ADD] = "ADD"
 | 
			
		||||
	CommandNames[REPLACE] = "REPLACE"
 | 
			
		||||
	CommandNames[DELETE] = "DELETE"
 | 
			
		||||
	CommandNames[INCREMENT] = "INCREMENT"
 | 
			
		||||
	CommandNames[DECREMENT] = "DECREMENT"
 | 
			
		||||
	CommandNames[QUIT] = "QUIT"
 | 
			
		||||
	CommandNames[FLUSH] = "FLUSH"
 | 
			
		||||
	CommandNames[GETQ] = "GETQ"
 | 
			
		||||
	CommandNames[NOOP] = "NOOP"
 | 
			
		||||
	CommandNames[VERSION] = "VERSION"
 | 
			
		||||
	CommandNames[GETK] = "GETK"
 | 
			
		||||
	CommandNames[GETKQ] = "GETKQ"
 | 
			
		||||
	CommandNames[APPEND] = "APPEND"
 | 
			
		||||
	CommandNames[PREPEND] = "PREPEND"
 | 
			
		||||
	CommandNames[STAT] = "STAT"
 | 
			
		||||
	CommandNames[SETQ] = "SETQ"
 | 
			
		||||
	CommandNames[ADDQ] = "ADDQ"
 | 
			
		||||
	CommandNames[REPLACEQ] = "REPLACEQ"
 | 
			
		||||
	CommandNames[DELETEQ] = "DELETEQ"
 | 
			
		||||
	CommandNames[INCREMENTQ] = "INCREMENTQ"
 | 
			
		||||
	CommandNames[DECREMENTQ] = "DECREMENTQ"
 | 
			
		||||
	CommandNames[QUITQ] = "QUITQ"
 | 
			
		||||
	CommandNames[FLUSHQ] = "FLUSHQ"
 | 
			
		||||
	CommandNames[APPENDQ] = "APPENDQ"
 | 
			
		||||
	CommandNames[PREPENDQ] = "PREPENDQ"
 | 
			
		||||
	CommandNames[RGET] = "RGET"
 | 
			
		||||
	CommandNames[RSET] = "RSET"
 | 
			
		||||
	CommandNames[RSETQ] = "RSETQ"
 | 
			
		||||
	CommandNames[RAPPEND] = "RAPPEND"
 | 
			
		||||
	CommandNames[RAPPENDQ] = "RAPPENDQ"
 | 
			
		||||
	CommandNames[RPREPEND] = "RPREPEND"
 | 
			
		||||
	CommandNames[RPREPENDQ] = "RPREPENDQ"
 | 
			
		||||
	CommandNames[RDELETE] = "RDELETE"
 | 
			
		||||
	CommandNames[RDELETEQ] = "RDELETEQ"
 | 
			
		||||
	CommandNames[RINCR] = "RINCR"
 | 
			
		||||
	CommandNames[RINCRQ] = "RINCRQ"
 | 
			
		||||
	CommandNames[RDECR] = "RDECR"
 | 
			
		||||
	CommandNames[RDECRQ] = "RDECRQ"
 | 
			
		||||
 | 
			
		||||
	CommandNames[SASL_LIST_MECHS] = "SASL_LIST_MECHS"
 | 
			
		||||
	CommandNames[SASL_AUTH] = "SASL_AUTH"
 | 
			
		||||
	CommandNames[SASL_STEP] = "SASL_STEP"
 | 
			
		||||
 | 
			
		||||
	CommandNames[TAP_CONNECT] = "TAP_CONNECT"
 | 
			
		||||
	CommandNames[TAP_MUTATION] = "TAP_MUTATION"
 | 
			
		||||
	CommandNames[TAP_DELETE] = "TAP_DELETE"
 | 
			
		||||
	CommandNames[TAP_FLUSH] = "TAP_FLUSH"
 | 
			
		||||
	CommandNames[TAP_OPAQUE] = "TAP_OPAQUE"
 | 
			
		||||
	CommandNames[TAP_VBUCKET_SET] = "TAP_VBUCKET_SET"
 | 
			
		||||
	CommandNames[TAP_CHECKPOINT_START] = "TAP_CHECKPOINT_START"
 | 
			
		||||
	CommandNames[TAP_CHECKPOINT_END] = "TAP_CHECKPOINT_END"
 | 
			
		||||
 | 
			
		||||
	CommandNames[UPR_OPEN] = "UPR_OPEN"
 | 
			
		||||
	CommandNames[UPR_ADDSTREAM] = "UPR_ADDSTREAM"
 | 
			
		||||
	CommandNames[UPR_CLOSESTREAM] = "UPR_CLOSESTREAM"
 | 
			
		||||
	CommandNames[UPR_FAILOVERLOG] = "UPR_FAILOVERLOG"
 | 
			
		||||
	CommandNames[UPR_STREAMREQ] = "UPR_STREAMREQ"
 | 
			
		||||
	CommandNames[UPR_STREAMEND] = "UPR_STREAMEND"
 | 
			
		||||
	CommandNames[UPR_SNAPSHOT] = "UPR_SNAPSHOT"
 | 
			
		||||
	CommandNames[UPR_MUTATION] = "UPR_MUTATION"
 | 
			
		||||
	CommandNames[UPR_DELETION] = "UPR_DELETION"
 | 
			
		||||
	CommandNames[UPR_EXPIRATION] = "UPR_EXPIRATION"
 | 
			
		||||
	CommandNames[UPR_FLUSH] = "UPR_FLUSH"
 | 
			
		||||
	CommandNames[UPR_NOOP] = "UPR_NOOP"
 | 
			
		||||
	CommandNames[UPR_BUFFERACK] = "UPR_BUFFERACK"
 | 
			
		||||
	CommandNames[UPR_CONTROL] = "UPR_CONTROL"
 | 
			
		||||
	CommandNames[SUBDOC_GET] = "SUBDOC_GET"
 | 
			
		||||
	CommandNames[SUBDOC_MULTI_LOOKUP] = "SUBDOC_MULTI_LOOKUP"
 | 
			
		||||
 | 
			
		||||
	StatusNames = make(map[Status]string)
 | 
			
		||||
	StatusNames[SUCCESS] = "SUCCESS"
 | 
			
		||||
	StatusNames[KEY_ENOENT] = "KEY_ENOENT"
 | 
			
		||||
	StatusNames[KEY_EEXISTS] = "KEY_EEXISTS"
 | 
			
		||||
	StatusNames[E2BIG] = "E2BIG"
 | 
			
		||||
	StatusNames[EINVAL] = "EINVAL"
 | 
			
		||||
	StatusNames[NOT_STORED] = "NOT_STORED"
 | 
			
		||||
	StatusNames[DELTA_BADVAL] = "DELTA_BADVAL"
 | 
			
		||||
	StatusNames[NOT_MY_VBUCKET] = "NOT_MY_VBUCKET"
 | 
			
		||||
	StatusNames[NO_BUCKET] = "NO_BUCKET"
 | 
			
		||||
	StatusNames[AUTH_STALE] = "AUTH_STALE"
 | 
			
		||||
	StatusNames[AUTH_ERROR] = "AUTH_ERROR"
 | 
			
		||||
	StatusNames[AUTH_CONTINUE] = "AUTH_CONTINUE"
 | 
			
		||||
	StatusNames[ERANGE] = "ERANGE"
 | 
			
		||||
	StatusNames[ROLLBACK] = "ROLLBACK"
 | 
			
		||||
	StatusNames[EACCESS] = "EACCESS"
 | 
			
		||||
	StatusNames[NOT_INITIALIZED] = "NOT_INITIALIZED"
 | 
			
		||||
	StatusNames[UNKNOWN_COMMAND] = "UNKNOWN_COMMAND"
 | 
			
		||||
	StatusNames[ENOMEM] = "ENOMEM"
 | 
			
		||||
	StatusNames[NOT_SUPPORTED] = "NOT_SUPPORTED"
 | 
			
		||||
	StatusNames[EINTERNAL] = "EINTERNAL"
 | 
			
		||||
	StatusNames[EBUSY] = "EBUSY"
 | 
			
		||||
	StatusNames[TMPFAIL] = "TMPFAIL"
 | 
			
		||||
	StatusNames[SUBDOC_PATH_NOT_FOUND] = "SUBDOC_PATH_NOT_FOUND"
 | 
			
		||||
	StatusNames[SUBDOC_BAD_MULTI] = "SUBDOC_BAD_MULTI"
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String an op code.
 | 
			
		||||
func (o CommandCode) String() (rv string) {
 | 
			
		||||
	rv = CommandNames[o]
 | 
			
		||||
	if rv == "" {
 | 
			
		||||
		rv = fmt.Sprintf("0x%02x", int(o))
 | 
			
		||||
	}
 | 
			
		||||
	return rv
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String an op code.
 | 
			
		||||
func (s Status) String() (rv string) {
 | 
			
		||||
	rv = StatusNames[s]
 | 
			
		||||
	if rv == "" {
 | 
			
		||||
		rv = fmt.Sprintf("0x%02x", int(s))
 | 
			
		||||
	}
 | 
			
		||||
	return rv
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsQuiet will return true if a command is a "quiet" command.
 | 
			
		||||
func (o CommandCode) IsQuiet() bool {
 | 
			
		||||
	switch o {
 | 
			
		||||
	case GETQ,
 | 
			
		||||
		GETKQ,
 | 
			
		||||
		SETQ,
 | 
			
		||||
		ADDQ,
 | 
			
		||||
		REPLACEQ,
 | 
			
		||||
		DELETEQ,
 | 
			
		||||
		INCREMENTQ,
 | 
			
		||||
		DECREMENTQ,
 | 
			
		||||
		QUITQ,
 | 
			
		||||
		FLUSHQ,
 | 
			
		||||
		APPENDQ,
 | 
			
		||||
		PREPENDQ,
 | 
			
		||||
		RSETQ,
 | 
			
		||||
		RAPPENDQ,
 | 
			
		||||
		RPREPENDQ,
 | 
			
		||||
		RDELETEQ,
 | 
			
		||||
		RINCRQ,
 | 
			
		||||
		RDECRQ:
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										197
									
								
								vendor/github.com/couchbase/gomemcached/mc_req.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								vendor/github.com/couchbase/gomemcached/mc_req.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,197 @@
 | 
			
		||||
package gomemcached
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// The maximum reasonable body length to expect.
 | 
			
		||||
// Anything larger than this will result in an error.
 | 
			
		||||
// The current limit, 20MB, is the size limit supported by ep-engine.
 | 
			
		||||
var MaxBodyLen = int(20 * 1024 * 1024)
 | 
			
		||||
 | 
			
		||||
// MCRequest is memcached Request
 | 
			
		||||
type MCRequest struct {
 | 
			
		||||
	// The command being issued
 | 
			
		||||
	Opcode CommandCode
 | 
			
		||||
	// The CAS (if applicable, or 0)
 | 
			
		||||
	Cas uint64
 | 
			
		||||
	// An opaque value to be returned with this request
 | 
			
		||||
	Opaque uint32
 | 
			
		||||
	// The vbucket to which this command belongs
 | 
			
		||||
	VBucket uint16
 | 
			
		||||
	// Command extras, key, and body
 | 
			
		||||
	Extras, Key, Body, ExtMeta []byte
 | 
			
		||||
	// Datatype identifier
 | 
			
		||||
	DataType uint8
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Size gives the number of bytes this request requires.
 | 
			
		||||
func (req *MCRequest) Size() int {
 | 
			
		||||
	return HDR_LEN + len(req.Extras) + len(req.Key) + len(req.Body) + len(req.ExtMeta)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A debugging string representation of this request
 | 
			
		||||
func (req MCRequest) String() string {
 | 
			
		||||
	return fmt.Sprintf("{MCRequest opcode=%s, bodylen=%d, key='%s'}",
 | 
			
		||||
		req.Opcode, len(req.Body), req.Key)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (req *MCRequest) fillHeaderBytes(data []byte) int {
 | 
			
		||||
 | 
			
		||||
	pos := 0
 | 
			
		||||
	data[pos] = REQ_MAGIC
 | 
			
		||||
	pos++
 | 
			
		||||
	data[pos] = byte(req.Opcode)
 | 
			
		||||
	pos++
 | 
			
		||||
	binary.BigEndian.PutUint16(data[pos:pos+2],
 | 
			
		||||
		uint16(len(req.Key)))
 | 
			
		||||
	pos += 2
 | 
			
		||||
 | 
			
		||||
	// 4
 | 
			
		||||
	data[pos] = byte(len(req.Extras))
 | 
			
		||||
	pos++
 | 
			
		||||
	// Data type
 | 
			
		||||
	if req.DataType != 0 {
 | 
			
		||||
		data[pos] = byte(req.DataType)
 | 
			
		||||
	}
 | 
			
		||||
	pos++
 | 
			
		||||
	binary.BigEndian.PutUint16(data[pos:pos+2], req.VBucket)
 | 
			
		||||
	pos += 2
 | 
			
		||||
 | 
			
		||||
	// 8
 | 
			
		||||
	binary.BigEndian.PutUint32(data[pos:pos+4],
 | 
			
		||||
		uint32(len(req.Body)+len(req.Key)+len(req.Extras)+len(req.ExtMeta)))
 | 
			
		||||
	pos += 4
 | 
			
		||||
 | 
			
		||||
	// 12
 | 
			
		||||
	binary.BigEndian.PutUint32(data[pos:pos+4], req.Opaque)
 | 
			
		||||
	pos += 4
 | 
			
		||||
 | 
			
		||||
	// 16
 | 
			
		||||
	if req.Cas != 0 {
 | 
			
		||||
		binary.BigEndian.PutUint64(data[pos:pos+8], req.Cas)
 | 
			
		||||
	}
 | 
			
		||||
	pos += 8
 | 
			
		||||
 | 
			
		||||
	if len(req.Extras) > 0 {
 | 
			
		||||
		copy(data[pos:pos+len(req.Extras)], req.Extras)
 | 
			
		||||
		pos += len(req.Extras)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(req.Key) > 0 {
 | 
			
		||||
		copy(data[pos:pos+len(req.Key)], req.Key)
 | 
			
		||||
		pos += len(req.Key)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return pos
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HeaderBytes will return the wire representation of the request header
 | 
			
		||||
// (with the extras and key).
 | 
			
		||||
func (req *MCRequest) HeaderBytes() []byte {
 | 
			
		||||
	data := make([]byte, HDR_LEN+len(req.Extras)+len(req.Key))
 | 
			
		||||
 | 
			
		||||
	req.fillHeaderBytes(data)
 | 
			
		||||
 | 
			
		||||
	return data
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Bytes will return the wire representation of this request.
 | 
			
		||||
func (req *MCRequest) Bytes() []byte {
 | 
			
		||||
	data := make([]byte, req.Size())
 | 
			
		||||
 | 
			
		||||
	pos := req.fillHeaderBytes(data)
 | 
			
		||||
 | 
			
		||||
	if len(req.Body) > 0 {
 | 
			
		||||
		copy(data[pos:pos+len(req.Body)], req.Body)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(req.ExtMeta) > 0 {
 | 
			
		||||
		copy(data[pos+len(req.Body):pos+len(req.Body)+len(req.ExtMeta)], req.ExtMeta)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return data
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Transmit will send this request message across a writer.
 | 
			
		||||
func (req *MCRequest) Transmit(w io.Writer) (n int, err error) {
 | 
			
		||||
	if len(req.Body) < 128 {
 | 
			
		||||
		n, err = w.Write(req.Bytes())
 | 
			
		||||
	} else {
 | 
			
		||||
		n, err = w.Write(req.HeaderBytes())
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			m := 0
 | 
			
		||||
			m, err = w.Write(req.Body)
 | 
			
		||||
			n += m
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Receive will fill this MCRequest with the data from a reader.
 | 
			
		||||
func (req *MCRequest) Receive(r io.Reader, hdrBytes []byte) (int, error) {
 | 
			
		||||
	if len(hdrBytes) < HDR_LEN {
 | 
			
		||||
		hdrBytes = []byte{
 | 
			
		||||
			0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
			0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
			0, 0, 0, 0, 0, 0, 0, 0}
 | 
			
		||||
	}
 | 
			
		||||
	n, err := io.ReadFull(r, hdrBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return n, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if hdrBytes[0] != RES_MAGIC && hdrBytes[0] != REQ_MAGIC {
 | 
			
		||||
		return n, fmt.Errorf("bad magic: 0x%02x", hdrBytes[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	klen := int(binary.BigEndian.Uint16(hdrBytes[2:]))
 | 
			
		||||
	elen := int(hdrBytes[4])
 | 
			
		||||
	// Data type at 5
 | 
			
		||||
	req.DataType = uint8(hdrBytes[5])
 | 
			
		||||
 | 
			
		||||
	req.Opcode = CommandCode(hdrBytes[1])
 | 
			
		||||
	// Vbucket at 6:7
 | 
			
		||||
	req.VBucket = binary.BigEndian.Uint16(hdrBytes[6:])
 | 
			
		||||
	totalBodyLen := int(binary.BigEndian.Uint32(hdrBytes[8:]))
 | 
			
		||||
 | 
			
		||||
	req.Opaque = binary.BigEndian.Uint32(hdrBytes[12:])
 | 
			
		||||
	req.Cas = binary.BigEndian.Uint64(hdrBytes[16:])
 | 
			
		||||
 | 
			
		||||
	if totalBodyLen > 0 {
 | 
			
		||||
		buf := make([]byte, totalBodyLen)
 | 
			
		||||
		m, err := io.ReadFull(r, buf)
 | 
			
		||||
		n += m
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			if req.Opcode >= TAP_MUTATION &&
 | 
			
		||||
				req.Opcode <= TAP_CHECKPOINT_END &&
 | 
			
		||||
				len(buf) > 1 {
 | 
			
		||||
				// In these commands there is "engine private"
 | 
			
		||||
				// data at the end of the extras.  The first 2
 | 
			
		||||
				// bytes of extra data give its length.
 | 
			
		||||
				elen += int(binary.BigEndian.Uint16(buf))
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			req.Extras = buf[0:elen]
 | 
			
		||||
			req.Key = buf[elen : klen+elen]
 | 
			
		||||
 | 
			
		||||
			// get the length of extended metadata
 | 
			
		||||
			extMetaLen := 0
 | 
			
		||||
			if elen > 29 {
 | 
			
		||||
				extMetaLen = int(binary.BigEndian.Uint16(req.Extras[28:30]))
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			bodyLen := totalBodyLen - klen - elen - extMetaLen
 | 
			
		||||
			if bodyLen > MaxBodyLen {
 | 
			
		||||
				return n, fmt.Errorf("%d is too big (max %d)",
 | 
			
		||||
					bodyLen, MaxBodyLen)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			req.Body = buf[klen+elen : klen+elen+bodyLen]
 | 
			
		||||
			req.ExtMeta = buf[klen+elen+bodyLen:]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return n, err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										267
									
								
								vendor/github.com/couchbase/gomemcached/mc_res.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								vendor/github.com/couchbase/gomemcached/mc_res.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,267 @@
 | 
			
		||||
package gomemcached
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// MCResponse is memcached response
 | 
			
		||||
type MCResponse struct {
 | 
			
		||||
	// The command opcode of the command that sent the request
 | 
			
		||||
	Opcode CommandCode
 | 
			
		||||
	// The status of the response
 | 
			
		||||
	Status Status
 | 
			
		||||
	// The opaque sent in the request
 | 
			
		||||
	Opaque uint32
 | 
			
		||||
	// The CAS identifier (if applicable)
 | 
			
		||||
	Cas uint64
 | 
			
		||||
	// Extras, key, and body for this response
 | 
			
		||||
	Extras, Key, Body []byte
 | 
			
		||||
	// If true, this represents a fatal condition and we should hang up
 | 
			
		||||
	Fatal bool
 | 
			
		||||
	// Datatype identifier
 | 
			
		||||
	DataType uint8
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A debugging string representation of this response
 | 
			
		||||
func (res MCResponse) String() string {
 | 
			
		||||
	return fmt.Sprintf("{MCResponse status=%v keylen=%d, extralen=%d, bodylen=%d}",
 | 
			
		||||
		res.Status, len(res.Key), len(res.Extras), len(res.Body))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Response as an error.
 | 
			
		||||
func (res *MCResponse) Error() string {
 | 
			
		||||
	return fmt.Sprintf("MCResponse status=%v, opcode=%v, opaque=%v, msg: %s",
 | 
			
		||||
		res.Status, res.Opcode, res.Opaque, string(res.Body))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func errStatus(e error) Status {
 | 
			
		||||
	status := Status(0xffff)
 | 
			
		||||
	if res, ok := e.(*MCResponse); ok {
 | 
			
		||||
		status = res.Status
 | 
			
		||||
	}
 | 
			
		||||
	return status
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsNotFound is true if this error represents a "not found" response.
 | 
			
		||||
func IsNotFound(e error) bool {
 | 
			
		||||
	return errStatus(e) == KEY_ENOENT
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsFatal is false if this error isn't believed to be fatal to a connection.
 | 
			
		||||
func IsFatal(e error) bool {
 | 
			
		||||
	if e == nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	_, ok := isFatal[errStatus(e)]
 | 
			
		||||
	if ok {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Size is number of bytes this response consumes on the wire.
 | 
			
		||||
func (res *MCResponse) Size() int {
 | 
			
		||||
	return HDR_LEN + len(res.Extras) + len(res.Key) + len(res.Body)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (res *MCResponse) fillHeaderBytes(data []byte) int {
 | 
			
		||||
	pos := 0
 | 
			
		||||
	data[pos] = RES_MAGIC
 | 
			
		||||
	pos++
 | 
			
		||||
	data[pos] = byte(res.Opcode)
 | 
			
		||||
	pos++
 | 
			
		||||
	binary.BigEndian.PutUint16(data[pos:pos+2],
 | 
			
		||||
		uint16(len(res.Key)))
 | 
			
		||||
	pos += 2
 | 
			
		||||
 | 
			
		||||
	// 4
 | 
			
		||||
	data[pos] = byte(len(res.Extras))
 | 
			
		||||
	pos++
 | 
			
		||||
	// Data type
 | 
			
		||||
	if res.DataType != 0 {
 | 
			
		||||
		data[pos] = byte(res.DataType)
 | 
			
		||||
	} else {
 | 
			
		||||
		data[pos] = 0
 | 
			
		||||
	}
 | 
			
		||||
	pos++
 | 
			
		||||
	binary.BigEndian.PutUint16(data[pos:pos+2], uint16(res.Status))
 | 
			
		||||
	pos += 2
 | 
			
		||||
 | 
			
		||||
	// 8
 | 
			
		||||
	binary.BigEndian.PutUint32(data[pos:pos+4],
 | 
			
		||||
		uint32(len(res.Body)+len(res.Key)+len(res.Extras)))
 | 
			
		||||
	pos += 4
 | 
			
		||||
 | 
			
		||||
	// 12
 | 
			
		||||
	binary.BigEndian.PutUint32(data[pos:pos+4], res.Opaque)
 | 
			
		||||
	pos += 4
 | 
			
		||||
 | 
			
		||||
	// 16
 | 
			
		||||
	binary.BigEndian.PutUint64(data[pos:pos+8], res.Cas)
 | 
			
		||||
	pos += 8
 | 
			
		||||
 | 
			
		||||
	if len(res.Extras) > 0 {
 | 
			
		||||
		copy(data[pos:pos+len(res.Extras)], res.Extras)
 | 
			
		||||
		pos += len(res.Extras)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(res.Key) > 0 {
 | 
			
		||||
		copy(data[pos:pos+len(res.Key)], res.Key)
 | 
			
		||||
		pos += len(res.Key)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return pos
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HeaderBytes will get just the header bytes for this response.
 | 
			
		||||
func (res *MCResponse) HeaderBytes() []byte {
 | 
			
		||||
	data := make([]byte, HDR_LEN+len(res.Extras)+len(res.Key))
 | 
			
		||||
 | 
			
		||||
	res.fillHeaderBytes(data)
 | 
			
		||||
 | 
			
		||||
	return data
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Bytes will return the actual bytes transmitted for this response.
 | 
			
		||||
func (res *MCResponse) Bytes() []byte {
 | 
			
		||||
	data := make([]byte, res.Size())
 | 
			
		||||
 | 
			
		||||
	pos := res.fillHeaderBytes(data)
 | 
			
		||||
 | 
			
		||||
	copy(data[pos:pos+len(res.Body)], res.Body)
 | 
			
		||||
 | 
			
		||||
	return data
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Transmit will send this response message across a writer.
 | 
			
		||||
func (res *MCResponse) Transmit(w io.Writer) (n int, err error) {
 | 
			
		||||
	if len(res.Body) < 128 {
 | 
			
		||||
		n, err = w.Write(res.Bytes())
 | 
			
		||||
	} else {
 | 
			
		||||
		n, err = w.Write(res.HeaderBytes())
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			m := 0
 | 
			
		||||
			m, err = w.Write(res.Body)
 | 
			
		||||
			m += n
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Receive will fill this MCResponse with the data from this reader.
 | 
			
		||||
func (res *MCResponse) Receive(r io.Reader, hdrBytes []byte) (n int, err error) {
 | 
			
		||||
	if len(hdrBytes) < HDR_LEN {
 | 
			
		||||
		hdrBytes = []byte{
 | 
			
		||||
			0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
			0, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
			0, 0, 0, 0, 0, 0, 0, 0}
 | 
			
		||||
	}
 | 
			
		||||
	n, err = io.ReadFull(r, hdrBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return n, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if hdrBytes[0] != RES_MAGIC && hdrBytes[0] != REQ_MAGIC {
 | 
			
		||||
		return n, fmt.Errorf("bad magic: 0x%02x", hdrBytes[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	klen := int(binary.BigEndian.Uint16(hdrBytes[2:4]))
 | 
			
		||||
	elen := int(hdrBytes[4])
 | 
			
		||||
 | 
			
		||||
	res.Opcode = CommandCode(hdrBytes[1])
 | 
			
		||||
	res.DataType = uint8(hdrBytes[5])
 | 
			
		||||
	res.Status = Status(binary.BigEndian.Uint16(hdrBytes[6:8]))
 | 
			
		||||
	res.Opaque = binary.BigEndian.Uint32(hdrBytes[12:16])
 | 
			
		||||
	res.Cas = binary.BigEndian.Uint64(hdrBytes[16:24])
 | 
			
		||||
 | 
			
		||||
	bodyLen := int(binary.BigEndian.Uint32(hdrBytes[8:12])) - (klen + elen)
 | 
			
		||||
 | 
			
		||||
	//defer function to debug the panic seen with MB-15557
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if e := recover(); e != nil {
 | 
			
		||||
			err = fmt.Errorf(`Panic in Receive. Response %v \n
 | 
			
		||||
                        key len %v extra len %v bodylen %v`, res, klen, elen, bodyLen)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	buf := make([]byte, klen+elen+bodyLen)
 | 
			
		||||
	m, err := io.ReadFull(r, buf)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		res.Extras = buf[0:elen]
 | 
			
		||||
		res.Key = buf[elen : klen+elen]
 | 
			
		||||
		res.Body = buf[klen+elen:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return n + m, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type MCResponsePool struct {
 | 
			
		||||
	pool *sync.Pool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewMCResponsePool() *MCResponsePool {
 | 
			
		||||
	rv := &MCResponsePool{
 | 
			
		||||
		pool: &sync.Pool{
 | 
			
		||||
			New: func() interface{} {
 | 
			
		||||
				return &MCResponse{}
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rv
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MCResponsePool) Get() *MCResponse {
 | 
			
		||||
	return this.pool.Get().(*MCResponse)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *MCResponsePool) Put(r *MCResponse) {
 | 
			
		||||
	if r == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r.Extras = nil
 | 
			
		||||
	r.Key = nil
 | 
			
		||||
	r.Body = nil
 | 
			
		||||
	r.Fatal = false
 | 
			
		||||
 | 
			
		||||
	this.pool.Put(r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type StringMCResponsePool struct {
 | 
			
		||||
	pool *sync.Pool
 | 
			
		||||
	size int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewStringMCResponsePool(size int) *StringMCResponsePool {
 | 
			
		||||
	rv := &StringMCResponsePool{
 | 
			
		||||
		pool: &sync.Pool{
 | 
			
		||||
			New: func() interface{} {
 | 
			
		||||
				return make(map[string]*MCResponse, size)
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		size: size,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rv
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *StringMCResponsePool) Get() map[string]*MCResponse {
 | 
			
		||||
	return this.pool.Get().(map[string]*MCResponse)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *StringMCResponsePool) Put(m map[string]*MCResponse) {
 | 
			
		||||
	if m == nil || len(m) > 2*this.size {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for k := range m {
 | 
			
		||||
		m[k] = nil
 | 
			
		||||
		delete(m, k)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.pool.Put(m)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										168
									
								
								vendor/github.com/couchbase/gomemcached/tap.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								vendor/github.com/couchbase/gomemcached/tap.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,168 @@
 | 
			
		||||
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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								vendor/github.com/couchbase/goutils/LICENSE.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								vendor/github.com/couchbase/goutils/LICENSE.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
COUCHBASE INC. COMMUNITY EDITION LICENSE AGREEMENT
 | 
			
		||||
 | 
			
		||||
IMPORTANT-READ CAREFULLY: BY CLICKING THE "I ACCEPT" BOX OR INSTALLING,
 | 
			
		||||
DOWNLOADING OR OTHERWISE USING THIS SOFTWARE AND ANY ASSOCIATED
 | 
			
		||||
DOCUMENTATION, YOU, ON BEHALF OF YOURSELF OR AS AN AUTHORIZED
 | 
			
		||||
REPRESENTATIVE ON BEHALF OF AN ENTITY ("LICENSEE") AGREE TO ALL THE
 | 
			
		||||
TERMS OF THIS COMMUNITY EDITION LICENSE AGREEMENT (THE "AGREEMENT")
 | 
			
		||||
REGARDING YOUR USE OF THE SOFTWARE.  YOU REPRESENT AND WARRANT THAT YOU
 | 
			
		||||
HAVE FULL LEGAL AUTHORITY TO BIND THE LICENSEE TO THIS AGREEMENT. IF YOU
 | 
			
		||||
DO NOT AGREE WITH ALL OF THESE TERMS, DO NOT SELECT THE "I ACCEPT" BOX
 | 
			
		||||
AND DO NOT INSTALL, DOWNLOAD OR OTHERWISE USE THE SOFTWARE. THE
 | 
			
		||||
EFFECTIVE DATE OF THIS AGREEMENT IS THE DATE ON WHICH YOU CLICK "I
 | 
			
		||||
ACCEPT" OR OTHERWISE INSTALL, DOWNLOAD OR USE THE SOFTWARE.
 | 
			
		||||
 | 
			
		||||
1. License Grant. Couchbase Inc. hereby grants Licensee, free of charge,
 | 
			
		||||
the non-exclusive right to use, copy, merge, publish, distribute,
 | 
			
		||||
sublicense, and/or sell copies of the Software, and to permit persons to
 | 
			
		||||
whom the Software is furnished to do so, subject to Licensee including
 | 
			
		||||
the following copyright notice in all copies or substantial portions of
 | 
			
		||||
the Software:
 | 
			
		||||
 | 
			
		||||
Couchbase (r) http://www.Couchbase.com Copyright 2016 Couchbase, Inc.
 | 
			
		||||
 | 
			
		||||
As used in this Agreement, "Software" means the object code version of
 | 
			
		||||
the applicable elastic data management server software provided by
 | 
			
		||||
Couchbase Inc.
 | 
			
		||||
 | 
			
		||||
2. Restrictions. Licensee will not reverse engineer, disassemble, or
 | 
			
		||||
decompile the Software (except to the extent such restrictions are
 | 
			
		||||
prohibited by law).
 | 
			
		||||
 | 
			
		||||
3. Support. Couchbase, Inc. will provide Licensee with access to, and
 | 
			
		||||
use of, the Couchbase, Inc. support forum available at the following
 | 
			
		||||
URL: http://www.couchbase.org/forums/. Couchbase, Inc. may, at its
 | 
			
		||||
discretion, modify, suspend or terminate support at any time upon notice
 | 
			
		||||
to Licensee.
 | 
			
		||||
 | 
			
		||||
4. Warranty Disclaimer and Limitation of Liability. THE SOFTWARE IS
 | 
			
		||||
PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 | 
			
		||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 | 
			
		||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | 
			
		||||
COUCHBASE INC. OR THE AUTHORS OR COPYRIGHT HOLDERS IN THE SOFTWARE BE
 | 
			
		||||
LIABLE FOR ANY CLAIM, DAMAGES (IINCLUDING, WITHOUT LIMITATION, DIRECT,
 | 
			
		||||
INDIRECT OR CONSEQUENTIAL DAMAGES) OR OTHER LIABILITY, WHETHER IN AN
 | 
			
		||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 | 
			
		||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
SOFTWARE.
 | 
			
		||||
							
								
								
									
										481
									
								
								vendor/github.com/couchbase/goutils/logging/logger.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										481
									
								
								vendor/github.com/couchbase/goutils/logging/logger.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,481 @@
 | 
			
		||||
//  Copyright (c) 2016 Couchbase, Inc.
 | 
			
		||||
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
 | 
			
		||||
//  except in compliance with the License. You may obtain a copy of the License at
 | 
			
		||||
//    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//  Unless required by applicable law or agreed to in writing, software distributed under the
 | 
			
		||||
//  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 | 
			
		||||
//  either express or implied. See the License for the specific language governing permissions
 | 
			
		||||
//  and limitations under the License.
 | 
			
		||||
 | 
			
		||||
package logging
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Level int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	NONE    = Level(iota) // Disable all logging
 | 
			
		||||
	FATAL                 // System is in severe error state and has to abort
 | 
			
		||||
	SEVERE                // System is in severe error state and cannot recover reliably
 | 
			
		||||
	ERROR                 // System is in error state but can recover and continue reliably
 | 
			
		||||
	WARN                  // System approaching error state, or is in a correct but undesirable state
 | 
			
		||||
	INFO                  // System-level events and status, in correct states
 | 
			
		||||
	REQUEST               // Request-level events, with request-specific rlevel
 | 
			
		||||
	TRACE                 // Trace detailed system execution, e.g. function entry / exit
 | 
			
		||||
	DEBUG                 // Debug
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type LogEntryFormatter int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	TEXTFORMATTER = LogEntryFormatter(iota)
 | 
			
		||||
	JSONFORMATTER
 | 
			
		||||
	KVFORMATTER
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (level Level) String() string {
 | 
			
		||||
	return _LEVEL_NAMES[level]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _LEVEL_NAMES = []string{
 | 
			
		||||
	DEBUG:   "DEBUG",
 | 
			
		||||
	TRACE:   "TRACE",
 | 
			
		||||
	REQUEST: "REQUEST",
 | 
			
		||||
	INFO:    "INFO",
 | 
			
		||||
	WARN:    "WARN",
 | 
			
		||||
	ERROR:   "ERROR",
 | 
			
		||||
	SEVERE:  "SEVERE",
 | 
			
		||||
	FATAL:   "FATAL",
 | 
			
		||||
	NONE:    "NONE",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _LEVEL_MAP = map[string]Level{
 | 
			
		||||
	"debug":   DEBUG,
 | 
			
		||||
	"trace":   TRACE,
 | 
			
		||||
	"request": REQUEST,
 | 
			
		||||
	"info":    INFO,
 | 
			
		||||
	"warn":    WARN,
 | 
			
		||||
	"error":   ERROR,
 | 
			
		||||
	"severe":  SEVERE,
 | 
			
		||||
	"fatal":   FATAL,
 | 
			
		||||
	"none":    NONE,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ParseLevel(name string) (level Level, ok bool) {
 | 
			
		||||
	level, ok = _LEVEL_MAP[strings.ToLower(name)]
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
Pair supports logging of key-value pairs.  Keys beginning with _ are
 | 
			
		||||
reserved for the logger, e.g. _time, _level, _msg, and _rlevel. The
 | 
			
		||||
Pair APIs are designed to avoid heap allocation and garbage
 | 
			
		||||
collection.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
type Pairs []Pair
 | 
			
		||||
type Pair struct {
 | 
			
		||||
	Name  string
 | 
			
		||||
	Value interface{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
Map allows key-value pairs to be specified using map literals or data
 | 
			
		||||
structures. For example:
 | 
			
		||||
 | 
			
		||||
Errorm(msg, Map{...})
 | 
			
		||||
 | 
			
		||||
Map incurs heap allocation and garbage collection, so the Pair APIs
 | 
			
		||||
should be preferred.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
type Map map[string]interface{}
 | 
			
		||||
 | 
			
		||||
// Logger provides a common interface for logging libraries
 | 
			
		||||
type Logger interface {
 | 
			
		||||
	/*
 | 
			
		||||
		These APIs write all the given pairs in addition to standard logger keys.
 | 
			
		||||
	*/
 | 
			
		||||
	Logp(level Level, msg string, kv ...Pair)
 | 
			
		||||
 | 
			
		||||
	Debugp(msg string, kv ...Pair)
 | 
			
		||||
 | 
			
		||||
	Tracep(msg string, kv ...Pair)
 | 
			
		||||
 | 
			
		||||
	Requestp(rlevel Level, msg string, kv ...Pair)
 | 
			
		||||
 | 
			
		||||
	Infop(msg string, kv ...Pair)
 | 
			
		||||
 | 
			
		||||
	Warnp(msg string, kv ...Pair)
 | 
			
		||||
 | 
			
		||||
	Errorp(msg string, kv ...Pair)
 | 
			
		||||
 | 
			
		||||
	Severep(msg string, kv ...Pair)
 | 
			
		||||
 | 
			
		||||
	Fatalp(msg string, kv ...Pair)
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
		These APIs write the fields in the given kv Map in addition to standard logger keys.
 | 
			
		||||
	*/
 | 
			
		||||
	Logm(level Level, msg string, kv Map)
 | 
			
		||||
 | 
			
		||||
	Debugm(msg string, kv Map)
 | 
			
		||||
 | 
			
		||||
	Tracem(msg string, kv Map)
 | 
			
		||||
 | 
			
		||||
	Requestm(rlevel Level, msg string, kv Map)
 | 
			
		||||
 | 
			
		||||
	Infom(msg string, kv Map)
 | 
			
		||||
 | 
			
		||||
	Warnm(msg string, kv Map)
 | 
			
		||||
 | 
			
		||||
	Errorm(msg string, kv Map)
 | 
			
		||||
 | 
			
		||||
	Severem(msg string, kv Map)
 | 
			
		||||
 | 
			
		||||
	Fatalm(msg string, kv Map)
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
 | 
			
		||||
		These APIs only write _msg, _time, _level, and other logger keys. If
 | 
			
		||||
		the msg contains other fields, use the Pair or Map APIs instead.
 | 
			
		||||
 | 
			
		||||
	*/
 | 
			
		||||
	Logf(level Level, fmt string, args ...interface{})
 | 
			
		||||
 | 
			
		||||
	Debugf(fmt string, args ...interface{})
 | 
			
		||||
 | 
			
		||||
	Tracef(fmt string, args ...interface{})
 | 
			
		||||
 | 
			
		||||
	Requestf(rlevel Level, fmt string, args ...interface{})
 | 
			
		||||
 | 
			
		||||
	Infof(fmt string, args ...interface{})
 | 
			
		||||
 | 
			
		||||
	Warnf(fmt string, args ...interface{})
 | 
			
		||||
 | 
			
		||||
	Errorf(fmt string, args ...interface{})
 | 
			
		||||
 | 
			
		||||
	Severef(fmt string, args ...interface{})
 | 
			
		||||
 | 
			
		||||
	Fatalf(fmt string, args ...interface{})
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
		These APIs control the logging level
 | 
			
		||||
	*/
 | 
			
		||||
 | 
			
		||||
	SetLevel(Level) // Set the logging level
 | 
			
		||||
 | 
			
		||||
	Level() Level // Get the current logging level
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var logger Logger = nil
 | 
			
		||||
var curLevel Level = DEBUG // initially set to never skip
 | 
			
		||||
 | 
			
		||||
var loggerMutex sync.RWMutex
 | 
			
		||||
 | 
			
		||||
// All the methods below first acquire the mutex (mostly in exclusive mode)
 | 
			
		||||
// and only then check if logging at the current level is enabled.
 | 
			
		||||
// This introduces a fair bottleneck for those log entries that should be
 | 
			
		||||
// skipped (the majority, at INFO or below levels)
 | 
			
		||||
// We try to predict here if we should lock the mutex at all by caching
 | 
			
		||||
// the current log level: while dynamically changing logger, there might
 | 
			
		||||
// be the odd entry skipped as the new level is cached.
 | 
			
		||||
// Since we seem to never change the logger, this is not an issue.
 | 
			
		||||
func skipLogging(level Level) bool {
 | 
			
		||||
	if logger == nil {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return level > curLevel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SetLogger(newLogger Logger) {
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger = newLogger
 | 
			
		||||
	if logger == nil {
 | 
			
		||||
		curLevel = NONE
 | 
			
		||||
	} else {
 | 
			
		||||
		curLevel = newLogger.Level()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Logp(level Level, msg string, kv ...Pair) {
 | 
			
		||||
	if skipLogging(level) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Logp(level, msg, kv...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Debugp(msg string, kv ...Pair) {
 | 
			
		||||
	if skipLogging(DEBUG) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Debugp(msg, kv...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Tracep(msg string, kv ...Pair) {
 | 
			
		||||
	if skipLogging(TRACE) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Tracep(msg, kv...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Requestp(rlevel Level, msg string, kv ...Pair) {
 | 
			
		||||
	if skipLogging(REQUEST) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Requestp(rlevel, msg, kv...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Infop(msg string, kv ...Pair) {
 | 
			
		||||
	if skipLogging(INFO) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Infop(msg, kv...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Warnp(msg string, kv ...Pair) {
 | 
			
		||||
	if skipLogging(WARN) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Warnp(msg, kv...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Errorp(msg string, kv ...Pair) {
 | 
			
		||||
	if skipLogging(ERROR) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Errorp(msg, kv...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Severep(msg string, kv ...Pair) {
 | 
			
		||||
	if skipLogging(SEVERE) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Severep(msg, kv...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Fatalp(msg string, kv ...Pair) {
 | 
			
		||||
	if skipLogging(FATAL) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Fatalp(msg, kv...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Logm(level Level, msg string, kv Map) {
 | 
			
		||||
	if skipLogging(level) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Logm(level, msg, kv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Debugm(msg string, kv Map) {
 | 
			
		||||
	if skipLogging(DEBUG) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Debugm(msg, kv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Tracem(msg string, kv Map) {
 | 
			
		||||
	if skipLogging(TRACE) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Tracem(msg, kv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Requestm(rlevel Level, msg string, kv Map) {
 | 
			
		||||
	if skipLogging(REQUEST) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Requestm(rlevel, msg, kv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Infom(msg string, kv Map) {
 | 
			
		||||
	if skipLogging(INFO) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Infom(msg, kv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Warnm(msg string, kv Map) {
 | 
			
		||||
	if skipLogging(WARN) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Warnm(msg, kv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Errorm(msg string, kv Map) {
 | 
			
		||||
	if skipLogging(ERROR) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Errorm(msg, kv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Severem(msg string, kv Map) {
 | 
			
		||||
	if skipLogging(SEVERE) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Severem(msg, kv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Fatalm(msg string, kv Map) {
 | 
			
		||||
	if skipLogging(FATAL) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Fatalm(msg, kv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Logf(level Level, fmt string, args ...interface{}) {
 | 
			
		||||
	if skipLogging(level) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Logf(level, fmt, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Debugf(fmt string, args ...interface{}) {
 | 
			
		||||
	if skipLogging(DEBUG) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Debugf(fmt, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Tracef(fmt string, args ...interface{}) {
 | 
			
		||||
	if skipLogging(TRACE) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Tracef(fmt, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Requestf(rlevel Level, fmt string, args ...interface{}) {
 | 
			
		||||
	if skipLogging(REQUEST) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Requestf(rlevel, fmt, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Infof(fmt string, args ...interface{}) {
 | 
			
		||||
	if skipLogging(INFO) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Infof(fmt, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Warnf(fmt string, args ...interface{}) {
 | 
			
		||||
	if skipLogging(WARN) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Warnf(fmt, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Errorf(fmt string, args ...interface{}) {
 | 
			
		||||
	if skipLogging(ERROR) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Errorf(fmt, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Severef(fmt string, args ...interface{}) {
 | 
			
		||||
	if skipLogging(SEVERE) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Severef(fmt, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Fatalf(fmt string, args ...interface{}) {
 | 
			
		||||
	if skipLogging(FATAL) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Fatalf(fmt, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SetLevel(level Level) {
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.SetLevel(level)
 | 
			
		||||
	curLevel = level
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func LogLevel() Level {
 | 
			
		||||
	loggerMutex.RLock()
 | 
			
		||||
	defer loggerMutex.RUnlock()
 | 
			
		||||
	return logger.Level()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Stackf(level Level, fmt string, args ...interface{}) {
 | 
			
		||||
	if skipLogging(level) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	buf := make([]byte, 1<<16)
 | 
			
		||||
	n := runtime.Stack(buf, false)
 | 
			
		||||
	s := string(buf[0:n])
 | 
			
		||||
	loggerMutex.Lock()
 | 
			
		||||
	defer loggerMutex.Unlock()
 | 
			
		||||
	logger.Logf(level, fmt, args...)
 | 
			
		||||
	logger.Logf(level, s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	logger = NewLogger(os.Stderr, INFO, TEXTFORMATTER)
 | 
			
		||||
	SetLogger(logger)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										318
									
								
								vendor/github.com/couchbase/goutils/logging/logger_golog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								vendor/github.com/couchbase/goutils/logging/logger_golog.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,318 @@
 | 
			
		||||
//  Copyright (c) 2016 Couchbase, Inc.
 | 
			
		||||
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
 | 
			
		||||
//  except in compliance with the License. You may obtain a copy of the License at
 | 
			
		||||
//    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//  Unless required by applicable law or agreed to in writing, software distributed under the
 | 
			
		||||
//  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 | 
			
		||||
//  either express or implied. See the License for the specific language governing permissions
 | 
			
		||||
//  and limitations under the License.
 | 
			
		||||
 | 
			
		||||
package logging
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type goLogger struct {
 | 
			
		||||
	logger         *log.Logger
 | 
			
		||||
	level          Level
 | 
			
		||||
	entryFormatter formatter
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_LEVEL  = "_level"
 | 
			
		||||
	_MSG    = "_msg"
 | 
			
		||||
	_TIME   = "_time"
 | 
			
		||||
	_RLEVEL = "_rlevel"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func NewLogger(out io.Writer, lvl Level, fmtLogging LogEntryFormatter) *goLogger {
 | 
			
		||||
	logger := &goLogger{
 | 
			
		||||
		logger: log.New(out, "", 0),
 | 
			
		||||
		level:  lvl,
 | 
			
		||||
	}
 | 
			
		||||
	if fmtLogging == JSONFORMATTER {
 | 
			
		||||
		logger.entryFormatter = &jsonFormatter{}
 | 
			
		||||
	} else if fmtLogging == KVFORMATTER {
 | 
			
		||||
		logger.entryFormatter = &keyvalueFormatter{}
 | 
			
		||||
	} else {
 | 
			
		||||
		logger.entryFormatter = &textFormatter{}
 | 
			
		||||
	}
 | 
			
		||||
	return logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Logp(level Level, msg string, kv ...Pair) {
 | 
			
		||||
	if gl.logger == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if level <= gl.level {
 | 
			
		||||
		e := newLogEntry(msg, level)
 | 
			
		||||
		copyPairs(e, kv)
 | 
			
		||||
		gl.log(e)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Debugp(msg string, kv ...Pair) {
 | 
			
		||||
	gl.Logp(DEBUG, msg, kv...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Tracep(msg string, kv ...Pair) {
 | 
			
		||||
	gl.Logp(TRACE, msg, kv...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Requestp(rlevel Level, msg string, kv ...Pair) {
 | 
			
		||||
	if gl.logger == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if REQUEST <= gl.level {
 | 
			
		||||
		e := newLogEntry(msg, REQUEST)
 | 
			
		||||
		e.Rlevel = rlevel
 | 
			
		||||
		copyPairs(e, kv)
 | 
			
		||||
		gl.log(e)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Infop(msg string, kv ...Pair) {
 | 
			
		||||
	gl.Logp(INFO, msg, kv...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Warnp(msg string, kv ...Pair) {
 | 
			
		||||
	gl.Logp(WARN, msg, kv...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Errorp(msg string, kv ...Pair) {
 | 
			
		||||
	gl.Logp(ERROR, msg, kv...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Severep(msg string, kv ...Pair) {
 | 
			
		||||
	gl.Logp(SEVERE, msg, kv...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Fatalp(msg string, kv ...Pair) {
 | 
			
		||||
	gl.Logp(FATAL, msg, kv...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Logm(level Level, msg string, kv Map) {
 | 
			
		||||
	if gl.logger == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if level <= gl.level {
 | 
			
		||||
		e := newLogEntry(msg, level)
 | 
			
		||||
		e.Data = kv
 | 
			
		||||
		gl.log(e)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Debugm(msg string, kv Map) {
 | 
			
		||||
	gl.Logm(DEBUG, msg, kv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Tracem(msg string, kv Map) {
 | 
			
		||||
	gl.Logm(TRACE, msg, kv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Requestm(rlevel Level, msg string, kv Map) {
 | 
			
		||||
	if gl.logger == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if REQUEST <= gl.level {
 | 
			
		||||
		e := newLogEntry(msg, REQUEST)
 | 
			
		||||
		e.Rlevel = rlevel
 | 
			
		||||
		e.Data = kv
 | 
			
		||||
		gl.log(e)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Infom(msg string, kv Map) {
 | 
			
		||||
	gl.Logm(INFO, msg, kv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Warnm(msg string, kv Map) {
 | 
			
		||||
	gl.Logm(WARN, msg, kv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Errorm(msg string, kv Map) {
 | 
			
		||||
	gl.Logm(ERROR, msg, kv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Severem(msg string, kv Map) {
 | 
			
		||||
	gl.Logm(SEVERE, msg, kv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Fatalm(msg string, kv Map) {
 | 
			
		||||
	gl.Logm(FATAL, msg, kv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Logf(level Level, format string, args ...interface{}) {
 | 
			
		||||
	if gl.logger == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if level <= gl.level {
 | 
			
		||||
		e := newLogEntry(fmt.Sprintf(format, args...), level)
 | 
			
		||||
		gl.log(e)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Debugf(format string, args ...interface{}) {
 | 
			
		||||
	gl.Logf(DEBUG, format, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Tracef(format string, args ...interface{}) {
 | 
			
		||||
	gl.Logf(TRACE, format, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Requestf(rlevel Level, format string, args ...interface{}) {
 | 
			
		||||
	if gl.logger == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if REQUEST <= gl.level {
 | 
			
		||||
		e := newLogEntry(fmt.Sprintf(format, args...), REQUEST)
 | 
			
		||||
		e.Rlevel = rlevel
 | 
			
		||||
		gl.log(e)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Infof(format string, args ...interface{}) {
 | 
			
		||||
	gl.Logf(INFO, format, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Warnf(format string, args ...interface{}) {
 | 
			
		||||
	gl.Logf(WARN, format, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Errorf(format string, args ...interface{}) {
 | 
			
		||||
	gl.Logf(ERROR, format, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Severef(format string, args ...interface{}) {
 | 
			
		||||
	gl.Logf(SEVERE, format, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Fatalf(format string, args ...interface{}) {
 | 
			
		||||
	gl.Logf(FATAL, format, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) Level() Level {
 | 
			
		||||
	return gl.level
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) SetLevel(level Level) {
 | 
			
		||||
	gl.level = level
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gl *goLogger) log(newEntry *logEntry) {
 | 
			
		||||
	s := gl.entryFormatter.format(newEntry)
 | 
			
		||||
	gl.logger.Print(s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type logEntry struct {
 | 
			
		||||
	Time    string
 | 
			
		||||
	Level   Level
 | 
			
		||||
	Rlevel  Level
 | 
			
		||||
	Message string
 | 
			
		||||
	Data    Map
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newLogEntry(msg string, level Level) *logEntry {
 | 
			
		||||
	return &logEntry{
 | 
			
		||||
		Time:    time.Now().Format("2006-01-02T15:04:05.000-07:00"), // time.RFC3339 with milliseconds
 | 
			
		||||
		Level:   level,
 | 
			
		||||
		Rlevel:  NONE,
 | 
			
		||||
		Message: msg,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func copyPairs(newEntry *logEntry, pairs []Pair) {
 | 
			
		||||
	newEntry.Data = make(Map, len(pairs))
 | 
			
		||||
	for _, p := range pairs {
 | 
			
		||||
		newEntry.Data[p.Name] = p.Value
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type formatter interface {
 | 
			
		||||
	format(*logEntry) string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type textFormatter struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ex. 2016-02-10T09:15:25.498-08:00 [INFO] This is a message from test in text format
 | 
			
		||||
 | 
			
		||||
func (*textFormatter) format(newEntry *logEntry) string {
 | 
			
		||||
	b := &bytes.Buffer{}
 | 
			
		||||
	appendValue(b, newEntry.Time)
 | 
			
		||||
	if newEntry.Rlevel != NONE {
 | 
			
		||||
		fmt.Fprintf(b, "[%s,%s] ", newEntry.Level.String(), newEntry.Rlevel.String())
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Fprintf(b, "[%s] ", newEntry.Level.String())
 | 
			
		||||
	}
 | 
			
		||||
	appendValue(b, newEntry.Message)
 | 
			
		||||
	for key, value := range newEntry.Data {
 | 
			
		||||
		appendKeyValue(b, key, value)
 | 
			
		||||
	}
 | 
			
		||||
	b.WriteByte('\n')
 | 
			
		||||
	s := bytes.NewBuffer(b.Bytes())
 | 
			
		||||
	return s.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func appendValue(b *bytes.Buffer, value interface{}) {
 | 
			
		||||
	if _, ok := value.(string); ok {
 | 
			
		||||
		fmt.Fprintf(b, "%s ", value)
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Fprintf(b, "%v ", value)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type keyvalueFormatter struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ex. _time=2016-02-10T09:15:25.498-08:00 _level=INFO _msg=This is a message from test in key-value format
 | 
			
		||||
 | 
			
		||||
func (*keyvalueFormatter) format(newEntry *logEntry) string {
 | 
			
		||||
	b := &bytes.Buffer{}
 | 
			
		||||
	appendKeyValue(b, _TIME, newEntry.Time)
 | 
			
		||||
	appendKeyValue(b, _LEVEL, newEntry.Level.String())
 | 
			
		||||
	if newEntry.Rlevel != NONE {
 | 
			
		||||
		appendKeyValue(b, _RLEVEL, newEntry.Rlevel.String())
 | 
			
		||||
	}
 | 
			
		||||
	appendKeyValue(b, _MSG, newEntry.Message)
 | 
			
		||||
	for key, value := range newEntry.Data {
 | 
			
		||||
		appendKeyValue(b, key, value)
 | 
			
		||||
	}
 | 
			
		||||
	b.WriteByte('\n')
 | 
			
		||||
	s := bytes.NewBuffer(b.Bytes())
 | 
			
		||||
	return s.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func appendKeyValue(b *bytes.Buffer, key, value interface{}) {
 | 
			
		||||
	if _, ok := value.(string); ok {
 | 
			
		||||
		fmt.Fprintf(b, "%v=%s ", key, value)
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Fprintf(b, "%v=%v ", key, value)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type jsonFormatter struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ex. {"_level":"INFO","_msg":"This is a message from test in json format","_time":"2016-02-10T09:12:59.518-08:00"}
 | 
			
		||||
 | 
			
		||||
func (*jsonFormatter) format(newEntry *logEntry) string {
 | 
			
		||||
	if newEntry.Data == nil {
 | 
			
		||||
		newEntry.Data = make(Map, 5)
 | 
			
		||||
	}
 | 
			
		||||
	newEntry.Data[_TIME] = newEntry.Time
 | 
			
		||||
	newEntry.Data[_LEVEL] = newEntry.Level.String()
 | 
			
		||||
	if newEntry.Rlevel != NONE {
 | 
			
		||||
		newEntry.Data[_RLEVEL] = newEntry.Rlevel.String()
 | 
			
		||||
	}
 | 
			
		||||
	newEntry.Data[_MSG] = newEntry.Message
 | 
			
		||||
	serialized, _ := json.Marshal(newEntry.Data)
 | 
			
		||||
	s := bytes.NewBuffer(append(serialized, '\n'))
 | 
			
		||||
	return s.String()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										207
									
								
								vendor/github.com/couchbase/goutils/scramsha/scramsha.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								vendor/github.com/couchbase/goutils/scramsha/scramsha.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,207 @@
 | 
			
		||||
// @author Couchbase <info@couchbase.com>
 | 
			
		||||
// @copyright 2018 Couchbase, Inc.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
// Package scramsha provides implementation of client side SCRAM-SHA
 | 
			
		||||
// according to https://tools.ietf.org/html/rfc5802
 | 
			
		||||
package scramsha
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/hmac"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/sha1"
 | 
			
		||||
	"crypto/sha256"
 | 
			
		||||
	"crypto/sha512"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"golang.org/x/crypto/pbkdf2"
 | 
			
		||||
	"hash"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func hmacHash(message []byte, secret []byte, hashFunc func() hash.Hash) []byte {
 | 
			
		||||
	h := hmac.New(hashFunc, secret)
 | 
			
		||||
	h.Write(message)
 | 
			
		||||
	return h.Sum(nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func shaHash(message []byte, hashFunc func() hash.Hash) []byte {
 | 
			
		||||
	h := hashFunc()
 | 
			
		||||
	h.Write(message)
 | 
			
		||||
	return h.Sum(nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func generateClientNonce(size int) (string, error) {
 | 
			
		||||
	randomBytes := make([]byte, size)
 | 
			
		||||
	_, err := rand.Read(randomBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", errors.Wrap(err, "Unable to generate nonce")
 | 
			
		||||
	}
 | 
			
		||||
	return base64.StdEncoding.EncodeToString(randomBytes), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ScramSha provides context for SCRAM-SHA handling
 | 
			
		||||
type ScramSha struct {
 | 
			
		||||
	hashSize       int
 | 
			
		||||
	hashFunc       func() hash.Hash
 | 
			
		||||
	clientNonce    string
 | 
			
		||||
	serverNonce    string
 | 
			
		||||
	salt           []byte
 | 
			
		||||
	i              int
 | 
			
		||||
	saltedPassword []byte
 | 
			
		||||
	authMessage    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var knownMethods = []string{"SCRAM-SHA512", "SCRAM-SHA256", "SCRAM-SHA1"}
 | 
			
		||||
 | 
			
		||||
// BestMethod returns SCRAM-SHA method we consider the best out of suggested
 | 
			
		||||
// by server
 | 
			
		||||
func BestMethod(methods string) (string, error) {
 | 
			
		||||
	for _, m := range knownMethods {
 | 
			
		||||
		if strings.Index(methods, m) != -1 {
 | 
			
		||||
			return m, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return "", errors.Errorf(
 | 
			
		||||
		"None of the server suggested methods [%s] are supported",
 | 
			
		||||
		methods)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewScramSha creates context for SCRAM-SHA handling
 | 
			
		||||
func NewScramSha(method string) (*ScramSha, error) {
 | 
			
		||||
	s := &ScramSha{}
 | 
			
		||||
 | 
			
		||||
	if method == knownMethods[0] {
 | 
			
		||||
		s.hashFunc = sha512.New
 | 
			
		||||
		s.hashSize = 64
 | 
			
		||||
	} else if method == knownMethods[1] {
 | 
			
		||||
		s.hashFunc = sha256.New
 | 
			
		||||
		s.hashSize = 32
 | 
			
		||||
	} else if method == knownMethods[2] {
 | 
			
		||||
		s.hashFunc = sha1.New
 | 
			
		||||
		s.hashSize = 20
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil, errors.Errorf("Unsupported method %s", method)
 | 
			
		||||
	}
 | 
			
		||||
	return s, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetStartRequest builds start SCRAM-SHA request to be sent to server
 | 
			
		||||
func (s *ScramSha) GetStartRequest(user string) (string, error) {
 | 
			
		||||
	var err error
 | 
			
		||||
	s.clientNonce, err = generateClientNonce(24)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", errors.Wrapf(err, "Unable to generate SCRAM-SHA "+
 | 
			
		||||
			"start request for user %s", user)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	message := fmt.Sprintf("n,,n=%s,r=%s", user, s.clientNonce)
 | 
			
		||||
	s.authMessage = message[3:]
 | 
			
		||||
	return message, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleStartResponse handles server response on start SCRAM-SHA request
 | 
			
		||||
func (s *ScramSha) HandleStartResponse(response string) error {
 | 
			
		||||
	parts := strings.Split(response, ",")
 | 
			
		||||
	if len(parts) != 3 {
 | 
			
		||||
		return errors.Errorf("expected 3 fields in first SCRAM-SHA-1 "+
 | 
			
		||||
			"server message %s", response)
 | 
			
		||||
	}
 | 
			
		||||
	if !strings.HasPrefix(parts[0], "r=") || len(parts[0]) < 3 {
 | 
			
		||||
		return errors.Errorf("Server sent an invalid nonce %s",
 | 
			
		||||
			parts[0])
 | 
			
		||||
	}
 | 
			
		||||
	if !strings.HasPrefix(parts[1], "s=") || len(parts[1]) < 3 {
 | 
			
		||||
		return errors.Errorf("Server sent an invalid salt %s", parts[1])
 | 
			
		||||
	}
 | 
			
		||||
	if !strings.HasPrefix(parts[2], "i=") || len(parts[2]) < 3 {
 | 
			
		||||
		return errors.Errorf("Server sent an invalid iteration count %s",
 | 
			
		||||
			parts[2])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.serverNonce = parts[0][2:]
 | 
			
		||||
	encodedSalt := parts[1][2:]
 | 
			
		||||
	var err error
 | 
			
		||||
	s.i, err = strconv.Atoi(parts[2][2:])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Errorf("Iteration count %s must be integer.",
 | 
			
		||||
			parts[2][2:])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if s.i < 1 {
 | 
			
		||||
		return errors.New("Iteration count should be positive")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !strings.HasPrefix(s.serverNonce, s.clientNonce) {
 | 
			
		||||
		return errors.Errorf("Server nonce %s doesn't contain client"+
 | 
			
		||||
			" nonce %s", s.serverNonce, s.clientNonce)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.salt, err = base64.StdEncoding.DecodeString(encodedSalt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrapf(err, "Unable to decode salt %s",
 | 
			
		||||
			encodedSalt)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.authMessage = s.authMessage + "," + response
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetFinalRequest builds final SCRAM-SHA request to be sent to server
 | 
			
		||||
func (s *ScramSha) GetFinalRequest(pass string) string {
 | 
			
		||||
	clientFinalMessageBare := "c=biws,r=" + s.serverNonce
 | 
			
		||||
	s.authMessage = s.authMessage + "," + clientFinalMessageBare
 | 
			
		||||
 | 
			
		||||
	s.saltedPassword = pbkdf2.Key([]byte(pass), s.salt, s.i,
 | 
			
		||||
		s.hashSize, s.hashFunc)
 | 
			
		||||
 | 
			
		||||
	clientKey := hmacHash([]byte("Client Key"), s.saltedPassword, s.hashFunc)
 | 
			
		||||
	storedKey := shaHash(clientKey, s.hashFunc)
 | 
			
		||||
	clientSignature := hmacHash([]byte(s.authMessage), storedKey, s.hashFunc)
 | 
			
		||||
 | 
			
		||||
	clientProof := make([]byte, len(clientSignature))
 | 
			
		||||
	for i := 0; i < len(clientSignature); i++ {
 | 
			
		||||
		clientProof[i] = clientKey[i] ^ clientSignature[i]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return clientFinalMessageBare + ",p=" +
 | 
			
		||||
		base64.StdEncoding.EncodeToString(clientProof)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleFinalResponse handles server's response on final SCRAM-SHA request
 | 
			
		||||
func (s *ScramSha) HandleFinalResponse(response string) error {
 | 
			
		||||
	if strings.Contains(response, ",") ||
 | 
			
		||||
		!strings.HasPrefix(response, "v=") {
 | 
			
		||||
		return errors.Errorf("Server sent an invalid final message %s",
 | 
			
		||||
			response)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	decodedMessage, err := base64.StdEncoding.DecodeString(response[2:])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrapf(err, "Unable to decode server message %s",
 | 
			
		||||
			response[2:])
 | 
			
		||||
	}
 | 
			
		||||
	serverKey := hmacHash([]byte("Server Key"), s.saltedPassword,
 | 
			
		||||
		s.hashFunc)
 | 
			
		||||
	serverSignature := hmacHash([]byte(s.authMessage), serverKey,
 | 
			
		||||
		s.hashFunc)
 | 
			
		||||
	if string(decodedMessage) != string(serverSignature) {
 | 
			
		||||
		return errors.Errorf("Server proof %s doesn't match "+
 | 
			
		||||
			"the expected: %s",
 | 
			
		||||
			string(decodedMessage), string(serverSignature))
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										252
									
								
								vendor/github.com/couchbase/goutils/scramsha/scramsha_http.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								vendor/github.com/couchbase/goutils/scramsha/scramsha_http.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,252 @@
 | 
			
		||||
// @author Couchbase <info@couchbase.com>
 | 
			
		||||
// @copyright 2018 Couchbase, Inc.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
// Package scramsha provides implementation of client side SCRAM-SHA
 | 
			
		||||
// via Http according to https://tools.ietf.org/html/rfc7804
 | 
			
		||||
package scramsha
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// consts used to parse scramsha response from target
 | 
			
		||||
const (
 | 
			
		||||
	WWWAuthenticate    = "WWW-Authenticate"
 | 
			
		||||
	AuthenticationInfo = "Authentication-Info"
 | 
			
		||||
	Authorization      = "Authorization"
 | 
			
		||||
	DataPrefix         = "data="
 | 
			
		||||
	SidPrefix          = "sid="
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Request provides implementation of http request that can be retried
 | 
			
		||||
type Request struct {
 | 
			
		||||
	body io.ReadSeeker
 | 
			
		||||
 | 
			
		||||
	// Embed an HTTP request directly. This makes a *Request act exactly
 | 
			
		||||
	// like an *http.Request so that all meta methods are supported.
 | 
			
		||||
	*http.Request
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type lenReader interface {
 | 
			
		||||
	Len() int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRequest creates http request that can be retried
 | 
			
		||||
func NewRequest(method, url string, body io.ReadSeeker) (*Request, error) {
 | 
			
		||||
	// Wrap the body in a noop ReadCloser if non-nil. This prevents the
 | 
			
		||||
	// reader from being closed by the HTTP client.
 | 
			
		||||
	var rcBody io.ReadCloser
 | 
			
		||||
	if body != nil {
 | 
			
		||||
		rcBody = ioutil.NopCloser(body)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Make the request with the noop-closer for the body.
 | 
			
		||||
	httpReq, err := http.NewRequest(method, url, rcBody)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check if we can set the Content-Length automatically.
 | 
			
		||||
	if lr, ok := body.(lenReader); ok {
 | 
			
		||||
		httpReq.ContentLength = int64(lr.Len())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &Request{body, httpReq}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func encode(str string) string {
 | 
			
		||||
	return base64.StdEncoding.EncodeToString([]byte(str))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func decode(str string) (string, error) {
 | 
			
		||||
	bytes, err := base64.StdEncoding.DecodeString(str)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", errors.Errorf("Cannot base64 decode %s",
 | 
			
		||||
			str)
 | 
			
		||||
	}
 | 
			
		||||
	return string(bytes), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func trimPrefix(s, prefix string) (string, error) {
 | 
			
		||||
	l := len(s)
 | 
			
		||||
	trimmed := strings.TrimPrefix(s, prefix)
 | 
			
		||||
	if l == len(trimmed) {
 | 
			
		||||
		return trimmed, errors.Errorf("Prefix %s not found in %s",
 | 
			
		||||
			prefix, s)
 | 
			
		||||
	}
 | 
			
		||||
	return trimmed, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func drainBody(resp *http.Response) {
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
	io.Copy(ioutil.Discard, resp.Body)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DoScramSha performs SCRAM-SHA handshake via Http
 | 
			
		||||
func DoScramSha(req *Request,
 | 
			
		||||
	username string,
 | 
			
		||||
	password string,
 | 
			
		||||
	client *http.Client) (*http.Response, error) {
 | 
			
		||||
 | 
			
		||||
	method := "SCRAM-SHA-512"
 | 
			
		||||
	s, err := NewScramSha("SCRAM-SHA512")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.Wrap(err,
 | 
			
		||||
			"Unable to initialize SCRAM-SHA handler")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	message, err := s.GetStartRequest(username)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	encodedMessage := method + " " + DataPrefix + encode(message)
 | 
			
		||||
 | 
			
		||||
	req.Header.Set(Authorization, encodedMessage)
 | 
			
		||||
 | 
			
		||||
	res, err := client.Do(req.Request)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.Wrap(err, "Problem sending SCRAM-SHA start"+
 | 
			
		||||
			"request")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if res.StatusCode != http.StatusUnauthorized {
 | 
			
		||||
		return res, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	authHeader := res.Header.Get(WWWAuthenticate)
 | 
			
		||||
	if authHeader == "" {
 | 
			
		||||
		drainBody(res)
 | 
			
		||||
		return nil, errors.Errorf("Header %s is not populated in "+
 | 
			
		||||
			"SCRAM-SHA start response", WWWAuthenticate)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	authHeader, err = trimPrefix(authHeader, method+" ")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if strings.HasPrefix(authHeader, "Basic ") {
 | 
			
		||||
			// user not found
 | 
			
		||||
			return res, nil
 | 
			
		||||
		}
 | 
			
		||||
		drainBody(res)
 | 
			
		||||
		return nil, errors.Wrapf(err, "Error while parsing SCRAM-SHA "+
 | 
			
		||||
			"start response %s", authHeader)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	drainBody(res)
 | 
			
		||||
 | 
			
		||||
	sid, response, err := parseSidAndData(authHeader)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.Wrapf(err, "Error while parsing SCRAM-SHA "+
 | 
			
		||||
			"start response %s", authHeader)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = s.HandleStartResponse(response)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.Wrapf(err, "Error parsing SCRAM-SHA start "+
 | 
			
		||||
			"response %s", response)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	message = s.GetFinalRequest(password)
 | 
			
		||||
	encodedMessage = method + " " + SidPrefix + sid + "," + DataPrefix +
 | 
			
		||||
		encode(message)
 | 
			
		||||
 | 
			
		||||
	req.Header.Set(Authorization, encodedMessage)
 | 
			
		||||
 | 
			
		||||
	// rewind request body so it can be resent again
 | 
			
		||||
	if req.body != nil {
 | 
			
		||||
		if _, err = req.body.Seek(0, 0); err != nil {
 | 
			
		||||
			return nil, errors.Errorf("Failed to seek body: %v",
 | 
			
		||||
				err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	res, err = client.Do(req.Request)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.Wrap(err, "Problem sending SCRAM-SHA final"+
 | 
			
		||||
			"request")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if res.StatusCode == http.StatusUnauthorized {
 | 
			
		||||
		// TODO retrieve and return error
 | 
			
		||||
		return res, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if res.StatusCode >= http.StatusInternalServerError {
 | 
			
		||||
		// in this case we cannot expect server to set headers properly
 | 
			
		||||
		return res, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	authHeader = res.Header.Get(AuthenticationInfo)
 | 
			
		||||
	if authHeader == "" {
 | 
			
		||||
		drainBody(res)
 | 
			
		||||
		return nil, errors.Errorf("Header %s is not populated in "+
 | 
			
		||||
			"SCRAM-SHA final response", AuthenticationInfo)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	finalSid, response, err := parseSidAndData(authHeader)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		drainBody(res)
 | 
			
		||||
		return nil, errors.Wrapf(err, "Error while parsing SCRAM-SHA "+
 | 
			
		||||
			"final response %s", authHeader)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if finalSid != sid {
 | 
			
		||||
		drainBody(res)
 | 
			
		||||
		return nil, errors.Errorf("Sid %s returned by server "+
 | 
			
		||||
			"doesn't match the original sid %s", finalSid, sid)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = s.HandleFinalResponse(response)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		drainBody(res)
 | 
			
		||||
		return nil, errors.Wrapf(err,
 | 
			
		||||
			"Error handling SCRAM-SHA final server response %s",
 | 
			
		||||
			response)
 | 
			
		||||
	}
 | 
			
		||||
	return res, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseSidAndData(authHeader string) (string, string, error) {
 | 
			
		||||
	sidIndex := strings.Index(authHeader, SidPrefix)
 | 
			
		||||
	if sidIndex < 0 {
 | 
			
		||||
		return "", "", errors.Errorf("Cannot find %s in %s",
 | 
			
		||||
			SidPrefix, authHeader)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sidEndIndex := strings.Index(authHeader, ",")
 | 
			
		||||
	if sidEndIndex < 0 {
 | 
			
		||||
		return "", "", errors.Errorf("Cannot find ',' in %s",
 | 
			
		||||
			authHeader)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sid := authHeader[sidIndex+len(SidPrefix) : sidEndIndex]
 | 
			
		||||
 | 
			
		||||
	dataIndex := strings.Index(authHeader, DataPrefix)
 | 
			
		||||
	if dataIndex < 0 {
 | 
			
		||||
		return "", "", errors.Errorf("Cannot find %s in %s",
 | 
			
		||||
			DataPrefix, authHeader)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data, err := decode(authHeader[dataIndex+len(DataPrefix):])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", "", err
 | 
			
		||||
	}
 | 
			
		||||
	return sid, data, nil
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user