11 changed files with 789 additions and 1 deletions
			
			
		- 
					2cos.go
- 
					7vendor/go-httpheader/.bumpversion.cfg
- 
					27vendor/go-httpheader/.gitignore
- 
					25vendor/go-httpheader/.travis.yml
- 
					10vendor/go-httpheader/CHANGELOG.md
- 
					21vendor/go-httpheader/LICENSE
- 
					15vendor/go-httpheader/Makefile
- 
					59vendor/go-httpheader/README.md
- 
					290vendor/go-httpheader/encode.go
- 
					282vendor/go-httpheader/encode_test.go
- 
					52vendor/go-httpheader/example_test.go
| @ -0,0 +1,7 @@ | |||
| [bumpversion] | |||
| commit = True | |||
| tag = True | |||
| current_version = 0.2.0 | |||
| 
 | |||
| [bumpversion:file:encode.go] | |||
| 
 | |||
| @ -0,0 +1,27 @@ | |||
| # Compiled Object files, Static and Dynamic libs (Shared Objects) | |||
| *.o | |||
| *.a | |||
| *.so | |||
| 
 | |||
| # Folders | |||
| _obj | |||
| _test | |||
| 
 | |||
| # Architecture specific extensions/prefixes | |||
| *.[568vq] | |||
| [568vq].out | |||
| 
 | |||
| *.cgo1.go | |||
| *.cgo2.c | |||
| _cgo_defun.c | |||
| _cgo_gotypes.go | |||
| _cgo_export.* | |||
| 
 | |||
| _testmain.go | |||
| 
 | |||
| *.exe | |||
| *.test | |||
| *.prof | |||
| dist/ | |||
| cover.html | |||
| cover.out | |||
| @ -0,0 +1,25 @@ | |||
| language: go | |||
| go: | |||
|   - 1.6 | |||
|   - 1.7 | |||
|   - 1.8 | |||
|   - tip | |||
| 
 | |||
| sudo: false | |||
| 
 | |||
| before_install: | |||
|   - go get github.com/mattn/goveralls | |||
| 
 | |||
| install: | |||
|   - go get | |||
|   - go build | |||
| 
 | |||
| script: | |||
|   - make test | |||
|   - $HOME/gopath/bin/goveralls -service=travis-ci -ignore=vendor/ | |||
| 
 | |||
| matrix: | |||
|   allow_failures: | |||
|     - go: 1.6 | |||
|     - go: 1.7 | |||
|     - go: tip | |||
| @ -0,0 +1,10 @@ | |||
| # Changelog | |||
| 
 | |||
| ## 0.2.0 (2017-06-24) | |||
| 
 | |||
| * support http.Header field. | |||
| 
 | |||
| 
 | |||
| ## 0.1.0 (2017-06-10) | |||
| 
 | |||
| * Initial Release | |||
| @ -0,0 +1,21 @@ | |||
| MIT License | |||
| 
 | |||
| Copyright (c) 2017 mozillazg | |||
| 
 | |||
| 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. | |||
| @ -0,0 +1,15 @@ | |||
| help: | |||
| 	@echo "test             run test" | |||
| 	@echo "lint             run lint" | |||
| 
 | |||
| .PHONY: test | |||
| test: | |||
| 	go test -v -cover -coverprofile cover.out | |||
| 	go tool cover -html=cover.out -o cover.html | |||
| 
 | |||
| .PHONY: lint | |||
| lint: | |||
| 	gofmt -s -w . | |||
| 	goimports -w . | |||
| 	golint . | |||
| 	go vet | |||
| @ -0,0 +1,59 @@ | |||
| # go-httpheader | |||
| 
 | |||
| go-httpheader is a Go library for encoding structs into Header fields. | |||
| 
 | |||
| 
 | |||
| ## install | |||
| 
 | |||
| `go get -u github.com/tencentyun/go-httpheader` | |||
| 
 | |||
| 
 | |||
| ## usage | |||
| 
 | |||
