fix (ftp): recreate connection if closed

An issue would araise when the connection is closed before we had time
to do the vacuum cleaning on servers like on infinity free where "Our server has quite aggressive inactivity timeouts and will kill the connection after only 20 seconds of inactivity."

reference: https://forum.infinityfree.net/t/good-online-ftp/69285/18
This commit is contained in:
Mickael Kerjean
2023-04-10 01:28:39 +10:00
parent 67e2a34853
commit 39f8fc66aa
+94 -40
View File
@@ -1,6 +1,7 @@
package plg_backend_ftp
import (
"context"
"crypto/tls"
"fmt"
. "github.com/mickael-kerjean/filestash/server/common"
@@ -47,7 +48,7 @@ func (f Ftp) Init(params map[string]string, app *App) (IBackend, error) {
if c := FtpCache.Get(params); c != nil {
d := c.(*Ftp)
if d == nil {
Log.Warning("plg_backend_ftp::sftp is nil on get")
Log.Warning("plg_backend_ftp::ftp is nil on get")
return nil, ErrInternal
} else if d.wg == nil {
Log.Warning("plg_backend_ftp::wg is nil on get")
@@ -246,31 +247,50 @@ func (f Ftp) Meta(path string) Metadata {
return Metadata{}
}
func (f Ftp) Home() (string, error) {
return f.client.Getwd()
func (f Ftp) Home() (home string, err error) {
f.Execute(func(client *goftp.Client) error {
home, err = f.client.Getwd()
return err
})
return home, err
}
func (f Ftp) Ls(path string) ([]os.FileInfo, error) {
return f.client.ReadDir(path)
func (f Ftp) Ls(path string) (files []os.FileInfo, err error) {
f.Execute(func(client *goftp.Client) error {
files, err = client.ReadDir(path)
return err
})
return files, err
}
func (f Ftp) Cat(path string) (io.ReadCloser, error) {
pr, pw := io.Pipe()
go func() {
if err := f.client.Retrieve(path, pw); err != nil {
pr.CloseWithError(NewError("Problem", 409))
func (f Ftp) Cat(path string) (reader io.ReadCloser, err error) {
f.Execute(func(client *goftp.Client) error {
if _, err = client.Stat(path); err != nil {
return err
}
pw.Close()
}()
return pr, nil
pr, pw := io.Pipe()
go func() {
err = client.Retrieve(path, pw)
if err != nil {
pr.CloseWithError(NewError("Problem", 409))
}
pw.Close()
}()
reader = pr
return nil
})
return reader, err
}
func (f Ftp) Mkdir(path string) error {
_, err := f.client.Mkdir(path)
func (f Ftp) Mkdir(path string) (err error) {
f.Execute(func(client *goftp.Client) error {
_, err = client.Mkdir(path)
return err
})
return err
}
func (f Ftp) Rm(path string) error {
func (f Ftp) Rm(path string) (err error) {
isDirectory := func(p string) bool {
return regexp.MustCompile(`\/$`).MatchString(p)
}
@@ -287,43 +307,77 @@ func (f Ftp) Rm(path string) error {
}
return e
}
if isDirectory(path) {
entries, err := f.Ls(path)
if transformError(err) != nil {
return err
}
for _, entry := range entries {
if entry.IsDir() {
err = f.Rm(path + entry.Name() + "/")
if transformError(err) != nil {
return err
}
} else {
err = f.Rm(path + entry.Name())
if transformError(err) != nil {
return err
var recursiveDelete func(client *goftp.Client, _path string) error
recursiveDelete = func(client *goftp.Client, _path string) error {
if isDirectory(_path) {
entries, err := client.ReadDir(_path)
if transformError(err) != nil {
return err
}
for _, entry := range entries {
if entry.IsDir() {
err = recursiveDelete(client, _path+entry.Name()+"/")
if transformError(err) != nil {
return err
}
} else {
err = recursiveDelete(client, _path+entry.Name())
if transformError(err) != nil {
return err
}
}
}
err = client.Rmdir(_path)
return transformError(err)
}
err = f.client.Rmdir(path)
err = client.Delete(_path)
return transformError(err)
}
err := f.client.Delete(path)
return transformError(err)
f.Execute(func(client *goftp.Client) error {
err = recursiveDelete(client, path)
return err
})
return err
}
func (f Ftp) Mv(from string, to string) error {
return f.client.Rename(from, to)
func (f Ftp) Mv(from string, to string) (err error) {
f.Execute(func(client *goftp.Client) error {
err = client.Rename(from, to)
return err
})
return err
}
func (f Ftp) Touch(path string) error {
return f.client.Store(path, strings.NewReader(""))
func (f Ftp) Touch(path string) (err error) {
f.Execute(func(client *goftp.Client) error {
err = client.Store(path, strings.NewReader(""))
return err
})
return err
}
func (f Ftp) Save(path string, file io.Reader) error {
return f.client.Store(path, file)
func (f Ftp) Save(path string, file io.Reader) (err error) {
f.Execute(func(client *goftp.Client) error {
err = client.Store(path, file)
return err
})
return err
}
func (f Ftp) Close() error {
return f.client.Close()
}
func (f Ftp) Execute(fn func(*goftp.Client) error) {
err := fn(f.client)
if ftpErr, ok := err.(goftp.Error); ok {
code := ftpErr.Code()
if code == 421 || (code == 0 && err.Error() == "error reading response: EOF") {
f.Close()
FtpCache.Set(f.p, nil)
if b, err := f.Init(f.p, &App{Context: context.Background()}); err == nil {
fn(b.(*Ftp).client)
}
}
}
}