fix deserialization panics on empty map

This commit is contained in:
yusing 2024-12-18 15:15:55 +08:00
parent 36069cbe6d
commit 654194b274
7 changed files with 123 additions and 28 deletions

View file

@ -18,11 +18,11 @@ func TestBaseWithSubject(t *testing.T) {
withSubjectf := err.Subjectf("%s %s", "foo", "bar")
ExpectError(t, err, withSubject)
ExpectStrEqual(t, withSubject.Error(), "foo: error")
ExpectEqual(t, withSubject.Error(), "foo: error")
ExpectTrue(t, withSubject.Is(err))
ExpectError(t, err, withSubjectf)
ExpectStrEqual(t, withSubjectf.Error(), "foo bar: error")
ExpectEqual(t, withSubjectf.Error(), "foo bar: error")
ExpectTrue(t, withSubjectf.Is(err))
}
@ -114,9 +114,9 @@ func TestErrorWith(t *testing.T) {
func TestErrorStringSimple(t *testing.T) {
errFailure := New("generic failure")
ne := errFailure.Subject("foo bar")
ExpectStrEqual(t, ne.Error(), "foo bar: generic failure")
ExpectEqual(t, ne.Error(), "foo bar: generic failure")
ne = ne.Subject("baz")
ExpectStrEqual(t, ne.Error(), "baz > foo bar: generic failure")
ExpectEqual(t, ne.Error(), "baz > foo bar: generic failure")
}
func TestErrorStringNested(t *testing.T) {
@ -153,5 +153,5 @@ func TestErrorStringNested(t *testing.T) {
action 3 > inner3: generic failure
3
3`
ExpectStrEqual(t, ne.Error(), want)
ExpectEqual(t, ne.Error(), want)
}

View file

@ -1,7 +1,6 @@
package err
import (
"errors"
"fmt"
)
@ -20,12 +19,16 @@ func Errorf(format string, args ...any) Error {
return &baseError{fmt.Errorf(format, args...)}
}
func From(err error) (e Error) {
func From(err error) Error {
if err == nil {
return nil
}
if errors.As(err, &e) {
return e
//nolint:errorlint
switch err := err.(type) {
case *baseError:
return err
case *nestedError:
return err
}
return &baseError{err}
}

View file

@ -0,0 +1,65 @@
example: # matching `example.y.z`
scheme: http
host: 10.0.0.254
port: 80
path_patterns: # Check https://pkg.go.dev/net/http#hdr-Patterns-ServeMux for syntax
- GET / # accept any GET request
- POST /auth # for /auth and /auth/* accept only POST
- GET /home/{$} # for exactly /home
healthcheck:
disabled: false
path: /
interval: 5s
load_balance:
link: app
mode: ip_hash
options:
header: X-Forwarded-For
middlewares:
cidr_whitelist:
allow:
- 127.0.0.1
- 10.0.0.0/8
status_code: 403
message: IP not allowed
hideXForwarded:
homepage:
name: Example App
icon: png/example.png
description: An example app
category: example
access_log:
buffer_size: 100
path: /var/log/example.log
filters:
status_codes:
values:
- 200-299
- 101
method:
values:
- GET
host:
values:
- example.y.z
headers:
negative: true
values:
- foo=bar
- baz
cidr:
values:
- 192.168.10.0/24
fields:
headers:
default: keep
config:
foo: redact
query:
default: drop
config:
foo: keep
cookies:
default: redact
config:
foo: keep

View file

@ -31,8 +31,12 @@ func FileProviderImpl(filename string) (ProviderImpl, error) {
return impl, nil
}
func validate(data []byte) (route.RawEntries, E.Error) {
return utils.DeserializeYAMLMap[*route.RawEntry](data)
}
func Validate(data []byte) (err E.Error) {
_, err = utils.DeserializeYAMLMap[*route.RawEntry](data)
_, err = validate(data)
return
}
@ -52,7 +56,7 @@ func (p *FileProvider) loadRoutesImpl() (route.Routes, E.Error) {
return routes, E.From(err)
}
entries, err := utils.DeserializeYAMLMap[*route.RawEntry](data)
entries, err := validate(data)
if err == nil {
return route.FromEntries(entries)
}

View file

@ -0,0 +1,17 @@
package provider
import (
"testing"
_ "embed"
. "github.com/yusing/go-proxy/internal/utils/testing"
)
//go:embed all_fields.yaml
var yaml []byte
func TestFile(t *testing.T) {
_, err := validate(yaml)
ExpectNoError(t, err)
}

View file

@ -277,6 +277,18 @@ func isIntFloat(t reflect.Kind) bool {
// Returns:
// - error: the error occurred during conversion, or nil if no error occurred.
func Convert(src reflect.Value, dst reflect.Value) E.Error {
if !dst.IsValid() {
return E.Errorf("convert: dst is %w", ErrNilValue)
}
if !src.IsValid() {
if dst.CanSet() {
dst.Set(reflect.Zero(dst.Type()))
return nil
}
return E.Errorf("convert: src is %w", ErrNilValue)
}
srcT := src.Type()
dstT := dst.Type()
@ -339,7 +351,7 @@ func Convert(src reflect.Value, dst reflect.Value) E.Error {
return nil
}
if dstT.Kind() != reflect.Slice {
return ErrUnsupportedConversion.Subject(dstT.String() + " to slice")
return ErrUnsupportedConversion.Subject(dstT.String() + " to " + srcT.String())
}
newSlice := reflect.MakeSlice(dstT, 0, src.Len())
i := 0
@ -469,11 +481,10 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr E.E
return true, errs.Error()
}
tmp = m
}
if tmp != nil {
return true, Convert(reflect.ValueOf(tmp), dst)
}
default:
return false, nil
}
return true, Convert(reflect.ValueOf(tmp), dst)
}
func DeserializeYAML[T any](data []byte, target T) E.Error {

View file

@ -23,7 +23,7 @@ func IgnoreError[Result any](r Result, _ error) Result {
func ExpectNoError(t *testing.T, err error) {
t.Helper()
if err != nil && !reflect.ValueOf(err).IsNil() {
t.Errorf("expected err=nil, got %v", err)
t.Errorf("expected err=nil, got %s", ansi.StripANSI(err.Error()))
t.FailNow()
}
}
@ -31,7 +31,7 @@ func ExpectNoError(t *testing.T, err error) {
func ExpectError(t *testing.T, expected error, err error) {
t.Helper()
if !errors.Is(err, expected) {
t.Errorf("expected err %s, got %v", expected, err)
t.Errorf("expected err %s, got %s", expected, ansi.StripANSI(err.Error()))
t.FailNow()
}
}
@ -39,7 +39,7 @@ func ExpectError(t *testing.T, expected error, err error) {
func ExpectError2(t *testing.T, input any, expected error, err error) {
t.Helper()
if !errors.Is(err, expected) {
t.Errorf("%v: expected err %s, got %v", input, expected, err)
t.Errorf("%v: expected err %s, got %s", input, expected, ansi.StripANSI(err.Error()))
t.FailNow()
}
}
@ -48,22 +48,17 @@ func ExpectErrorT[T error](t *testing.T, err error) {
t.Helper()
var errAs T
if !errors.As(err, &errAs) {
t.Errorf("expected err %T, got %v", errAs, err)
t.Errorf("expected err %T, got %s", errAs, ansi.StripANSI(err.Error()))
t.FailNow()
}
}
func ExpectEqual[T comparable](t *testing.T, got T, want T) {
t.Helper()
if got != want {
t.Errorf("expected:\n%v, got\n%v", want, got)
t.FailNow()
if gotStr, ok := any(got).(string); ok {
ExpectDeepEqual(t, ansi.StripANSI(gotStr), any(want).(string))
return
}
}
func ExpectStrEqual(t *testing.T, got string, want string) {
t.Helper()
got = ansi.StripANSI(got)
if got != want {
t.Errorf("expected:\n%v, got\n%v", want, got)
t.FailNow()