| ```go | |||
| package main | |||
| 
 | |||
| import ( | |||
| 	"fmt" | |||
| 	"net/http" | |||
| 
 | |||
| 	"github.com/tencentyun/go-httpheader" | |||
| ) | |||
| 
 | |||
| type Options struct { | |||
| 	hide         string | |||
| 	ContentType  string `header:"Content-Type"` | |||
| 	Length       int | |||
| 	XArray       []string `header:"X-Array"` | |||
| 	TestHide     string   `header:"-"` | |||
| 	IgnoreEmpty  string   `header:"X-Empty,omitempty"` | |||
| 	IgnoreEmptyN string   `header:"X-Empty-N,omitempty"` | |||
| 	CustomHeader http.Header | |||
| } | |||
| 
 | |||
| func main() { | |||
| 	opt := Options{ | |||
| 		hide:         "hide", | |||
| 		ContentType:  "application/json", | |||
| 		Length:       2, | |||
| 		XArray:       []string{"test1", "test2"}, | |||
| 		TestHide:     "hide", | |||
| 		IgnoreEmptyN: "n", | |||
| 		CustomHeader: http.Header{ | |||
| 			"X-Test-1": []string{"233"}, | |||
| 			"X-Test-2": []string{"666"}, | |||
| 		}, | |||
| 	} | |||
| 	h, _ := httpheader.Header(opt) | |||
| 	fmt.Printf("%#v", h) | |||
| 	// h: | |||
| 	// http.Header{ | |||
| 	//	"X-Test-1":     []string{"233"}, | |||
| 	//	"X-Test-2":     []string{"666"}, | |||
| 	//	"Content-Type": []string{"application/json"}, | |||
| 	//	"Length":       []string{"2"}, | |||
| 	//	"X-Array":      []string{"test1", "test2"}, | |||
| 	//	"X-Empty-N":    []string{"n"}, | |||
| 	//} | |||
| } | |||
| ``` | |||
| @ -0,0 +1,290 @@ | |||
| // Package query implements encoding of structs into http.Header fields.
 | |||
| //
 | |||
| // As a simple example:
 | |||
| //
 | |||
| // 	type Options struct {
 | |||
| // 		ContentType  string `header:"Content-Type"`
 | |||
| // 		Length       int
 | |||
| // 	}
 | |||
| //
 | |||
| // 	opt := Options{"application/json", 2}
 | |||
| // 	h, _ := httpheader.Header(opt)
 | |||
| // 	fmt.Printf("%#v", h)
 | |||
| // 	// will output:
 | |||
| // 	// http.Header{"Content-Type":[]string{"application/json"},"Length":[]string{"2"}}
 | |||
| //
 | |||
| // The exact mapping between Go values and http.Header is described in the
 | |||
| // documentation for the Header() function.
 | |||
| package httpheader | |||
| 
 | |||
| import ( | |||
| 	"fmt" | |||
| 	"net/http" | |||
| 	"reflect" | |||
| 	"strconv" | |||
| 	"strings" | |||
| 	"time" | |||
| ) | |||
| 
 | |||
| const tagName = "header" | |||
| 
 | |||
| // Version ...
 | |||
| const Version = "0.2.0" | |||
| 
 | |||
| var timeType = reflect.TypeOf(time.Time{}) | |||
| var headerType = reflect.TypeOf(http.Header{}) | |||
| 
 | |||
| var encoderType = reflect.TypeOf(new(Encoder)).Elem() | |||
| 
 | |||
| // Encoder is an interface implemented by any type that wishes to encode
 | |||
| // itself into Header fields in a non-standard way.
 | |||
| type Encoder interface { | |||
| 	EncodeHeader(key string, v *http.Header) error | |||
| } | |||
| 
 | |||
| // Header returns the http.Header encoding of v.
 | |||
| //
 | |||
| // Header expects to be passed a struct, and traverses it recursively using the
 | |||
| // following encoding rules.
 | |||
| //
 | |||
| // Each exported struct field is encoded as a Header field unless
 | |||
| //
 | |||
| //	- the field's tag is "-", or
 | |||
| //	- the field is empty and its tag specifies the "omitempty" option
 | |||
| //
 | |||
| // The empty values are false, 0, any nil pointer or interface value, any array
 | |||
| // slice, map, or string of length zero, and any time.Time that returns true
 | |||
| // for IsZero().
 | |||
| //
 | |||
| // The Header field name defaults to the struct field name but can be
 | |||
| // specified in the struct field's tag value.  The "header" key in the struct
 | |||
| // field's tag value is the key name, followed by an optional comma and
 | |||
| // options.  For example:
 | |||
| //
 | |||
| // 	// Field is ignored by this package.
 | |||
| // 	Field int `header:"-"`
 | |||
| //
 | |||
| // 	// Field appears as Header field "X-Name".
 | |||
