mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-29 00:10:42 +01:00
* 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
188 lines
6.4 KiB
Go
188 lines
6.4 KiB
Go
package command
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"os/user"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/util/version"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/security"
|
|
weed_server "github.com/seaweedfs/seaweedfs/weed/server"
|
|
"github.com/seaweedfs/seaweedfs/weed/util"
|
|
)
|
|
|
|
var (
|
|
webDavStandaloneOptions WebDavOption
|
|
)
|
|
|
|
type WebDavOption struct {
|
|
filer *string
|
|
ipBind *string
|
|
filerRootPath *string
|
|
port *int
|
|
collection *string
|
|
replication *string
|
|
disk *string
|
|
tlsPrivateKey *string
|
|
tlsCertificate *string
|
|
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.
|
|
shutdownCtx context.Context
|
|
}
|
|
|
|
func init() {
|
|
cmdWebDav.Run = runWebDav // break init cycle
|
|
webDavStandaloneOptions.filer = cmdWebDav.Flag.String("filer", "localhost:8888", "filer server address")
|
|
webDavStandaloneOptions.ipBind = cmdWebDav.Flag.String("ip.bind", "", "ip address to bind to. Default listen to all.")
|
|
webDavStandaloneOptions.port = cmdWebDav.Flag.Int("port", 7333, "webdav server http listen port")
|
|
webDavStandaloneOptions.collection = cmdWebDav.Flag.String("collection", "", "collection to create the files")
|
|
webDavStandaloneOptions.replication = cmdWebDav.Flag.String("replication", "", "replication to create the files")
|
|
webDavStandaloneOptions.disk = cmdWebDav.Flag.String("disk", "", "[hdd|ssd|<tag>] hard drive or solid state drive or any tag")
|
|
webDavStandaloneOptions.tlsPrivateKey = cmdWebDav.Flag.String("key.file", "", "path to the TLS private key file")
|
|
webDavStandaloneOptions.tlsCertificate = cmdWebDav.Flag.String("cert.file", "", "path to the TLS certificate file")
|
|
webDavStandaloneOptions.cacheDir = cmdWebDav.Flag.String("cacheDir", os.TempDir(), "local cache directory for file chunks")
|
|
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{
|
|
UsageLine: "webdav -port=7333 -filer=<ip:port>",
|
|
Short: "start a webdav server that is backed by a filer",
|
|
Long: `start a webdav server that is backed by a filer.
|
|
|
|
`,
|
|
}
|
|
|
|
func runWebDav(cmd *Command, args []string) bool {
|
|
|
|
webDavStandaloneOptions.resolvePaths()
|
|
util.LoadSecurityConfiguration()
|
|
|
|
listenAddress := fmt.Sprintf("%s:%d", *webDavStandaloneOptions.ipBind, *webDavStandaloneOptions.port)
|
|
glog.V(0).Infof("Starting Seaweed WebDav Server %s at %s", version.Version(), listenAddress)
|
|
|
|
return webDavStandaloneOptions.startWebDav()
|
|
|
|
}
|
|
|
|
// resolvePaths expands "~" in every user-supplied path flag.
|
|
// Idempotent — safe to call from any entry point.
|
|
func (wo *WebDavOption) resolvePaths() {
|
|
*wo.cacheDir = util.ResolvePath(*wo.cacheDir)
|
|
*wo.tlsCertificate = util.ResolvePath(*wo.tlsCertificate)
|
|
*wo.tlsPrivateKey = util.ResolvePath(*wo.tlsPrivateKey)
|
|
}
|
|
|
|
func (wo *WebDavOption) startWebDav() bool {
|
|
|
|
util.SetOutboundLocalIP(*wo.ipBind)
|
|
|
|
// detect current user
|
|
uid, gid := uint32(0), uint32(0)
|
|
if u, err := user.Current(); err == nil {
|
|
if parsedId, pe := strconv.ParseUint(u.Uid, 10, 32); pe == nil {
|
|
uid = uint32(parsedId)
|
|
}
|
|
if parsedId, pe := strconv.ParseUint(u.Gid, 10, 32); pe == nil {
|
|
gid = uint32(parsedId)
|
|
}
|
|
}
|
|
|
|
// parse filer grpc address
|
|
filerAddress := pb.ServerAddress(*wo.filer)
|
|
|
|
grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.client")
|
|
|
|
var cipher bool
|
|
// connect to filer
|
|
for {
|
|
err := pb.WithGrpcFilerClient(false, 0, filerAddress, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
|
|
resp, err := client.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{})
|
|
if err != nil {
|
|
return fmt.Errorf("get filer %s configuration: %v", filerAddress, err)
|
|
}
|
|
cipher = resp.Cipher
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
glog.V(2).Infof("wait to connect to filer %s grpc address %s", *wo.filer, filerAddress.ToGrpcAddress())
|
|
time.Sleep(time.Second)
|
|
} else {
|
|
glog.V(0).Infof("connected to filer %s grpc address %s", *wo.filer, filerAddress.ToGrpcAddress())
|
|
break
|
|
}
|
|
}
|
|
|
|
ws, webdavServer_err := weed_server.NewWebDavServer(&weed_server.WebDavOption{
|
|
Filer: filerAddress,
|
|
FilerRootPath: *wo.filerRootPath,
|
|
GrpcDialOption: grpcDialOption,
|
|
Collection: *wo.collection,
|
|
Replication: *wo.replication,
|
|
DiskType: *wo.disk,
|
|
Uid: uid,
|
|
Gid: gid,
|
|
Cipher: cipher,
|
|
CacheDir: *wo.cacheDir,
|
|
CacheSizeMB: *wo.cacheSizeMB,
|
|
MaxMB: *wo.maxMB,
|
|
})
|
|
if webdavServer_err != nil {
|
|
glog.Fatalf("WebDav Server startup error: %v", webdavServer_err)
|
|
}
|
|
|
|
httpS := &http.Server{Handler: ws.Handler}
|
|
|
|
idleTimeout := idleTimeoutSeconds(wo.idleTimeout)
|
|
|
|
listenAddress := fmt.Sprintf("%s:%d", *wo.ipBind, *wo.port)
|
|
webDavListener, err := util.NewListener(listenAddress, idleTimeout)
|
|
if err != nil {
|
|
glog.Fatalf("WebDav Server listener on %s error: %v", listenAddress, err)
|
|
}
|
|
|
|
if wo.shutdownCtx != nil {
|
|
go func() {
|
|
<-wo.shutdownCtx.Done()
|
|
httpS.Shutdown(context.Background())
|
|
}()
|
|
}
|
|
|
|
if *wo.tlsPrivateKey != "" {
|
|
glog.V(0).Infof("Start Seaweed WebDav Server %s at https %s", version.Version(), listenAddress)
|
|
getCert, certProvider, err := security.NewReloadingServerCertificate(*wo.tlsCertificate, *wo.tlsPrivateKey)
|
|
if err != nil {
|
|
glog.Fatalf("WebDav Server failed to load TLS certificate: %v", err)
|
|
}
|
|
defer certProvider.Close()
|
|
httpS.TLSConfig = &tls.Config{GetCertificate: getCert}
|
|
if err = httpS.ServeTLS(webDavListener, "", ""); err != nil && err != http.ErrServerClosed {
|
|
glog.Fatalf("WebDav Server Fail to serve: %v", err)
|
|
}
|
|
} else {
|
|
glog.V(0).Infof("Start Seaweed WebDav Server %s at http %s", version.Version(), listenAddress)
|
|
if err = httpS.Serve(webDavListener); err != nil && err != http.ErrServerClosed {
|
|
glog.Fatalf("WebDav Server Fail to serve: %v", err)
|
|
}
|
|
}
|
|
|
|
return true
|
|
|
|
}
|