Skip to content

Commit b82b7d6

Browse files
authored
cmd/go-cache-plugin: rearrange how the HTTP services are set up (#5)
The http.ServeMux has some annoying built-in behaviour that is inconvenient for us here. Specifically, it tries to automatically issue redirects when it cannot resolve the handler, and that gets in the way of the proxy. Instead, plumb together a simple direct dispatcher, since we only have a small number of path options anyway. To make the "serve" command less painful to read, split some of the setup code into its own file.
1 parent 4d376d5 commit b82b7d6

File tree

4 files changed

+274
-145
lines changed

4 files changed

+274
-145
lines changed

cmd/go-cache-plugin/commands.go

Lines changed: 20 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,20 @@ package main
66
import (
77
"context"
88
"errors"
9-
"expvar"
109
"fmt"
1110
"io"
1211
"log"
1312
"net"
1413
"net/http"
1514
"os"
1615
"os/signal"
17-
"path"
18-
"path/filepath"
1916
"strconv"
20-
"strings"
2117
"syscall"
2218
"time"
2319

24-
"github.com/aws/aws-sdk-go-v2/config"
25-
"github.com/aws/aws-sdk-go-v2/service/s3"
2620
"github.com/creachadair/command"
2721
"github.com/creachadair/gocache"
28-
"github.com/creachadair/gocache/cachedir"
2922
"github.com/creachadair/taskgroup"
30-
"github.com/goproxy/goproxy"
31-
"github.com/tailscale/go-cache-plugin/internal/s3util"
32-
"github.com/tailscale/go-cache-plugin/revproxy"
33-
"github.com/tailscale/go-cache-plugin/s3cache"
34-
"github.com/tailscale/go-cache-plugin/s3proxy"
35-
"tailscale.com/tsweb"
3623
)
3724

3825
var flags struct {
@@ -49,63 +36,6 @@ var flags struct {
4936
DebugLog bool `flag:"debug,default=$GOCACHE_DEBUG,Enable detailed per-request debug logging (noisy)"`
5037
}
5138

52-
func initCacheServer(env *command.Env) (*gocache.Server, *s3util.Client, error) {
53-
switch {
54-
case flags.CacheDir == "":
55-
return nil, nil, env.Usagef("you must provide a --cache-dir")
56-
case flags.S3Bucket == "":
57-
return nil, nil, env.Usagef("you must provide an S3 --bucket name")
58-
}
59-
region, err := getBucketRegion(env.Context(), flags.S3Bucket)
60-
if err != nil {
61-
return nil, nil, env.Usagef("you must provide an S3 --region name")
62-
}
63-
64-
dir, err := cachedir.New(flags.CacheDir)
65-
if err != nil {
66-
return nil, nil, fmt.Errorf("create local cache: %w", err)
67-
}
68-
69-
cfg, err := config.LoadDefaultConfig(env.Context(), config.WithRegion(region))
70-
if err != nil {
71-
return nil, nil, fmt.Errorf("laod AWS config: %w", err)
72-
}
73-
74-
vprintf("local cache directory: %s", flags.CacheDir)
75-
vprintf("S3 cache bucket %q (%s)", flags.S3Bucket, region)
76-
client := &s3util.Client{
77-
Client: s3.NewFromConfig(cfg),
78-
Bucket: flags.S3Bucket,
79-
}
80-
cache := &s3cache.Cache{
81-
Local: dir,
82-
S3Client: client,
83-
KeyPrefix: flags.KeyPrefix,
84-
MinUploadSize: flags.MinUploadSize,
85-
UploadConcurrency: flags.S3Concurrency,
86-
}
87-
cache.SetMetrics(env.Context(), expvar.NewMap("gocache_host"))
88-
89-
close := cache.Close
90-
if flags.Expiration > 0 {
91-
dirClose := dir.Cleanup(flags.Expiration)
92-
close = func(ctx context.Context) error {
93-
return errors.Join(cache.Close(ctx), dirClose(ctx))
94-
}
95-
}
96-
s := &gocache.Server{
97-
Get: cache.Get,
98-
Put: cache.Put,
99-
Close: close,
100-
SetMetrics: cache.SetMetrics,
101-
MaxRequests: flags.Concurrency,
102-
Logf: vprintf,
103-
LogRequests: flags.DebugLog,
104-
}
105-
expvar.Publish("gocache_server", s.Metrics().Get("server"))
106-
return s, client, nil
107-
}
108-
10939
// runDirect runs a cache communicating on stdin/stdout, for use as a direct
11040
// GOCACHEPROG plugin.
11141
func runDirect(env *command.Env) error {
@@ -160,93 +90,41 @@ func runServe(env *command.Env) error {
16090
var g taskgroup.Group
16191
g.Go(taskgroup.NoError(func() {
16292
<-ctx.Done()
163-
log.Printf("signal received, closing listener")
93+
log.Printf("closing plugin listener")
16494
lst.Close()
16595
}))
16696

167-
// If an HTTP server is enabled, start it up with debug routes.
168-
// If enabled, the module proxy and reverse cache attach to mux below.
169-
var mux *http.ServeMux
97+
// If a module proxy is enabled, start it.
98+
modProxy, modCleanup, err := initModProxy(env.SetContext(ctx), s3c)
99+
if err != nil {
100+
lst.Close()
101+
return fmt.Errorf("module proxy: %w", err)
102+
}
103+
defer modCleanup()
104+
105+
// If a reverse proxy is enabled, start it.
106+
revProxy, err := initRevProxy(env.SetContext(ctx), s3c, &g)
107+
if err != nil {
108+
lst.Close()
109+
return fmt.Errorf("reverse proxy: %w", err)
110+
}
111+
112+
// If an HTTP server is enabled, start it up with debug routes
113+
// and whatever other services were requested.
170114
if serveFlags.HTTP != "" {
171-
mux = http.NewServeMux()
172-
tsweb.Debugger(mux)
173115
srv := &http.Server{
174116
Addr: serveFlags.HTTP,
175-
Handler: mux,
117+
Handler: makeHandler(modProxy, revProxy),
176118
}
177119
g.Go(srv.ListenAndServe)
178120
vprintf("HTTP server listening at %q", serveFlags.HTTP)
179121
g.Go(taskgroup.NoError(func() {
180122
<-ctx.Done()
181-
vprintf("signal received, stopping HTTP service")
123+
vprintf("stopping HTTP service")
182124
srv.Shutdown(context.Background())
183125
}))
184126
}
185127

186-
// If a module proxy is enabled, start it.
187-
if serveFlags.ModProxy {
188-
if mux == nil {
189-
return env.Usagef("you must set --http to enable --modproxy")
190-
}
191-
modCachePath := filepath.Join(flags.CacheDir, "module")
192-
if err := os.MkdirAll(modCachePath, 0700); err != nil {
193-
lst.Close()
194-
return fmt.Errorf("create module cache: %w", err)
195-
}
196-
cacher := &s3proxy.Cacher{
197-
Local: modCachePath,
198-
S3Client: s3c,
199-
KeyPrefix: path.Join(flags.KeyPrefix, "module"),
200-
MaxTasks: flags.S3Concurrency,
201-
LogRequests: flags.DebugLog,
202-
Logf: vprintf,
203-
}
204-
defer func() {
205-
vprintf("close cacher (err=%v)", cacher.Close())
206-
}()
207-
proxy := &goproxy.Goproxy{
208-
Fetcher: &goproxy.GoFetcher{
209-
// As configured, the fetcher should never shell out to the go
210-
// tool. Specifically, because we set GOPROXY and do not set any
211-
// bypass via GONOPROXY, GOPRIVATE, etc., we will only attempt to
212-
// proxy for the specific server(s) listed in Env.
213-
GoBin: "/bin/false",
214-
Env: []string{"GOPROXY=https://proxy.golang.org"},
215-
},
216-
Cacher: cacher,
217-
ProxiedSumDBs: []string{"sum.golang.org"}, // default, see below
218-
}
219-
vprintf("enabling Go module proxy")
220-
if serveFlags.SumDB != "" {
221-
proxy.ProxiedSumDBs = strings.Split(serveFlags.SumDB, ",")
222-
vprintf("enabling sum DB proxy for %s", strings.Join(proxy.ProxiedSumDBs, ", "))
223-
}
224-
expvar.Publish("modcache", cacher.Metrics())
225-
mux.Handle("/mod/", http.StripPrefix("/mod", proxy))
226-
}
227-
228-
// If a reverse proxy is enabled, start it.
229-
if serveFlags.RevProxy != "" {
230-
if mux == nil {
231-
return env.Usagef("you must set --http to enable --revproxy")
232-
}
233-
revCachePath := filepath.Join(flags.CacheDir, "revproxy")
234-
if err := os.MkdirAll(revCachePath, 0700); err != nil {
235-
lst.Close()
236-
return fmt.Errorf("create revproxy cache: %w", err)
237-
}
238-
proxy := &revproxy.Server{
239-
Targets: strings.Split(serveFlags.RevProxy, ","),
240-
Local: revCachePath,
241-
S3Client: s3c,
242-
KeyPrefix: path.Join(flags.KeyPrefix, "revproxy"),
243-
Logf: vprintf,
244-
}
245-
expvar.Publish("revcache", proxy.Metrics())
246-
vprintf("enabling reverse proxy for %s", strings.Join(proxy.Targets, ", "))
247-
mux.Handle("/", proxy)
248-
}
249-
250128
for {
251129
conn, err := lst.Accept()
252130
if err != nil {

0 commit comments

Comments
 (0)