| // 	Field int `header:"X-Name"`
 | |||
| //
 | |||
| // 	// Field appears as Header field "X-Name" and the field is omitted if
 | |||
| // 	// its value is empty
 | |||
| // 	Field int `header:"X-Name,omitempty"`
 | |||
| //
 | |||
| // 	// Field appears as Header field "Field" (the default), but the field
 | |||
| // 	// is skipped if empty.  Note the leading comma.
 | |||
| // 	Field int `header:",omitempty"`
 | |||
| //
 | |||
| // For encoding individual field values, the following type-dependent rules
 | |||
| // apply:
 | |||
| //
 | |||
| // Boolean values default to encoding as the strings "true" or "false".
 | |||
| // Including the "int" option signals that the field should be encoded as the
 | |||
| // strings "1" or "0".
 | |||
| //
 | |||
| // time.Time values default to encoding as RFC1123("Mon, 02 Jan 2006 15:04:05 GMT")
 | |||
| // timestamps. Including the "unix" option signals that the field should be
 | |||
| // encoded as a Unix time (see time.Unix())
 | |||
| //
 | |||
| // Slice and Array values default to encoding as multiple Header values of the
 | |||
| // same name. example:
 | |||
| // X-Name: []string{"Tom", "Jim"}, etc.
 | |||
| //
 | |||
| // http.Header values will be used to extend the Header fields.
 | |||
| //
 | |||
| // Anonymous struct fields are usually encoded as if their inner exported
 | |||
| // fields were fields in the outer struct, subject to the standard Go
 | |||
| // visibility rules. An anonymous struct field with a name given in its Header
 | |||
| // tag is treated as having that name, rather than being anonymous.
 | |||
| //
 | |||
| // Non-nil pointer values are encoded as the value pointed to.
 | |||
| //
 | |||
| // All other values are encoded using their default string representation.
 | |||
| //
 | |||
| // Multiple fields that encode to the same Header filed name will be included
 | |||
| // as multiple Header values of the same name.
 | |||
| func Header(v interface{}) (http.Header, error) { | |||
| 	h := make(http.Header) | |||
| 	val := reflect.ValueOf(v) | |||
| 	for val.Kind() == reflect.Ptr { | |||
| 		if val.IsNil() { | |||
| 			return h, nil | |||
| 		} | |||
| 		val = val.Elem() | |||
| 	} | |||
| 
 | |||
| 	if v == nil { | |||
| 		return h, nil | |||
| 	} | |||
| 
 | |||
| 	if val.Kind() != reflect.Struct { | |||
| 		return nil, fmt.Errorf("httpheader: Header() expects struct input. Got %v", val.Kind()) | |||
| 	} | |||
| 
 | |||
| 	err := reflectValue(h, val) | |||
| 	return h, err | |||
| } | |||
| 
 | |||
| // reflectValue populates the header fields from the struct fields in val.
 | |||
| // Embedded structs are followed recursively (using the rules defined in the
 | |||
| // Values function documentation) breadth-first.
 | |||
| func reflectValue(header http.Header, val reflect.Value) error { | |||
| 	var embedded []reflect.Value | |||
| 
 | |||
| 	typ := val.Type() | |||
| 	for i := 0; i < typ.NumField(); i++ { | |||
| 		sf := typ.Field(i) | |||
| 		if sf.PkgPath != "" && !sf.Anonymous { // unexported
 | |||
| 			continue | |||
| 		} | |||
| 
 | |||
| 		sv := val.Field(i) | |||
| 		tag := sf.Tag.Get(tagName) | |||
| 		if tag == "-" { | |||
| 			continue | |||
| 		} | |||
| 		name, opts := parseTag(tag) | |||
| 		if name == "" { | |||
| 			if sf.Anonymous && sv.Kind() == reflect.Struct { | |||
| 				// save embedded struct for later processing
 | |||
| 				embedded = append(embedded, sv) | |||
| 				continue | |||
| 			} | |||
| 
 | |||
| 			name = sf.Name | |||
| 		} | |||
| 
 | |||
| 		if opts.Contains("omitempty") && isEmptyValue(sv) { | |||
| 			continue | |||
| 		} | |||
| 
 | |||
| 		if sv.Type().Implements(encoderType) { | |||
| 			if !reflect.Indirect(sv).IsValid() { | |||
| 				sv = reflect.New(sv.Type().Elem()) | |||
| 			} | |||
| 
 | |||
| 			m := sv.Interface().(Encoder) | |||
| 			if err := m.EncodeHeader(name, &header); err != nil { | |||
| 				return err | |||
| 			} | |||
| 			continue | |||
| 		} | |||
| 
 | |||
| 		if sv.Kind() == reflect.Slice || sv.Kind() == reflect.Array { | |||
| 			for i := 0; i < sv.Len(); i++ { | |||
| 				k := name | |||
| 				header.Add(k, valueString(sv.Index(i), opts)) | |||
| 			} | |||
| 			continue | |||
| 		} | |||
| 
 | |||
| 		for sv.Kind() == reflect.Ptr { | |||
| 			if sv.IsNil() { | |||
| 				break | |||
| 			} | |||
| 			sv = sv.Elem() | |||
| 		} | |||
| 
 | |||
| 		if sv.Type() == timeType { | |||
| 			header.Add(name, valueString(sv, opts)) | |||
| 			continue | |||
| 		} | |||
| 		if sv.Type() == headerType { | |||
| 			h := sv.Interface().(http.Header) | |||
| 			for k, vs := range h { | |||
| 				for _, v := range vs { | |||
| 					header.Add(k, v) | |||
| 				} | |||
| 			} | |||
| 			continue | |||
| 		} | |||
| 
 | |||
| 		if sv.Kind() == reflect.Struct { | |||
| 			reflectValue(header, sv) | |||
| 			continue | |||
| 		} | |||
| 
 | |||
| 		header.Add(name, valueString(sv, opts)) | |||
| 	} | |||
| 
 | |||
| 	for _, f := range embedded { | |||
| 		if err := reflectValue(header, f); err != nil { | |||
| 			return err | |||
| 		} | |||
| 	} | |||
| 
 | |||
| 	return nil | |||
| } | |||
| 
 | |||
| // valueString returns the string representation of a value.
 | |||
| func valueString(v reflect.Value, opts tagOptions) string { | |||
| 	for v.Kind() == reflect.Ptr { | |||
| 		if v.IsNil() { | |||
| 			return "" | |||
| 		} | |||
| 		v = v.Elem() | |||
| 	} | |||
| 
 | |||
| 	if v.Kind() == reflect.Bool && opts.Contains("int") { | |||
| 		if v.Bool() { | |||
| 			return "1" | |||
| 		} | |||
| 		return "0" | |||
| 	} | |||
| 
 | |||
| 	if v.Type() == timeType { | |||
| 		t := v.Interface().(time.Time) | |||
| 		if opts.Contains("unix") { | |||
| 			return strconv.FormatInt(t.Unix(), 10) | |||
| 		} | |||
| 		return t.Format(http.TimeFormat) | |||
| 	} | |||
| 
 | |||
| 	return fmt.Sprint(v.Interface()) | |||
| } | |||
| 
 | |||
| // isEmptyValue checks if a value should be considered empty for the purposes
 | |||
| // of omitting fields with the "omitempty" option.
 | |||
| func isEmptyValue(v reflect.Value) bool { | |||
| 	switch v.Kind() { | |||
| 	case reflect.Array, reflect.Map, reflect.Slice, reflect.String: | |||
| 		return v.Len() == 0 | |||
| 	case reflect.Bool: | |||
| 		return !v.Bool() | |||
| 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |||
| 		return v.Int() == 0 | |||
| 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | |||
| 		return v.Uint() == 0 | |||
| 	case reflect.Float32, reflect.Float64: | |||
| 		return v.Float() == 0 | |||
| 	case reflect.Interface, reflect.Ptr: | |||
| 		return v.IsNil() | |||
| 	} | |||
| 
 | |||
| 	if v.Type() == timeType { | |||
| 		return v.Interface().(time.Time).IsZero() | |||
| 	} | |||
| 
 | |||
| 	return false | |||
| } | |||
| 
 | |||
| // tagOptions is the string following a comma in a struct field's "header" tag, or
 | |||
| // the empty string. It does not include the leading comma.
 | |||
| type tagOptions []string | |||
| 
 | |||
| // parseTag splits a struct field's header tag into its name and comma-separated
 | |||
| // options.
 | |||
| func parseTag(tag string) (string, tagOptions) { | |||
| 	s := strings.Split(tag, ",") | |||
| 	return s[0], s[1:] | |||
| } | |||
| 
 | |||
