forked from tuupke/cuproxy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathloader.go
More file actions
84 lines (73 loc) · 2.36 KB
/
Copy pathloader.go
File metadata and controls
84 lines (73 loc) · 2.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package main
import (
"fmt"
"os"
"runtime"
"strings"
"sync"
"github.com/chebyrash/promise"
"github.com/panjf2000/ants/v2"
"github.com/rs/zerolog"
"github.com/tuupke/utils/env"
"github.com/tuupke/utils/lifecycle"
"github.com/valyala/fasthttp"
)
var (
cpuPool, ioPool promise.Pool
printKeys = strings.Split(env.String("PRINT_KEYS", "*"), ",")
)
func init() {
cpuAnts, err := ants.NewPool(runtime.NumCPU())
if err != nil {
panic(err)
}
ioAnts, err := ants.NewPool(runtime.NumCPU() * 5)
if err != nil {
panic(err)
}
cpuPool = promise.FromAntsPool(cpuAnts)
ioPool = promise.FromAntsPool(ioAnts)
}
// loadValues dispatches every configured webhook set for this request and
// returns a promise that resolves to the rendered (or cached) banner PDF.
//
// The promise always waits for every webhook to finish before rendering. A
// print request that arrives while webhooks are still in flight will block on
// Await rather than short-circuiting with partial data.
func loadValues(log zerolog.Logger, ctx *fasthttp.RequestCtx, jobId int32) *promise.Promise[*os.File] {
data := LoadFromRequest(ctx)
log = log.With().IPAddr("for", data.ip).Int32("job-id", jobId).Logger()
log.Info().Int("num_hooks", len(toCall)).Msg("loading data")
var wg sync.WaitGroup
for _, set := range toCall {
wg.Add(1)
ioPool.Go(func() {
defer wg.Done()
set.run(log, data)
})
}
allHooksDone := promise.New(func(resolve func(e), _ func(error)) {
wg.Wait()
log.Info().Msg("all webhooks finished")
resolve(empty)
})
return promise.ThenWithPool(allHooksDone, lifecycle.Context(), func(_ e) (*os.File, error) {
return renderBanner(log, data)
}, cpuPool)
}
// renderBanner opens the per-IP banner PDF, reuses it if it's newer than the
// most recent webhook data, otherwise renders a fresh page.
func renderBanner(log zerolog.Logger, data *Props) (*os.File, error) {
fn := pdfLocation + "/" + data.ip.String() + ".pdf"
file, err := os.OpenFile(fn, os.O_RDWR|os.O_CREATE, 0o755)
if err != nil {
return nil, fmt.Errorf("open banner file %q: %w", fn, err)
}
if fi, statErr := file.Stat(); statErr == nil && fi != nil && fi.Size() > 0 &&
!data.latestData.IsZero() && fi.ModTime().After(data.latestData) {
log.Info().Msg("reusing cached banner")
return file, nil
}
log.Err(file.Truncate(0)).Msg("rendering new banner")
return file, BannerPage(log, file, data, printKeys...)
}