add other session providers (#5963)
This commit is contained in:
21
vendor/github.com/BurntSushi/toml/COPYING
generated
vendored
Normal file
21
vendor/github.com/BurntSushi/toml/COPYING
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 TOML authors
|
||||
|
||||
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.
|
21
vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING
generated
vendored
Normal file
21
vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 TOML authors
|
||||
|
||||
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.
|
21
vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING
generated
vendored
Normal file
21
vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 TOML authors
|
||||
|
||||
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.
|
21
vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING
generated
vendored
Normal file
21
vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 TOML authors
|
||||
|
||||
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.
|
509
vendor/github.com/BurntSushi/toml/decode.go
generated
vendored
Normal file
509
vendor/github.com/BurntSushi/toml/decode.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
121
vendor/github.com/BurntSushi/toml/decode_meta.go
generated
vendored
Normal file
121
vendor/github.com/BurntSushi/toml/decode_meta.go
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
package toml
|
||||
|
||||
import "strings"
|
||||
|
||||
// MetaData allows access to meta information about TOML data that may not
|
||||
// be inferrable via reflection. In particular, whether a key has been defined
|
||||
// and the TOML type of a key.
|
||||
type MetaData struct {
|
||||
mapping map[string]interface{}
|
||||
types map[string]tomlType
|
||||
keys []Key
|
||||
decoded map[string]bool
|
||||
context Key // Used only during decoding.
|
||||
}
|
||||
|
||||
// IsDefined returns true if the key given exists in the TOML data. The key
|
||||
// should be specified hierarchially. e.g.,
|
||||
//
|
||||
// // access the TOML key 'a.b.c'
|
||||
// IsDefined("a", "b", "c")
|
||||
//
|
||||
// IsDefined will return false if an empty key given. Keys are case sensitive.
|
||||
func (md *MetaData) IsDefined(key ...string) bool {
|
||||
if len(key) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
var hash map[string]interface{}
|
||||
var ok bool
|
||||
var hashOrVal interface{} = md.mapping
|
||||
for _, k := range key {
|
||||
if hash, ok = hashOrVal.(map[string]interface{}); !ok {
|
||||
return false
|
||||
}
|
||||
if hashOrVal, ok = hash[k]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Type returns a string representation of the type of the key specified.
|
||||
//
|
||||
// Type will return the empty string if given an empty key or a key that
|
||||
// does not exist. Keys are case sensitive.
|
||||
func (md *MetaData) Type(key ...string) string {
|
||||
fullkey := strings.Join(key, ".")
|
||||
if typ, ok := md.types[fullkey]; ok {
|
||||
return typ.typeString()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Key is the type of any TOML key, including key groups. Use (MetaData).Keys
|
||||
// to get values of this type.
|
||||
type Key []string
|
||||
|
||||
func (k Key) String() string {
|
||||
return strings.Join(k, ".")
|
||||
}
|
||||
|
||||
func (k Key) maybeQuotedAll() string {
|
||||
var ss []string
|
||||
for i := range k {
|
||||
ss = append(ss, k.maybeQuoted(i))
|
||||
}
|
||||
return strings.Join(ss, ".")
|
||||
}
|
||||
|
||||
func (k Key) maybeQuoted(i int) string {
|
||||
quote := false
|
||||
for _, c := range k[i] {
|
||||
if !isBareKeyChar(c) {
|
||||
quote = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if quote {
|
||||
return "\"" + strings.Replace(k[i], "\"", "\\\"", -1) + "\""
|
||||
}
|
||||
return k[i]
|
||||
}
|
||||
|
||||
func (k Key) add(piece string) Key {
|
||||
newKey := make(Key, len(k)+1)
|
||||
copy(newKey, k)
|
||||
newKey[len(k)] = piece
|
||||
return newKey
|
||||
}
|
||||
|
||||
// Keys returns a slice of every key in the TOML data, including key groups.
|
||||
// Each key is itself a slice, where the first element is the top of the
|
||||
// hierarchy and the last is the most specific.
|
||||
//
|
||||
// The list will have the same order as the keys appeared in the TOML data.
|
||||
//
|
||||
// All keys returned are non-empty.
|
||||
func (md *MetaData) Keys() []Key {
|
||||
return md.keys
|
||||
}
|
||||
|
||||
// Undecoded returns all keys that have not been decoded in the order in which
|
||||
// they appear in the original TOML document.
|
||||
//
|
||||
// This includes keys that haven't been decoded because of a Primitive value.
|
||||
// Once the Primitive value is decoded, the keys will be considered decoded.
|
||||
//
|
||||
// Also note that decoding into an empty interface will result in no decoding,
|
||||
// and so no keys will be considered decoded.
|
||||
//
|
||||
// In this sense, the Undecoded keys correspond to keys in the TOML document
|
||||
// that do not have a concrete type in your representation.
|
||||
func (md *MetaData) Undecoded() []Key {
|
||||
undecoded := make([]Key, 0, len(md.keys))
|
||||
for _, key := range md.keys {
|
||||
if !md.decoded[key.String()] {
|
||||
undecoded = append(undecoded, key)
|
||||
}
|
||||
}
|
||||
return undecoded
|
||||
}
|
27
vendor/github.com/BurntSushi/toml/doc.go
generated
vendored
Normal file
27
vendor/github.com/BurntSushi/toml/doc.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
Package toml provides facilities for decoding and encoding TOML configuration
|
||||
files via reflection. There is also support for delaying decoding with
|
||||
the Primitive type, and querying the set of keys in a TOML document with the
|
||||
MetaData type.
|
||||
|
||||
The specification implemented: https://github.com/toml-lang/toml
|
||||
|
||||
The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify
|
||||
whether a file is a valid TOML document. It can also be used to print the
|
||||
type of each key in a TOML document.
|
||||
|
||||
Testing
|
||||
|
||||
There are two important types of tests used for this package. The first is
|
||||
contained inside '*_test.go' files and uses the standard Go unit testing
|
||||
framework. These tests are primarily devoted to holistically testing the
|
||||
decoder and encoder.
|
||||
|
||||
The second type of testing is used to verify the implementation's adherence
|
||||
to the TOML specification. These tests have been factored into their own
|
||||
project: https://github.com/BurntSushi/toml-test
|
||||
|
||||
The reason the tests are in a separate project is so that they can be used by
|
||||
any implementation of TOML. Namely, it is language agnostic.
|
||||
*/
|
||||
package toml
|
568
vendor/github.com/BurntSushi/toml/encode.go
generated
vendored
Normal file
568
vendor/github.com/BurntSushi/toml/encode.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
19
vendor/github.com/BurntSushi/toml/encoding_types.go
generated
vendored
Normal file
19
vendor/github.com/BurntSushi/toml/encoding_types.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// +build go1.2
|
||||
|
||||
package toml
|
||||
|
||||
// In order to support Go 1.1, we define our own TextMarshaler and
|
||||
// TextUnmarshaler types. For Go 1.2+, we just alias them with the
|
||||
// standard library interfaces.
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
)
|
||||
|
||||
// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here
|
||||
// so that Go 1.1 can be supported.
|
||||
type TextMarshaler encoding.TextMarshaler
|
||||
|
||||
// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined
|
||||
// here so that Go 1.1 can be supported.
|
||||
type TextUnmarshaler encoding.TextUnmarshaler
|
18
vendor/github.com/BurntSushi/toml/encoding_types_1.1.go
generated
vendored
Normal file
18
vendor/github.com/BurntSushi/toml/encoding_types_1.1.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
// +build !go1.2
|
||||
|
||||
package toml
|
||||
|
||||
// These interfaces were introduced in Go 1.2, so we add them manually when
|
||||
// compiling for Go 1.1.
|
||||
|
||||
// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here
|
||||
// so that Go 1.1 can be supported.
|
||||
type TextMarshaler interface {
|
||||
MarshalText() (text []byte, err error)
|
||||
}
|
||||
|
||||
// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined
|
||||
// here so that Go 1.1 can be supported.
|
||||
type TextUnmarshaler interface {
|
||||
UnmarshalText(text []byte) error
|
||||
}
|
953
vendor/github.com/BurntSushi/toml/lex.go
generated
vendored
Normal file
953
vendor/github.com/BurntSushi/toml/lex.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
592
vendor/github.com/BurntSushi/toml/parse.go
generated
vendored
Normal file
592
vendor/github.com/BurntSushi/toml/parse.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
91
vendor/github.com/BurntSushi/toml/type_check.go
generated
vendored
Normal file
91
vendor/github.com/BurntSushi/toml/type_check.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
package toml
|
||||
|
||||
// tomlType represents any Go type that corresponds to a TOML type.
|
||||
// While the first draft of the TOML spec has a simplistic type system that
|
||||
// probably doesn't need this level of sophistication, we seem to be militating
|
||||
// toward adding real composite types.
|
||||
type tomlType interface {
|
||||
typeString() string
|
||||
}
|
||||
|
||||
// typeEqual accepts any two types and returns true if they are equal.
|
||||
func typeEqual(t1, t2 tomlType) bool {
|
||||
if t1 == nil || t2 == nil {
|
||||
return false
|
||||
}
|
||||
return t1.typeString() == t2.typeString()
|
||||
}
|
||||
|
||||
func typeIsHash(t tomlType) bool {
|
||||
return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash)
|
||||
}
|
||||
|
||||
type tomlBaseType string
|
||||
|
||||
func (btype tomlBaseType) typeString() string {
|
||||
return string(btype)
|
||||
}
|
||||
|
||||
func (btype tomlBaseType) String() string {
|
||||
return btype.typeString()
|
||||
}
|
||||
|
||||
var (
|
||||
tomlInteger tomlBaseType = "Integer"
|
||||
tomlFloat tomlBaseType = "Float"
|
||||
tomlDatetime tomlBaseType = "Datetime"
|
||||
tomlString tomlBaseType = "String"
|
||||
tomlBool tomlBaseType = "Bool"
|
||||
tomlArray tomlBaseType = "Array"
|
||||
tomlHash tomlBaseType = "Hash"
|
||||
tomlArrayHash tomlBaseType = "ArrayHash"
|
||||
)
|
||||
|
||||
// typeOfPrimitive returns a tomlType of any primitive value in TOML.
|
||||
// Primitive values are: Integer, Float, Datetime, String and Bool.
|
||||
//
|
||||
// Passing a lexer item other than the following will cause a BUG message
|
||||
// to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime.
|
||||
func (p *parser) typeOfPrimitive(lexItem item) tomlType {
|
||||
switch lexItem.typ {
|
||||
case itemInteger:
|
||||
return tomlInteger
|
||||
case itemFloat:
|
||||
return tomlFloat
|
||||
case itemDatetime:
|
||||
return tomlDatetime
|
||||
case itemString:
|
||||
return tomlString
|
||||
case itemMultilineString:
|
||||
return tomlString
|
||||
case itemRawString:
|
||||
return tomlString
|
||||
case itemRawMultilineString:
|
||||
return tomlString
|
||||
case itemBool:
|
||||
return tomlBool
|
||||
}
|
||||
p.bug("Cannot infer primitive type of lex item '%s'.", lexItem)
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// typeOfArray returns a tomlType for an array given a list of types of its
|
||||
// values.
|
||||
//
|
||||
// In the current spec, if an array is homogeneous, then its type is always
|
||||
// "Array". If the array is not homogeneous, an error is generated.
|
||||
func (p *parser) typeOfArray(types []tomlType) tomlType {
|
||||
// Empty arrays are cool.
|
||||
if len(types) == 0 {
|
||||
return tomlArray
|
||||
}
|
||||
|
||||
theType := types[0]
|
||||
for _, t := range types[1:] {
|
||||
if !typeEqual(theType, t) {
|
||||
p.panicf("Array contains values of type '%s' and '%s', but "+
|
||||
"arrays must be homogeneous.", theType, t)
|
||||
}
|
||||
}
|
||||
return tomlArray
|
||||
}
|
242
vendor/github.com/BurntSushi/toml/type_fields.go
generated
vendored
Normal file
242
vendor/github.com/BurntSushi/toml/type_fields.go
generated
vendored
Normal file
@ -0,0 +1,242 @@
|
||||
package toml
|
||||
|
||||
// Struct field handling is adapted from code in encoding/json:
|
||||
//
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the Go distribution.
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A field represents a single field found in a struct.
|
||||
type field struct {
|
||||
name string // the name of the field (`toml` tag included)
|
||||
tag bool // whether field has a `toml` tag
|
||||
index []int // represents the depth of an anonymous field
|
||||
typ reflect.Type // the type of the field
|
||||
}
|
||||
|
||||
// byName sorts field by name, breaking ties with depth,
|
||||
// then breaking ties with "name came from toml tag", then
|
||||
// breaking ties with index sequence.
|
||||
type byName []field
|
||||
|
||||
func (x byName) Len() int { return len(x) }
|
||||
|
||||
func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
|
||||
func (x byName) Less(i, j int) bool {
|
||||
if x[i].name != x[j].name {
|
||||
return x[i].name < x[j].name
|
||||
}
|
||||
if len(x[i].index) != len(x[j].index) {
|
||||
return len(x[i].index) < len(x[j].index)
|
||||
}
|
||||
if x[i].tag != x[j].tag {
|
||||
return x[i].tag
|
||||
}
|
||||
return byIndex(x).Less(i, j)
|
||||
}
|
||||
|
||||
// byIndex sorts field by index sequence.
|
||||
type byIndex []field
|
||||
|
||||
func (x byIndex) Len() int { return len(x) }
|
||||
|
||||
func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
|
||||
func (x byIndex) Less(i, j int) bool {
|
||||
for k, xik := range x[i].index {
|
||||
if k >= len(x[j].index) {
|
||||
return false
|
||||
}
|
||||
if xik != x[j].index[k] {
|
||||
return xik < x[j].index[k]
|
||||
}
|
||||
}
|
||||
return len(x[i].index) < len(x[j].index)
|
||||
}
|
||||
|
||||
// typeFields returns a list of fields that TOML should recognize for the given
|
||||
// type. The algorithm is breadth-first search over the set of structs to
|
||||
// include - the top struct and then any reachable anonymous structs.
|
||||
func typeFields(t reflect.Type) []field {
|
||||
// Anonymous fields to explore at the current level and the next.
|
||||
current := []field{}
|
||||
next := []field{{typ: t}}
|
||||
|
||||
// Count of queued names for current level and the next.
|
||||
count := map[reflect.Type]int{}
|
||||
nextCount := map[reflect.Type]int{}
|
||||
|
||||
// Types already visited at an earlier level.
|
||||
visited := map[reflect.Type]bool{}
|
||||
|
||||
// Fields found.
|
||||
var fields []field
|
||||
|
||||
for len(next) > 0 {
|
||||
current, next = next, current[:0]
|
||||
count, nextCount = nextCount, map[reflect.Type]int{}
|
||||
|
||||
for _, f := range current {
|
||||
if visited[f.typ] {
|
||||
continue
|
||||
}
|
||||
visited[f.typ] = true
|
||||
|
||||
// Scan f.typ for fields to include.
|
||||
for i := 0; i < f.typ.NumField(); i++ {
|
||||
sf := f.typ.Field(i)
|
||||
if sf.PkgPath != "" && !sf.Anonymous { // unexported
|
||||
continue
|
||||
}
|
||||
opts := getOptions(sf.Tag)
|
||||
if opts.skip {
|
||||
continue
|
||||
}
|
||||
index := make([]int, len(f.index)+1)
|
||||
copy(index, f.index)
|
||||
index[len(f.index)] = i
|
||||
|
||||
ft := sf.Type
|
||||
if ft.Name() == "" && ft.Kind() == reflect.Ptr {
|
||||
// Follow pointer.
|
||||
ft = ft.Elem()
|
||||
}
|
||||
|
||||
// Record found field and index sequence.
|
||||
if opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
|
||||
tagged := opts.name != ""
|
||||
name := opts.name
|
||||
if name == "" {
|
||||
name = sf.Name
|
||||
}
|
||||
fields = append(fields, field{name, tagged, index, ft})
|
||||
if count[f.typ] > 1 {
|
||||
// If there were multiple instances, add a second,
|
||||
// so that the annihilation code will see a duplicate.
|
||||
// It only cares about the distinction between 1 or 2,
|
||||
// so don't bother generating any more copies.
|
||||
fields = append(fields, fields[len(fields)-1])
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Record new anonymous struct to explore in next round.
|
||||
nextCount[ft]++
|
||||
if nextCount[ft] == 1 {
|
||||
f := field{name: ft.Name(), index: index, typ: ft}
|
||||
next = append(next, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(byName(fields))
|
||||
|
||||
// Delete all fields that are hidden by the Go rules for embedded fields,
|
||||
// except that fields with TOML tags are promoted.
|
||||
|
||||
// The fields are sorted in primary order of name, secondary order
|
||||
// of field index length. Loop over names; for each name, delete
|
||||
// hidden fields by choosing the one dominant field that survives.
|
||||
out := fields[:0]
|
||||
for advance, i := 0, 0; i < len(fields); i += advance {
|
||||
// One iteration per name.
|
||||
// Find the sequence of fields with the name of this first field.
|
||||
fi := fields[i]
|
||||
name := fi.name
|
||||
for advance = 1; i+advance < len(fields); advance++ {
|
||||
fj := fields[i+advance]
|
||||
if fj.name != name {
|
||||
break
|
||||
}
|
||||
}
|
||||
if advance == 1 { // Only one field with this name
|
||||
out = append(out, fi)
|
||||
continue
|
||||
}
|
||||
dominant, ok := dominantField(fields[i : i+advance])
|
||||
if ok {
|
||||
out = append(out, dominant)
|
||||
}
|
||||
}
|
||||
|
||||
fields = out
|
||||
sort.Sort(byIndex(fields))
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
// dominantField looks through the fields, all of which are known to
|
||||
// have the same name, to find the single field that dominates the
|
||||
// others using Go's embedding rules, modified by the presence of
|
||||
// TOML tags. If there are multiple top-level fields, the boolean
|
||||
// will be false: This condition is an error in Go and we skip all
|
||||
// the fields.
|
||||
func dominantField(fields []field) (field, bool) {
|
||||
// The fields are sorted in increasing index-length order. The winner
|
||||
// must therefore be one with the shortest index length. Drop all
|
||||
// longer entries, which is easy: just truncate the slice.
|
||||
length := len(fields[0].index)
|
||||
tagged := -1 // Index of first tagged field.
|
||||
for i, f := range fields {
|
||||
if len(f.index) > length {
|
||||
fields = fields[:i]
|
||||
break
|
||||
}
|
||||
if f.tag {
|
||||
if tagged >= 0 {
|
||||
// Multiple tagged fields at the same level: conflict.
|
||||
// Return no field.
|
||||
return field{}, false
|
||||
}
|
||||
tagged = i
|
||||
}
|
||||
}
|
||||
if tagged >= 0 {
|
||||
return fields[tagged], true
|
||||
}
|
||||
// All remaining fields have the same length. If there's more than one,
|
||||
// we have a conflict (two fields named "X" at the same level) and we
|
||||
// return no field.
|
||||
if len(fields) > 1 {
|
||||
return field{}, false
|
||||
}
|
||||
return fields[0], true
|
||||
}
|
||||
|
||||
var fieldCache struct {
|
||||
sync.RWMutex
|
||||
m map[reflect.Type][]field
|
||||
}
|
||||
|
||||
// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
|
||||
func cachedTypeFields(t reflect.Type) []field {
|
||||
fieldCache.RLock()
|
||||
f := fieldCache.m[t]
|
||||
fieldCache.RUnlock()
|
||||
if f != nil {
|
||||
return f
|
||||
}
|
||||
|
||||
// Compute fields without lock.
|
||||
// Might duplicate effort but won't hold other computations back.
|
||||
f = typeFields(t)
|
||||
if f == nil {
|
||||
f = []field{}
|
||||
}
|
||||
|
||||
fieldCache.Lock()
|
||||
if fieldCache.m == nil {
|
||||
fieldCache.m = map[reflect.Type][]field{}
|
||||
}
|
||||
fieldCache.m[t] = f
|
||||
fieldCache.Unlock()
|
||||
return f
|
||||
}
|
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
File diff suppressed because it is too large
Load Diff
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
File diff suppressed because it is too large
Load Diff
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
File diff suppressed because it is too large
Load Diff
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
File diff suppressed because it is too large
Load Diff
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
|
||||
}
|
19
vendor/github.com/couchbaselabs/go-couchbase/LICENSE
generated
vendored
Normal file
19
vendor/github.com/couchbaselabs/go-couchbase/LICENSE
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2013 Couchbase, Inc.
|
||||
|
||||
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.
|
32
vendor/github.com/couchbaselabs/go-couchbase/audit.go
generated
vendored
Normal file
32
vendor/github.com/couchbaselabs/go-couchbase/audit.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
package couchbase
|
||||
|
||||
import ()
|
||||
|
||||
// Sample data:
|
||||
// {"disabled":["12333", "22244"],"uid":"132492431","auditdEnabled":true,
|
||||
// "disabledUsers":[{"name":"bill","domain":"local"},{"name":"bob","domain":"local"}],
|
||||
// "logPath":"/Users/johanlarson/Library/Application Support/Couchbase/var/lib/couchbase/logs",
|
||||
// "rotateInterval":86400,"rotateSize":20971520}
|
||||
type AuditSpec struct {
|
||||
Disabled []uint32 `json:"disabled"`
|
||||
Uid string `json:"uid"`
|
||||
AuditdEnabled bool `json:"auditdEnabled`
|
||||
DisabledUsers []AuditUser `json:"disabledUsers"`
|
||||
LogPath string `json:"logPath"`
|
||||
RotateInterval int64 `json:"rotateInterval"`
|
||||
RotateSize int64 `json:"rotateSize"`
|
||||
}
|
||||
|
||||
type AuditUser struct {
|
||||
Name string `json:"name"`
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
|
||||
func (c *Client) GetAuditSpec() (*AuditSpec, error) {
|
||||
ret := &AuditSpec{}
|
||||
err := c.parseURLResponse("/settings/audit", ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user