From b2e02c7e1f1531b589f0e9f33deb40fe6297b684 Mon Sep 17 00:00:00 2001 From: Ben Sarmiento Date: Thu, 11 Jan 2024 04:15:40 +0100 Subject: [PATCH] Support network proxies --- config.example.yml | 6 +++ go.mod | 1 + go.sum | 2 + internal/config/types.go | 6 +++ pkg/http/client.go | 85 ++++++++++++++++++++++++---------------- 5 files changed, 66 insertions(+), 34 deletions(-) diff --git a/config.example.yml b/config.example.yml index 4ea0c92..9601b62 100644 --- a/config.example.yml +++ b/config.example.yml @@ -10,6 +10,12 @@ port: 9999 # username: yowmamasita # password: 1234 +# You can proxy all zurg requests like this: +# proxy: "http://[username:password@]host:port" +# proxy: "https://[username:password@]host:port" +# proxy: "socks4://[username@]host:port" +# proxy: "socks5://[username:password@]host:port" + # How many requests in parallel should we send to Real-Debrid API? concurrent_workers: 20 diff --git a/go.mod b/go.mod index c4e3638..d8aca44 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/go-chi/chi/v5 v5.0.11 // indirect github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + golang.org/x/net v0.20.0 // indirect ) require ( diff --git a/go.sum b/go.sum index 145b70e..2cb773f 100644 --- a/go.sum +++ b/go.sum @@ -42,6 +42,8 @@ go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/internal/config/types.go b/internal/config/types.go index bd10cfd..a9c1ec2 100644 --- a/internal/config/types.go +++ b/internal/config/types.go @@ -13,6 +13,7 @@ type ConfigInterface interface { GetPort() string GetUsername() string GetPassword() string + GetProxy() string GetDirectories() []string MeetsConditions(directory, torrentName string, torrentSize int64, torrentIDs, fileNames []string, fileSizes []int64) bool GetOnLibraryUpdate() string @@ -39,6 +40,7 @@ type ZurgConfig struct { Port string `yaml:"port" json:"port"` Username string `yaml:"username" json:"username"` Password string `yaml:"password" json:"password"` + Proxy string `yaml:"proxy" json:"proxy"` NumOfWorkers int `yaml:"concurrent_workers" json:"concurrent_workers"` RefreshEverySeconds int `yaml:"check_for_changes_every_secs" json:"check_for_changes_every_secs"` @@ -92,6 +94,10 @@ func (z *ZurgConfig) GetPassword() string { return z.Password } +func (z *ZurgConfig) GetProxy() string { + return z.Proxy +} + func (z *ZurgConfig) GetNumOfWorkers() int { if z.NumOfWorkers == 0 { return 50 diff --git a/pkg/http/client.go b/pkg/http/client.go index 3cccccd..e9f0afc 100644 --- a/pkg/http/client.go +++ b/pkg/http/client.go @@ -9,12 +9,14 @@ import ( "math/rand" "net" "net/http" + "net/url" "strings" "time" "github.com/debridmediamanager/zurg/internal/config" "github.com/debridmediamanager/zurg/pkg/hosts" "github.com/debridmediamanager/zurg/pkg/logutil" + "golang.org/x/net/proxy" cmap "github.com/orcaman/concurrent-map/v2" ) @@ -110,46 +112,61 @@ func NewHTTPClient(token string, maxRetries int, timeoutSecs int, ensureIPv6Host log: log, } - if !cfg.ShouldForceIPv6() { - return &client + var dialer proxy.Dialer = &net.Dialer{} + if proxyURLString := cfg.GetProxy(); proxyURLString != "" { + proxyURL, err := url.Parse(proxyURLString) + if err != nil { + log.Errorf("Failed to parse proxy URL: %v", err) + return nil + } + proxyDialer, err := proxy.FromURL(proxyURL, dialer) + if err != nil { + log.Errorf("Failed to create proxy dialer: %v", err) + return nil + } + dialer = proxyDialer } - // set ipv6 hosts - ipv6List, err := hosts.FetchHosts(hosts.IPV6) - if err != nil { - log.Warnf("Failed to fetch IPv6 hosts: %v", err) - } - client.ipv6Hosts = ipv6List - log.Debugf("Fetched %d IPv6 hosts", len(ipv6List)) + if cfg.ShouldForceIPv6() { + ipv6List, err := hosts.FetchHosts(hosts.IPV6) + if err != nil { + log.Warnf("Failed to fetch IPv6 hosts: %v", err) + // Decide if you want to return nil here or continue without IPv6 + } else { + client.ipv6Hosts = ipv6List + log.Debugf("Fetched %d IPv6 hosts", len(ipv6List)) + } - // set ipv6 transport - dialer := &net.Dialer{} - dialContext := func(ctx context.Context, network, address string) (net.Conn, error) { - if ipv6Address, ok := client.ipv6.Get(address); ok { - return dialer.DialContext(ctx, network, ipv6Address) + client.client.Transport = &http.Transport{ + DialContext: func(ctx context.Context, network, address string) (net.Conn, error) { + if ipv6Address, ok := client.ipv6.Get(address); ok { + return dialer.Dial(network, ipv6Address) + } + host, port, err := net.SplitHostPort(address) + if err != nil { + return nil, err + } + ips, err := net.DefaultResolver.LookupIPAddr(ctx, host) + if err != nil { + return nil, err + } + for _, ip := range ips { + if ip.IP.To4() == nil { // IPv6 address found + ipv6Address := net.JoinHostPort(ip.IP.String(), port) + client.ipv6.Set(address, ipv6Address) + return dialer.Dial(network, ipv6Address) + } + } + return dialer.Dial(network, address) + }, } - host, port, err := net.SplitHostPort(address) - if err != nil { - return nil, err + } else { + client.client.Transport = &http.Transport{ + DialContext: func(ctx context.Context, network, address string) (net.Conn, error) { + return dialer.Dial(network, address) + }, } - ips, err := net.DefaultResolver.LookupIPAddr(ctx, host) - if err != nil { - return nil, err - } - for _, ip := range ips { - if ip.IP.To4() == nil { // IPv6 address found - ip6Host := ip.IP.String() - ipv6Address := net.JoinHostPort(ip6Host, port) - client.ipv6.Set(address, ipv6Address) - return dialer.DialContext(ctx, network, ipv6Address) - } - } - return dialer.DialContext(ctx, network, address) } - transport := &http.Transport{ - DialContext: dialContext, - } - client.client.Transport = transport return &client }