diff --git a/Makefile b/Makefile index c0168eb1..db509715 100755 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ build: CGO_ENABLED=0 GOOS=linux go build -pgo=auto -o bin/go-proxy github.com/yusing/go-proxy test: - cd src && go test && cd .. + cd src && go test ./... && cd .. up: docker compose up -d diff --git a/README.md b/README.md index b7618160..f96291d0 100755 --- a/README.md +++ b/README.md @@ -16,7 +16,6 @@ A [lightweight](docs/benchmark_result.md), easy-to-use, and efficient reverse pr - [Provider File](#provider-file) - [Known issues](#known-issues) - [Build it yourself](#build-it-yourself) - ## Key Points diff --git a/docs/docker.md b/docs/docker.md index b428ec53..6621003b 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -200,25 +200,22 @@ services: volumes: - nginx:/usr/share/nginx/html go-proxy: - image: ghcr.io/yusing/go-proxy + image: ghcr.io/yusing/go-proxy:latest container_name: go-proxy restart: always - ports: - - 80:80 # http - - 443:443 # optional, https - - 8080:8080 # http panel - - 8443:8443 # optional, https panel - - - 53:20000/udp # adguardhome - - 25565:20001/tcp # minecraft - - 8211:20002/udp # palworld - - 27015:20003/udp # palworld + network_mode: host volumes: - ./config:/app/config - /var/run/docker.sock:/var/run/docker.sock:ro + go-proxy-frontend: + image: ghcr.io/yusing/go-proxy-frontend:latest + container_name: go-proxy-frontend + restart: unless-stopped + network_mode: host labels: - - proxy.aliases=gp - - proxy.gp.port=8080 + - proxy.*.aliases=gp + depends_on: + - go-proxy ``` [🔼Back to top](#table-of-content) diff --git a/src/autocert/constants.go b/src/autocert/constants.go index cebde51e..96d80de0 100644 --- a/src/autocert/constants.go +++ b/src/autocert/constants.go @@ -21,6 +21,7 @@ const ( ) var providersGenMap = map[string]ProviderGenerator{ + "": providerGenerator(NewDummyDefaultConfig, NewDummyDNSProviderConfig), ProviderLocal: providerGenerator(NewDummyDefaultConfig, NewDummyDNSProviderConfig), ProviderCloudflare: providerGenerator(cloudflare.NewDefaultConfig, cloudflare.NewDNSProviderConfig), ProviderClouddns: providerGenerator(clouddns.NewDefaultConfig, clouddns.NewDNSProviderConfig), diff --git a/src/config/config.go b/src/config/config.go index eb9aa7f5..defe06d4 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -207,12 +207,12 @@ func (cfg *Config) load() E.NestedError { } } - errors := E.NewBuilder("errors validating config") + warnings := E.NewBuilder("errors validating config") cfg.l.Debug("starting autocert") ap, err := autocert.NewConfig(&model.AutoCert).GetProvider() if err.IsNotNil() { - errors.Add(E.Failure("autocert provider").With(err)) + warnings.Add(E.Failure("autocert provider").With(err)) } else { cfg.l.Debug("started autocert") } @@ -224,14 +224,14 @@ func (cfg *Config) load() E.NestedError { p := PR.NewProvider(name, pm) cfg.proxyProviders.Set(name, p) if err := p.StartAllRoutes(); err.IsNotNil() { - errors.Add(E.Failure("start routes").Subjectf("provider %s", name).With(err)) + warnings.Add(E.Failure("start routes").Subjectf("provider %s", name).With(err)) } } cfg.l.Debug("started providers") cfg.value = model - if err := errors.Build(); err.IsNotNil() { + if err := warnings.Build(); err.IsNotNil() { cfg.l.Warn(err) } diff --git a/src/docker/label.go b/src/docker/label.go index 937bd069..1468226f 100644 --- a/src/docker/label.go +++ b/src/docker/label.go @@ -1,7 +1,6 @@ package docker import ( - "errors" "strings" E "github.com/yusing/go-proxy/error" @@ -77,5 +76,3 @@ func RegisterNamespace(namespace string, pm ValueParserMap) { // namespace:target.attribute -> func(string) (any, error) var labelValueParserMap = make(map[string]ValueParserMap) - -var ErrInvalidLabel = errors.New("invalid label") diff --git a/src/docker/proxy_label_test.go b/src/docker/proxy_label_test.go index 1f86ffea..c87c091b 100644 --- a/src/docker/proxy_label_test.go +++ b/src/docker/proxy_label_test.go @@ -1,7 +1,6 @@ package docker import ( - "errors" "fmt" "net/http" "reflect" @@ -14,27 +13,13 @@ func makeLabel(namespace string, alias string, field string) string { return fmt.Sprintf("%s.%s.%s", namespace, alias, field) } -func TestInvalidLabel(t *testing.T) { - pl, err := ParseLabel("foo.bar", "1234") - if !errors.Is(err, ErrInvalidLabel) { - t.Errorf("expected errInvalidLabel, got %s", err) - } - if pl != nil { - t.Errorf("expected nil, got %v", pl) - } - _, err = ParseLabel("proxy.foo", "bar") - if !errors.Is(err, ErrInvalidLabel) { - t.Errorf("expected errInvalidLabel, got %s", err) - } -} - func TestHomePageLabel(t *testing.T) { alias := "foo" field := "ip" v := "bar" pl, err := ParseLabel(makeLabel(NSHomePage, alias, field), v) if err.IsNotNil() { - t.Errorf("expected err=nil, got %s", err) + t.Errorf("expected err=nil, got %s", err.Error()) } if pl.Target != alias { t.Errorf("expected alias=%s, got %s", alias, pl.Target) @@ -53,7 +38,7 @@ func TestStringProxyLabel(t *testing.T) { v := "bar" pl, err := ParseLabel(makeLabel(NSProxy, alias, field), v) if err.IsNotNil() { - t.Errorf("expected err=nil, got %s", err) + t.Errorf("expected err=nil, got %s", err.Error()) } if pl.Target != alias { t.Errorf("expected alias=%s, got %s", alias, pl.Target) @@ -83,7 +68,7 @@ func TestBoolProxyLabelValid(t *testing.T) { for k, v := range tests { pl, err := ParseLabel(makeLabel(NSProxy, alias, field), k) if err.IsNotNil() { - t.Errorf("expected err=nil, got %s", err) + t.Errorf("expected err=nil, got %s", err.Error()) } if pl.Target != alias { t.Errorf("expected alias=%s, got %s", alias, pl.Target) @@ -101,8 +86,8 @@ func TestBoolProxyLabelInvalid(t *testing.T) { alias := "foo" field := "no_tls_verify" _, err := ParseLabel(makeLabel(NSProxy, alias, field), "invalid") - if !errors.Is(err, E.ErrInvalid) { - t.Errorf("expected err InvalidProxyLabel, got %s", err) + if !err.Is(E.ErrInvalid) { + t.Errorf("expected err InvalidProxyLabel, got %v", reflect.TypeOf(err)) } } @@ -121,7 +106,7 @@ func TestHeaderProxyLabelValid(t *testing.T) { pl, err := ParseLabel(makeLabel(NSProxy, alias, field), v) if err.IsNotNil() { - t.Errorf("expected err=nil, got %s", err) + t.Errorf("expected err=nil, got %s", err.Error()) } if pl.Target != alias { t.Errorf("expected alias=%s, got %s", alias, pl.Target) @@ -152,7 +137,7 @@ func TestHeaderProxyLabelInvalid(t *testing.T) { for _, v := range tests { _, err := ParseLabel(makeLabel(NSProxy, alias, field), v) - if !errors.Is(err, E.ErrInvalid) { + if !err.Is(E.ErrInvalid) { t.Errorf("expected err InvalidProxyLabel for %q, got %v", v, err) } } @@ -164,7 +149,7 @@ func TestCommaSepProxyLabelSingle(t *testing.T) { v := "X-Custom-Header1" pl, err := ParseLabel(makeLabel(NSProxy, alias, field), v) if err.IsNotNil() { - t.Errorf("expected err=nil, got %s", err) + t.Errorf("expected err=nil, got %s", err.Error()) } if pl.Target != alias { t.Errorf("expected alias=%s, got %s", alias, pl.Target) @@ -188,7 +173,7 @@ func TestCommaSepProxyLabelMulti(t *testing.T) { v := "X-Custom-Header1, X-Custom-Header2,X-Custom-Header3" pl, err := ParseLabel(makeLabel(NSProxy, alias, field), v) if err.IsNotNil() { - t.Errorf("expected err=nil, got %s", err) + t.Errorf("expected err=nil, got %s", err.Error()) } if pl.Target != alias { t.Errorf("expected alias=%s, got %s", alias, pl.Target) diff --git a/src/error/builder_test.go b/src/error/builder_test.go new file mode 100644 index 00000000..d3424bf2 --- /dev/null +++ b/src/error/builder_test.go @@ -0,0 +1,28 @@ +package error + +import "testing" + +func TestBuilder(t *testing.T) { + eb := NewBuilder("error occurred") + eb.Add(Failure("Action 1").With(Invalid("Inner", "1")).With(Invalid("Inner", "2"))) + eb.Add(Failure("Action 2").With(Invalid("Inner", "3"))) + + got := eb.Build().Error() + expected1 := + (`error occurred: + - Action 1 failed: + - invalid Inner - 1 + - invalid Inner - 2 + - Action 2 failed: + - invalid Inner - 3`) + expected2 := + (`error occurred: + - Action 1 failed: + - invalid Inner - 2 + - invalid Inner - 1 + - Action 2 failed: + - invalid Inner - 3`) + if got != expected1 && got != expected2 { + t.Errorf("expected \n%s, got \n%s", expected1, got) + } +} diff --git a/src/error/error.go b/src/error/error.go index e8a8426d..3164d0dc 100644 --- a/src/error/error.go +++ b/src/error/error.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "strings" - "sync" ) type ( @@ -20,27 +19,24 @@ type ( // You should return (Slice/Map, NestedError). // Caller then should handle the nested error, // and continue with the valid values. - NestedError struct{ *nestedError } - nestedError struct { - neBase - sync.Mutex - } - neBase struct { + NestedError struct { subject any err error // can be nil - extras []neBase - inner *neBase // can be nil - level int + extras []NestedError } ) func Nil() NestedError { return NestedError{} } func From(err error) NestedError { - if err == nil { + switch err := err.(type) { + case nil: return Nil() + case NestedError: + return err + default: + return NestedError{err: err} } - return NestedError{&nestedError{neBase: *copyFrom(err)}} } // Check is a helper function that @@ -50,73 +46,41 @@ func Check[T any](obj T, err error) (T, NestedError) { } func Join(message string, err ...error) NestedError { - extras := make([]neBase, 0, len(err)) + extras := make([]NestedError, 0, len(err)) nErr := 0 for _, e := range err { if err == nil { continue } - extras = append(extras, *copyFrom(e)) + extras = append(extras, From(e)) nErr += 1 } if nErr == 0 { return Nil() } - return NestedError{&nestedError{ - neBase: neBase{ - err: errors.New(message), - extras: extras, - }, - }} -} - -func copyFrom(err error) *neBase { - if err == nil { - return nil + return NestedError{ + err: errors.New(message), + extras: extras, } - switch base := err.(type) { - case *neBase: - copy := *base - return © - } - return &neBase{err: err} } -func new(message ...string) NestedError { - if len(message) == 0 { - return From(nil) - } - return From(errors.New(strings.Join(message, " "))) -} - -func errorf(format string, args ...any) NestedError { - return From(fmt.Errorf(format, args...)) -} - -func (ne *neBase) Error() string { +func (ne NestedError) Error() string { var buf strings.Builder - ne.writeToSB(&buf, ne.level, "") + ne.writeToSB(&buf, 0, "") return buf.String() } -func (ne NestedError) ExtraError(err error) NestedError { - if err != nil { - ne.Lock() - ne.extras = append(ne.extras, From(err).addLevel(ne.Level()+1)) - ne.Unlock() - } - return ne +func (ne NestedError) Is(err error) bool { + return errors.Is(ne.err, err) } -func (ne NestedError) Extra(s string) NestedError { - return ne.ExtraError(errors.New(s)) -} - -func (ne NestedError) ExtraAny(s any) NestedError { +func (ne NestedError) With(s any) NestedError { var msg string switch ss := s.(type) { + case nil: + return ne case error: - return ne.ExtraError(ss) + return ne.withError(ss) case string: msg = ss case fmt.Stringer: @@ -124,11 +88,11 @@ func (ne NestedError) ExtraAny(s any) NestedError { default: msg = fmt.Sprint(s) } - return ne.ExtraError(errors.New(msg)) + return ne.withError(errors.New(msg)) } func (ne NestedError) Extraf(format string, args ...any) NestedError { - return ne.ExtraError(fmt.Errorf(format, args...)) + return ne.With(fmt.Errorf(format, args...)) } func (ne NestedError) Subject(s any) NestedError { @@ -146,71 +110,47 @@ func (ne NestedError) Subjectf(format string, args ...any) NestedError { return ne.Subject(fmt.Sprintf(format, args...)) } -func (ne NestedError) Level() int { - return ne.level +func (ne NestedError) IsNil() bool { + return ne.err == nil } -func (ne *nestedError) IsNil() bool { - return ne == nil +func (ne NestedError) IsNotNil() bool { + return ne.err != nil } -func (ne *nestedError) IsNotNil() bool { - return ne != nil +func errorf(format string, args ...any) NestedError { + return From(fmt.Errorf(format, args...)) } -func (ne NestedError) With(inner error) NestedError { - ne.Lock() - defer ne.Unlock() - - if ne.inner == nil { - ne.inner = copyFrom(inner) - } else { - ne.ExtraError(inner) - } - - root := &ne.neBase - for root.inner != nil { - root.inner.level = root.level + 1 - root = root.inner - } +func (ne NestedError) withError(err error) NestedError { + ne.extras = append(ne.extras, From(err)) return ne } -func (ne *neBase) addLevel(level int) neBase { - ret := *ne - ret.level += level - if ret.inner != nil { - inner := ret.inner.addLevel(level) - ret.inner = &inner - } - return ret -} - -func (ne *neBase) writeToSB(sb *strings.Builder, level int, prefix string) { +func (ne *NestedError) writeToSB(sb *strings.Builder, level int, prefix string) { ne.writeIndents(sb, level) sb.WriteString(prefix) if ne.err != nil { sb.WriteString(ne.err.Error()) - sb.WriteRune(' ') } if ne.subject != nil { - sb.WriteString(fmt.Sprintf("for %q", ne.subject)) + if ne.err != nil { + sb.WriteString(fmt.Sprintf(" for %q", ne.subject)) + } else { + sb.WriteString(fmt.Sprint(ne.subject)) + } } - if ne.inner != nil || len(ne.extras) > 0 { - sb.WriteString(":\n") - } - level += 1 - for _, extra := range ne.extras { - extra.writeToSB(sb, level, "- ") - sb.WriteRune('\n') - } - if ne.inner != nil { - ne.inner.writeToSB(sb, level, "- ") + if len(ne.extras) > 0 { + sb.WriteRune(':') + for _, extra := range ne.extras { + sb.WriteRune('\n') + extra.writeToSB(sb, level+1, "- ") + } } } -func (ne *neBase) writeIndents(sb *strings.Builder, level int) { +func (ne *NestedError) writeIndents(sb *strings.Builder, level int) { for i := 0; i < level; i++ { sb.WriteString(" ") } diff --git a/src/error/error_test.go b/src/error/error_test.go index d3936342..f8a0e3b0 100644 --- a/src/error/error_test.go +++ b/src/error/error_test.go @@ -4,63 +4,67 @@ import ( "testing" ) -func AssertEq(t *testing.T, got, want string) { +func AssertEq[T comparable](t *testing.T, got, want T) { t.Helper() if got != want { - t.Errorf("expected %q, got %q", want, got) + t.Errorf("expected:\n%v, got\n%v", want, got) } } +func TestErrorIs(t *testing.T) { + AssertEq(t, Failure("foo").Is(ErrFailure), true) + AssertEq(t, Failure("foo").With("bar").Is(ErrFailure), true) + AssertEq(t, Failure("foo").With("bar").Is(ErrInvalid), false) + AssertEq(t, Failure("foo").With("bar").With("baz").Is(ErrInvalid), false) + + AssertEq(t, Invalid("foo", "bar").Is(ErrInvalid), true) + AssertEq(t, Invalid("foo", "bar").Is(ErrFailure), false) +} + func TestErrorSimple(t *testing.T) { - ne := new("foo bar") - AssertEq(t, ne.Error(), "foo bar") - ne.Subject("baz") - AssertEq(t, ne.Error(), "baz: foo bar") + ne := Failure("foo bar") + AssertEq(t, ne.Error(), "foo bar failed") + ne = ne.Subject("baz") + AssertEq(t, ne.Error(), "foo bar failed for \"baz\"") } -func TestErrorSubjectOnly(t *testing.T) { - ne := new().Subject("bar") - AssertEq(t, ne.Error(), "bar") -} - -func TestErrorExtra(t *testing.T) { - ne := new("foo").Extra("bar").Extra("baz") - AssertEq(t, ne.Error(), "foo:\n - bar\n - baz\n") +func TestErrorWith(t *testing.T) { + ne := Failure("foo").With("bar").With("baz") + AssertEq(t, ne.Error(), "foo failed:\n - bar\n - baz") } func TestErrorNested(t *testing.T) { - inner := new("inner"). - Extra("123"). - Extra("456") - inner2 := new("inner"). - Subject("2"). - Extra("456"). - Extra("789") - inner3 := new("inner"). - Subject("3"). - Extra("456"). - Extra("789") - ne := new("foo"). - Extra("bar"). - Extra("baz"). - ExtraError(inner). + inner := Failure("inner"). + With("1"). + With("1") + inner2 := Failure("inner2"). + Subject("action 2"). + With("2"). + With("2") + inner3 := Failure("inner3"). + Subject("action 3"). + With("3"). + With("3") + ne := Failure("foo"). + With("bar"). + With("baz"). + With(inner). With(inner.With(inner2.With(inner3))) want := - `foo: + `foo failed: - bar - baz - - inner: - - 123 - - 456 - - inner: - - 123 - - 456 - - 2: inner: - - 456 - - 789 - - 3: inner: - - 456 - - 789 -` + - inner failed: + - 1 + - 1 + - inner failed: + - 1 + - 1 + - inner2 failed for "action 2": + - 2 + - 2 + - inner3 failed for "action 3": + - 3 + - 3` AssertEq(t, ne.Error(), want) } diff --git a/src/error/errors.go b/src/error/errors.go index 77bb47a6..6000c703 100644 --- a/src/error/errors.go +++ b/src/error/errors.go @@ -1,30 +1,33 @@ package error +import ( + stderrors "errors" +) + var ( - ErrAlreadyStarted = new("already started") - ErrNotStarted = new("not started") - ErrInvalid = new("invalid") - ErrUnsupported = new("unsupported") - ErrNotExists = new("does not exist") - ErrDuplicated = new("duplicated") + ErrFailure = stderrors.New("failed") + ErrInvalid = stderrors.New("invalid") + ErrUnsupported = stderrors.New("unsupported") + ErrNotExists = stderrors.New("does not exist") + ErrDuplicated = stderrors.New("duplicated") ) func Failure(what string) NestedError { - return errorf("%s failed", what) + return errorf("%s %w", what, ErrFailure) } func Invalid(subject, what any) NestedError { - return errorf("%w %s: %q", ErrInvalid, subject, what) + return errorf("%w %v - %v", ErrInvalid, subject, what) } func Unsupported(subject, what any) NestedError { - return errorf("%w %s: %q", ErrUnsupported, subject, what) + return errorf("%w %v - %v", ErrUnsupported, subject, what) } func NotExists(subject, what any) NestedError { - return errorf("%s %w: %q", subject, ErrNotExists, what) + return errorf("%s %v - %v", subject, ErrNotExists, what) } func Duplicated(subject, what any) NestedError { - return errorf("%w %s: %q", ErrDuplicated, subject, what) + return errorf("%w %v: %v", ErrDuplicated, subject, what) } diff --git a/src/go.mod b/src/go.mod index e93ddc84..0215843c 100644 --- a/src/go.mod +++ b/src/go.mod @@ -44,7 +44,7 @@ require ( golang.org/x/crypto v0.26.0 // indirect golang.org/x/mod v0.20.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.23.0 // indirect + golang.org/x/sys v0.24.0 // indirect golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.6.0 // indirect golang.org/x/tools v0.24.0 // indirect diff --git a/src/go.sum b/src/go.sum index d4e9933a..d2e85732 100644 --- a/src/go.sum +++ b/src/go.sum @@ -119,8 +119,8 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= diff --git a/src/main.go b/src/main.go index 023adf2c..1f3e0de6 100755 --- a/src/main.go +++ b/src/main.go @@ -16,6 +16,7 @@ import ( "github.com/yusing/go-proxy/common" "github.com/yusing/go-proxy/config" "github.com/yusing/go-proxy/docker" + E "github.com/yusing/go-proxy/error" R "github.com/yusing/go-proxy/route" "github.com/yusing/go-proxy/server" F "github.com/yusing/go-proxy/utils/functional" @@ -53,18 +54,26 @@ func main() { } onShutdown := F.NewSlice[func()]() + + // exit if only validate config + if args.Command == common.CommandValidate { + var err E.NestedError + data, err := E.Check(os.ReadFile(common.ConfigPath)) + if err.IsNotNil() { + l.WithError(err).Fatalf("config error") + } + if err = config.Validate(data); err.IsNotNil() { + l.WithError(err).Fatalf("config error") + } + l.Printf("config OK") + return + } + cfg, err := config.New() if err.IsNotNil() { l.Fatalf("config error: %s", err) } - // exit if only validate config - // TODO: validate without load - if args.Command == common.CommandValidate { - l.Printf("config OK") - return - } - onShutdown.Add(func() { docker.CloseAllClients() cfg.Dispose() @@ -76,22 +85,27 @@ func main() { signal.Notify(sig, syscall.SIGHUP) autocert := cfg.GetAutoCertProvider() - err = autocert.LoadCert() - if err.IsNotNil() { - l.Infof("error loading certificate: %s\nNow attempting to obtain a new certificate", err) - if err = autocert.ObtainCert(); err.IsNotNil() { - ctx, certRenewalCancel := context.WithCancel(context.Background()) - go autocert.ScheduleRenewal(ctx) - onShutdown.Add(certRenewalCancel) + if autocert != nil { + err = autocert.LoadCert() + + if err.IsNotNil() { + l.Error(err) + l.Info("Now attempting to obtain a new certificate...") + if err = autocert.ObtainCert(); err.IsNotNil() { + ctx, certRenewalCancel := context.WithCancel(context.Background()) + go autocert.ScheduleRenewal(ctx) + onShutdown.Add(certRenewalCancel) + } else { + l.Warn(err) + } } else { - l.Warn(err) - } - } else { - for name, expiry := range autocert.GetExpiries() { - l.Infof("certificate %q: expire on %s", name, expiry) + for name, expiry := range autocert.GetExpiries() { + l.Infof("certificate %q: expire on %s", name, expiry) + } } } + proxyServer := server.InitProxyServer(server.Options{ Name: "proxy", CertProvider: autocert, diff --git a/src/proxy/fields/path.go b/src/proxy/fields/path.go index 1397898f..115871e0 100644 --- a/src/proxy/fields/path.go +++ b/src/proxy/fields/path.go @@ -11,5 +11,5 @@ func NewPath(s string) (Path, E.NestedError) { if s == "" || s[0] == '/' { return Path{F.NewStringable(s)}, E.Nil() } - return Path{}, E.Invalid("path", s).Extra("must be empty or start with '/'") + return Path{}, E.Invalid("path", s).With("must be empty or start with '/'") } diff --git a/src/proxy/fields/stream_port.go b/src/proxy/fields/stream_port.go index 82f785d8..20a58ed5 100644 --- a/src/proxy/fields/stream_port.go +++ b/src/proxy/fields/stream_port.go @@ -15,7 +15,7 @@ type StreamPort struct { func NewStreamPort(p string) (StreamPort, E.NestedError) { split := strings.Split(p, ":") if len(split) != 2 { - return StreamPort{}, E.Invalid("stream port", p).Extra("should be in 'x:y' format") + return StreamPort{}, E.Invalid("stream port", p).With("should be in 'x:y' format") } listeningPort, err := NewPort(split[0]) diff --git a/src/route/stream_route.go b/src/route/stream_route.go index 9de3594a..a7ea93f9 100755 --- a/src/route/stream_route.go +++ b/src/route/stream_route.go @@ -51,7 +51,7 @@ func NewStreamRoute(entry *P.StreamEntry) (*StreamRoute, E.NestedError) { func (r *StreamRoute) Start() E.NestedError { if r.started.Load() { - return E.ErrAlreadyStarted + return E.Invalid("state", "already started") } r.wg.Wait() if err := r.Setup(); err != nil { @@ -66,7 +66,7 @@ func (r *StreamRoute) Start() E.NestedError { func (r *StreamRoute) Stop() E.NestedError { if !r.started.Load() { - return E.ErrNotStarted + return E.Invalid("state", "not started") } l := r.l close(r.stopCh) diff --git a/src/server/server.go b/src/server/server.go index ca51befa..4d44d339 100644 --- a/src/server/server.go +++ b/src/server/server.go @@ -49,8 +49,12 @@ func NewServer(opt Options) (s *server) { logrus.WithFields(logrus.Fields{"?": "server", "name": opt.Name}), }) - _, err := opt.CertProvider.GetCert(nil) - certAvailable := err == nil + certAvailable := false + if opt.CertProvider != nil { + _, err := opt.CertProvider.GetCert(nil) + certAvailable = err == nil + } + if certAvailable && opt.RedirectToHTTPS && opt.HTTPSPort != "" { httpHandler = redirectToTLSHandler(opt.HTTPSPort) } else { diff --git a/src/utils/functional/slice.go b/src/utils/functional/slice.go index 457b4b37..e27f825e 100644 --- a/src/utils/functional/slice.go +++ b/src/utils/functional/slice.go @@ -37,11 +37,13 @@ func (s *Slice[T]) Set(i int, v T) { } func (s *Slice[T]) Add(e T) *Slice[T] { - return &Slice[T]{append(s.s, e)} + s.s = append(s.s, e) + return s } func (s *Slice[T]) AddRange(other *Slice[T]) *Slice[T] { - return &Slice[T]{append(s.s, other.s...)} + s.s = append(s.s, other.s...) + return s } func (s *Slice[T]) ForEach(do func(T)) { diff --git a/version.txt b/version.txt index f73fb1ce..1ca90c1b 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.5.0-beta \ No newline at end of file +0.5.0-beta2 \ No newline at end of file