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) }