From 5f26ef6ad1b109f9e8a04ce8d87237814e9d651c Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Tue, 27 Oct 2026 17:31:23 -0700 Subject: [PATCH] filer,webdav,iam,sftp: add -idleTimeout flag for connection idle timeout (#12136) * filer: make the HTTP connection idle timeout configurable The filer applied a hardcoded 10s activity timeout to its HTTP listener. A buffering proxy in front of the filer can stall upstream reads longer than that when the end client drains slowly, so large downloads through it were truncated (#8062). Expose the value as -idleTimeout (default 10, unchanged), matching the existing -idleTimeout flags on the volume and s3 servers, and wire filer.idleTimeout through 'weed server' and 'weed mini' too. A nil option keeps the 10s default so embedders constructing FilerOptions directly keep working. * filer: apply the idle timeout to the readonly port too The -port.readonly listener ran without any connection timeout while the main API port timed out. Give it the same -idleTimeout so both ports behave identically. * webdav: make the HTTP connection idle timeout configurable The WebDAV server used the same hardcoded 10s activity timeout. Expose it as -idleTimeout (default 10, unchanged), including -webdav.idleTimeout when embedded via 'weed filer -webdav', 'weed server -webdav', and 'weed mini'. * iam: make the HTTP connection idle timeout configurable The IAM API server used the same hardcoded 10s activity timeout. Expose it as -idleTimeout (default 10, unchanged), including -iam.idleTimeout when embedded via 'weed filer -iam' and 'weed server -iam'. * sftp: make the connection idle timeout configurable The SFTP server used the same hardcoded 10s activity timeout. Expose it as -idleTimeout (default 10, unchanged), including -sftp.idleTimeout when embedded via 'weed filer -sftp' and 'weed server -sftp'. * command: clamp idle timeout seconds before converting to duration A seconds value past time.Duration's range would overflow on multiplication and silently disable the timeout. Bound it in one shared helper, which also deduplicates the nil-default handling at the four call sites. * command: return zero for non-positive idle timeout seconds A large negative seconds value could wrap on multiplication and come out positive. Guard before converting so non-positive input always disables the timeout. * command: apply idle timeout to local unix socket listeners The unix-socket listeners in filer, s3, and sftp served accepted connections with no activity deadline while the TCP listeners already wrapped conns in util.Listener. Wrap the socket listeners the same way so a positive idleTimeout also bounds idle socket connections. * command: clamp s3 idle timeout seconds before multiplying s3 still multiplied the flag inline, so an out-of-range value could overflow time.Duration. Route it through idleTimeoutSeconds like the other commands; non-positive keeps meaning disabled. * volume: clamp idle connection timeout seconds before converting * command: cover idleTimeoutSeconds conversion semantics * command: gate idleTimeoutSeconds clamp check on 64-bit int --- weed/command/filer.go | 11 +++++++++-- weed/command/iam.go | 5 ++++- weed/command/mini.go | 2 ++ weed/command/s3.go | 11 ++++++----- weed/command/server.go | 19 +++++++++++++++++++ weed/command/server_test.go | 35 ++++++++++++++++++++++++++++++++++- weed/command/sftp.go | 6 +++++- weed/command/volume.go | 4 ++-- weed/command/webdav.go | 6 +++++- 9 files changed, 86 insertions(+), 13 deletions(-) diff --git a/weed/command/filer.go b/weed/command/filer.go index e3a161b16c..630ab46b5b 100644 --- a/weed/command/filer.go +++ b/weed/command/filer.go @@ -68,6 +68,7 @@ type FilerOptions struct { port *int portGrpc *int publicPort *int + idleTimeout *int filerGroup *string collection *string defaultReplicaPlacement *string @@ -119,6 +120,7 @@ func init() { f.port = cmdFiler.Flag.Int("port", 8888, "filer server http listen port") f.portGrpc = cmdFiler.Flag.Int("port.grpc", 0, "filer server grpc listen port") f.publicPort = cmdFiler.Flag.Int("port.readonly", 0, "readonly port opened to public") + f.idleTimeout = cmdFiler.Flag.Int("idleTimeout", 10, "connection idle seconds") f.defaultReplicaPlacement = cmdFiler.Flag.String("defaultReplicaPlacement", "", "default replication type. If not specified, use master setting.") f.disableDirListing = cmdFiler.Flag.Bool("disableDirListing", false, "turn off directory listing") f.maxMB = cmdFiler.Flag.Int("maxMB", 4, "split files larger than the limit") @@ -197,15 +199,18 @@ func init() { filerWebDavOptions.cacheSizeMB = cmdFiler.Flag.Int64("webdav.cacheCapacityMB", 0, "local cache capacity in MB") filerWebDavOptions.maxMB = cmdFiler.Flag.Int("webdav.maxMB", 4, "split files larger than the limit") filerWebDavOptions.filerRootPath = cmdFiler.Flag.String("webdav.filer.path", "/", "use this remote path from filer server") + filerWebDavOptions.idleTimeout = cmdFiler.Flag.Int("webdav.idleTimeout", 10, "connection idle seconds") // start iam on filer filerStartIam = cmdFiler.Flag.Bool("iam", false, "whether to start IAM service") filerIamOptions.ip = cmdFiler.Flag.String("iam.ip", *f.ip, "iam server http listen ip address") filerIamOptions.port = cmdFiler.Flag.Int("iam.port", 8111, "iam server http listen port") + filerIamOptions.idleTimeout = cmdFiler.Flag.Int("iam.idleTimeout", 10, "connection idle seconds") filerIamOptions.selfServiceRequiresGrant = filerS3Options.iamSelfServiceRequiresGrant filerStartSftp = cmdFiler.Flag.Bool("sftp", false, "whether to start the SFTP server") filerSftpOptions.port = cmdFiler.Flag.Int("sftp.port", 2022, "SFTP server listen port") + filerSftpOptions.idleTimeout = cmdFiler.Flag.Int("sftp.idleTimeout", 10, "connection idle seconds") filerSftpOptions.sshPrivateKey = cmdFiler.Flag.String("sftp.sshPrivateKey", "", "path to the SSH private key file for host authentication") filerSftpOptions.hostKeysFolder = cmdFiler.Flag.String("sftp.hostKeysFolder", "", "path to folder containing SSH private key files for host authentication") filerSftpOptions.authMethods = cmdFiler.Flag.String("sftp.authMethods", "password,publickey", "comma-separated list of allowed auth methods: password, publickey, certificate") @@ -371,6 +376,7 @@ func (fo *FilerOptions) startFiler() { if fo.listenOnLocalhost != nil { listenOnLocalhost = *fo.listenOnLocalhost } + idleTimeout := idleTimeoutSeconds(fo.idleTimeout) if *fo.bindIp == "" { *fo.bindIp = *fo.ip } @@ -448,7 +454,7 @@ func (fo *FilerOptions) startFiler() { if *fo.publicPort != 0 { publicListeningAddress := util.JoinHostPort(*fo.bindIp, *fo.publicPort) glog.V(0).Infoln("Start Seaweed filer server", version.Version(), "public at", publicListeningAddress) - publicListener, localPublicListener, e := util.NewIpAndLocalListeners(*fo.bindIp, *fo.publicPort, 0, listenOnLocalhost) + publicListener, localPublicListener, e := util.NewIpAndLocalListeners(*fo.bindIp, *fo.publicPort, idleTimeout, listenOnLocalhost) if e != nil { glog.Fatalf("Filer server public listener error on port %d:%v", *fo.publicPort, e) } @@ -474,7 +480,7 @@ func (fo *FilerOptions) startFiler() { glog.V(0).Infof("Start Seaweed Filer %s at %s:%d", version.Version(), *fo.ip, *fo.port) filerListener, filerLocalListener, e := util.NewIpAndLocalListeners( *fo.bindIp, *fo.port, - time.Duration(10)*time.Second, listenOnLocalhost, + idleTimeout, listenOnLocalhost, ) if e != nil { glog.Fatalf("Filer listener error: %v", e) @@ -543,6 +549,7 @@ func (fo *FilerOptions) startFiler() { localSocket = fmt.Sprintf("/tmp/seaweedfs-filer-%d.sock", *fo.port) } if filerSocketListener := pb.ListenOnLocalSocket(localSocket); filerSocketListener != nil { + filerSocketListener = &util.Listener{Listener: filerSocketListener, Timeout: idleTimeout} socketServer := newHttpServer(defaultHandler, nil) httpServers = append(httpServers, socketServer) go socketServer.Serve(filerSocketListener) diff --git a/weed/command/iam.go b/weed/command/iam.go index a419171815..713421c10e 100644 --- a/weed/command/iam.go +++ b/weed/command/iam.go @@ -34,6 +34,7 @@ type IamOptions struct { ipBind *string listenOnLocalhost *bool port *int + idleTimeout *int selfServiceRequiresGrant *bool } @@ -44,6 +45,7 @@ func init() { iamStandaloneOptions.ip = cmdIam.Flag.String("ip", util.DetectedHostAddress(), "iam server http listen ip address") iamStandaloneOptions.ipBind = cmdIam.Flag.String("ip.bind", "", "ip address to bind to. If empty, default to same as -ip option.") iamStandaloneOptions.port = cmdIam.Flag.Int("port", 8111, "iam server http listen port") + iamStandaloneOptions.idleTimeout = cmdIam.Flag.Int("idleTimeout", 10, "connection idle seconds") iamStandaloneOptions.selfServiceRequiresGrant = cmdIam.Flag.Bool("selfServiceRequiresGrant", false, iamSelfServiceRequiresGrantUsage) } @@ -133,9 +135,10 @@ func (iamopt *IamOptions) startIamServer() bool { if iamopt.listenOnLocalhost != nil { listenOnLocalhost = *iamopt.listenOnLocalhost } + idleTimeout := idleTimeoutSeconds(iamopt.idleTimeout) listenAddress := fmt.Sprintf(":%d", *iamopt.port) - iamApiListener, iamApiLocalListener, err := util.NewIpAndLocalListeners(bindIp, *iamopt.port, time.Duration(10)*time.Second, listenOnLocalhost) + iamApiListener, iamApiLocalListener, err := util.NewIpAndLocalListeners(bindIp, *iamopt.port, idleTimeout, listenOnLocalhost) if err != nil { glog.Fatalf("IAM API Server listener on %s error: %v", listenAddress, err) } diff --git a/weed/command/mini.go b/weed/command/mini.go index 8d213bdf70..d0fb243514 100644 --- a/weed/command/mini.go +++ b/weed/command/mini.go @@ -487,6 +487,7 @@ func initMiniFilerFlags() { miniFilerOptions.tusSessionExpiry = cmdMini.Flag.Duration("filer.tusSessionExpiry", 24*time.Hour, "incomplete TUS upload sessions are cleaned up after this duration") miniFilerOptions.allowUntrustedRemoteEndpoints = cmdMini.Flag.Bool("filer.allowUntrustedRemoteEndpoints", false, allowUntrustedRemoteEndpointsUsage) miniFilerOptions.remoteCacheEvictThreshold = cmdMini.Flag.Float64("filer.remoteCacheEvictThreshold", 0.9, "evict remote-cached objects (oldest first) when any volume disk exceeds this usage fraction; 0 disables") + miniFilerOptions.idleTimeout = cmdMini.Flag.Int("filer.idleTimeout", 10, "connection idle seconds") } // initMiniVolumeFlags initializes Volume server flag options @@ -578,6 +579,7 @@ func initMiniWebDAVFlags() { miniWebDavOptions.cacheSizeMB = cmdMini.Flag.Int64("webdav.cacheCapacityMB", 0, "local cache capacity in MB") miniWebDavOptions.maxMB = cmdMini.Flag.Int("webdav.maxMB", 4, "split files larger than the limit") miniWebDavOptions.filerRootPath = cmdMini.Flag.String("webdav.filer.path", "/", "use this remote path from filer server") + miniWebDavOptions.idleTimeout = cmdMini.Flag.Int("webdav.idleTimeout", 10, "connection idle seconds") } // initMiniAdminFlags initializes Admin server flag options diff --git a/weed/command/s3.go b/weed/command/s3.go index 2d3a0137ad..e2720518c5 100644 --- a/weed/command/s3.go +++ b/weed/command/s3.go @@ -454,6 +454,7 @@ func (s3opt *S3Options) startS3Server() bool { localSocket = fmt.Sprintf("/tmp/seaweedfs-s3-%d.sock", *s3opt.port) } if s3SocketListener := pb.ListenOnLocalSocket(localSocket); s3SocketListener != nil { + s3SocketListener = &util.Listener{Listener: s3SocketListener, Timeout: idleTimeoutSeconds(s3opt.idleTimeout)} socketServer := newHttpServer(router, nil) httpServers = append(httpServers, socketServer) go func() { @@ -466,7 +467,7 @@ func (s3opt *S3Options) startS3Server() bool { listenAddress := fmt.Sprintf("%s:%d", *s3opt.bindIp, *s3opt.port) s3ApiListener, s3ApiLocalListener, err := util.NewIpAndLocalListeners( - *s3opt.bindIp, *s3opt.port, time.Duration(*s3opt.idleTimeout)*time.Second, listenOnLocalhost) + *s3opt.bindIp, *s3opt.port, idleTimeoutSeconds(s3opt.idleTimeout), listenOnLocalhost) if err != nil { glog.Fatalf("S3 API Server listener on %s error: %v", listenAddress, err) } @@ -548,7 +549,7 @@ func (s3opt *S3Options) startS3Server() bool { } else { glog.V(0).Infof("Start Seaweed S3 API Server %s at https port %d", version.Version(), *s3opt.portHttps) s3ApiListenerHttps, s3ApiLocalListenerHttps, err := util.NewIpAndLocalListeners( - *s3opt.bindIp, *s3opt.portHttps, time.Duration(*s3opt.idleTimeout)*time.Second, listenOnLocalhost) + *s3opt.bindIp, *s3opt.portHttps, idleTimeoutSeconds(s3opt.idleTimeout), listenOnLocalhost) if err != nil { glog.Fatalf("S3 API HTTPS listener on %s:%d error: %v", *s3opt.bindIp, *s3opt.portHttps, err) } @@ -630,7 +631,7 @@ func (s3opt *S3Options) startIcebergServer(s3ApiServer *s3api.S3ApiServer, liste listenAddress := fmt.Sprintf("%s:%d", *s3opt.bindIp, *s3opt.portIceberg) icebergListener, icebergLocalListener, err := util.NewIpAndLocalListeners( - *s3opt.bindIp, *s3opt.portIceberg, time.Duration(*s3opt.idleTimeout)*time.Second, listenOnLocalhost) + *s3opt.bindIp, *s3opt.portIceberg, idleTimeoutSeconds(s3opt.idleTimeout), listenOnLocalhost) if err != nil { // The catalog is auxiliary to the S3 gateway; a port conflict must not // take the whole server down. @@ -676,7 +677,7 @@ func (s3opt *S3Options) startLanceServer(s3ApiServer *s3api.S3ApiServer, listenO listenAddress := fmt.Sprintf("%s:%d", *s3opt.bindIp, *s3opt.portLance) lanceListener, lanceLocalListener, err := util.NewIpAndLocalListeners( - *s3opt.bindIp, *s3opt.portLance, time.Duration(*s3opt.idleTimeout)*time.Second, listenOnLocalhost) + *s3opt.bindIp, *s3opt.portLance, idleTimeoutSeconds(s3opt.idleTimeout), listenOnLocalhost) if err != nil { glog.Errorf("Lance Namespace listener on %s error: %v", listenAddress, err) return nil @@ -765,7 +766,7 @@ func (s3opt *S3Options) startVectorsServer(s3ApiServer *s3api.S3ApiServer, grpcD listenAddress := fmt.Sprintf("%s:%d", *s3opt.bindIp, *s3opt.portVectors) vectorsListener, vectorsLocalListener, err := util.NewIpAndLocalListeners( - *s3opt.bindIp, *s3opt.portVectors, time.Duration(*s3opt.idleTimeout)*time.Second, listenOnLocalhost) + *s3opt.bindIp, *s3opt.portVectors, idleTimeoutSeconds(s3opt.idleTimeout), listenOnLocalhost) if err != nil { glog.Errorf("S3 Vectors listener on %s error: %v", listenAddress, err) conn.Close() diff --git a/weed/command/server.go b/weed/command/server.go index b6a61a0774..818364859f 100644 --- a/weed/command/server.go +++ b/weed/command/server.go @@ -3,6 +3,7 @@ package command import ( "crypto/tls" "fmt" + "math" "net/http" "os" "strings" @@ -138,6 +139,7 @@ func init() { filerOptions.tusSessionExpiry = cmdServer.Flag.Duration("filer.tusSessionExpiry", 24*time.Hour, "incomplete TUS upload sessions are cleaned up after this duration, e.g. \"48h\", \"7h30m\"") filerOptions.allowUntrustedRemoteEndpoints = cmdServer.Flag.Bool("filer.allowUntrustedRemoteEndpoints", false, allowUntrustedRemoteEndpointsUsage) filerOptions.remoteCacheEvictThreshold = cmdServer.Flag.Float64("filer.remoteCacheEvictThreshold", 0.9, "evict remote-cached objects (oldest first) when any volume disk exceeds this usage fraction; 0 disables") + filerOptions.idleTimeout = cmdServer.Flag.Int("filer.idleTimeout", 10, "connection idle seconds") serverOptions.v.port = cmdServer.Flag.Int("volume.port", 8080, "volume server http listen port") serverOptions.v.portGrpc = cmdServer.Flag.Int("volume.port.grpc", 0, "volume server grpc listen port") @@ -207,6 +209,7 @@ func init() { s3Options.iamSelfServiceRequiresGrant = cmdServer.Flag.Bool("s3.iam.selfServiceRequiresGrant", false, iamSelfServiceRequiresGrantUsage) sftpOptions.port = cmdServer.Flag.Int("sftp.port", 2022, "SFTP server listen port") + sftpOptions.idleTimeout = cmdServer.Flag.Int("sftp.idleTimeout", 10, "connection idle seconds") sftpOptions.sshPrivateKey = cmdServer.Flag.String("sftp.sshPrivateKey", "", "path to the SSH private key file for host authentication") sftpOptions.hostKeysFolder = cmdServer.Flag.String("sftp.hostKeysFolder", "", "path to folder containing SSH private key files for host authentication") sftpOptions.authMethods = cmdServer.Flag.String("sftp.authMethods", "password,publickey", "comma-separated list of allowed auth methods: password, publickey, certificate") @@ -220,6 +223,7 @@ func init() { sftpOptions.dataCenter = cmdServer.Flag.String("sftp.dataCenter", "", "prefer to read and write to volumes in this data center") sftpOptions.localSocket = cmdServer.Flag.String("sftp.localSocket", "", "default to /tmp/seaweedfs-sftp-.sock") iamOptions.port = cmdServer.Flag.Int("iam.port", 8111, "iam server http listen port") + iamOptions.idleTimeout = cmdServer.Flag.Int("iam.idleTimeout", 10, "connection idle seconds") iamOptions.selfServiceRequiresGrant = s3Options.iamSelfServiceRequiresGrant webdavOptions.port = cmdServer.Flag.Int("webdav.port", 7333, "webdav server http listen port") @@ -232,6 +236,7 @@ func init() { webdavOptions.cacheSizeMB = cmdServer.Flag.Int64("webdav.cacheCapacityMB", 0, "local cache capacity in MB") webdavOptions.maxMB = cmdServer.Flag.Int("webdav.maxMB", 4, "split files larger than the limit") webdavOptions.filerRootPath = cmdServer.Flag.String("webdav.filer.path", "/", "use this remote path from filer server") + webdavOptions.idleTimeout = cmdServer.Flag.Int("webdav.idleTimeout", 10, "connection idle seconds") mqBrokerOptions.port = cmdServer.Flag.Int("mq.broker.port", 17777, "message queue broker gRPC listen port") mqBrokerOptions.logFlushInterval = cmdServer.Flag.Int("mq.broker.logFlushInterval", 5, "log buffer flush interval in seconds") @@ -508,6 +513,20 @@ func runServer(cmd *Command, args []string) bool { select {} } +// idleTimeoutSeconds converts a seconds flag to the listener activity timeout, +// keeping the 10s default when the option was never set. The clamp keeps the +// multiplication from overflowing time.Duration; non-positive values disable +// the timeout, as before. +func idleTimeoutSeconds(seconds *int) time.Duration { + if seconds == nil { + return 10 * time.Second + } + if *seconds <= 0 { + return 0 + } + return time.Duration(min(int64(*seconds), math.MaxInt64/int64(time.Second))) * time.Second +} + func newHttpServer(h http.Handler, tlsConfig *tls.Config) *http.Server { s := &http.Server{ Handler: h, diff --git a/weed/command/server_test.go b/weed/command/server_test.go index 1ad35e1484..fca2c53553 100644 --- a/weed/command/server_test.go +++ b/weed/command/server_test.go @@ -1,6 +1,11 @@ package command -import "testing" +import ( + "math" + "strconv" + "testing" + "time" +) func TestServerFilerDisableHttpFlag(t *testing.T) { flag := cmdServer.Flag.Lookup("filer.disableHttp") @@ -23,3 +28,31 @@ func TestServerGatewayDataCenterFlags(t *testing.T) { } } } + +func TestIdleTimeoutSeconds(t *testing.T) { + if got := idleTimeoutSeconds(nil); got != 10*time.Second { + t.Fatalf("idleTimeoutSeconds(nil) = %v, want 10s", got) + } + zero, neg, five := 0, -3, 5 + for _, tc := range []struct { + name string + seconds *int + want time.Duration + }{ + {"zero disables", &zero, 0}, + {"negative disables", &neg, 0}, + {"positive converts", &five, 5 * time.Second}, + } { + if got := idleTimeoutSeconds(tc.seconds); got != tc.want { + t.Errorf("%s: idleTimeoutSeconds = %v, want %v", tc.name, got, tc.want) + } + } + // Only 64-bit int can exceed the clamp threshold. + if strconv.IntSize == 64 { + huge := math.MaxInt + want := time.Duration(math.MaxInt64/int64(time.Second)) * time.Second + if got := idleTimeoutSeconds(&huge); got != want { + t.Errorf("huge clamps: idleTimeoutSeconds = %v, want %v", got, want) + } + } +} diff --git a/weed/command/sftp.go b/weed/command/sftp.go index 718229f087..1366579cdb 100644 --- a/weed/command/sftp.go +++ b/weed/command/sftp.go @@ -27,6 +27,7 @@ type SftpOptions struct { bindIp *string listenOnLocalhost *bool port *int + idleTimeout *int sshPrivateKey *string hostKeysFolder *string authMethods *string @@ -62,6 +63,7 @@ func init() { sftpOptionsStandalone.filer = cmdSftp.Flag.String("filer", "localhost:8888", "filer server address (ip:port)") sftpOptionsStandalone.bindIp = cmdSftp.Flag.String("ip.bind", "", "ip address to bind to. If empty, default to 0.0.0.0.") sftpOptionsStandalone.port = cmdSftp.Flag.Int("port", 2022, "SFTP server listen port") + sftpOptionsStandalone.idleTimeout = cmdSftp.Flag.Int("idleTimeout", 10, "connection idle seconds") sftpOptionsStandalone.sshPrivateKey = cmdSftp.Flag.String("sshPrivateKey", "", "path to the SSH private key file for host authentication") sftpOptionsStandalone.hostKeysFolder = cmdSftp.Flag.String("hostKeysFolder", "", "path to folder containing SSH private key files for host authentication") sftpOptionsStandalone.authMethods = cmdSftp.Flag.String("authMethods", "password,publickey", "comma-separated list of allowed auth methods: password, publickey, certificate") @@ -112,6 +114,7 @@ func (sftpOpt *SftpOptions) startSftpServer() bool { if sftpOpt.listenOnLocalhost != nil { listenOnLocalhost = *sftpOpt.listenOnLocalhost } + idleTimeout := idleTimeoutSeconds(sftpOpt.idleTimeout) if *sftpOpt.bindIp == "" { *sftpOpt.bindIp = "0.0.0.0" } @@ -190,6 +193,7 @@ func (sftpOpt *SftpOptions) startSftpServer() bool { localSocket = fmt.Sprintf("/tmp/seaweedfs-sftp-%d.sock", *sftpOpt.port) } if sftpSocketListener := pb.ListenOnLocalSocket(localSocket); sftpSocketListener != nil { + sftpSocketListener = &util.Listener{Listener: sftpSocketListener, Timeout: idleTimeout} go func() { // start on local unix socket if err := service.Serve(sftpSocketListener); err != nil { @@ -201,7 +205,7 @@ func (sftpOpt *SftpOptions) startSftpServer() bool { // Start the SFTP service on TCP listenAddress := fmt.Sprintf("%s:%d", *sftpOpt.bindIp, *sftpOpt.port) - sftpListener, sftpLocalListener, err := util.NewIpAndLocalListeners(*sftpOpt.bindIp, *sftpOpt.port, time.Duration(10)*time.Second, listenOnLocalhost) + sftpListener, sftpLocalListener, err := util.NewIpAndLocalListeners(*sftpOpt.bindIp, *sftpOpt.port, idleTimeout, listenOnLocalhost) if err != nil { glog.Fatalf("SFTP server listener on %s error: %v", listenAddress, err) } diff --git a/weed/command/volume.go b/weed/command/volume.go index d9ca553ff8..1388ae171d 100644 --- a/weed/command/volume.go +++ b/weed/command/volume.go @@ -664,7 +664,7 @@ func (v VolumeServerOptions) startGrpcService(vs volume_server_pb.VolumeServerSe func (v VolumeServerOptions) startPublicHttpService(handler http.Handler, listenOnLocalhost bool) httpdown.Server { publicListeningAddress := util.JoinHostPort(*v.bindIp, *v.publicPort) glog.V(0).Infoln("Start Seaweed volume server", version.Version(), "public at", publicListeningAddress) - publicListener, publicLocalListener, e := util.NewIpAndLocalListeners(*v.bindIp, *v.publicPort, time.Duration(*v.idleConnectionTimeout)*time.Second, listenOnLocalhost) + publicListener, publicLocalListener, e := util.NewIpAndLocalListeners(*v.bindIp, *v.publicPort, idleTimeoutSeconds(v.idleConnectionTimeout), listenOnLocalhost) if e != nil { glog.Fatalf("Volume server listener error:%v", e) } @@ -700,7 +700,7 @@ func (v VolumeServerOptions) startClusterHttpService(handler http.Handler, liste listeningAddress := util.JoinHostPort(*v.bindIp, *v.port) glog.V(0).Infof("Start Seaweed volume server %s at %s", version.Version(), listeningAddress) - listener, localListener, e := util.NewIpAndLocalListeners(*v.bindIp, *v.port, time.Duration(*v.idleConnectionTimeout)*time.Second, listenOnLocalhost) + listener, localListener, e := util.NewIpAndLocalListeners(*v.bindIp, *v.port, idleTimeoutSeconds(v.idleConnectionTimeout), listenOnLocalhost) if e != nil { glog.Fatalf("Volume server listener error:%v", e) } diff --git a/weed/command/webdav.go b/weed/command/webdav.go index 4df1e1fcee..90e9e4400e 100644 --- a/weed/command/webdav.go +++ b/weed/command/webdav.go @@ -37,6 +37,7 @@ type WebDavOption struct { cacheDir *string cacheSizeMB *int64 maxMB *int + idleTimeout *int // shutdownCtx, when non-nil, tells startWebDav to gracefully shut down the // HTTP server once the ctx is cancelled. Used by weed mini; nil for // standalone weed webdav. @@ -57,6 +58,7 @@ func init() { webDavStandaloneOptions.cacheSizeMB = cmdWebDav.Flag.Int64("cacheCapacityMB", 0, "local cache capacity in MB") webDavStandaloneOptions.maxMB = cmdWebDav.Flag.Int("maxMB", 4, "split files larger than the limit") webDavStandaloneOptions.filerRootPath = cmdWebDav.Flag.String("filer.path", "/", "use this remote path from filer server") + webDavStandaloneOptions.idleTimeout = cmdWebDav.Flag.Int("idleTimeout", 10, "connection idle seconds") } var cmdWebDav = &Command{ @@ -147,8 +149,10 @@ func (wo *WebDavOption) startWebDav() bool { httpS := &http.Server{Handler: ws.Handler} + idleTimeout := idleTimeoutSeconds(wo.idleTimeout) + listenAddress := fmt.Sprintf("%s:%d", *wo.ipBind, *wo.port) - webDavListener, err := util.NewListener(listenAddress, time.Duration(10)*time.Second) + webDavListener, err := util.NewListener(listenAddress, idleTimeout) if err != nil { glog.Fatalf("WebDav Server listener on %s error: %v", listenAddress, err) }