Skip to content

Commit 1c25764

Browse files
Merge pull request #143 from fclairamb/feature/shutdown-logic-improvement
Stopping logic improvement
2 parents d5e347e + e5032b5 commit 1c25764

19 files changed

+141
-89
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ If you're interested in a fully featured FTP server, you should use [ftpserver](
1717
* File and directory deletion and renaming
1818
* TLS support (AUTH + PROT)
1919
* File download/upload resume support (REST)
20-
* Complete driver for all the above features
2120
* Passive socket connections (EPSV and PASV commands)
2221
* Active socket connections (PORT command)
2322
* Small memory footprint
24-
* Only relies on the standard library except for:
25-
* [go-kit log](https://github.com/go-kit/kit/tree/master/log) for logging
23+
* Clean code: No sync, no sleep, no panic
24+
* Uses only the standard library except for:
2625
* [afero](https://github.com/spf13/afero) for generic file systems handling
26+
* [go-kit log](https://github.com/go-kit/kit/tree/master/log) (optional) for logging
2727
* Supported extensions:
2828
* [AUTH](https://tools.ietf.org/html/rfc2228#page-6) - Control session protection
2929
* [AUTH TLS](https://tools.ietf.org/html/rfc4217#section-4.1) - TLS session

client_handler.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,15 @@ func (server *FtpServer) newClientHandler(connection net.Conn, id uint32) *clien
4848
logger: server.Logger.With("clientId", id),
4949
}
5050

51-
// Just respecting the existing logic here, this could be probably be dropped at some point
52-
5351
return p
5452
}
5553

5654
func (c *clientHandler) disconnect() {
5755
if err := c.conn.Close(); err != nil {
5856
c.logger.Warn(
5957
"Problem disconnecting a client",
60-
"err", err)
58+
"err", err,
59+
)
6160
}
6261
}
6362

@@ -134,7 +133,7 @@ func (c *clientHandler) HandleCommands() {
134133
if c.server.settings.IdleTimeout > 0 {
135134
if err := c.conn.SetDeadline(
136135
time.Now().Add(time.Duration(time.Second.Nanoseconds() * int64(c.server.settings.IdleTimeout)))); err != nil {
137-
c.logger.Error("Network error", err)
136+
c.logger.Error("Network error", "err", err)
138137
}
139138
}
140139

@@ -160,7 +159,7 @@ func (c *clientHandler) handleCommandsStreamError(err error) {
160159
if err.Timeout() {
161160
// We have to extend the deadline now
162161
if err := c.conn.SetDeadline(time.Now().Add(time.Minute)); err != nil {
163-
c.logger.Error("Could not set read deadline", err)
162+
c.logger.Error("Could not set read deadline", "err", err)
164163
}
165164

166165
c.logger.Info("Client IDLE timeout", "err", err)
@@ -169,24 +168,24 @@ func (c *clientHandler) handleCommandsStreamError(err error) {
169168
fmt.Sprintf("command timeout (%d seconds): closing control connection", c.server.settings.IdleTimeout))
170169

171170
if err := c.writer.Flush(); err != nil {
172-
c.logger.Error("Flush error", err)
171+
c.logger.Error("Flush error", "err", err)
173172
}
174173

175174
if err := c.conn.Close(); err != nil {
176-
c.logger.Error("Close error", err)
175+
c.logger.Error("Close error", "err", err)
177176
}
178177

179178
break
180179
}
181180

182-
c.logger.Error("Network error", err)
181+
c.logger.Error("Network error", "err", err)
183182
default:
184183
if err == io.EOF {
185184
if c.debug {
186185
c.logger.Debug("Client disconnected", "clean", false)
187186
}
188187
} else {
189-
c.logger.Error("Read error", err)
188+
c.logger.Error("Read error", "err", err)
190189
}
191190
}
192191
}
@@ -212,6 +211,12 @@ func (c *clientHandler) handleCommand(line string) {
212211
defer func() {
213212
if r := recover(); r != nil {
214213
c.writeMessage(StatusSyntaxErrorNotRecognised, fmt.Sprintf("Unhandled internal error: %s", r))
214+
c.logger.Warn(
215+
"Internal command handling error",
216+
"err", r,
217+
"command", c.command,
218+
"param", c.param,
219+
)
215220
}
216221
}()
217222

@@ -294,7 +299,6 @@ func parseLine(line string) (string, string) {
294299
return params[0], params[1]
295300
}
296301

297-
// For future use
298302
func (c *clientHandler) multilineAnswer(code int, message string) func() {
299303
c.writeLine(fmt.Sprintf("%d-%s", code, message))
300304

client_handler_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010

1111
func TestConcurrency(t *testing.T) {
1212
s := NewTestServer(false)
13-
defer s.Stop()
13+
defer mustStopServer(s)
1414

1515
nbClients := 100
1616

consts.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
package ftpserver
33

44
// from @stevenh's PR proposal
5-
// https://github.com/fclairamb/ftpserver/blob/becc125a0770e3b670c4ced7e7bd12594fb024ff/server/consts.go
5+
// https://github.com/fclairamb/ftpserverlib/blob/becc125a0770e3b670c4ced7e7bd12594fb024ff/server/consts.go
66

77
// Status codes as documented by:
88
// https://tools.ietf.org/html/rfc959

driver.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ type Settings struct {
8686
ListenAddr string // Listening address
8787
PublicHost string // Public IP to expose (only an IP address is accepted at this stage)
8888
PublicIPResolver PublicIPResolver // (Optional) To fetch a public IP lookup
89-
PassiveTransferPortRange *PortRange // Port Range for data connections. Random if not specified
89+
PassiveTransferPortRange *PortRange // (Optional) Port Range for data connections. Random if not specified
9090
ActiveTransferPortNon20 bool // Do not impose the port 20 for active data transfer (#88, RFC 1579)
9191
IdleTimeout int // Maximum inactivity time before disconnecting (#58)
9292
ConnectionTimeout int // Maximum time to establish passive or active transfer connections

driver_test.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ import (
44
"crypto/tls"
55
"errors"
66
"fmt"
7+
"io"
78
"io/ioutil"
89
"os"
910

1011
gklog "github.com/go-kit/kit/log"
1112
"github.com/spf13/afero"
1213

13-
"github.com/fclairamb/ftpserverlib/log"
14+
"github.com/fclairamb/ftpserverlib/log/gokit"
1415
)
1516

1617
const (
@@ -38,17 +39,21 @@ func NewTestServerWithDriver(driver *TestServerDriver) *FtpServer {
3839

3940
// If we are in debug mode, we should log things
4041
if driver.Debug {
41-
s.Logger = log.NewGKLogger(gklog.NewLogfmtLogger(gklog.NewSyncWriter(os.Stdout))).With(
42-
"ts", log.GKDefaultTimestampUTC,
43-
"caller", log.GKDefaultCaller,
42+
s.Logger = gokit.NewGKLogger(gklog.NewLogfmtLogger(gklog.NewSyncWriter(os.Stdout))).With(
43+
"ts", gokit.GKDefaultTimestampUTC,
44+
"caller", gokit.GKDefaultCaller,
4445
)
4546
}
4647

4748
if err := s.Listen(); err != nil {
4849
return nil
4950
}
5051

51-
go s.Serve()
52+
go func() {
53+
if err := s.Serve(); err != nil && err != io.EOF {
54+
s.Logger.Error("problem serving", "err", err)
55+
}
56+
}()
5257

5358
return s
5459
}
@@ -81,6 +86,13 @@ func NewTestClientDriver() *TestClientDriver {
8186
}
8287
}
8388

89+
func mustStopServer(server *FtpServer) {
90+
err := server.Stop()
91+
if err != nil {
92+
panic(err)
93+
}
94+
}
95+
8496
// ClientConnected is the very first message people will see
8597
func (driver *TestServerDriver) ClientConnected(cc ClientContext) (string, error) {
8698
cc.SetDebug(driver.Debug)

handle_auth_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func reportError(err error) {
2222

2323
func TestLoginSuccess(t *testing.T) {
2424
s := NewTestServer(true)
25-
defer s.Stop()
25+
defer mustStopServer(s)
2626

2727
var err error
2828

@@ -55,7 +55,7 @@ func TestLoginSuccess(t *testing.T) {
5555

5656
func TestLoginFailure(t *testing.T) {
5757
s := NewTestServer(true)
58-
defer s.Stop()
58+
defer mustStopServer(s)
5959

6060
var err error
6161

@@ -77,7 +77,7 @@ func TestAuthTLS(t *testing.T) {
7777
Debug: true,
7878
TLS: true,
7979
})
80-
defer s.Stop()
80+
defer mustStopServer(s)
8181

8282
ftp, err := goftp.Connect(s.Addr())
8383
if err != nil {

handle_dirs.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ func (c *clientHandler) handleMLSD() error {
141141
// TODO: We have a lot of copy/paste around directory listing, we should refactor this.
142142
defer func() {
143143
if errClose := directory.Close(); errClose != nil {
144-
c.logger.Error("Couldn't close directory", errClose, "directory", directoryPath)
144+
c.logger.Error("Couldn't close directory", "err", errClose, "directory", directoryPath)
145145
}
146146
}()
147147

handle_dirs_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const DirKnown = "known"
1313
// TestDirAccess relies on LIST of files listing
1414
func TestDirListing(t *testing.T) {
1515
s := NewTestServerWithDriver(&TestServerDriver{Debug: true, Settings: &Settings{DisableMLSD: true}})
16-
defer s.Stop()
16+
defer mustStopServer(s)
1717

1818
var connErr error
1919

@@ -59,7 +59,7 @@ func TestDirListing(t *testing.T) {
5959

6060
func TestDirListingPathArg(t *testing.T) {
6161
s := NewTestServerWithDriver(&TestServerDriver{Debug: true, Settings: &Settings{DisableMLSD: true}})
62-
defer s.Stop()
62+
defer mustStopServer(s)
6363

6464
var connErr error
6565

@@ -123,7 +123,7 @@ func TestDirListingPathArg(t *testing.T) {
123123
// TestDirAccess relies on LIST of files listing
124124
func TestDirHandling(t *testing.T) {
125125
s := NewTestServer(true)
126-
defer s.Stop()
126+
defer mustStopServer(s)
127127

128128
var connErr error
129129

@@ -186,7 +186,7 @@ func TestDirHandling(t *testing.T) {
186186
// TestDirListingWithSpace uses the MLSD for files listing
187187
func TestDirListingWithSpace(t *testing.T) {
188188
s := NewTestServer(true)
189-
defer s.Stop()
189+
defer mustStopServer(s)
190190

191191
var connErr error
192192

@@ -237,7 +237,7 @@ func TestDirListingWithSpace(t *testing.T) {
237237

238238
func TestCleanPath(t *testing.T) {
239239
s := NewTestServer(true)
240-
defer s.Stop()
240+
defer mustStopServer(s)
241241

242242
var connErr error
243243

handle_files.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,8 @@ func (c *clientHandler) handleSTATFile() error {
219219
path := c.absPath(c.param)
220220

221221
if info, err := c.driver.Stat(path); err == nil {
222-
m := c.multilineAnswer(StatusSystemStatus, "System status")
223-
defer m()
222+
defer c.multilineAnswer(StatusSystemStatus, "System status")()
223+
224224
// c.writeLine(fmt.Sprintf("%d-Status follows:", StatusSystemStatus))
225225
if info.IsDir() {
226226
directory, errOpenFile := c.driver.Open(c.absPath(c.param))
@@ -254,8 +254,7 @@ func (c *clientHandler) handleMLST() error {
254254
path := c.absPath(c.param)
255255

256256
if info, err := c.driver.Stat(path); err == nil {
257-
m := c.multilineAnswer(StatusFileOK, "File details")
258-
defer m()
257+
defer c.multilineAnswer(StatusFileOK, "File details")()
259258

260259
if errWrite := c.writeMLSxOutput(c.writer, info); errWrite != nil {
261260
return errWrite

0 commit comments

Comments
 (0)