remove unnecessary encapsulation, setup branch updated to v0.8

This commit is contained in:
yusing 2024-12-18 00:33:48 +08:00
parent 2baeb6a572
commit 276684f076
10 changed files with 60 additions and 94 deletions

View file

@ -15,22 +15,14 @@ import (
type ReverseProxyEntry struct { // real model after validation
Raw *route.RawEntry `json:"raw"`
Alias route.Alias `json:"alias"`
Scheme route.Scheme `json:"scheme"`
URL net.URL `json:"url"`
NoTLSVerify bool `json:"no_tls_verify,omitempty"`
PathPatterns []string `json:"path_patterns,omitempty"`
HealthCheck *health.HealthCheckConfig `json:"healthcheck,omitempty"`
LoadBalance *loadbalance.Config `json:"load_balance,omitempty"`
Middlewares map[string]docker.LabelMap `json:"middlewares,omitempty"`
URL net.URL `json:"url"`
/* Docker only */
Idlewatcher *idlewatcher.Config `json:"idlewatcher,omitempty"`
}
func (rp *ReverseProxyEntry) TargetName() string {
return string(rp.Alias)
return rp.Raw.Alias
}
func (rp *ReverseProxyEntry) TargetURL() net.URL {
@ -42,11 +34,11 @@ func (rp *ReverseProxyEntry) RawEntry() *route.RawEntry {
}
func (rp *ReverseProxyEntry) LoadBalanceConfig() *loadbalance.Config {
return rp.LoadBalance
return rp.Raw.LoadBalance
}
func (rp *ReverseProxyEntry) HealthCheckConfig() *health.HealthCheckConfig {
return rp.HealthCheck
return rp.Raw.HealthCheck
}
func (rp *ReverseProxyEntry) IdlewatcherConfig() *idlewatcher.Config {
@ -59,14 +51,12 @@ func validateRPEntry(m *route.RawEntry, s route.Scheme, errs *E.Builder) *Revers
cont = docker.DummyContainer
}
lb := m.LoadBalance
if lb != nil && lb.Link == "" {
lb = nil
if m.LoadBalance != nil && m.LoadBalance.Link == "" {
m.LoadBalance = nil
}
host := E.Collect(errs, route.ValidateHost, m.Host)
port := E.Collect(errs, route.ValidatePort, m.Port)
url := E.Collect(errs, url.Parse, fmt.Sprintf("%s://%s:%d", s, host, port))
url := E.Collect(errs, url.Parse, fmt.Sprintf("%s://%s:%d", s, m.Host, port))
iwCfg := E.Collect(errs, idlewatcher.ValidateConfig, cont)
if errs.HasError() {
@ -74,15 +64,8 @@ func validateRPEntry(m *route.RawEntry, s route.Scheme, errs *E.Builder) *Revers
}
return &ReverseProxyEntry{
Raw: m,
Alias: route.Alias(m.Alias),
Scheme: s,
URL: net.NewURL(url),
NoTLSVerify: m.NoTLSVerify,
PathPatterns: m.PathPatterns,
HealthCheck: m.HealthCheck,
LoadBalance: lb,
Middlewares: m.Middlewares,
Idlewatcher: iwCfg,
Raw: m,
URL: net.NewURL(url),
Idlewatcher: iwCfg,
}
}

View file

@ -15,19 +15,17 @@ import (
type StreamEntry struct {
Raw *route.RawEntry `json:"raw"`
Alias route.Alias `json:"alias"`
Scheme route.StreamScheme `json:"scheme"`
URL net.URL `json:"url"`
Host route.Host `json:"host,omitempty"`
Port route.StreamPort `json:"port,omitempty"`
HealthCheck *health.HealthCheckConfig `json:"healthcheck,omitempty"`
Scheme route.StreamScheme `json:"scheme"`
URL net.URL `json:"url"`
ListenURL net.URL `json:"listening_url"`
Port route.StreamPort `json:"port,omitempty"`
/* Docker only */
Idlewatcher *idlewatcher.Config `json:"idlewatcher,omitempty"`
}
func (s *StreamEntry) TargetName() string {
return string(s.Alias)
return s.Raw.Alias
}
func (s *StreamEntry) TargetURL() net.URL {
@ -44,7 +42,7 @@ func (s *StreamEntry) LoadBalanceConfig() *loadbalance.Config {
}
func (s *StreamEntry) HealthCheckConfig() *health.HealthCheckConfig {
return s.HealthCheck
return s.Raw.HealthCheck
}
func (s *StreamEntry) IdlewatcherConfig() *idlewatcher.Config {
@ -57,10 +55,10 @@ func validateStreamEntry(m *route.RawEntry, errs *E.Builder) *StreamEntry {
cont = docker.DummyContainer
}
host := E.Collect(errs, route.ValidateHost, m.Host)
port := E.Collect(errs, route.ValidateStreamPort, m.Port)
scheme := E.Collect(errs, route.ValidateStreamScheme, m.Scheme)
url := E.Collect(errs, net.ParseURL, fmt.Sprintf("%s://%s:%d", scheme.ListeningScheme, host, port.ProxyPort))
url := E.Collect(errs, net.ParseURL, fmt.Sprintf("%s://%s:%d", scheme.ProxyScheme, m.Host, port.ProxyPort))
listenURL := E.Collect(errs, net.ParseURL, fmt.Sprintf("%s://:%d", scheme.ListeningScheme, port.ListeningPort))
idleWatcherCfg := E.Collect(errs, idlewatcher.ValidateConfig, cont)
if errs.HasError() {
@ -69,12 +67,10 @@ func validateStreamEntry(m *route.RawEntry, errs *E.Builder) *StreamEntry {
return &StreamEntry{
Raw: m,
Alias: route.Alias(m.Alias),
Scheme: *scheme,
URL: url,
Host: host,
ListenURL: listenURL,
Port: port,
HealthCheck: m.HealthCheck,
Idlewatcher: idleWatcherCfg,
}
}

View file

@ -35,25 +35,23 @@ type (
l zerolog.Logger
}
SubdomainKey = route.Alias
)
// var globalMux = http.NewServeMux() // TODO: support regex subdomain matching.
func NewHTTPRoute(entry *entry.ReverseProxyEntry) (impl, E.Error) {
var trans *http.Transport
if entry.NoTLSVerify {
if entry.Raw.NoTLSVerify {
trans = gphttp.DefaultTransportNoTLS
} else {
trans = gphttp.DefaultTransport
}
service := string(entry.Alias)
service := entry.TargetName()
rp := gphttp.NewReverseProxy(service, entry.URL, trans)
if len(entry.Middlewares) > 0 {
err := middleware.PatchReverseProxy(rp, entry.Middlewares)
if len(entry.Raw.Middlewares) > 0 {
err := middleware.PatchReverseProxy(rp, entry.Raw.Middlewares)
if err != nil {
return nil, err
}
@ -63,15 +61,15 @@ func NewHTTPRoute(entry *entry.ReverseProxyEntry) (impl, E.Error) {
ReverseProxyEntry: entry,
rp: rp,
l: logger.With().
Str("type", string(entry.Scheme)).
Str("name", string(entry.Alias)).
Str("type", entry.URL.Scheme).
Str("name", service).
Logger(),
}
return r, nil
}
func (r *HTTPRoute) String() string {
return string(r.Alias)
return r.TargetName()
}
// Start implements*task.TaskStarter.
@ -85,7 +83,7 @@ func (r *HTTPRoute) Start(providerSubtask *task.Task) E.Error {
switch {
case entry.UseIdleWatcher(r):
wakerTask := providerSubtask.Parent().Subtask("waker for " + string(r.Alias))
wakerTask := providerSubtask.Parent().Subtask("waker for " + r.TargetName())
waker, err := idlewatcher.NewHTTPWaker(wakerTask, r.ReverseProxyEntry, r.rp)
if err != nil {
return err
@ -96,26 +94,27 @@ func (r *HTTPRoute) Start(providerSubtask *task.Task) E.Error {
if entry.IsDocker(r) {
client, err := docker.ConnectClient(r.Idlewatcher.DockerHost)
if err == nil {
fallback := monitor.NewHTTPHealthChecker(r.rp.TargetURL, r.HealthCheck)
r.HealthMon = monitor.NewDockerHealthMonitor(client, r.Idlewatcher.ContainerID, r.HealthCheck, fallback)
fallback := monitor.NewHTTPHealthChecker(r.rp.TargetURL, r.Raw.HealthCheck)
r.HealthMon = monitor.NewDockerHealthMonitor(client, r.Idlewatcher.ContainerID, r.Raw.HealthCheck, fallback)
r.task.OnCancel("close docker client", client.Close)
}
}
if r.HealthMon == nil {
r.HealthMon = monitor.NewHTTPHealthMonitor(r.rp.TargetURL, r.HealthCheck)
r.HealthMon = monitor.NewHTTPHealthMonitor(r.rp.TargetURL, r.Raw.HealthCheck)
}
}
if r.handler == nil {
pathPatterns := r.Raw.PathPatterns
switch {
case len(r.PathPatterns) == 0:
case len(pathPatterns) == 0:
r.handler = r.rp
case len(r.PathPatterns) == 1 && r.PathPatterns[0] == "/":
case len(pathPatterns) == 1 && pathPatterns[0] == "/":
r.handler = r.rp
default:
mux := gphttp.NewServeMux()
patErrs := E.NewBuilder("invalid path pattern(s)")
for _, p := range r.PathPatterns {
for _, p := range pathPatterns {
patErrs.Add(mux.HandleFunc(p, r.rp.HandlerFunc))
}
if err := patErrs.Error(); err != nil {
@ -136,9 +135,9 @@ func (r *HTTPRoute) Start(providerSubtask *task.Task) E.Error {
if entry.UseLoadBalance(r) {
r.addToLoadBalancer()
} else {
routes.SetHTTPRoute(string(r.Alias), r)
routes.SetHTTPRoute(r.TargetName(), r)
r.task.OnFinished("remove from route table", func() {
routes.DeleteHTTPRoute(string(r.Alias))
routes.DeleteHTTPRoute(r.TargetName())
})
}
@ -159,20 +158,21 @@ func (r *HTTPRoute) ServeHTTP(w http.ResponseWriter, req *http.Request) {
func (r *HTTPRoute) addToLoadBalancer() {
var lb *loadbalancer.LoadBalancer
l, ok := routes.GetHTTPRoute(r.LoadBalance.Link)
cfg := r.Raw.LoadBalance
l, ok := routes.GetHTTPRoute(cfg.Link)
var linked *HTTPRoute
if ok {
linked = l.(*HTTPRoute)
lb = linked.loadBalancer
lb.UpdateConfigIfNeeded(r.LoadBalance)
lb.UpdateConfigIfNeeded(cfg)
if linked.Raw.Homepage == nil && r.Raw.Homepage != nil {
linked.Raw.Homepage = r.Raw.Homepage
}
} else {
lb = loadbalancer.New(r.LoadBalance)
lbTask := r.task.Parent().Subtask("loadbalancer " + r.LoadBalance.Link)
lb = loadbalancer.New(cfg)
lbTask := r.task.Parent().Subtask("loadbalancer " + cfg.Link)
lbTask.OnCancel("remove lb from routes", func() {
routes.DeleteHTTPRoute(r.LoadBalance.Link)
routes.DeleteHTTPRoute(cfg.Link)
})
if err := lb.Start(lbTask); err != nil {
panic(err) // should always return nil
@ -180,18 +180,18 @@ func (r *HTTPRoute) addToLoadBalancer() {
linked = &HTTPRoute{
ReverseProxyEntry: &entry.ReverseProxyEntry{
Raw: &route.RawEntry{
Alias: cfg.Link,
Homepage: r.Raw.Homepage,
},
Alias: route.Alias(lb.Link),
},
HealthMon: lb,
loadBalancer: lb,
handler: lb,
}
routes.SetHTTPRoute(r.LoadBalance.Link, linked)
routes.SetHTTPRoute(cfg.Link, linked)
}
r.loadBalancer = lb
r.server = loadbalance.NewServer(r.task.String(), r.rp.TargetURL, r.LoadBalance.Weight, r.handler, r.HealthMon)
r.server = loadbalance.NewServer(r.task.String(), r.rp.TargetURL, r.Raw.LoadBalance.Weight, r.handler, r.HealthMon)
lb.AddServer(r.server)
r.task.OnCancel("remove server from lb", func() {
lb.RemoveServer(r.server)

View file

@ -325,7 +325,7 @@ func TestStreamDefaultValues(t *testing.T) {
a := ExpectType[*entry.StreamEntry](t, en)
ExpectEqual(t, a.Scheme.ListeningScheme, T.Scheme("udp"))
ExpectEqual(t, a.Scheme.ProxyScheme, T.Scheme("udp"))
ExpectEqual(t, a.Host, T.Host(privIP))
ExpectEqual(t, a.URL.Hostname(), privIP)
ExpectEqual(t, a.Port.ListeningPort, 0)
ExpectEqual(t, a.Port.ProxyPort, T.Port(privPort))
})
@ -337,7 +337,7 @@ func TestStreamDefaultValues(t *testing.T) {
a := ExpectType[*entry.StreamEntry](t, en)
ExpectEqual(t, a.Scheme.ListeningScheme, T.Scheme("udp"))
ExpectEqual(t, a.Scheme.ProxyScheme, T.Scheme("udp"))
ExpectEqual(t, a.Host, testIP)
ExpectEqual(t, a.URL.Hostname(), testIP)
ExpectEqual(t, a.Port.ListeningPort, 0)
ExpectEqual(t, a.Port.ProxyPort, T.Port(pubPort))
})

View file

@ -3,7 +3,6 @@ package route
import (
"context"
"errors"
"fmt"
"github.com/rs/zerolog"
"github.com/yusing/go-proxy/internal/docker"
@ -44,7 +43,7 @@ func NewStreamRoute(entry *entry.StreamEntry) (impl, E.Error) {
}
func (r *StreamRoute) String() string {
return fmt.Sprintf("stream %s", r.Alias)
return "stream " + r.TargetName()
}
// Start implements*task.TaskStarter.
@ -59,7 +58,7 @@ func (r *StreamRoute) Start(providerSubtask *task.Task) E.Error {
switch {
case entry.UseIdleWatcher(r):
wakerTask := providerSubtask.Parent().Subtask("waker for " + string(r.Alias))
wakerTask := providerSubtask.Parent().Subtask("waker for " + r.TargetName())
waker, err := idlewatcher.NewStreamWaker(wakerTask, r.StreamEntry, r.Stream)
if err != nil {
r.task.Finish(err)
@ -71,13 +70,13 @@ func (r *StreamRoute) Start(providerSubtask *task.Task) E.Error {
if entry.IsDocker(r) {
client, err := docker.ConnectClient(r.Idlewatcher.DockerHost)
if err == nil {
fallback := monitor.NewRawHealthChecker(r.TargetURL(), r.HealthCheck)
r.HealthMon = monitor.NewDockerHealthMonitor(client, r.Idlewatcher.ContainerID, r.HealthCheck, fallback)
fallback := monitor.NewRawHealthChecker(r.TargetURL(), r.Raw.HealthCheck)
r.HealthMon = monitor.NewDockerHealthMonitor(client, r.Idlewatcher.ContainerID, r.Raw.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.NewRawHealthMonitor(r.TargetURL(), r.Raw.HealthCheck)
}
}
@ -106,9 +105,9 @@ func (r *StreamRoute) Start(providerSubtask *task.Task) E.Error {
go r.acceptConnections()
routes.SetStreamRoute(string(r.Alias), r)
routes.SetStreamRoute(r.TargetName(), r)
r.task.OnFinished("remove from route table", func() {
routes.DeleteStreamRoute(string(r.Alias))
routes.DeleteStreamRoute(r.TargetName())
})
return nil
}

View file

@ -45,22 +45,22 @@ func (stream *Stream) Setup() error {
switch stream.Scheme.ListeningScheme {
case "tcp":
stream.targetAddr, err = net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%v", stream.Host, stream.Port.ProxyPort))
stream.targetAddr, err = net.ResolveTCPAddr("tcp", stream.URL.Host)
if err != nil {
return err
}
tcpListener, err := lcfg.Listen(stream.task.Context(), "tcp", fmt.Sprintf(":%v", stream.Port.ListeningPort))
tcpListener, err := lcfg.Listen(stream.task.Context(), "tcp", stream.ListenURL.Host)
if err != nil {
return err
}
stream.Port.ListeningPort = T.Port(tcpListener.Addr().(*net.TCPAddr).Port)
stream.listener = types.NetListener(tcpListener)
case "udp":
stream.targetAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%v", stream.Host, stream.Port.ProxyPort))
stream.targetAddr, err = net.ResolveUDPAddr("udp", stream.URL.Host)
if err != nil {
return err
}
udpListener, err := lcfg.ListenPacket(stream.task.Context(), "udp", fmt.Sprintf(":%v", stream.Port.ListeningPort))
udpListener, err := lcfg.ListenPacket(stream.task.Context(), "udp", stream.ListenURL.Host)
if err != nil {
return err
}

View file

@ -1,3 +0,0 @@
package types
type Alias string

View file

@ -1,10 +0,0 @@
package types
type (
Host string
Subdomain = Alias
)
func ValidateHost[String ~string](s String) (Host, error) {
return Host(s), nil
}

View file

@ -33,6 +33,7 @@ type (
LoadBalance *loadbalance.Config `json:"load_balance,omitempty" yaml:"load_balance"`
Middlewares map[string]docker.LabelMap `json:"middlewares,omitempty" yaml:"middlewares"`
Homepage *homepage.Item `json:"homepage,omitempty" yaml:"homepage"`
// AccessLog *accesslog.Config `json:"access_log,omitempty" yaml:"access_log"`
/* Docker only */
Container *docker.Container `json:"container,omitempty" yaml:"-"`

View file

@ -12,7 +12,7 @@ import (
)
var (
branch = common.GetEnvString("BRANCH", "v0.7")
branch = common.GetEnvString("BRANCH", "v0.8")
baseURL = "https://github.com/yusing/go-proxy/raw/" + branch
requiredConfigs = []Config{
{common.ConfigBasePath, true, false, ""},