Support network proxies

This commit is contained in:
Ben Sarmiento
2024-01-11 04:15:40 +01:00
parent dfa3ef99b0
commit b2e02c7e1f
5 changed files with 66 additions and 34 deletions

View File

@@ -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

1
go.mod
View File

@@ -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 (

2
go.sum
View File

@@ -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=

View File

@@ -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

View File

@@ -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
}