Skip to content

Commit a8226a6

Browse files
authored
namecheap: add experimental proxy support (#2715)
1 parent b338263 commit a8226a6

File tree

5 files changed

+120
-6
lines changed

5 files changed

+120
-6
lines changed

.golangci.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,18 +216,18 @@ linters:
216216
text: load is a global variable
217217
linters:
218218
- gochecknoglobals
219-
- path: providers/dns/([\d\w]+/)*[\d\w]+_test.go
220-
text: envTest is a global variable
221-
linters:
222-
- gochecknoglobals
223-
- path: providers/http/([\d\w]+/)*[\d\w]+_test.go
219+
- path: providers/(dns|http)/([\d\w]+/)*[\d\w]+_test.go
224220
text: envTest is a global variable
225221
linters:
226222
- gochecknoglobals
227223
- path: providers/dns/namecheap/namecheap_test.go
228224
text: testCases is a global variable
229225
linters:
230226
- gochecknoglobals
227+
- path: providers/dns/namecheap/transport.go
228+
text: (envProxyOnce|envProxyFuncValue) is a global variable
229+
linters:
230+
- gochecknoglobals
231231
- path: providers/dns/acmedns/mock_test.go
232232
text: egTestAccount is a global variable
233233
linters:

providers/dns/internal/clientdebug/client.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ func (d *DumpTransport) RoundTrip(h *http.Request) (*http.Response, error) {
9191
_, _ = fmt.Fprintln(d.writer, d.redact(data))
9292

9393
resp, err := d.rt.RoundTrip(h)
94+
if err != nil {
95+
return nil, err
96+
}
9497

9598
data, _ = httputil.DumpResponse(resp, true)
9699

providers/dns/namecheap/namecheap.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ func NewDefaultConfig() *Config {
7676
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, time.Hour),
7777
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, 15*time.Second),
7878
HTTPClient: &http.Client{
79-
Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, time.Minute),
79+
Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, time.Minute),
80+
Transport: defaultTransport(envNamespace),
8081
},
8182
}
8283
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package namecheap
2+
3+
import (
4+
"net/http"
5+
"net/url"
6+
"strings"
7+
"sync"
8+
9+
"github.com/go-acme/lego/v4/platform/config/env"
10+
"golang.org/x/net/http/httpproxy"
11+
)
12+
13+
const (
14+
envHTTPProxy = "HTTP_PROXY"
15+
envHTTPProxyLower = "http_proxy"
16+
envHTTPSProxy = "HTTPS_PROXY"
17+
envHTTPSProxyLower = "https_proxy"
18+
envNoProxy = "NO_PROXY"
19+
envNoProxyLower = "no_proxy"
20+
envRequestMethod = "REQUEST_METHOD"
21+
)
22+
23+
// Allows lazy loading of the proxy.
24+
var (
25+
envProxyOnce sync.Once
26+
envProxyFuncValue func(*url.URL) (*url.URL, error)
27+
)
28+
29+
func defaultTransport(namespace string) http.RoundTripper {
30+
tr, ok := http.DefaultTransport.(*http.Transport)
31+
if !ok {
32+
return nil
33+
}
34+
35+
clone := tr.Clone()
36+
clone.Proxy = proxyFromEnvironment(namespace)
37+
38+
return clone
39+
}
40+
41+
// Inspired by:
42+
// - https://pkg.go.dev/net/http#ProxyFromEnvironment
43+
// - https://pkg.go.dev/golang.org/x/net/http/httpproxy#FromEnvironment
44+
func envProxyFunc(namespace string) func(*url.URL) (*url.URL, error) {
45+
envProxyOnce.Do(func() {
46+
cfg := &httpproxy.Config{
47+
HTTPProxy: getEnv(namespace, envHTTPProxy, envHTTPProxyLower),
48+
HTTPSProxy: getEnv(namespace, envHTTPSProxy, envHTTPSProxyLower),
49+
NoProxy: getEnv(namespace, envNoProxy, envNoProxyLower),
50+
CGI: env.GetOneWithFallback(namespace+envRequestMethod, "", env.ParseString, envRequestMethod) != "",
51+
}
52+
53+
envProxyFuncValue = cfg.ProxyFunc()
54+
})
55+
56+
return envProxyFuncValue
57+
}
58+
59+
// Inspired by:
60+
// - https://pkg.go.dev/net/http#ProxyFromEnvironment
61+
// - https://pkg.go.dev/golang.org/x/net/http/httpproxy#FromEnvironment
62+
func proxyFromEnvironment(namespace string) func(req *http.Request) (*url.URL, error) {
63+
return func(req *http.Request) (*url.URL, error) {
64+
return envProxyFunc(namespace)(req.URL)
65+
}
66+
}
67+
68+
func getEnv(namespace, baseEnvName, baseEnvNameLower string) string {
69+
return env.GetOneWithFallback(namespace+baseEnvName, "", env.ParseString,
70+
strings.ToLower(namespace)+baseEnvNameLower, baseEnvName, baseEnvNameLower)
71+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package namecheap
2+
3+
import (
4+
"net/http"
5+
"net/http/httptest"
6+
"testing"
7+
8+
"github.com/go-acme/lego/v4/platform/tester/servermock"
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
func Test_defaultTransport(t *testing.T) {
14+
client := servermock.NewBuilder(
15+
func(server *httptest.Server) (*http.Client, error) {
16+
cl := server.Client()
17+
18+
t.Setenv("NAMECHEAP_HTTP_PROXY", server.URL)
19+
20+
cl.Transport = defaultTransport(envNamespace)
21+
22+
return cl, nil
23+
}).
24+
Route("/",
25+
servermock.Noop().WithStatusCode(http.StatusTeapot)).
26+
Build(t)
27+
28+
req, err := http.NewRequest(http.MethodGet, "http://example.com", nil)
29+
require.NoError(t, err)
30+
31+
resp, err := client.Do(req)
32+
require.NoError(t, err)
33+
34+
t.Cleanup(func() {
35+
_ = resp.Body.Close()
36+
})
37+
38+
assert.Equal(t, http.StatusTeapot, resp.StatusCode)
39+
}

0 commit comments

Comments
 (0)