mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-19 20:32:35 +02:00
modules reorganized and code refactor
This commit is contained in:
parent
f3b21e6bd9
commit
d723403b6b
46 changed files with 437 additions and 331 deletions
|
@ -14,12 +14,12 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/api/v1/query"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/config"
|
||||
"github.com/yusing/go-proxy/internal/entrypoint"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/metrics"
|
||||
"github.com/yusing/go-proxy/internal/net/http/middleware"
|
||||
R "github.com/yusing/go-proxy/internal/route"
|
||||
"github.com/yusing/go-proxy/internal/server"
|
||||
"github.com/yusing/go-proxy/internal/net/http/server"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/pkg"
|
||||
)
|
||||
|
@ -136,7 +136,7 @@ func main() {
|
|||
CertProvider: autocert,
|
||||
HTTPAddr: common.ProxyHTTPAddr,
|
||||
HTTPSAddr: common.ProxyHTTPSAddr,
|
||||
Handler: http.HandlerFunc(R.ProxyHandler),
|
||||
Handler: http.HandlerFunc(entrypoint.Handler),
|
||||
RedirectToHTTPS: config.Value().RedirectToHTTPS,
|
||||
})
|
||||
server.StartServer(server.Options{
|
||||
|
|
|
@ -10,10 +10,10 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/autocert"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/entrypoint"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/notif"
|
||||
"github.com/yusing/go-proxy/internal/route"
|
||||
proxy "github.com/yusing/go-proxy/internal/route/provider"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
U "github.com/yusing/go-proxy/internal/utils"
|
||||
|
@ -183,7 +183,7 @@ func (cfg *Config) load() E.Error {
|
|||
model.MatchDomains[i] = "." + domain
|
||||
}
|
||||
}
|
||||
route.SetFindMuxDomains(model.MatchDomains)
|
||||
entrypoint.SetFindRouteDomains(model.MatchDomains)
|
||||
return errs.Error()
|
||||
}
|
||||
|
||||
|
|
|
@ -6,14 +6,16 @@ import (
|
|||
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
"github.com/yusing/go-proxy/internal/proxy/entry"
|
||||
"github.com/yusing/go-proxy/internal/route"
|
||||
route "github.com/yusing/go-proxy/internal/route"
|
||||
"github.com/yusing/go-proxy/internal/route/entry"
|
||||
proxy "github.com/yusing/go-proxy/internal/route/provider"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
"github.com/yusing/go-proxy/internal/route/types"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
func DumpEntries() map[string]*entry.RawEntry {
|
||||
entries := make(map[string]*entry.RawEntry)
|
||||
func DumpEntries() map[string]*types.RawEntry {
|
||||
entries := make(map[string]*types.RawEntry)
|
||||
instance.providers.RangeAll(func(_ string, p *proxy.Provider) {
|
||||
p.RangeRoutes(func(alias string, r *route.Route) {
|
||||
entries[alias] = r.Entry
|
||||
|
@ -43,8 +45,8 @@ func HomepageConfig() homepage.Config {
|
|||
}
|
||||
|
||||
hpCfg := homepage.NewHomePageConfig()
|
||||
route.GetReverseProxies().RangeAll(func(alias string, r *route.HTTPRoute) {
|
||||
en := r.Raw
|
||||
routes.GetHTTPRoutes().RangeAll(func(alias string, r types.HTTPRoute) {
|
||||
en := r.RawEntry()
|
||||
item := en.Homepage
|
||||
if item == nil {
|
||||
item = new(homepage.Item)
|
||||
|
@ -113,23 +115,23 @@ func HomepageConfig() homepage.Config {
|
|||
}
|
||||
|
||||
func RoutesByAlias(typeFilter ...route.RouteType) map[string]any {
|
||||
routes := make(map[string]any)
|
||||
rts := make(map[string]any)
|
||||
if len(typeFilter) == 0 || typeFilter[0] == "" {
|
||||
typeFilter = []route.RouteType{route.RouteTypeReverseProxy, route.RouteTypeStream}
|
||||
}
|
||||
for _, t := range typeFilter {
|
||||
switch t {
|
||||
case route.RouteTypeReverseProxy:
|
||||
route.GetReverseProxies().RangeAll(func(alias string, r *route.HTTPRoute) {
|
||||
routes[alias] = r
|
||||
routes.GetHTTPRoutes().RangeAll(func(alias string, r types.HTTPRoute) {
|
||||
rts[alias] = r
|
||||
})
|
||||
case route.RouteTypeStream:
|
||||
route.GetStreamProxies().RangeAll(func(alias string, r *route.StreamRoute) {
|
||||
routes[alias] = r
|
||||
routes.GetStreamRoutes().RangeAll(func(alias string, r types.StreamRoute) {
|
||||
rts[alias] = r
|
||||
})
|
||||
}
|
||||
}
|
||||
return routes
|
||||
return rts
|
||||
}
|
||||
|
||||
func Statistics() map[string]any {
|
||||
|
|
|
@ -6,28 +6,31 @@ import (
|
|||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
. "github.com/yusing/go-proxy/internal/docker/idlewatcher/types"
|
||||
"github.com/yusing/go-proxy/internal/docker/idlewatcher/types"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/metrics"
|
||||
gphttp "github.com/yusing/go-proxy/internal/net/http"
|
||||
net "github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/proxy/entry"
|
||||
route "github.com/yusing/go-proxy/internal/route/types"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
U "github.com/yusing/go-proxy/internal/utils"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health/monitor"
|
||||
)
|
||||
|
||||
type waker struct {
|
||||
_ U.NoCopy
|
||||
type (
|
||||
Waker = types.Waker
|
||||
waker struct {
|
||||
_ U.NoCopy
|
||||
|
||||
rp *gphttp.ReverseProxy
|
||||
stream net.Stream
|
||||
hc health.HealthChecker
|
||||
metric *metrics.Gauge
|
||||
rp *gphttp.ReverseProxy
|
||||
stream net.Stream
|
||||
hc health.HealthChecker
|
||||
metric *metrics.Gauge
|
||||
|
||||
ready atomic.Bool
|
||||
}
|
||||
ready atomic.Bool
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
idleWakerCheckInterval = 100 * time.Millisecond
|
||||
|
@ -36,7 +39,7 @@ const (
|
|||
|
||||
// TODO: support stream
|
||||
|
||||
func newWaker(providerSubTask task.Task, entry entry.Entry, rp *gphttp.ReverseProxy, stream net.Stream) (Waker, E.Error) {
|
||||
func newWaker(providerSubTask task.Task, entry route.Entry, rp *gphttp.ReverseProxy, stream net.Stream) (Waker, E.Error) {
|
||||
hcCfg := entry.HealthCheckConfig()
|
||||
hcCfg.Timeout = idleWakerCheckTimeout
|
||||
|
||||
|
@ -69,11 +72,11 @@ func newWaker(providerSubTask task.Task, entry entry.Entry, rp *gphttp.ReversePr
|
|||
}
|
||||
|
||||
// lifetime should follow route provider.
|
||||
func NewHTTPWaker(providerSubTask task.Task, entry entry.Entry, rp *gphttp.ReverseProxy) (Waker, E.Error) {
|
||||
func NewHTTPWaker(providerSubTask task.Task, entry route.Entry, rp *gphttp.ReverseProxy) (Waker, E.Error) {
|
||||
return newWaker(providerSubTask, entry, rp, nil)
|
||||
}
|
||||
|
||||
func NewStreamWaker(providerSubTask task.Task, entry entry.Entry, stream net.Stream) (Waker, E.Error) {
|
||||
func NewStreamWaker(providerSubTask task.Task, entry route.Entry, stream net.Stream) (Waker, E.Error) {
|
||||
return newWaker(providerSubTask, entry, nil, stream)
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
idlewatcher "github.com/yusing/go-proxy/internal/docker/idlewatcher/types"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/proxy/entry"
|
||||
route "github.com/yusing/go-proxy/internal/route/types"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
U "github.com/yusing/go-proxy/internal/utils"
|
||||
F "github.com/yusing/go-proxy/internal/utils/functional"
|
||||
|
@ -49,7 +49,7 @@ var (
|
|||
|
||||
const dockerReqTimeout = 3 * time.Second
|
||||
|
||||
func registerWatcher(providerSubtask task.Task, entry entry.Entry, waker *waker) (*Watcher, error) {
|
||||
func registerWatcher(providerSubtask task.Task, entry route.Entry, waker *waker) (*Watcher, error) {
|
||||
cfg := entry.IdlewatcherConfig()
|
||||
|
||||
if cfg.IdleTimeout == 0 {
|
||||
|
|
93
internal/entrypoint/entrypoint.go
Normal file
93
internal/entrypoint/entrypoint.go
Normal file
|
@ -0,0 +1,93 @@
|
|||
package entrypoint
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/net/http/middleware"
|
||||
"github.com/yusing/go-proxy/internal/net/http/middleware/errorpage"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
route "github.com/yusing/go-proxy/internal/route/types"
|
||||
)
|
||||
|
||||
var findRouteFunc = findRouteAnyDomain
|
||||
|
||||
func SetFindRouteDomains(domains []string) {
|
||||
if len(domains) == 0 {
|
||||
findRouteFunc = findRouteAnyDomain
|
||||
} else {
|
||||
findRouteFunc = findRouteByDomains(domains)
|
||||
}
|
||||
}
|
||||
|
||||
func Handler(w http.ResponseWriter, r *http.Request) {
|
||||
mux, err := findRouteFunc(r.Host)
|
||||
if err == nil {
|
||||
mux.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
// Why use StatusNotFound instead of StatusBadRequest or StatusBadGateway?
|
||||
// On nginx, when route for domain does not exist, it returns StatusBadGateway.
|
||||
// Then scraper / scanners will know the subdomain is invalid.
|
||||
// With StatusNotFound, they won't know whether it's the path, or the subdomain that is invalid.
|
||||
if !middleware.ServeStaticErrorPageFile(w, r) {
|
||||
logger.Err(err).Str("method", r.Method).Str("url", r.URL.String()).Msg("request")
|
||||
errorPage, ok := errorpage.GetErrorPageByStatus(http.StatusNotFound)
|
||||
if ok {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if _, err := w.Write(errorPage); err != nil {
|
||||
logger.Err(err).Msg("failed to write error page")
|
||||
}
|
||||
} else {
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func findRouteAnyDomain(host string) (route.HTTPRoute, error) {
|
||||
hostSplit := strings.Split(host, ".")
|
||||
n := len(hostSplit)
|
||||
switch {
|
||||
case n == 3:
|
||||
host = hostSplit[0]
|
||||
case n > 3:
|
||||
var builder strings.Builder
|
||||
builder.Grow(2*n - 3)
|
||||
builder.WriteString(hostSplit[0])
|
||||
for _, part := range hostSplit[:n-2] {
|
||||
builder.WriteRune('.')
|
||||
builder.WriteString(part)
|
||||
}
|
||||
host = builder.String()
|
||||
default:
|
||||
return nil, errors.New("missing subdomain in url")
|
||||
}
|
||||
if r, ok := routes.GetHTTPRoute(host); ok {
|
||||
return r, nil
|
||||
}
|
||||
return nil, fmt.Errorf("no such route: %s", host)
|
||||
}
|
||||
|
||||
func findRouteByDomains(domains []string) func(host string) (route.HTTPRoute, error) {
|
||||
return func(host string) (route.HTTPRoute, error) {
|
||||
var subdomain string
|
||||
|
||||
for _, domain := range domains {
|
||||
if strings.HasSuffix(host, domain) {
|
||||
subdomain = strings.TrimSuffix(host, domain)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if subdomain != "" { // matched
|
||||
if r, ok := routes.GetHTTPRoute(subdomain); ok {
|
||||
return r, nil
|
||||
}
|
||||
return nil, fmt.Errorf("no such route: %s", subdomain)
|
||||
}
|
||||
return nil, fmt.Errorf("%s does not match any base domain", host)
|
||||
}
|
||||
}
|
7
internal/entrypoint/logger.go
Normal file
7
internal/entrypoint/logger.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package entrypoint
|
||||
|
||||
import (
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
)
|
||||
|
||||
var logger = logging.With().Str("module", "entrypoint").Logger()
|
|
@ -14,7 +14,7 @@ type ipHash struct {
|
|||
*LoadBalancer
|
||||
|
||||
realIP *middleware.Middleware
|
||||
pool servers
|
||||
pool Servers
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ func (lb *LoadBalancer) newIPHash() impl {
|
|||
var err E.Error
|
||||
impl.realIP, err = middleware.NewRealIP(lb.Options)
|
||||
if err != nil {
|
||||
E.LogError("invalid real_ip options, ignoring", err, &impl.Logger)
|
||||
E.LogError("invalid real_ip options, ignoring", err, &impl.l)
|
||||
}
|
||||
return impl
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ func (impl *ipHash) OnRemoveServer(srv *Server) {
|
|||
}
|
||||
}
|
||||
|
||||
func (impl *ipHash) ServeHTTP(_ servers, rw http.ResponseWriter, r *http.Request) {
|
||||
func (impl *ipHash) ServeHTTP(_ Servers, rw http.ResponseWriter, r *http.Request) {
|
||||
if impl.realIP != nil {
|
||||
impl.realIP.ModifyRequest(impl.serveHTTP, rw, r)
|
||||
} else {
|
||||
|
@ -72,7 +72,7 @@ func (impl *ipHash) serveHTTP(rw http.ResponseWriter, r *http.Request) {
|
|||
ip, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
http.Error(rw, "Internal error", http.StatusInternalServerError)
|
||||
impl.Err(err).Msg("invalid remote address " + r.RemoteAddr)
|
||||
impl.l.Err(err).Msg("invalid remote address " + r.RemoteAddr)
|
||||
return
|
||||
}
|
||||
idx := hashIP(ip) % uint32(len(impl.pool))
|
||||
|
|
|
@ -27,18 +27,18 @@ func (impl *leastConn) OnRemoveServer(srv *Server) {
|
|||
impl.nConn.Delete(srv)
|
||||
}
|
||||
|
||||
func (impl *leastConn) ServeHTTP(srvs servers, rw http.ResponseWriter, r *http.Request) {
|
||||
func (impl *leastConn) ServeHTTP(srvs Servers, rw http.ResponseWriter, r *http.Request) {
|
||||
srv := srvs[0]
|
||||
minConn, ok := impl.nConn.Load(srv)
|
||||
if !ok {
|
||||
impl.Error().Msgf("[BUG] server %s not found", srv.Name)
|
||||
impl.l.Error().Msgf("[BUG] server %s not found", srv.Name)
|
||||
http.Error(rw, "Internal error", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
for i := 1; i < len(srvs); i++ {
|
||||
nConn, ok := impl.nConn.Load(srvs[i])
|
||||
if !ok {
|
||||
impl.Error().Msgf("[BUG] server %s not found", srv.Name)
|
||||
impl.l.Error().Msgf("[BUG] server %s not found", srv.Name)
|
||||
http.Error(rw, "Internal error", http.StatusInternalServerError)
|
||||
}
|
||||
if nConn.Load() < minConn.Load() {
|
||||
|
|
|
@ -7,9 +7,8 @@ import (
|
|||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
idlewatcher "github.com/yusing/go-proxy/internal/docker/idlewatcher/types"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/net/http/middleware"
|
||||
"github.com/yusing/go-proxy/internal/net/http/loadbalancer/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"
|
||||
|
@ -19,19 +18,12 @@ import (
|
|||
// TODO: support weighted mode.
|
||||
type (
|
||||
impl interface {
|
||||
ServeHTTP(srvs servers, rw http.ResponseWriter, r *http.Request)
|
||||
ServeHTTP(srvs Servers, rw http.ResponseWriter, r *http.Request)
|
||||
OnAddServer(srv *Server)
|
||||
OnRemoveServer(srv *Server)
|
||||
}
|
||||
Config struct {
|
||||
Link string `json:"link" yaml:"link"`
|
||||
Mode Mode `json:"mode" yaml:"mode"`
|
||||
Weight weightType `json:"weight" yaml:"weight"`
|
||||
Options middleware.OptionsRaw `json:"options,omitempty" yaml:"options,omitempty"`
|
||||
}
|
||||
LoadBalancer struct {
|
||||
zerolog.Logger
|
||||
|
||||
LoadBalancer struct {
|
||||
impl
|
||||
*Config
|
||||
|
||||
|
@ -40,20 +32,20 @@ type (
|
|||
pool Pool
|
||||
poolMu sync.Mutex
|
||||
|
||||
sumWeight weightType
|
||||
sumWeight Weight
|
||||
startTime time.Time
|
||||
}
|
||||
|
||||
weightType uint16
|
||||
l zerolog.Logger
|
||||
}
|
||||
)
|
||||
|
||||
const maxWeight weightType = 100
|
||||
const maxWeight Weight = 100
|
||||
|
||||
func New(cfg *Config) *LoadBalancer {
|
||||
lb := &LoadBalancer{
|
||||
Logger: logger.With().Str("name", cfg.Link).Logger(),
|
||||
Config: new(Config),
|
||||
pool: newPool(),
|
||||
pool: types.NewServerPool(),
|
||||
l: logger.With().Str("name", cfg.Link).Logger(),
|
||||
}
|
||||
lb.UpdateConfigIfNeeded(cfg)
|
||||
return lb
|
||||
|
@ -81,11 +73,11 @@ func (lb *LoadBalancer) Finish(reason any) {
|
|||
|
||||
func (lb *LoadBalancer) updateImpl() {
|
||||
switch lb.Mode {
|
||||
case Unset, RoundRobin:
|
||||
case types.ModeUnset, types.ModeRoundRobin:
|
||||
lb.impl = lb.newRoundRobin()
|
||||
case LeastConn:
|
||||
case types.ModeLeastConn:
|
||||
lb.impl = lb.newLeastConn()
|
||||
case IPHash:
|
||||
case types.ModeIPHash:
|
||||
lb.impl = lb.newIPHash()
|
||||
default: // should happen in test only
|
||||
lb.impl = lb.newRoundRobin()
|
||||
|
@ -102,10 +94,10 @@ func (lb *LoadBalancer) UpdateConfigIfNeeded(cfg *Config) {
|
|||
|
||||
lb.Link = cfg.Link
|
||||
|
||||
if lb.Mode == Unset && cfg.Mode != Unset {
|
||||
if lb.Mode == types.ModeUnset && cfg.Mode != types.ModeUnset {
|
||||
lb.Mode = cfg.Mode
|
||||
if !lb.Mode.ValidateUpdate() {
|
||||
lb.Error().Msgf("invalid mode %q, fallback to %q", cfg.Mode, lb.Mode)
|
||||
lb.l.Error().Msgf("invalid mode %q, fallback to %q", cfg.Mode, lb.Mode)
|
||||
}
|
||||
lb.updateImpl()
|
||||
}
|
||||
|
@ -135,7 +127,7 @@ func (lb *LoadBalancer) AddServer(srv *Server) {
|
|||
lb.rebalance()
|
||||
lb.impl.OnAddServer(srv)
|
||||
|
||||
lb.Debug().
|
||||
lb.l.Debug().
|
||||
Str("action", "add").
|
||||
Str("server", srv.Name).
|
||||
Msgf("%d servers available", lb.pool.Size())
|
||||
|
@ -155,7 +147,7 @@ func (lb *LoadBalancer) RemoveServer(srv *Server) {
|
|||
lb.rebalance()
|
||||
lb.impl.OnRemoveServer(srv)
|
||||
|
||||
lb.Debug().
|
||||
lb.l.Debug().
|
||||
Str("action", "remove").
|
||||
Str("server", srv.Name).
|
||||
Msgf("%d servers left", lb.pool.Size())
|
||||
|
@ -174,8 +166,8 @@ func (lb *LoadBalancer) rebalance() {
|
|||
return
|
||||
}
|
||||
if lb.sumWeight == 0 { // distribute evenly
|
||||
weightEach := maxWeight / weightType(lb.pool.Size())
|
||||
remainder := maxWeight % weightType(lb.pool.Size())
|
||||
weightEach := maxWeight / Weight(lb.pool.Size())
|
||||
remainder := maxWeight % Weight(lb.pool.Size())
|
||||
lb.pool.RangeAll(func(_ string, s *Server) {
|
||||
s.Weight = weightEach
|
||||
lb.sumWeight += weightEach
|
||||
|
@ -192,7 +184,7 @@ func (lb *LoadBalancer) rebalance() {
|
|||
lb.sumWeight = 0
|
||||
|
||||
lb.pool.RangeAll(func(_ string, s *Server) {
|
||||
s.Weight = weightType(float64(s.Weight) * scaleFactor)
|
||||
s.Weight = Weight(float64(s.Weight) * scaleFactor)
|
||||
lb.sumWeight += s.Weight
|
||||
})
|
||||
|
||||
|
@ -226,13 +218,7 @@ func (lb *LoadBalancer) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
|||
if r.Header.Get(common.HeaderCheckRedirect) != "" {
|
||||
// wake all servers
|
||||
for _, srv := range srvs {
|
||||
// wake only if server implements Waker
|
||||
waker, ok := srv.handler.(idlewatcher.Waker)
|
||||
if ok {
|
||||
if err := waker.Wake(); err != nil {
|
||||
lb.Err(err).Msgf("failed to wake server %s", srv.Name)
|
||||
}
|
||||
}
|
||||
srv.TryWake()
|
||||
}
|
||||
}
|
||||
lb.impl.ServeHTTP(srvs, rw, r)
|
||||
|
@ -246,7 +232,7 @@ func (lb *LoadBalancer) Uptime() time.Duration {
|
|||
func (lb *LoadBalancer) MarshalJSON() ([]byte, error) {
|
||||
extra := make(map[string]any)
|
||||
lb.pool.RangeAll(func(k string, v *Server) {
|
||||
extra[v.Name] = v.healthMon
|
||||
extra[v.Name] = v.HealthMonitor()
|
||||
})
|
||||
|
||||
return (&monitor.JSONRepresentation{
|
||||
|
|
|
@ -3,13 +3,14 @@ package loadbalancer
|
|||
import (
|
||||
"testing"
|
||||
|
||||
loadbalance "github.com/yusing/go-proxy/internal/net/http/loadbalancer/types"
|
||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||
)
|
||||
|
||||
func TestRebalance(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("zero", func(t *testing.T) {
|
||||
lb := New(new(Config))
|
||||
lb := New(new(loadbalance.Config))
|
||||
for range 10 {
|
||||
lb.AddServer(&Server{})
|
||||
}
|
||||
|
@ -17,25 +18,25 @@ func TestRebalance(t *testing.T) {
|
|||
ExpectEqual(t, lb.sumWeight, maxWeight)
|
||||
})
|
||||
t.Run("less", func(t *testing.T) {
|
||||
lb := New(new(Config))
|
||||
lb.AddServer(&Server{Weight: weightType(float64(maxWeight) * .1)})
|
||||
lb.AddServer(&Server{Weight: weightType(float64(maxWeight) * .2)})
|
||||
lb.AddServer(&Server{Weight: weightType(float64(maxWeight) * .3)})
|
||||
lb.AddServer(&Server{Weight: weightType(float64(maxWeight) * .2)})
|
||||
lb.AddServer(&Server{Weight: weightType(float64(maxWeight) * .1)})
|
||||
lb := New(new(loadbalance.Config))
|
||||
lb.AddServer(&Server{Weight: loadbalance.Weight(float64(maxWeight) * .1)})
|
||||
lb.AddServer(&Server{Weight: loadbalance.Weight(float64(maxWeight) * .2)})
|
||||
lb.AddServer(&Server{Weight: loadbalance.Weight(float64(maxWeight) * .3)})
|
||||
lb.AddServer(&Server{Weight: loadbalance.Weight(float64(maxWeight) * .2)})
|
||||
lb.AddServer(&Server{Weight: loadbalance.Weight(float64(maxWeight) * .1)})
|
||||
lb.rebalance()
|
||||
// t.Logf("%s", U.Must(json.MarshalIndent(lb.pool, "", " ")))
|
||||
ExpectEqual(t, lb.sumWeight, maxWeight)
|
||||
})
|
||||
t.Run("more", func(t *testing.T) {
|
||||
lb := New(new(Config))
|
||||
lb.AddServer(&Server{Weight: weightType(float64(maxWeight) * .1)})
|
||||
lb.AddServer(&Server{Weight: weightType(float64(maxWeight) * .2)})
|
||||
lb.AddServer(&Server{Weight: weightType(float64(maxWeight) * .3)})
|
||||
lb.AddServer(&Server{Weight: weightType(float64(maxWeight) * .4)})
|
||||
lb.AddServer(&Server{Weight: weightType(float64(maxWeight) * .3)})
|
||||
lb.AddServer(&Server{Weight: weightType(float64(maxWeight) * .2)})
|
||||
lb.AddServer(&Server{Weight: weightType(float64(maxWeight) * .1)})
|
||||
lb := New(new(loadbalance.Config))
|
||||
lb.AddServer(&Server{Weight: loadbalance.Weight(float64(maxWeight) * .1)})
|
||||
lb.AddServer(&Server{Weight: loadbalance.Weight(float64(maxWeight) * .2)})
|
||||
lb.AddServer(&Server{Weight: loadbalance.Weight(float64(maxWeight) * .3)})
|
||||
lb.AddServer(&Server{Weight: loadbalance.Weight(float64(maxWeight) * .4)})
|
||||
lb.AddServer(&Server{Weight: loadbalance.Weight(float64(maxWeight) * .3)})
|
||||
lb.AddServer(&Server{Weight: loadbalance.Weight(float64(maxWeight) * .2)})
|
||||
lb.AddServer(&Server{Weight: loadbalance.Weight(float64(maxWeight) * .1)})
|
||||
lb.rebalance()
|
||||
// t.Logf("%s", U.Must(json.MarshalIndent(lb.pool, "", " ")))
|
||||
ExpectEqual(t, lb.sumWeight, maxWeight)
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
package loadbalancer
|
||||
|
||||
import (
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
type Mode string
|
||||
|
||||
const (
|
||||
Unset Mode = ""
|
||||
RoundRobin Mode = "roundrobin"
|
||||
LeastConn Mode = "leastconn"
|
||||
IPHash Mode = "iphash"
|
||||
)
|
||||
|
||||
func (mode *Mode) ValidateUpdate() bool {
|
||||
switch strutils.ToLowerNoSnake(string(*mode)) {
|
||||
case "":
|
||||
return true
|
||||
case string(RoundRobin):
|
||||
*mode = RoundRobin
|
||||
return true
|
||||
case string(LeastConn):
|
||||
*mode = LeastConn
|
||||
return true
|
||||
case string(IPHash):
|
||||
*mode = IPHash
|
||||
return true
|
||||
}
|
||||
*mode = RoundRobin
|
||||
return false
|
||||
}
|
|
@ -13,7 +13,7 @@ func (*LoadBalancer) newRoundRobin() impl { return &roundRobin{} }
|
|||
func (lb *roundRobin) OnAddServer(srv *Server) {}
|
||||
func (lb *roundRobin) OnRemoveServer(srv *Server) {}
|
||||
|
||||
func (lb *roundRobin) ServeHTTP(srvs servers, rw http.ResponseWriter, r *http.Request) {
|
||||
func (lb *roundRobin) ServeHTTP(srvs Servers, rw http.ResponseWriter, r *http.Request) {
|
||||
index := lb.index.Add(1) % uint32(len(srvs))
|
||||
srvs[index].ServeHTTP(rw, r)
|
||||
if lb.index.Load() >= 2*uint32(len(srvs)) {
|
||||
|
|
14
internal/net/http/loadbalancer/types.go
Normal file
14
internal/net/http/loadbalancer/types.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
package loadbalancer
|
||||
|
||||
import (
|
||||
"github.com/yusing/go-proxy/internal/net/http/loadbalancer/types"
|
||||
)
|
||||
|
||||
type (
|
||||
Server = types.Server
|
||||
Servers = types.Servers
|
||||
Pool = types.Pool
|
||||
Weight = types.Weight
|
||||
Config = types.Config
|
||||
Mode = types.Mode
|
||||
)
|
8
internal/net/http/loadbalancer/types/config.go
Normal file
8
internal/net/http/loadbalancer/types/config.go
Normal file
|
@ -0,0 +1,8 @@
|
|||
package types
|
||||
|
||||
type Config struct {
|
||||
Link string `json:"link" yaml:"link"`
|
||||
Mode Mode `json:"mode" yaml:"mode"`
|
||||
Weight Weight `json:"weight" yaml:"weight"`
|
||||
Options map[string]any `json:"options,omitempty" yaml:"options,omitempty"`
|
||||
}
|
32
internal/net/http/loadbalancer/types/mode.go
Normal file
32
internal/net/http/loadbalancer/types/mode.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
type Mode string
|
||||
|
||||
const (
|
||||
ModeUnset Mode = ""
|
||||
ModeRoundRobin Mode = "roundrobin"
|
||||
ModeLeastConn Mode = "leastconn"
|
||||
ModeIPHash Mode = "iphash"
|
||||
)
|
||||
|
||||
func (mode *Mode) ValidateUpdate() bool {
|
||||
switch strutils.ToLowerNoSnake(string(*mode)) {
|
||||
case "":
|
||||
return true
|
||||
case string(ModeRoundRobin):
|
||||
*mode = ModeRoundRobin
|
||||
return true
|
||||
case string(ModeLeastConn):
|
||||
*mode = ModeLeastConn
|
||||
return true
|
||||
case string(ModeIPHash):
|
||||
*mode = ModeIPHash
|
||||
return true
|
||||
}
|
||||
*mode = ModeRoundRobin
|
||||
return false
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
package loadbalancer
|
||||
package types
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
idlewatcher "github.com/yusing/go-proxy/internal/docker/idlewatcher/types"
|
||||
"github.com/yusing/go-proxy/internal/net/types"
|
||||
U "github.com/yusing/go-proxy/internal/utils"
|
||||
F "github.com/yusing/go-proxy/internal/utils/functional"
|
||||
|
@ -16,18 +17,18 @@ type (
|
|||
|
||||
Name string
|
||||
URL types.URL
|
||||
Weight weightType
|
||||
Weight Weight
|
||||
|
||||
handler http.Handler
|
||||
healthMon health.HealthMonitor
|
||||
}
|
||||
servers = []*Server
|
||||
Servers = []*Server
|
||||
Pool = F.Map[string, *Server]
|
||||
)
|
||||
|
||||
var newPool = F.NewMap[Pool]
|
||||
var NewServerPool = F.NewMap[Pool]
|
||||
|
||||
func NewServer(name string, url types.URL, weight weightType, handler http.Handler, healthMon health.HealthMonitor) *Server {
|
||||
func NewServer(name string, url types.URL, weight Weight, handler http.Handler, healthMon health.HealthMonitor) *Server {
|
||||
srv := &Server{
|
||||
Name: name,
|
||||
URL: url,
|
||||
|
@ -53,3 +54,17 @@ func (srv *Server) Status() health.Status {
|
|||
func (srv *Server) Uptime() time.Duration {
|
||||
return srv.healthMon.Uptime()
|
||||
}
|
||||
|
||||
func (srv *Server) TryWake() error {
|
||||
waker, ok := srv.handler.(idlewatcher.Waker)
|
||||
if ok {
|
||||
if err := waker.Wake(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) HealthMonitor() health.HealthMonitor {
|
||||
return srv.healthMon
|
||||
}
|
3
internal/net/http/loadbalancer/types/weight.go
Normal file
3
internal/net/http/loadbalancer/types/weight.go
Normal file
|
@ -0,0 +1,3 @@
|
|||
package types
|
||||
|
||||
type Weight uint16
|
|
@ -1,25 +1,14 @@
|
|||
package entry
|
||||
|
||||
import (
|
||||
idlewatcher "github.com/yusing/go-proxy/internal/docker/idlewatcher/types"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/net/http/loadbalancer"
|
||||
net "github.com/yusing/go-proxy/internal/net/types"
|
||||
T "github.com/yusing/go-proxy/internal/proxy/fields"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
route "github.com/yusing/go-proxy/internal/route/types"
|
||||
)
|
||||
|
||||
type Entry interface {
|
||||
TargetName() string
|
||||
TargetURL() net.URL
|
||||
RawEntry() *RawEntry
|
||||
LoadBalanceConfig() *loadbalancer.Config
|
||||
HealthCheckConfig() *health.HealthCheckConfig
|
||||
IdlewatcherConfig() *idlewatcher.Config
|
||||
}
|
||||
type Entry = route.Entry
|
||||
|
||||
func ValidateEntry(m *RawEntry) (Entry, E.Error) {
|
||||
scheme, err := T.NewScheme(m.Scheme)
|
||||
func ValidateEntry(m *route.RawEntry) (Entry, E.Error) {
|
||||
scheme, err := route.NewScheme(m.Scheme)
|
||||
if err != nil {
|
||||
return nil, E.From(err)
|
||||
}
|
|
@ -7,22 +7,22 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/docker"
|
||||
idlewatcher "github.com/yusing/go-proxy/internal/docker/idlewatcher/types"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/net/http/loadbalancer"
|
||||
loadbalance "github.com/yusing/go-proxy/internal/net/http/loadbalancer/types"
|
||||
net "github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/proxy/fields"
|
||||
route "github.com/yusing/go-proxy/internal/route/types"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
)
|
||||
|
||||
type ReverseProxyEntry struct { // real model after validation
|
||||
Raw *RawEntry `json:"raw"`
|
||||
Raw *route.RawEntry `json:"raw"`
|
||||
|
||||
Alias fields.Alias `json:"alias"`
|
||||
Scheme fields.Scheme `json:"scheme"`
|
||||
Alias route.Alias `json:"alias"`
|
||||
Scheme route.Scheme `json:"scheme"`
|
||||
URL net.URL `json:"url"`
|
||||
NoTLSVerify bool `json:"no_tls_verify,omitempty"`
|
||||
PathPatterns fields.PathPatterns `json:"path_patterns,omitempty"`
|
||||
PathPatterns route.PathPatterns `json:"path_patterns,omitempty"`
|
||||
HealthCheck *health.HealthCheckConfig `json:"healthcheck,omitempty"`
|
||||
LoadBalance *loadbalancer.Config `json:"load_balance,omitempty"`
|
||||
LoadBalance *loadbalance.Config `json:"load_balance,omitempty"`
|
||||
Middlewares map[string]docker.LabelMap `json:"middlewares,omitempty"`
|
||||
|
||||
/* Docker only */
|
||||
|
@ -37,11 +37,11 @@ func (rp *ReverseProxyEntry) TargetURL() net.URL {
|
|||
return rp.URL
|
||||
}
|
||||
|
||||
func (rp *ReverseProxyEntry) RawEntry() *RawEntry {
|
||||
func (rp *ReverseProxyEntry) RawEntry() *route.RawEntry {
|
||||
return rp.Raw
|
||||
}
|
||||
|
||||
func (rp *ReverseProxyEntry) LoadBalanceConfig() *loadbalancer.Config {
|
||||
func (rp *ReverseProxyEntry) LoadBalanceConfig() *loadbalance.Config {
|
||||
return rp.LoadBalance
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ func (rp *ReverseProxyEntry) IdlewatcherConfig() *idlewatcher.Config {
|
|||
return rp.Idlewatcher
|
||||
}
|
||||
|
||||
func validateRPEntry(m *RawEntry, s fields.Scheme, errs *E.Builder) *ReverseProxyEntry {
|
||||
func validateRPEntry(m *route.RawEntry, s route.Scheme, errs *E.Builder) *ReverseProxyEntry {
|
||||
cont := m.Container
|
||||
if cont == nil {
|
||||
cont = docker.DummyContainer
|
||||
|
@ -64,9 +64,9 @@ func validateRPEntry(m *RawEntry, s fields.Scheme, errs *E.Builder) *ReverseProx
|
|||
lb = nil
|
||||
}
|
||||
|
||||
host := E.Collect(errs, fields.ValidateHost, m.Host)
|
||||
port := E.Collect(errs, fields.ValidatePort, m.Port)
|
||||
pathPats := E.Collect(errs, fields.ValidatePathPatterns, m.PathPatterns)
|
||||
host := E.Collect(errs, route.ValidateHost, m.Host)
|
||||
port := E.Collect(errs, route.ValidatePort, m.Port)
|
||||
pathPats := E.Collect(errs, route.ValidatePathPatterns, m.PathPatterns)
|
||||
url := E.Collect(errs, url.Parse, fmt.Sprintf("%s://%s:%d", s, host, port))
|
||||
iwCfg := E.Collect(errs, idlewatcher.ValidateConfig, cont)
|
||||
|
||||
|
@ -76,7 +76,7 @@ func validateRPEntry(m *RawEntry, s fields.Scheme, errs *E.Builder) *ReverseProx
|
|||
|
||||
return &ReverseProxyEntry{
|
||||
Raw: m,
|
||||
Alias: fields.Alias(m.Alias),
|
||||
Alias: route.Alias(m.Alias),
|
||||
Scheme: s,
|
||||
URL: net.NewURL(url),
|
||||
NoTLSVerify: m.NoTLSVerify,
|
|
@ -6,20 +6,20 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/docker"
|
||||
idlewatcher "github.com/yusing/go-proxy/internal/docker/idlewatcher/types"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/net/http/loadbalancer"
|
||||
loadbalance "github.com/yusing/go-proxy/internal/net/http/loadbalancer/types"
|
||||
net "github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/proxy/fields"
|
||||
route "github.com/yusing/go-proxy/internal/route/types"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
)
|
||||
|
||||
type StreamEntry struct {
|
||||
Raw *RawEntry `json:"raw"`
|
||||
Raw *route.RawEntry `json:"raw"`
|
||||
|
||||
Alias fields.Alias `json:"alias"`
|
||||
Scheme fields.StreamScheme `json:"scheme"`
|
||||
Alias route.Alias `json:"alias"`
|
||||
Scheme route.StreamScheme `json:"scheme"`
|
||||
URL net.URL `json:"url"`
|
||||
Host fields.Host `json:"host,omitempty"`
|
||||
Port fields.StreamPort `json:"port,omitempty"`
|
||||
Host route.Host `json:"host,omitempty"`
|
||||
Port route.StreamPort `json:"port,omitempty"`
|
||||
HealthCheck *health.HealthCheckConfig `json:"healthcheck,omitempty"`
|
||||
|
||||
/* Docker only */
|
||||
|
@ -34,11 +34,11 @@ func (s *StreamEntry) TargetURL() net.URL {
|
|||
return s.URL
|
||||
}
|
||||
|
||||
func (s *StreamEntry) RawEntry() *RawEntry {
|
||||
func (s *StreamEntry) RawEntry() *route.RawEntry {
|
||||
return s.Raw
|
||||
}
|
||||
|
||||
func (s *StreamEntry) LoadBalanceConfig() *loadbalancer.Config {
|
||||
func (s *StreamEntry) LoadBalanceConfig() *loadbalance.Config {
|
||||
// TODO: support stream load balance
|
||||
return nil
|
||||
}
|
||||
|
@ -51,15 +51,15 @@ func (s *StreamEntry) IdlewatcherConfig() *idlewatcher.Config {
|
|||
return s.Idlewatcher
|
||||
}
|
||||
|
||||
func validateStreamEntry(m *RawEntry, errs *E.Builder) *StreamEntry {
|
||||
func validateStreamEntry(m *route.RawEntry, errs *E.Builder) *StreamEntry {
|
||||
cont := m.Container
|
||||
if cont == nil {
|
||||
cont = docker.DummyContainer
|
||||
}
|
||||
|
||||
host := E.Collect(errs, fields.ValidateHost, m.Host)
|
||||
port := E.Collect(errs, fields.ValidateStreamPort, m.Port)
|
||||
scheme := E.Collect(errs, fields.ValidateStreamScheme, m.Scheme)
|
||||
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))
|
||||
idleWatcherCfg := E.Collect(errs, idlewatcher.ValidateConfig, cont)
|
||||
|
||||
|
@ -69,7 +69,7 @@ func validateStreamEntry(m *RawEntry, errs *E.Builder) *StreamEntry {
|
|||
|
||||
return &StreamEntry{
|
||||
Raw: m,
|
||||
Alias: fields.Alias(m.Alias),
|
||||
Alias: route.Alias(m.Alias),
|
||||
Scheme: *scheme,
|
||||
URL: url,
|
||||
Host: host,
|
|
@ -1,10 +1,7 @@
|
|||
package route
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
|
@ -12,12 +9,12 @@ import (
|
|||
E "github.com/yusing/go-proxy/internal/error"
|
||||
gphttp "github.com/yusing/go-proxy/internal/net/http"
|
||||
"github.com/yusing/go-proxy/internal/net/http/loadbalancer"
|
||||
loadbalance "github.com/yusing/go-proxy/internal/net/http/loadbalancer/types"
|
||||
"github.com/yusing/go-proxy/internal/net/http/middleware"
|
||||
"github.com/yusing/go-proxy/internal/net/http/middleware/errorpage"
|
||||
"github.com/yusing/go-proxy/internal/proxy/entry"
|
||||
PT "github.com/yusing/go-proxy/internal/proxy/fields"
|
||||
"github.com/yusing/go-proxy/internal/route/entry"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
route "github.com/yusing/go-proxy/internal/route/types"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
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/monitor"
|
||||
)
|
||||
|
@ -38,27 +35,10 @@ type (
|
|||
l zerolog.Logger
|
||||
}
|
||||
|
||||
SubdomainKey = PT.Alias
|
||||
SubdomainKey = route.Alias
|
||||
)
|
||||
|
||||
var (
|
||||
findMuxFunc = findMuxAnyDomain
|
||||
|
||||
httpRoutes = F.NewMapOf[string, *HTTPRoute]()
|
||||
// globalMux = http.NewServeMux() // TODO: support regex subdomain matching.
|
||||
)
|
||||
|
||||
func GetReverseProxies() F.Map[string, *HTTPRoute] {
|
||||
return httpRoutes
|
||||
}
|
||||
|
||||
func SetFindMuxDomains(domains []string) {
|
||||
if len(domains) == 0 {
|
||||
findMuxFunc = findMuxAnyDomain
|
||||
} else {
|
||||
findMuxFunc = findMuxByDomains(domains)
|
||||
}
|
||||
}
|
||||
// var globalMux = http.NewServeMux() // TODO: support regex subdomain matching.
|
||||
|
||||
func NewHTTPRoute(entry *entry.ReverseProxyEntry) (impl, E.Error) {
|
||||
var trans *http.Transport
|
||||
|
@ -141,9 +121,9 @@ func (r *HTTPRoute) Start(providerSubtask task.Task) E.Error {
|
|||
if entry.UseLoadBalance(r) {
|
||||
r.addToLoadBalancer()
|
||||
} else {
|
||||
httpRoutes.Store(string(r.Alias), r)
|
||||
routes.SetHTTPRoute(string(r.Alias), r)
|
||||
r.task.OnFinished("remove from route table", func() {
|
||||
httpRoutes.Delete(string(r.Alias))
|
||||
routes.DeleteHTTPRoute(string(r.Alias))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -164,7 +144,8 @@ func (r *HTTPRoute) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
|
||||
func (r *HTTPRoute) addToLoadBalancer() {
|
||||
var lb *loadbalancer.LoadBalancer
|
||||
linked, ok := httpRoutes.Load(r.LoadBalance.Link)
|
||||
l, ok := routes.GetHTTPRoute(r.LoadBalance.Link)
|
||||
linked := l.(*HTTPRoute)
|
||||
if ok {
|
||||
lb = linked.loadBalancer
|
||||
lb.UpdateConfigIfNeeded(r.LoadBalance)
|
||||
|
@ -175,96 +156,26 @@ func (r *HTTPRoute) addToLoadBalancer() {
|
|||
lb = loadbalancer.New(r.LoadBalance)
|
||||
lbTask := r.task.Parent().Subtask("loadbalancer " + r.LoadBalance.Link)
|
||||
lbTask.OnCancel("remove lb from routes", func() {
|
||||
httpRoutes.Delete(r.LoadBalance.Link)
|
||||
routes.DeleteHTTPRoute(r.LoadBalance.Link)
|
||||
})
|
||||
lb.Start(lbTask)
|
||||
linked = &HTTPRoute{
|
||||
ReverseProxyEntry: &entry.ReverseProxyEntry{
|
||||
Raw: &entry.RawEntry{
|
||||
Raw: &route.RawEntry{
|
||||
Homepage: r.Raw.Homepage,
|
||||
},
|
||||
Alias: PT.Alias(lb.Link),
|
||||
Alias: route.Alias(lb.Link),
|
||||
},
|
||||
HealthMon: lb,
|
||||
loadBalancer: lb,
|
||||
handler: lb,
|
||||
}
|
||||
httpRoutes.Store(r.LoadBalance.Link, linked)
|
||||
routes.SetHTTPRoute(r.LoadBalance.Link, linked)
|
||||
}
|
||||
r.loadBalancer = lb
|
||||
r.server = loadbalancer.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.LoadBalance.Weight, r.handler, r.HealthMon)
|
||||
lb.AddServer(r.server)
|
||||
r.task.OnCancel("remove server from lb", func() {
|
||||
lb.RemoveServer(r.server)
|
||||
})
|
||||
}
|
||||
|
||||
func ProxyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
mux, err := findMuxFunc(r.Host)
|
||||
if err == nil {
|
||||
mux.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
// Why use StatusNotFound instead of StatusBadRequest or StatusBadGateway?
|
||||
// On nginx, when route for domain does not exist, it returns StatusBadGateway.
|
||||
// Then scraper / scanners will know the subdomain is invalid.
|
||||
// With StatusNotFound, they won't know whether it's the path, or the subdomain that is invalid.
|
||||
if !middleware.ServeStaticErrorPageFile(w, r) {
|
||||
logger.Err(err).Str("method", r.Method).Str("url", r.URL.String()).Msg("request")
|
||||
errorPage, ok := errorpage.GetErrorPageByStatus(http.StatusNotFound)
|
||||
if ok {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if _, err := w.Write(errorPage); err != nil {
|
||||
logger.Err(err).Msg("failed to write error page")
|
||||
}
|
||||
} else {
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func findMuxAnyDomain(host string) (http.Handler, error) {
|
||||
hostSplit := strings.Split(host, ".")
|
||||
n := len(hostSplit)
|
||||
switch {
|
||||
case n == 3:
|
||||
host = hostSplit[0]
|
||||
case n > 3:
|
||||
var builder strings.Builder
|
||||
builder.Grow(2*n - 3)
|
||||
builder.WriteString(hostSplit[0])
|
||||
for _, part := range hostSplit[:n-2] {
|
||||
builder.WriteRune('.')
|
||||
builder.WriteString(part)
|
||||
}
|
||||
host = builder.String()
|
||||
default:
|
||||
return nil, errors.New("missing subdomain in url")
|
||||
}
|
||||
if r, ok := httpRoutes.Load(host); ok {
|
||||
return r.handler, nil
|
||||
}
|
||||
return nil, fmt.Errorf("no such route: %s", host)
|
||||
}
|
||||
|
||||
func findMuxByDomains(domains []string) func(host string) (http.Handler, error) {
|
||||
return func(host string) (http.Handler, error) {
|
||||
var subdomain string
|
||||
|
||||
for _, domain := range domains {
|
||||
if strings.HasSuffix(host, domain) {
|
||||
subdomain = strings.TrimSuffix(host, domain)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if subdomain != "" { // matched
|
||||
if r, ok := httpRoutes.Load(subdomain); ok {
|
||||
return r.handler, nil
|
||||
}
|
||||
return nil, fmt.Errorf("no such route: %s", subdomain)
|
||||
}
|
||||
return nil, fmt.Errorf("%s does not match any base domain", host)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/docker"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/proxy/entry"
|
||||
"github.com/yusing/go-proxy/internal/route"
|
||||
U "github.com/yusing/go-proxy/internal/utils"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
|
@ -55,7 +54,7 @@ func (p *DockerProvider) NewWatcher() watcher.Watcher {
|
|||
|
||||
func (p *DockerProvider) loadRoutesImpl() (route.Routes, E.Error) {
|
||||
routes := route.NewRoutes()
|
||||
entries := entry.NewProxyEntries()
|
||||
entries := route.NewProxyEntries()
|
||||
|
||||
containers, err := docker.ListContainers(p.dockerHost)
|
||||
if err != nil {
|
||||
|
@ -78,7 +77,7 @@ func (p *DockerProvider) loadRoutesImpl() (route.Routes, E.Error) {
|
|||
// there may be some valid entries in `en`
|
||||
dups := entries.MergeFrom(newEntries)
|
||||
// add the duplicate proxy entries to the error
|
||||
dups.RangeAll(func(k string, v *entry.RawEntry) {
|
||||
dups.RangeAll(func(k string, v *route.RawEntry) {
|
||||
errs.Addf("duplicated alias %s", k)
|
||||
})
|
||||
}
|
||||
|
@ -98,8 +97,8 @@ func (p *DockerProvider) shouldIgnore(container *docker.Container) bool {
|
|||
|
||||
// Returns a list of proxy entries for a container.
|
||||
// Always non-nil.
|
||||
func (p *DockerProvider) entriesFromContainerLabels(container *docker.Container) (entries entry.RawEntries, _ E.Error) {
|
||||
entries = entry.NewProxyEntries()
|
||||
func (p *DockerProvider) entriesFromContainerLabels(container *docker.Container) (entries route.RawEntries, _ E.Error) {
|
||||
entries = route.NewProxyEntries()
|
||||
|
||||
if p.shouldIgnore(container) {
|
||||
return
|
||||
|
@ -107,7 +106,7 @@ func (p *DockerProvider) entriesFromContainerLabels(container *docker.Container)
|
|||
|
||||
// init entries map for all aliases
|
||||
for _, a := range container.Aliases {
|
||||
entries.Store(a, &entry.RawEntry{
|
||||
entries.Store(a, &route.RawEntry{
|
||||
Alias: a,
|
||||
Container: container,
|
||||
})
|
||||
|
@ -154,9 +153,9 @@ func (p *DockerProvider) entriesFromContainerLabels(container *docker.Container)
|
|||
}
|
||||
|
||||
// init entry if not exist
|
||||
var en *entry.RawEntry
|
||||
var en *route.RawEntry
|
||||
if en, ok = entries.Load(alias); !ok {
|
||||
en = &entry.RawEntry{
|
||||
en = &route.RawEntry{
|
||||
Alias: alias,
|
||||
Container: container,
|
||||
}
|
||||
|
@ -172,7 +171,7 @@ func (p *DockerProvider) entriesFromContainerLabels(container *docker.Container)
|
|||
}
|
||||
}
|
||||
if wildcardProps != nil {
|
||||
entries.RangeAll(func(alias string, re *entry.RawEntry) {
|
||||
entries.RangeAll(func(alias string, re *route.RawEntry) {
|
||||
if err := U.Deserialize(wildcardProps, re); err != nil {
|
||||
errs.Add(err.Subject(alias))
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/common"
|
||||
D "github.com/yusing/go-proxy/internal/docker"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/proxy/entry"
|
||||
T "github.com/yusing/go-proxy/internal/proxy/fields"
|
||||
"github.com/yusing/go-proxy/internal/route/entry"
|
||||
T "github.com/yusing/go-proxy/internal/route/types"
|
||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||
)
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@ package provider
|
|||
import (
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/proxy/entry"
|
||||
"github.com/yusing/go-proxy/internal/route"
|
||||
"github.com/yusing/go-proxy/internal/route/entry"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/internal/watcher"
|
||||
)
|
||||
|
|
|
@ -7,8 +7,7 @@ import (
|
|||
"github.com/rs/zerolog"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/proxy/entry"
|
||||
R "github.com/yusing/go-proxy/internal/route"
|
||||
"github.com/yusing/go-proxy/internal/route"
|
||||
U "github.com/yusing/go-proxy/internal/utils"
|
||||
W "github.com/yusing/go-proxy/internal/watcher"
|
||||
)
|
||||
|
@ -44,9 +43,9 @@ func (p *FileProvider) Logger() *zerolog.Logger {
|
|||
return &p.l
|
||||
}
|
||||
|
||||
func (p *FileProvider) loadRoutesImpl() (R.Routes, E.Error) {
|
||||
routes := R.NewRoutes()
|
||||
entries := entry.NewProxyEntries()
|
||||
func (p *FileProvider) loadRoutesImpl() (route.Routes, E.Error) {
|
||||
routes := route.NewRoutes()
|
||||
entries := route.NewProxyEntries()
|
||||
|
||||
data, err := os.ReadFile(p.path)
|
||||
if err != nil {
|
||||
|
@ -61,7 +60,7 @@ func (p *FileProvider) loadRoutesImpl() (R.Routes, E.Error) {
|
|||
E.LogWarn("validation failure", err.Subject(p.fileName))
|
||||
}
|
||||
|
||||
return R.FromEntries(entries)
|
||||
return route.FromEntries(entries)
|
||||
}
|
||||
|
||||
func (p *FileProvider) NewWatcher() W.Watcher {
|
||||
|
|
|
@ -4,7 +4,8 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/docker"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
url "github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/proxy/entry"
|
||||
"github.com/yusing/go-proxy/internal/route/entry"
|
||||
"github.com/yusing/go-proxy/internal/route/types"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
U "github.com/yusing/go-proxy/internal/utils"
|
||||
F "github.com/yusing/go-proxy/internal/utils/functional"
|
||||
|
@ -16,7 +17,7 @@ type (
|
|||
_ U.NoCopy
|
||||
impl
|
||||
Type RouteType
|
||||
Entry *entry.RawEntry
|
||||
Entry *RawEntry
|
||||
}
|
||||
Routes = F.Map[string, *Route]
|
||||
|
||||
|
@ -27,6 +28,8 @@ type (
|
|||
String() string
|
||||
TargetURL() url.URL
|
||||
}
|
||||
RawEntry = types.RawEntry
|
||||
RawEntries = types.RawEntries
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -36,6 +39,7 @@ const (
|
|||
|
||||
// function alias.
|
||||
var NewRoutes = F.NewMap[Routes]
|
||||
var NewProxyEntries = types.NewProxyEntries
|
||||
|
||||
func (rt *Route) Container() *docker.Container {
|
||||
if rt.Entry.Container == nil {
|
||||
|
@ -44,7 +48,7 @@ func (rt *Route) Container() *docker.Container {
|
|||
return rt.Entry.Container
|
||||
}
|
||||
|
||||
func NewRoute(raw *entry.RawEntry) (*Route, E.Error) {
|
||||
func NewRoute(raw *RawEntry) (*Route, E.Error) {
|
||||
raw.Finalize()
|
||||
en, err := entry.ValidateEntry(raw)
|
||||
if err != nil {
|
||||
|
@ -74,11 +78,11 @@ func NewRoute(raw *entry.RawEntry) (*Route, E.Error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func FromEntries(entries entry.RawEntries) (Routes, E.Error) {
|
||||
func FromEntries(entries RawEntries) (Routes, E.Error) {
|
||||
b := E.NewBuilder("errors in routes")
|
||||
|
||||
routes := NewRoutes()
|
||||
entries.RangeAllParallel(func(alias string, en *entry.RawEntry) {
|
||||
entries.RangeAllParallel(func(alias string, en *RawEntry) {
|
||||
en.Alias = alias
|
||||
r, err := NewRoute(en)
|
||||
switch {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package route
|
||||
package routes
|
||||
|
||||
import "github.com/yusing/go-proxy/internal/metrics"
|
||||
|
43
internal/route/routes/routes.go
Normal file
43
internal/route/routes/routes.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"github.com/yusing/go-proxy/internal/route/types"
|
||||
F "github.com/yusing/go-proxy/internal/utils/functional"
|
||||
)
|
||||
|
||||
var (
|
||||
httpRoutes = F.NewMapOf[string, types.HTTPRoute]()
|
||||
streamRoutes = F.NewMapOf[string, types.StreamRoute]()
|
||||
)
|
||||
|
||||
func GetHTTPRoutes() F.Map[string, types.HTTPRoute] {
|
||||
return httpRoutes
|
||||
}
|
||||
|
||||
func GetStreamRoutes() F.Map[string, types.StreamRoute] {
|
||||
return streamRoutes
|
||||
}
|
||||
|
||||
func GetHTTPRoute(alias string) (types.HTTPRoute, bool) {
|
||||
return httpRoutes.Load(alias)
|
||||
}
|
||||
|
||||
func GetStreamRoute(alias string) (types.StreamRoute, bool) {
|
||||
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)
|
||||
}
|
|
@ -9,9 +9,9 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/docker/idlewatcher"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
net "github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/proxy/entry"
|
||||
"github.com/yusing/go-proxy/internal/route/entry"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
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/monitor"
|
||||
)
|
||||
|
@ -19,7 +19,7 @@ import (
|
|||
type StreamRoute struct {
|
||||
*entry.StreamEntry
|
||||
|
||||
stream net.Stream
|
||||
net.Stream
|
||||
|
||||
HealthMon health.HealthMonitor `json:"health"`
|
||||
|
||||
|
@ -28,12 +28,6 @@ type StreamRoute struct {
|
|||
l zerolog.Logger
|
||||
}
|
||||
|
||||
var streamRoutes = F.NewMapOf[string, *StreamRoute]()
|
||||
|
||||
func GetStreamProxies() F.Map[string, *StreamRoute] {
|
||||
return streamRoutes
|
||||
}
|
||||
|
||||
func NewStreamRoute(entry *entry.StreamEntry) (impl, E.Error) {
|
||||
// TODO: support non-coherent scheme
|
||||
if !entry.Scheme.IsCoherent() {
|
||||
|
@ -60,29 +54,29 @@ func (r *StreamRoute) Start(providerSubtask task.Task) E.Error {
|
|||
}
|
||||
|
||||
r.task = providerSubtask
|
||||
r.stream = NewStream(r)
|
||||
r.Stream = NewStream(r)
|
||||
|
||||
switch {
|
||||
case entry.UseIdleWatcher(r):
|
||||
wakerTask := providerSubtask.Parent().Subtask("waker for " + string(r.Alias))
|
||||
waker, err := idlewatcher.NewStreamWaker(wakerTask, r.StreamEntry, r.stream)
|
||||
waker, err := idlewatcher.NewStreamWaker(wakerTask, r.StreamEntry, r.Stream)
|
||||
if err != nil {
|
||||
r.task.Finish(err)
|
||||
return err
|
||||
}
|
||||
r.stream = waker
|
||||
r.Stream = waker
|
||||
r.HealthMon = waker
|
||||
case entry.UseHealthCheck(r):
|
||||
r.HealthMon = monitor.NewRawHealthMonitor(r.TargetURL(), r.HealthCheck)
|
||||
}
|
||||
|
||||
if err := r.stream.Setup(); err != nil {
|
||||
if err := r.Stream.Setup(); err != nil {
|
||||
r.task.Finish(err)
|
||||
return E.From(err)
|
||||
}
|
||||
|
||||
r.task.OnFinished("close stream", func() {
|
||||
if err := r.stream.Close(); err != nil {
|
||||
if err := r.Stream.Close(); err != nil {
|
||||
E.LogError("close stream failed", err, &r.l)
|
||||
}
|
||||
})
|
||||
|
@ -101,9 +95,9 @@ func (r *StreamRoute) Start(providerSubtask task.Task) E.Error {
|
|||
|
||||
go r.acceptConnections()
|
||||
|
||||
streamRoutes.Store(string(r.Alias), r)
|
||||
routes.SetStreamRoute(string(r.Alias), r)
|
||||
r.task.OnFinished("remove from route table", func() {
|
||||
streamRoutes.Delete(string(r.Alias))
|
||||
routes.DeleteStreamRoute(string(r.Alias))
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
@ -120,7 +114,7 @@ func (r *StreamRoute) acceptConnections() {
|
|||
case <-r.task.Context().Done():
|
||||
return
|
||||
default:
|
||||
conn, err := r.stream.Accept()
|
||||
conn, err := r.Stream.Accept()
|
||||
if err != nil {
|
||||
select {
|
||||
case <-r.task.Context().Done():
|
||||
|
@ -135,7 +129,7 @@ func (r *StreamRoute) acceptConnections() {
|
|||
}
|
||||
connTask := r.task.Subtask("connection")
|
||||
go func() {
|
||||
err := r.stream.Handle(conn)
|
||||
err := r.Stream.Handle(conn)
|
||||
if err != nil && !errors.Is(err, context.Canceled) {
|
||||
E.LogError("handle connection error", err, &r.l)
|
||||
connTask.Finish(err)
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/net/types"
|
||||
T "github.com/yusing/go-proxy/internal/proxy/fields"
|
||||
T "github.com/yusing/go-proxy/internal/route/types"
|
||||
U "github.com/yusing/go-proxy/internal/utils"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
package fields
|
||||
package types
|
||||
|
||||
type Alias string
|
17
internal/route/types/entry.go
Normal file
17
internal/route/types/entry.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
idlewatcher "github.com/yusing/go-proxy/internal/docker/idlewatcher/types"
|
||||
loadbalance "github.com/yusing/go-proxy/internal/net/http/loadbalancer/types"
|
||||
net "github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
)
|
||||
|
||||
type Entry interface {
|
||||
TargetName() string
|
||||
TargetURL() net.URL
|
||||
RawEntry() *RawEntry
|
||||
LoadBalanceConfig() *loadbalance.Config
|
||||
HealthCheckConfig() *health.HealthCheckConfig
|
||||
IdlewatcherConfig() *idlewatcher.Config
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package fields
|
||||
package types
|
||||
|
||||
import (
|
||||
"net/http"
|
|
@ -1,4 +1,4 @@
|
|||
package fields
|
||||
package types
|
||||
|
||||
type (
|
||||
Host string
|
|
@ -1,4 +1,4 @@
|
|||
package fields
|
||||
package types
|
||||
|
||||
import (
|
||||
"errors"
|
|
@ -1,4 +1,4 @@
|
|||
package fields
|
||||
package types
|
||||
|
||||
import (
|
||||
"errors"
|
|
@ -1,4 +1,4 @@
|
|||
package fields
|
||||
package types
|
||||
|
||||
import (
|
||||
"strconv"
|
|
@ -1,4 +1,4 @@
|
|||
package entry
|
||||
package types
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/docker"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/net/http/loadbalancer"
|
||||
loadbalance "github.com/yusing/go-proxy/internal/net/http/loadbalancer/types"
|
||||
U "github.com/yusing/go-proxy/internal/utils"
|
||||
F "github.com/yusing/go-proxy/internal/utils/functional"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
|
@ -29,7 +29,7 @@ type (
|
|||
NoTLSVerify bool `json:"no_tls_verify,omitempty" yaml:"no_tls_verify"` // https proxy only
|
||||
PathPatterns []string `json:"path_patterns,omitempty" yaml:"path_patterns"` // http(s) proxy only
|
||||
HealthCheck *health.HealthCheckConfig `json:"healthcheck,omitempty" yaml:"healthcheck"`
|
||||
LoadBalance *loadbalancer.Config `json:"load_balance,omitempty" yaml:"load_balance"`
|
||||
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"`
|
||||
|
18
internal/route/types/route.go
Normal file
18
internal/route/types/route.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
net "github.com/yusing/go-proxy/internal/net/types"
|
||||
)
|
||||
|
||||
type (
|
||||
HTTPRoute interface {
|
||||
Entry
|
||||
http.Handler
|
||||
}
|
||||
StreamRoute interface {
|
||||
Entry
|
||||
net.Stream
|
||||
}
|
||||
)
|
|
@ -1,4 +1,4 @@
|
|||
package fields
|
||||
package types
|
||||
|
||||
import (
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
|
@ -1,4 +1,4 @@
|
|||
package fields
|
||||
package types
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package fields
|
||||
package types
|
||||
|
||||
import (
|
||||
"strconv"
|
|
@ -1,4 +1,4 @@
|
|||
package fields
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,4 +1,4 @@
|
|||
package fields
|
||||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
Loading…
Add table
Reference in a new issue