Skip to content

Commit 85be435

Browse files
committed
feat(archive): refine max archive workers mechanism
1 parent a5c7ea7 commit 85be435

File tree

7 files changed

+73
-74
lines changed

7 files changed

+73
-74
lines changed

README.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -258,16 +258,17 @@ ghfs [options]
258258
-A|--global-archive
259259
Allow user to download the whole contents of current directory for all url paths.
260260
A download link will appear on top part of the page.
261-
--archive-max-workers <number>
262-
Maximum number of concurrent archive operations.
263-
Set to 0 for unlimited (default).
264-
When the limit is reached, new archive requests will receive 429 Too Many Requests.
261+
Make sure there is no circular symbol links.
265262
--archive <url-path> ...
266263
--archive-user <separator><url-path>[<separator><allowed-username>...] ...
267264
Allow user to download the whole contents of current directory for specific url paths(and sub paths).
268265
--archive-dir <fs-path> ...
269266
--archive-dir-user <separator><fs-path>[<separator><allowed-username>...] ...
270267
Similar to --archive, but use file system path instead of url path.
268+
--archive-workers-max <number>
269+
Maximum number of concurrent archive operations.
270+
Set to 0 for unlimited (default).
271+
When the limit is reached, new archive requests will be rejected with status code 429(Too Many Requests).
271272
272273
--global-cors
273274
Allow CORS requests for all url path.

README.zh-CN.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ go build main.go
2828
在8080端口启动服务器,根目录为当前工作目录:
2929
```sh
3030
ghfs -l 8080
31-
```
31+
```
3232

3333
在8080端口启动服务器,根目录为 /usr/share/doc:
3434
```sh
@@ -255,6 +255,10 @@ ghfs [选项]
255255
--archive-dir <文件系统路径> ...
256256
--archive-dir-user <分隔符><文件系统路径>[<分隔符><允许的用户名>...] ...
257257
与--archive类似,但指定的是文件系统路径,而不是URL路径。
258+
--archive-workers-max <数值>
259+
指定打包下载的最大并发数。
260+
设为0(默认)表示无限制。
261+
达到并发上限后,会拒绝后续打包请求并返回状态码429(Too Many Requests)。
258262
259263
--global-cors
260264
接受所有URL路径的CORS跨域请求。

src/param/cli.go

+5-6
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ package param
22

33
import (
44
"errors"
5-
"net/http"
6-
"os"
7-
"strings"
8-
95
"mjpclab.dev/ghfs/src/goNixArgParser"
106
"mjpclab.dev/ghfs/src/goVirtualHost"
117
"mjpclab.dev/ghfs/src/serverError"
8+
"net/http"
9+
"os"
10+
"strings"
1211
)
1312

