mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-20 12:42:34 +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")
|
withSubjectf := err.Subjectf("%s %s", "foo", "bar")
|
||||||
|
|
||||||
ExpectError(t, err, withSubject)
|
ExpectError(t, err, withSubject)
|
||||||
ExpectStrEqual(t, withSubject.Error(), "foo: error")
|
ExpectEqual(t, withSubject.Error(), "foo: error")
|
||||||
ExpectTrue(t, withSubject.Is(err))
|
ExpectTrue(t, withSubject.Is(err))
|
||||||
|
|
||||||
ExpectError(t, err, withSubjectf)
|
ExpectError(t, err, withSubjectf)
|
||||||
ExpectStrEqual(t, withSubjectf.Error(), "foo bar: error")
|
ExpectEqual(t, withSubjectf.Error(), "foo bar: error")
|
||||||
ExpectTrue(t, withSubjectf.Is(err))
|
ExpectTrue(t, withSubjectf.Is(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,9 +114,9 @@ func TestErrorWith(t *testing.T) {
|
||||||
func TestErrorStringSimple(t *testing.T) {
|
func TestErrorStringSimple(t *testing.T) {
|
||||||
errFailure := New("generic failure")
|
errFailure := New("generic failure")
|
||||||
ne := errFailure.Subject("foo bar")
|
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")
|
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) {
|
func TestErrorStringNested(t *testing.T) {
|
||||||
|
@ -153,5 +153,5 @@ func TestErrorStringNested(t *testing.T) {
|
||||||
• action 3 > inner3: generic failure
|
• action 3 > inner3: generic failure
|
||||||
• 3
|
• 3
|
||||||
• 3`
|
• 3`
|
||||||
ExpectStrEqual(t, ne.Error(), want)
|
ExpectEqual(t, ne.Error(), want)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package err
|
package err
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,12 +19,16 @@ func Errorf(format string, args ...any) Error {
|
||||||
return &baseError{fmt.Errorf(format, args...)}
|
return &baseError{fmt.Errorf(format, args...)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func From(err error) (e Error) {
|
func From(err error) Error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if errors.As(err, &e) {
|
//nolint:errorlint
|
||||||
return e
|
switch err := err.(type) {
|
||||||
|
case *baseError:
|
||||||
|
return err
|
||||||
|
case *nestedError:
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return &baseError{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
|
return impl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validate(data []byte) (route.RawEntries, E.Error) {
|
||||||
|
return utils.DeserializeYAMLMap[*route.RawEntry](data)
|
||||||
|
}
|
||||||
|
|
||||||
func Validate(data []byte) (err E.Error) {
|
func Validate(data []byte) (err E.Error) {
|
||||||
_, err = utils.DeserializeYAMLMap[*route.RawEntry](data)
|
_, err = validate(data)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +56,7 @@ func (p *FileProvider) loadRoutesImpl() (route.Routes, E.Error) {
|
||||||
return routes, E.From(err)
|
return routes, E.From(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
entries, err := utils.DeserializeYAMLMap[*route.RawEntry](data)
|
entries, err := validate(data)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return route.FromEntries(entries)
|
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:
|
// Returns:
|
||||||
// - error: the error occurred during conversion, or nil if no error occurred.
|
// - error: the error occurred during conversion, or nil if no error occurred.
|
||||||
func Convert(src reflect.Value, dst reflect.Value) E.Error {
|
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()
|
srcT := src.Type()
|
||||||
dstT := dst.Type()
|
dstT := dst.Type()
|
||||||
|
|
||||||
|
@ -339,7 +351,7 @@ func Convert(src reflect.Value, dst reflect.Value) E.Error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if dstT.Kind() != reflect.Slice {
|
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())
|
newSlice := reflect.MakeSlice(dstT, 0, src.Len())
|
||||||
i := 0
|
i := 0
|
||||||
|
@ -469,11 +481,10 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr E.E
|
||||||
return true, errs.Error()
|
return true, errs.Error()
|
||||||
}
|
}
|
||||||
tmp = m
|
tmp = m
|
||||||
|
default:
|
||||||
|
return false, nil
|
||||||
}
|
}
|
||||||
if tmp != nil {
|
return true, Convert(reflect.ValueOf(tmp), dst)
|
||||||
return true, Convert(reflect.ValueOf(tmp), dst)
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeserializeYAML[T any](data []byte, target T) E.Error {
|
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) {
|
func ExpectNoError(t *testing.T, err error) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if err != nil && !reflect.ValueOf(err).IsNil() {
|
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()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ func ExpectNoError(t *testing.T, err error) {
|
||||||
func ExpectError(t *testing.T, expected error, err error) {
|
func ExpectError(t *testing.T, expected error, err error) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if !errors.Is(err, expected) {
|
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()
|
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) {
|
func ExpectError2(t *testing.T, input any, expected error, err error) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if !errors.Is(err, expected) {
|
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()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,22 +48,17 @@ func ExpectErrorT[T error](t *testing.T, err error) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
var errAs T
|
var errAs T
|
||||||
if !errors.As(err, &errAs) {
|
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()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExpectEqual[T comparable](t *testing.T, got T, want T) {
|
func ExpectEqual[T comparable](t *testing.T, got T, want T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if got != want {
|
if gotStr, ok := any(got).(string); ok {
|
||||||
t.Errorf("expected:\n%v, got\n%v", want, got)
|
ExpectDeepEqual(t, ansi.StripANSI(gotStr), any(want).(string))
|
||||||
t.FailNow()
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func ExpectStrEqual(t *testing.T, got string, want string) {
|
|
||||||
t.Helper()
|
|
||||||
got = ansi.StripANSI(got)
|
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Errorf("expected:\n%v, got\n%v", want, got)
|
t.Errorf("expected:\n%v, got\n%v", want, got)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
|
|
Loading…
Add table
Reference in a new issue