From 1b8f961d075c6ace2ed880a98dbe907a3f0ee6d6 Mon Sep 17 00:00:00 2001 From: Ben Sarmiento Date: Sat, 25 Nov 2023 15:51:11 +0100 Subject: [PATCH] Force ipv6 setting --- cmd/zurg/main.go | 2 +- config.example.yml | 3 +++ healthcheck.sh | 2 +- internal/config/types.go | 6 ++++++ internal/universal/get.go | 4 +++- pkg/http/client.go | 42 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 56 insertions(+), 3 deletions(-) diff --git a/cmd/zurg/main.go b/cmd/zurg/main.go index 4fed888..20afd0a 100644 --- a/cmd/zurg/main.go +++ b/cmd/zurg/main.go @@ -42,7 +42,7 @@ func main() { cache := expirable.NewLRU[string, string](1e4, nil, time.Hour) - client := zurghttp.NewHTTPClient(config.GetToken(), 5, nil) + client := zurghttp.NewHTTPClient(config.GetToken(), 5, config) rd := realdebrid.NewRealDebrid(config.GetToken(), client, logutil.NewLogger().Named("realdebrid")) diff --git a/config.example.yml b/config.example.yml index 241f8f3..ba8232e 100644 --- a/config.example.yml +++ b/config.example.yml @@ -10,6 +10,7 @@ check_for_changes_every_secs: 15 enable_repair: true # BEWARE! THERE CAN ONLY BE 1 INSTANCE OF ZURG THAT SHOULD REPAIR YOUR TORRENTS retain_folder_name_extension: false # if true, zurg won't modify the filenames from real-debrid +retain_rd_torrent_name: false # if true, it will strictly follow RD API returned torrent name which should make this more compatible with rdt-client on_library_update: | for arg in "$@" do @@ -17,6 +18,8 @@ on_library_update: | done network_buffer_size: 1048576 # 1 MiB +serve_from_rclone: false +force_ipv6: true # preferred_hosts: # Run the script here https://github.com/debridmediamanager/real-debrid-network-test # - 20.download.real-debrid.com # - 21.download.real-debrid.com diff --git a/healthcheck.sh b/healthcheck.sh index 56daf0d..f8f8dc7 100644 --- a/healthcheck.sh +++ b/healthcheck.sh @@ -1,5 +1,5 @@ #!/bin/sh -response=$(curl -o /dev/null -s -w "%{http_code}" http://localhost:9999) +response=$(curl -o /dev/null -s -w "%{http_code}" http://zen.box:9999/__all__/) if [ "$response" -eq 200 ]; then exit 0 else diff --git a/internal/config/types.go b/internal/config/types.go index eca4ad5..0734aee 100644 --- a/internal/config/types.go +++ b/internal/config/types.go @@ -19,6 +19,7 @@ type ConfigInterface interface { EnableRetainRDTorrentName() bool GetRandomPreferredHost() string ShouldServeFromRclone() bool + ShouldForceIPv6() bool } type ZurgConfig struct { @@ -36,6 +37,7 @@ type ZurgConfig struct { RetainRDTorrentName bool `yaml:"retain_rd_torrent_name"` PreferredHosts []string `yaml:"preferred_hosts"` ServeFromRclone bool `yaml:"serve_from_rclone"` + ForceIPv6 bool `yaml:"force_ipv6"` } func (z *ZurgConfig) GetToken() string { @@ -111,3 +113,7 @@ func (z *ZurgConfig) GetRandomPreferredHost() string { func (z *ZurgConfig) ShouldServeFromRclone() bool { return z.ServeFromRclone } + +func (z *ZurgConfig) ShouldForceIPv6() bool { + return z.ForceIPv6 +} diff --git a/internal/universal/get.go b/internal/universal/get.go index c814e6f..7a1a06e 100644 --- a/internal/universal/get.go +++ b/internal/universal/get.go @@ -117,6 +117,7 @@ func (gf *GetFile) HandleGetRequest(w http.ResponseWriter, r *http.Request, t *i } else { gf.streamFileToResponse(file, resp.Download, w, r, t, c, log) } + return } } @@ -176,8 +177,9 @@ func (gf *GetFile) playErrorVideo(link string, w http.ResponseWriter, r *http.Re } if c.ShouldServeFromRclone() { redirect(w, r, resp.Download, c) + } else { + gf.streamFileToResponse(nil, resp.Download, w, r, t, c, log) } - gf.streamFileToResponse(nil, resp.Download, w, r, t, c, log) } func redirect(w http.ResponseWriter, r *http.Request, url string, c config.ConfigInterface) { diff --git a/pkg/http/client.go b/pkg/http/client.go index b06a1d2..95e7901 100644 --- a/pkg/http/client.go +++ b/pkg/http/client.go @@ -1,12 +1,15 @@ package http import ( + "context" + "net" "net/http" "strings" "time" "github.com/debridmediamanager.com/zurg/internal/config" "github.com/debridmediamanager.com/zurg/pkg/logutil" + cmap "github.com/orcaman/concurrent-map/v2" "go.uber.org/zap" ) @@ -18,6 +21,7 @@ type HTTPClient struct { BearerToken string log *zap.SugaredLogger config config.ConfigInterface + IPv6 cmap.ConcurrentMap[string, net.IP] } func (r *HTTPClient) Do(req *http.Request) (*http.Response, error) { @@ -30,6 +34,43 @@ func (r *HTTPClient) Do(req *http.Request) (*http.Response, error) { if r.BearerToken != "" { req.Header.Set("Authorization", "Bearer "+r.BearerToken) } + + if r.config.ShouldForceIPv6() { + dialer := &net.Dialer{} + transport := &http.Transport{ + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + host, _, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + if ipv6, ok := r.IPv6.Get(host); !ok { + // Lookup IP address if not found in map + ips, err := net.LookupIP(host) + if err != nil { + return nil, err + } + for _, ip := range ips { + if ip.To4() == nil { // IPv6 + ipv6 = ip + r.IPv6.Set(host, ipv6) + break + } + } + if ipv6 == nil { // No IPv6 address found, fallback to default + r.log.Warnf("No IPv6 address found for %s, falling back to default", host) + addr = net.JoinHostPort(host, req.URL.Port()) + } else { + addr = net.JoinHostPort(ipv6.String(), req.URL.Port()) + } + } else if ipv6 != nil && host == req.URL.Hostname() { + addr = net.JoinHostPort(ipv6.String(), req.URL.Port()) + } + return dialer.DialContext(ctx, network, addr) + }, + } + r.Client.Transport = transport + } + var resp *http.Response var err error for attempt := 0; attempt < r.MaxRetries; attempt++ { @@ -62,5 +103,6 @@ func NewHTTPClient(token string, maxRetries int, cfg config.ConfigInterface) *HT }, log: logutil.NewLogger().Named("client"), config: cfg, + IPv6: cmap.New[net.IP](), } }