Compare commits

..

18 commits

Author SHA1 Message Date
yusing
f179de9231 fix(setup): update DNS provider docs link in setup script 2025-06-15 10:00:53 +08:00
yusing
1d546624de fix(serialization): call of reflect.Value.IsNil on string Value 2025-06-14 22:12:24 +08:00
yusing
ecc9d306d1 refactor(agent): move agent pool to agent package, rename route.Agent() to route.GetAgent() (cont. 7d17a01) 2025-06-14 22:04:10 +08:00
yusing
5ce1c7865e feat(agent): allow specifying agent for routes in route files 2025-06-14 20:05:11 +08:00
yusing
7d17a01de1 refactor(agent): move agent pool to agent package, rename route.Agent() to route.GetAgent() 2025-06-14 20:04:39 +08:00
yusing
cabb840a91 tweak(docker): add hint when specified network not found 2025-06-14 19:32:36 +08:00
yusing
4825f768f3 feat(docker): allow specifying docker network, handle error when no network available 2025-06-14 17:08:07 +08:00
yusing
5fdb023188 feat(docker): add network field to container info 2025-06-14 10:05:45 +08:00
yusing
4abf61a421 refactor(notif): enhance retry mechanism with exponential backoff and jitter; replace retry channel with a set for managing retry messages 2025-06-14 09:31:09 +08:00
yusing
96b7c3fcec chore: upgrade dependenocies
Some checks failed
Docker Image CI (socket-proxy) / build (push) Has been cancelled
2025-06-13 23:06:34 +08:00
yusing
f8c57d930f fix(docker): wildcard labels not applying properly for YAML style values and alias without labels 2025-06-13 23:02:25 +08:00
yusing
880d66c75e docs: update links in config.example.yml to point to the new documentation site 2025-06-12 21:34:50 +08:00
yusing
4649c8d479 chore: update .gitignore to include .cursor directory 2025-06-12 21:08:00 +08:00
DarinDev1000
20021b3cae add GODOXY_API_JWT_SECURE=true to .env 2025-06-12 21:08:00 +08:00
yusing
cfa9201f82 fix(shutdown): change gracefulShutdown to call root.Finish directly instead of in a goroutine 2025-06-09 22:20:49 +08:00
yusing
b5328fe5e7 feat(idlesleep): support idlesleep for stream routes, rewritten and fixed stream implementation 2025-06-09 22:20:26 +08:00
yusing
25fbcc4ab9 fix(label): expand wildcard labels before unmarshaling and add corresponding test 2025-06-09 20:46:39 +08:00
yusing
421aaecba4 refactor: rename net/types to nettypes 2025-06-08 17:59:48 +08:00
70 changed files with 1401 additions and 831 deletions

View file

@ -8,6 +8,8 @@ TZ=ETC/UTC
GODOXY_UID=1000 GODOXY_UID=1000
GODOXY_GID=1000 GODOXY_GID=1000
# Set GODOXY_API_JWT_SECURE=false to allow http
GODOXY_API_JWT_SECURE=true
# API JWT Configuration (common) # API JWT Configuration (common)
# generate secret with `openssl rand -base64 32` # generate secret with `openssl rand -base64 32`
GODOXY_API_JWT_SECRET= GODOXY_API_JWT_SECRET=

1
.gitignore vendored
View file

@ -30,6 +30,7 @@ todo.md
mtrace.json mtrace.json
.env .env
.cursorrules .cursorrules
.cursor/
.windsurfrules .windsurfrules
test.Dockerfile test.Dockerfile

View file

