v0.5: fixed nil dereference for empty autocert config, fixed and simplified 'error' module, small readme and docs update

This commit is contained in:
default 2024-08-13 04:59:34 +08:00
parent 2fc82c3790
commit 85fb637551
20 changed files with 209 additions and 235 deletions

View file

@ -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

View file

@ -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)
<!-- /TOC -->
## Key Points

View file

@ -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)

View file

@ -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),

View file

@ -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)
}

View file

@ -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")

View file

@ -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)

28
src/error/builder_test.go Normal file
View file

@ -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)
}
}

View file

@ -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 &copy
}
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(" ")
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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

View file

@ -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=

View file

@ -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,

View file

@ -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 '/'")
}

View file

@ -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])

View file

@ -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)

View file

@ -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 {

View file

@ -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)) {

View file

@ -1 +1 @@
0.5.0-beta
0.5.0-beta2