mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-19 20:32:35 +02:00
v0.5: fixed nil dereference for empty autocert config, fixed and simplified 'error' module, small readme and docs update
This commit is contained in:
parent
2fc82c3790
commit
85fb637551
20 changed files with 209 additions and 235 deletions
2
Makefile
2
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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
28
src/error/builder_test.go
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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(" ")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=
|
||||
|
|
52
src/main.go
52
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,
|
||||
|
|
|
@ -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 '/'")
|
||||
}
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.5.0-beta
|
||||
0.5.0-beta2
|
Loading…
Add table
Reference in a new issue