1413
var cliCmd = NewCliCmd()
@@ -148,7 +147,7 @@ func NewCliCmd() *goNixArgParser.Command {
148147
err = options.AddFlagValues("archivedirsusers", "--archive-dir-user", "", nil, "file system path that allow archive files for specific users, <sep><fs-path>[<sep><user>...]")
149148
serverError.CheckFatal(err)
150149

151-
err = options.AddFlagValue("archivemaxworkers", "--archive-max-workers", "", "0", "maximum number of concurrent archive operations (0 for unlimited)")
150+
err = options.AddFlagValue("archiveworkersmax", "--archive-workers-max", "", "0", "maximum number of concurrent archive operations (0 for unlimited)")
152151
serverError.CheckFatal(err)
153152

154153
err = options.AddFlag("globalcors", "--global-cors", "GHFS_GLOBAL_CORS", "enable CORS headers for all directories")
@@ -440,7 +439,7 @@ func CmdResultsToParams(results []*goNixArgParser.ParseResult) (params Params, e
440439
archiveDirsUsers, _ := result.GetStrings("archivedirsusers")
441440
param.ArchiveDirsUsers = SplitAllKeyValues(archiveDirsUsers)
442441

443-
param.ArchiveMaxWorkers, _ = result.GetUint32("archivemaxworkers")
442+
param.ArchiveWorkersMax, _ = result.GetUint32("archiveworkersmax")
444443

445444
// global restrict access
446445
if result.HasKey("globalrestrictaccess") {

src/param/main.go

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
package param
22

33
import (
4-
"os"
5-
"path/filepath"
6-
74
"mjpclab.dev/ghfs/src/middleware"
85
"mjpclab.dev/ghfs/src/serverError"
96
"mjpclab.dev/ghfs/src/util"
7+
"os"
8+
"path/filepath"
109
)
1110

1211
type Param struct {
@@ -62,7 +61,7 @@ type Param struct {
6261
ArchiveUrlsUsers [][]string // [][path, user...]
6362
ArchiveDirs []string
6463
ArchiveDirsUsers [][]string // [][path, user...]
65-
ArchiveMaxWorkers uint32
64+
ArchiveWorkersMax uint32
6665

6766
GlobalCors bool
6867
CorsUrls []string

src/serverHandler/aliasHandler.go

+9-49
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
package serverHandler
22

33
import (
4-
"net/http"
5-
"regexp"
6-
"strconv"
7-
"strings"
8-
"sync/atomic"
9-
104
"mjpclab.dev/ghfs/src/middleware"
115
"mjpclab.dev/ghfs/src/param"
126
"mjpclab.dev/ghfs/src/serverLog"
137
"mjpclab.dev/ghfs/src/tpl/theme"
148
"mjpclab.dev/ghfs/src/user"
159
"mjpclab.dev/ghfs/src/util"
10+
"net/http"
11+
"regexp"
12+
"strconv"
13+
"strings"
1614
)
1715

1816
var defaultHandler = http.NotFoundHandler()
@@ -50,8 +48,8 @@ type aliasHandler struct {
5048
archive *hierarchyAvailability
5149
cors *hierarchyAvailability
5250

53-
archiveMaxWorkers uint32
54-
archiveWorkers *atomic.Uint32
51+
archiveWorkersMax uint32
52+
archivingWorkers *uint32
5553

5654
globalRestrictAccess []string
5755
restrictAccessUrls pathStringsList
@@ -125,7 +123,7 @@ func (h *aliasHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
125123

126124
if session.isMutate && h.mutate(w, r, session, data) {
127125
return
128-
} else if session.isArchive && h.createArchive(w, r, session, data) {
126+
} else if session.isArchive && h.tryArchive(w, r, session, data) {
129127
return
130128
}
131129
}
@@ -144,44 +142,6 @@ func (h *aliasHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
144142
}
145143
}
146144

147-
func (h *aliasHandler) createArchive(w http.ResponseWriter, r *http.Request, session *sessionContext, data *responseData) bool {
148-
if h.archiveMaxWorkers > 0 {
149-
for {
150-
current := h.archiveWorkers.Load()
151-
if current >= h.archiveMaxWorkers {
152-
data.Status = http.StatusTooManyRequests
153-
return false
154-
}
155-
if h.archiveWorkers.CompareAndSwap(current, current+1) {
156-
break
157-
}
158-
}
159-
160-
defer func() {
161-
for {
162-
current := h.archiveWorkers.Load()
163-
if current == 0 {
164-
break // prevent underflow just in case
165-
}
166-
if h.archiveWorkers.CompareAndSwap(current, current-1) {
167-
break
168-
}
169-
}
170-
}()
171-
}
172-
173-
switch session.archiveFormat {
174-
case tarFmt:
175-
return h.tar(w, r, session, data)
176-
case tgzFmt:
177-
return h.tgz(w, r, session, data)
178-
case zipFmt:
179-
return h.zip(w, r, session, data)
180-
}
181-
182-
return false
183-
}
184-
185145
func newAliasHandler(
186146
p *param.Param,
187147
vhostCtx *vhostContext,
@@ -209,8 +169,8 @@ func newAliasHandler(
209169
toHttpsPort: p.ToHttpsPort,
210170
defaultSort: p.DefaultSort,
211171

212-
archiveMaxWorkers: vhostCtx.archiveMaxWorkers,
213-
archiveWorkers: vhostCtx.archiveWorkers,
172+
archiveWorkersMax: p.ArchiveWorkersMax,
173+
archivingWorkers: vhostCtx.archivingWorkers,
214174

215175
users: vhostCtx.users,
216176
theme: vhostCtx.theme,

src/serverHandler/archive.go

+36-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
package serverHandler
22

33
import (
4+
"mjpclab.dev/ghfs/src/util"
45
"net/http"
56
"net/url"
67
"os"
78
"path"
89
"strings"
9-
10-
"mjpclab.dev/ghfs/src/util"
10+
"sync/atomic"
1111
)
1212

1313
type archiveCallback func(f *os.File, fInfo os.FileInfo, relPath string) error
@@ -211,3 +211,37 @@ func (h *aliasHandler) normalizeArchiveSelections(r *http.Request) ([]string, bo
211211

212212
return selections, true
213213
}
214+
215+
func (h *aliasHandler) startArchive(w http.ResponseWriter, r *http.Request, session *sessionContext, data *responseData) bool {
216+
switch session.archiveFormat {
217+
case tarFmt:
218+
return h.tar(w, r, session, data)
219+
case tgzFmt:
220+
return h.tgz(w, r, session, data)
221+
case zipFmt:
222+
return h.zip(w, r, session, data)
223+
}
224+
225+
return false
226+
}
227+
228+
func (h *aliasHandler) tryArchive(w http.ResponseWriter, r *http.Request, session *sessionContext, data *responseData) (ok bool) {
229+
if h.archivingWorkers == nil {
230+
return h.startArchive(w, r, session, data)
231+
}
232+
233+
if *h.archivingWorkers >= h.archiveWorkersMax {
234+
data.Status = http.StatusTooManyRequests
235+
return false
236+
}
237+
238+
archiving := atomic.AddUint32(h.archivingWorkers, 1)
239+
ok = archiving <= h.archiveWorkersMax
240+
if ok {
241+
ok = h.startArchive(w, r, session, data)
242+
} else {
243+
data.Status = http.StatusTooManyRequests
244+
}
245+
atomic.AddUint32(h.archivingWorkers, ^uint32(0)) // archiveWorkers -= 1
246+
return
247+
}

src/serverHandler/vhostHandler.go

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
package serverHandler
22

33
import (
4-
"net/http"
5-
"regexp"
6-
"sync/atomic"
7-
84
"mjpclab.dev/ghfs/src/param"
95
"mjpclab.dev/ghfs/src/serverError"
106
"mjpclab.dev/ghfs/src/serverLog"
117
"mjpclab.dev/ghfs/src/tpl/defaultTheme"
128
"mjpclab.dev/ghfs/src/tpl/theme"
139
"mjpclab.dev/ghfs/src/user"
10+
"net/http"
11+
"regexp"
1412
)
1513

1614
type vhostContext struct {
@@ -31,8 +29,7 @@ type vhostContext struct {
3129
archiveUrlsUsers pathIntsList
3230
archiveDirsUsers pathIntsList
3331

34-
archiveMaxWorkers uint32
35-
archiveWorkers *atomic.Uint32
32+
archivingWorkers *uint32
3633

3734
shows *regexp.Regexp
3835
showDirs *regexp.Regexp
@@ -100,6 +97,12 @@ func NewVhostHandler(
10097
theme = defaultTheme.DefaultTheme
10198
}
10299

100+
var archivingWorkersValue uint32
101+
var archivingWorkers *uint32
102+
if p.ArchiveWorkersMax > 0 {
103+
archivingWorkers = &archivingWorkersValue
104+
}
105+
103106
// alias param
104107
vhostCtx := &vhostContext{
105108
logger: logger,
@@ -119,8 +122,7 @@ func NewVhostHandler(
119122
archiveUrlsUsers: pathUsernamesToPathUids(users, p.ArchiveUrlsUsers),
120123
archiveDirsUsers: pathUsernamesToPathUids(users, p.ArchiveDirsUsers),
121124

122-
archiveMaxWorkers: p.ArchiveMaxWorkers,
123-
archiveWorkers: &atomic.Uint32{},
125+
archivingWorkers: archivingWorkers,
124126

125127
shows: shows,
126128
showDirs: showDirs,

0 commit comments

Comments
 (0)