mirror of
https://github.com/yusing/godoxy.git
synced 2025-07-05 06:14:26 +02:00
refactor: remove net.URL and net.CIDR types, improved unmarshal handling
This commit is contained in:
parent
1eac48e899
commit
fce96ff3be
37 changed files with 236 additions and 292 deletions
|
@ -8,7 +8,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||||
"github.com/yusing/go-proxy/internal/watcher/health/monitor"
|
"github.com/yusing/go-proxy/internal/watcher/health/monitor"
|
||||||
)
|
)
|
||||||
|
@ -44,11 +43,11 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
result, err = monitor.NewHTTPHealthMonitor(types.NewURL(&url.URL{
|
result, err = monitor.NewHTTPHealthMonitor(&url.URL{
|
||||||
Scheme: scheme,
|
Scheme: scheme,
|
||||||
Host: host,
|
Host: host,
|
||||||
Path: path,
|
Path: path,
|
||||||
}), defaultHealthConfig).CheckHealth()
|
}, defaultHealthConfig).CheckHealth()
|
||||||
case "tcp", "udp":
|
case "tcp", "udp":
|
||||||
host := query.Get("host")
|
host := query.Get("host")
|
||||||
if host == "" {
|
if host == "" {
|
||||||
|
@ -63,10 +62,10 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
result, err = monitor.NewRawHealthMonitor(types.NewURL(&url.URL{
|
result, err = monitor.NewRawHealthMonitor(&url.URL{
|
||||||
Scheme: scheme,
|
Scheme: scheme,
|
||||||
Host: host,
|
Host: host,
|
||||||
}), defaultHealthConfig).CheckHealth()
|
}, defaultHealthConfig).CheckHealth()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/yusing/go-proxy/internal/docker"
|
"github.com/yusing/go-proxy/internal/docker"
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
"github.com/yusing/go-proxy/internal/logging"
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func serviceUnavailable(w http.ResponseWriter, r *http.Request) {
|
func serviceUnavailable(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -22,10 +21,10 @@ func DockerSocketHandler() http.HandlerFunc {
|
||||||
logging.Warn().Err(err).Msg("failed to connect to docker client")
|
logging.Warn().Err(err).Msg("failed to connect to docker client")
|
||||||
return serviceUnavailable
|
return serviceUnavailable
|
||||||
}
|
}
|
||||||
rp := reverseproxy.NewReverseProxy("docker", types.NewURL(&url.URL{
|
rp := reverseproxy.NewReverseProxy("docker", &url.URL{
|
||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
Host: client.DummyHost,
|
Host: client.DummyHost,
|
||||||
}), dockerClient.HTTPClient().Transport)
|
}, dockerClient.HTTPClient().Transport)
|
||||||
|
|
||||||
return rp.ServeHTTP
|
return rp.ServeHTTP
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ import (
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
"github.com/yusing/go-proxy/internal/logging"
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -55,9 +54,9 @@ func ProxyHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
logging.Debug().Msgf("proxy http request: %s %s", r.Method, r.URL.String())
|
logging.Debug().Msgf("proxy http request: %s %s", r.Method, r.URL.String())
|
||||||
|
|
||||||
rp := reverseproxy.NewReverseProxy("agent", types.NewURL(&url.URL{
|
rp := reverseproxy.NewReverseProxy("agent", &url.URL{
|
||||||
Scheme: scheme,
|
Scheme: scheme,
|
||||||
Host: host,
|
Host: host,
|
||||||
}), transport)
|
}, transport)
|
||||||
rp.ServeHTTP(w, r)
|
rp.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,14 @@ package homepage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
net "github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type route interface {
|
type route interface {
|
||||||
TargetName() string
|
TargetName() string
|
||||||
ProviderName() string
|
ProviderName() string
|
||||||
Reference() string
|
Reference() string
|
||||||
TargetURL() *net.URL
|
TargetURL() *url.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
type httpRoute interface {
|
type httpRoute interface {
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,7 +23,7 @@ type (
|
||||||
Key, Value string
|
Key, Value string
|
||||||
}
|
}
|
||||||
Host string
|
Host string
|
||||||
CIDR struct{ types.CIDR }
|
CIDR struct{ net.IPNet }
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrInvalidHTTPHeaderFilter = gperr.New("invalid http header filter")
|
var ErrInvalidHTTPHeaderFilter = gperr.New("invalid http header filter")
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package accesslog_test
|
package accesslog_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -155,8 +156,11 @@ func TestHeaderFilter(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCIDRFilter(t *testing.T) {
|
func TestCIDRFilter(t *testing.T) {
|
||||||
cidr := []*CIDR{
|
cidr := []*CIDR{{
|
||||||
strutils.MustParse[*CIDR]("192.168.10.0/24"),
|
net.IPNet{
|
||||||
|
IP: net.ParseIP("192.168.10.0"),
|
||||||
|
Mask: net.CIDRMask(24, 32),
|
||||||
|
}},
|
||||||
}
|
}
|
||||||
ExpectEqual(t, cidr[0].String(), "192.168.10.0/24")
|
ExpectEqual(t, cidr[0].String(), "192.168.10.0/24")
|
||||||
inCIDR := &http.Request{
|
inCIDR := &http.Request{
|
||||||
|
|
|
@ -13,7 +13,6 @@ import (
|
||||||
"github.com/yusing/go-proxy/internal/route/routes"
|
"github.com/yusing/go-proxy/internal/route/routes"
|
||||||
"github.com/yusing/go-proxy/internal/task"
|
"github.com/yusing/go-proxy/internal/task"
|
||||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||||
"github.com/yusing/go-proxy/internal/watcher/health/monitor"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: stats of each server.
|
// TODO: stats of each server.
|
||||||
|
@ -240,14 +239,14 @@ func (lb *LoadBalancer) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
lb.impl.ServeHTTP(srvs, rw, r)
|
lb.impl.ServeHTTP(srvs, rw, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON implements health.HealthMonitor.
|
// MarshalMap implements health.HealthMonitor.
|
||||||
func (lb *LoadBalancer) MarshalJSON() ([]byte, error) {
|
func (lb *LoadBalancer) MarshalMap() map[string]any {
|
||||||
extra := make(map[string]any)
|
extra := make(map[string]any)
|
||||||
lb.pool.RangeAll(func(k string, v Server) {
|
lb.pool.RangeAll(func(k string, v Server) {
|
||||||
extra[v.Key()] = v
|
extra[v.Key()] = v
|
||||||
})
|
})
|
||||||
|
|
||||||
return (&monitor.JSONRepresentation{
|
return (&health.JSONRepresentation{
|
||||||
Name: lb.Name(),
|
Name: lb.Name(),
|
||||||
Status: lb.Status(),
|
Status: lb.Status(),
|
||||||
Started: lb.startTime,
|
Started: lb.startTime,
|
||||||
|
@ -256,7 +255,7 @@ func (lb *LoadBalancer) MarshalJSON() ([]byte, error) {
|
||||||
"config": lb.Config,
|
"config": lb.Config,
|
||||||
"pool": extra,
|
"pool": extra,
|
||||||
},
|
},
|
||||||
}).MarshalJSON()
|
}).MarshalMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name implements health.HealthMonitor.
|
// Name implements health.HealthMonitor.
|
||||||
|
|
|
@ -2,9 +2,9 @@ package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
|
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
|
||||||
net "github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
U "github.com/yusing/go-proxy/internal/utils"
|
U "github.com/yusing/go-proxy/internal/utils"
|
||||||
F "github.com/yusing/go-proxy/internal/utils/functional"
|
F "github.com/yusing/go-proxy/internal/utils/functional"
|
||||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||||
|
@ -15,7 +15,7 @@ type (
|
||||||
_ U.NoCopy
|
_ U.NoCopy
|
||||||
|
|
||||||
name string
|
name string
|
||||||
url *net.URL
|
url *url.URL
|
||||||
weight Weight
|
weight Weight
|
||||||
|
|
||||||
http.Handler `json:"-"`
|
http.Handler `json:"-"`
|
||||||
|
@ -27,7 +27,7 @@ type (
|
||||||
health.HealthMonitor
|
health.HealthMonitor
|
||||||
Name() string
|
Name() string
|
||||||
Key() string
|
Key() string
|
||||||
URL() *net.URL
|
URL() *url.URL
|
||||||
Weight() Weight
|
Weight() Weight
|
||||||
SetWeight(weight Weight)
|
SetWeight(weight Weight)
|
||||||
TryWake() error
|
TryWake() error
|
||||||
|
@ -38,7 +38,7 @@ type (
|
||||||
|
|
||||||
var NewServerPool = F.NewMap[Pool]
|
var NewServerPool = F.NewMap[Pool]
|
||||||
|
|
||||||
func NewServer(name string, url *net.URL, weight Weight, handler http.Handler, healthMon health.HealthMonitor) Server {
|
func NewServer(name string, url *url.URL, weight Weight, handler http.Handler, healthMon health.HealthMonitor) Server {
|
||||||
srv := &server{
|
srv := &server{
|
||||||
name: name,
|
name: name,
|
||||||
url: url,
|
url: url,
|
||||||
|
@ -52,7 +52,7 @@ func NewServer(name string, url *net.URL, weight Weight, handler http.Handler, h
|
||||||
func TestNewServer[T ~int | ~float32 | ~float64](weight T) Server {
|
func TestNewServer[T ~int | ~float32 | ~float64](weight T) Server {
|
||||||
srv := &server{
|
srv := &server{
|
||||||
weight: Weight(weight),
|
weight: Weight(weight),
|
||||||
url: net.MustParseURL("http://localhost"),
|
url: &url.URL{Scheme: "http", Host: "localhost"},
|
||||||
}
|
}
|
||||||
return srv
|
return srv
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ func (srv *server) Name() string {
|
||||||
return srv.name
|
return srv.name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *server) URL() *net.URL {
|
func (srv *server) URL() *url.URL {
|
||||||
return srv.url
|
return srv.url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
|
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
"github.com/yusing/go-proxy/internal/utils"
|
"github.com/yusing/go-proxy/internal/utils"
|
||||||
F "github.com/yusing/go-proxy/internal/utils/functional"
|
F "github.com/yusing/go-proxy/internal/utils/functional"
|
||||||
)
|
)
|
||||||
|
@ -18,8 +17,8 @@ type (
|
||||||
cachedAddr F.Map[string, bool] // cache for trusted IPs
|
cachedAddr F.Map[string, bool] // cache for trusted IPs
|
||||||
}
|
}
|
||||||
CIDRWhitelistOpts struct {
|
CIDRWhitelistOpts struct {
|
||||||
Allow []*types.CIDR `validate:"min=1"`
|
Allow []*net.IPNet `validate:"min=1"`
|
||||||
StatusCode int `json:"status_code" aliases:"status" validate:"omitempty,status_code"`
|
StatusCode int `json:"status_code" aliases:"status" validate:"omitempty,status_code"`
|
||||||
Message string
|
Message string
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -27,7 +26,7 @@ type (
|
||||||
var (
|
var (
|
||||||
CIDRWhiteList = NewMiddleware[cidrWhitelist]()
|
CIDRWhiteList = NewMiddleware[cidrWhitelist]()
|
||||||
cidrWhitelistDefaults = CIDRWhitelistOpts{
|
cidrWhitelistDefaults = CIDRWhitelistOpts{
|
||||||
Allow: []*types.CIDR{},
|
Allow: []*net.IPNet{},
|
||||||
StatusCode: http.StatusForbidden,
|
StatusCode: http.StatusForbidden,
|
||||||
Message: "IP not allowed",
|
Message: "IP not allowed",
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/common"
|
"github.com/yusing/go-proxy/internal/common"
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
"github.com/yusing/go-proxy/internal/logging"
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
"github.com/yusing/go-proxy/internal/utils/atomic"
|
"github.com/yusing/go-proxy/internal/utils/atomic"
|
||||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||||
)
|
)
|
||||||
|
@ -33,7 +32,7 @@ var (
|
||||||
cfCIDRsMu sync.Mutex
|
cfCIDRsMu sync.Mutex
|
||||||
|
|
||||||
// RFC 1918.
|
// RFC 1918.
|
||||||
localCIDRs = []*types.CIDR{
|
localCIDRs = []*net.IPNet{
|
||||||
{IP: net.IPv4(127, 0, 0, 1), Mask: net.IPv4Mask(255, 255, 255, 255)}, // 127.0.0.1/32
|
{IP: net.IPv4(127, 0, 0, 1), Mask: net.IPv4Mask(255, 255, 255, 255)}, // 127.0.0.1/32
|
||||||
{IP: net.IPv4(10, 0, 0, 0), Mask: net.IPv4Mask(255, 0, 0, 0)}, // 10.0.0.0/8
|
{IP: net.IPv4(10, 0, 0, 0), Mask: net.IPv4Mask(255, 0, 0, 0)}, // 10.0.0.0/8
|
||||||
{IP: net.IPv4(172, 16, 0, 0), Mask: net.IPv4Mask(255, 240, 0, 0)}, // 172.16.0.0/12
|
{IP: net.IPv4(172, 16, 0, 0), Mask: net.IPv4Mask(255, 240, 0, 0)}, // 172.16.0.0/12
|
||||||
|
@ -68,7 +67,7 @@ func (cri *cloudflareRealIP) getTracer() *Tracer {
|
||||||
return cri.realIP.getTracer()
|
return cri.realIP.getTracer()
|
||||||
}
|
}
|
||||||
|
|
||||||
func tryFetchCFCIDR() (cfCIDRs []*types.CIDR) {
|
func tryFetchCFCIDR() (cfCIDRs []*net.IPNet) {
|
||||||
if time.Since(cfCIDRsLastUpdate.Load()) < cfCIDRsUpdateInterval {
|
if time.Since(cfCIDRsLastUpdate.Load()) < cfCIDRsUpdateInterval {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -83,7 +82,7 @@ func tryFetchCFCIDR() (cfCIDRs []*types.CIDR) {
|
||||||
if common.IsTest {
|
if common.IsTest {
|
||||||
cfCIDRs = localCIDRs
|
cfCIDRs = localCIDRs
|
||||||
} else {
|
} else {
|
||||||
cfCIDRs = make([]*types.CIDR, 0, 30)
|
cfCIDRs = make([]*net.IPNet, 0, 30)
|
||||||
err := errors.Join(
|
err := errors.Join(
|
||||||
fetchUpdateCFIPRange(cfIPv4CIDRsEndpoint, &cfCIDRs),
|
fetchUpdateCFIPRange(cfIPv4CIDRsEndpoint, &cfCIDRs),
|
||||||
fetchUpdateCFIPRange(cfIPv6CIDRsEndpoint, &cfCIDRs),
|
fetchUpdateCFIPRange(cfIPv6CIDRsEndpoint, &cfCIDRs),
|
||||||
|
@ -103,7 +102,7 @@ func tryFetchCFCIDR() (cfCIDRs []*types.CIDR) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchUpdateCFIPRange(endpoint string, cfCIDRs *[]*types.CIDR) error {
|
func fetchUpdateCFIPRange(endpoint string, cfCIDRs *[]*net.IPNet) error {
|
||||||
resp, err := http.Get(endpoint)
|
resp, err := http.Get(endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -124,7 +123,7 @@ func fetchUpdateCFIPRange(endpoint string, cfCIDRs *[]*types.CIDR) error {
|
||||||
return fmt.Errorf("cloudflare responeded an invalid CIDR: %s", line)
|
return fmt.Errorf("cloudflare responeded an invalid CIDR: %s", line)
|
||||||
}
|
}
|
||||||
|
|
||||||
*cfCIDRs = append(*cfCIDRs, (*types.CIDR)(cidr))
|
*cfCIDRs = append(*cfCIDRs, (*net.IPNet)(cidr))
|
||||||
}
|
}
|
||||||
*cfCIDRs = append(*cfCIDRs, localCIDRs...)
|
*cfCIDRs = append(*cfCIDRs, localCIDRs...)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -4,10 +4,10 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"slices"
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -51,8 +51,8 @@ func TestModifyRequest(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("request_headers", func(t *testing.T) {
|
t.Run("request_headers", func(t *testing.T) {
|
||||||
reqURL := types.MustParseURL("https://my.app/?arg_1=b")
|
reqURL := Must(url.Parse("https://my.app/?arg_1=b"))
|
||||||
upstreamURL := types.MustParseURL("http://test.example.com")
|
upstreamURL := Must(url.Parse("http://test.example.com"))
|
||||||
result, err := newMiddlewareTest(ModifyRequest, &testArgs{
|
result, err := newMiddlewareTest(ModifyRequest, &testArgs{
|
||||||
middlewareOpt: opts,
|
middlewareOpt: opts,
|
||||||
reqURL: reqURL,
|
reqURL: reqURL,
|
||||||
|
@ -128,8 +128,8 @@ func TestModifyRequest(t *testing.T) {
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
reqURL := types.MustParseURL("https://my.app" + tt.path)
|
reqURL := Must(url.Parse("https://my.app" + tt.path))
|
||||||
upstreamURL := types.MustParseURL(tt.upstreamURL)
|
upstreamURL := Must(url.Parse(tt.upstreamURL))
|
||||||
|
|
||||||
opts["add_prefix"] = tt.addPrefix
|
opts["add_prefix"] = tt.addPrefix
|
||||||
result, err := newMiddlewareTest(ModifyRequest, &testArgs{
|
result, err := newMiddlewareTest(ModifyRequest, &testArgs{
|
||||||
|
|
|
@ -4,10 +4,10 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"slices"
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -54,8 +54,8 @@ func TestModifyResponse(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("response_headers", func(t *testing.T) {
|
t.Run("response_headers", func(t *testing.T) {
|
||||||
reqURL := types.MustParseURL("https://my.app/?arg_1=b")
|
reqURL := Must(url.Parse("https://my.app/?arg_1=b"))
|
||||||
upstreamURL := types.MustParseURL("http://test.example.com")
|
upstreamURL := Must(url.Parse("http://test.example.com"))
|
||||||
result, err := newMiddlewareTest(ModifyResponse, &testArgs{
|
result, err := newMiddlewareTest(ModifyResponse, &testArgs{
|
||||||
middlewareOpt: opts,
|
middlewareOpt: opts,
|
||||||
reqURL: reqURL,
|
reqURL: reqURL,
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// https://nginx.org/en/docs/http/ngx_http_realip_module.html
|
// https://nginx.org/en/docs/http/ngx_http_realip_module.html
|
||||||
|
@ -19,7 +18,7 @@ type (
|
||||||
// Header is the name of the header to use for the real client IP
|
// Header is the name of the header to use for the real client IP
|
||||||
Header string `validate:"required"`
|
Header string `validate:"required"`
|
||||||
// From is a list of Address / CIDRs to trust
|
// From is a list of Address / CIDRs to trust
|
||||||
From []*types.CIDR `validate:"required,min=1"`
|
From []*net.IPNet `validate:"required,min=1"`
|
||||||
/*
|
/*
|
||||||
If recursive search is disabled,
|
If recursive search is disabled,
|
||||||
the original client address that matches one of the trusted addresses is replaced by
|
the original client address that matches one of the trusted addresses is replaced by
|
||||||
|
@ -36,7 +35,7 @@ var (
|
||||||
RealIP = NewMiddleware[realIP]()
|
RealIP = NewMiddleware[realIP]()
|
||||||
realIPOptsDefault = RealIPOpts{
|
realIPOptsDefault = RealIPOpts{
|
||||||
Header: "X-Real-IP",
|
Header: "X-Real-IP",
|
||||||
From: []*types.CIDR{},
|
From: []*net.IPNet{},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,7 +22,7 @@ func TestSetRealIPOpts(t *testing.T) {
|
||||||
}
|
}
|
||||||
optExpected := &RealIPOpts{
|
optExpected := &RealIPOpts{
|
||||||
Header: httpheaders.HeaderXRealIP,
|
Header: httpheaders.HeaderXRealIP,
|
||||||
From: []*types.CIDR{
|
From: []*net.IPNet{
|
||||||
{
|
{
|
||||||
IP: net.ParseIP("127.0.0.0"),
|
IP: net.ParseIP("127.0.0.0"),
|
||||||
Mask: net.IPv4Mask(255, 0, 0, 0),
|
Mask: net.IPv4Mask(255, 0, 0, 0),
|
||||||
|
|
|
@ -2,15 +2,15 @@ package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRedirectToHTTPs(t *testing.T) {
|
func TestRedirectToHTTPs(t *testing.T) {
|
||||||
result, err := newMiddlewareTest(RedirectHTTP, &testArgs{
|
result, err := newMiddlewareTest(RedirectHTTP, &testArgs{
|
||||||
reqURL: types.MustParseURL("http://example.com"),
|
reqURL: Must(url.Parse("http://example.com")),
|
||||||
})
|
})
|
||||||
ExpectNoError(t, err)
|
ExpectNoError(t, err)
|
||||||
ExpectEqual(t, result.ResponseStatus, http.StatusPermanentRedirect)
|
ExpectEqual(t, result.ResponseStatus, http.StatusPermanentRedirect)
|
||||||
|
@ -19,7 +19,7 @@ func TestRedirectToHTTPs(t *testing.T) {
|
||||||
|
|
||||||
func TestNoRedirect(t *testing.T) {
|
func TestNoRedirect(t *testing.T) {
|
||||||
result, err := newMiddlewareTest(RedirectHTTP, &testArgs{
|
result, err := newMiddlewareTest(RedirectHTTP, &testArgs{
|
||||||
reqURL: types.MustParseURL("https://example.com"),
|
reqURL: Must(url.Parse("https://example.com")),
|
||||||
})
|
})
|
||||||
ExpectNoError(t, err)
|
ExpectNoError(t, err)
|
||||||
ExpectEqual(t, result.ResponseStatus, http.StatusOK)
|
ExpectEqual(t, result.ResponseStatus, http.StatusOK)
|
||||||
|
|
|
@ -7,11 +7,11 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/common"
|
"github.com/yusing/go-proxy/internal/common"
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -80,11 +80,11 @@ type TestResult struct {
|
||||||
|
|
||||||
type testArgs struct {
|
type testArgs struct {
|
||||||
middlewareOpt OptionsRaw
|
middlewareOpt OptionsRaw
|
||||||
upstreamURL *types.URL
|
upstreamURL *url.URL
|
||||||
|
|
||||||
realRoundTrip bool
|
realRoundTrip bool
|
||||||
|
|
||||||
reqURL *types.URL
|
reqURL *url.URL
|
||||||
reqMethod string
|
reqMethod string
|
||||||
headers http.Header
|
headers http.Header
|
||||||
body []byte
|
body []byte
|
||||||
|
@ -96,13 +96,13 @@ type testArgs struct {
|
||||||
|
|
||||||
func (args *testArgs) setDefaults() {
|
func (args *testArgs) setDefaults() {
|
||||||
if args.reqURL == nil {
|
if args.reqURL == nil {
|
||||||
args.reqURL = Must(types.ParseURL("https://example.com"))
|
args.reqURL = Must(url.Parse("https://example.com"))
|
||||||
}
|
}
|
||||||
if args.reqMethod == "" {
|
if args.reqMethod == "" {
|
||||||
args.reqMethod = http.MethodGet
|
args.reqMethod = http.MethodGet
|
||||||
}
|
}
|
||||||
if args.upstreamURL == nil {
|
if args.upstreamURL == nil {
|
||||||
args.upstreamURL = Must(types.ParseURL("https://10.0.0.1:8443")) // dummy url, no actual effect
|
args.upstreamURL = Must(url.Parse("https://10.0.0.1:8443")) // dummy url, no actual effect
|
||||||
}
|
}
|
||||||
if args.respHeaders == nil {
|
if args.respHeaders == nil {
|
||||||
args.respHeaders = http.Header{}
|
args.respHeaders = http.Header{}
|
||||||
|
|
|
@ -28,7 +28,6 @@ import (
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
"github.com/yusing/go-proxy/internal/logging"
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp/accesslog"
|
"github.com/yusing/go-proxy/internal/net/gphttp/accesslog"
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
U "github.com/yusing/go-proxy/internal/utils"
|
U "github.com/yusing/go-proxy/internal/utils"
|
||||||
"golang.org/x/net/http/httpguts"
|
"golang.org/x/net/http/httpguts"
|
||||||
)
|
)
|
||||||
|
@ -93,7 +92,7 @@ type ReverseProxy struct {
|
||||||
HandlerFunc http.HandlerFunc
|
HandlerFunc http.HandlerFunc
|
||||||
|
|
||||||
TargetName string
|
TargetName string
|
||||||
TargetURL *types.URL
|
TargetURL *url.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
func singleJoiningSlash(a, b string) string {
|
func singleJoiningSlash(a, b string) string {
|
||||||
|
@ -133,7 +132,7 @@ func joinURLPath(a, b *url.URL) (path, rawpath string) {
|
||||||
// URLs to the scheme, host, and base path provided in target. If the
|
// URLs to the scheme, host, and base path provided in target. If the
|
||||||
// target's path is "/base" and the incoming request was for "/dir",
|
// target's path is "/base" and the incoming request was for "/dir",
|
||||||
// the target request will be for /base/dir.
|
// the target request will be for /base/dir.
|
||||||
func NewReverseProxy(name string, target *types.URL, transport http.RoundTripper) *ReverseProxy {
|
func NewReverseProxy(name string, target *url.URL, transport http.RoundTripper) *ReverseProxy {
|
||||||
if transport == nil {
|
if transport == nil {
|
||||||
panic("nil transport")
|
panic("nil transport")
|
||||||
}
|
}
|
||||||
|
@ -151,7 +150,7 @@ func (p *ReverseProxy) rewriteRequestURL(req *http.Request) {
|
||||||
targetQuery := p.TargetURL.RawQuery
|
targetQuery := p.TargetURL.RawQuery
|
||||||
req.URL.Scheme = p.TargetURL.Scheme
|
req.URL.Scheme = p.TargetURL.Scheme
|
||||||
req.URL.Host = p.TargetURL.Host
|
req.URL.Host = p.TargetURL.Host
|
||||||
req.URL.Path, req.URL.RawPath = joinURLPath(&p.TargetURL.URL, req.URL)
|
req.URL.Path, req.URL.RawPath = joinURLPath(p.TargetURL, req.URL)
|
||||||
if targetQuery == "" || req.URL.RawQuery == "" {
|
if targetQuery == "" || req.URL.RawQuery == "" {
|
||||||
req.URL.RawQuery = targetQuery + req.URL.RawQuery
|
req.URL.RawQuery = targetQuery + req.URL.RawQuery
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
//nolint:recvcheck
|
|
||||||
type CIDR net.IPNet
|
|
||||||
|
|
||||||
func ParseCIDR(v string) (cidr CIDR, err error) {
|
|
||||||
err = cidr.Parse(v)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cidr *CIDR) Parse(v string) error {
|
|
||||||
if !strings.Contains(v, "/") {
|
|
||||||
v += "/32" // single IP
|
|
||||||
}
|
|
||||||
_, ipnet, err := net.ParseCIDR(v)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cidr.IP = ipnet.IP
|
|
||||||
cidr.Mask = ipnet.Mask
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cidr CIDR) Contains(ip net.IP) bool {
|
|
||||||
return (*net.IPNet)(&cidr).Contains(ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cidr CIDR) String() string {
|
|
||||||
return (*net.IPNet)(&cidr).String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cidr CIDR) MarshalText() ([]byte, error) {
|
|
||||||
return []byte(cidr.String()), nil
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
urlPkg "net/url"
|
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
type URL struct {
|
|
||||||
_ utils.NoCopy
|
|
||||||
urlPkg.URL
|
|
||||||
}
|
|
||||||
|
|
||||||
func MustParseURL(url string) *URL {
|
|
||||||
u, err := ParseURL(url)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseURL(url string) (*URL, error) {
|
|
||||||
u := &URL{}
|
|
||||||
return u, u.Parse(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewURL(url *urlPkg.URL) *URL {
|
|
||||||
return &URL{URL: *url}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *URL) Parse(url string) error {
|
|
||||||
uu, err := urlPkg.Parse(url)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
u.URL = *uu
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *URL) String() string {
|
|
||||||
if u == nil {
|
|
||||||
return "nil"
|
|
||||||
}
|
|
||||||
return u.URL.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *URL) MarshalJSON() (text []byte, err error) {
|
|
||||||
if u == nil {
|
|
||||||
return []byte("null"), nil
|
|
||||||
}
|
|
||||||
return []byte("\"" + u.URL.String() + "\""), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *URL) Equals(other *URL) bool {
|
|
||||||
return u.String() == other.String()
|
|
||||||
}
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/network"
|
"github.com/docker/docker/api/types/network"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/yusing/go-proxy/internal/common"
|
"github.com/yusing/go-proxy/internal/common"
|
||||||
|
@ -21,7 +21,7 @@ const (
|
||||||
testDockerIP = "172.17.0.123"
|
testDockerIP = "172.17.0.123"
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeRoutes(cont *types.Container, dockerHostIP ...string) route.Routes {
|
func makeRoutes(cont *container.Summary, dockerHostIP ...string) route.Routes {
|
||||||
var p DockerProvider
|
var p DockerProvider
|
||||||
var host string
|
var host string
|
||||||
if len(dockerHostIP) > 0 {
|
if len(dockerHostIP) > 0 {
|
||||||
|
@ -64,7 +64,7 @@ func TestApplyLabel(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
entries := makeRoutes(&types.Container{
|
entries := makeRoutes(&container.Summary{
|
||||||
Names: dummyNames,
|
Names: dummyNames,
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
D.LabelAliases: "a,b",
|
D.LabelAliases: "a,b",
|
||||||
|
@ -135,7 +135,7 @@ func TestApplyLabel(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestApplyLabelWithAlias(t *testing.T) {
|
func TestApplyLabelWithAlias(t *testing.T) {
|
||||||
entries := makeRoutes(&types.Container{
|
entries := makeRoutes(&container.Summary{
|
||||||
Names: dummyNames,
|
Names: dummyNames,
|
||||||
State: "running",
|
State: "running",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
|
@ -162,7 +162,7 @@ func TestApplyLabelWithAlias(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestApplyLabelWithRef(t *testing.T) {
|
func TestApplyLabelWithRef(t *testing.T) {
|
||||||
entries := makeRoutes(&types.Container{
|
entries := makeRoutes(&container.Summary{
|
||||||
Names: dummyNames,
|
Names: dummyNames,
|
||||||
State: "running",
|
State: "running",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
|
@ -190,7 +190,7 @@ func TestApplyLabelWithRef(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestApplyLabelWithRefIndexError(t *testing.T) {
|
func TestApplyLabelWithRefIndexError(t *testing.T) {
|
||||||
c := D.FromDocker(&types.Container{
|
c := D.FromDocker(&container.Summary{
|
||||||
Names: dummyNames,
|
Names: dummyNames,
|
||||||
State: "running",
|
State: "running",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
|
@ -204,7 +204,7 @@ func TestApplyLabelWithRefIndexError(t *testing.T) {
|
||||||
_, err := p.routesFromContainerLabels(c)
|
_, err := p.routesFromContainerLabels(c)
|
||||||
ExpectError(t, ErrAliasRefIndexOutOfRange, err)
|
ExpectError(t, ErrAliasRefIndexOutOfRange, err)
|
||||||
|
|
||||||
c = D.FromDocker(&types.Container{
|
c = D.FromDocker(&container.Summary{
|
||||||
Names: dummyNames,
|
Names: dummyNames,
|
||||||
State: "running",
|
State: "running",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
|
@ -217,7 +217,7 @@ func TestApplyLabelWithRefIndexError(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDynamicAliases(t *testing.T) {
|
func TestDynamicAliases(t *testing.T) {
|
||||||
c := &types.Container{
|
c := &container.Summary{
|
||||||
Names: []string{"app1"},
|
Names: []string{"app1"},
|
||||||
State: "running",
|
State: "running",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
|
@ -240,7 +240,7 @@ func TestDynamicAliases(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDisableHealthCheck(t *testing.T) {
|
func TestDisableHealthCheck(t *testing.T) {
|
||||||
c := &types.Container{
|
c := &container.Summary{
|
||||||
Names: dummyNames,
|
Names: dummyNames,
|
||||||
State: "running",
|
State: "running",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
|
@ -254,7 +254,7 @@ func TestDisableHealthCheck(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPublicIPLocalhost(t *testing.T) {
|
func TestPublicIPLocalhost(t *testing.T) {
|
||||||
c := &types.Container{Names: dummyNames, State: "running"}
|
c := &container.Summary{Names: dummyNames, State: "running"}
|
||||||
r, ok := makeRoutes(c)["a"]
|
r, ok := makeRoutes(c)["a"]
|
||||||
ExpectTrue(t, ok)
|
ExpectTrue(t, ok)
|
||||||
ExpectEqual(t, r.Container.PublicHostname, "127.0.0.1")
|
ExpectEqual(t, r.Container.PublicHostname, "127.0.0.1")
|
||||||
|
@ -262,7 +262,7 @@ func TestPublicIPLocalhost(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPublicIPRemote(t *testing.T) {
|
func TestPublicIPRemote(t *testing.T) {
|
||||||
c := &types.Container{Names: dummyNames, State: "running"}
|
c := &container.Summary{Names: dummyNames, State: "running"}
|
||||||
raw, ok := makeRoutes(c, testIP)["a"]
|
raw, ok := makeRoutes(c, testIP)["a"]
|
||||||
ExpectTrue(t, ok)
|
ExpectTrue(t, ok)
|
||||||
ExpectEqual(t, raw.Container.PublicHostname, testIP)
|
ExpectEqual(t, raw.Container.PublicHostname, testIP)
|
||||||
|
@ -270,9 +270,9 @@ func TestPublicIPRemote(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrivateIPLocalhost(t *testing.T) {
|
func TestPrivateIPLocalhost(t *testing.T) {
|
||||||
c := &types.Container{
|
c := &container.Summary{
|
||||||
Names: dummyNames,
|
Names: dummyNames,
|
||||||
NetworkSettings: &types.SummaryNetworkSettings{
|
NetworkSettings: &container.NetworkSettingsSummary{
|
||||||
Networks: map[string]*network.EndpointSettings{
|
Networks: map[string]*network.EndpointSettings{
|
||||||
"network": {
|
"network": {
|
||||||
IPAddress: testDockerIP,
|
IPAddress: testDockerIP,
|
||||||
|
@ -287,10 +287,10 @@ func TestPrivateIPLocalhost(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrivateIPRemote(t *testing.T) {
|
func TestPrivateIPRemote(t *testing.T) {
|
||||||
c := &types.Container{
|
c := &container.Summary{
|
||||||
Names: dummyNames,
|
Names: dummyNames,
|
||||||
State: "running",
|
State: "running",
|
||||||
NetworkSettings: &types.SummaryNetworkSettings{
|
NetworkSettings: &container.NetworkSettingsSummary{
|
||||||
Networks: map[string]*network.EndpointSettings{
|
Networks: map[string]*network.EndpointSettings{
|
||||||
"network": {
|
"network": {
|
||||||
IPAddress: testDockerIP,
|
IPAddress: testDockerIP,
|
||||||
|
@ -309,17 +309,17 @@ func TestStreamDefaultValues(t *testing.T) {
|
||||||
privPort := uint16(1234)
|
privPort := uint16(1234)
|
||||||
pubPort := uint16(4567)
|
pubPort := uint16(4567)
|
||||||
privIP := "172.17.0.123"
|
privIP := "172.17.0.123"
|
||||||
cont := &types.Container{
|
cont := &container.Summary{
|
||||||
Names: []string{"a"},
|
Names: []string{"a"},
|
||||||
State: "running",
|
State: "running",
|
||||||
NetworkSettings: &types.SummaryNetworkSettings{
|
NetworkSettings: &container.NetworkSettingsSummary{
|
||||||
Networks: map[string]*network.EndpointSettings{
|
Networks: map[string]*network.EndpointSettings{
|
||||||
"network": {
|
"network": {
|
||||||
IPAddress: privIP,
|
IPAddress: privIP,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Ports: []types.Port{
|
Ports: []container.Port{
|
||||||
{Type: "udp", PrivatePort: privPort, PublicPort: pubPort},
|
{Type: "udp", PrivatePort: privPort, PublicPort: pubPort},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -346,7 +346,7 @@ func TestStreamDefaultValues(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExplicitExclude(t *testing.T) {
|
func TestExplicitExclude(t *testing.T) {
|
||||||
r, ok := makeRoutes(&types.Container{
|
r, ok := makeRoutes(&container.Summary{
|
||||||
Names: dummyNames,
|
Names: dummyNames,
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
D.LabelAliases: "a",
|
D.LabelAliases: "a",
|
||||||
|
@ -360,9 +360,9 @@ func TestExplicitExclude(t *testing.T) {
|
||||||
|
|
||||||
func TestImplicitExcludeDatabase(t *testing.T) {
|
func TestImplicitExcludeDatabase(t *testing.T) {
|
||||||
t.Run("mount path detection", func(t *testing.T) {
|
t.Run("mount path detection", func(t *testing.T) {
|
||||||
r, ok := makeRoutes(&types.Container{
|
r, ok := makeRoutes(&container.Summary{
|
||||||
Names: dummyNames,
|
Names: dummyNames,
|
||||||
Mounts: []types.MountPoint{
|
Mounts: []container.MountPoint{
|
||||||
{Source: "/data", Destination: "/var/lib/postgresql/data"},
|
{Source: "/data", Destination: "/var/lib/postgresql/data"},
|
||||||
},
|
},
|
||||||
})["a"]
|
})["a"]
|
||||||
|
@ -370,9 +370,9 @@ func TestImplicitExcludeDatabase(t *testing.T) {
|
||||||
ExpectTrue(t, r.ShouldExclude())
|
ExpectTrue(t, r.ShouldExclude())
|
||||||
})
|
})
|
||||||
t.Run("exposed port detection", func(t *testing.T) {
|
t.Run("exposed port detection", func(t *testing.T) {
|
||||||
r, ok := makeRoutes(&types.Container{
|
r, ok := makeRoutes(&container.Summary{
|
||||||
Names: dummyNames,
|
Names: dummyNames,
|
||||||
Ports: []types.Port{
|
Ports: []container.Port{
|
||||||
{Type: "tcp", PrivatePort: 5432, PublicPort: 5432},
|
{Type: "tcp", PrivatePort: 5432, PublicPort: 5432},
|
||||||
},
|
},
|
||||||
})["a"]
|
})["a"]
|
||||||
|
|
|
@ -128,7 +128,7 @@ func (p *Provider) loadRoutes() (routes route.Routes, err gperr.Error) {
|
||||||
if err != nil && len(routes) == 0 {
|
if err != nil && len(routes) == 0 {
|
||||||
return route.Routes{}, err
|
return route.Routes{}, err
|
||||||
}
|
}
|
||||||
errs := gperr.NewBuilder("routes error")
|
errs := gperr.NewBuilder()
|
||||||
errs.Add(err)
|
errs.Add(err)
|
||||||
// check for exclusion
|
// check for exclusion
|
||||||
// set alias and provider, then validate
|
// set alias and provider, then validate
|
||||||
|
|
|
@ -6,10 +6,9 @@ import (
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||||
"github.com/yusing/go-proxy/agent/pkg/agentproxy"
|
"github.com/yusing/go-proxy/agent/pkg/agentproxy"
|
||||||
"github.com/yusing/go-proxy/internal/api/v1/favicon"
|
|
||||||
"github.com/yusing/go-proxy/internal/common"
|
"github.com/yusing/go-proxy/internal/common"
|
||||||
"github.com/yusing/go-proxy/internal/docker"
|
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
|
"github.com/yusing/go-proxy/internal/homepage"
|
||||||
"github.com/yusing/go-proxy/internal/idlewatcher"
|
"github.com/yusing/go-proxy/internal/idlewatcher"
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
"github.com/yusing/go-proxy/internal/logging"
|
||||||
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
|
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
|
||||||
|
@ -104,10 +103,10 @@ func (r *ReveseProxyRoute) Start(parent task.Parent) gperr.Error {
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case r.UseIdleWatcher():
|
case r.UseIdleWatcher():
|
||||||
waker, err := idlewatcher.NewHTTPWaker(parent, r, r.rp)
|
waker, err := idlewatcher.NewWatcher(parent, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.task.Finish(err)
|
r.task.Finish(err)
|
||||||
return err
|
return gperr.Wrap(err, "idlewatcher error")
|
||||||
}
|
}
|
||||||
r.handler = waker
|
r.handler = waker
|
||||||
r.HealthMon = waker
|
r.HealthMon = waker
|
||||||
|
@ -191,6 +190,10 @@ func (r *ReveseProxyRoute) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
r.handler.ServeHTTP(w, req)
|
r.handler.ServeHTTP(w, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ReveseProxyRoute) ReverseProxy() *reverseproxy.ReverseProxy {
|
||||||
|
return r.rp
|
||||||
|
}
|
||||||
|
|
||||||
func (r *ReveseProxyRoute) HealthMonitor() health.HealthMonitor {
|
func (r *ReveseProxyRoute) HealthMonitor() health.HealthMonitor {
|
||||||
return r.HealthMon
|
return r.HealthMon
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
"github.com/yusing/go-proxy/internal/homepage"
|
"github.com/yusing/go-proxy/internal/homepage"
|
||||||
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
|
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
|
||||||
net "github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
"github.com/yusing/go-proxy/internal/task"
|
"github.com/yusing/go-proxy/internal/task"
|
||||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||||
|
@ -52,8 +51,8 @@ type (
|
||||||
Provider string `json:"provider,omitempty"`
|
Provider string `json:"provider,omitempty"`
|
||||||
|
|
||||||
// private fields
|
// private fields
|
||||||
LisURL *net.URL `json:"lurl,omitempty"`
|
LisURL *url.URL `json:"lurl,omitempty"`
|
||||||
ProxyURL *net.URL `json:"purl,omitempty"`
|
ProxyURL *url.URL `json:"purl,omitempty"`
|
||||||
|
|
||||||
Idlewatcher *idlewatcher.Config `json:"idlewatcher,omitempty"`
|
Idlewatcher *idlewatcher.Config `json:"idlewatcher,omitempty"`
|
||||||
|
|
||||||
|
@ -88,7 +87,7 @@ func (r *Route) Validate() (err gperr.Error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs.Add(err)
|
errs.Add(err)
|
||||||
}
|
}
|
||||||
r.ProxyURL = gperr.Collect(errs, net.ParseURL, "file://"+r.Root)
|
r.ProxyURL = gperr.Collect(errs, url.Parse, "file://"+r.Root)
|
||||||
r.Host = ""
|
r.Host = ""
|
||||||
r.Port.Proxy = 0
|
r.Port.Proxy = 0
|
||||||
} else {
|
} else {
|
||||||
|
@ -98,9 +97,9 @@ func (r *Route) Validate() (err gperr.Error) {
|
||||||
errs.Addf("unexpected listening port for %s scheme", r.Scheme)
|
errs.Addf("unexpected listening port for %s scheme", r.Scheme)
|
||||||
}
|
}
|
||||||
case route.SchemeTCP, route.SchemeUDP:
|
case route.SchemeTCP, route.SchemeUDP:
|
||||||
r.LisURL = gperr.Collect(errs, net.ParseURL, fmt.Sprintf("%s://:%d", r.Scheme, r.Port.Listening))
|
r.LisURL = gperr.Collect(errs, url.Parse, fmt.Sprintf("%s://:%d", r.Scheme, r.Port.Listening))
|
||||||
}
|
}
|
||||||
r.ProxyURL = gperr.Collect(errs, net.ParseURL, fmt.Sprintf("%s://%s:%d", r.Scheme, r.Host, r.Port.Proxy))
|
r.ProxyURL = gperr.Collect(errs, url.Parse, fmt.Sprintf("%s://%s:%d", r.Scheme, r.Host, r.Port.Proxy))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !r.UseHealthCheck() && (r.UseLoadBalance() || r.UseIdleWatcher()) {
|
if !r.UseHealthCheck() && (r.UseLoadBalance() || r.UseIdleWatcher()) {
|
||||||
|
@ -160,7 +159,7 @@ func (r *Route) TargetName() string {
|
||||||
return r.Alias
|
return r.Alias
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Route) TargetURL() *net.URL {
|
func (r *Route) TargetURL() *url.URL {
|
||||||
return r.ProxyURL
|
return r.ProxyURL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package rules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -9,7 +10,6 @@ import (
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
|
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
|
||||||
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ var commands = map[string]struct {
|
||||||
},
|
},
|
||||||
validate: validateURL,
|
validate: validateURL,
|
||||||
build: func(args any) CommandHandler {
|
build: func(args any) CommandHandler {
|
||||||
target := args.(*types.URL).String()
|
target := args.(*url.URL).String()
|
||||||
return ReturningCommand(func(w http.ResponseWriter, r *http.Request) {
|
return ReturningCommand(func(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
|
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
|
||||||
})
|
})
|
||||||
|
@ -160,7 +160,7 @@ var commands = map[string]struct {
|
||||||
},
|
},
|
||||||
validate: validateAbsoluteURL,
|
validate: validateAbsoluteURL,
|
||||||
build: func(args any) CommandHandler {
|
build: func(args any) CommandHandler {
|
||||||
target := args.(*types.URL)
|
target := args.(*url.URL)
|
||||||
if target.Scheme == "" {
|
if target.Scheme == "" {
|
||||||
target.Scheme = "http"
|
target.Scheme = "http"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package rules
|
package rules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -205,7 +205,7 @@ var checkers = map[string]struct {
|
||||||
},
|
},
|
||||||
validate: validateCIDR,
|
validate: validateCIDR,
|
||||||
builder: func(args any) CheckFunc {
|
builder: func(args any) CheckFunc {
|
||||||
cidr := args.(types.CIDR)
|
cidr := args.(*net.IPNet)
|
||||||
return func(cached Cache, r *http.Request) bool {
|
return func(cached Cache, r *http.Request) bool {
|
||||||
ip := cached.GetRemoteIP(r)
|
ip := cached.GetRemoteIP(r)
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
|
|
|
@ -2,13 +2,14 @@ package rules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
|
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -48,24 +49,24 @@ func toKVOptionalV(args []string) (any, gperr.Error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateURL returns types.URL with the URL validated.
|
// validateURL returns url.URL with the URL validated.
|
||||||
func validateURL(args []string) (any, gperr.Error) {
|
func validateURL(args []string) (any, gperr.Error) {
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return nil, ErrExpectOneArg
|
return nil, ErrExpectOneArg
|
||||||
}
|
}
|
||||||
u, err := types.ParseURL(args[0])
|
u, err := url.Parse(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrInvalidArguments.With(err)
|
return nil, ErrInvalidArguments.With(err)
|
||||||
}
|
}
|
||||||
return u, nil
|
return u, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateAbsoluteURL returns types.URL with the URL validated.
|
// validateAbsoluteURL returns url.URL with the URL validated.
|
||||||
func validateAbsoluteURL(args []string) (any, gperr.Error) {
|
func validateAbsoluteURL(args []string) (any, gperr.Error) {
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return nil, ErrExpectOneArg
|
return nil, ErrExpectOneArg
|
||||||
}
|
}
|
||||||
u, err := types.ParseURL(args[0])
|
u, err := url.Parse(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrInvalidArguments.With(err)
|
return nil, ErrInvalidArguments.With(err)
|
||||||
}
|
}
|
||||||
|
@ -86,7 +87,7 @@ func validateCIDR(args []string) (any, gperr.Error) {
|
||||||
if !strings.Contains(args[0], "/") {
|
if !strings.Contains(args[0], "/") {
|
||||||
args[0] += "/32"
|
args[0] += "/32"
|
||||||
}
|
}
|
||||||
cidr, err := types.ParseCIDR(args[0])
|
_, cidr, err := net.ParseCIDR(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrInvalidArguments.With(err)
|
return nil, ErrInvalidArguments.With(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/yusing/go-proxy/internal/docker"
|
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
"github.com/yusing/go-proxy/internal/idlewatcher"
|
"github.com/yusing/go-proxy/internal/idlewatcher"
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
"github.com/yusing/go-proxy/internal/logging"
|
||||||
|
@ -58,10 +57,10 @@ func (r *StreamRoute) Start(parent task.Parent) gperr.Error {
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case r.UseIdleWatcher():
|
case r.UseIdleWatcher():
|
||||||
waker, err := idlewatcher.NewStreamWaker(parent, r, r.Stream)
|
waker, err := idlewatcher.NewWatcher(parent, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.task.Finish(err)
|
r.task.Finish(err)
|
||||||
return err
|
return gperr.Wrap(err, "idlewatcher error")
|
||||||
}
|
}
|
||||||
r.Stream = waker
|
r.Stream = waker
|
||||||
r.HealthMon = waker
|
r.HealthMon = waker
|
||||||
|
|
|
@ -2,6 +2,7 @@ package route
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||||
"github.com/yusing/go-proxy/internal/docker"
|
"github.com/yusing/go-proxy/internal/docker"
|
||||||
|
@ -22,7 +23,7 @@ type (
|
||||||
task.TaskFinisher
|
task.TaskFinisher
|
||||||
ProviderName() string
|
ProviderName() string
|
||||||
TargetName() string
|
TargetName() string
|
||||||
TargetURL() *net.URL
|
TargetURL() *url.URL
|
||||||
HealthMonitor() health.HealthMonitor
|
HealthMonitor() health.HealthMonitor
|
||||||
Reference() string
|
Reference() string
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -42,7 +45,14 @@ var (
|
||||||
tagAliases = "aliases" // declare aliases for fields
|
tagAliases = "aliases" // declare aliases for fields
|
||||||
)
|
)
|
||||||
|
|
||||||
var mapUnmarshalerType = reflect.TypeFor[MapUnmarshaller]()
|
var (
|
||||||
|
typeDuration = reflect.TypeFor[time.Duration]()
|
||||||
|
typeURL = reflect.TypeFor[url.URL]()
|
||||||
|
typeCIDR = reflect.TypeFor[*net.IPNet]()
|
||||||
|
|
||||||
|
typeMapMarshaller = reflect.TypeFor[MapMarshaller]()
|
||||||
|
typeMapUnmarshaler = reflect.TypeFor[MapUnmarshaller]()
|
||||||
|
)
|
||||||
|
|
||||||
var defaultValues = functional.NewMapOf[reflect.Type, func() any]()
|
var defaultValues = functional.NewMapOf[reflect.Type, func() any]()
|
||||||
|
|
||||||
|
@ -196,7 +206,7 @@ func MapUnmarshalValidate(src SerializedObject, dst any) (err gperr.Error) {
|
||||||
return gperr.Errorf("unmarshal: src is %w and dst is not settable", ErrNilValue)
|
return gperr.Errorf("unmarshal: src is %w and dst is not settable", ErrNilValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
if dstT.Implements(mapUnmarshalerType) {
|
if dstT.Implements(typeMapUnmarshaler) {
|
||||||
dstV, _, err = dive(dstV)
|
dstV, _, err = dive(dstV)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -370,7 +380,7 @@ func Convert(src reflect.Value, dst reflect.Value) gperr.Error {
|
||||||
}
|
}
|
||||||
obj, ok := src.Interface().(SerializedObject)
|
obj, ok := src.Interface().(SerializedObject)
|
||||||
if !ok {
|
if !ok {
|
||||||
return ErrUnsupportedConversion.Subject(dstT.String() + " to " + srcT.String())
|
return ErrUnsupportedConversion.Subjectf("%s to %s", srcT, dstT)
|
||||||
}
|
}
|
||||||
return MapUnmarshalValidate(obj, dst.Addr().Interface())
|
return MapUnmarshalValidate(obj, dst.Addr().Interface())
|
||||||
case srcKind == reflect.Slice:
|
case srcKind == reflect.Slice:
|
||||||
|
@ -378,7 +388,7 @@ func Convert(src reflect.Value, dst reflect.Value) gperr.Error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if dstT.Kind() != reflect.Slice {
|
if dstT.Kind() != reflect.Slice {
|
||||||
return ErrUnsupportedConversion.Subject(dstT.String() + " to " + srcT.String())
|
return ErrUnsupportedConversion.Subjectf("%s to %s", srcT, dstT)
|
||||||
}
|
}
|
||||||
sliceErrs := gperr.NewBuilder("slice conversion errors")
|
sliceErrs := gperr.NewBuilder("slice conversion errors")
|
||||||
newSlice := reflect.MakeSlice(dstT, src.Len(), src.Len())
|
newSlice := reflect.MakeSlice(dstT, src.Len(), src.Len())
|
||||||
|
@ -402,6 +412,10 @@ func Convert(src reflect.Value, dst reflect.Value) gperr.Error {
|
||||||
return ErrUnsupportedConversion.Subjectf("%s to %s", srcT, dstT)
|
return ErrUnsupportedConversion.Subjectf("%s to %s", srcT, dstT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nilPointer[T any]() reflect.Value {
|
||||||
|
return reflect.ValueOf((*T)(nil))
|
||||||
|
}
|
||||||
|
|
||||||
func ConvertString(src string, dst reflect.Value) (convertible bool, convErr gperr.Error) {
|
func ConvertString(src string, dst reflect.Value) (convertible bool, convErr gperr.Error) {
|
||||||
convertible = true
|
convertible = true
|
||||||
dstT := dst.Type()
|
dstT := dst.Type()
|
||||||
|
@ -417,10 +431,10 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr gpe
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch dstT {
|
switch dstT {
|
||||||
case reflect.TypeFor[time.Duration]():
|
case typeDuration:
|
||||||
if src == "" {
|
if src == "" {
|
||||||
dst.Set(reflect.Zero(dstT))
|
dst.Set(reflect.Zero(dstT))
|
||||||
return
|
return false, nil
|
||||||
}
|
}
|
||||||
d, err := time.ParseDuration(src)
|
d, err := time.ParseDuration(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -431,7 +445,31 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr gpe
|
||||||
}
|
}
|
||||||
dst.Set(reflect.ValueOf(d))
|
dst.Set(reflect.ValueOf(d))
|
||||||
return
|
return
|
||||||
default:
|
case typeURL:
|
||||||
|
if src == "" {
|
||||||
|
dst.Addr().Set(nilPointer[*url.URL]())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u, err := url.Parse(src)
|
||||||
|
if err != nil {
|
||||||
|
return true, gperr.Wrap(err)
|
||||||
|
}
|
||||||
|
dst.Set(reflect.ValueOf(u).Elem())
|
||||||
|
return
|
||||||
|
case typeCIDR:
|
||||||
|
if src == "" {
|
||||||
|
dst.Addr().Set(nilPointer[*net.IPNet]())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !strings.Contains(src, "/") {
|
||||||
|
src += "/32" // single IP
|
||||||
|
}
|
||||||
|
_, ipnet, err := net.ParseCIDR(src)
|
||||||
|
if err != nil {
|
||||||
|
return true, gperr.Wrap(err)
|
||||||
|
}
|
||||||
|
dst.Set(reflect.ValueOf(ipnet).Elem())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if dstKind := dst.Kind(); isIntFloat(dstKind) {
|
if dstKind := dst.Kind(); isIntFloat(dstKind) {
|
||||||
var i any
|
var i any
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
@ -42,7 +45,7 @@ func TestDeserialize(t *testing.T) {
|
||||||
var s2 S
|
var s2 S
|
||||||
err := MapUnmarshalValidate(testStructSerialized, &s2)
|
err := MapUnmarshalValidate(testStructSerialized, &s2)
|
||||||
ExpectNoError(t, err)
|
ExpectNoError(t, err)
|
||||||
ExpectEqual(t, s2, testStruct)
|
ExpectEqualValues(t, s2, testStruct)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,15 +65,15 @@ func TestDeserializeAnonymousField(t *testing.T) {
|
||||||
// t.Fatalf("anon %v, all %v", anon, all)
|
// t.Fatalf("anon %v, all %v", anon, all)
|
||||||
err := MapUnmarshalValidate(map[string]any{"a": 1, "b": 2, "c": 3}, &s)
|
err := MapUnmarshalValidate(map[string]any{"a": 1, "b": 2, "c": 3}, &s)
|
||||||
ExpectNoError(t, err)
|
ExpectNoError(t, err)
|
||||||
ExpectEqual(t, s.A, 1)
|
ExpectEqualValues(t, s.A, 1)
|
||||||
ExpectEqual(t, s.B, 2)
|
ExpectEqualValues(t, s.B, 2)
|
||||||
ExpectEqual(t, s.C, 3)
|
ExpectEqualValues(t, s.C, 3)
|
||||||
|
|
||||||
err = MapUnmarshalValidate(map[string]any{"a": 1, "b": 2, "c": 3}, &s2)
|
err = MapUnmarshalValidate(map[string]any{"a": 1, "b": 2, "c": 3}, &s2)
|
||||||
ExpectNoError(t, err)
|
ExpectNoError(t, err)
|
||||||
ExpectEqual(t, s2.A, 1)
|
ExpectEqualValues(t, s2.A, 1)
|
||||||
ExpectEqual(t, s2.B, 2)
|
ExpectEqualValues(t, s2.B, 2)
|
||||||
ExpectEqual(t, s2.C, 3)
|
ExpectEqualValues(t, s2.C, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStringIntConvert(t *testing.T) {
|
func TestStringIntConvert(t *testing.T) {
|
||||||
|
@ -91,42 +94,42 @@ func TestStringIntConvert(t *testing.T) {
|
||||||
|
|
||||||
ExpectTrue(t, ok)
|
ExpectTrue(t, ok)
|
||||||
ExpectNoError(t, err)
|
ExpectNoError(t, err)
|
||||||
ExpectEqual(t, test.i8, int8(127))
|
ExpectEqualValues(t, test.i8, int8(127))
|
||||||
|
|
||||||
ok, err = ConvertString(s, reflect.ValueOf(&test.i16))
|
ok, err = ConvertString(s, reflect.ValueOf(&test.i16))
|
||||||
ExpectTrue(t, ok)
|
ExpectTrue(t, ok)
|
||||||
ExpectNoError(t, err)
|
ExpectNoError(t, err)
|
||||||
ExpectEqual(t, test.i16, int16(127))
|
ExpectEqualValues(t, test.i16, int16(127))
|
||||||
|
|
||||||
ok, err = ConvertString(s, reflect.ValueOf(&test.i32))
|
ok, err = ConvertString(s, reflect.ValueOf(&test.i32))
|
||||||
ExpectTrue(t, ok)
|
ExpectTrue(t, ok)
|
||||||
ExpectNoError(t, err)
|
ExpectNoError(t, err)
|
||||||
ExpectEqual(t, test.i32, int32(127))
|
ExpectEqualValues(t, test.i32, int32(127))
|
||||||
|
|
||||||
ok, err = ConvertString(s, reflect.ValueOf(&test.i64))
|
ok, err = ConvertString(s, reflect.ValueOf(&test.i64))
|
||||||
ExpectTrue(t, ok)
|
ExpectTrue(t, ok)
|
||||||
ExpectNoError(t, err)
|
ExpectNoError(t, err)
|
||||||
ExpectEqual(t, test.i64, int64(127))
|
ExpectEqualValues(t, test.i64, int64(127))
|
||||||
|
|
||||||
ok, err = ConvertString(s, reflect.ValueOf(&test.u8))
|
ok, err = ConvertString(s, reflect.ValueOf(&test.u8))
|
||||||
ExpectTrue(t, ok)
|
ExpectTrue(t, ok)
|
||||||
ExpectNoError(t, err)
|
ExpectNoError(t, err)
|
||||||
ExpectEqual(t, test.u8, uint8(127))
|
ExpectEqualValues(t, test.u8, uint8(127))
|
||||||
|
|
||||||
ok, err = ConvertString(s, reflect.ValueOf(&test.u16))
|
ok, err = ConvertString(s, reflect.ValueOf(&test.u16))
|
||||||
ExpectTrue(t, ok)
|
ExpectTrue(t, ok)
|
||||||
ExpectNoError(t, err)
|
ExpectNoError(t, err)
|
||||||
ExpectEqual(t, test.u16, uint16(127))
|
ExpectEqualValues(t, test.u16, uint16(127))
|
||||||
|
|
||||||
ok, err = ConvertString(s, reflect.ValueOf(&test.u32))
|
ok, err = ConvertString(s, reflect.ValueOf(&test.u32))
|
||||||
ExpectTrue(t, ok)
|
ExpectTrue(t, ok)
|
||||||
ExpectNoError(t, err)
|
ExpectNoError(t, err)
|
||||||
ExpectEqual(t, test.u32, uint32(127))
|
ExpectEqualValues(t, test.u32, uint32(127))
|
||||||
|
|
||||||
ok, err = ConvertString(s, reflect.ValueOf(&test.u64))
|
ok, err = ConvertString(s, reflect.ValueOf(&test.u64))
|
||||||
ExpectTrue(t, ok)
|
ExpectTrue(t, ok)
|
||||||
ExpectNoError(t, err)
|
ExpectNoError(t, err)
|
||||||
ExpectEqual(t, test.u64, uint64(127))
|
ExpectEqualValues(t, test.u64, uint64(127))
|
||||||
}
|
}
|
||||||
|
|
||||||
type testModel struct {
|
type testModel struct {
|
||||||
|
@ -150,19 +153,19 @@ func TestConvertor(t *testing.T) {
|
||||||
m := new(testModel)
|
m := new(testModel)
|
||||||
ExpectNoError(t, MapUnmarshalValidate(map[string]any{"Test": "123"}, m))
|
ExpectNoError(t, MapUnmarshalValidate(map[string]any{"Test": "123"}, m))
|
||||||
|
|
||||||
ExpectEqual(t, m.Test.foo, 123)
|
ExpectEqualValues(t, m.Test.foo, 123)
|
||||||
ExpectEqual(t, m.Test.bar, "123")
|
ExpectEqualValues(t, m.Test.bar, "123")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("int_to_string", func(t *testing.T) {
|
t.Run("int_to_string", func(t *testing.T) {
|
||||||
m := new(testModel)
|
m := new(testModel)
|
||||||
ExpectNoError(t, MapUnmarshalValidate(map[string]any{"Test": "123"}, m))
|
ExpectNoError(t, MapUnmarshalValidate(map[string]any{"Test": "123"}, m))
|
||||||
|
|
||||||
ExpectEqual(t, m.Test.foo, 123)
|
ExpectEqualValues(t, m.Test.foo, 123)
|
||||||
ExpectEqual(t, m.Test.bar, "123")
|
ExpectEqualValues(t, m.Test.bar, "123")
|
||||||
|
|
||||||
ExpectNoError(t, MapUnmarshalValidate(map[string]any{"Baz": 123}, m))
|
ExpectNoError(t, MapUnmarshalValidate(map[string]any{"Baz": 123}, m))
|
||||||
ExpectEqual(t, m.Baz, "123")
|
ExpectEqualValues(t, m.Baz, "123")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("invalid", func(t *testing.T) {
|
t.Run("invalid", func(t *testing.T) {
|
||||||
|
@ -177,21 +180,21 @@ func TestStringToSlice(t *testing.T) {
|
||||||
convertible, err := ConvertString("a,b,c", reflect.ValueOf(&dst))
|
convertible, err := ConvertString("a,b,c", reflect.ValueOf(&dst))
|
||||||
ExpectTrue(t, convertible)
|
ExpectTrue(t, convertible)
|
||||||
ExpectNoError(t, err)
|
ExpectNoError(t, err)
|
||||||
ExpectEqual(t, dst, []string{"a", "b", "c"})
|
ExpectEqualValues(t, dst, []string{"a", "b", "c"})
|
||||||
})
|
})
|
||||||
t.Run("yaml-like", func(t *testing.T) {
|
t.Run("yaml-like", func(t *testing.T) {
|
||||||
dst := make([]string, 0)
|
dst := make([]string, 0)
|
||||||
convertible, err := ConvertString("- a\n- b\n- c", reflect.ValueOf(&dst))
|
convertible, err := ConvertString("- a\n- b\n- c", reflect.ValueOf(&dst))
|
||||||
ExpectTrue(t, convertible)
|
ExpectTrue(t, convertible)
|
||||||
ExpectNoError(t, err)
|
ExpectNoError(t, err)
|
||||||
ExpectEqual(t, dst, []string{"a", "b", "c"})
|
ExpectEqualValues(t, dst, []string{"a", "b", "c"})
|
||||||
})
|
})
|
||||||
t.Run("single-line-yaml-like", func(t *testing.T) {
|
t.Run("single-line-yaml-like", func(t *testing.T) {
|
||||||
dst := make([]string, 0)
|
dst := make([]string, 0)
|
||||||
convertible, err := ConvertString("- a", reflect.ValueOf(&dst))
|
convertible, err := ConvertString("- a", reflect.ValueOf(&dst))
|
||||||
ExpectTrue(t, convertible)
|
ExpectTrue(t, convertible)
|
||||||
ExpectNoError(t, err)
|
ExpectNoError(t, err)
|
||||||
ExpectEqual(t, dst, []string{"a"})
|
ExpectEqualValues(t, dst, []string{"a"})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,7 +218,7 @@ func TestStringToMap(t *testing.T) {
|
||||||
convertible, err := ConvertString(" a: b\n c: d", reflect.ValueOf(&dst))
|
convertible, err := ConvertString(" a: b\n c: d", reflect.ValueOf(&dst))
|
||||||
ExpectTrue(t, convertible)
|
ExpectTrue(t, convertible)
|
||||||
ExpectNoError(t, err)
|
ExpectNoError(t, err)
|
||||||
ExpectEqual(t, dst, map[string]string{"a": "b", "c": "d"})
|
ExpectEqualValues(t, dst, map[string]string{"a": "b", "c": "d"})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +245,7 @@ func TestStringToStruct(t *testing.T) {
|
||||||
convertible, err := ConvertString(" A: a\n B: 123", reflect.ValueOf(&dst))
|
convertible, err := ConvertString(" A: a\n B: 123", reflect.ValueOf(&dst))
|
||||||
ExpectTrue(t, convertible)
|
ExpectTrue(t, convertible)
|
||||||
ExpectNoError(t, err)
|
ExpectNoError(t, err)
|
||||||
ExpectEqual(t, dst, struct {
|
ExpectEqualValues(t, dst, struct {
|
||||||
A string
|
A string
|
||||||
B int
|
B int
|
||||||
}{"a", 123})
|
}{"a", 123})
|
||||||
|
|
|
@ -21,50 +21,55 @@ func Must[Result any](r Result, err error) Result {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExpectNoError(t *testing.T, err error) {
|
func ExpectNoError(t *testing.T, err error, msgAndArgs ...any) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err, msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExpectHasError(t *testing.T, err error) {
|
func ExpectHasError(t *testing.T, err error, msgAndArgs ...any) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
require.Error(t, err)
|
require.Error(t, err, msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExpectError(t *testing.T, expected error, err error) {
|
func ExpectError(t *testing.T, expected error, err error, msgAndArgs ...any) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
require.ErrorIs(t, err, expected)
|
require.ErrorIs(t, err, expected, msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExpectErrorT[T error](t *testing.T, err error) {
|
func ExpectErrorT[T error](t *testing.T, err error, msgAndArgs ...any) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
var errAs T
|
var errAs T
|
||||||
require.ErrorAs(t, err, &errAs)
|
require.ErrorAs(t, err, &errAs, msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExpectEqual[T any](t *testing.T, got T, want T) {
|
func ExpectEqual[T any](t *testing.T, got T, want T, msgAndArgs ...any) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
require.EqualValues(t, got, want)
|
require.Equal(t, want, got, msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExpectContains[T any](t *testing.T, got T, wants []T) {
|
func ExpectEqualValues(t *testing.T, got any, want any, msgAndArgs ...any) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
require.Contains(t, wants, got)
|
require.EqualValues(t, want, got, msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExpectTrue(t *testing.T, got bool) {
|
func ExpectContains[T any](t *testing.T, got T, wants []T, msgAndArgs ...any) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
require.True(t, got)
|
require.Contains(t, wants, got, msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExpectFalse(t *testing.T, got bool) {
|
func ExpectTrue(t *testing.T, got bool, msgAndArgs ...any) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
require.False(t, got)
|
require.True(t, got, msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExpectType[T any](t *testing.T, got any) (_ T) {
|
func ExpectFalse(t *testing.T, got bool, msgAndArgs ...any) {
|
||||||
|
t.Helper()
|
||||||
|
require.False(t, got, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExpectType[T any](t *testing.T, got any, msgAndArgs ...any) (_ T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
_, ok := got.(T)
|
_, ok := got.(T)
|
||||||
require.True(t, ok)
|
require.True(t, ok, msgAndArgs...)
|
||||||
return got.(T)
|
return got.(T)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,32 @@
|
||||||
package monitor
|
package health
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
net "github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type JSONRepresentation struct {
|
type JSONRepresentation struct {
|
||||||
Name string
|
Name string
|
||||||
Config *health.HealthCheckConfig
|
Config *HealthCheckConfig
|
||||||
Status health.Status
|
Status Status
|
||||||
Started time.Time
|
Started time.Time
|
||||||
Uptime time.Duration
|
Uptime time.Duration
|
||||||
Latency time.Duration
|
Latency time.Duration
|
||||||
LastSeen time.Time
|
LastSeen time.Time
|
||||||
Detail string
|
Detail string
|
||||||
URL *net.URL
|
URL *url.URL
|
||||||
Extra map[string]any
|
Extra map[string]any
|
||||||
}
|
}
|
||||||
|
|
||||||
func (jsonRepr *JSONRepresentation) MarshalJSON() ([]byte, error) {
|
func (jsonRepr *JSONRepresentation) MarshalMap() map[string]any {
|
||||||
url := jsonRepr.URL.String()
|
url := jsonRepr.URL.String()
|
||||||
if url == "http://:0" {
|
if url == "http://:0" {
|
||||||
url = ""
|
url = ""
|
||||||
}
|
}
|
||||||
return json.Marshal(map[string]any{
|
return map[string]any{
|
||||||
"name": jsonRepr.Name,
|
"name": jsonRepr.Name,
|
||||||
"config": jsonRepr.Config,
|
"config": jsonRepr.Config,
|
||||||
"started": jsonRepr.Started.Unix(),
|
"started": jsonRepr.Started.Unix(),
|
||||||
|
@ -43,5 +41,5 @@ func (jsonRepr *JSONRepresentation) MarshalJSON() ([]byte, error) {
|
||||||
"detail": jsonRepr.Detail,
|
"detail": jsonRepr.Detail,
|
||||||
"url": url,
|
"url": url,
|
||||||
"extra": jsonRepr.Extra,
|
"extra": jsonRepr.Extra,
|
||||||
})
|
}
|
||||||
}
|
}
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
agentPkg "github.com/yusing/go-proxy/agent/pkg/agent"
|
agentPkg "github.com/yusing/go-proxy/agent/pkg/agent"
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,7 +23,7 @@ type (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func AgentTargetFromURL(url *types.URL) *AgentCheckHealthTarget {
|
func AgentTargetFromURL(url *url.URL) *AgentCheckHealthTarget {
|
||||||
return &AgentCheckHealthTarget{
|
return &AgentCheckHealthTarget{
|
||||||
Scheme: url.Scheme,
|
Scheme: url.Scheme,
|
||||||
Host: url.Host,
|
Host: url.Host,
|
||||||
|
@ -40,12 +39,12 @@ func (target *AgentCheckHealthTarget) buildQuery() string {
|
||||||
return query.Encode()
|
return query.Encode()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (target *AgentCheckHealthTarget) displayURL() *types.URL {
|
func (target *AgentCheckHealthTarget) displayURL() *url.URL {
|
||||||
return types.NewURL(&url.URL{
|
return &url.URL{
|
||||||
Scheme: target.Scheme,
|
Scheme: target.Scheme,
|
||||||
Host: target.Host,
|
Host: target.Host,
|
||||||
Path: target.Path,
|
Path: target.Path,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAgentProxiedMonitor(agent *agentPkg.AgentConfig, config *health.HealthCheckConfig, target *AgentCheckHealthTarget) *AgentProxiedMonitor {
|
func NewAgentProxiedMonitor(agent *agentPkg.AgentConfig, config *health.HealthCheckConfig, target *AgentCheckHealthTarget) *AgentProxiedMonitor {
|
||||||
|
|
|
@ -4,9 +4,9 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||||
"github.com/yusing/go-proxy/pkg"
|
"github.com/yusing/go-proxy/pkg"
|
||||||
)
|
)
|
||||||
|
@ -26,7 +26,7 @@ var pinger = &http.Client{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTPHealthMonitor(url *types.URL, config *health.HealthCheckConfig) *HTTPHealthMonitor {
|
func NewHTTPHealthMonitor(url *url.URL, config *health.HealthCheckConfig) *HTTPHealthMonitor {
|
||||||
mon := new(HTTPHealthMonitor)
|
mon := new(HTTPHealthMonitor)
|
||||||
mon.monitor = newMonitor(url, config, mon.CheckHealth)
|
mon.monitor = newMonitor(url, config, mon.CheckHealth)
|
||||||
if config.UseGet {
|
if config.UseGet {
|
||||||
|
|
|
@ -4,12 +4,12 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/docker"
|
"github.com/yusing/go-proxy/internal/docker"
|
||||||
"github.com/yusing/go-proxy/internal/gperr"
|
"github.com/yusing/go-proxy/internal/gperr"
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
"github.com/yusing/go-proxy/internal/logging"
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
"github.com/yusing/go-proxy/internal/notif"
|
"github.com/yusing/go-proxy/internal/notif"
|
||||||
route "github.com/yusing/go-proxy/internal/route/types"
|
route "github.com/yusing/go-proxy/internal/route/types"
|
||||||
"github.com/yusing/go-proxy/internal/task"
|
"github.com/yusing/go-proxy/internal/task"
|
||||||
|
@ -23,7 +23,7 @@ type (
|
||||||
monitor struct {
|
monitor struct {
|
||||||
service string
|
service string
|
||||||
config *health.HealthCheckConfig
|
config *health.HealthCheckConfig
|
||||||
url atomic.Value[*types.URL]
|
url atomic.Value[*url.URL]
|
||||||
|
|
||||||
status atomic.Value[health.Status]
|
status atomic.Value[health.Status]
|
||||||
lastResult atomic.Value[*health.HealthCheckResult]
|
lastResult atomic.Value[*health.HealthCheckResult]
|
||||||
|
@ -63,7 +63,7 @@ func NewMonitor(r route.Route) health.HealthMonCheck {
|
||||||
return mon
|
return mon
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMonitor(url *types.URL, config *health.HealthCheckConfig, healthCheckFunc HealthCheckFunc) *monitor {
|
func newMonitor(url *url.URL, config *health.HealthCheckConfig, healthCheckFunc HealthCheckFunc) *monitor {
|
||||||
mon := &monitor{
|
mon := &monitor{
|
||||||
config: config,
|
config: config,
|
||||||
checkHealth: healthCheckFunc,
|
checkHealth: healthCheckFunc,
|
||||||
|
@ -135,12 +135,12 @@ func (mon *monitor) Finish(reason any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateURL implements HealthChecker.
|
// UpdateURL implements HealthChecker.
|
||||||
func (mon *monitor) UpdateURL(url *types.URL) {
|
func (mon *monitor) UpdateURL(url *url.URL) {
|
||||||
mon.url.Store(url)
|
mon.url.Store(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
// URL implements HealthChecker.
|
// URL implements HealthChecker.
|
||||||
func (mon *monitor) URL() *types.URL {
|
func (mon *monitor) URL() *url.URL {
|
||||||
return mon.url.Load()
|
return mon.url.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,8 +179,8 @@ func (mon *monitor) String() string {
|
||||||
return mon.Name()
|
return mon.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON implements json.Marshaler of HealthMonitor.
|
// MarshalMap implements health.HealthMonitor.
|
||||||
func (mon *monitor) MarshalJSON() ([]byte, error) {
|
func (mon *monitor) MarshalMap() map[string]any {
|
||||||
res := mon.lastResult.Load()
|
res := mon.lastResult.Load()
|
||||||
if res == nil {
|
if res == nil {
|
||||||
res = &health.HealthCheckResult{
|
res = &health.HealthCheckResult{
|
||||||
|
@ -188,7 +188,7 @@ func (mon *monitor) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (&JSONRepresentation{
|
return (&health.JSONRepresentation{
|
||||||
Name: mon.service,
|
Name: mon.service,
|
||||||
Config: mon.config,
|
Config: mon.config,
|
||||||
Status: mon.status.Load(),
|
Status: mon.status.Load(),
|
||||||
|
@ -198,7 +198,7 @@ func (mon *monitor) MarshalJSON() ([]byte, error) {
|
||||||
LastSeen: GetLastSeen(mon.service),
|
LastSeen: GetLastSeen(mon.service),
|
||||||
Detail: res.Detail,
|
Detail: res.Detail,
|
||||||
URL: mon.url.Load(),
|
URL: mon.url.Load(),
|
||||||
}).MarshalJSON()
|
}).MarshalMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mon *monitor) checkUpdateHealth() error {
|
func (mon *monitor) checkUpdateHealth() error {
|
||||||
|
|
|
@ -2,9 +2,9 @@ package monitor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ type (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewRawHealthMonitor(url *types.URL, config *health.HealthCheckConfig) *RawHealthMonitor {
|
func NewRawHealthMonitor(url *url.URL, config *health.HealthCheckConfig) *RawHealthMonitor {
|
||||||
mon := new(RawHealthMonitor)
|
mon := new(RawHealthMonitor)
|
||||||
mon.monitor = newMonitor(url, config, mon.CheckHealth)
|
mon.monitor = newMonitor(url, config, mon.CheckHealth)
|
||||||
mon.dialer = &net.Dialer{
|
mon.dialer = &net.Dialer{
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package health
|
package health
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/yusing/go-proxy/internal/net/types"
|
|
||||||
"github.com/yusing/go-proxy/internal/task"
|
"github.com/yusing/go-proxy/internal/task"
|
||||||
|
"github.com/yusing/go-proxy/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -24,15 +24,15 @@ type (
|
||||||
task.TaskStarter
|
task.TaskStarter
|
||||||
task.TaskFinisher
|
task.TaskFinisher
|
||||||
fmt.Stringer
|
fmt.Stringer
|
||||||
json.Marshaler
|
utils.MapMarshaller
|
||||||
WithHealthInfo
|
WithHealthInfo
|
||||||
Name() string
|
Name() string
|
||||||
}
|
}
|
||||||
HealthChecker interface {
|
HealthChecker interface {
|
||||||
CheckHealth() (result *HealthCheckResult, err error)
|
CheckHealth() (result *HealthCheckResult, err error)
|
||||||
URL() *types.URL
|
URL() *url.URL
|
||||||
Config() *HealthCheckConfig
|
Config() *HealthCheckConfig
|
||||||
UpdateURL(url *types.URL)
|
UpdateURL(url *url.URL)
|
||||||
}
|
}
|
||||||
HealthMonCheck interface {
|
HealthMonCheck interface {
|
||||||
HealthMonitor
|
HealthMonitor
|
||||||
|
|
Loading…
Add table
Reference in a new issue