mirror of
https://github.com/yusing/godoxy.git
synced 2025-06-15 06:26:47 +02:00
Compare commits
18 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f179de9231 | ||
![]() |
1d546624de | ||
![]() |
ecc9d306d1 | ||
![]() |
5ce1c7865e | ||
![]() |
7d17a01de1 | ||
![]() |
cabb840a91 | ||
![]() |
4825f768f3 | ||
![]() |
5fdb023188 | ||
![]() |
4abf61a421 | ||
![]() |
96b7c3fcec | ||
![]() |
f8c57d930f | ||
![]() |
880d66c75e | ||
![]() |
4649c8d479 | ||
![]() |
20021b3cae | ||
![]() |
cfa9201f82 | ||
![]() |
b5328fe5e7 | ||
![]() |
25fbcc4ab9 | ||
![]() |
421aaecba4 |
70 changed files with 1401 additions and 831 deletions
|
@ -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=
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -30,6 +30,7 @@ todo.md
|
||||||
mtrace.json
|
mtrace.json
|
||||||
.env
|
.env
|
||||||
.cursorrules
|
.cursorrules
|
||||||
|
.cursor/
|
||||||
.windsurfrules
|
.windsurfrules
|
||||||
test.Dockerfile
|
test.Dockerfile
|
||||||
|
|
||||||
|
@ -37,4 +38,4 @@ node_modules/
|
||||||
tsconfig.tsbuildinfo
|
tsconfig.tsbuildinfo
|
||||||
|
|
||||||
!agent.compose.yml
|
!agent.compose.yml
|
||||||
!agent/pkg/**
|
!agent/pkg/**
|
||||||
|
|
|
@ -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
|
||||||
|
|
16
agent/go.sum
16
agent/go.sum
|
@ -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=
|
||||||
|
|
57
agent/pkg/agent/agent_pool.go
Normal file
57
agent/pkg/agent/agent_pool.go
Normal 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
|
||||||
|
}
|
|
@ -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
48
go.mod
|
@ -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
86
go.sum
|
@ -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=
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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()))
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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=
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
34
internal/docker/errors.go
Normal 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()))
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
}}}
|
}}}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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,8 +17,8 @@ type (
|
||||||
cachedAddr F.Map[string, bool] // cache for trusted IPs
|
cachedAddr F.Map[string, bool] // cache for trusted IPs
|
||||||
}
|
}
|
||||||
CIDRWhitelistOpts struct {
|
CIDRWhitelistOpts struct {
|
||||||
Allow []*types.CIDR `validate:"min=1"`
|
Allow []*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",
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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{},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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{}
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package types
|
package nettypes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
|
@ -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
|
Close() error
|
||||||
Setup() error
|
|
||||||
Handle(conn StreamConn) error
|
|
||||||
}
|
|
||||||
StreamListener interface {
|
|
||||||
Addr() net.Addr
|
|
||||||
Accept() (StreamConn, error)
|
|
||||||
Close() error
|
|
||||||
}
|
|
||||||
StreamConn any
|
|
||||||
NetListenerWrapper struct {
|
|
||||||
net.Listener
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func NetListener(l net.Listener) StreamListener {
|
|
||||||
return NetListenerWrapper{Listener: l}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l NetListenerWrapper) Accept() (StreamConn, error) {
|
type HookFunc func(ctx context.Context) error
|
||||||
return l.Listener.Accept()
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package types
|
package nettypes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
urlPkg "net/url"
|
urlPkg "net/url"
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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")
|
||||||
}
|
}
|
||||||
return errs.Error()
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
|
33
internal/notif/retry_message.go
Normal file
33
internal/notif/retry_message.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,13 +197,15 @@ 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
|
||||||
switch r.Host {
|
if !r.IsAgent() {
|
||||||
case "localhost", "127.0.0.1":
|
switch r.Host {
|
||||||
switch r.Port.Proxy {
|
case "localhost", "127.0.0.1":
|
||||||
case common.ProxyHTTPPort, common.ProxyHTTPSPort, common.APIHTTPPort:
|
switch r.Port.Proxy {
|
||||||
if r.Scheme.IsReverseProxy() || r.Scheme == route.SchemeTCP {
|
case common.ProxyHTTPPort, common.ProxyHTTPSPort, common.APIHTTPPort:
|
||||||
return gperr.Errorf("localhost:%d is reserved for godoxy", r.Port.Proxy)
|
if r.Scheme.IsReverseProxy() || r.Scheme == route.SchemeTCP {
|
||||||
|
return gperr.Errorf("localhost:%d is reserved for godoxy", r.Port.Proxy)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
|
||||||
<-r.task.Context().Done()
|
func (r *StreamRoute) Close() error {
|
||||||
r.Close()
|
return r.stream.Close()
|
||||||
}()
|
}
|
||||||
|
|
||||||
for {
|
func (r *StreamRoute) LocalAddr() net.Addr {
|
||||||
select {
|
return r.stream.LocalAddr()
|
||||||
case <-r.task.Context().Done():
|
}
|
||||||
return
|
|
||||||
default:
|
func (r *StreamRoute) initStream() (nettypes.Stream, error) {
|
||||||
conn, err := r.Accept()
|
lurl, rurl := r.LisURL, r.ProxyURL
|
||||||
if err != nil {
|
if lurl != nil && lurl.Scheme != rurl.Scheme {
|
||||||
select {
|
return nil, fmt.Errorf("incoherent scheme is not yet supported: %s != %s", lurl.Scheme, rurl.Scheme)
|
||||||
case <-r.task.Context().Done():
|
}
|
||||||
default:
|
|
||||||
gperr.LogError("accept connection error", err, &r.l)
|
laddr := ":0"
|
||||||
}
|
if lurl != nil {
|
||||||
r.task.Finish(err)
|
laddr = lurl.Host
|
||||||
return
|
}
|
||||||
}
|
|
||||||
if conn == nil {
|
switch rurl.Scheme {
|
||||||
panic("connection is nil")
|
case "tcp":
|
||||||
}
|
return stream.NewTCPTCPStream(laddr, rurl.Host)
|
||||||
go func() {
|
case "udp":
|
||||||
err := r.Handle(conn)
|
return stream.NewUDPUDPStream(laddr, rurl.Host)
|
||||||
if err != nil && !errors.Is(err, context.Canceled) {
|
}
|
||||||
gperr.LogError("handle connection error", err, &r.l)
|
return nil, fmt.Errorf("unknown scheme: %s", rurl.Scheme)
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
12
internal/route/stream/debug_debug.go
Normal file
12
internal/route/stream/debug_debug.go
Normal 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...)
|
||||||
|
}
|
7
internal/route/stream/debug_prod.go
Normal file
7
internal/route/stream/debug_prod.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
//go:build !debug
|
||||||
|
|
||||||
|
package stream
|
||||||
|
|
||||||
|
import "github.com/rs/zerolog"
|
||||||
|
|
||||||
|
func logDebugf(stream zerolog.LogObjectMarshaler, format string, v ...any) {}
|
41
internal/route/stream/errors.go
Normal file
41
internal/route/stream/errors.go
Normal 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...)
|
||||||
|
}
|
162
internal/route/stream/tcp_tcp.go
Normal file
162
internal/route/stream/tcp_tcp.go
Normal 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
|
||||||
|
}
|
316
internal/route/stream/udp_udp.go
Normal file
316
internal/route/stream/udp_udp.go
Normal 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
|
||||||
|
}
|
|
@ -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()
|
|
||||||
}
|
|
|
@ -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()
|
|
||||||
}
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue