Skip to content

Commit 92fea7e

Browse files
committed
ftp: add --ftp-http-proxy to connect via HTTP CONNECT proxy
1 parent f226d12 commit 92fea7e

1 file changed

Lines changed: 35 additions & 1 deletion

File tree

backend/ftp/ftp.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"io"
1010
"net"
1111
"net/textproto"
12+
"net/url"
1213
"path"
1314
"runtime"
1415
"strings"
@@ -185,6 +186,14 @@ Supports the format user:pass@host:port, user@host:port, host:port.
185186
Example:
186187
187188
myUser:myPass@localhost:9005
189+
`,
190+
Advanced: true,
191+
}, {
192+
Name: "http_proxy",
193+
Default: "",
194+
Help: `URL for HTTP CONNECT proxy
195+
196+
Set this to a URL for an HTTP proxy which supports the HTTP CONNECT verb.
188197
`,
189198
Advanced: true,
190199
}, {
@@ -248,6 +257,7 @@ type Options struct {
248257
AskPassword bool `config:"ask_password"`
249258
Enc encoder.MultiEncoder `config:"encoding"`
250259
SocksProxy string `config:"socks_proxy"`
260+
HTTPProxy string `config:"http_proxy"`
251261
NoCheckUpload bool `config:"no_check_upload"`
252262
}
253263

@@ -266,6 +276,7 @@ type Fs struct {
266276
pool []*ftp.ServerConn
267277
drain *time.Timer // used to drain the pool when we stop using the connections
268278
tokens *pacer.TokenDispenser
279+
proxyURL *url.URL // address of HTTP proxy read from environment
269280
pacer *fs.Pacer // pacer for FTP connections
270281
fGetTime bool // true if the ftp library accepts GetTime
271282
fSetTime bool // true if the ftp library accepts SetTime
@@ -413,11 +424,26 @@ func (f *Fs) ftpConnection(ctx context.Context) (c *ftp.ServerConn, err error) {
413424
dial := func(network, address string) (conn net.Conn, err error) {
414425
fs.Debugf(f, "dial(%q,%q)", network, address)
415426
defer func() {
416-
fs.Debugf(f, "> dial: conn=%T, err=%v", conn, err)
427+
if err != nil {
428+
fs.Debugf(f, "> dial: conn=%v, err=%v", conn, err)
429+
} else {
430+
fs.Debugf(f, "> dial: conn=%s->%s, err=%v", conn.LocalAddr(), conn.RemoteAddr(), err)
431+
}
417432
}()
418433
baseDialer := fshttp.NewDialer(ctx)
419434
if f.opt.SocksProxy != "" {
420435
conn, err = proxy.SOCKS5Dial(network, address, f.opt.SocksProxy, baseDialer)
436+
} else if f.proxyURL != nil {
437+
// We need to make the onward connection to f.opt.Host. However the FTP
438+
// library sets the host to the proxy IP after using EPSV or PASV so we need
439+
// to correct that here.
440+
var dialPort string
441+
_, dialPort, err = net.SplitHostPort(address)
442+
if err != nil {
443+
return nil, err
444+
}
445+
dialAddress := net.JoinHostPort(f.opt.Host, dialPort)
446+
conn, err = proxy.HTTPConnectDial(network, dialAddress, f.proxyURL, baseDialer)
421447
} else {
422448
conn, err = baseDialer.Dial(network, address)
423449
}
@@ -631,6 +657,14 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (ff fs.Fs
631657
CanHaveEmptyDirectories: true,
632658
PartialUploads: true,
633659
}).Fill(ctx, f)
660+
// get proxy URL if set
661+
if opt.HTTPProxy != "" {
662+
proxyURL, err := url.Parse(opt.HTTPProxy)
663+
if err != nil {
664+
return nil, fmt.Errorf("failed to parse HTTP Proxy URL: %w", err)
665+
}
666+
f.proxyURL = proxyURL
667+
}
634668
// set the pool drainer timer going
635669
if f.opt.IdleTimeout > 0 {
636670
f.drain = time.AfterFunc(time.Duration(opt.IdleTimeout), func() { _ = f.drainPool(ctx) })

0 commit comments

Comments
 (0)