| // Contains checks whether the tagOptions contains the specified option.
 | |||
| func (o tagOptions) Contains(option string) bool { | |||
| 	for _, s := range o { | |||
| 		if s == option { | |||
| 			return true | |||
| 		} | |||
| 	} | |||
| 	return false | |||
| } | |||
| @ -0,0 +1,282 @@ | |||
| package httpheader | |||
| 
 | |||
| import ( | |||
| 	"fmt" | |||
| 	"net/http" | |||
| 	"reflect" | |||
| 	"testing" | |||
| 	"time" | |||
| ) | |||
| 
 | |||
| func TestHeader_types(t *testing.T) { | |||
| 	str := "string" | |||
| 	strPtr := &str | |||
| 	timeVal := time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC) | |||
| 
 | |||
| 	tests := []struct { | |||
| 		in   interface{} | |||
| 		want http.Header | |||
| 	}{ | |||
| 		{ | |||
| 			// basic primitives
 | |||
| 			struct { | |||
| 				A string | |||
| 				B int | |||
| 				C uint | |||
| 				D float32 | |||
| 				E bool | |||
| 			}{}, | |||
| 			http.Header{ | |||
| 				"A": []string{""}, | |||
| 				"B": []string{"0"}, | |||
| 				"C": []string{"0"}, | |||
| 				"D": []string{"0"}, | |||
| 				"E": []string{"false"}, | |||
| 			}, | |||
| 		}, | |||
| 		{ | |||
| 			// pointers
 | |||
| 			struct { | |||
| 				A *string | |||
| 				B *int | |||
| 				C **string | |||
| 				D *time.Time | |||
| 			}{ | |||
| 				A: strPtr, | |||
| 				C: &strPtr, | |||
| 				D: &timeVal, | |||
| 			}, | |||
| 			http.Header{ | |||
| 				"A": []string{str}, | |||
| 				"B": []string{""}, | |||
| 				"C": []string{str}, | |||
| 				"D": []string{"Sat, 01 Jan 2000 12:34:56 GMT"}, | |||
| 			}, | |||
| 		}, | |||
| 		{ | |||
| 			// slices and arrays
 | |||
| 			struct { | |||
| 				A []string | |||
| 				B []*string | |||
| 				C [2]string | |||
| 				D []bool `header:",int"` | |||
| 			}{ | |||
| 				A: []string{"a", "b"}, | |||
| 				B: []*string{&str, &str}, | |||
| 				C: [2]string{"a", "b"}, | |||
| 				D: []bool{true, false}, | |||
| 			}, | |||
| 			http.Header{ | |||
| 				"A": []string{"a", "b"}, | |||
| 				"B": {"string", "string"}, | |||
| 				"C": []string{"a", "b"}, | |||
| 				"D": {"1", "0"}, | |||
| 			}, | |||
| 		}, | |||
| 		{ | |||
| 			// other types
 | |||
| 			struct { | |||
| 				A time.Time | |||
| 				B time.Time `header:",unix"` | |||
| 				C bool      `header:",int"` | |||
| 				D bool      `header:",int"` | |||
| 				E http.Header | |||
| 			}{ | |||
| 				A: time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC), | |||
| 				B: time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC), | |||
| 				C: true, | |||
| 				D: false, | |||
| 				E: http.Header{ | |||
| 					"F": []string{"f1"}, | |||
| 					"G": []string{"gg"}, | |||
| 				}, | |||
| 			}, | |||
| 			http.Header{ | |||
| 				"A": []string{"Sat, 01 Jan 2000 12:34:56 GMT"}, | |||
| 				"B": []string{"946730096"}, | |||
| 				"C": []string{"1"}, | |||
| 				"D": []string{"0"}, | |||
| 				"F": []string{"f1"}, | |||
| 				"G": []string{"gg"}, | |||
| 			}, | |||
| 		}, | |||
| 		{ | |||
| 			nil, | |||
| 			http.Header{}, | |||
| 		}, | |||
| 		{ | |||
| 			&struct { | |||
| 				A string | |||
| 			}{"test"}, | |||
| 			http.Header{ | |||
| 				"A": []string{"test"}, | |||
| 			}, | |||
| 		}, | |||
| 	} | |||
| 
 | |||
