diff --git a/README.md b/README.md index ccca502d..8f46006c 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Will generate executable file "main" in current directory. Start server on port 8080, root directory is current working directory: ```sh ghfs -l 8080 -``` +``` Start server on port 8080, root directory is /usr/share/doc: ```sh @@ -265,6 +265,10 @@ ghfs [options] --archive-dir ... --archive-dir-user [...] ... Similar to --archive, but use file system path instead of url path. +--archive-workers-max + Maximum number of concurrent archive operations. + Set to 0 for unlimited (default). + When the limit is reached, new archive requests will be rejected with status code 429(Too Many Requests). --global-cors Allow CORS requests for all url path. @@ -304,7 +308,7 @@ ghfs [options] -S|--show ... -SD|--show-dir ... -SF|--show-file ... - If specified, files or directories match wildcards(except hidden by hide option) will be shown. + If specified, files or directories match wildcards(except hidden by hide option) will be shown. -H|--hide ... -HD|--hide-dir ... diff --git a/README.zh-CN.md b/README.zh-CN.md index a0f52762..2ebc6499 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -28,7 +28,7 @@ go build main.go 在8080端口启动服务器,根目录为当前工作目录: ```sh ghfs -l 8080 -``` +``` 在8080端口启动服务器,根目录为 /usr/share/doc: ```sh @@ -255,6 +255,10 @@ ghfs [选项] --archive-dir <文件系统路径> ... --archive-dir-user <分隔符><文件系统路径>[<分隔符><允许的用户名>...] ... 与--archive类似,但指定的是文件系统路径,而不是URL路径。 +--archive-workers-max <数值> + 指定打包下载的最大并发数。 + 设为0(默认)表示无限制。 + 达到并发上限后,会拒绝后续打包请求并返回状态码429(Too Many Requests)。 --global-cors 接受所有URL路径的CORS跨域请求。 diff --git a/src/param/cli.go b/src/param/cli.go index e35a7228..bcfd3b6c 100644 --- a/src/param/cli.go +++ b/src/param/cli.go @@ -147,6 +147,9 @@ func NewCliCmd() *goNixArgParser.Command { err = options.AddFlagValues("archivedirsusers", "--archive-dir-user", "", nil, "file system path that allow archive files for specific users, [...]") serverError.CheckFatal(err) + err = options.AddFlagValue("archiveworkersmax", "--archive-workers-max", "", "0", "maximum number of concurrent archive operations (0 for unlimited)") + serverError.CheckFatal(err) + err = options.AddFlag("globalcors", "--global-cors", "GHFS_GLOBAL_CORS", "enable CORS headers for all directories") serverError.CheckFatal(err) @@ -436,6 +439,8 @@ func CmdResultsToParams(results []*goNixArgParser.ParseResult) (params Params, e archiveDirsUsers, _ := result.GetStrings("archivedirsusers") param.ArchiveDirsUsers = SplitAllKeyValues(archiveDirsUsers) + param.ArchiveWorkersMax, _ = result.GetUint32("archiveworkersmax") + // global restrict access if result.HasKey("globalrestrictaccess") { param.GlobalRestrictAccess, _ = result.GetStrings("globalrestrictaccess") @@ -465,6 +470,7 @@ func CmdResultsToParams(results []*goNixArgParser.ParseResult) (params Params, e certFiles, _ := result.GetStrings("certs") keyFiles, _ := result.GetStrings("keys") param.CertKeyPaths, es = goVirtualHost.CertsKeysToPairs(certFiles, keyFiles) + errs = append(errs, es...) // listen listens, _ := result.GetStrings("listens") diff --git a/src/param/main.go b/src/param/main.go index 170bb602..04032434 100644 --- a/src/param/main.go +++ b/src/param/main.go @@ -56,11 +56,12 @@ type Param struct { DeleteDirs []string DeleteDirsUsers [][]string // [][path, user...] - GlobalArchive bool - ArchiveUrls []string - ArchiveUrlsUsers [][]string // [][path, user...] - ArchiveDirs []string - ArchiveDirsUsers [][]string // [][path, user...] + GlobalArchive bool + ArchiveUrls []string + ArchiveUrlsUsers [][]string // [][path, user...] + ArchiveDirs []string + ArchiveDirsUsers [][]string // [][path, user...] + ArchiveWorkersMax uint32 GlobalCors bool CorsUrls []string diff --git a/src/serverHandler/aliasHandler.go b/src/serverHandler/aliasHandler.go index 21a3d7f9..15d8e93b 100644 --- a/src/serverHandler/aliasHandler.go +++ b/src/serverHandler/aliasHandler.go @@ -48,6 +48,9 @@ type aliasHandler struct { archive *hierarchyAvailability cors *hierarchyAvailability + archiveWorkersMax uint32 + archivingWorkers *uint32 + globalRestrictAccess []string restrictAccessUrls pathStringsList restrictAccessDirs pathStringsList @@ -120,21 +123,8 @@ func (h *aliasHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if session.isMutate && h.mutate(w, r, session, data) { return - } else if session.isArchive { - switch session.archiveFormat { - case tarFmt: - if h.tar(w, r, session, data) { - return - } - case tgzFmt: - if h.tgz(w, r, session, data) { - return - } - case zipFmt: - if h.zip(w, r, session, data) { - return - } - } + } else if session.isArchive && h.tryArchive(w, r, session, data) { + return } } @@ -179,6 +169,9 @@ func newAliasHandler( toHttpsPort: p.ToHttpsPort, defaultSort: p.DefaultSort, + archiveWorkersMax: p.ArchiveWorkersMax, + archivingWorkers: vhostCtx.archivingWorkers, + users: vhostCtx.users, theme: vhostCtx.theme, logger: vhostCtx.logger, diff --git a/src/serverHandler/archive.go b/src/serverHandler/archive.go index df331653..5770bc1f 100644 --- a/src/serverHandler/archive.go +++ b/src/serverHandler/archive.go @@ -7,6 +7,7 @@ import ( "os" "path" "strings" + "sync/atomic" ) type archiveCallback func(f *os.File, fInfo os.FileInfo, relPath string) error @@ -210,3 +211,37 @@ func (h *aliasHandler) normalizeArchiveSelections(r *http.Request) ([]string, bo return selections, true } + +func (h *aliasHandler) startArchive(w http.ResponseWriter, r *http.Request, session *sessionContext, data *responseData) (ok bool) { + switch session.archiveFormat { + case tarFmt: + return h.tar(w, r, session, data) + case tgzFmt: + return h.tgz(w, r, session, data) + case zipFmt: + return h.zip(w, r, session, data) + } + + return +} + +func (h *aliasHandler) tryArchive(w http.ResponseWriter, r *http.Request, session *sessionContext, data *responseData) (ok bool) { + if h.archivingWorkers == nil { + return h.startArchive(w, r, session, data) + } + + if *h.archivingWorkers >= h.archiveWorkersMax { + data.Status = http.StatusTooManyRequests + return + } + + archiving := atomic.AddUint32(h.archivingWorkers, 1) + ok = archiving <= h.archiveWorkersMax + if ok { + ok = h.startArchive(w, r, session, data) + } else { + data.Status = http.StatusTooManyRequests + } + atomic.AddUint32(h.archivingWorkers, ^uint32(0)) // archiveWorkers -= 1 + return +} diff --git a/src/serverHandler/vhostHandler.go b/src/serverHandler/vhostHandler.go index ac3d85f7..8e15cad9 100644 --- a/src/serverHandler/vhostHandler.go +++ b/src/serverHandler/vhostHandler.go @@ -29,6 +29,8 @@ type vhostContext struct { archiveUrlsUsers pathIntsList archiveDirsUsers pathIntsList + archivingWorkers *uint32 + shows *regexp.Regexp showDirs *regexp.Regexp showFiles *regexp.Regexp @@ -95,6 +97,11 @@ func NewVhostHandler( theme = defaultTheme.DefaultTheme } + var archivingWorkers *uint32 + if p.ArchiveWorkersMax > 0 { + archivingWorkers = new(uint32) + } + // alias param vhostCtx := &vhostContext{ logger: logger, @@ -114,6 +121,8 @@ func NewVhostHandler( archiveUrlsUsers: pathUsernamesToPathUids(users, p.ArchiveUrlsUsers), archiveDirsUsers: pathUsernamesToPathUids(users, p.ArchiveDirsUsers), + archivingWorkers: archivingWorkers, + shows: shows, showDirs: showDirs, showFiles: showFiles,