@ -16,7 +16,7 @@ require (
github.com/gorilla/websocket v1.5.3 github.com/gorilla/websocket v1.5.3
github.com/rs/zerolog v1.34.0 github.com/rs/zerolog v1.34.0
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/yusing/go-proxy v0.14.1 github.com/yusing/go-proxy v0.14.2
github.com/yusing/go-proxy/internal/utils v0.0.0 github.com/yusing/go-proxy/internal/utils v0.0.0
github.com/yusing/go-proxy/socketproxy v0.0.0-00010101000000-000000000000 github.com/yusing/go-proxy/socketproxy v0.0.0-00010101000000-000000000000
) )
@ -48,7 +48,7 @@ require (
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect github.com/goccy/go-yaml v1.18.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/pprof v0.0.0-20250602020802-c6617b811d0e // indirect github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect
github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/mux v1.8.1 // indirect
github.com/gotify/server/v2 v2.6.3 // indirect github.com/gotify/server/v2 v2.6.3 // indirect
github.com/jinzhu/copier v0.4.0 // indirect github.com/jinzhu/copier v0.4.0 // indirect
@ -71,7 +71,7 @@ require (
github.com/puzpuzpuz/xsync/v4 v4.1.0 // indirect github.com/puzpuzpuz/xsync/v4 v4.1.0 // indirect
github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.52.0 // indirect github.com/quic-go/quic-go v0.52.0 // indirect
github.com/samber/lo v1.50.0 // indirect github.com/samber/lo v1.51.0 // indirect
github.com/samber/slog-common v0.18.1 // indirect github.com/samber/slog-common v0.18.1 // indirect
github.com/samber/slog-zerolog/v2 v2.7.3 // indirect github.com/samber/slog-zerolog/v2 v2.7.3 // indirect
github.com/shirou/gopsutil/v4 v4.25.5 // indirect github.com/shirou/gopsutil/v4 v4.25.5 // indirect

View file

@ -76,8 +76,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/pprof v0.0.0-20250602020802-c6617b811d0e h1:FJta/0WsADCe1r9vQjdHbd3KuiLPu7Y9WlyLGwMUNyE= github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18=
github.com/google/pprof v0.0.0-20250602020802-c6617b811d0e/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
@ -98,6 +98,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
@ -129,8 +131,6 @@ github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU=
@ -163,8 +163,8 @@ github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/samber/lo v1.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY= github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI=
github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc= github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
github.com/samber/slog-common v0.18.1 h1:c0EipD/nVY9HG5shgm/XAs67mgpWDMF+MmtptdJNCkQ= github.com/samber/slog-common v0.18.1 h1:c0EipD/nVY9HG5shgm/XAs67mgpWDMF+MmtptdJNCkQ=
github.com/samber/slog-common v0.18.1/go.mod h1:QNZiNGKakvrfbJ2YglQXLCZauzkI9xZBjOhWFKS3IKk= github.com/samber/slog-common v0.18.1/go.mod h1:QNZiNGKakvrfbJ2YglQXLCZauzkI9xZBjOhWFKS3IKk=
github.com/samber/slog-zerolog/v2 v2.7.3 h1:/MkPDl/tJhijN2GvB1MWwBn2FU8RiL3rQ8gpXkQm2EY= github.com/samber/slog-zerolog/v2 v2.7.3 h1:/MkPDl/tJhijN2GvB1MWwBn2FU8RiL3rQ8gpXkQm2EY=
@ -325,8 +325,8 @@ google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7E
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -0,0 +1,57 @@
package agent
import (
"github.com/yusing/go-proxy/internal/common"
"github.com/yusing/go-proxy/internal/utils/functional"
)
var agentPool = functional.NewMapOf[string, *AgentConfig]()
func init() {
if common.IsTest {
agentPool.Store("test-agent", &AgentConfig{
Addr: "test-agent",
})
}
}
func GetAgent(agentAddrOrDockerHost string) (*AgentConfig, bool) {
if !IsDockerHostAgent(agentAddrOrDockerHost) {
return getAgentByAddr(agentAddrOrDockerHost)
}
return getAgentByAddr(GetAgentAddrFromDockerHost(agentAddrOrDockerHost))
}
func GetAgentByName(name string) (*AgentConfig, bool) {
for _, agent := range agentPool.Range {
if agent.Name() == name {
return agent, true
}
}
return nil, false
}
func AddAgent(agent *AgentConfig) {
agentPool.Store(agent.Addr, agent)
}
func RemoveAgent(agent *AgentConfig) {
agentPool.Delete(agent.Addr)
}
func RemoveAllAgents() {
agentPool.Clear()
}
func ListAgents() []*AgentConfig {
agents := make([]*AgentConfig, 0, agentPool.Size())
for _, agent := range agentPool.Range {
agents = append(agents, agent)
}
return agents
}
func getAgentByAddr(addr string) (agent *AgentConfig, ok bool) {
agent, ok = agentPool.Load(addr)
return
}

View file

@ -15,7 +15,7 @@
# options: # options:
# auth_token: c1234565789-abcdefghijklmnopqrst # your zone API token # auth_token: c1234565789-abcdefghijklmnopqrst # your zone API token
# 3. other providers, see https://github.com/yusing/godoxy/wiki/Supported-DNS%E2%80%9001-Providers#supported-dns-01-providers # 3. other providers, see https://docs.godoxy.dev/DNS-01-Providers
# acl: # acl:
# default: allow # or deny (default: allow) # default: allow # or deny (default: allow)
@ -115,7 +115,7 @@ providers:
# secret: aaaa-bbbb-cccc-dddd # secret: aaaa-bbbb-cccc-dddd
# no_tls_verify: true # no_tls_verify: true
# Check https://github.com/yusing/godoxy/wiki/Certificates-and-domain-matching#domain-matching # Check https://docs.godoxy.dev/Certificates-and-domain-matching
# for explaination of `match_domains` # for explaination of `match_domains`
# #
# match_domains: # match_domains:

48
go.mod
View file

@ -46,8 +46,8 @@ require (
github.com/samber/slog-zerolog/v2 v2.7.3 github.com/samber/slog-zerolog/v2 v2.7.3
github.com/spf13/afero v1.14.0 github.com/spf13/afero v1.14.0
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/yusing/go-proxy/agent v0.0.0-20250605105311-09c244ef3cdb github.com/yusing/go-proxy/agent v0.0.0-20250612133450-880d66c75e3f
github.com/yusing/go-proxy/internal/dnsproviders v0.0.0-20250605105311-09c244ef3cdb github.com/yusing/go-proxy/internal/dnsproviders v0.0.0-20250612133450-880d66c75e3f
github.com/yusing/go-proxy/internal/utils v0.0.0 github.com/yusing/go-proxy/internal/utils v0.0.0
) )
@ -57,7 +57,7 @@ require (
cloud.google.com/go/compute/metadata v0.7.0 // indirect cloud.google.com/go/compute/metadata v0.7.0 // indirect
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect
@ -68,27 +68,27 @@ require (
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.63.107 // indirect github.com/aliyun/alibaba-cloud-sdk-go v1.63.107 // indirect
github.com/andybalholm/cascadia v1.3.3 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect github.com/aws/aws-sdk-go-v2 v1.36.4 // indirect
github.com/aws/aws-sdk-go-v2/config v1.29.15 // indirect github.com/aws/aws-sdk-go-v2/config v1.29.16 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.68 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.69 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.16 // indirect
github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.2 // indirect github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.3 // indirect
github.com/aws/aws-sdk-go-v2/service/route53 v1.52.0 // indirect github.com/aws/aws-sdk-go-v2/service/route53 v1.52.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.25.4 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.20 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.33.21 // indirect
github.com/aws/smithy-go v1.22.3 // indirect github.com/aws/smithy-go v1.22.3 // indirect
github.com/baidubce/bce-sdk-go v0.9.230 // indirect github.com/baidubce/bce-sdk-go v0.9.230 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect github.com/benbjohnson/clock v1.3.5 // indirect
github.com/boombuler/barcode v1.0.2 // indirect github.com/boombuler/barcode v1.0.2 // indirect
github.com/buger/goterm v1.0.4 // indirect github.com/buger/goterm v1.0.4 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/civo/civogo v0.5.4 // indirect github.com/civo/civogo v0.6.1 // indirect
github.com/cloudflare/cloudflare-go v0.115.0 // indirect github.com/cloudflare/cloudflare-go v0.115.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/diskfs/go-diskfs v1.6.0 // indirect github.com/diskfs/go-diskfs v1.6.0 // indirect
@ -117,7 +117,7 @@ require (
github.com/gofrs/flock v0.12.1 // indirect github.com/gofrs/flock v0.12.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-querystring v1.1.0 // indirect
github.com/google/pprof v0.0.0-20250602020802-c6617b811d0e // indirect github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect
github.com/google/s2a-go v0.1.9 // indirect github.com/google/s2a-go v0.1.9 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
@ -127,7 +127,7 @@ require (
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.153 // indirect github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.154 // indirect
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
github.com/infobloxopen/infoblox-go-client/v2 v2.10.0 // indirect github.com/infobloxopen/infoblox-go-client/v2 v2.10.0 // indirect
github.com/jinzhu/copier v0.4.0 // indirect github.com/jinzhu/copier v0.4.0 // indirect
@ -169,7 +169,7 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/oracle/oci-go-sdk/v65 v65.93.0 // indirect github.com/oracle/oci-go-sdk/v65 v65.93.1 // indirect
github.com/ovh/go-ovh v1.7.0 // indirect github.com/ovh/go-ovh v1.7.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect
@ -187,7 +187,7 @@ require (
github.com/sacloud/iaas-api-go v1.16.0 // indirect github.com/sacloud/iaas-api-go v1.16.0 // indirect
github.com/sacloud/packages-go v0.0.11 // indirect github.com/sacloud/packages-go v0.0.11 // indirect
github.com/sagikazarmark/locafero v0.9.0 // indirect github.com/sagikazarmark/locafero v0.9.0 // indirect
github.com/samber/lo v1.50.0 // indirect github.com/samber/lo v1.51.0 // indirect
github.com/samber/slog-common v0.18.1 // indirect github.com/samber/slog-common v0.18.1 // indirect
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33 // indirect github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33 // indirect
github.com/selectel/domains-go v1.1.0 // indirect github.com/selectel/domains-go v1.1.0 // indirect
@ -203,8 +203,8 @@ require (
github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/pflag v1.0.6 // indirect
github.com/spf13/viper v1.20.1 // indirect github.com/spf13/viper v1.20.1 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1181 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1186 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1174 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1184 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/tklauser/go-sysconf v0.3.15 // indirect github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect github.com/tklauser/numcpus v0.10.0 // indirect
@ -222,7 +222,7 @@ require (
go.opentelemetry.io/otel v1.36.0 // indirect go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect go.opentelemetry.io/otel/trace v1.36.0 // indirect
go.uber.org/atomic v1.11.0 // indirect go.uber.org/atomic v1.11.0
go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect
go.uber.org/mock v0.5.2 // indirect go.uber.org/mock v0.5.2 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
@ -231,7 +231,7 @@ require (
golang.org/x/sys v0.33.0 // indirect golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.26.0 // indirect golang.org/x/text v0.26.0 // indirect
golang.org/x/tools v0.34.0 // indirect golang.org/x/tools v0.34.0 // indirect
google.golang.org/api v0.236.0 // indirect google.golang.org/api v0.237.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/grpc v1.73.0 // indirect google.golang.org/grpc v1.73.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect google.golang.org/protobuf v1.36.6 // indirect

86
go.sum
View file

@ -606,8 +606,8 @@ github.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesn
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 h1:j8BorDEigD8UFOSZQiSqAMOOleyQOOQPnUAwV+Ls1gA= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
@ -678,35 +678,35 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= github.com/aws/aws-sdk-go-v2 v1.36.4 h1:GySzjhVvx0ERP6eyfAbAuAXLtAda5TEy19E5q5W8I9E=
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= github.com/aws/aws-sdk-go-v2 v1.36.4/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
github.com/aws/aws-sdk-go-v2/config v1.29.15 h1:I5XjesVMpDZXZEZonVfjI12VNMrYa38LtLnw4NtY5Ss= github.com/aws/aws-sdk-go-v2/config v1.29.16 h1:XkruGnXX1nEZ+Nyo9v84TzsX+nj86icbFAeust6uo8A=
github.com/aws/aws-sdk-go-v2/config v1.29.15/go.mod h1:tNIp4JIPonlsgaO5hxO372a6gjhN63aSWl2GVl5QoBQ= github.com/aws/aws-sdk-go-v2/config v1.29.16/go.mod h1:uCW7PNjGwZ5cOGZ5jr8vCWrYkGIhPoTNV23Q/tpHKzg=
github.com/aws/aws-sdk-go-v2/credentials v1.17.68 h1:cFb9yjI02/sWHBSYXAtkamjzCuRymvmeFmt0TC0MbYY= github.com/aws/aws-sdk-go-v2/credentials v1.17.69 h1:8B8ZQboRc3uaIKjshve/XlvJ570R7BKNy3gftSbS178=
github.com/aws/aws-sdk-go-v2/credentials v1.17.68/go.mod h1:H6E+jBzyqUu8u0vGaU6POkK3P0NylYEeRZ6ynBpMqIk= github.com/aws/aws-sdk-go-v2/credentials v1.17.69/go.mod h1:gPME6I8grR1jCqBFEGthULiolzf/Sexq/Wy42ibKK9c=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31 h1:oQWSGexYasNpYp4epLGZxxjsDo8BMBh6iNWkTXQvkwk=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31/go.mod h1:nc332eGUU+djP3vrMI6blS0woaCfHTe3KiSQUVTMRq0=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35 h1:o1v1VFfPcDVlK3ll1L5xHsaQAFdNtZ5GXnNR7SwueC4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35/go.mod h1:rZUQNYMNG+8uZxz9FOerQJ+FceCiodXvixpeRtdESrU=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35 h1:R5b82ubO2NntENm3SAm0ADME+H630HomNJdgv+yZ3xw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35/go.mod h1:FuA+nmgMRfkzVKYDNEqQadvEMxtxl9+RLT9ribCwEMs=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.16 h1:/ldKrPPXTC421bTNWrUIpq3CxwHwRI/kpc+jPUTJocM=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.16/go.mod h1:5vkf/Ws0/wgIMJDQbjI4p2op86hNW6Hie5QtebrDgT8=
github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.2 h1:Bz0MltpmIFP2EBYADc17VHdXYxZw9JPQl8Ksq+w6aEE= github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.3 h1:8NcRiAZn54D1W6rQZaZfZtjnLH07WciOhDcYIKpVKQM=
github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.2/go.mod h1:Qy22QnQSdHbZwMZrarsWZBIuK51isPlkD+Z4sztxX0o= github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.3/go.mod h1:nramkVwrN/StFgVfTZp6Azi1J4lWnq+99fwpJRTasj4=
github.com/aws/aws-sdk-go-v2/service/route53 v1.52.0 h1:OVj58l/k7bfrRjSbP4lbrCHAO7/NS2IbUjnHuJpmqho= github.com/aws/aws-sdk-go-v2/service/route53 v1.52.1 h1:PAbznrQ8b8IwTUJgBdcbVqc+r57SO3jy0YJi9bJKPmQ=
github.com/aws/aws-sdk-go-v2/service/route53 v1.52.0/go.mod h1:kGYOjvTa0Vw0qxrqrOLut1vMnui6qLxqv/SX3vYeM8Y= github.com/aws/aws-sdk-go-v2/service/route53 v1.52.1/go.mod h1:cpFFGJ0A6WKZjf26TVzYI3qFhbFXXb7xeF5bOOMax6c=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8= github.com/aws/aws-sdk-go-v2/service/sso v1.25.4 h1:EU58LP8ozQDVroOEyAfcq0cGc5R/FTZjVoYJ6tvby3w=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= github.com/aws/aws-sdk-go-v2/service/sso v1.25.4/go.mod h1:CrtOgCcysxMvrCoHnvNAD7PHWclmoFG78Q2xLK0KKcs=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.2 h1:XB4z0hbQtpmBnb1FQYvKaCM7UsS6Y/u8jVBwIUGeCTk=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.2/go.mod h1:hwRpqkRxnQ58J9blRDrB4IanlXCpcKmsC83EhG77upg=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.20 h1:oIaQ1e17CSKaWmUTu62MtraRWVIosn/iONMuZt0gbqc= github.com/aws/aws-sdk-go-v2/service/sts v1.33.21 h1:nyLjs8sYJShFYj6aiyjCBI3EcLn1udWrQTjEF+SOXB0=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.20/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= github.com/aws/aws-sdk-go-v2/service/sts v1.33.21/go.mod h1:EhdxtZ+g84MSGrSrHzZiUm9PYiZkrADNja15wtRJSJo=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k= github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
@ -751,8 +751,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/civo/civogo v0.5.4 h1:atR+zfqEEp9qvLa/DRr8eYWYPySi5Mh9uaMNbZ6b4fE= github.com/civo/civogo v0.6.1 h1:PFOh7rBU0vmj7LTDIv3z7l9uXG4SZyyzScCl3wyTFSc=
github.com/civo/civogo v0.5.4/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc= github.com/civo/civogo v0.6.1/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc=
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM= github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM=
@ -1039,8 +1039,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20250602020802-c6617b811d0e h1:FJta/0WsADCe1r9vQjdHbd3KuiLPu7Y9WlyLGwMUNyE= github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18=
github.com/google/pprof v0.0.0-20250602020802-c6617b811d0e/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
@ -1154,8 +1154,8 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.153 h1:EvwDKUhasJ7ePPCBIEAe5U6aqXKiC3XMAJ8J2IHpvsc= github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.154 h1:vqrLDi/UZpBEliQXLfdavmtETgGLj82iU8Tja0URzqI=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.153/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY= github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.154/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY=
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@ -1402,8 +1402,8 @@ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYr
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A= github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU= github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE=
github.com/oracle/oci-go-sdk/v65 v65.93.0 h1:L6cfEXHZYW9WXD+q0g+HPvLS5TkZjpn3b0RlkLWOLpM= github.com/oracle/oci-go-sdk/v65 v65.93.1 h1:lIvy/6aQOUenQI+cxXH1wDBJeXFPO9Du3CaomXeYFaY=
github.com/oracle/oci-go-sdk/v65 v65.93.0/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA= github.com/oracle/oci-go-sdk/v65 v65.93.1/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA=
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE= github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8= github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
github.com/ovh/go-ovh v1.7.0 h1:V14nF7FwDjQrZt9g7jzcvAAQ3HN6DNShRFRMC3jLoPw= github.com/ovh/go-ovh v1.7.0 h1:V14nF7FwDjQrZt9g7jzcvAAQ3HN6DNShRFRMC3jLoPw=
@ -1524,8 +1524,8 @@ github.com/sacloud/packages-go v0.0.11/go.mod h1:XNF5MCTWcHo9NiqWnYctVbASSSZR3ZO
github.com/sagikazarmark/crypt v0.10.0/go.mod h1:gwTNHQVoOS3xp9Xvz5LLR+1AauC5M6880z5NWzdhOyQ= github.com/sagikazarmark/crypt v0.10.0/go.mod h1:gwTNHQVoOS3xp9Xvz5LLR+1AauC5M6880z5NWzdhOyQ=
github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k= github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k=
github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk= github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
github.com/samber/lo v1.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY= github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI=
github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc= github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
github.com/samber/slog-common v0.18.1 h1:c0EipD/nVY9HG5shgm/XAs67mgpWDMF+MmtptdJNCkQ= github.com/samber/slog-common v0.18.1 h1:c0EipD/nVY9HG5shgm/XAs67mgpWDMF+MmtptdJNCkQ=
github.com/samber/slog-common v0.18.1/go.mod h1:QNZiNGKakvrfbJ2YglQXLCZauzkI9xZBjOhWFKS3IKk= github.com/samber/slog-common v0.18.1/go.mod h1:QNZiNGKakvrfbJ2YglQXLCZauzkI9xZBjOhWFKS3IKk=
github.com/samber/slog-zerolog/v2 v2.7.3 h1:/MkPDl/tJhijN2GvB1MWwBn2FU8RiL3rQ8gpXkQm2EY= github.com/samber/slog-zerolog/v2 v2.7.3 h1:/MkPDl/tJhijN2GvB1MWwBn2FU8RiL3rQ8gpXkQm2EY=
@ -1619,11 +1619,11 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1174/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1184/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1181 h1:zbezQIOlxe2ri7gsQ9GdNsY/xwmOoG8ZdiKr0BTmErI= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1186 h1:LlFJcouP8DMN4tLWfF9oYG+x4tzd40ISMMZ8MaGNH80=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1181/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1186/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1174 h1:LRmjetOxA7ZjHIEiYKoZmVOntxpzscgrse50PDEUs/s= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1184 h1:nZ6YVgr1X332hEI8bQEyW4Hhp9FFOYEZ6Q8fSxND2pQ=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1174/go.mod h1:H0oIwVla6DcgpKnEvFcFEphENZng64Mqkq2p4alU4tE= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1184/go.mod h1:nigwfh7wyX/yj24MdEEq++yvsPp1XkSczazhb0wzYts=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
@ -2298,8 +2298,8 @@ google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60c
google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0=
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms=
google.golang.org/api v0.236.0 h1:CAiEiDVtO4D/Qja2IA9VzlFrgPnK3XVMmRoJZlSWbc0= google.golang.org/api v0.237.0 h1:MP7XVsGZesOsx3Q8WVa4sUdbrsTvDSOERd3Vh4xj/wc=
google.golang.org/api v0.236.0/go.mod h1:X1WF9CU2oTc+Jml1tiIxGmWFK/UZezdqEu09gcxZAj4= google.golang.org/api v0.237.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=

View file

@ -7,6 +7,7 @@ import (
"time" "time"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/yusing/go-proxy/agent/pkg/agent"
config "github.com/yusing/go-proxy/internal/config/types" config "github.com/yusing/go-proxy/internal/config/types"
"github.com/yusing/go-proxy/internal/docker" "github.com/yusing/go-proxy/internal/docker"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
@ -43,7 +44,7 @@ func getDockerClients() (DockerClients, gperr.Error) {
dockerClients[name] = dockerClient dockerClients[name] = dockerClient
} }
for _, agent := range cfg.ListAgents() { for _, agent := range agent.ListAgents() {
dockerClient, err := docker.NewClient(agent.FakeDockerHost()) dockerClient, err := docker.NewClient(agent.FakeDockerHost())
if err != nil { if err != nil {
connErrs.Add(err) connErrs.Add(err)
@ -65,7 +66,7 @@ func getDockerClient(server string) (*docker.SharedClient, bool, error) {
} }
} }
if host == "" { if host == "" {
for _, agent := range cfg.ListAgents() { for _, agent := range agent.ListAgents() {
if agent.Name() == server { if agent.Name() == server {
host = agent.FakeDockerHost() host = agent.FakeDockerHost()
break break

View file

@ -5,18 +5,18 @@ import (
"time" "time"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
config "github.com/yusing/go-proxy/internal/config/types" "github.com/yusing/go-proxy/agent/pkg/agent"
"github.com/yusing/go-proxy/internal/net/gphttp" "github.com/yusing/go-proxy/internal/net/gphttp"
"github.com/yusing/go-proxy/internal/net/gphttp/gpwebsocket" "github.com/yusing/go-proxy/internal/net/gphttp/gpwebsocket"
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders" "github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
) )
func ListAgents(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) { func ListAgents(w http.ResponseWriter, r *http.Request) {
if httpheaders.IsWebsocket(r.Header) { if httpheaders.IsWebsocket(r.Header) {
gpwebsocket.Periodic(w, r, 10*time.Second, func(conn *websocket.Conn) error { gpwebsocket.Periodic(w, r, 10*time.Second, func(conn *websocket.Conn) error {
return conn.WriteJSON(cfg.ListAgents()) return conn.WriteJSON(agent.ListAgents())
}) })
} else { } else {
gphttp.RespondJSON(w, r, cfg.ListAgents()) gphttp.RespondJSON(w, r, agent.ListAgents())
} }
} }

View file

@ -39,7 +39,7 @@ func NewAgent(w http.ResponseWriter, r *http.Request) {
return return
} }
hostport := fmt.Sprintf("%s:%d", host, port) hostport := fmt.Sprintf("%s:%d", host, port)
if _, ok := config.GetInstance().GetAgent(hostport); ok { if _, ok := agent.GetAgent(hostport); ok {
gphttp.KeyAlreadyExists(w, "agent", hostport) gphttp.KeyAlreadyExists(w, "agent", hostport)
return return
} }

View file

@ -4,16 +4,15 @@ import (
"net/http" "net/http"
agentPkg "github.com/yusing/go-proxy/agent/pkg/agent" agentPkg "github.com/yusing/go-proxy/agent/pkg/agent"
config "github.com/yusing/go-proxy/internal/config/types"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/metrics/systeminfo" "github.com/yusing/go-proxy/internal/metrics/systeminfo"
"github.com/yusing/go-proxy/internal/net/gphttp" "github.com/yusing/go-proxy/internal/net/gphttp"
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders" "github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy" "github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
) )
func SystemInfo(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) { func SystemInfo(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query() query := r.URL.Query()
agentAddr := query.Get("agent_addr") agentAddr := query.Get("agent_addr")
query.Del("agent_addr") query.Del("agent_addr")
@ -22,7 +21,7 @@ func SystemInfo(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Reques
return return
} }
agent, ok := cfg.GetAgent(agentAddr) agent, ok := agentPkg.GetAgent(agentAddr)
if !ok { if !ok {
gphttp.NotFound(w, "agent_addr") gphttp.NotFound(w, "agent_addr")
return return
@ -41,7 +40,7 @@ func SystemInfo(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Reques
} }
gphttp.WriteBody(w, respData) gphttp.WriteBody(w, respData)
} else { } else {
rp := reverseproxy.NewReverseProxy("agent", types.NewURL(agentPkg.AgentURL), agent.Transport()) rp := reverseproxy.NewReverseProxy("agent", nettypes.NewURL(agentPkg.AgentURL), agent.Transport())
header := r.Header.Clone() header := r.Header.Clone()
r, err := http.NewRequestWithContext(r.Context(), r.Method, agentPkg.EndpointSystemInfo+"?"+query.Encode(), nil) r, err := http.NewRequestWithContext(r.Context(), r.Method, agentPkg.EndpointSystemInfo+"?"+query.Encode(), nil)
if err != nil { if err != nil {

View file

@ -6,31 +6,8 @@ import (
"github.com/yusing/go-proxy/agent/pkg/agent" "github.com/yusing/go-proxy/agent/pkg/agent"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/route/provider" "github.com/yusing/go-proxy/internal/route/provider"
"github.com/yusing/go-proxy/internal/utils/functional"
) )
var agentPool = functional.NewMapOf[string, *agent.AgentConfig]()
func addAgent(agent *agent.AgentConfig) {
agentPool.Store(agent.Addr, agent)
}
func removeAllAgents() {
agentPool.Clear()
}
func GetAgent(addr string) (agent *agent.AgentConfig, ok bool) {
agent, ok = agentPool.Load(addr)
return
}
func (cfg *Config) GetAgent(agentAddrOrDockerHost string) (*agent.AgentConfig, bool) {
if !agent.IsDockerHostAgent(agentAddrOrDockerHost) {
return GetAgent(agentAddrOrDockerHost)
}
return GetAgent(agent.GetAgentAddrFromDockerHost(agentAddrOrDockerHost))
}
func (cfg *Config) VerifyNewAgent(host string, ca agent.PEMPair, client agent.PEMPair) (int, gperr.Error) { func (cfg *Config) VerifyNewAgent(host string, ca agent.PEMPair, client agent.PEMPair) (int, gperr.Error) {
if slices.ContainsFunc(cfg.value.Providers.Agents, func(a *agent.AgentConfig) bool { if slices.ContainsFunc(cfg.value.Providers.Agents, func(a *agent.AgentConfig) bool {
return a.Addr == host return a.Addr == host
@ -44,23 +21,17 @@ func (cfg *Config) VerifyNewAgent(host string, ca agent.PEMPair, client agent.PE
if err != nil { if err != nil {
return 0, gperr.Wrap(err, "failed to start agent") return 0, gperr.Wrap(err, "failed to start agent")
} }
addAgent(&agentCfg) agent.AddAgent(&agentCfg)
provider := provider.NewAgentProvider(&agentCfg) provider := provider.NewAgentProvider(&agentCfg)
if err := cfg.errIfExists(provider); err != nil { if err := cfg.errIfExists(provider); err != nil {
agent.RemoveAgent(&agentCfg)
return 0, err return 0, err
} }
err = provider.LoadRoutes() err = provider.LoadRoutes()
if err != nil { if err != nil {
agent.RemoveAgent(&agentCfg)
return 0, gperr.Wrap(err, "failed to load routes") return 0, gperr.Wrap(err, "failed to load routes")
} }
return provider.NumRoutes(), nil return provider.NumRoutes(), nil
} }
func (cfg *Config) ListAgents() []*agent.AgentConfig {
agents := make([]*agent.AgentConfig, 0, agentPool.Size())
agentPool.RangeAll(func(key string, value *agent.AgentConfig) {
agents = append(agents, value)
})
return agents
}

View file

@ -11,6 +11,7 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
agentPkg "github.com/yusing/go-proxy/agent/pkg/agent"
"github.com/yusing/go-proxy/internal/api" "github.com/yusing/go-proxy/internal/api"
autocert "github.com/yusing/go-proxy/internal/autocert" autocert "github.com/yusing/go-proxy/internal/autocert"
"github.com/yusing/go-proxy/internal/common" "github.com/yusing/go-proxy/internal/common"
@ -323,14 +324,14 @@ func (cfg *Config) loadRouteProviders(providers *config.Providers) gperr.Error {
errs := gperr.NewBuilder("route provider errors") errs := gperr.NewBuilder("route provider errors")
results := gperr.NewBuilder("loaded route providers") results := gperr.NewBuilder("loaded route providers")
removeAllAgents() agentPkg.RemoveAllAgents()
for _, agent := range providers.Agents { for _, agent := range providers.Agents {
if err := agent.Start(cfg.task.Context()); err != nil { if err := agent.Start(cfg.task.Context()); err != nil {
errs.Add(gperr.PrependSubject(agent.String(), err)) errs.Add(gperr.PrependSubject(agent.String(), err))
continue continue
} }
addAgent(agent) agentPkg.AddAgent(agent)
p := proxy.NewAgentProvider(agent) p := proxy.NewAgentProvider(agent)
if err := cfg.errIfExists(p); err != nil { if err := cfg.errIfExists(p); err != nil {
errs.Add(err.Subject(p.String())) errs.Add(err.Subject(p.String()))

View file

@ -52,9 +52,7 @@ type (
Statistics() map[string]any Statistics() map[string]any
RouteProviderList() []RouteProviderListResponse RouteProviderList() []RouteProviderListResponse
Context() context.Context Context() context.Context
GetAgent(agentAddrOrDockerHost string) (*agent.AgentConfig, bool)
VerifyNewAgent(host string, ca agent.PEMPair, client agent.PEMPair) (int, gperr.Error) VerifyNewAgent(host string, ca agent.PEMPair, client agent.PEMPair) (int, gperr.Error)
ListAgents() []*agent.AgentConfig
AutoCertProvider() *autocert.Provider AutoCertProvider() *autocert.Provider
} }
) )

View file

@ -8,7 +8,7 @@ replace github.com/yusing/go-proxy/internal/utils => ../utils
require ( require (
github.com/go-acme/lego/v4 v4.23.1 github.com/go-acme/lego/v4 v4.23.1
github.com/yusing/go-proxy v0.14.1 github.com/yusing/go-proxy v0.14.2
) )
require ( require (
@ -17,7 +17,7 @@ require (
cloud.google.com/go/compute/metadata v0.7.0 // indirect cloud.google.com/go/compute/metadata v0.7.0 // indirect
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect
@ -26,26 +26,26 @@ require (
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.63.107 // indirect github.com/aliyun/alibaba-cloud-sdk-go v1.63.107 // indirect
github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect github.com/aws/aws-sdk-go-v2 v1.36.4 // indirect
github.com/aws/aws-sdk-go-v2/config v1.29.15 // indirect github.com/aws/aws-sdk-go-v2/config v1.29.16 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.68 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.69 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.16 // indirect
github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.2 // indirect github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.3 // indirect
github.com/aws/aws-sdk-go-v2/service/route53 v1.52.0 // indirect github.com/aws/aws-sdk-go-v2/service/route53 v1.52.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.25.4 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.20 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.33.21 // indirect
github.com/aws/smithy-go v1.22.3 // indirect github.com/aws/smithy-go v1.22.3 // indirect
github.com/baidubce/bce-sdk-go v0.9.230 // indirect github.com/baidubce/bce-sdk-go v0.9.230 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect github.com/benbjohnson/clock v1.3.5 // indirect
github.com/boombuler/barcode v1.0.2 // indirect github.com/boombuler/barcode v1.0.2 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/civo/civogo v0.5.4 // indirect github.com/civo/civogo v0.6.1 // indirect
github.com/cloudflare/cloudflare-go v0.115.0 // indirect github.com/cloudflare/cloudflare-go v0.115.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dnsimple/dnsimple-go v1.7.0 // indirect github.com/dnsimple/dnsimple-go v1.7.0 // indirect
@ -80,7 +80,7 @@ require (
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.153 // indirect github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.154 // indirect
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
github.com/infobloxopen/infoblox-go-client/v2 v2.10.0 // indirect github.com/infobloxopen/infoblox-go-client/v2 v2.10.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect
@ -115,7 +115,7 @@ require (
github.com/nrdcg/porkbun v0.4.0 // indirect github.com/nrdcg/porkbun v0.4.0 // indirect
github.com/nzdjb/go-metaname v1.0.0 // indirect github.com/nzdjb/go-metaname v1.0.0 // indirect
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/oracle/oci-go-sdk/v65 v65.93.0 // indirect github.com/oracle/oci-go-sdk/v65 v65.93.1 // indirect
github.com/ovh/go-ovh v1.7.0 // indirect github.com/ovh/go-ovh v1.7.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect
@ -148,8 +148,8 @@ require (
github.com/spf13/viper v1.20.1 // indirect github.com/spf13/viper v1.20.1 // indirect
github.com/stretchr/testify v1.10.0 // indirect github.com/stretchr/testify v1.10.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1181 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1186 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1174 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1184 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/transip/gotransip/v6 v6.26.0 // indirect github.com/transip/gotransip/v6 v6.26.0 // indirect
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // indirect github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // indirect
@ -177,7 +177,7 @@ require (
golang.org/x/text v0.26.0 // indirect golang.org/x/text v0.26.0 // indirect
golang.org/x/time v0.12.0 // indirect golang.org/x/time v0.12.0 // indirect
golang.org/x/tools v0.34.0 // indirect golang.org/x/tools v0.34.0 // indirect
google.golang.org/api v0.236.0 // indirect google.golang.org/api v0.237.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/grpc v1.73.0 // indirect google.golang.org/grpc v1.73.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect google.golang.org/protobuf v1.36.6 // indirect

View file

@ -606,8 +606,8 @@ github.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesn
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 h1:j8BorDEigD8UFOSZQiSqAMOOleyQOOQPnUAwV+Ls1gA= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
@ -670,35 +670,35 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= github.com/aws/aws-sdk-go-v2 v1.36.4 h1:GySzjhVvx0ERP6eyfAbAuAXLtAda5TEy19E5q5W8I9E=
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= github.com/aws/aws-sdk-go-v2 v1.36.4/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
github.com/aws/aws-sdk-go-v2/config v1.29.15 h1:I5XjesVMpDZXZEZonVfjI12VNMrYa38LtLnw4NtY5Ss= github.com/aws/aws-sdk-go-v2/config v1.29.16 h1:XkruGnXX1nEZ+Nyo9v84TzsX+nj86icbFAeust6uo8A=
github.com/aws/aws-sdk-go-v2/config v1.29.15/go.mod h1:tNIp4JIPonlsgaO5hxO372a6gjhN63aSWl2GVl5QoBQ= github.com/aws/aws-sdk-go-v2/config v1.29.16/go.mod h1:uCW7PNjGwZ5cOGZ5jr8vCWrYkGIhPoTNV23Q/tpHKzg=
github.com/aws/aws-sdk-go-v2/credentials v1.17.68 h1:cFb9yjI02/sWHBSYXAtkamjzCuRymvmeFmt0TC0MbYY= github.com/aws/aws-sdk-go-v2/credentials v1.17.69 h1:8B8ZQboRc3uaIKjshve/XlvJ570R7BKNy3gftSbS178=
github.com/aws/aws-sdk-go-v2/credentials v1.17.68/go.mod h1:H6E+jBzyqUu8u0vGaU6POkK3P0NylYEeRZ6ynBpMqIk= github.com/aws/aws-sdk-go-v2/credentials v1.17.69/go.mod h1:gPME6I8grR1jCqBFEGthULiolzf/Sexq/Wy42ibKK9c=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31 h1:oQWSGexYasNpYp4epLGZxxjsDo8BMBh6iNWkTXQvkwk=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31/go.mod h1:nc332eGUU+djP3vrMI6blS0woaCfHTe3KiSQUVTMRq0=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35 h1:o1v1VFfPcDVlK3ll1L5xHsaQAFdNtZ5GXnNR7SwueC4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35/go.mod h1:rZUQNYMNG+8uZxz9FOerQJ+FceCiodXvixpeRtdESrU=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35 h1:R5b82ubO2NntENm3SAm0ADME+H630HomNJdgv+yZ3xw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35/go.mod h1:FuA+nmgMRfkzVKYDNEqQadvEMxtxl9+RLT9ribCwEMs=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.16 h1:/ldKrPPXTC421bTNWrUIpq3CxwHwRI/kpc+jPUTJocM=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.16/go.mod h1:5vkf/Ws0/wgIMJDQbjI4p2op86hNW6Hie5QtebrDgT8=
github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.2 h1:Bz0MltpmIFP2EBYADc17VHdXYxZw9JPQl8Ksq+w6aEE= github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.3 h1:8NcRiAZn54D1W6rQZaZfZtjnLH07WciOhDcYIKpVKQM=
github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.2/go.mod h1:Qy22QnQSdHbZwMZrarsWZBIuK51isPlkD+Z4sztxX0o= github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.3/go.mod h1:nramkVwrN/StFgVfTZp6Azi1J4lWnq+99fwpJRTasj4=
github.com/aws/aws-sdk-go-v2/service/route53 v1.52.0 h1:OVj58l/k7bfrRjSbP4lbrCHAO7/NS2IbUjnHuJpmqho= github.com/aws/aws-sdk-go-v2/service/route53 v1.52.1 h1:PAbznrQ8b8IwTUJgBdcbVqc+r57SO3jy0YJi9bJKPmQ=
github.com/aws/aws-sdk-go-v2/service/route53 v1.52.0/go.mod h1:kGYOjvTa0Vw0qxrqrOLut1vMnui6qLxqv/SX3vYeM8Y= github.com/aws/aws-sdk-go-v2/service/route53 v1.52.1/go.mod h1:cpFFGJ0A6WKZjf26TVzYI3qFhbFXXb7xeF5bOOMax6c=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8= github.com/aws/aws-sdk-go-v2/service/sso v1.25.4 h1:EU58LP8ozQDVroOEyAfcq0cGc5R/FTZjVoYJ6tvby3w=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= github.com/aws/aws-sdk-go-v2/service/sso v1.25.4/go.mod h1:CrtOgCcysxMvrCoHnvNAD7PHWclmoFG78Q2xLK0KKcs=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.2 h1:XB4z0hbQtpmBnb1FQYvKaCM7UsS6Y/u8jVBwIUGeCTk=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.2/go.mod h1:hwRpqkRxnQ58J9blRDrB4IanlXCpcKmsC83EhG77upg=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.20 h1:oIaQ1e17CSKaWmUTu62MtraRWVIosn/iONMuZt0gbqc= github.com/aws/aws-sdk-go-v2/service/sts v1.33.21 h1:nyLjs8sYJShFYj6aiyjCBI3EcLn1udWrQTjEF+SOXB0=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.20/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= github.com/aws/aws-sdk-go-v2/service/sts v1.33.21/go.mod h1:EhdxtZ+g84MSGrSrHzZiUm9PYiZkrADNja15wtRJSJo=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k= github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
@ -739,8 +739,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/civo/civogo v0.5.4 h1:atR+zfqEEp9qvLa/DRr8eYWYPySi5Mh9uaMNbZ6b4fE= github.com/civo/civogo v0.6.1 h1:PFOh7rBU0vmj7LTDIv3z7l9uXG4SZyyzScCl3wyTFSc=
github.com/civo/civogo v0.5.4/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc= github.com/civo/civogo v0.6.1/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc=
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM= github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM=
@ -1100,8 +1100,8 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.153 h1:EvwDKUhasJ7ePPCBIEAe5U6aqXKiC3XMAJ8J2IHpvsc= github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.154 h1:vqrLDi/UZpBEliQXLfdavmtETgGLj82iU8Tja0URzqI=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.153/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY= github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.154/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY=
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@ -1322,8 +1322,8 @@ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYr
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A= github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU= github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE=
github.com/oracle/oci-go-sdk/v65 v65.93.0 h1:L6cfEXHZYW9WXD+q0g+HPvLS5TkZjpn3b0RlkLWOLpM= github.com/oracle/oci-go-sdk/v65 v65.93.1 h1:lIvy/6aQOUenQI+cxXH1wDBJeXFPO9Du3CaomXeYFaY=
github.com/oracle/oci-go-sdk/v65 v65.93.0/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA= github.com/oracle/oci-go-sdk/v65 v65.93.1/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA=
github.com/ovh/go-ovh v1.7.0 h1:V14nF7FwDjQrZt9g7jzcvAAQ3HN6DNShRFRMC3jLoPw= github.com/ovh/go-ovh v1.7.0 h1:V14nF7FwDjQrZt9g7jzcvAAQ3HN6DNShRFRMC3jLoPw=
github.com/ovh/go-ovh v1.7.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c= github.com/ovh/go-ovh v1.7.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
@ -1518,11 +1518,11 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1174/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1184/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1181 h1:zbezQIOlxe2ri7gsQ9GdNsY/xwmOoG8ZdiKr0BTmErI= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1186 h1:LlFJcouP8DMN4tLWfF9oYG+x4tzd40ISMMZ8MaGNH80=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1181/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1186/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1174 h1:LRmjetOxA7ZjHIEiYKoZmVOntxpzscgrse50PDEUs/s= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1184 h1:nZ6YVgr1X332hEI8bQEyW4Hhp9FFOYEZ6Q8fSxND2pQ=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1174/go.mod h1:H0oIwVla6DcgpKnEvFcFEphENZng64Mqkq2p4alU4tE= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1184/go.mod h1:nigwfh7wyX/yj24MdEEq++yvsPp1XkSczazhb0wzYts=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@ -2155,8 +2155,8 @@ google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60c
google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0=
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms=
google.golang.org/api v0.236.0 h1:CAiEiDVtO4D/Qja2IA9VzlFrgPnK3XVMmRoJZlSWbc0= google.golang.org/api v0.237.0 h1:MP7XVsGZesOsx3Q8WVa4sUdbrsTvDSOERd3Vh4xj/wc=
google.golang.org/api v0.236.0/go.mod h1:X1WF9CU2oTc+Jml1tiIxGmWFK/UZezdqEu09gcxZAj4= google.golang.org/api v0.237.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=

View file

@ -16,7 +16,6 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/yusing/go-proxy/agent/pkg/agent" "github.com/yusing/go-proxy/agent/pkg/agent"
"github.com/yusing/go-proxy/internal/common" "github.com/yusing/go-proxy/internal/common"
config "github.com/yusing/go-proxy/internal/config/types"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
) )
@ -122,7 +121,7 @@ func NewClient(host string) (*SharedClient, error) {
var dial func(ctx context.Context) (net.Conn, error) var dial func(ctx context.Context) (net.Conn, error)
if agent.IsDockerHostAgent(host) { if agent.IsDockerHostAgent(host) {
cfg, ok := config.GetInstance().GetAgent(host) cfg, ok := agent.GetAgent(host)
if !ok { if !ok {
panic(fmt.Errorf("agent %q not found", host)) panic(fmt.Errorf("agent %q not found", host))
} }

View file

@ -2,6 +2,8 @@ package docker
import ( import (
"context" "context"
"errors"
"fmt"
"net" "net"
"net/url" "net/url"
"strconv" "strconv"
@ -9,9 +11,7 @@ import (
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
"github.com/rs/zerolog/log"
"github.com/yusing/go-proxy/agent/pkg/agent" "github.com/yusing/go-proxy/agent/pkg/agent"
config "github.com/yusing/go-proxy/internal/config/types"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types" idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
"github.com/yusing/go-proxy/internal/serialization" "github.com/yusing/go-proxy/internal/serialization"
@ -35,6 +35,7 @@ type (
Mounts []string `json:"mounts"` Mounts []string `json:"mounts"`
Network string `json:"network,omitempty"`
PublicPortMapping PortMapping `json:"public_ports"` // non-zero publicPort:types.Port PublicPortMapping PortMapping `json:"public_ports"` // non-zero publicPort:types.Port
PrivatePortMapping PortMapping `json:"private_ports"` // privatePort:types.Port PrivatePortMapping PortMapping `json:"private_ports"` // privatePort:types.Port
PublicHostname string `json:"public_hostname"` PublicHostname string `json:"public_hostname"`
@ -45,6 +46,8 @@ type (
IsExplicit bool `json:"is_explicit"` IsExplicit bool `json:"is_explicit"`
IsHostNetworkMode bool `json:"is_host_network_mode"` IsHostNetworkMode bool `json:"is_host_network_mode"`
Running bool `json:"running"` Running bool `json:"running"`
Errors *containerError `json:"errors"`
} }
ContainerImage struct { ContainerImage struct {
Author string `json:"author,omitempty"` Author string `json:"author,omitempty"`
@ -55,6 +58,11 @@ type (
var DummyContainer = new(Container) var DummyContainer = new(Container)
var (
ErrNetworkNotFound = errors.New("network not found")
ErrNoNetwork = errors.New("no network found")
)
func FromDocker(c *container.SummaryTrimmed, dockerHost string) (res *Container) { func FromDocker(c *container.SummaryTrimmed, dockerHost string) (res *Container) {
_, isExplicit := c.Labels[LabelAliases] _, isExplicit := c.Labels[LabelAliases]
helper := containerHelper{c} helper := containerHelper{c}
@ -67,6 +75,7 @@ func FromDocker(c *container.SummaryTrimmed, dockerHost string) (res *Container)
} }
} }
} }
network := helper.getDeleteLabel(LabelNetwork)
isExcluded, _ := strconv.ParseBool(helper.getDeleteLabel(LabelExclude)) isExcluded, _ := strconv.ParseBool(helper.getDeleteLabel(LabelExclude))
res = &Container{ res = &Container{
@ -79,6 +88,7 @@ func FromDocker(c *container.SummaryTrimmed, dockerHost string) (res *Container)
Mounts: helper.getMounts(), Mounts: helper.getMounts(),
Network: network,
PublicPortMapping: helper.getPublicPortMapping(), PublicPortMapping: helper.getPublicPortMapping(),
PrivatePortMapping: helper.getPrivatePortMapping(), PrivatePortMapping: helper.getPrivatePortMapping(),
@ -91,15 +101,19 @@ func FromDocker(c *container.SummaryTrimmed, dockerHost string) (res *Container)
if agent.IsDockerHostAgent(dockerHost) { if agent.IsDockerHostAgent(dockerHost) {
var ok bool var ok bool
res.Agent, ok = config.GetInstance().GetAgent(dockerHost) res.Agent, ok = agent.GetAgent(dockerHost)
if !ok { if !ok {
log.Error().Msgf("agent %q not found", dockerHost) res.addError(fmt.Errorf("agent %q not found", dockerHost))
} }
} }
res.setPrivateHostname(helper) res.setPrivateHostname(helper)
res.setPublicHostname() res.setPublicHostname()
res.loadDeleteIdlewatcherLabels(helper) res.loadDeleteIdlewatcherLabels(helper)
if res.PrivateHostname == "" && res.PublicHostname == "" && res.Running {
res.addError(ErrNoNetwork)
}
return return
} }
@ -202,7 +216,6 @@ func (c *Container) setPublicHostname() {
} }
url, err := url.Parse(c.DockerHost) url, err := url.Parse(c.DockerHost)
if err != nil { if err != nil {
log.Err(err).Msgf("invalid docker host %q, falling back to 127.0.0.1", c.DockerHost)
c.PublicHostname = "127.0.0.1" c.PublicHostname = "127.0.0.1"
return return
} }
@ -216,8 +229,32 @@ func (c *Container) setPrivateHostname(helper containerHelper) {
if helper.NetworkSettings == nil { if helper.NetworkSettings == nil {
return return
} }
for _, v := range helper.NetworkSettings.Networks { if c.Network != "" {
v, ok := helper.NetworkSettings.Networks[c.Network]
if ok {
c.PrivateHostname = v.IPAddress
return
}
// try {project_name}_{network_name}
if proj := c.DockerComposeProject(); proj != "" {
oldNetwork, newNetwork := c.Network, fmt.Sprintf("%s_%s", proj, c.Network)
if newNetwork != oldNetwork {
v, ok = helper.NetworkSettings.Networks[newNetwork]
if ok {
c.Network = newNetwork // update network to the new one
c.PrivateHostname = v.IPAddress
return
}
}
}
nearest := gperr.DoYouMean(utils.NearestField(c.Network, helper.NetworkSettings.Networks))
c.addError(fmt.Errorf("network %q not found, %w", c.Network, nearest))
return
}
// fallback to first network if no network is specified
for k, v := range helper.NetworkSettings.Networks {
if v.IPAddress != "" { if v.IPAddress != "" {
c.Network = k // update network to the first network
c.PrivateHostname = v.IPAddress c.PrivateHostname = v.IPAddress
return return
} }
@ -250,9 +287,16 @@ func (c *Container) loadDeleteIdlewatcherLabels(helper containerHelper) {
err := serialization.MapUnmarshalValidate(cfg, idwCfg) err := serialization.MapUnmarshalValidate(cfg, idwCfg)
if err != nil { if err != nil {
gperr.LogWarn("invalid idlewatcher config", gperr.PrependSubject(c.ContainerName, err)) c.addError(err)
} else { } else {
c.IdlewatcherConfig = idwCfg c.IdlewatcherConfig = idwCfg
} }
} }
} }
func (c *Container) addError(err error) {
if c.Errors == nil {
c.Errors = new(containerError)
}
c.Errors.Add(err)
}

34
internal/docker/errors.go Normal file
View file

@ -0,0 +1,34 @@
package docker
import (
"encoding/json"
"github.com/yusing/go-proxy/internal/gperr"
)
type containerError struct {
errs *gperr.Builder
}
func (e *containerError) Add(err error) {
if e.errs == nil {
e.errs = gperr.NewBuilder()
}
e.errs.Add(err)
}
func (e *containerError) Error() string {
if e.errs == nil {
return "<niL>"
}
return e.errs.String()
}
func (e *containerError) Unwrap() error {
return e.errs.Error()
}
func (e *containerError) MarshalJSON() ([]byte, error) {
err := e.errs.Error().(interface{ Plain() []byte })
return json.Marshal(string(err.Plain()))
}

View file

@ -1,6 +1,10 @@
package docker package docker
import ( import (
"fmt"
"strings"
"github.com/goccy/go-yaml"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/utils/strutils" "github.com/yusing/go-proxy/internal/utils/strutils"
) )
@ -9,10 +13,12 @@ type LabelMap = map[string]any
var ErrInvalidLabel = gperr.New("invalid label") var ErrInvalidLabel = gperr.New("invalid label")
func ParseLabels(labels map[string]string) (LabelMap, gperr.Error) { func ParseLabels(labels map[string]string, aliases ...string) (LabelMap, gperr.Error) {
nestedMap := make(LabelMap) nestedMap := make(LabelMap)
errs := gperr.NewBuilder("labels error") errs := gperr.NewBuilder("labels error")
ExpandWildcard(labels, aliases...)
for lbl, value := range labels { for lbl, value := range labels {
parts := strutils.SplitRune(lbl, '.') parts := strutils.SplitRune(lbl, '.')
if parts[0] != NSProxy { if parts[0] != NSProxy {
@ -50,3 +56,93 @@ func ParseLabels(labels map[string]string) (LabelMap, gperr.Error) {
return nestedMap, errs.Error() return nestedMap, errs.Error()
} }
func ExpandWildcard(labels map[string]string, aliases ...string) {
// collect all explicit aliases first
aliasSet := make(map[string]struct{}, len(labels))
// wildcardLabels holds mapping suffix -> value derived from wildcard label definitions
wildcardLabels := make(map[string]string)
for _, alias := range aliases {
aliasSet[alias] = struct{}{}
}
// iterate over a copy of the keys to safely mutate the map while ranging
for lbl, value := range labels {
parts := strings.SplitN(lbl, ".", 3)
if len(parts) < 2 || parts[0] != NSProxy {
continue
}
alias := parts[1]
if alias == WildcardAlias { // "*"
// remove wildcard label from original map it should not remain afterwards
delete(labels, lbl)
// value looks like YAML (multiline)
if strings.Count(value, "\n") > 1 {
expandYamlWildcard(value, wildcardLabels)
continue
}
// normal wildcard label with suffix store directly
wildcardLabels[parts[2]] = value
continue
}
// explicit alias label remember the alias
aliasSet[alias] = struct{}{}
}
if len(aliasSet) == 0 || len(wildcardLabels) == 0 {
return // nothing to expand
}
// expand collected wildcard labels for every alias
for suffix, v := range wildcardLabels {
for alias := range aliasSet {
key := fmt.Sprintf("%s.%s.%s", NSProxy, alias, suffix)
if suffix == "" { // this should not happen (root wildcard handled earlier) but keep safe
key = fmt.Sprintf("%s.%s", NSProxy, alias)
}
labels[key] = v
}
}
}
// expandYamlWildcard parses a YAML document in value, flattens it to dot-notated keys and adds the
// results into dest map where each key is the flattened suffix and the value is the scalar string
// representation. The provided YAML is expected to be a mapping.
func expandYamlWildcard(value string, dest map[string]string) {
// replace tab indentation with spaces to make YAML parser happy
yamlStr := strings.ReplaceAll(value, "\t", " ")
raw := make(map[string]any)
if err := yaml.Unmarshal([]byte(yamlStr), &raw); err != nil {
// on parse error, ignore treat as no-op
return
}
flattenMap("", raw, dest)
}
// flattenMap converts nested maps into a flat map with dot-delimited keys.
func flattenMap(prefix string, src map[string]any, dest map[string]string) {
for k, v := range src {
key := k
if prefix != "" {
key = prefix + "." + k
}
switch vv := v.(type) {
case map[string]any:
flattenMap(key, vv, dest)
case map[any]any:
// convert to map[string]any by stringifying keys
tmp := make(map[string]any, len(vv))
for kk, vvv := range vv {
tmp[fmt.Sprintf("%v", kk)] = vvv
}
flattenMap(key, tmp, dest)
default:
dest[key] = fmt.Sprint(v)
}
}
}

View file

@ -3,16 +3,63 @@ package docker_test
import ( import (
"testing" "testing"
"github.com/stretchr/testify/require"
"github.com/yusing/go-proxy/internal/docker" "github.com/yusing/go-proxy/internal/docker"
) )
func TestExpandWildcard(t *testing.T) {
labels := map[string]string{
"proxy.a.host": "localhost",
"proxy.b.port": "4444",
"proxy.b.scheme": "http",
"proxy.*.port": "5555",
"proxy.*.healthcheck.disable": "true",
}
docker.ExpandWildcard(labels)
require.Equal(t, map[string]string{
"proxy.a.host": "localhost",
"proxy.a.port": "5555",
"proxy.a.healthcheck.disable": "true",
"proxy.b.scheme": "http",
"proxy.b.port": "5555",
"proxy.b.healthcheck.disable": "true",
}, labels)
}
func TestExpandWildcardYAML(t *testing.T) {
yaml := `
host: localhost
port: 5555
healthcheck:
disable: true`
labels := map[string]string{
"proxy.*": yaml[1:],
"proxy.a.port": "4444",
"proxy.a.healthcheck.disable": "false",
"proxy.a.healthcheck.path": "/health",
"proxy.b.port": "6666",
}
docker.ExpandWildcard(labels)
require.Equal(t, map[string]string{
"proxy.a.host": "localhost", // set by wildcard
"proxy.a.port": "5555", // overridden by wildcard
"proxy.a.healthcheck.disable": "true", // overridden by wildcard
"proxy.a.healthcheck.path": "/health", // own label
"proxy.b.host": "localhost", // set by wildcard
"proxy.b.port": "5555", // overridden by wildcard
"proxy.b.healthcheck.disable": "true", // overridden by wildcard
}, labels)
}
func BenchmarkParseLabels(b *testing.B) { func BenchmarkParseLabels(b *testing.B) {
for range b.N { for b.Loop() {
_, _ = docker.ParseLabels(map[string]string{ _, _ = docker.ParseLabels(map[string]string{
"proxy.a.host": "localhost", "proxy.a.host": "localhost",
"proxy.a.port": "4444", "proxy.b.port": "4444",
"proxy.a.scheme": "http", "proxy.*.scheme": "http",
"proxy.a.middlewares.request.hide_headers": "X-Header1,X-Header2", "proxy.*.middlewares.request.hide_headers": "X-Header1,X-Header2",
}) })
} }
} }

View file

@ -14,4 +14,5 @@ const (
LabelStopSignal = NSProxy + ".stop_signal" LabelStopSignal = NSProxy + ".stop_signal"
LabelStartEndpoint = NSProxy + ".start_endpoint" LabelStartEndpoint = NSProxy + ".start_endpoint"
LabelDependsOn = NSProxy + ".depends_on" LabelDependsOn = NSProxy + ".depends_on"
LabelNetwork = NSProxy + ".network"
) )

View file

@ -3,7 +3,7 @@ package homepage
import ( import (
"net/http" "net/http"
gpnet "github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/utils/pool" "github.com/yusing/go-proxy/internal/utils/pool"
) )
@ -11,7 +11,7 @@ type route interface {
pool.Object pool.Object
ProviderName() string ProviderName() string
References() []string References() []string
TargetURL() *gpnet.URL TargetURL() *nettypes.URL
} }
type httpRoute interface { type httpRoute interface {

View file

@ -1,8 +1,10 @@
package idlewatcher package idlewatcher
import "context" import (
"context"
)
func (w *Watcher) cancelled(reqCtx context.Context) bool { func (w *Watcher) canceled(reqCtx context.Context) bool {
select { select {
case <-reqCtx.Done(): case <-reqCtx.Done():
w.l.Debug().AnErr("cause", context.Cause(reqCtx)).Msg("wake canceled") w.l.Debug().AnErr("cause", context.Cause(reqCtx)).Msg("wake canceled")

View file

@ -92,7 +92,7 @@ func (w *Watcher) wakeFromHTTP(rw http.ResponseWriter, r *http.Request) (shouldN
} }
ctx := r.Context() ctx := r.Context()
if w.cancelled(ctx) { if w.canceled(ctx) {
w.redirectToStartEndpoint(rw, r) w.redirectToStartEndpoint(rw, r)
return false return false
} }
@ -107,7 +107,7 @@ func (w *Watcher) wakeFromHTTP(rw http.ResponseWriter, r *http.Request) (shouldN
for { for {
w.resetIdleTimer() w.resetIdleTimer()
if w.cancelled(ctx) { if w.canceled(ctx) {
w.redirectToStartEndpoint(rw, r) w.redirectToStartEndpoint(rw, r)
return false return false
} }

View file

@ -5,45 +5,51 @@ import (
"net" "net"
"time" "time"
gpnet "github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
) )
// Setup implements types.Stream. var _ nettypes.Stream = (*Watcher)(nil)
func (w *Watcher) Addr() net.Addr {
return w.stream.Addr() // ListenAndServe implements nettypes.Stream.
func (w *Watcher) ListenAndServe(ctx context.Context, predial, onRead nettypes.HookFunc) {
w.stream.ListenAndServe(ctx, func(ctx context.Context) error { //nolint:contextcheck
return w.preDial(ctx, predial)
}, func(ctx context.Context) error {
return w.onRead(ctx, onRead)
})
} }
// Setup implements types.Stream. // Close implements nettypes.Stream.
func (w *Watcher) Setup() error {
return w.stream.Setup()
}
// Accept implements types.Stream.
func (w *Watcher) Accept() (conn gpnet.StreamConn, err error) {
conn, err = w.stream.Accept()
if err != nil {
return
}
if wakeErr := w.wakeFromStream(); wakeErr != nil {
w.l.Err(wakeErr).Msg("error waking container")
}
return
}
// Handle implements types.Stream.
func (w *Watcher) Handle(conn gpnet.StreamConn) error {
if err := w.wakeFromStream(); err != nil {
return err
}
return w.stream.Handle(conn)
}
// Close implements types.Stream.
func (w *Watcher) Close() error { func (w *Watcher) Close() error {
return w.stream.Close() return w.stream.Close()
} }
func (w *Watcher) wakeFromStream() error { // LocalAddr implements nettypes.Stream.
func (w *Watcher) LocalAddr() net.Addr {
return w.stream.LocalAddr()
}
func (w *Watcher) preDial(ctx context.Context, predial nettypes.HookFunc) error {
if predial != nil {
if err := predial(ctx); err != nil {
return err
}
}
return w.wakeFromStream(ctx)
}
func (w *Watcher) onRead(ctx context.Context, onRead nettypes.HookFunc) error {
w.resetIdleTimer()
if onRead != nil {
if err := onRead(ctx); err != nil {
return err
}
}
return nil
}
func (w *Watcher) wakeFromStream(ctx context.Context) error {
w.resetIdleTimer() w.resetIdleTimer()
// pass through if container is already ready // pass through if container is already ready
@ -52,18 +58,27 @@ func (w *Watcher) wakeFromStream() error {
} }
w.l.Debug().Msg("wake signal received") w.l.Debug().Msg("wake signal received")
err := w.Wake(context.Background()) err := w.Wake(ctx)
if err != nil { if err != nil {
return err return err
} }
for { for {
w.resetIdleTimer()
if w.canceled(ctx) {
return nil
}
if !w.waitStarted(ctx) {
return nil
}
ready, err := w.checkUpdateState() ready, err := w.checkUpdateState()
if err != nil { if err != nil {
return err return err
} }
if ready { if ready {
w.resetIdleTimer()
w.l.Debug().Stringer("url", w.hc.URL()).Msg("container is ready, passing through") w.l.Debug().Stringer("url", w.hc.URL()).Msg("container is ready, passing through")
return nil return nil
} }

View file

@ -3,13 +3,13 @@ package idlewatcher
import ( import (
"net/http" "net/http"
net "github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/watcher/health" "github.com/yusing/go-proxy/internal/watcher/health"
) )
type Waker interface { type Waker interface {
health.HealthMonitor health.HealthMonitor
http.Handler http.Handler
net.Stream nettypes.Stream
Wake() error Wake() error
} }

View file

@ -14,7 +14,7 @@ import (
"github.com/yusing/go-proxy/internal/idlewatcher/provider" "github.com/yusing/go-proxy/internal/idlewatcher/provider"
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types" idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy" "github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
net "github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/route/routes" "github.com/yusing/go-proxy/internal/route/routes"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
U "github.com/yusing/go-proxy/internal/utils" U "github.com/yusing/go-proxy/internal/utils"
@ -30,7 +30,7 @@ type (
routeHelper struct { routeHelper struct {
route routes.Route route routes.Route
rp *reverseproxy.ReverseProxy rp *reverseproxy.ReverseProxy
stream net.Stream stream nettypes.Stream
hc health.HealthChecker hc health.HealthChecker
} }
@ -261,7 +261,7 @@ func NewWatcher(parent task.Parent, r routes.Route, cfg *idlewatcher.Config) (*W
case routes.ReverseProxyRoute: case routes.ReverseProxyRoute:
w.rp = r.ReverseProxy() w.rp = r.ReverseProxy()
case routes.StreamRoute: case routes.StreamRoute:
w.stream = r w.stream = r.Stream()
default: default:
w.provider.Close() w.provider.Close()
return nil, w.newWatcherError(gperr.Errorf("unexpected route type: %T", r)) return nil, w.newWatcherError(gperr.Errorf("unexpected route type: %T", r))

View file

@ -6,7 +6,7 @@ import (
"strings" "strings"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
gpnet "github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/utils/strutils" "github.com/yusing/go-proxy/internal/utils/strutils"
) )
@ -25,7 +25,7 @@ type (
} }
Host string Host string
CIDR struct { CIDR struct {
gpnet.CIDR nettypes.CIDR
} }
) )

View file

@ -6,7 +6,7 @@ import (
"testing" "testing"
. "github.com/yusing/go-proxy/internal/logging/accesslog" . "github.com/yusing/go-proxy/internal/logging/accesslog"
gpnet "github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/utils/strutils" "github.com/yusing/go-proxy/internal/utils/strutils"
expect "github.com/yusing/go-proxy/internal/utils/testing" expect "github.com/yusing/go-proxy/internal/utils/testing"
) )
@ -157,7 +157,7 @@ func TestHeaderFilter(t *testing.T) {
} }
func TestCIDRFilter(t *testing.T) { func TestCIDRFilter(t *testing.T) {
cidr := []*CIDR{{gpnet.CIDR{ cidr := []*CIDR{{nettypes.CIDR{
IP: net.ParseIP("192.168.10.0"), IP: net.ParseIP("192.168.10.0"),
Mask: net.CIDRMask(24, 32), Mask: net.CIDRMask(24, 32),
}}} }}}

View file

@ -4,7 +4,7 @@ import (
"net/http" "net/http"
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types" idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
net "github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
U "github.com/yusing/go-proxy/internal/utils" U "github.com/yusing/go-proxy/internal/utils"
"github.com/yusing/go-proxy/internal/watcher/health" "github.com/yusing/go-proxy/internal/watcher/health"
) )
@ -14,7 +14,7 @@ type (
_ U.NoCopy _ U.NoCopy
name string name string
url *net.URL url *nettypes.URL
weight Weight weight Weight
http.Handler `json:"-"` http.Handler `json:"-"`
@ -26,14 +26,14 @@ type (
health.HealthMonitor health.HealthMonitor
Name() string Name() string
Key() string Key() string
URL() *net.URL URL() *nettypes.URL
Weight() Weight Weight() Weight
SetWeight(weight Weight) SetWeight(weight Weight)
TryWake() error TryWake() error
} }
) )
func NewServer(name string, url *net.URL, weight Weight, handler http.Handler, healthMon health.HealthMonitor) Server { func NewServer(name string, url *nettypes.URL, weight Weight, handler http.Handler, healthMon health.HealthMonitor) Server {
srv := &server{ srv := &server{
name: name, name: name,
url: url, url: url,
@ -47,7 +47,7 @@ func NewServer(name string, url *net.URL, weight Weight, handler http.Handler, h
func TestNewServer[T ~int | ~float32 | ~float64](weight T) Server { func TestNewServer[T ~int | ~float32 | ~float64](weight T) Server {
srv := &server{ srv := &server{
weight: Weight(weight), weight: Weight(weight),
url: net.MustParseURL("http://localhost"), url: nettypes.MustParseURL("http://localhost"),
} }
return srv return srv
} }
@ -56,7 +56,7 @@ func (srv *server) Name() string {
return srv.name return srv.name
} }
func (srv *server) URL() *net.URL { func (srv *server) URL() *nettypes.URL {
return srv.url return srv.url
} }

View file

@ -10,7 +10,7 @@ import (
"github.com/yusing/go-proxy/internal/entrypoint" "github.com/yusing/go-proxy/internal/entrypoint"
. "github.com/yusing/go-proxy/internal/net/gphttp/middleware" . "github.com/yusing/go-proxy/internal/net/gphttp/middleware"
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy" "github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/route" "github.com/yusing/go-proxy/internal/route"
routeTypes "github.com/yusing/go-proxy/internal/route/types" routeTypes "github.com/yusing/go-proxy/internal/route/types"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
@ -99,7 +99,7 @@ func (f fakeRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
} }
func TestReverseProxyBypass(t *testing.T) { func TestReverseProxyBypass(t *testing.T) {
rp := reverseproxy.NewReverseProxy("test", types.MustParseURL("http://example.com"), fakeRoundTripper{}) rp := reverseproxy.NewReverseProxy("test", nettypes.MustParseURL("http://example.com"), fakeRoundTripper{})
err := PatchReverseProxy(rp, map[string]OptionsRaw{ err := PatchReverseProxy(rp, map[string]OptionsRaw{
"response": { "response": {
"bypass": "path /test/* | path /api", "bypass": "path /test/* | path /api",

View file

@ -6,7 +6,7 @@ import (
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
gphttp "github.com/yusing/go-proxy/internal/net/gphttp" gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/serialization" "github.com/yusing/go-proxy/internal/serialization"
F "github.com/yusing/go-proxy/internal/utils/functional" F "github.com/yusing/go-proxy/internal/utils/functional"
) )
@ -17,7 +17,7 @@ type (
cachedAddr F.Map[string, bool] // cache for trusted IPs cachedAddr F.Map[string, bool] // cache for trusted IPs
} }
CIDRWhitelistOpts struct { CIDRWhitelistOpts struct {
Allow []*types.CIDR `validate:"min=1"` Allow []*nettypes.CIDR `validate:"min=1"`
StatusCode int `json:"status_code" aliases:"status" validate:"omitempty,status_code"` StatusCode int `json:"status_code" aliases:"status" validate:"omitempty,status_code"`
Message string Message string
} }
@ -26,7 +26,7 @@ type (
var ( var (
CIDRWhiteList = NewMiddleware[cidrWhitelist]() CIDRWhiteList = NewMiddleware[cidrWhitelist]()
cidrWhitelistDefaults = CIDRWhitelistOpts{ cidrWhitelistDefaults = CIDRWhitelistOpts{
Allow: []*types.CIDR{}, Allow: []*nettypes.CIDR{},
StatusCode: http.StatusForbidden, StatusCode: http.StatusForbidden,
Message: "IP not allowed", Message: "IP not allowed",
} }

View file

@ -12,7 +12,7 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/yusing/go-proxy/internal/common" "github.com/yusing/go-proxy/internal/common"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/utils/atomic" "github.com/yusing/go-proxy/internal/utils/atomic"
"github.com/yusing/go-proxy/internal/utils/strutils" "github.com/yusing/go-proxy/internal/utils/strutils"
) )
@ -33,7 +33,7 @@ var (
cfCIDRsMu sync.Mutex cfCIDRsMu sync.Mutex
// RFC 1918. // RFC 1918.
localCIDRs = []*types.CIDR{ localCIDRs = []*nettypes.CIDR{
{IP: net.IPv4(127, 0, 0, 1), Mask: net.IPv4Mask(255, 255, 255, 255)}, // 127.0.0.1/32 {IP: net.IPv4(127, 0, 0, 1), Mask: net.IPv4Mask(255, 255, 255, 255)}, // 127.0.0.1/32
{IP: net.IPv4(10, 0, 0, 0), Mask: net.IPv4Mask(255, 0, 0, 0)}, // 10.0.0.0/8 {IP: net.IPv4(10, 0, 0, 0), Mask: net.IPv4Mask(255, 0, 0, 0)}, // 10.0.0.0/8
{IP: net.IPv4(172, 16, 0, 0), Mask: net.IPv4Mask(255, 240, 0, 0)}, // 172.16.0.0/12 {IP: net.IPv4(172, 16, 0, 0), Mask: net.IPv4Mask(255, 240, 0, 0)}, // 172.16.0.0/12
@ -60,7 +60,7 @@ func (cri *cloudflareRealIP) before(w http.ResponseWriter, r *http.Request) bool
return cri.realIP.before(w, r) return cri.realIP.before(w, r)
} }
func tryFetchCFCIDR() (cfCIDRs []*types.CIDR) { func tryFetchCFCIDR() (cfCIDRs []*nettypes.CIDR) {
if time.Since(cfCIDRsLastUpdate.Load()) < cfCIDRsUpdateInterval { if time.Since(cfCIDRsLastUpdate.Load()) < cfCIDRsUpdateInterval {
return return
} }
@ -75,7 +75,7 @@ func tryFetchCFCIDR() (cfCIDRs []*types.CIDR) {
if common.IsTest { if common.IsTest {
cfCIDRs = localCIDRs cfCIDRs = localCIDRs
} else { } else {
cfCIDRs = make([]*types.CIDR, 0, 30) cfCIDRs = make([]*nettypes.CIDR, 0, 30)
err := errors.Join( err := errors.Join(
fetchUpdateCFIPRange(cfIPv4CIDRsEndpoint, &cfCIDRs), fetchUpdateCFIPRange(cfIPv4CIDRsEndpoint, &cfCIDRs),
fetchUpdateCFIPRange(cfIPv6CIDRsEndpoint, &cfCIDRs), fetchUpdateCFIPRange(cfIPv6CIDRsEndpoint, &cfCIDRs),
@ -95,7 +95,7 @@ func tryFetchCFCIDR() (cfCIDRs []*types.CIDR) {
return return
} }
func fetchUpdateCFIPRange(endpoint string, cfCIDRs *[]*types.CIDR) error { func fetchUpdateCFIPRange(endpoint string, cfCIDRs *[]*nettypes.CIDR) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() defer cancel()
@ -124,7 +124,7 @@ func fetchUpdateCFIPRange(endpoint string, cfCIDRs *[]*types.CIDR) error {
return fmt.Errorf("cloudflare responeded an invalid CIDR: %s", line) return fmt.Errorf("cloudflare responeded an invalid CIDR: %s", line)
} }
*cfCIDRs = append(*cfCIDRs, (*types.CIDR)(cidr)) *cfCIDRs = append(*cfCIDRs, (*nettypes.CIDR)(cidr))
} }
*cfCIDRs = append(*cfCIDRs, localCIDRs...) *cfCIDRs = append(*cfCIDRs, localCIDRs...)
return nil return nil

View file

@ -7,7 +7,7 @@ import (
"slices" "slices"
"testing" "testing"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
. "github.com/yusing/go-proxy/internal/utils/testing" . "github.com/yusing/go-proxy/internal/utils/testing"
) )
@ -51,8 +51,8 @@ func TestModifyRequest(t *testing.T) {
}) })
t.Run("request_headers", func(t *testing.T) { t.Run("request_headers", func(t *testing.T) {
reqURL := types.MustParseURL("https://my.app/?arg_1=b") reqURL := nettypes.MustParseURL("https://my.app/?arg_1=b")
upstreamURL := types.MustParseURL("http://test.example.com") upstreamURL := nettypes.MustParseURL("http://test.example.com")
result, err := newMiddlewareTest(ModifyRequest, &testArgs{ result, err := newMiddlewareTest(ModifyRequest, &testArgs{
middlewareOpt: opts, middlewareOpt: opts,
reqURL: reqURL, reqURL: reqURL,
@ -128,8 +128,8 @@ func TestModifyRequest(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
reqURL := types.MustParseURL("https://my.app" + tt.path) reqURL := nettypes.MustParseURL("https://my.app" + tt.path)
upstreamURL := types.MustParseURL(tt.upstreamURL) upstreamURL := nettypes.MustParseURL(tt.upstreamURL)
opts["add_prefix"] = tt.addPrefix opts["add_prefix"] = tt.addPrefix
result, err := newMiddlewareTest(ModifyRequest, &testArgs{ result, err := newMiddlewareTest(ModifyRequest, &testArgs{

View file

@ -7,7 +7,7 @@ import (
"slices" "slices"
"testing" "testing"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
. "github.com/yusing/go-proxy/internal/utils/testing" . "github.com/yusing/go-proxy/internal/utils/testing"
) )
@ -54,8 +54,8 @@ func TestModifyResponse(t *testing.T) {
}) })
t.Run("response_headers", func(t *testing.T) { t.Run("response_headers", func(t *testing.T) {
reqURL := types.MustParseURL("https://my.app/?arg_1=b") reqURL := nettypes.MustParseURL("https://my.app/?arg_1=b")
upstreamURL := types.MustParseURL("http://test.example.com") upstreamURL := nettypes.MustParseURL("http://test.example.com")
result, err := newMiddlewareTest(ModifyResponse, &testArgs{ result, err := newMiddlewareTest(ModifyResponse, &testArgs{
middlewareOpt: opts, middlewareOpt: opts,
reqURL: reqURL, reqURL: reqURL,

View file

@ -5,7 +5,7 @@ import (
"net/http" "net/http"
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders" "github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
) )
// https://nginx.org/en/docs/http/ngx_http_realip_module.html // https://nginx.org/en/docs/http/ngx_http_realip_module.html
@ -16,7 +16,7 @@ type (
// Header is the name of the header to use for the real client IP // Header is the name of the header to use for the real client IP
Header string `validate:"required"` Header string `validate:"required"`
// From is a list of Address / CIDRs to trust // From is a list of Address / CIDRs to trust
From []*types.CIDR `validate:"required,min=1"` From []*nettypes.CIDR `validate:"required,min=1"`
/* /*
If recursive search is disabled, If recursive search is disabled,
the original client address that matches one of the trusted addresses is replaced by the original client address that matches one of the trusted addresses is replaced by
@ -33,7 +33,7 @@ var (
RealIP = NewMiddleware[realIP]() RealIP = NewMiddleware[realIP]()
realIPOptsDefault = RealIPOpts{ realIPOptsDefault = RealIPOpts{
Header: "X-Real-IP", Header: "X-Real-IP",
From: []*types.CIDR{}, From: []*nettypes.CIDR{},
} }
) )

View file

@ -7,7 +7,7 @@ import (
"testing" "testing"
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders" "github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
. "github.com/yusing/go-proxy/internal/utils/testing" . "github.com/yusing/go-proxy/internal/utils/testing"
) )
@ -23,7 +23,7 @@ func TestSetRealIPOpts(t *testing.T) {
} }
optExpected := &RealIPOpts{ optExpected := &RealIPOpts{
Header: httpheaders.HeaderXRealIP, Header: httpheaders.HeaderXRealIP,
From: []*types.CIDR{ From: []*nettypes.CIDR{
{ {
IP: net.ParseIP("127.0.0.0"), IP: net.ParseIP("127.0.0.0"),
Mask: net.IPv4Mask(255, 0, 0, 0), Mask: net.IPv4Mask(255, 0, 0, 0),

View file

@ -4,13 +4,13 @@ import (
"net/http" "net/http"
"testing" "testing"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
. "github.com/yusing/go-proxy/internal/utils/testing" . "github.com/yusing/go-proxy/internal/utils/testing"
) )
func TestRedirectToHTTPs(t *testing.T) { func TestRedirectToHTTPs(t *testing.T) {
result, err := newMiddlewareTest(RedirectHTTP, &testArgs{ result, err := newMiddlewareTest(RedirectHTTP, &testArgs{
reqURL: types.MustParseURL("http://example.com"), reqURL: nettypes.MustParseURL("http://example.com"),
}) })
ExpectNoError(t, err) ExpectNoError(t, err)
ExpectEqual(t, result.ResponseStatus, http.StatusPermanentRedirect) ExpectEqual(t, result.ResponseStatus, http.StatusPermanentRedirect)
@ -19,7 +19,7 @@ func TestRedirectToHTTPs(t *testing.T) {
func TestNoRedirect(t *testing.T) { func TestNoRedirect(t *testing.T) {
result, err := newMiddlewareTest(RedirectHTTP, &testArgs{ result, err := newMiddlewareTest(RedirectHTTP, &testArgs{
reqURL: types.MustParseURL("https://example.com"), reqURL: nettypes.MustParseURL("https://example.com"),
}) })
ExpectNoError(t, err) ExpectNoError(t, err)
ExpectEqual(t, result.ResponseStatus, http.StatusOK) ExpectEqual(t, result.ResponseStatus, http.StatusOK)

View file

@ -11,7 +11,7 @@ import (
"github.com/yusing/go-proxy/internal/common" "github.com/yusing/go-proxy/internal/common"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy" "github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
. "github.com/yusing/go-proxy/internal/utils/testing" . "github.com/yusing/go-proxy/internal/utils/testing"
) )
@ -80,11 +80,11 @@ type TestResult struct {
type testArgs struct { type testArgs struct {
middlewareOpt OptionsRaw middlewareOpt OptionsRaw
upstreamURL *types.URL upstreamURL *nettypes.URL
realRoundTrip bool realRoundTrip bool
reqURL *types.URL reqURL *nettypes.URL
reqMethod string reqMethod string
headers http.Header headers http.Header
body []byte body []byte
@ -96,13 +96,13 @@ type testArgs struct {
func (args *testArgs) setDefaults() { func (args *testArgs) setDefaults() {
if args.reqURL == nil { if args.reqURL == nil {
args.reqURL = Must(types.ParseURL("https://example.com")) args.reqURL = Must(nettypes.ParseURL("https://example.com"))
} }
if args.reqMethod == "" { if args.reqMethod == "" {
args.reqMethod = http.MethodGet args.reqMethod = http.MethodGet
} }
if args.upstreamURL == nil { if args.upstreamURL == nil {
args.upstreamURL = Must(types.ParseURL("https://10.0.0.1:8443")) // dummy url, no actual effect args.upstreamURL = Must(nettypes.ParseURL("https://10.0.0.1:8443")) // dummy url, no actual effect
} }
if args.respHeaders == nil { if args.respHeaders == nil {
args.respHeaders = http.Header{} args.respHeaders = http.Header{}

View file

@ -28,7 +28,7 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/yusing/go-proxy/internal/logging/accesslog" "github.com/yusing/go-proxy/internal/logging/accesslog"
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders" "github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
U "github.com/yusing/go-proxy/internal/utils" U "github.com/yusing/go-proxy/internal/utils"
"golang.org/x/net/http/httpguts" "golang.org/x/net/http/httpguts"
) )
@ -93,7 +93,7 @@ type ReverseProxy struct {
HandlerFunc http.HandlerFunc HandlerFunc http.HandlerFunc
TargetName string TargetName string
TargetURL *types.URL TargetURL *nettypes.URL
} }
func singleJoiningSlash(a, b string) string { func singleJoiningSlash(a, b string) string {
@ -133,7 +133,7 @@ func joinURLPath(a, b *url.URL) (path, rawpath string) {
// URLs to the scheme, host, and base path provided in target. If the // URLs to the scheme, host, and base path provided in target. If the
// target's path is "/base" and the incoming request was for "/dir", // target's path is "/base" and the incoming request was for "/dir",
// the target request will be for /base/dir. // the target request will be for /base/dir.
func NewReverseProxy(name string, target *types.URL, transport http.RoundTripper) *ReverseProxy { func NewReverseProxy(name string, target *nettypes.URL, transport http.RoundTripper) *ReverseProxy {
if transport == nil { if transport == nil {
panic("nil transport") panic("nil transport")
} }

View file

@ -1,4 +1,4 @@
package types package nettypes
import ( import (
"net" "net"

View file

@ -1,32 +1,14 @@
package types package nettypes
import ( import (
"fmt" "context"
"net" "net"
) )
type ( type Stream interface {
Stream interface { ListenAndServe(ctx context.Context, preDial, onRead HookFunc)
fmt.Stringer LocalAddr() net.Addr
StreamListener
Setup() error
Handle(conn StreamConn) error
}
StreamListener interface {
Addr() net.Addr
Accept() (StreamConn, error)
Close() error Close() error
} }
StreamConn any
NetListenerWrapper struct {
net.Listener
}
)
func NetListener(l net.Listener) StreamListener { type HookFunc func(ctx context.Context) error
return NetListenerWrapper{Listener: l}
}
func (l NetListenerWrapper) Accept() (StreamConn, error) {
return l.Listener.Accept()
}

View file

@ -1,4 +1,4 @@
package types package nettypes
import ( import (
urlPkg "net/url" urlPkg "net/url"

View file

@ -1,10 +1,12 @@
package notif package notif
import ( import (
"math"
"math/rand/v2"
"time" "time"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/yusing/go-proxy/internal/gperr" "github.com/rs/zerolog/log"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
F "github.com/yusing/go-proxy/internal/utils/functional" F "github.com/yusing/go-proxy/internal/utils/functional"
) )
@ -14,7 +16,7 @@ type (
task *task.Task task *task.Task
providers F.Set[Provider] providers F.Set[Provider]
logCh chan *LogMessage logCh chan *LogMessage
retryCh chan *RetryMessage retryMsg F.Set[*RetryMessage]
retryTicker *time.Ticker retryTicker *time.Ticker
} }
LogMessage struct { LogMessage struct {
@ -23,32 +25,22 @@ type (
Body LogBody Body LogBody
Color Color Color Color
} }
RetryMessage struct {
Message *LogMessage
Trials int
Provider Provider
}
) )
var dispatcher *Dispatcher var dispatcher *Dispatcher
const retryInterval = 5 * time.Second const (
retryInterval = time.Second
var maxRetries = map[zerolog.Level]int{ maxBackoffDelay = 5 * time.Minute
zerolog.DebugLevel: 1, backoffMultiplier = 2.0
zerolog.InfoLevel: 1, )
zerolog.WarnLevel: 3,
zerolog.ErrorLevel: 5,
zerolog.FatalLevel: 10,
zerolog.PanicLevel: 10,
}
func StartNotifDispatcher(parent task.Parent) *Dispatcher { func StartNotifDispatcher(parent task.Parent) *Dispatcher {
dispatcher = &Dispatcher{ dispatcher = &Dispatcher{
task: parent.Subtask("notification", true), task: parent.Subtask("notification", true),
providers: F.NewSet[Provider](), providers: F.NewSet[Provider](),
logCh: make(chan *LogMessage), logCh: make(chan *LogMessage, 100),
retryCh: make(chan *RetryMessage, 100), retryMsg: F.NewSet[*RetryMessage](),
retryTicker: time.NewTicker(retryInterval), retryTicker: time.NewTicker(retryInterval),
} }
go dispatcher.start() go dispatcher.start()
@ -73,11 +65,10 @@ func (disp *Dispatcher) RegisterProvider(cfg *NotificationConfig) {
func (disp *Dispatcher) start() { func (disp *Dispatcher) start() {
defer func() { defer func() {
dispatcher = nil
disp.providers.Clear() disp.providers.Clear()
close(disp.logCh) close(disp.logCh)
close(disp.retryCh)
disp.task.Finish(nil) disp.task.Finish(nil)
dispatcher = nil
}() }()
for { for {
@ -90,22 +81,7 @@ func (disp *Dispatcher) start() {
} }
go disp.dispatch(msg) go disp.dispatch(msg)
case <-disp.retryTicker.C: case <-disp.retryTicker.C:
if len(disp.retryCh) == 0 { disp.processRetries()
continue
}
var msgs []*RetryMessage
done := false
for !done {
select {
case msg := <-disp.retryCh:
msgs = append(msgs, msg)
default:
done = true
}
}
if err := disp.retry(msgs); err != nil {
gperr.LogError("notification retry failed", err)
}
} }
} }
} }
@ -114,34 +90,100 @@ func (disp *Dispatcher) dispatch(msg *LogMessage) {
task := disp.task.Subtask("dispatcher", true) task := disp.task.Subtask("dispatcher", true)
defer task.Finish("notif dispatched") defer task.Finish("notif dispatched")
l := log.With().
Str("level", msg.Level.String()).
Str("title", msg.Title).Logger()
disp.providers.RangeAllParallel(func(p Provider) { disp.providers.RangeAllParallel(func(p Provider) {
if err := msg.notify(task.Context(), p); err != nil { if err := msg.notify(task.Context(), p); err != nil {
disp.retryCh <- &RetryMessage{ msg := &RetryMessage{
Message: msg, Message: msg,
Trials: 0, Trials: 0,
Provider: p, Provider: p,
NextRetry: time.Now().Add(calculateBackoffDelay(0)),
} }
disp.retryMsg.Add(msg)
l.Debug().Err(err).EmbedObject(msg).Msg("notification failed, scheduling retry")
} else {
l.Debug().Str("provider", p.GetName()).Msg("notification sent successfully")
} }
}) })
} }
func (disp *Dispatcher) retry(messages []*RetryMessage) error { func (disp *Dispatcher) processRetries() {
if disp.retryMsg.Size() == 0 {
return
}
now := time.Now()
readyMessages := make([]*RetryMessage, 0)
for msg := range disp.retryMsg.Range {
if now.After(msg.NextRetry) {
readyMessages = append(readyMessages, msg)
disp.retryMsg.Remove(msg)
}
}
disp.retry(readyMessages)
}
func (disp *Dispatcher) retry(messages []*RetryMessage) {
if len(messages) == 0 {
return
}
task := disp.task.Subtask("retry", true) task := disp.task.Subtask("retry", true)
defer task.Finish("notif retried") defer task.Finish("notif retried")
errs := gperr.NewBuilder("notification failure") successCount := 0
failureCount := 0
for _, msg := range messages { for _, msg := range messages {
maxTrials := maxRetries[msg.Message.Level]
log.Debug().EmbedObject(msg).Msg("attempting notification retry")
err := msg.Message.notify(task.Context(), msg.Provider) err := msg.Message.notify(task.Context(), msg.Provider)
if err == nil { if err == nil {
msg.NextRetry = time.Time{}
successCount++
log.Debug().EmbedObject(msg).Msg("notification retry succeeded")
continue continue
} }
if msg.Trials > maxRetries[msg.Message.Level] {
errs.Addf("notification provider %s failed after %d trials", msg.Provider.GetName(), msg.Trials)
errs.Add(err)
continue
}
msg.Trials++ msg.Trials++
disp.retryCh <- msg failureCount++
if msg.Trials >= maxTrials {
log.Warn().Err(err).EmbedObject(msg).Msg("notification permanently failed after max retries")
continue
} }
return errs.Error()
// Schedule next retry with exponential backoff
msg.NextRetry = time.Now().Add(calculateBackoffDelay(msg.Trials))
disp.retryMsg.Add(msg)
log.Debug().EmbedObject(msg).Msg("notification retry failed, scheduled for later")
}
log.Info().
Int("total", len(messages)).
Int("successes", successCount).
Int("failures", failureCount).
Msg("notification retry batch completed")
}
// calculateBackoffDelay implements exponential backoff with jitter.
func calculateBackoffDelay(trials int) time.Duration {
if trials == 0 {
return retryInterval
}
// Exponential backoff: retryInterval * (backoffMultiplier ^ trials)
delay := min(float64(retryInterval)*math.Pow(backoffMultiplier, float64(trials)), float64(maxBackoffDelay))
// Add 20% jitter to prevent thundering herd
//nolint:gosec
jitter := delay * 0.2 * (rand.Float64() - 0.5) // -10% to +10%
return time.Duration(delay + jitter)
} }

View file

@ -8,7 +8,6 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/rs/zerolog/log"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/serialization" "github.com/yusing/go-proxy/internal/serialization"
) )
@ -72,13 +71,6 @@ func (msg *LogMessage) notify(ctx context.Context, provider Provider) error {
switch resp.StatusCode { switch resp.StatusCode {
case http.StatusOK, http.StatusCreated, http.StatusAccepted, http.StatusNoContent: case http.StatusOK, http.StatusCreated, http.StatusAccepted, http.StatusNoContent:
body, _ := io.ReadAll(resp.Body)
log.Debug().
Str("provider", provider.GetName()).
Str("url", provider.GetURL()).
Str("status", resp.Status).
RawJSON("resp_body", body).
Msg("notification sent")
return nil return nil
default: default:
return fmt.Errorf("http status %d: %w", resp.StatusCode, provider.fmtError(resp.Body)) return fmt.Errorf("http status %d: %w", resp.StatusCode, provider.fmtError(resp.Body))

View file

@ -0,0 +1,33 @@
package notif
import (
"time"
"github.com/rs/zerolog"
)
type RetryMessage struct {
Message *LogMessage
Trials int
Provider Provider
NextRetry time.Time
}
var maxRetries = map[zerolog.Level]int{
zerolog.DebugLevel: 1,
zerolog.InfoLevel: 1,
zerolog.WarnLevel: 3,
zerolog.ErrorLevel: 5,
zerolog.FatalLevel: 10,
zerolog.PanicLevel: 10,
}
func (msg *RetryMessage) MarshalZerologObject(e *zerolog.Event) {
e.Str("provider", msg.Provider.GetName()).
Int("trial", msg.Trials+1).
Str("title", msg.Message.Title)
if !msg.NextRetry.IsZero() {
e.Int("max_retries", maxRetries[msg.Message.Level]).
Time("next_retry", msg.NextRetry)
}
}

View file

@ -72,6 +72,11 @@ func (p *DockerProvider) loadRoutesImpl() (route.Routes, gperr.Error) {
for _, c := range containers { for _, c := range containers {
container := docker.FromDocker(&c, p.dockerHost) container := docker.FromDocker(&c, p.dockerHost)
if container.Errors != nil {
errs.Add(container.Errors)
continue
}
if container.IsHostNetworkMode { if container.IsHostNetworkMode {
err := container.UpdatePorts() err := container.UpdatePorts()
if err != nil { if err != nil {
@ -124,11 +129,9 @@ func (p *DockerProvider) routesFromContainerLabels(container *docker.Container)
errs := gperr.NewBuilder("label errors") errs := gperr.NewBuilder("label errors")
m, err := docker.ParseLabels(container.Labels) m, err := docker.ParseLabels(container.Labels, container.Aliases...)
errs.Add(err) errs.Add(err)
var wildcardProps docker.LabelMap
for alias, entryMapAny := range m { for alias, entryMapAny := range m {
if len(alias) == 0 { if len(alias) == 0 {
errs.Add(gperr.New("empty alias")) errs.Add(gperr.New("empty alias"))
@ -150,11 +153,6 @@ func (p *DockerProvider) routesFromContainerLabels(container *docker.Container)
} }
} }
if alias == docker.WildcardAlias {
wildcardProps = entryMap
continue
}
// check if it is an alias reference // check if it is an alias reference
switch alias[0] { switch alias[0] {
case aliasRefPrefix, aliasRefPrefixAlt: case aliasRefPrefix, aliasRefPrefixAlt:
@ -189,14 +187,6 @@ func (p *DockerProvider) routesFromContainerLabels(container *docker.Container)
routes[alias] = r routes[alias] = r
} }
} }
if wildcardProps != nil {
for _, re := range routes {
if err := serialization.MapUnmarshalValidate(wildcardProps, re); err != nil {
errs.Add(err.Subject(docker.WildcardAlias))
break
}
}
}
return routes, errs.Error() return routes, errs.Error()
} }

View file

@ -4,7 +4,6 @@ import (
"testing" "testing"
"time" "time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/client" "github.com/docker/docker/client"
D "github.com/yusing/go-proxy/internal/docker" D "github.com/yusing/go-proxy/internal/docker"
@ -319,7 +318,7 @@ func TestStreamDefaultValues(t *testing.T) {
}, },
}, },
}, },
Ports: []types.Port{ Ports: []container.Port{
{Type: "udp", PrivatePort: privPort, PublicPort: pubPort}, {Type: "udp", PrivatePort: privPort, PublicPort: pubPort},
}, },
} }
@ -372,7 +371,7 @@ func TestImplicitExcludeDatabase(t *testing.T) {
t.Run("exposed port detection", func(t *testing.T) { t.Run("exposed port detection", func(t *testing.T) {
r, ok := makeRoutes(&container.SummaryTrimmed{ r, ok := makeRoutes(&container.SummaryTrimmed{
Names: dummyNames, Names: dummyNames,
Ports: []types.Port{ Ports: []container.Port{
{Type: "tcp", PrivatePort: 5432, PublicPort: 5432}, {Type: "tcp", PrivatePort: 5432, PublicPort: 5432},
}, },
})["a"] })["a"]

View file

@ -16,7 +16,7 @@ import (
loadbalance "github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer/types" loadbalance "github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer/types"
"github.com/yusing/go-proxy/internal/net/gphttp/middleware" "github.com/yusing/go-proxy/internal/net/gphttp/middleware"
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy" "github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/route/routes" "github.com/yusing/go-proxy/internal/route/routes"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
"github.com/yusing/go-proxy/internal/watcher/health/monitor" "github.com/yusing/go-proxy/internal/watcher/health/monitor"
@ -39,10 +39,10 @@ func NewReverseProxyRoute(base *Route) (*ReveseProxyRoute, gperr.Error) {
proxyURL := base.ProxyURL proxyURL := base.ProxyURL
var trans *http.Transport var trans *http.Transport
a := base.Agent() a := base.GetAgent()
if a != nil { if a != nil {
trans = a.Transport() trans = a.Transport()
proxyURL = types.NewURL(agent.HTTPProxyURL) proxyURL = nettypes.NewURL(agent.HTTPProxyURL)
} else { } else {
trans = gphttp.NewTransport() trans = gphttp.NewTransport()
if httpConfig.NoTLSVerify { if httpConfig.NoTLSVerify {

View file

@ -16,7 +16,7 @@ import (
"github.com/yusing/go-proxy/internal/homepage" "github.com/yusing/go-proxy/internal/homepage"
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types" idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
netutils "github.com/yusing/go-proxy/internal/net" netutils "github.com/yusing/go-proxy/internal/net"
net "github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/proxmox" "github.com/yusing/go-proxy/internal/proxmox"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
"github.com/yusing/go-proxy/internal/utils/strutils" "github.com/yusing/go-proxy/internal/utils/strutils"
@ -39,7 +39,7 @@ type (
Alias string `json:"alias"` Alias string `json:"alias"`
Scheme route.Scheme `json:"scheme,omitempty"` Scheme route.Scheme `json:"scheme,omitempty"`
Host string `json:"host,omitempty"` Host string `json:"host,omitempty"`
Port route.Port `json:"port,omitempty"` Port route.Port `json:"port"`
Root string `json:"root,omitempty"` Root string `json:"root,omitempty"`
route.HTTPConfig route.HTTPConfig
@ -50,6 +50,7 @@ type (
Middlewares map[string]docker.LabelMap `json:"middlewares,omitempty"` Middlewares map[string]docker.LabelMap `json:"middlewares,omitempty"`
Homepage *homepage.ItemConfig `json:"homepage,omitempty"` Homepage *homepage.ItemConfig `json:"homepage,omitempty"`
AccessLog *accesslog.RequestLoggerConfig `json:"access_log,omitempty"` AccessLog *accesslog.RequestLoggerConfig `json:"access_log,omitempty"`
Agent string `json:"agent,omitempty"`
Idlewatcher *idlewatcher.Config `json:"idlewatcher,omitempty"` Idlewatcher *idlewatcher.Config `json:"idlewatcher,omitempty"`
HealthMon health.HealthMonitor `json:"health,omitempty"` HealthMon health.HealthMonitor `json:"health,omitempty"`
@ -64,8 +65,8 @@ type (
Provider string `json:"provider,omitempty"` // for backward compatibility Provider string `json:"provider,omitempty"` // for backward compatibility
// private fields // private fields
LisURL *net.URL `json:"lurl,omitempty"` LisURL *nettypes.URL `json:"lurl,omitempty"`
ProxyURL *net.URL `json:"purl,omitempty"` ProxyURL *nettypes.URL `json:"purl,omitempty"`
Excluded *bool `json:"excluded"` Excluded *bool `json:"excluded"`
@ -76,6 +77,8 @@ type (
lastError gperr.Error lastError gperr.Error
provider routes.Provider provider routes.Provider
agent *agent.AgentConfig
started chan struct{} started chan struct{}
once sync.Once once sync.Once
} }
@ -94,6 +97,23 @@ func (r *Route) Validate() gperr.Error {
return r.lastError return r.lastError
} }
r.isValidated = true r.isValidated = true
if r.Agent != "" {
if r.Container != nil {
return gperr.Errorf("specifying agent is not allowed for docker container routes")
}
var ok bool
// by agent address
r.agent, ok = agent.GetAgent(r.Agent)
if !ok {
// fallback to get agent by name
r.agent, ok = agent.GetAgentByName(r.Agent)
if !ok {
return gperr.Errorf("agent %s not found", r.Agent)
}
}
}
r.Finalize() r.Finalize()
r.started = make(chan struct{}) r.started = make(chan struct{})
@ -177,7 +197,8 @@ func (r *Route) Validate() gperr.Error {
r.Idlewatcher = r.Container.IdlewatcherConfig r.Idlewatcher = r.Container.IdlewatcherConfig
} }
// return error if route is localhost:<godoxy_port> // return error if route is localhost:<godoxy_port> but route is not agent
if !r.IsAgent() {
switch r.Host { switch r.Host {
case "localhost", "127.0.0.1": case "localhost", "127.0.0.1":
switch r.Port.Proxy { switch r.Port.Proxy {
@ -187,6 +208,7 @@ func (r *Route) Validate() gperr.Error {
} }
} }
} }
}
errs := gperr.NewBuilder("entry validation failed") errs := gperr.NewBuilder("entry validation failed")
@ -195,19 +217,19 @@ func (r *Route) Validate() gperr.Error {
switch r.Scheme { switch r.Scheme {
case route.SchemeFileServer: case route.SchemeFileServer:
r.ProxyURL = gperr.Collect(errs, net.ParseURL, "file://"+r.Root) r.ProxyURL = gperr.Collect(errs, nettypes.ParseURL, "file://"+r.Root)
r.Host = "" r.Host = ""
r.Port.Proxy = 0 r.Port.Proxy = 0
case route.SchemeHTTP, route.SchemeHTTPS: case route.SchemeHTTP, route.SchemeHTTPS:
if r.Port.Listening != 0 { if r.Port.Listening != 0 {
errs.Addf("unexpected listening port for %s scheme", r.Scheme) errs.Addf("unexpected listening port for %s scheme", r.Scheme)
} }
r.ProxyURL = gperr.Collect(errs, net.ParseURL, fmt.Sprintf("%s://%s:%d", r.Scheme, r.Host, r.Port.Proxy)) r.ProxyURL = gperr.Collect(errs, nettypes.ParseURL, fmt.Sprintf("%s://%s:%d", r.Scheme, r.Host, r.Port.Proxy))
case route.SchemeTCP, route.SchemeUDP: case route.SchemeTCP, route.SchemeUDP:
if !r.ShouldExclude() { if !r.ShouldExclude() {
r.LisURL = gperr.Collect(errs, net.ParseURL, fmt.Sprintf("%s://:%d", r.Scheme, r.Port.Listening)) r.LisURL = gperr.Collect(errs, nettypes.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.ProxyURL = gperr.Collect(errs, nettypes.ParseURL, fmt.Sprintf("%s://%s:%d", r.Scheme, r.Host, r.Port.Proxy))
} }
if !r.UseHealthCheck() && (r.UseLoadBalance() || r.UseIdleWatcher()) { if !r.UseHealthCheck() && (r.UseLoadBalance() || r.UseIdleWatcher()) {
@ -309,7 +331,7 @@ func (r *Route) ProviderName() string {
return r.Provider return r.Provider
} }
func (r *Route) TargetURL() *net.URL { func (r *Route) TargetURL() *nettypes.URL {
return r.ProxyURL return r.ProxyURL
} }
@ -351,15 +373,15 @@ func (r *Route) Type() route.RouteType {
panic(fmt.Errorf("unexpected scheme %s for alias %s", r.Scheme, r.Alias)) panic(fmt.Errorf("unexpected scheme %s for alias %s", r.Scheme, r.Alias))
} }
func (r *Route) Agent() *agent.AgentConfig { func (r *Route) GetAgent() *agent.AgentConfig {
if r.Container == nil { if r.Container != nil && r.Container.Agent != nil {
return nil
}
return r.Container.Agent return r.Container.Agent
} }
return r.agent
}
func (r *Route) IsAgent() bool { func (r *Route) IsAgent() bool {
return r.Container != nil && r.Container.Agent != nil return r.GetAgent() != nil
} }
func (r *Route) HealthMonitor() health.HealthMonitor { func (r *Route) HealthMonitor() health.HealthMonitor {

View file

@ -148,3 +148,37 @@ func TestPreferredPort(t *testing.T) {
port := preferredPort(ports) port := preferredPort(ports)
expect.Equal(t, port, 3000) expect.Equal(t, port, 3000)
} }
func TestDockerRouteDisallowAgent(t *testing.T) {
r := &Route{
Alias: "test",
Scheme: route.SchemeHTTP,
Host: "example.com",
Port: route.Port{Proxy: 80},
Agent: "test-agent",
Metadata: Metadata{
Container: &docker.Container{
ContainerID: "test-id",
Image: &docker.ContainerImage{
Name: "test-image",
},
},
},
}
err := r.Validate()
expect.HasError(t, err, "Validate should return error for docker route with agent")
expect.ErrorContains(t, err, "specifying agent is not allowed for docker container routes")
}
func TestRouteAgent(t *testing.T) {
r := &Route{
Alias: "test",
Scheme: route.SchemeHTTP,
Host: "example.com",
Port: route.Port{Proxy: 80},
Agent: "test-agent",
}
err := r.Validate()
expect.NoError(t, err, "Validate should not return error for valid route with agent")
expect.NotNil(t, r.GetAgent(), "GetAgent should return agent")
}

View file

@ -7,13 +7,13 @@ import (
"github.com/yusing/go-proxy/internal/docker" "github.com/yusing/go-proxy/internal/docker"
"github.com/yusing/go-proxy/internal/homepage" "github.com/yusing/go-proxy/internal/homepage"
idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types" idlewatcher "github.com/yusing/go-proxy/internal/idlewatcher/types"
net "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
"github.com/yusing/go-proxy/internal/utils/pool" "github.com/yusing/go-proxy/internal/utils/pool"
"github.com/yusing/go-proxy/internal/watcher/health" "github.com/yusing/go-proxy/internal/watcher/health"
loadbalance "github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer/types" loadbalance "github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer/types"
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy" "github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
nettypes "github.com/yusing/go-proxy/internal/net/types"
) )
type ( type (
@ -24,7 +24,7 @@ type (
pool.Object pool.Object
ProviderName() string ProviderName() string
GetProvider() Provider GetProvider() Provider
TargetURL() *net.URL TargetURL() *nettypes.URL
HealthMonitor() health.HealthMonitor HealthMonitor() health.HealthMonitor
SetHealthMonitor(m health.HealthMonitor) SetHealthMonitor(m health.HealthMonitor)
References() []string References() []string
@ -38,7 +38,7 @@ type (
HomepageItem() *homepage.Item HomepageItem() *homepage.Item
ContainerInfo() *docker.Container ContainerInfo() *docker.Container
Agent() *agent.AgentConfig GetAgent() *agent.AgentConfig
IsDocker() bool IsDocker() bool
IsAgent() bool IsAgent() bool
@ -57,7 +57,8 @@ type (
} }
StreamRoute interface { StreamRoute interface {
Route Route
net.Stream nettypes.Stream
Stream() nettypes.Stream
} }
Provider interface { Provider interface {
GetRoute(alias string) (r Route, ok bool) GetRoute(alias string) (r Route, ok bool)

View file

@ -9,7 +9,7 @@ import (
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
gphttp "github.com/yusing/go-proxy/internal/net/gphttp" gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy" "github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/utils/strutils" "github.com/yusing/go-proxy/internal/utils/strutils"
) )
@ -95,7 +95,7 @@ var commands = map[string]struct {
}, },
validate: validateURL, validate: validateURL,
build: func(args any) CommandHandler { build: func(args any) CommandHandler {
target := args.(*types.URL).String() target := args.(*nettypes.URL).String()
return ReturningCommand(func(w http.ResponseWriter, r *http.Request) { return ReturningCommand(func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, target, http.StatusTemporaryRedirect) http.Redirect(w, r, target, http.StatusTemporaryRedirect)
}) })
@ -160,7 +160,7 @@ var commands = map[string]struct {
}, },
validate: validateAbsoluteURL, validate: validateAbsoluteURL,
build: func(args any) CommandHandler { build: func(args any) CommandHandler {
target := args.(*types.URL) target := args.(*nettypes.URL)
if target.Scheme == "" { if target.Scheme == "" {
target.Scheme = "http" target.Scheme = "http"
} }

View file

@ -8,7 +8,7 @@ import (
"github.com/gobwas/glob" "github.com/gobwas/glob"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/route/routes" "github.com/yusing/go-proxy/internal/route/routes"
"github.com/yusing/go-proxy/internal/utils/strutils" "github.com/yusing/go-proxy/internal/utils/strutils"
) )
@ -205,7 +205,7 @@ var checkers = map[string]struct {
}, },
validate: validateCIDR, validate: validateCIDR,
builder: func(args any) CheckFunc { builder: func(args any) CheckFunc {
cidr := args.(types.CIDR) cidr := args.(nettypes.CIDR)
return func(cached Cache, r *http.Request) bool { return func(cached Cache, r *http.Request) bool {
ip := cached.GetRemoteIP(r) ip := cached.GetRemoteIP(r)
if ip == nil { if ip == nil {

View file

@ -10,7 +10,7 @@ import (
"github.com/gobwas/glob" "github.com/gobwas/glob"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
gphttp "github.com/yusing/go-proxy/internal/net/gphttp" gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
"github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
) )
type ( type (
@ -62,7 +62,7 @@ func validateURL(args []string) (any, gperr.Error) {
if len(args) != 1 { if len(args) != 1 {
return nil, ErrExpectOneArg return nil, ErrExpectOneArg
} }
u, err := types.ParseURL(args[0]) u, err := nettypes.ParseURL(args[0])
if err != nil { if err != nil {
return nil, ErrInvalidArguments.With(err) return nil, ErrInvalidArguments.With(err)
} }
@ -74,7 +74,7 @@ func validateAbsoluteURL(args []string) (any, gperr.Error) {
if len(args) != 1 { if len(args) != 1 {
return nil, ErrExpectOneArg return nil, ErrExpectOneArg
} }
u, err := types.ParseURL(args[0]) u, err := nettypes.ParseURL(args[0])
if err != nil { if err != nil {
return nil, ErrInvalidArguments.With(err) return nil, ErrInvalidArguments.With(err)
} }
@ -95,7 +95,7 @@ func validateCIDR(args []string) (any, gperr.Error) {
if !strings.Contains(args[0], "/") { if !strings.Contains(args[0], "/") {
args[0] += "/32" args[0] += "/32"
} }
cidr, err := types.ParseCIDR(args[0]) cidr, err := nettypes.ParseCIDR(args[0])
if err != nil { if err != nil {
return nil, ErrInvalidArguments.With(err) return nil, ErrInvalidArguments.With(err)
} }

View file

@ -2,14 +2,16 @@ package route
import ( import (
"context" "context"
"errors" "fmt"
"net"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/idlewatcher" "github.com/yusing/go-proxy/internal/idlewatcher"
net "github.com/yusing/go-proxy/internal/net/types" nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/route/routes" "github.com/yusing/go-proxy/internal/route/routes"
"github.com/yusing/go-proxy/internal/route/stream"
"github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/task"
"github.com/yusing/go-proxy/internal/watcher/health/monitor" "github.com/yusing/go-proxy/internal/watcher/health/monitor"
) )
@ -17,7 +19,7 @@ import (
// TODO: support stream load balance. // TODO: support stream load balance.
type StreamRoute struct { type StreamRoute struct {
*Route *Route
net.Stream `json:"-"` stream nettypes.Stream
l zerolog.Logger l zerolog.Logger
} }
@ -33,10 +35,19 @@ func NewStreamRoute(base *Route) (routes.Route, gperr.Error) {
}, nil }, nil
} }
func (r *StreamRoute) Stream() nettypes.Stream {
return r.stream
}
// Start implements task.TaskStarter. // Start implements task.TaskStarter.
func (r *StreamRoute) Start(parent task.Parent) gperr.Error { func (r *StreamRoute) Start(parent task.Parent) gperr.Error {
stream, err := r.initStream()
if err != nil {
return gperr.Wrap(err)
}
r.stream = stream
r.task = parent.Subtask("stream."+r.Name(), !r.ShouldExclude()) r.task = parent.Subtask("stream."+r.Name(), !r.ShouldExclude())
r.Stream = NewStream(r)
switch { switch {
case r.UseIdleWatcher(): case r.UseIdleWatcher():
@ -45,20 +56,12 @@ func (r *StreamRoute) Start(parent task.Parent) gperr.Error {
r.task.Finish(err) r.task.Finish(err)
return gperr.Wrap(err, "idlewatcher error") return gperr.Wrap(err, "idlewatcher error")
} }
r.Stream = waker r.stream = waker
r.HealthMon = waker r.HealthMon = waker
case r.UseHealthCheck(): case r.UseHealthCheck():
r.HealthMon = monitor.NewMonitor(r) r.HealthMon = monitor.NewMonitor(r)
} }
if !r.ShouldExclude() {
if err := r.Setup(); err != nil {
r.task.Finish(err)
return gperr.Wrap(err)
}
r.l.Info().Int("port", r.Port.Listening).Msg("listening")
}
if r.HealthMon != nil { if r.HealthMon != nil {
if err := r.HealthMon.Start(r.task); err != nil { if err := r.HealthMon.Start(r.task); err != nil {
gperr.LogWarn("health monitor error", err, &r.l) gperr.LogWarn("health monitor error", err, &r.l)
@ -73,7 +76,14 @@ func (r *StreamRoute) Start(parent task.Parent) gperr.Error {
return err return err
} }
go r.acceptConnections() r.ListenAndServe(r.task.Context(), nil, nil)
r.l = r.l.With().Stringer("rurl", r.ProxyURL).Stringer("laddr", r.LocalAddr()).Logger()
r.l.Info().Msg("stream started")
r.task.OnCancel("close_stream", func() {
r.stream.Close()
r.l.Info().Msg("stream closed")
})
routes.Stream.Add(r) routes.Stream.Add(r)
r.task.OnCancel("remove_route_from_stream", func() { r.task.OnCancel("remove_route_from_stream", func() {
@ -82,38 +92,34 @@ func (r *StreamRoute) Start(parent task.Parent) gperr.Error {
return nil return nil
} }
func (r *StreamRoute) acceptConnections() { func (r *StreamRoute) ListenAndServe(ctx context.Context, preDial, onRead nettypes.HookFunc) {
defer r.task.Finish("listener closed") r.stream.ListenAndServe(ctx, preDial, onRead)
}
go func() { func (r *StreamRoute) Close() error {
<-r.task.Context().Done() return r.stream.Close()
r.Close() }
}()
for { func (r *StreamRoute) LocalAddr() net.Addr {
select { return r.stream.LocalAddr()
case <-r.task.Context().Done():
return
default:
conn, err := r.Accept()
if err != nil {
select {
case <-r.task.Context().Done():
default:
gperr.LogError("accept connection error", err, &r.l)
} }
r.task.Finish(err)
return func (r *StreamRoute) initStream() (nettypes.Stream, error) {
lurl, rurl := r.LisURL, r.ProxyURL
if lurl != nil && lurl.Scheme != rurl.Scheme {
return nil, fmt.Errorf("incoherent scheme is not yet supported: %s != %s", lurl.Scheme, rurl.Scheme)
} }
if conn == nil {
panic("connection is nil") laddr := ":0"
} if lurl != nil {
go func() { laddr = lurl.Host
err := r.Handle(conn)
if err != nil && !errors.Is(err, context.Canceled) {
gperr.LogError("handle connection error", err, &r.l)
}
}()
} }
switch rurl.Scheme {
case "tcp":
return stream.NewTCPTCPStream(laddr, rurl.Host)
case "udp":
return stream.NewUDPUDPStream(laddr, rurl.Host)
} }
return nil, fmt.Errorf("unknown scheme: %s", rurl.Scheme)
} }

View file

@ -0,0 +1,12 @@
//go:build debug
package stream
import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func logDebugf(stream zerolog.LogObjectMarshaler, format string, v ...any) {
log.Debug().Object("stream", stream).Msgf(format, v...)
}

View file

@ -0,0 +1,7 @@
//go:build !debug
package stream
import "github.com/rs/zerolog"
func logDebugf(stream zerolog.LogObjectMarshaler, format string, v ...any) {}

View file

@ -0,0 +1,41 @@
package stream
import (
"context"
"errors"
"io"
"syscall"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func convertErr(err error) error {
if err == nil {
return nil
}
switch {
case errors.Is(err, context.Canceled),
errors.Is(err, io.ErrClosedPipe),
errors.Is(err, syscall.ECONNRESET):
return nil
default:
return err
}
}
func logErr(stream zerolog.LogObjectMarshaler, err error, msg string) {
err = convertErr(err)
if err == nil {
return
}
log.Err(err).Object("stream", stream).Msg(msg)
}
func logErrf(stream zerolog.LogObjectMarshaler, err error, format string, v ...any) {
err = convertErr(err)
if err == nil {
return
}
log.Err(err).Object("stream", stream).Msgf(format, v...)
}

View file

@ -0,0 +1,162 @@
package stream
import (
"context"
"net"
"github.com/rs/zerolog"
nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/utils"
"go.uber.org/atomic"
)
type TCPTCPStream struct {
listener *net.TCPListener
laddr *net.TCPAddr
dst *net.TCPAddr
preDial nettypes.HookFunc
onRead nettypes.HookFunc
closed atomic.Bool
}
func NewTCPTCPStream(listenAddr, dstAddr string) (nettypes.Stream, error) {
dst, err := net.ResolveTCPAddr("tcp", dstAddr)
if err != nil {
return nil, err
}
laddr, err := net.ResolveTCPAddr("tcp", listenAddr)
if err != nil {
return nil, err
}
return &TCPTCPStream{laddr: laddr, dst: dst}, nil
}
func (s *TCPTCPStream) ListenAndServe(ctx context.Context, preDial, onRead nettypes.HookFunc) {
listener, err := net.ListenTCP("tcp", s.laddr)
if err != nil {
logErr(s, err, "failed to listen")
return
}
s.listener = listener
s.preDial = preDial
s.onRead = onRead
go s.listen(ctx)
}
func (s *TCPTCPStream) Close() error {
if s.closed.Swap(true) || s.listener == nil {
return nil
}
return s.listener.Close()
}
func (s *TCPTCPStream) LocalAddr() net.Addr {
if s.listener == nil {
return s.laddr
}
return s.listener.Addr()
}
func (s *TCPTCPStream) MarshalZerologObject(e *zerolog.Event) {
e.Str("protocol", "tcp-tcp").Str("listen", s.listener.Addr().String()).Str("dst", s.dst.String())
}
func (s *TCPTCPStream) listen(ctx context.Context) {
for {
if s.closed.Load() {
return
}
select {
case <-ctx.Done():
return
default:
conn, err := s.listener.Accept()
if err != nil {
if s.closed.Load() {
return
}
logErr(s, err, "failed to accept connection")
continue
}
if s.onRead != nil {
if err := s.onRead(ctx); err != nil {
logErr(s, err, "failed to on read")
continue
}
}
go s.handle(ctx, conn)
}
}
}
func (s *TCPTCPStream) handle(ctx context.Context, conn net.Conn) {
defer conn.Close()
if s.preDial != nil {
if err := s.preDial(ctx); err != nil {
if !s.closed.Load() {
logErr(s, err, "failed to pre-dial")
}
return
}
}
if s.closed.Load() {
return
}
dstConn, err := net.DialTCP("tcp", nil, s.dst)
if err != nil {
if !s.closed.Load() {
logErr(s, err, "failed to dial destination")
}
return
}
defer dstConn.Close()
if s.closed.Load() {
return
}
src := conn
dst := net.Conn(dstConn)
if s.onRead != nil {
src = &wrapperConn{
Conn: conn,
ctx: ctx,
onRead: s.onRead,
}
dst = &wrapperConn{
Conn: dstConn,
ctx: ctx,
onRead: s.onRead,
}
}
pipe := utils.NewBidirectionalPipe(ctx, src, dst)
if err := pipe.Start(); err != nil && !s.closed.Load() {
logErr(s, err, "error in bidirectional pipe")
}
}
type wrapperConn struct {
net.Conn
ctx context.Context
onRead nettypes.HookFunc
}
func (w *wrapperConn) Read(b []byte) (n int, err error) {
n, err = w.Conn.Read(b)
if err != nil {
return
}
if w.onRead != nil {
if err = w.onRead(w.ctx); err != nil {
return
}
}
return
}

View file

@ -0,0 +1,316 @@
package stream
import (
"bytes"
"context"
"maps"
"net"
"sync"
"time"
"github.com/rs/zerolog"
nettypes "github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/utils/synk"
"go.uber.org/atomic"
)
type UDPUDPStream struct {
name string
listener *net.UDPConn
laddr *net.UDPAddr
dst *net.UDPAddr
preDial nettypes.HookFunc
onRead nettypes.HookFunc
cleanUpTicker *time.Ticker
conns map[string]*udpUDPConn
closed atomic.Bool
mu sync.Mutex
}
type udpUDPConn struct {
srcAddr *net.UDPAddr
dstConn *net.UDPConn
listener *net.UDPConn
lastUsed atomic.Time
closed atomic.Bool
mu sync.Mutex
}
const (
udpBufferSize = 16 * 1024
udpIdleTimeout = 5 * time.Minute // Longer timeout for game sessions
udpCleanupInterval = 1 * time.Minute
udpReadTimeout = 30 * time.Second
)
var bufPool = synk.NewBytesPool()
func NewUDPUDPStream(listenAddr, dstAddr string) (nettypes.Stream, error) {
dst, err := net.ResolveUDPAddr("udp", dstAddr)
if err != nil {
return nil, err
}
laddr, err := net.ResolveUDPAddr("udp", listenAddr)
if err != nil {
return nil, err
}
return &UDPUDPStream{
laddr: laddr,
dst: dst,
conns: make(map[string]*udpUDPConn),
}, nil
}
func (s *UDPUDPStream) ListenAndServe(ctx context.Context, preDial, onRead nettypes.HookFunc) {
listener, err := net.ListenUDP("udp", s.laddr)
if err != nil {
logErr(s, err, "failed to listen")
return
}
s.listener = listener
s.preDial = preDial
s.onRead = onRead
go s.listen(ctx)
go s.cleanUp(ctx)
}
func (s *UDPUDPStream) Close() error {
if s.closed.Swap(true) || s.listener == nil {
return nil
}
var wg sync.WaitGroup
s.mu.Lock()
for _, conn := range s.conns {
wg.Add(1)
go func(c *udpUDPConn) {
defer wg.Done()
c.Close()
}(conn)
}
clear(s.conns)
s.mu.Unlock()
wg.Wait()
return s.listener.Close()
}
func (s *UDPUDPStream) LocalAddr() net.Addr {
if s.listener == nil {
return s.laddr
}
return s.listener.LocalAddr()
}
func (s *UDPUDPStream) MarshalZerologObject(e *zerolog.Event) {
e.Str("protocol", "udp-udp").Str("name", s.name).Str("dst", s.dst.String())
}
func (s *UDPUDPStream) listen(ctx context.Context) {
buf := bufPool.GetSized(udpBufferSize)
defer bufPool.Put(buf)
for {
select {
case <-ctx.Done():
return
default:
n, srcAddr, err := s.listener.ReadFromUDP(buf)
if err != nil {
if s.closed.Load() {
return
}
logErr(s, err, "failed to read from listener")
continue
}
logDebugf(s, "read %d bytes from %s", n, srcAddr)
if s.onRead != nil {
if err := s.onRead(ctx); err != nil {
logErr(s, err, "failed to on read")
continue
}
}
// Get or create connection, passing the initial data
go s.getOrCreateConnection(ctx, srcAddr, bytes.Clone(buf[:n]))
}
}
}
func (s *UDPUDPStream) getOrCreateConnection(ctx context.Context, srcAddr *net.UDPAddr, initialData []byte) {
key := srcAddr.String()
s.mu.Lock()
if conn, ok := s.conns[key]; ok {
s.mu.Unlock()
// Forward packet for existing connection
go conn.forwardToDestination(initialData)
return
}
defer s.mu.Unlock()
// Create new connection with initial data
conn, ok := s.createConnection(ctx, srcAddr, initialData)
if ok && !conn.closed.Load() {
s.conns[key] = conn
}
}
func (s *UDPUDPStream) createConnection(ctx context.Context, srcAddr *net.UDPAddr, initialData []byte) (*udpUDPConn, bool) {
// Apply pre-dial if configured
if s.preDial != nil {
if err := s.preDial(ctx); err != nil {
logErr(s, err, "failed to pre-dial")
return nil, false
}
}
// Create UDP connection to destination
dstConn, err := net.DialUDP("udp", nil, s.dst)
if err != nil {
logErr(s, err, "failed to dial dst")
return nil, false
}
conn := &udpUDPConn{
srcAddr: srcAddr,
dstConn: dstConn,
listener: s.listener,
}
conn.lastUsed.Store(time.Now())
// Send initial data before starting response handler
if !conn.forwardToDestination(initialData) {
dstConn.Close()
return nil, false
}
// Start response handler after initial data is sent
go conn.handleResponses(ctx)
logDebugf(s, "created new connection from %s", srcAddr.String())
return conn, true
}
func (conn *udpUDPConn) MarshalZerologObject(e *zerolog.Event) {
e.Stringer("src", conn.srcAddr).Stringer("dst", conn.dstConn.RemoteAddr())
}
func (conn *udpUDPConn) handleResponses(ctx context.Context) {
buf := bufPool.GetSized(udpBufferSize)
defer bufPool.Put(buf)
defer conn.Close()
for {
if conn.closed.Load() {
return
}
select {
case <-ctx.Done():
return
default:
// Set a reasonable timeout for reads
_ = conn.dstConn.SetReadDeadline(time.Now().Add(udpReadTimeout))
n, err := conn.dstConn.Read(buf)
if err != nil {
if !conn.closed.Load() {
logErr(conn, err, "failed to read from dst")
}
return
}
// Clear deadline after successful read
_ = conn.dstConn.SetReadDeadline(time.Time{})
// Forward response back to client using the listener
_, err = conn.listener.WriteToUDP(buf[:n], conn.srcAddr)
if err != nil {
if !conn.closed.Load() {
logErrf(conn, err, "failed to write %d bytes to client", n)
}
return
}
conn.lastUsed.Store(time.Now())
logDebugf(conn, "forwarded response to client, %d bytes", n)
}
}
}
func (s *UDPUDPStream) cleanUp(ctx context.Context) {
s.cleanUpTicker = time.NewTicker(udpCleanupInterval)
defer s.cleanUpTicker.Stop()
for {
select {
case <-ctx.Done():
return
case <-s.cleanUpTicker.C:
s.mu.Lock()
conns := maps.Clone(s.conns)
s.mu.Unlock()
removed := []string(nil)
for key, conn := range conns {
if conn.Expired() {
conn.Close()
removed = append(removed, key)
}
}
s.mu.Lock()
for _, key := range removed {
logDebugf(s, "cleaning up expired connection: %s", key)
delete(s.conns, key)
}
s.mu.Unlock()
}
}
}
func (conn *udpUDPConn) forwardToDestination(data []byte) bool {
conn.mu.Lock()
defer conn.mu.Unlock()
if conn.closed.Load() {
return false
}
_, err := conn.dstConn.Write(data)
if err != nil {
logErrf(conn, err, "failed to write %d bytes to dst", len(data))
return false
}
conn.lastUsed.Store(time.Now())
logDebugf(conn, "forwarded %d bytes to dst", len(data))
return true
}
func (conn *udpUDPConn) Expired() bool {
return time.Since(conn.lastUsed.Load()) > udpIdleTimeout
}
func (conn *udpUDPConn) Close() {
conn.mu.Lock()
defer conn.mu.Unlock()
if conn.closed.Load() {
return
}
conn.closed.Store(true)
conn.dstConn.Close()
conn.dstConn = nil
}

View file

@ -1,129 +0,0 @@
package route
import (
"errors"
"fmt"
"io"
"net"
"time"
"github.com/yusing/go-proxy/internal/net/types"
U "github.com/yusing/go-proxy/internal/utils"
)
type (
Stream struct {
*StreamRoute
listener types.StreamListener
targetAddr net.Addr
}
)
const (
streamFirstConnBufferSize = 128
streamDialTimeout = 5 * time.Second
)
func NewStream(base *StreamRoute) *Stream {
return &Stream{
StreamRoute: base,
}
}
func (stream *Stream) Addr() net.Addr {
if stream.listener == nil {
panic("listener is nil")
}
return stream.listener.Addr()
}
func (stream *Stream) Setup() error {
var lcfg net.ListenConfig
var err error
ctx := stream.task.Context()
switch stream.Scheme {
case "tcp":
stream.targetAddr, err = net.ResolveTCPAddr("tcp", stream.ProxyURL.Host)
if err != nil {
return err
}
tcpListener, err := lcfg.Listen(ctx, "tcp", stream.LisURL.Host)
if err != nil {
return err
}
// in case ListeningPort was zero, get the actual port
stream.Port.Listening = tcpListener.Addr().(*net.TCPAddr).Port
stream.listener = types.NetListener(tcpListener)
case "udp":
stream.targetAddr, err = net.ResolveUDPAddr("udp", stream.ProxyURL.Host)
if err != nil {
return err
}
udpListener, err := lcfg.ListenPacket(ctx, "udp", stream.LisURL.Host)
if err != nil {
return err
}
udpConn, ok := udpListener.(*net.UDPConn)
if !ok {
udpListener.Close()
return errors.New("udp listener is not *net.UDPConn")
}
stream.Port.Listening = udpConn.LocalAddr().(*net.UDPAddr).Port
stream.listener = NewUDPForwarder(ctx, udpConn, stream.targetAddr)
default:
panic("should not reach here")
}
return nil
}
func (stream *Stream) Accept() (conn types.StreamConn, err error) {
if stream.listener == nil {
return nil, errors.New("listener is nil")
}
// prevent Accept from blocking forever
done := make(chan struct{})
go func() {
conn, err = stream.listener.Accept()
close(done)
}()
select {
case <-stream.task.Context().Done():
stream.Close()
return nil, stream.task.Context().Err()
case <-done:
return conn, nil
}
}
func (stream *Stream) Handle(conn types.StreamConn) error {
switch conn := conn.(type) {
case *UDPConn:
switch stream := stream.listener.(type) {
case *UDPForwarder:
return stream.Handle(conn)
default:
return fmt.Errorf("unexpected listener type: %T", stream)
}
case io.ReadWriteCloser:
dialer := &net.Dialer{Timeout: streamDialTimeout}
dstConn, err := dialer.DialContext(stream.task.Context(), stream.targetAddr.Network(), stream.targetAddr.String())
if err != nil {
return err
}
defer dstConn.Close()
defer conn.Close()
pipe := U.NewBidirectionalPipe(stream.task.Context(), conn, dstConn)
return pipe.Start()
default:
return fmt.Errorf("unexpected conn type: %T", conn)
}
}
func (stream *Stream) Close() error {
return stream.listener.Close()
}

View file

@ -1,204 +0,0 @@
package route
import (
"context"
"fmt"
"net"
"sync"
"github.com/rs/zerolog/log"
"github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/net/types"
F "github.com/yusing/go-proxy/internal/utils/functional"
)
type (
UDPForwarder struct {
ctx context.Context
forwarder *net.UDPConn
dstAddr net.Addr
connMap F.Map[string, *UDPConn]
mu sync.Mutex
}
UDPConn struct {
srcAddr *net.UDPAddr
conn net.Conn
buf *UDPBuf
}
UDPBuf struct {
data, oob []byte
n, oobn int
}
)
const udpConnBufferSize = 4096
func NewUDPForwarder(ctx context.Context, forwarder *net.UDPConn, dstAddr net.Addr) *UDPForwarder {
return &UDPForwarder{
ctx: ctx,
forwarder: forwarder,
dstAddr: dstAddr,
connMap: F.NewMapOf[string, *UDPConn](),
}
}
func newUDPBuf() *UDPBuf {
return &UDPBuf{
data: make([]byte, udpConnBufferSize),
oob: make([]byte, udpConnBufferSize),
}
}
func (conn *UDPConn) DstAddrString() string {
return conn.conn.RemoteAddr().Network() + "://" + conn.conn.RemoteAddr().String()
}
func (w *UDPForwarder) Addr() net.Addr {
return w.forwarder.LocalAddr()
}
func (w *UDPForwarder) Accept() (types.StreamConn, error) {
buf := newUDPBuf()
addr, err := w.readFromListener(buf)
if err != nil {
return nil, err
}
return &UDPConn{
srcAddr: addr,
buf: buf,
}, nil
}
func (w *UDPForwarder) dialDst() (dstConn net.Conn, err error) {
switch dstAddr := w.dstAddr.(type) {
case *net.UDPAddr:
var laddr *net.UDPAddr
if dstAddr.IP.IsLoopback() {
laddr, _ = net.ResolveUDPAddr(dstAddr.Network(), "127.0.0.1:")
}
dstConn, err = net.DialUDP(w.dstAddr.Network(), laddr, dstAddr)
case *net.TCPAddr:
dstConn, err = net.DialTCP(w.dstAddr.Network(), nil, dstAddr)
default:
err = fmt.Errorf("unsupported network %s", w.dstAddr.Network())
}
return
}
func (w *UDPForwarder) readFromListener(buf *UDPBuf) (srcAddr *net.UDPAddr, err error) {
buf.n, buf.oobn, _, srcAddr, err = w.forwarder.ReadMsgUDP(buf.data, buf.oob)
if err == nil {
log.Debug().Msgf("read from listener udp://%s success (n: %d, oobn: %d)", w.Addr().String(), buf.n, buf.oobn)
}
return
}
func (conn *UDPConn) read() (err error) {
switch dstConn := conn.conn.(type) {
case *net.UDPConn:
conn.buf.n, conn.buf.oobn, _, _, err = dstConn.ReadMsgUDP(conn.buf.data, conn.buf.oob)
default:
conn.buf.n, err = dstConn.Read(conn.buf.data[:conn.buf.n])
conn.buf.oobn = 0
}
if err == nil {
log.Debug().Msgf("read from dst %s success (n: %d, oobn: %d)", conn.DstAddrString(), conn.buf.n, conn.buf.oobn)
}
return
}
func (w *UDPForwarder) writeToSrc(srcAddr *net.UDPAddr, buf *UDPBuf) (err error) {
buf.n, buf.oobn, err = w.forwarder.WriteMsgUDP(buf.data[:buf.n], buf.oob[:buf.oobn], srcAddr)
if err == nil {
log.Debug().Msgf("write to src %s://%s success (n: %d, oobn: %d)", srcAddr.Network(), srcAddr.String(), buf.n, buf.oobn)
}
return
}
func (conn *UDPConn) write() (err error) {
switch dstConn := conn.conn.(type) {
case *net.UDPConn:
conn.buf.n, conn.buf.oobn, err = dstConn.WriteMsgUDP(conn.buf.data[:conn.buf.n], conn.buf.oob[:conn.buf.oobn], nil)
if err == nil {
log.Debug().Msgf("write to dst %s success (n: %d, oobn: %d)", conn.DstAddrString(), conn.buf.n, conn.buf.oobn)
}
default:
_, err = dstConn.Write(conn.buf.data[:conn.buf.n])
if err == nil {
log.Debug().Msgf("write to dst %s success (n: %d)", conn.DstAddrString(), conn.buf.n)
}
}
return
}
func (w *UDPForwarder) getInitConn(conn *UDPConn, key string) (*UDPConn, error) {
w.mu.Lock()
defer w.mu.Unlock()
dst, ok := w.connMap.Load(key)
if !ok {
var err error
dst = conn
dst.conn, err = w.dialDst()
if err != nil {
return nil, err
}
if err := dst.write(); err != nil {
dst.conn.Close()
return nil, err
}
w.connMap.Store(key, dst)
} else {
conn.conn = dst.conn
if err := conn.write(); err != nil {
w.connMap.Delete(key)
dst.conn.Close()
return nil, err
}
}
return dst, nil
}
func (w *UDPForwarder) Handle(streamConn types.StreamConn) error {
conn, ok := streamConn.(*UDPConn)
if !ok {
panic("unexpected conn type")
}
key := conn.srcAddr.String()
dst, err := w.getInitConn(conn, key)
if err != nil {
return err
}
for {
select {
case <-w.ctx.Done():
return nil
default:
if err := dst.read(); err != nil {
w.connMap.Delete(key)
dst.conn.Close()
return err
}
if err := w.writeToSrc(dst.srcAddr, dst.buf); err != nil {
return err
}
}
}
}
func (w *UDPForwarder) Close() error {
errs := gperr.NewBuilder("errors closing udp conn")
w.mu.Lock()
defer w.mu.Unlock()
w.connMap.RangeAll(func(key string, conn *UDPConn) {
errs.Add(conn.conn.Close())
})
w.connMap.Clear()
errs.Add(w.forwarder.Close())
return errs.Error()
}

View file

@ -152,20 +152,17 @@ func dive(dst reflect.Value) (v reflect.Value, t reflect.Type, err gperr.Error)
} }
dst = dst.Elem() dst = dst.Elem()
dstT = dst.Type() dstT = dst.Type()
case reflect.Struct: case reflect.Map:
if dst.IsNil() {
dst.Set(reflect.MakeMap(dstT))
}
return dst, dstT, nil
case reflect.Slice:
if dst.IsNil() {
dst.Set(reflect.MakeSlice(dstT, 0, 0))
}
return dst, dstT, nil return dst, dstT, nil
default: default:
if dst.IsNil() {
switch dst.Kind() {
case reflect.Map:
dst.Set(reflect.MakeMap(dstT))
case reflect.Slice:
dst.Set(reflect.MakeSlice(dstT, 0, 0))
default:
err = gperr.Errorf("deserialize: %w for dst %s", ErrInvalidType, dstT.String())
return
}
}
return dst, dstT, nil return dst, dstT, nil
} }
} }

View file

@ -85,7 +85,7 @@ func WaitExit(shutdownTimeout int) {
// still running when the timeout was reached, and their current tree // still running when the timeout was reached, and their current tree
// of subtasks. // of subtasks.
func gracefulShutdown(timeout time.Duration) error { func gracefulShutdown(timeout time.Duration) error {
go root.Finish(ErrProgramExiting) root.Finish(ErrProgramExiting)
if !root.waitFinish(timeout) { if !root.waitFinish(timeout) {
return context.DeadlineExceeded return context.DeadlineExceeded
} }

View file

@ -42,7 +42,7 @@ var ErrNegativeInterval = gperr.New("negative interval")
func NewMonitor(r routes.Route) health.HealthMonCheck { func NewMonitor(r routes.Route) health.HealthMonCheck {
var mon health.HealthMonCheck var mon health.HealthMonCheck
if r.IsAgent() { if r.IsAgent() {
mon = NewAgentProxiedMonitor(r.Agent(), r.HealthCheckConfig(), AgentTargetFromURL(&r.TargetURL().URL)) mon = NewAgentProxiedMonitor(r.GetAgent(), r.HealthCheckConfig(), AgentTargetFromURL(&r.TargetURL().URL))
} else { } else {
switch r := r.(type) { switch r := r.(type) {
case routes.HTTPRoute: case routes.HTTPRoute:

View file

@ -47,7 +47,6 @@ echo "Using ${DOWNLOAD_TOOL} for downloads"
REPO="yusing/godoxy" REPO="yusing/godoxy"
BRANCH=${BRANCH:-"main"} BRANCH=${BRANCH:-"main"}
REPO_URL="https://github.com/$REPO" REPO_URL="https://github.com/$REPO"
WIKI_URL="${REPO_URL}/wiki"
BASE_URL="${REPO_URL}/raw/${BRANCH}" BASE_URL="${REPO_URL}/raw/${BRANCH}"
# Config paths # Config paths
@ -248,7 +247,7 @@ if [ "$ENABLE_AUTOCERT" == "y" ]; then
read -p "Enter duckdns token: " token read -p "Enter duckdns token: " token
options=("token: \"$token\"") options=("token: \"$token\"")
else else
echo "Please check Wiki for other DNS providers: ${WIKI_URL}/Supported-DNS%E2%80%9001-Providers" echo "Please check Wiki for other DNS providers: https://docs.godoxy.dev/DNS-01-Providers"
echo "Skipping autocert setup" echo "Skipping autocert setup"
skip=true skip=true
fi fi

View file

@ -6,7 +6,7 @@ replace github.com/yusing/go-proxy/internal/utils => ../internal/utils
require ( require (
github.com/gorilla/mux v1.8.1 github.com/gorilla/mux v1.8.1
github.com/yusing/go-proxy/internal/utils v0.0.0-20250605105311-09c244ef3cdb github.com/yusing/go-proxy/internal/utils v0.0.0-20250612133450-880d66c75e3f
golang.org/x/net v0.41.0 golang.org/x/net v0.41.0
) )