| 	for i, tt := range tests { | |||
| 		v, err := Header(tt.in) | |||
| 		if err != nil { | |||
| 			t.Errorf("%d. Header(%q) returned error: %v", i, tt.in, err) | |||
| 		} | |||
| 
 | |||
| 		if !reflect.DeepEqual(tt.want, v) { | |||
| 			t.Errorf("%d. Header(%q) returned %#v, want %#v", i, tt.in, v, tt.want) | |||
| 		} | |||
| 	} | |||
| } | |||
| 
 | |||
| func TestHeader_omitEmpty(t *testing.T) { | |||
| 	str := "" | |||
| 	s := struct { | |||
| 		a string | |||
| 		A string | |||
| 		B string    `header:",omitempty"` | |||
| 		C string    `header:"-"` | |||
| 		D string    `header:"omitempty"` // actually named omitempty, not an option
 | |||
| 		E *string   `header:",omitempty"` | |||
| 		F bool      `header:",omitempty"` | |||
| 		G int       `header:",omitempty"` | |||
| 		H uint      `header:",omitempty"` | |||
| 		I float32   `header:",omitempty"` | |||
| 		J time.Time `header:",omitempty"` | |||
| 		K struct{}  `header:",omitempty"` | |||
| 	}{E: &str} | |||
| 
 | |||
| 	v, err := Header(s) | |||
| 	if err != nil { | |||
| 		t.Errorf("Header(%#v) returned error: %v", s, err) | |||
| 	} | |||
| 
 | |||
| 	want := http.Header{ | |||
| 		"A":         []string{""}, | |||
| 		"Omitempty": []string{""}, | |||
| 		"E":         []string{""}, // E is included because the pointer is not empty, even though the string being pointed to is
 | |||
| 	} | |||
| 	if !reflect.DeepEqual(want, v) { | |||
| 		t.Errorf("Header(%#v) returned %v, want %v", s, v, want) | |||
| 	} | |||
| } | |||
| 
 | |||
| type A struct { | |||
| 	B | |||
| } | |||
| 
 | |||
| type B struct { | |||
| 	C string | |||
| } | |||
| 
 | |||
| type D struct { | |||
| 	B | |||
| 	C string | |||
| } | |||
| 
 | |||
| type e struct { | |||
| 	B | |||
| 	C string | |||
| } | |||
| 
 | |||
| type F struct { | |||
| 	e | |||
| } | |||
| 
 | |||
| func TestHeader_embeddedStructs(t *testing.T) { | |||
| 	tests := []struct { | |||
| 		in   interface{} | |||
| 		want http.Header | |||
| 	}{ | |||
| 		{ | |||
| 			A{B{C: "foo"}}, | |||
| 			http.Header{"C": []string{"foo"}}, | |||
| 		}, | |||
| 		{ | |||
| 			D{B: B{C: "bar"}, C: "foo"}, | |||
| 			http.Header{"C": []string{"foo", "bar"}}, | |||
| 		}, | |||
| 		{ | |||
| 			F{e{B: B{C: "bar"}, C: "foo"}}, // With unexported embed
 | |||
| 			http.Header{"C": []string{"foo", "bar"}}, | |||
| 		}, | |||
| 	} | |||
| 
 | |||
| 	for i, tt := range tests { | |||
| 		v, err := Header(tt.in) | |||
| 		if err != nil { | |||
| 			t.Errorf("%d. Header(%q) returned error: %v", i, tt.in, err) | |||
| 		} | |||
| 
 | |||
| 		if !reflect.DeepEqual(tt.want, v) { | |||
| 			t.Errorf("%d. Header(%q) returned %v, want %v", i, tt.in, v, tt.want) | |||
| 		} | |||
| 	} | |||
| } | |||
| 
 | |||
| func TestHeader_invalidInput(t *testing.T) { | |||
| 	_, err := Header("") | |||
| 	if err == nil { | |||
| 		t.Errorf("expected Header() to return an error on invalid input") | |||
| 	} | |||
| } | |||
| 
 | |||
| type EncodedArgs []string | |||
| 
 | |||
| func (m EncodedArgs) EncodeHeader(key string, v *http.Header) error { | |||
| 	for i, arg := range m { | |||
| 		v.Set(fmt.Sprintf("%s.%d", key, i), arg) | |||
| 	} | |||
| 	return nil | |||
| } | |||
| 
 | |||
