merge: main branch

This commit is contained in:
yusing
2025-04-24 15:02:31 +08:00
parent 806184e98b
commit 663a107c06
107 changed files with 3047 additions and 2034 deletions

View File

@@ -57,7 +57,7 @@ func NewFileServer(base *Route) (*FileServer, gperr.Error) {
// Start implements task.TaskStarter.
func (s *FileServer) Start(parent task.Parent) gperr.Error {
s.task = parent.Subtask("fileserver."+s.TargetName(), false)
s.task = parent.Subtask("fileserver."+s.Name(), false)
pathPatterns := s.PathPatterns
switch {
@@ -92,7 +92,7 @@ func (s *FileServer) Start(parent task.Parent) gperr.Error {
}
if common.PrometheusEnabled {
metricsLogger := metricslogger.NewMetricsLogger(s.TargetName())
metricsLogger := metricslogger.NewMetricsLogger(s.Name())
s.handler = metricsLogger.GetHandler(s.handler)
s.task.OnCancel("reset_metrics", metricsLogger.ResetMetrics)
}
@@ -104,9 +104,9 @@ func (s *FileServer) Start(parent task.Parent) gperr.Error {
}
}
routes.SetHTTPRoute(s.TargetName(), s)
routes.HTTP.Add(s)
s.task.OnCancel("entrypoint_remove_route", func() {
routes.DeleteHTTPRoute(s.TargetName())
routes.HTTP.Del(s)
})
return nil
}

View File

@@ -3,7 +3,7 @@ package provider
import (
"testing"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/yusing/go-proxy/internal/docker"
. "github.com/yusing/go-proxy/internal/utils/testing"
"gopkg.in/yaml.v3"
@@ -21,11 +21,11 @@ func TestParseDockerLabels(t *testing.T) {
ExpectNoError(t, yaml.Unmarshal(testDockerLabelsYAML, &labels))
routes, err := provider.routesFromContainerLabels(
docker.FromDocker(&types.Container{
docker.FromDocker(&container.SummaryTrimmed{
Names: []string{"container"},
Labels: labels,
State: "running",
Ports: []types.Port{
Ports: []container.Port{
{Type: "tcp", PrivatePort: 1234, PublicPort: 1234},
},
}, "/var/run/docker.sock"),

View File

@@ -5,13 +5,13 @@ import (
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
"github.com/yusing/go-proxy/internal/common"
D "github.com/yusing/go-proxy/internal/docker"
"github.com/yusing/go-proxy/internal/route"
T "github.com/yusing/go-proxy/internal/route/types"
. "github.com/yusing/go-proxy/internal/utils/testing"
expect "github.com/yusing/go-proxy/internal/utils/testing"
)
var dummyNames = []string{"/a"}
@@ -21,7 +21,7 @@ const (
testDockerIP = "172.17.0.123"
)
func makeRoutes(cont *types.Container, dockerHostIP ...string) route.Routes {
func makeRoutes(cont *container.SummaryTrimmed, dockerHostIP ...string) route.Routes {
var p DockerProvider
var host string
if len(dockerHostIP) > 0 {
@@ -64,15 +64,15 @@ func TestApplyLabel(t *testing.T) {
},
},
}
entries := makeRoutes(&types.Container{
entries := makeRoutes(&container.SummaryTrimmed{
Names: dummyNames,
Labels: map[string]string{
D.LabelAliases: "a,b",
D.LabelIdleTimeout: "",
D.LabelStopMethod: common.StopMethodDefault,
D.LabelIdleTimeout: "10s",
D.LabelStopMethod: "stop",
D.LabelStopSignal: "SIGTERM",
D.LabelStopTimeout: common.StopTimeoutDefault,
D.LabelWakeTimeout: common.WakeTimeoutDefault,
D.LabelStopTimeout: "1h",
D.LabelWakeTimeout: "10s",
"proxy.*.no_tls_verify": "true",
"proxy.*.scheme": "https",
"proxy.*.host": "app",
@@ -88,54 +88,55 @@ func TestApplyLabel(t *testing.T) {
})
a, ok := entries["a"]
ExpectTrue(t, ok)
expect.True(t, ok)
b, ok := entries["b"]
ExpectTrue(t, ok)
expect.True(t, ok)
ExpectEqual(t, a.Scheme, "https")
ExpectEqual(t, b.Scheme, "https")
expect.Equal(t, a.Scheme, "https")
expect.Equal(t, b.Scheme, "https")
ExpectEqual(t, a.Host, "app")
ExpectEqual(t, b.Host, "app")
expect.Equal(t, a.Host, "app")
expect.Equal(t, b.Host, "app")
ExpectEqual(t, a.Port.Proxy, 4567)
ExpectEqual(t, b.Port.Proxy, 4567)
expect.Equal(t, a.Port.Proxy, 4567)
expect.Equal(t, b.Port.Proxy, 4567)
ExpectTrue(t, a.NoTLSVerify)
ExpectTrue(t, b.NoTLSVerify)
expect.True(t, a.NoTLSVerify)
expect.True(t, b.NoTLSVerify)
ExpectEqual(t, a.PathPatterns, pathPatternsExpect)
ExpectEqual(t, len(b.PathPatterns), 0)
expect.Equal(t, a.PathPatterns, pathPatternsExpect)
expect.Equal(t, len(b.PathPatterns), 0)
ExpectEqual(t, a.Middlewares, middlewaresExpect)
ExpectEqual(t, len(b.Middlewares), 0)
expect.Equal(t, a.Middlewares, middlewaresExpect)
expect.Equal(t, len(b.Middlewares), 0)
ExpectEqual(t, a.Container.IdleTimeout, "")
ExpectEqual(t, b.Container.IdleTimeout, "")
expect.NotNil(t, a.Container)
expect.NotNil(t, b.Container)
expect.NotNil(t, a.Container.IdlewatcherConfig)
expect.NotNil(t, b.Container.IdlewatcherConfig)
ExpectEqual(t, a.Container.StopTimeout, common.StopTimeoutDefault)
ExpectEqual(t, b.Container.StopTimeout, common.StopTimeoutDefault)
expect.Equal(t, a.Container.IdlewatcherConfig.IdleTimeout, 10*time.Second)
expect.Equal(t, b.Container.IdlewatcherConfig.IdleTimeout, 10*time.Second)
expect.Equal(t, a.Container.IdlewatcherConfig.StopTimeout, time.Hour)
expect.Equal(t, b.Container.IdlewatcherConfig.StopTimeout, time.Hour)
expect.Equal(t, a.Container.IdlewatcherConfig.StopMethod, "stop")
expect.Equal(t, b.Container.IdlewatcherConfig.StopMethod, "stop")
expect.Equal(t, a.Container.IdlewatcherConfig.WakeTimeout, 10*time.Second)
expect.Equal(t, b.Container.IdlewatcherConfig.WakeTimeout, 10*time.Second)
expect.Equal(t, a.Container.IdlewatcherConfig.StopSignal, "SIGTERM")
expect.Equal(t, b.Container.IdlewatcherConfig.StopSignal, "SIGTERM")
ExpectEqual(t, a.Container.StopMethod, common.StopMethodDefault)
ExpectEqual(t, b.Container.StopMethod, common.StopMethodDefault)
expect.Equal(t, a.Homepage.Show, true)
expect.Equal(t, a.Homepage.Icon.Value, "png/adguard-home.png")
expect.Equal(t, a.Homepage.Icon.Extra.FileType, "png")
expect.Equal(t, a.Homepage.Icon.Extra.Name, "adguard-home")
ExpectEqual(t, a.Container.WakeTimeout, common.WakeTimeoutDefault)
ExpectEqual(t, b.Container.WakeTimeout, common.WakeTimeoutDefault)
ExpectEqual(t, a.Container.StopSignal, "SIGTERM")
ExpectEqual(t, b.Container.StopSignal, "SIGTERM")
ExpectEqual(t, a.Homepage.Show, true)
ExpectEqual(t, a.Homepage.Icon.Value, "png/adguard-home.png")
ExpectEqual(t, a.Homepage.Icon.Extra.FileType, "png")
ExpectEqual(t, a.Homepage.Icon.Extra.Name, "adguard-home")
ExpectEqual(t, a.HealthCheck.Path, "/ping")
ExpectEqual(t, a.HealthCheck.Interval, 10*time.Second)
expect.Equal(t, a.HealthCheck.Path, "/ping")
expect.Equal(t, a.HealthCheck.Interval, 10*time.Second)
}
func TestApplyLabelWithAlias(t *testing.T) {
entries := makeRoutes(&types.Container{
entries := makeRoutes(&container.SummaryTrimmed{
Names: dummyNames,
State: "running",
Labels: map[string]string{
@@ -162,7 +163,7 @@ func TestApplyLabelWithAlias(t *testing.T) {
}
func TestApplyLabelWithRef(t *testing.T) {
entries := makeRoutes(&types.Container{
entries := makeRoutes(&container.SummaryTrimmed{
Names: dummyNames,
State: "running",
Labels: map[string]string{
@@ -190,7 +191,7 @@ func TestApplyLabelWithRef(t *testing.T) {
}
func TestApplyLabelWithRefIndexError(t *testing.T) {
c := D.FromDocker(&types.Container{
c := D.FromDocker(&container.SummaryTrimmed{
Names: dummyNames,
State: "running",
Labels: map[string]string{
@@ -204,7 +205,7 @@ func TestApplyLabelWithRefIndexError(t *testing.T) {
_, err := p.routesFromContainerLabels(c)
ExpectError(t, ErrAliasRefIndexOutOfRange, err)
c = D.FromDocker(&types.Container{
c = D.FromDocker(&container.SummaryTrimmed{
Names: dummyNames,
State: "running",
Labels: map[string]string{
@@ -217,7 +218,7 @@ func TestApplyLabelWithRefIndexError(t *testing.T) {
}
func TestDynamicAliases(t *testing.T) {
c := &types.Container{
c := &container.SummaryTrimmed{
Names: []string{"app1"},
State: "running",
Labels: map[string]string{
@@ -240,7 +241,7 @@ func TestDynamicAliases(t *testing.T) {
}
func TestDisableHealthCheck(t *testing.T) {
c := &types.Container{
c := &container.SummaryTrimmed{
Names: dummyNames,
State: "running",
Labels: map[string]string{
@@ -254,7 +255,7 @@ func TestDisableHealthCheck(t *testing.T) {
}
func TestPublicIPLocalhost(t *testing.T) {
c := &types.Container{Names: dummyNames, State: "running"}
c := &container.SummaryTrimmed{Names: dummyNames, State: "running"}
r, ok := makeRoutes(c)["a"]
ExpectTrue(t, ok)
ExpectEqual(t, r.Container.PublicHostname, "127.0.0.1")
@@ -262,7 +263,7 @@ func TestPublicIPLocalhost(t *testing.T) {
}
func TestPublicIPRemote(t *testing.T) {
c := &types.Container{Names: dummyNames, State: "running"}
c := &container.SummaryTrimmed{Names: dummyNames, State: "running"}
raw, ok := makeRoutes(c, testIP)["a"]
ExpectTrue(t, ok)
ExpectEqual(t, raw.Container.PublicHostname, testIP)
@@ -270,10 +271,10 @@ func TestPublicIPRemote(t *testing.T) {
}
func TestPrivateIPLocalhost(t *testing.T) {
c := &types.Container{
c := &container.SummaryTrimmed{
Names: dummyNames,
NetworkSettings: &types.SummaryNetworkSettings{
Networks: map[string]*network.EndpointSettings{
NetworkSettings: &container.NetworkSettingsSummaryTrimmed{
Networks: map[string]*struct{ IPAddress string }{
"network": {
IPAddress: testDockerIP,
},
@@ -287,11 +288,11 @@ func TestPrivateIPLocalhost(t *testing.T) {
}
func TestPrivateIPRemote(t *testing.T) {
c := &types.Container{
c := &container.SummaryTrimmed{
Names: dummyNames,
State: "running",
NetworkSettings: &types.SummaryNetworkSettings{
Networks: map[string]*network.EndpointSettings{
NetworkSettings: &container.NetworkSettingsSummaryTrimmed{
Networks: map[string]*struct{ IPAddress string }{
"network": {
IPAddress: testDockerIP,
},
@@ -309,11 +310,11 @@ func TestStreamDefaultValues(t *testing.T) {
privPort := uint16(1234)
pubPort := uint16(4567)
privIP := "172.17.0.123"
cont := &types.Container{
cont := &container.SummaryTrimmed{
Names: []string{"a"},
State: "running",
NetworkSettings: &types.SummaryNetworkSettings{
Networks: map[string]*network.EndpointSettings{
NetworkSettings: &container.NetworkSettingsSummaryTrimmed{
Networks: map[string]*struct{ IPAddress string }{
"network": {
IPAddress: privIP,
},
@@ -346,7 +347,7 @@ func TestStreamDefaultValues(t *testing.T) {
}
func TestExplicitExclude(t *testing.T) {
r, ok := makeRoutes(&types.Container{
r, ok := makeRoutes(&container.SummaryTrimmed{
Names: dummyNames,
Labels: map[string]string{
D.LabelAliases: "a",
@@ -360,17 +361,17 @@ func TestExplicitExclude(t *testing.T) {
func TestImplicitExcludeDatabase(t *testing.T) {
t.Run("mount path detection", func(t *testing.T) {
r, ok := makeRoutes(&types.Container{
r, ok := makeRoutes(&container.SummaryTrimmed{
Names: dummyNames,
Mounts: []types.MountPoint{
{Source: "/data", Destination: "/var/lib/postgresql/data"},
Mounts: []container.MountPointTrimmed{
{Destination: "/var/lib/postgresql/data"},
},
})["a"]
ExpectTrue(t, ok)
ExpectTrue(t, r.ShouldExclude())
})
t.Run("exposed port detection", func(t *testing.T) {
r, ok := makeRoutes(&types.Container{
r, ok := makeRoutes(&container.SummaryTrimmed{
Names: dummyNames,
Ports: []types.Port{
{Type: "tcp", PrivatePort: 5432, PublicPort: 5432},

View File

@@ -8,10 +8,8 @@ import (
"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/docker"
"github.com/yusing/go-proxy/internal/docker/idlewatcher"
"github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/logging"
"github.com/yusing/go-proxy/internal/idlewatcher"
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
"github.com/yusing/go-proxy/internal/net/gphttp/accesslog"
"github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer"
@@ -60,7 +58,7 @@ func NewReverseProxyRoute(base *Route) (*ReveseProxyRoute, gperr.Error) {
}
}
service := base.TargetName()
service := base.Name()
rp := reverseproxy.NewReverseProxy(service, proxyURL, trans)
if len(base.Middlewares) > 0 {
@@ -91,38 +89,24 @@ func NewReverseProxyRoute(base *Route) (*ReveseProxyRoute, gperr.Error) {
return r, nil
}
func (r *ReveseProxyRoute) String() string {
return r.TargetName()
}
// Start implements task.TaskStarter.
func (r *ReveseProxyRoute) Start(parent task.Parent) gperr.Error {
if existing, ok := routes.GetHTTPRoute(r.TargetName()); ok && !r.UseLoadBalance() {
if existing, ok := routes.HTTP.Get(r.Key()); ok && !r.UseLoadBalance() {
return gperr.Errorf("route already exists: from provider %s and %s", existing.ProviderName(), r.ProviderName())
}
r.task = parent.Subtask("http."+r.TargetName(), false)
r.task = parent.Subtask("http."+r.Name(), false)
switch {
case r.UseIdleWatcher():
waker, err := idlewatcher.NewHTTPWaker(parent, r, r.rp)
waker, err := idlewatcher.NewWatcher(parent, r)
if err != nil {
r.task.Finish(err)
return err
return gperr.Wrap(err)
}
r.handler = waker
r.HealthMon = waker
case r.UseHealthCheck():
if r.IsDocker() {
client, err := docker.NewClient(r.Container.DockerHost)
if err == nil {
fallback := r.newHealthMonitor()
r.HealthMon = monitor.NewDockerHealthMonitor(client, r.Container.ContainerID, r.TargetName(), r.HealthCheck, fallback)
r.task.OnCancel("close_docker_client", client.Close)
}
}
if r.HealthMon == nil {
r.HealthMon = r.newHealthMonitor()
}
r.HealthMon = monitor.NewMonitor(r)
}
if r.UseAccessLog() {
@@ -134,32 +118,8 @@ func (r *ReveseProxyRoute) Start(parent task.Parent) gperr.Error {
}
}
if r.handler == nil {
pathPatterns := r.PathPatterns
switch {
case len(pathPatterns) == 0:
r.handler = r.rp
case len(pathPatterns) == 1 && pathPatterns[0] == "/":
r.handler = r.rp
default:
logging.Warn().
Str("route", r.TargetName()).
Msg("`path_patterns` for reverse proxy is deprecated. Use `rules` instead.")
mux := gphttp.NewServeMux()
patErrs := gperr.NewBuilder("invalid path pattern(s)")
for _, p := range pathPatterns {
patErrs.Add(mux.HandleFunc(p, r.rp.HandlerFunc))
}
if err := patErrs.Error(); err != nil {
r.task.Finish(err)
return err
}
r.handler = mux
}
}
if len(r.Rules) > 0 {
r.handler = r.Rules.BuildHandler(r.TargetName(), r.handler)
r.handler = r.Rules.BuildHandler(r.Name(), r.handler)
}
if r.HealthMon != nil {
@@ -169,7 +129,7 @@ func (r *ReveseProxyRoute) Start(parent task.Parent) gperr.Error {
}
if common.PrometheusEnabled {
metricsLogger := metricslogger.NewMetricsLogger(r.TargetName())
metricsLogger := metricslogger.NewMetricsLogger(r.Name())
r.handler = metricsLogger.GetHandler(r.handler)
r.task.OnCancel("reset_metrics", metricsLogger.ResetMetrics)
}
@@ -177,9 +137,9 @@ func (r *ReveseProxyRoute) Start(parent task.Parent) gperr.Error {
if r.UseLoadBalance() {
r.addToLoadBalancer(parent)
} else {
routes.SetHTTPRoute(r.TargetName(), r)
r.task.OnCancel("entrypoint_remove_route", func() {
routes.DeleteHTTPRoute(r.TargetName())
routes.HTTP.Add(r)
r.task.OnFinished("entrypoint_remove_route", func() {
routes.HTTP.Del(r)
})
}
@@ -205,21 +165,10 @@ func (r *ReveseProxyRoute) HealthMonitor() health.HealthMonitor {
return r.HealthMon
}
func (r *ReveseProxyRoute) newHealthMonitor() interface {
health.HealthMonitor
health.HealthChecker
} {
if a := r.Agent(); a != nil {
target := monitor.AgentTargetFromURL(r.ProxyURL)
return monitor.NewAgentProxiedMonitor(a, r.HealthCheck, target)
}
return monitor.NewHTTPHealthMonitor(r.ProxyURL, r.HealthCheck)
}
func (r *ReveseProxyRoute) addToLoadBalancer(parent task.Parent) {
var lb *loadbalancer.LoadBalancer
cfg := r.LoadBalance
l, ok := routes.GetHTTPRoute(cfg.Link)
l, ok := routes.HTTP.Get(cfg.Link)
var linked *ReveseProxyRoute
if ok {
linked = l.(*ReveseProxyRoute)
@@ -240,7 +189,10 @@ func (r *ReveseProxyRoute) addToLoadBalancer(parent task.Parent) {
loadBalancer: lb,
handler: lb,
}
routes.SetHTTPRoute(cfg.Link, linked)
routes.HTTP.Add(linked)
r.task.OnFinished("entrypoint_remove_route", func() {
routes.HTTP.Del(linked)
})
}
r.loadBalancer = lb

View File

@@ -1,17 +1,22 @@
package route
import (
"context"
"fmt"
"strings"
"time"
"github.com/docker/docker/api/types/container"
"github.com/yusing/go-proxy/agent/pkg/agent"
"github.com/yusing/go-proxy/internal"
"github.com/yusing/go-proxy/internal/docker"
idlewatcher "github.com/yusing/go-proxy/internal/docker/idlewatcher/types"
"github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/homepage"
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
"github.com/yusing/go-proxy/internal/logging"
netutils "github.com/yusing/go-proxy/internal/net"
net "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/proxmox"
"github.com/yusing/go-proxy/internal/task"
"github.com/yusing/go-proxy/internal/utils/strutils"
"github.com/yusing/go-proxy/internal/watcher/health"
@@ -20,8 +25,9 @@ import (
config "github.com/yusing/go-proxy/internal/config/types"
"github.com/yusing/go-proxy/internal/net/gphttp/accesslog"
loadbalance "github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer/types"
"github.com/yusing/go-proxy/internal/route/routes"
"github.com/yusing/go-proxy/internal/route/rules"
"github.com/yusing/go-proxy/internal/route/types"
route "github.com/yusing/go-proxy/internal/route/types"
"github.com/yusing/go-proxy/internal/utils"
)
@@ -30,12 +36,12 @@ type (
_ utils.NoCopy
Alias string `json:"alias"`
Scheme types.Scheme `json:"scheme,omitempty"`
Scheme route.Scheme `json:"scheme,omitempty"`
Host string `json:"host,omitempty"`
Port types.Port `json:"port,omitempty"`
Port route.Port `json:"port,omitempty"`
Root string `json:"root,omitempty"`
types.HTTPConfig
route.HTTPConfig
PathPatterns []string `json:"path_patterns,omitempty"`
Rules rules.Rules `json:"rules,omitempty" validate:"omitempty,unique=Name"`
HealthCheck *health.HealthCheckConfig `json:"healthcheck,omitempty"`
@@ -44,6 +50,8 @@ type (
Homepage *homepage.ItemConfig `json:"homepage,omitempty"`
AccessLog *accesslog.Config `json:"access_log,omitempty"`
Idlewatcher *idlewatcher.Config `json:"idlewatcher,omitempty"`
Metadata `deserialize:"-"`
}
@@ -53,17 +61,18 @@ type (
Provider string `json:"provider,omitempty"`
// private fields
LisURL *net.URL `json:"lurl,omitempty"`
ProxyURL *net.URL `json:"purl,omitempty"`
Idlewatcher *idlewatcher.Config `json:"idlewatcher,omitempty"`
LisURL *net.URL `json:"lurl,omitempty"`
ProxyURL *net.URL `json:"purl,omitempty"`
impl types.Route
impl routes.Route
isValidated bool
lastError gperr.Error
}
Routes map[string]*Route
)
const DefaultHost = "localhost"
func (r Routes) Contains(alias string) bool {
_, ok := r[alias]
return ok
@@ -76,12 +85,79 @@ func (r *Route) Validate() gperr.Error {
r.isValidated = true
r.Finalize()
if r.Idlewatcher != nil && r.Idlewatcher.Proxmox != nil {
node := r.Idlewatcher.Proxmox.Node
vmid := r.Idlewatcher.Proxmox.VMID
if node == "" {
return gperr.Errorf("node (proxmox node name) is required")
}
if vmid <= 0 {
return gperr.Errorf("vmid (lxc id) is required")
}
if r.Host == DefaultHost {
containerName := r.Idlewatcher.ContainerName()
// get ip addresses of the vmid
node, ok := proxmox.Nodes.Get(node)
if !ok {
return gperr.Errorf("proxmox node %s not found in pool", node)
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ips, err := node.LXCGetIPs(ctx, vmid)
if err != nil {
return gperr.Errorf("failed to get ip addresses of vmid %d: %w", vmid, err)
}
if len(ips) == 0 {
return gperr.Multiline().
Addf("no ip addresses found for %s", containerName).
Adds("make sure you have set static ip address for container instead of dhcp").
Subject(containerName)
}
l := logging.With().Str("container", containerName).Logger()
l.Info().Msg("checking if container is running")
running, err := node.LXCIsRunning(ctx, vmid)
if err != nil {
return gperr.New("failed to check container state").With(err)
}
if !running {
l.Info().Msg("starting container")
if err := node.LXCAction(ctx, vmid, proxmox.LXCStart); err != nil {
return gperr.New("failed to start container").With(err)
}
}
l.Info().Msgf("finding reachable ip addresses")
errs := gperr.NewBuilder("failed to find reachable ip addresses")
for _, ip := range ips {
if err := netutils.PingTCP(ctx, ip, r.Port.Proxy); err != nil {
errs.Add(gperr.Unwrap(err).Subjectf("%s:%d", ip, r.Port.Proxy))
} else {
r.Host = ip.String()
l.Info().Msgf("using ip %s", r.Host)
break
}
}
if r.Host == DefaultHost {
return gperr.Multiline().
Addf("no reachable ip addresses found, tried %d IPs", len(ips)).
With(errs.Error()).
Subject(containerName)
}
}
}
// return error if route is localhost:<godoxy_port>
switch r.Host {
case "localhost", "127.0.0.1":
switch r.Port.Proxy {
case common.ProxyHTTPPort, common.ProxyHTTPSPort, common.APIHTTPPort:
if r.Scheme.IsReverseProxy() || r.Scheme == types.SchemeTCP {
if r.Scheme.IsReverseProxy() || r.Scheme == route.SchemeTCP {
return gperr.Errorf("localhost:%d is reserved for godoxy", r.Port.Proxy)
}
}
@@ -89,29 +165,27 @@ func (r *Route) Validate() gperr.Error {
errs := gperr.NewBuilder("entry validation failed")
var impl types.Route
var impl routes.Route
var err gperr.Error
switch r.Scheme {
case types.SchemeFileServer:
if r.Scheme == route.SchemeFileServer {
r.impl, err = NewFileServer(r)
if err != nil {
errs.Add(err)
}
case types.SchemeHTTP, types.SchemeHTTPS:
if r.Port.Listening != 0 {
errs.Addf("unexpected listening port for %s scheme", r.Scheme)
}
fallthrough
case types.SchemeTCP, types.SchemeUDP:
r.LisURL = gperr.Collect(errs, net.ParseURL, fmt.Sprintf("%s://:%d", r.Scheme, r.Port.Listening))
fallthrough
default:
if r.LoadBalance != nil && r.LoadBalance.Link == "" {
r.LoadBalance = nil
r.ProxyURL = gperr.Collect(errs, net.ParseURL, "file://"+r.Root)
r.Host = ""
r.Port.Proxy = 0
} else {
switch r.Scheme {
case route.SchemeHTTP, route.SchemeHTTPS:
if r.Port.Listening != 0 {
errs.Addf("unexpected listening port for %s scheme", r.Scheme)
}
case route.SchemeTCP, route.SchemeUDP:
r.LisURL = gperr.Collect(errs, net.ParseURL, 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.Idlewatcher = gperr.Collect(errs, idlewatcher.ValidateConfig, r.Container)
}
if !r.UseHealthCheck() && (r.UseLoadBalance() || r.UseIdleWatcher()) {
@@ -120,15 +194,15 @@ func (r *Route) Validate() gperr.Error {
if errs.HasError() {
r.lastError = errs.Error()
return r.lastError
return errs.Error()
}
switch r.Scheme {
case types.SchemeFileServer:
case route.SchemeFileServer:
impl, err = NewFileServer(r)
case types.SchemeHTTP, types.SchemeHTTPS:
case route.SchemeHTTP, route.SchemeHTTPS:
impl, err = NewReverseProxyRoute(r)
case types.SchemeTCP, types.SchemeUDP:
case route.SchemeTCP, route.SchemeUDP:
impl, err = NewStreamRoute(r)
default:
panic(fmt.Errorf("unexpected scheme %s for alias %s", r.Scheme, r.Alias))
@@ -167,20 +241,33 @@ func (r *Route) ProviderName() string {
return r.Provider
}
func (r *Route) TargetName() string {
return r.Alias
}
func (r *Route) TargetURL() *net.URL {
return r.ProxyURL
}
func (r *Route) Type() types.RouteType {
func (r *Route) Reference() string {
if r.Container != nil {
return r.Container.Image.Name
}
return r.Alias
}
// Name implements pool.Object.
func (r *Route) Name() string {
return r.Alias
}
// Key implements pool.Object.
func (r *Route) Key() string {
return r.Alias
}
func (r *Route) Type() route.RouteType {
switch r.Scheme {
case types.SchemeHTTP, types.SchemeHTTPS, types.SchemeFileServer:
return types.RouteTypeHTTP
case types.SchemeTCP, types.SchemeUDP:
return types.RouteTypeStream
case route.SchemeHTTP, route.SchemeHTTPS, route.SchemeFileServer:
return route.RouteTypeHTTP
case route.SchemeTCP, route.SchemeUDP:
return route.RouteTypeStream
}
panic(fmt.Errorf("unexpected scheme %s for alias %s", r.Scheme, r.Alias))
}
@@ -301,7 +388,7 @@ func (r *Route) Finalize() {
scheme, port, ok := getSchemePortByImageName(cont.Image.Name)
if ok {
if r.Scheme == "" {
r.Scheme = types.Scheme(scheme)
r.Scheme = route.Scheme(scheme)
}
if pp == 0 {
pp = port
@@ -311,7 +398,7 @@ func (r *Route) Finalize() {
if scheme, port, ok := getSchemePortByAlias(r.Alias); ok {
if r.Scheme == "" {
r.Scheme = types.Scheme(scheme)
r.Scheme = route.Scheme(scheme)
}
if pp == 0 {
pp = port
@@ -379,18 +466,6 @@ func (r *Route) Finalize() {
r.HealthCheck.Timeout = common.HealthCheckTimeoutDefault
}
}
if isDocker && cont.IdleTimeout != "" {
if cont.WakeTimeout == "" {
cont.WakeTimeout = common.WakeTimeoutDefault
}
if cont.StopTimeout == "" {
cont.StopTimeout = common.StopTimeoutDefault
}
if cont.StopMethod == "" {
cont.StopMethod = common.StopMethodDefault
}
}
}
func (r *Route) FinalizeHomepageConfig() {

View File

@@ -3,59 +3,45 @@ package route
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/yusing/go-proxy/internal/common"
"github.com/yusing/go-proxy/internal/docker"
loadbalance "github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer/types"
"github.com/yusing/go-proxy/internal/route/types"
route "github.com/yusing/go-proxy/internal/route/types"
expect "github.com/yusing/go-proxy/internal/utils/testing"
"github.com/yusing/go-proxy/internal/watcher/health"
)
func TestRouteValidate(t *testing.T) {
t.Run("AlreadyValidated", func(t *testing.T) {
r := &Route{
Alias: "test",
Scheme: types.SchemeHTTP,
Host: "example.com",
Port: types.Port{Proxy: 80},
Metadata: Metadata{
isValidated: true,
},
}
err := r.Validate()
require.NoError(t, err, "Validate should return nil for already validated route")
})
t.Run("ReservedPort", func(t *testing.T) {
r := &Route{
Alias: "test",
Scheme: types.SchemeHTTP,
Scheme: route.SchemeHTTP,
Host: "localhost",
Port: types.Port{Proxy: common.ProxyHTTPPort},
Port: route.Port{Proxy: common.ProxyHTTPPort},
}
err := r.Validate()
require.Error(t, err, "Validate should return error for localhost with reserved port")
require.Contains(t, err.Error(), "reserved for godoxy")
expect.HasError(t, err, "Validate should return error for localhost with reserved port")
expect.ErrorContains(t, err, "reserved for godoxy")
})
t.Run("ListeningPortWithHTTP", func(t *testing.T) {
r := &Route{
Alias: "test",
Scheme: types.SchemeHTTP,
Scheme: route.SchemeHTTP,
Host: "example.com",
Port: types.Port{Proxy: 80, Listening: 1234},
Port: route.Port{Proxy: 80, Listening: 1234},
}
err := r.Validate()
require.Error(t, err, "Validate should return error for HTTP scheme with listening port")
require.Contains(t, err.Error(), "unexpected listening port")
expect.HasError(t, err, "Validate should return error for HTTP scheme with listening port")
expect.ErrorContains(t, err, "unexpected listening port")
})
t.Run("DisabledHealthCheckWithLoadBalancer", func(t *testing.T) {
r := &Route{
Alias: "test",
Scheme: types.SchemeHTTP,
Scheme: route.SchemeHTTP,
Host: "example.com",
Port: types.Port{Proxy: 80},
Port: route.Port{Proxy: 80},
HealthCheck: &health.HealthCheckConfig{
Disable: true,
},
@@ -64,53 +50,53 @@ func TestRouteValidate(t *testing.T) {
}, // Minimal LoadBalance config with non-empty Link will be checked by UseLoadBalance
}
err := r.Validate()
require.Error(t, err, "Validate should return error for disabled healthcheck with loadbalancer")
require.Contains(t, err.Error(), "cannot disable healthcheck")
expect.HasError(t, err, "Validate should return error for disabled healthcheck with loadbalancer")
expect.ErrorContains(t, err, "cannot disable healthcheck")
})
t.Run("FileServerScheme", func(t *testing.T) {
r := &Route{
Alias: "test",
Scheme: types.SchemeFileServer,
Scheme: route.SchemeFileServer,
Host: "example.com",
Port: types.Port{Proxy: 80},
Port: route.Port{Proxy: 80},
Root: "/tmp", // Root is required for file server
}
err := r.Validate()
require.NoError(t, err, "Validate should not return error for valid file server route")
require.NotNil(t, r.impl, "Impl should be initialized")
expect.NoError(t, err, "Validate should not return error for valid file server route")
expect.NotNil(t, r.impl, "Impl should be initialized")
})
t.Run("HTTPScheme", func(t *testing.T) {
r := &Route{
Alias: "test",
Scheme: types.SchemeHTTP,
Scheme: route.SchemeHTTP,
Host: "example.com",
Port: types.Port{Proxy: 80},
Port: route.Port{Proxy: 80},
}
err := r.Validate()
require.NoError(t, err, "Validate should not return error for valid HTTP route")
require.NotNil(t, r.impl, "Impl should be initialized")
expect.NoError(t, err, "Validate should not return error for valid HTTP route")
expect.NotNil(t, r.impl, "Impl should be initialized")
})
t.Run("TCPScheme", func(t *testing.T) {
r := &Route{
Alias: "test",
Scheme: types.SchemeTCP,
Scheme: route.SchemeTCP,
Host: "example.com",
Port: types.Port{Proxy: 80, Listening: 8080},
Port: route.Port{Proxy: 80, Listening: 8080},
}
err := r.Validate()
require.NoError(t, err, "Validate should not return error for valid TCP route")
require.NotNil(t, r.impl, "Impl should be initialized")
expect.NoError(t, err, "Validate should not return error for valid TCP route")
expect.NotNil(t, r.impl, "Impl should be initialized")
})
t.Run("DockerContainer", func(t *testing.T) {
r := &Route{
Alias: "test",
Scheme: types.SchemeHTTP,
Scheme: route.SchemeHTTP,
Host: "example.com",
Port: types.Port{Proxy: 80},
Port: route.Port{Proxy: 80},
Metadata: Metadata{
Container: &docker.Container{
ContainerID: "test-id",
@@ -121,8 +107,8 @@ func TestRouteValidate(t *testing.T) {
},
}
err := r.Validate()
require.NoError(t, err, "Validate should not return error for valid docker container route")
require.NotNil(t, r.ProxyURL, "ProxyURL should be set")
expect.NoError(t, err, "Validate should not return error for valid docker container route")
expect.NotNil(t, r.ProxyURL, "ProxyURL should be set")
})
t.Run("InvalidScheme", func(t *testing.T) {
@@ -130,9 +116,9 @@ func TestRouteValidate(t *testing.T) {
Alias: "test",
Scheme: "invalid",
Host: "example.com",
Port: types.Port{Proxy: 80},
Port: route.Port{Proxy: 80},
}
require.Panics(t, func() {
expect.Panics(t, func() {
_ = r.Validate()
}, "Validate should panic for invalid scheme")
})
@@ -140,14 +126,13 @@ func TestRouteValidate(t *testing.T) {
t.Run("ModifiedFields", func(t *testing.T) {
r := &Route{
Alias: "test",
Scheme: types.SchemeHTTP,
Scheme: route.SchemeHTTP,
Host: "example.com",
Port: types.Port{Proxy: 80},
Port: route.Port{Proxy: 80},
}
err := r.Validate()
require.NoError(t, err)
require.True(t, r.isValidated)
require.NotNil(t, r.ProxyURL)
require.NotNil(t, r.HealthCheck)
expect.NoError(t, err)
expect.NotNil(t, r.ProxyURL)
expect.NotNil(t, r.HealthCheck)
})
}

View File

@@ -1,15 +1,14 @@
package routequery
package routes
import (
"time"
"github.com/yusing/go-proxy/internal/homepage"
"github.com/yusing/go-proxy/internal/route/routes"
route "github.com/yusing/go-proxy/internal/route/types"
"github.com/yusing/go-proxy/internal/watcher/health"
)
func getHealthInfo(r route.Route) map[string]string {
func getHealthInfo(r Route) map[string]string {
mon := r.HealthMonitor()
if mon == nil {
return map[string]string{
@@ -26,11 +25,11 @@ func getHealthInfo(r route.Route) map[string]string {
}
type HealthInfoRaw struct {
Status health.Status
Latency time.Duration
Status health.Status `json:"status,string"`
Latency time.Duration `json:"latency"`
}
func getHealthInfoRaw(r route.Route) *HealthInfoRaw {
func getHealthInfoRaw(r Route) *HealthInfoRaw {
mon := r.HealthMonitor()
if mon == nil {
return &HealthInfoRaw{
@@ -45,69 +44,69 @@ func getHealthInfoRaw(r route.Route) *HealthInfoRaw {
}
func HealthMap() map[string]map[string]string {
healthMap := make(map[string]map[string]string, routes.NumRoutes())
routes.RangeRoutes(func(alias string, r route.Route) {
healthMap := make(map[string]map[string]string, NumRoutes())
for alias, r := range Iter {
healthMap[alias] = getHealthInfo(r)
})
}
return healthMap
}
func HealthInfo() map[string]*HealthInfoRaw {
healthMap := make(map[string]*HealthInfoRaw, routes.NumRoutes())
routes.RangeRoutes(func(alias string, r route.Route) {
healthMap := make(map[string]*HealthInfoRaw, NumRoutes())
for alias, r := range Iter {
healthMap[alias] = getHealthInfoRaw(r)
})
}
return healthMap
}
func HomepageCategories() []string {
check := make(map[string]struct{})
categories := make([]string, 0)
routes.GetHTTPRoutes().RangeAll(func(alias string, r route.HTTPRoute) {
for _, r := range HTTP.Iter {
item := r.HomepageConfig()
if item == nil || item.Category == "" {
return
continue
}
if _, ok := check[item.Category]; ok {
return
continue
}
check[item.Category] = struct{}{}
categories = append(categories, item.Category)
})
}
return categories
}
func HomepageConfig(categoryFilter, providerFilter string) homepage.Homepage {
hp := make(homepage.Homepage)
routes.GetHTTPRoutes().RangeAll(func(alias string, r route.HTTPRoute) {
for _, r := range HTTP.Iter {
if providerFilter != "" && r.ProviderName() != providerFilter {
return
continue
}
item := r.HomepageItem()
if categoryFilter != "" && item.Category != categoryFilter {
return
continue
}
hp.Add(item)
})
}
return hp
}
func RoutesByAlias(typeFilter ...route.RouteType) map[string]route.Route {
rts := make(map[string]route.Route)
func ByAlias(typeFilter ...route.RouteType) map[string]Route {
rts := make(map[string]Route)
if len(typeFilter) == 0 || typeFilter[0] == "" {
typeFilter = []route.RouteType{route.RouteTypeHTTP, route.RouteTypeStream}
}
for _, t := range typeFilter {
switch t {
case route.RouteTypeHTTP:
routes.GetHTTPRoutes().RangeAll(func(alias string, r route.HTTPRoute) {
for alias, r := range HTTP.Iter {
rts[alias] = r
})
}
case route.RouteTypeStream:
routes.GetStreamRoutes().RangeAll(func(alias string, r route.StreamRoute) {
for alias, r := range Stream.Iter {
rts[alias] = r
})
}
}
}
return rts

View File

@@ -1,17 +1,19 @@
package types
package routes
import (
"net/http"
"github.com/yusing/go-proxy/agent/pkg/agent"
"github.com/yusing/go-proxy/internal/docker"
idlewatcher "github.com/yusing/go-proxy/internal/docker/idlewatcher/types"
"github.com/yusing/go-proxy/internal/homepage"
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/utils/pool"
"github.com/yusing/go-proxy/internal/watcher/health"
loadbalance "github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer/types"
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
)
type (
@@ -19,10 +21,11 @@ type (
Route interface {
task.TaskStarter
task.TaskFinisher
pool.Object
ProviderName() string
TargetName() string
TargetURL() *net.URL
HealthMonitor() health.HealthMonitor
Reference() string
Started() bool
@@ -46,6 +49,10 @@ type (
Route
http.Handler
}
ReverseProxyRoute interface {
HTTPRoute
ReverseProxy() *reverseproxy.ReverseProxy
}
StreamRoute interface {
Route
net.Stream

View File

@@ -1,78 +1,49 @@
package routes
import (
"github.com/yusing/go-proxy/internal/route/types"
F "github.com/yusing/go-proxy/internal/utils/functional"
"github.com/yusing/go-proxy/internal/utils/pool"
)
var (
httpRoutes = F.NewMapOf[string, types.HTTPRoute]()
streamRoutes = F.NewMapOf[string, types.StreamRoute]()
HTTP = pool.New[HTTPRoute]("http_routes")
Stream = pool.New[StreamRoute]("stream_routes")
)
func RangeRoutes(callback func(alias string, r types.Route)) {
httpRoutes.RangeAll(func(alias string, r types.HTTPRoute) {
callback(alias, r)
})
streamRoutes.RangeAll(func(alias string, r types.StreamRoute) {
callback(alias, r)
})
func Iter(yield func(alias string, r Route) bool) {
for k, r := range HTTP.Iter {
if !yield(k, r) {
break
}
}
for k, r := range Stream.Iter {
if !yield(k, r) {
break
}
}
}
func NumRoutes() int {
return httpRoutes.Size() + streamRoutes.Size()
return HTTP.Size() + Stream.Size()
}
func GetHTTPRoutes() F.Map[string, types.HTTPRoute] {
return httpRoutes
func Clear() {
HTTP.Clear()
Stream.Clear()
}
func GetStreamRoutes() F.Map[string, types.StreamRoute] {
return streamRoutes
}
func GetHTTPRouteOrExact(alias, host string) (types.HTTPRoute, bool) {
r, ok := httpRoutes.Load(alias)
func GetHTTPRouteOrExact(alias, host string) (HTTPRoute, bool) {
r, ok := HTTP.Get(alias)
if ok {
return r, true
}
// try find with exact match
return httpRoutes.Load(host)
return HTTP.Get(host)
}
func GetHTTPRoute(alias string) (types.HTTPRoute, bool) {
return httpRoutes.Load(alias)
}
func GetStreamRoute(alias string) (types.StreamRoute, bool) {
return streamRoutes.Load(alias)
}
func GetRoute(alias string) (types.Route, bool) {
r, ok := httpRoutes.Load(alias)
func Get(alias string) (Route, bool) {
r, ok := HTTP.Get(alias)
if ok {
return r, true
}
return streamRoutes.Load(alias)
}
func SetHTTPRoute(alias string, r types.HTTPRoute) {
httpRoutes.Store(alias, r)
}
func SetStreamRoute(alias string, r types.StreamRoute) {
streamRoutes.Store(alias, r)
}
func DeleteHTTPRoute(alias string) {
httpRoutes.Delete(alias)
}
func DeleteStreamRoute(alias string) {
streamRoutes.Delete(alias)
}
func TestClear() {
httpRoutes = F.NewMapOf[string, types.HTTPRoute]()
streamRoutes = F.NewMapOf[string, types.StreamRoute]()
return Stream.Get(alias)
}

View File

@@ -5,13 +5,11 @@ import (
"errors"
"github.com/rs/zerolog"
"github.com/yusing/go-proxy/internal/docker"
"github.com/yusing/go-proxy/internal/docker/idlewatcher"
"github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/idlewatcher"
"github.com/yusing/go-proxy/internal/logging"
net "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/route/routes"
route "github.com/yusing/go-proxy/internal/route/types"
"github.com/yusing/go-proxy/internal/task"
"github.com/yusing/go-proxy/internal/watcher/health"
"github.com/yusing/go-proxy/internal/watcher/health/monitor"
@@ -30,53 +28,36 @@ type StreamRoute struct {
l zerolog.Logger
}
func NewStreamRoute(base *Route) (route.Route, gperr.Error) {
func NewStreamRoute(base *Route) (routes.Route, gperr.Error) {
// TODO: support non-coherent scheme
return &StreamRoute{
Route: base,
l: logging.With().
Str("type", string(base.Scheme)).
Str("name", base.TargetName()).
Str("name", base.Name()).
Logger(),
}, nil
}
func (r *StreamRoute) String() string {
return "stream " + r.TargetName()
}
// Start implements task.TaskStarter.
func (r *StreamRoute) Start(parent task.Parent) gperr.Error {
if existing, ok := routes.GetStreamRoute(r.TargetName()); ok {
if existing, ok := routes.Stream.Get(r.Key()); ok {
return gperr.Errorf("route already exists: from provider %s and %s", existing.ProviderName(), r.ProviderName())
}
r.task = parent.Subtask("stream." + r.TargetName())
r.task = parent.Subtask("stream." + r.Name())
r.Stream = NewStream(r)
parent.OnCancel("finish", func() {
r.task.Finish(nil)
})
switch {
case r.UseIdleWatcher():
waker, err := idlewatcher.NewStreamWaker(parent, r, r.Stream)
waker, err := idlewatcher.NewWatcher(parent, r)
if err != nil {
r.task.Finish(err)
return err
return gperr.Wrap(err, "idlewatcher error")
}
r.Stream = waker
r.HealthMon = waker
case r.UseHealthCheck():
if r.IsDocker() {
client, err := docker.NewClient(r.Container.DockerHost)
if err == nil {
fallback := monitor.NewRawHealthChecker(r.TargetURL(), r.HealthCheck)
r.HealthMon = monitor.NewDockerHealthMonitor(client, r.Container.ContainerID, r.TargetName(), r.HealthCheck, fallback)
r.task.OnCancel("close_docker_client", client.Close)
}
}
if r.HealthMon == nil {
r.HealthMon = monitor.NewRawHealthMonitor(r.TargetURL(), r.HealthCheck)
}
r.HealthMon = monitor.NewMonitor(r)
}
if err := r.Stream.Setup(); err != nil {
@@ -94,9 +75,9 @@ func (r *StreamRoute) Start(parent task.Parent) gperr.Error {
go r.acceptConnections()
routes.SetStreamRoute(r.TargetName(), r)
r.task.OnCancel("entrypoint_remove_route", func() {
routes.DeleteStreamRoute(r.TargetName())
routes.Stream.Add(r)
r.task.OnFinished("entrypoint_remove_route", func() {
routes.Stream.Del(r)
})
return nil
}

View File

@@ -1,4 +1,4 @@
package types
package route
import (
"time"

View File

@@ -1,27 +1,27 @@
package types_test
package route_test
import (
"testing"
"time"
. "github.com/yusing/go-proxy/internal/route"
"github.com/yusing/go-proxy/internal/route/types"
route "github.com/yusing/go-proxy/internal/route/types"
"github.com/yusing/go-proxy/internal/utils"
. "github.com/yusing/go-proxy/internal/utils/testing"
expect "github.com/yusing/go-proxy/internal/utils/testing"
)
func TestHTTPConfigDeserialize(t *testing.T) {
tests := []struct {
name string
input map[string]any
expected types.HTTPConfig
expected route.HTTPConfig
}{
{
name: "no_tls_verify",
input: map[string]any{
"no_tls_verify": "true",
},
expected: types.HTTPConfig{
expected: route.HTTPConfig{
NoTLSVerify: true,
},
},
@@ -30,7 +30,7 @@ func TestHTTPConfigDeserialize(t *testing.T) {
input: map[string]any{
"response_header_timeout": "1s",
},
expected: types.HTTPConfig{
expected: route.HTTPConfig{
ResponseHeaderTimeout: 1 * time.Second,
},
},
@@ -39,11 +39,12 @@ func TestHTTPConfigDeserialize(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := Route{}
tt.input["host"] = "internal"
err := utils.Deserialize(tt.input, &cfg)
if err != nil {
ExpectNoError(t, err)
expect.NoError(t, err)
}
ExpectEqual(t, cfg.HTTPConfig, tt.expected)
expect.Equal(t, cfg.HTTPConfig, tt.expected)
})
}
}

View File

@@ -1,4 +1,4 @@
package types
package route
import (
"strconv"

View File

@@ -1,4 +1,4 @@
package types
package route
import (
"errors"

View File

@@ -1,4 +1,4 @@
package types
package route
type RouteType string

View File

@@ -1,4 +1,4 @@
package types
package route
import (
"github.com/yusing/go-proxy/internal/gperr"