mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-20 04:42:33 +02:00
fix deserialization panics on empty map
This commit is contained in:
parent
36069cbe6d
commit
654194b274
7 changed files with 123 additions and 28 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
}
|
||||
|
|
65
internal/route/provider/all_fields.yaml
Normal file
65
internal/route/provider/all_fields.yaml
Normal 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
|
|
@ -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)
|
||||
}
|
||||
|
|
17
internal/route/provider/file_test.go
Normal file
17
internal/route/provider/file_test.go
Normal 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)
|
||||
}
|
|
@ -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
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
if tmp != nil {
|
||||
return true, Convert(reflect.ValueOf(tmp), dst)
|
||||
}
|
||||
return false, nil
|
||||
return true, Convert(reflect.ValueOf(tmp), dst)
|
||||
}
|
||||
|
||||
func DeserializeYAML[T any](data []byte, target T) E.Error {
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Add table
Reference in a new issue