ls 4 years ago
parent
commit
876c489003
12 changed files with 209 additions and 184 deletions
  1. 5 83
      binding/binding.go
  2. 19 0
      binding/bytesconv.go
  3. 0 48
      binding/default_validator.go
  4. 1 27
      binding/form.go
  5. 37 18
      binding/form_mapping.go
  6. 34 0
      binding/header.go
  7. 10 3
      binding/json.go
  8. 2 0
      binding/msgpack.go
  9. 66 0
      binding/multipart_form_mapping.go
  10. 2 2
      binding/uri.go
  11. 4 3
      go.mod
  12. 29 0
      go.sum

+ 5 - 83
binding/binding.go

@@ -2,6 +2,8 @@
 // Use of this source code is governed by a MIT style
 // license that can be found in the LICENSE file.
 
+// +build !nomsgpack
+
 package binding
 
 import "net/http"
@@ -29,42 +31,6 @@ type Binding interface {
 	Bind(*http.Request, interface{}) error
 }
 
-// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
-// but it reads the body from supplied bytes instead of req.Body.
-type BindingBody interface {
-	Binding
-	BindBody([]byte, interface{}) error
-}
-
-// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
-// but it read the Params.
-type BindingUri interface {
-	Name() string
-	BindUri(map[string][]string, interface{}) error
-}
-
-// StructValidator is the minimal interface which needs to be implemented in
-// order for it to be used as the validator engine for ensuring the correctness
-// of the request. Gin provides a default implementation for this using
-// https://github.com/go-playground/validator/tree/v8.18.2.
-type StructValidator interface {
-	// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
-	// If the received type is not a struct, any validation should be skipped and nil must be returned.
-	// If the received type is a struct or pointer to a struct, the validation should be performed.
-	// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
-	// Otherwise nil must be returned.
-	ValidateStruct(interface{}) error
-
-	// Engine returns the underlying validator engine which powers the
-	// StructValidator implementation.
-	Engine() interface{}
-}
-
-// Validator is the default validator which implements the StructValidator
-// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2
-// under the hood.
-//var Validator StructValidator = &defaultValidator{}
-
 // These implement the Binding interface and can be used to bind the data
 // present in the request to struct instances.
 var (
@@ -77,13 +43,14 @@ var (
 	ProtoBuf      = protobufBinding{}
 	MsgPack       = msgpackBinding{}
 	YAML          = yamlBinding{}
-	Uri           = uriBinding{}
+	URI           = uriBinding{}
+	Header        = headerBinding{}
 )
 
 // Default returns the appropriate Binding instance based on the HTTP method
 // and the content type.
 func Default(method, contentType string) Binding {
-	if method == "GET" {
+	if method == http.MethodGet {
 		return Form
 	}
 
@@ -107,49 +74,4 @@ func Default(method, contentType string) Binding {
 
 func validate(obj interface{}) error {
 	return nil
-	/*
-		if Validator == nil {
-			return nil
-		}
-		return Validator.ValidateStruct(obj)
-		// */
-}
-
-// Bind checks the Content-Type to select a binding engine automatically,
-// Depending the "Content-Type" header different bindings are used:
-//     "application/json" --> JSON binding
-//     "application/xml"  --> XML binding
-// otherwise --> returns an error.
-// It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input.
-// It decodes the json payload into the struct specified as a pointer.
-// It writes a 400 error and sets Content-Type header "text/plain" in the response if input is not valid.
-func Bind(req *http.Request, obj interface{}) error {
-	b := Default(req.Method, ContentType(req))
-	return MustBindWith(req, obj, b)
-}
-
-// BindQuery query to struct
-func BindQuery(req *http.Request, obj interface{}) error {
-	return MustBindWith(req, obj, Form)
-}
-
-// MustBindWith binds the passed struct pointer using the specified binding engine.
-// It will abort the request with HTTP 400 if any error occurs.
-// See the binding package.
-func MustBindWith(req *http.Request, obj interface{}, b Binding) (err error) {
-	return b.Bind(req, obj)
-}
-
-// ContentType returns the Content-Type header of the request.
-func ContentType(req *http.Request) string {
-	return filterFlags(req.Header.Get("Content-Type"))
-}
-
-func filterFlags(content string) string {
-	for i, char := range content {
-		if char == ' ' || char == ';' {
-			return content[:i]
-		}
-	}
-	return content
 }

+ 19 - 0
binding/bytesconv.go

@@ -0,0 +1,19 @@
+package binding
+
+import (
+	"reflect"
+	"unsafe"
+)
+
+// StringToBytes converts string to byte slice without a memory allocation.
+func StringToBytes(s string) (b []byte) {
+	sh := *(*reflect.StringHeader)(unsafe.Pointer(&s))
+	bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+	bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len
+	return b
+}
+
+// BytesToString converts byte slice to string without a memory allocation.
+func BytesToString(b []byte) string {
+	return *(*string)(unsafe.Pointer(&b))
+}

+ 0 - 48
binding/default_validator.go

@@ -1,48 +0,0 @@
-// Copyright 2017 Manu Martinez-Almeida.  All rights reserved.
-// Use of this source code is governed by a MIT style
-// license that can be found in the LICENSE file.
-
-package binding
-
-//"gopkg.in/go-playground/validator.v8"
-
-/*
-type defaultValidator struct {
-	once     sync.Once
-	validate *validator.Validate
-}
-
-var _ StructValidator = &defaultValidator{}
-
-// ValidateStruct receives any kind of type, but only performed struct or pointer to struct type.
-func (v *defaultValidator) ValidateStruct(obj interface{}) error {
-	value := reflect.ValueOf(obj)
-	valueType := value.Kind()
-	if valueType == reflect.Ptr {
-		valueType = value.Elem().Kind()
-	}
-	if valueType == reflect.Struct {
-		v.lazyinit()
-		if err := v.validate.Struct(obj); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-// Engine returns the underlying validator engine which powers the default
-// Validator instance. This is useful if you want to register custom validations
-// or struct level validations. See validator GoDoc for more info -
-// https://godoc.org/gopkg.in/go-playground/validator.v8
-func (v *defaultValidator) Engine() interface{} {
-	v.lazyinit()
-	return v.validate
-}
-
-func (v *defaultValidator) lazyinit() {
-	v.once.Do(func() {
-		config := &validator.Config{TagName: "binding"}
-		v.validate = validator.New(config)
-	})
-}
-// */

+ 1 - 27
binding/form.go

@@ -5,12 +5,10 @@
 package binding
 
 import (
-	"mime/multipart"
 	"net/http"
-	"reflect"
 )
 
-const defaultMemory = 32 * 1024 * 1024
+const defaultMemory = 32 << 20
 
 type formBinding struct{}
 type formPostBinding struct{}
@@ -63,27 +61,3 @@ func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error {
 
 	return validate(obj)
 }
-
-type multipartRequest http.Request
-
-var _ setter = (*multipartRequest)(nil)
-
-var (
-	multipartFileHeaderStructType = reflect.TypeOf(multipart.FileHeader{})
-)
-
-// TrySet tries to set a value by the multipart request with the binding a form file
-func (r *multipartRequest) TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSetted bool, err error) {
-	if value.Type() == multipartFileHeaderStructType {
-		_, file, err := (*http.Request)(r).FormFile(key)
-		if err != nil {
-			return false, err
-		}
-		if file != nil {
-			value.Set(reflect.ValueOf(*file))
-			return true, nil
-		}
-	}
-
-	return setByForm(value, field, r.MultipartForm.Value, key, opt)
-}

+ 37 - 18
binding/form_mapping.go

@@ -12,12 +12,11 @@ import (
 	"strconv"
 	"strings"
 	"time"
-	//"github.com/gin-gonic/gin/internal/json"
 )
 
-var errUnknownType = errors.New("Unknown type")
+var errUnknownType = errors.New("unknown type")
 
-func mapUri(ptr interface{}, m map[string][]string) error {
+func mapURI(ptr interface{}, m map[string][]string) error {
 	return mapFormByTag(ptr, m, "uri")
 }
 
@@ -51,6 +50,10 @@ func mappingByPtr(ptr interface{}, setter setter, tag string) error {
 }
 
 func mapping(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) {
+	if field.Tag.Get(tag) == "-" { // just ignoring this field
+		return false, nil
+	}
+
 	var vKind = value.Kind()
 
 	if vKind == reflect.Ptr {
@@ -70,12 +73,14 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag
 		return isSetted, nil
 	}
 
-	ok, err := tryToSetValue(value, field, setter, tag)
-	if err != nil {
-		return false, err
-	}
-	if ok {
-		return true, nil
+	if vKind != reflect.Struct || !field.Anonymous {
+		ok, err := tryToSetValue(value, field, setter, tag)
+		if err != nil {
+			return false, err
+		}
+		if ok {
+			return true, nil
+		}
 	}
 
 	if vKind == reflect.Struct {
@@ -83,7 +88,8 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag
 
 		var isSetted bool
 		for i := 0; i < value.NumField(); i++ {
-			if !value.Field(i).CanSet() {
+			sf := tValue.Field(i)
+			if sf.PkgPath != "" && !sf.Anonymous { // unexported
 				continue
 			}
 			ok, err := mapping(value.Field(i), tValue.Field(i), setter, tag)
@@ -109,9 +115,6 @@ func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter
 	tagValue = field.Tag.Get(tag)
 	tagValue, opts := head(tagValue, ",")
 
-	if tagValue == "-" { // just ignoring this field
-		return false, nil
-	}
 	if tagValue == "" { // default value is FieldName
 		tagValue = field.Name
 	}
@@ -123,9 +126,7 @@ func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter
 	for len(opts) > 0 {
 		opt, opts = head(opts, ",")
 
-		k, v := head(opt, "=")
-		switch k {
-		case "default":
+		if k, v := head(opt, "="); k == "default" {
 			setOpt.isDefaultExists = true
 			setOpt.defaultValue = v
 		}
@@ -206,9 +207,9 @@ func setWithProperType(val string, value reflect.Value, field reflect.StructFiel
 		case time.Time:
 			return setTimeField(val, field, value)
 		}
-		return json.Unmarshal([]byte(val), value.Addr().Interface())
+		return json.Unmarshal(StringToBytes(val), value.Addr().Interface())
 	case reflect.Map:
-		return json.Unmarshal([]byte(val), value.Addr().Interface())
+		return json.Unmarshal(StringToBytes(val), value.Addr().Interface())
 	default:
 		return errUnknownType
 	}
@@ -265,6 +266,24 @@ func setTimeField(val string, structField reflect.StructField, value reflect.Val
 		timeFormat = time.RFC3339
 	}
 
+	switch tf := strings.ToLower(timeFormat); tf {
+	case "unix", "unixnano":
+		tv, err := strconv.ParseInt(val, 10, 0)
+		if err != nil {
+			return err
+		}
+
+		d := time.Duration(1)
+		if tf == "unixnano" {
+			d = time.Second
+		}
+
+		t := time.Unix(tv/int64(d), tv%int64(d))
+		value.Set(reflect.ValueOf(t))
+		return nil
+
+	}
+
 	if val == "" {
 		value.Set(reflect.ValueOf(time.Time{}))
 		return nil

+ 34 - 0
binding/header.go

@@ -0,0 +1,34 @@
+package binding
+
+import (
+	"net/http"
+	"net/textproto"
+	"reflect"
+)
+
+type headerBinding struct{}
+
+func (headerBinding) Name() string {
+	return "header"
+}
+
+func (headerBinding) Bind(req *http.Request, obj interface{}) error {
+
+	if err := mapHeader(obj, req.Header); err != nil {
+		return err
+	}
+
+	return validate(obj)
+}
+
+func mapHeader(ptr interface{}, h map[string][]string) error {
+	return mappingByPtr(ptr, headerSource(h), "header")
+}
+
+type headerSource map[string][]string
+
+var _ setter = headerSource(nil)
+
+func (hs headerSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSetted bool, err error) {
+	return setByForm(value, field, hs, textproto.CanonicalMIMEHeaderKey(tagValue), opt)
+}

+ 10 - 3
binding/json.go

@@ -6,12 +6,10 @@ package binding
 
 import (
 	"bytes"
+	"encoding/json"
 	"fmt"
 	"io"
 	"net/http"
-
-	"encoding/json"
-	//"github.com/gin-gonic/gin/internal/json"
 )
 
 // EnableDecoderUseNumber is used to call the UseNumber method on the JSON
@@ -19,6 +17,12 @@ import (
 // interface{} as a Number instead of as a float64.
 var EnableDecoderUseNumber = false
 
+// EnableDecoderDisallowUnknownFields is used to call the DisallowUnknownFields method
+// on the JSON Decoder instance. DisallowUnknownFields causes the Decoder to
+// return an error when the destination is a struct and the input contains object
+// keys which do not match any non-ignored, exported fields in the destination.
+var EnableDecoderDisallowUnknownFields = false
+
 type jsonBinding struct{}
 
 func (jsonBinding) Name() string {
@@ -41,6 +45,9 @@ func decodeJSON(r io.Reader, obj interface{}) error {
 	if EnableDecoderUseNumber {
 		decoder.UseNumber()
 	}
+	if EnableDecoderDisallowUnknownFields {
+		decoder.DisallowUnknownFields()
+	}
 	if err := decoder.Decode(obj); err != nil {
 		return err
 	}

+ 2 - 0
binding/msgpack.go

@@ -2,6 +2,8 @@
 // Use of this source code is governed by a MIT style
 // license that can be found in the LICENSE file.
 
+// +build !nomsgpack
+
 package binding
 
 import (

+ 66 - 0
binding/multipart_form_mapping.go

@@ -0,0 +1,66 @@
+// Copyright 2019 Gin Core Team.  All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+	"errors"
+	"mime/multipart"
+	"net/http"
+	"reflect"
+)
+
+type multipartRequest http.Request
+
+var _ setter = (*multipartRequest)(nil)
+
+// TrySet tries to set a value by the multipart request with the binding a form file
+func (r *multipartRequest) TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSetted bool, err error) {
+	if files := r.MultipartForm.File[key]; len(files) != 0 {
+		return setByMultipartFormFile(value, field, files)
+	}
+
+	return setByForm(value, field, r.MultipartForm.Value, key, opt)
+}
+
+func setByMultipartFormFile(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSetted bool, err error) {
+	switch value.Kind() {
+	case reflect.Ptr:
+		switch value.Interface().(type) {
+		case *multipart.FileHeader:
+			value.Set(reflect.ValueOf(files[0]))
+			return true, nil
+		}
+	case reflect.Struct:
+		switch value.Interface().(type) {
+		case multipart.FileHeader:
+			value.Set(reflect.ValueOf(*files[0]))
+			return true, nil
+		}
+	case reflect.Slice:
+		slice := reflect.MakeSlice(value.Type(), len(files), len(files))
+		isSetted, err = setArrayOfMultipartFormFiles(slice, field, files)
+		if err != nil || !isSetted {
+			return isSetted, err
+		}
+		value.Set(slice)
+		return true, nil
+	case reflect.Array:
+		return setArrayOfMultipartFormFiles(value, field, files)
+	}
+	return false, errors.New("unsupported field type for multipart.FileHeader")
+}
+
+func setArrayOfMultipartFormFiles(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSetted bool, err error) {
+	if value.Len() != len(files) {
+		return false, errors.New("unsupported len of array for []*multipart.FileHeader")
+	}
+	for i := range files {
+		setted, err := setByMultipartFormFile(value.Index(i), field, files[i:i+1])
+		if err != nil || !setted {
+			return setted, err
+		}
+	}
+	return true, nil
+}

+ 2 - 2
binding/uri.go

@@ -10,8 +10,8 @@ func (uriBinding) Name() string {
 	return "uri"
 }
 
-func (uriBinding) BindUri(m map[string][]string, obj interface{}) error {
-	if err := mapUri(obj, m); err != nil {
+func (uriBinding) BindURI(m map[string][]string, obj interface{}) error {
+	if err := mapURI(obj, m); err != nil {
 		return err
 	}
 	return validate(obj)

+ 4 - 3
go.mod

@@ -7,8 +7,9 @@ require (
 	github.com/go-sql-driver/mysql v1.5.0
 	github.com/golang/protobuf v1.3.5
 	github.com/jmoiron/sqlx v1.2.0
-	github.com/lib/pq v1.3.0
-	github.com/ugorji/go v1.1.4
+	github.com/lib/pq v1.5.2
+	github.com/ugorji/go v1.1.7
+	github.com/ugorji/go/codec v1.1.7
 	golang.org/x/net v0.0.0-20190923162816-aa69164e4478
-	gopkg.in/yaml.v2 v2.2.4
+	gopkg.in/yaml.v2 v2.2.8
 )

+ 29 - 0
go.sum

@@ -1,5 +1,14 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
+github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
 github.com/go-redis/redis/v7 v7.2.0 h1:CrCexy/jYWZjW0AyVoHlcJUeZN19VWlbepTh1Vq6dJs=
 github.com/go-redis/redis/v7 v7.2.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
 github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk=
@@ -8,30 +17,46 @@ github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gG
 github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
 github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
 github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
 github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
+github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
 github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
 github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
 github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.5.2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
 github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
 github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
 github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw=
 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
+github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
+github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
+github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
+github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
@@ -41,6 +66,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
 golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
@@ -55,5 +81,8 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=