🏡 index : quine-global/quintodrome.git

package httpclient

import (
	"net/http"
	"net/http/httptest"
	"net/url"
	"sync/atomic"
	"time"

	"github.com/navidrome/navidrome/utils/netguard"
	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
)

var _ = Describe("NewExternal with a proxy", func() {
	var proxy *httptest.Server
	var proxied atomic.Int32
	var proxyURL *url.URL

	BeforeEach(func() {
		proxied.Store(0)
		proxy = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
			proxied.Add(1)
			_, _ = w.Write([]byte("via proxy"))
		}))
		DeferCleanup(proxy.Close)
		proxyURL, _ = url.Parse(proxy.URL)
		prev := proxyFunc
		DeferCleanup(func() { proxyFunc = prev })
	})

	It("dials the configured proxy even though it listens on a private address", func() {
		proxyFunc = func(*http.Request) (*url.URL, error) { return proxyURL, nil }

		resp, err := NewExternal(time.Second).Get("http://navidrome.example.com/cover.jpg")
		Expect(err).ToNot(HaveOccurred())
		defer resp.Body.Close()
		Expect(proxied.Load()).To(Equal(int32(1)))
	})

	// net/http never proxies loopback targets, so a URL aimed at a loopback proxy is dialed directly.
	It("refuses a direct dial to the proxy's own address when the request is not proxied", func() {
		proxyFunc = func(r *http.Request) (*url.URL, error) {
			if r.URL.Hostname() == "127.0.0.1" {
				return nil, nil
			}
			return proxyURL, nil
		}

		_, err := NewExternal(time.Second).Get(proxy.URL + "/secret")
		Expect(err).To(MatchError(netguard.ErrPrivateAddress))
		Expect(proxied.Load()).To(BeZero())
	})

	It("still refuses a direct dial to a private address", func() {
		proxyFunc = func(r *http.Request) (*url.URL, error) {
			if r.URL.Scheme == "https" {
				return proxyURL, nil
			}
			return nil, nil
		}
		target := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}))
		DeferCleanup(target.Close)

		_, err := NewExternal(time.Second).Get(target.URL)
		Expect(err).To(MatchError(netguard.ErrPrivateAddress))
	})
})