| func TestHeader_Marshaler(t *testing.T) { | |||
| 	s := struct { | |||
| 		Args EncodedArgs `header:"arg"` | |||
| 	}{[]string{"a", "b", "c"}} | |||
| 	v, err := Header(s) | |||
| 	if err != nil { | |||
| 		t.Errorf("Header(%q) returned error: %v", s, err) | |||
| 	} | |||
| 
 | |||
| 	want := http.Header{ | |||
| 		"Arg.0": []string{"a"}, | |||
| 		"Arg.1": []string{"b"}, | |||
| 		"Arg.2": []string{"c"}, | |||
| 	} | |||
| 	if !reflect.DeepEqual(want, v) { | |||
| 		t.Errorf("Header(%q) returned %v, want %v", s, v, want) | |||
| 	} | |||
| } | |||
| 
 | |||
| func TestHeader_MarshalerWithNilPointer(t *testing.T) { | |||
| 	s := struct { | |||
| 		Args *EncodedArgs `header:"arg"` | |||
| 	}{} | |||
| 	v, err := Header(s) | |||
| 	if err != nil { | |||
| 		t.Errorf("Header(%q) returned error: %v", s, err) | |||
| 	} | |||
| 
 | |||
| 	want := http.Header{} | |||
| 	if !reflect.DeepEqual(want, v) { | |||
| 		t.Errorf("Header(%q) returned %v, want %v", s, v, want) | |||
| 	} | |||
| } | |||
| 
 | |||
| func TestTagParsing(t *testing.T) { | |||
| 	name, opts := parseTag("field,foobar,foo") | |||
| 	if name != "field" { | |||
| 		t.Fatalf("name = %q, want field", name) | |||
| 	} | |||
| 	for _, tt := range []struct { | |||
| 		opt  string | |||
| 		want bool | |||
| 	}{ | |||
| 		{"foobar", true}, | |||
| 		{"foo", true}, | |||
| 		{"bar", false}, | |||
| 		{"field", false}, | |||
| 	} { | |||
| 		if opts.Contains(tt.opt) != tt.want { | |||
| 			t.Errorf("Contains(%q) = %v", tt.opt, !tt.want) | |||
| 		} | |||
| 	} | |||
| } | |||
| @ -0,0 +1,52 @@ | |||
| package httpheader_test | |||
| 
 | |||
| import ( | |||
| 	"fmt" | |||
| 	"net/http" | |||
| 
 | |||
| 	"github.com/tencentyun/go-httpheader" | |||
| ) | |||
| 
 | |||
| func ExampleHeader() { | |||
| 	type Options struct { | |||
| 		ContentType  string `header:"Content-Type"` | |||
| 		Length       int | |||
| 		XArray       []string `header:"X-Array"` | |||
| 		TestHide     string   `header:"-"` | |||
| 		IgnoreEmpty  string   `header:"X-Empty,omitempty"` | |||
| 		IgnoreEmptyN string   `header:"X-Empty-N,omitempty"` | |||
| 		CustomHeader http.Header | |||
| 	} | |||
| 
 | |||
| 	opt := Options{ | |||
| 		ContentType:  "application/json", | |||
| 		Length:       2, | |||
| 		XArray:       []string{"test1", "test2"}, | |||
| 		TestHide:     "hide", | |||
| 		IgnoreEmptyN: "n", | |||
| 		CustomHeader: http.Header{ | |||
| 			"X-Test-1": []string{"233"}, | |||
| 			"X-Test-2": []string{"666"}, | |||
| 		}, | |||
| 	} | |||
| 	h, _ := httpheader.Header(opt) | |||
| 	fmt.Println(h["Content-Type"]) | |||
| 	fmt.Println(h["Length"]) | |||
| 	fmt.Println(h["X-Array"]) | |||
| 	_, ok := h["TestHide"] | |||
| 	fmt.Println(ok) | |||
| 	_, ok = h["X-Empty"] | |||
| 	fmt.Println(ok) | |||
| 	fmt.Println(h["X-Empty-N"]) | |||
| 	fmt.Println(h["X-Test-1"]) | |||
| 	fmt.Println(h["X-Test-2"]) | |||
| 	// Output:
 | |||
| 	// [application/json]
 | |||
| 	// [2]
 | |||
| 	// [test1 test2]
 | |||
| 	// false
 | |||
| 	// false
 | |||
| 	// [n]
 | |||
| 	// [233]
 | |||
| 	// [666]
 | |||
| } | |||
						Write
						Preview
					
					
					Loading…
					
					Cancel
						Save
					
		Reference in new issue